Skip to content

Commit 28f75a0

Browse files
committed
Btrfs: refill block reserves during truncate
When truncate starts, it allocates some space in the block reserves so that we'll have enough to update metadata along the way. For very large files, we can easily go through all of that space as we loop through the extents. This changes truncate to refill the space reservation as it progresses through the file. Signed-off-by: Chris Mason <[email protected]>
1 parent 1262133 commit 28f75a0

File tree

3 files changed

+46
-11
lines changed

3 files changed

+46
-11
lines changed

fs/btrfs/ctree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,9 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
32973297
}
32983298

32993299
/* extent-tree.c */
3300+
3301+
u64 btrfs_csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes);
3302+
33003303
static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
33013304
unsigned num_items)
33023305
{

fs/btrfs/extent-tree.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,7 +2636,7 @@ static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads)
26362636
* Takes the number of bytes to be csumm'ed and figures out how many leaves it
26372637
* would require to store the csums for that many bytes.
26382638
*/
2639-
static u64 csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes)
2639+
u64 btrfs_csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes)
26402640
{
26412641
u64 csum_size;
26422642
u64 num_csums_per_leaf;
@@ -2665,7 +2665,7 @@ int btrfs_check_space_for_delayed_refs(struct btrfs_trans_handle *trans,
26652665
if (num_heads > 1)
26662666
num_bytes += (num_heads - 1) * root->nodesize;
26672667
num_bytes <<= 1;
2668-
num_bytes += csum_bytes_to_leaves(root, csum_bytes) * root->nodesize;
2668+
num_bytes += btrfs_csum_bytes_to_leaves(root, csum_bytes) * root->nodesize;
26692669
global_rsv = &root->fs_info->global_block_rsv;
26702670

26712671
/*
@@ -5098,13 +5098,12 @@ static u64 calc_csum_metadata_size(struct inode *inode, u64 num_bytes,
50985098
BTRFS_I(inode)->csum_bytes == 0)
50995099
return 0;
51005100

5101-
old_csums = csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
5102-
5101+
old_csums = btrfs_csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
51035102
if (reserve)
51045103
BTRFS_I(inode)->csum_bytes += num_bytes;
51055104
else
51065105
BTRFS_I(inode)->csum_bytes -= num_bytes;
5107-
num_csums = csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
5106+
num_csums = btrfs_csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
51085107

51095108
/* No change, no need to reserve more */
51105109
if (old_csums == num_csums)

fs/btrfs/inode.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4163,6 +4163,21 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
41634163
return err;
41644164
}
41654165

4166+
static int truncate_space_check(struct btrfs_trans_handle *trans,
4167+
struct btrfs_root *root,
4168+
u64 bytes_deleted)
4169+
{
4170+
int ret;
4171+
4172+
bytes_deleted = btrfs_csum_bytes_to_leaves(root, bytes_deleted);
4173+
ret = btrfs_block_rsv_add(root, &root->fs_info->trans_block_rsv,
4174+
bytes_deleted, BTRFS_RESERVE_NO_FLUSH);
4175+
if (!ret)
4176+
trans->bytes_reserved += bytes_deleted;
4177+
return ret;
4178+
4179+
}
4180+
41664181
/*
41674182
* this can truncate away extent items, csum items and directory items.
41684183
* It starts at a high offset and removes keys until it can't find
@@ -4201,6 +4216,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
42014216
u64 bytes_deleted = 0;
42024217
bool be_nice = 0;
42034218
bool should_throttle = 0;
4219+
bool should_end = 0;
42044220

42054221
BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY);
42064222

@@ -4396,6 +4412,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
43964412
} else {
43974413
break;
43984414
}
4415+
should_throttle = 0;
4416+
43994417
if (found_extent &&
44004418
(test_bit(BTRFS_ROOT_REF_COWS, &root->state) ||
44014419
root == root->fs_info->tree_root)) {
@@ -4409,17 +4427,24 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
44094427
if (btrfs_should_throttle_delayed_refs(trans, root))
44104428
btrfs_async_run_delayed_refs(root,
44114429
trans->delayed_ref_updates * 2, 0);
4430+
if (be_nice) {
4431+
if (truncate_space_check(trans, root,
4432+
extent_num_bytes)) {
4433+
should_end = 1;
4434+
}
4435+
if (btrfs_should_throttle_delayed_refs(trans,
4436+
root)) {
4437+
should_throttle = 1;
4438+
}
4439+
}
44124440
}
44134441

44144442
if (found_type == BTRFS_INODE_ITEM_KEY)
44154443
break;
44164444

4417-
should_throttle =
4418-
btrfs_should_throttle_delayed_refs(trans, root);
4419-
44204445
if (path->slots[0] == 0 ||
44214446
path->slots[0] != pending_del_slot ||
4422-
(be_nice && should_throttle)) {
4447+
should_throttle || should_end) {
44234448
if (pending_del_nr) {
44244449
ret = btrfs_del_items(trans, root, path,
44254450
pending_del_slot,
@@ -4432,7 +4457,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
44324457
pending_del_nr = 0;
44334458
}
44344459
btrfs_release_path(path);
4435-
if (be_nice && should_throttle) {
4460+
if (should_throttle) {
44364461
unsigned long updates = trans->delayed_ref_updates;
44374462
if (updates) {
44384463
trans->delayed_ref_updates = 0;
@@ -4441,6 +4466,14 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
44414466
err = ret;
44424467
}
44434468
}
4469+
/*
4470+
* if we failed to refill our space rsv, bail out
4471+
* and let the transaction restart
4472+
*/
4473+
if (should_end) {
4474+
err = -EAGAIN;
4475+
goto error;
4476+
}
44444477
goto search_again;
44454478
} else {
44464479
path->slots[0]--;
@@ -4460,7 +4493,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
44604493

44614494
btrfs_free_path(path);
44624495

4463-
if (be_nice && btrfs_should_throttle_delayed_refs(trans, root)) {
4496+
if (be_nice && bytes_deleted > 32 * 1024 * 1024) {
44644497
unsigned long updates = trans->delayed_ref_updates;
44654498
if (updates) {
44664499
trans->delayed_ref_updates = 0;

0 commit comments

Comments
 (0)