From 618a3c03fcfdf1ac4543247c8ddfb0c9d775ff33 Mon Sep 17 00:00:00 2001 From: Hal Rosenstock Date: Tue, 28 Mar 2006 16:40:04 -0800 Subject: IB/mad: RMPP support for additional classes Add RMPP support for additional management classes that support it. Also, validate RMPP is consistent with management class specified. Signed-off-by: Hal Rosenstock Signed-off-by: Sean Hefty Signed-off-by: Roland Dreier --- include/rdma/ib_mad.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h index 51ab8eddb295..5ff77558013b 100644 --- a/include/rdma/ib_mad.h +++ b/include/rdma/ib_mad.h @@ -3,7 +3,7 @@ * Copyright (c) 2004 Infinicon Corporation. All rights reserved. * Copyright (c) 2004 Intel Corporation. All rights reserved. * Copyright (c) 2004 Topspin Corporation. All rights reserved. - * Copyright (c) 2004 Voltaire Corporation. All rights reserved. + * Copyright (c) 2004-2006 Voltaire Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -55,6 +55,10 @@ #define IB_MGMT_CLASS_DEVICE_MGMT 0x06 #define IB_MGMT_CLASS_CM 0x07 #define IB_MGMT_CLASS_SNMP 0x08 +#define IB_MGMT_CLASS_DEVICE_ADM 0x10 +#define IB_MGMT_CLASS_BOOT_MGMT 0x11 +#define IB_MGMT_CLASS_BIS 0x12 +#define IB_MGMT_CLASS_CONG_MGMT 0x21 #define IB_MGMT_CLASS_VENDOR_RANGE2_START 0x30 #define IB_MGMT_CLASS_VENDOR_RANGE2_END 0x4F @@ -117,6 +121,8 @@ enum { IB_MGMT_VENDOR_DATA = 216, IB_MGMT_SA_HDR = 56, IB_MGMT_SA_DATA = 200, + IB_MGMT_DEVICE_HDR = 64, + IB_MGMT_DEVICE_DATA = 192, }; struct ib_mad_hdr { @@ -602,6 +608,25 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent, int hdr_len, int data_len, gfp_t gfp_mask); +/** + * ib_is_mad_class_rmpp - returns whether given management class + * supports RMPP. + * @mgmt_class: management class + * + * This routine returns whether the management class supports RMPP. + */ +int ib_is_mad_class_rmpp(u8 mgmt_class); + +/** + * ib_get_mad_data_offset - returns the data offset for a given + * management class. + * @mgmt_class: management class + * + * This routine returns the data offset in the MAD for the management + * class requested. + */ +int ib_get_mad_data_offset(u8 mgmt_class); + /** * ib_get_rmpp_segment - returns the data buffer for a given RMPP segment. * @send_buf: Previously allocated send data buffer. -- cgit v1.2.3 From 3283a67d8618c9a292eced23e8753ab64adc6dba Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Thu, 30 Mar 2006 10:13:22 -0500 Subject: [IA64] Add __mca_table to the DISCARD list in gate.lds Add __mca_table to the DISCARD list for the gate.lds linker script to avoid broken linker references when linking the final vmlinux file. Also add comment to include/asm-ia64/asmmacros.h to avoid anyone else hitting this problem in the future. Credits to James Bottomley for spotting the DISCARD list in gate.lds.S Signed-off-by: Jes Sorensen Signed-off-by: Tony Luck --- arch/ia64/kernel/gate.lds.S | 1 + include/asm-ia64/asmmacro.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/arch/ia64/kernel/gate.lds.S b/arch/ia64/kernel/gate.lds.S index e1e4aba9ecd0..7c99e6ec3daf 100644 --- a/arch/ia64/kernel/gate.lds.S +++ b/arch/ia64/kernel/gate.lds.S @@ -59,6 +59,7 @@ SECTIONS *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(__ex_table) + *(__mca_table) } } diff --git a/include/asm-ia64/asmmacro.h b/include/asm-ia64/asmmacro.h index d4cec32083d8..edf2cebb2969 100644 --- a/include/asm-ia64/asmmacro.h +++ b/include/asm-ia64/asmmacro.h @@ -38,6 +38,10 @@ name: /* * Helper macros for accessing user memory. + * + * When adding any new .section/.previous entries here, make sure to + * also add it to the DISCARD section in arch/ia64/kernel/gate.lds.S or + * unpleasant things will happen. */ .section "__ex_table", "a" // declare section & section attributes -- cgit v1.2.3 From a81dd18eb974cc34634c53a6447b2799ec0c3158 Mon Sep 17 00:00:00 2001 From: Thibaut VARENE Date: Fri, 3 Feb 2006 18:06:30 -0700 Subject: [PARISC] Clarify pdc_stable license terms pdc_stable.c is explicitly licensed under GPL version 2. Signed-off-by: Thibaut VARENE Signed-off-by: Kyle McMartin --- arch/parisc/kernel/pdc_chassis.c | 5 ++--- drivers/parisc/pdc_stable.c | 5 ++--- include/asm-parisc/pdc_chassis.h | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c index 0cea6958f427..a45e2e2ffd9f 100644 --- a/arch/parisc/kernel/pdc_chassis.c +++ b/arch/parisc/kernel/pdc_chassis.c @@ -5,9 +5,8 @@ * Copyright (C) 2002-2004 Thibaut VARENE * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index a28e17898fbd..4e53be9c03ab 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -4,9 +4,8 @@ * Copyright (C) 2005-2006 Thibaut VARENE * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/asm-parisc/pdc_chassis.h b/include/asm-parisc/pdc_chassis.h index adac9ac2743f..a609273dc6bf 100644 --- a/include/asm-parisc/pdc_chassis.h +++ b/include/asm-parisc/pdc_chassis.h @@ -6,9 +6,8 @@ * * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of -- cgit v1.2.3 From ab43227c8a568119a3aebc952a95ac3023e1730d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 22 Mar 2006 08:28:59 -0700 Subject: [PARISC] Add parisc implementation of flush_anon_page() This should now allow SG_IO and fuse to function correctly on our platform. Signed-off-by: James Bottomley Signed-off-by: Kyle McMartin --- include/asm-parisc/cacheflush.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index c53af9ff41b5..482be77551e3 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h @@ -184,6 +184,14 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long } +static inline void +flush_anon_page(struct page *page, unsigned long vmaddr) +{ + if (PageAnon(page)) + flush_user_dcache_page(vmaddr); +} +#define ARCH_HAS_FLUSH_ANON_PAGE + #ifdef CONFIG_DEBUG_RODATA void mark_rodata_ro(void); #endif -- cgit v1.2.3 From ba57583396585a1ca509e2a84d970a2ff3f9bbfb Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 22 Mar 2006 09:42:04 -0700 Subject: [PARISC] Add parisc implementation of flush_kernel_dcache_page() We need to do a little renaming of our original syntax because of the difference in arguments. Signed-off-by: James Bottomley Signed-off-by: Kyle McMartin --- arch/parisc/kernel/cache.c | 6 +++--- arch/parisc/kernel/pacache.S | 4 ++-- include/asm-parisc/cache.h | 2 +- include/asm-parisc/cacheflush.h | 9 ++++++++- include/asm-parisc/page.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index d8a4ca021aac..360b7391cb8c 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -89,7 +89,7 @@ update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) if (pfn_valid(page_to_pfn(page)) && page_mapping(page) && test_bit(PG_dcache_dirty, &page->flags)) { - flush_kernel_dcache_page(page_address(page)); + flush_kernel_dcache_page(page); clear_bit(PG_dcache_dirty, &page->flags); } } @@ -278,7 +278,7 @@ void flush_dcache_page(struct page *page) return; } - flush_kernel_dcache_page(page_address(page)); + flush_kernel_dcache_page(page); if (!mapping) return; @@ -317,7 +317,7 @@ EXPORT_SYMBOL(flush_dcache_page); /* Defined in arch/parisc/kernel/pacache.S */ EXPORT_SYMBOL(flush_kernel_dcache_range_asm); -EXPORT_SYMBOL(flush_kernel_dcache_page); +EXPORT_SYMBOL(flush_kernel_dcache_page_asm); EXPORT_SYMBOL(flush_data_cache_local); EXPORT_SYMBOL(flush_kernel_icache_range_asm); diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S index 9534ee17b9be..7a4f07e8d3c3 100644 --- a/arch/parisc/kernel/pacache.S +++ b/arch/parisc/kernel/pacache.S @@ -621,9 +621,9 @@ __clear_user_page_asm: .procend - .export flush_kernel_dcache_page + .export flush_kernel_dcache_page_asm -flush_kernel_dcache_page: +flush_kernel_dcache_page_asm: .proc .callinfo NO_CALLS .entry diff --git a/include/asm-parisc/cache.h b/include/asm-parisc/cache.h index ae50f8e12eed..c831665473cb 100644 --- a/include/asm-parisc/cache.h +++ b/include/asm-parisc/cache.h @@ -48,7 +48,7 @@ extern void flush_user_icache_range_asm(unsigned long, unsigned long); extern void flush_kernel_icache_range_asm(unsigned long, unsigned long); extern void flush_user_dcache_range_asm(unsigned long, unsigned long); extern void flush_kernel_dcache_range_asm(unsigned long, unsigned long); -extern void flush_kernel_dcache_page(void *); +extern void flush_kernel_dcache_page_asm(void *); extern void flush_kernel_icache_page(void *); extern void disable_sr_hashing(void); /* turns off space register hashing */ extern void disable_sr_hashing_asm(int); /* low level support for above */ diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index 482be77551e3..76b6b7d6046a 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h @@ -62,7 +62,7 @@ extern void flush_dcache_page(struct page *page); #define flush_dcache_mmap_unlock(mapping) \ write_unlock_irq(&(mapping)->tree_lock) -#define flush_icache_page(vma,page) do { flush_kernel_dcache_page(page_address(page)); flush_kernel_icache_page(page_address(page)); } while (0) +#define flush_icache_page(vma,page) do { flush_kernel_dcache_page(page); flush_kernel_icache_page(page_address(page)); } while (0) #define flush_icache_range(s,e) do { flush_kernel_dcache_range_asm(s,e); flush_kernel_icache_range_asm(s,e); } while (0) @@ -192,6 +192,13 @@ flush_anon_page(struct page *page, unsigned long vmaddr) } #define ARCH_HAS_FLUSH_ANON_PAGE +static inline void +flush_kernel_dcache_page(struct page *page) +{ + flush_kernel_dcache_page_asm(page_address(page)); +} +#define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE + #ifdef CONFIG_DEBUG_RODATA void mark_rodata_ro(void); #endif diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index 9f303c0c3cd7..c6c9d5793070 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -26,7 +26,7 @@ static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, struct page *pg) { copy_user_page_asm(vto, vfrom); - flush_kernel_dcache_page(vto); + flush_kernel_dcache_page_asm(vto); /* XXX: ppc flushes icache too, should we? */ } -- cgit v1.2.3 From b31059f7634931a06f6811247ae9217d1a833a46 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 23 Mar 2006 15:48:15 -0700 Subject: [PARISC] Add STRICT_MM_TYPECHECKS Add STRICT_MM_TYPECHECKS to page.h as other architectures do. Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- include/asm-parisc/page.h | 56 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index c6c9d5793070..45e02aa5bf4b 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -40,14 +40,19 @@ clear_user_page(void *page, unsigned long vaddr, struct page *pg) /* * These are used to make use of C type-checking.. */ -#ifdef __LP64__ -typedef struct { unsigned long pte; } pte_t; -#else -typedef struct { - unsigned long pte; - unsigned long flags; -} pte_t; +#define STRICT_MM_TYPECHECKS +#ifdef STRICT_MM_TYPECHECKS +typedef struct { unsigned long pte; +#if !defined(CONFIG_64BIT) + unsigned long future_flags; + /* XXX: it's possible to remove future_flags and change BITS_PER_PTE_ENTRY + to 2, but then strangely the identical 32bit kernel boots on a + c3000(pa20), but not any longer on a 715(pa11). + Still investigating... HelgeD. + */ #endif +} pte_t; /* either 32 or 64bit */ + /* NOTE: even on 64 bits, these entries are __u32 because we allocate * the pmd and pgd in ZONE_DMA (i.e. under 4GB) */ typedef struct { __u32 pmd; } pmd_t; @@ -55,25 +60,44 @@ typedef struct { __u32 pgd; } pgd_t; typedef struct { unsigned long pgprot; } pgprot_t; #define pte_val(x) ((x).pte) -#ifdef __LP64__ -#define pte_flags(x) (*(__u32 *)&((x).pte)) -#else -#define pte_flags(x) ((x).flags) -#endif - /* These do not work lvalues, so make sure we don't use them as such. */ #define pmd_val(x) ((x).pmd + 0) #define pgd_val(x) ((x).pgd + 0) #define pgprot_val(x) ((x).pgprot) -#define __pmd_val_set(x,n) (x).pmd = (n) -#define __pgd_val_set(x,n) (x).pgd = (n) - #define __pte(x) ((pte_t) { (x) } ) #define __pmd(x) ((pmd_t) { (x) } ) #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) +#define __pmd_val_set(x,n) (x).pmd = (n) +#define __pgd_val_set(x,n) (x).pgd = (n) + +#else +/* + * .. while these make it easier on the compiler + */ +typedef unsigned long pte_t; +typedef __u32 pmd_t; +typedef __u32 pgd_t; +typedef unsigned long pgprot_t; + +#define pte_val(x) (x) +#define pmd_val(x) (x) +#define pgd_val(x) (x) +#define pgprot_val(x) (x) + +#define __pte(x) (x) +#define __pmd(x) (x) +#define __pgd(x) (x) +#define __pgprot(x) (x) + +#define __pmd_val_set(x,n) (x) = (n) +#define __pgd_val_set(x,n) (x) = (n) + +#endif /* STRICT_MM_TYPECHECKS */ + + typedef struct __physmem_range { unsigned long start_pfn; unsigned long pages; /* PAGE_SIZE pages */ -- cgit v1.2.3 From b8ce0aadcdebbaf5ec013c57e2a0803060817bcc Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 7 Mar 2006 14:12:13 -0700 Subject: [PARISC] Add CONFIG_HPPA_IOREMAP to conditionally enable ioremap Instead of making it a #define in asm/io.h, allow user to select to turn on IOREMAP from the config menu. Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- arch/parisc/Kconfig | 11 +++++++++++ arch/parisc/mm/ioremap.c | 13 ++++++------- include/asm-parisc/io.h | 16 +++++----------- 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 6b3c50964ca9..fc5755d1db57 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -138,6 +138,17 @@ config 64BIT enable this option otherwise. The 64bit kernel is significantly bigger and slower than the 32bit one. +config HPPA_IOREMAP + bool "Enable IOREMAP functionality (EXPERIMENTAL)" + depends on EXPERIMENTAL + default n + help + Enable this option if you want to enable real IOREMAPPING on PA-RISC. + Currently we just "simulate" I/O remapping, and enabling this option + will just crash your machine. + + Say N here, unless you are a real PA-RISC Linux kernel hacker. + config SMP bool "Symmetric multi-processing support" ---help--- diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index edd9a9559cba..028772144191 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -72,7 +72,7 @@ remap_area_pmd(pmd_t *pmd, unsigned long address, unsigned long size, return 0; } -#if USE_HPPA_IOREMAP +#ifdef CONFIG_HPPA_IOREMAP static int remap_area_pages(unsigned long address, unsigned long phys_addr, unsigned long size, unsigned long flags) @@ -114,7 +114,7 @@ remap_area_pages(unsigned long address, unsigned long phys_addr, return error; } -#endif /* USE_HPPA_IOREMAP */ +#endif /* CONFIG_HPPA_IOREMAP */ #ifdef CONFIG_DEBUG_IOREMAP static unsigned long last = 0; @@ -154,8 +154,7 @@ EXPORT_SYMBOL(__raw_bad_addr); */ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) { -#if !(USE_HPPA_IOREMAP) - +#if !defined(CONFIG_HPPA_IOREMAP) unsigned long end = phys_addr + size - 1; /* Support EISA addresses */ if ((phys_addr >= 0x00080000 && end < 0x000fffff) @@ -222,10 +221,10 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l void iounmap(void __iomem *addr) { -#if !(USE_HPPA_IOREMAP) - return; -#else +#ifdef CONFIG_HPPA_IOREMAP if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long __force) addr)); +#else + return; #endif } diff --git a/include/asm-parisc/io.h b/include/asm-parisc/io.h index be0c7234a6da..2fb253255102 100644 --- a/include/asm-parisc/io.h +++ b/include/asm-parisc/io.h @@ -26,11 +26,7 @@ extern unsigned long parisc_vmerge_max_size; */ #ifdef CONFIG_DEBUG_IOREMAP -#ifdef CONFIG_64BIT -#define NYBBLE_SHIFT 60 -#else -#define NYBBLE_SHIFT 28 -#endif +#define NYBBLE_SHIFT (BITS_PER_LONG - 4) extern void gsc_bad_addr(unsigned long addr); extern void __raw_bad_addr(const volatile void __iomem *addr); #define gsc_check_addr(addr) \ @@ -181,13 +177,11 @@ extern inline void * ioremap_nocache(unsigned long offset, unsigned long size) extern void iounmap(void __iomem *addr); /* - * USE_HPPA_IOREMAP is the magic flag to enable or disable real ioremap() + * CONFIG_HPPA_IOREMAP is the magic flag to enable or disable real ioremap() * functionality. It's currently disabled because it may not work on some * machines. */ -#define USE_HPPA_IOREMAP 0 - -#if USE_HPPA_IOREMAP +#ifdef CONFIG_HPPA_IOREMAP static inline unsigned char __raw_readb(const volatile void __iomem *addr) { return (*(volatile unsigned char __force *) (addr)); @@ -221,7 +215,7 @@ static inline void __raw_writeq(unsigned long long b, volatile void __iomem *add { *(volatile unsigned long long __force *) addr = b; } -#else /* !USE_HPPA_IOREMAP */ +#else /* !CONFIG_HPPA_IOREMAP */ static inline unsigned char __raw_readb(const volatile void __iomem *addr) { __raw_check_addr(addr); @@ -271,7 +265,7 @@ static inline void __raw_writeq(unsigned long long b, volatile void __iomem *add gsc_writeq(b, (unsigned long) addr); } -#endif /* !USE_HPPA_IOREMAP */ +#endif /* !CONFIG_HPPA_IOREMAP */ /* readb can never be const, so use __fswab instead of le*_to_cpu */ #define readb(addr) __raw_readb(addr) -- cgit v1.2.3 From 29ef8295327653ff09a56285c35213cd31fa54b3 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 23 Mar 2006 00:32:46 -0700 Subject: [PARISC] Enable ioremap functionality unconditionally Enable CONFIG_HPPA_IOREMAP by default and remove all now unnecessary code. Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- arch/parisc/Kconfig | 11 --------- arch/parisc/mm/ioremap.c | 16 ++----------- include/asm-parisc/io.h | 58 +----------------------------------------------- 3 files changed, 3 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index fc5755d1db57..6b3c50964ca9 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -138,17 +138,6 @@ config 64BIT enable this option otherwise. The 64bit kernel is significantly bigger and slower than the 32bit one. -config HPPA_IOREMAP - bool "Enable IOREMAP functionality (EXPERIMENTAL)" - depends on EXPERIMENTAL - default n - help - Enable this option if you want to enable real IOREMAPPING on PA-RISC. - Currently we just "simulate" I/O remapping, and enabling this option - will just crash your machine. - - Say N here, unless you are a real PA-RISC Linux kernel hacker. - config SMP bool "Symmetric multi-processing support" ---help--- diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index 028772144191..5a22e1cab217 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -72,7 +72,6 @@ remap_area_pmd(pmd_t *pmd, unsigned long address, unsigned long size, return 0; } -#ifdef CONFIG_HPPA_IOREMAP static int remap_area_pages(unsigned long address, unsigned long phys_addr, unsigned long size, unsigned long flags) @@ -114,7 +113,6 @@ remap_area_pages(unsigned long address, unsigned long phys_addr, return error; } -#endif /* CONFIG_HPPA_IOREMAP */ #ifdef CONFIG_DEBUG_IOREMAP static unsigned long last = 0; @@ -154,21 +152,16 @@ EXPORT_SYMBOL(__raw_bad_addr); */ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) { -#if !defined(CONFIG_HPPA_IOREMAP) +#ifdef CONFIG_EISA + #error FIXME. unsigned long end = phys_addr + size - 1; /* Support EISA addresses */ if ((phys_addr >= 0x00080000 && end < 0x000fffff) || (phys_addr >= 0x00500000 && end < 0x03bfffff)) { phys_addr |= 0xfc000000; } - -#ifdef CONFIG_DEBUG_IOREMAP - return (void __iomem *)(phys_addr - (0x1UL << NYBBLE_SHIFT)); -#else - return (void __iomem *)phys_addr; #endif -#else void *addr; struct vm_struct *area; unsigned long offset, last_addr; @@ -216,15 +209,10 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l } return (void __iomem *) (offset + (char *)addr); -#endif } void iounmap(void __iomem *addr) { -#ifdef CONFIG_HPPA_IOREMAP if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long __force) addr)); -#else - return; -#endif } diff --git a/include/asm-parisc/io.h b/include/asm-parisc/io.h index 2fb253255102..bcfe0b09c93f 100644 --- a/include/asm-parisc/io.h +++ b/include/asm-parisc/io.h @@ -176,12 +176,7 @@ extern inline void * ioremap_nocache(unsigned long offset, unsigned long size) extern void iounmap(void __iomem *addr); -/* - * CONFIG_HPPA_IOREMAP is the magic flag to enable or disable real ioremap() - * functionality. It's currently disabled because it may not work on some - * machines. - */ -#ifdef CONFIG_HPPA_IOREMAP + static inline unsigned char __raw_readb(const volatile void __iomem *addr) { return (*(volatile unsigned char __force *) (addr)); @@ -215,57 +210,6 @@ static inline void __raw_writeq(unsigned long long b, volatile void __iomem *add { *(volatile unsigned long long __force *) addr = b; } -#else /* !CONFIG_HPPA_IOREMAP */ -static inline unsigned char __raw_readb(const volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - return gsc_readb((unsigned long) addr); -} -static inline unsigned short __raw_readw(const volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - return gsc_readw((unsigned long) addr); -} -static inline unsigned int __raw_readl(const volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - return gsc_readl((unsigned long) addr); -} -static inline unsigned long long __raw_readq(const volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - return gsc_readq((unsigned long) addr); -} - -static inline void __raw_writeb(unsigned char b, volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - gsc_writeb(b, (unsigned long) addr); -} -static inline void __raw_writew(unsigned short b, volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - gsc_writew(b, (unsigned long) addr); -} -static inline void __raw_writel(unsigned int b, volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - gsc_writel(b, (unsigned long) addr); -} -static inline void __raw_writeq(unsigned long long b, volatile void __iomem *addr) -{ - __raw_check_addr(addr); - - gsc_writeq(b, (unsigned long) addr); -} -#endif /* !CONFIG_HPPA_IOREMAP */ /* readb can never be const, so use __fswab instead of le*_to_cpu */ #define readb(addr) __raw_readb(addr) -- cgit v1.2.3 From a41d3862dfd44a1b09a0f6243bb34773061fd9a2 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 23 Mar 2006 01:07:00 -0700 Subject: [PARISC] Remove obsolete CONFIG_DEBUG_IOREMAP Remove CONFIG_DEBUG_IOREMAP, it's now obsolete and won't work anyway. Remove it from lib/KConfig since it was only available on parisc. Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- arch/parisc/lib/iomap.c | 4 ---- arch/parisc/mm/ioremap.c | 24 ------------------------ include/asm-parisc/io.h | 33 --------------------------------- lib/Kconfig.debug | 13 ------------- 4 files changed, 74 deletions(-) (limited to 'include') diff --git a/arch/parisc/lib/iomap.c b/arch/parisc/lib/iomap.c index 01bec8fcbd0d..f4a811690ab3 100644 --- a/arch/parisc/lib/iomap.c +++ b/arch/parisc/lib/iomap.c @@ -263,11 +263,7 @@ static const struct iomap_ops iomem_ops = { const struct iomap_ops *iomap_ops[8] = { [0] = &ioport_ops, -#ifdef CONFIG_DEBUG_IOREMAP - [6] = &iomem_ops, -#else [7] = &iomem_ops -#endif }; diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index 21a16747e56e..a5967b7d3726 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -114,30 +114,6 @@ remap_area_pages(unsigned long address, unsigned long phys_addr, return error; } -#ifdef CONFIG_DEBUG_IOREMAP -static unsigned long last = 0; - -void gsc_bad_addr(unsigned long addr) -{ - if (time_after(jiffies, last + HZ*10)) { - printk("gsc_foo() called with bad address 0x%lx\n", addr); - dump_stack(); - last = jiffies; - } -} -EXPORT_SYMBOL(gsc_bad_addr); - -void __raw_bad_addr(const volatile void __iomem *addr) -{ - if (time_after(jiffies, last + HZ*10)) { - printk("__raw_foo() called with bad address 0x%p\n", addr); - dump_stack(); - last = jiffies; - } -} -EXPORT_SYMBOL(__raw_bad_addr); -#endif - /* * Generic mapping function (not visible outside): */ diff --git a/include/asm-parisc/io.h b/include/asm-parisc/io.h index bcfe0b09c93f..29da31194b91 100644 --- a/include/asm-parisc/io.h +++ b/include/asm-parisc/io.h @@ -25,31 +25,11 @@ extern unsigned long parisc_vmerge_max_size; * eg dev->hpa or 0xfee00000. */ -#ifdef CONFIG_DEBUG_IOREMAP -#define NYBBLE_SHIFT (BITS_PER_LONG - 4) -extern void gsc_bad_addr(unsigned long addr); -extern void __raw_bad_addr(const volatile void __iomem *addr); -#define gsc_check_addr(addr) \ - if ((addr >> NYBBLE_SHIFT) != 0xf) { \ - gsc_bad_addr(addr); \ - addr |= 0xfUL << NYBBLE_SHIFT; \ - } -#define __raw_check_addr(addr) \ - if (((unsigned long)addr >> NYBBLE_SHIFT) != 0xe) \ - __raw_bad_addr(addr); \ - addr = (void __iomem *)((unsigned long)addr | (0xfUL << NYBBLE_SHIFT)); -#else -#define gsc_check_addr(addr) -#define __raw_check_addr(addr) -#endif - static inline unsigned char gsc_readb(unsigned long addr) { long flags; unsigned char ret; - gsc_check_addr(addr); - __asm__ __volatile__( " rsm 2,%0\n" " ldbx 0(%2),%1\n" @@ -64,8 +44,6 @@ static inline unsigned short gsc_readw(unsigned long addr) long flags; unsigned short ret; - gsc_check_addr(addr); - __asm__ __volatile__( " rsm 2,%0\n" " ldhx 0(%2),%1\n" @@ -79,8 +57,6 @@ static inline unsigned int gsc_readl(unsigned long addr) { u32 ret; - gsc_check_addr(addr); - __asm__ __volatile__( " ldwax 0(%1),%0\n" : "=r" (ret) : "r" (addr) ); @@ -91,7 +67,6 @@ static inline unsigned int gsc_readl(unsigned long addr) static inline unsigned long long gsc_readq(unsigned long addr) { unsigned long long ret; - gsc_check_addr(addr); #ifdef __LP64__ __asm__ __volatile__( @@ -108,8 +83,6 @@ static inline unsigned long long gsc_readq(unsigned long addr) static inline void gsc_writeb(unsigned char val, unsigned long addr) { long flags; - gsc_check_addr(addr); - __asm__ __volatile__( " rsm 2,%0\n" " stbs %1,0(%2)\n" @@ -120,8 +93,6 @@ static inline void gsc_writeb(unsigned char val, unsigned long addr) static inline void gsc_writew(unsigned short val, unsigned long addr) { long flags; - gsc_check_addr(addr); - __asm__ __volatile__( " rsm 2,%0\n" " sths %1,0(%2)\n" @@ -131,8 +102,6 @@ static inline void gsc_writew(unsigned short val, unsigned long addr) static inline void gsc_writel(unsigned int val, unsigned long addr) { - gsc_check_addr(addr); - __asm__ __volatile__( " stwas %0,0(%1)\n" : : "r" (val), "r" (addr) ); @@ -140,8 +109,6 @@ static inline void gsc_writel(unsigned int val, unsigned long addr) static inline void gsc_writeq(unsigned long long val, unsigned long addr) { - gsc_check_addr(addr); - #ifdef __LP64__ __asm__ __volatile__( " stda %0,0(%1)\n" diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6e8a60f67c7a..d57fd9181b18 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -157,19 +157,6 @@ config DEBUG_INFO If unsure, say N. -config DEBUG_IOREMAP - bool "Enable ioremap() debugging" - depends on DEBUG_KERNEL && PARISC - help - Enabling this option will cause the kernel to distinguish between - ioremapped and physical addresses. It will print a backtrace (at - most one every 10 seconds), hopefully allowing you to see which - drivers need work. Fixing all these problems is a prerequisite - for turning on USE_HPPA_IOREMAP. The warnings are harmless; - the kernel has enough information to fix the broken drivers - automatically, but we'd like to make it more efficient by not - having to do that. - config DEBUG_FS bool "Debug Filesystem" depends on SYSFS -- cgit v1.2.3 From 10267cdd0c2dee46a3f59d93fbfac7229d416dba Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 26 Mar 2006 01:54:16 -0700 Subject: [PARISC] Fixup CONFIG_EISA a bit Fix up some ISA/EISA stuff. (Note: isa_ accessors have been removed from asm/io.h) Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- arch/parisc/mm/ioremap.c | 8 +++----- include/asm-parisc/pci.h | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index a5967b7d3726..5067ea90d91a 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -135,11 +135,9 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l #ifdef CONFIG_EISA unsigned long end = phys_addr + size - 1; /* Support EISA addresses */ - if ((phys_addr >= 0x00080000 && end < 0x000fffff) - || (phys_addr >= 0x00500000 && end < 0x03bfffff)) { - phys_addr |= 0xfc000000; -#warning "FIXME: EISA regions do not work yet..." - return NULL; /* XXX */ + if ((phys_addr >= 0x00080000 && end < 0x000fffff) || + (phys_addr >= 0x00500000 && end < 0x03bfffff)) { + phys_addr |= F_EXTEND(0xfc000000); } #endif diff --git a/include/asm-parisc/pci.h b/include/asm-parisc/pci.h index fe7f6a2f5aa7..77bbafb7f73e 100644 --- a/include/asm-parisc/pci.h +++ b/include/asm-parisc/pci.h @@ -289,4 +289,9 @@ static inline void pcibios_add_platform_entries(struct pci_dev *dev) { } +static inline void pcibios_penalize_isa_irq(int irq, int active) +{ + /* We don't need to penalize isa irq's */ +} + #endif /* __ASM_PARISC_PCI_H */ -- cgit v1.2.3 From bc8846c522264d2522b0082321ec8c2051a4536f Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Fri, 24 Mar 2006 21:22:02 -0700 Subject: [PARISC] More useful readwrite lock helpers spinlock.c needs _can_lock helpers. Rewrite _is_locked helpers to be _can_lock helpers. Signed-off-by: Kyle McMartin --- include/asm-parisc/spinlock.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-parisc/spinlock.h b/include/asm-parisc/spinlock.h index 16c2ac075fc5..a93960e232cf 100644 --- a/include/asm-parisc/spinlock.h +++ b/include/asm-parisc/spinlock.h @@ -134,14 +134,22 @@ static __inline__ int __raw_write_trylock(raw_rwlock_t *rw) return 1; } -static __inline__ int __raw_is_read_locked(raw_rwlock_t *rw) +/* + * read_can_lock - would read_trylock() succeed? + * @lock: the rwlock in question. + */ +static __inline__ int __raw_read_can_lock(raw_rwlock_t *rw) { - return rw->counter > 0; + return rw->counter >= 0; } -static __inline__ int __raw_is_write_locked(raw_rwlock_t *rw) +/* + * write_can_lock - would write_trylock() succeed? + * @lock: the rwlock in question. + */ +static __inline__ int __raw_write_can_lock(raw_rwlock_t *rw) { - return rw->counter < 0; + return !rw->counter; } #endif /* __ASM_SPINLOCK_H */ -- cgit v1.2.3 From 50a34dbd612925f2ec55b1781632835ef36b97d5 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Fri, 24 Mar 2006 21:24:21 -0700 Subject: [PARISC] Add PREEMPT support Signed-off-by: Kyle McMartin --- arch/parisc/Kconfig | 6 +----- arch/parisc/kernel/entry.S | 39 ++++++++++++++++++++++++++++++++++++--- include/asm-parisc/thread_info.h | 3 ++- 3 files changed, 39 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 6b3c50964ca9..2fdf21989dc2 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -177,14 +177,10 @@ config ARCH_DISCONTIGMEM_DEFAULT def_bool y depends on ARCH_DISCONTIGMEM_ENABLE +source "kernel/Kconfig.preempt" source "kernel/Kconfig.hz" source "mm/Kconfig" -config PREEMPT - bool -# bool "Preemptible Kernel" - default n - config COMPAT def_bool y depends on 64BIT diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index 6d17c0a3431b..7c95d7663c29 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S @@ -1014,14 +1014,21 @@ intr_restore: nop nop +#ifndef CONFIG_PREEMPT +# define intr_do_preempt intr_restore +#endif /* !CONFIG_PREEMPT */ + .import schedule,code intr_do_resched: - /* Only do reschedule if we are returning to user space */ + /* Only call schedule on return to userspace. If we're returning + * to kernel space, we may schedule if CONFIG_PREEMPT, otherwise + * we jump back to intr_restore. + */ LDREG PT_IASQ0(%r16), %r20 - CMPIB= 0,%r20,intr_restore /* backward */ + CMPIB= 0, %r20, intr_do_preempt nop LDREG PT_IASQ1(%r16), %r20 - CMPIB= 0,%r20,intr_restore /* backward */ + CMPIB= 0, %r20, intr_do_preempt nop #ifdef CONFIG_64BIT @@ -1037,6 +1044,32 @@ intr_do_resched: #endif ldo R%intr_check_sig(%r2), %r2 + /* preempt the current task on returning to kernel + * mode from an interrupt, iff need_resched is set, + * and preempt_count is 0. otherwise, we continue on + * our merry way back to the current running task. + */ +#ifdef CONFIG_PREEMPT + .import preempt_schedule_irq,code +intr_do_preempt: + rsm PSW_SM_I, %r0 /* disable interrupts */ + + /* current_thread_info()->preempt_count */ + mfctl %cr30, %r1 + LDREG TI_PRE_COUNT(%r1), %r19 + CMPIB<> 0, %r19, intr_restore /* if preempt_count > 0 */ + nop /* prev insn branched backwards */ + + /* check if we interrupted a critical path */ + LDREG PT_PSW(%r16), %r20 + bb,<,n %r20, 31 - PSW_SM_I, intr_restore + nop + + BL preempt_schedule_irq, %r2 + nop + + b intr_restore /* ssm PSW_SM_I done by intr_restore */ +#endif /* CONFIG_PREEMPT */ .import do_signal,code intr_do_signal: diff --git a/include/asm-parisc/thread_info.h b/include/asm-parisc/thread_info.h index ac32f140b83a..f2f83b04cd8b 100644 --- a/include/asm-parisc/thread_info.h +++ b/include/asm-parisc/thread_info.h @@ -49,7 +49,8 @@ struct thread_info { #endif /* !__ASSEMBLY */ -#define PREEMPT_ACTIVE 0x10000000 +#define PREEMPT_ACTIVE_BIT 28 +#define PREEMPT_ACTIVE (1 << PREEMPT_ACTIVE_BIT) /* * thread information flags -- cgit v1.2.3 From 2746ae14fe55f9483ae94ef89d4495d0eb8ee03d Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Wed, 29 Mar 2006 07:04:49 -0700 Subject: [PARISC] Make local_t use atomic_long_t As done in asm-generic/local.h in mainline. Otherwise local_t was 32-bit even on a 64-bit kernel. Signed-off-by: Kyle McMartin --- include/asm-parisc/local.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/asm-parisc/local.h b/include/asm-parisc/local.h index 892b3b2c4962..d0f550912755 100644 --- a/include/asm-parisc/local.h +++ b/include/asm-parisc/local.h @@ -4,16 +4,16 @@ #include #include -typedef atomic_t local_t; +typedef atomic_long_t local_t; -#define LOCAL_INIT(i) ATOMIC_INIT(i) -#define local_read(v) atomic_read(v) -#define local_set(v,i) atomic_set(v,i) +#define LOCAL_INIT(i) ATOMIC_LONG_INIT(i) +#define local_read(v) atomic_long_read(v) +#define local_set(v,i) atomic_long_set(v,i) -#define local_inc(v) atomic_inc(v) -#define local_dec(v) atomic_dec(v) -#define local_add(i, v) atomic_add(i, v) -#define local_sub(i, v) atomic_sub(i, v) +#define local_inc(v) atomic_long_inc(v) +#define local_dec(v) atomic_long_dec(v) +#define local_add(i, v) atomic_long_add(i, v) +#define local_sub(i, v) atomic_long_sub(i, v) #define __local_inc(v) ((v)->counter++) #define __local_dec(v) ((v)->counter--) -- cgit v1.2.3 From 4da9f131a74d12de56c44da6d522a9116da06805 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Wed, 29 Mar 2006 19:47:32 -0500 Subject: [PARISC] Add atomic_sub_and_test Define atomic_sub_and_test to fix build failures. Signed-off-by: Kyle McMartin --- include/asm-parisc/atomic.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/asm-parisc/atomic.h b/include/asm-parisc/atomic.h index 4dc7253ff5d0..403ea97316cf 100644 --- a/include/asm-parisc/atomic.h +++ b/include/asm-parisc/atomic.h @@ -210,6 +210,8 @@ static __inline__ int atomic_read(const atomic_t *v) #define atomic_dec_and_test(v) (atomic_dec_return(v) == 0) +#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0) + #define ATOMIC_INIT(i) ((atomic_t) { (i) }) #define smp_mb__before_atomic_dec() smp_mb() @@ -267,6 +269,7 @@ atomic64_read(const atomic64_t *v) #define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0) #define atomic64_dec_and_test(v) (atomic64_dec_return(v) == 0) +#define atomic64_sub_and_test(i,v) (atomic64_sub_return((i),(v)) == 0) #endif /* __LP64__ */ -- cgit v1.2.3 From 5274f052e7b3dbd81935772eb551dfd0325dfa9d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 30 Mar 2006 15:15:30 +0200 Subject: [PATCH] Introduce sys_splice() system call This adds support for the sys_splice system call. Using a pipe as a transport, it can connect to files or sockets (latter as output only). From the splice.c comments: "splice": joining two ropes together by interweaving their strands. This is the "extended pipe" functionality, where a pipe is used as an arbitrary in-memory buffer. Think of a pipe as a small kernel buffer that you can use to transfer data from one end to the other. The traditional unix read/write is extended with a "splice()" operation that transfers data buffers to or from a pipe buffer. Named by Larry McVoy, original implementation from Linus, extended by Jens to support splicing to files and fixing the initial implementation bugs. Signed-off-by: Jens Axboe Signed-off-by: Linus Torvalds --- arch/i386/kernel/syscall_table.S | 1 + arch/ia64/kernel/entry.S | 1 + fs/Makefile | 2 +- fs/ext2/file.c | 2 + fs/ext3/file.c | 2 + fs/pipe.c | 33 ++- fs/reiserfs/file.c | 2 + fs/splice.c | 612 +++++++++++++++++++++++++++++++++++++++ include/asm-i386/unistd.h | 3 +- include/asm-ia64/unistd.h | 3 +- include/asm-powerpc/unistd.h | 3 +- include/asm-x86_64/unistd.h | 4 +- include/linux/fs.h | 4 + include/linux/syscalls.h | 2 + net/socket.c | 6 +- 15 files changed, 669 insertions(+), 11 deletions(-) create mode 100644 fs/splice.c (limited to 'include') diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 326595f3fa4d..ce3ef4fa0551 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -312,3 +312,4 @@ ENTRY(sys_call_table) .long sys_unshare /* 310 */ .long sys_set_robust_list .long sys_get_robust_list + .long sys_splice diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 0e3eda99e549..750e8e7fbdc3 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1605,5 +1605,6 @@ sys_call_table: data8 sys_ni_syscall // reserved for pselect data8 sys_ni_syscall // 1295 reserved for ppoll data8 sys_unshare + data8 sys_splice .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/fs/Makefile b/fs/Makefile index 080b3867be4d..f3a4f7077175 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -10,7 +10,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ - ioprio.o pnode.o drop_caches.o + ioprio.o pnode.o drop_caches.o splice.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 509cceca04db..23e2c7ccec1d 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -53,6 +53,8 @@ const struct file_operations ext2_file_operations = { .readv = generic_file_readv, .writev = generic_file_writev, .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, }; #ifdef CONFIG_EXT2_FS_XIP diff --git a/fs/ext3/file.c b/fs/ext3/file.c index 783a796220bb..1efefb630ea9 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -119,6 +119,8 @@ const struct file_operations ext3_file_operations = { .release = ext3_release_file, .fsync = ext3_sync_file, .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, }; struct inode_operations ext3_file_inode_operations = { diff --git a/fs/pipe.c b/fs/pipe.c index e2f4f1d9ffc2..2414bf270db6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -94,11 +95,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buff { struct page *page = buf->page; - if (info->tmp_page) { - __free_page(page); + /* + * If nobody else uses this page, and we don't already have a + * temporary page, let's keep track of it as a one-deep + * allocation cache + */ + if (page_count(page) == 1 && !info->tmp_page) { + info->tmp_page = page; return; } - info->tmp_page = page; + + /* + * Otherwise just release our reference to it + */ + page_cache_release(page); } static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) @@ -152,6 +162,11 @@ pipe_readv(struct file *filp, const struct iovec *_iov, chars = total_len; addr = ops->map(filp, info, buf); + if (IS_ERR(addr)) { + if (!ret) + ret = PTR_ERR(addr); + break; + } error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); ops->unmap(info, buf); if (unlikely(error)) { @@ -254,8 +269,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov, struct pipe_buf_operations *ops = buf->ops; int offset = buf->offset + buf->len; if (ops->can_merge && offset + chars <= PAGE_SIZE) { - void *addr = ops->map(filp, info, buf); - int error = pipe_iov_copy_from_user(offset + addr, iov, chars); + void *addr; + int error; + + addr = ops->map(filp, info, buf); + if (IS_ERR(addr)) { + error = PTR_ERR(addr); + goto out; + } + error = pipe_iov_copy_from_user(offset + addr, iov, + chars); ops->unmap(info, buf); ret = error; do_wakeup = 1; diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 010094d14da6..cf6e1cf40351 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1576,6 +1576,8 @@ const struct file_operations reiserfs_file_operations = { .sendfile = generic_file_sendfile, .aio_read = generic_file_aio_read, .aio_write = reiserfs_aio_write, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, }; struct inode_operations reiserfs_file_inode_operations = { diff --git a/fs/splice.c b/fs/splice.c new file mode 100644 index 000000000000..efa47c1c4e13 --- /dev/null +++ b/fs/splice.c @@ -0,0 +1,612 @@ +/* + * "splice": joining two ropes together by interweaving their strands. + * + * This is the "extended pipe" functionality, where a pipe is used as + * an arbitrary in-memory buffer. Think of a pipe as a small kernel + * buffer that you can use to transfer data from one end to the other. + * + * The traditional unix read/write is extended with a "splice()" operation + * that transfers data buffers to or from a pipe buffer. + * + * Named by Larry McVoy, original implementation from Linus, extended by + * Jens to support splicing to files and fixing the initial implementation + * bugs. + * + * Copyright (C) 2005 Jens Axboe + * Copyright (C) 2005 Linus Torvalds + * + */ +#include +#include +#include +#include +#include + +/* + * Passed to the actors + */ +struct splice_desc { + unsigned int len, total_len; /* current and remaining length */ + unsigned int flags; /* splice flags */ + struct file *file; /* file to read/write */ + loff_t pos; /* file position */ +}; + +static void page_cache_pipe_buf_release(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + page_cache_release(buf->page); + buf->page = NULL; +} + +static void *page_cache_pipe_buf_map(struct file *file, + struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + struct page *page = buf->page; + + lock_page(page); + + if (!PageUptodate(page)) { + unlock_page(page); + return ERR_PTR(-EIO); + } + + if (!page->mapping) { + unlock_page(page); + return ERR_PTR(-ENODATA); + } + + return kmap(buf->page); +} + +static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + unlock_page(buf->page); + kunmap(buf->page); +} + +static struct pipe_buf_operations page_cache_pipe_buf_ops = { + .can_merge = 0, + .map = page_cache_pipe_buf_map, + .unmap = page_cache_pipe_buf_unmap, + .release = page_cache_pipe_buf_release, +}; + +static ssize_t move_to_pipe(struct inode *inode, struct page **pages, + int nr_pages, unsigned long offset, + unsigned long len) +{ + struct pipe_inode_info *info; + int ret, do_wakeup, i; + + ret = 0; + do_wakeup = 0; + i = 0; + + mutex_lock(PIPE_MUTEX(*inode)); + + info = inode->i_pipe; + for (;;) { + int bufs; + + if (!PIPE_READERS(*inode)) { + send_sig(SIGPIPE, current, 0); + if (!ret) + ret = -EPIPE; + break; + } + + bufs = info->nrbufs; + if (bufs < PIPE_BUFFERS) { + int newbuf = (info->curbuf + bufs) & (PIPE_BUFFERS - 1); + struct pipe_buffer *buf = info->bufs + newbuf; + struct page *page = pages[i++]; + unsigned long this_len; + + this_len = PAGE_CACHE_SIZE - offset; + if (this_len > len) + this_len = len; + + buf->page = page; + buf->offset = offset; + buf->len = this_len; + buf->ops = &page_cache_pipe_buf_ops; + info->nrbufs = ++bufs; + do_wakeup = 1; + + ret += this_len; + len -= this_len; + offset = 0; + if (!--nr_pages) + break; + if (!len) + break; + if (bufs < PIPE_BUFFERS) + continue; + + break; + } + + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + if (do_wakeup) { + wake_up_interruptible_sync(PIPE_WAIT(*inode)); + kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, + POLL_IN); + do_wakeup = 0; + } + + PIPE_WAITING_WRITERS(*inode)++; + pipe_wait(inode); + PIPE_WAITING_WRITERS(*inode)--; + } + + mutex_unlock(PIPE_MUTEX(*inode)); + + if (do_wakeup) { + wake_up_interruptible(PIPE_WAIT(*inode)); + kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN); + } + + while (i < nr_pages) + page_cache_release(pages[i++]); + + return ret; +} + +static int __generic_file_splice_read(struct file *in, struct inode *pipe, + size_t len) +{ + struct address_space *mapping = in->f_mapping; + unsigned int offset, nr_pages; + struct page *pages[PIPE_BUFFERS], *shadow[PIPE_BUFFERS]; + struct page *page; + pgoff_t index, pidx; + int i, j; + + index = in->f_pos >> PAGE_CACHE_SHIFT; + offset = in->f_pos & ~PAGE_CACHE_MASK; + nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + if (nr_pages > PIPE_BUFFERS) + nr_pages = PIPE_BUFFERS; + + /* + * initiate read-ahead on this page range + */ + do_page_cache_readahead(mapping, in, index, nr_pages); + + /* + * Get as many pages from the page cache as possible.. + * Start IO on the page cache entries we create (we + * can assume that any pre-existing ones we find have + * already had IO started on them). + */ + i = find_get_pages(mapping, index, nr_pages, pages); + + /* + * common case - we found all pages and they are contiguous, + * kick them off + */ + if (i && (pages[i - 1]->index == index + i - 1)) + goto splice_them; + + /* + * fill shadow[] with pages at the right locations, so we only + * have to fill holes + */ + memset(shadow, 0, i * sizeof(struct page *)); + for (j = 0, pidx = index; j < i; pidx++, j++) + shadow[pages[j]->index - pidx] = pages[j]; + + /* + * now fill in the holes + */ + for (i = 0, pidx = index; i < nr_pages; pidx++, i++) { + int error; + + if (shadow[i]) + continue; + + /* + * no page there, look one up / create it + */ + page = find_or_create_page(mapping, pidx, + mapping_gfp_mask(mapping)); + if (!page) + break; + + if (PageUptodate(page)) + unlock_page(page); + else { + error = mapping->a_ops->readpage(in, page); + + if (unlikely(error)) { + page_cache_release(page); + break; + } + } + shadow[i] = page; + } + + if (!i) { + for (i = 0; i < nr_pages; i++) { + if (shadow[i]) + page_cache_release(shadow[i]); + } + return 0; + } + + memcpy(pages, shadow, i * sizeof(struct page *)); + + /* + * Now we splice them into the pipe.. + */ +splice_them: + return move_to_pipe(pipe, pages, i, offset, len); +} + +ssize_t generic_file_splice_read(struct file *in, struct inode *pipe, + size_t len, unsigned int flags) +{ + ssize_t spliced; + int ret; + + ret = 0; + spliced = 0; + while (len) { + ret = __generic_file_splice_read(in, pipe, len); + + if (ret <= 0) + break; + + in->f_pos += ret; + len -= ret; + spliced += ret; + } + + if (spliced) + return spliced; + + return ret; +} + +/* + * Send 'len' bytes to socket from 'file' at position 'pos' using sendpage(). + */ +static int pipe_to_sendpage(struct pipe_inode_info *info, + struct pipe_buffer *buf, struct splice_desc *sd) +{ + struct file *file = sd->file; + loff_t pos = sd->pos; + unsigned int offset; + ssize_t ret; + void *ptr; + + /* + * sub-optimal, but we are limited by the pipe ->map. we don't + * need a kmap'ed buffer here, we just want to make sure we + * have the page pinned if the pipe page originates from the + * page cache + */ + ptr = buf->ops->map(file, info, buf); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + + offset = pos & ~PAGE_CACHE_MASK; + + ret = file->f_op->sendpage(file, buf->page, offset, sd->len, &pos, + sd->len < sd->total_len); + + buf->ops->unmap(info, buf); + if (ret == sd->len) + return 0; + + return -EIO; +} + +/* + * This is a little more tricky than the file -> pipe splicing. There are + * basically three cases: + * + * - Destination page already exists in the address space and there + * are users of it. For that case we have no other option that + * copying the data. Tough luck. + * - Destination page already exists in the address space, but there + * are no users of it. Make sure it's uptodate, then drop it. Fall + * through to last case. + * - Destination page does not exist, we can add the pipe page to + * the page cache and avoid the copy. + * + * For now we just do the slower thing and always copy pages over, it's + * easier than migrating pages from the pipe to the target file. For the + * case of doing file | file splicing, the migrate approach had some LRU + * nastiness... + */ +static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, + struct splice_desc *sd) +{ + struct file *file = sd->file; + struct address_space *mapping = file->f_mapping; + unsigned int offset; + struct page *page; + char *src, *dst; + pgoff_t index; + int ret; + + /* + * after this, page will be locked and unmapped + */ + src = buf->ops->map(file, info, buf); + if (IS_ERR(src)) + return PTR_ERR(src); + + index = sd->pos >> PAGE_CACHE_SHIFT; + offset = sd->pos & ~PAGE_CACHE_MASK; + +find_page: + ret = -ENOMEM; + page = find_or_create_page(mapping, index, mapping_gfp_mask(mapping)); + if (!page) + goto out; + + /* + * If the page is uptodate, it is also locked. If it isn't + * uptodate, we can mark it uptodate if we are filling the + * full page. Otherwise we need to read it in first... + */ + if (!PageUptodate(page)) { + if (sd->len < PAGE_CACHE_SIZE) { + ret = mapping->a_ops->readpage(file, page); + if (unlikely(ret)) + goto out; + + lock_page(page); + + if (!PageUptodate(page)) { + /* + * page got invalidated, repeat + */ + if (!page->mapping) { + unlock_page(page); + page_cache_release(page); + goto find_page; + } + ret = -EIO; + goto out; + } + } else { + WARN_ON(!PageLocked(page)); + SetPageUptodate(page); + } + } + + ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); + if (ret) + goto out; + + dst = kmap_atomic(page, KM_USER0); + memcpy(dst + offset, src + buf->offset, sd->len); + flush_dcache_page(page); + kunmap_atomic(dst, KM_USER0); + + ret = mapping->a_ops->commit_write(file, page, 0, sd->len); + if (ret < 0) + goto out; + + set_page_dirty(page); + ret = write_one_page(page, 0); +out: + if (ret < 0) + unlock_page(page); + page_cache_release(page); + buf->ops->unmap(info, buf); + return ret; +} + +typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *, + struct splice_desc *); + +static ssize_t move_from_pipe(struct inode *inode, struct file *out, + size_t len, unsigned int flags, + splice_actor *actor) +{ + struct pipe_inode_info *info; + int ret, do_wakeup, err; + struct splice_desc sd; + + ret = 0; + do_wakeup = 0; + + sd.total_len = len; + sd.flags = flags; + sd.file = out; + sd.pos = out->f_pos; + + mutex_lock(PIPE_MUTEX(*inode)); + + info = inode->i_pipe; + for (;;) { + int bufs = info->nrbufs; + + if (bufs) { + int curbuf = info->curbuf; + struct pipe_buffer *buf = info->bufs + curbuf; + struct pipe_buf_operations *ops = buf->ops; + + sd.len = buf->len; + if (sd.len > sd.total_len) + sd.len = sd.total_len; + + err = actor(info, buf, &sd); + if (err) { + if (!ret && err != -ENODATA) + ret = err; + + break; + } + + ret += sd.len; + buf->offset += sd.len; + buf->len -= sd.len; + if (!buf->len) { + buf->ops = NULL; + ops->release(info, buf); + curbuf = (curbuf + 1) & (PIPE_BUFFERS - 1); + info->curbuf = curbuf; + info->nrbufs = --bufs; + do_wakeup = 1; + } + + sd.pos += sd.len; + sd.total_len -= sd.len; + if (!sd.total_len) + break; + } + + if (bufs) + continue; + if (!PIPE_WRITERS(*inode)) + break; + if (!PIPE_WAITING_WRITERS(*inode)) { + if (ret) + break; + } + + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + if (do_wakeup) { + wake_up_interruptible_sync(PIPE_WAIT(*inode)); + kill_fasync(PIPE_FASYNC_WRITERS(*inode),SIGIO,POLL_OUT); + do_wakeup = 0; + } + + pipe_wait(inode); + } + + mutex_unlock(PIPE_MUTEX(*inode)); + + if (do_wakeup) { + wake_up_interruptible(PIPE_WAIT(*inode)); + kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT); + } + + mutex_lock(&out->f_mapping->host->i_mutex); + out->f_pos = sd.pos; + mutex_unlock(&out->f_mapping->host->i_mutex); + return ret; + +} + +ssize_t generic_file_splice_write(struct inode *inode, struct file *out, + size_t len, unsigned int flags) +{ + return move_from_pipe(inode, out, len, flags, pipe_to_file); +} + +ssize_t generic_splice_sendpage(struct inode *inode, struct file *out, + size_t len, unsigned int flags) +{ + return move_from_pipe(inode, out, len, flags, pipe_to_sendpage); +} + +static long do_splice_from(struct inode *pipe, struct file *out, size_t len, + unsigned int flags) +{ + loff_t pos; + int ret; + + if (!out->f_op || !out->f_op->splice_write) + return -EINVAL; + + if (!(out->f_mode & FMODE_WRITE)) + return -EBADF; + + pos = out->f_pos; + ret = rw_verify_area(WRITE, out, &pos, len); + if (unlikely(ret < 0)) + return ret; + + return out->f_op->splice_write(pipe, out, len, flags); +} + +static long do_splice_to(struct file *in, struct inode *pipe, size_t len, + unsigned int flags) +{ + loff_t pos, isize, left; + int ret; + + if (!in->f_op || !in->f_op->splice_read) + return -EINVAL; + + if (!(in->f_mode & FMODE_READ)) + return -EBADF; + + pos = in->f_pos; + ret = rw_verify_area(READ, in, &pos, len); + if (unlikely(ret < 0)) + return ret; + + isize = i_size_read(in->f_mapping->host); + if (unlikely(in->f_pos >= isize)) + return 0; + + left = isize - in->f_pos; + if (left < len) + len = left; + + return in->f_op->splice_read(in, pipe, len, flags); +} + +static long do_splice(struct file *in, struct file *out, size_t len, + unsigned int flags) +{ + struct inode *pipe; + + pipe = in->f_dentry->d_inode; + if (pipe->i_pipe) + return do_splice_from(pipe, out, len, flags); + + pipe = out->f_dentry->d_inode; + if (pipe->i_pipe) + return do_splice_to(in, pipe, len, flags); + + return -EINVAL; +} + +asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags) +{ + long error; + struct file *in, *out; + int fput_in, fput_out; + + if (unlikely(!len)) + return 0; + + error = -EBADF; + in = fget_light(fdin, &fput_in); + if (in) { + if (in->f_mode & FMODE_READ) { + out = fget_light(fdout, &fput_out); + if (out) { + if (out->f_mode & FMODE_WRITE) + error = do_splice(in, out, len, flags); + fput_light(out, fput_out); + } + } + + fput_light(in, fput_in); + } + + return error; +} diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 014e3562895b..789e9bdd0a40 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -318,8 +318,9 @@ #define __NR_unshare 310 #define __NR_set_robust_list 311 #define __NR_get_robust_list 312 +#define __NR_sys_splice 313 -#define NR_syscalls 313 +#define NR_syscalls 314 /* * user-visible error numbers are in the range -1 - -128: see diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 019956c613e4..36070c1014d8 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -285,12 +285,13 @@ #define __NR_faccessat 1293 /* 1294, 1295 reserved for pselect/ppoll */ #define __NR_unshare 1296 +#define __NR_splice 1297 #ifdef __KERNEL__ #include -#define NR_syscalls 273 /* length of syscall table */ +#define NR_syscalls 274 /* length of syscall table */ #define __ARCH_WANT_SYS_RT_SIGACTION diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 1e990747dce7..536ba0873052 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -301,8 +301,9 @@ #define __NR_pselect6 280 #define __NR_ppoll 281 #define __NR_unshare 282 +#define __NR_splice 283 -#define __NR_syscalls 283 +#define __NR_syscalls 284 #ifdef __KERNEL__ #define __NR__exit __NR_exit diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index fcc516353087..f21ff2c1e960 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -609,8 +609,10 @@ __SYSCALL(__NR_unshare, sys_unshare) __SYSCALL(__NR_set_robust_list, sys_set_robust_list) #define __NR_get_robust_list 274 __SYSCALL(__NR_get_robust_list, sys_get_robust_list) +#define __NR_splice 275 +__SYSCALL(__NR_splice, sys_splice) -#define __NR_syscall_max __NR_get_robust_list +#define __NR_syscall_max __NR_splice #ifndef __NO_STUBS diff --git a/include/linux/fs.h b/include/linux/fs.h index 408fe89498f4..20fa5f6d7269 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1032,6 +1032,8 @@ struct file_operations { int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); + ssize_t (*splice_write)(struct inode *, struct file *, size_t, unsigned int); + ssize_t (*splice_read)(struct file *, struct inode *, size_t, unsigned int); }; struct inode_operations { @@ -1609,6 +1611,8 @@ extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor extern void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *, struct file *, loff_t *, read_descriptor_t *, read_actor_t); +extern ssize_t generic_file_splice_read(struct file *, struct inode *, size_t, unsigned int); +extern ssize_t generic_file_splice_write(struct inode *, struct file *, size_t, unsigned int); extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov, diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e487e3b60f60..e78ffc7d5b56 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -569,5 +569,7 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename, asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode); asmlinkage long sys_unshare(unsigned long unshare_flags); +asmlinkage long sys_splice(int fdin, int fdout, size_t len, + unsigned int flags); #endif diff --git a/net/socket.c b/net/socket.c index fcd77eac0ccf..b13042f68c02 100644 --- a/net/socket.c +++ b/net/socket.c @@ -119,6 +119,9 @@ static ssize_t sock_writev(struct file *file, const struct iovec *vector, static ssize_t sock_sendpage(struct file *file, struct page *page, int offset, size_t size, loff_t *ppos, int more); +extern ssize_t generic_splice_sendpage(struct inode *inode, struct file *out, + size_t len, unsigned int flags); + /* * Socket files have a set of 'special' operations as well as the generic file ones. These don't appear @@ -141,7 +144,8 @@ static struct file_operations socket_file_ops = { .fasync = sock_fasync, .readv = sock_readv, .writev = sock_writev, - .sendpage = sock_sendpage + .sendpage = sock_sendpage, + .splice_write = generic_splice_sendpage, }; /* -- cgit v1.2.3 From 5abc97aa25b2c41413b3a520faee83f2282d9f18 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 30 Mar 2006 15:16:46 +0200 Subject: [PATCH] splice: add support for SPLICE_F_MOVE flag This enables the caller to migrate pages from one address space page cache to another. In buzz word marketing, you can do zero-copy file copies! Signed-off-by: Jens Axboe Signed-off-by: Linus Torvalds --- fs/pipe.c | 8 +++ fs/splice.c | 121 ++++++++++++++++++++++++++++++++-------------- include/linux/pipe_fs_i.h | 8 +++ 3 files changed, 100 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/fs/pipe.c b/fs/pipe.c index 2414bf270db6..109a102c150d 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -121,11 +121,19 @@ static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer kunmap(buf->page); } +static int anon_pipe_buf_steal(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + buf->stolen = 1; + return 0; +} + static struct pipe_buf_operations anon_pipe_buf_ops = { .can_merge = 1, .map = anon_pipe_buf_map, .unmap = anon_pipe_buf_unmap, .release = anon_pipe_buf_release, + .steal = anon_pipe_buf_steal, }; static ssize_t diff --git a/fs/splice.c b/fs/splice.c index efa47c1c4e13..4a026f95884f 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * Passed to the actors @@ -32,11 +33,37 @@ struct splice_desc { loff_t pos; /* file position */ }; +static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + struct page *page = buf->page; + + WARN_ON(!PageLocked(page)); + WARN_ON(!PageUptodate(page)); + + if (!remove_mapping(page_mapping(page), page)) + return 1; + + if (PageLRU(page)) { + struct zone *zone = page_zone(page); + + spin_lock_irq(&zone->lru_lock); + BUG_ON(!PageLRU(page)); + __ClearPageLRU(page); + del_page_from_lru(zone, page); + spin_unlock_irq(&zone->lru_lock); + } + + buf->stolen = 1; + return 0; +} + static void page_cache_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_release(buf->page); buf->page = NULL; + buf->stolen = 0; } static void *page_cache_pipe_buf_map(struct file *file, @@ -63,7 +90,8 @@ static void *page_cache_pipe_buf_map(struct file *file, static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer *buf) { - unlock_page(buf->page); + if (!buf->stolen) + unlock_page(buf->page); kunmap(buf->page); } @@ -72,6 +100,7 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { .map = page_cache_pipe_buf_map, .unmap = page_cache_pipe_buf_unmap, .release = page_cache_pipe_buf_release, + .steal = page_cache_pipe_buf_steal, }; static ssize_t move_to_pipe(struct inode *inode, struct page **pages, @@ -336,8 +365,8 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, struct address_space *mapping = file->f_mapping; unsigned int offset; struct page *page; - char *src, *dst; pgoff_t index; + char *src; int ret; /* @@ -350,40 +379,54 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, index = sd->pos >> PAGE_CACHE_SHIFT; offset = sd->pos & ~PAGE_CACHE_MASK; -find_page: - ret = -ENOMEM; - page = find_or_create_page(mapping, index, mapping_gfp_mask(mapping)); - if (!page) - goto out; - /* - * If the page is uptodate, it is also locked. If it isn't - * uptodate, we can mark it uptodate if we are filling the - * full page. Otherwise we need to read it in first... + * reuse buf page, if SPLICE_F_MOVE is set */ - if (!PageUptodate(page)) { - if (sd->len < PAGE_CACHE_SIZE) { - ret = mapping->a_ops->readpage(file, page); - if (unlikely(ret)) - goto out; - - lock_page(page); - - if (!PageUptodate(page)) { - /* - * page got invalidated, repeat - */ - if (!page->mapping) { - unlock_page(page); - page_cache_release(page); - goto find_page; + if (sd->flags & SPLICE_F_MOVE) { + if (buf->ops->steal(info, buf)) + goto find_page; + + page = buf->page; + if (add_to_page_cache_lru(page, mapping, index, + mapping_gfp_mask(mapping))) + goto find_page; + } else { +find_page: + ret = -ENOMEM; + page = find_or_create_page(mapping, index, + mapping_gfp_mask(mapping)); + if (!page) + goto out; + + /* + * If the page is uptodate, it is also locked. If it isn't + * uptodate, we can mark it uptodate if we are filling the + * full page. Otherwise we need to read it in first... + */ + if (!PageUptodate(page)) { + if (sd->len < PAGE_CACHE_SIZE) { + ret = mapping->a_ops->readpage(file, page); + if (unlikely(ret)) + goto out; + + lock_page(page); + + if (!PageUptodate(page)) { + /* + * page got invalidated, repeat + */ + if (!page->mapping) { + unlock_page(page); + page_cache_release(page); + goto find_page; + } + ret = -EIO; + goto out; } - ret = -EIO; - goto out; + } else { + WARN_ON(!PageLocked(page)); + SetPageUptodate(page); } - } else { - WARN_ON(!PageLocked(page)); - SetPageUptodate(page); } } @@ -391,10 +434,13 @@ find_page: if (ret) goto out; - dst = kmap_atomic(page, KM_USER0); - memcpy(dst + offset, src + buf->offset, sd->len); - flush_dcache_page(page); - kunmap_atomic(dst, KM_USER0); + if (!buf->stolen) { + char *dst = kmap_atomic(page, KM_USER0); + + memcpy(dst + offset, src + buf->offset, sd->len); + flush_dcache_page(page); + kunmap_atomic(dst, KM_USER0); + } ret = mapping->a_ops->commit_write(file, page, 0, sd->len); if (ret < 0) @@ -405,7 +451,8 @@ find_page: out: if (ret < 0) unlock_page(page); - page_cache_release(page); + if (!buf->stolen) + page_cache_release(page); buf->ops->unmap(info, buf); return ret; } diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index b12e59c75752..75c7f55023ab 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -9,6 +9,7 @@ struct pipe_buffer { struct page *page; unsigned int offset, len; struct pipe_buf_operations *ops; + unsigned int stolen; }; struct pipe_buf_operations { @@ -16,6 +17,7 @@ struct pipe_buf_operations { void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *); void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *); + int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); }; struct pipe_inode_info { @@ -53,4 +55,10 @@ void pipe_wait(struct inode * inode); struct inode* pipe_new(struct inode* inode); void free_pipe_info(struct inode* inode); +/* + * splice is tied to pipes as a transport (at least for now), so we'll just + * add the splice flags here. + */ +#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */ + #endif -- cgit v1.2.3