From nobody Sat Feb 7 23:12:04 2026 Received: from out-174.mta0.migadu.com (out-174.mta0.migadu.com [91.218.175.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F26B51F4CB2 for ; Fri, 2 May 2025 20:00:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; cv=none; b=jJoNycMUKP7HaBvbAclMwBo9NT9nta2vxwZQx9KRSw0hv/K9ZnOJqfA7wXmjdOrW/j+8VtOnzSa5d/xcfqfuqobcuTGnnfwTzTiNwuXKpXBc+E8bCxPcbnJzETp8Rs3tcbLyIT6KSNRHeH1UYJjZrafmueY+qjjyaXys+gvVYmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; c=relaxed/simple; bh=COnXl7xNWOqiKKmxks1tCdAdXNnW+oUqmDfsZHJhInc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=klcBJzsVFvD9aVt+JZtmJAX/8x/KyJ2D2tTs6IOza0Dq1XmXJ94bOOQBIwAGf+pBOQ1DuGV45L8IZmlb4d/V41gt5ThWnPe/jTIGCqBh6uIBdmpRESslvKWkNZGSxkpjxIpbKW6oie5xuwgs+ouUXtHW+lWNUYIXNJ+noDBRnLE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=TR7wSGmP; arc=none smtp.client-ip=91.218.175.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="TR7wSGmP" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216009; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vUabQjbK26Lzn1a14NK0+47lQ7wVHhLpV3brDitDze8=; b=TR7wSGmPQwL/LGYU/iIVLo5x2oixNy2bgdP1gAtGh5kcZ4tozlRwQgxIGmacbrn9K6zz0u xDGwXIkmYzqUc4PhKDq/IJkqBs8NDXzTroUAZqeyQ4b00UMS3mJeZlTQj/QtlBdNfUIj9X iC+mN87XpkIlPsuXJokhx9/FCxtr7kE= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 1/8] bcachefs: snapshot delete progress indicator Date: Fri, 2 May 2025 15:59:53 -0400 Message-ID: <20250502200002.1309862-2-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Signed-off-by: Kent Overstreet --- fs/bcachefs/bcachefs.h | 3 +- fs/bcachefs/snapshot.c | 132 ++++++++++++++++++++++------------- fs/bcachefs/snapshot.h | 7 +- fs/bcachefs/snapshot_types.h | 25 +++++++ fs/bcachefs/subvolume.c | 2 - fs/bcachefs/subvolume.h | 3 - fs/bcachefs/super.c | 1 + fs/bcachefs/sysfs.c | 5 ++ 8 files changed, 121 insertions(+), 57 deletions(-) create mode 100644 fs/bcachefs/snapshot_types.h diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index 0369dd656d32..cd35d1cf3fbb 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -216,6 +216,7 @@ #include "recovery_passes_types.h" #include "sb-errors_types.h" #include "seqmutex.h" +#include "snapshot_types.h" #include "time_stats.h" #include "util.h" =20 @@ -869,7 +870,7 @@ struct bch_fs { struct mutex snapshot_table_lock; struct rw_semaphore snapshot_create_lock; =20 - struct work_struct snapshot_delete_work; + struct snapshot_delete snapshot_delete; struct work_struct snapshot_wait_for_pagecache_and_delete_work; snapshot_id_list snapshots_unlinked; struct mutex snapshots_unlinked_lock; diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c index 94cf60f76b64..2f2f129ce482 100644 --- a/fs/bcachefs/snapshot.c +++ b/fs/bcachefs/snapshot.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 =20 #include "bcachefs.h" +#include "bbpos.h" #include "bkey_buf.h" #include "btree_cache.h" #include "btree_key_cache.h" @@ -1346,12 +1347,6 @@ int bch2_snapshot_node_create(struct btree_trans *tr= ans, u32 parent, * that key to snapshot leaf nodes, where we can mutate it */ =20 -struct snapshot_interior_delete { - u32 id; - u32 live_child; -}; -typedef DARRAY(struct snapshot_interior_delete) interior_delete_list; - static inline u32 interior_delete_has_id(interior_delete_list *l, u32 id) { darray_for_each(*l, i) @@ -1385,28 +1380,28 @@ static unsigned __live_child(struct snapshot_table = *t, u32 id, return 0; } =20 -static unsigned live_child(struct bch_fs *c, u32 id, - snapshot_id_list *delete_leaves, - interior_delete_list *delete_interior) +static unsigned live_child(struct bch_fs *c, u32 id) { + struct snapshot_delete *d =3D &c->snapshot_delete; + rcu_read_lock(); u32 ret =3D __live_child(rcu_dereference(c->snapshots), id, - delete_leaves, delete_interior); + &d->delete_leaves, &d->delete_interior); rcu_read_unlock(); return ret; } =20 static int delete_dead_snapshots_process_key(struct btree_trans *trans, struct btree_iter *iter, - struct bkey_s_c k, - snapshot_id_list *delete_leaves, - interior_delete_list *delete_interior) + struct bkey_s_c k) { - if (snapshot_list_has_id(delete_leaves, k.k->p.snapshot)) + struct snapshot_delete *d =3D &trans->c->snapshot_delete; + + if (snapshot_list_has_id(&d->delete_leaves, k.k->p.snapshot)) return bch2_btree_delete_at(trans, iter, BTREE_UPDATE_internal_snapshot_node); =20 - u32 live_child =3D interior_delete_has_id(delete_interior, k.k->p.snapsho= t); + u32 live_child =3D interior_delete_has_id(&d->delete_interior, k.k->p.sna= pshot); if (live_child) { struct bkey_i *new =3D bch2_bkey_make_mut_noupdate(trans, k); int ret =3D PTR_ERR_OR_ZERO(new); @@ -1442,14 +1437,13 @@ static int delete_dead_snapshots_process_key(struct= btree_trans *trans, * it doesn't have child snapshot nodes - it's now redundant and we can ma= rk it * as deleted. */ -static int check_should_delete_snapshot(struct btree_trans *trans, struct = bkey_s_c k, - snapshot_id_list *delete_leaves, - interior_delete_list *delete_interior) +static int check_should_delete_snapshot(struct btree_trans *trans, struct = bkey_s_c k) { if (k.k->type !=3D KEY_TYPE_snapshot) return 0; =20 struct bch_fs *c =3D trans->c; + struct snapshot_delete *d =3D &c->snapshot_delete; struct bkey_s_c_snapshot s =3D bkey_s_c_to_snapshot(k); unsigned live_children =3D 0; =20 @@ -1460,23 +1454,23 @@ static int check_should_delete_snapshot(struct btre= e_trans *trans, struct bkey_s u32 child =3D le32_to_cpu(s.v->children[i]); =20 live_children +=3D child && - !snapshot_list_has_id(delete_leaves, child); + !snapshot_list_has_id(&d->delete_leaves, child); } =20 if (live_children =3D=3D 0) { - return snapshot_list_add(c, delete_leaves, s.k->p.offset); + return snapshot_list_add(c, &d->delete_leaves, s.k->p.offset); } else if (live_children =3D=3D 1) { - struct snapshot_interior_delete d =3D { + struct snapshot_interior_delete n =3D { .id =3D s.k->p.offset, - .live_child =3D live_child(c, s.k->p.offset, delete_leaves, delete_inte= rior), + .live_child =3D live_child(c, s.k->p.offset), }; =20 - if (!d.live_child) { - bch_err(c, "error finding live child of snapshot %u", d.id); + if (!n.live_child) { + bch_err(c, "error finding live child of snapshot %u", n.id); return -EINVAL; } =20 - return darray_push(delete_interior, d); + return darray_push(&d->delete_interior, n); } else { return 0; } @@ -1555,39 +1549,48 @@ static int bch2_fix_child_of_deleted_snapshot(struc= t btree_trans *trans, return bch2_trans_update(trans, iter, &s->k_i, 0); } =20 +static void bch2_snapshot_delete_nodes_to_text(struct printbuf *out, struc= t snapshot_delete *d) +{ + prt_printf(out, "deleting leaves"); + darray_for_each(d->delete_leaves, i) + prt_printf(out, " %u", *i); + + prt_printf(out, " interior"); + darray_for_each(d->delete_interior, i) + prt_printf(out, " %u->%u", i->id, i->live_child); +} + int bch2_delete_dead_snapshots(struct bch_fs *c) { if (!test_and_clear_bit(BCH_FS_need_delete_dead_snapshots, &c->flags)) return 0; =20 struct btree_trans *trans =3D bch2_trans_get(c); - snapshot_id_list delete_leaves =3D {}; - interior_delete_list delete_interior =3D {}; + struct snapshot_delete *d =3D &c->snapshot_delete; int ret =3D 0; =20 /* * For every snapshot node: If we have no live children and it's not * pointed to by a subvolume, delete it: */ + mutex_lock(&d->lock); + d->running =3D true; + d->pos =3D BBPOS_MIN; + ret =3D for_each_btree_key(trans, iter, BTREE_ID_snapshots, POS_MIN, 0, k, - check_should_delete_snapshot(trans, k, &delete_leaves, &delete_interior)= ); + check_should_delete_snapshot(trans, k)); + mutex_unlock(&d->lock); if (!bch2_err_matches(ret, EROFS)) bch_err_msg(c, ret, "walking snapshots"); if (ret) goto err; =20 - if (!delete_leaves.nr && !delete_interior.nr) + if (!d->delete_leaves.nr && !d->delete_interior.nr) goto err; =20 { struct printbuf buf =3D PRINTBUF; - prt_printf(&buf, "deleting leaves"); - darray_for_each(delete_leaves, i) - prt_printf(&buf, " %u", *i); - - prt_printf(&buf, " interior"); - darray_for_each(delete_interior, i) - prt_printf(&buf, " %u->%u", i->id, i->live_child); + bch2_snapshot_delete_nodes_to_text(&buf, d); =20 ret =3D commit_do(trans, NULL, NULL, 0, bch2_trans_log_msg(trans, &buf)); printbuf_exit(&buf); @@ -1595,19 +1598,21 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) goto err; } =20 - for (unsigned btree =3D 0; btree < BTREE_ID_NR; btree++) { + for (d->pos.btree =3D 0; d->pos.btree < BTREE_ID_NR; d->pos.btree++) { struct disk_reservation res =3D { 0 }; =20 - if (!btree_type_has_snapshots(btree)) + d->pos.pos =3D POS_MIN; + + if (!btree_type_has_snapshots(d->pos.btree)) continue; =20 ret =3D for_each_btree_key_commit(trans, iter, - btree, POS_MIN, + d->pos.btree, POS_MIN, BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, - &res, NULL, BCH_TRANS_COMMIT_no_enospc, - delete_dead_snapshots_process_key(trans, &iter, k, - &delete_leaves, - &delete_interior)); + &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ + d->pos.pos =3D iter.pos; + delete_dead_snapshots_process_key(trans, &iter, k); + })); =20 bch2_disk_reservation_put(c, &res); =20 @@ -1617,7 +1622,7 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) goto err; } =20 - darray_for_each(delete_leaves, i) { + darray_for_each(d->delete_leaves, i) { ret =3D commit_do(trans, NULL, NULL, 0, bch2_snapshot_node_delete(trans, *i)); if (!bch2_err_matches(ret, EROFS)) @@ -1634,11 +1639,11 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) ret =3D for_each_btree_key_commit(trans, iter, BTREE_ID_snapshots, POS_MI= N, BTREE_ITER_intent, k, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, - bch2_fix_child_of_deleted_snapshot(trans, &iter, k, &delete_interior)); + bch2_fix_child_of_deleted_snapshot(trans, &iter, k, &d->delete_interior)= ); if (ret) goto err; =20 - darray_for_each(delete_interior, i) { + darray_for_each(d->delete_interior, i) { ret =3D commit_do(trans, NULL, NULL, 0, bch2_snapshot_node_delete(trans, i->id)); if (!bch2_err_matches(ret, EROFS)) @@ -1647,8 +1652,11 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) goto err; } err: - darray_exit(&delete_interior); - darray_exit(&delete_leaves); + mutex_lock(&d->lock); + darray_exit(&d->delete_interior); + darray_exit(&d->delete_leaves); + d->running =3D false; + mutex_unlock(&d->lock); bch2_trans_put(trans); if (!bch2_err_matches(ret, EROFS)) bch_err_fn(c, ret); @@ -1657,7 +1665,7 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) =20 void bch2_delete_dead_snapshots_work(struct work_struct *work) { - struct bch_fs *c =3D container_of(work, struct bch_fs, snapshot_delete_wo= rk); + struct bch_fs *c =3D container_of(work, struct bch_fs, snapshot_delete.wo= rk); =20 set_worker_desc("bcachefs-delete-dead-snapshots/%s", c->name); =20 @@ -1672,10 +1680,27 @@ void bch2_delete_dead_snapshots_async(struct bch_fs= *c) =20 BUG_ON(!test_bit(BCH_FS_may_go_rw, &c->flags)); =20 - if (!queue_work(c->write_ref_wq, &c->snapshot_delete_work)) + if (!queue_work(c->write_ref_wq, &c->snapshot_delete.work)) enumerated_ref_put(&c->writes, BCH_WRITE_REF_delete_dead_snapshots); } =20 +void bch2_snapshot_delete_status_to_text(struct printbuf *out, struct bch_= fs *c) +{ + struct snapshot_delete *d =3D &c->snapshot_delete; + + if (!d->running) { + prt_str(out, "(not running)"); + return; + } + + mutex_lock(&d->lock); + bch2_snapshot_delete_nodes_to_text(out, d); + prt_newline(out); + mutex_unlock(&d->lock); + + bch2_bbpos_to_text(out, d->pos); +} + int __bch2_key_has_snapshot_overwrites(struct btree_trans *trans, enum btree_id id, struct bpos pos) @@ -1750,3 +1775,10 @@ void bch2_fs_snapshots_exit(struct bch_fs *c) { kvfree(rcu_dereference_protected(c->snapshots, true)); } + +void bch2_fs_snapshots_init_early(struct bch_fs *c) +{ + INIT_WORK(&c->snapshot_delete.work, bch2_delete_dead_snapshots_work); + mutex_init(&c->snapshot_delete.lock); + mutex_init(&c->snapshots_unlinked_lock); +} diff --git a/fs/bcachefs/snapshot.h b/fs/bcachefs/snapshot.h index 81180181d7c9..24a451bb7024 100644 --- a/fs/bcachefs/snapshot.h +++ b/fs/bcachefs/snapshot.h @@ -244,7 +244,6 @@ int bch2_reconstruct_snapshots(struct bch_fs *); int bch2_check_key_has_snapshot(struct btree_trans *, struct btree_iter *,= struct bkey_s_c); =20 int bch2_snapshot_node_set_deleted(struct btree_trans *, u32); -void bch2_delete_dead_snapshots_work(struct work_struct *); =20 int __bch2_key_has_snapshot_overwrites(struct btree_trans *, enum btree_id= , struct bpos); =20 @@ -259,7 +258,13 @@ static inline int bch2_key_has_snapshot_overwrites(str= uct btree_trans *trans, return __bch2_key_has_snapshot_overwrites(trans, id, pos); } =20 +int bch2_delete_dead_snapshots(struct bch_fs *); +void bch2_delete_dead_snapshots_work(struct work_struct *); +void bch2_delete_dead_snapshots_async(struct bch_fs *); +void bch2_snapshot_delete_status_to_text(struct printbuf *, struct bch_fs = *); + int bch2_snapshots_read(struct bch_fs *); void bch2_fs_snapshots_exit(struct bch_fs *); +void bch2_fs_snapshots_init_early(struct bch_fs *); =20 #endif /* _BCACHEFS_SNAPSHOT_H */ diff --git a/fs/bcachefs/snapshot_types.h b/fs/bcachefs/snapshot_types.h new file mode 100644 index 000000000000..bb67a6beb6e3 --- /dev/null +++ b/fs/bcachefs/snapshot_types.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_SNAPSHOT_TYPES_H +#define _BCACHEFS_SNAPSHOT_TYPES_H + +#include "bbpos_types.h" +#include "subvolume_types.h" + +struct snapshot_interior_delete { + u32 id; + u32 live_child; +}; +typedef DARRAY(struct snapshot_interior_delete) interior_delete_list; + +struct snapshot_delete { + struct work_struct work; + + struct mutex lock; + snapshot_id_list delete_leaves; + interior_delete_list delete_interior; + + bool running; + struct bbpos pos; +}; + +#endif /* _BCACHEFS_SNAPSHOT_TYPES_H */ diff --git a/fs/bcachefs/subvolume.c b/fs/bcachefs/subvolume.c index 51ab2ee10706..3c6ba1469de2 100644 --- a/fs/bcachefs/subvolume.c +++ b/fs/bcachefs/subvolume.c @@ -730,8 +730,6 @@ int bch2_fs_upgrade_for_subvolumes(struct bch_fs *c) =20 void bch2_fs_subvolumes_init_early(struct bch_fs *c) { - INIT_WORK(&c->snapshot_delete_work, bch2_delete_dead_snapshots_work); INIT_WORK(&c->snapshot_wait_for_pagecache_and_delete_work, bch2_subvolume_wait_for_pagecache_and_delete); - mutex_init(&c->snapshots_unlinked_lock); } diff --git a/fs/bcachefs/subvolume.h b/fs/bcachefs/subvolume.h index ee5e4e5a0fc8..075f55e25c70 100644 --- a/fs/bcachefs/subvolume.h +++ b/fs/bcachefs/subvolume.h @@ -77,9 +77,6 @@ bch2_btree_iter_peek_in_subvolume_max_type(struct btree_t= rans *trans, struct btr _end, _subvolid, _flags, _k, _do); \ }) =20 -int bch2_delete_dead_snapshots(struct bch_fs *); -void bch2_delete_dead_snapshots_async(struct bch_fs *); - int bch2_subvolume_unlink(struct btree_trans *, u32); int bch2_subvolume_create(struct btree_trans *, u64, u32, u32, u32 *, u32 = *, bool); =20 diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index 03efda996348..9626468600af 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -864,6 +864,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, = struct bch_opts *opts, bch2_fs_quota_init(c); bch2_fs_rebalance_init(c); bch2_fs_sb_errors_init_early(c); + bch2_fs_snapshots_init_early(c); bch2_fs_subvolumes_init_early(c); =20 INIT_LIST_HEAD(&c->list); diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c index 1d0c0f24a7b9..adf99a805a62 100644 --- a/fs/bcachefs/sysfs.c +++ b/fs/bcachefs/sysfs.c @@ -198,6 +198,7 @@ read_attribute(copy_gc_wait); =20 sysfs_pd_controller_attribute(rebalance); read_attribute(rebalance_status); +read_attribute(snapshot_delete_status); =20 read_attribute(new_stripes); =20 @@ -320,6 +321,9 @@ SHOW(bch2_fs) if (attr =3D=3D &sysfs_rebalance_status) bch2_rebalance_status_to_text(out, c); =20 + if (attr =3D=3D &sysfs_snapshot_delete_status) + bch2_snapshot_delete_status_to_text(out, c); + /* Debugging: */ =20 if (attr =3D=3D &sysfs_journal_debug) @@ -466,6 +470,7 @@ struct attribute *bch2_fs_files[] =3D { &sysfs_btree_write_stats, =20 &sysfs_rebalance_status, + &sysfs_snapshot_delete_status, =20 &sysfs_compression_stats, =20 --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-185.mta0.migadu.com (out-185.mta0.migadu.com [91.218.175.185]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 749D81F75A6 for ; Fri, 2 May 2025 20:00:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; cv=none; b=G/3U/JD65Fzkhm8ZFCgBkS6zQhn7ys5OMhLTx0NP9HLCwY/59PX9Q8BhCbTIGJ7CVoEexWLvwsz0KAdooEF/lMVKgf/0qXlLZMN8nZGDYyaFpUtuE/M9lnJcNX/0zW2aX8vo0YEDLCnfoWuj4QyMdm7d30/o5jpFHYOeiNS7WqE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; c=relaxed/simple; bh=9p+mouHTY72VPq36WvcgDsTvwoxa7aOGfU59JZO9XN0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ch/IWH4mnke0HqydCUt2SWueRlZV9AM9FaYdryHc5LNCu4RP4NuOEMqChOOlAM4zZ8I+4kWShINSgPUojm/W9pGWodpYZH/GimU9x+/aKJBJ1ZM3NeCF5YN9qPIGdkOVjakuTbA0CBMGy8Dxve8vtCK7Hgm//dfJCY/ltrBQqxQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=FyUMCW3P; arc=none smtp.client-ip=91.218.175.185 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="FyUMCW3P" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216010; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9wr7RceKqg/rAgUJoa76ffr37XLUb8mFMZSWx0uA0JE=; b=FyUMCW3PXg/ApW3e54Pv4EU+KN3GIzU+JFLTwVH8hbiEqahyeGcuieJeqZkqle77TkALI0 i6brdlYrj1N/zgy8uAu1ntqFEie3Pa62giUcsfrqCEHJBa+SjLlKXmn9S8tSdsFch/v6tM KnkGm6zgQTKTcCql9puo0aJlFu1h95Y= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 2/8] bcachefs: Add comments for inode snapshot requirements Date: Fri, 2 May 2025 15:59:54 -0400 Message-ID: <20250502200002.1309862-3-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Signed-off-by: Kent Overstreet --- fs/bcachefs/io_write.c | 6 ++++++ fs/bcachefs/xattr.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c index add141ac45b5..399df8fede8b 100644 --- a/fs/bcachefs/io_write.c +++ b/fs/bcachefs/io_write.c @@ -279,6 +279,12 @@ static inline int bch2_extent_update_i_size_sectors(st= ruct btree_trans *trans, inode_update_flags =3D 0; } =20 + /* + * extents, dirents and xattrs updates require that an inode update also + * happens - to ensure that if a key exists in one of those btrees with + * a given snapshot ID an inode is also present - so we may have to skip + * the nojournal optimization: + */ if (inode->k.p.snapshot !=3D iter.snapshot) { inode->k.p.snapshot =3D iter.snapshot; inode_update_flags =3D 0; diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c index b8bc2fb04f15..ea3f87f6fe49 100644 --- a/fs/bcachefs/xattr.c +++ b/fs/bcachefs/xattr.c @@ -176,6 +176,11 @@ int bch2_xattr_set(struct btree_trans *trans, subvol_i= num inum, if (ret) return ret; =20 + /* + * Besides the ctime update, extents, dirents and xattrs updates require + * that an inode update also happens - to ensure that if a key exists in + * one of those btrees with a given snapshot ID an inode is also present + */ inode_u->bi_ctime =3D bch2_current_time(c); =20 ret =3D bch2_inode_write(trans, &inode_iter, inode_u); --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-170.mta0.migadu.com (out-170.mta0.migadu.com [91.218.175.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 21AF71F7569 for ; Fri, 2 May 2025 20:00:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; cv=none; b=kQ/JTpomTWAlxvIgXOWpT6DErr0F4vs0Ppmcz2e6ObL65yUbqQ4YVFqWzVDIF04ifkSHPSsU9gpZ0IKqyA61nBn5Lrk12MNQdcU3PnEJS3OvmTy20/w21SCUImoI7KKssIWKp2tz79XDCFaTUk4sNZJTploprbKzFTNsIwCY3KQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216015; c=relaxed/simple; bh=RcKkJtsoCg4JEJhBqrBT9I2/p66KBZXHKCNjzceiIgc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cPpKLpNke3LVilo8RsLeYx7NlCHTKbu4LySoKTOBCZp3u3a5FJE0798G6oDm5qjaxxRpRa9LLcX5dg0f2xtuxjIYl+9DfpuB3J41VHvQUgYvoMw6uH778ByfNg4SenChpMLDVexPooYXmuz2SckeyO4Kg48RLf3fdL6Oj8kRoag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=RwqluP+B; arc=none smtp.client-ip=91.218.175.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="RwqluP+B" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216011; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ReUYFSWRiMLieiP4blFnICzA1f5cEZz7EuEF3waNFEM=; b=RwqluP+BwPz7xncWNhvsjsnXmyLwSsGI7x0539bGRwn3aakoLktYiUgKr5RVUvO3nuGe0a ILEjYgLn08Jci8WzMMuASOxnpgtvHA8ZMfpWgCLmimtYQLb/7kpoSMPHdAfVchx1EhshZt 4tE6se55T/y6xsYXb1QVeOBbFKicSYA= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 3/8] bcachefs: kill inode_walker_entry.snapshot Date: Fri, 2 May 2025 15:59:55 -0400 Message-ID: <20250502200002.1309862-4-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" redundant Signed-off-by: Kent Overstreet --- fs/bcachefs/fsck.c | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 7d3dd1a0ae4f..f0aa3c7479e1 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -785,12 +785,11 @@ static int ref_visible2(struct bch_fs *c, =20 #define for_each_visible_inode(_c, _s, _w, _snapshot, _i) \ for (_i =3D (_w)->inodes.data; _i < (_w)->inodes.data + (_w)->inodes.nr &= & \ - (_i)->snapshot <=3D (_snapshot); _i++) \ - if (key_visible_in_snapshot(_c, _s, _i->snapshot, _snapshot)) + (_i)->inode.bi_snapshot <=3D (_snapshot); _i++) \ + if (key_visible_in_snapshot(_c, _s, _i->inode.bi_snapshot, _snapshot)) =20 struct inode_walker_entry { struct bch_inode_unpacked inode; - u32 snapshot; u64 count; u64 i_size; }; @@ -824,7 +823,6 @@ static int add_inode(struct bch_fs *c, struct inode_wal= ker *w, return bch2_inode_unpack(inode, &u) ?: darray_push(&w->inodes, ((struct inode_walker_entry) { .inode =3D u, - .snapshot =3D inode.k->p.snapshot, })); } =20 @@ -870,19 +868,19 @@ lookup_inode_for_snapshot(struct bch_fs *c, struct in= ode_walker *w, struct bkey_ =20 struct inode_walker_entry *i; __darray_for_each(w->inodes, i) - if (bch2_snapshot_is_ancestor(c, k.k->p.snapshot, i->snapshot)) + if (bch2_snapshot_is_ancestor(c, k.k->p.snapshot, i->inode.bi_snapshot)) goto found; =20 return NULL; found: - BUG_ON(k.k->p.snapshot > i->snapshot); + BUG_ON(k.k->p.snapshot > i->inode.bi_snapshot); =20 - if (k.k->p.snapshot !=3D i->snapshot && !is_whiteout) { + if (k.k->p.snapshot !=3D i->inode.bi_snapshot && !is_whiteout) { struct inode_walker_entry new =3D *i; =20 - new.snapshot =3D k.k->p.snapshot; - new.count =3D 0; - new.i_size =3D 0; + new.inode.bi_snapshot =3D k.k->p.snapshot; + new.count =3D 0; + new.i_size =3D 0; =20 struct printbuf buf =3D PRINTBUF; bch2_bkey_val_to_text(&buf, c, k); @@ -890,10 +888,10 @@ lookup_inode_for_snapshot(struct bch_fs *c, struct in= ode_walker *w, struct bkey_ bch_info(c, "have key for inode %llu:%u but have inode in ancestor snaps= hot %u\n" "unexpected because we should always update the inode when we update a= key in that inode\n" "%s", - w->last_pos.inode, k.k->p.snapshot, i->snapshot, buf.buf); + w->last_pos.inode, k.k->p.snapshot, i->inode.bi_snapshot, buf.buf); printbuf_exit(&buf); =20 - while (i > w->inodes.data && i[-1].snapshot > k.k->p.snapshot) + while (i > w->inodes.data && i[-1].inode.bi_snapshot > k.k->p.snapshot) --i; =20 size_t pos =3D i - w->inodes.data; @@ -1496,21 +1494,21 @@ static int check_i_sectors_notnested(struct btree_t= rans *trans, struct inode_wal if (i->inode.bi_sectors =3D=3D i->count) continue; =20 - count2 =3D bch2_count_inode_sectors(trans, w->last_pos.inode, i->snapsho= t); + count2 =3D bch2_count_inode_sectors(trans, w->last_pos.inode, i->inode.b= i_snapshot); =20 if (w->recalculate_sums) i->count =3D count2; =20 if (i->count !=3D count2) { bch_err_ratelimited(c, "fsck counted i_sectors wrong for inode %llu:%u:= got %llu should be %llu", - w->last_pos.inode, i->snapshot, i->count, count2); + w->last_pos.inode, i->inode.bi_snapshot, i->count, count2); i->count =3D count2; } =20 if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_i_sectors_dirty), trans, inode_i_sectors_wrong, "inode %llu:%u has incorrect i_sectors: got %llu, should be %llu", - w->last_pos.inode, i->snapshot, + w->last_pos.inode, i->inode.bi_snapshot, i->inode.bi_sectors, i->count)) { i->inode.bi_sectors =3D i->count; ret =3D bch2_fsck_write_inode(trans, &i->inode); @@ -1821,20 +1819,20 @@ static int check_extent(struct btree_trans *trans, = struct btree_iter *iter, for (struct inode_walker_entry *i =3D extent_i ?: &darray_last(inode->in= odes); inode->inodes.data && i >=3D inode->inodes.data; --i) { - if (i->snapshot > k.k->p.snapshot || - !key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot)) + if (i->inode.bi_snapshot > k.k->p.snapshot || + !key_visible_in_snapshot(c, s, i->inode.bi_snapshot, k.k->p.snapsho= t)) continue; =20 if (fsck_err_on(k.k->p.offset > round_up(i->inode.bi_size, block_bytes(= c)) >> 9 && !bkey_extent_is_reservation(k), trans, 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, + i->inode.bi_inum, i->inode.bi_snapshot, i->inode.bi_size, (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) { struct btree_iter iter2; =20 bch2_trans_copy_iter(trans, &iter2, iter); - bch2_btree_iter_set_snapshot(trans, &iter2, i->snapshot); + bch2_btree_iter_set_snapshot(trans, &iter2, i->inode.bi_snapshot); ret =3D bch2_btree_iter_traverse(trans, &iter2) ?: bch2_btree_delete_at(trans, &iter2, BTREE_UPDATE_internal_snapshot_node); @@ -1856,8 +1854,8 @@ static int check_extent(struct btree_trans *trans, st= ruct btree_iter *iter, for (struct inode_walker_entry *i =3D extent_i ?: &darray_last(inode->in= odes); inode->inodes.data && i >=3D inode->inodes.data; --i) { - if (i->snapshot > k.k->p.snapshot || - !key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot)) + if (i->inode.bi_snapshot > k.k->p.snapshot || + !key_visible_in_snapshot(c, s, i->inode.bi_snapshot, k.k->p.snapsho= t)) continue; =20 i->count +=3D k.k->size; @@ -1939,13 +1937,13 @@ static int check_subdir_count_notnested(struct btre= e_trans *trans, struct inode_ if (i->inode.bi_nlink =3D=3D i->count) continue; =20 - count2 =3D bch2_count_subdirs(trans, w->last_pos.inode, i->snapshot); + count2 =3D bch2_count_subdirs(trans, w->last_pos.inode, i->inode.bi_snap= shot); if (count2 < 0) return count2; =20 if (i->count !=3D count2) { bch_err_ratelimited(c, "fsck counted subdirectories wrong for inum %llu= :%u: got %llu should be %llu", - w->last_pos.inode, i->snapshot, i->count, count2); + w->last_pos.inode, i->inode.bi_snapshot, i->count, count2); i->count =3D count2; if (i->inode.bi_nlink =3D=3D i->count) continue; @@ -1954,7 +1952,7 @@ static int check_subdir_count_notnested(struct btree_= trans *trans, struct inode_ if (fsck_err_on(i->inode.bi_nlink !=3D i->count, trans, 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)) { + w->last_pos.inode, i->inode.bi_snapshot, i->inode.bi_nlink, i->count))= { i->inode.bi_nlink =3D i->count; ret =3D bch2_fsck_write_inode(trans, &i->inode); if (ret) --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-172.mta0.migadu.com (out-172.mta0.migadu.com [91.218.175.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D174C1F8743 for ; Fri, 2 May 2025 20:00:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216016; cv=none; b=hwxUWMnFhBVbDGG3yVrUY3zwN5gdYkD62ddorWJOuRXIK+Bl0OsxM7mnPzJJEiLQ1Psth5FmiWsakPMxls5cpGONxkD4033o+Dvoy4Wrjk0V/f8gA9Wx7S4GUpK3K/XAuC8ZcrScc2sJEi9KFA30tgVe4LrEqLzgQXYTjPfNAMI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216016; c=relaxed/simple; bh=1S6uzaRBa0FtSxA9MxLj/7q3+opFssceFjQyClReKl8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tb0qYTkxCj7DbATQ+SWXJBU4n4OpqR8AEkJVe88zdhPoYpMfEMsr2axSbEqhihaVe+lGv0FTuaj8RRujdQWbKH/F19Jv6hKLxOx5B2FiYc5ttUaswrBSkaEt9YpBB7j/c3jJpdHhClrk3SC35PlJ0HVk7PS5jjg7nkRWss6tDSM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=v/7x32wP; arc=none smtp.client-ip=91.218.175.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="v/7x32wP" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216012; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MZFpD/v/lwWIJu11j483vAie+phHYD55UN+mWXSFa1c=; b=v/7x32wPaX2xCjkcuyxBeWCh+y6lrc8/7WRTWV+nNsLj9wf4KndpxMEAllTBUkRxTp19ok WRn5NOx6oBWW5B26b8037BpV+O85+5fqkQ4DrI1W897wnwsReBvVlqW3bM50I9JmEOkkMG 5eMrxJJzEH2Ct7fpf4JP0wb7uBIAaf4= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 4/8] bcachefs: BCH_FSCK_ERR_snapshot_key_missing_inode_snapshot Date: Fri, 2 May 2025 15:59:56 -0400 Message-ID: <20250502200002.1309862-5-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" We're going to be doing some snapshot deletion performance improvements, and those will strictly require that if an extent/dirent/xattr is present, an inode is present in that snapshot ID. We already check for this, but we don't repair it on disk: this patch adds that repair and turns it into a real fsck_err(). Signed-off-by: Kent Overstreet --- fs/bcachefs/fsck.c | 44 ++++++++++++++++------------------ fs/bcachefs/sb-errors_format.h | 3 ++- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index f0aa3c7479e1..dd88113a7c70 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -862,9 +862,9 @@ static int get_inodes_all_snapshots(struct btree_trans = *trans, } =20 static struct inode_walker_entry * -lookup_inode_for_snapshot(struct bch_fs *c, struct inode_walker *w, struct= bkey_s_c k) +lookup_inode_for_snapshot(struct btree_trans *trans, struct inode_walker *= w, struct bkey_s_c k) { - bool is_whiteout =3D k.k->type =3D=3D KEY_TYPE_whiteout; + struct bch_fs *c =3D trans->c; =20 struct inode_walker_entry *i; __darray_for_each(w->inodes, i) @@ -875,34 +875,32 @@ lookup_inode_for_snapshot(struct bch_fs *c, struct in= ode_walker *w, struct bkey_ found: BUG_ON(k.k->p.snapshot > i->inode.bi_snapshot); =20 - if (k.k->p.snapshot !=3D i->inode.bi_snapshot && !is_whiteout) { - struct inode_walker_entry new =3D *i; - - new.inode.bi_snapshot =3D k.k->p.snapshot; - new.count =3D 0; - new.i_size =3D 0; - - struct printbuf buf =3D PRINTBUF; - bch2_bkey_val_to_text(&buf, c, k); + struct printbuf buf =3D PRINTBUF; + int ret =3D 0; =20 - bch_info(c, "have key for inode %llu:%u but have inode in ancestor snaps= hot %u\n" + if (fsck_err_on(k.k->p.snapshot !=3D i->inode.bi_snapshot, + trans, snapshot_key_missing_inode_snapshot, + "have key for inode %llu:%u but have inode in ancestor snapshot %u\n" "unexpected because we should always update the inode when we update a= key in that inode\n" "%s", - w->last_pos.inode, k.k->p.snapshot, i->inode.bi_snapshot, buf.buf); - printbuf_exit(&buf); + w->last_pos.inode, k.k->p.snapshot, i->inode.bi_snapshot, + (bch2_bkey_val_to_text(&buf, c, k), + buf.buf))) { + struct bch_inode_unpacked new =3D i->inode; =20 - while (i > w->inodes.data && i[-1].inode.bi_snapshot > k.k->p.snapshot) - --i; + new.bi_snapshot =3D k.k->p.snapshot; =20 - size_t pos =3D i - w->inodes.data; - int ret =3D darray_insert_item(&w->inodes, pos, new); - if (ret) - return ERR_PTR(ret); - - i =3D w->inodes.data + pos; + ret =3D __bch2_fsck_write_inode(trans, &new) ?: + bch2_trans_commit(trans, NULL, NULL, 0) ?: + -BCH_ERR_transaction_restart_nested; + goto fsck_err; } =20 + printbuf_exit(&buf); return i; +fsck_err: + printbuf_exit(&buf); + return ERR_PTR(ret); } =20 static struct inode_walker_entry *walk_inode(struct btree_trans *trans, @@ -917,7 +915,7 @@ static struct inode_walker_entry *walk_inode(struct btr= ee_trans *trans, =20 w->last_pos =3D k.k->p; =20 - return lookup_inode_for_snapshot(trans->c, w, k); + return lookup_inode_for_snapshot(trans, w, k); } =20 static int get_visible_inodes(struct btree_trans *trans, diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index 6389f6e0f8dc..82bc1906aa00 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -216,6 +216,7 @@ enum bch_fsck_flags { x(inode_str_hash_invalid, 194, 0) \ x(inode_v3_fields_start_bad, 195, 0) \ x(inode_snapshot_mismatch, 196, 0) \ + x(snapshot_key_missing_inode_snapshot, 314, FSCK_AUTOFIX) \ x(inode_unlinked_but_clean, 197, 0) \ x(inode_unlinked_but_nlink_nonzero, 198, 0) \ x(inode_unlinked_and_not_open, 281, 0) \ @@ -323,7 +324,7 @@ enum bch_fsck_flags { x(dirent_stray_data_after_cf_name, 305, 0) \ x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \ x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \ - x(MAX, 314, 0) + x(MAX, 315, 0) =20 enum bch_sb_error_id { #define x(t, n, ...) BCH_FSCK_ERR_##t =3D n, --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-174.mta0.migadu.com (out-174.mta0.migadu.com [91.218.175.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7CC661F8BC7 for ; Fri, 2 May 2025 20:00:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216016; cv=none; b=XA9KmmtFULb+x8CRMzO9GIrz6hCiNcIsXT70tEb3LttHqzeLhQJFE6ytoMH70dy7RYjUjIwzchSEPXTikIM+//opwkNf7LvzJmVqkWNdwK2ofmdrL1LUbIMlzRfUjjd3Z01Aw6QnNq6XtfA9wjBGVSOTHvpKYlszzyCmkZ1H0Zg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216016; c=relaxed/simple; bh=1k/qLvfk5Gpy98xthVQ1JPV5K5pWtiv6CS8I/dN1uKs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=du840YrCrtK/eDhNOkBtkuFnfCU/fUKZ5QLYGh3J/WTkJm41T4DtmnqyugY3mAPIlpdl/BSss8zu4XX+xG+T97NkqydwFfg7jWxMe5qxjqEk45fzx7iMf6A5XTFJjXahD7roJwBJWEGalCdFATeQWqM25lq02sxs9V16kcMZpYc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=HtEJJu5m; arc=none smtp.client-ip=91.218.175.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="HtEJJu5m" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216012; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UReUCsLGNEVLao4OCJWiFeZpqM6wWN3x9UsUAyCZv4U=; b=HtEJJu5mjs5w3XtaV3C/aYZYgZmTXLepn67CcXJSx4mNelhkM9umx8DSM47LexRCewWwxm GDBb5u8o9/jSoj922iW1qhK7SwB5H193EFKjmxZmnTm0JLN2b8uTMPUfy9Pit52iKFgrx1 3XApM+3fD3G7nREK0VyAgMhtuDFVdes= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 5/8] bcachefs: Skip unrelated snapshot trees in snapshot deletion Date: Fri, 2 May 2025 15:59:57 -0400 Message-ID: <20250502200002.1309862-6-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Don't scan keys in inodes for which the snapshot tree doesn't match any we're deleting from. Signed-off-by: Kent Overstreet --- fs/bcachefs/snapshot.c | 35 +++++++++++++++++++++++++++++++++-- fs/bcachefs/snapshot_types.h | 1 + 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c index 2f2f129ce482..219cba038778 100644 --- a/fs/bcachefs/snapshot.c +++ b/fs/bcachefs/snapshot.c @@ -1432,6 +1432,24 @@ static int delete_dead_snapshots_process_key(struct = btree_trans *trans, return 0; } =20 +static bool skip_unrelated_snapshot_tree(struct btree_trans *trans, struct= btree_iter *iter) +{ + struct bch_fs *c =3D trans->c; + struct snapshot_delete *d =3D &c->snapshot_delete; + + bool ret =3D !snapshot_list_has_id(&d->deleting_from_trees, + bch2_snapshot_tree(c, iter->pos.snapshot)); + if (unlikely(ret)) { + struct bpos pos =3D iter->pos; + pos.snapshot =3D 0; + if (iter->btree_id !=3D BTREE_ID_inodes) + pos.offset =3D U64_MAX; + bch2_btree_iter_set_pos(trans, iter, bpos_nosnap_successor(pos)); + } + + return ret; +} + /* * For a given snapshot, if it doesn't have a subvolume that points to it,= and * it doesn't have child snapshot nodes - it's now redundant and we can ma= rk it @@ -1457,8 +1475,11 @@ static int check_should_delete_snapshot(struct btree= _trans *trans, struct bkey_s !snapshot_list_has_id(&d->delete_leaves, child); } =20 + u32 tree =3D bch2_snapshot_tree(c, s.k->p.offset); + if (live_children =3D=3D 0) { - return snapshot_list_add(c, &d->delete_leaves, s.k->p.offset); + return snapshot_list_add_nodup(c, &d->deleting_from_trees, tree) ?: + snapshot_list_add(c, &d->delete_leaves, s.k->p.offset); } else if (live_children =3D=3D 1) { struct snapshot_interior_delete n =3D { .id =3D s.k->p.offset, @@ -1470,7 +1491,8 @@ static int check_should_delete_snapshot(struct btree_= trans *trans, struct bkey_s return -EINVAL; } =20 - return darray_push(&d->delete_interior, n); + return snapshot_list_add_nodup(c, &d->deleting_from_trees, tree) ?: + darray_push(&d->delete_interior, n); } else { return 0; } @@ -1551,6 +1573,10 @@ static int bch2_fix_child_of_deleted_snapshot(struct= btree_trans *trans, =20 static void bch2_snapshot_delete_nodes_to_text(struct printbuf *out, struc= t snapshot_delete *d) { + prt_printf(out, "deleting from trees"); + darray_for_each(d->deleting_from_trees, i) + prt_printf(out, " %u", *i); + prt_printf(out, "deleting leaves"); darray_for_each(d->delete_leaves, i) prt_printf(out, " %u", *i); @@ -1611,6 +1637,10 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ d->pos.pos =3D iter.pos; + + if (skip_unrelated_snapshot_tree(trans, &iter)) + continue; + delete_dead_snapshots_process_key(trans, &iter, k); })); =20 @@ -1653,6 +1683,7 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) } err: mutex_lock(&d->lock); + darray_exit(&d->deleting_from_trees); darray_exit(&d->delete_interior); darray_exit(&d->delete_leaves); d->running =3D false; diff --git a/fs/bcachefs/snapshot_types.h b/fs/bcachefs/snapshot_types.h index bb67a6beb6e3..39fb47f43183 100644 --- a/fs/bcachefs/snapshot_types.h +++ b/fs/bcachefs/snapshot_types.h @@ -15,6 +15,7 @@ struct snapshot_delete { struct work_struct work; =20 struct mutex lock; + snapshot_id_list deleting_from_trees; snapshot_id_list delete_leaves; interior_delete_list delete_interior; =20 --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-178.mta0.migadu.com (out-178.mta0.migadu.com [91.218.175.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F16B91FCFE2 for ; Fri, 2 May 2025 20:00:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216018; cv=none; b=RriVqiB//nbYI3J/Rq9cqhs8uSU16Moy58Gczl+Lh1t0uyoxCL4oz8mig+uPz/DA8EQTu1UJ7MbaG+C/95t+dIomb4XpoGObCrjlhO15EIMI2hDh8f9vN5vd0bteC3T7V9CrBJ9jcwl3Ciw6xwgVF3xoEPRI/PN6M/myymeLyK0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216018; c=relaxed/simple; bh=qrcHTKc3T2CBYpInX5zoEg0eS8fpTDC/0Cr1IpF3WzE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V9yHo8ARW+IVwbdgZB4+ctH7b2JxSqJ6dTj+fL1S24PT9WA4sktf5Dwf7pE5AFVJHQKjiqgKoYUgO1ndSSQKWQixR5wHxzWLNw8UpeoFsII69mSDMxKWC/WHPd6HKkffLQ5whralo6ZzRtR8Cc3ie8SAzwZjGpz8/JUERGw5/R4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=HzFdNXNz; arc=none smtp.client-ip=91.218.175.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="HzFdNXNz" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qIsio+xzgKh9uKa+u77mHFbVQuuvfgvskJa7iDsD9C4=; b=HzFdNXNzQWMDIpNvh7RLmhMhb0HQx4oyNbR017/a4XvFPtD3GNHfM5Ls9YeXJqww25ig9v ZjT7UDUmrqBmRAb+36Eryuo4NUD5VJEHSWrrs7zCZSGQmfj8yDSTE4Uy0V/lU6ZRz68nkh aWdCxkJbzhwgUA2ynCNEsHVEqyqy3mY= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 6/8] bcachefs: BCH_SNAPSHOT_DELETED -> BCH_SNAPSHOT_WILL_DELETE Date: Fri, 2 May 2025 15:59:58 -0400 Message-ID: <20250502200002.1309862-7-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" We're going to be speeding up snapshot deletion, by only having it process the extents/dirents/xattrs btrees if an inode of a given snapshot ID was present. This raises the possibility of 'bkey_in_missing_snapshot' errors popping up, if we ever accidentally don't do the corresponding inode update, or if the new algorithm has bugs. So we'll want to be able to differentiate more definitively between 'snapshot went missing' (and perhaps needs to be reconstructed), and 'key in snapshot that was deleted'. So instead of deleting snapshot IDs, we'll be adding a new deleted flag and leaving them permanently. Signed-off-by: Kent Overstreet --- fs/bcachefs/snapshot.c | 12 ++++++------ fs/bcachefs/snapshot_format.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c index 219cba038778..7349f7f33a4f 100644 --- a/fs/bcachefs/snapshot.c +++ b/fs/bcachefs/snapshot.c @@ -213,7 +213,7 @@ void bch2_snapshot_to_text(struct printbuf *out, struct= bch_fs *c, =20 prt_printf(out, "is_subvol %llu deleted %llu parent %10u children %10u %1= 0u subvol %u tree %u", BCH_SNAPSHOT_SUBVOL(s.v), - BCH_SNAPSHOT_DELETED(s.v), + BCH_SNAPSHOT_WILL_DELETE(s.v), le32_to_cpu(s.v->parent), le32_to_cpu(s.v->children[0]), le32_to_cpu(s.v->children[1]), @@ -339,7 +339,7 @@ static int __bch2_mark_snapshot(struct btree_trans *tra= ns, parent - id - 1 < IS_ANCESTOR_BITMAP) __set_bit(parent - id - 1, t->is_ancestor); =20 - if (BCH_SNAPSHOT_DELETED(s.v)) { + if (BCH_SNAPSHOT_WILL_DELETE(s.v)) { set_bit(BCH_FS_need_delete_dead_snapshots, &c->flags); if (c->curr_recovery_pass > BCH_RECOVERY_PASS_delete_dead_snapshots) bch2_delete_dead_snapshots_async(c); @@ -748,7 +748,7 @@ static int check_snapshot(struct btree_trans *trans, } =20 bool should_have_subvol =3D BCH_SNAPSHOT_SUBVOL(&s) && - !BCH_SNAPSHOT_DELETED(&s); + !BCH_SNAPSHOT_WILL_DELETE(&s); =20 if (should_have_subvol) { id =3D le32_to_cpu(s.subvol); @@ -1062,10 +1062,10 @@ int bch2_snapshot_node_set_deleted(struct btree_tra= ns *trans, u32 id) } =20 /* already deleted? */ - if (BCH_SNAPSHOT_DELETED(&s->v)) + if (BCH_SNAPSHOT_WILL_DELETE(&s->v)) goto err; =20 - SET_BCH_SNAPSHOT_DELETED(&s->v, true); + SET_BCH_SNAPSHOT_WILL_DELETE(&s->v, true); SET_BCH_SNAPSHOT_SUBVOL(&s->v, false); s->v.subvol =3D 0; err: @@ -1770,7 +1770,7 @@ static int bch2_check_snapshot_needs_deletion(struct = btree_trans *trans, struct return 0; =20 struct bkey_s_c_snapshot snap =3D bkey_s_c_to_snapshot(k); - if (BCH_SNAPSHOT_DELETED(snap.v) || + if (BCH_SNAPSHOT_WILL_DELETE(snap.v) || interior_snapshot_needs_delete(snap)) set_bit(BCH_FS_need_delete_dead_snapshots, &trans->c->flags); =20 diff --git a/fs/bcachefs/snapshot_format.h b/fs/bcachefs/snapshot_format.h index aabcd3a74cd9..685a9fe209ab 100644 --- a/fs/bcachefs/snapshot_format.h +++ b/fs/bcachefs/snapshot_format.h @@ -15,7 +15,7 @@ struct bch_snapshot { bch_le128 btime; }; =20 -LE32_BITMASK(BCH_SNAPSHOT_DELETED, struct bch_snapshot, flags, 0, 1) +LE32_BITMASK(BCH_SNAPSHOT_WILL_DELETE, struct bch_snapshot, flags, 0, 1) =20 /* True if a subvolume points to this snapshot node: */ LE32_BITMASK(BCH_SNAPSHOT_SUBVOL, struct bch_snapshot, flags, 1, 2) --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-172.mta0.migadu.com (out-172.mta0.migadu.com [91.218.175.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DA431F4E34 for ; Fri, 2 May 2025 20:00:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216020; cv=none; b=oqzESFVmpLDqCM1ogUtpVeHfoItcKy4ys4Eb1DMEMJyVVPC0v6AxKV4JqnXc8uzsXNstZ/ta35CV1XDEbbqfGFWQLsSI6QjsIUWIHGq9eSYxT1O6EKODZplspDjvoTP3S0JCuBKainAtTSG9EMNgZatZYHgkmtTXi39PzEGLPb0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216020; c=relaxed/simple; bh=x9+BBsUWn5CT6heUB5Cn0XLl9xnBkRFy8cicf5/iK1w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EaDrOmi2Ao6ykhDy2wNvpIwoIVJRb5KYQPUOSBTh1LbEo19DVuVQAP0p6k5qh5/30tadGaHmyM3UvklH8tCYU2JPqh1de+nqSX1tYiwJ4SiKGxsHpKX3aC13NabI5VhzcPRtzdsv6hoYbAqBYZMSg2gxy4cMaOci6Nv1a4uFAEA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Z/BPyKfG; arc=none smtp.client-ip=91.218.175.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Z/BPyKfG" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ovDVNBJHIHUHnzYc0hryULdGfBYXL9xjKAwOPlwvfkw=; b=Z/BPyKfGQ0BpXJOwC2hKF4LUf8P/GRdFXO/7xOlMxdsYclhrIm0PNWqdhnHlG3bTPKccvu pUEjHwjIHET+RmGZnVdplBgY7Y7sh8Ys5KL23kTIwleDpT0CQVwoUJGIMaVN3gK/xSvnv+ sl4tNYmH66kpOfSyh+JNiqupAiCrMe0= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 7/8] bcachefs: bcachefs_metadata_version_snapshot_deletion_v2 Date: Fri, 2 May 2025 15:59:59 -0400 Message-ID: <20250502200002.1309862-8-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" We're going to be speeding up snapshot deletion, by only having it process the extents/dirents/xattrs btrees if an inode of a given snapshot ID was present. This raises the possibility of 'bkey_in_missing_snapshot' errors popping up, if we ever accidentally don't do the corresponding inode update, or if the new algorithm has bugs. So instead of deleting snapshot IDs, add a new deleted flag, so that 'key in missing snapshot' errors can more definitively tell what happened and automatically repair. Signed-off-by: Kent Overstreet --- fs/bcachefs/bcachefs_format.h | 3 +- fs/bcachefs/sb-errors_format.h | 3 +- fs/bcachefs/snapshot.c | 80 ++++++++++++++++++++++++++-------- fs/bcachefs/snapshot.h | 25 ++++++++--- fs/bcachefs/snapshot_format.h | 2 +- fs/bcachefs/snapshot_types.h | 30 +++++++++++++ fs/bcachefs/subvolume_types.h | 27 ------------ 7 files changed, 116 insertions(+), 54 deletions(-) diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index 7ce475c565b5..0beff6af7ecf 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -695,7 +695,8 @@ struct bch_sb_field_ext { x(stripe_backpointers, BCH_VERSION(1, 22)) \ x(stripe_lru, BCH_VERSION(1, 23)) \ x(casefolding, BCH_VERSION(1, 24)) \ - x(extent_flags, BCH_VERSION(1, 25)) + x(extent_flags, BCH_VERSION(1, 25)) \ + x(snapshot_deletion_v2, BCH_VERSION(1, 26)) =20 enum bcachefs_metadata_version { bcachefs_metadata_version_min =3D 9, diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index 82bc1906aa00..448326c01d13 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -209,6 +209,7 @@ enum bch_fsck_flags { x(subvol_to_missing_root, 188, 0) \ x(subvol_root_wrong_bi_subvol, 189, FSCK_AUTOFIX) \ x(bkey_in_missing_snapshot, 190, 0) \ + x(bkey_in_deleted_snapshot, 315, 0) \ x(inode_pos_inode_nonzero, 191, 0) \ x(inode_pos_blockdev_range, 192, 0) \ x(inode_alloc_cursor_inode_bad, 301, 0) \ @@ -324,7 +325,7 @@ enum bch_fsck_flags { x(dirent_stray_data_after_cf_name, 305, 0) \ x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \ x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \ - x(MAX, 315, 0) + x(MAX, 316, 0) =20 enum bch_sb_error_id { #define x(t, n, ...) BCH_FSCK_ERR_##t =3D n, diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c index 7349f7f33a4f..f074b9de5024 100644 --- a/fs/bcachefs/snapshot.c +++ b/fs/bcachefs/snapshot.c @@ -314,7 +314,9 @@ static int __bch2_mark_snapshot(struct btree_trans *tra= ns, if (new.k->type =3D=3D KEY_TYPE_snapshot) { struct bkey_s_c_snapshot s =3D bkey_s_c_to_snapshot(new); =20 - t->live =3D true; + t->state =3D !BCH_SNAPSHOT_DELETED(s.v) + ? SNAPSHOT_ID_live + : SNAPSHOT_ID_deleted; t->parent =3D le32_to_cpu(s.v->parent); t->children[0] =3D le32_to_cpu(s.v->children[0]); t->children[1] =3D le32_to_cpu(s.v->children[1]); @@ -711,6 +713,9 @@ static int check_snapshot(struct btree_trans *trans, memset(&s, 0, sizeof(s)); memcpy(&s, k.v, min(sizeof(s), bkey_val_bytes(k.k))); =20 + if (BCH_SNAPSHOT_DELETED(&s)) + return 0; + id =3D le32_to_cpu(s.parent); if (id) { ret =3D bch2_snapshot_lookup(trans, id, &v); @@ -998,7 +1003,7 @@ int bch2_reconstruct_snapshots(struct bch_fs *c) snapshot_id_list_to_text(&buf, t); =20 darray_for_each(*t, id) { - if (fsck_err_on(!bch2_snapshot_exists(c, *id), + if (fsck_err_on(bch2_snapshot_id_state(c, *id) =3D=3D SNAPSHOT_ID_empty, trans, snapshot_node_missing, "snapshot node %u from tree %s missing, recreate?", *id, buf.buf)) { if (t->nr > 1) { @@ -1023,22 +1028,38 @@ int bch2_reconstruct_snapshots(struct bch_fs *c) return ret; } =20 -int bch2_check_key_has_snapshot(struct btree_trans *trans, - struct btree_iter *iter, - struct bkey_s_c k) +int __bch2_check_key_has_snapshot(struct btree_trans *trans, + struct btree_iter *iter, + struct bkey_s_c k) { struct bch_fs *c =3D trans->c; struct printbuf buf =3D PRINTBUF; int ret =3D 0; + enum snapshot_id_state state =3D bch2_snapshot_id_state(c, k.k->p.snapsho= t); + + /* Snapshot was definitively deleted, this error is marked autofix */ + if (fsck_err_on(state =3D=3D SNAPSHOT_ID_deleted, + trans, bkey_in_deleted_snapshot, + "key in deleted snapshot %s, delete?", + (bch2_btree_id_to_text(&buf, iter->btree_id), + prt_char(&buf, ' '), + bch2_bkey_val_to_text(&buf, c, k), buf.buf))) + ret =3D bch2_btree_delete_at(trans, iter, + BTREE_UPDATE_internal_snapshot_node) ?: 1; =20 - if (fsck_err_on(!bch2_snapshot_exists(c, k.k->p.snapshot), + /* + * Snapshot missing: we should have caught this with btree_lost_data and + * kicked off reconstruct_snapshots, so if we end up here we have no + * idea what happened: + */ + if (fsck_err_on(state =3D=3D SNAPSHOT_ID_empty, trans, bkey_in_missing_snapshot, "key in missing snapshot %s, delete?", (bch2_btree_id_to_text(&buf, iter->btree_id), prt_char(&buf, ' '), bch2_bkey_val_to_text(&buf, c, k), buf.buf))) ret =3D bch2_btree_delete_at(trans, iter, - BTREE_UPDATE_internal_snapshot_node) ?: 1; + BTREE_UPDATE_internal_snapshot_node) ?: 1; fsck_err: printbuf_exit(&buf); return ret; @@ -1085,24 +1106,25 @@ static int bch2_snapshot_node_delete(struct btree_t= rans *trans, u32 id) struct btree_iter iter, p_iter =3D {}; struct btree_iter c_iter =3D {}; struct btree_iter tree_iter =3D {}; - struct bkey_s_c_snapshot s; u32 parent_id, child_id; unsigned i; int ret =3D 0; =20 - s =3D bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_snapshots, POS(0, i= d), - BTREE_ITER_intent, snapshot); - ret =3D bkey_err(s); + struct bkey_i_snapshot *s =3D + bch2_bkey_get_mut_typed(trans, &iter, BTREE_ID_snapshots, POS(0, id), + BTREE_ITER_intent, snapshot); + ret =3D PTR_ERR_OR_ZERO(s); bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOENT), c, "missing snapshot %u", id); =20 if (ret) goto err; =20 - BUG_ON(s.v->children[1]); + BUG_ON(BCH_SNAPSHOT_DELETED(&s->v)); + BUG_ON(s->v.children[1]); =20 - parent_id =3D le32_to_cpu(s.v->parent); - child_id =3D le32_to_cpu(s.v->children[0]); + parent_id =3D le32_to_cpu(s->v.parent); + child_id =3D le32_to_cpu(s->v.children[0]); =20 if (parent_id) { struct bkey_i_snapshot *parent; @@ -1160,24 +1182,38 @@ static int bch2_snapshot_node_delete(struct btree_t= rans *trans, u32 id) */ struct bkey_i_snapshot_tree *s_t; =20 - BUG_ON(s.v->children[1]); + BUG_ON(s->v.children[1]); =20 s_t =3D bch2_bkey_get_mut_typed(trans, &tree_iter, - BTREE_ID_snapshot_trees, POS(0, le32_to_cpu(s.v->tree)), + BTREE_ID_snapshot_trees, POS(0, le32_to_cpu(s->v.tree)), 0, snapshot_tree); ret =3D PTR_ERR_OR_ZERO(s_t); if (ret) goto err; =20 - if (s.v->children[0]) { - s_t->v.root_snapshot =3D s.v->children[0]; + if (s->v.children[0]) { + s_t->v.root_snapshot =3D s->v.children[0]; } else { s_t->k.type =3D KEY_TYPE_deleted; set_bkey_val_u64s(&s_t->k, 0); } } =20 - ret =3D bch2_btree_delete_at(trans, &iter, 0); + if (!bch2_request_incompat_feature(c, bcachefs_metadata_version_snapshot_= deletion_v2)) { + SET_BCH_SNAPSHOT_DELETED(&s->v, true); + s->v.parent =3D 0; + s->v.children[0] =3D 0; + s->v.children[1] =3D 0; + s->v.subvol =3D 0; + s->v.tree =3D 0; + s->v.depth =3D 0; + s->v.skip[0] =3D 0; + s->v.skip[1] =3D 0; + s->v.skip[2] =3D 0; + } else { + s->k.type =3D KEY_TYPE_deleted; + set_bkey_val_u64s(&s->k, 0); + } err: bch2_trans_iter_exit(trans, &tree_iter); bch2_trans_iter_exit(trans, &p_iter); @@ -1468,6 +1504,9 @@ static int check_should_delete_snapshot(struct btree_= trans *trans, struct bkey_s if (BCH_SNAPSHOT_SUBVOL(s.v)) return 0; =20 + if (BCH_SNAPSHOT_DELETED(s.v)) + return 0; + for (unsigned i =3D 0; i < 2; i++) { u32 child =3D le32_to_cpu(s.v->children[i]); =20 @@ -1524,6 +1563,9 @@ static int bch2_fix_child_of_deleted_snapshot(struct = btree_trans *trans, struct bkey_i_snapshot *s; int ret; =20 + if (!bch2_snapshot_exists(c, k.k->p.offset)) + return 0; + if (k.k->type !=3D KEY_TYPE_snapshot) return 0; =20 diff --git a/fs/bcachefs/snapshot.h b/fs/bcachefs/snapshot.h index 24a451bb7024..69c484b77729 100644 --- a/fs/bcachefs/snapshot.h +++ b/fs/bcachefs/snapshot.h @@ -120,21 +120,26 @@ static inline u32 bch2_snapshot_root(struct bch_fs *c= , u32 id) return id; } =20 -static inline bool __bch2_snapshot_exists(struct bch_fs *c, u32 id) +static inline enum snapshot_id_state __bch2_snapshot_id_state(struct bch_f= s *c, u32 id) { const struct snapshot_t *s =3D snapshot_t(c, id); - return s ? s->live : 0; + return s ? s->state : SNAPSHOT_ID_empty; } =20 -static inline bool bch2_snapshot_exists(struct bch_fs *c, u32 id) +static inline enum snapshot_id_state bch2_snapshot_id_state(struct bch_fs = *c, u32 id) { rcu_read_lock(); - bool ret =3D __bch2_snapshot_exists(c, id); + enum snapshot_id_state ret =3D __bch2_snapshot_id_state(c, id); rcu_read_unlock(); =20 return ret; } =20 +static inline bool bch2_snapshot_exists(struct bch_fs *c, u32 id) +{ + return bch2_snapshot_id_state(c, id) =3D=3D SNAPSHOT_ID_live; +} + static inline int bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id) { rcu_read_lock(); @@ -241,7 +246,17 @@ int bch2_snapshot_node_create(struct btree_trans *, u3= 2, int bch2_check_snapshot_trees(struct bch_fs *); int bch2_check_snapshots(struct bch_fs *); int bch2_reconstruct_snapshots(struct bch_fs *); -int bch2_check_key_has_snapshot(struct btree_trans *, struct btree_iter *,= struct bkey_s_c); + +int __bch2_check_key_has_snapshot(struct btree_trans *, struct btree_iter = *, struct bkey_s_c); + +static inline int bch2_check_key_has_snapshot(struct btree_trans *trans, + struct btree_iter *iter, + struct bkey_s_c k) +{ + return likely(bch2_snapshot_exists(trans->c, k.k->p.snapshot)) + ? 0 + : __bch2_check_key_has_snapshot(trans, iter, k); +} =20 int bch2_snapshot_node_set_deleted(struct btree_trans *, u32); =20 diff --git a/fs/bcachefs/snapshot_format.h b/fs/bcachefs/snapshot_format.h index 685a9fe209ab..9bccae1f3590 100644 --- a/fs/bcachefs/snapshot_format.h +++ b/fs/bcachefs/snapshot_format.h @@ -16,9 +16,9 @@ struct bch_snapshot { }; =20 LE32_BITMASK(BCH_SNAPSHOT_WILL_DELETE, struct bch_snapshot, flags, 0, 1) - /* True if a subvolume points to this snapshot node: */ LE32_BITMASK(BCH_SNAPSHOT_SUBVOL, struct bch_snapshot, flags, 1, 2) +LE32_BITMASK(BCH_SNAPSHOT_DELETED, struct bch_snapshot, flags, 2, 3) =20 /* * Snapshot trees: diff --git a/fs/bcachefs/snapshot_types.h b/fs/bcachefs/snapshot_types.h index 39fb47f43183..a64f4b942655 100644 --- a/fs/bcachefs/snapshot_types.h +++ b/fs/bcachefs/snapshot_types.h @@ -3,8 +3,38 @@ #define _BCACHEFS_SNAPSHOT_TYPES_H =20 #include "bbpos_types.h" +#include "darray.h" #include "subvolume_types.h" =20 +typedef DARRAY(u32) snapshot_id_list; + +#define IS_ANCESTOR_BITMAP 128 + +struct snapshot_t { + enum snapshot_id_state { + SNAPSHOT_ID_empty, + SNAPSHOT_ID_live, + SNAPSHOT_ID_deleted, + } state; + u32 parent; + u32 skip[3]; + u32 depth; + u32 children[2]; + u32 subvol; /* Nonzero only if a subvolume points to this node: */ + u32 tree; + unsigned long is_ancestor[BITS_TO_LONGS(IS_ANCESTOR_BITMAP)]; +}; + +struct snapshot_table { + struct rcu_head rcu; + size_t nr; +#ifndef RUST_BINDGEN + DECLARE_FLEX_ARRAY(struct snapshot_t, s); +#else + struct snapshot_t s[0]; +#endif +}; + struct snapshot_interior_delete { u32 id; u32 live_child; diff --git a/fs/bcachefs/subvolume_types.h b/fs/bcachefs/subvolume_types.h index 1549d6daf7af..9d634b906dcd 100644 --- a/fs/bcachefs/subvolume_types.h +++ b/fs/bcachefs/subvolume_types.h @@ -2,33 +2,6 @@ #ifndef _BCACHEFS_SUBVOLUME_TYPES_H #define _BCACHEFS_SUBVOLUME_TYPES_H =20 -#include "darray.h" - -typedef DARRAY(u32) snapshot_id_list; - -#define IS_ANCESTOR_BITMAP 128 - -struct snapshot_t { - bool live; - u32 parent; - u32 skip[3]; - u32 depth; - u32 children[2]; - u32 subvol; /* Nonzero only if a subvolume points to this node: */ - u32 tree; - unsigned long is_ancestor[BITS_TO_LONGS(IS_ANCESTOR_BITMAP)]; -}; - -struct snapshot_table { - struct rcu_head rcu; - size_t nr; -#ifndef RUST_BINDGEN - DECLARE_FLEX_ARRAY(struct snapshot_t, s); -#else - struct snapshot_t s[0]; -#endif -}; - typedef struct { /* we can't have padding in this struct: */ u64 subvol; --=20 2.49.0 From nobody Sat Feb 7 23:12:04 2026 Received: from out-184.mta0.migadu.com (out-184.mta0.migadu.com [91.218.175.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CDCC120C46B for ; Fri, 2 May 2025 20:00:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216021; cv=none; b=nkSuX7VF22gEZE4mVMDfYZRcvoLnECoAMHf3auk2uowN0C3aCa179arHb8DL+zXx5od7yIlTVL0RaQNAq0kvnJ1oAmVbs3d+rHRiKOpd0LYXu6cZI+TC+2/imxfNqewpTuY1TiR/S33QtUJ3CMSMqP2KUagtL+XfdAKhqeVcb+4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746216021; c=relaxed/simple; bh=olfLATCqt7MYlXIiSukI3idO0XtyHFLFR+HrqBPxAOw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=I8PFhw0fy4BWcazGrV5NDr7fqfL6tPoLguxN5xSSCmr0eoGnRAwPh6HiLhJKZgW3KHTqDF/gnITFEVWRtig8K/P5EOGnQP1Hs5Zl+q2PjKDUNtvINmS6hoii+Zy6LQo0Oas25ly6wdIvPrr3ERMPxXn9P1U+XEDAAxVsnlYJ410= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=n53WbPyX; arc=none smtp.client-ip=91.218.175.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="n53WbPyX" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1746216015; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Icd2jt5qAGvJ0P+TDlSTRon6g9asG4XriI8ehob+oLU=; b=n53WbPyXqYfS7huHyfTbwHRzsquUPdee7TXk/TUeZGIyqj6zJkLtBXzeOoTte9mb1WyvCR 15CJUoMDJFx340f80+4TAKXdGwbTNIMFmmQiQbCwWO9gv4VfbaOP32CAn2+bKvHq3ZrJBN oox1PAzJhbkMnoiMoxd4jcSFxvO0GCY= From: Kent Overstreet To: linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Kent Overstreet Subject: [PATCH 8/8] bcachefs: delete_dead_snapshot_keys_v2() Date: Fri, 2 May 2025 16:00:00 -0400 Message-ID: <20250502200002.1309862-9-kent.overstreet@linux.dev> In-Reply-To: <20250502200002.1309862-1-kent.overstreet@linux.dev> References: <20250502200002.1309862-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Since extents, dirents and xattrs require an inode with the corresponding snapshot ID to exists, we can avoid a lot of scanning by only scanning those trees for keys to process if the correspending inode exists. Signed-off-by: Kent Overstreet --- fs/bcachefs/snapshot.c | 160 ++++++++++++++++++++++++++++++++++------- 1 file changed, 133 insertions(+), 27 deletions(-) diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c index f074b9de5024..83b95269d38d 100644 --- a/fs/bcachefs/snapshot.c +++ b/fs/bcachefs/snapshot.c @@ -1427,6 +1427,12 @@ static unsigned live_child(struct bch_fs *c, u32 id) return ret; } =20 +static bool snapshot_id_dying(struct snapshot_delete *d, unsigned id) +{ + return snapshot_list_has_id(&d->delete_leaves, id) || + interior_delete_has_id(&d->delete_interior, id) !=3D 0; +} + static int delete_dead_snapshots_process_key(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k) @@ -1486,6 +1492,126 @@ static bool skip_unrelated_snapshot_tree(struct btr= ee_trans *trans, struct btree return ret; } =20 +static int delete_dead_snapshot_keys_v1(struct btree_trans *trans) +{ + struct bch_fs *c =3D trans->c; + struct snapshot_delete *d =3D &c->snapshot_delete; + + for (d->pos.btree =3D 0; d->pos.btree < BTREE_ID_NR; d->pos.btree++) { + struct disk_reservation res =3D { 0 }; + + d->pos.pos =3D POS_MIN; + + if (!btree_type_has_snapshots(d->pos.btree)) + continue; + + int ret =3D for_each_btree_key_commit(trans, iter, + d->pos.btree, POS_MIN, + BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, + &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ + d->pos.pos =3D iter.pos; + + if (skip_unrelated_snapshot_tree(trans, &iter)) + continue; + + delete_dead_snapshots_process_key(trans, &iter, k); + })); + + bch2_disk_reservation_put(c, &res); + + if (ret) + return ret; + } + + return 0; +} + +static int delete_dead_snapshot_keys_range(struct btree_trans *trans, enum= btree_id btree, + struct bpos start, struct bpos end) +{ + struct bch_fs *c =3D trans->c; + struct snapshot_delete *d =3D &c->snapshot_delete; + struct disk_reservation res =3D { 0 }; + + d->pos.btree =3D btree; + d->pos.pos =3D POS_MIN; + + int ret =3D for_each_btree_key_max_commit(trans, iter, + btree, start, end, + BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, + &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ + d->pos.pos =3D iter.pos; + delete_dead_snapshots_process_key(trans, &iter, k); + })); + + bch2_disk_reservation_put(c, &res); + return ret; +} + +static int delete_dead_snapshot_keys_v2(struct btree_trans *trans) +{ + struct bch_fs *c =3D trans->c; + struct snapshot_delete *d =3D &c->snapshot_delete; + struct disk_reservation res =3D { 0 }; + int ret =3D 0; + + struct btree_iter iter; + bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes, POS_MIN, + BTREE_ITER_prefetch|BTREE_ITER_all_snapshots); + + while (1) { + struct bkey_s_c k; + ret =3D lockrestart_do(trans, + bkey_err(k =3D bch2_btree_iter_peek(trans, &iter))); + if (ret) + break; + + if (!k.k) + break; + + d->pos.btree =3D iter.btree_id; + d->pos.pos =3D iter.pos; + + if (skip_unrelated_snapshot_tree(trans, &iter)) + continue; + + if (snapshot_id_dying(d, k.k->p.snapshot)) { + struct bpos start =3D POS(k.k->p.offset, 0); + struct bpos end =3D POS(k.k->p.offset, U64_MAX); + + ret =3D delete_dead_snapshot_keys_range(trans, BTREE_ID_extents, star= t, end) ?: + delete_dead_snapshot_keys_range(trans, BTREE_ID_dirents, start, end) ?: + delete_dead_snapshot_keys_range(trans, BTREE_ID_xattrs, start, end); + if (ret) + break; + + bch2_btree_iter_set_pos(trans, &iter, POS(0, k.k->p.offset + 1)); + } else { + bch2_btree_iter_advance(trans, &iter); + } + } + bch2_trans_iter_exit(trans, &iter); + + if (ret) + goto err; + + ret =3D for_each_btree_key_commit(trans, iter, + BTREE_ID_inodes, POS_MIN, + BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, + &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ + d->pos.btree =3D iter.btree_id; + d->pos.pos =3D iter.pos; + + if (skip_unrelated_snapshot_tree(trans, &iter)) + continue; + + delete_dead_snapshots_process_key(trans, &iter, k); + })); +err: + bch2_disk_reservation_put(c, &res); + return ret; +} + /* * For a given snapshot, if it doesn't have a subvolume that points to it,= and * it doesn't have child snapshot nodes - it's now redundant and we can ma= rk it @@ -1666,33 +1792,13 @@ int bch2_delete_dead_snapshots(struct bch_fs *c) goto err; } =20 - for (d->pos.btree =3D 0; d->pos.btree < BTREE_ID_NR; d->pos.btree++) { - struct disk_reservation res =3D { 0 }; - - d->pos.pos =3D POS_MIN; - - if (!btree_type_has_snapshots(d->pos.btree)) - continue; - - ret =3D for_each_btree_key_commit(trans, iter, - d->pos.btree, POS_MIN, - BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, - &res, NULL, BCH_TRANS_COMMIT_no_enospc, ({ - d->pos.pos =3D iter.pos; - - if (skip_unrelated_snapshot_tree(trans, &iter)) - continue; - - delete_dead_snapshots_process_key(trans, &iter, k); - })); - - bch2_disk_reservation_put(c, &res); - - if (!bch2_err_matches(ret, EROFS)) - bch_err_msg(c, ret, "deleting keys from dying snapshots"); - if (ret) - goto err; - } + ret =3D !bch2_request_incompat_feature(c, bcachefs_metadata_version_snaps= hot_deletion_v2) + ? delete_dead_snapshot_keys_v2(trans) + : delete_dead_snapshot_keys_v1(trans); + if (!bch2_err_matches(ret, EROFS)) + bch_err_msg(c, ret, "deleting keys from dying snapshots"); + if (ret) + goto err; =20 darray_for_each(d->delete_leaves, i) { ret =3D commit_do(trans, NULL, NULL, 0, --=20 2.49.0