]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bcachefs: bch2_inum_to_path()
authorKent Overstreet <kent.overstreet@linux.dev>
Sat, 28 Sep 2024 19:40:49 +0000 (15:40 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 21 Dec 2024 06:36:21 +0000 (01:36 -0500)
Add a function for walking backpointers to find a path from a given
inode number, and convert various error messages to use it.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/errcode.h
fs/bcachefs/error.c
fs/bcachefs/error.h
fs/bcachefs/fs-common.c
fs/bcachefs/fs-common.h
fs/bcachefs/fs-io-buffered.c
fs/bcachefs/fsck.c
fs/bcachefs/io_misc.c
fs/bcachefs/io_read.c

index a0cfc0f286f4c125350410ff3c314ee65e3875d9..47387f7d6202af6d2579ce13d8e778a876dced3f 100644 (file)
        x(ENOENT,                       ENOENT_dirent_doesnt_match_inode)       \
        x(ENOENT,                       ENOENT_dev_not_found)                   \
        x(ENOENT,                       ENOENT_dev_idx_not_found)               \
+       x(ENOENT,                       ENOENT_inode_no_backpointer)            \
        x(ENOTEMPTY,                    ENOTEMPTY_dir_not_empty)                \
        x(ENOTEMPTY,                    ENOTEMPTY_subvol_not_empty)             \
        x(EEXIST,                       EEXIST_str_hash_set)                    \
index 0517782ca57a611d535921394d4c92b519eb0d29..abaa9570cd6201eccf9fd3a58dc0e0391b6eacf0 100644 (file)
@@ -3,6 +3,7 @@
 #include "btree_cache.h"
 #include "btree_iter.h"
 #include "error.h"
+#include "fs-common.h"
 #include "journal.h"
 #include "recovery_passes.h"
 #include "super.h"
@@ -515,3 +516,36 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
 
        mutex_unlock(&c->fsck_error_msgs_lock);
 }
+
+int bch2_inum_err_msg_trans(struct btree_trans *trans, struct printbuf *out, subvol_inum inum)
+{
+       u32 restart_count = trans->restart_count;
+       int ret = 0;
+
+       /* XXX: we don't yet attempt to print paths when we don't know the subvol */
+       if (inum.subvol)
+               ret = lockrestart_do(trans, bch2_inum_to_path(trans, inum, out));
+       if (!inum.subvol || ret)
+               prt_printf(out, "inum %llu:%llu", inum.subvol, inum.inum);
+
+       return trans_was_restarted(trans, restart_count);
+}
+
+int bch2_inum_offset_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
+                                   subvol_inum inum, u64 offset)
+{
+       int ret = bch2_inum_err_msg_trans(trans, out, inum);
+       prt_printf(out, " offset %llu: ", offset);
+       return ret;
+}
+
+void bch2_inum_err_msg(struct bch_fs *c, struct printbuf *out, subvol_inum inum)
+{
+       bch2_trans_run(c, bch2_inum_err_msg_trans(trans, out, inum));
+}
+
+void bch2_inum_offset_err_msg(struct bch_fs *c, struct printbuf *out,
+                             subvol_inum inum, u64 offset)
+{
+       bch2_trans_run(c, bch2_inum_offset_err_msg_trans(trans, out, inum, offset));
+}
index 12ca5287e20a0cc6dc1cfd06a71ee36f52304e5c..7acf2a27ca281fd7edefd6ab38c276274a6f7b41 100644 (file)
@@ -238,4 +238,10 @@ void bch2_io_error(struct bch_dev *, enum bch_member_error_type);
        _ret;                                                           \
 })
 
+int bch2_inum_err_msg_trans(struct btree_trans *, struct printbuf *, subvol_inum);
+int bch2_inum_offset_err_msg_trans(struct btree_trans *, struct printbuf *, subvol_inum, u64);
+
+void bch2_inum_err_msg(struct bch_fs *, struct printbuf *, subvol_inum);
+void bch2_inum_offset_err_msg(struct bch_fs *, struct printbuf *, subvol_inum, u64);
+
 #endif /* _BCACHEFS_ERROR_H */
