Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+38 -36
+32 -33
fs/nfs/write.c
··· 242 242 return ret; 243 243 spin_lock(&inode->i_lock); 244 244 } 245 - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { 246 - /* This request is marked for commit */ 245 + if (test_bit(PG_CLEAN, &req->wb_flags)) { 247 246 spin_unlock(&inode->i_lock); 248 - nfs_clear_page_tag_locked(req); 249 - nfs_pageio_complete(pgio); 250 - return 0; 247 + BUG(); 251 248 } 252 249 if (nfs_set_page_writeback(page) != 0) { 253 250 spin_unlock(&inode->i_lock); ··· 388 391 __set_page_dirty_nobuffers(req->wb_page); 389 392 } 390 393 391 - /* 392 - * Check if a request is dirty 393 - */ 394 - static inline int 395 - nfs_dirty_request(struct nfs_page *req) 396 - { 397 - struct page *page = req->wb_page; 398 - 399 - if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags)) 400 - return 0; 401 - return !PageWriteback(page); 402 - } 403 - 404 394 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 405 395 /* 406 396 * Add a request to the inode's commit list. ··· 400 416 401 417 spin_lock(&inode->i_lock); 402 418 nfsi->ncommit++; 403 - set_bit(PG_NEED_COMMIT, &(req)->wb_flags); 419 + set_bit(PG_CLEAN, &(req)->wb_flags); 404 420 radix_tree_tag_set(&nfsi->nfs_page_tree, 405 421 req->wb_index, 406 422 NFS_PAGE_TAG_COMMIT); ··· 408 424 inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 409 425 inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); 410 426 __mark_inode_dirty(inode, I_DIRTY_DATASYNC); 427 + } 428 + 429 + static int 430 + nfs_clear_request_commit(struct nfs_page *req) 431 + { 432 + struct page *page = req->wb_page; 433 + 434 + if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { 435 + dec_zone_page_state(page, NR_UNSTABLE_NFS); 436 + dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); 437 + return 1; 438 + } 439 + return 0; 411 440 } 412 441 413 442 static inline ··· 432 435 static inline 433 436 int nfs_reschedule_unstable_write(struct nfs_page *req) 434 437 { 435 - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { 438 + if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { 436 439 nfs_mark_request_commit(req); 437 440 return 1; 438 441 } ··· 446 449 static inline void 447 450 nfs_mark_request_commit(struct nfs_page *req) 448 451 { 452 + } 453 + 454 + static inline int 455 + nfs_clear_request_commit(struct nfs_page *req) 456 + { 457 + return 0; 449 458 } 450 459 451 460 static inline ··· 511 508 512 509 while(!list_empty(head)) { 513 510 req = nfs_list_entry(head->next); 514 - dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 515 - dec_bdi_stat(req->wb_page->mapping->backing_dev_info, 516 - BDI_RECLAIMABLE); 517 511 nfs_list_remove_request(req); 518 - clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); 512 + nfs_clear_request_commit(req); 519 513 nfs_inode_remove_request(req); 520 514 nfs_unlock_request(req); 521 515 } ··· 584 584 * Note: nfs_flush_incompatible() will already 585 585 * have flushed out requests having wrong owners. 586 586 */ 587 - if (!nfs_dirty_request(req) 588 - || offset > rqend 587 + if (offset > rqend 589 588 || end < req->wb_offset) 590 589 goto out_flushme; 591 590 ··· 599 600 goto out_err; 600 601 spin_lock(&inode->i_lock); 601 602 } 603 + 604 + if (nfs_clear_request_commit(req)) 605 + radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree, 606 + req->wb_index, NFS_PAGE_TAG_COMMIT); 602 607 603 608 /* Okay, the request matches. Update the region */ 604 609 if (offset < req->wb_offset) { ··· 685 682 req = nfs_page_find_request(page); 686 683 if (req == NULL) 687 684 return 0; 688 - do_flush = req->wb_page != page || req->wb_context != ctx 689 - || !nfs_dirty_request(req); 685 + do_flush = req->wb_page != page || req->wb_context != ctx; 690 686 nfs_release_request(req); 691 687 if (!do_flush) 692 688 return 0; ··· 1290 1288 while (!list_empty(&data->pages)) { 1291 1289 req = nfs_list_entry(data->pages.next); 1292 1290 nfs_list_remove_request(req); 1293 - clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); 1294 - dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 1295 - dec_bdi_stat(req->wb_page->mapping->backing_dev_info, 1296 - BDI_RECLAIMABLE); 1291 + nfs_clear_request_commit(req); 1297 1292 1298 1293 dprintk("NFS: commit (%s/%lld %d@%lld)", 1299 1294 req->wb_context->path.dentry->d_inode->i_sb->s_id, ··· 1466 1467 req = nfs_page_find_request(page); 1467 1468 if (req == NULL) 1468 1469 goto out; 1469 - if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { 1470 + if (test_bit(PG_CLEAN, &req->wb_flags)) { 1470 1471 nfs_release_request(req); 1471 1472 break; 1472 1473 }
+6 -3
include/linux/nfs_page.h
··· 27 27 /* 28 28 * Valid flags for a dirty buffer 29 29 */ 30 - #define PG_BUSY 0 31 - #define PG_NEED_COMMIT 1 32 - #define PG_NEED_RESCHED 2 30 + enum { 31 + PG_BUSY = 0, 32 + PG_CLEAN, 33 + PG_NEED_COMMIT, 34 + PG_NEED_RESCHED, 35 + }; 33 36 34 37 struct nfs_inode; 35 38 struct nfs_page {