SUNRPC: Avoid deep recursion in rpc_release_client
In cases where an rpc client has a parent hierarchy, then rpc_free_client may end up calling rpc_release_client() on the parent, thus recursing back into rpc_free_client. If the hierarchy is deep enough, then we can get into situations where the stack simply overflows. The fix is to have rpc_release_client() loop so that it can take care of the parent rpc client hierarchy without needing to recurse. Reported-by: Jeff Layton <jlayton@redhat.com> Reported-by: Weston Andros Adamson <dros@netapp.com> Reported-by: Bruce Fields <bfields@fieldses.org> Link: http://lkml.kernel.org/r/2C73011F-0939-434C-9E4D-13A1EB1403D7@netapp.com Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
a6b31d18b0
commit
d07ba8422f
1 changed files with 17 additions and 12 deletions
|
@ -750,14 +750,16 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
|
|||
/*
|
||||
* Free an RPC client
|
||||
*/
|
||||
static void
|
||||
static struct rpc_clnt *
|
||||
rpc_free_client(struct rpc_clnt *clnt)
|
||||
{
|
||||
struct rpc_clnt *parent = NULL;
|
||||
|
||||
dprintk_rcu("RPC: destroying %s client for %s\n",
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
if (clnt->cl_parent != clnt)
|
||||
rpc_release_client(clnt->cl_parent);
|
||||
parent = clnt->cl_parent;
|
||||
rpc_clnt_remove_pipedir(clnt);
|
||||
rpc_unregister_client(clnt);
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
|
@ -766,18 +768,17 @@ rpc_free_client(struct rpc_clnt *clnt)
|
|||
rpciod_down();
|
||||
rpc_free_clid(clnt);
|
||||
kfree(clnt);
|
||||
return parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an RPC client
|
||||
*/
|
||||
static void
|
||||
static struct rpc_clnt *
|
||||
rpc_free_auth(struct rpc_clnt *clnt)
|
||||
{
|
||||
if (clnt->cl_auth == NULL) {
|
||||
rpc_free_client(clnt);
|
||||
return;
|
||||
}
|
||||
if (clnt->cl_auth == NULL)
|
||||
return rpc_free_client(clnt);
|
||||
|
||||
/*
|
||||
* Note: RPCSEC_GSS may need to send NULL RPC calls in order to
|
||||
|
@ -788,7 +789,8 @@ rpc_free_auth(struct rpc_clnt *clnt)
|
|||
rpcauth_release(clnt->cl_auth);
|
||||
clnt->cl_auth = NULL;
|
||||
if (atomic_dec_and_test(&clnt->cl_count))
|
||||
rpc_free_client(clnt);
|
||||
return rpc_free_client(clnt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -799,10 +801,13 @@ rpc_release_client(struct rpc_clnt *clnt)
|
|||
{
|
||||
dprintk("RPC: rpc_release_client(%p)\n", clnt);
|
||||
|
||||
if (list_empty(&clnt->cl_tasks))
|
||||
wake_up(&destroy_wait);
|
||||
if (atomic_dec_and_test(&clnt->cl_count))
|
||||
rpc_free_auth(clnt);
|
||||
do {
|
||||
if (list_empty(&clnt->cl_tasks))
|
||||
wake_up(&destroy_wait);
|
||||
if (!atomic_dec_and_test(&clnt->cl_count))
|
||||
break;
|
||||
clnt = rpc_free_auth(clnt);
|
||||
} while (clnt != NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_release_client);
|
||||
|
||||
|
|
Loading…
Reference in a new issue