]> git.sesse.net Git - bcachefs-tools-debian/commitdiff
Update bcachefs sources to 7250b2ee5574 bcachefs: Fix deleted inodes btree in snapsho...
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 27 Oct 2023 20:43:11 +0000 (16:43 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Fri, 27 Oct 2023 20:43:11 +0000 (16:43 -0400)
56 files changed:
.bcachefs_revision
libbcachefs.c
libbcachefs/alloc_background.c
libbcachefs/alloc_background.h
libbcachefs/backpointers.c
libbcachefs/backpointers.h
libbcachefs/bcachefs.h
libbcachefs/bcachefs_format.h
libbcachefs/bkey_methods.c
libbcachefs/bkey_methods.h
libbcachefs/btree_gc.c
libbcachefs/btree_io.c
libbcachefs/btree_update_interior.c
libbcachefs/buckets.c
libbcachefs/dirent.c
libbcachefs/dirent.h
libbcachefs/ec.c
libbcachefs/ec.h
libbcachefs/errcode.h
libbcachefs/error.c
libbcachefs/error.h
libbcachefs/extents.c
libbcachefs/extents.h
libbcachefs/fsck.c
libbcachefs/inode.c
libbcachefs/inode.h
libbcachefs/io_read.c
libbcachefs/io_write.c
libbcachefs/journal_io.c
libbcachefs/lru.c
libbcachefs/lru.h
libbcachefs/opts.c
libbcachefs/opts.h
libbcachefs/quota.c
libbcachefs/quota.h
libbcachefs/rebalance.c
libbcachefs/recovery.c
libbcachefs/reflink.c
libbcachefs/reflink.h
libbcachefs/replicas.c
libbcachefs/sb-clean.c
libbcachefs/sb-errors.c [new file with mode: 0644]
libbcachefs/sb-errors.h [new file with mode: 0644]
libbcachefs/sb-errors_types.h [new file with mode: 0644]
libbcachefs/sb-members.c
libbcachefs/sb-members.h
libbcachefs/snapshot.c
libbcachefs/snapshot.h
libbcachefs/subvolume.c
libbcachefs/subvolume.h
libbcachefs/super-io.c
libbcachefs/super-io.h
libbcachefs/super.c
libbcachefs/sysfs.c
libbcachefs/xattr.c
libbcachefs/xattr.h

index db0b3f7ab23057b1ace93292efe83019a48906c7..e904af2ab99defa0a6c503306cc0d7431ccb6ba8 100644 (file)
@@ -1 +1 @@
-0d63ed13ea3d867055ae5752e2e0514a227d1dcb
+7250b2ee5574d7e2063c4498dde98ba11e2fd35f
index 016ff882ddc0f21bfbbad05c7ea6b95712f32114..c4a327e33a36911fbb7fb1a5c80c66db93f0c899 100644 (file)
@@ -276,7 +276,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                SET_BCH_SB_ENCRYPTION_TYPE(sb.sb, 1);
        }
 
-       bch_members_cpy_v2_v1(&sb);
+       bch2_sb_members_cpy_v2_v1(&sb);
 
        for (i = devs; i < devs + nr_devs; i++) {
                u64 size_sectors = i->size >> 9;
index 455ee0b47f314c0205b5eb2d988df5657d43a04b..c342ec3b0385ecd474fd7a93fe240cc6c90694ec 100644 (file)
@@ -192,114 +192,109 @@ static unsigned bch_alloc_v1_val_u64s(const struct bch_alloc *a)
        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)
@@ -521,17 +516,18 @@ static unsigned alloc_gen(struct bkey_s_c k, unsigned offset)
                : 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)
@@ -986,6 +982,7 @@ int bch2_check_alloc_key(struct btree_trans *trans,
        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);
@@ -1005,7 +1002,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,
 
        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],
@@ -1035,7 +1033,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,
 
        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],
@@ -1066,7 +1065,8 @@ int bch2_check_alloc_key(struct btree_trans *trans,
 
        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),
@@ -1124,7 +1124,8 @@ int bch2_check_alloc_hole_freespace(struct btree_trans *trans,
 
        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,
@@ -1187,6 +1188,7 @@ int bch2_check_alloc_hole_bucket_gens(struct btree_trans *trans,
 
                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,
@@ -1244,6 +1246,7 @@ static noinline_for_stack int __bch2_check_discard_freespace_key(struct btree_tr
                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;
@@ -1253,6 +1256,7 @@ static noinline_for_stack int __bch2_check_discard_freespace_key(struct btree_tr
        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),
@@ -1320,6 +1324,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
        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);
@@ -1330,6 +1335,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
        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);
@@ -1338,6 +1344,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
 
        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;
@@ -1345,6 +1352,7 @@ int bch2_check_bucket_gens_key(struct btree_trans *trans,
 
        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;
@@ -1495,11 +1503,13 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
                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),
index 97042067d2a960e32f586b5686f4e30490cf0974..e1ce38ef052eb3d2eef5483d2d80b25bb16c1a9b 100644 (file)
@@ -149,13 +149,13 @@ struct bkey_i_alloc_v4 *bch2_alloc_to_v4_mut(struct btree_trans *, struct bkey_s
 
 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);
@@ -193,7 +193,7 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
        .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);
 
index e74295c21a03e3d315e85b997844052200a25510..3b79bde1ce2f2abdda4827274b127c8a22e03e7a 100644 (file)
@@ -37,19 +37,20 @@ static bool extent_matches_bp(struct bch_fs *c,
        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)
@@ -356,6 +357,7 @@ static int bch2_check_btree_backpointer(struct btree_trans *trans, struct btree_
        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);
