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