Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+32 −33
Original line number Diff line number Diff line
@@ -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;
@@ -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,
@@ -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;
}

@@ -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;
@@ -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;
@@ -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
		 */
@@ -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
@@ -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;
@@ -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;