Skip to content

Commit a292be3

Browse files
author
Kent Overstreet
committed
bcachefs: Reconstruct missing snapshot nodes
When the snapshots btree is going, we'll have to delete huge amounts of data - unless we can reconstruct it by looking at the keys that refer to it. Signed-off-by: Kent Overstreet <[email protected]>
1 parent 55936af commit a292be3

File tree

6 files changed

+199
-6
lines changed

6 files changed

+199
-6
lines changed

fs/bcachefs/bcachefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ struct bch_dev {
615615
*/
616616

617617
#define BCH_FS_FLAGS() \
618+
x(new_fs) \
618619
x(started) \
619620
x(may_go_rw) \
620621
x(rw) \

fs/bcachefs/recovery.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,7 @@ int bch2_fs_initialize(struct bch_fs *c)
938938
int ret;
939939

940940
bch_notice(c, "initializing new filesystem");
941+
set_bit(BCH_FS_new_fs, &c->flags);
941942

942943
mutex_lock(&c->sb_lock);
943944
c->disk_sb.sb->compat[0] |= cpu_to_le64(1ULL << BCH_COMPAT_extents_above_btree_updates_done);

fs/bcachefs/recovery_passes_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
x(check_alloc_to_lru_refs, 15, PASS_ONLINE|PASS_FSCK) \
3333
x(fs_freespace_init, 16, PASS_ALWAYS|PASS_SILENT) \
3434
x(bucket_gens_init, 17, 0) \
35+
x(reconstruct_snapshots, 38, 0) \
3536
x(check_snapshot_trees, 18, PASS_ONLINE|PASS_FSCK) \
3637
x(check_snapshots, 19, PASS_ONLINE|PASS_FSCK) \
3738
x(check_subvols, 20, PASS_ONLINE|PASS_FSCK) \

fs/bcachefs/sb-errors_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@
268268
x(btree_node_bkey_bad_u64s, 260) \
269269
x(btree_node_topology_empty_interior_node, 261) \
270270
x(btree_ptr_v2_min_key_bad, 262) \
271-
x(btree_root_unreadable_and_scan_found_nothing, 263)
271+
x(btree_root_unreadable_and_scan_found_nothing, 263) \
272+
x(snapshot_node_missing, 264)
272273

273274
enum bch_sb_error_id {
274275
#define x(t, n) BCH_FSCK_ERR_##t = n,

fs/bcachefs/snapshot.c

Lines changed: 171 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "errcode.h"
99
#include "error.h"
1010
#include "fs.h"
11+
#include "recovery_passes.h"
1112
#include "snapshot.h"
1213

1314
#include <linux/random.h>
@@ -574,6 +575,13 @@ static int check_snapshot_tree(struct btree_trans *trans,
574575
u32 subvol_id;
575576

576577
ret = bch2_snapshot_tree_master_subvol(trans, root_id, &subvol_id);
578+
bch_err_fn(c, ret);
579+
580+
if (bch2_err_matches(ret, ENOENT)) { /* nothing to be done here */
581+
ret = 0;
582+
goto err;
583+
}
584+
577585
if (ret)
578586
goto err;
579587

@@ -731,7 +739,6 @@ static int check_snapshot(struct btree_trans *trans,
731739
u32 parent_id = bch2_snapshot_parent_early(c, k.k->p.offset);
732740
u32 real_depth;
733741
struct printbuf buf = PRINTBUF;
734-
bool should_have_subvol;
735742
u32 i, id;
736743
int ret = 0;
737744

@@ -777,7 +784,7 @@ static int check_snapshot(struct btree_trans *trans,
777784
}
778785
}
779786

780-
should_have_subvol = BCH_SNAPSHOT_SUBVOL(&s) &&
787+
bool should_have_subvol = BCH_SNAPSHOT_SUBVOL(&s) &&
781788
!BCH_SNAPSHOT_DELETED(&s);
782789

783790
if (should_have_subvol) {
@@ -879,6 +886,154 @@ int bch2_check_snapshots(struct bch_fs *c)
879886
return ret;
880887
}
881888

889+
static int check_snapshot_exists(struct btree_trans *trans, u32 id)
890+
{
891+
struct bch_fs *c = trans->c;
892+
893+
if (bch2_snapshot_equiv(c, id))
894+
return 0;
895+
896+
u32 tree_id;
897+
int ret = bch2_snapshot_tree_create(trans, id, 0, &tree_id);
898+
if (ret)
899+
return ret;
900+
901+
struct bkey_i_snapshot *snapshot = bch2_trans_kmalloc(trans, sizeof(*snapshot));
902+
ret = PTR_ERR_OR_ZERO(snapshot);
903+
if (ret)
904+
return ret;
905+
906+
bkey_snapshot_init(&snapshot->k_i);
907+
snapshot->k.p = POS(0, id);
908+
snapshot->v.tree = cpu_to_le32(tree_id);
909+
snapshot->v.btime.lo = cpu_to_le64(bch2_current_time(c));
910+
911+
return bch2_btree_insert_trans(trans, BTREE_ID_snapshots, &snapshot->k_i, 0) ?:
912+
bch2_mark_snapshot(trans, BTREE_ID_snapshots, 0,
913+
bkey_s_c_null, bkey_i_to_s(&snapshot->k_i), 0) ?:
914+
bch2_snapshot_set_equiv(trans, bkey_i_to_s_c(&snapshot->k_i));
915+
}
916+
917+
/* Figure out which snapshot nodes belong in the same tree: */
918+
struct snapshot_tree_reconstruct {
919+
enum btree_id btree;
920+
struct bpos cur_pos;
921+
snapshot_id_list cur_ids;
922+
DARRAY(snapshot_id_list) trees;
923+
};
924+
925+
static void snapshot_tree_reconstruct_exit(struct snapshot_tree_reconstruct *r)
926+
{
927+
darray_for_each(r->trees, i)
928+
darray_exit(i);
929+
darray_exit(&r->trees);
930+
darray_exit(&r->cur_ids);
931+
}
932+
933+
static inline bool same_snapshot(struct snapshot_tree_reconstruct *r, struct bpos pos)
934+
{
935+
return r->btree == BTREE_ID_inodes
936+
? r->cur_pos.offset == pos.offset
937+
: r->cur_pos.inode == pos.inode;
938+
}
939+
940+
static inline bool snapshot_id_lists_have_common(snapshot_id_list *l, snapshot_id_list *r)
941+
{
942+
darray_for_each(*l, i)
943+
if (snapshot_list_has_id(r, *i))
944+
return true;
945+
return false;
946+
}
947+
948+
static void snapshot_id_list_to_text(struct printbuf *out, snapshot_id_list *s)
949+
{
950+
bool first = true;
951+
darray_for_each(*s, i) {
952+
if (!first)
953+
prt_char(out, ' ');
954+
first = false;
955+
prt_printf(out, "%u", *i);
956+
}
957+
}
958+
959+
static int snapshot_tree_reconstruct_next(struct bch_fs *c, struct snapshot_tree_reconstruct *r)
960+
{
961+
if (r->cur_ids.nr) {
962+
darray_for_each(r->trees, i)
963+
if (snapshot_id_lists_have_common(i, &r->cur_ids)) {
964+
int ret = snapshot_list_merge(c, i, &r->cur_ids);
965+
if (ret)
966+
return ret;
967+
goto out;
968+
}
969+
darray_push(&r->trees, r->cur_ids);
970+
darray_init(&r->cur_ids);
971+
}
972+
out:
973+
r->cur_ids.nr = 0;
974+
return 0;
975+
}
976+
977+
static int get_snapshot_trees(struct bch_fs *c, struct snapshot_tree_reconstruct *r, struct bpos pos)
978+
{
979+
if (!same_snapshot(r, pos))
980+
snapshot_tree_reconstruct_next(c, r);
981+
r->cur_pos = pos;
982+
return snapshot_list_add_nodup(c, &r->cur_ids, pos.snapshot);
983+
}
984+
985+
int bch2_reconstruct_snapshots(struct bch_fs *c)
986+
{
987+
struct btree_trans *trans = bch2_trans_get(c);
988+
struct printbuf buf = PRINTBUF;
989+
struct snapshot_tree_reconstruct r = {};
990+
int ret = 0;
991+
992+
for (unsigned btree = 0; btree < BTREE_ID_NR; btree++) {
993+
if (btree_type_has_snapshots(btree)) {
994+
r.btree = btree;
995+
996+
ret = for_each_btree_key(trans, iter, btree, POS_MIN,
997+
BTREE_ITER_ALL_SNAPSHOTS|BTREE_ITER_PREFETCH, k, ({
998+
get_snapshot_trees(c, &r, k.k->p);
999+
}));
1000+
if (ret)
1001+
goto err;
1002+
1003+
snapshot_tree_reconstruct_next(c, &r);
1004+
}
1005+
}
1006+
1007+
darray_for_each(r.trees, t) {
1008+
printbuf_reset(&buf);
1009+
snapshot_id_list_to_text(&buf, t);
1010+
1011+
darray_for_each(*t, id) {
1012+
if (fsck_err_on(!bch2_snapshot_equiv(c, *id),
1013+
c, snapshot_node_missing,
1014+
"snapshot node %u from tree %s missing", *id, buf.buf)) {
1015+
if (t->nr > 1) {
1016+
bch_err(c, "cannot reconstruct snapshot trees with multiple nodes");
1017+
ret = -BCH_ERR_fsck_repair_unimplemented;
1018+
goto err;
1019+
}
1020+
1021+
ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
1022+
check_snapshot_exists(trans, *id));
1023+
if (ret)
1024+
goto err;
1025+
}
1026+
}
1027+
}
1028+
fsck_err:
1029+
err:
1030+
bch2_trans_put(trans);
1031+
snapshot_tree_reconstruct_exit(&r);
1032+
printbuf_exit(&buf);
1033+
bch_err_fn(c, ret);
1034+
return ret;
1035+
}
1036+
8821037
/*
8831038
* Mark a snapshot as deleted, for future cleanup:
8841039
*/
@@ -1689,6 +1844,20 @@ int bch2_snapshots_read(struct bch_fs *c)
16891844
POS_MIN, 0, k,
16901845
(set_is_ancestor_bitmap(c, k.k->p.offset), 0)));
16911846
bch_err_fn(c, ret);
1847+
1848+
/*
1849+
* It's important that we check if we need to reconstruct snapshots
1850+
* before going RW, so we mark that pass as required in the superblock -
1851+
* otherwise, we could end up deleting keys with missing snapshot nodes
1852+
* instead
1853+
*/
1854+
BUG_ON(!test_bit(BCH_FS_new_fs, &c->flags) &&
1855+
test_bit(BCH_FS_may_go_rw, &c->flags));
1856+
1857+
if (bch2_err_matches(ret, EIO) ||
1858+
(c->sb.btrees_lost_data & BIT_ULL(BTREE_ID_snapshots)))
1859+
ret = bch2_run_explicit_recovery_pass_persistent(c, BCH_RECOVERY_PASS_reconstruct_snapshots);
1860+
16921861
return ret;
16931862
}
16941863

