pNFS: Prevent the layout header refcount going to zero in pnfs_roc()
authorTrond Myklebust <trond.myklebust@primarydata.com>
Wed, 7 Mar 2018 19:49:06 +0000 (14:49 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 8 Mar 2018 17:56:31 +0000 (12:56 -0500)
Ensure that we hold a reference to the layout header when processing
the pNFS return-on-close so that the refcount value does not inadvertently
go to zero.

Reported-by: Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: stable@vger.kernel.org # v4.10+
Tested-by: Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
fs/nfs/pnfs.c

index c13e826614b5798ccd7628398944c140ae07f983..ee723aa153a3300633bc434f52156f3cf443f3dd 100644 (file)
@@ -292,8 +292,11 @@ pnfs_detach_layout_hdr(struct pnfs_layout_hdr *lo)
 void
 pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
 {
-       struct inode *inode = lo->plh_inode;
+       struct inode *inode;
 
+       if (!lo)
+               return;
+       inode = lo->plh_inode;
        pnfs_layoutreturn_before_put_layout_hdr(lo);
 
        if (refcount_dec_and_lock(&lo->plh_refcount, &inode->i_lock)) {
@@ -1241,10 +1244,12 @@ retry:
        spin_lock(&ino->i_lock);
        lo = nfsi->layout;
        if (!lo || !pnfs_layout_is_valid(lo) ||
-           test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags))
+           test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
+               lo = NULL;
                goto out_noroc;
+       }
+       pnfs_get_layout_hdr(lo);
        if (test_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags)) {
-               pnfs_get_layout_hdr(lo);
                spin_unlock(&ino->i_lock);
                wait_on_bit(&lo->plh_flags, NFS_LAYOUT_RETURN,
                                TASK_UNINTERRUPTIBLE);
@@ -1312,10 +1317,12 @@ out_noroc:
                struct pnfs_layoutdriver_type *ld = NFS_SERVER(ino)->pnfs_curr_ld;
                if (ld->prepare_layoutreturn)
                        ld->prepare_layoutreturn(args);
+               pnfs_put_layout_hdr(lo);
                return true;
        }
        if (layoutreturn)
                pnfs_send_layoutreturn(lo, &stateid, iomode, true);
+       pnfs_put_layout_hdr(lo);
        return false;
 }