]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.10.87/iscsi-target-fix-iscsit_start_kthreads-failure-oops.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.10.87 / iscsi-target-fix-iscsit_start_kthreads-failure-oops.patch
1 From e54198657b65625085834847ab6271087323ffea Mon Sep 17 00:00:00 2001
2 From: Nicholas Bellinger <nab@linux-iscsi.org>
3 Date: Wed, 22 Jul 2015 23:14:19 -0700
4 Subject: iscsi-target: Fix iscsit_start_kthreads failure OOPs
5
6 From: Nicholas Bellinger <nab@linux-iscsi.org>
7
8 commit e54198657b65625085834847ab6271087323ffea upstream.
9
10 This patch fixes a regression introduced with the following commit
11 in v4.0-rc1 code, where a iscsit_start_kthreads() failure triggers
12 a NULL pointer dereference OOPs:
13
14 commit 88dcd2dab5c23b1c9cfc396246d8f476c872f0ca
15 Author: Nicholas Bellinger <nab@linux-iscsi.org>
16 Date: Thu Feb 26 22:19:15 2015 -0800
17
18 iscsi-target: Convert iscsi_thread_set usage to kthread.h
19
20 To address this bug, move iscsit_start_kthreads() immediately
21 preceeding the transmit of last login response, before signaling
22 a successful transition into full-feature-phase within existing
23 iscsi_target_do_tx_login_io() logic.
24
25 This ensures that no target-side resource allocation failures can
26 occur after the final login response has been successfully sent.
27
28 Also, it adds a iscsi_conn->rx_login_comp to allow the RX thread
29 to sleep to prevent other socket related failures until the final
30 iscsi_post_login_handler() call is able to complete.
31
32 Cc: Sagi Grimberg <sagig@mellanox.com>
33 Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
34 Signed-off-by: Nicholas Bellinger <nab@daterainc.com>
35 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
36
37 ---
38 drivers/target/iscsi/iscsi_target.c | 18 ++++++++++--
39 drivers/target/iscsi/iscsi_target_core.h | 1
40 drivers/target/iscsi/iscsi_target_login.c | 43 +++++++++++-------------------
41 drivers/target/iscsi/iscsi_target_login.h | 1
42 drivers/target/iscsi/iscsi_target_nego.c | 34 +++++++++++++++++++++++
43 5 files changed, 66 insertions(+), 31 deletions(-)
44
45 --- a/drivers/target/iscsi/iscsi_target.c
46 +++ b/drivers/target/iscsi/iscsi_target.c
47 @@ -3874,7 +3874,13 @@ get_immediate:
48 }
49
50 transport_err:
51 - iscsit_take_action_for_connection_exit(conn);
52 + /*
53 + * Avoid the normal connection failure code-path if this connection
54 + * is still within LOGIN mode, and iscsi_np process context is
55 + * responsible for cleaning up the early connection failure.
56 + */
57 + if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
58 + iscsit_take_action_for_connection_exit(conn);
59 out:
60 return 0;
61 }
62 @@ -3956,7 +3962,7 @@ reject:
63
64 int iscsi_target_rx_thread(void *arg)
65 {
66 - int ret;
67 + int ret, rc;
68 u8 buffer[ISCSI_HDR_LEN], opcode;
69 u32 checksum = 0, digest = 0;
70 struct iscsi_conn *conn = arg;
71 @@ -3966,10 +3972,16 @@ int iscsi_target_rx_thread(void *arg)
72 * connection recovery / failure event can be triggered externally.
73 */
74 allow_signal(SIGINT);
75 + /*
76 + * Wait for iscsi_post_login_handler() to complete before allowing
77 + * incoming iscsi/tcp socket I/O, and/or failing the connection.
78 + */
79 + rc = wait_for_completion_interruptible(&conn->rx_login_comp);
80 + if (rc < 0)
81 + return 0;
82
83 if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
84 struct completion comp;
85 - int rc;
86
87 init_completion(&comp);
88 rc = wait_for_completion_interruptible(&comp);
89 --- a/drivers/target/iscsi/iscsi_target_core.h
90 +++ b/drivers/target/iscsi/iscsi_target_core.h
91 @@ -589,6 +589,7 @@ struct iscsi_conn {
92 int bitmap_id;
93 int rx_thread_active;
94 struct task_struct *rx_thread;
95 + struct completion rx_login_comp;
96 int tx_thread_active;
97 struct task_struct *tx_thread;
98 /* list_head for session connection list */
99 --- a/drivers/target/iscsi/iscsi_target_login.c
100 +++ b/drivers/target/iscsi/iscsi_target_login.c
101 @@ -84,6 +84,7 @@ static struct iscsi_login *iscsi_login_i
102 init_completion(&conn->conn_logout_comp);
103 init_completion(&conn->rx_half_close_comp);
104 init_completion(&conn->tx_half_close_comp);
105 + init_completion(&conn->rx_login_comp);
106 spin_lock_init(&conn->cmd_lock);
107 spin_lock_init(&conn->conn_usage_lock);
108 spin_lock_init(&conn->immed_queue_lock);
109 @@ -718,6 +719,7 @@ int iscsit_start_kthreads(struct iscsi_c
110
111 return 0;
112 out_tx:
113 + send_sig(SIGINT, conn->tx_thread, 1);
114 kthread_stop(conn->tx_thread);
115 conn->tx_thread_active = false;
116 out_bitmap:
117 @@ -728,7 +730,7 @@ out_bitmap:
118 return ret;
119 }
120
121 -int iscsi_post_login_handler(
122 +void iscsi_post_login_handler(
123 struct iscsi_np *np,
124 struct iscsi_conn *conn,
125 u8 zero_tsih)
126 @@ -738,7 +740,6 @@ int iscsi_post_login_handler(
127 struct se_session *se_sess = sess->se_sess;
128 struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
129 struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
130 - int rc;
131
132 iscsit_inc_conn_usage_count(conn);
133
134 @@ -779,10 +780,6 @@ int iscsi_post_login_handler(
135 sess->sess_ops->InitiatorName);
136 spin_unlock_bh(&sess->conn_lock);
137
138 - rc = iscsit_start_kthreads(conn);
139 - if (rc)
140 - return rc;
141 -
142 iscsi_post_login_start_timers(conn);
143 /*
144 * Determine CPU mask to ensure connection's RX and TX kthreads
145 @@ -791,15 +788,20 @@ int iscsi_post_login_handler(
146 iscsit_thread_get_cpumask(conn);
147 conn->conn_rx_reset_cpumask = 1;
148 conn->conn_tx_reset_cpumask = 1;
149 -
150 + /*
151 + * Wakeup the sleeping iscsi_target_rx_thread() now that
152 + * iscsi_conn is in TARG_CONN_STATE_LOGGED_IN state.
153 + */
154 + complete(&conn->rx_login_comp);
155 iscsit_dec_conn_usage_count(conn);
156 +
157 if (stop_timer) {
158 spin_lock_bh(&se_tpg->session_lock);
159 iscsit_stop_time2retain_timer(sess);
160 spin_unlock_bh(&se_tpg->session_lock);
161 }
162 iscsit_dec_session_usage_count(sess);
163 - return 0;
164 + return;
165 }
166
167 iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1);
168 @@ -840,10 +842,6 @@ int iscsi_post_login_handler(
169 " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt);
170 spin_unlock_bh(&se_tpg->session_lock);
171
172 - rc = iscsit_start_kthreads(conn);
173 - if (rc)
174 - return rc;
175 -
176 iscsi_post_login_start_timers(conn);
177 /*
178 * Determine CPU mask to ensure connection's RX and TX kthreads
179 @@ -852,10 +850,12 @@ int iscsi_post_login_handler(
180 iscsit_thread_get_cpumask(conn);
181 conn->conn_rx_reset_cpumask = 1;
182 conn->conn_tx_reset_cpumask = 1;
183 -
184 + /*
185 + * Wakeup the sleeping iscsi_target_rx_thread() now that
186 + * iscsi_conn is in TARG_CONN_STATE_LOGGED_IN state.
187 + */
188 + complete(&conn->rx_login_comp);
189 iscsit_dec_conn_usage_count(conn);
190 -
191 - return 0;
192 }
193
194 static void iscsi_handle_login_thread_timeout(unsigned long data)
195 @@ -1331,20 +1331,9 @@ static int __iscsi_target_login_thread(s
196 if (iscsi_target_start_negotiation(login, conn) < 0)
197 goto new_sess_out;
198
199 - if (!conn->sess) {
200 - pr_err("struct iscsi_conn session pointer is NULL!\n");
201 - goto new_sess_out;
202 - }
203 -
204 iscsi_stop_login_thread_timer(np);
205
206 - if (signal_pending(current))
207 - goto new_sess_out;
208 -
209 - ret = iscsi_post_login_handler(np, conn, zero_tsih);
210 -
211 - if (ret < 0)
212 - goto new_sess_out;
213 + iscsi_post_login_handler(np, conn, zero_tsih);
214
215 iscsit_deaccess_np(np, tpg);
216 tpg = NULL;
217 --- a/drivers/target/iscsi/iscsi_target_login.h
218 +++ b/drivers/target/iscsi/iscsi_target_login.h
219 @@ -12,6 +12,7 @@ extern int iscsit_accept_np(struct iscsi
220 extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *);
221 extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
222 extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);
223 +extern int iscsit_start_kthreads(struct iscsi_conn *);
224 extern int iscsi_target_login_thread(void *);
225 extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *);
226
227 --- a/drivers/target/iscsi/iscsi_target_nego.c
228 +++ b/drivers/target/iscsi/iscsi_target_nego.c
229 @@ -19,6 +19,7 @@
230 ******************************************************************************/
231
232 #include <linux/ctype.h>
233 +#include <linux/kthread.h>
234 #include <scsi/iscsi_proto.h>
235 #include <target/target_core_base.h>
236 #include <target/target_core_fabric.h>
237 @@ -352,10 +353,24 @@ static int iscsi_target_do_tx_login_io(s
238 ntohl(login_rsp->statsn), login->rsp_length);
239
240 padding = ((-login->rsp_length) & 3);
241 + /*
242 + * Before sending the last login response containing the transition
243 + * bit for full-feature-phase, go ahead and start up TX/RX threads
244 + * now to avoid potential resource allocation failures after the
245 + * final login response has been sent.
246 + */
247 + if (login->login_complete) {
248 + int rc = iscsit_start_kthreads(conn);
249 + if (rc) {
250 + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
251 + ISCSI_LOGIN_STATUS_NO_RESOURCES);
252 + return -1;
253 + }
254 + }
255
256 if (conn->conn_transport->iscsit_put_login_tx(conn, login,
257 login->rsp_length + padding) < 0)
258 - return -1;
259 + goto err;
260
261 login->rsp_length = 0;
262 mutex_lock(&sess->cmdsn_mutex);
263 @@ -364,6 +379,23 @@ static int iscsi_target_do_tx_login_io(s
264 mutex_unlock(&sess->cmdsn_mutex);
265
266 return 0;
267 +
268 +err:
269 + if (login->login_complete) {
270 + if (conn->rx_thread && conn->rx_thread_active) {
271 + send_sig(SIGINT, conn->rx_thread, 1);
272 + kthread_stop(conn->rx_thread);
273 + }
274 + if (conn->tx_thread && conn->tx_thread_active) {
275 + send_sig(SIGINT, conn->tx_thread, 1);
276 + kthread_stop(conn->tx_thread);
277 + }
278 + spin_lock(&iscsit_global->ts_bitmap_lock);
279 + bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id,
280 + get_order(1));
281 + spin_unlock(&iscsit_global->ts_bitmap_lock);
282 + }
283 + return -1;
284 }
285
286 static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)