diff options
author | Christoph Hellwig <hch@lst.de> | 2015-04-30 05:49:24 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2015-05-04 12:02:41 -0400 |
commit | cba5f62b1830c1919b47544789bc993e6e617dc6 (patch) | |
tree | 6f85f8982fe083ea576b7938799e56fe4d675bec /fs/nfsd | |
parent | ef2a1b3e1067195f1d6b89d8329454775c87f033 (diff) |
nfsd: fix callback restarts
Checking the rpc_client pointer is not a reliable way to detect
backchannel changes: cl_cb_client is changed only after shutting down
the rpc client, so the condition cl_cb_client = tk_client will always be
true.
Check the RPC_TASK_KILLED flag instead, and rewrite the code to avoid
the buggy cl_callbacks list and fix the lifetime rules due to double
calls of the ->prepare callback operations method for this retry case.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 52 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 1 | ||||
-rw-r--r-- | fs/nfsd/state.h | 4 |
3 files changed, 24 insertions, 33 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index cd58b7cd3f1a..4c993aaf1e6e 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -879,13 +879,6 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) | |||
879 | if (!nfsd41_cb_get_slot(clp, task)) | 879 | if (!nfsd41_cb_get_slot(clp, task)) |
880 | return; | 880 | return; |
881 | } | 881 | } |
882 | spin_lock(&clp->cl_lock); | ||
883 | if (list_empty(&cb->cb_per_client)) { | ||
884 | /* This is the first call, not a restart */ | ||
885 | cb->cb_done = false; | ||
886 | list_add(&cb->cb_per_client, &clp->cl_callbacks); | ||
887 | } | ||
888 | spin_unlock(&clp->cl_lock); | ||
889 | rpc_call_start(task); | 882 | rpc_call_start(task); |
890 | } | 883 | } |
891 | 884 | ||
@@ -907,16 +900,21 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
907 | clp->cl_cb_session->se_cb_seq_nr); | 900 | clp->cl_cb_session->se_cb_seq_nr); |
908 | } | 901 | } |
909 | 902 | ||
910 | if (clp->cl_cb_client != task->tk_client) { | 903 | /* |
911 | /* We're shutting down or changing cl_cb_client; leave | 904 | * If the backchannel connection was shut down while this |
912 | * it to nfsd4_process_cb_update to restart the call if | 905 | * task was queued, we need to resubmit it after setting up |
913 | * necessary. */ | 906 | * a new backchannel connection. |
907 | * | ||
908 | * Note that if we lost our callback connection permanently | ||
909 | * the submission code will error out, so we don't need to | ||
910 | * handle that case here. | ||
911 | */ | ||
912 | if (task->tk_flags & RPC_TASK_KILLED) { | ||
913 | task->tk_status = 0; | ||
914 | cb->cb_need_restart = true; | ||
914 | return; | 915 | return; |
915 | } | 916 | } |
916 | 917 | ||
917 | if (cb->cb_done) | ||
918 | return; | ||
919 | |||
920 | if (cb->cb_status) { | 918 | if (cb->cb_status) { |
921 | WARN_ON_ONCE(task->tk_status); | 919 | WARN_ON_ONCE(task->tk_status); |
922 | task->tk_status = cb->cb_status; | 920 | task->tk_status = cb->cb_status; |
@@ -936,21 +934,17 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
936 | default: | 934 | default: |
937 | BUG(); | 935 | BUG(); |
938 | } | 936 | } |
939 | cb->cb_done = true; | ||
940 | } | 937 | } |
941 | 938 | ||
942 | static void nfsd4_cb_release(void *calldata) | 939 | static void nfsd4_cb_release(void *calldata) |
943 | { | 940 | { |
944 | struct nfsd4_callback *cb = calldata; | 941 | struct nfsd4_callback *cb = calldata; |
945 | struct nfs4_client *clp = cb->cb_clp; | ||
946 | |||
947 | if (cb->cb_done) { | ||
948 | spin_lock(&clp->cl_lock); | ||
949 | list_del(&cb->cb_per_client); | ||
950 | spin_unlock(&clp->cl_lock); | ||
951 | 942 | ||
943 | if (cb->cb_need_restart) | ||
944 | nfsd4_run_cb(cb); | ||
945 | else | ||
952 | cb->cb_ops->release(cb); | 946 | cb->cb_ops->release(cb); |
953 | } | 947 | |
954 | } | 948 | } |
955 | 949 | ||
956 | static const struct rpc_call_ops nfsd4_cb_ops = { | 950 | static const struct rpc_call_ops nfsd4_cb_ops = { |
@@ -1045,9 +1039,6 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
1045 | nfsd4_mark_cb_down(clp, err); | 1039 | nfsd4_mark_cb_down(clp, err); |
1046 | return; | 1040 | return; |
1047 | } | 1041 | } |
1048 | /* Yay, the callback channel's back! Restart any callbacks: */ | ||
1049 | list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client) | ||
1050 | queue_work(callback_wq, &cb->cb_work); | ||
1051 | } | 1042 | } |
1052 | 1043 | ||
1053 | static void | 1044 | static void |
@@ -1058,8 +1049,12 @@ nfsd4_run_cb_work(struct work_struct *work) | |||
1058 | struct nfs4_client *clp = cb->cb_clp; | 1049 | struct nfs4_client *clp = cb->cb_clp; |
1059 | struct rpc_clnt *clnt; | 1050 | struct rpc_clnt *clnt; |
1060 | 1051 | ||
1061 | if (cb->cb_ops && cb->cb_ops->prepare) | 1052 | if (cb->cb_need_restart) { |
1062 | cb->cb_ops->prepare(cb); | 1053 | cb->cb_need_restart = false; |
1054 | } else { | ||
1055 | if (cb->cb_ops && cb->cb_ops->prepare) | ||
1056 | cb->cb_ops->prepare(cb); | ||
1057 | } | ||
1063 | 1058 | ||
1064 | if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) | 1059 | if (clp->cl_flags & NFSD4_CLIENT_CB_FLAG_MASK) |
1065 | nfsd4_process_cb_update(cb); | 1060 | nfsd4_process_cb_update(cb); |
@@ -1085,9 +1080,8 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, | |||
1085 | cb->cb_msg.rpc_resp = cb; | 1080 | cb->cb_msg.rpc_resp = cb; |
1086 | cb->cb_ops = ops; | 1081 | cb->cb_ops = ops; |
1087 | INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); | 1082 | INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); |
1088 | INIT_LIST_HEAD(&cb->cb_per_client); | ||
1089 | cb->cb_status = 0; | 1083 | cb->cb_status = 0; |
1090 | cb->cb_done = true; | 1084 | cb->cb_need_restart = false; |
1091 | } | 1085 | } |
1092 | 1086 | ||
1093 | void nfsd4_run_cb(struct nfsd4_callback *cb) | 1087 | void nfsd4_run_cb(struct nfsd4_callback *cb) |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 66067a291267..039f9c8a95e8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1626,7 +1626,6 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
1626 | INIT_LIST_HEAD(&clp->cl_openowners); | 1626 | INIT_LIST_HEAD(&clp->cl_openowners); |
1627 | INIT_LIST_HEAD(&clp->cl_delegations); | 1627 | INIT_LIST_HEAD(&clp->cl_delegations); |
1628 | INIT_LIST_HEAD(&clp->cl_lru); | 1628 | INIT_LIST_HEAD(&clp->cl_lru); |
1629 | INIT_LIST_HEAD(&clp->cl_callbacks); | ||
1630 | INIT_LIST_HEAD(&clp->cl_revoked); | 1629 | INIT_LIST_HEAD(&clp->cl_revoked); |
1631 | #ifdef CONFIG_NFSD_PNFS | 1630 | #ifdef CONFIG_NFSD_PNFS |
1632 | INIT_LIST_HEAD(&clp->cl_lo_states); | 1631 | INIT_LIST_HEAD(&clp->cl_lo_states); |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index e791985a7318..dbc4f85a5008 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -63,13 +63,12 @@ typedef struct { | |||
63 | 63 | ||
64 | struct nfsd4_callback { | 64 | struct nfsd4_callback { |
65 | struct nfs4_client *cb_clp; | 65 | struct nfs4_client *cb_clp; |
66 | struct list_head cb_per_client; | ||
67 | u32 cb_minorversion; | 66 | u32 cb_minorversion; |
68 | struct rpc_message cb_msg; | 67 | struct rpc_message cb_msg; |
69 | struct nfsd4_callback_ops *cb_ops; | 68 | struct nfsd4_callback_ops *cb_ops; |
70 | struct work_struct cb_work; | 69 | struct work_struct cb_work; |
71 | int cb_status; | 70 | int cb_status; |
72 | bool cb_done; | 71 | bool cb_need_restart; |
73 | }; | 72 | }; |
74 | 73 | ||
75 | struct nfsd4_callback_ops { | 74 | struct nfsd4_callback_ops { |
@@ -334,7 +333,6 @@ struct nfs4_client { | |||
334 | int cl_cb_state; | 333 | int cl_cb_state; |
335 | struct nfsd4_callback cl_cb_null; | 334 | struct nfsd4_callback cl_cb_null; |
336 | struct nfsd4_session *cl_cb_session; | 335 | struct nfsd4_session *cl_cb_session; |
337 | struct list_head cl_callbacks; /* list of in-progress callbacks */ | ||
338 | 336 | ||
339 | /* for all client information that callback code might need: */ | 337 | /* for all client information that callback code might need: */ |
340 | spinlock_t cl_lock; | 338 | spinlock_t cl_lock; |