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: Goldwyn Rodrigues <rgoldwyn@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
c86537a42f
commit
b8d8e1fd57
151
fs/btrfs/file.c
151
fs/btrfs/file.c
|
@ -1564,6 +1564,79 @@ void btrfs_check_nocow_unlock(struct btrfs_inode *inode)
|
||||||
btrfs_drew_write_unlock(&inode->root->snapshot_lock);
|
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,
|
static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
|
||||||
struct iov_iter *i)
|
struct iov_iter *i)
|
||||||
{
|
{
|
||||||
|
@ -1873,24 +1946,6 @@ out:
|
||||||
return written ? written : err;
|
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,
|
static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
|
||||||
struct iov_iter *from)
|
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 inode *inode = file_inode(file);
|
||||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
u64 start_pos;
|
|
||||||
u64 end_pos;
|
|
||||||
ssize_t num_written = 0;
|
ssize_t num_written = 0;
|
||||||
const bool sync = iocb->ki_flags & IOCB_DSYNC;
|
const bool sync = iocb->ki_flags & IOCB_DSYNC;
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
loff_t pos;
|
|
||||||
size_t count;
|
|
||||||
loff_t oldsize;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the fs flips readonly due to some impossible error, although we
|
* 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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = iocb->ki_pos;
|
err = btrfs_write_check(iocb, from, err);
|
||||||
count = iov_iter_count(from);
|
if (err < 0) {
|
||||||
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);
|
inode_unlock(inode);
|
||||||
goto out;
|
return err;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
inode_unlock(inode);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
|
@ -2042,7 +2045,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
atomic_dec(&BTRFS_I(inode)->sync_writers);
|
atomic_dec(&BTRFS_I(inode)->sync_writers);
|
||||||
out:
|
|
||||||
current->backing_dev_info = NULL;
|
current->backing_dev_info = NULL;
|
||||||
return num_written ? num_written : err;
|
return num_written ? num_written : err;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue