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