summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/backpointers.c47
-rw-r--r--fs/bcachefs/bcachefs_format.h6
-rw-r--r--fs/bcachefs/btree_io.c6
-rw-r--r--fs/bcachefs/dirent.c2
-rw-r--r--fs/bcachefs/extent_update.c8
-rw-r--r--fs/bcachefs/fsck.c5
-rw-r--r--fs/bcachefs/inode.c11
-rw-r--r--fs/bcachefs/inode.h2
-rw-r--r--fs/bcachefs/inode_format.h3
-rw-r--r--fs/bcachefs/journal.c2
-rw-r--r--fs/bcachefs/migrate.c4
-rw-r--r--fs/bcachefs/move.c4
-rw-r--r--fs/bcachefs/namei.c3
-rw-r--r--fs/bcachefs/opts.h7
-rw-r--r--fs/bcachefs/recovery.c1
-rw-r--r--fs/bcachefs/recovery_passes_format.h2
-rw-r--r--fs/bcachefs/sb-counters_format.h10
-rw-r--r--fs/bcachefs/snapshot.c3
-rw-r--r--fs/bcachefs/snapshot.h15
-rw-r--r--fs/bcachefs/str_hash.h14
-rw-r--r--fs/bcachefs/super-io.c53
-rw-r--r--fs/bcachefs/super-io.h3
-rw-r--r--fs/bcachefs/super.c6
-rw-r--r--fs/bcachefs/xattr.c19
-rw-r--r--mm/shrinker.c4
25 files changed, 172 insertions, 68 deletions
diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c
index 45d3db41225a..cb25cddb759b 100644
--- a/fs/bcachefs/backpointers.c
+++ b/fs/bcachefs/backpointers.c
@@ -532,10 +532,6 @@ static int check_bp_exists(struct btree_trans *trans,
struct btree_iter other_extent_iter = {};
CLASS(printbuf, buf)();
- if (bpos_lt(bp->k.p, s->bp_start) ||
- bpos_gt(bp->k.p, s->bp_end))
- return 0;
-
CLASS(btree_iter, bp_iter)(trans, BTREE_ID_backpointers, bp->k.p, 0);
struct bkey_s_c bp_k = bch2_btree_iter_peek_slot(&bp_iter);
int ret = bkey_err(bp_k);
@@ -690,6 +686,10 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
struct bkey_i_backpointer bp;
bch2_extent_ptr_to_bp(c, btree, level, k, p, entry, &bp);
+ if (bpos_lt(bp.k.p, s->bp_start) ||
+ bpos_gt(bp.k.p, s->bp_end))
+ continue;
+
int ret = !empty
? check_bp_exists(trans, s, &bp, k)
: bch2_bucket_backpointer_mod(trans, k, &bp, true);
@@ -897,7 +897,7 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(bp_k);
- if (c->sb.version_upgrade_complete >= bcachefs_metadata_version_backpointer_bucket_gen &&
+ if (c->sb.version_upgrade_complete < bcachefs_metadata_version_backpointer_bucket_gen &&
(bp.v->bucket_gen != a->gen ||
bp.v->pad)) {
ret = bch2_backpointer_del(trans, bp_k.k->p);
@@ -929,6 +929,14 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
if (sectors[ALLOC_dirty] != a->dirty_sectors ||
sectors[ALLOC_cached] != a->cached_sectors ||
sectors[ALLOC_stripe] != a->stripe_sectors) {
+ /*
+ * Post 1.14 upgrade, we assume that backpointers are mostly
+ * correct and a sector count mismatch is probably due to a
+ * write buffer race
+ *
+ * Pre upgrade, we expect all the buckets to be wrong, a write
+ * buffer flush is pointless:
+ */
if (c->sb.version_upgrade_complete >= bcachefs_metadata_version_backpointer_bucket_gen) {
ret = bch2_backpointers_maybe_flush(trans, alloc_k, last_flushed);
if (ret)
@@ -976,12 +984,22 @@ static bool backpointer_node_has_missing(struct bch_fs *c, struct bkey_s_c k)
goto next;
struct bpos bucket = bp_pos_to_bucket(ca, pos);
- u64 next = ca->mi.nbuckets;
-
- unsigned long *bitmap = READ_ONCE(ca->bucket_backpointer_mismatch.buckets);
- if (bitmap)
- next = min_t(u64, next,
- find_next_bit(bitmap, ca->mi.nbuckets, bucket.offset));
+ u64 next = min(bucket.offset, ca->mi.nbuckets);
+
+ unsigned long *mismatch = READ_ONCE(ca->bucket_backpointer_mismatch.buckets);
+ unsigned long *empty = READ_ONCE(ca->bucket_backpointer_empty.buckets);
+ /*
+ * Find the first bucket with mismatches - but
+ * not empty buckets; we don't need to pin those
+ * because we just recreate all backpointers in
+ * those buckets
+ */
+ if (mismatch && empty)
+ next = find_next_andnot_bit(mismatch, empty, ca->mi.nbuckets, next);
+ else if (mismatch)
+ next = find_next_bit(mismatch, ca->mi.nbuckets, next);
+ else
+ next = ca->mi.nbuckets;
bucket.offset = next;
if (bucket.offset == ca->mi.nbuckets)
@@ -1108,17 +1126,18 @@ int bch2_check_extents_to_backpointers(struct bch_fs *c)
if (ret)
goto err;
- u64 nr_buckets = 0, nr_mismatches = 0;
+ u64 nr_buckets = 0, nr_mismatches = 0, nr_empty = 0;
for_each_member_device(c, ca) {
nr_buckets += ca->mi.nbuckets;
nr_mismatches += ca->bucket_backpointer_mismatch.nr;
+ nr_empty += ca->bucket_backpointer_empty.nr;
}
if (!nr_mismatches)
goto err;
- bch_info(c, "scanning for missing backpointers in %llu/%llu buckets",
- nr_mismatches, nr_buckets);
+ bch_info(c, "scanning for missing backpointers in %llu/%llu buckets, %llu buckets with no backpointers",
+ nr_mismatches - nr_empty, nr_buckets, nr_empty);
while (1) {
ret = bch2_pin_backpointer_nodes_with_missing(trans, s.bp_start, &s.bp_end);
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index 7a0b602c1b27..b2de993d802b 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/bcachefs_format.h
@@ -706,7 +706,8 @@ struct bch_sb_field_ext {
x(snapshot_deletion_v2, BCH_VERSION(1, 26)) \
x(fast_device_removal, BCH_VERSION(1, 27)) \
x(inode_has_case_insensitive, BCH_VERSION(1, 28)) \
- x(extent_snapshot_whiteouts, BCH_VERSION(1, 29))
+ x(extent_snapshot_whiteouts, BCH_VERSION(1, 29)) \
+ x(31bit_dirent_offset, BCH_VERSION(1, 30))
enum bcachefs_metadata_version {
bcachefs_metadata_version_min = 9,
@@ -1378,7 +1379,8 @@ enum btree_id_flags {
BIT_ULL(KEY_TYPE_alloc_v4)) \
x(quotas, 5, 0, \
BIT_ULL(KEY_TYPE_quota)) \
- x(stripes, 6, 0, \
+ x(stripes, 6, \
+ BTREE_IS_data, \
BIT_ULL(KEY_TYPE_stripe)) \
x(reflink, 7, \
BTREE_IS_extents| \
diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c
index 276cf088539e..b1f58fb31ed1 100644
--- a/fs/bcachefs/btree_io.c
+++ b/fs/bcachefs/btree_io.c
@@ -131,10 +131,10 @@ static void *btree_bounce_alloc(struct bch_fs *c, size_t size,
BUG_ON(size > c->opts.btree_node_size);
*used_mempool = false;
- p = kvmalloc(size, GFP_NOWAIT);
+ p = kvmalloc(size, GFP_NOWAIT|__GFP_ACCOUNT|__GFP_RECLAIMABLE);
if (!p) {
*used_mempool = true;
- p = mempool_alloc(&c->btree_bounce_pool, GFP_NOFS);
+ p = mempool_alloc(&c->btree_bounce_pool, GFP_NOFS|__GFP_ACCOUNT|__GFP_RECLAIMABLE);
}
memalloc_nofs_restore(flags);
return p;
@@ -1470,7 +1470,7 @@ start:
}
prt_newline(&buf);
- if (failed.nr)
+ if (ret || failed.nr)
bch2_print_str_ratelimited(c, KERN_ERR, buf.buf);
async_object_list_del(c, btree_read_bio, rb->list_idx);
diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c
index cb44b35e0f1d..fe6f3d874a47 100644
--- a/fs/bcachefs/dirent.c
+++ b/fs/bcachefs/dirent.c
@@ -95,7 +95,7 @@ static u64 bch2_dirent_hash(const struct bch_hash_info *info,
bch2_str_hash_update(&ctx, info, name->name, name->len);
/* [0,2) reserved for dots */
- return max_t(u64, bch2_str_hash_end(&ctx, info), 2);
+ return max_t(u64, bch2_str_hash_end(&ctx, info, true), 2);
}
static u64 dirent_hash_key(const struct bch_hash_info *info, const void *key)
diff --git a/fs/bcachefs/extent_update.c b/fs/bcachefs/extent_update.c
index 7ddb156c765c..73eb28090bc7 100644
--- a/fs/bcachefs/extent_update.c
+++ b/fs/bcachefs/extent_update.c
@@ -115,9 +115,15 @@ int bch2_extent_trim_atomic(struct btree_trans *trans,
copy.flags |= BTREE_ITER_nofilter_whiteouts;
+ /*
+ * We're doing our own whiteout filtering, but we still need to pass a
+ * max key to avoid popping an assert in bch2_snapshot_is_ancestor():
+ */
struct bkey_s_c k;
unsigned nr_iters = 0;
- for_each_btree_key_continue_norestart(copy, 0, k, ret) {
+ for_each_btree_key_max_continue_norestart(copy,
+ POS(insert->k.p.inode, U64_MAX),
+ 0, k, ret) {
unsigned offset = 0;
if (bkey_gt(iter->pos, bkey_start_pos(k.k)))
diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c
index 01c1c6372229..ccc44b1fc178 100644
--- a/fs/bcachefs/fsck.c
+++ b/fs/bcachefs/fsck.c
@@ -266,7 +266,8 @@ create_lostfound:
root_inode.bi_nlink++;
- ret = bch2_inode_create(trans, &lostfound_iter, lostfound, snapshot, cpu);
+ ret = bch2_inode_create(trans, &lostfound_iter, lostfound, snapshot, cpu,
+ inode_opt_get(c, &root_inode, inodes_32bit));
if (ret)
goto err;
@@ -573,7 +574,7 @@ static int reconstruct_subvol(struct btree_trans *trans, u32 snapshotid, u32 sub
new_inode.bi_subvol = subvolid;
- int ret = bch2_inode_create(trans, &inode_iter, &new_inode, snapshotid, cpu) ?:
+ int ret = bch2_inode_create(trans, &inode_iter, &new_inode, snapshotid, cpu, false) ?:
bch2_btree_iter_traverse(&inode_iter) ?:
bch2_inode_write(trans, &inode_iter, &new_inode);
bch2_trans_iter_exit(&inode_iter);
diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c
index d5e5190f0663..4aa130ff7cf6 100644
--- a/fs/bcachefs/inode.c
+++ b/fs/bcachefs/inode.c
@@ -944,11 +944,12 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
}
static struct bkey_i_inode_alloc_cursor *
-bch2_inode_alloc_cursor_get(struct btree_trans *trans, u64 cpu, u64 *min, u64 *max)
+bch2_inode_alloc_cursor_get(struct btree_trans *trans, u64 cpu, u64 *min, u64 *max,
+ bool is_32bit)
{
struct bch_fs *c = trans->c;
- u64 cursor_idx = c->opts.inodes_32bit ? 0 : cpu + 1;
+ u64 cursor_idx = is_32bit ? 0 : cpu + 1;
cursor_idx &= ~(~0ULL << c->opts.shard_inode_numbers_bits);
@@ -967,7 +968,7 @@ bch2_inode_alloc_cursor_get(struct btree_trans *trans, u64 cpu, u64 *min, u64 *m
if (IS_ERR(cursor))
return cursor;
- if (c->opts.inodes_32bit) {
+ if (is_32bit) {
*min = BLOCKDEV_INODE_MAX;
*max = INT_MAX;
} else {
@@ -996,11 +997,11 @@ bch2_inode_alloc_cursor_get(struct btree_trans *trans, u64 cpu, u64 *min, u64 *m
int bch2_inode_create(struct btree_trans *trans,
struct btree_iter *iter,
struct bch_inode_unpacked *inode_u,
- u32 snapshot, u64 cpu)
+ u32 snapshot, u64 cpu, bool is_32bit)
{
u64 min, max;
struct bkey_i_inode_alloc_cursor *cursor =
- bch2_inode_alloc_cursor_get(trans, cpu, &min, &max);
+ bch2_inode_alloc_cursor_get(trans, cpu, &min, &max, is_32bit);
int ret = PTR_ERR_OR_ZERO(cursor);
if (ret)
return ret;
diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h
index b8ec3e628d90..79092ea74844 100644
--- a/fs/bcachefs/inode.h
+++ b/fs/bcachefs/inode.h
@@ -172,7 +172,7 @@ void bch2_inode_init(struct bch_fs *, struct bch_inode_unpacked *,
struct bch_inode_unpacked *);
int bch2_inode_create(struct btree_trans *, struct btree_iter *,
- struct bch_inode_unpacked *, u32, u64);
+ struct bch_inode_unpacked *, u32, u64, bool);
int bch2_inode_rm(struct bch_fs *, subvol_inum);
diff --git a/fs/bcachefs/inode_format.h b/fs/bcachefs/inode_format.h
index 1f00938b1bdc..e07fa6cc99bd 100644
--- a/fs/bcachefs/inode_format.h
+++ b/fs/bcachefs/inode_format.h
@@ -144,7 +144,8 @@ enum inode_opt_id {
x(unlinked, 7) \
x(backptr_untrusted, 8) \
x(has_child_snapshot, 9) \
- x(has_case_insensitive, 10)
+ x(has_case_insensitive, 10) \
+ x(31bit_dirent_offset, 11)
/* bits 20+ reserved for packed fields below: */
diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c
index 07869436a964..d56959f12210 100644
--- a/fs/bcachefs/journal.c
+++ b/fs/bcachefs/journal.c
@@ -737,9 +737,9 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res,
return ret;
CLASS(printbuf, buf)();
+ prt_printf(&buf, bch2_fmt(c, "Journal stuck? Waited for 10 seconds, err %s"), bch2_err_str(ret));
bch2_journal_debug_to_text(&buf, j);
bch2_print_str(c, KERN_ERR, buf.buf);
- prt_printf(&buf, bch2_fmt(c, "Journal stuck? Waited for 10 seconds, err %s"), bch2_err_str(ret));
closure_wait_event(&j->async_wait,
!bch2_err_matches(ret = __journal_res_get(j, res, flags), BCH_ERR_operation_blocked) ||
diff --git a/fs/bcachefs/migrate.c b/fs/bcachefs/migrate.c
index a66d01d04e57..892990b4a6a6 100644
--- a/fs/bcachefs/migrate.c
+++ b/fs/bcachefs/migrate.c
@@ -125,6 +125,10 @@ static int bch2_dev_usrdata_drop(struct bch_fs *c,
if (!btree_type_has_ptrs(id))
continue;
+ /* Stripe keys have pointers, but are handled separately */
+ if (id == BTREE_ID_stripes)
+ continue;
+
int ret = for_each_btree_key_commit(trans, iter, id, POS_MIN,
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c
index df6833416855..4f41f1f6ec6c 100644
--- a/fs/bcachefs/move.c
+++ b/fs/bcachefs/move.c
@@ -819,7 +819,9 @@ static int bch2_move_data(struct bch_fs *c,
unsigned min_depth_this_btree = min_depth;
- if (!btree_type_has_ptrs(id))
+ /* Stripe keys have pointers, but are handled separately */
+ if (!btree_type_has_ptrs(id) ||
+ id == BTREE_ID_stripes)
min_depth_this_btree = max(min_depth_this_btree, 1);
for (unsigned level = min_depth_this_btree;
diff --git a/fs/bcachefs/namei.c b/fs/bcachefs/namei.c
index d1019052f182..5c321a0d1f89 100644
--- a/fs/bcachefs/namei.c
+++ b/fs/bcachefs/namei.c
@@ -62,7 +62,8 @@ int bch2_create_trans(struct btree_trans *trans,
if (flags & BCH_CREATE_TMPFILE)
new_inode->bi_flags |= BCH_INODE_unlinked;
- ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
+ ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu,
+ inode_opt_get(c, dir_u, inodes_32bit));
if (ret)
goto err;
diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
index 84ce69a7f131..31a3abcbd83e 100644
--- a/fs/bcachefs/opts.h
+++ b/fs/bcachefs/opts.h
@@ -242,7 +242,7 @@ enum fsck_err_opts {
x(inodes_32bit, u8, \
OPT_FS|OPT_INODE|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME, \
OPT_BOOL(), \
- BCH_SB_INODE_32BIT, true, \
+ BCH_SB_INODE_32BIT, false, \
NULL, "Constrain inode numbers to 32 bits") \
x(shard_inode_numbers_bits, u8, \
OPT_FS|OPT_FORMAT, \
@@ -321,6 +321,11 @@ enum fsck_err_opts {
OPT_BOOL(), \
BCH2_NO_SB_OPT, false, \
NULL, "Don't kick drives out when splitbrain detected")\
+ x(no_version_check, u8, \
+ OPT_HIDDEN, \
+ OPT_BOOL(), \
+ BCH2_NO_SB_OPT, false, \
+ NULL, "Don't fail reading the superblock due to incompatible version")\
x(verbose, u8, \
OPT_FS|OPT_MOUNT|OPT_RUNTIME, \
OPT_BOOL(), \
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 29e81f96db0f..8280ca333f5b 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -64,7 +64,6 @@ int bch2_btree_lost_data(struct bch_fs *c,
* but in debug mode we want the next fsck run to be clean:
*/
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_lrus, 0, &write_sb) ?: ret;
- ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_backpointers_to_extents, 0, &write_sb) ?: ret;
#endif
write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_lru_entry_bad, ext->errors_silent);
diff --git a/fs/bcachefs/recovery_passes_format.h b/fs/bcachefs/recovery_passes_format.h
index b63c20558d3d..2696eee00345 100644
--- a/fs/bcachefs/recovery_passes_format.h
+++ b/fs/bcachefs/recovery_passes_format.h
@@ -37,7 +37,7 @@
x(check_alloc_info, 10, PASS_ONLINE|PASS_FSCK_ALLOC) \
x(check_lrus, 11, PASS_ONLINE|PASS_FSCK_ALLOC) \
x(check_btree_backpointers, 12, PASS_ONLINE|PASS_FSCK_ALLOC) \
- x(check_backpointers_to_extents, 13, PASS_ONLINE|PASS_FSCK_DEBUG) \
+ x(check_backpointers_to_extents, 13, PASS_ONLINE) \
x(check_extents_to_backpointers, 14, PASS_ONLINE|PASS_FSCK_ALLOC) \
x(check_alloc_to_lru_refs, 15, PASS_ONLINE|PASS_FSCK_ALLOC) \
x(fs_freespace_init, 16, PASS_ALWAYS|PASS_SILENT) \
diff --git a/fs/bcachefs/sb-counters_format.h b/fs/bcachefs/sb-counters_format.h
index 44bc12573a0c..bfeb713dd210 100644
--- a/fs/bcachefs/sb-counters_format.h
+++ b/fs/bcachefs/sb-counters_format.h
@@ -22,7 +22,7 @@ enum counters_flags {
x(io_read_split, 33, TYPE_COUNTER) \
x(io_read_reuse_race, 34, TYPE_COUNTER) \
x(io_read_retry, 32, TYPE_COUNTER) \
- x(io_read_fail_and_poison, 82, TYPE_COUNTER) \
+ x(io_read_fail_and_poison, 95, TYPE_COUNTER) \
x(io_write, 1, TYPE_SECTORS) \
x(io_move, 2, TYPE_SECTORS) \
x(io_move_read, 35, TYPE_SECTORS) \
@@ -124,4 +124,12 @@ struct bch_sb_field_counters {
__le64 d[];
};
+static inline void __maybe_unused check_bch_counter_ids_unique(void) {
+ switch(0){
+#define x(t, n, ...) case (n):
+ BCH_PERSISTENT_COUNTERS()
+#undef x
+ }
+}
+
#endif /* _BCACHEFS_SB_COUNTERS_FORMAT_H */
diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c
index 84f987d3a02a..eab0c1e3ff56 100644
--- a/fs/bcachefs/snapshot.c
+++ b/fs/bcachefs/snapshot.c
@@ -1673,7 +1673,8 @@ static int bch2_fix_child_of_deleted_snapshot(struct btree_trans *trans,
return ret;
darray_for_each(*deleted, i)
- nr_deleted_ancestors += bch2_snapshot_is_ancestor(c, s->k.p.offset, i->id);
+ nr_deleted_ancestors += bch2_snapshots_same_tree(c, s->k.p.offset, i->id) &&
+ bch2_snapshot_is_ancestor(c, s->k.p.offset, i->id);
if (!nr_deleted_ancestors)
return 0;
diff --git a/fs/bcachefs/snapshot.h b/fs/bcachefs/snapshot.h
index fef32a0118c4..28d9a29a1fd0 100644
--- a/fs/bcachefs/snapshot.h
+++ b/fs/bcachefs/snapshot.h
@@ -51,6 +51,17 @@ static inline u32 bch2_snapshot_tree(struct bch_fs *c, u32 id)
return s ? s->tree : 0;
}
+static inline bool bch2_snapshots_same_tree(struct bch_fs *c, u32 id1, u32 id2)
+{
+ if (id1 == id2)
+ return true;
+
+ guard(rcu)();
+ const struct snapshot_t *s1 = snapshot_t(c, id1);
+ const struct snapshot_t *s2 = snapshot_t(c, id2);
+ return s1 && s2 && s1->tree == s2->tree;
+}
+
static inline u32 __bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s = snapshot_t(c, id);
@@ -157,6 +168,10 @@ bool __bch2_snapshot_is_ancestor(struct bch_fs *, u32, u32);
static inline bool bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
{
+ EBUG_ON(!id);
+ EBUG_ON(!ancestor);
+ EBUG_ON(!bch2_snapshots_same_tree(c, id, ancestor));
+
return id == ancestor
? true
: __bch2_snapshot_is_ancestor(c, id, ancestor);
diff --git a/fs/bcachefs/str_hash.h b/fs/bcachefs/str_hash.h
index 8c0fb44929cc..2a61cc36ddbf 100644
--- a/fs/bcachefs/str_hash.h
+++ b/fs/bcachefs/str_hash.h
@@ -34,6 +34,7 @@ bch2_str_hash_opt_to_type(struct bch_fs *c, enum bch_str_hash_opts opt)
struct bch_hash_info {
u32 inum_snapshot;
u8 type;
+ bool is_31bit;
struct unicode_map *cf_encoding;
/*
* For crc32 or crc64 string hashes the first key value of
@@ -48,6 +49,7 @@ bch2_hash_info_init(struct bch_fs *c, const struct bch_inode_unpacked *bi)
struct bch_hash_info info = {
.inum_snapshot = bi->bi_snapshot,
.type = INODE_STR_HASH(bi),
+ .is_31bit = bi->bi_flags & BCH_INODE_31bit_dirent_offset,
.cf_encoding = bch2_inode_casefold(c, bi) ? c->cf_encoding : NULL,
.siphash_key = { .k0 = bi->bi_hash_seed }
};
@@ -112,8 +114,8 @@ static inline void bch2_str_hash_update(struct bch_str_hash_ctx *ctx,
}
}
-static inline u64 bch2_str_hash_end(struct bch_str_hash_ctx *ctx,
- const struct bch_hash_info *info)
+static inline u64 __bch2_str_hash_end(struct bch_str_hash_ctx *ctx,
+ const struct bch_hash_info *info)
{
switch (info->type) {
case BCH_STR_HASH_crc32c:
@@ -128,6 +130,14 @@ static inline u64 bch2_str_hash_end(struct bch_str_hash_ctx *ctx,
}
}
+static inline u64 bch2_str_hash_end(struct bch_str_hash_ctx *ctx,
+ const struct bch_hash_info *info,
+ bool maybe_31bit)
+{
+ return __bch2_str_hash_end(ctx, info) &
+ (maybe_31bit && info->is_31bit ? INT_MAX : U64_MAX);
+}
+
struct bch_hash_desc {
enum btree_id btree_id;
u8 key_type;
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index be7ed612d28f..5897380c4c08 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -89,7 +89,7 @@ int bch2_set_version_incompat(struct bch_fs *c, enum bcachefs_metadata_version v
prt_str(&buf, "requested incompat feature ");
bch2_version_to_text(&buf, version);
prt_str(&buf, " currently not enabled, allowed up to ");
- bch2_version_to_text(&buf, version);
+ bch2_version_to_text(&buf, c->sb.version_incompat_allowed);
prt_printf(&buf, "\n set version_upgrade=incompat to enable");
bch_notice(c, "%s", buf.buf);
@@ -379,7 +379,7 @@ static int bch2_sb_compatible(struct bch_sb *sb, struct printbuf *out)
return 0;
}
-int bch2_sb_validate(struct bch_sb *sb, u64 read_offset,
+int bch2_sb_validate(struct bch_sb *sb, struct bch_opts *opts, u64 read_offset,
enum bch_validate_flags flags, struct printbuf *out)
{
enum bch_opt_id opt_id;
@@ -389,28 +389,30 @@ int bch2_sb_validate(struct bch_sb *sb, u64 read_offset,
if (ret)
return ret;
- u64 incompat = le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR);
- unsigned incompat_bit = 0;
- if (incompat)
- incompat_bit = __ffs64(incompat);
- else if (sb->features[1])
- incompat_bit = 64 + __ffs64(le64_to_cpu(sb->features[1]));
-
- if (incompat_bit) {
- prt_printf(out, "Filesystem has incompatible feature bit %u, highest supported %s (%u)",
- incompat_bit,
- bch2_sb_features[BCH_FEATURE_NR - 1],
- BCH_FEATURE_NR - 1);
- return -BCH_ERR_invalid_sb_features;
- }
+ if (!opts->no_version_check) {
+ u64 incompat = le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR);
+ unsigned incompat_bit = 0;
+ if (incompat)
+ incompat_bit = __ffs64(incompat);
+ else if (sb->features[1])
+ incompat_bit = 64 + __ffs64(le64_to_cpu(sb->features[1]));
+
+ if (incompat_bit) {
+ prt_printf(out, "Filesystem has incompatible feature bit %u, highest supported %s (%u)",
+ incompat_bit,
+ bch2_sb_features[BCH_FEATURE_NR - 1],
+ BCH_FEATURE_NR - 1);
+ return -BCH_ERR_invalid_sb_features;
+ }
- if (BCH_VERSION_MAJOR(le16_to_cpu(sb->version)) > BCH_VERSION_MAJOR(bcachefs_metadata_version_current) ||
- BCH_SB_VERSION_INCOMPAT(sb) > bcachefs_metadata_version_current) {
- prt_str(out, "Filesystem has incompatible version ");
- bch2_version_to_text(out, le16_to_cpu(sb->version));
- prt_str(out, ", current version ");
- bch2_version_to_text(out, bcachefs_metadata_version_current);
- return -BCH_ERR_invalid_sb_features;
+ if (BCH_VERSION_MAJOR(le16_to_cpu(sb->version)) > BCH_VERSION_MAJOR(bcachefs_metadata_version_current) ||
+ BCH_SB_VERSION_INCOMPAT(sb) > bcachefs_metadata_version_current) {
+ prt_str(out, "Filesystem has incompatible version ");
+ bch2_version_to_text(out, le16_to_cpu(sb->version));
+ prt_str(out, ", current version ");
+ bch2_version_to_text(out, bcachefs_metadata_version_current);
+ return -BCH_ERR_invalid_sb_features;
+ }
}
if (bch2_is_zero(sb->user_uuid.b, sizeof(sb->user_uuid))) {
@@ -915,7 +917,7 @@ got_super:
sb->have_layout = true;
- ret = bch2_sb_validate(sb->sb, offset, 0, &err);
+ ret = bch2_sb_validate(sb->sb, opts, offset, 0, &err);
if (ret) {
bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error validating superblock: %s\n",
path, err.buf);
@@ -1081,9 +1083,10 @@ int bch2_write_super(struct bch_fs *c)
bch2_sb_from_fs(c, (*ca));
darray_for_each(online_devices, ca) {
+ struct bch_opts opts = bch2_opts_empty();
printbuf_reset(&err);
- ret = bch2_sb_validate((*ca)->disk_sb.sb, 0, BCH_VALIDATE_write, &err);
+ ret = bch2_sb_validate((*ca)->disk_sb.sb, &opts, 0, BCH_VALIDATE_write, &err);
if (ret) {
bch2_fs_inconsistent(c, "sb invalid before write: %s", err.buf);
goto out;
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index a3b7a90f2533..82cb3a3ceeae 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/super-io.h
@@ -92,7 +92,8 @@ int bch2_sb_from_fs(struct bch_fs *, struct bch_dev *);
void bch2_free_super(struct bch_sb_handle *);
int bch2_sb_realloc(struct bch_sb_handle *, unsigned);
-int bch2_sb_validate(struct bch_sb *, u64, enum bch_validate_flags, struct printbuf *);
+int bch2_sb_validate(struct bch_sb *, struct bch_opts *, u64,
+ enum bch_validate_flags, struct printbuf *);
int bch2_read_super(const char *, struct bch_opts *, struct bch_sb_handle *);
int bch2_read_super_silent(const char *, struct bch_opts *, struct bch_sb_handle *);
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 09e7f8ae9922..ee3b30b1c2b5 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1021,6 +1021,12 @@ static int bch2_fs_opt_version_init(struct bch_fs *c)
prt_bitflags(&p, bch2_recovery_passes, sb_passes);
}
+ u64 btrees_lost_data = le64_to_cpu(ext->btrees_lost_data);
+ if (btrees_lost_data) {
+ prt_str(&p, "\nsuperblock indicates damage to following btrees:\n ");
+ prt_bitflags(&p, __bch2_btree_ids, btrees_lost_data);
+ }
+
if (bch2_check_version_downgrade(c)) {
prt_str(&p, "\nVersion downgrade required:");
diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c
index 6094b568dd33..6d7303008b19 100644
--- a/fs/bcachefs/xattr.c
+++ b/fs/bcachefs/xattr.c
@@ -4,6 +4,7 @@
#include "acl.h"
#include "bkey_methods.h"
#include "btree_update.h"
+#include "dirent.h"
#include "extents.h"
#include "fs.h"
#include "rebalance.h"
@@ -25,7 +26,7 @@ static u64 bch2_xattr_hash(const struct bch_hash_info *info,
bch2_str_hash_update(&ctx, info, &key->type, sizeof(key->type));
bch2_str_hash_update(&ctx, info, key->name.name, key->name.len);
- return bch2_str_hash_end(&ctx, info);
+ return bch2_str_hash_end(&ctx, info, false);
}
static u64 xattr_hash_key(const struct bch_hash_info *info, const void *key)
@@ -484,6 +485,22 @@ static int inode_opt_set_fn(struct btree_trans *trans,
return ret;
}
+ if (s->id == Inode_opt_inodes_32bit &&
+ !bch2_request_incompat_feature(trans->c, bcachefs_metadata_version_31bit_dirent_offset)) {
+ /*
+ * Make sure the dir is empty, as otherwise we'd need to
+ * rehash everything and update the dirent keys.
+ */
+ int ret = bch2_empty_dir_trans(trans, inode_inum(inode));
+ if (ret < 0)
+ return ret;
+
+ if (s->defined)
+ bi->bi_flags |= BCH_INODE_31bit_dirent_offset;
+ else
+ bi->bi_flags &= ~BCH_INODE_31bit_dirent_offset;
+ }
+
if (s->defined)
bi->bi_fields_set |= 1U << s->id;
else
diff --git a/mm/shrinker.c b/mm/shrinker.c
index c94eedf2cfd8..4a76364d2b7e 100644
--- a/mm/shrinker.c
+++ b/mm/shrinker.c
@@ -833,7 +833,9 @@ void shrinker_to_text(struct seq_buf *out, struct shrinker *shrinker)
};
unsigned long nr_freed = atomic_long_read(&shrinker->objects_freed);
- seq_buf_puts(out, shrinker->name);
+ seq_buf_printf(out, "%ps", shrinker->scan_objects);
+ if (shrinker->name)
+ seq_buf_printf(out, ": %s", shrinker->name);
seq_buf_putc(out, '\n');
seq_buf_printf(out, "objects: %lu\n", shrinker->count_objects(shrinker, &sc));