diff --git a/6.5/misc/0001-bcachefs.patch b/6.5/misc/0001-bcachefs.patch index e4e6ebdb..894b267b 100644 --- a/6.5/misc/0001-bcachefs.patch +++ b/6.5/misc/0001-bcachefs.patch @@ -1,6 +1,6 @@ -From d38fd7e7c19d40faeaf94e3e5237d382ab122c0e Mon Sep 17 00:00:00 2001 +From 0c07fb584b982c7c1d33fbbf84dd37b28690bd8b Mon Sep 17 00:00:00 2001 From: Piotr Gorski -Date: Wed, 25 Oct 2023 09:08:59 +0200 +Date: Fri, 27 Oct 2023 15:33:43 +0200 Subject: [PATCH] bcachefs Signed-off-by: Piotr Gorski @@ -28,36 +28,36 @@ Signed-off-by: Piotr Gorski fs/Makefile | 1 + fs/aio.c | 66 +- fs/bcachefs/Kconfig | 76 + - fs/bcachefs/Makefile | 85 + + fs/bcachefs/Makefile | 86 + fs/bcachefs/acl.c | 463 +++ fs/bcachefs/acl.h | 60 + - fs/bcachefs/alloc_background.c | 2146 +++++++++++ + fs/bcachefs/alloc_background.c | 2156 +++++++++++ fs/bcachefs/alloc_background.h | 258 ++ fs/bcachefs/alloc_foreground.c | 1576 ++++++++ fs/bcachefs/alloc_foreground.h | 224 ++ fs/bcachefs/alloc_types.h | 126 + - fs/bcachefs/backpointers.c | 868 +++++ + fs/bcachefs/backpointers.c | 872 +++++ fs/bcachefs/backpointers.h | 131 + fs/bcachefs/bbpos.h | 37 + fs/bcachefs/bbpos_types.h | 18 + - fs/bcachefs/bcachefs.h | 1155 ++++++ - fs/bcachefs/bcachefs_format.h | 2403 ++++++++++++ + fs/bcachefs/bcachefs.h | 1161 ++++++ + fs/bcachefs/bcachefs_format.h | 2430 +++++++++++++ fs/bcachefs/bcachefs_ioctl.h | 368 ++ fs/bcachefs/bkey.c | 1120 ++++++ fs/bcachefs/bkey.h | 782 ++++ fs/bcachefs/bkey_buf.h | 61 + fs/bcachefs/bkey_cmp.h | 129 + - fs/bcachefs/bkey_methods.c | 469 +++ - fs/bcachefs/bkey_methods.h | 178 + + fs/bcachefs/bkey_methods.c | 454 +++ + fs/bcachefs/bkey_methods.h | 179 + fs/bcachefs/bkey_sort.c | 201 + fs/bcachefs/bkey_sort.h | 54 + fs/bcachefs/bset.c | 1592 ++++++++ fs/bcachefs/bset.h | 541 +++ fs/bcachefs/btree_cache.c | 1276 +++++++ fs/bcachefs/btree_cache.h | 131 + - fs/bcachefs/btree_gc.c | 2112 +++++++++++ + fs/bcachefs/btree_gc.c | 2148 +++++++++++ fs/bcachefs/btree_gc.h | 114 + - fs/bcachefs/btree_io.c | 2213 ++++++++++++ + fs/bcachefs/btree_io.c | 2298 ++++++++++++ fs/bcachefs/btree_io.h | 228 ++ fs/bcachefs/btree_iter.c | 3217 +++++++++++++++++ fs/bcachefs/btree_iter.h | 939 +++++ @@ -76,7 +76,7 @@ Signed-off-by: Piotr Gorski fs/bcachefs/btree_write_buffer.c | 375 ++ fs/bcachefs/btree_write_buffer.h | 14 + fs/bcachefs/btree_write_buffer_types.h | 44 + - fs/bcachefs/buckets.c | 2161 +++++++++++ + fs/bcachefs/buckets.c | 2168 +++++++++++ fs/bcachefs/buckets.h | 458 +++ fs/bcachefs/buckets_types.h | 92 + fs/bcachefs/buckets_waiting_for_journal.c | 166 + @@ -98,21 +98,21 @@ Signed-off-by: Piotr Gorski fs/bcachefs/data_update.h | 44 + fs/bcachefs/debug.c | 954 +++++ fs/bcachefs/debug.h | 32 + - fs/bcachefs/dirent.c | 587 +++ + fs/bcachefs/dirent.c | 577 +++ fs/bcachefs/dirent.h | 70 + fs/bcachefs/disk_groups.c | 620 ++++ fs/bcachefs/disk_groups.h | 111 + fs/bcachefs/disk_groups_types.h | 18 + - fs/bcachefs/ec.c | 1966 ++++++++++ + fs/bcachefs/ec.c | 1967 ++++++++++ fs/bcachefs/ec.h | 260 ++ fs/bcachefs/ec_types.h | 41 + fs/bcachefs/errcode.c | 68 + - fs/bcachefs/errcode.h | 266 ++ - fs/bcachefs/error.c | 293 ++ - fs/bcachefs/error.h | 206 ++ + fs/bcachefs/errcode.h | 267 ++ + fs/bcachefs/error.c | 299 ++ + fs/bcachefs/error.h | 242 ++ fs/bcachefs/extent_update.c | 173 + fs/bcachefs/extent_update.h | 12 + - fs/bcachefs/extents.c | 1540 ++++++++ + fs/bcachefs/extents.c | 1516 ++++++++ fs/bcachefs/extents.h | 765 ++++ fs/bcachefs/extents_types.h | 40 + fs/bcachefs/eytzinger.h | 281 ++ @@ -131,9 +131,9 @@ Signed-off-by: Piotr Gorski fs/bcachefs/fs-ioctl.h | 81 + fs/bcachefs/fs.c | 1980 ++++++++++ fs/bcachefs/fs.h | 209 ++ - fs/bcachefs/fsck.c | 2466 +++++++++++++ + fs/bcachefs/fsck.c | 2490 +++++++++++++ fs/bcachefs/fsck.h | 15 + - fs/bcachefs/inode.c | 1147 ++++++ + fs/bcachefs/inode.c | 1150 ++++++ fs/bcachefs/inode.h | 208 ++ fs/bcachefs/io_misc.c | 524 +++ fs/bcachefs/io_misc.h | 34 + @@ -144,7 +144,7 @@ Signed-off-by: Piotr Gorski fs/bcachefs/io_write_types.h | 96 + fs/bcachefs/journal.c | 1468 ++++++++ fs/bcachefs/journal.h | 549 +++ - fs/bcachefs/journal_io.c | 1894 ++++++++++ + fs/bcachefs/journal_io.c | 1931 ++++++++++ fs/bcachefs/journal_io.h | 65 + fs/bcachefs/journal_reclaim.c | 876 +++++ fs/bcachefs/journal_reclaim.h | 87 + @@ -158,7 +158,7 @@ Signed-off-by: Piotr Gorski fs/bcachefs/keylist_types.h | 16 + fs/bcachefs/logged_ops.c | 112 + fs/bcachefs/logged_ops.h | 20 + - fs/bcachefs/lru.c | 162 + + fs/bcachefs/lru.c | 164 + fs/bcachefs/lru.h | 69 + fs/bcachefs/migrate.c | 179 + fs/bcachefs/migrate.h | 7 + @@ -170,17 +170,17 @@ Signed-off-by: Piotr Gorski fs/bcachefs/nocow_locking.c | 144 + fs/bcachefs/nocow_locking.h | 50 + fs/bcachefs/nocow_locking_types.h | 20 + - fs/bcachefs/opts.c | 607 ++++ - fs/bcachefs/opts.h | 565 +++ + fs/bcachefs/opts.c | 602 +++ + fs/bcachefs/opts.h | 564 +++ fs/bcachefs/printbuf.c | 425 +++ fs/bcachefs/printbuf.h | 284 ++ - fs/bcachefs/quota.c | 978 +++++ + fs/bcachefs/quota.c | 979 +++++ fs/bcachefs/quota.h | 74 + fs/bcachefs/quota_types.h | 43 + - fs/bcachefs/rebalance.c | 465 +++ + fs/bcachefs/rebalance.c | 466 +++ fs/bcachefs/rebalance.h | 27 + fs/bcachefs/rebalance_types.h | 37 + - fs/bcachefs/recovery.c | 1044 ++++++ + fs/bcachefs/recovery.c | 1050 ++++++ fs/bcachefs/recovery.h | 33 + fs/bcachefs/recovery_types.h | 53 + fs/bcachefs/reflink.c | 406 +++ @@ -188,27 +188,30 @@ Signed-off-by: Piotr Gorski fs/bcachefs/replicas.c | 1058 ++++++ fs/bcachefs/replicas.h | 91 + fs/bcachefs/replicas_types.h | 27 + - fs/bcachefs/sb-clean.c | 395 ++ + fs/bcachefs/sb-clean.c | 398 ++ fs/bcachefs/sb-clean.h | 16 + - fs/bcachefs/sb-members.c | 339 ++ - fs/bcachefs/sb-members.h | 182 + + fs/bcachefs/sb-errors.c | 175 + + fs/bcachefs/sb-errors.h | 270 ++ + fs/bcachefs/sb-errors_types.h | 16 + + fs/bcachefs/sb-members.c | 426 +++ + fs/bcachefs/sb-members.h | 222 ++ fs/bcachefs/seqmutex.h | 48 + fs/bcachefs/siphash.c | 173 + fs/bcachefs/siphash.h | 87 + fs/bcachefs/six.c | 917 +++++ fs/bcachefs/six.h | 388 ++ - fs/bcachefs/snapshot.c | 1703 +++++++++ + fs/bcachefs/snapshot.c | 1704 +++++++++ fs/bcachefs/snapshot.h | 268 ++ fs/bcachefs/str_hash.h | 370 ++ - fs/bcachefs/subvolume.c | 435 +++ + fs/bcachefs/subvolume.c | 437 +++ fs/bcachefs/subvolume.h | 35 + fs/bcachefs/subvolume_types.h | 31 + - fs/bcachefs/super-io.c | 1258 +++++++ - fs/bcachefs/super-io.h | 124 + - fs/bcachefs/super.c | 2020 +++++++++++ + fs/bcachefs/super-io.c | 1266 +++++++ + fs/bcachefs/super-io.h | 94 + + fs/bcachefs/super.c | 2021 +++++++++++ fs/bcachefs/super.h | 52 + fs/bcachefs/super_types.h | 40 + - fs/bcachefs/sysfs.c | 1024 ++++++ + fs/bcachefs/sysfs.c | 1034 ++++++ fs/bcachefs/sysfs.h | 48 + fs/bcachefs/tests.c | 919 +++++ fs/bcachefs/tests.h | 15 + @@ -221,7 +224,7 @@ Signed-off-by: Piotr Gorski fs/bcachefs/varint.c | 129 + fs/bcachefs/varint.h | 11 + fs/bcachefs/vstructs.h | 63 + - fs/bcachefs/xattr.c | 651 ++++ + fs/bcachefs/xattr.c | 643 ++++ fs/bcachefs/xattr.h | 50 + fs/dcache.c | 12 +- fs/inode.c | 218 +- @@ -280,7 +283,7 @@ Signed-off-by: Piotr Gorski scripts/Makefile.lib | 2 +- scripts/kallsyms.c | 13 + tools/objtool/noreturns.h | 2 + - 275 files changed, 96481 insertions(+), 316 deletions(-) + 278 files changed, 97296 insertions(+), 316 deletions(-) create mode 100644 fs/bcachefs/Kconfig create mode 100644 fs/bcachefs/Makefile create mode 100644 fs/bcachefs/acl.c @@ -444,6 +447,9 @@ Signed-off-by: Piotr Gorski create mode 100644 fs/bcachefs/replicas_types.h create mode 100644 fs/bcachefs/sb-clean.c create mode 100644 fs/bcachefs/sb-clean.h + create mode 100644 fs/bcachefs/sb-errors.c + create mode 100644 fs/bcachefs/sb-errors.h + create mode 100644 fs/bcachefs/sb-errors_types.h create mode 100644 fs/bcachefs/sb-members.c create mode 100644 fs/bcachefs/sb-members.h create mode 100644 fs/bcachefs/seqmutex.h @@ -703,10 +709,10 @@ index 9967fcfa2..4e8122fb6 100644 ivpu_err(vdev, "Failed to allocate pages for BO: %d", ret); return ret; diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h -index f4130586f..9f072916c 100644 +index 6b0ceda5f..b81cf2af0 100644 --- a/drivers/accel/ivpu/ivpu_gem.h +++ b/drivers/accel/ivpu/ivpu_gem.h -@@ -44,7 +44,7 @@ enum ivpu_bo_type { +@@ -42,7 +42,7 @@ enum ivpu_bo_type { struct ivpu_bo_ops { enum ivpu_bo_type type; const char *name; @@ -839,10 +845,10 @@ index 6f3cb7c92..f61ab1bad 100644 #ifdef CONFIG_BCACHE_DEBUG diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c -index b6f4be25b..a09ce965c 100644 +index b66aa5de2..58a3d609c 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c -@@ -2510,7 +2510,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, +@@ -2519,7 +2519,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled); @@ -851,7 +857,7 @@ index b6f4be25b..a09ce965c 100644 cap_str, sizeof(cap_str)); pr_info("%s: %s %s %s%s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), -@@ -2706,7 +2706,7 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, +@@ -2715,7 +2715,7 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, list_add(&rpmb->node, &md->rpmbs); @@ -1144,10 +1150,10 @@ index 000000000..fb5b24f20 + This disables device latency tracking and time stats, only for performance testing diff --git a/fs/bcachefs/Makefile b/fs/bcachefs/Makefile new file mode 100644 -index 000000000..e8e49f96e +index 000000000..3eccbcc64 --- /dev/null +++ b/fs/bcachefs/Makefile -@@ -0,0 +1,85 @@ +@@ -0,0 +1,86 @@ + +obj-$(CONFIG_BCACHEFS_FS) += bcachefs.o + @@ -1219,6 +1225,7 @@ index 000000000..e8e49f96e + reflink.o \ + replicas.o \ + sb-clean.o \ ++ sb-errors.o \ + sb-members.o \ + siphash.o \ + six.o \ @@ -1770,10 +1777,10 @@ index 000000000..27e7eec0f +#endif /* _BCACHEFS_ACL_H */ diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c new file mode 100644 -index 000000000..455ee0b47 +index 000000000..c342ec3b0 --- /dev/null +++ b/fs/bcachefs/alloc_background.c -@@ -0,0 +1,2146 @@ +@@ -0,0 +1,2156 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "bcachefs.h" +#include "alloc_background.h" @@ -1968,114 +1975,109 @@ index 000000000..455ee0b47 + return DIV_ROUND_UP(bytes, sizeof(u64)); +} + -+int bch2_alloc_v1_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_alloc_v1_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_alloc a = bkey_s_c_to_alloc(k); ++ int ret = 0; + + /* allow for unknown fields */ -+ if (bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v)) { -+ prt_printf(err, "incorrect value size (%zu < %u)", -+ bkey_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v)); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v), c, err, ++ alloc_v1_val_size_bad, ++ "incorrect value size (%zu < %u)", ++ bkey_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v)); ++fsck_err: ++ return ret; +} + -+int bch2_alloc_v2_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_alloc_v2_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_alloc_unpacked u; ++ int ret = 0; + -+ if (bch2_alloc_unpack_v2(&u, k)) { -+ prt_printf(err, "unpack error"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(bch2_alloc_unpack_v2(&u, k), c, err, ++ alloc_v2_unpack_error, ++ "unpack error"); ++fsck_err: ++ return ret; +} + -+int bch2_alloc_v3_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_alloc_v3_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_alloc_unpacked u; ++ int ret = 0; + -+ if (bch2_alloc_unpack_v3(&u, k)) { -+ prt_printf(err, "unpack error"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(bch2_alloc_unpack_v3(&u, k), c, err, ++ alloc_v2_unpack_error, ++ "unpack error"); ++fsck_err: ++ return ret; +} + -+int bch2_alloc_v4_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_alloc_v4_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ + struct bkey_s_c_alloc_v4 a = bkey_s_c_to_alloc_v4(k); ++ int ret = 0; + -+ if (alloc_v4_u64s(a.v) > bkey_val_u64s(k.k)) { -+ prt_printf(err, "bad val size (%u > %zu)", -+ alloc_v4_u64s(a.v), bkey_val_u64s(k.k)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(alloc_v4_u64s(a.v) > bkey_val_u64s(k.k), c, err, ++ alloc_v4_val_size_bad, ++ "bad val size (%u > %zu)", ++ alloc_v4_u64s(a.v), bkey_val_u64s(k.k)); + -+ if (!BCH_ALLOC_V4_BACKPOINTERS_START(a.v) && -+ BCH_ALLOC_V4_NR_BACKPOINTERS(a.v)) { -+ prt_printf(err, "invalid backpointers_start"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!BCH_ALLOC_V4_BACKPOINTERS_START(a.v) && ++ BCH_ALLOC_V4_NR_BACKPOINTERS(a.v), c, err, ++ alloc_v4_backpointers_start_bad, ++ "invalid backpointers_start"); + -+ if (alloc_data_type(*a.v, a.v->data_type) != a.v->data_type) { -+ prt_printf(err, "invalid data type (got %u should be %u)", -+ a.v->data_type, alloc_data_type(*a.v, a.v->data_type)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(alloc_data_type(*a.v, a.v->data_type) != a.v->data_type, c, err, ++ alloc_key_data_type_bad, ++ "invalid data type (got %u should be %u)", ++ a.v->data_type, alloc_data_type(*a.v, a.v->data_type)); + + switch (a.v->data_type) { + case BCH_DATA_free: + case BCH_DATA_need_gc_gens: + case BCH_DATA_need_discard: -+ if (a.v->dirty_sectors || -+ a.v->cached_sectors || -+ a.v->stripe) { -+ prt_printf(err, "empty data type free but have data"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(a.v->dirty_sectors || ++ a.v->cached_sectors || ++ a.v->stripe, c, err, ++ alloc_key_empty_but_have_data, ++ "empty data type free but have data"); + break; + case BCH_DATA_sb: + case BCH_DATA_journal: + case BCH_DATA_btree: + case BCH_DATA_user: + case BCH_DATA_parity: -+ if (!a.v->dirty_sectors) { -+ prt_printf(err, "data_type %s but dirty_sectors==0", -+ bch2_data_types[a.v->data_type]); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!a.v->dirty_sectors, c, err, ++ alloc_key_dirty_sectors_0, ++ "data_type %s but dirty_sectors==0", ++ bch2_data_types[a.v->data_type]); + break; + case BCH_DATA_cached: -+ if (!a.v->cached_sectors || -+ a.v->dirty_sectors || -+ a.v->stripe) { -+ prt_printf(err, "data type inconsistency"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (!a.v->io_time[READ] && -+ c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs) { -+ prt_printf(err, "cached bucket with read_time == 0"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!a.v->cached_sectors || ++ a.v->dirty_sectors || ++ a.v->stripe, c, err, ++ alloc_key_cached_inconsistency, ++ "data type inconsistency"); ++ ++ bkey_fsck_err_on(!a.v->io_time[READ] && ++ c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs, ++ c, err, ++ alloc_key_cached_but_read_time_zero, ++ "cached bucket with read_time == 0"); + break; + case BCH_DATA_stripe: + break; + } -+ -+ return 0; ++fsck_err: ++ return ret; +} + +static inline u64 swab40(u64 x) @@ -2297,17 +2299,18 @@ index 000000000..455ee0b47 + : 0; +} + -+int bch2_bucket_gens_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_bucket_gens_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (bkey_val_bytes(k.k) != sizeof(struct bch_bucket_gens)) { -+ prt_printf(err, "bad val size (%zu != %zu)", -+ bkey_val_bytes(k.k), sizeof(struct bch_bucket_gens)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(bkey_val_bytes(k.k) != sizeof(struct bch_bucket_gens), c, err, ++ bucket_gens_val_size_bad, ++ "bad val size (%zu != %zu)", ++ bkey_val_bytes(k.k), sizeof(struct bch_bucket_gens)); ++fsck_err: ++ return ret; +} + +void bch2_bucket_gens_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) @@ -2762,6 +2765,7 @@ index 000000000..455ee0b47 + int ret; + + if (fsck_err_on(!bch2_dev_bucket_exists(c, alloc_k.k->p), c, ++ alloc_key_to_missing_dev_bucket, + "alloc key for invalid device:bucket %llu:%llu", + alloc_k.k->p.inode, alloc_k.k->p.offset)) + return bch2_btree_delete_at(trans, alloc_iter, 0); @@ -2781,7 +2785,8 @@ index 000000000..455ee0b47 + + if (k.k->type != discard_key_type && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "incorrect key in need_discard btree (got %s should be %s)\n" ++ fsck_err(c, need_discard_key_wrong, ++ "incorrect key in need_discard btree (got %s should be %s)\n" + " %s", + bch2_bkey_types[k.k->type], + bch2_bkey_types[discard_key_type], @@ -2811,7 +2816,8 @@ index 000000000..455ee0b47 + + if (k.k->type != freespace_key_type && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "incorrect key in freespace btree (got %s should be %s)\n" ++ fsck_err(c, freespace_key_wrong, ++ "incorrect key in freespace btree (got %s should be %s)\n" + " %s", + bch2_bkey_types[k.k->type], + bch2_bkey_types[freespace_key_type], @@ -2842,7 +2848,8 @@ index 000000000..455ee0b47 + + if (a->gen != alloc_gen(k, gens_offset) && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "incorrect gen in bucket_gens btree (got %u should be %u)\n" ++ fsck_err(c, bucket_gens_key_wrong, ++ "incorrect gen in bucket_gens btree (got %u should be %u)\n" + " %s", + alloc_gen(k, gens_offset), a->gen, + (printbuf_reset(&buf), @@ -2900,7 +2907,8 @@ index 000000000..455ee0b47 + + if (k.k->type != KEY_TYPE_set && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "hole in alloc btree missing in freespace btree\n" ++ fsck_err(c, freespace_hole_missing, ++ "hole in alloc btree missing in freespace btree\n" + " device %llu buckets %llu-%llu", + freespace_iter->pos.inode, + freespace_iter->pos.offset, @@ -2963,6 +2971,7 @@ index 000000000..455ee0b47 + + for (i = gens_offset; i < gens_end_offset; i++) { + if (fsck_err_on(g.v.gens[i], c, ++ bucket_gens_hole_wrong, + "hole in alloc btree at %llu:%llu with nonzero gen in bucket_gens btree (%u)", + bucket_gens_pos_to_alloc(k.k->p, i).inode, + bucket_gens_pos_to_alloc(k.k->p, i).offset, @@ -3020,6 +3029,7 @@ index 000000000..455ee0b47 + return ret; + + if (fsck_err_on(!bch2_dev_bucket_exists(c, pos), c, ++ need_discard_freespace_key_to_invalid_dev_bucket, + "entry in %s btree for nonexistant dev:bucket %llu:%llu", + bch2_btree_id_str(iter->btree_id), pos.inode, pos.offset)) + goto delete; @@ -3029,6 +3039,7 @@ index 000000000..455ee0b47 + if (fsck_err_on(a->data_type != state || + (state == BCH_DATA_free && + genbits != alloc_freespace_genbits(*a)), c, ++ need_discard_freespace_key_bad, + "%s\n incorrectly set at %s:%llu:%llu:0 (free %u, genbits %llu should be %llu)", + (bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf), + bch2_btree_id_str(iter->btree_id), @@ -3096,6 +3107,7 @@ index 000000000..455ee0b47 + dev_exists = bch2_dev_exists2(c, k.k->p.inode); + if (!dev_exists) { + if (fsck_err_on(!dev_exists, c, ++ bucket_gens_to_invalid_dev, + "bucket_gens key for invalid device:\n %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + ret = bch2_btree_delete_at(trans, iter, 0); @@ -3106,6 +3118,7 @@ index 000000000..455ee0b47 + ca = bch_dev_bkey_exists(c, k.k->p.inode); + if (fsck_err_on(end <= ca->mi.first_bucket || + start >= ca->mi.nbuckets, c, ++ bucket_gens_to_invalid_buckets, + "bucket_gens key for invalid buckets:\n %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + ret = bch2_btree_delete_at(trans, iter, 0); @@ -3114,6 +3127,7 @@ index 000000000..455ee0b47 + + for (b = start; b < ca->mi.first_bucket; b++) + if (fsck_err_on(g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK], c, ++ bucket_gens_nonzero_for_invalid_buckets, + "bucket_gens key has nonzero gen for invalid bucket")) { + g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK] = 0; + need_update = true; @@ -3121,6 +3135,7 @@ index 000000000..455ee0b47 + + for (b = ca->mi.nbuckets; b < end; b++) + if (fsck_err_on(g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK], c, ++ bucket_gens_nonzero_for_invalid_buckets, + "bucket_gens key has nonzero gen for invalid bucket")) { + g.v.gens[b & KEY_TYPE_BUCKET_GENS_MASK] = 0; + need_update = true; @@ -3271,11 +3286,13 @@ index 000000000..455ee0b47 + return ret; + + if (fsck_err_on(!a->io_time[READ], c, ++ alloc_key_cached_but_read_time_zero, + "cached bucket with read_time 0\n" + " %s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf)) || + fsck_err_on(lru_k.k->type != KEY_TYPE_set, c, ++ alloc_key_to_missing_lru_entry, + "missing lru entry\n" + " %s", + (printbuf_reset(&buf), @@ -3922,7 +3939,7 @@ index 000000000..455ee0b47 +} diff --git a/fs/bcachefs/alloc_background.h b/fs/bcachefs/alloc_background.h new file mode 100644 -index 000000000..97042067d +index 000000000..e1ce38ef0 --- /dev/null +++ b/fs/bcachefs/alloc_background.h @@ -0,0 +1,258 @@ @@ -4077,13 +4094,13 @@ index 000000000..97042067d + +int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int); + -+int bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_alloc_v1_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_alloc_v2_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_alloc_v3_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_alloc_v4_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_alloc_v4_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_alloc_v4_swab(struct bkey_s); +void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); @@ -4121,7 +4138,7 @@ index 000000000..97042067d + .min_val_size = 48, \ +}) + -+int bch2_bucket_gens_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_bucket_gens_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_bucket_gens_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -6130,10 +6147,10 @@ index 000000000..b91b7a461 +#endif /* _BCACHEFS_ALLOC_TYPES_H */ diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c new file mode 100644 -index 000000000..e74295c21 +index 000000000..3b79bde1c --- /dev/null +++ b/fs/bcachefs/backpointers.c -@@ -0,0 +1,868 @@ +@@ -0,0 +1,872 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "bcachefs.h" +#include "bbpos.h" @@ -6173,19 +6190,20 @@ index 000000000..e74295c21 + return false; +} + -+int bch2_backpointer_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_backpointer_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k); + struct bpos bucket = bp_pos_to_bucket(c, bp.k->p); ++ int ret = 0; + -+ if (!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset))) { -+ prt_str(err, "backpointer at wrong pos"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset)), ++ c, err, ++ backpointer_pos_wrong, ++ "backpointer at wrong pos"); ++fsck_err: ++ return ret; +} + +void bch2_backpointer_to_text(struct printbuf *out, const struct bch_backpointer *bp) @@ -6492,6 +6510,7 @@ index 000000000..e74295c21 + int ret = 0; + + if (fsck_err_on(!bch2_dev_exists2(c, k.k->p.inode), c, ++ backpointer_to_missing_device, + "backpointer for missing device:\n%s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + ret = bch2_btree_delete_at(trans, bp_iter, 0); @@ -6505,6 +6524,7 @@ index 000000000..e74295c21 + goto out; + + if (fsck_err_on(alloc_k.k->type != KEY_TYPE_alloc_v4, c, ++ backpointer_to_missing_alloc, + "backpointer for nonexistent alloc key: %llu:%llu:0\n%s", + alloc_iter.pos.inode, alloc_iter.pos.offset, + (bch2_bkey_val_to_text(&buf, c, alloc_k), buf.buf))) { @@ -6596,7 +6616,7 @@ index 000000000..e74295c21 + + if (c->sb.version_upgrade_complete < bcachefs_metadata_version_backpointers || + c->opts.reconstruct_alloc || -+ fsck_err(c, "%s", buf.buf)) ++ fsck_err(c, ptr_to_missing_backpointer, "%s", buf.buf)) + ret = bch2_bucket_backpointer_mod(trans, bucket, bp, orig_k, true); + + goto out; @@ -6929,6 +6949,7 @@ index 000000000..e74295c21 + } + + if (fsck_err_on(!k.k, c, ++ backpointer_to_missing_ptr, + "backpointer for missing extent\n %s", + (bch2_bkey_val_to_text(&buf, c, bp.s_c), buf.buf))) { + ret = bch2_btree_delete_at_buffered(trans, BTREE_ID_backpointers, bp.k->p); @@ -7004,7 +7025,7 @@ index 000000000..e74295c21 +} diff --git a/fs/bcachefs/backpointers.h b/fs/bcachefs/backpointers.h new file mode 100644 -index 000000000..547e06176 +index 000000000..4ab9f3562 --- /dev/null +++ b/fs/bcachefs/backpointers.h @@ -0,0 +1,131 @@ @@ -7017,7 +7038,7 @@ index 000000000..547e06176 +#include "buckets.h" +#include "super.h" + -+int bch2_backpointer_invalid(const struct bch_fs *, struct bkey_s_c k, ++int bch2_backpointer_invalid(struct bch_fs *, struct bkey_s_c k, + enum bkey_invalid_flags, struct printbuf *); +void bch2_backpointer_to_text(struct printbuf *, const struct bch_backpointer *); +void bch2_backpointer_k_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); @@ -7208,10 +7229,10 @@ index 000000000..5198e94cf +#endif /* _BCACHEFS_BBPOS_TYPES_H */ diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h new file mode 100644 -index 000000000..68f0ff03c +index 000000000..9cb868495 --- /dev/null +++ b/fs/bcachefs/bcachefs.h -@@ -0,0 +1,1155 @@ +@@ -0,0 +1,1161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_H +#define _BCACHEFS_H @@ -7423,6 +7444,7 @@ index 000000000..68f0ff03c +#include "nocow_locking_types.h" +#include "opts.h" +#include "recovery_types.h" ++#include "sb-errors_types.h" +#include "seqmutex.h" +#include "util.h" + @@ -7716,6 +7738,8 @@ index 000000000..68f0ff03c + * Committed by bch2_write_super() -> bch_fs_mi_update() + */ + struct bch_member_cpu mi; ++ atomic64_t errors[BCH_MEMBER_ERROR_NR]; ++ + __uuid_t uuid; + char name[BDEVNAME_SIZE]; + @@ -8204,11 +8228,6 @@ index 000000000..68f0ff03c + struct bio_set dio_read_bioset; + struct bio_set nocow_flush_bioset; + -+ /* ERRORS */ -+ struct list_head fsck_errors; -+ struct mutex fsck_error_lock; -+ bool fsck_alloc_err; -+ + /* QUOTAS */ + struct bch_memquota_type quotas[QTYP_NR]; + @@ -8257,6 +8276,14 @@ index 000000000..68f0ff03c + struct bch2_time_stats times[BCH_TIME_STAT_NR]; + + struct btree_transaction_stats btree_transaction_stats[BCH_TRANSACTIONS_NR]; ++ ++ /* ERRORS */ ++ struct list_head fsck_error_msgs; ++ struct mutex fsck_error_msgs_lock; ++ bool fsck_alloc_msgs_err; ++ ++ bch_sb_errors_cpu fsck_error_counts; ++ struct mutex fsck_error_counts_lock; +}; + +extern struct wait_queue_head bch2_read_only_wait; @@ -8369,10 +8396,10 @@ index 000000000..68f0ff03c +#endif /* _BCACHEFS_H */ diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h new file mode 100644 -index 000000000..e04999c57 +index 000000000..29b000c6b --- /dev/null +++ b/fs/bcachefs/bcachefs_format.h -@@ -0,0 +1,2403 @@ +@@ -0,0 +1,2430 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_FORMAT_H +#define _BCACHEFS_FORMAT_H @@ -9593,7 +9620,8 @@ index 000000000..e04999c57 + x(journal_seq_blacklist, 8) \ + x(journal_v2, 9) \ + x(counters, 10) \ -+ x(members_v2, 11) ++ x(members_v2, 11) \ ++ x(errors, 12) + +enum bch_sb_field_type { +#define x(f, nr) BCH_SB_FIELD_##f = nr, @@ -9643,6 +9671,18 @@ index 000000000..e04999c57 + BCH_IOPS_NR +}; + ++#define BCH_MEMBER_ERROR_TYPES() \ ++ x(read, 0) \ ++ x(write, 1) \ ++ x(checksum, 2) ++ ++enum bch_member_error_type { ++#define x(t, n) BCH_MEMBER_ERROR_##t = n, ++ BCH_MEMBER_ERROR_TYPES() ++#undef x ++ BCH_MEMBER_ERROR_NR ++}; ++ +struct bch_member { + __uuid_t uuid; + __le64 nbuckets; /* device size */ @@ -9653,6 +9693,9 @@ index 000000000..e04999c57 + + __le64 flags; + __le32 iops[4]; ++ __le64 errors[BCH_MEMBER_ERROR_NR]; ++ __le64 errors_at_reset[BCH_MEMBER_ERROR_NR]; ++ __le64 errors_reset_time; +}; + +#define BCH_MEMBER_V1_BYTES 56 @@ -9981,6 +10024,17 @@ index 000000000..e04999c57 + __u64 _data[]; +}; + ++struct bch_sb_field_errors { ++ struct bch_sb_field field; ++ struct bch_sb_field_error_entry { ++ __le64 v; ++ __le64 last_error_time; ++ } entries[]; ++}; ++ ++LE64_BITMASK(BCH_SB_ERROR_ENTRY_ID, struct bch_sb_field_error_entry, v, 0, 16); ++LE64_BITMASK(BCH_SB_ERROR_ENTRY_NR, struct bch_sb_field_error_entry, v, 16, 64); ++ +/* Superblock: */ + +/* @@ -13268,10 +13322,10 @@ index 000000000..5f42a6e69 +#endif /* _BCACHEFS_BKEY_CMP_H */ diff --git a/fs/bcachefs/bkey_methods.c b/fs/bcachefs/bkey_methods.c new file mode 100644 -index 000000000..baf491878 +index 000000000..2f518d7e1 --- /dev/null +++ b/fs/bcachefs/bkey_methods.c -@@ -0,0 +1,469 @@ +@@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -13300,7 +13354,7 @@ index 000000000..baf491878 + NULL +}; + -+static int deleted_key_invalid(const struct bch_fs *c, struct bkey_s_c k, ++static int deleted_key_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ + return 0; @@ -13314,23 +13368,24 @@ index 000000000..baf491878 + .key_invalid = deleted_key_invalid, \ +}) + -+static int empty_val_key_invalid(const struct bch_fs *c, struct bkey_s_c k, ++static int empty_val_key_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ -+ if (bkey_val_bytes(k.k)) { -+ prt_printf(err, "incorrect value size (%zu != 0)", -+ bkey_val_bytes(k.k)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(bkey_val_bytes(k.k), c, err, ++ bkey_val_size_nonzero, ++ "incorrect value size (%zu != 0)", ++ bkey_val_bytes(k.k)); ++fsck_err: ++ return ret; +} + +#define bch2_bkey_ops_error ((struct bkey_ops) { \ + .key_invalid = empty_val_key_invalid, \ +}) + -+static int key_type_cookie_invalid(const struct bch_fs *c, struct bkey_s_c k, ++static int key_type_cookie_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ + return 0; @@ -13345,7 +13400,7 @@ index 000000000..baf491878 + .key_invalid = empty_val_key_invalid, \ +}) + -+static int key_type_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k, ++static int key_type_inline_data_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ + return 0; @@ -13366,18 +13421,6 @@ index 000000000..baf491878 + .val_to_text = key_type_inline_data_to_text, \ +}) + -+static int key_type_set_invalid(const struct bch_fs *c, struct bkey_s_c k, -+ enum bkey_invalid_flags flags, struct printbuf *err) -+{ -+ if (bkey_val_bytes(k.k)) { -+ prt_printf(err, "incorrect value size (%zu != %zu)", -+ bkey_val_bytes(k.k), sizeof(struct bch_cookie)); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; -+} -+ +static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r) +{ + bch2_key_resize(l.k, l.k->size + r.k->size); @@ -13385,7 +13428,7 @@ index 000000000..baf491878 +} + +#define bch2_bkey_ops_set ((struct bkey_ops) { \ -+ .key_invalid = key_type_set_invalid, \ ++ .key_invalid = empty_val_key_invalid, \ + .key_merge = key_type_set_merge, \ +}) + @@ -13403,17 +13446,19 @@ index 000000000..baf491878 + struct printbuf *err) +{ + const struct bkey_ops *ops = bch2_bkey_type_ops(k.k->type); ++ int ret = 0; + -+ if (bkey_val_bytes(k.k) < ops->min_val_size) { -+ prt_printf(err, "bad val size (%zu < %u)", -+ bkey_val_bytes(k.k), ops->min_val_size); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_val_bytes(k.k) < ops->min_val_size, c, err, ++ bkey_val_size_too_small, ++ "bad val size (%zu < %u)", ++ bkey_val_bytes(k.k), ops->min_val_size); + + if (!ops->key_invalid) + return 0; + -+ return ops->key_invalid(c, k, flags, err); ++ ret = ops->key_invalid(c, k, flags, err); ++fsck_err: ++ return ret; +} + +static u64 bch2_key_types_allowed[] = { @@ -13436,61 +13481,55 @@ index 000000000..baf491878 + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (k.k->u64s < BKEY_U64s) { -+ prt_printf(err, "u64s too small (%u < %zu)", k.k->u64s, BKEY_U64s); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; ++ ++ bkey_fsck_err_on(k.k->u64s < BKEY_U64s, c, err, ++ bkey_u64s_too_small, ++ "u64s too small (%u < %zu)", k.k->u64s, BKEY_U64s); + + if (type >= BKEY_TYPE_NR) + return 0; + -+ if (flags & BKEY_INVALID_COMMIT && -+ !(bch2_key_types_allowed[type] & BIT_ULL(k.k->type))) { -+ prt_printf(err, "invalid key type for btree %s (%s)", -+ bch2_btree_node_type_str(type), bch2_bkey_types[k.k->type]); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on((flags & BKEY_INVALID_COMMIT) && ++ !(bch2_key_types_allowed[type] & BIT_ULL(k.k->type)), c, err, ++ bkey_invalid_type_for_btree, ++ "invalid key type for btree %s (%s)", ++ bch2_btree_node_type_str(type), bch2_bkey_types[k.k->type]); + + if (btree_node_type_is_extents(type) && !bkey_whiteout(k.k)) { -+ if (k.k->size == 0) { -+ prt_printf(err, "size == 0"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (k.k->size > k.k->p.offset) { -+ prt_printf(err, "size greater than offset (%u > %llu)", -+ k.k->size, k.k->p.offset); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(k.k->size == 0, c, err, ++ bkey_extent_size_zero, ++ "size == 0"); ++ ++ bkey_fsck_err_on(k.k->size > k.k->p.offset, c, err, ++ bkey_extent_size_greater_than_offset, ++ "size greater than offset (%u > %llu)", ++ k.k->size, k.k->p.offset); + } else { -+ if (k.k->size) { -+ prt_printf(err, "size != 0"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(k.k->size, c, err, ++ bkey_size_nonzero, ++ "size != 0"); + } + + if (type != BKEY_TYPE_btree) { + enum btree_id btree = type - 1; + -+ if (!btree_type_has_snapshots(btree) && -+ k.k->p.snapshot) { -+ prt_printf(err, "nonzero snapshot"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!btree_type_has_snapshots(btree) && ++ k.k->p.snapshot, c, err, ++ bkey_snapshot_nonzero, ++ "nonzero snapshot"); + -+ if (btree_type_has_snapshots(btree) && -+ !k.k->p.snapshot) { -+ prt_printf(err, "snapshot == 0"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(btree_type_has_snapshots(btree) && ++ !k.k->p.snapshot, c, err, ++ bkey_snapshot_zero, ++ "snapshot == 0"); + -+ if (bkey_eq(k.k->p, POS_MAX)) { -+ prt_printf(err, "key at POS_MAX"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_eq(k.k->p, POS_MAX), c, err, ++ bkey_at_pos_max, ++ "key at POS_MAX"); + } -+ -+ return 0; ++fsck_err: ++ return ret; +} + +int bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k, @@ -13502,20 +13541,20 @@ index 000000000..baf491878 + bch2_bkey_val_invalid(c, k, flags, err); +} + -+int bch2_bkey_in_btree_node(struct btree *b, struct bkey_s_c k, -+ struct printbuf *err) ++int bch2_bkey_in_btree_node(struct bch_fs *c, struct btree *b, ++ struct bkey_s_c k, struct printbuf *err) +{ -+ if (bpos_lt(k.k->p, b->data->min_key)) { -+ prt_printf(err, "key before start of btree node"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ if (bpos_gt(k.k->p, b->data->max_key)) { -+ prt_printf(err, "key past end of btree node"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bpos_lt(k.k->p, b->data->min_key), c, err, ++ bkey_before_start_of_btree_node, ++ "key before start of btree node"); + -+ return 0; ++ bkey_fsck_err_on(bpos_gt(k.k->p, b->data->max_key), c, err, ++ bkey_after_end_of_btree_node, ++ "key past end of btree node"); ++fsck_err: ++ return ret; +} + +void bch2_bpos_to_text(struct printbuf *out, struct bpos pos) @@ -13743,10 +13782,10 @@ index 000000000..baf491878 +} diff --git a/fs/bcachefs/bkey_methods.h b/fs/bcachefs/bkey_methods.h new file mode 100644 -index 000000000..c829c8e38 +index 000000000..3a370b708 --- /dev/null +++ b/fs/bcachefs/bkey_methods.h -@@ -0,0 +1,178 @@ +@@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_BKEY_METHODS_H +#define _BCACHEFS_BKEY_METHODS_H @@ -13770,7 +13809,7 @@ index 000000000..c829c8e38 + * being read or written; more aggressive checks can be enabled when rw == WRITE. + */ +struct bkey_ops { -+ int (*key_invalid)(const struct bch_fs *c, struct bkey_s_c k, ++ int (*key_invalid)(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err); + void (*val_to_text)(struct printbuf *, struct bch_fs *, + struct bkey_s_c); @@ -13804,7 +13843,8 @@ index 000000000..c829c8e38 + enum bkey_invalid_flags, struct printbuf *); +int bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, enum btree_node_type, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_bkey_in_btree_node(struct btree *, struct bkey_s_c, struct printbuf *); ++int bch2_bkey_in_btree_node(struct bch_fs *, struct btree *, ++ struct bkey_s_c, struct printbuf *); + +void bch2_bpos_to_text(struct printbuf *, struct bpos); +void bch2_bkey_to_text(struct printbuf *, const struct bkey *); @@ -17758,10 +17798,10 @@ index 000000000..bfe1d7482 +#endif /* _BCACHEFS_BTREE_CACHE_H */ diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c new file mode 100644 -index 000000000..475f8e8d9 +index 000000000..7cd517ae5 --- /dev/null +++ b/fs/bcachefs/btree_gc.c -@@ -0,0 +1,2112 @@ +@@ -0,0 +1,2148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 Kent Overstreet @@ -17859,15 +17899,15 @@ index 000000000..475f8e8d9 + bch2_bkey_val_to_text(&buf2, c, bkey_i_to_s_c(cur.k)); + + if (__fsck_err(c, -+ FSCK_CAN_FIX| -+ FSCK_CAN_IGNORE| -+ FSCK_NO_RATELIMIT, -+ "btree node with incorrect min_key at btree %s level %u:\n" -+ " prev %s\n" -+ " cur %s", -+ bch2_btree_id_str(b->c.btree_id), b->c.level, -+ buf1.buf, buf2.buf) && -+ should_restart_for_topology_repair(c)) { ++ FSCK_CAN_FIX| ++ FSCK_CAN_IGNORE| ++ FSCK_NO_RATELIMIT, ++ btree_node_topology_bad_min_key, ++ "btree node with incorrect min_key at btree %s level %u:\n" ++ " prev %s\n" ++ " cur %s", ++ bch2_btree_id_str(b->c.btree_id), b->c.level, ++ buf1.buf, buf2.buf) && should_restart_for_topology_repair(c)) { + bch_info(c, "Halting mark and sweep to start topology repair pass"); + ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_topology); + goto err; @@ -17886,10 +17926,8 @@ index 000000000..475f8e8d9 + bch2_bkey_val_to_text(&buf1, c, bkey_i_to_s_c(cur.k)); + bch2_bpos_to_text(&buf2, node_end); + -+ if (__fsck_err(c, -+ FSCK_CAN_FIX| -+ FSCK_CAN_IGNORE| -+ FSCK_NO_RATELIMIT, ++ if (__fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE|FSCK_NO_RATELIMIT, ++ btree_node_topology_bad_max_key, + "btree node with incorrect max_key at btree %s level %u:\n" + " %s\n" + " expected %s", @@ -18051,6 +18089,7 @@ index 000000000..475f8e8d9 + + if (mustfix_fsck_err_on(bpos_ge(prev->data->min_key, + cur->data->min_key), c, ++ btree_node_topology_overwritten_by_next_node, + "btree node overwritten by next node at btree %s level %u:\n" + " node %s\n" + " next %s", @@ -18062,6 +18101,7 @@ index 000000000..475f8e8d9 + + if (mustfix_fsck_err_on(!bpos_eq(prev->key.k.p, + bpos_predecessor(cur->data->min_key)), c, ++ btree_node_topology_bad_max_key, + "btree node with incorrect max_key at btree %s level %u:\n" + " node %s\n" + " next %s", @@ -18074,6 +18114,7 @@ index 000000000..475f8e8d9 + + if (mustfix_fsck_err_on(bpos_ge(expected_start, + cur->data->max_key), c, ++ btree_node_topology_overwritten_by_prev_node, + "btree node overwritten by prev node at btree %s level %u:\n" + " prev %s\n" + " node %s", @@ -18084,6 +18125,7 @@ index 000000000..475f8e8d9 + } + + if (mustfix_fsck_err_on(!bpos_eq(expected_start, cur->data->min_key), c, ++ btree_node_topology_bad_min_key, + "btree node with incorrect min_key at btree %s level %u:\n" + " prev %s\n" + " node %s", @@ -18108,6 +18150,7 @@ index 000000000..475f8e8d9 + bch2_bpos_to_text(&buf2, b->key.k.p); + + if (mustfix_fsck_err_on(!bpos_eq(child->key.k.p, b->key.k.p), c, ++ btree_node_topology_bad_max_key, + "btree node with incorrect max_key at btree %s level %u:\n" + " %s\n" + " expected %s", @@ -18160,6 +18203,7 @@ index 000000000..475f8e8d9 + bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(cur_k.k)); + + if (mustfix_fsck_err_on(ret == -EIO, c, ++ btree_node_unreadable, + "Topology repair: unreadable btree node at btree %s level %u:\n" + " %s", + bch2_btree_id_str(b->c.btree_id), @@ -18268,6 +18312,7 @@ index 000000000..475f8e8d9 + bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key)); + + if (mustfix_fsck_err_on(!have_child, c, ++ btree_node_topology_interior_node_empty, + "empty interior btree node at btree %s level %u\n" + " %s", + bch2_btree_id_str(b->c.btree_id), @@ -18346,7 +18391,8 @@ index 000000000..475f8e8d9 + + if (!g->gen_valid && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "bucket %u:%zu data type %s ptr gen %u missing in alloc btree\n" ++ fsck_err(c, ptr_to_missing_alloc_key, ++ "bucket %u:%zu data type %s ptr gen %u missing in alloc btree\n" + "while marking %s", + p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), + bch2_data_types[ptr_data_type(k->k, &p.ptr)], @@ -18363,7 +18409,8 @@ index 000000000..475f8e8d9 + + if (gen_cmp(p.ptr.gen, g->gen) > 0 && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "bucket %u:%zu data type %s ptr gen in the future: %u > %u\n" ++ fsck_err(c, ptr_gen_newer_than_bucket_gen, ++ "bucket %u:%zu data type %s ptr gen in the future: %u > %u\n" + "while marking %s", + p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), + bch2_data_types[ptr_data_type(k->k, &p.ptr)], @@ -18384,7 +18431,8 @@ index 000000000..475f8e8d9 + + if (gen_cmp(g->gen, p.ptr.gen) > BUCKET_GC_GEN_MAX && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n" ++ fsck_err(c, ptr_gen_newer_than_bucket_gen, ++ "bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n" + "while marking %s", + p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), g->gen, + bch2_data_types[ptr_data_type(k->k, &p.ptr)], @@ -18395,7 +18443,8 @@ index 000000000..475f8e8d9 + + if (!p.ptr.cached && gen_cmp(p.ptr.gen, g->gen) < 0 && + (c->opts.reconstruct_alloc || -+ fsck_err(c, "bucket %u:%zu data type %s stale dirty ptr: %u < %u\n" ++ fsck_err(c, stale_dirty_ptr, ++ "bucket %u:%zu data type %s stale dirty ptr: %u < %u\n" + "while marking %s", + p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), + bch2_data_types[ptr_data_type(k->k, &p.ptr)], @@ -18409,6 +18458,7 @@ index 000000000..475f8e8d9 + + if (fsck_err_on(bucket_data_type(g->data_type) && + bucket_data_type(g->data_type) != data_type, c, ++ ptr_bucket_data_type_mismatch, + "bucket %u:%zu different types of data in same bucket: %s, %s\n" + "while marking %s", + p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), @@ -18428,6 +18478,7 @@ index 000000000..475f8e8d9 + struct gc_stripe *m = genradix_ptr(&c->gc_stripes, p.ec.idx); + + if (fsck_err_on(!m || !m->alive, c, ++ ptr_to_missing_stripe, + "pointer to nonexistent stripe %llu\n" + "while marking %s", + (u64) p.ec.idx, @@ -18436,6 +18487,7 @@ index 000000000..475f8e8d9 + do_update = true; + + if (fsck_err_on(m && m->alive && !bch2_ptr_matches_stripe_m(m, p), c, ++ ptr_to_incorrect_stripe, + "pointer does not match stripe %llu\n" + "while marking %s", + (u64) p.ec.idx, @@ -18575,6 +18627,7 @@ index 000000000..475f8e8d9 + goto err; + + if (fsck_err_on(k->k->version.lo > atomic64_read(&c->key_version), c, ++ bkey_version_in_future, + "key version number higher than recorded: %llu > %llu", + k->k->version.lo, + atomic64_read(&c->key_version))) @@ -18732,6 +18785,7 @@ index 000000000..475f8e8d9 + FSCK_CAN_FIX| + FSCK_CAN_IGNORE| + FSCK_NO_RATELIMIT, ++ btree_node_read_error, + "Unreadable btree node at btree %s level %u:\n" + " %s", + bch2_btree_id_str(b->c.btree_id), @@ -18789,6 +18843,7 @@ index 000000000..475f8e8d9 + printbuf_reset(&buf); + bch2_bpos_to_text(&buf, b->data->min_key); + if (mustfix_fsck_err_on(!bpos_eq(b->data->min_key, POS_MIN), c, ++ btree_root_bad_min_key, + "btree root with incorrect min_key: %s", buf.buf)) { + bch_err(c, "repair unimplemented"); + ret = -BCH_ERR_fsck_repair_unimplemented; @@ -18798,6 +18853,7 @@ index 000000000..475f8e8d9 + printbuf_reset(&buf); + bch2_bpos_to_text(&buf, b->data->max_key); + if (mustfix_fsck_err_on(!bpos_eq(b->data->max_key, SPOS_MAX), c, ++ btree_root_bad_max_key, + "btree root with incorrect max_key: %s", buf.buf)) { + bch_err(c, "repair unimplemented"); + ret = -BCH_ERR_fsck_repair_unimplemented; @@ -18974,16 +19030,16 @@ index 000000000..475f8e8d9 + + percpu_down_write(&c->mark_lock); + -+#define copy_field(_f, _msg, ...) \ ++#define copy_field(_err, _f, _msg, ...) \ + if (dst->_f != src->_f && \ + (!verify || \ -+ fsck_err(c, _msg ": got %llu, should be %llu" \ ++ fsck_err(c, _err, _msg ": got %llu, should be %llu" \ + , ##__VA_ARGS__, dst->_f, src->_f))) \ + dst->_f = src->_f -+#define copy_dev_field(_f, _msg, ...) \ -+ copy_field(_f, "dev %u has wrong " _msg, dev, ##__VA_ARGS__) -+#define copy_fs_field(_f, _msg, ...) \ -+ copy_field(_f, "fs has wrong " _msg, ##__VA_ARGS__) ++#define copy_dev_field(_err, _f, _msg, ...) \ ++ copy_field(_err, _f, "dev %u has wrong " _msg, dev, ##__VA_ARGS__) ++#define copy_fs_field(_err, _f, _msg, ...) \ ++ copy_field(_err, _f, "fs has wrong " _msg, ##__VA_ARGS__) + + for (i = 0; i < ARRAY_SIZE(c->usage); i++) + bch2_fs_usage_acc_to_base(c, i); @@ -18994,13 +19050,17 @@ index 000000000..475f8e8d9 + bch2_acc_percpu_u64s((u64 __percpu *) ca->usage_gc, + dev_usage_u64s()); + -+ copy_dev_field(buckets_ec, "buckets_ec"); -+ + for (i = 0; i < BCH_DATA_NR; i++) { -+ copy_dev_field(d[i].buckets, "%s buckets", bch2_data_types[i]); -+ copy_dev_field(d[i].sectors, "%s sectors", bch2_data_types[i]); -+ copy_dev_field(d[i].fragmented, "%s fragmented", bch2_data_types[i]); ++ copy_dev_field(dev_usage_buckets_wrong, ++ d[i].buckets, "%s buckets", bch2_data_types[i]); ++ copy_dev_field(dev_usage_sectors_wrong, ++ d[i].sectors, "%s sectors", bch2_data_types[i]); ++ copy_dev_field(dev_usage_fragmented_wrong, ++ d[i].fragmented, "%s fragmented", bch2_data_types[i]); + } ++ ++ copy_dev_field(dev_usage_buckets_ec_wrong, ++ buckets_ec, "buckets_ec"); + } + + { @@ -19009,17 +19069,24 @@ index 000000000..475f8e8d9 + struct bch_fs_usage *src = (void *) + bch2_acc_percpu_u64s((u64 __percpu *) c->usage_gc, nr); + -+ copy_fs_field(hidden, "hidden"); -+ copy_fs_field(btree, "btree"); ++ copy_fs_field(fs_usage_hidden_wrong, ++ hidden, "hidden"); ++ copy_fs_field(fs_usage_btree_wrong, ++ btree, "btree"); + + if (!metadata_only) { -+ copy_fs_field(data, "data"); -+ copy_fs_field(cached, "cached"); -+ copy_fs_field(reserved, "reserved"); -+ copy_fs_field(nr_inodes,"nr_inodes"); ++ copy_fs_field(fs_usage_data_wrong, ++ data, "data"); ++ copy_fs_field(fs_usage_cached_wrong, ++ cached, "cached"); ++ copy_fs_field(fs_usage_reserved_wrong, ++ reserved, "reserved"); ++ copy_fs_field(fs_usage_nr_inodes_wrong, ++ nr_inodes,"nr_inodes"); + + for (i = 0; i < BCH_REPLICAS_MAX; i++) -+ copy_fs_field(persistent_reserved[i], ++ copy_fs_field(fs_usage_persistent_reserved_wrong, ++ persistent_reserved[i], + "persistent_reserved[%i]", i); + } + @@ -19035,7 +19102,8 @@ index 000000000..475f8e8d9 + printbuf_reset(&buf); + bch2_replicas_entry_to_text(&buf, e); + -+ copy_fs_field(replicas[i], "%s", buf.buf); ++ copy_fs_field(fs_usage_replicas_wrong, ++ replicas[i], "%s", buf.buf); + } + } + @@ -19171,6 +19239,7 @@ index 000000000..475f8e8d9 + + if (c->opts.reconstruct_alloc || + fsck_err_on(new.data_type != gc.data_type, c, ++ alloc_key_data_type_wrong, + "bucket %llu:%llu gen %u has wrong data_type" + ": got %s, should be %s", + iter->pos.inode, iter->pos.offset, @@ -19179,9 +19248,9 @@ index 000000000..475f8e8d9 + bch2_data_types[gc.data_type])) + new.data_type = gc.data_type; + -+#define copy_bucket_field(_f) \ ++#define copy_bucket_field(_errtype, _f) \ + if (c->opts.reconstruct_alloc || \ -+ fsck_err_on(new._f != gc._f, c, \ ++ fsck_err_on(new._f != gc._f, c, _errtype, \ + "bucket %llu:%llu gen %u data type %s has wrong " #_f \ + ": got %u, should be %u", \ + iter->pos.inode, iter->pos.offset, \ @@ -19190,11 +19259,16 @@ index 000000000..475f8e8d9 + new._f, gc._f)) \ + new._f = gc._f; \ + -+ copy_bucket_field(gen); -+ copy_bucket_field(dirty_sectors); -+ copy_bucket_field(cached_sectors); -+ copy_bucket_field(stripe_redundancy); -+ copy_bucket_field(stripe); ++ copy_bucket_field(alloc_key_gen_wrong, ++ gen); ++ copy_bucket_field(alloc_key_dirty_sectors_wrong, ++ dirty_sectors); ++ copy_bucket_field(alloc_key_cached_sectors_wrong, ++ cached_sectors); ++ copy_bucket_field(alloc_key_stripe_wrong, ++ stripe); ++ copy_bucket_field(alloc_key_stripe_redundancy_wrong, ++ stripe_redundancy); +#undef copy_bucket_field + + if (!bch2_alloc_v4_cmp(*old, new)) @@ -19351,6 +19425,7 @@ index 000000000..475f8e8d9 + } + + if (fsck_err_on(r->refcount != le64_to_cpu(*refcount), c, ++ reflink_v_refcount_wrong, + "reflink key has wrong refcount:\n" + " %s\n" + " should be %u", @@ -19476,7 +19551,8 @@ index 000000000..475f8e8d9 + if (bad) + bch2_bkey_val_to_text(&buf, c, k); + -+ if (fsck_err_on(bad, c, "%s", buf.buf)) { ++ if (fsck_err_on(bad, c, stripe_sector_count_wrong, ++ "%s", buf.buf)) { + struct bkey_i_stripe *new; + + new = bch2_trans_kmalloc(trans, bkey_bytes(k.k)); @@ -19996,10 +20072,10 @@ index 000000000..607575f83 +#endif /* _BCACHEFS_BTREE_GC_H */ diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c new file mode 100644 -index 000000000..7bf3ee25b +index 000000000..4d2d6f935 --- /dev/null +++ b/fs/bcachefs/btree_io.c -@@ -0,0 +1,2213 @@ +@@ -0,0 +1,2298 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -20532,7 +20608,7 @@ index 000000000..7bf3ee25b + prt_str(out, ": "); +} + -+__printf(8, 9) ++__printf(9, 10) +static int __btree_err(int ret, + struct bch_fs *c, + struct bch_dev *ca, @@ -20540,6 +20616,7 @@ index 000000000..7bf3ee25b + struct bset *i, + int write, + bool have_retry, ++ enum bch_sb_error_id err_type, + const char *fmt, ...) +{ + struct printbuf out = PRINTBUF; @@ -20564,9 +20641,15 @@ index 000000000..7bf3ee25b + if (!have_retry && ret == -BCH_ERR_btree_node_read_err_must_retry) + ret = -BCH_ERR_btree_node_read_err_bad_node; + ++ if (ret != -BCH_ERR_btree_node_read_err_fixable) ++ bch2_sb_error_count(c, err_type); ++ + switch (ret) { + case -BCH_ERR_btree_node_read_err_fixable: -+ mustfix_fsck_err(c, "%s", out.buf); ++ ret = bch2_fsck_err(c, FSCK_CAN_FIX, err_type, "%s", out.buf); ++ if (ret != -BCH_ERR_fsck_fix && ++ ret != -BCH_ERR_fsck_ignore) ++ goto fsck_err; + ret = -BCH_ERR_fsck_fix; + break; + case -BCH_ERR_btree_node_read_err_want_retry: @@ -20591,9 +20674,11 @@ index 000000000..7bf3ee25b + return ret; +} + -+#define btree_err(type, c, ca, b, i, msg, ...) \ ++#define btree_err(type, c, ca, b, i, _err_type, msg, ...) \ +({ \ -+ int _ret = __btree_err(type, c, ca, b, i, write, have_retry, msg, ##__VA_ARGS__);\ ++ int _ret = __btree_err(type, c, ca, b, i, write, have_retry, \ ++ BCH_FSCK_ERR_##_err_type, \ ++ msg, ##__VA_ARGS__); \ + \ + if (_ret != -BCH_ERR_fsck_fix) { \ + ret = _ret; \ @@ -20668,13 +20753,17 @@ index 000000000..7bf3ee25b + int ret = 0; + + btree_err_on(!bch2_version_compatible(version), -+ -BCH_ERR_btree_node_read_err_incompatible, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_incompatible, ++ c, ca, b, i, ++ btree_node_unsupported_version, + "unsupported bset version %u.%u", + BCH_VERSION_MAJOR(version), + BCH_VERSION_MINOR(version)); + + if (btree_err_on(version < c->sb.version_min, -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bset_older_than_sb_min, + "bset version %u older than superblock version_min %u", + version, c->sb.version_min)) { + mutex_lock(&c->sb_lock); @@ -20685,7 +20774,9 @@ index 000000000..7bf3ee25b + + if (btree_err_on(BCH_VERSION_MAJOR(version) > + BCH_VERSION_MAJOR(c->sb.version), -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bset_newer_than_sb, + "bset version %u newer than superblock version %u", + version, c->sb.version)) { + mutex_lock(&c->sb_lock); @@ -20695,11 +20786,15 @@ index 000000000..7bf3ee25b + } + + btree_err_on(BSET_SEPARATE_WHITEOUTS(i), -+ -BCH_ERR_btree_node_read_err_incompatible, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_incompatible, ++ c, ca, b, i, ++ btree_node_unsupported_version, + "BSET_SEPARATE_WHITEOUTS no longer supported"); + + if (btree_err_on(offset + sectors > btree_sectors(c), -+ -BCH_ERR_btree_node_read_err_fixable, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, ca, b, i, ++ bset_past_end_of_btree_node, + "bset past end of btree node")) { + i->u64s = 0; + ret = 0; @@ -20707,12 +20802,15 @@ index 000000000..7bf3ee25b + } + + btree_err_on(offset && !i->u64s, -+ -BCH_ERR_btree_node_read_err_fixable, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, ca, b, i, ++ bset_empty, + "empty bset"); + -+ btree_err_on(BSET_OFFSET(i) && -+ BSET_OFFSET(i) != offset, -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i, ++ btree_err_on(BSET_OFFSET(i) && BSET_OFFSET(i) != offset, ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, i, ++ bset_wrong_sector_offset, + "bset at wrong sector offset"); + + if (!offset) { @@ -20726,16 +20824,22 @@ index 000000000..7bf3ee25b + + /* XXX endianness */ + btree_err_on(bp->seq != bn->keys.seq, -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ bset_bad_seq, + "incorrect sequence number (wrong btree node)"); + } + + btree_err_on(BTREE_NODE_ID(bn) != b->c.btree_id, -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, i, ++ btree_node_bad_btree, + "incorrect btree id"); + + btree_err_on(BTREE_NODE_LEVEL(bn) != b->c.level, -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, i, ++ btree_node_bad_level, + "incorrect level"); + + if (!write) @@ -20752,7 +20856,9 @@ index 000000000..7bf3ee25b + } + + btree_err_on(!bpos_eq(b->data->min_key, bp->min_key), -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ btree_node_bad_min_key, + "incorrect min_key: got %s should be %s", + (printbuf_reset(&buf1), + bch2_bpos_to_text(&buf1, bn->min_key), buf1.buf), @@ -20761,7 +20867,9 @@ index 000000000..7bf3ee25b + } + + btree_err_on(!bpos_eq(bn->max_key, b->key.k.p), -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, i, ++ btree_node_bad_max_key, + "incorrect max key %s", + (printbuf_reset(&buf1), + bch2_bpos_to_text(&buf1, bn->max_key), buf1.buf)); @@ -20771,7 +20879,9 @@ index 000000000..7bf3ee25b + BSET_BIG_ENDIAN(i), write, bn); + + btree_err_on(bch2_bkey_format_invalid(c, &bn->format, write, &buf1), -+ -BCH_ERR_btree_node_read_err_bad_node, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_bad_node, ++ c, ca, b, i, ++ btree_node_bad_format, + "invalid bkey format: %s\n %s", buf1.buf, + (printbuf_reset(&buf2), + bch2_bkey_format_to_text(&buf2, &bn->format), buf2.buf)); @@ -20794,7 +20904,7 @@ index 000000000..7bf3ee25b + struct printbuf *err) +{ + return __bch2_bkey_invalid(c, k, btree_node_type(b), READ, err) ?: -+ (!updated_range ? bch2_bkey_in_btree_node(b, k, err) : 0) ?: ++ (!updated_range ? bch2_bkey_in_btree_node(c, b, k, err) : 0) ?: + (rw == WRITE ? bch2_bkey_val_invalid(c, k, READ, err) : 0); +} + @@ -20815,14 +20925,18 @@ index 000000000..7bf3ee25b + struct bkey tmp; + + if (btree_err_on(bkey_p_next(k) > vstruct_last(i), -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bkey_past_bset_end, + "key extends past end of bset")) { + i->u64s = cpu_to_le16((u64 *) k - i->_data); + break; + } + + if (btree_err_on(k->format > KEY_FORMAT_CURRENT, -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bkey_bad_format, + "invalid bkey format %u", k->format)) { + i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s); + memmove_u64s_down(k, bkey_p_next(k), @@ -20841,12 +20955,14 @@ index 000000000..7bf3ee25b + printbuf_reset(&buf); + if (bset_key_invalid(c, b, u.s_c, updated_range, write, &buf)) { + printbuf_reset(&buf); -+ prt_printf(&buf, "invalid bkey: "); + bset_key_invalid(c, b, u.s_c, updated_range, write, &buf); + prt_printf(&buf, "\n "); + bch2_bkey_val_to_text(&buf, c, u.s_c); + -+ btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf); ++ btree_err(-BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bad_bkey, ++ "invalid bkey: %s", buf.buf); + + i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s); + memmove_u64s_down(k, bkey_p_next(k), @@ -20870,7 +20986,10 @@ index 000000000..7bf3ee25b + + bch2_dump_bset(c, b, i, 0); + -+ if (btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf)) { ++ if (btree_err(-BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bkey_out_of_order, ++ "%s", buf.buf)) { + i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s); + memmove_u64s_down(k, bkey_p_next(k), + (u64 *) vstruct_end(i) - (u64 *) k); @@ -20911,47 +21030,62 @@ index 000000000..7bf3ee25b + sort_iter_init(iter, b, (btree_blocks(c) + 1) * 2); + + if (bch2_meta_read_fault("btree")) -+ btree_err(-BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, ++ btree_err(-BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ btree_node_fault_injected, + "dynamic fault"); + + btree_err_on(le64_to_cpu(b->data->magic) != bset_magic(c), -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ btree_node_bad_magic, + "bad magic: want %llx, got %llx", + bset_magic(c), le64_to_cpu(b->data->magic)); + -+ btree_err_on(!b->data->keys.seq, -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, -+ "bad btree header: seq 0"); -+ + if (b->key.k.type == KEY_TYPE_btree_ptr_v2) { + struct bch_btree_ptr_v2 *bp = + &bkey_i_to_btree_ptr_v2(&b->key)->v; + + btree_err_on(b->data->keys.seq != bp->seq, -+ -BCH_ERR_btree_node_read_err_must_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ btree_node_bad_seq, + "got wrong btree node (seq %llx want %llx)", + b->data->keys.seq, bp->seq); ++ } else { ++ btree_err_on(!b->data->keys.seq, ++ -BCH_ERR_btree_node_read_err_must_retry, ++ c, ca, b, NULL, ++ btree_node_bad_seq, ++ "bad btree header: seq 0"); + } + + while (b->written < (ptr_written ?: btree_sectors(c))) { + unsigned sectors; + struct nonce nonce; -+ struct bch_csum csum; + bool first = !b->written; ++ bool csum_bad; + + if (!b->written) { + i = &b->data->keys; + + btree_err_on(!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)), -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i, -+ "unknown checksum type %llu", -+ BSET_CSUM_TYPE(i)); ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, i, ++ bset_unknown_csum, ++ "unknown checksum type %llu", BSET_CSUM_TYPE(i)); + + nonce = btree_nonce(i, b->written << 9); -+ csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, b->data); + -+ btree_err_on(bch2_crc_cmp(csum, b->data->csum), -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i, ++ csum_bad = bch2_crc_cmp(b->data->csum, ++ csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, b->data)); ++ if (csum_bad) ++ bch2_io_error(ca, BCH_MEMBER_ERROR_checksum); ++ ++ btree_err_on(csum_bad, ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, i, ++ bset_bad_csum, + "invalid checksum"); + + ret = bset_encrypt(c, i, b->written << 9); @@ -20961,7 +21095,9 @@ index 000000000..7bf3ee25b + + btree_err_on(btree_node_type_is_extents(btree_node_type(b)) && + !BTREE_NODE_NEW_EXTENT_OVERWRITE(b->data), -+ -BCH_ERR_btree_node_read_err_incompatible, c, NULL, b, NULL, ++ -BCH_ERR_btree_node_read_err_incompatible, ++ c, NULL, b, NULL, ++ btree_node_unsupported_version, + "btree node does not have NEW_EXTENT_OVERWRITE set"); + + sectors = vstruct_sectors(b->data, c->block_bits); @@ -20973,15 +21109,21 @@ index 000000000..7bf3ee25b + break; + + btree_err_on(!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)), -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i, -+ "unknown checksum type %llu", -+ BSET_CSUM_TYPE(i)); ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, i, ++ bset_unknown_csum, ++ "unknown checksum type %llu", BSET_CSUM_TYPE(i)); + + nonce = btree_nonce(i, b->written << 9); -+ csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne); -+ -+ btree_err_on(bch2_crc_cmp(csum, bne->csum), -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, i, ++ csum_bad = bch2_crc_cmp(bne->csum, ++ csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne)); ++ if (csum_bad) ++ bch2_io_error(ca, BCH_MEMBER_ERROR_checksum); ++ ++ btree_err_on(csum_bad, ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, i, ++ bset_bad_csum, + "invalid checksum"); + + ret = bset_encrypt(c, i, b->written << 9); @@ -21014,12 +21156,16 @@ index 000000000..7bf3ee25b + true); + + btree_err_on(blacklisted && first, -+ -BCH_ERR_btree_node_read_err_fixable, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, ca, b, i, ++ bset_blacklisted_journal_seq, + "first btree node bset has blacklisted journal seq (%llu)", + le64_to_cpu(i->journal_seq)); + + btree_err_on(blacklisted && ptr_written, -+ -BCH_ERR_btree_node_read_err_fixable, c, ca, b, i, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, ca, b, i, ++ first_bset_blacklisted_journal_seq, + "found blacklisted bset (journal seq %llu) in btree node at offset %u-%u/%u", + le64_to_cpu(i->journal_seq), + b->written, b->written + sectors, ptr_written); @@ -21036,7 +21182,9 @@ index 000000000..7bf3ee25b + + if (ptr_written) { + btree_err_on(b->written < ptr_written, -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, NULL, ++ btree_node_data_missing, + "btree node data missing: expected %u sectors, found %u", + ptr_written, b->written); + } else { @@ -21047,7 +21195,9 @@ index 000000000..7bf3ee25b + !bch2_journal_seq_is_blacklisted(c, + le64_to_cpu(bne->keys.journal_seq), + true), -+ -BCH_ERR_btree_node_read_err_want_retry, c, ca, b, NULL, ++ -BCH_ERR_btree_node_read_err_want_retry, ++ c, ca, b, NULL, ++ btree_node_bset_after_end, + "found bset signature after last bset"); + } + @@ -21089,7 +21239,10 @@ index 000000000..7bf3ee25b + prt_printf(&buf, "\n "); + bch2_bkey_val_to_text(&buf, c, u.s_c); + -+ btree_err(-BCH_ERR_btree_node_read_err_fixable, c, NULL, b, i, "%s", buf.buf); ++ btree_err(-BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, i, ++ btree_node_bad_bkey, ++ "%s", buf.buf); + + btree_keys_account_key_drop(&b->nr, 0, k); + @@ -21170,7 +21323,8 @@ index 000000000..7bf3ee25b +start: + printbuf_reset(&buf); + bch2_btree_pos_to_text(&buf, c, b); -+ bch2_dev_io_err_on(bio->bi_status, ca, "btree read error %s for %s", ++ bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_read, ++ "btree read error %s for %s", + bch2_blk_status_to_str(bio->bi_status), buf.buf); + if (rb->have_ioref) + percpu_ref_put(&ca->io_ref); @@ -21314,14 +21468,20 @@ index 000000000..7bf3ee25b + } + + written2 = btree_node_sectors_written(c, ra->buf[i]); -+ if (btree_err_on(written2 != written, -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL, ++ if (btree_err_on(written2 != written, -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, NULL, ++ btree_node_replicas_sectors_written_mismatch, + "btree node sectors written mismatch: %u != %u", + written, written2) || + btree_err_on(btree_node_has_extra_bsets(c, written2, ra->buf[i]), -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, NULL, ++ btree_node_bset_after_end, + "found bset signature after last bset") || + btree_err_on(memcmp(ra->buf[best], ra->buf[i], written << 9), -+ -BCH_ERR_btree_node_read_err_fixable, c, NULL, b, NULL, ++ -BCH_ERR_btree_node_read_err_fixable, ++ c, NULL, b, NULL, ++ btree_node_replicas_data_mismatch, + "btree node replicas content mismatch")) + dump_bset_maps = true; + @@ -21751,7 +21911,8 @@ index 000000000..7bf3ee25b + if (wbio->have_ioref) + bch2_latency_acct(ca, wbio->submit_time, WRITE); + -+ if (bch2_dev_io_err_on(bio->bi_status, ca, "btree write error: %s", ++ if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write, ++ "btree write error: %s", + bch2_blk_status_to_str(bio->bi_status)) || + bch2_meta_write_fault("btree")) { + spin_lock_irqsave(&c->btree_write_error_lock, flags); @@ -32788,7 +32949,7 @@ index 000000000..9816d2286 +#endif /* _BCACHEFS_BTREE_UPDATE_H */ diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c new file mode 100644 -index 000000000..7dbf6b6c7 +index 000000000..818a83f35 --- /dev/null +++ b/fs/bcachefs/btree_update_interior.c @@ -0,0 +1,2480 @@ @@ -34068,14 +34229,14 @@ index 000000000..7dbf6b6c7 + + if (bch2_bkey_invalid(c, bkey_i_to_s_c(insert), + btree_node_type(b), WRITE, &buf) ?: -+ bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf)) { ++ bch2_bkey_in_btree_node(c, b, bkey_i_to_s_c(insert), &buf)) { + printbuf_reset(&buf); + prt_printf(&buf, "inserting invalid bkey\n "); + bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert)); + prt_printf(&buf, "\n "); + bch2_bkey_invalid(c, bkey_i_to_s_c(insert), + btree_node_type(b), WRITE, &buf); -+ bch2_bkey_in_btree_node(b, bkey_i_to_s_c(insert), &buf); ++ bch2_bkey_in_btree_node(c, b, bkey_i_to_s_c(insert), &buf); + + bch2_fs_inconsistent(c, "%s", buf.buf); + dump_stack(); @@ -36068,10 +36229,10 @@ index 000000000..99993ba77 +#endif /* _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H */ diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c new file mode 100644 -index 000000000..a8af803e7 +index 000000000..2acd727d3 --- /dev/null +++ b/fs/bcachefs/buckets.c -@@ -0,0 +1,2161 @@ +@@ -0,0 +1,2168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Code for manipulating bucket marks for garbage collection. @@ -36444,8 +36605,8 @@ index 000000000..a8af803e7 + + idx = bch2_replicas_entry_idx(c, r); + if (idx < 0 && -+ fsck_err(c, "no replicas entry\n" -+ " while marking %s", ++ fsck_err(c, ptr_to_missing_replicas_entry, ++ "no replicas entry\n while marking %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + percpu_up_read(&c->mark_lock); + ret = bch2_mark_replicas(c, r); @@ -36769,6 +36930,7 @@ index 000000000..a8af803e7 + + if (gen_after(ptr->gen, b_gen)) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_ptr_gen_newer_than_bucket_gen, + "bucket %u:%zu gen %u data type %s: ptr gen %u newer than bucket gen\n" + "while marking %s", + ptr->dev, bucket_nr, b_gen, @@ -36781,6 +36943,7 @@ index 000000000..a8af803e7 + + if (gen_cmp(b_gen, ptr->gen) > BUCKET_GC_GEN_MAX) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_ptr_too_stale, + "bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n" + "while marking %s", + ptr->dev, bucket_nr, b_gen, @@ -36794,6 +36957,7 @@ index 000000000..a8af803e7 + + if (b_gen != ptr->gen && !ptr->cached) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_stale_dirty_ptr, + "bucket %u:%zu gen %u (mem gen %u) data type %s: stale dirty ptr (gen %u)\n" + "while marking %s", + ptr->dev, bucket_nr, b_gen, @@ -36815,6 +36979,7 @@ index 000000000..a8af803e7 + ptr_data_type && + bucket_data_type != ptr_data_type) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_ptr_bucket_data_type_mismatch, + "bucket %u:%zu gen %u different types of data in same bucket: %s, %s\n" + "while marking %s", + ptr->dev, bucket_nr, b_gen, @@ -36828,6 +36993,7 @@ index 000000000..a8af803e7 + + if ((u64) bucket_sectors + sectors > U32_MAX) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_bucket_sector_count_overflow, + "bucket %u:%zu gen %u data type %s sector count overflow: %u + %lli > U32_MAX\n" + "while marking %s", + ptr->dev, bucket_nr, b_gen, @@ -37269,7 +37435,8 @@ index 000000000..a8af803e7 + *idx = r->offset; + return 0; +not_found: -+ if (fsck_err(c, "pointer to missing indirect extent\n" ++ if (fsck_err(c, reflink_p_to_missing_reflink_v, ++ "pointer to missing indirect extent\n" + " %s\n" + " missing range %llu-%llu", + (bch2_bkey_val_to_text(&buf, c, p.s_c), buf.buf), @@ -37931,6 +38098,7 @@ index 000000000..a8af803e7 + + if (a->v.data_type && type && a->v.data_type != type) { + bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ++ BCH_FSCK_ERR_bucket_metadata_type_mismatch, + "bucket %llu:%llu gen %u different types of data in same bucket: %s, %s\n" + "while marking %s", + iter.pos.inode, iter.pos.offset, a->v.gen, @@ -43818,10 +43986,10 @@ index 000000000..2c37143b5 +#endif /* _BCACHEFS_DEBUG_H */ diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c new file mode 100644 -index 000000000..6c6c8d57d +index 000000000..1a0f2d571 --- /dev/null +++ b/fs/bcachefs/dirent.c -@@ -0,0 +1,587 @@ +@@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -43921,61 +44089,51 @@ index 000000000..6c6c8d57d + .is_visible = dirent_is_visible, +}; + -+int bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_dirent_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); + struct qstr d_name = bch2_dirent_get_name(d); ++ int ret = 0; + -+ if (!d_name.len) { -+ prt_printf(err, "empty name"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!d_name.len, c, err, ++ dirent_empty_name, ++ "empty name"); + -+ if (bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len)) { -+ prt_printf(err, "value too big (%zu > %u)", -+ bkey_val_u64s(k.k), dirent_val_u64s(d_name.len)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len), c, err, ++ dirent_val_too_big, ++ "value too big (%zu > %u)", ++ bkey_val_u64s(k.k), dirent_val_u64s(d_name.len)); + + /* + * Check new keys don't exceed the max length + * (older keys may be larger.) + */ -+ if ((flags & BKEY_INVALID_COMMIT) && d_name.len > BCH_NAME_MAX) { -+ prt_printf(err, "dirent name too big (%u > %u)", -+ d_name.len, BCH_NAME_MAX); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (d_name.len != strnlen(d_name.name, d_name.len)) { -+ prt_printf(err, "dirent has stray data after name's NUL"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (d_name.len == 1 && !memcmp(d_name.name, ".", 1)) { -+ prt_printf(err, "invalid name"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (d_name.len == 2 && !memcmp(d_name.name, "..", 2)) { -+ prt_printf(err, "invalid name"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (memchr(d_name.name, '/', d_name.len)) { -+ prt_printf(err, "invalid name"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (d.v->d_type != DT_SUBVOL && -+ le64_to_cpu(d.v->d_inum) == d.k->p.inode) { -+ prt_printf(err, "dirent points to own directory"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on((flags & BKEY_INVALID_COMMIT) && d_name.len > BCH_NAME_MAX, c, err, ++ dirent_name_too_long, ++ "dirent name too big (%u > %u)", ++ d_name.len, BCH_NAME_MAX); ++ ++ bkey_fsck_err_on(d_name.len != strnlen(d_name.name, d_name.len), c, err, ++ dirent_name_embedded_nul, ++ "dirent has stray data after name's NUL"); ++ ++ bkey_fsck_err_on((d_name.len == 1 && !memcmp(d_name.name, ".", 1)) || ++ (d_name.len == 2 && !memcmp(d_name.name, "..", 2)), c, err, ++ dirent_name_dot_or_dotdot, ++ "invalid name"); ++ ++ bkey_fsck_err_on(memchr(d_name.name, '/', d_name.len), c, err, ++ dirent_name_has_slash, ++ "name with /"); ++ ++ bkey_fsck_err_on(d.v->d_type != DT_SUBVOL && ++ le64_to_cpu(d.v->d_inum) == d.k->p.inode, c, err, ++ dirent_to_itself, ++ "dirent points to own directory"); ++fsck_err: ++ return ret; +} + +void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c, @@ -44411,7 +44569,7 @@ index 000000000..6c6c8d57d +} diff --git a/fs/bcachefs/dirent.h b/fs/bcachefs/dirent.h new file mode 100644 -index 000000000..e9fa1df38 +index 000000000..cd262bf4d --- /dev/null +++ b/fs/bcachefs/dirent.h @@ -0,0 +1,70 @@ @@ -44424,7 +44582,7 @@ index 000000000..e9fa1df38 +enum bkey_invalid_flags; +extern const struct bch_hash_desc bch2_dirent_hash_desc; + -+int bch2_dirent_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_dirent_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_dirent_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -45254,10 +45412,10 @@ index 000000000..a54ef085b +#endif /* _BCACHEFS_DISK_GROUPS_TYPES_H */ diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c new file mode 100644 -index 000000000..8646856e4 +index 000000000..5da0e7a69 --- /dev/null +++ b/fs/bcachefs/ec.c -@@ -0,0 +1,1966 @@ +@@ -0,0 +1,1967 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* erasure coding */ @@ -45365,29 +45523,26 @@ index 000000000..8646856e4 + +/* Stripes btree keys: */ + -+int bch2_stripe_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_stripe_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + const struct bch_stripe *s = bkey_s_c_to_stripe(k).v; ++ int ret = 0; + -+ if (bkey_eq(k.k->p, POS_MIN)) { -+ prt_printf(err, "stripe at POS_MIN"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (k.k->p.inode) { -+ prt_printf(err, "nonzero inode field"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_eq(k.k->p, POS_MIN) || ++ bpos_gt(k.k->p, POS(0, U32_MAX)), c, err, ++ stripe_pos_bad, ++ "stripe at bad pos"); + -+ if (bkey_val_u64s(k.k) < stripe_val_u64s(s)) { -+ prt_printf(err, "incorrect value size (%zu < %u)", -+ bkey_val_u64s(k.k), stripe_val_u64s(s)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_val_u64s(k.k) < stripe_val_u64s(s), c, err, ++ stripe_val_size_bad, ++ "incorrect value size (%zu < %u)", ++ bkey_val_u64s(k.k), stripe_val_u64s(s)); + -+ return bch2_bkey_ptrs_invalid(c, k, flags, err); ++ ret = bch2_bkey_ptrs_invalid(c, k, flags, err); ++fsck_err: ++ return ret; +} + +void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c, @@ -45633,7 +45788,11 @@ index 000000000..8646856e4 + struct bch_dev *ca = ec_bio->ca; + struct closure *cl = bio->bi_private; + -+ if (bch2_dev_io_err_on(bio->bi_status, ca, "erasure coding %s error: %s", ++ if (bch2_dev_io_err_on(bio->bi_status, ca, ++ bio_data_dir(bio) ++ ? BCH_MEMBER_ERROR_write ++ : BCH_MEMBER_ERROR_read, ++ "erasure coding %s error: %s", + bio_data_dir(bio) ? "write" : "read", + bch2_blk_status_to_str(bio->bi_status))) + clear_bit(ec_bio->idx, ec_bio->buf->valid); @@ -47226,7 +47385,7 @@ index 000000000..8646856e4 +} diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h new file mode 100644 -index 000000000..966d165a3 +index 000000000..61c67aa0a --- /dev/null +++ b/fs/bcachefs/ec.h @@ -0,0 +1,260 @@ @@ -47240,7 +47399,7 @@ index 000000000..966d165a3 + +enum bkey_invalid_flags; + -+int bch2_stripe_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_stripe_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_stripe_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); @@ -47613,10 +47772,10 @@ index 000000000..d260ff9bb +} diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h new file mode 100644 -index 000000000..3e9f09cea +index 000000000..2a11f32cf --- /dev/null +++ b/fs/bcachefs/errcode.h -@@ -0,0 +1,266 @@ +@@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_ERRCODE_H +#define _BCACHEFS_ERRCODE_H @@ -47832,6 +47991,7 @@ index 000000000..3e9f09cea + x(BCH_ERR_invalid_sb, invalid_sb_crypt) \ + x(BCH_ERR_invalid_sb, invalid_sb_clean) \ + x(BCH_ERR_invalid_sb, invalid_sb_quota) \ ++ x(BCH_ERR_invalid_sb, invalid_sb_errors) \ + x(BCH_ERR_invalid_sb, invalid_sb_opt_compression) \ + x(BCH_ERR_invalid, invalid_bkey) \ + x(BCH_ERR_operation_blocked, nocow_lock_blocked) \ @@ -47885,10 +48045,10 @@ index 000000000..3e9f09cea +#endif /* _BCACHFES_ERRCODE_H */ diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c new file mode 100644 -index 000000000..2a5af8872 +index 000000000..7b28d3792 --- /dev/null +++ b/fs/bcachefs/error.c -@@ -0,0 +1,293 @@ +@@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "bcachefs.h" +#include "error.h" @@ -47947,8 +48107,9 @@ index 000000000..2a5af8872 + up_write(&c->state_lock); +} + -+void bch2_io_error(struct bch_dev *ca) ++void bch2_io_error(struct bch_dev *ca, enum bch_member_error_type type) +{ ++ atomic64_inc(&ca->errors[type]); + //queue_work(system_long_wq, &ca->io_error_work); +} + @@ -48007,31 +48168,34 @@ index 000000000..2a5af8872 + if (test_bit(BCH_FS_FSCK_DONE, &c->flags)) + return NULL; + -+ list_for_each_entry(s, &c->fsck_errors, list) ++ list_for_each_entry(s, &c->fsck_error_msgs, list) + if (s->fmt == fmt) { + /* + * move it to the head of the list: repeated fsck errors + * are common + */ -+ list_move(&s->list, &c->fsck_errors); ++ list_move(&s->list, &c->fsck_error_msgs); + return s; + } + + s = kzalloc(sizeof(*s), GFP_NOFS); + if (!s) { -+ if (!c->fsck_alloc_err) ++ if (!c->fsck_alloc_msgs_err) + bch_err(c, "kmalloc err, cannot ratelimit fsck errs"); -+ c->fsck_alloc_err = true; ++ c->fsck_alloc_msgs_err = true; + return NULL; + } + + INIT_LIST_HEAD(&s->list); + s->fmt = fmt; -+ list_add(&s->list, &c->fsck_errors); ++ list_add(&s->list, &c->fsck_error_msgs); + return s; +} + -+int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...) ++int bch2_fsck_err(struct bch_fs *c, ++ enum bch_fsck_flags flags, ++ enum bch_sb_error_id err, ++ const char *fmt, ...) +{ + struct fsck_err_state *s = NULL; + va_list args; @@ -48039,11 +48203,13 @@ index 000000000..2a5af8872 + struct printbuf buf = PRINTBUF, *out = &buf; + int ret = -BCH_ERR_fsck_ignore; + ++ bch2_sb_error_count(c, err); ++ + va_start(args, fmt); + prt_vprintf(out, fmt, args); + va_end(args); + -+ mutex_lock(&c->fsck_error_lock); ++ mutex_lock(&c->fsck_error_msgs_lock); + s = fsck_err_get(c, fmt); + if (s) { + /* @@ -48053,7 +48219,7 @@ index 000000000..2a5af8872 + */ + if (s->last_msg && !strcmp(buf.buf, s->last_msg)) { + ret = s->ret; -+ mutex_unlock(&c->fsck_error_lock); ++ mutex_unlock(&c->fsck_error_msgs_lock); + printbuf_exit(&buf); + return ret; + } @@ -48148,7 +48314,7 @@ index 000000000..2a5af8872 + if (s) + s->ret = ret; + -+ mutex_unlock(&c->fsck_error_lock); ++ mutex_unlock(&c->fsck_error_msgs_lock); + + printbuf_exit(&buf); + @@ -48169,9 +48335,9 @@ index 000000000..2a5af8872 +{ + struct fsck_err_state *s, *n; + -+ mutex_lock(&c->fsck_error_lock); ++ mutex_lock(&c->fsck_error_msgs_lock); + -+ list_for_each_entry_safe(s, n, &c->fsck_errors, list) { ++ list_for_each_entry_safe(s, n, &c->fsck_error_msgs, list) { + if (s->ratelimited && s->last_msg) + bch_err(c, "Saw %llu errors like:\n %s", s->nr, s->last_msg); + @@ -48180,20 +48346,21 @@ index 000000000..2a5af8872 + kfree(s); + } + -+ mutex_unlock(&c->fsck_error_lock); ++ mutex_unlock(&c->fsck_error_msgs_lock); +} diff --git a/fs/bcachefs/error.h b/fs/bcachefs/error.h new file mode 100644 -index 000000000..7ce954005 +index 000000000..d167d6598 --- /dev/null +++ b/fs/bcachefs/error.h -@@ -0,0 +1,206 @@ +@@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_ERROR_H +#define _BCACHEFS_ERROR_H + +#include +#include ++#include "sb-errors.h" + +struct bch_dev; +struct bch_fs; @@ -48291,18 +48458,26 @@ index 000000000..7ce954005 + char *last_msg; +}; + -+#define FSCK_CAN_FIX (1 << 0) -+#define FSCK_CAN_IGNORE (1 << 1) -+#define FSCK_NEED_FSCK (1 << 2) -+#define FSCK_NO_RATELIMIT (1 << 3) ++enum bch_fsck_flags { ++ FSCK_CAN_FIX = 1 << 0, ++ FSCK_CAN_IGNORE = 1 << 1, ++ FSCK_NEED_FSCK = 1 << 2, ++ FSCK_NO_RATELIMIT = 1 << 3, ++}; + -+__printf(3, 4) __cold -+int bch2_fsck_err(struct bch_fs *, unsigned, const char *, ...); ++#define fsck_err_count(_c, _err) bch2_sb_err_count(_c, BCH_FSCK_ERR_##_err) ++ ++__printf(4, 5) __cold ++int bch2_fsck_err(struct bch_fs *, ++ enum bch_fsck_flags, ++ enum bch_sb_error_id, ++ const char *, ...); +void bch2_flush_fsck_errs(struct bch_fs *); + -+#define __fsck_err(c, _flags, msg, ...) \ ++#define __fsck_err(c, _flags, _err_type, ...) \ +({ \ -+ int _ret = bch2_fsck_err(c, _flags, msg, ##__VA_ARGS__); \ ++ int _ret = bch2_fsck_err(c, _flags, BCH_FSCK_ERR_##_err_type, \ ++ __VA_ARGS__); \ + \ + if (_ret != -BCH_ERR_fsck_fix && \ + _ret != -BCH_ERR_fsck_ignore) { \ @@ -48317,26 +48492,53 @@ index 000000000..7ce954005 + +/* XXX: mark in superblock that filesystem contains errors, if we ignore: */ + -+#define __fsck_err_on(cond, c, _flags, ...) \ -+ (unlikely(cond) ? __fsck_err(c, _flags, ##__VA_ARGS__) : false) ++#define __fsck_err_on(cond, c, _flags, _err_type, ...) \ ++ (unlikely(cond) ? __fsck_err(c, _flags, _err_type, __VA_ARGS__) : false) ++ ++#define need_fsck_err_on(cond, c, _err_type, ...) \ ++ __fsck_err_on(cond, c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, _err_type, __VA_ARGS__) + -+#define need_fsck_err_on(cond, c, ...) \ -+ __fsck_err_on(cond, c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ##__VA_ARGS__) ++#define need_fsck_err(c, _err_type, ...) \ ++ __fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, _err_type, __VA_ARGS__) + -+#define need_fsck_err(c, ...) \ -+ __fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ##__VA_ARGS__) ++#define mustfix_fsck_err(c, _err_type, ...) \ ++ __fsck_err(c, FSCK_CAN_FIX, _err_type, __VA_ARGS__) + -+#define mustfix_fsck_err(c, ...) \ -+ __fsck_err(c, FSCK_CAN_FIX, ##__VA_ARGS__) ++#define mustfix_fsck_err_on(cond, c, _err_type, ...) \ ++ __fsck_err_on(cond, c, FSCK_CAN_FIX, _err_type, __VA_ARGS__) + -+#define mustfix_fsck_err_on(cond, c, ...) \ -+ __fsck_err_on(cond, c, FSCK_CAN_FIX, ##__VA_ARGS__) ++#define fsck_err(c, _err_type, ...) \ ++ __fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, _err_type, __VA_ARGS__) + -+#define fsck_err(c, ...) \ -+ __fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_ARGS__) ++#define fsck_err_on(cond, c, _err_type, ...) \ ++ __fsck_err_on(cond, c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, _err_type, __VA_ARGS__) + -+#define fsck_err_on(cond, c, ...) \ -+ __fsck_err_on(cond, c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_ARGS__) ++static inline void bch2_bkey_fsck_err(struct bch_fs *c, ++ struct printbuf *err_msg, ++ enum bch_sb_error_id err_type, ++ const char *fmt, ...) ++{ ++ va_list args; ++ ++ va_start(args, fmt); ++ prt_vprintf(err_msg, fmt, args); ++ va_end(args); ++ ++} ++ ++#define bkey_fsck_err(c, _err_msg, _err_type, ...) \ ++do { \ ++ prt_printf(_err_msg, __VA_ARGS__); \ ++ bch2_sb_error_count(c, BCH_FSCK_ERR_##_err_type); \ ++ ret = -BCH_ERR_invalid_bkey; \ ++ goto fsck_err; \ ++} while (0) ++ ++#define bkey_fsck_err_on(cond, ...) \ ++do { \ ++ if (unlikely(cond)) \ ++ bkey_fsck_err(__VA_ARGS__); \ ++} while (0) + +/* + * Fatal errors: these don't indicate a bug, but we can't continue running in RW @@ -48369,26 +48571,26 @@ index 000000000..7ce954005 +void bch2_io_error_work(struct work_struct *); + +/* Does the error handling without logging a message */ -+void bch2_io_error(struct bch_dev *); ++void bch2_io_error(struct bch_dev *, enum bch_member_error_type); + -+#define bch2_dev_io_err_on(cond, ca, ...) \ ++#define bch2_dev_io_err_on(cond, ca, _type, ...) \ +({ \ + bool _ret = (cond); \ + \ + if (_ret) { \ + bch_err_dev_ratelimited(ca, __VA_ARGS__); \ -+ bch2_io_error(ca); \ ++ bch2_io_error(ca, _type); \ + } \ + _ret; \ +}) + -+#define bch2_dev_inum_io_err_on(cond, ca, ...) \ ++#define bch2_dev_inum_io_err_on(cond, ca, _type, ...) \ +({ \ + bool _ret = (cond); \ + \ + if (_ret) { \ + bch_err_inum_offset_ratelimited(ca, __VA_ARGS__); \ -+ bch2_io_error(ca); \ ++ bch2_io_error(ca, _type); \ + } \ + _ret; \ +}) @@ -48593,10 +48795,10 @@ index 000000000..6f5cf4493 +#endif /* _BCACHEFS_EXTENT_UPDATE_H */ diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c new file mode 100644 -index 000000000..0c60d49c3 +index 000000000..a864de231 --- /dev/null +++ b/fs/bcachefs/extents.c -@@ -0,0 +1,1540 @@ +@@ -0,0 +1,1516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 Kent Overstreet @@ -48762,17 +48964,19 @@ index 000000000..0c60d49c3 + +/* KEY_TYPE_btree_ptr: */ + -+int bch2_btree_ptr_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_btree_ptr_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (bkey_val_u64s(k.k) > BCH_REPLICAS_MAX) { -+ prt_printf(err, "value too big (%zu > %u)", -+ bkey_val_u64s(k.k), BCH_REPLICAS_MAX); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return bch2_bkey_ptrs_invalid(c, k, flags, err); ++ bkey_fsck_err_on(bkey_val_u64s(k.k) > BCH_REPLICAS_MAX, c, err, ++ btree_ptr_val_too_big, ++ "value too big (%zu > %u)", bkey_val_u64s(k.k), BCH_REPLICAS_MAX); ++ ++ ret = bch2_bkey_ptrs_invalid(c, k, flags, err); ++fsck_err: ++ return ret; +} + +void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c, @@ -48781,17 +48985,20 @@ index 000000000..0c60d49c3 + bch2_bkey_ptrs_to_text(out, c, k); +} + -+int bch2_btree_ptr_v2_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_btree_ptr_v2_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX) { -+ prt_printf(err, "value too big (%zu > %zu)", -+ bkey_val_u64s(k.k), BKEY_BTREE_PTR_VAL_U64s_MAX); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return bch2_bkey_ptrs_invalid(c, k, flags, err); ++ bkey_fsck_err_on(bkey_val_u64s(k.k) > BKEY_BTREE_PTR_VAL_U64s_MAX, c, err, ++ btree_ptr_v2_val_too_big, ++ "value too big (%zu > %zu)", ++ bkey_val_u64s(k.k), BKEY_BTREE_PTR_VAL_U64s_MAX); ++ ++ ret = bch2_bkey_ptrs_invalid(c, k, flags, err); ++fsck_err: ++ return ret; +} + +void bch2_btree_ptr_v2_to_text(struct printbuf *out, struct bch_fs *c, @@ -48972,19 +49179,18 @@ index 000000000..0c60d49c3 + +/* KEY_TYPE_reservation: */ + -+int bch2_reservation_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_reservation_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_reservation r = bkey_s_c_to_reservation(k); ++ int ret = 0; + -+ if (!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX) { -+ prt_printf(err, "invalid nr_replicas (%u)", -+ r.v->nr_replicas); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(!r.v->nr_replicas || r.v->nr_replicas > BCH_REPLICAS_MAX, c, err, ++ reservation_key_nr_replicas_invalid, ++ "invalid nr_replicas (%u)", r.v->nr_replicas); ++fsck_err: ++ return ret; +} + +void bch2_reservation_to_text(struct printbuf *out, struct bch_fs *c, @@ -49657,7 +49863,7 @@ index 000000000..0c60d49c3 + } +} + -+static int extent_ptr_invalid(const struct bch_fs *c, ++static int extent_ptr_invalid(struct bch_fs *c, + struct bkey_s_c k, + enum bkey_invalid_flags flags, + const struct bch_extent_ptr *ptr, @@ -49670,6 +49876,7 @@ index 000000000..0c60d49c3 + u64 bucket; + u32 bucket_offset; + struct bch_dev *ca; ++ int ret = 0; + + if (!bch2_dev_exists2(c, ptr->dev)) { + /* @@ -49680,41 +49887,33 @@ index 000000000..0c60d49c3 + if (flags & BKEY_INVALID_WRITE) + return 0; + -+ prt_printf(err, "pointer to invalid device (%u)", ptr->dev); -+ return -BCH_ERR_invalid_bkey; ++ bkey_fsck_err(c, err, ptr_to_invalid_device, ++ "pointer to invalid device (%u)", ptr->dev); + } + + ca = bch_dev_bkey_exists(c, ptr->dev); + bkey_for_each_ptr(ptrs, ptr2) -+ if (ptr != ptr2 && ptr->dev == ptr2->dev) { -+ prt_printf(err, "multiple pointers to same device (%u)", ptr->dev); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(ptr != ptr2 && ptr->dev == ptr2->dev, c, err, ++ ptr_to_duplicate_device, ++ "multiple pointers to same device (%u)", ptr->dev); + + bucket = sector_to_bucket_and_offset(ca, ptr->offset, &bucket_offset); + -+ if (bucket >= ca->mi.nbuckets) { -+ prt_printf(err, "pointer past last bucket (%llu > %llu)", -+ bucket, ca->mi.nbuckets); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket)) { -+ prt_printf(err, "pointer before first bucket (%llu < %u)", -+ bucket, ca->mi.first_bucket); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (bucket_offset + size_ondisk > ca->mi.bucket_size) { -+ prt_printf(err, "pointer spans multiple buckets (%u + %u > %u)", ++ bkey_fsck_err_on(bucket >= ca->mi.nbuckets, c, err, ++ ptr_after_last_bucket, ++ "pointer past last bucket (%llu > %llu)", bucket, ca->mi.nbuckets); ++ bkey_fsck_err_on(ptr->offset < bucket_to_sector(ca, ca->mi.first_bucket), c, err, ++ ptr_before_first_bucket, ++ "pointer before first bucket (%llu < %u)", bucket, ca->mi.first_bucket); ++ bkey_fsck_err_on(bucket_offset + size_ondisk > ca->mi.bucket_size, c, err, ++ ptr_spans_multiple_buckets, ++ "pointer spans multiple buckets (%u + %u > %u)", + bucket_offset, size_ondisk, ca->mi.bucket_size); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++fsck_err: ++ return ret; +} + -+int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_bkey_ptrs_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ @@ -49724,24 +49923,22 @@ index 000000000..0c60d49c3 + unsigned size_ondisk = k.k->size; + unsigned nonce = UINT_MAX; + unsigned nr_ptrs = 0; -+ bool unwritten = false, have_ec = false, crc_since_last_ptr = false; -+ int ret; ++ bool have_written = false, have_unwritten = false, have_ec = false, crc_since_last_ptr = false; ++ int ret = 0; + + if (bkey_is_btree_ptr(k.k)) + size_ondisk = btree_sectors(c); + + bkey_extent_entry_for_each(ptrs, entry) { -+ if (__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX) { -+ prt_printf(err, "invalid extent entry type (got %u, max %u)", -+ __extent_entry_type(entry), BCH_EXTENT_ENTRY_MAX); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(__extent_entry_type(entry) >= BCH_EXTENT_ENTRY_MAX, c, err, ++ extent_ptrs_invalid_entry, ++ "invalid extent entry type (got %u, max %u)", ++ __extent_entry_type(entry), BCH_EXTENT_ENTRY_MAX); + -+ if (bkey_is_btree_ptr(k.k) && -+ !extent_entry_is_ptr(entry)) { -+ prt_printf(err, "has non ptr field"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_is_btree_ptr(k.k) && ++ !extent_entry_is_ptr(entry), c, err, ++ btree_ptr_has_non_ptr, ++ "has non ptr field"); + + switch (extent_entry_type(entry)) { + case BCH_EXTENT_ENTRY_ptr: @@ -49750,22 +49947,15 @@ index 000000000..0c60d49c3 + if (ret) + return ret; + -+ if (nr_ptrs && unwritten != entry->ptr.unwritten) { -+ prt_printf(err, "extent with unwritten and written ptrs"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (k.k->type != KEY_TYPE_extent && entry->ptr.unwritten) { -+ prt_printf(err, "has unwritten ptrs"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(entry->ptr.cached && have_ec, c, err, ++ ptr_cached_and_erasure_coded, ++ "cached, erasure coded ptr"); + -+ if (entry->ptr.cached && have_ec) { -+ prt_printf(err, "cached, erasure coded ptr"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ if (!entry->ptr.unwritten) ++ have_written = true; ++ else ++ have_unwritten = true; + -+ unwritten = entry->ptr.unwritten; + have_ec = false; + crc_since_last_ptr = false; + nr_ptrs++; @@ -49775,52 +49965,41 @@ index 000000000..0c60d49c3 + case BCH_EXTENT_ENTRY_crc128: + crc = bch2_extent_crc_unpack(k.k, entry_to_crc(entry)); + -+ if (crc.offset + crc.live_size > -+ crc.uncompressed_size) { -+ prt_printf(err, "checksum offset + key size > uncompressed size"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ size_ondisk = crc.compressed_size; -+ -+ if (!bch2_checksum_type_valid(c, crc.csum_type)) { -+ prt_printf(err, "invalid checksum type"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (crc.compression_type >= BCH_COMPRESSION_TYPE_NR) { -+ prt_printf(err, "invalid compression type"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(crc.offset + crc.live_size > crc.uncompressed_size, c, err, ++ ptr_crc_uncompressed_size_too_small, ++ "checksum offset + key size > uncompressed size"); ++ bkey_fsck_err_on(!bch2_checksum_type_valid(c, crc.csum_type), c, err, ++ ptr_crc_csum_type_unknown, ++ "invalid checksum type"); ++ bkey_fsck_err_on(crc.compression_type >= BCH_COMPRESSION_TYPE_NR, c, err, ++ ptr_crc_compression_type_unknown, ++ "invalid compression type"); + + if (bch2_csum_type_is_encryption(crc.csum_type)) { + if (nonce == UINT_MAX) + nonce = crc.offset + crc.nonce; -+ else if (nonce != crc.offset + crc.nonce) { -+ prt_printf(err, "incorrect nonce"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ else if (nonce != crc.offset + crc.nonce) ++ bkey_fsck_err(c, err, ptr_crc_nonce_mismatch, ++ "incorrect nonce"); + } + -+ if (crc_since_last_ptr) { -+ prt_printf(err, "redundant crc entry"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(crc_since_last_ptr, c, err, ++ ptr_crc_redundant, ++ "redundant crc entry"); + crc_since_last_ptr = true; + -+ if (crc_is_encoded(crc) && -+ (crc.uncompressed_size > c->opts.encoded_extent_max >> 9) && -+ (flags & (BKEY_INVALID_WRITE|BKEY_INVALID_COMMIT))) { -+ prt_printf(err, "too large encoded extent"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(crc_is_encoded(crc) && ++ (crc.uncompressed_size > c->opts.encoded_extent_max >> 9) && ++ (flags & (BKEY_INVALID_WRITE|BKEY_INVALID_COMMIT)), c, err, ++ ptr_crc_uncompressed_size_too_big, ++ "too large encoded extent"); + ++ size_ondisk = crc.compressed_size; + break; + case BCH_EXTENT_ENTRY_stripe_ptr: -+ if (have_ec) { -+ prt_printf(err, "redundant stripe entry"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(have_ec, c, err, ++ ptr_stripe_redundant, ++ "redundant stripe entry"); + have_ec = true; + break; + case BCH_EXTENT_ENTRY_rebalance: { @@ -49837,27 +50016,26 @@ index 000000000..0c60d49c3 + } + } + -+ if (!nr_ptrs) { -+ prt_str(err, "no ptrs"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (nr_ptrs >= BCH_BKEY_PTRS_MAX) { -+ prt_str(err, "too many ptrs"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (crc_since_last_ptr) { -+ prt_printf(err, "redundant crc entry"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (have_ec) { -+ prt_printf(err, "redundant stripe entry"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ return 0; ++ bkey_fsck_err_on(!nr_ptrs, c, err, ++ extent_ptrs_no_ptrs, ++ "no ptrs"); ++ bkey_fsck_err_on(nr_ptrs > BCH_BKEY_PTRS_MAX, c, err, ++ extent_ptrs_too_many_ptrs, ++ "too many ptrs: %u > %u", nr_ptrs, BCH_BKEY_PTRS_MAX); ++ bkey_fsck_err_on(have_written && have_unwritten, c, err, ++ extent_ptrs_written_and_unwritten, ++ "extent with unwritten and written ptrs"); ++ bkey_fsck_err_on(k.k->type != KEY_TYPE_extent && have_unwritten, c, err, ++ extent_ptrs_unwritten, ++ "has unwritten ptrs"); ++ bkey_fsck_err_on(crc_since_last_ptr, c, err, ++ extent_ptrs_redundant_crc, ++ "redundant crc entry"); ++ bkey_fsck_err_on(have_ec, c, err, ++ extent_ptrs_redundant_stripe, ++ "redundant stripe entry"); ++fsck_err: ++ return ret; +} + +void bch2_ptr_swab(struct bkey_s k) @@ -50139,7 +50317,7 @@ index 000000000..0c60d49c3 +} diff --git a/fs/bcachefs/extents.h b/fs/bcachefs/extents.h new file mode 100644 -index 000000000..9110acae7 +index 000000000..a2ce8a3be --- /dev/null +++ b/fs/bcachefs/extents.h @@ -0,0 +1,765 @@ @@ -50545,12 +50723,12 @@ index 000000000..9110acae7 + +/* KEY_TYPE_btree_ptr: */ + -+int bch2_btree_ptr_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_btree_ptr_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_btree_ptr_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); + -+int bch2_btree_ptr_v2_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_btree_ptr_v2_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_btree_ptr_v2_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); +void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned, @@ -50590,7 +50768,7 @@ index 000000000..9110acae7 + +/* KEY_TYPE_reservation: */ + -+int bch2_reservation_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_reservation_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_reservation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); +bool bch2_reservation_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c); @@ -50850,7 +51028,7 @@ index 000000000..9110acae7 +bool bch2_extent_normalize(struct bch_fs *, struct bkey_s); +void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); -+int bch2_bkey_ptrs_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_bkey_ptrs_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); + +void bch2_ptr_swab(struct bkey_s); @@ -58885,10 +59063,10 @@ index 000000000..5edf1d4b9 +#endif /* _BCACHEFS_FS_H */ diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c new file mode 100644 -index 000000000..328cb3b3e +index 000000000..0e470ebd7 --- /dev/null +++ b/fs/bcachefs/fsck.c -@@ -0,0 +1,2466 @@ +@@ -0,0 +1,2490 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -59612,8 +59790,9 @@ index 000000000..328cb3b3e + int ret = 0; + + if (mustfix_fsck_err_on(!bch2_snapshot_equiv(c, k.k->p.snapshot), c, -+ "key in missing snapshot: %s", -+ (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) ++ bkey_in_missing_snapshot, ++ "key in missing snapshot: %s", ++ (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) + ret = bch2_btree_delete_at(trans, iter, + BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?: 1; +fsck_err: @@ -59682,6 +59861,7 @@ index 000000000..328cb3b3e + + if (fsck_err_on(k.k->type == desc.key_type && + !desc.cmp_bkey(k, hash_k), c, ++ hash_table_key_duplicate, + "duplicate hash table keys:\n%s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, hash_k), @@ -59700,7 +59880,8 @@ index 000000000..328cb3b3e + printbuf_exit(&buf); + return ret; +bad_hash: -+ if (fsck_err(c, "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s", ++ if (fsck_err(c, hash_table_key_wrong_offset, ++ "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s", + bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash, + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) { @@ -59751,7 +59932,8 @@ index 000000000..328cb3b3e + *prev = u; + + if (fsck_err_on(prev->bi_hash_seed != u.bi_hash_seed || -+ inode_d_type(prev) != inode_d_type(&u), c, ++ inode_d_type(prev) != inode_d_type(&u), ++ c, inode_snapshot_mismatch, + "inodes in different snapshots don't match")) { + bch_err(c, "repair not implemented yet"); + return -EINVAL; @@ -59779,7 +59961,8 @@ index 000000000..328cb3b3e + + if (u.bi_flags & BCH_INODE_UNLINKED && + (!c->sb.clean || -+ fsck_err(c, "filesystem marked clean, but inode %llu unlinked", ++ fsck_err(c, inode_unlinked_but_clean, ++ "filesystem marked clean, but inode %llu unlinked", + u.bi_inum))) { + bch2_trans_unlock(trans); + bch2_fs_lazy_rw(c); @@ -59791,7 +59974,8 @@ index 000000000..328cb3b3e + + if (u.bi_flags & BCH_INODE_I_SIZE_DIRTY && + (!c->sb.clean || -+ fsck_err(c, "filesystem marked clean, but inode %llu has i_size dirty", ++ fsck_err(c, inode_i_size_dirty_but_clean, ++ "filesystem marked clean, but inode %llu has i_size dirty", + u.bi_inum))) { + bch_verbose(c, "truncating inode %llu", u.bi_inum); + @@ -59823,7 +60007,8 @@ index 000000000..328cb3b3e + + if (u.bi_flags & BCH_INODE_I_SECTORS_DIRTY && + (!c->sb.clean || -+ fsck_err(c, "filesystem marked clean, but inode %llu has i_sectors dirty", ++ fsck_err(c, inode_i_sectors_dirty_but_clean, ++ "filesystem marked clean, but inode %llu has i_sectors dirty", + u.bi_inum))) { + s64 sectors; + @@ -59949,10 +60134,11 @@ index 000000000..328cb3b3e + return -BCH_ERR_internal_fsck_err; + } + -+ if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SECTORS_DIRTY), c, -+ "inode %llu:%u has incorrect i_sectors: got %llu, should be %llu", -+ w->last_pos.inode, i->snapshot, -+ i->inode.bi_sectors, i->count)) { ++ if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SECTORS_DIRTY), ++ c, inode_i_sectors_wrong, ++ "inode %llu:%u has incorrect i_sectors: got %llu, should be %llu", ++ w->last_pos.inode, i->snapshot, ++ i->inode.bi_sectors, i->count)) { + i->inode.bi_sectors = i->count; + ret = fsck_write_inode(trans, &i->inode, i->snapshot); + if (ret) @@ -60093,7 +60279,8 @@ index 000000000..328cb3b3e + prt_printf(&buf, "\n overwriting %s extent", + pos1.snapshot >= pos2.p.snapshot ? "first" : "second"); + -+ if (fsck_err(c, "overlapping extents%s", buf.buf)) { ++ if (fsck_err(c, extent_overlapping, ++ "overlapping extents%s", buf.buf)) { + struct btree_iter *old_iter = &iter1; + struct disk_reservation res = { 0 }; + @@ -60248,7 +60435,7 @@ index 000000000..328cb3b3e + goto err; + + if (k.k->type != KEY_TYPE_whiteout) { -+ if (fsck_err_on(!i, c, ++ if (fsck_err_on(!i, c, extent_in_missing_inode, + "extent in missing inode:\n %s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, k), buf.buf))) @@ -60256,7 +60443,8 @@ index 000000000..328cb3b3e + + if (fsck_err_on(i && + !S_ISREG(i->inode.bi_mode) && -+ !S_ISLNK(i->inode.bi_mode), c, ++ !S_ISLNK(i->inode.bi_mode), ++ c, extent_in_non_reg_inode, + "extent in non regular inode mode %o:\n %s", + i->inode.bi_mode, + (printbuf_reset(&buf), @@ -60288,7 +60476,8 @@ index 000000000..328cb3b3e + if (k.k->type != KEY_TYPE_whiteout) { + if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_I_SIZE_DIRTY) && + k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 && -+ !bkey_extent_is_reservation(k), c, ++ !bkey_extent_is_reservation(k), ++ c, extent_past_end_of_inode, + "extent type past end of inode %llu:%u, i_size %llu\n %s", + i->inode.bi_inum, i->snapshot, i->inode.bi_size, + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { @@ -60410,7 +60599,8 @@ index 000000000..328cb3b3e + continue; + } + -+ if (fsck_err_on(i->inode.bi_nlink != i->count, c, ++ if (fsck_err_on(i->inode.bi_nlink != i->count, ++ c, inode_dir_wrong_nlink, + "directory %llu:%u with wrong i_nlink: got %u, should be %llu", + w->last_pos.inode, i->snapshot, i->inode.bi_nlink, i->count)) { + i->inode.bi_nlink = i->count; @@ -60454,16 +60644,16 @@ index 000000000..328cb3b3e + backpointer_exists = ret; + ret = 0; + -+ if (fsck_err_on(S_ISDIR(target->bi_mode) && -+ backpointer_exists, c, ++ if (fsck_err_on(S_ISDIR(target->bi_mode) && backpointer_exists, ++ c, inode_dir_multiple_links, + "directory %llu with multiple links", + target->bi_inum)) { + ret = __remove_dirent(trans, d.k->p); + goto out; + } + -+ if (fsck_err_on(backpointer_exists && -+ !target->bi_nlink, c, ++ if (fsck_err_on(backpointer_exists && !target->bi_nlink, ++ c, inode_multiple_links_but_nlink_0, + "inode %llu type %s has multiple links but i_nlink 0", + target->bi_inum, bch2_d_types[d.v->d_type])) { + target->bi_nlink++; @@ -60474,7 +60664,8 @@ index 000000000..328cb3b3e + goto err; + } + -+ if (fsck_err_on(!backpointer_exists, c, ++ if (fsck_err_on(!backpointer_exists, ++ c, inode_wrong_backpointer, + "inode %llu:%u has wrong backpointer:\n" + "got %llu:%llu\n" + "should be %llu:%llu", @@ -60492,7 +60683,8 @@ index 000000000..328cb3b3e + } + } + -+ if (fsck_err_on(d.v->d_type != inode_d_type(target), c, ++ if (fsck_err_on(d.v->d_type != inode_d_type(target), ++ c, dirent_d_type_wrong, + "incorrect d_type: got %s, should be %s:\n%s", + bch2_d_type_str(d.v->d_type), + bch2_d_type_str(inode_d_type(target)), @@ -60516,7 +60708,8 @@ index 000000000..328cb3b3e + if (d.v->d_type == DT_SUBVOL && + target->bi_parent_subvol != le32_to_cpu(d.v->d_parent_subvol) && + (c->sb.version < bcachefs_metadata_version_subvol_dirent || -+ fsck_err(c, "dirent has wrong d_parent_subvol field: got %u, should be %u", ++ fsck_err(c, dirent_d_parent_subvol_wrong, ++ "dirent has wrong d_parent_subvol field: got %u, should be %u", + le32_to_cpu(d.v->d_parent_subvol), + target->bi_parent_subvol))) { + n = bch2_trans_kmalloc(trans, bkey_bytes(d.k)); @@ -60588,7 +60781,7 @@ index 000000000..328cb3b3e + *hash_info = bch2_hash_info_init(c, &dir->inodes.data[0].inode); + dir->first_this_inode = false; + -+ if (fsck_err_on(!i, c, ++ if (fsck_err_on(!i, c, dirent_in_missing_dir_inode, + "dirent in nonexisting directory:\n%s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { @@ -60600,7 +60793,8 @@ index 000000000..328cb3b3e + if (!i) + goto out; + -+ if (fsck_err_on(!S_ISDIR(i->inode.bi_mode), c, ++ if (fsck_err_on(!S_ISDIR(i->inode.bi_mode), ++ c, dirent_in_non_dir_inode, + "dirent in non directory inode type %s:\n%s", + bch2_d_type_str(inode_d_type(&i->inode)), + (printbuf_reset(&buf), @@ -60634,7 +60828,7 @@ index 000000000..328cb3b3e + if (ret && !bch2_err_matches(ret, ENOENT)) + goto err; + -+ if (fsck_err_on(ret, c, ++ if (fsck_err_on(ret, c, dirent_to_missing_subvol, + "dirent points to missing subvolume %u", + le32_to_cpu(d.v->d_child_subvol))) { + ret = __remove_dirent(trans, d.k->p); @@ -60646,7 +60840,7 @@ index 000000000..328cb3b3e + if (ret && !bch2_err_matches(ret, ENOENT)) + goto err; + -+ if (fsck_err_on(ret, c, ++ if (fsck_err_on(ret, c, subvol_to_missing_root, + "subvolume %u points to missing subvolume root %llu", + target_subvol, + target_inum)) { @@ -60655,7 +60849,8 @@ index 000000000..328cb3b3e + goto err; + } + -+ if (fsck_err_on(subvol_root.bi_subvol != target_subvol, c, ++ if (fsck_err_on(subvol_root.bi_subvol != target_subvol, ++ c, subvol_root_wrong_bi_subvol, + "subvol root %llu has wrong bi_subvol field: got %u, should be %u", + target_inum, + subvol_root.bi_subvol, target_subvol)) { @@ -60674,7 +60869,8 @@ index 000000000..328cb3b3e + if (ret) + goto err; + -+ if (fsck_err_on(!target->inodes.nr, c, ++ if (fsck_err_on(!target->inodes.nr, ++ c, dirent_to_missing_inode, + "dirent points to missing inode: (equiv %u)\n%s", + equiv.snapshot, + (printbuf_reset(&buf), @@ -60760,7 +60956,7 @@ index 000000000..328cb3b3e + *hash_info = bch2_hash_info_init(c, &inode->inodes.data[0].inode); + inode->first_this_inode = false; + -+ if (fsck_err_on(!i, c, ++ if (fsck_err_on(!i, c, xattr_in_missing_inode, + "xattr for missing inode %llu", + k.k->p.inode)) + return bch2_btree_delete_at(trans, iter, 0); @@ -60809,7 +61005,8 @@ index 000000000..328cb3b3e + if (ret && !bch2_err_matches(ret, ENOENT)) + return ret; + -+ if (mustfix_fsck_err_on(ret, c, "root subvol missing")) { ++ if (mustfix_fsck_err_on(ret, c, root_subvol_missing, ++ "root subvol missing")) { + struct bkey_i_subvolume root_subvol; + + snapshot = U32_MAX; @@ -60835,8 +61032,10 @@ index 000000000..328cb3b3e + if (ret && !bch2_err_matches(ret, ENOENT)) + return ret; + -+ if (mustfix_fsck_err_on(ret, c, "root directory missing") || -+ mustfix_fsck_err_on(!S_ISDIR(root_inode.bi_mode), c, ++ if (mustfix_fsck_err_on(ret, c, root_dir_missing, ++ "root directory missing") || ++ mustfix_fsck_err_on(!S_ISDIR(root_inode.bi_mode), ++ c, root_inode_not_dir, + "root inode not a directory")) { + bch2_inode_init(c, &root_inode, 0, 0, S_IFDIR|0755, + 0, NULL); @@ -60940,7 +61139,8 @@ index 000000000..328cb3b3e + } + + if (bch2_err_matches(ret, ENOENT)) { -+ if (fsck_err(c, "unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu", ++ if (fsck_err(c, inode_unreachable, ++ "unreachable inode %llu:%u, type %s nlink %u backptr %llu:%llu", + inode->bi_inum, snapshot, + bch2_d_type_str(inode_d_type(inode)), + inode->bi_nlink, @@ -60980,7 +61180,8 @@ index 000000000..328cb3b3e + pr_err("%llu:%u", i->inum, i->snapshot); + pr_err("%llu:%u", inode->bi_inum, snapshot); + -+ if (!fsck_err(c, "directory structure loop")) ++ if (!fsck_err(c, dir_loop, ++ "directory structure loop")) + return 0; + + ret = commit_do(trans, NULL, NULL, @@ -61240,7 +61441,8 @@ index 000000000..328cb3b3e + link = &links->d[++*idx]; + } + -+ if (fsck_err_on(bch2_inode_nlink_get(&u) != link->count, c, ++ if (fsck_err_on(bch2_inode_nlink_get(&u) != link->count, ++ c, inode_wrong_nlink, + "inode %llu type %s has wrong i_nlink (%u, should be %u)", + u.bi_inum, bch2_d_types[mode_to_type(u.bi_mode)], + bch2_inode_nlink_get(&u), link->count)) { @@ -61378,10 +61580,10 @@ index 000000000..da991e8cf +#endif /* _BCACHEFS_FSCK_H */ diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c new file mode 100644 -index 000000000..23fcd442c +index 000000000..925d1b7f2 --- /dev/null +++ b/fs/bcachefs/inode.c -@@ -0,0 +1,1147 @@ +@@ -0,0 +1,1150 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -61782,104 +61984,102 @@ index 000000000..23fcd442c + return &inode_p->inode.k_i; +} + -+static int __bch2_inode_invalid(struct bkey_s_c k, struct printbuf *err) ++static int __bch2_inode_invalid(struct bch_fs *c, struct bkey_s_c k, struct printbuf *err) +{ + struct bch_inode_unpacked unpacked; ++ int ret = 0; + -+ if (k.k->p.inode) { -+ prt_printf(err, "nonzero k.p.inode"); -+ return -BCH_ERR_invalid_bkey; -+ } -+ -+ if (k.k->p.offset < BLOCKDEV_INODE_MAX) { -+ prt_printf(err, "fs inode in blockdev range"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(k.k->p.inode, c, err, ++ inode_pos_inode_nonzero, ++ "nonzero k.p.inode"); + -+ if (bch2_inode_unpack(k, &unpacked)) { -+ prt_printf(err, "invalid variable length fields"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(k.k->p.offset < BLOCKDEV_INODE_MAX, c, err, ++ inode_pos_blockdev_range, ++ "fs inode in blockdev range"); + -+ if (unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1) { -+ prt_printf(err, "invalid data checksum type (%u >= %u", -+ unpacked.bi_data_checksum, BCH_CSUM_OPT_NR + 1); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bch2_inode_unpack(k, &unpacked), c, err, ++ inode_unpack_error, ++ "invalid variable length fields"); + -+ if (unpacked.bi_compression && -+ !bch2_compression_opt_valid(unpacked.bi_compression - 1)) { -+ prt_printf(err, "invalid compression opt %u", -+ unpacked.bi_compression - 1); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(unpacked.bi_data_checksum >= BCH_CSUM_OPT_NR + 1, c, err, ++ inode_checksum_type_invalid, ++ "invalid data checksum type (%u >= %u", ++ unpacked.bi_data_checksum, BCH_CSUM_OPT_NR + 1); + -+ if ((unpacked.bi_flags & BCH_INODE_UNLINKED) && -+ unpacked.bi_nlink != 0) { -+ prt_printf(err, "flagged as unlinked but bi_nlink != 0"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(unpacked.bi_compression && ++ !bch2_compression_opt_valid(unpacked.bi_compression - 1), c, err, ++ inode_compression_type_invalid, ++ "invalid compression opt %u", unpacked.bi_compression - 1); + -+ if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) { -+ prt_printf(err, "subvolume root but not a directory"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on((unpacked.bi_flags & BCH_INODE_UNLINKED) && ++ unpacked.bi_nlink != 0, c, err, ++ inode_unlinked_but_nlink_nonzero, ++ "flagged as unlinked but bi_nlink != 0"); + -+ return 0; ++ bkey_fsck_err_on(unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode), c, err, ++ inode_subvol_root_but_not_dir, ++ "subvolume root but not a directory"); ++fsck_err: ++ return ret; +} + -+int bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_inode_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); ++ int ret = 0; + -+ if (INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR) { -+ prt_printf(err, "invalid str hash type (%llu >= %u)", -+ INODE_STR_HASH(inode.v), BCH_STR_HASH_NR); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err, ++ inode_str_hash_invalid, ++ "invalid str hash type (%llu >= %u)", ++ INODE_STR_HASH(inode.v), BCH_STR_HASH_NR); + -+ return __bch2_inode_invalid(k, err); ++ ret = __bch2_inode_invalid(c, k, err); ++fsck_err: ++ return ret; +} + -+int bch2_inode_v2_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_inode_v2_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_inode_v2 inode = bkey_s_c_to_inode_v2(k); ++ int ret = 0; + -+ if (INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR) { -+ prt_printf(err, "invalid str hash type (%llu >= %u)", -+ INODEv2_STR_HASH(inode.v), BCH_STR_HASH_NR); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(INODEv2_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err, ++ inode_str_hash_invalid, ++ "invalid str hash type (%llu >= %u)", ++ INODEv2_STR_HASH(inode.v), BCH_STR_HASH_NR); + -+ return __bch2_inode_invalid(k, err); ++ ret = __bch2_inode_invalid(c, k, err); ++fsck_err: ++ return ret; +} + -+int bch2_inode_v3_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_inode_v3_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_inode_v3 inode = bkey_s_c_to_inode_v3(k); ++ int ret = 0; + -+ if (INODEv3_FIELDS_START(inode.v) < INODEv3_FIELDS_START_INITIAL || -+ INODEv3_FIELDS_START(inode.v) > bkey_val_u64s(inode.k)) { -+ prt_printf(err, "invalid fields_start (got %llu, min %u max %zu)", -+ INODEv3_FIELDS_START(inode.v), -+ INODEv3_FIELDS_START_INITIAL, -+ bkey_val_u64s(inode.k)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(INODEv3_FIELDS_START(inode.v) < INODEv3_FIELDS_START_INITIAL || ++ INODEv3_FIELDS_START(inode.v) > bkey_val_u64s(inode.k), c, err, ++ inode_v3_fields_start_bad, ++ "invalid fields_start (got %llu, min %u max %zu)", ++ INODEv3_FIELDS_START(inode.v), ++ INODEv3_FIELDS_START_INITIAL, ++ bkey_val_u64s(inode.k)); + -+ if (INODEv3_STR_HASH(inode.v) >= BCH_STR_HASH_NR) { -+ prt_printf(err, "invalid str hash type (%llu >= %u)", -+ INODEv3_STR_HASH(inode.v), BCH_STR_HASH_NR); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(INODEv3_STR_HASH(inode.v) >= BCH_STR_HASH_NR, c, err, ++ inode_str_hash_invalid, ++ "invalid str hash type (%llu >= %u)", ++ INODEv3_STR_HASH(inode.v), BCH_STR_HASH_NR); + -+ return __bch2_inode_invalid(k, err); ++ ret = __bch2_inode_invalid(c, k, err); ++fsck_err: ++ return ret; +} + +static void __bch2_inode_unpacked_to_text(struct printbuf *out, @@ -61996,16 +62196,17 @@ index 000000000..23fcd442c + return 0; +} + -+int bch2_inode_generation_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_inode_generation_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (k.k->p.inode) { -+ prt_printf(err, "nonzero k.p.inode"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(k.k->p.inode, c, err, ++ inode_pos_inode_nonzero, ++ "nonzero k.p.inode"); ++fsck_err: ++ return ret; +} + +void bch2_inode_generation_to_text(struct printbuf *out, struct bch_fs *c, @@ -62452,6 +62653,7 @@ index 000000000..23fcd442c + return 0; + + if (!fsck_err_on(c->sb.clean, c, ++ deleted_inode_but_clean, + "filesystem marked as clean but have deleted inode %llu:%u", + pos.offset, pos.snapshot)) + return 0; @@ -62463,6 +62665,7 @@ index 000000000..23fcd442c + + ret = bkey_is_inode(k.k) ? 0 : -BCH_ERR_ENOENT_inode; + if (fsck_err_on(!bkey_is_inode(k.k), c, ++ deleted_inode_missing, + "nonexistent inode %llu:%u in deleted_inodes btree", + pos.offset, pos.snapshot)) + goto delete; @@ -62472,11 +62675,13 @@ index 000000000..23fcd442c + goto err; + + if (fsck_err_on(S_ISDIR(inode.bi_mode), c, ++ deleted_inode_is_dir, + "directory %llu:%u in deleted_inodes btree", + pos.offset, pos.snapshot)) + goto delete; + + if (fsck_err_on(!(inode.bi_flags & BCH_INODE_UNLINKED), c, ++ deleted_inode_not_unlinked, + "non-deleted inode %llu:%u in deleted_inodes btree", + pos.offset, pos.snapshot)) + goto delete; @@ -62531,7 +62736,7 @@ index 000000000..23fcd442c +} diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h new file mode 100644 -index 000000000..2781e3281 +index 000000000..74c62e6c1 --- /dev/null +++ b/fs/bcachefs/inode.h @@ -0,0 +1,208 @@ @@ -62545,11 +62750,11 @@ index 000000000..2781e3281 +enum bkey_invalid_flags; +extern const char * const bch2_inode_opts[]; + -+int bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_inode_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_inode_v2_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_inode_v2_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); -+int bch2_inode_v3_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_inode_v3_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -62589,7 +62794,7 @@ index 000000000..2781e3281 + k->type == KEY_TYPE_inode_v3; +} + -+int bch2_inode_generation_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_inode_generation_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_inode_generation_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -63315,7 +63520,7 @@ index 000000000..c9e6ed40e +#endif /* _BCACHEFS_IO_MISC_H */ diff --git a/fs/bcachefs/io_read.c b/fs/bcachefs/io_read.c new file mode 100644 -index 000000000..443c3ea65 +index 000000000..ae36fc485 --- /dev/null +++ b/fs/bcachefs/io_read.c @@ -0,0 +1,1210 @@ @@ -63964,7 +64169,7 @@ index 000000000..443c3ea65 + "data checksum error: expected %0llx:%0llx got %0llx:%0llx (type %s)", + rbio->pick.crc.csum.hi, rbio->pick.crc.csum.lo, + csum.hi, csum.lo, bch2_csum_types[crc.csum_type]); -+ bch2_io_error(ca); ++ bch2_io_error(ca, BCH_MEMBER_ERROR_checksum); + bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR); + goto out; +decompression_err: @@ -63998,7 +64203,7 @@ index 000000000..443c3ea65 + if (!rbio->split) + rbio->bio.bi_end_io = rbio->end_io; + -+ if (bch2_dev_inum_io_err_on(bio->bi_status, ca, ++ if (bch2_dev_inum_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_read, + rbio->read_pos.inode, + rbio->read_pos.offset, + "data read error: %s", @@ -64695,7 +64900,7 @@ index 000000000..d9c18bb7d +#endif /* _BCACHEFS_IO_READ_H */ diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c new file mode 100644 -index 000000000..6d9c77721 +index 000000000..613f38436 --- /dev/null +++ b/fs/bcachefs/io_write.c @@ -0,0 +1,1664 @@ @@ -65338,7 +65543,7 @@ index 000000000..6d9c77721 + struct bch_fs *c = wbio->c; + struct bch_dev *ca = bch_dev_bkey_exists(c, wbio->dev); + -+ if (bch2_dev_inum_io_err_on(bio->bi_status, ca, ++ if (bch2_dev_inum_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write, + op->pos.inode, + wbio->inode_offset << 9, + "data write error: %s", @@ -68612,10 +68817,10 @@ index 000000000..011711e99 +#endif /* _BCACHEFS_JOURNAL_H */ diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c new file mode 100644 -index 000000000..b29ece313 +index 000000000..658785429 --- /dev/null +++ b/fs/bcachefs/journal_io.c -@@ -0,0 +1,1894 @@ +@@ -0,0 +1,1931 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "bcachefs.h" +#include "alloc_background.h" @@ -68758,7 +68963,8 @@ index 000000000..b29ece313 + if (!dup->csum_good) + goto replace; + -+ fsck_err(c, "found duplicate but non identical journal entries (seq %llu)", ++ fsck_err(c, journal_entry_replicas_data_mismatch, ++ "found duplicate but non identical journal entries (seq %llu)", + le64_to_cpu(j->seq)); + i = dup; + goto found; @@ -68853,7 +69059,7 @@ index 000000000..b29ece313 + prt_str(out, ": "); +} + -+#define journal_entry_err(c, version, jset, entry, msg, ...) \ ++#define journal_entry_err(c, version, jset, entry, _err, msg, ...) \ +({ \ + struct printbuf _buf = PRINTBUF; \ + \ @@ -68862,9 +69068,10 @@ index 000000000..b29ece313 + \ + switch (flags & BKEY_INVALID_WRITE) { \ + case READ: \ -+ mustfix_fsck_err(c, "%s", _buf.buf); \ ++ mustfix_fsck_err(c, _err, "%s", _buf.buf); \ + break; \ + case WRITE: \ ++ bch2_sb_error_count(c, BCH_FSCK_ERR_##_err); \ + bch_err(c, "corrupt metadata before write: %s\n", _buf.buf);\ + if (bch2_fs_inconsistent(c)) { \ + ret = -BCH_ERR_fsck_errors_not_fixed; \ @@ -68877,8 +69084,8 @@ index 000000000..b29ece313 + true; \ +}) + -+#define journal_entry_err_on(cond, c, version, jset, entry, msg, ...) \ -+ ((cond) ? journal_entry_err(c, version, jset, entry, msg, ##__VA_ARGS__) : false) ++#define journal_entry_err_on(cond, ...) \ ++ ((cond) ? journal_entry_err(__VA_ARGS__) : false) + +#define FSCK_DELETED_KEY 5 + @@ -68895,7 +69102,10 @@ index 000000000..b29ece313 + struct printbuf buf = PRINTBUF; + int ret = 0; + -+ if (journal_entry_err_on(!k->k.u64s, c, version, jset, entry, "k->u64s 0")) { ++ if (journal_entry_err_on(!k->k.u64s, ++ c, version, jset, entry, ++ journal_entry_bkey_u64s_0, ++ "k->u64s 0")) { + entry->u64s = cpu_to_le16((u64 *) k - entry->_data); + journal_entry_null_range(vstruct_next(entry), next); + return FSCK_DELETED_KEY; @@ -68904,6 +69114,7 @@ index 000000000..b29ece313 + if (journal_entry_err_on((void *) bkey_next(k) > + (void *) vstruct_next(entry), + c, version, jset, entry, ++ journal_entry_bkey_past_end, + "extends past end of journal entry")) { + entry->u64s = cpu_to_le16((u64 *) k - entry->_data); + journal_entry_null_range(vstruct_next(entry), next); @@ -68912,6 +69123,7 @@ index 000000000..b29ece313 + + if (journal_entry_err_on(k->k.format != KEY_FORMAT_CURRENT, + c, version, jset, entry, ++ journal_entry_bkey_bad_format, + "bad format %u", k->k.format)) { + le16_add_cpu(&entry->u64s, -((u16) k->k.u64s)); + memmove(k, bkey_next(k), next - (void *) bkey_next(k)); @@ -68935,7 +69147,8 @@ index 000000000..b29ece313 + bch2_bkey_invalid(c, bkey_i_to_s_c(k), + __btree_node_type(level, btree_id), write, &buf); + -+ mustfix_fsck_err(c, "%s", buf.buf); ++ mustfix_fsck_err(c, journal_entry_bkey_invalid, ++ "%s", buf.buf); + + le16_add_cpu(&entry->u64s, -((u16) k->k.u64s)); + memmove(k, bkey_next(k), next - (void *) bkey_next(k)); @@ -69005,6 +69218,7 @@ index 000000000..b29ece313 + if (journal_entry_err_on(!entry->u64s || + le16_to_cpu(entry->u64s) != k->k.u64s, + c, version, jset, entry, ++ journal_entry_btree_root_bad_size, + "invalid btree root journal entry: wrong number of keys")) { + void *next = vstruct_next(entry); + /* @@ -69054,6 +69268,7 @@ index 000000000..b29ece313 + + if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 1, + c, version, jset, entry, ++ journal_entry_blacklist_bad_size, + "invalid journal seq blacklist entry: bad size")) { + journal_entry_null_range(entry, vstruct_next(entry)); + } @@ -69081,6 +69296,7 @@ index 000000000..b29ece313 + + if (journal_entry_err_on(le16_to_cpu(entry->u64s) != 2, + c, version, jset, entry, ++ journal_entry_blacklist_v2_bad_size, + "invalid journal seq blacklist entry: bad size")) { + journal_entry_null_range(entry, vstruct_next(entry)); + goto out; @@ -69091,6 +69307,7 @@ index 000000000..b29ece313 + if (journal_entry_err_on(le64_to_cpu(bl_entry->start) > + le64_to_cpu(bl_entry->end), + c, version, jset, entry, ++ journal_entry_blacklist_v2_start_past_end, + "invalid journal seq blacklist entry: start > end")) { + journal_entry_null_range(entry, vstruct_next(entry)); + } @@ -69123,6 +69340,7 @@ index 000000000..b29ece313 + + if (journal_entry_err_on(bytes < sizeof(*u), + c, version, jset, entry, ++ journal_entry_usage_bad_size, + "invalid journal entry usage: bad size")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; @@ -69157,6 +69375,7 @@ index 000000000..b29ece313 + if (journal_entry_err_on(bytes < sizeof(*u) || + bytes < sizeof(*u) + u->r.nr_devs, + c, version, jset, entry, ++ journal_entry_data_usage_bad_size, + "invalid journal entry usage: bad size")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; @@ -69188,13 +69407,17 @@ index 000000000..b29ece313 + int ret = 0; + + if (journal_entry_err_on(bytes != sizeof(*clock), -+ c, version, jset, entry, "bad size")) { ++ c, version, jset, entry, ++ journal_entry_clock_bad_size, ++ "bad size")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; + } + + if (journal_entry_err_on(clock->rw > 1, -+ c, version, jset, entry, "bad rw")) { ++ c, version, jset, entry, ++ journal_entry_clock_bad_rw, ++ "bad rw")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; + } @@ -69226,7 +69449,9 @@ index 000000000..b29ece313 + int ret = 0; + + if (journal_entry_err_on(bytes < expected, -+ c, version, jset, entry, "bad size (%u < %u)", ++ c, version, jset, entry, ++ journal_entry_dev_usage_bad_size, ++ "bad size (%u < %u)", + bytes, expected)) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; @@ -69235,13 +69460,17 @@ index 000000000..b29ece313 + dev = le32_to_cpu(u->dev); + + if (journal_entry_err_on(!bch2_dev_exists2(c, dev), -+ c, version, jset, entry, "bad dev")) { ++ c, version, jset, entry, ++ journal_entry_dev_usage_bad_dev, ++ "bad dev")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; + } + + if (journal_entry_err_on(u->pad, -+ c, version, jset, entry, "bad pad")) { ++ c, version, jset, entry, ++ journal_entry_dev_usage_bad_pad, ++ "bad pad")) { + journal_entry_null_range(entry, vstruct_next(entry)); + return ret; + } @@ -69356,7 +69585,8 @@ index 000000000..b29ece313 + + vstruct_for_each(jset, entry) { + if (journal_entry_err_on(vstruct_next(entry) > vstruct_last(jset), -+ c, version, jset, entry, ++ c, version, jset, entry, ++ journal_entry_past_jset_end, + "journal entry extends past end of jset")) { + jset->u64s = cpu_to_le32((u64 *) entry - jset->_data); + break; @@ -69385,6 +69615,7 @@ index 000000000..b29ece313 + version = le32_to_cpu(jset->version); + if (journal_entry_err_on(!bch2_version_compatible(version), + c, version, jset, NULL, ++ jset_unsupported_version, + "%s sector %llu seq %llu: incompatible journal entry version %u.%u", + ca ? ca->name : c->name, + sector, le64_to_cpu(jset->seq), @@ -69395,7 +69626,8 @@ index 000000000..b29ece313 + } + + if (journal_entry_err_on(!bch2_checksum_type_valid(c, JSET_CSUM_TYPE(jset)), -+ c, version, jset, NULL, ++ c, version, jset, NULL, ++ jset_unknown_csum, + "%s sector %llu seq %llu: journal entry with unknown csum type %llu", + ca ? ca->name : c->name, + sector, le64_to_cpu(jset->seq), @@ -69406,6 +69638,7 @@ index 000000000..b29ece313 + if (journal_entry_err_on(!JSET_NO_FLUSH(jset) && + le64_to_cpu(jset->last_seq) > le64_to_cpu(jset->seq), + c, version, jset, NULL, ++ jset_last_seq_newer_than_seq, + "invalid journal entry: last_seq > seq (%llu > %llu)", + le64_to_cpu(jset->last_seq), + le64_to_cpu(jset->seq))) { @@ -69434,7 +69667,8 @@ index 000000000..b29ece313 + + version = le32_to_cpu(jset->version); + if (journal_entry_err_on(!bch2_version_compatible(version), -+ c, version, jset, NULL, ++ c, version, jset, NULL, ++ jset_unsupported_version, + "%s sector %llu seq %llu: unknown journal entry version %u.%u", + ca ? ca->name : c->name, + sector, le64_to_cpu(jset->seq), @@ -69449,7 +69683,8 @@ index 000000000..b29ece313 + return JOURNAL_ENTRY_REREAD; + + if (journal_entry_err_on(bytes > bucket_sectors_left << 9, -+ c, version, jset, NULL, ++ c, version, jset, NULL, ++ jset_past_bucket_end, + "%s sector %llu seq %llu: journal entry too big (%zu bytes)", + ca ? ca->name : c->name, + sector, le64_to_cpu(jset->seq), bytes)) @@ -69518,7 +69753,7 @@ index 000000000..b29ece313 + ret = submit_bio_wait(bio); + kfree(bio); + -+ if (bch2_dev_io_err_on(ret, ca, ++ if (bch2_dev_io_err_on(ret, ca, BCH_MEMBER_ERROR_read, + "journal read error: sector %llu", + offset) || + bch2_meta_read_fault("journal")) { @@ -69574,7 +69809,8 @@ index 000000000..b29ece313 + ja->bucket_seq[bucket] = le64_to_cpu(j->seq); + + csum_good = jset_csum_good(c, j); -+ if (!csum_good) ++ if (bch2_dev_io_err_on(!csum_good, ca, BCH_MEMBER_ERROR_checksum, ++ "journal checksum error")) + saw_bad = true; + + ret = bch2_encrypt(c, JSET_CSUM_TYPE(j), journal_nonce(j), @@ -69790,6 +70026,7 @@ index 000000000..b29ece313 + + if (journal_entry_err_on(le64_to_cpu(i->j.last_seq) > le64_to_cpu(i->j.seq), + c, le32_to_cpu(i->j.version), &i->j, NULL, ++ jset_last_seq_newer_than_seq, + "invalid journal entry: last_seq > seq (%llu > %llu)", + le64_to_cpu(i->j.last_seq), + le64_to_cpu(i->j.seq))) @@ -69806,7 +70043,8 @@ index 000000000..b29ece313 + } + + if (!*last_seq) { -+ fsck_err(c, "journal read done, but no entries found after dropping non-flushes"); ++ fsck_err(c, dirty_but_no_journal_entries_post_drop_nonflushes, ++ "journal read done, but no entries found after dropping non-flushes"); + return 0; + } + @@ -69832,6 +70070,7 @@ index 000000000..b29ece313 + + if (bch2_journal_seq_is_blacklisted(c, seq, true)) { + fsck_err_on(!JSET_NO_FLUSH(&i->j), c, ++ jset_seq_blacklisted, + "found blacklisted journal entry %llu", seq); + i->ignore = true; + } @@ -69872,7 +70111,8 @@ index 000000000..b29ece313 + bch2_journal_ptrs_to_text(&buf2, c, i); + + missing_end = seq - 1; -+ fsck_err(c, "journal entries %llu-%llu missing! (replaying %llu-%llu)\n" ++ fsck_err(c, journal_entries_missing, ++ "journal entries %llu-%llu missing! (replaying %llu-%llu)\n" + " prev at %s\n" + " next at %s", + missing_start, missing_end, @@ -69927,7 +70167,8 @@ index 000000000..b29ece313 + if (!degraded && + !bch2_replicas_marked(c, &replicas.e) && + (le64_to_cpu(i->j.seq) == *last_seq || -+ fsck_err(c, "superblock not marked as containing replicas for journal entry %llu\n %s", ++ fsck_err(c, journal_entry_replicas_not_marked, ++ "superblock not marked as containing replicas for journal entry %llu\n %s", + le64_to_cpu(i->j.seq), buf.buf))) { + ret = bch2_mark_replicas(c, &replicas.e); + if (ret) @@ -70199,7 +70440,8 @@ index 000000000..b29ece313 + struct journal_buf *w = journal_last_unwritten_buf(j); + unsigned long flags; + -+ if (bch2_dev_io_err_on(bio->bi_status, ca, "error writing journal entry %llu: %s", ++ if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write, ++ "error writing journal entry %llu: %s", + le64_to_cpu(w->data->seq), + bch2_blk_status_to_str(bio->bi_status)) || + bch2_meta_write_fault("journal")) { @@ -72822,10 +73064,10 @@ index 000000000..4d1e786a2 +#endif /* _BCACHEFS_LOGGED_OPS_H */ diff --git a/fs/bcachefs/lru.c b/fs/bcachefs/lru.c new file mode 100644 -index 000000000..215a65332 +index 000000000..a5cc0ed19 --- /dev/null +++ b/fs/bcachefs/lru.c -@@ -0,0 +1,162 @@ +@@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -72838,17 +73080,17 @@ index 000000000..215a65332 +#include "recovery.h" + +/* KEY_TYPE_lru is obsolete: */ -+int bch2_lru_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_lru_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (!lru_pos_time(k.k->p)) { -+ prt_printf(err, "lru entry at time=0"); -+ return -BCH_ERR_invalid_bkey; -+ -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(!lru_pos_time(k.k->p), c, err, ++ lru_entry_at_time_0, ++ "lru entry at time=0"); ++fsck_err: ++ return ret; +} + +void bch2_lru_to_text(struct printbuf *out, struct bch_fs *c, @@ -72923,6 +73165,7 @@ index 000000000..215a65332 + int ret; + + if (fsck_err_on(!bch2_dev_bucket_exists(c, alloc_pos), c, ++ lru_entry_to_invalid_bucket, + "lru key points to nonexistent device:bucket %llu:%llu", + alloc_pos.inode, alloc_pos.offset)) + return bch2_btree_delete_at(trans, lru_iter, 0); @@ -72953,7 +73196,8 @@ index 000000000..215a65332 + } + + if (c->opts.reconstruct_alloc || -+ fsck_err(c, "incorrect lru entry: lru %s time %llu\n" ++ fsck_err(c, lru_entry_bad, ++ "incorrect lru entry: lru %s time %llu\n" + " %s\n" + " for %s", + bch2_lru_types[type], @@ -72990,7 +73234,7 @@ index 000000000..215a65332 +} diff --git a/fs/bcachefs/lru.h b/fs/bcachefs/lru.h new file mode 100644 -index 000000000..be66bf9ad +index 000000000..429dca816 --- /dev/null +++ b/fs/bcachefs/lru.h @@ -0,0 +1,69 @@ @@ -73044,7 +73288,7 @@ index 000000000..be66bf9ad + return BCH_LRU_read; +} + -+int bch2_lru_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_lru_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_lru_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -75317,10 +75561,10 @@ index 000000000..bd12bf677 + diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c new file mode 100644 -index 000000000..b7722b623 +index 000000000..4ad588066 --- /dev/null +++ b/fs/bcachefs/opts.c -@@ -0,0 +1,607 @@ +@@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -75335,11 +75579,6 @@ index 000000000..b7722b623 + +#define x(t, n, ...) [n] = #t, + -+const char * const bch2_iops_measurements[] = { -+ BCH_IOPS_MEASUREMENTS() -+ NULL -+}; -+ +const char * const bch2_error_actions[] = { + BCH_ERROR_ACTIONS() + NULL @@ -75930,10 +76169,10 @@ index 000000000..b7722b623 +} diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h new file mode 100644 -index 000000000..2307cdd2a +index 000000000..8526f1774 --- /dev/null +++ b/fs/bcachefs/opts.h -@@ -0,0 +1,565 @@ +@@ -0,0 +1,564 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_OPTS_H +#define _BCACHEFS_OPTS_H @@ -75946,7 +76185,6 @@ index 000000000..2307cdd2a + +struct bch_fs; + -+extern const char * const bch2_iops_measurements[]; +extern const char * const bch2_error_actions[]; +extern const char * const bch2_fsck_fix_opts[]; +extern const char * const bch2_version_upgrade_opts[]; @@ -77222,10 +77460,10 @@ index 000000000..2191423d9 +#endif /* _BCACHEFS_PRINTBUF_H */ diff --git a/fs/bcachefs/quota.c b/fs/bcachefs/quota.c new file mode 100644 -index 000000000..cb68ae44d +index 000000000..a54647c36 --- /dev/null +++ b/fs/bcachefs/quota.c -@@ -0,0 +1,978 @@ +@@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "bcachefs.h" +#include "btree_update.h" @@ -77287,17 +77525,18 @@ index 000000000..cb68ae44d + .to_text = bch2_sb_quota_to_text, +}; + -+int bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_quota_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (k.k->p.inode >= QTYP_NR) { -+ prt_printf(err, "invalid quota type (%llu >= %u)", -+ k.k->p.inode, QTYP_NR); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(k.k->p.inode >= QTYP_NR, c, err, ++ quota_type_invalid, ++ "invalid quota type (%llu >= %u)", ++ k.k->p.inode, QTYP_NR); ++fsck_err: ++ return ret; +} + +void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, @@ -78206,7 +78445,7 @@ index 000000000..cb68ae44d +#endif /* CONFIG_BCACHEFS_QUOTA */ diff --git a/fs/bcachefs/quota.h b/fs/bcachefs/quota.h new file mode 100644 -index 000000000..2f463874a +index 000000000..884f601f4 --- /dev/null +++ b/fs/bcachefs/quota.h @@ -0,0 +1,74 @@ @@ -78220,7 +78459,7 @@ index 000000000..2f463874a +enum bkey_invalid_flags; +extern const struct bch_sb_field_ops bch_sb_field_ops_quota; + -+int bch2_quota_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_quota_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -78335,10 +78574,10 @@ index 000000000..6a136083d +#endif /* _BCACHEFS_QUOTA_TYPES_H */ diff --git a/fs/bcachefs/rebalance.c b/fs/bcachefs/rebalance.c new file mode 100644 -index 000000000..6ba8574b4 +index 000000000..6ee4d2e02 --- /dev/null +++ b/fs/bcachefs/rebalance.c -@@ -0,0 +1,465 @@ +@@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -78695,7 +78934,8 @@ index 000000000..6ba8574b4 + rebalance_wait(c); + } + -+ bch_err_fn(c, ret); ++ if (!bch2_err_matches(ret, EROFS)) ++ bch_err_fn(c, ret); + return ret; +} + @@ -78882,10 +79122,10 @@ index 000000000..0fffb536c +#endif /* _BCACHEFS_REBALANCE_TYPES_H */ diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c new file mode 100644 -index 000000000..02025099c +index 000000000..f73338f37 --- /dev/null +++ b/fs/bcachefs/recovery.c -@@ -0,0 +1,1044 @@ +@@ -0,0 +1,1050 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -79253,8 +79493,10 @@ index 000000000..02025099c + } + + if (r->error) { -+ __fsck_err(c, btree_id_is_alloc(i) ++ __fsck_err(c, ++ btree_id_is_alloc(i) + ? FSCK_CAN_IGNORE : 0, ++ btree_root_bkey_invalid, + "invalid btree root %s", + bch2_btree_id_str(i)); + if (i == BTREE_ID_alloc) @@ -79264,6 +79506,7 @@ index 000000000..02025099c + ret = bch2_btree_root_read(c, i, &r->key, r->level); + if (ret) { + fsck_err(c, ++ btree_root_read_error, + "error reading btree root %s", + bch2_btree_id_str(i)); + if (btree_id_is_alloc(i)) @@ -79602,6 +79845,7 @@ index 000000000..02025099c + if (mustfix_fsck_err_on(c->sb.clean && + last_journal_entry && + !journal_entry_empty(last_journal_entry), c, ++ clean_but_journal_not_empty, + "filesystem marked clean but journal not empty")) { + c->sb.compat &= ~(1ULL << BCH_COMPAT_alloc_info); + SET_BCH_SB_CLEAN(c->disk_sb.sb, false); @@ -79609,7 +79853,9 @@ index 000000000..02025099c + } + + if (!last_journal_entry) { -+ fsck_err_on(!c->sb.clean, c, "no journal entries found"); ++ fsck_err_on(!c->sb.clean, c, ++ dirty_but_no_journal_entries, ++ "no journal entries found"); + if (clean) + goto use_clean; + @@ -80030,7 +80276,7 @@ index 000000000..515e3d62c +#endif /* _BCACHEFS_RECOVERY_TYPES_H */ diff --git a/fs/bcachefs/reflink.c b/fs/bcachefs/reflink.c new file mode 100644 -index 000000000..dbbdf1955 +index 000000000..eb31df605 --- /dev/null +++ b/fs/bcachefs/reflink.c @@ -0,0 +1,406 @@ @@ -80064,7 +80310,7 @@ index 000000000..dbbdf1955 + +/* reflink pointers */ + -+int bch2_reflink_p_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_reflink_p_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ @@ -80111,7 +80357,7 @@ index 000000000..dbbdf1955 + +/* indirect extents */ + -+int bch2_reflink_v_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_reflink_v_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ @@ -80162,7 +80408,7 @@ index 000000000..dbbdf1955 + +/* indirect inline data */ + -+int bch2_indirect_inline_data_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_indirect_inline_data_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ @@ -80442,7 +80688,7 @@ index 000000000..dbbdf1955 +} diff --git a/fs/bcachefs/reflink.h b/fs/bcachefs/reflink.h new file mode 100644 -index 000000000..fe52538ef +index 000000000..8ccf3f9c4 --- /dev/null +++ b/fs/bcachefs/reflink.h @@ -0,0 +1,81 @@ @@ -80452,7 +80698,7 @@ index 000000000..fe52538ef + +enum bkey_invalid_flags; + -+int bch2_reflink_p_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_reflink_p_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_reflink_p_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); @@ -80467,7 +80713,7 @@ index 000000000..fe52538ef + .min_val_size = 16, \ +}) + -+int bch2_reflink_v_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_reflink_v_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_reflink_v_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); @@ -80483,7 +80729,7 @@ index 000000000..fe52538ef + .min_val_size = 8, \ +}) + -+int bch2_indirect_inline_data_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_indirect_inline_data_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_indirect_inline_data_to_text(struct printbuf *, + struct bch_fs *, struct bkey_s_c); @@ -81723,10 +81969,10 @@ index 000000000..5cfff489b +#endif /* _BCACHEFS_REPLICAS_TYPES_H */ diff --git a/fs/bcachefs/sb-clean.c b/fs/bcachefs/sb-clean.c new file mode 100644 -index 000000000..61203d7c8 +index 000000000..9b6cc86d2 --- /dev/null +++ b/fs/bcachefs/sb-clean.c -@@ -0,0 +1,395 @@ +@@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -81811,6 +82057,7 @@ index 000000000..61203d7c8 + int ret = 0; + + if (mustfix_fsck_err_on(j->seq != clean->journal_seq, c, ++ sb_clean_journal_seq_mismatch, + "superblock journal seq (%llu) doesn't match journal (%llu) after clean shutdown", + le64_to_cpu(clean->journal_seq), + le64_to_cpu(j->seq))) { @@ -81848,6 +82095,7 @@ index 000000000..61203d7c8 + k1->k.u64s != k2->k.u64s || + memcmp(k1, k2, bkey_bytes(&k1->k)) || + l1 != l2, c, ++ sb_clean_btree_root_mismatch, + "superblock btree root %u doesn't match journal after clean shutdown\n" + "sb: l=%u %s\n" + "journal: l=%u %s\n", i, @@ -81869,6 +82117,7 @@ index 000000000..61203d7c8 + sb_clean = bch2_sb_field_get(c->disk_sb.sb, clean); + + if (fsck_err_on(!sb_clean, c, ++ sb_clean_missing, + "superblock marked clean but clean section not present")) { + SET_BCH_SB_CLEAN(c->disk_sb.sb, false); + c->sb.clean = false; @@ -82144,12 +82393,491 @@ index 000000000..71caef281 +void bch2_fs_mark_clean(struct bch_fs *); + +#endif /* _BCACHEFS_SB_CLEAN_H */ +diff --git a/fs/bcachefs/sb-errors.c b/fs/bcachefs/sb-errors.c +new file mode 100644 +index 000000000..3d66f15ae +--- /dev/null ++++ b/fs/bcachefs/sb-errors.c +@@ -0,0 +1,175 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include "bcachefs.h" ++#include "sb-errors.h" ++#include "super-io.h" ++ ++static const char * const bch2_sb_error_strs[] = { ++#define x(t, n, ...) [n] = #t, ++ BCH_SB_ERRS() ++ NULL ++}; ++ ++static void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id) ++{ ++ if (id < BCH_SB_ERR_MAX) ++ prt_str(out, bch2_sb_error_strs[id]); ++ else ++ prt_printf(out, "(unknown error %u)", id); ++} ++ ++static inline unsigned bch2_sb_field_errors_nr_entries(struct bch_sb_field_errors *e) ++{ ++ return e ++ ? (bch2_sb_field_bytes(&e->field) - sizeof(*e)) / sizeof(e->entries[0]) ++ : 0; ++} ++ ++static inline unsigned bch2_sb_field_errors_u64s(unsigned nr) ++{ ++ return (sizeof(struct bch_sb_field_errors) + ++ sizeof(struct bch_sb_field_error_entry) * nr) / sizeof(u64); ++} ++ ++static int bch2_sb_errors_validate(struct bch_sb *sb, struct bch_sb_field *f, ++ struct printbuf *err) ++{ ++ struct bch_sb_field_errors *e = field_to_type(f, errors); ++ unsigned i, nr = bch2_sb_field_errors_nr_entries(e); ++ ++ for (i = 0; i < nr; i++) { ++ if (!BCH_SB_ERROR_ENTRY_NR(&e->entries[i])) { ++ prt_printf(err, "entry with count 0 (id "); ++ bch2_sb_error_id_to_text(err, BCH_SB_ERROR_ENTRY_ID(&e->entries[i])); ++ prt_printf(err, ")"); ++ return -BCH_ERR_invalid_sb_errors; ++ } ++ ++ if (i + 1 < nr && ++ BCH_SB_ERROR_ENTRY_ID(&e->entries[i]) >= ++ BCH_SB_ERROR_ENTRY_ID(&e->entries[i + 1])) { ++ prt_printf(err, "entries out of order"); ++ return -BCH_ERR_invalid_sb_errors; ++ } ++ } ++ ++ return 0; ++} ++ ++static void bch2_sb_errors_to_text(struct printbuf *out, struct bch_sb *sb, ++ struct bch_sb_field *f) ++{ ++ struct bch_sb_field_errors *e = field_to_type(f, errors); ++ unsigned i, nr = bch2_sb_field_errors_nr_entries(e); ++ u64 now = ktime_get_real_seconds(); ++ ++ if (out->nr_tabstops <= 1) ++ printbuf_tabstop_push(out, 16); ++ ++ for (i = 0; i < nr; i++) { ++ bch2_sb_error_id_to_text(out, BCH_SB_ERROR_ENTRY_ID(&e->entries[i])); ++ prt_tab(out); ++ prt_u64(out, BCH_SB_ERROR_ENTRY_NR(&e->entries[i])); ++ prt_tab(out); ++ bch2_pr_time_units(out, (now - le64_to_cpu(e->entries[i].last_error_time)) * ++ NSEC_PER_SEC); ++ prt_str(out, " ago"); ++ prt_newline(out); ++ } ++} ++ ++const struct bch_sb_field_ops bch_sb_field_ops_errors = { ++ .validate = bch2_sb_errors_validate, ++ .to_text = bch2_sb_errors_to_text, ++}; ++ ++void bch2_sb_error_count(struct bch_fs *c, enum bch_sb_error_id err) ++{ ++ bch_sb_errors_cpu *e = &c->fsck_error_counts; ++ struct bch_sb_error_entry_cpu n = { ++ .id = err, ++ .nr = 1, ++ .last_error_time = ktime_get_real_seconds() ++ }; ++ unsigned i; ++ ++ mutex_lock(&c->fsck_error_counts_lock); ++ for (i = 0; i < e->nr; i++) { ++ if (err == e->data[i].id) { ++ e->data[i].nr++; ++ e->data[i].last_error_time = n.last_error_time; ++ goto out; ++ } ++ if (err < e->data[i].id) ++ break; ++ } ++ ++ if (darray_make_room(e, 1)) ++ goto out; ++ ++ darray_insert_item(e, i, n); ++out: ++ mutex_unlock(&c->fsck_error_counts_lock); ++} ++ ++void bch2_sb_errors_from_cpu(struct bch_fs *c) ++{ ++ bch_sb_errors_cpu *src = &c->fsck_error_counts; ++ struct bch_sb_field_errors *dst = ++ bch2_sb_field_resize(&c->disk_sb, errors, ++ bch2_sb_field_errors_u64s(src->nr)); ++ unsigned i; ++ ++ if (!dst) ++ return; ++ ++ for (i = 0; i < src->nr; i++) { ++ SET_BCH_SB_ERROR_ENTRY_ID(&dst->entries[i], src->data[i].id); ++ SET_BCH_SB_ERROR_ENTRY_NR(&dst->entries[i], src->data[i].nr); ++ dst->entries[i].last_error_time = cpu_to_le64(src->data[i].last_error_time); ++ } ++} ++ ++static int bch2_sb_errors_to_cpu(struct bch_fs *c) ++{ ++ struct bch_sb_field_errors *src = bch2_sb_field_get(c->disk_sb.sb, errors); ++ bch_sb_errors_cpu *dst = &c->fsck_error_counts; ++ unsigned i, nr = bch2_sb_field_errors_nr_entries(src); ++ int ret; ++ ++ if (!nr) ++ return 0; ++ ++ mutex_lock(&c->fsck_error_counts_lock); ++ ret = darray_make_room(dst, nr); ++ if (ret) ++ goto err; ++ ++ dst->nr = nr; ++ ++ for (i = 0; i < nr; i++) { ++ dst->data[i].id = BCH_SB_ERROR_ENTRY_ID(&src->entries[i]); ++ dst->data[i].nr = BCH_SB_ERROR_ENTRY_NR(&src->entries[i]); ++ dst->data[i].last_error_time = le64_to_cpu(src->entries[i].last_error_time); ++ } ++err: ++ mutex_unlock(&c->fsck_error_counts_lock); ++ ++ return ret; ++} ++ ++void bch2_fs_sb_errors_exit(struct bch_fs *c) ++{ ++ darray_exit(&c->fsck_error_counts); ++} ++ ++void bch2_fs_sb_errors_init_early(struct bch_fs *c) ++{ ++ mutex_init(&c->fsck_error_counts_lock); ++ darray_init(&c->fsck_error_counts); ++} ++ ++int bch2_fs_sb_errors_init(struct bch_fs *c) ++{ ++ return bch2_sb_errors_to_cpu(c); ++} +diff --git a/fs/bcachefs/sb-errors.h b/fs/bcachefs/sb-errors.h +new file mode 100644 +index 000000000..5a09a5396 +--- /dev/null ++++ b/fs/bcachefs/sb-errors.h +@@ -0,0 +1,270 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _BCACHEFS_SB_ERRORS_H ++#define _BCACHEFS_SB_ERRORS_H ++ ++#include "sb-errors_types.h" ++ ++#define BCH_SB_ERRS() \ ++ x(clean_but_journal_not_empty, 0) \ ++ x(dirty_but_no_journal_entries, 1) \ ++ x(dirty_but_no_journal_entries_post_drop_nonflushes, 2) \ ++ x(sb_clean_journal_seq_mismatch, 3) \ ++ x(sb_clean_btree_root_mismatch, 4) \ ++ x(sb_clean_missing, 5) \ ++ x(jset_unsupported_version, 6) \ ++ x(jset_unknown_csum, 7) \ ++ x(jset_last_seq_newer_than_seq, 8) \ ++ x(jset_past_bucket_end, 9) \ ++ x(jset_seq_blacklisted, 10) \ ++ x(journal_entries_missing, 11) \ ++ x(journal_entry_replicas_not_marked, 12) \ ++ x(journal_entry_past_jset_end, 13) \ ++ x(journal_entry_replicas_data_mismatch, 14) \ ++ x(journal_entry_bkey_u64s_0, 15) \ ++ x(journal_entry_bkey_past_end, 16) \ ++ x(journal_entry_bkey_bad_format, 17) \ ++ x(journal_entry_bkey_invalid, 18) \ ++ x(journal_entry_btree_root_bad_size, 19) \ ++ x(journal_entry_blacklist_bad_size, 20) \ ++ x(journal_entry_blacklist_v2_bad_size, 21) \ ++ x(journal_entry_blacklist_v2_start_past_end, 22) \ ++ x(journal_entry_usage_bad_size, 23) \ ++ x(journal_entry_data_usage_bad_size, 24) \ ++ x(journal_entry_clock_bad_size, 25) \ ++ x(journal_entry_clock_bad_rw, 26) \ ++ x(journal_entry_dev_usage_bad_size, 27) \ ++ x(journal_entry_dev_usage_bad_dev, 28) \ ++ x(journal_entry_dev_usage_bad_pad, 29) \ ++ x(btree_node_unreadable, 30) \ ++ x(btree_node_fault_injected, 31) \ ++ x(btree_node_bad_magic, 32) \ ++ x(btree_node_bad_seq, 33) \ ++ x(btree_node_unsupported_version, 34) \ ++ x(btree_node_bset_older_than_sb_min, 35) \ ++ x(btree_node_bset_newer_than_sb, 36) \ ++ x(btree_node_data_missing, 37) \ ++ x(btree_node_bset_after_end, 38) \ ++ x(btree_node_replicas_sectors_written_mismatch, 39) \ ++ x(btree_node_replicas_data_mismatch, 40) \ ++ x(bset_unknown_csum, 41) \ ++ x(bset_bad_csum, 42) \ ++ x(bset_past_end_of_btree_node, 43) \ ++ x(bset_wrong_sector_offset, 44) \ ++ x(bset_empty, 45) \ ++ x(bset_bad_seq, 46) \ ++ x(bset_blacklisted_journal_seq, 47) \ ++ x(first_bset_blacklisted_journal_seq, 48) \ ++ x(btree_node_bad_btree, 49) \ ++ x(btree_node_bad_level, 50) \ ++ x(btree_node_bad_min_key, 51) \ ++ x(btree_node_bad_max_key, 52) \ ++ x(btree_node_bad_format, 53) \ ++ x(btree_node_bkey_past_bset_end, 54) \ ++ x(btree_node_bkey_bad_format, 55) \ ++ x(btree_node_bad_bkey, 56) \ ++ x(btree_node_bkey_out_of_order, 57) \ ++ x(btree_root_bkey_invalid, 58) \ ++ x(btree_root_read_error, 59) \ ++ x(btree_root_bad_min_key, 50) \ ++ x(btree_root_bad_max_key, 61) \ ++ x(btree_node_read_error, 62) \ ++ x(btree_node_topology_bad_min_key, 63) \ ++ x(btree_node_topology_bad_max_key, 64) \ ++ x(btree_node_topology_overwritten_by_prev_node, 65) \ ++ x(btree_node_topology_overwritten_by_next_node, 66) \ ++ x(btree_node_topology_interior_node_empty, 67) \ ++ x(fs_usage_hidden_wrong, 68) \ ++ x(fs_usage_btree_wrong, 69) \ ++ x(fs_usage_data_wrong, 70) \ ++ x(fs_usage_cached_wrong, 71) \ ++ x(fs_usage_reserved_wrong, 72) \ ++ x(fs_usage_persistent_reserved_wrong, 73) \ ++ x(fs_usage_nr_inodes_wrong, 74) \ ++ x(fs_usage_replicas_wrong, 75) \ ++ x(dev_usage_buckets_wrong, 76) \ ++ x(dev_usage_sectors_wrong, 77) \ ++ x(dev_usage_fragmented_wrong, 78) \ ++ x(dev_usage_buckets_ec_wrong, 79) \ ++ x(bkey_version_in_future, 80) \ ++ x(bkey_u64s_too_small, 81) \ ++ x(bkey_invalid_type_for_btree, 82) \ ++ x(bkey_extent_size_zero, 83) \ ++ x(bkey_extent_size_greater_than_offset, 84) \ ++ x(bkey_size_nonzero, 85) \ ++ x(bkey_snapshot_nonzero, 86) \ ++ x(bkey_snapshot_zero, 87) \ ++ x(bkey_at_pos_max, 88) \ ++ x(bkey_before_start_of_btree_node, 89) \ ++ x(bkey_after_end_of_btree_node, 90) \ ++ x(bkey_val_size_nonzero, 91) \ ++ x(bkey_val_size_too_small, 92) \ ++ x(alloc_v1_val_size_bad, 93) \ ++ x(alloc_v2_unpack_error, 94) \ ++ x(alloc_v3_unpack_error, 95) \ ++ x(alloc_v4_val_size_bad, 96) \ ++ x(alloc_v4_backpointers_start_bad, 97) \ ++ x(alloc_key_data_type_bad, 98) \ ++ x(alloc_key_empty_but_have_data, 99) \ ++ x(alloc_key_dirty_sectors_0, 100) \ ++ x(alloc_key_data_type_inconsistency, 101) \ ++ x(alloc_key_to_missing_dev_bucket, 102) \ ++ x(alloc_key_cached_inconsistency, 103) \ ++ x(alloc_key_cached_but_read_time_zero, 104) \ ++ x(alloc_key_to_missing_lru_entry, 105) \ ++ x(alloc_key_data_type_wrong, 106) \ ++ x(alloc_key_gen_wrong, 107) \ ++ x(alloc_key_dirty_sectors_wrong, 108) \ ++ x(alloc_key_cached_sectors_wrong, 109) \ ++ x(alloc_key_stripe_wrong, 110) \ ++ x(alloc_key_stripe_redundancy_wrong, 111) \ ++ x(bucket_sector_count_overflow, 112) \ ++ x(bucket_metadata_type_mismatch, 113) \ ++ x(need_discard_key_wrong, 114) \ ++ x(freespace_key_wrong, 115) \ ++ x(freespace_hole_missing, 116) \ ++ x(bucket_gens_val_size_bad, 117) \ ++ x(bucket_gens_key_wrong, 118) \ ++ x(bucket_gens_hole_wrong, 119) \ ++ x(bucket_gens_to_invalid_dev, 120) \ ++ x(bucket_gens_to_invalid_buckets, 121) \ ++ x(bucket_gens_nonzero_for_invalid_buckets, 122) \ ++ x(need_discard_freespace_key_to_invalid_dev_bucket, 123) \ ++ x(need_discard_freespace_key_bad, 124) \ ++ x(backpointer_pos_wrong, 125) \ ++ x(backpointer_to_missing_device, 126) \ ++ x(backpointer_to_missing_alloc, 127) \ ++ x(backpointer_to_missing_ptr, 128) \ ++ x(lru_entry_at_time_0, 129) \ ++ x(lru_entry_to_invalid_bucket, 130) \ ++ x(lru_entry_bad, 131) \ ++ x(btree_ptr_val_too_big, 132) \ ++ x(btree_ptr_v2_val_too_big, 133) \ ++ x(btree_ptr_has_non_ptr, 134) \ ++ x(extent_ptrs_invalid_entry, 135) \ ++ x(extent_ptrs_no_ptrs, 136) \ ++ x(extent_ptrs_too_many_ptrs, 137) \ ++ x(extent_ptrs_redundant_crc, 138) \ ++ x(extent_ptrs_redundant_stripe, 139) \ ++ x(extent_ptrs_unwritten, 140) \ ++ x(extent_ptrs_written_and_unwritten, 141) \ ++ x(ptr_to_invalid_device, 142) \ ++ x(ptr_to_duplicate_device, 143) \ ++ x(ptr_after_last_bucket, 144) \ ++ x(ptr_before_first_bucket, 145) \ ++ x(ptr_spans_multiple_buckets, 146) \ ++ x(ptr_to_missing_backpointer, 147) \ ++ x(ptr_to_missing_alloc_key, 148) \ ++ x(ptr_to_missing_replicas_entry, 149) \ ++ x(ptr_to_missing_stripe, 150) \ ++ x(ptr_to_incorrect_stripe, 151) \ ++ x(ptr_gen_newer_than_bucket_gen, 152) \ ++ x(ptr_too_stale, 153) \ ++ x(stale_dirty_ptr, 154) \ ++ x(ptr_bucket_data_type_mismatch, 155) \ ++ x(ptr_cached_and_erasure_coded, 156) \ ++ x(ptr_crc_uncompressed_size_too_small, 157) \ ++ x(ptr_crc_csum_type_unknown, 158) \ ++ x(ptr_crc_compression_type_unknown, 159) \ ++ x(ptr_crc_redundant, 160) \ ++ x(ptr_crc_uncompressed_size_too_big, 161) \ ++ x(ptr_crc_nonce_mismatch, 162) \ ++ x(ptr_stripe_redundant, 163) \ ++ x(reservation_key_nr_replicas_invalid, 164) \ ++ x(reflink_v_refcount_wrong, 165) \ ++ x(reflink_p_to_missing_reflink_v, 166) \ ++ x(stripe_pos_bad, 167) \ ++ x(stripe_val_size_bad, 168) \ ++ x(stripe_sector_count_wrong, 169) \ ++ x(snapshot_tree_pos_bad, 170) \ ++ x(snapshot_tree_to_missing_snapshot, 171) \ ++ x(snapshot_tree_to_missing_subvol, 172) \ ++ x(snapshot_tree_to_wrong_subvol, 173) \ ++ x(snapshot_tree_to_snapshot_subvol, 174) \ ++ x(snapshot_pos_bad, 175) \ ++ x(snapshot_parent_bad, 176) \ ++ x(snapshot_children_not_normalized, 177) \ ++ x(snapshot_child_duplicate, 178) \ ++ x(snapshot_child_bad, 179) \ ++ x(snapshot_skiplist_not_normalized, 180) \ ++ x(snapshot_skiplist_bad, 181) \ ++ x(snapshot_should_not_have_subvol, 182) \ ++ x(snapshot_to_bad_snapshot_tree, 183) \ ++ x(snapshot_bad_depth, 184) \ ++ x(snapshot_bad_skiplist, 185) \ ++ x(subvol_pos_bad, 186) \ ++ x(subvol_not_master_and_not_snapshot, 187) \ ++ x(subvol_to_missing_root, 188) \ ++ x(subvol_root_wrong_bi_subvol, 189) \ ++ x(bkey_in_missing_snapshot, 190) \ ++ x(inode_pos_inode_nonzero, 191) \ ++ x(inode_pos_blockdev_range, 192) \ ++ x(inode_unpack_error, 193) \ ++ x(inode_str_hash_invalid, 194) \ ++ x(inode_v3_fields_start_bad, 195) \ ++ x(inode_snapshot_mismatch, 196) \ ++ x(inode_unlinked_but_clean, 197) \ ++ x(inode_unlinked_but_nlink_nonzero, 198) \ ++ x(inode_checksum_type_invalid, 199) \ ++ x(inode_compression_type_invalid, 200) \ ++ x(inode_subvol_root_but_not_dir, 201) \ ++ x(inode_i_size_dirty_but_clean, 202) \ ++ x(inode_i_sectors_dirty_but_clean, 203) \ ++ x(inode_i_sectors_wrong, 204) \ ++ x(inode_dir_wrong_nlink, 205) \ ++ x(inode_dir_multiple_links, 206) \ ++ x(inode_multiple_links_but_nlink_0, 207) \ ++ x(inode_wrong_backpointer, 208) \ ++ x(inode_wrong_nlink, 209) \ ++ x(inode_unreachable, 210) \ ++ x(deleted_inode_but_clean, 211) \ ++ x(deleted_inode_missing, 212) \ ++ x(deleted_inode_is_dir, 213) \ ++ x(deleted_inode_not_unlinked, 214) \ ++ x(extent_overlapping, 215) \ ++ x(extent_in_missing_inode, 216) \ ++ x(extent_in_non_reg_inode, 217) \ ++ x(extent_past_end_of_inode, 218) \ ++ x(dirent_empty_name, 219) \ ++ x(dirent_val_too_big, 220) \ ++ x(dirent_name_too_long, 221) \ ++ x(dirent_name_embedded_nul, 222) \ ++ x(dirent_name_dot_or_dotdot, 223) \ ++ x(dirent_name_has_slash, 224) \ ++ x(dirent_d_type_wrong, 225) \ ++ x(dirent_d_parent_subvol_wrong, 226) \ ++ x(dirent_in_missing_dir_inode, 227) \ ++ x(dirent_in_non_dir_inode, 228) \ ++ x(dirent_to_missing_inode, 229) \ ++ x(dirent_to_missing_subvol, 230) \ ++ x(dirent_to_itself, 231) \ ++ x(quota_type_invalid, 232) \ ++ x(xattr_val_size_too_small, 233) \ ++ x(xattr_val_size_too_big, 234) \ ++ x(xattr_invalid_type, 235) \ ++ x(xattr_name_invalid_chars, 236) \ ++ x(xattr_in_missing_inode, 237) \ ++ x(root_subvol_missing, 238) \ ++ x(root_dir_missing, 239) \ ++ x(root_inode_not_dir, 240) \ ++ x(dir_loop, 241) \ ++ x(hash_table_key_duplicate, 242) \ ++ x(hash_table_key_wrong_offset, 243) ++ ++enum bch_sb_error_id { ++#define x(t, n) BCH_FSCK_ERR_##t = n, ++ BCH_SB_ERRS() ++#undef x ++ BCH_SB_ERR_MAX ++}; ++ ++extern const struct bch_sb_field_ops bch_sb_field_ops_errors; ++ ++void bch2_sb_error_count(struct bch_fs *, enum bch_sb_error_id); ++ ++void bch2_sb_errors_from_cpu(struct bch_fs *); ++ ++void bch2_fs_sb_errors_exit(struct bch_fs *); ++void bch2_fs_sb_errors_init_early(struct bch_fs *); ++int bch2_fs_sb_errors_init(struct bch_fs *); ++ ++#endif /* _BCACHEFS_SB_ERRORS_H */ +diff --git a/fs/bcachefs/sb-errors_types.h b/fs/bcachefs/sb-errors_types.h +new file mode 100644 +index 000000000..b1c099843 +--- /dev/null ++++ b/fs/bcachefs/sb-errors_types.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _BCACHEFS_SB_ERRORS_TYPES_H ++#define _BCACHEFS_SB_ERRORS_TYPES_H ++ ++#include "darray.h" ++ ++struct bch_sb_error_entry_cpu { ++ u64 id:16, ++ nr:48; ++ u64 last_error_time; ++}; ++ ++typedef DARRAY(struct bch_sb_error_entry_cpu) bch_sb_errors_cpu; ++ ++#endif /* _BCACHEFS_SB_ERRORS_TYPES_H */ ++ diff --git a/fs/bcachefs/sb-members.c b/fs/bcachefs/sb-members.c new file mode 100644 -index 000000000..6dd85bb99 +index 000000000..032fe4548 --- /dev/null +++ b/fs/bcachefs/sb-members.c -@@ -0,0 +1,339 @@ +@@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -82159,6 +82887,18 @@ index 000000000..6dd85bb99 +#include "sb-members.h" +#include "super-io.h" + ++#define x(t, n, ...) [n] = #t, ++static const char * const bch2_iops_measurements[] = { ++ BCH_IOPS_MEASUREMENTS() ++ NULL ++}; ++ ++char * const bch2_member_error_strs[] = { ++ BCH_MEMBER_ERROR_TYPES() ++ NULL ++}; ++#undef x ++ +/* Code for bch_sb_field_members_v1: */ + +static struct bch_member *members_v2_get_mut(struct bch_sb_field_members_v2 *mi, int i) @@ -82188,7 +82928,8 @@ index 000000000..6dd85bb99 +{ + struct bch_member ret, *p = members_v1_get_mut(mi, i); + memset(&ret, 0, sizeof(ret)); -+ memcpy(&ret, p, min_t(size_t, sizeof(struct bch_member), sizeof(ret))); return ret; ++ memcpy(&ret, p, min_t(size_t, BCH_MEMBER_V1_BYTES, sizeof(ret))); ++ return ret; +} + +struct bch_member bch2_sb_member_get(struct bch_sb *sb, int i) @@ -82223,7 +82964,7 @@ index 000000000..6dd85bb99 + return 0; +} + -+int bch2_members_v2_init(struct bch_fs *c) ++int bch2_sb_members_v2_init(struct bch_fs *c) +{ + struct bch_sb_field_members_v1 *mi1; + struct bch_sb_field_members_v2 *mi2; @@ -82243,7 +82984,7 @@ index 000000000..6dd85bb99 + return sb_members_v2_resize_entries(c); +} + -+int bch_members_cpy_v2_v1(struct bch_sb_handle *disk_sb) ++int bch2_sb_members_cpy_v2_v1(struct bch_sb_handle *disk_sb) +{ + struct bch_sb_field_members_v1 *mi1; + struct bch_sb_field_members_v2 *mi2; @@ -82307,7 +83048,6 @@ index 000000000..6dd85bb99 + u64 bucket_size = le16_to_cpu(m.bucket_size); + u64 device_size = le64_to_cpu(m.nbuckets) * bucket_size; + -+ + prt_printf(out, "Device:"); + prt_tab(out); + prt_printf(out, "%u", i); @@ -82315,6 +83055,21 @@ index 000000000..6dd85bb99 + + printbuf_indent_add(out, 2); + ++ prt_printf(out, "Label:"); ++ prt_tab(out); ++ if (BCH_MEMBER_GROUP(&m)) { ++ unsigned idx = BCH_MEMBER_GROUP(&m) - 1; ++ ++ if (idx < disk_groups_nr(gi)) ++ prt_printf(out, "%s (%u)", ++ gi->entries[idx].label, idx); ++ else ++ prt_printf(out, "(bad disk labels section)"); ++ } else { ++ prt_printf(out, "(none)"); ++ } ++ prt_newline(out); ++ + prt_printf(out, "UUID:"); + prt_tab(out); + pr_uuid(out, m.uuid.b); @@ -82325,6 +83080,13 @@ index 000000000..6dd85bb99 + prt_units_u64(out, device_size << 9); + prt_newline(out); + ++ for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) { ++ prt_printf(out, "%s errors:", bch2_member_error_strs[i]); ++ prt_tab(out); ++ prt_u64(out, le64_to_cpu(m.errors[i])); ++ prt_newline(out); ++ } ++ + for (unsigned i = 0; i < BCH_IOPS_NR; i++) { + prt_printf(out, "%s iops:", bch2_iops_measurements[i]); + prt_tab(out); @@ -82363,21 +83125,6 @@ index 000000000..6dd85bb99 + : "unknown"); + prt_newline(out); + -+ prt_printf(out, "Label:"); -+ prt_tab(out); -+ if (BCH_MEMBER_GROUP(&m)) { -+ unsigned idx = BCH_MEMBER_GROUP(&m) - 1; -+ -+ if (idx < disk_groups_nr(gi)) -+ prt_printf(out, "%s (%u)", -+ gi->entries[idx].label, idx); -+ else -+ prt_printf(out, "(bad disk labels section)"); -+ } else { -+ prt_printf(out, "(none)"); -+ } -+ prt_newline(out); -+ + prt_printf(out, "Data allowed:"); + prt_tab(out); + if (BCH_MEMBER_DATA_ALLOWED(&m)) @@ -82414,8 +83161,7 @@ index 000000000..6dd85bb99 + struct bch_sb_field_members_v1 *mi = field_to_type(f, members_v1); + unsigned i; + -+ if ((void *) members_v1_get_mut(mi, sb->nr_devices) > -+ vstruct_end(&mi->field)) { ++ if ((void *) members_v1_get_mut(mi, sb->nr_devices) > vstruct_end(&mi->field)) { + prt_printf(err, "too many devices for section size"); + return -BCH_ERR_invalid_sb_members; + } @@ -82489,18 +83235,89 @@ index 000000000..6dd85bb99 + .validate = bch2_sb_members_v2_validate, + .to_text = bch2_sb_members_v2_to_text, +}; ++ ++void bch2_sb_members_from_cpu(struct bch_fs *c) ++{ ++ struct bch_sb_field_members_v2 *mi = bch2_sb_field_get(c->disk_sb.sb, members_v2); ++ struct bch_dev *ca; ++ unsigned i, e; ++ ++ rcu_read_lock(); ++ for_each_member_device_rcu(ca, c, i, NULL) { ++ struct bch_member *m = members_v2_get_mut(mi, i); ++ ++ for (e = 0; e < BCH_MEMBER_ERROR_NR; e++) ++ m->errors[e] = cpu_to_le64(atomic64_read(&ca->errors[e])); ++ } ++ rcu_read_unlock(); ++} ++ ++void bch2_dev_io_errors_to_text(struct printbuf *out, struct bch_dev *ca) ++{ ++ struct bch_fs *c = ca->fs; ++ struct bch_member m; ++ ++ mutex_lock(&ca->fs->sb_lock); ++ m = bch2_sb_member_get(c->disk_sb.sb, ca->dev_idx); ++ mutex_unlock(&ca->fs->sb_lock); ++ ++ printbuf_tabstop_push(out, 12); ++ ++ prt_str(out, "IO errors since filesystem creation"); ++ prt_newline(out); ++ ++ printbuf_indent_add(out, 2); ++ for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) { ++ prt_printf(out, "%s:", bch2_member_error_strs[i]); ++ prt_tab(out); ++ prt_u64(out, atomic64_read(&ca->errors[i])); ++ prt_newline(out); ++ } ++ printbuf_indent_sub(out, 2); ++ ++ prt_str(out, "IO errors since "); ++ bch2_pr_time_units(out, (ktime_get_real_seconds() - le64_to_cpu(m.errors_reset_time)) * NSEC_PER_SEC); ++ prt_str(out, " ago"); ++ prt_newline(out); ++ ++ printbuf_indent_add(out, 2); ++ for (unsigned i = 0; i < BCH_MEMBER_ERROR_NR; i++) { ++ prt_printf(out, "%s:", bch2_member_error_strs[i]); ++ prt_tab(out); ++ prt_u64(out, atomic64_read(&ca->errors[i]) - le64_to_cpu(m.errors_at_reset[i])); ++ prt_newline(out); ++ } ++ printbuf_indent_sub(out, 2); ++} ++ ++void bch2_dev_errors_reset(struct bch_dev *ca) ++{ ++ struct bch_fs *c = ca->fs; ++ struct bch_member *m; ++ ++ mutex_lock(&c->sb_lock); ++ m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); ++ for (unsigned i = 0; i < ARRAY_SIZE(m->errors_at_reset); i++) ++ m->errors_at_reset[i] = cpu_to_le64(atomic64_read(&ca->errors[i])); ++ m->errors_reset_time = ktime_get_real_seconds(); ++ ++ bch2_write_super(c); ++ mutex_unlock(&c->sb_lock); ++} diff --git a/fs/bcachefs/sb-members.h b/fs/bcachefs/sb-members.h new file mode 100644 -index 000000000..430f3457b +index 000000000..1583e80af --- /dev/null +++ b/fs/bcachefs/sb-members.h -@@ -0,0 +1,182 @@ +@@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_SB_MEMBERS_H +#define _BCACHEFS_SB_MEMBERS_H + -+int bch2_members_v2_init(struct bch_fs *c); -+int bch_members_cpy_v2_v1(struct bch_sb_handle *disk_sb); ++extern char * const bch2_member_error_strs[]; ++ ++int bch2_sb_members_v2_init(struct bch_fs *c); ++int bch2_sb_members_cpy_v2_v1(struct bch_sb_handle *disk_sb); +struct bch_member *bch2_members_v2_get_mut(struct bch_sb *sb, int i); +struct bch_member bch2_sb_member_get(struct bch_sb *sb, int i); + @@ -82676,6 +83493,44 @@ index 000000000..430f3457b +extern const struct bch_sb_field_ops bch_sb_field_ops_members_v1; +extern const struct bch_sb_field_ops bch_sb_field_ops_members_v2; + ++static inline bool bch2_member_exists(struct bch_member *m) ++{ ++ return !bch2_is_zero(&m->uuid, sizeof(m->uuid)); ++} ++ ++static inline bool bch2_dev_exists(struct bch_sb *sb, ++ unsigned dev) ++{ ++ if (dev < sb->nr_devices) { ++ struct bch_member m = bch2_sb_member_get(sb, dev); ++ return bch2_member_exists(&m); ++ } ++ return false; ++} ++ ++static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi) ++{ ++ return (struct bch_member_cpu) { ++ .nbuckets = le64_to_cpu(mi->nbuckets), ++ .first_bucket = le16_to_cpu(mi->first_bucket), ++ .bucket_size = le16_to_cpu(mi->bucket_size), ++ .group = BCH_MEMBER_GROUP(mi), ++ .state = BCH_MEMBER_STATE(mi), ++ .discard = BCH_MEMBER_DISCARD(mi), ++ .data_allowed = BCH_MEMBER_DATA_ALLOWED(mi), ++ .durability = BCH_MEMBER_DURABILITY(mi) ++ ? BCH_MEMBER_DURABILITY(mi) - 1 ++ : 1, ++ .freespace_initialized = BCH_MEMBER_FREESPACE_INITIALIZED(mi), ++ .valid = bch2_member_exists(mi), ++ }; ++} ++ ++void bch2_sb_members_from_cpu(struct bch_fs *); ++ ++void bch2_dev_io_errors_to_text(struct printbuf *, struct bch_dev *); ++void bch2_dev_errors_reset(struct bch_dev *); ++ +#endif /* _BCACHEFS_SB_MEMBERS_H */ diff --git a/fs/bcachefs/seqmutex.h b/fs/bcachefs/seqmutex.h new file mode 100644 @@ -84322,10 +85177,10 @@ index 000000000..394da423c +#endif /* _LINUX_SIX_H */ diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c new file mode 100644 -index 000000000..315e88cc3 +index 000000000..a3fecc785 --- /dev/null +++ b/fs/bcachefs/snapshot.c -@@ -0,0 +1,1703 @@ +@@ -0,0 +1,1704 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -84358,17 +85213,18 @@ index 000000000..315e88cc3 + le32_to_cpu(t.v->root_snapshot)); +} + -+int bch2_snapshot_tree_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_snapshot_tree_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ if (bkey_gt(k.k->p, POS(0, U32_MAX)) || -+ bkey_lt(k.k->p, POS(0, 1))) { -+ prt_printf(err, "bad pos"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(bkey_gt(k.k->p, POS(0, U32_MAX)) || ++ bkey_lt(k.k->p, POS(0, 1)), c, err, ++ snapshot_tree_pos_bad, ++ "bad pos"); ++fsck_err: ++ return ret; +} + +int bch2_snapshot_tree_lookup(struct btree_trans *trans, u32 id, @@ -84530,68 +85386,60 @@ index 000000000..315e88cc3 + le32_to_cpu(s.v->skip[2])); +} + -+int bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_snapshot_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + struct bkey_s_c_snapshot s; + u32 i, id; ++ int ret = 0; + -+ if (bkey_gt(k.k->p, POS(0, U32_MAX)) || -+ bkey_lt(k.k->p, POS(0, 1))) { -+ prt_printf(err, "bad pos"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_gt(k.k->p, POS(0, U32_MAX)) || ++ bkey_lt(k.k->p, POS(0, 1)), c, err, ++ snapshot_pos_bad, ++ "bad pos"); + + s = bkey_s_c_to_snapshot(k); + + id = le32_to_cpu(s.v->parent); -+ if (id && id <= k.k->p.offset) { -+ prt_printf(err, "bad parent node (%u <= %llu)", -+ id, k.k->p.offset); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(id && id <= k.k->p.offset, c, err, ++ snapshot_parent_bad, ++ "bad parent node (%u <= %llu)", ++ id, k.k->p.offset); + -+ if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) { -+ prt_printf(err, "children not normalized"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1]), c, err, ++ snapshot_children_not_normalized, ++ "children not normalized"); + -+ if (s.v->children[0] && -+ s.v->children[0] == s.v->children[1]) { -+ prt_printf(err, "duplicate child nodes"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(s.v->children[0] && s.v->children[0] == s.v->children[1], c, err, ++ snapshot_child_duplicate, ++ "duplicate child nodes"); + + for (i = 0; i < 2; i++) { + id = le32_to_cpu(s.v->children[i]); + -+ if (id >= k.k->p.offset) { -+ prt_printf(err, "bad child node (%u >= %llu)", -+ id, k.k->p.offset); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(id >= k.k->p.offset, c, err, ++ snapshot_child_bad, ++ "bad child node (%u >= %llu)", ++ id, k.k->p.offset); + } + + if (bkey_val_bytes(k.k) > offsetof(struct bch_snapshot, skip)) { -+ if (le32_to_cpu(s.v->skip[0]) > le32_to_cpu(s.v->skip[1]) || -+ le32_to_cpu(s.v->skip[1]) > le32_to_cpu(s.v->skip[2])) { -+ prt_printf(err, "skiplist not normalized"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(le32_to_cpu(s.v->skip[0]) > le32_to_cpu(s.v->skip[1]) || ++ le32_to_cpu(s.v->skip[1]) > le32_to_cpu(s.v->skip[2]), c, err, ++ snapshot_skiplist_not_normalized, ++ "skiplist not normalized"); + + for (i = 0; i < ARRAY_SIZE(s.v->skip); i++) { + id = le32_to_cpu(s.v->skip[i]); + -+ if ((id && !s.v->parent) || -+ (id && id <= k.k->p.offset)) { -+ prt_printf(err, "bad skiplist node %u", id); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(id && id < le32_to_cpu(s.v->parent), c, err, ++ snapshot_skiplist_bad, ++ "bad skiplist node %u", id); + } + } -+ -+ return 0; ++fsck_err: ++ return ret; +} + +static void __set_is_ancestor_bitmap(struct bch_fs *c, u32 id) @@ -84858,7 +85706,7 @@ index 000000000..315e88cc3 + if (fsck_err_on(ret || + root_id != bch2_snapshot_root(c, root_id) || + st.k->p.offset != le32_to_cpu(s.tree), -+ c, ++ c, snapshot_tree_to_missing_snapshot, + "snapshot tree points to missing/incorrect snapshot:\n %s", + (bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) { + ret = bch2_btree_delete_at(trans, iter, 0); @@ -84870,17 +85718,20 @@ index 000000000..315e88cc3 + if (ret && !bch2_err_matches(ret, ENOENT)) + goto err; + -+ if (fsck_err_on(ret, c, ++ if (fsck_err_on(ret, ++ c, snapshot_tree_to_missing_subvol, + "snapshot tree points to missing subvolume:\n %s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) || + fsck_err_on(!bch2_snapshot_is_ancestor_early(c, + le32_to_cpu(subvol.snapshot), -+ root_id), c, ++ root_id), ++ c, snapshot_tree_to_wrong_subvol, + "snapshot tree points to subvolume that does not point to snapshot in this tree:\n %s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) || -+ fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol), c, ++ fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol), ++ c, snapshot_tree_to_snapshot_subvol, + "snapshot tree points to snapshot subvolume:\n %s", + (printbuf_reset(&buf), + bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) { @@ -85116,7 +85967,9 @@ index 000000000..315e88cc3 + goto err; + } + } else { -+ if (fsck_err_on(s.subvol, c, "snapshot should not point to subvol:\n %s", ++ if (fsck_err_on(s.subvol, ++ c, snapshot_should_not_have_subvol, ++ "snapshot should not point to subvol:\n %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot); + ret = PTR_ERR_OR_ZERO(u); @@ -85132,7 +85985,8 @@ index 000000000..315e88cc3 + if (ret < 0) + goto err; + -+ if (fsck_err_on(!ret, c, "snapshot points to missing/incorrect tree:\n %s", ++ if (fsck_err_on(!ret, c, snapshot_to_bad_snapshot_tree, ++ "snapshot points to missing/incorrect tree:\n %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { + ret = snapshot_tree_ptr_repair(trans, iter, k, &s); + if (ret) @@ -85144,7 +85998,8 @@ index 000000000..315e88cc3 + + if (le32_to_cpu(s.depth) != real_depth && + (c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists || -+ fsck_err(c, "snapshot with incorrect depth field, should be %u:\n %s", ++ fsck_err(c, snapshot_bad_depth, ++ "snapshot with incorrect depth field, should be %u:\n %s", + real_depth, (bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) { + u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot); + ret = PTR_ERR_OR_ZERO(u); @@ -85161,7 +86016,8 @@ index 000000000..315e88cc3 + + if (!ret && + (c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists || -+ fsck_err(c, "snapshot with bad skiplist field:\n %s", ++ fsck_err(c, snapshot_bad_skiplist, ++ "snapshot with bad skiplist field:\n %s", + (bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) { + u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot); + ret = PTR_ERR_OR_ZERO(u); @@ -85676,12 +86532,12 @@ index 000000000..315e88cc3 + u32 id = le32_to_cpu(s->v.skip[j]); + + if (snapshot_list_has_id(deleted, id)) { -+ id = depth > 1 -+ ? bch2_snapshot_nth_parent_skip(c, ++ id = bch2_snapshot_nth_parent_skip(c, + parent, -+ get_random_u32_below(depth - 1), -+ deleted) -+ : parent; ++ depth > 1 ++ ? get_random_u32_below(depth - 1) ++ : 0, ++ deleted); + s->v.skip[j] = cpu_to_le32(id); + } + } @@ -86031,7 +86887,7 @@ index 000000000..315e88cc3 +} diff --git a/fs/bcachefs/snapshot.h b/fs/bcachefs/snapshot.h new file mode 100644 -index 000000000..01f006cac +index 000000000..f09a22f44 --- /dev/null +++ b/fs/bcachefs/snapshot.h @@ -0,0 +1,268 @@ @@ -86042,7 +86898,7 @@ index 000000000..01f006cac +enum bkey_invalid_flags; + +void bch2_snapshot_tree_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); -+int bch2_snapshot_tree_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_snapshot_tree_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); + +#define bch2_bkey_ops_snapshot_tree ((struct bkey_ops) { \ @@ -86056,7 +86912,7 @@ index 000000000..01f006cac +int bch2_snapshot_tree_lookup(struct btree_trans *, u32, struct bch_snapshot_tree *); + +void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); -+int bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_snapshot_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +int bch2_mark_snapshot(struct btree_trans *, enum btree_id, unsigned, + struct bkey_s_c, struct bkey_s_c, unsigned); @@ -86681,10 +87537,10 @@ index 000000000..ae21a8cca +#endif /* _BCACHEFS_STR_HASH_H */ diff --git a/fs/bcachefs/subvolume.c b/fs/bcachefs/subvolume.c new file mode 100644 -index 000000000..73ba22c21 +index 000000000..fccd25aa3 --- /dev/null +++ b/fs/bcachefs/subvolume.c -@@ -0,0 +1,435 @@ +@@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -86749,7 +87605,8 @@ index 000000000..73ba22c21 + if (ret) + return ret; + -+ if (fsck_err_on(le32_to_cpu(st.master_subvol) != subvol.k->p.offset, c, ++ if (fsck_err_on(le32_to_cpu(st.master_subvol) != subvol.k->p.offset, ++ c, subvol_not_master_and_not_snapshot, + "subvolume %llu is not set as snapshot but is not master subvolume", + k.k->p.offset)) { + struct bkey_i_subvolume *s = @@ -86784,16 +87641,17 @@ index 000000000..73ba22c21 + +/* Subvolumes: */ + -+int bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_subvolume_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, struct printbuf *err) +{ -+ if (bkey_lt(k.k->p, SUBVOL_POS_MIN) || -+ bkey_gt(k.k->p, SUBVOL_POS_MAX)) { -+ prt_printf(err, "invalid pos"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ int ret = 0; + -+ return 0; ++ bkey_fsck_err_on(bkey_lt(k.k->p, SUBVOL_POS_MIN) || ++ bkey_gt(k.k->p, SUBVOL_POS_MAX), c, err, ++ subvol_pos_bad, ++ "invalid pos"); ++fsck_err: ++ return ret; +} + +void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c, @@ -87122,7 +87980,7 @@ index 000000000..73ba22c21 +} diff --git a/fs/bcachefs/subvolume.h b/fs/bcachefs/subvolume.h new file mode 100644 -index 000000000..bb14f92e8 +index 000000000..a1003d30a --- /dev/null +++ b/fs/bcachefs/subvolume.h @@ -0,0 +1,35 @@ @@ -87137,7 +87995,7 @@ index 000000000..bb14f92e8 + +int bch2_check_subvols(struct bch_fs *); + -+int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_subvolume_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -87200,10 +88058,10 @@ index 000000000..86833445a +#endif /* _BCACHEFS_SUBVOLUME_TYPES_H */ diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c new file mode 100644 -index 000000000..332d41e1c +index 000000000..83bdb4368 --- /dev/null +++ b/fs/bcachefs/super-io.c -@@ -0,0 +1,1258 @@ +@@ -0,0 +1,1266 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -87219,6 +88077,7 @@ index 000000000..332d41e1c +#include "replicas.h" +#include "quota.h" +#include "sb-clean.h" ++#include "sb-errors.h" +#include "sb-members.h" +#include "super-io.h" +#include "super.h" @@ -88011,7 +88870,12 @@ index 000000000..332d41e1c + + /* XXX: return errors directly */ + -+ if (bch2_dev_io_err_on(bio->bi_status, ca, "superblock write error: %s", ++ if (bch2_dev_io_err_on(bio->bi_status, ca, ++ bio_data_dir(bio) ++ ? BCH_MEMBER_ERROR_write ++ : BCH_MEMBER_ERROR_read, ++ "superblock %s error: %s", ++ bio_data_dir(bio) ? "write" : "read", + bch2_blk_status_to_str(bio->bi_status))) + ca->sb_write_error = 1; + @@ -88098,7 +88962,9 @@ index 000000000..332d41e1c + SET_BCH_SB_BIG_ENDIAN(c->disk_sb.sb, CPU_BIG_ENDIAN); + + bch2_sb_counters_from_cpu(c); -+ bch_members_cpy_v2_v1(&c->disk_sb); ++ bch2_sb_members_from_cpu(c); ++ bch2_sb_members_cpy_v2_v1(&c->disk_sb); ++ bch2_sb_errors_from_cpu(c); + + for_each_online_member(ca, c, i) + bch2_sb_from_fs(c, ca); @@ -88464,10 +89330,10 @@ index 000000000..332d41e1c +} diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h new file mode 100644 -index 000000000..b0d8584f4 +index 000000000..f5abd102b --- /dev/null +++ b/fs/bcachefs/super-io.h -@@ -0,0 +1,124 @@ +@@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_SUPER_IO_H +#define _BCACHEFS_SUPER_IO_H @@ -88493,6 +89359,11 @@ index 000000000..b0d8584f4 + unsigned, + unsigned); + ++static inline size_t bch2_sb_field_bytes(struct bch_sb_field *f) ++{ ++ return le32_to_cpu(f->u64s) * sizeof(u64); ++} ++ +#define field_to_type(_f, _name) \ + container_of_or_null(_f, struct bch_sb_field_##_name, field) + @@ -88548,41 +89419,6 @@ index 000000000..b0d8584f4 + __bch2_check_set_feature(c, feat); +} + -+/* BCH_SB_FIELD_members_v1: */ -+ -+static inline bool bch2_member_exists(struct bch_member *m) -+{ -+ return !bch2_is_zero(&m->uuid, sizeof(m->uuid)); -+} -+ -+static inline bool bch2_dev_exists(struct bch_sb *sb, -+ unsigned dev) -+{ -+ if (dev < sb->nr_devices) { -+ struct bch_member m = bch2_sb_member_get(sb, dev); -+ return bch2_member_exists(&m); -+ } -+ return false; -+} -+ -+static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi) -+{ -+ return (struct bch_member_cpu) { -+ .nbuckets = le64_to_cpu(mi->nbuckets), -+ .first_bucket = le16_to_cpu(mi->first_bucket), -+ .bucket_size = le16_to_cpu(mi->bucket_size), -+ .group = BCH_MEMBER_GROUP(mi), -+ .state = BCH_MEMBER_STATE(mi), -+ .discard = BCH_MEMBER_DISCARD(mi), -+ .data_allowed = BCH_MEMBER_DATA_ALLOWED(mi), -+ .durability = BCH_MEMBER_DURABILITY(mi) -+ ? BCH_MEMBER_DURABILITY(mi) - 1 -+ : 1, -+ .freespace_initialized = BCH_MEMBER_FREESPACE_INITIALIZED(mi), -+ .valid = bch2_member_exists(mi), -+ }; -+} -+ +void bch2_sb_maybe_downgrade(struct bch_fs *); +void bch2_sb_upgrade(struct bch_fs *, unsigned); + @@ -88594,10 +89430,10 @@ index 000000000..b0d8584f4 +#endif /* _BCACHEFS_SUPER_IO_H */ diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c new file mode 100644 -index 000000000..ce59018b2 +index 000000000..1b5c2a1bd --- /dev/null +++ b/fs/bcachefs/super.c -@@ -0,0 +1,2020 @@ +@@ -0,0 +1,2021 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * bcachefs setup/teardown code, and some metadata io - read a superblock and @@ -88649,6 +89485,7 @@ index 000000000..ce59018b2 +#include "recovery.h" +#include "replicas.h" +#include "sb-clean.h" ++#include "sb-errors.h" +#include "sb-members.h" +#include "snapshot.h" +#include "subvolume.h" @@ -89000,7 +89837,7 @@ index 000000000..ce59018b2 + + bch_info(c, "going read-write"); + -+ ret = bch2_members_v2_init(c); ++ ret = bch2_sb_members_v2_init(c); + if (ret) + goto err; + @@ -89081,6 +89918,7 @@ index 000000000..ce59018b2 + bch2_time_stats_exit(&c->times[i]); + + bch2_free_pending_node_rewrites(c); ++ bch2_fs_sb_errors_exit(c); + bch2_fs_counters_exit(c); + bch2_fs_snapshots_exit(c); + bch2_fs_quota_exit(c); @@ -89313,6 +90151,7 @@ index 000000000..ce59018b2 + bch2_fs_quota_init(c); + bch2_fs_ec_init_early(c); + bch2_fs_move_init(c); ++ bch2_fs_sb_errors_init_early(c); + + INIT_LIST_HEAD(&c->list); + @@ -89329,8 +90168,8 @@ index 000000000..ce59018b2 + + INIT_LIST_HEAD(&c->journal_iters); + -+ INIT_LIST_HEAD(&c->fsck_errors); -+ mutex_init(&c->fsck_error_lock); ++ INIT_LIST_HEAD(&c->fsck_error_msgs); ++ mutex_init(&c->fsck_error_msgs_lock); + + seqcount_init(&c->gc_pos_lock); + @@ -89440,6 +90279,7 @@ index 000000000..ce59018b2 + } + + ret = bch2_fs_counters_init(c) ?: ++ bch2_fs_sb_errors_init(c) ?: + bch2_io_clock_init(&c->io_clock[READ]) ?: + bch2_io_clock_init(&c->io_clock[WRITE]) ?: + bch2_fs_journal_init(&c->journal) ?: @@ -89542,7 +90382,7 @@ index 000000000..ce59018b2 + + mutex_lock(&c->sb_lock); + -+ ret = bch2_members_v2_init(c); ++ ret = bch2_sb_members_v2_init(c); + if (ret) { + mutex_unlock(&c->sb_lock); + goto err; @@ -89731,6 +90571,7 @@ index 000000000..ce59018b2 + struct bch_member *member) +{ + struct bch_dev *ca; ++ unsigned i; + + ca = kzalloc(sizeof(*ca), GFP_KERNEL); + if (!ca) @@ -89748,6 +90589,10 @@ index 000000000..ce59018b2 + bch2_time_stats_init(&ca->io_latency[WRITE]); + + ca->mi = bch2_mi_to_cpu(member); ++ ++ for (i = 0; i < ARRAY_SIZE(member->errors); i++) ++ atomic64_set(&ca->errors[i], le64_to_cpu(member->errors[i])); ++ + ca->uuid = member->uuid; + + ca->nr_btree_reserve = DIV_ROUND_UP(BTREE_NODE_RESERVE, @@ -90222,16 +91067,6 @@ index 000000000..ce59018b2 + goto err_unlock; + } + -+ mi = bch2_sb_field_get(ca->disk_sb.sb, members_v2); -+ -+ if (!bch2_sb_field_resize(&ca->disk_sb, members_v2, -+ le32_to_cpu(mi->field.u64s) + -+ sizeof(dev_mi) / sizeof(u64))) { -+ ret = -BCH_ERR_ENOSPC_sb_members; -+ bch_err_msg(c, ret, "setting up new superblock"); -+ goto err_unlock; -+ } -+ + if (dynamic_fault("bcachefs:add:no_slot")) + goto no_slot; + @@ -90245,6 +91080,8 @@ index 000000000..ce59018b2 + +have_slot: + nr_devices = max_t(unsigned, dev_idx + 1, c->sb.nr_devices); ++ ++ mi = bch2_sb_field_get(c->disk_sb.sb, members_v2); + u64s = DIV_ROUND_UP(sizeof(struct bch_sb_field_members_v2) + + le16_to_cpu(mi->member_bytes) * nr_devices, sizeof(u64)); + @@ -90724,10 +91561,10 @@ index 000000000..7dda4985b +#endif /* _BCACHEFS_SUPER_TYPES_H */ diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c new file mode 100644 -index 000000000..db2727e5c +index 000000000..8df45da5a --- /dev/null +++ b/fs/bcachefs/sysfs.c -@@ -0,0 +1,1024 @@ +@@ -0,0 +1,1034 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * bcache sysfs interfaces @@ -90879,7 +91716,9 @@ index 000000000..db2727e5c +read_attribute(first_bucket); +read_attribute(nbuckets); +rw_attribute(durability); -+read_attribute(iodone); ++read_attribute(io_done); ++read_attribute(io_errors); ++write_attribute(io_errors_reset); + +read_attribute(io_latency_read); +read_attribute(io_latency_write); @@ -91610,7 +92449,7 @@ index 000000000..db2727e5c + NULL +}; + -+static void dev_iodone_to_text(struct printbuf *out, struct bch_dev *ca) ++static void dev_io_done_to_text(struct printbuf *out, struct bch_dev *ca) +{ + int rw, i; + @@ -91653,8 +92492,11 @@ index 000000000..db2727e5c + prt_char(out, '\n'); + } + -+ if (attr == &sysfs_iodone) -+ dev_iodone_to_text(out, ca); ++ if (attr == &sysfs_io_done) ++ dev_io_done_to_text(out, ca); ++ ++ if (attr == &sysfs_io_errors) ++ bch2_dev_io_errors_to_text(out, ca); + + sysfs_print(io_latency_read, atomic64_read(&ca->cur_latency[READ])); + sysfs_print(io_latency_write, atomic64_read(&ca->cur_latency[WRITE])); @@ -91721,6 +92563,9 @@ index 000000000..db2727e5c + return ret; + } + ++ if (attr == &sysfs_io_errors_reset) ++ bch2_dev_errors_reset(ca); ++ + return size; +} +SYSFS_OPS(bch2_dev); @@ -91738,7 +92583,9 @@ index 000000000..db2727e5c + &sysfs_label, + + &sysfs_has_data, -+ &sysfs_iodone, ++ &sysfs_io_done, ++ &sysfs_io_errors, ++ &sysfs_io_errors_reset, + + &sysfs_io_latency_read, + &sysfs_io_latency_write, @@ -96382,10 +97229,10 @@ index 000000000..a6561b4b3 +#endif /* _VSTRUCTS_H */ diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c new file mode 100644 -index 000000000..74b41f567 +index 000000000..a39ff0c29 --- /dev/null +++ b/fs/bcachefs/xattr.c -@@ -0,0 +1,651 @@ +@@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "bcachefs.h" @@ -96458,46 +97305,38 @@ index 000000000..74b41f567 + .cmp_bkey = xattr_cmp_bkey, +}; + -+int bch2_xattr_invalid(const struct bch_fs *c, struct bkey_s_c k, ++int bch2_xattr_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ -+ const struct xattr_handler *handler; + struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k); ++ unsigned val_u64s = xattr_val_u64s(xattr.v->x_name_len, ++ le16_to_cpu(xattr.v->x_val_len)); ++ int ret = 0; + -+ if (bkey_val_u64s(k.k) < -+ xattr_val_u64s(xattr.v->x_name_len, -+ le16_to_cpu(xattr.v->x_val_len))) { -+ prt_printf(err, "value too small (%zu < %u)", -+ bkey_val_u64s(k.k), -+ xattr_val_u64s(xattr.v->x_name_len, -+ le16_to_cpu(xattr.v->x_val_len))); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_val_u64s(k.k) < val_u64s, c, err, ++ xattr_val_size_too_small, ++ "value too small (%zu < %u)", ++ bkey_val_u64s(k.k), val_u64s); + + /* XXX why +4 ? */ -+ if (bkey_val_u64s(k.k) > -+ xattr_val_u64s(xattr.v->x_name_len, -+ le16_to_cpu(xattr.v->x_val_len) + 4)) { -+ prt_printf(err, "value too big (%zu > %u)", -+ bkey_val_u64s(k.k), -+ xattr_val_u64s(xattr.v->x_name_len, -+ le16_to_cpu(xattr.v->x_val_len) + 4)); -+ return -BCH_ERR_invalid_bkey; -+ } ++ val_u64s = xattr_val_u64s(xattr.v->x_name_len, ++ le16_to_cpu(xattr.v->x_val_len) + 4); + -+ handler = bch2_xattr_type_to_handler(xattr.v->x_type); -+ if (!handler) { -+ prt_printf(err, "invalid type (%u)", xattr.v->x_type); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(bkey_val_u64s(k.k) > val_u64s, c, err, ++ xattr_val_size_too_big, ++ "value too big (%zu > %u)", ++ bkey_val_u64s(k.k), val_u64s); + -+ if (memchr(xattr.v->x_name, '\0', xattr.v->x_name_len)) { -+ prt_printf(err, "xattr name has invalid characters"); -+ return -BCH_ERR_invalid_bkey; -+ } ++ bkey_fsck_err_on(!bch2_xattr_type_to_handler(xattr.v->x_type), c, err, ++ xattr_invalid_type, ++ "invalid type (%u)", xattr.v->x_type); + -+ return 0; ++ bkey_fsck_err_on(memchr(xattr.v->x_name, '\0', xattr.v->x_name_len), c, err, ++ xattr_name_invalid_chars, ++ "xattr name has invalid characters"); ++fsck_err: ++ return ret; +} + +void bch2_xattr_to_text(struct printbuf *out, struct bch_fs *c, @@ -97039,7 +97878,7 @@ index 000000000..74b41f567 +} diff --git a/fs/bcachefs/xattr.h b/fs/bcachefs/xattr.h new file mode 100644 -index 000000000..f5a52e3a6 +index 000000000..1337f31a5 --- /dev/null +++ b/fs/bcachefs/xattr.h @@ -0,0 +1,50 @@ @@ -97051,7 +97890,7 @@ index 000000000..f5a52e3a6 + +extern const struct bch_hash_desc bch2_xattr_hash_desc; + -+int bch2_xattr_invalid(const struct bch_fs *, struct bkey_s_c, ++int bch2_xattr_invalid(struct bch_fs *, struct bkey_s_c, + enum bkey_invalid_flags, struct printbuf *); +void bch2_xattr_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + @@ -97984,24 +98823,24 @@ index 6b351e009..3da2f0545 100644 extern struct dentry *d_find_alias(struct inode *); diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h -index 11fbd0ee1..f49a7d311 100644 +index 11fbd0ee1..f75e0914d 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h -@@ -98,6 +98,12 @@ enum fid_type { +@@ -104,6 +104,12 @@ enum fid_type { */ - FILEID_FAT_WITH_PARENT = 0x72, + FILEID_LUSTRE = 0x97, + /* + * 64 bit inode number, 32 bit subvolume, 32 bit generation number: + */ -+ FILEID_BCACHEFS_WITHOUT_PARENT = 0x80, -+ FILEID_BCACHEFS_WITH_PARENT = 0x81, ++ FILEID_BCACHEFS_WITHOUT_PARENT = 0xb1, ++ FILEID_BCACHEFS_WITH_PARENT = 0xb2, + /* - * 128 bit child FID (struct lu_fid) - * 128 bit parent FID (struct lu_fid) + * 64 bit unique kernfs id + */ diff --git a/include/linux/fs.h b/include/linux/fs.h -index 562f2623c..810fa0812 100644 +index 87a21a18d..7142b86de 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -660,7 +660,8 @@ struct inode { @@ -99998,7 +100837,7 @@ index 9c0e09d0f..7bcf32b47 100644 } #endif diff --git a/mm/slab_common.c b/mm/slab_common.c -index 5658da50a..2ac6d9b18 100644 +index e2701dbb8..c8914adcb 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -26,6 +26,7 @@ @@ -100009,7 +100848,7 @@ index 5658da50a..2ac6d9b18 100644 #include #include "internal.h" -@@ -1273,10 +1274,15 @@ static int slab_show(struct seq_file *m, void *p) +@@ -1275,10 +1276,15 @@ static int slab_show(struct seq_file *m, void *p) return 0; } @@ -100026,7 +100865,7 @@ index 5658da50a..2ac6d9b18 100644 /* * Here acquiring slab_mutex is risky since we don't prefer to get -@@ -1286,24 +1292,52 @@ void dump_unreclaimable_slab(void) +@@ -1288,24 +1294,52 @@ void dump_unreclaimable_slab(void) * without acquiring the mutex. */ if (!mutex_trylock(&slab_mutex)) {