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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * iSCSI connection interfaces
26  */
27 
28 #include "iscsi.h"
29 #include "persistent.h"
30 #include <sys/bootprops.h>
31 
32 extern ib_boot_prop_t   *iscsiboot_prop;
33 
34 /* interface connection interfaces */
35 static iscsi_status_t iscsi_conn_state_free(iscsi_conn_t *icp,
36     iscsi_conn_event_t event);
37 static void iscsi_conn_state_in_login(iscsi_conn_t *icp,
38     iscsi_conn_event_t event);
39 static void iscsi_conn_state_logged_in(iscsi_conn_t *icp,
40     iscsi_conn_event_t event);
41 static void iscsi_conn_state_in_logout(iscsi_conn_t *icp,
42     iscsi_conn_event_t event);
43 static void iscsi_conn_state_failed(iscsi_conn_t *icp,
44     iscsi_conn_event_t event);
45 static void iscsi_conn_state_polling(iscsi_conn_t *icp,
46     iscsi_conn_event_t event);
47 static char *iscsi_conn_event_str(iscsi_conn_event_t event);
48 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
49 
50 static void iscsi_conn_logged_in(iscsi_sess_t *isp,
51     iscsi_conn_t *icp);
52 static void iscsi_conn_retry(iscsi_sess_t *isp,
53     iscsi_conn_t *icp);
54 
55 #define	SHUTDOWN_TIMEOUT	180 /* seconds */
56 
57 extern int modrootloaded;
58 /*
59  * +--------------------------------------------------------------------+
60  * | External Connection Interfaces					|
61  * +--------------------------------------------------------------------+
62  */
63 
64 /*
65  * iscsi_conn_create - This creates an iscsi connection structure and
66  * associates it with a session structure.  The session's sess_conn_list_rwlock
67  * should be held as a writer before calling this function.
68  */
69 iscsi_status_t
70 iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp)
71 {
72 	iscsi_conn_t	*icp	= NULL;
73 	char		th_name[ISCSI_TH_MAX_NAME_LEN];
74 
75 	/* See if this connection already exists */
76 	for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) {
77 
78 		/*
79 		 * Compare the ioctl information to see if
80 		 * its a match for this connection.  (This
81 		 * is done by making sure the IPs are of
82 		 * the same size and then they are the
83 		 * same value.
84 		 */
85 		if (bcmp(&icp->conn_base_addr, addr,
86 		    SIZEOF_SOCKADDR(addr)) == 0) {
87 			/* It's a match, record this connection */
88 			break;
89 		}
90 	}
91 
92 	/* If icp is found return it */
93 	if (icp != NULL) {
94 		*icpp = icp;
95 		return (ISCSI_STATUS_SUCCESS);
96 	}
97 
98 	/* We are creating the connection, allocate, and setup */
99 	icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP);
100 
101 	/*
102 	 * Setup connection
103 	 */
104 	icp->conn_sig			= ISCSI_SIG_CONN;
105 	icp->conn_state			= ISCSI_CONN_STATE_FREE;
106 	mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
107 	cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
108 	icp->conn_state_destroy		= B_FALSE;
109 	icp->conn_sess			= isp;
110 	icp->conn_state_lbolt		= ddi_get_lbolt();
111 
112 	mutex_enter(&iscsi_oid_mutex);
113 	icp->conn_oid = iscsi_oid++;
114 	mutex_exit(&iscsi_oid_mutex);
115 
116 	/* Creation of the receive thread */
117 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_RXTH_NAME_FORMAT,
118 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
119 	    icp->conn_oid) >= sizeof (th_name)) {
120 		cv_destroy(&icp->conn_state_change);
121 		mutex_destroy(&icp->conn_state_mutex);
122 		kmem_free(icp, sizeof (iscsi_conn_t));
123 		*icpp = NULL;
124 		return (ISCSI_STATUS_INTERNAL_ERROR);
125 	}
126 
127 	icp->conn_rx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
128 	    th_name, iscsi_rx_thread, icp);
129 
130 	/* Creation of the transfer thread */
131 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
132 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
133 	    icp->conn_oid) >= sizeof (th_name)) {
134 		iscsi_thread_destroy(icp->conn_rx_thread);
135 		cv_destroy(&icp->conn_state_change);
136 		mutex_destroy(&icp->conn_state_mutex);
137 		kmem_free(icp, sizeof (iscsi_conn_t));
138 		*icpp = NULL;
139 		return (ISCSI_STATUS_INTERNAL_ERROR);
140 	}
141 
142 	icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
143 	    th_name, iscsi_tx_thread, icp);
144 
145 	/* setup connection queues */
146 	iscsi_init_queue(&icp->conn_queue_active);
147 
148 	bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
149 
150 	/* Add new connection to the session connection list */
151 	icp->conn_cid = isp->sess_conn_next_cid++;
152 	if (isp->sess_conn_list == NULL) {
153 		isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp;
154 	} else {
155 		isp->sess_conn_list_last_ptr->conn_next = icp;
156 		isp->sess_conn_list_last_ptr = icp;
157 	}
158 
159 	KSTAT_INC_SESS_CNTR_CONN(isp);
160 	(void) iscsi_conn_kstat_init(icp);
161 
162 	*icpp = icp;
163 
164 	return (ISCSI_STATUS_SUCCESS);
165 }
166 
167 
168 /*
169  * iscsi_conn_offline - This attempts to take a connection from
170  * any state to ISCSI_CONN_STATE_FREE.
171  */
172 iscsi_status_t
173 iscsi_conn_offline(iscsi_conn_t *icp)
174 {
175 	clock_t		delay;
176 
177 	ASSERT(icp != NULL);
178 
179 	/*
180 	 * We can only destroy a connection if its either in
181 	 * a state of FREE or LOGGED.  The other states are
182 	 * transitionary and its unsafe to perform actions
183 	 * on the connection in those states.  Set a flag
184 	 * on the connection to influence the transitions
185 	 * to quickly complete.  Then wait for a state
186 	 * transition.
187 	 */
188 	delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
189 	mutex_enter(&icp->conn_state_mutex);
190 	icp->conn_state_destroy = B_TRUE;
191 	while ((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
192 	    (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN) &&
193 	    (ddi_get_lbolt() < delay)) {
194 		/* wait for transition */
195 		(void) cv_timedwait(&icp->conn_state_change,
196 		    &icp->conn_state_mutex, delay);
197 	}
198 
199 	/* Final check whether we can destroy the connection */
200 	switch (icp->conn_state) {
201 	case ISCSI_CONN_STATE_FREE:
202 		/* Easy case - Connection is dead */
203 		break;
204 	case ISCSI_CONN_STATE_LOGGED_IN:
205 		/* Hard case - Force connection logout */
206 		(void) iscsi_conn_state_machine(icp,
207 		    ISCSI_CONN_EVENT_T9);
208 		break;
209 	case ISCSI_CONN_STATE_IN_LOGIN:
210 	case ISCSI_CONN_STATE_IN_LOGOUT:
211 	case ISCSI_CONN_STATE_FAILED:
212 	case ISCSI_CONN_STATE_POLLING:
213 	default:
214 		/* All other cases fail the destroy */
215 		icp->conn_state_destroy = B_FALSE;
216 		mutex_exit(&icp->conn_state_mutex);
217 		return (ISCSI_STATUS_INTERNAL_ERROR);
218 	}
219 	mutex_exit(&icp->conn_state_mutex);
220 
221 	return (ISCSI_STATUS_SUCCESS);
222 }
223 
224 /*
225  * iscsi_conn_destroy - This destroys an iscsi connection structure
226  * and de-associates it with the session.  The connection should
227  * already been in the ISCSI_CONN_STATE_FREE when attempting this
228  * operation.
229  */
230 iscsi_status_t
231 iscsi_conn_destroy(iscsi_conn_t *icp)
232 {
233 	iscsi_sess_t	*isp;
234 	iscsi_conn_t	*t_icp;
235 
236 	ASSERT(icp != NULL);
237 	isp = icp->conn_sess;
238 	ASSERT(isp != NULL);
239 
240 	if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
241 		return (ISCSI_STATUS_INTERNAL_ERROR);
242 	}
243 
244 	/* Destroy receive thread */
245 	iscsi_thread_destroy(icp->conn_rx_thread);
246 
247 	/* Destroy transfer thread */
248 	iscsi_thread_destroy(icp->conn_tx_thread);
249 
250 	/* Terminate connection queues */
251 	iscsi_destroy_queue(&icp->conn_queue_active);
252 
253 	cv_destroy(&icp->conn_state_change);
254 	mutex_destroy(&icp->conn_state_mutex);
255 
256 	/*
257 	 * Remove connection from sessions linked list.
258 	 */
259 	if (isp->sess_conn_list == icp) {
260 		/* connection first item in list */
261 		isp->sess_conn_list = icp->conn_next;
262 		/*
263 		 * check if this is also the last item in the list
264 		 */
265 		if (isp->sess_conn_list_last_ptr == icp) {
266 			isp->sess_conn_list_last_ptr = NULL;
267 		}
268 	} else {
269 		/*
270 		 * search session list for icp pointing
271 		 * to connection being removed.  Then
272 		 * update that connections next pointer.
273 		 */
274 		t_icp = isp->sess_conn_list;
275 		while (t_icp->conn_next != NULL) {
276 			if (t_icp->conn_next == icp) {
277 				break;
278 			}
279 			t_icp = t_icp->conn_next;
280 		}
281 		if (t_icp->conn_next == icp) {
282 			t_icp->conn_next = icp->conn_next;
283 			/*
284 			 * if this is the last connection in the list
285 			 * update the last_ptr to point to t_icp
286 			 */
287 			if (isp->sess_conn_list_last_ptr == icp) {
288 				isp->sess_conn_list_last_ptr = t_icp;
289 			}
290 		} else {
291 			/* couldn't find session */
292 			ASSERT(FALSE);
293 		}
294 	}
295 
296 	/* Free this Connections Data */
297 	iscsi_conn_kstat_term(icp);
298 	kmem_free(icp, sizeof (iscsi_conn_t));
299 
300 	return (ISCSI_STATUS_SUCCESS);
301 }
302 
303 
304 /*
305  * iscsi_conn_set_login_min_max - set min/max login window
306  *
307  * Used to set the min and max login window.  Input values
308  * are in seconds.
309  */
310 void
311 iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max)
312 {
313 	ASSERT(icp != NULL);
314 
315 	icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
316 	icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
317 }
318 
319 
320 
321 /*
322  * iscsi_conn_state_machine - This function is used to drive the
323  * state machine of the iscsi connection.  It takes in a connection
324  * and the associated event effecting the connection.
325  *
326  * 7.1.3  Connection State Diagram for an Initiator
327  *      Symbolic Names for States:
328  *      S1: FREE        - State on instantiation, or after successful
329  *                        connection closure.
330  *      S2: IN_LOGIN    - Waiting for login process to conclude,
331  *                        possibly involving several PDU exchanges.
332  *      S3: LOGGED_IN   - In Full Feature Phase, waiting for all internal,
333  *                        iSCSI, and transport events
334  *      S4: IN_LOGOUT   - Waiting for the Logout repsonse.
335  *      S5: FAILED      - The connection has failed.  Attempting
336  *			  to reconnect.
337  *      S6: POLLING     - The connection reconnect attempts have
338  *                        failed.  Continue to poll at a lower
339  *                        frequency.
340  *
341  *      States S3, S4 constitute the Full Feature Phase
342  *              of the connection.
343  *
344  *      The state diagram is as follows:
345  *                 -------
346  *      +-------->/ S1    \<------------------------------+
347  *      |      +->\       /<---+            /---\         |
348  *      |     /    ---+---     |T7/30     T7|   |         |
349  *      |    +        |        |            \->------     |
350  *      |  T8|        |T1     /      T5       / S6   \--->|
351  *      |    |        |      /     +----------\      /T30 |
352  *      |    |        V     /     /            ------     |
353  *      |    |     ------- /     /               ^        |
354  *      |    |    / S2    \     /   T5           |T7      |
355  *      |    |    \       /    +-------------- --+---     |
356  *      |    |     ---+---    /               / S5   \--->|
357  *      |    |        |      /      T14/T15   \      /T30 |
358  *      |    |        |T5   /  +-------------> ------     |
359  *      |    |        |    /  /                           |
360  *      |    |        |   /  /         T11                |
361  *      |    |        |  /  /         +----+              |
362  *      |    |        V V  /          |    |              |
363  *      |    |      ------+       ----+--  |              |
364  *      |    +-----/ S3    \T9/11/ S4    \<+              |
365  *      +----------\       /---->\       /----------------+
366  *                  -------       -------        T15/T17
367  *
368  * The state transition table is as follows:
369  *
370  *         +-----+---+---+------+------+---+
371  *         |S1   |S2 |S3 |S4    |S5    |S6 |
372  *      ---+-----+---+---+------+------+---+
373  *       S1|T1   |T1 | - | -    | -    |   |
374  *      ---+-----+---+---+------+------+---+
375  *       S2|T7/30|-  |T5 | -    | -    |   |
376  *      ---+-----+---+---+------+------+---+
377  *       S3|T8   |-  | - |T9/11 |T14/15|   |
378  *      ---+-----+---+---+------+------+---+
379  *       S4|     |-  | - |T11   |T15/17|   |
380  *      ---+-----+---+---+------+------+---+
381  *       S5|T30  |   |T5 |      |      |T7 |
382  *      ---+-----+---+---+------+------+---+
383  *       S6|T30  |   |T5 |      |      |T7 |
384  *      ---+-----+---+---+------+------+---+
385  *
386  * Events definitions:
387  *
388  * -T1: Transport connection request was made (e.g., TCP SYN sent).
389  * -T5: The final iSCSI Login response with a Status-Class of zero was
390  *      received.
391  * -T7: One of the following events caused the transition:
392  *      - Login timed out.
393  *      - A transport disconnect indication was received.
394  *      - A transport reset was received.
395  *      - An internal event indicating a transport timeout was
396  *        received.
397  *      - An internal event of receiving a Logout repsonse (success)
398  *        on another connection for a "close the session" Logout
399  *        request was received.
400  *      * In all these cases, the transport connection is closed.
401  * -T8: An internal event of receiving a Logout response (success)
402  *      on another connection for a "close the session" Logout request
403  *      was received, thus closing this connection requiring no further
404  *      cleanup.
405  * -T9: An internal event that indicates the readiness to start the
406  *      Logout process was received, thus prompting an iSCSI Logout to
407  *      be sent by the initiator.
408  * -T11: Async PDU with AsyncEvent "Request Logout" was received.
409  * -T13: An iSCSI Logout response (success) was received, or an internal
410  *      event of receiving a Logout response (success) on another
411  *      connection was received.
412  * -T14: One or more of the following events case this transition:
413  *	- Header Digest Error
414  *	- Protocol Error
415  * -T15: One or more of the following events caused this transition:
416  *      - Internal event that indicates a transport connection timeout
417  *        was received thus prompting transport RESET or transport
418  *        connection closure.
419  *      - A transport RESET
420  *      - A transport disconnect indication.
421  *      - Async PDU with AsyncEvent "Drop connection" (for this CID)
422  *      - Async PDU with AsyncEvent "Drop all connections"
423  * -T17: One or more of the following events caused this transition:
424  *      - Logout response, (failure i.e., a non-zero status) was
425  *      received, or Logout timed out.
426  *      - Any of the events specified for T15.
427  * -T30: One of the following event caused the transition:
428  *	- Thefinal iSCSI Login response was received with a non-zero
429  *	  Status-Class.
430  */
431 iscsi_status_t
432 iscsi_conn_state_machine(iscsi_conn_t *icp, iscsi_conn_event_t event)
433 {
434 	iscsi_status_t	    status = ISCSI_STATUS_SUCCESS;
435 
436 	ASSERT(icp != NULL);
437 	ASSERT(mutex_owned(&icp->conn_state_mutex));
438 
439 	DTRACE_PROBE3(event, iscsi_conn_t *, icp,
440 	    char *, iscsi_conn_state_str(icp->conn_state),
441 	    char *, iscsi_conn_event_str(event));
442 
443 	icp->conn_prev_state = icp->conn_state;
444 	icp->conn_state_lbolt = ddi_get_lbolt();
445 
446 	switch (icp->conn_state) {
447 	case ISCSI_CONN_STATE_FREE:
448 		status = iscsi_conn_state_free(icp, event);
449 		break;
450 	case ISCSI_CONN_STATE_IN_LOGIN:
451 		iscsi_conn_state_in_login(icp, event);
452 		break;
453 	case ISCSI_CONN_STATE_LOGGED_IN:
454 		iscsi_conn_state_logged_in(icp, event);
455 		break;
456 	case ISCSI_CONN_STATE_IN_LOGOUT:
457 		iscsi_conn_state_in_logout(icp, event);
458 		break;
459 	case ISCSI_CONN_STATE_FAILED:
460 		iscsi_conn_state_failed(icp, event);
461 		break;
462 	case ISCSI_CONN_STATE_POLLING:
463 		iscsi_conn_state_polling(icp, event);
464 		break;
465 	default:
466 		ASSERT(FALSE);
467 		status = ISCSI_STATUS_INTERNAL_ERROR;
468 	}
469 
470 	cv_broadcast(&icp->conn_state_change);
471 	return (status);
472 }
473 
474 
475 /*
476  * iscsi_conn_state_str - converts state enum to a string
477  */
478 char *
479 iscsi_conn_state_str(iscsi_conn_state_t state)
480 {
481 	switch (state) {
482 	case ISCSI_CONN_STATE_FREE:
483 		return ("free");
484 	case ISCSI_CONN_STATE_IN_LOGIN:
485 		return ("in_login");
486 	case ISCSI_CONN_STATE_LOGGED_IN:
487 		return ("logged_in");
488 	case ISCSI_CONN_STATE_IN_LOGOUT:
489 		return ("in_logout");
490 	case ISCSI_CONN_STATE_FAILED:
491 		return ("failed");
492 	case ISCSI_CONN_STATE_POLLING:
493 		return ("polling");
494 	default:
495 		return ("unknown");
496 	}
497 }
498 
499 
500 /*
501  * iscsi_conn_sync_params - used to update connection parameters
502  *
503  * Used to update connection parameters with current configured
504  * parameters in the persistent store.  This should be called
505  * before starting to make a new iscsi connection in iscsi_login.
506  */
507 iscsi_status_t
508 iscsi_conn_sync_params(iscsi_conn_t *icp)
509 {
510 	iscsi_sess_t		*isp;
511 	iscsi_hba_t		*ihp;
512 	int			param_id;
513 	persistent_param_t	pp;
514 	iscsi_config_sess_t	*ics;
515 	int			idx, size;
516 	char			*name;
517 
518 	ASSERT(icp != NULL);
519 	ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
520 	    (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
521 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING));
522 	isp = icp->conn_sess;
523 	ASSERT(isp != NULL);
524 	ihp = isp->sess_hba;
525 	ASSERT(ihp != NULL);
526 
527 	/*
528 	 * Check if someone is trying to destroy this
529 	 * connection.  If so fail the sync request,
530 	 * as a method of fast fail.
531 	 */
532 	if (icp->conn_state_destroy == B_TRUE) {
533 		return (ISCSI_STATUS_SHUTDOWN);
534 	}
535 
536 	bzero(&pp, sizeof (pp));
537 
538 	/* First get a copy of the HBA params */
539 	bcopy(&ihp->hba_params, &icp->conn_params,
540 	    sizeof (iscsi_login_params_t));
541 
542 	/*
543 	 * Now we need to get the session configured
544 	 * values from the persistent store and apply
545 	 * them to our connection.
546 	 */
547 	(void) persistent_param_get((char *)isp->sess_name, &pp);
548 	for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM;
549 	    param_id++) {
550 		if (iscsiboot_prop && modrootloaded &&
551 		    !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) {
552 			/*
553 			 * iscsi boot with mpxio disabled
554 			 * while iscsi booting target's parameter overriden
555 			 * do no update target's parameters.
556 			 */
557 			if (pp.p_bitmap) {
558 				cmn_err(CE_NOTE, "Adopting "
559 				    " default login parameters in"
560 				    " boot session as MPxIO is disabled");
561 			}
562 			break;
563 		}
564 		if (pp.p_bitmap & (1 << param_id)) {
565 				switch (param_id) {
566 			/*
567 			 * Boolean parameters
568 			 */
569 			case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
570 				icp->conn_params.data_pdu_in_order =
571 				    pp.p_params.data_pdu_in_order;
572 				break;
573 			case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA:
574 				icp->conn_params.immediate_data =
575 				    pp.p_params.immediate_data;
576 				break;
577 			case ISCSI_LOGIN_PARAM_INITIAL_R2T:
578 				icp->conn_params.initial_r2t =
579 				    pp.p_params.initial_r2t;
580 				break;
581 			case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER:
582 				icp->conn_params.data_pdu_in_order =
583 				    pp.p_params.data_pdu_in_order;
584 				break;
585 			/*
586 			 * Integer parameters
587 			 */
588 			case ISCSI_LOGIN_PARAM_HEADER_DIGEST:
589 				icp->conn_params.header_digest =
590 				    pp.p_params.header_digest;
591 				break;
592 			case ISCSI_LOGIN_PARAM_DATA_DIGEST:
593 				icp->conn_params.data_digest =
594 				    pp.p_params.data_digest;
595 				break;
596 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN:
597 				icp->conn_params.default_time_to_retain =
598 				    pp.p_params.default_time_to_retain;
599 				break;
600 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT:
601 				icp->conn_params.default_time_to_wait =
602 				    pp.p_params.default_time_to_wait;
603 				break;
604 			case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH:
605 				icp->conn_params.max_recv_data_seg_len =
606 				    pp.p_params.max_recv_data_seg_len;
607 				break;
608 			case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH:
609 				icp->conn_params.first_burst_length =
610 				    pp.p_params.first_burst_length;
611 				break;
612 			case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH:
613 				icp->conn_params.max_burst_length =
614 				    pp.p_params.max_burst_length;
615 				break;
616 
617 			/*
618 			 * Integer parameters which currently are unsettable
619 			 */
620 			case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS:
621 				/* FALLTHRU */
622 			case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T:
623 				/* FALLTHRU */
624 			case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL:
625 				/* FALLTHRU */
626 			default:
627 				break;
628 			}
629 		}
630 	}
631 
632 	/* Skip binding checks on discovery sessions */
633 	if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
634 		return (ISCSI_STATUS_SUCCESS);
635 	}
636 
637 	/*
638 	 * Now we need to get the current optional connection
639 	 * binding information.
640 	 */
641 	/* setup initial buffer for configured session information */
642 	size = sizeof (*ics);
643 	ics = kmem_zalloc(size, KM_SLEEP);
644 	ics->ics_in = 1;
645 
646 	/* get configured sessions information */
647 	name = (char *)isp->sess_name;
648 	if (persistent_get_config_session(name, ics) == B_FALSE) {
649 		/*
650 		 * If we were unable to get target level information
651 		 * then check the initiator level information.
652 		 */
653 		name = (char *)isp->sess_hba->hba_name;
654 		if (persistent_get_config_session(name, ics) == B_FALSE) {
655 			/*
656 			 * No hba information is found.  So assume default
657 			 * one session unbound behavior.
658 			 */
659 			ics->ics_out = 1;
660 			ics->ics_bound = B_FALSE;
661 		}
662 	}
663 
664 	if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot &&
665 	    !iscsi_chk_bootlun_mpxio(ihp)) {
666 		/*
667 		 * iscsi booting session with mpxio disabled,
668 		 * no need set multiple sessions for booting session
669 		 */
670 		ics->ics_out = 1;
671 		ics->ics_bound = B_FALSE;
672 		cmn_err(CE_NOTE, "MPxIO is disabled,"
673 		    " no need to configure multiple boot sessions");
674 	}
675 
676 	/*
677 	 * Check to make sure this session is still a configured
678 	 * session.  The user might have decreased the session
679 	 * count. (NOTE: byte 5 of the sess_isid is the session
680 	 * count (via MS/T).  This counter starts at 0.)
681 	 */
682 
683 
684 	idx = isp->sess_isid[5];
685 
686 	if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) {
687 		/*
688 		 * This is temporary session for boot session propose
689 		 * no need to bound IP for this session
690 		 */
691 		icp->conn_bound = B_FALSE;
692 		kmem_free(ics, sizeof (iscsi_config_sess_t));
693 		return (ISCSI_STATUS_SUCCESS);
694 	}
695 
696 	if (ics->ics_out <= idx) {
697 		/*
698 		 * No longer a configured session.  Return a
699 		 * failure so we don't attempt to relogin.
700 		 */
701 		return (ISCSI_STATUS_SHUTDOWN);
702 	}
703 
704 	/*
705 	 * If sessions are unbound set this information on
706 	 * the connection and return success.
707 	 */
708 	if (ics->ics_bound == B_FALSE) {
709 		icp->conn_bound = B_FALSE;
710 		kmem_free(ics, sizeof (iscsi_config_sess_t));
711 		return (ISCSI_STATUS_SUCCESS);
712 	}
713 
714 	/*
715 	 * Since the sessions are bound we need to find the matching
716 	 * binding information for the session's isid.  If this
717 	 * session's isid is > 0 then we need to get more configured
718 	 * session information to find the binding info.
719 	 */
720 	if (idx > 0) {
721 		int ics_out;
722 
723 		ics_out = ics->ics_out;
724 		/* record new size and free last buffer */
725 		size = ISCSI_SESSION_CONFIG_SIZE(ics_out);
726 		kmem_free(ics, sizeof (*ics));
727 
728 		/* allocate new buffer */
729 		ics = kmem_zalloc(size, KM_SLEEP);
730 		ics->ics_in = ics_out;
731 
732 		/* get configured sessions information */
733 		if (persistent_get_config_session(name, ics) != B_TRUE) {
734 			cmn_err(CE_NOTE, "iscsi session(%d) - "
735 			    "unable to get configured session information\n",
736 			    isp->sess_oid);
737 			kmem_free(ics, size);
738 			return (ISCSI_STATUS_SHUTDOWN);
739 		}
740 	}
741 
742 	/* Copy correct binding information to the connection */
743 	icp->conn_bound = B_TRUE;
744 	if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) {
745 		bcopy(&ics->ics_bindings[idx].i_addr.in4,
746 		    &icp->conn_bound_addr.sin4.sin_addr.s_addr,
747 		    sizeof (struct in_addr));
748 	} else {
749 		bcopy(&ics->ics_bindings[idx].i_addr.in6,
750 		    &icp->conn_bound_addr.sin6.sin6_addr.s6_addr,
751 		    sizeof (struct in6_addr));
752 	}
753 
754 	kmem_free(ics, size);
755 
756 	return (ISCSI_STATUS_SUCCESS);
757 }
758 
759 /*
760  * +--------------------------------------------------------------------+
761  * | Internal Connection Interfaces					|
762  * +--------------------------------------------------------------------+
763  */
764 
765 
766 /*
767  * iscsi_conn_state_free -
768  *
769  * S1: FREE - State on instantiation, or after successful
770  * connection closure.
771  */
772 static iscsi_status_t
773 iscsi_conn_state_free(iscsi_conn_t *icp, iscsi_conn_event_t event)
774 {
775 	iscsi_sess_t		*isp;
776 	iscsi_hba_t		*ihp;
777 	iscsi_task_t		*itp;
778 	iscsi_status_t		status = ISCSI_STATUS_SUCCESS;
779 
780 	ASSERT(icp != NULL);
781 	isp = icp->conn_sess;
782 	ASSERT(isp != NULL);
783 	ihp = isp->sess_hba;
784 	ASSERT(ihp != NULL);
785 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
786 
787 	/* switch on event change */
788 	switch (event) {
789 	/* -T1: Transport connection request was request */
790 	case ISCSI_CONN_EVENT_T1:
791 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGIN;
792 
793 		/*
794 		 * Release the connection state mutex cross the
795 		 * the dispatch of the login task.  The login task
796 		 * will reacquire the connection state mutex when
797 		 * it pushes the connection successful or failed.
798 		 */
799 		mutex_exit(&icp->conn_state_mutex);
800 
801 		/* start login */
802 		itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
803 		itp->t_arg = icp;
804 		itp->t_blocking = B_TRUE;
805 
806 		/*
807 		 * Sync base connection information before login
808 		 * A login redirection might have shifted the
809 		 * current information from the base.
810 		 */
811 		bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
812 		    sizeof (icp->conn_curr_addr));
813 
814 		status = iscsi_login_start(itp);
815 		kmem_free(itp, sizeof (iscsi_task_t));
816 
817 		mutex_enter(&icp->conn_state_mutex);
818 		break;
819 
820 	/* All other events are invalid for this state */
821 	default:
822 		ASSERT(FALSE);
823 		status = ISCSI_STATUS_INTERNAL_ERROR;
824 	}
825 	return (status);
826 }
827 
828 /*
829  * iscsi_conn_state_in_login - During this state we are trying to
830  * connect the TCP connection and make a successful login to the
831  * target.  To complete this we have a task queue item that is
832  * trying this processing at this point in time.  When the task
833  * queue completed its processing it will issue either a T5/7
834  * event.
835  */
836 static void
837 iscsi_conn_state_in_login(iscsi_conn_t *icp, iscsi_conn_event_t event)
838 {
839 	iscsi_sess_t	*isp;
840 
841 	ASSERT(icp != NULL);
842 	isp = icp->conn_sess;
843 	ASSERT(isp != NULL);
844 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN);
845 
846 	/* switch on event change */
847 	switch (event) {
848 	/*
849 	 * -T5: The final iSCSI Login response with a Status-Class of zero
850 	 *	was received.
851 	 */
852 	case ISCSI_CONN_EVENT_T5:
853 		iscsi_conn_logged_in(isp, icp);
854 		break;
855 
856 	/*
857 	 * -T30: One of the following event caused the transition:
858 	 *	- Thefinal iSCSI Login response was received with a non-zero
859 	 *	  Status-Class.
860 	 */
861 	case ISCSI_CONN_EVENT_T30:
862 		/* FALLTHRU */
863 
864 	/*
865 	 * -T7: One of the following events caused the transition:
866 	 *	- Login timed out.
867 	 *	- A transport disconnect indication was received.
868 	 *	- A transport reset was received.
869 	 *	- An internal event indicating a transport timeout was
870 	 *	  received.
871 	 *	- An internal event of receiving a Logout repsonse (success)
872 	 *	  on another connection for a "close the session" Logout
873 	 *	  request was received.
874 	 *	* In all these cases, the transport connection is closed.
875 	 */
876 	case ISCSI_CONN_EVENT_T7:
877 		icp->conn_state = ISCSI_CONN_STATE_FREE;
878 		break;
879 
880 	/* All other events are invalid for this state */
881 	default:
882 		ASSERT(FALSE);
883 	}
884 }
885 
886 
887 /*
888  * iscsi_conn_state_logged_in -
889  *
890  */
891 static void
892 iscsi_conn_state_logged_in(iscsi_conn_t *icp, iscsi_conn_event_t event)
893 {
894 	iscsi_sess_t		*isp;
895 	iscsi_hba_t		*ihp;
896 
897 	ASSERT(icp != NULL);
898 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN);
899 	isp = icp->conn_sess;
900 	ASSERT(isp != NULL);
901 	ihp = isp->sess_hba;
902 	ASSERT(ihp != NULL);
903 
904 	/* switch on event change */
905 	switch (event) {
906 	/*
907 	 * -T8: An internal event of receiving a Logout response (success)
908 	 *	on another connection for a "close the session" Logout request
909 	 *	was received, thus closing this connection requiring no further
910 	 *	cleanup.
911 	 */
912 	case ISCSI_CONN_EVENT_T8:
913 		icp->conn_state = ISCSI_CONN_STATE_FREE;
914 
915 		/* stop tx thread */
916 		(void) iscsi_thread_stop(icp->conn_tx_thread);
917 
918 		/* Disconnect connection */
919 		iscsi_net->close(icp->conn_socket);
920 
921 		/* Notify session that a connection logged out */
922 		mutex_enter(&isp->sess_state_mutex);
923 		iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3);
924 		mutex_exit(&isp->sess_state_mutex);
925 		break;
926 
927 	/*
928 	 * -T9: An internal event that indicates the readiness to start the
929 	 *	Logout process was received, thus prompting an iSCSI Logout
930 	 *	to be sent by the initiator.
931 	 */
932 	case ISCSI_CONN_EVENT_T9:
933 		/* FALLTHRU */
934 
935 	/*
936 	 * -T11: Aync PDU with AsyncEvent "Request Logout" was recevied
937 	 */
938 	case ISCSI_CONN_EVENT_T11:
939 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT;
940 
941 		(void) iscsi_handle_logout(icp);
942 		break;
943 
944 	/*
945 	 * -T14: One or more of the following events case this transition:
946 	 *	- Header Digest Error
947 	 *	- Protocol Error
948 	 */
949 	case ISCSI_CONN_EVENT_T14:
950 		icp->conn_state = ISCSI_CONN_STATE_FAILED;
951 
952 		/* stop tx thread */
953 		(void) iscsi_thread_stop(icp->conn_tx_thread);
954 
955 		/*
956 		 * Error Recovery Level 0 states we should drop
957 		 * the connection here.  Then we will fall through
958 		 * and treat this event like a T15.
959 		 */
960 		iscsi_net->close(icp->conn_socket);
961 
962 		/* FALLTHRU */
963 
964 	/*
965 	 * -T15: One or more of the following events caused this transition
966 	 *	- Internal event that indicates a transport connection timeout
967 	 *	  was received thus prompting transport RESET or transport
968 	 *	  connection closure.
969 	 *	- A transport RESET
970 	 *	- A transport disconnect indication.
971 	 *	- Async PDU with AsyncEvent "Drop connection" (for this CID)
972 	 *	- Async PDU with AsyncEvent "Drop all connections"
973 	 */
974 	case ISCSI_CONN_EVENT_T15:
975 		icp->conn_state = ISCSI_CONN_STATE_FAILED;
976 
977 		/* stop tx thread, no-op if already done for T14 */
978 		(void) iscsi_thread_stop(icp->conn_tx_thread);
979 
980 		iscsi_conn_flush_active_cmds(icp);
981 
982 		mutex_enter(&isp->sess_state_mutex);
983 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N5);
984 		mutex_exit(&isp->sess_state_mutex);
985 
986 		/*
987 		 * If session type is NORMAL, create a new login task
988 		 * to get this connection reestablished.
989 		 */
990 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
991 			iscsi_conn_retry(isp, icp);
992 		} else {
993 			icp->conn_state = ISCSI_CONN_STATE_FREE;
994 			mutex_enter(&isp->sess_state_mutex);
995 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
996 			mutex_exit(&isp->sess_state_mutex);
997 		}
998 		break;
999 
1000 	/* All other events are invalid for this state */
1001 	default:
1002 		ASSERT(FALSE);
1003 	}
1004 }
1005 
1006 
1007 /*
1008  * iscsi_conn_state_in_logout -
1009  *
1010  */
1011 static void
1012 iscsi_conn_state_in_logout(iscsi_conn_t *icp, iscsi_conn_event_t event)
1013 {
1014 	iscsi_sess_t	*isp	= NULL;
1015 
1016 	ASSERT(icp != NULL);
1017 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGOUT);
1018 	isp = icp->conn_sess;
1019 	ASSERT(isp != NULL);
1020 
1021 	/* switch on event change */
1022 	switch (event) {
1023 	/*
1024 	 * -T11: Async PDU with AsyncEvent "Request Logout" was received again
1025 	 */
1026 	case ISCSI_CONN_EVENT_T11:
1027 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT;
1028 
1029 		/* Already in LOGOUT ignore the request */
1030 		break;
1031 
1032 	/*
1033 	 * -T17: One or more of the following events caused this transition:
1034 	 *	- Logout response, (failure i.e., a non-zero status) was
1035 	 *	received, or logout timed out.
1036 	 *	- Any of the events specified for T15
1037 	 *
1038 	 * -T14: One or more of the following events case this transition:
1039 	 *	- Header Digest Error
1040 	 *	- Protocol Error
1041 	 *
1042 	 * -T15: One or more of the following events caused this transition
1043 	 *	- Internal event that indicates a transport connection timeout
1044 	 *	  was received thus prompting transport RESET or transport
1045 	 *	  connection closure.
1046 	 *	- A transport RESET
1047 	 *	- A transport disconnect indication.
1048 	 *	- Async PDU with AsyncEvent "Drop connection" (for this CID)
1049 	 *	- Async PDU with AsyncEvent "Drop all connections"
1050 	 */
1051 	case ISCSI_CONN_EVENT_T17:
1052 	case ISCSI_CONN_EVENT_T14:
1053 	case ISCSI_CONN_EVENT_T15:
1054 		icp->conn_state = ISCSI_CONN_STATE_FREE;
1055 
1056 		/* stop tx thread */
1057 		(void) iscsi_thread_stop(icp->conn_tx_thread);
1058 
1059 		/* Disconnect Connection */
1060 		iscsi_net->close(icp->conn_socket);
1061 
1062 		iscsi_conn_flush_active_cmds(icp);
1063 
1064 		/* Notify session of a failed logout */
1065 		mutex_enter(&isp->sess_state_mutex);
1066 		iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3);
1067 		mutex_exit(&isp->sess_state_mutex);
1068 		break;
1069 
1070 	/* All other events are invalid for this state */
1071 	default:
1072 		ASSERT(FALSE);
1073 	}
1074 }
1075 
1076 
1077 /*
1078  * iscsi_conn_state_failed -
1079  *
1080  */
1081 static void
1082 iscsi_conn_state_failed(iscsi_conn_t *icp, iscsi_conn_event_t event)
1083 {
1084 	iscsi_sess_t	*isp;
1085 
1086 	ASSERT(icp != NULL);
1087 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FAILED);
1088 	isp = icp->conn_sess;
1089 	ASSERT(isp != NULL);
1090 
1091 	/* switch on event change */
1092 	switch (event) {
1093 
1094 	/*
1095 	 * -T5: The final iSCSI Login response with a Status-Class of zero
1096 	 *	was received.
1097 	 */
1098 	case ISCSI_CONN_EVENT_T5:
1099 		iscsi_conn_logged_in(isp, icp);
1100 		break;
1101 
1102 	/*
1103 	 * -T30: One of the following event caused the transition:
1104 	 *	- Thefinal iSCSI Login response was received with a non-zero
1105 	 *	  Status-Class.
1106 	 */
1107 	case ISCSI_CONN_EVENT_T30:
1108 		icp->conn_state = ISCSI_CONN_STATE_FREE;
1109 
1110 		mutex_enter(&isp->sess_state_mutex);
1111 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
1112 		mutex_exit(&isp->sess_state_mutex);
1113 
1114 		break;
1115 
1116 	/*
1117 	 * -T7: One of the following events caused the transition:
1118 	 *	- Login timed out.
1119 	 *	- A transport disconnect indication was received.
1120 	 *	- A transport reset was received.
1121 	 *	- An internal event indicating a transport timeout was
1122 	 *	  received.
1123 	 *	- An internal event of receiving a Logout repsonse (success)
1124 	 *	  on another connection for a "close the session" Logout
1125 	 *	  request was received.
1126 	 *	* In all these cases, the transport connection is closed.
1127 	 */
1128 	case ISCSI_CONN_EVENT_T7:
1129 		icp->conn_state = ISCSI_CONN_STATE_POLLING;
1130 
1131 		mutex_enter(&isp->sess_state_mutex);
1132 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
1133 		mutex_exit(&isp->sess_state_mutex);
1134 
1135 		iscsi_conn_retry(isp, icp);
1136 		break;
1137 
1138 	/* There are no valid transition out of this state. */
1139 	default:
1140 		ASSERT(FALSE);
1141 	}
1142 }
1143 
1144 /*
1145  * iscsi_conn_state_polling -
1146  *
1147  * S6: POLLING - State on instantiation, or after successful
1148  * connection closure.
1149  */
1150 static void
1151 iscsi_conn_state_polling(iscsi_conn_t *icp, iscsi_conn_event_t event)
1152 {
1153 	iscsi_sess_t *isp = NULL;
1154 
1155 	ASSERT(icp != NULL);
1156 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_POLLING);
1157 	isp = icp->conn_sess;
1158 	ASSERT(isp != NULL);
1159 
1160 	/* switch on event change */
1161 	switch (event) {
1162 	/*
1163 	 * -T5: The final iSCSI Login response with a Status-Class of zero
1164 	 *	was received.
1165 	 */
1166 	case ISCSI_CONN_EVENT_T5:
1167 		iscsi_conn_logged_in(isp, icp);
1168 		break;
1169 
1170 	/*
1171 	 * -T30: One of the following event caused the transition:
1172 	 *	- Thefinal iSCSI Login response was received with a non-zero
1173 	 *	  Status-Class.
1174 	 */
1175 	case ISCSI_CONN_EVENT_T30:
1176 		icp->conn_state = ISCSI_CONN_STATE_FREE;
1177 
1178 		mutex_enter(&isp->sess_state_mutex);
1179 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
1180 		mutex_exit(&isp->sess_state_mutex);
1181 
1182 		break;
1183 
1184 	/*
1185 	 * -T7: One of the following events caused the transition:
1186 	 *	- Login timed out.
1187 	 *	- A transport disconnect indication was received.
1188 	 *	- A transport reset was received.
1189 	 *	- An internal event indicating a transport timeout was
1190 	 *	  received.
1191 	 *	- An internal event of receiving a Logout repsonse (success)
1192 	 *	  on another connection for a "close the session" Logout
1193 	 *	  request was received.
1194 	 *	* In all these cases, the transport connection is closed.
1195 	 */
1196 	case ISCSI_CONN_EVENT_T7:
1197 		/*
1198 		 * If session type is NORMAL, create a new login task
1199 		 * to get this connection reestablished.
1200 		 */
1201 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
1202 			iscsi_conn_retry(isp, icp);
1203 		} else {
1204 			icp->conn_state = ISCSI_CONN_STATE_FREE;
1205 		}
1206 		break;
1207 
1208 	/* All other events are invalid for this state */
1209 	default:
1210 		ASSERT(FALSE);
1211 	}
1212 }
1213 
1214 /*
1215  * iscsi_conn_event_str - converts event enum to a string
1216  */
1217 static char *
1218 iscsi_conn_event_str(iscsi_conn_event_t event)
1219 {
1220 	switch (event) {
1221 	case ISCSI_CONN_EVENT_T1:
1222 		return ("T1");
1223 	case ISCSI_CONN_EVENT_T5:
1224 		return ("T5");
1225 	case ISCSI_CONN_EVENT_T7:
1226 		return ("T7");
1227 	case ISCSI_CONN_EVENT_T8:
1228 		return ("T8");
1229 	case ISCSI_CONN_EVENT_T9:
1230 		return ("T9");
1231 	case ISCSI_CONN_EVENT_T11:
1232 		return ("T11");
1233 	case ISCSI_CONN_EVENT_T14:
1234 		return ("T14");
1235 	case ISCSI_CONN_EVENT_T15:
1236 		return ("T15");
1237 	case ISCSI_CONN_EVENT_T17:
1238 		return ("T17");
1239 	case ISCSI_CONN_EVENT_T30:
1240 		return ("T30");
1241 
1242 	default:
1243 		return ("unknown");
1244 	}
1245 }
1246 
1247 /*
1248  * iscsi_conn_flush_active_cmds - flush all active icmdps
1249  *	for a connection.
1250  */
1251 static void
1252 iscsi_conn_flush_active_cmds(iscsi_conn_t *icp)
1253 {
1254 	iscsi_cmd_t	*icmdp;
1255 	iscsi_sess_t	*isp;
1256 	boolean_t	lock_held = B_FALSE;
1257 
1258 	ASSERT(icp != NULL);
1259 	isp = icp->conn_sess;
1260 	ASSERT(isp != NULL);
1261 
1262 	if (mutex_owned(&icp->conn_queue_active.mutex)) {
1263 		lock_held = B_TRUE;
1264 	} else {
1265 		mutex_enter(&icp->conn_queue_active.mutex);
1266 	}
1267 
1268 	/* Flush active queue */
1269 	icmdp = icp->conn_queue_active.head;
1270 	while (icmdp != NULL) {
1271 		iscsi_cmd_state_machine(icmdp,
1272 		    ISCSI_CMD_EVENT_E7, isp);
1273 		icmdp = icp->conn_queue_active.head;
1274 	}
1275 
1276 	if (lock_held == B_FALSE) {
1277 		mutex_exit(&icp->conn_queue_active.mutex);
1278 	}
1279 }
1280 
1281 
1282 /*
1283  * iscsi_conn_logged_in - connection has successfully logged in
1284  */
1285 static void
1286 iscsi_conn_logged_in(iscsi_sess_t *isp, iscsi_conn_t *icp)
1287 {
1288 	ASSERT(isp != NULL);
1289 	ASSERT(icp != NULL);
1290 
1291 	icp->conn_state = ISCSI_CONN_STATE_LOGGED_IN;
1292 	/*
1293 	 * We need to drop the connection state lock
1294 	 * before updating the session state.  On update
1295 	 * of the session state it will enumerate the
1296 	 * target.  If we hold the lock during enumeration
1297 	 * will block the watchdog thread from timing
1298 	 * a scsi_pkt, if required.  This will lead to
1299 	 * a possible hang condition.
1300 	 *
1301 	 * Also the lock is no longer needed once the
1302 	 * connection state was updated.
1303 	 */
1304 	mutex_exit(&icp->conn_state_mutex);
1305 
1306 	/* startup threads */
1307 	(void) iscsi_thread_start(icp->conn_rx_thread);
1308 	(void) iscsi_thread_start(icp->conn_tx_thread);
1309 
1310 	/* Notify the session that a connection is logged in */
1311 	mutex_enter(&isp->sess_state_mutex);
1312 	iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N1);
1313 	mutex_exit(&isp->sess_state_mutex);
1314 
1315 	mutex_enter(&icp->conn_state_mutex);
1316 }
1317 
1318 /*
1319  * iscsi_conn_retry - retry connect/login
1320  */
1321 static void
1322 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
1323 {
1324 	iscsi_task_t *itp;
1325 
1326 	ASSERT(isp != NULL);
1327 	ASSERT(icp != NULL);
1328 
1329 	/* set login min/max time values */
1330 	iscsi_conn_set_login_min_max(icp,
1331 	    ISCSI_CONN_DEFAULT_LOGIN_MIN,
1332 	    ISCSI_CONN_DEFAULT_LOGIN_MAX);
1333 
1334 	/*
1335 	 * Sync base connection information before login.
1336 	 * A login redirection might have shifted the
1337 	 * current information from the base.
1338 	 */
1339 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
1340 	    sizeof (icp->conn_curr_addr));
1341 
1342 	/* schedule login task */
1343 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
1344 	itp->t_arg = icp;
1345 	itp->t_blocking = B_FALSE;
1346 	if (ddi_taskq_dispatch(isp->sess_taskq,
1347 	    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
1348 	    DDI_SUCCESS) {
1349 		kmem_free(itp, sizeof (iscsi_task_t));
1350 		cmn_err(CE_WARN,
1351 		    "iscsi connection(%u) failure - "
1352 		    "unable to schedule login task",
1353 		    icp->conn_oid);
1354 
1355 		icp->conn_state = ISCSI_CONN_STATE_FREE;
1356 		mutex_enter(&isp->sess_state_mutex);
1357 		iscsi_sess_state_machine(isp,
1358 		    ISCSI_SESS_EVENT_N6);
1359 		mutex_exit(&isp->sess_state_mutex);
1360 	}
1361 }
1362