@@ -369,6 +371,7 @@ static int bch2_check_btree_backpointer(struct btree_trans *trans, struct btree_
                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))) {
@@ -460,7 +463,7 @@ missing:
 
        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;
@@ -793,6 +796,7 @@ static int check_one_backpointer(struct btree_trans *trans,
        }
 
        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);
index 547e0617602ab21049571e171eca313ab968a65c..4ab9f3562912de16a3e41f3baefa8645e9b2323c 100644 (file)
@@ -7,7 +7,7 @@
 #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);
index 68f0ff03c28a603e104d625640703d7a69f33fcd..9cb8684959ee17affdc2a558c14414477fef922d 100644 (file)
 #include "nocow_locking_types.h"
 #include "opts.h"
 #include "recovery_types.h"
+#include "sb-errors_types.h"
 #include "seqmutex.h"
 #include "util.h"
 
@@ -502,6 +503,8 @@ struct bch_dev {
         * 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];
 
@@ -990,11 +993,6 @@ struct bch_fs {
        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];
 
@@ -1043,6 +1041,14 @@ struct bch_fs {
        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;
index e04999c578920f63bec8b5baec4ec4efffbd1c72..29b000c6b7e1f49495e51b361ba0cdc66d81107e 100644 (file)
@@ -1218,7 +1218,8 @@ struct bch_sb_field {
        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,
@@ -1268,6 +1269,18 @@ enum bch_iops_measurement {
        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 */
@@ -1278,6 +1291,9 @@ struct bch_member {
 
        __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
@@ -1606,6 +1622,17 @@ struct bch_sb_field_journal_seq_blacklist {
        __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: */
 
 /*
index baf491878bf1ea18551ee00053439f6ff503e110..2f518d7e1a6444fa2f03b86235f850f7190c010c 100644 (file)
@@ -26,7 +26,7 @@ const char * const bch2_bkey_types[] = {
        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;
@@ -40,23 +40,24 @@ static int deleted_key_invalid(const struct bch_fs *c, struct bkey_s_c k,
        .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;
-       }
-
-       return 0;
+       int ret = 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;
@@ -71,7 +72,7 @@ static int key_type_cookie_invalid(const struct bch_fs *c, struct bkey_s_c k,
        .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;
@@ -92,18 +93,6 @@ static void key_type_inline_data_to_text(struct printbuf *out, struct bch_fs *c,
        .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);
@@ -111,7 +100,7 @@ static bool key_type_set_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_
 }
 
 #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,           \
 })
 
@@ -129,17 +118,19 @@ int bch2_bkey_val_invalid(struct bch_fs *c, struct bkey_s_c k,
                          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[] = {
@@ -162,61 +153,55 @@ int __bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
                        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,
@@ -228,20 +213,20 @@ int bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
                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)
index c829c8e381a7edc550641d04a78149cd848e04c3..3a370b7087acea9bed0de0e1c565034336303d01 100644 (file)
@@ -21,7 +21,7 @@ extern const struct bkey_ops bch2_bkey_null_ops;
  * 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);
@@ -55,7 +55,8 @@ int __bch2_bkey_invalid(struct bch_fs *, struct bkey_s_c, enum btree_node_type,
                        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 *);
index 475f8e8d9185da5f718c69a42ddddb7d9fd701c6..7cd517ae5bf49a56bff58aa0da11d896242e56ef 100644 (file)
@@ -95,15 +95,15 @@ static int bch2_gc_check_topology(struct bch_fs *c,
                        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;
@@ -122,10 +122,8 @@ static int bch2_gc_check_topology(struct bch_fs *c,
                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",
@@ -287,6 +285,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
 
                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",
@@ -298,6 +297,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
 
                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",
@@ -310,6 +310,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
 
                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",
@@ -320,6 +321,7 @@ static int btree_repair_node_boundaries(struct bch_fs *c, struct btree *b,
                }
 
                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",
@@ -344,6 +346,7 @@ static int btree_repair_node_end(struct bch_fs *c, struct btree *b,
        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",
@@ -396,6 +399,7 @@ again:
                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),
@@ -504,6 +508,7 @@ again:
        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),
@@ -582,7 +587,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
 
                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)],
@@ -599,7 +605,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
 
                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)],
@@ -620,7 +627,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
 
                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)],
@@ -631,7 +639,8 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
 
                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)],
@@ -645,6 +654,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
 
                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),
@@ -664,6 +674,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
                        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,
@@ -672,6 +683,7 @@ static int bch2_check_fix_ptrs(struct btree_trans *trans, enum btree_id btree_id
                                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,
@@ -811,6 +823,7 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
                        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)))
@@ -968,6 +981,7 @@ static int bch2_gc_btree_init_recurse(struct btree_trans *trans, struct btree *b
                                          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),
@@ -1025,6 +1039,7 @@ static int bch2_gc_btree_init(struct btree_trans *trans,
        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;
@@ -1034,6 +1049,7 @@ static int bch2_gc_btree_init(struct btree_trans *trans,
        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;
@@ -1210,16 +1226,16 @@ static int bch2_gc_done(struct bch_fs *c,
 
        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);
@@ -1230,13 +1246,17 @@ static int bch2_gc_done(struct bch_fs *c,
                        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");
        }
 
        {
@@ -1245,17 +1265,24 @@ static int bch2_gc_done(struct bch_fs *c,
                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);
                }
 
@@ -1271,7 +1298,8 @@ static int bch2_gc_done(struct bch_fs *c,
                        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);
                }
        }
 
@@ -1407,6 +1435,7 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
 
        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,
@@ -1415,9 +1444,9 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
                        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,              \
@@ -1426,11 +1455,16 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
                        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))
@@ -1587,6 +1621,7 @@ static int bch2_gc_write_reflink_key(struct btree_trans *trans,
        }
 
        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",
@@ -1712,7 +1747,8 @@ static int bch2_gc_write_stripes_key(struct btree_trans *trans,
        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));
index 7bf3ee25bc32e878f81cbe8a656208787ffdd27b..4d2d6f93500d22008a1ce031b134494c52812212 100644 (file)
@@ -530,7 +530,7 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
        prt_str(out, ": ");
 }
 
-__printf(8, 9)
+__printf(9, 10)
 static int __btree_err(int ret,
                       struct bch_fs *c,
                       struct bch_dev *ca,
@@ -538,6 +538,7 @@ static int __btree_err(int ret,
                       struct bset *i,
                       int write,
                       bool have_retry,
+                      enum bch_sb_error_id err_type,
                       const char *fmt, ...)
 {
        struct printbuf out = PRINTBUF;
@@ -562,9 +563,15 @@ static int __btree_err(int ret,
        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:
@@ -589,9 +596,11 @@ fsck_err:
        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;                                             \
@@ -666,13 +675,17 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
        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);
@@ -683,7 +696,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
 
        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);
@@ -693,11 +708,15 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
        }
 
        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;
@@ -705,12 +724,15 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
        }
 
        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) {
@@ -724,16 +746,22 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
 
                        /* 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)
@@ -750,7 +778,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                        }
 
                        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),
@@ -759,7 +789,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                }
 
                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));
@@ -769,7 +801,9 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                                          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));
@@ -792,7 +826,7 @@ static int bset_key_invalid(struct bch_fs *c, struct btree *b,
                            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);
 }
 
@@ -813,14 +847,18 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
                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),
@@ -839,12 +877,14 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
                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),
@@ -868,7 +908,10 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
 
                        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);
@@ -909,47 +952,62 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
        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);
@@ -959,7 +1017,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
 
                        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);
@@ -971,15 +1031,21 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                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);
@@ -1012,12 +1078,16 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                        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);
@@ -1034,7 +1104,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
 
        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 {
@@ -1045,7 +1117,9 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                     !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");
        }
 
@@ -1087,7 +1161,10 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                        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);
 
@@ -1168,7 +1245,8 @@ static void btree_node_read_work(struct work_struct *work)
 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);
@@ -1312,14 +1390,20 @@ static void btree_node_read_all_replicas_done(struct closure *cl)
                }
 
                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;
 
@@ -1749,7 +1833,8 @@ static void btree_node_write_endio(struct bio *bio)
        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);
index 7dbf6b6c7f3481e0d0ab0e15b9b5ec6c501131f0..818a83f35d276149ebbcdd2d1e1430f4c281cae1 100644 (file)
@@ -1274,14 +1274,14 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
 
        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();
index a8af803e7289fec94dfcc03929b1fcc1c94d5b98..2acd727d3f9b142b329fa2cdb35a9f0fb8623f17 100644 (file)
@@ -370,8 +370,8 @@ static inline int update_replicas(struct bch_fs *c, struct bkey_s_c k,
 
        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);
@@ -695,6 +695,7 @@ static int check_bucket_ref(struct btree_trans *trans,
 
        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,
@@ -707,6 +708,7 @@ static int check_bucket_ref(struct btree_trans *trans,
 
        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,
@@ -720,6 +722,7 @@ static int check_bucket_ref(struct btree_trans *trans,
 
        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,
@@ -741,6 +744,7 @@ static int check_bucket_ref(struct btree_trans *trans,
            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,
@@ -754,6 +758,7 @@ static int check_bucket_ref(struct btree_trans *trans,
 
        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,
@@ -1195,7 +1200,8 @@ static s64 __bch2_mark_reflink_p(struct btree_trans *trans,
        *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),
@@ -1857,6 +1863,7 @@ static int __bch2_trans_mark_metadata_bucket(struct btree_trans *trans,
 
        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,
index 6c6c8d57d72b43a0acdfe41f04c9e7cd3ee53383..1a0f2d5715692baa2f26a088c61b55742e03fec3 100644 (file)
@@ -97,61 +97,51 @@ const struct bch_hash_desc bch2_dirent_hash_desc = {
        .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,
index e9fa1df38232bf34d43afb7a6a671212f74ff5e0..cd262bf4d9c5365747562f22536309dc5853d070 100644 (file)
@@ -7,7 +7,7 @@
 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);
 
index 8646856e4539eae46fed2634154ebb340f841a46..5da0e7a69323a5ca7d99cc5a18b4b9561027256f 100644 (file)
@@ -105,29 +105,26 @@ struct ec_bio {
 
 /* 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,
@@ -373,7 +370,11 @@ static void ec_block_endio(struct bio *bio)
        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);
index 966d165a3b6602c200f0be8c4090be6d8027e7ea..61c67aa0aa49a202a7e3d4ddee3531308b45a7e0 100644 (file)
@@ -8,7 +8,7 @@
 
 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);
index 3e9f09cea6c799477193cc535b33fb32462e13f4..2a11f32cf30a24050f6237ddaf506f9bb87f31de 100644 (file)
        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)                     \
index 2a5af88726132bc936e4ddc404086b862f0666ef..7b28d37922fd0e47d82ac1d27403f031cc577c7b 100644 (file)
@@ -56,8 +56,9 @@ void bch2_io_error_work(struct work_struct *work)
        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);
 }
 
@@ -116,31 +117,34 @@ static struct fsck_err_state *fsck_err_get(struct bch_fs *c, const char *fmt)
        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;
@@ -148,11 +152,13 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
        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) {
                /*
@@ -162,7 +168,7 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
                 */
                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;
                }
@@ -257,7 +263,7 @@ int bch2_fsck_err(struct bch_fs *c, unsigned flags, const char *fmt, ...)
        if (s)
                s->ret = ret;
 
-       mutex_unlock(&c->fsck_error_lock);
+       mutex_unlock(&c->fsck_error_msgs_lock);
 
        printbuf_exit(&buf);
 
@@ -278,9 +284,9 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
 {
        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);
 
@@ -289,5 +295,5 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
                kfree(s);
        }
 
-       mutex_unlock(&c->fsck_error_lock);
+       mutex_unlock(&c->fsck_error_msgs_lock);
 }
index 7ce9540052e53df99a90d4ac958c671ad375bdc7..d167d65986e0425f2c2e8b2d5503b5d0a6526c6c 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/list.h>
 #include <linux/printk.h>
+#include "sb-errors.h"
 
 struct bch_dev;
 struct bch_fs;
@@ -101,18 +102,26 @@ struct fsck_err_state {
        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,
+};
+
+#define fsck_err_count(_c, _err)       bch2_sb_err_count(_c, BCH_FSCK_ERR_##_err)
 
-__printf(3, 4) __cold
-int bch2_fsck_err(struct bch_fs *, unsigned, const char *, ...);
+__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) {                             \
@@ -127,26 +136,53 @@ void bch2_flush_fsck_errs(struct bch_fs *);
 
 /* 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(c, _err_type, ...)                               \
+       __fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, _err_type, __VA_ARGS__)
+
+#define mustfix_fsck_err(c, _err_type, ...)                            \
+       __fsck_err(c, FSCK_CAN_FIX, _err_type, __VA_ARGS__)
+
+#define mustfix_fsck_err_on(cond, c, _err_type, ...)                   \
+       __fsck_err_on(cond, c, FSCK_CAN_FIX, _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 fsck_err(c, _err_type, ...)                                    \
+       __fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, _err_type, __VA_ARGS__)
 
-#define need_fsck_err(c, ...)                                          \
-       __fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, ##__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 mustfix_fsck_err(c, ...)                                       \
-       __fsck_err(c, FSCK_CAN_FIX, ##__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;
 
-#define mustfix_fsck_err_on(cond, c, ...)                              \
-       __fsck_err_on(cond, c, FSCK_CAN_FIX, ##__VA_ARGS__)
+       va_start(args, fmt);
+       prt_vprintf(err_msg, fmt, args);
+       va_end(args);
 
-#define fsck_err(c, ...)                                               \
-       __fsck_err(c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_ARGS__)
+}
 
-#define fsck_err_on(cond, c, ...)                                      \
-       __fsck_err_on(cond, c, FSCK_CAN_FIX|FSCK_CAN_IGNORE, ##__VA_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
@@ -179,26 +215,26 @@ do {                                                                      \
 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;                                                           \
 })
index 0c60d49c3599723d0a526fa9c7d06307e11603eb..a864de231b69e297e85491dfd285928152c467b8 100644 (file)
@@ -163,17 +163,19 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
 
 /* 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,
@@ -182,17 +184,20 @@ void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c,
        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,
@@ -373,19 +378,18 @@ bool bch2_extent_merge(struct bch_fs *c, struct bkey_s l, struct bkey_s_c r)
 
 /* 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,
@@ -1058,7 +1062,7 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
        }
 }
 
-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,
@@ -1071,6 +1075,7 @@ static int extent_ptr_invalid(const struct bch_fs *c,
        u64 bucket;
        u32 bucket_offset;
        struct bch_dev *ca;
+       int ret = 0;
 
        if (!bch2_dev_exists2(c, ptr->dev)) {
                /*
@@ -1081,41 +1086,33 @@ static int extent_ptr_invalid(const struct bch_fs *c,
                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)
 {
@@ -1125,24 +1122,22 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
        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:
@@ -1151,22 +1146,15 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
                        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++;
@@ -1176,52 +1164,41 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
                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: {
@@ -1238,27 +1215,26 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
                }
        }
 
-       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)
index 9110acae7e3ca94fea1e56d805405a4326ab73a1..a2ce8a3be13ca418a001d8ff93d9091565aed800 100644 (file)
@@ -400,12 +400,12 @@ int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c,
 
 /* 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,
@@ -445,7 +445,7 @@ bool bch2_extent_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
 
 /* 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);
@@ -705,7 +705,7 @@ void bch2_extent_ptr_set_cached(struct bkey_s, struct bch_extent_ptr *);
 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);
index 328cb3b3e21338878f3ea594cf7342983cf15168..0e470ebd7f103944cb7ba2a744a4c806ff9f3478 100644 (file)
@@ -721,8 +721,9 @@ static int check_key_has_snapshot(struct btree_trans *trans,
        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:
@@ -791,6 +792,7 @@ static int hash_check_key(struct btree_trans *trans,
 
                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),
@@ -809,7 +811,8 @@ out:
        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))) {
@@ -860,7 +863,8 @@ static int check_inode(struct btree_trans *trans,
                *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;
@@ -888,7 +892,8 @@ static int check_inode(struct btree_trans *trans,
 
        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);
@@ -900,7 +905,8 @@ static int check_inode(struct btree_trans *trans,
 
        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);
 
@@ -932,7 +938,8 @@ static int check_inode(struct btree_trans *trans,
 
        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;
 
@@ -1058,10 +1065,11 @@ static int check_i_sectors(struct btree_trans *trans, struct inode_walker *w)
                        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)
@@ -1202,7 +1210,8 @@ static int overlapping_extents_found(struct btree_trans *trans,
        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 };
 
@@ -1357,7 +1366,7 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
                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)))
@@ -1365,7 +1374,8 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
 
                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),
@@ -1397,7 +1407,8 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
                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))) {
@@ -1519,7 +1530,8 @@ static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
                                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;
@@ -1563,16 +1575,16 @@ static int check_dirent_target(struct btree_trans *trans,
                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++;
@@ -1583,7 +1595,8 @@ static int check_dirent_target(struct btree_trans *trans,
                                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",
@@ -1601,7 +1614,8 @@ static int check_dirent_target(struct btree_trans *trans,
                }
        }
 
-       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)),
@@ -1625,7 +1639,8 @@ static int check_dirent_target(struct btree_trans *trans,
        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));
@@ -1697,7 +1712,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                *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))) {
@@ -1709,7 +1724,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
        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),
@@ -1743,7 +1759,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                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);
@@ -1755,7 +1771,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                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)) {
@@ -1764,7 +1780,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                        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)) {
@@ -1783,7 +1800,8 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                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),
@@ -1869,7 +1887,7 @@ static int check_xattr(struct btree_trans *trans, struct btree_iter *iter,
                *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);
@@ -1918,7 +1936,8 @@ static int check_root_trans(struct btree_trans *trans)
        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;
@@ -1944,8 +1963,10 @@ static int check_root_trans(struct btree_trans *trans)
        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);
@@ -2049,7 +2070,8 @@ static int check_path(struct btree_trans *trans,
                }
 
                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,
@@ -2089,7 +2111,8 @@ static int check_path(struct btree_trans *trans,
                                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,
@@ -2349,7 +2372,8 @@ static int check_nlinks_update_inode(struct btree_trans *trans, struct btree_ite
                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)) {
index 23fcd442c5145aca5fdb754b78bff2d2c67044a1..925d1b7f28877de06c1e424eb91ec2b691f75716 100644 (file)
@@ -398,104 +398,102 @@ struct bkey_i *bch2_inode_to_v3(struct btree_trans *trans, struct bkey_i *k)
        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;
-       }
+       bkey_fsck_err_on(k.k->p.inode, c, err,
+                        inode_pos_inode_nonzero,
+                        "nonzero k.p.inode");
 
-       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.offset < BLOCKDEV_INODE_MAX, c, err,
+                        inode_pos_blockdev_range,
+                        "fs inode in blockdev range");
 
-       if (bch2_inode_unpack(k, &unpacked)) {
-               prt_printf(err, "invalid variable length fields");
-               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_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(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_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_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_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_flags & BCH_INODE_UNLINKED) &&
+                        unpacked.bi_nlink != 0, c, err,
+                        inode_unlinked_but_nlink_nonzero,
+                        "flagged as unlinked but bi_nlink != 0");
 
-       if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode)) {
-               prt_printf(err, "subvolume root but not a directory");
-               return -BCH_ERR_invalid_bkey;
-       }
-
-       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,
@@ -612,16 +610,17 @@ int bch2_mark_inode(struct btree_trans *trans,
        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,
@@ -1068,6 +1067,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
                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;
@@ -1079,6 +1079,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
 
        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;
@@ -1088,11 +1089,13 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
                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;
index 2781e328158375322dad3b6d7be0858960f0471a..74c62e6c16cc0669b82ffc74f6fd34a88843044c 100644 (file)
@@ -8,11 +8,11 @@
 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);
 
@@ -52,7 +52,7 @@ static inline bool bkey_is_inode(const struct bkey *k)
                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);
 
index 443c3ea655271efc649bb425d752ef9d447f4dd4..ae36fc485f5f1e018cddb728ed244d84a1459d05 100644 (file)
@@ -643,7 +643,7 @@ csum_err:
                "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:
@@ -677,7 +677,7 @@ static void bch2_read_endio(struct bio *bio)
        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",
index 6d9c777213e3ca56305f37123d7e1f82549a9c1a..613f384366403816dd4a0ca5cf066e8b55b3d8f5 100644 (file)
@@ -637,7 +637,7 @@ static void bch2_write_endio(struct bio *bio)
        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",
index b29ece313e443c681a7b0720caf5a7d4267c7d4a..65878542940d924e9d6656ff5590a14fe8a70b14 100644 (file)
@@ -140,7 +140,8 @@ static int journal_entry_add(struct bch_fs *c, struct bch_dev *ca,
                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;
@@ -235,7 +236,7 @@ static void journal_entry_err_msg(struct printbuf *out,
        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;                                \
                                                                        \
@@ -244,9 +245,10 @@ static void journal_entry_err_msg(struct printbuf *out,
                                                                        \
        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;           \
@@ -259,8 +261,8 @@ static void journal_entry_err_msg(struct printbuf *out,
        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
 
@@ -277,7 +279,10 @@ static int journal_validate_key(struct bch_fs *c,
        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;
@@ -286,6 +291,7 @@ static int journal_validate_key(struct bch_fs *c,
        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);
@@ -294,6 +300,7 @@ static int journal_validate_key(struct bch_fs *c,
 
        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));
@@ -317,7 +324,8 @@ static int journal_validate_key(struct bch_fs *c,
                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));
@@ -387,6 +395,7 @@ static int journal_entry_btree_root_validate(struct bch_fs *c,
        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);
                /*
@@ -436,6 +445,7 @@ static int journal_entry_blacklist_validate(struct bch_fs *c,
 
        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));
        }
@@ -463,6 +473,7 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
 
        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;
@@ -473,6 +484,7 @@ static int journal_entry_blacklist_v2_validate(struct bch_fs *c,
        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));
        }
@@ -505,6 +517,7 @@ static int journal_entry_usage_validate(struct bch_fs *c,
 
        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;
@@ -539,6 +552,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
        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;
@@ -570,13 +584,17 @@ static int journal_entry_clock_validate(struct bch_fs *c,
        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;
        }
@@ -608,7 +626,9 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
        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;
@@ -617,13 +637,17 @@ static int journal_entry_dev_usage_validate(struct bch_fs *c,
        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;
        }
@@ -738,7 +762,8 @@ static int jset_validate_entries(struct bch_fs *c, struct jset *jset,
 
        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;
@@ -767,6 +792,7 @@ static int jset_validate(struct bch_fs *c,
        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),
@@ -777,7 +803,8 @@ static int jset_validate(struct bch_fs *c,
        }
 
        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),
@@ -788,6 +815,7 @@ static int jset_validate(struct bch_fs *c,
        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))) {
@@ -816,7 +844,8 @@ static int jset_validate_early(struct bch_fs *c,
 
        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),
@@ -831,7 +860,8 @@ static int jset_validate_early(struct bch_fs *c,
                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))
@@ -900,7 +930,7 @@ reread:
                        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")) {
@@ -956,7 +986,8 @@ reread:
                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),
@@ -1172,6 +1203,7 @@ int bch2_journal_read(struct bch_fs *c,
 
                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)))
@@ -1188,7 +1220,8 @@ int bch2_journal_read(struct bch_fs *c,
        }
 
        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;
        }
 
@@ -1214,6 +1247,7 @@ int bch2_journal_read(struct bch_fs *c,
 
                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;
                }
@@ -1254,7 +1288,8 @@ int bch2_journal_read(struct bch_fs *c,
                        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,
@@ -1309,7 +1344,8 @@ int bch2_journal_read(struct bch_fs *c,
                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)
@@ -1581,7 +1617,8 @@ static void journal_write_endio(struct bio *bio)
        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")) {
index 215a653322f3b49c58fa0a4a8c775c1785ff1aeb..a5cc0ed195d6324d1f49718d5860b24045579f1b 100644 (file)
 #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,
@@ -95,6 +95,7 @@ static int bch2_check_lru_key(struct btree_trans *trans,
        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);
@@ -125,7 +126,8 @@ static int bch2_check_lru_key(struct btree_trans *trans,
                }
 
                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],
index be66bf9ad80911006cff4b6d5ea139141511e3c0..429dca816df5c5049c85e31ea20eb1e92ea694cf 100644 (file)
@@ -48,7 +48,7 @@ static inline enum bch_lru_type lru_type(struct bkey_s_c l)
        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);
 
index b7722b6236978cd692a478cd4ec324ade4456bb4..4ad5880664b0fbf8105ae4b175ede930cae43e14 100644 (file)
 
 #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
index 2307cdd2a23cd18ad324d223aefef231ea7cb857..8526f177450a56900c907a2e4cba3950fe5f9e00 100644 (file)
@@ -10,7 +10,6 @@
 
 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[];
index cb68ae44d597a6a97babfc0540a55035a692a12a..a54647c36b8501b7099c81fd5c4e9a6cba410787 100644 (file)
@@ -59,17 +59,18 @@ const struct bch_sb_field_ops bch_sb_field_ops_quota = {
        .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,
index 2f463874a3628238fb62c17f341d698f4fc3f3e6..884f601f41c425b711f14ea05be3c8fc26f12158 100644 (file)
@@ -8,7 +8,7 @@
 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);
 
index 6ba8574b4a69f8ea5f093a65fe9105700aee9678..6ee4d2e02073851a07ad1ef28eb02150e2a430e1 100644 (file)
@@ -354,7 +354,8 @@ static int do_rebalance(struct moving_context *ctxt)
                rebalance_wait(c);
        }
 
-       bch_err_fn(c, ret);
+       if (!bch2_err_matches(ret, EROFS))
+               bch_err_fn(c, ret);
        return ret;
 }
 
index 02025099c38fc5e2b284d4fff065c20622611711..f73338f37bf1a800325f11665d378d4c95bcc88b 100644 (file)
@@ -365,8 +365,10 @@ static int read_btree_roots(struct bch_fs *c)
                }
 
                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)
@@ -376,6 +378,7 @@ static int read_btree_roots(struct bch_fs *c)
                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))
@@ -714,6 +717,7 @@ int bch2_fs_recovery(struct bch_fs *c)
                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);
@@ -721,7 +725,9 @@ int bch2_fs_recovery(struct bch_fs *c)
                }
 
                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;
 
index dbbdf1955f7664ea43b74f8e902eb5de9dd48209..eb31df605c2e0c0f11fb71be8b399f0450adc360 100644 (file)
@@ -28,7 +28,7 @@ static inline unsigned bkey_type_to_indirect(const struct bkey *k)
 
 /* 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)
 {
@@ -75,7 +75,7 @@ bool bch2_reflink_p_merge(struct bch_fs *c, struct bkey_s _l, struct bkey_s_c _r
 
 /* 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)
 {
@@ -126,7 +126,7 @@ int bch2_trans_mark_reflink_v(struct btree_trans *trans,
 
 /* 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)
 {
index fe52538efb522940cd6cf7b853bc6d5d4afe3c94..8ccf3f9c4939eed45d9d9dc231bf5632506de836 100644 (file)
@@ -4,7 +4,7 @@
 
 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);
@@ -19,7 +19,7 @@ bool bch2_reflink_p_merge(struct bch_fs *, struct bkey_s, struct bkey_s_c);
        .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);
@@ -35,7 +35,7 @@ int bch2_trans_mark_reflink_v(struct btree_trans *, enum btree_id, unsigned,
        .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);
index cef2a0447b8601a191ac39852ae4a7e0d9fc317d..1c3ae13bfced1d8ce9eeee118cb6e9fe1552e7a5 100644 (file)
@@ -462,18 +462,13 @@ int bch2_replicas_gc_end(struct bch_fs *c, int ret)
 {
        lockdep_assert_held(&c->replicas_gc_lock);
 
-       if (ret)
-               goto err;
-
        mutex_lock(&c->sb_lock);
        percpu_down_write(&c->mark_lock);
 
-       ret = bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc);
-       if (ret)
-               goto err;
+       ret =   ret ?:
+               bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc) ?:
+               replicas_table_update(c, &c->replicas_gc);
 
-       ret = replicas_table_update(c, &c->replicas_gc);
-err:
        kfree(c->replicas_gc.entries);
        c->replicas_gc.entries = NULL;
 
@@ -579,12 +574,9 @@ retry:
 
        bch2_cpu_replicas_sort(&new);
 
-       ret = bch2_cpu_replicas_to_sb_replicas(c, &new);
-       if (ret)
-               goto err;
+       ret =   bch2_cpu_replicas_to_sb_replicas(c, &new) ?:
+               replicas_table_update(c, &new);
 
-       ret = replicas_table_update(c, &new);
-err:
        kfree(new.entries);
 
        percpu_up_write(&c->mark_lock);
index 61203d7c8d36054400ff0d1ee2e1838966168a73..9b6cc86d264a1f9e2412a552a0547c6d3f4d4f28 100644 (file)
@@ -82,6 +82,7 @@ int bch2_verify_superblock_clean(struct bch_fs *c,
        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))) {
@@ -119,6 +120,7 @@ int bch2_verify_superblock_clean(struct bch_fs *c,
                                    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,
@@ -140,6 +142,7 @@ struct bch_sb_field_clean *bch2_read_superblock_clean(struct bch_fs *c)
        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;
diff --git a/libbcachefs/sb-errors.c b/libbcachefs/sb-errors.c
new file mode 100644 (file)
index 0000000..3d66f15
--- /dev/null
@@ -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/libbcachefs/sb-errors.h b/libbcachefs/sb-errors.h
new file mode 100644 (file)
index 0000000..5a09a53
--- /dev/null
@@ -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/libbcachefs/sb-errors_types.h b/libbcachefs/sb-errors_types.h
new file mode 100644 (file)
index 0000000..b1c0998
--- /dev/null
@@ -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 */
+
index 6dd85bb996feed0c318595215d4d72f2c63b0371..032fe45481d3d088ecd2e13dee5f1c9e91fbef1c 100644 (file)
@@ -7,6 +7,18 @@
 #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)
@@ -36,7 +48,8 @@ static struct bch_member members_v1_get(struct bch_sb_field_members_v1 *mi, int
 {
        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)
@@ -71,7 +84,7 @@ static int sb_members_v2_resize_entries(struct bch_fs *c)
        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;
@@ -91,7 +104,7 @@ int bch2_members_v2_init(struct bch_fs *c)
        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;
@@ -155,7 +168,6 @@ static void member_to_text(struct printbuf *out,
        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);
@@ -163,6 +175,21 @@ static void member_to_text(struct printbuf *out,
 
        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);
@@ -173,6 +200,13 @@ static void member_to_text(struct printbuf *out,
        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);
@@ -211,21 +245,6 @@ static void member_to_text(struct printbuf *out,
                   : "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))
@@ -262,8 +281,7 @@ static int bch2_sb_members_v1_validate(struct bch_sb *sb,
        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;
        }
@@ -337,3 +355,72 @@ const struct bch_sb_field_ops bch_sb_field_ops_members_v2 = {
        .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);
+}
index 430f3457bfd46b2999fdc724176e2059a51b0d2f..1583e80afcbf3387c858d9941b4e9036c682947d 100644 (file)
@@ -2,8 +2,10 @@
 #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);
 
@@ -179,4 +181,42 @@ static inline struct bch_devs_mask bch2_online_devs(struct bch_fs *c)
 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 */
index 315e88cc3867f5bbbf13b9017f9eab8fa3234515..e9af77b384c76c694194c53b348706e354df9a22 100644 (file)
@@ -30,17 +30,18 @@ void bch2_snapshot_tree_to_text(struct printbuf *out, struct bch_fs *c,
                   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,
@@ -202,68 +203,60 @@ void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
                           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)
@@ -530,7 +523,7 @@ static int check_snapshot_tree(struct btree_trans *trans,
        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);
@@ -542,17 +535,20 @@ static int check_snapshot_tree(struct btree_trans *trans,
        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))) {
@@ -788,7 +784,9 @@ static int check_snapshot(struct btree_trans *trans,
                        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);
@@ -804,7 +802,8 @@ static int check_snapshot(struct btree_trans *trans,
        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)
@@ -816,7 +815,8 @@ static int check_snapshot(struct btree_trans *trans,
 
        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);
@@ -833,7 +833,8 @@ static int check_snapshot(struct btree_trans *trans,
 
        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);
@@ -1348,12 +1349,12 @@ static int bch2_fix_child_of_deleted_snapshot(struct btree_trans *trans,
                        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);
                        }
                }
