Skip to content

Commit 3672bda

Browse files
author
Kent Overstreet
committed
bcachefs: fix transaction restart handling in check_extents(), check_dirents()
Dealing with outside state within a btree transaction is always tricky. check_extents() and check_dirents() have to accumulate counters for i_sectors and i_nlink (for subdirectories). There were two bugs: - transaction commit may return a restart; therefore we have to commit before accumulating to those counters - get_inode_all_snapshots() may return a transaction restart, before updating w->last_pos; then, on the restart, check_i_sectors()/check_subdir_count() would see inodes that were not for w->last_pos Signed-off-by: Kent Overstreet <[email protected]>
1 parent 22a507d commit 3672bda

File tree

1 file changed

+55
-39
lines changed

1 file changed

+55
-39
lines changed

fs/bcachefs/fsck.c

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ struct inode_walker_entry {
631631

632632
struct inode_walker {
633633
bool first_this_inode;
634+
bool have_inodes;
634635
bool recalculate_sums;
635636
struct bpos last_pos;
636637

@@ -668,6 +669,12 @@ static int get_inodes_all_snapshots(struct btree_trans *trans,
668669
struct bkey_s_c k;
669670
int ret;
670671

672+
/*
673+
* We no longer have inodes for w->last_pos; clear this to avoid
674+
* screwing up check_i_sectors/check_subdir_count if we take a
675+
* transaction restart here:
676+
*/
677+
w->have_inodes = false;
671678
w->recalculate_sums = false;
672679
w->inodes.nr = 0;
673680

@@ -685,6 +692,7 @@ static int get_inodes_all_snapshots(struct btree_trans *trans,
685692
return ret;
686693

687694
w->first_this_inode = true;
695+
w->have_inodes = true;
688696
return 0;
689697
}
690698

@@ -1551,10 +1559,10 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
15511559
struct bkey_s_c k,
15521560
struct inode_walker *inode,
15531561
struct snapshots_seen *s,
1554-
struct extent_ends *extent_ends)
1562+
struct extent_ends *extent_ends,
1563+
struct disk_reservation *res)
15551564
{
15561565
struct bch_fs *c = trans->c;
1557-
struct inode_walker_entry *i;
15581566
struct printbuf buf = PRINTBUF;
15591567
int ret = 0;
15601568

@@ -1564,7 +1572,7 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
15641572
goto out;
15651573
}
15661574

1567-
if (inode->last_pos.inode != k.k->p.inode) {
1575+
if (inode->last_pos.inode != k.k->p.inode && inode->have_inodes) {
15681576
ret = check_i_sectors(trans, inode);
15691577
if (ret)
15701578
goto err;
@@ -1574,12 +1582,12 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
15741582
if (ret)
15751583
goto err;
15761584

1577-
i = walk_inode(trans, inode, k);
1578-
ret = PTR_ERR_OR_ZERO(i);
1585+
struct inode_walker_entry *extent_i = walk_inode(trans, inode, k);
1586+
ret = PTR_ERR_OR_ZERO(extent_i);
15791587
if (ret)
15801588
goto err;
15811589

1582-
ret = check_key_has_inode(trans, iter, inode, i, k);
1590+
ret = check_key_has_inode(trans, iter, inode, extent_i, k);
15831591
if (ret)
15841592
goto err;
15851593

@@ -1588,24 +1596,19 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
15881596
&inode->recalculate_sums);
15891597
if (ret)
15901598
goto err;
1591-
}
1592-
1593-
/*
1594-
* Check inodes in reverse order, from oldest snapshots to newest,
1595-
* starting from the inode that matches this extent's snapshot. If we
1596-
* didn't have one, iterate over all inodes:
1597-
*/
1598-
if (!i)
1599-
i = &darray_last(inode->inodes);
16001599

1601-
for (;
1602-
inode->inodes.data && i >= inode->inodes.data;
1603-
--i) {
1604-
if (i->snapshot > k.k->p.snapshot ||
1605-
!key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot))
1606-
continue;
1600+
/*
1601+
* Check inodes in reverse order, from oldest snapshots to
1602+
* newest, starting from the inode that matches this extent's
1603+
* snapshot. If we didn't have one, iterate over all inodes:
1604+
*/
1605+
for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
1606+
inode->inodes.data && i >= inode->inodes.data;
1607+
--i) {
1608+
if (i->snapshot > k.k->p.snapshot ||
1609+
!key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot))
1610+
continue;
16071611

