Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/subr_witness.c
Show First 20 Lines • Show All 332 Lines • ▼ Show 20 Lines | |||||
static void witness_ddb_display(int(*)(const char *fmt, ...)); | static void witness_ddb_display(int(*)(const char *fmt, ...)); | ||||
static void witness_ddb_display_descendants(int(*)(const char *fmt, ...), | static void witness_ddb_display_descendants(int(*)(const char *fmt, ...), | ||||
struct witness *, int indent); | struct witness *, int indent); | ||||
static void witness_ddb_display_list(int(*prnt)(const char *fmt, ...), | static void witness_ddb_display_list(int(*prnt)(const char *fmt, ...), | ||||
struct witness_list *list); | struct witness_list *list); | ||||
static void witness_ddb_level_descendants(struct witness *parent, int l); | static void witness_ddb_level_descendants(struct witness *parent, int l); | ||||
static void witness_ddb_list(struct thread *td); | static void witness_ddb_list(struct thread *td); | ||||
#endif | #endif | ||||
static void witness_enter_debugger(const char *msg); | |||||
static void witness_debugger(int cond, const char *msg); | static void witness_debugger(int cond, const char *msg); | ||||
static void witness_free(struct witness *m); | static void witness_free(struct witness *m); | ||||
static struct witness *witness_get(void); | static struct witness *witness_get(void); | ||||
static uint32_t witness_hash_djb2(const uint8_t *key, uint32_t size); | static uint32_t witness_hash_djb2(const uint8_t *key, uint32_t size); | ||||
static struct witness *witness_hash_get(const char *key); | static struct witness *witness_hash_get(const char *key); | ||||
static void witness_hash_put(struct witness *w); | static void witness_hash_put(struct witness *w); | ||||
static void witness_init_hash_tables(void); | static void witness_init_hash_tables(void); | ||||
static void witness_increment_graph_generation(void); | static void witness_increment_graph_generation(void); | ||||
static void witness_lock_list_free(struct lock_list_entry *lle); | static void witness_lock_list_free(struct lock_list_entry *lle); | ||||
static struct lock_list_entry *witness_lock_list_get(void); | static struct lock_list_entry *witness_lock_list_get(void); | ||||
static int witness_lock_order_add(struct witness *parent, | static int witness_lock_order_add(struct witness *parent, | ||||
struct witness *child); | struct witness *child); | ||||
static int witness_lock_order_check(struct witness *parent, | static int witness_lock_order_check(struct witness *parent, | ||||
struct witness *child); | struct witness *child); | ||||
static struct witness_lock_order_data *witness_lock_order_get( | static struct witness_lock_order_data *witness_lock_order_get( | ||||
struct witness *parent, | struct witness *parent, | ||||
struct witness *child); | struct witness *child); | ||||
static void witness_list_lock(struct lock_instance *instance, | static void witness_list_lock(struct lock_instance *instance, | ||||
int (*prnt)(const char *fmt, ...)); | int (*prnt)(const char *fmt, ...)); | ||||
static int witness_output(const char *fmt, ...) __printflike(1, 2); | static int witness_output(const char *fmt, ...) __printflike(1, 2); | ||||
static int witness_output_drain(void *arg __unused, const char *data, | |||||
int len); | |||||
static int witness_voutput(const char *fmt, va_list ap) __printflike(1, 0); | static int witness_voutput(const char *fmt, va_list ap) __printflike(1, 0); | ||||
static void witness_setflag(struct lock_object *lock, int flag, int set); | static void witness_setflag(struct lock_object *lock, int flag, int set); | ||||
FEATURE(witness, "kernel has witness(9) support"); | FEATURE(witness, "kernel has witness(9) support"); | ||||
static SYSCTL_NODE(_debug, OID_AUTO, witness, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, | static SYSCTL_NODE(_debug, OID_AUTO, witness, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, | ||||
"Witness Locking"); | "Witness Locking"); | ||||
▲ Show 20 Lines • Show All 907 Lines • ▼ Show 20 Lines | witness_checkorder(struct lock_object *lock, int flags, const char *file, | ||||
* the lock we most recently acquired in the lock order tree, | * the lock we most recently acquired in the lock order tree, | ||||
* then there is no need for any further checks. | * then there is no need for any further checks. | ||||
*/ | */ | ||||
if (isitmychild(w1, w)) | if (isitmychild(w1, w)) | ||||
goto out; | goto out; | ||||
for (j = 0, lle = lock_list; lle != NULL; lle = lle->ll_next) { | for (j = 0, lle = lock_list; lle != NULL; lle = lle->ll_next) { | ||||
for (i = lle->ll_count - 1; i >= 0; i--, j++) { | for (i = lle->ll_count - 1; i >= 0; i--, j++) { | ||||
struct stack pstack; | |||||
bool pstackv, trace; | |||||
MPASS(j < LOCK_CHILDCOUNT * LOCK_NCHILDREN); | MPASS(j < LOCK_CHILDCOUNT * LOCK_NCHILDREN); | ||||
lock1 = &lle->ll_children[i]; | lock1 = &lle->ll_children[i]; | ||||
/* | /* | ||||
* Ignore the interlock. | * Ignore the interlock. | ||||
*/ | */ | ||||
if (interlock == lock1->li_lock) | if (interlock == lock1->li_lock) | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | reversal: | ||||
/* | /* | ||||
* If the lock order is blessed, bail before logging | * If the lock order is blessed, bail before logging | ||||
* anything. We don't look for other lock order | * anything. We don't look for other lock order | ||||
* violations though, which may be a bug. | * violations though, which may be a bug. | ||||
*/ | */ | ||||
if (blessed(w, w1)) | if (blessed(w, w1)) | ||||
goto out; | goto out; | ||||
trace = atomic_load_int(&witness_trace); | |||||
if (trace) { | |||||
struct witness_lock_order_data *data; | |||||
pstackv = false; | |||||
data = witness_lock_order_get(w, w1); | |||||
if (data != NULL) { | |||||
stack_copy(&data->wlod_stack, | |||||
&pstack); | |||||
pstackv = true; | |||||
} | |||||
} | |||||
mtx_unlock_spin(&w_mtx); | mtx_unlock_spin(&w_mtx); | ||||
#ifdef WITNESS_NO_VNODE | #ifdef WITNESS_NO_VNODE | ||||
/* | /* | ||||
* There are known LORs between VNODE locks. They are | * There are known LORs between VNODE locks. They are | ||||
* not an indication of a bug. VNODE locks are flagged | * not an indication of a bug. VNODE locks are flagged | ||||
* as such (LO_IS_VNODE) and we don't yell if the LOR | * as such (LO_IS_VNODE) and we don't yell if the LOR | ||||
* is between 2 VNODE locks. | * is between 2 VNODE locks. | ||||
Show All 30 Lines | "lock order reversal: (Giant after non-sleepable)\n"); | ||||
if (i == 0 && lle->ll_next != NULL) { | if (i == 0 && lle->ll_next != NULL) { | ||||
lle = lle->ll_next; | lle = lle->ll_next; | ||||
i = lle->ll_count - 1; | i = lle->ll_count - 1; | ||||
MPASS(i >= 0 && i < LOCK_NCHILDREN); | MPASS(i >= 0 && i < LOCK_NCHILDREN); | ||||
} else | } else | ||||
i--; | i--; | ||||
} while (i >= 0); | } while (i >= 0); | ||||
if (i < 0) { | if (i < 0) { | ||||
witness_output(" 1st %p %s (%s) @ %s:%d\n", | witness_output(" 1st %p %s (%s, %s) @ %s:%d\n", | ||||
lock1->li_lock, lock1->li_lock->lo_name, | lock1->li_lock, lock1->li_lock->lo_name, | ||||
w1->w_name, fixup_filename(lock1->li_file), | w1->w_name, w1->w_class->lc_name, | ||||
fixup_filename(lock1->li_file), | |||||
lock1->li_line); | lock1->li_line); | ||||
witness_output(" 2nd %p %s (%s) @ %s:%d\n", lock, | witness_output(" 2nd %p %s (%s, %s) @ %s:%d\n", | ||||
lock->lo_name, w->w_name, | lock, lock->lo_name, w->w_name, | ||||
fixup_filename(file), line); | w->w_class->lc_name, fixup_filename(file), | ||||
line); | |||||
} else { | } else { | ||||
witness_output(" 1st %p %s (%s) @ %s:%d\n", | struct witness *w2 = lock2->li_lock->lo_witness; | ||||
witness_output(" 1st %p %s (%s, %s) @ %s:%d\n", | |||||
lock2->li_lock, lock2->li_lock->lo_name, | lock2->li_lock, lock2->li_lock->lo_name, | ||||
lock2->li_lock->lo_witness->w_name, | w2->w_name, w2->w_class->lc_name, | ||||
fixup_filename(lock2->li_file), | fixup_filename(lock2->li_file), | ||||
lock2->li_line); | lock2->li_line); | ||||
witness_output(" 2nd %p %s (%s) @ %s:%d\n", | witness_output(" 2nd %p %s (%s, %s) @ %s:%d\n", | ||||
lock1->li_lock, lock1->li_lock->lo_name, | lock1->li_lock, lock1->li_lock->lo_name, | ||||
w1->w_name, fixup_filename(lock1->li_file), | w1->w_name, w1->w_class->lc_name, | ||||
fixup_filename(lock1->li_file), | |||||
lock1->li_line); | lock1->li_line); | ||||
witness_output(" 3rd %p %s (%s) @ %s:%d\n", lock, | witness_output(" 3rd %p %s (%s, %s) @ %s:%d\n", lock, | ||||
lock->lo_name, w->w_name, | lock->lo_name, w->w_name, | ||||
fixup_filename(file), line); | w->w_class->lc_name, fixup_filename(file), | ||||
line); | |||||
} | } | ||||
witness_debugger(1, __func__); | if (trace) { | ||||
char buf[64]; | |||||
struct sbuf sb; | |||||
sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); | |||||
sbuf_set_drain(&sb, witness_output_drain, | |||||
NULL); | |||||
if (pstackv) { | |||||
sbuf_printf(&sb, | |||||
"lock order %s -> %s established at:\n", | |||||
w->w_name, w1->w_name); | |||||
stack_sbuf_print_flags(&sb, &pstack, | |||||
M_NOWAIT, STACK_SBUF_FMT_LONG); | |||||
} | |||||
sbuf_printf(&sb, | |||||
"lock order %s -> %s attempted at:\n", | |||||
w1->w_name, w->w_name); | |||||
stack_save(&pstack); | |||||
stack_sbuf_print_flags(&sb, &pstack, M_NOWAIT, | |||||
STACK_SBUF_FMT_LONG); | |||||
sbuf_finish(&sb); | |||||
sbuf_delete(&sb); | |||||
} | |||||
witness_enter_debugger(__func__); | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* If requested, build a new lock order. However, don't build a new | * If requested, build a new lock order. However, don't build a new | ||||
* relationship between a sleepable lock and Giant if it is in the | * relationship between a sleepable lock and Giant if it is in the | ||||
* wrong direction. The correct lock order is that sleepable locks | * wrong direction. The correct lock order is that sleepable locks | ||||
▲ Show 20 Lines • Show All 1,634 Lines • ▼ Show 20 Lines | if (witness_trace) { | ||||
stack_zero(&st); | stack_zero(&st); | ||||
stack_save(&st); | stack_save(&st); | ||||
witness_output("stack backtrace:\n"); | witness_output("stack backtrace:\n"); | ||||
stack_sbuf_print_ddb(&sb, &st); | stack_sbuf_print_ddb(&sb, &st); | ||||
sbuf_finish(&sb); | sbuf_finish(&sb); | ||||
} | } | ||||
witness_enter_debugger(msg); | |||||
} | |||||
static void | |||||
witness_enter_debugger(const char *msg) | |||||
{ | |||||
#ifdef KDB | #ifdef KDB | ||||
if (witness_kdb) | if (witness_kdb) | ||||
kdb_enter(KDB_WHY_WITNESS, msg); | kdb_enter(KDB_WHY_WITNESS, msg); | ||||
#endif | #endif | ||||
} | } |