summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2016-05-02 22:03:12 -0800
committerKent Overstreet <kent.overstreet@gmail.com>2016-10-07 12:36:25 -0800
commit590ad7c71f8e28c67b6de99b02f4658ac57d2df9 (patch)
tree551d49d67f6cd69a1ff294a6d8db8099e566d2b2
parent1b9e33b00610b1cd53b4c35a1371d8eb0d247b1a (diff)
bcachefs: fsck improvements
Check for various inconsistencies on every mount - it's fast, and we really want to know if anyone is seeing them. Doesn't fix anything yet, because that has its own risk and none of these inconsistencies have been observed in the wild yet.
-rw-r--r--drivers/md/bcache/debug.c98
-rw-r--r--drivers/md/bcache/debug.h2
-rw-r--r--drivers/md/bcache/fs-gc.c103
-rw-r--r--drivers/md/bcache/fs-gc.h1
-rw-r--r--drivers/md/bcache/super.c2
5 files changed, 105 insertions, 101 deletions
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
index 9cb105cfd4cb..9d69872dc777 100644
--- a/drivers/md/bcache/debug.c
+++ b/drivers/md/bcache/debug.c
@@ -183,104 +183,6 @@ out_put:
bio_put(check);
}
-void bch_verify_inode_refs(struct cache_set *c)
-{
- struct btree_iter iter;
- struct bkey_s_c k;
- struct bkey_i_inode inode;
- u64 cur_inum = 0;
- u64 i_size, i_sectors;
- u16 i_mode;
- char buf[100];
-
- for_each_btree_key(&iter, c, BTREE_ID_EXTENTS,
- POS(BCACHE_ROOT_INO, 0), k) {
- if (k.k->type == KEY_TYPE_DISCARD ||
- k.k->type == BCH_RESERVATION)
- continue;
-
- if (k.k->p.inode != cur_inum &&
- bch_inode_find_by_inum(c, k.k->p.inode, &inode)) {
- bch_bkey_val_to_text(c, BTREE_ID_EXTENTS, buf,
- sizeof(buf), k);
- cache_set_inconsistent(c,
- "extent for missing inode %llu\n%s",
- k.k->p.inode, buf);
- bch_btree_iter_unlock(&iter);
- return;
- }
-
- if (k.k->p.inode != cur_inum) {
- BUG_ON(le32_to_cpu(inode.v.i_flags) & BCH_INODE_I_SIZE_DIRTY);
- BUG_ON(le32_to_cpu(inode.v.i_flags) & BCH_INODE_I_SECTORS_DIRTY);
-
- i_sectors = bch_count_inode_sectors(c, inode.k.p.inode);
- cache_set_inconsistent_on(i_sectors !=
- le64_to_cpu(inode.v.i_sectors), c,
- "i_sectors wrong: got %llu, should be %llu",
- le64_to_cpu(inode.v.i_sectors), i_sectors);
- }
-
- cur_inum = k.k->p.inode;
- i_mode = le16_to_cpu(inode.v.i_mode);
- i_size = le64_to_cpu(inode.v.i_size);
-
- if (!S_ISREG(i_mode) &&
- !S_ISLNK(i_mode))
- cache_set_inconsistent(c,
- "extent for non regular file, inode %llu mode %u",
- k.k->p.inode, i_mode);
-
- if (k.k->p.offset > round_up(i_size, PAGE_SIZE) >> 9) {
- bch_bkey_val_to_text(c, BTREE_ID_EXTENTS, buf,
- sizeof(buf), k);
- cache_set_inconsistent(c,
- "extent past end of inode %llu: i_size %llu extent\n%s",
- k.k->p.inode, i_size, buf);
- }
- }
- bch_btree_iter_unlock(&iter);
-
- for_each_btree_key(&iter, c, BTREE_ID_DIRENTS,
- POS(BCACHE_ROOT_INO, 0), k) {
- /* XXX: skipping whiteouts for now */
- if (k.k->type != BCH_DIRENT)
- continue;
-
- if (k.k->p.inode != cur_inum &&
- bch_inode_find_by_inum(c, k.k->p.inode, &inode)) {
- cache_set_inconsistent(c, "dirent for missing inode %llu",
- k.k->p.inode);
- bch_btree_iter_unlock(&iter);
- return;
- }
-
- cur_inum = k.k->p.inode;
- i_mode = le16_to_cpu(inode.v.i_mode);
-
- if (!S_ISDIR(i_mode))
- cache_set_inconsistent(c,
- "dirent for non directory, inode %llu mode %u",
- k.k->p.inode, i_mode);
- }
- bch_btree_iter_unlock(&iter);
-
- for_each_btree_key(&iter, c, BTREE_ID_XATTRS,
- POS(BCACHE_ROOT_INO, 0), k) {
- if (k.k->p.inode != cur_inum &&
- bch_inode_find_by_inum(c, k.k->p.inode, &inode)) {
- cache_set_inconsistent(c,
- "xattr for missing inode %llu",
- k.k->p.inode);
- bch_btree_iter_unlock(&iter);
- return;
- }
-
- cur_inum = k.k->p.inode;
- }
- bch_btree_iter_unlock(&iter);
-}
-
#endif
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/md/bcache/debug.h b/drivers/md/bcache/debug.h
index ad7c79a1e5ba..c8f3ae64bd7d 100644
--- a/drivers/md/bcache/debug.h
+++ b/drivers/md/bcache/debug.h
@@ -28,7 +28,6 @@ BCH_DEBUG_PARAMS_DEBUG()
void __bch_btree_verify(struct cache_set *, struct btree *);
void bch_data_verify(struct cached_dev *, struct bio *);
-void bch_verify_inode_refs(struct cache_set *);
#define bypass_torture_test(d) ((d)->bypass_torture_test)
@@ -41,7 +40,6 @@ BCH_DEBUG_PARAMS_DEBUG()
static inline void __bch_btree_verify(struct cache_set *c, struct btree *b) {}
static inline void bch_data_verify(struct cached_dev *dc, struct bio *bio) {}
-static inline void bch_verify_inode_refs(struct cache_set *c) {}
#define bypass_torture_test(d) 0
diff --git a/drivers/md/bcache/fs-gc.c b/drivers/md/bcache/fs-gc.c
index 168d56f90d99..b54105aa0521 100644
--- a/drivers/md/bcache/fs-gc.c
+++ b/drivers/md/bcache/fs-gc.c
@@ -271,3 +271,106 @@ int bch_gc_inode_nlinks(struct cache_set *c)
return ret;
}
+
+static inline bool next_inode(struct cache_set *c, struct bkey_s_c k,
+ u64 *cur_inum,
+ struct bkey_i_inode *inode,
+ struct bch_inode **bi,
+ u64 *i_size, u16 *i_mode)
+{
+ if (k.k->p.inode == *cur_inum)
+ return false;
+
+ if (!bch_inode_find_by_inum(c, k.k->p.inode, inode)) {
+ *i_mode = le16_to_cpu(inode->v.i_mode);
+ *i_size = le64_to_cpu(inode->v.i_size);
+ *bi = &inode->v;
+ } else {
+ *bi = NULL;
+ }
+
+ *cur_inum = k.k->p.inode;
+ return true;
+}
+
+#define fsck_err(c, fmt, ...) \
+do { \
+ bch_err(c, fmt, ##__VA_ARGS__); \
+} while (0)
+
+/*
+ * Checks for inconsistencies that shouldn't happen, unless we have a bug.
+ * Doesn't fix them yet, mainly because they haven't yet been observed:
+ */
+void bch_fsck(struct cache_set *c)
+{
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ struct bkey_i_inode inode;
+ struct bch_inode *bi = NULL;
+ u64 i_size = 0;
+ u16 i_mode = 0;
+ u64 cur_inum;
+ char buf[100];
+
+ cur_inum = -1;
+ for_each_btree_key(&iter, c, BTREE_ID_EXTENTS,
+ POS(BCACHE_ROOT_INO, 0), k) {
+ if (k.k->type == KEY_TYPE_DISCARD)
+ continue;
+
+ if (next_inode(c, k, &cur_inum, &inode, &bi,
+ &i_size, &i_mode) &&
+ bi &&
+ !(le32_to_cpu(bi->i_flags) & BCH_INODE_I_SECTORS_DIRTY)) {
+ u64 i_sectors = bch_count_inode_sectors(c, cur_inum);
+
+ if (i_sectors != le64_to_cpu(bi->i_sectors))
+ fsck_err(c,
+ "i_sectors wrong: got %llu, should be %llu",
+ le64_to_cpu(bi->i_sectors), i_sectors);
+ }
+
+ if (!S_ISREG(i_mode) &&
+ !S_ISLNK(i_mode))
+ fsck_err(c,
+ "extent type %u for non regular file, inode %llu mode %o",
+ k.k->type, k.k->p.inode, i_mode);
+
+ if (k.k->type != BCH_RESERVATION &&
+ k.k->p.offset > round_up(i_size, PAGE_SIZE) >> 9) {
+ bch_bkey_val_to_text(c, BTREE_ID_EXTENTS, buf,
+ sizeof(buf), k);
+ fsck_err(c,
+ "extent past end of inode %llu: i_size %llu extent\n%s",
+ k.k->p.inode, i_size, buf);
+ }
+ }
+ bch_btree_iter_unlock(&iter);
+
+ cur_inum = -1;
+ for_each_btree_key(&iter, c, BTREE_ID_DIRENTS,
+ POS(BCACHE_ROOT_INO, 0), k) {
+ next_inode(c, k, &cur_inum, &inode, &bi, &i_size, &i_mode);
+
+ if (!bi)
+ fsck_err(c, "dirent for missing inode %llu", k.k->p.inode);
+
+ if (!S_ISDIR(i_mode))
+ fsck_err(c,
+ "dirent for non directory, inode %llu mode %o",
+ k.k->p.inode, i_mode);
+ }
+ bch_btree_iter_unlock(&iter);
+
+ cur_inum = -1;
+ for_each_btree_key(&iter, c, BTREE_ID_XATTRS,
+ POS(BCACHE_ROOT_INO, 0), k) {
+ next_inode(c, k, &cur_inum, &inode, &bi, &i_size, &i_mode);
+
+ if (!bi)
+ fsck_err(c, "xattr for missing inode %llu",
+ k.k->p.inode);
+ }
+ bch_btree_iter_unlock(&iter);
+}
diff --git a/drivers/md/bcache/fs-gc.h b/drivers/md/bcache/fs-gc.h
index 9e0a34cc2e5f..04f08978af3a 100644
--- a/drivers/md/bcache/fs-gc.h
+++ b/drivers/md/bcache/fs-gc.h
@@ -3,5 +3,6 @@
s64 bch_count_inode_sectors(struct cache_set *, u64);
int bch_gc_inode_nlinks(struct cache_set *);
+void bch_fsck(struct cache_set *);
#endif /* _BCACHE_FS_GC_H */
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 61394008e18f..4aa5aa0abbd9 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1362,7 +1362,7 @@ static const char *run_cache_set(struct cache_set *c)
if (bch_gc_inode_nlinks(c))
goto err;
- bch_verify_inode_refs(c);
+ bch_fsck(c);
} else {
struct bkey_i_inode inode;
struct closure cl;