aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/file.c64
-rw-r--r--fs/nfs/write.c13
-rw-r--r--include/linux/nfs_fs.h3
3 files changed, 57 insertions, 23 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 07f43791f696..5595b32c0915 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -177,6 +177,31 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
177} 177}
178 178
179/* 179/*
180 * Helper for nfs_file_flush() and nfs_fsync()
181 *
182 * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to
183 * disk, but it retrieves and clears ctx->error after synching, despite
184 * the two being set at the same time in nfs_context_set_write_error().
185 * This is because the former is used to notify the _next_ call to
186 * nfs_file_write() that a write error occured, and hence cause it to
187 * fall back to doing a synchronous write.
188 */
189static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode)
190{
191 int have_error, status;
192 int ret = 0;
193
194 have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
195 status = nfs_wb_all(inode);
196 have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
197 if (have_error)
198 ret = xchg(&ctx->error, 0);
199 if (!ret)
200 ret = status;
201 return ret;
202}
203
204/*
180 * Flush all dirty pages, and check for write errors. 205 * Flush all dirty pages, and check for write errors.
181 * 206 *
182 */ 207 */
@@ -192,16 +217,11 @@ nfs_file_flush(struct file *file, fl_owner_t id)
192 if ((file->f_mode & FMODE_WRITE) == 0) 217 if ((file->f_mode & FMODE_WRITE) == 0)
193 return 0; 218 return 0;
194 nfs_inc_stats(inode, NFSIOS_VFSFLUSH); 219 nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
195 lock_kernel(); 220
196 /* Ensure that data+attribute caches are up to date after close() */ 221 /* Ensure that data+attribute caches are up to date after close() */
197 status = nfs_wb_all(inode); 222 status = nfs_do_fsync(ctx, inode);
198 if (!status) { 223 if (!status)
199 status = ctx->error; 224 nfs_revalidate_inode(NFS_SERVER(inode), inode);
200 ctx->error = 0;
201 if (!status)
202 nfs_revalidate_inode(NFS_SERVER(inode), inode);
203 }
204 unlock_kernel();
205 return status; 225 return status;
206} 226}
207 227
@@ -278,19 +298,11 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
278{ 298{
279 struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; 299 struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
280 struct inode *inode = dentry->d_inode; 300 struct inode *inode = dentry->d_inode;
281 int status;
282 301
283 dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); 302 dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
284 303
285 nfs_inc_stats(inode, NFSIOS_VFSFSYNC); 304 nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
286 lock_kernel(); 305 return nfs_do_fsync(ctx, inode);
287 status = nfs_wb_all(inode);
288 if (!status) {
289 status = ctx->error;
290 ctx->error = 0;
291 }
292 unlock_kernel();
293 return status;
294} 306}
295 307
296/* 308/*
@@ -377,6 +389,18 @@ static struct vm_operations_struct nfs_file_vm_ops = {
377 .page_mkwrite = nfs_vm_page_mkwrite, 389 .page_mkwrite = nfs_vm_page_mkwrite,
378}; 390};
379 391
392static int nfs_need_sync_write(struct file *filp, struct inode *inode)
393{
394 struct nfs_open_context *ctx;
395
396 if (IS_SYNC(inode) || (filp->f_flags & O_SYNC))
397 return 1;
398 ctx = filp->private_data;
399 if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags))
400 return 1;
401 return 0;
402}
403
380static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, 404static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
381 unsigned long nr_segs, loff_t pos) 405 unsigned long nr_segs, loff_t pos)
382{ 406{
@@ -413,8 +437,8 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
413 nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count); 437 nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
414 result = generic_file_aio_write(iocb, iov, nr_segs, pos); 438 result = generic_file_aio_write(iocb, iov, nr_segs, pos);
415 /* Return error values for O_SYNC and IS_SYNC() */ 439 /* Return error values for O_SYNC and IS_SYNC() */
416 if (result >= 0 && (IS_SYNC(inode) || (iocb->ki_filp->f_flags & O_SYNC))) { 440 if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) {
417 int err = nfs_fsync(iocb->ki_filp, dentry, 1); 441 int err = nfs_do_fsync(iocb->ki_filp->private_data, inode);
418 if (err < 0) 442 if (err < 0)
419 result = err; 443 result = err;
420 } 444 }
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index b3c5f5db73a4..fb396ea5accf 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -110,6 +110,13 @@ void nfs_writedata_release(void *wdata)
110 nfs_writedata_free(wdata); 110 nfs_writedata_free(wdata);
111} 111}
112 112
113static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
114{
115 ctx->error = error;
116 smp_wmb();
117 set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
118}
119
113static struct nfs_page *nfs_page_find_request_locked(struct page *page) 120static struct nfs_page *nfs_page_find_request_locked(struct page *page)
114{ 121{
115 struct nfs_page *req = NULL; 122 struct nfs_page *req = NULL;
@@ -945,7 +952,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
945 952
946 if (task->tk_status < 0) { 953 if (task->tk_status < 0) {
947 nfs_set_pageerror(page); 954 nfs_set_pageerror(page);
948 req->wb_context->error = task->tk_status; 955 nfs_context_set_write_error(req->wb_context, task->tk_status);
949 dprintk(", error = %d\n", task->tk_status); 956 dprintk(", error = %d\n", task->tk_status);
950 goto out; 957 goto out;
951 } 958 }
@@ -1008,7 +1015,7 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
1008 1015
1009 if (task->tk_status < 0) { 1016 if (task->tk_status < 0) {
1010 nfs_set_pageerror(page); 1017 nfs_set_pageerror(page);
1011 req->wb_context->error = task->tk_status; 1018 nfs_context_set_write_error(req->wb_context, task->tk_status);
1012 dprintk(", error = %d\n", task->tk_status); 1019 dprintk(", error = %d\n", task->tk_status);
1013 goto remove_request; 1020 goto remove_request;
1014 } 1021 }
@@ -1222,7 +1229,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1222 req->wb_bytes, 1229 req->wb_bytes,
1223 (long long)req_offset(req)); 1230 (long long)req_offset(req));
1224 if (task->tk_status < 0) { 1231 if (task->tk_status < 0) {
1225 req->wb_context->error = task->tk_status; 1232 nfs_context_set_write_error(req->wb_context, task->tk_status);
1226 nfs_inode_remove_request(req); 1233 nfs_inode_remove_request(req);
1227 dprintk(", error = %d\n", task->tk_status); 1234 dprintk(", error = %d\n", task->tk_status);
1228 goto next; 1235 goto next;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 15eec27cb60b..1d1343fd2ddd 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -77,6 +77,9 @@ struct nfs_open_context {
77 struct nfs4_state *state; 77 struct nfs4_state *state;
78 fl_owner_t lockowner; 78 fl_owner_t lockowner;
79 int mode; 79 int mode;
80
81 unsigned long flags;
82#define NFS_CONTEXT_ERROR_WRITE (0)
80 int error; 83 int error;
81 84
82 struct list_head list; 85 struct list_head list;