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