xref: /titanic_50/usr/src/uts/common/io/idm/idm_conn_sm.c (revision 57ff5e7e2f79bd09ccd01cdeba02f38fdde17a80)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2013 by Delphix. All rights reserved.
25  */
26 
27 #include <sys/cpuvar.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/socket.h>
32 #include <sys/strsubr.h>
33 #include <sys/note.h>
34 #include <sys/sdt.h>
35 
36 #define	IDM_CONN_SM_STRINGS
37 #define	IDM_CN_NOTIFY_STRINGS
38 #include <sys/idm/idm.h>
39 
40 boolean_t	idm_sm_logging = B_FALSE;
41 
42 extern idm_global_t	idm; /* Global state */
43 
44 static void
45 idm_conn_event_handler(void *event_ctx_opaque);
46 
47 static void
48 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
49 
50 static void
51 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
52 
53 static void
54 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
55 
56 static void
57 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
58 
59 static void
60 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
61 
62 static void
63 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
64 
65 static void
66 idm_logout_req_timeout(void *arg);
67 
68 static void
69 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
70 
71 static void
72 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
73 
74 static void
75 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
76 
77 static void
78 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
79 
80 static void
81 idm_state_s9b_wait_snd_done_cb(idm_pdu_t *pdu,
82     idm_status_t status);
83 
84 static void
85 idm_state_s9b_wait_snd_done(idm_conn_t *ic,
86     idm_conn_event_ctx_t *event_ctx);
87 
88 static void
89 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
90 
91 static void
92 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
93 
94 static void
95 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
96 
97 static void
98 idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state,
99     idm_conn_event_ctx_t *event_ctx);
100 
101 static void
102 idm_conn_unref(void *ic_void);
103 
104 static void
105 idm_conn_reject_unref(void *ic_void);
106 
107 static idm_pdu_event_action_t
108 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx,
109     idm_pdu_t *pdu);
110 
111 static idm_status_t
112 idm_ffp_enable(idm_conn_t *ic);
113 
114 static void
115 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type);
116 
117 static void
118 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
119 
120 static void
121 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
122 
123 idm_status_t
idm_conn_sm_init(idm_conn_t * ic)124 idm_conn_sm_init(idm_conn_t *ic)
125 {
126 	char taskq_name[32];
127 
128 	/*
129 	 * Caller should have assigned a unique connection ID.  Use this
130 	 * connection ID to create a unique connection name string
131 	 */
132 	ASSERT(ic->ic_internal_cid != 0);
133 	(void) snprintf(taskq_name, sizeof (taskq_name) - 1, "conn_sm%08x",
134 	    ic->ic_internal_cid);
135 
136 	ic->ic_state_taskq = taskq_create(taskq_name, 1, minclsyspri, 4, 16384,
137 	    TASKQ_PREPOPULATE);
138 	if (ic->ic_state_taskq == NULL) {
139 		return (IDM_STATUS_FAIL);
140 	}
141 
142 	idm_sm_audit_init(&ic->ic_state_audit);
143 	mutex_init(&ic->ic_state_mutex, NULL, MUTEX_DEFAULT, NULL);
144 	cv_init(&ic->ic_state_cv, NULL, CV_DEFAULT, NULL);
145 
146 	ic->ic_state = CS_S1_FREE;
147 	ic->ic_last_state = CS_S1_FREE;
148 
149 	return (IDM_STATUS_SUCCESS);
150 }
151 
152 void
idm_conn_sm_fini(idm_conn_t * ic)153 idm_conn_sm_fini(idm_conn_t *ic)
154 {
155 
156 	/*
157 	 * The connection may only be partially created. If there
158 	 * is no taskq, then the connection SM was not initialized.
159 	 */
160 	if (ic->ic_state_taskq == NULL) {
161 		return;
162 	}
163 
164 	taskq_destroy(ic->ic_state_taskq);
165 
166 	cv_destroy(&ic->ic_state_cv);
167 	/*
168 	 * The thread that generated the event that got us here may still
169 	 * hold the ic_state_mutex. Once it is released we can safely
170 	 * destroy it since there is no way to locate the object now.
171 	 */
172 	mutex_enter(&ic->ic_state_mutex);
173 	mutex_destroy(&ic->ic_state_mutex);
174 }
175 
176 void
idm_conn_event(idm_conn_t * ic,idm_conn_event_t event,uintptr_t event_info)177 idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info)
178 {
179 	mutex_enter(&ic->ic_state_mutex);
180 	idm_conn_event_locked(ic, event, event_info, CT_NONE);
181 	mutex_exit(&ic->ic_state_mutex);
182 }
183 
184 
185 idm_status_t
idm_conn_reinstate_event(idm_conn_t * old_ic,idm_conn_t * new_ic)186 idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic)
187 {
188 	int result;
189 
190 	mutex_enter(&old_ic->ic_state_mutex);
191 	if (((old_ic->ic_conn_type == CONN_TYPE_INI) &&
192 	    (old_ic->ic_state != CS_S8_CLEANUP)) ||
193 	    ((old_ic->ic_conn_type == CONN_TYPE_TGT) &&
194 	    (old_ic->ic_state < CS_S5_LOGGED_IN))) {
195 		result = IDM_STATUS_FAIL;
196 	} else {
197 		result = IDM_STATUS_SUCCESS;
198 		new_ic->ic_reinstate_conn = old_ic;
199 		idm_conn_event_locked(new_ic->ic_reinstate_conn,
200 		    CE_CONN_REINSTATE, (uintptr_t)new_ic, CT_NONE);
201 	}
202 	mutex_exit(&old_ic->ic_state_mutex);
203 
204 	return (result);
205 }
206 
207 void
idm_conn_tx_pdu_event(idm_conn_t * ic,idm_conn_event_t event,uintptr_t event_info)208 idm_conn_tx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
209     uintptr_t event_info)
210 {
211 	ASSERT(mutex_owned(&ic->ic_state_mutex));
212 	ic->ic_pdu_events++;
213 	idm_conn_event_locked(ic, event, event_info, CT_TX_PDU);
214 }
215 
216 void
idm_conn_rx_pdu_event(idm_conn_t * ic,idm_conn_event_t event,uintptr_t event_info)217 idm_conn_rx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
218     uintptr_t event_info)
219 {
220 	ASSERT(mutex_owned(&ic->ic_state_mutex));
221 	ic->ic_pdu_events++;
222 	idm_conn_event_locked(ic, event, event_info, CT_RX_PDU);
223 }
224 
225 void
idm_conn_event_locked(idm_conn_t * ic,idm_conn_event_t event,uintptr_t event_info,idm_pdu_event_type_t pdu_event_type)226 idm_conn_event_locked(idm_conn_t *ic, idm_conn_event_t event,
227     uintptr_t event_info, idm_pdu_event_type_t pdu_event_type)
228 {
229 	idm_conn_event_ctx_t	*event_ctx;
230 
231 	ASSERT(mutex_owned(&ic->ic_state_mutex));
232 
233 	idm_sm_audit_event(&ic->ic_state_audit, SAS_IDM_CONN,
234 	    (int)ic->ic_state, (int)event, event_info);
235 
236 	/*
237 	 * It's very difficult to prevent a few straggling events
238 	 * at the end.  For example idm_sorx_thread will generate
239 	 * a CE_TRANSPORT_FAIL event when it exits.  Rather than
240 	 * push complicated restrictions all over the code to
241 	 * prevent this we will simply drop the events (and in
242 	 * the case of PDU events release them appropriately)
243 	 * since they are irrelevant once we are in a terminal state.
244 	 * Of course those threads need to have appropriate holds on
245 	 * the connection otherwise it might disappear.
246 	 */
247 	if ((ic->ic_state == CS_S9_INIT_ERROR) ||
248 	    (ic->ic_state == CS_S9A_REJECTED) ||
249 	    (ic->ic_state == CS_S11_COMPLETE)) {
250 		if ((pdu_event_type == CT_TX_PDU) ||
251 		    (pdu_event_type == CT_RX_PDU)) {
252 			ic->ic_pdu_events--;
253 			idm_pdu_complete((idm_pdu_t *)event_info,
254 			    IDM_STATUS_SUCCESS);
255 		}
256 		IDM_SM_LOG(CE_NOTE, "*** Dropping event %s (%d) because of"
257 		    "state %s (%d)",
258 		    idm_ce_name[event], event,
259 		    idm_cs_name[ic->ic_state], ic->ic_state);
260 		return;
261 	}
262 
263 	/*
264 	 * Normal event handling
265 	 */
266 	idm_conn_hold(ic);
267 
268 	event_ctx = kmem_zalloc(sizeof (*event_ctx), KM_SLEEP);
269 	event_ctx->iec_ic = ic;
270 	event_ctx->iec_event = event;
271 	event_ctx->iec_info = event_info;
272 	event_ctx->iec_pdu_event_type = pdu_event_type;
273 
274 	(void) taskq_dispatch(ic->ic_state_taskq, &idm_conn_event_handler,
275 	    event_ctx, TQ_SLEEP);
276 }
277 
278 static void
idm_conn_event_handler(void * event_ctx_opaque)279 idm_conn_event_handler(void *event_ctx_opaque)
280 {
281 	idm_conn_event_ctx_t *event_ctx = event_ctx_opaque;
282 	idm_conn_t *ic = event_ctx->iec_ic;
283 	idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info;
284 	idm_pdu_event_action_t action;
285 
286 	IDM_SM_LOG(CE_NOTE, "idm_conn_event_handler: conn %p event %s(%d)",
287 	    (void *)ic, idm_ce_name[event_ctx->iec_event],
288 	    event_ctx->iec_event);
289 	DTRACE_PROBE2(conn__event,
290 	    idm_conn_t *, ic, idm_conn_event_ctx_t *, event_ctx);
291 
292 	/*
293 	 * Validate event
294 	 */
295 	ASSERT(event_ctx->iec_event != CE_UNDEFINED);
296 	ASSERT3U(event_ctx->iec_event, <, CE_MAX_EVENT);
297 
298 	/*
299 	 * Validate current state
300 	 */
301 	ASSERT(ic->ic_state != CS_S0_UNDEFINED);
302 	ASSERT3U(ic->ic_state, <, CS_MAX_STATE);
303 
304 	/*
305 	 * Validate PDU-related events against the current state.  If a PDU
306 	 * is not allowed in the current state we change the event to a
307 	 * protocol error.  This simplifies the state-specific event handlers.
308 	 * For example the CS_S2_XPT_WAIT state only needs to handle the
309 	 * CE_TX_PROTOCOL_ERROR and CE_RX_PROTOCOL_ERROR events since
310 	 * no PDU's can be transmitted or received in that state.
311 	 */
312 	event_ctx->iec_pdu_forwarded = B_FALSE;
313 	if (event_ctx->iec_pdu_event_type != CT_NONE) {
314 		ASSERT(pdu != NULL);
315 		action = idm_conn_sm_validate_pdu(ic, event_ctx, pdu);
316 
317 		switch (action) {
318 		case CA_TX_PROTOCOL_ERROR:
319 			/*
320 			 * Change event and forward the PDU
321 			 */
322 			event_ctx->iec_event = CE_TX_PROTOCOL_ERROR;
323 			break;
324 		case CA_RX_PROTOCOL_ERROR:
325 			/*
326 			 * Change event and forward the PDU.
327 			 */
328 			event_ctx->iec_event = CE_RX_PROTOCOL_ERROR;
329 			break;
330 		case CA_FORWARD:
331 			/*
332 			 * Let the state-specific event handlers take
333 			 * care of it.
334 			 */
335 			break;
336 		case CA_DROP:
337 			/*
338 			 * It never even happened
339 			 */
340 			IDM_SM_LOG(CE_NOTE, "*** drop PDU %p", (void *) pdu);
341 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
342 			break;
343 		default:
344 			ASSERT(0);
345 			break;
346 		}
347 	}
348 
349 	switch (ic->ic_state) {
350 	case CS_S1_FREE:
351 		idm_state_s1_free(ic, event_ctx);
352 		break;
353 	case CS_S2_XPT_WAIT:
354 		idm_state_s2_xpt_wait(ic, event_ctx);
355 		break;
356 	case CS_S3_XPT_UP:
357 		idm_state_s3_xpt_up(ic, event_ctx);
358 		break;
359 	case CS_S4_IN_LOGIN:
360 		idm_state_s4_in_login(ic, event_ctx);
361 		break;
362 	case CS_S5_LOGGED_IN:
363 		idm_state_s5_logged_in(ic, event_ctx);
364 		break;
365 	case CS_S6_IN_LOGOUT:
366 		idm_state_s6_in_logout(ic, event_ctx);
367 		break;
368 	case CS_S7_LOGOUT_REQ:
369 		idm_state_s7_logout_req(ic, event_ctx);
370 		break;
371 	case CS_S8_CLEANUP:
372 		idm_state_s8_cleanup(ic, event_ctx);
373 		break;
374 	case CS_S9A_REJECTED:
375 		idm_state_s9a_rejected(ic, event_ctx);
376 		break;
377 	case CS_S9B_WAIT_SND_DONE:
378 		idm_state_s9b_wait_snd_done(ic, event_ctx);
379 		break;
380 	case CS_S9_INIT_ERROR:
381 		idm_state_s9_init_error(ic, event_ctx);
382 		break;
383 	case CS_S10_IN_CLEANUP:
384 		idm_state_s10_in_cleanup(ic, event_ctx);
385 		break;
386 	case CS_S11_COMPLETE:
387 		idm_state_s11_complete(ic, event_ctx);
388 		break;
389 	case CS_S12_ENABLE_DM:
390 		idm_state_s12_enable_dm(ic, event_ctx);
391 		break;
392 	default:
393 		ASSERT(0);
394 		break;
395 	}
396 
397 	/*
398 	 * Now that we've updated the state machine, if this was
399 	 * a PDU-related event take the appropriate action on the PDU
400 	 * (transmit it, forward it to the clients RX callback, drop
401 	 * it, etc).
402 	 */
403 	if (event_ctx->iec_pdu_event_type != CT_NONE) {
404 		switch (action) {
405 		case CA_TX_PROTOCOL_ERROR:
406 			idm_pdu_tx_protocol_error(ic, pdu);
407 			break;
408 		case CA_RX_PROTOCOL_ERROR:
409 			idm_pdu_rx_protocol_error(ic, pdu);
410 			break;
411 		case CA_FORWARD:
412 			if (!event_ctx->iec_pdu_forwarded) {
413 				if (event_ctx->iec_pdu_event_type ==
414 				    CT_RX_PDU) {
415 					idm_pdu_rx_forward(ic, pdu);
416 				} else {
417 					idm_pdu_tx_forward(ic, pdu);
418 				}
419 			}
420 			break;
421 		default:
422 			ASSERT(0);
423 			break;
424 		}
425 	}
426 
427 	/*
428 	 * Update outstanding PDU event count (see idm_pdu_tx for
429 	 * how this is used)
430 	 */
431 	if ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ||
432 	    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
433 		mutex_enter(&ic->ic_state_mutex);
434 		ic->ic_pdu_events--;
435 		mutex_exit(&ic->ic_state_mutex);
436 	}
437 
438 	idm_conn_rele(ic);
439 	kmem_free(event_ctx, sizeof (*event_ctx));
440 }
441 
442 static void
idm_state_s1_free(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)443 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
444 {
445 	switch (event_ctx->iec_event) {
446 	case CE_CONNECT_REQ:
447 		/* T1 */
448 		idm_update_state(ic, CS_S2_XPT_WAIT, event_ctx);
449 		break;
450 	case CE_CONNECT_ACCEPT:
451 		/* T3 */
452 		idm_update_state(ic, CS_S3_XPT_UP, event_ctx);
453 		break;
454 	case CE_TX_PROTOCOL_ERROR:
455 	case CE_RX_PROTOCOL_ERROR:
456 		/* This should never happen */
457 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
458 		break;
459 	default:
460 		ASSERT(0);
461 		/*NOTREACHED*/
462 	}
463 }
464 
465 
466 static void
idm_state_s2_xpt_wait(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)467 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
468 {
469 	switch (event_ctx->iec_event) {
470 	case CE_CONNECT_SUCCESS:
471 		/* T4 */
472 		idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
473 		break;
474 	case CE_TRANSPORT_FAIL:
475 	case CE_CONNECT_FAIL:
476 	case CE_LOGOUT_OTHER_CONN_RCV:
477 	case CE_TX_PROTOCOL_ERROR:
478 	case CE_RX_PROTOCOL_ERROR:
479 		/* T2 */
480 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
481 		break;
482 	default:
483 		ASSERT(0);
484 		/*NOTREACHED*/
485 	}
486 }
487 
488 
489 static void
idm_login_timeout(void * arg)490 idm_login_timeout(void *arg)
491 {
492 	idm_conn_t *ic = arg;
493 
494 	idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL);
495 }
496 
497 static void
idm_state_s3_xpt_up(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)498 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
499 {
500 	switch (event_ctx->iec_event) {
501 	case CE_LOGIN_RCV:
502 		/* T4 */
503 		idm_initial_login_actions(ic, event_ctx);
504 		idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
505 		break;
506 	case CE_LOGIN_TIMEOUT:
507 		/*
508 		 * Don't need to cancel login timer since the timer is
509 		 * presumed to be the source of this event.
510 		 */
511 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
512 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
513 		break;
514 	case CE_CONNECT_REJECT:
515 		/*
516 		 * Iscsit doesn't want to hear from us again in this case.
517 		 * Since it rejected the connection it doesn't have a
518 		 * connection context to handle additional notifications.
519 		 * IDM needs to just clean things up on its own.
520 		 */
521 		(void) untimeout(ic->ic_state_timeout);
522 		idm_update_state(ic, CS_S9A_REJECTED, event_ctx);
523 		break;
524 	case CE_CONNECT_FAIL:
525 	case CE_TRANSPORT_FAIL:
526 	case CE_LOGOUT_OTHER_CONN_SND:
527 		/* T6 */
528 		(void) untimeout(ic->ic_state_timeout);
529 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
530 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
531 		break;
532 	case CE_TX_PROTOCOL_ERROR:
533 	case CE_RX_PROTOCOL_ERROR:
534 		/* Don't care */
535 		break;
536 	default:
537 		ASSERT(0);
538 		/*NOTREACHED*/
539 	}
540 }
541 
542 static void
idm_state_s4_in_login(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)543 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
544 {
545 	idm_pdu_t *pdu;
546 
547 	/*
548 	 * Login timer should no longer be active after leaving this
549 	 * state.
550 	 */
551 	switch (event_ctx->iec_event) {
552 	case CE_LOGIN_SUCCESS_RCV:
553 	case CE_LOGIN_SUCCESS_SND:
554 		ASSERT(ic->ic_client_callback == NULL);
555 
556 		(void) untimeout(ic->ic_state_timeout);
557 		idm_login_success_actions(ic, event_ctx);
558 		if (ic->ic_rdma_extensions) {
559 			/* T19 */
560 			idm_update_state(ic, CS_S12_ENABLE_DM, event_ctx);
561 		} else {
562 			/* T5 */
563 			idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
564 		}
565 		break;
566 	case CE_LOGIN_TIMEOUT:
567 		/* T7 */
568 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
569 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
570 		break;
571 	case CE_LOGIN_FAIL_SND:
572 		/*
573 		 * Allow the logout response pdu to be sent and defer
574 		 * the state machine cleanup until the completion callback.
575 		 * Only 1 level or callback interposition is allowed.
576 		 */
577 		(void) untimeout(ic->ic_state_timeout);
578 		pdu = (idm_pdu_t *)event_ctx->iec_info;
579 		ASSERT(ic->ic_client_callback == NULL);
580 		ic->ic_client_callback = pdu->isp_callback;
581 		pdu->isp_callback =
582 		    idm_state_s9b_wait_snd_done_cb;
583 		idm_update_state(ic, CS_S9B_WAIT_SND_DONE,
584 		    event_ctx);
585 		break;
586 	case CE_LOGIN_FAIL_RCV:
587 		ASSERT(ic->ic_client_callback == NULL);
588 		/*
589 		 * Need to deliver this PDU to the initiator now because after
590 		 * we update the state to CS_S9_INIT_ERROR the initiator will
591 		 * no longer be in an appropriate state.
592 		 */
593 		event_ctx->iec_pdu_forwarded = B_TRUE;
594 		pdu = (idm_pdu_t *)event_ctx->iec_info;
595 		idm_pdu_rx_forward(ic, pdu);
596 		/* FALLTHROUGH */
597 	case CE_TRANSPORT_FAIL:
598 	case CE_LOGOUT_OTHER_CONN_SND:
599 	case CE_LOGOUT_OTHER_CONN_RCV:
600 		/* T7 */
601 		(void) untimeout(ic->ic_state_timeout);
602 		(void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
603 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
604 		break;
605 	case CE_LOGOUT_SESSION_SUCCESS:
606 		/*
607 		 * T8
608 		 * A session reinstatement request can be received while a
609 		 * session is active and a login is in process. The iSCSI
610 		 * connections are shut down by a CE_LOGOUT_SESSION_SUCCESS
611 		 * event sent from the session to the IDM layer.
612 		 */
613 		(void) untimeout(ic->ic_state_timeout);
614 		if (IDM_CONN_ISTGT(ic)) {
615 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
616 		} else {
617 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
618 		}
619 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
620 		break;
621 
622 	case CE_LOGIN_SND:
623 		ASSERT(ic->ic_client_callback == NULL);
624 		/*
625 		 * Initiator connections will see initial login PDU
626 		 * in this state.  Target connections see initial
627 		 * login PDU in "xpt up" state.
628 		 */
629 		mutex_enter(&ic->ic_state_mutex);
630 		if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) {
631 			idm_initial_login_actions(ic, event_ctx);
632 		}
633 		mutex_exit(&ic->ic_state_mutex);
634 		break;
635 	case CE_MISC_TX:
636 	case CE_MISC_RX:
637 	case CE_LOGIN_RCV:
638 	case CE_TX_PROTOCOL_ERROR:
639 	case CE_RX_PROTOCOL_ERROR:
640 		/* Don't care */
641 		break;
642 	default:
643 		ASSERT(0);
644 		/*NOTREACHED*/
645 	}
646 }
647 
648 
649 static void
idm_state_s5_logged_in(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)650 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
651 {
652 	switch (event_ctx->iec_event) {
653 	case CE_MISC_RX:
654 		/* MC/S: when removing the non-leading connection */
655 	case CE_LOGOUT_THIS_CONN_RCV:
656 	case CE_LOGOUT_THIS_CONN_SND:
657 	case CE_LOGOUT_OTHER_CONN_RCV:
658 	case CE_LOGOUT_OTHER_CONN_SND:
659 		/* T9 */
660 		idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
661 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
662 		break;
663 	case CE_LOGOUT_SESSION_RCV:
664 	case CE_LOGOUT_SESSION_SND:
665 		/* T9 */
666 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
667 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
668 		break;
669 	case CE_LOGOUT_SESSION_SUCCESS:
670 		/* T8 */
671 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
672 
673 		/* Close connection */
674 		if (IDM_CONN_ISTGT(ic)) {
675 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
676 		} else {
677 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
678 		}
679 
680 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
681 		break;
682 	case CE_ASYNC_LOGOUT_RCV:
683 	case CE_ASYNC_LOGOUT_SND:
684 		/* T11 */
685 		idm_update_state(ic, CS_S7_LOGOUT_REQ, event_ctx);
686 		break;
687 	case CE_TRANSPORT_FAIL:
688 	case CE_ASYNC_DROP_CONN_RCV:
689 	case CE_ASYNC_DROP_CONN_SND:
690 	case CE_ASYNC_DROP_ALL_CONN_RCV:
691 	case CE_ASYNC_DROP_ALL_CONN_SND:
692 		/* T15 */
693 		idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
694 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
695 		break;
696 	case CE_MISC_TX:
697 	case CE_TX_PROTOCOL_ERROR:
698 	case CE_RX_PROTOCOL_ERROR:
699 	case CE_LOGIN_TIMEOUT:
700 		/* Don't care */
701 		break;
702 	default:
703 		ASSERT(0);
704 	}
705 }
706 
707 static void
idm_state_s6_in_logout_success_snd_done(idm_pdu_t * pdu,idm_status_t status)708 idm_state_s6_in_logout_success_snd_done(idm_pdu_t *pdu, idm_status_t status)
709 {
710 	idm_conn_t		*ic = pdu->isp_ic;
711 
712 	/*
713 	 * This pdu callback can be invoked by the tx thread,
714 	 * so run the disconnect code from another thread.
715 	 */
716 	pdu->isp_status = status;
717 	idm_conn_event(ic, CE_LOGOUT_SUCCESS_SND_DONE, (uintptr_t)pdu);
718 }
719 
720 static void
idm_state_s6_in_logout_fail_snd_done(idm_pdu_t * pdu,idm_status_t status)721 idm_state_s6_in_logout_fail_snd_done(idm_pdu_t *pdu, idm_status_t status)
722 {
723 	idm_conn_t		*ic = pdu->isp_ic;
724 
725 	/*
726 	 * This pdu callback can be invoked by the tx thread,
727 	 * so run the disconnect code from another thread.
728 	 */
729 	pdu->isp_status = status;
730 	idm_conn_event(ic, CE_LOGOUT_FAIL_SND_DONE, (uintptr_t)pdu);
731 }
732 
733 static void
idm_state_s6_in_logout(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)734 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
735 {
736 	idm_pdu_t *pdu;
737 
738 	switch (event_ctx->iec_event) {
739 	case CE_LOGOUT_SUCCESS_SND_DONE:
740 		pdu = (idm_pdu_t *)event_ctx->iec_info;
741 
742 		/* Close connection (if it's not already closed) */
743 		ASSERT(IDM_CONN_ISTGT(ic));
744 		ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
745 
746 		/* restore client callback */
747 		pdu->isp_callback =  ic->ic_client_callback;
748 		ic->ic_client_callback = NULL;
749 		idm_pdu_complete(pdu, pdu->isp_status);
750 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
751 		break;
752 	case CE_LOGOUT_FAIL_SND_DONE:
753 		pdu = (idm_pdu_t *)event_ctx->iec_info;
754 		/* restore client callback */
755 		pdu->isp_callback =  ic->ic_client_callback;
756 		ic->ic_client_callback = NULL;
757 		idm_pdu_complete(pdu, pdu->isp_status);
758 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
759 		break;
760 	case CE_LOGOUT_SUCCESS_SND:
761 	case CE_LOGOUT_FAIL_SND:
762 		/*
763 		 * Allow the logout response pdu to be sent and defer
764 		 * the state machine update until the completion callback.
765 		 * Only 1 level or callback interposition is allowed.
766 		 */
767 		pdu = (idm_pdu_t *)event_ctx->iec_info;
768 		ASSERT(ic->ic_client_callback == NULL);
769 		ic->ic_client_callback = pdu->isp_callback;
770 		if (event_ctx->iec_event == CE_LOGOUT_SUCCESS_SND) {
771 			pdu->isp_callback =
772 			    idm_state_s6_in_logout_success_snd_done;
773 		} else {
774 			pdu->isp_callback =
775 			    idm_state_s6_in_logout_fail_snd_done;
776 		}
777 		break;
778 	case CE_LOGOUT_SUCCESS_RCV:
779 		/*
780 		 * Need to deliver this PDU to the initiator now because after
781 		 * we update the state to CS_S11_COMPLETE the initiator will
782 		 * no longer be in an appropriate state.
783 		 */
784 		event_ctx->iec_pdu_forwarded = B_TRUE;
785 		pdu = (idm_pdu_t *)event_ctx->iec_info;
786 		idm_pdu_rx_forward(ic, pdu);
787 		/* FALLTHROUGH */
788 	case CE_LOGOUT_SESSION_SUCCESS:
789 		/* T13 */
790 
791 		/* Close connection (if it's not already closed) */
792 		if (IDM_CONN_ISTGT(ic)) {
793 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
794 		} else {
795 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
796 		}
797 
798 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
799 		break;
800 	case CE_ASYNC_LOGOUT_RCV:
801 		/* T14 Do nothing */
802 		break;
803 	case CE_TRANSPORT_FAIL:
804 	case CE_ASYNC_DROP_CONN_RCV:
805 	case CE_ASYNC_DROP_CONN_SND:
806 	case CE_ASYNC_DROP_ALL_CONN_RCV:
807 	case CE_ASYNC_DROP_ALL_CONN_SND:
808 	case CE_LOGOUT_FAIL_RCV:
809 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
810 		break;
811 	case CE_TX_PROTOCOL_ERROR:
812 	case CE_RX_PROTOCOL_ERROR:
813 	case CE_MISC_TX:
814 	case CE_MISC_RX:
815 	case CE_LOGIN_TIMEOUT:
816 		/* Don't care */
817 		break;
818 	default:
819 		ASSERT(0);
820 	}
821 }
822 
823 
824 static void
idm_logout_req_timeout(void * arg)825 idm_logout_req_timeout(void *arg)
826 {
827 	idm_conn_t *ic = arg;
828 
829 	idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL);
830 }
831 
832 static void
idm_state_s7_logout_req(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)833 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
834 {
835 	/* Must cancel logout timer before leaving this state */
836 	switch (event_ctx->iec_event) {
837 	case CE_LOGOUT_THIS_CONN_RCV:
838 	case CE_LOGOUT_THIS_CONN_SND:
839 	case CE_LOGOUT_OTHER_CONN_RCV:
840 	case CE_LOGOUT_OTHER_CONN_SND:
841 		/* T10 */
842 		if (IDM_CONN_ISTGT(ic)) {
843 			(void) untimeout(ic->ic_state_timeout);
844 		}
845 		idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
846 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
847 		break;
848 	case CE_LOGOUT_SESSION_RCV:
849 	case CE_LOGOUT_SESSION_SND:
850 		/* T10 */
851 		if (IDM_CONN_ISTGT(ic)) {
852 			(void) untimeout(ic->ic_state_timeout);
853 		}
854 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
855 		idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
856 		break;
857 	case CE_ASYNC_LOGOUT_RCV:
858 	case CE_ASYNC_LOGOUT_SND:
859 		/* T12 Do nothing */
860 		break;
861 	case CE_TRANSPORT_FAIL:
862 	case CE_ASYNC_DROP_CONN_RCV:
863 	case CE_ASYNC_DROP_CONN_SND:
864 	case CE_ASYNC_DROP_ALL_CONN_RCV:
865 	case CE_ASYNC_DROP_ALL_CONN_SND:
866 		/* T16 */
867 		if (IDM_CONN_ISTGT(ic)) {
868 			(void) untimeout(ic->ic_state_timeout);
869 		}
870 		/* FALLTHROUGH */
871 	case CE_LOGOUT_TIMEOUT:
872 		idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
873 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
874 		break;
875 	case CE_LOGOUT_SESSION_SUCCESS:
876 		/* T18 */
877 		if (IDM_CONN_ISTGT(ic)) {
878 			(void) untimeout(ic->ic_state_timeout);
879 		}
880 		idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
881 
882 		/* Close connection (if it's not already closed) */
883 		if (IDM_CONN_ISTGT(ic)) {
884 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
885 		} else {
886 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
887 		}
888 
889 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
890 		break;
891 	case CE_TX_PROTOCOL_ERROR:
892 	case CE_RX_PROTOCOL_ERROR:
893 	case CE_MISC_TX:
894 	case CE_MISC_RX:
895 	case CE_LOGIN_TIMEOUT:
896 		/* Don't care */
897 		break;
898 	default:
899 		ASSERT(0);
900 	}
901 }
902 
903 
904 static void
idm_cleanup_timeout(void * arg)905 idm_cleanup_timeout(void *arg)
906 {
907 	idm_conn_t *ic = arg;
908 
909 	idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL);
910 }
911 
912 static void
idm_state_s8_cleanup(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)913 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
914 {
915 	idm_pdu_t *pdu;
916 
917 	/*
918 	 * Need to cancel the cleanup timeout before leaving this state
919 	 * if it hasn't already fired.
920 	 */
921 	switch (event_ctx->iec_event) {
922 	case CE_LOGOUT_SUCCESS_RCV:
923 	case CE_LOGOUT_SUCCESS_SND:
924 	case CE_LOGOUT_SESSION_SUCCESS:
925 		(void) untimeout(ic->ic_state_timeout);
926 		/*FALLTHROUGH*/
927 	case CE_CLEANUP_TIMEOUT:
928 		/* M1 */
929 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
930 		break;
931 	case CE_LOGOUT_OTHER_CONN_RCV:
932 	case CE_LOGOUT_OTHER_CONN_SND:
933 		/* M2 */
934 		idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx);
935 		break;
936 	case CE_LOGOUT_SUCCESS_SND_DONE:
937 	case CE_LOGOUT_FAIL_SND_DONE:
938 		pdu = (idm_pdu_t *)event_ctx->iec_info;
939 		/* restore client callback */
940 		pdu->isp_callback =  ic->ic_client_callback;
941 		ic->ic_client_callback = NULL;
942 		idm_pdu_complete(pdu, pdu->isp_status);
943 		break;
944 	case CE_LOGOUT_SESSION_RCV:
945 	case CE_LOGOUT_SESSION_SND:
946 	case CE_TX_PROTOCOL_ERROR:
947 	case CE_RX_PROTOCOL_ERROR:
948 	case CE_MISC_TX:
949 	case CE_MISC_RX:
950 	case CE_TRANSPORT_FAIL:
951 	case CE_LOGIN_TIMEOUT:
952 	case CE_LOGOUT_TIMEOUT:
953 		/* Don't care */
954 		break;
955 	default:
956 		ASSERT(0);
957 	}
958 }
959 
960 /* ARGSUSED */
961 static void
idm_state_s9_init_error(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)962 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
963 {
964 	/* All events ignored in this state */
965 }
966 
967 /* ARGSUSED */
968 static void
idm_state_s9a_rejected(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)969 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
970 {
971 	/* All events ignored in this state */
972 }
973 
974 
975 static void
idm_state_s9b_wait_snd_done_cb(idm_pdu_t * pdu,idm_status_t status)976 idm_state_s9b_wait_snd_done_cb(idm_pdu_t *pdu, idm_status_t status)
977 {
978 	idm_conn_t		*ic = pdu->isp_ic;
979 
980 	/*
981 	 * This pdu callback can be invoked by the tx thread,
982 	 * so run the disconnect code from another thread.
983 	 */
984 	pdu->isp_status = status;
985 	idm_conn_event(ic, CE_LOGIN_FAIL_SND_DONE, (uintptr_t)pdu);
986 }
987 
988 /*
989  * CS_S9B_WAIT_SND_DONE -- wait for callback completion.
990  */
991 /* ARGSUSED */
992 static void
idm_state_s9b_wait_snd_done(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)993 idm_state_s9b_wait_snd_done(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
994 {
995 	idm_pdu_t *pdu;
996 	/*
997 	 * Wait for completion of the login fail sequence and then
998 	 * go to state S9_INIT_ERROR to clean up the connection.
999 	 */
1000 	switch (event_ctx->iec_event) {
1001 	case CE_LOGIN_FAIL_SND_DONE:
1002 		pdu = (idm_pdu_t *)event_ctx->iec_info;
1003 		/* restore client callback */
1004 		pdu->isp_callback =  ic->ic_client_callback;
1005 		ic->ic_client_callback = NULL;
1006 		idm_pdu_complete(pdu, pdu->isp_status);
1007 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
1008 		break;
1009 
1010 	/* All other events ignored */
1011 	}
1012 }
1013 
1014 
1015 
1016 
1017 static void
idm_state_s10_in_cleanup(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)1018 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1019 {
1020 	idm_pdu_t *pdu;
1021 
1022 	/*
1023 	 * Need to cancel the cleanup timeout before leaving this state
1024 	 * if it hasn't already fired.
1025 	 */
1026 	switch (event_ctx->iec_event) {
1027 	case CE_LOGOUT_FAIL_RCV:
1028 	case CE_LOGOUT_FAIL_SND:
1029 		idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
1030 		break;
1031 	case CE_LOGOUT_SUCCESS_SND:
1032 	case CE_LOGOUT_SUCCESS_RCV:
1033 	case CE_LOGOUT_SESSION_SUCCESS:
1034 		(void) untimeout(ic->ic_state_timeout);
1035 		/*FALLTHROUGH*/
1036 	case CE_CLEANUP_TIMEOUT:
1037 		idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
1038 		break;
1039 	case CE_LOGOUT_SUCCESS_SND_DONE:
1040 	case CE_LOGOUT_FAIL_SND_DONE:
1041 		pdu = (idm_pdu_t *)event_ctx->iec_info;
1042 		/* restore client callback */
1043 		pdu->isp_callback =  ic->ic_client_callback;
1044 		ic->ic_client_callback = NULL;
1045 		idm_pdu_complete(pdu, pdu->isp_status);
1046 		break;
1047 	case CE_TX_PROTOCOL_ERROR:
1048 	case CE_RX_PROTOCOL_ERROR:
1049 	case CE_MISC_TX:
1050 	case CE_MISC_RX:
1051 	case CE_LOGIN_TIMEOUT:
1052 	case CE_LOGOUT_TIMEOUT:
1053 		/* Don't care */
1054 		break;
1055 	default:
1056 		ASSERT(0);
1057 	}
1058 }
1059 
1060 /* ARGSUSED */
1061 static void
idm_state_s11_complete(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)1062 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1063 {
1064 	idm_pdu_t *pdu;
1065 
1066 	/*
1067 	 * Cleanup logout success/fail completion if it's been delayed
1068 	 * until now.
1069 	 *
1070 	 * All new events are filtered out before reaching this state, but
1071 	 * there might already be events in the event queue, so handle the
1072 	 * SND_DONE events here. Note that if either of the following
1073 	 * SND_DONE events happens AFTER the change to state S11, then the
1074 	 * event filter inside dm_conn_event_locked does enough cleanup.
1075 	 */
1076 	switch (event_ctx->iec_event) {
1077 	case CE_LOGOUT_SUCCESS_SND_DONE:
1078 	case CE_LOGOUT_FAIL_SND_DONE:
1079 		pdu = (idm_pdu_t *)event_ctx->iec_info;
1080 		/* restore client callback */
1081 		pdu->isp_callback =  ic->ic_client_callback;
1082 		ic->ic_client_callback = NULL;
1083 		idm_pdu_complete(pdu, pdu->isp_status);
1084 		break;
1085 	}
1086 
1087 }
1088 
1089 static void
idm_state_s12_enable_dm(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)1090 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1091 {
1092 	switch (event_ctx->iec_event) {
1093 	case CE_ENABLE_DM_SUCCESS:
1094 		/* T20 */
1095 		idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
1096 		break;
1097 	case CE_ENABLE_DM_FAIL:
1098 		/* T21 */
1099 		idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
1100 		break;
1101 	case CE_TRANSPORT_FAIL:
1102 		/*
1103 		 * We expect to always hear back from the transport layer
1104 		 * once we have an "enable data-mover" request outstanding.
1105 		 * Therefore we'll ignore other events that may occur even
1106 		 * when they clearly indicate a problem and wait for
1107 		 * CE_ENABLE_DM_FAIL.  On a related note this means the
1108 		 * transport must ensure that it eventually completes the
1109 		 * "enable data-mover" operation with either success or
1110 		 * failure -- otherwise we'll be stuck here.
1111 		 */
1112 		break;
1113 	default:
1114 		ASSERT(0);
1115 		break;
1116 	}
1117 }
1118 
1119 static void
idm_update_state(idm_conn_t * ic,idm_conn_state_t new_state,idm_conn_event_ctx_t * event_ctx)1120 idm_update_state(idm_conn_t *ic, idm_conn_state_t new_state,
1121     idm_conn_event_ctx_t *event_ctx)
1122 {
1123 	int rc;
1124 	idm_status_t idm_status;
1125 
1126 	/*
1127 	 * Validate new state
1128 	 */
1129 	ASSERT(new_state != CS_S0_UNDEFINED);
1130 	ASSERT3U(new_state, <, CS_MAX_STATE);
1131 
1132 	/*
1133 	 * Update state in context.  We protect this with a mutex
1134 	 * even though the state machine code is single threaded so that
1135 	 * other threads can check the state value atomically.
1136 	 */
1137 	new_state = (new_state < CS_MAX_STATE) ?
1138 	    new_state : CS_S0_UNDEFINED;
1139 
1140 	IDM_SM_LOG(CE_NOTE, "idm_update_state: conn %p, evt %s(%d), "
1141 	    "%s(%d) --> %s(%d)", (void *)ic,
1142 	    idm_ce_name[event_ctx->iec_event], event_ctx->iec_event,
1143 	    idm_cs_name[ic->ic_state], ic->ic_state,
1144 	    idm_cs_name[new_state], new_state);
1145 
1146 	DTRACE_PROBE2(conn__state__change,
1147 	    idm_conn_t *, ic, idm_conn_state_t, new_state);
1148 
1149 	mutex_enter(&ic->ic_state_mutex);
1150 	idm_sm_audit_state_change(&ic->ic_state_audit, SAS_IDM_CONN,
1151 	    (int)ic->ic_state, (int)new_state);
1152 	ic->ic_last_state = ic->ic_state;
1153 	ic->ic_state = new_state;
1154 	cv_signal(&ic->ic_state_cv);
1155 	mutex_exit(&ic->ic_state_mutex);
1156 
1157 	switch (ic->ic_state) {
1158 	case CS_S1_FREE:
1159 		ASSERT(0); /* Initial state, can't return */
1160 		break;
1161 	case CS_S2_XPT_WAIT:
1162 		if ((rc = idm_ini_conn_finish(ic)) != 0) {
1163 			idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1164 		} else {
1165 			idm_conn_event(ic, CE_CONNECT_SUCCESS, NULL);
1166 		}
1167 		break;
1168 	case CS_S3_XPT_UP:
1169 		/*
1170 		 * Finish any connection related setup including
1171 		 * waking up the idm_tgt_conn_accept thread.
1172 		 * and starting the login timer.  If the function
1173 		 * fails then we return to "free" state.
1174 		 */
1175 		if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) {
1176 			switch (rc) {
1177 			case IDM_STATUS_REJECT:
1178 				idm_conn_event(ic, CE_CONNECT_REJECT, NULL);
1179 				break;
1180 			default:
1181 				idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1182 				break;
1183 			}
1184 		}
1185 
1186 		/*
1187 		 * First login received will cause a transition to
1188 		 * CS_S4_IN_LOGIN.  Start login timer.
1189 		 */
1190 		ic->ic_state_timeout = timeout(idm_login_timeout, ic,
1191 		    drv_usectohz(IDM_LOGIN_SECONDS*1000000));
1192 		break;
1193 	case CS_S4_IN_LOGIN:
1194 		if (ic->ic_conn_type == CONN_TYPE_INI) {
1195 			(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
1196 			mutex_enter(&ic->ic_state_mutex);
1197 			ic->ic_state_flags |= CF_LOGIN_READY;
1198 			cv_signal(&ic->ic_state_cv);
1199 			mutex_exit(&ic->ic_state_mutex);
1200 		}
1201 		break;
1202 	case CS_S5_LOGGED_IN:
1203 		ASSERT(!ic->ic_ffp);
1204 		/*
1205 		 * IDM can go to FFP before the initiator but it
1206 		 * needs to go to FFP after the target (IDM target should
1207 		 * go to FFP after notify_ack).
1208 		 */
1209 		idm_status = idm_ffp_enable(ic);
1210 		if (idm_status != IDM_STATUS_SUCCESS) {
1211 			idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1212 		}
1213 
1214 		if (ic->ic_reinstate_conn) {
1215 			/* Connection reinstatement is complete */
1216 			idm_conn_event(ic->ic_reinstate_conn,
1217 			    CE_CONN_REINSTATE_SUCCESS, NULL);
1218 		}
1219 		break;
1220 	case CS_S6_IN_LOGOUT:
1221 		break;
1222 	case CS_S7_LOGOUT_REQ:
1223 		/* Start logout timer for target connections */
1224 		if (IDM_CONN_ISTGT(ic)) {
1225 			ic->ic_state_timeout = timeout(idm_logout_req_timeout,
1226 			    ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000));
1227 		}
1228 		break;
1229 	case CS_S8_CLEANUP:
1230 		/* Close connection (if it's not already closed) */
1231 		if (IDM_CONN_ISTGT(ic)) {
1232 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1233 		} else {
1234 			ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1235 		}
1236 
1237 		/* Stop executing active tasks */
1238 		idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND);
1239 
1240 		/* Start logout timer */
1241 		ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic,
1242 		    drv_usectohz(IDM_CLEANUP_SECONDS*1000000));
1243 		break;
1244 	case CS_S10_IN_CLEANUP:
1245 		break;
1246 	case CS_S9A_REJECTED:
1247 		/*
1248 		 * We never finished establishing the connection so no
1249 		 * disconnect.  No client notifications because the client
1250 		 * rejected the connection.
1251 		 */
1252 		idm_refcnt_async_wait_ref(&ic->ic_refcnt,
1253 		    &idm_conn_reject_unref);
1254 		break;
1255 	case CS_S9B_WAIT_SND_DONE:
1256 		break;
1257 	case CS_S9_INIT_ERROR:
1258 		if (IDM_CONN_ISTGT(ic)) {
1259 			ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1260 		} else {
1261 			mutex_enter(&ic->ic_state_mutex);
1262 			ic->ic_state_flags |= CF_ERROR;
1263 			ic->ic_conn_sm_status = IDM_STATUS_FAIL;
1264 			cv_signal(&ic->ic_state_cv);
1265 			mutex_exit(&ic->ic_state_mutex);
1266 			if (ic->ic_last_state != CS_S1_FREE &&
1267 			    ic->ic_last_state != CS_S2_XPT_WAIT) {
1268 				ic->ic_transport_ops->it_ini_conn_disconnect(
1269 				    ic);
1270 			} else {
1271 				(void) idm_notify_client(ic, CN_CONNECT_FAIL,
1272 				    NULL);
1273 			}
1274 		}
1275 		/*FALLTHROUGH*/
1276 	case CS_S11_COMPLETE:
1277 		/*
1278 		 * No more traffic on this connection.  If this is an
1279 		 * initiator connection and we weren't connected yet
1280 		 * then don't send the "connect lost" event.
1281 		 * It's useful to the initiator to know whether we were
1282 		 * logging in at the time so send that information in the
1283 		 * data field.
1284 		 */
1285 		if (IDM_CONN_ISTGT(ic) ||
1286 		    ((ic->ic_last_state != CS_S1_FREE) &&
1287 		    (ic->ic_last_state != CS_S2_XPT_WAIT))) {
1288 			(void) idm_notify_client(ic, CN_CONNECT_LOST,
1289 			    (uintptr_t)(ic->ic_last_state == CS_S4_IN_LOGIN));
1290 		}
1291 
1292 		/* Abort all tasks */
1293 		idm_task_abort(ic, NULL, AT_INTERNAL_ABORT);
1294 
1295 		/*
1296 		 * Handle terminal state actions on the global taskq so
1297 		 * we can clean up all the connection resources from
1298 		 * a separate thread context.
1299 		 */
1300 		idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref);
1301 		break;
1302 	case CS_S12_ENABLE_DM:
1303 
1304 		/*
1305 		 * The Enable DM state indicates the initiator to initiate
1306 		 * the hello sequence and the target to get ready to accept
1307 		 * the iSER Hello Message.
1308 		 */
1309 		idm_status = (IDM_CONN_ISINI(ic)) ?
1310 		    ic->ic_transport_ops->it_ini_enable_datamover(ic) :
1311 		    ic->ic_transport_ops->it_tgt_enable_datamover(ic);
1312 
1313 		if (idm_status == IDM_STATUS_SUCCESS) {
1314 			idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, NULL);
1315 		} else {
1316 			idm_conn_event(ic, CE_ENABLE_DM_FAIL, NULL);
1317 		}
1318 
1319 		break;
1320 
1321 	default:
1322 		ASSERT(0);
1323 		break;
1324 
1325 	}
1326 }
1327 
1328 
1329 static void
idm_conn_unref(void * ic_void)1330 idm_conn_unref(void *ic_void)
1331 {
1332 	idm_conn_t *ic = ic_void;
1333 
1334 	/*
1335 	 * Client should not be notified that the connection is destroyed
1336 	 * until all references on the idm connection have been removed.
1337 	 * Otherwise references on the associated client context would need
1338 	 * to be tracked separately which seems like a waste (at least when
1339 	 * there is a one for one correspondence with references on the
1340 	 * IDM connection).
1341 	 */
1342 	if (IDM_CONN_ISTGT(ic)) {
1343 		(void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1344 		idm_svc_conn_destroy(ic);
1345 	} else {
1346 		/* Initiator may destroy connection during this call */
1347 		(void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1348 	}
1349 }
1350 
1351 static void
idm_conn_reject_unref(void * ic_void)1352 idm_conn_reject_unref(void *ic_void)
1353 {
1354 	idm_conn_t *ic = ic_void;
1355 
1356 	ASSERT(IDM_CONN_ISTGT(ic));
1357 
1358 	/* Don't notify the client since it rejected the connection */
1359 	idm_svc_conn_destroy(ic);
1360 }
1361 
1362 
1363 
1364 static idm_pdu_event_action_t
idm_conn_sm_validate_pdu(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx,idm_pdu_t * pdu)1365 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx,
1366 	idm_pdu_t *pdu)
1367 {
1368 	char			*reason_string;
1369 	idm_pdu_event_action_t	action;
1370 
1371 	ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) ||
1372 	    (event_ctx->iec_pdu_event_type == CT_TX_PDU));
1373 
1374 	/*
1375 	 * Let's check the simple stuff first.  Make sure if this is a
1376 	 * target connection that the PDU is appropriate for a target
1377 	 * and if this is an initiator connection that the PDU is
1378 	 * appropriate for an initiator.  This code is not in the data
1379 	 * path so organization is more important than performance.
1380 	 */
1381 	switch (IDM_PDU_OPCODE(pdu)) {
1382 	case ISCSI_OP_NOOP_OUT:
1383 	case ISCSI_OP_SCSI_CMD:
1384 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1385 	case ISCSI_OP_LOGIN_CMD:
1386 	case ISCSI_OP_TEXT_CMD:
1387 	case ISCSI_OP_SCSI_DATA:
1388 	case ISCSI_OP_LOGOUT_CMD:
1389 	case ISCSI_OP_SNACK_CMD:
1390 		/*
1391 		 * Only the initiator should send these PDU's and
1392 		 * only the target should receive them.
1393 		 */
1394 		if (IDM_CONN_ISINI(ic) &&
1395 		    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1396 			reason_string = "Invalid RX PDU for initiator";
1397 			action = CA_RX_PROTOCOL_ERROR;
1398 			goto validate_pdu_done;
1399 		}
1400 
1401 		if (IDM_CONN_ISTGT(ic) &&
1402 		    (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1403 			reason_string = "Invalid TX PDU for target";
1404 			action = CA_TX_PROTOCOL_ERROR;
1405 			goto validate_pdu_done;
1406 		}
1407 		break;
1408 	case ISCSI_OP_NOOP_IN:
1409 	case ISCSI_OP_SCSI_RSP:
1410 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1411 	case ISCSI_OP_LOGIN_RSP:
1412 	case ISCSI_OP_TEXT_RSP:
1413 	case ISCSI_OP_SCSI_DATA_RSP:
1414 	case ISCSI_OP_LOGOUT_RSP:
1415 	case ISCSI_OP_RTT_RSP:
1416 	case ISCSI_OP_ASYNC_EVENT:
1417 	case ISCSI_OP_REJECT_MSG:
1418 		/*
1419 		 * Only the target should send these PDU's and
1420 		 * only the initiator should receive them.
1421 		 */
1422 		if (IDM_CONN_ISTGT(ic) &&
1423 		    (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1424 			reason_string = "Invalid RX PDU for target";
1425 			action = CA_RX_PROTOCOL_ERROR;
1426 			goto validate_pdu_done;
1427 		}
1428 
1429 		if (IDM_CONN_ISINI(ic) &&
1430 		    (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1431 			reason_string = "Invalid TX PDU for initiator";
1432 			action = CA_TX_PROTOCOL_ERROR;
1433 			goto validate_pdu_done;
1434 		}
1435 		break;
1436 	default:
1437 		reason_string = "Unknown PDU Type";
1438 		action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1439 		    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1440 		goto validate_pdu_done;
1441 	}
1442 
1443 	/*
1444 	 * Now validate the opcodes against the current state.
1445 	 */
1446 	reason_string = "PDU not allowed in current state";
1447 	switch (IDM_PDU_OPCODE(pdu)) {
1448 	case ISCSI_OP_NOOP_OUT:
1449 	case ISCSI_OP_NOOP_IN:
1450 		/*
1451 		 * Obviously S1-S3 are not allowed since login hasn't started.
1452 		 * S8 is probably out as well since the connection has been
1453 		 * dropped.
1454 		 */
1455 		switch (ic->ic_state) {
1456 		case CS_S4_IN_LOGIN:
1457 		case CS_S5_LOGGED_IN:
1458 		case CS_S6_IN_LOGOUT:
1459 		case CS_S7_LOGOUT_REQ:
1460 			action = CA_FORWARD;
1461 			goto validate_pdu_done;
1462 		case CS_S8_CLEANUP:
1463 		case CS_S10_IN_CLEANUP:
1464 			action = CA_DROP;
1465 			break;
1466 		default:
1467 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1468 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1469 			goto validate_pdu_done;
1470 		}
1471 		/*NOTREACHED*/
1472 	case ISCSI_OP_SCSI_CMD:
1473 	case ISCSI_OP_SCSI_RSP:
1474 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1475 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1476 	case ISCSI_OP_SCSI_DATA:
1477 	case ISCSI_OP_SCSI_DATA_RSP:
1478 	case ISCSI_OP_RTT_RSP:
1479 	case ISCSI_OP_SNACK_CMD:
1480 	case ISCSI_OP_TEXT_CMD:
1481 	case ISCSI_OP_TEXT_RSP:
1482 		switch (ic->ic_state) {
1483 		case CS_S5_LOGGED_IN:
1484 		case CS_S6_IN_LOGOUT:
1485 		case CS_S7_LOGOUT_REQ:
1486 			action = CA_FORWARD;
1487 			goto validate_pdu_done;
1488 		case CS_S8_CLEANUP:
1489 		case CS_S10_IN_CLEANUP:
1490 			action = CA_DROP;
1491 			break;
1492 		default:
1493 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1494 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1495 			goto validate_pdu_done;
1496 		}
1497 		/*NOTREACHED*/
1498 	case ISCSI_OP_LOGOUT_CMD:
1499 	case ISCSI_OP_LOGOUT_RSP:
1500 	case ISCSI_OP_REJECT_MSG:
1501 	case ISCSI_OP_ASYNC_EVENT:
1502 		switch (ic->ic_state) {
1503 		case CS_S5_LOGGED_IN:
1504 		case CS_S6_IN_LOGOUT:
1505 		case CS_S7_LOGOUT_REQ:
1506 			action = CA_FORWARD;
1507 			goto validate_pdu_done;
1508 		case CS_S8_CLEANUP:
1509 		case CS_S10_IN_CLEANUP:
1510 			action = CA_DROP;
1511 			break;
1512 		default:
1513 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1514 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1515 			goto validate_pdu_done;
1516 		}
1517 		/*NOTREACHED*/
1518 	case ISCSI_OP_LOGIN_CMD:
1519 	case ISCSI_OP_LOGIN_RSP:
1520 		switch (ic->ic_state) {
1521 		case CS_S3_XPT_UP:
1522 		case CS_S4_IN_LOGIN:
1523 			action = CA_FORWARD;
1524 			goto validate_pdu_done;
1525 		default:
1526 			action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1527 			    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1528 			goto validate_pdu_done;
1529 		}
1530 		/*NOTREACHED*/
1531 	default:
1532 		/* This should never happen -- we already checked above */
1533 		ASSERT(0);
1534 		/*NOTREACHED*/
1535 	}
1536 
1537 	action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1538 	    CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1539 
1540 validate_pdu_done:
1541 	if (action != CA_FORWARD) {
1542 		DTRACE_PROBE2(idm__int__protocol__error,
1543 		    idm_conn_event_ctx_t *, event_ctx,
1544 		    char *, reason_string);
1545 	}
1546 
1547 	return (action);
1548 }
1549 
1550 /* ARGSUSED */
1551 void
idm_pdu_tx_protocol_error(idm_conn_t * ic,idm_pdu_t * pdu)1552 idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1553 {
1554 	/*
1555 	 * Return the PDU to the caller indicating it was a protocol error.
1556 	 * Caller can take appropriate action.
1557 	 */
1558 	idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR);
1559 }
1560 
1561 void
idm_pdu_rx_protocol_error(idm_conn_t * ic,idm_pdu_t * pdu)1562 idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1563 {
1564 	/*
1565 	 * Forward PDU to caller indicating it is a protocol error.
1566 	 * Caller should take appropriate action.
1567 	 */
1568 	(*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR);
1569 }
1570 
1571 idm_status_t
idm_notify_client(idm_conn_t * ic,idm_client_notify_t cn,uintptr_t data)1572 idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data)
1573 {
1574 	/*
1575 	 * We may want to make this more complicated at some point but
1576 	 * for now lets just call the client's notify function and return
1577 	 * the status.
1578 	 */
1579 	ASSERT(!mutex_owned(&ic->ic_state_mutex));
1580 	cn = (cn > CN_MAX) ? CN_MAX : cn;
1581 	IDM_SM_LOG(CE_NOTE, "idm_notify_client: ic=%p %s(%d)\n",
1582 	    (void *)ic, idm_cn_strings[cn], cn);
1583 	return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data));
1584 }
1585 
1586 static idm_status_t
idm_ffp_enable(idm_conn_t * ic)1587 idm_ffp_enable(idm_conn_t *ic)
1588 {
1589 	idm_status_t rc;
1590 
1591 	/*
1592 	 * On the initiator side the client will see this notification
1593 	 * before the actual login succes PDU.  This shouldn't be a big
1594 	 * deal since the initiator drives the connection.  It can simply
1595 	 * wait for the login response then start sending SCSI commands.
1596 	 * Kind ugly though compared with the way things work on target
1597 	 * connections.
1598 	 */
1599 	mutex_enter(&ic->ic_state_mutex);
1600 	ic->ic_ffp = B_TRUE;
1601 	mutex_exit(&ic->ic_state_mutex);
1602 
1603 	rc = idm_notify_client(ic, CN_FFP_ENABLED, NULL);
1604 	if (rc != IDM_STATUS_SUCCESS) {
1605 		mutex_enter(&ic->ic_state_mutex);
1606 		ic->ic_ffp = B_FALSE;
1607 		mutex_exit(&ic->ic_state_mutex);
1608 	}
1609 	return (rc);
1610 }
1611 
1612 static void
idm_ffp_disable(idm_conn_t * ic,idm_ffp_disable_t disable_type)1613 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type)
1614 {
1615 	mutex_enter(&ic->ic_state_mutex);
1616 	ic->ic_ffp = B_FALSE;
1617 	mutex_exit(&ic->ic_state_mutex);
1618 
1619 	/* Client can't "fail" CN_FFP_DISABLED */
1620 	(void) idm_notify_client(ic, CN_FFP_DISABLED,
1621 	    (uintptr_t)disable_type);
1622 }
1623 
1624 static void
idm_initial_login_actions(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)1625 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1626 {
1627 	ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) ||
1628 	    (event_ctx->iec_event == CE_LOGIN_SND));
1629 
1630 	/*
1631 	 * Currently it's not clear what we would do here -- since
1632 	 * we went to the trouble of coding an "initial login" hook
1633 	 * we'll leave it in for now.  Remove before integration if
1634 	 * it's not used for anything.
1635 	 */
1636 	ic->ic_state_flags |= CF_INITIAL_LOGIN;
1637 }
1638 
1639 static void
idm_login_success_actions(idm_conn_t * ic,idm_conn_event_ctx_t * event_ctx)1640 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1641 {
1642 	idm_pdu_t		*pdu = (idm_pdu_t *)event_ctx->iec_info;
1643 	iscsi_login_hdr_t	*login_req =
1644 	    (iscsi_login_hdr_t *)pdu->isp_hdr;
1645 
1646 	ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) ||
1647 	    (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND));
1648 
1649 	/*
1650 	 * Save off CID
1651 	 */
1652 	mutex_enter(&ic->ic_state_mutex);
1653 	ic->ic_login_cid = ntohs(login_req->cid);
1654 	ic->ic_login_info_valid =  B_TRUE;
1655 
1656 	mutex_exit(&ic->ic_state_mutex);
1657 }
1658