/*
* Generic infrastructure for lifetime debugging of objects.
*
* Started by Thomas Gleixner
*
* Copyright (C) 2008, Thomas Gleixner <tglx@linutronix.de>
*
* For licencing details see kernel-base/COPYING
*/
#include <linux/debugobjects.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/hash.h>
#define ODEBUG_HASH_BITS 14
#define ODEBUG_HASH_SIZE (1 << ODEBUG_HASH_BITS)
#define ODEBUG_POOL_SIZE 512
#define ODEBUG_POOL_MIN_LEVEL 256
#define ODEBUG_CHUNK_SHIFT PAGE_SHIFT
#define ODEBUG_CHUNK_SIZE (1 << ODEBUG_CHUNK_SHIFT)
#define ODEBUG_CHUNK_MASK (~(ODEBUG_CHUNK_SIZE - 1))
struct debug_bucket {
struct hlist_head list;
spinlock_t lock;
};
static struct debug_bucket obj_hash[ODEBUG_HASH_SIZE];
static struct debug_obj obj_static_pool[ODEBUG_POOL_SIZE];
static DEFINE_SPINLOCK(pool_lock);
static HLIST_HEAD(obj_pool);
static int obj_pool_min_free = ODEBUG_POOL_SIZE;
static int obj_pool_free = ODEBUG_POOL_SIZE;
static int obj_pool_used;
static int obj_pool_max_used;
static struct kmem_cache *obj_cache;
static int debug_objects_maxchain __read_mostly;
static int debug_objects_fixups __read_mostly;
static int debug_objects_warnings __read_mostly;
static int debug_objects_enabled __read_mostly;
static struct debug_obj_descr *descr_test __read_mostly;
static int __init enable_object_debug(char *str)
{
debug_objects_enabled = 1;
return 0;
}
early_param("debug_objects", enable_object_debug);
static const char *obj_states[ODEBUG_STATE_MAX] = {
[ODEBUG_STATE_NONE] = "none",
[ODEBUG_STATE_INIT] = "initialized",
[ODEBUG_STATE_INACTIVE] = "inactive",
[ODEBUG_STATE_ACTIVE] = "active",
[ODEBUG_STATE_DESTROYED] = "destroyed",
[ODEBUG_STATE_NOTAVAILABLE] = "not available",
};
static int fill_pool(void)
{
gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN;
struct debug_obj *new;
if (likely(obj_pool_free >= ODEBUG_POOL_MIN_LEVEL))
return obj_pool_free;
if (unlikely(!obj_cache))
return obj_pool_free;
while (obj_pool_free < ODEBUG_POOL_MIN_LEVEL) {
new = kmem_cache_zalloc(obj_cache, gfp);
if (!new)
return obj_pool_free;
spin_lock(&pool_lock);
hlist_add_head(&new->node, &obj_pool);
obj_pool_free++;
spin_unlock(&pool_lock);
}
return obj_pool_free;
}
/*
* Lookup an object in the hash bucket.
*/
static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b)
{
struct hlist_node *node;
struct debug_obj *obj;
int cnt = 0;
hlist_for_each_entry(obj, node, &b->list, node) {
cnt++;
if (obj->object == addr)
return obj;
}
if (cnt > debug_objects_maxchain)
debug_objects_maxchain = cnt;
return NULL;
}
/*
* Allocate a new object. If the pool is empty and no refill possible,
* switch off the debugger.
*/
static struct debug_obj *
alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr)
{
struct debug_obj *obj = NULL;
int retry = 0;
repeat:
spin_lock(&pool_lock);
if (obj_pool.first) {
obj = hlist_entry(obj_pool.first, typeof(*obj), node);
obj->object = addr;
obj->descr = descr;
obj->state = ODEBUG_STATE_NONE;
hlist_del(&obj->node);
hlist_add_head(&obj->node, &b->list);
obj_pool_used++;
if (obj_pool_used > obj_pool_max_used)
obj_pool_max_used = obj_pool_used;
obj_pool_free--;
if (obj_pool_free < obj_pool_min_free)
obj_pool_min_free = obj_pool_free;
}
spin_unlock(&pool_lock);
if (fill_pool() && !obj && !retry++)
goto repeat;
return obj;
}
/*
* Put the object back into the pool or give it back to kmem_cache:
*/
static void free_object(struct debug_obj *obj)
{
unsigned long idx = (unsigned long)(obj - obj_static_pool);
if (obj_pool_free < ODEBUG_POOL_SIZE || idx < ODEBUG_POOL_SIZE) {
spin_lock(&pool_lock);
hlist_add_head(&obj->node, &obj_pool);
obj_pool_free++;
obj_pool_used--;
spin_unlock(&pool_lock);
} else {
spin_lock(&pool_lock);
obj_pool_used--;
spin_unlock(&pool_lock);
kmem_cache_free(obj_cache, obj);
}
}
/*
* We run out of memory. That means we probably have tons of objects
* allocated.
*/
static void debug_objects_oom(void)
{
struct debug_bucket *db = obj_hash;
struct hlist_node *node, *tmp;
struct debug_obj *obj;
unsigned long flags;
int i;
printk(KERN_WARNING "ODEBUG: Out of memory. ODEBUG disabled\n");
for (i = 0; i < ODEBUG_HASH_SIZE; i++, db++) {
spin_lock_irqsave(&db->lock, flags);