diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-02-19 20:04:20 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-02-26 00:40:33 -0500 |
commit | 383ba71938519959be8e0b598ec658f0c211ff45 (patch) | |
tree | 01eb0155676fe69d40f01dc137ea3be952d88997 | |
parent | 4b5621f6b127bce9218998c187bd25bf7f9fc371 (diff) |
NFS: Fix a deadlock with lazy umount
We can't allow rpc callback functions like task->tk_ops->rpc_call_prepare()
and task->tk_ops->rpc_call_done() to call mntput() in any way, since
that will cause a deadlock when the call to rpc_shutdown_client() attempts
to wait on 'task' to complete.
We can avoid the above deadlock by moving calls to mntput to
task->tk_ops->rpc_release() callback, since at that time the task will be
marked as completed, and so rpc_shutdown_client won't attempt to wait on
it.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/direct.c | 5 | ||||
-rw-r--r-- | fs/nfs/inode.c | 6 | ||||
-rw-r--r-- | fs/nfs/read.c | 7 | ||||
-rw-r--r-- | fs/nfs/write.c | 13 |
4 files changed, 23 insertions, 8 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 16844f98f50e..e0170407a885 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -323,7 +323,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, | |||
323 | data->inode = inode; | 323 | data->inode = inode; |
324 | data->cred = msg.rpc_cred; | 324 | data->cred = msg.rpc_cred; |
325 | data->args.fh = NFS_FH(inode); | 325 | data->args.fh = NFS_FH(inode); |
326 | data->args.context = ctx; | 326 | data->args.context = get_nfs_open_context(ctx); |
327 | data->args.offset = pos; | 327 | data->args.offset = pos; |
328 | data->args.pgbase = pgbase; | 328 | data->args.pgbase = pgbase; |
329 | data->args.pages = data->pagevec; | 329 | data->args.pages = data->pagevec; |
@@ -546,6 +546,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) | |||
546 | data->args.fh = NFS_FH(data->inode); | 546 | data->args.fh = NFS_FH(data->inode); |
547 | data->args.offset = 0; | 547 | data->args.offset = 0; |
548 | data->args.count = 0; | 548 | data->args.count = 0; |
549 | data->args.context = get_nfs_open_context(dreq->ctx); | ||
549 | data->res.count = 0; | 550 | data->res.count = 0; |
550 | data->res.fattr = &data->fattr; | 551 | data->res.fattr = &data->fattr; |
551 | data->res.verf = &data->verf; | 552 | data->res.verf = &data->verf; |
@@ -728,7 +729,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, | |||
728 | data->inode = inode; | 729 | data->inode = inode; |
729 | data->cred = msg.rpc_cred; | 730 | data->cred = msg.rpc_cred; |
730 | data->args.fh = NFS_FH(inode); | 731 | data->args.fh = NFS_FH(inode); |
731 | data->args.context = ctx; | 732 | data->args.context = get_nfs_open_context(ctx); |
732 | data->args.offset = pos; | 733 | data->args.offset = pos; |
733 | data->args.pgbase = pgbase; | 734 | data->args.pgbase = pgbase; |
734 | data->args.pages = data->pagevec; | 735 | data->args.pages = data->pagevec; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 966a8850aa30..a499fb58d858 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -521,8 +521,12 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) | |||
521 | 521 | ||
522 | static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait) | 522 | static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait) |
523 | { | 523 | { |
524 | struct inode *inode = ctx->path.dentry->d_inode; | 524 | struct inode *inode; |
525 | 525 | ||
526 | if (ctx == NULL) | ||
527 | return; | ||
528 | |||
529 | inode = ctx->path.dentry->d_inode; | ||
526 | if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock)) | 530 | if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock)) |
527 | return; | 531 | return; |
528 | list_del(&ctx->list); | 532 | list_del(&ctx->list); |
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 3d7d9631e125..fab0d3720a03 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -73,7 +73,10 @@ static void nfs_readdata_free(struct nfs_read_data *rdata) | |||
73 | 73 | ||
74 | void nfs_readdata_release(void *data) | 74 | void nfs_readdata_release(void *data) |
75 | { | 75 | { |
76 | nfs_readdata_free(data); | 76 | struct nfs_read_data *rdata = data; |
77 | |||
78 | put_nfs_open_context(rdata->args.context); | ||
79 | nfs_readdata_free(rdata); | ||
77 | } | 80 | } |
78 | 81 | ||
79 | static | 82 | static |
@@ -186,7 +189,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | |||
186 | data->args.pgbase = req->wb_pgbase + offset; | 189 | data->args.pgbase = req->wb_pgbase + offset; |
187 | data->args.pages = data->pagevec; | 190 | data->args.pages = data->pagevec; |
188 | data->args.count = count; | 191 | data->args.count = count; |
189 | data->args.context = req->wb_context; | 192 | data->args.context = get_nfs_open_context(req->wb_context); |
190 | 193 | ||
191 | data->res.fattr = &data->fattr; | 194 | data->res.fattr = &data->fattr; |
192 | data->res.count = count; | 195 | data->res.count = count; |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 80c61fdb2720..69b4158d9a10 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -105,8 +105,11 @@ static void nfs_writedata_free(struct nfs_write_data *wdata) | |||
105 | call_rcu_bh(&wdata->task.u.tk_rcu, nfs_writedata_rcu_free); | 105 | call_rcu_bh(&wdata->task.u.tk_rcu, nfs_writedata_rcu_free); |
106 | } | 106 | } |
107 | 107 | ||
108 | void nfs_writedata_release(void *wdata) | 108 | void nfs_writedata_release(void *data) |
109 | { | 109 | { |
110 | struct nfs_write_data *wdata = data; | ||
111 | |||
112 | put_nfs_open_context(wdata->args.context); | ||
110 | nfs_writedata_free(wdata); | 113 | nfs_writedata_free(wdata); |
111 | } | 114 | } |
112 | 115 | ||
@@ -816,7 +819,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req, | |||
816 | data->args.pgbase = req->wb_pgbase + offset; | 819 | data->args.pgbase = req->wb_pgbase + offset; |
817 | data->args.pages = data->pagevec; | 820 | data->args.pages = data->pagevec; |
818 | data->args.count = count; | 821 | data->args.count = count; |
819 | data->args.context = req->wb_context; | 822 | data->args.context = get_nfs_open_context(req->wb_context); |
820 | data->args.stable = NFS_UNSTABLE; | 823 | data->args.stable = NFS_UNSTABLE; |
821 | if (how & FLUSH_STABLE) { | 824 | if (how & FLUSH_STABLE) { |
822 | data->args.stable = NFS_DATA_SYNC; | 825 | data->args.stable = NFS_DATA_SYNC; |
@@ -1153,8 +1156,11 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1153 | 1156 | ||
1154 | 1157 | ||
1155 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1158 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
1156 | void nfs_commit_release(void *wdata) | 1159 | void nfs_commit_release(void *data) |
1157 | { | 1160 | { |
1161 | struct nfs_write_data *wdata = data; | ||
1162 | |||
1163 | put_nfs_open_context(wdata->args.context); | ||
1158 | nfs_commit_free(wdata); | 1164 | nfs_commit_free(wdata); |
1159 | } | 1165 | } |
1160 | 1166 | ||
@@ -1197,6 +1203,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, | |||
1197 | /* Note: we always request a commit of the entire inode */ | 1203 | /* Note: we always request a commit of the entire inode */ |
1198 | data->args.offset = 0; | 1204 | data->args.offset = 0; |
1199 | data->args.count = 0; | 1205 | data->args.count = 0; |
1206 | data->args.context = get_nfs_open_context(first->wb_context); | ||
1200 | data->res.count = 0; | 1207 | data->res.count = 0; |
1201 | data->res.fattr = &data->fattr; | 1208 | data->res.fattr = &data->fattr; |
1202 | data->res.verf = &data->verf; | 1209 | data->res.verf = &data->verf; |