diff options
author | J. Bruce Fields <bfields@citi.umich.edu> | 2010-03-07 23:39:01 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2010-04-22 11:34:02 -0400 |
commit | 4b21d0defcc9680da8a694e92d5fe8eb668c2c0b (patch) | |
tree | 35684f814bbbc428236e0e542b292521e59f06e3 /fs | |
parent | 2bf23875f55af6038a5d1c164a52cec4c24609ba (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')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 29 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 7 | ||||
-rw-r--r-- | fs/nfsd/state.h | 2 |
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 | ||
460 | int setup_callback_client(struct nfs4_client *clp) | 460 | int 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 | */ |
551 | void | 550 | void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb) |
552 | nfsd4_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 | |||
648 | static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | 647 | static 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 | ||
708 | void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt | 720 | void 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); | |||
390 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); | 390 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); |
391 | extern void nfs4_free_stateowner(struct kref *kref); | 391 | extern void nfs4_free_stateowner(struct kref *kref); |
392 | extern int set_callback_cred(void); | 392 | extern int set_callback_cred(void); |
393 | extern void nfsd4_probe_callback(struct nfs4_client *clp); | 393 | extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); |
394 | extern void nfsd4_do_callback_rpc(struct work_struct *); | 394 | extern void nfsd4_do_callback_rpc(struct work_struct *); |
395 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); | 395 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); |
396 | extern int nfsd4_create_callback_queue(void); | 396 | extern int nfsd4_create_callback_queue(void); |