diff options
author | Nick Piggin <nickpiggin@yahoo.com.au> | 2007-05-17 01:10:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-17 08:23:02 -0400 |
commit | afc0cedbe9138e3e8b38bfa1e4dfd01a2c537d62 (patch) | |
tree | 0df03f95645ef76a387dd541da062b682319c921 /mm/slob.c | |
parent | b2cd64153b94473f6bd82448a68b8e8c041676ea (diff) |
slob: implement RCU freeing
The SLOB allocator should implement SLAB_DESTROY_BY_RCU correctly, because
even on UP, RCU freeing semantics are not equivalent to simply freeing
immediately. This also allows SLOB to be used on SMP.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Acked-by: Matt Mackall <mpm@selenic.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/slob.c')
-rw-r--r-- | mm/slob.c | 52 |
1 files changed, 45 insertions, 7 deletions
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/init.h> | 35 | #include <linux/init.h> |
36 | #include <linux/module.h> | 36 | #include <linux/module.h> |
37 | #include <linux/timer.h> | 37 | #include <linux/timer.h> |
38 | #include <linux/rcupdate.h> | ||
38 | 39 | ||
39 | struct slob_block { | 40 | struct slob_block { |
40 | int units; | 41 | int units; |
@@ -53,6 +54,16 @@ struct bigblock { | |||
53 | }; | 54 | }; |
54 | typedef struct bigblock bigblock_t; | 55 | typedef struct bigblock bigblock_t; |
55 | 56 | ||
57 | /* | ||
58 | * struct slob_rcu is inserted at the tail of allocated slob blocks, which | ||
59 | * were created with a SLAB_DESTROY_BY_RCU slab. slob_rcu is used to free | ||
60 | * the block using call_rcu. | ||
61 | */ | ||
62 | struct slob_rcu { | ||
63 | struct rcu_head head; | ||
64 | int size; | ||
65 | }; | ||
66 | |||
56 | static slob_t arena = { .next = &arena, .units = 1 }; | 67 | static slob_t arena = { .next = &arena, .units = 1 }; |
57 | static slob_t *slobfree = &arena; | 68 | static slob_t *slobfree = &arena; |
58 | static bigblock_t *bigblocks; | 69 | static bigblock_t *bigblocks; |
@@ -266,6 +277,7 @@ size_t ksize(const void *block) | |||
266 | 277 | ||
267 | struct kmem_cache { | 278 | struct kmem_cache { |
268 | unsigned int size, align; | 279 | unsigned int size, align; |
280 | unsigned long flags; | ||
269 | const char *name; | 281 | const char *name; |
270 | void (*ctor)(void *, struct kmem_cache *, unsigned long); | 282 | void (*ctor)(void *, struct kmem_cache *, unsigned long); |
271 | void (*dtor)(void *, struct kmem_cache *, unsigned long); | 283 | void (*dtor)(void *, struct kmem_cache *, unsigned long); |
@@ -283,6 +295,12 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, | |||
283 | if (c) { | 295 | if (c) { |
284 | c->name = name; | 296 | c->name = name; |
285 | c->size = size; | 297 | c->size = size; |
298 | if (flags & SLAB_DESTROY_BY_RCU) { | ||
299 | BUG_ON(dtor); | ||
300 | /* leave room for rcu footer at the end of object */ | ||
301 | c->size += sizeof(struct slob_rcu); | ||
302 | } | ||
303 | c->flags = flags; | ||
286 | c->ctor = ctor; | 304 | c->ctor = ctor; |
287 | c->dtor = dtor; | 305 | c->dtor = dtor; |
288 | /* ignore alignment unless it's forced */ | 306 | /* ignore alignment unless it's forced */ |
@@ -328,15 +346,35 @@ void *kmem_cache_zalloc(struct kmem_cache *c, gfp_t flags) | |||
328 | } | 346 | } |
329 | EXPORT_SYMBOL(kmem_cache_zalloc); | 347 | EXPORT_SYMBOL(kmem_cache_zalloc); |
330 | 348 | ||
331 | void kmem_cache_free(struct kmem_cache *c, void *b) | 349 | static void __kmem_cache_free(void *b, int size) |
332 | { | 350 | { |
333 | if (c->dtor) | 351 | if (size < PAGE_SIZE) |
334 | c->dtor(b, c, 0); | 352 | slob_free(b, size); |
335 | |||
336 | if (c->size < PAGE_SIZE) | ||
337 | slob_free(b, c->size); | ||
338 | else | 353 | else |
339 | free_pages((unsigned long)b, get_order(c->size)); | 354 | free_pages((unsigned long)b, get_order(size)); |
355 | } | ||
356 | |||
357 | static void kmem_rcu_free(struct rcu_head *head) | ||
358 | { | ||
359 | struct slob_rcu *slob_rcu = (struct slob_rcu *)head; | ||
360 | void *b = (void *)slob_rcu - (slob_rcu->size - sizeof(struct slob_rcu)); | ||
361 | |||
362 | __kmem_cache_free(b, slob_rcu->size); | ||
363 | } | ||
364 | |||
365 | void kmem_cache_free(struct kmem_cache *c, void *b) | ||
366 | { | ||
367 | if (unlikely(c->flags & SLAB_DESTROY_BY_RCU)) { | ||
368 | struct slob_rcu *slob_rcu; | ||
369 | slob_rcu = b + (c->size - sizeof(struct slob_rcu)); | ||
370 | INIT_RCU_HEAD(&slob_rcu->head); | ||
371 | slob_rcu->size = c->size; | ||
372 | call_rcu(&slob_rcu->head, kmem_rcu_free); | ||
373 | } else { | ||
374 | if (c->dtor) | ||
375 | c->dtor(b, c, 0); | ||
376 | __kmem_cache_free(b, c->size); | ||
377 | } | ||
340 | } | 378 | } |
341 | EXPORT_SYMBOL(kmem_cache_free); | 379 | EXPORT_SYMBOL(kmem_cache_free); |
342 | 380 | ||