usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_conn.c
changeset 9162 b011b0287065
parent 8194 f204d5cda6c1
child 9358 0fc8688a5b21
equal deleted inserted replaced
9161:7dca69f75d8e 9162:b011b0287065
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
    18  *
    18  *
    19  * CDDL HEADER END
    19  * CDDL HEADER END
    20  */
    20  */
    21 /*
    21 /*
    22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
    22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
    23  * Use is subject to license terms.
    23  * Use is subject to license terms.
    24  *
    24  *
    25  * iSCSI connection interfaces
    25  * iSCSI connection interfaces
    26  */
    26  */
    27 
    27 
       
    28 #define	ISCSI_ICS_NAMES
    28 #include "iscsi.h"
    29 #include "iscsi.h"
    29 #include "persistent.h"
    30 #include "persistent.h"
    30 #include <sys/bootprops.h>
    31 #include <sys/bootprops.h>
    31 
    32 
    32 extern ib_boot_prop_t   *iscsiboot_prop;
    33 extern ib_boot_prop_t   *iscsiboot_prop;
    33 
    34 
    34 /* interface connection interfaces */
    35 static void iscsi_client_notify_task(void *cn_task_void);
    35 static iscsi_status_t iscsi_conn_state_free(iscsi_conn_t *icp,
    36 
    36     iscsi_conn_event_t event);
       
    37 static void iscsi_conn_state_in_login(iscsi_conn_t *icp,
       
    38     iscsi_conn_event_t event);
       
    39 static void iscsi_conn_state_logged_in(iscsi_conn_t *icp,
       
    40     iscsi_conn_event_t event);
       
    41 static void iscsi_conn_state_in_logout(iscsi_conn_t *icp,
       
    42     iscsi_conn_event_t event);
       
    43 static void iscsi_conn_state_failed(iscsi_conn_t *icp,
       
    44     iscsi_conn_event_t event);
       
    45 static void iscsi_conn_state_polling(iscsi_conn_t *icp,
       
    46     iscsi_conn_event_t event);
       
    47 static char *iscsi_conn_event_str(iscsi_conn_event_t event);
       
    48 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
    37 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp);
    49 
    38 
    50 static void iscsi_conn_logged_in(iscsi_sess_t *isp,
       
    51     iscsi_conn_t *icp);
       
    52 static void iscsi_conn_retry(iscsi_sess_t *isp,
       
    53     iscsi_conn_t *icp);
       
    54 
       
    55 #define	SHUTDOWN_TIMEOUT	180 /* seconds */
    39 #define	SHUTDOWN_TIMEOUT	180 /* seconds */
    56 
    40 
    57 extern int modrootloaded;
    41 extern int modrootloaded;
       
    42 
       
    43 boolean_t iscsi_conn_logging = B_FALSE;
       
    44 
    58 /*
    45 /*
    59  * +--------------------------------------------------------------------+
    46  * +--------------------------------------------------------------------+
    60  * | External Connection Interfaces					|
    47  * | External Connection Interfaces					|
    61  * +--------------------------------------------------------------------+
    48  * +--------------------------------------------------------------------+
    62  */
    49  */
   103 	 */
    90 	 */
   104 	icp->conn_sig			= ISCSI_SIG_CONN;
    91 	icp->conn_sig			= ISCSI_SIG_CONN;
   105 	icp->conn_state			= ISCSI_CONN_STATE_FREE;
    92 	icp->conn_state			= ISCSI_CONN_STATE_FREE;
   106 	mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
    93 	mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL);
   107 	cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
    94 	cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL);
       
    95 	mutex_init(&icp->conn_login_mutex, NULL, MUTEX_DRIVER, NULL);
       
    96 	cv_init(&icp->conn_login_cv, NULL, CV_DRIVER, NULL);
   108 	icp->conn_state_destroy		= B_FALSE;
    97 	icp->conn_state_destroy		= B_FALSE;
       
    98 	idm_sm_audit_init(&icp->conn_state_audit);
   109 	icp->conn_sess			= isp;
    99 	icp->conn_sess			= isp;
   110 	icp->conn_state_lbolt		= ddi_get_lbolt();
       
   111 
   100 
   112 	mutex_enter(&iscsi_oid_mutex);
   101 	mutex_enter(&iscsi_oid_mutex);
   113 	icp->conn_oid = iscsi_oid++;
   102 	icp->conn_oid = iscsi_oid++;
   114 	mutex_exit(&iscsi_oid_mutex);
   103 	mutex_exit(&iscsi_oid_mutex);
   115 
   104 
   116 	/* Creation of the receive thread */
   105 	/*
   117 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_RXTH_NAME_FORMAT,
   106 	 * IDM CN taskq
       
   107 	 */
       
   108 
       
   109 	if (snprintf(th_name, sizeof (th_name) - 1,
       
   110 	    ISCSI_CONN_CN_TASKQ_NAME_FORMAT,
   118 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
   111 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
   119 	    icp->conn_oid) >= sizeof (th_name)) {
   112 	    icp->conn_oid) >= sizeof (th_name)) {
   120 		cv_destroy(&icp->conn_state_change);
   113 		cv_destroy(&icp->conn_state_change);
   121 		mutex_destroy(&icp->conn_state_mutex);
   114 		mutex_destroy(&icp->conn_state_mutex);
   122 		kmem_free(icp, sizeof (iscsi_conn_t));
   115 		kmem_free(icp, sizeof (iscsi_conn_t));
   123 		*icpp = NULL;
   116 		*icpp = NULL;
   124 		return (ISCSI_STATUS_INTERNAL_ERROR);
   117 		return (ISCSI_STATUS_INTERNAL_ERROR);
   125 	}
   118 	}
   126 
   119 
   127 	icp->conn_rx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
   120 	icp->conn_cn_taskq =
   128 	    th_name, iscsi_rx_thread, icp);
   121 	    ddi_taskq_create(icp->conn_sess->sess_hba->hba_dip, th_name, 1,
   129 
   122 	    TASKQ_DEFAULTPRI, 0);
   130 	/* Creation of the transfer thread */
   123 	if (icp->conn_cn_taskq == NULL) {
   131 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
       
   132 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
       
   133 	    icp->conn_oid) >= sizeof (th_name)) {
       
   134 		iscsi_thread_destroy(icp->conn_rx_thread);
       
   135 		cv_destroy(&icp->conn_state_change);
   124 		cv_destroy(&icp->conn_state_change);
   136 		mutex_destroy(&icp->conn_state_mutex);
   125 		mutex_destroy(&icp->conn_state_mutex);
   137 		kmem_free(icp, sizeof (iscsi_conn_t));
   126 		kmem_free(icp, sizeof (iscsi_conn_t));
   138 		*icpp = NULL;
   127 		*icpp = NULL;
   139 		return (ISCSI_STATUS_INTERNAL_ERROR);
   128 		return (ISCSI_STATUS_INTERNAL_ERROR);
   140 	}
   129 	}
   141 
   130 
       
   131 	/* Creation of the transfer thread */
       
   132 	if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT,
       
   133 	    icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid,
       
   134 	    icp->conn_oid) >= sizeof (th_name)) {
       
   135 		cv_destroy(&icp->conn_state_change);
       
   136 		mutex_destroy(&icp->conn_state_mutex);
       
   137 		kmem_free(icp, sizeof (iscsi_conn_t));
       
   138 		ddi_taskq_destroy(icp->conn_cn_taskq);
       
   139 		*icpp = NULL;
       
   140 		return (ISCSI_STATUS_INTERNAL_ERROR);
       
   141 	}
       
   142 
   142 	icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
   143 	icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip,
   143 	    th_name, iscsi_tx_thread, icp);
   144 	    th_name, iscsi_tx_thread, icp);
   144 
   145 
   145 	/* setup connection queues */
   146 	/* setup connection queues */
   146 	iscsi_init_queue(&icp->conn_queue_active);
   147 	iscsi_init_queue(&icp->conn_queue_active);
       
   148 	iscsi_init_queue(&icp->conn_queue_idm_aborting);
   147 
   149 
   148 	bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
   150 	bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr));
   149 
   151 
   150 	/* Add new connection to the session connection list */
   152 	/* Add new connection to the session connection list */
   151 	icp->conn_cid = isp->sess_conn_next_cid++;
   153 	icp->conn_cid = isp->sess_conn_next_cid++;
   162 	*icpp = icp;
   164 	*icpp = icp;
   163 
   165 
   164 	return (ISCSI_STATUS_SUCCESS);
   166 	return (ISCSI_STATUS_SUCCESS);
   165 }
   167 }
   166 
   168 
       
   169 /*
       
   170  * iscsi_conn_online - This attempts to take a connection from
       
   171  * ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN.
       
   172  */
       
   173 iscsi_status_t
       
   174 iscsi_conn_online(iscsi_conn_t *icp)
       
   175 {
       
   176 	iscsi_task_t	*itp;
       
   177 	iscsi_status_t	rval;
       
   178 
       
   179 	ASSERT(icp != NULL);
       
   180 	ASSERT(mutex_owned(&icp->conn_state_mutex));
       
   181 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
       
   182 
       
   183 	/*
       
   184 	 * If we are attempting to connect then for the purposes of the
       
   185 	 * other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN.
       
   186 	 */
       
   187 	iscsi_conn_update_state_locked(icp, ISCSI_CONN_STATE_IN_LOGIN);
       
   188 	mutex_exit(&icp->conn_state_mutex);
       
   189 
       
   190 	/*
       
   191 	 * Sync base connection information before login
       
   192 	 * A login redirection might have shifted the
       
   193 	 * current information from the base.
       
   194 	 */
       
   195 	bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
       
   196 	    sizeof (icp->conn_curr_addr));
       
   197 
       
   198 	itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
       
   199 	ASSERT(itp != NULL);
       
   200 
       
   201 	itp->t_arg = icp;
       
   202 	itp->t_blocking = B_TRUE;
       
   203 	rval = iscsi_login_start(itp);
       
   204 	kmem_free(itp, sizeof (iscsi_task_t));
       
   205 
       
   206 	mutex_enter(&icp->conn_state_mutex);
       
   207 
       
   208 	return (rval);
       
   209 }
   167 
   210 
   168 /*
   211 /*
   169  * iscsi_conn_offline - This attempts to take a connection from
   212  * iscsi_conn_offline - This attempts to take a connection from
   170  * any state to ISCSI_CONN_STATE_FREE.
   213  * any state to ISCSI_CONN_STATE_FREE.
   171  */
   214  */
   182 	 * transitionary and its unsafe to perform actions
   225 	 * transitionary and its unsafe to perform actions
   183 	 * on the connection in those states.  Set a flag
   226 	 * on the connection in those states.  Set a flag
   184 	 * on the connection to influence the transitions
   227 	 * on the connection to influence the transitions
   185 	 * to quickly complete.  Then wait for a state
   228 	 * to quickly complete.  Then wait for a state
   186 	 * transition.
   229 	 * transition.
       
   230 	 *
       
   231 	 * ISCSI_CONN_STATE_LOGGED_IN is set immediately at the
       
   232 	 * start of CN_NOTIFY_FFP processing. icp->conn_state_ffp
       
   233 	 * is set to true at the end of ffp processing, at which
       
   234 	 * point any session updates are complete.  We don't
       
   235 	 * want to start offlining the connection before we're
       
   236 	 * done completing the FFP processing since this might
       
   237 	 * interrupt the discovery process.
   187 	 */
   238 	 */
   188 	delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
   239 	delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT);
   189 	mutex_enter(&icp->conn_state_mutex);
   240 	mutex_enter(&icp->conn_state_mutex);
   190 	icp->conn_state_destroy = B_TRUE;
   241 	icp->conn_state_destroy = B_TRUE;
   191 	while ((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
   242 	while ((((icp->conn_state != ISCSI_CONN_STATE_FREE) &&
   192 	    (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN) &&
   243 	    (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN)) ||
       
   244 	    ((icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) &&
       
   245 	    !icp->conn_state_ffp)) &&
   193 	    (ddi_get_lbolt() < delay)) {
   246 	    (ddi_get_lbolt() < delay)) {
   194 		/* wait for transition */
   247 		/* wait for transition */
   195 		(void) cv_timedwait(&icp->conn_state_change,
   248 		(void) cv_timedwait(&icp->conn_state_change,
   196 		    &icp->conn_state_mutex, delay);
   249 		    &icp->conn_state_mutex, delay);
   197 	}
   250 	}
   198 
   251 
   199 	/* Final check whether we can destroy the connection */
       
   200 	switch (icp->conn_state) {
   252 	switch (icp->conn_state) {
   201 	case ISCSI_CONN_STATE_FREE:
   253 	case ISCSI_CONN_STATE_FREE:
   202 		/* Easy case - Connection is dead */
       
   203 		break;
   254 		break;
   204 	case ISCSI_CONN_STATE_LOGGED_IN:
   255 	case ISCSI_CONN_STATE_LOGGED_IN:
   205 		/* Hard case - Force connection logout */
   256 		if (icp->conn_state_ffp)
   206 		(void) iscsi_conn_state_machine(icp,
   257 			(void) iscsi_handle_logout(icp);
   207 		    ISCSI_CONN_EVENT_T9);
   258 		else {
       
   259 			icp->conn_state_destroy = B_FALSE;
       
   260 			mutex_exit(&icp->conn_state_mutex);
       
   261 			return (ISCSI_STATUS_INTERNAL_ERROR);
       
   262 		}
   208 		break;
   263 		break;
   209 	case ISCSI_CONN_STATE_IN_LOGIN:
   264 	case ISCSI_CONN_STATE_IN_LOGIN:
   210 	case ISCSI_CONN_STATE_IN_LOGOUT:
   265 	case ISCSI_CONN_STATE_IN_LOGOUT:
   211 	case ISCSI_CONN_STATE_FAILED:
   266 	case ISCSI_CONN_STATE_FAILED:
   212 	case ISCSI_CONN_STATE_POLLING:
   267 	case ISCSI_CONN_STATE_POLLING:
   213 	default:
   268 	default:
   214 		/* All other cases fail the destroy */
       
   215 		icp->conn_state_destroy = B_FALSE;
   269 		icp->conn_state_destroy = B_FALSE;
   216 		mutex_exit(&icp->conn_state_mutex);
   270 		mutex_exit(&icp->conn_state_mutex);
   217 		return (ISCSI_STATUS_INTERNAL_ERROR);
   271 		return (ISCSI_STATUS_INTERNAL_ERROR);
   218 	}
   272 	}
   219 	mutex_exit(&icp->conn_state_mutex);
   273 	mutex_exit(&icp->conn_state_mutex);
   239 
   293 
   240 	if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
   294 	if (icp->conn_state != ISCSI_CONN_STATE_FREE) {
   241 		return (ISCSI_STATUS_INTERNAL_ERROR);
   295 		return (ISCSI_STATUS_INTERNAL_ERROR);
   242 	}
   296 	}
   243 
   297 
   244 	/* Destroy receive thread */
       
   245 	iscsi_thread_destroy(icp->conn_rx_thread);
       
   246 
       
   247 	/* Destroy transfer thread */
   298 	/* Destroy transfer thread */
   248 	iscsi_thread_destroy(icp->conn_tx_thread);
   299 	iscsi_thread_destroy(icp->conn_tx_thread);
       
   300 	ddi_taskq_destroy(icp->conn_cn_taskq);
   249 
   301 
   250 	/* Terminate connection queues */
   302 	/* Terminate connection queues */
       
   303 	iscsi_destroy_queue(&icp->conn_queue_idm_aborting);
   251 	iscsi_destroy_queue(&icp->conn_queue_active);
   304 	iscsi_destroy_queue(&icp->conn_queue_active);
   252 
   305 
       
   306 	cv_destroy(&icp->conn_login_cv);
       
   307 	mutex_destroy(&icp->conn_login_mutex);
   253 	cv_destroy(&icp->conn_state_change);
   308 	cv_destroy(&icp->conn_state_change);
   254 	mutex_destroy(&icp->conn_state_mutex);
   309 	mutex_destroy(&icp->conn_state_mutex);
   255 
   310 
   256 	/*
   311 	/*
   257 	 * Remove connection from sessions linked list.
   312 	 * Remove connection from sessions linked list.
   315 	icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
   370 	icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min);
   316 	icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
   371 	icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max);
   317 }
   372 }
   318 
   373 
   319 
   374 
   320 
   375 /*
   321 /*
   376  * Process the idm notifications
   322  * iscsi_conn_state_machine - This function is used to drive the
   377  */
   323  * state machine of the iscsi connection.  It takes in a connection
   378 idm_status_t
   324  * and the associated event effecting the connection.
   379 iscsi_client_notify(idm_conn_t *ic, idm_client_notify_t icn, uintptr_t data)
   325  *
   380 {
   326  * 7.1.3  Connection State Diagram for an Initiator
   381 	iscsi_cn_task_t		*cn;
   327  *      Symbolic Names for States:
   382 	iscsi_conn_t		*icp = ic->ic_handle;
   328  *      S1: FREE        - State on instantiation, or after successful
   383 	iscsi_sess_t		*isp;
   329  *                        connection closure.
   384 
   330  *      S2: IN_LOGIN    - Waiting for login process to conclude,
   385 	/*
   331  *                        possibly involving several PDU exchanges.
   386 	 * Don't access icp if the notification is CN_CONNECT_DESTROY
   332  *      S3: LOGGED_IN   - In Full Feature Phase, waiting for all internal,
   387 	 * since icp may have already been freed.
   333  *                        iSCSI, and transport events
   388 	 *
   334  *      S4: IN_LOGOUT   - Waiting for the Logout repsonse.
   389 	 * Handle CN_FFP_ENABLED and CN_CONNECT_DESTROY immediately
   335  *      S5: FAILED      - The connection has failed.  Attempting
   390 	 */
   336  *			  to reconnect.
   391 	switch (icn) {
   337  *      S6: POLLING     - The connection reconnect attempts have
   392 	case CN_CONNECT_FAIL:
   338  *                        failed.  Continue to poll at a lower
   393 	case CN_LOGIN_FAIL:
   339  *                        frequency.
   394 		/*
   340  *
   395 		 * Wakeup any thread waiting for login stuff to happen.
   341  *      States S3, S4 constitute the Full Feature Phase
   396 		 */
   342  *              of the connection.
   397 		ASSERT(icp != NULL);
   343  *
   398 		iscsi_login_update_state(icp, LOGIN_ERROR);
   344  *      The state diagram is as follows:
   399 		return (IDM_STATUS_SUCCESS);
   345  *                 -------
   400 	case CN_READY_FOR_LOGIN:
   346  *      +-------->/ S1    \<------------------------------+
   401 		idm_conn_hold(ic); /* Released in CN_CONNECT_LOST */
   347  *      |      +->\       /<---+            /---\         |
   402 		mutex_enter(&icp->conn_state_mutex);
   348  *      |     /    ---+---     |T7/30     T7|   |         |
   403 		icp->conn_state_idm_connected = B_TRUE;
   349  *      |    +        |        |            \->------     |
   404 		cv_broadcast(&icp->conn_state_change);
   350  *      |  T8|        |T1     /      T5       / S6   \--->|
   405 		mutex_exit(&icp->conn_state_mutex);
   351  *      |    |        |      /     +----------\      /T30 |
   406 
   352  *      |    |        V     /     /            ------     |
   407 		iscsi_login_update_state(icp, LOGIN_READY);
   353  *      |    |     ------- /     /               ^        |
   408 		return (IDM_STATUS_SUCCESS);
   354  *      |    |    / S2    \     /   T5           |T7      |
   409 	case CN_CONNECT_DESTROY:
   355  *      |    |    \       /    +-------------- --+---     |
   410 		/*
   356  *      |    |     ---+---    /               / S5   \--->|
   411 		 * We released any dependecies we had on this object in
   357  *      |    |        |      /      T14/T15   \      /T30 |
   412 		 * either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need
   358  *      |    |        |T5   /  +-------------> ------     |
   413 		 * to destroy the IDM connection now.
   359  *      |    |        |    /  /                           |
   414 		 */
   360  *      |    |        |   /  /         T11                |
   415 		idm_ini_conn_destroy(ic);
   361  *      |    |        |  /  /         +----+              |
   416 		return (IDM_STATUS_SUCCESS);
   362  *      |    |        V V  /          |    |              |
   417 	}
   363  *      |    |      ------+       ----+--  |              |
       
   364  *      |    +-----/ S3    \T9/11/ S4    \<+              |
       
   365  *      +----------\       /---->\       /----------------+
       
   366  *                  -------       -------        T15/T17
       
   367  *
       
   368  * The state transition table is as follows:
       
   369  *
       
   370  *         +-----+---+---+------+------+---+
       
   371  *         |S1   |S2 |S3 |S4    |S5    |S6 |
       
   372  *      ---+-----+---+---+------+------+---+
       
   373  *       S1|T1   |T1 | - | -    | -    |   |
       
   374  *      ---+-----+---+---+------+------+---+
       
   375  *       S2|T7/30|-  |T5 | -    | -    |   |
       
   376  *      ---+-----+---+---+------+------+---+
       
   377  *       S3|T8   |-  | - |T9/11 |T14/15|   |
       
   378  *      ---+-----+---+---+------+------+---+
       
   379  *       S4|     |-  | - |T11   |T15/17|   |
       
   380  *      ---+-----+---+---+------+------+---+
       
   381  *       S5|T30  |   |T5 |      |      |T7 |
       
   382  *      ---+-----+---+---+------+------+---+
       
   383  *       S6|T30  |   |T5 |      |      |T7 |
       
   384  *      ---+-----+---+---+------+------+---+
       
   385  *
       
   386  * Events definitions:
       
   387  *
       
   388  * -T1: Transport connection request was made (e.g., TCP SYN sent).
       
   389  * -T5: The final iSCSI Login response with a Status-Class of zero was
       
   390  *      received.
       
   391  * -T7: One of the following events caused the transition:
       
   392  *      - Login timed out.
       
   393  *      - A transport disconnect indication was received.
       
   394  *      - A transport reset was received.
       
   395  *      - An internal event indicating a transport timeout was
       
   396  *        received.
       
   397  *      - An internal event of receiving a Logout repsonse (success)
       
   398  *        on another connection for a "close the session" Logout
       
   399  *        request was received.
       
   400  *      * In all these cases, the transport connection is closed.
       
   401  * -T8: An internal event of receiving a Logout response (success)
       
   402  *      on another connection for a "close the session" Logout request
       
   403  *      was received, thus closing this connection requiring no further
       
   404  *      cleanup.
       
   405  * -T9: An internal event that indicates the readiness to start the
       
   406  *      Logout process was received, thus prompting an iSCSI Logout to
       
   407  *      be sent by the initiator.
       
   408  * -T11: Async PDU with AsyncEvent "Request Logout" was received.
       
   409  * -T13: An iSCSI Logout response (success) was received, or an internal
       
   410  *      event of receiving a Logout response (success) on another
       
   411  *      connection was received.
       
   412  * -T14: One or more of the following events case this transition:
       
   413  *	- Header Digest Error
       
   414  *	- Protocol Error
       
   415  * -T15: One or more of the following events caused this transition:
       
   416  *      - Internal event that indicates a transport connection timeout
       
   417  *        was received thus prompting transport RESET or transport
       
   418  *        connection closure.
       
   419  *      - A transport RESET
       
   420  *      - A transport disconnect indication.
       
   421  *      - Async PDU with AsyncEvent "Drop connection" (for this CID)
       
   422  *      - Async PDU with AsyncEvent "Drop all connections"
       
   423  * -T17: One or more of the following events caused this transition:
       
   424  *      - Logout response, (failure i.e., a non-zero status) was
       
   425  *      received, or Logout timed out.
       
   426  *      - Any of the events specified for T15.
       
   427  * -T30: One of the following event caused the transition:
       
   428  *	- Thefinal iSCSI Login response was received with a non-zero
       
   429  *	  Status-Class.
       
   430  */
       
   431 iscsi_status_t
       
   432 iscsi_conn_state_machine(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
   433 {
       
   434 	iscsi_status_t	    status = ISCSI_STATUS_SUCCESS;
       
   435 
   418 
   436 	ASSERT(icp != NULL);
   419 	ASSERT(icp != NULL);
   437 	ASSERT(mutex_owned(&icp->conn_state_mutex));
   420 	isp = icp->conn_sess;
   438 
   421 
   439 	DTRACE_PROBE3(event, iscsi_conn_t *, icp,
   422 	/*
   440 	    char *, iscsi_conn_state_str(icp->conn_state),
   423 	 * Dispatch notifications to the taskq since they often require
   441 	    char *, iscsi_conn_event_str(event));
   424 	 * long blocking operations.  In the case of CN_CONNECT_DESTROY
   442 
   425 	 * we actually just want to destroy the connection which we
   443 	icp->conn_prev_state = icp->conn_state;
   426 	 * can't do in the IDM taskq context.
   444 	icp->conn_state_lbolt = ddi_get_lbolt();
   427 	 */
   445 
   428 	cn = kmem_alloc(sizeof (*cn), KM_SLEEP);
   446 	switch (icp->conn_state) {
   429 
   447 	case ISCSI_CONN_STATE_FREE:
   430 	cn->ct_ic = ic;
   448 		status = iscsi_conn_state_free(icp, event);
   431 	cn->ct_icn = icn;
       
   432 	cn->ct_data = data;
       
   433 
       
   434 	idm_conn_hold(ic);
       
   435 
       
   436 	if (ddi_taskq_dispatch(icp->conn_cn_taskq,
       
   437 	    iscsi_client_notify_task, cn, DDI_SLEEP) != DDI_SUCCESS) {
       
   438 		idm_conn_rele(ic);
       
   439 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
       
   440 		    "unable to schedule notify task", icp->conn_oid);
       
   441 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
       
   442 		mutex_enter(&isp->sess_state_mutex);
       
   443 		iscsi_sess_state_machine(isp,
       
   444 		    ISCSI_SESS_EVENT_N6);
       
   445 		mutex_exit(&isp->sess_state_mutex);
       
   446 	}
       
   447 
       
   448 	return (IDM_STATUS_SUCCESS);
       
   449 }
       
   450 
       
   451 static void
       
   452 iscsi_client_notify_task(void *cn_task_void)
       
   453 {
       
   454 	iscsi_cn_task_t		*cn_task = cn_task_void;
       
   455 	iscsi_conn_t		*icp;
       
   456 	iscsi_sess_t		*isp;
       
   457 	idm_conn_t		*ic;
       
   458 	idm_client_notify_t	icn;
       
   459 	uintptr_t		data;
       
   460 	idm_ffp_disable_t	disable_type;
       
   461 	boolean_t		in_login;
       
   462 
       
   463 	ic = cn_task->ct_ic;
       
   464 	icn = cn_task->ct_icn;
       
   465 	data = cn_task->ct_data;
       
   466 
       
   467 	icp = ic->ic_handle;
       
   468 	ASSERT(icp != NULL);
       
   469 	isp = icp->conn_sess;
       
   470 
       
   471 	switch (icn) {
       
   472 	case CN_FFP_ENABLED:
       
   473 		mutex_enter(&icp->conn_state_mutex);
       
   474 		icp->conn_async_logout = B_FALSE;
       
   475 		icp->conn_state_ffp = B_TRUE;
       
   476 		cv_broadcast(&icp->conn_state_change);
       
   477 		mutex_exit(&icp->conn_state_mutex);
       
   478 
       
   479 		/*
       
   480 		 * This logic assumes that the IDM login-snooping code
       
   481 		 * and the initiator login code will agree on whether
       
   482 		 * the connection is in FFP.  The reason we do this
       
   483 		 * is that we don't want to process CN_FFP_DISABLED until
       
   484 		 * CN_FFP_ENABLED has been full handled.
       
   485 		 */
       
   486 		mutex_enter(&icp->conn_login_mutex);
       
   487 		while (icp->conn_login_state != LOGIN_FFP) {
       
   488 			cv_wait(&icp->conn_login_cv, &icp->conn_login_mutex);
       
   489 		}
       
   490 		mutex_exit(&icp->conn_login_mutex);
   449 		break;
   491 		break;
   450 	case ISCSI_CONN_STATE_IN_LOGIN:
   492 	case CN_FFP_DISABLED:
   451 		iscsi_conn_state_in_login(icp, event);
   493 		disable_type = (idm_ffp_disable_t)data;
       
   494 
       
   495 		mutex_enter(&icp->conn_state_mutex);
       
   496 		switch (disable_type) {
       
   497 		case FD_SESS_LOGOUT:
       
   498 		case FD_CONN_LOGOUT:
       
   499 			if (icp->conn_async_logout) {
       
   500 				/*
       
   501 				 * Our logout was in response to an
       
   502 				 * async logout request so treat this
       
   503 				 * like a connection failure (we will
       
   504 				 * try to re-establish the connection)
       
   505 				 */
       
   506 				iscsi_conn_update_state_locked(icp,
       
   507 				    ISCSI_CONN_STATE_FAILED);
       
   508 			} else {
       
   509 				/*
       
   510 				 * Logout due to to user config change,
       
   511 				 * we will not try to re-establish
       
   512 				 * the connection.
       
   513 				 */
       
   514 				iscsi_conn_update_state_locked(icp,
       
   515 				    ISCSI_CONN_STATE_IN_LOGOUT);
       
   516 				/*
       
   517 				 * Hold off generating the ISCSI_SESS_EVENT_N3
       
   518 				 * event until we get the CN_CONNECT_LOST
       
   519 				 * notification.  This matches the pre-IDM
       
   520 				 * implementation better.
       
   521 				 */
       
   522 			}
       
   523 			break;
       
   524 
       
   525 		case FD_CONN_FAIL:
       
   526 		default:
       
   527 			iscsi_conn_update_state_locked(icp,
       
   528 			    ISCSI_CONN_STATE_FAILED);
       
   529 			break;
       
   530 		}
       
   531 
       
   532 		icp->conn_state_ffp = B_FALSE;
       
   533 		cv_broadcast(&icp->conn_state_change);
       
   534 		mutex_exit(&icp->conn_state_mutex);
       
   535 
   452 		break;
   536 		break;
   453 	case ISCSI_CONN_STATE_LOGGED_IN:
   537 	case CN_CONNECT_LOST:
   454 		iscsi_conn_state_logged_in(icp, event);
   538 		/*
   455 		break;
   539 		 * We only care about CN_CONNECT_LOST if we've logged in.  IDM
   456 	case ISCSI_CONN_STATE_IN_LOGOUT:
   540 		 * sends a flag as the data payload to indicate whether we
   457 		iscsi_conn_state_in_logout(icp, event);
   541 		 * were trying to login.  The CN_LOGIN_FAIL notification
   458 		break;
   542 		 * gives us what we need to know for login failures and
   459 	case ISCSI_CONN_STATE_FAILED:
   543 		 * otherwise we will need to keep a bunch of state to know
   460 		iscsi_conn_state_failed(icp, event);
   544 		 * what CN_CONNECT_LOST means to us.
   461 		break;
   545 		 */
   462 	case ISCSI_CONN_STATE_POLLING:
   546 		in_login = (boolean_t)data;
   463 		iscsi_conn_state_polling(icp, event);
   547 		if (in_login) {
       
   548 			mutex_enter(&icp->conn_state_mutex);
       
   549 
       
   550 			icp->conn_state_idm_connected = B_FALSE;
       
   551 			cv_broadcast(&icp->conn_state_change);
       
   552 			mutex_exit(&icp->conn_state_mutex);
       
   553 
       
   554 			/* Release connect hold from CN_READY_FOR_LOGIN */
       
   555 			idm_conn_rele(ic);
       
   556 			break;
       
   557 		}
       
   558 
       
   559 		/* Any remaining commands are never going to finish */
       
   560 		iscsi_conn_flush_active_cmds(icp);
       
   561 
       
   562 		/*
       
   563 		 * The connection is no longer active so cleanup any
       
   564 		 * references to the connection and release any holds so
       
   565 		 * that IDM can finish cleanup.
       
   566 		 */
       
   567 		mutex_enter(&icp->conn_state_mutex);
       
   568 		if (icp->conn_state != ISCSI_CONN_STATE_FAILED) {
       
   569 
       
   570 			mutex_enter(&isp->sess_state_mutex);
       
   571 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N3);
       
   572 			mutex_exit(&isp->sess_state_mutex);
       
   573 
       
   574 			iscsi_conn_update_state_locked(icp,
       
   575 			    ISCSI_CONN_STATE_FREE);
       
   576 		} else {
       
   577 
       
   578 			mutex_enter(&isp->sess_state_mutex);
       
   579 			iscsi_sess_state_machine(isp,
       
   580 			    ISCSI_SESS_EVENT_N5);
       
   581 			mutex_exit(&isp->sess_state_mutex);
       
   582 
       
   583 			/*
       
   584 			 * If session type is NORMAL, try to reestablish the
       
   585 			 * connection.
       
   586 			 */
       
   587 			if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
       
   588 				iscsi_conn_retry(isp, icp);
       
   589 			} else {
       
   590 
       
   591 				mutex_enter(&isp->sess_state_mutex);
       
   592 				iscsi_sess_state_machine(isp,
       
   593 				    ISCSI_SESS_EVENT_N6);
       
   594 				mutex_exit(&isp->sess_state_mutex);
       
   595 
       
   596 				iscsi_conn_update_state_locked(icp,
       
   597 				    ISCSI_CONN_STATE_FREE);
       
   598 			}
       
   599 		}
       
   600 
       
   601 		(void) iscsi_thread_stop(icp->conn_tx_thread);
       
   602 
       
   603 		icp->conn_state_idm_connected = B_FALSE;
       
   604 		cv_broadcast(&icp->conn_state_change);
       
   605 		mutex_exit(&icp->conn_state_mutex);
       
   606 
       
   607 		/* Release connect hold from CN_READY_FOR_LOGIN */
       
   608 		idm_conn_rele(ic);
   464 		break;
   609 		break;
   465 	default:
   610 	default:
   466 		ASSERT(FALSE);
   611 		ISCSI_CONN_LOG(CE_WARN,
   467 		status = ISCSI_STATUS_INTERNAL_ERROR;
   612 		    "iscsi_client_notify: unknown notification: "
   468 	}
   613 		    "%x: NOT IMPLEMENTED YET: icp: %p ic: %p ",
   469 
   614 		    icn, (void *)icp, (void *)ic);
   470 	cv_broadcast(&icp->conn_state_change);
   615 		break;
   471 	return (status);
   616 	}
   472 }
   617 	/* free the task notify structure we allocated in iscsi_client_notify */
   473 
   618 	kmem_free(cn_task, sizeof (*cn_task));
   474 
   619 
   475 /*
   620 	/* Release the hold we acquired in iscsi_client_notify */
   476  * iscsi_conn_state_str - converts state enum to a string
   621 	idm_conn_rele(ic);
   477  */
   622 }
   478 char *
       
   479 iscsi_conn_state_str(iscsi_conn_state_t state)
       
   480 {
       
   481 	switch (state) {
       
   482 	case ISCSI_CONN_STATE_FREE:
       
   483 		return ("free");
       
   484 	case ISCSI_CONN_STATE_IN_LOGIN:
       
   485 		return ("in_login");
       
   486 	case ISCSI_CONN_STATE_LOGGED_IN:
       
   487 		return ("logged_in");
       
   488 	case ISCSI_CONN_STATE_IN_LOGOUT:
       
   489 		return ("in_logout");
       
   490 	case ISCSI_CONN_STATE_FAILED:
       
   491 		return ("failed");
       
   492 	case ISCSI_CONN_STATE_POLLING:
       
   493 		return ("polling");
       
   494 	default:
       
   495 		return ("unknown");
       
   496 	}
       
   497 }
       
   498 
       
   499 
   623 
   500 /*
   624 /*
   501  * iscsi_conn_sync_params - used to update connection parameters
   625  * iscsi_conn_sync_params - used to update connection parameters
   502  *
   626  *
   503  * Used to update connection parameters with current configured
   627  * Used to update connection parameters with current configured
   560 				    " boot session as MPxIO is disabled");
   684 				    " boot session as MPxIO is disabled");
   561 			}
   685 			}
   562 			break;
   686 			break;
   563 		}
   687 		}
   564 		if (pp.p_bitmap & (1 << param_id)) {
   688 		if (pp.p_bitmap & (1 << param_id)) {
   565 				switch (param_id) {
   689 
       
   690 			switch (param_id) {
   566 			/*
   691 			/*
   567 			 * Boolean parameters
   692 			 * Boolean parameters
   568 			 */
   693 			 */
   569 			case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
   694 			case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER:
   570 				icp->conn_params.data_pdu_in_order =
   695 				icp->conn_params.data_pdu_in_order =
   760  * +--------------------------------------------------------------------+
   885  * +--------------------------------------------------------------------+
   761  * | Internal Connection Interfaces					|
   886  * | Internal Connection Interfaces					|
   762  * +--------------------------------------------------------------------+
   887  * +--------------------------------------------------------------------+
   763  */
   888  */
   764 
   889 
   765 
       
   766 /*
       
   767  * iscsi_conn_state_free -
       
   768  *
       
   769  * S1: FREE - State on instantiation, or after successful
       
   770  * connection closure.
       
   771  */
       
   772 static iscsi_status_t
       
   773 iscsi_conn_state_free(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
   774 {
       
   775 	iscsi_sess_t		*isp;
       
   776 	iscsi_hba_t		*ihp;
       
   777 	iscsi_task_t		*itp;
       
   778 	iscsi_status_t		status = ISCSI_STATUS_SUCCESS;
       
   779 
       
   780 	ASSERT(icp != NULL);
       
   781 	isp = icp->conn_sess;
       
   782 	ASSERT(isp != NULL);
       
   783 	ihp = isp->sess_hba;
       
   784 	ASSERT(ihp != NULL);
       
   785 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE);
       
   786 
       
   787 	/* switch on event change */
       
   788 	switch (event) {
       
   789 	/* -T1: Transport connection request was request */
       
   790 	case ISCSI_CONN_EVENT_T1:
       
   791 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGIN;
       
   792 
       
   793 		/*
       
   794 		 * Release the connection state mutex cross the
       
   795 		 * the dispatch of the login task.  The login task
       
   796 		 * will reacquire the connection state mutex when
       
   797 		 * it pushes the connection successful or failed.
       
   798 		 */
       
   799 		mutex_exit(&icp->conn_state_mutex);
       
   800 
       
   801 		/* start login */
       
   802 		itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP);
       
   803 		itp->t_arg = icp;
       
   804 		itp->t_blocking = B_TRUE;
       
   805 
       
   806 		/*
       
   807 		 * Sync base connection information before login
       
   808 		 * A login redirection might have shifted the
       
   809 		 * current information from the base.
       
   810 		 */
       
   811 		bcopy(&icp->conn_base_addr, &icp->conn_curr_addr,
       
   812 		    sizeof (icp->conn_curr_addr));
       
   813 
       
   814 		status = iscsi_login_start(itp);
       
   815 		kmem_free(itp, sizeof (iscsi_task_t));
       
   816 
       
   817 		mutex_enter(&icp->conn_state_mutex);
       
   818 		break;
       
   819 
       
   820 	/* All other events are invalid for this state */
       
   821 	default:
       
   822 		ASSERT(FALSE);
       
   823 		status = ISCSI_STATUS_INTERNAL_ERROR;
       
   824 	}
       
   825 	return (status);
       
   826 }
       
   827 
       
   828 /*
       
   829  * iscsi_conn_state_in_login - During this state we are trying to
       
   830  * connect the TCP connection and make a successful login to the
       
   831  * target.  To complete this we have a task queue item that is
       
   832  * trying this processing at this point in time.  When the task
       
   833  * queue completed its processing it will issue either a T5/7
       
   834  * event.
       
   835  */
       
   836 static void
       
   837 iscsi_conn_state_in_login(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
   838 {
       
   839 	iscsi_sess_t	*isp;
       
   840 
       
   841 	ASSERT(icp != NULL);
       
   842 	isp = icp->conn_sess;
       
   843 	ASSERT(isp != NULL);
       
   844 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN);
       
   845 
       
   846 	/* switch on event change */
       
   847 	switch (event) {
       
   848 	/*
       
   849 	 * -T5: The final iSCSI Login response with a Status-Class of zero
       
   850 	 *	was received.
       
   851 	 */
       
   852 	case ISCSI_CONN_EVENT_T5:
       
   853 		iscsi_conn_logged_in(isp, icp);
       
   854 		break;
       
   855 
       
   856 	/*
       
   857 	 * -T30: One of the following event caused the transition:
       
   858 	 *	- Thefinal iSCSI Login response was received with a non-zero
       
   859 	 *	  Status-Class.
       
   860 	 */
       
   861 	case ISCSI_CONN_EVENT_T30:
       
   862 		/* FALLTHRU */
       
   863 
       
   864 	/*
       
   865 	 * -T7: One of the following events caused the transition:
       
   866 	 *	- Login timed out.
       
   867 	 *	- A transport disconnect indication was received.
       
   868 	 *	- A transport reset was received.
       
   869 	 *	- An internal event indicating a transport timeout was
       
   870 	 *	  received.
       
   871 	 *	- An internal event of receiving a Logout repsonse (success)
       
   872 	 *	  on another connection for a "close the session" Logout
       
   873 	 *	  request was received.
       
   874 	 *	* In all these cases, the transport connection is closed.
       
   875 	 */
       
   876 	case ISCSI_CONN_EVENT_T7:
       
   877 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
   878 		break;
       
   879 
       
   880 	/* All other events are invalid for this state */
       
   881 	default:
       
   882 		ASSERT(FALSE);
       
   883 	}
       
   884 }
       
   885 
       
   886 
       
   887 /*
       
   888  * iscsi_conn_state_logged_in -
       
   889  *
       
   890  */
       
   891 static void
       
   892 iscsi_conn_state_logged_in(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
   893 {
       
   894 	iscsi_sess_t		*isp;
       
   895 	iscsi_hba_t		*ihp;
       
   896 
       
   897 	ASSERT(icp != NULL);
       
   898 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN);
       
   899 	isp = icp->conn_sess;
       
   900 	ASSERT(isp != NULL);
       
   901 	ihp = isp->sess_hba;
       
   902 	ASSERT(ihp != NULL);
       
   903 
       
   904 	/* switch on event change */
       
   905 	switch (event) {
       
   906 	/*
       
   907 	 * -T8: An internal event of receiving a Logout response (success)
       
   908 	 *	on another connection for a "close the session" Logout request
       
   909 	 *	was received, thus closing this connection requiring no further
       
   910 	 *	cleanup.
       
   911 	 */
       
   912 	case ISCSI_CONN_EVENT_T8:
       
   913 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
   914 
       
   915 		/* stop tx thread */
       
   916 		(void) iscsi_thread_stop(icp->conn_tx_thread);
       
   917 
       
   918 		/* Disconnect connection */
       
   919 		iscsi_net->close(icp->conn_socket);
       
   920 
       
   921 		/* Notify session that a connection logged out */
       
   922 		mutex_enter(&isp->sess_state_mutex);
       
   923 		iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3);
       
   924 		mutex_exit(&isp->sess_state_mutex);
       
   925 		break;
       
   926 
       
   927 	/*
       
   928 	 * -T9: An internal event that indicates the readiness to start the
       
   929 	 *	Logout process was received, thus prompting an iSCSI Logout
       
   930 	 *	to be sent by the initiator.
       
   931 	 */
       
   932 	case ISCSI_CONN_EVENT_T9:
       
   933 		/* FALLTHRU */
       
   934 
       
   935 	/*
       
   936 	 * -T11: Aync PDU with AsyncEvent "Request Logout" was recevied
       
   937 	 */
       
   938 	case ISCSI_CONN_EVENT_T11:
       
   939 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT;
       
   940 
       
   941 		(void) iscsi_handle_logout(icp);
       
   942 		break;
       
   943 
       
   944 	/*
       
   945 	 * -T14: One or more of the following events case this transition:
       
   946 	 *	- Header Digest Error
       
   947 	 *	- Protocol Error
       
   948 	 */
       
   949 	case ISCSI_CONN_EVENT_T14:
       
   950 		icp->conn_state = ISCSI_CONN_STATE_FAILED;
       
   951 
       
   952 		/* stop tx thread */
       
   953 		(void) iscsi_thread_stop(icp->conn_tx_thread);
       
   954 
       
   955 		/*
       
   956 		 * Error Recovery Level 0 states we should drop
       
   957 		 * the connection here.  Then we will fall through
       
   958 		 * and treat this event like a T15.
       
   959 		 */
       
   960 		iscsi_net->close(icp->conn_socket);
       
   961 
       
   962 		/* FALLTHRU */
       
   963 
       
   964 	/*
       
   965 	 * -T15: One or more of the following events caused this transition
       
   966 	 *	- Internal event that indicates a transport connection timeout
       
   967 	 *	  was received thus prompting transport RESET or transport
       
   968 	 *	  connection closure.
       
   969 	 *	- A transport RESET
       
   970 	 *	- A transport disconnect indication.
       
   971 	 *	- Async PDU with AsyncEvent "Drop connection" (for this CID)
       
   972 	 *	- Async PDU with AsyncEvent "Drop all connections"
       
   973 	 */
       
   974 	case ISCSI_CONN_EVENT_T15:
       
   975 		icp->conn_state = ISCSI_CONN_STATE_FAILED;
       
   976 
       
   977 		/* stop tx thread, no-op if already done for T14 */
       
   978 		(void) iscsi_thread_stop(icp->conn_tx_thread);
       
   979 
       
   980 		iscsi_conn_flush_active_cmds(icp);
       
   981 
       
   982 		mutex_enter(&isp->sess_state_mutex);
       
   983 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N5);
       
   984 		mutex_exit(&isp->sess_state_mutex);
       
   985 
       
   986 		/*
       
   987 		 * If session type is NORMAL, create a new login task
       
   988 		 * to get this connection reestablished.
       
   989 		 */
       
   990 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
       
   991 			iscsi_conn_retry(isp, icp);
       
   992 		} else {
       
   993 			icp->conn_state = ISCSI_CONN_STATE_FREE;
       
   994 			mutex_enter(&isp->sess_state_mutex);
       
   995 			iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
       
   996 			mutex_exit(&isp->sess_state_mutex);
       
   997 		}
       
   998 		break;
       
   999 
       
  1000 	/* All other events are invalid for this state */
       
  1001 	default:
       
  1002 		ASSERT(FALSE);
       
  1003 	}
       
  1004 }
       
  1005 
       
  1006 
       
  1007 /*
       
  1008  * iscsi_conn_state_in_logout -
       
  1009  *
       
  1010  */
       
  1011 static void
       
  1012 iscsi_conn_state_in_logout(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
  1013 {
       
  1014 	iscsi_sess_t	*isp	= NULL;
       
  1015 
       
  1016 	ASSERT(icp != NULL);
       
  1017 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGOUT);
       
  1018 	isp = icp->conn_sess;
       
  1019 	ASSERT(isp != NULL);
       
  1020 
       
  1021 	/* switch on event change */
       
  1022 	switch (event) {
       
  1023 	/*
       
  1024 	 * -T11: Async PDU with AsyncEvent "Request Logout" was received again
       
  1025 	 */
       
  1026 	case ISCSI_CONN_EVENT_T11:
       
  1027 		icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT;
       
  1028 
       
  1029 		/* Already in LOGOUT ignore the request */
       
  1030 		break;
       
  1031 
       
  1032 	/*
       
  1033 	 * -T17: One or more of the following events caused this transition:
       
  1034 	 *	- Logout response, (failure i.e., a non-zero status) was
       
  1035 	 *	received, or logout timed out.
       
  1036 	 *	- Any of the events specified for T15
       
  1037 	 *
       
  1038 	 * -T14: One or more of the following events case this transition:
       
  1039 	 *	- Header Digest Error
       
  1040 	 *	- Protocol Error
       
  1041 	 *
       
  1042 	 * -T15: One or more of the following events caused this transition
       
  1043 	 *	- Internal event that indicates a transport connection timeout
       
  1044 	 *	  was received thus prompting transport RESET or transport
       
  1045 	 *	  connection closure.
       
  1046 	 *	- A transport RESET
       
  1047 	 *	- A transport disconnect indication.
       
  1048 	 *	- Async PDU with AsyncEvent "Drop connection" (for this CID)
       
  1049 	 *	- Async PDU with AsyncEvent "Drop all connections"
       
  1050 	 */
       
  1051 	case ISCSI_CONN_EVENT_T17:
       
  1052 	case ISCSI_CONN_EVENT_T14:
       
  1053 	case ISCSI_CONN_EVENT_T15:
       
  1054 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
  1055 
       
  1056 		/* stop tx thread */
       
  1057 		(void) iscsi_thread_stop(icp->conn_tx_thread);
       
  1058 
       
  1059 		/* Disconnect Connection */
       
  1060 		iscsi_net->close(icp->conn_socket);
       
  1061 
       
  1062 		iscsi_conn_flush_active_cmds(icp);
       
  1063 
       
  1064 		/* Notify session of a failed logout */
       
  1065 		mutex_enter(&isp->sess_state_mutex);
       
  1066 		iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3);
       
  1067 		mutex_exit(&isp->sess_state_mutex);
       
  1068 		break;
       
  1069 
       
  1070 	/* All other events are invalid for this state */
       
  1071 	default:
       
  1072 		ASSERT(FALSE);
       
  1073 	}
       
  1074 }
       
  1075 
       
  1076 
       
  1077 /*
       
  1078  * iscsi_conn_state_failed -
       
  1079  *
       
  1080  */
       
  1081 static void
       
  1082 iscsi_conn_state_failed(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
  1083 {
       
  1084 	iscsi_sess_t	*isp;
       
  1085 
       
  1086 	ASSERT(icp != NULL);
       
  1087 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_FAILED);
       
  1088 	isp = icp->conn_sess;
       
  1089 	ASSERT(isp != NULL);
       
  1090 
       
  1091 	/* switch on event change */
       
  1092 	switch (event) {
       
  1093 
       
  1094 	/*
       
  1095 	 * -T5: The final iSCSI Login response with a Status-Class of zero
       
  1096 	 *	was received.
       
  1097 	 */
       
  1098 	case ISCSI_CONN_EVENT_T5:
       
  1099 		iscsi_conn_logged_in(isp, icp);
       
  1100 		break;
       
  1101 
       
  1102 	/*
       
  1103 	 * -T30: One of the following event caused the transition:
       
  1104 	 *	- Thefinal iSCSI Login response was received with a non-zero
       
  1105 	 *	  Status-Class.
       
  1106 	 */
       
  1107 	case ISCSI_CONN_EVENT_T30:
       
  1108 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
  1109 
       
  1110 		mutex_enter(&isp->sess_state_mutex);
       
  1111 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
       
  1112 		mutex_exit(&isp->sess_state_mutex);
       
  1113 
       
  1114 		break;
       
  1115 
       
  1116 	/*
       
  1117 	 * -T7: One of the following events caused the transition:
       
  1118 	 *	- Login timed out.
       
  1119 	 *	- A transport disconnect indication was received.
       
  1120 	 *	- A transport reset was received.
       
  1121 	 *	- An internal event indicating a transport timeout was
       
  1122 	 *	  received.
       
  1123 	 *	- An internal event of receiving a Logout repsonse (success)
       
  1124 	 *	  on another connection for a "close the session" Logout
       
  1125 	 *	  request was received.
       
  1126 	 *	* In all these cases, the transport connection is closed.
       
  1127 	 */
       
  1128 	case ISCSI_CONN_EVENT_T7:
       
  1129 		icp->conn_state = ISCSI_CONN_STATE_POLLING;
       
  1130 
       
  1131 		mutex_enter(&isp->sess_state_mutex);
       
  1132 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
       
  1133 		mutex_exit(&isp->sess_state_mutex);
       
  1134 
       
  1135 		iscsi_conn_retry(isp, icp);
       
  1136 		break;
       
  1137 
       
  1138 	/* There are no valid transition out of this state. */
       
  1139 	default:
       
  1140 		ASSERT(FALSE);
       
  1141 	}
       
  1142 }
       
  1143 
       
  1144 /*
       
  1145  * iscsi_conn_state_polling -
       
  1146  *
       
  1147  * S6: POLLING - State on instantiation, or after successful
       
  1148  * connection closure.
       
  1149  */
       
  1150 static void
       
  1151 iscsi_conn_state_polling(iscsi_conn_t *icp, iscsi_conn_event_t event)
       
  1152 {
       
  1153 	iscsi_sess_t *isp = NULL;
       
  1154 
       
  1155 	ASSERT(icp != NULL);
       
  1156 	ASSERT(icp->conn_state == ISCSI_CONN_STATE_POLLING);
       
  1157 	isp = icp->conn_sess;
       
  1158 	ASSERT(isp != NULL);
       
  1159 
       
  1160 	/* switch on event change */
       
  1161 	switch (event) {
       
  1162 	/*
       
  1163 	 * -T5: The final iSCSI Login response with a Status-Class of zero
       
  1164 	 *	was received.
       
  1165 	 */
       
  1166 	case ISCSI_CONN_EVENT_T5:
       
  1167 		iscsi_conn_logged_in(isp, icp);
       
  1168 		break;
       
  1169 
       
  1170 	/*
       
  1171 	 * -T30: One of the following event caused the transition:
       
  1172 	 *	- Thefinal iSCSI Login response was received with a non-zero
       
  1173 	 *	  Status-Class.
       
  1174 	 */
       
  1175 	case ISCSI_CONN_EVENT_T30:
       
  1176 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
  1177 
       
  1178 		mutex_enter(&isp->sess_state_mutex);
       
  1179 		iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6);
       
  1180 		mutex_exit(&isp->sess_state_mutex);
       
  1181 
       
  1182 		break;
       
  1183 
       
  1184 	/*
       
  1185 	 * -T7: One of the following events caused the transition:
       
  1186 	 *	- Login timed out.
       
  1187 	 *	- A transport disconnect indication was received.
       
  1188 	 *	- A transport reset was received.
       
  1189 	 *	- An internal event indicating a transport timeout was
       
  1190 	 *	  received.
       
  1191 	 *	- An internal event of receiving a Logout repsonse (success)
       
  1192 	 *	  on another connection for a "close the session" Logout
       
  1193 	 *	  request was received.
       
  1194 	 *	* In all these cases, the transport connection is closed.
       
  1195 	 */
       
  1196 	case ISCSI_CONN_EVENT_T7:
       
  1197 		/*
       
  1198 		 * If session type is NORMAL, create a new login task
       
  1199 		 * to get this connection reestablished.
       
  1200 		 */
       
  1201 		if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) {
       
  1202 			iscsi_conn_retry(isp, icp);
       
  1203 		} else {
       
  1204 			icp->conn_state = ISCSI_CONN_STATE_FREE;
       
  1205 		}
       
  1206 		break;
       
  1207 
       
  1208 	/* All other events are invalid for this state */
       
  1209 	default:
       
  1210 		ASSERT(FALSE);
       
  1211 	}
       
  1212 }
       
  1213 
       
  1214 /*
       
  1215  * iscsi_conn_event_str - converts event enum to a string
       
  1216  */
       
  1217 static char *
       
  1218 iscsi_conn_event_str(iscsi_conn_event_t event)
       
  1219 {
       
  1220 	switch (event) {
       
  1221 	case ISCSI_CONN_EVENT_T1:
       
  1222 		return ("T1");
       
  1223 	case ISCSI_CONN_EVENT_T5:
       
  1224 		return ("T5");
       
  1225 	case ISCSI_CONN_EVENT_T7:
       
  1226 		return ("T7");
       
  1227 	case ISCSI_CONN_EVENT_T8:
       
  1228 		return ("T8");
       
  1229 	case ISCSI_CONN_EVENT_T9:
       
  1230 		return ("T9");
       
  1231 	case ISCSI_CONN_EVENT_T11:
       
  1232 		return ("T11");
       
  1233 	case ISCSI_CONN_EVENT_T14:
       
  1234 		return ("T14");
       
  1235 	case ISCSI_CONN_EVENT_T15:
       
  1236 		return ("T15");
       
  1237 	case ISCSI_CONN_EVENT_T17:
       
  1238 		return ("T17");
       
  1239 	case ISCSI_CONN_EVENT_T30:
       
  1240 		return ("T30");
       
  1241 
       
  1242 	default:
       
  1243 		return ("unknown");
       
  1244 	}
       
  1245 }
       
  1246 
       
  1247 /*
   890 /*
  1248  * iscsi_conn_flush_active_cmds - flush all active icmdps
   891  * iscsi_conn_flush_active_cmds - flush all active icmdps
  1249  *	for a connection.
   892  *	for a connection.
  1250  */
   893  */
  1251 static void
   894 static void
  1271 		iscsi_cmd_state_machine(icmdp,
   914 		iscsi_cmd_state_machine(icmdp,
  1272 		    ISCSI_CMD_EVENT_E7, isp);
   915 		    ISCSI_CMD_EVENT_E7, isp);
  1273 		icmdp = icp->conn_queue_active.head;
   916 		icmdp = icp->conn_queue_active.head;
  1274 	}
   917 	}
  1275 
   918 
       
   919 	/* Wait for active queue to drain */
       
   920 	while (icp->conn_queue_active.count) {
       
   921 		mutex_exit(&icp->conn_queue_active.mutex);
       
   922 		delay(drv_usectohz(100000));
       
   923 		mutex_enter(&icp->conn_queue_active.mutex);
       
   924 	}
       
   925 
  1276 	if (lock_held == B_FALSE) {
   926 	if (lock_held == B_FALSE) {
  1277 		mutex_exit(&icp->conn_queue_active.mutex);
   927 		mutex_exit(&icp->conn_queue_active.mutex);
  1278 	}
   928 	}
  1279 }
   929 
  1280 
   930 	/* Wait for IDM abort queue to drain (if necessary) */
  1281 
   931 	mutex_enter(&icp->conn_queue_idm_aborting.mutex);
  1282 /*
   932 	while (icp->conn_queue_idm_aborting.count) {
  1283  * iscsi_conn_logged_in - connection has successfully logged in
   933 		mutex_exit(&icp->conn_queue_idm_aborting.mutex);
  1284  */
   934 		delay(drv_usectohz(100000));
  1285 static void
   935 		mutex_enter(&icp->conn_queue_idm_aborting.mutex);
  1286 iscsi_conn_logged_in(iscsi_sess_t *isp, iscsi_conn_t *icp)
   936 	}
  1287 {
   937 	mutex_exit(&icp->conn_queue_idm_aborting.mutex);
  1288 	ASSERT(isp != NULL);
       
  1289 	ASSERT(icp != NULL);
       
  1290 
       
  1291 	icp->conn_state = ISCSI_CONN_STATE_LOGGED_IN;
       
  1292 	/*
       
  1293 	 * We need to drop the connection state lock
       
  1294 	 * before updating the session state.  On update
       
  1295 	 * of the session state it will enumerate the
       
  1296 	 * target.  If we hold the lock during enumeration
       
  1297 	 * will block the watchdog thread from timing
       
  1298 	 * a scsi_pkt, if required.  This will lead to
       
  1299 	 * a possible hang condition.
       
  1300 	 *
       
  1301 	 * Also the lock is no longer needed once the
       
  1302 	 * connection state was updated.
       
  1303 	 */
       
  1304 	mutex_exit(&icp->conn_state_mutex);
       
  1305 
       
  1306 	/* startup threads */
       
  1307 	(void) iscsi_thread_start(icp->conn_rx_thread);
       
  1308 	(void) iscsi_thread_start(icp->conn_tx_thread);
       
  1309 
       
  1310 	/* Notify the session that a connection is logged in */
       
  1311 	mutex_enter(&isp->sess_state_mutex);
       
  1312 	iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N1);
       
  1313 	mutex_exit(&isp->sess_state_mutex);
       
  1314 
       
  1315 	mutex_enter(&icp->conn_state_mutex);
       
  1316 }
   938 }
  1317 
   939 
  1318 /*
   940 /*
  1319  * iscsi_conn_retry - retry connect/login
   941  * iscsi_conn_retry - retry connect/login
  1320  */
   942  */
  1321 static void
   943 void
  1322 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
   944 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp)
  1323 {
   945 {
  1324 	iscsi_task_t *itp;
   946 	iscsi_task_t *itp;
  1325 
   947 
  1326 	ASSERT(isp != NULL);
   948 	ASSERT(isp != NULL);
  1328 
   950 
  1329 	/* set login min/max time values */
   951 	/* set login min/max time values */
  1330 	iscsi_conn_set_login_min_max(icp,
   952 	iscsi_conn_set_login_min_max(icp,
  1331 	    ISCSI_CONN_DEFAULT_LOGIN_MIN,
   953 	    ISCSI_CONN_DEFAULT_LOGIN_MIN,
  1332 	    ISCSI_CONN_DEFAULT_LOGIN_MAX);
   954 	    ISCSI_CONN_DEFAULT_LOGIN_MAX);
       
   955 
       
   956 	ISCSI_CONN_LOG(CE_NOTE, "DEBUG: iscsi_conn_retry: icp: %p icp: %p ",
       
   957 	    (void *)icp,
       
   958 	    (void *)icp->conn_ic);
  1333 
   959 
  1334 	/*
   960 	/*
  1335 	 * Sync base connection information before login.
   961 	 * Sync base connection information before login.
  1336 	 * A login redirection might have shifted the
   962 	 * A login redirection might have shifted the
  1337 	 * current information from the base.
   963 	 * current information from the base.
  1345 	itp->t_blocking = B_FALSE;
   971 	itp->t_blocking = B_FALSE;
  1346 	if (ddi_taskq_dispatch(isp->sess_taskq,
   972 	if (ddi_taskq_dispatch(isp->sess_taskq,
  1347 	    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
   973 	    (void(*)())iscsi_login_start, itp, DDI_SLEEP) !=
  1348 	    DDI_SUCCESS) {
   974 	    DDI_SUCCESS) {
  1349 		kmem_free(itp, sizeof (iscsi_task_t));
   975 		kmem_free(itp, sizeof (iscsi_task_t));
  1350 		cmn_err(CE_WARN,
   976 		cmn_err(CE_WARN, "iscsi connection(%u) failure - "
  1351 		    "iscsi connection(%u) failure - "
   977 		    "unable to schedule login task", icp->conn_oid);
  1352 		    "unable to schedule login task",
   978 
  1353 		    icp->conn_oid);
   979 		iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE);
  1354 
       
  1355 		icp->conn_state = ISCSI_CONN_STATE_FREE;
       
  1356 		mutex_enter(&isp->sess_state_mutex);
   980 		mutex_enter(&isp->sess_state_mutex);
  1357 		iscsi_sess_state_machine(isp,
   981 		iscsi_sess_state_machine(isp,
  1358 		    ISCSI_SESS_EVENT_N6);
   982 		    ISCSI_SESS_EVENT_N6);
  1359 		mutex_exit(&isp->sess_state_mutex);
   983 		mutex_exit(&isp->sess_state_mutex);
  1360 	}
   984 	}
  1361 }
   985 }
       
   986 
       
   987 void
       
   988 iscsi_conn_update_state(iscsi_conn_t *icp, iscsi_conn_state_t
       
   989 			    next_state)
       
   990 {
       
   991 	mutex_enter(&icp->conn_state_mutex);
       
   992 	(void) iscsi_conn_update_state_locked(icp, next_state);
       
   993 	mutex_exit(&icp->conn_state_mutex);
       
   994 }
       
   995 
       
   996 void
       
   997 iscsi_conn_update_state_locked(iscsi_conn_t *icp,
       
   998 	    iscsi_conn_state_t next_state)
       
   999 {
       
  1000 	ASSERT(mutex_owned(&icp->conn_state_mutex));
       
  1001 	next_state = (next_state > ISCSI_CONN_STATE_MAX) ?
       
  1002 	    ISCSI_CONN_STATE_MAX : next_state;
       
  1003 	idm_sm_audit_state_change(&icp->conn_state_audit,
       
  1004 	    SAS_ISCSI_CONN, icp->conn_state, next_state);
       
  1005 	switch (next_state) {
       
  1006 	case ISCSI_CONN_STATE_FREE:
       
  1007 	case ISCSI_CONN_STATE_IN_LOGIN:
       
  1008 	case ISCSI_CONN_STATE_LOGGED_IN:
       
  1009 	case ISCSI_CONN_STATE_IN_LOGOUT:
       
  1010 	case ISCSI_CONN_STATE_FAILED:
       
  1011 	case ISCSI_CONN_STATE_POLLING:
       
  1012 		ISCSI_CONN_LOG(CE_NOTE,
       
  1013 		    "iscsi_conn_update_state conn %p %s(%d) -> %s(%d)",
       
  1014 		    (void *)icp,
       
  1015 		    iscsi_ics_name[icp->conn_state], icp->conn_state,
       
  1016 		    iscsi_ics_name[next_state], next_state);
       
  1017 		icp->conn_prev_state = icp->conn_state;
       
  1018 		icp->conn_state = next_state;
       
  1019 		cv_broadcast(&icp->conn_state_change);
       
  1020 		break;
       
  1021 	default:
       
  1022 		cmn_err(CE_WARN, "Update state found illegal state: %x "
       
  1023 		    "prev_state: %x", next_state, icp->conn_prev_state);
       
  1024 		ASSERT(0);
       
  1025 	}
       
  1026 }