diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-01-24 18:14:34 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-01-30 02:06:12 -0500 |
commit | e6f810759505bc86c009854b82cc495ffd8eb020 (patch) | |
tree | 1590631fe3b222d49015dd53b421a5547e13e4dc /fs | |
parent | 99fadcd76465842c014c88b8c9c19b457e9debc0 (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.c | 29 | ||||
-rw-r--r-- | fs/nfs/delegation.h | 3 | ||||
-rw-r--r-- | fs/nfs/dir.c | 1 | ||||
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 22 |
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 | ||
177 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation) | 177 | static 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 | ||
214 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 214 | static 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 | */ | ||
237 | void 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 | |||
231 | int nfs_inode_return_delegation(struct inode *inode) | 252 | int 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 | |||
29 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); | 29 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); |
30 | int nfs_inode_return_delegation(struct inode *inode); | 30 | int nfs_inode_return_delegation(struct inode *inode); |
31 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); | 31 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); |
32 | void nfs_inode_return_delegation_noreclaim(struct inode *inode); | ||
32 | 33 | ||
33 | struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); | 34 | struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); |
34 | void nfs_return_all_delegations(struct super_block *sb); | 35 | void nfs_return_all_delegations(struct super_block *sb); |
@@ -39,7 +40,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp); | |||
39 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp); | 40 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp); |
40 | 41 | ||
41 | /* NFSv4 delegation-related procedures */ | 42 | /* NFSv4 delegation-related procedures */ |
42 | int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid); | 43 | int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync); |
43 | int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); | 44 | int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); |
44 | int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); | 45 | int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); |
45 | int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); | 46 | int 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 | */ |
865 | static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) | 865 | static 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) | |||
1145 | void nfs4_clear_inode(struct inode *inode) | 1145 | void 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 | ||
2994 | static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) | 2994 | static 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); | ||
3040 | out: | ||
3037 | rpc_put_task(task); | 3041 | rpc_put_task(task); |
3038 | return status; | 3042 | return status; |
3039 | } | 3043 | } |
3040 | 3044 | ||
3041 | int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) | 3045 | int 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: |