Implement a basic state machine checker in the debugobjects. This state machine checker detects races and inconsistencies within the "active" life of a debugobject. The checker only keeps track of the current state; all the state machine logic is kept at the object instance level. The checker works by adding a supplementary "unsigned int astate" field to the debug_obj structure. It keeps track of the current "active state" of the object. The only constraints that are imposed on the states by the debugobjects system is that: - activation of an object sets the current active state to 0, - deactivation of an object expects the current active state to be 0. For the rest of the states, the state mapping is determined by the specific object instance. Therefore, the logic keeping track of the state machine is within the specialized instance, without any need to know about it at the debugobject level. The current object active state is changed by calling: debug_object_active_state(addr, descr, expect, next) where "expect" is the expected state and "next" is the next state to move to if the expected state is found. A warning is generated if the expected is not found.
Showing the changes to lib/debugobjects.c (1 of 2 files). Show all
Unified · Split
| @@ -141,6 +141,7 @@ alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr) · 140 unmodified lines | ||
| 141 | 141 | obj->object = addr; |
| 142 | 142 | obj->descr = descr; |
| 143 | 143 | obj->state = ODEBUG_STATE_NONE; |
| 144 | + obj->astate = 0; | |
| 144 | 145 | hlist_del(&obj->node); |
| 145 | 146 | |
| 146 | 147 | hlist_add_head(&obj->node, &b->list); |
| @@ -252,8 +253,10 @@ static void debug_print_object(struct debug_obj *obj, char *msg) · 105 unmodified lines | ||
| 252 | 253 | |
| 253 | 254 | if (limit < 5 && obj->descr != descr_test) { |
| 254 | 255 | limit++; |
| 255 | − WARN(1, KERN_ERR "ODEBUG: %s %s object type: %s\n", msg, | |
| 256 | − obj_states[obj->state], obj->descr->name); | |
| 256 | + WARN(1, KERN_ERR "ODEBUG: %s %s (active state %u) " | |
| 257 | + "object type: %s\n", | |
| 258 | + msg, obj_states[obj->state], obj->astate, | |
| 259 | + obj->descr->name); | |
| 257 | 260 | } |
| 258 | 261 | debug_objects_warnings++; |
| 259 | 262 | } |
| @@ -447,7 +450,10 @@ void debug_object_deactivate(void *addr, struct debug_obj_descr *descr) · 187 unmodified lines | ||
| 447 | 450 | case ODEBUG_STATE_INIT: |
| 448 | 451 | case ODEBUG_STATE_INACTIVE: |
| 449 | 452 | case ODEBUG_STATE_ACTIVE: |
| 450 | − obj->state = ODEBUG_STATE_INACTIVE; | |
| 453 | + if (!obj->astate) | |
| 454 | + obj->state = ODEBUG_STATE_INACTIVE; | |
| 455 | + else | |
| 456 | + debug_print_object(obj, "deactivate"); | |
| 451 | 457 | break; |
| 452 | 458 | |
| 453 | 459 | case ODEBUG_STATE_DESTROYED: |
| @@ -553,6 +559,53 @@ out_unlock: · 99 unmodified lines | ||
| 553 | 559 | raw_spin_unlock_irqrestore(&db->lock, flags); |
| 554 | 560 | } |
| 555 | 561 | |
| 562 | +/** | |
| 563 | + * debug_object_active_state - debug checks object usage state machine | |
| 564 | + * @addr: address of the object | |
| 565 | + * @descr: pointer to an object specific debug description structure | |
| 566 | + * @expect: expected state | |
| 567 | + * @next: state to move to if expected state is found | |
| 568 | + */ | |
| 569 | +void | |
| 570 | +debug_object_active_state(void *addr, struct debug_obj_descr *descr, | |
| 571 | + unsigned int expect, unsigned int next) | |
| 572 | +{ | |
| 573 | + struct debug_bucket *db; | |
| 574 | + struct debug_obj *obj; | |
| 575 | + unsigned long flags; | |
| 576 | + | |
| 577 | + if (!debug_objects_enabled) | |
| 578 | + return; | |
| 579 | + | |
| 580 | + db = get_bucket((unsigned long) addr); | |
| 581 | + | |
| 582 | + raw_spin_lock_irqsave(&db->lock, flags); | |
| 583 | + | |
| 584 | + obj = lookup_object(addr, db); | |
| 585 | + if (obj) { | |
| 586 | + switch (obj->state) { | |
| 587 | + case ODEBUG_STATE_ACTIVE: | |
| 588 | + if (obj->astate == expect) | |
| 589 | + obj->astate = next; | |
| 590 | + else | |
| 591 | + debug_print_object(obj, "active_state"); | |
| 592 | + break; | |
| 593 | + | |
| 594 | + default: | |
| 595 | + debug_print_object(obj, "active_state"); | |
| 596 | + break; | |
| 597 | + } | |
| 598 | + } else { | |
| 599 | + struct debug_obj o = { .object = addr, | |
| 600 | + .state = ODEBUG_STATE_NOTAVAILABLE, | |
| 601 | + .descr = descr }; | |
| 602 | + | |
| 603 | + debug_print_object(&o, "active_state"); | |
| 604 | + } | |
| 605 | + | |
| 606 | + raw_spin_unlock_irqrestore(&db->lock, flags); | |
| 607 | +} | |
| 608 | + | |
| 556 | 609 | #ifdef CONFIG_DEBUG_OBJECTS_FREE |
| 557 | 610 | static void __debug_check_no_obj_freed(const void *address, unsigned long size) |
| 558 | 611 | { |