diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-04-20 16:12:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-04-21 01:56:29 -0400 |
commit | 612c9384fd0486686699f7d49b774f0c7a79c511 (patch) | |
tree | 443192b69689488f0be8ca72f3e8f73db0a53988 | |
parent | 6d677e3504cd173b2ff7fc393ee4241b3c0f92a6 (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.c | 47 |
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 @@ | |||
38 | static struct nfs_page * nfs_update_request(struct nfs_open_context*, | 38 | static 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); |
41 | static void nfs_mark_request_dirty(struct nfs_page *req); | ||
42 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); | 41 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); |
43 | static const struct rpc_call_ops nfs_write_partial_ops; | 42 | static const struct rpc_call_ops nfs_write_partial_ops; |
44 | static const struct rpc_call_ops nfs_write_full_ops; | 43 | static const struct rpc_call_ops nfs_write_full_ops; |
@@ -255,7 +254,8 @@ static void nfs_end_page_writeback(struct page *page) | |||
255 | static int nfs_page_mark_flush(struct page *page) | 254 | static 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 | */ | ||
412 | static void | ||
413 | nfs_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 | |||
427 | static void | 421 | static void |
428 | nfs_redirty_request(struct nfs_page *req) | 422 | nfs_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) | |||
470 | static inline | 465 | static inline |
471 | int nfs_reschedule_unstable_write(struct nfs_page *req) | 466 | int 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)", |