--- include/linux/slub_def.h | 2 - mm/slub.c | 74 +++++++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 29 deletions(-) Index: linux-2.6/mm/slub.c =================================================================== --- linux-2.6.orig/mm/slub.c 2008-03-14 02:42:39.000000000 -0700 +++ linux-2.6/mm/slub.c 2008-03-14 03:22:58.000000000 -0700 @@ -303,7 +303,8 @@ static inline struct kmem_cache_cpu *get * that we can decide if a given pointer is still valid through a cmpxchg. */ #define HALF_BITS_PER_LONG (BITS_PER_LONG / 2) -#define HALF_LONG_MASK ((1UL << HALF_BITS_PER_LONG) - 1) +#define HALF_LONG_SIZE (1UL << HALF_BITS_PER_LONG) +#define HALF_LONG_MASK (HALF_LONG_SIZE - 1) /* * Define a half-long type to use register selection instead of bitmasks. @@ -326,27 +327,28 @@ union halfselect { hulong h; #endif } s; - unsigned long v; + void *v; }; -static inline unsigned long get_high_half(unsigned long v) +static inline unsigned long get_high_half(void *v) { return ((union halfselect)v).s.h; } -static inline unsigned long get_low_half(unsigned long v) +static inline unsigned long get_low_half(void *v) { return ((union halfselect)v).s.l; } -static inline int same_base(unsigned long base, void *object) +static inline int same_base(void *base, void *object) { - return get_high_half(base) == get_high_half((unsigned long)object); + return !get_high_half((void *) + (((unsigned long)base ^ (unsigned long)object))); } -static inline void *make_ptr(unsigned long base, void *freelist) +static inline void *make_ptr(void *base, void *freelist) { - return (void *)(base | get_low_half((unsigned long)freelist)); + return base + get_low_half(freelist); } /* @@ -356,10 +358,10 @@ static inline void *make_ptr(unsigned lo static inline void *make_version(void *version, void *object) { union halfselect ret = { - .s.h = get_high_half((unsigned long)version), - .s.l = get_low_half((unsigned long)object), + .s.h = get_high_half(version), + .s.l = get_low_half(object), }; - return (void *)ret.v; + return ret.v; } #define END ((void *)1) @@ -374,20 +376,31 @@ static inline void **get_freelist(struct return make_ptr(c->base, c->freelist); } +static inline void *baseof(void *x) +{ + return (void *)((unsigned long)x & ~HALF_LONG_MASK); +} +/* Call with irqs disabled */ +static inline void __set_freelist(struct kmem_cache_cpu *c, void **freelist) +{ + c->base = baseof(freelist); + c->freelist = make_version((void *)c->freelist + HALF_LONG_SIZE, + freelist); +} + static inline void set_freelist(struct kmem_cache_cpu *c, void **freelist) { - c->base = get_high_half((unsigned long)freelist) << HALF_BITS_PER_LONG; /* * Detect overflow. Only wrap if not in interrupt. * Slowpath is always taken when a counter overflow is detected. */ - if (unlikely(get_high_half((unsigned long)c->freelist) + if (unlikely(get_high_half(c->freelist) == HALF_LONG_MASK && in_interrupt())) { - c->freelist = make_version((void *)c->freelist, freelist); + c->base = baseof(freelist); + c->freelist = make_version(c->freelist, freelist); return; } - c->freelist = make_version((void *)c->freelist + HALF_LONG_MASK + 1, - freelist); + return __set_freelist(c, freelist); } #else @@ -407,11 +420,16 @@ static inline void **get_freelist(struct return c->freelist; } -static inline void set_freelist(struct kmem_cache_cpu *c, void **p) +static inline void __set_freelist(struct kmem_cache_cpu *c, void **p) { c->freelist = p; } +static inline void set_freelist(struct kmem_cache_cpu *c, void **p) +{ + __set_freelist(c, p); +} + static inline void *make_version(void *version, void *object) { return object; @@ -1553,7 +1571,7 @@ static void deactivate_slab(struct kmem_ page->inuse--; } if (!tail) - set_freelist(c, END); + __set_freelist(c, END); c->page = NULL; unfreeze_slab(s, page, tail); } @@ -1658,7 +1676,7 @@ load_freelist: if (unlikely(SlabDebug(c->page))) goto debug; - set_freelist(c, object[c->offset]); + __set_freelist(c, object[c->offset]); c->page->inuse = slab_objects(s, c->page); c->page->freelist = END; c->node = page_to_nid(c->page); @@ -1746,7 +1764,7 @@ static __always_inline void *slab_alloc( #ifdef SLUB_FASTPATH void *old, *new, *result, *next_object; - unsigned long base; + void *base; preempt_disable(); c = get_cpu_slab(s, raw_smp_processor_id()); @@ -1761,7 +1779,7 @@ fastpath: /* fastpath cmpxchg loop */ base = c->base; if (unlikely(is_end(old) || !node_match(c, node))) goto slowpath; - if (unlikely(get_high_half((unsigned long)old) == HALF_LONG_MASK)) + if (unlikely(get_high_half(old) == HALF_LONG_MASK)) goto slowpath; /* * make_ptr on base should always return a valid pointer; @@ -1796,7 +1814,7 @@ fastpath: /* fastpath cmpxchg loop */ * half-way to overflow. That would be insane to do that much * allocations/free in interrupt handers, but check it anyway. */ - WARN_ON(result - old > -1UL >> 1); + WARN_ON(result - old > (-1UL >> 1)); #endif if (result != old) goto fastpath; /* retry */ @@ -1822,7 +1840,7 @@ got_object: else { object = get_freelist(c); - set_freelist(c, object[c->offset]); + __set_freelist(c, object[c->offset]); stat(c, ALLOC_FASTPATH); } local_irq_restore(flags); @@ -1945,7 +1963,7 @@ static __always_inline void slab_free(st #ifdef SLUB_FASTPATH void *old, *new, *result; - unsigned long base; + void *base; preempt_disable(); c = get_cpu_slab(s, raw_smp_processor_id()); @@ -1965,7 +1983,7 @@ static __always_inline void slab_free(st */ barrier(); base = c->base; - if (unlikely(get_high_half((unsigned long)old) == HALF_LONG_MASK + if (unlikely(get_high_half(old) == HALF_LONG_MASK || !same_base(base, object) || page != c->page || c->node < 0)) { preempt_enable(); @@ -2012,7 +2030,7 @@ static __always_inline void slab_free(st debug_check_no_locks_freed(object, c->objsize); if (likely(page == c->page && c->node >= 0)) { object[c->offset] = get_freelist(c); - set_freelist(c, object); + __set_freelist(c, object); stat(c, FREE_FASTPATH); } else __slab_free(s, page, x, addr, c->offset); @@ -2196,8 +2214,8 @@ static void init_kmem_cache_cpu(struct k struct kmem_cache_cpu *c) { c->page = NULL; - set_freelist(c, make_version(0, END)); - c->base = 0; + __set_freelist(c, make_version(0, END)); + c->base = NULL; c->node = 0; c->offset = s->offset / sizeof(void *); c->objsize = s->objsize; Index: linux-2.6/include/linux/slub_def.h =================================================================== --- linux-2.6.orig/include/linux/slub_def.h 2008-03-14 02:52:58.000000000 -0700 +++ linux-2.6/include/linux/slub_def.h 2008-03-14 02:53:20.000000000 -0700 @@ -35,7 +35,7 @@ enum stat_item { struct kmem_cache_cpu { void **freelist; /* Pointer to first free per cpu object */ struct page *page; /* The slab from which we are allocating */ - unsigned long base; /* Base for fastpath. */ + void *base; /* Base for fastpath. */ 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) */