Commit ff0905bb authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'bcachefs-2025-06-04' of git://evilpiepirate.org/bcachefs

Pull more bcachefs updates from Kent Overstreet:
 "More bcachefs updates:

   - More stack usage improvements (~600 bytes)

   - Define CLASS()es for some commonly used types, and convert most
     rcu_read_lock() uses to the new lock guards

   - New introspection:
       - Superblock error counters are now available in sysfs:
         previously, they were only visible with 'show-super', which
         doesn't provide a live view
       - New tracepoint, error_throw(), which is called any time we
         return an error and start to unwind

   - Repair
       - check_fix_ptrs() can now repair btree node roots
       - We can now repair when we've somehow ended up with the journal
         using a superblock bucket

   - Revert some leftovers from the aborted directory i_size feature,
     and add repair code: some userspace programs (e.g. sshfs) were
     getting confused

  It seems in 6.15 there's a bug where i_nlink on the vfs inode has been
  getting incorrectly set to 0, with some unfortunate results;
  list_journal analysis showed bch2_inode_rm() being called (by
  bch2_evict_inode()) when it clearly should not have been.

   - bch2_inode_rm() now runs "should we be deleting this inode?" checks
     that were previously only run when deleting unlinked inodes in
     recovery

   - check_subvol() was treating a dangling subvol (pointing to a
     missing root inode) like a dangling dirent, and deleting it. This
     was the really unfortunate one: check_subvol() will now recreate
     the root inode if necessary

  This took longer to debug than it should have, and we lost several
  filesystems unnecessarily, because users have been ignoring the
  release notes and blindly running 'fsck -y'. Debugging required
  reconstructing what happened through analyzing the journal, when
  ideally someone would have noticed 'hey, fsck is asking me if I want
  to repair this: it usually doesn't, maybe I should run this in dry run
  mode and check what's going on?'

  As a reminder, fsck errors are being marked as autofix once we've
  verified, in real world usage, that they're working correctly; blindly
  running 'fsck -y' on an experimental filesystem is playing with fire

  Up to this incident we've had an excellent track record of not losing
  data, so let's try to learn from this one

  This is a community effort, I wouldn't be able to get this done
  without the help of all the people QAing and providing excellent bug
  reports and feedback based on real world usage. But please don't
  ignore advice and expect me to pick up the pieces

  If an error isn't marked as autofix, and it /is/ happening in the
  wild, that's also something I need to know about so we can check it
  out and add it to the autofix list if repair looks good. I haven't
  been getting those reports, and I should be; since we don't have any
  sort of telemetry yet I am absolutely dependent on user reports

  Now I'll be spending the weekend working on new repair code to see if
  I can get a filesystem back for a user who didn't have backups"

* tag 'bcachefs-2025-06-04' of git://evilpiepirate.org/bcachefs: (69 commits)
  bcachefs: add cond_resched() to handle_overwrites()
  bcachefs: Make journal read log message a bit quieter
  bcachefs: Fix subvol to missing root repair
  bcachefs: Run may_delete_deleted_inode() checks in bch2_inode_rm()
  bcachefs: delete dead code from may_delete_deleted_inode()
  bcachefs: Add flags to subvolume_to_text()
  bcachefs: Fix oops in btree_node_seq_matches()
  bcachefs: Fix dirent_casefold_mismatch repair
  bcachefs: Fix bch2_fsck_rename_dirent() for casefold
  bcachefs: Redo bch2_dirent_init_name()
  bcachefs: Fix -Wc23-extensions in bch2_check_dirents()
  bcachefs: Run check_dirents second time if required
  bcachefs: Run snapshot deletion out of system_long_wq
  bcachefs: Make check_key_has_snapshot safer
  bcachefs: BCH_RECOVERY_PASS_NO_RATELIMIT
  bcachefs: bch2_require_recovery_pass()
  bcachefs: bch_err_throw()
  bcachefs: Repair code for directory i_size
  bcachefs: Kill un-reverted directory i_size code
  bcachefs: Delete redundant fsck_err()
  ...
parents 16b70698 3d11125f
Loading
Loading
Loading
Loading
+40 −39
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
#include "error.h"
#include "lru.h"
#include "recovery.h"
#include "trace.h"
#include "varint.h"

