aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2008-01-24 18:14:34 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-01-30 02:06:12 -0500
commite6f810759505bc86c009854b82cc495ffd8eb020 (patch)
tree1590631fe3b222d49015dd53b421a5547e13e4dc /fs
parent99fadcd76465842c014c88b8c9c19b457e9debc0 (diff)
NFS: Add an asynchronous delegreturn operation for use in nfs_clear_inode
Otherwise, there is a potential deadlock if the last dput() from an NFSv4 close() or other asynchronous operation leads to nfs_clear_inode calling the synchronous delegreturn. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/delegation.c29
-rw-r--r--fs/nfs/delegation.h3
-rw-r--r--fs/nfs/dir.c1
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/nfs4proc.c22
5 files changed, 41 insertions, 16 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index b03dcd8403f1..2dead8d1dd55 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -174,11 +174,11 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
174 return status; 174 return status;
175} 175}
176 176
177static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation) 177static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
178{ 178{
179 int res = 0; 179 int res = 0;
180 180
181 res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); 181 res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
182 nfs_free_delegation(delegation); 182 nfs_free_delegation(delegation);
183 return res; 183 return res;
184} 184}
@@ -208,7 +208,7 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat
208 up_read(&clp->cl_sem); 208 up_read(&clp->cl_sem);
209 nfs_msync_inode(inode); 209 nfs_msync_inode(inode);
210 210
211 return nfs_do_return_delegation(inode, delegation); 211 return nfs_do_return_delegation(inode, delegation, 1);
212} 212}
213 213
214static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) 214static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
@@ -228,6 +228,27 @@ nomatch:
228 return NULL; 228 return NULL;
229} 229}
230 230
231/*
232 * This function returns the delegation without reclaiming opens
233 * or protecting against delegation reclaims.
234 * It is therefore really only safe to be called from
235 * nfs4_clear_inode()
236 */
237void nfs_inode_return_delegation_noreclaim(struct inode *inode)
238{
239 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
240 struct nfs_inode *nfsi = NFS_I(inode);
241 struct nfs_delegation *delegation;
242
243 if (rcu_dereference(nfsi->delegation) != NULL) {
244 spin_lock(&clp->cl_lock);
245 delegation = nfs_detach_delegation_locked(nfsi, NULL);
246 spin_unlock(&clp->cl_lock);
247 if (delegation != NULL)
248 nfs_do_return_delegation(inode, delegation, 0);
249 }
250}
251
231int nfs_inode_return_delegation(struct inode *inode) 252int nfs_inode_return_delegation(struct inode *inode)
232{ 253{
233 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 254 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
@@ -388,7 +409,7 @@ static int recall_thread(void *data)
388 nfs_msync_inode(inode); 409 nfs_msync_inode(inode);
389 410
390 if (delegation != NULL) 411 if (delegation != NULL)
391 nfs_do_return_delegation(inode, delegation); 412 nfs_do_return_delegation(inode, delegation, 1);
392 iput(inode); 413 iput(inode);
393 module_put_and_exit(0); 414 module_put_and_exit(0);
394} 415}
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 5874ce7fdbae..f1c5e2a5d88e 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -29,6 +29,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
29void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); 29void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
30int nfs_inode_return_delegation(struct inode *inode); 30int nfs_inode_return_delegation(struct inode *inode);
31int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); 31int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
32void nfs_inode_return_delegation_noreclaim(struct inode *inode);
32 33
33struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); 34struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
34void nfs_return_all_delegations(struct super_block *sb); 35void nfs_return_all_delegations(struct super_block *sb);
@@ -39,7 +40,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp);
39void nfs_delegation_reap_unclaimed(struct nfs_client *clp); 40void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
40 41
41/* NFSv4 delegation-related procedures */ 42/* NFSv4 delegation-related procedures */
42int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid); 43int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
43int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); 44int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid);
44int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); 45int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
45int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); 46int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index c578d942f000..5ca762de88bf 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -864,7 +864,6 @@ static int nfs_dentry_delete(struct dentry *dentry)
864 */ 864 */
865static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) 865static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
866{ 866{
867 nfs_inode_return_delegation(inode);
868 if (S_ISDIR(inode->i_mode)) 867 if (S_ISDIR(inode->i_mode))
869 /* drop any readdir cache as it could easily be old */ 868 /* drop any readdir cache as it could easily be old */
870 NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; 869 NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 5d381cfbfe7e..3f332e54e760 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1145,7 +1145,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
1145void nfs4_clear_inode(struct inode *inode) 1145void nfs4_clear_inode(struct inode *inode)
1146{ 1146{
1147 /* If we are holding a delegation, return it! */ 1147 /* If we are holding a delegation, return it! */
1148 nfs_inode_return_delegation(inode); 1148 nfs_inode_return_delegation_noreclaim(inode);
1149 /* First call standard NFS clear_inode() code */ 1149 /* First call standard NFS clear_inode() code */
1150 nfs_clear_inode(inode); 1150 nfs_clear_inode(inode);
1151} 1151}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 89efbcd6fd53..5c189bd57eb2 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2991,7 +2991,7 @@ static const struct rpc_call_ops nfs4_delegreturn_ops = {
2991 .rpc_release = nfs4_delegreturn_release, 2991 .rpc_release = nfs4_delegreturn_release,
2992}; 2992};
2993 2993
2994static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) 2994static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
2995{ 2995{
2996 struct nfs4_delegreturndata *data; 2996 struct nfs4_delegreturndata *data;
2997 struct nfs_server *server = NFS_SERVER(inode); 2997 struct nfs_server *server = NFS_SERVER(inode);
@@ -3006,7 +3006,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
3006 .callback_ops = &nfs4_delegreturn_ops, 3006 .callback_ops = &nfs4_delegreturn_ops,
3007 .flags = RPC_TASK_ASYNC, 3007 .flags = RPC_TASK_ASYNC,
3008 }; 3008 };
3009 int status; 3009 int status = 0;
3010 3010
3011 data = kmalloc(sizeof(*data), GFP_KERNEL); 3011 data = kmalloc(sizeof(*data), GFP_KERNEL);
3012 if (data == NULL) 3012 if (data == NULL)
@@ -3028,23 +3028,27 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
3028 task = rpc_run_task(&task_setup_data); 3028 task = rpc_run_task(&task_setup_data);
3029 if (IS_ERR(task)) 3029 if (IS_ERR(task))
3030 return PTR_ERR(task); 3030 return PTR_ERR(task);
3031 if (!issync)
3032 goto out;
3031 status = nfs4_wait_for_completion_rpc_task(task); 3033 status = nfs4_wait_for_completion_rpc_task(task);
3032 if (status == 0) { 3034 if (status != 0)
3033 status = data->rpc_status; 3035 goto out;
3034 if (status == 0) 3036 status = data->rpc_status;
3035 nfs_refresh_inode(inode, &data->fattr); 3037 if (status != 0)
3036 } 3038 goto out;
3039 nfs_refresh_inode(inode, &data->fattr);
3040out:
3037 rpc_put_task(task); 3041 rpc_put_task(task);
3038 return status; 3042 return status;
3039} 3043}
3040 3044
3041int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) 3045int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
3042{ 3046{
3043 struct nfs_server *server = NFS_SERVER(inode); 3047 struct nfs_server *server = NFS_SERVER(inode);
3044 struct nfs4_exception exception = { }; 3048 struct nfs4_exception exception = { };
3045 int err; 3049 int err;
3046 do { 3050 do {
3047 err = _nfs4_proc_delegreturn(inode, cred, stateid); 3051 err = _nfs4_proc_delegreturn(inode, cred, stateid, issync);
3048 switch (err) { 3052 switch (err) {
3049 case -NFS4ERR_STALE_STATEID: 3053 case -NFS4ERR_STALE_STATEID:
3050 case -NFS4ERR_EXPIRED: 3054 case -NFS4ERR_EXPIRED: