cpu alloc / cpu ops v3: Optimize per cpu access
Subsystems (30)
cover cpu alloc / cpu ops v3: Optimize per cpu access
In various places the kernel maintains arrays of pointers indexed by processor numbers. These are used to locate objects that need to be used when executing on a specirfic processor. Both the slab allocator and the page allocator use these arrays and there the arrays are used in performance critical code. The allocpercpu functionality is a simple allocator to provide these arrays. However, there are certain drawbacks in using such arrays:
1. The arrays become huge for large systems and may be very sparsely populated (if they are dimensionied for NR_CPUS) on an architecture like IA64 that allows up to 4k cpus if a kernel is then booted on a machine that only supports 8 processors. We could nr_cpu_ids there but we would still have to allocate all possible processors up to the number of processor ids. cpu_alloc can deal with sparse cpu_maps.
2. The arrays cause surrounding variables to no longer fit into a single cacheline. The layout of core data structure is typically optimized so that variables frequently used together are placed in the same cacheline. Arrays of pointers move these variables far apart and destroy this effect.
3. A processor frequently follows only one pointer for its own use. Thus that cacheline with that pointer has to be kept in memory. The neighboring pointers are all to other processors that are rarely used. So a whole cacheline of 128 bytes may be consumed but only 8 bytes of information is constant use. It would be better to be able to place more information in this cacheline.
4. The lookup of the per cpu object is expensive and requires multiple memory accesses to:
A) smp_processor_id()
B) pointer to the base of the per cpu pointer array
C) pointer to the per cpu object in the pointer array
D) the per cpu object itself.
5. Each use of allocper requires its own per cpu array. On large system large arrays have to be allocated again and again.
6. Processor hotplug cannot effectively track the per cpu objects since the VM cannot find all memory that was allocated for a specific cpu. It is impossible to add or remove objects in a consistent way. Although the allocpercpu subsystem was extended to add that capability is not used since use would require adding cpu hotplug callbacks to each and every use of allocpercpu in the kernel.
The patchset here provides an cpu allocator that arranges data differently. Objects are placed tightly in linear areas reserved for each processor. The areas are of a fixed size so that address calculation can be used instead of a lookup. This means that
1. The VM knows where all the per cpu variables are and it could remove or add cpu areas as cpus come online or go offline.
2. There is only a single per cpu array that is used for the percpu area and all per cpu allocations.
3. The lookup of a per cpu object is easy and requires memory access to (worst case: architecture does not provide cpu ops):
A) per cpu offset from the per cpu pointer table (if its the current processor then there is usually some more efficient means of retrieving the offset)
B) cpu pointer to the object
C) the per cpu object itself.
4. Surrounding variables can be placed in the same cacheline. This allow f.e. in SLUB to avoid caching objects in per cpu structures since the kmem_cache structure is finally available without the need to access a cache cold cacheline.
5. A single pointer can be used regardless of the number of processors in the system.
The cpu allocator manages a fixed size data per cpu data area. The size can be configured as needed.
The current usage of the cpu area can be seen in the field
cpu_bytes
in /proc/vmstat
The patchset is agsinst 2.6.26-rc4.
There are two arch implementation of cpu ops provides.
1. x86. Another version of the zero based x86 patches exist by Mike.
2. IA64. Limited implementation since IA64 has no fast RMV ops. But we can avoid the addition of the my_cpu_offset in hotpaths.
This is a rather complex patchset and I am not sure how to merge it. Maybe it would be best to merge a piece at a time beginning with the basic infrastructure in the first few patches?
PATCH 1/41 cpu_alloc: Increase percpu area size to 128k
The per cpu allocator requires more per cpu space and we are already near the limit on IA64. Increase the maximum size of the IA64 per cpu area from 64K to 128K.
--- include/asm-ia64/page.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/asm-ia64/page.h +1 -1
@@ -42,7 +42,7 @@#define PAGE_MASK (~(PAGE_SIZE - 1))#define PAGE_ALIGN (addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) -#define PERCPU_PAGE_SHIFT 16/* log2() of max. size of per-CPU area */ +#define PERCPU_PAGE_SHIFT 17/* log2() of max. size of per-CPU area */ #define PERCPU_PAGE_SIZE (__IA64_UL_CONST(1) << PERCPU_PAGE_SHIFT)
RE: [patch 01/41] cpu_alloc: Increase percpu area size to 128k
include/asm-ia64/page.h line 45
Quoted 6 lines
The per cpu allocator requires more per cpu space and we are already near the limit on IA64. Increase the maximum size of the IA64 per cpu area from 64K to 128K.
include/asm-ia64/page.h +1
-1-#define PERCPU_PAGE_SHIFT 16/* log2() of max. size of per-CPU area */ +#define PERCPU_PAGE_SHIFT 17/* log2() of max. size of per-CPU area */
Don't you need some more changes to the alt_dtlb_miss handler in ivt.S for this to work? 128K is not a supported pagesize on any processor model.
-Tony
Re: [patch 01/41] cpu_alloc: Increase percpu area size to 128k
Quoted 10 lines · Tony
include/asm-ia64/page.h line 45
Quoted 6 lines
The per cpu allocator requires more per cpu space and we are already near the limit on IA64. Increase the maximum size of the IA64 per cpu area from 64K to 128K.
include/asm-ia64/page.h +1
-1-#define PERCPU_PAGE_SHIFT 16/* log2() of max. size of per-CPU area */ +#define PERCPU_PAGE_SHIFT 17/* log2() of max. size of per-CPU area */ Don't you need some more changes to the
alt_dtlb_misshandler inivt.Sfor this to work? 128K is not a supported pagesize on any processor model.
Yes, this was one of the issues with IA64 and extending the per-cpu area. It's probable that the IA64 TLB nailing trick might have to give way for dynamic per-cpu...
Rusty.
RE: [patch 01/41] cpu_alloc: Increase percpu area size to 128k
In reply to Luck, TonyQuoted 10 lines · Tony
include/asm-ia64/page.h line 45
Quoted 6 lines
The per cpu allocator requires more per cpu space and we are already near the limit on IA64. Increase the maximum size of the IA64 per cpu area from 64K to 128K.
include/asm-ia64/page.h +1
-1-#define PERCPU_PAGE_SHIFT 16/* log2() of max. size of per-CPU area */ +#define PERCPU_PAGE_SHIFT 17/* log2() of max. size of per-CPU area */ Don't you need some more changes to the
alt_dtlb_misshandler inivt.Sfor this to work? 128K is not a supported pagesize on any processor model.
Ok so this needs to be 18?
RE: [patch 01/41] cpu_alloc: Increase percpu area size to 128k
Quoted 8 lines
Quoted 6 lines
include/asm-ia64/page.h line 45
Quoted 2 lines
include/asm-ia64/page.h +1
-1-#define PERCPU_PAGE_SHIFT 16/* log2() of max. size of per-CPU area */ +#define PERCPU_PAGE_SHIFT 17/* log2() of max. size of per-CPU area */ Don't you need some more changes to the
alt_dtlb_misshandler inivt.Sfor this to work? 128K is not a supported pagesize on any processor model.Ok so this needs to be 18?
Yes. 18 will work (256K is an architected page size, guaranteed to be supported by all processor models ... SDM 2:52 table 4-4).
-Tony
PATCH 2/41 cpu alloc: The allocator
In reply to Christoph LameterThe per cpu allocator allows dynamic allocation of memory on all processors simultaneously. A bitmap is used to track used areas. The allocator implements tight packing to reduce the cache footprint and increase speed since cacheline contention is typically not a concern for memory mainly used by a single cpu. Small objects will fill up gaps left by larger allocations that required alignments.
The size of the cpu_alloc area can be changed via make menuconfig.
--- include/linux/percpu.h | 46 +++++++++++++ include/linux/vmstat.h | 2 mm/Kconfig | 6 + mm/Makefile | 2 mm/cpu_alloc.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ mm/vmstat.c | 1 6 files changed, 222 insertions(+), 2 deletions(-)
include/linux/vmstat.h +1 -1
@@ -37,7 +37,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSFOR_ALL_ZONES (PGSCAN_KSWAPD ),FOR_ALL_ZONES (PGSCAN_DIRECT ),PGINODESTEAL ,SLABS_SCANNED ,KSWAPD_STEAL ,KSWAPD_INODESTEAL , -PAGEOUTRUN ,ALLOCSTALL ,PGROTATED , +PAGEOUTRUN ,ALLOCSTALL ,PGROTATED ,CPU_BYTES , #ifdef CONFIG_HUGETLB_PAGE HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL, #endif
mm/Kconfig +6 -0
@@ -205,3 +205,9 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000" + help + Sets the maximum amount of memory that can be allocated via cpu_alloc
mm/Makefile +1 -1
@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o $(mmu-y) + page_isolation.o cpu_alloc.o $(mmu-y) obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o obj-$(CONFIG_BOUNCE) += bounce.o
mm/cpu_alloc.c new file +167 -0
167 lines not on this page · raw message
mm/vmstat.c +1 -0
@@ -653,6 +653,7 @@ static const char * const vmstat_text[]"allocstall" ,"pgrotated" , +"cpu_bytes" , #ifdef CONFIG_HUGETLB_PAGE"htlb_buddy_alloc_success" , "htlb_buddy_alloc_fail",
include/linux/percpu.h +46 -0
50 lines not on this page · raw message
Andrew Morton
maintainerRe: [patch 02/41] cpu alloc: The allocator
Quoted 11 lines · On Thu, 29 May 2008 20:56:22 -0700 Christoph Lameter <[email protected]> wrote:
The per cpu allocator allows dynamic allocation of memory on all processors simultaneously. A bitmap is used to track used areas. The allocator implements tight packing to reduce the cache footprint and increase speed since cacheline contention is typically not a concern for memory mainly used by a single cpu. Small objects will fill up gaps left by larger allocations that required alignments.
The size of the
cpu_allocarea can be changed via make menuconfig....
mm/Kconfig config NR_QUICK · line 211
Quoted 3 lines
mm/Kconfig +3
-0+config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000"
strange choice of a default? I guess it makes it clear that there's no particular advantage in making it a power-of-two or anything like that.
Quoted 48 lines
mm/Kconfig +2
-0+ help + Sets the maximum amount of memory that can be allocated via cpu_allocIndex: linux-2.6/mm/Makefile
===================================================================mm/Makefile +1
-1@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o $(mmu-y) + page_isolation.o cpu_alloc.o $(mmu-y) obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o obj-$(CONFIG_BOUNCE) += bounce.oIndex:
linux-2.6/mm/cpu_alloc.c
===================================================================mm/cpu_alloc.c new file +28
-0@@ -0,0 +1,167 @@ +/* +* Cpu allocator - Manage objects allocated for each processor +* +* (C) 2008 SGI, Christoph Lameter <[email protected]> +* Basic implementation with allocation and free from a dedicated per +* cpu area. +* +* The per cpu allocator allows dynamic allocation of memory on all +* processor simultaneously. A bitmap is used to track used areas. +* The allocator implements tight packing to reduce the cache footprint +* and increase speed since cacheline contention is typically not a concern +* for memory mainly used by a single cpu. Small objects will fill up gaps +* left by larger allocations that required alignments. +*/ +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/module.h> +#include <linux/percpu.h> +#include <linux/bitmap.h> +#include <asm/sections.h> + +/* +* Basic allocation unit. A bit map is created to track the use of each +* UNIT_SIZE element in the cpu area. +*/ +#define UNIT_TYPE int +#define UNIT_SIZE sizeof(UNIT_TYPE) +#define UNITS (CONFIG_CPU_ALLOC_SIZE / UNIT_SIZE)
mm/cpu_alloc.c line 38
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+ +static DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]); + +/* +* How many units are needed for an object of a given size +*/ +static int size_to_units (unsigned long size) +{ +return DIV_ROUND_UP (size,UNIT_SIZE ); +}
Perhaps it should return UNIT_TYPE? (ugh).
I guess there's no need to ever change that type, so no?
mm/cpu_alloc.c line 45
Quoted 6 lines
mm/cpu_alloc.c new file +6
-0+/* +* Lock to protect the bitmap and the meta data for the cpu allocator. +*/ +static DEFINE_SPINLOCK (cpu_alloc_map_lock); +static DECLARE_BITMAP(cpu_alloc_map, UNITS); +static int first_free;/* First known free unit */
Would be nicer to move these above size_to_units(), IMO.
mm/cpu_alloc.c line 56
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+/* +* Mark an object as used in the cpu_alloc_map +* +* Must hold cpu_alloc_map_lock +*/ +static void set_map (int start,int length) +{ +while (length-- >0 ) +__set_bit (start++, cpu_alloc_map); +}
bitmap_fill()?
mm/cpu_alloc.c line 67
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+/* +* Mark an area as freed. +* +* Must hold cpu_alloc_map_lock +*/ +static void clear_map (int start,int length) +{ +while (length-- >0 ) +__clear_bit (start++, cpu_alloc_map); +}
bitmap_zero()?
mm/cpu_alloc.c line 74
Quoted 6 lines
mm/cpu_alloc.c new file +6
-0+/* +* Allocate an object of a certain size +* +* Returns a special pointer that can be used with CPU_PTR to find the +* address of the object for a certain cpu. +*/
Should be kerneldoc, I guess.
mm/cpu_alloc.c line 84
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+void *cpu_alloc (unsigned long size,gfp_t gfpflags,unsigned long align) +{ +unsigned long start; +int units =size_to_units (size); +void *ptr; +int first; +unsigned long flags; + +if (!size) +return ZERO_SIZE_PTR ;
OK, so we reuse ZERO_SIZE_PTR from kmalloc.
Quoted 15 lines
mm/cpu_alloc.c new file +15
-0+spin_lock_irqsave (&cpu_alloc_map_lock, flags); + + first =1 ; + start = first_free; + +for ( ; ; ) { + + start =find_next_zero_bit (cpu_alloc_map,UNITS , start); +if (start >=UNITS ) + goto out_of_memory; + +if (first) + first_free = start; + +/*
mm/cpu_alloc.c line 110
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+* Check alignment and that there is enough space after +* the starting unit. +*/ +if (start % (align /UNIT_SIZE ) ==0 && +find_next_bit (cpu_alloc_map,UNITS , start +1 ) + >= start + units) +break ; + start++; + first =0 ; + }
This is kinda bitmap_find_free_region(), only bitmap_find_free_region() isn't quite strong enough.
Generally I think it would have been better if you had added new primitives to the bitmap library (or enhanced existing ones) and used them here, rather than implementing private functionality.
Quoted 34 lines
mm/cpu_alloc.c new file +34
-0+if (first) + first_free = start + units; + +if (start + units >UNITS ) + goto out_of_memory; + +set_map (start, units); +__count_vm_events (CPU_BYTES , units *UNIT_SIZE ); + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); + + ptr =per_cpu_var (area) + start; + +if (gfpflags & __GFP_ZERO) { +int cpu; + +for_each_possible_cpu (cpu) +memset (CPU_PTR (ptr, cpu),0 , size); + } + +return ptr; + +out_of_memory: +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +return NULL ; +} +EXPORT_SYMBOL (cpu_alloc); + +/* +* Free an object. The pointer must be a cpu pointer allocated +* via cpu_alloc. +*/ +void cpu_free (void *start,unsigned long size) +{
mm/cpu_alloc.c line 155
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+unsigned long units =size_to_units (size); +unsigned long index = (int *)start -per_cpu_var (area); +unsigned long flags; + +if (!start || start ==ZERO_SIZE_PTR ) +return ; + +BUG_ON (index >=UNITS || + !test_bit (index, cpu_alloc_map) || + !test_bit (index + units -1 , cpu_alloc_map));
If this assertion triggers for someone, you'll wish like hell that it had been implemented as three separate BUG_ONs.
Quoted 49 lines
mm/cpu_alloc.c new file +11
-0+spin_lock_irqsave (&cpu_alloc_map_lock, flags); + +clear_map (index, units); +__count_vm_events (CPU_BYTES , -units *UNIT_SIZE ); + +if (index < first_free) + first_free = index; + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +} +EXPORT_SYMBOL (cpu_free);Index:
linux-2.6/mm/vmstat.c
===================================================================mm/vmstat.c +1
-0@@ -653,6 +653,7 @@ static const char * const vmstat_text[]"allocstall" ,"pgrotated" , +"cpu_bytes" , #ifdef CONFIG_HUGETLB_PAGE"htlb_buddy_alloc_success" , "htlb_buddy_alloc_fail",Index:
linux-2.6/include/linux/percpu.h
===================================================================include/linux/percpu.h +18
-0@@ -135,4 +135,50 @@ static inline void percpu_free(void *__p#define free_percpu (ptr) percpu_free((ptr))#define per_cpu_ptr (ptr, cpu) percpu_ptr((ptr), (cpu)) + +/* +* cpu allocator definitions +* +* The cpu allocator allows allocating an instance of an object for each +* processor and the use of a single pointer to access all instances +* of the object. cpu_alloc provides optimized means for accessing the +* instance of the object belonging to the currently executing processor +* as well as special atomic operations on fields of objects of the +* currently executing processor. +* +* Cpu objects are typically small. The allocator packs them tightly +* to increase the chance on each access that a per cpu object is already +* cached. Alignments may be specified but the intent is to align the data +* properly due to cpu alignment constraints and not to avoid cacheline +* contention. Any holes left by aligning objects are filled up with smaller +* objects that are allocated later. +*
include/linux/percpu.h static inline void percpu_free(void *__p · line 165
Quoted 10 lines
include/linux/percpu.h +10
-0+* Cpu data can be allocated using CPU_ALLOC. The resulting pointer is +* pointing to the instance of the variable in the per cpu area provided +* by the loader. It is generally an error to use the pointer directly +* unless we are booting the system. +* +* __GFP_ZERO may be passed as a flag to zero the allocated memory. +*/ + +/* Return a pointer to the instance of a object for a particular processor */ +#define CPU_PTR (__p, __cpu) SHIFT_PERCPU_PTR((__p), per_cpu_offset(__cpu))
eek, a major interface function which is ALL IN CAPS!
can we do this in lower-case? In a C function?
include/linux/percpu.h static inline void percpu_free(void *__p · line 176
Quoted 10 lines
include/linux/percpu.h +10
-0+/* +* Return a pointer to the instance of the object belonging to the processor +* running the current code. +*/ +#define THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), my_cpu_offset) +#define __THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), __my_cpu_offset) + +#define CPU_ALLOC (type, flags) ((typeof(type) *)cpu_alloc(sizeof(type), \ + (flags), __alignof__(type))) +#define CPU_FREE (pointer) cpu_free((pointer), sizeof(*(pointer)))
Dittoes.
Quoted 7 lines
include/linux/percpu.h +6
-0+/* +* Raw calls +*/ +void *cpu_alloc (unsigned long size,gfp_t flags,unsigned long align); +void cpu_free (void *cpu_pointer,unsigned long size); +#endif /* __LINUX_PERCPU_H */
Re: [patch 02/41] cpu alloc: The allocator
Quoted 6 lines · Andrew Morton
mm/Kconfig config NR_QUICK · line 211
Quoted 3 lines
mm/Kconfig +3
-0+config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000"strange choice of a default? I guess it makes it clear that there's no particular advantage in making it a power-of-two or anything like that.
The cpu alloc has a cpu_bytes field in vmstat that shows how much memory is being used. 30000 seemed to be reasonable after staring at these numbers for awhile.
Quoted 6 lines
mm/cpu_alloc.c line 38
Quoted 4 lines
mm/cpu_alloc.c new file +4
-0+static int size_to_units (unsigned long size) +{ +return DIV_ROUND_UP (size,UNIT_SIZE ); +}Perhaps it should return
UNIT_TYPE? (ugh).
No. The UNIT_TYPE is the basic allocation unit. This returns the number of allocation units.
Quoted 1 line
I guess there's no need to ever change that type, so no?
We could go to finer or coarser grained someday? Maybe if the area becomes 1M of size of so we could go to 8 bytes?
Quoted 5 lines
mm/cpu_alloc.c line 45
Quoted 3 lines
mm/cpu_alloc.c new file +3
-0+static DEFINE_SPINLOCK (cpu_alloc_map_lock); +static DECLARE_BITMAP(cpu_alloc_map, UNITS); +static int first_free;/* First known free unit */ Would be nicer to move these above
size_to_units(), IMO.
size_to_units is fairly basic for most of the logic. These are variaables that manage the allocator state.
Quoted 7 lines
mm/cpu_alloc.c line 56
Quoted 5 lines
mm/cpu_alloc.c new file +5
-0+static void set_map (int start,int length) +{ +while (length-- >0 ) +__set_bit (start++, cpu_alloc_map); +}
bitmap_fill()?
Good idea.
Quoted 8 lines
mm/cpu_alloc.c line 67
Quoted 6 lines
+ */
mm/cpu_alloc.c new file +5
-0+static void clear_map (int start,int length) +{ +while (length-- >0 ) +__clear_bit (start++, cpu_alloc_map); +}
bitmap_zero()?
Ditto.
Quoted 4 lines
mm/cpu_alloc.c line 84
Quoted 2 lines
mm/cpu_alloc.c new file +2
-0+if (!size) +return ZERO_SIZE_PTR ;OK, so we reuse
ZERO_SIZE_PTRfrom kmalloc.
Well yes slab convention...
Quoted 10 lines
mm/cpu_alloc.c line 110
Quoted 3 lines
mm/cpu_alloc.c new file +3
-0+ start++; + first =0 ; + }This is kinda
bitmap_find_free_region(), onlybitmap_find_free_region()isn't quite strong enough.Generally I think it would have been better if you had added new primitives to the bitmap library (or enhanced existing ones) and used them here, rather than implementing private functionality.
The scope of the patchset is already fairly large. The search here is different and not performance critical. Not sure if this is useful for other purposes.
Quoted 7 lines
mm/cpu_alloc.c line 155
Quoted 4 lines
mm/cpu_alloc.c new file +4
-0+ +BUG_ON (index >=UNITS || + !test_bit (index, cpu_alloc_map) || + !test_bit (index + units -1 , cpu_alloc_map));If this assertion triggers for someone, you'll wish like hell that it had been implemented as three separate
BUG_ONs.
Ok. But in all cases we have an invalid index.
Quoted 8 lines
include/linux/percpu.h static inline void percpu_free(void *__p · line 165
Quoted 4 lines
+ */
include/linux/percpu.h +3
-0+ +/* Return a pointer to the instance of a object for a particular processor */ +#define CPU_PTR (__p, __cpu) SHIFT_PERCPU_PTR((__p), per_cpu_offset(__cpu))eek, a major interface function which is ALL IN CAPS!
can we do this in lower-case? In a C function?
No. This is a macro and therefore uppercase (there is macro magic going on that ppl need to be aware of). AFAICR you wanted it this way last year. C function not possible because of the type checking.
Andrew Morton
maintainerRe: [patch 02/41] cpu alloc: The allocator
Quoted 7 lines · On Thu, 29 May 2008 22:10:25 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 7 lines
mm/cpu_alloc.c line 110
Quoted 3 lines
mm/cpu_alloc.c new file +3
-0+ start++; + first =0 ; + }This is kinda
bitmap_find_free_region(), onlybitmap_find_free_region()isn't quite strong enough.
Quoted 5 lines
Quoted 3 lines
Generally I think it would have been better if you had added new primitives to the bitmap library (or enhanced existing ones) and used them here, rather than implementing private functionality.
The scope of the patchset is already fairly large.
It would be a relatively small incremental effort ;)
Quoted 3 lines
The search here is
different and not performance critical. Not sure if this is useful for other purposes.
I think that strengthening bitmap_find_free_region() would end up giving us a better kernel than open-coding something similar here.
Quoted 7 lines
Quoted 7 lines
include/linux/percpu.h static inline void percpu_free(void *__p · line 165
Quoted 4 lines
+ */
include/linux/percpu.h +3
-0+ +/* Return a pointer to the instance of a object for a particular processor */ +#define CPU_PTR (__p, __cpu) SHIFT_PERCPU_PTR((__p), per_cpu_offset(__cpu))eek, a major interface function which is ALL IN CAPS!
Quoted 5 lines
Quoted 1 line
can we do this in lower-case? In a C function?
No. This is a macro and therefore uppercase (there is macro magic going on that ppl need to be aware of). AFAICR you wanted it this way last year. C function not possible because of the type checking.
urgh. This is a C-convention versus kernel-convention thing. The C convention exists for very good reasons. But it sure does suck.
What do others think?
Re: [patch 02/41] cpu alloc: The allocator
Quoted 6 lines · Andrew
Quoted 6 lines
Quoted 5 lines
Quoted 1 line
+#define
CPU_PTR(__p, __cpu)SHIFT_PERCPU_PTR((__p), per_cpu_offset(__cpu))eek, a major interface function which is ALL IN CAPS!
can we do this in lower-case? In a C function?
Quoted 8 lines
Quoted 3 lines
No. This is a macro and therefore uppercase (there is macro magic going on that ppl need to be aware of). AFAICR you wanted it this way last year. C function not possible because of the type checking.
urgh. This is a C-convention versus kernel-convention thing. The C convention exists for very good reasons. But it sure does suck.
What do others think?
A few, key symbols get to be special ... short but distinctive names that become (in)famous. The classic was "u", for the per-user structure, aka the "user area", in old Unix kernels. In people's names, a few one word or first names such as "Ike", "Madonna", "Ali", "Tiger", "Cher", "Mao", "OJ", "Plato", "Linus", ... have become distinctive and well known to many people.
How about "_pcpu", instead of CPU_PTR? "_pcpu" is a short, unique (not currently in use) symbol that, tersely, says what we want to say.
Yes - it violates multiple conventions. "The Boss" (Bruce Springsteen) gets to do that.
Signature · 3 lines
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.940.382.4214Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterQuoted 9 lines · On Thu, 29 May 2008 22:10:25 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 6 lines · Andrew Morton
mm/Kconfig config NR_QUICK · line 211
Quoted 3 lines
mm/Kconfig +3
-0+config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000"strange choice of a default? I guess it makes it clear that there's no particular advantage in making it a power-of-two or anything like that.
Quoted 4 lines
The cpu alloc has a
cpu_bytesfield in vmstat that shows how much memory is being used. 30000 seemed to be reasonable after staring at these numbers for awhile.
30000 is suitable for both of 32bits/64bits arch ?
Thanks,
-kame
Re: [patch 02/41] cpu alloc: The allocator
Quoted 1 line · KAMEZAWA Hiroyuki
30000 is suitable for both of 32bits/64bits arch ?
Was developed on 64bit. As ususal 32 bit was an afterthought.
Re: [patch 02/41] cpu alloc: The allocator
In reply to Andrew MortonAndrew Morton wrote:
...
Quoted 25 lines
mm/cpu_alloc.c line 56
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+/* +* Mark an object as used in the cpu_alloc_map +* +* Must hold cpu_alloc_map_lock +*/ +static void set_map (int start,int length) +{ +while (length-- >0 ) +__set_bit (start++, cpu_alloc_map); +}
bitmap_fill()?mm/cpu_alloc.c line 67
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+/* +* Mark an area as freed. +* +* Must hold cpu_alloc_map_lock +*/ +static void clear_map (int start,int length) +{ +while (length-- >0 ) +__clear_bit (start++, cpu_alloc_map); +}
bitmap_zero()?
...
Quoted 42 lines
mm/cpu_alloc.c line 84
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+void *cpu_alloc (unsigned long size,gfp_t gfpflags,unsigned long align) +{ +unsigned long start; +int units =size_to_units (size); +void *ptr; +int first; +unsigned long flags; + +if (!size) +return ZERO_SIZE_PTR ;OK, so we reuse
ZERO_SIZE_PTRfrom kmalloc.Quoted 15 lines
mm/cpu_alloc.c new file +15
-0+spin_lock_irqsave (&cpu_alloc_map_lock, flags); + + first =1 ; + start = first_free; + +for ( ; ; ) { + + start =find_next_zero_bit (cpu_alloc_map,UNITS , start); +if (start >=UNITS ) + goto out_of_memory; + +if (first) + first_free = start; + +/* mm/cpu_alloc.c line 110
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+* Check alignment and that there is enough space after +* the starting unit. +*/ +if (start % (align /UNIT_SIZE ) ==0 && +find_next_bit (cpu_alloc_map,UNITS , start +1 ) + >= start + units) +break ; + start++; + first =0 ; + }This is kinda
bitmap_find_free_region(), onlybitmap_find_free_region()isn't quite strong enough.
Quoted 3 lines
Generally I think it would have been better if you had added new primitives to the bitmap library (or enhanced existing ones) and used them here, rather than implementing private functionality.
Hi Andrew,
I've sort of inherited this now...
So are you suggesting that we add new bitmap primitives to:
bitmap_fill_offset(bitmap, start, nbits) /* start at bitmap[start] */
bitmap_zero_offset(bitmap, start, nbits) bitmap_find_free_area(bitmap, nbits, size, align) /* size not order */
Thanks,
Mike
Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterQuoted 85 lines · Christoph Lameter
The per cpu allocator allows dynamic allocation of memory on all processors simultaneously. A bitmap is used to track used areas. The allocator implements tight packing to reduce the cache footprint and increase speed since cacheline contention is typically not a concern for memory mainly used by a single cpu. Small objects will fill up gaps left by larger allocations that required alignments.
The size of the
cpu_allocarea can be changed via make menuconfig.Signed-off-by Christoph Lameter <[email protected]>--- include/linux/percpu.h | 46 +++++++++++++ include/linux/vmstat.h | 2 mm/Kconfig | 6 + mm/Makefile | 2 mm/cpu_alloc.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ mm/vmstat.c | 1 6 files changed, 222 insertions(+), 2 deletions(-)Index:
linux-2.6/include/linux/vmstat.h
===================================================================include/linux/vmstat.h +1
-1@@ -37,7 +37,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSFOR_ALL_ZONES (PGSCAN_KSWAPD ),FOR_ALL_ZONES (PGSCAN_DIRECT ),PGINODESTEAL ,SLABS_SCANNED ,KSWAPD_STEAL ,KSWAPD_INODESTEAL , -PAGEOUTRUN ,ALLOCSTALL ,PGROTATED , +PAGEOUTRUN ,ALLOCSTALL ,PGROTATED ,CPU_BYTES , #ifdef CONFIG_HUGETLB_PAGE HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL, #endifIndex: linux-2.6/mm/Kconfig
===================================================================mm/Kconfig +6
-0@@ -205,3 +205,9 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000" + help + Sets the maximum amount of memory that can be allocated via cpu_allocIndex: linux-2.6/mm/Makefile
===================================================================mm/Makefile +1
-1@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o $(mmu-y) + page_isolation.o cpu_alloc.o $(mmu-y) obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o obj-$(CONFIG_BOUNCE) += bounce.oIndex:
linux-2.6/mm/cpu_alloc.c
===================================================================mm/cpu_alloc.c new file +20
-0@@ -0,0 +1,167 @@ +/* +* Cpu allocator - Manage objects allocated for each processor +* +* (C) 2008 SGI, Christoph Lameter <[email protected]> +* Basic implementation with allocation and free from a dedicated per +* cpu area. +* +* The per cpu allocator allows dynamic allocation of memory on all +* processor simultaneously. A bitmap is used to track used areas. +* The allocator implements tight packing to reduce the cache footprint +* and increase speed since cacheline contention is typically not a concern +* for memory mainly used by a single cpu. Small objects will fill up gaps +* left by larger allocations that required alignments. +*/ +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/module.h> +#include <linux/percpu.h> +#include <linux/bitmap.h> +#include <asm/sections.h>
mm/cpu_alloc.c line 30
Quoted 10 lines
mm/cpu_alloc.c new file +10
-0+ +/* +* Basic allocation unit. A bit map is created to track the use of each +* UNIT_SIZE element in the cpu area. +*/ +#define UNIT_TYPE int +#define UNIT_SIZE sizeof(UNIT_TYPE) +#define UNITS (CONFIG_CPU_ALLOC_SIZE / UNIT_SIZE) + +static DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);
area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.
Either you should add an __attribute__((__aligned__(PAGE_SIZE))),
or take into account the real address of area[] in cpu_alloc() to avoid waste of up to PAGE_SIZE bytes per cpu.
Quoted 206 lines
mm/cpu_alloc.c new file +137
-0+ +/* +* How many units are needed for an object of a given size +*/ +static int size_to_units (unsigned long size) +{ +return DIV_ROUND_UP (size,UNIT_SIZE ); +} + +/* +* Lock to protect the bitmap and the meta data for the cpu allocator. +*/ +static DEFINE_SPINLOCK (cpu_alloc_map_lock); +static DECLARE_BITMAP(cpu_alloc_map, UNITS); +static int first_free;/* First known free unit */ + +/* +* Mark an object as used in the cpu_alloc_map +* +* Must hold cpu_alloc_map_lock +*/ +static void set_map (int start,int length) +{ +while (length-- >0 ) +__set_bit (start++, cpu_alloc_map); +} + +/* +* Mark an area as freed. +* +* Must hold cpu_alloc_map_lock +*/ +static void clear_map (int start,int length) +{ +while (length-- >0 ) +__clear_bit (start++, cpu_alloc_map); +} + +/* +* Allocate an object of a certain size +* +* Returns a special pointer that can be used with CPU_PTR to find the +* address of the object for a certain cpu. +*/ +void *cpu_alloc (unsigned long size,gfp_t gfpflags,unsigned long align) +{ +unsigned long start; +int units =size_to_units (size); +void *ptr; +int first; +unsigned long flags; + +if (!size) +return ZERO_SIZE_PTR ; + +spin_lock_irqsave (&cpu_alloc_map_lock, flags); + + first =1 ; + start = first_free; + +for ( ; ; ) { + + start =find_next_zero_bit (cpu_alloc_map,UNITS , start); +if (start >=UNITS ) + goto out_of_memory; + +if (first) + first_free = start; + +/* +* Check alignment and that there is enough space after +* the starting unit. +*/ +if (start % (align /UNIT_SIZE ) ==0 && +find_next_bit (cpu_alloc_map,UNITS , start +1 ) + >= start + units) +break ; + start++; + first =0 ; + } + +if (first) + first_free = start + units; + +if (start + units >UNITS ) + goto out_of_memory; + +set_map (start, units); +__count_vm_events (CPU_BYTES , units *UNIT_SIZE ); + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); + + ptr =per_cpu_var (area) + start; + +if (gfpflags & __GFP_ZERO) { +int cpu; + +for_each_possible_cpu (cpu) +memset (CPU_PTR (ptr, cpu),0 , size); + } + +return ptr; + +out_of_memory: +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +return NULL ; +} +EXPORT_SYMBOL (cpu_alloc); + +/* +* Free an object. The pointer must be a cpu pointer allocated +* via cpu_alloc. +*/ +void cpu_free (void *start,unsigned long size) +{ +unsigned long units =size_to_units (size); +unsigned long index = (int *)start -per_cpu_var (area); +unsigned long flags; + +if (!start || start ==ZERO_SIZE_PTR ) +return ; + +BUG_ON (index >=UNITS || + !test_bit (index, cpu_alloc_map) || + !test_bit (index + units -1 , cpu_alloc_map)); + +spin_lock_irqsave (&cpu_alloc_map_lock, flags); + +clear_map (index, units); +__count_vm_events (CPU_BYTES , -units *UNIT_SIZE ); + +if (index < first_free) + first_free = index; + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +} +EXPORT_SYMBOL (cpu_free);Index:
linux-2.6/mm/vmstat.c
===================================================================mm/vmstat.c +1
-0@@ -653,6 +653,7 @@ static const char * const vmstat_text[]"allocstall" ,"pgrotated" , +"cpu_bytes" , #ifdef CONFIG_HUGETLB_PAGE"htlb_buddy_alloc_success" , "htlb_buddy_alloc_fail",Index:
linux-2.6/include/linux/percpu.h
===================================================================include/linux/percpu.h +46
-0@@ -135,4 +135,50 @@ static inline void percpu_free(void *__p#define free_percpu (ptr) percpu_free((ptr))#define per_cpu_ptr (ptr, cpu) percpu_ptr((ptr), (cpu)) + +/* +* cpu allocator definitions +* +* The cpu allocator allows allocating an instance of an object for each +* processor and the use of a single pointer to access all instances +* of the object. cpu_alloc provides optimized means for accessing the +* instance of the object belonging to the currently executing processor +* as well as special atomic operations on fields of objects of the +* currently executing processor. +* +* Cpu objects are typically small. The allocator packs them tightly +* to increase the chance on each access that a per cpu object is already +* cached. Alignments may be specified but the intent is to align the data +* properly due to cpu alignment constraints and not to avoid cacheline +* contention. Any holes left by aligning objects are filled up with smaller +* objects that are allocated later. +* +* Cpu data can be allocated using CPU_ALLOC. The resulting pointer is +* pointing to the instance of the variable in the per cpu area provided +* by the loader. It is generally an error to use the pointer directly +* unless we are booting the system. +* +* __GFP_ZERO may be passed as a flag to zero the allocated memory. +*/ + +/* Return a pointer to the instance of a object for a particular processor */ +#define CPU_PTR (__p, __cpu) SHIFT_PERCPU_PTR((__p), per_cpu_offset(__cpu)) + +/* +* Return a pointer to the instance of the object belonging to the processor +* running the current code. +*/ +#define THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), my_cpu_offset) +#define __THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), __my_cpu_offset) + +#define CPU_ALLOC (type, flags) ((typeof(type) *)cpu_alloc(sizeof(type), \ + (flags), __alignof__(type))) +#define CPU_FREE (pointer) cpu_free((pointer), sizeof(*(pointer))) + +/* +* Raw calls +*/ +void *cpu_alloc (unsigned long size,gfp_t flags,unsigned long align); +void cpu_free (void *cpu_pointer,unsigned long size); +#endif /* __LINUX_PERCPU_H */
Re: [patch 02/41] cpu alloc: The allocator
Quoted 11 lines · Eric Dumazet
Quoted 2 lines
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.
I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
Re: [patch 02/41] cpu alloc: The allocator
Quoted 8 lines · On Friday 30 May 2008 15:20:45 Christoph Lameter wrote:
Quoted 7 lines · Eric Dumazet
Quoted 1 line
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.
Quoted 7 lines
Quoted 4 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
I vaguely recall there were issues with this in the module code. They might be gone now, but failing to meet alignment contraints without a big warning would suck.
But modifying your code to consider the actual alignment is actually pretty trivial, AFAICT.
Cheers,
Rusty.
Re: [patch 02/41] cpu alloc: The allocator
Quoted 19 lines · Rusty Russell
Quoted 7 lines · On Friday 30 May 2008 15:20:45 Christoph Lameter wrote:
Quoted 6 lines · Eric Dumazet
Quoted 1 line
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.Quoted 6 lines
Quoted 4 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
I vaguely recall there were issues with this in the module code. They might be gone now, but failing to meet alignment contraints without a big warning would suck.
Quoted 5 lines
But modifying your code to consider the actual alignment is actually pretty trivial, AFAICT.
Cheers,
Rusty.
So paraphrasing my earlier email, we should add:
bitmap_find_free_area(bitmap, nbits, size, align, alignbase)
so that > cacheline alignment is possible?
My thinking is that if we do go to true dynamically sized cpu_alloc area then allocating PAGE_SIZE units may be both practical and worthwhile...?
Thanks,
Mike
Re: [patch 02/41] cpu alloc: The allocator
Quoted 32 lines · On Thursday 05 June 2008 01:30:23 Mike Travis wrote:
Quoted 21 lines · Rusty Russell
Quoted 8 lines · On Friday 30 May 2008 15:20:45 Christoph Lameter wrote:
Quoted 7 lines · Eric Dumazet
Quoted 1 line
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.Quoted 7 lines
Quoted 4 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
I vaguely recall there were issues with this in the module code. They might be gone now, but failing to meet alignment contraints without a big warning would suck.
Quoted 5 lines
But modifying your code to consider the actual alignment is actually pretty trivial, AFAICT.
Cheers,
Rusty.So paraphrasing my earlier email, we should add:
bitmap_find_free_area(bitmap, nbits, size, align, alignbase)
Quoted 7 lines
so that > cacheline alignment is possible?
My thinking is that if we do go to true dynamically sized
cpu_allocarea then allocatingPAGE_SIZEunits may be both practical and worthwhile...?Thanks,
Mike
Well, my thinking is that unless we do true dynamic per-cpu, this entire patch series is a non-starter :(
Once we have that, we can reopen this. Then we'll discuss why we're writing a new allocator rather than using the existing one :)
Cheers,
Rusty.
Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterQuoted 11 lines · Christoph Lameter
Quoted 8 lines · Eric Dumazet
Quoted 3 lines
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.
Quoted 10 lines
Quoted 5 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
Hum, maybe, but then we broke modules that might request up to PAGE_SIZE alignement for their percpu section, if I read your 3rd patch correctly.
Taking into account the ((unsigned long)area & (PAGE_SIZE-1)) offset in cpu_alloc() should give up to PAGE_SIZE alignment for free.
Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterQuoted 9 lines · Christoph Lameter
Quoted 7 lines · Eric Dumazet
Quoted 2 lines
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.
Quoted 7 lines
Quoted 4 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
I'm a bit confused. Why is DEFINE_PER_CPU_SHARED_ALIGNED() conditioned on ifdef MODULE?
#ifdef MODULE
#define SHARED_ALIGNED_SECTION ".data.percpu"
#else
#define SHARED_ALIGNED_SECTION ".data.percpu.shared_aligned"
#endif
#define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \
__attribute__((__section__(SHARED_ALIGNED_SECTION))) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \
____cacheline_aligned_in_smpThanks,
Mike
Re: [patch 02/41] cpu alloc: The allocator
Quoted 31 lines · Mike Travis
Quoted 9 lines · Christoph Lameter
Quoted 7 lines · Eric Dumazet
Quoted 2 lines
+static
DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]);area[] is not guaranteed to be aligned on anything but 4 bytes.
If someone then needs to call
cpu_alloc(8, GFP_KERNEL, 8), it might get an non aligned result.Quoted 6 lines
Quoted 4 lines
Either you should add an __attribute__((__aligned__(PAGE_SIZE))), or take into account the real address of area[] in
cpu_alloc()to avoid waste of up toPAGE_SIZEbytes
per cpu.I think cacheline aligning should be sufficient. People should not allocate large page aligned objects here.
I'm a bit confused. Why is
DEFINE_PER_CPU_SHARED_ALIGNED()conditioned on ifdef MODULE?#ifdef MODULE #define SHARED_ALIGNED_SECTION ".data.percpu" #else #define SHARED_ALIGNED_SECTION ".data.percpu.shared_aligned" #endif #define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \ __attribute__((__section__(SHARED_ALIGNED_SECTION))) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \ ____cacheline_aligned_in_smp
Quoted 4 lines
Thanks,
Mike
Because we had crashes when loading oprofile module, when a previous version of oprofile used to use DEFINE_PER_CPU_SHARED_ALIGNED variable
module loader only takes into account the special section ".data.percpu" and ignores ".data.percpu.shared_aligned"
I therefore submitted two patches :
1) commit 8b8b498836942c0c855333d357d121c0adeefbd9
oprofile: don't request cache line alignment for cpu_buffer
Alignment was previously requested because cpu_buffer was an [NR_CPUS] array, to avoid cache line sharing between CPUS.
After commit 608dfddd845da5ab6accef70154c8910529699f7 (oprofile: change cpu_buffer from array to per_cpu variable ), we dont need to force an alignement anymore since cpu_buffer sits in per_cpu zone.
2) and commit 44c81433e8b05dbc85985d939046f10f95901184per_cpu: fix DEFINE_PER_CPU_SHARED_ALIGNED for modules
Current module loader lookups ".data.percpu" ELF section to perform per_cpu relocation. But DEFINE_PER_CPU_SHARED_ALIGNED() uses another section (".data.percpu.shared_aligned"), currently only handled in vmlinux.lds, not by module loader.
To correct this problem, instead of adding logic into module loader, or using at build time a module.lds file for all arches to group ".data.percpu.shared_aligned" into ".data.percpu", just use ".data.percpu" for modules.
Alignment requirements are correctly handled by ld and module loader.
Re: [patch 02/41] cpu alloc: The allocator
Quoted 10 lines · On Thursday 05 June 2008 01:11:00 Eric Dumazet wrote:
Quoted 2 lines · Mike Travis
I'm a bit confused. Why is
DEFINE_PER_CPU_SHARED_ALIGNED()conditioned on ifdef MODULE?Because we had crashes when loading oprofile module, when a previous version of oprofile used to use
DEFINE_PER_CPU_SHARED_ALIGNEDvariablemodule loader only takes into account the special section ".data.percpu" and ignores ".data.percpu.shared_aligned"
I therefore submitted two patches :
Put one way, putting page-aligned per-cpu data in a separate section is a space-saving hack: one which is not really required for modules because of the low frequency of such variables. Put another way, not respecting the .data.percpu.shared_aligned section in modules is a bug.
But a comment would probably be nice!
Cheers,
Rusty.
Re: [patch 02/41] cpu alloc: The allocator
In reply to Mike TravisQuoted 13 lines · Mike Travis
I'm a bit confused. Why is
DEFINE_PER_CPU_SHARED_ALIGNED()conditioned on ifdef MODULE?#ifdef MODULE #define SHARED_ALIGNED_SECTION ".data.percpu" #else #define SHARED_ALIGNED_SECTION ".data.percpu.shared_aligned" #endif #define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \ __attribute__((__section__(SHARED_ALIGNED_SECTION))) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \ ____cacheline_aligned_in_smp
Looks wrong to me. There can be shared objects even without modules.
Re: [patch 02/41] cpu alloc: The allocator
Quoted 11 lines · Christoph Lameter
Quoted 9 lines · Mike Travis
I'm a bit confused. Why is
DEFINE_PER_CPU_SHARED_ALIGNED()conditioned on ifdef MODULE?#ifdef MODULE #define SHARED_ALIGNED_SECTION ".data.percpu" #else #define SHARED_ALIGNED_SECTION ".data.percpu.shared_aligned" #endif
Quoted 8 lines
Quoted 4 lines
#define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \ __attribute__((__section__(SHARED_ALIGNED_SECTION))) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \
____cacheline_aligned_in_smpLooks wrong to me. There can be shared objects even without modules.
Well, MODULE is not CONFIG_MODULES :)
If compiling an object that is going to be statically linked to kernel, MODULE is not defined, so we have shared objects.
When compiling a module, we cannot *yet* use .data.percpu.shared_aligned section, since module loader wont handle this section.
Alternative is to change modules linking for all arches to merge .data.percpu{*} subsections correctly, or tell module loader to take into account all .data.percpu sections.
AFAIK no module uses DEFINE_PER_CPU_SHARED_ALIGNED() yet...
Re: [patch 02/41] cpu alloc: The allocator
Quoted 5 lines · Eric Dumazet
Well, MODULE is not
CONFIG_MODULES:)If compiling an object that is going to be statically linked to kernel, MODULE is not defined, so we have shared objects.
Quoted 8 lines
When compiling a module, we cannot *yet* use .data.percpu.shared_aligned section, since module loader wont handle this section.
Alternative is to change modules linking for all arches to merge .data.percpu{*} subsections correctly, or tell module loader to take into account all .data.percpu sections.
AFAIK no module uses
DEFINE_PER_CPU_SHARED_ALIGNED()yet...
Ahhh. Makes sense. Add a comment to explain this?
Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterQuoted 6 lines · On Friday 30 May 2008 13:56:22 Christoph Lameter wrote:
The per cpu allocator allows dynamic allocation of memory on all processors simultaneously. A bitmap is used to track used areas. The allocator implements tight packing to reduce the cache footprint and increase speed since cacheline contention is typically not a concern for memory mainly used by a single cpu. Small objects will fill up gaps left by larger allocations that required alignments.
Allocator seems nice and simple, similar to existing one in module.c (which predates cool bitmap operators).
Being able to do per-cpu allocations in an interrupt handler seems like enouraging a Bad Idea though: I'd be tempted to avoid the flags word, always zero, and use a mutex instead of a spinlock.
Cheers,
Rusty.
Quoted 293 lines
The size of the
cpu_allocarea can be changed via make menuconfig.Signed-off-by Christoph Lameter <[email protected]>--- include/linux/percpu.h | 46 +++++++++++++ include/linux/vmstat.h | 2 mm/Kconfig | 6 + mm/Makefile | 2 mm/cpu_alloc.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ mm/vmstat.c | 1 6 files changed, 222 insertions(+), 2 deletions(-)Index:
linux-2.6/include/linux/vmstat.h
===================================================================
---linux-2.6.orig/include/linux/vmstat.h2008-05-29 19:41:21.000000000
-0700 +++linux-2.6/include/linux/vmstat.h2008-05-29 20:15:37.000000000
-0700 @@ -37,7 +37,7 @@ enumvm_event_item{ PGPGIN, PGPGOUT, PSinclude/linux/vmstat.h +1
-1FOR_ALL_ZONES (PGSCAN_KSWAPD ),FOR_ALL_ZONES (PGSCAN_DIRECT ),PGINODESTEAL ,SLABS_SCANNED ,KSWAPD_STEAL ,KSWAPD_INODESTEAL , -PAGEOUTRUN ,ALLOCSTALL ,PGROTATED , +PAGEOUTRUN ,ALLOCSTALL ,PGROTATED ,CPU_BYTES , #ifdef CONFIG_HUGETLB_PAGE HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL, #endifIndex: linux-2.6/mm/Kconfig
===================================================================mm/Kconfig +6
-0@@ -205,3 +205,9 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000" + help + Sets the maximum amount of memory that can be allocated via cpu_allocIndex: linux-2.6/mm/Makefile
===================================================================mm/Makefile +1
-1@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o $(mmu-y) + page_isolation.o cpu_alloc.o $(mmu-y) obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o obj-$(CONFIG_BOUNCE) += bounce.oIndex:
linux-2.6/mm/cpu_alloc.c
===================================================================mm/cpu_alloc.c new file +11
-0@@ -0,0 +1,167 @@ +/* +* Cpu allocator - Manage objects allocated for each processor +* +* (C) 2008 SGI, Christoph Lameter <[email protected]> +* Basic implementation with allocation and free from a dedicated per +* cpu area. +* +* The per cpu allocator allows dynamic allocation of memory on all +* processor simultaneously. A bitmap is used to track used areas. +* The allocator implements tight packing to reduce the cache footprint +* and increase speed since cacheline contention is typically not a concern + * for memory mainly used by a single cpu. Small objects will fill up gaps + * left by larger allocations that required alignments.
+ */mm/cpu_alloc.c new file +153
-0+#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/module.h> +#include <linux/percpu.h> +#include <linux/bitmap.h> +#include <asm/sections.h> + +/* +* Basic allocation unit. A bit map is created to track the use of each +* UNIT_SIZE element in the cpu area. +*/ +#define UNIT_TYPE int +#define UNIT_SIZE sizeof(UNIT_TYPE) +#define UNITS (CONFIG_CPU_ALLOC_SIZE / UNIT_SIZE) + +static DEFINE_PER_CPU(UNIT_TYPE, area[UNITS]); + +/* +* How many units are needed for an object of a given size +*/ +static int size_to_units (unsigned long size) +{ +return DIV_ROUND_UP (size,UNIT_SIZE ); +} + +/* +* Lock to protect the bitmap and the meta data for the cpu allocator. +*/ +static DEFINE_SPINLOCK (cpu_alloc_map_lock); +static DECLARE_BITMAP(cpu_alloc_map, UNITS); +static int first_free;/* First known free unit */ + +/* +* Mark an object as used in the cpu_alloc_map +* +* Must hold cpu_alloc_map_lock +*/ +static void set_map (int start,int length) +{ +while (length-- >0 ) +__set_bit (start++, cpu_alloc_map); +} + +/* +* Mark an area as freed. +* +* Must hold cpu_alloc_map_lock +*/ +static void clear_map (int start,int length) +{ +while (length-- >0 ) +__clear_bit (start++, cpu_alloc_map); +} + +/* +* Allocate an object of a certain size +* +* Returns a special pointer that can be used with CPU_PTR to find the +* address of the object for a certain cpu. +*/ +void *cpu_alloc (unsigned long size,gfp_t gfpflags,unsigned long align) +{ +unsigned long start; +int units =size_to_units (size); +void *ptr; +int first; +unsigned long flags; + +if (!size) +return ZERO_SIZE_PTR ; + +spin_lock_irqsave (&cpu_alloc_map_lock, flags); + + first =1 ; + start = first_free; + +for ( ; ; ) { + + start =find_next_zero_bit (cpu_alloc_map,UNITS , start); +if (start >=UNITS ) + goto out_of_memory; + +if (first) + first_free = start; + +/* +* Check alignment and that there is enough space after +* the starting unit. +*/ +if (start % (align /UNIT_SIZE ) ==0 && +find_next_bit (cpu_alloc_map,UNITS , start +1 ) + >= start + units) +break ; + start++; + first =0 ; + } + +if (first) + first_free = start + units; + +if (start + units >UNITS ) + goto out_of_memory; + +set_map (start, units); +__count_vm_events (CPU_BYTES , units *UNIT_SIZE ); + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); + + ptr =per_cpu_var (area) + start; + +if (gfpflags & __GFP_ZERO) { +int cpu; + +for_each_possible_cpu (cpu) +memset (CPU_PTR (ptr, cpu),0 , size); + } + +return ptr; + +out_of_memory: +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +return NULL ; +} +EXPORT_SYMBOL (cpu_alloc); + +/* +* Free an object. The pointer must be a cpu pointer allocated +* via cpu_alloc. +*/ +void cpu_free (void *start,unsigned long size) +{ +unsigned long units =size_to_units (size); +unsigned long index = (int *)start -per_cpu_var (area); +unsigned long flags; + +if (!start || start ==ZERO_SIZE_PTR ) +return ; + +BUG_ON (index >=UNITS || + !test_bit (index, cpu_alloc_map) || + !test_bit (index + units -1 , cpu_alloc_map)); + +spin_lock_irqsave (&cpu_alloc_map_lock, flags); + +clear_map (index, units); +__count_vm_events (CPU_BYTES , -units *UNIT_SIZE ); + +if (index < first_free) + first_free = index; + +spin_unlock_irqrestore (&cpu_alloc_map_lock, flags); +} +EXPORT_SYMBOL (cpu_free);Index:
linux-2.6/mm/vmstat.c
===================================================================mm/vmstat.c +1
-0@@ -653,6 +653,7 @@ static const char * const vmstat_text[]"allocstall" ,"pgrotated" , +"cpu_bytes" , #ifdef CONFIG_HUGETLB_PAGE"htlb_buddy_alloc_success" , "htlb_buddy_alloc_fail",Index:
linux-2.6/include/linux/percpu.h
===================================================================
---linux-2.6.orig/include/linux/percpu.h2008-05-29 19:41:21.000000000
-0700 +++linux-2.6/include/linux/percpu.h2008-05-29 20:29:12.000000000
-0700 @@ -135,4 +135,50 @@ static inline void percpu_free(void*__p
#definefree_percpu(ptr)percpu_free((ptr))
#defineper_cpu_ptr(ptr, cpu)percpu_ptr((ptr), (cpu))mm/vmstat.c +16
-0+ +/* +* cpu allocator definitions +* +* The cpu allocator allows allocating an instance of an object for each +* processor and the use of a single pointer to access all instances +* of the object. cpu_alloc provides optimized means for accessing the +* instance of the object belonging to the currently executing processor +* as well as special atomic operations on fields of objects of the +* currently executing processor. +* +* Cpu objects are typically small. The allocator packs them tightly +* to increase the chance on each access that a per cpu object is already +* cached. Alignments may be specified but the intent is to align the data +* properly due to cpu alignment constraints and not to avoid cacheline +* contention. Any holes left by aligning objects are filled up with smaller + * objects that are allocated later.
+ *
+ * Cpu data can be allocated usingCPU_ALLOC. The resulting pointer is
+ * pointing to the instance of the variable in the per cpu area provided
+ * by the loader. It is generally an error to use the pointer directly
+ * unless we are booting the system.
+ *
+ *__GFP_ZEROmay be passed as a flag to zero the allocated memory.
+ */mm/vmstat.c +2
-0+ +/* Return a pointer to the instance of a object for a particular processor */ +#define
CPU_PTR(__p, __cpu)SHIFT_PERCPU_PTR((__p),per_cpu_offset(__cpu)) +
+/*
+ * Return a pointer to the instance of the object belonging to the processor + * running the current code.
+ */mm/vmstat.c +13
-0+#define THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), my_cpu_offset) +#define __THIS_CPU (__p) SHIFT_PERCPU_PTR((__p), __my_cpu_offset) + +#define CPU_ALLOC (type, flags) ((typeof(type) *)cpu_alloc(sizeof(type), \ + (flags), __alignof__(type))) +#define CPU_FREE (pointer) cpu_free((pointer), sizeof(*(pointer))) + +/* +* Raw calls +*/ +void *cpu_alloc (unsigned long size,gfp_t flags,unsigned long align); +void cpu_free (void *cpu_pointer,unsigned long size); +#endif /* __LINUX_PERCPU_H */
Re: [patch 02/41] cpu alloc: The allocator
Quoted 11 lines · Rusty Russell
Quoted 6 lines · On Friday 30 May 2008 13:56:22 Christoph Lameter wrote:
The per cpu allocator allows dynamic allocation of memory on all processors simultaneously. A bitmap is used to track used areas. The allocator implements tight packing to reduce the cache footprint and increase speed since cacheline contention is typically not a concern for memory mainly used by a single cpu. Small objects will fill up gaps left by larger allocations that required alignments.
Allocator seems nice and simple, similar to existing one in
module.c(which predates cool bitmap operators).
Quoted 6 lines
Being able to do per-cpu allocations in an interrupt handler seems like encouraging a Bad Idea though: I'd be tempted to avoid the flags word, always zero, and use a mutex instead of a spinlock.
Cheers,
Rusty.
I haven't seen any further discussion on these aspects... is there a consensus to remove the flags from CPU_ALLOC() and use a mutex?
Thanks,
Mike
Re: [patch 02/41] cpu alloc: The allocator
Quoted 2 lines · Mike Travis
I haven't seen any further discussion on these aspects... is there a consensus to remove the flags from
CPU_ALLOC()and use a mutex?
We want to have extensable per cpu areas. This means you need an allocation context. So we need to keep the flags. Mutex is not a bad idea.
Re: [patch 02/41] cpu alloc: The allocator
In reply to Christoph LameterHi!
Quoted 5 lines
Index: linux-2.6/mm/Kconfig
===================================================================
mm/Kconfig config NR_QUICK · line 213
Quoted 9 lines
mm/Kconfig +6
-0@@ -205,3 +205,9 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config CPU_ALLOC_SIZE + int "Size of cpu alloc area" + default "30000" + help + Sets the maximum amount of memory that can be allocated via cpu_alloc
Missing . at end of line, but more important:
How do you expect user to answer this?!
I mean, can they put 0 there and expect kernel to work? If not, you should disallow that config. And you shoudl really explain how much memory is needed... and perhaps mention that size is in bytes?
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH 3/41 cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
In reply to Christoph LameterRemove the builtin per cpu allocator from modules.c and use cpu_alloc instead.
The patch also removes PERCPU_ENOUGH_ROOM. The size of the cpu_alloc area is determined by CONFIG_CPU_AREA_SIZE. PERCPU_ENOUGH_ROOMs default was 8k. CONFIG_CPU_AREA_SIZE defaults to 30k. Thus we have more space to load modules.
--- arch/powerpc/kernel/setup_64.c | 5 - arch/sparc64/kernel/smp.c | 2 arch/x86/kernel/setup.c | 11 +- include/asm-ia64/percpu.h | 2 include/linux/module.h | 1 include/linux/percpu.h | 11 -- init/main.c | 9 -- kernel/lockdep.c | 2 kernel/module.c | 178 +++-------------------------------------- 9 files changed, 28 insertions(+), 193 deletions(-)
kernel/module.c +16 -162
@@ -314,121 +314,6 @@ static struct module *find_module(constreturn NULL ; } -#ifdef CONFIG_SMP -/* Number of blocks used and allocated. */ -static unsigned int pcpu_num_used, pcpu_num_allocated; -/* Size of each block. -ve means used. */ -static int *pcpu_size; - -static int split_block (unsigned int i,unsigned short size) -{ -/* Reallocation required? */ -if (pcpu_num_used +1 > pcpu_num_allocated) { -int *new; - - new =krealloc (pcpu_size,sizeof (new[0 ])*pcpu_num_allocated*2 , -GFP_KERNEL ); -if (!new) -return 0 ; - - pcpu_num_allocated *=2 ; - pcpu_size = new; - } - -/* Insert a new subblock */ -memmove (&pcpu_size[i+1 ], &pcpu_size[i], -sizeof (pcpu_size[0 ]) * (pcpu_num_used - i)); - pcpu_num_used++; - - pcpu_size[i+1 ] -= size; - pcpu_size[i] = size; -return 1 ; -} - -static inline unsigned int block_size (int val) -{ -if (val <0 ) -return -val; -return val; -} - -static void *percpu_modalloc (unsigned long size,unsigned long align, -const char *name) -{ -unsigned long extra; -unsigned int i; -void *ptr; - -if (align >PAGE_SIZE ) { -printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , - name, align,PAGE_SIZE ); - align =PAGE_SIZE ; - } - - ptr = __per_cpu_start; -for (i =0 ; i < pcpu_num_used; ptr +=block_size (pcpu_size[i]), i++) { -/* Extra for alignment requirement. */ - extra =ALIGN ((unsigned long )ptr, align) - (unsigned long )ptr; -BUG_ON (i ==0 && extra !=0 ); - -if (pcpu_size[i] <0 || pcpu_size[i] < extra + size) -continue ; - -/* Transfer extra to previous block. */ -if (pcpu_size[i-1 ] <0 ) - pcpu_size[i-1 ] -= extra; -else - pcpu_size[i-1 ] += extra; - pcpu_size[i] -= extra; - ptr += extra; - -/* Split block if warranted */ -if (pcpu_size[i] - size >sizeof (unsigned long )) -if (!split_block (i, size)) -return NULL ; - -/* Mark allocated */ - pcpu_size[i] = -pcpu_size[i]; -return ptr; - } - -printk (KERN_WARNING "Could not allocate %lu bytes percpu data\n" , - size); -return NULL ; -} - -static void percpu_modfree (void *freeme) -{ -unsigned int i; -void *ptr = __per_cpu_start +block_size (pcpu_size[0 ]); - -/* First entry is core kernel percpu data. */ -for (i =1 ; i < pcpu_num_used; ptr +=block_size (pcpu_size[i]), i++) { -if (ptr == freeme) { - pcpu_size[i] = -pcpu_size[i]; - goto free; - } - } -BUG (); - - free: -/* Merge with previous? */ -if (pcpu_size[i-1 ] >=0 ) { - pcpu_size[i-1 ] += pcpu_size[i]; - pcpu_num_used--; -memmove (&pcpu_size[i], &pcpu_size[i+1 ], - (pcpu_num_used - i) *sizeof (pcpu_size[0 ])); - i--; - } -/* Merge with next? */ -if (i+1 < pcpu_num_used && pcpu_size[i+1 ] >=0 ) { - pcpu_size[i] += pcpu_size[i+1 ]; - pcpu_num_used--; -memmove (&pcpu_size[i+1 ], &pcpu_size[i+2 ], - (pcpu_num_used - (i+1 )) *sizeof (pcpu_size[0 ])); - } -} -static unsigned int find_pcpusec (Elf_Ehdr *hdr,Elf_Shdr *sechdrs,const char *secstrings)
@@ -444,48 +329,6 @@ static void percpu_modcopy(void *pcpudesmemcpy (pcpudest +per_cpu_offset (cpu), from, size); } -static int percpu_modinit (void ) -{ - pcpu_num_used =2 ; - pcpu_num_allocated =2 ; - pcpu_size =kmalloc (sizeof (pcpu_size[0 ]) * pcpu_num_allocated, -GFP_KERNEL ); -/* Static in-kernel percpu data (used). */ - pcpu_size[0 ] = -(__per_cpu_end-__per_cpu_start); -/* Free room. */ - pcpu_size[1 ] =PERCPU_ENOUGH_ROOM + pcpu_size[0 ]; -if (pcpu_size[1 ] <0 ) { -printk (KERN_ERR "No per-cpu room for modules.\n" ); - pcpu_num_used =1 ; - } - -return 0 ; -} -__initcall (percpu_modinit); -#else /* ... !CONFIG_SMP */ -static inline void *percpu_modalloc (unsigned long size,unsigned long align, -const char *name) -{ -return NULL ; -} -static inline void percpu_modfree (void *pcpuptr) -{ -BUG (); -} -static inline unsigned int find_pcpusec (Elf_Ehdr *hdr, -Elf_Shdr *sechdrs, -const char *secstrings) -{ -return 0 ; -} -static inline void percpu_modcopy (void *pcpudst,const void *src, -unsigned long size) -{ -/* pcpusec should be 0, and size of that section should be 0. */ -BUG_ON (size !=0 ); -} -#endif /* CONFIG_SMP */ -#define MODINFO_ATTR (field) \ static void setup_modinfo_##field(struct module *mod, const char *s) \ { \
@@ -1403,7 +1246,7 @@ static void free_module(struct module *mmodule_free (mod, mod->module_init);kfree (mod->args);if (mod->percpu) -percpu_modfree (mod->percpu); +cpu_free (mod->percpu, mod->percpu_size);/* Free lock-classes: */ lockdep_free_key_range (mod->module_core, mod->core_size);
@@ -1772,6 +1615,7 @@ static struct module *load_module(void _unsigned int markersstringsindex;struct module *mod;long err =0 ; +unsigned long percpu_size =0 ;void *percpu =NULL , *ptr =NULL ;/* Stops spurious gcc warning */ struct exception_table_entry *extable;mm_segment_t old_fs;
@@ -1918,15 +1762,25 @@ static struct module *load_module(void _if (pcpuindex) {/* We have a special allocation for this section. */ - percpu =percpu_modalloc (sechdrs[pcpuindex].sh_size, - sechdrs[pcpuindex].sh_addralign, - mod->name); +unsigned long align = sechdrs[pcpuindex].sh_addralign; +unsigned long size = sechdrs[pcpuindex].sh_size; + +if (align >PAGE_SIZE ) { +printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , + mod->name, align,PAGE_SIZE ); + align =PAGE_SIZE ; + } + percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) +printk (KERN_WARNING "Could not allocate %lu bytes percpu data\n" , + size);if (!percpu) { err = -ENOMEM ; goto free_mod; } sechdrs[pcpuindex].sh_flags &= ~(unsigned long )SHF_ALLOC ; mod->percpu = percpu; + mod->percpu_size = percpu_size; }/* Determine total sizes, and put offsets in sh_entsize. For now
@@ -2175,7 +2029,7 @@ static struct module *load_module(void _module_free (mod, mod->module_core); free_percpu:if (percpu) -percpu_modfree (percpu); +cpu_free (percpu, percpu_size); free_mod:kfree (args); free_hdr:
include/linux/percpu.h +0 -11
@@ -34,17 +34,6 @@#define EXPORT_PER_CPU_SYMBOL (var) EXPORT_SYMBOL(per_cpu__##var)#define EXPORT_PER_CPU_SYMBOL_GPL (var) EXPORT_SYMBOL_GPL(per_cpu__##var) -/* Enough to cover all DEFINE_PER_CPUs in kernel, including modules. */ -#ifndef PERCPU_ENOUGH_ROOM -#ifdef CONFIG_MODULES -#define PERCPU_MODULE_RESERVE 8192 -#else -#define PERCPU_MODULE_RESERVE 0 -#endif - -#define PERCPU_ENOUGH_ROOM \ - (__per_cpu_end - __per_cpu_start + PERCPU_MODULE_RESERVE) -#endif /* PERCPU_ENOUGH_ROOM */ /* * Must be an lvalue. Since @var must be a simple identifier,
include/linux/module.h +1 -0
@@ -334,6 +334,7 @@ struct module/* Per-cpu data. */ void *percpu; +int percpu_size;/* The command line arguments (may be mangled). People like keeping pointers to this stuff */
arch/powerpc/kernel/setup_64.c +0 -5
@@ -596,11 +596,6 @@ void __init setup_per_cpu_areas(void)/* Copy section for each CPU (we discard the original) */ size =ALIGN (__per_cpu_end - __per_cpu_start,PAGE_SIZE ); -#ifdef CONFIG_MODULES -if (size <PERCPU_ENOUGH_ROOM ) - size =PERCPU_ENOUGH_ROOM ; -#endif -for_each_possible_cpu (i) { ptr =alloc_bootmem_pages_node (NODE_DATA (cpu_to_node (i)), size);if (!ptr)
arch/sparc64/kernel/smp.c +1 -1
@@ -1454,7 +1454,7 @@ void __init real_setup_per_cpu_areas(voichar *ptr;/* Copy section for each CPU (we discard the original) */ - goal =PERCPU_ENOUGH_ROOM ; + goal = __per_cpu_size; __per_cpu_shift =PAGE_SHIFT ;for (size =PAGE_SIZE ; size < goal; size <<=1UL )
arch/x86/kernel/setup.c +5 -6
@@ -89,30 +89,29 @@ EXPORT_SYMBOL(__per_cpu_offset);void __init setup_per_cpu_areas (void ) {int i, highest_cpu =0 ; -unsigned long size;#ifdef CONFIG_HOTPLUG_CPU prefill_possible_map ();#endif /* Copy section for each CPU (we discard the original) */ - size =PERCPU_ENOUGH_ROOM ;printk (KERN_INFO "PERCPU: Allocating %lu bytes of per cpu data\n" , - size); + __per_cpu_size);for_each_possible_cpu (i) {char *ptr;#ifndef CONFIG_NEED_MULTIPLE_NODES - ptr =alloc_bootmem_pages (size); + ptr =alloc_bootmem_pages (__per_cpu_size);#else int node =early_cpu_to_node (i);if (!node_online (node) || !NODE_DATA (node)) { - ptr =alloc_bootmem_pages (size); + ptr =alloc_bootmem_pages (__per_cpu_size);printk (KERN_INFO "cpu %d has no node or node-local memory\n" , i); }else - ptr =alloc_bootmem_pages_node (NODE_DATA (node), size); + ptr =alloc_bootmem_pages_node (NODE_DATA (node), + __per_cpu_size);#endif if (!ptr)panic ("Cannot allocate cpu data for CPU %d\n" , i);
include/asm-ia64/percpu.h +0 -2
@@ -6,8 +6,6 @@* David Mosberger-Tang <[email protected]> */ -#define PERCPU_ENOUGH_ROOM PERCPU_PAGE_SIZE -#ifdef __ASSEMBLY__# define THIS_CPU (var) (per_cpu__##var)/* use this to mark accesses to per-CPU variables... */ #else /* !__ASSEMBLY__ */
init/main.c +4 -5
@@ -393,18 +393,17 @@ EXPORT_SYMBOL(__per_cpu_offset);static void __init setup_per_cpu_areas (void ) { -unsigned long size, i; +unsigned long i;char *ptr;unsigned long nr_possible_cpus =num_possible_cpus ();/* Copy section for each CPU (we discard the original) */ - size =ALIGN (PERCPU_ENOUGH_ROOM ,PAGE_SIZE ); - ptr =alloc_bootmem_pages (size * nr_possible_cpus); + ptr =alloc_bootmem_pages (__per_cpu_size * nr_possible_cpus);for_each_possible_cpu (i) { __per_cpu_offset[i] = ptr - __per_cpu_start; -memcpy (ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); - ptr += size; +memcpy (ptr, __per_cpu_start, __per_cpu_size); + ptr += __per_cpu_size; } }#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */
kernel/lockdep.c +1 -1
@@ -610,7 +610,7 @@ static int static_obj(void *obj)*/ for_each_possible_cpu (i) { start = (unsigned long ) &__per_cpu_start +per_cpu_offset (i); - end = (unsigned long ) &__per_cpu_start +PERCPU_ENOUGH_ROOM + end = (unsigned long ) &__per_cpu_start + __per_cpu_size +per_cpu_offset (i);if ((addr >= start) && (addr < end))
Andrew Morton
maintainerRe: [patch 03/41] cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
Quoted 9 lines · On Thu, 29 May 2008 20:56:23 -0700 Christoph Lameter <[email protected]> wrote:
Remove the builtin per cpu allocator from
modules.cand usecpu_allocinstead.The patch also removes
PERCPU_ENOUGH_ROOM. The size of thecpu_allocarea is determined byCONFIG_CPU_AREA_SIZE.PERCPU_ENOUGH_ROOMsdefault was 8k.CONFIG_CPU_AREA_SIZEdefaults to 30k. Thus we have more space to load modules....
kernel/module.c static struct module *load_module(void _ · line 1770
Quoted 6 lines
kernel/module.c +6
-0+unsigned long align = sechdrs[pcpuindex].sh_addralign; +unsigned long size = sechdrs[pcpuindex].sh_size; + +if (align >PAGE_SIZE ) { +printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , + mod->name, align,PAGE_SIZE );
Indenting broke.
Alas, PAGE_SIZE has, iirc, unsigned type on some architectures and unsigned long on others. I suspect you'll need to cast it to be able to print it.
kernel/module.c static struct module *load_module(void _ · line 1775
Quoted 5 lines
kernel/module.c +5
-0+ align =PAGE_SIZE ; + } + percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) + printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",
80-col bustage,.
A printk like this should, I think, identify what part of the kernel it came from.
But really, I don't think any printk should be present here. cpu_alloc() itself should dump the warning and the backtrace when it runs out. Because a cpu_alloc() failure is a major catastrophe. It probably means a reconfigure-and-reboot cycle.
Right now it means a reconfigure-kernel-rebuild-kernel-reinstall-kernel-then-reboot cycle. Or a call-vendor-complain-pay-money-and-wait cycle. But I hope we can fix that with the boot parameter thing?
Re: [patch 03/41] cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
Quoted 4 lines · Andrew Morton
kernel/module.c static struct module *load_module(void _ · line 1770
Quoted 2 lines
kernel/module.c +2
-0+printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , + mod->name, align,PAGE_SIZE );Indenting broke.
Hmmm. Okay.
Quoted 3 lines
Alas,
PAGE_SIZEhas, iirc, unsigned type on some architectures and unsigned long on others. I suspect you'll need to cast it to be able to print it.
This is code that was moved.
Quoted 8 lines
kernel/module.c static struct module *load_module(void _ · line 1775
Quoted 3 lines
kernel/module.c +3
-0+ percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) + printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",80-col bustage,.
A printk like this should, I think, identify what part of the kernel it came from.
Again moved code. Should I really do string separations for code that is moved?
Quoted 4 lines
But really, I don't think any printk should be present here.
cpu_alloc()itself should dump the warning and the backtrace when it runs out. Because acpu_alloc()failure is a major catastrophe. It probably means a reconfigure-and-reboot cycle.
The code has been able to deal with an allocpercpu failure in the past. Why would it have trouble with a cpu_alloc failure here?
Andrew Morton
maintainerRe: [patch 03/41] cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
Quoted 21 lines · On Thu, 29 May 2008 22:14:17 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 4 lines · Andrew Morton
kernel/module.c static struct module *load_module(void _ · line 1770
Quoted 2 lines
kernel/module.c +2
-0+printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , + mod->name, align,PAGE_SIZE );Indenting broke.
Hmmm. Okay.
Quoted 3 lines
Alas,
PAGE_SIZEhas, iirc, unsigned type on some architectures and unsigned long on others. I suspect you'll need to cast it to be able to print it.This is code that was moved.
Quoted 6 lines
kernel/module.c static struct module *load_module(void _ · line 1775
Quoted 3 lines
kernel/module.c +3
-0+ percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) + printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",80-col bustage,.
Quoted 5 lines
Quoted 2 lines
A printk like this should, I think, identify what part of the kernel it came from.
Again moved code. Should I really do string separations for code that is moved?
That's not a string separation - it is a functional improvement.
Sure, why not fix these little things while we're there?
Quoted 7 lines
Quoted 4 lines
But really, I don't think any printk should be present here.
cpu_alloc()itself should dump the warning and the backtrace when it runs out. Because acpu_alloc()failure is a major catastrophe. It probably means a reconfigure-and-reboot cycle.The code has been able to deal with an allocpercpu failure in the past. Why would it have trouble with a
cpu_allocfailure here?
Because an alloc_percpu failure is a page allocator failure. This is a well-known situation which we know basically never happens, or at least happens under well-known circumstances.
Whereas a cpu_alloc() failure is a dead box. We cannot fix it via running page reclaim. We cannot fix it via oom-killing someone. We are dead.
Re: [patch 03/41] cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
In reply to Christoph Lameterkernel/module.c static struct module *find_module(const · line 315
Quoted 5 lines · On Friday 30 May 2008 13:56:23 Christoph Lameter wrote:
kernel/module.c +0
-0@@ -314,121 +314,6 @@ static struct module *find_module(constreturn NULL ; }
115 lines removed... This is my favourite part of the series so far :)
kernel/module.c static void free_module(struct module *m · line 1249
Quoted 3 lines
kernel/module.c +1
-1if (mod->percpu) -percpu_modfree (mod->percpu); +cpu_free (mod->percpu, mod->percpu_size);
Hmm, does cpu_free(NULL, 0) do something? Seems like it shouldn't, for symmetry with free().
kernel/module.c static struct module *load_module(void _ · line 1779
Quoted 12 lines
kernel/module.c +9
-0+if (align >PAGE_SIZE ) { +printk (KERN_WARNING "%s: per-cpu alignment %li > %li\n" , + mod->name, align,PAGE_SIZE ); + align =PAGE_SIZE ; + } + percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) +printk (KERN_WARNING "Could not allocate %lu bytes percpu data\n" , + size);if (!percpu) { err = -ENOMEM ; goto free_mod;
OK, we've *never* had a report of the per-cpu alignment message, so I'd be happy to pass that through to cpu_alloc() and have it fail. Also, the if (!percpu) cases should be combined.
kernel/module.c static struct module *load_module(void _ · line 2032
Quoted 4 lines
free_percpu:kernel/module.c +1
-1if (percpu) -percpu_modfree (percpu); +cpu_free (percpu, percpu_size);
As above.
kernel/module.c
Quoted 1 line
kernel/module.c +1
-0+ goal = __per_cpu_size;
Where did __per_cpu_size come from? I missed it in the earlier patches...
Thanks,
Rusty.
Re: [patch 03/41] cpu alloc: Use cpu allocator instead of the builtin modules per cpu allocator
Quoted 2 lines · Rusty Russell
Hmm, does
cpu_free(NULL, 0)do something? Seems like it shouldn't, for symmetry withfree().
No it just returns.
Quoted 11 lines
kernel/module.c static struct module *load_module(void _ · line 1779
Quoted 7 lines
kernel/module.c +4
-0+ percpu =cpu_alloc (size,GFP_KERNEL |__GFP_ZERO, align); +if (!percpu) +printk (KERN_WARNING "Could not allocate %lu bytes percpu data\n" , + size);if (!percpu) { err = -ENOMEM ; goto free_mod;OK, we've *never* had a report of the per-cpu alignment message, so I'd be happy to pass that through to
cpu_alloc()and have it fail. Also, the if (!percpu) cases should be combined.
Ack.
Quoted 6 lines
kernel/module.c static struct module *load_module(void _ · line 2032
Quoted 4 lines
free_percpu:kernel/module.c +1
-1if (percpu) -percpu_modfree (percpu); +cpu_free (percpu, percpu_size);As above.
The if can be dropped.
Quoted 4 lines
arch/sparc64/kernel/smp.c void __init real_setup_per_cpu_areas(voi · line 1457
Quoted 2 lines
arch/sparc64/kernel/smp.c +1
-0+ goal = __per_cpu_size;Where did
__per_cpu_sizecome from? I missed it in the earlier patches...
Its __per_cpu_end - __per_cpu_start.
PATCH 4/41 cpu ops: Core piece for generic atomic per cpu operations
In reply to Christoph LameterCurrently the per cpu subsystem is not able to use the atomic capabilities that are provided by many of the available processors.
This patch adds new functionality that allows the optimizing of per cpu variable handling. In particular it provides a simple way to exploit atomic operations in order to avoid having to disable interrupts or performing address calculation to access per cpu data.
F.e. Using our current methods we may do
unsigned long flags; struct stat_struct *p; local_irq_save(flags); /* Calculate address of per processor area */ p = CPU_PTR(stat, smp_processor_id()); p->counter++; local_irq_restore(flags);
The segment can be replaced by a single atomic CPU operation:
CPU_INC(stat->counter);
Most processors have instructions to perform the increment using a a single atomic instruction. Processors may have segment registers, global registers or per cpu mappings of per cpu areas that can be used to generate atomic instructions that combine the following in a single operation:
1. Adding of an offset / register to a base address
2. Read modify write operation on the address calculated by the instruction.
If 1+2 are combined in an instruction then the instruction is atomic vs interrupts. This means that percpu atomic operations do not need to disable interrupts to increments counters etc.
The existing methods in use in the kernel cannot utilize the power of these atomic instructions. local_t is not really addressing the issue since the offset calculation performed before the atomic operation. The operation is therefor not atomic. Disabling interrupt or preemption is required in order to use local_t.
local_t is also very specific to the x86 processor. The solution here can utilize other methods than just those provided by the x86 instruction set.
On x86 the above CPU_INC translated into a single instruction:
inc %%gs:(&stat->counter)
This instruction is interrupt safe since it can either be completed or not. Both adding of the offset and the read modify write are combined in one instruction.
The determination of the correct per cpu area for the current processor does not require access to smp_processor_id() (expensive...). The gs register is used to provide a processor specific offset to the respective per cpu area where the per cpu variable resides.
Note that the counter offset into the struct was added *before* the segment selector was added. This is necessary to avoid calculations. In the past we first determine the address of the stats structure on the respective processor and then added the field offset. However, the offset may as well be added earlier. The adding of the per cpu offset (here through the gs register) must be done by the instruction used for atomic per cpu access.
If "stat" was declared via DECLARE_PER_CPU then this patchset is capable of convincing the linker to provide the proper base address. In that case no calculations are necessary.
Should the stat structure be reachable via a register then the address calculation capabilities can be leveraged to avoid calculations.
On IA64 we can get the same combination of operations in a single instruction by using the virtual address that always maps to the local per cpu area:
fetchadd &stat->counter + (VCPU_BASE - __per_cpu_start)
The access is forced into the per cpu address reachable via the virtualized address. IA64 allows the embedding of an offset into the instruction. So the fetchadd can perform both the relocation of the pointer into the per cpu area as well as the atomic read modify write cycle.
In order to be able to exploit the atomicity of these instructions we introduce a series of new functions that take either:
1. A per cpu pointer as returned by cpu_alloc() or CPU_ALLOC().
2. A per cpu variable address as returned by per_cpu_var(<percpuvarname>).
CPU_READ()CPU_WRITE()CPU_INCCPU_DECCPU_ADDCPU_SUBCPU_XCHGCPU_CMPXCHG
--- include/linux/percpu.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+)
include/linux/percpu.h +135 -0
@@ -179,4 +179,139 @@void *cpu_alloc (unsigned long size,gfp_t flags,unsigned long align);void cpu_free (void *cpu_pointer,unsigned long size); +/* +* Fast atomic per cpu operations. +* +* The following operations can be overridden by arches to implement fast +* and efficient operations. The operations are atomic meaning that the +* determination of the processor, the calculation of the address and the +* operation on the data is an atomic operation. +* +* The parameter passed to the atomic per cpu operations is an lvalue not a +* pointer to the object. +*/ +#ifndef CONFIG_HAVE_CPU_OPS + +/* +* Fallback in case the arch does not provide for atomic per cpu operations. +* +* The first group of macros is used when it is safe to update the per +* cpu variable because preemption is off (per cpu variables that are not +* updated from interrupt context) or because interrupts are already off. +*/ +#define __CPU_READ (var) \ +({ \ + (*THIS_CPU(&(var))); \ +}) + +#define __CPU_WRITE (var, value) \ +({ \ + *THIS_CPU(&(var)) = (value); \ +}) + +#define __CPU_ADD (var, value) \ +({ \ + *THIS_CPU(&(var)) += (value); \ +}) + +#define __CPU_INC (var) __CPU_ADD((var), 1) +#define __CPU_DEC (var) __CPU_ADD((var), -1) +#define __CPU_SUB (var, value) __CPU_ADD((var), -(value)) + +#define __CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = THIS_CPU(&(obj)); \ + x = *p; \ + if (x == (old)) \ + *p = (new); \ + (x); \ +}) + +#define __CPU_XCHG (obj, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = THIS_CPU(&(obj)); \ + x = *p; \ + *p = (new); \ + (x); \ +}) + +/* +* Second group used for per cpu variables that are not updated from an +* interrupt context. In that case we can simply disable preemption which +* may be free if the kernel is compiled without support for preemption. +*/ +#define _CPU_READ __CPU_READ +#define _CPU_WRITE __CPU_WRITE + +#define _CPU_ADD (var, value) \ +({ \ + preempt_disable(); \ + __CPU_ADD((var), (value)); \ + preempt_enable(); \ +}) + +#define _CPU_INC (var) _CPU_ADD((var), 1) +#define _CPU_DEC (var) _CPU_ADD((var), -1) +#define _CPU_SUB (var, value) _CPU_ADD((var), -(value)) + +#define _CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(addr) x; \ + preempt_disable(); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + preempt_enable(); \ + (x); \ +}) + +#define _CPU_XCHG (var, new) \ +({ \ + typeof(var) x; \ + preempt_disable(); \ + x = __CPU_XCHG((var), (new)); \ + preempt_enable(); \ + (x); \ +}) + +/* +* Third group: Interrupt safe CPU functions +*/ +#define CPU_READ __CPU_READ +#define CPU_WRITE __CPU_WRITE + +#define CPU_ADD (var, value) \ +({ \ + unsigned long flags; \ + local_irq_save(flags); \ + __CPU_ADD((var), (value)); \ + local_irq_restore(flags); \ +}) + +#define CPU_INC (var) CPU_ADD((var), 1) +#define CPU_DEC (var) CPU_ADD((var), -1) +#define CPU_SUB (var, value) CPU_ADD((var), -(value)) + +#define CPU_CMPXCHG (var, old, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) + +#define CPU_XCHG (var, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_XCHG((var), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) + +#endif /* CONFIG_HAVE_CPU_OPS */ +#endif /* __LINUX_PERCPU_H */
Andrew Morton
maintainerRe: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 10 lines · On Thu, 29 May 2008 20:56:24 -0700 Christoph Lameter <[email protected]> wrote:
Currently the per cpu subsystem is not able to use the atomic capabilities that are provided by many of the available processors.
This patch adds new functionality that allows the optimizing of per cpu variable handling. In particular it provides a simple way to exploit atomic operations in order to avoid having to disable interrupts or performing address calculation to access per cpu data.
F.e. Using our current methods we may do
Quoted 8 lines
unsigned long flags; struct stat_struct *p; local_irq_save(flags); /* Calculate address of per processor area */ p = CPU_PTR(stat, smp_processor_id()); p->counter++; local_irq_restore(flags);
eh? That's what local_t is for?
Quoted 3 lines
The segment can be replaced by a single atomic CPU operation:
CPU_INC(stat->counter);
hm, I guess this _has_ to be implemented as a macro. ho hum. But
please: "cpu_inc"?
Quoted 14 lines
Most processors have instructions to perform the increment using a a single atomic instruction. Processors may have segment registers, global registers or per cpu mappings of per cpu areas that can be used to generate atomic instructions that combine the following in a single operation:
1. Adding of an offset / register to a base address
2. Read modify write operation on the address calculated by the instruction.If 1+2 are combined in an instruction then the instruction is atomic vs interrupts. This means that percpu atomic operations do not need to disable interrupts to increments counters etc.
Quoted 5 lines
The existing methods in use in the kernel cannot utilize the power of these atomic instructions.
local_tis not really addressing the issue since the offset calculation performed before the atomic operation. The operation is therefor not atomic. Disabling interrupt or preemption is required in order to uselocal_t.
Your terminology is totally confusing here.
To me, an "atomic operation" is one which is atomic wrt other CPUs:atomic_t, for example.
Here we're talking about atomic-wrt-this-cpu-only, yes?
If so, we should invent a new term for that different concept and stick to it like glue. How about "self-atomic"? Or "locally-atomic" in deference to the existing local_t?
Quoted 1 line
local_tis also very specific to the x86 processor.
And alpha, m32r, mips and powerpc, methinks. Probably others, but people just haven't got around to it.
Quoted 54 lines
The solution here can
utilize other methods than just those provided by the x86 instruction set.On x86 the above
CPU_INCtranslated into a single instruction:inc %%gs:(&stat->counter)This instruction is interrupt safe since it can either be completed or not. Both adding of the offset and the read modify write are combined in one instruction.
The determination of the correct per cpu area for the current processor does not require access to
smp_processor_id()(expensive...). The gs register is used to provide a processor specific offset to the respective per cpu area where the per cpu variable resides.Note that the counter offset into the struct was added *before* the segment selector was added. This is necessary to avoid calculations. In the past we first determine the address of the stats structure on the respective processor and then added the field offset. However, the offset may as well be added earlier. The adding of the per cpu offset (here through the gs register) must be done by the instruction used for atomic per cpu access.
If "stat" was declared via
DECLARE_PER_CPUthen this patchset is capable of convincing the linker to provide the proper base address. In that case no calculations are necessary.Should the stat structure be reachable via a register then the address calculation capabilities can be leveraged to avoid calculations.
On IA64 we can get the same combination of operations in a single instruction by using the virtual address that always maps to the local per cpu area:
fetchadd &stat->counter + (VCPU_BASE - __per_cpu_start)The access is forced into the per cpu address reachable via the virtualized address. IA64 allows the embedding of an offset into the instruction. So the fetchadd can perform both the relocation of the pointer into the per cpu area as well as the atomic read modify write cycle.
In order to be able to exploit the atomicity of these instructions we introduce a series of new functions that take either:
1. A per cpu pointer as returned by
cpu_alloc()orCPU_ALLOC().2. A per cpu variable address as returned by
per_cpu_var(<percpuvarname>).
Quoted 9 lines
CPU_READ()CPU_WRITE()CPU_INCCPU_DECCPU_ADDCPU_SUBCPU_XCHGCPU_CMPXCHG
I think I'll need to come back another time to understand all that ;)
Thanks for writing it up carefully.
include/linux/percpu.h
Quoted 9 lines
--- include/linux/percpu.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+)Index:
linux-2.6/include/linux/percpu.h
===================================================================include/linux/percpu.h +0
-0
I wonder if all this stuff should be in a new header file.
We could get lazy and include that header from percpu.h if needed.
Quoted 6 lines
include/linux/percpu.h +2
-0@@ -179,4 +179,139 @@void *cpu_alloc (unsigned long size,gfp_t flags,unsigned long align);void cpu_free (void *cpu_pointer,unsigned long size); +/* +* Fast atomic per cpu operations.
include/linux/percpu.h line 193
Quoted 10 lines
include/linux/percpu.h +10
-0+* +* The following operations can be overridden by arches to implement fast +* and efficient operations. The operations are atomic meaning that the +* determination of the processor, the calculation of the address and the +* operation on the data is an atomic operation. +* +* The parameter passed to the atomic per cpu operations is an lvalue not a +* pointer to the object. +*/ +#ifndef CONFIG_HAVE_CPU_OPS
If you move this functionality into a new cpu_alloc.h then the below code goes into include/asm-generic/cpu_alloc.h and most architectures' include/asm/cpu_alloc.h will include asm-generic/cpu_alloc.h.
include/linux/percpu.h can still include linux/cpu_alloc.h (which includes asm/cpu_alloc.h) if needed. But it would be better to just teach the .c files to include <linux/cpu_alloc.h>
Quoted 123 lines
include/linux/percpu.h +122
-0+/* +* Fallback in case the arch does not provide for atomic per cpu operations. +* +* The first group of macros is used when it is safe to update the per +* cpu variable because preemption is off (per cpu variables that are not +* updated from interrupt context) or because interrupts are already off. +*/ +#define __CPU_READ (var) \ +({ \ + (*THIS_CPU(&(var))); \ +}) + +#define __CPU_WRITE (var, value) \ +({ \ + *THIS_CPU(&(var)) = (value); \ +}) + +#define __CPU_ADD (var, value) \ +({ \ + *THIS_CPU(&(var)) += (value); \ +}) + +#define __CPU_INC (var) __CPU_ADD((var), 1) +#define __CPU_DEC (var) __CPU_ADD((var), -1) +#define __CPU_SUB (var, value) __CPU_ADD((var), -(value)) + +#define __CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = THIS_CPU(&(obj)); \ + x = *p; \ + if (x == (old)) \ + *p = (new); \ + (x); \ +}) + +#define __CPU_XCHG (obj, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = THIS_CPU(&(obj)); \ + x = *p; \ + *p = (new); \ + (x); \ +}) + +/* +* Second group used for per cpu variables that are not updated from an +* interrupt context. In that case we can simply disable preemption which +* may be free if the kernel is compiled without support for preemption. +*/ +#define _CPU_READ __CPU_READ +#define _CPU_WRITE __CPU_WRITE + +#define _CPU_ADD (var, value) \ +({ \ + preempt_disable(); \ + __CPU_ADD((var), (value)); \ + preempt_enable(); \ +}) + +#define _CPU_INC (var) _CPU_ADD((var), 1) +#define _CPU_DEC (var) _CPU_ADD((var), -1) +#define _CPU_SUB (var, value) _CPU_ADD((var), -(value)) + +#define _CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(addr) x; \ + preempt_disable(); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + preempt_enable(); \ + (x); \ +}) + +#define _CPU_XCHG (var, new) \ +({ \ + typeof(var) x; \ + preempt_disable(); \ + x = __CPU_XCHG((var), (new)); \ + preempt_enable(); \ + (x); \ +}) + +/* +* Third group: Interrupt safe CPU functions +*/ +#define CPU_READ __CPU_READ +#define CPU_WRITE __CPU_WRITE + +#define CPU_ADD (var, value) \ +({ \ + unsigned long flags; \ + local_irq_save(flags); \ + __CPU_ADD((var), (value)); \ + local_irq_restore(flags); \ +}) + +#define CPU_INC (var) CPU_ADD((var), 1) +#define CPU_DEC (var) CPU_ADD((var), -1) +#define CPU_SUB (var, value) CPU_ADD((var), -(value)) + +#define CPU_CMPXCHG (var, old, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) + +#define CPU_XCHG (var, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_XCHG((var), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) + +#endif /* CONFIG_HAVE_CPU_OPS */ +#endif /* __LINUX_PERCPU_H */
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 7 lines · Andrew Morton
Quoted 5 lines
local_irq_save(flags); /* Calculate address of per processor area */ p = CPU_PTR(stat, smp_processor_id()); p->counter++; local_irq_restore(flags);eh? That's what
local_tis for?
No that is what local_t exactly cannot do.
Quoted 6 lines
Quoted 3 lines
The segment can be replaced by a single atomic CPU operation:
CPU_INC(stat->counter);hm, I guess this _has_ to be implemented as a macro. ho hum. But
please: "cpu_inc"?
A lowercase macro?
Quoted 6 lines
Quoted 5 lines
The existing methods in use in the kernel cannot utilize the power of these atomic instructions.
local_tis not really addressing the issue since the offset calculation performed before the atomic operation. The operation is therefor not atomic. Disabling interrupt or preemption is required in order to uselocal_t.
Quoted 6 lines
Your terminology is totally confusing here.
To me, an "atomic operation" is one which is atomic wrt other CPUs:
atomic_t, for example.Here we're talking about atomic-wrt-this-cpu-only, yes?
Right.
Quoted 4 lines
Quoted 1 line
local_tis also very specific to the x86 processor.And alpha, m32r, mips and powerpc, methinks. Probably others, but people just haven't got around to it.
No local_t does not do the relocation of the address to the correct percpu area. It requies disabling of interrupts etc. Its not atomic (wrt interrupts) because of that.
Quoted 3 lines
I think I'll need to come back another time to understand all that ;)
Thanks for writing it up carefully.
Well this stuff is so large in scope that I have difficulties keeping everything straight.
Quoted 3 lines
I wonder if all this stuff should be in a new header file.
We could get lazy and include that header from
percpu.hif needed.
But then its related to percpu operations and relies extensively on the various percpu.h files in asm-generic and asm-arch and include/linux
Andrew Morton
maintainerRe: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 7 lines · On Thu, 29 May 2008 22:17:55 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 4 lines
Quoted 1 line
local_tis also very specific to the x86 processor.And alpha, m32r, mips and powerpc, methinks. Probably others, but people just haven't got around to it.
No
local_tdoes not do the relocation of the address to the correct percpu area. It requies disabling of interrupts etc.
No it doesn't. Look:
static inline void local_inc(local_t *l)
{
asm volatile(_ASM_INC "%0"
: "+m" (l->a.counter));
}Quoted 3 lines
Its not atomic (wrt
interrupts) because of that.
Yes it is.
Quoted 7 lines
Quoted 3 lines
I think I'll need to come back another time to understand all that ;)
Thanks for writing it up carefully.
Well this stuff is so large in scope that I have difficulties keeping everything straight.
Quoted 6 lines
Quoted 3 lines
I wonder if all this stuff should be in a new header file.
We could get lazy and include that header from
percpu.hif needed.But then its related to percpu operations and relies extensively on the various
percpu.hfiles in asm-generic and asm-arch andinclude/linux
Well that should be fixed. We should never have mixed the alloc_percpu() and DEFINE_PER_CPU things inthe same header. They're different.
otoh as you propose removing the old alloc_percpu() I guess the end result is no worse than what we presently have.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 10 lines · Andrew Morton
Quoted 1 line
area. It requies disabling of interrupts etc.
No it doesn't. Look:
static inline void
local_inc(local_t *l){ asm volatile(_ASM_INC "%0" : "+m" (l->a.counter)); }
Quoted 5 lines
Quoted 3 lines
Its not atomic (wrt
interrupts) because of that.Yes it is.
No its not! In order to increment a per cpu value you need to calculate the per cpu pointer address in the current per cpu segment. local_t cannot do that in an atomic (wrt interrupt/preempt fashion) fashion. cpu ops can use a segment prefix and thus the insructions can calculate the per cpu adress and perform the atomic inc without disabling preempt or interrupts.
local_t is only useful when you disable interrupt or premption otherwise. But then you could also use a regular increment.
Quoted 6 lines
Quoted 2 lines
But then its related to percpu operations and relies extensively on the various
percpu.hfiles in asm-generic and asm-arch andinclude/linuxWell that should be fixed. We should never have mixed the
alloc_percpu()andDEFINE_PER_CPUthings inthe same header. They're different.
With cpu_alloc they are the same. They allocate from the same per cpu area.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 13 lines · On Friday 30 May 2008 16:12:59 Christoph Lameter wrote:
Quoted 12 lines · Andrew Morton
Quoted 1 line
area. It requies disabling of interrupts etc.
No it doesn't. Look:
static inline void
local_inc(local_t *l){ asm volatile(_ASM_INC "%0" : "+m" (l->a.counter));}
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Its not atomic (wrt
interrupts) because of that.Yes it is.
No its not! In order to increment a per cpu value you need to calculate the per cpu pointer address in the current per cpu segment.
Christoph, you just missed it, that's all. Look at cpu_local_read et al in include/asm-i386/local.h (ie. before the x86 mergers chose the lowest common denominator one).
Cheers,
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 6 lines · Rusty Russell
Quoted 2 lines
No its not! In order to increment a per cpu value you need to calculate the per cpu pointer address in the current per cpu segment.
Christoph, you just missed it, that's all. Look at
cpu_local_readet al ininclude/asm-i386/local.h(ie. before the x86 mergers chose the lowest common denominator one).
There is no doubt that local_t does perform an atomic vs. interrupt inc for example. But its not usable. Because you need to determine the address of the local_t belonging to the current processor first. As soon as you have loaded a processor specific address you can no longer be preempted because that may change the processor and then the wrong address may be increment (and then we have a race again since now we are incrementing counters belonging to other processors). So local_t at mininum requires disabling preempt.
Believe me I have tried to use local_t repeatedly for vm statistics etc. It always fails on that issue. See f.e. the patch that converts vmstat to cpu alloc and compare with my initial local_t based implementation 2 years ago that bombed out because I assumed that local_t would work right.
cpu ops does both
1. The determination of the address of the object belonging to the local processor.
and
2. The RMW
in one instruction. That avoids having to disable preemption or interrupts and it shortens the instructions significantly.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 11 lines · On Saturday 31 May 2008 04:00:40 Christoph Lameter wrote:
Quoted 6 lines · Rusty Russell
Quoted 2 lines
No its not! In order to increment a per cpu value you need to calculate the per cpu pointer address in the current per cpu segment.
Christoph, you just missed it, that's all. Look at
cpu_local_readet al ininclude/asm-i386/local.h(ie. before the x86 mergers chose the lowest common denominator one).There is no doubt that
local_tdoes perform an atomic vs. interrupt inc for example. But its not usable. Because you need to determine the address of thelocal_tbelonging to the current processor first.
Christoph!
STOP typing, and START reading.
cpu_local_inc() does all this: it takes the name of a local_t var, and is expected to increment this cpu's version of that. You ripped this out and called it CPU_INC().
Do not make me explain it a third time.
Quoted 6 lines
As soon as you
have loaded a processor specific address you can no longer be preempted because that may change the processor and then the wrong address may be increment (and then we have a race again since now we are incrementing counters belonging to other processors). Solocal_tat mininum requires disabling preempt.
Think for a moment. What are the chances that I didn't understand this when I wrote the multiple implementations of local_t?
You are wasting my time explaining the obvious, and wasting your own.
Quoted 2 lines
Believe me I have tried to use
local_trepeatedly for vm statistics etc. It always fails on that issue.
Frankly, I am finding it increasingly easy to believe that you failed. But you are blaming the wrong thing.
There are three implementations of local_t which are obvious. The best is for architectures which can locate and increment a per-cpu var in one instruction (eg. x86). Otherwise, using atomic_t/atomic64_t for local_t provides a general solution. The other general solution would involve local_irq_disable()/increment/local_irq_enable().
My (fading) hope is that this idiocy is an abberation, Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 4 lines
cpu_local_inc()does all this: it takes the name of alocal_tvar, and is expected to increment this cpu's version of that. You ripped this out and called itCPU_INC().
Hi,
I'm attempting to test both approaches to compare the object generated in order to understand the issues involved here. Here's my code:
void test_cpu_inc(int *s)
{
__CPU_INC(s);
}
void test_local_inc(local_t *t)
{
__local_inc(THIS_CPU(t));
}
void test_cpu_local_inc(local_t *t)
{
__cpu_local_inc(t);
}But I don't know how I can use cpu_local_inc because the pointer to the object is not &__get_cpu_var(l):
#define __cpu_local_inc(l) cpu_local_inc((l)) #define cpu_local_inc(l) cpu_local_wrap(local_inc(&__get_cpu_var((l))))
At the minimum, we would need a new local_t op to get the correct CPU_ALLOC'd pointer value for the increment. These new local_t ops for CPU_ALLOC'd variables could use CPU_XXX primitives to implement them, or just a base val_to_ptr primitive to replace __get_cpu_var().
I did notice this in local.h:
* X86_64: This could be done better if we moved the per cpu data directly
* after GS.
... which it now is, so true per_cpu variables could be optimized better as well.
Also, the above cpu_local_wrap(...) adds:
#define cpu_local_wrap(l) \
({ \
preempt_disable(); \
(l); \
preempt_enable(); \
}) \... and there isn't a non-preemption version that I can find.
Here are the objects.
0000000000000000 <test_cpu_inc>:
0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 08 sub $0x8,%rsp 8: 48 89 7d f8 mov %rdi,0xfffffffffffffff8(%rbp) c: 65 48 ff 45 f8 incq %gs:0xfffffffffffffff8(%rbp) 11: c9 leaveq 12: c3 retq
0000000000000013 <test_local_inc>:
13: 55 push %rbp 14: 65 48 8b 05 00 00 00 mov %gs:0(%rip),%rax # 1c <test_local_inc+0x9> 1b: 00 1c: 48 89 e5 mov %rsp,%rbp 1f: 48 ff 04 07 incq (%rdi,%rax,1) 23: c9 leaveq 24: c3 retq
With a new local_t op then test_local_inc probably could be optimized to be the same instructions as test_cpu_inc.
One other distinction is CPU_INC increments an arbitrary sized variable while local_inc requires a local_t variable. This may not make it usable in all cases.
Thanks,
Mike
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 24 lines · On Thursday 05 June 2008 04:18:19 Mike Travis wrote:
Quoted 3 lines
cpu_local_inc()does all this: it takes the name of alocal_tvar, and is expected to increment this cpu's version of that. You ripped this out and called itCPU_INC().Hi,
I'm attempting to test both approaches to compare the object generated in order to understand the issues involved here. Here's my code:
void test_cpu_inc(int *s) { __CPU_INC(s); } void test_local_inc(local_t *t) { __local_inc(THIS_CPU(t)); } void test_cpu_local_inc(local_t *t) { __cpu_local_inc(t); }
Quoted 2 lines
But I don't know how I can use
cpu_local_incbecause the pointer to the object is not&__get_cpu_var(l):
Yes. Because the only true per-cpu vars are the static ones, cpu_local_inc() only works on identifiers, not arbitrary pointers. Once this is fixed, we should be enhancing the infrastructure to allow that (AFAICT it's not too hard, but we should add an __percpu marker for sparse).
Quoted 4 lines
At the minimum, we would need a new
local_top to get the correct CPU_ALLOC'd pointer value for the increment. These newlocal_tops for CPU_ALLOC'd variables could useCPU_XXXprimitives to implement them, or just a baseval_to_ptrprimitive to replace__get_cpu_var().
I think the latter: __get_cpu_ptr() perhaps?
Quoted 7 lines
I did notice this in
local.h:*
X86_64: This could be done better if we moved the per cpu data directly
* after GS.... which it now is, so true
per_cpuvariables could be optimized better as well.
Indeed.
Quoted 11 lines
Also, the above
cpu_local_wrap(...)adds:#define cpu_local_wrap(l) \ ({ \ preempt_disable(); \ (l); \ preempt_enable(); \ }) \... and there isn't a non-preemption version that I can find.
Yes, this should be fixed. I thought i386 had optimized versions pre-merge, but I was wrong (%gs for per-cpu came later, and noone cleaned up these naive versions). Did you want me to write them?
I actually think that using local_t == atomic_t is better than preempt_disable/enable for most archs which can't do atomic deref-and-inc.
Quoted 3 lines
One other distinction is
CPU_INCincrements an arbitrary sized variable whilelocal_increquires alocal_tvariable. This may not make it usable in all cases.
You might be right, but note that local_t is 64 bit on 64-bit platforms. And speculation of possible use cases isn't a good reason to rip out working infrastructure :)
Cheers,
Rusty.
Quoted 3 lines
Thanks,
Mike
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 9 lines · Rusty Russell
Quoted 9 lines
Also, the above
cpu_local_wrap(...)adds:#define cpu_local_wrap(l) \ ({ \ preempt_disable(); \ (l); \ preempt_enable(); \ }) \
Quoted 5 lines
Quoted 1 line
... and there isn't a non-preemption version that I can find.
Yes, this should be fixed. I thought i386 had optimized versions pre-merge, but I was wrong (%gs for per-cpu came later, and noone cleaned up these naive versions). Did you want me to write them?
How can that be fixed? You have no atomic instruction that calculates the per cpu address in one go. And as long as that is the case you need to disable preempt. Otherwise you may increment the per cpu variable of another processor because the process was rescheduled after the address was calculated but before the increment was done.
Quoted 7 lines
Quoted 3 lines
One other distinction is
CPU_INCincrements an arbitrary sized variable whilelocal_increquires alocal_tvariable. This may not make it usable in all cases.You might be right, but note that
local_tis 64 bit on 64-bit platforms. And speculation of possible use cases isn't a good reason to rip out working infrastructure :)
Its fundamentally broken since because of the preemption issue. This is also why local_t is rarely used.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 10 lines · On Tuesday 10 June 2008 05:00:36 Christoph Lameter wrote:
Quoted 9 lines · Rusty Russell
Quoted 9 lines
Also, the above
cpu_local_wrap(...)adds:#define cpu_local_wrap(l) \ ({ \ preempt_disable(); \ (l); \ preempt_enable(); \ }) \
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
... and there isn't a non-preemption version that I can find.
Yes, this should be fixed. I thought i386 had optimized versions pre-merge, but I was wrong (%gs for per-cpu came later, and noone cleaned up these naive versions). Did you want me to write them?
How can that be fixed? You have no atomic instruction that calculates the per cpu address in one go.
Huh? "incl %fs:varname" does exactly this.
Quoted 4 lines
And as long as that is the case you need to
disable preempt. Otherwise you may increment the per cpu variable of another processor because the process was rescheduled after the address was calculated but before the increment was done.
But of course, that is not a problem. You make local_t an atomic_t, and then it doesn't matter which CPU you incremented.
By definition if the caller cared, they would have had premption disabled.
Hope that clarifies,
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 8 lines · Rusty Russell
Quoted 6 lines
Quoted 3 lines
Yes, this should be fixed. I thought i386 had optimized versions pre-merge, but I was wrong (%gs for per-cpu came later, and noone cleaned up these naive versions). Did you want me to write them?
How can that be fixed? You have no atomic instruction that calculates the per cpu address in one go.
Huh? "incl %fs:varname" does exactly this.
Right that is what the cpu alloc patches do. So you could implement cpu_local_inc on top of some of the cpu alloc patches.
Quoted 7 lines
Quoted 4 lines
And as long as that is the case you need to
disable preempt. Otherwise you may increment the per cpu variable of another processor because the process was rescheduled after the address was calculated but before the increment was done.But of course, that is not a problem. You make
local_tanatomic_t, and then it doesn't matter which CPU you incremented.
But then the whole point of local_t is gone. Why not use atomic_t in the first place?
Quoted 1 line
By definition if the caller cared, they would have had premption disabled.
There are numerous instances where the caller does not care about preemption. Its just important that one per cpu counter is increment in the least intrusive way. See f.e. the VM event counters.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 5 lines · On Tuesday 10 June 2008 09:54:09 Christoph Lameter wrote:
Quoted 4 lines · Rusty Russell
Quoted 4 lines
Quoted 3 lines
Yes, this should be fixed. I thought i386 had optimized versions pre-merge, but I was wrong (%gs for per-cpu came later, and noone cleaned up these naive versions). Did you want me to write them?
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
How can that be fixed? You have no atomic instruction that calculates the per cpu address in one go.
Huh? "incl %fs:varname" does exactly this.
Right that is what the cpu alloc patches do. So you could implement
cpu_local_incon top of some of the cpu alloc patches.
Or you could just implement it today as a standalone patch.
Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
And as long as that is the case you need to
disable preempt. Otherwise you may increment the per cpu variable of another processor because the process was rescheduled after the address was calculated but before the increment was done.But of course, that is not a problem. You make
local_tanatomic_t, and then it doesn't matter which CPU you incremented.But then the whole point of
local_tis gone. Why not useatomic_tin the first place?
Because some archs can do better.
Quoted 6 lines
Quoted 2 lines
By definition if the caller cared, they would have had premption disabled.
There are numerous instances where the caller does not care about preemption. Its just important that one per cpu counter is increment in the least intrusive way. See f.e. the VM event counters.
Yes, and that's exactly the point. The VM event counters are exactly a case where you should have used cpu_local_inc.
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 4 lines · Rusty Russell
Quoted 2 lines
Right that is what the cpu alloc patches do. So you could implement
cpu_local_incon top of some of the cpu alloc patches.Or you could just implement it today as a standalone patch.
You need at least the zero basing to enable the use of the segment register on x86_64.
Quoted 4 lines
Quoted 2 lines
But then the whole point of
local_tis gone. Why not useatomic_tin the first place?Because some archs can do better.
The argument does not make any sense. First you want to use atomic_t then not?
Quoted 9 lines
Quoted 6 lines
Quoted 2 lines
By definition if the caller cared, they would have had premption disabled.
There are numerous instances where the caller does not care about preemption. Its just important that one per cpu counter is increment in the least intrusive way. See f.e. the VM event counters.
Yes, and that's exactly the point. The VM event counters are exactly a case where you should have used
cpu_local_inc.
I tried it and did not give any benefit except first failing due to bugs because local_t did not disable preempt6... This led to Andi fixing local_t.
But with the preempt disabling I could not discern what the benefit would be.
CPU_INC does not require disabling of preempt and the cpu alloc patches shorten the code sequence to increment a VM counter significantly.
Here is the header from the patch. How would cpu_local_inc be able to do better unless you adopt this patchset and add a shim layer?
Subject: VM statistics: Use CPU ops
The use of CPU ops here avoids the offset calculations that we used to have to do with per cpu operations. The result of this patch is that event counters are coded with a single instruction the following way:
incq %gs:offset(%rip)
Without these patches this was:
mov %gs:0x8,%rdx
mov %eax,0x38(%rsp)
mov xxx(%rip),%eax
mov %eax,0x48(%rsp)
mov varoffset,%rax
incq 0x110(%rax,%rdx,1)Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 8 lines · On Tuesday 10 June 2008 13:18:25 Christoph Lameter wrote:
Quoted 4 lines · Rusty Russell
Quoted 2 lines
Right that is what the cpu alloc patches do. So you could implement
cpu_local_incon top of some of the cpu alloc patches.Or you could just implement it today as a standalone patch.
You need at least the zero basing to enable the use of the segment register on
x86_64.
Indeed. Works for i386 as is, but 64 bit will need that patch.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
But then the whole point of
local_tis gone. Why not useatomic_tin the first place?Because some archs can do better.
The argument does not make any sense. First you want to use
atomic_tthen not?
You're being obtuse. See previous mail about the three possible implementations of local_t, and the comment in asm-generic/local.h.
The paths forward are clear:
1) Improve x86 local_t (mostly orthogonal to the others, but useful).
2) Implement extensible per-cpu areas.
3) Generalize per-cpu accessors.
4) Extend or replace the module.c per-cpu allocator to alloc from the other areas.
5) Convert alloc_percpu et al. to use the new code.
Hope that clarifies,
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 2 lines · Rusty Russell
You're being obtuse. See previous mail about the three possible implementations of
local_t, and the comment inasm-generic/local.h.
OK. I hope I responded in the other email in a more intelligent fashion.
Quoted 2 lines
The paths forward are clear:
1) Improve x86local_t(mostly orthogonal to the others, but useful).
Not sure about that. Its rarely used and the more general cpu alloc stuff can be used in lots of places as evident by the rest of the patchset. But lets leave it if its important for some reason.
Quoted 5 lines
2) Implement extensible per-cpu areas.
3) Generalize per-cpu accessors.
4) Extend or replace themodule.cper-cpu allocator to alloc from the other areas.
5) Convertalloc_percpuet al. to use the new code.
Yes thanks. We are mostly on the same wavelength.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
In reply to Mike TravisQuoted 8 lines · Mike Travis
0000000000000013 <
test_local_inc>:13: 55 push %rbp 14: 65 48 8b 05 00 00 00 mov %gs:0(%rip),%rax # 1c <test_local_inc+0x9> 1b: 00 1c: 48 89 e5 mov %rsp,%rbp 1f: 48 ff 04 07 incq (%rdi,%rax,1) 23: c9 leaveq 24: c3 retq
Note also that the address calculation occurs before the incq. That is why disabling preemption is required otherwise the processor may change between the determination of the per cpu area address and the increment.
The local_t operations could be modified to avoid the preemption issues with the zero based patches applied. Then there would still be the inflexbility of not being able to increment an arbitrary variable.
I think it is also bad to treat a per cpu variable like an atomic. Its not truly atomic nor are strictly atomic accesses used. It is fine to use regular operations on the per cpu variable provided one has either disabled preemption or interrupts. The per cpu atomic wrt interrupt ops are only useful when preemption and/or interrupts are off.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
In reply to Rusty RussellQuoted 6 lines · Rusty Russell
Quoted 2 lines
Believe me I have tried to use
local_trepeatedly for vm statistics etc. It always fails on that issue.Frankly, I am finding it increasingly easy to believe that you failed. But you are blaming the wrong thing.
Quoted 7 lines
There are three implementations of
local_twhich are obvious. The best is for architectures which can locate and increment a per-cpu var in one instruction (eg. x86). Otherwise, using atomic_t/atomic64_t forlocal_tprovides a general solution. The other general solution would involve local_irq_disable()/increment/local_irq_enable().My (fading) hope is that this idiocy is an abberation,
1. The x86 implementation does not exist because the segment register has so far not been available on x86_64. So you could not do the solution. You need the zero basing. Then you can use per_xxx_add in cpu_inc.
2. The general solution created overhead that is often not needed. If we would have done vm event counters with local_t then we would have atomic overhead for each increment on f.e. IA64. That was not acceptable. cpu_alloc never falls back to atomic operations.
3. local_t is based on the atomic logic. But percpu handling is fundamentally different in that accesses without the special macros are okay provided you are in a non preemptible or irq context! A local_t declaration makes such accesses impossible.
4. The modeling of local_t on atomic_t limits it to 32bit! There is no way to use this with pointers or 64 bit entities. Adding that would duplicate the API for each type added.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 3 lines · On Wednesday 11 June 2008 03:42:15 Christoph Lameter wrote:
1. The x86 implementation does not exist because the segment register has so far not been available on
x86_64. So you could not do the solution. You need the zero basing. Then you can useper_xxx_addincpu_inc.
Yes: for 64 bit x86, getting rid of the PDA or zero-basing is required.
Quoted 4 lines
2. The general solution created overhead that is often not needed. If we would have done vm event counters with
local_tthen we would have atomic overhead for each increment on f.e. IA64. That was not acceptable.cpu_allocnever falls back to atomic operations.
You can implement it either way. I've said that three times now. The current generic one uses atomics, but preempt disable/enable is possible.
Quoted 4 lines
3.
local_tis based on the atomic logic. But percpu handling is fundamentally different in that accesses without the special macros are okay provided you are in a non preemptible or irq context! Alocal_tdeclaration makes such accesses impossible.
Again, untrue. The interface is already there. So feel free to implement __cpu_local_inc et al in terms of preempt enable and disable so it doesn't need to use atomics.
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!
Again wrong. And adding an exclamation mark doesn't make it true.
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 3 lines · Rusty Russell
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!Again wrong. And adding an exclamation mark doesn't make it true.
Ewww ... Its atomic_long_t ahh. Ok then there no 32 bit support. What about pointers?
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 7 lines · Christoph Lameter
Quoted 3 lines · Rusty Russell
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!Again wrong. And adding an exclamation mark doesn't make it true.
Ewww ... Its
atomic_long_tahh. Ok then there no 32 bit support. What about pointers?
sizeof(long) == sizeof(void *) in Linux, right?
If you were to support just a single data type, long would probably be the most useful. Still, it might be more consistent to support int and long, same as atomic.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 6 lines · On Thursday 12 June 2008 10:58:01 Nick Piggin wrote:
Quoted 5 lines · Christoph Lameter
Quoted 3 lines · Rusty Russell
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!Again wrong. And adding an exclamation mark doesn't make it true.
Quoted 8 lines
Quoted 2 lines
Ewww ... Its
atomic_long_tahh. Ok then there no 32 bit support. What about pointers?sizeof(long) ==
sizeof(void *)in Linux, right?If you were to support just a single data type, long would probably be the most useful. Still, it might be more consistent to support int and long, same as atomic.
Sure, but in practice these tend to be simple counters: that could well change when dynamic percpu allocs become first class citizens, but let's not put the cart before the horse...
Per-cpu seems to be particularly prone to over-engineering: see commit 7ff6f08295d90ab20d25200ef485ebb45b1b8d71 from almost two years ago. Grepping here reveals that this infrastructure is still not used.
Cheers,
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 12 lines · Rusty Russell
Quoted 11 lines · On Thursday 12 June 2008 10:58:01 Nick Piggin wrote:
Quoted 7 lines · Christoph Lameter
Quoted 3 lines · Rusty Russell
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!Again wrong. And adding an exclamation mark doesn't make it true.
Ewww ... Its
atomic_long_tahh. Ok then there no 32 bit support. What about pointers?sizeof(long) ==
sizeof(void *)in Linux, right?
Quoted 7 lines
Quoted 3 lines
If you were to support just a single data type, long would probably be the most useful. Still, it might be more consistent to support int and long, same as atomic.
Sure, but in practice these tend to be simple counters: that could well change when dynamic percpu allocs become first class citizens, but let's not put the cart before the horse...
Right, I was just responding to Christoph's puzzling question.
Quoted 3 lines
Per-cpu seems to be particularly prone to over-engineering: see commit
7ff6f08295d90ab20d25200ef485ebb45b1b8d71from almost two years ago. Grepping here reveals that this infrastructure is still not used.
Hmm. Something like that needs the question asked "who uses this?" before it is merged I guess. If it were a trivial patch maybe not, but something like this that sits untested for so long is almost broken by definition ;)
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 24 lines · Nick Piggin
Quoted 12 lines · Rusty Russell
Quoted 11 lines · On Thursday 12 June 2008 10:58:01 Nick Piggin wrote:
Quoted 7 lines · Christoph Lameter
Quoted 3 lines · Rusty Russell
Quoted 1 line
4. The modeling of
local_tonatomic_tlimits it to 32bit!Again wrong. And adding an exclamation mark doesn't make it true.
Ewww ... Its
atomic_long_tahh. Ok then there no 32 bit support. What about pointers?sizeof(long) ==
sizeof(void *)in Linux, right?Quoted 7 lines
Quoted 3 lines
If you were to support just a single data type, long would probably be the most useful. Still, it might be more consistent to support int and long, same as atomic.
Sure, but in practice these tend to be simple counters: that could well change when dynamic percpu allocs become first class citizens, but let's not put the cart before the horse...
Right, I was just responding to Christoph's puzzling question.
Quoted 8 lines
Quoted 3 lines
Per-cpu seems to be particularly prone to over-engineering: see commit
7ff6f08295d90ab20d25200ef485ebb45b1b8d71from almost two years ago. Grepping here reveals that this infrastructure is still not used.Hmm. Something like that needs the question asked "who uses this?" before it is merged I guess. If it were a trivial patch maybe not, but something like this that sits untested for so long is almost broken by definition ;)
Some code of mine which didn't make it beyond -mm used this small per-cpu extension. So the commit you refer to was tested.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 7 lines · Martin Peschke
Quoted 6 lines · Nick Piggin
Quoted 3 lines · Rusty Russell
Per-cpu seems to be particularly prone to over-engineering: see commit
7ff6f08295d90ab20d25200ef485ebb45b1b8d71from almost two years ago. Grepping here reveals that this infrastructure is still not used.
Quoted 7 lines
Quoted 4 lines
Hmm. Something like that needs the question asked "who uses this?" before it is merged I guess. If it were a trivial patch maybe not, but something like this that sits untested for so long is almost broken by definition ;)
Some code of mine which didn't make it beyond -mm used this small per-cpu extension. So the commit you refer to was tested.
Right, but it can easily rot after initial testing if it isn't continually used.
Maybe this isn't the best example because maybe it still works fine. But in general, unused, non-trivial code isn't good just to leave around "just in case" IMO.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Populating the per cpu areas on demand is a good thing especially for configurations with a large number of processors. If we really go to support 4k processor by default then we need to allocate the smallest amount of per cpu structures necessary. Maybe ACPI or so can tell us how many processors are possible and we only allocate those. But it would be best if the percpu structures are only allocated for actually active processors.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 7 lines · On Friday 13 June 2008 03:19:51 Christoph Lameter wrote:
Populating the per cpu areas on demand is a good thing especially for configurations with a large number of processors. If we really go to support 4k processor by default then we need to allocate the smallest amount of per cpu structures necessary. Maybe ACPI or so can tell us how many processors are possible and we only allocate those. But it would be best if the percpu structures are only allocated for actually active processors.
cpu_possible_map should definitely be minimal, but your point is well made:
dynamic percpu could actually cut memory allocation. If we go for a hybrid scheme where static percpu is always allocated from the initial chunk, however, we still need the current pessimistic overallocation.
Mike's a clever guy, I'm sure he'll think of something :) Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 4 lines · Rusty Russell
cpu_possible_mapshould definitely be minimal, but your point is well made:
dynamic percpu could actually cut memory allocation. If we go for a hybrid scheme where static percpu is always allocated from the initial chunk, however, we still need the current pessimistic overallocation.
The initial chunk would mean that the percpu areas all come from the same NUMA node. We really need to allocate from the node that is nearest to a processor (not all processors have processor local memory!).
It would be good to standardize the way that percpu areas are allocated. We have various ways of allocation now in various arches. init/main.c:setup_per_cpu_ares() needs to be generalized:
1. Allocate the per cpu areas in a NUMA aware fashions.
2. Have a function for instantiating a single per cpu area that can be used during cpu hotplug.
3. Some hooks for arches to override particular behavior as needed. F.e. IA64 allocates percpu structures in a special way. x86_64 needs to do some tricks for the pda etc etc.
Quoted 1 line
Mike's a clever guy, I'm sure he'll think of something :)
Hopefully. Otherwise he will ask me =-).
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 9 lines · On Friday 13 June 2008 12:27:07 Christoph Lameter wrote:
Quoted 4 lines · Rusty Russell
cpu_possible_mapshould definitely be minimal, but your point is well
made: dynamic percpu could actually cut memory allocation. If we go for a hybrid scheme where static percpu is always allocated from the initial chunk, however, we still need the current pessimistic overallocation.The initial chunk would mean that the percpu areas all come from the same NUMA node. We really need to allocate from the node that is nearest to a processor (not all processors have processor local memory!).
Yes, this is where it gets nasty. We shouldn't even allocate the initial chunk in a non-NUMA aware way (I'm using the term chunk loosely, it's a chunk per cpu of course).
Quoted 5 lines
It would be good to standardize the way that percpu areas are allocated. We have various ways of allocation now in various arches. init/main.c:setup_per_cpu_ares() needs to be generalized:
1. Allocate the per cpu areas in a NUMA aware fashions.
Definitely. We also need to reserve virtual address space to create more areas with congruent mappings; that's the fun part.
Maybe a simpler non-NUMA variant too, but it's trivial if we want it.
Quoted 2 lines
2. Have a function for instantiating a single per cpu area that can be used during cpu hotplug.
Unfortunately this breaks the current percpu semantics: that if you iterate over all possible cpus you can access percpu vars. This means you don't need to have hotplug CPU notifiers for simple percpu counters. We could do this with helpers, but AFAICT it's orthogonal to the other plans.
Quoted 3 lines
3. Some hooks for arches to override particular behavior as needed. F.e. IA64 allocates percpu structures in a special way.
x86_64needs to do some tricks for the pda etc etc.
IA64 is going to need some work, since dynamic percpu addresses won't be able to use their pinned TLB trick to get the local version.
Quoted 3 lines
Quoted 1 line
Mike's a clever guy, I'm sure he'll think of something :)
Hopefully. Otherwise he will ask me =-).
And as always, lkml will offer feedback; useful and otherwise :)
Cheers,
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 6 lines · Rusty Russell
Quoted 3 lines
3. Some hooks for arches to override particular behavior as needed. F.e. IA64 allocates percpu structures in a special way.
x86_64needs to do some tricks for the pda etc etc.IA64 is going to need some work, since dynamic percpu addresses won't be able to use their pinned TLB trick to get the local version.
The ia64 hook could simply return the address of percpu area that was reserved when the per node memory layout was generated (which happens very early during node bootstrap).
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 11 lines · On Tuesday 17 June 2008 00:52:08 Christoph Lameter wrote:
Quoted 6 lines · Rusty Russell
Quoted 3 lines
3. Some hooks for arches to override particular behavior as needed. F.e. IA64 allocates percpu structures in a special way.
x86_64needs to do some tricks for the pda etc etc.IA64 is going to need some work, since dynamic percpu addresses won't be able to use their pinned TLB trick to get the local version.
The ia64 hook could simply return the address of percpu area that was reserved when the per node memory layout was generated (which happens very early during node bootstrap).
Apologies, this time I read the code. I thought IA64 used the pinned TLB area to access per-cpu vars under some circumstances, but they only do that via an arch-specific macro.
So creating new congruent mappings to expand the percpu area(s) is our main concern now?
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
Quoted 10 lines · Rusty Russell
Quoted 3 lines
The ia64 hook could simply return the address of percpu area that was reserved when the per node memory layout was generated (which happens very early during node bootstrap).
Apologies, this time I read the code. I thought IA64 used the pinned TLB area to access per-cpu vars under some circumstances, but they only do that via an arch-specific macro.
So creating new congruent mappings to expand the percpu area(s) is our main concern now?
The concern here was just consolidating the setup code for the per cpu areas.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
In reply to Rusty RussellQuoted 11 lines · Rusty Russell
Quoted 9 lines · On Tuesday 17 June 2008 00:52:08 Christoph Lameter wrote:
Quoted 5 lines · Rusty Russell
Quoted 3 lines
3. Some hooks for arches to override particular behavior as needed. F.e. IA64 allocates percpu structures in a special way.
x86_64needs to do some tricks for the pda etc etc.IA64 is going to need some work, since dynamic percpu addresses won't be able to use their pinned TLB trick to get the local version.
The ia64 hook could simply return the address of percpu area that was reserved when the per node memory layout was generated (which happens very early during node bootstrap).
Quoted 8 lines
Apologies, this time I read the code. I thought IA64 used the pinned TLB area to access per-cpu vars under some circumstances, but they only do that via an arch-specific macro.
So creating new congruent mappings to expand the percpu area(s) is our main concern now?
Rusty.
Not exactly. Getting the system to not panic early in the boot (before x86_64_start_kernel()) is the primary problem right now. This happens in the tip tree with the change to use zero-based percpu offsets. It gets much farther on the linux-next tree.
Thanks,
Mike
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
In reply to Andrew MortonQuoted 1 line · On Friday 30 May 2008 15:38:25 Andrew Morton wrote:
On Thu, 29 May 2008 22:17:55 -0700 (PDT) Christoph Lameter
Quoted 9 lines · <[email protected]> wrote:
Quoted 2 lines
But then its related to percpu operations and relies extensively on the various
percpu.hfiles in asm-generic and asm-arch andinclude/linuxWell that should be fixed. We should never have mixed the
alloc_percpu()andDEFINE_PER_CPUthings inthe same header. They're different.otoh as you propose removing the old
alloc_percpu()I guess the end result is no worse than what we presently have.
No, the worst thing is that this is a great deal of churn which doesn't actually fix the "running out of per-cpu memory" problem.
It can, and should, be fixed, before changing dynamic percpu alloc to use the same percpu pool.
Rusty.
Re: [patch 04/41] cpu ops: Core piece for generic atomic per cpu operations
In reply to Christoph LameterQuoted 10 lines · On Friday 30 May 2008 15:17:55 Christoph Lameter wrote:
Quoted 7 lines · Andrew Morton
Quoted 5 lines
local_irq_save(flags); /* Calculate address of per processor area */ p = CPU_PTR(stat, smp_processor_id()); p->counter++; local_irq_restore(flags);eh? That's what
local_tis for?No that is what
local_texactly cannot do.
Yes, but this is local_t for dynamically allocated per-cpu vars. You've lost potential symmetry and invented a whole new nomenclature :(
local_ptr_inc() etc would be far preferable IMHO.
Cheers,
Rusty.
PATCH 5/41 cpu alloc: Percpu_counter conversion
In reply to Christoph LameterUse cpu_alloc instead of allocpercpu.
--- lib/percpu_counter.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
lib/percpu_counter.c +10 -12
@@ -20,7 +20,7 @@spin_lock (&fbc->lock);for_each_possible_cpu (cpu) { -s32 *pcount =per_cpu_ptr (fbc->counters, cpu); +s32 *pcount =CPU_PTR (fbc->counters, cpu); *pcount =0 ; } fbc->count = amount;
@@ -31,20 +31,18 @@void __percpu_counter_add (struct percpu_counter *fbc,s64 amount,s32 batch) {s64 count; -s32 *pcount; -int cpu =get_cpu (); - pcount =per_cpu_ptr (fbc->counters, cpu); - count = *pcount + amount; +preempt_disable (); + count =__CPU_READ (*fbc->counters) + amount;if (count >= batch || count <= -batch) {spin_lock (&fbc->lock); fbc->count += count; - *pcount =0 ; +__CPU_WRITE (*fbc->counters,0 );spin_unlock (&fbc->lock); }else { - *pcount = count; +__CPU_WRITE (*fbc->counters, count); } -put_cpu (); +preempt_enable (); }EXPORT_SYMBOL (__percpu_counter_add);
@@ -60,7 +58,7 @@spin_lock (&fbc->lock); ret = fbc->count;for_each_online_cpu (cpu) { -s32 *pcount =per_cpu_ptr (fbc->counters, cpu); +s32 *pcount =CPU_PTR (fbc->counters, cpu); ret += *pcount; }spin_unlock (&fbc->lock);
@@ -74,7 +72,7 @@ {spin_lock_init (&fbc->lock); fbc->count = amount; - fbc->counters =alloc_percpu (s32); + fbc->counters =CPU_ALLOC (s32,GFP_KERNEL |__GFP_ZERO);if (!fbc->counters)return -ENOMEM ;#ifdef CONFIG_HOTPLUG_CPU
@@ -101,7 +99,7 @@if (!fbc->counters)return ; -free_percpu (fbc->counters); +CPU_FREE (fbc->counters); fbc->counters =NULL ;#ifdef CONFIG_HOTPLUG_CPU mutex_lock (&percpu_counters_lock);
@@ -128,7 +126,7 @@unsigned long flags;spin_lock_irqsave (&fbc->lock, flags); - pcount =per_cpu_ptr (fbc->counters, cpu); + pcount =CPU_PTR (fbc->counters, cpu); fbc->count += *pcount; *pcount =0 ;spin_unlock_irqrestore (&fbc->lock, flags);
Re: [patch 05/41] cpu alloc: Percpu_counter conversion
Quoted 1 line · On Friday 30 May 2008 13:56:25 Christoph Lameter wrote:
Use
cpu_allocinstead of allocpercpu.
These patches seem like useless churn.
Plus, the new code is uglier than the old code :(
Rusty.
Re: [patch 05/41] cpu alloc: Percpu_counter conversion
Quoted 6 lines · Rusty Russell
Quoted 1 line · On Friday 30 May 2008 13:56:25 Christoph Lameter wrote:
Use
cpu_allocinstead of allocpercpu.These patches seem like useless churn.
Plus, the new code is uglier than the old code :(
It drastically reduces the memory size f.e. 4 byte allocations require SLAB f.e. to allocate a 32 byte chunk. This reduces memory requirements by 32/4 = 8 times.
Plus the per cpu counters allocated in order are likely placed in the same cacheline (whereas the slab allocators avoid placing multiple objects in the same cacheline). Reduces cache footprint.
PATCH 6/41 cpu alloc: crash_notes conversion
In reply to Christoph LameterConvert crash_notes access to use cpu alloc.
--- arch/ia64/kernel/crash.c | 2 +- drivers/base/cpu.c | 2 +- kernel/kexec.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-)
arch/ia64/kernel/crash.c +1 -1
@@ -72,7 +72,7 @@ dst[46 ] = (unsigned long )ia64_rse_skip_regs ((unsigned long *)dst[46 ], sof - sol); - buf = (u64 *)per_cpu_ptr (crash_notes, cpu); + buf = (u64 *)CPU_PTR (crash_notes, cpu);if (!buf)return ; buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, prstatus,
drivers/base/cpu.c +1 -1
@@ -95,7 +95,7 @@* boot up and this data does not change there after. Hence this * operation should be safe. No locking required. */ - addr =__pa (per_cpu_ptr (crash_notes, cpunum)); + addr =__pa (CPU_PTR (crash_notes, cpunum)); rc =sprintf (buf,"%Lx\n" , addr);return rc; }
kernel/kexec.c +2 -2
@@ -1121,7 +1121,7 @@* squirrelled away. ELF notes happen to provide * all of that, so there is no need to invent something new. */ - buf = (u32 *)per_cpu_ptr (crash_notes, cpu); + buf = (u32 *)CPU_PTR (crash_notes, cpu);if (!buf)return ;memset (&prstatus,0 ,sizeof (prstatus));
@@ -1135,7 +1135,7 @@static int __init crash_notes_memory_init (void ) {/* Allocate memory for saving cpu registers. */ - crash_notes =alloc_percpu (note_buf_t); + crash_notes =CPU_ALLOC (note_buf_t,GFP_KERNEL |__GFP_ZERO);if (!crash_notes) {printk ("Kexec: Memory allocation for saving cpu register" " states failed\n" );
PATCH 7/41 cpu alloc: Workqueue conversion
In reply to Christoph LameterConvert the workqueue waitqueue handling to cpu alloc.
The second parameter to wq_per_cpu() is always the current processor id. So drop the parameter and use THIS_CPU in wq_per_cpu that we rename to wq_this_cpu.
--- kernel/workqueue.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-)
kernel/workqueue.c +21 -19
@@ -95,11 +95,11 @@ }static -struct cpu_workqueue_struct *wq_per_cpu (struct workqueue_struct *wq,int cpu) +struct cpu_workqueue_struct *wq_this_cpu (struct workqueue_struct *wq) {if (unlikely (is_single_threaded (wq))) - cpu = singlethread_cpu; -return per_cpu_ptr (wq->cpu_wq, cpu); +return CPU_PTR (wq->cpu_wq, singlethread_cpu); +return THIS_CPU (wq->cpu_wq); }/*
@@ -167,8 +167,9 @@if (!test_and_set_bit (WORK_STRUCT_PENDING ,work_data_bits (work))) {BUG_ON (!list_empty (&work->entry)); -__queue_work (wq_per_cpu (wq,get_cpu ()), work); -put_cpu (); +preempt_disable (); +__queue_work (wq_this_cpu (wq), work); +preempt_enable (); ret =1 ; }return ret;
@@ -181,7 +182,7 @@struct cpu_workqueue_struct *cwq =get_wq_data (&dwork->work);struct workqueue_struct *wq = cwq->wq; -__queue_work (wq_per_cpu (wq,smp_processor_id ()), &dwork->work); +__queue_work (wq_this_cpu (wq), &dwork->work); }/**
@@ -225,7 +226,7 @@timer_stats_timer_set_start_info (&dwork->timer);/* This stores cwq for the moment, for the timer_fn */ -set_wq_data (work,wq_per_cpu (wq,raw_smp_processor_id ())); +set_wq_data (work,wq_this_cpu (wq)); timer->expires = jiffies + delay; timer->data = (unsigned long )dwork; timer->function = delayed_work_timer_fn;
@@ -398,7 +399,7 @@lock_acquire (&wq->lockdep_map,0 ,0 ,0 ,2 , _THIS_IP_);lock_release (&wq->lockdep_map,1 , _THIS_IP_);for_each_cpu_mask (cpu, *cpu_map) -flush_cpu_workqueue (per_cpu_ptr (wq->cpu_wq, cpu)); +flush_cpu_workqueue (CPU_PTR (wq->cpu_wq, cpu)); }EXPORT_SYMBOL_GPL (flush_workqueue);
@@ -478,7 +479,7 @@ cpu_map =wq_cpu_map (wq);for_each_cpu_mask (cpu, *cpu_map) -wait_on_cpu_work (per_cpu_ptr (wq->cpu_wq, cpu), work); +wait_on_cpu_work (CPU_PTR (wq->cpu_wq, cpu), work); } static int __cancel_work_timer(struct work_struct *work,
@@ -598,21 +599,21 @@int cpu;struct work_struct *works; - works = alloc_percpu(struct work_struct); + works = CPU_ALLOC(struct work_struct, GFP_KERNEL);if (!works)return -ENOMEM ;get_online_cpus ();for_each_online_cpu (cpu) { -struct work_struct *work =per_cpu_ptr (works, cpu); +struct work_struct *work =CPU_PTR (works, cpu);INIT_WORK (work, func);set_bit (WORK_STRUCT_PENDING ,work_data_bits (work)); -__queue_work (per_cpu_ptr (keventd_wq->cpu_wq, cpu), work); +__queue_work (CPU_PTR (keventd_wq->cpu_wq, cpu), work); }flush_workqueue (keventd_wq);put_online_cpus (); -free_percpu (works); +CPU_FREE (works);return 0 ; }
@@ -661,7 +662,7 @@BUG_ON (!keventd_wq); - cwq =per_cpu_ptr (keventd_wq->cpu_wq, cpu); + cwq =CPU_PTR (keventd_wq->cpu_wq, cpu);if (current == cwq->thread) ret =1 ;
@@ -672,7 +673,7 @@static struct cpu_workqueue_struct *init_cpu_workqueue (struct workqueue_struct *wq,int cpu) { -struct cpu_workqueue_struct *cwq =per_cpu_ptr (wq->cpu_wq, cpu); +struct cpu_workqueue_struct *cwq =CPU_PTR (wq->cpu_wq, cpu); cwq->wq = wq;spin_lock_init (&cwq->lock);
@@ -730,7 +731,8 @@if (!wq)return NULL ; - wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct); + wq->cpu_wq = CPU_ALLOC(struct cpu_workqueue_struct, +GFP_KERNEL |__GFP_ZERO);if (!wq->cpu_wq) {kfree (wq);return NULL ;
@@ -814,10 +816,10 @@spin_unlock (&workqueue_lock);for_each_cpu_mask (cpu, *cpu_map) -cleanup_workqueue_thread (per_cpu_ptr (wq->cpu_wq, cpu)); +cleanup_workqueue_thread (CPU_PTR (wq->cpu_wq, cpu));put_online_cpus (); -free_percpu (wq->cpu_wq); +CPU_FREE (wq->cpu_wq);kfree (wq); }EXPORT_SYMBOL_GPL (destroy_workqueue);
@@ -838,7 +840,7 @@ }list_for_each_entry (wq, &workqueues, list) { - cwq =per_cpu_ptr (wq->cpu_wq, cpu); + cwq =CPU_PTR (wq->cpu_wq, cpu);switch (action) {case CPU_UP_PREPARE :
PATCH 8/41 cpu alloc: ACPI cstate handling conversion
In reply to Christoph LameterConvert ACPI per cpu handling to cpu alloc.
--- arch/x86/kernel/acpi/cstate.c | 9 +++++---- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 7 ++++--- drivers/acpi/processor_perflib.c | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-)
arch/x86/kernel/acpi/cstate.c +5 -4
@@ -85,7 +85,7 @@if (reg->bit_offset !=NATIVE_CSTATE_BEYOND_HALT )return -1 ; - percpu_entry =per_cpu_ptr (cpu_cstate_entry, cpu); + percpu_entry =CPU_PTR (cpu_cstate_entry, cpu); percpu_entry->states[cx->index].eax =0 ; percpu_entry->states[cx->index].ecx =0 ;
@@ -138,7 +138,7 @@unsigned int cpu =smp_processor_id ();struct cstate_entry *percpu_entry; - percpu_entry =per_cpu_ptr (cpu_cstate_entry, cpu); + percpu_entry =CPU_PTR (cpu_cstate_entry, cpu);mwait_idle_with_hints (percpu_entry->states[cx->index].eax, percpu_entry->states[cx->index].ecx); }
@@ -150,13 +150,14 @@if (c->x86_vendor !=X86_VENDOR_INTEL )return -1 ; - cpu_cstate_entry = alloc_percpu(struct cstate_entry); + cpu_cstate_entry = CPU_ALLOC(struct cstate_entry, +GFP_KERNEL |__GFP_ZERO);return 0 ; } static void __exit ffh_cstate_exit(void) { -free_percpu (cpu_cstate_entry); +CPU_FREE (cpu_cstate_entry); cpu_cstate_entry =NULL ; }
arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +4 -3
@@ -524,7 +524,8 @@ {dprintk ("acpi_cpufreq_early_init\n" ); - acpi_perf_data = alloc_percpu(struct acpi_processor_performance); + acpi_perf_data = CPU_ALLOC(struct acpi_processor_performance, +GFP_KERNEL |__GFP_ZERO);if (!acpi_perf_data) {dprintk ("Memory allocation error for acpi_perf_data.\n" );return -ENOMEM ;
@@ -580,7 +581,7 @@if (!data)return -ENOMEM ; - data->acpi_data =percpu_ptr (acpi_perf_data, cpu); + data->acpi_data =CPU_PTR (acpi_perf_data, cpu);per_cpu (drv_data, cpu) = data;if (cpu_has (c,X86_FEATURE_CONSTANT_TSC ))
@@ -794,7 +795,7 @@cpufreq_unregister_driver (&acpi_cpufreq_driver); -free_percpu (acpi_perf_data); +CPU_FREE (acpi_perf_data);return ; }
drivers/acpi/processor_perflib.c +2 -2
@@ -583,12 +583,12 @@continue ; } -if (!performance || !percpu_ptr (performance, i)) { +if (!performance || !CPU_PTR (performance, i)) { retval = -EINVAL ;continue ; } - pr->performance =percpu_ptr (performance, i); + pr->performance =CPU_PTR (performance, i);cpu_set (i, pr->performance->shared_cpu_map);if (acpi_processor_get_psd (pr)) { retval = -EINVAL ;
PATCH 9/41 cpu alloc: Genhd statistics conversion
In reply to Christoph LameterConvert genhd statistics to cpu alloc. The patch also drops the UP special casing of the statistics.
--- include/linux/genhd.h | 113 +++++++++++--------------------------------------- 1 file changed, 25 insertions(+), 88 deletions(-)
include/linux/genhd.h +25 -88
@@ -95,11 +95,7 @@ struct hd_struct {#endif unsigned long stamp;int in_flight; -#ifdef CONFIG_SMP struct disk_stats *dkstats; -#else -struct disk_stats dkstats; -#endif };#define GENHD_FL_REMOVABLE 1
@@ -135,11 +131,7 @@ struct gendisk {atomic_t sync_io;/* RAID */ unsigned long stamp;int in_flight; -#ifdef CONFIG_SMP struct disk_stats *dkstats; -#else -struct disk_stats dkstats; -#endif struct work_struct async_notify; };
@@ -163,16 +155,15 @@ static inline struct hd_struct *get_partreturn NULL ; } -#ifdef CONFIG_SMP #define __disk_stat_add (gendiskp, field, addnd) \ - (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) + __CPU_ADD(gendiskp->dkstats->field, addnd)#define disk_stat_read (gendiskp, field) \ ({ \ typeof(gendiskp->dkstats->field) res = 0; \ int i; \ for_each_possible_cpu(i) \ - res += per_cpu_ptr(gendiskp->dkstats, i)->field; \ + res += CPU_PTR(gendiskp->dkstats, i)->field; \ res; \ })
@@ -180,12 +171,12 @@ static inline void disk_stat_set_all(strint i;for_each_possible_cpu (i) -memset (per_cpu_ptr (gendiskp->dkstats, i), value, +memset (CPU_PTR (gendiskp->dkstats, i), value,sizeof (struct disk_stats )); -} +} -#define __part_stat_add (part, field, addnd) \ - (per_cpu_ptr(part->dkstats, smp_processor_id())->field += addnd) +#define __part_stat_add (part, field, addnd) \ + __CPU_ADD(part->dkstats->field, addnd)#define __all_stat_add (gendiskp, part, field, addnd, sector) \ ({ \
@@ -199,7 +190,7 @@ static inline void disk_stat_set_all(str typeof(part->dkstats->field) res = 0; \int i; \for_each_possible_cpu (i) \ - res += per_cpu_ptr(part->dkstats, i)->field; \ + res += CPU_PTR(part->dkstats, i)->field; \ res; \ })
@@ -208,56 +199,23 @@ static inline void part_stat_set_all(strint i;for_each_possible_cpu (i) -memset (per_cpu_ptr (part->dkstats, i), value, +memset (CPU_PTR (part->dkstats, i), value,sizeof (struct disk_stats )); } - -#else /* !CONFIG_SMP */ -#define __disk_stat_add (gendiskp, field, addnd) \ - (gendiskp->dkstats.field += addnd) -#define disk_stat_read (gendiskp, field) (gendiskp->dkstats.field) - -static inline void disk_stat_set_all (struct gendisk *gendiskp,int value) -{ -memset (&gendiskp->dkstats, value,sizeof (struct disk_stats )); -} - -#define __part_stat_add (part, field, addnd) \ - (part->dkstats.field += addnd) - -#define __all_stat_add (gendiskp, part, field, addnd, sector) \ -({ \ - if (part) \ - part->dkstats.field += addnd; \ - __disk_stat_add(gendiskp, field, addnd); \ -}) - -#define part_stat_read (part, field) (part->dkstats.field) - -static inline void part_stat_set_all (struct hd_struct *part,int value) -{ -memset (&part->dkstats, value,sizeof (struct disk_stats )); -} - -#endif /* CONFIG_SMP */ #define disk_stat_add (gendiskp, field, addnd) \ - do { \ - preempt_disable(); \ - __disk_stat_add(gendiskp, field, addnd); \ - preempt_enable(); \ - } while (0) + _CPU_ADD(gendiskp->dkstats->field, addnd) -#define __disk_stat_dec (gendiskp, field) __disk_stat_add(gendiskp, field, -1) -#define disk_stat_dec (gendiskp, field) disk_stat_add(gendiskp, field, -1) +#define __disk_stat_dec (gendiskp, field) __CPU_DEC(gendiskp->dkstats->field) +#define disk_stat_dec (gendiskp, field) _CPU_DEC(gendiskp->dkstats->field) -#define __disk_stat_inc (gendiskp, field) __disk_stat_add(gendiskp, field, 1) -#define disk_stat_inc (gendiskp, field) disk_stat_add(gendiskp, field, 1) +#define __disk_stat_inc (gendiskp, field) __CPU_INC(gendiskp->dkstats->field) +#define disk_stat_inc (gendiskp, field) _CPU_INC(gendiskp->dkstats->field)#define __disk_stat_sub (gendiskp, field, subnd) \ - __disk_stat_add(gendiskp, field, -subnd) + __CPU_SUB(gendisk->dkstats->field, subnd)#define disk_stat_sub (gendiskp, field, subnd) \ - disk_stat_add(gendiskp, field, -subnd) + _CPU_SUB(gendisk->dkstats->field, subnd)#define part_stat_add (gendiskp, field, addnd) \ do { \
@@ -266,16 +224,16 @@ static inline void part_stat_set_all(strpreempt_enable (); \ } while (0) -#define __part_stat_dec (gendiskp, field) __part_stat_add(gendiskp, field, -1) -#define part_stat_dec (gendiskp, field) part_stat_add(gendiskp, field, -1) +#define __part_stat_dec (gendiskp, field) __CPU_DEC(gendiskp->dkstats->field) +#define part_stat_dec (gendiskp, field) _CPU_DEC(gendiskp->dkstats->field) -#define __part_stat_inc (gendiskp, field) __part_stat_add(gendiskp, field, 1) -#define part_stat_inc (gendiskp, field) part_stat_add(gendiskp, field, 1) +#define __part_stat_inc (gendiskp, field) __CPU_INC(gendiskp->dkstats->field) +#define part_stat_inc (gendiskp, field) _CPU_INC(gendiskp->dkstats->field)#define __part_stat_sub (gendiskp, field, subnd) \ - __part_stat_add(gendiskp, field, -subnd) + __CPU_SUB(gendiskp->dkstats->field, subnd)#define part_stat_sub (gendiskp, field, subnd) \ - part_stat_add(gendiskp, field, -subnd) + _CPU_SUB(gendiskp->dkstats->field, subnd)#define all_stat_add (gendiskp, part, field, addnd, sector) \ do { \
@@ -300,10 +258,9 @@ static inline void part_stat_set_all(strall_stat_add (gendiskp, part, field, -subnd, sector) /* Inlines to alloc and free disk stats in struct gendisk */ -#ifdef CONFIG_SMP static inline int init_disk_stats (struct gendisk *disk) { - disk->dkstats = alloc_percpu(struct disk_stats); + disk->dkstats = CPU_ALLOC(struct disk_stats, GFP_KERNEL | __GFP_ZERO);if (!disk->dkstats)return 0 ;return 1 ;
@@ -311,12 +268,12 @@ static inline int init_disk_stats(structstatic inline void free_disk_stats (struct gendisk *disk) { -free_percpu (disk->dkstats); +CPU_FREE (disk->dkstats); }static inline int init_part_stats (struct hd_struct *part) { - part->dkstats = alloc_percpu(struct disk_stats); + part->dkstats = CPU_ALLOC(struct disk_stats, GFP_KERNEL|__GFP_ZERO);if (!part->dkstats)return 0 ;return 1 ;
@@ -324,28 +281,8 @@ static inline int init_part_stats(structstatic inline void free_part_stats (struct hd_struct *part) { -free_percpu (part->dkstats); -} - -#else /* CONFIG_SMP */ -static inline int init_disk_stats (struct gendisk *disk) -{ -return 1 ; -} - -static inline void free_disk_stats (struct gendisk *disk) -{ -} - -static inline int init_part_stats (struct hd_struct *part) -{ -return 1 ; -} - -static inline void free_part_stats (struct hd_struct *part) -{ +CPU_FREE (part->dkstats); } -#endif /* CONFIG_SMP */ /* drivers/block/ll_rw_blk.c */ extern void disk_round_stats (struct gendisk *disk);
PATCH 10/41 cpu alloc: blktrace conversion
In reply to Christoph LameterConvert blktrace percpu handling to cpu_alloc.
--- block/blktrace.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-)
block/blktrace.c +10 -14
@@ -82,7 +82,7 @@ void __trace_note_message(struct blk_trachar *buf;preempt_disable (); - buf =per_cpu_ptr (bt->msg_data,smp_processor_id ()); + buf =THIS_CPU (bt->msg_data);va_start (args, fmt); n =vscnprintf (buf,BLK_TN_MAX_MSG , fmt, args);va_end (args);
@@ -138,9 +138,7 @@ void __blk_add_trace(struct blk_trace *bstruct task_struct *tsk = current;struct blk_io_trace *t;unsigned long flags; -unsigned long *sequence;pid_t pid; -int cpu;if (unlikely (bt->trace_state != Blktrace_running))return ;
@@ -170,18 +168,16 @@ void __blk_add_trace(struct blk_trace *b t =relay_reserve (bt->rchan,sizeof (*t) + pdu_len);if (t) { - cpu =smp_processor_id (); - sequence =per_cpu_ptr (bt->sequence, cpu); - +__CPU_INC (bt->sequence); t->magic =BLK_IO_TRACE_MAGIC |BLK_IO_TRACE_VERSION ; - t->sequence = ++(*sequence); + t->sequence =__CPU_READ (bt->sequence); t->time =ktime_to_ns (ktime_get ()); t->sector = sector; t->bytes = bytes; t->action = what; t->pid = pid; t->device = bt->dev; - t->cpu = cpu; + t->cpu =smp_processor_id (); t->error = error; t->pdu_len = pdu_len;
@@ -248,8 +244,8 @@ static void blk_trace_cleanup(struct blkrelay_close (bt->rchan);debugfs_remove (bt->dropped_file);blk_remove_tree (bt->dir); -free_percpu (bt->sequence); -free_percpu (bt->msg_data); +CPU_FREE (bt->sequence); +CPU_FREE (bt->msg_data);kfree (bt); }
@@ -360,11 +356,11 @@ int do_blk_trace_setup(struct request_quif (!bt) goto err; - bt->sequence = alloc_percpu(unsigned long); + bt->sequence = CPU_ALLOC(unsigned long, GFP_KERNEL | __GFP_ZERO);if (!bt->sequence) goto err; - bt->msg_data =__alloc_percpu (BLK_TN_MAX_MSG ); + bt->msg_data =cpu_alloc (BLK_TN_MAX_MSG ,GFP_KERNEL | __GFP_ZERO,0 );if (!bt->msg_data) goto err;
@@ -413,8 +409,8 @@ err:if (bt) {if (bt->dropped_file)debugfs_remove (bt->dropped_file); -free_percpu (bt->sequence); -free_percpu (bt->msg_data); +CPU_FREE (bt->sequence); +CPU_FREE (bt->msg_data);if (bt->rchan)relay_close (bt->rchan);kfree (bt);
PATCH 11/41 cpu alloc: SRCU cpu alloc conversion
In reply to Christoph Lameter--- kernel/rcutorture.c | 4 ++-- kernel/srcu.c | 20 ++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-)
kernel/rcutorture.c +2 -2
@@ -442,8 +442,8 @@ torture_type, TORTURE_FLAG, idx);for_each_possible_cpu (cpu) { cnt +=sprintf (&page[cnt]," %d(%d,%d)" , cpu, -per_cpu_ptr (srcu_ctl.per_cpu_ref, cpu)->c[!idx], -per_cpu_ptr (srcu_ctl.per_cpu_ref, cpu)->c[idx]); +CPU_PTR (srcu_ctl.per_cpu_ref, cpu)->c[!idx], +CPU_PTR (srcu_ctl.per_cpu_ref, cpu)->c[idx]); } cnt +=sprintf (&page[cnt],"\n" );return cnt;
kernel/srcu.c +8 -12
@@ -46,7 +46,8 @@ { sp->completed =0 ;mutex_init (&sp->mutex); - sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array); + sp->per_cpu_ref = CPU_ALLOC(struct srcu_struct_array, +GFP_KERNEL |__GFP_ZERO);return (sp->per_cpu_ref ?0 : -ENOMEM ); }
@@ -62,7 +63,7 @@ sum =0 ;for_each_possible_cpu (cpu) - sum +=per_cpu_ptr (sp->per_cpu_ref, cpu)->c[idx]; + sum +=CPU_PTR (sp->per_cpu_ref, cpu)->c[idx];return sum; }
@@ -94,7 +95,7 @@WARN_ON (sum);/* Leakage unless caller handles error. */ if (sum !=0 )return ; -free_percpu (sp->per_cpu_ref); +CPU_FREE (sp->per_cpu_ref); sp->per_cpu_ref =NULL ; }
@@ -110,12 +111,9 @@ {int idx; -preempt_disable (); idx = sp->completed &0x1 ; -barrier ();/* ensure compiler looks -once- at sp->completed. */ -per_cpu_ptr (sp->per_cpu_ref,smp_processor_id ())->c[idx]++; -srcu_barrier ();/* ensure compiler won't misorder critical section. */ -preempt_enable (); +srcu_barrier (); +_CPU_INC (sp->per_cpu_ref->c[idx]);return idx; }
@@ -131,10 +129,8 @@*/ void srcu_read_unlock (struct srcu_struct *sp,int idx) { -preempt_disable (); -srcu_barrier ();/* ensure compiler won't misorder critical section. */ -per_cpu_ptr (sp->per_cpu_ref,smp_processor_id ())->c[idx]--; -preempt_enable (); +srcu_barrier (); +_CPU_DEC (sp->per_cpu_ref->c[idx]); }/**
PATCH 12/41 cpu alloc: XFS counter conversion.
In reply to Christoph LameterAlso remove the useless zeroing after allocation. Even allocpercpu() already zeroed the objects on alloc.
--- fs/xfs/xfs_mount.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-)
fs/xfs/xfs_mount.c +8 -16
@@ -2016,7 +2016,7 @@ mp = (xfs_mount_t *)container_of (nfb, xfs_mount_t, m_icsb_notifier); cntp = (xfs_icsb_cnts_t *) -per_cpu_ptr (mp->m_sb_cnts, (unsigned long )hcpu); +CPU_PTR (mp->m_sb_cnts, (unsigned long )hcpu);switch (action) {case CPU_UP_PREPARE :case CPU_UP_PREPARE_FROZEN :
@@ -2065,10 +2065,7 @@xfs_icsb_init_counters ( xfs_mount_t *mp) { -xfs_icsb_cnts_t *cntp; -int i; - - mp->m_sb_cnts =alloc_percpu (xfs_icsb_cnts_t); + mp->m_sb_cnts =CPU_ALLOC (xfs_icsb_cnts_t,GFP_KERNEL | __GFP_ZERO);if (mp->m_sb_cnts ==NULL )return -ENOMEM ;
@@ -2078,11 +2075,6 @@register_hotcpu_notifier (&mp->m_icsb_notifier);#endif /* CONFIG_HOTPLUG_CPU */ -for_each_online_cpu (i) { - cntp = (xfs_icsb_cnts_t *)per_cpu_ptr (mp->m_sb_cnts, i); -memset (cntp,0 ,sizeof (xfs_icsb_cnts_t)); - } -mutex_init (&mp->m_icsb_mutex);/*
@@ -2115,7 +2107,7 @@ {if (mp->m_sb_cnts) {unregister_hotcpu_notifier (&mp->m_icsb_notifier); -free_percpu (mp->m_sb_cnts); +CPU_FREE (mp->m_sb_cnts); }mutex_destroy (&mp->m_icsb_mutex); }
@@ -2145,7 +2137,7 @@int i;for_each_online_cpu (i) { - cntp = (xfs_icsb_cnts_t *)per_cpu_ptr (mp->m_sb_cnts, i); + cntp = (xfs_icsb_cnts_t *)CPU_PTR (mp->m_sb_cnts, i);xfs_icsb_lock_cntr (cntp); } }
@@ -2158,7 +2150,7 @@int i;for_each_online_cpu (i) { - cntp = (xfs_icsb_cnts_t *)per_cpu_ptr (mp->m_sb_cnts, i); + cntp = (xfs_icsb_cnts_t *)CPU_PTR (mp->m_sb_cnts, i);xfs_icsb_unlock_cntr (cntp); } }
@@ -2178,7 +2170,7 @@xfs_icsb_lock_all_counters (mp);for_each_online_cpu (i) { - cntp = (xfs_icsb_cnts_t *)per_cpu_ptr (mp->m_sb_cnts, i); + cntp = (xfs_icsb_cnts_t *)CPU_PTR (mp->m_sb_cnts, i); cnt->icsb_icount += cntp->icsb_icount; cnt->icsb_ifree += cntp->icsb_ifree; cnt->icsb_fdblocks += cntp->icsb_fdblocks;
@@ -2254,7 +2246,7 @@xfs_icsb_lock_all_counters (mp);for_each_online_cpu (i) { - cntp =per_cpu_ptr (mp->m_sb_cnts, i); + cntp =CPU_PTR (mp->m_sb_cnts, i);switch (field) {case XFS_SBS_ICOUNT : cntp->icsb_icount = count + resid;
@@ -2391,7 +2383,7 @@might_sleep (); again: cpu =get_cpu (); - icsbp = (xfs_icsb_cnts_t *)per_cpu_ptr (mp->m_sb_cnts, cpu); + icsbp = (xfs_icsb_cnts_t *)CPU_PTR (mp->m_sb_cnts, cpu);/* * if the counter is disabled, go to slow path
PATCH 13/41 cpu alloc: NFS statistics
In reply to Christoph LameterConvert NFS statistics to cpu alloc.
--- fs/nfs/iostat.h | 27 +++++++++++---------------- fs/nfs/super.c | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-)
fs/nfs/iostat.h +11 -16
@@ -119,13 +119,7 @@ struct nfs_iostats {static inline void nfs_inc_server_stats (struct nfs_server *server,enum nfs_stat_eventcounters stat) { -struct nfs_iostats *iostats; -int cpu; - - cpu =get_cpu (); - iostats =per_cpu_ptr (server->io_stats, cpu); - iostats->events[stat] ++; -put_cpu_no_resched (); +_CPU_INC (server->io_stats->events[stat]); }static inline void nfs_inc_stats (struct inode *inode,enum nfs_stat_eventcounters stat)
@@ -135,13 +129,14 @@ static inline void nfs_inc_stats(structstatic inline void nfs_add_server_stats (struct nfs_server *server,enum nfs_stat_bytecounters stat,unsigned long addend) { -struct nfs_iostats *iostats; -int cpu; - - cpu =get_cpu (); - iostats =per_cpu_ptr (server->io_stats, cpu); - iostats->bytes[stat] += addend; -put_cpu_no_resched (); +#ifdef CONFIG_64BIT +_CPU_ADD (server->io_stats->bytes[stat], addend); +#else +/* 32bit cannot perform atomic 64 bit inc sodisable preemption */ +preempt_disable (); +THIS_CPU (server->io_stats)->bytes[stat] += addend; +preempt_enable_no_resched (); +#endif }static inline void nfs_add_stats (struct inode *inode,enum nfs_stat_bytecounters stat,unsigned long addend)
@@ -151,13 +146,13 @@ static inline void nfs_add_stats(structstatic inline struct nfs_iostats *nfs_alloc_iostats (void ) { - return alloc_percpu(struct nfs_iostats); + return CPU_ALLOC(struct nfs_iostats, GFP_KERNEL | __GFP_ZERO); }static inline void nfs_free_iostats (struct nfs_iostats *stats) {if (stats !=NULL ) -free_percpu (stats); +CPU_FREE (stats); }#endif
fs/nfs/super.c +1 -1
@@ -620,7 +620,7 @@ static int nfs_show_stats(struct seq_filstruct nfs_iostats *stats;preempt_disable (); - stats =per_cpu_ptr (nfss->io_stats, cpu); + stats =CPU_PTR (nfss->io_stats, cpu);for (i =0 ; i < __NFSIOS_COUNTSMAX; i++) totals.events[i] += stats->events[i];
PATCH 14/41 cpu alloc: Neigbour statistics
In reply to Christoph LameterConvert neighbor stats to cpu alloc.
--- include/net/neighbour.h | 6 +----- net/core/neighbour.c | 11 ++++++----- 2 files changed, 7 insertions(+), 10 deletions(-)
include/net/neighbour.h +1 -6
@@ -87,12 +87,7 @@unsigned long forced_gc_runs;/* number of forced GC runs */ }; -#define NEIGH_CACHE_STAT_INC (tbl, field) \ - do { \ - preempt_disable(); \ - (per_cpu_ptr((tbl)->stats, smp_processor_id())->field)++; \ - preempt_enable(); \ - } while (0) +#define NEIGH_CACHE_STAT_INC (tbl, field) _CPU_INC((tbl)->stats->field)struct neighbour {
net/core/neighbour.c +6 -5
@@ -1425,7 +1425,8 @@kmem_cache_create (tbl->id, tbl->entry_size,0 ,SLAB_HWCACHE_ALIGN |SLAB_PANIC ,NULL ); - tbl->stats = alloc_percpu(struct neigh_statistics); + tbl->stats = CPU_ALLOC(struct neigh_statistics, +GFP_KERNEL | __GFP_ZERO);if (!tbl->stats)panic ("cannot create neighbour cache statistics" );
@@ -1511,7 +1512,7 @@remove_proc_entry (tbl->id, init_net.proc_net_stat); -free_percpu (tbl->stats); +CPU_FREE (tbl->stats); tbl->stats =NULL ;kmem_cache_destroy (tbl->kmem_cachep);
@@ -1769,7 +1770,7 @@for_each_possible_cpu (cpu) {struct neigh_statistics *st; - st =per_cpu_ptr (tbl->stats, cpu); + st =CPU_PTR (tbl->stats, cpu); ndst.ndts_allocs += st->allocs; ndst.ndts_destroys += st->destroys; ndst.ndts_hash_grows += st->hash_grows;
@@ -2429,7 +2430,7 @@if (!cpu_possible (cpu))continue ; *pos = cpu+1 ; -return per_cpu_ptr (tbl->stats, cpu); +return CPU_PTR (tbl->stats, cpu); }return NULL ; }
@@ -2444,7 +2445,7 @@if (!cpu_possible (cpu))continue ; *pos = cpu+1 ; -return per_cpu_ptr (tbl->stats, cpu); +return CPU_PTR (tbl->stats, cpu); }return NULL ; }
PATCH 15/41 cpu_alloc: Convert ip route statistics
In reply to Christoph LameterConvert IP route stats to cpu alloc.
net/ipv4/ip_input.c +1 -1
@@ -345,7 +345,7 @@#ifdef CONFIG_NET_CLS_ROUTE if (unlikely (skb->dst->tclassid)) { -struct ip_rt_acct *st =per_cpu_ptr (ip_rt_acct,smp_processor_id ()); +struct ip_rt_acct *st =THIS_CPU (ip_rt_acct);u32 idx = skb->dst->tclassid; st[idx&0xFF ].o_packets++; st[idx&0xFF ].o_bytes+=skb->len;
net/ipv4/route.c +3 -2
@@ -534,7 +534,7 @@unsigned int j;u32 *src; - src = ((u32 *)per_cpu_ptr (ip_rt_acct, i)) + offset; + src = ((u32 *)CPU_PTR (ip_rt_acct, i)) + offset;for (j =0 ; j < length/4 ; j++) dst[j] += src[j]; }
@@ -3035,7 +3035,8 @@ (jiffies ^ (jiffies >> 7))));#ifdef CONFIG_NET_CLS_ROUTE - ip_rt_acct =__alloc_percpu (256 *sizeof (struct ip_rt_acct )); + ip_rt_acct =cpu_alloc (256 *sizeof (struct ip_rt_acct ), +GFP_KERNEL |__GFP_ZERO, __alignof__(struct ip_rt_acct ));if (!ip_rt_acct)panic ("IP: failed to allocate ip_rt_acct\n" );#endif
PATCH 16/41 cpu alloc: Tcp statistics conversion
In reply to Christoph LameterConvert tcp statistics to cpu alloc.
--- net/ipv4/tcp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/ipv4/tcp.c +5 -5
@@ -2456,7 +2456,7 @@ {int cpu;for_each_possible_cpu (cpu) { -struct tcp_md5sig_pool *p = *per_cpu_ptr (pool, cpu); +struct tcp_md5sig_pool *p = *CPU_PTR (pool, cpu);if (p) {if (p->md5_desc.tfm)crypto_free_hash (p->md5_desc.tfm);
@@ -2464,7 +2464,7 @@ p =NULL ; } } -free_percpu (pool); +CPU_FREE (pool); }void tcp_free_md5sig_pool (void )
@@ -2488,7 +2488,7 @@int cpu;struct tcp_md5sig_pool **pool; - pool = alloc_percpu(struct tcp_md5sig_pool *); + pool = CPU_ALLOC(struct tcp_md5sig_pool *, GFP_KERNEL);if (!pool)return NULL ;
@@ -2499,7 +2499,7 @@ p =kzalloc (sizeof (*p),GFP_KERNEL );if (!p) goto out_free; - *per_cpu_ptr (pool, cpu) = p; + *CPU_PTR (pool, cpu) = p; hash =crypto_alloc_hash ("md5" ,0 ,CRYPTO_ALG_ASYNC );if (!hash ||IS_ERR (hash))
@@ -2564,7 +2564,7 @@if (p) tcp_md5sig_users++;spin_unlock_bh (&tcp_md5sig_pool_lock); -return (p ? *per_cpu_ptr (p, cpu) :NULL ); +return (p ? *CPU_PTR (p, cpu) :NULL ); }EXPORT_SYMBOL (__tcp_get_md5sig_pool);
PATCH 17/41 cpu alloc: Convert scratches to cpu alloc
In reply to Christoph LameterConvert scratch handling in the network stack to cpu alloc.
--- net/ipv4/ipcomp.c | 26 +++++++++++++------------- net/ipv6/ipcomp6.c | 26 +++++++++++++------------- 2 files changed, 26 insertions(+), 26 deletions(-)
net/ipv4/ipcomp.c +13 -13
@@ -47,8 +47,8 @@int dlen =IPCOMP_SCRATCH_SIZE ;const u8 *start = skb->data;const int cpu =get_cpu (); -u8 *scratch = *per_cpu_ptr (ipcomp_scratches, cpu); -struct crypto_comp *tfm = *per_cpu_ptr (ipcd->tfms, cpu); +u8 *scratch = *CPU_PTR (ipcomp_scratches, cpu); +struct crypto_comp *tfm = *CPU_PTR (ipcd->tfms, cpu);int err =crypto_comp_decompress (tfm, start, plen, scratch, &dlen);if (err)
@@ -105,8 +105,8 @@int dlen =IPCOMP_SCRATCH_SIZE ;u8 *start = skb->data;const int cpu =get_cpu (); -u8 *scratch = *per_cpu_ptr (ipcomp_scratches, cpu); -struct crypto_comp *tfm = *per_cpu_ptr (ipcd->tfms, cpu); +u8 *scratch = *CPU_PTR (ipcomp_scratches, cpu); +struct crypto_comp *tfm = *CPU_PTR (ipcd->tfms, cpu);int err;local_bh_disable ();
@@ -254,9 +254,9 @@return ;for_each_possible_cpu (i) -vfree (*per_cpu_ptr (scratches, i)); +vfree (*CPU_PTR (scratches, i)); -free_percpu (scratches); +CPU_FREE (scratches); }static void **ipcomp_alloc_scratches (void )
@@ -267,7 +267,7 @@if (ipcomp_scratch_users++)return ipcomp_scratches; - scratches = alloc_percpu(void *); + scratches = CPU_ALLOC(void *, GFP_KERNEL);if (!scratches)return NULL ;
@@ -277,7 +277,7 @@void *scratch =vmalloc (IPCOMP_SCRATCH_SIZE );if (!scratch)return NULL ; - *per_cpu_ptr (scratches, i) = scratch; + *CPU_PTR (scratches, i) = scratch; }return scratches;
@@ -305,10 +305,10 @@return ;for_each_possible_cpu (cpu) { -struct crypto_comp *tfm = *per_cpu_ptr (tfms, cpu); +struct crypto_comp *tfm = *CPU_PTR (tfms, cpu);crypto_free_comp (tfm); } -free_percpu (tfms); +CPU_FREE (tfms); }static struct crypto_comp **ipcomp_alloc_tfms (const char *alg_name)
@@ -324,7 +324,7 @@struct crypto_comp *tfm; tfms = pos->tfms; - tfm = *per_cpu_ptr (tfms, cpu); + tfm = *CPU_PTR (tfms, cpu);if (!strcmp (crypto_comp_name (tfm), alg_name)) { pos->users++;
@@ -340,7 +340,7 @@INIT_LIST_HEAD (&pos->list);list_add (&pos->list, &ipcomp_tfms_list); - pos->tfms = tfms = alloc_percpu(struct crypto_comp *); + pos->tfms = tfms = CPU_ALLOC(struct crypto_comp *, GFP_KERNEL);if (!tfms) goto error;
@@ -349,7 +349,7 @@ CRYPTO_ALG_ASYNC);if (IS_ERR (tfm)) goto error; - *per_cpu_ptr (tfms, cpu) = tfm; + *CPU_PTR (tfms, cpu) = tfm; }return tfms;
net/ipv6/ipcomp6.c +13 -13
@@ -90,8 +90,8 @@ start = skb->data; cpu =get_cpu (); - scratch = *per_cpu_ptr (ipcomp6_scratches, cpu); - tfm = *per_cpu_ptr (ipcd->tfms, cpu); + scratch = *CPU_PTR (ipcomp6_scratches, cpu); + tfm = *CPU_PTR (ipcd->tfms, cpu); err =crypto_comp_decompress (tfm, start, plen, scratch, &dlen);if (err)
@@ -142,8 +142,8 @@ start = skb->data; cpu =get_cpu (); - scratch = *per_cpu_ptr (ipcomp6_scratches, cpu); - tfm = *per_cpu_ptr (ipcd->tfms, cpu); + scratch = *CPU_PTR (ipcomp6_scratches, cpu); + tfm = *CPU_PTR (ipcd->tfms, cpu);local_bh_disable (); err =crypto_comp_compress (tfm, start, plen, scratch, &dlen);
@@ -264,12 +264,12 @@return ;for_each_possible_cpu (i) { -void *scratch = *per_cpu_ptr (scratches, i); +void *scratch = *CPU_PTR (scratches, i);vfree (scratch); } -free_percpu (scratches); +CPU_FREE (scratches); }static void **ipcomp6_alloc_scratches (void )
@@ -280,7 +280,7 @@if (ipcomp6_scratch_users++)return ipcomp6_scratches; - scratches = alloc_percpu(void *); + scratches = CPU_ALLOC(void *, GFP_KERNEL);if (!scratches)return NULL ;
@@ -290,7 +290,7 @@void *scratch =vmalloc (IPCOMP_SCRATCH_SIZE );if (!scratch)return NULL ; - *per_cpu_ptr (scratches, i) = scratch; + *CPU_PTR (scratches, i) = scratch; }return scratches;
@@ -318,10 +318,10 @@return ;for_each_possible_cpu (cpu) { -struct crypto_comp *tfm = *per_cpu_ptr (tfms, cpu); +struct crypto_comp *tfm = *CPU_PTR (tfms, cpu);crypto_free_comp (tfm); } -free_percpu (tfms); +CPU_FREE (tfms); }static struct crypto_comp **ipcomp6_alloc_tfms (const char *alg_name)
@@ -337,7 +337,7 @@struct crypto_comp *tfm; tfms = pos->tfms; - tfm = *per_cpu_ptr (tfms, cpu); + tfm = *CPU_PTR (tfms, cpu);if (!strcmp (crypto_comp_name (tfm), alg_name)) { pos->users++;
@@ -353,7 +353,7 @@INIT_LIST_HEAD (&pos->list);list_add (&pos->list, &ipcomp6_tfms_list); - pos->tfms = tfms = alloc_percpu(struct crypto_comp *); + pos->tfms = tfms = CPU_ALLOC(struct crypto_comp *, GFP_KERNEL);if (!tfms) goto error;
@@ -362,7 +362,7 @@ CRYPTO_ALG_ASYNC);if (IS_ERR (tfm)) goto error; - *per_cpu_ptr (tfms, cpu) = tfm; + *CPU_PTR (tfms, cpu) = tfm; }return tfms;
PATCH 18/41 cpu alloc: Dmaengine conversion
In reply to Christoph LameterConvert DMA engine to use CPU_xx operations. This also removes the use of local_t from the dmaengine.
--- drivers/dma/dmaengine.c | 38 ++++++++++++++------------------------ include/linux/dmaengine.h | 16 ++++++---------- 2 files changed, 20 insertions(+), 34 deletions(-)
drivers/dma/dmaengine.c +14 -24
@@ -84,7 +84,7 @@int i;for_each_possible_cpu (i) - count +=per_cpu_ptr (chan->local, i)->memcpy_count; + count +=CPU_PTR (chan->local, i)->memcpy_count;return sprintf (buf,"%lu\n" , count); }
@@ -97,7 +97,7 @@int i;for_each_possible_cpu (i) - count +=per_cpu_ptr (chan->local, i)->bytes_transferred; + count +=CPU_PTR (chan->local, i)->bytes_transferred;return sprintf (buf,"%lu\n" , count); }
@@ -111,10 +111,8 @@ atomic_read(&chan->refcount.refcount) > 1) in_use = 1; else { -if (local_read (&(per_cpu_ptr (chan->local, -get_cpu ())->refcount)) >0 ) +if (_CPU_READ (chan->local->refcount) >0 ) in_use =1 ; -put_cpu (); }return sprintf (buf,"%d\n" , in_use);
@@ -227,7 +225,7 @@int bias =0x7FFFFFFF ;int i;for_each_possible_cpu (i) - bias -=local_read (&per_cpu_ptr (chan->local, i)->refcount); + bias -=_CPU_READ (chan->local->refcount);atomic_sub (bias, &chan->refcount.refcount);kref_put (&chan->refcount, dma_chan_cleanup); }
@@ -372,7 +370,8 @@/* represent channels in sysfs. Probably want devs too */ list_for_each_entry (chan, &device->channels, device_node) { - chan->local =alloc_percpu (typeof (*chan->local)); + chan->local =CPU_ALLOC (typeof (*chan->local), +GFP_KERNEL | __GFP_ZERO);if (chan->local ==NULL )continue ;
@@ -385,7 +384,7 @@ rc =device_register (&chan->dev);if (rc) { chancnt--; -free_percpu (chan->local); +CPU_FREE (chan->local); chan->local =NULL ; goto err_out; }
@@ -413,7 +412,7 @@kref_put (&device->refcount, dma_async_device_cleanup);device_unregister (&chan->dev); chancnt--; -free_percpu (chan->local); +CPU_FREE (chan->local); }return rc; }
@@ -490,11 +489,8 @@ tx->callback =NULL ; cookie = tx->tx_submit (tx); - cpu =get_cpu (); -per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; -per_cpu_ptr (chan->local, cpu)->memcpy_count++; -put_cpu (); - +__CPU_ADD (chan->local->bytes_transferred, len); +__CPU_INC (chan->local->memcpy_count);return cookie; }EXPORT_SYMBOL (dma_async_memcpy_buf_to_buf);
@@ -536,11 +532,8 @@ tx->callback =NULL ; cookie = tx->tx_submit (tx); - cpu =get_cpu (); -per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; -per_cpu_ptr (chan->local, cpu)->memcpy_count++; -put_cpu (); - +_CPU_ADD (chan->local->bytes_transferred, len); +_CPU_INC (chan->local->memcpy_count);return cookie; }EXPORT_SYMBOL (dma_async_memcpy_buf_to_pg);
@@ -585,11 +578,8 @@ tx->callback =NULL ; cookie = tx->tx_submit (tx); - cpu =get_cpu (); -per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; -per_cpu_ptr (chan->local, cpu)->memcpy_count++; -put_cpu (); - +_CPU_ADD (chan->local->bytes_transferred, len); +_CPU_INC (chan->local->memcpy_count);return cookie; }EXPORT_SYMBOL (dma_async_memcpy_pg_to_pg);
include/linux/dmaengine.h +6 -10
@@ -116,13 +116,13 @@/** * struct dma_chan_percpu - the per-CPU part of struct dma_chan -* @refcount: local_t used for open-coded "bigref" counting +* @refcount: int used for open-coded "bigref" counting * @memcpy_count: transaction counter * @bytes_transferred: byte counter */ struct dma_chan_percpu { -local_t refcount; +int refcount;/* stats */ unsigned long memcpy_count;unsigned long bytes_transferred;
@@ -164,20 +164,16 @@ {if (unlikely (chan->slow_ref))kref_get (&chan->refcount); -else { -local_inc (&(per_cpu_ptr (chan->local,get_cpu ())->refcount)); -put_cpu (); - } +else +_CPU_INC (chan->local->refcount); }static inline void dma_chan_put (struct dma_chan *chan) {if (unlikely (chan->slow_ref))kref_put (&chan->refcount, dma_chan_cleanup); -else { -local_dec (&(per_cpu_ptr (chan->local,get_cpu ())->refcount)); -put_cpu (); - } +else +_CPU_DEC (chan->local->refcount); }/*
PATCH 19/41 cpu alloc: Convert loopback statistics
In reply to Christoph LameterConvert loopback statistics to cpu alloc.
--- drivers/net/loopback.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
drivers/net/loopback.c +7 -8
@@ -132,7 +132,7 @@*/ static int loopback_xmit (struct sk_buff *skb,struct net_device *dev) { -struct pcpu_lstats *pcpu_lstats, *lb_stats; +struct pcpu_lstats *pcpu_lstats;skb_orphan (skb);
@@ -152,11 +152,10 @@#endif dev->last_rx = jiffies; -/* it's OK to use per_cpu_ptr() because BHs are off */ +/* it's OK to use __xxx cpu operations because BHs are off */ pcpu_lstats =netdev_priv (dev); - lb_stats =per_cpu_ptr (pcpu_lstats,smp_processor_id ()); - lb_stats->bytes += skb->len; - lb_stats->packets++; +__CPU_ADD (pcpu_lstats->bytes, skb->len); +__CPU_INC (pcpu_lstats->packets);netif_rx (skb);
@@ -175,7 +174,7 @@for_each_possible_cpu (i) {const struct pcpu_lstats *lb_stats; - lb_stats =per_cpu_ptr (pcpu_lstats, i); + lb_stats =CPU_PTR (pcpu_lstats, i); bytes += lb_stats->bytes; packets += lb_stats->packets; }
@@ -203,7 +202,7 @@ {struct pcpu_lstats *lstats; - lstats = alloc_percpu(struct pcpu_lstats); + lstats = CPU_ALLOC(struct pcpu_lstats, GFP_KERNEL | __GFP_ZERO);if (!lstats)return -ENOMEM ;
@@ -215,7 +214,7 @@ {struct pcpu_lstats *lstats =netdev_priv (dev); -free_percpu (lstats); +CPU_FREE (lstats);free_netdev (dev); }
PATCH 20/41 cpu alloc: Veth conversion
In reply to Christoph Lameterveth statistics conversion.
--- drivers/net/veth.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/veth.c +9 -14
@@ -152,8 +152,7 @@ {struct net_device *rcv =NULL ;struct veth_priv *priv, *rcv_priv; -struct veth_net_stats *stats; -int length, cpu; +int length;skb_orphan (skb);
@@ -161,9 +160,6 @@ rcv = priv->peer; rcv_priv =netdev_priv (rcv); - cpu =smp_processor_id (); - stats =per_cpu_ptr (priv->stats, cpu); -if (!(rcv->flags &IFF_UP )) goto outf;
@@ -180,19 +176,18 @@ length = skb->len; - stats->tx_bytes += length; - stats->tx_packets++; +__CPU_ADD (priv->stats->tx_bytes, length); +__CPU_INC (priv->stats->tx_packets); - stats =per_cpu_ptr (rcv_priv->stats, cpu); - stats->rx_bytes += length; - stats->rx_packets++; +__CPU_ADD (rcv_priv->stats->rx_bytes, length); +__CPU_INC (rcv_priv->stats->rx_packets);netif_rx (skb);return 0 ; outf:kfree_skb (skb); - stats->tx_dropped++; +__CPU_INC (priv->stats->tx_dropped);return 0 ; }
@@ -217,7 +212,7 @@ dev_stats->tx_dropped =0 ;for_each_online_cpu (cpu) { - stats =per_cpu_ptr (priv->stats, cpu); + stats =CPU_PTR (priv->stats, cpu); dev_stats->rx_packets += stats->rx_packets; dev_stats->tx_packets += stats->tx_packets;
@@ -249,7 +244,7 @@struct veth_net_stats *stats;struct veth_priv *priv; - stats = alloc_percpu(struct veth_net_stats); + stats = CPU_ALLOC(struct veth_net_stats, GFP_KERNEL | __GFP_ZER);if (stats ==NULL )return -ENOMEM ;
@@ -263,7 +258,7 @@struct veth_priv *priv; priv =netdev_priv (dev); -free_percpu (priv->stats); +CPU_FREE (priv->stats);free_netdev (dev); }
PATCH 21/41 cpu alloc: Chelsio statistics conversion
In reply to Christoph LameterConvert chelsio statistics to cpu alloc.
--- drivers/net/chelsio/sge.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/net/chelsio/sge.c +8 -11
@@ -809,7 +809,7 @@int i;for_each_port (sge->adapter, i) -free_percpu (sge->port_stats[i]); +CPU_FREE (sge->port_stats[i]);kfree (sge->tx_sched);free_tx_resources (sge);
@@ -988,7 +988,7 @@memset (ss,0 ,sizeof (*ss));for_each_possible_cpu (cpu) { -struct sge_port_stats *st =per_cpu_ptr (sge->port_stats[port], cpu); +struct sge_port_stats *st =CPU_PTR (sge->port_stats[port], cpu); ss->rx_cso_good += st->rx_cso_good; ss->tx_cso += st->tx_cso;
@@ -1367,7 +1367,6 @@struct sk_buff *skb;const struct cpl_rx_pkt *p;struct adapter *adapter = sge->adapter; -struct sge_port_stats *st; skb =get_packet (adapter->pdev, fl, len - sge->rx_pkt_pad);if (unlikely (!skb)) {
@@ -1382,20 +1381,18 @@ }__skb_pull (skb,sizeof (*p)); - st =per_cpu_ptr (sge->port_stats[p->iff],smp_processor_id ()); - skb->protocol =eth_type_trans (skb, adapter->port[p->iff].dev); skb->dev->last_rx = jiffies;if ((adapter->flags &RX_CSUM_ENABLED ) && p->csum ==0xffff && skb->protocol ==htons (ETH_P_IP ) && (skb->data[9 ] ==IPPROTO_TCP || skb->data[9 ] ==IPPROTO_UDP )) { - ++st->rx_cso_good; +__CPU_INC (sge->port_stats[p->iff]->rx_cso_good); skb->ip_summed =CHECKSUM_UNNECESSARY ; }else skb->ip_summed =CHECKSUM_NONE ;if (unlikely (adapter->vlan_grp && p->vlan_valid)) { - st->vlan_xtract++; +__CPU_INC (sge->port_stats[p->iff]->vlan_xtract);#ifdef CONFIG_CHELSIO_T1_NAPI vlan_hwaccel_receive_skb (skb, adapter->vlan_grp,ntohs (p->vlan));
@@ -1848,8 +1845,7 @@ {struct adapter *adapter = dev->priv;struct sge *sge = adapter->sge; -struct sge_port_stats *st =per_cpu_ptr (sge->port_stats[dev->if_port], -smp_processor_id ()); +struct sge_port_stats *st =THIS_CPU (sge->port_stats[dev->if_port]);struct cpl_tx_pkt *cpl;struct sk_buff *orig_skb = skb;int ret;
@@ -2159,7 +2155,8 @@ sge->jumbo_fl =t1_is_T1B (adapter) ?1 :0 ;for_each_port (adapter, i) { - sge->port_stats[i] = alloc_percpu(struct sge_port_stats); + sge->port_stats[i] = CPU_ALLOC(struct sge_port_stats, +GFP_KERNEL | __GFP_ZERO);if (!sge->port_stats[i]) goto nomem_port; }
@@ -2203,7 +2200,7 @@return sge; nomem_port:while (i >=0 ) { -free_percpu (sge->port_stats[i]); +CPU_FREE (sge->port_stats[i]); --i; }kfree (sge);
PATCH 22/41 cpu alloc: Convert network sockets inuse counter
In reply to Christoph LameterConvert handling of the inuse counters to cpu alloc.
--- net/core/sock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/sock.c +4 -5
@@ -1943,8 +1943,7 @@#ifdef CONFIG_NET_NS void sock_prot_inuse_add (struct net *net,struct proto *prot,int val) { -int cpu =smp_processor_id (); -per_cpu_ptr (net->core.inuse, cpu)->val[prot->inuse_idx] += val; +__CPU_ADD (net->core.inuse->val[prot->inuse_idx], val); }EXPORT_SYMBOL_GPL (sock_prot_inuse_add);
@@ -1954,7 +1953,7 @@int res =0 ;for_each_possible_cpu (cpu) - res +=per_cpu_ptr (net->core.inuse, cpu)->val[idx]; + res +=CPU_PTR (net->core.inuse, cpu)->val[idx];return res >=0 ? res :0 ; }
@@ -1962,13 +1961,13 @@static int sock_inuse_init_net (struct net *net) { - net->core.inuse = alloc_percpu(struct prot_inuse); + net->core.inuse = CPU_ALLOC(struct prot_inuse, GFP_KERNEL|__GFP_ZERO);return net->core.inuse ?0 : -ENOMEM ; }static void sock_inuse_exit_net (struct net *net) { -free_percpu (net->core.inuse); +CPU_FREE (net->core.inuse); }static struct pernet_operations net_inuse_ops = {
PATCH 23/41 cpu alloc: Use it for infiniband
In reply to Christoph LameterUse cpu alloc for infiniband.
--- drivers/infiniband/hw/ehca/ehca_irq.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
drivers/infiniband/hw/ehca/ehca_irq.c +11 -11
@@ -680,7 +680,7 @@ cpu_id =find_next_online_cpu (pool);BUG_ON (!cpu_online (cpu_id)); - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu_id); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu_id);BUG_ON (!cct);spin_lock_irqsave (&cct->task_lock, flags);
@@ -688,7 +688,7 @@spin_unlock_irqrestore (&cct->task_lock, flags);if (cq_jobs >0 ) { cpu_id =find_next_online_cpu (pool); - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu_id); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu_id);BUG_ON (!cct); }
@@ -761,7 +761,7 @@ {struct ehca_cpu_comp_task *cct; - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu);spin_lock_init (&cct->task_lock);INIT_LIST_HEAD (&cct->cq_list);init_waitqueue_head (&cct->wait_queue);
@@ -777,7 +777,7 @@struct task_struct *task;unsigned long flags_cct; - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu);spin_lock_irqsave (&cct->task_lock, flags_cct);
@@ -793,7 +793,7 @@static void __cpuinit take_over_work (struct ehca_comp_pool *pool,int cpu) { -struct ehca_cpu_comp_task *cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu); +struct ehca_cpu_comp_task *cct =CPU_PTR (pool->cpu_comp_tasks, cpu);LIST_HEAD (list);struct ehca_cq *cq;unsigned long flags_cct;
@@ -806,8 +806,7 @@ cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);list_del (&cq->entry); -__queue_comp_task (cq,per_cpu_ptr (pool->cpu_comp_tasks, -smp_processor_id ())); +__queue_comp_task (cq,THIS_CPU (pool->cpu_comp_tasks)); }spin_unlock_irqrestore (&cct->task_lock, flags_cct);
@@ -833,14 +832,14 @@case CPU_UP_CANCELED :case CPU_UP_CANCELED_FROZEN :ehca_gen_dbg ("CPU: %x (CPU_CANCELED)" , cpu); - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu);kthread_bind (cct->task,any_online_cpu (cpu_online_map));destroy_comp_task (pool, cpu);break ;case CPU_ONLINE :case CPU_ONLINE_FROZEN :ehca_gen_dbg ("CPU: %x (CPU_ONLINE)" , cpu); - cct =per_cpu_ptr (pool->cpu_comp_tasks, cpu); + cct =CPU_PTR (pool->cpu_comp_tasks, cpu);kthread_bind (cct->task, cpu);wake_up_process (cct->task);break ;
@@ -883,7 +882,8 @@spin_lock_init (&pool->last_cpu_lock); pool->last_cpu =any_online_cpu (cpu_online_map); - pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task); + pool->cpu_comp_tasks = CPU_ALLOC(struct ehca_cpu_comp_task, +GFP_KERNEL | __GFP_ZERO);if (pool->cpu_comp_tasks ==NULL ) {kfree (pool);return -EINVAL ;
@@ -917,6 +917,6 @@if (cpu_online (i))destroy_comp_task (pool, i); } -free_percpu (pool->cpu_comp_tasks); +CPU_FREE (pool->cpu_comp_tasks);kfree (pool); }
PATCH 24/41 cpu alloc: Use in the crypto subsystem.
In reply to Christoph LameterUse cpu alloc for the crypto subsystem.
--- crypto/async_tx/async_tx.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
crypto/async_tx/async_tx.c +9 -10
@@ -221,10 +221,10 @@for_each_dma_cap_mask (cap, dma_cap_mask_all)for_each_possible_cpu (cpu) {struct dma_chan_ref *ref = -per_cpu_ptr (channel_table[cap], cpu)->ref; +CPU_PTR (channel_table[cap], cpu)->ref;if (ref) {atomic_set (&ref->count,0 ); -per_cpu_ptr (channel_table[cap], cpu)->ref = +CPU_PTR (channel_table[cap], cpu)->ref =NULL ; } }
@@ -237,7 +237,7 @@else new =get_chan_ref_by_cap (cap,-1 ); -per_cpu_ptr (channel_table[cap], cpu)->ref = new; +CPU_PTR (channel_table[cap], cpu)->ref = new; }spin_unlock_irqrestore (&async_tx_lock, flags);
@@ -341,7 +341,8 @@clear_bit (DMA_INTERRUPT , dma_cap_mask_all.bits);for_each_dma_cap_mask (cap, dma_cap_mask_all) { - channel_table[cap] = alloc_percpu(struct chan_ref_percpu); + channel_table[cap] = CPU_ALLOC(struct chan_ref_percpu, +GFP_KERNEL | __GFP_ZERO);if (!channel_table[cap]) goto err; }
@@ -357,7 +358,7 @@printk (KERN_ERR "async_tx: initialization failure\n" );while (--cap >=0 ) -free_percpu (channel_table[cap]); +CPU_FREE (channel_table[cap]);return 1 ; }
@@ -370,7 +371,7 @@for_each_dma_cap_mask (cap, dma_cap_mask_all)if (channel_table[cap]) -free_percpu (channel_table[cap]); +CPU_FREE (channel_table[cap]);dma_async_client_unregister (&async_tx_dma); }
@@ -390,10 +391,8 @@ dma_has_cap(tx_type, depend_tx->chan->device->cap_mask))return depend_tx->chan; else if (likely(channel_table_initialized)) { -struct dma_chan_ref *ref; -int cpu =get_cpu (); - ref =per_cpu_ptr (channel_table[tx_type], cpu)->ref; -put_cpu (); +struct dma_chan_ref *ref = +_CPU_READ (channel_table[tx_type]->ref);return ref ? ref->chan :NULL ; } else return NULL;
PATCH 25/41 cpu alloc: scheduler: Convert cpuusage to cpu_alloc.
In reply to Christoph LameterConvert scheduler handling of cpuusage to cpu alloc.
--- arch/x86/kernel/setup.c | 2 +- kernel/sched.c | 10 +++++----- mm/cpu_alloc.c | 25 +++++++++++++++++++------ 3 files changed, 25 insertions(+), 12 deletions(-)
kernel/sched.c +5 -5
@@ -9090,7 +9090,7 @@if (!ca)return ERR_PTR (-ENOMEM ); - ca->cpuusage =alloc_percpu (u64); + ca->cpuusage =CPU_ALLOC (u64,GFP_KERNEL |__GFP_ZERO);if (!ca->cpuusage) {kfree (ca);return ERR_PTR (-ENOMEM );
@@ -9105,7 +9105,7 @@ {struct cpuacct *ca =cgroup_ca (cgrp); -free_percpu (ca->cpuusage); +CPU_FREE (ca->cpuusage);kfree (ca); }
@@ -9117,7 +9117,7 @@int i;for_each_possible_cpu (i) { -u64 *cpuusage =percpu_ptr (ca->cpuusage, i); +u64 *cpuusage =CPU_PTR (cpuusage, i);/* * Take rq->lock to make 64-bit addition safe on 32-bit
@@ -9144,7 +9144,7 @@ }for_each_possible_cpu (i) { -u64 *cpuusage =percpu_ptr (ca->cpuusage, i); +u64 *cpuusage =CPU_PTR (ca->cpuusage, i);spin_lock_irq (&cpu_rq (i)->lock); *cpuusage =0 ;
@@ -9181,7 +9181,7 @@ ca =task_ca (tsk);if (ca) { -u64 *cpuusage =percpu_ptr (ca->cpuusage,task_cpu (tsk)); +u64 *cpuusage =CPU_PTR (ca->cpuusage,task_cpu (tsk)); *cpuusage += cputime; }
PATCH 26/41 cpu alloc: Convert mib handling to cpu alloc
In reply to Christoph LameterUse the cpu alloc functions for the mib handling functions in the net layer. The API for snmp_mib_free() is changed to add a size parameter since cpu_free() requires a size parameter.
--- include/net/ip.h | 2 +- include/net/snmp.h | 32 ++++++++------------------------ net/dccp/proto.c | 2 +- net/ipv4/af_inet.c | 31 +++++++++++++++++-------------- net/ipv6/addrconf.c | 11 ++++++----- net/ipv6/af_inet6.c | 20 +++++++++++--------- net/sctp/protocol.c | 2 +- net/xfrm/xfrm_proc.c | 4 ++-- 8 files changed, 47 insertions(+), 57 deletions(-)
include/net/ip.h +1 -1
@@ -170,7 +170,7 @@ DECLARE_SNMP_STAT(struct linux_mib, net_extern unsigned long snmp_fold_field (void *mib[],int offt);extern int snmp_mib_init (void *ptr[2 ],size_t mibsize); -extern void snmp_mib_free (void *ptr[2 ]); +extern void snmp_mib_free (void *ptr[2 ],size_t mibsize);extern void inet_get_local_port_range (int *low,int *high);
include/net/snmp.h +8 -24
@@ -138,29 +138,13 @@ struct linux_xfrm_mib {#define SNMP_STAT_BHPTR (name) (name[0])#define SNMP_STAT_USRPTR (name) (name[1]) -#define SNMP_INC_STATS_BH (mib, field) \ - (per_cpu_ptr(mib[0], raw_smp_processor_id())->mibs[field]++) -#define SNMP_INC_STATS_USER (mib, field) \ - do { \ - per_cpu_ptr(mib[1], get_cpu())->mibs[field]++; \ - put_cpu(); \ - } while (0) -#define SNMP_INC_STATS (mib, field) \ - do { \ - per_cpu_ptr(mib[!in_softirq()], get_cpu())->mibs[field]++; \ - put_cpu(); \ - } while (0) -#define SNMP_DEC_STATS (mib, field) \ - do { \ - per_cpu_ptr(mib[!in_softirq()], get_cpu())->mibs[field]--; \ - put_cpu(); \ - } while (0) -#define SNMP_ADD_STATS_BH (mib, field, addend) \ - (per_cpu_ptr(mib[0], raw_smp_processor_id())->mibs[field] += addend) -#define SNMP_ADD_STATS_USER (mib, field, addend) \ - do { \ - per_cpu_ptr(mib[1], get_cpu())->mibs[field] += addend; \ - put_cpu(); \ - } while (0) +#define SNMP_INC_STATS_BH (mib, field) __CPU_INC(mib[0]->mibs[field]) +#define SNMP_INC_STATS_USER (mib, field) _CPU_INC(mib[1]->mibs[field]) +#define SNMP_INC_STATS (mib, field) _CPU_INC(mib[!in_softirq()]->mibs[field]) +#define SNMP_DEC_STATS (mib, field) _CPU_DEC(mib[!in_softirq()]->mibs[field]) +#define SNMP_ADD_STATS_BH (mib, field, addend) \ + __CPU_ADD(mib[0]->mibs[field], addend) +#define SNMP_ADD_STATS_USER (mib, field, addend) \ + _CPU_ADD(mib[1]->mibs[field], addend)#endif
net/ipv4/af_inet.c +17 -14
@@ -1279,8 +1279,8 @@ unsigned long snmp_fold_field(void *mib[int i;for_each_possible_cpu (i) { - res += *(((unsigned long *)per_cpu_ptr (mib[0 ], i)) + offt); - res += *(((unsigned long *)per_cpu_ptr (mib[1 ], i)) + offt); + res += *(((unsigned long *)CPU_PTR (mib[0 ], i)) + offt); + res += *(((unsigned long *)CPU_PTR (mib[1 ], i)) + offt); }return res; }
@@ -1289,26 +1289,28 @@ EXPORT_SYMBOL_GPL(snmp_fold_field);int snmp_mib_init (void *ptr[2 ],size_t mibsize) {BUG_ON (ptr ==NULL ); - ptr[0 ] =__alloc_percpu (mibsize); + ptr[0 ] =cpu_alloc (mibsize,GFP_KERNEL | __GFP_ZERO, +L1_CACHE_BYTES );if (!ptr[0 ]) goto err0; - ptr[1 ] =__alloc_percpu (mibsize); + ptr[1 ] =cpu_alloc (mibsize,GFP_KERNEL | __GFP_ZERO, +L1_CACHE_BYTES );if (!ptr[1 ]) goto err1;return 0 ; err1: -free_percpu (ptr[0 ]); +cpu_free (ptr[0 ], mibsize); ptr[0 ] =NULL ; err0:return -ENOMEM ; }EXPORT_SYMBOL_GPL (snmp_mib_init); -void snmp_mib_free (void *ptr[2 ]) +void snmp_mib_free (void *ptr[2 ],size_t mibsize) {BUG_ON (ptr ==NULL ); -free_percpu (ptr[0 ]); -free_percpu (ptr[1 ]); +cpu_free (ptr[0 ], mibsize); +cpu_free (ptr[1 ], mibsize); ptr[0 ] = ptr[1 ] =NULL ; }EXPORT_SYMBOL_GPL (snmp_mib_free);
@@ -1370,17 +1372,18 @@ static int __init init_ipv4_mibs(void)return 0 ; err_udplite_mib: -snmp_mib_free ((void **)udp_statistics); +snmp_mib_free ((void **)udp_statistics,sizeof (struct udp_mib )); err_udp_mib: -snmp_mib_free ((void **)tcp_statistics); +snmp_mib_free ((void **)tcp_statistics,sizeof (struct tcp_mib )); err_tcp_mib: -snmp_mib_free ((void **)icmpmsg_statistics); +snmp_mib_free ((void **)icmpmsg_statistics, +sizeof (struct icmpmsg_mib )); err_icmpmsg_mib: -snmp_mib_free ((void **)icmp_statistics); +snmp_mib_free ((void **)icmp_statistics,sizeof (struct icmp_mib )); err_icmp_mib: -snmp_mib_free ((void **)ip_statistics); +snmp_mib_free ((void **)ip_statistics,sizeof (struct ipstats_mib )); err_ip_mib: -snmp_mib_free ((void **)net_statistics); +snmp_mib_free ((void **)net_statistics,sizeof (struct linux_mib )); err_net_mib:return -ENOMEM ; }
net/ipv6/addrconf.c +6 -5
@@ -279,18 +279,19 @@ static int snmp6_alloc_dev(struct inet6_return 0 ; err_icmpmsg: -snmp_mib_free ((void **)idev->stats.icmpv6); +snmp_mib_free ((void **)idev->stats.icmpv6,sizeof (struct icmpv6_mib )); err_icmp: -snmp_mib_free ((void **)idev->stats.ipv6); +snmp_mib_free ((void **)idev->stats.ipv6,sizeof (struct ipstats_mib )); err_ip:return -ENOMEM ; }static void snmp6_free_dev (struct inet6_dev *idev) { -snmp_mib_free ((void **)idev->stats.icmpv6msg); -snmp_mib_free ((void **)idev->stats.icmpv6); -snmp_mib_free ((void **)idev->stats.ipv6); +snmp_mib_free ((void **)idev->stats.icmpv6msg, +sizeof (struct icmpv6msg_mib )); +snmp_mib_free ((void **)idev->stats.icmpv6,sizeof (struct icmpv6_mib )); +snmp_mib_free ((void **)idev->stats.ipv6,sizeof (struct ipstats_mib )); }/* Nobody refers to this device, we may destroy it. */
net/ipv6/af_inet6.c +11 -9
@@ -822,13 +822,14 @@ static int __init init_ipv6_mibs(void)return 0 ; err_udplite_mib: -snmp_mib_free ((void **)udp_stats_in6); +snmp_mib_free ((void **)udp_stats_in6,sizeof (struct udp_mib )); err_udp_mib: -snmp_mib_free ((void **)icmpv6msg_statistics); +snmp_mib_free ((void **)icmpv6msg_statistics, +sizeof (struct icmpv6msg_mib )); err_icmpmsg_mib: -snmp_mib_free ((void **)icmpv6_statistics); +snmp_mib_free ((void **)icmpv6_statistics,sizeof (struct icmpv6_mib )); err_icmp_mib: -snmp_mib_free ((void **)ipv6_statistics); +snmp_mib_free ((void **)ipv6_statistics,sizeof (struct ipstats_mib )); err_ip_mib:return -ENOMEM ;
@@ -836,11 +837,12 @@ err_ip_mib:static void cleanup_ipv6_mibs (void ) { -snmp_mib_free ((void **)ipv6_statistics); -snmp_mib_free ((void **)icmpv6_statistics); -snmp_mib_free ((void **)icmpv6msg_statistics); -snmp_mib_free ((void **)udp_stats_in6); -snmp_mib_free ((void **)udplite_stats_in6); +snmp_mib_free ((void **)ipv6_statistics,sizeof (struct ipstats_mib )); +snmp_mib_free ((void **)icmpv6_statistics,sizeof (struct icmpv6_mib )); +snmp_mib_free ((void **)icmpv6msg_statistics, +sizeof (struct icmpv6msg_mib )); +snmp_mib_free ((void **)udp_stats_in6,sizeof (struct udp_mib )); +snmp_mib_free ((void **)udplite_stats_in6,sizeof (struct udp_mib )); }static int inet6_net_init (struct net *net)
net/dccp/proto.c +1 -1
@@ -1016,7 +1016,7 @@ static inline int dccp_mib_init(void)static inline void dccp_mib_exit (void ) { -snmp_mib_free ((void **)dccp_statistics); +snmp_mib_free ((void **)dccp_statistics,sizeof (struct dccp_mib )); }static int thash_entries;
net/sctp/protocol.c +1 -1
@@ -981,7 +981,7 @@ static inline int init_sctp_mibs(void)static inline void cleanup_sctp_mibs (void ) { -snmp_mib_free ((void **)sctp_statistics); +snmp_mib_free ((void **)sctp_statistics,sizeof (struct sctp_mib )); }static void sctp_v4_pf_init (void )
net/xfrm/xfrm_proc.c +2 -2
@@ -51,8 +51,8 @@ fold_field(void *mib[], int offt)int i;for_each_possible_cpu (i) { - res += *(((unsigned long *)per_cpu_ptr (mib[0 ], i)) + offt); - res += *(((unsigned long *)per_cpu_ptr (mib[1 ], i)) + offt); + res += *(((unsigned long *)CPU_PTR (mib[0 ], i)) + offt); + res += *(((unsigned long *)CPU_PTR (mib[1 ], i)) + offt); }return res; }
Re: [patch 26/41] cpu alloc: Convert mib handling to cpu alloc
Quoted 5 lines · Christoph Lameter
Use the cpu alloc functions for the mib handling functions in the net layer. The API for
snmp_mib_free()is changed to add a size parameter sincecpu_free()requires a size parameter.Signed-off-by Christoph Lameter <[email protected]>
Quoted 11 lines
--- include/net/ip.h | 2 +- include/net/snmp.h | 32 ++++++++------------------------ net/dccp/proto.c | 2 +- net/ipv4/af_inet.c | 31 +++++++++++++++++-------------- net/ipv6/addrconf.c | 11 ++++++----- net/ipv6/af_inet6.c | 20 +++++++++++--------- net/sctp/protocol.c | 2 +- net/xfrm/xfrm_proc.c | 4 ++-- 8 files changed, 47 insertions(+), 57 deletions(-)
We also can avoid the use of two arrays when CONFIG_HAVE_CPU_OPS since _CPU_INC() and __CPU_INC() are both interrupt safe. This would reduce size of mibs by 50% and complexity (no need to sum)
Re: [patch 26/41] cpu alloc: Convert mib handling to cpu alloc
Quoted 3 lines · Eric Dumazet
We also can avoid the use of two arrays when
CONFIG_HAVE_CPU_OPSsince_CPU_INC()and__CPU_INC()are both interrupt safe. This would reduce size of mibs by 50% and complexity (no need to sum)
Right.
PATCH 27/41 cpu alloc: Remove the allocpercpu functionality
In reply to Christoph LameterThere is no user of allocpercpu left after all the earlier patches were applied. Remove the allocpercpu code.
--- include/linux/percpu.h | 80 ------------------------------ mm/Makefile | 1 mm/allocpercpu.c | 127 ------------------------------------------------- 3 files changed, 208 deletions(-) delete mode 100644 mm/allocpercpu.c
include/linux/percpu.h +0 -80
@@ -53,86 +53,6 @@ &__get_cpu_var(var); }))#define put_cpu_var (var) preempt_enable() -#ifdef CONFIG_SMP - -struct percpu_data { -void *ptrs[1 ]; -}; - -#define __percpu_disguise (pdata) (struct percpu_data *)~(unsigned long)(pdata) -/* -* Use this to get to a cpu's version of the per-cpu object dynamically -* allocated. Non-atomic access to the current CPU's version should -* probably be combined with get_cpu()/put_cpu(). -*/ -#define percpu_ptr (ptr, cpu) \ -({ \ - struct percpu_data *__p = __percpu_disguise(ptr); \ - (__typeof__(ptr))__p->ptrs[(cpu)]; \ -}) - -extern void *percpu_populate (void *__pdata,size_t size,gfp_t gfp,int cpu); -extern void percpu_depopulate (void *__pdata,int cpu); -extern int __percpu_populate_mask (void *__pdata,size_t size,gfp_t gfp, -cpumask_t *mask); -extern void __percpu_depopulate_mask (void *__pdata,cpumask_t *mask); -extern void *__percpu_alloc_mask (size_t size,gfp_t gfp,cpumask_t *mask); -extern void percpu_free (void *__pdata); - -#else /* CONFIG_SMP */ - -#define percpu_ptr (ptr, cpu) ({ (void)(cpu); (ptr); }) - -static inline void percpu_depopulate (void *__pdata,int cpu) -{ -} - -static inline void __percpu_depopulate_mask (void *__pdata,cpumask_t *mask) -{ -} - -static inline void *percpu_populate (void *__pdata,size_t size,gfp_t gfp, -int cpu) -{ -return percpu_ptr (__pdata, cpu); -} - -static inline int __percpu_populate_mask (void *__pdata,size_t size,gfp_t gfp, -cpumask_t *mask) -{ -return 0 ; -} - -static __always_inline void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask) -{ -return kzalloc (size, gfp); -} - -static inline void percpu_free (void *__pdata) -{ -kfree (__pdata); -} - -#endif /* CONFIG_SMP */ - -#define percpu_populate_mask (__pdata, size, gfp, mask) \ - __percpu_populate_mask((__pdata), (size), (gfp), &(mask)) -#define percpu_depopulate_mask (__pdata, mask) \ - __percpu_depopulate_mask((__pdata), &(mask)) -#define percpu_alloc_mask (size, gfp, mask) \ - __percpu_alloc_mask((size), (gfp), &(mask)) - -#define percpu_alloc (size, gfp) percpu_alloc_mask((size), (gfp), cpu_online_map) - -/* (legacy) interface for use without CPU hotplug handling */ - -#define __alloc_percpu (size) percpu_alloc_mask((size), GFP_KERNEL, \ - cpu_possible_map) -#define alloc_percpu (type) (type *)__alloc_percpu(sizeof(type)) -#define free_percpu (ptr) percpu_free((ptr)) -#define per_cpu_ptr (ptr, cpu) percpu_ptr((ptr), (cpu)) - -/* * cpu allocator definitions *
mm/Makefile +0 -1
@@ -30,7 +30,6 @@ obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o -obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o
mm/allocpercpu.c deleted +0 -141
141 lines not on this page · raw message
Andrew Morton
maintainerRe: [patch 27/41] cpu alloc: Remove the allocpercpu functionality
Quoted 2 lines · On Thu, 29 May 2008 20:56:47 -0700 Christoph Lameter <[email protected]> wrote:
There is no user of allocpercpu left after all the earlier patches were applied. Remove the allocpercpu code.
Wow.
y:/usr/src/25> grep alloc_percpu patches/*.patch
y:/usr/src/25>
we might just be able to get away with doing this.
But it might make life easier to defer the removal of the old stuff for a while. This can be worked out later.
PATCH 28/41 Module handling: Use CPU_xx ops to dynamically allocate counters
In reply to Christoph LameterUse cpu ops to deal with the per cpu data instead of a local_t. Reduces memory requirements, cache footprint and decreases cycle counts.
Avoid a loop to NR_CPUS here. Use the possible map instead.
--- include/linux/module.h | 13 +++++-------- kernel/module.c | 17 +++++++---------- 2 files changed, 12 insertions(+), 18 deletions(-)
include/linux/module.h +7 -8
@@ -219,8 +219,8 @@struct module_ref { -local_t count; -}____cacheline_aligned ; +int count; +};enum module_state {
@@ -307,7 +307,7 @@#ifdef CONFIG_MODULE_UNLOAD /* Reference counts */ -struct module_ref ref[NR_CPUS ]; +struct module_ref *ref;/* What modules depend on me? */ struct list_head modules_which_use_me;
@@ -385,8 +385,7 @@ {if (module) {BUG_ON (module_refcount (module) ==0 ); -local_inc (&module->ref[get_cpu ()].count); -put_cpu (); +_CPU_INC (module->ref->count); } }
@@ -395,12 +394,12 @@int ret =1 ;if (module) { -unsigned int cpu =get_cpu (); +preempt_disable ();if (likely (module_is_live (module))) -local_inc (&module->ref[cpu].count); +__CPU_INC (module->ref->count);else ret =0 ; -put_cpu (); +preempt_enable (); }return ret; }
kernel/module.c +9 -10
@@ -366,13 +366,11 @@/* Init the unload section of the module. */ static void module_unload_init (struct module *mod) { -unsigned int i; -INIT_LIST_HEAD (&mod->modules_which_use_me); -for (i =0 ; i <NR_CPUS ; i++) -local_set (&mod->ref[i].count,0 ); + mod->ref = CPU_ALLOC(struct module_ref, GFP_KERNEL | __GFP_ZERO); +/* Hold reference count during initialization. */ -local_set (&mod->ref[raw_smp_processor_id ()].count,1 ); +__CPU_WRITE (mod->ref->count,1 );/* Backwards compatibility macros put refcount during init. */ mod->waiter = current; }
@@ -450,6 +448,7 @@kfree (use);sysfs_remove_link (i->holders_dir, mod->name);/* There can be at most one match. */ +CPU_FREE (i->ref);break ; } }
@@ -505,8 +504,8 @@ {unsigned int i, total =0 ; -for (i =0 ; i <NR_CPUS ; i++) - total +=local_read (&mod->ref[i].count); +for_each_online_cpu (i) + total +=CPU_PTR (mod->ref, i)->count;return total; }EXPORT_SYMBOL (module_refcount);
@@ -667,12 +666,12 @@void module_put (struct module *module) {if (module) { -unsigned int cpu =get_cpu (); -local_dec (&module->ref[cpu].count); +preempt_disable (); +_CPU_DEC (module->ref->count);/* Maybe they're waiting for us to drop reference? */ if (unlikely (!module_is_live (module)))wake_up_process (module->waiter); -put_cpu (); +preempt_enable (); } }EXPORT_SYMBOL (module_put);
PATCH 29/41 x86_64: Use CPU ops for nmi alert counter
In reply to Christoph LameterThese are critical fast paths. Reduce overhead by using a segment override instead of an address calculation.
--- arch/x86/kernel/nmi_64.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/x86/kernel/nmi_64.c +4 -4
@@ -290,7 +290,7 @@*/ static DEFINE_PER_CPU(unsigned, last_irq_sum); -static DEFINE_PER_CPU(local_t, alert_counter); +static DEFINE_PER_CPU(int, alert_counter); static DEFINE_PER_CPU(int, nmi_touch);void touch_nmi_watchdog (void )
@@ -356,13 +356,13 @@* Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ -local_inc (&__get_cpu_var (alert_counter)); -if (local_read (&__get_cpu_var (alert_counter)) ==5 *nmi_hz) +CPU_INC (per_cpu_var (alert_counter)); +if (CPU_READ (per_cpu_var (alert_counter)) ==5 *nmi_hz)die_nmi ("NMI Watchdog detected LOCKUP on CPU %d\n" , regs, panic_on_timeout); }else {__get_cpu_var (last_irq_sum) = sum; -local_set (&__get_cpu_var (alert_counter),0 ); +CPU_WRITE (per_cpu_var (alert_counter),0 ); }/* see if the nmi watchdog went off */
PATCH 30/41 Remove local_t support
In reply to Christoph LameterThere is no user of local_t remaining after the cpu ops patchset. local_t always suffered from the problem that the operations it generated were not able to perform the relocation of a pointer to the target processor and the atomic update at the same time. There was a need to disable preemption and/or interrupts which made it awkward to use.
Quirk:
- linux/module.h needs to include hardirq.h now since asm-generic/local.h did and some arches now depend on it (sparc64 for example).
--- Documentation/local_ops.txt | 186 --------------------- arch/frv/kernel/local.h | 59 ------ include/asm-alpha/local.h | 118 ------------- include/asm-arm/local.h | 1 include/asm-avr32/local.h | 6 include/asm-blackfin/local.h | 6 include/asm-cris/local.h | 1 include/asm-frv/local.h | 6 include/asm-generic/local.h | 75 -------- include/asm-h8300/local.h | 6 include/asm-ia64/local.h | 1 include/asm-m32r/local.h | 366 ------------------------------------------ include/asm-m68k/local.h | 6 include/asm-m68knommu/local.h | 6 include/asm-mips/local.h | 221 ------------------------- include/asm-mn10300/local.h | 1 include/asm-parisc/local.h | 1 include/asm-powerpc/local.h | 200 ---------------------- include/asm-s390/local.h | 1 include/asm-sh/local.h | 7 include/asm-sparc/local.h | 6 include/asm-sparc64/local.h | 1 include/asm-um/local.h | 6 include/asm-v850/local.h | 6 include/asm-x86/local.h | 235 -------------------------- include/asm-xtensa/local.h | 16 - include/linux/module.h | 4 27 files changed, 3 insertions(+), 1545 deletions(-)
Documentation/local_ops.txt deleted +0 -186
@@ -1,186 +0,0 @@ - Semantics and Behavior of Local Atomic Operations - - Mathieu Desnoyers - - - This document explains the purpose of the local atomic operations, how -to implement them for any given architecture and shows how they can be used -properly. It also stresses on the precautions that must be taken when reading -those local variables across CPUs when the order of memory writes matters. - - - -* Purpose of local atomic operations - -Local atomic operations are meant to provide fast and highly reentrant per CPU -counters. They minimize the performance cost of standard atomic operations by -removing the LOCK prefix and memory barriers normally required to synchronize -across CPUs. - -Having fast per CPU atomic counters is interesting in many cases : it does not -require disabling interrupts to protect from interrupt handlers and it permits -coherent counters in NMI handlers. It is especially useful for tracing purposes -and for various performance monitoring counters. - -Local atomic operations only guarantee variable modification atomicity wrt the -CPU which owns the data. Therefore, care must taken to make sure that only one -CPU writes to the local_t data. This is done by using per cpu data and making -sure that we modify it from within a preemption safe context. It is however -permitted to read local_t data from any CPU : it will then appear to be written -out of order wrt other memory writes by the owner CPU. - - -* Implementation for a given architecture - -It can be done by slightly modifying the standard atomic operations : only -their UP variant must be kept. It typically means removing LOCK prefix (on -i386 and x86_64) and any SMP sychronization barrier. If the architecture does -not have a different behavior between SMP and UP, including asm-generic/local.h -in your archtecture's local.h is sufficient. - -The local_t type is defined as an opaque signed long by embedding an -atomic_long_t inside a structure. This is made so a cast from this type to a -long fails. The definition looks like : - -typedef struct { atomic_long_t a; } local_t; - - -* Rules to follow when using local atomic operations - -- Variables touched by local ops must be per cpu variables. -- _Only_ the CPU owner of these variables must write to them. -- This CPU can use local ops from any context (process, irq, softirq, nmi, ...) - to update its local_t variables. -- Preemption (or interrupts) must be disabled when using local ops in - process context to make sure the process won't be migrated to a - different CPU between getting the per-cpu variable and doing the - actual local op. -- When using local ops in interrupt context, no special care must be - taken on a mainline kernel, since they will run on the local CPU with - preemption already disabled. I suggest, however, to explicitly - disable preemption anyway to make sure it will still work correctly on - -rt kernels. -- Reading the local cpu variable will provide the current copy of the - variable. -- Reads of these variables can be done from any CPU, because updates to - "long", aligned, variables are always atomic. Since no memory - synchronization is done by the writer CPU, an outdated copy of the - variable can be read when reading some _other_ cpu's variables. - - -* How to use local atomic operations - -#include <linux/percpu.h> -#include <asm/local.h> - -static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0); - - -* Counting - -Counting is done on all the bits of a signed long. - -In preemptible context, use get_cpu_var() and put_cpu_var() around local atomic -operations : it makes sure that preemption is disabled around write access to -the per cpu variable. For instance : - - local_inc(&get_cpu_var(counters)); - put_cpu_var(counters); - -If you are already in a preemption-safe context, you can directly use -__get_cpu_var() instead. - - local_inc(&__get_cpu_var(counters)); - - - -* Reading the counters - -Those local counters can be read from foreign CPUs to sum the count. Note that -the data seen by local_read across CPUs must be considered to be out of order -relatively to other memory writes happening on the CPU that owns the data. - - long sum = 0; - for_each_online_cpu(cpu) - sum += local_read(&per_cpu(counters, cpu)); - -If you want to use a remote local_read to synchronize access to a resource -between CPUs, explicit smp_wmb() and smp_rmb() memory barriers must be used -respectively on the writer and the reader CPUs. It would be the case if you use -the local_t variable as a counter of bytes written in a buffer : there should -be a smp_wmb() between the buffer write and the counter increment and also a -smp_rmb() between the counter read and the buffer read. - - -Here is a sample module which implements a basic per cpu counter using local.h. - ---- BEGIN --- -/* test-local.c - * - * Sample module for local.h usage. - */ - - -#include <asm/local.h> -#include <linux/module.h> -#include <linux/timer.h> - -static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0); - -static struct timer_list test_timer; - -/* IPI called on each CPU. */ -static void test_each(void *info) -{ - /* Increment the counter from a non preemptible context */ - printk("Increment on cpu %d\n", smp_processor_id()); - local_inc(&__get_cpu_var(counters)); - - /* This is what incrementing the variable would look like within a - * preemptible context (it disables preemption) : - * - * local_inc(&get_cpu_var(counters)); - * put_cpu_var(counters); - */ -} - -static void do_test_timer(unsigned long data) -{ - int cpu; - - /* Increment the counters */ - on_each_cpu(test_each, NULL, 0, 1); - /* Read all the counters */ - printk("Counters read from CPU %d\n", smp_processor_id()); - for_each_online_cpu(cpu) { - printk("Read : CPU %d, count %ld\n", cpu, - local_read(&per_cpu(counters, cpu))); - } - del_timer(&test_timer); - test_timer.expires = jiffies + 1000; - add_timer(&test_timer); -} - -static int __init test_init(void) -{ - /* initialize the timer that will increment the counter */ - init_timer(&test_timer); - test_timer.function = do_test_timer; - test_timer.expires = jiffies + 1; - add_timer(&test_timer); - - return 0; -} - -static void __exit test_exit(void) -{ - del_timer_sync(&test_timer); -} - -module_init(test_init); -module_exit(test_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Local Atomic Ops"); ---- END ---
include/asm-x86/local.h deleted +0 -235
235 lines not on this page · raw message
arch/frv/kernel/local.h deleted +0 -59
59 lines not on this page · raw message
include/asm-alpha/local.h deleted +0 -118
118 lines not on this page · raw message
include/asm-arm/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-avr32/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef __ASM_AVR32_LOCAL_H -#define __ASM_AVR32_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* __ASM_AVR32_LOCAL_H */
include/asm-blackfin/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef __BLACKFIN_LOCAL_H -#define __BLACKFIN_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* __BLACKFIN_LOCAL_H */
include/asm-cris/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-frv/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef _ASM_LOCAL_H -#define _ASM_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* _ASM_LOCAL_H */
include/asm-generic/local.h deleted +0 -75
75 lines not on this page · raw message
include/asm-h8300/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef _H8300_LOCAL_H_ -#define _H8300_LOCAL_H_ - -#include <asm-generic/local.h> - -#endif
include/asm-ia64/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-m32r/local.h deleted +0 -366
366 lines not on this page · raw message
include/asm-m68k/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef _ASM_M68K_LOCAL_H -#define _ASM_M68K_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* _ASM_M68K_LOCAL_H */
include/asm-m68knommu/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef __M68KNOMMU_LOCAL_H -#define __M68KNOMMU_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* __M68KNOMMU_LOCAL_H */
include/asm-mips/local.h deleted +0 -221
221 lines not on this page · raw message
include/asm-parisc/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-powerpc/local.h deleted +0 -200
200 lines not on this page · raw message
include/asm-s390/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-sh/local.h deleted +0 -7
@@ -1,7 +0,0 @@ -#ifndef __ASM_SH_LOCAL_H -#define __ASM_SH_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* __ASM_SH_LOCAL_H */ -
include/asm-sparc/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef _SPARC_LOCAL_H -#define _SPARC_LOCAL_H - -#include <asm-generic/local.h> - -#endif
include/asm-sparc64/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
include/asm-um/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef __UM_LOCAL_H -#define __UM_LOCAL_H - -#include "asm/arch/local.h" - -#endif
include/asm-v850/local.h deleted +0 -6
@@ -1,6 +0,0 @@ -#ifndef __V850_LOCAL_H__ -#define __V850_LOCAL_H__ - -#include <asm-generic/local.h> - -#endif /* __V850_LOCAL_H__ */
include/asm-xtensa/local.h deleted +0 -16
@@ -1,16 +0,0 @@ -/* -* include/asm-xtensa/local.h -* -* This file is subject to the terms and conditions of the GNU General Public -* License. See the file "COPYING" in the main directory of this archive -* for more details. -* -* Copyright (C) 2001 - 2005 Tensilica Inc. -*/ - -#ifndef _XTENSA_LOCAL_H -#define _XTENSA_LOCAL_H - -#include <asm-generic/local.h> - -#endif /* _XTENSA_LOCAL_H */
include/linux/module.h +3 -1
@@ -16,10 +16,12 @@#include <linux/kobject.h> #include <linux/moduleparam.h> #include <linux/marker.h> -#include <asm/local.h> +#include <linux/percpu.h> +#include <linux/hardirq.h> #include <asm/module.h> +/* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE (name)
include/asm-mn10300/local.h deleted +0 -1
@@ -1 +0,0 @@ -#include <asm-generic/local.h>
PATCH 31/41 VM statistics: Use CPU ops
In reply to Christoph LameterThe use of CPU ops here avoids the offset calculations that we used to have to do with per cpu operations. The result of this patch is that event counters are coded with a single instruction the following way:
incq %gs:offset(%rip)
Without these patches this was:
mov %gs:0x8,%rdx mov %eax,0x38(%rsp) mov xxx(%rip),%eax mov %eax,0x48(%rsp) mov varoffset,%rax incq 0x110(%rax,%rdx,1)
--- include/linux/vmstat.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
include/linux/vmstat.h +4 -6
@@ -63,24 +63,22 @@static inline void __count_vm_event (enum vm_event_item item) { -__get_cpu_var (vm_event_states).event[item]++; +__CPU_INC (per_cpu_var (vm_event_states).event[item]); }static inline void count_vm_event (enum vm_event_item item) { -get_cpu_var (vm_event_states).event[item]++; -put_cpu (); +_CPU_INC (per_cpu_var (vm_event_states).event[item]); }static inline void __count_vm_events (enum vm_event_item item,long delta) { -__get_cpu_var (vm_event_states).event[item] += delta; +__CPU_ADD (per_cpu_var (vm_event_states).event[item], delta); }static inline void count_vm_events (enum vm_event_item item,long delta) { -get_cpu_var (vm_event_states).event[item] += delta; -put_cpu (); +_CPU_ADD (per_cpu_var (vm_event_states).event[item], delta); }extern void all_vm_events (unsigned long *);
PATCH 32/41 cpu alloc: Use in slub
In reply to Christoph LameterUsing cpu alloc removes the needs for the per cpu arrays in the kmem_cache struct. These could get quite big if we have to support system of up to thousands of cpus. The use of cpu_alloc means that:
1. The size of kmem_cache for SMP configuration shrinks since we will only need 1 pointer instead of NR_CPUS. The same pointer can be used by all processors. Reduces cache footprint of the allocator.
2. We can dynamically size kmem_cache according to the actual nodes in the system meaning less memory overhead for configurations that may potentially support up to 1k NUMA nodes.
3. We can remove the diddle widdle with allocating and releasing of kmem_cache_cpu structures when bringing up and shutting down cpus. The cpu alloc logic will do it all for us. Removes some portions of the cpu hotplug functionality.
4. Fastpath performance increases by 20%.
--- arch/x86/Kconfig | 4 include/linux/slub_def.h | 6 - mm/slub.c | 226 ++++++++++------------------------------------- 3 files changed, 50 insertions(+), 186 deletions(-)
include/linux/slub_def.h +1 -5
@@ -67,6 +67,7 @@* Slab cache management. */ struct kmem_cache { +struct kmem_cache_cpu *cpu_slab;/* Used for retriving partial slabs etc */ unsigned long flags;int size;/* The size of an object including meta data */
@@ -101,11 +102,6 @@int remote_node_defrag_ratio;struct kmem_cache_node *node[MAX_NUMNODES ];#endif -#ifdef CONFIG_SMP -struct kmem_cache_cpu *cpu_slab[NR_CPUS ]; -#else -struct kmem_cache_cpu cpu_slab; -#endif };/*
mm/slub.c +33 -160
341 lines not on this page · raw message
PATCH 33/41 cpu alloc: Remove slub fields
In reply to Christoph LameterRemove the fields in kmem_cache_cpu that were used to cache data from kmem_cache when they were in different cachelines. The cacheline that holds the per cpu array pointer now also holds these values. We can cut down the kmem_cache_cpu size to almost half.
The get_freepointer() and set_freepointer() functions that used to be only intended for the slow path now are also useful for the hot path since access to the field does not require accessing an additional cacheline anymore. This results in consistent use of setting the freepointer for objects throughout SLUB.
--- include/linux/slub_def.h | 3 -- mm/slub.c | 48 +++++++++++++++-------------------------------- 2 files changed, 16 insertions(+), 35 deletions(-)
include/linux/slub_def.h +0 -2
@@ -36,8 +36,6 @@void **freelist;/* Pointer to first free per cpu object */ struct page *page;/* The slab from which we are allocating */ int node;/* The node of the page (or -1 for debug) */ -unsigned int offset;/* Freepointer offset (in word units) */ -unsigned int objsize;/* Size of an object (from kmem_cache) */ #ifdef CONFIG_SLUB_STATS unsigned stat[NR_SLUB_STAT_ITEMS ];#endif
mm/slub.c +11 -28
@@ -276,13 +276,6 @@return 1 ; } -/* -* Slow version of get and set free pointer. -* -* This version requires touching the cache lines of kmem_cache which -* we avoid to do in the fast alloc free paths. There we obtain the offset -* from the page struct. -*/ static inline void *get_freepointer (struct kmem_cache *s,void *object) {return *(void **)(object + s->offset);
@@ -1447,10 +1440,10 @@/* Retrieve object from cpu_freelist */ object = c->freelist; - c->freelist = c->freelist[c->offset]; + c->freelist =get_freepointer (s, c->freelist);/* And put onto the regular freelist */ - object[c->offset] = page->freelist; +set_freepointer (s, object, page->freelist); page->freelist = object; page->inuse--; }
@@ -1555,7 +1548,7 @@if (unlikely (SlabDebug (c->page))) goto debug; - c->freelist = object[c->offset]; + c->freelist =get_freepointer (s, object); c->page->inuse = c->page->objects; c->page->freelist =NULL ; c->node =page_to_nid (c->page);
@@ -1599,7 +1592,7 @@ goto another_slab; c->page->inuse++; - c->page->freelist = object[c->offset]; + c->page->freelist =get_freepointer (s, object); c->node =-1 ; goto unlock_out; }
@@ -1629,7 +1622,7 @@ object =__slab_alloc (s, gfpflags, node, addr, c); else { - c->freelist = object[c->offset]; + c->freelist =get_freepointer (s, object);stat (c,ALLOC_FASTPATH ); }local_irq_restore (flags);
@@ -1663,7 +1656,7 @@* handling required then we can return immediately. */ static void __slab_free (struct kmem_cache *s,struct page *page, -void *x,void *addr,unsigned int offset) +void *x,void *addr) {void *prior;void **object = (void *)x;
@@ -1677,7 +1670,8 @@ goto debug; checks_ok: - prior = object[offset] = page->freelist; + prior = page->freelist; +set_freepointer (s, object, prior); page->freelist = object; page->inuse--;
@@ -1741,15 +1735,15 @@local_irq_save (flags); c =__THIS_CPU (s->cpu_slab); -debug_check_no_locks_freed (object, c->objsize); +debug_check_no_locks_freed (object, s->objsize);if (!(s->flags &SLAB_DEBUG_OBJECTS ))debug_check_no_obj_freed (object, s->objsize);if (likely (page == c->page && c->node >=0 )) { - object[c->offset] = c->freelist; +set_freepointer (s, object, c->freelist); c->freelist = object;stat (c,FREE_FASTPATH ); }else -__slab_free (s, page, x, addr, c->offset); +__slab_free (s, page, x, addr);local_irq_restore (flags); }
@@ -1936,8 +1930,6 @@ c->page =NULL ; c->freelist =NULL ; c->node =0 ; - c->offset = s->offset /sizeof (void *); - c->objsize = s->objsize;#ifdef CONFIG_SLUB_STATS memset (c->stat,0 ,NR_SLUB_STAT_ITEMS *sizeof (unsigned ));#endif
@@ -2993,8 +2985,6 @@down_write (&slub_lock); s =find_mergeable (size, align, flags, name, ctor);if (s) { -int cpu; - s->refcount++;/* * Adjust the object sizes so that we clear
@@ -3002,13 +2992,6 @@*/ s->objsize =max (s->objsize, (int )size); -/* -* And then we need to update the object size in the -* per cpu structures -*/ -for_each_online_cpu (cpu) -CPU_PTR (s->cpu_slab, cpu)->objsize = s->objsize; - s->inuse =max_t (int, s->inuse,ALIGN (size,sizeof (void *)));up_write (&slub_lock);
PATCH 34/41 cpu alloc: Page allocator conversion
In reply to Christoph LameterUse the new cpu_alloc functionality to avoid per cpu arrays in struct zone. This drastically reduces the size of struct zone for systems with a large amounts of processors and allows placement of critical variables of struct zone in one cacheline even on very large systems.
Another effect is that the pagesets of one processor are placed near one another. If multiple pagesets from different zones fit into one cacheline then additional cacheline fetches can be avoided on the hot paths when allocating memory from multiple zones.
Surprisingly this clears up much of the painful NUMA bringup. Bootstrap becomes simpler if we use the same scheme for UP, SMP, NUMA. #ifdefs are reduced and we can drop the zone_pcp macro.
Hotplug handling is also simplified since hotplug already brings up a percpu area which comes with a per cpu alloc area. So there is no need to allocate or free individual pagesets anymore.
--- arch/x86/kernel/setup.c | 6 + include/linux/gfp.h | 1 include/linux/mm.h | 4 - include/linux/mmzone.h | 12 --- init/main.c | 5 + mm/page_alloc.c | 172 +++++++++++++++++++----------------------------- mm/vmstat.c | 14 ++- 7 files changed, 93 insertions(+), 121 deletions(-)
include/linux/mm.h +0 -4
@@ -1024,11 +1024,7 @@ extern void show_mem(void);extern void si_meminfo (struct sysinfo * val);extern void si_meminfo_node (struct sysinfo *val,int nid); -#ifdef CONFIG_NUMA extern void setup_per_cpu_pageset (void ); -#else -static inline void setup_per_cpu_pageset (void ) {} -#endif /* prio_tree.c */ void vma_prio_tree_add (struct vm_area_struct *,struct vm_area_struct *old);
include/linux/mmzone.h +2 -10
@@ -123,13 +123,7 @@ struct per_cpu_pageset {s8 stat_threshold;s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS ]; #endif -}____cacheline_aligned_in_smp ; - -#ifdef CONFIG_NUMA -#define zone_pcp (__z, __cpu) ((__z)->pageset[(__cpu)]) -#else -#define zone_pcp (__z, __cpu) (&(__z)->pageset[(__cpu)]) -#endif +};#endif /* !__GENERATING_BOUNDS.H */
@@ -224,10 +218,8 @@ struct zone {*/ unsigned long min_unmapped_pages;unsigned long min_slab_pages; -struct per_cpu_pageset *pageset[NR_CPUS ]; -#else -struct per_cpu_pageset pageset[NR_CPUS ];#endif +struct per_cpu_pageset *pageset;/* * free areas of different sizes */
mm/page_alloc.c +71 -101
290 lines not on this page · raw message
mm/vmstat.c +8 -6
@@ -142,7 +142,8 @@ static void refresh_zone_stat_thresholds threshold =calculate_threshold (zone);for_each_online_cpu (cpu) -zone_pcp (zone, cpu)->stat_threshold = threshold; +CPU_PTR (zone->pageset, cpu)->stat_threshold + = threshold; } }
@@ -152,7 +153,8 @@ static void refresh_zone_stat_thresholdsvoid __mod_zone_page_state (struct zone *zone,enum zone_stat_item item,int delta) { -struct per_cpu_pageset *pcp =zone_pcp (zone,smp_processor_id ()); +struct per_cpu_pageset *pcp =THIS_CPU (zone->pageset); +s8 *p = pcp->vm_stat_diff + item;long x;
@@ -205,7 +207,7 @@ EXPORT_SYMBOL(mod_zone_page_state);*/ void __inc_zone_state (struct zone *zone,enum zone_stat_item item) { -struct per_cpu_pageset *pcp =zone_pcp (zone,smp_processor_id ()); +struct per_cpu_pageset *pcp =THIS_CPU (zone->pageset);s8 *p = pcp->vm_stat_diff + item; (*p)++;
@@ -226,7 +228,7 @@ EXPORT_SYMBOL(__inc_zone_page_state);void __dec_zone_state (struct zone *zone,enum zone_stat_item item) { -struct per_cpu_pageset *pcp =zone_pcp (zone,smp_processor_id ()); +struct per_cpu_pageset *pcp =THIS_CPU (zone->pageset);s8 *p = pcp->vm_stat_diff + item; (*p)--;
@@ -306,7 +308,7 @@ void refresh_cpu_vm_stats(int cpu)if (!populated_zone (zone))continue ; - p =zone_pcp (zone, cpu); + p =CPU_PTR (zone->pageset, cpu);for (i =0 ; i <NR_VM_ZONE_STAT_ITEMS ; i++)if (p->vm_stat_diff[i]) {
@@ -698,7 +700,7 @@ static void zoneinfo_show_print(struct sfor_each_online_cpu (i) {struct per_cpu_pageset *pageset; - pageset =zone_pcp (zone, i); + pageset =CPU_PTR (zone->pageset, i);seq_printf (m,"\n cpu: %i" "\n count: %i"
arch/x86/kernel/setup.c +6 -0
@@ -125,6 +125,12 @@ void __init setup_per_cpu_areas(void) highest_cpu = i; } +/* +* The per_cpu offsets have changed and therefore the pageset +* pointers need to be updated. +*/ +setup_boot_pagesets (); + nr_cpu_ids = highest_cpu +1 ;printk (KERN_DEBUG "NR_CPUS: %d, nr_cpu_ids: %d\n" ,NR_CPUS , nr_cpu_ids);
include/linux/gfp.h +1 -0
@@ -233,5 +233,6 @@ void page_alloc_init(void);void drain_zone_pages (struct zone *zone,struct per_cpu_pages *pcp);void drain_all_pages (void );void drain_local_pages (void *dummy); +void setup_boot_pagesets (void );#endif /* __LINUX_GFP_H */
init/main.c +5 -0
@@ -405,6 +405,11 @@ static void __init setup_per_cpu_areas(vmemcpy (ptr, __per_cpu_start, __per_cpu_size); ptr += __per_cpu_size; } +/* +* __per_cpu_offset[] have changed. Need to update the +* pointers to the boot page set. +*/ +setup_boot_pagesets (); }#endif /* CONFIG_HAVE_SETUP_PER_CPU_AREA */
PATCH 35/41 Support for CPU ops
In reply to Christoph LameterIA64 has no efficient atomic operations. But we can get rid of the need to add my_percpu_offset(). The address of a per cpu variable can be used directly on IA64 since its mapped to a per processor area.
This also allows us to kill off the __ia64_get_cpu_var macro. Its nothing but per_cpu_var().
--- arch/ia64/Kconfig | 3 arch/ia64/kernel/perfmon.c | 2 arch/ia64/kernel/setup.c | 2 arch/ia64/kernel/smp.c | 4 - arch/ia64/sn/kernel/setup.c | 4 - include/asm-ia64/mmu_context.h | 6 - include/asm-ia64/percpu.h | 133 ++++++++++++++++++++++++++++++++++++++--- include/asm-ia64/processor.h | 2 include/asm-ia64/sn/pda.h | 2 9 files changed, 138 insertions(+), 20 deletions(-)
include/asm-ia64/percpu.h +124 -9
@@ -19,7 +19,7 @@# define PER_CPU_ATTRIBUTES __attribute__((__model__ (__small__)))#endif -#define __my_cpu_offset __ia64_per_cpu_var(local_per_cpu_offset) +#define __my_cpu_offset CPU_READ(per_cpu_var(local_per_cpu_offset))extern void *per_cpu_init (void );
@@ -31,14 +31,6 @@ extern void *per_cpu_init(void);#endif /* SMP */ -/* -* Be extremely careful when taking the address of this variable! Due to virtual -* remapping, it is different from the canonical address returned by __get_cpu_var(var)! -* On the positive side, using __ia64_per_cpu_var() instead of __get_cpu_var() is slightly -* more efficient. -*/ -#define __ia64_per_cpu_var (var) per_cpu__##var -#include <asm-generic/percpu.h> /* Equal to __per_cpu_offset[smp_processor_id()], but faster to access: */
@@ -46,4 +38,127 @@ DECLARE_PER_CPU(unsigned long, local_per#endif /* !__ASSEMBLY__ */ +/* +* Per cpu ops. +* +* IA64 has no instructions that would allow light weight RMW operations. +* +* However, the canonical address of a per cpu variable is mapped via +* a processor specific TLB entry to the per cpu area of the respective +* processor. The THIS_CPU() macro is therefore not necessary here +* since the canonical address of the per cpu variable allows access +* to the instance of the per cpu variable for the current processor. +* +* Sadly we cannot simply define THIS_CPU() to return an address in +* the per processor mapping space since the address acquired by THIS_CPU\ +* may be passed to another processor. +*/ +#define __CPU_READ (var) \ +({ \ + (var); \ +}) + +#define __CPU_WRITE (var, value) \ +({ \ + (var) = (value); \ +}) + +#define __CPU_ADD (var, value) \ +({ \ + (var) += (value); \ +}) + +#define __CPU_INC (var) __CPU_ADD((var), 1) +#define __CPU_DEC (var) __CPU_ADD((var), -1) +#define __CPU_SUB (var, value) __CPU_ADD((var), -(value)) + +#define __CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = &(var); \ + x = *p; \ + if (x == (old)) \ + *p = (new); \ + (x); \ +}) + +#define __CPU_XCHG (obj, new) \ +({ \ + typeof(obj) x; \ + typeof(obj) *p = &(obj); \ + x = *p; \ + *p = (new); \ + (x); \ +}) + +#define _CPU_READ __CPU_READ +#define _CPU_WRITE __CPU_WRITE + +#define _CPU_ADD (var, value) \ +({ \ + preempt_disable(); \ + __CPU_ADD((var), (value)); \ + preempt_enable(); \ +}) + +#define _CPU_INC (var) _CPU_ADD((var), 1) +#define _CPU_DEC (var) _CPU_ADD((var), -1) +#define _CPU_SUB (var, value) _CPU_ADD((var), -(value)) + +#define _CPU_CMPXCHG (var, old, new) \ +({ \ + typeof(addr) x; \ + preempt_disable(); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + preempt_enable(); \ + (x); \ +}) + +#define _CPU_XCHG (var, new) \ +({ \ + typeof(var) x; \ + preempt_disable(); \ + x = __CPU_XCHG((var), (new)); \ + preempt_enable(); \ + (x); \ +}) + +/* +* Third group: Interrupt safe CPU functions +*/ +#define CPU_READ __CPU_READ +#define CPU_WRITE __CPU_WRITE + +#define CPU_ADD (var, value) \ +({ \ + unsigned long flags; \ + local_irq_save(flags); \ + __CPU_ADD((var), (value)); \ + local_irq_restore(flags); \ +}) + +#define CPU_INC (var) CPU_ADD((var), 1) +#define CPU_DEC (var) CPU_ADD((var), -1) +#define CPU_SUB (var, value) CPU_ADD((var), -(value)) + +#define CPU_CMPXCHG (var, old, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_CMPXCHG((var), (old), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) + +#define CPU_XCHG (var, new) \ +({ \ + unsigned long flags; \ + typeof(var) x; \ + local_irq_save(flags); \ + x = __CPU_XCHG((var), (new)); \ + local_irq_restore(flags); \ + (x); \ +}) +#endif /* _ASM_IA64_PERCPU_H */
arch/ia64/kernel/perfmon.c +1 -1
@@ -576,7 +576,7 @@ static struct ctl_table_header *pfm_syscstatic int pfm_context_unload (pfm_context_t *ctx,void *arg,int count,struct pt_regs *regs); -#define pfm_get_cpu_var (v) __ia64_per_cpu_var(v) +#define pfm_get_cpu_var (v) per_cpu_var(v)#define pfm_get_cpu_data (a,b) per_cpu(a, b) static inline void
arch/ia64/kernel/setup.c +1 -1
@@ -925,7 +925,7 @@ cpu_init (void)* depends on the data returned by identify_cpu(). We break the dependency by * accessing cpu_data() through the canonical per-CPU address. */ - cpu_info = cpu_data + ((char *) &__ia64_per_cpu_var (cpu_info) - __per_cpu_start); + cpu_info = cpu_data + ((char *)&per_cpu_var (cpu_info) - __per_cpu_start);identify_cpu (cpu_info);#ifdef CONFIG_MCKINLEY
arch/ia64/kernel/smp.c +2 -2
@@ -150,7 +150,7 @@ irqreturn_t handle_IPI (int irq, void *dev_id) { int this_cpu = get_cpu(); -unsigned long *pending_ipis = &__ia64_per_cpu_var (ipi_operation); +unsigned long *pending_ipis = &per_cpu_var (ipi_operation);unsigned long ops;mb ();/* Order interrupt and bit testing. */
@@ -303,7 +303,7 @@ smp_local_flush_tlb(void)void smp_flush_tlb_cpumask (cpumask_t xcpumask) { -unsigned int *counts =__ia64_per_cpu_var (shadow_flush_counts); +unsigned int *counts =per_cpu_var (shadow_flush_counts);cpumask_t cpumask = xcpumask;int mycpu, cpu, flush_mycpu =0 ;
arch/ia64/sn/kernel/setup.c +2 -2
@@ -645,7 +645,7 @@ void __cpuinit sn_cpu_init(void)/* copy cpu 0's sn_cnodeid_to_nasid table to this cpu's */ memcpy (sn_cnodeid_to_nasid, (&per_cpu (__sn_cnodeid_to_nasid,0 )), -sizeof (__ia64_per_cpu_var (__sn_cnodeid_to_nasid))); +sizeof (per_cpu_var (__sn_cnodeid_to_nasid))); }/*
@@ -706,7 +706,7 @@ void __init build_cnode_tables(void)memset (physical_node_map,-1 ,sizeof (physical_node_map));memset (sn_cnodeid_to_nasid,-1 , -sizeof (__ia64_per_cpu_var (__sn_cnodeid_to_nasid))); +sizeof (per_cpu_var (__sn_cnodeid_to_nasid)));/* * First populate the tables with C/M bricks. This ensures that
include/asm-ia64/mmu_context.h +3 -3
@@ -64,11 +64,11 @@ delayed_tlb_flush (void)extern void local_flush_tlb_all (void );unsigned long flags; -if (unlikely (__ia64_per_cpu_var (ia64_need_tlb_flush))) { +if (unlikely (CPU_READ (per_cpu_var (ia64_need_tlb_flush)))) {spin_lock_irqsave (&ia64_ctx.lock, flags); -if (__ia64_per_cpu_var (ia64_need_tlb_flush)) { +if (CPU_READ (per_cpu_var (ia64_need_tlb_flush))) {local_flush_tlb_all (); -__ia64_per_cpu_var (ia64_need_tlb_flush) =0 ; +CPU_WRITE (per_cpu_var (ia64_need_tlb_flush),0 ); }spin_unlock_irqrestore (&ia64_ctx.lock, flags); }
include/asm-ia64/processor.h +1 -1
@@ -237,7 +237,7 @@ DECLARE_PER_CPU(struct cpuinfo_ia64, cpu* Do not use the address of local_cpu_data, since it will be different from * cpu_data(smp_processor_id())! */ -#define local_cpu_data (&__ia64_per_cpu_var(cpu_info)) +#define local_cpu_data (&per_cpu_var(cpu_info))#define cpu_data (cpu) (&per_cpu(cpu_info, cpu))extern void print_cpu_info (struct cpuinfo_ia64 *);
include/asm-ia64/sn/pda.h +1 -1
@@ -62,7 +62,7 @@ typedef struct pda_s {*/ DECLARE_PER_CPU(struct pda_s, pda_percpu); -#define pda (&__ia64_per_cpu_var(pda_percpu)) +#define pda (&per_cpu_var(pda_percpu))#define pdacpu (cpu) (&per_cpu(pda_percpu, cpu))
arch/ia64/Kconfig +3 -0
@@ -92,6 +92,9 @@ config GENERIC_TIME_VSYSCALL config HAVE_SETUP_PER_CPU_AREA def_bool y +config HAVE_CPU_OPS + def_bool y + config DMI bool default y
Andrew Morton
maintainerRe: [patch 35/41] Support for CPU ops
Quoted 1 line · On Thu, 29 May 2008 20:56:55 -0700 Christoph Lameter <[email protected]> wrote:
Subject: [patch 35/41] Support for CPU ops
Should be called "ia64: support for CPU ops", please.
Re: [patch 35/41] Support for CPU ops
Quoted 5 lines · Andrew Morton
Quoted 1 line · On Thu, 29 May 2008 20:56:55 -0700 Christoph Lameter <[email protected]> wrote:
Subject: [patch 35/41] Support for CPU ops
Should be called "ia64: support for CPU ops", please.
Argh. Gazillions of details on gazillions of arches over all sorts of kernel subsystems.
PATCH 36/41 Zero based percpu: Infrastructure to rebase the per cpu area to zero
In reply to Christoph Lameter* Support an option
CONFIG_HAVE_ZERO_BASED_PER_CPU
to make offsets for per cpu variables to start at zero.
If a percpu area starts at zero then:
- We do not need RELOC_HIDE anymore
- Provides for the future capability of architectures providing a per cpu allocator that returns offsets instead of pointers. The offsets would be independent of the processor so that address calculations can be done in a processor independent way. Per cpu instructions can then add the processor specific offset at the last minute possibly in an atomic instruction.
The data the linker provides is different for zero based percpu segments:
__per_cpu_load -> The address at which the percpu area was loaded
__per_cpu_size -> The length of the per cpu area
* Removes the &__per_cpu_x in lockdep. The __per_cpu_x are already pointers. There is no need to take the address.
* Updates kernel/module.c to be able to deal with a percpu area that is loaded at __per_cpu_load but is accessed at __per_cpu_start.
--- include/asm-generic/percpu.h | 9 ++++++++- include/asm-generic/sections.h | 10 ++++++++++ include/asm-generic/vmlinux.lds.h | 16 ++++++++++++++++ include/linux/percpu.h | 8 ++++++++ kernel/lockdep.c | 4 ++-- 5 files changed, 44 insertions(+), 3 deletions(-)
include/asm-generic/percpu.h +8 -1
@@ -45,7 +45,12 @@ extern unsigned long __per_cpu_offset[NR* Only S390 provides its own means of moving the pointer. */ #ifndef SHIFT_PERCPU_PTR -#define SHIFT_PERCPU_PTR (__p, __offset) RELOC_HIDE((__p), (__offset)) +# ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU +# define SHIFT_PERCPU_PTR (__p, __offset) \ + ((__typeof(__p))(((void *)(__p)) + (__offset))) +# else +# define SHIFT_PERCPU_PTR (__p, __offset) RELOC_HIDE((__p), (__offset)) +# endif /* CONFIG_HAVE_ZERO_BASED_PER_CPU */ #endif /*
@@ -70,6 +75,8 @@ extern void setup_per_cpu_areas(void);#define per_cpu (var, cpu) (*((void)(cpu), &per_cpu_var(var)))#define __get_cpu_var (var) per_cpu_var(var)#define __raw_get_cpu_var (var) per_cpu_var(var) +#define SHIFT_PERCPU_PTR (__p, __offset) (__p) +#define per_cpu_offset (x) 0L#endif /* SMP */
include/asm-generic/sections.h +10 -0
@@ -9,7 +9,17 @@ extern char __bss_start[], __bss_stop[];extern char __init_begin[], __init_end[];extern char _sinittext[], _einittext[];extern char _end[]; +#ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU +extern char __per_cpu_load[]; +extern char ____per_cpu_size[]; +#define __per_cpu_size ((unsigned long)&____per_cpu_size) +#define __per_cpu_start ((char *)0) +#define __per_cpu_end ((char *)__per_cpu_size) +#else extern char __per_cpu_start[], __per_cpu_end[]; +#define __per_cpu_load __per_cpu_start +#define __per_cpu_size (__per_cpu_end - __per_cpu_start) +#endif extern char __kprobes_text_start[], __kprobes_text_end[];extern char __initdata_begin[], __initdata_end[];extern char __start_rodata[], __end_rodata[];
include/asm-generic/vmlinux.lds.h +16 -0
@@ -344,6 +344,21 @@ *(.initcall7.init) \ *(.initcall7s.init) +#ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU +#define PERCPU (align) \ + . = ALIGN(align); \ + percpu : { } :percpu \ + __per_cpu_load = .; \ + .data.percpu 0 : AT(__per_cpu_load - LOAD_OFFSET) { \ + *(.data.percpu.first) \ + *(.data.percpu.shared_aligned) \ + *(.data.percpu) \ + *(.data.percpu.page_aligned) \ + ____per_cpu_size = .; \ + } \ + . = __per_cpu_load + ____per_cpu_size; \ + data : { } :data +#else #define PERCPU (align) \ . = ALIGN(align); \ __per_cpu_start = .; \
@@ -352,3 +367,4 @@ *(.data.percpu.shared_aligned) \ } \ __per_cpu_end = .; +#endif
kernel/lockdep.c +2 -2
@@ -609,8 +609,8 @@ static int static_obj(void *obj)* percpu var? */ for_each_possible_cpu (i) { - start = (unsigned long ) &__per_cpu_start +per_cpu_offset (i); - end = (unsigned long ) &__per_cpu_start + __per_cpu_size + start = (unsigned long ) __per_cpu_start +per_cpu_offset (i); + end = (unsigned long ) __per_cpu_start + __per_cpu_size +per_cpu_offset (i);if ((addr >= start) && (addr < end))
include/linux/percpu.h +8 -0
@@ -23,12 +23,20 @@ __attribute__((__section__(SHARED_ALIGNED_SECTION))) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \ ____cacheline_aligned_in_smp + +#define DEFINE_PER_CPU_FIRST(type, name) \ + __attribute__((__section__(".data.percpu.first"))) \ + PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name + #else #define DEFINE_PER_CPU(type, name) \ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name #define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \ DEFINE_PER_CPU(type, name) + +#define DEFINE_PER_CPU_FIRST(type, name) \ + DEFINE_PER_CPU(type, name) #endif #define EXPORT_PER_CPU_SYMBOL(var) EXPORT_SYMBOL(per_cpu__##var)
PATCH 37/41 x86_64: Fold pda into per cpu area
In reply to Christoph Lameter * Declare the pda as a per cpu variable.
* Make the x86_64 per cpu area start at zero.
* Since %gs is pointing to the pda, it will then also point to the per cpu variables and can be accessed thusly:
%gs:[&per_cpu_xxxx - __per_cpu_start]
--- arch/x86/Kconfig | 3 +++ arch/x86/kernel/setup.c | 22 ++++++++++++++++++++-- arch/x86/kernel/smpboot.c | 16 ---------------- arch/x86/kernel/vmlinux_64.lds.S | 1 + include/asm-x86/percpu.h | 19 +++++++++---------- 5 files changed, 33 insertions(+), 28 deletions(-)
arch/x86/Kconfig +3 -0
@@ -126,6 +126,9 @@ config HAVE_SETUP_PER_CPU_AREA config HAVE_CPUMASK_OF_CPU_MAP def_bool X86_64_SMP +config HAVE_ZERO_BASED_PER_CPU + def_bool X86_64 && SMP + config ARCH_HIBERNATION_POSSIBLE def_bool y depends on !SMP || !X86_VOYAGER
arch/x86/kernel/setup.c +20 -2
@@ -26,6 +26,11 @@ EXPORT_PER_CPU_SYMBOL(x86_cpu_to_apicid)physid_mask_t phys_cpu_present_map;#endif +#ifdef CONFIG_X86_64 +DEFINE_PER_CPU_FIRST(struct x8664_pda, pda); +EXPORT_PER_CPU_SYMBOL (pda); +#endif +#if defined(CONFIG_HAVE_SETUP_PER_CPU_AREA ) && defined(CONFIG_X86_SMP )/* * Copy data used in early init routines from the initial arrays to the
@@ -115,13 +120,20 @@ void __init setup_per_cpu_areas(void)#endif if (!ptr)panic ("Cannot allocate cpu data for CPU %d\n" , i); + +memcpy (ptr, __per_cpu_load, __per_cpu_size); +#ifdef CONFIG_X86_64 +/* +* So far an embryonic per cpu area was used containing only +* the pda. Move the pda contents into the full per cpu area. +*/ cpu_pda (i)->data_offset = ptr - __per_cpu_start; +memcpy (ptr,cpu_pda (i),sizeof (struct x8664_pda )); +cpu_pda (i) = (struct x8664_pda *)ptr;#else __per_cpu_offset[i] = ptr - __per_cpu_start;#endif -memcpy (ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); - highest_cpu = i; }
@@ -132,6 +144,12 @@ void __init setup_per_cpu_areas(void)setup_boot_pagesets (); nr_cpu_ids = highest_cpu +1 ; + +#ifdef CONFIG_X86_64 +/* Fix up pda for boot processor */ +pda_init (0 ); +#endif +printk (KERN_DEBUG "NR_CPUS: %d, nr_cpu_ids: %d\n" ,NR_CPUS , nr_cpu_ids);/* Setup percpu data maps */
arch/x86/kernel/vmlinux_64.lds.S +1 -0
@@ -16,6 +16,7 @@ jiffies_64 = jiffies; _proxy_pda = 1; PHDRS { text PT_LOAD FLAGS(5); /* R_E */ + percpu PT_LOAD FLAGS(4); /* R__ */ data PT_LOAD FLAGS(7); /* RWE */ user PT_LOAD FLAGS(7); /* RWE */ data.init PT_LOAD FLAGS(7); /* RWE */
include/asm-x86/percpu.h +9 -10
@@ -3,21 +3,16 @@#ifdef CONFIG_X86_64 #include <linux/compiler.h> - -/* Same as asm-generic/percpu.h, except that we store the per cpu offset -in the PDA. Longer term the PDA and every per cpu variable -should be just put into a single section and referenced directly -from %gs */ - -#ifdef CONFIG_SMP #include <asm/pda.h> +#ifdef CONFIG_SMP #define __per_cpu_offset (cpu) (cpu_pda(cpu)->data_offset)#define __my_cpu_offset read_pda(data_offset) -#define per_cpu_offset (x) (__per_cpu_offset(x)) -#endif + +#define __percpu_seg "%%gs:" +#include <asm-generic/percpu.h> DECLARE_PER_CPU(struct x8664_pda, pda);
@@ -81,6 +76,11 @@ DECLARE_PER_CPU(struct x8664_pda, pda);/* We can use this directly for local CPU (faster). */ DECLARE_PER_CPU(unsigned long, this_cpu_off); +#endif /* __ASSEMBLY__ */ +#endif /* !CONFIG_X86_64 */ + +#ifndef __ASSEMBLY__ +/* For arch-specific code, we can use direct single-insn ops (they * don't give an lvalue though). */ extern void __bad_percpu_size (void );
@@ -142,5 +142,4 @@ do { \#define x86_sub_percpu (var, val) percpu_to_op("sub", per_cpu__##var, val)#define x86_or_percpu (var, val) percpu_to_op("or", per_cpu__##var, val)#endif /* !__ASSEMBLY__ */ -#endif /* !CONFIG_X86_64 */ #endif /* _ASM_X86_PERCPU_H_ */
arch/x86/kernel/smpboot.c +0 -16
@@ -855,22 +855,6 @@ static int __cpuinit do_boot_cpu(int apiprintk (KERN_ERR "Failed to allocate GDT for CPU %d\n" , cpu); return -1; } - -/* Allocate node local memory for AP pdas */ -if (cpu_pda (cpu) == &boot_cpu_pda[cpu]) { -struct x8664_pda *newpda, *pda; -int node =cpu_to_node (cpu); - pda =cpu_pda (cpu); - newpda =kmalloc_node (sizeof (struct x8664_pda ),GFP_ATOMIC , - node); -if (newpda) { -memcpy (newpda, pda,sizeof (struct x8664_pda )); -cpu_pda (cpu) = newpda; - }else -printk (KERN_ERR -"Could not allocate node local PDA for CPU %d on node %d\n" , - cpu, node); - }#endif alternatives_smp_switch (1 );
PATCH 38/41 x86: Extend percpu ops to 64 bit
In reply to Christoph Lameterx86 percpu ops now will work on 64 bit too. So add the missing 8 byte cases. Also add a number of atomic ops that will be useful in the future:x86_xchg_percpu() and x86_cmpxchg_percpu().
Add x86_inc_percpu and x86_dec_percpu. Increment by one can generate more efficient instructions and inc/dec will be supported by cpu ops later.
Also use per_cpu_var() instead of per_cpu__##xxx.
--- include/asm-x86/percpu.h | 83 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-)
include/asm-x86/percpu.h +78 -5
@@ -108,6 +108,11 @@ do { \ : "+m" (var) \ : "ri" ((T__)val)); \break ; \ +case 8 : \ + asm(op"q %1," __percpu_seg"%0" \ + :"+m" (var) \ + :"ri" ((T__ )val)); \ +break ; \default :__bad_percpu_size (); \ } \ }while (0 )
@@ -131,15 +136,83 @@ do { \ : "=r" (ret__) \ : "m" (var)); \break ; \ +case 8 : \ + asm(op"q " __percpu_seg"%1,%0" \ + :"=r" (ret__) \ + :"m" (var)); \ +break ; \default :__bad_percpu_size (); \ } \ ret__; \ }) -#define x86_read_percpu (var) percpu_from_op("mov", per_cpu__##var) -#define x86_write_percpu (var, val) percpu_to_op("mov", per_cpu__##var, val) -#define x86_add_percpu (var, val) percpu_to_op("add", per_cpu__##var, val) -#define x86_sub_percpu (var, val) percpu_to_op("sub", per_cpu__##var, val) -#define x86_or_percpu (var, val) percpu_to_op("or", per_cpu__##var, val) +#define percpu_addr_op (op, var) \ +({ \ + switch (sizeof(var)) { \ + case 1: \ + asm(op "b "__percpu_seg"%0" \ + : : "m"(var)); \ + break; \ + case 2: \ + asm(op "w "__percpu_seg"%0" \ + : : "m"(var)); \ + break; \ + case 4: \ + asm(op "l "__percpu_seg"%0" \ + : : "m"(var)); \ + break; \ + case 8: \ + asm(op "q "__percpu_seg"%0" \ + : : "m"(var)); \ + break; \ + default: __bad_percpu_size(); \ + } \ +}) + +#define percpu_cmpxchg_op (var, old, new) \ +({ \ + typeof(var) prev; \ + switch (sizeof(var)) { \ + case 1: \ + asm("cmpxchgb %b1, "__percpu_seg"%2" \ + : "=a"(prev) \ + : "q"(new), "m"(var), "0"(old) \ + : "memory"); \ + break; \ + case 2: \ + asm("cmpxchgw %w1, "__percpu_seg"%2" \ + : "=a"(prev) \ + : "r"(new), "m"(var), "0"(old) \ + : "memory"); \ + break; \ + case 4: \ + asm("cmpxchgl %k1, "__percpu_seg"%2" \ + : "=a"(prev) \ + : "r"(new), "m"(var), "0"(old) \ + : "memory"); \ + break; \ + case 8: \ + asm("cmpxchgq %1, "__percpu_seg"%2" \ + : "=a"(prev) \ + : "r"(new), "m"(var), "0"(old) \ + : "memory"); \ + break; \ + default: \ + __bad_percpu_size(); \ + } \ + return prev; \ +}) + +#define x86_read_percpu (var) percpu_from_op("mov", per_cpu_var(var)) +#define x86_write_percpu (var, val) percpu_to_op("mov", per_cpu_var(var), val) +#define x86_add_percpu (var, val) percpu_to_op("add", per_cpu_var(var), val) +#define x86_sub_percpu (var, val) percpu_to_op("sub", per_cpu_var(var), val) +#define x86_inc_percpu (var) percpu_addr_op("inc", per_cpu_var(var)) +#define x86_dec_percpu (var) percpu_addr_op("dec", per_cpu_var(var)) +#define x86_or_percpu (var, val) percpu_to_op("or", per_cpu_var(var), val) +#define x86_xchg_percpu (var, val) percpu_to_op("xchg", per_cpu_var(var), val) +#define x86_cmpxchg_percpu (var, old, new) \ + percpu_cmpxchg_op(per_cpu_var(var), old, new) +#endif /* !__ASSEMBLY__ */ #endif /* _ASM_X86_PERCPU_H_ */
PATCH 39/41 x86: Replace cpu_pda() using percpu logic and get rid of _cpu_pda()
In reply to Christoph Lameter_cpu_pda() is pointing to the pda which is at the beginning of the per cpu area. This means that cpu_pda and _cpu_pda[] are both pointing at the percpu area! per_cpu() can be used instead of cpu_pda() when accessing pda fields.
Typically the offsets to the per cpu areas are stored in an array _per_cpu_offset (generic per cpu support can then provide more functionality).
Use that array for x86_64 and get rid of the pda pointers.
--- arch/x86/kernel/head64.c | 7 ++++++- arch/x86/kernel/irq_64.c | 16 ++++++++-------- arch/x86/kernel/nmi_64.c | 6 +++--- arch/x86/kernel/setup.c | 15 ++++----------- arch/x86/kernel/setup64.c | 6 +----- arch/x86/kernel/smpboot.c | 2 +- arch/x86/kernel/traps_64.c | 9 +++++---- include/asm-x86/pda.h | 4 ---- include/asm-x86/percpu.h | 32 +++++++------------------------- 9 files changed, 35 insertions(+), 62 deletions(-)
arch/x86/kernel/head64.c +6 -1
@@ -119,8 +119,13 @@ static void __init reserve_setup_data(vo } } +static struct x8664_pda boot_cpu_pda[NR_CPUS] __cacheline_aligned; +void __init x86_64_start_kernel (char * real_mode_data) { +#ifndef CONFIG_SMP +unsigned long __per_cpu_offset[1 ]; +#endif int i;/*
@@ -157,7 +162,7 @@ void __init x86_64_start_kernel(char * rearly_printk ("Kernel alive\n" );for (i =0 ; i <NR_CPUS ; i++) -cpu_pda (i) = &boot_cpu_pda[i]; + __per_cpu_offset[i] = (unsigned long )&boot_cpu_pda[i];pda_init (0 );copy_bootdata (__va (real_mode_data));
arch/x86/kernel/irq_64.c +8 -8
@@ -115,37 +115,37 @@ skip: }else if (i ==NR_IRQS ) {seq_printf (p,"NMI: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->__nmi_count); +seq_printf (p,"%10u " ,per_cpu (pda.__nmi_count, j));seq_printf (p," Non-maskable interrupts\n" );seq_printf (p,"LOC: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->apic_timer_irqs); +seq_printf (p,"%10u " ,per_cpu (pda.apic_timer_irqs, j));seq_printf (p," Local timer interrupts\n" );#ifdef CONFIG_SMP seq_printf (p,"RES: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_resched_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_resched_count, j));seq_printf (p," Rescheduling interrupts\n" );seq_printf (p,"CAL: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_call_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_call_count, j));seq_printf (p," function call interrupts\n" );seq_printf (p,"TLB: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_tlb_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_tlb_count, j));seq_printf (p," TLB shootdowns\n" );#endif seq_printf (p,"TRM: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_thermal_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_thermal_count, j));seq_printf (p," Thermal event interrupts\n" );seq_printf (p,"THR: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_threshold_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_threshold_count, j));seq_printf (p," Threshold APIC interrupts\n" );seq_printf (p,"SPU: " );for_each_online_cpu (j) -seq_printf (p,"%10u " ,cpu_pda (j)->irq_spurious_count); +seq_printf (p,"%10u " ,per_cpu (pda.irq_spurious_count, j));seq_printf (p," Spurious interrupts\n" );seq_printf (p,"ERR: %10u\n" ,atomic_read (&irq_err_count)); }
arch/x86/kernel/nmi_64.c +3 -3
@@ -100,19 +100,19 @@ int __init check_nmi_watchdog(void)#endif for (cpu =0 ; cpu <NR_CPUS ; cpu++) - prev_nmi_count[cpu] =cpu_pda (cpu)->__nmi_count; + prev_nmi_count[cpu] =per_cpu (pda.__nmi_count, cpu);local_irq_enable ();mdelay ((20 *1000 )/nmi_hz);// wait 20 ticks for_each_online_cpu (cpu) {if (!per_cpu (wd_enabled, cpu))continue ; -if (cpu_pda (cpu)->__nmi_count - prev_nmi_count[cpu] <=5 ) { +if (per_cpu (pda.__nmi_count, cpu) - prev_nmi_count[cpu] <=5 ) {printk (KERN_WARNING "WARNING: CPU#%d: NMI " "appears to be stuck (%d->%d)!\n" , cpu, prev_nmi_count[cpu], -cpu_pda (cpu)->__nmi_count); +per_cpu (pda.__nmi_count, cpu));per_cpu (wd_enabled, cpu) =0 ;atomic_dec (&nmi_active); }
arch/x86/kernel/setup.c +4 -11
@@ -77,14 +77,8 @@ static void __init setup_cpumask_of_cpu(static inline void setup_cpumask_of_cpu (void ) { }#endif -#ifdef CONFIG_X86_32 -/* -* Great future not-so-futuristic plan: make i386 and x86_64 do it -* the same way -*/ unsigned long __per_cpu_offset[NR_CPUS ]__read_mostly ;EXPORT_SYMBOL (__per_cpu_offset); -#endif /* * Great future plan:
@@ -128,12 +122,11 @@ void __init setup_per_cpu_areas(void)* So far an embryonic per cpu area was used containing only * the pda. Move the pda contents into the full per cpu area. */ -cpu_pda (i)->data_offset = ptr - __per_cpu_start; -memcpy (ptr,cpu_pda (i),sizeof (struct x8664_pda )); -cpu_pda (i) = (struct x8664_pda *)ptr; -#else - __per_cpu_offset[i] = ptr - __per_cpu_start; +per_cpu (pda.data_offset, i) = ptr - __per_cpu_start; +memcpy (ptr, &per_cpu (pda, i),sizeof (struct x8664_pda ));#endif + __per_cpu_offset[i] = ptr - __per_cpu_start; + highest_cpu = i; }
arch/x86/kernel/setup64.c +1 -5
@@ -34,10 +34,6 @@ struct boot_params boot_params;cpumask_t cpu_initialized__cpuinitdata =CPU_MASK_NONE ; -struct x8664_pda *_cpu_pda[NR_CPUS ]__read_mostly ; -EXPORT_SYMBOL (_cpu_pda); -struct x8664_pda boot_cpu_pda[NR_CPUS] __cacheline_aligned; -struct desc_ptr idt_descr = {256 *16 -1 , (unsigned long ) idt_table }; char boot_cpu_stack[IRQSTACKSIZE] __attribute__((section(".bss.page_aligned")));
@@ -89,7 +85,7 @@ __setup("noexec32=", nonx32_setup);void pda_init (int cpu) { -struct x8664_pda *pda =cpu_pda (cpu); +struct x8664_pda *pda = &per_cpu (pda, cpu);/* Setup up data that may be needed in __get_free_pages early */ asmvolatile ("movl %0,%%fs ; movl %0,%%gs" ::"r" (0 ));
arch/x86/kernel/smpboot.c +1 -1
@@ -895,7 +895,7 @@ do_rest: stack_start.sp = (void *) c_idle.idle->thread.sp;irq_ctx_init (cpu);#else -cpu_pda (cpu)->pcurrent = c_idle.idle; +per_cpu (pda.pcurrent, cpu) = c_idle.idle; init_rsp = c_idle.idle->thread.sp;load_sp0 (&per_cpu (init_tss, cpu), &c_idle.idle->thread); initial_code = (unsigned long )start_secondary;
arch/x86/kernel/traps_64.c +5 -4
@@ -263,7 +263,8 @@ void dump_trace(struct task_struct *tsk, const struct stacktrace_ops *ops, void *data) { const unsigned cpu = get_cpu(); -unsigned long *irqstack_end = (unsigned long *)cpu_pda (cpu)->irqstackptr; +unsigned long *irqstack_end = + (unsigned long *)per_cpu (pda.irqstackptr, cpu);unsigned used =0 ;struct thread_info *tinfo;
@@ -397,8 +398,8 @@ _show_stack(struct task_struct *tsk, strunsigned long *stack;int i;const int cpu =smp_processor_id (); -unsigned long *irqstack_end = (unsigned long *) (cpu_pda (cpu)->irqstackptr); -unsigned long *irqstack = (unsigned long *) (cpu_pda (cpu)->irqstackptr -IRQSTACKSIZE ); +unsigned long *irqstack_end = (unsigned long *)per_cpu (pda.irqstackptr, cpu); +unsigned long *irqstack = (unsigned long *)(per_cpu (pda.irqstackptr, cpu) -IRQSTACKSIZE );// debugging aid: "show_stack(NULL, NULL);" prints the // back trace for this cpu.
@@ -462,7 +463,7 @@ void show_registers(struct pt_regs *regsint i;unsigned long sp;const int cpu =smp_processor_id (); -struct task_struct *cur =cpu_pda (cpu)->pcurrent; +struct task_struct *cur =per_cpu (pda.pcurrent, cpu);u8 *ip;unsigned int code_prologue = code_bytes *43 /64 ;unsigned int code_len = code_bytes;
include/asm-x86/pda.h +0 -4
@@ -37,12 +37,8 @@ struct x8664_pda {unsigned irq_spurious_count; }____cacheline_aligned_in_smp ; -extern struct x8664_pda *_cpu_pda[]; -extern struct x8664_pda boot_cpu_pda[];extern void pda_init (int ); -#define cpu_pda (i) (_cpu_pda[i]) -/* * There is no fast way to get the base address of the PDA, all the accesses * have to mention %fs/%gs. So it needs to be done this Torvaldian way.
include/asm-x86/percpu.h +7 -25
@@ -6,12 +6,12 @@#include <asm/pda.h> #ifdef CONFIG_SMP -#define __per_cpu_offset (cpu) (cpu_pda(cpu)->data_offset) -#define __my_cpu_offset read_pda(data_offset) -#define per_cpu_offset (x) (__per_cpu_offset(x)) -#endif - +#define __my_cpu_offset x86_read_percpu(pda.data_offset)#define __percpu_seg "%%gs:" +#else +#define __percpu_seg "" + +#endif #include <asm-generic/percpu.h>
@@ -46,30 +46,12 @@ DECLARE_PER_CPU(struct x8664_pda, pda);#else /* ...!ASSEMBLY */ -/* -* PER_CPU finds an address of a per-cpu variable. -* -* Args: -* var - variable name -* cpu - 32bit register containing the current CPU number -* -* The resulting address is stored in the "cpu" argument. -* -* Example: -* PER_CPU(cpu_gdt_descr, %ebx) -*/ #ifdef CONFIG_SMP -#define __my_cpu_offset x86_read_percpu(this_cpu_off) - -/* fs segment starts at (positive) offset == __per_cpu_offset[cpu] */ #define __percpu_seg "%%fs:" - -#else /* !SMP */ - +#else #define __percpu_seg "" - -#endif /* SMP */ +#endif #include <asm-generic/percpu.h>
PATCH 40/41 x86: Replace xxx_pda() operations with x86_xx_percpu().
In reply to Christoph LameterIt is now possible to use percpu operations for pda access since the pda is in the percpu area. Drop the pda operations.
--- arch/x86/kernel/apic_64.c | 4 - arch/x86/kernel/cpu/mcheck/mce_amd_64.c | 2 arch/x86/kernel/cpu/mcheck/mce_intel_64.c | 2 arch/x86/kernel/nmi_64.c | 5 + arch/x86/kernel/process_64.c | 16 ++--- arch/x86/kernel/smp.c | 4 - arch/x86/kernel/time_64.c | 2 arch/x86/kernel/tlb_64.c | 12 ++-- arch/x86/kernel/x8664_ksyms_64.c | 2 include/asm-x86/current_64.h | 4 - include/asm-x86/hardirq_64.h | 6 +- include/asm-x86/mmu_context_64.h | 12 ++-- include/asm-x86/pda.h | 86 ------------------------------ include/asm-x86/smp.h | 2 include/asm-x86/thread_info_64.h | 2 15 files changed, 37 insertions(+), 124 deletions(-)
arch/x86/kernel/apic_64.c +2 -2
@@ -481,7 +481,7 @@ static void local_apic_timer_interrupt(v/* * the NMI deadlock-detector uses this. */ -add_pda (apic_timer_irqs,1 ); +x86_inc_percpu (pda.apic_timer_irqs); evt->event_handler (evt); }
@@ -986,7 +986,7 @@ asmlinkage void smp_spurious_interrupt(vif (v & (1 << (SPURIOUS_APIC_VECTOR &0x1f )))ack_APIC_irq (); -add_pda (irq_spurious_count,1 ); +x86_inc_percpu (pda.irq_spurious_count);irq_exit (); }
arch/x86/kernel/cpu/mcheck/mce_amd_64.c +1 -1
@@ -237,7 +237,7 @@ asmlinkage void mce_threshold_interrupt( } } out: -add_pda (irq_threshold_count,1 ); +x86_inc_percpu (pda.irq_threshold_count);irq_exit (); }
arch/x86/kernel/cpu/mcheck/mce_intel_64.c +1 -1
@@ -26,7 +26,7 @@ asmlinkage void smp_thermal_interrupt(voif (therm_throt_process (msr_val &1 ))mce_log_therm_throt_event (smp_processor_id (), msr_val); -add_pda (irq_thermal_count,1 ); +x86_inc_percpu (pda.irq_thermal_count);irq_exit (); }
arch/x86/kernel/nmi_64.c +3 -2
@@ -328,7 +328,8 @@ nmi_watchdog_tick(struct pt_regs *regs, touched =1 ; } - sum =read_pda (apic_timer_irqs) +read_pda (irq0_irqs); + sum =x86_read_percpu (pda.apic_timer_irqs) + +x86_read_percpu (pda.irq0_irqs);if (__get_cpu_var (nmi_touch)) {__get_cpu_var (nmi_touch) =0 ; touched =1 ;
@@ -389,7 +390,7 @@ asmlinkage notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code) {nmi_enter (); -add_pda (__nmi_count,1 ); +x86_inc_percpu (pda.__nmi_count);if (!ignore_nmis)default_do_nmi (regs);nmi_exit ();
arch/x86/kernel/process_64.c +8 -8
@@ -74,13 +74,13 @@ void idle_notifier_register(struct notifvoid enter_idle (void ) { -write_pda (isidle,1 ); +x86_write_percpu (pda.isidle,1 );atomic_notifier_call_chain (&idle_notifier,IDLE_START ,NULL ); }static void __exit_idle (void ) { -if (test_and_clear_bit_pda (0 , isidle) ==0 ) +if (test_and_clear_bit (0 , &per_cpu (pda.isidle,smp_processor_id ())) ==0 )return ;atomic_notifier_call_chain (&idle_notifier,IDLE_END ,NULL ); }
@@ -411,7 +411,7 @@ start_thread(struct pt_regs *regs, unsigload_gs_index (0 ); regs->ip = new_ip; regs->sp = new_sp; -write_pda (oldrsp, new_sp); +x86_write_percpu (pda.oldrsp, new_sp); regs->cs = __USER_CS; regs->ss = __USER_DS; regs->flags =0x200 ;
@@ -633,14 +633,14 @@ __switch_to(struct task_struct *prev_p,/* * Switch the PDA and FPU contexts. */ - prev->usersp =read_pda (oldrsp); -write_pda (oldrsp, next->usersp); -write_pda (pcurrent, next_p); + prev->usersp =x86_read_percpu (pda.oldrsp); +x86_write_percpu (pda.oldrsp, next->usersp); +x86_write_percpu (pda.pcurrent, next_p); -write_pda (kernelstack, +x86_write_percpu (pda.kernelstack, (unsigned long )task_stack_page (next_p) +THREAD_SIZE -PDA_STACKOFFSET );#ifdef CONFIG_CC_STACKPROTECTOR -write_pda (stack_canary, next_p->stack_canary); +x86_write_percpu (pda.stack_canary, next_p->stack_canary);/* * Build time only check to make sure the stack_canary is at * offset 40 in the pda; this is a gcc ABI requirement
arch/x86/kernel/smp.c +2 -2
@@ -295,7 +295,7 @@ void smp_reschedule_interrupt(struct pt_#ifdef CONFIG_X86_32 __get_cpu_var (irq_stat).irq_resched_count++;#else -add_pda (irq_resched_count,1 ); +x86_inc_percpu (pda.irq_resched_count);#endif }
@@ -320,7 +320,7 @@ void smp_call_function_interrupt(struct#ifdef CONFIG_X86_32 __get_cpu_var (irq_stat).irq_call_count++;#else -add_pda (irq_call_count,1 ); +x86_inc_percpu (pda.irq_call_count);#endif irq_exit ();
arch/x86/kernel/time_64.c +1 -1
@@ -46,7 +46,7 @@ EXPORT_SYMBOL(profile_pc);static irqreturn_t timer_event_interrupt (int irq,void *dev_id) { -add_pda (irq0_irqs,1 ); +x86_inc_percpu (pda.irq0_irqs); global_clock_event->event_handler (global_clock_event);
arch/x86/kernel/tlb_64.c +6 -6
@@ -60,9 +60,9 @@ static DEFINE_PER_CPU(union smp_flush_st*/ void leave_mm (int cpu) { -if (read_pda (mmu_state) ==TLBSTATE_OK ) +if (x86_read_percpu (pda.mmu_state) ==TLBSTATE_OK )BUG (); -cpu_clear (cpu,read_pda (active_mm)->cpu_vm_mask); +cpu_clear (cpu,x86_read_percpu (pda.active_mm)->cpu_vm_mask);load_cr3 (swapper_pg_dir); }EXPORT_SYMBOL_GPL (leave_mm);
@@ -140,8 +140,8 @@ asmlinkage void smp_invalidate_interrupt* BUG(); */ -if (f->flush_mm ==read_pda (active_mm)) { -if (read_pda (mmu_state) ==TLBSTATE_OK ) { +if (f->flush_mm ==x86_read_percpu (pda.active_mm)) { +if (x86_read_percpu (pda.mmu_state) ==TLBSTATE_OK ) {if (f->flush_va ==TLB_FLUSH_ALL )local_flush_tlb ();else
@@ -152,7 +152,7 @@ asmlinkage void smp_invalidate_interrupt out:ack_APIC_irq ();cpu_clear (cpu, f->flush_cpumask); -add_pda (irq_tlb_count,1 ); +x86_inc_percpu (pda.irq_tlb_count); } void native_flush_tlb_others(const cpumask_t *cpumaskp, struct mm_struct *mm,
@@ -264,7 +264,7 @@ static void do_flush_tlb_all(void *info)unsigned long cpu =smp_processor_id ();__flush_tlb_all (); -if (read_pda (mmu_state) ==TLBSTATE_LAZY ) +if (x86_read_percpu (pda.mmu_state) ==TLBSTATE_LAZY )leave_mm (cpu); }
include/asm-x86/pda.h +0 -86
92 lines not on this page · raw message
arch/x86/kernel/x8664_ksyms_64.c +0 -2
@@ -52,8 +52,6 @@ EXPORT_SYMBOL(empty_zero_page);EXPORT_SYMBOL (init_level4_pgt);EXPORT_SYMBOL (load_gs_index); -EXPORT_SYMBOL (_proxy_pda); -#ifdef CONFIG_PARAVIRT /* Virtualized guests may want to use it */ EXPORT_SYMBOL_GPL (cpu_gdt_descr);
include/asm-x86/current_64.h +2 -2
@@ -5,11 +5,11 @@struct task_struct ;#include <asm/pda.h> +#include <asm/percpu.h> static inline struct task_struct *get_current (void ) { -struct task_struct *t =read_pda (pcurrent); -return t; +return x86_read_percpu (pda.pcurrent); }#define current get_current()
include/asm-x86/hardirq_64.h +3 -3
@@ -11,12 +11,12 @@#define __ARCH_IRQ_STAT 1 -#define local_softirq_pending () read_pda(__softirq_pending) +#define local_softirq_pending () x86_read_percpu(pda.__softirq_pending)#define __ARCH_SET_SOFTIRQ_PENDING 1 -#define set_softirq_pending (x) write_pda(__softirq_pending, (x)) -#define or_softirq_pending (x) or_pda(__softirq_pending, (x)) +#define set_softirq_pending (x) x86_write_percpu(pda.__softirq_pending, (x)) +#define or_softirq_pending (x) x86_or_percpu(pda.__softirq_pending, (x))extern void ack_bad_irq (unsigned int irq);
include/asm-x86/mmu_context_64.h +6 -6
@@ -20,8 +20,8 @@ void destroy_context(struct mm_struct *mstatic inline void enter_lazy_tlb (struct mm_struct *mm,struct task_struct *tsk) {#ifdef CONFIG_SMP -if (read_pda (mmu_state) ==TLBSTATE_OK ) -write_pda (mmu_state,TLBSTATE_LAZY ); +if (x86_read_percpu (pda.mmu_state) ==TLBSTATE_OK ) +x86_write_percpu (pda.mmu_state,TLBSTATE_LAZY );#endif }
@@ -33,8 +33,8 @@ static inline void switch_mm(struct mm_s/* stop flush ipis for the previous mm */ cpu_clear (cpu, prev->cpu_vm_mask);#ifdef CONFIG_SMP -write_pda (mmu_state,TLBSTATE_OK ); -write_pda (active_mm, next); +x86_write_percpu (pda.mmu_state,TLBSTATE_OK ); +x86_write_percpu (pda.active_mm, next);#endif cpu_set (cpu, next->cpu_vm_mask);load_cr3 (next->pgd);
@@ -44,8 +44,8 @@ static inline void switch_mm(struct mm_s }#ifdef CONFIG_SMP else { -write_pda (mmu_state,TLBSTATE_OK ); -if (read_pda (active_mm) != next) +x86_write_percpu (pda.mmu_state,TLBSTATE_OK ); +if (x86_read_percpu (pda.active_mm) != next)BUG ();if (!cpu_test_and_set (cpu, next->cpu_vm_mask)) {/* We were in lazy tlb mode and leave_mm disabled
include/asm-x86/smp.h +1 -1
@@ -143,7 +143,7 @@ DECLARE_PER_CPU(int, cpu_number);extern int safe_smp_processor_id (void );#elif defined(CONFIG_X86_64_SMP ) -#define raw_smp_processor_id () read_pda(cpunumber) +#define raw_smp_processor_id () x86_read_percpu(pda.cpunumber)#define stack_smp_processor_id () \ ({ \
include/asm-x86/thread_info_64.h +1 -1
@@ -63,7 +63,7 @@ struct thread_info {static inline struct thread_info *current_thread_info (void ) {struct thread_info *ti; - ti = (void *)(read_pda (kernelstack) +PDA_STACKOFFSET -THREAD_SIZE ); + ti = (void *)(x86_read_percpu (pda.kernelstack) +PDA_STACKOFFSET -THREAD_SIZE );return ti; }
PATCH 41/41 x86_64: Support for cpu ops
In reply to Christoph LameterSupport fast cpu ops in x86_64 by providing a series of functions that generate the proper instructions.
Define CONFIG_HAVE_CPU_OPS so that core code can exploit the availability of fast per cpu operations.
--- arch/x86/Kconfig | 4 ++++ include/asm-x86/percpu.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+)
arch/x86/Kconfig +4 -0
@@ -168,6 +168,10 @@ config GENERIC_PENDING_IRQ depends on GENERIC_HARDIRQS && SMP default y +config HAVE_CPU_OPS + bool + default y + config X86_SMP bool depends on SMP && ((X86_32 && !X86_VOYAGER) || X86_64)
include/asm-x86/percpu.h +31 -0
@@ -196,5 +196,36 @@ do { \#define x86_cmpxchg_percpu (var, old, new) \ percpu_cmpxchg_op(per_cpu_var(var), old, new) +#define CPU_READ (obj) percpu_from_op("mov", obj) +#define CPU_WRITE (obj, val) percpu_to_op("mov", obj, val) +#define CPU_ADD (obj, val) percpu_to_op("add", obj, val) +#define CPU_SUB (obj, val) percpu_to_op("sub", obj, val) +#define CPU_INC (obj) percpu_addr_op("inc", obj) +#define CPU_DEC (obj) percpu_addr_op("dec", obj) +#define CPU_XCHG (obj, val) percpu_to_op("xchg", var, val) +#define CPU_CMPXCHG (obj, old, new) percpu_cmpxchg_op(var, old, new) + +/* +* All cpu operations are interrupt safe and do not need to disable +* preempt. So the other variants all reduce to the same instruction. +*/ +#define _CPU_READ CPU_READ +#define _CPU_WRITE CPU_WRITE +#define _CPU_ADD CPU_ADD +#define _CPU_SUB CPU_SUB +#define _CPU_INC CPU_INC +#define _CPU_DEC CPU_DEC +#define _CPU_XCHG CPU_XCHG +#define _CPU_CMPXCHG CPU_CMPXCHG + +#define __CPU_READ CPU_READ +#define __CPU_WRITE CPU_WRITE +#define __CPU_ADD CPU_ADD +#define __CPU_SUB CPU_SUB +#define __CPU_INC CPU_INC +#define __CPU_DEC CPU_DEC +#define __CPU_XCHG CPU_XCHG +#define __CPU_CMPXCHG CPU_CMPXCHG +#endif /* !__ASSEMBLY__ */ #endif /* _ASM_X86_PERCPU_H_ */
Andrew Morton
maintainerQuoted 6 lines · On Thu, 29 May 2008 20:56:20 -0700 Christoph Lameter <[email protected]> wrote:
In various places the kernel maintains arrays of pointers indexed by processor numbers. These are used to locate objects that need to be used when executing on a specirfic processor. Both the slab allocator and the page allocator use these arrays and there the arrays are used in performance critical code. The allocpercpu functionality is a simple allocator to provide these arrays.
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
I wonder if we can arrange for the default to be overridden via a kernel boot option?
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
Quoted 2 lines · Andrew Morton
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
The per cpu memory use by subsystems is typically quite small. We already have an 8k limitation for percpu space for modules. And that does not seem to be a problem.
Quoted 4 lines
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
Typically these are fairly small 8 bytes * 5000 is only 20k.
Quoted 2 lines
I wonder if we can arrange for the default to be overridden via a kernel boot option?
We could do that yes.
Quoted 2 lines
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
But then per cpu data is not frequently allocated and freed.
Going away from allocpercpu saves a lot of memory. We could make this 128k or so to be safe?
Andrew Morton
maintainerQuoted 8 lines · On Thu, 29 May 2008 22:03:14 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 2 lines · Andrew Morton
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
The per cpu memory use by subsystems is typically quite small. We already have an 8k limitation for percpu space for modules. And that does not seem to be a problem.
eh? That's DEFINE_PERCPU memory, not alloc_pecpu() memory?
Quoted 6 lines
Quoted 4 lines
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
Typically these are fairly small 8 bytes * 5000 is only 20k.
It was just an example. There will be others.
tcp_v4_md5_do_add ->tcp_alloc_md5sig_pool ->__tcp_alloc_md5sig_pool
does an alloc_percpu for each md5-capable TCP connection. I think - it doesn't matter really, because something _could_. And if something _does_, we're screwed.
Quoted 4 lines
Quoted 2 lines
I wonder if we can arrange for the default to be overridden via a kernel boot option?
We could do that yes.
Phew.
Quoted 4 lines
Quoted 2 lines
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
But then per cpu data is not frequently allocated and freed.
I think it is, in the TCP case. And that's the only one I looked at.
Plus who knows what lies ahead of us?
Quoted 2 lines
Going away from allocpercpu saves a lot of memory. We could make this 128k or so to be safe?
("alloc_percpu" - please be careful about getting this stuff right)
I don't think there is presently any upper limit on alloc_percpu()? It uses kmalloc() and kmalloc_node()?
Even if there is some limit, is it an unfixable one?
Quoted 5 lines · Andrew Morton
Quoted 3 lines
The per cpu memory use by subsystems is typically quite small. We already have an 8k limitation for percpu space for modules. And that does not seem to be a problem.
eh? That's
DEFINE_PERCPUmemory, notalloc_pecpu()memory?
No. The module subsystem has its own alloc_percpu subsystem that the cpu_alloc replaces.
Quoted 3 lines
Quoted 1 line
We could do that yes.
Phew.
But its going to be even more complicated and I have a hard time managing the complexity here. Could someone take pieces off my hand?
Quoted 3 lines
Quoted 1 line
But then per cpu data is not frequently allocated and freed.
I think it is, in the TCP case. And that's the only one I looked at.
Which tcp case?
Quoted 1 line
Plus who knows what lies ahead of us?
Well invariably we will end up with cpu area defragmentation.... Sigh.
Quoted 4 lines
I don't think there is presently any upper limit on
alloc_percpu()? It useskmalloc()andkmalloc_node()?Even if there is some limit, is it an unfixable one?
No there is no limit. It just wastes lots of space (pointer arrays, alignment etc) that we could use to configure sufficiently large per cpu areas.
Andrew Morton
maintainerQuoted 10 lines · On Thu, 29 May 2008 22:27:53 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 5 lines · Andrew Morton
Quoted 3 lines
The per cpu memory use by subsystems is typically quite small. We already have an 8k limitation for percpu space for modules. And that does not seem to be a problem.
eh? That's
DEFINE_PERCPUmemory, notalloc_pecpu()memory?No. The module subsystem has its own
alloc_percpusubsystem that thecpu_allocreplaces.
That is to support DEFINE_PER_CPU, not alloc_percpu().
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
We could do that yes.
Phew.
But its going to be even more complicated and I have a hard time managing the complexity here. Could someone take pieces off my hand?
It could be done later on.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
But then per cpu data is not frequently allocated and freed.
I think it is, in the TCP case. And that's the only one I looked at.
Which tcp case?
The one you just deleted from my reply :(
Quoted 4 lines
Quoted 1 line
Plus who knows what lies ahead of us?
Well invariably we will end up with cpu area defragmentation.... Sigh.
Quoted 8 lines
Quoted 4 lines
I don't think there is presently any upper limit on
alloc_percpu()? It useskmalloc()andkmalloc_node()?Even if there is some limit, is it an unfixable one?
No there is no limit. It just wastes lots of space (pointer arrays, alignment etc) that we could use to configure sufficiently large per cpu areas.
Christoph, please. An allocator which is of fixed size and which is vulnerable to internal fragmentation is a huge problem! The kernel is subject to wildly varying workloads both between different users and in the hands of a single user.
If we were to merge all this code and then run into the problems which I fear then we are tremendously screwed. We must examine this exhaustively, in the most paranoid fashion.
Quoted 4 lines · Andrew Morton
Quoted 2 lines
No. The module subsystem has its own
alloc_percpusubsystem that thecpu_allocreplaces.That is to support
DEFINE_PER_CPU, notalloc_percpu().
Right but it needs to have its own section of the percpu space from which it allocates the percpu segments for the modules. So it effectively implements an allocator.
Quoted 3 lines
If we were to merge all this code and then run into the problems which I fear then we are tremendously screwed. We must examine this exhaustively, in the most paranoid fashion.
Well V2 virtually mapped the cpu alloc area which allowed extending it arbitrarily. But that made things very complicated.
The number of per cpu resources needed is mostly fixed. The number of zones, nodes, slab caches, network interfaces etc etc does not change much during typical operations.
Quoted 11 lines · On Thu, 29 May 2008 23:16:11 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 4 lines · Andrew Morton
Quoted 2 lines
No. The module subsystem has its own
alloc_percpusubsystem that thecpu_allocreplaces.That is to support
DEFINE_PER_CPU, notalloc_percpu().Right but it needs to have its own section of the percpu space from which it allocates the percpu segments for the modules. So it effectively implements an allocator.
Could you add a text to explain "This interface is for wise use of pre-allocated limited area (see Documentation/xxxx). please use this only when you need very fast access to per-cpu object and you can estimate the amount which you finally need. If unsure, please use generic allocator."
for the moment ?
At first look, I thought of using this in memory-resource-controller but it seems I shouldn't do so because thousands of cgroup can be used in theory...
Thanks,
-Kame
Quoted 1 line · Christoph Lameter
On Thu, 29 May 2008, Andrew Morton wrote:
...
Quoted 4 lines
Quoted 1 line
Plus who knows what lies ahead of us?
Well invariably we will end up with cpu area defragmentation.... Sigh.
Quoted 8 lines
Quoted 4 lines
I don't think there is presently any upper limit on
alloc_percpu()? It useskmalloc()andkmalloc_node()?Even if there is some limit, is it an unfixable one?
No there is no limit. It just wastes lots of space (pointer arrays, alignment etc) that we could use to configure sufficiently large per cpu areas.
Is there any reason why the per_cpu area couldn't be made extensible? Maybe a simple linked list of available areas? (And use a config variable and/or boot param for initial size and increment size?) [Ignoring the problem of reclaiming the space...]
Quoted 8 lines · Mike Travis
Quoted 3 lines
No there is no limit. It just wastes lots of space (pointer arrays, alignment etc) that we could use to configure sufficiently large per cpu areas.
Is there any reason why the
per_cpuarea couldn't be made extensible? Maybe a simple linked list of available areas? (And use a config variable and/or boot param for initial size and increment size?) [Ignoring the problem of reclaiming the space...]
cpu alloc v2 had an extendable per cpu space. You have the patches. We could put this on top of this patchset if necessary. But then it not so nice and simple anymore. Maybe we can rstrict the use of cpu alloc instead to users with objects < cache_line_size() or so?
Quoted 4 lines · Christoph Lameter
cpu alloc v2 had an extendable per cpu space. You have the patches. We could put this on top of this patchset if necessary. But then it not so nice and simple anymore. Maybe we can rstrict the use of cpu alloc instead to users with objects <
cache_line_size()or so?
Restricting the use of cpu_alloc based on size of object is no good when you're trying to allocate 45,000 objects. Extending the per CPU space is the only option.
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Quoted 9 lines · Matthew Wilcox
Quoted 4 lines · Christoph Lameter
cpu alloc v2 had an extendable per cpu space. You have the patches. We could put this on top of this patchset if necessary. But then it not so nice and simple anymore. Maybe we can rstrict the use of cpu alloc instead to users with objects <
cache_line_size()or so?Restricting the use of
cpu_allocbased on size of object is no good when you're trying to allocate 45,000 objects. Extending the per CPU space is the only option.
Ok guess we need to bring the virtually mapped per cpu patches forward.
Quoted 11 lines · Andrew Morton
It was just an example. There will be others.
tcp_v4_md5_do_add ->tcp_alloc_md5sig_pool ->__tcp_alloc_md5sig_pooldoes an
alloc_percpufor each md5-capable TCP connection. I think - it doesn't matter really, because something _could_. And if something _does_, we're screwed.
Last time I took a look on this stuff, this was a percpu allocation for all connections, not for each TCP session. (It should be static, instead of dynamic )
Really, percpu allocations are currently not frequent at all.
vmalloc()/vfreee() are way more frequent and still use a list.
Andrew Morton
maintainerQuoted 3 lines · On Fri, 30 May 2008 08:01:02 +0200 Eric Dumazet <[email protected]> wrote:
Really, percpu allocations are currently not frequent at all.
vmalloc()/vfreee() are way more frequent and still use a list.
Sure it's hard to conceive how anyone could go and do a per-cpu allocation on a fastpath.
But this has nothing to do with the frequency! The problems surround the _amount_ of allocated memory and the allocation/freeing patterns.
Here's another example. And it's only an example! Generalise!
ext3 maintains three percpu_counters per mount. Each percpu_counter does one percpu_alloc. People can mount an arbitrary number of ext3 filesystems!
Another: there are two percpu_counters (and hence two percpu_alloc()s) per backing_dev_info. One backing_dev_info per disk and people have been known to have thousands (iirc ~10,000) disks online.
And those examples were plucked only from today's kernel. Who knows what other problems will be in 2.6.45?
Quoted 3 lines · Andrew Morton
ext3 maintains three
percpu_countersper mount. Eachpercpu_counterdoes onepercpu_alloc. People can mount an arbitrary number of ext3 filesystems!
But its 4 bytes per alloc right?
Quoted 3 lines
Another: there are two
percpu_counters(and hence two percpu_alloc()s) perbacking_dev_info. Onebacking_dev_infoper disk and people have been known to have thousands (iirc ~10,000) disks online.
8 bytes per backing device. 80000 bytes for 10000 disks.
Quoted 2 lines
And those examples were plucked only from today's kernel. Who knows what other problems will be in 2.6.45?
We can always increase the sizes.
Andrew Morton
maintainerQuoted 7 lines · On Thu, 29 May 2008 23:22:31 -0700 (PDT) Christoph Lameter <[email protected]>…
Quoted 3 lines · Andrew Morton
ext3 maintains three
percpu_countersper mount. Eachpercpu_counterdoes onepercpu_alloc. People can mount an arbitrary number of ext3 filesystems!But its 4 bytes per alloc right?
It could be 4000. The present alloc_percpu() would support that.
And struct nfs_iostats is 264 bytes and nfs does an alloc_percpu() of one of those per server and mounting thousands of servers per client is, I believe, a real-world operation.
Plus for the entyenth time: saying that this code will probably work acceptably for most people in 2.6.26 is not sufficient!
Quoted 3 lines · Andrew Morton
And struct
nfs_iostatsis 264 bytes and nfs does analloc_percpu()of one of those per server and mounting thousands of servers per client is, I believe, a real-world operation.
Another example, not as extreme, there's an alloc_percpu(struct disk_stats) [80 bytes on 64-bit machines] for every disk and every partition in the machine. The TPC system has 3000 disks, each with 14 partitions on it. That's 15 * 80 * 3000 = 3,600,000 bytes.
Even if you're only putting a pointer to each allocation in the percpu area, that's still 360,000 bytes, 12x as much as you think is sufficient for the entire system.
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Quoted 17 lines · Andrew Morton
Quoted 6 lines · On Thu, 29 May 2008 20:56:20 -0700 Christoph Lameter <[email protected]> wrote:
In various places the kernel maintains arrays of pointers indexed by processor numbers. These are used to locate objects that need to be used when executing on a specirfic processor. Both the slab allocator and the page allocator use these arrays and there the arrays are used in performance critical code. The allocpercpu functionality is a simple allocator to provide these arrays.
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
Quoted 6 lines
I wonder if we can arrange for the default to be overridden via a kernel boot option?
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
One problem with variable sized cpu_alloc area is this comment in bitmap.h:
* Note that nbits should be always a compile time evaluable constant.
* Otherwise many inlines will generate horrible code.
I'm guessing since this will be of low use and not performance critical, then we can ignore the "horrible code"? ;-)
Thanks,
Mike
Quoted 17 lines · Mike Travis
Quoted 16 lines · Andrew Morton
Quoted 6 lines · On Thu, 29 May 2008 20:56:20 -0700 Christoph Lameter <[email protected]> wrote:
In various places the kernel maintains arrays of pointers indexed by processor numbers. These are used to locate objects that need to be used when executing on a specirfic processor. Both the slab allocator and the page allocator use these arrays and there the arrays are used in performance critical code. The allocpercpu functionality is a simple allocator to provide these arrays.
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
Quoted 6 lines
Quoted 6 lines
I wonder if we can arrange for the default to be overridden via a kernel boot option?
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
Christoph & Mike,
Please forgive me if I beat a dead horse, but this percpu stuff should find its way.
I wonder why you absolutely want to have only one chunk holding all percpu variables, static(vmlinux) & static(modules) & dynamically allocated.
Its *not* possible to put an arbitrary limit to this global zone. You'll allways find somebody to break this limit. This is the point we must solve, before coding anything.
Have you considered using a list of fixed size chunks, each chunk having its own bitmap ?
We only want fix offsets between CPU locations. For a given variable, we MUST find addresses for all CPUS looking at the same offset table. (Then we can optimize things on x86, using %gs/%fs register, instead of a table lookup)
We could chose chunk size at compile time, depending on various parameters (32/64 bit arches, or hugepage sizes on NUMA), and a minimum value (ABI guarantee)
On x86_64 && NUMA we could use 2 Mbytes chunks, while on x86_32 or non NUMA we should probably use 64 Kbytes.
At boot time, we setup the first chunk (chunk 0) and copy .data.percpu on this chunk, for each possible cpu, and we build the bitmap for future dynamic/module percpu allocations. So we still have the restriction that sizeofsection(.data.percpu) should fit in the chunk 0. Not a problem in practice.
Then if we need to expand percpu zone for heavy duty machine, and chunk 0 is already filled, we can add as many 2 M/ 64K chunks we need.
This would limit the dynamic percpu allocation to 64 kbytes for a given variable, so huge users should probably still use a different allocator (like oprofile alloc_cpu_buffers() function) But at least we dont anymore limit the total size of percpu area.
I understand you want to offset percpu data to 0, but for static percpu data. (pda being included in, to share %gs)
For dynamically allocated percpu variables (including modules ".data.percpu"), nothing forces you to have low offsets, relative to %gs/%fs register. Access to these variables will be register indirect based anyway (eg %gs:(%rax) )
1) NUMA case
For a 64 bit NUMA arch, chunk size of 2Mbytes
Allocates 2Mb for each possible processor (on its preferred memory node), and compute values to setup offset_of_cpu[NR_CPUS] array.
Chunk 0
CPU 0 : virtual address XXXXXX
CPU 1 : virtual address XXXXXX + offset_of_cpu[1]
...
CPU n : virtual address XXXXXX + offset_of_cpu[n]
+ a shared bitmap
For next chunks, we could use vmalloc() zone to find nr_possible_cpus virtual addresses ranges where you can map a 2Mb page per possible cpu, as long as we respect the relative delta between each cpu block, that was computed when chunk 0 was setup.
Chunk 1..n
CPU 0 : virtual address YYYYYYYYYYYYYY
CPU 1 : virtual address YYYYYYYYYYYYYY + offset_of_cpu[1]
...
CPU n : virtual address YYYYYYYYYYYYYY + offset_of_cpu[n]
+ a shared bitmap (32Kbytes if 8 bytes granularity in allocator)
For a variable located in chunk 0, its 'address' relative to current cpu %gs will be some number between [0 and 2^20-1]
For a variable located in chunk 1, its 'address' relative to current cpu %gs will be some number between
[YYYYYYYYYYYYYY - XXXXXX and YYYYYYYYYYYYYY - XXXXXX + 2^20 - 1], not necessarly [2^20 to 2^21 - 1]
Chunk 0 would use normal memory (no vmap TLB cost), only next ones need vmalloc().
So the extra TLB cost would only be taken for very special NUMA setups (only if using a lot of percpu allocations)
Also, using a 2Mb page granularity probably wastes about 2Mb per cpu, but this is nothing for NUMA machines :)
2) SMP && !NUMA
On non NUMA machines, we dont need vmalloc games, since we can allocate chunk space using contiguous memory, (size = nr_possible_cpus*64Kbytes)
offset_of_cpu[N] = N * CHUNK_SIZE
(On a 4 CPU x86_32 machine, allocate a 256 Kbyte bloc then divide it in 64 kb blocs)
If this order-6 allocation fails, then fallback to vmalloc(), but most percpu allocations happens at boot time, when memory is not yet fragmented...
3) UP case : fallback to standard allocators. No need for bitmaps.
NUMA special casing can be implemented later of course...
Thanks for reading
Quoted 135 lines · Eric Dumazet
Quoted 17 lines · Mike Travis
Quoted 16 lines · Andrew Morton
Quoted 6 lines · On Thu, 29 May 2008 20:56:20 -0700 Christoph Lameter <[email protected]> wrote:
In various places the kernel maintains arrays of pointers indexed by processor numbers. These are used to locate objects that need to be used when executing on a specirfic processor. Both the slab allocator and the page allocator use these arrays and there the arrays are used in performance critical code. The allocpercpu functionality is a simple allocator to provide these arrays.
All seems reasonable to me. The obvious question is "how do we size the arena". We either waste memory or, much worse, run out.
And running out is a real possibility, I think. Most people will only mount a handful of XFS filesystems. But some customer will come along who wants to mount 5,000, and distributors will need to cater for that, but how can they?
Quoted 6 lines
Quoted 6 lines
I wonder if we can arrange for the default to be overridden via a kernel boot option?
Another obvious question is "how much of a problem will we have with internal fragmentation"? This might be a drop-dead showstopper.
Christoph & Mike,
Please forgive me if I beat a dead horse, but this percpu stuff should find its way.
I wonder why you absolutely want to have only one chunk holding all percpu variables, static(vmlinux) & static(modules) & dynamically allocated.
Its *not* possible to put an arbitrary limit to this global zone. You'll allways find somebody to break this limit. This is the point we must solve, before coding anything.
Have you considered using a list of fixed size chunks, each chunk having its own bitmap ?
We only want fix offsets between CPU locations. For a given variable, we MUST find addresses for all CPUS looking at the same offset table. (Then we can optimize things on x86, using %gs/%fs register, instead of a table lookup)
We could chose chunk size at compile time, depending on various parameters (32/64 bit arches, or hugepage sizes on NUMA), and a minimum value (ABI guarantee)
On
x86_64&& NUMA we could use 2 Mbytes chunks, while onx86_32or non NUMA we should probably use 64 Kbytes.At boot time, we setup the first chunk (chunk 0) and copy .data.percpu on this chunk, for each possible cpu, and we build the bitmap for future dynamic/module percpu allocations. So we still have the restriction that sizeofsection(.data.percpu) should fit in the chunk 0. Not a problem in practice.
Then if we need to expand percpu zone for heavy duty machine, and chunk 0 is already filled, we can add as many 2 M/ 64K chunks we need.
This would limit the dynamic percpu allocation to 64 kbytes for a given variable, so huge users should probably still use a different allocator (like oprofile
alloc_cpu_buffers()function) But at least we dont anymore limit the total size of percpu area.I understand you want to offset percpu data to 0, but for static percpu data. (pda being included in, to share %gs)
For dynamically allocated percpu variables (including modules ".data.percpu"), nothing forces you to have low offsets, relative to %gs/%fs register. Access to these variables will be register indirect based anyway (eg %gs:(%rax) )
1) NUMA case
For a 64 bit NUMA arch, chunk size of 2Mbytes
Allocates 2Mb for each possible processor (on its preferred memory node), and compute values to setup offset_of_cpu[NR_CPUS] array.
Chunk 0
CPU 0 : virtual address XXXXXX
CPU 1 : virtual address XXXXXX + offset_of_cpu[1]
...
CPU n : virtual address XXXXXX + offset_of_cpu[n]
+ a shared bitmapFor next chunks, we could use
vmalloc()zone to findnr_possible_cpusvirtual addresses ranges where you can map a 2Mb page per possible cpu, as long as we respect the relative delta between each cpu block, that was computed when chunk 0 was setup.Chunk 1..n
CPU 0 : virtual address YYYYYYYYYYYYYY
CPU 1 : virtual address YYYYYYYYYYYYYY + offset_of_cpu[1]
...
CPU n : virtual address YYYYYYYYYYYYYY + offset_of_cpu[n]
+ a shared bitmap (32Kbytes if 8 bytes granularity in allocator)For a variable located in chunk 0, its 'address' relative to current cpu %gs will be some number between [0 and 2^20-1]
For a variable located in chunk 1, its 'address' relative to current cpu %gs will be some number between
[YYYYYYYYYYYYYY - XXXXXX and YYYYYYYYYYYYYY - XXXXXX + 2^20 - 1], not necessarly [2^20 to 2^21 - 1]Chunk 0 would use normal memory (no vmap TLB cost), only next ones need
vmalloc().So the extra TLB cost would only be taken for very special NUMA setups (only if using a lot of percpu allocations)
Also, using a 2Mb page granularity probably wastes about 2Mb per cpu, but this is nothing for NUMA machines :)
2) SMP && !NUMA
On non NUMA machines, we dont need vmalloc games, since we can allocate chunk space using contiguous memory, (size = nr_possible_cpus*64Kbytes)
offset_of_cpu[N] = N *
CHUNK_SIZE(On a 4 CPU
x86_32machine, allocate a 256 Kbyte bloc then divide it in 64 kb blocs)
If this order-6 allocation fails, then fallback tovmalloc(), but most percpu allocations happens at boot time, when memory is not yet fragmented...
Quoted 8 lines
3) UP case : fallback to standard allocators. No need for bitmaps.
NUMA special casing can be implemented later of course...
Thanks for reading
Wow! Thanks for the detail! It's extremely useful (to me at least) to see it spelled out.
Since Christoph is still on vacation I'll try to summarize where we're at at the moment. (Besides being stuck on a boot up problem with the %gs based percpu variables that is. ;-)
Yes, the problem is we need to use virtual addresses to expand the percpu areas since each cpu needs the same fixed offset to the newly allocated variables. This was in the prior (v2) version of cpu_alloc so I'm looking at pulling that forward. And I also figured that the size of the expansion allocations should be based on the system size to minimize the effect on small systems (seems to be my life the past 6 months... ;-)
I'm also looking at integrating more into the already present infrastructure (thanks Rusty!) so there are less "diffs" (and less new testing needed.) And of course, there's the complexities of submitting patches to many architectures simultaneously.
Hopefully, I'll have something for review soon.
Thanks again,
Mike
Quoted 37 lines · On Friday 06 June 2008 15:33:22 Eric Dumazet wrote:
1) NUMA case
For a 64 bit NUMA arch, chunk size of 2Mbytes
Allocates 2Mb for each possible processor (on its preferred memory node), and compute values to setup offset_of_cpu[NR_CPUS] array.
Chunk 0
CPU 0 : virtual address XXXXXX
CPU 1 : virtual address XXXXXX + offset_of_cpu[1]
...
CPU n : virtual address XXXXXX + offset_of_cpu[n]
+ a shared bitmapFor next chunks, we could use
vmalloc()zone to findnr_possible_cpusvirtual addresses ranges where you can map a 2Mb page per possible cpu, as long as we respect the relative delta between each cpu block, that was computed when chunk 0 was setup.Chunk 1..n
CPU 0 : virtual address YYYYYYYYYYYYYY
CPU 1 : virtual address YYYYYYYYYYYYYY + offset_of_cpu[1]
...
CPU n : virtual address YYYYYYYYYYYYYY + offset_of_cpu[n]
+ a shared bitmap (32Kbytes if 8 bytes granularity in allocator)For a variable located in chunk 0, its 'address' relative to current cpu %gs will be some number between [0 and 2^20-1]
For a variable located in chunk 1, its 'address' relative to current cpu %gs will be some number between
[YYYYYYYYYYYYYY - XXXXXX and YYYYYYYYYYYYYY - XXXXXX + 2^20 - 1], not necessarly [2^20 to 2^21 - 1]
Quoted 8 lines
Chunk 0 would use normal memory (no vmap TLB cost), only next ones need
vmalloc().So the extra TLB cost would only be taken for very special NUMA setups (only if using a lot of percpu allocations)
Also, using a 2Mb page granularity probably wastes about 2Mb per cpu, but this is nothing for NUMA machines :)
If you're prepared to have mappings for chunk 0, you can simply make it virtually linear and creating a new chunk is simple. If not, you need to reserve the virtual address space(s) for future mappings. Otherwise you're unlikely to get the same layout for allocations.
This is not a show-stopper: we've lived with limited vmalloc room since forever. It just has to be sufficient.
Otherwise, your analysis is correct, if a little verbose :)
Cheers,
Rusty.
Quoted 2 lines · Eric Dumazet
Please forgive me if I beat a dead horse, but this percpu stuff should find its way.
Definitely and its very complex so any more eyes on this are appreciated.
Quoted 7 lines
I wonder why you absolutely want to have only one chunk holding all percpu variables, static(vmlinux) & static(modules) & dynamically allocated.
Its *not* possible to put an arbitrary limit to this global zone. You'll allways find somebody to break this limit. This is the point we must solve, before coding anything.
The problem is that offsets relative to %gs or %fs are limited by the small memory model that is chosen. We cannot have an offset large than 2GB. So we must have a linear address range and cannot use separate chunks of memory. If we do not use the segment register then we cannot do atomic (wrt interrupt) cpu ops.
Quoted 2 lines
Have you considered using a list of fixed size chunks, each chunk having its own bitmap ?
Mike has done so and then I had to tell him what I just told you.
Quoted 2 lines
On
x86_64&& NUMA we could use 2 Mbytes chunks, while onx86_32or non NUMA we should probably use 64 Kbytes.
Right that is what cpu_alloc v2 did. It created a virtual mapping and populated it on demand with 2MB PMD entries.
Quoted 7 lines
I understand you want to offset percpu data to 0, but for static percpu data. (pda being included in, to share %gs)
For dynamically allocated percpu variables (including modules ".data.percpu"), nothing forces you to have low offsets, relative to %gs/%fs register. Access to these variables will be register indirect based anyway (eg %gs:(%rax) )
The relative to 0 stuff comes in at the x86_64 level because we want to unify pda and percpu accesses. pda access have been relative to 0 and in particular the stack canary in glibc directly accesses the pda at a certain offset. So we must be zero based in order to preserve compatibility with glibc.
Quoted 1 line
Chunk 0 would use normal memory (no vmap TLB cost), only next ones need
vmalloc().
Normal memory uses 2MB tlbs. There is no overhead therefore by mapping the percpu areas using 2MB tlbs. So we do not need to be that complicated.
What v2 did was allocate an area n * MAX_VIRT_PER_CPU_SIZE in vmalloc space and then it dynamically populated 2MB segments as needed. The MAX size was 128MB or so.
We could either do the same on i386 or use 4kb mappings (then we can directly use the vmalloc functionality). But then there would be additional TLB overhead.
We have similar 2MB virtual mapping tricks for the virtual memmap. Basically we can copy the functions and customize them for the virtual per cpu areas (Mike is hopefully listening and reading the V2 patch ....)
Quoted 2 lines · Christoph Lameter
The problem is that offsets relative to %gs or %fs are limited by the small memory model that is chosen.
Actually they are not. If you really want you can do
movabs $64bit,%reg ; op ...,%gs:(%reg)
It's just not very efficient compared to small (or rather kernel) model and also older binutils didn't support large model.
-Andi
Quoted 10 lines · Andi Kleen
Quoted 2 lines · Christoph Lameter
The problem is that offsets relative to %gs or %fs are limited by the small memory model that is chosen.
Actually they are not. If you really want you can do
movabs $64bit,%reg ; op ...,%gs:(%reg)
It's just not very efficient compared to small (or rather kernel) model and also older binutils didn't support large model.
I am not sure Christoph was refering to actual instructions.
I was suggesting using for static percpu (vmlinux or modules) :
vmlinux : (offset31 computed by linker at vmlinux link edit time) incl %gs:offset31
modules : (offset31 computed at module load time by module loader) incl %gs:offset31
(If we make sure all this stuff is allocated in first chunk)
And for dynamic percpu :
movq field(%rdi),%rax
incl %gs:(%rax) /* full 64bits 'offsets' */
I understood (but might be wrong again) that %gs itself could not be used with an offset > 2GB, because the way %gs segment is setup. So in the 'dynamic percpu' case, %rax should not exceed 2^31
Hi Christoph,
I'm having some trouble liking this.
For one the fixed size alloction limit as pointed out by Andrew.
Secondly but way more important (for me), you seem to make life way more difficult for -rt.
The thing we generally do is, we add a lock to each per-cpu data item, use raw_smp_processor_id() to obtain the current cpu's data lock the thing and work from it - even if we are migrated away.
For instance:
struct kmem_cache_cpu {
.....
spinlock_t lock;
}struct kmem_cache_cpu *get_cpu_slab(struct kmem_cache *s, int cpu)
{
struct kmem_cache_cpu *c = s->cpu_slab[cpu];
spin_lock(&c->lock);
return c;
}void put_cpu_slab(struct kmem_cache_cpu *c)
{
spin_unlock(&c->lock);
}What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
One sign of that is that you replace things like get_cpu with explicit preempt_disable().
We're trying to get rid of as many explicit preempt_disable()s as possible - in the light of -rt, preempt_disable() is as bad as the BKL in that its opaque - unrelated to a specific data set.
*lightbulb*
Ah, we could still do the above by writing:
struct kmem_cache_cpu *get_cpu_slab(struct kmem_cache *s)
{
struct kmem_cache_cpu *c = THIS_CPU(s->cpu_slab);
spin_lock(&c->lock);
return c;
}void put_cpu_slab(struct kmem_cache_cpu *c)
{
spin_unlock(&c->lock);
}Would it be possible to re-structure your API to also have these get/put methods instead of just a get?
#define GET_THIS_CPU(__p) \
({ preempt_disable(); SHIFT_PERCPU_PTR((__p), my_cpu_offset); })#define PUT_THIS_CPU(__c) \
do { \
(void)(__c); \
preempt_enable(); \
} while (0)That would immediately get rid of those preempt_disable() calls, and provide enough hooks for -rt.
Also, please add:
QUILT_DIFF_OPTS="--show-c-function"
somewhere to your environment or .quiltrc
Quoted 3 lines · Peter Zijlstra
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
and that's not just an issue for -rt but also for general kernel
maintainability: opaque APIs weaken our data structures because it's not apparent what is done exactly why. In that sense a spin_lock(&data->lock) is as clear as it gets.
Quoted 18 lines
One sign of that is that you replace things like
get_cpuwith explicitpreempt_disable().We're trying to get rid of as many explicit preempt_disable()s as possible - in the light of -rt,
preempt_disable()is as bad as the BKL in that its opaque - unrelated to a specific data set.*lightbulb*
Ah, we could still do the above by writing:
struct
kmem_cache_cpu*get_cpu_slab(structkmem_cache*s){ struct kmem_cache_cpu *c = THIS_CPU(s->cpu_slab); spin_lock(&c->lock); return c; }
Quoted 7 lines
void
put_cpu_slab(struct kmem_cache_cpu *c){ spin_unlock(&c->lock); }Would it be possible to re-structure your API to also have these get/put methods instead of just a get?
seconded.
Ingo
Quoted 11 lines · Ingo Molnar
Quoted 3 lines · Peter Zijlstra
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
and that's not just an issue for -rt but also for general kernel
maintainability: opaque APIs weaken our data structures because it's not apparent what is done exactly why. In that sense aspin_lock(&data->lock)is as clear as it gets.
The intent of these CPU_OPS is to avoid synchronization by having atomic per cpu ops that do not require preempt or interrupt disable to function properly. F.e. it allows the updating of per cpu statistics without get_cpu/put_cpu or preempt_enable/disable etc etc. This should help -rt quite a bit by avoiding numerous of these things in hot vm paths.
Quoted 14 lines · Christoph Lameter
Quoted 11 lines · Ingo Molnar
Quoted 3 lines · Peter Zijlstra
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
and that's not just an issue for -rt but also for general kernel
maintainability: opaque APIs weaken our data structures because it's not apparent what is done exactly why. In that sense aspin_lock(&data->lock)is as clear as it gets.
Quoted 5 lines
The intent of these
CPU_OPSis to avoid synchronization by having atomic per cpu ops that do not require preempt or interrupt disable to function properly. F.e. it allows the updating of per cpu statistics without get_cpu/put_cpu or preempt_enable/disable etc etc. This should help -rt quite a bit by avoiding numerous of these things in hot vm paths.
Yes, I get that, but for instance kmem_cache_cpu usage would require horrible long preempt off sections, hence we add a lock and manage consistency using that lock instead of strict per-cpu and preempt disable.
Quoted 4 lines · Peter Zijlstra
Yes, I get that, but for instance
kmem_cache_cpuusage would require horrible long preempt off sections, hence we add a lock and manage consistency using that lock instead of strict per-cpu and preempt disable.
Really? Where is that horrible long preempt off section? I thought they were all short and we use semaphores when looping over all slabs.
Quoted 9 lines · Christoph Lameter
Quoted 4 lines · Peter Zijlstra
Yes, I get that, but for instance
kmem_cache_cpuusage would require horrible long preempt off sections, hence we add a lock and manage consistency using that lock instead of strict per-cpu and preempt disable.Really? Where is that horrible long preempt off section? I thought they were all short and we use semaphores when looping over all slabs.
Remember, horribly long on -rt is in the order of ~30us.
See for instance the path:
flush_slab()
slab_lock() <-- bit_spinlock deactive_slab()
loop over objects
unfreeze_slab()
add_partial()
spin_lock(&n->list_lock) <-- another lock
all of that runs with preempt disabled, and worse it can spin for some indefinite amount of time.
That is totally unacceptable on -rt, so we need to make it all preemptible and use sleeping locks (IRQ inversion can't happen, for the only thing that runs in hardirq is try_to_wake_up()).
Quoted 8 lines · Peter Zijlstra
Remember, horribly long on -rt is in the order of ~30us.
See for instance the path:
flush_slab()
slab_lock()<--bit_spinlock
deactive_slab()
loop over objects
looping over all objects is rarely ever done.
Quoted 10 lines
unfreeze_slab() add_partial() spin_lock(&n->list_lock) <-- another lockall of that runs with preempt disabled, and worse it can spin for some indefinite amount of time.
That is totally unacceptable on -rt, so we need to make it all preemptible and use sleeping locks (IRQ inversion can't happen, for the only thing that runs in hardirq is
try_to_wake_up()).
The way I thought about -rt was that it would convert the spinlocks to semaphores. Isnt it possible to sleep on a bit spinlock in -rt? Aside from that I would have thought that the simple disabling of rescheduling on another processor in some context would gets us there.
Never thought about the details though.
Quoted 24 lines · Christoph Lameter
Quoted 8 lines · Peter Zijlstra
Remember, horribly long on -rt is in the order of ~30us.
See for instance the path:
flush_slab()
slab_lock()<--bit_spinlock
deactive_slab()
loop over objectslooping over all objects is rarely ever done.
Quoted 10 lines
unfreeze_slab() add_partial() spin_lock(&n->list_lock) <-- another lockall of that runs with preempt disabled, and worse it can spin for some indefinite amount of time.
That is totally unacceptable on -rt, so we need to make it all preemptible and use sleeping locks (IRQ inversion can't happen, for the only thing that runs in hardirq is
try_to_wake_up()).
Quoted 6 lines
The way I thought about -rt was that it would convert the spinlocks to semaphores. Isnt it possible to sleep on a bit spinlock in -rt? Aside from that I would have thought that the simple disabling of rescheduling on another processor in some context would gets us there.
Never thought about the details though.
s/semaphores/PI-mutexes/
yes you can do a bit-sleep-lock ala lock_page.
But for any sleeping lock we must not have preemption disabled, coming right back to the initial problem.
Quoted 1 line · Peter Zijlstra
For one the fixed size alloction limit as pointed out by Andrew.
Ok.
Quoted 6 lines
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
One sign of that is that you replace things like
get_cpuwith explicitpreempt_disable().
That is a side effect of trying to make transformation of source code easily visible. Its not necessary if some changes are made to the code.
Quoted 3 lines
We're trying to get rid of as many explicit preempt_disable()s as possible - in the light of -rt,
preempt_disable()is as bad as the BKL in that its opaque - unrelated to a specific data set.
The cpu ops patches get rid of lots of preempt enable /disable and get_cpu/put_cpu() because the per cpu address determination no longer needs to be protected. See some of the other patches. Some code may need to be a bit rewritten to take advantage. The SLUB fastpath f.e. (and likely the page allocator too) can be done exclusively with CPU_OPS thereby avoiding locks and preemption issues.
Quoted 20 lines · Christoph Lameter
Quoted 1 line · Peter Zijlstra
For one the fixed size alloction limit as pointed out by Andrew.
Ok.
Quoted 6 lines
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
One sign of that is that you replace things like
get_cpuwith explicitpreempt_disable().That is a side effect of trying to make transformation of source code easily visible. Its not necessary if some changes are made to the code.
Quoted 3 lines
We're trying to get rid of as many explicit preempt_disable()s as possible - in the light of -rt,
preempt_disable()is as bad as the BKL in that its opaque - unrelated to a specific data set.
Quoted 6 lines
The cpu ops patches get rid of lots of preempt enable /disable and get_cpu/put_cpu() because the per cpu address determination no longer needs to be protected. See some of the other patches. Some code may need to be a bit rewritten to take advantage. The SLUB fastpath f.e. (and likely the page allocator too) can be done exclusively with
CPU_OPSthereby avoiding locks and preemption issues.
Sure, that's good stuff, and I'm not objecting to that.
But for example the perpcu_counter, workqueue, (nfs), module handling, slub and page allocator can use the scheme I proposed.
Please consider adding this get/put interface.
Quoted 1 line · Peter Zijlstra
Please consider adding this get/put interface.
If you could explain why? So far this seems to be based on a misunderstanding.
Quoted 6 lines · Christoph Lameter
Quoted 1 line · Peter Zijlstra
Please consider adding this get/put interface.
If you could explain why? So far this seems to be based on a misunderstanding.
Take for instance kmem_cache_cpu, you currently serialize that by strict per-cpu-ness and disabling preemption.
The problem is that the preempt-off sections are rather long - so what we do is add a lock (mutex) and serialize that way - ignoring the cpu affinity.
so currently:
preempt_disable(); c = get_cpu_slab();
do load of stuff on c;
preempt_enable();
where the preempt-off section is rather long, we'd like to change that to:
c = get_cpu_slab();
do stuff to c;
put_cpu_slab(c);
so that we can pick between:
!rt
get_cpu_slab(s)
{
preempt_disable();
return THIS_CPU(s->cpu_slab);
}put_cpu_slab(c)
{
preempt_enable();
}-rt:
get_cpu_slab(s)
{
c = THIS_CPU(s->cpu_slab);
spin_lock(&c->lock); // <-- really a PI-mutex
}put_cpu_slab(c)
{
spin_unlock(&c->lock);
}Also, it explicitly ties the preempt-off section to the data used in case of !rt, which in turn allows for the direct conversion to the locked version.
Quoted 2 lines · Peter Zijlstra
Take for instance
kmem_cache_cpu, you currently serialize that by strict per-cpu-ness and disabling preemption.
Right. The preemption in the fast paths could go away. Would be more difficult to do in the slow paths.
Quoted 3 lines
The problem is that the preempt-off sections are rather long - so what we do is add a lock (mutex) and serialize that way - ignoring the cpu affinity.
long? What is so expensive to process there?
Quoted 47 lines
so currently:
preempt_disable(); c = get_cpu_slab();do load of stuff on c;
preempt_enable();where the preempt-off section is rather long, we'd like to change that to:
c = get_cpu_slab();do stuff to c;
put_cpu_slab(c);so that we can pick between:
!rt
get_cpu_slab(s){ preempt_disable(); return THIS_CPU(s->cpu_slab); }
put_cpu_slab(c){ preempt_enable(); }-rt:
get_cpu_slab(s){ c = THIS_CPU(s->cpu_slab); spin_lock(&c->lock); // <-- really a PI-mutex }
put_cpu_slab(c)
Quoted 8 lines
{ spin_unlock(&c->lock); }Also, it explicitly ties the preempt-off section to the data used in case of !rt, which in turn allows for the direct conversion to the locked version.
Ahh. Okay. This would make the lockless preemptless fastpath impossible because it would have to use some sort of locking to avoid access to the same percpu data from multiple processor?
Quoted 5 lines · Christoph Lameter
Quoted 2 lines · Peter Zijlstra
Take for instance
kmem_cache_cpu, you currently serialize that by strict per-cpu-ness and disabling preemption.
Quoted 8 lines
Right. The preemption in the fast paths could go away. Would be more difficult to do in the slow paths.
Quoted 3 lines
The problem is that the preempt-off sections are rather long - so what we do is add a lock (mutex) and serialize that way - ignoring the cpu affinity.
long? What is so expensive to process there?
I hope my last mail that detailed the flush_slab() path explains that.
Quoted 52 lines
Quoted 52 lines
so currently:
preempt_disable(); c = get_cpu_slab();do load of stuff on c;
preempt_enable();where the preempt-off section is rather long, we'd like to change that to:
c = get_cpu_slab();do stuff to c;
put_cpu_slab(c);so that we can pick between:
!rt
get_cpu_slab(s){ preempt_disable(); return THIS_CPU(s->cpu_slab); }
put_cpu_slab(c){ preempt_enable(); }-rt:
get_cpu_slab(s){ c = THIS_CPU(s->cpu_slab); spin_lock(&c->lock); // <-- really a PI-mutex }
put_cpu_slab(c){ spin_unlock(&c->lock); }
Quoted 7 lines
Quoted 3 lines
Also, it explicitly ties the preempt-off section to the data used in case of !rt, which in turn allows for the direct conversion to the locked version.
Ahh. Okay. This would make the lockless preemptless fastpath impossible because it would have to use some sort of locking to avoid access to the same percpu data from multiple processor?
TBH its been a while since I attempted slub-rt, but yes that got hairy. I think it can be done using cmpxchg and speculative page refs, but I can't quite recall.
Quoted 9 lines · Peter Zijlstra
Quoted 3 lines · Christoph Lameter
Ahh. Okay. This would make the lockless preemptless fastpath impossible because it would have to use some sort of locking to avoid access to the same percpu data from multiple processor?
TBH its been a while since I attempted slub-rt, but yes that got hairy. I think it can be done using cmpxchg and speculative page refs, but I can't quite recall.
This is the last version I could find on my disks (2007-11-17) - it does indeed have a severely handicapped fast-path.
Never got around to testing it properly - so it might be utter bollocks.
---
Subject: rt: make SLUB usable
Spurred by John Corbet's harsh words that SLUB is not available for -rt I made a quick fix for this.
--- include/linux/slub_def.h | 3 + init/Kconfig | 1 mm/slub.c | 108 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 88 insertions(+), 24 deletions(-)
init/Kconfig +0 -1
@@ -635,7 +635,6 @@ config SLAB config SLUB bool "SLUB (Unqueued Allocator)" - depends on !PREEMPT_RT help SLUB is a slab allocator that minimizes cache line usage instead of managing queues of cached objects (SLAB approach).
include/linux/slub_def.h +3 -0
@@ -17,6 +17,9 @@ struct kmem_cache_cpu {int node;unsigned int offset;unsigned int objsize; +#ifdef CONFIG_PREEMPT_RT +spinlock_t lock; +#endif };struct kmem_cache_node {
mm/slub.c +85 -23
269 lines not on this page · raw message
Quoted 11 lines · Peter Zijlstra
Quoted 7 lines
Quoted 3 lines
Also, it explicitly ties the preempt-off section to the data used in case of !rt, which in turn allows for the direct conversion to the locked version.
Ahh. Okay. This would make the lockless preemptless fastpath impossible because it would have to use some sort of locking to avoid access to the same percpu data from multiple processor?
TBH its been a while since I attempted slub-rt, but yes that got hairy. I think it can be done using cmpxchg and speculative page refs, but I can't quite recall.
Uhhh... Well I think you really need two different percpu sections. One that works like the upstream percpu section and another where it works with a spinlock. Basically its like a struct protected by an extra lock.
The one with a spinlock could not support the atomic percpu ops.
Quoted 11 lines · Peter Zijlstra
The thing we generally do is, we add a lock to each per-cpu data item, use
raw_smp_processor_id()to obtain the current cpu's data lock the thing and work from it - even if we are migrated away.For instance:
struct kmem_cache_cpu { ..... spinlock_t lock; }
Quoted 5 lines
struct
kmem_cache_cpu*get_cpu_slab(structkmem_cache*s, int cpu){ struct kmem_cache_cpu *c = s->cpu_slab[cpu]; spin_lock(&c->lock); return c;
Hmmm... Can we reschedule before spin_lock? It seems that preemption must already be off for this to work.
Quoted 3 lines
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
Yes the cpu ops allow atomic per cpu ops without preemption / interrupt enable disable. I thought that would help -rt quit a bit.
Quoted 9 lines
Ah, we could still do the above by writing:
struct
kmem_cache_cpu*get_cpu_slab(structkmem_cache*s){ struct kmem_cache_cpu *c = THIS_CPU(s->cpu_slab); spin_lock(&c->lock); return c; }
Quoted 7 lines
void
put_cpu_slab(struct kmem_cache_cpu *c){ spin_unlock(&c->lock); }Would it be possible to re-structure your API to also have these get/put methods instead of just a get?
I do not see a problem since you must already have preemption disabled when callin get_cpu_slab(). Otherwise you may take the lock on another processor if the process was rescheduled.
Quoted 13 lines · Christoph Lameter
Quoted 11 lines · Peter Zijlstra
The thing we generally do is, we add a lock to each per-cpu data item, use
raw_smp_processor_id()to obtain the current cpu's data lock the thing and work from it - even if we are migrated away.For instance:
struct kmem_cache_cpu { ..... spinlock_t lock; }
Quoted 8 lines
Quoted 5 lines
struct
kmem_cache_cpu*get_cpu_slab(structkmem_cache*s, int cpu){ struct kmem_cache_cpu *c = s->cpu_slab[cpu]; spin_lock(&c->lock); return c;Hmmm... Can we reschedule before
spin_lock? It seems that preemption must already be off for this to work.
spinlocks turn into PI-mutexes for -rt, so preemption doesn't get disabled at all.
Quoted 7 lines
Quoted 4 lines
What this does is make a strong connection between data and concurrency control. Your proposed scheme weakens the data<->concurrency relation instead of making it stronger.
Yes the cpu ops allow atomic per cpu ops without preemption / interrupt enable disable. I thought that would help -rt quit a bit.
It does, but that is not the thing I'm pointing out.
The thing I'm addressing here is using structured per-cpu data like the kmem_cache_cpu that needs preempt disabled (or another form of concurrency control).
For -rt add the spinlock and thereby guarantee mutual exclusion in the face of preemption. This of course does mean reduced performance due to remote cpu memory accesses, but otherwise the allocator (slub in this case) would have far too large preempt-off sections.
Quoted 14 lines
Quoted 14 lines
Ah, we could still do the above by writing:
struct
kmem_cache_cpu*get_cpu_slab(structkmem_cache*s){ struct kmem_cache_cpu *c = THIS_CPU(s->cpu_slab); spin_lock(&c->lock); return c; }void
put_cpu_slab(struct kmem_cache_cpu *c){ spin_unlock(&c->lock); }
Quoted 6 lines
Quoted 2 lines
Would it be possible to re-structure your API to also have these get/put methods instead of just a get?
I do not see a problem since you must already have preemption disabled when callin
get_cpu_slab(). Otherwise you may take the lock on another processor if the process was rescheduled.
No, no, the !rt version looks like:
struct kmem_cache_cpu *get_cpu_slab(struct kmem_cache *s)
{
preempt_disable();
return THIS_CPU(s->cpu_slab);
}void put_cpu_slab(struct kmem_cache_cpu *c)
{
preempt_enable();
}For the -rt version it doesn't matter if we preempt or not, access is regulated by the ->lock.
Quoted 4 lines · Peter Zijlstra
For -rt add the spinlock and thereby guarantee mutual exclusion in the face of preemption. This of course does mean reduced performance due to remote cpu memory accesses, but otherwise the allocator (slub in this case) would have far too large preempt-off sections.
Hmmm.. So you want to lock and then be migrated while still accessing per cpu data of the earlier cpu? That breaks a lot of assumptions in the main kernel and would make cpu ops and local_t etc impossible because these rely on atomicity vs. interrupt vs atomicity vs access from other cpus. If you modify the per cpu data of one processor from multiple processor concurrently then races will result.
If you want to do it that way then local_t and cpu_ops would not work.
The cpu ops scheme is to not rely on any locking and still guarantee access to the correct per cpu area regardless of whether the process is rescheduled or not simply by the fact that the per cpu address calculation and the RMW cycle are one instruction.
Maybe we need to have different types of per cpu areas for both approaches? Your requires a spinlock to be taken for a section of the percpu space.
Quoted 7 lines · Christoph Lameter
Quoted 4 lines · Peter Zijlstra
For -rt add the spinlock and thereby guarantee mutual exclusion in the face of preemption. This of course does mean reduced performance due to remote cpu memory accesses, but otherwise the allocator (slub in this case) would have far too large preempt-off sections.
Quoted 6 lines
Hmmm.. So you want to lock and then be migrated while still accessing per cpu data of the earlier cpu? That breaks a lot of assumptions in the main kernel and would make cpu ops and
local_tetc impossible because these rely on atomicity vs. interrupt vs atomicity vs access from other cpus. If you modify the per cpu data of one processor from multiple processor concurrently then races will result.
Interrupts are not an issue on -rt.
Also, all sites that have but a single word of percpu data can just use a real atomic update.
All other sites should be converted to a locked version.
We also have DEFINE_PER_CPU_LOCKED() which has a similar get/put interface as I was asking for - it uses preempt-off for !rt and a lock for -rt.
Quoted 10 lines
If you want to do it that way then
local_tandcpu_opswould not work.The cpu ops scheme is to not rely on any locking and still guarantee access to the correct per cpu area regardless of whether the process is rescheduled or not simply by the fact that the per cpu address calculation and the RMW cycle are one instruction.
Maybe we need to have different types of per cpu areas for both approaches? Your requires a spinlock to be taken for a section of the percpu space.
For -rt we'd need to make the CPU_ops true atomic operations, with that all the single word stuff works again.
All the multi word stuff (that currently requires preempt-off) must be converted to a locked version.
Quoted 2 lines · Peter Zijlstra
For -rt we'd need to make the
CPU_opstrue atomic operations, with that all the single word stuff works again.
Hmmmm.... Okay then this would just mean adding an #ifdef CONFIG_RT to include/asm-x86/percpu.h.
Quoted 2 lines
All the multi word stuff (that currently requires preempt-off) must be converted to a locked version.
How would the (now atomic) per cpu ops interact with the locks? I.e. you take the lock for the kmem_cache_cpu structure and then the fastpath tries to perform an atomic update on one of the variables therein?