1608-
if (k.k->type != KEY_TYPE_whiteout) {
16091612
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_i_size_dirty) &&
16101613
k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
16111614
!bkey_extent_is_reservation(k),
@@ -1625,10 +1628,24 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
16251628
goto err;
16261629

16271630
iter->k.type = KEY_TYPE_whiteout;
1631+
break;
16281632
}
1633+
}
1634+
}
1635+
1636+
ret = bch2_trans_commit(trans, res, NULL, BCH_TRANS_COMMIT_no_enospc);
1637+
if (ret)
1638+
goto err;
1639+
1640+
if (bkey_extent_is_allocation(k.k)) {
1641+
for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
1642+
inode->inodes.data && i >= inode->inodes.data;
1643+
--i) {
1644+
if (i->snapshot > k.k->p.snapshot ||
1645+
!key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot))
1646+
continue;
16291647

1630-
if (bkey_extent_is_allocation(k.k))
1631-
i->count += k.k->size;
1648+
i->count += k.k->size;
16321649
}
16331650
}
16341651

@@ -1660,13 +1677,11 @@ int bch2_check_extents(struct bch_fs *c)
16601677
extent_ends_init(&extent_ends);
16611678

16621679
int ret = bch2_trans_run(c,
1663-
for_each_btree_key_commit(trans, iter, BTREE_ID_extents,
1680+
for_each_btree_key(trans, iter, BTREE_ID_extents,
16641681
POS(BCACHEFS_ROOT_INO, 0),
1665-
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
1666-
&res, NULL,
1667-
BCH_TRANS_COMMIT_no_enospc, ({
1682+
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, ({
16681683
bch2_disk_reservation_put(c, &res);
1669-
check_extent(trans, &iter, k, &w, &s, &extent_ends) ?:
1684+
check_extent(trans, &iter, k, &w, &s, &extent_ends, &res) ?:
16701685
check_extent_overbig(trans, &iter, k);
16711686
})) ?:
16721687
check_i_sectors_notnested(trans, &w));
@@ -2068,7 +2083,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
20682083
if (k.k->type == KEY_TYPE_whiteout)
20692084
goto out;
20702085

2071-
if (dir->last_pos.inode != k.k->p.inode) {
2086+
if (dir->last_pos.inode != k.k->p.inode && dir->have_inodes) {
20722087
ret = check_subdir_count(trans, dir);
20732088
if (ret)
20742089
goto err;
@@ -2130,11 +2145,15 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
21302145
if (ret)
21312146
goto err;
21322147
}
2133-
2134-
if (d.v->d_type == DT_DIR)
2135-
for_each_visible_inode(c, s, dir, d.k->p.snapshot, i)
2136-
i->count++;
21372148
}
2149+
2150+
ret = bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
2151+
if (ret)
2152+
goto err;
2153+
2154+
if (d.v->d_type == DT_DIR)
2155+
for_each_visible_inode(c, s, dir, d.k->p.snapshot, i)
2156+
i->count++;
21382157
out:
21392158
err:
21402159
fsck_err:
@@ -2157,12 +2176,9 @@ int bch2_check_dirents(struct bch_fs *c)
21572176
snapshots_seen_init(&s);
21582177

21592178
int ret = bch2_trans_run(c,
2160-
for_each_btree_key_commit(trans, iter, BTREE_ID_dirents,
2179+
for_each_btree_key(trans, iter, BTREE_ID_dirents,
21612180
POS(BCACHEFS_ROOT_INO, 0),
2162-
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots,
2163-
k,
2164-
NULL, NULL,
2165-
BCH_TRANS_COMMIT_no_enospc,
2181+
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
21662182
check_dirent(trans, &iter, k, &hash_info, &dir, &target, &s)) ?:
21672183
check_subdir_count_notnested(trans, &dir));
21682184

0 commit comments

Comments
 (0)