xref: /illumos-gate/usr/src/uts/common/io/idm/idm.c (revision 56261083)
1a6d42e7dSPeter Dunlap /*
2a6d42e7dSPeter Dunlap  * CDDL HEADER START
3a6d42e7dSPeter Dunlap  *
4a6d42e7dSPeter Dunlap  * The contents of this file are subject to the terms of the
5a6d42e7dSPeter Dunlap  * Common Development and Distribution License (the "License").
6a6d42e7dSPeter Dunlap  * You may not use this file except in compliance with the License.
7a6d42e7dSPeter Dunlap  *
8a6d42e7dSPeter Dunlap  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a6d42e7dSPeter Dunlap  * or http://www.opensolaris.org/os/licensing.
10a6d42e7dSPeter Dunlap  * See the License for the specific language governing permissions
11a6d42e7dSPeter Dunlap  * and limitations under the License.
12a6d42e7dSPeter Dunlap  *
13a6d42e7dSPeter Dunlap  * When distributing Covered Code, include this CDDL HEADER in each
14a6d42e7dSPeter Dunlap  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a6d42e7dSPeter Dunlap  * If applicable, add the following below this CDDL HEADER, with the
16a6d42e7dSPeter Dunlap  * fields enclosed by brackets "[]" replaced with your own identifying
17a6d42e7dSPeter Dunlap  * information: Portions Copyright [yyyy] [name of copyright owner]
18a6d42e7dSPeter Dunlap  *
19a6d42e7dSPeter Dunlap  * CDDL HEADER END
20a6d42e7dSPeter Dunlap  */
21a6d42e7dSPeter Dunlap /*
2230e7468fSPeter Dunlap  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23a6d42e7dSPeter Dunlap  * Use is subject to license terms.
24a6d42e7dSPeter Dunlap  */
25a6d42e7dSPeter Dunlap 
26a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
27a6d42e7dSPeter Dunlap #include <sys/conf.h>
28a6d42e7dSPeter Dunlap #include <sys/file.h>
29a6d42e7dSPeter Dunlap #include <sys/ddi.h>
30a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
31a6d42e7dSPeter Dunlap #include <sys/modctl.h>
32a6d42e7dSPeter Dunlap 
33a6d42e7dSPeter Dunlap #include <sys/socket.h>
34a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
35a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
36a6d42e7dSPeter Dunlap 
37a6d42e7dSPeter Dunlap #include <sys/socketvar.h>
38a6d42e7dSPeter Dunlap #include <netinet/in.h>
39a6d42e7dSPeter Dunlap 
40a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
41a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
42a6d42e7dSPeter Dunlap 
43a6d42e7dSPeter Dunlap #define	IDM_NAME_VERSION	"iSCSI Data Mover"
44a6d42e7dSPeter Dunlap 
45a6d42e7dSPeter Dunlap extern struct mod_ops mod_miscops;
46a6d42e7dSPeter Dunlap extern struct mod_ops mod_miscops;
47a6d42e7dSPeter Dunlap 
48a6d42e7dSPeter Dunlap static struct modlmisc modlmisc = {
49a6d42e7dSPeter Dunlap 	&mod_miscops,	/* Type of module */
50a6d42e7dSPeter Dunlap 	IDM_NAME_VERSION
51a6d42e7dSPeter Dunlap };
52a6d42e7dSPeter Dunlap 
53a6d42e7dSPeter Dunlap static struct modlinkage modlinkage = {
54a6d42e7dSPeter Dunlap 	MODREV_1, (void *)&modlmisc, NULL
55a6d42e7dSPeter Dunlap };
56a6d42e7dSPeter Dunlap 
57a6d42e7dSPeter Dunlap extern int idm_task_compare(const void *t1, const void *t2);
58a6d42e7dSPeter Dunlap extern void idm_wd_thread(void *arg);
59a6d42e7dSPeter Dunlap 
60a6d42e7dSPeter Dunlap static int _idm_init(void);
61a6d42e7dSPeter Dunlap static int _idm_fini(void);
62a6d42e7dSPeter Dunlap static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
63a6d42e7dSPeter Dunlap static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
64a6d42e7dSPeter Dunlap static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
65a6d42e7dSPeter Dunlap static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
66a6d42e7dSPeter Dunlap static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
67a6d42e7dSPeter Dunlap     idm_abort_type_t abort_type);
68a6d42e7dSPeter Dunlap static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
6930e7468fSPeter Dunlap static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
7030e7468fSPeter Dunlap     int sleepflag);
71a6d42e7dSPeter Dunlap 
72a6d42e7dSPeter Dunlap boolean_t idm_conn_logging = 0;
73a6d42e7dSPeter Dunlap boolean_t idm_svc_logging = 0;
7430e7468fSPeter Dunlap #ifdef DEBUG
7530e7468fSPeter Dunlap boolean_t idm_pattern_checking = 1;
7630e7468fSPeter Dunlap #else
7730e7468fSPeter Dunlap boolean_t idm_pattern_checking = 0;
7830e7468fSPeter Dunlap #endif
79a6d42e7dSPeter Dunlap 
80a6d42e7dSPeter Dunlap /*
81a6d42e7dSPeter Dunlap  * Potential tuneable for the maximum number of tasks.  Default to
82a6d42e7dSPeter Dunlap  * IDM_TASKIDS_MAX
83a6d42e7dSPeter Dunlap  */
84a6d42e7dSPeter Dunlap 
85a6d42e7dSPeter Dunlap uint32_t	idm_max_taskids = IDM_TASKIDS_MAX;
86a6d42e7dSPeter Dunlap 
87a6d42e7dSPeter Dunlap /*
88a6d42e7dSPeter Dunlap  * Global list of transport handles
89a6d42e7dSPeter Dunlap  *   These are listed in preferential order, so we can simply take the
90a6d42e7dSPeter Dunlap  *   first "it_conn_is_capable" hit. Note also that the order maps to
91a6d42e7dSPeter Dunlap  *   the order of the idm_transport_type_t list.
92a6d42e7dSPeter Dunlap  */
93a6d42e7dSPeter Dunlap idm_transport_t idm_transport_list[] = {
94a6d42e7dSPeter Dunlap 
95a6d42e7dSPeter Dunlap 	/* iSER on InfiniBand transport handle */
96a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_ISER,	/* type */
97a6d42e7dSPeter Dunlap 	"/devices/ib/iser@0:iser",	/* device path */
98a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
99a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
100a6d42e7dSPeter Dunlap 	NULL},				/* transport caps */
101a6d42e7dSPeter Dunlap 
102a6d42e7dSPeter Dunlap 	/* IDM native sockets transport handle */
103a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_SOCKETS,	/* type */
104a6d42e7dSPeter Dunlap 	NULL,				/* device path */
105a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
106a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
107a6d42e7dSPeter Dunlap 	NULL}				/* transport caps */
108a6d42e7dSPeter Dunlap 
109a6d42e7dSPeter Dunlap };
110a6d42e7dSPeter Dunlap 
111a6d42e7dSPeter Dunlap int
112a6d42e7dSPeter Dunlap _init(void)
113a6d42e7dSPeter Dunlap {
114a6d42e7dSPeter Dunlap 	int rc;
115a6d42e7dSPeter Dunlap 
116a6d42e7dSPeter Dunlap 	if ((rc = _idm_init()) != 0) {
117a6d42e7dSPeter Dunlap 		return (rc);
118a6d42e7dSPeter Dunlap 	}
119a6d42e7dSPeter Dunlap 
120a6d42e7dSPeter Dunlap 	return (mod_install(&modlinkage));
121a6d42e7dSPeter Dunlap }
122a6d42e7dSPeter Dunlap 
123a6d42e7dSPeter Dunlap int
124a6d42e7dSPeter Dunlap _fini(void)
125a6d42e7dSPeter Dunlap {
126a6d42e7dSPeter Dunlap 	int rc;
127a6d42e7dSPeter Dunlap 
128a6d42e7dSPeter Dunlap 	if ((rc = _idm_fini()) != 0) {
129a6d42e7dSPeter Dunlap 		return (rc);
130a6d42e7dSPeter Dunlap 	}
131a6d42e7dSPeter Dunlap 
132a6d42e7dSPeter Dunlap 	if ((rc = mod_remove(&modlinkage)) != 0) {
133a6d42e7dSPeter Dunlap 		return (rc);
134a6d42e7dSPeter Dunlap 	}
135a6d42e7dSPeter Dunlap 
136a6d42e7dSPeter Dunlap 	return (rc);
137a6d42e7dSPeter Dunlap }
138a6d42e7dSPeter Dunlap 
139a6d42e7dSPeter Dunlap int
140a6d42e7dSPeter Dunlap _info(struct modinfo *modinfop)
141a6d42e7dSPeter Dunlap {
142a6d42e7dSPeter Dunlap 	return (mod_info(&modlinkage, modinfop));
143a6d42e7dSPeter Dunlap }
144a6d42e7dSPeter Dunlap 
145a6d42e7dSPeter Dunlap /*
146a6d42e7dSPeter Dunlap  * idm_transport_register()
147a6d42e7dSPeter Dunlap  *
148a6d42e7dSPeter Dunlap  * Provides a mechanism for an IDM transport driver to register its
149a6d42e7dSPeter Dunlap  * transport ops and caps with the IDM kernel module. Invoked during
150a6d42e7dSPeter Dunlap  * a transport driver's attach routine.
151a6d42e7dSPeter Dunlap  */
152a6d42e7dSPeter Dunlap idm_status_t
153a6d42e7dSPeter Dunlap idm_transport_register(idm_transport_attr_t *attr)
154a6d42e7dSPeter Dunlap {
155a6d42e7dSPeter Dunlap 	ASSERT(attr->it_ops != NULL);
156a6d42e7dSPeter Dunlap 	ASSERT(attr->it_caps != NULL);
157a6d42e7dSPeter Dunlap 
158a6d42e7dSPeter Dunlap 	switch (attr->type) {
159a6d42e7dSPeter Dunlap 	/* All known non-native transports here; for now, iSER */
160a6d42e7dSPeter Dunlap 	case IDM_TRANSPORT_TYPE_ISER:
161a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_ops	= attr->it_ops;
162a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_caps	= attr->it_caps;
163a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
164a6d42e7dSPeter Dunlap 
165a6d42e7dSPeter Dunlap 	default:
166a6d42e7dSPeter Dunlap 		cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
167a6d42e7dSPeter Dunlap 		    "idm_transport_register", attr->type);
168a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
169a6d42e7dSPeter Dunlap 	}
170a6d42e7dSPeter Dunlap }
171a6d42e7dSPeter Dunlap 
172a6d42e7dSPeter Dunlap /*
173a6d42e7dSPeter Dunlap  * idm_ini_conn_create
174a6d42e7dSPeter Dunlap  *
175a6d42e7dSPeter Dunlap  * This function is invoked by the iSCSI layer to create a connection context.
176a6d42e7dSPeter Dunlap  * This does not actually establish the socket connection.
177a6d42e7dSPeter Dunlap  *
178a6d42e7dSPeter Dunlap  * cr - Connection request parameters
179a6d42e7dSPeter Dunlap  * new_con - Output parameter that contains the new request if successful
180a6d42e7dSPeter Dunlap  *
181a6d42e7dSPeter Dunlap  */
182a6d42e7dSPeter Dunlap idm_status_t
183a6d42e7dSPeter Dunlap idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
184a6d42e7dSPeter Dunlap {
185a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
186a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
187a6d42e7dSPeter Dunlap 	int			rc;
188a6d42e7dSPeter Dunlap 
189a6d42e7dSPeter Dunlap 	it = idm_transport_lookup(cr);
190a6d42e7dSPeter Dunlap 
191a6d42e7dSPeter Dunlap retry:
192a6d42e7dSPeter Dunlap 	ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
193a6d42e7dSPeter Dunlap 	    &cr->icr_conn_ops);
194a6d42e7dSPeter Dunlap 
195a6d42e7dSPeter Dunlap 	bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
196a6d42e7dSPeter Dunlap 	    sizeof (cr->cr_ini_dst_addr));
197a6d42e7dSPeter Dunlap 
198a6d42e7dSPeter Dunlap 	/* create the transport-specific connection components */
199a6d42e7dSPeter Dunlap 	rc = it->it_ops->it_ini_conn_create(cr, ic);
200a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
201a6d42e7dSPeter Dunlap 		/* cleanup the failed connection */
202a6d42e7dSPeter Dunlap 		idm_conn_destroy_common(ic);
203a6d42e7dSPeter Dunlap 
204a6d42e7dSPeter Dunlap 		/*
205a6d42e7dSPeter Dunlap 		 * It is possible for an IB client to connect to
206a6d42e7dSPeter Dunlap 		 * an ethernet-only client via an IB-eth gateway.
207a6d42e7dSPeter Dunlap 		 * Therefore, if we are attempting to use iSER and
208a6d42e7dSPeter Dunlap 		 * fail, retry with sockets before ultimately
209a6d42e7dSPeter Dunlap 		 * failing the connection.
210a6d42e7dSPeter Dunlap 		 */
211a6d42e7dSPeter Dunlap 		if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
212a6d42e7dSPeter Dunlap 			it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
213a6d42e7dSPeter Dunlap 			goto retry;
214a6d42e7dSPeter Dunlap 		}
215a6d42e7dSPeter Dunlap 
216a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
217a6d42e7dSPeter Dunlap 	}
218a6d42e7dSPeter Dunlap 
219a6d42e7dSPeter Dunlap 	*new_con = ic;
220a6d42e7dSPeter Dunlap 
221a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
222a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_ini_conn_list, ic);
223a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
224a6d42e7dSPeter Dunlap 
225a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
226a6d42e7dSPeter Dunlap }
227a6d42e7dSPeter Dunlap 
228a6d42e7dSPeter Dunlap /*
229a6d42e7dSPeter Dunlap  * idm_ini_conn_destroy
230a6d42e7dSPeter Dunlap  *
231a6d42e7dSPeter Dunlap  * Releases any resources associated with the connection.  This is the
232a6d42e7dSPeter Dunlap  * complement to idm_ini_conn_create.
233a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
234a6d42e7dSPeter Dunlap  *
235a6d42e7dSPeter Dunlap  */
23630e7468fSPeter Dunlap void
23730e7468fSPeter Dunlap idm_ini_conn_destroy_task(void *ic_void)
23830e7468fSPeter Dunlap {
23930e7468fSPeter Dunlap 	idm_conn_t *ic = ic_void;
24030e7468fSPeter Dunlap 
24130e7468fSPeter Dunlap 	ic->ic_transport_ops->it_ini_conn_destroy(ic);
24230e7468fSPeter Dunlap 	idm_conn_destroy_common(ic);
24330e7468fSPeter Dunlap }
24430e7468fSPeter Dunlap 
245a6d42e7dSPeter Dunlap void
246a6d42e7dSPeter Dunlap idm_ini_conn_destroy(idm_conn_t *ic)
247a6d42e7dSPeter Dunlap {
24830e7468fSPeter Dunlap 	/*
24930e7468fSPeter Dunlap 	 * It's reasonable for the initiator to call idm_ini_conn_destroy
25030e7468fSPeter Dunlap 	 * from within the context of the CN_CONNECT_DESTROY notification.
25130e7468fSPeter Dunlap 	 * That's a problem since we want to destroy the taskq for the
25230e7468fSPeter Dunlap 	 * state machine associated with the connection.  Remove the
25330e7468fSPeter Dunlap 	 * connection from the list right away then handle the remaining
25430e7468fSPeter Dunlap 	 * work via the idm_global_taskq.
25530e7468fSPeter Dunlap 	 */
256a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
257a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_ini_conn_list, ic);
258a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
259a6d42e7dSPeter Dunlap 
26030e7468fSPeter Dunlap 	if (taskq_dispatch(idm.idm_global_taskq,
26130e7468fSPeter Dunlap 	    &idm_ini_conn_destroy_task, ic, TQ_SLEEP) == NULL) {
26230e7468fSPeter Dunlap 		cmn_err(CE_WARN,
26330e7468fSPeter Dunlap 		    "idm_ini_conn_destroy: Couldn't dispatch task");
26430e7468fSPeter Dunlap 	}
265a6d42e7dSPeter Dunlap }
266a6d42e7dSPeter Dunlap 
267a6d42e7dSPeter Dunlap /*
268a6d42e7dSPeter Dunlap  * idm_ini_conn_connect
269a6d42e7dSPeter Dunlap  *
270a6d42e7dSPeter Dunlap  * Establish connection to the remote system identified in idm_conn_t.
271a6d42e7dSPeter Dunlap  * The connection parameters including the remote IP address were established
27230e7468fSPeter Dunlap  * in the call to idm_ini_conn_create.  The IDM state machine will
27330e7468fSPeter Dunlap  * perform client notifications as necessary to prompt the initiator through
27430e7468fSPeter Dunlap  * the login process.  IDM also keeps a timer running so that if the login
27530e7468fSPeter Dunlap  * process doesn't complete in a timely manner it will fail.
276a6d42e7dSPeter Dunlap  *
277a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
278a6d42e7dSPeter Dunlap  *
279a6d42e7dSPeter Dunlap  * Returns success if the connection was established, otherwise some kind
280a6d42e7dSPeter Dunlap  * of meaningful error code.
281a6d42e7dSPeter Dunlap  *
28230e7468fSPeter Dunlap  * Upon return the login has either failed or is loggin in (ffp)
283a6d42e7dSPeter Dunlap  */
284a6d42e7dSPeter Dunlap idm_status_t
285a6d42e7dSPeter Dunlap idm_ini_conn_connect(idm_conn_t *ic)
286a6d42e7dSPeter Dunlap {
28730e7468fSPeter Dunlap 	idm_status_t	rc = IDM_STATUS_SUCCESS;
288a6d42e7dSPeter Dunlap 
289a6d42e7dSPeter Dunlap 	rc = idm_conn_sm_init(ic);
290a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
291a6d42e7dSPeter Dunlap 		return (ic->ic_conn_sm_status);
292a6d42e7dSPeter Dunlap 	}
29330e7468fSPeter Dunlap 
29430e7468fSPeter Dunlap 	/* Hold connection until we return */
29530e7468fSPeter Dunlap 	idm_conn_hold(ic);
29630e7468fSPeter Dunlap 
297a6d42e7dSPeter Dunlap 	/* Kick state machine */
298a6d42e7dSPeter Dunlap 	idm_conn_event(ic, CE_CONNECT_REQ, NULL);
299a6d42e7dSPeter Dunlap 
300a6d42e7dSPeter Dunlap 	/* Wait for login flag */
301a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
302a6d42e7dSPeter Dunlap 	while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
303a6d42e7dSPeter Dunlap 	    !(ic->ic_state_flags & CF_ERROR)) {
304a6d42e7dSPeter Dunlap 		cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
305a6d42e7dSPeter Dunlap 	}
306a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
307a6d42e7dSPeter Dunlap 
308a6d42e7dSPeter Dunlap 	if (ic->ic_state_flags & CF_ERROR) {
309a6d42e7dSPeter Dunlap 		/* ic->ic_conn_sm_status will contains failure status */
31030e7468fSPeter Dunlap 		idm_conn_rele(ic);
311a6d42e7dSPeter Dunlap 		return (ic->ic_conn_sm_status);
312a6d42e7dSPeter Dunlap 	}
313a6d42e7dSPeter Dunlap 
314a6d42e7dSPeter Dunlap 	/* Ready to login */
315a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_state_flags & CF_LOGIN_READY);
316a6d42e7dSPeter Dunlap 	(void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
317a6d42e7dSPeter Dunlap 
31830e7468fSPeter Dunlap 	idm_conn_rele(ic);
31930e7468fSPeter Dunlap 
32030e7468fSPeter Dunlap 	return (rc);
321a6d42e7dSPeter Dunlap }
322a6d42e7dSPeter Dunlap 
323a6d42e7dSPeter Dunlap /*
32430e7468fSPeter Dunlap  * idm_ini_conn_disconnect
325a6d42e7dSPeter Dunlap  *
32630e7468fSPeter Dunlap  * Forces a connection (previously established using idm_ini_conn_connect)
32730e7468fSPeter Dunlap  * to perform a controlled shutdown, cleaning up any outstanding requests.
32830e7468fSPeter Dunlap  *
32930e7468fSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
33030e7468fSPeter Dunlap  *
33130e7468fSPeter Dunlap  * This is asynchronous and will return before the connection is properly
33230e7468fSPeter Dunlap  * shutdown
333a6d42e7dSPeter Dunlap  */
33430e7468fSPeter Dunlap /* ARGSUSED */
33530e7468fSPeter Dunlap void
33630e7468fSPeter Dunlap idm_ini_conn_disconnect(idm_conn_t *ic)
337a6d42e7dSPeter Dunlap {
33830e7468fSPeter Dunlap 	idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
339a6d42e7dSPeter Dunlap }
340a6d42e7dSPeter Dunlap 
341a6d42e7dSPeter Dunlap /*
34230e7468fSPeter Dunlap  * idm_ini_conn_disconnect_wait
343a6d42e7dSPeter Dunlap  *
344a6d42e7dSPeter Dunlap  * Forces a connection (previously established using idm_ini_conn_connect)
34530e7468fSPeter Dunlap  * to perform a controlled shutdown.  Blocks until the connection is
34630e7468fSPeter Dunlap  * disconnected.
347a6d42e7dSPeter Dunlap  *
348a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
349a6d42e7dSPeter Dunlap  */
350a6d42e7dSPeter Dunlap /* ARGSUSED */
351a6d42e7dSPeter Dunlap void
35230e7468fSPeter Dunlap idm_ini_conn_disconnect_sync(idm_conn_t *ic)
353a6d42e7dSPeter Dunlap {
354a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
35530e7468fSPeter Dunlap 	if ((ic->ic_state != CS_S9_INIT_ERROR) &&
35630e7468fSPeter Dunlap 	    (ic->ic_state != CS_S11_COMPLETE)) {
35730e7468fSPeter Dunlap 		idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, NULL, CT_NONE);
35830e7468fSPeter Dunlap 		while ((ic->ic_state != CS_S9_INIT_ERROR) &&
35930e7468fSPeter Dunlap 		    (ic->ic_state != CS_S11_COMPLETE))
36030e7468fSPeter Dunlap 			cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
361a6d42e7dSPeter Dunlap 	}
362a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
363a6d42e7dSPeter Dunlap }
364a6d42e7dSPeter Dunlap 
365a6d42e7dSPeter Dunlap /*
366a6d42e7dSPeter Dunlap  * idm_tgt_svc_create
367a6d42e7dSPeter Dunlap  *
368a6d42e7dSPeter Dunlap  * The target calls this service to obtain a service context for each available
369a6d42e7dSPeter Dunlap  * transport, starting a service of each type related to the IP address and port
370a6d42e7dSPeter Dunlap  * passed. The idm_svc_req_t contains the service parameters.
371a6d42e7dSPeter Dunlap  */
372a6d42e7dSPeter Dunlap idm_status_t
373a6d42e7dSPeter Dunlap idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
374a6d42e7dSPeter Dunlap {
375a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
376a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
377a6d42e7dSPeter Dunlap 	idm_svc_t		*is;
378a6d42e7dSPeter Dunlap 	int			rc;
379a6d42e7dSPeter Dunlap 
380a6d42e7dSPeter Dunlap 	*new_svc = NULL;
381a6d42e7dSPeter Dunlap 	is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
382a6d42e7dSPeter Dunlap 
383a6d42e7dSPeter Dunlap 	/* Initialize transport-agnostic components of the service handle */
384a6d42e7dSPeter Dunlap 	is->is_svc_req = *sr;
385a6d42e7dSPeter Dunlap 	mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
386a6d42e7dSPeter Dunlap 	cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
387a6d42e7dSPeter Dunlap 	mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
388a6d42e7dSPeter Dunlap 	cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
389a6d42e7dSPeter Dunlap 	idm_refcnt_init(&is->is_refcnt, is);
390a6d42e7dSPeter Dunlap 
391a6d42e7dSPeter Dunlap 	/*
392a6d42e7dSPeter Dunlap 	 * Make sure all available transports are setup.  We call this now
393a6d42e7dSPeter Dunlap 	 * instead of at initialization time in case IB has become available
394a6d42e7dSPeter Dunlap 	 * since we started (hotplug, etc).
395a6d42e7dSPeter Dunlap 	 */
396a6d42e7dSPeter Dunlap 	idm_transport_setup(sr->sr_li);
397a6d42e7dSPeter Dunlap 
398a6d42e7dSPeter Dunlap 	/*
399a6d42e7dSPeter Dunlap 	 * Loop through the transports, configuring the transport-specific
400a6d42e7dSPeter Dunlap 	 * components of each one.
401a6d42e7dSPeter Dunlap 	 */
402a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
403a6d42e7dSPeter Dunlap 
404a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
405a6d42e7dSPeter Dunlap 		/*
406a6d42e7dSPeter Dunlap 		 * If it_ops is NULL then the transport is unconfigured
407a6d42e7dSPeter Dunlap 		 * and we shouldn't try to start the service.
408a6d42e7dSPeter Dunlap 		 */
409a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
410a6d42e7dSPeter Dunlap 			continue;
411a6d42e7dSPeter Dunlap 		}
412a6d42e7dSPeter Dunlap 
413a6d42e7dSPeter Dunlap 		rc = it->it_ops->it_tgt_svc_create(sr, is);
414a6d42e7dSPeter Dunlap 		if (rc != IDM_STATUS_SUCCESS) {
415a6d42e7dSPeter Dunlap 			/* Teardown any configured services */
416a6d42e7dSPeter Dunlap 			while (type--) {
417a6d42e7dSPeter Dunlap 				it = &idm_transport_list[type];
418a6d42e7dSPeter Dunlap 				if (it->it_ops == NULL) {
419a6d42e7dSPeter Dunlap 					continue;
420a6d42e7dSPeter Dunlap 				}
421a6d42e7dSPeter Dunlap 				it->it_ops->it_tgt_svc_destroy(is);
422a6d42e7dSPeter Dunlap 			}
423a6d42e7dSPeter Dunlap 			/* Free the svc context and return */
424a6d42e7dSPeter Dunlap 			kmem_free(is, sizeof (idm_svc_t));
425a6d42e7dSPeter Dunlap 			return (rc);
426a6d42e7dSPeter Dunlap 		}
427a6d42e7dSPeter Dunlap 	}
428a6d42e7dSPeter Dunlap 
429a6d42e7dSPeter Dunlap 	*new_svc = is;
430a6d42e7dSPeter Dunlap 
431a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
432a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_tgt_svc_list, is);
433a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
434a6d42e7dSPeter Dunlap 
435a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
436a6d42e7dSPeter Dunlap }
437a6d42e7dSPeter Dunlap 
438a6d42e7dSPeter Dunlap /*
439a6d42e7dSPeter Dunlap  * idm_tgt_svc_destroy
440a6d42e7dSPeter Dunlap  *
441a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
442a6d42e7dSPeter Dunlap  *
443a6d42e7dSPeter Dunlap  * Cleanup any resources associated with the idm_svc_t.
444a6d42e7dSPeter Dunlap  */
445a6d42e7dSPeter Dunlap void
446a6d42e7dSPeter Dunlap idm_tgt_svc_destroy(idm_svc_t *is)
447a6d42e7dSPeter Dunlap {
448a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
449a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
450a6d42e7dSPeter Dunlap 
451a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
452a6d42e7dSPeter Dunlap 	/* remove this service from the global list */
453a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_tgt_svc_list, is);
454a6d42e7dSPeter Dunlap 	/* wakeup any waiters for service change */
455a6d42e7dSPeter Dunlap 	cv_broadcast(&idm.idm_tgt_svc_cv);
456a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
457a6d42e7dSPeter Dunlap 
458a6d42e7dSPeter Dunlap 	/* teardown each transport-specific service */
459a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
460a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
461a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
462a6d42e7dSPeter Dunlap 			continue;
463a6d42e7dSPeter Dunlap 		}
464a6d42e7dSPeter Dunlap 
465a6d42e7dSPeter Dunlap 		it->it_ops->it_tgt_svc_destroy(is);
466a6d42e7dSPeter Dunlap 	}
467a6d42e7dSPeter Dunlap 
46830e7468fSPeter Dunlap 	/* tear down the svc resources */
46930e7468fSPeter Dunlap 	idm_refcnt_destroy(&is->is_refcnt);
47030e7468fSPeter Dunlap 	cv_destroy(&is->is_count_cv);
47130e7468fSPeter Dunlap 	mutex_destroy(&is->is_count_mutex);
47230e7468fSPeter Dunlap 	cv_destroy(&is->is_cv);
47330e7468fSPeter Dunlap 	mutex_destroy(&is->is_mutex);
47430e7468fSPeter Dunlap 
475a6d42e7dSPeter Dunlap 	/* free the svc handle */
476a6d42e7dSPeter Dunlap 	kmem_free(is, sizeof (idm_svc_t));
477a6d42e7dSPeter Dunlap }
478a6d42e7dSPeter Dunlap 
479a6d42e7dSPeter Dunlap void
480a6d42e7dSPeter Dunlap idm_tgt_svc_hold(idm_svc_t *is)
481a6d42e7dSPeter Dunlap {
482a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&is->is_refcnt);
483a6d42e7dSPeter Dunlap }
484a6d42e7dSPeter Dunlap 
485a6d42e7dSPeter Dunlap void
486a6d42e7dSPeter Dunlap idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
487a6d42e7dSPeter Dunlap {
488a6d42e7dSPeter Dunlap 	idm_refcnt_rele_and_destroy(&is->is_refcnt,
489a6d42e7dSPeter Dunlap 	    (idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
490a6d42e7dSPeter Dunlap }
491a6d42e7dSPeter Dunlap 
492a6d42e7dSPeter Dunlap /*
493a6d42e7dSPeter Dunlap  * idm_tgt_svc_online
494a6d42e7dSPeter Dunlap  *
495a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
496a6d42e7dSPeter Dunlap  *
497a6d42e7dSPeter Dunlap  * Online each transport service, as we want this target to be accessible
498a6d42e7dSPeter Dunlap  * via any configured transport.
499a6d42e7dSPeter Dunlap  *
500a6d42e7dSPeter Dunlap  * When the initiator establishes a new connection to the target, IDM will
501a6d42e7dSPeter Dunlap  * call the "new connect" callback defined in the idm_svc_req_t structure
502a6d42e7dSPeter Dunlap  * and it will pass an idm_conn_t structure representing that new connection.
503a6d42e7dSPeter Dunlap  */
504a6d42e7dSPeter Dunlap idm_status_t
505a6d42e7dSPeter Dunlap idm_tgt_svc_online(idm_svc_t *is)
506a6d42e7dSPeter Dunlap {
507a6d42e7dSPeter Dunlap 
50830e7468fSPeter Dunlap 	idm_transport_type_t	type, last_type;
509a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
51030e7468fSPeter Dunlap 	int			rc = IDM_STATUS_SUCCESS;
511a6d42e7dSPeter Dunlap 
512a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
513a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
51430e7468fSPeter Dunlap 		/* Walk through each of the transports and online them */
515a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
516a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
517a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
518a6d42e7dSPeter Dunlap 				/* transport is not registered */
519a6d42e7dSPeter Dunlap 				continue;
520a6d42e7dSPeter Dunlap 			}
521a6d42e7dSPeter Dunlap 
522a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
523a6d42e7dSPeter Dunlap 			rc = it->it_ops->it_tgt_svc_online(is);
524a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
52530e7468fSPeter Dunlap 			if (rc != IDM_STATUS_SUCCESS) {
52630e7468fSPeter Dunlap 				last_type = type;
52730e7468fSPeter Dunlap 				break;
52830e7468fSPeter Dunlap 			}
52930e7468fSPeter Dunlap 		}
53030e7468fSPeter Dunlap 		if (rc != IDM_STATUS_SUCCESS) {
53130e7468fSPeter Dunlap 			/*
53230e7468fSPeter Dunlap 			 * The last transport failed to online.
53330e7468fSPeter Dunlap 			 * Offline any transport onlined above and
53430e7468fSPeter Dunlap 			 * do not online the target.
53530e7468fSPeter Dunlap 			 */
53630e7468fSPeter Dunlap 			for (type = 0; type < last_type; type++) {
53730e7468fSPeter Dunlap 				it = &idm_transport_list[type];
53830e7468fSPeter Dunlap 				if (it->it_ops == NULL) {
53930e7468fSPeter Dunlap 					/* transport is not registered */
54030e7468fSPeter Dunlap 					continue;
54130e7468fSPeter Dunlap 				}
54230e7468fSPeter Dunlap 
54330e7468fSPeter Dunlap 				mutex_exit(&is->is_mutex);
54430e7468fSPeter Dunlap 				it->it_ops->it_tgt_svc_offline(is);
54530e7468fSPeter Dunlap 				mutex_enter(&is->is_mutex);
546a6d42e7dSPeter Dunlap 			}
54730e7468fSPeter Dunlap 		} else {
54830e7468fSPeter Dunlap 			/* Target service now online */
54930e7468fSPeter Dunlap 			is->is_online = 1;
550a6d42e7dSPeter Dunlap 		}
551a6d42e7dSPeter Dunlap 	} else {
55230e7468fSPeter Dunlap 		/* Target service already online, just bump the count */
553a6d42e7dSPeter Dunlap 		is->is_online++;
55430e7468fSPeter Dunlap 	}
555a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
556a6d42e7dSPeter Dunlap 
55730e7468fSPeter Dunlap 	return (rc);
558a6d42e7dSPeter Dunlap }
559a6d42e7dSPeter Dunlap 
560a6d42e7dSPeter Dunlap /*
561a6d42e7dSPeter Dunlap  * idm_tgt_svc_offline
562a6d42e7dSPeter Dunlap  *
563a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
564a6d42e7dSPeter Dunlap  *
565a6d42e7dSPeter Dunlap  * Shutdown any online target services.
566a6d42e7dSPeter Dunlap  */
567a6d42e7dSPeter Dunlap void
568a6d42e7dSPeter Dunlap idm_tgt_svc_offline(idm_svc_t *is)
569a6d42e7dSPeter Dunlap {
570a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
571a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
572a6d42e7dSPeter Dunlap 
573a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
574a6d42e7dSPeter Dunlap 	is->is_online--;
575a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
576a6d42e7dSPeter Dunlap 		/* Walk through each of the transports and offline them */
577a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
578a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
579a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
580a6d42e7dSPeter Dunlap 				/* transport is not registered */
581a6d42e7dSPeter Dunlap 				continue;
582a6d42e7dSPeter Dunlap 			}
583a6d42e7dSPeter Dunlap 
584a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
585a6d42e7dSPeter Dunlap 			it->it_ops->it_tgt_svc_offline(is);
586a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
587a6d42e7dSPeter Dunlap 		}
588a6d42e7dSPeter Dunlap 	}
589a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
590a6d42e7dSPeter Dunlap }
591a6d42e7dSPeter Dunlap 
592a6d42e7dSPeter Dunlap /*
593a6d42e7dSPeter Dunlap  * idm_tgt_svc_lookup
594a6d42e7dSPeter Dunlap  *
595a6d42e7dSPeter Dunlap  * Lookup a service instance listening on the specified port
596a6d42e7dSPeter Dunlap  */
597a6d42e7dSPeter Dunlap 
598a6d42e7dSPeter Dunlap idm_svc_t *
599a6d42e7dSPeter Dunlap idm_tgt_svc_lookup(uint16_t port)
600a6d42e7dSPeter Dunlap {
601a6d42e7dSPeter Dunlap 	idm_svc_t *result;
602a6d42e7dSPeter Dunlap 
603a6d42e7dSPeter Dunlap retry:
604a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
605a6d42e7dSPeter Dunlap 	for (result = list_head(&idm.idm_tgt_svc_list);
606a6d42e7dSPeter Dunlap 	    result != NULL;
607a6d42e7dSPeter Dunlap 	    result = list_next(&idm.idm_tgt_svc_list, result)) {
608a6d42e7dSPeter Dunlap 		if (result->is_svc_req.sr_port == port) {
609a6d42e7dSPeter Dunlap 			if (result->is_online == 0) {
610a6d42e7dSPeter Dunlap 				/*
611a6d42e7dSPeter Dunlap 				 * A service exists on this port, but it
612a6d42e7dSPeter Dunlap 				 * is going away, wait for it to cleanup.
613a6d42e7dSPeter Dunlap 				 */
614a6d42e7dSPeter Dunlap 				cv_wait(&idm.idm_tgt_svc_cv,
615a6d42e7dSPeter Dunlap 				    &idm.idm_global_mutex);
616a6d42e7dSPeter Dunlap 				mutex_exit(&idm.idm_global_mutex);
617a6d42e7dSPeter Dunlap 				goto retry;
618a6d42e7dSPeter Dunlap 			}
619a6d42e7dSPeter Dunlap 			idm_tgt_svc_hold(result);
620a6d42e7dSPeter Dunlap 			mutex_exit(&idm.idm_global_mutex);
621a6d42e7dSPeter Dunlap 			return (result);
622a6d42e7dSPeter Dunlap 		}
623a6d42e7dSPeter Dunlap 	}
624a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
625a6d42e7dSPeter Dunlap 
626a6d42e7dSPeter Dunlap 	return (NULL);
627a6d42e7dSPeter Dunlap }
628a6d42e7dSPeter Dunlap 
629a6d42e7dSPeter Dunlap /*
630a6d42e7dSPeter Dunlap  * idm_negotiate_key_values()
631a6d42e7dSPeter Dunlap  * Give IDM level a chance to negotiate any login parameters it should own.
632a6d42e7dSPeter Dunlap  *  -- leave unhandled parameters alone on request_nvl
633a6d42e7dSPeter Dunlap  *  -- move all handled parameters to response_nvl with an appropriate response
634a6d42e7dSPeter Dunlap  *  -- also add an entry to negotiated_nvl for any accepted parameters
635a6d42e7dSPeter Dunlap  */
636a6d42e7dSPeter Dunlap kv_status_t
637a6d42e7dSPeter Dunlap idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
638a6d42e7dSPeter Dunlap     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
639a6d42e7dSPeter Dunlap {
640a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
641a6d42e7dSPeter Dunlap 	return (ic->ic_transport_ops->it_negotiate_key_values(ic,
642a6d42e7dSPeter Dunlap 	    request_nvl, response_nvl, negotiated_nvl));
643a6d42e7dSPeter Dunlap }
644a6d42e7dSPeter Dunlap 
645a6d42e7dSPeter Dunlap /*
646a6d42e7dSPeter Dunlap  * idm_notice_key_values()
647a6d42e7dSPeter Dunlap  * Activate at the IDM level any parameters that have been negotiated.
648a6d42e7dSPeter Dunlap  * Passes the set of key value pairs to the transport for activation.
649a6d42e7dSPeter Dunlap  * This will be invoked as the connection is entering full-feature mode.
650a6d42e7dSPeter Dunlap  */
65130e7468fSPeter Dunlap void
652a6d42e7dSPeter Dunlap idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
653a6d42e7dSPeter Dunlap {
654a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
65530e7468fSPeter Dunlap 	ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl);
656a6d42e7dSPeter Dunlap }
657a6d42e7dSPeter Dunlap 
658*56261083SCharles Ting /*
659*56261083SCharles Ting  * idm_declare_key_values()
660*56261083SCharles Ting  * Activate an operational set of declarative parameters from the config_nvl,
661*56261083SCharles Ting  * and return the selected values in the outgoing_nvl.
662*56261083SCharles Ting  */
663*56261083SCharles Ting kv_status_t
664*56261083SCharles Ting idm_declare_key_values(idm_conn_t *ic, nvlist_t *config_nvl,
665*56261083SCharles Ting     nvlist_t *outgoing_nvl)
666*56261083SCharles Ting {
667*56261083SCharles Ting 	ASSERT(ic->ic_transport_ops != NULL);
668*56261083SCharles Ting 	return (ic->ic_transport_ops->it_declare_key_values(ic, config_nvl,
669*56261083SCharles Ting 	    outgoing_nvl));
670*56261083SCharles Ting }
671*56261083SCharles Ting 
672a6d42e7dSPeter Dunlap /*
673a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini
674a6d42e7dSPeter Dunlap  *
675a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Put_Data' operational primitive.
676a6d42e7dSPeter Dunlap  *
677a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
678a6d42e7dSPeter Dunlap  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
679a6d42e7dSPeter Dunlap  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
680a6d42e7dSPeter Dunlap  * transferred to the initiator associated with task 'idt'. The connection
681a6d42e7dSPeter Dunlap  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
682a6d42e7dSPeter Dunlap  * and the callback (idb->idb_buf_cb) at transfer completion are
683a6d42e7dSPeter Dunlap  * provided as input.
684a6d42e7dSPeter Dunlap  *
685a6d42e7dSPeter Dunlap  * This data transfer takes place transparently to the remote iSCSI layer,
686a6d42e7dSPeter Dunlap  * i.e. without its participation.
687a6d42e7dSPeter Dunlap  *
688a6d42e7dSPeter Dunlap  * Using sockets, IDM implements the data transfer by segmenting the data
689a6d42e7dSPeter Dunlap  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
690a6d42e7dSPeter Dunlap  * initiator. iSER performs the transfer using RDMA write.
691a6d42e7dSPeter Dunlap  *
692a6d42e7dSPeter Dunlap  */
693a6d42e7dSPeter Dunlap idm_status_t
694a6d42e7dSPeter Dunlap idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
695a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
696a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
697a6d42e7dSPeter Dunlap {
698a6d42e7dSPeter Dunlap 	idm_status_t rc;
699a6d42e7dSPeter Dunlap 
700a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
701a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
702a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
703a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
70430e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_start);
70530e7468fSPeter Dunlap 
70630e7468fSPeter Dunlap 	/*
70730e7468fSPeter Dunlap 	 * Buffer should not contain the pattern.  If the pattern is
70830e7468fSPeter Dunlap 	 * present then we've been asked to transmit initialized data
70930e7468fSPeter Dunlap 	 */
71030e7468fSPeter Dunlap 	IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT);
711a6d42e7dSPeter Dunlap 
712a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
713a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
714a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
715a6d42e7dSPeter Dunlap 		idt->idt_tx_to_ini_start++;
716a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
717a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
718a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
719a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
720a6d42e7dSPeter Dunlap 		    (idt, idb);
721a6d42e7dSPeter Dunlap 		return (rc);
722a6d42e7dSPeter Dunlap 
723a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
724a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
725a6d42e7dSPeter Dunlap 		/*
726a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
727a6d42e7dSPeter Dunlap 		 * is suspended
728a6d42e7dSPeter Dunlap 		 */
729a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
730a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
731a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
732a6d42e7dSPeter Dunlap 
733a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
734a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
735a6d42e7dSPeter Dunlap 		/*
736a6d42e7dSPeter Dunlap 		 * Once the task is aborted, any buffers added to the
737a6d42e7dSPeter Dunlap 		 * idt_inbufv will never get cleaned up, so just return
738a6d42e7dSPeter Dunlap 		 * SUCCESS.  The buffer should get cleaned up by the
739a6d42e7dSPeter Dunlap 		 * client or framework once task_aborted has completed.
740a6d42e7dSPeter Dunlap 		 */
741a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
742a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
743a6d42e7dSPeter Dunlap 
744a6d42e7dSPeter Dunlap 	default:
745a6d42e7dSPeter Dunlap 		ASSERT(0);
746a6d42e7dSPeter Dunlap 		break;
747a6d42e7dSPeter Dunlap 	}
748a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
749a6d42e7dSPeter Dunlap 
750a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
751a6d42e7dSPeter Dunlap }
752a6d42e7dSPeter Dunlap 
753a6d42e7dSPeter Dunlap /*
754a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini
755a6d42e7dSPeter Dunlap  *
756a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Get_Data' operational primitive.
757a6d42e7dSPeter Dunlap  *
758a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
759a6d42e7dSPeter Dunlap  * Datamover layer to retrieve certain data identified by the R2T PDU from the
760a6d42e7dSPeter Dunlap  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
761a6d42e7dSPeter Dunlap  * mapped to the respective buffer by the task tags (ITT & TTT).
762a6d42e7dSPeter Dunlap  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
763a6d42e7dSPeter Dunlap  * the callback (idb->idb_buf_cb) notification for data transfer completion are
764a6d42e7dSPeter Dunlap  * are provided as input.
765a6d42e7dSPeter Dunlap  *
766a6d42e7dSPeter Dunlap  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
767a6d42e7dSPeter Dunlap  * Datamover layer, the local and remote Datamover layers transparently bring
768a6d42e7dSPeter Dunlap  * about the data transfer requested by the R2T PDU, without the participation
769a6d42e7dSPeter Dunlap  * of the iSCSI layers.
770a6d42e7dSPeter Dunlap  *
771a6d42e7dSPeter Dunlap  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
772a6d42e7dSPeter Dunlap  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
773a6d42e7dSPeter Dunlap  *
774a6d42e7dSPeter Dunlap  */
775a6d42e7dSPeter Dunlap idm_status_t
776a6d42e7dSPeter Dunlap idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
777a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
778a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
779a6d42e7dSPeter Dunlap {
780a6d42e7dSPeter Dunlap 	idm_status_t rc;
781a6d42e7dSPeter Dunlap 
782a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
783a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
784a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
785a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
78630e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_start);
787a6d42e7dSPeter Dunlap 
788a6d42e7dSPeter Dunlap 	/*
789a6d42e7dSPeter Dunlap 	 * "In" buf list is for "Data In" PDU's, "Out" buf list is for
790a6d42e7dSPeter Dunlap 	 * "Data Out" PDU's
791a6d42e7dSPeter Dunlap 	 */
792a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
793a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
794a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
795a6d42e7dSPeter Dunlap 		idt->idt_rx_from_ini_start++;
796a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
797a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
798a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
799a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
800a6d42e7dSPeter Dunlap 		    (idt, idb);
801a6d42e7dSPeter Dunlap 		return (rc);
802a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
803a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
804a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
805a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
806a6d42e7dSPeter Dunlap 		/*
807a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
808a6d42e7dSPeter Dunlap 		 * is suspended
809a6d42e7dSPeter Dunlap 		 */
810a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
811a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
812a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
813a6d42e7dSPeter Dunlap 	default:
814a6d42e7dSPeter Dunlap 		ASSERT(0);
815a6d42e7dSPeter Dunlap 		break;
816a6d42e7dSPeter Dunlap 	}
817a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
818a6d42e7dSPeter Dunlap 
819a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
820a6d42e7dSPeter Dunlap }
821a6d42e7dSPeter Dunlap 
822a6d42e7dSPeter Dunlap /*
823a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini_done
824a6d42e7dSPeter Dunlap  *
825a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
826a6d42e7dSPeter Dunlap  * a call to transport_buf_tx_to_ini
827a6d42e7dSPeter Dunlap  *
828a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
829a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
830a6d42e7dSPeter Dunlap  */
831a6d42e7dSPeter Dunlap void
832a6d42e7dSPeter Dunlap idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
833a6d42e7dSPeter Dunlap {
834a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
835a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
836a6d42e7dSPeter Dunlap 	idb->idb_tx_thread = B_FALSE;
837a6d42e7dSPeter Dunlap 	idt->idt_tx_to_ini_done++;
83830e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_done);
839a6d42e7dSPeter Dunlap 
840a6d42e7dSPeter Dunlap 	/*
841a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
842a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
843a6d42e7dSPeter Dunlap 	 * to 0.
844a6d42e7dSPeter Dunlap 	 */
845a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
846a6d42e7dSPeter Dunlap 	idb->idb_status = status;
847a6d42e7dSPeter Dunlap 
848a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
849a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
85072cf3143Speter dunlap 		idt->idt_ic->ic_timestamp = ddi_get_lbolt();
851a6d42e7dSPeter Dunlap 		idm_buf_unbind_in_locked(idt, idb);
852a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
853a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
854a6d42e7dSPeter Dunlap 		return;
855a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
856a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
857a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
858a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
859a6d42e7dSPeter Dunlap 		/*
860a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
861a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
862a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
863a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
864a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
865a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
866a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
867a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
868a6d42e7dSPeter Dunlap 		 *
869a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
870a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
871a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
872a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want
873a6d42e7dSPeter Dunlap 		 * the client to here from us again after the call to
874a6d42e7dSPeter Dunlap 		 * client_task_aborted() but we don't want to give it a bunch
875a6d42e7dSPeter Dunlap 		 * of failed buffer transfers until we've called
876a6d42e7dSPeter Dunlap 		 * client_task_aborted().  Instead we'll just leave all the
877a6d42e7dSPeter Dunlap 		 * buffers bound and allow the client to cleanup.
878a6d42e7dSPeter Dunlap 		 */
879a6d42e7dSPeter Dunlap 		break;
880a6d42e7dSPeter Dunlap 	default:
881a6d42e7dSPeter Dunlap 		ASSERT(0);
882a6d42e7dSPeter Dunlap 	}
883a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
884a6d42e7dSPeter Dunlap }
885a6d42e7dSPeter Dunlap 
886a6d42e7dSPeter Dunlap /*
887a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini_done
888a6d42e7dSPeter Dunlap  *
889a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
890a6d42e7dSPeter Dunlap  * a call totransport_buf_tx_to_ini
891a6d42e7dSPeter Dunlap  *
892a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
893a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
894a6d42e7dSPeter Dunlap  */
895a6d42e7dSPeter Dunlap void
896a6d42e7dSPeter Dunlap idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
897a6d42e7dSPeter Dunlap {
898a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
899a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
900a6d42e7dSPeter Dunlap 	idt->idt_rx_from_ini_done++;
90130e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_done);
902a6d42e7dSPeter Dunlap 
903a6d42e7dSPeter Dunlap 	/*
904a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
905a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
906a6d42e7dSPeter Dunlap 	 * to 0.
907a6d42e7dSPeter Dunlap 	 */
908a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
909a6d42e7dSPeter Dunlap 	idb->idb_status = status;
910a6d42e7dSPeter Dunlap 
91130e7468fSPeter Dunlap 	if (status == IDM_STATUS_SUCCESS) {
91230e7468fSPeter Dunlap 		/*
91330e7468fSPeter Dunlap 		 * Buffer should not contain the pattern.  If it does then
91430e7468fSPeter Dunlap 		 * we did not get the data from the remote host.
91530e7468fSPeter Dunlap 		 */
91630e7468fSPeter Dunlap 		IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT);
91730e7468fSPeter Dunlap 	}
91830e7468fSPeter Dunlap 
919a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
920a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
92172cf3143Speter dunlap 		idt->idt_ic->ic_timestamp = ddi_get_lbolt();
922a6d42e7dSPeter Dunlap 		idm_buf_unbind_out_locked(idt, idb);
923a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
924a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
925a6d42e7dSPeter Dunlap 		return;
926a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
927a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
928a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
929a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
930a6d42e7dSPeter Dunlap 		/*
931a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
932a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
933a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
934a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
935a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
936a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
937a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
938a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
939a6d42e7dSPeter Dunlap 		 *
940a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
941a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
942a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
943a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want
944a6d42e7dSPeter Dunlap 		 * the client to here from us again after the call to
945a6d42e7dSPeter Dunlap 		 * client_task_aborted() but we don't want to give it a bunch
946a6d42e7dSPeter Dunlap 		 * of failed buffer transfers until we've called
947a6d42e7dSPeter Dunlap 		 * client_task_aborted().  Instead we'll just leave all the
948a6d42e7dSPeter Dunlap 		 * buffers bound and allow the client to cleanup.
949a6d42e7dSPeter Dunlap 		 */
950a6d42e7dSPeter Dunlap 		break;
951a6d42e7dSPeter Dunlap 	default:
952a6d42e7dSPeter Dunlap 		ASSERT(0);
953a6d42e7dSPeter Dunlap 	}
954a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
955a6d42e7dSPeter Dunlap }
956a6d42e7dSPeter Dunlap 
957a6d42e7dSPeter Dunlap /*
958a6d42e7dSPeter Dunlap  * idm_buf_alloc
959a6d42e7dSPeter Dunlap  *
960a6d42e7dSPeter Dunlap  * Allocates a buffer handle and registers it for use with the transport
961a6d42e7dSPeter Dunlap  * layer. If a buffer is not passed on bufptr, the buffer will be allocated
962a6d42e7dSPeter Dunlap  * as well as the handle.
963a6d42e7dSPeter Dunlap  *
964a6d42e7dSPeter Dunlap  * ic		- connection on which the buffer will be transferred
965a6d42e7dSPeter Dunlap  * bufptr	- allocate memory for buffer if NULL, else assign to buffer
966a6d42e7dSPeter Dunlap  * buflen	- length of buffer
967a6d42e7dSPeter Dunlap  *
968a6d42e7dSPeter Dunlap  * Returns idm_buf_t handle if successful, otherwise NULL
969a6d42e7dSPeter Dunlap  */
970a6d42e7dSPeter Dunlap idm_buf_t *
971a6d42e7dSPeter Dunlap idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen)
972a6d42e7dSPeter Dunlap {
973a6d42e7dSPeter Dunlap 	idm_buf_t	*buf = NULL;
974a6d42e7dSPeter Dunlap 	int		rc;
975a6d42e7dSPeter Dunlap 
976a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
977a6d42e7dSPeter Dunlap 	ASSERT(idm.idm_buf_cache != NULL);
978a6d42e7dSPeter Dunlap 	ASSERT(buflen > 0);
979a6d42e7dSPeter Dunlap 
980a6d42e7dSPeter Dunlap 	/* Don't allocate new buffers if we are not in FFP */
981a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
982a6d42e7dSPeter Dunlap 	if (!ic->ic_ffp) {
983a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
984a6d42e7dSPeter Dunlap 		return (NULL);
985a6d42e7dSPeter Dunlap 	}
986a6d42e7dSPeter Dunlap 
987a6d42e7dSPeter Dunlap 
988a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
989a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
990a6d42e7dSPeter Dunlap 
991a6d42e7dSPeter Dunlap 	buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP);
992a6d42e7dSPeter Dunlap 	if (buf == NULL) {
993a6d42e7dSPeter Dunlap 		idm_conn_rele(ic);
994a6d42e7dSPeter Dunlap 		return (NULL);
995a6d42e7dSPeter Dunlap 	}
996a6d42e7dSPeter Dunlap 
997a6d42e7dSPeter Dunlap 	buf->idb_ic		= ic;
998a6d42e7dSPeter Dunlap 	buf->idb_buflen		= buflen;
999a6d42e7dSPeter Dunlap 	buf->idb_exp_offset	= 0;
1000a6d42e7dSPeter Dunlap 	buf->idb_bufoffset	= 0;
1001a6d42e7dSPeter Dunlap 	buf->idb_xfer_len 	= 0;
1002a6d42e7dSPeter Dunlap 	buf->idb_magic		= IDM_BUF_MAGIC;
100330e7468fSPeter Dunlap 	buf->idb_in_transport	= B_FALSE;
100430e7468fSPeter Dunlap 	buf->idb_bufbcopy	= B_FALSE;
1005a6d42e7dSPeter Dunlap 
1006a6d42e7dSPeter Dunlap 	/*
1007a6d42e7dSPeter Dunlap 	 * If bufptr is NULL, we have an implicit request to allocate
1008a6d42e7dSPeter Dunlap 	 * memory for this IDM buffer handle and register it for use
1009a6d42e7dSPeter Dunlap 	 * with the transport. To simplify this, and to give more freedom
1010a6d42e7dSPeter Dunlap 	 * to the transport layer for it's own buffer management, both of
1011a6d42e7dSPeter Dunlap 	 * these actions will take place in the transport layer.
1012a6d42e7dSPeter Dunlap 	 * If bufptr is set, then the caller has allocated memory (or more
1013a6d42e7dSPeter Dunlap 	 * likely it's been passed from an upper layer), and we need only
1014a6d42e7dSPeter Dunlap 	 * register the buffer for use with the transport layer.
1015a6d42e7dSPeter Dunlap 	 */
1016a6d42e7dSPeter Dunlap 	if (bufptr == NULL) {
1017a6d42e7dSPeter Dunlap 		/*
1018a6d42e7dSPeter Dunlap 		 * Allocate a buffer from the transport layer (which
1019a6d42e7dSPeter Dunlap 		 * will also register the buffer for use).
1020a6d42e7dSPeter Dunlap 		 */
1021a6d42e7dSPeter Dunlap 		rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen);
1022a6d42e7dSPeter Dunlap 		if (rc != 0) {
1023a6d42e7dSPeter Dunlap 			idm_conn_rele(ic);
1024a6d42e7dSPeter Dunlap 			kmem_cache_free(idm.idm_buf_cache, buf);
1025a6d42e7dSPeter Dunlap 			return (NULL);
1026a6d42e7dSPeter Dunlap 		}
1027a6d42e7dSPeter Dunlap 		/* Set the bufalloc'd flag */
1028a6d42e7dSPeter Dunlap 		buf->idb_bufalloc = B_TRUE;
1029a6d42e7dSPeter Dunlap 	} else {
1030a6d42e7dSPeter Dunlap 		/*
103130e7468fSPeter Dunlap 		 * For large transfers, Set the passed bufptr into
103230e7468fSPeter Dunlap 		 * the buf handle, and register the handle with the
103330e7468fSPeter Dunlap 		 * transport layer. As memory registration with the
103430e7468fSPeter Dunlap 		 * transport layer is a time/cpu intensive operation,
103530e7468fSPeter Dunlap 		 * for small transfers (up to a pre-defined bcopy
103630e7468fSPeter Dunlap 		 * threshold), use pre-registered memory buffers
103730e7468fSPeter Dunlap 		 * and bcopy data at the appropriate time.
1038a6d42e7dSPeter Dunlap 		 */
1039a6d42e7dSPeter Dunlap 		buf->idb_buf = bufptr;
1040a6d42e7dSPeter Dunlap 
1041a6d42e7dSPeter Dunlap 		rc = ic->ic_transport_ops->it_buf_setup(buf);
1042a6d42e7dSPeter Dunlap 		if (rc != 0) {
1043a6d42e7dSPeter Dunlap 			idm_conn_rele(ic);
1044a6d42e7dSPeter Dunlap 			kmem_cache_free(idm.idm_buf_cache, buf);
1045a6d42e7dSPeter Dunlap 			return (NULL);
1046a6d42e7dSPeter Dunlap 		}
104730e7468fSPeter Dunlap 		/*
104830e7468fSPeter Dunlap 		 * The transport layer is now expected to set the idb_bufalloc
104930e7468fSPeter Dunlap 		 * correctly to indicate if resources have been allocated.
105030e7468fSPeter Dunlap 		 */
1051a6d42e7dSPeter Dunlap 	}
1052a6d42e7dSPeter Dunlap 
105330e7468fSPeter Dunlap 	IDM_BUFPAT_SET(buf);
1054a6d42e7dSPeter Dunlap 
105530e7468fSPeter Dunlap 	return (buf);
1056a6d42e7dSPeter Dunlap }
1057a6d42e7dSPeter Dunlap 
1058a6d42e7dSPeter Dunlap /*
1059a6d42e7dSPeter Dunlap  * idm_buf_free
1060a6d42e7dSPeter Dunlap  *
1061a6d42e7dSPeter Dunlap  * Release a buffer handle along with the associated buffer that was allocated
1062a6d42e7dSPeter Dunlap  * or assigned with idm_buf_alloc
1063a6d42e7dSPeter Dunlap  */
1064a6d42e7dSPeter Dunlap void
1065a6d42e7dSPeter Dunlap idm_buf_free(idm_buf_t *buf)
1066a6d42e7dSPeter Dunlap {
1067a6d42e7dSPeter Dunlap 	idm_conn_t *ic = buf->idb_ic;
1068a6d42e7dSPeter Dunlap 
1069a6d42e7dSPeter Dunlap 
1070a6d42e7dSPeter Dunlap 	buf->idb_task_binding	= NULL;
1071a6d42e7dSPeter Dunlap 
1072a6d42e7dSPeter Dunlap 	if (buf->idb_bufalloc) {
1073a6d42e7dSPeter Dunlap 		ic->ic_transport_ops->it_buf_free(buf);
1074a6d42e7dSPeter Dunlap 	} else {
1075a6d42e7dSPeter Dunlap 		ic->ic_transport_ops->it_buf_teardown(buf);
1076a6d42e7dSPeter Dunlap 	}
1077a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_buf_cache, buf);
1078a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
1079a6d42e7dSPeter Dunlap }
1080a6d42e7dSPeter Dunlap 
1081a6d42e7dSPeter Dunlap /*
1082a6d42e7dSPeter Dunlap  * idm_buf_bind_in
1083a6d42e7dSPeter Dunlap  *
1084a6d42e7dSPeter Dunlap  * This function associates a buffer with a task. This is only for use by the
1085a6d42e7dSPeter Dunlap  * iSCSI initiator that will have only one buffer per transfer direction
1086a6d42e7dSPeter Dunlap  *
1087a6d42e7dSPeter Dunlap  */
1088a6d42e7dSPeter Dunlap void
1089a6d42e7dSPeter Dunlap idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf)
1090a6d42e7dSPeter Dunlap {
1091a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1092a6d42e7dSPeter Dunlap 	idm_buf_bind_in_locked(idt, buf);
1093a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1094a6d42e7dSPeter Dunlap }
1095a6d42e7dSPeter Dunlap 
1096a6d42e7dSPeter Dunlap static void
1097a6d42e7dSPeter Dunlap idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf)
1098a6d42e7dSPeter Dunlap {
1099a6d42e7dSPeter Dunlap 	buf->idb_task_binding = idt;
1100a6d42e7dSPeter Dunlap 	buf->idb_ic = idt->idt_ic;
1101a6d42e7dSPeter Dunlap 	idm_listbuf_insert(&idt->idt_inbufv, buf);
1102a6d42e7dSPeter Dunlap }
1103a6d42e7dSPeter Dunlap 
1104a6d42e7dSPeter Dunlap void
1105a6d42e7dSPeter Dunlap idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf)
1106a6d42e7dSPeter Dunlap {
110730e7468fSPeter Dunlap 	/*
110830e7468fSPeter Dunlap 	 * For small transfers, the iSER transport delegates the IDM
110930e7468fSPeter Dunlap 	 * layer to bcopy the SCSI Write data for faster IOPS.
111030e7468fSPeter Dunlap 	 */
111130e7468fSPeter Dunlap 	if (buf->idb_bufbcopy == B_TRUE) {
111230e7468fSPeter Dunlap 
111330e7468fSPeter Dunlap 		bcopy(buf->idb_bufptr, buf->idb_buf, buf->idb_buflen);
111430e7468fSPeter Dunlap 	}
1115a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1116a6d42e7dSPeter Dunlap 	idm_buf_bind_out_locked(idt, buf);
1117a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1118a6d42e7dSPeter Dunlap }
1119a6d42e7dSPeter Dunlap 
1120a6d42e7dSPeter Dunlap static void
1121a6d42e7dSPeter Dunlap idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf)
1122a6d42e7dSPeter Dunlap {
1123a6d42e7dSPeter Dunlap 	buf->idb_task_binding = idt;
1124a6d42e7dSPeter Dunlap 	buf->idb_ic = idt->idt_ic;
1125a6d42e7dSPeter Dunlap 	idm_listbuf_insert(&idt->idt_outbufv, buf);
1126a6d42e7dSPeter Dunlap }
1127a6d42e7dSPeter Dunlap 
1128a6d42e7dSPeter Dunlap void
1129a6d42e7dSPeter Dunlap idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf)
1130a6d42e7dSPeter Dunlap {
113130e7468fSPeter Dunlap 	/*
113230e7468fSPeter Dunlap 	 * For small transfers, the iSER transport delegates the IDM
113330e7468fSPeter Dunlap 	 * layer to bcopy the SCSI Read data into the read buufer
113430e7468fSPeter Dunlap 	 * for faster IOPS.
113530e7468fSPeter Dunlap 	 */
113630e7468fSPeter Dunlap 	if (buf->idb_bufbcopy == B_TRUE) {
113730e7468fSPeter Dunlap 		bcopy(buf->idb_buf, buf->idb_bufptr, buf->idb_buflen);
113830e7468fSPeter Dunlap 	}
1139a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1140a6d42e7dSPeter Dunlap 	idm_buf_unbind_in_locked(idt, buf);
1141a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1142a6d42e7dSPeter Dunlap }
1143a6d42e7dSPeter Dunlap 
1144a6d42e7dSPeter Dunlap static void
1145a6d42e7dSPeter Dunlap idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf)
1146a6d42e7dSPeter Dunlap {
1147a6d42e7dSPeter Dunlap 	list_remove(&idt->idt_inbufv, buf);
1148a6d42e7dSPeter Dunlap }
1149a6d42e7dSPeter Dunlap 
1150a6d42e7dSPeter Dunlap void
1151a6d42e7dSPeter Dunlap idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf)
1152a6d42e7dSPeter Dunlap {
1153a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1154a6d42e7dSPeter Dunlap 	idm_buf_unbind_out_locked(idt, buf);
1155a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1156a6d42e7dSPeter Dunlap }
1157a6d42e7dSPeter Dunlap 
1158a6d42e7dSPeter Dunlap static void
1159a6d42e7dSPeter Dunlap idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf)
1160a6d42e7dSPeter Dunlap {
1161a6d42e7dSPeter Dunlap 	list_remove(&idt->idt_outbufv, buf);
1162a6d42e7dSPeter Dunlap }
1163a6d42e7dSPeter Dunlap 
1164a6d42e7dSPeter Dunlap /*
1165a6d42e7dSPeter Dunlap  * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the
1166a6d42e7dSPeter Dunlap  * iSCSI PDU
1167a6d42e7dSPeter Dunlap  */
1168a6d42e7dSPeter Dunlap idm_buf_t *
1169a6d42e7dSPeter Dunlap idm_buf_find(void *lbuf, size_t data_offset)
1170a6d42e7dSPeter Dunlap {
1171a6d42e7dSPeter Dunlap 	idm_buf_t	*idb;
1172a6d42e7dSPeter Dunlap 	list_t		*lst = (list_t *)lbuf;
1173a6d42e7dSPeter Dunlap 
1174a6d42e7dSPeter Dunlap 	/* iterate through the list to find the buffer */
1175a6d42e7dSPeter Dunlap 	for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) {
1176a6d42e7dSPeter Dunlap 
1177a6d42e7dSPeter Dunlap 		ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) ||
1178a6d42e7dSPeter Dunlap 		    (idb->idb_bufoffset == 0));
1179a6d42e7dSPeter Dunlap 
1180a6d42e7dSPeter Dunlap 		if ((data_offset >= idb->idb_bufoffset) &&
1181a6d42e7dSPeter Dunlap 		    (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) {
1182a6d42e7dSPeter Dunlap 
1183a6d42e7dSPeter Dunlap 			return (idb);
1184a6d42e7dSPeter Dunlap 		}
1185a6d42e7dSPeter Dunlap 	}
1186a6d42e7dSPeter Dunlap 
1187a6d42e7dSPeter Dunlap 	return (NULL);
1188a6d42e7dSPeter Dunlap }
1189a6d42e7dSPeter Dunlap 
119030e7468fSPeter Dunlap void
119130e7468fSPeter Dunlap idm_bufpat_set(idm_buf_t *idb)
119230e7468fSPeter Dunlap {
119330e7468fSPeter Dunlap 	idm_bufpat_t	*bufpat;
119430e7468fSPeter Dunlap 	int		len, i;
119530e7468fSPeter Dunlap 
119630e7468fSPeter Dunlap 	len = idb->idb_buflen;
119730e7468fSPeter Dunlap 	len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
119830e7468fSPeter Dunlap 
119930e7468fSPeter Dunlap 	bufpat = idb->idb_buf;
120030e7468fSPeter Dunlap 	for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
120130e7468fSPeter Dunlap 		bufpat->bufpat_idb = idb;
120230e7468fSPeter Dunlap 		bufpat->bufpat_bufmagic = IDM_BUF_MAGIC;
120330e7468fSPeter Dunlap 		bufpat->bufpat_offset = i;
120430e7468fSPeter Dunlap 		bufpat++;
120530e7468fSPeter Dunlap 	}
120630e7468fSPeter Dunlap }
120730e7468fSPeter Dunlap 
120830e7468fSPeter Dunlap boolean_t
120930e7468fSPeter Dunlap idm_bufpat_check(idm_buf_t *idb, int check_len, idm_bufpat_check_type_t type)
121030e7468fSPeter Dunlap {
121130e7468fSPeter Dunlap 	idm_bufpat_t	*bufpat;
121230e7468fSPeter Dunlap 	int		len, i;
121330e7468fSPeter Dunlap 
121430e7468fSPeter Dunlap 	len = (type == BP_CHECK_QUICK) ? sizeof (idm_bufpat_t) : check_len;
121530e7468fSPeter Dunlap 	len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t);
121630e7468fSPeter Dunlap 	ASSERT(len <= idb->idb_buflen);
121730e7468fSPeter Dunlap 	bufpat = idb->idb_buf;
121830e7468fSPeter Dunlap 
121930e7468fSPeter Dunlap 	/*
122030e7468fSPeter Dunlap 	 * Don't check the pattern in buffers that came from outside IDM
122130e7468fSPeter Dunlap 	 * (these will be buffers from the initiator that we opted not
122230e7468fSPeter Dunlap 	 * to double-buffer)
122330e7468fSPeter Dunlap 	 */
122430e7468fSPeter Dunlap 	if (!idb->idb_bufalloc)
122530e7468fSPeter Dunlap 		return (B_FALSE);
122630e7468fSPeter Dunlap 
122730e7468fSPeter Dunlap 	/*
122830e7468fSPeter Dunlap 	 * Return true if we find the pattern anywhere in the buffer
122930e7468fSPeter Dunlap 	 */
123030e7468fSPeter Dunlap 	for (i = 0; i < len; i += sizeof (idm_bufpat_t)) {
123130e7468fSPeter Dunlap 		if (BUFPAT_MATCH(bufpat, idb)) {
123230e7468fSPeter Dunlap 			IDM_CONN_LOG(CE_WARN, "idm_bufpat_check found: "
123330e7468fSPeter Dunlap 			    "idb %p bufpat %p "
123430e7468fSPeter Dunlap 			    "bufpat_idb=%p bufmagic=%08x offset=%08x",
123530e7468fSPeter Dunlap 			    (void *)idb, (void *)bufpat, bufpat->bufpat_idb,
123630e7468fSPeter Dunlap 			    bufpat->bufpat_bufmagic, bufpat->bufpat_offset);
123730e7468fSPeter Dunlap 			DTRACE_PROBE2(bufpat__pattern__found,
123830e7468fSPeter Dunlap 			    idm_buf_t *, idb, idm_bufpat_t *, bufpat);
123930e7468fSPeter Dunlap 			if (type == BP_CHECK_ASSERT) {
124030e7468fSPeter Dunlap 				ASSERT(0);
124130e7468fSPeter Dunlap 			}
124230e7468fSPeter Dunlap 			return (B_TRUE);
124330e7468fSPeter Dunlap 		}
124430e7468fSPeter Dunlap 		bufpat++;
124530e7468fSPeter Dunlap 	}
124630e7468fSPeter Dunlap 
124730e7468fSPeter Dunlap 	return (B_FALSE);
124830e7468fSPeter Dunlap }
124930e7468fSPeter Dunlap 
1250a6d42e7dSPeter Dunlap /*
1251a6d42e7dSPeter Dunlap  * idm_task_alloc
1252a6d42e7dSPeter Dunlap  *
1253a6d42e7dSPeter Dunlap  * This function will allocate a idm_task_t structure. A task tag is also
1254a6d42e7dSPeter Dunlap  * generated and saved in idt_tt. The task is not active.
1255a6d42e7dSPeter Dunlap  */
1256a6d42e7dSPeter Dunlap idm_task_t *
1257a6d42e7dSPeter Dunlap idm_task_alloc(idm_conn_t *ic)
1258a6d42e7dSPeter Dunlap {
1259a6d42e7dSPeter Dunlap 	idm_task_t	*idt;
1260a6d42e7dSPeter Dunlap 
1261a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1262a6d42e7dSPeter Dunlap 
1263a6d42e7dSPeter Dunlap 	/* Don't allocate new tasks if we are not in FFP */
1264a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1265a6d42e7dSPeter Dunlap 	if (!ic->ic_ffp) {
1266a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1267a6d42e7dSPeter Dunlap 		return (NULL);
1268a6d42e7dSPeter Dunlap 	}
1269a6d42e7dSPeter Dunlap 	idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP);
1270a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1271a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1272a6d42e7dSPeter Dunlap 		return (NULL);
1273a6d42e7dSPeter Dunlap 	}
1274a6d42e7dSPeter Dunlap 
1275a6d42e7dSPeter Dunlap 	ASSERT(list_is_empty(&idt->idt_inbufv));
1276a6d42e7dSPeter Dunlap 	ASSERT(list_is_empty(&idt->idt_outbufv));
1277a6d42e7dSPeter Dunlap 
1278a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
1279a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1280a6d42e7dSPeter Dunlap 
1281a6d42e7dSPeter Dunlap 	idt->idt_state		= TASK_IDLE;
1282a6d42e7dSPeter Dunlap 	idt->idt_ic		= ic;
1283a6d42e7dSPeter Dunlap 	idt->idt_private 	= NULL;
1284a6d42e7dSPeter Dunlap 	idt->idt_exp_datasn	= 0;
1285a6d42e7dSPeter Dunlap 	idt->idt_exp_rttsn	= 0;
1286a6d42e7dSPeter Dunlap 
1287a6d42e7dSPeter Dunlap 	return (idt);
1288a6d42e7dSPeter Dunlap }
1289a6d42e7dSPeter Dunlap 
1290a6d42e7dSPeter Dunlap /*
1291a6d42e7dSPeter Dunlap  * idm_task_start
1292a6d42e7dSPeter Dunlap  *
129330e7468fSPeter Dunlap  * Mark the task active and initialize some stats. The caller
1294a6d42e7dSPeter Dunlap  * sets up the idm_task_t structure with a prior call to idm_task_alloc().
1295a6d42e7dSPeter Dunlap  * The task service does not function as a task/work engine, it is the
1296a6d42e7dSPeter Dunlap  * responsibility of the initiator to start the data transfer and free the
1297a6d42e7dSPeter Dunlap  * resources.
1298a6d42e7dSPeter Dunlap  */
1299a6d42e7dSPeter Dunlap void
1300a6d42e7dSPeter Dunlap idm_task_start(idm_task_t *idt, uintptr_t handle)
1301a6d42e7dSPeter Dunlap {
1302a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
1303a6d42e7dSPeter Dunlap 
1304a6d42e7dSPeter Dunlap 	/* mark the task as ACTIVE */
1305a6d42e7dSPeter Dunlap 	idt->idt_state = TASK_ACTIVE;
1306a6d42e7dSPeter Dunlap 	idt->idt_client_handle = handle;
1307a6d42e7dSPeter Dunlap 	idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done =
130830e7468fSPeter Dunlap 	    idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done =
130930e7468fSPeter Dunlap 	    idt->idt_tx_bytes = idt->idt_rx_bytes = 0;
1310a6d42e7dSPeter Dunlap }
1311a6d42e7dSPeter Dunlap 
1312a6d42e7dSPeter Dunlap /*
1313a6d42e7dSPeter Dunlap  * idm_task_done
1314a6d42e7dSPeter Dunlap  *
131530e7468fSPeter Dunlap  * This function sets the state to indicate that the task is no longer active.
1316a6d42e7dSPeter Dunlap  */
1317a6d42e7dSPeter Dunlap void
1318a6d42e7dSPeter Dunlap idm_task_done(idm_task_t *idt)
1319a6d42e7dSPeter Dunlap {
1320a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
1321a6d42e7dSPeter Dunlap 
132230e7468fSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1323a6d42e7dSPeter Dunlap 	idt->idt_state = TASK_IDLE;
132430e7468fSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
132530e7468fSPeter Dunlap 
132630e7468fSPeter Dunlap 	/*
132730e7468fSPeter Dunlap 	 * Although unlikely it is possible for a reference to come in after
132830e7468fSPeter Dunlap 	 * the client has decided the task is over but before we've marked
132930e7468fSPeter Dunlap 	 * the task idle.  One specific unavoidable scenario is the case where
133030e7468fSPeter Dunlap 	 * received PDU with the matching ITT/TTT results in a successful
133130e7468fSPeter Dunlap 	 * lookup of this task.  We are at the mercy of the remote node in
133230e7468fSPeter Dunlap 	 * that case so we need to handle it.  Now that the task state
133330e7468fSPeter Dunlap 	 * has changed no more references will occur so a simple call to
133430e7468fSPeter Dunlap 	 * idm_refcnt_wait_ref should deal with the situation.
133530e7468fSPeter Dunlap 	 */
133630e7468fSPeter Dunlap 	idm_refcnt_wait_ref(&idt->idt_refcnt);
1337a6d42e7dSPeter Dunlap 	idm_refcnt_reset(&idt->idt_refcnt);
1338a6d42e7dSPeter Dunlap }
1339a6d42e7dSPeter Dunlap 
1340a6d42e7dSPeter Dunlap /*
1341a6d42e7dSPeter Dunlap  * idm_task_free
1342a6d42e7dSPeter Dunlap  *
1343a6d42e7dSPeter Dunlap  * This function will free the Task Tag and the memory allocated for the task
1344a6d42e7dSPeter Dunlap  * idm_task_done should be called prior to this call
1345a6d42e7dSPeter Dunlap  */
1346a6d42e7dSPeter Dunlap void
1347a6d42e7dSPeter Dunlap idm_task_free(idm_task_t *idt)
1348a6d42e7dSPeter Dunlap {
134930e7468fSPeter Dunlap 	idm_conn_t *ic;
1350a6d42e7dSPeter Dunlap 
1351a6d42e7dSPeter Dunlap 	ASSERT(idt != NULL);
135230e7468fSPeter Dunlap 	ASSERT(idt->idt_refcnt.ir_refcnt == 0);
1353a6d42e7dSPeter Dunlap 	ASSERT(idt->idt_state == TASK_IDLE);
1354a6d42e7dSPeter Dunlap 
135530e7468fSPeter Dunlap 	ic = idt->idt_ic;
135630e7468fSPeter Dunlap 
1357a6d42e7dSPeter Dunlap 	/*
1358a6d42e7dSPeter Dunlap 	 * It's possible for items to still be in the idt_inbufv list if
1359a6d42e7dSPeter Dunlap 	 * they were added after idm_task_cleanup was called.  We rely on
1360a6d42e7dSPeter Dunlap 	 * STMF to free all buffers associated with the task however STMF
1361a6d42e7dSPeter Dunlap 	 * doesn't know that we have this reference to the buffers.
1362a6d42e7dSPeter Dunlap 	 * Use list_create so that we don't end up with stale references
1363a6d42e7dSPeter Dunlap 	 * to these buffers.
1364a6d42e7dSPeter Dunlap 	 */
1365a6d42e7dSPeter Dunlap 	list_create(&idt->idt_inbufv, sizeof (idm_buf_t),
1366a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1367a6d42e7dSPeter Dunlap 	list_create(&idt->idt_outbufv, sizeof (idm_buf_t),
1368a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1369a6d42e7dSPeter Dunlap 
1370a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_task_cache, idt);
1371a6d42e7dSPeter Dunlap 
1372a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
1373a6d42e7dSPeter Dunlap }
1374a6d42e7dSPeter Dunlap 
1375a6d42e7dSPeter Dunlap /*
137630e7468fSPeter Dunlap  * idm_task_find_common
137730e7468fSPeter Dunlap  *	common code for idm_task_find() and idm_task_find_and_complete()
1378a6d42e7dSPeter Dunlap  */
1379a6d42e7dSPeter Dunlap /*ARGSUSED*/
138030e7468fSPeter Dunlap static idm_task_t *
138130e7468fSPeter Dunlap idm_task_find_common(idm_conn_t *ic, uint32_t itt, uint32_t ttt,
138230e7468fSPeter Dunlap     boolean_t complete)
1383a6d42e7dSPeter Dunlap {
1384a6d42e7dSPeter Dunlap 	uint32_t	tt, client_handle;
1385a6d42e7dSPeter Dunlap 	idm_task_t	*idt;
1386a6d42e7dSPeter Dunlap 
1387a6d42e7dSPeter Dunlap 	/*
1388a6d42e7dSPeter Dunlap 	 * Must match both itt and ttt.  The table is indexed by itt
1389a6d42e7dSPeter Dunlap 	 * for initiator connections and ttt for target connections.
1390a6d42e7dSPeter Dunlap 	 */
1391a6d42e7dSPeter Dunlap 	if (IDM_CONN_ISTGT(ic)) {
1392a6d42e7dSPeter Dunlap 		tt = ttt;
1393a6d42e7dSPeter Dunlap 		client_handle = itt;
1394a6d42e7dSPeter Dunlap 	} else {
1395a6d42e7dSPeter Dunlap 		tt = itt;
1396a6d42e7dSPeter Dunlap 		client_handle = ttt;
1397a6d42e7dSPeter Dunlap 	}
1398a6d42e7dSPeter Dunlap 
1399a6d42e7dSPeter Dunlap 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1400a6d42e7dSPeter Dunlap 	if (tt >= idm.idm_taskid_max) {
1401a6d42e7dSPeter Dunlap 		rw_exit(&idm.idm_taskid_table_lock);
1402a6d42e7dSPeter Dunlap 		return (NULL);
1403a6d42e7dSPeter Dunlap 	}
1404a6d42e7dSPeter Dunlap 
1405a6d42e7dSPeter Dunlap 	idt = idm.idm_taskid_table[tt];
1406a6d42e7dSPeter Dunlap 
1407a6d42e7dSPeter Dunlap 	if (idt != NULL) {
1408a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1409a6d42e7dSPeter Dunlap 		if ((idt->idt_state != TASK_ACTIVE) ||
141030e7468fSPeter Dunlap 		    (idt->idt_ic != ic) ||
1411a6d42e7dSPeter Dunlap 		    (IDM_CONN_ISTGT(ic) &&
1412a6d42e7dSPeter Dunlap 		    (idt->idt_client_handle != client_handle))) {
1413a6d42e7dSPeter Dunlap 			/*
141430e7468fSPeter Dunlap 			 * Task doesn't match or task is aborting and
141530e7468fSPeter Dunlap 			 * we don't want any more references.
1416a6d42e7dSPeter Dunlap 			 */
141730e7468fSPeter Dunlap 			if ((idt->idt_ic != ic) &&
141830e7468fSPeter Dunlap 			    (idt->idt_state == TASK_ACTIVE) &&
141930e7468fSPeter Dunlap 			    (IDM_CONN_ISINI(ic) || idt->idt_client_handle ==
142030e7468fSPeter Dunlap 			    client_handle)) {
142130e7468fSPeter Dunlap 				IDM_CONN_LOG(CE_WARN,
142230e7468fSPeter Dunlap 				"idm_task_find: wrong connection %p != %p",
142330e7468fSPeter Dunlap 				    (void *)ic, (void *)idt->idt_ic);
142430e7468fSPeter Dunlap 			}
1425a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1426a6d42e7dSPeter Dunlap 			rw_exit(&idm.idm_taskid_table_lock);
1427a6d42e7dSPeter Dunlap 			return (NULL);
1428a6d42e7dSPeter Dunlap 		}
1429a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
143030e7468fSPeter Dunlap 		/*
143130e7468fSPeter Dunlap 		 * Set the task state to TASK_COMPLETE so it can no longer
143230e7468fSPeter Dunlap 		 * be found or aborted.
143330e7468fSPeter Dunlap 		 */
143430e7468fSPeter Dunlap 		if (B_TRUE == complete)
143530e7468fSPeter Dunlap 			idt->idt_state = TASK_COMPLETE;
1436a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1437a6d42e7dSPeter Dunlap 	}
1438a6d42e7dSPeter Dunlap 	rw_exit(&idm.idm_taskid_table_lock);
1439a6d42e7dSPeter Dunlap 
1440a6d42e7dSPeter Dunlap 	return (idt);
1441a6d42e7dSPeter Dunlap }
1442a6d42e7dSPeter Dunlap 
144330e7468fSPeter Dunlap /*
144430e7468fSPeter Dunlap  * This function looks up a task by task tag.
144530e7468fSPeter Dunlap  */
144630e7468fSPeter Dunlap idm_task_t *
144730e7468fSPeter Dunlap idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
144830e7468fSPeter Dunlap {
144930e7468fSPeter Dunlap 	return (idm_task_find_common(ic, itt, ttt, B_FALSE));
145030e7468fSPeter Dunlap }
145130e7468fSPeter Dunlap 
145230e7468fSPeter Dunlap /*
145330e7468fSPeter Dunlap  * This function looks up a task by task tag. If found, the task state
145430e7468fSPeter Dunlap  * is atomically set to TASK_COMPLETE so it can longer be found or aborted.
145530e7468fSPeter Dunlap  */
145630e7468fSPeter Dunlap idm_task_t *
145730e7468fSPeter Dunlap idm_task_find_and_complete(idm_conn_t *ic, uint32_t itt, uint32_t ttt)
145830e7468fSPeter Dunlap {
145930e7468fSPeter Dunlap 	return (idm_task_find_common(ic, itt, ttt, B_TRUE));
146030e7468fSPeter Dunlap }
146130e7468fSPeter Dunlap 
1462a6d42e7dSPeter Dunlap /*
1463a6d42e7dSPeter Dunlap  * idm_task_find_by_handle
1464a6d42e7dSPeter Dunlap  *
1465a6d42e7dSPeter Dunlap  * This function looks up a task by the client-private idt_client_handle.
1466a6d42e7dSPeter Dunlap  *
1467a6d42e7dSPeter Dunlap  * This function should NEVER be called in the performance path.  It is
1468a6d42e7dSPeter Dunlap  * intended strictly for error recovery/task management.
1469a6d42e7dSPeter Dunlap  */
1470a6d42e7dSPeter Dunlap /*ARGSUSED*/
1471a6d42e7dSPeter Dunlap void *
1472a6d42e7dSPeter Dunlap idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle)
1473a6d42e7dSPeter Dunlap {
1474a6d42e7dSPeter Dunlap 	idm_task_t	*idt = NULL;
1475a6d42e7dSPeter Dunlap 	int		idx = 0;
1476a6d42e7dSPeter Dunlap 
1477a6d42e7dSPeter Dunlap 	rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1478a6d42e7dSPeter Dunlap 
1479a6d42e7dSPeter Dunlap 	for (idx = 0; idx < idm.idm_taskid_max; idx++) {
1480a6d42e7dSPeter Dunlap 		idt = idm.idm_taskid_table[idx];
1481a6d42e7dSPeter Dunlap 
1482a6d42e7dSPeter Dunlap 		if (idt == NULL)
1483a6d42e7dSPeter Dunlap 			continue;
1484a6d42e7dSPeter Dunlap 
1485a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1486a6d42e7dSPeter Dunlap 
1487a6d42e7dSPeter Dunlap 		if (idt->idt_state != TASK_ACTIVE) {
1488a6d42e7dSPeter Dunlap 			/*
1489a6d42e7dSPeter Dunlap 			 * Task is either in suspend, abort, or already
1490a6d42e7dSPeter Dunlap 			 * complete.
1491a6d42e7dSPeter Dunlap 			 */
1492a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1493a6d42e7dSPeter Dunlap 			continue;
1494a6d42e7dSPeter Dunlap 		}
1495a6d42e7dSPeter Dunlap 
1496a6d42e7dSPeter Dunlap 		if (idt->idt_client_handle == handle) {
1497a6d42e7dSPeter Dunlap 			idm_task_hold(idt);
1498a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1499a6d42e7dSPeter Dunlap 			break;
1500a6d42e7dSPeter Dunlap 		}
1501a6d42e7dSPeter Dunlap 
1502a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1503a6d42e7dSPeter Dunlap 	}
1504a6d42e7dSPeter Dunlap 
1505a6d42e7dSPeter Dunlap 	rw_exit(&idm.idm_taskid_table_lock);
1506a6d42e7dSPeter Dunlap 
1507a6d42e7dSPeter Dunlap 	if ((idt == NULL) || (idx == idm.idm_taskid_max))
1508a6d42e7dSPeter Dunlap 		return (NULL);
1509a6d42e7dSPeter Dunlap 
1510a6d42e7dSPeter Dunlap 	return (idt->idt_private);
1511a6d42e7dSPeter Dunlap }
1512a6d42e7dSPeter Dunlap 
1513a6d42e7dSPeter Dunlap void
1514a6d42e7dSPeter Dunlap idm_task_hold(idm_task_t *idt)
1515a6d42e7dSPeter Dunlap {
1516a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&idt->idt_refcnt);
1517a6d42e7dSPeter Dunlap }
1518a6d42e7dSPeter Dunlap 
1519a6d42e7dSPeter Dunlap void
1520a6d42e7dSPeter Dunlap idm_task_rele(idm_task_t *idt)
1521a6d42e7dSPeter Dunlap {
1522a6d42e7dSPeter Dunlap 	idm_refcnt_rele(&idt->idt_refcnt);
1523a6d42e7dSPeter Dunlap }
1524a6d42e7dSPeter Dunlap 
1525a6d42e7dSPeter Dunlap void
1526a6d42e7dSPeter Dunlap idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
1527a6d42e7dSPeter Dunlap {
1528a6d42e7dSPeter Dunlap 	idm_task_t	*task;
1529a6d42e7dSPeter Dunlap 	int		idx;
1530a6d42e7dSPeter Dunlap 
1531a6d42e7dSPeter Dunlap 	/*
1532a6d42e7dSPeter Dunlap 	 * Passing NULL as the task indicates that all tasks
1533a6d42e7dSPeter Dunlap 	 * for this connection should be aborted.
1534a6d42e7dSPeter Dunlap 	 */
1535a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1536a6d42e7dSPeter Dunlap 		/*
1537a6d42e7dSPeter Dunlap 		 * Only the connection state machine should ask for
1538a6d42e7dSPeter Dunlap 		 * all tasks to abort and this should never happen in FFP.
1539a6d42e7dSPeter Dunlap 		 */
1540a6d42e7dSPeter Dunlap 		ASSERT(!ic->ic_ffp);
1541a6d42e7dSPeter Dunlap 		rw_enter(&idm.idm_taskid_table_lock, RW_READER);
1542a6d42e7dSPeter Dunlap 		for (idx = 0; idx < idm.idm_taskid_max; idx++) {
1543a6d42e7dSPeter Dunlap 			task = idm.idm_taskid_table[idx];
154430e7468fSPeter Dunlap 			if (task == NULL)
154530e7468fSPeter Dunlap 				continue;
154630e7468fSPeter Dunlap 			mutex_enter(&task->idt_mutex);
154730e7468fSPeter Dunlap 			if ((task->idt_state != TASK_IDLE) &&
154830e7468fSPeter Dunlap 			    (task->idt_state != TASK_COMPLETE) &&
1549a6d42e7dSPeter Dunlap 			    (task->idt_ic == ic)) {
1550a6d42e7dSPeter Dunlap 				rw_exit(&idm.idm_taskid_table_lock);
1551a6d42e7dSPeter Dunlap 				idm_task_abort_one(ic, task, abort_type);
1552a6d42e7dSPeter Dunlap 				rw_enter(&idm.idm_taskid_table_lock, RW_READER);
155330e7468fSPeter Dunlap 			} else
155430e7468fSPeter Dunlap 				mutex_exit(&task->idt_mutex);
1555a6d42e7dSPeter Dunlap 		}
1556a6d42e7dSPeter Dunlap 		rw_exit(&idm.idm_taskid_table_lock);
1557a6d42e7dSPeter Dunlap 	} else {
155830e7468fSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1559a6d42e7dSPeter Dunlap 		idm_task_abort_one(ic, idt, abort_type);
1560a6d42e7dSPeter Dunlap 	}
1561a6d42e7dSPeter Dunlap }
1562a6d42e7dSPeter Dunlap 
1563a6d42e7dSPeter Dunlap static void
1564a6d42e7dSPeter Dunlap idm_task_abort_unref_cb(void *ref)
1565a6d42e7dSPeter Dunlap {
1566a6d42e7dSPeter Dunlap 	idm_task_t *idt = ref;
1567a6d42e7dSPeter Dunlap 
1568a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1569a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
1570a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
1571a6d42e7dSPeter Dunlap 		idt->idt_state = TASK_SUSPENDED;
1572a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1573a6d42e7dSPeter Dunlap 		idm_task_aborted(idt, IDM_STATUS_SUSPENDED);
1574a6d42e7dSPeter Dunlap 		return;
1575a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
1576a6d42e7dSPeter Dunlap 		idt->idt_state = TASK_ABORTED;
1577a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1578a6d42e7dSPeter Dunlap 		idm_task_aborted(idt, IDM_STATUS_ABORTED);
1579a6d42e7dSPeter Dunlap 		return;
1580a6d42e7dSPeter Dunlap 	default:
1581a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1582a6d42e7dSPeter Dunlap 		ASSERT(0);
1583a6d42e7dSPeter Dunlap 		break;
1584a6d42e7dSPeter Dunlap 	}
1585a6d42e7dSPeter Dunlap }
1586a6d42e7dSPeter Dunlap 
158730e7468fSPeter Dunlap /*
158830e7468fSPeter Dunlap  * Abort the idm task.
158930e7468fSPeter Dunlap  *    Caller must hold the task mutex, which will be released before return
159030e7468fSPeter Dunlap  */
1591a6d42e7dSPeter Dunlap static void
1592a6d42e7dSPeter Dunlap idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
1593a6d42e7dSPeter Dunlap {
1594a6d42e7dSPeter Dunlap 	/* Caller must hold connection mutex */
159530e7468fSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
1596a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
1597a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
1598a6d42e7dSPeter Dunlap 		switch (abort_type) {
1599a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1600a6d42e7dSPeter Dunlap 			/* Call transport to release any resources */
1601a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_SUSPENDING;
1602a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1603a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_free_task_rsrc(idt);
1604a6d42e7dSPeter Dunlap 
1605a6d42e7dSPeter Dunlap 			/*
1606a6d42e7dSPeter Dunlap 			 * Wait for outstanding references.  When all
1607a6d42e7dSPeter Dunlap 			 * references are released the callback will call
1608a6d42e7dSPeter Dunlap 			 * idm_task_aborted().
1609a6d42e7dSPeter Dunlap 			 */
1610a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1611a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1612a6d42e7dSPeter Dunlap 			return;
1613a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1614a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1615a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1616a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1617a6d42e7dSPeter Dunlap 			ic->ic_transport_ops->it_free_task_rsrc(idt);
1618a6d42e7dSPeter Dunlap 
1619a6d42e7dSPeter Dunlap 			/*
1620a6d42e7dSPeter Dunlap 			 * Wait for outstanding references.  When all
1621a6d42e7dSPeter Dunlap 			 * references are released the callback will call
1622a6d42e7dSPeter Dunlap 			 * idm_task_aborted().
1623a6d42e7dSPeter Dunlap 			 */
1624a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1625a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1626a6d42e7dSPeter Dunlap 			return;
1627a6d42e7dSPeter Dunlap 		default:
1628a6d42e7dSPeter Dunlap 			ASSERT(0);
1629a6d42e7dSPeter Dunlap 		}
1630a6d42e7dSPeter Dunlap 		break;
1631a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
1632a6d42e7dSPeter Dunlap 		/* Already called transport_free_task_rsrc(); */
1633a6d42e7dSPeter Dunlap 		switch (abort_type) {
1634a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1635a6d42e7dSPeter Dunlap 			/* Already doing it */
1636a6d42e7dSPeter Dunlap 			break;
1637a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1638a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1639a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1640a6d42e7dSPeter Dunlap 			break;
1641a6d42e7dSPeter Dunlap 		default:
1642a6d42e7dSPeter Dunlap 			ASSERT(0);
1643a6d42e7dSPeter Dunlap 		}
1644a6d42e7dSPeter Dunlap 		break;
1645a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
1646a6d42e7dSPeter Dunlap 		/* Already called transport_free_task_rsrc(); */
1647a6d42e7dSPeter Dunlap 		switch (abort_type) {
1648a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1649a6d42e7dSPeter Dunlap 			/* Already doing it */
1650a6d42e7dSPeter Dunlap 			break;
1651a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1652a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1653a6d42e7dSPeter Dunlap 			idt->idt_state = TASK_ABORTING;
1654a6d42e7dSPeter Dunlap 			mutex_exit(&idt->idt_mutex);
1655a6d42e7dSPeter Dunlap 
1656a6d42e7dSPeter Dunlap 			/*
1657a6d42e7dSPeter Dunlap 			 * We could probably call idm_task_aborted directly
1658a6d42e7dSPeter Dunlap 			 * here but we may be holding the conn lock. It's
1659a6d42e7dSPeter Dunlap 			 * easier to just switch contexts.  Even though
1660a6d42e7dSPeter Dunlap 			 * we shouldn't really have any references we'll
1661a6d42e7dSPeter Dunlap 			 * set the state to TASK_ABORTING instead of
1662a6d42e7dSPeter Dunlap 			 * TASK_ABORTED so we can use the same code path.
1663a6d42e7dSPeter Dunlap 			 */
1664a6d42e7dSPeter Dunlap 			idm_refcnt_async_wait_ref(&idt->idt_refcnt,
1665a6d42e7dSPeter Dunlap 			    &idm_task_abort_unref_cb);
1666a6d42e7dSPeter Dunlap 			return;
1667a6d42e7dSPeter Dunlap 		default:
1668a6d42e7dSPeter Dunlap 			ASSERT(0);
1669a6d42e7dSPeter Dunlap 		}
1670a6d42e7dSPeter Dunlap 		break;
1671a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
1672a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
1673a6d42e7dSPeter Dunlap 		switch (abort_type) {
1674a6d42e7dSPeter Dunlap 		case AT_INTERNAL_SUSPEND:
1675a6d42e7dSPeter Dunlap 			/* We're already past this point... */
1676a6d42e7dSPeter Dunlap 		case AT_INTERNAL_ABORT:
1677a6d42e7dSPeter Dunlap 		case AT_TASK_MGMT_ABORT:
1678a6d42e7dSPeter Dunlap 			/* Already doing it */
1679a6d42e7dSPeter Dunlap 			break;
1680a6d42e7dSPeter Dunlap 		default:
1681a6d42e7dSPeter Dunlap 			ASSERT(0);
1682a6d42e7dSPeter Dunlap 		}
1683a6d42e7dSPeter Dunlap 		break;
1684a6d42e7dSPeter Dunlap 	case TASK_COMPLETE:
1685a6d42e7dSPeter Dunlap 		/*
1686a6d42e7dSPeter Dunlap 		 * In this case, let it go.  The status has already been
1687a6d42e7dSPeter Dunlap 		 * sent (which may or may not get successfully transmitted)
1688a6d42e7dSPeter Dunlap 		 * and we don't want to end up in a race between completing
1689a6d42e7dSPeter Dunlap 		 * the status PDU and marking the task suspended.
1690a6d42e7dSPeter Dunlap 		 */
1691a6d42e7dSPeter Dunlap 		break;
1692a6d42e7dSPeter Dunlap 	default:
1693a6d42e7dSPeter Dunlap 		ASSERT(0);
1694a6d42e7dSPeter Dunlap 	}
1695a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1696a6d42e7dSPeter Dunlap }
1697a6d42e7dSPeter Dunlap 
1698a6d42e7dSPeter Dunlap static void
1699a6d42e7dSPeter Dunlap idm_task_aborted(idm_task_t *idt, idm_status_t status)
1700a6d42e7dSPeter Dunlap {
1701a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status);
1702a6d42e7dSPeter Dunlap }
1703a6d42e7dSPeter Dunlap 
1704a6d42e7dSPeter Dunlap void
1705a6d42e7dSPeter Dunlap idm_task_cleanup(idm_task_t *idt)
1706a6d42e7dSPeter Dunlap {
1707a6d42e7dSPeter Dunlap 	idm_buf_t *idb, *next_idb;
1708a6d42e7dSPeter Dunlap 	list_t		tmp_buflist;
1709a6d42e7dSPeter Dunlap 	ASSERT((idt->idt_state == TASK_SUSPENDED) ||
1710a6d42e7dSPeter Dunlap 	    (idt->idt_state == TASK_ABORTED));
1711a6d42e7dSPeter Dunlap 
1712a6d42e7dSPeter Dunlap 	list_create(&tmp_buflist, sizeof (idm_buf_t),
1713a6d42e7dSPeter Dunlap 	    offsetof(idm_buf_t, idb_buflink));
1714a6d42e7dSPeter Dunlap 
1715a6d42e7dSPeter Dunlap 	/*
1716a6d42e7dSPeter Dunlap 	 * Remove all the buffers from the task and add them to a
1717a6d42e7dSPeter Dunlap 	 * temporary local list -- we do this so that we can hold
1718a6d42e7dSPeter Dunlap 	 * the task lock and prevent the task from going away if
1719a6d42e7dSPeter Dunlap 	 * the client decides to call idm_task_done/idm_task_free.
1720a6d42e7dSPeter Dunlap 	 * This could happen during abort in iscsit.
1721a6d42e7dSPeter Dunlap 	 */
1722a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1723a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_inbufv);
1724a6d42e7dSPeter Dunlap 	    idb != NULL;
1725a6d42e7dSPeter Dunlap 	    idb = next_idb) {
1726a6d42e7dSPeter Dunlap 		next_idb = list_next(&idt->idt_inbufv, idb);
1727a6d42e7dSPeter Dunlap 		idm_buf_unbind_in_locked(idt, idb);
1728a6d42e7dSPeter Dunlap 		list_insert_tail(&tmp_buflist, idb);
1729a6d42e7dSPeter Dunlap 	}
1730a6d42e7dSPeter Dunlap 
1731a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_outbufv);
1732a6d42e7dSPeter Dunlap 	    idb != NULL;
1733a6d42e7dSPeter Dunlap 	    idb = next_idb) {
1734a6d42e7dSPeter Dunlap 		next_idb = list_next(&idt->idt_outbufv, idb);
1735a6d42e7dSPeter Dunlap 		idm_buf_unbind_out_locked(idt, idb);
1736a6d42e7dSPeter Dunlap 		list_insert_tail(&tmp_buflist, idb);
1737a6d42e7dSPeter Dunlap 	}
1738a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1739a6d42e7dSPeter Dunlap 
1740a6d42e7dSPeter Dunlap 	for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) {
1741a6d42e7dSPeter Dunlap 		next_idb = list_next(&tmp_buflist, idb);
1742a6d42e7dSPeter Dunlap 		list_remove(&tmp_buflist, idb);
1743a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED);
1744a6d42e7dSPeter Dunlap 	}
1745a6d42e7dSPeter Dunlap 	list_destroy(&tmp_buflist);
1746a6d42e7dSPeter Dunlap }
1747a6d42e7dSPeter Dunlap 
1748a6d42e7dSPeter Dunlap 
1749a6d42e7dSPeter Dunlap /*
1750a6d42e7dSPeter Dunlap  * idm_pdu_tx
1751a6d42e7dSPeter Dunlap  *
1752a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Send_Control' operational primitive.
1753a6d42e7dSPeter Dunlap  * This function is invoked by an initiator iSCSI layer requesting the transfer
1754a6d42e7dSPeter Dunlap  * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
1755a6d42e7dSPeter Dunlap  * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover
1756a6d42e7dSPeter Dunlap  * layer to the peer iSCSI layer in the remote iSCSI node. The connection info
1757a6d42e7dSPeter Dunlap  * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size
1758a6d42e7dSPeter Dunlap  * are provided as input.
1759a6d42e7dSPeter Dunlap  *
1760a6d42e7dSPeter Dunlap  */
1761a6d42e7dSPeter Dunlap void
1762a6d42e7dSPeter Dunlap idm_pdu_tx(idm_pdu_t *pdu)
1763a6d42e7dSPeter Dunlap {
1764a6d42e7dSPeter Dunlap 	idm_conn_t		*ic = pdu->isp_ic;
1765a6d42e7dSPeter Dunlap 	iscsi_async_evt_hdr_t	*async_evt;
1766a6d42e7dSPeter Dunlap 
1767a6d42e7dSPeter Dunlap 	/*
1768a6d42e7dSPeter Dunlap 	 * If we are in full-featured mode then route SCSI-related
1769a6d42e7dSPeter Dunlap 	 * commands to the appropriate function vector without checking
1770a6d42e7dSPeter Dunlap 	 * the connection state.  We will only be in full-feature mode
1771a6d42e7dSPeter Dunlap 	 * when we are in an acceptable state for SCSI PDU's.
1772a6d42e7dSPeter Dunlap 	 *
1773a6d42e7dSPeter Dunlap 	 * We also need to ensure that there are no PDU events outstanding
1774a6d42e7dSPeter Dunlap 	 * on the state machine.  Any non-SCSI PDU's received in full-feature
1775a6d42e7dSPeter Dunlap 	 * mode will result in PDU events and until these have been handled
1776a6d42e7dSPeter Dunlap 	 * we need to route all PDU's through the state machine as PDU
1777a6d42e7dSPeter Dunlap 	 * events to maintain ordering.
1778a6d42e7dSPeter Dunlap 	 *
1779a6d42e7dSPeter Dunlap 	 * Note that IDM cannot enter FFP mode until it processes in
1780a6d42e7dSPeter Dunlap 	 * its state machine the last xmit of the login process.
1781a6d42e7dSPeter Dunlap 	 * Hence, checking the IDM_PDU_LOGIN_TX flag here would be
1782a6d42e7dSPeter Dunlap 	 * superfluous.
1783a6d42e7dSPeter Dunlap 	 */
1784a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
1785a6d42e7dSPeter Dunlap 	if (ic->ic_ffp && (ic->ic_pdu_events == 0)) {
1786a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
1787a6d42e7dSPeter Dunlap 		switch (IDM_PDU_OPCODE(pdu)) {
1788a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_RSP:
1789a6d42e7dSPeter Dunlap 			/* Target only */
1790a668b114SPriya Krishnan 			DTRACE_ISCSI_2(scsi__response, idm_conn_t *, ic,
1791a668b114SPriya Krishnan 			    iscsi_scsi_rsp_hdr_t *,
1792a668b114SPriya Krishnan 			    (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr);
1793a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1794a6d42e7dSPeter Dunlap 			return;
1795a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_TASK_MGT_RSP:
1796a6d42e7dSPeter Dunlap 			/* Target only */
1797a668b114SPriya Krishnan 			DTRACE_ISCSI_2(task__response, idm_conn_t *, ic,
1798a668b114SPriya Krishnan 			    iscsi_text_rsp_hdr_t *,
1799a668b114SPriya Krishnan 			    (iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
1800a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1801a6d42e7dSPeter Dunlap 			return;
1802a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_DATA_RSP:
1803a6d42e7dSPeter Dunlap 			/* Target only */
1804a668b114SPriya Krishnan 			DTRACE_ISCSI_2(data__send, idm_conn_t *, ic,
1805a668b114SPriya Krishnan 			    iscsi_data_rsp_hdr_t *,
1806a668b114SPriya Krishnan 			    (iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
1807a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1808a6d42e7dSPeter Dunlap 			return;
1809a6d42e7dSPeter Dunlap 		case ISCSI_OP_RTT_RSP:
1810a6d42e7dSPeter Dunlap 			/* Target only */
1811a668b114SPriya Krishnan 			DTRACE_ISCSI_2(data__request, idm_conn_t *, ic,
1812a668b114SPriya Krishnan 			    iscsi_rtt_hdr_t *,
1813a668b114SPriya Krishnan 			    (iscsi_rtt_hdr_t *)pdu->isp_hdr);
1814a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1815a6d42e7dSPeter Dunlap 			return;
1816a6d42e7dSPeter Dunlap 		case ISCSI_OP_NOOP_IN:
1817a6d42e7dSPeter Dunlap 			/* Target only */
1818a668b114SPriya Krishnan 			DTRACE_ISCSI_2(nop__send, idm_conn_t *, ic,
1819a668b114SPriya Krishnan 			    iscsi_nop_in_hdr_t *,
1820a668b114SPriya Krishnan 			    (iscsi_nop_in_hdr_t *)pdu->isp_hdr);
1821a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1822a6d42e7dSPeter Dunlap 			return;
1823a6d42e7dSPeter Dunlap 		case ISCSI_OP_TEXT_RSP:
1824a6d42e7dSPeter Dunlap 			/* Target only */
1825a668b114SPriya Krishnan 			DTRACE_ISCSI_2(text__response, idm_conn_t *, ic,
1826a668b114SPriya Krishnan 			    iscsi_text_rsp_hdr_t *,
1827a668b114SPriya Krishnan 			    (iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
1828a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1829a6d42e7dSPeter Dunlap 			return;
1830a6d42e7dSPeter Dunlap 		case ISCSI_OP_TEXT_CMD:
1831a6d42e7dSPeter Dunlap 		case ISCSI_OP_NOOP_OUT:
1832a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_CMD:
1833a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_DATA:
1834a6d42e7dSPeter Dunlap 		case ISCSI_OP_SCSI_TASK_MGT_MSG:
1835a6d42e7dSPeter Dunlap 			/* Initiator only */
1836a6d42e7dSPeter Dunlap 			idm_pdu_tx_forward(ic, pdu);
1837a6d42e7dSPeter Dunlap 			return;
1838a6d42e7dSPeter Dunlap 		default:
1839a6d42e7dSPeter Dunlap 			break;
1840a6d42e7dSPeter Dunlap 		}
1841a6d42e7dSPeter Dunlap 
1842a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
1843a6d42e7dSPeter Dunlap 	}
1844a6d42e7dSPeter Dunlap 
1845a6d42e7dSPeter Dunlap 	/*
1846a6d42e7dSPeter Dunlap 	 * Any PDU's processed outside of full-feature mode and non-SCSI
1847a6d42e7dSPeter Dunlap 	 * PDU's in full-feature mode are handled by generating an
1848a6d42e7dSPeter Dunlap 	 * event to the connection state machine.  The state machine
1849a6d42e7dSPeter Dunlap 	 * will validate the PDU against the current state and either
1850a6d42e7dSPeter Dunlap 	 * transmit the PDU if the opcode is allowed or handle an
1851a6d42e7dSPeter Dunlap 	 * error if the PDU is not allowed.
1852a6d42e7dSPeter Dunlap 	 *
1853a6d42e7dSPeter Dunlap 	 * This code-path will also generate any events that are implied
1854a6d42e7dSPeter Dunlap 	 * by the PDU opcode.  For example a "login response" with success
1855a6d42e7dSPeter Dunlap 	 * status generates a CE_LOGOUT_SUCCESS_SND event.
1856a6d42e7dSPeter Dunlap 	 */
1857a6d42e7dSPeter Dunlap 	switch (IDM_PDU_OPCODE(pdu)) {
1858a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_CMD:
1859a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu);
1860a6d42e7dSPeter Dunlap 		break;
1861a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGIN_RSP:
1862a668b114SPriya Krishnan 		DTRACE_ISCSI_2(login__response, idm_conn_t *, ic,
1863a668b114SPriya Krishnan 		    iscsi_login_rsp_hdr_t *,
1864a668b114SPriya Krishnan 		    (iscsi_login_rsp_hdr_t *)pdu->isp_hdr);
1865a6d42e7dSPeter Dunlap 		idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE);
1866a6d42e7dSPeter Dunlap 		break;
1867a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_CMD:
1868a6d42e7dSPeter Dunlap 		idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE);
1869a6d42e7dSPeter Dunlap 		break;
1870a6d42e7dSPeter Dunlap 	case ISCSI_OP_LOGOUT_RSP:
1871a668b114SPriya Krishnan 		DTRACE_ISCSI_2(logout__response, idm_conn_t *, ic,
1872a668b114SPriya Krishnan 		    iscsi_logout_rsp_hdr_t *,
1873a668b114SPriya Krishnan 		    (iscsi_logout_rsp_hdr_t *)pdu->isp_hdr);
1874a6d42e7dSPeter Dunlap 		idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE);
1875a6d42e7dSPeter Dunlap 		break;
1876a6d42e7dSPeter Dunlap 	case ISCSI_OP_ASYNC_EVENT:
1877a668b114SPriya Krishnan 		DTRACE_ISCSI_2(async__send, idm_conn_t *, ic,
1878a668b114SPriya Krishnan 		    iscsi_async_evt_hdr_t *,
1879a668b114SPriya Krishnan 		    (iscsi_async_evt_hdr_t *)pdu->isp_hdr);
1880a6d42e7dSPeter Dunlap 		async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr;
1881a6d42e7dSPeter Dunlap 		switch (async_evt->async_event) {
1882a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
1883a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND,
1884a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1885a6d42e7dSPeter Dunlap 			break;
1886a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
1887a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND,
1888a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1889a6d42e7dSPeter Dunlap 			break;
1890a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
1891a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND,
1892a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1893a6d42e7dSPeter Dunlap 			break;
1894a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_SCSI_EVENT:
1895a6d42e7dSPeter Dunlap 		case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
1896a6d42e7dSPeter Dunlap 		default:
1897a6d42e7dSPeter Dunlap 			idm_conn_tx_pdu_event(ic, CE_MISC_TX,
1898a6d42e7dSPeter Dunlap 			    (uintptr_t)pdu);
1899a6d42e7dSPeter Dunlap 			break;
1900a6d42e7dSPeter Dunlap 		}
1901a6d42e7dSPeter Dunlap 		break;
1902a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_RSP:
1903a6d42e7dSPeter Dunlap 		/* Target only */
1904a668b114SPriya Krishnan 		DTRACE_ISCSI_2(scsi__response, idm_conn_t *, ic,
1905a668b114SPriya Krishnan 		    iscsi_scsi_rsp_hdr_t *,
1906a668b114SPriya Krishnan 		    (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr);
1907a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1908a6d42e7dSPeter Dunlap 		break;
1909a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_RSP:
1910a6d42e7dSPeter Dunlap 		/* Target only */
1911a668b114SPriya Krishnan 		DTRACE_ISCSI_2(task__response, idm_conn_t *, ic,
1912a668b114SPriya Krishnan 		    iscsi_scsi_task_mgt_rsp_hdr_t *,
1913a668b114SPriya Krishnan 		    (iscsi_scsi_task_mgt_rsp_hdr_t *)pdu->isp_hdr);
1914a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1915a6d42e7dSPeter Dunlap 		break;
1916a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA_RSP:
1917a6d42e7dSPeter Dunlap 		/* Target only */
1918a668b114SPriya Krishnan 		DTRACE_ISCSI_2(data__send, idm_conn_t *, ic,
1919a668b114SPriya Krishnan 		    iscsi_data_rsp_hdr_t *,
1920a668b114SPriya Krishnan 		    (iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
1921a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1922a6d42e7dSPeter Dunlap 		break;
1923a6d42e7dSPeter Dunlap 	case ISCSI_OP_RTT_RSP:
1924a6d42e7dSPeter Dunlap 		/* Target only */
1925a668b114SPriya Krishnan 		DTRACE_ISCSI_2(data__request, idm_conn_t *, ic,
1926a668b114SPriya Krishnan 		    iscsi_rtt_hdr_t *,
1927a668b114SPriya Krishnan 		    (iscsi_rtt_hdr_t *)pdu->isp_hdr);
1928a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1929a6d42e7dSPeter Dunlap 		break;
1930a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_IN:
1931a6d42e7dSPeter Dunlap 		/* Target only */
1932a668b114SPriya Krishnan 		DTRACE_ISCSI_2(nop__send, idm_conn_t *, ic,
1933a668b114SPriya Krishnan 		    iscsi_nop_in_hdr_t *,
1934a668b114SPriya Krishnan 		    (iscsi_nop_in_hdr_t *)pdu->isp_hdr);
1935a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1936a6d42e7dSPeter Dunlap 		break;
1937a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_RSP:
1938a6d42e7dSPeter Dunlap 		/* Target only */
1939a668b114SPriya Krishnan 		DTRACE_ISCSI_2(text__response, idm_conn_t *, ic,
1940a668b114SPriya Krishnan 		    iscsi_text_rsp_hdr_t *,
1941a668b114SPriya Krishnan 		    (iscsi_text_rsp_hdr_t *)pdu->isp_hdr);
1942a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1943a6d42e7dSPeter Dunlap 		break;
1944a6d42e7dSPeter Dunlap 		/* Initiator only */
1945a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_CMD:
1946a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
1947a6d42e7dSPeter Dunlap 	case ISCSI_OP_SCSI_DATA:
1948a6d42e7dSPeter Dunlap 	case ISCSI_OP_NOOP_OUT:
1949a6d42e7dSPeter Dunlap 	case ISCSI_OP_TEXT_CMD:
1950a6d42e7dSPeter Dunlap 	case ISCSI_OP_SNACK_CMD:
1951a6d42e7dSPeter Dunlap 	case ISCSI_OP_REJECT_MSG:
1952a6d42e7dSPeter Dunlap 	default:
1953a6d42e7dSPeter Dunlap 		/*
1954a6d42e7dSPeter Dunlap 		 * Connection state machine will validate these PDU's against
1955a6d42e7dSPeter Dunlap 		 * the current state.  A PDU not allowed in the current
1956a6d42e7dSPeter Dunlap 		 * state will cause a protocol error.
1957a6d42e7dSPeter Dunlap 		 */
1958a6d42e7dSPeter Dunlap 		idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu);
1959a6d42e7dSPeter Dunlap 		break;
1960a6d42e7dSPeter Dunlap 	}
1961a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
1962a6d42e7dSPeter Dunlap }
1963a6d42e7dSPeter Dunlap 
1964a6d42e7dSPeter Dunlap /*
196530e7468fSPeter Dunlap  * Common allocation of a PDU along with memory for header and data.
1966a6d42e7dSPeter Dunlap  */
196730e7468fSPeter Dunlap static idm_pdu_t *
196830e7468fSPeter Dunlap idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, int sleepflag)
1969a6d42e7dSPeter Dunlap {
1970a6d42e7dSPeter Dunlap 	idm_pdu_t *result;
1971a6d42e7dSPeter Dunlap 
1972a6d42e7dSPeter Dunlap 	/*
1973a6d42e7dSPeter Dunlap 	 * IDM clients should cache these structures for performance
1974a6d42e7dSPeter Dunlap 	 * critical paths.  We can't cache effectively in IDM because we
1975a6d42e7dSPeter Dunlap 	 * don't know the correct header and data size.
1976a6d42e7dSPeter Dunlap 	 *
1977a6d42e7dSPeter Dunlap 	 * Valid header length is assumed to be hdrlen and valid data
1978a6d42e7dSPeter Dunlap 	 * length is assumed to be datalen.  isp_hdrlen and isp_datalen
1979a6d42e7dSPeter Dunlap 	 * can be adjusted after the PDU is returned if necessary.
1980a6d42e7dSPeter Dunlap 	 */
198130e7468fSPeter Dunlap 	result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, sleepflag);
198230e7468fSPeter Dunlap 	if (result != NULL) {
198330e7468fSPeter Dunlap 		/* For idm_pdu_free sanity check */
198430e7468fSPeter Dunlap 		result->isp_flags |= IDM_PDU_ALLOC;
198530e7468fSPeter Dunlap 		/* pointer arithmetic */
198630e7468fSPeter Dunlap 		result->isp_hdr = (iscsi_hdr_t *)(result + 1);
198730e7468fSPeter Dunlap 		result->isp_hdrlen = hdrlen;
198830e7468fSPeter Dunlap 		result->isp_hdrbuflen = hdrlen;
198930e7468fSPeter Dunlap 		result->isp_transport_hdrlen = 0;
19904142b486SJames Moore 		if (datalen != 0)
19914142b486SJames Moore 			result->isp_data = (uint8_t *)result->isp_hdr + hdrlen;
199230e7468fSPeter Dunlap 		result->isp_datalen = datalen;
199330e7468fSPeter Dunlap 		result->isp_databuflen = datalen;
199430e7468fSPeter Dunlap 		result->isp_magic = IDM_PDU_MAGIC;
199530e7468fSPeter Dunlap 	}
1996a6d42e7dSPeter Dunlap 
1997a6d42e7dSPeter Dunlap 	return (result);
1998a6d42e7dSPeter Dunlap }
1999a6d42e7dSPeter Dunlap 
200030e7468fSPeter Dunlap /*
200130e7468fSPeter Dunlap  * Typical idm_pdu_alloc invocation, will block for resources.
200230e7468fSPeter Dunlap  */
200330e7468fSPeter Dunlap idm_pdu_t *
200430e7468fSPeter Dunlap idm_pdu_alloc(uint_t hdrlen, uint_t datalen)
200530e7468fSPeter Dunlap {
200630e7468fSPeter Dunlap 	return (idm_pdu_alloc_common(hdrlen, datalen, KM_SLEEP));
200730e7468fSPeter Dunlap }
200830e7468fSPeter Dunlap 
200930e7468fSPeter Dunlap /*
201030e7468fSPeter Dunlap  * Non-blocking idm_pdu_alloc implementation, returns NULL if resources
201130e7468fSPeter Dunlap  * are not available.  Needed for transport-layer allocations which may
201230e7468fSPeter Dunlap  * be invoking in interrupt context.
201330e7468fSPeter Dunlap  */
201430e7468fSPeter Dunlap idm_pdu_t *
201530e7468fSPeter Dunlap idm_pdu_alloc_nosleep(uint_t hdrlen, uint_t datalen)
201630e7468fSPeter Dunlap {
201730e7468fSPeter Dunlap 	return (idm_pdu_alloc_common(hdrlen, datalen, KM_NOSLEEP));
201830e7468fSPeter Dunlap }
201930e7468fSPeter Dunlap 
2020a6d42e7dSPeter Dunlap /*
2021a6d42e7dSPeter Dunlap  * Free a PDU previously allocated with idm_pdu_alloc() including any
2022a6d42e7dSPeter Dunlap  * header and data space allocated as part of the original request.
2023a6d42e7dSPeter Dunlap  * Additional memory regions referenced by subsequent modification of
2024a6d42e7dSPeter Dunlap  * the isp_hdr and/or isp_data fields will not be freed.
2025a6d42e7dSPeter Dunlap  */
2026a6d42e7dSPeter Dunlap void
2027a6d42e7dSPeter Dunlap idm_pdu_free(idm_pdu_t *pdu)
2028a6d42e7dSPeter Dunlap {
2029a6d42e7dSPeter Dunlap 	/* Make sure the structure was allocated using idm_pdu_alloc() */
2030a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_flags & IDM_PDU_ALLOC);
2031a6d42e7dSPeter Dunlap 	kmem_free(pdu,
2032a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen);
2033a6d42e7dSPeter Dunlap }
2034a6d42e7dSPeter Dunlap 
2035a6d42e7dSPeter Dunlap /*
2036a6d42e7dSPeter Dunlap  * Initialize the connection, private and callback fields in a PDU.
2037a6d42e7dSPeter Dunlap  */
2038a6d42e7dSPeter Dunlap void
2039a6d42e7dSPeter Dunlap idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb)
2040a6d42e7dSPeter Dunlap {
2041a6d42e7dSPeter Dunlap 	/*
2042a6d42e7dSPeter Dunlap 	 * idm_pdu_complete() will call idm_pdu_free if the callback is
2043a6d42e7dSPeter Dunlap 	 * NULL.  This will only work if the PDU was originally allocated
2044a6d42e7dSPeter Dunlap 	 * with idm_pdu_alloc().
2045a6d42e7dSPeter Dunlap 	 */
2046a6d42e7dSPeter Dunlap 	ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) ||
2047a6d42e7dSPeter Dunlap 	    (cb != NULL));
2048a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
2049a6d42e7dSPeter Dunlap 	pdu->isp_ic = ic;
2050a6d42e7dSPeter Dunlap 	pdu->isp_private = private;
2051a6d42e7dSPeter Dunlap 	pdu->isp_callback = cb;
2052a6d42e7dSPeter Dunlap }
2053a6d42e7dSPeter Dunlap 
2054a6d42e7dSPeter Dunlap /*
2055a6d42e7dSPeter Dunlap  * Initialize the header and header length field.  This function should
2056a6d42e7dSPeter Dunlap  * not be used to adjust the header length in a buffer allocated via
2057a6d42e7dSPeter Dunlap  * pdu_pdu_alloc since it overwrites the existing header pointer.
2058a6d42e7dSPeter Dunlap  */
2059a6d42e7dSPeter Dunlap void
2060a6d42e7dSPeter Dunlap idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen)
2061a6d42e7dSPeter Dunlap {
2062a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr);
2063a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = hdrlen;
2064a6d42e7dSPeter Dunlap }
2065a6d42e7dSPeter Dunlap 
2066a6d42e7dSPeter Dunlap /*
2067a6d42e7dSPeter Dunlap  * Initialize the data and data length fields.  This function should
2068a6d42e7dSPeter Dunlap  * not be used to adjust the data length of a buffer allocated via
2069a6d42e7dSPeter Dunlap  * idm_pdu_alloc since it overwrites the existing data pointer.
2070a6d42e7dSPeter Dunlap  */
2071a6d42e7dSPeter Dunlap void
2072a6d42e7dSPeter Dunlap idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen)
2073a6d42e7dSPeter Dunlap {
2074a6d42e7dSPeter Dunlap 	pdu->isp_data = data;
2075a6d42e7dSPeter Dunlap 	pdu->isp_datalen = datalen;
2076a6d42e7dSPeter Dunlap }
2077a6d42e7dSPeter Dunlap 
2078a6d42e7dSPeter Dunlap void
2079a6d42e7dSPeter Dunlap idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status)
2080a6d42e7dSPeter Dunlap {
2081a6d42e7dSPeter Dunlap 	if (pdu->isp_callback) {
2082a6d42e7dSPeter Dunlap 		pdu->isp_status = status;
2083a6d42e7dSPeter Dunlap 		(*pdu->isp_callback)(pdu, status);
2084a6d42e7dSPeter Dunlap 	} else {
2085a6d42e7dSPeter Dunlap 		idm_pdu_free(pdu);
2086a6d42e7dSPeter Dunlap 	}
2087a6d42e7dSPeter Dunlap }
2088a6d42e7dSPeter Dunlap 
2089a6d42e7dSPeter Dunlap /*
2090a6d42e7dSPeter Dunlap  * State machine auditing
2091a6d42e7dSPeter Dunlap  */
2092a6d42e7dSPeter Dunlap 
2093a6d42e7dSPeter Dunlap void
2094a6d42e7dSPeter Dunlap idm_sm_audit_init(sm_audit_buf_t *audit_buf)
2095a6d42e7dSPeter Dunlap {
2096a6d42e7dSPeter Dunlap 	bzero(audit_buf, sizeof (sm_audit_buf_t));
2097a6d42e7dSPeter Dunlap 	audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1;
2098a6d42e7dSPeter Dunlap }
2099a6d42e7dSPeter Dunlap 
2100a6d42e7dSPeter Dunlap static
2101a6d42e7dSPeter Dunlap sm_audit_record_t *
2102a6d42e7dSPeter Dunlap idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type,
2103a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type,
2104a6d42e7dSPeter Dunlap     int current_state)
2105a6d42e7dSPeter Dunlap {
2106a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
2107a6d42e7dSPeter Dunlap 
2108a6d42e7dSPeter Dunlap 	sar = audit_buf->sab_records;
2109a6d42e7dSPeter Dunlap 	sar += audit_buf->sab_index;
2110a6d42e7dSPeter Dunlap 	audit_buf->sab_index++;
2111a6d42e7dSPeter Dunlap 	audit_buf->sab_index &= audit_buf->sab_max_index;
2112a6d42e7dSPeter Dunlap 
2113a6d42e7dSPeter Dunlap 	sar->sar_type = r_type;
2114a6d42e7dSPeter Dunlap 	gethrestime(&sar->sar_timestamp);
2115a6d42e7dSPeter Dunlap 	sar->sar_sm_type = sm_type;
2116a6d42e7dSPeter Dunlap 	sar->sar_state = current_state;
2117a6d42e7dSPeter Dunlap 
2118a6d42e7dSPeter Dunlap 	return (sar);
2119a6d42e7dSPeter Dunlap }
2120a6d42e7dSPeter Dunlap 
2121a6d42e7dSPeter Dunlap void
2122a6d42e7dSPeter Dunlap idm_sm_audit_event(sm_audit_buf_t *audit_buf,
2123a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type, int current_state,
2124a6d42e7dSPeter Dunlap     int event, uintptr_t event_info)
2125a6d42e7dSPeter Dunlap {
2126a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
2127a6d42e7dSPeter Dunlap 
2128a6d42e7dSPeter Dunlap 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT,
2129a6d42e7dSPeter Dunlap 	    sm_type, current_state);
2130a6d42e7dSPeter Dunlap 	sar->sar_event = event;
2131a6d42e7dSPeter Dunlap 	sar->sar_event_info = event_info;
2132a6d42e7dSPeter Dunlap }
2133a6d42e7dSPeter Dunlap 
2134a6d42e7dSPeter Dunlap void
2135a6d42e7dSPeter Dunlap idm_sm_audit_state_change(sm_audit_buf_t *audit_buf,
2136a6d42e7dSPeter Dunlap     sm_audit_sm_type_t sm_type, int current_state, int new_state)
2137a6d42e7dSPeter Dunlap {
2138a6d42e7dSPeter Dunlap 	sm_audit_record_t *sar;
2139a6d42e7dSPeter Dunlap 
2140a6d42e7dSPeter Dunlap 	sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE,
2141a6d42e7dSPeter Dunlap 	    sm_type, current_state);
2142a6d42e7dSPeter Dunlap 	sar->sar_new_state = new_state;
2143a6d42e7dSPeter Dunlap }
2144a6d42e7dSPeter Dunlap 
2145a6d42e7dSPeter Dunlap 
2146a6d42e7dSPeter Dunlap /*
2147a6d42e7dSPeter Dunlap  * Object reference tracking
2148a6d42e7dSPeter Dunlap  */
2149a6d42e7dSPeter Dunlap 
2150a6d42e7dSPeter Dunlap void
2151a6d42e7dSPeter Dunlap idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj)
2152a6d42e7dSPeter Dunlap {
2153a6d42e7dSPeter Dunlap 	bzero(refcnt, sizeof (*refcnt));
2154a6d42e7dSPeter Dunlap 	idm_refcnt_reset(refcnt);
2155a6d42e7dSPeter Dunlap 	refcnt->ir_referenced_obj = referenced_obj;
2156a6d42e7dSPeter Dunlap 	bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t));
2157a6d42e7dSPeter Dunlap 	refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1;
2158a6d42e7dSPeter Dunlap 	mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL);
2159a6d42e7dSPeter Dunlap 	cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL);
2160a6d42e7dSPeter Dunlap }
2161a6d42e7dSPeter Dunlap 
2162a6d42e7dSPeter Dunlap void
2163a6d42e7dSPeter Dunlap idm_refcnt_destroy(idm_refcnt_t *refcnt)
2164a6d42e7dSPeter Dunlap {
216572cf3143Speter dunlap 	/*
216672cf3143Speter dunlap 	 * Grab the mutex to there are no other lingering threads holding
216772cf3143Speter dunlap 	 * the mutex before we destroy it (e.g. idm_refcnt_rele just after
216872cf3143Speter dunlap 	 * the refcnt goes to zero if ir_waiting == REF_WAIT_ASYNC)
216972cf3143Speter dunlap 	 */
217072cf3143Speter dunlap 	mutex_enter(&refcnt->ir_mutex);
2171a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt == 0);
2172a6d42e7dSPeter Dunlap 	cv_destroy(&refcnt->ir_cv);
2173a6d42e7dSPeter Dunlap 	mutex_destroy(&refcnt->ir_mutex);
2174a6d42e7dSPeter Dunlap }
2175a6d42e7dSPeter Dunlap 
2176a6d42e7dSPeter Dunlap void
2177a6d42e7dSPeter Dunlap idm_refcnt_reset(idm_refcnt_t *refcnt)
2178a6d42e7dSPeter Dunlap {
2179a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_NOWAIT;
2180a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt = 0;
2181a6d42e7dSPeter Dunlap }
2182a6d42e7dSPeter Dunlap 
2183a6d42e7dSPeter Dunlap void
2184a6d42e7dSPeter Dunlap idm_refcnt_hold(idm_refcnt_t *refcnt)
2185a6d42e7dSPeter Dunlap {
2186a6d42e7dSPeter Dunlap 	/*
2187a6d42e7dSPeter Dunlap 	 * Nothing should take a hold on an object after a call to
2188a6d42e7dSPeter Dunlap 	 * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref
2189a6d42e7dSPeter Dunlap 	 */
2190a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_waiting == REF_NOWAIT);
2191a6d42e7dSPeter Dunlap 
2192a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2193a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt++;
2194a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2195a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2196a6d42e7dSPeter Dunlap }
2197a6d42e7dSPeter Dunlap 
2198a6d42e7dSPeter Dunlap static void
2199a6d42e7dSPeter Dunlap idm_refcnt_unref_task(void *refcnt_void)
2200a6d42e7dSPeter Dunlap {
2201a6d42e7dSPeter Dunlap 	idm_refcnt_t *refcnt = refcnt_void;
2202a6d42e7dSPeter Dunlap 
2203a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2204a6d42e7dSPeter Dunlap 	(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
2205a6d42e7dSPeter Dunlap }
2206a6d42e7dSPeter Dunlap 
2207a6d42e7dSPeter Dunlap void
2208a6d42e7dSPeter Dunlap idm_refcnt_rele(idm_refcnt_t *refcnt)
2209a6d42e7dSPeter Dunlap {
2210a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2211a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt > 0);
2212a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt--;
2213a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2214a6d42e7dSPeter Dunlap 	if (refcnt->ir_waiting == REF_NOWAIT) {
2215a6d42e7dSPeter Dunlap 		/* No one is waiting on this object */
2216a6d42e7dSPeter Dunlap 		mutex_exit(&refcnt->ir_mutex);
2217a6d42e7dSPeter Dunlap 		return;
2218a6d42e7dSPeter Dunlap 	}
2219a6d42e7dSPeter Dunlap 
2220a6d42e7dSPeter Dunlap 	/*
2221a6d42e7dSPeter Dunlap 	 * Someone is waiting for this object to go idle so check if
2222a6d42e7dSPeter Dunlap 	 * refcnt is 0.  Waiting on an object then later grabbing another
2223a6d42e7dSPeter Dunlap 	 * reference is not allowed so we don't need to handle that case.
2224a6d42e7dSPeter Dunlap 	 */
2225a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
2226a6d42e7dSPeter Dunlap 		if (refcnt->ir_waiting == REF_WAIT_ASYNC) {
2227a6d42e7dSPeter Dunlap 			if (taskq_dispatch(idm.idm_global_taskq,
2228a6d42e7dSPeter Dunlap 			    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
2229a6d42e7dSPeter Dunlap 				cmn_err(CE_WARN,
2230a6d42e7dSPeter Dunlap 				    "idm_refcnt_rele: Couldn't dispatch task");
2231a6d42e7dSPeter Dunlap 			}
2232a6d42e7dSPeter Dunlap 		} else if (refcnt->ir_waiting == REF_WAIT_SYNC) {
2233a6d42e7dSPeter Dunlap 			cv_signal(&refcnt->ir_cv);
2234a6d42e7dSPeter Dunlap 		}
2235a6d42e7dSPeter Dunlap 	}
2236a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2237a6d42e7dSPeter Dunlap }
2238a6d42e7dSPeter Dunlap 
2239a6d42e7dSPeter Dunlap void
2240a6d42e7dSPeter Dunlap idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
2241a6d42e7dSPeter Dunlap {
2242a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2243a6d42e7dSPeter Dunlap 	ASSERT(refcnt->ir_refcnt > 0);
2244a6d42e7dSPeter Dunlap 	refcnt->ir_refcnt--;
2245a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2246a6d42e7dSPeter Dunlap 
2247a6d42e7dSPeter Dunlap 	/*
2248a6d42e7dSPeter Dunlap 	 * Someone is waiting for this object to go idle so check if
2249a6d42e7dSPeter Dunlap 	 * refcnt is 0.  Waiting on an object then later grabbing another
2250a6d42e7dSPeter Dunlap 	 * reference is not allowed so we don't need to handle that case.
2251a6d42e7dSPeter Dunlap 	 */
2252a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
2253a6d42e7dSPeter Dunlap 		refcnt->ir_cb = cb_func;
2254a6d42e7dSPeter Dunlap 		refcnt->ir_waiting = REF_WAIT_ASYNC;
2255a6d42e7dSPeter Dunlap 		if (taskq_dispatch(idm.idm_global_taskq,
2256a6d42e7dSPeter Dunlap 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
2257a6d42e7dSPeter Dunlap 			cmn_err(CE_WARN,
2258a6d42e7dSPeter Dunlap 			    "idm_refcnt_rele: Couldn't dispatch task");
2259a6d42e7dSPeter Dunlap 		}
2260a6d42e7dSPeter Dunlap 	}
2261a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2262a6d42e7dSPeter Dunlap }
2263a6d42e7dSPeter Dunlap 
2264a6d42e7dSPeter Dunlap void
2265a6d42e7dSPeter Dunlap idm_refcnt_wait_ref(idm_refcnt_t *refcnt)
2266a6d42e7dSPeter Dunlap {
2267a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2268a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_WAIT_SYNC;
2269a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2270a6d42e7dSPeter Dunlap 	while (refcnt->ir_refcnt != 0)
2271a6d42e7dSPeter Dunlap 		cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex);
2272a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2273a6d42e7dSPeter Dunlap }
2274a6d42e7dSPeter Dunlap 
2275a6d42e7dSPeter Dunlap void
2276a6d42e7dSPeter Dunlap idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func)
2277a6d42e7dSPeter Dunlap {
2278a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2279a6d42e7dSPeter Dunlap 	refcnt->ir_waiting = REF_WAIT_ASYNC;
2280a6d42e7dSPeter Dunlap 	refcnt->ir_cb = cb_func;
2281a6d42e7dSPeter Dunlap 	REFCNT_AUDIT(refcnt);
2282a6d42e7dSPeter Dunlap 	/*
2283a6d42e7dSPeter Dunlap 	 * It's possible we don't have any references.  To make things easier
2284a6d42e7dSPeter Dunlap 	 * on the caller use a taskq to call the callback instead of
2285a6d42e7dSPeter Dunlap 	 * calling it synchronously
2286a6d42e7dSPeter Dunlap 	 */
2287a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
2288a6d42e7dSPeter Dunlap 		if (taskq_dispatch(idm.idm_global_taskq,
2289a6d42e7dSPeter Dunlap 		    &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) {
2290a6d42e7dSPeter Dunlap 			cmn_err(CE_WARN,
2291a6d42e7dSPeter Dunlap 			    "idm_refcnt_async_wait_ref: "
2292a6d42e7dSPeter Dunlap 			    "Couldn't dispatch task");
2293a6d42e7dSPeter Dunlap 		}
2294a6d42e7dSPeter Dunlap 	}
2295a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2296a6d42e7dSPeter Dunlap }
2297a6d42e7dSPeter Dunlap 
2298a6d42e7dSPeter Dunlap void
2299a6d42e7dSPeter Dunlap idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt,
2300a6d42e7dSPeter Dunlap     idm_refcnt_cb_t *cb_func)
2301a6d42e7dSPeter Dunlap {
2302a6d42e7dSPeter Dunlap 	mutex_enter(&refcnt->ir_mutex);
2303a6d42e7dSPeter Dunlap 	if (refcnt->ir_refcnt == 0) {
2304a6d42e7dSPeter Dunlap 		mutex_exit(&refcnt->ir_mutex);
2305a6d42e7dSPeter Dunlap 		(*cb_func)(refcnt->ir_referenced_obj);
2306a6d42e7dSPeter Dunlap 		return;
2307a6d42e7dSPeter Dunlap 	}
2308a6d42e7dSPeter Dunlap 	mutex_exit(&refcnt->ir_mutex);
2309a6d42e7dSPeter Dunlap }
2310a6d42e7dSPeter Dunlap 
2311a6d42e7dSPeter Dunlap void
2312a6d42e7dSPeter Dunlap idm_conn_hold(idm_conn_t *ic)
2313a6d42e7dSPeter Dunlap {
2314a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&ic->ic_refcnt);
2315a6d42e7dSPeter Dunlap }
2316a6d42e7dSPeter Dunlap 
2317a6d42e7dSPeter Dunlap void
2318a6d42e7dSPeter Dunlap idm_conn_rele(idm_conn_t *ic)
2319a6d42e7dSPeter Dunlap {
2320a6d42e7dSPeter Dunlap 	idm_refcnt_rele(&ic->ic_refcnt);
2321a6d42e7dSPeter Dunlap }
2322a6d42e7dSPeter Dunlap 
2323a668b114SPriya Krishnan void
2324a668b114SPriya Krishnan idm_conn_set_target_name(idm_conn_t *ic, char *target_name)
2325a668b114SPriya Krishnan {
2326a668b114SPriya Krishnan 	(void) strlcpy(ic->ic_target_name, target_name, ISCSI_MAX_NAME_LEN + 1);
2327a668b114SPriya Krishnan }
2328a668b114SPriya Krishnan 
2329a668b114SPriya Krishnan void
2330a668b114SPriya Krishnan idm_conn_set_initiator_name(idm_conn_t *ic, char *initiator_name)
2331a668b114SPriya Krishnan {
2332a668b114SPriya Krishnan 	(void) strlcpy(ic->ic_initiator_name, initiator_name,
2333a668b114SPriya Krishnan 	    ISCSI_MAX_NAME_LEN + 1);
2334a668b114SPriya Krishnan }
2335a668b114SPriya Krishnan 
2336a668b114SPriya Krishnan void
2337a668b114SPriya Krishnan idm_conn_set_isid(idm_conn_t *ic, uint8_t isid[ISCSI_ISID_LEN])
2338a668b114SPriya Krishnan {
2339a668b114SPriya Krishnan 	(void) snprintf(ic->ic_isid, ISCSI_MAX_ISID_LEN + 1,
2340a668b114SPriya Krishnan 	    "%02x%02x%02x%02x%02x%02x",
2341a668b114SPriya Krishnan 	    isid[0], isid[1], isid[2], isid[3], isid[4], isid[5]);
2342a668b114SPriya Krishnan }
2343a6d42e7dSPeter Dunlap 
2344a6d42e7dSPeter Dunlap static int
2345a6d42e7dSPeter Dunlap _idm_init(void)
2346a6d42e7dSPeter Dunlap {
2347a6d42e7dSPeter Dunlap 	/* Initialize the rwlock for the taskid table */
2348a6d42e7dSPeter Dunlap 	rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL);
2349a6d42e7dSPeter Dunlap 
2350a6d42e7dSPeter Dunlap 	/* Initialize the global mutex and taskq */
2351a6d42e7dSPeter Dunlap 	mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL);
2352a6d42e7dSPeter Dunlap 
2353a6d42e7dSPeter Dunlap 	cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL);
2354a6d42e7dSPeter Dunlap 	cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL);
2355a6d42e7dSPeter Dunlap 
235630e7468fSPeter Dunlap 	/*
235730e7468fSPeter Dunlap 	 * The maximum allocation needs to be high here since there can be
235830e7468fSPeter Dunlap 	 * many concurrent tasks using the global taskq.
235930e7468fSPeter Dunlap 	 */
2360a6d42e7dSPeter Dunlap 	idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri,
236130e7468fSPeter Dunlap 	    128, 16384, TASKQ_PREPOPULATE);
2362a6d42e7dSPeter Dunlap 	if (idm.idm_global_taskq == NULL) {
2363a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_wd_cv);
2364a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_tgt_svc_cv);
2365a6d42e7dSPeter Dunlap 		mutex_destroy(&idm.idm_global_mutex);
2366a6d42e7dSPeter Dunlap 		rw_destroy(&idm.idm_taskid_table_lock);
2367a6d42e7dSPeter Dunlap 		return (ENOMEM);
2368a6d42e7dSPeter Dunlap 	}
2369a6d42e7dSPeter Dunlap 
2370a41f9819SJames Moore 	/* Start watchdog thread */
2371a6d42e7dSPeter Dunlap 	idm.idm_wd_thread = thread_create(NULL, 0,
2372a6d42e7dSPeter Dunlap 	    idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri);
2373a6d42e7dSPeter Dunlap 	if (idm.idm_wd_thread == NULL) {
2374a6d42e7dSPeter Dunlap 		/* Couldn't create the watchdog thread */
2375a6d42e7dSPeter Dunlap 		taskq_destroy(idm.idm_global_taskq);
2376a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_wd_cv);
2377a6d42e7dSPeter Dunlap 		cv_destroy(&idm.idm_tgt_svc_cv);
2378a6d42e7dSPeter Dunlap 		mutex_destroy(&idm.idm_global_mutex);
2379a6d42e7dSPeter Dunlap 		rw_destroy(&idm.idm_taskid_table_lock);
2380a6d42e7dSPeter Dunlap 		return (ENOMEM);
2381a6d42e7dSPeter Dunlap 	}
2382a6d42e7dSPeter Dunlap 
2383a41f9819SJames Moore 	/* Pause until the watchdog thread is running */
2384a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
2385a6d42e7dSPeter Dunlap 	while (!idm.idm_wd_thread_running)
2386a6d42e7dSPeter Dunlap 		cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex);
2387a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
2388a6d42e7dSPeter Dunlap 
2389a6d42e7dSPeter Dunlap 	/*
2390a6d42e7dSPeter Dunlap 	 * Allocate the task ID table and set "next" to 0.
2391a6d42e7dSPeter Dunlap 	 */
2392a6d42e7dSPeter Dunlap 
2393a6d42e7dSPeter Dunlap 	idm.idm_taskid_max = idm_max_taskids;
2394a6d42e7dSPeter Dunlap 	idm.idm_taskid_table = (idm_task_t **)
2395a6d42e7dSPeter Dunlap 	    kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP);
2396a6d42e7dSPeter Dunlap 	idm.idm_taskid_next = 0;
2397a6d42e7dSPeter Dunlap 
2398a6d42e7dSPeter Dunlap 	/* Create the global buffer and task kmem caches */
2399a6d42e7dSPeter Dunlap 	idm.idm_buf_cache = kmem_cache_create("idm_buf_cache",
2400a6d42e7dSPeter Dunlap 	    sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
2401a6d42e7dSPeter Dunlap 
2402a6d42e7dSPeter Dunlap 	/*
2403a6d42e7dSPeter Dunlap 	 * Note, we're explicitly allocating an additional iSER header-
2404a6d42e7dSPeter Dunlap 	 * sized chunk for each of these elements. See idm_task_constructor().
2405a6d42e7dSPeter Dunlap 	 */
2406a6d42e7dSPeter Dunlap 	idm.idm_task_cache = kmem_cache_create("idm_task_cache",
2407a6d42e7dSPeter Dunlap 	    sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8,
2408a6d42e7dSPeter Dunlap 	    &idm_task_constructor, &idm_task_destructor,
2409a6d42e7dSPeter Dunlap 	    NULL, NULL, NULL, KM_SLEEP);
2410a6d42e7dSPeter Dunlap 
2411a6d42e7dSPeter Dunlap 	/* Create the service and connection context lists */
2412a6d42e7dSPeter Dunlap 	list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t),
2413a6d42e7dSPeter Dunlap 	    offsetof(idm_svc_t, is_list_node));
2414a6d42e7dSPeter Dunlap 	list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t),
2415a6d42e7dSPeter Dunlap 	    offsetof(idm_conn_t, ic_list_node));
2416a6d42e7dSPeter Dunlap 	list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t),
2417a6d42e7dSPeter Dunlap 	    offsetof(idm_conn_t, ic_list_node));
2418a6d42e7dSPeter Dunlap 
2419a6d42e7dSPeter Dunlap 	/* Initialize the native sockets transport */
2420a6d42e7dSPeter Dunlap 	idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]);
2421a6d42e7dSPeter Dunlap 
2422a6d42e7dSPeter Dunlap 	/* Create connection ID pool */
2423a6d42e7dSPeter Dunlap 	(void) idm_idpool_create(&idm.idm_conn_id_pool);
2424a6d42e7dSPeter Dunlap 
2425a6d42e7dSPeter Dunlap 	return (DDI_SUCCESS);
2426a6d42e7dSPeter Dunlap }
2427a6d42e7dSPeter Dunlap 
2428a6d42e7dSPeter Dunlap static int
2429a6d42e7dSPeter Dunlap _idm_fini(void)
2430a6d42e7dSPeter Dunlap {
2431a6d42e7dSPeter Dunlap 	if (!list_is_empty(&idm.idm_ini_conn_list) ||
2432a6d42e7dSPeter Dunlap 	    !list_is_empty(&idm.idm_tgt_conn_list) ||
2433a6d42e7dSPeter Dunlap 	    !list_is_empty(&idm.idm_tgt_svc_list)) {
2434a6d42e7dSPeter Dunlap 		return (EBUSY);
2435a6d42e7dSPeter Dunlap 	}
2436a6d42e7dSPeter Dunlap 
2437a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
2438a6d42e7dSPeter Dunlap 	idm.idm_wd_thread_running = B_FALSE;
2439a6d42e7dSPeter Dunlap 	cv_signal(&idm.idm_wd_cv);
2440a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
2441a6d42e7dSPeter Dunlap 
2442a6d42e7dSPeter Dunlap 	thread_join(idm.idm_wd_thread_did);
2443a6d42e7dSPeter Dunlap 
2444a6d42e7dSPeter Dunlap 	idm_idpool_destroy(&idm.idm_conn_id_pool);
244530e7468fSPeter Dunlap 
244630e7468fSPeter Dunlap 	/* Close any LDI handles we have open on transport drivers */
244730e7468fSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
244830e7468fSPeter Dunlap 	idm_transport_teardown();
244930e7468fSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
245030e7468fSPeter Dunlap 
245130e7468fSPeter Dunlap 	/* Teardown the native sockets transport */
2452a6d42e7dSPeter Dunlap 	idm_so_fini();
245330e7468fSPeter Dunlap 
2454a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_ini_conn_list);
2455a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_tgt_conn_list);
2456a6d42e7dSPeter Dunlap 	list_destroy(&idm.idm_tgt_svc_list);
2457a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_task_cache);
2458a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_buf_cache);
2459a6d42e7dSPeter Dunlap 	kmem_free(idm.idm_taskid_table,
2460a6d42e7dSPeter Dunlap 	    idm.idm_taskid_max * sizeof (idm_task_t *));
2461a6d42e7dSPeter Dunlap 	mutex_destroy(&idm.idm_global_mutex);
2462a6d42e7dSPeter Dunlap 	cv_destroy(&idm.idm_wd_cv);
2463a6d42e7dSPeter Dunlap 	cv_destroy(&idm.idm_tgt_svc_cv);
2464a6d42e7dSPeter Dunlap 	rw_destroy(&idm.idm_taskid_table_lock);
2465a6d42e7dSPeter Dunlap 
2466a6d42e7dSPeter Dunlap 	return (0);
2467a6d42e7dSPeter Dunlap }
2468