index 7e10a9ddcfd9633d13b9dce2dd0dab8fa58425b4..dcaa47f68f31ec71362fdb265624e5608d316a9c 100644 (file)
@@ -548,3 +548,84 @@ err:
        bch2_trans_iter_exit(trans, &src_dir_iter);
        return ret;
 }
+
+static inline void prt_bytes_reversed(struct printbuf *out, const void *b, unsigned n)
+{
+       bch2_printbuf_make_room(out, n);
+
+       unsigned can_print = min(n, printbuf_remaining(out));
+
+       b += n;
+
+       for (unsigned i = 0; i < can_print; i++)
+               out->buf[out->pos++] = *((char *) --b);
+
+       printbuf_nul_terminate(out);
+}
+
+static inline void reverse_bytes(void *b, size_t n)
+{
+       char *e = b + n, *s = b;
+
+       while (s < e) {
+               --e;
+               swap(*s, *e);
+               s++;
+       }
+}
+
+/* XXX: we don't yet attempt to print paths when we don't know the subvol */
+int bch2_inum_to_path(struct btree_trans *trans, subvol_inum inum, struct printbuf *path)
+{
+       unsigned orig_pos = path->pos;
+       int ret = 0;
+
+       while (!(inum.subvol == BCACHEFS_ROOT_SUBVOL &&
+                inum.inum   == BCACHEFS_ROOT_INO)) {
+               struct bch_inode_unpacked inode;
+               ret = bch2_inode_find_by_inum_trans(trans, inum, &inode);
+               if (ret)
+                       goto err;
+
+               if (!inode.bi_dir && !inode.bi_dir_offset) {
+                       ret = -BCH_ERR_ENOENT_inode_no_backpointer;
+                       goto err;
+               }
+
+               u32 snapshot;
+               ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot);
+               if (ret)
+                       goto err;
+
+               struct btree_iter d_iter;
+               struct bkey_s_c_dirent d = bch2_bkey_get_iter_typed(trans, &d_iter,
+                               BTREE_ID_dirents, SPOS(inode.bi_dir, inode.bi_dir_offset, snapshot),
+                               0, dirent);
+               ret = bkey_err(d.s_c);
+               if (ret)
+                       goto err;
+
+               struct qstr dirent_name = bch2_dirent_get_name(d);
+               prt_bytes_reversed(path, dirent_name.name, dirent_name.len);
+
+               prt_char(path, '/');
+
+               if (d.v->d_type == DT_SUBVOL)
+                       inum.subvol = le32_to_cpu(d.v->d_parent_subvol);
+               inum.inum = d.k->p.inode;
+
+               bch2_trans_iter_exit(trans, &d_iter);
+       }
+
+       if (orig_pos == path->pos)
+               prt_char(path, '/');
+
+       ret = path->allocation_failure ? -ENOMEM : 0;
+       if (ret)
+               goto err;
+
+       reverse_bytes(path->buf + orig_pos, path->pos - orig_pos);
+       return 0;
+err:
+       return ret;
+}
index c934e807b380f3ca2d784a4d202f8ccf5e404263..2b59210bb5e8a9f68fc313a3e28e1267538f9362 100644 (file)
@@ -42,4 +42,6 @@ int bch2_rename_trans(struct btree_trans *,
 bool bch2_reinherit_attrs(struct bch_inode_unpacked *,
                          struct bch_inode_unpacked *);
 
+int bch2_inum_to_path(struct btree_trans *, subvol_inum, struct printbuf *);
+
 #endif /* _BCACHEFS_FS_COMMON_H */
index d55e215e8aa62a8b3796799a691562fd62107178..ff8b8df50bf38b074cbfd43e83b4a04a6de6c285 100644 (file)
@@ -231,10 +231,12 @@ err:
        bch2_trans_iter_exit(trans, &iter);
 
        if (ret) {
-               bch_err_inum_offset_ratelimited(c,
-                               iter.pos.inode,
-                               iter.pos.offset << 9,
-                               "read error %i from btree lookup", ret);
+               struct printbuf buf = PRINTBUF;
+               bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter.pos.offset << 9);
+               prt_printf(&buf, "read error %i from btree lookup", ret);
+               bch_err_ratelimited(c, "%s", buf.buf);
+               printbuf_exit(&buf);
+
                rbio->bio.bi_status = BLK_STS_IOERR;
                bio_endio(&rbio->bio);
        }
index cc15ff135cd6da78205675f8411bb444da351a66..1a5a07112779adfb868ee6d7e7e34d2cf5082657 100644 (file)
@@ -212,6 +212,7 @@ static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
 {
        struct bch_fs *c = trans->c;
        struct qstr lostfound_str = QSTR("lost+found");
+       struct btree_iter lostfound_iter = { NULL };
        u64 inum = 0;
        unsigned d_type = 0;
        int ret;
@@ -290,11 +291,16 @@ create_lostfound:
         * XXX: we could have a nicer log message here  if we had a nice way to
         * walk backpointers to print a path
         */
-       bch_notice(c, "creating lost+found in subvol %llu snapshot %u",
-                  root_inum.subvol, le32_to_cpu(st.root_snapshot));
+       struct printbuf path = PRINTBUF;
+       ret = bch2_inum_to_path(trans, root_inum, &path);
+       if (ret)
+               goto err;
+
+       bch_notice(c, "creating %s/lost+found in subvol %llu snapshot %u",
+                  path.buf, root_inum.subvol, snapshot);
+       printbuf_exit(&path);
 
        u64 now = bch2_current_time(c);
-       struct btree_iter lostfound_iter = { NULL };
        u64 cpu = raw_smp_processor_id();
 
        bch2_inode_init_early(c, lostfound);
index 524e31e7411bf52cd211afb8394fdfa969ae831e..5353979117b0a44c8b4c54bed82846631c944558 100644 (file)
@@ -113,11 +113,13 @@ int bch2_extent_fallocate(struct btree_trans *trans,
 err:
        if (!ret && sectors_allocated)
                bch2_increment_clock(c, sectors_allocated, WRITE);
-       if (should_print_err(ret))
-               bch_err_inum_offset_ratelimited(c,
-                       inum.inum,
-                       iter->pos.offset << 9,
-                       "%s(): error: %s", __func__, bch2_err_str(ret));
+       if (should_print_err(ret)) {
+               struct printbuf buf = PRINTBUF;
+               bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter->pos.offset << 9);
+               prt_printf(&buf, "fallocate error: %s", bch2_err_str(ret));
+               bch_err_ratelimited(c, "%s", buf.buf);
+               printbuf_exit(&buf);
+       }
 err_noprint:
        bch2_open_buckets_put(c, &open_buckets);
        bch2_disk_reservation_put(c, &disk_res);
index 4b6b6d25725bb020db7bfb5ed831013cab9af3e6..34a3569d085a3d8ee700f9bdd078fb352a79fc7c 100644 (file)
@@ -322,6 +322,20 @@ nopromote:
 
 /* Read */
 
+static int bch2_read_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
+                                  struct bch_read_bio *rbio, struct bpos read_pos)
+{
+       return bch2_inum_offset_err_msg_trans(trans, out,
+               (subvol_inum) { rbio->subvol, read_pos.inode },
+               read_pos.offset << 9);
+}
+
+static void bch2_read_err_msg(struct bch_fs *c, struct printbuf *out,
+                             struct bch_read_bio *rbio, struct bpos read_pos)
+{
+       bch2_trans_run(c, bch2_read_err_msg_trans(trans, out, rbio, read_pos));
+}
+
 #define READ_RETRY_AVOID       1
 #define READ_RETRY             2
 #define READ_ERR               3
@@ -500,6 +514,29 @@ static void bch2_rbio_error(struct bch_read_bio *rbio, int retry,
        }
 }
 
+static void bch2_read_io_err(struct work_struct *work)
+{
+       struct bch_read_bio *rbio =
+               container_of(work, struct bch_read_bio, work);
+       struct bio *bio = &rbio->bio;
+       struct bch_fs *c        = rbio->c;
+       struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
+       struct printbuf buf = PRINTBUF;
+
+       bch2_read_err_msg(c, &buf, rbio, rbio->read_pos);
+       prt_printf(&buf, "data read error: %s", bch2_blk_status_to_str(bio->bi_status));
+
+       if (ca) {
+               bch2_io_error(ca, BCH_MEMBER_ERROR_read);
+               bch_err_ratelimited(ca, "%s", buf.buf);
+       } else {
+               bch_err_ratelimited(c, "%s", buf.buf);
+       }
+
+       printbuf_exit(&buf);
+       bch2_rbio_error(rbio, READ_RETRY_AVOID, bio->bi_status);
+}
+
 static int __bch2_rbio_narrow_crcs(struct btree_trans *trans,
                                   struct bch_read_bio *rbio)
 {
@@ -563,6 +600,73 @@ static noinline void bch2_rbio_narrow_crcs(struct bch_read_bio *rbio)
                             __bch2_rbio_narrow_crcs(trans, rbio));
 }
 
