aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@citi.umich.edu>2010-03-07 23:39:01 -0500
committerJ. Bruce Fields <bfields@citi.umich.edu>2010-04-22 11:34:02 -0400
commit4b21d0defcc9680da8a694e92d5fe8eb668c2c0b (patch)
tree35684f814bbbc428236e0e542b292521e59f06e3 /fs/nfsd
parent2bf23875f55af6038a5d1c164a52cec4c24609ba (diff)
nfsd4: allow 4.0 clients to change callback path
The rfc allows a client to change the callback parameters, but we didn't previously implement it. Teach the callbacks to rerun themselves (by placing themselves on a workqueue) when they recognize that their rpc task has been killed and that the callback connection has changed. Then we can change the callback connection by setting up a new rpc client, modifying the nfs4 client to point at it, waiting for any work in progress to complete, and then shutting down the old client. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4callback.c29
-rw-r--r--fs/nfsd/nfs4state.c7
-rw-r--r--fs/nfsd/state.h2
3 files changed, 24 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index d6c46a9de422..ea77aa63754a 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -457,9 +457,8 @@ static int max_cb_time(void)
457/* Reference counting, callback cleanup, etc., all look racy as heck. 457/* Reference counting, callback cleanup, etc., all look racy as heck.
458 * And why is cl_cb_set an atomic? */ 458 * And why is cl_cb_set an atomic? */
459 459
460int setup_callback_client(struct nfs4_client *clp) 460int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
461{ 461{
462 struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
463 struct rpc_timeout timeparms = { 462 struct rpc_timeout timeparms = {
464 .to_initval = max_cb_time(), 463 .to_initval = max_cb_time(),
465 .to_retries = 0, 464 .to_retries = 0,
@@ -481,7 +480,7 @@ int setup_callback_client(struct nfs4_client *clp)
481 if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) 480 if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
482 return -EINVAL; 481 return -EINVAL;
483 if (cb->cb_minorversion) { 482 if (cb->cb_minorversion) {
484 args.bc_xprt = clp->cl_cb_conn.cb_xprt; 483 args.bc_xprt = cb->cb_xprt;
485 args.protocol = XPRT_TRANSPORT_BC_TCP; 484 args.protocol = XPRT_TRANSPORT_BC_TCP;
486 } 485 }
487 /* Create RPC client */ 486 /* Create RPC client */
@@ -491,7 +490,7 @@ int setup_callback_client(struct nfs4_client *clp)
491 PTR_ERR(client)); 490 PTR_ERR(client));
492 return PTR_ERR(client); 491 return PTR_ERR(client);
493 } 492 }
494 clp->cl_cb_client = client; 493 nfsd4_set_callback_client(clp, client);
495 return 0; 494 return 0;
496 495
497} 496}
@@ -548,14 +547,13 @@ void do_probe_callback(struct nfs4_client *clp)
548/* 547/*
549 * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... 548 * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
550 */ 549 */
551void 550void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
552nfsd4_probe_callback(struct nfs4_client *clp)
553{ 551{
554 int status; 552 int status;
555 553
556 BUG_ON(atomic_read(&clp->cl_cb_set)); 554 BUG_ON(atomic_read(&clp->cl_cb_set));
557 555
558 status = setup_callback_client(clp); 556 status = setup_callback_client(clp, cb);
559 if (status) { 557 if (status) {
560 warn_no_callback_path(clp, status); 558 warn_no_callback_path(clp, status);
561 return; 559 return;
@@ -645,18 +643,32 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
645 } 643 }
646} 644}
647 645
646
648static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) 647static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
649{ 648{
650 struct nfs4_delegation *dp = calldata; 649 struct nfs4_delegation *dp = calldata;
651 struct nfs4_client *clp = dp->dl_client; 650 struct nfs4_client *clp = dp->dl_client;
651 struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
652 652
653 nfsd4_cb_done(task, calldata); 653 nfsd4_cb_done(task, calldata);
654 654
655 if (current_rpc_client == NULL) {
656 /* We're shutting down; give up. */
657 /* XXX: err, or is it ok just to fall through
658 * and rpc_restart_call? */
659 return;
660 }
661
655 switch (task->tk_status) { 662 switch (task->tk_status) {
656 case -EIO: 663 case -EIO:
657 /* Network partition? */ 664 /* Network partition? */
658 atomic_set(&clp->cl_cb_set, 0); 665 atomic_set(&clp->cl_cb_set, 0);
659 warn_no_callback_path(clp, task->tk_status); 666 warn_no_callback_path(clp, task->tk_status);
667 if (current_rpc_client != task->tk_client) {
668 /* queue a callback on the new connection: */
669 nfsd4_cb_recall(dp);
670 return;
671 }
660 case -EBADHANDLE: 672 case -EBADHANDLE:
661 case -NFS4ERR_BAD_STATEID: 673 case -NFS4ERR_BAD_STATEID:
662 /* Race: client probably got cb_recall 674 /* Race: client probably got cb_recall
@@ -705,8 +717,7 @@ void nfsd4_destroy_callback_queue(void)
705 destroy_workqueue(callback_wq); 717 destroy_workqueue(callback_wq);
706} 718}
707 719
708void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt 720void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
709*new)
710{ 721{
711 struct rpc_clnt *old = clp->cl_cb_client; 722 struct rpc_clnt *old = clp->cl_cb_client;
712 723
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 59c9bd4c89e1..4300d9ffe95f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1312,7 +1312,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
1312 cstate->minorversion; 1312 cstate->minorversion;
1313 unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog; 1313 unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
1314 unconf->cl_cb_seq_nr = 1; 1314 unconf->cl_cb_seq_nr = 1;
1315 nfsd4_probe_callback(unconf); 1315 nfsd4_probe_callback(unconf, &unconf->cl_cb_conn);
1316 } 1316 }
1317 conf = unconf; 1317 conf = unconf;
1318 } else { 1318 } else {
@@ -1605,9 +1605,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
1605 if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) 1605 if (!same_creds(&conf->cl_cred, &unconf->cl_cred))
1606 status = nfserr_clid_inuse; 1606 status = nfserr_clid_inuse;
1607 else { 1607 else {
1608 /* XXX: We just turn off callbacks until we can handle
1609 * change request correctly. */
1610 atomic_set(&conf->cl_cb_set, 0); 1608 atomic_set(&conf->cl_cb_set, 0);
1609 nfsd4_probe_callback(conf, &unconf->cl_cb_conn);
1611 expire_client(unconf); 1610 expire_client(unconf);
1612 status = nfs_ok; 1611 status = nfs_ok;
1613 1612
@@ -1641,7 +1640,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
1641 } 1640 }
1642 move_to_confirmed(unconf); 1641 move_to_confirmed(unconf);
1643 conf = unconf; 1642 conf = unconf;
1644 nfsd4_probe_callback(conf); 1643 nfsd4_probe_callback(conf, &conf->cl_cb_conn);
1645 status = nfs_ok; 1644 status = nfs_ok;
1646 } 1645 }
1647 } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm))) 1646 } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index cf43812e6da5..98836fd87f69 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -390,7 +390,7 @@ extern int nfs4_in_grace(void);
390extern __be32 nfs4_check_open_reclaim(clientid_t *clid); 390extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
391extern void nfs4_free_stateowner(struct kref *kref); 391extern void nfs4_free_stateowner(struct kref *kref);
392extern int set_callback_cred(void); 392extern int set_callback_cred(void);
393extern void nfsd4_probe_callback(struct nfs4_client *clp); 393extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
394extern void nfsd4_do_callback_rpc(struct work_struct *); 394extern void nfsd4_do_callback_rpc(struct work_struct *);
395extern void nfsd4_cb_recall(struct nfs4_delegation *dp); 395extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
396extern int nfsd4_create_callback_queue(void); 396extern int nfsd4_create_callback_queue(void);