#include <linux/kthread.h>
@@ -337,11 +336,10 @@ void bch2_alloc_v4_swab(struct bkey_s k)
	a->stripe_sectors	= swab32(a->stripe_sectors);
}

void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
static inline void __bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c,
					   unsigned dev, const struct bch_alloc_v4 *a)
{
	struct bch_alloc_v4 _a;
	const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_a);
	struct bch_dev *ca = c ? bch2_dev_bucket_tryget_noerror(c, k.k->p) : NULL;
	struct bch_dev *ca = c ? bch2_dev_tryget_noerror(c, dev) : NULL;

	prt_newline(out);
	printbuf_indent_add(out, 2);
@@ -369,6 +367,19 @@ void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
	bch2_dev_put(ca);
}

void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
{
	struct bch_alloc_v4 _a;
	const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_a);

	__bch2_alloc_v4_to_text(out, c, k.k->p.inode, a);
}

void bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
{
	__bch2_alloc_v4_to_text(out, c, k.k->p.inode, bkey_s_c_to_alloc_v4(k).v);
}

void __bch2_alloc_to_v4(struct bkey_s_c k, struct bch_alloc_v4 *out)
{
	if (k.k->type == KEY_TYPE_alloc_v4) {
@@ -697,8 +708,8 @@ static int __need_discard_or_freespace_err(struct btree_trans *trans,
				  set ? "" : "un",
				  bch2_btree_id_str(btree),
				  buf.buf);
	if (ret == -BCH_ERR_fsck_ignore ||
	    ret == -BCH_ERR_fsck_errors_not_fixed)
	if (bch2_err_matches(ret, BCH_ERR_fsck_ignore) ||
	    bch2_err_matches(ret, BCH_ERR_fsck_errors_not_fixed))
		ret = 0;

	printbuf_exit(&buf);
@@ -854,7 +865,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,

	struct bch_dev *ca = bch2_dev_bucket_tryget(c, new.k->p);
	if (!ca)
		return -BCH_ERR_trigger_alloc;
		return bch_err_throw(c, trigger_alloc);

	struct bch_alloc_v4 old_a_convert;
	const struct bch_alloc_v4 *old_a = bch2_alloc_to_v4(old, &old_a_convert);
@@ -988,14 +999,11 @@ int bch2_trigger_alloc(struct btree_trans *trans,
		}

		if (new_a->gen != old_a->gen) {
			rcu_read_lock();
			guard(rcu)();
			u8 *gen = bucket_gen(ca, new.k->p.offset);
			if (unlikely(!gen)) {
				rcu_read_unlock();
			if (unlikely(!gen))
				goto invalid_bucket;
			}
			*gen = new_a->gen;
			rcu_read_unlock();
		}

#define eval_state(_a, expr)		({ const struct bch_alloc_v4 *a = _a; expr; })
@@ -1021,15 +1029,12 @@ int bch2_trigger_alloc(struct btree_trans *trans,
	}

	if ((flags & BTREE_TRIGGER_gc) && (flags & BTREE_TRIGGER_insert)) {
		rcu_read_lock();
		guard(rcu)();
		struct bucket *g = gc_bucket(ca, new.k->p.offset);
		if (unlikely(!g)) {
			rcu_read_unlock();
		if (unlikely(!g))
			goto invalid_bucket;
		}
		g->gen_valid	= 1;
		g->gen		= new_a->gen;
		rcu_read_unlock();
	}
err:
fsck_err:
@@ -1039,7 +1044,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
invalid_bucket:
	bch2_fs_inconsistent(c, "reference to invalid bucket\n%s",
			     (bch2_bkey_val_to_text(&buf, c, new.s_c), buf.buf));
	ret = -BCH_ERR_trigger_alloc;
	ret = bch_err_throw(c, trigger_alloc);
	goto err;
}

@@ -1105,13 +1110,12 @@ static bool next_bucket(struct bch_fs *c, struct bch_dev **ca, struct bpos *buck
		bucket->offset = 0;
	}

	rcu_read_lock();
	guard(rcu)();
	*ca = __bch2_next_dev_idx(c, bucket->inode, NULL);
	if (*ca) {
		*bucket = POS((*ca)->dev_idx, (*ca)->mi.first_bucket);
		bch2_dev_get(*ca);
	}
	rcu_read_unlock();

	return *ca != NULL;
}
@@ -1454,7 +1458,7 @@ int bch2_check_discard_freespace_key(struct btree_trans *trans, struct btree_ite
		ret =   bch2_btree_bit_mod_iter(trans, iter, false) ?:
			bch2_trans_commit(trans, NULL, NULL,
				BCH_TRANS_COMMIT_no_enospc) ?:
			-BCH_ERR_transaction_restart_commit;
			bch_err_throw(c, transaction_restart_commit);
		goto out;
	} else {
		/*
@@ -1777,12 +1781,14 @@ int bch2_check_alloc_to_lru_refs(struct bch_fs *c)

static int discard_in_flight_add(struct bch_dev *ca, u64 bucket, bool in_progress)
{
	struct bch_fs *c = ca->fs;
	int ret;

	mutex_lock(&ca->discard_buckets_in_flight_lock);
	darray_for_each(ca->discard_buckets_in_flight, i)
		if (i->bucket == bucket) {
			ret = -BCH_ERR_EEXIST_discard_in_flight_add;
	struct discard_in_flight *i =
		darray_find_p(ca->discard_buckets_in_flight, i, i->bucket == bucket);
	if (i) {
		ret = bch_err_throw(c, EEXIST_discard_in_flight_add);
		goto out;
	}

@@ -1798,14 +1804,11 @@ static int discard_in_flight_add(struct bch_dev *ca, u64 bucket, bool in_progres
static void discard_in_flight_remove(struct bch_dev *ca, u64 bucket)
{
	mutex_lock(&ca->discard_buckets_in_flight_lock);
	darray_for_each(ca->discard_buckets_in_flight, i)
		if (i->bucket == bucket) {
			BUG_ON(!i->in_progress);
	struct discard_in_flight *i =
		darray_find_p(ca->discard_buckets_in_flight, i, i->bucket == bucket);
	BUG_ON(!i || !i->in_progress);

	darray_remove_item(&ca->discard_buckets_in_flight, i);
			goto found;
		}
	BUG();
found:
	mutex_unlock(&ca->discard_buckets_in_flight_lock);
}

@@ -2504,7 +2507,7 @@ void bch2_recalc_capacity(struct bch_fs *c)

	lockdep_assert_held(&c->state_lock);

	rcu_read_lock();
	guard(rcu)();
	for_each_member_device_rcu(c, ca, NULL) {
		struct block_device *bdev = READ_ONCE(ca->disk_sb.bdev);
		if (bdev)
@@ -2549,7 +2552,6 @@ void bch2_recalc_capacity(struct bch_fs *c)
		bucket_size_max = max_t(unsigned, bucket_size_max,
					ca->mi.bucket_size);
	}
	rcu_read_unlock();

	bch2_set_ra_pages(c, ra_pages);

@@ -2574,10 +2576,9 @@ u64 bch2_min_rw_member_capacity(struct bch_fs *c)
{
	u64 ret = U64_MAX;

	rcu_read_lock();
	guard(rcu)();
	for_each_rw_member_rcu(c, ca)
		ret = min(ret, ca->mi.nbuckets * ca->mi.bucket_size);
	rcu_read_unlock();
	return ret;
}

+4 −5
Original line number Diff line number Diff line
@@ -13,11 +13,9 @@

static inline bool bch2_dev_bucket_exists(struct bch_fs *c, struct bpos pos)
{
	rcu_read_lock();
	guard(rcu)();
	struct bch_dev *ca = bch2_dev_rcu_noerror(c, pos.inode);
	bool ret = ca && bucket_valid(ca, pos.offset);
	rcu_read_unlock();
	return ret;
	return ca && bucket_valid(ca, pos.offset);
}

static inline u64 bucket_to_u64(struct bpos bucket)
@@ -253,6 +251,7 @@ int bch2_alloc_v4_validate(struct bch_fs *, struct bkey_s_c,
			   struct bkey_validate_context);
void bch2_alloc_v4_swab(struct bkey_s);
void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
void bch2_alloc_v4_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);

#define bch2_bkey_ops_alloc ((struct bkey_ops) {	\
	.key_validate	= bch2_alloc_v1_validate,	\
@@ -277,7 +276,7 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);

#define bch2_bkey_ops_alloc_v4 ((struct bkey_ops) {	\
	.key_validate	= bch2_alloc_v4_validate,	\
	.val_to_text	= bch2_alloc_to_text,		\
	.val_to_text	= bch2_alloc_v4_to_text,	\
	.swab		= bch2_alloc_v4_swab,		\
	.trigger	= bch2_trigger_alloc,		\
	.min_val_size	= 48,				\
+51 −57
Original line number Diff line number Diff line
@@ -69,10 +69,9 @@ const char * const bch2_watermarks[] = {

void bch2_reset_alloc_cursors(struct bch_fs *c)
{
	rcu_read_lock();
	guard(rcu)();
	for_each_member_device_rcu(c, ca, NULL)
		memset(ca->alloc_cursor, 0, sizeof(ca->alloc_cursor));
	rcu_read_unlock();
}

static void bch2_open_bucket_hash_add(struct bch_fs *c, struct open_bucket *ob)
@@ -166,9 +165,8 @@ static void open_bucket_free_unused(struct bch_fs *c, struct open_bucket *ob)
	       ARRAY_SIZE(c->open_buckets_partial));

	spin_lock(&c->freelist_lock);
	rcu_read_lock();
	scoped_guard(rcu)
		bch2_dev_rcu(c, ob->dev)->nr_partial_buckets++;
	rcu_read_unlock();

	ob->on_partial_list = true;
	c->open_buckets_partial[c->open_buckets_partial_nr++] =
@@ -229,7 +227,7 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c,

		track_event_change(&c->times[BCH_TIME_blocked_allocate_open_bucket], true);
		spin_unlock(&c->freelist_lock);
		return ERR_PTR(-BCH_ERR_open_buckets_empty);
		return ERR_PTR(bch_err_throw(c, open_buckets_empty));
	}

	/* Recheck under lock: */
@@ -535,7 +533,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,

		track_event_change(&c->times[BCH_TIME_blocked_allocate], true);

		ob = ERR_PTR(-BCH_ERR_freelist_empty);
		ob = ERR_PTR(bch_err_throw(c, freelist_empty));
		goto err;
	}

