Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / fs / nfs / pnfs.c
index 9304984bde80af05eeea42c1b4637e8719fe17c6..4f802b02fbb9b0c6fc61912be044f6cef94acd32 100644 (file)
@@ -34,6 +34,7 @@
 #include "pnfs.h"
 #include "iostat.h"
 #include "nfs4trace.h"
+#include "delegation.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PNFS
 #define PNFS_LAYOUTGET_RETRY_TIMEOUT (120*HZ)
@@ -360,14 +361,9 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
        return true;
 }
 
-static void pnfs_layoutreturn_free_lseg(struct work_struct *work)
+static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg,
+               struct pnfs_layout_hdr *lo, struct inode *inode)
 {
-       struct pnfs_layout_segment *lseg;
-       struct pnfs_layout_hdr *lo;
-       struct inode *inode;
-
-       lseg = container_of(work, struct pnfs_layout_segment, pls_work);
-       WARN_ON(atomic_read(&lseg->pls_refcount));
        lo = lseg->pls_layout;
        inode = lo->plh_inode;
 
@@ -382,24 +378,12 @@ static void pnfs_layoutreturn_free_lseg(struct work_struct *work)
                lo->plh_block_lgets++;
                lo->plh_return_iomode = 0;
                spin_unlock(&inode->i_lock);
+               pnfs_get_layout_hdr(lo);
 
-               pnfs_send_layoutreturn(lo, stateid, iomode, true);
-               spin_lock(&inode->i_lock);
+               /* Send an async layoutreturn so we dont deadlock */
+               pnfs_send_layoutreturn(lo, stateid, iomode, false);
        } else
-               /* match pnfs_get_layout_hdr #2 in pnfs_put_lseg */
-               pnfs_put_layout_hdr(lo);
-       pnfs_layout_remove_lseg(lo, lseg);
-       spin_unlock(&inode->i_lock);
-       pnfs_free_lseg(lseg);
-       /* match pnfs_get_layout_hdr #1 in pnfs_put_lseg */
-       pnfs_put_layout_hdr(lo);
-}
-
-static void
-pnfs_layoutreturn_free_lseg_async(struct pnfs_layout_segment *lseg)
-{
-       INIT_WORK(&lseg->pls_work, pnfs_layoutreturn_free_lseg);
-       queue_work(nfsiod_workqueue, &lseg->pls_work);
+               spin_unlock(&inode->i_lock);
 }
 
 void
@@ -414,21 +398,23 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
        dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg,
                atomic_read(&lseg->pls_refcount),
                test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
+
+       /* Handle the case where refcount != 1 */
+       if (atomic_add_unless(&lseg->pls_refcount, -1, 1))
+               return;
+
        lo = lseg->pls_layout;
        inode = lo->plh_inode;
+       /* Do we need a layoutreturn? */
+       if (test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags))
+               pnfs_layoutreturn_before_put_lseg(lseg, lo, inode);
+
        if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) {
                pnfs_get_layout_hdr(lo);
-               if (pnfs_layout_need_return(lo, lseg)) {
-                       spin_unlock(&inode->i_lock);
-                       /* hdr reference dropped in nfs4_layoutreturn_release */
-                       pnfs_get_layout_hdr(lo);
-                       pnfs_layoutreturn_free_lseg_async(lseg);
-               } else {
-                       pnfs_layout_remove_lseg(lo, lseg);
-                       spin_unlock(&inode->i_lock);
-                       pnfs_free_lseg(lseg);
-                       pnfs_put_layout_hdr(lo);
-               }
+               pnfs_layout_remove_lseg(lo, lseg);
+               spin_unlock(&inode->i_lock);
+               pnfs_free_lseg(lseg);
+               pnfs_put_layout_hdr(lo);
        }
 }
 EXPORT_SYMBOL_GPL(pnfs_put_lseg);
@@ -947,7 +933,7 @@ pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, nfs4_stateid stateid,
        struct nfs4_layoutreturn *lrp;
        int status = 0;
 
-       lrp = kzalloc(sizeof(*lrp), GFP_KERNEL);
+       lrp = kzalloc(sizeof(*lrp), GFP_NOFS);
        if (unlikely(lrp == NULL)) {
                status = -ENOMEM;
                spin_lock(&ino->i_lock);
@@ -1066,6 +1052,9 @@ pnfs_commit_and_return_layout(struct inode *inode)
 
 bool pnfs_roc(struct inode *ino)
 {
+       struct nfs_inode *nfsi = NFS_I(ino);
+       struct nfs_open_context *ctx;
+       struct nfs4_state *state;
        struct pnfs_layout_hdr *lo;
        struct pnfs_layout_segment *lseg, *tmp;
        nfs4_stateid stateid;
@@ -1073,10 +1062,22 @@ bool pnfs_roc(struct inode *ino)
        bool found = false, layoutreturn = false;
 
        spin_lock(&ino->i_lock);
-       lo = NFS_I(ino)->layout;
+       lo = nfsi->layout;
        if (!lo || !test_and_clear_bit(NFS_LAYOUT_ROC, &lo->plh_flags) ||
            test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags))
-               goto out_nolayout;
+               goto out_noroc;
+
+       /* Don't return layout if we hold a delegation */
+       if (nfs4_check_delegation(ino, FMODE_READ))
+               goto out_noroc;
+
+       list_for_each_entry(ctx, &nfsi->open_files, list) {
+               state = ctx->state;
+               /* Don't return layout if there is open file state */
+               if (state != NULL && state->state != 0)
+                       goto out_noroc;
+       }
+
        pnfs_clear_retry_layoutget(lo);
        list_for_each_entry_safe(lseg, tmp, &lo->plh_segs, pls_list)
                if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
@@ -1084,14 +1085,14 @@ bool pnfs_roc(struct inode *ino)
                        found = true;
                }
        if (!found)
-               goto out_nolayout;
+               goto out_noroc;
        lo->plh_block_lgets++;
        pnfs_get_layout_hdr(lo); /* matched in pnfs_roc_release */
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&tmp_list);
        return true;
 
-out_nolayout:
+out_noroc:
        if (lo) {
                stateid = lo->plh_stateid;
                layoutreturn =