diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-14 16:17:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-14 16:17:26 -0500 |
commit | 18bce371ae09af6c20ee62c1092a4d1d0e84dd49 (patch) | |
tree | f3467fafd8e49392e3f6efef7b88a7b4dd3b7b06 /fs/nfsd/nfs4callback.c | |
parent | ec08bdb148767f1193f5f3028749ed865ac27181 (diff) | |
parent | a8f2800b4f7b76cecb7209cb6a7d2b14904fc711 (diff) |
Merge branch 'for-2.6.38' of git://linux-nfs.org/~bfields/linux
* 'for-2.6.38' of git://linux-nfs.org/~bfields/linux: (62 commits)
nfsd4: fix callback restarting
nfsd: break lease on unlink, link, and rename
nfsd4: break lease on nfsd setattr
nfsd: don't support msnfs export option
nfsd4: initialize cb_per_client
nfsd4: allow restarting callbacks
nfsd4: simplify nfsd4_cb_prepare
nfsd4: give out delegations more quickly in 4.1 case
nfsd4: add helper function to run callbacks
nfsd4: make sure sequence flags are set after destroy_session
nfsd4: re-probe callback on connection loss
nfsd4: set sequence flag when backchannel is down
nfsd4: keep finer-grained callback status
rpc: allow xprt_class->setup to return a preexisting xprt
rpc: keep backchannel xprt as long as server connection
rpc: move sk_bc_xprt to svc_xprt
nfsd4: allow backchannel recovery
nfsd4: support BIND_CONN_TO_SESSION
nfsd4: modify session list under cl_lock
Documentation: fl_mylease no longer exists
...
Fix up conflicts in fs/nfsd/vfs.c with the vfs-scale work. The
vfs-scale work touched some msnfs cases, and this merge removes support
for that entirely, so the conflict was trivial to resolve.
Diffstat (limited to 'fs/nfsd/nfs4callback.c')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 151 |
1 files changed, 99 insertions, 52 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 21a63da305ff..3be975e18919 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -628,10 +628,8 @@ static int max_cb_time(void) | |||
628 | return max(nfsd4_lease/10, (time_t)1) * HZ; | 628 | return max(nfsd4_lease/10, (time_t)1) * HZ; |
629 | } | 629 | } |
630 | 630 | ||
631 | /* Reference counting, callback cleanup, etc., all look racy as heck. | ||
632 | * And why is cl_cb_set an atomic? */ | ||
633 | 631 | ||
634 | int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | 632 | static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) |
635 | { | 633 | { |
636 | struct rpc_timeout timeparms = { | 634 | struct rpc_timeout timeparms = { |
637 | .to_initval = max_cb_time(), | 635 | .to_initval = max_cb_time(), |
@@ -641,6 +639,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | |||
641 | .net = &init_net, | 639 | .net = &init_net, |
642 | .address = (struct sockaddr *) &conn->cb_addr, | 640 | .address = (struct sockaddr *) &conn->cb_addr, |
643 | .addrsize = conn->cb_addrlen, | 641 | .addrsize = conn->cb_addrlen, |
642 | .saddress = (struct sockaddr *) &conn->cb_saddr, | ||
644 | .timeout = &timeparms, | 643 | .timeout = &timeparms, |
645 | .program = &cb_program, | 644 | .program = &cb_program, |
646 | .version = 0, | 645 | .version = 0, |
@@ -657,6 +656,10 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | |||
657 | args.protocol = XPRT_TRANSPORT_TCP; | 656 | args.protocol = XPRT_TRANSPORT_TCP; |
658 | clp->cl_cb_ident = conn->cb_ident; | 657 | clp->cl_cb_ident = conn->cb_ident; |
659 | } else { | 658 | } else { |
659 | if (!conn->cb_xprt) | ||
660 | return -EINVAL; | ||
661 | clp->cl_cb_conn.cb_xprt = conn->cb_xprt; | ||
662 | clp->cl_cb_session = ses; | ||
660 | args.bc_xprt = conn->cb_xprt; | 663 | args.bc_xprt = conn->cb_xprt; |
661 | args.prognumber = clp->cl_cb_session->se_cb_prog; | 664 | args.prognumber = clp->cl_cb_session->se_cb_prog; |
662 | args.protocol = XPRT_TRANSPORT_BC_TCP; | 665 | args.protocol = XPRT_TRANSPORT_BC_TCP; |
@@ -679,14 +682,20 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason) | |||
679 | (int)clp->cl_name.len, clp->cl_name.data, reason); | 682 | (int)clp->cl_name.len, clp->cl_name.data, reason); |
680 | } | 683 | } |
681 | 684 | ||
685 | static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason) | ||
686 | { | ||
687 | clp->cl_cb_state = NFSD4_CB_DOWN; | ||
688 | warn_no_callback_path(clp, reason); | ||
689 | } | ||
690 | |||
682 | static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) | 691 | static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) |
683 | { | 692 | { |
684 | struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null); | 693 | struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null); |
685 | 694 | ||
686 | if (task->tk_status) | 695 | if (task->tk_status) |
687 | warn_no_callback_path(clp, task->tk_status); | 696 | nfsd4_mark_cb_down(clp, task->tk_status); |
688 | else | 697 | else |
689 | atomic_set(&clp->cl_cb_set, 1); | 698 | clp->cl_cb_state = NFSD4_CB_UP; |
690 | } | 699 | } |
691 | 700 | ||
692 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { | 701 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { |
@@ -709,6 +718,11 @@ int set_callback_cred(void) | |||
709 | 718 | ||
710 | static struct workqueue_struct *callback_wq; | 719 | static struct workqueue_struct *callback_wq; |
711 | 720 | ||
721 | static void run_nfsd4_cb(struct nfsd4_callback *cb) | ||
722 | { | ||
723 | queue_work(callback_wq, &cb->cb_work); | ||
724 | } | ||
725 | |||
712 | static void do_probe_callback(struct nfs4_client *clp) | 726 | static void do_probe_callback(struct nfs4_client *clp) |
713 | { | 727 | { |
714 | struct nfsd4_callback *cb = &clp->cl_cb_null; | 728 | struct nfsd4_callback *cb = &clp->cl_cb_null; |
@@ -723,7 +737,7 @@ static void do_probe_callback(struct nfs4_client *clp) | |||
723 | 737 | ||
724 | cb->cb_ops = &nfsd4_cb_probe_ops; | 738 | cb->cb_ops = &nfsd4_cb_probe_ops; |
725 | 739 | ||
726 | queue_work(callback_wq, &cb->cb_work); | 740 | run_nfsd4_cb(cb); |
727 | } | 741 | } |
728 | 742 | ||
729 | /* | 743 | /* |
@@ -732,14 +746,21 @@ static void do_probe_callback(struct nfs4_client *clp) | |||
732 | */ | 746 | */ |
733 | void nfsd4_probe_callback(struct nfs4_client *clp) | 747 | void nfsd4_probe_callback(struct nfs4_client *clp) |
734 | { | 748 | { |
749 | /* XXX: atomicity? Also, should we be using cl_cb_flags? */ | ||
750 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; | ||
735 | set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); | 751 | set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); |
736 | do_probe_callback(clp); | 752 | do_probe_callback(clp); |
737 | } | 753 | } |
738 | 754 | ||
739 | void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | 755 | void nfsd4_probe_callback_sync(struct nfs4_client *clp) |
740 | { | 756 | { |
741 | BUG_ON(atomic_read(&clp->cl_cb_set)); | 757 | nfsd4_probe_callback(clp); |
758 | flush_workqueue(callback_wq); | ||
759 | } | ||
742 | 760 | ||
761 | void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | ||
762 | { | ||
763 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; | ||
743 | spin_lock(&clp->cl_lock); | 764 | spin_lock(&clp->cl_lock); |
744 | memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); | 765 | memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); |
745 | spin_unlock(&clp->cl_lock); | 766 | spin_unlock(&clp->cl_lock); |
@@ -750,24 +771,14 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) | |||
750 | * If the slot is available, then mark it busy. Otherwise, set the | 771 | * If the slot is available, then mark it busy. Otherwise, set the |
751 | * thread for sleeping on the callback RPC wait queue. | 772 | * thread for sleeping on the callback RPC wait queue. |
752 | */ | 773 | */ |
753 | static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, | 774 | static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task) |
754 | struct rpc_task *task) | ||
755 | { | 775 | { |
756 | u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data; | ||
757 | int status = 0; | ||
758 | |||
759 | dprintk("%s: %u:%u:%u:%u\n", __func__, | ||
760 | ptr[0], ptr[1], ptr[2], ptr[3]); | ||
761 | |||
762 | if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { | 776 | if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { |
763 | rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); | 777 | rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); |
764 | dprintk("%s slot is busy\n", __func__); | 778 | dprintk("%s slot is busy\n", __func__); |
765 | status = -EAGAIN; | 779 | return false; |
766 | goto out; | ||
767 | } | 780 | } |
768 | out: | 781 | return true; |
769 | dprintk("%s status=%d\n", __func__, status); | ||
770 | return status; | ||
771 | } | 782 | } |
772 | 783 | ||
773 | /* | 784 | /* |
@@ -780,20 +791,19 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) | |||
780 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); | 791 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); |
781 | struct nfs4_client *clp = dp->dl_client; | 792 | struct nfs4_client *clp = dp->dl_client; |
782 | u32 minorversion = clp->cl_minorversion; | 793 | u32 minorversion = clp->cl_minorversion; |
783 | int status = 0; | ||
784 | 794 | ||
785 | cb->cb_minorversion = minorversion; | 795 | cb->cb_minorversion = minorversion; |
786 | if (minorversion) { | 796 | if (minorversion) { |
787 | status = nfsd41_cb_setup_sequence(clp, task); | 797 | if (!nfsd41_cb_get_slot(clp, task)) |
788 | if (status) { | ||
789 | if (status != -EAGAIN) { | ||
790 | /* terminate rpc task */ | ||
791 | task->tk_status = status; | ||
792 | task->tk_action = NULL; | ||
793 | } | ||
794 | return; | 798 | return; |
795 | } | ||
796 | } | 799 | } |
800 | spin_lock(&clp->cl_lock); | ||
801 | if (list_empty(&cb->cb_per_client)) { | ||
802 | /* This is the first call, not a restart */ | ||
803 | cb->cb_done = false; | ||
804 | list_add(&cb->cb_per_client, &clp->cl_callbacks); | ||
805 | } | ||
806 | spin_unlock(&clp->cl_lock); | ||
797 | rpc_call_start(task); | 807 | rpc_call_start(task); |
798 | } | 808 | } |
799 | 809 | ||
@@ -829,15 +839,18 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
829 | 839 | ||
830 | nfsd4_cb_done(task, calldata); | 840 | nfsd4_cb_done(task, calldata); |
831 | 841 | ||
832 | if (current_rpc_client == NULL) { | 842 | if (current_rpc_client != task->tk_client) { |
833 | /* We're shutting down; give up. */ | 843 | /* We're shutting down or changing cl_cb_client; leave |
834 | /* XXX: err, or is it ok just to fall through | 844 | * it to nfsd4_process_cb_update to restart the call if |
835 | * and rpc_restart_call? */ | 845 | * necessary. */ |
836 | return; | 846 | return; |
837 | } | 847 | } |
838 | 848 | ||
849 | if (cb->cb_done) | ||
850 | return; | ||
839 | switch (task->tk_status) { | 851 | switch (task->tk_status) { |
840 | case 0: | 852 | case 0: |
853 | cb->cb_done = true; | ||
841 | return; | 854 | return; |
842 | case -EBADHANDLE: | 855 | case -EBADHANDLE: |
843 | case -NFS4ERR_BAD_STATEID: | 856 | case -NFS4ERR_BAD_STATEID: |
@@ -846,32 +859,30 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
846 | break; | 859 | break; |
847 | default: | 860 | default: |
848 | /* Network partition? */ | 861 | /* Network partition? */ |
849 | atomic_set(&clp->cl_cb_set, 0); | 862 | nfsd4_mark_cb_down(clp, task->tk_status); |
850 | warn_no_callback_path(clp, task->tk_status); | ||
851 | if (current_rpc_client != task->tk_client) { | ||
852 | /* queue a callback on the new connection: */ | ||
853 | atomic_inc(&dp->dl_count); | ||
854 | nfsd4_cb_recall(dp); | ||
855 | return; | ||
856 | } | ||
857 | } | 863 | } |
858 | if (dp->dl_retries--) { | 864 | if (dp->dl_retries--) { |
859 | rpc_delay(task, 2*HZ); | 865 | rpc_delay(task, 2*HZ); |
860 | task->tk_status = 0; | 866 | task->tk_status = 0; |
861 | rpc_restart_call_prepare(task); | 867 | rpc_restart_call_prepare(task); |
862 | return; | 868 | return; |
863 | } else { | ||
864 | atomic_set(&clp->cl_cb_set, 0); | ||
865 | warn_no_callback_path(clp, task->tk_status); | ||
866 | } | 869 | } |
870 | nfsd4_mark_cb_down(clp, task->tk_status); | ||
871 | cb->cb_done = true; | ||
867 | } | 872 | } |
868 | 873 | ||
869 | static void nfsd4_cb_recall_release(void *calldata) | 874 | static void nfsd4_cb_recall_release(void *calldata) |
870 | { | 875 | { |
871 | struct nfsd4_callback *cb = calldata; | 876 | struct nfsd4_callback *cb = calldata; |
877 | struct nfs4_client *clp = cb->cb_clp; | ||
872 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); | 878 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); |
873 | 879 | ||
874 | nfs4_put_delegation(dp); | 880 | if (cb->cb_done) { |
881 | spin_lock(&clp->cl_lock); | ||
882 | list_del(&cb->cb_per_client); | ||
883 | spin_unlock(&clp->cl_lock); | ||
884 | nfs4_put_delegation(dp); | ||
885 | } | ||
875 | } | 886 | } |
876 | 887 | ||
877 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { | 888 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { |
@@ -906,16 +917,33 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp) | |||
906 | flush_workqueue(callback_wq); | 917 | flush_workqueue(callback_wq); |
907 | } | 918 | } |
908 | 919 | ||
909 | void nfsd4_release_cb(struct nfsd4_callback *cb) | 920 | static void nfsd4_release_cb(struct nfsd4_callback *cb) |
910 | { | 921 | { |
911 | if (cb->cb_ops->rpc_release) | 922 | if (cb->cb_ops->rpc_release) |
912 | cb->cb_ops->rpc_release(cb); | 923 | cb->cb_ops->rpc_release(cb); |
913 | } | 924 | } |
914 | 925 | ||
915 | void nfsd4_process_cb_update(struct nfsd4_callback *cb) | 926 | /* requires cl_lock: */ |
927 | static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp) | ||
928 | { | ||
929 | struct nfsd4_session *s; | ||
930 | struct nfsd4_conn *c; | ||
931 | |||
932 | list_for_each_entry(s, &clp->cl_sessions, se_perclnt) { | ||
933 | list_for_each_entry(c, &s->se_conns, cn_persession) { | ||
934 | if (c->cn_flags & NFS4_CDFC4_BACK) | ||
935 | return c; | ||
936 | } | ||
937 | } | ||
938 | return NULL; | ||
939 | } | ||
940 | |||
941 | static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | ||
916 | { | 942 | { |
917 | struct nfs4_cb_conn conn; | 943 | struct nfs4_cb_conn conn; |
918 | struct nfs4_client *clp = cb->cb_clp; | 944 | struct nfs4_client *clp = cb->cb_clp; |
945 | struct nfsd4_session *ses = NULL; | ||
946 | struct nfsd4_conn *c; | ||
919 | int err; | 947 | int err; |
920 | 948 | ||
921 | /* | 949 | /* |
@@ -926,6 +954,10 @@ void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
926 | rpc_shutdown_client(clp->cl_cb_client); | 954 | rpc_shutdown_client(clp->cl_cb_client); |
927 | clp->cl_cb_client = NULL; | 955 | clp->cl_cb_client = NULL; |
928 | } | 956 | } |
957 | if (clp->cl_cb_conn.cb_xprt) { | ||
958 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); | ||
959 | clp->cl_cb_conn.cb_xprt = NULL; | ||
960 | } | ||
929 | if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags)) | 961 | if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags)) |
930 | return; | 962 | return; |
931 | spin_lock(&clp->cl_lock); | 963 | spin_lock(&clp->cl_lock); |
@@ -936,11 +968,22 @@ void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
936 | BUG_ON(!clp->cl_cb_flags); | 968 | BUG_ON(!clp->cl_cb_flags); |
937 | clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); | 969 | clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); |
938 | memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn)); | 970 | memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn)); |
971 | c = __nfsd4_find_backchannel(clp); | ||
972 | if (c) { | ||
973 | svc_xprt_get(c->cn_xprt); | ||
974 | conn.cb_xprt = c->cn_xprt; | ||
975 | ses = c->cn_session; | ||
976 | } | ||
939 | spin_unlock(&clp->cl_lock); | 977 | spin_unlock(&clp->cl_lock); |
940 | 978 | ||
941 | err = setup_callback_client(clp, &conn); | 979 | err = setup_callback_client(clp, &conn, ses); |
942 | if (err) | 980 | if (err) { |
943 | warn_no_callback_path(clp, err); | 981 | warn_no_callback_path(clp, err); |
982 | return; | ||
983 | } | ||
984 | /* Yay, the callback channel's back! Restart any callbacks: */ | ||
985 | list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client) | ||
986 | run_nfsd4_cb(cb); | ||
944 | } | 987 | } |
945 | 988 | ||
946 | void nfsd4_do_callback_rpc(struct work_struct *w) | 989 | void nfsd4_do_callback_rpc(struct work_struct *w) |
@@ -965,10 +1008,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w) | |||
965 | void nfsd4_cb_recall(struct nfs4_delegation *dp) | 1008 | void nfsd4_cb_recall(struct nfs4_delegation *dp) |
966 | { | 1009 | { |
967 | struct nfsd4_callback *cb = &dp->dl_recall; | 1010 | struct nfsd4_callback *cb = &dp->dl_recall; |
1011 | struct nfs4_client *clp = dp->dl_client; | ||
968 | 1012 | ||
969 | dp->dl_retries = 1; | 1013 | dp->dl_retries = 1; |
970 | cb->cb_op = dp; | 1014 | cb->cb_op = dp; |
971 | cb->cb_clp = dp->dl_client; | 1015 | cb->cb_clp = clp; |
972 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; | 1016 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; |
973 | cb->cb_msg.rpc_argp = cb; | 1017 | cb->cb_msg.rpc_argp = cb; |
974 | cb->cb_msg.rpc_resp = cb; | 1018 | cb->cb_msg.rpc_resp = cb; |
@@ -977,5 +1021,8 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) | |||
977 | cb->cb_ops = &nfsd4_cb_recall_ops; | 1021 | cb->cb_ops = &nfsd4_cb_recall_ops; |
978 | dp->dl_retries = 1; | 1022 | dp->dl_retries = 1; |
979 | 1023 | ||
980 | queue_work(callback_wq, &dp->dl_recall.cb_work); | 1024 | INIT_LIST_HEAD(&cb->cb_per_client); |
1025 | cb->cb_done = true; | ||
1026 | |||
1027 | run_nfsd4_cb(&dp->dl_recall); | ||
981 | } | 1028 | } |