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