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 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/cpuvar.h>
26 #include <sys/types.h>
27 #include <sys/conf.h>
28 #include <sys/file.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/sysmacros.h>
33 #include <sys/scsi/generic/persist.h>
34
35 #include <sys/socket.h>
36 #include <sys/strsubr.h>
37 #include <sys/note.h>
38 #include <sys/sdt.h>
39
40 #include <sys/stmf.h>
41 #include <sys/stmf_ioctl.h>
42 #include <sys/portif.h>
43 #include <sys/idm/idm.h>
44
45 #define ISCSIT_SESS_SM_STRINGS
46 #include "iscsit.h"
47
48 typedef struct {
49 list_node_t se_ctx_node;
50 iscsit_session_event_t se_ctx_event;
51 iscsit_conn_t *se_event_data;
52 } sess_event_ctx_t;
53
54 static void
55 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event,
56 iscsit_conn_t *ict);
57
58 static void
59 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
60
61 static void
62 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
63
64 static void
65 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
66
67 static void
68 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
69
70 static void
71 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
72
73 static void
74 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
75
76 static void
77 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
78
79 static void
80 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx);
81
82 static void
83 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx,
84 iscsit_session_state_t new_state);
85
86 static int
87 iscsit_task_itt_compare(const void *void_task1, const void *void_task2);
88
89 static uint16_t
90 iscsit_tsih_alloc(void)
91 {
92 uintptr_t result;
93
94 result = (uintptr_t)vmem_alloc(iscsit_global.global_tsih_pool,
95 1, VM_NOSLEEP | VM_NEXTFIT);
96
97 /* ISCSI_UNSPEC_TSIH (0) indicates failure */
98 if (result > ISCSI_MAX_TSIH) {
99 vmem_free(iscsit_global.global_tsih_pool, (void *)result, 1);
100 result = ISCSI_UNSPEC_TSIH;
101 }
102
103 return ((uint16_t)result);
104 }
105
106 static void
107 iscsit_tsih_free(uint16_t tsih)
108 {
109 vmem_free(iscsit_global.global_tsih_pool, (void *)(uintptr_t)tsih, 1);
110 }
111
112
113 iscsit_sess_t *
114 iscsit_sess_create(iscsit_tgt_t *tgt, iscsit_conn_t *ict,
115 uint32_t cmdsn, uint8_t *isid, uint16_t tag,
116 char *initiator_name, char *target_name,
117 uint8_t *error_class, uint8_t *error_detail)
118 {
119 iscsit_sess_t *result;
120
121 *error_class = ISCSI_STATUS_CLASS_SUCCESS;
122
123 /*
124 * Even if this session create "fails" for some reason we still need
125 * to return a valid session pointer so that we can send the failed
126 * login response.
127 */
128 result = kmem_zalloc(sizeof (*result), KM_SLEEP);
129
130 /* Allocate TSIH */
131 if ((result->ist_tsih = iscsit_tsih_alloc()) == ISCSI_UNSPEC_TSIH) {
132 /* Out of TSIH's */
133 *error_class = ISCSI_STATUS_CLASS_TARGET_ERR;
134 *error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
135 /*
136 * Continue initializing this session so we can use it
137 * to complete the login process.
138 */
139 }
140
141 idm_sm_audit_init(&result->ist_state_audit);
142 mutex_init(&result->ist_sn_mutex, NULL, MUTEX_DEFAULT, NULL);
143 mutex_init(&result->ist_mutex, NULL, MUTEX_DEFAULT, NULL);
144 cv_init(&result->ist_cv, NULL, CV_DEFAULT, NULL);
145 list_create(&result->ist_events, sizeof (sess_event_ctx_t),
146 offsetof(sess_event_ctx_t, se_ctx_node));
147 list_create(&result->ist_conn_list, sizeof (iscsit_conn_t),
148 offsetof(iscsit_conn_t, ict_sess_ln));
149 avl_create(&result->ist_task_list, iscsit_task_itt_compare,
150 sizeof (iscsit_task_t), offsetof(iscsit_task_t, it_sess_ln));
151 result->ist_rxpdu_queue = kmem_zalloc(sizeof (iscsit_cbuf_t), KM_SLEEP);
152 result->ist_state = SS_Q1_FREE;
153 result->ist_last_state = SS_Q1_FREE;
154 bcopy(isid, result->ist_isid, ISCSI_ISID_LEN);
155 result->ist_tpgt_tag = tag;
156
157 result->ist_tgt = tgt;
158 /*
159 * cmdsn/expcmdsn do not advance during login phase.
160 */
161 result->ist_expcmdsn = cmdsn;
162 result->ist_maxcmdsn = result->ist_expcmdsn + 1;
163
164 result->ist_initiator_name =
165 kmem_alloc(strlen(initiator_name) + 1, KM_SLEEP);
166 (void) strcpy(result->ist_initiator_name, initiator_name);
167 if (target_name) {
168 /* A discovery session might not have a target name */
169 result->ist_target_name =
170 kmem_alloc(strlen(target_name) + 1, KM_SLEEP);
171 (void) strcpy(result->ist_target_name, target_name);
172 }
173 idm_refcnt_init(&result->ist_refcnt, result);
174
175 /* Login code will fill in ist_stmf_sess if necessary */
176
177 if (*error_class == ISCSI_STATUS_CLASS_SUCCESS) {
178 /*
179 * Make sure the service is still enabled and if so get a global
180 * hold to represent this session.
181 */
182 mutex_enter(&iscsit_global.global_state_mutex);
183 if (iscsit_global.global_svc_state == ISE_ENABLED) {
184 iscsit_global_hold();
185 mutex_exit(&iscsit_global.global_state_mutex);
186
187 /*
188 * Kick session state machine (also binds connection
189 * to session)
190 */
191 iscsit_sess_sm_event(result, SE_CONN_IN_LOGIN, ict);
192
193 *error_class = ISCSI_STATUS_CLASS_SUCCESS;
194 } else {
195 mutex_exit(&iscsit_global.global_state_mutex);
196 *error_class = ISCSI_STATUS_CLASS_TARGET_ERR;
197 *error_detail = ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE;
198 }
199 }
200
201 /*
202 * As noted above we must return a session pointer even if something
203 * failed. The resources will get freed later.
204 */
205 return (result);
206 }
207
208 static void
209 iscsit_sess_unref(void *ist_void)
210 {
211 iscsit_sess_t *ist = ist_void;
212 stmf_scsi_session_t *iss;
213
214 /*
215 * State machine has run to completion, destroy session
216 *
217 * If we have an associated STMF session we should clean it
218 * up now.
219 *
220 * This session is no longer associated with a target at this
221 * point so don't touch the target.
222 */
223 mutex_enter(&ist->ist_mutex);
224 ASSERT(ist->ist_conn_count == 0);
225 iss = ist->ist_stmf_sess;
226 if (iss != NULL) {
227 stmf_deregister_scsi_session(ist->ist_lport, iss);
228 kmem_free(iss->ss_rport_id, sizeof (scsi_devid_desc_t) +
229 strlen(ist->ist_initiator_name) + 1);
230 stmf_remote_port_free(iss->ss_rport);
231 stmf_free(iss);
232 }
233 mutex_exit(&ist->ist_mutex);
234
235 iscsit_sess_destroy(ist);
236 iscsit_global_rele();
237 }
238
239 void
240 iscsit_sess_destroy(iscsit_sess_t *ist)
241 {
242 idm_refcnt_destroy(&ist->ist_refcnt);
243 if (ist->ist_initiator_name)
244 kmem_free(ist->ist_initiator_name,
245 strlen(ist->ist_initiator_name) + 1);
246 if (ist->ist_initiator_alias)
247 kmem_free(ist->ist_initiator_alias,
248 strlen(ist->ist_initiator_alias) + 1);
249 if (ist->ist_target_name)
250 kmem_free(ist->ist_target_name,
251 strlen(ist->ist_target_name) + 1);
252 if (ist->ist_target_alias)
253 kmem_free(ist->ist_target_alias,
254 strlen(ist->ist_target_alias) + 1);
255 avl_destroy(&ist->ist_task_list);
256 kmem_free(ist->ist_rxpdu_queue, sizeof (iscsit_cbuf_t));
257 list_destroy(&ist->ist_conn_list);
258 list_destroy(&ist->ist_events);
259 cv_destroy(&ist->ist_cv);
260 mutex_destroy(&ist->ist_mutex);
261 mutex_destroy(&ist->ist_sn_mutex);
262 kmem_free(ist, sizeof (*ist));
263 }
264
265 void
266 iscsit_sess_close(iscsit_sess_t *ist)
267 {
268 iscsit_conn_t *ict;
269
270 mutex_enter(&ist->ist_mutex);
271 /*
272 * Note in the session state that we are forcing this session
273 * to close so that the session state machine can avoid
274 * pointless delays like transitions to SS_Q4_FAILED state.
275 */
276 ist->ist_admin_close = B_TRUE;
277 if (ist->ist_state == SS_Q3_LOGGED_IN) {
278 for (ict = list_head(&ist->ist_conn_list);
279 ict != NULL;
280 ict = list_next(&ist->ist_conn_list, ict)) {
281 iscsit_send_async_event(ict,
282 ISCSI_ASYNC_EVENT_REQUEST_LOGOUT);
283 }
284 }
285 mutex_exit(&ist->ist_mutex);
286 }
287
288
289 void
290 iscsit_sess_bind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
291 {
292 iscsit_conn_hold(ict);
293 iscsit_sess_hold(ist);
294 ict->ict_sess = ist;
295 mutex_enter(&ist->ist_mutex);
296 ist->ist_conn_count++;
297 list_insert_tail(&ist->ist_conn_list, ict);
298 mutex_exit(&ist->ist_mutex);
299 }
300
301 void
302 iscsit_sess_unbind_conn(iscsit_sess_t *ist, iscsit_conn_t *ict)
303 {
304 mutex_enter(&ist->ist_mutex);
305 list_remove(&ist->ist_conn_list, ict);
306 ist->ist_conn_count--;
307 mutex_exit(&ist->ist_mutex);
308 iscsit_sess_rele(ist);
309 iscsit_conn_rele(ict);
310 }
311
312 void
313 iscsit_sess_hold(iscsit_sess_t *ist)
314 {
315 idm_refcnt_hold(&ist->ist_refcnt);
316 }
317
318 void
319 iscsit_sess_rele(iscsit_sess_t *ist)
320 {
321 idm_refcnt_rele(&ist->ist_refcnt);
322 }
323
324 idm_status_t
325 iscsit_sess_check_hold(iscsit_sess_t *ist)
326 {
327 mutex_enter(&ist->ist_mutex);
328 if (ist->ist_state != SS_Q6_DONE &&
329 ist->ist_state != SS_Q7_ERROR) {
330 idm_refcnt_hold(&ist->ist_refcnt);
331 mutex_exit(&ist->ist_mutex);
332 return (IDM_STATUS_SUCCESS);
333 }
334 mutex_exit(&ist->ist_mutex);
335 return (IDM_STATUS_FAIL);
336 }
337
338 iscsit_conn_t *
339 iscsit_sess_lookup_conn(iscsit_sess_t *ist, uint16_t cid)
340 {
341 iscsit_conn_t *result;
342
343 mutex_enter(&ist->ist_mutex);
344 for (result = list_head(&ist->ist_conn_list);
345 result != NULL;
346 result = list_next(&ist->ist_conn_list, result)) {
347 if (result->ict_cid == cid) {
348 iscsit_conn_hold(result);
349 mutex_exit(&ist->ist_mutex);
350 return (result);
351 }
352 }
353 mutex_exit(&ist->ist_mutex);
354
355 return (NULL);
356 }
357
358 iscsit_sess_t *
359 iscsit_sess_reinstate(iscsit_tgt_t *tgt, iscsit_sess_t *ist, iscsit_conn_t *ict,
360 uint8_t *error_class, uint8_t *error_detail)
361 {
362 iscsit_sess_t *new_sess;
363
364 mutex_enter(&ist->ist_mutex);
365
366 /*
367 * Session reinstatement replaces a current session with a new session.
368 * The new session will have the same ISID as the existing session.
369 */
370 new_sess = iscsit_sess_create(tgt, ict, 0,
371 ist->ist_isid, ist->ist_tpgt_tag,
372 ist->ist_initiator_name, ist->ist_target_name,
373 error_class, error_detail);
374 ASSERT(new_sess != NULL);
375
376 /* Copy additional fields from original session */
377 new_sess->ist_expcmdsn = ist->ist_expcmdsn;
378 new_sess->ist_maxcmdsn = ist->ist_expcmdsn + 1;
379
380 if (ist->ist_state != SS_Q6_DONE &&
381 ist->ist_state != SS_Q7_ERROR) {
382 /*
383 * Generate reinstate event
384 */
385 sess_sm_event_locked(ist, SE_SESSION_REINSTATE, NULL);
386 }
387 mutex_exit(&ist->ist_mutex);
388
389 return (new_sess);
390 }
391
392 int
393 iscsit_sess_avl_compare(const void *void_sess1, const void *void_sess2)
394 {
395 const iscsit_sess_t *sess1 = void_sess1;
396 const iscsit_sess_t *sess2 = void_sess2;
397 int result;
398
399 /*
400 * Sort by initiator name, then ISID then portal group tag
401 */
402 result = strcmp(sess1->ist_initiator_name, sess2->ist_initiator_name);
403 if (result < 0) {
404 return (-1);
405 } else if (result > 0) {
406 return (1);
407 }
408
409 /*
410 * Initiator names match, compare ISIDs
411 */
412 result = memcmp(sess1->ist_isid, sess2->ist_isid, ISCSI_ISID_LEN);
413 if (result < 0) {
414 return (-1);
415 } else if (result > 0) {
416 return (1);
417 }
418
419 /*
420 * ISIDs match, compare portal group tags
421 */
422 if (sess1->ist_tpgt_tag < sess2->ist_tpgt_tag) {
423 return (-1);
424 } else if (sess1->ist_tpgt_tag > sess2->ist_tpgt_tag) {
425 return (1);
426 }
427
428 /*
429 * Portal group tags match, compare TSIHs
430 */
431 if (sess1->ist_tsih < sess2->ist_tsih) {
432 return (-1);
433 } else if (sess1->ist_tsih > sess2->ist_tsih) {
434 return (1);
435 }
436
437 /*
438 * Sessions match
439 */
440 return (0);
441 }
442
443 int
444 iscsit_task_itt_compare(const void *void_task1, const void *void_task2)
445 {
446 const iscsit_task_t *task1 = void_task1;
447 const iscsit_task_t *task2 = void_task2;
448
449 if (task1->it_itt < task2->it_itt)
450 return (-1);
451 else if (task1->it_itt > task2->it_itt)
452 return (1);
453
454 return (0);
455 }
456
457 /*
458 * State machine
459 */
460
461 void
462 iscsit_sess_sm_event(iscsit_sess_t *ist, iscsit_session_event_t event,
463 iscsit_conn_t *ict)
464 {
465 mutex_enter(&ist->ist_mutex);
466 sess_sm_event_locked(ist, event, ict);
467 mutex_exit(&ist->ist_mutex);
468 }
469
470 static void
471 sess_sm_event_locked(iscsit_sess_t *ist, iscsit_session_event_t event,
472 iscsit_conn_t *ict)
473 {
474 sess_event_ctx_t *ctx;
475
476 iscsit_sess_hold(ist);
477
478 ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
479
480 ctx->se_ctx_event = event;
481 ctx->se_event_data = ict;
482
483 list_insert_tail(&ist->ist_events, ctx);
484 /*
485 * Use the ist_sm_busy to keep the state machine single threaded.
486 * This also serves as recursion avoidance since this flag will
487 * always be set if we call login_sm_event from within the
488 * state machine code.
489 */
490 if (!ist->ist_sm_busy) {
491 ist->ist_sm_busy = B_TRUE;
492 while (!list_is_empty(&ist->ist_events)) {
493 ctx = list_head(&ist->ist_events);
494 list_remove(&ist->ist_events, ctx);
495 idm_sm_audit_event(&ist->ist_state_audit,
496 SAS_ISCSIT_SESS, (int)ist->ist_state,
497 (int)ctx->se_ctx_event, (uintptr_t)ict);
498 mutex_exit(&ist->ist_mutex);
499 sess_sm_event_dispatch(ist, ctx);
500 mutex_enter(&ist->ist_mutex);
501 }
502 ist->ist_sm_busy = B_FALSE;
503
504 }
505
506 iscsit_sess_rele(ist);
507 }
508
509 static void
510 sess_sm_event_dispatch(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
511 {
512 iscsit_conn_t *ict;
513
514 DTRACE_PROBE2(session__event, iscsit_sess_t *, ist,
515 sess_event_ctx_t *, ctx);
516
517 IDM_SM_LOG(CE_NOTE, "sess_sm_event_dispatch: sess %p event %s(%d)",
518 (void *)ist, iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event);
519
520 /* State independent actions */
521 switch (ctx->se_ctx_event) {
522 case SE_CONN_IN_LOGIN:
523 ict = ctx->se_event_data;
524 iscsit_sess_bind_conn(ist, ict);
525 break;
526 case SE_CONN_FAIL:
527 ict = ctx->se_event_data;
528 iscsit_sess_unbind_conn(ist, ict);
529 break;
530 }
531
532 /* State dependent actions */
533 switch (ist->ist_state) {
534 case SS_Q1_FREE:
535 sess_sm_q1_free(ist, ctx);
536 break;
537 case SS_Q2_ACTIVE:
538 sess_sm_q2_active(ist, ctx);
539 break;
540 case SS_Q3_LOGGED_IN:
541 sess_sm_q3_logged_in(ist, ctx);
542 break;
543 case SS_Q4_FAILED:
544 sess_sm_q4_failed(ist, ctx);
545 break;
546 case SS_Q5_CONTINUE:
547 sess_sm_q5_continue(ist, ctx);
548 break;
549 case SS_Q6_DONE:
550 sess_sm_q6_done(ist, ctx);
551 break;
552 case SS_Q7_ERROR:
553 sess_sm_q7_error(ist, ctx);
554 break;
555 default:
556 ASSERT(0);
557 break;
558 }
559
560 kmem_free(ctx, sizeof (*ctx));
561 }
562
563 static void
564 sess_sm_q1_free(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
565 {
566 switch (ctx->se_ctx_event) {
567 case SE_CONN_IN_LOGIN:
568 /* N1 */
569 sess_sm_new_state(ist, ctx, SS_Q2_ACTIVE);
570 break;
571 default:
572 ASSERT(0);
573 break;
574 }
575 }
576
577
578 static void
579 sess_sm_q2_active(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
580 {
581 iscsit_conn_t *ict;
582
583 switch (ctx->se_ctx_event) {
584 case SE_CONN_LOGGED_IN:
585 /* N2 track FFP connections */
586 ist->ist_ffp_conn_count++;
587 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
588 break;
589 case SE_CONN_IN_LOGIN:
590 /* N2.1, don't care stay in this state */
591 break;
592 case SE_CONN_FAIL:
593 /* N9 */
594 sess_sm_new_state(ist, ctx, SS_Q7_ERROR);
595 break;
596 case SE_SESSION_REINSTATE:
597 /* N11 */
598 /*
599 * Shutdown the iSCSI connections by
600 * sending an implicit logout to all
601 * the IDM connections and transition
602 * the session to SS_Q6_DONE state.
603 */
604 mutex_enter(&ist->ist_mutex);
605 for (ict = list_head(&ist->ist_conn_list);
606 ict != NULL;
607 ict = list_next(&ist->ist_conn_list, ict)) {
608 iscsit_conn_logout(ict);
609 }
610 mutex_exit(&ist->ist_mutex);
611 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
612 break;
613 default:
614 ASSERT(0);
615 break;
616 }
617 }
618
619 static void
620 sess_sm_q3_logged_in(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
621 {
622 iscsit_conn_t *ict;
623
624 switch (ctx->se_ctx_event) {
625 case SE_CONN_IN_LOGIN:
626 case SE_CONN_FAIL:
627 /* N2.2, don't care */
628 break;
629 case SE_CONN_LOGGED_IN:
630 /* N2.2, track FFP connections */
631 ist->ist_ffp_conn_count++;
632 break;
633 case SE_CONN_FFP_FAIL:
634 case SE_CONN_FFP_DISABLE:
635 /*
636 * Event data from event context is the associated connection
637 * which in this case happens to be the last FFP connection
638 * for the session. In certain cases we need to refer
639 * to this last valid connection (i.e. RFC3720 section 12.16)
640 * so we'll save off a pointer here for later use.
641 */
642 ASSERT(ist->ist_ffp_conn_count >= 1);
643 ist->ist_failed_conn = (iscsit_conn_t *)ctx->se_event_data;
644 ist->ist_ffp_conn_count--;
645 if (ist->ist_ffp_conn_count == 0) {
646 /*
647 * N5(fail) or N3(disable)
648 *
649 * If the event is SE_CONN_FFP_FAIL but we are
650 * in the midst of an administrative session close
651 * because of a service or target offline then
652 * there is no need to go to "failed" state.
653 */
654 sess_sm_new_state(ist, ctx,
655 ((ctx->se_ctx_event == SE_CONN_FFP_DISABLE) ||
656 (ist->ist_admin_close)) ?
657 SS_Q6_DONE : SS_Q4_FAILED);
658 }
659 break;
660 case SE_SESSION_CLOSE:
661 case SE_SESSION_REINSTATE:
662 /* N3 */
663 mutex_enter(&ist->ist_mutex);
664 if (ctx->se_ctx_event == SE_SESSION_CLOSE) {
665 ASSERT(ist->ist_ffp_conn_count >= 1);
666 ist->ist_ffp_conn_count--;
667 }
668 for (ict = list_head(&ist->ist_conn_list);
669 ict != NULL;
670 ict = list_next(&ist->ist_conn_list, ict)) {
671 if ((ctx->se_ctx_event == SE_SESSION_CLOSE) &&
672 ((iscsit_conn_t *)ctx->se_event_data == ict)) {
673 /*
674 * Skip this connection since it will
675 * see the logout response
676 */
677 continue;
678 }
679 iscsit_conn_logout(ict);
680 }
681 mutex_exit(&ist->ist_mutex);
682
683 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
684 break;
685 default:
686 ASSERT(0);
687 break;
688 }
689 }
690
691 static void
692 sess_sm_timeout(void *arg)
693 {
694 iscsit_sess_t *ist = arg;
695
696 iscsit_sess_sm_event(ist, SE_SESSION_TIMEOUT, NULL);
697 }
698
699 static void
700 sess_sm_q4_failed(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
701 {
702 /* Session timer must not be running when we leave this event */
703 switch (ctx->se_ctx_event) {
704 case SE_CONN_IN_LOGIN:
705 /* N7 */
706 sess_sm_new_state(ist, ctx, SS_Q5_CONTINUE);
707 break;
708 case SE_SESSION_REINSTATE:
709 /* N6 */
710 (void) untimeout(ist->ist_state_timeout);
711 /*FALLTHROUGH*/
712 case SE_SESSION_TIMEOUT:
713 /* N6 */
714 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
715 break;
716 case SE_CONN_FAIL:
717 /* Don't care */
718 break;
719 default:
720 ASSERT(0);
721 break;
722 }
723 }
724
725 static void
726 sess_sm_q5_continue(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
727 {
728 switch (ctx->se_ctx_event) {
729 case SE_CONN_FAIL:
730 /* N5 */
731 sess_sm_new_state(ist, ctx, SS_Q4_FAILED);
732 break;
733 case SE_CONN_LOGGED_IN:
734 /* N10 */
735 sess_sm_new_state(ist, ctx, SS_Q3_LOGGED_IN);
736 break;
737 case SE_SESSION_REINSTATE:
738 /* N11 */
739 sess_sm_new_state(ist, ctx, SS_Q6_DONE);
740 break;
741 default:
742 ASSERT(0);
743 break;
744 }
745 }
746
747 static void
748 sess_sm_q6_done(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
749 {
750 /* Terminal state */
751 switch (ctx->se_ctx_event) {
752 case SE_CONN_LOGGED_IN:
753 /*
754 * It's possible to get this event if we encountered
755 * an SE_SESSION_REINSTATE_EVENT while we were in
756 * SS_Q2_ACTIVE state. If so we want to update
757 * ist->ist_ffp_conn_count because we know an
758 * SE_CONN_FFP_FAIL or SE_CONN_FFP_DISABLE is on the
759 * way.
760 */
761 ist->ist_ffp_conn_count++;
762 break;
763 case SE_CONN_FFP_FAIL:
764 case SE_CONN_FFP_DISABLE:
765 ASSERT(ist->ist_ffp_conn_count >= 1);
766 ist->ist_ffp_conn_count--;
767 break;
768 case SE_CONN_FAIL:
769 if (ist->ist_conn_count == 0) {
770 idm_refcnt_async_wait_ref(&ist->ist_refcnt,
771 &iscsit_sess_unref);
772 }
773 break;
774 default:
775 break;
776 }
777 }
778
779 static void
780 sess_sm_q7_error(iscsit_sess_t *ist, sess_event_ctx_t *ctx)
781 {
782 /* Terminal state */
783 switch (ctx->se_ctx_event) {
784 case SE_CONN_FAIL:
785 if (ist->ist_conn_count == 0) {
786 idm_refcnt_async_wait_ref(&ist->ist_refcnt,
787 &iscsit_sess_unref);
788 }
789 break;
790 default:
791 break;
792 }
793 }
794
795 static void
796 sess_sm_new_state(iscsit_sess_t *ist, sess_event_ctx_t *ctx,
797 iscsit_session_state_t new_state)
798 {
799 int t2r_secs;
800
801 /*
802 * Validate new state
803 */
804 ASSERT(new_state != SS_UNDEFINED);
805 ASSERT3U(new_state, <, SS_MAX_STATE);
806
807 new_state = (new_state < SS_MAX_STATE) ?
808 new_state : SS_UNDEFINED;
809
810 IDM_SM_LOG(CE_NOTE, "sess_sm_new_state: sess %p, evt %s(%d), "
811 "%s(%d) --> %s(%d)\n", (void *) ist,
812 iscsit_se_name[ctx->se_ctx_event], ctx->se_ctx_event,
813 iscsit_ss_name[ist->ist_state], ist->ist_state,
814 iscsit_ss_name[new_state], new_state);
815
816 DTRACE_PROBE3(sess__state__change,
817 iscsit_sess_t *, ist, sess_event_ctx_t *, ctx,
818 iscsit_session_state_t, new_state);
819
820 mutex_enter(&ist->ist_mutex);
821 idm_sm_audit_state_change(&ist->ist_state_audit, SAS_ISCSIT_SESS,
822 (int)ist->ist_state, (int)new_state);
823 ist->ist_last_state = ist->ist_state;
824 ist->ist_state = new_state;
825 mutex_exit(&ist->ist_mutex);
826
827 switch (ist->ist_state) {
828 case SS_Q1_FREE:
829 break;
830 case SS_Q2_ACTIVE:
831 iscsit_tgt_bind_sess(ist->ist_tgt, ist);
832 break;
833 case SS_Q3_LOGGED_IN:
834 break;
835 case SS_Q4_FAILED:
836 t2r_secs =
837 ist->ist_failed_conn->ict_op.op_default_time_2_retain;
838 ist->ist_state_timeout = timeout(sess_sm_timeout, ist,
839 drv_usectohz(t2r_secs*1000000));
840 break;
841 case SS_Q5_CONTINUE:
842 break;
843 case SS_Q6_DONE:
844 case SS_Q7_ERROR:
845 /*
846 * We won't need our TSIH anymore and it represents an
847 * implicit reference to the global TSIH pool. Get rid
848 * of it.
849 */
850 if (ist->ist_tsih != ISCSI_UNSPEC_TSIH) {
851 iscsit_tsih_free(ist->ist_tsih);
852 }
853
854 /*
855 * We don't want this session to show up anymore so unbind
856 * it now. After this call this session cannot have any
857 * references outside itself (implicit or explicit).
858 */
859 iscsit_tgt_unbind_sess(ist->ist_tgt, ist);
860
861 /*
862 * If we have more connections bound then more events
863 * are comming so don't wait for idle yet.
864 */
865 if (ist->ist_conn_count == 0) {
866 idm_refcnt_async_wait_ref(&ist->ist_refcnt,
867 &iscsit_sess_unref);
868 }
869 break;
870 default:
871 ASSERT(0);
872 /*NOTREACHED*/
873 }
874 }
Cache object: 7b1cfe603a8ad422cb02d080edbc412e
|