From 4d65c520fb4abed970069d18c119cfe85624f46d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 25 Mar 2011 14:15:11 -0400 Subject: NFS: Fix a hang in the writeback path Now that the inode scalability patches have been merged, it is no longer safe to call igrab() under the inode->i_lock. Now that we no longer call nfs_clear_request() until the nfs_page is being freed, we know that we are always holding a reference to the nfs_open_context, which again holds a reference to the path, and so the inode cannot be freed until the last nfs_page has been removed from the radix tree and freed. We can therefore skip the igrab()/iput() altogether. Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 4 ++-- fs/nfs/write.c | 13 +++---------- include/linux/nfs_page.h | 1 - 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 87a593c2b055..c80add6e2213 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -135,14 +135,14 @@ void nfs_clear_page_tag_locked(struct nfs_page *req) nfs_unlock_request(req); } -/** +/* * nfs_clear_request - Free up all resources allocated to the request * @req: * * Release page and open context resources associated with a read/write * request after it has completed. */ -void nfs_clear_request(struct nfs_page *req) +static void nfs_clear_request(struct nfs_page *req) { struct page *page = req->wb_page; struct nfs_open_context *ctx = req->wb_context; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 85d75254328e..af0c6279a4a7 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -389,11 +389,8 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) spin_lock(&inode->i_lock); error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req); BUG_ON(error); - if (!nfsi->npages) { - igrab(inode); - if (nfs_have_delegation(inode, FMODE_WRITE)) - nfsi->change_attr++; - } + if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) + nfsi->change_attr++; set_bit(PG_MAPPED, &req->wb_flags); SetPagePrivate(req->wb_page); set_page_private(req->wb_page, (unsigned long)req); @@ -423,11 +420,7 @@ static void nfs_inode_remove_request(struct nfs_page *req) clear_bit(PG_MAPPED, &req->wb_flags); radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); nfsi->npages--; - if (!nfsi->npages) { - spin_unlock(&inode->i_lock); - iput(inode); - } else - spin_unlock(&inode->i_lock); + spin_unlock(&inode->i_lock); nfs_release_request(req); } diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 8023e4e25133..91af2e49fa3a 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -78,7 +78,6 @@ extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, struct page *page, unsigned int offset, unsigned int count); -extern void nfs_clear_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req); -- cgit v1.2.3 From a0e7e3cf7932d6c77de0dc79a40dbaeb8060b544 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 26 Mar 2011 02:24:35 -0400 Subject: NFS: Don't leak RPC clients in NFSv4 secinfo negotiation Signed-off-by: Trond Myklebust --- fs/nfs/namespace.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index ad92bf731ff5..9166fcb66da2 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -192,13 +192,15 @@ static rpc_authflavor_t nfs_lookup_with_sec(struct nfs_server *server, struct de auth = rpcauth_create(flavor, clone); if (!auth) { flavor = -EIO; - goto out; + goto out_shutdown; } err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode, &path->dentry->d_name, fh, fattr); if (err < 0) flavor = err; +out_shutdown: + rpc_shutdown_client(clone); out: return flavor; } -- cgit v1.2.3 From a271c5a0dea418931b6a903ef85adc30ad4c54be Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 27 Mar 2011 17:48:57 +0200 Subject: NFS: Ensure that rpc_release_resources_task() can be called twice. BUG: atomic_dec_and_test(): -1: atomic counter underflow at: Pid: 2827, comm: mount.nfs Not tainted 2.6.38 #1 Call Trace: [] ? put_rpccred+0x44/0x14e [sunrpc] [] ? rpc_ping+0x4e/0x58 [sunrpc] [] ? rpc_create+0x481/0x4fc [sunrpc] [] ? rpcauth_lookup_credcache+0xab/0x22d [sunrpc] [] ? nfs_create_rpc_client+0xa6/0xeb [nfs] [] ? nfs4_set_client+0xc2/0x1f9 [nfs] [] ? nfs4_create_server+0xf2/0x2a6 [nfs] [] ? nfs4_remote_mount+0x4e/0x14a [nfs] [] ? vfs_kern_mount+0x6e/0x133 [] ? nfs_do_root_mount+0x76/0x95 [nfs] [] ? nfs4_try_mount+0x56/0xaf [nfs] [] ? nfs_get_sb+0x435/0x73c [nfs] [] ? vfs_kern_mount+0x99/0x133 [] ? do_kern_mount+0x48/0xd8 [] ? do_mount+0x6da/0x741 [] ? sys_mount+0x83/0xc0 [] ? system_call_fastpath+0x16/0x1b Well, so, I think this is real bug of nfs codes somewhere. With some review, the code rpc_call_sync() rpc_run_task rpc_execute() __rpc_execute() rpc_release_task() rpc_release_resources_task() put_rpccred() <= release cred rpc_put_task rpc_do_put_task() rpc_release_resources_task() put_rpccred() <= release cred again seems to be release cred unintendedly. Signed-off-by: OGAWA Hirofumi Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index ffb687671da0..6b43ee7221d5 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -860,8 +860,10 @@ static void rpc_release_resources_task(struct rpc_task *task) { if (task->tk_rqstp) xprt_release(task); - if (task->tk_msg.rpc_cred) + if (task->tk_msg.rpc_cred) { put_rpccred(task->tk_msg.rpc_cred); + task->tk_msg.rpc_cred = NULL; + } rpc_task_release_client(task); } -- cgit v1.2.3