diff options
-rw-r--r-- | fs/nfs/file.c | 64 | ||||
-rw-r--r-- | fs/nfs/write.c | 13 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 3 |
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 | */ | ||
189 | static 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 | ||
392 | static 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 | |||
380 | static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, | 404 | static 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 | ||
113 | static 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 | |||
113 | static struct nfs_page *nfs_page_find_request_locked(struct page *page) | 120 | static 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; |