bcachefs: bch2_fpunch_snapshot()

Add a new version of fpunch for operating on a snapshot ID, not a
subvolume - and use it for "extent past end of inode" repair.

Previously, repair would try to delete everything at once, but deleting
too many extents at once can overflow the btree_trans bump allocator, as
well as causing other problems - the new helper properly uses
bch2_extent_trim_atomic().

Reported-and-tested-by: Edoardo Codeglia <bcachefs@404.blue>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2025-07-02 13:28:55 -04:00
parent 94426e4201
commit 63d6e93119
3 changed files with 35 additions and 27 deletions

View file

@ -12,6 +12,7 @@
#include "fs.h"
#include "fsck.h"
#include "inode.h"
#include "io_misc.h"
#include "keylist.h"
#include "namei.h"
#include "recovery_passes.h"
@ -1919,33 +1920,11 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
"extent type past end of inode %llu:%u, i_size %llu\n%s",
i->inode.bi_inum, i->inode.bi_snapshot, i->inode.bi_size,
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
struct bkey_i *whiteout = bch2_trans_kmalloc(trans, sizeof(*whiteout));
ret = PTR_ERR_OR_ZERO(whiteout);
if (ret)
goto err;
bkey_init(&whiteout->k);
whiteout->k.p = SPOS(k.k->p.inode,
last_block,
i->inode.bi_snapshot);
bch2_key_resize(&whiteout->k,
min(KEY_SIZE_MAX & (~0 << c->block_bits),
U64_MAX - whiteout->k.p.offset));
/*
* Need a normal (not BTREE_ITER_all_snapshots)
* iterator, if we're deleting in a different
* snapshot and need to emit a whiteout
*/
struct btree_iter iter2;
bch2_trans_iter_init(trans, &iter2, BTREE_ID_extents,
bkey_start_pos(&whiteout->k),
BTREE_ITER_intent);
ret = bch2_btree_iter_traverse(trans, &iter2) ?:
bch2_trans_update(trans, &iter2, whiteout,
BTREE_UPDATE_internal_snapshot_node);
bch2_trans_iter_exit(trans, &iter2);
ret = bch2_fpunch_snapshot(trans,
SPOS(i->inode.bi_inum,
last_block,
i->inode.bi_snapshot),
POS(i->inode.bi_inum, U64_MAX));
if (ret)
goto err;

View file

@ -135,6 +135,33 @@ err_noprint:
return ret;
}
/* For fsck */
int bch2_fpunch_snapshot(struct btree_trans *trans, struct bpos start, struct bpos end)
{
u32 restart_count = trans->restart_count;
struct bch_fs *c = trans->c;
struct disk_reservation disk_res = bch2_disk_reservation_init(c, 0);
unsigned max_sectors = KEY_SIZE_MAX & (~0 << c->block_bits);
struct bkey_i delete;
int ret = for_each_btree_key_max_commit(trans, iter, BTREE_ID_extents,
start, end, 0, k,
&disk_res, NULL, BCH_TRANS_COMMIT_no_enospc, ({
bkey_init(&delete.k);
delete.k.p = iter.pos;
/* create the biggest key we can */
bch2_key_resize(&delete.k, max_sectors);
bch2_cut_back(end, &delete);
bch2_extent_trim_atomic(trans, &iter, &delete) ?:
bch2_trans_update(trans, &iter, &delete, 0);
}));
bch2_disk_reservation_put(c, &disk_res);
return ret ?: trans_was_restarted(trans, restart_count);
}
/*
* Returns -BCH_ERR_transacton_restart if we had to drop locks:
*/

View file

@ -5,6 +5,8 @@
int bch2_extent_fallocate(struct btree_trans *, subvol_inum, struct btree_iter *,
u64, struct bch_io_opts, s64 *,
struct write_point_specifier);
int bch2_fpunch_snapshot(struct btree_trans *, struct bpos, struct bpos);
int bch2_fpunch_at(struct btree_trans *, struct btree_iter *,
subvol_inum, u64, s64 *);
int bch2_fpunch(struct bch_fs *c, subvol_inum, u64, u64, s64 *);