summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2017-07-18 19:31:10 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2017-08-15 11:54:46 -0400
commit08fead2ae5a9953d47677416cc5f6bcae448480d (patch)
treeb54fbec7169469205db49b660b2c7ba0e2dad6be
parent1403390d8366c717139cab26b8e94d943915fa12 (diff)
downloadlinux-stable-08fead2ae5a9953d47677416cc5f6bcae448480d.tar.gz
linux-stable-08fead2ae5a9953d47677416cc5f6bcae448480d.tar.bz2
linux-stable-08fead2ae5a9953d47677416cc5f6bcae448480d.zip
NFS: Ensure we always dereference the page head last
This fixes a race with nfs_page_group_sync_on_bit() whereby the call to wake_up_bit() in nfs_page_group_unlock() could occur after the page header had been freed. Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r--fs/nfs/pagelist.c11
1 files changed, 6 insertions, 5 deletions
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index de9066a92c0d..a6f2bbd709ba 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -306,14 +306,11 @@ static void
nfs_page_group_destroy(struct kref *kref)
{
struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref);
+ struct nfs_page *head = req->wb_head;
struct nfs_page *tmp, *next;
- /* subrequests must release the ref on the head request */
- if (req->wb_head != req)
- nfs_release_request(req->wb_head);
-
if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN))
- return;
+ goto out;
tmp = req;
do {
@@ -324,6 +321,10 @@ nfs_page_group_destroy(struct kref *kref)
nfs_free_request(tmp);
tmp = next;
} while (tmp != req);
+out:
+ /* subrequests must release the ref on the head request */
+ if (head != req)
+ nfs_release_request(head);
}
/**