Commit 2b2d5493 authored by Ingo Molnar's avatar Ingo Molnar Committed by Linus Torvalds

[PATCH] lockdep: annotate SLAB code

Teach special (recursive) locking code to the lock validator.  Has no effect
on non-lockdep kernels.

Fix initialize-locks-via-memcpy assumptions.

Effects on non-lockdep kernels: the subclass nesting parameter is passed into
cache_free_alien() and __cache_free(), and turns one internal
kmem_cache_free() call into an open-coded __cache_free() call.
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarArjan van de Ven <arjan@linux.intel.com>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Cc: Christoph Lameter <clameter@engr.sgi.com>
Cc: Manfred Spraul <manfred@colorfullife.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 897c6ff9
...@@ -1021,7 +1021,8 @@ static void drain_alien_cache(struct kmem_cache *cachep, ...@@ -1021,7 +1021,8 @@ static void drain_alien_cache(struct kmem_cache *cachep,
} }
} }
static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) static inline int cache_free_alien(struct kmem_cache *cachep, void *objp,
int nesting)
{ {
struct slab *slabp = virt_to_slab(objp); struct slab *slabp = virt_to_slab(objp);
int nodeid = slabp->nodeid; int nodeid = slabp->nodeid;
...@@ -1039,7 +1040,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) ...@@ -1039,7 +1040,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
STATS_INC_NODEFREES(cachep); STATS_INC_NODEFREES(cachep);
if (l3->alien && l3->alien[nodeid]) { if (l3->alien && l3->alien[nodeid]) {
alien = l3->alien[nodeid]; alien = l3->alien[nodeid];
spin_lock(&alien->lock); spin_lock_nested(&alien->lock, nesting);
if (unlikely(alien->avail == alien->limit)) { if (unlikely(alien->avail == alien->limit)) {
STATS_INC_ACOVERFLOW(cachep); STATS_INC_ACOVERFLOW(cachep);
__drain_alien_cache(cachep, alien, nodeid); __drain_alien_cache(cachep, alien, nodeid);
...@@ -1068,7 +1069,8 @@ static inline void free_alien_cache(struct array_cache **ac_ptr) ...@@ -1068,7 +1069,8 @@ static inline void free_alien_cache(struct array_cache **ac_ptr)
{ {
} }
static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) static inline int cache_free_alien(struct kmem_cache *cachep, void *objp,
int nesting)
{ {
return 0; return 0;
} }
...@@ -1272,6 +1274,11 @@ static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list, ...@@ -1272,6 +1274,11 @@ static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
local_irq_disable(); local_irq_disable();
memcpy(ptr, list, sizeof(struct kmem_list3)); memcpy(ptr, list, sizeof(struct kmem_list3));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->list_lock);
MAKE_ALL_LISTS(cachep, ptr, nodeid); MAKE_ALL_LISTS(cachep, ptr, nodeid);
cachep->nodelists[nodeid] = ptr; cachep->nodelists[nodeid] = ptr;
local_irq_enable(); local_irq_enable();
...@@ -1398,7 +1405,7 @@ void __init kmem_cache_init(void) ...@@ -1398,7 +1405,7 @@ void __init kmem_cache_init(void)
} }
/* 4) Replace the bootstrap head arrays */ /* 4) Replace the bootstrap head arrays */
{ {
void *ptr; struct array_cache *ptr;
ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL); ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);
...@@ -1406,6 +1413,11 @@ void __init kmem_cache_init(void) ...@@ -1406,6 +1413,11 @@ void __init kmem_cache_init(void)
BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache); BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
memcpy(ptr, cpu_cache_get(&cache_cache), memcpy(ptr, cpu_cache_get(&cache_cache),
sizeof(struct arraycache_init)); sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
cache_cache.array[smp_processor_id()] = ptr; cache_cache.array[smp_processor_id()] = ptr;
local_irq_enable(); local_irq_enable();
...@@ -1416,6 +1428,11 @@ void __init kmem_cache_init(void) ...@@ -1416,6 +1428,11 @@ void __init kmem_cache_init(void)
!= &initarray_generic.cache); != &initarray_generic.cache);
memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep), memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
sizeof(struct arraycache_init)); sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] = malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
ptr; ptr;
local_irq_enable(); local_irq_enable();
...@@ -1743,6 +1760,8 @@ static void slab_destroy_objs(struct kmem_cache *cachep, struct slab *slabp) ...@@ -1743,6 +1760,8 @@ static void slab_destroy_objs(struct kmem_cache *cachep, struct slab *slabp)
} }
#endif #endif
static void __cache_free(struct kmem_cache *cachep, void *objp, int nesting);
/** /**
* slab_destroy - destroy and release all objects in a slab * slab_destroy - destroy and release all objects in a slab
* @cachep: cache pointer being destroyed * @cachep: cache pointer being destroyed
...@@ -1766,8 +1785,17 @@ static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp) ...@@ -1766,8 +1785,17 @@ static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp)
call_rcu(&slab_rcu->head, kmem_rcu_free); call_rcu(&slab_rcu->head, kmem_rcu_free);
} else { } else {
kmem_freepages(cachep, addr); kmem_freepages(cachep, addr);
if (OFF_SLAB(cachep)) if (OFF_SLAB(cachep)) {
kmem_cache_free(cachep->slabp_cache, slabp); unsigned long flags;
/*
* lockdep: we may nest inside an already held
* ac->lock, so pass in a nesting flag:
*/
local_irq_save(flags);
__cache_free(cachep->slabp_cache, slabp, 1);
local_irq_restore(flags);
}
} }
} }
...@@ -3072,7 +3100,16 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, ...@@ -3072,7 +3100,16 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
if (slabp->inuse == 0) { if (slabp->inuse == 0) {
if (l3->free_objects > l3->free_limit) { if (l3->free_objects > l3->free_limit) {
l3->free_objects -= cachep->num; l3->free_objects -= cachep->num;
/*
* It is safe to drop the lock. The slab is
* no longer linked to the cache. cachep
* cannot disappear - we are using it and
* all destruction of caches must be
* serialized properly by the user.
*/
spin_unlock(&l3->list_lock);
slab_destroy(cachep, slabp); slab_destroy(cachep, slabp);
spin_lock(&l3->list_lock);
} else { } else {
list_add(&slabp->list, &l3->slabs_free); list_add(&slabp->list, &l3->slabs_free);
} }
...@@ -3098,7 +3135,7 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) ...@@ -3098,7 +3135,7 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
#endif #endif
check_irq_off(); check_irq_off();
l3 = cachep->nodelists[node]; l3 = cachep->nodelists[node];
spin_lock(&l3->list_lock); spin_lock_nested(&l3->list_lock, SINGLE_DEPTH_NESTING);
if (l3->shared) { if (l3->shared) {
struct array_cache *shared_array = l3->shared; struct array_cache *shared_array = l3->shared;
int max = shared_array->limit - shared_array->avail; int max = shared_array->limit - shared_array->avail;
...@@ -3141,14 +3178,14 @@ free_done: ...@@ -3141,14 +3178,14 @@ free_done:
* Release an obj back to its cache. If the obj has a constructed state, it must * Release an obj back to its cache. If the obj has a constructed state, it must
* be in this state _before_ it is released. Called with disabled ints. * be in this state _before_ it is released. Called with disabled ints.
*/ */
static inline void __cache_free(struct kmem_cache *cachep, void *objp) static void __cache_free(struct kmem_cache *cachep, void *objp, int nesting)
{ {
struct array_cache *ac = cpu_cache_get(cachep); struct array_cache *ac = cpu_cache_get(cachep);
check_irq_off(); check_irq_off();
objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
if (cache_free_alien(cachep, objp)) if (cache_free_alien(cachep, objp, nesting))
return; return;
if (likely(ac->avail < ac->limit)) { if (likely(ac->avail < ac->limit)) {
...@@ -3387,7 +3424,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) ...@@ -3387,7 +3424,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)
BUG_ON(virt_to_cache(objp) != cachep); BUG_ON(virt_to_cache(objp) != cachep);
local_irq_save(flags); local_irq_save(flags);
__cache_free(cachep, objp); __cache_free(cachep, objp, 0);
local_irq_restore(flags); local_irq_restore(flags);
} }
EXPORT_SYMBOL(kmem_cache_free); EXPORT_SYMBOL(kmem_cache_free);
...@@ -3412,7 +3449,7 @@ void kfree(const void *objp) ...@@ -3412,7 +3449,7 @@ void kfree(const void *objp)
kfree_debugcheck(objp); kfree_debugcheck(objp);
c = virt_to_cache(objp); c = virt_to_cache(objp);
debug_check_no_locks_freed(objp, obj_size(c)); debug_check_no_locks_freed(objp, obj_size(c));
__cache_free(c, (void *)objp); __cache_free(c, (void *)objp, 0);
local_irq_restore(flags); local_irq_restore(flags);
} }
EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kfree);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment