aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/pagelist.c
diff options
context:
space:
mode:
authorWeston Andros Adamson <dros@primarydata.com>2014-08-14 17:39:32 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2014-08-22 18:04:44 -0400
commitbba5c1887a925a9945d22217d38d58d8b3ba1043 (patch)
tree9326421cb1b32538ae194287eb0614faf7f50089 /fs/nfs/pagelist.c
parent7c3af975257383ece54b83c0505d3e0656cb7daf (diff)
nfs: disallow duplicate pages in pgio page vectors
Adjacent requests that share the same page are allowed, but should only use one entry in the page vector. This avoids overruning the page vector - it is sized based on how many bytes there are, not by request count. This fixes issues that manifest as "Redzone overwritten" bugs (the vector overrun) and hangs waiting on page read / write, as it waits on the same page more than once. This also adds bounds checking to the page vector with a graceful failure (WARN_ON_ONCE and pgio error returned to application). Reported-by: Toralf Förster <toralf.foerster@gmx.de> Signed-off-by: Weston Andros Adamson <dros@primarydata.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs/pagelist.c')
-rw-r--r--fs/nfs/pagelist.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 4ec67f8d70aa..a1d1de7b97c5 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -724,10 +724,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
724 struct nfs_pgio_header *hdr) 724 struct nfs_pgio_header *hdr)
725{ 725{
726 struct nfs_page *req; 726 struct nfs_page *req;
727 struct page **pages; 727 struct page **pages,
728 *last_page;
728 struct list_head *head = &desc->pg_list; 729 struct list_head *head = &desc->pg_list;
729 struct nfs_commit_info cinfo; 730 struct nfs_commit_info cinfo;
730 unsigned int pagecount; 731 unsigned int pagecount, pageused;
731 732
732 pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count); 733 pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count);
733 if (!nfs_pgarray_set(&hdr->page_array, pagecount)) 734 if (!nfs_pgarray_set(&hdr->page_array, pagecount))
@@ -735,12 +736,23 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
735 736
736 nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); 737 nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
737 pages = hdr->page_array.pagevec; 738 pages = hdr->page_array.pagevec;
739 last_page = NULL;
740 pageused = 0;
738 while (!list_empty(head)) { 741 while (!list_empty(head)) {
739 req = nfs_list_entry(head->next); 742 req = nfs_list_entry(head->next);
740 nfs_list_remove_request(req); 743 nfs_list_remove_request(req);
741 nfs_list_add_request(req, &hdr->pages); 744 nfs_list_add_request(req, &hdr->pages);
742 *pages++ = req->wb_page; 745
746 if (WARN_ON_ONCE(pageused >= pagecount))
747 return nfs_pgio_error(desc, hdr);
748
749 if (!last_page || last_page != req->wb_page) {
750 *pages++ = last_page = req->wb_page;
751 pageused++;
752 }
743 } 753 }
754 if (WARN_ON_ONCE(pageused != pagecount))
755 return nfs_pgio_error(desc, hdr);
744 756
745 if ((desc->pg_ioflags & FLUSH_COND_STABLE) && 757 if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
746 (desc->pg_moreio || nfs_reqs_to_commit(&cinfo))) 758 (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))