Skip to content

Commit

Permalink
Btrfs: Make fallocate(2) more ENOSPC friendly
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yan, Zheng authored and Chris Mason committed Dec 17, 2009
1 parent 2e4bfab commit 5a303d5
Showing 1 changed file with 32 additions and 33 deletions.
65 changes: 32 additions & 33 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -5664,10 +5664,10 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
return err;
}

static int prealloc_file_range(struct btrfs_trans_handle *trans,
struct inode *inode, u64 start, u64 end,
static int prealloc_file_range(struct inode *inode, u64 start, u64 end,
u64 alloc_hint, int mode)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_key ins;
u64 alloc_size;
Expand All @@ -5678,17 +5678,23 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
while (num_bytes > 0) {
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,
root->sectorsize, 0, alloc_hint,
(u64)-1, &ins, 1);
if (ret) {
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,
cur_offset, ins.objectid,
ins.offset, ins.offset,
Expand All @@ -5697,22 +5703,25 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
BUG_ON(ret);
btrfs_drop_extent_cache(inode, cur_offset,
cur_offset + ins.offset -1, 0);

num_bytes -= ins.offset;
cur_offset += ins.offset;
alloc_hint = ins.objectid + ins.offset;
btrfs_unreserve_metadata_space(root, 1);
}
out:
if (cur_offset > start) {

inode->i_ctime = CURRENT_TIME;
BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
cur_offset > i_size_read(inode))
btrfs_i_size_write(inode, cur_offset);
cur_offset > inode->i_size) {
i_size_write(inode, cur_offset);
btrfs_ordered_update_i_size(inode, cur_offset, NULL);
}

ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret);
}

btrfs_end_transaction(trans, root);
btrfs_unreserve_metadata_space(root, 3);
}
return ret;
}

Expand All @@ -5727,8 +5736,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
u64 locked_end;
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
struct extent_map *em;
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
int ret;

alloc_start = offset & ~mask;
Expand All @@ -5747,9 +5754,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
goto out;
}

root = BTRFS_I(inode)->root;

ret = btrfs_check_data_free_space(root, inode,
ret = btrfs_check_data_free_space(BTRFS_I(inode)->root, inode,
alloc_end - alloc_start);
if (ret)
goto out;
Expand All @@ -5758,12 +5763,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
while (1) {
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
* transaction
*/
Expand All @@ -5777,8 +5776,6 @@ static long btrfs_fallocate(struct inode *inode, int mode,
btrfs_put_ordered_extent(ordered);
unlock_extent(&BTRFS_I(inode)->io_tree,
alloc_start, locked_end, GFP_NOFS);
btrfs_end_transaction(trans, BTRFS_I(inode)->root);

/*
* we can't wait on the range with the transaction
* running or with the extent lock held
Expand All @@ -5799,9 +5796,12 @@ static long btrfs_fallocate(struct inode *inode, int mode,
BUG_ON(IS_ERR(em) || !em);
last_byte = min(extent_map_end(em), alloc_end);
last_byte = (last_byte + mask) & ~mask;
if (em->block_start == EXTENT_MAP_HOLE) {
ret = prealloc_file_range(trans, inode, cur_offset,
last_byte, alloc_hint, mode);
if (em->block_start == EXTENT_MAP_HOLE ||
(cur_offset >= inode->i_size &&
!test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
ret = prealloc_file_range(inode,
cur_offset, last_byte,
alloc_hint, mode);
if (ret < 0) {
free_extent_map(em);
break;
Expand All @@ -5820,9 +5820,8 @@ static long btrfs_fallocate(struct inode *inode, int mode,
unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
GFP_NOFS);

btrfs_end_transaction(trans, BTRFS_I(inode)->root);
out_free:
btrfs_free_reserved_data_space(root, inode, alloc_end - alloc_start);
btrfs_free_reserved_data_space(BTRFS_I(inode)->root, inode,
alloc_end - alloc_start);
out:
mutex_unlock(&inode->i_mutex);
return ret;
Expand Down

0 comments on commit 5a303d5

Please sign in to comment.