aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2011-10-12 10:57:42 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-10-18 12:08:11 -0400
commit2da956523526e440ef4f4dd174e26f5ac06fe011 (patch)
tree718d8edd26d3eb59727dea97075ff59ba4d068fa /fs/nfs/write.c
parentb9dd3abbbc708da5e3c53424a5b2c66ab580f97e (diff)
nfs: don't try to migrate pages with active requests
nfs_find_and_lock_request will take a reference to the nfs_page and will then put it if the req is already locked. It's possible though that the reference will be the last one. That put then can kick off a whole series of reference puts: nfs_page nfs_open_context dentry inode If the inode ends up being deleted, then the VFS will call truncate_inode_pages. That function will try to take the page lock, but it was already locked when migrate_page was called. The code deadlocks. Fix this by simply refusing the migration request if PagePrivate is already set, indicating that the page is already associated with an active read or write request. We've had a customer test a backported version of this patch and the preliminary results seem good. Cc: stable@kernel.org Cc: Andrea Arcangeli <aarcange@redhat.com> Reported-by: Harshula Jayasuriya <harshula@redhat.com> Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c36
1 files changed, 11 insertions, 25 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 46aa4389ce13..72813ede029e 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1691,34 +1691,20 @@ out_error:
1691int nfs_migrate_page(struct address_space *mapping, struct page *newpage, 1691int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
1692 struct page *page) 1692 struct page *page)
1693{ 1693{
1694 struct nfs_page *req; 1694 /*
1695 int ret; 1695 * If PagePrivate is set, then the page is currently associated with
1696 * an in-progress read or write request. Don't try to migrate it.
1697 *
1698 * FIXME: we could do this in principle, but we'll need a way to ensure
1699 * that we can safely release the inode reference while holding
1700 * the page lock.
1701 */
1702 if (PagePrivate(page))
1703 return -EBUSY;
1696 1704
1697 nfs_fscache_release_page(page, GFP_KERNEL); 1705 nfs_fscache_release_page(page, GFP_KERNEL);
1698 1706
1699 req = nfs_find_and_lock_request(page, false); 1707 return migrate_page(mapping, newpage, page);
1700 ret = PTR_ERR(req);
1701 if (IS_ERR(req))
1702 goto out;
1703
1704 ret = migrate_page(mapping, newpage, page);
1705 if (!req)
1706 goto out;
1707 if (ret)
1708 goto out_unlock;
1709 page_cache_get(newpage);
1710 spin_lock(&mapping->host->i_lock);
1711 req->wb_page = newpage;
1712 SetPagePrivate(newpage);
1713 set_page_private(newpage, (unsigned long)req);
1714 ClearPagePrivate(page);
1715 set_page_private(page, 0);
1716 spin_unlock(&mapping->host->i_lock);
1717 page_cache_release(page);
1718out_unlock:
1719 nfs_clear_page_tag_locked(req);
1720out:
1721 return ret;
1722} 1708}
1723#endif 1709#endif
1724 1710