xref: /onnv-gate/usr/src/uts/common/io/idm/idm.c (revision 8209:c5db9bfe2b4b)
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 /*
227978SPeter.Dunlap@Sun.COM  * Copyright 2008 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);
697978SPeter.Dunlap@Sun.COM 
707978SPeter.Dunlap@Sun.COM boolean_t idm_conn_logging = 0;
717978SPeter.Dunlap@Sun.COM boolean_t idm_svc_logging = 0;
727978SPeter.Dunlap@Sun.COM 
737978SPeter.Dunlap@Sun.COM /*
747978SPeter.Dunlap@Sun.COM  * Potential tuneable for the maximum number of tasks.  Default to
757978SPeter.Dunlap@Sun.COM  * IDM_TASKIDS_MAX
767978SPeter.Dunlap@Sun.COM  */
777978SPeter.Dunlap@Sun.COM 
787978SPeter.Dunlap@Sun.COM uint32_t	idm_max_taskids = IDM_TASKIDS_MAX;
797978SPeter.Dunlap@Sun.COM 
807978SPeter.Dunlap@Sun.COM /*
817978SPeter.Dunlap@Sun.COM  * Global list of transport handles
827978SPeter.Dunlap@Sun.COM  *   These are listed in preferential order, so we can simply take the
837978SPeter.Dunlap@Sun.COM  *   first "it_conn_is_capable" hit. Note also that the order maps to
847978SPeter.Dunlap@Sun.COM  *   the order of the idm_transport_type_t list.
857978SPeter.Dunlap@Sun.COM  */
867978SPeter.Dunlap@Sun.COM idm_transport_t idm_transport_list[] = {
877978SPeter.Dunlap@Sun.COM 
887978SPeter.Dunlap@Sun.COM 	/* iSER on InfiniBand transport handle */
897978SPeter.Dunlap@Sun.COM 	{IDM_TRANSPORT_TYPE_ISER,	/* type */
907978SPeter.Dunlap@Sun.COM 	"/devices/ib/iser@0:iser",	/* device path */
917978SPeter.Dunlap@Sun.COM 	NULL,				/* LDI handle */
927978SPeter.Dunlap@Sun.COM 	NULL,				/* transport ops */
937978SPeter.Dunlap@Sun.COM 	NULL},				/* transport caps */
947978SPeter.Dunlap@Sun.COM 
957978SPeter.Dunlap@Sun.COM 	/* IDM native sockets transport handle */
967978SPeter.Dunlap@Sun.COM 	{IDM_TRANSPORT_TYPE_SOCKETS,	/* type */
977978SPeter.Dunlap@Sun.COM 	NULL,				/* 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 };
1037978SPeter.Dunlap@Sun.COM 
1047978SPeter.Dunlap@Sun.COM int
1057978SPeter.Dunlap@Sun.COM _init(void)
1067978SPeter.Dunlap@Sun.COM {
1077978SPeter.Dunlap@Sun.COM 	int rc;
1087978SPeter.Dunlap@Sun.COM 
1097978SPeter.Dunlap@Sun.COM 	if ((rc = _idm_init()) != 0) {
1107978SPeter.Dunlap@Sun.COM 		return (rc);
1117978SPeter.Dunlap@Sun.COM 	}
1127978SPeter.Dunlap@Sun.COM 
1137978SPeter.Dunlap@Sun.COM 	return (mod_install(&modlinkage));
1147978SPeter.Dunlap@Sun.COM }
1157978SPeter.Dunlap@Sun.COM 
1167978SPeter.Dunlap@Sun.COM int
1177978SPeter.Dunlap@Sun.COM _fini(void)
1187978SPeter.Dunlap@Sun.COM {
1197978SPeter.Dunlap@Sun.COM 	int rc;
1207978SPeter.Dunlap@Sun.COM 
1217978SPeter.Dunlap@Sun.COM 	if ((rc = _idm_fini()) != 0) {
1227978SPeter.Dunlap@Sun.COM 		return (rc);
1237978SPeter.Dunlap@Sun.COM 	}
1247978SPeter.Dunlap@Sun.COM 
1257978SPeter.Dunlap@Sun.COM 	if ((rc = mod_remove(&modlinkage)) != 0) {
1267978SPeter.Dunlap@Sun.COM 		return (rc);
1277978SPeter.Dunlap@Sun.COM 	}
1287978SPeter.Dunlap@Sun.COM 
1297978SPeter.Dunlap@Sun.COM 	return (rc);
1307978SPeter.Dunlap@Sun.COM }
1317978SPeter.Dunlap@Sun.COM 
1327978SPeter.Dunlap@Sun.COM int
1337978SPeter.Dunlap@Sun.COM _info(struct modinfo *modinfop)
1347978SPeter.Dunlap@Sun.COM {
1357978SPeter.Dunlap@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1367978SPeter.Dunlap@Sun.COM }
1377978SPeter.Dunlap@Sun.COM 
1387978SPeter.Dunlap@Sun.COM /*
1397978SPeter.Dunlap@Sun.COM  * idm_transport_register()
1407978SPeter.Dunlap@Sun.COM  *
1417978SPeter.Dunlap@Sun.COM  * Provides a mechanism for an IDM transport driver to register its
1427978SPeter.Dunlap@Sun.COM  * transport ops and caps with the IDM kernel module. Invoked during
1437978SPeter.Dunlap@Sun.COM  * a transport driver's attach routine.
1447978SPeter.Dunlap@Sun.COM  */
1457978SPeter.Dunlap@Sun.COM idm_status_t
1467978SPeter.Dunlap@Sun.COM idm_transport_register(idm_transport_attr_t *attr)
1477978SPeter.Dunlap@Sun.COM {
1487978SPeter.Dunlap@Sun.COM 	ASSERT(attr->it_ops != NULL);
1497978SPeter.Dunlap@Sun.COM 	ASSERT(attr->it_caps != NULL);
1507978SPeter.Dunlap@Sun.COM 
1517978SPeter.Dunlap@Sun.COM 	switch (attr->type) {
1527978SPeter.Dunlap@Sun.COM 	/* All known non-native transports here; for now, iSER */
1537978SPeter.Dunlap@Sun.COM 	case IDM_TRANSPORT_TYPE_ISER:
1547978SPeter.Dunlap@Sun.COM 		idm_transport_list[attr->type].it_ops	= attr->it_ops;
1557978SPeter.Dunlap@Sun.COM 		idm_transport_list[attr->type].it_caps	= attr->it_caps;
1567978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
1577978SPeter.Dunlap@Sun.COM 
1587978SPeter.Dunlap@Sun.COM 	default:
1597978SPeter.Dunlap@Sun.COM 		cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
1607978SPeter.Dunlap@Sun.COM 		    "idm_transport_register", attr->type);
1617978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
1627978SPeter.Dunlap@Sun.COM 	}
1637978SPeter.Dunlap@Sun.COM }
1647978SPeter.Dunlap@Sun.COM 
1657978SPeter.Dunlap@Sun.COM /*
1667978SPeter.Dunlap@Sun.COM  * idm_ini_conn_create
1677978SPeter.Dunlap@Sun.COM  *
1687978SPeter.Dunlap@Sun.COM  * This function is invoked by the iSCSI layer to create a connection context.
1697978SPeter.Dunlap@Sun.COM  * This does not actually establish the socket connection.
1707978SPeter.Dunlap@Sun.COM  *
1717978SPeter.Dunlap@Sun.COM  * cr - Connection request parameters
1727978SPeter.Dunlap@Sun.COM  * new_con - Output parameter that contains the new request if successful
1737978SPeter.Dunlap@Sun.COM  *
1747978SPeter.Dunlap@Sun.COM  */
1757978SPeter.Dunlap@Sun.COM idm_status_t
1767978SPeter.Dunlap@Sun.COM idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
1777978SPeter.Dunlap@Sun.COM {
1787978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
1797978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
1807978SPeter.Dunlap@Sun.COM 	int			rc;
1817978SPeter.Dunlap@Sun.COM 
1827978SPeter.Dunlap@Sun.COM 	it = idm_transport_lookup(cr);
1837978SPeter.Dunlap@Sun.COM 
1847978SPeter.Dunlap@Sun.COM retry:
1857978SPeter.Dunlap@Sun.COM 	ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
1867978SPeter.Dunlap@Sun.COM 	    &cr->icr_conn_ops);
1877978SPeter.Dunlap@Sun.COM 
1887978SPeter.Dunlap@Sun.COM 	bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
1897978SPeter.Dunlap@Sun.COM 	    sizeof (cr->cr_ini_dst_addr));
1907978SPeter.Dunlap@Sun.COM 
1917978SPeter.Dunlap@Sun.COM 	/* create the transport-specific connection components */
1927978SPeter.Dunlap@Sun.COM 	rc = it->it_ops->it_ini_conn_create(cr, ic);
1937978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
1947978SPeter.Dunlap@Sun.COM 		/* cleanup the failed connection */
1957978SPeter.Dunlap@Sun.COM 		idm_conn_destroy_common(ic);
1967978SPeter.Dunlap@Sun.COM 		kmem_free(ic, sizeof (idm_conn_t));
1977978SPeter.Dunlap@Sun.COM 
1987978SPeter.Dunlap@Sun.COM 		/*
1997978SPeter.Dunlap@Sun.COM 		 * It is possible for an IB client to connect to
2007978SPeter.Dunlap@Sun.COM 		 * an ethernet-only client via an IB-eth gateway.
2017978SPeter.Dunlap@Sun.COM 		 * Therefore, if we are attempting to use iSER and
2027978SPeter.Dunlap@Sun.COM 		 * fail, retry with sockets before ultimately
2037978SPeter.Dunlap@Sun.COM 		 * failing the connection.
2047978SPeter.Dunlap@Sun.COM 		 */
2057978SPeter.Dunlap@Sun.COM 		if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
2067978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
2077978SPeter.Dunlap@Sun.COM 			goto retry;
2087978SPeter.Dunlap@Sun.COM 		}
2097978SPeter.Dunlap@Sun.COM 
2107978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
2117978SPeter.Dunlap@Sun.COM 	}
2127978SPeter.Dunlap@Sun.COM 
2137978SPeter.Dunlap@Sun.COM 	*new_con = ic;
2147978SPeter.Dunlap@Sun.COM 
2157978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
2167978SPeter.Dunlap@Sun.COM 	list_insert_tail(&idm.idm_ini_conn_list, ic);
2177978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
2187978SPeter.Dunlap@Sun.COM 
2197978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
2207978SPeter.Dunlap@Sun.COM }
2217978SPeter.Dunlap@Sun.COM 
2227978SPeter.Dunlap@Sun.COM /*
2237978SPeter.Dunlap@Sun.COM  * idm_ini_conn_destroy
2247978SPeter.Dunlap@Sun.COM  *
2257978SPeter.Dunlap@Sun.COM  * Releases any resources associated with the connection.  This is the
2267978SPeter.Dunlap@Sun.COM  * complement to idm_ini_conn_create.
2277978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
2287978SPeter.Dunlap@Sun.COM  *
2297978SPeter.Dunlap@Sun.COM  */
2307978SPeter.Dunlap@Sun.COM void
2317978SPeter.Dunlap@Sun.COM idm_ini_conn_destroy(idm_conn_t *ic)
2327978SPeter.Dunlap@Sun.COM {
2337978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
2347978SPeter.Dunlap@Sun.COM 	list_remove(&idm.idm_ini_conn_list, ic);
2357978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
2367978SPeter.Dunlap@Sun.COM 
2377978SPeter.Dunlap@Sun.COM 	ic->ic_transport_ops->it_ini_conn_destroy(ic);
2387978SPeter.Dunlap@Sun.COM 	idm_conn_destroy_common(ic);
2397978SPeter.Dunlap@Sun.COM }
2407978SPeter.Dunlap@Sun.COM 
2417978SPeter.Dunlap@Sun.COM /*
2427978SPeter.Dunlap@Sun.COM  * idm_ini_conn_connect
2437978SPeter.Dunlap@Sun.COM  *
2447978SPeter.Dunlap@Sun.COM  * Establish connection to the remote system identified in idm_conn_t.
2457978SPeter.Dunlap@Sun.COM  * The connection parameters including the remote IP address were established
2467978SPeter.Dunlap@Sun.COM  * in the call to idm_ini_conn_create.
2477978SPeter.Dunlap@Sun.COM  *
2487978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
2497978SPeter.Dunlap@Sun.COM  *
2507978SPeter.Dunlap@Sun.COM  * Returns success if the connection was established, otherwise some kind
2517978SPeter.Dunlap@Sun.COM  * of meaningful error code.
2527978SPeter.Dunlap@Sun.COM  *
2537978SPeter.Dunlap@Sun.COM  * Upon return the initiator can send a "login" request when it is ready.
2547978SPeter.Dunlap@Sun.COM  */
2557978SPeter.Dunlap@Sun.COM idm_status_t
2567978SPeter.Dunlap@Sun.COM idm_ini_conn_connect(idm_conn_t *ic)
2577978SPeter.Dunlap@Sun.COM {
2587978SPeter.Dunlap@Sun.COM 	idm_status_t	rc;
2597978SPeter.Dunlap@Sun.COM 
2607978SPeter.Dunlap@Sun.COM 	rc = idm_conn_sm_init(ic);
2617978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
2627978SPeter.Dunlap@Sun.COM 		return (ic->ic_conn_sm_status);
2637978SPeter.Dunlap@Sun.COM 	}
2647978SPeter.Dunlap@Sun.COM 	/* Kick state machine */
2657978SPeter.Dunlap@Sun.COM 	idm_conn_event(ic, CE_CONNECT_REQ, NULL);
2667978SPeter.Dunlap@Sun.COM 
2677978SPeter.Dunlap@Sun.COM 	/* Wait for login flag */
2687978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
2697978SPeter.Dunlap@Sun.COM 	while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
2707978SPeter.Dunlap@Sun.COM 	    !(ic->ic_state_flags & CF_ERROR)) {
2717978SPeter.Dunlap@Sun.COM 		cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
2727978SPeter.Dunlap@Sun.COM 	}
2737978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
2747978SPeter.Dunlap@Sun.COM 
2757978SPeter.Dunlap@Sun.COM 	if (ic->ic_state_flags & CF_ERROR) {
2767978SPeter.Dunlap@Sun.COM 		/* ic->ic_conn_sm_status will contains failure status */
2777978SPeter.Dunlap@Sun.COM 		return (ic->ic_conn_sm_status);
2787978SPeter.Dunlap@Sun.COM 	}
2797978SPeter.Dunlap@Sun.COM 
2807978SPeter.Dunlap@Sun.COM 	/* Ready to login */
2817978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
2827978SPeter.Dunlap@Sun.COM 	(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
2837978SPeter.Dunlap@Sun.COM 
2847978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
2857978SPeter.Dunlap@Sun.COM }
2867978SPeter.Dunlap@Sun.COM 
2877978SPeter.Dunlap@Sun.COM /*
2887978SPeter.Dunlap@Sun.COM  * idm_ini_conn_sm_fini_task()
2897978SPeter.Dunlap@Sun.COM  *
2907978SPeter.Dunlap@Sun.COM  * Dispatch a thread on the global taskq to tear down an initiator connection's
2917978SPeter.Dunlap@Sun.COM  * state machine. Note: We cannot do this from the disconnect thread as we will
2927978SPeter.Dunlap@Sun.COM  * end up in a situation wherein the thread is running on a taskq that it then
2937978SPeter.Dunlap@Sun.COM  * attempts to destroy.
2947978SPeter.Dunlap@Sun.COM  */
2957978SPeter.Dunlap@Sun.COM static void
2967978SPeter.Dunlap@Sun.COM idm_ini_conn_sm_fini_task(void *ic_void)
2977978SPeter.Dunlap@Sun.COM {
2987978SPeter.Dunlap@Sun.COM 	idm_conn_sm_fini((idm_conn_t *)ic_void);
2997978SPeter.Dunlap@Sun.COM }
3007978SPeter.Dunlap@Sun.COM 
3017978SPeter.Dunlap@Sun.COM /*
3027978SPeter.Dunlap@Sun.COM  * idm_ini_conn_disconnect
3037978SPeter.Dunlap@Sun.COM  *
3047978SPeter.Dunlap@Sun.COM  * Forces a connection (previously established using idm_ini_conn_connect)
3057978SPeter.Dunlap@Sun.COM  * to perform a controlled shutdown, cleaning up any outstanding requests.
3067978SPeter.Dunlap@Sun.COM  *
3077978SPeter.Dunlap@Sun.COM  * ic - idm_conn_t structure representing the relevant connection
3087978SPeter.Dunlap@Sun.COM  *
3097978SPeter.Dunlap@Sun.COM  * This is synchronous and it will return when the connection has been
3107978SPeter.Dunlap@Sun.COM  * properly shutdown.
3117978SPeter.Dunlap@Sun.COM  */
3127978SPeter.Dunlap@Sun.COM /* ARGSUSED */
3137978SPeter.Dunlap@Sun.COM void
3147978SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect(idm_conn_t *ic)
3157978SPeter.Dunlap@Sun.COM {
3167978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
3177978SPeter.Dunlap@Sun.COM 
3187978SPeter.Dunlap@Sun.COM 	if (ic->ic_state_flags == 0) {
3197978SPeter.Dunlap@Sun.COM 		/* already disconnected */
3207978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
3217978SPeter.Dunlap@Sun.COM 		return;
3227978SPeter.Dunlap@Sun.COM 	}
3237978SPeter.Dunlap@Sun.COM 	ic->ic_state_flags = 0;
3247978SPeter.Dunlap@Sun.COM 	ic->ic_conn_sm_status = 0;
3257978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
3267978SPeter.Dunlap@Sun.COM 
3277978SPeter.Dunlap@Sun.COM 	/* invoke the transport-specific conn_destroy */
3287978SPeter.Dunlap@Sun.COM 	(void) ic->ic_transport_ops->it_ini_conn_disconnect(ic);
3297978SPeter.Dunlap@Sun.COM 
3307978SPeter.Dunlap@Sun.COM 	/* teardown the connection sm */
3317978SPeter.Dunlap@Sun.COM 	(void) taskq_dispatch(idm.idm_global_taskq, &idm_ini_conn_sm_fini_task,
3327978SPeter.Dunlap@Sun.COM 	    (void *)ic, TQ_SLEEP);
3337978SPeter.Dunlap@Sun.COM }
3347978SPeter.Dunlap@Sun.COM 
3357978SPeter.Dunlap@Sun.COM /*
3367978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_create
3377978SPeter.Dunlap@Sun.COM  *
3387978SPeter.Dunlap@Sun.COM  * The target calls this service to obtain a service context for each available
3397978SPeter.Dunlap@Sun.COM  * transport, starting a service of each type related to the IP address and port
3407978SPeter.Dunlap@Sun.COM  * passed. The idm_svc_req_t contains the service parameters.
3417978SPeter.Dunlap@Sun.COM  */
3427978SPeter.Dunlap@Sun.COM idm_status_t
3437978SPeter.Dunlap@Sun.COM idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
3447978SPeter.Dunlap@Sun.COM {
3457978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
3467978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
3477978SPeter.Dunlap@Sun.COM 	idm_svc_t		*is;
3487978SPeter.Dunlap@Sun.COM 	int			rc;
3497978SPeter.Dunlap@Sun.COM 
3507978SPeter.Dunlap@Sun.COM 	*new_svc = NULL;
3517978SPeter.Dunlap@Sun.COM 	is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
3527978SPeter.Dunlap@Sun.COM 
3537978SPeter.Dunlap@Sun.COM 	/* Initialize transport-agnostic components of the service handle */
3547978SPeter.Dunlap@Sun.COM 	is->is_svc_req = *sr;
3557978SPeter.Dunlap@Sun.COM 	mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
3567978SPeter.Dunlap@Sun.COM 	cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
3577978SPeter.Dunlap@Sun.COM 	mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
3587978SPeter.Dunlap@Sun.COM 	cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
3597978SPeter.Dunlap@Sun.COM 	idm_refcnt_init(&is->is_refcnt, is);
3607978SPeter.Dunlap@Sun.COM 
3617978SPeter.Dunlap@Sun.COM 	/*
3627978SPeter.Dunlap@Sun.COM 	 * Make sure all available transports are setup.  We call this now
3637978SPeter.Dunlap@Sun.COM 	 * instead of at initialization time in case IB has become available
3647978SPeter.Dunlap@Sun.COM 	 * since we started (hotplug, etc).
3657978SPeter.Dunlap@Sun.COM 	 */
3667978SPeter.Dunlap@Sun.COM 	idm_transport_setup(sr->sr_li);
3677978SPeter.Dunlap@Sun.COM 
3687978SPeter.Dunlap@Sun.COM 	/*
3697978SPeter.Dunlap@Sun.COM 	 * Loop through the transports, configuring the transport-specific
3707978SPeter.Dunlap@Sun.COM 	 * components of each one.
3717978SPeter.Dunlap@Sun.COM 	 */
3727978SPeter.Dunlap@Sun.COM 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
3737978SPeter.Dunlap@Sun.COM 
3747978SPeter.Dunlap@Sun.COM 		it = &idm_transport_list[type];
3757978SPeter.Dunlap@Sun.COM 		/*
3767978SPeter.Dunlap@Sun.COM 		 * If it_ops is NULL then the transport is unconfigured
3777978SPeter.Dunlap@Sun.COM 		 * and we shouldn't try to start the service.
3787978SPeter.Dunlap@Sun.COM 		 */
3797978SPeter.Dunlap@Sun.COM 		if (it->it_ops == NULL) {
3807978SPeter.Dunlap@Sun.COM 			continue;
3817978SPeter.Dunlap@Sun.COM 		}
3827978SPeter.Dunlap@Sun.COM 
3837978SPeter.Dunlap@Sun.COM 		rc = it->it_ops->it_tgt_svc_create(sr, is);
3847978SPeter.Dunlap@Sun.COM 		if (rc != IDM_STATUS_SUCCESS) {
3857978SPeter.Dunlap@Sun.COM 			/* Teardown any configured services */
3867978SPeter.Dunlap@Sun.COM 			while (type--) {
3877978SPeter.Dunlap@Sun.COM 				it = &idm_transport_list[type];
3887978SPeter.Dunlap@Sun.COM 				if (it->it_ops == NULL) {
3897978SPeter.Dunlap@Sun.COM 					continue;
3907978SPeter.Dunlap@Sun.COM 				}
3917978SPeter.Dunlap@Sun.COM 				it->it_ops->it_tgt_svc_destroy(is);
3927978SPeter.Dunlap@Sun.COM 			}
3937978SPeter.Dunlap@Sun.COM 			/* Free the svc context and return */
3947978SPeter.Dunlap@Sun.COM 			kmem_free(is, sizeof (idm_svc_t));
3957978SPeter.Dunlap@Sun.COM 			return (rc);
3967978SPeter.Dunlap@Sun.COM 		}
3977978SPeter.Dunlap@Sun.COM 	}
3987978SPeter.Dunlap@Sun.COM 
3997978SPeter.Dunlap@Sun.COM 	*new_svc = is;
4007978SPeter.Dunlap@Sun.COM 
4017978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
4027978SPeter.Dunlap@Sun.COM 	list_insert_tail(&idm.idm_tgt_svc_list, is);
4037978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
4047978SPeter.Dunlap@Sun.COM 
4057978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
4067978SPeter.Dunlap@Sun.COM }
4077978SPeter.Dunlap@Sun.COM 
4087978SPeter.Dunlap@Sun.COM /*
4097978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_destroy
4107978SPeter.Dunlap@Sun.COM  *
4117978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
4127978SPeter.Dunlap@Sun.COM  *
4137978SPeter.Dunlap@Sun.COM  * Cleanup any resources associated with the idm_svc_t.
4147978SPeter.Dunlap@Sun.COM  */
4157978SPeter.Dunlap@Sun.COM void
4167978SPeter.Dunlap@Sun.COM idm_tgt_svc_destroy(idm_svc_t *is)
4177978SPeter.Dunlap@Sun.COM {
4187978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
4197978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
4207978SPeter.Dunlap@Sun.COM 
4217978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
4227978SPeter.Dunlap@Sun.COM 	/* remove this service from the global list */
4237978SPeter.Dunlap@Sun.COM 	list_remove(&idm.idm_tgt_svc_list, is);
4247978SPeter.Dunlap@Sun.COM 	/* wakeup any waiters for service change */
4257978SPeter.Dunlap@Sun.COM 	cv_broadcast(&idm.idm_tgt_svc_cv);
4267978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
4277978SPeter.Dunlap@Sun.COM 
4287978SPeter.Dunlap@Sun.COM 	/* tear down the svc resources */
4297978SPeter.Dunlap@Sun.COM 	idm_refcnt_destroy(&is->is_refcnt);
4307978SPeter.Dunlap@Sun.COM 	cv_destroy(&is->is_count_cv);
4317978SPeter.Dunlap@Sun.COM 	mutex_destroy(&is->is_count_mutex);
4327978SPeter.Dunlap@Sun.COM 	cv_destroy(&is->is_cv);
4337978SPeter.Dunlap@Sun.COM 	mutex_destroy(&is->is_mutex);
4347978SPeter.Dunlap@Sun.COM 
4357978SPeter.Dunlap@Sun.COM 	/* teardown each transport-specific service */
4367978SPeter.Dunlap@Sun.COM 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
4377978SPeter.Dunlap@Sun.COM 		it = &idm_transport_list[type];
4387978SPeter.Dunlap@Sun.COM 		if (it->it_ops == NULL) {
4397978SPeter.Dunlap@Sun.COM 			continue;
4407978SPeter.Dunlap@Sun.COM 		}
4417978SPeter.Dunlap@Sun.COM 
4427978SPeter.Dunlap@Sun.COM 		it->it_ops->it_tgt_svc_destroy(is);
4437978SPeter.Dunlap@Sun.COM 	}
4447978SPeter.Dunlap@Sun.COM 
4457978SPeter.Dunlap@Sun.COM 	/* free the svc handle */
4467978SPeter.Dunlap@Sun.COM 	kmem_free(is, sizeof (idm_svc_t));
4477978SPeter.Dunlap@Sun.COM }
4487978SPeter.Dunlap@Sun.COM 
4497978SPeter.Dunlap@Sun.COM void
4507978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(idm_svc_t *is)
4517978SPeter.Dunlap@Sun.COM {
4527978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&is->is_refcnt);
4537978SPeter.Dunlap@Sun.COM }
4547978SPeter.Dunlap@Sun.COM 
4557978SPeter.Dunlap@Sun.COM void
4567978SPeter.Dunlap@Sun.COM idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
4577978SPeter.Dunlap@Sun.COM {
4587978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele_and_destroy(&is->is_refcnt,
4597978SPeter.Dunlap@Sun.COM 	    (idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
4607978SPeter.Dunlap@Sun.COM }
4617978SPeter.Dunlap@Sun.COM 
4627978SPeter.Dunlap@Sun.COM /*
4637978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_online
4647978SPeter.Dunlap@Sun.COM  *
4657978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
4667978SPeter.Dunlap@Sun.COM  *
4677978SPeter.Dunlap@Sun.COM  * Online each transport service, as we want this target to be accessible
4687978SPeter.Dunlap@Sun.COM  * via any configured transport.
4697978SPeter.Dunlap@Sun.COM  *
4707978SPeter.Dunlap@Sun.COM  * When the initiator establishes a new connection to the target, IDM will
4717978SPeter.Dunlap@Sun.COM  * call the "new connect" callback defined in the idm_svc_req_t structure
4727978SPeter.Dunlap@Sun.COM  * and it will pass an idm_conn_t structure representing that new connection.
4737978SPeter.Dunlap@Sun.COM  */
4747978SPeter.Dunlap@Sun.COM idm_status_t
4757978SPeter.Dunlap@Sun.COM idm_tgt_svc_online(idm_svc_t *is)
4767978SPeter.Dunlap@Sun.COM {
4777978SPeter.Dunlap@Sun.COM 
4787978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
4797978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
4807978SPeter.Dunlap@Sun.COM 	int			rc;
4817978SPeter.Dunlap@Sun.COM 	int			svc_found;
4827978SPeter.Dunlap@Sun.COM 
4837978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
4847978SPeter.Dunlap@Sun.COM 	/* Walk through each of the transports and online them */
4857978SPeter.Dunlap@Sun.COM 	if (is->is_online == 0) {
4867978SPeter.Dunlap@Sun.COM 		svc_found = 0;
4877978SPeter.Dunlap@Sun.COM 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
4887978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[type];
4897978SPeter.Dunlap@Sun.COM 			if (it->it_ops == NULL) {
4907978SPeter.Dunlap@Sun.COM 				/* transport is not registered */
4917978SPeter.Dunlap@Sun.COM 				continue;
4927978SPeter.Dunlap@Sun.COM 			}
4937978SPeter.Dunlap@Sun.COM 
4947978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
4957978SPeter.Dunlap@Sun.COM 			rc = it->it_ops->it_tgt_svc_online(is);
4967978SPeter.Dunlap@Sun.COM 			mutex_enter(&is->is_mutex);
4977978SPeter.Dunlap@Sun.COM 			if (rc == IDM_STATUS_SUCCESS) {
4987978SPeter.Dunlap@Sun.COM 				/* We have at least one service running. */
4997978SPeter.Dunlap@Sun.COM 				svc_found = 1;
5007978SPeter.Dunlap@Sun.COM 			}
5017978SPeter.Dunlap@Sun.COM 		}
5027978SPeter.Dunlap@Sun.COM 	} else {
5037978SPeter.Dunlap@Sun.COM 		svc_found = 1;
5047978SPeter.Dunlap@Sun.COM 	}
5057978SPeter.Dunlap@Sun.COM 	if (svc_found)
5067978SPeter.Dunlap@Sun.COM 		is->is_online++;
5077978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
5087978SPeter.Dunlap@Sun.COM 
5097978SPeter.Dunlap@Sun.COM 	return (svc_found ? IDM_STATUS_SUCCESS : IDM_STATUS_FAIL);
5107978SPeter.Dunlap@Sun.COM }
5117978SPeter.Dunlap@Sun.COM 
5127978SPeter.Dunlap@Sun.COM /*
5137978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_offline
5147978SPeter.Dunlap@Sun.COM  *
5157978SPeter.Dunlap@Sun.COM  * is - idm_svc_t returned by the call to idm_tgt_svc_create
5167978SPeter.Dunlap@Sun.COM  *
5177978SPeter.Dunlap@Sun.COM  * Shutdown any online target services.
5187978SPeter.Dunlap@Sun.COM  */
5197978SPeter.Dunlap@Sun.COM void
5207978SPeter.Dunlap@Sun.COM idm_tgt_svc_offline(idm_svc_t *is)
5217978SPeter.Dunlap@Sun.COM {
5227978SPeter.Dunlap@Sun.COM 	idm_transport_type_t	type;
5237978SPeter.Dunlap@Sun.COM 	idm_transport_t		*it;
5247978SPeter.Dunlap@Sun.COM 
5257978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
5267978SPeter.Dunlap@Sun.COM 	is->is_online--;
5277978SPeter.Dunlap@Sun.COM 	if (is->is_online == 0) {
5287978SPeter.Dunlap@Sun.COM 		/* Walk through each of the transports and offline them */
5297978SPeter.Dunlap@Sun.COM 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
5307978SPeter.Dunlap@Sun.COM 			it = &idm_transport_list[type];
5317978SPeter.Dunlap@Sun.COM 			if (it->it_ops == NULL) {
5327978SPeter.Dunlap@Sun.COM 				/* transport is not registered */
5337978SPeter.Dunlap@Sun.COM 				continue;
5347978SPeter.Dunlap@Sun.COM 			}
5357978SPeter.Dunlap@Sun.COM 
5367978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
5377978SPeter.Dunlap@Sun.COM 			it->it_ops->it_tgt_svc_offline(is);
5387978SPeter.Dunlap@Sun.COM 			mutex_enter(&is->is_mutex);
5397978SPeter.Dunlap@Sun.COM 		}
5407978SPeter.Dunlap@Sun.COM 	}
5417978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
5427978SPeter.Dunlap@Sun.COM }
5437978SPeter.Dunlap@Sun.COM 
5447978SPeter.Dunlap@Sun.COM /*
5457978SPeter.Dunlap@Sun.COM  * idm_tgt_svc_lookup
5467978SPeter.Dunlap@Sun.COM  *
5477978SPeter.Dunlap@Sun.COM  * Lookup a service instance listening on the specified port
5487978SPeter.Dunlap@Sun.COM  */
5497978SPeter.Dunlap@Sun.COM 
5507978SPeter.Dunlap@Sun.COM idm_svc_t *
5517978SPeter.Dunlap@Sun.COM idm_tgt_svc_lookup(uint16_t port)
5527978SPeter.Dunlap@Sun.COM {
5537978SPeter.Dunlap@Sun.COM 	idm_svc_t *result;
5547978SPeter.Dunlap@Sun.COM 
5557978SPeter.Dunlap@Sun.COM retry:
5567978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
5577978SPeter.Dunlap@Sun.COM 	for (result = list_head(&idm.idm_tgt_svc_list);
5587978SPeter.Dunlap@Sun.COM 	    result != NULL;
5597978SPeter.Dunlap@Sun.COM 	    result = list_next(&idm.idm_tgt_svc_list, result)) {
5607978SPeter.Dunlap@Sun.COM 		if (result->is_svc_req.sr_port == port) {
5617978SPeter.Dunlap@Sun.COM 			if (result->is_online == 0) {
5627978SPeter.Dunlap@Sun.COM 				/*
5637978SPeter.Dunlap@Sun.COM 				 * A service exists on this port, but it
5647978SPeter.Dunlap@Sun.COM 				 * is going away, wait for it to cleanup.
5657978SPeter.Dunlap@Sun.COM 				 */
5667978SPeter.Dunlap@Sun.COM 				cv_wait(&idm.idm_tgt_svc_cv,
5677978SPeter.Dunlap@Sun.COM 				    &idm.idm_global_mutex);
5687978SPeter.Dunlap@Sun.COM 				mutex_exit(&idm.idm_global_mutex);
5697978SPeter.Dunlap@Sun.COM 				goto retry;
5707978SPeter.Dunlap@Sun.COM 			}
5717978SPeter.Dunlap@Sun.COM 			idm_tgt_svc_hold(result);
5727978SPeter.Dunlap@Sun.COM 			mutex_exit(&idm.idm_global_mutex);
5737978SPeter.Dunlap@Sun.COM 			return (result);
5747978SPeter.Dunlap@Sun.COM 		}
5757978SPeter.Dunlap@Sun.COM 	}
5767978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
5777978SPeter.Dunlap@Sun.COM 
5787978SPeter.Dunlap@Sun.COM 	return (NULL);
5797978SPeter.Dunlap@Sun.COM }
5807978SPeter.Dunlap@Sun.COM 
5817978SPeter.Dunlap@Sun.COM /*
5827978SPeter.Dunlap@Sun.COM  * idm_negotiate_key_values()
5837978SPeter.Dunlap@Sun.COM  * Give IDM level a chance to negotiate any login parameters it should own.
5847978SPeter.Dunlap@Sun.COM  *  -- leave unhandled parameters alone on request_nvl
5857978SPeter.Dunlap@Sun.COM  *  -- move all handled parameters to response_nvl with an appropriate response
5867978SPeter.Dunlap@Sun.COM  *  -- also add an entry to negotiated_nvl for any accepted parameters
5877978SPeter.Dunlap@Sun.COM  */
5887978SPeter.Dunlap@Sun.COM kv_status_t
5897978SPeter.Dunlap@Sun.COM idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
5907978SPeter.Dunlap@Sun.COM     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
5917978SPeter.Dunlap@Sun.COM {
5927978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_transport_ops != NULL);
5937978SPeter.Dunlap@Sun.COM 	return (ic->ic_transport_ops->it_negotiate_key_values(ic,
5947978SPeter.Dunlap@Sun.COM 	    request_nvl, response_nvl, negotiated_nvl));
5957978SPeter.Dunlap@Sun.COM }
5967978SPeter.Dunlap@Sun.COM 
5977978SPeter.Dunlap@Sun.COM /*
5987978SPeter.Dunlap@Sun.COM  * idm_notice_key_values()
5997978SPeter.Dunlap@Sun.COM  * Activate at the IDM level any parameters that have been negotiated.
6007978SPeter.Dunlap@Sun.COM  * Passes the set of key value pairs to the transport for activation.
6017978SPeter.Dunlap@Sun.COM  * This will be invoked as the connection is entering full-feature mode.
6027978SPeter.Dunlap@Sun.COM  */
6037978SPeter.Dunlap@Sun.COM idm_status_t
6047978SPeter.Dunlap@Sun.COM idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
6057978SPeter.Dunlap@Sun.COM {
6067978SPeter.Dunlap@Sun.COM 	ASSERT(ic->ic_transport_ops != NULL);
6077978SPeter.Dunlap@Sun.COM 	return (ic->ic_transport_ops->it_notice_key_values(ic,
6087978SPeter.Dunlap@Sun.COM 	    negotiated_nvl));
6097978SPeter.Dunlap@Sun.COM }
6107978SPeter.Dunlap@Sun.COM 
6117978SPeter.Dunlap@Sun.COM /*
6127978SPeter.Dunlap@Sun.COM  * idm_buf_tx_to_ini
6137978SPeter.Dunlap@Sun.COM  *
6147978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Put_Data' operational primitive.
6157978SPeter.Dunlap@Sun.COM  *
6167978SPeter.Dunlap@Sun.COM  * This function is invoked by a target iSCSI layer to request its local
6177978SPeter.Dunlap@Sun.COM  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
6187978SPeter.Dunlap@Sun.COM  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
6197978SPeter.Dunlap@Sun.COM  * transferred to the initiator associated with task 'idt'. The connection
6207978SPeter.Dunlap@Sun.COM  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
6217978SPeter.Dunlap@Sun.COM  * and the callback (idb->idb_buf_cb) at transfer completion are
6227978SPeter.Dunlap@Sun.COM  * provided as input.
6237978SPeter.Dunlap@Sun.COM  *
6247978SPeter.Dunlap@Sun.COM  * This data transfer takes place transparently to the remote iSCSI layer,
6257978SPeter.Dunlap@Sun.COM  * i.e. without its participation.
6267978SPeter.Dunlap@Sun.COM  *
6277978SPeter.Dunlap@Sun.COM  * Using sockets, IDM implements the data transfer by segmenting the data
6287978SPeter.Dunlap@Sun.COM  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
6297978SPeter.Dunlap@Sun.COM  * initiator. iSER performs the transfer using RDMA write.
6307978SPeter.Dunlap@Sun.COM  *
6317978SPeter.Dunlap@Sun.COM  */
6327978SPeter.Dunlap@Sun.COM idm_status_t
6337978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
6347978SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t xfer_len,
6357978SPeter.Dunlap@Sun.COM     idm_buf_cb_t idb_buf_cb, void *cb_arg)
6367978SPeter.Dunlap@Sun.COM {
6377978SPeter.Dunlap@Sun.COM 	idm_status_t rc;
6387978SPeter.Dunlap@Sun.COM 
6397978SPeter.Dunlap@Sun.COM 	idb->idb_bufoffset = offset;
6407978SPeter.Dunlap@Sun.COM 	idb->idb_xfer_len = xfer_len;
6417978SPeter.Dunlap@Sun.COM 	idb->idb_buf_cb = idb_buf_cb;
6427978SPeter.Dunlap@Sun.COM 	idb->idb_cb_arg = cb_arg;
6437978SPeter.Dunlap@Sun.COM 
6447978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
6457978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
6467978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
6477978SPeter.Dunlap@Sun.COM 		idt->idt_tx_to_ini_start++;
6487978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
6497978SPeter.Dunlap@Sun.COM 		idm_buf_bind_in_locked(idt, idb);
6507978SPeter.Dunlap@Sun.COM 		idb->idb_in_transport = B_TRUE;
6517978SPeter.Dunlap@Sun.COM 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
6527978SPeter.Dunlap@Sun.COM 		    (idt, idb);
6537978SPeter.Dunlap@Sun.COM 		return (rc);
6547978SPeter.Dunlap@Sun.COM 
6557978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
6567978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
6577978SPeter.Dunlap@Sun.COM 		/*
6587978SPeter.Dunlap@Sun.COM 		 * Bind buffer but don't start a transfer since the task
6597978SPeter.Dunlap@Sun.COM 		 * is suspended
6607978SPeter.Dunlap@Sun.COM 		 */
6617978SPeter.Dunlap@Sun.COM 		idm_buf_bind_in_locked(idt, idb);
6627978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
6637978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
6647978SPeter.Dunlap@Sun.COM 
6657978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
6667978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
6677978SPeter.Dunlap@Sun.COM 		/*
6687978SPeter.Dunlap@Sun.COM 		 * Once the task is aborted, any buffers added to the
6697978SPeter.Dunlap@Sun.COM 		 * idt_inbufv will never get cleaned up, so just return
6707978SPeter.Dunlap@Sun.COM 		 * SUCCESS.  The buffer should get cleaned up by the
6717978SPeter.Dunlap@Sun.COM 		 * client or framework once task_aborted has completed.
6727978SPeter.Dunlap@Sun.COM 		 */
6737978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
6747978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
6757978SPeter.Dunlap@Sun.COM 
6767978SPeter.Dunlap@Sun.COM 	default:
6777978SPeter.Dunlap@Sun.COM 		ASSERT(0);
6787978SPeter.Dunlap@Sun.COM 		break;
6797978SPeter.Dunlap@Sun.COM 	}
6807978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
6817978SPeter.Dunlap@Sun.COM 
6827978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_FAIL);
6837978SPeter.Dunlap@Sun.COM }
6847978SPeter.Dunlap@Sun.COM 
6857978SPeter.Dunlap@Sun.COM /*
6867978SPeter.Dunlap@Sun.COM  * idm_buf_rx_from_ini
6877978SPeter.Dunlap@Sun.COM  *
6887978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Get_Data' operational primitive.
6897978SPeter.Dunlap@Sun.COM  *
6907978SPeter.Dunlap@Sun.COM  * This function is invoked by a target iSCSI layer to request its local
6917978SPeter.Dunlap@Sun.COM  * Datamover layer to retrieve certain data identified by the R2T PDU from the
6927978SPeter.Dunlap@Sun.COM  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
6937978SPeter.Dunlap@Sun.COM  * mapped to the respective buffer by the task tags (ITT & TTT).
6947978SPeter.Dunlap@Sun.COM  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
6957978SPeter.Dunlap@Sun.COM  * the callback (idb->idb_buf_cb) notification for data transfer completion are
6967978SPeter.Dunlap@Sun.COM  * are provided as input.
6977978SPeter.Dunlap@Sun.COM  *
6987978SPeter.Dunlap@Sun.COM  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
6997978SPeter.Dunlap@Sun.COM  * Datamover layer, the local and remote Datamover layers transparently bring
7007978SPeter.Dunlap@Sun.COM  * about the data transfer requested by the R2T PDU, without the participation
7017978SPeter.Dunlap@Sun.COM  * of the iSCSI layers.
7027978SPeter.Dunlap@Sun.COM  *
7037978SPeter.Dunlap@Sun.COM  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
7047978SPeter.Dunlap@Sun.COM  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
7057978SPeter.Dunlap@Sun.COM  *
7067978SPeter.Dunlap@Sun.COM  */
7077978SPeter.Dunlap@Sun.COM idm_status_t
7087978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
7097978SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t xfer_len,
7107978SPeter.Dunlap@Sun.COM     idm_buf_cb_t idb_buf_cb, void *cb_arg)
7117978SPeter.Dunlap@Sun.COM {
7127978SPeter.Dunlap@Sun.COM 	idm_status_t rc;
7137978SPeter.Dunlap@Sun.COM 
7147978SPeter.Dunlap@Sun.COM 	idb->idb_bufoffset = offset;
7157978SPeter.Dunlap@Sun.COM 	idb->idb_xfer_len = xfer_len;
7167978SPeter.Dunlap@Sun.COM 	idb->idb_buf_cb = idb_buf_cb;
7177978SPeter.Dunlap@Sun.COM 	idb->idb_cb_arg = cb_arg;
7187978SPeter.Dunlap@Sun.COM 
7197978SPeter.Dunlap@Sun.COM 	/*
7207978SPeter.Dunlap@Sun.COM 	 * "In" buf list is for "Data In" PDU's, "Out" buf list is for
7217978SPeter.Dunlap@Sun.COM 	 * "Data Out" PDU's
7227978SPeter.Dunlap@Sun.COM 	 */
7237978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
7247978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
7257978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
7267978SPeter.Dunlap@Sun.COM 		idt->idt_rx_from_ini_start++;
7277978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
7287978SPeter.Dunlap@Sun.COM 		idm_buf_bind_out_locked(idt, idb);
7297978SPeter.Dunlap@Sun.COM 		idb->idb_in_transport = B_TRUE;
7307978SPeter.Dunlap@Sun.COM 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
7317978SPeter.Dunlap@Sun.COM 		    (idt, idb);
7327978SPeter.Dunlap@Sun.COM 		return (rc);
7337978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
7347978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
7357978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
7367978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
7377978SPeter.Dunlap@Sun.COM 		/*
7387978SPeter.Dunlap@Sun.COM 		 * Bind buffer but don't start a transfer since the task
7397978SPeter.Dunlap@Sun.COM 		 * is suspended
7407978SPeter.Dunlap@Sun.COM 		 */
7417978SPeter.Dunlap@Sun.COM 		idm_buf_bind_out_locked(idt, idb);
7427978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
7437978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
7447978SPeter.Dunlap@Sun.COM 	default:
7457978SPeter.Dunlap@Sun.COM 		ASSERT(0);
7467978SPeter.Dunlap@Sun.COM 		break;
7477978SPeter.Dunlap@Sun.COM 	}
7487978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
7497978SPeter.Dunlap@Sun.COM 
7507978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_FAIL);
7517978SPeter.Dunlap@Sun.COM }
7527978SPeter.Dunlap@Sun.COM 
7537978SPeter.Dunlap@Sun.COM /*
7547978SPeter.Dunlap@Sun.COM  * idm_buf_tx_to_ini_done
7557978SPeter.Dunlap@Sun.COM  *
7567978SPeter.Dunlap@Sun.COM  * The transport calls this after it has completed a transfer requested by
7577978SPeter.Dunlap@Sun.COM  * a call to transport_buf_tx_to_ini
7587978SPeter.Dunlap@Sun.COM  *
7597978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
7607978SPeter.Dunlap@Sun.COM  * idt may be freed after the call to idb->idb_buf_cb.
7617978SPeter.Dunlap@Sun.COM  */
7627978SPeter.Dunlap@Sun.COM void
7637978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
7647978SPeter.Dunlap@Sun.COM {
7657978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
7667978SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
7677978SPeter.Dunlap@Sun.COM 	idb->idb_tx_thread = B_FALSE;
7687978SPeter.Dunlap@Sun.COM 	idt->idt_tx_to_ini_done++;
7697978SPeter.Dunlap@Sun.COM 
7707978SPeter.Dunlap@Sun.COM 	/*
7717978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
7727978SPeter.Dunlap@Sun.COM 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
7737978SPeter.Dunlap@Sun.COM 	 * to 0.
7747978SPeter.Dunlap@Sun.COM 	 */
7757978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
7767978SPeter.Dunlap@Sun.COM 	idb->idb_status = status;
7777978SPeter.Dunlap@Sun.COM 
7787978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
7797978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
7807978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_in_locked(idt, idb);
7817978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
7827978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, status);
7837978SPeter.Dunlap@Sun.COM 		return;
7847978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
7857978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
7867978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
7877978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
7887978SPeter.Dunlap@Sun.COM 		/*
7897978SPeter.Dunlap@Sun.COM 		 * To keep things simple we will ignore the case where the
7907978SPeter.Dunlap@Sun.COM 		 * transfer was successful and leave all buffers bound to the
7917978SPeter.Dunlap@Sun.COM 		 * task.  This allows us to also ignore the case where we've
7927978SPeter.Dunlap@Sun.COM 		 * been asked to abort a task but the last transfer of the
7937978SPeter.Dunlap@Sun.COM 		 * task has completed.  IDM has no idea whether this was, in
7947978SPeter.Dunlap@Sun.COM 		 * fact, the last transfer of the task so it would be difficult
7957978SPeter.Dunlap@Sun.COM 		 * to handle this case.  Everything should get sorted out again
7967978SPeter.Dunlap@Sun.COM 		 * after task reassignment is complete.
7977978SPeter.Dunlap@Sun.COM 		 *
7987978SPeter.Dunlap@Sun.COM 		 * In the case of TASK_ABORTING we could conceivably call the
7997978SPeter.Dunlap@Sun.COM 		 * buffer callback here but the timing of when the client's
8007978SPeter.Dunlap@Sun.COM 		 * client_task_aborted callback is invoked vs. when the client's
8017978SPeter.Dunlap@Sun.COM 		 * buffer callback gets invoked gets sticky.  We don't want
8027978SPeter.Dunlap@Sun.COM 		 * the client to here from us again after the call to
8037978SPeter.Dunlap@Sun.COM 		 * client_task_aborted() but we don't want to give it a bunch
8047978SPeter.Dunlap@Sun.COM 		 * of failed buffer transfers until we've called
8057978SPeter.Dunlap@Sun.COM 		 * client_task_aborted().  Instead we'll just leave all the
8067978SPeter.Dunlap@Sun.COM 		 * buffers bound and allow the client to cleanup.
8077978SPeter.Dunlap@Sun.COM 		 */
8087978SPeter.Dunlap@Sun.COM 		break;
8097978SPeter.Dunlap@Sun.COM 	default:
8107978SPeter.Dunlap@Sun.COM 		ASSERT(0);
8117978SPeter.Dunlap@Sun.COM 	}
8127978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
8137978SPeter.Dunlap@Sun.COM }
8147978SPeter.Dunlap@Sun.COM 
8157978SPeter.Dunlap@Sun.COM /*
8167978SPeter.Dunlap@Sun.COM  * idm_buf_rx_from_ini_done
8177978SPeter.Dunlap@Sun.COM  *
8187978SPeter.Dunlap@Sun.COM  * The transport calls this after it has completed a transfer requested by
8197978SPeter.Dunlap@Sun.COM  * a call totransport_buf_tx_to_ini
8207978SPeter.Dunlap@Sun.COM  *
8217978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
8227978SPeter.Dunlap@Sun.COM  * idt may be freed after the call to idb->idb_buf_cb.
8237978SPeter.Dunlap@Sun.COM  */
8247978SPeter.Dunlap@Sun.COM void
8257978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
8267978SPeter.Dunlap@Sun.COM {
8277978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
8287978SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
8297978SPeter.Dunlap@Sun.COM 	idt->idt_rx_from_ini_done++;
8307978SPeter.Dunlap@Sun.COM 
8317978SPeter.Dunlap@Sun.COM 	/*
8327978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
8337978SPeter.Dunlap@Sun.COM 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
8347978SPeter.Dunlap@Sun.COM 	 * to 0.
8357978SPeter.Dunlap@Sun.COM 	 */
8367978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
8377978SPeter.Dunlap@Sun.COM 	idb->idb_status = status;
8387978SPeter.Dunlap@Sun.COM 
8397978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
8407978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
8417978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_out_locked(idt, idb);
8427978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
8437978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, status);
8447978SPeter.Dunlap@Sun.COM 		return;
8457978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
8467978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
8477978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
8487978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
8497978SPeter.Dunlap@Sun.COM 		/*
8507978SPeter.Dunlap@Sun.COM 		 * To keep things simple we will ignore the case where the
8517978SPeter.Dunlap@Sun.COM 		 * transfer was successful and leave all buffers bound to the
8527978SPeter.Dunlap@Sun.COM 		 * task.  This allows us to also ignore the case where we've
8537978SPeter.Dunlap@Sun.COM 		 * been asked to abort a task but the last transfer of the
8547978SPeter.Dunlap@Sun.COM 		 * task has completed.  IDM has no idea whether this was, in
8557978SPeter.Dunlap@Sun.COM 		 * fact, the last transfer of the task so it would be difficult
8567978SPeter.Dunlap@Sun.COM 		 * to handle this case.  Everything should get sorted out again
8577978SPeter.Dunlap@Sun.COM 		 * after task reassignment is complete.
8587978SPeter.Dunlap@Sun.COM 		 *
8597978SPeter.Dunlap@Sun.COM 		 * In the case of TASK_ABORTING we could conceivably call the
8607978SPeter.Dunlap@Sun.COM 		 * buffer callback here but the timing of when the client's
8617978SPeter.Dunlap@Sun.COM 		 * client_task_aborted callback is invoked vs. when the client's
8627978SPeter.Dunlap@Sun.COM 		 * buffer callback gets invoked gets sticky.  We don't want
8637978SPeter.Dunlap@Sun.COM 		 * the client to here from us again after the call to
8647978SPeter.Dunlap@Sun.COM 		 * client_task_aborted() but we don't want to give it a bunch
8657978SPeter.Dunlap@Sun.COM 		 * of failed buffer transfers until we've called
8667978SPeter.Dunlap@Sun.COM 		 * client_task_aborted().  Instead we'll just leave all the
8677978SPeter.Dunlap@Sun.COM 		 * buffers bound and allow the client to cleanup.
8687978SPeter.Dunlap@Sun.COM 		 */
8697978SPeter.Dunlap@Sun.COM 		break;
8707978SPeter.Dunlap@Sun.COM 	default:
8717978SPeter.Dunlap@Sun.COM 		ASSERT(0);
8727978SPeter.Dunlap@Sun.COM 	}
8737978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
8747978SPeter.Dunlap@Sun.COM }
8757978SPeter.Dunlap@Sun.COM 
8767978SPeter.Dunlap@Sun.COM /*
8777978SPeter.Dunlap@Sun.COM  * idm_buf_alloc
8787978SPeter.Dunlap@Sun.COM  *
8797978SPeter.Dunlap@Sun.COM  * Allocates a buffer handle and registers it for use with the transport
8807978SPeter.Dunlap@Sun.COM  * layer. If a buffer is not passed on bufptr, the buffer will be allocated
8817978SPeter.Dunlap@Sun.COM  * as well as the handle.
8827978SPeter.Dunlap@Sun.COM  *
8837978SPeter.Dunlap@Sun.COM  * ic		- connection on which the buffer will be transferred
8847978SPeter.Dunlap@Sun.COM  * bufptr	- allocate memory for buffer if NULL, else assign to buffer
8857978SPeter.Dunlap@Sun.COM  * buflen	- length of buffer
8867978SPeter.Dunlap@Sun.COM  *
8877978SPeter.Dunlap@Sun.COM  * Returns idm_buf_t handle if successful, otherwise NULL
8887978SPeter.Dunlap@Sun.COM  */
8897978SPeter.Dunlap@Sun.COM idm_buf_t *
8907978SPeter.Dunlap@Sun.COM idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
8917978SPeter.Dunlap@Sun.COM {
8927978SPeter.Dunlap@Sun.COM 	idm_buf_t	*buf = NULL;
8937978SPeter.Dunlap@Sun.COM 	int		rc;
8947978SPeter.Dunlap@Sun.COM 
8957978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
8967978SPeter.Dunlap@Sun.COM 	ASSERT(idm.idm_buf_cache != NULL);
8977978SPeter.Dunlap@Sun.COM 	ASSERT(buflen > 0);
8987978SPeter.Dunlap@Sun.COM 
8997978SPeter.Dunlap@Sun.COM 	/* Don't allocate new buffers if we are not in FFP */
9007978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
9017978SPeter.Dunlap@Sun.COM 	if (!ic->ic_ffp) {
9027978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
9037978SPeter.Dunlap@Sun.COM 		return (NULL);
9047978SPeter.Dunlap@Sun.COM 	}
9057978SPeter.Dunlap@Sun.COM 
9067978SPeter.Dunlap@Sun.COM 
9077978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
9087978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
9097978SPeter.Dunlap@Sun.COM 
9107978SPeter.Dunlap@Sun.COM 	buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP);
9117978SPeter.Dunlap@Sun.COM 	if (buf == NULL) {
9127978SPeter.Dunlap@Sun.COM 		idm_conn_rele(ic);
9137978SPeter.Dunlap@Sun.COM 		return (NULL);
9147978SPeter.Dunlap@Sun.COM 	}
9157978SPeter.Dunlap@Sun.COM 
9167978SPeter.Dunlap@Sun.COM 	buf->idb_ic		= ic;
9177978SPeter.Dunlap@Sun.COM 	buf->idb_buflen		= buflen;
9187978SPeter.Dunlap@Sun.COM 	buf->idb_exp_offset	= 0;
9197978SPeter.Dunlap@Sun.COM 	buf->idb_bufoffset	= 0;
9207978SPeter.Dunlap@Sun.COM 	buf->idb_xfer_len 	= 0;
9217978SPeter.Dunlap@Sun.COM 	buf->idb_magic		= IDM_BUF_MAGIC;
9227978SPeter.Dunlap@Sun.COM 
9237978SPeter.Dunlap@Sun.COM 	/*
9247978SPeter.Dunlap@Sun.COM 	 * If bufptr is NULL, we have an implicit request to allocate
9257978SPeter.Dunlap@Sun.COM 	 * memory for this IDM buffer handle and register it for use
9267978SPeter.Dunlap@Sun.COM 	 * with the transport. To simplify this, and to give more freedom
9277978SPeter.Dunlap@Sun.COM 	 * to the transport layer for it's own buffer management, both of
9287978SPeter.Dunlap@Sun.COM 	 * these actions will take place in the transport layer.
9297978SPeter.Dunlap@Sun.COM 	 * If bufptr is set, then the caller has allocated memory (or more
9307978SPeter.Dunlap@Sun.COM 	 * likely it's been passed from an upper layer), and we need only
9317978SPeter.Dunlap@Sun.COM 	 * register the buffer for use with the transport layer.
9327978SPeter.Dunlap@Sun.COM 	 */
9337978SPeter.Dunlap@Sun.COM 	if (bufptr == NULL) {
9347978SPeter.Dunlap@Sun.COM 		/*
9357978SPeter.Dunlap@Sun.COM 		 * Allocate a buffer from the transport layer (which
9367978SPeter.Dunlap@Sun.COM 		 * will also register the buffer for use).
9377978SPeter.Dunlap@Sun.COM 		 */
9387978SPeter.Dunlap@Sun.COM 		rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen);
9397978SPeter.Dunlap@Sun.COM 		if (rc != 0) {
9407978SPeter.Dunlap@Sun.COM 			idm_conn_rele(ic);
9417978SPeter.Dunlap@Sun.COM 			kmem_cache_free(idm.idm_buf_cache, buf);
9427978SPeter.Dunlap@Sun.COM 			return (NULL);
9437978SPeter.Dunlap@Sun.COM 		}
9447978SPeter.Dunlap@Sun.COM 		/* Set the bufalloc'd flag */
9457978SPeter.Dunlap@Sun.COM 		buf->idb_bufalloc = B_TRUE;
9467978SPeter.Dunlap@Sun.COM 	} else {
9477978SPeter.Dunlap@Sun.COM 		/*
9487978SPeter.Dunlap@Sun.COM 		 * Set the passed bufptr into the buf handle, and
9497978SPeter.Dunlap@Sun.COM 		 * register the handle with the transport layer.
9507978SPeter.Dunlap@Sun.COM 		 */
9517978SPeter.Dunlap@Sun.COM 		buf->idb_buf = bufptr;
9527978SPeter.Dunlap@Sun.COM 
9537978SPeter.Dunlap@Sun.COM 		rc = ic->ic_transport_ops->it_buf_setup(buf);
9547978SPeter.Dunlap@Sun.COM 		if (rc != 0) {
9557978SPeter.Dunlap@Sun.COM 			idm_conn_rele(ic);
9567978SPeter.Dunlap@Sun.COM 			kmem_cache_free(idm.idm_buf_cache, buf);
9577978SPeter.Dunlap@Sun.COM 			return (NULL);
9587978SPeter.Dunlap@Sun.COM 		}
9597978SPeter.Dunlap@Sun.COM 		/* Ensure bufalloc'd flag is unset */
9607978SPeter.Dunlap@Sun.COM 		buf->idb_bufalloc = B_FALSE;
9617978SPeter.Dunlap@Sun.COM 	}
9627978SPeter.Dunlap@Sun.COM 
9637978SPeter.Dunlap@Sun.COM 	return (buf);
9647978SPeter.Dunlap@Sun.COM 
9657978SPeter.Dunlap@Sun.COM }
9667978SPeter.Dunlap@Sun.COM 
9677978SPeter.Dunlap@Sun.COM /*
9687978SPeter.Dunlap@Sun.COM  * idm_buf_free
9697978SPeter.Dunlap@Sun.COM  *
9707978SPeter.Dunlap@Sun.COM  * Release a buffer handle along with the associated buffer that was allocated
9717978SPeter.Dunlap@Sun.COM  * or assigned with idm_buf_alloc
9727978SPeter.Dunlap@Sun.COM  */
9737978SPeter.Dunlap@Sun.COM void
9747978SPeter.Dunlap@Sun.COM idm_buf_free(idm_buf_t *buf)
9757978SPeter.Dunlap@Sun.COM {
9767978SPeter.Dunlap@Sun.COM 	idm_conn_t *ic = buf->idb_ic;
9777978SPeter.Dunlap@Sun.COM 
9787978SPeter.Dunlap@Sun.COM 
9797978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding	= NULL;
9807978SPeter.Dunlap@Sun.COM 
9817978SPeter.Dunlap@Sun.COM 	if (buf->idb_bufalloc) {
9827978SPeter.Dunlap@Sun.COM 		ic->ic_transport_ops->it_buf_free(buf);
9837978SPeter.Dunlap@Sun.COM 	} else {
9847978SPeter.Dunlap@Sun.COM 		ic->ic_transport_ops->it_buf_teardown(buf);
9857978SPeter.Dunlap@Sun.COM 	}
9867978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_buf_cache, buf);
9877978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
9887978SPeter.Dunlap@Sun.COM }
9897978SPeter.Dunlap@Sun.COM 
9907978SPeter.Dunlap@Sun.COM /*
9917978SPeter.Dunlap@Sun.COM  * idm_buf_bind_in
9927978SPeter.Dunlap@Sun.COM  *
9937978SPeter.Dunlap@Sun.COM  * This function associates a buffer with a task. This is only for use by the
9947978SPeter.Dunlap@Sun.COM  * iSCSI initiator that will have only one buffer per transfer direction
9957978SPeter.Dunlap@Sun.COM  *
9967978SPeter.Dunlap@Sun.COM  */
9977978SPeter.Dunlap@Sun.COM void
9987978SPeter.Dunlap@Sun.COM idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
9997978SPeter.Dunlap@Sun.COM {
10007978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
10017978SPeter.Dunlap@Sun.COM 	idm_buf_bind_in_locked(idt, buf);
10027978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
10037978SPeter.Dunlap@Sun.COM }
10047978SPeter.Dunlap@Sun.COM 
10057978SPeter.Dunlap@Sun.COM static void
10067978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf)
10077978SPeter.Dunlap@Sun.COM {
10087978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding = idt;
10097978SPeter.Dunlap@Sun.COM 	buf->idb_ic = idt->idt_ic;
10107978SPeter.Dunlap@Sun.COM 	idm_listbuf_insert(&idt->idt_inbufv, buf);
10117978SPeter.Dunlap@Sun.COM }
10127978SPeter.Dunlap@Sun.COM 
10137978SPeter.Dunlap@Sun.COM void
10147978SPeter.Dunlap@Sun.COM idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
10157978SPeter.Dunlap@Sun.COM {
10167978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
10177978SPeter.Dunlap@Sun.COM 	idm_buf_bind_out_locked(idt, buf);
10187978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
10197978SPeter.Dunlap@Sun.COM }
10207978SPeter.Dunlap@Sun.COM 
10217978SPeter.Dunlap@Sun.COM static void
10227978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf)
10237978SPeter.Dunlap@Sun.COM {
10247978SPeter.Dunlap@Sun.COM 	buf->idb_task_binding = idt;
10257978SPeter.Dunlap@Sun.COM 	buf->idb_ic = idt->idt_ic;
10267978SPeter.Dunlap@Sun.COM 	idm_listbuf_insert(&idt->idt_outbufv, buf);
10277978SPeter.Dunlap@Sun.COM }
10287978SPeter.Dunlap@Sun.COM 
10297978SPeter.Dunlap@Sun.COM void
10307978SPeter.Dunlap@Sun.COM idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
10317978SPeter.Dunlap@Sun.COM {
10327978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
10337978SPeter.Dunlap@Sun.COM 	idm_buf_unbind_in_locked(idt, buf);
10347978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
10357978SPeter.Dunlap@Sun.COM }
10367978SPeter.Dunlap@Sun.COM 
10377978SPeter.Dunlap@Sun.COM static void
10387978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf)
10397978SPeter.Dunlap@Sun.COM {
10407978SPeter.Dunlap@Sun.COM 	list_remove(&idt->idt_inbufv, buf);
10417978SPeter.Dunlap@Sun.COM }
10427978SPeter.Dunlap@Sun.COM 
10437978SPeter.Dunlap@Sun.COM void
10447978SPeter.Dunlap@Sun.COM idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
10457978SPeter.Dunlap@Sun.COM {
10467978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
10477978SPeter.Dunlap@Sun.COM 	idm_buf_unbind_out_locked(idt, buf);
10487978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
10497978SPeter.Dunlap@Sun.COM }
10507978SPeter.Dunlap@Sun.COM 
10517978SPeter.Dunlap@Sun.COM static void
10527978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf)
10537978SPeter.Dunlap@Sun.COM {
10547978SPeter.Dunlap@Sun.COM 	list_remove(&idt->idt_outbufv, buf);
10557978SPeter.Dunlap@Sun.COM }
10567978SPeter.Dunlap@Sun.COM 
10577978SPeter.Dunlap@Sun.COM /*
10587978SPeter.Dunlap@Sun.COM  * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the
10597978SPeter.Dunlap@Sun.COM  * iSCSI PDU
10607978SPeter.Dunlap@Sun.COM  */
10617978SPeter.Dunlap@Sun.COM idm_buf_t *
10627978SPeter.Dunlap@Sun.COM idm_buf_find(void *lbuf, size_t data_offset)
10637978SPeter.Dunlap@Sun.COM {
10647978SPeter.Dunlap@Sun.COM 	idm_buf_t	*idb;
10657978SPeter.Dunlap@Sun.COM 	list_t		*lst = (list_t *)lbuf;
10667978SPeter.Dunlap@Sun.COM 
10677978SPeter.Dunlap@Sun.COM 	/* iterate through the list to find the buffer */
10687978SPeter.Dunlap@Sun.COM 	for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
10697978SPeter.Dunlap@Sun.COM 
10707978SPeter.Dunlap@Sun.COM 		ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) ||
10717978SPeter.Dunlap@Sun.COM 		    (idb->idb_bufoffset == 0));
10727978SPeter.Dunlap@Sun.COM 
10737978SPeter.Dunlap@Sun.COM 		if ((data_offset >= idb->idb_bufoffset) &&
10747978SPeter.Dunlap@Sun.COM 		    (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
10757978SPeter.Dunlap@Sun.COM 
10767978SPeter.Dunlap@Sun.COM 			return (idb);
10777978SPeter.Dunlap@Sun.COM 		}
10787978SPeter.Dunlap@Sun.COM 	}
10797978SPeter.Dunlap@Sun.COM 
10807978SPeter.Dunlap@Sun.COM 	return (NULL);
10817978SPeter.Dunlap@Sun.COM }
10827978SPeter.Dunlap@Sun.COM 
10837978SPeter.Dunlap@Sun.COM /*
10847978SPeter.Dunlap@Sun.COM  * idm_task_alloc
10857978SPeter.Dunlap@Sun.COM  *
10867978SPeter.Dunlap@Sun.COM  * This function will allocate a idm_task_t structure. A task tag is also
10877978SPeter.Dunlap@Sun.COM  * generated and saved in idt_tt. The task is not active.
10887978SPeter.Dunlap@Sun.COM  */
10897978SPeter.Dunlap@Sun.COM idm_task_t *
10907978SPeter.Dunlap@Sun.COM idm_task_alloc(idm_conn_t *ic)
10917978SPeter.Dunlap@Sun.COM {
10927978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt;
10937978SPeter.Dunlap@Sun.COM 
10947978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
10957978SPeter.Dunlap@Sun.COM 
10967978SPeter.Dunlap@Sun.COM 	/* Don't allocate new tasks if we are not in FFP */
10977978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
10987978SPeter.Dunlap@Sun.COM 	if (!ic->ic_ffp) {
10997978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
11007978SPeter.Dunlap@Sun.COM 		return (NULL);
11017978SPeter.Dunlap@Sun.COM 	}
11027978SPeter.Dunlap@Sun.COM 	idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP);
11037978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
11047978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
11057978SPeter.Dunlap@Sun.COM 		return (NULL);
11067978SPeter.Dunlap@Sun.COM 	}
11077978SPeter.Dunlap@Sun.COM 
11087978SPeter.Dunlap@Sun.COM 	ASSERT(list_is_empty(&idt->idt_inbufv));
11097978SPeter.Dunlap@Sun.COM 	ASSERT(list_is_empty(&idt->idt_outbufv));
11107978SPeter.Dunlap@Sun.COM 
11117978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
11127978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
11137978SPeter.Dunlap@Sun.COM 
11147978SPeter.Dunlap@Sun.COM 	idt->idt_state		= TASK_IDLE;
11157978SPeter.Dunlap@Sun.COM 	idt->idt_ic		= ic;
11167978SPeter.Dunlap@Sun.COM 	idt->idt_private 	= NULL;
11177978SPeter.Dunlap@Sun.COM 	idt->idt_exp_datasn	= 0;
11187978SPeter.Dunlap@Sun.COM 	idt->idt_exp_rttsn	= 0;
11197978SPeter.Dunlap@Sun.COM 
11207978SPeter.Dunlap@Sun.COM 	return (idt);
11217978SPeter.Dunlap@Sun.COM }
11227978SPeter.Dunlap@Sun.COM 
11237978SPeter.Dunlap@Sun.COM /*
11247978SPeter.Dunlap@Sun.COM  * idm_task_start
11257978SPeter.Dunlap@Sun.COM  *
11267978SPeter.Dunlap@Sun.COM  * Add the task to an AVL tree to notify IDM about a new task. The caller
11277978SPeter.Dunlap@Sun.COM  * sets up the idm_task_t structure with a prior call to idm_task_alloc().
11287978SPeter.Dunlap@Sun.COM  * The task service does not function as a task/work engine, it is the
11297978SPeter.Dunlap@Sun.COM  * responsibility of the initiator to start the data transfer and free the
11307978SPeter.Dunlap@Sun.COM  * resources.
11317978SPeter.Dunlap@Sun.COM  */
11327978SPeter.Dunlap@Sun.COM void
11337978SPeter.Dunlap@Sun.COM idm_task_start(idm_task_t *idt, uintptr_t handle)
11347978SPeter.Dunlap@Sun.COM {
11357978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
11367978SPeter.Dunlap@Sun.COM 
11377978SPeter.Dunlap@Sun.COM 	/* mark the task as ACTIVE */
11387978SPeter.Dunlap@Sun.COM 	idt->idt_state = TASK_ACTIVE;
11397978SPeter.Dunlap@Sun.COM 	idt->idt_client_handle = handle;
11407978SPeter.Dunlap@Sun.COM 	idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
11417978SPeter.Dunlap@Sun.COM 	    idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 0;
11427978SPeter.Dunlap@Sun.COM }
11437978SPeter.Dunlap@Sun.COM 
11447978SPeter.Dunlap@Sun.COM /*
11457978SPeter.Dunlap@Sun.COM  * idm_task_done
11467978SPeter.Dunlap@Sun.COM  *
11477978SPeter.Dunlap@Sun.COM  * This function will remove the task from the AVL tree indicating that the
11487978SPeter.Dunlap@Sun.COM  * task is no longer active.
11497978SPeter.Dunlap@Sun.COM  */
11507978SPeter.Dunlap@Sun.COM void
11517978SPeter.Dunlap@Sun.COM idm_task_done(idm_task_t *idt)
11527978SPeter.Dunlap@Sun.COM {
11537978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
11547978SPeter.Dunlap@Sun.COM 	ASSERT(idt->idt_refcnt.ir_refcnt == 0);
11557978SPeter.Dunlap@Sun.COM 
11567978SPeter.Dunlap@Sun.COM 	idt->idt_state = TASK_IDLE;
11577978SPeter.Dunlap@Sun.COM 	idm_refcnt_reset(&idt->idt_refcnt);
11587978SPeter.Dunlap@Sun.COM }
11597978SPeter.Dunlap@Sun.COM 
11607978SPeter.Dunlap@Sun.COM /*
11617978SPeter.Dunlap@Sun.COM  * idm_task_free
11627978SPeter.Dunlap@Sun.COM  *
11637978SPeter.Dunlap@Sun.COM  * This function will free the Task Tag and the memory allocated for the task
11647978SPeter.Dunlap@Sun.COM  * idm_task_done should be called prior to this call
11657978SPeter.Dunlap@Sun.COM  */
11667978SPeter.Dunlap@Sun.COM void
11677978SPeter.Dunlap@Sun.COM idm_task_free(idm_task_t *idt)
11687978SPeter.Dunlap@Sun.COM {
11697978SPeter.Dunlap@Sun.COM 	idm_conn_t *ic = idt->idt_ic;
11707978SPeter.Dunlap@Sun.COM 
11717978SPeter.Dunlap@Sun.COM 	ASSERT(idt != NULL);
11727978SPeter.Dunlap@Sun.COM 	ASSERT(idt->idt_state == TASK_IDLE);
11737978SPeter.Dunlap@Sun.COM 
11747978SPeter.Dunlap@Sun.COM 	/*
11757978SPeter.Dunlap@Sun.COM 	 * It's possible for items to still be in the idt_inbufv list if
11767978SPeter.Dunlap@Sun.COM 	 * they were added after idm_task_cleanup was called.  We rely on
11777978SPeter.Dunlap@Sun.COM 	 * STMF to free all buffers associated with the task however STMF
11787978SPeter.Dunlap@Sun.COM 	 * doesn't know that we have this reference to the buffers.
11797978SPeter.Dunlap@Sun.COM 	 * Use list_create so that we don't end up with stale references
11807978SPeter.Dunlap@Sun.COM 	 * to these buffers.
11817978SPeter.Dunlap@Sun.COM 	 */
11827978SPeter.Dunlap@Sun.COM 	list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
11837978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
11847978SPeter.Dunlap@Sun.COM 	list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
11857978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
11867978SPeter.Dunlap@Sun.COM 
11877978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_task_cache, idt);
11887978SPeter.Dunlap@Sun.COM 
11897978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
11907978SPeter.Dunlap@Sun.COM }
11917978SPeter.Dunlap@Sun.COM 
11927978SPeter.Dunlap@Sun.COM /*
11937978SPeter.Dunlap@Sun.COM  * idm_task_find
11947978SPeter.Dunlap@Sun.COM  *
11957978SPeter.Dunlap@Sun.COM  * This function looks up a task by task tag
11967978SPeter.Dunlap@Sun.COM  */
11977978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
11987978SPeter.Dunlap@Sun.COM idm_task_t *
11997978SPeter.Dunlap@Sun.COM idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
12007978SPeter.Dunlap@Sun.COM {
12017978SPeter.Dunlap@Sun.COM 	uint32_t	tt, client_handle;
12027978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt;
12037978SPeter.Dunlap@Sun.COM 
12047978SPeter.Dunlap@Sun.COM 	/*
12057978SPeter.Dunlap@Sun.COM 	 * Must match both itt and ttt.  The table is indexed by itt
12067978SPeter.Dunlap@Sun.COM 	 * for initiator connections and ttt for target connections.
12077978SPeter.Dunlap@Sun.COM 	 */
12087978SPeter.Dunlap@Sun.COM 	if (IDM_CONN_ISTGT(ic)) {
12097978SPeter.Dunlap@Sun.COM 		tt = ttt;
12107978SPeter.Dunlap@Sun.COM 		client_handle = itt;
12117978SPeter.Dunlap@Sun.COM 	} else {
12127978SPeter.Dunlap@Sun.COM 		tt = itt;
12137978SPeter.Dunlap@Sun.COM 		client_handle = ttt;
12147978SPeter.Dunlap@Sun.COM 	}
12157978SPeter.Dunlap@Sun.COM 
12167978SPeter.Dunlap@Sun.COM 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
12177978SPeter.Dunlap@Sun.COM 	if (tt >= idm.idm_taskid_max) {
12187978SPeter.Dunlap@Sun.COM 		rw_exit(&idm.idm_taskid_table_lock);
12197978SPeter.Dunlap@Sun.COM 		return (NULL);
12207978SPeter.Dunlap@Sun.COM 	}
12217978SPeter.Dunlap@Sun.COM 
12227978SPeter.Dunlap@Sun.COM 	idt = idm.idm_taskid_table[tt];
12237978SPeter.Dunlap@Sun.COM 
12247978SPeter.Dunlap@Sun.COM 	if (idt != NULL) {
12257978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
12267978SPeter.Dunlap@Sun.COM 		if ((idt->idt_state != TASK_ACTIVE) ||
12277978SPeter.Dunlap@Sun.COM 		    (IDM_CONN_ISTGT(ic) &&
12287978SPeter.Dunlap@Sun.COM 		    (idt->idt_client_handle != client_handle))) {
12297978SPeter.Dunlap@Sun.COM 			/*
12307978SPeter.Dunlap@Sun.COM 			 * Task is aborting, we don't want any more references.
12317978SPeter.Dunlap@Sun.COM 			 */
12327978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
12337978SPeter.Dunlap@Sun.COM 			rw_exit(&idm.idm_taskid_table_lock);
12347978SPeter.Dunlap@Sun.COM 			return (NULL);
12357978SPeter.Dunlap@Sun.COM 		}
12367978SPeter.Dunlap@Sun.COM 		idm_task_hold(idt);
12377978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
12387978SPeter.Dunlap@Sun.COM 	}
12397978SPeter.Dunlap@Sun.COM 	rw_exit(&idm.idm_taskid_table_lock);
12407978SPeter.Dunlap@Sun.COM 
12417978SPeter.Dunlap@Sun.COM 	return (idt);
12427978SPeter.Dunlap@Sun.COM }
12437978SPeter.Dunlap@Sun.COM 
12447978SPeter.Dunlap@Sun.COM /*
12457978SPeter.Dunlap@Sun.COM  * idm_task_find_by_handle
12467978SPeter.Dunlap@Sun.COM  *
12477978SPeter.Dunlap@Sun.COM  * This function looks up a task by the client-private idt_client_handle.
12487978SPeter.Dunlap@Sun.COM  *
12497978SPeter.Dunlap@Sun.COM  * This function should NEVER be called in the performance path.  It is
12507978SPeter.Dunlap@Sun.COM  * intended strictly for error recovery/task management.
12517978SPeter.Dunlap@Sun.COM  */
12527978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
12537978SPeter.Dunlap@Sun.COM void *
12547978SPeter.Dunlap@Sun.COM idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle)
12557978SPeter.Dunlap@Sun.COM {
12567978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt = NULL;
12577978SPeter.Dunlap@Sun.COM 	int		idx = 0;
12587978SPeter.Dunlap@Sun.COM 
12597978SPeter.Dunlap@Sun.COM 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
12607978SPeter.Dunlap@Sun.COM 
12617978SPeter.Dunlap@Sun.COM 	for (idx = 0; idx < idm.idm_taskid_max; idx++) {
12627978SPeter.Dunlap@Sun.COM 		idt = idm.idm_taskid_table[idx];
12637978SPeter.Dunlap@Sun.COM 
12647978SPeter.Dunlap@Sun.COM 		if (idt == NULL)
12657978SPeter.Dunlap@Sun.COM 			continue;
12667978SPeter.Dunlap@Sun.COM 
12677978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
12687978SPeter.Dunlap@Sun.COM 
12697978SPeter.Dunlap@Sun.COM 		if (idt->idt_state != TASK_ACTIVE) {
12707978SPeter.Dunlap@Sun.COM 			/*
12717978SPeter.Dunlap@Sun.COM 			 * Task is either in suspend, abort, or already
12727978SPeter.Dunlap@Sun.COM 			 * complete.
12737978SPeter.Dunlap@Sun.COM 			 */
12747978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
12757978SPeter.Dunlap@Sun.COM 			continue;
12767978SPeter.Dunlap@Sun.COM 		}
12777978SPeter.Dunlap@Sun.COM 
12787978SPeter.Dunlap@Sun.COM 		if (idt->idt_client_handle == handle) {
12797978SPeter.Dunlap@Sun.COM 			idm_task_hold(idt);
12807978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
12817978SPeter.Dunlap@Sun.COM 			break;
12827978SPeter.Dunlap@Sun.COM 		}
12837978SPeter.Dunlap@Sun.COM 
12847978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
12857978SPeter.Dunlap@Sun.COM 	}
12867978SPeter.Dunlap@Sun.COM 
12877978SPeter.Dunlap@Sun.COM 	rw_exit(&idm.idm_taskid_table_lock);
12887978SPeter.Dunlap@Sun.COM 
12897978SPeter.Dunlap@Sun.COM 	if ((idt == NULL) || (idx == idm.idm_taskid_max))
12907978SPeter.Dunlap@Sun.COM 		return (NULL);
12917978SPeter.Dunlap@Sun.COM 
12927978SPeter.Dunlap@Sun.COM 	return (idt->idt_private);
12937978SPeter.Dunlap@Sun.COM }
12947978SPeter.Dunlap@Sun.COM 
12957978SPeter.Dunlap@Sun.COM void
12967978SPeter.Dunlap@Sun.COM idm_task_hold(idm_task_t *idt)
12977978SPeter.Dunlap@Sun.COM {
12987978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&idt->idt_refcnt);
12997978SPeter.Dunlap@Sun.COM }
13007978SPeter.Dunlap@Sun.COM 
13017978SPeter.Dunlap@Sun.COM void
13027978SPeter.Dunlap@Sun.COM idm_task_rele(idm_task_t *idt)
13037978SPeter.Dunlap@Sun.COM {
13047978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&idt->idt_refcnt);
13057978SPeter.Dunlap@Sun.COM }
13067978SPeter.Dunlap@Sun.COM 
13077978SPeter.Dunlap@Sun.COM void
13087978SPeter.Dunlap@Sun.COM idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
13097978SPeter.Dunlap@Sun.COM {
13107978SPeter.Dunlap@Sun.COM 	idm_task_t	*task;
13117978SPeter.Dunlap@Sun.COM 	int		idx;
13127978SPeter.Dunlap@Sun.COM 
13137978SPeter.Dunlap@Sun.COM 	/*
13147978SPeter.Dunlap@Sun.COM 	 * Passing NULL as the task indicates that all tasks
13157978SPeter.Dunlap@Sun.COM 	 * for this connection should be aborted.
13167978SPeter.Dunlap@Sun.COM 	 */
13177978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
13187978SPeter.Dunlap@Sun.COM 		/*
13197978SPeter.Dunlap@Sun.COM 		 * Only the connection state machine should ask for
13207978SPeter.Dunlap@Sun.COM 		 * all tasks to abort and this should never happen in FFP.
13217978SPeter.Dunlap@Sun.COM 		 */
13227978SPeter.Dunlap@Sun.COM 		ASSERT(!ic->ic_ffp);
13237978SPeter.Dunlap@Sun.COM 		rw_enter(&idm.idm_taskid_table_lock, RW_READER);
13247978SPeter.Dunlap@Sun.COM 		for (idx = 0; idx < idm.idm_taskid_max; idx++) {
13257978SPeter.Dunlap@Sun.COM 			task = idm.idm_taskid_table[idx];
13267978SPeter.Dunlap@Sun.COM 			if (task && (task->idt_state != TASK_IDLE) &&
13277978SPeter.Dunlap@Sun.COM 			    (task->idt_ic == ic)) {
13287978SPeter.Dunlap@Sun.COM 				rw_exit(&idm.idm_taskid_table_lock);
13297978SPeter.Dunlap@Sun.COM 				idm_task_abort_one(ic, task, abort_type);
13307978SPeter.Dunlap@Sun.COM 				rw_enter(&idm.idm_taskid_table_lock, RW_READER);
13317978SPeter.Dunlap@Sun.COM 			}
13327978SPeter.Dunlap@Sun.COM 		}
13337978SPeter.Dunlap@Sun.COM 		rw_exit(&idm.idm_taskid_table_lock);
13347978SPeter.Dunlap@Sun.COM 	} else {
13357978SPeter.Dunlap@Sun.COM 		idm_task_abort_one(ic, idt, abort_type);
13367978SPeter.Dunlap@Sun.COM 	}
13377978SPeter.Dunlap@Sun.COM }
13387978SPeter.Dunlap@Sun.COM 
13397978SPeter.Dunlap@Sun.COM static void
13407978SPeter.Dunlap@Sun.COM idm_task_abort_unref_cb(void *ref)
13417978SPeter.Dunlap@Sun.COM {
13427978SPeter.Dunlap@Sun.COM 	idm_task_t *idt = ref;
13437978SPeter.Dunlap@Sun.COM 
13447978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
13457978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
13467978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
13477978SPeter.Dunlap@Sun.COM 		idt->idt_state = TASK_SUSPENDED;
13487978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
13497978SPeter.Dunlap@Sun.COM 		idm_task_aborted(idt, IDM_STATUS_SUSPENDED);
13507978SPeter.Dunlap@Sun.COM 		return;
13517978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
13527978SPeter.Dunlap@Sun.COM 		idt->idt_state = TASK_ABORTED;
13537978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
13547978SPeter.Dunlap@Sun.COM 		idm_task_aborted(idt, IDM_STATUS_ABORTED);
13557978SPeter.Dunlap@Sun.COM 		return;
13567978SPeter.Dunlap@Sun.COM 	default:
13577978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
13587978SPeter.Dunlap@Sun.COM 		ASSERT(0);
13597978SPeter.Dunlap@Sun.COM 		break;
13607978SPeter.Dunlap@Sun.COM 	}
13617978SPeter.Dunlap@Sun.COM }
13627978SPeter.Dunlap@Sun.COM 
13637978SPeter.Dunlap@Sun.COM static void
13647978SPeter.Dunlap@Sun.COM idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
13657978SPeter.Dunlap@Sun.COM {
13667978SPeter.Dunlap@Sun.COM 	/* Caller must hold connection mutex */
13677978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
13687978SPeter.Dunlap@Sun.COM 	switch (idt->idt_state) {
13697978SPeter.Dunlap@Sun.COM 	case TASK_ACTIVE:
13707978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
13717978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
13727978SPeter.Dunlap@Sun.COM 			/* Call transport to release any resources */
13737978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_SUSPENDING;
13747978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
13757978SPeter.Dunlap@Sun.COM 			ic->ic_transport_ops->it_free_task_rsrc(idt);
13767978SPeter.Dunlap@Sun.COM 
13777978SPeter.Dunlap@Sun.COM 			/*
13787978SPeter.Dunlap@Sun.COM 			 * Wait for outstanding references.  When all
13797978SPeter.Dunlap@Sun.COM 			 * references are released the callback will call
13807978SPeter.Dunlap@Sun.COM 			 * idm_task_aborted().
13817978SPeter.Dunlap@Sun.COM 			 */
13827978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
13837978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
13847978SPeter.Dunlap@Sun.COM 			return;
13857978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
13867978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
13877978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
13887978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
13897978SPeter.Dunlap@Sun.COM 			ic->ic_transport_ops->it_free_task_rsrc(idt);
13907978SPeter.Dunlap@Sun.COM 
13917978SPeter.Dunlap@Sun.COM 			/*
13927978SPeter.Dunlap@Sun.COM 			 * Wait for outstanding references.  When all
13937978SPeter.Dunlap@Sun.COM 			 * references are released the callback will call
13947978SPeter.Dunlap@Sun.COM 			 * idm_task_aborted().
13957978SPeter.Dunlap@Sun.COM 			 */
13967978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
13977978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
13987978SPeter.Dunlap@Sun.COM 			return;
13997978SPeter.Dunlap@Sun.COM 		default:
14007978SPeter.Dunlap@Sun.COM 			ASSERT(0);
14017978SPeter.Dunlap@Sun.COM 		}
14027978SPeter.Dunlap@Sun.COM 		break;
14037978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDING:
14047978SPeter.Dunlap@Sun.COM 		/* Already called transport_free_task_rsrc(); */
14057978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
14067978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
14077978SPeter.Dunlap@Sun.COM 			/* Already doing it */
14087978SPeter.Dunlap@Sun.COM 			break;
14097978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
14107978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
14117978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
14127978SPeter.Dunlap@Sun.COM 			break;
14137978SPeter.Dunlap@Sun.COM 		default:
14147978SPeter.Dunlap@Sun.COM 			ASSERT(0);
14157978SPeter.Dunlap@Sun.COM 		}
14167978SPeter.Dunlap@Sun.COM 		break;
14177978SPeter.Dunlap@Sun.COM 	case TASK_SUSPENDED:
14187978SPeter.Dunlap@Sun.COM 		/* Already called transport_free_task_rsrc(); */
14197978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
14207978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
14217978SPeter.Dunlap@Sun.COM 			/* Already doing it */
14227978SPeter.Dunlap@Sun.COM 			break;
14237978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
14247978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
14257978SPeter.Dunlap@Sun.COM 			idt->idt_state = TASK_ABORTING;
14267978SPeter.Dunlap@Sun.COM 			mutex_exit(&idt->idt_mutex);
14277978SPeter.Dunlap@Sun.COM 
14287978SPeter.Dunlap@Sun.COM 			/*
14297978SPeter.Dunlap@Sun.COM 			 * We could probably call idm_task_aborted directly
14307978SPeter.Dunlap@Sun.COM 			 * here but we may be holding the conn lock. It's
14317978SPeter.Dunlap@Sun.COM 			 * easier to just switch contexts.  Even though
14327978SPeter.Dunlap@Sun.COM 			 * we shouldn't really have any references we'll
14337978SPeter.Dunlap@Sun.COM 			 * set the state to TASK_ABORTING instead of
14347978SPeter.Dunlap@Sun.COM 			 * TASK_ABORTED so we can use the same code path.
14357978SPeter.Dunlap@Sun.COM 			 */
14367978SPeter.Dunlap@Sun.COM 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
14377978SPeter.Dunlap@Sun.COM 			    &idm_task_abort_unref_cb);
14387978SPeter.Dunlap@Sun.COM 			return;
14397978SPeter.Dunlap@Sun.COM 		default:
14407978SPeter.Dunlap@Sun.COM 			ASSERT(0);
14417978SPeter.Dunlap@Sun.COM 		}
14427978SPeter.Dunlap@Sun.COM 		break;
14437978SPeter.Dunlap@Sun.COM 	case TASK_ABORTING:
14447978SPeter.Dunlap@Sun.COM 	case TASK_ABORTED:
14457978SPeter.Dunlap@Sun.COM 		switch (abort_type) {
14467978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_SUSPEND:
14477978SPeter.Dunlap@Sun.COM 			/* We're already past this point... */
14487978SPeter.Dunlap@Sun.COM 		case AT_INTERNAL_ABORT:
14497978SPeter.Dunlap@Sun.COM 		case AT_TASK_MGMT_ABORT:
14507978SPeter.Dunlap@Sun.COM 			/* Already doing it */
14517978SPeter.Dunlap@Sun.COM 			break;
14527978SPeter.Dunlap@Sun.COM 		default:
14537978SPeter.Dunlap@Sun.COM 			ASSERT(0);
14547978SPeter.Dunlap@Sun.COM 		}
14557978SPeter.Dunlap@Sun.COM 		break;
14567978SPeter.Dunlap@Sun.COM 	case TASK_COMPLETE:
14577978SPeter.Dunlap@Sun.COM 		/*
14587978SPeter.Dunlap@Sun.COM 		 * In this case, let it go.  The status has already been
14597978SPeter.Dunlap@Sun.COM 		 * sent (which may or may not get successfully transmitted)
14607978SPeter.Dunlap@Sun.COM 		 * and we don't want to end up in a race between completing
14617978SPeter.Dunlap@Sun.COM 		 * the status PDU and marking the task suspended.
14627978SPeter.Dunlap@Sun.COM 		 */
14637978SPeter.Dunlap@Sun.COM 		break;
14647978SPeter.Dunlap@Sun.COM 	default:
14657978SPeter.Dunlap@Sun.COM 		ASSERT(0);
14667978SPeter.Dunlap@Sun.COM 	}
14677978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
14687978SPeter.Dunlap@Sun.COM }
14697978SPeter.Dunlap@Sun.COM 
14707978SPeter.Dunlap@Sun.COM static void
14717978SPeter.Dunlap@Sun.COM idm_task_aborted(idm_task_t *idt, idm_status_t status)
14727978SPeter.Dunlap@Sun.COM {
14737978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status);
14747978SPeter.Dunlap@Sun.COM }
14757978SPeter.Dunlap@Sun.COM 
14767978SPeter.Dunlap@Sun.COM void
14777978SPeter.Dunlap@Sun.COM idm_task_cleanup(idm_task_t *idt)
14787978SPeter.Dunlap@Sun.COM {
14797978SPeter.Dunlap@Sun.COM 	idm_buf_t *idb, *next_idb;
14807978SPeter.Dunlap@Sun.COM 	list_t		tmp_buflist;
14817978SPeter.Dunlap@Sun.COM 	ASSERT((idt->idt_state == TASK_SUSPENDED) ||
14827978SPeter.Dunlap@Sun.COM 	    (idt->idt_state == TASK_ABORTED));
14837978SPeter.Dunlap@Sun.COM 
14847978SPeter.Dunlap@Sun.COM 	list_create(&tmp_buflist, sizeof (idm_buf_t),
14857978SPeter.Dunlap@Sun.COM 	    offsetof(idm_buf_t, idb_buflink));
14867978SPeter.Dunlap@Sun.COM 
14877978SPeter.Dunlap@Sun.COM 	/*
14887978SPeter.Dunlap@Sun.COM 	 * Remove all the buffers from the task and add them to a
14897978SPeter.Dunlap@Sun.COM 	 * temporary local list -- we do this so that we can hold
14907978SPeter.Dunlap@Sun.COM 	 * the task lock and prevent the task from going away if
14917978SPeter.Dunlap@Sun.COM 	 * the client decides to call idm_task_done/idm_task_free.
14927978SPeter.Dunlap@Sun.COM 	 * This could happen during abort in iscsit.
14937978SPeter.Dunlap@Sun.COM 	 */
14947978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
14957978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_inbufv);
14967978SPeter.Dunlap@Sun.COM 	    idb != NULL;
14977978SPeter.Dunlap@Sun.COM 	    idb = next_idb) {
14987978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&idt->idt_inbufv, idb);
14997978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_in_locked(idt, idb);
15007978SPeter.Dunlap@Sun.COM 		list_insert_tail(&tmp_buflist, idb);
15017978SPeter.Dunlap@Sun.COM 	}
15027978SPeter.Dunlap@Sun.COM 
15037978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_outbufv);
15047978SPeter.Dunlap@Sun.COM 	    idb != NULL;
15057978SPeter.Dunlap@Sun.COM 	    idb = next_idb) {
15067978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&idt->idt_outbufv, idb);
15077978SPeter.Dunlap@Sun.COM 		idm_buf_unbind_out_locked(idt, idb);
15087978SPeter.Dunlap@Sun.COM 		list_insert_tail(&tmp_buflist, idb);
15097978SPeter.Dunlap@Sun.COM 	}
15107978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
15117978SPeter.Dunlap@Sun.COM 
15127978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) {
15137978SPeter.Dunlap@Sun.COM 		next_idb = list_next(&tmp_buflist, idb);
15147978SPeter.Dunlap@Sun.COM 		list_remove(&tmp_buflist, idb);
15157978SPeter.Dunlap@Sun.COM 		(*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED);
15167978SPeter.Dunlap@Sun.COM 	}
15177978SPeter.Dunlap@Sun.COM 	list_destroy(&tmp_buflist);
15187978SPeter.Dunlap@Sun.COM }
15197978SPeter.Dunlap@Sun.COM 
15207978SPeter.Dunlap@Sun.COM 
15217978SPeter.Dunlap@Sun.COM /*
15227978SPeter.Dunlap@Sun.COM  * idm_pdu_tx
15237978SPeter.Dunlap@Sun.COM  *
15247978SPeter.Dunlap@Sun.COM  * This is IDM's implementation of the 'Send_Control' operational primitive.
15257978SPeter.Dunlap@Sun.COM  * This function is invoked by an initiator iSCSI layer requesting the transfer
15267978SPeter.Dunlap@Sun.COM  * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
15277978SPeter.Dunlap@Sun.COM  * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover
15287978SPeter.Dunlap@Sun.COM  * layer to the peer iSCSI layer in the remote iSCSI node. The connection info
15297978SPeter.Dunlap@Sun.COM  * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size
15307978SPeter.Dunlap@Sun.COM  * are provided as input.
15317978SPeter.Dunlap@Sun.COM  *
15327978SPeter.Dunlap@Sun.COM  */
15337978SPeter.Dunlap@Sun.COM void
15347978SPeter.Dunlap@Sun.COM idm_pdu_tx(idm_pdu_t *pdu)
15357978SPeter.Dunlap@Sun.COM {
15367978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic = pdu->isp_ic;
15377978SPeter.Dunlap@Sun.COM 	iscsi_async_evt_hdr_t	*async_evt;
15387978SPeter.Dunlap@Sun.COM 
15397978SPeter.Dunlap@Sun.COM 	/*
15407978SPeter.Dunlap@Sun.COM 	 * If we are in full-featured mode then route SCSI-related
15417978SPeter.Dunlap@Sun.COM 	 * commands to the appropriate function vector without checking
15427978SPeter.Dunlap@Sun.COM 	 * the connection state.  We will only be in full-feature mode
15437978SPeter.Dunlap@Sun.COM 	 * when we are in an acceptable state for SCSI PDU's.
15447978SPeter.Dunlap@Sun.COM 	 *
15457978SPeter.Dunlap@Sun.COM 	 * We also need to ensure that there are no PDU events outstanding
15467978SPeter.Dunlap@Sun.COM 	 * on the state machine.  Any non-SCSI PDU's received in full-feature
15477978SPeter.Dunlap@Sun.COM 	 * mode will result in PDU events and until these have been handled
15487978SPeter.Dunlap@Sun.COM 	 * we need to route all PDU's through the state machine as PDU
15497978SPeter.Dunlap@Sun.COM 	 * events to maintain ordering.
15507978SPeter.Dunlap@Sun.COM 	 *
15517978SPeter.Dunlap@Sun.COM 	 * Note that IDM cannot enter FFP mode until it processes in
15527978SPeter.Dunlap@Sun.COM 	 * its state machine the last xmit of the login process.
15537978SPeter.Dunlap@Sun.COM 	 * Hence, checking the IDM_PDU_LOGIN_TX flag here would be
15547978SPeter.Dunlap@Sun.COM 	 * superfluous.
15557978SPeter.Dunlap@Sun.COM 	 */
15567978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_state_mutex);
15577978SPeter.Dunlap@Sun.COM 	if (ic->ic_ffp && (ic->ic_pdu_events == 0)) {
15587978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
15597978SPeter.Dunlap@Sun.COM 		switch (IDM_PDU_OPCODE(pdu)) {
15607978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_RSP:
15617978SPeter.Dunlap@Sun.COM 			/* Target only */
15627978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15637978SPeter.Dunlap@Sun.COM 			return;
15647978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_TASK_MGT_RSP:
15657978SPeter.Dunlap@Sun.COM 			/* Target only */
15667978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15677978SPeter.Dunlap@Sun.COM 			return;
15687978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_DATA_RSP:
15697978SPeter.Dunlap@Sun.COM 			/* Target only */
15707978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15717978SPeter.Dunlap@Sun.COM 			return;
15727978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_RTT_RSP:
15737978SPeter.Dunlap@Sun.COM 			/* Target only */
15747978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15757978SPeter.Dunlap@Sun.COM 			return;
15767978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_NOOP_IN:
15777978SPeter.Dunlap@Sun.COM 			/* Target only */
15787978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15797978SPeter.Dunlap@Sun.COM 			return;
15807978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_TEXT_RSP:
15817978SPeter.Dunlap@Sun.COM 			/* Target only */
15827978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15837978SPeter.Dunlap@Sun.COM 			return;
15847978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_TEXT_CMD:
15857978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_NOOP_OUT:
15867978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_CMD:
15877978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_DATA:
15887978SPeter.Dunlap@Sun.COM 		case ISCSI_OP_SCSI_TASK_MGT_MSG:
15897978SPeter.Dunlap@Sun.COM 			/* Initiator only */
15907978SPeter.Dunlap@Sun.COM 			idm_pdu_tx_forward(ic, pdu);
15917978SPeter.Dunlap@Sun.COM 			return;
15927978SPeter.Dunlap@Sun.COM 		default:
15937978SPeter.Dunlap@Sun.COM 			break;
15947978SPeter.Dunlap@Sun.COM 		}
15957978SPeter.Dunlap@Sun.COM 
15967978SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_state_mutex);
15977978SPeter.Dunlap@Sun.COM 	}
15987978SPeter.Dunlap@Sun.COM 
15997978SPeter.Dunlap@Sun.COM 	/*
16007978SPeter.Dunlap@Sun.COM 	 * Any PDU's processed outside of full-feature mode and non-SCSI
16017978SPeter.Dunlap@Sun.COM 	 * PDU's in full-feature mode are handled by generating an
16027978SPeter.Dunlap@Sun.COM 	 * event to the connection state machine.  The state machine
16037978SPeter.Dunlap@Sun.COM 	 * will validate the PDU against the current state and either
16047978SPeter.Dunlap@Sun.COM 	 * transmit the PDU if the opcode is allowed or handle an
16057978SPeter.Dunlap@Sun.COM 	 * error if the PDU is not allowed.
16067978SPeter.Dunlap@Sun.COM 	 *
16077978SPeter.Dunlap@Sun.COM 	 * This code-path will also generate any events that are implied
16087978SPeter.Dunlap@Sun.COM 	 * by the PDU opcode.  For example a "login response" with success
16097978SPeter.Dunlap@Sun.COM 	 * status generates a CE_LOGOUT_SUCCESS_SND event.
16107978SPeter.Dunlap@Sun.COM 	 */
16117978SPeter.Dunlap@Sun.COM 	switch (IDM_PDU_OPCODE(pdu)) {
16127978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_CMD:
16137978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
16147978SPeter.Dunlap@Sun.COM 		break;
16157978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_RSP:
16167978SPeter.Dunlap@Sun.COM 		idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE);
16177978SPeter.Dunlap@Sun.COM 		break;
16187978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_CMD:
16197978SPeter.Dunlap@Sun.COM 		idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE);
16207978SPeter.Dunlap@Sun.COM 		break;
16217978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_RSP:
16227978SPeter.Dunlap@Sun.COM 		idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE);
16237978SPeter.Dunlap@Sun.COM 		break;
16247978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_ASYNC_EVENT:
16257978SPeter.Dunlap@Sun.COM 		async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
16267978SPeter.Dunlap@Sun.COM 		switch (async_evt->async_event) {
16277978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
16287978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
16297978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
16307978SPeter.Dunlap@Sun.COM 			break;
16317978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
16327978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND,
16337978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
16347978SPeter.Dunlap@Sun.COM 			break;
16357978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
16367978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND,
16377978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
16387978SPeter.Dunlap@Sun.COM 			break;
16397978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_SCSI_EVENT:
16407978SPeter.Dunlap@Sun.COM 		case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
16417978SPeter.Dunlap@Sun.COM 		default:
16427978SPeter.Dunlap@Sun.COM 			idm_conn_tx_pdu_event(ic, CE_MISC_TX,
16437978SPeter.Dunlap@Sun.COM 			    (uintptr_t)pdu);
16447978SPeter.Dunlap@Sun.COM 			break;
16457978SPeter.Dunlap@Sun.COM 		}
16467978SPeter.Dunlap@Sun.COM 		break;
16477978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_RSP:
16487978SPeter.Dunlap@Sun.COM 		/* Target only */
16497978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16507978SPeter.Dunlap@Sun.COM 		break;
16517978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
16527978SPeter.Dunlap@Sun.COM 		/* Target only */
16537978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16547978SPeter.Dunlap@Sun.COM 		break;
16557978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_DATA_RSP:
16567978SPeter.Dunlap@Sun.COM 		/* Target only */
16577978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16587978SPeter.Dunlap@Sun.COM 		break;
16597978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_RTT_RSP:
16607978SPeter.Dunlap@Sun.COM 		/* Target only */
16617978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16627978SPeter.Dunlap@Sun.COM 		break;
16637978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_IN:
16647978SPeter.Dunlap@Sun.COM 		/* Target only */
16657978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16667978SPeter.Dunlap@Sun.COM 		break;
16677978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_RSP:
16687978SPeter.Dunlap@Sun.COM 		/* Target only */
16697978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16707978SPeter.Dunlap@Sun.COM 		break;
16717978SPeter.Dunlap@Sun.COM 		/* Initiator only */
16727978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_CMD:
16737978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
16747978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_DATA:
16757978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_OUT:
16767978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_CMD:
16777978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SNACK_CMD:
16787978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_REJECT_MSG:
16797978SPeter.Dunlap@Sun.COM 	default:
16807978SPeter.Dunlap@Sun.COM 		/*
16817978SPeter.Dunlap@Sun.COM 		 * Connection state machine will validate these PDU's against
16827978SPeter.Dunlap@Sun.COM 		 * the current state.  A PDU not allowed in the current
16837978SPeter.Dunlap@Sun.COM 		 * state will cause a protocol error.
16847978SPeter.Dunlap@Sun.COM 		 */
16857978SPeter.Dunlap@Sun.COM 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
16867978SPeter.Dunlap@Sun.COM 		break;
16877978SPeter.Dunlap@Sun.COM 	}
16887978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_state_mutex);
16897978SPeter.Dunlap@Sun.COM }
16907978SPeter.Dunlap@Sun.COM 
16917978SPeter.Dunlap@Sun.COM /*
16927978SPeter.Dunlap@Sun.COM  * Allocates a PDU along with memory for header and data.
16937978SPeter.Dunlap@Sun.COM  */
16947978SPeter.Dunlap@Sun.COM 
16957978SPeter.Dunlap@Sun.COM idm_pdu_t *
16967978SPeter.Dunlap@Sun.COM idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
16977978SPeter.Dunlap@Sun.COM {
16987978SPeter.Dunlap@Sun.COM 	idm_pdu_t *result;
16997978SPeter.Dunlap@Sun.COM 
17007978SPeter.Dunlap@Sun.COM 	/*
17017978SPeter.Dunlap@Sun.COM 	 * IDM clients should cache these structures for performance
17027978SPeter.Dunlap@Sun.COM 	 * critical paths.  We can't cache effectively in IDM because we
17037978SPeter.Dunlap@Sun.COM 	 * don't know the correct header and data size.
17047978SPeter.Dunlap@Sun.COM 	 *
17057978SPeter.Dunlap@Sun.COM 	 * Valid header length is assumed to be hdrlen and valid data
17067978SPeter.Dunlap@Sun.COM 	 * length is assumed to be datalen.  isp_hdrlen and isp_datalen
17077978SPeter.Dunlap@Sun.COM 	 * can be adjusted after the PDU is returned if necessary.
17087978SPeter.Dunlap@Sun.COM 	 */
17097978SPeter.Dunlap@Sun.COM 	result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, KM_SLEEP);
17107978SPeter.Dunlap@Sun.COM 	result->isp_flags |= IDM_PDU_ALLOC; /* For idm_pdu_free sanity check */
17117978SPeter.Dunlap@Sun.COM 	result->isp_hdr = (iscsi_hdr_t *)(result + 1); /* Ptr. Arithmetic */
17127978SPeter.Dunlap@Sun.COM 	result->isp_hdrlen = hdrlen;
17137978SPeter.Dunlap@Sun.COM 	result->isp_hdrbuflen = hdrlen;
17147978SPeter.Dunlap@Sun.COM 	result->isp_transport_hdrlen = 0;
17157978SPeter.Dunlap@Sun.COM 	result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
17167978SPeter.Dunlap@Sun.COM 	result->isp_datalen = datalen;
17177978SPeter.Dunlap@Sun.COM 	result->isp_databuflen = datalen;
17187978SPeter.Dunlap@Sun.COM 	result->isp_magic = IDM_PDU_MAGIC;
17197978SPeter.Dunlap@Sun.COM 
17207978SPeter.Dunlap@Sun.COM 	return (result);
17217978SPeter.Dunlap@Sun.COM }
17227978SPeter.Dunlap@Sun.COM 
17237978SPeter.Dunlap@Sun.COM /*
17247978SPeter.Dunlap@Sun.COM  * Free a PDU previously allocated with idm_pdu_alloc() including any
17257978SPeter.Dunlap@Sun.COM  * header and data space allocated as part of the original request.
17267978SPeter.Dunlap@Sun.COM  * Additional memory regions referenced by subsequent modification of
17277978SPeter.Dunlap@Sun.COM  * the isp_hdr and/or isp_data fields will not be freed.
17287978SPeter.Dunlap@Sun.COM  */
17297978SPeter.Dunlap@Sun.COM void
17307978SPeter.Dunlap@Sun.COM idm_pdu_free(idm_pdu_t *pdu)
17317978SPeter.Dunlap@Sun.COM {
17327978SPeter.Dunlap@Sun.COM 	/* Make sure the structure was allocated using idm_pdu_alloc() */
17337978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
17347978SPeter.Dunlap@Sun.COM 	kmem_free(pdu,
17357978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
17367978SPeter.Dunlap@Sun.COM }
17377978SPeter.Dunlap@Sun.COM 
17387978SPeter.Dunlap@Sun.COM /*
17397978SPeter.Dunlap@Sun.COM  * Initialize the connection, private and callback fields in a PDU.
17407978SPeter.Dunlap@Sun.COM  */
17417978SPeter.Dunlap@Sun.COM void
17427978SPeter.Dunlap@Sun.COM idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
17437978SPeter.Dunlap@Sun.COM {
17447978SPeter.Dunlap@Sun.COM 	/*
17457978SPeter.Dunlap@Sun.COM 	 * idm_pdu_complete() will call idm_pdu_free if the callback is
17467978SPeter.Dunlap@Sun.COM 	 * NULL.  This will only work if the PDU was originally allocated
17477978SPeter.Dunlap@Sun.COM 	 * with idm_pdu_alloc().
17487978SPeter.Dunlap@Sun.COM 	 */
17497978SPeter.Dunlap@Sun.COM 	ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
17507978SPeter.Dunlap@Sun.COM 	    (cb != NULL));
17517978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
17527978SPeter.Dunlap@Sun.COM 	pdu->isp_ic = ic;
17537978SPeter.Dunlap@Sun.COM 	pdu->isp_private = private;
17547978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = cb;
17557978SPeter.Dunlap@Sun.COM }
17567978SPeter.Dunlap@Sun.COM 
17577978SPeter.Dunlap@Sun.COM /*
17587978SPeter.Dunlap@Sun.COM  * Initialize the header and header length field.  This function should
17597978SPeter.Dunlap@Sun.COM  * not be used to adjust the header length in a buffer allocated via
17607978SPeter.Dunlap@Sun.COM  * pdu_pdu_alloc since it overwrites the existing header pointer.
17617978SPeter.Dunlap@Sun.COM  */
17627978SPeter.Dunlap@Sun.COM void
17637978SPeter.Dunlap@Sun.COM idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
17647978SPeter.Dunlap@Sun.COM {
17657978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr);
17667978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = hdrlen;
17677978SPeter.Dunlap@Sun.COM }
17687978SPeter.Dunlap@Sun.COM 
17697978SPeter.Dunlap@Sun.COM /*
17707978SPeter.Dunlap@Sun.COM  * Initialize the data and data length fields.  This function should
17717978SPeter.Dunlap@Sun.COM  * not be used to adjust the data length of a buffer allocated via
17727978SPeter.Dunlap@Sun.COM  * idm_pdu_alloc since it overwrites the existing data pointer.
17737978SPeter.Dunlap@Sun.COM  */
17747978SPeter.Dunlap@Sun.COM void
17757978SPeter.Dunlap@Sun.COM idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
17767978SPeter.Dunlap@Sun.COM {
17777978SPeter.Dunlap@Sun.COM 	pdu->isp_data = data;
17787978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = datalen;
17797978SPeter.Dunlap@Sun.COM }
17807978SPeter.Dunlap@Sun.COM 
17817978SPeter.Dunlap@Sun.COM void
17827978SPeter.Dunlap@Sun.COM idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
17837978SPeter.Dunlap@Sun.COM {
17847978SPeter.Dunlap@Sun.COM 	if (pdu->isp_callback) {
17857978SPeter.Dunlap@Sun.COM 		pdu->isp_status = status;
17867978SPeter.Dunlap@Sun.COM 		(*pdu->isp_callback)(pdu, status);
17877978SPeter.Dunlap@Sun.COM 	} else {
17887978SPeter.Dunlap@Sun.COM 		idm_pdu_free(pdu);
17897978SPeter.Dunlap@Sun.COM 	}
17907978SPeter.Dunlap@Sun.COM }
17917978SPeter.Dunlap@Sun.COM 
17927978SPeter.Dunlap@Sun.COM /*
17937978SPeter.Dunlap@Sun.COM  * State machine auditing
17947978SPeter.Dunlap@Sun.COM  */
17957978SPeter.Dunlap@Sun.COM 
17967978SPeter.Dunlap@Sun.COM void
17977978SPeter.Dunlap@Sun.COM idm_sm_audit_init(sm_audit_buf_t *audit_buf)
17987978SPeter.Dunlap@Sun.COM {
17997978SPeter.Dunlap@Sun.COM 	bzero(audit_buf, sizeof (sm_audit_buf_t));
18007978SPeter.Dunlap@Sun.COM 	audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1;
18017978SPeter.Dunlap@Sun.COM }
18027978SPeter.Dunlap@Sun.COM 
18037978SPeter.Dunlap@Sun.COM static
18047978SPeter.Dunlap@Sun.COM sm_audit_record_t *
18057978SPeter.Dunlap@Sun.COM idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type,
18067978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type,
18077978SPeter.Dunlap@Sun.COM     int current_state)
18087978SPeter.Dunlap@Sun.COM {
18097978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
18107978SPeter.Dunlap@Sun.COM 
18117978SPeter.Dunlap@Sun.COM 	sar = audit_buf->sab_records;
18127978SPeter.Dunlap@Sun.COM 	sar += audit_buf->sab_index;
18137978SPeter.Dunlap@Sun.COM 	audit_buf->sab_index++;
18147978SPeter.Dunlap@Sun.COM 	audit_buf->sab_index &= audit_buf->sab_max_index;
18157978SPeter.Dunlap@Sun.COM 
18167978SPeter.Dunlap@Sun.COM 	sar->sar_type = r_type;
18177978SPeter.Dunlap@Sun.COM 	gethrestime(&sar->sar_timestamp);
18187978SPeter.Dunlap@Sun.COM 	sar->sar_sm_type = sm_type;
18197978SPeter.Dunlap@Sun.COM 	sar->sar_state = current_state;
18207978SPeter.Dunlap@Sun.COM 
18217978SPeter.Dunlap@Sun.COM 	return (sar);
18227978SPeter.Dunlap@Sun.COM }
18237978SPeter.Dunlap@Sun.COM 
18247978SPeter.Dunlap@Sun.COM void
18257978SPeter.Dunlap@Sun.COM idm_sm_audit_event(sm_audit_buf_t *audit_buf,
18267978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type, int current_state,
18277978SPeter.Dunlap@Sun.COM     int event, uintptr_t event_info)
18287978SPeter.Dunlap@Sun.COM {
18297978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
18307978SPeter.Dunlap@Sun.COM 
18317978SPeter.Dunlap@Sun.COM 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT,
18327978SPeter.Dunlap@Sun.COM 	    sm_type, current_state);
18337978SPeter.Dunlap@Sun.COM 	sar->sar_event = event;
18347978SPeter.Dunlap@Sun.COM 	sar->sar_event_info = event_info;
18357978SPeter.Dunlap@Sun.COM }
18367978SPeter.Dunlap@Sun.COM 
18377978SPeter.Dunlap@Sun.COM void
18387978SPeter.Dunlap@Sun.COM idm_sm_audit_state_change(sm_audit_buf_t *audit_buf,
18397978SPeter.Dunlap@Sun.COM     sm_audit_sm_type_t sm_type, int current_state, int new_state)
18407978SPeter.Dunlap@Sun.COM {
18417978SPeter.Dunlap@Sun.COM 	sm_audit_record_t *sar;
18427978SPeter.Dunlap@Sun.COM 
18437978SPeter.Dunlap@Sun.COM 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE,
18447978SPeter.Dunlap@Sun.COM 	    sm_type, current_state);
18457978SPeter.Dunlap@Sun.COM 	sar->sar_new_state = new_state;
18467978SPeter.Dunlap@Sun.COM }
18477978SPeter.Dunlap@Sun.COM 
18487978SPeter.Dunlap@Sun.COM 
18497978SPeter.Dunlap@Sun.COM /*
18507978SPeter.Dunlap@Sun.COM  * Object reference tracking
18517978SPeter.Dunlap@Sun.COM  */
18527978SPeter.Dunlap@Sun.COM 
18537978SPeter.Dunlap@Sun.COM void
18547978SPeter.Dunlap@Sun.COM idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj)
18557978SPeter.Dunlap@Sun.COM {
18567978SPeter.Dunlap@Sun.COM 	bzero(refcnt, sizeof (*refcnt));
18577978SPeter.Dunlap@Sun.COM 	idm_refcnt_reset(refcnt);
18587978SPeter.Dunlap@Sun.COM 	refcnt->ir_referenced_obj = referenced_obj;
18597978SPeter.Dunlap@Sun.COM 	bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t));
18607978SPeter.Dunlap@Sun.COM 	refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1;
18617978SPeter.Dunlap@Sun.COM 	mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL);
18627978SPeter.Dunlap@Sun.COM 	cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL);
18637978SPeter.Dunlap@Sun.COM }
18647978SPeter.Dunlap@Sun.COM 
18657978SPeter.Dunlap@Sun.COM void
18667978SPeter.Dunlap@Sun.COM idm_refcnt_destroy(idm_refcnt_t *refcnt)
18677978SPeter.Dunlap@Sun.COM {
18687978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt == 0);
18697978SPeter.Dunlap@Sun.COM 	cv_destroy(&refcnt->ir_cv);
18707978SPeter.Dunlap@Sun.COM 	mutex_destroy(&refcnt->ir_mutex);
18717978SPeter.Dunlap@Sun.COM }
18727978SPeter.Dunlap@Sun.COM 
18737978SPeter.Dunlap@Sun.COM void
18747978SPeter.Dunlap@Sun.COM idm_refcnt_reset(idm_refcnt_t *refcnt)
18757978SPeter.Dunlap@Sun.COM {
18767978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_NOWAIT;
18777978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt = 0;
18787978SPeter.Dunlap@Sun.COM }
18797978SPeter.Dunlap@Sun.COM 
18807978SPeter.Dunlap@Sun.COM void
18817978SPeter.Dunlap@Sun.COM idm_refcnt_hold(idm_refcnt_t *refcnt)
18827978SPeter.Dunlap@Sun.COM {
18837978SPeter.Dunlap@Sun.COM 	/*
18847978SPeter.Dunlap@Sun.COM 	 * Nothing should take a hold on an object after a call to
18857978SPeter.Dunlap@Sun.COM 	 * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref
18867978SPeter.Dunlap@Sun.COM 	 */
18877978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_waiting == REF_NOWAIT);
18887978SPeter.Dunlap@Sun.COM 
18897978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
18907978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt++;
18917978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
18927978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
18937978SPeter.Dunlap@Sun.COM }
18947978SPeter.Dunlap@Sun.COM 
18957978SPeter.Dunlap@Sun.COM static void
18967978SPeter.Dunlap@Sun.COM idm_refcnt_unref_task(void *refcnt_void)
18977978SPeter.Dunlap@Sun.COM {
18987978SPeter.Dunlap@Sun.COM 	idm_refcnt_t *refcnt = refcnt_void;
18997978SPeter.Dunlap@Sun.COM 
19007978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
19017978SPeter.Dunlap@Sun.COM 	(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
19027978SPeter.Dunlap@Sun.COM }
19037978SPeter.Dunlap@Sun.COM 
19047978SPeter.Dunlap@Sun.COM void
19057978SPeter.Dunlap@Sun.COM idm_refcnt_rele(idm_refcnt_t *refcnt)
19067978SPeter.Dunlap@Sun.COM {
19077978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
19087978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt > 0);
19097978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt--;
19107978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
19117978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_waiting == REF_NOWAIT) {
19127978SPeter.Dunlap@Sun.COM 		/* No one is waiting on this object */
19137978SPeter.Dunlap@Sun.COM 		mutex_exit(&refcnt->ir_mutex);
19147978SPeter.Dunlap@Sun.COM 		return;
19157978SPeter.Dunlap@Sun.COM 	}
19167978SPeter.Dunlap@Sun.COM 
19177978SPeter.Dunlap@Sun.COM 	/*
19187978SPeter.Dunlap@Sun.COM 	 * Someone is waiting for this object to go idle so check if
19197978SPeter.Dunlap@Sun.COM 	 * refcnt is 0.  Waiting on an object then later grabbing another
19207978SPeter.Dunlap@Sun.COM 	 * reference is not allowed so we don't need to handle that case.
19217978SPeter.Dunlap@Sun.COM 	 */
19227978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
19237978SPeter.Dunlap@Sun.COM 		if (refcnt->ir_waiting == REF_WAIT_ASYNC) {
19247978SPeter.Dunlap@Sun.COM 			if (taskq_dispatch(idm.idm_global_taskq,
19257978SPeter.Dunlap@Sun.COM 			    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
19267978SPeter.Dunlap@Sun.COM 				cmn_err(CE_WARN,
19277978SPeter.Dunlap@Sun.COM 				    "idm_refcnt_rele: Couldn't dispatch task");
19287978SPeter.Dunlap@Sun.COM 			}
19297978SPeter.Dunlap@Sun.COM 		} else if (refcnt->ir_waiting == REF_WAIT_SYNC) {
19307978SPeter.Dunlap@Sun.COM 			cv_signal(&refcnt->ir_cv);
19317978SPeter.Dunlap@Sun.COM 		}
19327978SPeter.Dunlap@Sun.COM 	}
19337978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
19347978SPeter.Dunlap@Sun.COM }
19357978SPeter.Dunlap@Sun.COM 
19367978SPeter.Dunlap@Sun.COM void
19377978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
19387978SPeter.Dunlap@Sun.COM {
19397978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
19407978SPeter.Dunlap@Sun.COM 	ASSERT(refcnt->ir_refcnt > 0);
19417978SPeter.Dunlap@Sun.COM 	refcnt->ir_refcnt--;
19427978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
19437978SPeter.Dunlap@Sun.COM 
19447978SPeter.Dunlap@Sun.COM 	/*
19457978SPeter.Dunlap@Sun.COM 	 * Someone is waiting for this object to go idle so check if
19467978SPeter.Dunlap@Sun.COM 	 * refcnt is 0.  Waiting on an object then later grabbing another
19477978SPeter.Dunlap@Sun.COM 	 * reference is not allowed so we don't need to handle that case.
19487978SPeter.Dunlap@Sun.COM 	 */
19497978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
19507978SPeter.Dunlap@Sun.COM 		refcnt->ir_cb = cb_func;
19517978SPeter.Dunlap@Sun.COM 		refcnt->ir_waiting = REF_WAIT_ASYNC;
19527978SPeter.Dunlap@Sun.COM 		if (taskq_dispatch(idm.idm_global_taskq,
19537978SPeter.Dunlap@Sun.COM 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
19547978SPeter.Dunlap@Sun.COM 			cmn_err(CE_WARN,
19557978SPeter.Dunlap@Sun.COM 			    "idm_refcnt_rele: Couldn't dispatch task");
19567978SPeter.Dunlap@Sun.COM 		}
19577978SPeter.Dunlap@Sun.COM 	}
19587978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
19597978SPeter.Dunlap@Sun.COM }
19607978SPeter.Dunlap@Sun.COM 
19617978SPeter.Dunlap@Sun.COM void
19627978SPeter.Dunlap@Sun.COM idm_refcnt_wait_ref(idm_refcnt_t *refcnt)
19637978SPeter.Dunlap@Sun.COM {
19647978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
19657978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_WAIT_SYNC;
19667978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
19677978SPeter.Dunlap@Sun.COM 	while (refcnt->ir_refcnt != 0)
19687978SPeter.Dunlap@Sun.COM 		cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex);
19697978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
19707978SPeter.Dunlap@Sun.COM }
19717978SPeter.Dunlap@Sun.COM 
19727978SPeter.Dunlap@Sun.COM void
19737978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
19747978SPeter.Dunlap@Sun.COM {
19757978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
19767978SPeter.Dunlap@Sun.COM 	refcnt->ir_waiting = REF_WAIT_ASYNC;
19777978SPeter.Dunlap@Sun.COM 	refcnt->ir_cb = cb_func;
19787978SPeter.Dunlap@Sun.COM 	REFCNT_AUDIT(refcnt);
19797978SPeter.Dunlap@Sun.COM 	/*
19807978SPeter.Dunlap@Sun.COM 	 * It's possible we don't have any references.  To make things easier
19817978SPeter.Dunlap@Sun.COM 	 * on the caller use a taskq to call the callback instead of
19827978SPeter.Dunlap@Sun.COM 	 * calling it synchronously
19837978SPeter.Dunlap@Sun.COM 	 */
19847978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
19857978SPeter.Dunlap@Sun.COM 		if (taskq_dispatch(idm.idm_global_taskq,
19867978SPeter.Dunlap@Sun.COM 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
19877978SPeter.Dunlap@Sun.COM 			cmn_err(CE_WARN,
19887978SPeter.Dunlap@Sun.COM 			    "idm_refcnt_async_wait_ref: "
19897978SPeter.Dunlap@Sun.COM 			    "Couldn't dispatch task");
19907978SPeter.Dunlap@Sun.COM 		}
19917978SPeter.Dunlap@Sun.COM 	}
19927978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
19937978SPeter.Dunlap@Sun.COM }
19947978SPeter.Dunlap@Sun.COM 
19957978SPeter.Dunlap@Sun.COM void
19967978SPeter.Dunlap@Sun.COM idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt,
19977978SPeter.Dunlap@Sun.COM     idm_refcnt_cb_t *cb_func)
19987978SPeter.Dunlap@Sun.COM {
19997978SPeter.Dunlap@Sun.COM 	mutex_enter(&refcnt->ir_mutex);
20007978SPeter.Dunlap@Sun.COM 	if (refcnt->ir_refcnt == 0) {
20017978SPeter.Dunlap@Sun.COM 		mutex_exit(&refcnt->ir_mutex);
20027978SPeter.Dunlap@Sun.COM 		(*cb_func)(refcnt->ir_referenced_obj);
20037978SPeter.Dunlap@Sun.COM 		return;
20047978SPeter.Dunlap@Sun.COM 	}
20057978SPeter.Dunlap@Sun.COM 	mutex_exit(&refcnt->ir_mutex);
20067978SPeter.Dunlap@Sun.COM }
20077978SPeter.Dunlap@Sun.COM 
20087978SPeter.Dunlap@Sun.COM void
20097978SPeter.Dunlap@Sun.COM idm_conn_hold(idm_conn_t *ic)
20107978SPeter.Dunlap@Sun.COM {
20117978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&ic->ic_refcnt);
20127978SPeter.Dunlap@Sun.COM }
20137978SPeter.Dunlap@Sun.COM 
20147978SPeter.Dunlap@Sun.COM void
20157978SPeter.Dunlap@Sun.COM idm_conn_rele(idm_conn_t *ic)
20167978SPeter.Dunlap@Sun.COM {
20177978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&ic->ic_refcnt);
20187978SPeter.Dunlap@Sun.COM }
20197978SPeter.Dunlap@Sun.COM 
20207978SPeter.Dunlap@Sun.COM 
20217978SPeter.Dunlap@Sun.COM static int
20227978SPeter.Dunlap@Sun.COM _idm_init(void)
20237978SPeter.Dunlap@Sun.COM {
20247978SPeter.Dunlap@Sun.COM 	/* Initialize the rwlock for the taskid table */
20257978SPeter.Dunlap@Sun.COM 	rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL);
20267978SPeter.Dunlap@Sun.COM 
20277978SPeter.Dunlap@Sun.COM 	/* Initialize the global mutex and taskq */
20287978SPeter.Dunlap@Sun.COM 	mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
20297978SPeter.Dunlap@Sun.COM 
20307978SPeter.Dunlap@Sun.COM 	cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
20317978SPeter.Dunlap@Sun.COM 	cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
20327978SPeter.Dunlap@Sun.COM 
20337978SPeter.Dunlap@Sun.COM 	idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
20347978SPeter.Dunlap@Sun.COM 	    4, 4, TASKQ_PREPOPULATE);
20357978SPeter.Dunlap@Sun.COM 	if (idm.idm_global_taskq == NULL) {
20367978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_wd_cv);
20377978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_tgt_svc_cv);
20387978SPeter.Dunlap@Sun.COM 		mutex_destroy(&idm.idm_global_mutex);
20397978SPeter.Dunlap@Sun.COM 		rw_destroy(&idm.idm_taskid_table_lock);
20407978SPeter.Dunlap@Sun.COM 		return (ENOMEM);
20417978SPeter.Dunlap@Sun.COM 	}
20427978SPeter.Dunlap@Sun.COM 
2043*8209SJames.Moore@Sun.COM 	/* Start watchdog thread */
20447978SPeter.Dunlap@Sun.COM 	idm.idm_wd_thread = thread_create(NULL, 0,
20457978SPeter.Dunlap@Sun.COM 	    idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri);
20467978SPeter.Dunlap@Sun.COM 	if (idm.idm_wd_thread == NULL) {
20477978SPeter.Dunlap@Sun.COM 		/* Couldn't create the watchdog thread */
20487978SPeter.Dunlap@Sun.COM 		taskq_destroy(idm.idm_global_taskq);
20497978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_wd_cv);
20507978SPeter.Dunlap@Sun.COM 		cv_destroy(&idm.idm_tgt_svc_cv);
20517978SPeter.Dunlap@Sun.COM 		mutex_destroy(&idm.idm_global_mutex);
20527978SPeter.Dunlap@Sun.COM 		rw_destroy(&idm.idm_taskid_table_lock);
20537978SPeter.Dunlap@Sun.COM 		return (ENOMEM);
20547978SPeter.Dunlap@Sun.COM 	}
20557978SPeter.Dunlap@Sun.COM 
2056*8209SJames.Moore@Sun.COM 	/* Pause until the watchdog thread is running */
20577978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
20587978SPeter.Dunlap@Sun.COM 	while (!idm.idm_wd_thread_running)
20597978SPeter.Dunlap@Sun.COM 		cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex);
20607978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
20617978SPeter.Dunlap@Sun.COM 
20627978SPeter.Dunlap@Sun.COM 	/*
20637978SPeter.Dunlap@Sun.COM 	 * Allocate the task ID table and set "next" to 0.
20647978SPeter.Dunlap@Sun.COM 	 */
20657978SPeter.Dunlap@Sun.COM 
20667978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_max = idm_max_taskids;
20677978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_table = (idm_task_t **)
20687978SPeter.Dunlap@Sun.COM 	    kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP);
20697978SPeter.Dunlap@Sun.COM 	idm.idm_taskid_next = 0;
20707978SPeter.Dunlap@Sun.COM 
20717978SPeter.Dunlap@Sun.COM 	/* Create the global buffer and task kmem caches */
20727978SPeter.Dunlap@Sun.COM 	idm.idm_buf_cache = kmem_cache_create("idm_buf_cache",
20737978SPeter.Dunlap@Sun.COM 	    sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
20747978SPeter.Dunlap@Sun.COM 
20757978SPeter.Dunlap@Sun.COM 	/*
20767978SPeter.Dunlap@Sun.COM 	 * Note, we're explicitly allocating an additional iSER header-
20777978SPeter.Dunlap@Sun.COM 	 * sized chunk for each of these elements. See idm_task_constructor().
20787978SPeter.Dunlap@Sun.COM 	 */
20797978SPeter.Dunlap@Sun.COM 	idm.idm_task_cache = kmem_cache_create("idm_task_cache",
20807978SPeter.Dunlap@Sun.COM 	    sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8,
20817978SPeter.Dunlap@Sun.COM 	    &idm_task_constructor, &idm_task_destructor,
20827978SPeter.Dunlap@Sun.COM 	    NULL, NULL, NULL, KM_SLEEP);
20837978SPeter.Dunlap@Sun.COM 
20847978SPeter.Dunlap@Sun.COM 	/* Create the service and connection context lists */
20857978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
20867978SPeter.Dunlap@Sun.COM 	    offsetof(idm_svc_t, is_list_node));
20877978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t),
20887978SPeter.Dunlap@Sun.COM 	    offsetof(idm_conn_t, ic_list_node));
20897978SPeter.Dunlap@Sun.COM 	list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
20907978SPeter.Dunlap@Sun.COM 	    offsetof(idm_conn_t, ic_list_node));
20917978SPeter.Dunlap@Sun.COM 
20927978SPeter.Dunlap@Sun.COM 	/* Initialize the native sockets transport */
20937978SPeter.Dunlap@Sun.COM 	idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]);
20947978SPeter.Dunlap@Sun.COM 
20957978SPeter.Dunlap@Sun.COM 	/* Create connection ID pool */
20967978SPeter.Dunlap@Sun.COM 	(void) idm_idpool_create(&idm.idm_conn_id_pool);
20977978SPeter.Dunlap@Sun.COM 
20987978SPeter.Dunlap@Sun.COM 	return (DDI_SUCCESS);
20997978SPeter.Dunlap@Sun.COM }
21007978SPeter.Dunlap@Sun.COM 
21017978SPeter.Dunlap@Sun.COM static int
21027978SPeter.Dunlap@Sun.COM _idm_fini(void)
21037978SPeter.Dunlap@Sun.COM {
21047978SPeter.Dunlap@Sun.COM 	if (!list_is_empty(&idm.idm_ini_conn_list) ||
21057978SPeter.Dunlap@Sun.COM 	    !list_is_empty(&idm.idm_tgt_conn_list) ||
21067978SPeter.Dunlap@Sun.COM 	    !list_is_empty(&idm.idm_tgt_svc_list)) {
21077978SPeter.Dunlap@Sun.COM 		return (EBUSY);
21087978SPeter.Dunlap@Sun.COM 	}
21097978SPeter.Dunlap@Sun.COM 
21107978SPeter.Dunlap@Sun.COM 	mutex_enter(&idm.idm_global_mutex);
21117978SPeter.Dunlap@Sun.COM 	idm.idm_wd_thread_running = B_FALSE;
21127978SPeter.Dunlap@Sun.COM 	cv_signal(&idm.idm_wd_cv);
21137978SPeter.Dunlap@Sun.COM 	mutex_exit(&idm.idm_global_mutex);
21147978SPeter.Dunlap@Sun.COM 
21157978SPeter.Dunlap@Sun.COM 	thread_join(idm.idm_wd_thread_did);
21167978SPeter.Dunlap@Sun.COM 
21177978SPeter.Dunlap@Sun.COM 	idm_idpool_destroy(&idm.idm_conn_id_pool);
21187978SPeter.Dunlap@Sun.COM 	idm_so_fini();
21197978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_ini_conn_list);
21207978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_tgt_conn_list);
21217978SPeter.Dunlap@Sun.COM 	list_destroy(&idm.idm_tgt_svc_list);
21227978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_task_cache);
21237978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_buf_cache);
21247978SPeter.Dunlap@Sun.COM 	kmem_free(idm.idm_taskid_table,
21257978SPeter.Dunlap@Sun.COM 	    idm.idm_taskid_max * sizeof (idm_task_t *));
21267978SPeter.Dunlap@Sun.COM 	mutex_destroy(&idm.idm_global_mutex);
21277978SPeter.Dunlap@Sun.COM 	cv_destroy(&idm.idm_wd_cv);
21287978SPeter.Dunlap@Sun.COM 	cv_destroy(&idm.idm_tgt_svc_cv);
21297978SPeter.Dunlap@Sun.COM 	rw_destroy(&idm.idm_taskid_table_lock);
21307978SPeter.Dunlap@Sun.COM 
21317978SPeter.Dunlap@Sun.COM 	return (0);
21327978SPeter.Dunlap@Sun.COM }
2133