xref: /onnv-gate/usr/src/uts/common/io/idm/idm.c (revision 9162:b011b0287065)
17978SPeter.Dunlap@Sun.COM /*
27978SPeter.Dunlap@Sun.COM  * CDDL HEADER START
37978SPeter.Dunlap@Sun.COM  *
47978SPeter.Dunlap@Sun.COM  * The contents of this file are subject to the terms of the
57978SPeter.Dunlap@Sun.COM  * Common Development and Distribution License (the "License").
67978SPeter.Dunlap@Sun.COM  * You may not use this file except in compliance with the License.
77978SPeter.Dunlap@Sun.COM  *
87978SPeter.Dunlap@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97978SPeter.Dunlap@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107978SPeter.Dunlap@Sun.COM  * See the License for the specific language governing permissions
117978SPeter.Dunlap@Sun.COM  * and limitations under the License.
127978SPeter.Dunlap@Sun.COM  *
137978SPeter.Dunlap@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147978SPeter.Dunlap@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157978SPeter.Dunlap@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167978SPeter.Dunlap@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177978SPeter.Dunlap@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187978SPeter.Dunlap@Sun.COM  *
197978SPeter.Dunlap@Sun.COM  * CDDL HEADER END
207978SPeter.Dunlap@Sun.COM  */
217978SPeter.Dunlap@Sun.COM /*
22*9162SPeter.Dunlap@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237978SPeter.Dunlap@Sun.COM  * Use is subject to license terms.
247978SPeter.Dunlap@Sun.COM  */
257978SPeter.Dunlap@Sun.COM 
267978SPeter.Dunlap@Sun.COM #include <sys/cpuvar.h>
277978SPeter.Dunlap@Sun.COM #include <sys/conf.h>
287978SPeter.Dunlap@Sun.COM #include <sys/file.h>
297978SPeter.Dunlap@Sun.COM #include <sys/ddi.h>
307978SPeter.Dunlap@Sun.COM #include <sys/sunddi.h>
317978SPeter.Dunlap@Sun.COM #include <sys/modctl.h>
327978SPeter.Dunlap@Sun.COM 
337978SPeter.Dunlap@Sun.COM #include <sys/socket.h>
347978SPeter.Dunlap@Sun.COM #include <sys/strsubr.h>
357978SPeter.Dunlap@Sun.COM #include <sys/sysmacros.h>
367978SPeter.Dunlap@Sun.COM 
377978SPeter.Dunlap@Sun.COM #include <sys/socketvar.h>
387978SPeter.Dunlap@Sun.COM #include <netinet/in.h>
397978SPeter.Dunlap@Sun.COM 
407978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h>
417978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_so.h>
427978SPeter.Dunlap@Sun.COM 
437978SPeter.Dunlap@Sun.COM #define	IDM_NAME_VERSION	"iSCSI Data Mover"
447978SPeter.Dunlap@Sun.COM 
457978SPeter.Dunlap@Sun.COM extern struct mod_ops mod_miscops;
467978SPeter.Dunlap@Sun.COM extern struct mod_ops mod_miscops;
477978SPeter.Dunlap@Sun.COM 
487978SPeter.Dunlap@Sun.COM static struct modlmisc modlmisc = {
497978SPeter.Dunlap@Sun.COM 	&mod_miscops,	/* Type of module */
507978SPeter.Dunlap@Sun.COM 	IDM_NAME_VERSION
517978SPeter.Dunlap@Sun.COM };
527978SPeter.Dunlap@Sun.COM 
537978SPeter.Dunlap@Sun.COM static struct modlinkage modlinkage = {
547978SPeter.Dunlap@Sun.COM 	MODREV_1, (void *)&modlmisc, NULL
557978SPeter.Dunlap@Sun.COM };
567978SPeter.Dunlap@Sun.COM 
577978SPeter.Dunlap@Sun.COM extern int idm_task_compare(const void *t1, const void *t2);
587978SPeter.Dunlap@Sun.COM extern void idm_wd_thread(void *arg);
597978SPeter.Dunlap@Sun.COM 
607978SPeter.Dunlap@Sun.COM static int _idm_init(void);
617978SPeter.Dunlap@Sun.COM static int _idm_fini(void);
627978SPeter.Dunlap@Sun.COM static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
637978SPeter.Dunlap@Sun.COM static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
647978SPeter.Dunlap@Sun.COM static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
657978SPeter.Dunlap@Sun.COM static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
667978SPeter.Dunlap@Sun.COM static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
677978SPeter.Dunlap@Sun.COM     idm_abort_type_t abort_type);
687978SPeter.Dunlap@Sun.COM static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
69*9162SPeter.Dunlap@Sun.COM static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
70*9162SPeter.Dunlap@Sun.COM     int sleepflag);
717978SPeter.Dunlap@Sun.COM 
727978SPeter.Dunlap@Sun.COM boolean_t idm_conn_logging = 0;
737978SPeter.Dunlap@Sun.COM boolean_t idm_svc_logging = 0;
74*9162SPeter.Dunlap@Sun.COM #ifdef DEBUG
75*9162SPeter.Dunlap@Sun.COM boolean_t idm_pattern_checking = 1;
76*9162SPeter.Dunlap@Sun.COM #else
77*9162SPeter.Dunlap@Sun.COM boolean_t idm_pattern_checking = 0;
78*9162SPeter.Dunlap@Sun.COM #endif
797978SPeter.Dunlap@Sun.COM 
807978SPeter.Dunlap@Sun.COM /*
817978SPeter.Dunlap@Sun.COM  * Potential tuneable for the maximum number of tasks.  Default to
827978SPeter.Dunlap@Sun.COM  * IDM_TASKIDS_MAX
837978SPeter.Dunlap@Sun.COM  */
847978SPeter.Dunlap@Sun.COM 
857978SPeter.Dunlap@Sun.COM uint32_t	idm_max_taskids = IDM_TASKIDS_MAX;
867978SPeter.Dunlap@Sun.COM 
877978SPeter.Dunlap@Sun.COM /*
887978SPeter.Dunlap@Sun.COM  * Global list of transport handles
897978SPeter.Dunlap@Sun.COM  *   These are listed in preferential order, so we can simply take the
907978SPeter.Dunlap@Sun.COM  *   first "it_conn_is_capable" hit. Note also that the order maps to
917978SPeter.Dunlap@Sun.COM  *   the order of the idm_transport_type_t list.
927978SPeter.Dunlap@Sun.COM  */
937978SPeter.Dunlap@Sun.COM idm_transport_t idm_transport_list[] = {
947978SPeter.Dunlap@Sun.COM 
957978SPeter.Dunlap@Sun.COM 	/* iSER on InfiniBand transport handle */
967978SPeter.Dunlap@Sun.COM 	{IDM_TRANSPORT_TYPE_ISER,	/* type */
977978SPeter.Dunlap@Sun.COM 	"/devices/ib/iser@0:iser",	/* device path */
987978SPeter.Dunlap@Sun.COM 	NULL,				/* LDI handle */
997978SPeter.Dunlap@Sun.COM 	NULL,				/* transport ops */
1007978SPeter.Dunlap@Sun.COM 	NULL},				/* transport caps */
1017978SPeter.Dunlap@Sun.COM 
1027978SPeter.Dunlap@Sun.COM 	/* IDM native sockets transport handle */
1037978SPeter.Dunlap@Sun.COM 	{IDM_TRANSPORT_TYPE_SOCKETS,	/* type */
1047978SPeter.Dunlap@Sun.COM 	NULL,				/* device path */
1057978SPeter.Dunlap@Sun.COM 	NULL,				/* LDI handle */
1067978SPeter.Dunlap@Sun.COM 	NULL,				/* transport ops */
1077978SPeter.Dunlap@Sun.COM 	NULL}				/* transport caps */
1087978SPeter.Dunlap@Sun.COM 
1097978SPeter.Dunlap@Sun.COM };
1107978SPeter.Dunlap@Sun.COM 
1117978SPeter.Dunlap@Sun.COM int
1127978SPeter.Dunlap@Sun.COM _init(void)
1137978SPeter.Dunlap@Sun.COM {
1147978SPeter.Dunlap@Sun.COM 	int rc;
1157978SPeter.Dunlap@Sun.COM 
1167978SPeter.Dunlap@Sun.COM 	if ((rc = _idm_init()) != 0) {
1177978SPeter.Dunlap@Sun.COM 		return (rc);
1187978SPeter.Dunlap@Sun.COM 	}
1197978SPeter.Dunlap@Sun.COM 
1207978SPeter.Dunlap@Sun.COM 	return (mod_install(&modlinkage));
1217978SPeter.Dunlap@Sun.COM }
1227978SPeter.Dunlap@Sun.COM 
1237978SPeter.Dunlap@Sun.COM int
1247978SPeter.Dunlap@Sun.COM _fini(void)
1257978SPeter.Dunlap@Sun.COM {
1267978SPeter.Dunlap@Sun.COM 	int rc;
1277978SPeter.Dunlap@Sun.COM 
1287978SPeter.Dunlap@Sun.COM 	if ((rc = _idm_fini()) != 0) {
1297978SPeter.Dunlap@Sun.COM 		return (rc);
1307978SPeter.Dunlap@Sun.COM 	}
1317978SPeter.Dunlap@Sun.COM 
1327978SPeter.Dunlap@Sun.COM 	if ((rc = mod_remove(&modlinkage)) != 0) {
1337978SPeter.Dunlap@Sun.COM 		return (rc);
1347978SPeter.Dunlap@Sun.COM 	}
1357978SPeter.Dunlap@Sun.COM 
1367978SPeter.Dunlap@Sun.COM 	return (rc);
1377978SPeter.Dunlap@Sun.COM }
1387978SPeter.Dunlap@Sun.COM 
1397978SPeter.Dunlap@Sun.COM int
1407978SPeter.Dunlap@Sun.COM _info(struct modinfo *modinfop)
1417978SPeter.Dunlap@Sun.COM {
1427978SPeter.Dunlap@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1437978SPeter.Dunlap@Sun.COM }
1447978SPeter.Dunlap@Sun.COM 
1457978SPeter.Dunlap@Sun.COM /*
1467978SPeter.Dunlap@Sun.COM  * idm_transport_register()
1477978SPeter.Dunlap@Sun.COM  *
1487978SPeter.Dunlap@Sun.COM  * Provides a mechanism for an IDM transport driver to register its
1497978SPeter.Dunlap@Sun.COM  * transport ops and caps with the IDM kernel module. Invoked during
1507978SPeter.Dunlap@Sun.COM  * a transport driver's attach routine.
1517978SPeter.Dunlap@Sun.COM  */
1527978SPeter.Dunlap@Sun.COM idm_status_t
1537978SPeter.Dunlap@Sun.COM idm_transport_register(idm_transport_attr_t *attr)
1547978SPeter.Dunlap@Sun.COM {
1557978SPeter.Dunlap@Sun.COM 	ASSERT(attr->it_ops != NULL);
1567978SPeter.Dunlap@Sun.COM 	ASSERT(attr->it_caps != NULL);
1577978SPeter.Dunlap@Sun.COM 
1587978SPeter.Dunlap@Sun.COM 	switch (attr->type) {
1597978SPeter.Dunlap@Sun.COM 	/* All known non-native transports here; for now, iSER */
1607978SPeter.Dunlap@Sun.COM 	case IDM_TRANSPORT_TYPE_ISER:
1617978SPeter.Dunlap@Sun.COM 		idm_transport_list[attr->type].it_ops	= attr->it_ops;
1627978SPeter.Dunlap@Sun.COM 		idm_transport_list[attr->type].it_caps	= attr->it_caps;
1637978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
1647978SPeter.Dunlap@Sun.COM 
1657978SPeter.Dunlap@Sun.COM 	default:
1667978SPeter.Dunlap@Sun.COM 		cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
1677978SPeter.Dunlap@Sun.COM 		    "idm_transport_register", attr->type);
1687978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
1697978SPeter.Dunlap@Sun.COM 	}
1707978SPeter.Dunlap@Sun.COM }
1717978SPeter.Dunlap@Sun.COM 
1727978SPeter.Dunlap@Sun.COM /*
1737978SPeter.Dunlap@Sun.COM  * idm_ini_conn_create
1747978SPeter.Dunlap@Sun.COM  *
1757978SPeter.Dunlap@Sun.COM  * This function is invoked by the iSCSI layer to create a connection context.
1767978SPeter.Dunlap@Sun.COM  * This does not actually establish the socket connection.
1777978SPeter.Dunlap@Sun.COM  *
1787978SPeter.Dunlap@Sun.COM  * cr - Connection request parameters
1797978SPeter.Dunlap@Sun.COM  * new_con - Output parameter that contains the new request if successful
1807978SPeter.Dunlap@Sun.COM  *
1817978SPeter.Dunlap@Sun.COM  */
1827978SPeter.Dunlap@Sun.COM idm_status_t
1837978SPeter.Dunlap@Sun.COM idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
1847978SPeter.Dunlap@Sun.COM {
1857978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
1867978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
1877978SPeter.Dunlap@Sun.COM 	int			rc;
1887978SPeter.Dunlap@Sun.COM 
1897978SPeter.Dunlap@Sun.COM 	it = idm_transport_lookup(cr);
1907978SPeter.Dunlap@Sun.COM 
1917978SPeter.Dunlap@Sun.COM retry:
1927978SPeter.Dunlap@Sun.COM 	ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
1937978SPeter.Dunlap@Sun.COM 	    &cr->icr_conn_ops);
1947978SPeter.Dunlap@Sun.COM 
1957978SPeter.Dunlap@Sun.COM 	bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
1967978SPeter.Dunlap@Sun.COM 	    sizeof (cr->cr_ini_dst_addr));
1977978SPeter.Dunlap@Sun.COM 
1987978SPeter.Dunlap@Sun.COM 	/* create the transport-specific connection components */
1997978SPeter.Dunlap@Sun.COM 	rc = it->it_ops->it_ini_conn_create(cr, ic);
2007978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
2017978SPeter.Dunlap@Sun.COM 		/* cleanup the failed connection */
2027978SPeter.Dunlap@Sun.COM 		idm_conn_destroy_common(ic);
2037978SPeter.Dunlap@Sun.COM 		kmem_free(ic, sizeof (idm_conn_t));
2047978SPeter.Dunlap@Sun.COM 
2057978SPeter.Dunlap@Sun.COM 		/*
2067978SPeter.Dunlap@Sun.COM 		 * It is possible for an IB client to connect to
2077978SPeter.Dunlap@Sun.COM 		 * an ethernet-only client via an IB-eth gateway.
2087978SPeter.Dunlap@Sun.COM 		 * Therefore, if we are attempting to use iSER and
2097978SPeter.Dunlap@Sun.COM 		 * fail, retry with sockets before ultimately
2107978SPeter.Dunlap@Sun.COM 		 * failing the connection.
2117978SPeter.Dunlap@Sun.COM 		 */
2127978SPeter.Dunlap@Sun.COM 		if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
2137978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
2147978SPeter.Dunlap@Sun.COM 			goto retry;
2157978SPeter.Dunlap@Sun.COM 		}
2167978SPeter.Dunlap@Sun.COM 
2177978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
2187978SPeter.Dunlap@Sun.COM 	}
2197978SPeter.Dunlap@Sun.COM 
2207978SPeter.Dunlap@Sun.COM 	*new_con = ic;
2217978SPeter.Dunlap@Sun.COM 
2227978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
2237978SPeter.Dunlap@Sun.COM 	list_insert_tail(&idm.idm_ini_conn_list, ic);
2247978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
2257978SPeter.Dunlap@Sun.COM 
2267978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
2277978SPeter.Dunlap@Sun.COM }
2287978SPeter.Dunlap@Sun.COM 
2297978SPeter.Dunlap@Sun.COM /*
2307978SPeter.Dunlap@Sun.COM  * idm_ini_conn_destroy
2317978SPeter.Dunlap@Sun.COM  *
2327978SPeter.Dunlap@Sun.COM  * Releases any resources associated with the connection.  This is the
2337978SPeter.Dunlap@Sun.COM  * complement to idm_ini_conn_create.
2347978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
2357978SPeter.Dunlap@Sun.COM  *
2367978SPeter.Dunlap@Sun.COM  */
2377978SPeter.Dunlap@Sun.COM void
238*9162SPeter.Dunlap@Sun.COM idm_ini_conn_destroy_task(void *ic_void)
239*9162SPeter.Dunlap@Sun.COM {
240*9162SPeter.Dunlap@Sun.COM 	idm_conn_t *ic = ic_void;
241*9162SPeter.Dunlap@Sun.COM 
242*9162SPeter.Dunlap@Sun.COM 	ic->ic_transport_ops->it_ini_conn_destroy(ic);
243*9162SPeter.Dunlap@Sun.COM 	idm_conn_destroy_common(ic);
244*9162SPeter.Dunlap@Sun.COM }
245*9162SPeter.Dunlap@Sun.COM 
246*9162SPeter.Dunlap@Sun.COM void
2477978SPeter.Dunlap@Sun.COM idm_ini_conn_destroy(idm_conn_t *ic)
2487978SPeter.Dunlap@Sun.COM {
249*9162SPeter.Dunlap@Sun.COM 	/*
250*9162SPeter.Dunlap@Sun.COM 	 * It's reasonable for the initiator to call idm_ini_conn_destroy
251*9162SPeter.Dunlap@Sun.COM 	 * from within the context of the CN_CONNECT_DESTROY notification.
252*9162SPeter.Dunlap@Sun.COM 	 * That's a problem since we want to destroy the taskq for the
253*9162SPeter.Dunlap@Sun.COM 	 * state machine associated with the connection.  Remove the
254*9162SPeter.Dunlap@Sun.COM 	 * connection from the list right away then handle the remaining
255*9162SPeter.Dunlap@Sun.COM 	 * work via the idm_global_taskq.
256*9162SPeter.Dunlap@Sun.COM 	 */
2577978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
2587978SPeter.Dunlap@Sun.COM 	list_remove(&idm.idm_ini_conn_list, ic);
2597978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
2607978SPeter.Dunlap@Sun.COM 
261*9162SPeter.Dunlap@Sun.COM 	if (taskq_dispatch(idm.idm_global_taskq,
262*9162SPeter.Dunlap@Sun.COM 	    &idm_ini_conn_destroy_task, ic, TQ_SLEEP) == NULL) {
263*9162SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN,
264*9162SPeter.Dunlap@Sun.COM 		    "idm_ini_conn_destroy: Couldn't dispatch task");
265*9162SPeter.Dunlap@Sun.COM 	}
2667978SPeter.Dunlap@Sun.COM }
2677978SPeter.Dunlap@Sun.COM 
2687978SPeter.Dunlap@Sun.COM /*
2697978SPeter.Dunlap@Sun.COM  * idm_ini_conn_connect
2707978SPeter.Dunlap@Sun.COM  *
2717978SPeter.Dunlap@Sun.COM  * Establish connection to the remote system identified in idm_conn_t.
2727978SPeter.Dunlap@Sun.COM  * The connection parameters including the remote IP address were established
273*9162SPeter.Dunlap@Sun.COM  * in the call to idm_ini_conn_create.  The IDM state machine will
274*9162SPeter.Dunlap@Sun.COM  * perform client notifications as necessary to prompt the initiator through
275*9162SPeter.Dunlap@Sun.COM  * the login process.  IDM also keeps a timer running so that if the login
276*9162SPeter.Dunlap@Sun.COM  * process doesn't complete in a timely manner it will fail.
2777978SPeter.Dunlap@Sun.COM  *
2787978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
2797978SPeter.Dunlap@Sun.COM  *
2807978SPeter.Dunlap@Sun.COM  * Returns success if the connection was established, otherwise some kind
2817978SPeter.Dunlap@Sun.COM  * of meaningful error code.
2827978SPeter.Dunlap@Sun.COM  *
283*9162SPeter.Dunlap@Sun.COM  * Upon return the login has either failed or is loggin in (ffp)
2847978SPeter.Dunlap@Sun.COM  */
2857978SPeter.Dunlap@Sun.COM idm_status_t
2867978SPeter.Dunlap@Sun.COM idm_ini_conn_connect(idm_conn_t *ic)
2877978SPeter.Dunlap@Sun.COM {
288*9162SPeter.Dunlap@Sun.COM 	idm_status_t	rc = IDM_STATUS_SUCCESS;
2897978SPeter.Dunlap@Sun.COM 
2907978SPeter.Dunlap@Sun.COM 	rc = idm_conn_sm_init(ic);
2917978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
2927978SPeter.Dunlap@Sun.COM 		return (ic->ic_conn_sm_status);
2937978SPeter.Dunlap@Sun.COM 	}
294*9162SPeter.Dunlap@Sun.COM 
295*9162SPeter.Dunlap@Sun.COM 	/* Hold connection until we return */
296*9162SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
297*9162SPeter.Dunlap@Sun.COM 
2987978SPeter.Dunlap@Sun.COM 	/* Kick state machine */
2997978SPeter.Dunlap@Sun.COM 	idm_conn_event(ic, CE_CONNECT_REQ, NULL);
3007978SPeter.Dunlap@Sun.COM 
3017978SPeter.Dunlap@Sun.COM 	/* Wait for login flag */
3027978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
3037978SPeter.Dunlap@Sun.COM 	while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
3047978SPeter.Dunlap@Sun.COM 	    !(ic->ic_state_flags & CF_ERROR)) {
3057978SPeter.Dunlap@Sun.COM 		cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
3067978SPeter.Dunlap@Sun.COM 	}
3077978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
3087978SPeter.Dunlap@Sun.COM 
3097978SPeter.Dunlap@Sun.COM 	if (ic->ic_state_flags & CF_ERROR) {
3107978SPeter.Dunlap@Sun.COM 		/* ic->ic_conn_sm_status will contains failure status */
311*9162SPeter.Dunlap@Sun.COM 		idm_conn_rele(ic);
3127978SPeter.Dunlap@Sun.COM 		return (ic->ic_conn_sm_status);
3137978SPeter.Dunlap@Sun.COM 	}
3147978SPeter.Dunlap@Sun.COM 
3157978SPeter.Dunlap@Sun.COM 	/* Ready to login */
3167978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
3177978SPeter.Dunlap@Sun.COM 	(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
3187978SPeter.Dunlap@Sun.COM 
319*9162SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
3207978SPeter.Dunlap@Sun.COM 
321*9162SPeter.Dunlap@Sun.COM 	return (rc);
3227978SPeter.Dunlap@Sun.COM }
3237978SPeter.Dunlap@Sun.COM 
3247978SPeter.Dunlap@Sun.COM /*
3257978SPeter.Dunlap@Sun.COM  * idm_ini_conn_disconnect
3267978SPeter.Dunlap@Sun.COM  *
3277978SPeter.Dunlap@Sun.COM  * Forces a connection (previously established using idm_ini_conn_connect)
3287978SPeter.Dunlap@Sun.COM  * to perform a controlled shutdown, cleaning up any outstanding requests.
3297978SPeter.Dunlap@Sun.COM  *
3307978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
3317978SPeter.Dunlap@Sun.COM  *
332*9162SPeter.Dunlap@Sun.COM  * This is asynchronous and will return before the connection is properly
333*9162SPeter.Dunlap@Sun.COM  * shutdown
3347978SPeter.Dunlap@Sun.COM  */
3357978SPeter.Dunlap@Sun.COM /* ARGSUSED */
3367978SPeter.Dunlap@Sun.COM void
3377978SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect(idm_conn_t *ic)
3387978SPeter.Dunlap@Sun.COM {
339*9162SPeter.Dunlap@Sun.COM 	idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
340*9162SPeter.Dunlap@Sun.COM }
3417978SPeter.Dunlap@Sun.COM 
342*9162SPeter.Dunlap@Sun.COM /*
343*9162SPeter.Dunlap@Sun.COM  * idm_ini_conn_disconnect_wait
344*9162SPeter.Dunlap@Sun.COM  *
345*9162SPeter.Dunlap@Sun.COM  * Forces a connection (previously established using idm_ini_conn_connect)
346*9162SPeter.Dunlap@Sun.COM  * to perform a controlled shutdown.  Blocks until the connection is
347*9162SPeter.Dunlap@Sun.COM  * disconnected.
348*9162SPeter.Dunlap@Sun.COM  *
349*9162SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
350*9162SPeter.Dunlap@Sun.COM  */
351*9162SPeter.Dunlap@Sun.COM /* ARGSUSED */
352*9162SPeter.Dunlap@Sun.COM void
353*9162SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect_sync(idm_conn_t *ic)
354*9162SPeter.Dunlap@Sun.COM {
355*9162SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
356*9162SPeter.Dunlap@Sun.COM 	if ((ic->ic_state != CS_S9_INIT_ERROR) &&
357*9162SPeter.Dunlap@Sun.COM 	    (ic->ic_state != CS_S11_COMPLETE)) {
358*9162SPeter.Dunlap@Sun.COM 		idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, NULL, CT_NONE);
359*9162SPeter.Dunlap@Sun.COM 		while ((ic->ic_state != CS_S9_INIT_ERROR) &&
360*9162SPeter.Dunlap@Sun.COM 		    (ic->ic_state != CS_S11_COMPLETE))
361*9162SPeter.Dunlap@Sun.COM 			cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
3627978SPeter.Dunlap@Sun.COM 	}
3637978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
3647978SPeter.Dunlap@Sun.COM }
3657978SPeter.Dunlap@Sun.COM 
3667978SPeter.Dunlap@Sun.COM /*
3677978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_create
3687978SPeter.Dunlap@Sun.COM  *
3697978SPeter.Dunlap@Sun.COM  * The target calls this service to obtain a service context for each available
3707978SPeter.Dunlap@Sun.COM  * transport, starting a service of each type related to the IP address and port
3717978SPeter.Dunlap@Sun.COM  * passed. The idm_svc_req_t contains the service parameters.
3727978SPeter.Dunlap@Sun.COM  */
3737978SPeter.Dunlap@Sun.COM idm_status_t
3747978SPeter.Dunlap@Sun.COM idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
3757978SPeter.Dunlap@Sun.COM {
3767978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
3777978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
3787978SPeter.Dunlap@Sun.COM 	idm_svc_t		*is;
3797978SPeter.Dunlap@Sun.COM 	int			rc;
3807978SPeter.Dunlap@Sun.COM 
3817978SPeter.Dunlap@Sun.COM 	*new_svc = NULL;
3827978SPeter.Dunlap@Sun.COM 	is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
3837978SPeter.Dunlap@Sun.COM 
3847978SPeter.Dunlap@Sun.COM 	/* Initialize transport-agnostic components of the service handle */
3857978SPeter.Dunlap@Sun.COM 	is->is_svc_req = *sr;
3867978SPeter.Dunlap@Sun.COM 	mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
3877978SPeter.Dunlap@Sun.COM 	cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
3887978SPeter.Dunlap@Sun.COM 	mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
3897978SPeter.Dunlap@Sun.COM 	cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
3907978SPeter.Dunlap@Sun.COM 	idm_refcnt_init(&is->is_refcnt, is);
3917978SPeter.Dunlap@Sun.COM 
3927978SPeter.Dunlap@Sun.COM 	/*
3937978SPeter.Dunlap@Sun.COM 	 * Make sure all available transports are setup.  We call this now
3947978SPeter.Dunlap@Sun.COM 	 * instead of at initialization time in case IB has become available
3957978SPeter.Dunlap@Sun.COM 	 * since we started (hotplug, etc).
3967978SPeter.Dunlap@Sun.COM 	 */
3977978SPeter.Dunlap@Sun.COM 	idm_transport_setup(sr->sr_li);
3987978SPeter.Dunlap@Sun.COM 
3997978SPeter.Dunlap@Sun.COM 	/*
4007978SPeter.Dunlap@Sun.COM 	 * Loop through the transports, configuring the transport-specific
4017978SPeter.Dunlap@Sun.COM 	 * components of each one.
4027978SPeter.Dunlap@Sun.COM 	 */
4037978SPeter.Dunlap@Sun.COM 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
4047978SPeter.Dunlap@Sun.COM 
4057978SPeter.Dunlap@Sun.COM 		it = &idm_transport_list[type];
4067978SPeter.Dunlap@Sun.COM 		/*
4077978SPeter.Dunlap@Sun.COM 		 * If it_ops is NULL then the transport is unconfigured
4087978SPeter.Dunlap@Sun.COM 		 * and we shouldn't try to start the service.
4097978SPeter.Dunlap@Sun.COM 		 */
4107978SPeter.Dunlap@Sun.COM 		if (it->it_ops == NULL) {
4117978SPeter.Dunlap@Sun.COM 			continue;
4127978SPeter.Dunlap@Sun.COM 		}
4137978SPeter.Dunlap@Sun.COM 
4147978SPeter.Dunlap@Sun.COM 		rc = it->it_ops->it_tgt_svc_create(sr, is);
4157978SPeter.Dunlap@Sun.COM 		if (rc != IDM_STATUS_SUCCESS) {
4167978SPeter.Dunlap@Sun.COM 			/* Teardown any configured services */
4177978SPeter.Dunlap@Sun.COM 			while (type--) {
4187978SPeter.Dunlap@Sun.COM 				it = &idm_transport_list[type];
4197978SPeter.Dunlap@Sun.COM 				if (it->it_ops == NULL) {
4207978SPeter.Dunlap@Sun.COM 					continue;
4217978SPeter.Dunlap@Sun.COM 				}
4227978SPeter.Dunlap@Sun.COM 				it->it_ops->it_tgt_svc_destroy(is);
4237978SPeter.Dunlap@Sun.COM 			}
4247978SPeter.Dunlap@Sun.COM 			/* Free the svc context and return */
4257978SPeter.Dunlap@Sun.COM 			kmem_free(is, sizeof (idm_svc_t));
4267978SPeter.Dunlap@Sun.COM 			return (rc);
4277978SPeter.Dunlap@Sun.COM 		}
4287978SPeter.Dunlap@Sun.COM 	}
4297978SPeter.Dunlap@Sun.COM 
4307978SPeter.Dunlap@Sun.COM 	*new_svc = is;
4317978SPeter.Dunlap@Sun.COM 
4327978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
4337978SPeter.Dunlap@Sun.COM 	list_insert_tail(&idm.idm_tgt_svc_list, is);
4347978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
4357978SPeter.Dunlap@Sun.COM 
4367978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
4377978SPeter.Dunlap@Sun.COM }
4387978SPeter.Dunlap@Sun.COM 
4397978SPeter.Dunlap@Sun.COM /*
4407978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_destroy
4417978SPeter.Dunlap@Sun.COM  *
4427978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
4437978SPeter.Dunlap@Sun.COM  *
4447978SPeter.Dunlap@Sun.COM  * Cleanup any resources associated with the idm_svc_t.
4457978SPeter.Dunlap@Sun.COM  */
4467978SPeter.Dunlap@Sun.COM void
4477978SPeter.Dunlap@Sun.COM idm_tgt_svc_destroy(idm_svc_t *is)
4487978SPeter.Dunlap@Sun.COM {
4497978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
4507978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
4517978SPeter.Dunlap@Sun.COM 
4527978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
4537978SPeter.Dunlap@Sun.COM 	/* remove this service from the global list */
4547978SPeter.Dunlap@Sun.COM 	list_remove(&idm.idm_tgt_svc_list, is);
4557978SPeter.Dunlap@Sun.COM 	/* wakeup any waiters for service change */
4567978SPeter.Dunlap@Sun.COM 	cv_broadcast(&idm.idm_tgt_svc_cv);
4577978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
4587978SPeter.Dunlap@Sun.COM 
4597978SPeter.Dunlap@Sun.COM 	/* teardown each transport-specific service */
4607978SPeter.Dunlap@Sun.COM 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
4617978SPeter.Dunlap@Sun.COM 		it = &idm_transport_list[type];
4627978SPeter.Dunlap@Sun.COM 		if (it->it_ops == NULL) {
4637978SPeter.Dunlap@Sun.COM 			continue;
4647978SPeter.Dunlap@Sun.COM 		}
4657978SPeter.Dunlap@Sun.COM 
4667978SPeter.Dunlap@Sun.COM 		it->it_ops->it_tgt_svc_destroy(is);
4677978SPeter.Dunlap@Sun.COM 	}
4687978SPeter.Dunlap@Sun.COM 
469*9162SPeter.Dunlap@Sun.COM 	/* tear down the svc resources */
470*9162SPeter.Dunlap@Sun.COM 	idm_refcnt_destroy(&is->is_refcnt);
471*9162SPeter.Dunlap@Sun.COM 	cv_destroy(&is->is_count_cv);
472*9162SPeter.Dunlap@Sun.COM 	mutex_destroy(&is->is_count_mutex);
473*9162SPeter.Dunlap@Sun.COM 	cv_destroy(&is->is_cv);
474*9162SPeter.Dunlap@Sun.COM 	mutex_destroy(&is->is_mutex);
475*9162SPeter.Dunlap@Sun.COM 
4767978SPeter.Dunlap@Sun.COM 	/* free the svc handle */
4777978SPeter.Dunlap@Sun.COM 	kmem_free(is, sizeof (idm_svc_t));
4787978SPeter.Dunlap@Sun.COM }
4797978SPeter.Dunlap@Sun.COM 
4807978SPeter.Dunlap@Sun.COM void
4817978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(idm_svc_t *is)
4827978SPeter.Dunlap@Sun.COM {
4837978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&is->is_refcnt);
4847978SPeter.Dunlap@Sun.COM }
4857978SPeter.Dunlap@Sun.COM 
4867978SPeter.Dunlap@Sun.COM void
4877978SPeter.Dunlap@Sun.COM idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
4887978SPeter.Dunlap@Sun.COM {
4897978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele_and_destroy(&is->is_refcnt,
4907978SPeter.Dunlap@Sun.COM 	    (idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
4917978SPeter.Dunlap@Sun.COM }
4927978SPeter.Dunlap@Sun.COM 
4937978SPeter.Dunlap@Sun.COM /*
4947978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_online
4957978SPeter.Dunlap@Sun.COM  *
4967978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
4977978SPeter.Dunlap@Sun.COM  *
4987978SPeter.Dunlap@Sun.COM  * Online each transport service, as we want this target to be accessible
4997978SPeter.Dunlap@Sun.COM  * via any configured transport.
5007978SPeter.Dunlap@Sun.COM  *
5017978SPeter.Dunlap@Sun.COM  * When the initiator establishes a new connection to the target, IDM will
5027978SPeter.Dunlap@Sun.COM  * call the "new connect" callback defined in the idm_svc_req_t structure
5037978SPeter.Dunlap@Sun.COM  * and it will pass an idm_conn_t structure representing that new connection.
5047978SPeter.Dunlap@Sun.COM  */
5057978SPeter.Dunlap@Sun.COM idm_status_t
5067978SPeter.Dunlap@Sun.COM idm_tgt_svc_online(idm_svc_t *is)
5077978SPeter.Dunlap@Sun.COM {
5087978SPeter.Dunlap@Sun.COM 
509*9162SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type, last_type;
5107978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
511*9162SPeter.Dunlap@Sun.COM 	int			rc = IDM_STATUS_SUCCESS;
5127978SPeter.Dunlap@Sun.COM 
5137978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
5147978SPeter.Dunlap@Sun.COM 	if (is->is_online == 0) {
515*9162SPeter.Dunlap@Sun.COM 		/* Walk through each of the transports and online them */
5167978SPeter.Dunlap@Sun.COM 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
5177978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[type];
5187978SPeter.Dunlap@Sun.COM 			if (it->it_ops == NULL) {
5197978SPeter.Dunlap@Sun.COM 				/* transport is not registered */
5207978SPeter.Dunlap@Sun.COM 				continue;
5217978SPeter.Dunlap@Sun.COM 			}
5227978SPeter.Dunlap@Sun.COM 
5237978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
5247978SPeter.Dunlap@Sun.COM 			rc = it->it_ops->it_tgt_svc_online(is);
5257978SPeter.Dunlap@Sun.COM 			mutex_enter(&is->is_mutex);
526*9162SPeter.Dunlap@Sun.COM 			if (rc != IDM_STATUS_SUCCESS) {
527*9162SPeter.Dunlap@Sun.COM 				last_type = type;
528*9162SPeter.Dunlap@Sun.COM 				break;
5297978SPeter.Dunlap@Sun.COM 			}
5307978SPeter.Dunlap@Sun.COM 		}
531*9162SPeter.Dunlap@Sun.COM 		if (rc != IDM_STATUS_SUCCESS) {
532*9162SPeter.Dunlap@Sun.COM 			/*
533*9162SPeter.Dunlap@Sun.COM 			 * The last transport failed to online.
534*9162SPeter.Dunlap@Sun.COM 			 * Offline any transport onlined above and
535*9162SPeter.Dunlap@Sun.COM 			 * do not online the target.
536*9162SPeter.Dunlap@Sun.COM 			 */
537*9162SPeter.Dunlap@Sun.COM 			for (type = 0; type < last_type; type++) {
538*9162SPeter.Dunlap@Sun.COM 				it = &idm_transport_list[type];
539*9162SPeter.Dunlap@Sun.COM 				if (it->it_ops == NULL) {
540*9162SPeter.Dunlap@Sun.COM 					/* transport is not registered */
541*9162SPeter.Dunlap@Sun.COM 					continue;
542*9162SPeter.Dunlap@Sun.COM 				}
543*9162SPeter.Dunlap@Sun.COM 
544*9162SPeter.Dunlap@Sun.COM 				mutex_exit(&is->is_mutex);
545*9162SPeter.Dunlap@Sun.COM 				it->it_ops->it_tgt_svc_offline(is);
546*9162SPeter.Dunlap@Sun.COM 				mutex_enter(&is->is_mutex);
547*9162SPeter.Dunlap@Sun.COM 			}
548*9162SPeter.Dunlap@Sun.COM 		} else {
549*9162SPeter.Dunlap@Sun.COM 			/* Target service now online */
550*9162SPeter.Dunlap@Sun.COM 			is->is_online = 1;
551*9162SPeter.Dunlap@Sun.COM 		}
5527978SPeter.Dunlap@Sun.COM 	} else {
553*9162SPeter.Dunlap@Sun.COM 		/* Target service already online, just bump the count */
554*9162SPeter.Dunlap@Sun.COM 		is->is_online++;
5557978SPeter.Dunlap@Sun.COM 	}
5567978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
5577978SPeter.Dunlap@Sun.COM 
558*9162SPeter.Dunlap@Sun.COM 	return (rc);
5597978SPeter.Dunlap@Sun.COM }
5607978SPeter.Dunlap@Sun.COM 
5617978SPeter.Dunlap@Sun.COM /*
5627978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_offline
5637978SPeter.Dunlap@Sun.COM  *
5647978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
5657978SPeter.Dunlap@Sun.COM  *
5667978SPeter.Dunlap@Sun.COM  * Shutdown any online target services.
5677978SPeter.Dunlap@Sun.COM  */
5687978SPeter.Dunlap@Sun.COM void
5697978SPeter.Dunlap@Sun.COM idm_tgt_svc_offline(idm_svc_t *is)
5707978SPeter.Dunlap@Sun.COM {
5717978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
5727978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
5737978SPeter.Dunlap@Sun.COM 
5747978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
5757978SPeter.Dunlap@Sun.COM 	is->is_online--;
5767978SPeter.Dunlap@Sun.COM 	if (is->is_online == 0) {
5777978SPeter.Dunlap@Sun.COM 		/* Walk through each of the transports and offline them */
5787978SPeter.Dunlap@Sun.COM 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
5797978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[type];
5807978SPeter.Dunlap@Sun.COM 			if (it->it_ops == NULL) {
5817978SPeter.Dunlap@Sun.COM 				/* transport is not registered */
5827978SPeter.Dunlap@Sun.COM 				continue;
5837978SPeter.Dunlap@Sun.COM 			}
5847978SPeter.Dunlap@Sun.COM 
5857978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
5867978SPeter.Dunlap@Sun.COM 			it->it_ops->it_tgt_svc_offline(is);
5877978SPeter.Dunlap@Sun.COM 			mutex_enter(&is->is_mutex);
5887978SPeter.Dunlap@Sun.COM 		}
5897978SPeter.Dunlap@Sun.COM 	}
5907978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
5917978SPeter.Dunlap@Sun.COM }
5927978SPeter.Dunlap@Sun.COM 
5937978SPeter.Dunlap@Sun.COM /*
5947978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_lookup
5957978SPeter.Dunlap@Sun.COM  *
5967978SPeter.Dunlap@Sun.COM  * Lookup a service instance listening on the specified port
5977978SPeter.Dunlap@Sun.COM  */
5987978SPeter.Dunlap@Sun.COM 
5997978SPeter.Dunlap@Sun.COM idm_svc_t *
6007978SPeter.Dunlap@Sun.COM idm_tgt_svc_lookup(uint16_t port)
6017978SPeter.Dunlap@Sun.COM {
6027978SPeter.Dunlap@Sun.COM 	idm_svc_t *result;
6037978SPeter.Dunlap@Sun.COM 
6047978SPeter.Dunlap@Sun.COM retry:
6057978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
6067978SPeter.Dunlap@Sun.COM 	for (result = list_head(&idm.idm_tgt_svc_list);
6077978SPeter.Dunlap@Sun.COM 	    result != NULL;
6087978SPeter.Dunlap@Sun.COM 	    result = list_next(&idm.idm_tgt_svc_list, result)) {
6097978SPeter.Dunlap@Sun.COM 		if (result->is_svc_req.sr_port == port) {
6107978SPeter.Dunlap@Sun.COM 			if (result->is_online == 0) {
6117978SPeter.Dunlap@Sun.COM 				/*
6127978SPeter.Dunlap@Sun.COM 				 * A service exists on this port, but it
6137978SPeter.Dunlap@Sun.COM 				 * is going away, wait for it to cleanup.
6147978SPeter.Dunlap@Sun.COM 				 */
6157978SPeter.Dunlap@Sun.COM 				cv_wait(&idm.idm_tgt_svc_cv,
6167978SPeter.Dunlap@Sun.COM 				    &idm.idm_global_mutex);
6177978SPeter.Dunlap@Sun.COM 				mutex_exit(&idm.idm_global_mutex);
6187978SPeter.Dunlap@Sun.COM 				goto retry;
6197978SPeter.Dunlap@Sun.COM 			}
6207978SPeter.Dunlap@Sun.COM 			idm_tgt_svc_hold(result);
6217978SPeter.Dunlap@Sun.COM 			mutex_exit(&idm.idm_global_mutex);
6227978SPeter.Dunlap@Sun.COM 			return (result);
6237978SPeter.Dunlap@Sun.COM 		}
6247978SPeter.Dunlap@Sun.COM 	}
6257978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
6267978SPeter.Dunlap@Sun.COM 
6277978SPeter.Dunlap@Sun.COM 	return (NULL);
6287978SPeter.Dunlap@Sun.COM }
6297978SPeter.Dunlap@Sun.COM 
6307978SPeter.Dunlap@Sun.COM /*
6317978SPeter.Dunlap@Sun.COM  * idm_negotiate_key_values()
6327978SPeter.Dunlap@Sun.COM  * Give IDM level a chance to negotiate any login parameters it should own.
6337978SPeter.Dunlap@Sun.COM  *  -- leave unhandled parameters alone on request_nvl
6347978SPeter.Dunlap@Sun.COM  *  -- move all handled parameters to response_nvl with an appropriate response
6357978SPeter.Dunlap@Sun.COM  *  -- also add an entry to negotiated_nvl for any accepted parameters
6367978SPeter.Dunlap@Sun.COM  */
6377978SPeter.Dunlap@Sun.COM kv_status_t
6387978SPeter.Dunlap@Sun.COM idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
6397978SPeter.Dunlap@Sun.COM     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
6407978SPeter.Dunlap@Sun.COM {
6417978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_transport_ops != NULL);
6427978SPeter.Dunlap@Sun.COM 	return (ic->ic_transport_ops->it_negotiate_key_values(ic,
6437978SPeter.Dunlap@Sun.COM 	    request_nvl, response_nvl, negotiated_nvl));
6447978SPeter.Dunlap@Sun.COM }
6457978SPeter.Dunlap@Sun.COM 
6467978SPeter.Dunlap@Sun.COM /*
6477978SPeter.Dunlap@Sun.COM  * idm_notice_key_values()
6487978SPeter.Dunlap@Sun.COM  * Activate at the IDM level any parameters that have been negotiated.
6497978SPeter.Dunlap@Sun.COM  * Passes the set of key value pairs to the transport for activation.
6507978SPeter.Dunlap@Sun.COM  * This will be invoked as the connection is entering full-feature mode.
6517978SPeter.Dunlap@Sun.COM  */
652*9162SPeter.Dunlap@Sun.COM void
6537978SPeter.Dunlap@Sun.COM idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
6547978SPeter.Dunlap@Sun.COM {
6557978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_transport_ops != NULL);
656*9162SPeter.Dunlap@Sun.COM 	ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl);
6577978SPeter.Dunlap@Sun.COM }
6587978SPeter.Dunlap@Sun.COM 
6597978SPeter.Dunlap@Sun.COM /*
6607978SPeter.Dunlap@Sun.COM  * idm_buf_tx_to_ini
6617978SPeter.Dunlap@Sun.COM  *
6627978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Put_Data' operational primitive.
6637978SPeter.Dunlap@Sun.COM  *
6647978SPeter.Dunlap@Sun.COM  * This function is invoked by a target iSCSI layer to request its local
6657978SPeter.Dunlap@Sun.COM  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
6667978SPeter.Dunlap@Sun.COM  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
6677978SPeter.Dunlap@Sun.COM  * transferred to the initiator associated with task 'idt'. The connection
6687978SPeter.Dunlap@Sun.COM  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
6697978SPeter.Dunlap@Sun.COM  * and the callback (idb->idb_buf_cb) at transfer completion are
6707978SPeter.Dunlap@Sun.COM  * provided as input.
6717978SPeter.Dunlap@Sun.COM  *
6727978SPeter.Dunlap@Sun.COM  * This data transfer takes place transparently to the remote iSCSI layer,
6737978SPeter.Dunlap@Sun.COM  * i.e. without its participation.
6747978SPeter.Dunlap@Sun.COM  *
6757978SPeter.Dunlap@Sun.COM  * Using sockets, IDM implements the data transfer by segmenting the data
6767978SPeter.Dunlap@Sun.COM  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
6777978SPeter.Dunlap@Sun.COM  * initiator. iSER performs the transfer using RDMA write.
6787978SPeter.Dunlap@Sun.COM  *
6797978SPeter.Dunlap@Sun.COM  */
6807978SPeter.Dunlap@Sun.COM idm_status_t
6817978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
6827978SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t xfer_len,
6837978SPeter.Dunlap@Sun.COM     idm_buf_cb_t idb_buf_cb, void *cb_arg)
6847978SPeter.Dunlap@Sun.COM {
6857978SPeter.Dunlap@Sun.COM 	idm_status_t rc;
6867978SPeter.Dunlap@Sun.COM 
6877978SPeter.Dunlap@Sun.COM 	idb->idb_bufoffset = offset;
6887978SPeter.Dunlap@Sun.COM 	idb->idb_xfer_len = xfer_len;
6897978SPeter.Dunlap@Sun.COM 	idb->idb_buf_cb = idb_buf_cb;
6907978SPeter.Dunlap@Sun.COM 	idb->idb_cb_arg = cb_arg;
691*9162SPeter.Dunlap@Sun.COM 	gethrestime(&idb->idb_xfer_start);
692*9162SPeter.Dunlap@Sun.COM 
693*9162SPeter.Dunlap@Sun.COM 	/*
694*9162SPeter.Dunlap@Sun.COM 	 * Buffer should not contain the pattern.  If the pattern is
695*9162SPeter.Dunlap@Sun.COM 	 * present then we've been asked to transmit initialized data
696*9162SPeter.Dunlap@Sun.COM 	 */
697*9162SPeter.Dunlap@Sun.COM 	IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT);
6987978SPeter.Dunlap@Sun.COM 
6997978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
7007978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
7017978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
7027978SPeter.Dunlap@Sun.COM 		idt->idt_tx_to_ini_start++;
7037978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
7047978SPeter.Dunlap@Sun.COM 		idm_buf_bind_in_locked(idt, idb);
7057978SPeter.Dunlap@Sun.COM 		idb->idb_in_transport = B_TRUE;
7067978SPeter.Dunlap@Sun.COM 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
7077978SPeter.Dunlap@Sun.COM 		    (idt, idb);
7087978SPeter.Dunlap@Sun.COM 		return (rc);
7097978SPeter.Dunlap@Sun.COM 
7107978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
7117978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
7127978SPeter.Dunlap@Sun.COM 		/*
7137978SPeter.Dunlap@Sun.COM 		 * Bind buffer but don't start a transfer since the task
7147978SPeter.Dunlap@Sun.COM 		 * is suspended
7157978SPeter.Dunlap@Sun.COM 		 */
7167978SPeter.Dunlap@Sun.COM 		idm_buf_bind_in_locked(idt, idb);
7177978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
7187978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
7197978SPeter.Dunlap@Sun.COM 
7207978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
7217978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
7227978SPeter.Dunlap@Sun.COM 		/*
7237978SPeter.Dunlap@Sun.COM 		 * Once the task is aborted, any buffers added to the
7247978SPeter.Dunlap@Sun.COM 		 * idt_inbufv will never get cleaned up, so just return
7257978SPeter.Dunlap@Sun.COM 		 * SUCCESS.  The buffer should get cleaned up by the
7267978SPeter.Dunlap@Sun.COM 		 * client or framework once task_aborted has completed.
7277978SPeter.Dunlap@Sun.COM 		 */
7287978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
7297978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
7307978SPeter.Dunlap@Sun.COM 
7317978SPeter.Dunlap@Sun.COM 	default:
7327978SPeter.Dunlap@Sun.COM 		ASSERT(0);
7337978SPeter.Dunlap@Sun.COM 		break;
7347978SPeter.Dunlap@Sun.COM 	}
7357978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
7367978SPeter.Dunlap@Sun.COM 
7377978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_FAIL);
7387978SPeter.Dunlap@Sun.COM }
7397978SPeter.Dunlap@Sun.COM 
7407978SPeter.Dunlap@Sun.COM /*
7417978SPeter.Dunlap@Sun.COM  * idm_buf_rx_from_ini
7427978SPeter.Dunlap@Sun.COM  *
7437978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Get_Data' operational primitive.
7447978SPeter.Dunlap@Sun.COM  *
7457978SPeter.Dunlap@Sun.COM  * This function is invoked by a target iSCSI layer to request its local
7467978SPeter.Dunlap@Sun.COM  * Datamover layer to retrieve certain data identified by the R2T PDU from the
7477978SPeter.Dunlap@Sun.COM  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
7487978SPeter.Dunlap@Sun.COM  * mapped to the respective buffer by the task tags (ITT & TTT).
7497978SPeter.Dunlap@Sun.COM  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
7507978SPeter.Dunlap@Sun.COM  * the callback (idb->idb_buf_cb) notification for data transfer completion are
7517978SPeter.Dunlap@Sun.COM  * are provided as input.
7527978SPeter.Dunlap@Sun.COM  *
7537978SPeter.Dunlap@Sun.COM  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
7547978SPeter.Dunlap@Sun.COM  * Datamover layer, the local and remote Datamover layers transparently bring
7557978SPeter.Dunlap@Sun.COM  * about the data transfer requested by the R2T PDU, without the participation
7567978SPeter.Dunlap@Sun.COM  * of the iSCSI layers.
7577978SPeter.Dunlap@Sun.COM  *
7587978SPeter.Dunlap@Sun.COM  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
7597978SPeter.Dunlap@Sun.COM  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
7607978SPeter.Dunlap@Sun.COM  *
7617978SPeter.Dunlap@Sun.COM  */
7627978SPeter.Dunlap@Sun.COM idm_status_t
7637978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
7647978SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t xfer_len,
7657978SPeter.Dunlap@Sun.COM     idm_buf_cb_t idb_buf_cb, void *cb_arg)
7667978SPeter.Dunlap@Sun.COM {
7677978SPeter.Dunlap@Sun.COM 	idm_status_t rc;
7687978SPeter.Dunlap@Sun.COM 
7697978SPeter.Dunlap@Sun.COM 	idb->idb_bufoffset = offset;
7707978SPeter.Dunlap@Sun.COM 	idb->idb_xfer_len = xfer_len;
7717978SPeter.Dunlap@Sun.COM 	idb->idb_buf_cb = idb_buf_cb;
7727978SPeter.Dunlap@Sun.COM 	idb->idb_cb_arg = cb_arg;
773*9162SPeter.Dunlap@Sun.COM 	gethrestime(&idb->idb_xfer_start);
7747978SPeter.Dunlap@Sun.COM 
7757978SPeter.Dunlap@Sun.COM 	/*
7767978SPeter.Dunlap@Sun.COM 	 * "In" buf list is for "Data In" PDU's, "Out" buf list is for
7777978SPeter.Dunlap@Sun.COM 	 * "Data Out" PDU's
7787978SPeter.Dunlap@Sun.COM 	 */
7797978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
7807978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
7817978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
7827978SPeter.Dunlap@Sun.COM 		idt->idt_rx_from_ini_start++;
7837978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
7847978SPeter.Dunlap@Sun.COM 		idm_buf_bind_out_locked(idt, idb);
7857978SPeter.Dunlap@Sun.COM 		idb->idb_in_transport = B_TRUE;
7867978SPeter.Dunlap@Sun.COM 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
7877978SPeter.Dunlap@Sun.COM 		    (idt, idb);
7887978SPeter.Dunlap@Sun.COM 		return (rc);
7897978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
7907978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
7917978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
7927978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
7937978SPeter.Dunlap@Sun.COM 		/*
7947978SPeter.Dunlap@Sun.COM 		 * Bind buffer but don't start a transfer since the task
7957978SPeter.Dunlap@Sun.COM 		 * is suspended
7967978SPeter.Dunlap@Sun.COM 		 */
7977978SPeter.Dunlap@Sun.COM 		idm_buf_bind_out_locked(idt, idb);
7987978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
7997978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
8007978SPeter.Dunlap@Sun.COM 	default:
8017978SPeter.Dunlap@Sun.COM 		ASSERT(0);
8027978SPeter.Dunlap@Sun.COM 		break;
8037978SPeter.Dunlap@Sun.COM 	}
8047978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
8057978SPeter.Dunlap@Sun.COM 
8067978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_FAIL);
8077978SPeter.Dunlap@Sun.COM }
8087978SPeter.Dunlap@Sun.COM 
8097978SPeter.Dunlap@Sun.COM /*
8107978SPeter.Dunlap@Sun.COM  * idm_buf_tx_to_ini_done
8117978SPeter.Dunlap@Sun.COM  *
8127978SPeter.Dunlap@Sun.COM  * The transport calls this after it has completed a transfer requested by
8137978SPeter.Dunlap@Sun.COM  * a call to transport_buf_tx_to_ini
8147978SPeter.Dunlap@Sun.COM  *
8157978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
8167978SPeter.Dunlap@Sun.COM  * idt may be freed after the call to idb->idb_buf_cb.
8177978SPeter.Dunlap@Sun.COM  */
8187978SPeter.Dunlap@Sun.COM void
8197978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
8207978SPeter.Dunlap@Sun.COM {
8217978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
8227978SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
8237978SPeter.Dunlap@Sun.COM 	idb->idb_tx_thread = B_FALSE;
8247978SPeter.Dunlap@Sun.COM 	idt->idt_tx_to_ini_done++;
825*9162SPeter.Dunlap@Sun.COM 	gethrestime(&idb->idb_xfer_done);
8267978SPeter.Dunlap@Sun.COM 
8277978SPeter.Dunlap@Sun.COM 	/*
8287978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
8297978SPeter.Dunlap@Sun.COM 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
8307978SPeter.Dunlap@Sun.COM 	 * to 0.
8317978SPeter.Dunlap@Sun.COM 	 */
8327978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
8337978SPeter.Dunlap@Sun.COM 	idb->idb_status = status;
8347978SPeter.Dunlap@Sun.COM 
8357978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
8367978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
8377978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_in_locked(idt, idb);
8387978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
8397978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, status);
8407978SPeter.Dunlap@Sun.COM 		return;
8417978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
8427978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
8437978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
8447978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
8457978SPeter.Dunlap@Sun.COM 		/*
8467978SPeter.Dunlap@Sun.COM 		 * To keep things simple we will ignore the case where the
8477978SPeter.Dunlap@Sun.COM 		 * transfer was successful and leave all buffers bound to the
8487978SPeter.Dunlap@Sun.COM 		 * task.  This allows us to also ignore the case where we've
8497978SPeter.Dunlap@Sun.COM 		 * been asked to abort a task but the last transfer of the
8507978SPeter.Dunlap@Sun.COM 		 * task has completed.  IDM has no idea whether this was, in
8517978SPeter.Dunlap@Sun.COM 		 * fact, the last transfer of the task so it would be difficult
8527978SPeter.Dunlap@Sun.COM 		 * to handle this case.  Everything should get sorted out again
8537978SPeter.Dunlap@Sun.COM 		 * after task reassignment is complete.
8547978SPeter.Dunlap@Sun.COM 		 *
8557978SPeter.Dunlap@Sun.COM 		 * In the case of TASK_ABORTING we could conceivably call the
8567978SPeter.Dunlap@Sun.COM 		 * buffer callback here but the timing of when the client's
8577978SPeter.Dunlap@Sun.COM 		 * client_task_aborted callback is invoked vs. when the client's
8587978SPeter.Dunlap@Sun.COM 		 * buffer callback gets invoked gets sticky.  We don't want
8597978SPeter.Dunlap@Sun.COM 		 * the client to here from us again after the call to
8607978SPeter.Dunlap@Sun.COM 		 * client_task_aborted() but we don't want to give it a bunch
8617978SPeter.Dunlap@Sun.COM 		 * of failed buffer transfers until we've called
8627978SPeter.Dunlap@Sun.COM 		 * client_task_aborted().  Instead we'll just leave all the
8637978SPeter.Dunlap@Sun.COM 		 * buffers bound and allow the client to cleanup.
8647978SPeter.Dunlap@Sun.COM 		 */
8657978SPeter.Dunlap@Sun.COM 		break;
8667978SPeter.Dunlap@Sun.COM 	default:
8677978SPeter.Dunlap@Sun.COM 		ASSERT(0);
8687978SPeter.Dunlap@Sun.COM 	}
8697978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
8707978SPeter.Dunlap@Sun.COM }
8717978SPeter.Dunlap@Sun.COM 
8727978SPeter.Dunlap@Sun.COM /*
8737978SPeter.Dunlap@Sun.COM  * idm_buf_rx_from_ini_done
8747978SPeter.Dunlap@Sun.COM  *
8757978SPeter.Dunlap@Sun.COM  * The transport calls this after it has completed a transfer requested by
8767978SPeter.Dunlap@Sun.COM  * a call totransport_buf_tx_to_ini
8777978SPeter.Dunlap@Sun.COM  *
8787978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
8797978SPeter.Dunlap@Sun.COM  * idt may be freed after the call to idb->idb_buf_cb.
8807978SPeter.Dunlap@Sun.COM  */
8817978SPeter.Dunlap@Sun.COM void
8827978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
8837978SPeter.Dunlap@Sun.COM {
8847978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
8857978SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
8867978SPeter.Dunlap@Sun.COM 	idt->idt_rx_from_ini_done++;
887*9162SPeter.Dunlap@Sun.COM 	gethrestime(&idb->idb_xfer_done);
8887978SPeter.Dunlap@Sun.COM 
8897978SPeter.Dunlap@Sun.COM 	/*
8907978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
8917978SPeter.Dunlap@Sun.COM 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
8927978SPeter.Dunlap@Sun.COM 	 * to 0.
8937978SPeter.Dunlap@Sun.COM 	 */
8947978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
8957978SPeter.Dunlap@Sun.COM 	idb->idb_status = status;
8967978SPeter.Dunlap@Sun.COM 
897*9162SPeter.Dunlap@Sun.COM 	if (status == IDM_STATUS_SUCCESS) {
898*9162SPeter.Dunlap@Sun.COM 		/*
899*9162SPeter.Dunlap@Sun.COM 		 * Buffer should not contain the pattern.  If it does then
900*9162SPeter.Dunlap@Sun.COM 		 * we did not get the data from the remote host.
901*9162SPeter.Dunlap@Sun.COM 		 */
902*9162SPeter.Dunlap@Sun.COM 		IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT);
903*9162SPeter.Dunlap@Sun.COM 	}
904*9162SPeter.Dunlap@Sun.COM 
9057978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
9067978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
9077978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_out_locked(idt, idb);
9087978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
9097978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, status);
9107978SPeter.Dunlap@Sun.COM 		return;
9117978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
9127978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
9137978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
9147978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
9157978SPeter.Dunlap@Sun.COM 		/*
9167978SPeter.Dunlap@Sun.COM 		 * To keep things simple we will ignore the case where the
9177978SPeter.Dunlap@Sun.COM 		 * transfer was successful and leave all buffers bound to the
9187978SPeter.Dunlap@Sun.COM 		 * task.  This allows us to also ignore the case where we've
9197978SPeter.Dunlap@Sun.COM 		 * been asked to abort a task but the last transfer of the
9207978SPeter.Dunlap@Sun.COM 		 * task has completed.  IDM has no idea whether this was, in
9217978SPeter.Dunlap@Sun.COM 		 * fact, the last transfer of the task so it would be difficult
9227978SPeter.Dunlap@Sun.COM 		 * to handle this case.  Everything should get sorted out again
9237978SPeter.Dunlap@Sun.COM 		 * after task reassignment is complete.
9247978SPeter.Dunlap@Sun.COM 		 *
9257978SPeter.Dunlap@Sun.COM 		 * In the case of TASK_ABORTING we could conceivably call the
9267978SPeter.Dunlap@Sun.COM 		 * buffer callback here but the timing of when the client's
9277978SPeter.Dunlap@Sun.COM 		 * client_task_aborted callback is invoked vs. when the client's
9287978SPeter.Dunlap@Sun.COM 		 * buffer callback gets invoked gets sticky.  We don't want
9297978SPeter.Dunlap@Sun.COM 		 * the client to here from us again after the call to
9307978SPeter.Dunlap@Sun.COM 		 * client_task_aborted() but we don't want to give it a bunch
9317978SPeter.Dunlap@Sun.COM 		 * of failed buffer transfers until we've called
9327978SPeter.Dunlap@Sun.COM 		 * client_task_aborted().  Instead we'll just leave all the
9337978SPeter.Dunlap@Sun.COM 		 * buffers bound and allow the client to cleanup.
9347978SPeter.Dunlap@Sun.COM 		 */
9357978SPeter.Dunlap@Sun.COM 		break;
9367978SPeter.Dunlap@Sun.COM 	default:
9377978SPeter.Dunlap@Sun.COM 		ASSERT(0);
9387978SPeter.Dunlap@Sun.COM 	}
9397978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
9407978SPeter.Dunlap@Sun.COM }
9417978SPeter.Dunlap@Sun.COM 
9427978SPeter.Dunlap@Sun.COM /*
9437978SPeter.Dunlap@Sun.COM  * idm_buf_alloc
9447978SPeter.Dunlap@Sun.COM  *
9457978SPeter.Dunlap@Sun.COM  * Allocates a buffer handle and registers it for use with the transport
9467978SPeter.Dunlap@Sun.COM  * layer. If a buffer is not passed on bufptr, the buffer will be allocated
9477978SPeter.Dunlap@Sun.COM  * as well as the handle.
9487978SPeter.Dunlap@Sun.COM  *
9497978SPeter.Dunlap@Sun.COM  * ic		- connection on which the buffer will be transferred
9507978SPeter.Dunlap@Sun.COM  * bufptr	- allocate memory for buffer if NULL, else assign to buffer
9517978SPeter.Dunlap@Sun.COM  * buflen	- length of buffer
9527978SPeter.Dunlap@Sun.COM  *
9537978SPeter.Dunlap@Sun.COM  * Returns idm_buf_t handle if successful, otherwise NULL
9547978SPeter.Dunlap@Sun.COM  */
9557978SPeter.Dunlap@Sun.COM idm_buf_t *
9567978SPeter.Dunlap@Sun.COM idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
9577978SPeter.Dunlap@Sun.COM {
9587978SPeter.Dunlap@Sun.COM 	idm_buf_t	*buf = NULL;
9597978SPeter.Dunlap@Sun.COM 	int		rc;
9607978SPeter.Dunlap@Sun.COM 
9617978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
9627978SPeter.Dunlap@Sun.COM 	ASSERT(idm.idm_buf_cache != NULL);
9637978SPeter.Dunlap@Sun.COM 	ASSERT(buflen > 0);
9647978SPeter.Dunlap@Sun.COM 
9657978SPeter.Dunlap@Sun.COM 	/* Don't allocate new buffers if we are not in FFP */
9667978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
9677978SPeter.Dunlap@Sun.COM 	if (!ic->ic_ffp) {
9687978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
9697978SPeter.Dunlap@Sun.COM 		return (NULL);
9707978SPeter.Dunlap@Sun.COM 	}
9717978SPeter.Dunlap@Sun.COM 
9727978SPeter.Dunlap@Sun.COM 
9737978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
9747978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
9757978SPeter.Dunlap@Sun.COM 
9767978SPeter.Dunlap@Sun.COM 	buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP);
9777978SPeter.Dunlap@Sun.COM 	if (buf == NULL) {
9787978SPeter.Dunlap@Sun.COM 		idm_conn_rele(ic);
9797978SPeter.Dunlap@Sun.COM 		return (NULL);
9807978SPeter.Dunlap@Sun.COM 	}
9817978SPeter.Dunlap@Sun.COM 
9827978SPeter.Dunlap@Sun.COM 	buf->idb_ic		= ic;
9837978SPeter.Dunlap@Sun.COM 	buf->idb_buflen		= buflen;
9847978SPeter.Dunlap@Sun.COM 	buf->idb_exp_offset	= 0;
9857978SPeter.Dunlap@Sun.COM 	buf->idb_bufoffset	= 0;
9867978SPeter.Dunlap@Sun.COM 	buf->idb_xfer_len 	= 0;
9877978SPeter.Dunlap@Sun.COM 	buf->idb_magic		= IDM_BUF_MAGIC;
988*9162SPeter.Dunlap@Sun.COM 	buf->idb_in_transport	= B_FALSE;
989*9162SPeter.Dunlap@Sun.COM 	buf->idb_bufbcopy	= B_FALSE;
9907978SPeter.Dunlap@Sun.COM 
9917978SPeter.Dunlap@Sun.COM 	/*
9927978SPeter.Dunlap@Sun.COM 	 * If bufptr is NULL, we have an implicit request to allocate
9937978SPeter.Dunlap@Sun.COM 	 * memory for this IDM buffer handle and register it for use
9947978SPeter.Dunlap@Sun.COM 	 * with the transport. To simplify this, and to give more freedom
9957978SPeter.Dunlap@Sun.COM 	 * to the transport layer for it's own buffer management, both of
9967978SPeter.Dunlap@Sun.COM 	 * these actions will take place in the transport layer.
9977978SPeter.Dunlap@Sun.COM 	 * If bufptr is set, then the caller has allocated memory (or more
9987978SPeter.Dunlap@Sun.COM 	 * likely it's been passed from an upper layer), and we need only
9997978SPeter.Dunlap@Sun.COM 	 * register the buffer for use with the transport layer.
10007978SPeter.Dunlap@Sun.COM 	 */
10017978SPeter.Dunlap@Sun.COM 	if (bufptr == NULL) {
10027978SPeter.Dunlap@Sun.COM 		/*
10037978SPeter.Dunlap@Sun.COM 		 * Allocate a buffer from the transport layer (which
10047978SPeter.Dunlap@Sun.COM 		 * will also register the buffer for use).
10057978SPeter.Dunlap@Sun.COM 		 */
10067978SPeter.Dunlap@Sun.COM 		rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen);
10077978SPeter.Dunlap@Sun.COM 		if (rc != 0) {
10087978SPeter.Dunlap@Sun.COM 			idm_conn_rele(ic);
10097978SPeter.Dunlap@Sun.COM 			kmem_cache_free(idm.idm_buf_cache, buf);
10107978SPeter.Dunlap@Sun.COM 			return (NULL);
10117978SPeter.Dunlap@Sun.COM 		}
10127978SPeter.Dunlap@Sun.COM 		/* Set the bufalloc'd flag */
10137978SPeter.Dunlap@Sun.COM 		buf->idb_bufalloc = B_TRUE;
10147978SPeter.Dunlap@Sun.COM 	} else {
10157978SPeter.Dunlap@Sun.COM 		/*
1016*9162SPeter.Dunlap@Sun.COM 		 * For large transfers, Set the passed bufptr into
1017*9162SPeter.Dunlap@Sun.COM 		 * the buf handle, and register the handle with the
1018*9162SPeter.Dunlap@Sun.COM 		 * transport layer. As memory registration with the
1019*9162SPeter.Dunlap@Sun.COM 		 * transport layer is a time/cpu intensive operation,
1020*9162SPeter.Dunlap@Sun.COM 		 * for small transfers (up to a pre-defined bcopy
1021*9162SPeter.Dunlap@Sun.COM 		 * threshold), use pre-registered memory buffers
1022*9162SPeter.Dunlap@Sun.COM 		 * and bcopy data at the appropriate time.
10237978SPeter.Dunlap@Sun.COM 		 */
10247978SPeter.Dunlap@Sun.COM 		buf->idb_buf = bufptr;
10257978SPeter.Dunlap@Sun.COM 
10267978SPeter.Dunlap@Sun.COM 		rc = ic->ic_transport_ops->it_buf_setup(buf);
10277978SPeter.Dunlap@Sun.COM 		if (rc != 0) {
10287978SPeter.Dunlap@Sun.COM 			idm_conn_rele(ic);
10297978SPeter.Dunlap@Sun.COM 			kmem_cache_free(idm.idm_buf_cache, buf);
10307978SPeter.Dunlap@Sun.COM 			return (NULL);
10317978SPeter.Dunlap@Sun.COM 		}
1032*9162SPeter.Dunlap@Sun.COM 		/*
1033*9162SPeter.Dunlap@Sun.COM 		 * The transport layer is now expected to set the idb_bufalloc
1034*9162SPeter.Dunlap@Sun.COM 		 * correctly to indicate if resources have been allocated.
1035*9162SPeter.Dunlap@Sun.COM 		 */
10367978SPeter.Dunlap@Sun.COM 	}
10377978SPeter.Dunlap@Sun.COM 
1038*9162SPeter.Dunlap@Sun.COM 	IDM_BUFPAT_SET(buf);
1039*9162SPeter.Dunlap@Sun.COM 
10407978SPeter.Dunlap@Sun.COM 	return (buf);
10417978SPeter.Dunlap@Sun.COM }
10427978SPeter.Dunlap@Sun.COM 
10437978SPeter.Dunlap@Sun.COM /*
10447978SPeter.Dunlap@Sun.COM  * idm_buf_free
10457978SPeter.Dunlap@Sun.COM  *
10467978SPeter.Dunlap@Sun.COM  * Release a buffer handle along with the associated buffer that was allocated
10477978SPeter.Dunlap@Sun.COM  * or assigned with idm_buf_alloc
10487978SPeter.Dunlap@Sun.COM  */
10497978SPeter.Dunlap@Sun.COM void
10507978SPeter.Dunlap@Sun.COM idm_buf_free(idm_buf_t *buf)
10517978SPeter.Dunlap@Sun.COM {
10527978SPeter.Dunlap@Sun.COM 	idm_conn_t *ic = buf->idb_ic;
10537978SPeter.Dunlap@Sun.COM 
10547978SPeter.Dunlap@Sun.COM 
10557978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding	= NULL;
10567978SPeter.Dunlap@Sun.COM 
10577978SPeter.Dunlap@Sun.COM 	if (buf->idb_bufalloc) {
10587978SPeter.Dunlap@Sun.COM 		ic->ic_transport_ops->it_buf_free(buf);
10597978SPeter.Dunlap@Sun.COM 	} else {
10607978SPeter.Dunlap@Sun.COM 		ic->ic_transport_ops->it_buf_teardown(buf);
10617978SPeter.Dunlap@Sun.COM 	}
10627978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_buf_cache, buf);
10637978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
10647978SPeter.Dunlap@Sun.COM }
10657978SPeter.Dunlap@Sun.COM 
10667978SPeter.Dunlap@Sun.COM /*
10677978SPeter.Dunlap@Sun.COM  * idm_buf_bind_in
10687978SPeter.Dunlap@Sun.COM  *
10697978SPeter.Dunlap@Sun.COM  * This function associates a buffer with a task. This is only for use by the
10707978SPeter.Dunlap@Sun.COM  * iSCSI initiator that will have only one buffer per transfer direction
10717978SPeter.Dunlap@Sun.COM  *
10727978SPeter.Dunlap@Sun.COM  */
10737978SPeter.Dunlap@Sun.COM void
10747978SPeter.Dunlap@Sun.COM idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
10757978SPeter.Dunlap@Sun.COM {
10767978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
10777978SPeter.Dunlap@Sun.COM 	idm_buf_bind_in_locked(idt, buf);
10787978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
10797978SPeter.Dunlap@Sun.COM }
10807978SPeter.Dunlap@Sun.COM 
10817978SPeter.Dunlap@Sun.COM static void
10827978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf)
10837978SPeter.Dunlap@Sun.COM {
10847978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding = idt;
10857978SPeter.Dunlap@Sun.COM 	buf->idb_ic = idt->idt_ic;
10867978SPeter.Dunlap@Sun.COM 	idm_listbuf_insert(&idt->idt_inbufv, buf);
10877978SPeter.Dunlap@Sun.COM }
10887978SPeter.Dunlap@Sun.COM 
10897978SPeter.Dunlap@Sun.COM void
10907978SPeter.Dunlap@Sun.COM idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
10917978SPeter.Dunlap@Sun.COM {
1092*9162SPeter.Dunlap@Sun.COM 	/*
1093*9162SPeter.Dunlap@Sun.COM 	 * For small transfers, the iSER transport delegates the IDM
1094*9162SPeter.Dunlap@Sun.COM 	 * layer to bcopy the SCSI Write data for faster IOPS.
1095*9162SPeter.Dunlap@Sun.COM 	 */
1096*9162SPeter.Dunlap@Sun.COM 	if (buf->idb_bufbcopy == B_TRUE) {
1097*9162SPeter.Dunlap@Sun.COM 
1098*9162SPeter.Dunlap@Sun.COM 		bcopy(buf->idb_bufptr, buf->idb_buf, buf->idb_buflen);
1099*9162SPeter.Dunlap@Sun.COM 	}
11007978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
11017978SPeter.Dunlap@Sun.COM 	idm_buf_bind_out_locked(idt, buf);
11027978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
11037978SPeter.Dunlap@Sun.COM }
11047978SPeter.Dunlap@Sun.COM 
11057978SPeter.Dunlap@Sun.COM static void
11067978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf)
11077978SPeter.Dunlap@Sun.COM {
11087978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding = idt;
11097978SPeter.Dunlap@Sun.COM 	buf->idb_ic = idt->idt_ic;
11107978SPeter.Dunlap@Sun.COM 	idm_listbuf_insert(&idt->idt_outbufv, buf);
11117978SPeter.Dunlap@Sun.COM }
11127978SPeter.Dunlap@Sun.COM 
11137978SPeter.Dunlap@Sun.COM void
11147978SPeter.Dunlap@Sun.COM idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
11157978SPeter.Dunlap@Sun.COM {
1116*9162SPeter.Dunlap@Sun.COM 	/*
1117*9162SPeter.Dunlap@Sun.COM 	 * For small transfers, the iSER transport delegates the IDM
1118*9162SPeter.Dunlap@Sun.COM 	 * layer to bcopy the SCSI Read data into the read buufer
1119*9162SPeter.Dunlap@Sun.COM 	 * for faster IOPS.
1120*9162SPeter.Dunlap@Sun.COM 	 */
1121*9162SPeter.Dunlap@Sun.COM 	if (buf->idb_bufbcopy == B_TRUE) {
1122*9162SPeter.Dunlap@Sun.COM 		bcopy(buf->idb_buf, buf->idb_bufptr, buf->idb_buflen);
1123*9162SPeter.Dunlap@Sun.COM 	}
11247978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
11257978SPeter.Dunlap@Sun.COM 	idm_buf_unbind_in_locked(idt, buf);
11267978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
11277978SPeter.Dunlap@Sun.COM }
11287978SPeter.Dunlap@Sun.COM 
11297978SPeter.Dunlap@Sun.COM static void
11307978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf)
11317978SPeter.Dunlap@Sun.COM {
11327978SPeter.Dunlap@Sun.COM 	list_remove(&idt->idt_inbufv, buf);
11337978SPeter.Dunlap@Sun.COM }
11347978SPeter.Dunlap@Sun.COM 
11357978SPeter.Dunlap@Sun.COM void
11367978SPeter.Dunlap@Sun.COM idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
11377978SPeter.Dunlap@Sun.COM {
11387978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
11397978SPeter.Dunlap@Sun.COM 	idm_buf_unbind_out_locked(idt, buf);
11407978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
11417978SPeter.Dunlap@Sun.COM }
11427978SPeter.Dunlap@Sun.COM 
11437978SPeter.Dunlap@Sun.COM static void
11447978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf)
11457978SPeter.Dunlap@Sun.COM {
11467978SPeter.Dunlap@Sun.COM 	list_remove(&idt->idt_outbufv, buf);
11477978SPeter.Dunlap@Sun.COM }
11487978SPeter.Dunlap@Sun.COM 
11497978SPeter.Dunlap@Sun.COM /*
11507978SPeter.Dunlap@Sun.COM  * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the
11517978SPeter.Dunlap@Sun.COM  * iSCSI PDU
11527978SPeter.Dunlap@Sun.COM  */
11537978SPeter.Dunlap@Sun.COM idm_buf_t *
11547978SPeter.Dunlap@Sun.COM idm_buf_find(void *lbuf, size_t data_offset)
11557978SPeter.Dunlap@Sun.COM {
11567978SPeter.Dunlap@Sun.COM 	idm_buf_t	*idb;
11577978SPeter.Dunlap@Sun.COM 	list_t		*lst = (list_t *)lbuf;
11587978SPeter.Dunlap@Sun.COM 
11597978SPeter.Dunlap@Sun.COM 	/* iterate through the list to find the buffer */
11607978SPeter.Dunlap@Sun.COM 	for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
11617978SPeter.Dunlap@Sun.COM 
11627978SPeter.Dunlap@Sun.COM 		ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) ||
11637978SPeter.Dunlap@Sun.COM 		    (idb->idb_bufoffset == 0));
11647978SPeter.Dunlap@Sun.COM 
11657978SPeter.Dunlap@Sun.COM 		if ((data_offset >= idb->idb_bufoffset) &&
11667978SPeter.Dunlap@Sun.COM 		    (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
11677978SPeter.Dunlap@Sun.COM 
11687978SPeter.Dunlap@Sun.COM 			return (idb);
11697978SPeter.Dunlap@Sun.COM 		}
11707978SPeter.Dunlap@Sun.COM 	}
11717978SPeter.Dunlap@Sun.COM 
11727978SPeter.Dunlap@Sun.COM 	return (NULL);
11737978SPeter.Dunlap@Sun.COM }
11747978SPeter.Dunlap@Sun.COM 
1175*9162SPeter.Dunlap@Sun.COM void
1176*9162SPeter.Dunlap@Sun.COM idm_bufpat_set(idm_buf_t *idb)
1177*9162SPeter.Dunlap@Sun.COM {
1178*9162SPeter.Dunlap@Sun.COM 	idm_bufpat_t	*bufpat;
1179*9162SPeter.Dunlap@Sun.COM 	int		len, i;
1180*9162SPeter.Dunlap@Sun.COM 
1181*9162SPeter.Dunlap@Sun.COM 	len = idb->idb_buflen;
1182*9162SPeter.Dunlap@Sun.COM 	len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
1183*9162SPeter.Dunlap@Sun.COM 
1184*9162SPeter.Dunlap@Sun.COM 	bufpat = idb->idb_buf;
1185*9162SPeter.Dunlap@Sun.COM 	for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
1186*9162SPeter.Dunlap@Sun.COM 		bufpat->bufpat_idb = idb;
1187*9162SPeter.Dunlap@Sun.COM 		bufpat->bufpat_bufmagic = IDM_BUF_MAGIC;
1188*9162SPeter.Dunlap@Sun.COM 		bufpat->bufpat_offset = i;
1189*9162SPeter.Dunlap@Sun.COM 		bufpat++;
1190*9162SPeter.Dunlap@Sun.COM 	}
1191*9162SPeter.Dunlap@Sun.COM }
1192*9162SPeter.Dunlap@Sun.COM 
1193*9162SPeter.Dunlap@Sun.COM boolean_t
1194*9162SPeter.Dunlap@Sun.COM idm_bufpat_check(idm_buf_t *idb, int check_len, idm_bufpat_check_type_t type)
1195*9162SPeter.Dunlap@Sun.COM {
1196*9162SPeter.Dunlap@Sun.COM 	idm_bufpat_t	*bufpat;
1197*9162SPeter.Dunlap@Sun.COM 	int		len, i;
1198*9162SPeter.Dunlap@Sun.COM 
1199*9162SPeter.Dunlap@Sun.COM 	len = (type == BP_CHECK_QUICK) ? sizeof (idm_bufpat_t) : check_len;
1200*9162SPeter.Dunlap@Sun.COM 	len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
1201*9162SPeter.Dunlap@Sun.COM 	ASSERT(len <= idb->idb_buflen);
1202*9162SPeter.Dunlap@Sun.COM 	bufpat = idb->idb_buf;
1203*9162SPeter.Dunlap@Sun.COM 
1204*9162SPeter.Dunlap@Sun.COM 	/*
1205*9162SPeter.Dunlap@Sun.COM 	 * Don't check the pattern in buffers that came from outside IDM
1206*9162SPeter.Dunlap@Sun.COM 	 * (these will be buffers from the initiator that we opted not
1207*9162SPeter.Dunlap@Sun.COM 	 * to double-buffer)
1208*9162SPeter.Dunlap@Sun.COM 	 */
1209*9162SPeter.Dunlap@Sun.COM 	if (!idb->idb_bufalloc)
1210*9162SPeter.Dunlap@Sun.COM 		return (B_FALSE);
1211*9162SPeter.Dunlap@Sun.COM 
1212*9162SPeter.Dunlap@Sun.COM 	/*
1213*9162SPeter.Dunlap@Sun.COM 	 * Return true if we find the pattern anywhere in the buffer
1214*9162SPeter.Dunlap@Sun.COM 	 */
1215*9162SPeter.Dunlap@Sun.COM 	for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
1216*9162SPeter.Dunlap@Sun.COM 		if (BUFPAT_MATCH(bufpat, idb)) {
1217*9162SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN, "idm_bufpat_check found: "
1218*9162SPeter.Dunlap@Sun.COM 			    "idb %p bufpat %p "
1219*9162SPeter.Dunlap@Sun.COM 			    "bufpat_idb=%p bufmagic=%08x offset=%08x",
1220*9162SPeter.Dunlap@Sun.COM 			    (void *)idb, (void *)bufpat, bufpat->bufpat_idb,
1221*9162SPeter.Dunlap@Sun.COM 			    bufpat->bufpat_bufmagic, bufpat->bufpat_offset);
1222*9162SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(bufpat__pattern__found,
1223*9162SPeter.Dunlap@Sun.COM 			    idm_buf_t *, idb, idm_bufpat_t *, bufpat);
1224*9162SPeter.Dunlap@Sun.COM 			if (type == BP_CHECK_ASSERT) {
1225*9162SPeter.Dunlap@Sun.COM 				ASSERT(0);
1226*9162SPeter.Dunlap@Sun.COM 			}
1227*9162SPeter.Dunlap@Sun.COM 			return (B_TRUE);
1228*9162SPeter.Dunlap@Sun.COM 		}
1229*9162SPeter.Dunlap@Sun.COM 		bufpat++;
1230*9162SPeter.Dunlap@Sun.COM 	}
1231*9162SPeter.Dunlap@Sun.COM 
1232*9162SPeter.Dunlap@Sun.COM 	return (B_FALSE);
1233*9162SPeter.Dunlap@Sun.COM }
1234*9162SPeter.Dunlap@Sun.COM 
12357978SPeter.Dunlap@Sun.COM /*
12367978SPeter.Dunlap@Sun.COM  * idm_task_alloc
12377978SPeter.Dunlap@Sun.COM  *
12387978SPeter.Dunlap@Sun.COM  * This function will allocate a idm_task_t structure. A task tag is also
12397978SPeter.Dunlap@Sun.COM  * generated and saved in idt_tt. The task is not active.
12407978SPeter.Dunlap@Sun.COM  */
12417978SPeter.Dunlap@Sun.COM idm_task_t *
12427978SPeter.Dunlap@Sun.COM idm_task_alloc(idm_conn_t *ic)
12437978SPeter.Dunlap@Sun.COM {
12447978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt;
12457978SPeter.Dunlap@Sun.COM 
12467978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
12477978SPeter.Dunlap@Sun.COM 
12487978SPeter.Dunlap@Sun.COM 	/* Don't allocate new tasks if we are not in FFP */
12497978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
12507978SPeter.Dunlap@Sun.COM 	if (!ic->ic_ffp) {
12517978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
12527978SPeter.Dunlap@Sun.COM 		return (NULL);
12537978SPeter.Dunlap@Sun.COM 	}
12547978SPeter.Dunlap@Sun.COM 	idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP);
12557978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
12567978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
12577978SPeter.Dunlap@Sun.COM 		return (NULL);
12587978SPeter.Dunlap@Sun.COM 	}
12597978SPeter.Dunlap@Sun.COM 
12607978SPeter.Dunlap@Sun.COM 	ASSERT(list_is_empty(&idt->idt_inbufv));
12617978SPeter.Dunlap@Sun.COM 	ASSERT(list_is_empty(&idt->idt_outbufv));
12627978SPeter.Dunlap@Sun.COM 
12637978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
12647978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
12657978SPeter.Dunlap@Sun.COM 
12667978SPeter.Dunlap@Sun.COM 	idt->idt_state		= TASK_IDLE;
12677978SPeter.Dunlap@Sun.COM 	idt->idt_ic		= ic;
12687978SPeter.Dunlap@Sun.COM 	idt->idt_private 	= NULL;
12697978SPeter.Dunlap@Sun.COM 	idt->idt_exp_datasn	= 0;
12707978SPeter.Dunlap@Sun.COM 	idt->idt_exp_rttsn	= 0;
12717978SPeter.Dunlap@Sun.COM 
12727978SPeter.Dunlap@Sun.COM 	return (idt);
12737978SPeter.Dunlap@Sun.COM }
12747978SPeter.Dunlap@Sun.COM 
12757978SPeter.Dunlap@Sun.COM /*
12767978SPeter.Dunlap@Sun.COM  * idm_task_start
12777978SPeter.Dunlap@Sun.COM  *
1278*9162SPeter.Dunlap@Sun.COM  * Mark the task active and initialize some stats. The caller
12797978SPeter.Dunlap@Sun.COM  * sets up the idm_task_t structure with a prior call to idm_task_alloc().
12807978SPeter.Dunlap@Sun.COM  * The task service does not function as a task/work engine, it is the
12817978SPeter.Dunlap@Sun.COM  * responsibility of the initiator to start the data transfer and free the
12827978SPeter.Dunlap@Sun.COM  * resources.
12837978SPeter.Dunlap@Sun.COM  */
12847978SPeter.Dunlap@Sun.COM void
12857978SPeter.Dunlap@Sun.COM idm_task_start(idm_task_t *idt, uintptr_t handle)
12867978SPeter.Dunlap@Sun.COM {
12877978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
12887978SPeter.Dunlap@Sun.COM 
12897978SPeter.Dunlap@Sun.COM 	/* mark the task as ACTIVE */
12907978SPeter.Dunlap@Sun.COM 	idt->idt_state = TASK_ACTIVE;
12917978SPeter.Dunlap@Sun.COM 	idt->idt_client_handle = handle;
12927978SPeter.Dunlap@Sun.COM 	idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
1293*9162SPeter.Dunlap@Sun.COM 	    idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done =
1294*9162SPeter.Dunlap@Sun.COM 	    idt->idt_tx_bytes = idt->idt_rx_bytes = 0;
12957978SPeter.Dunlap@Sun.COM }
12967978SPeter.Dunlap@Sun.COM 
12977978SPeter.Dunlap@Sun.COM /*
12987978SPeter.Dunlap@Sun.COM  * idm_task_done
12997978SPeter.Dunlap@Sun.COM  *
1300*9162SPeter.Dunlap@Sun.COM  * This function sets the state to indicate that the task is no longer active.
13017978SPeter.Dunlap@Sun.COM  */
13027978SPeter.Dunlap@Sun.COM void
13037978SPeter.Dunlap@Sun.COM idm_task_done(idm_task_t *idt)
13047978SPeter.Dunlap@Sun.COM {
13057978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
1306*9162SPeter.Dunlap@Sun.COM 
1307*9162SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
1308*9162SPeter.Dunlap@Sun.COM 	idt->idt_state = TASK_IDLE;
1309*9162SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
13107978SPeter.Dunlap@Sun.COM 
1311*9162SPeter.Dunlap@Sun.COM 	/*
1312*9162SPeter.Dunlap@Sun.COM 	 * Although unlikely it is possible for a reference to come in after
1313*9162SPeter.Dunlap@Sun.COM 	 * the client has decided the task is over but before we've marked
1314*9162SPeter.Dunlap@Sun.COM 	 * the task idle.  One specific unavoidable scenario is the case where
1315*9162SPeter.Dunlap@Sun.COM 	 * received PDU with the matching ITT/TTT results in a successful
1316*9162SPeter.Dunlap@Sun.COM 	 * lookup of this task.  We are at the mercy of the remote node in
1317*9162SPeter.Dunlap@Sun.COM 	 * that case so we need to handle it.  Now that the task state
1318*9162SPeter.Dunlap@Sun.COM 	 * has changed no more references will occur so a simple call to
1319*9162SPeter.Dunlap@Sun.COM 	 * idm_refcnt_wait_ref should deal with the situation.
1320*9162SPeter.Dunlap@Sun.COM 	 */
1321*9162SPeter.Dunlap@Sun.COM 	idm_refcnt_wait_ref(&idt->idt_refcnt);
13227978SPeter.Dunlap@Sun.COM 	idm_refcnt_reset(&idt->idt_refcnt);
13237978SPeter.Dunlap@Sun.COM }
13247978SPeter.Dunlap@Sun.COM 
13257978SPeter.Dunlap@Sun.COM /*
13267978SPeter.Dunlap@Sun.COM  * idm_task_free
13277978SPeter.Dunlap@Sun.COM  *
13287978SPeter.Dunlap@Sun.COM  * This function will free the Task Tag and the memory allocated for the task
13297978SPeter.Dunlap@Sun.COM  * idm_task_done should be called prior to this call
13307978SPeter.Dunlap@Sun.COM  */
13317978SPeter.Dunlap@Sun.COM void
13327978SPeter.Dunlap@Sun.COM idm_task_free(idm_task_t *idt)
13337978SPeter.Dunlap@Sun.COM {
1334*9162SPeter.Dunlap@Sun.COM 	idm_conn_t *ic;
13357978SPeter.Dunlap@Sun.COM 
13367978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
1337*9162SPeter.Dunlap@Sun.COM 	ASSERT(idt->idt_refcnt.ir_refcnt == 0);
13387978SPeter.Dunlap@Sun.COM 	ASSERT(idt->idt_state == TASK_IDLE);
13397978SPeter.Dunlap@Sun.COM 
1340*9162SPeter.Dunlap@Sun.COM 	ic = idt->idt_ic;
1341*9162SPeter.Dunlap@Sun.COM 
13427978SPeter.Dunlap@Sun.COM 	/*
13437978SPeter.Dunlap@Sun.COM 	 * It's possible for items to still be in the idt_inbufv list if
13447978SPeter.Dunlap@Sun.COM 	 * they were added after idm_task_cleanup was called.  We rely on
13457978SPeter.Dunlap@Sun.COM 	 * STMF to free all buffers associated with the task however STMF
13467978SPeter.Dunlap@Sun.COM 	 * doesn't know that we have this reference to the buffers.
13477978SPeter.Dunlap@Sun.COM 	 * Use list_create so that we don't end up with stale references
13487978SPeter.Dunlap@Sun.COM 	 * to these buffers.
13497978SPeter.Dunlap@Sun.COM 	 */
13507978SPeter.Dunlap@Sun.COM 	list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
13517978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
13527978SPeter.Dunlap@Sun.COM 	list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
13537978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
13547978SPeter.Dunlap@Sun.COM 
13557978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_task_cache, idt);
13567978SPeter.Dunlap@Sun.COM 
13577978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
13587978SPeter.Dunlap@Sun.COM }
13597978SPeter.Dunlap@Sun.COM 
13607978SPeter.Dunlap@Sun.COM /*
1361*9162SPeter.Dunlap@Sun.COM  * idm_task_find_common
1362*9162SPeter.Dunlap@Sun.COM  *	common code for idm_task_find() and idm_task_find_and_complete()
13637978SPeter.Dunlap@Sun.COM  */
13647978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
1365*9162SPeter.Dunlap@Sun.COM static idm_task_t *
1366*9162SPeter.Dunlap@Sun.COM idm_task_find_common(idm_conn_t *ic, uint32_t itt, uint32_t ttt,
1367*9162SPeter.Dunlap@Sun.COM     boolean_t complete)
13687978SPeter.Dunlap@Sun.COM {
13697978SPeter.Dunlap@Sun.COM 	uint32_t	tt, client_handle;
13707978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt;
13717978SPeter.Dunlap@Sun.COM 
13727978SPeter.Dunlap@Sun.COM 	/*
13737978SPeter.Dunlap@Sun.COM 	 * Must match both itt and ttt.  The table is indexed by itt
13747978SPeter.Dunlap@Sun.COM 	 * for initiator connections and ttt for target connections.
13757978SPeter.Dunlap@Sun.COM 	 */
13767978SPeter.Dunlap@Sun.COM 	if (IDM_CONN_ISTGT(ic)) {
13777978SPeter.Dunlap@Sun.COM 		tt = ttt;
13787978SPeter.Dunlap@Sun.COM 		client_handle = itt;
13797978SPeter.Dunlap@Sun.COM 	} else {
13807978SPeter.Dunlap@Sun.COM 		tt = itt;
13817978SPeter.Dunlap@Sun.COM 		client_handle = ttt;
13827978SPeter.Dunlap@Sun.COM 	}
13837978SPeter.Dunlap@Sun.COM 
13847978SPeter.Dunlap@Sun.COM 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
13857978SPeter.Dunlap@Sun.COM 	if (tt >= idm.idm_taskid_max) {
13867978SPeter.Dunlap@Sun.COM 		rw_exit(&idm.idm_taskid_table_lock);
13877978SPeter.Dunlap@Sun.COM 		return (NULL);
13887978SPeter.Dunlap@Sun.COM 	}
13897978SPeter.Dunlap@Sun.COM 
13907978SPeter.Dunlap@Sun.COM 	idt = idm.idm_taskid_table[tt];
13917978SPeter.Dunlap@Sun.COM 
13927978SPeter.Dunlap@Sun.COM 	if (idt != NULL) {
13937978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
13947978SPeter.Dunlap@Sun.COM 		if ((idt->idt_state != TASK_ACTIVE) ||
1395*9162SPeter.Dunlap@Sun.COM 		    (idt->idt_ic != ic) ||
13967978SPeter.Dunlap@Sun.COM 		    (IDM_CONN_ISTGT(ic) &&
13977978SPeter.Dunlap@Sun.COM 		    (idt->idt_client_handle != client_handle))) {
13987978SPeter.Dunlap@Sun.COM 			/*
1399*9162SPeter.Dunlap@Sun.COM 			 * Task doesn't match or task is aborting and
1400*9162SPeter.Dunlap@Sun.COM 			 * we don't want any more references.
14017978SPeter.Dunlap@Sun.COM 			 */
1402*9162SPeter.Dunlap@Sun.COM 			if ((idt->idt_ic != ic) &&
1403*9162SPeter.Dunlap@Sun.COM 			    (idt->idt_state == TASK_ACTIVE) &&
1404*9162SPeter.Dunlap@Sun.COM 			    (IDM_CONN_ISINI(ic) || idt->idt_client_handle ==
1405*9162SPeter.Dunlap@Sun.COM 			    client_handle)) {
1406*9162SPeter.Dunlap@Sun.COM 				IDM_CONN_LOG(CE_WARN,
1407*9162SPeter.Dunlap@Sun.COM 				"idm_task_find: wrong connection %p != %p",
1408*9162SPeter.Dunlap@Sun.COM 				    (void *)ic, (void *)idt->idt_ic);
1409*9162SPeter.Dunlap@Sun.COM 			}
14107978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
14117978SPeter.Dunlap@Sun.COM 			rw_exit(&idm.idm_taskid_table_lock);
14127978SPeter.Dunlap@Sun.COM 			return (NULL);
14137978SPeter.Dunlap@Sun.COM 		}
14147978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
1415*9162SPeter.Dunlap@Sun.COM 		/*
1416*9162SPeter.Dunlap@Sun.COM 		 * Set the task state to TASK_COMPLETE so it can no longer
1417*9162SPeter.Dunlap@Sun.COM 		 * be found or aborted.
1418*9162SPeter.Dunlap@Sun.COM 		 */
1419*9162SPeter.Dunlap@Sun.COM 		if (B_TRUE == complete)
1420*9162SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_COMPLETE;
14217978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
14227978SPeter.Dunlap@Sun.COM 	}
14237978SPeter.Dunlap@Sun.COM 	rw_exit(&idm.idm_taskid_table_lock);
14247978SPeter.Dunlap@Sun.COM 
14257978SPeter.Dunlap@Sun.COM 	return (idt);
14267978SPeter.Dunlap@Sun.COM }
14277978SPeter.Dunlap@Sun.COM 
14287978SPeter.Dunlap@Sun.COM /*
1429*9162SPeter.Dunlap@Sun.COM  * This function looks up a task by task tag.
1430*9162SPeter.Dunlap@Sun.COM  */
1431*9162SPeter.Dunlap@Sun.COM idm_task_t *
1432*9162SPeter.Dunlap@Sun.COM idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
1433*9162SPeter.Dunlap@Sun.COM {
1434*9162SPeter.Dunlap@Sun.COM 	return (idm_task_find_common(ic, itt, ttt, B_FALSE));
1435*9162SPeter.Dunlap@Sun.COM }
1436*9162SPeter.Dunlap@Sun.COM 
1437*9162SPeter.Dunlap@Sun.COM /*
1438*9162SPeter.Dunlap@Sun.COM  * This function looks up a task by task tag. If found, the task state
1439*9162SPeter.Dunlap@Sun.COM  * is atomically set to TASK_COMPLETE so it can longer be found or aborted.
1440*9162SPeter.Dunlap@Sun.COM  */
1441*9162SPeter.Dunlap@Sun.COM idm_task_t *
1442*9162SPeter.Dunlap@Sun.COM idm_task_find_and_complete(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
1443*9162SPeter.Dunlap@Sun.COM {
1444*9162SPeter.Dunlap@Sun.COM 	return (idm_task_find_common(ic, itt, ttt, B_TRUE));
1445*9162SPeter.Dunlap@Sun.COM }
1446*9162SPeter.Dunlap@Sun.COM 
1447*9162SPeter.Dunlap@Sun.COM /*
14487978SPeter.Dunlap@Sun.COM  * idm_task_find_by_handle
14497978SPeter.Dunlap@Sun.COM  *
14507978SPeter.Dunlap@Sun.COM  * This function looks up a task by the client-private idt_client_handle.
14517978SPeter.Dunlap@Sun.COM  *
14527978SPeter.Dunlap@Sun.COM  * This function should NEVER be called in the performance path.  It is
14537978SPeter.Dunlap@Sun.COM  * intended strictly for error recovery/task management.
14547978SPeter.Dunlap@Sun.COM  */
14557978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
14567978SPeter.Dunlap@Sun.COM void *
14577978SPeter.Dunlap@Sun.COM idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle)
14587978SPeter.Dunlap@Sun.COM {
14597978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt = NULL;
14607978SPeter.Dunlap@Sun.COM 	int		idx = 0;
14617978SPeter.Dunlap@Sun.COM 
14627978SPeter.Dunlap@Sun.COM 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
14637978SPeter.Dunlap@Sun.COM 
14647978SPeter.Dunlap@Sun.COM 	for (idx = 0; idx < idm.idm_taskid_max; idx++) {
14657978SPeter.Dunlap@Sun.COM 		idt = idm.idm_taskid_table[idx];
14667978SPeter.Dunlap@Sun.COM 
14677978SPeter.Dunlap@Sun.COM 		if (idt == NULL)
14687978SPeter.Dunlap@Sun.COM 			continue;
14697978SPeter.Dunlap@Sun.COM 
14707978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
14717978SPeter.Dunlap@Sun.COM 
14727978SPeter.Dunlap@Sun.COM 		if (idt->idt_state != TASK_ACTIVE) {
14737978SPeter.Dunlap@Sun.COM 			/*
14747978SPeter.Dunlap@Sun.COM 			 * Task is either in suspend, abort, or already
14757978SPeter.Dunlap@Sun.COM 			 * complete.
14767978SPeter.Dunlap@Sun.COM 			 */
14777978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
14787978SPeter.Dunlap@Sun.COM 			continue;
14797978SPeter.Dunlap@Sun.COM 		}
14807978SPeter.Dunlap@Sun.COM 
14817978SPeter.Dunlap@Sun.COM 		if (idt->idt_client_handle == handle) {
14827978SPeter.Dunlap@Sun.COM 			idm_task_hold(idt);
14837978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
14847978SPeter.Dunlap@Sun.COM 			break;
14857978SPeter.Dunlap@Sun.COM 		}
14867978SPeter.Dunlap@Sun.COM 
14877978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
14887978SPeter.Dunlap@Sun.COM 	}
14897978SPeter.Dunlap@Sun.COM 
14907978SPeter.Dunlap@Sun.COM 	rw_exit(&idm.idm_taskid_table_lock);
14917978SPeter.Dunlap@Sun.COM 
14927978SPeter.Dunlap@Sun.COM 	if ((idt == NULL) || (idx == idm.idm_taskid_max))
14937978SPeter.Dunlap@Sun.COM 		return (NULL);
14947978SPeter.Dunlap@Sun.COM 
14957978SPeter.Dunlap@Sun.COM 	return (idt->idt_private);
14967978SPeter.Dunlap@Sun.COM }
14977978SPeter.Dunlap@Sun.COM 
14987978SPeter.Dunlap@Sun.COM void
14997978SPeter.Dunlap@Sun.COM idm_task_hold(idm_task_t *idt)
15007978SPeter.Dunlap@Sun.COM {
15017978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&idt->idt_refcnt);
15027978SPeter.Dunlap@Sun.COM }
15037978SPeter.Dunlap@Sun.COM 
15047978SPeter.Dunlap@Sun.COM void
15057978SPeter.Dunlap@Sun.COM idm_task_rele(idm_task_t *idt)
15067978SPeter.Dunlap@Sun.COM {
15077978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&idt->idt_refcnt);
15087978SPeter.Dunlap@Sun.COM }
15097978SPeter.Dunlap@Sun.COM 
15107978SPeter.Dunlap@Sun.COM void
15117978SPeter.Dunlap@Sun.COM idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
15127978SPeter.Dunlap@Sun.COM {
15137978SPeter.Dunlap@Sun.COM 	idm_task_t	*task;
15147978SPeter.Dunlap@Sun.COM 	int		idx;
15157978SPeter.Dunlap@Sun.COM 
15167978SPeter.Dunlap@Sun.COM 	/*
15177978SPeter.Dunlap@Sun.COM 	 * Passing NULL as the task indicates that all tasks
15187978SPeter.Dunlap@Sun.COM 	 * for this connection should be aborted.
15197978SPeter.Dunlap@Sun.COM 	 */
15207978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
15217978SPeter.Dunlap@Sun.COM 		/*
15227978SPeter.Dunlap@Sun.COM 		 * Only the connection state machine should ask for
15237978SPeter.Dunlap@Sun.COM 		 * all tasks to abort and this should never happen in FFP.
15247978SPeter.Dunlap@Sun.COM 		 */
15257978SPeter.Dunlap@Sun.COM 		ASSERT(!ic->ic_ffp);
15267978SPeter.Dunlap@Sun.COM 		rw_enter(&idm.idm_taskid_table_lock, RW_READER);
15277978SPeter.Dunlap@Sun.COM 		for (idx = 0; idx < idm.idm_taskid_max; idx++) {
15287978SPeter.Dunlap@Sun.COM 			task = idm.idm_taskid_table[idx];
1529*9162SPeter.Dunlap@Sun.COM 			if (task == NULL)
1530*9162SPeter.Dunlap@Sun.COM 				continue;
1531*9162SPeter.Dunlap@Sun.COM 			mutex_enter(&task->idt_mutex);
1532*9162SPeter.Dunlap@Sun.COM 			if ((task->idt_state != TASK_IDLE) &&
1533*9162SPeter.Dunlap@Sun.COM 			    (task->idt_state != TASK_COMPLETE) &&
15347978SPeter.Dunlap@Sun.COM 			    (task->idt_ic == ic)) {
15357978SPeter.Dunlap@Sun.COM 				rw_exit(&idm.idm_taskid_table_lock);
15367978SPeter.Dunlap@Sun.COM 				idm_task_abort_one(ic, task, abort_type);
15377978SPeter.Dunlap@Sun.COM 				rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1538*9162SPeter.Dunlap@Sun.COM 			} else
1539*9162SPeter.Dunlap@Sun.COM 				mutex_exit(&task->idt_mutex);
15407978SPeter.Dunlap@Sun.COM 		}
15417978SPeter.Dunlap@Sun.COM 		rw_exit(&idm.idm_taskid_table_lock);
15427978SPeter.Dunlap@Sun.COM 	} else {
1543*9162SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
15447978SPeter.Dunlap@Sun.COM 		idm_task_abort_one(ic, idt, abort_type);
15457978SPeter.Dunlap@Sun.COM 	}
15467978SPeter.Dunlap@Sun.COM }
15477978SPeter.Dunlap@Sun.COM 
15487978SPeter.Dunlap@Sun.COM static void
15497978SPeter.Dunlap@Sun.COM idm_task_abort_unref_cb(void *ref)
15507978SPeter.Dunlap@Sun.COM {
15517978SPeter.Dunlap@Sun.COM 	idm_task_t *idt = ref;
15527978SPeter.Dunlap@Sun.COM 
15537978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
15547978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
15557978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
15567978SPeter.Dunlap@Sun.COM 		idt->idt_state = TASK_SUSPENDED;
15577978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15587978SPeter.Dunlap@Sun.COM 		idm_task_aborted(idt, IDM_STATUS_SUSPENDED);
15597978SPeter.Dunlap@Sun.COM 		return;
15607978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
15617978SPeter.Dunlap@Sun.COM 		idt->idt_state = TASK_ABORTED;
15627978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15637978SPeter.Dunlap@Sun.COM 		idm_task_aborted(idt, IDM_STATUS_ABORTED);
15647978SPeter.Dunlap@Sun.COM 		return;
15657978SPeter.Dunlap@Sun.COM 	default:
15667978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15677978SPeter.Dunlap@Sun.COM 		ASSERT(0);
15687978SPeter.Dunlap@Sun.COM 		break;
15697978SPeter.Dunlap@Sun.COM 	}
15707978SPeter.Dunlap@Sun.COM }
15717978SPeter.Dunlap@Sun.COM 
1572*9162SPeter.Dunlap@Sun.COM /*
1573*9162SPeter.Dunlap@Sun.COM  * Abort the idm task.
1574*9162SPeter.Dunlap@Sun.COM  *    Caller must hold the task mutex, which will be released before return
1575*9162SPeter.Dunlap@Sun.COM  */
15767978SPeter.Dunlap@Sun.COM static void
15777978SPeter.Dunlap@Sun.COM idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
15787978SPeter.Dunlap@Sun.COM {
15797978SPeter.Dunlap@Sun.COM 	/* Caller must hold connection mutex */
1580*9162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
15817978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
15827978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
15837978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
15847978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
15857978SPeter.Dunlap@Sun.COM 			/* Call transport to release any resources */
15867978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_SUSPENDING;
15877978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
15887978SPeter.Dunlap@Sun.COM 			ic->ic_transport_ops->it_free_task_rsrc(idt);
15897978SPeter.Dunlap@Sun.COM 
15907978SPeter.Dunlap@Sun.COM 			/*
15917978SPeter.Dunlap@Sun.COM 			 * Wait for outstanding references.  When all
15927978SPeter.Dunlap@Sun.COM 			 * references are released the callback will call
15937978SPeter.Dunlap@Sun.COM 			 * idm_task_aborted().
15947978SPeter.Dunlap@Sun.COM 			 */
15957978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
15967978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
15977978SPeter.Dunlap@Sun.COM 			return;
15987978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
15997978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
16007978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
16017978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
16027978SPeter.Dunlap@Sun.COM 			ic->ic_transport_ops->it_free_task_rsrc(idt);
16037978SPeter.Dunlap@Sun.COM 
16047978SPeter.Dunlap@Sun.COM 			/*
16057978SPeter.Dunlap@Sun.COM 			 * Wait for outstanding references.  When all
16067978SPeter.Dunlap@Sun.COM 			 * references are released the callback will call
16077978SPeter.Dunlap@Sun.COM 			 * idm_task_aborted().
16087978SPeter.Dunlap@Sun.COM 			 */
16097978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
16107978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
16117978SPeter.Dunlap@Sun.COM 			return;
16127978SPeter.Dunlap@Sun.COM 		default:
16137978SPeter.Dunlap@Sun.COM 			ASSERT(0);
16147978SPeter.Dunlap@Sun.COM 		}
16157978SPeter.Dunlap@Sun.COM 		break;
16167978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
16177978SPeter.Dunlap@Sun.COM 		/* Already called transport_free_task_rsrc(); */
16187978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
16197978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
16207978SPeter.Dunlap@Sun.COM 			/* Already doing it */
16217978SPeter.Dunlap@Sun.COM 			break;
16227978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
16237978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
16247978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
16257978SPeter.Dunlap@Sun.COM 			break;
16267978SPeter.Dunlap@Sun.COM 		default:
16277978SPeter.Dunlap@Sun.COM 			ASSERT(0);
16287978SPeter.Dunlap@Sun.COM 		}
16297978SPeter.Dunlap@Sun.COM 		break;
16307978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
16317978SPeter.Dunlap@Sun.COM 		/* Already called transport_free_task_rsrc(); */
16327978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
16337978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
16347978SPeter.Dunlap@Sun.COM 			/* Already doing it */
16357978SPeter.Dunlap@Sun.COM 			break;
16367978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
16377978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
16387978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
16397978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
16407978SPeter.Dunlap@Sun.COM 
16417978SPeter.Dunlap@Sun.COM 			/*
16427978SPeter.Dunlap@Sun.COM 			 * We could probably call idm_task_aborted directly
16437978SPeter.Dunlap@Sun.COM 			 * here but we may be holding the conn lock. It's
16447978SPeter.Dunlap@Sun.COM 			 * easier to just switch contexts.  Even though
16457978SPeter.Dunlap@Sun.COM 			 * we shouldn't really have any references we'll
16467978SPeter.Dunlap@Sun.COM 			 * set the state to TASK_ABORTING instead of
16477978SPeter.Dunlap@Sun.COM 			 * TASK_ABORTED so we can use the same code path.
16487978SPeter.Dunlap@Sun.COM 			 */
16497978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
16507978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
16517978SPeter.Dunlap@Sun.COM 			return;
16527978SPeter.Dunlap@Sun.COM 		default:
16537978SPeter.Dunlap@Sun.COM 			ASSERT(0);
16547978SPeter.Dunlap@Sun.COM 		}
16557978SPeter.Dunlap@Sun.COM 		break;
16567978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
16577978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
16587978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
16597978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
16607978SPeter.Dunlap@Sun.COM 			/* We're already past this point... */
16617978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
16627978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
16637978SPeter.Dunlap@Sun.COM 			/* Already doing it */
16647978SPeter.Dunlap@Sun.COM 			break;
16657978SPeter.Dunlap@Sun.COM 		default:
16667978SPeter.Dunlap@Sun.COM 			ASSERT(0);
16677978SPeter.Dunlap@Sun.COM 		}
16687978SPeter.Dunlap@Sun.COM 		break;
16697978SPeter.Dunlap@Sun.COM 	case TASK_COMPLETE:
16707978SPeter.Dunlap@Sun.COM 		/*
16717978SPeter.Dunlap@Sun.COM 		 * In this case, let it go.  The status has already been
16727978SPeter.Dunlap@Sun.COM 		 * sent (which may or may not get successfully transmitted)
16737978SPeter.Dunlap@Sun.COM 		 * and we don't want to end up in a race between completing
16747978SPeter.Dunlap@Sun.COM 		 * the status PDU and marking the task suspended.
16757978SPeter.Dunlap@Sun.COM 		 */
16767978SPeter.Dunlap@Sun.COM 		break;
16777978SPeter.Dunlap@Sun.COM 	default:
16787978SPeter.Dunlap@Sun.COM 		ASSERT(0);
16797978SPeter.Dunlap@Sun.COM 	}
16807978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
16817978SPeter.Dunlap@Sun.COM }
16827978SPeter.Dunlap@Sun.COM 
16837978SPeter.Dunlap@Sun.COM static void
16847978SPeter.Dunlap@Sun.COM idm_task_aborted(idm_task_t *idt, idm_status_t status)
16857978SPeter.Dunlap@Sun.COM {
16867978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status);
16877978SPeter.Dunlap@Sun.COM }
16887978SPeter.Dunlap@Sun.COM 
16897978SPeter.Dunlap@Sun.COM void
16907978SPeter.Dunlap@Sun.COM idm_task_cleanup(idm_task_t *idt)
16917978SPeter.Dunlap@Sun.COM {
16927978SPeter.Dunlap@Sun.COM 	idm_buf_t *idb, *next_idb;
16937978SPeter.Dunlap@Sun.COM 	list_t		tmp_buflist;
16947978SPeter.Dunlap@Sun.COM 	ASSERT((idt->idt_state == TASK_SUSPENDED) ||
16957978SPeter.Dunlap@Sun.COM 	    (idt->idt_state == TASK_ABORTED));
16967978SPeter.Dunlap@Sun.COM 
16977978SPeter.Dunlap@Sun.COM 	list_create(&tmp_buflist, sizeof (idm_buf_t),
16987978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
16997978SPeter.Dunlap@Sun.COM 
17007978SPeter.Dunlap@Sun.COM 	/*
17017978SPeter.Dunlap@Sun.COM 	 * Remove all the buffers from the task and add them to a
17027978SPeter.Dunlap@Sun.COM 	 * temporary local list -- we do this so that we can hold
17037978SPeter.Dunlap@Sun.COM 	 * the task lock and prevent the task from going away if
17047978SPeter.Dunlap@Sun.COM 	 * the client decides to call idm_task_done/idm_task_free.
17057978SPeter.Dunlap@Sun.COM 	 * This could happen during abort in iscsit.
17067978SPeter.Dunlap@Sun.COM 	 */
17077978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
17087978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_inbufv);
17097978SPeter.Dunlap@Sun.COM 	    idb != NULL;
17107978SPeter.Dunlap@Sun.COM 	    idb = next_idb) {
17117978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&idt->idt_inbufv, idb);
17127978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_in_locked(idt, idb);
17137978SPeter.Dunlap@Sun.COM 		list_insert_tail(&tmp_buflist, idb);
17147978SPeter.Dunlap@Sun.COM 	}
17157978SPeter.Dunlap@Sun.COM 
17167978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_outbufv);
17177978SPeter.Dunlap@Sun.COM 	    idb != NULL;
17187978SPeter.Dunlap@Sun.COM 	    idb = next_idb) {
17197978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&idt->idt_outbufv, idb);
17207978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_out_locked(idt, idb);
17217978SPeter.Dunlap@Sun.COM 		list_insert_tail(&tmp_buflist, idb);
17227978SPeter.Dunlap@Sun.COM 	}
17237978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
17247978SPeter.Dunlap@Sun.COM 
17257978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) {
17267978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&tmp_buflist, idb);
17277978SPeter.Dunlap@Sun.COM 		list_remove(&tmp_buflist, idb);
17287978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED);
17297978SPeter.Dunlap@Sun.COM 	}
17307978SPeter.Dunlap@Sun.COM 	list_destroy(&tmp_buflist);
17317978SPeter.Dunlap@Sun.COM }
17327978SPeter.Dunlap@Sun.COM 
17337978SPeter.Dunlap@Sun.COM 
17347978SPeter.Dunlap@Sun.COM /*
17357978SPeter.Dunlap@Sun.COM  * idm_pdu_tx
17367978SPeter.Dunlap@Sun.COM  *
17377978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Send_Control' operational primitive.
17387978SPeter.Dunlap@Sun.COM  * This function is invoked by an initiator iSCSI layer requesting the transfer
17397978SPeter.Dunlap@Sun.COM  * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
17407978SPeter.Dunlap@Sun.COM  * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover
17417978SPeter.Dunlap@Sun.COM  * layer to the peer iSCSI layer in the remote iSCSI node. The connection info
17427978SPeter.Dunlap@Sun.COM  * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size
17437978SPeter.Dunlap@Sun.COM  * are provided as input.
17447978SPeter.Dunlap@Sun.COM  *
17457978SPeter.Dunlap@Sun.COM  */
17467978SPeter.Dunlap@Sun.COM void
17477978SPeter.Dunlap@Sun.COM idm_pdu_tx(idm_pdu_t *pdu)
17487978SPeter.Dunlap@Sun.COM {
17497978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic = pdu->isp_ic;
17507978SPeter.Dunlap@Sun.COM 	iscsi_async_evt_hdr_t	*async_evt;
17517978SPeter.Dunlap@Sun.COM 
17527978SPeter.Dunlap@Sun.COM 	/*
17537978SPeter.Dunlap@Sun.COM 	 * If we are in full-featured mode then route SCSI-related
17547978SPeter.Dunlap@Sun.COM 	 * commands to the appropriate function vector without checking
17557978SPeter.Dunlap@Sun.COM 	 * the connection state.  We will only be in full-feature mode
17567978SPeter.Dunlap@Sun.COM 	 * when we are in an acceptable state for SCSI PDU's.
17577978SPeter.Dunlap@Sun.COM 	 *
17587978SPeter.Dunlap@Sun.COM 	 * We also need to ensure that there are no PDU events outstanding
17597978SPeter.Dunlap@Sun.COM 	 * on the state machine.  Any non-SCSI PDU's received in full-feature
17607978SPeter.Dunlap@Sun.COM 	 * mode will result in PDU events and until these have been handled
17617978SPeter.Dunlap@Sun.COM 	 * we need to route all PDU's through the state machine as PDU
17627978SPeter.Dunlap@Sun.COM 	 * events to maintain ordering.
17637978SPeter.Dunlap@Sun.COM 	 *
17647978SPeter.Dunlap@Sun.COM 	 * Note that IDM cannot enter FFP mode until it processes in
17657978SPeter.Dunlap@Sun.COM 	 * its state machine the last xmit of the login process.
17667978SPeter.Dunlap@Sun.COM 	 * Hence, checking the IDM_PDU_LOGIN_TX flag here would be
17677978SPeter.Dunlap@Sun.COM 	 * superfluous.
17687978SPeter.Dunlap@Sun.COM 	 */
17697978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
17707978SPeter.Dunlap@Sun.COM 	if (ic->ic_ffp && (ic->ic_pdu_events == 0)) {
17717978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
17727978SPeter.Dunlap@Sun.COM 		switch (IDM_PDU_OPCODE(pdu)) {
17737978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_RSP:
17747978SPeter.Dunlap@Sun.COM 			/* Target only */
17757978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17767978SPeter.Dunlap@Sun.COM 			return;
17777978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_TASK_MGT_RSP:
17787978SPeter.Dunlap@Sun.COM 			/* Target only */
17797978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17807978SPeter.Dunlap@Sun.COM 			return;
17817978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_DATA_RSP:
17827978SPeter.Dunlap@Sun.COM 			/* Target only */
17837978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17847978SPeter.Dunlap@Sun.COM 			return;
17857978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_RTT_RSP:
17867978SPeter.Dunlap@Sun.COM 			/* Target only */
17877978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17887978SPeter.Dunlap@Sun.COM 			return;
17897978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_NOOP_IN:
17907978SPeter.Dunlap@Sun.COM 			/* Target only */
17917978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17927978SPeter.Dunlap@Sun.COM 			return;
17937978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_TEXT_RSP:
17947978SPeter.Dunlap@Sun.COM 			/* Target only */
17957978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
17967978SPeter.Dunlap@Sun.COM 			return;
17977978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_TEXT_CMD:
17987978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_NOOP_OUT:
17997978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_CMD:
18007978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_DATA:
18017978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_TASK_MGT_MSG:
18027978SPeter.Dunlap@Sun.COM 			/* Initiator only */
18037978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
18047978SPeter.Dunlap@Sun.COM 			return;
18057978SPeter.Dunlap@Sun.COM 		default:
18067978SPeter.Dunlap@Sun.COM 			break;
18077978SPeter.Dunlap@Sun.COM 		}
18087978SPeter.Dunlap@Sun.COM 
18097978SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_state_mutex);
18107978SPeter.Dunlap@Sun.COM 	}
18117978SPeter.Dunlap@Sun.COM 
18127978SPeter.Dunlap@Sun.COM 	/*
18137978SPeter.Dunlap@Sun.COM 	 * Any PDU's processed outside of full-feature mode and non-SCSI
18147978SPeter.Dunlap@Sun.COM 	 * PDU's in full-feature mode are handled by generating an
18157978SPeter.Dunlap@Sun.COM 	 * event to the connection state machine.  The state machine
18167978SPeter.Dunlap@Sun.COM 	 * will validate the PDU against the current state and either
18177978SPeter.Dunlap@Sun.COM 	 * transmit the PDU if the opcode is allowed or handle an
18187978SPeter.Dunlap@Sun.COM 	 * error if the PDU is not allowed.
18197978SPeter.Dunlap@Sun.COM 	 *
18207978SPeter.Dunlap@Sun.COM 	 * This code-path will also generate any events that are implied
18217978SPeter.Dunlap@Sun.COM 	 * by the PDU opcode.  For example a "login response" with success
18227978SPeter.Dunlap@Sun.COM 	 * status generates a CE_LOGOUT_SUCCESS_SND event.
18237978SPeter.Dunlap@Sun.COM 	 */
18247978SPeter.Dunlap@Sun.COM 	switch (IDM_PDU_OPCODE(pdu)) {
18257978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_CMD:
18267978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
18277978SPeter.Dunlap@Sun.COM 		break;
18287978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_RSP:
18297978SPeter.Dunlap@Sun.COM 		idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE);
18307978SPeter.Dunlap@Sun.COM 		break;
18317978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_CMD:
18327978SPeter.Dunlap@Sun.COM 		idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE);
18337978SPeter.Dunlap@Sun.COM 		break;
18347978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_RSP:
18357978SPeter.Dunlap@Sun.COM 		idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE);
18367978SPeter.Dunlap@Sun.COM 		break;
18377978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_ASYNC_EVENT:
18387978SPeter.Dunlap@Sun.COM 		async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
18397978SPeter.Dunlap@Sun.COM 		switch (async_evt->async_event) {
18407978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
18417978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
18427978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
18437978SPeter.Dunlap@Sun.COM 			break;
18447978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
18457978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND,
18467978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
18477978SPeter.Dunlap@Sun.COM 			break;
18487978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
18497978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND,
18507978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
18517978SPeter.Dunlap@Sun.COM 			break;
18527978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_SCSI_EVENT:
18537978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
18547978SPeter.Dunlap@Sun.COM 		default:
18557978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_MISC_TX,
18567978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
18577978SPeter.Dunlap@Sun.COM 			break;
18587978SPeter.Dunlap@Sun.COM 		}
18597978SPeter.Dunlap@Sun.COM 		break;
18607978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_RSP:
18617978SPeter.Dunlap@Sun.COM 		/* Target only */
18627978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18637978SPeter.Dunlap@Sun.COM 		break;
18647978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
18657978SPeter.Dunlap@Sun.COM 		/* Target only */
18667978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18677978SPeter.Dunlap@Sun.COM 		break;
18687978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_DATA_RSP:
18697978SPeter.Dunlap@Sun.COM 		/* Target only */
18707978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18717978SPeter.Dunlap@Sun.COM 		break;
18727978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_RTT_RSP:
18737978SPeter.Dunlap@Sun.COM 		/* Target only */
18747978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18757978SPeter.Dunlap@Sun.COM 		break;
18767978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_IN:
18777978SPeter.Dunlap@Sun.COM 		/* Target only */
18787978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18797978SPeter.Dunlap@Sun.COM 		break;
18807978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_RSP:
18817978SPeter.Dunlap@Sun.COM 		/* Target only */
18827978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18837978SPeter.Dunlap@Sun.COM 		break;
18847978SPeter.Dunlap@Sun.COM 		/* Initiator only */
18857978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_CMD:
18867978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
18877978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_DATA:
18887978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_OUT:
18897978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_CMD:
18907978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SNACK_CMD:
18917978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_REJECT_MSG:
18927978SPeter.Dunlap@Sun.COM 	default:
18937978SPeter.Dunlap@Sun.COM 		/*
18947978SPeter.Dunlap@Sun.COM 		 * Connection state machine will validate these PDU's against
18957978SPeter.Dunlap@Sun.COM 		 * the current state.  A PDU not allowed in the current
18967978SPeter.Dunlap@Sun.COM 		 * state will cause a protocol error.
18977978SPeter.Dunlap@Sun.COM 		 */
18987978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
18997978SPeter.Dunlap@Sun.COM 		break;
19007978SPeter.Dunlap@Sun.COM 	}
19017978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
19027978SPeter.Dunlap@Sun.COM }
19037978SPeter.Dunlap@Sun.COM 
19047978SPeter.Dunlap@Sun.COM /*
1905*9162SPeter.Dunlap@Sun.COM  * Common allocation of a PDU along with memory for header and data.
19067978SPeter.Dunlap@Sun.COM  */
1907*9162SPeter.Dunlap@Sun.COM static idm_pdu_t *
1908*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, int sleepflag)
19097978SPeter.Dunlap@Sun.COM {
19107978SPeter.Dunlap@Sun.COM 	idm_pdu_t *result;
19117978SPeter.Dunlap@Sun.COM 
19127978SPeter.Dunlap@Sun.COM 	/*
19137978SPeter.Dunlap@Sun.COM 	 * IDM clients should cache these structures for performance
19147978SPeter.Dunlap@Sun.COM 	 * critical paths.  We can't cache effectively in IDM because we
19157978SPeter.Dunlap@Sun.COM 	 * don't know the correct header and data size.
19167978SPeter.Dunlap@Sun.COM 	 *
19177978SPeter.Dunlap@Sun.COM 	 * Valid header length is assumed to be hdrlen and valid data
19187978SPeter.Dunlap@Sun.COM 	 * length is assumed to be datalen.  isp_hdrlen and isp_datalen
19197978SPeter.Dunlap@Sun.COM 	 * can be adjusted after the PDU is returned if necessary.
19207978SPeter.Dunlap@Sun.COM 	 */
1921*9162SPeter.Dunlap@Sun.COM 	result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, sleepflag);
1922*9162SPeter.Dunlap@Sun.COM 	if (result != NULL) {
1923*9162SPeter.Dunlap@Sun.COM 		/* For idm_pdu_free sanity check */
1924*9162SPeter.Dunlap@Sun.COM 		result->isp_flags |= IDM_PDU_ALLOC;
1925*9162SPeter.Dunlap@Sun.COM 		/* pointer arithmetic */
1926*9162SPeter.Dunlap@Sun.COM 		result->isp_hdr = (iscsi_hdr_t *)(result + 1);
1927*9162SPeter.Dunlap@Sun.COM 		result->isp_hdrlen = hdrlen;
1928*9162SPeter.Dunlap@Sun.COM 		result->isp_hdrbuflen = hdrlen;
1929*9162SPeter.Dunlap@Sun.COM 		result->isp_transport_hdrlen = 0;
1930*9162SPeter.Dunlap@Sun.COM 		result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
1931*9162SPeter.Dunlap@Sun.COM 		result->isp_datalen = datalen;
1932*9162SPeter.Dunlap@Sun.COM 		result->isp_databuflen = datalen;
1933*9162SPeter.Dunlap@Sun.COM 		result->isp_magic = IDM_PDU_MAGIC;
1934*9162SPeter.Dunlap@Sun.COM 	}
19357978SPeter.Dunlap@Sun.COM 
19367978SPeter.Dunlap@Sun.COM 	return (result);
19377978SPeter.Dunlap@Sun.COM }
19387978SPeter.Dunlap@Sun.COM 
19397978SPeter.Dunlap@Sun.COM /*
1940*9162SPeter.Dunlap@Sun.COM  * Typical idm_pdu_alloc invocation, will block for resources.
1941*9162SPeter.Dunlap@Sun.COM  */
1942*9162SPeter.Dunlap@Sun.COM idm_pdu_t *
1943*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
1944*9162SPeter.Dunlap@Sun.COM {
1945*9162SPeter.Dunlap@Sun.COM 	return (idm_pdu_alloc_common(hdrlen, datalen, KM_SLEEP));
1946*9162SPeter.Dunlap@Sun.COM }
1947*9162SPeter.Dunlap@Sun.COM 
1948*9162SPeter.Dunlap@Sun.COM /*
1949*9162SPeter.Dunlap@Sun.COM  * Non-blocking idm_pdu_alloc implementation, returns NULL if resources
1950*9162SPeter.Dunlap@Sun.COM  * are not available.  Needed for transport-layer allocations which may
1951*9162SPeter.Dunlap@Sun.COM  * be invoking in interrupt context.
1952*9162SPeter.Dunlap@Sun.COM  */
1953*9162SPeter.Dunlap@Sun.COM idm_pdu_t *
1954*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc_nosleep(uint_t hdrlen, uint_t datalen)
1955*9162SPeter.Dunlap@Sun.COM {
1956*9162SPeter.Dunlap@Sun.COM 	return (idm_pdu_alloc_common(hdrlen, datalen, KM_NOSLEEP));
1957*9162SPeter.Dunlap@Sun.COM }
1958*9162SPeter.Dunlap@Sun.COM 
1959*9162SPeter.Dunlap@Sun.COM /*
19607978SPeter.Dunlap@Sun.COM  * Free a PDU previously allocated with idm_pdu_alloc() including any
19617978SPeter.Dunlap@Sun.COM  * header and data space allocated as part of the original request.
19627978SPeter.Dunlap@Sun.COM  * Additional memory regions referenced by subsequent modification of
19637978SPeter.Dunlap@Sun.COM  * the isp_hdr and/or isp_data fields will not be freed.
19647978SPeter.Dunlap@Sun.COM  */
19657978SPeter.Dunlap@Sun.COM void
19667978SPeter.Dunlap@Sun.COM idm_pdu_free(idm_pdu_t *pdu)
19677978SPeter.Dunlap@Sun.COM {
19687978SPeter.Dunlap@Sun.COM 	/* Make sure the structure was allocated using idm_pdu_alloc() */
19697978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
19707978SPeter.Dunlap@Sun.COM 	kmem_free(pdu,
19717978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
19727978SPeter.Dunlap@Sun.COM }
19737978SPeter.Dunlap@Sun.COM 
19747978SPeter.Dunlap@Sun.COM /*
19757978SPeter.Dunlap@Sun.COM  * Initialize the connection, private and callback fields in a PDU.
19767978SPeter.Dunlap@Sun.COM  */
19777978SPeter.Dunlap@Sun.COM void
19787978SPeter.Dunlap@Sun.COM idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
19797978SPeter.Dunlap@Sun.COM {
19807978SPeter.Dunlap@Sun.COM 	/*
19817978SPeter.Dunlap@Sun.COM 	 * idm_pdu_complete() will call idm_pdu_free if the callback is
19827978SPeter.Dunlap@Sun.COM 	 * NULL.  This will only work if the PDU was originally allocated
19837978SPeter.Dunlap@Sun.COM 	 * with idm_pdu_alloc().
19847978SPeter.Dunlap@Sun.COM 	 */
19857978SPeter.Dunlap@Sun.COM 	ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
19867978SPeter.Dunlap@Sun.COM 	    (cb != NULL));
19877978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
19887978SPeter.Dunlap@Sun.COM 	pdu->isp_ic = ic;
19897978SPeter.Dunlap@Sun.COM 	pdu->isp_private = private;
19907978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = cb;
19917978SPeter.Dunlap@Sun.COM }
19927978SPeter.Dunlap@Sun.COM 
19937978SPeter.Dunlap@Sun.COM /*
19947978SPeter.Dunlap@Sun.COM  * Initialize the header and header length field.  This function should
19957978SPeter.Dunlap@Sun.COM  * not be used to adjust the header length in a buffer allocated via
19967978SPeter.Dunlap@Sun.COM  * pdu_pdu_alloc since it overwrites the existing header pointer.
19977978SPeter.Dunlap@Sun.COM  */
19987978SPeter.Dunlap@Sun.COM void
19997978SPeter.Dunlap@Sun.COM idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
20007978SPeter.Dunlap@Sun.COM {
20017978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr);
20027978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = hdrlen;
20037978SPeter.Dunlap@Sun.COM }
20047978SPeter.Dunlap@Sun.COM 
20057978SPeter.Dunlap@Sun.COM /*
20067978SPeter.Dunlap@Sun.COM  * Initialize the data and data length fields.  This function should
20077978SPeter.Dunlap@Sun.COM  * not be used to adjust the data length of a buffer allocated via
20087978SPeter.Dunlap@Sun.COM  * idm_pdu_alloc since it overwrites the existing data pointer.
20097978SPeter.Dunlap@Sun.COM  */
20107978SPeter.Dunlap@Sun.COM void
20117978SPeter.Dunlap@Sun.COM idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
20127978SPeter.Dunlap@Sun.COM {
20137978SPeter.Dunlap@Sun.COM 	pdu->isp_data = data;
20147978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = datalen;
20157978SPeter.Dunlap@Sun.COM }
20167978SPeter.Dunlap@Sun.COM 
20177978SPeter.Dunlap@Sun.COM void
20187978SPeter.Dunlap@Sun.COM idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
20197978SPeter.Dunlap@Sun.COM {
20207978SPeter.Dunlap@Sun.COM 	if (pdu->isp_callback) {
20217978SPeter.Dunlap@Sun.COM 		pdu->isp_status = status;
20227978SPeter.Dunlap@Sun.COM 		(*pdu->isp_callback)(pdu, status);
20237978SPeter.Dunlap@Sun.COM 	} else {
20247978SPeter.Dunlap@Sun.COM 		idm_pdu_free(pdu);
20257978SPeter.Dunlap@Sun.COM 	}
20267978SPeter.Dunlap@Sun.COM }
20277978SPeter.Dunlap@Sun.COM 
20287978SPeter.Dunlap@Sun.COM /*
20297978SPeter.Dunlap@Sun.COM  * State machine auditing
20307978SPeter.Dunlap@Sun.COM  */
20317978SPeter.Dunlap@Sun.COM 
20327978SPeter.Dunlap@Sun.COM void
20337978SPeter.Dunlap@Sun.COM idm_sm_audit_init(sm_audit_buf_t *audit_buf)
20347978SPeter.Dunlap@Sun.COM {
20357978SPeter.Dunlap@Sun.COM 	bzero(audit_buf, sizeof (sm_audit_buf_t));
20367978SPeter.Dunlap@Sun.COM 	audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1;
20377978SPeter.Dunlap@Sun.COM }
20387978SPeter.Dunlap@Sun.COM 
20397978SPeter.Dunlap@Sun.COM static
20407978SPeter.Dunlap@Sun.COM sm_audit_record_t *
20417978SPeter.Dunlap@Sun.COM idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type,
20427978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type,
20437978SPeter.Dunlap@Sun.COM     int current_state)
20447978SPeter.Dunlap@Sun.COM {
20457978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
20467978SPeter.Dunlap@Sun.COM 
20477978SPeter.Dunlap@Sun.COM 	sar = audit_buf->sab_records;
20487978SPeter.Dunlap@Sun.COM 	sar += audit_buf->sab_index;
20497978SPeter.Dunlap@Sun.COM 	audit_buf->sab_index++;
20507978SPeter.Dunlap@Sun.COM 	audit_buf->sab_index &= audit_buf->sab_max_index;
20517978SPeter.Dunlap@Sun.COM 
20527978SPeter.Dunlap@Sun.COM 	sar->sar_type = r_type;
20537978SPeter.Dunlap@Sun.COM 	gethrestime(&sar->sar_timestamp);
20547978SPeter.Dunlap@Sun.COM 	sar->sar_sm_type = sm_type;
20557978SPeter.Dunlap@Sun.COM 	sar->sar_state = current_state;
20567978SPeter.Dunlap@Sun.COM 
20577978SPeter.Dunlap@Sun.COM 	return (sar);
20587978SPeter.Dunlap@Sun.COM }
20597978SPeter.Dunlap@Sun.COM 
20607978SPeter.Dunlap@Sun.COM void
20617978SPeter.Dunlap@Sun.COM idm_sm_audit_event(sm_audit_buf_t *audit_buf,
20627978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type, int current_state,
20637978SPeter.Dunlap@Sun.COM     int event, uintptr_t event_info)
20647978SPeter.Dunlap@Sun.COM {
20657978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
20667978SPeter.Dunlap@Sun.COM 
20677978SPeter.Dunlap@Sun.COM 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT,
20687978SPeter.Dunlap@Sun.COM 	    sm_type, current_state);
20697978SPeter.Dunlap@Sun.COM 	sar->sar_event = event;
20707978SPeter.Dunlap@Sun.COM 	sar->sar_event_info = event_info;
20717978SPeter.Dunlap@Sun.COM }
20727978SPeter.Dunlap@Sun.COM 
20737978SPeter.Dunlap@Sun.COM void
20747978SPeter.Dunlap@Sun.COM idm_sm_audit_state_change(sm_audit_buf_t *audit_buf,
20757978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type, int current_state, int new_state)
20767978SPeter.Dunlap@Sun.COM {
20777978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
20787978SPeter.Dunlap@Sun.COM 
20797978SPeter.Dunlap@Sun.COM 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE,
20807978SPeter.Dunlap@Sun.COM 	    sm_type, current_state);
20817978SPeter.Dunlap@Sun.COM 	sar->sar_new_state = new_state;
20827978SPeter.Dunlap@Sun.COM }
20837978SPeter.Dunlap@Sun.COM 
20847978SPeter.Dunlap@Sun.COM 
20857978SPeter.Dunlap@Sun.COM /*
20867978SPeter.Dunlap@Sun.COM  * Object reference tracking
20877978SPeter.Dunlap@Sun.COM  */
20887978SPeter.Dunlap@Sun.COM 
20897978SPeter.Dunlap@Sun.COM void
20907978SPeter.Dunlap@Sun.COM idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj)
20917978SPeter.Dunlap@Sun.COM {
20927978SPeter.Dunlap@Sun.COM 	bzero(refcnt, sizeof (*refcnt));
20937978SPeter.Dunlap@Sun.COM 	idm_refcnt_reset(refcnt);
20947978SPeter.Dunlap@Sun.COM 	refcnt->ir_referenced_obj = referenced_obj;
20957978SPeter.Dunlap@Sun.COM 	bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t));
20967978SPeter.Dunlap@Sun.COM 	refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1;
20977978SPeter.Dunlap@Sun.COM 	mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL);
20987978SPeter.Dunlap@Sun.COM 	cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL);
20997978SPeter.Dunlap@Sun.COM }
21007978SPeter.Dunlap@Sun.COM 
21017978SPeter.Dunlap@Sun.COM void
21027978SPeter.Dunlap@Sun.COM idm_refcnt_destroy(idm_refcnt_t *refcnt)
21037978SPeter.Dunlap@Sun.COM {
21047978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt == 0);
21057978SPeter.Dunlap@Sun.COM 	cv_destroy(&refcnt->ir_cv);
21067978SPeter.Dunlap@Sun.COM 	mutex_destroy(&refcnt->ir_mutex);
21077978SPeter.Dunlap@Sun.COM }
21087978SPeter.Dunlap@Sun.COM 
21097978SPeter.Dunlap@Sun.COM void
21107978SPeter.Dunlap@Sun.COM idm_refcnt_reset(idm_refcnt_t *refcnt)
21117978SPeter.Dunlap@Sun.COM {
21127978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_NOWAIT;
21137978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt = 0;
21147978SPeter.Dunlap@Sun.COM }
21157978SPeter.Dunlap@Sun.COM 
21167978SPeter.Dunlap@Sun.COM void
21177978SPeter.Dunlap@Sun.COM idm_refcnt_hold(idm_refcnt_t *refcnt)
21187978SPeter.Dunlap@Sun.COM {
21197978SPeter.Dunlap@Sun.COM 	/*
21207978SPeter.Dunlap@Sun.COM 	 * Nothing should take a hold on an object after a call to
21217978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref
21227978SPeter.Dunlap@Sun.COM 	 */
21237978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_waiting == REF_NOWAIT);
21247978SPeter.Dunlap@Sun.COM 
21257978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
21267978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt++;
21277978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
21287978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
21297978SPeter.Dunlap@Sun.COM }
21307978SPeter.Dunlap@Sun.COM 
21317978SPeter.Dunlap@Sun.COM static void
21327978SPeter.Dunlap@Sun.COM idm_refcnt_unref_task(void *refcnt_void)
21337978SPeter.Dunlap@Sun.COM {
21347978SPeter.Dunlap@Sun.COM 	idm_refcnt_t *refcnt = refcnt_void;
21357978SPeter.Dunlap@Sun.COM 
21367978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
21377978SPeter.Dunlap@Sun.COM 	(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
21387978SPeter.Dunlap@Sun.COM }
21397978SPeter.Dunlap@Sun.COM 
21407978SPeter.Dunlap@Sun.COM void
21417978SPeter.Dunlap@Sun.COM idm_refcnt_rele(idm_refcnt_t *refcnt)
21427978SPeter.Dunlap@Sun.COM {
21437978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
21447978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt > 0);
21457978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt--;
21467978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
21477978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_waiting == REF_NOWAIT) {
21487978SPeter.Dunlap@Sun.COM 		/* No one is waiting on this object */
21497978SPeter.Dunlap@Sun.COM 		mutex_exit(&refcnt->ir_mutex);
21507978SPeter.Dunlap@Sun.COM 		return;
21517978SPeter.Dunlap@Sun.COM 	}
21527978SPeter.Dunlap@Sun.COM 
21537978SPeter.Dunlap@Sun.COM 	/*
21547978SPeter.Dunlap@Sun.COM 	 * Someone is waiting for this object to go idle so check if
21557978SPeter.Dunlap@Sun.COM 	 * refcnt is 0.  Waiting on an object then later grabbing another
21567978SPeter.Dunlap@Sun.COM 	 * reference is not allowed so we don't need to handle that case.
21577978SPeter.Dunlap@Sun.COM 	 */
21587978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
21597978SPeter.Dunlap@Sun.COM 		if (refcnt->ir_waiting == REF_WAIT_ASYNC) {
21607978SPeter.Dunlap@Sun.COM 			if (taskq_dispatch(idm.idm_global_taskq,
21617978SPeter.Dunlap@Sun.COM 			    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
21627978SPeter.Dunlap@Sun.COM 				cmn_err(CE_WARN,
21637978SPeter.Dunlap@Sun.COM 				    "idm_refcnt_rele: Couldn't dispatch task");
21647978SPeter.Dunlap@Sun.COM 			}
21657978SPeter.Dunlap@Sun.COM 		} else if (refcnt->ir_waiting == REF_WAIT_SYNC) {
21667978SPeter.Dunlap@Sun.COM 			cv_signal(&refcnt->ir_cv);
21677978SPeter.Dunlap@Sun.COM 		}
21687978SPeter.Dunlap@Sun.COM 	}
21697978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
21707978SPeter.Dunlap@Sun.COM }
21717978SPeter.Dunlap@Sun.COM 
21727978SPeter.Dunlap@Sun.COM void
21737978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
21747978SPeter.Dunlap@Sun.COM {
21757978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
21767978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt > 0);
21777978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt--;
21787978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
21797978SPeter.Dunlap@Sun.COM 
21807978SPeter.Dunlap@Sun.COM 	/*
21817978SPeter.Dunlap@Sun.COM 	 * Someone is waiting for this object to go idle so check if
21827978SPeter.Dunlap@Sun.COM 	 * refcnt is 0.  Waiting on an object then later grabbing another
21837978SPeter.Dunlap@Sun.COM 	 * reference is not allowed so we don't need to handle that case.
21847978SPeter.Dunlap@Sun.COM 	 */
21857978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
21867978SPeter.Dunlap@Sun.COM 		refcnt->ir_cb = cb_func;
21877978SPeter.Dunlap@Sun.COM 		refcnt->ir_waiting = REF_WAIT_ASYNC;
21887978SPeter.Dunlap@Sun.COM 		if (taskq_dispatch(idm.idm_global_taskq,
21897978SPeter.Dunlap@Sun.COM 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
21907978SPeter.Dunlap@Sun.COM 			cmn_err(CE_WARN,
21917978SPeter.Dunlap@Sun.COM 			    "idm_refcnt_rele: Couldn't dispatch task");
21927978SPeter.Dunlap@Sun.COM 		}
21937978SPeter.Dunlap@Sun.COM 	}
21947978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
21957978SPeter.Dunlap@Sun.COM }
21967978SPeter.Dunlap@Sun.COM 
21977978SPeter.Dunlap@Sun.COM void
21987978SPeter.Dunlap@Sun.COM idm_refcnt_wait_ref(idm_refcnt_t *refcnt)
21997978SPeter.Dunlap@Sun.COM {
22007978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
22017978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_WAIT_SYNC;
22027978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
22037978SPeter.Dunlap@Sun.COM 	while (refcnt->ir_refcnt != 0)
22047978SPeter.Dunlap@Sun.COM 		cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex);
22057978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
22067978SPeter.Dunlap@Sun.COM }
22077978SPeter.Dunlap@Sun.COM 
22087978SPeter.Dunlap@Sun.COM void
22097978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
22107978SPeter.Dunlap@Sun.COM {
22117978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
22127978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_WAIT_ASYNC;
22137978SPeter.Dunlap@Sun.COM 	refcnt->ir_cb = cb_func;
22147978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
22157978SPeter.Dunlap@Sun.COM 	/*
22167978SPeter.Dunlap@Sun.COM 	 * It's possible we don't have any references.  To make things easier
22177978SPeter.Dunlap@Sun.COM 	 * on the caller use a taskq to call the callback instead of
22187978SPeter.Dunlap@Sun.COM 	 * calling it synchronously
22197978SPeter.Dunlap@Sun.COM 	 */
22207978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
22217978SPeter.Dunlap@Sun.COM 		if (taskq_dispatch(idm.idm_global_taskq,
22227978SPeter.Dunlap@Sun.COM 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
22237978SPeter.Dunlap@Sun.COM 			cmn_err(CE_WARN,
22247978SPeter.Dunlap@Sun.COM 			    "idm_refcnt_async_wait_ref: "
22257978SPeter.Dunlap@Sun.COM 			    "Couldn't dispatch task");
22267978SPeter.Dunlap@Sun.COM 		}
22277978SPeter.Dunlap@Sun.COM 	}
22287978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
22297978SPeter.Dunlap@Sun.COM }
22307978SPeter.Dunlap@Sun.COM 
22317978SPeter.Dunlap@Sun.COM void
22327978SPeter.Dunlap@Sun.COM idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt,
22337978SPeter.Dunlap@Sun.COM     idm_refcnt_cb_t *cb_func)
22347978SPeter.Dunlap@Sun.COM {
22357978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
22367978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
22377978SPeter.Dunlap@Sun.COM 		mutex_exit(&refcnt->ir_mutex);
22387978SPeter.Dunlap@Sun.COM 		(*cb_func)(refcnt->ir_referenced_obj);
22397978SPeter.Dunlap@Sun.COM 		return;
22407978SPeter.Dunlap@Sun.COM 	}
22417978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
22427978SPeter.Dunlap@Sun.COM }
22437978SPeter.Dunlap@Sun.COM 
22447978SPeter.Dunlap@Sun.COM void
22457978SPeter.Dunlap@Sun.COM idm_conn_hold(idm_conn_t *ic)
22467978SPeter.Dunlap@Sun.COM {
22477978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&ic->ic_refcnt);
22487978SPeter.Dunlap@Sun.COM }
22497978SPeter.Dunlap@Sun.COM 
22507978SPeter.Dunlap@Sun.COM void
22517978SPeter.Dunlap@Sun.COM idm_conn_rele(idm_conn_t *ic)
22527978SPeter.Dunlap@Sun.COM {
22537978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&ic->ic_refcnt);
22547978SPeter.Dunlap@Sun.COM }
22557978SPeter.Dunlap@Sun.COM 
22567978SPeter.Dunlap@Sun.COM 
22577978SPeter.Dunlap@Sun.COM static int
22587978SPeter.Dunlap@Sun.COM _idm_init(void)
22597978SPeter.Dunlap@Sun.COM {
22607978SPeter.Dunlap@Sun.COM 	/* Initialize the rwlock for the taskid table */
22617978SPeter.Dunlap@Sun.COM 	rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL);
22627978SPeter.Dunlap@Sun.COM 
22637978SPeter.Dunlap@Sun.COM 	/* Initialize the global mutex and taskq */
22647978SPeter.Dunlap@Sun.COM 	mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
22657978SPeter.Dunlap@Sun.COM 
22667978SPeter.Dunlap@Sun.COM 	cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
22677978SPeter.Dunlap@Sun.COM 	cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
22687978SPeter.Dunlap@Sun.COM 
2269*9162SPeter.Dunlap@Sun.COM 	/*
2270*9162SPeter.Dunlap@Sun.COM 	 * The maximum allocation needs to be high here since there can be
2271*9162SPeter.Dunlap@Sun.COM 	 * many concurrent tasks using the global taskq.
2272*9162SPeter.Dunlap@Sun.COM 	 */
22737978SPeter.Dunlap@Sun.COM 	idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
2274*9162SPeter.Dunlap@Sun.COM 	    128, 16384, TASKQ_PREPOPULATE);
22757978SPeter.Dunlap@Sun.COM 	if (idm.idm_global_taskq == NULL) {
22767978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_wd_cv);
22777978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_tgt_svc_cv);
22787978SPeter.Dunlap@Sun.COM 		mutex_destroy(&idm.idm_global_mutex);
22797978SPeter.Dunlap@Sun.COM 		rw_destroy(&idm.idm_taskid_table_lock);
22807978SPeter.Dunlap@Sun.COM 		return (ENOMEM);
22817978SPeter.Dunlap@Sun.COM 	}
22827978SPeter.Dunlap@Sun.COM 
22838209SJames.Moore@Sun.COM 	/* Start watchdog thread */
22847978SPeter.Dunlap@Sun.COM 	idm.idm_wd_thread = thread_create(NULL, 0,
22857978SPeter.Dunlap@Sun.COM 	    idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri);
22867978SPeter.Dunlap@Sun.COM 	if (idm.idm_wd_thread == NULL) {
22877978SPeter.Dunlap@Sun.COM 		/* Couldn't create the watchdog thread */
22887978SPeter.Dunlap@Sun.COM 		taskq_destroy(idm.idm_global_taskq);
22897978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_wd_cv);
22907978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_tgt_svc_cv);
22917978SPeter.Dunlap@Sun.COM 		mutex_destroy(&idm.idm_global_mutex);
22927978SPeter.Dunlap@Sun.COM 		rw_destroy(&idm.idm_taskid_table_lock);
22937978SPeter.Dunlap@Sun.COM 		return (ENOMEM);
22947978SPeter.Dunlap@Sun.COM 	}
22957978SPeter.Dunlap@Sun.COM 
22968209SJames.Moore@Sun.COM 	/* Pause until the watchdog thread is running */
22977978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
22987978SPeter.Dunlap@Sun.COM 	while (!idm.idm_wd_thread_running)
22997978SPeter.Dunlap@Sun.COM 		cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex);
23007978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
23017978SPeter.Dunlap@Sun.COM 
23027978SPeter.Dunlap@Sun.COM 	/*
23037978SPeter.Dunlap@Sun.COM 	 * Allocate the task ID table and set "next" to 0.
23047978SPeter.Dunlap@Sun.COM 	 */
23057978SPeter.Dunlap@Sun.COM 
23067978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_max = idm_max_taskids;
23077978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_table = (idm_task_t **)
23087978SPeter.Dunlap@Sun.COM 	    kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP);
23097978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_next = 0;
23107978SPeter.Dunlap@Sun.COM 
23117978SPeter.Dunlap@Sun.COM 	/* Create the global buffer and task kmem caches */
23127978SPeter.Dunlap@Sun.COM 	idm.idm_buf_cache = kmem_cache_create("idm_buf_cache",
23137978SPeter.Dunlap@Sun.COM 	    sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
23147978SPeter.Dunlap@Sun.COM 
23157978SPeter.Dunlap@Sun.COM 	/*
23167978SPeter.Dunlap@Sun.COM 	 * Note, we're explicitly allocating an additional iSER header-
23177978SPeter.Dunlap@Sun.COM 	 * sized chunk for each of these elements. See idm_task_constructor().
23187978SPeter.Dunlap@Sun.COM 	 */
23197978SPeter.Dunlap@Sun.COM 	idm.idm_task_cache = kmem_cache_create("idm_task_cache",
23207978SPeter.Dunlap@Sun.COM 	    sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8,
23217978SPeter.Dunlap@Sun.COM 	    &idm_task_constructor, &idm_task_destructor,
23227978SPeter.Dunlap@Sun.COM 	    NULL, NULL, NULL, KM_SLEEP);
23237978SPeter.Dunlap@Sun.COM 
23247978SPeter.Dunlap@Sun.COM 	/* Create the service and connection context lists */
23257978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
23267978SPeter.Dunlap@Sun.COM 	    offsetof(idm_svc_t, is_list_node));
23277978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t),
23287978SPeter.Dunlap@Sun.COM 	    offsetof(idm_conn_t, ic_list_node));
23297978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
23307978SPeter.Dunlap@Sun.COM 	    offsetof(idm_conn_t, ic_list_node));
23317978SPeter.Dunlap@Sun.COM 
23327978SPeter.Dunlap@Sun.COM 	/* Initialize the native sockets transport */
23337978SPeter.Dunlap@Sun.COM 	idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]);
23347978SPeter.Dunlap@Sun.COM 
23357978SPeter.Dunlap@Sun.COM 	/* Create connection ID pool */
23367978SPeter.Dunlap@Sun.COM 	(void) idm_idpool_create(&idm.idm_conn_id_pool);
23377978SPeter.Dunlap@Sun.COM 
23387978SPeter.Dunlap@Sun.COM 	return (DDI_SUCCESS);
23397978SPeter.Dunlap@Sun.COM }
23407978SPeter.Dunlap@Sun.COM 
23417978SPeter.Dunlap@Sun.COM static int
23427978SPeter.Dunlap@Sun.COM _idm_fini(void)
23437978SPeter.Dunlap@Sun.COM {
23447978SPeter.Dunlap@Sun.COM 	if (!list_is_empty(&idm.idm_ini_conn_list) ||
23457978SPeter.Dunlap@Sun.COM 	    !list_is_empty(&idm.idm_tgt_conn_list) ||
23467978SPeter.Dunlap@Sun.COM 	    !list_is_empty(&idm.idm_tgt_svc_list)) {
23477978SPeter.Dunlap@Sun.COM 		return (EBUSY);
23487978SPeter.Dunlap@Sun.COM 	}
23497978SPeter.Dunlap@Sun.COM 
23507978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
23517978SPeter.Dunlap@Sun.COM 	idm.idm_wd_thread_running = B_FALSE;
23527978SPeter.Dunlap@Sun.COM 	cv_signal(&idm.idm_wd_cv);
23537978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
23547978SPeter.Dunlap@Sun.COM 
23557978SPeter.Dunlap@Sun.COM 	thread_join(idm.idm_wd_thread_did);
23567978SPeter.Dunlap@Sun.COM 
23577978SPeter.Dunlap@Sun.COM 	idm_idpool_destroy(&idm.idm_conn_id_pool);
2358*9162SPeter.Dunlap@Sun.COM 
2359*9162SPeter.Dunlap@Sun.COM 	/* Close any LDI handles we have open on transport drivers */
2360*9162SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
2361*9162SPeter.Dunlap@Sun.COM 	idm_transport_teardown();
2362*9162SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
2363*9162SPeter.Dunlap@Sun.COM 
2364*9162SPeter.Dunlap@Sun.COM 	/* Teardown the native sockets transport */
23657978SPeter.Dunlap@Sun.COM 	idm_so_fini();
2366*9162SPeter.Dunlap@Sun.COM 
23677978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_ini_conn_list);
23687978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_tgt_conn_list);
23697978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_tgt_svc_list);
23707978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_task_cache);
23717978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_buf_cache);
23727978SPeter.Dunlap@Sun.COM 	kmem_free(idm.idm_taskid_table,
23737978SPeter.Dunlap@Sun.COM 	    idm.idm_taskid_max * sizeof (idm_task_t *));
23747978SPeter.Dunlap@Sun.COM 	mutex_destroy(&idm.idm_global_mutex);
23757978SPeter.Dunlap@Sun.COM 	cv_destroy(&idm.idm_wd_cv);
23767978SPeter.Dunlap@Sun.COM 	cv_destroy(&idm.idm_tgt_svc_cv);
23777978SPeter.Dunlap@Sun.COM 	rw_destroy(&idm.idm_taskid_table_lock);
23787978SPeter.Dunlap@Sun.COM 
23797978SPeter.Dunlap@Sun.COM 	return (0);
23807978SPeter.Dunlap@Sun.COM }
2381