@@ -1436,6 +1437,15 @@ int bch2_delete_dead_snapshots(struct bch_fs *c)
                if (!btree_type_has_snapshots(id))
                        continue;
 
+               /*
+                * deleted inodes btree is maintained by a trigger on the inodes
+                * btree - no work for us to do here, and it's not safe to scan
+                * it because we'll see out of date keys due to the btree write
+                * buffer:
+                */
+               if (id == BTREE_ID_deleted_inodes)
+                       continue;
+
                ret = for_each_btree_key_commit(trans, iter,
                                id, POS_MIN,
                                BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
index 01f006cac83105bbb2b15e0c550296bbe56f62fc..f09a22f4423969024ea29224340f6a1a528d2821 100644 (file)
@@ -5,7 +5,7 @@
 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) {       \
@@ -19,7 +19,7 @@ struct bkey_i_snapshot_tree *__bch2_snapshot_tree_create(struct btree_trans *);
 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);
index 73ba22c219a1c97f600a7ec0756fccf3463d85cd..fccd25aa32426a4233882a9d97cc214cba9dc6f5 100644 (file)
@@ -62,7 +62,8 @@ static int check_subvol(struct btree_trans *trans,
                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 =
@@ -97,16 +98,17 @@ int bch2_check_subvols(struct bch_fs *c)
 
 /* 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,
index bb14f92e8687185c4a702e643072695f28e2edf8..a1003d30ab0a0c613b644c54fba09964d9ec4b29 100644 (file)
@@ -9,7 +9,7 @@ enum bkey_invalid_flags;
 
 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);
 
index 332d41e1c0a32082988f70ded38bd004b35444c0..83bdb4368289f44a4675578b524346d7132872dc 100644 (file)
@@ -13,6 +13,7 @@
 #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"
@@ -805,7 +806,12 @@ static void write_super_endio(struct bio *bio)
 
        /* 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;
 
@@ -892,7 +898,9 @@ int bch2_write_super(struct bch_fs *c)
        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);
index b0d8584f475f1f2290b33e3120500cda726b4473..f5abd102bff7502bd2f142dfde8487c82f8aed29 100644 (file)
@@ -23,6 +23,11 @@ u64 bch2_upgrade_recovery_passes(struct bch_fs *c,
                                 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)
 
@@ -78,41 +83,6 @@ static inline void bch2_check_set_feature(struct bch_fs *c, unsigned feat)
                __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);
 
index ce59018b27acc92d70b75ba68ba6e2b1c4db643a..1b5c2a1bd68a26356b921baea4474791b72caa26 100644 (file)
@@ -49,6 +49,7 @@
 #include "recovery.h"
 #include "replicas.h"
 #include "sb-clean.h"
+#include "sb-errors.h"
 #include "sb-members.h"
 #include "snapshot.h"
 #include "subvolume.h"
@@ -400,7 +401,7 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
 
        bch_info(c, "going read-write");
 
-       ret = bch2_members_v2_init(c);
+       ret = bch2_sb_members_v2_init(c);
        if (ret)
                goto err;
 
@@ -481,6 +482,7 @@ static void __bch2_fs_free(struct bch_fs *c)
                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);
@@ -713,6 +715,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        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);
 
@@ -729,8 +732,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 
        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);
 
@@ -840,6 +843,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        }
 
        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) ?:
@@ -942,7 +946,7 @@ int bch2_fs_start(struct bch_fs *c)
 
        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;
@@ -1131,6 +1135,7 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
                                        struct bch_member *member)
 {
        struct bch_dev *ca;
+       unsigned i;
 
        ca = kzalloc(sizeof(*ca), GFP_KERNEL);
        if (!ca)
@@ -1148,6 +1153,10 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
        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,
@@ -1622,16 +1631,6 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
                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;
 
@@ -1645,6 +1644,8 @@ no_slot:
 
 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));
 
index db2727e5cc5feec1a9c4d9a4c53a689bd8a91201..8df45da5a9bf9b8a795623afdedd69ca99589701 100644 (file)
@@ -149,7 +149,9 @@ read_attribute(bucket_size);
 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);
@@ -880,7 +882,7 @@ static const char * const bch2_rw[] = {
        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;
 
@@ -923,8 +925,11 @@ SHOW(bch2_dev)
                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]));
@@ -991,6 +996,9 @@ STORE(bch2_dev)
                        return ret;
        }
 
+       if (attr == &sysfs_io_errors_reset)
+               bch2_dev_errors_reset(ca);
+
        return size;
 }
 SYSFS_OPS(bch2_dev);
@@ -1008,7 +1016,9 @@ struct attribute *bch2_dev_files[] = {
        &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,
index 74b41f567ab808f7c4a581cd0812e9de3867b46d..a39ff0c296ecfb2a000edd6aace20bdbb8db20ea 100644 (file)
@@ -70,46 +70,38 @@ const struct bch_hash_desc bch2_xattr_hash_desc = {
        .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;
-       }
-
-       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;
-       }
-
-       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;
-       }
-
-       return 0;
+       val_u64s = xattr_val_u64s(xattr.v->x_name_len,
+                                 le16_to_cpu(xattr.v->x_val_len) + 4);
+
+       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);
+
+       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);
+
+       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,
index f5a52e3a6016e7e67db20d01f1c78c18f1b5a228..1337f31a5c492c8401eefefdcd56c4a450d73514 100644 (file)
@@ -6,7 +6,7 @@
 
 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);