Commit 6b377547 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'core-debugobjects-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull debugobjects updates from Thomas Gleixner:
 "A set of updates for debugobjects:

  - A series of changes to make debugobjects more scalable by
    introducing per cpu pools and reducing the number of lock
    acquisitions

  - debugfs cleanup"

* 'core-debugobjects-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  debugobjects: Move printk out of db->lock critical sections
  debugobjects: Less aggressive freeing of excess debug objects
  debugobjects: Reduce number of pool_lock acquisitions in fill_pool()
  debugobjects: Percpu pool lookahead freeing/allocation
  debugobjects: Add percpu free pools
  debugobjects: No need to check return value of debugfs_create()
parents 1758fedd d5f34153
...@@ -25,16 +25,37 @@ ...@@ -25,16 +25,37 @@
#define ODEBUG_POOL_SIZE 1024 #define ODEBUG_POOL_SIZE 1024
#define ODEBUG_POOL_MIN_LEVEL 256 #define ODEBUG_POOL_MIN_LEVEL 256
#define ODEBUG_POOL_PERCPU_SIZE 64
#define ODEBUG_BATCH_SIZE 16
#define ODEBUG_CHUNK_SHIFT PAGE_SHIFT #define ODEBUG_CHUNK_SHIFT PAGE_SHIFT
#define ODEBUG_CHUNK_SIZE (1 << ODEBUG_CHUNK_SHIFT) #define ODEBUG_CHUNK_SIZE (1 << ODEBUG_CHUNK_SHIFT)
#define ODEBUG_CHUNK_MASK (~(ODEBUG_CHUNK_SIZE - 1)) #define ODEBUG_CHUNK_MASK (~(ODEBUG_CHUNK_SIZE - 1))
/*
* We limit the freeing of debug objects via workqueue at a maximum
* frequency of 10Hz and about 1024 objects for each freeing operation.
* So it is freeing at most 10k debug objects per second.
*/
#define ODEBUG_FREE_WORK_MAX 1024
#define ODEBUG_FREE_WORK_DELAY DIV_ROUND_UP(HZ, 10)
struct debug_bucket { struct debug_bucket {
struct hlist_head list; struct hlist_head list;
raw_spinlock_t lock; raw_spinlock_t lock;
}; };
/*
* Debug object percpu free list
* Access is protected by disabling irq
*/
struct debug_percpu_free {
struct hlist_head free_objs;
int obj_free;
};
static DEFINE_PER_CPU(struct debug_percpu_free, percpu_obj_pool);
static struct debug_bucket obj_hash[ODEBUG_HASH_SIZE]; static struct debug_bucket obj_hash[ODEBUG_HASH_SIZE];
static struct debug_obj obj_static_pool[ODEBUG_POOL_SIZE] __initdata; static struct debug_obj obj_static_pool[ODEBUG_POOL_SIZE] __initdata;
...@@ -44,13 +65,20 @@ static DEFINE_RAW_SPINLOCK(pool_lock); ...@@ -44,13 +65,20 @@ static DEFINE_RAW_SPINLOCK(pool_lock);
static HLIST_HEAD(obj_pool); static HLIST_HEAD(obj_pool);
static HLIST_HEAD(obj_to_free); static HLIST_HEAD(obj_to_free);
/*
* Because of the presence of percpu free pools, obj_pool_free will
* under-count those in the percpu free pools. Similarly, obj_pool_used
* will over-count those in the percpu free pools. Adjustments will be
* made at debug_stats_show(). Both obj_pool_min_free and obj_pool_max_used
* can be off.
*/
static int obj_pool_min_free = ODEBUG_POOL_SIZE; static int obj_pool_min_free = ODEBUG_POOL_SIZE;
static int obj_pool_free = ODEBUG_POOL_SIZE; static int obj_pool_free = ODEBUG_POOL_SIZE;
static int obj_pool_used; static int obj_pool_used;
static int obj_pool_max_used; static int obj_pool_max_used;
static bool obj_freeing;
/* The number of objs on the global free list */ /* The number of objs on the global free list */
static int obj_nr_tofree; static int obj_nr_tofree;
static struct kmem_cache *obj_cache;
static int debug_objects_maxchain __read_mostly; static int debug_objects_maxchain __read_mostly;
static int __maybe_unused debug_objects_maxchecked __read_mostly; static int __maybe_unused debug_objects_maxchecked __read_mostly;
...@@ -63,6 +91,7 @@ static int debug_objects_pool_size __read_mostly ...@@ -63,6 +91,7 @@ static int debug_objects_pool_size __read_mostly
static int debug_objects_pool_min_level __read_mostly static int debug_objects_pool_min_level __read_mostly
= ODEBUG_POOL_MIN_LEVEL; = ODEBUG_POOL_MIN_LEVEL;
static struct debug_obj_descr *descr_test __read_mostly; static struct debug_obj_descr *descr_test __read_mostly;
static struct kmem_cache *obj_cache __read_mostly;
/* /*
* Track numbers of kmem_cache_alloc()/free() calls done. * Track numbers of kmem_cache_alloc()/free() calls done.
...@@ -71,7 +100,7 @@ static int debug_objects_allocated; ...@@ -71,7 +100,7 @@ static int debug_objects_allocated;
static int debug_objects_freed; static int debug_objects_freed;
static void free_obj_work(struct work_struct *work); static void free_obj_work(struct work_struct *work);
static DECLARE_WORK(debug_obj_work, free_obj_work); static DECLARE_DELAYED_WORK(debug_obj_work, free_obj_work);
static int __init enable_object_debug(char *str) static int __init enable_object_debug(char *str)
{ {
...@@ -100,7 +129,7 @@ static const char *obj_states[ODEBUG_STATE_MAX] = { ...@@ -100,7 +129,7 @@ static const char *obj_states[ODEBUG_STATE_MAX] = {
static void fill_pool(void) static void fill_pool(void)
{ {
gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN; gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN;
struct debug_obj *new, *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
if (likely(obj_pool_free >= debug_objects_pool_min_level)) if (likely(obj_pool_free >= debug_objects_pool_min_level))
...@@ -116,7 +145,7 @@ static void fill_pool(void) ...@@ -116,7 +145,7 @@ static void fill_pool(void)
* Recheck with the lock held as the worker thread might have * Recheck with the lock held as the worker thread might have
* won the race and freed the global free list already. * won the race and freed the global free list already.
*/ */
if (obj_nr_tofree) { while (obj_nr_tofree && (obj_pool_free < obj_pool_min_free)) {
obj = hlist_entry(obj_to_free.first, typeof(*obj), node); obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
hlist_del(&obj->node); hlist_del(&obj->node);
obj_nr_tofree--; obj_nr_tofree--;
...@@ -130,15 +159,23 @@ static void fill_pool(void) ...@@ -130,15 +159,23 @@ static void fill_pool(void)
return; return;
while (obj_pool_free < debug_objects_pool_min_level) { while (obj_pool_free < debug_objects_pool_min_level) {
struct debug_obj *new[ODEBUG_BATCH_SIZE];
int cnt;
new = kmem_cache_zalloc(obj_cache, gfp); for (cnt = 0; cnt < ODEBUG_BATCH_SIZE; cnt++) {
if (!new) new[cnt] = kmem_cache_zalloc(obj_cache, gfp);
if (!new[cnt])
break;
}
if (!cnt)
return; return;
raw_spin_lock_irqsave(&pool_lock, flags); raw_spin_lock_irqsave(&pool_lock, flags);
hlist_add_head(&new->node, &obj_pool); while (cnt) {
hlist_add_head(&new[--cnt]->node, &obj_pool);
debug_objects_allocated++; debug_objects_allocated++;
obj_pool_free++; obj_pool_free++;
}
raw_spin_unlock_irqrestore(&pool_lock, flags); raw_spin_unlock_irqrestore(&pool_lock, flags);
} }
} }
...@@ -162,6 +199,21 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b) ...@@ -162,6 +199,21 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b)
return NULL; return NULL;
} }
/*
* Allocate a new object from the hlist
*/
static struct debug_obj *__alloc_object(struct hlist_head *list)
{
struct debug_obj *obj = NULL;
if (list->first) {
obj = hlist_entry(list->first, typeof(*obj), node);
hlist_del(&obj->node);
}
return obj;
}
/* /*
* Allocate a new object. If the pool is empty, switch off the debugger. * Allocate a new object. If the pool is empty, switch off the debugger.
* Must be called with interrupts disabled. * Must be called with interrupts disabled.
...@@ -169,30 +221,60 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b) ...@@ -169,30 +221,60 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b)
static struct debug_obj * static struct debug_obj *
alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr) alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr)
{ {
struct debug_obj *obj = NULL; struct debug_percpu_free *percpu_pool = this_cpu_ptr(&percpu_obj_pool);
struct debug_obj *obj;
if (likely(obj_cache)) {
obj = __alloc_object(&percpu_pool->free_objs);
if (obj) {
percpu_pool->obj_free--;
goto init_obj;
}
}
raw_spin_lock(&pool_lock); raw_spin_lock(&pool_lock);
if (obj_pool.first) { obj = __alloc_object(&obj_pool);
obj = hlist_entry(obj_pool.first, typeof(*obj), node); if (obj) {
obj_pool_used++;
obj_pool_free--;
obj->object = addr; /*
obj->descr = descr; * Looking ahead, allocate one batch of debug objects and
obj->state = ODEBUG_STATE_NONE; * put them into the percpu free pool.
obj->astate = 0; */
hlist_del(&obj->node); if (likely(obj_cache)) {
int i;
hlist_add_head(&obj->node, &b->list); for (i = 0; i < ODEBUG_BATCH_SIZE; i++) {
struct debug_obj *obj2;
obj2 = __alloc_object(&obj_pool);
if (!obj2)
break;
hlist_add_head(&obj2->node,
&percpu_pool->free_objs);
percpu_pool->obj_free++;
obj_pool_used++; obj_pool_used++;
obj_pool_free--;
}
}
if (obj_pool_used > obj_pool_max_used) if (obj_pool_used > obj_pool_max_used)
obj_pool_max_used = obj_pool_used; obj_pool_max_used = obj_pool_used;
obj_pool_free--;
if (obj_pool_free < obj_pool_min_free) if (obj_pool_free < obj_pool_min_free)
obj_pool_min_free = obj_pool_free; obj_pool_min_free = obj_pool_free;
} }
raw_spin_unlock(&pool_lock); raw_spin_unlock(&pool_lock);
init_obj:
if (obj) {
obj->object = addr;
obj->descr = descr;
obj->state = ODEBUG_STATE_NONE;
obj->astate = 0;
hlist_add_head(&obj->node, &b->list);
}
return obj; return obj;
} }
...@@ -209,13 +291,19 @@ static void free_obj_work(struct work_struct *work) ...@@ -209,13 +291,19 @@ static void free_obj_work(struct work_struct *work)
unsigned long flags; unsigned long flags;
HLIST_HEAD(tofree); HLIST_HEAD(tofree);
WRITE_ONCE(obj_freeing, false);
if (!raw_spin_trylock_irqsave(&pool_lock, flags)) if (!raw_spin_trylock_irqsave(&pool_lock, flags))
return; return;
if (obj_pool_free >= debug_objects_pool_size)
goto free_objs;
/* /*
* The objs on the pool list might be allocated before the work is * The objs on the pool list might be allocated before the work is
* run, so recheck if pool list it full or not, if not fill pool * run, so recheck if pool list it full or not, if not fill pool
* list from the global free list * list from the global free list. As it is likely that a workload
* may be gearing up to use more and more objects, don't free any
* of them until the next round.
*/ */
while (obj_nr_tofree && obj_pool_free < debug_objects_pool_size) { while (obj_nr_tofree && obj_pool_free < debug_objects_pool_size) {
obj = hlist_entry(obj_to_free.first, typeof(*obj), node); obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
...@@ -224,7 +312,10 @@ static void free_obj_work(struct work_struct *work) ...@@ -224,7 +312,10 @@ static void free_obj_work(struct work_struct *work)
obj_pool_free++; obj_pool_free++;
obj_nr_tofree--; obj_nr_tofree--;
} }
raw_spin_unlock_irqrestore(&pool_lock, flags);
return;
free_objs:
/* /*
* Pool list is already full and there are still objs on the free * Pool list is already full and there are still objs on the free
* list. Move remaining free objs to a temporary list to free the * list. Move remaining free objs to a temporary list to free the
...@@ -243,24 +334,86 @@ static void free_obj_work(struct work_struct *work) ...@@ -243,24 +334,86 @@ static void free_obj_work(struct work_struct *work)
} }
} }
static bool __free_object(struct debug_obj *obj) static void __free_object(struct debug_obj *obj)
{ {
struct debug_obj *objs[ODEBUG_BATCH_SIZE];
struct debug_percpu_free *percpu_pool;
int lookahead_count = 0;
unsigned long flags; unsigned long flags;
bool work; bool work;
raw_spin_lock_irqsave(&pool_lock, flags); local_irq_save(flags);
work = (obj_pool_free > debug_objects_pool_size) && obj_cache; if (!obj_cache)
goto free_to_obj_pool;
/*
* Try to free it into the percpu pool first.
*/
percpu_pool = this_cpu_ptr(&percpu_obj_pool);
if (percpu_pool->obj_free < ODEBUG_POOL_PERCPU_SIZE) {
hlist_add_head(&obj->node, &percpu_pool->free_objs);
percpu_pool->obj_free++;
local_irq_restore(flags);
return;
}
/*
* As the percpu pool is full, look ahead and pull out a batch
* of objects from the percpu pool and free them as well.
*/
for (; lookahead_count < ODEBUG_BATCH_SIZE; lookahead_count++) {
objs[lookahead_count] = __alloc_object(&percpu_pool->free_objs);
if (!objs[lookahead_count])
break;
percpu_pool->obj_free--;
}
free_to_obj_pool:
raw_spin_lock(&pool_lock);
work = (obj_pool_free > debug_objects_pool_size) && obj_cache &&
(obj_nr_tofree < ODEBUG_FREE_WORK_MAX);
obj_pool_used--; obj_pool_used--;
if (work) { if (work) {
obj_nr_tofree++; obj_nr_tofree++;
hlist_add_head(&obj->node, &obj_to_free); hlist_add_head(&obj->node, &obj_to_free);
if (lookahead_count) {
obj_nr_tofree += lookahead_count;
obj_pool_used -= lookahead_count;
while (lookahead_count) {
hlist_add_head(&objs[--lookahead_count]->node,
&obj_to_free);
}
}
if ((obj_pool_free > debug_objects_pool_size) &&
(obj_nr_tofree < ODEBUG_FREE_WORK_MAX)) {
int i;
/*
* Free one more batch of objects from obj_pool.
*/
for (i = 0; i < ODEBUG_BATCH_SIZE; i++) {
obj = __alloc_object(&obj_pool);
hlist_add_head(&obj->node, &obj_to_free);
obj_pool_free--;
obj_nr_tofree++;
}
}
} else { } else {
obj_pool_free++; obj_pool_free++;
hlist_add_head(&obj->node, &obj_pool); hlist_add_head(&obj->node, &obj_pool);
if (lookahead_count) {
obj_pool_free += lookahead_count;
obj_pool_used -= lookahead_count;
while (lookahead_count) {
hlist_add_head(&objs[--lookahead_count]->node,
&obj_pool);
} }
raw_spin_unlock_irqrestore(&pool_lock, flags); }
return work; }
raw_spin_unlock(&pool_lock);
local_irq_restore(flags);
} }
/* /*
...@@ -269,8 +422,11 @@ static bool __free_object(struct debug_obj *obj) ...@@ -269,8 +422,11 @@ static bool __free_object(struct debug_obj *obj)
*/ */
static void free_object(struct debug_obj *obj) static void free_object(struct debug_obj *obj)
{ {
if (__free_object(obj)) __free_object(obj);
schedule_work(&debug_obj_work); if (!obj_freeing && obj_nr_tofree) {
WRITE_ONCE(obj_freeing, true);
schedule_delayed_work(&debug_obj_work, ODEBUG_FREE_WORK_DELAY);
}
} }
/* /*
...@@ -372,6 +528,7 @@ static void ...@@ -372,6 +528,7 @@ static void
__debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack) __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack)
{ {
enum debug_obj_state state; enum debug_obj_state state;
bool check_stack = false;
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
...@@ -391,7 +548,7 @@ __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack) ...@@ -391,7 +548,7 @@ __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack)
debug_objects_oom(); debug_objects_oom();
return; return;
} }
debug_object_is_on_stack(addr, onstack); check_stack = true;
} }
switch (obj->state) { switch (obj->state) {
...@@ -402,20 +559,23 @@ __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack) ...@@ -402,20 +559,23 @@ __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack)
break; break;
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
debug_print_object(obj, "init");
state = obj->state; state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "init");
debug_object_fixup(descr->fixup_init, addr, state); debug_object_fixup(descr->fixup_init, addr, state);
return; return;
case ODEBUG_STATE_DESTROYED: case ODEBUG_STATE_DESTROYED:
raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "init"); debug_print_object(obj, "init");
break; return;
default: default:
break; break;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (check_stack)
debug_object_is_on_stack(addr, onstack);
} }
/** /**
...@@ -473,6 +633,8 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr) ...@@ -473,6 +633,8 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr)
obj = lookup_object(addr, db); obj = lookup_object(addr, db);
if (obj) { if (obj) {
bool print_object = false;
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_INIT: case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE: case ODEBUG_STATE_INACTIVE:
...@@ -481,14 +643,14 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr) ...@@ -481,14 +643,14 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr)
break; break;
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
debug_print_object(obj, "activate");
state = obj->state; state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "activate");
ret = debug_object_fixup(descr->fixup_activate, addr, state); ret = debug_object_fixup(descr->fixup_activate, addr, state);
return ret ? 0 : -EINVAL; return ret ? 0 : -EINVAL;
case ODEBUG_STATE_DESTROYED: case ODEBUG_STATE_DESTROYED:
debug_print_object(obj, "activate"); print_object = true;
ret = -EINVAL; ret = -EINVAL;
break; break;
default: default:
...@@ -496,10 +658,13 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr) ...@@ -496,10 +658,13 @@ int debug_object_activate(void *addr, struct debug_obj_descr *descr)
break; break;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (print_object)
debug_print_object(obj, "activate");
return ret; return ret;
} }
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
/* /*
* We are here when a static object is activated. We * We are here when a static object is activated. We
* let the type specific code confirm whether this is * let the type specific code confirm whether this is
...@@ -531,6 +696,7 @@ void debug_object_deactivate(void *addr, struct debug_obj_descr *descr) ...@@ -531,6 +696,7 @@ void debug_object_deactivate(void *addr, struct debug_obj_descr *descr)
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -548,24 +714,27 @@ void debug_object_deactivate(void *addr, struct debug_obj_descr *descr) ...@@ -548,24 +714,27 @@ void debug_object_deactivate(void *addr, struct debug_obj_descr *descr)
if (!obj->astate) if (!obj->astate)
obj->state = ODEBUG_STATE_INACTIVE; obj->state = ODEBUG_STATE_INACTIVE;
else else
debug_print_object(obj, "deactivate"); print_object = true;
break; break;
case ODEBUG_STATE_DESTROYED: case ODEBUG_STATE_DESTROYED:
debug_print_object(obj, "deactivate"); print_object = true;
break; break;
default: default:
break; break;
} }
} else { }
raw_spin_unlock_irqrestore(&db->lock, flags);
if (!obj) {
struct debug_obj o = { .object = addr, struct debug_obj o = { .object = addr,
.state = ODEBUG_STATE_NOTAVAILABLE, .state = ODEBUG_STATE_NOTAVAILABLE,
.descr = descr }; .descr = descr };
debug_print_object(&o, "deactivate"); debug_print_object(&o, "deactivate");
} else if (print_object) {
debug_print_object(obj, "deactivate");
} }
raw_spin_unlock_irqrestore(&db->lock, flags);
} }
EXPORT_SYMBOL_GPL(debug_object_deactivate); EXPORT_SYMBOL_GPL(debug_object_deactivate);
...@@ -580,6 +749,7 @@ void debug_object_destroy(void *addr, struct debug_obj_descr *descr) ...@@ -580,6 +749,7 @@ void debug_object_destroy(void *addr, struct debug_obj_descr *descr)
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -599,20 +769,22 @@ void debug_object_destroy(void *addr, struct debug_obj_descr *descr) ...@@ -599,20 +769,22 @@ void debug_object_destroy(void *addr, struct debug_obj_descr *descr)
obj->state = ODEBUG_STATE_DESTROYED; obj->state = ODEBUG_STATE_DESTROYED;
break; break;
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
debug_print_object(obj, "destroy");
state = obj->state; state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "destroy");
debug_object_fixup(descr->fixup_destroy, addr, state); debug_object_fixup(descr->fixup_destroy, addr, state);
return; return;
case ODEBUG_STATE_DESTROYED: case ODEBUG_STATE_DESTROYED:
debug_print_object(obj, "destroy"); print_object = true;
break; break;
default: default:
break; break;
} }
out_unlock: out_unlock:
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
if (print_object)
debug_print_object(obj, "destroy");
} }
EXPORT_SYMBOL_GPL(debug_object_destroy); EXPORT_SYMBOL_GPL(debug_object_destroy);
...@@ -641,9 +813,9 @@ void debug_object_free(void *addr, struct debug_obj_descr *descr) ...@@ -641,9 +813,9 @@ void debug_object_free(void *addr, struct debug_obj_descr *descr)
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
debug_print_object(obj, "free");
state = obj->state; state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "free");
debug_object_fixup(descr->fixup_free, addr, state); debug_object_fixup(descr->fixup_free, addr, state);
return; return;
default: default:
...@@ -716,6 +888,7 @@ debug_object_active_state(void *addr, struct debug_obj_descr *descr, ...@@ -716,6 +888,7 @@ debug_object_active_state(void *addr, struct debug_obj_descr *descr,
struct debug_bucket *db; struct debug_bucket *db;
struct debug_obj *obj; struct debug_obj *obj;
unsigned long flags; unsigned long flags;
bool print_object = false;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
...@@ -731,22 +904,25 @@ debug_object_active_state(void *addr, struct debug_obj_descr *descr, ...@@ -731,22 +904,25 @@ debug_object_active_state(void *addr, struct debug_obj_descr *descr,
if (obj->astate == expect) if (obj->astate == expect)
obj->astate = next; obj->astate = next;
else else
debug_print_object(obj, "active_state"); print_object = true;
break; break;
default: default:
debug_print_object(obj, "active_state"); print_object = true;
break; break;
} }
} else { }
raw_spin_unlock_irqrestore(&db->lock, flags);
if (!obj) {
struct debug_obj o = { .object = addr, struct debug_obj o = { .object = addr,
.state = ODEBUG_STATE_NOTAVAILABLE, .state = ODEBUG_STATE_NOTAVAILABLE,
.descr = descr }; .descr = descr };
debug_print_object(&o, "active_state"); debug_print_object(&o, "active_state");
} else if (print_object) {
debug_print_object(obj, "active_state");
} }
raw_spin_unlock_irqrestore(&db->lock, flags);
} }
EXPORT_SYMBOL_GPL(debug_object_active_state); EXPORT_SYMBOL_GPL(debug_object_active_state);
...@@ -760,7 +936,6 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) ...@@ -760,7 +936,6 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size)
struct hlist_node *tmp; struct hlist_node *tmp;
struct debug_obj *obj; struct debug_obj *obj;
int cnt, objs_checked = 0; int cnt, objs_checked = 0;
bool work = false;
saddr = (unsigned long) address; saddr = (unsigned long) address;
eaddr = saddr + size; eaddr = saddr + size;
...@@ -782,16 +957,16 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) ...@@ -782,16 +957,16 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size)
switch (obj->state) { switch (obj->state) {
case ODEBUG_STATE_ACTIVE: case ODEBUG_STATE_ACTIVE:
debug_print_object(obj, "free");
descr = obj->descr; descr = obj->descr;
state = obj->state; state = obj->state;
raw_spin_unlock_irqrestore(&db->lock, flags); raw_spin_unlock_irqrestore(&db->lock, flags);
debug_print_object(obj, "free");
debug_object_fixup(descr->fixup_free, debug_object_fixup(descr->fixup_free,
(void *) oaddr, state); (void *) oaddr, state);
goto repeat; goto repeat;
default: default:
hlist_del(&obj->node); hlist_del(&obj->node);
work |= __free_object(obj); __free_object(obj);
break; break;
} }
} }
...@@ -807,8 +982,10 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) ...@@ -807,8 +982,10 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size)
debug_objects_maxchecked = objs_checked; debug_objects_maxchecked = objs_checked;
/* Schedule work to actually kmem_cache_free() objects */ /* Schedule work to actually kmem_cache_free() objects */
if (work) if (!obj_freeing && obj_nr_tofree) {
schedule_work(&debug_obj_work); WRITE_ONCE(obj_freeing, true);
schedule_delayed_work(&debug_obj_work, ODEBUG_FREE_WORK_DELAY);
}
} }
void debug_check_no_obj_freed(const void *address, unsigned long size) void debug_check_no_obj_freed(const void *address, unsigned long size)
...@@ -822,13 +999,19 @@ void debug_check_no_obj_freed(const void *address, unsigned long size) ...@@ -822,13 +999,19 @@ void debug_check_no_obj_freed(const void *address, unsigned long size)
static int debug_stats_show(struct seq_file *m, void *v) static int debug_stats_show(struct seq_file *m, void *v)
{ {
int cpu, obj_percpu_free = 0;
for_each_possible_cpu(cpu)
obj_percpu_free += per_cpu(percpu_obj_pool.obj_free, cpu);
seq_printf(m, "max_chain :%d\n", debug_objects_maxchain); seq_printf(m, "max_chain :%d\n", debug_objects_maxchain);
seq_printf(m, "max_checked :%d\n", debug_objects_maxchecked); seq_printf(m, "max_checked :%d\n", debug_objects_maxchecked);
seq_printf(m, "warnings :%d\n", debug_objects_warnings); seq_printf(m, "warnings :%d\n", debug_objects_warnings);
seq_printf(m, "fixups :%d\n", debug_objects_fixups); seq_printf(m, "fixups :%d\n", debug_objects_fixups);
seq_printf(m, "pool_free :%d\n", obj_pool_free); seq_printf(m, "pool_free :%d\n", obj_pool_free + obj_percpu_free);
seq_printf(m, "pool_pcp_free :%d\n", obj_percpu_free);
seq_printf(m, "pool_min_free :%d\n", obj_pool_min_free); seq_printf(m, "pool_min_free :%d\n", obj_pool_min_free);
seq_printf(m, "pool_used :%d\n", obj_pool_used); seq_printf(m, "pool_used :%d\n", obj_pool_used - obj_percpu_free);
seq_printf(m, "pool_max_used :%d\n", obj_pool_max_used); seq_printf(m, "pool_max_used :%d\n", obj_pool_max_used);
seq_printf(m, "on_free_list :%d\n", obj_nr_tofree); seq_printf(m, "on_free_list :%d\n", obj_nr_tofree);
seq_printf(m, "objs_allocated:%d\n", debug_objects_allocated); seq_printf(m, "objs_allocated:%d\n", debug_objects_allocated);
...@@ -850,26 +1033,16 @@ static const struct file_operations debug_stats_fops = { ...@@ -850,26 +1033,16 @@ static const struct file_operations debug_stats_fops = {
static int __init debug_objects_init_debugfs(void) static int __init debug_objects_init_debugfs(void)
{ {
struct dentry *dbgdir, *dbgstats; struct dentry *dbgdir;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return 0; return 0;
dbgdir = debugfs_create_dir("debug_objects", NULL); dbgdir = debugfs_create_dir("debug_objects", NULL);
if (!dbgdir)
return -ENOMEM;
dbgstats = debugfs_create_file("stats", 0444, dbgdir, NULL, debugfs_create_file("stats", 0444, dbgdir, NULL, &debug_stats_fops);
&debug_stats_fops);
if (!dbgstats)
goto err;
return 0; return 0;
err:
debugfs_remove(dbgdir);
return -ENOMEM;
} }
__initcall(debug_objects_init_debugfs); __initcall(debug_objects_init_debugfs);
...@@ -1175,9 +1348,20 @@ static int __init debug_objects_replace_static_objects(void) ...@@ -1175,9 +1348,20 @@ static int __init debug_objects_replace_static_objects(void)
*/ */
void __init debug_objects_mem_init(void) void __init debug_objects_mem_init(void)
{ {
int cpu, extras;
if (!debug_objects_enabled) if (!debug_objects_enabled)
return; return;
/*
* Initialize the percpu object pools
*
* Initialization is not strictly necessary, but was done for
* completeness.
*/
for_each_possible_cpu(cpu)
INIT_HLIST_HEAD(&per_cpu(percpu_obj_pool.free_objs, cpu));
obj_cache = kmem_cache_create("debug_objects_cache", obj_cache = kmem_cache_create("debug_objects_cache",
sizeof (struct debug_obj), 0, sizeof (struct debug_obj), 0,
SLAB_DEBUG_OBJECTS | SLAB_NOLEAKTRACE, SLAB_DEBUG_OBJECTS | SLAB_NOLEAKTRACE,
...@@ -1194,6 +1378,7 @@ void __init debug_objects_mem_init(void) ...@@ -1194,6 +1378,7 @@ void __init debug_objects_mem_init(void)
* Increase the thresholds for allocating and freeing objects * Increase the thresholds for allocating and freeing objects
* according to the number of possible CPUs available in the system. * according to the number of possible CPUs available in the system.
*/ */
debug_objects_pool_size += num_possible_cpus() * 32; extras = num_possible_cpus() * ODEBUG_BATCH_SIZE;
debug_objects_pool_min_level += num_possible_cpus() * 4; debug_objects_pool_size += extras;
debug_objects_pool_min_level += extras;
} }
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