diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-01-10 16:44:41 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2011-01-11 15:04:11 -0500 |
commit | 5ce8ba25d657a71d6d8cdb05a2b90c5ae7debfda (patch) | |
tree | 831d815e91e23de373fe7fdc7a54b3eb098067f5 | |
parent | 3ff3600e7eab16301e824293e8f49b9990bd4641 (diff) |
nfsd4: allow restarting callbacks
If we lose the backchannel and then the client repairs the problem,
resend any callbacks.
We use a new cb_done flag to track whether there is still work to be
done for the callback or whether it can be destroyed with the rpc.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/nfsd/nfs4callback.c | 34 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 1 | ||||
-rw-r--r-- | fs/nfsd/state.h | 3 |
3 files changed, 32 insertions, 6 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 69955e98e086..f1d9dd45553a 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -639,6 +639,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) | |||
639 | if (!nfsd41_cb_get_slot(clp, task)) | 639 | if (!nfsd41_cb_get_slot(clp, task)) |
640 | return; | 640 | return; |
641 | } | 641 | } |
642 | cb->cb_done = false; | ||
643 | spin_lock(&clp->cl_lock); | ||
644 | list_add(&cb->cb_per_client, &clp->cl_callbacks); | ||
645 | spin_unlock(&clp->cl_lock); | ||
642 | rpc_call_start(task); | 646 | rpc_call_start(task); |
643 | } | 647 | } |
644 | 648 | ||
@@ -681,8 +685,11 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
681 | return; | 685 | return; |
682 | } | 686 | } |
683 | 687 | ||
688 | if (cb->cb_done) | ||
689 | return; | ||
684 | switch (task->tk_status) { | 690 | switch (task->tk_status) { |
685 | case 0: | 691 | case 0: |
692 | cb->cb_done = true; | ||
686 | return; | 693 | return; |
687 | case -EBADHANDLE: | 694 | case -EBADHANDLE: |
688 | case -NFS4ERR_BAD_STATEID: | 695 | case -NFS4ERR_BAD_STATEID: |
@@ -695,7 +702,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
695 | if (current_rpc_client != task->tk_client) { | 702 | if (current_rpc_client != task->tk_client) { |
696 | /* queue a callback on the new connection: */ | 703 | /* queue a callback on the new connection: */ |
697 | atomic_inc(&dp->dl_count); | 704 | atomic_inc(&dp->dl_count); |
698 | nfsd4_cb_recall(dp); | 705 | run_nfsd4_cb(&dp->dl_recall); |
699 | return; | 706 | return; |
700 | } | 707 | } |
701 | } | 708 | } |
@@ -704,16 +711,23 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
704 | task->tk_status = 0; | 711 | task->tk_status = 0; |
705 | rpc_restart_call_prepare(task); | 712 | rpc_restart_call_prepare(task); |
706 | return; | 713 | return; |
707 | } else | 714 | } |
708 | nfsd4_mark_cb_down(clp, task->tk_status); | 715 | nfsd4_mark_cb_down(clp, task->tk_status); |
716 | cb->cb_done = true; | ||
709 | } | 717 | } |
710 | 718 | ||
711 | static void nfsd4_cb_recall_release(void *calldata) | 719 | static void nfsd4_cb_recall_release(void *calldata) |
712 | { | 720 | { |
713 | struct nfsd4_callback *cb = calldata; | 721 | struct nfsd4_callback *cb = calldata; |
722 | struct nfs4_client *clp = cb->cb_clp; | ||
714 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); | 723 | struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); |
715 | 724 | ||
716 | nfs4_put_delegation(dp); | 725 | if (cb->cb_done) { |
726 | spin_lock(&clp->cl_lock); | ||
727 | list_del(&cb->cb_per_client); | ||
728 | spin_unlock(&clp->cl_lock); | ||
729 | nfs4_put_delegation(dp); | ||
730 | } | ||
717 | } | 731 | } |
718 | 732 | ||
719 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { | 733 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { |
@@ -808,8 +822,13 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
808 | spin_unlock(&clp->cl_lock); | 822 | spin_unlock(&clp->cl_lock); |
809 | 823 | ||
810 | err = setup_callback_client(clp, &conn, ses); | 824 | err = setup_callback_client(clp, &conn, ses); |
811 | if (err) | 825 | if (err) { |
812 | warn_no_callback_path(clp, err); | 826 | warn_no_callback_path(clp, err); |
827 | return; | ||
828 | } | ||
829 | /* Yay, the callback channel's back! Restart any callbacks: */ | ||
830 | list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client) | ||
831 | run_nfsd4_cb(cb); | ||
813 | } | 832 | } |
814 | 833 | ||
815 | void nfsd4_do_callback_rpc(struct work_struct *w) | 834 | void nfsd4_do_callback_rpc(struct work_struct *w) |
@@ -834,10 +853,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w) | |||
834 | void nfsd4_cb_recall(struct nfs4_delegation *dp) | 853 | void nfsd4_cb_recall(struct nfs4_delegation *dp) |
835 | { | 854 | { |
836 | struct nfsd4_callback *cb = &dp->dl_recall; | 855 | struct nfsd4_callback *cb = &dp->dl_recall; |
856 | struct nfs4_client *clp = dp->dl_client; | ||
837 | 857 | ||
838 | dp->dl_retries = 1; | 858 | dp->dl_retries = 1; |
839 | cb->cb_op = dp; | 859 | cb->cb_op = dp; |
840 | cb->cb_clp = dp->dl_client; | 860 | cb->cb_clp = clp; |
841 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; | 861 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; |
842 | cb->cb_msg.rpc_argp = cb; | 862 | cb->cb_msg.rpc_argp = cb; |
843 | cb->cb_msg.rpc_resp = cb; | 863 | cb->cb_msg.rpc_resp = cb; |
@@ -846,5 +866,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) | |||
846 | cb->cb_ops = &nfsd4_cb_recall_ops; | 866 | cb->cb_ops = &nfsd4_cb_recall_ops; |
847 | dp->dl_retries = 1; | 867 | dp->dl_retries = 1; |
848 | 868 | ||
869 | cb->cb_done = true; | ||
870 | |||
849 | run_nfsd4_cb(&dp->dl_recall); | 871 | run_nfsd4_cb(&dp->dl_recall); |
850 | } | 872 | } |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 408957cf6016..6e1f9aadd439 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1077,6 +1077,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | |||
1077 | INIT_LIST_HEAD(&clp->cl_openowners); | 1077 | INIT_LIST_HEAD(&clp->cl_openowners); |
1078 | INIT_LIST_HEAD(&clp->cl_delegations); | 1078 | INIT_LIST_HEAD(&clp->cl_delegations); |
1079 | INIT_LIST_HEAD(&clp->cl_lru); | 1079 | INIT_LIST_HEAD(&clp->cl_lru); |
1080 | INIT_LIST_HEAD(&clp->cl_callbacks); | ||
1080 | spin_lock_init(&clp->cl_lock); | 1081 | spin_lock_init(&clp->cl_lock); |
1081 | INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); | 1082 | INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); |
1082 | clp->cl_time = get_seconds(); | 1083 | clp->cl_time = get_seconds(); |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 4e5bdfd9169c..3074656ba7bf 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -68,10 +68,12 @@ typedef struct { | |||
68 | struct nfsd4_callback { | 68 | struct nfsd4_callback { |
69 | void *cb_op; | 69 | void *cb_op; |
70 | struct nfs4_client *cb_clp; | 70 | struct nfs4_client *cb_clp; |
71 | struct list_head cb_per_client; | ||
71 | u32 cb_minorversion; | 72 | u32 cb_minorversion; |
72 | struct rpc_message cb_msg; | 73 | struct rpc_message cb_msg; |
73 | const struct rpc_call_ops *cb_ops; | 74 | const struct rpc_call_ops *cb_ops; |
74 | struct work_struct cb_work; | 75 | struct work_struct cb_work; |
76 | bool cb_done; | ||
75 | }; | 77 | }; |
76 | 78 | ||
77 | struct nfs4_delegation { | 79 | struct nfs4_delegation { |
@@ -248,6 +250,7 @@ struct nfs4_client { | |||
248 | int cl_cb_state; | 250 | int cl_cb_state; |
249 | struct nfsd4_callback cl_cb_null; | 251 | struct nfsd4_callback cl_cb_null; |
250 | struct nfsd4_session *cl_cb_session; | 252 | struct nfsd4_session *cl_cb_session; |
253 | struct list_head cl_callbacks; /* list of in-progress callbacks */ | ||
251 | 254 | ||
252 | /* for all client information that callback code might need: */ | 255 | /* for all client information that callback code might need: */ |
253 | spinlock_t cl_lock; | 256 | spinlock_t cl_lock; |