diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-06-13 13:25:22 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-07-09 12:09:24 -0400 |
commit | e468bae97d243fe0e1515abaa1f7d0edf1476ad0 (patch) | |
tree | 42bbbfe8d6dfc60bcaaaa1a7164ba75dc1060050 | |
parent | e7d39069e387a12d4c57f4067d9f48c1d29ea900 (diff) |
NFS: Allow redirtying of a completed unstable write.
Currently, if an unstable write completes, we cannot redirty the page in
order to reflect a new change in the page data until after we've sent a
COMMIT request.
This patch allows a page rewrite to proceed without the unnecessary COMMIT
step, putting it immediately back onto the dirty page list, undoing the
VM unstable write accounting, and removing the NFS_PAGE_TAG_COMMIT tag from
the NFS radix tree.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/write.c | 65 | ||||
-rw-r--r-- | include/linux/nfs_page.h | 9 |
2 files changed, 38 insertions, 36 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 04f51e52e184..feca8c648766 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -242,12 +242,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, | |||
242 | return ret; | 242 | return ret; |
243 | spin_lock(&inode->i_lock); | 243 | spin_lock(&inode->i_lock); |
244 | } | 244 | } |
245 | if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { | 245 | if (test_bit(PG_CLEAN, &req->wb_flags)) { |
246 | /* This request is marked for commit */ | ||
247 | spin_unlock(&inode->i_lock); | 246 | spin_unlock(&inode->i_lock); |
248 | nfs_clear_page_tag_locked(req); | 247 | BUG(); |
249 | nfs_pageio_complete(pgio); | ||
250 | return 0; | ||
251 | } | 248 | } |
252 | if (nfs_set_page_writeback(page) != 0) { | 249 | if (nfs_set_page_writeback(page) != 0) { |
253 | spin_unlock(&inode->i_lock); | 250 | spin_unlock(&inode->i_lock); |
@@ -391,19 +388,6 @@ nfs_mark_request_dirty(struct nfs_page *req) | |||
391 | __set_page_dirty_nobuffers(req->wb_page); | 388 | __set_page_dirty_nobuffers(req->wb_page); |
392 | } | 389 | } |
393 | 390 | ||
394 | /* | ||
395 | * Check if a request is dirty | ||
396 | */ | ||
397 | static inline int | ||
398 | nfs_dirty_request(struct nfs_page *req) | ||
399 | { | ||
400 | struct page *page = req->wb_page; | ||
401 | |||
402 | if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags)) | ||
403 | return 0; | ||
404 | return !PageWriteback(page); | ||
405 | } | ||
406 | |||
407 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 391 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
408 | /* | 392 | /* |
409 | * Add a request to the inode's commit list. | 393 | * Add a request to the inode's commit list. |
@@ -416,7 +400,7 @@ nfs_mark_request_commit(struct nfs_page *req) | |||
416 | 400 | ||
417 | spin_lock(&inode->i_lock); | 401 | spin_lock(&inode->i_lock); |
418 | nfsi->ncommit++; | 402 | nfsi->ncommit++; |
419 | set_bit(PG_NEED_COMMIT, &(req)->wb_flags); | 403 | set_bit(PG_CLEAN, &(req)->wb_flags); |
420 | radix_tree_tag_set(&nfsi->nfs_page_tree, | 404 | radix_tree_tag_set(&nfsi->nfs_page_tree, |
421 | req->wb_index, | 405 | req->wb_index, |
422 | NFS_PAGE_TAG_COMMIT); | 406 | NFS_PAGE_TAG_COMMIT); |
@@ -426,6 +410,19 @@ nfs_mark_request_commit(struct nfs_page *req) | |||
426 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); | 410 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); |
427 | } | 411 | } |
428 | 412 | ||
413 | static int | ||
414 | nfs_clear_request_commit(struct nfs_page *req) | ||
415 | { | ||
416 | struct page *page = req->wb_page; | ||
417 | |||
418 | if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { | ||
419 | dec_zone_page_state(page, NR_UNSTABLE_NFS); | ||
420 | dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); | ||
421 | return 1; | ||
422 | } | ||
423 | return 0; | ||
424 | } | ||
425 | |||
429 | static inline | 426 | static inline |
430 | int nfs_write_need_commit(struct nfs_write_data *data) | 427 | int nfs_write_need_commit(struct nfs_write_data *data) |
431 | { | 428 | { |
@@ -435,7 +432,7 @@ int nfs_write_need_commit(struct nfs_write_data *data) | |||
435 | static inline | 432 | static inline |
436 | int nfs_reschedule_unstable_write(struct nfs_page *req) | 433 | int nfs_reschedule_unstable_write(struct nfs_page *req) |
437 | { | 434 | { |
438 | if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { | 435 | if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { |
439 | nfs_mark_request_commit(req); | 436 | nfs_mark_request_commit(req); |
440 | return 1; | 437 | return 1; |
441 | } | 438 | } |
@@ -451,6 +448,12 @@ nfs_mark_request_commit(struct nfs_page *req) | |||
451 | { | 448 | { |
452 | } | 449 | } |
453 | 450 | ||
451 | static inline int | ||
452 | nfs_clear_request_commit(struct nfs_page *req) | ||
453 | { | ||
454 | return 0; | ||
455 | } | ||
456 | |||
454 | static inline | 457 | static inline |
455 | int nfs_write_need_commit(struct nfs_write_data *data) | 458 | int nfs_write_need_commit(struct nfs_write_data *data) |
456 | { | 459 | { |
@@ -508,11 +511,8 @@ static void nfs_cancel_commit_list(struct list_head *head) | |||
508 | 511 | ||
509 | while(!list_empty(head)) { | 512 | while(!list_empty(head)) { |
510 | req = nfs_list_entry(head->next); | 513 | req = nfs_list_entry(head->next); |
511 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | ||
512 | dec_bdi_stat(req->wb_page->mapping->backing_dev_info, | ||
513 | BDI_RECLAIMABLE); | ||
514 | nfs_list_remove_request(req); | 514 | nfs_list_remove_request(req); |
515 | clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); | 515 | nfs_clear_request_commit(req); |
516 | nfs_inode_remove_request(req); | 516 | nfs_inode_remove_request(req); |
517 | nfs_unlock_request(req); | 517 | nfs_unlock_request(req); |
518 | } | 518 | } |
@@ -584,8 +584,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, | |||
584 | * Note: nfs_flush_incompatible() will already | 584 | * Note: nfs_flush_incompatible() will already |
585 | * have flushed out requests having wrong owners. | 585 | * have flushed out requests having wrong owners. |
586 | */ | 586 | */ |
587 | if (!nfs_dirty_request(req) | 587 | if (offset > rqend |
588 | || offset > rqend | ||
589 | || end < req->wb_offset) | 588 | || end < req->wb_offset) |
590 | goto out_flushme; | 589 | goto out_flushme; |
591 | 590 | ||
@@ -601,6 +600,10 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode, | |||
601 | spin_lock(&inode->i_lock); | 600 | spin_lock(&inode->i_lock); |
602 | } | 601 | } |
603 | 602 | ||
603 | if (nfs_clear_request_commit(req)) | ||
604 | radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree, | ||
605 | req->wb_index, NFS_PAGE_TAG_COMMIT); | ||
606 | |||
604 | /* Okay, the request matches. Update the region */ | 607 | /* Okay, the request matches. Update the region */ |
605 | if (offset < req->wb_offset) { | 608 | if (offset < req->wb_offset) { |
606 | req->wb_offset = offset; | 609 | req->wb_offset = offset; |
@@ -682,8 +685,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) | |||
682 | req = nfs_page_find_request(page); | 685 | req = nfs_page_find_request(page); |
683 | if (req == NULL) | 686 | if (req == NULL) |
684 | return 0; | 687 | return 0; |
685 | do_flush = req->wb_page != page || req->wb_context != ctx | 688 | do_flush = req->wb_page != page || req->wb_context != ctx; |
686 | || !nfs_dirty_request(req); | ||
687 | nfs_release_request(req); | 689 | nfs_release_request(req); |
688 | if (!do_flush) | 690 | if (!do_flush) |
689 | return 0; | 691 | return 0; |
@@ -1288,10 +1290,7 @@ static void nfs_commit_release(void *calldata) | |||
1288 | while (!list_empty(&data->pages)) { | 1290 | while (!list_empty(&data->pages)) { |
1289 | req = nfs_list_entry(data->pages.next); | 1291 | req = nfs_list_entry(data->pages.next); |
1290 | nfs_list_remove_request(req); | 1292 | nfs_list_remove_request(req); |
1291 | clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); | 1293 | nfs_clear_request_commit(req); |
1292 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | ||
1293 | dec_bdi_stat(req->wb_page->mapping->backing_dev_info, | ||
1294 | BDI_RECLAIMABLE); | ||
1295 | 1294 | ||
1296 | dprintk("NFS: commit (%s/%lld %d@%lld)", | 1295 | dprintk("NFS: commit (%s/%lld %d@%lld)", |
1297 | req->wb_context->path.dentry->d_inode->i_sb->s_id, | 1296 | req->wb_context->path.dentry->d_inode->i_sb->s_id, |
@@ -1467,7 +1466,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) | |||
1467 | req = nfs_page_find_request(page); | 1466 | req = nfs_page_find_request(page); |
1468 | if (req == NULL) | 1467 | if (req == NULL) |
1469 | goto out; | 1468 | goto out; |
1470 | if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { | 1469 | if (test_bit(PG_CLEAN, &req->wb_flags)) { |
1471 | nfs_release_request(req); | 1470 | nfs_release_request(req); |
1472 | break; | 1471 | break; |
1473 | } | 1472 | } |
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index a1676e19e491..3c60685d972b 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h | |||
@@ -27,9 +27,12 @@ | |||
27 | /* | 27 | /* |
28 | * Valid flags for a dirty buffer | 28 | * Valid flags for a dirty buffer |
29 | */ | 29 | */ |
30 | #define PG_BUSY 0 | 30 | enum { |
31 | #define PG_NEED_COMMIT 1 | 31 | PG_BUSY = 0, |
32 | #define PG_NEED_RESCHED 2 | 32 | PG_CLEAN, |
33 | PG_NEED_COMMIT, | ||
34 | PG_NEED_RESCHED, | ||
35 | }; | ||
33 | 36 | ||
34 | struct nfs_inode; | 37 | struct nfs_inode; |
35 | struct nfs_page { | 38 | struct nfs_page { |