diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2022-06-18 18:15:56 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2022-10-03 22:52:23 -0400 |
commit | 1f44fa78a45e564112dfc3046da45cd1cda1a89f (patch) | |
tree | 5fcc8ad4fd1db38f9ce4be80ddad7700c778f03e | |
parent | 7077f371a1f30d1ff0bb8660dab6e72d9dcd7d2e (diff) |
bcachefs: Move 'btree_transactions' debug to debugs
This moves btree_transactions from sysfs to debugfs, and makes it more
verbose: now we also include the backtrace of each task, since we
generally need this for debugging deadlocks.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/btree_iter.c | 105 | ||||
-rw-r--r-- | fs/bcachefs/btree_iter.h | 2 | ||||
-rw-r--r-- | fs/bcachefs/btree_types.h | 2 | ||||
-rw-r--r-- | fs/bcachefs/debug.c | 73 | ||||
-rw-r--r-- | fs/bcachefs/sysfs.c | 5 |
5 files changed, 123 insertions, 64 deletions
diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c index 5c5e14d1d360..a1512eb06914 100644 --- a/fs/bcachefs/btree_iter.c +++ b/fs/bcachefs/btree_iter.c @@ -3273,11 +3273,14 @@ void __bch2_trans_init(struct btree_trans *trans, struct bch_fs *c, const char *fn) __acquires(&c->btree_trans_barrier) { + struct btree_trans *pos; + BUG_ON(lock_class_is_held(&bch2_btree_node_lock_key)); memset(trans, 0, sizeof(*trans)); trans->c = c; trans->fn = fn; + trans->task = current; bch2_trans_alloc_paths(trans, c); @@ -3293,9 +3296,15 @@ void __bch2_trans_init(struct btree_trans *trans, struct bch_fs *c, trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier); - trans->pid = current->pid; mutex_lock(&c->btree_trans_lock); - list_add(&trans->list, &c->btree_trans_list); + list_for_each_entry(pos, &c->btree_trans_list, list) { + if (trans->task->pid < pos->task->pid) { + list_add_tail(&trans->list, &pos->list); + goto list_add_done; + } + } + list_add_tail(&trans->list, &c->btree_trans_list); +list_add_done: mutex_unlock(&c->btree_trans_lock); } @@ -3383,73 +3392,55 @@ bch2_btree_path_node_to_text(struct printbuf *out, bch2_bpos_to_text(out, btree_node_pos(_b, cached)); } -static bool trans_has_locks(struct btree_trans *trans) -{ - struct btree_path *path; - - trans_for_each_path(trans, path) - if (path->nodes_locked) - return true; - return false; -} - -void bch2_btree_trans_to_text(struct printbuf *out, struct bch_fs *c) +void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans) { - struct btree_trans *trans; struct btree_path *path; struct btree *b; static char lock_types[] = { 'r', 'i', 'w' }; unsigned l; - mutex_lock(&c->btree_trans_lock); - list_for_each_entry(trans, &c->btree_trans_list, list) { - if (!trans_has_locks(trans)) - continue; - - prt_printf(out, "%i %s\n", trans->pid, trans->fn); + prt_printf(out, "%i %s\n", trans->task->pid, trans->fn); - trans_for_each_path(trans, path) { - if (!path->nodes_locked) - continue; + trans_for_each_path(trans, path) { + if (!path->nodes_locked) + continue; - prt_printf(out, " path %u %c l=%u %s:", - path->idx, - path->cached ? 'c' : 'b', - path->level, - bch2_btree_ids[path->btree_id]); - bch2_bpos_to_text(out, path->pos); - prt_printf(out, "\n"); - - for (l = 0; l < BTREE_MAX_DEPTH; l++) { - if (btree_node_locked(path, l)) { - prt_printf(out, " %s l=%u ", - btree_node_intent_locked(path, l) ? "i" : "r", l); - bch2_btree_path_node_to_text(out, - (void *) path->l[l].b, - path->cached); - prt_printf(out, "\n"); - } + prt_printf(out, " path %u %c l=%u %s:", + path->idx, + path->cached ? 'c' : 'b', + path->level, + bch2_btree_ids[path->btree_id]); + bch2_bpos_to_text(out, path->pos); + prt_printf(out, "\n"); + + for (l = 0; l < BTREE_MAX_DEPTH; l++) { + if (btree_node_locked(path, l)) { + prt_printf(out, " %s l=%u ", + btree_node_intent_locked(path, l) ? "i" : "r", l); + bch2_btree_path_node_to_text(out, + (void *) path->l[l].b, + path->cached); + prt_printf(out, "\n"); } } + } - b = READ_ONCE(trans->locking); - if (b) { - path = &trans->paths[trans->locking_path_idx]; - prt_printf(out, " locking path %u %c l=%u %c %s:", - trans->locking_path_idx, - path->cached ? 'c' : 'b', - trans->locking_level, - lock_types[trans->locking_lock_type], - bch2_btree_ids[trans->locking_btree_id]); - bch2_bpos_to_text(out, trans->locking_pos); - - prt_printf(out, " node "); - bch2_btree_path_node_to_text(out, - (void *) b, path->cached); - prt_printf(out, "\n"); - } + b = READ_ONCE(trans->locking); + if (b) { + path = &trans->paths[trans->locking_path_idx]; + prt_printf(out, " locking path %u %c l=%u %c %s:", + trans->locking_path_idx, + path->cached ? 'c' : 'b', + trans->locking_level, + lock_types[trans->locking_lock_type], + bch2_btree_ids[trans->locking_btree_id]); + bch2_bpos_to_text(out, trans->locking_pos); + + prt_printf(out, " node "); + bch2_btree_path_node_to_text(out, + (void *) b, path->cached); + prt_printf(out, "\n"); } - mutex_unlock(&c->btree_trans_lock); } void bch2_fs_btree_iter_exit(struct bch_fs *c) diff --git a/fs/bcachefs/btree_iter.h b/fs/bcachefs/btree_iter.h index f2b302c8150c..9da0a4152ce4 100644 --- a/fs/bcachefs/btree_iter.h +++ b/fs/bcachefs/btree_iter.h @@ -403,7 +403,7 @@ void bch2_trans_exit(struct btree_trans *); #define bch2_trans_init(...) __bch2_trans_init(__VA_ARGS__, __func__) -void bch2_btree_trans_to_text(struct printbuf *, struct bch_fs *); +void bch2_btree_trans_to_text(struct printbuf *, struct btree_trans *); void bch2_fs_btree_iter_exit(struct bch_fs *); int bch2_fs_btree_iter_init(struct bch_fs *); diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h index 5382f2b85e19..7e26e7584f1d 100644 --- a/fs/bcachefs/btree_types.h +++ b/fs/bcachefs/btree_types.h @@ -388,7 +388,7 @@ struct btree_trans { u8 locking_btree_id; u8 locking_level; u8 locking_lock_type; - pid_t pid; + struct task_struct *task; int srcu_idx; u8 nr_sorted; diff --git a/fs/bcachefs/debug.c b/fs/bcachefs/debug.c index bdc50d5ba3a2..05cae0ed41ae 100644 --- a/fs/bcachefs/debug.c +++ b/fs/bcachefs/debug.c @@ -529,6 +529,76 @@ static const struct file_operations cached_btree_nodes_ops = { .read = bch2_cached_btree_nodes_read, }; +static int prt_backtrace(struct printbuf *out, struct task_struct *task) +{ + unsigned long entries[32]; + unsigned i, nr_entries; + int ret; + + ret = down_read_killable(&task->signal->exec_update_lock); + if (ret) + return ret; + + nr_entries = stack_trace_save_tsk(task, entries, ARRAY_SIZE(entries), 0); + for (i = 0; i < nr_entries; i++) { + prt_printf(out, "[<0>] %pB", (void *)entries[i]); + prt_newline(out); + } + + up_read(&task->signal->exec_update_lock); + return 0; +} + +static ssize_t bch2_btree_transactions_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct dump_iter *i = file->private_data; + struct bch_fs *c = i->c; + struct btree_trans *trans; + int err; + + i->ubuf = buf; + i->size = size; + i->ret = 0; + + mutex_lock(&c->btree_trans_lock); + list_for_each_entry(trans, &c->btree_trans_list, list) { + if (trans->task->pid <= i->iter) + continue; + + err = flush_buf(i); + if (err) + return err; + + if (!i->size) + break; + + bch2_btree_trans_to_text(&i->buf, trans); + + prt_printf(&i->buf, "backtrace:"); + prt_newline(&i->buf); + printbuf_indent_add(&i->buf, 2); + prt_backtrace(&i->buf, trans->task); + printbuf_indent_sub(&i->buf, 2); + prt_newline(&i->buf); + + i->iter = trans->task->pid; + } + mutex_unlock(&c->btree_trans_lock); + + if (i->buf.allocation_failure) + return -ENOMEM; + + return i->ret; +} + +static const struct file_operations btree_transactions_ops = { + .owner = THIS_MODULE, + .open = bch2_dump_open, + .release = bch2_dump_release, + .read = bch2_btree_transactions_read, +}; + static ssize_t bch2_journal_pins_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { @@ -588,6 +658,9 @@ void bch2_fs_debug_init(struct bch_fs *c) debugfs_create_file("cached_btree_nodes", 0400, c->fs_debug_dir, c->btree_debug, &cached_btree_nodes_ops); + debugfs_create_file("btree_transactions", 0400, c->fs_debug_dir, + c->btree_debug, &btree_transactions_ops); + debugfs_create_file("journal_pins", 0400, c->fs_debug_dir, c->btree_debug, &journal_pins_ops); diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c index d72ec0629a37..d1a3ce747fda 100644 --- a/fs/bcachefs/sysfs.c +++ b/fs/bcachefs/sysfs.c @@ -182,7 +182,6 @@ read_attribute(journal_debug); read_attribute(btree_updates); read_attribute(btree_cache); read_attribute(btree_key_cache); -read_attribute(btree_transactions); read_attribute(stripes_heap); read_attribute(open_buckets); @@ -420,9 +419,6 @@ SHOW(bch2_fs) if (attr == &sysfs_btree_key_cache) bch2_btree_key_cache_to_text(out, &c->btree_key_cache); - if (attr == &sysfs_btree_transactions) - bch2_btree_trans_to_text(out, c); - if (attr == &sysfs_stripes_heap) bch2_stripes_heap_to_text(out, c); @@ -617,7 +613,6 @@ struct attribute *bch2_fs_internal_files[] = { &sysfs_btree_updates, &sysfs_btree_cache, &sysfs_btree_key_cache, - &sysfs_btree_transactions, &sysfs_new_stripes, &sysfs_stripes_heap, &sysfs_open_buckets, |