Commit 28f75a0e authored by Chris Mason's avatar Chris Mason

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: default avatarChris Mason <clm@fb.com>
parent 1262133b
...@@ -3297,6 +3297,9 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping) ...@@ -3297,6 +3297,9 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
} }
/* extent-tree.c */ /* extent-tree.c */
u64 btrfs_csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes);
static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root, static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
unsigned num_items) unsigned num_items)
{ {
......
...@@ -2636,7 +2636,7 @@ static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads) ...@@ -2636,7 +2636,7 @@ static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads)
* Takes the number of bytes to be csumm'ed and figures out how many leaves it * Takes the number of bytes to be csumm'ed and figures out how many leaves it
* would require to store the csums for that many bytes. * would require to store the csums for that many bytes.
*/ */
static u64 csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes) u64 btrfs_csum_bytes_to_leaves(struct btrfs_root *root, u64 csum_bytes)
{ {
u64 csum_size; u64 csum_size;
u64 num_csums_per_leaf; u64 num_csums_per_leaf;
...@@ -2665,7 +2665,7 @@ int btrfs_check_space_for_delayed_refs(struct btrfs_trans_handle *trans, ...@@ -2665,7 +2665,7 @@ int btrfs_check_space_for_delayed_refs(struct btrfs_trans_handle *trans,
if (num_heads > 1) if (num_heads > 1)
num_bytes += (num_heads - 1) * root->nodesize; num_bytes += (num_heads - 1) * root->nodesize;
num_bytes <<= 1; num_bytes <<= 1;
num_bytes += csum_bytes_to_leaves(root, csum_bytes) * root->nodesize; num_bytes += btrfs_csum_bytes_to_leaves(root, csum_bytes) * root->nodesize;
global_rsv = &root->fs_info->global_block_rsv; global_rsv = &root->fs_info->global_block_rsv;
/* /*
...@@ -5098,13 +5098,12 @@ static u64 calc_csum_metadata_size(struct inode *inode, u64 num_bytes, ...@@ -5098,13 +5098,12 @@ static u64 calc_csum_metadata_size(struct inode *inode, u64 num_bytes,
BTRFS_I(inode)->csum_bytes == 0) BTRFS_I(inode)->csum_bytes == 0)
return 0; return 0;
old_csums = csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes); old_csums = btrfs_csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
if (reserve) if (reserve)
BTRFS_I(inode)->csum_bytes += num_bytes; BTRFS_I(inode)->csum_bytes += num_bytes;
else else
BTRFS_I(inode)->csum_bytes -= num_bytes; BTRFS_I(inode)->csum_bytes -= num_bytes;
num_csums = csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes); num_csums = btrfs_csum_bytes_to_leaves(root, BTRFS_I(inode)->csum_bytes);
/* No change, no need to reserve more */ /* No change, no need to reserve more */
if (old_csums == num_csums) if (old_csums == num_csums)
......
...@@ -4163,6 +4163,21 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -4163,6 +4163,21 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
return err; return err;
} }
static int truncate_space_check(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytes_deleted)
{
int ret;
bytes_deleted = btrfs_csum_bytes_to_leaves(root, bytes_deleted);
ret = btrfs_block_rsv_add(root, &root->fs_info->trans_block_rsv,
bytes_deleted, BTRFS_RESERVE_NO_FLUSH);
if (!ret)
trans->bytes_reserved += bytes_deleted;
return ret;
}
/* /*
* this can truncate away extent items, csum items and directory items. * this can truncate away extent items, csum items and directory items.
* It starts at a high offset and removes keys until it can't find * 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, ...@@ -4201,6 +4216,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
u64 bytes_deleted = 0; u64 bytes_deleted = 0;
bool be_nice = 0; bool be_nice = 0;
bool should_throttle = 0; bool should_throttle = 0;
bool should_end = 0;
BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY); BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY);
...@@ -4396,6 +4412,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -4396,6 +4412,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
} else { } else {
break; break;
} }
should_throttle = 0;
if (found_extent && if (found_extent &&
(test_bit(BTRFS_ROOT_REF_COWS, &root->state) || (test_bit(BTRFS_ROOT_REF_COWS, &root->state) ||
root == root->fs_info->tree_root)) { root == root->fs_info->tree_root)) {
...@@ -4409,17 +4427,24 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -4409,17 +4427,24 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
if (btrfs_should_throttle_delayed_refs(trans, root)) if (btrfs_should_throttle_delayed_refs(trans, root))
btrfs_async_run_delayed_refs(root, btrfs_async_run_delayed_refs(root,
trans->delayed_ref_updates * 2, 0); trans->delayed_ref_updates * 2, 0);
if (be_nice) {
if (truncate_space_check(trans, root,
extent_num_bytes)) {
should_end = 1;
}
if (btrfs_should_throttle_delayed_refs(trans,
root)) {
should_throttle = 1;
}
}
} }
if (found_type == BTRFS_INODE_ITEM_KEY) if (found_type == BTRFS_INODE_ITEM_KEY)
break; break;
should_throttle =
btrfs_should_throttle_delayed_refs(trans, root);
if (path->slots[0] == 0 || if (path->slots[0] == 0 ||
path->slots[0] != pending_del_slot || path->slots[0] != pending_del_slot ||
(be_nice && should_throttle)) { should_throttle || should_end) {
if (pending_del_nr) { if (pending_del_nr) {
ret = btrfs_del_items(trans, root, path, ret = btrfs_del_items(trans, root, path,
pending_del_slot, pending_del_slot,
...@@ -4432,7 +4457,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -4432,7 +4457,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
pending_del_nr = 0; pending_del_nr = 0;
} }
btrfs_release_path(path); btrfs_release_path(path);
if (be_nice && should_throttle) { if (should_throttle) {
unsigned long updates = trans->delayed_ref_updates; unsigned long updates = trans->delayed_ref_updates;
if (updates) { if (updates) {
trans->delayed_ref_updates = 0; trans->delayed_ref_updates = 0;
...@@ -4441,6 +4466,14 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -4441,6 +4466,14 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
err = ret; err = ret;
} }
} }
/*
* if we failed to refill our space rsv, bail out
* and let the transaction restart
*/
if (should_end) {
err = -EAGAIN;
goto error;
}
goto search_again; goto search_again;
} else { } else {
path->slots[0]--; path->slots[0]--;
...@@ -4460,7 +4493,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -4460,7 +4493,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
btrfs_free_path(path); btrfs_free_path(path);
if (be_nice && btrfs_should_throttle_delayed_refs(trans, root)) { if (be_nice && bytes_deleted > 32 * 1024 * 1024) {
unsigned long updates = trans->delayed_ref_updates; unsigned long updates = trans->delayed_ref_updates;
if (updates) { if (updates) {
trans->delayed_ref_updates = 0; trans->delayed_ref_updates = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment