--- mm/allocpercpu.c | 63 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 13 deletions(-) Index: linux-2.6/mm/allocpercpu.c =================================================================== --- linux-2.6.orig/mm/allocpercpu.c 2007-10-31 12:19:56.415672508 -0700 +++ linux-2.6/mm/allocpercpu.c 2007-10-31 12:52:53.107172016 -0700 @@ -5,12 +5,28 @@ * * (C) 2007 SGI, Christoph Lameter * Basic implementation with allocation and free from a dedicated per cpu area. + * + * The per cpu allocator allows allocation of memory from a per cpu array that + * is statically allocated. A byte array exists that described the state of + * each of the available units that can be allocated via cpu_alloc and + * cpu_free. The possible states are + * + * FREE = The per cpu unit is not allocated + * USED = The per cpu unit is allocated and more units follow. + * END = The last per cpu unit used for an allocation (needed to + * establish the size of the allocation on free) + * + * The per cpu allocator is typically used to allocate small sized object from 8 to 32 + * bytes and it is rarely used. Allocation is looking for the first available object + * in the cpu_alloc_map. If the allocator would be used frequently with varying sizes + * of objects then this could lead to fragmentation. */ #include #include #define PER_CPU_ALLOC_SIZE 32768 -#define UNITS_PER_CPU (PER_CPU_ALLOC_SIZE / sizeof(int)) +#define UNIT_SIZE sizeof(unsigned long long) +#define UNITS_PER_CPU (PER_CPU_ALLOC_SIZE / UNIT_SIZE) enum unit_type { FREE, END, USED }; @@ -20,19 +36,30 @@ static DEFINE_PER_CPU(int, cpu_area)[UNI #define CPU_DATA_OFFSET ((unsigned long)&per_cpu__cpu_area) -static inline int size_to_units(unsigned long size) +/* + * How many units are needed for an object of a given size + */ +static int size_to_units(unsigned long size) { - return (size + sizeof(int) - 1) / sizeof(int); + return DIV_ROUND_UP(size, UNIT_SIZE); } -static inline void set_map(int start, int length) +/* + * Mark an object as used in the cpu_alloc_map + */ +static void set_map(int start, int length) { cpu_alloc_map[start + length - 1] = END; if (length > 1) memset(cpu_alloc_map + start, USED, length - 1); } -static inline int clear_map(int start) +/* + * Mark an area as freed. + * + * Return the number of units taken up by the object freed. + */ +static int clear_map(int start) { int units = 0; @@ -45,7 +72,12 @@ static inline int clear_map(int start) return units + 1; } -static inline void *cpu_alloc(unsigned long size) +/* + * Allocate an object of a certain size + * + * Returns a per cpu pointer that must not be directly used. + */ +static void *cpu_alloc(unsigned long size) { unsigned long start = 0; int units = size_to_units(size); @@ -69,24 +101,29 @@ static inline void *cpu_alloc(unsigned l } while (1); set_map(start, units); - __count_vm_events(ALLOC_PERCPU, units * sizeof(int)); + __count_vm_events(ALLOC_PERCPU, units * UNIT_SIZE); spin_unlock(&cpu_area_lock); - return (void *)(start * sizeof(int) + CPU_DATA_OFFSET); + return (void *)(start * UNIT_SIZE + CPU_DATA_OFFSET); } +/* + * Free an object. The pointer must be a per cpu pointer allocated + * via cpu_alloc. + */ static inline void cpu_free(void *pcpu) { unsigned long start = (unsigned long)pcpu; + int index; int units; BUG_ON(start < CPU_DATA_OFFSET); - start = (start - CPU_DATA_OFFSET) / sizeof(int); - BUG_ON(cpu_alloc_map[start] == FREE || - start >= UNITS_PER_CPU); + index = (start - CPU_DATA_OFFSET) / UNIT_SIZE; + BUG_ON(cpu_alloc_map[index] == FREE || + index >= UNITS_PER_CPU); spin_lock(&cpu_area_lock); - units = clear_map(start); - __count_vm_events(ALLOC_PERCPU, -units * sizeof(int)); + units = clear_map(index); + __count_vm_events(ALLOC_PERCPU, -units * UNIT_SIZE); spin_unlock(&cpu_area_lock); }