bcachefs: bch2_inum_to_path()

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>
This commit is contained in:
Kent Overstreet 2024-09-28 15:40:49 -04:00
parent 428a2c2d6b
commit 2ab8d31989
9 changed files with 279 additions and 64 deletions

View File

@ -116,6 +116,7 @@
x(ENOENT, ENOENT_dirent_doesnt_match_inode) \ x(ENOENT, ENOENT_dirent_doesnt_match_inode) \
x(ENOENT, ENOENT_dev_not_found) \ x(ENOENT, ENOENT_dev_not_found) \
x(ENOENT, ENOENT_dev_idx_not_found) \ x(ENOENT, ENOENT_dev_idx_not_found) \
x(ENOENT, ENOENT_inode_no_backpointer) \
x(ENOTEMPTY, ENOTEMPTY_dir_not_empty) \ x(ENOTEMPTY, ENOTEMPTY_dir_not_empty) \
x(ENOTEMPTY, ENOTEMPTY_subvol_not_empty) \ x(ENOTEMPTY, ENOTEMPTY_subvol_not_empty) \
x(EEXIST, EEXIST_str_hash_set) \ x(EEXIST, EEXIST_str_hash_set) \

View File

@ -3,6 +3,7 @@
#include "btree_cache.h" #include "btree_cache.h"
#include "btree_iter.h" #include "btree_iter.h"
#include "error.h" #include "error.h"
#include "fs-common.h"
#include "journal.h" #include "journal.h"
#include "recovery_passes.h" #include "recovery_passes.h"
#include "super.h" #include "super.h"
@ -515,3 +516,36 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
mutex_unlock(&c->fsck_error_msgs_lock); 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));
}

View File

@ -238,4 +238,10 @@ void bch2_io_error(struct bch_dev *, enum bch_member_error_type);
_ret; \ _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 */ #endif /* _BCACHEFS_ERROR_H */

View File

@ -548,3 +548,84 @@ int bch2_rename_trans(struct btree_trans *trans,
bch2_trans_iter_exit(trans, &src_dir_iter); bch2_trans_iter_exit(trans, &src_dir_iter);
return ret; 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;
}

View File

@ -42,4 +42,6 @@ int bch2_rename_trans(struct btree_trans *,
bool bch2_reinherit_attrs(struct bch_inode_unpacked *, bool bch2_reinherit_attrs(struct bch_inode_unpacked *,
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 */ #endif /* _BCACHEFS_FS_COMMON_H */

View File

@ -231,10 +231,12 @@ static void bchfs_read(struct btree_trans *trans,
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, &iter);
if (ret) { if (ret) {
bch_err_inum_offset_ratelimited(c, struct printbuf buf = PRINTBUF;
iter.pos.inode, bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter.pos.offset << 9);
iter.pos.offset << 9, prt_printf(&buf, "read error %i from btree lookup", ret);
"read error %i from btree lookup", ret); bch_err_ratelimited(c, "%s", buf.buf);
printbuf_exit(&buf);
rbio->bio.bi_status = BLK_STS_IOERR; rbio->bio.bi_status = BLK_STS_IOERR;
bio_endio(&rbio->bio); bio_endio(&rbio->bio);
} }

View File

@ -212,6 +212,7 @@ static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct qstr lostfound_str = QSTR("lost+found"); struct qstr lostfound_str = QSTR("lost+found");
struct btree_iter lostfound_iter = { NULL };
u64 inum = 0; u64 inum = 0;
unsigned d_type = 0; unsigned d_type = 0;
int ret; int ret;
@ -290,11 +291,16 @@ static int lookup_lostfound(struct btree_trans *trans, u32 snapshot,
* XXX: we could have a nicer log message here if we had a nice way to * XXX: we could have a nicer log message here if we had a nice way to
* walk backpointers to print a path * walk backpointers to print a path
*/ */
bch_notice(c, "creating lost+found in subvol %llu snapshot %u", struct printbuf path = PRINTBUF;
root_inum.subvol, le32_to_cpu(st.root_snapshot)); 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); u64 now = bch2_current_time(c);
struct btree_iter lostfound_iter = { NULL };
u64 cpu = raw_smp_processor_id(); u64 cpu = raw_smp_processor_id();
bch2_inode_init_early(c, lostfound); bch2_inode_init_early(c, lostfound);

View File

@ -113,11 +113,13 @@ int bch2_extent_fallocate(struct btree_trans *trans,
err: err:
if (!ret && sectors_allocated) if (!ret && sectors_allocated)
bch2_increment_clock(c, sectors_allocated, WRITE); bch2_increment_clock(c, sectors_allocated, WRITE);
if (should_print_err(ret)) if (should_print_err(ret)) {
bch_err_inum_offset_ratelimited(c, struct printbuf buf = PRINTBUF;
inum.inum, bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter->pos.offset << 9);
iter->pos.offset << 9, prt_printf(&buf, "fallocate error: %s", bch2_err_str(ret));
"%s(): error: %s", __func__, bch2_err_str(ret)); bch_err_ratelimited(c, "%s", buf.buf);
printbuf_exit(&buf);
}
err_noprint: err_noprint:
bch2_open_buckets_put(c, &open_buckets); bch2_open_buckets_put(c, &open_buckets);
bch2_disk_reservation_put(c, &disk_res); bch2_disk_reservation_put(c, &disk_res);

View File

@ -322,6 +322,20 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
/* Read */ /* 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_AVOID 1
#define READ_RETRY 2 #define READ_RETRY 2
#define READ_ERR 3 #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, static int __bch2_rbio_narrow_crcs(struct btree_trans *trans,
struct bch_read_bio *rbio) 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)); __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 */ /* Inner part that may run in process context */
static void __bch2_read_endio(struct work_struct *work) static void __bch2_read_endio(struct work_struct *work)
{ {
@ -669,33 +773,13 @@ static void __bch2_read_endio(struct work_struct *work)
goto out; goto out;
} }
struct printbuf buf = PRINTBUF; bch2_rbio_punt(rbio, bch2_read_csum_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
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);
goto out; goto out;
decompression_err: decompression_err:
bch_err_inum_offset_ratelimited(c, rbio->read_pos.inode, bch2_rbio_punt(rbio, bch2_read_decompress_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
rbio->read_pos.offset << 9,
"decompression error");
bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
goto out; goto out;
decrypt_err: decrypt_err:
bch_err_inum_offset_ratelimited(c, rbio->read_pos.inode, bch2_rbio_punt(rbio, bch2_read_decrypt_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
rbio->read_pos.offset << 9,
"decrypt error");
bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
goto out; goto out;
} }
@ -716,16 +800,8 @@ static void bch2_read_endio(struct bio *bio)
if (!rbio->split) if (!rbio->split)
rbio->bio.bi_end_io = rbio->end_io; rbio->bio.bi_end_io = rbio->end_io;
if (bio->bi_status) { if (unlikely(bio->bi_status)) {
if (ca) { bch2_rbio_punt(rbio, bch2_read_io_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
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);
return; return;
} }
@ -832,25 +908,22 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
if (unlikely(pick_ret < 0)) { if (unlikely(pick_ret < 0)) {
struct printbuf buf = PRINTBUF; 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); bch2_bkey_val_to_text(&buf, c, k);
bch_err_inum_offset_ratelimited(c, bch_err_ratelimited(c, "%s", buf.buf);
read_pos.inode, read_pos.offset << 9,
"no device to read from: %s\n %s",
bch2_err_str(pick_ret),
buf.buf);
printbuf_exit(&buf); printbuf_exit(&buf);
goto err; goto err;
} }
if (unlikely(bch2_csum_type_is_encryption(pick.crc.csum_type)) && !c->chacha20) { if (unlikely(bch2_csum_type_is_encryption(pick.crc.csum_type)) && !c->chacha20) {
struct printbuf buf = PRINTBUF; 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); bch2_bkey_val_to_text(&buf, c, k);
bch_err_inum_offset_ratelimited(c, bch_err_ratelimited(c, "%s", buf.buf);
read_pos.inode, read_pos.offset << 9,
"attempting to read encrypted data without encryption key\n %s",
buf.buf);
printbuf_exit(&buf); printbuf_exit(&buf);
goto err; goto err;
} }
@ -1036,11 +1109,15 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
} }
if (!rbio->pick.idx) { if (!rbio->pick.idx) {
if (!rbio->have_ioref) { if (unlikely(!rbio->have_ioref)) {
bch_err_inum_offset_ratelimited(c, struct printbuf buf = PRINTBUF;
read_pos.inode, bch2_read_err_msg_trans(trans, &buf, rbio, read_pos);
read_pos.offset << 9, prt_printf(&buf, "no device to read from:\n ");
"no device to read from"); 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); bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
goto out; goto out;
} }
@ -1202,16 +1279,20 @@ void __bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
} }
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, &iter);
bch2_trans_put(trans);
bch2_bkey_buf_exit(&sk, c);
if (ret) { if (ret) {
bch_err_inum_offset_ratelimited(c, inum.inum, struct printbuf buf = PRINTBUF;
bvec_iter.bi_sector << 9, bch2_inum_offset_err_msg_trans(trans, &buf, inum, bvec_iter.bi_sector << 9);
"read error %i from btree lookup", ret); 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; rbio->bio.bi_status = BLK_STS_IOERR;
bch2_rbio_done(rbio); bch2_rbio_done(rbio);
} }
bch2_trans_put(trans);
bch2_bkey_buf_exit(&sk, c);
} }
void bch2_fs_io_read_exit(struct bch_fs *c) void bch2_fs_io_read_exit(struct bch_fs *c)