NFS: Ensure we always dereference the page head last
authorTrond Myklebust <trond.myklebust@primarydata.com>
Tue, 18 Jul 2017 23:31:10 +0000 (19:31 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Tue, 15 Aug 2017 15:54:46 +0000 (11:54 -0400)
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>
fs/nfs/pagelist.c

index de9066a92c0d27cb9e397233fb5b92116c8dc127..a6f2bbd709ba725ddacbd71acb7a0be051c2b059 100644 (file)
@@ -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);
 }
 
 /**