Commit 837bb1d7 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4.2: Fix writeback races in nfs4_copy_file_range

We need to ensure that any writes to the destination file are serialised
with the copy, meaning that the writeback has to occur under the inode lock.

Also relax the writeback requirement on the source, and rely on the
stateid checking to tell us if the source rebooted. Add the helper
nfs_filemap_write_and_wait_range() to call pnfs_sync_inode() as
is appropriate for pNFS servers that may need a layoutcommit.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 1e564d3d
...@@ -512,6 +512,9 @@ int nfs_key_timeout_notify(struct file *filp, struct inode *inode); ...@@ -512,6 +512,9 @@ int nfs_key_timeout_notify(struct file *filp, struct inode *inode);
bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx); bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx);
void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio); void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio);
int nfs_filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend);
#ifdef CONFIG_NFS_V4_1 #ifdef CONFIG_NFS_V4_1
static inline static inline
void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo) void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo)
......
...@@ -156,11 +156,20 @@ static ssize_t _nfs42_proc_copy(struct file *src, loff_t pos_src, ...@@ -156,11 +156,20 @@ static ssize_t _nfs42_proc_copy(struct file *src, loff_t pos_src,
if (status) if (status)
return status; return status;
status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
pos_src, pos_src + (loff_t)count - 1);
if (status)
return status;
status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
dst_lock, FMODE_WRITE); dst_lock, FMODE_WRITE);
if (status) if (status)
return status; return status;
status = nfs_sync_inode(dst_inode);
if (status)
return status;
status = nfs4_call_sync(server->client, server, &msg, status = nfs4_call_sync(server->client, server, &msg,
&args.seq_args, &res.seq_res, 0); &args.seq_args, &res.seq_res, 0);
if (status == -ENOTSUPP) if (status == -ENOTSUPP)
......
...@@ -133,21 +133,9 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -133,21 +133,9 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, struct file *file_out, loff_t pos_out,
size_t count, unsigned int flags) size_t count, unsigned int flags)
{ {
struct inode *in_inode = file_inode(file_in); if (file_inode(file_in) == file_inode(file_out))
struct inode *out_inode = file_inode(file_out);
int ret;
if (in_inode == out_inode)
return -EINVAL; return -EINVAL;
/* flush any pending writes */
ret = nfs_sync_inode(in_inode);
if (ret)
return ret;
ret = nfs_sync_inode(out_inode);
if (ret)
return ret;
return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count); return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
} }
......
...@@ -1912,6 +1912,24 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -1912,6 +1912,24 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
} }
EXPORT_SYMBOL_GPL(nfs_write_inode); EXPORT_SYMBOL_GPL(nfs_write_inode);
/*
* Wrapper for filemap_write_and_wait_range()
*
* Needed for pNFS in order to ensure data becomes visible to the
* client.
*/
int nfs_filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend)
{
int ret;
ret = filemap_write_and_wait_range(mapping, lstart, lend);
if (ret == 0)
ret = pnfs_sync_inode(mapping->host, true);
return ret;
}
EXPORT_SYMBOL_GPL(nfs_filemap_write_and_wait_range);
/* /*
* flush the inode to disk. * flush the inode to disk.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment