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