xref: /illumos-gate/usr/src/uts/common/io/comstar/port/iscsit/iscsit_sess.c (revision 61dfa5098dc8576d9a5e277deba6df647bb70c06)
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