summaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2012-03-15 17:16:40 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-03-17 11:09:33 -0400
commit8dd3775889345850ecddd689b5c200cdd91bd8c9 (patch)
treeea697cfcac3f3a927e90d0048e9ed76b5a3ea8e5 /fs/nfs/write.c
parent95a13f7b33be87d85d8e6652126a3f4d64d164db (diff)
NFSv4.1: Clean ups and bugfixes for the pNFS read/writeback/commit code
Move more pnfs-isms out of the generic commit code. Bugfixes: - filelayout_scan_commit_lists doesn't need to get/put the lseg. In fact since it is run under the inode->i_lock, the lseg_put() can deadlock. - Ensure that we distinguish between what needs to be done for commit-to-data server and what needs to be done for commit-to-MDS using the new flag PG_COMMIT_TO_DS. Otherwise we may end up calling put_lseg() on a bucket for a struct nfs_page that got written through the MDS. - Fix a case where we were using list_del() on an nfs_page->wb_list instead of list_del_init(). - filelayout_initiate_commit needs to call filelayout_commit_release on error instead of the mds_ops->rpc_release(). Otherwise it won't clear the commit lock. Cleanups: - Let the files layout manage the commit lists for the pNFS case. Don't expose stuff like pnfs_choose_commit_list, and the fact that the commit buckets hold references to the layout segment in common code. - Cast out the put_lseg() calls for the struct nfs_read/write_data->lseg into the pNFS layer from whence they came. - Let the pNFS layer manage the NFS_INO_PNFS_COMMIT bit. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: Fred Isaman <iisaman@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c124
1 files changed, 77 insertions, 47 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index a630ad65d64c..0de19f413f92 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -100,7 +100,6 @@ void nfs_writedata_free(struct nfs_write_data *p)
100 100
101void nfs_writedata_release(struct nfs_write_data *wdata) 101void nfs_writedata_release(struct nfs_write_data *wdata)
102{ 102{
103 put_lseg(wdata->lseg);
104 put_nfs_open_context(wdata->args.context); 103 put_nfs_open_context(wdata->args.context);
105 nfs_writedata_free(wdata); 104 nfs_writedata_free(wdata);
106} 105}
@@ -393,8 +392,6 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
393 spin_unlock(&inode->i_lock); 392 spin_unlock(&inode->i_lock);
394} 393}
395 394
396static struct pnfs_layout_segment *nfs_clear_request_commit(struct nfs_page *req);
397
398/* 395/*
399 * Remove a write request from an inode 396 * Remove a write request from an inode
400 */ 397 */
@@ -402,18 +399,15 @@ static void nfs_inode_remove_request(struct nfs_page *req)
402{ 399{
403 struct inode *inode = req->wb_context->dentry->d_inode; 400 struct inode *inode = req->wb_context->dentry->d_inode;
404 struct nfs_inode *nfsi = NFS_I(inode); 401 struct nfs_inode *nfsi = NFS_I(inode);
405 struct pnfs_layout_segment *lseg;
406 402
407 BUG_ON (!NFS_WBACK_BUSY(req)); 403 BUG_ON (!NFS_WBACK_BUSY(req));
408 404
409 spin_lock(&inode->i_lock); 405 spin_lock(&inode->i_lock);
410 lseg = nfs_clear_request_commit(req);
411 set_page_private(req->wb_page, 0); 406 set_page_private(req->wb_page, 0);
412 ClearPagePrivate(req->wb_page); 407 ClearPagePrivate(req->wb_page);
413 clear_bit(PG_MAPPED, &req->wb_flags); 408 clear_bit(PG_MAPPED, &req->wb_flags);
414 nfsi->npages--; 409 nfsi->npages--;
415 spin_unlock(&inode->i_lock); 410 spin_unlock(&inode->i_lock);
416 put_lseg(lseg);
417 nfs_release_request(req); 411 nfs_release_request(req);
418} 412}
419 413
@@ -424,26 +418,69 @@ nfs_mark_request_dirty(struct nfs_page *req)
424} 418}
425 419
426#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 420#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
427/* 421/**
428 * Add a request to the inode's commit list. 422 * nfs_request_add_commit_list - add request to a commit list
423 * @req: pointer to a struct nfs_page
424 * @head: commit list head
425 *
426 * This sets the PG_CLEAN bit, updates the inode global count of
427 * number of outstanding requests requiring a commit as well as
428 * the MM page stats.
429 *
430 * The caller must _not_ hold the inode->i_lock, but must be
431 * holding the nfs_page lock.
429 */ 432 */
430static void 433void
431nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) 434nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head)
432{ 435{
433 struct inode *inode = req->wb_context->dentry->d_inode; 436 struct inode *inode = req->wb_context->dentry->d_inode;
434 struct nfs_inode *nfsi = NFS_I(inode);
435 struct list_head *clist;
436 437
437 clist = pnfs_choose_commit_list(req, lseg);
438 spin_lock(&inode->i_lock);
439 set_bit(PG_CLEAN, &(req)->wb_flags); 438 set_bit(PG_CLEAN, &(req)->wb_flags);
440 nfs_list_add_request(req, clist); 439 spin_lock(&inode->i_lock);
441 nfsi->ncommit++; 440 nfs_list_add_request(req, head);
441 NFS_I(inode)->ncommit++;
442 spin_unlock(&inode->i_lock); 442 spin_unlock(&inode->i_lock);
443 inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); 443 inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
444 inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); 444 inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE);
445 __mark_inode_dirty(inode, I_DIRTY_DATASYNC); 445 __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
446} 446}
447EXPORT_SYMBOL_GPL(nfs_request_add_commit_list);
448
449/**
450 * nfs_request_remove_commit_list - Remove request from a commit list
451 * @req: pointer to a nfs_page
452 *
453 * This clears the PG_CLEAN bit, and updates the inode global count of
454 * number of outstanding requests requiring a commit
455 * It does not update the MM page stats.
456 *
457 * The caller _must_ hold the inode->i_lock and the nfs_page lock.
458 */
459void
460nfs_request_remove_commit_list(struct nfs_page *req)
461{
462 struct inode *inode = req->wb_context->dentry->d_inode;
463
464 if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags))
465 return;
466 nfs_list_remove_request(req);
467 NFS_I(inode)->ncommit--;
468}
469EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);
470
471
472/*
473 * Add a request to the inode's commit list.
474 */
475static void
476nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
477{
478 struct inode *inode = req->wb_context->dentry->d_inode;
479
480 if (pnfs_mark_request_commit(req, lseg))
481 return;
482 nfs_request_add_commit_list(req, &NFS_I(inode)->commit_list);
483}
447 484
448static void 485static void
449nfs_clear_page_commit(struct page *page) 486nfs_clear_page_commit(struct page *page)
@@ -452,18 +489,19 @@ nfs_clear_page_commit(struct page *page)
452 dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); 489 dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
453} 490}
454 491
455static struct pnfs_layout_segment * 492static void
456nfs_clear_request_commit(struct nfs_page *req) 493nfs_clear_request_commit(struct nfs_page *req)
457{ 494{
458 struct pnfs_layout_segment *lseg = NULL; 495 if (test_bit(PG_CLEAN, &req->wb_flags)) {
496 struct inode *inode = req->wb_context->dentry->d_inode;
459 497
460 if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { 498 if (!pnfs_clear_request_commit(req)) {
499 spin_lock(&inode->i_lock);
500 nfs_request_remove_commit_list(req);
501 spin_unlock(&inode->i_lock);
502 }
461 nfs_clear_page_commit(req->wb_page); 503 nfs_clear_page_commit(req->wb_page);
462 lseg = pnfs_clear_request_commit(req);
463 NFS_I(req->wb_context->dentry->d_inode)->ncommit--;
464 list_del(&req->wb_list);
465 } 504 }
466 return lseg;
467} 505}
468 506
469static inline 507static inline
@@ -490,15 +528,14 @@ int nfs_reschedule_unstable_write(struct nfs_page *req,
490 return 0; 528 return 0;
491} 529}
492#else 530#else
493static inline void 531static void
494nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg) 532nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
495{ 533{
496} 534}
497 535
498static inline struct pnfs_layout_segment * 536static void
499nfs_clear_request_commit(struct nfs_page *req) 537nfs_clear_request_commit(struct nfs_page *req)
500{ 538{
501 return NULL;
502} 539}
503 540
504static inline 541static inline
@@ -523,25 +560,23 @@ nfs_need_commit(struct nfs_inode *nfsi)
523} 560}
524 561
525/* i_lock held by caller */ 562/* i_lock held by caller */
526int 563static int
527nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max) 564nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max)
528{ 565{
529 struct nfs_page *req, *tmp; 566 struct nfs_page *req, *tmp;
530 int ret = 0; 567 int ret = 0;
531 568
532 list_for_each_entry_safe(req, tmp, src, wb_list) { 569 list_for_each_entry_safe(req, tmp, src, wb_list) {
533 if (nfs_lock_request_dontget(req)) { 570 if (!nfs_lock_request(req))
534 kref_get(&req->wb_kref); 571 continue;
535 list_move_tail(&req->wb_list, dst); 572 nfs_request_remove_commit_list(req);
536 clear_bit(PG_CLEAN, &(req)->wb_flags); 573 nfs_list_add_request(req, dst);
537 ret++; 574 ret++;
538 if (ret == max) 575 if (ret == max)
539 break; 576 break;
540 }
541 } 577 }
542 return ret; 578 return ret;
543} 579}
544EXPORT_SYMBOL_GPL(nfs_scan_commit_list);
545 580
546/* 581/*
547 * nfs_scan_commit - Scan an inode for commit requests 582 * nfs_scan_commit - Scan an inode for commit requests
@@ -559,14 +594,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst)
559 594
560 spin_lock(&inode->i_lock); 595 spin_lock(&inode->i_lock);
561 if (nfsi->ncommit > 0) { 596 if (nfsi->ncommit > 0) {
597 const int max = INT_MAX;
562 int pnfs_ret; 598 int pnfs_ret;
563 599
564 ret = nfs_scan_commit_list(&nfsi->commit_list, dst, INT_MAX); 600 ret = nfs_scan_commit_list(&nfsi->commit_list, dst, max);
565 pnfs_ret = pnfs_scan_commit_lists(inode, INT_MAX - ret); 601 pnfs_ret = pnfs_scan_commit_lists(inode, max - ret);
566 if (pnfs_ret) { 602 ret += pnfs_ret;
567 ret += pnfs_ret;
568 set_bit(NFS_INO_PNFS_COMMIT, &nfsi->flags);
569 }
570 nfsi->ncommit -= ret; 603 nfsi->ncommit -= ret;
571 } 604 }
572 spin_unlock(&inode->i_lock); 605 spin_unlock(&inode->i_lock);
@@ -601,7 +634,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
601 unsigned int rqend; 634 unsigned int rqend;
602 unsigned int end; 635 unsigned int end;
603 int error; 636 int error;
604 struct pnfs_layout_segment *lseg = NULL;
605 637
606 if (!PagePrivate(page)) 638 if (!PagePrivate(page))
607 return NULL; 639 return NULL;
@@ -637,8 +669,6 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
637 spin_lock(&inode->i_lock); 669 spin_lock(&inode->i_lock);
638 } 670 }
639 671
640 lseg = nfs_clear_request_commit(req);
641
642 /* Okay, the request matches. Update the region */ 672 /* Okay, the request matches. Update the region */
643 if (offset < req->wb_offset) { 673 if (offset < req->wb_offset) {
644 req->wb_offset = offset; 674 req->wb_offset = offset;
@@ -650,7 +680,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
650 req->wb_bytes = rqend - req->wb_offset; 680 req->wb_bytes = rqend - req->wb_offset;
651out_unlock: 681out_unlock:
652 spin_unlock(&inode->i_lock); 682 spin_unlock(&inode->i_lock);
653 put_lseg(lseg); 683 nfs_clear_request_commit(req);
654 return req; 684 return req;
655out_flushme: 685out_flushme:
656 spin_unlock(&inode->i_lock); 686 spin_unlock(&inode->i_lock);
@@ -1337,7 +1367,6 @@ void nfs_commitdata_release(void *data)
1337{ 1367{
1338 struct nfs_write_data *wdata = data; 1368 struct nfs_write_data *wdata = data;
1339 1369
1340 put_lseg(wdata->lseg);
1341 put_nfs_open_context(wdata->args.context); 1370 put_nfs_open_context(wdata->args.context);
1342 nfs_commit_free(wdata); 1371 nfs_commit_free(wdata);
1343} 1372}
@@ -1647,6 +1676,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
1647 if (req == NULL) 1676 if (req == NULL)
1648 break; 1677 break;
1649 if (nfs_lock_request_dontget(req)) { 1678 if (nfs_lock_request_dontget(req)) {
1679 nfs_clear_request_commit(req);
1650 nfs_inode_remove_request(req); 1680 nfs_inode_remove_request(req);
1651 /* 1681 /*
1652 * In case nfs_inode_remove_request has marked the 1682 * In case nfs_inode_remove_request has marked the