aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-07-25 14:09:54 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-10-09 17:15:23 -0400
commit7b159fc18d417980f57aef64cab3417ee6af70f8 (patch)
tree880f31179a9836ad9cd63b91dd6d77b61b01017c
parent34901f70d119d88126e7390351b8c780646628e1 (diff)
NFS: Fall back to synchronous writes when a background write errors...
This helps prevent huge queues of background writes from building up whenever the server runs out of disk or quota space, or if someone changes the file access modes behind our backs. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-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;