pNFS: Delay getting the layout header in CB_LAYOUTRECALL handlers

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>
This commit is contained in:
Trond Myklebust 2016-10-31 10:05:21 -04:00
parent 17822b207f
commit 7b410d9ce4

View file

@ -110,20 +110,52 @@ out:
#if defined(CONFIG_NFS_V4_1) #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. * Note: returns a refcount on the inode and superblock
* Caller must put the layout hdr and the inode.
*
* 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, static struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp,
struct nfs_fh *fh) 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
*
*/
static struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp,
const struct nfs_fh *fh)
{ {
struct nfs_server *server; struct nfs_server *server;
struct nfs_inode *nfsi; struct nfs_inode *nfsi;
struct inode *ino; struct inode *inode;
struct pnfs_layout_hdr *lo; struct pnfs_layout_hdr *lo;
restart: restart:
@ -134,37 +166,38 @@ restart:
continue; continue;
if (nfsi->layout != lo) if (nfsi->layout != lo)
continue; continue;
ino = igrab(lo->plh_inode); inode = igrab(lo->plh_inode);
if (!ino) if (!inode)
break; continue;
spin_lock(&ino->i_lock); if (!nfs_sb_active(inode->i_sb)) {
/* Is this layout in the process of being freed? */ rcu_read_lock();
if (nfsi->layout != lo) { spin_unlock(&clp->cl_lock);
spin_unlock(&ino->i_lock); iput(inode);
iput(ino); spin_lock(&clp->cl_lock);
goto restart; goto restart;
} }
pnfs_get_layout_hdr(lo); return inode;
spin_unlock(&ino->i_lock);
return lo;
} }
} }
return NULL; return NULL;
} }
static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp, static struct inode *nfs_layout_find_inode(struct nfs_client *clp,
struct nfs_fh *fh) const struct nfs_fh *fh,
const nfs4_stateid *stateid)
{ {
struct pnfs_layout_hdr *lo; struct inode *inode;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
rcu_read_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(); rcu_read_unlock();
spin_unlock(&clp->cl_lock); 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; u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
LIST_HEAD(free_me_list); LIST_HEAD(free_me_list);
lo = get_layout_by_fh(clp, &args->cbl_fh); ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid);
if (!lo) { if (!ino)
trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
&args->cbl_stateid, -rv);
goto out; goto out;
}
ino = lo->plh_inode;
pnfs_layoutcommit_inode(ino, false); pnfs_layoutcommit_inode(ino, false);
spin_lock(&ino->i_lock); 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); rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
if (rv != NFS_OK) if (rv != NFS_OK)
goto unlock; goto unlock;
@ -258,10 +293,10 @@ unlock:
/* Free all lsegs that are attached to commit buckets */ /* Free all lsegs that are attached to commit buckets */
nfs_commit_inode(ino, 0); nfs_commit_inode(ino, 0);
pnfs_put_layout_hdr(lo); pnfs_put_layout_hdr(lo);
out:
trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino, trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
&args->cbl_stateid, -rv); &args->cbl_stateid, -rv);
iput(ino); nfs_iput_and_deactive(ino);
out:
return rv; return rv;
} }