pNFS: Delay getting the layout header in CB_LAYOUTRECALL handlers
authorTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 31 Oct 2016 14:05:21 +0000 (10:05 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 1 Dec 2016 22:21:42 +0000 (17:21 -0500)
Instead of grabbing the layout, we want to get the inode so that we
can reduce races between layoutget and layoutrecall when the server
does not support call referring.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/callback_proc.c

index e9aa235e9d10197d8b138f1d0e657dd6449c578f..f073a6d2c6a51a4ec91cf38783519ca957bdece4 100644 (file)
@@ -110,20 +110,52 @@ out:
 #if defined(CONFIG_NFS_V4_1)
 
 /*
- * Lookup a layout by filehandle.
+ * Lookup a layout inode by stateid
  *
- * Note: gets a refcount on the layout hdr and on its respective inode.
- * Caller must put the layout hdr and the inode.
+ * Note: returns a refcount on the inode and superblock
+ */
+static struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_server *server;
+       struct inode *inode;
+       struct pnfs_layout_hdr *lo;
+
+restart:
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               list_for_each_entry(lo, &server->layouts, plh_layouts) {
+                       if (stateid != NULL &&
+                           !nfs4_stateid_match_other(stateid, &lo->plh_stateid))
+                               continue;
+                       inode = igrab(lo->plh_inode);
+                       if (!inode)
+                               continue;
+                       if (!nfs_sb_active(inode->i_sb)) {
+                               rcu_read_lock();
+                               spin_unlock(&clp->cl_lock);
+                               iput(inode);
+                               spin_lock(&clp->cl_lock);
+                               goto restart;
+                       }
+                       return inode;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Lookup a layout inode by filehandle.
+ *
+ * Note: returns a refcount on the inode and superblock
  *
- * TODO: keep track of all layouts (and delegations) in a hash table
- * hashed by filehandle.
  */
-static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
-               struct nfs_fh *fh)
+static struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp,
+               const struct nfs_fh *fh)
 {
        struct nfs_server *server;
        struct nfs_inode *nfsi;
-       struct inode *ino;
+       struct inode *inode;
        struct pnfs_layout_hdr *lo;
 
 restart:
@@ -134,37 +166,38 @@ restart:
                                continue;
                        if (nfsi->layout != lo)
                                continue;
-                       ino = igrab(lo->plh_inode);
-                       if (!ino)
-                               break;
-                       spin_lock(&ino->i_lock);
-                       /* Is this layout in the process of being freed? */
-                       if (nfsi->layout != lo) {
-                               spin_unlock(&ino->i_lock);
-                               iput(ino);
+                       inode = igrab(lo->plh_inode);
+                       if (!inode)
+                               continue;
+                       if (!nfs_sb_active(inode->i_sb)) {
+                               rcu_read_lock();
+                               spin_unlock(&clp->cl_lock);
+                               iput(inode);
+                               spin_lock(&clp->cl_lock);
                                goto restart;
                        }
-                       pnfs_get_layout_hdr(lo);
-                       spin_unlock(&ino->i_lock);
-                       return lo;
+                       return inode;
                }
        }
 
        return NULL;
 }
 
-static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
-               struct nfs_fh *fh)
+static struct inode *nfs_layout_find_inode(struct nfs_client *clp,
+               const struct nfs_fh *fh,
+               const nfs4_stateid *stateid)
 {
-       struct pnfs_layout_hdr *lo;
+       struct inode *inode;
 
        spin_lock(&clp->cl_lock);
        rcu_read_lock();
-       lo = get_layout_by_fh_locked(clp, fh);
+       inode = nfs_layout_find_inode_by_stateid(clp, stateid);
+       if (!inode)
+               inode = nfs_layout_find_inode_by_fh(clp, fh);
        rcu_read_unlock();
        spin_unlock(&clp->cl_lock);
 
-       return lo;
+       return inode;
 }
 
 /*
@@ -213,18 +246,20 @@ static u32 initiate_file_draining(struct nfs_client *clp,
        u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
        LIST_HEAD(free_me_list);
 
-       lo = get_layout_by_fh(clp, &args->cbl_fh);
-       if (!lo) {
-               trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
-                               &args->cbl_stateid, -rv);
+       ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid);
+       if (!ino)
                goto out;
-       }
 
-       ino = lo->plh_inode;
        pnfs_layoutcommit_inode(ino, false);
 
 
        spin_lock(&ino->i_lock);
+       lo = NFS_I(ino)->layout;
+       if (!lo) {
+               spin_unlock(&ino->i_lock);
+               goto out;
+       }
+       pnfs_get_layout_hdr(lo);
        rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
        if (rv != NFS_OK)
                goto unlock;
@@ -258,10 +293,10 @@ unlock:
        /* Free all lsegs that are attached to commit buckets */
        nfs_commit_inode(ino, 0);
        pnfs_put_layout_hdr(lo);
+out:
        trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
                        &args->cbl_stateid, -rv);
-       iput(ino);
-out:
+       nfs_iput_and_deactive(ino);
        return rv;
 }