#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
+#include <linux/slab.h>
#include <linux/hash.h>
#define ODEBUG_HASH_BITS 14
[ODEBUG_STATE_NOTAVAILABLE] = "not available",
};
-static int fill_pool(void)
+static void fill_pool(void)
{
gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN;
struct debug_obj *new;
unsigned long flags;
if (likely(obj_pool_free >= ODEBUG_POOL_MIN_LEVEL))
- return obj_pool_free;
+ return;
if (unlikely(!obj_cache))
- return obj_pool_free;
+ return;
while (obj_pool_free < ODEBUG_POOL_MIN_LEVEL) {
new = kmem_cache_zalloc(obj_cache, gfp);
if (!new)
- return obj_pool_free;
+ return;
raw_spin_lock_irqsave(&pool_lock, flags);
hlist_add_head(&new->node, &obj_pool);
obj_pool_free++;
raw_spin_unlock_irqrestore(&pool_lock, flags);
}
- return obj_pool_free;
}
/*
*/
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) {
+ hlist_for_each_entry(obj, &b->list, node) {
cnt++;
if (obj->object == addr)
return obj;
obj->object = addr;
obj->descr = descr;
obj->state = ODEBUG_STATE_NONE;
+ obj->astate = 0;
hlist_del(&obj->node);
hlist_add_head(&obj->node, &b->list);
* initialized:
*/
if (obj_pool_free > ODEBUG_POOL_SIZE && obj_cache)
- sched = !work_pending(&debug_obj_work);
+ sched = keventd_up() && !work_pending(&debug_obj_work);
hlist_add_head(&obj->node, &obj_pool);
obj_pool_free++;
obj_pool_used--;
static void debug_objects_oom(void)
{
struct debug_bucket *db = obj_hash;
- struct hlist_node *node, *tmp;
+ struct hlist_node *tmp;
HLIST_HEAD(freelist);
struct debug_obj *obj;
unsigned long flags;
raw_spin_unlock_irqrestore(&db->lock, flags);
/* Now free them */
- hlist_for_each_entry_safe(obj, node, tmp, &freelist, node) {
+ hlist_for_each_entry_safe(obj, tmp, &freelist, node) {
hlist_del(&obj->node);
free_object(obj);
}
static void debug_print_object(struct debug_obj *obj, char *msg)
{
+ struct debug_obj_descr *descr = obj->descr;
static int limit;
- if (limit < 5 && obj->descr != descr_test) {
+ if (limit < 5 && descr != descr_test) {
+ void *hint = descr->debug_hint ?
+ descr->debug_hint(obj->object) : NULL;
limit++;
- WARN(1, KERN_ERR "ODEBUG: %s %s object type: %s\n", msg,
- obj_states[obj->state], obj->descr->name);
+ WARN(1, KERN_ERR "ODEBUG: %s %s (active state %u) "
+ "object type: %s hint: %pS\n",
+ msg, obj_states[obj->state], obj->astate,
+ descr->name, hint);
}
debug_objects_warnings++;
}
* Try to repair the damage, so we have a better chance to get useful
* debug output.
*/
-static void
+static int
debug_object_fixup(int (*fixup)(void *addr, enum debug_obj_state state),
void * addr, enum debug_obj_state state)
{
+ int fixed = 0;
+
if (fixup)
- debug_objects_fixups += fixup(addr, state);
+ fixed = fixup(addr, state);
+ debug_objects_fixups += fixed;
+ return fixed;
}
static void debug_object_is_on_stack(void *addr, int onstack)
struct debug_bucket *db;
struct debug_obj *obj;
unsigned long flags;
+ struct debug_obj o = { .object = addr,
+ .state = ODEBUG_STATE_NOTAVAILABLE,
+ .descr = descr };
if (!debug_objects_enabled)
return;
* let the type specific code decide whether this is
* true or not.
*/
- debug_object_fixup(descr->fixup_activate, addr,
- ODEBUG_STATE_NOTAVAILABLE);
+ if (debug_object_fixup(descr->fixup_activate, addr,
+ ODEBUG_STATE_NOTAVAILABLE))
+ debug_print_object(&o, "activate");
}
/**
case ODEBUG_STATE_INIT:
case ODEBUG_STATE_INACTIVE:
case ODEBUG_STATE_ACTIVE:
- obj->state = ODEBUG_STATE_INACTIVE;
+ if (!obj->astate)
+ obj->state = ODEBUG_STATE_INACTIVE;
+ else
+ debug_print_object(obj, "deactivate");
break;
case ODEBUG_STATE_DESTROYED:
raw_spin_unlock_irqrestore(&db->lock, flags);
}
+/**
+ * debug_object_assert_init - debug checks when object should be init-ed
+ * @addr: address of the object
+ * @descr: pointer to an object specific debug description structure
+ */
+void debug_object_assert_init(void *addr, struct debug_obj_descr *descr)
+{
+ struct debug_bucket *db;
+ struct debug_obj *obj;
+ unsigned long flags;
+
+ if (!debug_objects_enabled)
+ return;
+
+ db = get_bucket((unsigned long) addr);
+
+ raw_spin_lock_irqsave(&db->lock, flags);
+
+ obj = lookup_object(addr, db);
+ if (!obj) {
+ struct debug_obj o = { .object = addr,
+ .state = ODEBUG_STATE_NOTAVAILABLE,
+ .descr = descr };
+
+ raw_spin_unlock_irqrestore(&db->lock, flags);
+ /*
+ * Maybe the object is static. Let the type specific
+ * code decide what to do.
+ */
+ if (debug_object_fixup(descr->fixup_assert_init, addr,
+ ODEBUG_STATE_NOTAVAILABLE))
+ debug_print_object(&o, "assert_init");
+ return;
+ }
+
+ raw_spin_unlock_irqrestore(&db->lock, flags);
+}
+
+/**
+ * debug_object_active_state - debug checks object usage state machine
+ * @addr: address of the object
+ * @descr: pointer to an object specific debug description structure
+ * @expect: expected state
+ * @next: state to move to if expected state is found
+ */
+void
+debug_object_active_state(void *addr, struct debug_obj_descr *descr,
+ unsigned int expect, unsigned int next)
+{
+ struct debug_bucket *db;
+ struct debug_obj *obj;
+ unsigned long flags;
+
+ if (!debug_objects_enabled)
+ return;
+
+ db = get_bucket((unsigned long) addr);
+
+ raw_spin_lock_irqsave(&db->lock, flags);
+
+ obj = lookup_object(addr, db);
+ if (obj) {
+ switch (obj->state) {
+ case ODEBUG_STATE_ACTIVE:
+ if (obj->astate == expect)
+ obj->astate = next;
+ else
+ debug_print_object(obj, "active_state");
+ break;
+
+ default:
+ debug_print_object(obj, "active_state");
+ break;
+ }
+ } else {
+ struct debug_obj o = { .object = addr,
+ .state = ODEBUG_STATE_NOTAVAILABLE,
+ .descr = descr };
+
+ debug_print_object(&o, "active_state");
+ }
+
+ raw_spin_unlock_irqrestore(&db->lock, flags);
+}
+
#ifdef CONFIG_DEBUG_OBJECTS_FREE
static void __debug_check_no_obj_freed(const void *address, unsigned long size)
{
unsigned long flags, oaddr, saddr, eaddr, paddr, chunks;
- struct hlist_node *node, *tmp;
+ struct hlist_node *tmp;
HLIST_HEAD(freelist);
struct debug_obj_descr *descr;
enum debug_obj_state state;
repeat:
cnt = 0;
raw_spin_lock_irqsave(&db->lock, flags);
- hlist_for_each_entry_safe(obj, node, tmp, &db->list, node) {
+ hlist_for_each_entry_safe(obj, tmp, &db->list, node) {
cnt++;
oaddr = (unsigned long) obj->object;
if (oaddr < saddr || oaddr >= eaddr)
raw_spin_unlock_irqrestore(&db->lock, flags);
/* Now free them */
- hlist_for_each_entry_safe(obj, node, tmp, &freelist, node) {
+ hlist_for_each_entry_safe(obj, tmp, &freelist, node) {
hlist_del(&obj->node);
free_object(obj);
}
if (obj->static_init == 1) {
debug_object_init(obj, &descr_type_test);
debug_object_activate(obj, &descr_type_test);
- /*
- * Real code should return 0 here ! This is
- * not a fixup of some bad behaviour. We
- * merily call the debug_init function to keep
- * track of the object.
- */
- return 1;
- } else {
- /* Real code needs to emit a warning here */
+ return 0;
}
- return 0;
+ return 1;
case ODEBUG_STATE_ACTIVE:
debug_object_deactivate(obj, &descr_type_test);
obj.static_init = 1;
debug_object_activate(&obj, &descr_type_test);
- if (check_results(&obj, ODEBUG_STATE_ACTIVE, ++fixups, warnings))
+ if (check_results(&obj, ODEBUG_STATE_ACTIVE, fixups, warnings))
goto out;
debug_object_init(&obj, &descr_type_test);
if (check_results(&obj, ODEBUG_STATE_INIT, ++fixups, ++warnings))
static int __init debug_objects_replace_static_objects(void)
{
struct debug_bucket *db = obj_hash;
- struct hlist_node *node, *tmp;
+ struct hlist_node *tmp;
struct debug_obj *obj, *new;
HLIST_HEAD(objects);
int i, cnt = 0;
local_irq_disable();
/* Remove the statically allocated objects from the pool */
- hlist_for_each_entry_safe(obj, node, tmp, &obj_pool, node)
+ hlist_for_each_entry_safe(obj, tmp, &obj_pool, node)
hlist_del(&obj->node);
/* Move the allocated objects to the pool */
hlist_move_list(&objects, &obj_pool);
for (i = 0; i < ODEBUG_HASH_SIZE; i++, db++) {
hlist_move_list(&db->list, &objects);
- hlist_for_each_entry(obj, node, &objects, node) {
+ hlist_for_each_entry(obj, &objects, node) {
new = hlist_entry(obj_pool.first, typeof(*obj), node);
hlist_del(&new->node);
/* copy object data */
cnt++;
}
}
+ local_irq_enable();
printk(KERN_DEBUG "ODEBUG: %d of %d active objects replaced\n", cnt,
obj_pool_used);
- local_irq_enable();
return 0;
free:
- hlist_for_each_entry_safe(obj, node, tmp, &objects, node) {
+ hlist_for_each_entry_safe(obj, tmp, &objects, node) {
hlist_del(&obj->node);
kmem_cache_free(obj_cache, obj);
}