Commit b8d8e1fd authored by Goldwyn Rodrigues's avatar Goldwyn Rodrigues Committed by David Sterba
Browse files

btrfs: introduce btrfs_write_check()



btrfs_write_check() checks write parameters in one place before
beginning a write. This does away with inode_unlock() after every check.
In the later patches, it will help push inode_lock/unlock() in buffered
and direct write functions respectively.

generic_write_checks needs to be called before as it could truncate
iov_iter and its return used as count.

Signed-off-by: default avatarGoldwyn Rodrigues <rgoldwyn@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c86537a4
Loading
Loading
Loading
Loading
+77 −74
Original line number Diff line number Diff line
@@ -1564,6 +1564,79 @@ void btrfs_check_nocow_unlock(struct btrfs_inode *inode)
	btrfs_drew_write_unlock(&inode->root->snapshot_lock);
}

static void update_time_for_write(struct inode *inode)
{
	struct timespec64 now;

	if (IS_NOCMTIME(inode))
		return;

	now = current_time(inode);
	if (!timespec64_equal(&inode->i_mtime, &now))
		inode->i_mtime = now;

	if (!timespec64_equal(&inode->i_ctime, &now))
		inode->i_ctime = now;

	if (IS_I_VERSION(inode))
		inode_inc_iversion(inode);
}

static int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from,
			     size_t count)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
	loff_t pos = iocb->ki_pos;
	int ret;
	loff_t oldsize;
	loff_t start_pos;

	if (iocb->ki_flags & IOCB_NOWAIT) {
		size_t nocow_bytes = count;

		/* We will allocate space in case nodatacow is not set, so bail */
		if (check_nocow_nolock(BTRFS_I(inode), pos, &nocow_bytes) <= 0)
			return -EAGAIN;
		/*
		 * There are holes in the range or parts of the range that must
		 * be COWed (shared extents, RO block groups, etc), so just bail
		 * out.
		 */
		if (nocow_bytes < count)
			return -EAGAIN;
	}

	current->backing_dev_info = inode_to_bdi(inode);
	ret = file_remove_privs(file);
	if (ret)
		return ret;

	/*
	 * We reserve space for updating the inode when we reserve space for the
	 * extent we are going to write, so we will enospc out there.  We don't
	 * need to start yet another transaction to update the inode as we will
	 * update the inode when we finish writing whatever data we write.
	 */
	update_time_for_write(inode);

	start_pos = round_down(pos, fs_info->sectorsize);
	oldsize = i_size_read(inode);
	if (start_pos > oldsize) {
		/* Expand hole size to cover write data, preventing empty gap */
		loff_t end_pos = round_up(pos + count, fs_info->sectorsize);

		ret = btrfs_cont_expand(inode, oldsize, end_pos);
		if (ret) {
			current->backing_dev_info = NULL;
			return ret;
		}
	}

	return 0;
}

static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
					       struct iov_iter *i)
{
@@ -1873,24 +1946,6 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
	return written ? written : err;
}

static void update_time_for_write(struct inode *inode)
{
	struct timespec64 now;

	if (IS_NOCMTIME(inode))
		return;

	now = current_time(inode);
	if (!timespec64_equal(&inode->i_mtime, &now))
		inode->i_mtime = now;

	if (!timespec64_equal(&inode->i_ctime, &now))
		inode->i_ctime = now;

	if (IS_I_VERSION(inode))
		inode_inc_iversion(inode);
}

static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
				    struct iov_iter *from)
{
@@ -1898,14 +1953,9 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
	struct inode *inode = file_inode(file);
	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
	struct btrfs_root *root = BTRFS_I(inode)->root;
	u64 start_pos;
	u64 end_pos;
	ssize_t num_written = 0;
	const bool sync = iocb->ki_flags & IOCB_DSYNC;
	ssize_t err;
	loff_t pos;
	size_t count;
	loff_t oldsize;

	/*
	 * If the fs flips readonly due to some impossible error, although we
@@ -1932,57 +1982,10 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
		return err;
	}

	pos = iocb->ki_pos;
	count = iov_iter_count(from);
	if (iocb->ki_flags & IOCB_NOWAIT) {
		size_t nocow_bytes = count;

		/*
		 * We will allocate space in case nodatacow is not set,
		 * so bail
		 */
		if (check_nocow_nolock(BTRFS_I(inode), pos, &nocow_bytes)
		    <= 0) {
			inode_unlock(inode);
			return -EAGAIN;
		}
		/*
		 * There are holes in the range or parts of the range that must
		 * be COWed (shared extents, RO block groups, etc), so just bail
		 * out.
		 */
		if (nocow_bytes < count) {
			inode_unlock(inode);
			return -EAGAIN;
		}
	}

	current->backing_dev_info = inode_to_bdi(inode);
	err = file_remove_privs(file);
	if (err) {
		inode_unlock(inode);
		goto out;
	}

	/*
	 * We reserve space for updating the inode when we reserve space for the
	 * extent we are going to write, so we will enospc out there.  We don't
	 * need to start yet another transaction to update the inode as we will
	 * update the inode when we finish writing whatever data we write.
	 */
	update_time_for_write(inode);

	start_pos = round_down(pos, fs_info->sectorsize);
	oldsize = i_size_read(inode);
	if (start_pos > oldsize) {
		/* Expand hole size to cover write data, preventing empty gap */
		end_pos = round_up(pos + count,
				   fs_info->sectorsize);
		err = btrfs_cont_expand(inode, oldsize, end_pos);
		if (err) {
	err = btrfs_write_check(iocb, from, err);
	if (err < 0) {
		inode_unlock(inode);
			goto out;
		}
		return err;
	}

	if (sync)
@@ -2042,7 +2045,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,

	if (sync)
		atomic_dec(&BTRFS_I(inode)->sync_writers);
out:

	current->backing_dev_info = NULL;
	return num_written ? num_written : err;
}