xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_conn.c (revision 12161:1b0453703429)
17836SJohn.Forte@Sun.COM /*
27836SJohn.Forte@Sun.COM  * CDDL HEADER START
37836SJohn.Forte@Sun.COM  *
47836SJohn.Forte@Sun.COM  * The contents of this file are subject to the terms of the
57836SJohn.Forte@Sun.COM  * Common Development and Distribution License (the "License").
67836SJohn.Forte@Sun.COM  * You may not use this file except in compliance with the License.
77836SJohn.Forte@Sun.COM  *
87836SJohn.Forte@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97836SJohn.Forte@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107836SJohn.Forte@Sun.COM  * See the License for the specific language governing permissions
117836SJohn.Forte@Sun.COM  * and limitations under the License.
127836SJohn.Forte@Sun.COM  *
137836SJohn.Forte@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147836SJohn.Forte@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157836SJohn.Forte@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167836SJohn.Forte@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177836SJohn.Forte@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187836SJohn.Forte@Sun.COM  *
197836SJohn.Forte@Sun.COM  * CDDL HEADER END
207836SJohn.Forte@Sun.COM  */
217836SJohn.Forte@Sun.COM /*
22*12161SJack.Meng@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
237836SJohn.Forte@Sun.COM  *
247836SJohn.Forte@Sun.COM  * iSCSI connection interfaces
257836SJohn.Forte@Sun.COM  */
267836SJohn.Forte@Sun.COM 
279162SPeter.Dunlap@Sun.COM #define	ISCSI_ICS_NAMES
287836SJohn.Forte@Sun.COM #include "iscsi.h"
297836SJohn.Forte@Sun.COM #include "persistent.h"
308194SJack.Meng@Sun.COM #include <sys/bootprops.h>
318194SJack.Meng@Sun.COM 
328194SJack.Meng@Sun.COM extern ib_boot_prop_t   *iscsiboot_prop;
337836SJohn.Forte@Sun.COM 
349162SPeter.Dunlap@Sun.COM static void iscsi_client_notify_task(void *cn_task_void);
359162SPeter.Dunlap@Sun.COM 
367836SJohn.Forte@Sun.COM static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
377836SJohn.Forte@Sun.COM 
387836SJohn.Forte@Sun.COM #define	SHUTDOWN_TIMEOUT	180 /* seconds */
397836SJohn.Forte@Sun.COM 
408194SJack.Meng@Sun.COM extern int modrootloaded;
419162SPeter.Dunlap@Sun.COM 
429162SPeter.Dunlap@Sun.COM boolean_t iscsi_conn_logging = B_FALSE;
439162SPeter.Dunlap@Sun.COM 
4412001SZhang.Yi@Sun.COM #define	ISCSI_LOGIN_TPGT_NEGO_ERROR(icp) \
4512001SZhang.Yi@Sun.COM 	(((icp)->conn_login_state == LOGIN_ERROR) && \
4612001SZhang.Yi@Sun.COM 	((icp)->conn_login_status == ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL))
4712001SZhang.Yi@Sun.COM 
487836SJohn.Forte@Sun.COM /*
497836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
507836SJohn.Forte@Sun.COM  * | External Connection Interfaces					|
517836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
527836SJohn.Forte@Sun.COM  */
537836SJohn.Forte@Sun.COM 
547836SJohn.Forte@Sun.COM /*
557836SJohn.Forte@Sun.COM  * iscsi_conn_create - This creates an iscsi connection structure and
567836SJohn.Forte@Sun.COM  * associates it with a session structure.  The session's sess_conn_list_rwlock
577836SJohn.Forte@Sun.COM  * should be held as a writer before calling this function.
587836SJohn.Forte@Sun.COM  */
597836SJohn.Forte@Sun.COM iscsi_status_t
iscsi_conn_create(struct sockaddr * addr,iscsi_sess_t * isp,iscsi_conn_t ** icpp)607836SJohn.Forte@Sun.COM iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp)
617836SJohn.Forte@Sun.COM {
627836SJohn.Forte@Sun.COM 	iscsi_conn_t	*icp	= NULL;
637836SJohn.Forte@Sun.COM 	char		th_name[ISCSI_TH_MAX_NAME_LEN];
647836SJohn.Forte@Sun.COM 
657836SJohn.Forte@Sun.COM 	/* See if this connection already exists */
667836SJohn.Forte@Sun.COM 	for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) {
677836SJohn.Forte@Sun.COM 
687836SJohn.Forte@Sun.COM 		/*
697836SJohn.Forte@Sun.COM 		 * Compare the ioctl information to see if
707836SJohn.Forte@Sun.COM 		 * its a match for this connection.  (This
717836SJohn.Forte@Sun.COM 		 * is done by making sure the IPs are of
727836SJohn.Forte@Sun.COM 		 * the same size and then they are the
737836SJohn.Forte@Sun.COM 		 * same value.
747836SJohn.Forte@Sun.COM 		 */
757836SJohn.Forte@Sun.COM 		if (bcmp(&icp->conn_base_addr, addr,
767836SJohn.Forte@Sun.COM 		    SIZEOF_SOCKADDR(addr)) == 0) {
777836SJohn.Forte@Sun.COM 			/* It's a match, record this connection */
787836SJohn.Forte@Sun.COM 			break;
797836SJohn.Forte@Sun.COM 		}
807836SJohn.Forte@Sun.COM 	}
817836SJohn.Forte@Sun.COM 
827836SJohn.Forte@Sun.COM 	/* If icp is found return it */
837836SJohn.Forte@Sun.COM 	if (icp != NULL) {
847836SJohn.Forte@Sun.COM 		*icpp = icp;
857836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SUCCESS);
867836SJohn.Forte@Sun.COM 	}
877836SJohn.Forte@Sun.COM 
887836SJohn.Forte@Sun.COM 	/* We are creating the connection, allocate, and setup */
897836SJohn.Forte@Sun.COM 	icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP);
907836SJohn.Forte@Sun.COM 
917836SJohn.Forte@Sun.COM 	/*
927836SJohn.Forte@Sun.COM 	 * Setup connection
937836SJohn.Forte@Sun.COM 	 */
947836SJohn.Forte@Sun.COM 	icp->conn_sig			= ISCSI_SIG_CONN;
957836SJohn.Forte@Sun.COM 	icp->conn_state			= ISCSI_CONN_STATE_FREE;
967836SJohn.Forte@Sun.COM 	mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
977836SJohn.Forte@Sun.COM 	cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
989162SPeter.Dunlap@Sun.COM 	mutex_init(&icp->conn_login_mutex, NULL, MUTEX_DRIVER, NULL);
999162SPeter.Dunlap@Sun.COM 	cv_init(&icp->conn_login_cv, NULL, CV_DRIVER, NULL);
1007836SJohn.Forte@Sun.COM 	icp->conn_state_destroy		= B_FALSE;
1019162SPeter.Dunlap@Sun.COM 	idm_sm_audit_init(&icp->conn_state_audit);
1027836SJohn.Forte@Sun.COM 	icp->conn_sess			= isp;
1037836SJohn.Forte@Sun.COM 
1047836SJohn.Forte@Sun.COM 	mutex_enter(&iscsi_oid_mutex);
1057836SJohn.Forte@Sun.COM 	icp->conn_oid = iscsi_oid++;
1067836SJohn.Forte@Sun.COM 	mutex_exit(&iscsi_oid_mutex);
1077836SJohn.Forte@Sun.COM 
1089162SPeter.Dunlap@Sun.COM 	/*
1099162SPeter.Dunlap@Sun.COM 	 * IDM CN taskq
1109162SPeter.Dunlap@Sun.COM 	 */
1119162SPeter.Dunlap@Sun.COM 
1129162SPeter.Dunlap@Sun.COM 	if (snprintf(th_name, sizeof (th_name) - 1,
1139162SPeter.Dunlap@Sun.COM 	    ISCSI_CONN_CN_TASKQ_NAME_FORMAT,
1147836SJohn.Forte@Sun.COM 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
1157836SJohn.Forte@Sun.COM 	    icp->conn_oid) >= sizeof (th_name)) {
1167836SJohn.Forte@Sun.COM 		cv_destroy(&icp->conn_state_change);
1177836SJohn.Forte@Sun.COM 		mutex_destroy(&icp->conn_state_mutex);
1187836SJohn.Forte@Sun.COM 		kmem_free(icp, sizeof (iscsi_conn_t));
1197836SJohn.Forte@Sun.COM 		*icpp = NULL;
1207836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
1217836SJohn.Forte@Sun.COM 	}
1227836SJohn.Forte@Sun.COM 
1239162SPeter.Dunlap@Sun.COM 	icp->conn_cn_taskq =
1249162SPeter.Dunlap@Sun.COM 	    ddi_taskq_create(icp->conn_sess->sess_hba->hba_dip, th_name, 1,
1259162SPeter.Dunlap@Sun.COM 	    TASKQ_DEFAULTPRI, 0);
1269162SPeter.Dunlap@Sun.COM 	if (icp->conn_cn_taskq == NULL) {
1279162SPeter.Dunlap@Sun.COM 		cv_destroy(&icp->conn_state_change);
1289162SPeter.Dunlap@Sun.COM 		mutex_destroy(&icp->conn_state_mutex);
1299162SPeter.Dunlap@Sun.COM 		kmem_free(icp, sizeof (iscsi_conn_t));
1309162SPeter.Dunlap@Sun.COM 		*icpp = NULL;
1319162SPeter.Dunlap@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
1329162SPeter.Dunlap@Sun.COM 	}
1337836SJohn.Forte@Sun.COM 
1347836SJohn.Forte@Sun.COM 	/* Creation of the transfer thread */
1357836SJohn.Forte@Sun.COM 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
1367836SJohn.Forte@Sun.COM 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
1377836SJohn.Forte@Sun.COM 	    icp->conn_oid) >= sizeof (th_name)) {
1387836SJohn.Forte@Sun.COM 		cv_destroy(&icp->conn_state_change);
1397836SJohn.Forte@Sun.COM 		mutex_destroy(&icp->conn_state_mutex);
1407836SJohn.Forte@Sun.COM 		kmem_free(icp, sizeof (iscsi_conn_t));
1419162SPeter.Dunlap@Sun.COM 		ddi_taskq_destroy(icp->conn_cn_taskq);
1427836SJohn.Forte@Sun.COM 		*icpp = NULL;
1437836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
1447836SJohn.Forte@Sun.COM 	}
1457836SJohn.Forte@Sun.COM 
1467836SJohn.Forte@Sun.COM 	icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
1477836SJohn.Forte@Sun.COM 	    th_name, iscsi_tx_thread, icp);
1487836SJohn.Forte@Sun.COM 
1497836SJohn.Forte@Sun.COM 	/* setup connection queues */
1507836SJohn.Forte@Sun.COM 	iscsi_init_queue(&icp->conn_queue_active);
1519162SPeter.Dunlap@Sun.COM 	iscsi_init_queue(&icp->conn_queue_idm_aborting);
1527836SJohn.Forte@Sun.COM 
1537836SJohn.Forte@Sun.COM 	bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
1547836SJohn.Forte@Sun.COM 
1557836SJohn.Forte@Sun.COM 	/* Add new connection to the session connection list */
1567836SJohn.Forte@Sun.COM 	icp->conn_cid = isp->sess_conn_next_cid++;
1577836SJohn.Forte@Sun.COM 	if (isp->sess_conn_list == NULL) {
1587836SJohn.Forte@Sun.COM 		isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp;
1597836SJohn.Forte@Sun.COM 	} else {
1607836SJohn.Forte@Sun.COM 		isp->sess_conn_list_last_ptr->conn_next = icp;
1617836SJohn.Forte@Sun.COM 		isp->sess_conn_list_last_ptr = icp;
1627836SJohn.Forte@Sun.COM 	}
1637836SJohn.Forte@Sun.COM 
1647836SJohn.Forte@Sun.COM 	KSTAT_INC_SESS_CNTR_CONN(isp);
1657836SJohn.Forte@Sun.COM 	(void) iscsi_conn_kstat_init(icp);
1667836SJohn.Forte@Sun.COM 
1677836SJohn.Forte@Sun.COM 	*icpp = icp;
1687836SJohn.Forte@Sun.COM 
1697836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
1707836SJohn.Forte@Sun.COM }
1717836SJohn.Forte@Sun.COM 
1729162SPeter.Dunlap@Sun.COM /*
1739162SPeter.Dunlap@Sun.COM  * iscsi_conn_online - This attempts to take a connection from
1749162SPeter.Dunlap@Sun.COM  * ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN.
1759162SPeter.Dunlap@Sun.COM  */
1769162SPeter.Dunlap@Sun.COM iscsi_status_t
iscsi_conn_online(iscsi_conn_t * icp)1779162SPeter.Dunlap@Sun.COM iscsi_conn_online(iscsi_conn_t *icp)
1789162SPeter.Dunlap@Sun.COM {
1799162SPeter.Dunlap@Sun.COM 	iscsi_task_t	*itp;
1809162SPeter.Dunlap@Sun.COM 	iscsi_status_t	rval;
1819162SPeter.Dunlap@Sun.COM 
1829162SPeter.Dunlap@Sun.COM 	ASSERT(icp != NULL);
1839162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&icp->conn_state_mutex));
1849162SPeter.Dunlap@Sun.COM 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
1859162SPeter.Dunlap@Sun.COM 
1869162SPeter.Dunlap@Sun.COM 	/*
1879162SPeter.Dunlap@Sun.COM 	 * If we are attempting to connect then for the purposes of the
1889162SPeter.Dunlap@Sun.COM 	 * other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN.
1899162SPeter.Dunlap@Sun.COM 	 */
1909162SPeter.Dunlap@Sun.COM 	iscsi_conn_update_state_locked(icp, ISCSI_CONN_STATE_IN_LOGIN);
1919162SPeter.Dunlap@Sun.COM 	mutex_exit(&icp->conn_state_mutex);
1929162SPeter.Dunlap@Sun.COM 
1939162SPeter.Dunlap@Sun.COM 	/*
1949162SPeter.Dunlap@Sun.COM 	 * Sync base connection information before login
1959162SPeter.Dunlap@Sun.COM 	 * A login redirection might have shifted the
1969162SPeter.Dunlap@Sun.COM 	 * current information from the base.
1979162SPeter.Dunlap@Sun.COM 	 */
1989162SPeter.Dunlap@Sun.COM 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
1999162SPeter.Dunlap@Sun.COM 	    sizeof (icp->conn_curr_addr));
2009162SPeter.Dunlap@Sun.COM 
2019162SPeter.Dunlap@Sun.COM 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
2029162SPeter.Dunlap@Sun.COM 	ASSERT(itp != NULL);
2039162SPeter.Dunlap@Sun.COM 
2049162SPeter.Dunlap@Sun.COM 	itp->t_arg = icp;
2059162SPeter.Dunlap@Sun.COM 	itp->t_blocking = B_TRUE;
2069162SPeter.Dunlap@Sun.COM 	rval = iscsi_login_start(itp);
2079162SPeter.Dunlap@Sun.COM 	kmem_free(itp, sizeof (iscsi_task_t));
2089162SPeter.Dunlap@Sun.COM 
2099162SPeter.Dunlap@Sun.COM 	mutex_enter(&icp->conn_state_mutex);
2109162SPeter.Dunlap@Sun.COM 
2119162SPeter.Dunlap@Sun.COM 	return (rval);
2129162SPeter.Dunlap@Sun.COM }
2137836SJohn.Forte@Sun.COM 
2147836SJohn.Forte@Sun.COM /*
2157836SJohn.Forte@Sun.COM  * iscsi_conn_offline - This attempts to take a connection from
2167836SJohn.Forte@Sun.COM  * any state to ISCSI_CONN_STATE_FREE.
2177836SJohn.Forte@Sun.COM  */
2187836SJohn.Forte@Sun.COM iscsi_status_t
iscsi_conn_offline(iscsi_conn_t * icp)2197836SJohn.Forte@Sun.COM iscsi_conn_offline(iscsi_conn_t *icp)
2207836SJohn.Forte@Sun.COM {
2217836SJohn.Forte@Sun.COM 	clock_t		delay;
2227836SJohn.Forte@Sun.COM 
2237836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
2247836SJohn.Forte@Sun.COM 
2257836SJohn.Forte@Sun.COM 	/*
2267836SJohn.Forte@Sun.COM 	 * We can only destroy a connection if its either in
2277836SJohn.Forte@Sun.COM 	 * a state of FREE or LOGGED.  The other states are
2287836SJohn.Forte@Sun.COM 	 * transitionary and its unsafe to perform actions
2297836SJohn.Forte@Sun.COM 	 * on the connection in those states.  Set a flag
2307836SJohn.Forte@Sun.COM 	 * on the connection to influence the transitions
2317836SJohn.Forte@Sun.COM 	 * to quickly complete.  Then wait for a state
2327836SJohn.Forte@Sun.COM 	 * transition.
2339162SPeter.Dunlap@Sun.COM 	 *
2349162SPeter.Dunlap@Sun.COM 	 * ISCSI_CONN_STATE_LOGGED_IN is set immediately at the
2359162SPeter.Dunlap@Sun.COM 	 * start of CN_NOTIFY_FFP processing. icp->conn_state_ffp
2369162SPeter.Dunlap@Sun.COM 	 * is set to true at the end of ffp processing, at which
2379162SPeter.Dunlap@Sun.COM 	 * point any session updates are complete.  We don't
2389162SPeter.Dunlap@Sun.COM 	 * want to start offlining the connection before we're
2399162SPeter.Dunlap@Sun.COM 	 * done completing the FFP processing since this might
2409162SPeter.Dunlap@Sun.COM 	 * interrupt the discovery process.
2417836SJohn.Forte@Sun.COM 	 */
2427836SJohn.Forte@Sun.COM 	delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
2437836SJohn.Forte@Sun.COM 	mutex_enter(&icp->conn_state_mutex);
2447836SJohn.Forte@Sun.COM 	icp->conn_state_destroy = B_TRUE;
2459162SPeter.Dunlap@Sun.COM 	while ((((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
2469162SPeter.Dunlap@Sun.COM 	    (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN)) ||
2479162SPeter.Dunlap@Sun.COM 	    ((icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) &&
2489162SPeter.Dunlap@Sun.COM 	    !icp->conn_state_ffp)) &&
2497836SJohn.Forte@Sun.COM 	    (ddi_get_lbolt() < delay)) {
2507836SJohn.Forte@Sun.COM 		/* wait for transition */
2517836SJohn.Forte@Sun.COM 		(void) cv_timedwait(&icp->conn_state_change,
2527836SJohn.Forte@Sun.COM 		    &icp->conn_state_mutex, delay);
2537836SJohn.Forte@Sun.COM 	}
2547836SJohn.Forte@Sun.COM 
2557836SJohn.Forte@Sun.COM 	switch (icp->conn_state) {
2567836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_FREE:
2577836SJohn.Forte@Sun.COM 		break;
2587836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_LOGGED_IN:
25911078SPeter.Cudhea@Sun.COM 		if (icp->conn_state_ffp) {
26011078SPeter.Cudhea@Sun.COM 			/* Hold is released in iscsi_handle_logout */
26111078SPeter.Cudhea@Sun.COM 			idm_conn_hold(icp->conn_ic);
2629162SPeter.Dunlap@Sun.COM 			(void) iscsi_handle_logout(icp);
26311078SPeter.Cudhea@Sun.COM 		} else {
2649162SPeter.Dunlap@Sun.COM 			icp->conn_state_destroy = B_FALSE;
2659162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
2669162SPeter.Dunlap@Sun.COM 			return (ISCSI_STATUS_INTERNAL_ERROR);
2679162SPeter.Dunlap@Sun.COM 		}
2687836SJohn.Forte@Sun.COM 		break;
2697836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_IN_LOGIN:
2707836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_IN_LOGOUT:
2717836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_FAILED:
2727836SJohn.Forte@Sun.COM 	case ISCSI_CONN_STATE_POLLING:
2737836SJohn.Forte@Sun.COM 	default:
2747836SJohn.Forte@Sun.COM 		icp->conn_state_destroy = B_FALSE;
2757836SJohn.Forte@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
2767836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
2777836SJohn.Forte@Sun.COM 	}
2787836SJohn.Forte@Sun.COM 	mutex_exit(&icp->conn_state_mutex);
2797836SJohn.Forte@Sun.COM 
2807836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
2817836SJohn.Forte@Sun.COM }
2827836SJohn.Forte@Sun.COM 
2837836SJohn.Forte@Sun.COM /*
2847836SJohn.Forte@Sun.COM  * iscsi_conn_destroy - This destroys an iscsi connection structure
2857836SJohn.Forte@Sun.COM  * and de-associates it with the session.  The connection should
2867836SJohn.Forte@Sun.COM  * already been in the ISCSI_CONN_STATE_FREE when attempting this
2877836SJohn.Forte@Sun.COM  * operation.
2887836SJohn.Forte@Sun.COM  */
2897836SJohn.Forte@Sun.COM iscsi_status_t
iscsi_conn_destroy(iscsi_conn_t * icp)2907836SJohn.Forte@Sun.COM iscsi_conn_destroy(iscsi_conn_t *icp)
2917836SJohn.Forte@Sun.COM {
2927836SJohn.Forte@Sun.COM 	iscsi_sess_t	*isp;
2937836SJohn.Forte@Sun.COM 	iscsi_conn_t	*t_icp;
2947836SJohn.Forte@Sun.COM 
2957836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
2967836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
2977836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
2987836SJohn.Forte@Sun.COM 
2997836SJohn.Forte@Sun.COM 	if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
3007836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_INTERNAL_ERROR);
3017836SJohn.Forte@Sun.COM 	}
3027836SJohn.Forte@Sun.COM 
3037836SJohn.Forte@Sun.COM 	/* Destroy transfer thread */
3047836SJohn.Forte@Sun.COM 	iscsi_thread_destroy(icp->conn_tx_thread);
3059162SPeter.Dunlap@Sun.COM 	ddi_taskq_destroy(icp->conn_cn_taskq);
3067836SJohn.Forte@Sun.COM 
3077836SJohn.Forte@Sun.COM 	/* Terminate connection queues */
3089162SPeter.Dunlap@Sun.COM 	iscsi_destroy_queue(&icp->conn_queue_idm_aborting);
3097836SJohn.Forte@Sun.COM 	iscsi_destroy_queue(&icp->conn_queue_active);
3107836SJohn.Forte@Sun.COM 
3119162SPeter.Dunlap@Sun.COM 	cv_destroy(&icp->conn_login_cv);
3129162SPeter.Dunlap@Sun.COM 	mutex_destroy(&icp->conn_login_mutex);
3137836SJohn.Forte@Sun.COM 	cv_destroy(&icp->conn_state_change);
3147836SJohn.Forte@Sun.COM 	mutex_destroy(&icp->conn_state_mutex);
3157836SJohn.Forte@Sun.COM 
3167836SJohn.Forte@Sun.COM 	/*
3177836SJohn.Forte@Sun.COM 	 * Remove connection from sessions linked list.
3187836SJohn.Forte@Sun.COM 	 */
3197836SJohn.Forte@Sun.COM 	if (isp->sess_conn_list == icp) {
3207836SJohn.Forte@Sun.COM 		/* connection first item in list */
3217836SJohn.Forte@Sun.COM 		isp->sess_conn_list = icp->conn_next;
3227836SJohn.Forte@Sun.COM 		/*
3237836SJohn.Forte@Sun.COM 		 * check if this is also the last item in the list
3247836SJohn.Forte@Sun.COM 		 */
3257836SJohn.Forte@Sun.COM 		if (isp->sess_conn_list_last_ptr == icp) {
3267836SJohn.Forte@Sun.COM 			isp->sess_conn_list_last_ptr = NULL;
3277836SJohn.Forte@Sun.COM 		}
3287836SJohn.Forte@Sun.COM 	} else {
3297836SJohn.Forte@Sun.COM 		/*
3307836SJohn.Forte@Sun.COM 		 * search session list for icp pointing
3317836SJohn.Forte@Sun.COM 		 * to connection being removed.  Then
3327836SJohn.Forte@Sun.COM 		 * update that connections next pointer.
3337836SJohn.Forte@Sun.COM 		 */
3347836SJohn.Forte@Sun.COM 		t_icp = isp->sess_conn_list;
3357836SJohn.Forte@Sun.COM 		while (t_icp->conn_next != NULL) {
3367836SJohn.Forte@Sun.COM 			if (t_icp->conn_next == icp) {
3377836SJohn.Forte@Sun.COM 				break;
3387836SJohn.Forte@Sun.COM 			}
3397836SJohn.Forte@Sun.COM 			t_icp = t_icp->conn_next;
3407836SJohn.Forte@Sun.COM 		}
3417836SJohn.Forte@Sun.COM 		if (t_icp->conn_next == icp) {
3427836SJohn.Forte@Sun.COM 			t_icp->conn_next = icp->conn_next;
3437836SJohn.Forte@Sun.COM 			/*
3447836SJohn.Forte@Sun.COM 			 * if this is the last connection in the list
3457836SJohn.Forte@Sun.COM 			 * update the last_ptr to point to t_icp
3467836SJohn.Forte@Sun.COM 			 */
3477836SJohn.Forte@Sun.COM 			if (isp->sess_conn_list_last_ptr == icp) {
3487836SJohn.Forte@Sun.COM 				isp->sess_conn_list_last_ptr = t_icp;
3497836SJohn.Forte@Sun.COM 			}
3507836SJohn.Forte@Sun.COM 		} else {
3517836SJohn.Forte@Sun.COM 			/* couldn't find session */
3527836SJohn.Forte@Sun.COM 			ASSERT(FALSE);
3537836SJohn.Forte@Sun.COM 		}
3547836SJohn.Forte@Sun.COM 	}
3557836SJohn.Forte@Sun.COM 
3567836SJohn.Forte@Sun.COM 	/* Free this Connections Data */
3577836SJohn.Forte@Sun.COM 	iscsi_conn_kstat_term(icp);
3587836SJohn.Forte@Sun.COM 	kmem_free(icp, sizeof (iscsi_conn_t));
3597836SJohn.Forte@Sun.COM 
3607836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
3617836SJohn.Forte@Sun.COM }
3627836SJohn.Forte@Sun.COM 
3637836SJohn.Forte@Sun.COM 
3647836SJohn.Forte@Sun.COM /*
3657836SJohn.Forte@Sun.COM  * iscsi_conn_set_login_min_max - set min/max login window
3667836SJohn.Forte@Sun.COM  *
3677836SJohn.Forte@Sun.COM  * Used to set the min and max login window.  Input values
3687836SJohn.Forte@Sun.COM  * are in seconds.
3697836SJohn.Forte@Sun.COM  */
3707836SJohn.Forte@Sun.COM void
iscsi_conn_set_login_min_max(iscsi_conn_t * icp,int min,int max)3717836SJohn.Forte@Sun.COM iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max)
3727836SJohn.Forte@Sun.COM {
3737836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
3747836SJohn.Forte@Sun.COM 
3757836SJohn.Forte@Sun.COM 	icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
3767836SJohn.Forte@Sun.COM 	icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
3777836SJohn.Forte@Sun.COM }
3787836SJohn.Forte@Sun.COM 
3797836SJohn.Forte@Sun.COM 
3809162SPeter.Dunlap@Sun.COM /*
3819162SPeter.Dunlap@Sun.COM  * Process the idm notifications
3829162SPeter.Dunlap@Sun.COM  */
3839162SPeter.Dunlap@Sun.COM idm_status_t
iscsi_client_notify(idm_conn_t * ic,idm_client_notify_t icn,uintptr_t data)3849162SPeter.Dunlap@Sun.COM iscsi_client_notify(idm_conn_t *ic, idm_client_notify_t icn, uintptr_t data)
3859162SPeter.Dunlap@Sun.COM {
3869162SPeter.Dunlap@Sun.COM 	iscsi_cn_task_t		*cn;
3879162SPeter.Dunlap@Sun.COM 	iscsi_conn_t		*icp = ic->ic_handle;
3889162SPeter.Dunlap@Sun.COM 	iscsi_sess_t		*isp;
389*12161SJack.Meng@Sun.COM 	uint32_t		event_count;
3907836SJohn.Forte@Sun.COM 
3919162SPeter.Dunlap@Sun.COM 	/*
3929162SPeter.Dunlap@Sun.COM 	 * Don't access icp if the notification is CN_CONNECT_DESTROY
3939162SPeter.Dunlap@Sun.COM 	 * since icp may have already been freed.
3949162SPeter.Dunlap@Sun.COM 	 *
39510766SPeter.Cudhea@Sun.COM 	 * In particular, we cannot audit the CN_CONNECT_DESTROY event.
39610766SPeter.Cudhea@Sun.COM 	 *
39710766SPeter.Cudhea@Sun.COM 	 * Handle a few cases immediately, the rest in a task queue.
3989162SPeter.Dunlap@Sun.COM 	 */
3999162SPeter.Dunlap@Sun.COM 	switch (icn) {
4009162SPeter.Dunlap@Sun.COM 	case CN_CONNECT_FAIL:
4019162SPeter.Dunlap@Sun.COM 	case CN_LOGIN_FAIL:
4029162SPeter.Dunlap@Sun.COM 		/*
4039162SPeter.Dunlap@Sun.COM 		 * Wakeup any thread waiting for login stuff to happen.
4049162SPeter.Dunlap@Sun.COM 		 */
4059162SPeter.Dunlap@Sun.COM 		ASSERT(icp != NULL);
40610766SPeter.Cudhea@Sun.COM 
40710766SPeter.Cudhea@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
40810766SPeter.Cudhea@Sun.COM 		idm_sm_audit_event(&icp->conn_state_audit,
40910766SPeter.Cudhea@Sun.COM 		    SAS_ISCSI_CONN, icp->conn_state, icn, data);
41010766SPeter.Cudhea@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
4119162SPeter.Dunlap@Sun.COM 		iscsi_login_update_state(icp, LOGIN_ERROR);
4129162SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
41310766SPeter.Cudhea@Sun.COM 
4149162SPeter.Dunlap@Sun.COM 	case CN_READY_FOR_LOGIN:
4159162SPeter.Dunlap@Sun.COM 		idm_conn_hold(ic); /* Released in CN_CONNECT_LOST */
41610766SPeter.Cudhea@Sun.COM 		ASSERT(icp != NULL);
41710766SPeter.Cudhea@Sun.COM 
4189162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
41910766SPeter.Cudhea@Sun.COM 		idm_sm_audit_event(&icp->conn_state_audit,
42010766SPeter.Cudhea@Sun.COM 		    SAS_ISCSI_CONN, icp->conn_state, icn, data);
4219162SPeter.Dunlap@Sun.COM 		icp->conn_state_idm_connected = B_TRUE;
4229162SPeter.Dunlap@Sun.COM 		cv_broadcast(&icp->conn_state_change);
4239162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
4249162SPeter.Dunlap@Sun.COM 
4259162SPeter.Dunlap@Sun.COM 		iscsi_login_update_state(icp, LOGIN_READY);
4269162SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
42710766SPeter.Cudhea@Sun.COM 
4289162SPeter.Dunlap@Sun.COM 	case CN_CONNECT_DESTROY:
4299162SPeter.Dunlap@Sun.COM 		/*
4309162SPeter.Dunlap@Sun.COM 		 * We released any dependecies we had on this object in
4319162SPeter.Dunlap@Sun.COM 		 * either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need
4329162SPeter.Dunlap@Sun.COM 		 * to destroy the IDM connection now.
4339162SPeter.Dunlap@Sun.COM 		 */
4349162SPeter.Dunlap@Sun.COM 		idm_ini_conn_destroy(ic);
4359162SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
4369162SPeter.Dunlap@Sun.COM 	}
4377836SJohn.Forte@Sun.COM 
4387836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
43910766SPeter.Cudhea@Sun.COM 	mutex_enter(&icp->conn_state_mutex);
44010766SPeter.Cudhea@Sun.COM 	idm_sm_audit_event(&icp->conn_state_audit,
44110766SPeter.Cudhea@Sun.COM 	    SAS_ISCSI_CONN, icp->conn_state, icn, data);
44210766SPeter.Cudhea@Sun.COM 	mutex_exit(&icp->conn_state_mutex);
4439162SPeter.Dunlap@Sun.COM 	isp = icp->conn_sess;
4449162SPeter.Dunlap@Sun.COM 
4459162SPeter.Dunlap@Sun.COM 	/*
4469162SPeter.Dunlap@Sun.COM 	 * Dispatch notifications to the taskq since they often require
4479162SPeter.Dunlap@Sun.COM 	 * long blocking operations.  In the case of CN_CONNECT_DESTROY
4489162SPeter.Dunlap@Sun.COM 	 * we actually just want to destroy the connection which we
4499162SPeter.Dunlap@Sun.COM 	 * can't do in the IDM taskq context.
4509162SPeter.Dunlap@Sun.COM 	 */
4519162SPeter.Dunlap@Sun.COM 	cn = kmem_alloc(sizeof (*cn), KM_SLEEP);
4529162SPeter.Dunlap@Sun.COM 
4539162SPeter.Dunlap@Sun.COM 	cn->ct_ic = ic;
4549162SPeter.Dunlap@Sun.COM 	cn->ct_icn = icn;
4559162SPeter.Dunlap@Sun.COM 	cn->ct_data = data;
4567836SJohn.Forte@Sun.COM 
4579162SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
4589162SPeter.Dunlap@Sun.COM 
4599162SPeter.Dunlap@Sun.COM 	if (ddi_taskq_dispatch(icp->conn_cn_taskq,
4609162SPeter.Dunlap@Sun.COM 	    iscsi_client_notify_task, cn, DDI_SLEEP) != DDI_SUCCESS) {
4619162SPeter.Dunlap@Sun.COM 		idm_conn_rele(ic);
4629162SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
4639162SPeter.Dunlap@Sun.COM 		    "unable to schedule notify task", icp->conn_oid);
4649162SPeter.Dunlap@Sun.COM 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
465*12161SJack.Meng@Sun.COM 		event_count = atomic_inc_32_nv(&isp->sess_state_event_count);
466*12161SJack.Meng@Sun.COM 		iscsi_sess_enter_state_zone(isp);
467*12161SJack.Meng@Sun.COM 
4689162SPeter.Dunlap@Sun.COM 		iscsi_sess_state_machine(isp,
469*12161SJack.Meng@Sun.COM 		    ISCSI_SESS_EVENT_N6, event_count);
470*12161SJack.Meng@Sun.COM 
471*12161SJack.Meng@Sun.COM 		iscsi_sess_exit_state_zone(isp);
4729162SPeter.Dunlap@Sun.COM 	}
4739162SPeter.Dunlap@Sun.COM 
4749162SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
4759162SPeter.Dunlap@Sun.COM }
4767836SJohn.Forte@Sun.COM 
4779162SPeter.Dunlap@Sun.COM static void
iscsi_client_notify_task(void * cn_task_void)4789162SPeter.Dunlap@Sun.COM iscsi_client_notify_task(void *cn_task_void)
4799162SPeter.Dunlap@Sun.COM {
4809162SPeter.Dunlap@Sun.COM 	iscsi_cn_task_t		*cn_task = cn_task_void;
4819162SPeter.Dunlap@Sun.COM 	iscsi_conn_t		*icp;
4829162SPeter.Dunlap@Sun.COM 	iscsi_sess_t		*isp;
4839162SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
4849162SPeter.Dunlap@Sun.COM 	idm_client_notify_t	icn;
4859162SPeter.Dunlap@Sun.COM 	uintptr_t		data;
4869162SPeter.Dunlap@Sun.COM 	idm_ffp_disable_t	disable_type;
4879162SPeter.Dunlap@Sun.COM 	boolean_t		in_login;
488*12161SJack.Meng@Sun.COM 	uint32_t		event_count;
4899162SPeter.Dunlap@Sun.COM 
4909162SPeter.Dunlap@Sun.COM 	ic = cn_task->ct_ic;
4919162SPeter.Dunlap@Sun.COM 	icn = cn_task->ct_icn;
4929162SPeter.Dunlap@Sun.COM 	data = cn_task->ct_data;
4939162SPeter.Dunlap@Sun.COM 
4949162SPeter.Dunlap@Sun.COM 	icp = ic->ic_handle;
4959162SPeter.Dunlap@Sun.COM 	ASSERT(icp != NULL);
4969162SPeter.Dunlap@Sun.COM 	isp = icp->conn_sess;
4977836SJohn.Forte@Sun.COM 
4989162SPeter.Dunlap@Sun.COM 	switch (icn) {
4999162SPeter.Dunlap@Sun.COM 	case CN_FFP_ENABLED:
5009162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
5019162SPeter.Dunlap@Sun.COM 		icp->conn_async_logout = B_FALSE;
5029162SPeter.Dunlap@Sun.COM 		icp->conn_state_ffp = B_TRUE;
5039162SPeter.Dunlap@Sun.COM 		cv_broadcast(&icp->conn_state_change);
5049162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
5059162SPeter.Dunlap@Sun.COM 
5069162SPeter.Dunlap@Sun.COM 		/*
5079162SPeter.Dunlap@Sun.COM 		 * This logic assumes that the IDM login-snooping code
50810317SZhang.Yi@Sun.COM 		 * and the initiator login code will agree to go when
50910317SZhang.Yi@Sun.COM 		 * the connection is in FFP or final error received.
51010317SZhang.Yi@Sun.COM 		 * The reason we do this is that we don't want to process
51110317SZhang.Yi@Sun.COM 		 * CN_FFP_DISABLED until CN_FFP_ENABLED has been full handled.
5129162SPeter.Dunlap@Sun.COM 		 */
5139162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_login_mutex);
51410317SZhang.Yi@Sun.COM 		while ((icp->conn_login_state != LOGIN_FFP) &&
51510317SZhang.Yi@Sun.COM 		    (icp->conn_login_state != LOGIN_ERROR)) {
5169162SPeter.Dunlap@Sun.COM 			cv_wait(&icp->conn_login_cv, &icp->conn_login_mutex);
5179162SPeter.Dunlap@Sun.COM 		}
5189162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_login_mutex);
5197836SJohn.Forte@Sun.COM 		break;
5209162SPeter.Dunlap@Sun.COM 	case CN_FFP_DISABLED:
5219162SPeter.Dunlap@Sun.COM 		disable_type = (idm_ffp_disable_t)data;
5229162SPeter.Dunlap@Sun.COM 
5239162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
5249162SPeter.Dunlap@Sun.COM 		switch (disable_type) {
5259162SPeter.Dunlap@Sun.COM 		case FD_SESS_LOGOUT:
5269162SPeter.Dunlap@Sun.COM 		case FD_CONN_LOGOUT:
5279162SPeter.Dunlap@Sun.COM 			if (icp->conn_async_logout) {
5289162SPeter.Dunlap@Sun.COM 				/*
5299162SPeter.Dunlap@Sun.COM 				 * Our logout was in response to an
5309162SPeter.Dunlap@Sun.COM 				 * async logout request so treat this
5319162SPeter.Dunlap@Sun.COM 				 * like a connection failure (we will
5329162SPeter.Dunlap@Sun.COM 				 * try to re-establish the connection)
5339162SPeter.Dunlap@Sun.COM 				 */
5349162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
5359162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_FAILED);
5369162SPeter.Dunlap@Sun.COM 			} else {
5379162SPeter.Dunlap@Sun.COM 				/*
5389162SPeter.Dunlap@Sun.COM 				 * Logout due to to user config change,
5399162SPeter.Dunlap@Sun.COM 				 * we will not try to re-establish
5409162SPeter.Dunlap@Sun.COM 				 * the connection.
5419162SPeter.Dunlap@Sun.COM 				 */
5429162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
5439162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_IN_LOGOUT);
5449162SPeter.Dunlap@Sun.COM 				/*
5459162SPeter.Dunlap@Sun.COM 				 * Hold off generating the ISCSI_SESS_EVENT_N3
5469162SPeter.Dunlap@Sun.COM 				 * event until we get the CN_CONNECT_LOST
5479162SPeter.Dunlap@Sun.COM 				 * notification.  This matches the pre-IDM
5489162SPeter.Dunlap@Sun.COM 				 * implementation better.
5499162SPeter.Dunlap@Sun.COM 				 */
5509162SPeter.Dunlap@Sun.COM 			}
5519162SPeter.Dunlap@Sun.COM 			break;
5529162SPeter.Dunlap@Sun.COM 
5539162SPeter.Dunlap@Sun.COM 		case FD_CONN_FAIL:
5549162SPeter.Dunlap@Sun.COM 		default:
55510317SZhang.Yi@Sun.COM 			if (icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) {
55610317SZhang.Yi@Sun.COM 				iscsi_conn_update_state_locked(icp,
55710317SZhang.Yi@Sun.COM 				    ISCSI_CONN_STATE_FREE);
55810317SZhang.Yi@Sun.COM 			} else {
55910317SZhang.Yi@Sun.COM 				iscsi_conn_update_state_locked(icp,
56010317SZhang.Yi@Sun.COM 				    ISCSI_CONN_STATE_FAILED);
56110317SZhang.Yi@Sun.COM 			}
5629162SPeter.Dunlap@Sun.COM 			break;
5639162SPeter.Dunlap@Sun.COM 		}
5649162SPeter.Dunlap@Sun.COM 
5659162SPeter.Dunlap@Sun.COM 		icp->conn_state_ffp = B_FALSE;
5669162SPeter.Dunlap@Sun.COM 		cv_broadcast(&icp->conn_state_change);
5679162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
5689162SPeter.Dunlap@Sun.COM 
5697836SJohn.Forte@Sun.COM 		break;
5709162SPeter.Dunlap@Sun.COM 	case CN_CONNECT_LOST:
5719162SPeter.Dunlap@Sun.COM 		/*
5729162SPeter.Dunlap@Sun.COM 		 * We only care about CN_CONNECT_LOST if we've logged in.  IDM
5739162SPeter.Dunlap@Sun.COM 		 * sends a flag as the data payload to indicate whether we
5749162SPeter.Dunlap@Sun.COM 		 * were trying to login.  The CN_LOGIN_FAIL notification
5759162SPeter.Dunlap@Sun.COM 		 * gives us what we need to know for login failures and
5769162SPeter.Dunlap@Sun.COM 		 * otherwise we will need to keep a bunch of state to know
5779162SPeter.Dunlap@Sun.COM 		 * what CN_CONNECT_LOST means to us.
5789162SPeter.Dunlap@Sun.COM 		 */
5799162SPeter.Dunlap@Sun.COM 		in_login = (boolean_t)data;
58010317SZhang.Yi@Sun.COM 		if (in_login ||
58110317SZhang.Yi@Sun.COM 		    (icp->conn_prev_state == ISCSI_CONN_STATE_IN_LOGIN)) {
5829162SPeter.Dunlap@Sun.COM 			mutex_enter(&icp->conn_state_mutex);
5839162SPeter.Dunlap@Sun.COM 
5849162SPeter.Dunlap@Sun.COM 			icp->conn_state_idm_connected = B_FALSE;
5859162SPeter.Dunlap@Sun.COM 			cv_broadcast(&icp->conn_state_change);
5869162SPeter.Dunlap@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
5879162SPeter.Dunlap@Sun.COM 
5889162SPeter.Dunlap@Sun.COM 			/* Release connect hold from CN_READY_FOR_LOGIN */
5899162SPeter.Dunlap@Sun.COM 			idm_conn_rele(ic);
5909162SPeter.Dunlap@Sun.COM 			break;
5919162SPeter.Dunlap@Sun.COM 		}
5929162SPeter.Dunlap@Sun.COM 
5939162SPeter.Dunlap@Sun.COM 		/* Any remaining commands are never going to finish */
5949162SPeter.Dunlap@Sun.COM 		iscsi_conn_flush_active_cmds(icp);
5959162SPeter.Dunlap@Sun.COM 
5969162SPeter.Dunlap@Sun.COM 		/*
5979162SPeter.Dunlap@Sun.COM 		 * The connection is no longer active so cleanup any
5989162SPeter.Dunlap@Sun.COM 		 * references to the connection and release any holds so
5999162SPeter.Dunlap@Sun.COM 		 * that IDM can finish cleanup.
6009162SPeter.Dunlap@Sun.COM 		 */
6019162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_state_mutex);
6029162SPeter.Dunlap@Sun.COM 		if (icp->conn_state != ISCSI_CONN_STATE_FAILED) {
603*12161SJack.Meng@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
604*12161SJack.Meng@Sun.COM 			event_count = atomic_inc_32_nv(
605*12161SJack.Meng@Sun.COM 			    &isp->sess_state_event_count);
606*12161SJack.Meng@Sun.COM 			iscsi_sess_enter_state_zone(isp);
6079162SPeter.Dunlap@Sun.COM 
608*12161SJack.Meng@Sun.COM 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N3,
609*12161SJack.Meng@Sun.COM 			    event_count);
6109162SPeter.Dunlap@Sun.COM 
611*12161SJack.Meng@Sun.COM 			iscsi_sess_exit_state_zone(isp);
612*12161SJack.Meng@Sun.COM 
613*12161SJack.Meng@Sun.COM 			mutex_enter(&icp->conn_state_mutex);
6149162SPeter.Dunlap@Sun.COM 			iscsi_conn_update_state_locked(icp,
6159162SPeter.Dunlap@Sun.COM 			    ISCSI_CONN_STATE_FREE);
6169162SPeter.Dunlap@Sun.COM 		} else {
617*12161SJack.Meng@Sun.COM 			mutex_exit(&icp->conn_state_mutex);
618*12161SJack.Meng@Sun.COM 			event_count = atomic_inc_32_nv(
619*12161SJack.Meng@Sun.COM 			    &isp->sess_state_event_count);
620*12161SJack.Meng@Sun.COM 			iscsi_sess_enter_state_zone(isp);
6219162SPeter.Dunlap@Sun.COM 
6229162SPeter.Dunlap@Sun.COM 			iscsi_sess_state_machine(isp,
623*12161SJack.Meng@Sun.COM 			    ISCSI_SESS_EVENT_N5, event_count);
624*12161SJack.Meng@Sun.COM 
625*12161SJack.Meng@Sun.COM 			iscsi_sess_exit_state_zone(isp);
6269162SPeter.Dunlap@Sun.COM 
6279162SPeter.Dunlap@Sun.COM 			/*
6289162SPeter.Dunlap@Sun.COM 			 * If session type is NORMAL, try to reestablish the
6299162SPeter.Dunlap@Sun.COM 			 * connection.
6309162SPeter.Dunlap@Sun.COM 			 */
63112001SZhang.Yi@Sun.COM 			if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) &&
63212001SZhang.Yi@Sun.COM 			    !(ISCSI_LOGIN_TPGT_NEGO_ERROR(icp))) {
6339162SPeter.Dunlap@Sun.COM 				iscsi_conn_retry(isp, icp);
634*12161SJack.Meng@Sun.COM 				mutex_enter(&icp->conn_state_mutex);
6359162SPeter.Dunlap@Sun.COM 			} else {
636*12161SJack.Meng@Sun.COM 				event_count = atomic_inc_32_nv(
637*12161SJack.Meng@Sun.COM 				    &isp->sess_state_event_count);
638*12161SJack.Meng@Sun.COM 				iscsi_sess_enter_state_zone(isp);
6399162SPeter.Dunlap@Sun.COM 
6409162SPeter.Dunlap@Sun.COM 				iscsi_sess_state_machine(isp,
641*12161SJack.Meng@Sun.COM 				    ISCSI_SESS_EVENT_N6, event_count);
6429162SPeter.Dunlap@Sun.COM 
643*12161SJack.Meng@Sun.COM 				iscsi_sess_exit_state_zone(isp);
644*12161SJack.Meng@Sun.COM 
645*12161SJack.Meng@Sun.COM 				mutex_enter(&icp->conn_state_mutex);
6469162SPeter.Dunlap@Sun.COM 				iscsi_conn_update_state_locked(icp,
6479162SPeter.Dunlap@Sun.COM 				    ISCSI_CONN_STATE_FREE);
6489162SPeter.Dunlap@Sun.COM 			}
6499162SPeter.Dunlap@Sun.COM 		}
6509162SPeter.Dunlap@Sun.COM 
6519162SPeter.Dunlap@Sun.COM 		(void) iscsi_thread_stop(icp->conn_tx_thread);
6529162SPeter.Dunlap@Sun.COM 
6539162SPeter.Dunlap@Sun.COM 		icp->conn_state_idm_connected = B_FALSE;
6549162SPeter.Dunlap@Sun.COM 		cv_broadcast(&icp->conn_state_change);
6559162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_state_mutex);
6569162SPeter.Dunlap@Sun.COM 
6579162SPeter.Dunlap@Sun.COM 		/* Release connect hold from CN_READY_FOR_LOGIN */
6589162SPeter.Dunlap@Sun.COM 		idm_conn_rele(ic);
6597836SJohn.Forte@Sun.COM 		break;
6607836SJohn.Forte@Sun.COM 	default:
6619162SPeter.Dunlap@Sun.COM 		ISCSI_CONN_LOG(CE_WARN,
6629162SPeter.Dunlap@Sun.COM 		    "iscsi_client_notify: unknown notification: "
6639162SPeter.Dunlap@Sun.COM 		    "%x: NOT IMPLEMENTED YET: icp: %p ic: %p ",
6649162SPeter.Dunlap@Sun.COM 		    icn, (void *)icp, (void *)ic);
6659162SPeter.Dunlap@Sun.COM 		break;
6667836SJohn.Forte@Sun.COM 	}
6679162SPeter.Dunlap@Sun.COM 	/* free the task notify structure we allocated in iscsi_client_notify */
6689162SPeter.Dunlap@Sun.COM 	kmem_free(cn_task, sizeof (*cn_task));
6697836SJohn.Forte@Sun.COM 
6709162SPeter.Dunlap@Sun.COM 	/* Release the hold we acquired in iscsi_client_notify */
6719162SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
6727836SJohn.Forte@Sun.COM }
6737836SJohn.Forte@Sun.COM 
6747836SJohn.Forte@Sun.COM /*
6757836SJohn.Forte@Sun.COM  * iscsi_conn_sync_params - used to update connection parameters
6767836SJohn.Forte@Sun.COM  *
6777836SJohn.Forte@Sun.COM  * Used to update connection parameters with current configured
6787836SJohn.Forte@Sun.COM  * parameters in the persistent store.  This should be called
6797836SJohn.Forte@Sun.COM  * before starting to make a new iscsi connection in iscsi_login.
6807836SJohn.Forte@Sun.COM  */
6817836SJohn.Forte@Sun.COM iscsi_status_t
iscsi_conn_sync_params(iscsi_conn_t * icp)6827836SJohn.Forte@Sun.COM iscsi_conn_sync_params(iscsi_conn_t *icp)
6837836SJohn.Forte@Sun.COM {
6847836SJohn.Forte@Sun.COM 	iscsi_sess_t		*isp;
6857836SJohn.Forte@Sun.COM 	iscsi_hba_t		*ihp;
6867836SJohn.Forte@Sun.COM 	int			param_id;
6877836SJohn.Forte@Sun.COM 	persistent_param_t	pp;
68810156SZhang.Yi@Sun.COM 	persistent_tunable_param_t	ptp;
6897836SJohn.Forte@Sun.COM 	iscsi_config_sess_t	*ics;
6907836SJohn.Forte@Sun.COM 	int			idx, size;
6917836SJohn.Forte@Sun.COM 	char			*name;
6927836SJohn.Forte@Sun.COM 
6937836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
6947836SJohn.Forte@Sun.COM 	ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) ||
6957836SJohn.Forte@Sun.COM 	    (icp->conn_state == ISCSI_CONN_STATE_FAILED) ||
6967836SJohn.Forte@Sun.COM 	    (icp->conn_state == ISCSI_CONN_STATE_POLLING));
6977836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
6987836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
6997836SJohn.Forte@Sun.COM 	ihp = isp->sess_hba;
7007836SJohn.Forte@Sun.COM 	ASSERT(ihp != NULL);
7017836SJohn.Forte@Sun.COM 
7027836SJohn.Forte@Sun.COM 	/*
7037836SJohn.Forte@Sun.COM 	 * Check if someone is trying to destroy this
7047836SJohn.Forte@Sun.COM 	 * connection.  If so fail the sync request,
7057836SJohn.Forte@Sun.COM 	 * as a method of fast fail.
7067836SJohn.Forte@Sun.COM 	 */
7077836SJohn.Forte@Sun.COM 	if (icp->conn_state_destroy == B_TRUE) {
7087836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SHUTDOWN);
7097836SJohn.Forte@Sun.COM 	}
7107836SJohn.Forte@Sun.COM 
7117836SJohn.Forte@Sun.COM 	bzero(&pp, sizeof (pp));
7127836SJohn.Forte@Sun.COM 
7137836SJohn.Forte@Sun.COM 	/* First get a copy of the HBA params */
7147836SJohn.Forte@Sun.COM 	bcopy(&ihp->hba_params, &icp->conn_params,
7157836SJohn.Forte@Sun.COM 	    sizeof (iscsi_login_params_t));
71610156SZhang.Yi@Sun.COM 	bcopy(&ihp->hba_tunable_params, &icp->conn_tunable_params,
71710156SZhang.Yi@Sun.COM 	    sizeof (iscsi_tunable_params_t));
7187836SJohn.Forte@Sun.COM 
7197836SJohn.Forte@Sun.COM 	/*
7207836SJohn.Forte@Sun.COM 	 * Now we need to get the session configured
7217836SJohn.Forte@Sun.COM 	 * values from the persistent store and apply
7227836SJohn.Forte@Sun.COM 	 * them to our connection.
7237836SJohn.Forte@Sun.COM 	 */
7247836SJohn.Forte@Sun.COM 	(void) persistent_param_get((char *)isp->sess_name, &pp);
7257836SJohn.Forte@Sun.COM 	for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM;
7267836SJohn.Forte@Sun.COM 	    param_id++) {
7278194SJack.Meng@Sun.COM 		if (iscsiboot_prop && modrootloaded &&
7288194SJack.Meng@Sun.COM 		    !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) {
7298194SJack.Meng@Sun.COM 			/*
7308194SJack.Meng@Sun.COM 			 * iscsi boot with mpxio disabled
7318194SJack.Meng@Sun.COM 			 * while iscsi booting target's parameter overriden
7328194SJack.Meng@Sun.COM 			 * do no update target's parameters.
7338194SJack.Meng@Sun.COM 			 */
7348194SJack.Meng@Sun.COM 			if (pp.p_bitmap) {
7358194SJack.Meng@Sun.COM 				cmn_err(CE_NOTE, "Adopting "
7368194SJack.Meng@Sun.COM 				    " default login parameters in"
7378194SJack.Meng@Sun.COM 				    " boot session as MPxIO is disabled");
7388194SJack.Meng@Sun.COM 			}
7398194SJack.Meng@Sun.COM 			break;
7408194SJack.Meng@Sun.COM 		}
7417836SJohn.Forte@Sun.COM 		if (pp.p_bitmap & (1 << param_id)) {
7429162SPeter.Dunlap@Sun.COM 
7439162SPeter.Dunlap@Sun.COM 			switch (param_id) {
7447836SJohn.Forte@Sun.COM 			/*
7457836SJohn.Forte@Sun.COM 			 * Boolean parameters
7467836SJohn.Forte@Sun.COM 			 */
7477836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
7487836SJohn.Forte@Sun.COM 				icp->conn_params.data_pdu_in_order =
7497836SJohn.Forte@Sun.COM 				    pp.p_params.data_pdu_in_order;
7507836SJohn.Forte@Sun.COM 				break;
7517836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA:
7527836SJohn.Forte@Sun.COM 				icp->conn_params.immediate_data =
7537836SJohn.Forte@Sun.COM 				    pp.p_params.immediate_data;
7547836SJohn.Forte@Sun.COM 				break;
7557836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_INITIAL_R2T:
7567836SJohn.Forte@Sun.COM 				icp->conn_params.initial_r2t =
7577836SJohn.Forte@Sun.COM 				    pp.p_params.initial_r2t;
7587836SJohn.Forte@Sun.COM 				break;
7597836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER:
7607836SJohn.Forte@Sun.COM 				icp->conn_params.data_pdu_in_order =
7617836SJohn.Forte@Sun.COM 				    pp.p_params.data_pdu_in_order;
7627836SJohn.Forte@Sun.COM 				break;
7637836SJohn.Forte@Sun.COM 			/*
7647836SJohn.Forte@Sun.COM 			 * Integer parameters
7657836SJohn.Forte@Sun.COM 			 */
7667836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_HEADER_DIGEST:
7677836SJohn.Forte@Sun.COM 				icp->conn_params.header_digest =
7687836SJohn.Forte@Sun.COM 				    pp.p_params.header_digest;
7697836SJohn.Forte@Sun.COM 				break;
7707836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_DATA_DIGEST:
7717836SJohn.Forte@Sun.COM 				icp->conn_params.data_digest =
7727836SJohn.Forte@Sun.COM 				    pp.p_params.data_digest;
7737836SJohn.Forte@Sun.COM 				break;
7747836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN:
7757836SJohn.Forte@Sun.COM 				icp->conn_params.default_time_to_retain =
7767836SJohn.Forte@Sun.COM 				    pp.p_params.default_time_to_retain;
7777836SJohn.Forte@Sun.COM 				break;
7787836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT:
7797836SJohn.Forte@Sun.COM 				icp->conn_params.default_time_to_wait =
7807836SJohn.Forte@Sun.COM 				    pp.p_params.default_time_to_wait;
7817836SJohn.Forte@Sun.COM 				break;
7827836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH:
7837836SJohn.Forte@Sun.COM 				icp->conn_params.max_recv_data_seg_len =
7847836SJohn.Forte@Sun.COM 				    pp.p_params.max_recv_data_seg_len;
7857836SJohn.Forte@Sun.COM 				break;
7867836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH:
7877836SJohn.Forte@Sun.COM 				icp->conn_params.first_burst_length =
7887836SJohn.Forte@Sun.COM 				    pp.p_params.first_burst_length;
7897836SJohn.Forte@Sun.COM 				break;
7907836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH:
7917836SJohn.Forte@Sun.COM 				icp->conn_params.max_burst_length =
7927836SJohn.Forte@Sun.COM 				    pp.p_params.max_burst_length;
7937836SJohn.Forte@Sun.COM 				break;
7947836SJohn.Forte@Sun.COM 
7957836SJohn.Forte@Sun.COM 			/*
7967836SJohn.Forte@Sun.COM 			 * Integer parameters which currently are unsettable
7977836SJohn.Forte@Sun.COM 			 */
7987836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS:
7997836SJohn.Forte@Sun.COM 				/* FALLTHRU */
8007836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T:
8017836SJohn.Forte@Sun.COM 				/* FALLTHRU */
8027836SJohn.Forte@Sun.COM 			case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL:
8037836SJohn.Forte@Sun.COM 				/* FALLTHRU */
8047836SJohn.Forte@Sun.COM 			default:
8057836SJohn.Forte@Sun.COM 				break;
8067836SJohn.Forte@Sun.COM 			}
8077836SJohn.Forte@Sun.COM 		}
8087836SJohn.Forte@Sun.COM 	}
8097836SJohn.Forte@Sun.COM 
81010156SZhang.Yi@Sun.COM 	if (persistent_get_tunable_param((char *)isp->sess_name, &ptp) ==
81110156SZhang.Yi@Sun.COM 	    B_TRUE) {
81210156SZhang.Yi@Sun.COM 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE) {
81310156SZhang.Yi@Sun.COM 			icp->conn_tunable_params.recv_login_rsp_timeout =
81410156SZhang.Yi@Sun.COM 			    ptp.p_params.recv_login_rsp_timeout;
81510156SZhang.Yi@Sun.COM 		}
81610156SZhang.Yi@Sun.COM 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX) {
81710156SZhang.Yi@Sun.COM 			icp->conn_tunable_params.conn_login_max =
81810156SZhang.Yi@Sun.COM 			    ptp.p_params.conn_login_max;
81910156SZhang.Yi@Sun.COM 		}
82010156SZhang.Yi@Sun.COM 		if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY) {
82110156SZhang.Yi@Sun.COM 			icp->conn_tunable_params.polling_login_delay =
82210156SZhang.Yi@Sun.COM 			    ptp.p_params.polling_login_delay;
82310156SZhang.Yi@Sun.COM 		}
82410156SZhang.Yi@Sun.COM 	}
82510156SZhang.Yi@Sun.COM 
8267836SJohn.Forte@Sun.COM 	/* Skip binding checks on discovery sessions */
8277836SJohn.Forte@Sun.COM 	if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) {
8287836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SUCCESS);
8297836SJohn.Forte@Sun.COM 	}
8307836SJohn.Forte@Sun.COM 
8317836SJohn.Forte@Sun.COM 	/*
8327836SJohn.Forte@Sun.COM 	 * Now we need to get the current optional connection
8337836SJohn.Forte@Sun.COM 	 * binding information.
8347836SJohn.Forte@Sun.COM 	 */
8357836SJohn.Forte@Sun.COM 	/* setup initial buffer for configured session information */
8367836SJohn.Forte@Sun.COM 	size = sizeof (*ics);
8377836SJohn.Forte@Sun.COM 	ics = kmem_zalloc(size, KM_SLEEP);
8387836SJohn.Forte@Sun.COM 	ics->ics_in = 1;
8397836SJohn.Forte@Sun.COM 
8407836SJohn.Forte@Sun.COM 	/* get configured sessions information */
8417836SJohn.Forte@Sun.COM 	name = (char *)isp->sess_name;
8427836SJohn.Forte@Sun.COM 	if (persistent_get_config_session(name, ics) == B_FALSE) {
8437836SJohn.Forte@Sun.COM 		/*
8447836SJohn.Forte@Sun.COM 		 * If we were unable to get target level information
8457836SJohn.Forte@Sun.COM 		 * then check the initiator level information.
8467836SJohn.Forte@Sun.COM 		 */
8477836SJohn.Forte@Sun.COM 		name = (char *)isp->sess_hba->hba_name;
8487836SJohn.Forte@Sun.COM 		if (persistent_get_config_session(name, ics) == B_FALSE) {
8497836SJohn.Forte@Sun.COM 			/*
8507836SJohn.Forte@Sun.COM 			 * No hba information is found.  So assume default
8517836SJohn.Forte@Sun.COM 			 * one session unbound behavior.
8527836SJohn.Forte@Sun.COM 			 */
8537836SJohn.Forte@Sun.COM 			ics->ics_out = 1;
8547836SJohn.Forte@Sun.COM 			ics->ics_bound = B_FALSE;
8557836SJohn.Forte@Sun.COM 		}
8567836SJohn.Forte@Sun.COM 	}
8577836SJohn.Forte@Sun.COM 
8588194SJack.Meng@Sun.COM 	if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot &&
8598194SJack.Meng@Sun.COM 	    !iscsi_chk_bootlun_mpxio(ihp)) {
8608194SJack.Meng@Sun.COM 		/*
8618194SJack.Meng@Sun.COM 		 * iscsi booting session with mpxio disabled,
8628194SJack.Meng@Sun.COM 		 * no need set multiple sessions for booting session
8638194SJack.Meng@Sun.COM 		 */
8648194SJack.Meng@Sun.COM 		ics->ics_out = 1;
8658194SJack.Meng@Sun.COM 		ics->ics_bound = B_FALSE;
8668194SJack.Meng@Sun.COM 		cmn_err(CE_NOTE, "MPxIO is disabled,"
8678194SJack.Meng@Sun.COM 		    " no need to configure multiple boot sessions");
8688194SJack.Meng@Sun.COM 	}
8698194SJack.Meng@Sun.COM 
8707836SJohn.Forte@Sun.COM 	/*
8717836SJohn.Forte@Sun.COM 	 * Check to make sure this session is still a configured
8727836SJohn.Forte@Sun.COM 	 * session.  The user might have decreased the session
8737836SJohn.Forte@Sun.COM 	 * count. (NOTE: byte 5 of the sess_isid is the session
8747836SJohn.Forte@Sun.COM 	 * count (via MS/T).  This counter starts at 0.)
8757836SJohn.Forte@Sun.COM 	 */
8768194SJack.Meng@Sun.COM 
8778194SJack.Meng@Sun.COM 
8787836SJohn.Forte@Sun.COM 	idx = isp->sess_isid[5];
8798194SJack.Meng@Sun.COM 
8808194SJack.Meng@Sun.COM 	if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) {
8818194SJack.Meng@Sun.COM 		/*
8828194SJack.Meng@Sun.COM 		 * This is temporary session for boot session propose
8838194SJack.Meng@Sun.COM 		 * no need to bound IP for this session
8848194SJack.Meng@Sun.COM 		 */
8858194SJack.Meng@Sun.COM 		icp->conn_bound = B_FALSE;
8868194SJack.Meng@Sun.COM 		kmem_free(ics, sizeof (iscsi_config_sess_t));
8878194SJack.Meng@Sun.COM 		return (ISCSI_STATUS_SUCCESS);
8888194SJack.Meng@Sun.COM 	}
8898194SJack.Meng@Sun.COM 
8907836SJohn.Forte@Sun.COM 	if (ics->ics_out <= idx) {
8917836SJohn.Forte@Sun.COM 		/*
8927836SJohn.Forte@Sun.COM 		 * No longer a configured session.  Return a
8937836SJohn.Forte@Sun.COM 		 * failure so we don't attempt to relogin.
8947836SJohn.Forte@Sun.COM 		 */
8957836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SHUTDOWN);
8967836SJohn.Forte@Sun.COM 	}
8977836SJohn.Forte@Sun.COM 
8987836SJohn.Forte@Sun.COM 	/*
8997836SJohn.Forte@Sun.COM 	 * If sessions are unbound set this information on
9007836SJohn.Forte@Sun.COM 	 * the connection and return success.
9017836SJohn.Forte@Sun.COM 	 */
9027836SJohn.Forte@Sun.COM 	if (ics->ics_bound == B_FALSE) {
9037836SJohn.Forte@Sun.COM 		icp->conn_bound = B_FALSE;
9047836SJohn.Forte@Sun.COM 		kmem_free(ics, sizeof (iscsi_config_sess_t));
9057836SJohn.Forte@Sun.COM 		return (ISCSI_STATUS_SUCCESS);
9067836SJohn.Forte@Sun.COM 	}
9077836SJohn.Forte@Sun.COM 
9087836SJohn.Forte@Sun.COM 	/*
9097836SJohn.Forte@Sun.COM 	 * Since the sessions are bound we need to find the matching
9107836SJohn.Forte@Sun.COM 	 * binding information for the session's isid.  If this
9117836SJohn.Forte@Sun.COM 	 * session's isid is > 0 then we need to get more configured
9127836SJohn.Forte@Sun.COM 	 * session information to find the binding info.
9137836SJohn.Forte@Sun.COM 	 */
9147836SJohn.Forte@Sun.COM 	if (idx > 0) {
9157836SJohn.Forte@Sun.COM 		int ics_out;
9167836SJohn.Forte@Sun.COM 
9177836SJohn.Forte@Sun.COM 		ics_out = ics->ics_out;
9187836SJohn.Forte@Sun.COM 		/* record new size and free last buffer */
9197836SJohn.Forte@Sun.COM 		size = ISCSI_SESSION_CONFIG_SIZE(ics_out);
9207836SJohn.Forte@Sun.COM 		kmem_free(ics, sizeof (*ics));
9217836SJohn.Forte@Sun.COM 
9227836SJohn.Forte@Sun.COM 		/* allocate new buffer */
9237836SJohn.Forte@Sun.COM 		ics = kmem_zalloc(size, KM_SLEEP);
9247836SJohn.Forte@Sun.COM 		ics->ics_in = ics_out;
9257836SJohn.Forte@Sun.COM 
9267836SJohn.Forte@Sun.COM 		/* get configured sessions information */
9277836SJohn.Forte@Sun.COM 		if (persistent_get_config_session(name, ics) != B_TRUE) {
9287836SJohn.Forte@Sun.COM 			cmn_err(CE_NOTE, "iscsi session(%d) - "
9297836SJohn.Forte@Sun.COM 			    "unable to get configured session information\n",
9307836SJohn.Forte@Sun.COM 			    isp->sess_oid);
9317836SJohn.Forte@Sun.COM 			kmem_free(ics, size);
9327836SJohn.Forte@Sun.COM 			return (ISCSI_STATUS_SHUTDOWN);
9337836SJohn.Forte@Sun.COM 		}
9347836SJohn.Forte@Sun.COM 	}
9357836SJohn.Forte@Sun.COM 
9367836SJohn.Forte@Sun.COM 	/* Copy correct binding information to the connection */
9377836SJohn.Forte@Sun.COM 	icp->conn_bound = B_TRUE;
9387836SJohn.Forte@Sun.COM 	if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) {
9397836SJohn.Forte@Sun.COM 		bcopy(&ics->ics_bindings[idx].i_addr.in4,
9407836SJohn.Forte@Sun.COM 		    &icp->conn_bound_addr.sin4.sin_addr.s_addr,
9417836SJohn.Forte@Sun.COM 		    sizeof (struct in_addr));
9429358SJames.Moore@Sun.COM 		icp->conn_bound_addr.sin4.sin_family = AF_INET;
9437836SJohn.Forte@Sun.COM 	} else {
9447836SJohn.Forte@Sun.COM 		bcopy(&ics->ics_bindings[idx].i_addr.in6,
9457836SJohn.Forte@Sun.COM 		    &icp->conn_bound_addr.sin6.sin6_addr.s6_addr,
9467836SJohn.Forte@Sun.COM 		    sizeof (struct in6_addr));
9479358SJames.Moore@Sun.COM 		icp->conn_bound_addr.sin6.sin6_family = AF_INET6;
9487836SJohn.Forte@Sun.COM 	}
9497836SJohn.Forte@Sun.COM 
9507836SJohn.Forte@Sun.COM 	kmem_free(ics, size);
9517836SJohn.Forte@Sun.COM 
9527836SJohn.Forte@Sun.COM 	return (ISCSI_STATUS_SUCCESS);
9537836SJohn.Forte@Sun.COM }
9547836SJohn.Forte@Sun.COM 
9557836SJohn.Forte@Sun.COM /*
9567836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
9577836SJohn.Forte@Sun.COM  * | Internal Connection Interfaces					|
9587836SJohn.Forte@Sun.COM  * +--------------------------------------------------------------------+
9597836SJohn.Forte@Sun.COM  */
9607836SJohn.Forte@Sun.COM 
9617836SJohn.Forte@Sun.COM /*
9627836SJohn.Forte@Sun.COM  * iscsi_conn_flush_active_cmds - flush all active icmdps
9637836SJohn.Forte@Sun.COM  *	for a connection.
9647836SJohn.Forte@Sun.COM  */
9657836SJohn.Forte@Sun.COM static void
iscsi_conn_flush_active_cmds(iscsi_conn_t * icp)9667836SJohn.Forte@Sun.COM iscsi_conn_flush_active_cmds(iscsi_conn_t *icp)
9677836SJohn.Forte@Sun.COM {
9687836SJohn.Forte@Sun.COM 	iscsi_cmd_t	*icmdp;
9697836SJohn.Forte@Sun.COM 	iscsi_sess_t	*isp;
9707836SJohn.Forte@Sun.COM 	boolean_t	lock_held = B_FALSE;
9717836SJohn.Forte@Sun.COM 
9727836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
9737836SJohn.Forte@Sun.COM 	isp = icp->conn_sess;
9747836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
9757836SJohn.Forte@Sun.COM 
9767836SJohn.Forte@Sun.COM 	if (mutex_owned(&icp->conn_queue_active.mutex)) {
9777836SJohn.Forte@Sun.COM 		lock_held = B_TRUE;
9787836SJohn.Forte@Sun.COM 	} else {
9797836SJohn.Forte@Sun.COM 		mutex_enter(&icp->conn_queue_active.mutex);
9807836SJohn.Forte@Sun.COM 	}
9817836SJohn.Forte@Sun.COM 
9827836SJohn.Forte@Sun.COM 	/* Flush active queue */
9837836SJohn.Forte@Sun.COM 	icmdp = icp->conn_queue_active.head;
9847836SJohn.Forte@Sun.COM 	while (icmdp != NULL) {
9859780SBing.Zhao@Sun.COM 
9869780SBing.Zhao@Sun.COM 		mutex_enter(&icmdp->cmd_mutex);
9879780SBing.Zhao@Sun.COM 		if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
9889780SBing.Zhao@Sun.COM 			icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
9899780SBing.Zhao@Sun.COM 		}
9909780SBing.Zhao@Sun.COM 		mutex_exit(&icmdp->cmd_mutex);
9919780SBing.Zhao@Sun.COM 
9927836SJohn.Forte@Sun.COM 		iscsi_cmd_state_machine(icmdp,
9937836SJohn.Forte@Sun.COM 		    ISCSI_CMD_EVENT_E7, isp);
9947836SJohn.Forte@Sun.COM 		icmdp = icp->conn_queue_active.head;
9957836SJohn.Forte@Sun.COM 	}
9967836SJohn.Forte@Sun.COM 
9979162SPeter.Dunlap@Sun.COM 	/* Wait for active queue to drain */
9989162SPeter.Dunlap@Sun.COM 	while (icp->conn_queue_active.count) {
9999162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_queue_active.mutex);
10009162SPeter.Dunlap@Sun.COM 		delay(drv_usectohz(100000));
10019162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_queue_active.mutex);
10029162SPeter.Dunlap@Sun.COM 	}
10039162SPeter.Dunlap@Sun.COM 
10047836SJohn.Forte@Sun.COM 	if (lock_held == B_FALSE) {
10057836SJohn.Forte@Sun.COM 		mutex_exit(&icp->conn_queue_active.mutex);
10067836SJohn.Forte@Sun.COM 	}
10077836SJohn.Forte@Sun.COM 
10089162SPeter.Dunlap@Sun.COM 	/* Wait for IDM abort queue to drain (if necessary) */
10099162SPeter.Dunlap@Sun.COM 	mutex_enter(&icp->conn_queue_idm_aborting.mutex);
10109162SPeter.Dunlap@Sun.COM 	while (icp->conn_queue_idm_aborting.count) {
10119162SPeter.Dunlap@Sun.COM 		mutex_exit(&icp->conn_queue_idm_aborting.mutex);
10129162SPeter.Dunlap@Sun.COM 		delay(drv_usectohz(100000));
10139162SPeter.Dunlap@Sun.COM 		mutex_enter(&icp->conn_queue_idm_aborting.mutex);
10149162SPeter.Dunlap@Sun.COM 	}
10159162SPeter.Dunlap@Sun.COM 	mutex_exit(&icp->conn_queue_idm_aborting.mutex);
10167836SJohn.Forte@Sun.COM }
10177836SJohn.Forte@Sun.COM 
10187836SJohn.Forte@Sun.COM /*
10197836SJohn.Forte@Sun.COM  * iscsi_conn_retry - retry connect/login
10207836SJohn.Forte@Sun.COM  */
10219162SPeter.Dunlap@Sun.COM void
iscsi_conn_retry(iscsi_sess_t * isp,iscsi_conn_t * icp)10227836SJohn.Forte@Sun.COM iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
10237836SJohn.Forte@Sun.COM {
10247836SJohn.Forte@Sun.COM 	iscsi_task_t *itp;
1025*12161SJack.Meng@Sun.COM 	uint32_t event_count;
10267836SJohn.Forte@Sun.COM 
10277836SJohn.Forte@Sun.COM 	ASSERT(isp != NULL);
10287836SJohn.Forte@Sun.COM 	ASSERT(icp != NULL);
10297836SJohn.Forte@Sun.COM 
10307836SJohn.Forte@Sun.COM 	/* set login min/max time values */
10317836SJohn.Forte@Sun.COM 	iscsi_conn_set_login_min_max(icp,
10327836SJohn.Forte@Sun.COM 	    ISCSI_CONN_DEFAULT_LOGIN_MIN,
103310156SZhang.Yi@Sun.COM 	    icp->conn_tunable_params.conn_login_max);
10347836SJohn.Forte@Sun.COM 
10359162SPeter.Dunlap@Sun.COM 	ISCSI_CONN_LOG(CE_NOTE, "DEBUG: iscsi_conn_retry: icp: %p icp: %p ",
10369162SPeter.Dunlap@Sun.COM 	    (void *)icp,
10379162SPeter.Dunlap@Sun.COM 	    (void *)icp->conn_ic);
10389162SPeter.Dunlap@Sun.COM 
10397836SJohn.Forte@Sun.COM 	/*
10407836SJohn.Forte@Sun.COM 	 * Sync base connection information before login.
10417836SJohn.Forte@Sun.COM 	 * A login redirection might have shifted the
10427836SJohn.Forte@Sun.COM 	 * current information from the base.
10437836SJohn.Forte@Sun.COM 	 */
10447836SJohn.Forte@Sun.COM 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
10457836SJohn.Forte@Sun.COM 	    sizeof (icp->conn_curr_addr));
10467836SJohn.Forte@Sun.COM 
10477836SJohn.Forte@Sun.COM 	/* schedule login task */
10487836SJohn.Forte@Sun.COM 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
10497836SJohn.Forte@Sun.COM 	itp->t_arg = icp;
10507836SJohn.Forte@Sun.COM 	itp->t_blocking = B_FALSE;
1051*12161SJack.Meng@Sun.COM 	if (ddi_taskq_dispatch(isp->sess_login_taskq,
10527836SJohn.Forte@Sun.COM 	    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
10537836SJohn.Forte@Sun.COM 	    DDI_SUCCESS) {
10547836SJohn.Forte@Sun.COM 		kmem_free(itp, sizeof (iscsi_task_t));
10559162SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
10569162SPeter.Dunlap@Sun.COM 		    "unable to schedule login task", icp->conn_oid);
10577836SJohn.Forte@Sun.COM 
10589162SPeter.Dunlap@Sun.COM 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
1059*12161SJack.Meng@Sun.COM 		event_count = atomic_inc_32_nv(
1060*12161SJack.Meng@Sun.COM 		    &isp->sess_state_event_count);
1061*12161SJack.Meng@Sun.COM 		iscsi_sess_enter_state_zone(isp);
1062*12161SJack.Meng@Sun.COM 
10637836SJohn.Forte@Sun.COM 		iscsi_sess_state_machine(isp,
1064*12161SJack.Meng@Sun.COM 		    ISCSI_SESS_EVENT_N6, event_count);
1065*12161SJack.Meng@Sun.COM 
1066*12161SJack.Meng@Sun.COM 		iscsi_sess_exit_state_zone(isp);
10677836SJohn.Forte@Sun.COM 	}
10687836SJohn.Forte@Sun.COM }
10699162SPeter.Dunlap@Sun.COM 
10709162SPeter.Dunlap@Sun.COM void
iscsi_conn_update_state(iscsi_conn_t * icp,iscsi_conn_state_t next_state)10719162SPeter.Dunlap@Sun.COM iscsi_conn_update_state(iscsi_conn_t *icp, iscsi_conn_state_t
10729162SPeter.Dunlap@Sun.COM 			    next_state)
10739162SPeter.Dunlap@Sun.COM {
10749162SPeter.Dunlap@Sun.COM 	mutex_enter(&icp->conn_state_mutex);
10759162SPeter.Dunlap@Sun.COM 	(void) iscsi_conn_update_state_locked(icp, next_state);
10769162SPeter.Dunlap@Sun.COM 	mutex_exit(&icp->conn_state_mutex);
10779162SPeter.Dunlap@Sun.COM }
10789162SPeter.Dunlap@Sun.COM 
10799162SPeter.Dunlap@Sun.COM void
iscsi_conn_update_state_locked(iscsi_conn_t * icp,iscsi_conn_state_t next_state)10809162SPeter.Dunlap@Sun.COM iscsi_conn_update_state_locked(iscsi_conn_t *icp,
10819162SPeter.Dunlap@Sun.COM 	    iscsi_conn_state_t next_state)
10829162SPeter.Dunlap@Sun.COM {
10839162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&icp->conn_state_mutex));
10849162SPeter.Dunlap@Sun.COM 	next_state = (next_state > ISCSI_CONN_STATE_MAX) ?
10859162SPeter.Dunlap@Sun.COM 	    ISCSI_CONN_STATE_MAX : next_state;
10869162SPeter.Dunlap@Sun.COM 	idm_sm_audit_state_change(&icp->conn_state_audit,
10879162SPeter.Dunlap@Sun.COM 	    SAS_ISCSI_CONN, icp->conn_state, next_state);
10889162SPeter.Dunlap@Sun.COM 	switch (next_state) {
10899162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_FREE:
10909162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_IN_LOGIN:
10919162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_LOGGED_IN:
10929162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_IN_LOGOUT:
10939162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_FAILED:
10949162SPeter.Dunlap@Sun.COM 	case ISCSI_CONN_STATE_POLLING:
10959162SPeter.Dunlap@Sun.COM 		ISCSI_CONN_LOG(CE_NOTE,
10969162SPeter.Dunlap@Sun.COM 		    "iscsi_conn_update_state conn %p %s(%d) -> %s(%d)",
10979162SPeter.Dunlap@Sun.COM 		    (void *)icp,
10989162SPeter.Dunlap@Sun.COM 		    iscsi_ics_name[icp->conn_state], icp->conn_state,
10999162SPeter.Dunlap@Sun.COM 		    iscsi_ics_name[next_state], next_state);
11009162SPeter.Dunlap@Sun.COM 		icp->conn_prev_state = icp->conn_state;
11019162SPeter.Dunlap@Sun.COM 		icp->conn_state = next_state;
11029162SPeter.Dunlap@Sun.COM 		cv_broadcast(&icp->conn_state_change);
11039162SPeter.Dunlap@Sun.COM 		break;
11049162SPeter.Dunlap@Sun.COM 	default:
11059162SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "Update state found illegal state: %x "
11069162SPeter.Dunlap@Sun.COM 		    "prev_state: %x", next_state, icp->conn_prev_state);
11079162SPeter.Dunlap@Sun.COM 		ASSERT(0);
11089162SPeter.Dunlap@Sun.COM 	}
11099162SPeter.Dunlap@Sun.COM }
1110