aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-04-20 16:12:45 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-04-21 01:56:29 -0400
commit612c9384fd0486686699f7d49b774f0c7a79c511 (patch)
tree443192b69689488f0be8ca72f3e8f73db0a53988
parent6d677e3504cd173b2ff7fc393ee4241b3c0f92a6 (diff)
NFS: Fix the 'desynchronized value of nfs_i.ncommit' error
Redirtying a request that is already marked for commit will screw up the accounting for NR_UNSTABLE_NFS as well as nfs_i.ncommit. Ensure that all requests on the commit queue are labelled with the PG_NEED_COMMIT flag, and avoid moving them onto the dirty list inside nfs_page_mark_flush(). Also inline nfs_mark_request_dirty() into nfs_page_mark_flush() for atomicity reasons. Avoid dropping the spinlock until we're done marking the request in the radix tree and have added it to the ->dirty list. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/nfs/write.c47
1 files changed, 22 insertions, 25 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 8e9424651bc6..ce5b4a9f2d8b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -38,7 +38,6 @@
38static struct nfs_page * nfs_update_request(struct nfs_open_context*, 38static struct nfs_page * nfs_update_request(struct nfs_open_context*,
39 struct page *, 39 struct page *,
40 unsigned int, unsigned int); 40 unsigned int, unsigned int);
41static void nfs_mark_request_dirty(struct nfs_page *req);
42static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); 41static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how);
43static const struct rpc_call_ops nfs_write_partial_ops; 42static const struct rpc_call_ops nfs_write_partial_ops;
44static const struct rpc_call_ops nfs_write_full_ops; 43static const struct rpc_call_ops nfs_write_full_ops;
@@ -255,7 +254,8 @@ static void nfs_end_page_writeback(struct page *page)
255static int nfs_page_mark_flush(struct page *page) 254static int nfs_page_mark_flush(struct page *page)
256{ 255{
257 struct nfs_page *req; 256 struct nfs_page *req;
258 spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; 257 struct nfs_inode *nfsi = NFS_I(page->mapping->host);
258 spinlock_t *req_lock = &nfsi->req_lock;
259 int ret; 259 int ret;
260 260
261 spin_lock(req_lock); 261 spin_lock(req_lock);
@@ -279,11 +279,23 @@ static int nfs_page_mark_flush(struct page *page)
279 return ret; 279 return ret;
280 spin_lock(req_lock); 280 spin_lock(req_lock);
281 } 281 }
282 spin_unlock(req_lock); 282 if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
283 /* This request is marked for commit */
284 spin_unlock(req_lock);
285 nfs_unlock_request(req);
286 return 1;
287 }
283 if (nfs_set_page_writeback(page) == 0) { 288 if (nfs_set_page_writeback(page) == 0) {
284 nfs_list_remove_request(req); 289 nfs_list_remove_request(req);
285 nfs_mark_request_dirty(req); 290 /* add the request to the inode's dirty list. */
286 } 291 radix_tree_tag_set(&nfsi->nfs_page_tree,
292 req->wb_index, NFS_PAGE_TAG_DIRTY);
293 nfs_list_add_request(req, &nfsi->dirty);
294 nfsi->ndirty++;
295 spin_unlock(req_lock);
296 __mark_inode_dirty(page->mapping->host, I_DIRTY_PAGES);
297 } else
298 spin_unlock(req_lock);
287 ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); 299 ret = test_bit(PG_NEED_FLUSH, &req->wb_flags);
288 nfs_unlock_request(req); 300 nfs_unlock_request(req);
289 return ret; 301 return ret;
@@ -406,24 +418,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
406 nfs_release_request(req); 418 nfs_release_request(req);
407} 419}
408 420
409/*
410 * Add a request to the inode's dirty list.
411 */
412static void
413nfs_mark_request_dirty(struct nfs_page *req)
414{
415 struct inode *inode = req->wb_context->dentry->d_inode;
416 struct nfs_inode *nfsi = NFS_I(inode);
417
418 spin_lock(&nfsi->req_lock);
419 radix_tree_tag_set(&nfsi->nfs_page_tree,
420 req->wb_index, NFS_PAGE_TAG_DIRTY);
421 nfs_list_add_request(req, &nfsi->dirty);
422 nfsi->ndirty++;
423 spin_unlock(&nfsi->req_lock);
424 __mark_inode_dirty(inode, I_DIRTY_PAGES);
425}
426
427static void 421static void
428nfs_redirty_request(struct nfs_page *req) 422nfs_redirty_request(struct nfs_page *req)
429{ 423{
@@ -438,7 +432,7 @@ nfs_dirty_request(struct nfs_page *req)
438{ 432{
439 struct page *page = req->wb_page; 433 struct page *page = req->wb_page;
440 434
441 if (page == NULL) 435 if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags))
442 return 0; 436 return 0;
443 return !PageWriteback(req->wb_page); 437 return !PageWriteback(req->wb_page);
444} 438}
@@ -456,6 +450,7 @@ nfs_mark_request_commit(struct nfs_page *req)
456 spin_lock(&nfsi->req_lock); 450 spin_lock(&nfsi->req_lock);
457 nfs_list_add_request(req, &nfsi->commit); 451 nfs_list_add_request(req, &nfsi->commit);
458 nfsi->ncommit++; 452 nfsi->ncommit++;
453 set_bit(PG_NEED_COMMIT, &(req)->wb_flags);
459 spin_unlock(&nfsi->req_lock); 454 spin_unlock(&nfsi->req_lock);
460 inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 455 inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
461 __mark_inode_dirty(inode, I_DIRTY_DATASYNC); 456 __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
@@ -470,7 +465,7 @@ int nfs_write_need_commit(struct nfs_write_data *data)
470static inline 465static inline
471int nfs_reschedule_unstable_write(struct nfs_page *req) 466int nfs_reschedule_unstable_write(struct nfs_page *req)
472{ 467{
473 if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { 468 if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
474 nfs_mark_request_commit(req); 469 nfs_mark_request_commit(req);
475 return 1; 470 return 1;
476 } 471 }
@@ -557,6 +552,7 @@ static void nfs_cancel_commit_list(struct list_head *head)
557 req = nfs_list_entry(head->next); 552 req = nfs_list_entry(head->next);
558 dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 553 dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
559 nfs_list_remove_request(req); 554 nfs_list_remove_request(req);
555 clear_bit(PG_NEED_COMMIT, &(req)->wb_flags);
560 nfs_inode_remove_request(req); 556 nfs_inode_remove_request(req);
561 nfs_unlock_request(req); 557 nfs_unlock_request(req);
562 } 558 }
@@ -1295,6 +1291,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1295 while (!list_empty(&data->pages)) { 1291 while (!list_empty(&data->pages)) {
1296 req = nfs_list_entry(data->pages.next); 1292 req = nfs_list_entry(data->pages.next);
1297 nfs_list_remove_request(req); 1293 nfs_list_remove_request(req);
1294 clear_bit(PG_NEED_COMMIT, &(req)->wb_flags);
1298 dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 1295 dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
1299 1296
1300 dprintk("NFS: commit (%s/%Ld %d@%Ld)", 1297 dprintk("NFS: commit (%s/%Ld %d@%Ld)",