xref: /illumos-gate/usr/src/uts/common/io/idm/idm.c (revision a6d42e7d)
1*a6d42e7dSPeter Dunlap /*
2*a6d42e7dSPeter Dunlap  * CDDL HEADER START
3*a6d42e7dSPeter Dunlap  *
4*a6d42e7dSPeter Dunlap  * The contents of this file are subject to the terms of the
5*a6d42e7dSPeter Dunlap  * Common Development and Distribution License (the "License").
6*a6d42e7dSPeter Dunlap  * You may not use this file except in compliance with the License.
7*a6d42e7dSPeter Dunlap  *
8*a6d42e7dSPeter Dunlap  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a6d42e7dSPeter Dunlap  * or http://www.opensolaris.org/os/licensing.
10*a6d42e7dSPeter Dunlap  * See the License for the specific language governing permissions
11*a6d42e7dSPeter Dunlap  * and limitations under the License.
12*a6d42e7dSPeter Dunlap  *
13*a6d42e7dSPeter Dunlap  * When distributing Covered Code, include this CDDL HEADER in each
14*a6d42e7dSPeter Dunlap  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a6d42e7dSPeter Dunlap  * If applicable, add the following below this CDDL HEADER, with the
16*a6d42e7dSPeter Dunlap  * fields enclosed by brackets "[]" replaced with your own identifying
17*a6d42e7dSPeter Dunlap  * information: Portions Copyright [yyyy] [name of copyright owner]
18*a6d42e7dSPeter Dunlap  *
19*a6d42e7dSPeter Dunlap  * CDDL HEADER END
20*a6d42e7dSPeter Dunlap  */
21*a6d42e7dSPeter Dunlap /*
22*a6d42e7dSPeter Dunlap  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*a6d42e7dSPeter Dunlap  * Use is subject to license terms.
24*a6d42e7dSPeter Dunlap  */
25*a6d42e7dSPeter Dunlap 
26*a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
27*a6d42e7dSPeter Dunlap #include <sys/conf.h>
28*a6d42e7dSPeter Dunlap #include <sys/file.h>
29*a6d42e7dSPeter Dunlap #include <sys/ddi.h>
30*a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
31*a6d42e7dSPeter Dunlap #include <sys/modctl.h>
32*a6d42e7dSPeter Dunlap 
33*a6d42e7dSPeter Dunlap #include <sys/socket.h>
34*a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
35*a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
36*a6d42e7dSPeter Dunlap 
37*a6d42e7dSPeter Dunlap #include <sys/socketvar.h>
38*a6d42e7dSPeter Dunlap #include <netinet/in.h>
39*a6d42e7dSPeter Dunlap 
40*a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
41*a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
42*a6d42e7dSPeter Dunlap 
43*a6d42e7dSPeter Dunlap #define	IDM_NAME_VERSION	"iSCSI Data Mover"
44*a6d42e7dSPeter Dunlap 
45*a6d42e7dSPeter Dunlap extern struct mod_ops mod_miscops;
46*a6d42e7dSPeter Dunlap extern struct mod_ops mod_miscops;
47*a6d42e7dSPeter Dunlap 
48*a6d42e7dSPeter Dunlap static struct modlmisc modlmisc = {
49*a6d42e7dSPeter Dunlap 	&mod_miscops,	/* Type of module */
50*a6d42e7dSPeter Dunlap 	IDM_NAME_VERSION
51*a6d42e7dSPeter Dunlap };
52*a6d42e7dSPeter Dunlap 
53*a6d42e7dSPeter Dunlap static struct modlinkage modlinkage = {
54*a6d42e7dSPeter Dunlap 	MODREV_1, (void *)&modlmisc, NULL
55*a6d42e7dSPeter Dunlap };
56*a6d42e7dSPeter Dunlap 
57*a6d42e7dSPeter Dunlap extern int idm_task_compare(const void *t1, const void *t2);
58*a6d42e7dSPeter Dunlap extern void idm_wd_thread(void *arg);
59*a6d42e7dSPeter Dunlap 
60*a6d42e7dSPeter Dunlap static int _idm_init(void);
61*a6d42e7dSPeter Dunlap static int _idm_fini(void);
62*a6d42e7dSPeter Dunlap static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
63*a6d42e7dSPeter Dunlap static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
64*a6d42e7dSPeter Dunlap static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
65*a6d42e7dSPeter Dunlap static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
66*a6d42e7dSPeter Dunlap static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
67*a6d42e7dSPeter Dunlap     idm_abort_type_t abort_type);
68*a6d42e7dSPeter Dunlap static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
69*a6d42e7dSPeter Dunlap 
70*a6d42e7dSPeter Dunlap boolean_t idm_conn_logging = 0;
71*a6d42e7dSPeter Dunlap boolean_t idm_svc_logging = 0;
72*a6d42e7dSPeter Dunlap 
73*a6d42e7dSPeter Dunlap /*
74*a6d42e7dSPeter Dunlap  * Potential tuneable for the maximum number of tasks.  Default to
75*a6d42e7dSPeter Dunlap  * IDM_TASKIDS_MAX
76*a6d42e7dSPeter Dunlap  */
77*a6d42e7dSPeter Dunlap 
78*a6d42e7dSPeter Dunlap uint32_t	idm_max_taskids = IDM_TASKIDS_MAX;
79*a6d42e7dSPeter Dunlap 
80*a6d42e7dSPeter Dunlap /*
81*a6d42e7dSPeter Dunlap  * Global list of transport handles
82*a6d42e7dSPeter Dunlap  *   These are listed in preferential order, so we can simply take the
83*a6d42e7dSPeter Dunlap  *   first "it_conn_is_capable" hit. Note also that the order maps to
84*a6d42e7dSPeter Dunlap  *   the order of the idm_transport_type_t list.
85*a6d42e7dSPeter Dunlap  */
86*a6d42e7dSPeter Dunlap idm_transport_t idm_transport_list[] = {
87*a6d42e7dSPeter Dunlap 
88*a6d42e7dSPeter Dunlap 	/* iSER on InfiniBand transport handle */
89*a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_ISER,	/* type */
90*a6d42e7dSPeter Dunlap 	"/devices/ib/iser@0:iser",	/* device path */
91*a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
92*a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
93*a6d42e7dSPeter Dunlap 	NULL},				/* transport caps */
94*a6d42e7dSPeter Dunlap 
95*a6d42e7dSPeter Dunlap 	/* IDM native sockets transport handle */
96*a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_SOCKETS,	/* type */
97*a6d42e7dSPeter Dunlap 	NULL,				/* device path */
98*a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
99*a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
100*a6d42e7dSPeter Dunlap 	NULL}				/* transport caps */
101*a6d42e7dSPeter Dunlap 
102*a6d42e7dSPeter Dunlap };
103*a6d42e7dSPeter Dunlap 
104*a6d42e7dSPeter Dunlap int
105*a6d42e7dSPeter Dunlap _init(void)
106*a6d42e7dSPeter Dunlap {
107*a6d42e7dSPeter Dunlap 	int rc;
108*a6d42e7dSPeter Dunlap 
109*a6d42e7dSPeter Dunlap 	if ((rc = _idm_init()) != 0) {
110*a6d42e7dSPeter Dunlap 		return (rc);
111*a6d42e7dSPeter Dunlap 	}
112*a6d42e7dSPeter Dunlap 
113*a6d42e7dSPeter Dunlap 	return (mod_install(&modlinkage));
114*a6d42e7dSPeter Dunlap }
115*a6d42e7dSPeter Dunlap 
116*a6d42e7dSPeter Dunlap int
117*a6d42e7dSPeter Dunlap _fini(void)
118*a6d42e7dSPeter Dunlap {
119*a6d42e7dSPeter Dunlap 	int rc;
120*a6d42e7dSPeter Dunlap 
121*a6d42e7dSPeter Dunlap 	if ((rc = _idm_fini()) != 0) {
122*a6d42e7dSPeter Dunlap 		return (rc);
123*a6d42e7dSPeter Dunlap 	}
124*a6d42e7dSPeter Dunlap 
125*a6d42e7dSPeter Dunlap 	if ((rc = mod_remove(&modlinkage)) != 0) {
126*a6d42e7dSPeter Dunlap 		return (rc);
127*a6d42e7dSPeter Dunlap 	}
128*a6d42e7dSPeter Dunlap 
129*a6d42e7dSPeter Dunlap 	return (rc);
130*a6d42e7dSPeter Dunlap }
131*a6d42e7dSPeter Dunlap 
132*a6d42e7dSPeter Dunlap int
133*a6d42e7dSPeter Dunlap _info(struct modinfo *modinfop)
134*a6d42e7dSPeter Dunlap {
135*a6d42e7dSPeter Dunlap 	return (mod_info(&modlinkage, modinfop));
136*a6d42e7dSPeter Dunlap }
137*a6d42e7dSPeter Dunlap 
138*a6d42e7dSPeter Dunlap /*
139*a6d42e7dSPeter Dunlap  * idm_transport_register()
140*a6d42e7dSPeter Dunlap  *
141*a6d42e7dSPeter Dunlap  * Provides a mechanism for an IDM transport driver to register its
142*a6d42e7dSPeter Dunlap  * transport ops and caps with the IDM kernel module. Invoked during
143*a6d42e7dSPeter Dunlap  * a transport driver's attach routine.
144*a6d42e7dSPeter Dunlap  */
145*a6d42e7dSPeter Dunlap idm_status_t
146*a6d42e7dSPeter Dunlap idm_transport_register(idm_transport_attr_t *attr)
147*a6d42e7dSPeter Dunlap {
148*a6d42e7dSPeter Dunlap 	ASSERT(attr->it_ops != NULL);
149*a6d42e7dSPeter Dunlap 	ASSERT(attr->it_caps != NULL);
150*a6d42e7dSPeter Dunlap 
151*a6d42e7dSPeter Dunlap 	switch (attr->type) {
152*a6d42e7dSPeter Dunlap 	/* All known non-native transports here; for now, iSER */
153*a6d42e7dSPeter Dunlap 	case IDM_TRANSPORT_TYPE_ISER:
154*a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_ops	= attr->it_ops;
155*a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_caps	= attr->it_caps;
156*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
157*a6d42e7dSPeter Dunlap 
158*a6d42e7dSPeter Dunlap 	default:
159*a6d42e7dSPeter Dunlap 		cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
160*a6d42e7dSPeter Dunlap 		    "idm_transport_register", attr->type);
161*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
162*a6d42e7dSPeter Dunlap 	}
163*a6d42e7dSPeter Dunlap }
164*a6d42e7dSPeter Dunlap 
165*a6d42e7dSPeter Dunlap /*
166*a6d42e7dSPeter Dunlap  * idm_ini_conn_create
167*a6d42e7dSPeter Dunlap  *
168*a6d42e7dSPeter Dunlap  * This function is invoked by the iSCSI layer to create a connection context.
169*a6d42e7dSPeter Dunlap  * This does not actually establish the socket connection.
170*a6d42e7dSPeter Dunlap  *
171*a6d42e7dSPeter Dunlap  * cr - Connection request parameters
172*a6d42e7dSPeter Dunlap  * new_con - Output parameter that contains the new request if successful
173*a6d42e7dSPeter Dunlap  *
174*a6d42e7dSPeter Dunlap  */
175*a6d42e7dSPeter Dunlap idm_status_t
176*a6d42e7dSPeter Dunlap idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
177*a6d42e7dSPeter Dunlap {
178*a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
179*a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
180*a6d42e7dSPeter Dunlap 	int			rc;
181*a6d42e7dSPeter Dunlap 
182*a6d42e7dSPeter Dunlap 	it = idm_transport_lookup(cr);
183*a6d42e7dSPeter Dunlap 
184*a6d42e7dSPeter Dunlap retry:
185*a6d42e7dSPeter Dunlap 	ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
186*a6d42e7dSPeter Dunlap 	    &cr->icr_conn_ops);
187*a6d42e7dSPeter Dunlap 
188*a6d42e7dSPeter Dunlap 	bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
189*a6d42e7dSPeter Dunlap 	    sizeof (cr->cr_ini_dst_addr));
190*a6d42e7dSPeter Dunlap 
191*a6d42e7dSPeter Dunlap 	/* create the transport-specific connection components */
192*a6d42e7dSPeter Dunlap 	rc = it->it_ops->it_ini_conn_create(cr, ic);
193*a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
194*a6d42e7dSPeter Dunlap 		/* cleanup the failed connection */
195*a6d42e7dSPeter Dunlap 		idm_conn_destroy_common(ic);
196*a6d42e7dSPeter Dunlap 		kmem_free(ic, sizeof (idm_conn_t));
197*a6d42e7dSPeter Dunlap 
198*a6d42e7dSPeter Dunlap 		/*
199*a6d42e7dSPeter Dunlap 		 * It is possible for an IB client to connect to
200*a6d42e7dSPeter Dunlap 		 * an ethernet-only client via an IB-eth gateway.
201*a6d42e7dSPeter Dunlap 		 * Therefore, if we are attempting to use iSER and
202*a6d42e7dSPeter Dunlap 		 * fail, retry with sockets before ultimately
203*a6d42e7dSPeter Dunlap 		 * failing the connection.
204*a6d42e7dSPeter Dunlap 		 */
205*a6d42e7dSPeter Dunlap 		if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
206*a6d42e7dSPeter Dunlap 			it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
207*a6d42e7dSPeter Dunlap 			goto retry;
208*a6d42e7dSPeter Dunlap 		}
209*a6d42e7dSPeter Dunlap 
210*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
211*a6d42e7dSPeter Dunlap 	}
212*a6d42e7dSPeter Dunlap 
213*a6d42e7dSPeter Dunlap 	*new_con = ic;
214*a6d42e7dSPeter Dunlap 
215*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
216*a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_ini_conn_list, ic);
217*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
218*a6d42e7dSPeter Dunlap 
219*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
220*a6d42e7dSPeter Dunlap }
221*a6d42e7dSPeter Dunlap 
222*a6d42e7dSPeter Dunlap /*
223*a6d42e7dSPeter Dunlap  * idm_ini_conn_destroy
224*a6d42e7dSPeter Dunlap  *
225*a6d42e7dSPeter Dunlap  * Releases any resources associated with the connection.  This is the
226*a6d42e7dSPeter Dunlap  * complement to idm_ini_conn_create.
227*a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
228*a6d42e7dSPeter Dunlap  *
229*a6d42e7dSPeter Dunlap  */
230*a6d42e7dSPeter Dunlap void
231*a6d42e7dSPeter Dunlap idm_ini_conn_destroy(idm_conn_t *ic)
232*a6d42e7dSPeter Dunlap {
233*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
234*a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_ini_conn_list, ic);
235*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
236*a6d42e7dSPeter Dunlap 
237*a6d42e7dSPeter Dunlap 	ic->ic_transport_ops->it_ini_conn_destroy(ic);
238*a6d42e7dSPeter Dunlap 	idm_conn_destroy_common(ic);
239*a6d42e7dSPeter Dunlap }
240*a6d42e7dSPeter Dunlap 
241*a6d42e7dSPeter Dunlap /*
242*a6d42e7dSPeter Dunlap  * idm_ini_conn_connect
243*a6d42e7dSPeter Dunlap  *
244*a6d42e7dSPeter Dunlap  * Establish connection to the remote system identified in idm_conn_t.
245*a6d42e7dSPeter Dunlap  * The connection parameters including the remote IP address were established
246*a6d42e7dSPeter Dunlap  * in the call to idm_ini_conn_create.
247*a6d42e7dSPeter Dunlap  *
248*a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
249*a6d42e7dSPeter Dunlap  *
250*a6d42e7dSPeter Dunlap  * Returns success if the connection was established, otherwise some kind
251*a6d42e7dSPeter Dunlap  * of meaningful error code.
252*a6d42e7dSPeter Dunlap  *
253*a6d42e7dSPeter Dunlap  * Upon return the initiator can send a "login" request when it is ready.
254*a6d42e7dSPeter Dunlap  */
255*a6d42e7dSPeter Dunlap idm_status_t
256*a6d42e7dSPeter Dunlap idm_ini_conn_connect(idm_conn_t *ic)
257*a6d42e7dSPeter Dunlap {
258*a6d42e7dSPeter Dunlap 	idm_status_t	rc;
259*a6d42e7dSPeter Dunlap 
260*a6d42e7dSPeter Dunlap 	rc = idm_conn_sm_init(ic);
261*a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
262*a6d42e7dSPeter Dunlap 		return (ic->ic_conn_sm_status);
263*a6d42e7dSPeter Dunlap 	}
264*a6d42e7dSPeter Dunlap 	/* Kick state machine */
265*a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_CONNECT_REQ, NULL);
266*a6d42e7dSPeter Dunlap 
267*a6d42e7dSPeter Dunlap 	/* Wait for login flag */
268*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
269*a6d42e7dSPeter Dunlap 	while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
270*a6d42e7dSPeter Dunlap 	    !(ic->ic_state_flags & CF_ERROR)) {
271*a6d42e7dSPeter Dunlap 		cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
272*a6d42e7dSPeter Dunlap 	}
273*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
274*a6d42e7dSPeter Dunlap 
275*a6d42e7dSPeter Dunlap 	if (ic->ic_state_flags & CF_ERROR) {
276*a6d42e7dSPeter Dunlap 		/* ic->ic_conn_sm_status will contains failure status */
277*a6d42e7dSPeter Dunlap 		return (ic->ic_conn_sm_status);
278*a6d42e7dSPeter Dunlap 	}
279*a6d42e7dSPeter Dunlap 
280*a6d42e7dSPeter Dunlap 	/* Ready to login */
281*a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
282*a6d42e7dSPeter Dunlap 	(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
283*a6d42e7dSPeter Dunlap 
284*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
285*a6d42e7dSPeter Dunlap }
286*a6d42e7dSPeter Dunlap 
287*a6d42e7dSPeter Dunlap /*
288*a6d42e7dSPeter Dunlap  * idm_ini_conn_sm_fini_task()
289*a6d42e7dSPeter Dunlap  *
290*a6d42e7dSPeter Dunlap  * Dispatch a thread on the global taskq to tear down an initiator connection's
291*a6d42e7dSPeter Dunlap  * state machine. Note: We cannot do this from the disconnect thread as we will
292*a6d42e7dSPeter Dunlap  * end up in a situation wherein the thread is running on a taskq that it then
293*a6d42e7dSPeter Dunlap  * attempts to destroy.
294*a6d42e7dSPeter Dunlap  */
295*a6d42e7dSPeter Dunlap static void
296*a6d42e7dSPeter Dunlap idm_ini_conn_sm_fini_task(void *ic_void)
297*a6d42e7dSPeter Dunlap {
298*a6d42e7dSPeter Dunlap 	idm_conn_sm_fini((idm_conn_t *)ic_void);
299*a6d42e7dSPeter Dunlap }
300*a6d42e7dSPeter Dunlap 
301*a6d42e7dSPeter Dunlap /*
302*a6d42e7dSPeter Dunlap  * idm_ini_conn_disconnect
303*a6d42e7dSPeter Dunlap  *
304*a6d42e7dSPeter Dunlap  * Forces a connection (previously established using idm_ini_conn_connect)
305*a6d42e7dSPeter Dunlap  * to perform a controlled shutdown, cleaning up any outstanding requests.
306*a6d42e7dSPeter Dunlap  *
307*a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
308*a6d42e7dSPeter Dunlap  *
309*a6d42e7dSPeter Dunlap  * This is synchronous and it will return when the connection has been
310*a6d42e7dSPeter Dunlap  * properly shutdown.
311*a6d42e7dSPeter Dunlap  */
312*a6d42e7dSPeter Dunlap /* ARGSUSED */
313*a6d42e7dSPeter Dunlap void
314*a6d42e7dSPeter Dunlap idm_ini_conn_disconnect(idm_conn_t *ic)
315*a6d42e7dSPeter Dunlap {
316*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
317*a6d42e7dSPeter Dunlap 
318*a6d42e7dSPeter Dunlap 	if (ic->ic_state_flags == 0) {
319*a6d42e7dSPeter Dunlap 		/* already disconnected */
320*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
321*a6d42e7dSPeter Dunlap 		return;
322*a6d42e7dSPeter Dunlap 	}
323*a6d42e7dSPeter Dunlap 	ic->ic_state_flags = 0;
324*a6d42e7dSPeter Dunlap 	ic->ic_conn_sm_status = 0;
325*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
326*a6d42e7dSPeter Dunlap 
327*a6d42e7dSPeter Dunlap 	/* invoke the transport-specific conn_destroy */
328*a6d42e7dSPeter Dunlap 	(void) ic->ic_transport_ops->it_ini_conn_disconnect(ic);
329*a6d42e7dSPeter Dunlap 
330*a6d42e7dSPeter Dunlap 	/* teardown the connection sm */
331*a6d42e7dSPeter Dunlap 	(void) taskq_dispatch(idm.idm_global_taskq, &idm_ini_conn_sm_fini_task,
332*a6d42e7dSPeter Dunlap 	    (void *)ic, TQ_SLEEP);
333*a6d42e7dSPeter Dunlap }
334*a6d42e7dSPeter Dunlap 
335*a6d42e7dSPeter Dunlap /*
336*a6d42e7dSPeter Dunlap  * idm_tgt_svc_create
337*a6d42e7dSPeter Dunlap  *
338*a6d42e7dSPeter Dunlap  * The target calls this service to obtain a service context for each available
339*a6d42e7dSPeter Dunlap  * transport, starting a service of each type related to the IP address and port
340*a6d42e7dSPeter Dunlap  * passed. The idm_svc_req_t contains the service parameters.
341*a6d42e7dSPeter Dunlap  */
342*a6d42e7dSPeter Dunlap idm_status_t
343*a6d42e7dSPeter Dunlap idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
344*a6d42e7dSPeter Dunlap {
345*a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
346*a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
347*a6d42e7dSPeter Dunlap 	idm_svc_t		*is;
348*a6d42e7dSPeter Dunlap 	int			rc;
349*a6d42e7dSPeter Dunlap 
350*a6d42e7dSPeter Dunlap 	*new_svc = NULL;
351*a6d42e7dSPeter Dunlap 	is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
352*a6d42e7dSPeter Dunlap 
353*a6d42e7dSPeter Dunlap 	/* Initialize transport-agnostic components of the service handle */
354*a6d42e7dSPeter Dunlap 	is->is_svc_req = *sr;
355*a6d42e7dSPeter Dunlap 	mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
356*a6d42e7dSPeter Dunlap 	cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
357*a6d42e7dSPeter Dunlap 	mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
358*a6d42e7dSPeter Dunlap 	cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
359*a6d42e7dSPeter Dunlap 	idm_refcnt_init(&is->is_refcnt, is);
360*a6d42e7dSPeter Dunlap 
361*a6d42e7dSPeter Dunlap 	/*
362*a6d42e7dSPeter Dunlap 	 * Make sure all available transports are setup.  We call this now
363*a6d42e7dSPeter Dunlap 	 * instead of at initialization time in case IB has become available
364*a6d42e7dSPeter Dunlap 	 * since we started (hotplug, etc).
365*a6d42e7dSPeter Dunlap 	 */
366*a6d42e7dSPeter Dunlap 	idm_transport_setup(sr->sr_li);
367*a6d42e7dSPeter Dunlap 
368*a6d42e7dSPeter Dunlap 	/*
369*a6d42e7dSPeter Dunlap 	 * Loop through the transports, configuring the transport-specific
370*a6d42e7dSPeter Dunlap 	 * components of each one.
371*a6d42e7dSPeter Dunlap 	 */
372*a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
373*a6d42e7dSPeter Dunlap 
374*a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
375*a6d42e7dSPeter Dunlap 		/*
376*a6d42e7dSPeter Dunlap 		 * If it_ops is NULL then the transport is unconfigured
377*a6d42e7dSPeter Dunlap 		 * and we shouldn't try to start the service.
378*a6d42e7dSPeter Dunlap 		 */
379*a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
380*a6d42e7dSPeter Dunlap 			continue;
381*a6d42e7dSPeter Dunlap 		}
382*a6d42e7dSPeter Dunlap 
383*a6d42e7dSPeter Dunlap 		rc = it->it_ops->it_tgt_svc_create(sr, is);
384*a6d42e7dSPeter Dunlap 		if (rc != IDM_STATUS_SUCCESS) {
385*a6d42e7dSPeter Dunlap 			/* Teardown any configured services */
386*a6d42e7dSPeter Dunlap 			while (type--) {
387*a6d42e7dSPeter Dunlap 				it = &idm_transport_list[type];
388*a6d42e7dSPeter Dunlap 				if (it->it_ops == NULL) {
389*a6d42e7dSPeter Dunlap 					continue;
390*a6d42e7dSPeter Dunlap 				}
391*a6d42e7dSPeter Dunlap 				it->it_ops->it_tgt_svc_destroy(is);
392*a6d42e7dSPeter Dunlap 			}
393*a6d42e7dSPeter Dunlap 			/* Free the svc context and return */
394*a6d42e7dSPeter Dunlap 			kmem_free(is, sizeof (idm_svc_t));
395*a6d42e7dSPeter Dunlap 			return (rc);
396*a6d42e7dSPeter Dunlap 		}
397*a6d42e7dSPeter Dunlap 	}
398*a6d42e7dSPeter Dunlap 
399*a6d42e7dSPeter Dunlap 	*new_svc = is;
400*a6d42e7dSPeter Dunlap 
401*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
402*a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_tgt_svc_list, is);
403*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
404*a6d42e7dSPeter Dunlap 
405*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
406*a6d42e7dSPeter Dunlap }
407*a6d42e7dSPeter Dunlap 
408*a6d42e7dSPeter Dunlap /*
409*a6d42e7dSPeter Dunlap  * idm_tgt_svc_destroy
410*a6d42e7dSPeter Dunlap  *
411*a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
412*a6d42e7dSPeter Dunlap  *
413*a6d42e7dSPeter Dunlap  * Cleanup any resources associated with the idm_svc_t.
414*a6d42e7dSPeter Dunlap  */
415*a6d42e7dSPeter Dunlap void
416*a6d42e7dSPeter Dunlap idm_tgt_svc_destroy(idm_svc_t *is)
417*a6d42e7dSPeter Dunlap {
418*a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
419*a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
420*a6d42e7dSPeter Dunlap 
421*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
422*a6d42e7dSPeter Dunlap 	/* remove this service from the global list */
423*a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_tgt_svc_list, is);
424*a6d42e7dSPeter Dunlap 	/* wakeup any waiters for service change */
425*a6d42e7dSPeter Dunlap 	cv_broadcast(&idm.idm_tgt_svc_cv);
426*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
427*a6d42e7dSPeter Dunlap 
428*a6d42e7dSPeter Dunlap 	/* tear down the svc resources */
429*a6d42e7dSPeter Dunlap 	idm_refcnt_destroy(&is->is_refcnt);
430*a6d42e7dSPeter Dunlap 	cv_destroy(&is->is_count_cv);
431*a6d42e7dSPeter Dunlap 	mutex_destroy(&is->is_count_mutex);
432*a6d42e7dSPeter Dunlap 	cv_destroy(&is->is_cv);
433*a6d42e7dSPeter Dunlap 	mutex_destroy(&is->is_mutex);
434*a6d42e7dSPeter Dunlap 
435*a6d42e7dSPeter Dunlap 	/* teardown each transport-specific service */
436*a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
437*a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
438*a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
439*a6d42e7dSPeter Dunlap 			continue;
440*a6d42e7dSPeter Dunlap 		}
441*a6d42e7dSPeter Dunlap 
442*a6d42e7dSPeter Dunlap 		it->it_ops->it_tgt_svc_destroy(is);
443*a6d42e7dSPeter Dunlap 	}
444*a6d42e7dSPeter Dunlap 
445*a6d42e7dSPeter Dunlap 	/* free the svc handle */
446*a6d42e7dSPeter Dunlap 	kmem_free(is, sizeof (idm_svc_t));
447*a6d42e7dSPeter Dunlap }
448*a6d42e7dSPeter Dunlap 
449*a6d42e7dSPeter Dunlap void
450*a6d42e7dSPeter Dunlap idm_tgt_svc_hold(idm_svc_t *is)
451*a6d42e7dSPeter Dunlap {
452*a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&is->is_refcnt);
453*a6d42e7dSPeter Dunlap }
454*a6d42e7dSPeter Dunlap 
455*a6d42e7dSPeter Dunlap void
456*a6d42e7dSPeter Dunlap idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
457*a6d42e7dSPeter Dunlap {
458*a6d42e7dSPeter Dunlap 	idm_refcnt_rele_and_destroy(&is->is_refcnt,
459*a6d42e7dSPeter Dunlap 	    (idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
460*a6d42e7dSPeter Dunlap }
461*a6d42e7dSPeter Dunlap 
462*a6d42e7dSPeter Dunlap /*
463*a6d42e7dSPeter Dunlap  * idm_tgt_svc_online
464*a6d42e7dSPeter Dunlap  *
465*a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
466*a6d42e7dSPeter Dunlap  *
467*a6d42e7dSPeter Dunlap  * Online each transport service, as we want this target to be accessible
468*a6d42e7dSPeter Dunlap  * via any configured transport.
469*a6d42e7dSPeter Dunlap  *
470*a6d42e7dSPeter Dunlap  * When the initiator establishes a new connection to the target, IDM will
471*a6d42e7dSPeter Dunlap  * call the "new connect" callback defined in the idm_svc_req_t structure
472*a6d42e7dSPeter Dunlap  * and it will pass an idm_conn_t structure representing that new connection.
473*a6d42e7dSPeter Dunlap  */
474*a6d42e7dSPeter Dunlap idm_status_t
475*a6d42e7dSPeter Dunlap idm_tgt_svc_online(idm_svc_t *is)
476*a6d42e7dSPeter Dunlap {
477*a6d42e7dSPeter Dunlap 
478*a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
479*a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
480*a6d42e7dSPeter Dunlap 	int			rc;
481*a6d42e7dSPeter Dunlap 	int			svc_found;
482*a6d42e7dSPeter Dunlap 
483*a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
484*a6d42e7dSPeter Dunlap 	/* Walk through each of the transports and online them */
485*a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
486*a6d42e7dSPeter Dunlap 		svc_found = 0;
487*a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
488*a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
489*a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
490*a6d42e7dSPeter Dunlap 				/* transport is not registered */
491*a6d42e7dSPeter Dunlap 				continue;
492*a6d42e7dSPeter Dunlap 			}
493*a6d42e7dSPeter Dunlap 
494*a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
495*a6d42e7dSPeter Dunlap 			rc = it->it_ops->it_tgt_svc_online(is);
496*a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
497*a6d42e7dSPeter Dunlap 			if (rc == IDM_STATUS_SUCCESS) {
498*a6d42e7dSPeter Dunlap 				/* We have at least one service running. */
499*a6d42e7dSPeter Dunlap 				svc_found = 1;
500*a6d42e7dSPeter Dunlap 			}
501*a6d42e7dSPeter Dunlap 		}
502*a6d42e7dSPeter Dunlap 	} else {
503*a6d42e7dSPeter Dunlap 		svc_found = 1;
504*a6d42e7dSPeter Dunlap 	}
505*a6d42e7dSPeter Dunlap 	if (svc_found)
506*a6d42e7dSPeter Dunlap 		is->is_online++;
507*a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
508*a6d42e7dSPeter Dunlap 
509*a6d42e7dSPeter Dunlap 	return (svc_found ? IDM_STATUS_SUCCESS : IDM_STATUS_FAIL);
510*a6d42e7dSPeter Dunlap }
511*a6d42e7dSPeter Dunlap 
512*a6d42e7dSPeter Dunlap /*
513*a6d42e7dSPeter Dunlap  * idm_tgt_svc_offline
514*a6d42e7dSPeter Dunlap  *
515*a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
516*a6d42e7dSPeter Dunlap  *
517*a6d42e7dSPeter Dunlap  * Shutdown any online target services.
518*a6d42e7dSPeter Dunlap  */
519*a6d42e7dSPeter Dunlap void
520*a6d42e7dSPeter Dunlap idm_tgt_svc_offline(idm_svc_t *is)
521*a6d42e7dSPeter Dunlap {
522*a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
523*a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
524*a6d42e7dSPeter Dunlap 
525*a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
526*a6d42e7dSPeter Dunlap 	is->is_online--;
527*a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
528*a6d42e7dSPeter Dunlap 		/* Walk through each of the transports and offline them */
529*a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
530*a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
531*a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
532*a6d42e7dSPeter Dunlap 				/* transport is not registered */
533*a6d42e7dSPeter Dunlap 				continue;
534*a6d42e7dSPeter Dunlap 			}
535*a6d42e7dSPeter Dunlap 
536*a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
537*a6d42e7dSPeter Dunlap 			it->it_ops->it_tgt_svc_offline(is);
538*a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
539*a6d42e7dSPeter Dunlap 		}
540*a6d42e7dSPeter Dunlap 	}
541*a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
542*a6d42e7dSPeter Dunlap }
543*a6d42e7dSPeter Dunlap 
544*a6d42e7dSPeter Dunlap /*
545*a6d42e7dSPeter Dunlap  * idm_tgt_svc_lookup
546*a6d42e7dSPeter Dunlap  *
547*a6d42e7dSPeter Dunlap  * Lookup a service instance listening on the specified port
548*a6d42e7dSPeter Dunlap  */
549*a6d42e7dSPeter Dunlap 
550*a6d42e7dSPeter Dunlap idm_svc_t *
551*a6d42e7dSPeter Dunlap idm_tgt_svc_lookup(uint16_t port)
552*a6d42e7dSPeter Dunlap {
553*a6d42e7dSPeter Dunlap 	idm_svc_t *result;
554*a6d42e7dSPeter Dunlap 
555*a6d42e7dSPeter Dunlap retry:
556*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
557*a6d42e7dSPeter Dunlap 	for (result = list_head(&idm.idm_tgt_svc_list);
558*a6d42e7dSPeter Dunlap 	    result != NULL;
559*a6d42e7dSPeter Dunlap 	    result = list_next(&idm.idm_tgt_svc_list, result)) {
560*a6d42e7dSPeter Dunlap 		if (result->is_svc_req.sr_port == port) {
561*a6d42e7dSPeter Dunlap 			if (result->is_online == 0) {
562*a6d42e7dSPeter Dunlap 				/*
563*a6d42e7dSPeter Dunlap 				 * A service exists on this port, but it
564*a6d42e7dSPeter Dunlap 				 * is going away, wait for it to cleanup.
565*a6d42e7dSPeter Dunlap 				 */
566*a6d42e7dSPeter Dunlap 				cv_wait(&idm.idm_tgt_svc_cv,
567*a6d42e7dSPeter Dunlap 				    &idm.idm_global_mutex);
568*a6d42e7dSPeter Dunlap 				mutex_exit(&idm.idm_global_mutex);
569*a6d42e7dSPeter Dunlap 				goto retry;
570*a6d42e7dSPeter Dunlap 			}
571*a6d42e7dSPeter Dunlap 			idm_tgt_svc_hold(result);
572*a6d42e7dSPeter Dunlap 			mutex_exit(&idm.idm_global_mutex);
573*a6d42e7dSPeter Dunlap 			return (result);
574*a6d42e7dSPeter Dunlap 		}
575*a6d42e7dSPeter Dunlap 	}
576*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
577*a6d42e7dSPeter Dunlap 
578*a6d42e7dSPeter Dunlap 	return (NULL);
579*a6d42e7dSPeter Dunlap }
580*a6d42e7dSPeter Dunlap 
581*a6d42e7dSPeter Dunlap /*
582*a6d42e7dSPeter Dunlap  * idm_negotiate_key_values()
583*a6d42e7dSPeter Dunlap  * Give IDM level a chance to negotiate any login parameters it should own.
584*a6d42e7dSPeter Dunlap  *  -- leave unhandled parameters alone on request_nvl
585*a6d42e7dSPeter Dunlap  *  -- move all handled parameters to response_nvl with an appropriate response
586*a6d42e7dSPeter Dunlap  *  -- also add an entry to negotiated_nvl for any accepted parameters
587*a6d42e7dSPeter Dunlap  */
588*a6d42e7dSPeter Dunlap kv_status_t
589*a6d42e7dSPeter Dunlap idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
590*a6d42e7dSPeter Dunlap     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
591*a6d42e7dSPeter Dunlap {
592*a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
593*a6d42e7dSPeter Dunlap 	return (ic->ic_transport_ops->it_negotiate_key_values(ic,
594*a6d42e7dSPeter Dunlap 	    request_nvl, response_nvl, negotiated_nvl));
595*a6d42e7dSPeter Dunlap }
596*a6d42e7dSPeter Dunlap 
597*a6d42e7dSPeter Dunlap /*
598*a6d42e7dSPeter Dunlap  * idm_notice_key_values()
599*a6d42e7dSPeter Dunlap  * Activate at the IDM level any parameters that have been negotiated.
600*a6d42e7dSPeter Dunlap  * Passes the set of key value pairs to the transport for activation.
601*a6d42e7dSPeter Dunlap  * This will be invoked as the connection is entering full-feature mode.
602*a6d42e7dSPeter Dunlap  */
603*a6d42e7dSPeter Dunlap idm_status_t
604*a6d42e7dSPeter Dunlap idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
605*a6d42e7dSPeter Dunlap {
606*a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
607*a6d42e7dSPeter Dunlap 	return (ic->ic_transport_ops->it_notice_key_values(ic,
608*a6d42e7dSPeter Dunlap 	    negotiated_nvl));
609*a6d42e7dSPeter Dunlap }
610*a6d42e7dSPeter Dunlap 
611*a6d42e7dSPeter Dunlap /*
612*a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini
613*a6d42e7dSPeter Dunlap  *
614*a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Put_Data' operational primitive.
615*a6d42e7dSPeter Dunlap  *
616*a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
617*a6d42e7dSPeter Dunlap  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
618*a6d42e7dSPeter Dunlap  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
619*a6d42e7dSPeter Dunlap  * transferred to the initiator associated with task 'idt'. The connection
620*a6d42e7dSPeter Dunlap  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
621*a6d42e7dSPeter Dunlap  * and the callback (idb->idb_buf_cb) at transfer completion are
622*a6d42e7dSPeter Dunlap  * provided as input.
623*a6d42e7dSPeter Dunlap  *
624*a6d42e7dSPeter Dunlap  * This data transfer takes place transparently to the remote iSCSI layer,
625*a6d42e7dSPeter Dunlap  * i.e. without its participation.
626*a6d42e7dSPeter Dunlap  *
627*a6d42e7dSPeter Dunlap  * Using sockets, IDM implements the data transfer by segmenting the data
628*a6d42e7dSPeter Dunlap  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
629*a6d42e7dSPeter Dunlap  * initiator. iSER performs the transfer using RDMA write.
630*a6d42e7dSPeter Dunlap  *
631*a6d42e7dSPeter Dunlap  */
632*a6d42e7dSPeter Dunlap idm_status_t
633*a6d42e7dSPeter Dunlap idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
634*a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
635*a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
636*a6d42e7dSPeter Dunlap {
637*a6d42e7dSPeter Dunlap 	idm_status_t rc;
638*a6d42e7dSPeter Dunlap 
639*a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
640*a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
641*a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
642*a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
643*a6d42e7dSPeter Dunlap 
644*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
645*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
646*a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
647*a6d42e7dSPeter Dunlap 		idt->idt_tx_to_ini_start++;
648*a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
649*a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
650*a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
651*a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
652*a6d42e7dSPeter Dunlap 		    (idt, idb);
653*a6d42e7dSPeter Dunlap 		return (rc);
654*a6d42e7dSPeter Dunlap 
655*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
656*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
657*a6d42e7dSPeter Dunlap 		/*
658*a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
659*a6d42e7dSPeter Dunlap 		 * is suspended
660*a6d42e7dSPeter Dunlap 		 */
661*a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
662*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
663*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
664*a6d42e7dSPeter Dunlap 
665*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
666*a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
667*a6d42e7dSPeter Dunlap 		/*
668*a6d42e7dSPeter Dunlap 		 * Once the task is aborted, any buffers added to the
669*a6d42e7dSPeter Dunlap 		 * idt_inbufv will never get cleaned up, so just return
670*a6d42e7dSPeter Dunlap 		 * SUCCESS.  The buffer should get cleaned up by the
671*a6d42e7dSPeter Dunlap 		 * client or framework once task_aborted has completed.
672*a6d42e7dSPeter Dunlap 		 */
673*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
674*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
675*a6d42e7dSPeter Dunlap 
676*a6d42e7dSPeter Dunlap 	default:
677*a6d42e7dSPeter Dunlap 		ASSERT(0);
678*a6d42e7dSPeter Dunlap 		break;
679*a6d42e7dSPeter Dunlap 	}
680*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
681*a6d42e7dSPeter Dunlap 
682*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
683*a6d42e7dSPeter Dunlap }
684*a6d42e7dSPeter Dunlap 
685*a6d42e7dSPeter Dunlap /*
686*a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini
687*a6d42e7dSPeter Dunlap  *
688*a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Get_Data' operational primitive.
689*a6d42e7dSPeter Dunlap  *
690*a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
691*a6d42e7dSPeter Dunlap  * Datamover layer to retrieve certain data identified by the R2T PDU from the
692*a6d42e7dSPeter Dunlap  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
693*a6d42e7dSPeter Dunlap  * mapped to the respective buffer by the task tags (ITT & TTT).
694*a6d42e7dSPeter Dunlap  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
695*a6d42e7dSPeter Dunlap  * the callback (idb->idb_buf_cb) notification for data transfer completion are
696*a6d42e7dSPeter Dunlap  * are provided as input.
697*a6d42e7dSPeter Dunlap  *
698*a6d42e7dSPeter Dunlap  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
699*a6d42e7dSPeter Dunlap  * Datamover layer, the local and remote Datamover layers transparently bring
700*a6d42e7dSPeter Dunlap  * about the data transfer requested by the R2T PDU, without the participation
701*a6d42e7dSPeter Dunlap  * of the iSCSI layers.
702*a6d42e7dSPeter Dunlap  *
703*a6d42e7dSPeter Dunlap  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
704*a6d42e7dSPeter Dunlap  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
705*a6d42e7dSPeter Dunlap  *
706*a6d42e7dSPeter Dunlap  */
707*a6d42e7dSPeter Dunlap idm_status_t
708*a6d42e7dSPeter Dunlap idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
709*a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
710*a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
711*a6d42e7dSPeter Dunlap {
712*a6d42e7dSPeter Dunlap 	idm_status_t rc;
713*a6d42e7dSPeter Dunlap 
714*a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
715*a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
716*a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
717*a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
718*a6d42e7dSPeter Dunlap 
719*a6d42e7dSPeter Dunlap 	/*
720*a6d42e7dSPeter Dunlap 	 * "In" buf list is for "Data In" PDU's, "Out" buf list is for
721*a6d42e7dSPeter Dunlap 	 * "Data Out" PDU's
722*a6d42e7dSPeter Dunlap 	 */
723*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
724*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
725*a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
726*a6d42e7dSPeter Dunlap 		idt->idt_rx_from_ini_start++;
727*a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
728*a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
729*a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
730*a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
731*a6d42e7dSPeter Dunlap 		    (idt, idb);
732*a6d42e7dSPeter Dunlap 		return (rc);
733*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
734*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
735*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
736*a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
737*a6d42e7dSPeter Dunlap 		/*
738*a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
739*a6d42e7dSPeter Dunlap 		 * is suspended
740*a6d42e7dSPeter Dunlap 		 */
741*a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
742*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
743*a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
744*a6d42e7dSPeter Dunlap 	default:
745*a6d42e7dSPeter Dunlap 		ASSERT(0);
746*a6d42e7dSPeter Dunlap 		break;
747*a6d42e7dSPeter Dunlap 	}
748*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
749*a6d42e7dSPeter Dunlap 
750*a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
751*a6d42e7dSPeter Dunlap }
752*a6d42e7dSPeter Dunlap 
753*a6d42e7dSPeter Dunlap /*
754*a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini_done
755*a6d42e7dSPeter Dunlap  *
756*a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
757*a6d42e7dSPeter Dunlap  * a call to transport_buf_tx_to_ini
758*a6d42e7dSPeter Dunlap  *
759*a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
760*a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
761*a6d42e7dSPeter Dunlap  */
762*a6d42e7dSPeter Dunlap void
763*a6d42e7dSPeter Dunlap idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
764*a6d42e7dSPeter Dunlap {
765*a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
766*a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
767*a6d42e7dSPeter Dunlap 	idb->idb_tx_thread = B_FALSE;
768*a6d42e7dSPeter Dunlap 	idt->idt_tx_to_ini_done++;
769*a6d42e7dSPeter Dunlap 
770*a6d42e7dSPeter Dunlap 	/*
771*a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
772*a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
773*a6d42e7dSPeter Dunlap 	 * to 0.
774*a6d42e7dSPeter Dunlap 	 */
775*a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
776*a6d42e7dSPeter Dunlap 	idb->idb_status = status;
777*a6d42e7dSPeter Dunlap 
778*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
779*a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
780*a6d42e7dSPeter Dunlap 		idm_buf_unbind_in_locked(idt, idb);
781*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
782*a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
783*a6d42e7dSPeter Dunlap 		return;
784*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
785*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
786*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
787*a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
788*a6d42e7dSPeter Dunlap 		/*
789*a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
790*a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
791*a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
792*a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
793*a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
794*a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
795*a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
796*a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
797*a6d42e7dSPeter Dunlap 		 *
798*a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
799*a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
800*a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
801*a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want
802*a6d42e7dSPeter Dunlap 		 * the client to here from us again after the call to
803*a6d42e7dSPeter Dunlap 		 * client_task_aborted() but we don't want to give it a bunch
804*a6d42e7dSPeter Dunlap 		 * of failed buffer transfers until we've called
805*a6d42e7dSPeter Dunlap 		 * client_task_aborted().  Instead we'll just leave all the
806*a6d42e7dSPeter Dunlap 		 * buffers bound and allow the client to cleanup.
807*a6d42e7dSPeter Dunlap 		 */
808*a6d42e7dSPeter Dunlap 		break;
809*a6d42e7dSPeter Dunlap 	default:
810*a6d42e7dSPeter Dunlap 		ASSERT(0);
811*a6d42e7dSPeter Dunlap 	}
812*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
813*a6d42e7dSPeter Dunlap }
814*a6d42e7dSPeter Dunlap 
815*a6d42e7dSPeter Dunlap /*
816*a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini_done
817*a6d42e7dSPeter Dunlap  *
818*a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
819*a6d42e7dSPeter Dunlap  * a call totransport_buf_tx_to_ini
820*a6d42e7dSPeter Dunlap  *
821*a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
822*a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
823*a6d42e7dSPeter Dunlap  */
824*a6d42e7dSPeter Dunlap void
825*a6d42e7dSPeter Dunlap idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
826*a6d42e7dSPeter Dunlap {
827*a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
828*a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
829*a6d42e7dSPeter Dunlap 	idt->idt_rx_from_ini_done++;
830*a6d42e7dSPeter Dunlap 
831*a6d42e7dSPeter Dunlap 	/*
832*a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
833*a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
834*a6d42e7dSPeter Dunlap 	 * to 0.
835*a6d42e7dSPeter Dunlap 	 */
836*a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
837*a6d42e7dSPeter Dunlap 	idb->idb_status = status;
838*a6d42e7dSPeter Dunlap 
839*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
840*a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
841*a6d42e7dSPeter Dunlap 		idm_buf_unbind_out_locked(idt, idb);
842*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
843*a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
844*a6d42e7dSPeter Dunlap 		return;
845*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
846*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
847*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
848*a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
849*a6d42e7dSPeter Dunlap 		/*
850*a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
851*a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
852*a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
853*a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
854*a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
855*a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
856*a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
857*a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
858*a6d42e7dSPeter Dunlap 		 *
859*a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
860*a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
861*a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
862*a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want
863*a6d42e7dSPeter Dunlap 		 * the client to here from us again after the call to
864*a6d42e7dSPeter Dunlap 		 * client_task_aborted() but we don't want to give it a bunch
865*a6d42e7dSPeter Dunlap 		 * of failed buffer transfers until we've called
866*a6d42e7dSPeter Dunlap 		 * client_task_aborted().  Instead we'll just leave all the
867*a6d42e7dSPeter Dunlap 		 * buffers bound and allow the client to cleanup.
868*a6d42e7dSPeter Dunlap 		 */
869*a6d42e7dSPeter Dunlap 		break;
870*a6d42e7dSPeter Dunlap 	default:
871*a6d42e7dSPeter Dunlap 		ASSERT(0);
872*a6d42e7dSPeter Dunlap 	}
873*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
874*a6d42e7dSPeter Dunlap }
875*a6d42e7dSPeter Dunlap 
876*a6d42e7dSPeter Dunlap /*
877*a6d42e7dSPeter Dunlap  * idm_buf_alloc
878*a6d42e7dSPeter Dunlap  *
879*a6d42e7dSPeter Dunlap  * Allocates a buffer handle and registers it for use with the transport
880*a6d42e7dSPeter Dunlap  * layer. If a buffer is not passed on bufptr, the buffer will be allocated
881*a6d42e7dSPeter Dunlap  * as well as the handle.
882*a6d42e7dSPeter Dunlap  *
883*a6d42e7dSPeter Dunlap  * ic		- connection on which the buffer will be transferred
884*a6d42e7dSPeter Dunlap  * bufptr	- allocate memory for buffer if NULL, else assign to buffer
885*a6d42e7dSPeter Dunlap  * buflen	- length of buffer
886*a6d42e7dSPeter Dunlap  *
887*a6d42e7dSPeter Dunlap  * Returns idm_buf_t handle if successful, otherwise NULL
888*a6d42e7dSPeter Dunlap  */
889*a6d42e7dSPeter Dunlap idm_buf_t *
890*a6d42e7dSPeter Dunlap idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
891*a6d42e7dSPeter Dunlap {
892*a6d42e7dSPeter Dunlap 	idm_buf_t	*buf = NULL;
893*a6d42e7dSPeter Dunlap 	int		rc;
894*a6d42e7dSPeter Dunlap 
895*a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
896*a6d42e7dSPeter Dunlap 	ASSERT(idm.idm_buf_cache != NULL);
897*a6d42e7dSPeter Dunlap 	ASSERT(buflen > 0);
898*a6d42e7dSPeter Dunlap 
899*a6d42e7dSPeter Dunlap 	/* Don't allocate new buffers if we are not in FFP */
900*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
901*a6d42e7dSPeter Dunlap 	if (!ic->ic_ffp) {
902*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
903*a6d42e7dSPeter Dunlap 		return (NULL);
904*a6d42e7dSPeter Dunlap 	}
905*a6d42e7dSPeter Dunlap 
906*a6d42e7dSPeter Dunlap 
907*a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
908*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
909*a6d42e7dSPeter Dunlap 
910*a6d42e7dSPeter Dunlap 	buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP);
911*a6d42e7dSPeter Dunlap 	if (buf == NULL) {
912*a6d42e7dSPeter Dunlap 		idm_conn_rele(ic);
913*a6d42e7dSPeter Dunlap 		return (NULL);
914*a6d42e7dSPeter Dunlap 	}
915*a6d42e7dSPeter Dunlap 
916*a6d42e7dSPeter Dunlap 	buf->idb_ic		= ic;
917*a6d42e7dSPeter Dunlap 	buf->idb_buflen		= buflen;
918*a6d42e7dSPeter Dunlap 	buf->idb_exp_offset	= 0;
919*a6d42e7dSPeter Dunlap 	buf->idb_bufoffset	= 0;
920*a6d42e7dSPeter Dunlap 	buf->idb_xfer_len 	= 0;
921*a6d42e7dSPeter Dunlap 	buf->idb_magic		= IDM_BUF_MAGIC;
922*a6d42e7dSPeter Dunlap 
923*a6d42e7dSPeter Dunlap 	/*
924*a6d42e7dSPeter Dunlap 	 * If bufptr is NULL, we have an implicit request to allocate
925*a6d42e7dSPeter Dunlap 	 * memory for this IDM buffer handle and register it for use
926*a6d42e7dSPeter Dunlap 	 * with the transport. To simplify this, and to give more freedom
927*a6d42e7dSPeter Dunlap 	 * to the transport layer for it's own buffer management, both of
928*a6d42e7dSPeter Dunlap 	 * these actions will take place in the transport layer.
929*a6d42e7dSPeter Dunlap 	 * If bufptr is set, then the caller has allocated memory (or more
930*a6d42e7dSPeter Dunlap 	 * likely it's been passed from an upper layer), and we need only
931*a6d42e7dSPeter Dunlap 	 * register the buffer for use with the transport layer.
932*a6d42e7dSPeter Dunlap 	 */
933*a6d42e7dSPeter Dunlap 	if (bufptr == NULL) {
934*a6d42e7dSPeter Dunlap 		/*
935*a6d42e7dSPeter Dunlap 		 * Allocate a buffer from the transport layer (which
936*a6d42e7dSPeter Dunlap 		 * will also register the buffer for use).
937*a6d42e7dSPeter Dunlap 		 */
938*a6d42e7dSPeter Dunlap 		rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen);
939*a6d42e7dSPeter Dunlap 		if (rc != 0) {
940*a6d42e7dSPeter Dunlap 			idm_conn_rele(ic);
941*a6d42e7dSPeter Dunlap 			kmem_cache_free(idm.idm_buf_cache, buf);
942*a6d42e7dSPeter Dunlap 			return (NULL);
943*a6d42e7dSPeter Dunlap 		}
944*a6d42e7dSPeter Dunlap 		/* Set the bufalloc'd flag */
945*a6d42e7dSPeter Dunlap 		buf->idb_bufalloc = B_TRUE;
946*a6d42e7dSPeter Dunlap 	} else {
947*a6d42e7dSPeter Dunlap 		/*
948*a6d42e7dSPeter Dunlap 		 * Set the passed bufptr into the buf handle, and
949*a6d42e7dSPeter Dunlap 		 * register the handle with the transport layer.
950*a6d42e7dSPeter Dunlap 		 */
951*a6d42e7dSPeter Dunlap 		buf->idb_buf = bufptr;
952*a6d42e7dSPeter Dunlap 
953*a6d42e7dSPeter Dunlap 		rc = ic->ic_transport_ops->it_buf_setup(buf);
954*a6d42e7dSPeter Dunlap 		if (rc != 0) {
955*a6d42e7dSPeter Dunlap 			idm_conn_rele(ic);
956*a6d42e7dSPeter Dunlap 			kmem_cache_free(idm.idm_buf_cache, buf);
957*a6d42e7dSPeter Dunlap 			return (NULL);
958*a6d42e7dSPeter Dunlap 		}
959*a6d42e7dSPeter Dunlap 		/* Ensure bufalloc'd flag is unset */
960*a6d42e7dSPeter Dunlap 		buf->idb_bufalloc = B_FALSE;
961*a6d42e7dSPeter Dunlap 	}
962*a6d42e7dSPeter Dunlap 
963*a6d42e7dSPeter Dunlap 	return (buf);
964*a6d42e7dSPeter Dunlap 
965*a6d42e7dSPeter Dunlap }
966*a6d42e7dSPeter Dunlap 
967*a6d42e7dSPeter Dunlap /*
968*a6d42e7dSPeter Dunlap  * idm_buf_free
969*a6d42e7dSPeter Dunlap  *
970*a6d42e7dSPeter Dunlap  * Release a buffer handle along with the associated buffer that was allocated
971*a6d42e7dSPeter Dunlap  * or assigned with idm_buf_alloc
972*a6d42e7dSPeter Dunlap  */
973*a6d42e7dSPeter Dunlap void
974*a6d42e7dSPeter Dunlap idm_buf_free(idm_buf_t *buf)
975*a6d42e7dSPeter Dunlap {
976*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = buf->idb_ic;
977*a6d42e7dSPeter Dunlap 
978*a6d42e7dSPeter Dunlap 
979*a6d42e7dSPeter Dunlap 	buf->idb_task_binding	= NULL;
980*a6d42e7dSPeter Dunlap 
981*a6d42e7dSPeter Dunlap 	if (buf->idb_bufalloc) {
982*a6d42e7dSPeter Dunlap 		ic->ic_transport_ops->it_buf_free(buf);
983*a6d42e7dSPeter Dunlap 	} else {
984*a6d42e7dSPeter Dunlap 		ic->ic_transport_ops->it_buf_teardown(buf);
985*a6d42e7dSPeter Dunlap 	}
986*a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_buf_cache, buf);
987*a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
988*a6d42e7dSPeter Dunlap }
989*a6d42e7dSPeter Dunlap 
990*a6d42e7dSPeter Dunlap /*
991*a6d42e7dSPeter Dunlap  * idm_buf_bind_in
992*a6d42e7dSPeter Dunlap  *
993*a6d42e7dSPeter Dunlap  * This function associates a buffer with a task. This is only for use by the
994*a6d42e7dSPeter Dunlap  * iSCSI initiator that will have only one buffer per transfer direction
995*a6d42e7dSPeter Dunlap  *
996*a6d42e7dSPeter Dunlap  */
997*a6d42e7dSPeter Dunlap void
998*a6d42e7dSPeter Dunlap idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
999*a6d42e7dSPeter Dunlap {
1000*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1001*a6d42e7dSPeter Dunlap 	idm_buf_bind_in_locked(idt, buf);
1002*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1003*a6d42e7dSPeter Dunlap }
1004*a6d42e7dSPeter Dunlap 
1005*a6d42e7dSPeter Dunlap static void
1006*a6d42e7dSPeter Dunlap idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf)
1007*a6d42e7dSPeter Dunlap {
1008*a6d42e7dSPeter Dunlap 	buf->idb_task_binding = idt;
1009*a6d42e7dSPeter Dunlap 	buf->idb_ic = idt->idt_ic;
1010*a6d42e7dSPeter Dunlap 	idm_listbuf_insert(&idt->idt_inbufv, buf);
1011*a6d42e7dSPeter Dunlap }
1012*a6d42e7dSPeter Dunlap 
1013*a6d42e7dSPeter Dunlap void
1014*a6d42e7dSPeter Dunlap idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
1015*a6d42e7dSPeter Dunlap {
1016*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1017*a6d42e7dSPeter Dunlap 	idm_buf_bind_out_locked(idt, buf);
1018*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1019*a6d42e7dSPeter Dunlap }
1020*a6d42e7dSPeter Dunlap 
1021*a6d42e7dSPeter Dunlap static void
1022*a6d42e7dSPeter Dunlap idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf)
1023*a6d42e7dSPeter Dunlap {
1024*a6d42e7dSPeter Dunlap 	buf->idb_task_binding = idt;
1025*a6d42e7dSPeter Dunlap 	buf->idb_ic = idt->idt_ic;
1026*a6d42e7dSPeter Dunlap 	idm_listbuf_insert(&idt->idt_outbufv, buf);
1027*a6d42e7dSPeter Dunlap }
1028*a6d42e7dSPeter Dunlap 
1029*a6d42e7dSPeter Dunlap void
1030*a6d42e7dSPeter Dunlap idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
1031*a6d42e7dSPeter Dunlap {
1032*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1033*a6d42e7dSPeter Dunlap 	idm_buf_unbind_in_locked(idt, buf);
1034*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1035*a6d42e7dSPeter Dunlap }
1036*a6d42e7dSPeter Dunlap 
1037*a6d42e7dSPeter Dunlap static void
1038*a6d42e7dSPeter Dunlap idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf)
1039*a6d42e7dSPeter Dunlap {
1040*a6d42e7dSPeter Dunlap 	list_remove(&idt->idt_inbufv, buf);
1041*a6d42e7dSPeter Dunlap }
1042*a6d42e7dSPeter Dunlap 
1043*a6d42e7dSPeter Dunlap void
1044*a6d42e7dSPeter Dunlap idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
1045*a6d42e7dSPeter Dunlap {
1046*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1047*a6d42e7dSPeter Dunlap 	idm_buf_unbind_out_locked(idt, buf);
1048*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1049*a6d42e7dSPeter Dunlap }
1050*a6d42e7dSPeter Dunlap 
1051*a6d42e7dSPeter Dunlap static void
1052*a6d42e7dSPeter Dunlap idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf)
1053*a6d42e7dSPeter Dunlap {
1054*a6d42e7dSPeter Dunlap 	list_remove(&idt->idt_outbufv, buf);
1055*a6d42e7dSPeter Dunlap }
1056*a6d42e7dSPeter Dunlap 
1057*a6d42e7dSPeter Dunlap /*
1058*a6d42e7dSPeter Dunlap  * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the
1059*a6d42e7dSPeter Dunlap  * iSCSI PDU
1060*a6d42e7dSPeter Dunlap  */
1061*a6d42e7dSPeter Dunlap idm_buf_t *
1062*a6d42e7dSPeter Dunlap idm_buf_find(void *lbuf, size_t data_offset)
1063*a6d42e7dSPeter Dunlap {
1064*a6d42e7dSPeter Dunlap 	idm_buf_t	*idb;
1065*a6d42e7dSPeter Dunlap 	list_t		*lst = (list_t *)lbuf;
1066*a6d42e7dSPeter Dunlap 
1067*a6d42e7dSPeter Dunlap 	/* iterate through the list to find the buffer */
1068*a6d42e7dSPeter Dunlap 	for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
1069*a6d42e7dSPeter Dunlap 
1070*a6d42e7dSPeter Dunlap 		ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) ||
1071*a6d42e7dSPeter Dunlap 		    (idb->idb_bufoffset == 0));
1072*a6d42e7dSPeter Dunlap 
1073*a6d42e7dSPeter Dunlap 		if ((data_offset >= idb->idb_bufoffset) &&
1074*a6d42e7dSPeter Dunlap 		    (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
1075*a6d42e7dSPeter Dunlap 
1076*a6d42e7dSPeter Dunlap 			return (idb);
1077*a6d42e7dSPeter Dunlap 		}
1078*a6d42e7dSPeter Dunlap 	}
1079*a6d42e7dSPeter Dunlap 
1080*a6d42e7dSPeter Dunlap 	return (NULL);
1081*a6d42e7dSPeter Dunlap }
1082*a6d42e7dSPeter Dunlap 
1083*a6d42e7dSPeter Dunlap /*
1084*a6d42e7dSPeter Dunlap  * idm_task_alloc
1085*a6d42e7dSPeter Dunlap  *
1086*a6d42e7dSPeter Dunlap  * This function will allocate a idm_task_t structure. A task tag is also
1087*a6d42e7dSPeter Dunlap  * generated and saved in idt_tt. The task is not active.
1088*a6d42e7dSPeter Dunlap  */
1089*a6d42e7dSPeter Dunlap idm_task_t *
1090*a6d42e7dSPeter Dunlap idm_task_alloc(idm_conn_t *ic)
1091*a6d42e7dSPeter Dunlap {
1092*a6d42e7dSPeter Dunlap 	idm_task_t	*idt;
1093*a6d42e7dSPeter Dunlap 
1094*a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1095*a6d42e7dSPeter Dunlap 
1096*a6d42e7dSPeter Dunlap 	/* Don't allocate new tasks if we are not in FFP */
1097*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1098*a6d42e7dSPeter Dunlap 	if (!ic->ic_ffp) {
1099*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1100*a6d42e7dSPeter Dunlap 		return (NULL);
1101*a6d42e7dSPeter Dunlap 	}
1102*a6d42e7dSPeter Dunlap 	idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP);
1103*a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1104*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1105*a6d42e7dSPeter Dunlap 		return (NULL);
1106*a6d42e7dSPeter Dunlap 	}
1107*a6d42e7dSPeter Dunlap 
1108*a6d42e7dSPeter Dunlap 	ASSERT(list_is_empty(&idt->idt_inbufv));
1109*a6d42e7dSPeter Dunlap 	ASSERT(list_is_empty(&idt->idt_outbufv));
1110*a6d42e7dSPeter Dunlap 
1111*a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
1112*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1113*a6d42e7dSPeter Dunlap 
1114*a6d42e7dSPeter Dunlap 	idt->idt_state		= TASK_IDLE;
1115*a6d42e7dSPeter Dunlap 	idt->idt_ic		= ic;
1116*a6d42e7dSPeter Dunlap 	idt->idt_private 	= NULL;
1117*a6d42e7dSPeter Dunlap 	idt->idt_exp_datasn	= 0;
1118*a6d42e7dSPeter Dunlap 	idt->idt_exp_rttsn	= 0;
1119*a6d42e7dSPeter Dunlap 
1120*a6d42e7dSPeter Dunlap 	return (idt);
1121*a6d42e7dSPeter Dunlap }
1122*a6d42e7dSPeter Dunlap 
1123*a6d42e7dSPeter Dunlap /*
1124*a6d42e7dSPeter Dunlap  * idm_task_start
1125*a6d42e7dSPeter Dunlap  *
1126*a6d42e7dSPeter Dunlap  * Add the task to an AVL tree to notify IDM about a new task. The caller
1127*a6d42e7dSPeter Dunlap  * sets up the idm_task_t structure with a prior call to idm_task_alloc().
1128*a6d42e7dSPeter Dunlap  * The task service does not function as a task/work engine, it is the
1129*a6d42e7dSPeter Dunlap  * responsibility of the initiator to start the data transfer and free the
1130*a6d42e7dSPeter Dunlap  * resources.
1131*a6d42e7dSPeter Dunlap  */
1132*a6d42e7dSPeter Dunlap void
1133*a6d42e7dSPeter Dunlap idm_task_start(idm_task_t *idt, uintptr_t handle)
1134*a6d42e7dSPeter Dunlap {
1135*a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
1136*a6d42e7dSPeter Dunlap 
1137*a6d42e7dSPeter Dunlap 	/* mark the task as ACTIVE */
1138*a6d42e7dSPeter Dunlap 	idt->idt_state = TASK_ACTIVE;
1139*a6d42e7dSPeter Dunlap 	idt->idt_client_handle = handle;
1140*a6d42e7dSPeter Dunlap 	idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
1141*a6d42e7dSPeter Dunlap 	    idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 0;
1142*a6d42e7dSPeter Dunlap }
1143*a6d42e7dSPeter Dunlap 
1144*a6d42e7dSPeter Dunlap /*
1145*a6d42e7dSPeter Dunlap  * idm_task_done
1146*a6d42e7dSPeter Dunlap  *
1147*a6d42e7dSPeter Dunlap  * This function will remove the task from the AVL tree indicating that the
1148*a6d42e7dSPeter Dunlap  * task is no longer active.
1149*a6d42e7dSPeter Dunlap  */
1150*a6d42e7dSPeter Dunlap void
1151*a6d42e7dSPeter Dunlap idm_task_done(idm_task_t *idt)
1152*a6d42e7dSPeter Dunlap {
1153*a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
1154*a6d42e7dSPeter Dunlap 	ASSERT(idt->idt_refcnt.ir_refcnt == 0);
1155*a6d42e7dSPeter Dunlap 
1156*a6d42e7dSPeter Dunlap 	idt->idt_state = TASK_IDLE;
1157*a6d42e7dSPeter Dunlap 	idm_refcnt_reset(&idt->idt_refcnt);
1158*a6d42e7dSPeter Dunlap }
1159*a6d42e7dSPeter Dunlap 
1160*a6d42e7dSPeter Dunlap /*
1161*a6d42e7dSPeter Dunlap  * idm_task_free
1162*a6d42e7dSPeter Dunlap  *
1163*a6d42e7dSPeter Dunlap  * This function will free the Task Tag and the memory allocated for the task
1164*a6d42e7dSPeter Dunlap  * idm_task_done should be called prior to this call
1165*a6d42e7dSPeter Dunlap  */
1166*a6d42e7dSPeter Dunlap void
1167*a6d42e7dSPeter Dunlap idm_task_free(idm_task_t *idt)
1168*a6d42e7dSPeter Dunlap {
1169*a6d42e7dSPeter Dunlap 	idm_conn_t *ic = idt->idt_ic;
1170*a6d42e7dSPeter Dunlap 
1171*a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
1172*a6d42e7dSPeter Dunlap 	ASSERT(idt->idt_state == TASK_IDLE);
1173*a6d42e7dSPeter Dunlap 
1174*a6d42e7dSPeter Dunlap 	/*
1175*a6d42e7dSPeter Dunlap 	 * It's possible for items to still be in the idt_inbufv list if
1176*a6d42e7dSPeter Dunlap 	 * they were added after idm_task_cleanup was called.  We rely on
1177*a6d42e7dSPeter Dunlap 	 * STMF to free all buffers associated with the task however STMF
1178*a6d42e7dSPeter Dunlap 	 * doesn't know that we have this reference to the buffers.
1179*a6d42e7dSPeter Dunlap 	 * Use list_create so that we don't end up with stale references
1180*a6d42e7dSPeter Dunlap 	 * to these buffers.
1181*a6d42e7dSPeter Dunlap 	 */
1182*a6d42e7dSPeter Dunlap 	list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
1183*a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1184*a6d42e7dSPeter Dunlap 	list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
1185*a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1186*a6d42e7dSPeter Dunlap 
1187*a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_task_cache, idt);
1188*a6d42e7dSPeter Dunlap 
1189*a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
1190*a6d42e7dSPeter Dunlap }
1191*a6d42e7dSPeter Dunlap 
1192*a6d42e7dSPeter Dunlap /*
1193*a6d42e7dSPeter Dunlap  * idm_task_find
1194*a6d42e7dSPeter Dunlap  *
1195*a6d42e7dSPeter Dunlap  * This function looks up a task by task tag
1196*a6d42e7dSPeter Dunlap  */
1197*a6d42e7dSPeter Dunlap /*ARGSUSED*/
1198*a6d42e7dSPeter Dunlap idm_task_t *
1199*a6d42e7dSPeter Dunlap idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
1200*a6d42e7dSPeter Dunlap {
1201*a6d42e7dSPeter Dunlap 	uint32_t	tt, client_handle;
1202*a6d42e7dSPeter Dunlap 	idm_task_t	*idt;
1203*a6d42e7dSPeter Dunlap 
1204*a6d42e7dSPeter Dunlap 	/*
1205*a6d42e7dSPeter Dunlap 	 * Must match both itt and ttt.  The table is indexed by itt
1206*a6d42e7dSPeter Dunlap 	 * for initiator connections and ttt for target connections.
1207*a6d42e7dSPeter Dunlap 	 */
1208*a6d42e7dSPeter Dunlap 	if (IDM_CONN_ISTGT(ic)) {
1209*a6d42e7dSPeter Dunlap 		tt = ttt;
1210*a6d42e7dSPeter Dunlap 		client_handle = itt;
1211*a6d42e7dSPeter Dunlap 	} else {
1212*a6d42e7dSPeter Dunlap 		tt = itt;
1213*a6d42e7dSPeter Dunlap 		client_handle = ttt;
1214*a6d42e7dSPeter Dunlap 	}
1215*a6d42e7dSPeter Dunlap 
1216*a6d42e7dSPeter Dunlap 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1217*a6d42e7dSPeter Dunlap 	if (tt >= idm.idm_taskid_max) {
1218*a6d42e7dSPeter Dunlap 		rw_exit(&idm.idm_taskid_table_lock);
1219*a6d42e7dSPeter Dunlap 		return (NULL);
1220*a6d42e7dSPeter Dunlap 	}
1221*a6d42e7dSPeter Dunlap 
1222*a6d42e7dSPeter Dunlap 	idt = idm.idm_taskid_table[tt];
1223*a6d42e7dSPeter Dunlap 
1224*a6d42e7dSPeter Dunlap 	if (idt != NULL) {
1225*a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1226*a6d42e7dSPeter Dunlap 		if ((idt->idt_state != TASK_ACTIVE) ||
1227*a6d42e7dSPeter Dunlap 		    (IDM_CONN_ISTGT(ic) &&
1228*a6d42e7dSPeter Dunlap 		    (idt->idt_client_handle != client_handle))) {
1229*a6d42e7dSPeter Dunlap 			/*
1230*a6d42e7dSPeter Dunlap 			 * Task is aborting, we don't want any more references.
1231*a6d42e7dSPeter Dunlap 			 */
1232*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1233*a6d42e7dSPeter Dunlap 			rw_exit(&idm.idm_taskid_table_lock);
1234*a6d42e7dSPeter Dunlap 			return (NULL);
1235*a6d42e7dSPeter Dunlap 		}
1236*a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
1237*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1238*a6d42e7dSPeter Dunlap 	}
1239*a6d42e7dSPeter Dunlap 	rw_exit(&idm.idm_taskid_table_lock);
1240*a6d42e7dSPeter Dunlap 
1241*a6d42e7dSPeter Dunlap 	return (idt);
1242*a6d42e7dSPeter Dunlap }
1243*a6d42e7dSPeter Dunlap 
1244*a6d42e7dSPeter Dunlap /*
1245*a6d42e7dSPeter Dunlap  * idm_task_find_by_handle
1246*a6d42e7dSPeter Dunlap  *
1247*a6d42e7dSPeter Dunlap  * This function looks up a task by the client-private idt_client_handle.
1248*a6d42e7dSPeter Dunlap  *
1249*a6d42e7dSPeter Dunlap  * This function should NEVER be called in the performance path.  It is
1250*a6d42e7dSPeter Dunlap  * intended strictly for error recovery/task management.
1251*a6d42e7dSPeter Dunlap  */
1252*a6d42e7dSPeter Dunlap /*ARGSUSED*/
1253*a6d42e7dSPeter Dunlap void *
1254*a6d42e7dSPeter Dunlap idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle)
1255*a6d42e7dSPeter Dunlap {
1256*a6d42e7dSPeter Dunlap 	idm_task_t	*idt = NULL;
1257*a6d42e7dSPeter Dunlap 	int		idx = 0;
1258*a6d42e7dSPeter Dunlap 
1259*a6d42e7dSPeter Dunlap 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1260*a6d42e7dSPeter Dunlap 
1261*a6d42e7dSPeter Dunlap 	for (idx = 0; idx < idm.idm_taskid_max; idx++) {
1262*a6d42e7dSPeter Dunlap 		idt = idm.idm_taskid_table[idx];
1263*a6d42e7dSPeter Dunlap 
1264*a6d42e7dSPeter Dunlap 		if (idt == NULL)
1265*a6d42e7dSPeter Dunlap 			continue;
1266*a6d42e7dSPeter Dunlap 
1267*a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1268*a6d42e7dSPeter Dunlap 
1269*a6d42e7dSPeter Dunlap 		if (idt->idt_state != TASK_ACTIVE) {
1270*a6d42e7dSPeter Dunlap 			/*
1271*a6d42e7dSPeter Dunlap 			 * Task is either in suspend, abort, or already
1272*a6d42e7dSPeter Dunlap 			 * complete.
1273*a6d42e7dSPeter Dunlap 			 */
1274*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1275*a6d42e7dSPeter Dunlap 			continue;
1276*a6d42e7dSPeter Dunlap 		}
1277*a6d42e7dSPeter Dunlap 
1278*a6d42e7dSPeter Dunlap 		if (idt->idt_client_handle == handle) {
1279*a6d42e7dSPeter Dunlap 			idm_task_hold(idt);
1280*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1281*a6d42e7dSPeter Dunlap 			break;
1282*a6d42e7dSPeter Dunlap 		}
1283*a6d42e7dSPeter Dunlap 
1284*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1285*a6d42e7dSPeter Dunlap 	}
1286*a6d42e7dSPeter Dunlap 
1287*a6d42e7dSPeter Dunlap 	rw_exit(&idm.idm_taskid_table_lock);
1288*a6d42e7dSPeter Dunlap 
1289*a6d42e7dSPeter Dunlap 	if ((idt == NULL) || (idx == idm.idm_taskid_max))
1290*a6d42e7dSPeter Dunlap 		return (NULL);
1291*a6d42e7dSPeter Dunlap 
1292*a6d42e7dSPeter Dunlap 	return (idt->idt_private);
1293*a6d42e7dSPeter Dunlap }
1294*a6d42e7dSPeter Dunlap 
1295*a6d42e7dSPeter Dunlap void
1296*a6d42e7dSPeter Dunlap idm_task_hold(idm_task_t *idt)
1297*a6d42e7dSPeter Dunlap {
1298*a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&idt->idt_refcnt);
1299*a6d42e7dSPeter Dunlap }
1300*a6d42e7dSPeter Dunlap 
1301*a6d42e7dSPeter Dunlap void
1302*a6d42e7dSPeter Dunlap idm_task_rele(idm_task_t *idt)
1303*a6d42e7dSPeter Dunlap {
1304*a6d42e7dSPeter Dunlap 	idm_refcnt_rele(&idt->idt_refcnt);
1305*a6d42e7dSPeter Dunlap }
1306*a6d42e7dSPeter Dunlap 
1307*a6d42e7dSPeter Dunlap void
1308*a6d42e7dSPeter Dunlap idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
1309*a6d42e7dSPeter Dunlap {
1310*a6d42e7dSPeter Dunlap 	idm_task_t	*task;
1311*a6d42e7dSPeter Dunlap 	int		idx;
1312*a6d42e7dSPeter Dunlap 
1313*a6d42e7dSPeter Dunlap 	/*
1314*a6d42e7dSPeter Dunlap 	 * Passing NULL as the task indicates that all tasks
1315*a6d42e7dSPeter Dunlap 	 * for this connection should be aborted.
1316*a6d42e7dSPeter Dunlap 	 */
1317*a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1318*a6d42e7dSPeter Dunlap 		/*
1319*a6d42e7dSPeter Dunlap 		 * Only the connection state machine should ask for
1320*a6d42e7dSPeter Dunlap 		 * all tasks to abort and this should never happen in FFP.
1321*a6d42e7dSPeter Dunlap 		 */
1322*a6d42e7dSPeter Dunlap 		ASSERT(!ic->ic_ffp);
1323*a6d42e7dSPeter Dunlap 		rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1324*a6d42e7dSPeter Dunlap 		for (idx = 0; idx < idm.idm_taskid_max; idx++) {
1325*a6d42e7dSPeter Dunlap 			task = idm.idm_taskid_table[idx];
1326*a6d42e7dSPeter Dunlap 			if (task && (task->idt_state != TASK_IDLE) &&
1327*a6d42e7dSPeter Dunlap 			    (task->idt_ic == ic)) {
1328*a6d42e7dSPeter Dunlap 				rw_exit(&idm.idm_taskid_table_lock);
1329*a6d42e7dSPeter Dunlap 				idm_task_abort_one(ic, task, abort_type);
1330*a6d42e7dSPeter Dunlap 				rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1331*a6d42e7dSPeter Dunlap 			}
1332*a6d42e7dSPeter Dunlap 		}
1333*a6d42e7dSPeter Dunlap 		rw_exit(&idm.idm_taskid_table_lock);
1334*a6d42e7dSPeter Dunlap 	} else {
1335*a6d42e7dSPeter Dunlap 		idm_task_abort_one(ic, idt, abort_type);
1336*a6d42e7dSPeter Dunlap 	}
1337*a6d42e7dSPeter Dunlap }
1338*a6d42e7dSPeter Dunlap 
1339*a6d42e7dSPeter Dunlap static void
1340*a6d42e7dSPeter Dunlap idm_task_abort_unref_cb(void *ref)
1341*a6d42e7dSPeter Dunlap {
1342*a6d42e7dSPeter Dunlap 	idm_task_t *idt = ref;
1343*a6d42e7dSPeter Dunlap 
1344*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1345*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
1346*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
1347*a6d42e7dSPeter Dunlap 		idt->idt_state = TASK_SUSPENDED;
1348*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1349*a6d42e7dSPeter Dunlap 		idm_task_aborted(idt, IDM_STATUS_SUSPENDED);
1350*a6d42e7dSPeter Dunlap 		return;
1351*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
1352*a6d42e7dSPeter Dunlap 		idt->idt_state = TASK_ABORTED;
1353*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1354*a6d42e7dSPeter Dunlap 		idm_task_aborted(idt, IDM_STATUS_ABORTED);
1355*a6d42e7dSPeter Dunlap 		return;
1356*a6d42e7dSPeter Dunlap 	default:
1357*a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1358*a6d42e7dSPeter Dunlap 		ASSERT(0);
1359*a6d42e7dSPeter Dunlap 		break;
1360*a6d42e7dSPeter Dunlap 	}
1361*a6d42e7dSPeter Dunlap }
1362*a6d42e7dSPeter Dunlap 
1363*a6d42e7dSPeter Dunlap static void
1364*a6d42e7dSPeter Dunlap idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
1365*a6d42e7dSPeter Dunlap {
1366*a6d42e7dSPeter Dunlap 	/* Caller must hold connection mutex */
1367*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1368*a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
1369*a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
1370*a6d42e7dSPeter Dunlap 		switch (abort_type) {
1371*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1372*a6d42e7dSPeter Dunlap 			/* Call transport to release any resources */
1373*a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_SUSPENDING;
1374*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1375*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_free_task_rsrc(idt);
1376*a6d42e7dSPeter Dunlap 
1377*a6d42e7dSPeter Dunlap 			/*
1378*a6d42e7dSPeter Dunlap 			 * Wait for outstanding references.  When all
1379*a6d42e7dSPeter Dunlap 			 * references are released the callback will call
1380*a6d42e7dSPeter Dunlap 			 * idm_task_aborted().
1381*a6d42e7dSPeter Dunlap 			 */
1382*a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1383*a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1384*a6d42e7dSPeter Dunlap 			return;
1385*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1386*a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1387*a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1388*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1389*a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_free_task_rsrc(idt);
1390*a6d42e7dSPeter Dunlap 
1391*a6d42e7dSPeter Dunlap 			/*
1392*a6d42e7dSPeter Dunlap 			 * Wait for outstanding references.  When all
1393*a6d42e7dSPeter Dunlap 			 * references are released the callback will call
1394*a6d42e7dSPeter Dunlap 			 * idm_task_aborted().
1395*a6d42e7dSPeter Dunlap 			 */
1396*a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1397*a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1398*a6d42e7dSPeter Dunlap 			return;
1399*a6d42e7dSPeter Dunlap 		default:
1400*a6d42e7dSPeter Dunlap 			ASSERT(0);
1401*a6d42e7dSPeter Dunlap 		}
1402*a6d42e7dSPeter Dunlap 		break;
1403*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
1404*a6d42e7dSPeter Dunlap 		/* Already called transport_free_task_rsrc(); */
1405*a6d42e7dSPeter Dunlap 		switch (abort_type) {
1406*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1407*a6d42e7dSPeter Dunlap 			/* Already doing it */
1408*a6d42e7dSPeter Dunlap 			break;
1409*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1410*a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1411*a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1412*a6d42e7dSPeter Dunlap 			break;
1413*a6d42e7dSPeter Dunlap 		default:
1414*a6d42e7dSPeter Dunlap 			ASSERT(0);
1415*a6d42e7dSPeter Dunlap 		}
1416*a6d42e7dSPeter Dunlap 		break;
1417*a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
1418*a6d42e7dSPeter Dunlap 		/* Already called transport_free_task_rsrc(); */
1419*a6d42e7dSPeter Dunlap 		switch (abort_type) {
1420*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1421*a6d42e7dSPeter Dunlap 			/* Already doing it */
1422*a6d42e7dSPeter Dunlap 			break;
1423*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1424*a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1425*a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1426*a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1427*a6d42e7dSPeter Dunlap 
1428*a6d42e7dSPeter Dunlap 			/*
1429*a6d42e7dSPeter Dunlap 			 * We could probably call idm_task_aborted directly
1430*a6d42e7dSPeter Dunlap 			 * here but we may be holding the conn lock. It's
1431*a6d42e7dSPeter Dunlap 			 * easier to just switch contexts.  Even though
1432*a6d42e7dSPeter Dunlap 			 * we shouldn't really have any references we'll
1433*a6d42e7dSPeter Dunlap 			 * set the state to TASK_ABORTING instead of
1434*a6d42e7dSPeter Dunlap 			 * TASK_ABORTED so we can use the same code path.
1435*a6d42e7dSPeter Dunlap 			 */
1436*a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1437*a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1438*a6d42e7dSPeter Dunlap 			return;
1439*a6d42e7dSPeter Dunlap 		default:
1440*a6d42e7dSPeter Dunlap 			ASSERT(0);
1441*a6d42e7dSPeter Dunlap 		}
1442*a6d42e7dSPeter Dunlap 		break;
1443*a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
1444*a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
1445*a6d42e7dSPeter Dunlap 		switch (abort_type) {
1446*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1447*a6d42e7dSPeter Dunlap 			/* We're already past this point... */
1448*a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1449*a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1450*a6d42e7dSPeter Dunlap 			/* Already doing it */
1451*a6d42e7dSPeter Dunlap 			break;
1452*a6d42e7dSPeter Dunlap 		default:
1453*a6d42e7dSPeter Dunlap 			ASSERT(0);
1454*a6d42e7dSPeter Dunlap 		}
1455*a6d42e7dSPeter Dunlap 		break;
1456*a6d42e7dSPeter Dunlap 	case TASK_COMPLETE:
1457*a6d42e7dSPeter Dunlap 		/*
1458*a6d42e7dSPeter Dunlap 		 * In this case, let it go.  The status has already been
1459*a6d42e7dSPeter Dunlap 		 * sent (which may or may not get successfully transmitted)
1460*a6d42e7dSPeter Dunlap 		 * and we don't want to end up in a race between completing
1461*a6d42e7dSPeter Dunlap 		 * the status PDU and marking the task suspended.
1462*a6d42e7dSPeter Dunlap 		 */
1463*a6d42e7dSPeter Dunlap 		break;
1464*a6d42e7dSPeter Dunlap 	default:
1465*a6d42e7dSPeter Dunlap 		ASSERT(0);
1466*a6d42e7dSPeter Dunlap 	}
1467*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1468*a6d42e7dSPeter Dunlap }
1469*a6d42e7dSPeter Dunlap 
1470*a6d42e7dSPeter Dunlap static void
1471*a6d42e7dSPeter Dunlap idm_task_aborted(idm_task_t *idt, idm_status_t status)
1472*a6d42e7dSPeter Dunlap {
1473*a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status);
1474*a6d42e7dSPeter Dunlap }
1475*a6d42e7dSPeter Dunlap 
1476*a6d42e7dSPeter Dunlap void
1477*a6d42e7dSPeter Dunlap idm_task_cleanup(idm_task_t *idt)
1478*a6d42e7dSPeter Dunlap {
1479*a6d42e7dSPeter Dunlap 	idm_buf_t *idb, *next_idb;
1480*a6d42e7dSPeter Dunlap 	list_t		tmp_buflist;
1481*a6d42e7dSPeter Dunlap 	ASSERT((idt->idt_state == TASK_SUSPENDED) ||
1482*a6d42e7dSPeter Dunlap 	    (idt->idt_state == TASK_ABORTED));
1483*a6d42e7dSPeter Dunlap 
1484*a6d42e7dSPeter Dunlap 	list_create(&tmp_buflist, sizeof (idm_buf_t),
1485*a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1486*a6d42e7dSPeter Dunlap 
1487*a6d42e7dSPeter Dunlap 	/*
1488*a6d42e7dSPeter Dunlap 	 * Remove all the buffers from the task and add them to a
1489*a6d42e7dSPeter Dunlap 	 * temporary local list -- we do this so that we can hold
1490*a6d42e7dSPeter Dunlap 	 * the task lock and prevent the task from going away if
1491*a6d42e7dSPeter Dunlap 	 * the client decides to call idm_task_done/idm_task_free.
1492*a6d42e7dSPeter Dunlap 	 * This could happen during abort in iscsit.
1493*a6d42e7dSPeter Dunlap 	 */
1494*a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1495*a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_inbufv);
1496*a6d42e7dSPeter Dunlap 	    idb != NULL;
1497*a6d42e7dSPeter Dunlap 	    idb = next_idb) {
1498*a6d42e7dSPeter Dunlap 		next_idb = list_next(&idt->idt_inbufv, idb);
1499*a6d42e7dSPeter Dunlap 		idm_buf_unbind_in_locked(idt, idb);
1500*a6d42e7dSPeter Dunlap 		list_insert_tail(&tmp_buflist, idb);
1501*a6d42e7dSPeter Dunlap 	}
1502*a6d42e7dSPeter Dunlap 
1503*a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_outbufv);
1504*a6d42e7dSPeter Dunlap 	    idb != NULL;
1505*a6d42e7dSPeter Dunlap 	    idb = next_idb) {
1506*a6d42e7dSPeter Dunlap 		next_idb = list_next(&idt->idt_outbufv, idb);
1507*a6d42e7dSPeter Dunlap 		idm_buf_unbind_out_locked(idt, idb);
1508*a6d42e7dSPeter Dunlap 		list_insert_tail(&tmp_buflist, idb);
1509*a6d42e7dSPeter Dunlap 	}
1510*a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1511*a6d42e7dSPeter Dunlap 
1512*a6d42e7dSPeter Dunlap 	for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) {
1513*a6d42e7dSPeter Dunlap 		next_idb = list_next(&tmp_buflist, idb);
1514*a6d42e7dSPeter Dunlap 		list_remove(&tmp_buflist, idb);
1515*a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED);
1516*a6d42e7dSPeter Dunlap 	}
1517*a6d42e7dSPeter Dunlap 	list_destroy(&tmp_buflist);
1518*a6d42e7dSPeter Dunlap }
1519*a6d42e7dSPeter Dunlap 
1520*a6d42e7dSPeter Dunlap 
1521*a6d42e7dSPeter Dunlap /*
1522*a6d42e7dSPeter Dunlap  * idm_pdu_tx
1523*a6d42e7dSPeter Dunlap  *
1524*a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Send_Control' operational primitive.
1525*a6d42e7dSPeter Dunlap  * This function is invoked by an initiator iSCSI layer requesting the transfer
1526*a6d42e7dSPeter Dunlap  * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
1527*a6d42e7dSPeter Dunlap  * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover
1528*a6d42e7dSPeter Dunlap  * layer to the peer iSCSI layer in the remote iSCSI node. The connection info
1529*a6d42e7dSPeter Dunlap  * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size
1530*a6d42e7dSPeter Dunlap  * are provided as input.
1531*a6d42e7dSPeter Dunlap  *
1532*a6d42e7dSPeter Dunlap  */
1533*a6d42e7dSPeter Dunlap void
1534*a6d42e7dSPeter Dunlap idm_pdu_tx(idm_pdu_t *pdu)
1535*a6d42e7dSPeter Dunlap {
1536*a6d42e7dSPeter Dunlap 	idm_conn_t		*ic = pdu->isp_ic;
1537*a6d42e7dSPeter Dunlap 	iscsi_async_evt_hdr_t	*async_evt;
1538*a6d42e7dSPeter Dunlap 
1539*a6d42e7dSPeter Dunlap 	/*
1540*a6d42e7dSPeter Dunlap 	 * If we are in full-featured mode then route SCSI-related
1541*a6d42e7dSPeter Dunlap 	 * commands to the appropriate function vector without checking
1542*a6d42e7dSPeter Dunlap 	 * the connection state.  We will only be in full-feature mode
1543*a6d42e7dSPeter Dunlap 	 * when we are in an acceptable state for SCSI PDU's.
1544*a6d42e7dSPeter Dunlap 	 *
1545*a6d42e7dSPeter Dunlap 	 * We also need to ensure that there are no PDU events outstanding
1546*a6d42e7dSPeter Dunlap 	 * on the state machine.  Any non-SCSI PDU's received in full-feature
1547*a6d42e7dSPeter Dunlap 	 * mode will result in PDU events and until these have been handled
1548*a6d42e7dSPeter Dunlap 	 * we need to route all PDU's through the state machine as PDU
1549*a6d42e7dSPeter Dunlap 	 * events to maintain ordering.
1550*a6d42e7dSPeter Dunlap 	 *
1551*a6d42e7dSPeter Dunlap 	 * Note that IDM cannot enter FFP mode until it processes in
1552*a6d42e7dSPeter Dunlap 	 * its state machine the last xmit of the login process.
1553*a6d42e7dSPeter Dunlap 	 * Hence, checking the IDM_PDU_LOGIN_TX flag here would be
1554*a6d42e7dSPeter Dunlap 	 * superfluous.
1555*a6d42e7dSPeter Dunlap 	 */
1556*a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1557*a6d42e7dSPeter Dunlap 	if (ic->ic_ffp && (ic->ic_pdu_events == 0)) {
1558*a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1559*a6d42e7dSPeter Dunlap 		switch (IDM_PDU_OPCODE(pdu)) {
1560*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_RSP:
1561*a6d42e7dSPeter Dunlap 			/* Target only */
1562*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1563*a6d42e7dSPeter Dunlap 			return;
1564*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_TASK_MGT_RSP:
1565*a6d42e7dSPeter Dunlap 			/* Target only */
1566*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1567*a6d42e7dSPeter Dunlap 			return;
1568*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_DATA_RSP:
1569*a6d42e7dSPeter Dunlap 			/* Target only */
1570*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1571*a6d42e7dSPeter Dunlap 			return;
1572*a6d42e7dSPeter Dunlap 		case ISCSI_OP_RTT_RSP:
1573*a6d42e7dSPeter Dunlap 			/* Target only */
1574*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1575*a6d42e7dSPeter Dunlap 			return;
1576*a6d42e7dSPeter Dunlap 		case ISCSI_OP_NOOP_IN:
1577*a6d42e7dSPeter Dunlap 			/* Target only */
1578*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1579*a6d42e7dSPeter Dunlap 			return;
1580*a6d42e7dSPeter Dunlap 		case ISCSI_OP_TEXT_RSP:
1581*a6d42e7dSPeter Dunlap 			/* Target only */
1582*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1583*a6d42e7dSPeter Dunlap 			return;
1584*a6d42e7dSPeter Dunlap 		case ISCSI_OP_TEXT_CMD:
1585*a6d42e7dSPeter Dunlap 		case ISCSI_OP_NOOP_OUT:
1586*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_CMD:
1587*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_DATA:
1588*a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_TASK_MGT_MSG:
1589*a6d42e7dSPeter Dunlap 			/* Initiator only */
1590*a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1591*a6d42e7dSPeter Dunlap 			return;
1592*a6d42e7dSPeter Dunlap 		default:
1593*a6d42e7dSPeter Dunlap 			break;
1594*a6d42e7dSPeter Dunlap 		}
1595*a6d42e7dSPeter Dunlap 
1596*a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
1597*a6d42e7dSPeter Dunlap 	}
1598*a6d42e7dSPeter Dunlap 
1599*a6d42e7dSPeter Dunlap 	/*
1600*a6d42e7dSPeter Dunlap 	 * Any PDU's processed outside of full-feature mode and non-SCSI
1601*a6d42e7dSPeter Dunlap 	 * PDU's in full-feature mode are handled by generating an
1602*a6d42e7dSPeter Dunlap 	 * event to the connection state machine.  The state machine
1603*a6d42e7dSPeter Dunlap 	 * will validate the PDU against the current state and either
1604*a6d42e7dSPeter Dunlap 	 * transmit the PDU if the opcode is allowed or handle an
1605*a6d42e7dSPeter Dunlap 	 * error if the PDU is not allowed.
1606*a6d42e7dSPeter Dunlap 	 *
1607*a6d42e7dSPeter Dunlap 	 * This code-path will also generate any events that are implied
1608*a6d42e7dSPeter Dunlap 	 * by the PDU opcode.  For example a "login response" with success
1609*a6d42e7dSPeter Dunlap 	 * status generates a CE_LOGOUT_SUCCESS_SND event.
1610*a6d42e7dSPeter Dunlap 	 */
1611*a6d42e7dSPeter Dunlap 	switch (IDM_PDU_OPCODE(pdu)) {
1612*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_CMD:
1613*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
1614*a6d42e7dSPeter Dunlap 		break;
1615*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_RSP:
1616*a6d42e7dSPeter Dunlap 		idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE);
1617*a6d42e7dSPeter Dunlap 		break;
1618*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_CMD:
1619*a6d42e7dSPeter Dunlap 		idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE);
1620*a6d42e7dSPeter Dunlap 		break;
1621*a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_RSP:
1622*a6d42e7dSPeter Dunlap 		idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE);
1623*a6d42e7dSPeter Dunlap 		break;
1624*a6d42e7dSPeter Dunlap 	case ISCSI_OP_ASYNC_EVENT:
1625*a6d42e7dSPeter Dunlap 		async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
1626*a6d42e7dSPeter Dunlap 		switch (async_evt->async_event) {
1627*a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
1628*a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
1629*a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1630*a6d42e7dSPeter Dunlap 			break;
1631*a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
1632*a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND,
1633*a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1634*a6d42e7dSPeter Dunlap 			break;
1635*a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
1636*a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND,
1637*a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1638*a6d42e7dSPeter Dunlap 			break;
1639*a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_SCSI_EVENT:
1640*a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
1641*a6d42e7dSPeter Dunlap 		default:
1642*a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_MISC_TX,
1643*a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1644*a6d42e7dSPeter Dunlap 			break;
1645*a6d42e7dSPeter Dunlap 		}
1646*a6d42e7dSPeter Dunlap 		break;
1647*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_RSP:
1648*a6d42e7dSPeter Dunlap 		/* Target only */
1649*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1650*a6d42e7dSPeter Dunlap 		break;
1651*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1652*a6d42e7dSPeter Dunlap 		/* Target only */
1653*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1654*a6d42e7dSPeter Dunlap 		break;
1655*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA_RSP:
1656*a6d42e7dSPeter Dunlap 		/* Target only */
1657*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1658*a6d42e7dSPeter Dunlap 		break;
1659*a6d42e7dSPeter Dunlap 	case ISCSI_OP_RTT_RSP:
1660*a6d42e7dSPeter Dunlap 		/* Target only */
1661*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1662*a6d42e7dSPeter Dunlap 		break;
1663*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_IN:
1664*a6d42e7dSPeter Dunlap 		/* Target only */
1665*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1666*a6d42e7dSPeter Dunlap 		break;
1667*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_RSP:
1668*a6d42e7dSPeter Dunlap 		/* Target only */
1669*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1670*a6d42e7dSPeter Dunlap 		break;
1671*a6d42e7dSPeter Dunlap 		/* Initiator only */
1672*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_CMD:
1673*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1674*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA:
1675*a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_OUT:
1676*a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_CMD:
1677*a6d42e7dSPeter Dunlap 	case ISCSI_OP_SNACK_CMD:
1678*a6d42e7dSPeter Dunlap 	case ISCSI_OP_REJECT_MSG:
1679*a6d42e7dSPeter Dunlap 	default:
1680*a6d42e7dSPeter Dunlap 		/*
1681*a6d42e7dSPeter Dunlap 		 * Connection state machine will validate these PDU's against
1682*a6d42e7dSPeter Dunlap 		 * the current state.  A PDU not allowed in the current
1683*a6d42e7dSPeter Dunlap 		 * state will cause a protocol error.
1684*a6d42e7dSPeter Dunlap 		 */
1685*a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1686*a6d42e7dSPeter Dunlap 		break;
1687*a6d42e7dSPeter Dunlap 	}
1688*a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1689*a6d42e7dSPeter Dunlap }
1690*a6d42e7dSPeter Dunlap 
1691*a6d42e7dSPeter Dunlap /*
1692*a6d42e7dSPeter Dunlap  * Allocates a PDU along with memory for header and data.
1693*a6d42e7dSPeter Dunlap  */
1694*a6d42e7dSPeter Dunlap 
1695*a6d42e7dSPeter Dunlap idm_pdu_t *
1696*a6d42e7dSPeter Dunlap idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
1697*a6d42e7dSPeter Dunlap {
1698*a6d42e7dSPeter Dunlap 	idm_pdu_t *result;
1699*a6d42e7dSPeter Dunlap 
1700*a6d42e7dSPeter Dunlap 	/*
1701*a6d42e7dSPeter Dunlap 	 * IDM clients should cache these structures for performance
1702*a6d42e7dSPeter Dunlap 	 * critical paths.  We can't cache effectively in IDM because we
1703*a6d42e7dSPeter Dunlap 	 * don't know the correct header and data size.
1704*a6d42e7dSPeter Dunlap 	 *
1705*a6d42e7dSPeter Dunlap 	 * Valid header length is assumed to be hdrlen and valid data
1706*a6d42e7dSPeter Dunlap 	 * length is assumed to be datalen.  isp_hdrlen and isp_datalen
1707*a6d42e7dSPeter Dunlap 	 * can be adjusted after the PDU is returned if necessary.
1708*a6d42e7dSPeter Dunlap 	 */
1709*a6d42e7dSPeter Dunlap 	result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, KM_SLEEP);
1710*a6d42e7dSPeter Dunlap 	result->isp_flags |= IDM_PDU_ALLOC; /* For idm_pdu_free sanity check */
1711*a6d42e7dSPeter Dunlap 	result->isp_hdr = (iscsi_hdr_t *)(result + 1); /* Ptr. Arithmetic */
1712*a6d42e7dSPeter Dunlap 	result->isp_hdrlen = hdrlen;
1713*a6d42e7dSPeter Dunlap 	result->isp_hdrbuflen = hdrlen;
1714*a6d42e7dSPeter Dunlap 	result->isp_transport_hdrlen = 0;
1715*a6d42e7dSPeter Dunlap 	result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
1716*a6d42e7dSPeter Dunlap 	result->isp_datalen = datalen;
1717*a6d42e7dSPeter Dunlap 	result->isp_databuflen = datalen;
1718*a6d42e7dSPeter Dunlap 	result->isp_magic = IDM_PDU_MAGIC;
1719*a6d42e7dSPeter Dunlap 
1720*a6d42e7dSPeter Dunlap 	return (result);
1721*a6d42e7dSPeter Dunlap }
1722*a6d42e7dSPeter Dunlap 
1723*a6d42e7dSPeter Dunlap /*
1724*a6d42e7dSPeter Dunlap  * Free a PDU previously allocated with idm_pdu_alloc() including any
1725*a6d42e7dSPeter Dunlap  * header and data space allocated as part of the original request.
1726*a6d42e7dSPeter Dunlap  * Additional memory regions referenced by subsequent modification of
1727*a6d42e7dSPeter Dunlap  * the isp_hdr and/or isp_data fields will not be freed.
1728*a6d42e7dSPeter Dunlap  */
1729*a6d42e7dSPeter Dunlap void
1730*a6d42e7dSPeter Dunlap idm_pdu_free(idm_pdu_t *pdu)
1731*a6d42e7dSPeter Dunlap {
1732*a6d42e7dSPeter Dunlap 	/* Make sure the structure was allocated using idm_pdu_alloc() */
1733*a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
1734*a6d42e7dSPeter Dunlap 	kmem_free(pdu,
1735*a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
1736*a6d42e7dSPeter Dunlap }
1737*a6d42e7dSPeter Dunlap 
1738*a6d42e7dSPeter Dunlap /*
1739*a6d42e7dSPeter Dunlap  * Initialize the connection, private and callback fields in a PDU.
1740*a6d42e7dSPeter Dunlap  */
1741*a6d42e7dSPeter Dunlap void
1742*a6d42e7dSPeter Dunlap idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
1743*a6d42e7dSPeter Dunlap {
1744*a6d42e7dSPeter Dunlap 	/*
1745*a6d42e7dSPeter Dunlap 	 * idm_pdu_complete() will call idm_pdu_free if the callback is
1746*a6d42e7dSPeter Dunlap 	 * NULL.  This will only work if the PDU was originally allocated
1747*a6d42e7dSPeter Dunlap 	 * with idm_pdu_alloc().
1748*a6d42e7dSPeter Dunlap 	 */
1749*a6d42e7dSPeter Dunlap 	ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
1750*a6d42e7dSPeter Dunlap 	    (cb != NULL));
1751*a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
1752*a6d42e7dSPeter Dunlap 	pdu->isp_ic = ic;
1753*a6d42e7dSPeter Dunlap 	pdu->isp_private = private;
1754*a6d42e7dSPeter Dunlap 	pdu->isp_callback = cb;
1755*a6d42e7dSPeter Dunlap }
1756*a6d42e7dSPeter Dunlap 
1757*a6d42e7dSPeter Dunlap /*
1758*a6d42e7dSPeter Dunlap  * Initialize the header and header length field.  This function should
1759*a6d42e7dSPeter Dunlap  * not be used to adjust the header length in a buffer allocated via
1760*a6d42e7dSPeter Dunlap  * pdu_pdu_alloc since it overwrites the existing header pointer.
1761*a6d42e7dSPeter Dunlap  */
1762*a6d42e7dSPeter Dunlap void
1763*a6d42e7dSPeter Dunlap idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
1764*a6d42e7dSPeter Dunlap {
1765*a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr);
1766*a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = hdrlen;
1767*a6d42e7dSPeter Dunlap }
1768*a6d42e7dSPeter Dunlap 
1769*a6d42e7dSPeter Dunlap /*
1770*a6d42e7dSPeter Dunlap  * Initialize the data and data length fields.  This function should
1771*a6d42e7dSPeter Dunlap  * not be used to adjust the data length of a buffer allocated via
1772*a6d42e7dSPeter Dunlap  * idm_pdu_alloc since it overwrites the existing data pointer.
1773*a6d42e7dSPeter Dunlap  */
1774*a6d42e7dSPeter Dunlap void
1775*a6d42e7dSPeter Dunlap idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
1776*a6d42e7dSPeter Dunlap {
1777*a6d42e7dSPeter Dunlap 	pdu->isp_data = data;
1778*a6d42e7dSPeter Dunlap 	pdu->isp_datalen = datalen;
1779*a6d42e7dSPeter Dunlap }
1780*a6d42e7dSPeter Dunlap 
1781*a6d42e7dSPeter Dunlap void
1782*a6d42e7dSPeter Dunlap idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
1783*a6d42e7dSPeter Dunlap {
1784*a6d42e7dSPeter Dunlap 	if (pdu->isp_callback) {
1785*a6d42e7dSPeter Dunlap 		pdu->isp_status = status;
1786*a6d42e7dSPeter Dunlap 		(*pdu->isp_callback)(pdu, status);
1787*a6d42e7dSPeter Dunlap 	} else {
1788*a6d42e7dSPeter Dunlap 		idm_pdu_free(pdu);
1789*a6d42e7dSPeter Dunlap 	}
1790*a6d42e7dSPeter Dunlap }
1791*a6d42e7dSPeter Dunlap 
1792*a6d42e7dSPeter Dunlap /*
1793*a6d42e7dSPeter Dunlap  * State machine auditing
1794*a6d42e7dSPeter Dunlap  */
1795*a6d42e7dSPeter Dunlap 
1796*a6d42e7dSPeter Dunlap void
1797*a6d42e7dSPeter Dunlap idm_sm_audit_init(sm_audit_buf_t *audit_buf)
1798*a6d42e7dSPeter Dunlap {
1799*a6d42e7dSPeter Dunlap 	bzero(audit_buf, sizeof (sm_audit_buf_t));
1800*a6d42e7dSPeter Dunlap 	audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1;
1801*a6d42e7dSPeter Dunlap }
1802*a6d42e7dSPeter Dunlap 
1803*a6d42e7dSPeter Dunlap static
1804*a6d42e7dSPeter Dunlap sm_audit_record_t *
1805*a6d42e7dSPeter Dunlap idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type,
1806*a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type,
1807*a6d42e7dSPeter Dunlap     int current_state)
1808*a6d42e7dSPeter Dunlap {
1809*a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
1810*a6d42e7dSPeter Dunlap 
1811*a6d42e7dSPeter Dunlap 	sar = audit_buf->sab_records;
1812*a6d42e7dSPeter Dunlap 	sar += audit_buf->sab_index;
1813*a6d42e7dSPeter Dunlap 	audit_buf->sab_index++;
1814*a6d42e7dSPeter Dunlap 	audit_buf->sab_index &= audit_buf->sab_max_index;
1815*a6d42e7dSPeter Dunlap 
1816*a6d42e7dSPeter Dunlap 	sar->sar_type = r_type;
1817*a6d42e7dSPeter Dunlap 	gethrestime(&sar->sar_timestamp);
1818*a6d42e7dSPeter Dunlap 	sar->sar_sm_type = sm_type;
1819*a6d42e7dSPeter Dunlap 	sar->sar_state = current_state;
1820*a6d42e7dSPeter Dunlap 
1821*a6d42e7dSPeter Dunlap 	return (sar);
1822*a6d42e7dSPeter Dunlap }
1823*a6d42e7dSPeter Dunlap 
1824*a6d42e7dSPeter Dunlap void
1825*a6d42e7dSPeter Dunlap idm_sm_audit_event(sm_audit_buf_t *audit_buf,
1826*a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type, int current_state,
1827*a6d42e7dSPeter Dunlap     int event, uintptr_t event_info)
1828*a6d42e7dSPeter Dunlap {
1829*a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
1830*a6d42e7dSPeter Dunlap 
1831*a6d42e7dSPeter Dunlap 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT,
1832*a6d42e7dSPeter Dunlap 	    sm_type, current_state);
1833*a6d42e7dSPeter Dunlap 	sar->sar_event = event;
1834*a6d42e7dSPeter Dunlap 	sar->sar_event_info = event_info;
1835*a6d42e7dSPeter Dunlap }
1836*a6d42e7dSPeter Dunlap 
1837*a6d42e7dSPeter Dunlap void
1838*a6d42e7dSPeter Dunlap idm_sm_audit_state_change(sm_audit_buf_t *audit_buf,
1839*a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type, int current_state, int new_state)
1840*a6d42e7dSPeter Dunlap {
1841*a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
1842*a6d42e7dSPeter Dunlap 
1843*a6d42e7dSPeter Dunlap 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE,
1844*a6d42e7dSPeter Dunlap 	    sm_type, current_state);
1845*a6d42e7dSPeter Dunlap 	sar->sar_new_state = new_state;
1846*a6d42e7dSPeter Dunlap }
1847*a6d42e7dSPeter Dunlap 
1848*a6d42e7dSPeter Dunlap 
1849*a6d42e7dSPeter Dunlap /*
1850*a6d42e7dSPeter Dunlap  * Object reference tracking
1851*a6d42e7dSPeter Dunlap  */
1852*a6d42e7dSPeter Dunlap 
1853*a6d42e7dSPeter Dunlap void
1854*a6d42e7dSPeter Dunlap idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj)
1855*a6d42e7dSPeter Dunlap {
1856*a6d42e7dSPeter Dunlap 	bzero(refcnt, sizeof (*refcnt));
1857*a6d42e7dSPeter Dunlap 	idm_refcnt_reset(refcnt);
1858*a6d42e7dSPeter Dunlap 	refcnt->ir_referenced_obj = referenced_obj;
1859*a6d42e7dSPeter Dunlap 	bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t));
1860*a6d42e7dSPeter Dunlap 	refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1;
1861*a6d42e7dSPeter Dunlap 	mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL);
1862*a6d42e7dSPeter Dunlap 	cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL);
1863*a6d42e7dSPeter Dunlap }
1864*a6d42e7dSPeter Dunlap 
1865*a6d42e7dSPeter Dunlap void
1866*a6d42e7dSPeter Dunlap idm_refcnt_destroy(idm_refcnt_t *refcnt)
1867*a6d42e7dSPeter Dunlap {
1868*a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt == 0);
1869*a6d42e7dSPeter Dunlap 	cv_destroy(&refcnt->ir_cv);
1870*a6d42e7dSPeter Dunlap 	mutex_destroy(&refcnt->ir_mutex);
1871*a6d42e7dSPeter Dunlap }
1872*a6d42e7dSPeter Dunlap 
1873*a6d42e7dSPeter Dunlap void
1874*a6d42e7dSPeter Dunlap idm_refcnt_reset(idm_refcnt_t *refcnt)
1875*a6d42e7dSPeter Dunlap {
1876*a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_NOWAIT;
1877*a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt = 0;
1878*a6d42e7dSPeter Dunlap }
1879*a6d42e7dSPeter Dunlap 
1880*a6d42e7dSPeter Dunlap void
1881*a6d42e7dSPeter Dunlap idm_refcnt_hold(idm_refcnt_t *refcnt)
1882*a6d42e7dSPeter Dunlap {
1883*a6d42e7dSPeter Dunlap 	/*
1884*a6d42e7dSPeter Dunlap 	 * Nothing should take a hold on an object after a call to
1885*a6d42e7dSPeter Dunlap 	 * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref
1886*a6d42e7dSPeter Dunlap 	 */
1887*a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_waiting == REF_NOWAIT);
1888*a6d42e7dSPeter Dunlap 
1889*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
1890*a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt++;
1891*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1892*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
1893*a6d42e7dSPeter Dunlap }
1894*a6d42e7dSPeter Dunlap 
1895*a6d42e7dSPeter Dunlap static void
1896*a6d42e7dSPeter Dunlap idm_refcnt_unref_task(void *refcnt_void)
1897*a6d42e7dSPeter Dunlap {
1898*a6d42e7dSPeter Dunlap 	idm_refcnt_t *refcnt = refcnt_void;
1899*a6d42e7dSPeter Dunlap 
1900*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1901*a6d42e7dSPeter Dunlap 	(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
1902*a6d42e7dSPeter Dunlap }
1903*a6d42e7dSPeter Dunlap 
1904*a6d42e7dSPeter Dunlap void
1905*a6d42e7dSPeter Dunlap idm_refcnt_rele(idm_refcnt_t *refcnt)
1906*a6d42e7dSPeter Dunlap {
1907*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
1908*a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt > 0);
1909*a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt--;
1910*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1911*a6d42e7dSPeter Dunlap 	if (refcnt->ir_waiting == REF_NOWAIT) {
1912*a6d42e7dSPeter Dunlap 		/* No one is waiting on this object */
1913*a6d42e7dSPeter Dunlap 		mutex_exit(&refcnt->ir_mutex);
1914*a6d42e7dSPeter Dunlap 		return;
1915*a6d42e7dSPeter Dunlap 	}
1916*a6d42e7dSPeter Dunlap 
1917*a6d42e7dSPeter Dunlap 	/*
1918*a6d42e7dSPeter Dunlap 	 * Someone is waiting for this object to go idle so check if
1919*a6d42e7dSPeter Dunlap 	 * refcnt is 0.  Waiting on an object then later grabbing another
1920*a6d42e7dSPeter Dunlap 	 * reference is not allowed so we don't need to handle that case.
1921*a6d42e7dSPeter Dunlap 	 */
1922*a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
1923*a6d42e7dSPeter Dunlap 		if (refcnt->ir_waiting == REF_WAIT_ASYNC) {
1924*a6d42e7dSPeter Dunlap 			if (taskq_dispatch(idm.idm_global_taskq,
1925*a6d42e7dSPeter Dunlap 			    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
1926*a6d42e7dSPeter Dunlap 				cmn_err(CE_WARN,
1927*a6d42e7dSPeter Dunlap 				    "idm_refcnt_rele: Couldn't dispatch task");
1928*a6d42e7dSPeter Dunlap 			}
1929*a6d42e7dSPeter Dunlap 		} else if (refcnt->ir_waiting == REF_WAIT_SYNC) {
1930*a6d42e7dSPeter Dunlap 			cv_signal(&refcnt->ir_cv);
1931*a6d42e7dSPeter Dunlap 		}
1932*a6d42e7dSPeter Dunlap 	}
1933*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
1934*a6d42e7dSPeter Dunlap }
1935*a6d42e7dSPeter Dunlap 
1936*a6d42e7dSPeter Dunlap void
1937*a6d42e7dSPeter Dunlap idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
1938*a6d42e7dSPeter Dunlap {
1939*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
1940*a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt > 0);
1941*a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt--;
1942*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1943*a6d42e7dSPeter Dunlap 
1944*a6d42e7dSPeter Dunlap 	/*
1945*a6d42e7dSPeter Dunlap 	 * Someone is waiting for this object to go idle so check if
1946*a6d42e7dSPeter Dunlap 	 * refcnt is 0.  Waiting on an object then later grabbing another
1947*a6d42e7dSPeter Dunlap 	 * reference is not allowed so we don't need to handle that case.
1948*a6d42e7dSPeter Dunlap 	 */
1949*a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
1950*a6d42e7dSPeter Dunlap 		refcnt->ir_cb = cb_func;
1951*a6d42e7dSPeter Dunlap 		refcnt->ir_waiting = REF_WAIT_ASYNC;
1952*a6d42e7dSPeter Dunlap 		if (taskq_dispatch(idm.idm_global_taskq,
1953*a6d42e7dSPeter Dunlap 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
1954*a6d42e7dSPeter Dunlap 			cmn_err(CE_WARN,
1955*a6d42e7dSPeter Dunlap 			    "idm_refcnt_rele: Couldn't dispatch task");
1956*a6d42e7dSPeter Dunlap 		}
1957*a6d42e7dSPeter Dunlap 	}
1958*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
1959*a6d42e7dSPeter Dunlap }
1960*a6d42e7dSPeter Dunlap 
1961*a6d42e7dSPeter Dunlap void
1962*a6d42e7dSPeter Dunlap idm_refcnt_wait_ref(idm_refcnt_t *refcnt)
1963*a6d42e7dSPeter Dunlap {
1964*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
1965*a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_WAIT_SYNC;
1966*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1967*a6d42e7dSPeter Dunlap 	while (refcnt->ir_refcnt != 0)
1968*a6d42e7dSPeter Dunlap 		cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex);
1969*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
1970*a6d42e7dSPeter Dunlap }
1971*a6d42e7dSPeter Dunlap 
1972*a6d42e7dSPeter Dunlap void
1973*a6d42e7dSPeter Dunlap idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
1974*a6d42e7dSPeter Dunlap {
1975*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
1976*a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_WAIT_ASYNC;
1977*a6d42e7dSPeter Dunlap 	refcnt->ir_cb = cb_func;
1978*a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
1979*a6d42e7dSPeter Dunlap 	/*
1980*a6d42e7dSPeter Dunlap 	 * It's possible we don't have any references.  To make things easier
1981*a6d42e7dSPeter Dunlap 	 * on the caller use a taskq to call the callback instead of
1982*a6d42e7dSPeter Dunlap 	 * calling it synchronously
1983*a6d42e7dSPeter Dunlap 	 */
1984*a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
1985*a6d42e7dSPeter Dunlap 		if (taskq_dispatch(idm.idm_global_taskq,
1986*a6d42e7dSPeter Dunlap 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
1987*a6d42e7dSPeter Dunlap 			cmn_err(CE_WARN,
1988*a6d42e7dSPeter Dunlap 			    "idm_refcnt_async_wait_ref: "
1989*a6d42e7dSPeter Dunlap 			    "Couldn't dispatch task");
1990*a6d42e7dSPeter Dunlap 		}
1991*a6d42e7dSPeter Dunlap 	}
1992*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
1993*a6d42e7dSPeter Dunlap }
1994*a6d42e7dSPeter Dunlap 
1995*a6d42e7dSPeter Dunlap void
1996*a6d42e7dSPeter Dunlap idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt,
1997*a6d42e7dSPeter Dunlap     idm_refcnt_cb_t *cb_func)
1998*a6d42e7dSPeter Dunlap {
1999*a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2000*a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
2001*a6d42e7dSPeter Dunlap 		mutex_exit(&refcnt->ir_mutex);
2002*a6d42e7dSPeter Dunlap 		(*cb_func)(refcnt->ir_referenced_obj);
2003*a6d42e7dSPeter Dunlap 		return;
2004*a6d42e7dSPeter Dunlap 	}
2005*a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2006*a6d42e7dSPeter Dunlap }
2007*a6d42e7dSPeter Dunlap 
2008*a6d42e7dSPeter Dunlap void
2009*a6d42e7dSPeter Dunlap idm_conn_hold(idm_conn_t *ic)
2010*a6d42e7dSPeter Dunlap {
2011*a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&ic->ic_refcnt);
2012*a6d42e7dSPeter Dunlap }
2013*a6d42e7dSPeter Dunlap 
2014*a6d42e7dSPeter Dunlap void
2015*a6d42e7dSPeter Dunlap idm_conn_rele(idm_conn_t *ic)
2016*a6d42e7dSPeter Dunlap {
2017*a6d42e7dSPeter Dunlap 	idm_refcnt_rele(&ic->ic_refcnt);
2018*a6d42e7dSPeter Dunlap }
2019*a6d42e7dSPeter Dunlap 
2020*a6d42e7dSPeter Dunlap 
2021*a6d42e7dSPeter Dunlap static int
2022*a6d42e7dSPeter Dunlap _idm_init(void)
2023*a6d42e7dSPeter Dunlap {
2024*a6d42e7dSPeter Dunlap 	/* Initialize the rwlock for the taskid table */
2025*a6d42e7dSPeter Dunlap 	rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL);
2026*a6d42e7dSPeter Dunlap 
2027*a6d42e7dSPeter Dunlap 	/* Initialize the global mutex and taskq */
2028*a6d42e7dSPeter Dunlap 	mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
2029*a6d42e7dSPeter Dunlap 
2030*a6d42e7dSPeter Dunlap 	cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
2031*a6d42e7dSPeter Dunlap 	cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
2032*a6d42e7dSPeter Dunlap 
2033*a6d42e7dSPeter Dunlap 	idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
2034*a6d42e7dSPeter Dunlap 	    4, 4, TASKQ_PREPOPULATE);
2035*a6d42e7dSPeter Dunlap 	if (idm.idm_global_taskq == NULL) {
2036*a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_wd_cv);
2037*a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_tgt_svc_cv);
2038*a6d42e7dSPeter Dunlap 		mutex_destroy(&idm.idm_global_mutex);
2039*a6d42e7dSPeter Dunlap 		rw_destroy(&idm.idm_taskid_table_lock);
2040*a6d42e7dSPeter Dunlap 		return (ENOMEM);
2041*a6d42e7dSPeter Dunlap 	}
2042*a6d42e7dSPeter Dunlap 
2043*a6d42e7dSPeter Dunlap 	/* start watchdog thread */
2044*a6d42e7dSPeter Dunlap 	idm.idm_wd_thread = thread_create(NULL, 0,
2045*a6d42e7dSPeter Dunlap 	    idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri);
2046*a6d42e7dSPeter Dunlap 	if (idm.idm_wd_thread == NULL) {
2047*a6d42e7dSPeter Dunlap 		/* Couldn't create the watchdog thread */
2048*a6d42e7dSPeter Dunlap 		taskq_destroy(idm.idm_global_taskq);
2049*a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_wd_cv);
2050*a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_tgt_svc_cv);
2051*a6d42e7dSPeter Dunlap 		mutex_destroy(&idm.idm_global_mutex);
2052*a6d42e7dSPeter Dunlap 		rw_destroy(&idm.idm_taskid_table_lock);
2053*a6d42e7dSPeter Dunlap 		return (ENOMEM);
2054*a6d42e7dSPeter Dunlap 	}
2055*a6d42e7dSPeter Dunlap 
2056*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
2057*a6d42e7dSPeter Dunlap 	while (!idm.idm_wd_thread_running)
2058*a6d42e7dSPeter Dunlap 		cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex);
2059*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
2060*a6d42e7dSPeter Dunlap 
2061*a6d42e7dSPeter Dunlap 	/*
2062*a6d42e7dSPeter Dunlap 	 * Allocate the task ID table and set "next" to 0.
2063*a6d42e7dSPeter Dunlap 	 */
2064*a6d42e7dSPeter Dunlap 
2065*a6d42e7dSPeter Dunlap 	idm.idm_taskid_max = idm_max_taskids;
2066*a6d42e7dSPeter Dunlap 	idm.idm_taskid_table = (idm_task_t **)
2067*a6d42e7dSPeter Dunlap 	    kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP);
2068*a6d42e7dSPeter Dunlap 	idm.idm_taskid_next = 0;
2069*a6d42e7dSPeter Dunlap 
2070*a6d42e7dSPeter Dunlap 	/* Create the global buffer and task kmem caches */
2071*a6d42e7dSPeter Dunlap 	idm.idm_buf_cache = kmem_cache_create("idm_buf_cache",
2072*a6d42e7dSPeter Dunlap 	    sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
2073*a6d42e7dSPeter Dunlap 
2074*a6d42e7dSPeter Dunlap 	/*
2075*a6d42e7dSPeter Dunlap 	 * Note, we're explicitly allocating an additional iSER header-
2076*a6d42e7dSPeter Dunlap 	 * sized chunk for each of these elements. See idm_task_constructor().
2077*a6d42e7dSPeter Dunlap 	 */
2078*a6d42e7dSPeter Dunlap 	idm.idm_task_cache = kmem_cache_create("idm_task_cache",
2079*a6d42e7dSPeter Dunlap 	    sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8,
2080*a6d42e7dSPeter Dunlap 	    &idm_task_constructor, &idm_task_destructor,
2081*a6d42e7dSPeter Dunlap 	    NULL, NULL, NULL, KM_SLEEP);
2082*a6d42e7dSPeter Dunlap 
2083*a6d42e7dSPeter Dunlap 	/* Create the service and connection context lists */
2084*a6d42e7dSPeter Dunlap 	list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
2085*a6d42e7dSPeter Dunlap 	    offsetof(idm_svc_t, is_list_node));
2086*a6d42e7dSPeter Dunlap 	list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t),
2087*a6d42e7dSPeter Dunlap 	    offsetof(idm_conn_t, ic_list_node));
2088*a6d42e7dSPeter Dunlap 	list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
2089*a6d42e7dSPeter Dunlap 	    offsetof(idm_conn_t, ic_list_node));
2090*a6d42e7dSPeter Dunlap 
2091*a6d42e7dSPeter Dunlap 	/* Initialize the native sockets transport */
2092*a6d42e7dSPeter Dunlap 	idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]);
2093*a6d42e7dSPeter Dunlap 
2094*a6d42e7dSPeter Dunlap 	/* Create connection ID pool */
2095*a6d42e7dSPeter Dunlap 	(void) idm_idpool_create(&idm.idm_conn_id_pool);
2096*a6d42e7dSPeter Dunlap 
2097*a6d42e7dSPeter Dunlap 	return (DDI_SUCCESS);
2098*a6d42e7dSPeter Dunlap }
2099*a6d42e7dSPeter Dunlap 
2100*a6d42e7dSPeter Dunlap static int
2101*a6d42e7dSPeter Dunlap _idm_fini(void)
2102*a6d42e7dSPeter Dunlap {
2103*a6d42e7dSPeter Dunlap 	if (!list_is_empty(&idm.idm_ini_conn_list) ||
2104*a6d42e7dSPeter Dunlap 	    !list_is_empty(&idm.idm_tgt_conn_list) ||
2105*a6d42e7dSPeter Dunlap 	    !list_is_empty(&idm.idm_tgt_svc_list)) {
2106*a6d42e7dSPeter Dunlap 		return (EBUSY);
2107*a6d42e7dSPeter Dunlap 	}
2108*a6d42e7dSPeter Dunlap 
2109*a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
2110*a6d42e7dSPeter Dunlap 	idm.idm_wd_thread_running = B_FALSE;
2111*a6d42e7dSPeter Dunlap 	cv_signal(&idm.idm_wd_cv);
2112*a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
2113*a6d42e7dSPeter Dunlap 
2114*a6d42e7dSPeter Dunlap 	thread_join(idm.idm_wd_thread_did);
2115*a6d42e7dSPeter Dunlap 
2116*a6d42e7dSPeter Dunlap 	idm_idpool_destroy(&idm.idm_conn_id_pool);
2117*a6d42e7dSPeter Dunlap 	idm_so_fini();
2118*a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_ini_conn_list);
2119*a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_tgt_conn_list);
2120*a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_tgt_svc_list);
2121*a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_task_cache);
2122*a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_buf_cache);
2123*a6d42e7dSPeter Dunlap 	kmem_free(idm.idm_taskid_table,
2124*a6d42e7dSPeter Dunlap 	    idm.idm_taskid_max * sizeof (idm_task_t *));
2125*a6d42e7dSPeter Dunlap 	mutex_destroy(&idm.idm_global_mutex);
2126*a6d42e7dSPeter Dunlap 	cv_destroy(&idm.idm_wd_cv);
2127*a6d42e7dSPeter Dunlap 	cv_destroy(&idm.idm_tgt_svc_cv);
2128*a6d42e7dSPeter Dunlap 	rw_destroy(&idm.idm_taskid_table_lock);
2129*a6d42e7dSPeter Dunlap 
2130*a6d42e7dSPeter Dunlap 	return (0);
2131*a6d42e7dSPeter Dunlap }
2132