+static void bch2_read_csum_err(struct work_struct *work)
+{
+       struct bch_read_bio *rbio =
+               container_of(work, struct bch_read_bio, work);
+       struct bch_fs *c        = rbio->c;
+       struct bio *src         = &rbio->bio;
+       struct bch_extent_crc_unpacked crc = rbio->pick.crc;
+       struct nonce nonce = extent_nonce(rbio->version, crc);
+       struct bch_csum csum = bch2_checksum_bio(c, crc.csum_type, nonce, src);
+       struct printbuf buf = PRINTBUF;
+
+       bch2_read_err_msg(c, &buf, rbio, rbio->read_pos);
+       prt_str(&buf, "data ");
+       bch2_csum_err_msg(&buf, crc.csum_type, rbio->pick.crc.csum, csum);
+
+       struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
+       if (ca) {
+               bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
+               bch_err_ratelimited(ca, "%s", buf.buf);
+       } else {
+               bch_err_ratelimited(c, "%s", buf.buf);
+       }
+
+       bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
+       printbuf_exit(&buf);
+}
+
+static void bch2_read_decompress_err(struct work_struct *work)
+{
+       struct bch_read_bio *rbio =
+               container_of(work, struct bch_read_bio, work);
+       struct bch_fs *c        = rbio->c;
+       struct printbuf buf = PRINTBUF;
+
+       bch2_read_err_msg(c, &buf, rbio, rbio->read_pos);
+       prt_str(&buf, "decompression error");
+
+       struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
+       if (ca)
+               bch_err_ratelimited(ca, "%s", buf.buf);
+       else
+               bch_err_ratelimited(c, "%s", buf.buf);
+
+       bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
+       printbuf_exit(&buf);
+}
+
+static void bch2_read_decrypt_err(struct work_struct *work)
+{
+       struct bch_read_bio *rbio =
+               container_of(work, struct bch_read_bio, work);
+       struct bch_fs *c        = rbio->c;
+       struct printbuf buf = PRINTBUF;
+
+       bch2_read_err_msg(c, &buf, rbio, rbio->read_pos);
+       prt_str(&buf, "decrypt error");
+
+       struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
+       if (ca)
+               bch_err_ratelimited(ca, "%s", buf.buf);
+       else
+               bch_err_ratelimited(c, "%s", buf.buf);
+
+       bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
+       printbuf_exit(&buf);
+}
+
 /* Inner part that may run in process context */
 static void __bch2_read_endio(struct work_struct *work)
 {
@@ -669,33 +773,13 @@ csum_err:
                goto out;
        }
 
