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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * iSCSI connection interfaces
26  */
27 
28 #define	ISCSI_ICS_NAMES
29 #include "iscsi.h"
30 #include "persistent.h"
31 #include <sys/bootprops.h>
32 
33 extern ib_boot_prop_t   *iscsiboot_prop;
34 
35 static void iscsi_client_notify_task(void *cn_task_void);
36 
37 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
38 
39 #define	SHUTDOWN_TIMEOUT	180 /* seconds */
40 
41 extern int modrootloaded;
42 
43 boolean_t iscsi_conn_logging = B_FALSE;
44 
45 #define	ISCSI_LOGIN_TPGT_NEGO_ERROR(icp) \
46 	(((icp)->conn_login_state == LOGIN_ERROR) && \
47 	((icp)->conn_login_status == ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL))
48 
49 /*
50  * +--------------------------------------------------------------------+
51  * | External Connection Interfaces					|
52  * +--------------------------------------------------------------------+
53  */
54 
55 /*
56  * iscsi_conn_create - This creates an iscsi connection structure and
57  * associates it with a session structure.  The session's sess_conn_list_rwlock
58  * should be held as a writer before calling this function.
59  */
60 iscsi_status_t
61 iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp)
62 {
63 	iscsi_conn_t	*icp	= NULL;
64 	char		th_name[ISCSI_TH_MAX_NAME_LEN];
65 
66 	/* See if this connection already exists */
67 	for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) {
68 
69 		/*
70 		 * Compare the ioctl information to see if
71 		 * its a match for this connection.  (This
72 		 * is done by making sure the IPs are of
73 		 * the same size and then they are the
74 		 * same value.
75 		 */
76 		if (bcmp(&icp->conn_base_addr, addr,
77 		    SIZEOF_SOCKADDR(addr)) == 0) {
78 			/* It's a match, record this connection */
79 			break;
80 		}
81 	}
82 
83 	/* If icp is found return it */
84 	if (icp != NULL) {
85 		*icpp = icp;
86 		return (ISCSI_STATUS_SUCCESS);
87 	}
88 
89 	/* We are creating the connection, allocate, and setup */
90 	icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP);
91 
92 	/*
93 	 * Setup connection
94 	 */
95 	icp->conn_sig			= ISCSI_SIG_CONN;
96 	icp->conn_state			= ISCSI_CONN_STATE_FREE;
97 	mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
98 	cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
99 	mutex_init(&icp->conn_login_mutex, NULL, MUTEX_DRIVER, NULL);
100 	cv_init(&icp->conn_login_cv, NULL, CV_DRIVER, NULL);
101 	icp->conn_state_destroy		= B_FALSE;
102 	idm_sm_audit_init(&icp->conn_state_audit);
103 	icp->conn_sess			= isp;
104 
105 	mutex_enter(&iscsi_oid_mutex);
106 	icp->conn_oid = iscsi_oid++;
107 	mutex_exit(&iscsi_oid_mutex);
108 
109 	/*
110 	 * IDM CN taskq
111 	 */
112 
113 	if (snprintf(th_name, sizeof (th_name) - 1,
114 	    ISCSI_CONN_CN_TASKQ_NAME_FORMAT,
115 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
116 	    icp->conn_oid) >= sizeof (th_name)) {
117 		cv_destroy(&icp->conn_state_change);
118 		mutex_destroy(&icp->conn_state_mutex);
119 		kmem_free(icp, sizeof (iscsi_conn_t));
120 		*icpp = NULL;
121 		return (ISCSI_STATUS_INTERNAL_ERROR);
122 	}
123 
124 	icp->conn_cn_taskq =
125 	    ddi_taskq_create(icp->conn_sess->sess_hba->hba_dip, th_name, 1,
126 	    TASKQ_DEFAULTPRI, 0);
127 	if (icp->conn_cn_taskq == NULL) {
128 		cv_destroy(&icp->conn_state_change);
129 		mutex_destroy(&icp->conn_state_mutex);
130 		kmem_free(icp, sizeof (iscsi_conn_t));
131 		*icpp = NULL;
132 		return (ISCSI_STATUS_INTERNAL_ERROR);
133 	}
134 
135 	/* Creation of the transfer thread */
136 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
137 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
138 	    icp->conn_oid) >= sizeof (th_name)) {
139 		cv_destroy(&icp->conn_state_change);
140 		mutex_destroy(&icp->conn_state_mutex);
141 		kmem_free(icp, sizeof (iscsi_conn_t));
142 		ddi_taskq_destroy(icp->conn_cn_taskq);
143 		*icpp = NULL;
144 		return (ISCSI_STATUS_INTERNAL_ERROR);
145 	}
146 
147 	icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
148 	    th_name, iscsi_tx_thread, icp);
149 
150 	/* setup connection queues */
151 	iscsi_init_queue(&icp->conn_queue_active);
152 	iscsi_init_queue(&icp->conn_queue_idm_aborting);
153 
154 	bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
155 
156 	/* Add new connection to the session connection list */
157 	icp->conn_cid = isp->sess_conn_next_cid++;
158 	if (isp->sess_conn_list == NULL) {
159 		isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp;
160 	} else {
161 		isp->sess_conn_list_last_ptr->conn_next = icp;
162 		isp->sess_conn_list_last_ptr = icp;
163 	}
164 
165 	KSTAT_INC_SESS_CNTR_CONN(isp);
166 	(void) iscsi_conn_kstat_init(icp);
167 
168 	*icpp = icp;
169 
170 	return (ISCSI_STATUS_SUCCESS);
171 }
172 
173 /*
174  * iscsi_conn_online - This attempts to take a connection from
175  * ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN.
176  */
177 iscsi_status_t
178 iscsi_conn_online(iscsi_conn_t *icp)
179 {
180 	iscsi_task_t	*itp;
181 	iscsi_status_t	rval;
182 
183 	ASSERT(icp != NULL);
184 	ASSERT(mutex_owned(&icp->conn_state_mutex));
185 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
186 
187 	/*
188 	 * If we are attempting to connect then for the purposes of the
189 	 * other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN.
190 	 */
191 	iscsi_conn_update_state_locked(icp, ISCSI_CONN_STATE_IN_LOGIN);
192 	mutex_exit(&icp->conn_state_mutex);
193 
194 	/*
195 	 * Sync base connection information before login
196 	 * A login redirection might have shifted the
197 	 * current information from the base.
198 	 */
199 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
200 	    sizeof (icp->conn_curr_addr));
201 
202 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
203 	ASSERT(itp != NULL);
204 
205 	itp->t_arg = icp;
206 	itp->t_blocking = B_TRUE;
207 	rval = iscsi_login_start(itp);
208 	kmem_free(itp, sizeof (iscsi_task_t));
209 
210 	mutex_enter(&icp->conn_state_mutex);
211 
212 	return (rval);
213 }
214 
215 /*
216  * iscsi_conn_offline - This attempts to take a connection from
217  * any state to ISCSI_CONN_STATE_FREE.
218  */
219 iscsi_status_t
220 iscsi_conn_offline(iscsi_conn_t *icp)
221 {
222 	clock_t		delay;
223 
224 	ASSERT(icp != NULL);
225 
226 	/*
227 	 * We can only destroy a connection if its either in
228 	 * a state of FREE or LOGGED.  The other states are
229 	 * transitionary and its unsafe to perform actions
230 	 * on the connection in those states.  Set a flag
231 	 * on the connection to influence the transitions
232 	 * to quickly complete.  Then wait for a state
233 	 * transition.
234 	 *
235 	 * ISCSI_CONN_STATE_LOGGED_IN is set immediately at the
236 	 * start of CN_NOTIFY_FFP processing. icp->conn_state_ffp
237 	 * is set to true at the end of ffp processing, at which
238 	 * point any session updates are complete.  We don't
239 	 * want to start offlining the connection before we're
240 	 * done completing the FFP processing since this might
241 	 * interrupt the discovery process.
242 	 */
243 	delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
244 	mutex_enter(&icp->conn_state_mutex);
245 	icp->conn_state_destroy = B_TRUE;
246 	while ((((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
247 	    (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN)) ||
248 	    ((icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) &&
249 	    !icp->conn_state_ffp)) &&
250 	    (ddi_get_lbolt() < delay)) {
251 		/* wait for transition */
252 		(void) cv_timedwait(&icp->conn_state_change,
253 		    &icp->conn_state_mutex, delay);
254 	}
255 
256 	switch (icp->conn_state) {
257 	case ISCSI_CONN_STATE_FREE:
258 		break;
259 	case ISCSI_CONN_STATE_LOGGED_IN:
260 		if (icp->conn_state_ffp) {
261 			/* Hold is released in iscsi_handle_logout */
262 			idm_conn_hold(icp->conn_ic);
263 			(void) iscsi_handle_logout(icp);
264 		} else {
265 			icp->conn_state_destroy = B_FALSE;
266 			mutex_exit(&icp->conn_state_mutex);
267 			return (ISCSI_STATUS_INTERNAL_ERROR);
268 		}
269 		break;
270 	case ISCSI_CONN_STATE_IN_LOGIN:
271 	case ISCSI_CONN_STATE_IN_LOGOUT:
272 	case ISCSI_CONN_STATE_FAILED:
273 	case ISCSI_CONN_STATE_POLLING:
274 	default:
275 		icp->conn_state_destroy = B_FALSE;
276 		mutex_exit(&icp->conn_state_mutex);
277 		return (ISCSI_STATUS_INTERNAL_ERROR);
278 	}
279 	mutex_exit(&icp->conn_state_mutex);
280 
281 	return (ISCSI_STATUS_SUCCESS);
282 }
283 
284 /*
285  * iscsi_conn_destroy - This destroys an iscsi connection structure
286  * and de-associates it with the session.  The connection should
287  * already been in the ISCSI_CONN_STATE_FREE when attempting this
288  * operation.
289  */
290 iscsi_status_t
291 iscsi_conn_destroy(iscsi_conn_t *icp)
292 {
293 	iscsi_sess_t	*isp;
294 	iscsi_conn_t	*t_icp;
295 
296 	ASSERT(icp != NULL);
297 	isp = icp->conn_sess;
298 	ASSERT(isp != NULL);
299 
300 	if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
301 		return (ISCSI_STATUS_INTERNAL_ERROR);
302 	}
303 
304 	/* Destroy transfer thread */
305 	iscsi_thread_destroy(icp->conn_tx_thread);
306 	ddi_taskq_destroy(icp->conn_cn_taskq);
307 
308 	/* Terminate connection queues */
309 	iscsi_destroy_queue(&icp->conn_queue_idm_aborting);
310 	iscsi_destroy_queue(&icp->conn_queue_active);
311 
312 	cv_destroy(&icp->conn_login_cv);
313 	mutex_destroy(&icp->conn_login_mutex);
314 	cv_destroy(&icp->conn_state_change);
315 	mutex_destroy(&icp->conn_state_mutex);
316 
317 	/*
318 	 * Remove connection from sessions linked list.
319 	 */
320 	if (isp->sess_conn_list == icp) {
321 		/* connection first item in list */
322 		isp->sess_conn_list = icp->conn_next;
323 		/*
324 		 * check if this is also the last item in the list
325 		 */
326 		if (isp->sess_conn_list_last_ptr == icp) {
327 			isp->sess_conn_list_last_ptr = NULL;
328 		}
329 	} else {
330 		/*
331 		 * search session list for icp pointing
332 		 * to connection being removed.  Then
333 		 * update that connections next pointer.
334 		 */
335 		t_icp = isp->sess_conn_list;
336 		while (t_icp->conn_next != NULL) {
337 			if (t_icp->conn_next == icp) {
338 				break;
339 			}
340 			t_icp = t_icp->conn_next;
341 		}
342 		if (t_icp->conn_next == icp) {
343 			t_icp->conn_next = icp->conn_next;
344 			/*
345 			 * if this is the last connection in the list
346 			 * update the last_ptr to point to t_icp
347 			 */
348 			if (isp->sess_conn_list_last_ptr == icp) {
349 				isp->sess_conn_list_last_ptr = t_icp;
350 			}
351 		} else {
352 			/* couldn't find session */
353 			ASSERT(FALSE);
354 		}
355 	}
356 
357 	/* Free this Connections Data */
358 	iscsi_conn_kstat_term(icp);
359 	kmem_free(icp, sizeof (iscsi_conn_t));
360 
361 	return (ISCSI_STATUS_SUCCESS);
362 }
363 
364 
365 /*
366  * iscsi_conn_set_login_min_max - set min/max login window
367  *
368  * Used to set the min and max login window.  Input values
369  * are in seconds.
370  */
371 void
372 iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max)
373 {
374 	ASSERT(icp != NULL);
375 
376 	icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
377 	icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
378 }
379 
380 
381 /*
382  * Process the idm notifications
383  */
384 idm_status_t
385 iscsi_client_notify(idm_conn_t *ic, idm_client_notify_t icn, uintptr_t data)
386 {
387 	iscsi_cn_task_t		*cn;
388 	iscsi_conn_t		*icp = ic->ic_handle;
389 	iscsi_sess_t		*isp;
390 
391 	/*
392 	 * Don't access icp if the notification is CN_CONNECT_DESTROY
393 	 * since icp may have already been freed.
394 	 *
395 	 * In particular, we cannot audit the CN_CONNECT_DESTROY event.
396 	 *
397 	 * Handle a few cases immediately, the rest in a task queue.
398 	 */
399 	switch (icn) {
400 	case CN_CONNECT_FAIL:
401 	case CN_LOGIN_FAIL:
402 		/*
403 		 * Wakeup any thread waiting for login stuff to happen.
404 		 */
405 		ASSERT(icp != NULL);
406 
407 		mutex_enter(&icp->conn_state_mutex);
408 		idm_sm_audit_event(&icp->conn_state_audit,
409 		    SAS_ISCSI_CONN, icp->conn_state, icn, data);
410 		mutex_exit(&icp->conn_state_mutex);
411 		iscsi_login_update_state(icp, LOGIN_ERROR);
412 		return (IDM_STATUS_SUCCESS);
413 
414 	case CN_READY_FOR_LOGIN:
415 		idm_conn_hold(ic); /* Released in CN_CONNECT_LOST */
416 		ASSERT(icp != NULL);
417 
418 		mutex_enter(&icp->conn_state_mutex);
419 		idm_sm_audit_event(&icp->conn_state_audit,
420 		    SAS_ISCSI_CONN, icp->conn_state, icn, data);
421 		icp->conn_state_idm_connected = B_TRUE;
422 		cv_broadcast(&icp->conn_state_change);
423 		mutex_exit(&icp->conn_state_mutex);
424 
425 		iscsi_login_update_state(icp, LOGIN_READY);
426 		return (IDM_STATUS_SUCCESS);
427 
428 	case CN_CONNECT_DESTROY:
429 		/*
430 		 * We released any dependecies we had on this object in
431 		 * either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need
432 		 * to destroy the IDM connection now.
433 		 */
434 		idm_ini_conn_destroy(ic);
435 		return (IDM_STATUS_SUCCESS);
436 	}
437 
438 	ASSERT(icp != NULL);
439 	mutex_enter(&icp->conn_state_mutex);
440 	idm_sm_audit_event(&icp->conn_state_audit,
441 	    SAS_ISCSI_CONN, icp->conn_state, icn, data);
442 	mutex_exit(&icp->conn_state_mutex);
443 	isp = icp->conn_sess;
444 
445 	/*
446 	 * Dispatch notifications to the taskq since they often require
447 	 * long blocking operations.  In the case of CN_CONNECT_DESTROY
448 	 * we actually just want to destroy the connection which we
449 	 * can't do in the IDM taskq context.
450 	 */
451 	cn = kmem_alloc(sizeof (*cn), KM_SLEEP);
452 
453 	cn->ct_ic = ic;
454 	cn->ct_icn = icn;
455 	cn->ct_data = data;
456 
457 	idm_conn_hold(ic);
458 
459 	if (ddi_taskq_dispatch(icp->conn_cn_taskq,
460 	    iscsi_client_notify_task, cn, DDI_SLEEP) != DDI_SUCCESS) {
461 		idm_conn_rele(ic);
462 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
463 		    "unable to schedule notify task", icp->conn_oid);
464 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
465 		mutex_enter(&isp->sess_state_mutex);
466 		iscsi_sess_state_machine(isp,
467 		    ISCSI_SESS_EVENT_N6);
468 		mutex_exit(&isp->sess_state_mutex);
469 	}
470 
471 	return (IDM_STATUS_SUCCESS);
472 }
473 
474 static void
475 iscsi_client_notify_task(void *cn_task_void)
476 {
477 	iscsi_cn_task_t		*cn_task = cn_task_void;
478 	iscsi_conn_t		*icp;
479 	iscsi_sess_t		*isp;
480 	idm_conn_t		*ic;
481 	idm_client_notify_t	icn;
482 	uintptr_t		data;
483 	idm_ffp_disable_t	disable_type;
484 	boolean_t		in_login;
485 
486 	ic = cn_task->ct_ic;
487 	icn = cn_task->ct_icn;
488 	data = cn_task->ct_data;
489 
490 	icp = ic->ic_handle;
491 	ASSERT(icp != NULL);
492 	isp = icp->conn_sess;
493 
494 	switch (icn) {
495 	case CN_FFP_ENABLED:
496 		mutex_enter(&icp->conn_state_mutex);
497 		icp->conn_async_logout = B_FALSE;
498 		icp->conn_state_ffp = B_TRUE;
499 		cv_broadcast(&icp->conn_state_change);
500 		mutex_exit(&icp->conn_state_mutex);
501 
502 		/*
503 		 * This logic assumes that the IDM login-snooping code
504 		 * and the initiator login code will agree to go when
505 		 * the connection is in FFP or final error received.
506 		 * The reason we do this is that we don't want to process
507 		 * CN_FFP_DISABLED until CN_FFP_ENABLED has been full handled.
508 		 */
509 		mutex_enter(&icp->conn_login_mutex);
510 		while ((icp->conn_login_state != LOGIN_FFP) &&
511 		    (icp->conn_login_state != LOGIN_ERROR)) {
512 			cv_wait(&icp->conn_login_cv, &icp->conn_login_mutex);
513 		}
514 		mutex_exit(&icp->conn_login_mutex);
515 		break;
516 	case CN_FFP_DISABLED:
517 		disable_type = (idm_ffp_disable_t)data;
518 
519 		mutex_enter(&icp->conn_state_mutex);
520 		switch (disable_type) {
521 		case FD_SESS_LOGOUT:
522 		case FD_CONN_LOGOUT:
523 			if (icp->conn_async_logout) {
524 				/*
525 				 * Our logout was in response to an
526 				 * async logout request so treat this
527 				 * like a connection failure (we will
528 				 * try to re-establish the connection)
529 				 */
530 				iscsi_conn_update_state_locked(icp,
531 				    ISCSI_CONN_STATE_FAILED);
532 			} else {
533 				/*
534 				 * Logout due to to user config change,
535 				 * we will not try to re-establish
536 				 * the connection.
537 				 */
538 				iscsi_conn_update_state_locked(icp,
539 				    ISCSI_CONN_STATE_IN_LOGOUT);
540 				/*
541 				 * Hold off generating the ISCSI_SESS_EVENT_N3
542 				 * event until we get the CN_CONNECT_LOST
543 				 * notification.  This matches the pre-IDM
544 				 * implementation better.
545 				 */
546 			}
547 			break;
548 
549 		case FD_CONN_FAIL:
550 		default:
551 			if (icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) {
552 				iscsi_conn_update_state_locked(icp,
553 				    ISCSI_CONN_STATE_FREE);
554 			} else {
555 				iscsi_conn_update_state_locked(icp,
556 				    ISCSI_CONN_STATE_FAILED);
557 			}
558 			break;
559 		}
560 
561 		icp->conn_state_ffp = B_FALSE;
562 		cv_broadcast(&icp->conn_state_change);
563 		mutex_exit(&icp->conn_state_mutex);
564 
565 		break;
566 	case CN_CONNECT_LOST:
567 		/*
568 		 * We only care about CN_CONNECT_LOST if we've logged in.  IDM
569 		 * sends a flag as the data payload to indicate whether we
570 		 * were trying to login.  The CN_LOGIN_FAIL notification
571 		 * gives us what we need to know for login failures and
572 		 * otherwise we will need to keep a bunch of state to know
573 		 * what CN_CONNECT_LOST means to us.
574 		 */
575 		in_login = (boolean_t)data;
576 		if (in_login ||
577 		    (icp->conn_prev_state == ISCSI_CONN_STATE_IN_LOGIN)) {
578 			mutex_enter(&icp->conn_state_mutex);
579 
580 			icp->conn_state_idm_connected = B_FALSE;
581 			cv_broadcast(&icp->conn_state_change);
582 			mutex_exit(&icp->conn_state_mutex);
583 
584 			/* Release connect hold from CN_READY_FOR_LOGIN */
585 			idm_conn_rele(ic);
586 			break;
587 		}
588 
589 		/* Any remaining commands are never going to finish */
590 		iscsi_conn_flush_active_cmds(icp);
591 
592 		/*
593 		 * The connection is no longer active so cleanup any
594 		 * references to the connection and release any holds so
595 		 * that IDM can finish cleanup.
596 		 */
597 		mutex_enter(&icp->conn_state_mutex);
598 		if (icp->conn_state != ISCSI_CONN_STATE_FAILED) {
599 
600 			mutex_enter(&isp->sess_state_mutex);
601 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N3);
602 			mutex_exit(&isp->sess_state_mutex);
603 
604 			iscsi_conn_update_state_locked(icp,
605 			    ISCSI_CONN_STATE_FREE);
606 		} else {
607 
608 			mutex_enter(&isp->sess_state_mutex);
609 			iscsi_sess_state_machine(isp,
610 			    ISCSI_SESS_EVENT_N5);
611 			mutex_exit(&isp->sess_state_mutex);
612 
613 			/*
614 			 * If session type is NORMAL, try to reestablish the
615 			 * connection.
616 			 */
617 			if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
618 			    !(ISCSI_LOGIN_TPGT_NEGO_ERROR(icp))) {
619 				iscsi_conn_retry(isp, icp);
620 			} else {
621 
622 				mutex_enter(&isp->sess_state_mutex);
623 				iscsi_sess_state_machine(isp,
624 				    ISCSI_SESS_EVENT_N6);
625 				mutex_exit(&isp->sess_state_mutex);
626 
627 				iscsi_conn_update_state_locked(icp,
628 				    ISCSI_CONN_STATE_FREE);
629 			}
630 		}
631 
632 		(void) iscsi_thread_stop(icp->conn_tx_thread);
633 
634 		icp->conn_state_idm_connected = B_FALSE;
635 		cv_broadcast(&icp->conn_state_change);
636 		mutex_exit(&icp->conn_state_mutex);
637 
638 		/* Release connect hold from CN_READY_FOR_LOGIN */
639 		idm_conn_rele(ic);
640 		break;
641 	default:
642 		ISCSI_CONN_LOG(CE_WARN,
643 		    "iscsi_client_notify: unknown notification: "
644 		    "%x: NOT IMPLEMENTED YET: icp: %p ic: %p ",
645 		    icn, (void *)icp, (void *)ic);
646 		break;
647 	}
648 	/* free the task notify structure we allocated in iscsi_client_notify */
649 	kmem_free(cn_task, sizeof (*cn_task));
650 
651 	/* Release the hold we acquired in iscsi_client_notify */
652 	idm_conn_rele(ic);
653 }
654 
655 /*
656  * iscsi_conn_sync_params - used to update connection parameters
657  *
658  * Used to update connection parameters with current configured
659  * parameters in the persistent store.  This should be called
660  * before starting to make a new iscsi connection in iscsi_login.
661  */
662 iscsi_status_t
663 iscsi_conn_sync_params(iscsi_conn_t *icp)
664 {
665 	iscsi_sess_t		*isp;
666 	iscsi_hba_t		*ihp;
667 	int			param_id;
668 	persistent_param_t	pp;
669 	persistent_tunable_param_t	ptp;
670 	iscsi_config_sess_t	*ics;
671 	int			idx, size;
672 	char			*name;
673 
674 	ASSERT(icp != NULL);
675 	ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
676 	    (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
677 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING));
678 	isp = icp->conn_sess;
679 	ASSERT(isp != NULL);
680 	ihp = isp->sess_hba;
681 	ASSERT(ihp != NULL);
682 
683 	/*
684 	 * Check if someone is trying to destroy this
685 	 * connection.  If so fail the sync request,
686 	 * as a method of fast fail.
687 	 */
688 	if (icp->conn_state_destroy == B_TRUE) {
689 		return (ISCSI_STATUS_SHUTDOWN);
690 	}
691 
692 	bzero(&pp, sizeof (pp));
693 
694 	/* First get a copy of the HBA params */
695 	bcopy(&ihp->hba_params, &icp->conn_params,
696 	    sizeof (iscsi_login_params_t));
697 	bcopy(&ihp->hba_tunable_params, &icp->conn_tunable_params,
698 	    sizeof (iscsi_tunable_params_t));
699 
700 	/*
701 	 * Now we need to get the session configured
702 	 * values from the persistent store and apply
703 	 * them to our connection.
704 	 */
705 	(void) persistent_param_get((char *)isp->sess_name, &pp);
706 	for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM;
707 	    param_id++) {
708 		if (iscsiboot_prop && modrootloaded &&
709 		    !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) {
710 			/*
711 			 * iscsi boot with mpxio disabled
712 			 * while iscsi booting target's parameter overriden
713 			 * do no update target's parameters.
714 			 */
715 			if (pp.p_bitmap) {
716 				cmn_err(CE_NOTE, "Adopting "
717 				    " default login parameters in"
718 				    " boot session as MPxIO is disabled");
719 			}
720 			break;
721 		}
722 		if (pp.p_bitmap & (1 << param_id)) {
723 
724 			switch (param_id) {
725 			/*
726 			 * Boolean parameters
727 			 */
728 			case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
729 				icp->conn_params.data_pdu_in_order =
730 				    pp.p_params.data_pdu_in_order;
731 				break;
732 			case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA:
733 				icp->conn_params.immediate_data =
734 				    pp.p_params.immediate_data;
735 				break;
736 			case ISCSI_LOGIN_PARAM_INITIAL_R2T:
737 				icp->conn_params.initial_r2t =
738 				    pp.p_params.initial_r2t;
739 				break;
740 			case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER:
741 				icp->conn_params.data_pdu_in_order =
742 				    pp.p_params.data_pdu_in_order;
743 				break;
744 			/*
745 			 * Integer parameters
746 			 */
747 			case ISCSI_LOGIN_PARAM_HEADER_DIGEST:
748 				icp->conn_params.header_digest =
749 				    pp.p_params.header_digest;
750 				break;
751 			case ISCSI_LOGIN_PARAM_DATA_DIGEST:
752 				icp->conn_params.data_digest =
753 				    pp.p_params.data_digest;
754 				break;
755 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN:
756 				icp->conn_params.default_time_to_retain =
757 				    pp.p_params.default_time_to_retain;
758 				break;
759 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT:
760 				icp->conn_params.default_time_to_wait =
761 				    pp.p_params.default_time_to_wait;
762 				break;
763 			case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH:
764 				icp->conn_params.max_recv_data_seg_len =
765 				    pp.p_params.max_recv_data_seg_len;
766 				break;
767 			case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH:
768 				icp->conn_params.first_burst_length =
769 				    pp.p_params.first_burst_length;
770 				break;
771 			case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH:
772 				icp->conn_params.max_burst_length =
773 				    pp.p_params.max_burst_length;
774 				break;
775 
776 			/*
777 			 * Integer parameters which currently are unsettable
778 			 */
779 			case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS:
780 				/* FALLTHRU */
781 			case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T:
782 				/* FALLTHRU */
783 			case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL:
784 				/* FALLTHRU */
785 			default:
786 				break;
787 			}
788 		}
789 	}
790 
791 	if (persistent_get_tunable_param((char *)isp->sess_name, &ptp) ==
792 	    B_TRUE) {
793 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE) {
794 			icp->conn_tunable_params.recv_login_rsp_timeout =
795 			    ptp.p_params.recv_login_rsp_timeout;
796 		}
797 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX) {
798 			icp->conn_tunable_params.conn_login_max =
799 			    ptp.p_params.conn_login_max;
800 		}
801 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY) {
802 			icp->conn_tunable_params.polling_login_delay =
803 			    ptp.p_params.polling_login_delay;
804 		}
805 	}
806 
807 	/* Skip binding checks on discovery sessions */
808 	if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
809 		return (ISCSI_STATUS_SUCCESS);
810 	}
811 
812 	/*
813 	 * Now we need to get the current optional connection
814 	 * binding information.
815 	 */
816 	/* setup initial buffer for configured session information */
817 	size = sizeof (*ics);
818 	ics = kmem_zalloc(size, KM_SLEEP);
819 	ics->ics_in = 1;
820 
821 	/* get configured sessions information */
822 	name = (char *)isp->sess_name;
823 	if (persistent_get_config_session(name, ics) == B_FALSE) {
824 		/*
825 		 * If we were unable to get target level information
826 		 * then check the initiator level information.
827 		 */
828 		name = (char *)isp->sess_hba->hba_name;
829 		if (persistent_get_config_session(name, ics) == B_FALSE) {
830 			/*
831 			 * No hba information is found.  So assume default
832 			 * one session unbound behavior.
833 			 */
834 			ics->ics_out = 1;
835 			ics->ics_bound = B_FALSE;
836 		}
837 	}
838 
839 	if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot &&
840 	    !iscsi_chk_bootlun_mpxio(ihp)) {
841 		/*
842 		 * iscsi booting session with mpxio disabled,
843 		 * no need set multiple sessions for booting session
844 		 */
845 		ics->ics_out = 1;
846 		ics->ics_bound = B_FALSE;
847 		cmn_err(CE_NOTE, "MPxIO is disabled,"
848 		    " no need to configure multiple boot sessions");
849 	}
850 
851 	/*
852 	 * Check to make sure this session is still a configured
853 	 * session.  The user might have decreased the session
854 	 * count. (NOTE: byte 5 of the sess_isid is the session
855 	 * count (via MS/T).  This counter starts at 0.)
856 	 */
857 
858 
859 	idx = isp->sess_isid[5];
860 
861 	if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) {
862 		/*
863 		 * This is temporary session for boot session propose
864 		 * no need to bound IP for this session
865 		 */
866 		icp->conn_bound = B_FALSE;
867 		kmem_free(ics, sizeof (iscsi_config_sess_t));
868 		return (ISCSI_STATUS_SUCCESS);
869 	}
870 
871 	if (ics->ics_out <= idx) {
872 		/*
873 		 * No longer a configured session.  Return a
874 		 * failure so we don't attempt to relogin.
875 		 */
876 		return (ISCSI_STATUS_SHUTDOWN);
877 	}
878 
879 	/*
880 	 * If sessions are unbound set this information on
881 	 * the connection and return success.
882 	 */
883 	if (ics->ics_bound == B_FALSE) {
884 		icp->conn_bound = B_FALSE;
885 		kmem_free(ics, sizeof (iscsi_config_sess_t));
886 		return (ISCSI_STATUS_SUCCESS);
887 	}
888 
889 	/*
890 	 * Since the sessions are bound we need to find the matching
891 	 * binding information for the session's isid.  If this
892 	 * session's isid is > 0 then we need to get more configured
893 	 * session information to find the binding info.
894 	 */
895 	if (idx > 0) {
896 		int ics_out;
897 
898 		ics_out = ics->ics_out;
899 		/* record new size and free last buffer */
900 		size = ISCSI_SESSION_CONFIG_SIZE(ics_out);
901 		kmem_free(ics, sizeof (*ics));
902 
903 		/* allocate new buffer */
904 		ics = kmem_zalloc(size, KM_SLEEP);
905 		ics->ics_in = ics_out;
906 
907 		/* get configured sessions information */
908 		if (persistent_get_config_session(name, ics) != B_TRUE) {
909 			cmn_err(CE_NOTE, "iscsi session(%d) - "
910 			    "unable to get configured session information\n",
911 			    isp->sess_oid);
912 			kmem_free(ics, size);
913 			return (ISCSI_STATUS_SHUTDOWN);
914 		}
915 	}
916 
917 	/* Copy correct binding information to the connection */
918 	icp->conn_bound = B_TRUE;
919 	if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) {
920 		bcopy(&ics->ics_bindings[idx].i_addr.in4,
921 		    &icp->conn_bound_addr.sin4.sin_addr.s_addr,
922 		    sizeof (struct in_addr));
923 		icp->conn_bound_addr.sin4.sin_family = AF_INET;
924 	} else {
925 		bcopy(&ics->ics_bindings[idx].i_addr.in6,
926 		    &icp->conn_bound_addr.sin6.sin6_addr.s6_addr,
927 		    sizeof (struct in6_addr));
928 		icp->conn_bound_addr.sin6.sin6_family = AF_INET6;
929 	}
930 
931 	kmem_free(ics, size);
932 
933 	return (ISCSI_STATUS_SUCCESS);
934 }
935 
936 /*
937  * +--------------------------------------------------------------------+
938  * | Internal Connection Interfaces					|
939  * +--------------------------------------------------------------------+
940  */
941 
942 /*
943  * iscsi_conn_flush_active_cmds - flush all active icmdps
944  *	for a connection.
945  */
946 static void
947 iscsi_conn_flush_active_cmds(iscsi_conn_t *icp)
948 {
949 	iscsi_cmd_t	*icmdp;
950 	iscsi_sess_t	*isp;
951 	boolean_t	lock_held = B_FALSE;
952 
953 	ASSERT(icp != NULL);
954 	isp = icp->conn_sess;
955 	ASSERT(isp != NULL);
956 
957 	if (mutex_owned(&icp->conn_queue_active.mutex)) {
958 		lock_held = B_TRUE;
959 	} else {
960 		mutex_enter(&icp->conn_queue_active.mutex);
961 	}
962 
963 	/* Flush active queue */
964 	icmdp = icp->conn_queue_active.head;
965 	while (icmdp != NULL) {
966 
967 		mutex_enter(&icmdp->cmd_mutex);
968 		if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
969 			icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
970 		}
971 		mutex_exit(&icmdp->cmd_mutex);
972 
973 		iscsi_cmd_state_machine(icmdp,
974 		    ISCSI_CMD_EVENT_E7, isp);
975 		icmdp = icp->conn_queue_active.head;
976 	}
977 
978 	/* Wait for active queue to drain */
979 	while (icp->conn_queue_active.count) {
980 		mutex_exit(&icp->conn_queue_active.mutex);
981 		delay(drv_usectohz(100000));
982 		mutex_enter(&icp->conn_queue_active.mutex);
983 	}
984 
985 	if (lock_held == B_FALSE) {
986 		mutex_exit(&icp->conn_queue_active.mutex);
987 	}
988 
989 	/* Wait for IDM abort queue to drain (if necessary) */
990 	mutex_enter(&icp->conn_queue_idm_aborting.mutex);
991 	while (icp->conn_queue_idm_aborting.count) {
992 		mutex_exit(&icp->conn_queue_idm_aborting.mutex);
993 		delay(drv_usectohz(100000));
994 		mutex_enter(&icp->conn_queue_idm_aborting.mutex);
995 	}
996 	mutex_exit(&icp->conn_queue_idm_aborting.mutex);
997 }
998 
999 /*
1000  * iscsi_conn_retry - retry connect/login
1001  */
1002 void
1003 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
1004 {
1005 	iscsi_task_t *itp;
1006 
1007 	ASSERT(isp != NULL);
1008 	ASSERT(icp != NULL);
1009 
1010 	/* set login min/max time values */
1011 	iscsi_conn_set_login_min_max(icp,
1012 	    ISCSI_CONN_DEFAULT_LOGIN_MIN,
1013 	    icp->conn_tunable_params.conn_login_max);
1014 
1015 	ISCSI_CONN_LOG(CE_NOTE, "DEBUG: iscsi_conn_retry: icp: %p icp: %p ",
1016 	    (void *)icp,
1017 	    (void *)icp->conn_ic);
1018 
1019 	/*
1020 	 * Sync base connection information before login.
1021 	 * A login redirection might have shifted the
1022 	 * current information from the base.
1023 	 */
1024 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
1025 	    sizeof (icp->conn_curr_addr));
1026 
1027 	/* schedule login task */
1028 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
1029 	itp->t_arg = icp;
1030 	itp->t_blocking = B_FALSE;
1031 	if (ddi_taskq_dispatch(isp->sess_taskq,
1032 	    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
1033 	    DDI_SUCCESS) {
1034 		kmem_free(itp, sizeof (iscsi_task_t));
1035 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
1036 		    "unable to schedule login task", icp->conn_oid);
1037 
1038 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
1039 		mutex_enter(&isp->sess_state_mutex);
1040 		iscsi_sess_state_machine(isp,
1041 		    ISCSI_SESS_EVENT_N6);
1042 		mutex_exit(&isp->sess_state_mutex);
1043 	}
1044 }
1045 
1046 void
1047 iscsi_conn_update_state(iscsi_conn_t *icp, iscsi_conn_state_t
1048 			    next_state)
1049 {
1050 	mutex_enter(&icp->conn_state_mutex);
1051 	(void) iscsi_conn_update_state_locked(icp, next_state);
1052 	mutex_exit(&icp->conn_state_mutex);
1053 }
1054 
1055 void
1056 iscsi_conn_update_state_locked(iscsi_conn_t *icp,
1057 	    iscsi_conn_state_t next_state)
1058 {
1059 	ASSERT(mutex_owned(&icp->conn_state_mutex));
1060 	next_state = (next_state > ISCSI_CONN_STATE_MAX) ?
1061 	    ISCSI_CONN_STATE_MAX : next_state;
1062 	idm_sm_audit_state_change(&icp->conn_state_audit,
1063 	    SAS_ISCSI_CONN, icp->conn_state, next_state);
1064 	switch (next_state) {
1065 	case ISCSI_CONN_STATE_FREE:
1066 	case ISCSI_CONN_STATE_IN_LOGIN:
1067 	case ISCSI_CONN_STATE_LOGGED_IN:
1068 	case ISCSI_CONN_STATE_IN_LOGOUT:
1069 	case ISCSI_CONN_STATE_FAILED:
1070 	case ISCSI_CONN_STATE_POLLING:
1071 		ISCSI_CONN_LOG(CE_NOTE,
1072 		    "iscsi_conn_update_state conn %p %s(%d) -> %s(%d)",
1073 		    (void *)icp,
1074 		    iscsi_ics_name[icp->conn_state], icp->conn_state,
1075 		    iscsi_ics_name[next_state], next_state);
1076 		icp->conn_prev_state = icp->conn_state;
1077 		icp->conn_state = next_state;
1078 		cv_broadcast(&icp->conn_state_change);
1079 		break;
1080 	default:
1081 		cmn_err(CE_WARN, "Update state found illegal state: %x "
1082 		    "prev_state: %x", next_state, icp->conn_prev_state);
1083 		ASSERT(0);
1084 	}
1085 }
1086