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