-       struct printbuf buf = PRINTBUF;
-       buf.atomic++;
-       prt_str(&buf, "data ");
-       bch2_csum_err_msg(&buf, crc.csum_type, rbio->pick.crc.csum, csum);
-
-       struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
-       if (ca) {
-               bch_err_inum_offset_ratelimited(ca,
-                       rbio->read_pos.inode,
-                       rbio->read_pos.offset << 9,
-                       "data %s", buf.buf);
-               bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
-       }
-       printbuf_exit(&buf);
-       bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
+       bch2_rbio_punt(rbio, bch2_read_csum_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
        goto out;
 decompression_err:
-       bch_err_inum_offset_ratelimited(c, rbio->read_pos.inode,
-                                       rbio->read_pos.offset << 9,
-                                       "decompression error");
-       bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
+       bch2_rbio_punt(rbio, bch2_read_decompress_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
        goto out;
 decrypt_err:
-       bch_err_inum_offset_ratelimited(c, rbio->read_pos.inode,
-                                       rbio->read_pos.offset << 9,
-                                       "decrypt error");
-       bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
+       bch2_rbio_punt(rbio, bch2_read_decrypt_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
        goto out;
 }
 
@@ -716,16 +800,8 @@ static void bch2_read_endio(struct bio *bio)
        if (!rbio->split)
                rbio->bio.bi_end_io = rbio->end_io;
 
-       if (bio->bi_status) {
-               if (ca) {
-                       bch_err_inum_offset_ratelimited(ca,
-                               rbio->read_pos.inode,
-                               rbio->read_pos.offset,
-                               "data read error: %s",
-                               bch2_blk_status_to_str(bio->bi_status));
-                       bch2_io_error(ca, BCH_MEMBER_ERROR_read);
-               }
-               bch2_rbio_error(rbio, READ_RETRY_AVOID, bio->bi_status);
+       if (unlikely(bio->bi_status)) {
+               bch2_rbio_punt(rbio, bch2_read_io_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
                return;
        }
 
@@ -832,25 +908,22 @@ retry_pick:
 
        if (unlikely(pick_ret < 0)) {
                struct printbuf buf = PRINTBUF;
+               bch2_read_err_msg_trans(trans, &buf, orig, read_pos);
+               prt_printf(&buf, "no device to read from: %s\n  ", bch2_err_str(pick_ret));
                bch2_bkey_val_to_text(&buf, c, k);
 
-               bch_err_inum_offset_ratelimited(c,
-                               read_pos.inode, read_pos.offset << 9,
-                               "no device to read from: %s\n  %s",
-                               bch2_err_str(pick_ret),
-                               buf.buf);
+               bch_err_ratelimited(c, "%s", buf.buf);
                printbuf_exit(&buf);
                goto err;
        }
 
        if (unlikely(bch2_csum_type_is_encryption(pick.crc.csum_type)) && !c->chacha20) {
                struct printbuf buf = PRINTBUF;
+               bch2_read_err_msg_trans(trans, &buf, orig, read_pos);
+               prt_printf(&buf, "attempting to read encrypted data without encryption key\n  ");
                bch2_bkey_val_to_text(&buf, c, k);
 
-               bch_err_inum_offset_ratelimited(c,
-                               read_pos.inode, read_pos.offset << 9,
-                               "attempting to read encrypted data without encryption key\n  %s",
-                               buf.buf);
+               bch_err_ratelimited(c, "%s", buf.buf);
                printbuf_exit(&buf);
                goto err;
        }
@@ -1036,11 +1109,15 @@ get_bio:
        }
 
        if (!rbio->pick.idx) {
-               if (!rbio->have_ioref) {
-                       bch_err_inum_offset_ratelimited(c,
-                                       read_pos.inode,
-                                       read_pos.offset << 9,
-                                       "no device to read from");
+               if (unlikely(!rbio->have_ioref)) {
+                       struct printbuf buf = PRINTBUF;
+                       bch2_read_err_msg_trans(trans, &buf, rbio, read_pos);
+                       prt_printf(&buf, "no device to read from:\n  ");
+                       bch2_bkey_val_to_text(&buf, c, k);
+
+                       bch_err_ratelimited(c, "%s", buf.buf);
+                       printbuf_exit(&buf);
+
                        bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
                        goto out;
                }
@@ -1202,16 +1279,20 @@ err:
        }
 
        bch2_trans_iter_exit(trans, &iter);
-       bch2_trans_put(trans);
-       bch2_bkey_buf_exit(&sk, c);
 
        if (ret) {
-               bch_err_inum_offset_ratelimited(c, inum.inum,
-                                               bvec_iter.bi_sector << 9,
-                                               "read error %i from btree lookup", ret);
+               struct printbuf buf = PRINTBUF;
+               bch2_inum_offset_err_msg_trans(trans, &buf, inum, bvec_iter.bi_sector << 9);
+               prt_printf(&buf, "read error %i from btree lookup", ret);
+               bch_err_ratelimited(c, "%s", buf.buf);
+               printbuf_exit(&buf);
+
                rbio->bio.bi_status = BLK_STS_IOERR;
                bch2_rbio_done(rbio);
        }
+
+       bch2_trans_put(trans);
+       bch2_bkey_buf_exit(&sk, c);
 }
 
 void bch2_fs_io_read_exit(struct bch_fs *c)