bcachefs: Make various async objs visible in debugfs
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 21 Apr 2025 17:02:51 +0000 (13:02 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:14:30 +0000 (20:14 -0400)
Add async objs list for
- promote_op
- bch_read_bio
- btree_read_bio
- btree_write_bio

This gets us introspection on in-flight async ops, and because under the
hood it uses fast_lists (percpu slot buffer on top of a radix tree),
it'll be fast enough to enable in production.

This will be very helpful for debugging "something got stuck" issues,
which have been cropping up from time to time (in the CI, especially
with folio writeback).

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/async_objs.c
fs/bcachefs/async_objs_types.h
fs/bcachefs/btree_io.c
fs/bcachefs/btree_io.h
fs/bcachefs/data_update.h
fs/bcachefs/io_read.c
fs/bcachefs/io_read.h

index 8d78f390a7595c1071fe72dac24c6bd17ee5aa8b..57e2fe4214616824f4a54586ebce34e621ac3434 100644 (file)
 
 #include <linux/debugfs.h>
 
+static void promote_obj_to_text(struct printbuf *out, void *obj)
+{
+       bch2_promote_op_to_text(out, obj);
+}
+
+static void rbio_obj_to_text(struct printbuf *out, void *obj)
+{
+       bch2_read_bio_to_text(out, obj);
+}
+
+static void btree_read_bio_obj_to_text(struct printbuf *out, void *obj)
+{
+       struct btree_read_bio *rbio = obj;
+       bch2_btree_read_bio_to_text(out, rbio);
+}
+
+static void btree_write_bio_obj_to_text(struct printbuf *out, void *obj)
+{
+       struct btree_write_bio *wbio = obj;
+       bch2_bio_to_text(out, &wbio->wbio.bio);
+}
+
 static int bch2_async_obj_list_open(struct inode *inode, struct file *file)
 {
        struct async_obj_list *list = inode->i_private;
@@ -65,7 +87,6 @@ static ssize_t bch2_async_obj_list_read(struct file *file, char __user *buf,
        return ret ?: i->ret;
 }
 
-__maybe_unused
 static const struct file_operations async_obj_ops = {
        .owner          = THIS_MODULE,
        .open           = bch2_async_obj_list_open,
index 28cb73e3f56db4b5fba69fa61106896ed3758363..310a4f90f49b2165b066ecdab681a6470c7027e1 100644 (file)
@@ -2,7 +2,11 @@
 #ifndef _BCACHEFS_ASYNC_OBJS_TYPES_H
 #define _BCACHEFS_ASYNC_OBJS_TYPES_H
 
-#define BCH_ASYNC_OBJ_LISTS()
+#define BCH_ASYNC_OBJ_LISTS()                                          \
+       x(promote)                                                      \
+       x(rbio)                                                         \
+       x(btree_read_bio)                                               \
+       x(btree_write_bio)
 
 enum bch_async_obj_lists {
 #define x(n)           BCH_ASYNC_OBJ_LIST_##n,
index 8fe9e0fc662902ce31a94456d51520d78e8e9ea1..84dae4c1ec13c90d3de70d31e0f67f393cbd96b4 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include "bcachefs.h"
+#include "async_objs.h"
 #include "bkey_buf.h"
 #include "bkey_methods.h"
 #include "bkey_sort.h"
@@ -1376,6 +1377,7 @@ start:
                }
        }
 
+       async_object_list_del(c, btree_read_bio, rb->list_idx);
        bch2_time_stats_update(&c->times[BCH_TIME_btree_node_read],
                               rb->start_time);
        bio_put(&rb->bio);
@@ -1416,6 +1418,11 @@ static void btree_node_read_endio(struct bio *bio)
        queue_work(c->btree_read_complete_wq, &rb->work);
 }
 
+void bch2_btree_read_bio_to_text(struct printbuf *out, struct btree_read_bio *rbio)
+{
+       bch2_bio_to_text(out, &rbio->bio);
+}
+
 struct btree_node_read_all {
        struct closure          cl;
        struct bch_fs           *c;
@@ -1748,6 +1755,8 @@ void bch2_btree_node_read(struct btree_trans *trans, struct btree *b,
        bio->bi_end_io          = btree_node_read_endio;
        bch2_bio_map(bio, b->data, btree_buf_bytes(b));
 
+       async_object_list_add(c, btree_read_bio, rb, &rb->list_idx);
+
        if (rb->have_ioref) {
                this_cpu_add(ca->io_done->sectors[READ][BCH_DATA_btree],
                             bio_sectors(bio));
@@ -2121,6 +2130,7 @@ static void btree_node_write_work(struct work_struct *work)
                        goto err;
        }
 out:
+       async_object_list_del(c, btree_write_bio, wbio->list_idx);
        bio_put(&wbio->wbio.bio);
        btree_node_write_done(c, b, start_time);
        return;
@@ -2473,6 +2483,8 @@ do_write:
        atomic64_inc(&c->btree_write_stats[type].nr);
        atomic64_add(bytes_to_write, &c->btree_write_stats[type].bytes);
 
+       async_object_list_add(c, btree_write_bio, wbio, &wbio->list_idx);
+
        INIT_WORK(&wbio->work, btree_write_submit);
        queue_work(c->btree_write_submit_wq, &wbio->work);
        return;
index dbf76d22c660d897af0efe9de75f0016a823eb80..afdb11a9f71cbebe9d1a7100e4d0f883e53682ea 100644 (file)
@@ -41,6 +41,9 @@ struct btree_read_bio {
        u64                     start_time;
        unsigned                have_ioref:1;
        unsigned                idx:7;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       unsigned                list_idx;
+#endif
        struct extent_ptr_decoded       pick;
        struct work_struct      work;
        struct bio              bio;
@@ -53,6 +56,9 @@ struct btree_write_bio {
        unsigned                data_bytes;
        unsigned                sector_offset;
        u64                     start_time;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       unsigned                list_idx;
+#endif
        struct bch_write_bio    wbio;
 };
 
@@ -133,6 +139,8 @@ void bch2_btree_node_read(struct btree_trans *, struct btree *, bool);
 int bch2_btree_root_read(struct bch_fs *, enum btree_id,
                         const struct bkey_i *, unsigned);
 
+void bch2_btree_read_bio_to_text(struct printbuf *, struct btree_read_bio *);
+
 int bch2_btree_node_scrub(struct btree_trans *, enum btree_id, unsigned,
                          struct bkey_s_c, unsigned);
 
index ed05125867da5216f519a3a120385cda7c6739c8..5e14d13568de8f980a4452be5d423a56d6657890 100644 (file)
@@ -50,6 +50,21 @@ struct data_update {
        struct bio_vec          *bvecs;
 };
 
+struct promote_op {
+       struct rcu_head         rcu;
+       u64                     start_time;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       unsigned                list_idx;
+#endif
+
+       struct rhash_head       hash;
+       struct bpos             pos;
+
+       struct work_struct      work;
+       struct data_update      write;
+       struct bio_vec          bi_inline_vecs[]; /* must be last */
+};
+
 void bch2_data_update_to_text(struct printbuf *, struct data_update *);
 void bch2_data_update_inflight_to_text(struct printbuf *, struct data_update *);
 
index df96e2c8ceda26ec967aa149158631b3772b87e7..abfd3a4c1d7d672265feed0116f685d3d6d2adde 100644 (file)
@@ -9,6 +9,7 @@
 #include "bcachefs.h"
 #include "alloc_background.h"
 #include "alloc_foreground.h"
+#include "async_objs.h"
 #include "btree_update.h"
 #include "buckets.h"
 #include "checksum.h"
@@ -88,18 +89,6 @@ static bool bch2_target_congested(struct bch_fs *c, u16 target)
 
 /* Cache promotion on read */
 
-struct promote_op {
-       struct rcu_head         rcu;
-       u64                     start_time;
-
-       struct rhash_head       hash;
-       struct bpos             pos;
-
-       struct work_struct      work;
-       struct data_update      write;
-       struct bio_vec          bi_inline_vecs[]; /* must be last */
-};
-
 static const struct rhashtable_params bch_promote_params = {
        .head_offset            = offsetof(struct promote_op, hash),
        .key_offset             = offsetof(struct promote_op, pos),
@@ -177,6 +166,8 @@ static noinline void promote_free(struct bch_read_bio *rbio)
                                         bch_promote_params);
        BUG_ON(ret);
 
+       async_object_list_del(c, promote, op->list_idx);
+
        bch2_data_update_exit(&op->write);
 
        enumerated_ref_put(&c->writes, BCH_WRITE_REF_promote);
@@ -262,6 +253,10 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans,
                goto err;
        }
 
+       ret = async_object_list_add(c, promote, op, &op->list_idx);
+       if (ret < 0)
+               goto err_remove_hash;
+
        ret = bch2_data_update_init(trans, NULL, NULL, &op->write,
                        writepoint_hashed((unsigned long) current),
                        &orig->opts,
@@ -273,7 +268,7 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans,
         * -BCH_ERR_ENOSPC_disk_reservation:
         */
        if (ret)
-               goto err_remove_hash;
+               goto err_remove_list;
 
        rbio_init_fragment(&op->write.rbio.bio, orig);
        op->write.rbio.bounce   = true;
@@ -281,6 +276,8 @@ static struct bch_read_bio *__promote_alloc(struct btree_trans *trans,
        op->write.op.end_io = promote_done;
 
        return &op->write.rbio;
+err_remove_list:
+       async_object_list_del(c, promote, op->list_idx);
 err_remove_hash:
        BUG_ON(rhashtable_remove_fast(&c->promote_table, &op->hash,
                                      bch_promote_params));
@@ -353,6 +350,18 @@ nopromote:
        return NULL;
 }
 
+void bch2_promote_op_to_text(struct printbuf *out, struct promote_op *op)
+{
+       if (!op->write.read_done) {
+               prt_printf(out, "parent read: %px\n", op->write.rbio.parent);
+               printbuf_indent_add(out, 2);
+               bch2_read_bio_to_text(out, op->write.rbio.parent);
+               printbuf_indent_sub(out, 2);
+       }
+
+       bch2_data_update_to_text(out, &op->write);
+}
+
 /* Read */
 
 static int bch2_read_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
@@ -421,6 +430,8 @@ static inline struct bch_read_bio *bch2_rbio_free(struct bch_read_bio *rbio)
                        else
                                promote_free(rbio);
                } else {
+                       async_object_list_del(rbio->c, rbio, rbio->list_idx);
+
                        if (rbio->bounce)
                                bch2_bio_free_pages_pool(rbio->c, &rbio->bio);
 
@@ -1246,6 +1257,8 @@ retry_pick:
        rbio->bio.bi_iter.bi_sector = pick.ptr.offset;
        rbio->bio.bi_end_io     = bch2_read_endio;
 
+       async_object_list_add(c, rbio, rbio, &rbio->list_idx);
+
        if (rbio->bounce)
                trace_and_count(c, io_read_bounce, &rbio->bio);
 
index 13bb68eb91c4ede27cc0ee0278af7f3da1ad2bde..c08b9c047b3eea03adea7a987baefb2a4787bfc7 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "bkey_buf.h"
 #include "btree_iter.h"
+#include "extents_types.h"
 #include "reflink.h"
 
 struct bch_read_bio {
@@ -48,6 +49,9 @@ struct bch_read_bio {
        u16                     _state;
        };
        s16                     ret;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       unsigned                list_idx;
+#endif
 
        struct extent_ptr_decoded pick;
 
@@ -173,6 +177,9 @@ static inline struct bch_read_bio *rbio_init_fragment(struct bio *bio,
        rbio->split             = true;
        rbio->parent            = orig;
        rbio->opts              = orig->opts;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       rbio->list_idx  = 0;
+#endif
        return rbio;
 }
 
@@ -190,9 +197,14 @@ static inline struct bch_read_bio *rbio_init(struct bio *bio,
        rbio->ret               = 0;
        rbio->opts              = opts;
        rbio->bio.bi_end_io     = end_io;
+#ifdef CONFIG_BCACHEFS_ASYNC_OBJECT_LISTS
+       rbio->list_idx  = 0;
+#endif
        return rbio;
 }
 
+struct promote_op;
+void bch2_promote_op_to_text(struct printbuf *, struct promote_op *);
 void bch2_read_bio_to_text(struct printbuf *, struct bch_read_bio *);
 
 void bch2_fs_io_read_exit(struct bch_fs *);