diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-10-03 16:48:34 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-10-07 18:18:53 -0400 |
commit | 9a4bd29fe8f6d3f015fe1c8e5450eb62cfebfcc9 (patch) | |
tree | c3175a0a07ecf41f8eae1204ef5f5421c4b023f5 | |
parent | d1ce02e1689dff9d413138f60a79b4e3affb4708 (diff) |
SUNRPC: Fix autobind on cloned rpc clients
Despite the fact that cloned rpc clients won't have the cl_autobind flag
set, they may still find themselves calling rpcb_getport_async(). For this
to happen, it suffices for a _parent_ rpc_clnt to use autobinding, in which
case any clone may find itself triggering the !xprt_bound() case in
call_bind().
The correct fix for this is to walk back up the tree of cloned rpc clients,
in order to find the parent that 'owns' the transport, either because it
has clnt->cl_autobind set, or because it originally created the
transport...
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | net/sunrpc/rpcb_clnt.c | 36 |
1 files changed, 29 insertions, 7 deletions
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 24db2b4d12d3..172935b046de 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c | |||
@@ -469,6 +469,28 @@ static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbi | |||
469 | return rpc_run_task(&task_setup_data); | 469 | return rpc_run_task(&task_setup_data); |
470 | } | 470 | } |
471 | 471 | ||
472 | /* | ||
473 | * In the case where rpc clients have been cloned, we want to make | ||
474 | * sure that we use the program number/version etc of the actual | ||
475 | * owner of the xprt. To do so, we walk back up the tree of parents | ||
476 | * to find whoever created the transport and/or whoever has the | ||
477 | * autobind flag set. | ||
478 | */ | ||
479 | static struct rpc_clnt *rpcb_find_transport_owner(struct rpc_clnt *clnt) | ||
480 | { | ||
481 | struct rpc_clnt *parent = clnt->cl_parent; | ||
482 | |||
483 | while (parent != clnt) { | ||
484 | if (parent->cl_xprt != clnt->cl_xprt) | ||
485 | break; | ||
486 | if (clnt->cl_autobind) | ||
487 | break; | ||
488 | clnt = parent; | ||
489 | parent = parent->cl_parent; | ||
490 | } | ||
491 | return clnt; | ||
492 | } | ||
493 | |||
472 | /** | 494 | /** |
473 | * rpcb_getport_async - obtain the port for a given RPC service on a given host | 495 | * rpcb_getport_async - obtain the port for a given RPC service on a given host |
474 | * @task: task that is waiting for portmapper request | 496 | * @task: task that is waiting for portmapper request |
@@ -478,10 +500,10 @@ static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbi | |||
478 | */ | 500 | */ |
479 | void rpcb_getport_async(struct rpc_task *task) | 501 | void rpcb_getport_async(struct rpc_task *task) |
480 | { | 502 | { |
481 | struct rpc_clnt *clnt = task->tk_client; | 503 | struct rpc_clnt *clnt; |
482 | struct rpc_procinfo *proc; | 504 | struct rpc_procinfo *proc; |
483 | u32 bind_version; | 505 | u32 bind_version; |
484 | struct rpc_xprt *xprt = task->tk_xprt; | 506 | struct rpc_xprt *xprt; |
485 | struct rpc_clnt *rpcb_clnt; | 507 | struct rpc_clnt *rpcb_clnt; |
486 | static struct rpcbind_args *map; | 508 | static struct rpcbind_args *map; |
487 | struct rpc_task *child; | 509 | struct rpc_task *child; |
@@ -490,13 +512,13 @@ void rpcb_getport_async(struct rpc_task *task) | |||
490 | size_t salen; | 512 | size_t salen; |
491 | int status; | 513 | int status; |
492 | 514 | ||
515 | clnt = rpcb_find_transport_owner(task->tk_client); | ||
516 | xprt = clnt->cl_xprt; | ||
517 | |||
493 | dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", | 518 | dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", |
494 | task->tk_pid, __func__, | 519 | task->tk_pid, __func__, |
495 | clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot); | 520 | clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot); |
496 | 521 | ||
497 | /* Autobind on cloned rpc clients is discouraged */ | ||
498 | BUG_ON(clnt->cl_parent != clnt); | ||
499 | |||
500 | /* Put self on the wait queue to ensure we get notified if | 522 | /* Put self on the wait queue to ensure we get notified if |
501 | * some other task is already attempting to bind the port */ | 523 | * some other task is already attempting to bind the port */ |
502 | rpc_sleep_on(&xprt->binding, task, NULL); | 524 | rpc_sleep_on(&xprt->binding, task, NULL); |
@@ -578,9 +600,9 @@ void rpcb_getport_async(struct rpc_task *task) | |||
578 | task->tk_pid, __func__); | 600 | task->tk_pid, __func__); |
579 | return; | 601 | return; |
580 | } | 602 | } |
581 | rpc_put_task(child); | ||
582 | 603 | ||
583 | task->tk_xprt->stat.bind_count++; | 604 | xprt->stat.bind_count++; |
605 | rpc_put_task(child); | ||
584 | return; | 606 | return; |
585 | 607 | ||
586 | bailout_nofree: | 608 | bailout_nofree: |