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/nfs/nfs4proc.c | |
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/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 22 |
1 files changed, 13 insertions, 9 deletions
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: |