Commit 5a303d5d authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason

Btrfs: Make fallocate(2) more ENOSPC friendly

fallocate(2) may allocate large number of file extents, so it's not
good to do it in a single transaction. This patch make fallocate(2)
start a new transaction for each file extents it allocates.
Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 2e4bfab9
...@@ -5664,10 +5664,10 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -5664,10 +5664,10 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
return err; return err;
} }
static int prealloc_file_range(struct btrfs_trans_handle *trans, static int prealloc_file_range(struct inode *inode, u64 start, u64 end,
struct inode *inode, u64 start, u64 end,
u64 alloc_hint, int mode) u64 alloc_hint, int mode)
{ {
struct btrfs_trans_handle *trans;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_key ins; struct btrfs_key ins;
u64 alloc_size; u64 alloc_size;
...@@ -5678,17 +5678,23 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans, ...@@ -5678,17 +5678,23 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
while (num_bytes > 0) { while (num_bytes > 0) {
alloc_size = min(num_bytes, root->fs_info->max_extent); alloc_size = min(num_bytes, root->fs_info->max_extent);
ret = btrfs_reserve_metadata_space(root, 1);
if (ret)
goto out;
ret = btrfs_reserve_extent(trans, root, alloc_size, ret = btrfs_reserve_extent(trans, root, alloc_size,
root->sectorsize, 0, alloc_hint, root->sectorsize, 0, alloc_hint,
(u64)-1, &ins, 1); (u64)-1, &ins, 1);
if (ret) { if (ret) {
WARN_ON(1); WARN_ON(1);
goto out; break;
}
ret = btrfs_reserve_metadata_space(root, 3);
if (ret) {
btrfs_free_reserved_extent(root, ins.objectid,
ins.offset);
break;
} }
trans = btrfs_start_transaction(root, 1);
ret = insert_reserved_file_extent(trans, inode, ret = insert_reserved_file_extent(trans, inode,
cur_offset, ins.objectid, cur_offset, ins.objectid,
ins.offset, ins.offset, ins.offset, ins.offset,
...@@ -5697,22 +5703,25 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans, ...@@ -5697,22 +5703,25 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
BUG_ON(ret); BUG_ON(ret);
btrfs_drop_extent_cache(inode, cur_offset, btrfs_drop_extent_cache(inode, cur_offset,
cur_offset + ins.offset -1, 0); cur_offset + ins.offset -1, 0);
num_bytes -= ins.offset; num_bytes -= ins.offset;
cur_offset += ins.offset; cur_offset += ins.offset;
alloc_hint = ins.objectid + ins.offset; alloc_hint = ins.objectid + ins.offset;
btrfs_unreserve_metadata_space(root, 1);
}
out:
if (cur_offset > start) {
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC; BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
if (!(mode & FALLOC_FL_KEEP_SIZE) && if (!(mode & FALLOC_FL_KEEP_SIZE) &&
cur_offset > i_size_read(inode)) cur_offset > inode->i_size) {
btrfs_i_size_write(inode, cur_offset); i_size_write(inode, cur_offset);
btrfs_ordered_update_i_size(inode, cur_offset, NULL);
}
ret = btrfs_update_inode(trans, root, inode); ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret); BUG_ON(ret);
}
btrfs_end_transaction(trans, root);
btrfs_unreserve_metadata_space(root, 3);
}
return ret; return ret;
} }
...@@ -5727,8 +5736,6 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5727,8 +5736,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
u64 locked_end; u64 locked_end;
u64 mask = BTRFS_I(inode)->root->sectorsize - 1; u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
struct extent_map *em; struct extent_map *em;
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
int ret; int ret;
alloc_start = offset & ~mask; alloc_start = offset & ~mask;
...@@ -5747,9 +5754,7 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5747,9 +5754,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
goto out; goto out;
} }
root = BTRFS_I(inode)->root; ret = btrfs_check_data_free_space(BTRFS_I(inode)->root, inode,
ret = btrfs_check_data_free_space(root, inode,
alloc_end - alloc_start); alloc_end - alloc_start);
if (ret) if (ret)
goto out; goto out;
...@@ -5758,12 +5763,6 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5758,12 +5763,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
while (1) { while (1) {
struct btrfs_ordered_extent *ordered; struct btrfs_ordered_extent *ordered;
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
if (!trans) {
ret = -EIO;
goto out_free;
}
/* the extent lock is ordered inside the running /* the extent lock is ordered inside the running
* transaction * transaction
*/ */
...@@ -5777,8 +5776,6 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5777,8 +5776,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
unlock_extent(&BTRFS_I(inode)->io_tree, unlock_extent(&BTRFS_I(inode)->io_tree,
alloc_start, locked_end, GFP_NOFS); alloc_start, locked_end, GFP_NOFS);
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
/* /*
* we can't wait on the range with the transaction * we can't wait on the range with the transaction
* running or with the extent lock held * running or with the extent lock held
...@@ -5799,9 +5796,12 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5799,9 +5796,12 @@ static long btrfs_fallocate(struct inode *inode, int mode,
BUG_ON(IS_ERR(em) || !em); BUG_ON(IS_ERR(em) || !em);
last_byte = min(extent_map_end(em), alloc_end); last_byte = min(extent_map_end(em), alloc_end);
last_byte = (last_byte + mask) & ~mask; last_byte = (last_byte + mask) & ~mask;
if (em->block_start == EXTENT_MAP_HOLE) { if (em->block_start == EXTENT_MAP_HOLE ||
ret = prealloc_file_range(trans, inode, cur_offset, (cur_offset >= inode->i_size &&
last_byte, alloc_hint, mode); !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
ret = prealloc_file_range(inode,
cur_offset, last_byte,
alloc_hint, mode);
if (ret < 0) { if (ret < 0) {
free_extent_map(em); free_extent_map(em);
break; break;
...@@ -5820,9 +5820,8 @@ static long btrfs_fallocate(struct inode *inode, int mode, ...@@ -5820,9 +5820,8 @@ static long btrfs_fallocate(struct inode *inode, int mode,
unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end, unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
GFP_NOFS); GFP_NOFS);
btrfs_end_transaction(trans, BTRFS_I(inode)->root); btrfs_free_reserved_data_space(BTRFS_I(inode)->root, inode,
out_free: alloc_end - alloc_start);
btrfs_free_reserved_data_space(root, inode, alloc_end - alloc_start);
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return ret; return ret;
......
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