summaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorWeston Andros Adamson <dros@primarydata.com>2014-05-15 11:56:45 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2014-05-29 11:11:44 -0400
commit2bfc6e566daa8386c9cffef2f7de17fc330d3835 (patch)
treea615bb7091787ad574c5b31bcd6a30a5bfb8c2f9 /fs/nfs/write.c
parentab75e417192a486ffe63a314b6d2e7361f0e157f (diff)
nfs: add support for multiple nfs reqs per page
Add "page groups" - a circular list of nfs requests (struct nfs_page) that all reference the same page. This gives nfs read and write paths the ability to account for sub-page regions independently. This somewhat follows the design of struct buffer_head's sub-page accounting. Only "head" requests are ever added/removed from the inode list in the buffered write path. "head" and "sub" requests are treated the same through the read path and the rest of the write/commit path. Requests are given an extra reference across the life of the list. Page groups are never rejoined after being split. If the read/write request fails and the client falls back to another path (ie revert to MDS in PNFS case), the already split requests are pushed through the recoalescing code again, which may split them further and then coalesce them into properly sized requests on the wire. Fragmentation shouldn't be a problem with the current design, because we flush all requests in page group when a non-contiguous request is added, so the only time resplitting should occur is on a resend of a read or write. This patch lays the groundwork for sub-page splitting, but does not actually do any splitting. For now all page groups have one request as pg_test functions don't yet split pages. There are several related patches that are needed support multiple requests per page group. Signed-off-by: Weston Andros Adamson <dros@primarydata.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index e773df207c05..d0f30f12a8b3 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -367,6 +367,8 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
367{ 367{
368 struct nfs_inode *nfsi = NFS_I(inode); 368 struct nfs_inode *nfsi = NFS_I(inode);
369 369
370 WARN_ON_ONCE(req->wb_this_page != req);
371
370 /* Lock the request! */ 372 /* Lock the request! */
371 nfs_lock_request(req); 373 nfs_lock_request(req);
372 374
@@ -383,6 +385,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
383 set_page_private(req->wb_page, (unsigned long)req); 385 set_page_private(req->wb_page, (unsigned long)req);
384 } 386 }
385 nfsi->npages++; 387 nfsi->npages++;
388 set_bit(PG_INODE_REF, &req->wb_flags);
386 kref_get(&req->wb_kref); 389 kref_get(&req->wb_kref);
387 spin_unlock(&inode->i_lock); 390 spin_unlock(&inode->i_lock);
388} 391}
@@ -567,6 +570,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
567{ 570{
568 struct nfs_commit_info cinfo; 571 struct nfs_commit_info cinfo;
569 unsigned long bytes = 0; 572 unsigned long bytes = 0;
573 bool do_destroy;
570 574
571 if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) 575 if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
572 goto out; 576 goto out;
@@ -596,6 +600,7 @@ remove_req:
596next: 600next:
597 nfs_unlock_request(req); 601 nfs_unlock_request(req);
598 nfs_end_page_writeback(req->wb_page); 602 nfs_end_page_writeback(req->wb_page);
603 do_destroy = !test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags);
599 nfs_release_request(req); 604 nfs_release_request(req);
600 } 605 }
601out: 606out:
@@ -700,6 +705,10 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
700 if (req == NULL) 705 if (req == NULL)
701 goto out_unlock; 706 goto out_unlock;
702 707
708 /* should be handled by nfs_flush_incompatible */
709 WARN_ON_ONCE(req->wb_head != req);
710 WARN_ON_ONCE(req->wb_this_page != req);
711
703 rqend = req->wb_offset + req->wb_bytes; 712 rqend = req->wb_offset + req->wb_bytes;
704 /* 713 /*
705 * Tell the caller to flush out the request if 714 * Tell the caller to flush out the request if
@@ -761,7 +770,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
761 req = nfs_try_to_update_request(inode, page, offset, bytes); 770 req = nfs_try_to_update_request(inode, page, offset, bytes);
762 if (req != NULL) 771 if (req != NULL)
763 goto out; 772 goto out;
764 req = nfs_create_request(ctx, page, offset, bytes); 773 req = nfs_create_request(ctx, page, NULL, offset, bytes);
765 if (IS_ERR(req)) 774 if (IS_ERR(req))
766 goto out; 775 goto out;
767 nfs_inode_add_request(inode, req); 776 nfs_inode_add_request(inode, req);
@@ -805,6 +814,8 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
805 return 0; 814 return 0;
806 l_ctx = req->wb_lock_context; 815 l_ctx = req->wb_lock_context;
807 do_flush = req->wb_page != page || req->wb_context != ctx; 816 do_flush = req->wb_page != page || req->wb_context != ctx;
817 /* for now, flush if more than 1 request in page_group */
818 do_flush |= req->wb_this_page != req;
808 if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) { 819 if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) {
809 do_flush |= l_ctx->lockowner.l_owner != current->files 820 do_flush |= l_ctx->lockowner.l_owner != current->files
810 || l_ctx->lockowner.l_pid != current->tgid; 821 || l_ctx->lockowner.l_pid != current->tgid;