bcachefs: Improve btree_deadlock debugfs output
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 2 Oct 2022 05:41:08 +0000 (01:41 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:42 +0000 (17:09 -0400)
This changes bch2_check_for_deadlock() to print the longest chains it
finds - when we have a deadlock because the cycle detector isn't finding
something, this will let us see what it's missing.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_locking.c
fs/bcachefs/debug.c

index 19062cea8774cc97351e99fc28b04346848548a2..b79543ae5eebc9a165828a008c3a3ff1ccb33de7 100644 (file)
@@ -71,11 +71,6 @@ struct lock_graph {
        unsigned                        nr;
 };
 
-static void lock_graph_pop(struct lock_graph *g)
-{
-       closure_put(&g->g[--g->nr].trans->ref);
-}
-
 static noinline void print_cycle(struct printbuf *out, struct lock_graph *g)
 {
        struct trans_waiting_for_lock *i;
@@ -87,6 +82,18 @@ static noinline void print_cycle(struct printbuf *out, struct lock_graph *g)
                bch2_btree_trans_to_text(out, i->trans);
 }
 
+static noinline void print_chain(struct printbuf *out, struct lock_graph *g)
+{
+       struct trans_waiting_for_lock *i;
+
+       for (i = g->g; i != g->g + g->nr; i++) {
+               if (i != g->g)
+                       prt_str(out, "<- ");
+               prt_printf(out, "%u ", i->trans->locking_wait.task->pid);
+       }
+       prt_newline(out);
+}
+
 static int abort_lock(struct lock_graph *g, struct trans_waiting_for_lock *i)
 {
        int ret;
@@ -134,6 +141,21 @@ static noinline int break_cycle(struct lock_graph *g)
        BUG();
 }
 
+static void lock_graph_pop(struct lock_graph *g)
+{
+       closure_put(&g->g[--g->nr].trans->ref);
+}
+
+static void lock_graph_pop_above(struct lock_graph *g, struct trans_waiting_for_lock *above,
+                                struct printbuf *cycle)
+{
+       if (g->nr > 1 && cycle)
+               print_chain(cycle, g);
+
+       while (g->g + g->nr > above)
+               lock_graph_pop(g);
+}
+
 static int lock_graph_descend(struct lock_graph *g, struct btree_trans *trans,
                              struct printbuf *cycle)
 {
@@ -142,11 +164,10 @@ static int lock_graph_descend(struct lock_graph *g, struct btree_trans *trans,
        int ret = 0;
 
        for (i = g->g; i < g->g + g->nr; i++) {
-               if (i->trans->locking != i->node_want)
-                       while (g->g + g->nr >= i) {
-                               lock_graph_pop(g);
-                               return 0;
-                       }
+               if (i->trans->locking != i->node_want) {
+                       lock_graph_pop_above(g, i - 1, cycle);
+                       return 0;
+               }
 
                if (i->trans == trans) {
                        if (cycle) {
@@ -185,20 +206,19 @@ static int lock_graph_descend(struct lock_graph *g, struct btree_trans *trans,
 
        return 0;
 deadlock:
-       while (g->nr)
-               lock_graph_pop(g);
+       lock_graph_pop_above(g, g->g, cycle);
        return ret;
 }
 
-static noinline void lock_graph_remove_non_waiters(struct lock_graph *g)
+static noinline void lock_graph_remove_non_waiters(struct lock_graph *g,
+                                                  struct printbuf *cycle)
 {
        struct trans_waiting_for_lock *i;
 
        for (i = g->g + 1; i < g->g + g->nr; i++)
                if (i->trans->locking != i->node_want ||
                    i->trans->locking_wait.start_time != i[-1].lock_start_time) {
-                       while (g->g + g->nr >= i)
-                               lock_graph_pop(g);
+                       lock_graph_pop_above(g, i - 1, cycle);
                        return;
                }
        BUG();
@@ -252,7 +272,7 @@ next:
                        b = &READ_ONCE(path->l[top->level].b)->c;
 
                        if (unlikely(IS_ERR_OR_NULL(b))) {
-                               lock_graph_remove_non_waiters(&g);
+                               lock_graph_remove_non_waiters(&g, cycle);
                                goto next;
                        }
 
@@ -286,6 +306,8 @@ next:
                }
        }
 
+       if (g.nr > 1 && cycle)
+               print_chain(cycle, &g);
        lock_graph_pop(&g);
        goto next;
 }
index 41b2772afef99fc8a0b0681c6ea2cce5e0513273..c7d5583813884de146155fe92da8b588a5004e85 100644 (file)
@@ -725,11 +725,18 @@ static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf,
                goto out;
 
        mutex_lock(&c->btree_trans_lock);
-       list_for_each_entry(trans, &c->btree_trans_list, list)
-               if (bch2_check_for_deadlock(trans, &i->buf)) {
-                       i->iter = 1;
-                       break;
-               }
+       list_for_each_entry(trans, &c->btree_trans_list, list) {
+               if (trans->locking_wait.task->pid <= i->iter)
+                       continue;
+
+               ret = flush_buf(i);
+               if (ret)
+                       return ret;
+
+               bch2_check_for_deadlock(trans, &i->buf);
+
+               i->iter = trans->locking_wait.task->pid;
+       }
        mutex_unlock(&c->btree_trans_lock);
 out:
        if (i->buf.allocation_failure)