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 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
26 */
27
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>
36
37 #define IDM_CONN_SM_STRINGS
38 #define IDM_CN_NOTIFY_STRINGS
39 #include <sys/idm/idm.h>
40
41 boolean_t idm_sm_logging = B_FALSE;
42
43 extern idm_global_t idm; /* Global state */
44
45 static void
46 idm_conn_event_handler(void *event_ctx_opaque);
47
48 static void
49 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
50
51 static void
52 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
53
54 static void
55 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
56
57 static void
58 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
59
60 static void
61 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
62
63 static void
64 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
65
66 static void
67 idm_logout_req_timeout(void *arg);
68
69 static void
70 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
71
72 static void
73 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
74
75 static void
76 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
77
78 static void
79 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
80
81 static void
82 idm_state_s9b_wait_snd_done_cb(idm_pdu_t *pdu,
83 idm_status_t status);
84
85 static void
86 idm_state_s9b_wait_snd_done(idm_conn_t *ic,
87 idm_conn_event_ctx_t *event_ctx);
88
89 static void
90 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
91
92 static void
93 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
94
95 static void
96 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
97
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);
101
102 static void
103 idm_conn_unref(void *ic_void);
104
105 static void
106 idm_conn_reject_unref(void *ic_void);
107
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);
111
112 static idm_status_t
113 idm_ffp_enable(idm_conn_t *ic);
114
115 static void
116 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type);
117
118 static void
119 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
120
121 static void
122 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx);
123
124 idm_status_t
125 idm_conn_sm_init(idm_conn_t *ic)
126 {
127 char taskq_name[32];
128
129 /*
130 * Caller should have assigned a unique connection ID. Use this
131 * connection ID to create a unique connection name string
132 */
133 ASSERT(ic->ic_internal_cid != 0);
134 (void) snprintf(taskq_name, sizeof (taskq_name) - 1, "conn_sm%08x",
135 ic->ic_internal_cid);
136
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);
141 }
142
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);
146
147 ic->ic_state = CS_S1_FREE;
148 ic->ic_last_state = CS_S1_FREE;
149
150 return (IDM_STATUS_SUCCESS);
151 }
152
153 void
154 idm_conn_sm_fini(idm_conn_t *ic)
155 {
156
157 /*
158 * The connection may only be partially created. If there
159 * is no taskq, then the connection SM was not initialized.
160 */
161 if (ic->ic_state_taskq == NULL) {
162 return;
163 }
164
165 taskq_destroy(ic->ic_state_taskq);
166
167 cv_destroy(&ic->ic_state_cv);
168 /*
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.
172 */
173 mutex_enter(&ic->ic_state_mutex);
174 IDM_SM_TIMER_CLEAR(ic);
175 mutex_destroy(&ic->ic_state_mutex);
176 }
177
178 void
179 idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info)
180 {
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);
184 }
185
186
187 idm_status_t
188 idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic)
189 {
190 int result;
191
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);
203 }
204 mutex_exit(&old_ic->ic_state_mutex);
205
206 return (result);
207 }
208
209 void
210 idm_conn_tx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
211 uintptr_t event_info)
212 {
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);
216 }
217
218 void
219 idm_conn_rx_pdu_event(idm_conn_t *ic, idm_conn_event_t event,
220 uintptr_t event_info)
221 {
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);
225 }
226
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)
230 {
231 idm_conn_event_ctx_t *event_ctx;
232
233 ASSERT(mutex_owned(&ic->ic_state_mutex));
234
235 idm_sm_audit_event(&ic->ic_state_audit, SAS_IDM_CONN,
236 (int)ic->ic_state, (int)event, event_info);
237
238 /*
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.
248 */
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);
257 }
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;
263 }
264
265 /*
266 * Normal event handling
267 */
268 idm_conn_hold(ic);
269
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;
275
276 (void) taskq_dispatch(ic->ic_state_taskq, &idm_conn_event_handler,
277 event_ctx, TQ_SLEEP);
278 }
279
280 static void
281 idm_conn_event_handler(void *event_ctx_opaque)
282 {
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;
287
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);
293
294 /*
295 * Validate event
296 */
297 ASSERT(event_ctx->iec_event != CE_UNDEFINED);
298 ASSERT3U(event_ctx->iec_event, <, CE_MAX_EVENT);
299
300 /*
301 * Validate current state
302 */
303 ASSERT(ic->ic_state != CS_S0_UNDEFINED);
304 ASSERT3U(ic->ic_state, <, CS_MAX_STATE);
305
306 /*
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.
313 */
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);
318
319 switch (action) {
320 case CA_TX_PROTOCOL_ERROR:
321 /*
322 * Change event and forward the PDU
323 */
324 event_ctx->iec_event = CE_TX_PROTOCOL_ERROR;
325 break;
326 case CA_RX_PROTOCOL_ERROR:
327 /*
328 * Change event and forward the PDU.
329 */
330 event_ctx->iec_event = CE_RX_PROTOCOL_ERROR;
331 break;
332 case CA_FORWARD:
333 /*
334 * Let the state-specific event handlers take
335 * care of it.
336 */
337 break;
338 case CA_DROP:
339 /*
340 * It never even happened
341 */
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;
348 }
349 }
350
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;
397 }
398
399 /*
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).
404 */
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);
420 }
421 }
422 break;
423 default:
424 ASSERT(0);
425 break;
426 }
427 }
428
429 /*
430 * Update outstanding PDU event count (see idm_pdu_tx for
431 * how this is used)
432 */
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);
438 }
439
440 idm_conn_rele(ic);
441 kmem_free(event_ctx, sizeof (*event_ctx));
442 }
443
444 static void
445 idm_state_s1_free(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
446 {
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*/
464 }
465 }
466
467
468 static void
469 idm_state_s2_xpt_wait(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
470 {
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*/
487 }
488 }
489
490
491 static void
492 idm_login_timeout(void *arg)
493 {
494 idm_conn_t *ic = arg;
495
496 ic->ic_state_timeout = 0;
497 idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL);
498 }
499
500 static void
501 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
502 {
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:
511 /*
512 * Don't need to cancel login timer since the timer is
513 * presumed to be the source of this event.
514 */
515 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
516 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
517 break;
518 case CE_CONNECT_REJECT:
519 /*
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.
524 */
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, 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*/
543 }
544 }
545
546 static void
547 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
548 {
549 idm_pdu_t *pdu;
550
551 /*
552 * Login timer should no longer be active after leaving this
553 * state.
554 */
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);
559
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);
568 }
569 break;
570 case CE_LOGIN_TIMEOUT:
571 /* T7 */
572 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
573 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
574 break;
575 case CE_LOGIN_FAIL_SND:
576 /*
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.
580 */
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);
592 /*
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.
596 */
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, NULL);
607 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
608 break;
609 case CE_LOGOUT_SESSION_SUCCESS:
610 /*
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.
616 */
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);
622 }
623 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
624 break;
625
626 case CE_LOGIN_SND:
627 ASSERT(ic->ic_client_callback == NULL);
628 /*
629 * Initiator connections will see initial login PDU
630 * in this state. Target connections see initial
631 * login PDU in "xpt up" state.
632 */
633 mutex_enter(&ic->ic_state_mutex);
634 if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) {
635 idm_initial_login_actions(ic, event_ctx);
636 }
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*/
649 }
650 }
651
652
653 static void
654 idm_state_s5_logged_in(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
655 {
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 */
676
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);
682 }
683
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);
708 }
709 }
710
711 static void
712 idm_state_s6_in_logout_success_snd_done(idm_pdu_t *pdu, idm_status_t status)
713 {
714 idm_conn_t *ic = pdu->isp_ic;
715
716 /*
717 * This pdu callback can be invoked by the tx thread,
718 * so run the disconnect code from another thread.
719 */
720 pdu->isp_status = status;
721 idm_conn_event(ic, CE_LOGOUT_SUCCESS_SND_DONE, (uintptr_t)pdu);
722 }
723
724 static void
725 idm_state_s6_in_logout_fail_snd_done(idm_pdu_t *pdu, idm_status_t status)
726 {
727 idm_conn_t *ic = pdu->isp_ic;
728
729 /*
730 * This pdu callback can be invoked by the tx thread,
731 * so run the disconnect code from another thread.
732 */
733 pdu->isp_status = status;
734 idm_conn_event(ic, CE_LOGOUT_FAIL_SND_DONE, (uintptr_t)pdu);
735 }
736
737 static void
738 idm_state_s6_in_logout(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
739 {
740 idm_pdu_t *pdu;
741
742 switch (event_ctx->iec_event) {
743 case CE_LOGOUT_SUCCESS_SND_DONE:
744 pdu = (idm_pdu_t *)event_ctx->iec_info;
745
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);
749
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:
766 /*
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.
770 */
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;
780 }
781 break;
782 case CE_LOGOUT_SUCCESS_RCV:
783 /*
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.
787 */
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 */
794
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);
800 }
801
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);
824 }
825 }
826
827
828 static void
829 idm_logout_req_timeout(void *arg)
830 {
831 idm_conn_t *ic = arg;
832
833 ic->ic_state_timeout = 0;
834 idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL);
835 }
836
837 static void
838 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
839 {
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);
849 }
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);
858 }
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);
874 }
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);
884 }
885 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
886
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);
892 }
893
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);
905 }
906 }
907
908
909 static void
910 idm_cleanup_timeout(void *arg)
911 {
912 idm_conn_t *ic = arg;
913
914 ic->ic_state_timeout = 0;
915 idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL);
916 }
917
918 static void
919 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
920 {
921 idm_pdu_t *pdu;
922
923 /*
924 * Need to cancel the cleanup timeout before leaving this state
925 * if it hasn't already fired.
926 */
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);
963 }
964 }
965
966 /* ARGSUSED */
967 static void
968 idm_state_s9_init_error(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
969 {
970 /* All events ignored in this state */
971 }
972
973 /* ARGSUSED */
974 static void
975 idm_state_s9a_rejected(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
976 {
977 /* All events ignored in this state */
978 }
979
980
981 static void
982 idm_state_s9b_wait_snd_done_cb(idm_pdu_t *pdu, idm_status_t status)
983 {
984 idm_conn_t *ic = pdu->isp_ic;
985
986 /*
987 * This pdu callback can be invoked by the tx thread,
988 * so run the disconnect code from another thread.
989 */
990 pdu->isp_status = status;
991 idm_conn_event(ic, CE_LOGIN_FAIL_SND_DONE, (uintptr_t)pdu);
992 }
993
994 /*
995 * CS_S9B_WAIT_SND_DONE -- wait for callback completion.
996 */
997 /* ARGSUSED */
998 static void
999 idm_state_s9b_wait_snd_done(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1000 {
1001 idm_pdu_t *pdu;
1002 /*
1003 * Wait for completion of the login fail sequence and then
1004 * go to state S9_INIT_ERROR to clean up the connection.
1005 */
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;
1015
1016 /* All other events ignored */
1017 }
1018 }
1019
1020
1021
1022
1023 static void
1024 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1025 {
1026 idm_pdu_t *pdu;
1027
1028 /*
1029 * Need to cancel the cleanup timeout before leaving this state
1030 * if it hasn't already fired.
1031 */
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);
1063 }
1064 }
1065
1066 /* ARGSUSED */
1067 static void
1068 idm_state_s11_complete(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1069 {
1070 idm_pdu_t *pdu;
1071
1072 /*
1073 * Cleanup logout success/fail completion if it's been delayed
1074 * until now.
1075 *
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.
1081 */
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;
1091 }
1092
1093 }
1094
1095 static void
1096 idm_state_s12_enable_dm(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1097 {
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:
1108 /*
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.
1117 */
1118 break;
1119 default:
1120 ASSERT(0);
1121 break;
1122 }
1123 }
1124
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)
1128 {
1129 int rc;
1130 idm_status_t idm_status;
1131
1132 /*
1133 * Validate new state
1134 */
1135 ASSERT(new_state != CS_S0_UNDEFINED);
1136 ASSERT3U(new_state, <, CS_MAX_STATE);
1137
1138 /*
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.
1142 */
1143 new_state = (new_state < CS_MAX_STATE) ?
1144 new_state : CS_S0_UNDEFINED;
1145
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);
1151
1152 DTRACE_PROBE2(conn__state__change,
1153 idm_conn_t *, ic, idm_conn_state_t, new_state);
1154
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);
1162
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, NULL);
1170 } else {
1171 idm_conn_event(ic, CE_CONNECT_SUCCESS, NULL);
1172 }
1173 break;
1174 case CS_S3_XPT_UP:
1175 /*
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.
1180 */
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, NULL);
1185 break;
1186 default:
1187 idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1188 break;
1189 }
1190 }
1191
1192 /*
1193 * First login received will cause a transition to
1194 * CS_S4_IN_LOGIN. Start login timer.
1195 */
1196 IDM_SM_TIMER_CHECK(ic);
1197 ic->ic_state_timeout = timeout(idm_login_timeout, ic,
1198 drv_usectohz(IDM_LOGIN_SECONDS*1000000));
1199 break;
1200 case CS_S4_IN_LOGIN:
1201 if (ic->ic_conn_type == CONN_TYPE_INI) {
1202 (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
1203 mutex_enter(&ic->ic_state_mutex);
1204 ic->ic_state_flags |= CF_LOGIN_READY;
1205 cv_signal(&ic->ic_state_cv);
1206 mutex_exit(&ic->ic_state_mutex);
1207 }
1208 break;
1209 case CS_S5_LOGGED_IN:
1210 ASSERT(!ic->ic_ffp);
1211 /*
1212 * IDM can go to FFP before the initiator but it
1213 * needs to go to FFP after the target (IDM target should
1214 * go to FFP after notify_ack).
1215 */
1216 idm_status = idm_ffp_enable(ic);
1217 if (idm_status != IDM_STATUS_SUCCESS) {
1218 idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1219 }
1220
1221 if (ic->ic_reinstate_conn) {
1222 /* Connection reinstatement is complete */
1223 idm_conn_event(ic->ic_reinstate_conn,
1224 CE_CONN_REINSTATE_SUCCESS, NULL);
1225 }
1226 break;
1227 case CS_S6_IN_LOGOUT:
1228 break;
1229 case CS_S7_LOGOUT_REQ:
1230 /* Start logout timer for target connections */
1231 if (IDM_CONN_ISTGT(ic)) {
1232 IDM_SM_TIMER_CHECK(ic);
1233 ic->ic_state_timeout = timeout(idm_logout_req_timeout,
1234 ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000));
1235 }
1236 break;
1237 case CS_S8_CLEANUP:
1238 /* Close connection (if it's not already closed) */
1239 if (IDM_CONN_ISTGT(ic)) {
1240 ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1241 } else {
1242 ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1243 }
1244
1245 /* Stop executing active tasks */
1246 (void) idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND);
1247
1248 /* Start logout timer */
1249 IDM_SM_TIMER_CHECK(ic);
1250 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic,
1251 drv_usectohz(IDM_CLEANUP_SECONDS*1000000));
1252 break;
1253 case CS_S10_IN_CLEANUP:
1254 break;
1255 case CS_S9A_REJECTED:
1256 /*
1257 * We never finished establishing the connection so no
1258 * disconnect. No client notifications because the client
1259 * rejected the connection.
1260 */
1261 idm_refcnt_async_wait_ref(&ic->ic_refcnt,
1262 &idm_conn_reject_unref);
1263 break;
1264 case CS_S9B_WAIT_SND_DONE:
1265 break;
1266 case CS_S9_INIT_ERROR:
1267 if (IDM_CONN_ISTGT(ic)) {
1268 ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1269 } else {
1270 mutex_enter(&ic->ic_state_mutex);
1271 ic->ic_state_flags |= CF_ERROR;
1272 ic->ic_conn_sm_status = IDM_STATUS_FAIL;
1273 cv_signal(&ic->ic_state_cv);
1274 mutex_exit(&ic->ic_state_mutex);
1275 if (ic->ic_last_state != CS_S1_FREE &&
1276 ic->ic_last_state != CS_S2_XPT_WAIT) {
1277 ic->ic_transport_ops->it_ini_conn_disconnect(
1278 ic);
1279 } else {
1280 (void) idm_notify_client(ic, CN_CONNECT_FAIL,
1281 NULL);
1282 }
1283 }
1284 /*FALLTHROUGH*/
1285 case CS_S11_COMPLETE:
1286 /*
1287 * No more traffic on this connection. If this is an
1288 * initiator connection and we weren't connected yet
1289 * then don't send the "connect lost" event.
1290 * It's useful to the initiator to know whether we were
1291 * logging in at the time so send that information in the
1292 * data field.
1293 */
1294 if (IDM_CONN_ISTGT(ic) ||
1295 ((ic->ic_last_state != CS_S1_FREE) &&
1296 (ic->ic_last_state != CS_S2_XPT_WAIT))) {
1297 (void) idm_notify_client(ic, CN_CONNECT_LOST,
1298 (uintptr_t)(ic->ic_last_state == CS_S4_IN_LOGIN));
1299 }
1300
1301 /* Abort all tasks */
1302 (void) idm_task_abort(ic, NULL, AT_INTERNAL_ABORT);
1303
1304 /*
1305 * Handle terminal state actions on the global taskq so
1306 * we can clean up all the connection resources from
1307 * a separate thread context.
1308 */
1309 idm_refcnt_async_wait_ref(&ic->ic_refcnt, &idm_conn_unref);
1310 break;
1311 case CS_S12_ENABLE_DM:
1312
1313 /*
1314 * The Enable DM state indicates the initiator to initiate
1315 * the hello sequence and the target to get ready to accept
1316 * the iSER Hello Message.
1317 */
1318 idm_status = (IDM_CONN_ISINI(ic)) ?
1319 ic->ic_transport_ops->it_ini_enable_datamover(ic) :
1320 ic->ic_transport_ops->it_tgt_enable_datamover(ic);
1321
1322 if (idm_status == IDM_STATUS_SUCCESS) {
1323 idm_conn_event(ic, CE_ENABLE_DM_SUCCESS, NULL);
1324 } else {
1325 idm_conn_event(ic, CE_ENABLE_DM_FAIL, NULL);
1326 }
1327
1328 break;
1329
1330 default:
1331 ASSERT(0);
1332 break;
1333
1334 }
1335 }
1336
1337
1338 static void
1339 idm_conn_unref(void *ic_void)
1340 {
1341 idm_conn_t *ic = ic_void;
1342
1343 /*
1344 * Client should not be notified that the connection is destroyed
1345 * until all references on the idm connection have been removed.
1346 * Otherwise references on the associated client context would need
1347 * to be tracked separately which seems like a waste (at least when
1348 * there is a one for one correspondence with references on the
1349 * IDM connection).
1350 */
1351 if (IDM_CONN_ISTGT(ic)) {
1352 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1353 idm_svc_conn_destroy(ic);
1354 } else {
1355 /* Initiator may destroy connection during this call */
1356 (void) idm_notify_client(ic, CN_CONNECT_DESTROY, NULL);
1357 }
1358 }
1359
1360 static void
1361 idm_conn_reject_unref(void *ic_void)
1362 {
1363 idm_conn_t *ic = ic_void;
1364
1365 ASSERT(IDM_CONN_ISTGT(ic));
1366
1367 /* Don't notify the client since it rejected the connection */
1368 idm_svc_conn_destroy(ic);
1369 }
1370
1371
1372
1373 static idm_pdu_event_action_t
1374 idm_conn_sm_validate_pdu(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx,
1375 idm_pdu_t *pdu)
1376 {
1377 char *reason_string;
1378 idm_pdu_event_action_t action;
1379
1380 ASSERT((event_ctx->iec_pdu_event_type == CT_RX_PDU) ||
1381 (event_ctx->iec_pdu_event_type == CT_TX_PDU));
1382
1383 /*
1384 * Let's check the simple stuff first. Make sure if this is a
1385 * target connection that the PDU is appropriate for a target
1386 * and if this is an initiator connection that the PDU is
1387 * appropriate for an initiator. This code is not in the data
1388 * path so organization is more important than performance.
1389 */
1390 switch (IDM_PDU_OPCODE(pdu)) {
1391 case ISCSI_OP_NOOP_OUT:
1392 case ISCSI_OP_SCSI_CMD:
1393 case ISCSI_OP_SCSI_TASK_MGT_MSG:
1394 case ISCSI_OP_LOGIN_CMD:
1395 case ISCSI_OP_TEXT_CMD:
1396 case ISCSI_OP_SCSI_DATA:
1397 case ISCSI_OP_LOGOUT_CMD:
1398 case ISCSI_OP_SNACK_CMD:
1399 /*
1400 * Only the initiator should send these PDU's and
1401 * only the target should receive them.
1402 */
1403 if (IDM_CONN_ISINI(ic) &&
1404 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1405 reason_string = "Invalid RX PDU for initiator";
1406 action = CA_RX_PROTOCOL_ERROR;
1407 goto validate_pdu_done;
1408 }
1409
1410 if (IDM_CONN_ISTGT(ic) &&
1411 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1412 reason_string = "Invalid TX PDU for target";
1413 action = CA_TX_PROTOCOL_ERROR;
1414 goto validate_pdu_done;
1415 }
1416 break;
1417 case ISCSI_OP_NOOP_IN:
1418 case ISCSI_OP_SCSI_RSP:
1419 case ISCSI_OP_SCSI_TASK_MGT_RSP:
1420 case ISCSI_OP_LOGIN_RSP:
1421 case ISCSI_OP_TEXT_RSP:
1422 case ISCSI_OP_SCSI_DATA_RSP:
1423 case ISCSI_OP_LOGOUT_RSP:
1424 case ISCSI_OP_RTT_RSP:
1425 case ISCSI_OP_ASYNC_EVENT:
1426 case ISCSI_OP_REJECT_MSG:
1427 /*
1428 * Only the target should send these PDU's and
1429 * only the initiator should receive them.
1430 */
1431 if (IDM_CONN_ISTGT(ic) &&
1432 (event_ctx->iec_pdu_event_type == CT_RX_PDU)) {
1433 reason_string = "Invalid RX PDU for target";
1434 action = CA_RX_PROTOCOL_ERROR;
1435 goto validate_pdu_done;
1436 }
1437
1438 if (IDM_CONN_ISINI(ic) &&
1439 (event_ctx->iec_pdu_event_type == CT_TX_PDU)) {
1440 reason_string = "Invalid TX PDU for initiator";
1441 action = CA_TX_PROTOCOL_ERROR;
1442 goto validate_pdu_done;
1443 }
1444 break;
1445 default:
1446 reason_string = "Unknown PDU Type";
1447 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1448 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1449 goto validate_pdu_done;
1450 }
1451
1452 /*
1453 * Now validate the opcodes against the current state.
1454 */
1455 reason_string = "PDU not allowed in current state";
1456 switch (IDM_PDU_OPCODE(pdu)) {
1457 case ISCSI_OP_NOOP_OUT:
1458 case ISCSI_OP_NOOP_IN:
1459 /*
1460 * Obviously S1-S3 are not allowed since login hasn't started.
1461 * S8 is probably out as well since the connection has been
1462 * dropped.
1463 */
1464 switch (ic->ic_state) {
1465 case CS_S4_IN_LOGIN:
1466 case CS_S5_LOGGED_IN:
1467 case CS_S6_IN_LOGOUT:
1468 case CS_S7_LOGOUT_REQ:
1469 action = CA_FORWARD;
1470 goto validate_pdu_done;
1471 case CS_S8_CLEANUP:
1472 case CS_S10_IN_CLEANUP:
1473 action = CA_DROP;
1474 break;
1475 default:
1476 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1477 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1478 goto validate_pdu_done;
1479 }
1480 /*NOTREACHED*/
1481 case ISCSI_OP_SCSI_CMD:
1482 case ISCSI_OP_SCSI_RSP:
1483 case ISCSI_OP_SCSI_TASK_MGT_MSG:
1484 case ISCSI_OP_SCSI_TASK_MGT_RSP:
1485 case ISCSI_OP_SCSI_DATA:
1486 case ISCSI_OP_SCSI_DATA_RSP:
1487 case ISCSI_OP_RTT_RSP:
1488 case ISCSI_OP_SNACK_CMD:
1489 case ISCSI_OP_TEXT_CMD:
1490 case ISCSI_OP_TEXT_RSP:
1491 switch (ic->ic_state) {
1492 case CS_S5_LOGGED_IN:
1493 case CS_S6_IN_LOGOUT:
1494 case CS_S7_LOGOUT_REQ:
1495 action = CA_FORWARD;
1496 goto validate_pdu_done;
1497 case CS_S8_CLEANUP:
1498 case CS_S10_IN_CLEANUP:
1499 action = CA_DROP;
1500 break;
1501 default:
1502 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1503 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1504 goto validate_pdu_done;
1505 }
1506 /*NOTREACHED*/
1507 case ISCSI_OP_LOGOUT_CMD:
1508 case ISCSI_OP_LOGOUT_RSP:
1509 case ISCSI_OP_REJECT_MSG:
1510 case ISCSI_OP_ASYNC_EVENT:
1511 switch (ic->ic_state) {
1512 case CS_S5_LOGGED_IN:
1513 case CS_S6_IN_LOGOUT:
1514 case CS_S7_LOGOUT_REQ:
1515 action = CA_FORWARD;
1516 goto validate_pdu_done;
1517 case CS_S8_CLEANUP:
1518 case CS_S10_IN_CLEANUP:
1519 action = CA_DROP;
1520 break;
1521 default:
1522 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1523 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1524 goto validate_pdu_done;
1525 }
1526 /*NOTREACHED*/
1527 case ISCSI_OP_LOGIN_CMD:
1528 case ISCSI_OP_LOGIN_RSP:
1529 switch (ic->ic_state) {
1530 case CS_S3_XPT_UP:
1531 case CS_S4_IN_LOGIN:
1532 action = CA_FORWARD;
1533 goto validate_pdu_done;
1534 default:
1535 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1536 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1537 goto validate_pdu_done;
1538 }
1539 /*NOTREACHED*/
1540 default:
1541 /* This should never happen -- we already checked above */
1542 ASSERT(0);
1543 /*NOTREACHED*/
1544 }
1545
1546 action = ((event_ctx->iec_pdu_event_type == CT_TX_PDU) ?
1547 CA_TX_PROTOCOL_ERROR : CA_RX_PROTOCOL_ERROR);
1548
1549 validate_pdu_done:
1550 if (action != CA_FORWARD) {
1551 DTRACE_PROBE2(idm__int__protocol__error,
1552 idm_conn_event_ctx_t *, event_ctx,
1553 char *, reason_string);
1554 }
1555
1556 return (action);
1557 }
1558
1559 /* ARGSUSED */
1560 void
1561 idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1562 {
1563 /*
1564 * Return the PDU to the caller indicating it was a protocol error.
1565 * Caller can take appropriate action.
1566 */
1567 idm_pdu_complete(pdu, IDM_STATUS_PROTOCOL_ERROR);
1568 }
1569
1570 void
1571 idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu)
1572 {
1573 /*
1574 * Forward PDU to caller indicating it is a protocol error.
1575 * Caller should take appropriate action.
1576 */
1577 (*ic->ic_conn_ops.icb_rx_error)(ic, pdu, IDM_STATUS_PROTOCOL_ERROR);
1578 }
1579
1580 idm_status_t
1581 idm_notify_client(idm_conn_t *ic, idm_client_notify_t cn, uintptr_t data)
1582 {
1583 /*
1584 * We may want to make this more complicated at some point but
1585 * for now lets just call the client's notify function and return
1586 * the status.
1587 */
1588 ASSERT(!mutex_owned(&ic->ic_state_mutex));
1589 cn = (cn > CN_MAX) ? CN_MAX : cn;
1590 IDM_SM_LOG(CE_NOTE, "idm_notify_client: ic=%p %s(%d)\n",
1591 (void *)ic, idm_cn_strings[cn], cn);
1592 return ((*ic->ic_conn_ops.icb_client_notify)(ic, cn, data));
1593 }
1594
1595 static idm_status_t
1596 idm_ffp_enable(idm_conn_t *ic)
1597 {
1598 idm_status_t rc;
1599
1600 /*
1601 * On the initiator side the client will see this notification
1602 * before the actual login succes PDU. This shouldn't be a big
1603 * deal since the initiator drives the connection. It can simply
1604 * wait for the login response then start sending SCSI commands.
1605 * Kind ugly though compared with the way things work on target
1606 * connections.
1607 */
1608 mutex_enter(&ic->ic_state_mutex);
1609 ic->ic_ffp = B_TRUE;
1610 mutex_exit(&ic->ic_state_mutex);
1611
1612 rc = idm_notify_client(ic, CN_FFP_ENABLED, NULL);
1613 if (rc != IDM_STATUS_SUCCESS) {
1614 mutex_enter(&ic->ic_state_mutex);
1615 ic->ic_ffp = B_FALSE;
1616 mutex_exit(&ic->ic_state_mutex);
1617 }
1618 return (rc);
1619 }
1620
1621 static void
1622 idm_ffp_disable(idm_conn_t *ic, idm_ffp_disable_t disable_type)
1623 {
1624 mutex_enter(&ic->ic_state_mutex);
1625 ic->ic_ffp = B_FALSE;
1626 mutex_exit(&ic->ic_state_mutex);
1627
1628 /* Client can't "fail" CN_FFP_DISABLED */
1629 (void) idm_notify_client(ic, CN_FFP_DISABLED,
1630 (uintptr_t)disable_type);
1631 }
1632
1633 static void
1634 idm_initial_login_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1635 {
1636 ASSERT((event_ctx->iec_event == CE_LOGIN_RCV) ||
1637 (event_ctx->iec_event == CE_LOGIN_SND));
1638
1639 /*
1640 * Currently it's not clear what we would do here -- since
1641 * we went to the trouble of coding an "initial login" hook
1642 * we'll leave it in for now. Remove before integration if
1643 * it's not used for anything.
1644 */
1645 ic->ic_state_flags |= CF_INITIAL_LOGIN;
1646 }
1647
1648 static void
1649 idm_login_success_actions(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1650 {
1651 idm_pdu_t *pdu = (idm_pdu_t *)event_ctx->iec_info;
1652 iscsi_login_hdr_t *login_req =
1653 (iscsi_login_hdr_t *)pdu->isp_hdr;
1654
1655 ASSERT((event_ctx->iec_event == CE_LOGIN_SUCCESS_RCV) ||
1656 (event_ctx->iec_event == CE_LOGIN_SUCCESS_SND));
1657
1658 /*
1659 * Save off CID
1660 */
1661 mutex_enter(&ic->ic_state_mutex);
1662 ic->ic_login_cid = ntohs(login_req->cid);
1663 ic->ic_login_info_valid = B_TRUE;
1664
1665 mutex_exit(&ic->ic_state_mutex);
1666 }