diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2017-07-18 19:31:10 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2017-08-15 11:54:46 -0400 |
commit | 08fead2ae5a9953d47677416cc5f6bcae448480d (patch) | |
tree | b54fbec7169469205db49b660b2c7ba0e2dad6be | |
parent | 1403390d8366c717139cab26b8e94d943915fa12 (diff) | |
download | linux-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.c | 11 |
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); } /** |