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