Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SUNRPC: Add a helper to switch the transport of an rpc_clnt

Add an RPC client API to redirect an rpc_clnt's transport from a
source server to a destination server during a migration event.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
[ cel: forward ported to 3.12 ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

+104 -7
+4
include/linux/sunrpc/clnt.h
··· 136 136 struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); 137 137 struct rpc_clnt *rpc_clone_client_set_auth(struct rpc_clnt *, 138 138 rpc_authflavor_t); 139 + int rpc_switch_client_transport(struct rpc_clnt *, 140 + struct xprt_create *, 141 + const struct rpc_timeout *); 142 + 139 143 void rpc_shutdown_client(struct rpc_clnt *); 140 144 void rpc_release_client(struct rpc_clnt *); 141 145 void rpc_task_release_client(struct rpc_task *);
+100 -7
net/sunrpc/clnt.c
··· 25 25 #include <linux/namei.h> 26 26 #include <linux/mount.h> 27 27 #include <linux/slab.h> 28 + #include <linux/rcupdate.h> 28 29 #include <linux/utsname.h> 29 30 #include <linux/workqueue.h> 30 31 #include <linux/in.h> ··· 265 264 return rpc_pipefs_notifier_unregister(&rpc_clients_block); 266 265 } 267 266 267 + static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt, 268 + struct rpc_xprt *xprt, 269 + const struct rpc_timeout *timeout) 270 + { 271 + struct rpc_xprt *old; 272 + 273 + spin_lock(&clnt->cl_lock); 274 + old = clnt->cl_xprt; 275 + 276 + if (!xprt_bound(xprt)) 277 + clnt->cl_autobind = 1; 278 + 279 + clnt->cl_timeout = timeout; 280 + rcu_assign_pointer(clnt->cl_xprt, xprt); 281 + spin_unlock(&clnt->cl_lock); 282 + 283 + return old; 284 + } 285 + 268 286 static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) 269 287 { 270 288 clnt->cl_nodelen = strlen(nodename); ··· 358 338 { 359 339 const struct rpc_program *program = args->program; 360 340 const struct rpc_version *version; 361 - struct rpc_clnt *clnt = NULL; 341 + struct rpc_clnt *clnt = NULL; 342 + const struct rpc_timeout *timeout; 362 343 int err; 363 344 364 345 /* sanity check the name before trying to print it */ ··· 387 366 if (err) 388 367 goto out_no_clid; 389 368 390 - rcu_assign_pointer(clnt->cl_xprt, xprt); 391 369 clnt->cl_procinfo = version->procs; 392 370 clnt->cl_maxproc = version->nrprocs; 393 371 clnt->cl_prog = args->prognumber ? : program->number; ··· 401 381 INIT_LIST_HEAD(&clnt->cl_tasks); 402 382 spin_lock_init(&clnt->cl_lock); 403 383 404 - if (!xprt_bound(xprt)) 405 - clnt->cl_autobind = 1; 406 - 407 - clnt->cl_timeout = xprt->timeout; 384 + timeout = xprt->timeout; 408 385 if (args->timeout != NULL) { 409 386 memcpy(&clnt->cl_timeout_default, args->timeout, 410 387 sizeof(clnt->cl_timeout_default)); 411 - clnt->cl_timeout = &clnt->cl_timeout_default; 388 + timeout = &clnt->cl_timeout_default; 412 389 } 390 + 391 + rpc_clnt_set_transport(clnt, xprt, timeout); 413 392 414 393 clnt->cl_rtt = &clnt->cl_rtt_default; 415 394 rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); ··· 619 600 return __rpc_clone_client(&args, clnt); 620 601 } 621 602 EXPORT_SYMBOL_GPL(rpc_clone_client_set_auth); 603 + 604 + /** 605 + * rpc_switch_client_transport: switch the RPC transport on the fly 606 + * @clnt: pointer to a struct rpc_clnt 607 + * @args: pointer to the new transport arguments 608 + * @timeout: pointer to the new timeout parameters 609 + * 610 + * This function allows the caller to switch the RPC transport for the 611 + * rpc_clnt structure 'clnt' to allow it to connect to a mirrored NFS 612 + * server, for instance. It assumes that the caller has ensured that 613 + * there are no active RPC tasks by using some form of locking. 614 + * 615 + * Returns zero if "clnt" is now using the new xprt. Otherwise a 616 + * negative errno is returned, and "clnt" continues to use the old 617 + * xprt. 618 + */ 619 + int rpc_switch_client_transport(struct rpc_clnt *clnt, 620 + struct xprt_create *args, 621 + const struct rpc_timeout *timeout) 622 + { 623 + const struct rpc_timeout *old_timeo; 624 + rpc_authflavor_t pseudoflavor; 625 + struct rpc_xprt *xprt, *old; 626 + struct rpc_clnt *parent; 627 + int err; 628 + 629 + xprt = xprt_create_transport(args); 630 + if (IS_ERR(xprt)) { 631 + dprintk("RPC: failed to create new xprt for clnt %p\n", 632 + clnt); 633 + return PTR_ERR(xprt); 634 + } 635 + 636 + pseudoflavor = clnt->cl_auth->au_flavor; 637 + 638 + old_timeo = clnt->cl_timeout; 639 + old = rpc_clnt_set_transport(clnt, xprt, timeout); 640 + 641 + rpc_unregister_client(clnt); 642 + __rpc_clnt_remove_pipedir(clnt); 643 + 644 + /* 645 + * A new transport was created. "clnt" therefore 646 + * becomes the root of a new cl_parent tree. clnt's 647 + * children, if it has any, still point to the old xprt. 648 + */ 649 + parent = clnt->cl_parent; 650 + clnt->cl_parent = clnt; 651 + 652 + /* 653 + * The old rpc_auth cache cannot be re-used. GSS 654 + * contexts in particular are between a single 655 + * client and server. 656 + */ 657 + err = rpc_client_register(clnt, pseudoflavor, NULL); 658 + if (err) 659 + goto out_revert; 660 + 661 + synchronize_rcu(); 662 + if (parent != clnt) 663 + rpc_release_client(parent); 664 + xprt_put(old); 665 + dprintk("RPC: replaced xprt for clnt %p\n", clnt); 666 + return 0; 667 + 668 + out_revert: 669 + rpc_clnt_set_transport(clnt, old, old_timeo); 670 + clnt->cl_parent = parent; 671 + rpc_client_register(clnt, pseudoflavor, NULL); 672 + xprt_put(xprt); 673 + dprintk("RPC: failed to switch xprt for clnt %p\n", clnt); 674 + return err; 675 + } 676 + EXPORT_SYMBOL_GPL(rpc_switch_client_transport); 622 677 623 678 /* 624 679 * Kill all tasks for the given client.