diff options
Diffstat (limited to 'libbcachefs/backpointers.c')
-rw-r--r-- | libbcachefs/backpointers.c | 155 |
1 files changed, 142 insertions, 13 deletions
diff --git a/libbcachefs/backpointers.c b/libbcachefs/backpointers.c index 7e8b1301..ee7e610f 100644 --- a/libbcachefs/backpointers.c +++ b/libbcachefs/backpointers.c @@ -6,6 +6,8 @@ #include "btree_update.h" #include "error.h" +#include <linux/mm.h> + #define MAX_EXTENT_COMPRESS_RATIO_SHIFT 10 /* @@ -802,6 +804,103 @@ err: return ret; } +struct bbpos { + enum btree_id btree; + struct bpos pos; +}; + +static inline int bbpos_cmp(struct bbpos l, struct bbpos r) +{ + return cmp_int(l.btree, r.btree) ?: bpos_cmp(l.pos, r.pos); +} + +static inline struct bbpos bbpos_successor(struct bbpos pos) +{ + if (bpos_cmp(pos.pos, SPOS_MAX)) { + pos.pos = bpos_successor(pos.pos); + return pos; + } + + if (pos.btree != BTREE_ID_NR) { + pos.btree++; + pos.pos = POS_MIN; + return pos; + } + + BUG(); +} + +#if 0 +static void bbpos_to_text(struct printbuf *out, struct bbpos pos) +{ + prt_str(out, bch2_btree_ids[pos.btree]); + prt_char(out, ':'); + bch2_bpos_to_text(out, pos.pos); +} +#endif + +static inline struct bbpos bp_to_bbpos(struct bch_backpointer bp) +{ + return (struct bbpos) { + .btree = bp.btree_id, + .pos = bp.pos, + }; +} + +int bch2_get_btree_in_memory_pos(struct btree_trans *trans, + unsigned btree_leaf_mask, + unsigned btree_interior_mask, + struct bbpos start, struct bbpos *end) +{ + struct btree_iter iter; + struct bkey_s_c k; + struct sysinfo i; + size_t btree_nodes; + enum btree_id btree; + int ret = 0; + + si_meminfo(&i); + + btree_nodes = (i.totalram >> 1) / btree_bytes(trans->c); + + for (btree = start.btree; btree < BTREE_ID_NR && !ret; btree++) { + unsigned depth = ((1U << btree) & btree_leaf_mask) ? 1 : 2; + + if (!((1U << btree) & btree_leaf_mask) && + !((1U << btree) & btree_interior_mask)) + continue; + + bch2_trans_node_iter_init(trans, &iter, btree, + btree == start.btree ? start.pos : POS_MIN, + 0, depth, 0); + /* + * for_each_btree_key_contineu() doesn't check the return value + * from bch2_btree_iter_advance(), which is needed when + * iterating over interior nodes where we'll see keys at + * SPOS_MAX: + */ + do { + k = __bch2_btree_iter_peek_and_restart(trans, &iter, 0); + ret = bkey_err(k); + if (!k.k || ret) + break; + + --btree_nodes; + if (!btree_nodes) { + end->btree = btree; + end->pos = k.k->p; + bch2_trans_iter_exit(trans, &iter); + return 0; + } + } while (bch2_btree_iter_advance(&iter)); + bch2_trans_iter_exit(trans, &iter); + } + + end->btree = BTREE_ID_NR; + end->pos = POS_MIN; + return ret; +} + int bch2_check_extents_to_backpointers(struct bch_fs *c) { struct btree_trans trans; @@ -845,19 +944,26 @@ int bch2_check_extents_to_backpointers(struct bch_fs *c) static int check_one_backpointer(struct btree_trans *trans, struct bpos bucket, - u64 *bp_offset) + u64 *bp_offset, + struct bbpos start, + struct bbpos end) { struct btree_iter iter; struct bch_backpointer bp; + struct bbpos pos; struct bkey_s_c k; struct printbuf buf = PRINTBUF; int ret; - ret = bch2_get_next_backpointer(trans, bucket, -1, - bp_offset, &bp); + ret = bch2_get_next_backpointer(trans, bucket, -1, bp_offset, &bp); if (ret || *bp_offset == U64_MAX) return ret; + pos = bp_to_bbpos(bp); + if (bbpos_cmp(pos, start) < 0 || + bbpos_cmp(pos, end) > 0) + return 0; + k = bch2_backpointer_get_key(trans, &iter, bucket, *bp_offset, bp); ret = bkey_err(k); if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node) @@ -880,29 +986,52 @@ fsck_err: return ret; } -int bch2_check_backpointers_to_extents(struct bch_fs *c) +static int bch2_check_backpointers_to_extents_pass(struct btree_trans *trans, + struct bbpos start, + struct bbpos end) { - struct btree_trans trans; struct btree_iter iter; struct bkey_s_c k; int ret = 0; - bch2_trans_init(&trans, c, 0, 0); - for_each_btree_key(&trans, iter, BTREE_ID_alloc, POS_MIN, + for_each_btree_key(trans, iter, BTREE_ID_alloc, POS_MIN, BTREE_ITER_PREFETCH, k, ret) { u64 bp_offset = 0; - while (!(ret = commit_do(&trans, NULL, NULL, - BTREE_INSERT_LAZY_RW| - BTREE_INSERT_NOFAIL, - check_one_backpointer(&trans, iter.pos, &bp_offset))) && + while (!(ret = commit_do(trans, NULL, NULL, + BTREE_INSERT_LAZY_RW| + BTREE_INSERT_NOFAIL, + check_one_backpointer(trans, iter.pos, &bp_offset, start, end))) && bp_offset < U64_MAX) bp_offset++; if (ret) break; } - bch2_trans_iter_exit(&trans, &iter); - bch2_trans_exit(&trans); + bch2_trans_iter_exit(trans, &iter); return ret < 0 ? ret : 0; } + +int bch2_check_backpointers_to_extents(struct bch_fs *c) +{ + struct btree_trans trans; + struct bbpos start = (struct bbpos) { .btree = 0, .pos = POS_MIN, }, end; + int ret; + + bch2_trans_init(&trans, c, 0, 0); + while (1) { + ret = bch2_get_btree_in_memory_pos(&trans, + (1U << BTREE_ID_extents)| + (1U << BTREE_ID_reflink), + ~0, + start, &end) ?: + bch2_check_backpointers_to_extents_pass(&trans, start, end); + if (ret || end.btree == BTREE_ID_NR) + break; + + start = bbpos_successor(end); + } + bch2_trans_exit(&trans); + + return ret; +} |