summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorTrond Myklebust <trondmy@gmail.com>2019-04-05 11:54:37 -0400
committerJ. Bruce Fields <bfields@redhat.com>2019-04-08 12:43:15 -0400
commite6abc8caa6deb14be2a206253f7e1c5e37e9515b (patch)
treee3eea6085be977aed5822ab9ad06b13b6e697f48 /fs/nfsd
parent3c86794ac0e6582eea7733619d58ea150198502f (diff)
nfsd: Don't release the callback slot unless it was actually held
If there are multiple callbacks queued, waiting for the callback slot when the callback gets shut down, then they all currently end up acting as if they hold the slot, and call nfsd4_cb_sequence_done() resulting in interesting side-effects. In addition, the 'retry_nowait' path in nfsd4_cb_sequence_done() causes a loop back to nfsd4_cb_prepare() without first freeing the slot, which causes a deadlock when nfsd41_cb_get_slot() gets called a second time. This patch therefore adds a boolean to track whether or not the callback did pick up the slot, so that it can do the right thing in these 2 cases. Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4callback.c8
-rw-r--r--fs/nfsd/state.h1
2 files changed, 8 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index d219159b98af..7caa3801ce72 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1010,8 +1010,9 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
1010 cb->cb_seq_status = 1; 1010 cb->cb_seq_status = 1;
1011 cb->cb_status = 0; 1011 cb->cb_status = 0;
1012 if (minorversion) { 1012 if (minorversion) {
1013 if (!nfsd41_cb_get_slot(clp, task)) 1013 if (!cb->cb_holds_slot && !nfsd41_cb_get_slot(clp, task))
1014 return; 1014 return;
1015 cb->cb_holds_slot = true;
1015 } 1016 }
1016 rpc_call_start(task); 1017 rpc_call_start(task);
1017} 1018}
@@ -1038,6 +1039,9 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
1038 return true; 1039 return true;
1039 } 1040 }
1040 1041
1042 if (!cb->cb_holds_slot)
1043 goto need_restart;
1044
1041 switch (cb->cb_seq_status) { 1045 switch (cb->cb_seq_status) {
1042 case 0: 1046 case 0:
1043 /* 1047 /*
@@ -1076,6 +1080,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
1076 cb->cb_seq_status); 1080 cb->cb_seq_status);
1077 } 1081 }
1078 1082
1083 cb->cb_holds_slot = false;
1079 clear_bit(0, &clp->cl_cb_slot_busy); 1084 clear_bit(0, &clp->cl_cb_slot_busy);
1080 rpc_wake_up_next(&clp->cl_cb_waitq); 1085 rpc_wake_up_next(&clp->cl_cb_waitq);
1081 dprintk("%s: freed slot, new seqid=%d\n", __func__, 1086 dprintk("%s: freed slot, new seqid=%d\n", __func__,
@@ -1283,6 +1288,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
1283 cb->cb_seq_status = 1; 1288 cb->cb_seq_status = 1;
1284 cb->cb_status = 0; 1289 cb->cb_status = 0;
1285 cb->cb_need_restart = false; 1290 cb->cb_need_restart = false;
1291 cb->cb_holds_slot = false;
1286} 1292}
1287 1293
1288void nfsd4_run_cb(struct nfsd4_callback *cb) 1294void nfsd4_run_cb(struct nfsd4_callback *cb)
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 396c76755b03..9d6cb246c6c5 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -70,6 +70,7 @@ struct nfsd4_callback {
70 int cb_seq_status; 70 int cb_seq_status;
71 int cb_status; 71 int cb_status;
72 bool cb_need_restart; 72 bool cb_need_restart;
73 bool cb_holds_slot;
73}; 74};
74 75
75struct nfsd4_callback_ops { 76struct nfsd4_callback_ops {