Debugobject changes for v6.8:
- Make tracking object use more robust: it's not safe to access a tracking object after releasing the hashbucket lock. Create a persistent copy for debug printouts instead. Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmWbwj4RHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1iQORAAkO9jZmvIgh3e0boJyvVkYh+m+O0sMSek /HAnoCLk5ipA8I8uhK7r5aqLHPPCoxw2MLihnKj4rTK1nPULZU/VMmge+4hUComv vy1m7tHeUShdAlxSvjZ/807oh546nA5IySv3qG6KCaemHeELllp3gRoIkkTjmnz6 GcPUPQ9WIk8ptyplvyxOR4mw6QVynkIofk7p6C0ePwGNn7yFrYczc3VsnFL+Kmat fl5ZsyN9bRfG4q4bCqj4nKXuUyiJZdbtO2VkyPZtYFmlpERcvGiDHCtzQoKPHS4k CvAz4Eq11DdHjPr5VdRIuW7M91CyGE/2QMOfCOlxPqD+Jh0zTbO00U5BuRhicPXz rn0Yg5J54gDqv/2jtq6eSNsM28OrhGEu+nep9165e4BBhI6oYd0Wbq0TH+rlzohb 048zMePY77nLffwNEpa4dEK2+UKLwufeQiP+d2BGfe96mQQ2i4LIRDzdu/BQtR4W CU7gbcy2e4q+hjI0gB7smFhQi5zR7tIt3WSfLY6hRpzDcpRqaTvfILyzWFgYN2RN I3B9+n3aTlCyCdeFseFajcVvBg5KexkAaPphhfBrkZ9viB+iKUjmcN8mMrvRfX6I vjCeIaNCL7arCAkce0hhHZ0WUf/f+DPoEJCb+Z9EuBmOosO/3tlNH6YGyeJNN+vx r7mt+MSC4CY= =WFe/ -----END PGP SIGNATURE----- Merge tag 'core-debugobjects-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull debugobject update from Ingo Molnar: - Make tracking object use more robust: it's not safe to access a tracking object after releasing the hashbucket lock. Create a persistent copy for debug printouts instead. * tag 'core-debugobjects-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: debugobjects: Stop accessing objects after releasing hash bucket lock
This commit is contained in:
commit
ab9517fa9a
@ -620,9 +620,8 @@ static void debug_objects_fill_pool(void)
|
||||
static void
|
||||
__debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack)
|
||||
{
|
||||
enum debug_obj_state state;
|
||||
struct debug_obj *obj, o;
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
|
||||
debug_objects_fill_pool();
|
||||
@ -643,24 +642,18 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack
|
||||
case ODEBUG_STATE_INIT:
|
||||
case ODEBUG_STATE_INACTIVE:
|
||||
obj->state = ODEBUG_STATE_INIT;
|
||||
break;
|
||||
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
state = obj->state;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "init");
|
||||
debug_object_fixup(descr->fixup_init, addr, state);
|
||||
return;
|
||||
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "init");
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
o = *obj;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(&o, "init");
|
||||
|
||||
if (o.state == ODEBUG_STATE_ACTIVE)
|
||||
debug_object_fixup(descr->fixup_init, addr, o.state);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -701,11 +694,9 @@ EXPORT_SYMBOL_GPL(debug_object_init_on_stack);
|
||||
int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
|
||||
{
|
||||
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
|
||||
enum debug_obj_state state;
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!debug_objects_enabled)
|
||||
return 0;
|
||||
@ -717,49 +708,38 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
|
||||
raw_spin_lock_irqsave(&db->lock, flags);
|
||||
|
||||
obj = lookup_object_or_alloc(addr, db, descr, false, true);
|
||||
if (likely(!IS_ERR_OR_NULL(obj))) {
|
||||
bool print_object = false;
|
||||
|
||||
if (unlikely(!obj)) {
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_objects_oom();
|
||||
return 0;
|
||||
} else if (likely(!IS_ERR(obj))) {
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
o = *obj;
|
||||
break;
|
||||
case ODEBUG_STATE_INIT:
|
||||
case ODEBUG_STATE_INACTIVE:
|
||||
obj->state = ODEBUG_STATE_ACTIVE;
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
state = obj->state;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "activate");
|
||||
ret = debug_object_fixup(descr->fixup_activate, addr, state);
|
||||
return ret ? 0 : -EINVAL;
|
||||
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
print_object = true;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
fallthrough;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
if (print_object)
|
||||
debug_print_object(obj, "activate");
|
||||
return ret;
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
|
||||
/* If NULL the allocation has hit OOM */
|
||||
if (!obj) {
|
||||
debug_objects_oom();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Object is neither static nor tracked. It's not initialized */
|
||||
debug_print_object(&o, "activate");
|
||||
ret = debug_object_fixup(descr->fixup_activate, addr, ODEBUG_STATE_NOTAVAILABLE);
|
||||
return ret ? 0 : -EINVAL;
|
||||
|
||||
switch (o.state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
case ODEBUG_STATE_NOTAVAILABLE:
|
||||
if (debug_object_fixup(descr->fixup_activate, addr, o.state))
|
||||
return 0;
|
||||
fallthrough;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debug_object_activate);
|
||||
|
||||
@ -770,10 +750,10 @@ EXPORT_SYMBOL_GPL(debug_object_activate);
|
||||
*/
|
||||
void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)
|
||||
{
|
||||
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
bool print_object = false;
|
||||
|
||||
if (!debug_objects_enabled)
|
||||
return;
|
||||
@ -785,33 +765,24 @@ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr)
|
||||
obj = lookup_object(addr, db);
|
||||
if (obj) {
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
break;
|
||||
case ODEBUG_STATE_INIT:
|
||||
case ODEBUG_STATE_INACTIVE:
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
if (!obj->astate)
|
||||
obj->state = ODEBUG_STATE_INACTIVE;
|
||||
else
|
||||
print_object = true;
|
||||
break;
|
||||
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
print_object = true;
|
||||
break;
|
||||
if (obj->astate)
|
||||
break;
|
||||
obj->state = ODEBUG_STATE_INACTIVE;
|
||||
fallthrough;
|
||||
default:
|
||||
break;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return;
|
||||
}
|
||||
o = *obj;
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
if (!obj) {
|
||||
struct debug_obj o = { .object = addr,
|
||||
.state = ODEBUG_STATE_NOTAVAILABLE,
|
||||
.descr = descr };
|
||||
|
||||
debug_print_object(&o, "deactivate");
|
||||
} else if (print_object) {
|
||||
debug_print_object(obj, "deactivate");
|
||||
}
|
||||
debug_print_object(&o, "deactivate");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debug_object_deactivate);
|
||||
|
||||
@ -822,11 +793,9 @@ EXPORT_SYMBOL_GPL(debug_object_deactivate);
|
||||
*/
|
||||
void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)
|
||||
{
|
||||
enum debug_obj_state state;
|
||||
struct debug_obj *obj, o;
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
bool print_object = false;
|
||||
|
||||
if (!debug_objects_enabled)
|
||||
return;
|
||||
@ -836,32 +805,31 @@ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr)
|
||||
raw_spin_lock_irqsave(&db->lock, flags);
|
||||
|
||||
obj = lookup_object(addr, db);
|
||||
if (!obj)
|
||||
goto out_unlock;
|
||||
if (!obj) {
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
break;
|
||||
case ODEBUG_STATE_NONE:
|
||||
case ODEBUG_STATE_INIT:
|
||||
case ODEBUG_STATE_INACTIVE:
|
||||
obj->state = ODEBUG_STATE_DESTROYED;
|
||||
break;
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
state = obj->state;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "destroy");
|
||||
debug_object_fixup(descr->fixup_destroy, addr, state);
|
||||
return;
|
||||
|
||||
case ODEBUG_STATE_DESTROYED:
|
||||
print_object = true;
|
||||
break;
|
||||
fallthrough;
|
||||
default:
|
||||
break;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return;
|
||||
}
|
||||
out_unlock:
|
||||
|
||||
o = *obj;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
if (print_object)
|
||||
debug_print_object(obj, "destroy");
|
||||
debug_print_object(&o, "destroy");
|
||||
|
||||
if (o.state == ODEBUG_STATE_ACTIVE)
|
||||
debug_object_fixup(descr->fixup_destroy, addr, o.state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debug_object_destroy);
|
||||
|
||||
@ -872,9 +840,8 @@ EXPORT_SYMBOL_GPL(debug_object_destroy);
|
||||
*/
|
||||
void debug_object_free(void *addr, const struct debug_obj_descr *descr)
|
||||
{
|
||||
enum debug_obj_state state;
|
||||
struct debug_obj *obj, o;
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
|
||||
if (!debug_objects_enabled)
|
||||
@ -885,24 +852,26 @@ void debug_object_free(void *addr, const struct debug_obj_descr *descr)
|
||||
raw_spin_lock_irqsave(&db->lock, flags);
|
||||
|
||||
obj = lookup_object(addr, db);
|
||||
if (!obj)
|
||||
goto out_unlock;
|
||||
if (!obj) {
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
state = obj->state;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "free");
|
||||
debug_object_fixup(descr->fixup_free, addr, state);
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
hlist_del(&obj->node);
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
free_object(obj);
|
||||
return;
|
||||
}
|
||||
out_unlock:
|
||||
|
||||
o = *obj;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(&o, "free");
|
||||
|
||||
debug_object_fixup(descr->fixup_free, addr, o.state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debug_object_free);
|
||||
|
||||
@ -954,10 +923,10 @@ void
|
||||
debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
|
||||
unsigned int expect, unsigned int next)
|
||||
{
|
||||
struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr };
|
||||
struct debug_bucket *db;
|
||||
struct debug_obj *obj;
|
||||
unsigned long flags;
|
||||
bool print_object = false;
|
||||
|
||||
if (!debug_objects_enabled)
|
||||
return;
|
||||
@ -970,28 +939,19 @@ debug_object_active_state(void *addr, const struct debug_obj_descr *descr,
|
||||
if (obj) {
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
if (obj->astate == expect)
|
||||
obj->astate = next;
|
||||
else
|
||||
print_object = true;
|
||||
break;
|
||||
|
||||
if (obj->astate != expect)
|
||||
break;
|
||||
obj->astate = next;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
return;
|
||||
default:
|
||||
print_object = true;
|
||||
break;
|
||||
}
|
||||
o = *obj;
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
if (!obj) {
|
||||
struct debug_obj o = { .object = addr,
|
||||
.state = ODEBUG_STATE_NOTAVAILABLE,
|
||||
.descr = descr };
|
||||
|
||||
debug_print_object(&o, "active_state");
|
||||
} else if (print_object) {
|
||||
debug_print_object(obj, "active_state");
|
||||
}
|
||||
debug_print_object(&o, "active_state");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debug_object_active_state);
|
||||
|
||||
@ -999,12 +959,10 @@ EXPORT_SYMBOL_GPL(debug_object_active_state);
|
||||
static void __debug_check_no_obj_freed(const void *address, unsigned long size)
|
||||
{
|
||||
unsigned long flags, oaddr, saddr, eaddr, paddr, chunks;
|
||||
const struct debug_obj_descr *descr;
|
||||
enum debug_obj_state state;
|
||||
int cnt, objs_checked = 0;
|
||||
struct debug_obj *obj, o;
|
||||
struct debug_bucket *db;
|
||||
struct hlist_node *tmp;
|
||||
struct debug_obj *obj;
|
||||
int cnt, objs_checked = 0;
|
||||
|
||||
saddr = (unsigned long) address;
|
||||
eaddr = saddr + size;
|
||||
@ -1026,12 +984,10 @@ repeat:
|
||||
|
||||
switch (obj->state) {
|
||||
case ODEBUG_STATE_ACTIVE:
|
||||
descr = obj->descr;
|
||||
state = obj->state;
|
||||
o = *obj;
|
||||
raw_spin_unlock_irqrestore(&db->lock, flags);
|
||||
debug_print_object(obj, "free");
|
||||
debug_object_fixup(descr->fixup_free,
|
||||
(void *) oaddr, state);
|
||||
debug_print_object(&o, "free");
|
||||
debug_object_fixup(o.descr->fixup_free, (void *)oaddr, o.state);
|
||||
goto repeat;
|
||||
default:
|
||||
hlist_del(&obj->node);
|
||||
|
Loading…
x
Reference in New Issue
Block a user