fs/bcachefs/snapshot.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,34 @@ static inline bool snapshot_list_has_ancestor(struct bch_fs *c, snapshot_id_list
209209

210210
static inline int snapshot_list_add(struct bch_fs *c, snapshot_id_list *s, u32 id)
211211
{
212-
int ret;
213-
214212
BUG_ON(snapshot_list_has_id(s, id));
215-
ret = darray_push(s, id);
213+
int ret = darray_push(s, id);
216214
if (ret)
217215
bch_err(c, "error reallocating snapshot_id_list (size %zu)", s->size);
218216
return ret;
219217
}
220218

219+
static inline int snapshot_list_add_nodup(struct bch_fs *c, snapshot_id_list *s, u32 id)
220+
{
221+
int ret = snapshot_list_has_id(s, id)
222+
? 0
223+
: darray_push(s, id);
224+
if (ret)
225+
bch_err(c, "error reallocating snapshot_id_list (size %zu)", s->size);
226+
return ret;
227+
}
228+
229+
static inline int snapshot_list_merge(struct bch_fs *c, snapshot_id_list *dst, snapshot_id_list *src)
230+
{
231+
darray_for_each(*src, i) {
232+
int ret = snapshot_list_add_nodup(c, dst, *i);
233+
if (ret)
234+
return ret;
235+
}
236+
237+
return 0;
238+
}
239+
221240
int bch2_snapshot_lookup(struct btree_trans *trans, u32 id,
222241
struct bch_snapshot *s);
223242
int bch2_snapshot_get_subvol(struct btree_trans *, u32,
@@ -229,6 +248,7 @@ int bch2_snapshot_node_create(struct btree_trans *, u32,
229248

230249
int bch2_check_snapshot_trees(struct bch_fs *);
231250
int bch2_check_snapshots(struct bch_fs *);
251+
int bch2_reconstruct_snapshots(struct bch_fs *);
232252

233253
int bch2_snapshot_node_set_deleted(struct btree_trans *, u32);
234254
void bch2_delete_dead_snapshots_work(struct work_struct *);

0 commit comments

Comments
 (0)