aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2015-04-30 05:49:24 -0400
committerJ. Bruce Fields <bfields@redhat.com>2015-05-04 12:02:41 -0400
commitcba5f62b1830c1919b47544789bc993e6e617dc6 (patch)
tree6f85f8982fe083ea576b7938799e56fe4d675bec /fs/nfsd
parentef2a1b3e1067195f1d6b89d8329454775c87f033 (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.c52
-rw-r--r--fs/nfsd/nfs4state.c1
-rw-r--r--fs/nfsd/state.h4
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
942static void nfsd4_cb_release(void *calldata) 939static 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
956static const struct rpc_call_ops nfsd4_cb_ops = { 950static 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
1053static void 1044static 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
1093void nfsd4_run_cb(struct nfsd4_callback *cb) 1087void 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
64struct nfsd4_callback { 64struct 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
75struct nfsd4_callback_ops { 74struct 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;