diff options
Diffstat (limited to 'fs/nfsd/nfs4callback.c')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 140 |
1 files changed, 92 insertions, 48 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 7e32bd394e86..eb78e7e22077 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -32,6 +32,7 @@ | |||
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <linux/sunrpc/clnt.h> | 34 | #include <linux/sunrpc/clnt.h> |
35 | #include <linux/sunrpc/svc_xprt.h> | ||
35 | #include <linux/slab.h> | 36 | #include <linux/slab.h> |
36 | #include "nfsd.h" | 37 | #include "nfsd.h" |
37 | #include "state.h" | 38 | #include "state.h" |
@@ -79,11 +80,6 @@ enum nfs_cb_opnum4 { | |||
79 | cb_sequence_dec_sz + \ | 80 | cb_sequence_dec_sz + \ |
80 | op_dec_sz) | 81 | op_dec_sz) |
81 | 82 | ||
82 | struct nfs4_rpc_args { | ||
83 | void *args_op; | ||
84 | struct nfsd4_cb_sequence args_seq; | ||
85 | }; | ||
86 | |||
87 | /* | 83 | /* |
88 | * Generic encode routines from fs/nfs/nfs4xdr.c | 84 | * Generic encode routines from fs/nfs/nfs4xdr.c |
89 | */ | 85 | */ |
@@ -428,13 +424,19 @@ static struct rpc_procinfo nfs4_cb_procedures[] = { | |||
428 | }; | 424 | }; |
429 | 425 | ||
430 | static struct rpc_version nfs_cb_version4 = { | 426 | static struct rpc_version nfs_cb_version4 = { |
427 | /* | ||
428 | * Note on the callback rpc program version number: despite language in rfc | ||
429 | * 5661 section 18.36.3 requiring servers to use 4 in this field, the | ||
430 | * official xdr descriptions for both 4.0 and 4.1 specify version 1, and | ||
431 | * in practice that appears to be what implementations use. The section | ||
432 | * 18.36.3 language is expected to be fixed in an erratum. | ||
433 | */ | ||
431 | .number = 1, | 434 | .number = 1, |
432 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), | 435 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), |
433 | .procs = nfs4_cb_procedures | 436 | .procs = nfs4_cb_procedures |
434 | }; | 437 | }; |
435 | 438 | ||
436 | static struct rpc_version * nfs_cb_version[] = { | 439 | static struct rpc_version * nfs_cb_version[] = { |
437 | NULL, | ||
438 | &nfs_cb_version4, | 440 | &nfs_cb_version4, |
439 | }; | 441 | }; |
440 | 442 | ||
@@ -456,15 +458,14 @@ static struct rpc_program cb_program = { | |||
456 | 458 | ||
457 | static int max_cb_time(void) | 459 | static int max_cb_time(void) |
458 | { | 460 | { |
459 | return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ; | 461 | return max(nfsd4_lease/10, (time_t)1) * HZ; |
460 | } | 462 | } |
461 | 463 | ||
462 | /* Reference counting, callback cleanup, etc., all look racy as heck. | 464 | /* Reference counting, callback cleanup, etc., all look racy as heck. |
463 | * And why is cb_set an atomic? */ | 465 | * And why is cl_cb_set an atomic? */ |
464 | 466 | ||
465 | int setup_callback_client(struct nfs4_client *clp) | 467 | int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) |
466 | { | 468 | { |
467 | struct nfs4_cb_conn *cb = &clp->cl_cb_conn; | ||
468 | struct rpc_timeout timeparms = { | 469 | struct rpc_timeout timeparms = { |
469 | .to_initval = max_cb_time(), | 470 | .to_initval = max_cb_time(), |
470 | .to_retries = 0, | 471 | .to_retries = 0, |
@@ -476,7 +477,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
476 | .timeout = &timeparms, | 477 | .timeout = &timeparms, |
477 | .program = &cb_program, | 478 | .program = &cb_program, |
478 | .prognumber = cb->cb_prog, | 479 | .prognumber = cb->cb_prog, |
479 | .version = nfs_cb_version[1]->number, | 480 | .version = 0, |
480 | .authflavor = clp->cl_flavor, | 481 | .authflavor = clp->cl_flavor, |
481 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), | 482 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), |
482 | .client_name = clp->cl_principal, | 483 | .client_name = clp->cl_principal, |
@@ -486,7 +487,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
486 | if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) | 487 | if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) |
487 | return -EINVAL; | 488 | return -EINVAL; |
488 | if (cb->cb_minorversion) { | 489 | if (cb->cb_minorversion) { |
489 | args.bc_xprt = clp->cl_cb_xprt; | 490 | args.bc_xprt = cb->cb_xprt; |
490 | args.protocol = XPRT_TRANSPORT_BC_TCP; | 491 | args.protocol = XPRT_TRANSPORT_BC_TCP; |
491 | } | 492 | } |
492 | /* Create RPC client */ | 493 | /* Create RPC client */ |
@@ -496,7 +497,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
496 | PTR_ERR(client)); | 497 | PTR_ERR(client)); |
497 | return PTR_ERR(client); | 498 | return PTR_ERR(client); |
498 | } | 499 | } |
499 | cb->cb_client = client; | 500 | nfsd4_set_callback_client(clp, client); |
500 | return 0; | 501 | return 0; |
501 | 502 | ||
502 | } | 503 | } |
@@ -514,8 +515,7 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) | |||
514 | if (task->tk_status) | 515 | if (task->tk_status) |
515 | warn_no_callback_path(clp, task->tk_status); | 516 | warn_no_callback_path(clp, task->tk_status); |
516 | else | 517 | else |
517 | atomic_set(&clp->cl_cb_conn.cb_set, 1); | 518 | atomic_set(&clp->cl_cb_set, 1); |
518 | put_nfs4_client(clp); | ||
519 | } | 519 | } |
520 | 520 | ||
521 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { | 521 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { |
@@ -537,7 +537,6 @@ int set_callback_cred(void) | |||
537 | 537 | ||
538 | void do_probe_callback(struct nfs4_client *clp) | 538 | void do_probe_callback(struct nfs4_client *clp) |
539 | { | 539 | { |
540 | struct nfs4_cb_conn *cb = &clp->cl_cb_conn; | ||
541 | struct rpc_message msg = { | 540 | struct rpc_message msg = { |
542 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], | 541 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], |
543 | .rpc_argp = clp, | 542 | .rpc_argp = clp, |
@@ -545,34 +544,27 @@ void do_probe_callback(struct nfs4_client *clp) | |||
545 | }; | 544 | }; |
546 | int status; | 545 | int status; |
547 | 546 | ||
548 | status = rpc_call_async(cb->cb_client, &msg, | 547 | status = rpc_call_async(clp->cl_cb_client, &msg, |
549 | RPC_TASK_SOFT | RPC_TASK_SOFTCONN, | 548 | RPC_TASK_SOFT | RPC_TASK_SOFTCONN, |
550 | &nfsd4_cb_probe_ops, (void *)clp); | 549 | &nfsd4_cb_probe_ops, (void *)clp); |
551 | if (status) { | 550 | if (status) |
552 | warn_no_callback_path(clp, status); | 551 | warn_no_callback_path(clp, status); |
553 | put_nfs4_client(clp); | ||
554 | } | ||
555 | } | 552 | } |
556 | 553 | ||
557 | /* | 554 | /* |
558 | * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... | 555 | * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... |
559 | */ | 556 | */ |
560 | void | 557 | void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb) |
561 | nfsd4_probe_callback(struct nfs4_client *clp) | ||
562 | { | 558 | { |
563 | int status; | 559 | int status; |
564 | 560 | ||
565 | BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set)); | 561 | BUG_ON(atomic_read(&clp->cl_cb_set)); |
566 | 562 | ||
567 | status = setup_callback_client(clp); | 563 | status = setup_callback_client(clp, cb); |
568 | if (status) { | 564 | if (status) { |
569 | warn_no_callback_path(clp, status); | 565 | warn_no_callback_path(clp, status); |
570 | return; | 566 | return; |
571 | } | 567 | } |
572 | |||
573 | /* the task holds a reference to the nfs4_client struct */ | ||
574 | atomic_inc(&clp->cl_count); | ||
575 | |||
576 | do_probe_callback(clp); | 568 | do_probe_callback(clp); |
577 | } | 569 | } |
578 | 570 | ||
@@ -658,18 +650,32 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
658 | } | 650 | } |
659 | } | 651 | } |
660 | 652 | ||
653 | |||
661 | static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | 654 | static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) |
662 | { | 655 | { |
663 | struct nfs4_delegation *dp = calldata; | 656 | struct nfs4_delegation *dp = calldata; |
664 | struct nfs4_client *clp = dp->dl_client; | 657 | struct nfs4_client *clp = dp->dl_client; |
658 | struct rpc_clnt *current_rpc_client = clp->cl_cb_client; | ||
665 | 659 | ||
666 | nfsd4_cb_done(task, calldata); | 660 | nfsd4_cb_done(task, calldata); |
667 | 661 | ||
662 | if (current_rpc_client == NULL) { | ||
663 | /* We're shutting down; give up. */ | ||
664 | /* XXX: err, or is it ok just to fall through | ||
665 | * and rpc_restart_call? */ | ||
666 | return; | ||
667 | } | ||
668 | |||
668 | switch (task->tk_status) { | 669 | switch (task->tk_status) { |
669 | case -EIO: | 670 | case -EIO: |
670 | /* Network partition? */ | 671 | /* Network partition? */ |
671 | atomic_set(&clp->cl_cb_conn.cb_set, 0); | 672 | atomic_set(&clp->cl_cb_set, 0); |
672 | warn_no_callback_path(clp, task->tk_status); | 673 | warn_no_callback_path(clp, task->tk_status); |
674 | if (current_rpc_client != task->tk_client) { | ||
675 | /* queue a callback on the new connection: */ | ||
676 | nfsd4_cb_recall(dp); | ||
677 | return; | ||
678 | } | ||
673 | case -EBADHANDLE: | 679 | case -EBADHANDLE: |
674 | case -NFS4ERR_BAD_STATEID: | 680 | case -NFS4ERR_BAD_STATEID: |
675 | /* Race: client probably got cb_recall | 681 | /* Race: client probably got cb_recall |
@@ -677,7 +683,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
677 | break; | 683 | break; |
678 | default: | 684 | default: |
679 | /* success, or error we can't handle */ | 685 | /* success, or error we can't handle */ |
680 | goto done; | 686 | return; |
681 | } | 687 | } |
682 | if (dp->dl_retries--) { | 688 | if (dp->dl_retries--) { |
683 | rpc_delay(task, 2*HZ); | 689 | rpc_delay(task, 2*HZ); |
@@ -685,20 +691,16 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
685 | rpc_restart_call(task); | 691 | rpc_restart_call(task); |
686 | return; | 692 | return; |
687 | } else { | 693 | } else { |
688 | atomic_set(&clp->cl_cb_conn.cb_set, 0); | 694 | atomic_set(&clp->cl_cb_set, 0); |
689 | warn_no_callback_path(clp, task->tk_status); | 695 | warn_no_callback_path(clp, task->tk_status); |
690 | } | 696 | } |
691 | done: | ||
692 | kfree(task->tk_msg.rpc_argp); | ||
693 | } | 697 | } |
694 | 698 | ||
695 | static void nfsd4_cb_recall_release(void *calldata) | 699 | static void nfsd4_cb_recall_release(void *calldata) |
696 | { | 700 | { |
697 | struct nfs4_delegation *dp = calldata; | 701 | struct nfs4_delegation *dp = calldata; |
698 | struct nfs4_client *clp = dp->dl_client; | ||
699 | 702 | ||
700 | nfs4_put_delegation(dp); | 703 | nfs4_put_delegation(dp); |
701 | put_nfs4_client(clp); | ||
702 | } | 704 | } |
703 | 705 | ||
704 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { | 706 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { |
@@ -707,33 +709,75 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = { | |||
707 | .rpc_release = nfsd4_cb_recall_release, | 709 | .rpc_release = nfsd4_cb_recall_release, |
708 | }; | 710 | }; |
709 | 711 | ||
712 | static struct workqueue_struct *callback_wq; | ||
713 | |||
714 | int nfsd4_create_callback_queue(void) | ||
715 | { | ||
716 | callback_wq = create_singlethread_workqueue("nfsd4_callbacks"); | ||
717 | if (!callback_wq) | ||
718 | return -ENOMEM; | ||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | void nfsd4_destroy_callback_queue(void) | ||
723 | { | ||
724 | destroy_workqueue(callback_wq); | ||
725 | } | ||
726 | |||
727 | /* must be called under the state lock */ | ||
728 | void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new) | ||
729 | { | ||
730 | struct rpc_clnt *old = clp->cl_cb_client; | ||
731 | |||
732 | clp->cl_cb_client = new; | ||
733 | /* | ||
734 | * After this, any work that saw the old value of cl_cb_client will | ||
735 | * be gone: | ||
736 | */ | ||
737 | flush_workqueue(callback_wq); | ||
738 | /* So we can safely shut it down: */ | ||
739 | if (old) | ||
740 | rpc_shutdown_client(old); | ||
741 | } | ||
742 | |||
710 | /* | 743 | /* |
711 | * called with dp->dl_count inc'ed. | 744 | * called with dp->dl_count inc'ed. |
712 | */ | 745 | */ |
713 | void | 746 | static void _nfsd4_cb_recall(struct nfs4_delegation *dp) |
714 | nfsd4_cb_recall(struct nfs4_delegation *dp) | ||
715 | { | 747 | { |
716 | struct nfs4_client *clp = dp->dl_client; | 748 | struct nfs4_client *clp = dp->dl_client; |
717 | struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; | 749 | struct rpc_clnt *clnt = clp->cl_cb_client; |
718 | struct nfs4_rpc_args *args; | 750 | struct nfs4_rpc_args *args = &dp->dl_recall.cb_args; |
719 | struct rpc_message msg = { | 751 | struct rpc_message msg = { |
720 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], | 752 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], |
721 | .rpc_cred = callback_cred | 753 | .rpc_cred = callback_cred |
722 | }; | 754 | }; |
723 | int status = -ENOMEM; | 755 | int status; |
756 | |||
757 | if (clnt == NULL) | ||
758 | return; /* Client is shutting down; give up. */ | ||
724 | 759 | ||
725 | args = kzalloc(sizeof(*args), GFP_KERNEL); | ||
726 | if (!args) | ||
727 | goto out; | ||
728 | args->args_op = dp; | 760 | args->args_op = dp; |
729 | msg.rpc_argp = args; | 761 | msg.rpc_argp = args; |
730 | dp->dl_retries = 1; | 762 | dp->dl_retries = 1; |
731 | status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT, | 763 | status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT, |
732 | &nfsd4_cb_recall_ops, dp); | 764 | &nfsd4_cb_recall_ops, dp); |
733 | out: | 765 | if (status) |
734 | if (status) { | ||
735 | kfree(args); | ||
736 | put_nfs4_client(clp); | ||
737 | nfs4_put_delegation(dp); | 766 | nfs4_put_delegation(dp); |
738 | } | 767 | } |
768 | |||
769 | void nfsd4_do_callback_rpc(struct work_struct *w) | ||
770 | { | ||
771 | /* XXX: for now, just send off delegation recall. */ | ||
772 | /* In future, generalize to handle any sort of callback. */ | ||
773 | struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work); | ||
774 | struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall); | ||
775 | |||
776 | _nfsd4_cb_recall(dp); | ||
777 | } | ||
778 | |||
779 | |||
780 | void nfsd4_cb_recall(struct nfs4_delegation *dp) | ||
781 | { | ||
782 | queue_work(callback_wq, &dp->dl_recall.cb_work); | ||
739 | } | 783 | } |