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: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
@@ -5664,10 +5664,10 @@ out_fail:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user