aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback_proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/callback_proc.c')
-rw-r--r--fs/nfs/callback_proc.c99
1 files changed, 67 insertions, 32 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index e9aa235e9d10..f073a6d2c6a5 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -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;
}