summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2016-06-25 18:12:03 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2016-07-05 19:11:07 -0400
commit837bb1d752d92ea4d870877ffbd6ec5cf76624b3 (patch)
treebc6efb2d8fff743b5dd56ed0848fa9f4f99c2e3c
parent1e564d3dbd684a105582471cb9ff2aada64a9052 (diff)
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: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r--fs/nfs/internal.h3
-rw-r--r--fs/nfs/nfs42proc.c9
-rw-r--r--fs/nfs/nfs4file.c14
-rw-r--r--fs/nfs/write.c18
4 files changed, 31 insertions, 13 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 01dccf18da0a..3b01c9146e15 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -512,6 +512,9 @@ int nfs_key_timeout_notify(struct file *filp, struct inode *inode);
512bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx); 512bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx);
513void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio); 513void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio);
514 514
515int nfs_filemap_write_and_wait_range(struct address_space *mapping,
516 loff_t lstart, loff_t lend);
517
515#ifdef CONFIG_NFS_V4_1 518#ifdef CONFIG_NFS_V4_1
516static inline 519static inline
517void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo) 520void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo)
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 0f9f536e647b..b7d457cea03f 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -156,11 +156,20 @@ static ssize_t _nfs42_proc_copy(struct file *src, loff_t pos_src,
156 if (status) 156 if (status)
157 return status; 157 return status;
158 158
159 status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
160 pos_src, pos_src + (loff_t)count - 1);
161 if (status)
162 return status;
163
159 status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, 164 status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
160 dst_lock, FMODE_WRITE); 165 dst_lock, FMODE_WRITE);
161 if (status) 166 if (status)
162 return status; 167 return status;
163 168
169 status = nfs_sync_inode(dst_inode);
170 if (status)
171 return status;
172
164 status = nfs4_call_sync(server->client, server, &msg, 173 status = nfs4_call_sync(server->client, server, &msg,
165 &args.seq_args, &res.seq_res, 0); 174 &args.seq_args, &res.seq_res, 0);
166 if (status == -ENOTSUPP) 175 if (status == -ENOTSUPP)
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 014b0e41ace5..7cdc0ab9e6f5 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -133,21 +133,9 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
133 struct file *file_out, loff_t pos_out, 133 struct file *file_out, loff_t pos_out,
134 size_t count, unsigned int flags) 134 size_t count, unsigned int flags)
135{ 135{
136 struct inode *in_inode = file_inode(file_in); 136 if (file_inode(file_in) == file_inode(file_out))
137 struct inode *out_inode = file_inode(file_out);
138 int ret;
139
140 if (in_inode == out_inode)
141 return -EINVAL; 137 return -EINVAL;
142 138
143 /* flush any pending writes */
144 ret = nfs_sync_inode(in_inode);
145 if (ret)
146 return ret;
147 ret = nfs_sync_inode(out_inode);
148 if (ret)
149 return ret;
150
151 return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count); 139 return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count);
152} 140}
153 141
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 3087fb6f1983..538a473b324b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1913,6 +1913,24 @@ out_mark_dirty:
1913EXPORT_SYMBOL_GPL(nfs_write_inode); 1913EXPORT_SYMBOL_GPL(nfs_write_inode);
1914 1914
1915/* 1915/*
1916 * Wrapper for filemap_write_and_wait_range()
1917 *
1918 * Needed for pNFS in order to ensure data becomes visible to the
1919 * client.
1920 */
1921int nfs_filemap_write_and_wait_range(struct address_space *mapping,
1922 loff_t lstart, loff_t lend)
1923{
1924 int ret;
1925
1926 ret = filemap_write_and_wait_range(mapping, lstart, lend);
1927 if (ret == 0)
1928 ret = pnfs_sync_inode(mapping->host, true);
1929 return ret;
1930}
1931EXPORT_SYMBOL_GPL(nfs_filemap_write_and_wait_range);
1932
1933/*
1916 * flush the inode to disk. 1934 * flush the inode to disk.
1917 */ 1935 */
1918int nfs_wb_all(struct inode *inode) 1936int nfs_wb_all(struct inode *inode)