@@ -560,7 +558,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,
	}
err:
	if (!ob)
		ob = ERR_PTR(-BCH_ERR_no_buckets_found);
		ob = ERR_PTR(bch_err_throw(c, no_buckets_found));

	if (!IS_ERR(ob))
		ob->data_type = req->data_type;
@@ -603,18 +601,18 @@ static int __dev_stripe_cmp(struct dev_stripe_state *stripe,

#define dev_stripe_cmp(l, r) __dev_stripe_cmp(stripe, l, r)

struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *c,
void bch2_dev_alloc_list(struct bch_fs *c,
			 struct dev_stripe_state *stripe,
					  struct bch_devs_mask *devs)
			 struct bch_devs_mask *devs,
			 struct dev_alloc_list *ret)
{
	struct dev_alloc_list ret = { .nr = 0 };
	unsigned i;
	ret->nr = 0;

	unsigned i;
	for_each_set_bit(i, devs->d, BCH_SB_MEMBERS_MAX)
		ret.data[ret.nr++] = i;
		ret->data[ret->nr++] = i;

	bubble_sort(ret.data, ret.nr, dev_stripe_cmp);
	return ret;
	bubble_sort(ret->data, ret->nr, dev_stripe_cmp);
}

static const u64 stripe_clock_hand_rescale	= 1ULL << 62; /* trigger rescale at */
@@ -705,18 +703,19 @@ static int add_new_bucket(struct bch_fs *c,
	return 0;
}

int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
				       struct alloc_request *req,
				       struct dev_stripe_state *stripe,
				       struct closure *cl)
{
	struct bch_fs *c = trans->c;
	int ret = -BCH_ERR_insufficient_devices;
	int ret = 0;

	BUG_ON(req->nr_effective >= req->nr_replicas);

	struct dev_alloc_list devs_sorted = bch2_dev_alloc_list(c, stripe, &req->devs_may_alloc);
	darray_for_each(devs_sorted, i) {
	bch2_dev_alloc_list(c, stripe, &req->devs_may_alloc, &req->devs_sorted);

	darray_for_each(req->devs_sorted, i) {
		req->ca = bch2_dev_tryget_noerror(c, *i);
		if (!req->ca)
			continue;
@@ -739,13 +738,16 @@ int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
			continue;
		}

		if (add_new_bucket(c, req, ob)) {
			ret = 0;
		ret = add_new_bucket(c, req, ob);
		if (ret)
			break;
	}
	}

	if (ret == 1)
		return 0;
	if (ret)
		return ret;
	return bch_err_throw(c, insufficient_devices);
}

/* Allocate from stripes: */
@@ -776,9 +778,9 @@ static int bucket_alloc_from_stripe(struct btree_trans *trans,
	if (!h)
		return 0;

	struct dev_alloc_list devs_sorted =
		bch2_dev_alloc_list(c, &req->wp->stripe, &req->devs_may_alloc);
	darray_for_each(devs_sorted, i)
	bch2_dev_alloc_list(c, &req->wp->stripe, &req->devs_may_alloc, &req->devs_sorted);

	darray_for_each(req->devs_sorted, i)
		for (unsigned ec_idx = 0; ec_idx < h->s->nr_data; ec_idx++) {
			if (!h->s->blocks[ec_idx])
				continue;
@@ -872,9 +874,8 @@ static int bucket_alloc_set_partial(struct bch_fs *c,
					  i);
			ob->on_partial_list = false;

			rcu_read_lock();
			scoped_guard(rcu)
				bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
			rcu_read_unlock();

			ret = add_new_bucket(c, req, ob);
			if (ret)
@@ -1056,9 +1057,8 @@ void bch2_open_buckets_stop(struct bch_fs *c, struct bch_dev *ca,

			ob->on_partial_list = false;

			rcu_read_lock();
			scoped_guard(rcu)
				bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
			rcu_read_unlock();

			spin_unlock(&c->freelist_lock);
			bch2_open_bucket_put(c, ob);
@@ -1086,14 +1086,11 @@ static struct write_point *__writepoint_find(struct hlist_head *head,
{
	struct write_point *wp;

	rcu_read_lock();
	guard(rcu)();
	hlist_for_each_entry_rcu(wp, head, node)
		if (wp->write_point == write_point)
			goto out;
	wp = NULL;
out:
	rcu_read_unlock();
			return wp;
	return NULL;
}

static inline bool too_many_writepoints(struct bch_fs *c, unsigned factor)
@@ -1104,7 +1101,7 @@ static inline bool too_many_writepoints(struct bch_fs *c, unsigned factor)
	return stranded * factor > free;
}

static bool try_increase_writepoints(struct bch_fs *c)
static noinline bool try_increase_writepoints(struct bch_fs *c)
{
	struct write_point *wp;

@@ -1117,7 +1114,7 @@ static bool try_increase_writepoints(struct bch_fs *c)
	return true;
}

static bool try_decrease_writepoints(struct btree_trans *trans, unsigned old_nr)
static noinline bool try_decrease_writepoints(struct btree_trans *trans, unsigned old_nr)
{
	struct bch_fs *c = trans->c;
	struct write_point *wp;
@@ -1379,11 +1376,11 @@ int bch2_alloc_sectors_start_trans(struct btree_trans *trans,
		goto retry;

	if (cl && bch2_err_matches(ret, BCH_ERR_open_buckets_empty))
		ret = -BCH_ERR_bucket_alloc_blocked;
		ret = bch_err_throw(c, bucket_alloc_blocked);

	if (cl && !(flags & BCH_WRITE_alloc_nowait) &&
	    bch2_err_matches(ret, BCH_ERR_freelist_empty))
		ret = -BCH_ERR_bucket_alloc_blocked;
		ret = bch_err_throw(c, bucket_alloc_blocked);

	return ret;
}
@@ -1637,9 +1634,8 @@ static noinline void bch2_print_allocator_stuck(struct bch_fs *c)

	bch2_printbuf_make_room(&buf, 4096);

	rcu_read_lock();
	buf.atomic++;

	scoped_guard(rcu)
		for_each_online_member_rcu(c, ca) {
			prt_printf(&buf, "Dev %u:\n", ca->dev_idx);
			printbuf_indent_add(&buf, 2);
@@ -1647,9 +1643,7 @@ static noinline void bch2_print_allocator_stuck(struct bch_fs *c)
			printbuf_indent_sub(&buf, 2);
			prt_newline(&buf);
		}

	--buf.atomic;
	rcu_read_unlock();

	prt_printf(&buf, "Copygc debug:\n");
	printbuf_indent_add(&buf, 2);
+5 −3
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ struct alloc_request {
	struct bch_devs_mask	devs_may_alloc;

	/* bch2_bucket_alloc_set_trans(): */
	struct dev_alloc_list	devs_sorted;
	struct bch_dev_usage	usage;

	/* bch2_bucket_alloc_trans(): */
@@ -71,9 +72,10 @@ struct alloc_request {
	struct bch_devs_mask	scratch_devs_may_alloc;
};

struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *,
void bch2_dev_alloc_list(struct bch_fs *,
			 struct dev_stripe_state *,
					  struct bch_devs_mask *);
			 struct bch_devs_mask *,
			 struct dev_alloc_list *);
void bch2_dev_stripe_increment(struct bch_dev *, struct dev_stripe_state *);

static inline struct bch_dev *ob_dev(struct bch_fs *c, struct open_bucket *ob)
+36 −36
Original line number Diff line number Diff line
@@ -48,17 +48,19 @@ void bch2_backpointer_to_text(struct printbuf *out, struct bch_fs *c, struct bke
{
	struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k);

	rcu_read_lock();
	struct bch_dev *ca = bch2_dev_rcu_noerror(c, bp.k->p.inode);
	if (ca) {
	struct bch_dev *ca;
	u32 bucket_offset;
		struct bpos bucket = bp_pos_to_bucket_and_offset(ca, bp.k->p, &bucket_offset);
		rcu_read_unlock();
	struct bpos bucket;
	scoped_guard(rcu) {
		ca = bch2_dev_rcu_noerror(c, bp.k->p.inode);
		if (ca)
			bucket = bp_pos_to_bucket_and_offset(ca, bp.k->p, &bucket_offset);
	}

	if (ca)
		prt_printf(out, "bucket=%llu:%llu:%u ", bucket.inode, bucket.offset, bucket_offset);
	} else {
		rcu_read_unlock();
	else
		prt_printf(out, "sector=%llu:%llu ", bp.k->p.inode, bp.k->p.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT);
	}

	bch2_btree_id_level_to_text(out, bp.v->btree_id, bp.v->level);
	prt_str(out, " data_type=");
@@ -140,7 +142,7 @@ static noinline int backpointer_mod_err(struct btree_trans *trans,
	}

	if (!will_check && __bch2_inconsistent_error(c, &buf))
		ret = -BCH_ERR_erofs_unfixed_errors;
		ret = bch_err_throw(c, erofs_unfixed_errors);

	bch_err(c, "%s", buf.buf);
	printbuf_exit(&buf);
@@ -293,7 +295,7 @@ static struct btree *__bch2_backpointer_get_node(struct btree_trans *trans,
		return b;

	if (btree_node_will_make_reachable(b)) {
		b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node);
		b = ERR_PTR(bch_err_throw(c, backpointer_to_overwritten_btree_node));
	} else {
		int ret = backpointer_target_not_found(trans, bp, bkey_i_to_s_c(&b->key),
						       last_flushed, commit);
@@ -351,7 +353,7 @@ static struct bkey_s_c __bch2_backpointer_get_key(struct btree_trans *trans,
		return ret ? bkey_s_c_err(ret) : bkey_s_c_null;
	} else {
		struct btree *b = __bch2_backpointer_get_node(trans, bp, iter, last_flushed, commit);
		if (b == ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node))
		if (b == ERR_PTR(bch_err_throw(c, backpointer_to_overwritten_btree_node)))
			return bkey_s_c_null;
		if (IS_ERR_OR_NULL(b))
			return ((struct bkey_s_c) { .k = ERR_CAST(b) });
@@ -591,6 +593,7 @@ static int check_bp_exists(struct btree_trans *trans,
		bkey_for_each_ptr(other_extent_ptrs, ptr)
			if (ptr->dev == bp->k.p.inode &&
			    dev_ptr_stale_rcu(ca, ptr)) {
				rcu_read_unlock();
				ret = drop_dev_and_update(trans, other_bp.v->btree_id,
							  other_extent, bp->k.p.inode);
				if (ret)
@@ -648,7 +651,7 @@ static int check_bp_exists(struct btree_trans *trans,
	prt_newline(&buf);
	bch2_bkey_val_to_text(&buf, c, other_extent);
	bch_err(c, "%s", buf.buf);
	ret = -BCH_ERR_fsck_repair_unimplemented;
	ret = bch_err_throw(c, fsck_repair_unimplemented);
	goto err;
missing:
	printbuf_reset(&buf);
@@ -679,26 +682,23 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
		if (p.ptr.dev == BCH_SB_MEMBER_INVALID)
			continue;

		rcu_read_lock();
		bool empty;
		{
			/* scoped_guard() is a loop, so it breaks continue */
			guard(rcu)();
			struct bch_dev *ca = bch2_dev_rcu_noerror(c, p.ptr.dev);
		if (!ca) {
			rcu_read_unlock();
			if (!ca)
				continue;
		}

		if (p.ptr.cached && dev_ptr_stale_rcu(ca, &p.ptr)) {
			rcu_read_unlock();
			if (p.ptr.cached && dev_ptr_stale_rcu(ca, &p.ptr))
				continue;
		}

			u64 b = PTR_BUCKET_NR(ca, &p.ptr);
		if (!bch2_bucket_bitmap_test(&ca->bucket_backpointer_mismatch, b)) {
			rcu_read_unlock();
			if (!bch2_bucket_bitmap_test(&ca->bucket_backpointer_mismatch, b))
				continue;
		}

		bool empty = bch2_bucket_bitmap_test(&ca->bucket_backpointer_empty, b);
		rcu_read_unlock();
			empty = bch2_bucket_bitmap_test(&ca->bucket_backpointer_empty, b);
		}

		struct bkey_i_backpointer bp;
		bch2_extent_ptr_to_bp(c, btree, level, k, p, entry, &bp);
@@ -953,7 +953,7 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
		    sectors[ALLOC_cached] > a->cached_sectors ||
		    sectors[ALLOC_stripe] > a->stripe_sectors) {
			ret = check_bucket_backpointers_to_extents(trans, ca, alloc_k.k->p) ?:
				-BCH_ERR_transaction_restart_nested;
				bch_err_throw(c, transaction_restart_nested);
			goto err;
		}

@@ -981,7 +981,7 @@ static bool backpointer_node_has_missing(struct bch_fs *c, struct bkey_s_c k)
	case KEY_TYPE_btree_ptr_v2: {
		bool ret = false;

		rcu_read_lock();
		guard(rcu)();
		struct bpos pos = bkey_s_c_to_btree_ptr_v2(k).v->min_key;
		while (pos.inode <= k.k->p.inode) {
			if (pos.inode >= c->sb.nr_devices)
@@ -1009,7 +1009,6 @@ static bool backpointer_node_has_missing(struct bch_fs *c, struct bkey_s_c k)
next:
			pos = SPOS(pos.inode + 1, 0, 0);
		}
		rcu_read_unlock();

		return ret;
	}
@@ -1352,7 +1351,7 @@ static int bch2_bucket_bitmap_set(struct bch_dev *ca, struct bucket_bitmap *b, u
			b->buckets = kvcalloc(BITS_TO_LONGS(ca->mi.nbuckets),
					      sizeof(unsigned long), GFP_KERNEL);
			if (!b->buckets)
				return -BCH_ERR_ENOMEM_backpointer_mismatches_bitmap;
				return bch_err_throw(ca->fs, ENOMEM_backpointer_mismatches_bitmap);
		}

		b->nr += !__test_and_set_bit(bit, b->buckets);
@@ -1361,7 +1360,8 @@ static int bch2_bucket_bitmap_set(struct bch_dev *ca, struct bucket_bitmap *b, u
	return 0;
}

int bch2_bucket_bitmap_resize(struct bucket_bitmap *b, u64 old_size, u64 new_size)
int bch2_bucket_bitmap_resize(struct bch_dev *ca, struct bucket_bitmap *b,
			      u64 old_size, u64 new_size)
{
	scoped_guard(mutex, &b->lock) {
		if (!b->buckets)
@@ -1370,7 +1370,7 @@ int bch2_bucket_bitmap_resize(struct bucket_bitmap *b, u64 old_size, u64 new_siz
		unsigned long *n = kvcalloc(BITS_TO_LONGS(new_size),
					    sizeof(unsigned long), GFP_KERNEL);
		if (!n)
			return -BCH_ERR_ENOMEM_backpointer_mismatches_bitmap;
			return bch_err_throw(ca->fs, ENOMEM_backpointer_mismatches_bitmap);

		memcpy(n, b->buckets,
		       BITS_TO_LONGS(min(old_size, new_size)) * sizeof(unsigned long));
Loading