xref: /illumos-gate/usr/src/uts/common/io/idm/idm.c (revision a98e9e2e)
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 /*
22d618d68dSPriya Krishnan  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2361dfa509SRick McNeal  * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
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 void idm_wd_thread(void *arg);
58a6d42e7dSPeter Dunlap 
59a6d42e7dSPeter Dunlap static int _idm_init(void);
60a6d42e7dSPeter Dunlap static int _idm_fini(void);
61a6d42e7dSPeter Dunlap static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
62a6d42e7dSPeter Dunlap static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
63a6d42e7dSPeter Dunlap static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
64a6d42e7dSPeter Dunlap static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
6561dfa509SRick McNeal static stmf_status_t idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
66a6d42e7dSPeter Dunlap     idm_abort_type_t abort_type);
67a6d42e7dSPeter Dunlap static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
6830e7468fSPeter Dunlap static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
6930e7468fSPeter Dunlap     int sleepflag);
70a6d42e7dSPeter Dunlap 
71a6d42e7dSPeter Dunlap boolean_t idm_conn_logging = 0;
72a6d42e7dSPeter Dunlap boolean_t idm_svc_logging = 0;
7330e7468fSPeter Dunlap #ifdef DEBUG
7430e7468fSPeter Dunlap boolean_t idm_pattern_checking = 1;
7530e7468fSPeter Dunlap #else
7630e7468fSPeter Dunlap boolean_t idm_pattern_checking = 0;
7730e7468fSPeter Dunlap #endif
78a6d42e7dSPeter Dunlap 
79a6d42e7dSPeter Dunlap /*
80a6d42e7dSPeter Dunlap  * Potential tuneable for the maximum number of tasks.  Default to
81a6d42e7dSPeter Dunlap  * IDM_TASKIDS_MAX
82a6d42e7dSPeter Dunlap  */
83a6d42e7dSPeter Dunlap 
84a6d42e7dSPeter Dunlap uint32_t	idm_max_taskids = IDM_TASKIDS_MAX;
85a6d42e7dSPeter Dunlap 
86a6d42e7dSPeter Dunlap /*
87a6d42e7dSPeter Dunlap  * Global list of transport handles
88a6d42e7dSPeter Dunlap  *   These are listed in preferential order, so we can simply take the
89a6d42e7dSPeter Dunlap  *   first "it_conn_is_capable" hit. Note also that the order maps to
90a6d42e7dSPeter Dunlap  *   the order of the idm_transport_type_t list.
91a6d42e7dSPeter Dunlap  */
92a6d42e7dSPeter Dunlap idm_transport_t idm_transport_list[] = {
93a6d42e7dSPeter Dunlap 
94a6d42e7dSPeter Dunlap 	/* iSER on InfiniBand transport handle */
95a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_ISER,	/* type */
96a6d42e7dSPeter Dunlap 	"/devices/ib/iser@0:iser",	/* device path */
97a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
98a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
99a6d42e7dSPeter Dunlap 	NULL},				/* transport caps */
100a6d42e7dSPeter Dunlap 
101a6d42e7dSPeter Dunlap 	/* IDM native sockets transport handle */
102a6d42e7dSPeter Dunlap 	{IDM_TRANSPORT_TYPE_SOCKETS,	/* type */
103a6d42e7dSPeter Dunlap 	NULL,				/* device path */
104a6d42e7dSPeter Dunlap 	NULL,				/* LDI handle */
105a6d42e7dSPeter Dunlap 	NULL,				/* transport ops */
106a6d42e7dSPeter Dunlap 	NULL}				/* transport caps */
107a6d42e7dSPeter Dunlap 
108a6d42e7dSPeter Dunlap };
109a6d42e7dSPeter Dunlap 
110*a98e9e2eSToomas Soome idm_global_t idm;	/* Global state */
111*a98e9e2eSToomas Soome 
112a6d42e7dSPeter Dunlap int
_init(void)113a6d42e7dSPeter Dunlap _init(void)
114a6d42e7dSPeter Dunlap {
115a6d42e7dSPeter Dunlap 	int rc;
116a6d42e7dSPeter Dunlap 
117a6d42e7dSPeter Dunlap 	if ((rc = _idm_init()) != 0) {
118a6d42e7dSPeter Dunlap 		return (rc);
119a6d42e7dSPeter Dunlap 	}
120a6d42e7dSPeter Dunlap 
121a6d42e7dSPeter Dunlap 	return (mod_install(&modlinkage));
122a6d42e7dSPeter Dunlap }
123a6d42e7dSPeter Dunlap 
124a6d42e7dSPeter Dunlap int
_fini(void)125a6d42e7dSPeter Dunlap _fini(void)
126a6d42e7dSPeter Dunlap {
127a6d42e7dSPeter Dunlap 	int rc;
128a6d42e7dSPeter Dunlap 
129a6d42e7dSPeter Dunlap 	if ((rc = _idm_fini()) != 0) {
130a6d42e7dSPeter Dunlap 		return (rc);
131a6d42e7dSPeter Dunlap 	}
132a6d42e7dSPeter Dunlap 
133a6d42e7dSPeter Dunlap 	if ((rc = mod_remove(&modlinkage)) != 0) {
134a6d42e7dSPeter Dunlap 		return (rc);
135a6d42e7dSPeter Dunlap 	}
136a6d42e7dSPeter Dunlap 
137a6d42e7dSPeter Dunlap 	return (rc);
138a6d42e7dSPeter Dunlap }
139a6d42e7dSPeter Dunlap 
140a6d42e7dSPeter Dunlap int
_info(struct modinfo * modinfop)141a6d42e7dSPeter Dunlap _info(struct modinfo *modinfop)
142a6d42e7dSPeter Dunlap {
143a6d42e7dSPeter Dunlap 	return (mod_info(&modlinkage, modinfop));
144a6d42e7dSPeter Dunlap }
145a6d42e7dSPeter Dunlap 
146a6d42e7dSPeter Dunlap /*
147a6d42e7dSPeter Dunlap  * idm_transport_register()
148a6d42e7dSPeter Dunlap  *
149a6d42e7dSPeter Dunlap  * Provides a mechanism for an IDM transport driver to register its
150a6d42e7dSPeter Dunlap  * transport ops and caps with the IDM kernel module. Invoked during
151a6d42e7dSPeter Dunlap  * a transport driver's attach routine.
152a6d42e7dSPeter Dunlap  */
153a6d42e7dSPeter Dunlap idm_status_t
idm_transport_register(idm_transport_attr_t * attr)154a6d42e7dSPeter Dunlap idm_transport_register(idm_transport_attr_t *attr)
155a6d42e7dSPeter Dunlap {
156a6d42e7dSPeter Dunlap 	ASSERT(attr->it_ops != NULL);
157a6d42e7dSPeter Dunlap 	ASSERT(attr->it_caps != NULL);
158a6d42e7dSPeter Dunlap 
159a6d42e7dSPeter Dunlap 	switch (attr->type) {
160a6d42e7dSPeter Dunlap 	/* All known non-native transports here; for now, iSER */
161a6d42e7dSPeter Dunlap 	case IDM_TRANSPORT_TYPE_ISER:
162a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_ops	= attr->it_ops;
163a6d42e7dSPeter Dunlap 		idm_transport_list[attr->type].it_caps	= attr->it_caps;
164a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
165a6d42e7dSPeter Dunlap 
166a6d42e7dSPeter Dunlap 	default:
167a6d42e7dSPeter Dunlap 		cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in "
168a6d42e7dSPeter Dunlap 		    "idm_transport_register", attr->type);
169a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
170a6d42e7dSPeter Dunlap 	}
171a6d42e7dSPeter Dunlap }
172a6d42e7dSPeter Dunlap 
173a6d42e7dSPeter Dunlap /*
174a6d42e7dSPeter Dunlap  * idm_ini_conn_create
175a6d42e7dSPeter Dunlap  *
176a6d42e7dSPeter Dunlap  * This function is invoked by the iSCSI layer to create a connection context.
177a6d42e7dSPeter Dunlap  * This does not actually establish the socket connection.
178a6d42e7dSPeter Dunlap  *
179a6d42e7dSPeter Dunlap  * cr - Connection request parameters
180a6d42e7dSPeter Dunlap  * new_con - Output parameter that contains the new request if successful
181a6d42e7dSPeter Dunlap  *
182a6d42e7dSPeter Dunlap  */
183a6d42e7dSPeter Dunlap idm_status_t
idm_ini_conn_create(idm_conn_req_t * cr,idm_conn_t ** new_con)184a6d42e7dSPeter Dunlap idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con)
185a6d42e7dSPeter Dunlap {
186a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
187a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
188a6d42e7dSPeter Dunlap 	int			rc;
189a6d42e7dSPeter Dunlap 
190a6d42e7dSPeter Dunlap 	it = idm_transport_lookup(cr);
191a6d42e7dSPeter Dunlap 
192a6d42e7dSPeter Dunlap retry:
193a6d42e7dSPeter Dunlap 	ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type,
194a6d42e7dSPeter Dunlap 	    &cr->icr_conn_ops);
195a6d42e7dSPeter Dunlap 
196a6d42e7dSPeter Dunlap 	bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr,
197a6d42e7dSPeter Dunlap 	    sizeof (cr->cr_ini_dst_addr));
198a6d42e7dSPeter Dunlap 
199a6d42e7dSPeter Dunlap 	/* create the transport-specific connection components */
200a6d42e7dSPeter Dunlap 	rc = it->it_ops->it_ini_conn_create(cr, ic);
201a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
202a6d42e7dSPeter Dunlap 		/* cleanup the failed connection */
203a6d42e7dSPeter Dunlap 		idm_conn_destroy_common(ic);
204a6d42e7dSPeter Dunlap 
205a6d42e7dSPeter Dunlap 		/*
206a6d42e7dSPeter Dunlap 		 * It is possible for an IB client to connect to
207a6d42e7dSPeter Dunlap 		 * an ethernet-only client via an IB-eth gateway.
208a6d42e7dSPeter Dunlap 		 * Therefore, if we are attempting to use iSER and
209a6d42e7dSPeter Dunlap 		 * fail, retry with sockets before ultimately
210a6d42e7dSPeter Dunlap 		 * failing the connection.
211a6d42e7dSPeter Dunlap 		 */
212a6d42e7dSPeter Dunlap 		if (it->it_type == IDM_TRANSPORT_TYPE_ISER) {
213a6d42e7dSPeter Dunlap 			it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS];
214a6d42e7dSPeter Dunlap 			goto retry;
215a6d42e7dSPeter Dunlap 		}
216a6d42e7dSPeter Dunlap 
217a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
218a6d42e7dSPeter Dunlap 	}
219a6d42e7dSPeter Dunlap 
220a6d42e7dSPeter Dunlap 	*new_con = ic;
221a6d42e7dSPeter Dunlap 
222a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
223a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_ini_conn_list, ic);
224a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
225a6d42e7dSPeter Dunlap 
226a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
227a6d42e7dSPeter Dunlap }
228a6d42e7dSPeter Dunlap 
229a6d42e7dSPeter Dunlap /*
230a6d42e7dSPeter Dunlap  * idm_ini_conn_destroy
231a6d42e7dSPeter Dunlap  *
232a6d42e7dSPeter Dunlap  * Releases any resources associated with the connection.  This is the
233a6d42e7dSPeter Dunlap  * complement to idm_ini_conn_create.
234a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
235a6d42e7dSPeter Dunlap  *
236a6d42e7dSPeter Dunlap  */
23730e7468fSPeter Dunlap void
idm_ini_conn_destroy_task(void * ic_void)23830e7468fSPeter Dunlap idm_ini_conn_destroy_task(void *ic_void)
23930e7468fSPeter Dunlap {
24030e7468fSPeter Dunlap 	idm_conn_t *ic = ic_void;
24130e7468fSPeter Dunlap 
24230e7468fSPeter Dunlap 	ic->ic_transport_ops->it_ini_conn_destroy(ic);
24330e7468fSPeter Dunlap 	idm_conn_destroy_common(ic);
24430e7468fSPeter Dunlap }
24530e7468fSPeter Dunlap 
246a6d42e7dSPeter Dunlap void
idm_ini_conn_destroy(idm_conn_t * ic)247a6d42e7dSPeter Dunlap idm_ini_conn_destroy(idm_conn_t *ic)
248a6d42e7dSPeter Dunlap {
24930e7468fSPeter Dunlap 	/*
25030e7468fSPeter Dunlap 	 * It's reasonable for the initiator to call idm_ini_conn_destroy
25130e7468fSPeter Dunlap 	 * from within the context of the CN_CONNECT_DESTROY notification.
25230e7468fSPeter Dunlap 	 * That's a problem since we want to destroy the taskq for the
25330e7468fSPeter Dunlap 	 * state machine associated with the connection.  Remove the
25430e7468fSPeter Dunlap 	 * connection from the list right away then handle the remaining
25530e7468fSPeter Dunlap 	 * work via the idm_global_taskq.
25630e7468fSPeter Dunlap 	 */
257a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
258a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_ini_conn_list, ic);
259a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
260a6d42e7dSPeter Dunlap 
26130e7468fSPeter Dunlap 	if (taskq_dispatch(idm.idm_global_taskq,
262fc8ae2ecSToomas Soome 	    &idm_ini_conn_destroy_task, ic, TQ_SLEEP) == TASKQID_INVALID) {
26330e7468fSPeter Dunlap 		cmn_err(CE_WARN,
26430e7468fSPeter Dunlap 		    "idm_ini_conn_destroy: Couldn't dispatch task");
26530e7468fSPeter Dunlap 	}
266a6d42e7dSPeter Dunlap }
267a6d42e7dSPeter Dunlap 
268a6d42e7dSPeter Dunlap /*
269a6d42e7dSPeter Dunlap  * idm_ini_conn_connect
270a6d42e7dSPeter Dunlap  *
271a6d42e7dSPeter Dunlap  * Establish connection to the remote system identified in idm_conn_t.
272a6d42e7dSPeter Dunlap  * The connection parameters including the remote IP address were established
27330e7468fSPeter Dunlap  * in the call to idm_ini_conn_create.  The IDM state machine will
27430e7468fSPeter Dunlap  * perform client notifications as necessary to prompt the initiator through
27530e7468fSPeter Dunlap  * the login process.  IDM also keeps a timer running so that if the login
27630e7468fSPeter Dunlap  * process doesn't complete in a timely manner it will fail.
277a6d42e7dSPeter Dunlap  *
278a6d42e7dSPeter Dunlap  * ic - idm_conn_t structure representing the relevant connection
279a6d42e7dSPeter Dunlap  *
280a6d42e7dSPeter Dunlap  * Returns success if the connection was established, otherwise some kind
281a6d42e7dSPeter Dunlap  * of meaningful error code.
282a6d42e7dSPeter Dunlap  *
28330e7468fSPeter Dunlap  * Upon return the login has either failed or is loggin in (ffp)
284a6d42e7dSPeter Dunlap  */
285a6d42e7dSPeter Dunlap idm_status_t
idm_ini_conn_connect(idm_conn_t * ic)286a6d42e7dSPeter Dunlap idm_ini_conn_connect(idm_conn_t *ic)
287a6d42e7dSPeter Dunlap {
2885f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	idm_status_t	rc;
289a6d42e7dSPeter Dunlap 
290a6d42e7dSPeter Dunlap 	rc = idm_conn_sm_init(ic);
291a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
292a6d42e7dSPeter Dunlap 		return (ic->ic_conn_sm_status);
293a6d42e7dSPeter Dunlap 	}
29430e7468fSPeter Dunlap 
29530e7468fSPeter Dunlap 	/* Hold connection until we return */
29630e7468fSPeter Dunlap 	idm_conn_hold(ic);
29730e7468fSPeter Dunlap 
298a6d42e7dSPeter Dunlap 	/* Kick state machine */
2998e718be9SToomas Soome 	idm_conn_event(ic, CE_CONNECT_REQ, (uintptr_t)NULL);
300a6d42e7dSPeter Dunlap 
301a6d42e7dSPeter Dunlap 	/* Wait for login flag */
302a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_state_mutex);
303a6d42e7dSPeter Dunlap 	while (!(ic->ic_state_flags & CF_LOGIN_READY) &&
304a6d42e7dSPeter Dunlap 	    !(ic->ic_state_flags & CF_ERROR)) {
305a6d42e7dSPeter Dunlap 		cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
306a6d42e7dSPeter Dunlap 	}
307a6d42e7dSPeter Dunlap 
3085f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	/*
3095f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	 * The CN_READY_TO_LOGIN and/or the CN_CONNECT_FAIL call to
3105f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	 * idm_notify_client has already been generated by the idm conn
3115f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	 * state machine.  If connection fails any time after this
3125f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	 * check, we will detect it in iscsi_login.
3135f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	 */
314a6d42e7dSPeter Dunlap 	if (ic->ic_state_flags & CF_ERROR) {
3155f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		rc = ic->ic_conn_sm_status;
316a6d42e7dSPeter Dunlap 	}
3175f7d09c6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 	mutex_exit(&ic->ic_state_mutex);
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
idm_ini_conn_disconnect(idm_conn_t * ic)33630e7468fSPeter Dunlap idm_ini_conn_disconnect(idm_conn_t *ic)
337a6d42e7dSPeter Dunlap {
3388e718be9SToomas Soome 	idm_conn_event(ic, CE_TRANSPORT_FAIL, (uintptr_t)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
idm_ini_conn_disconnect_sync(idm_conn_t * ic)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)) {
3578e718be9SToomas Soome 		idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, (uintptr_t)NULL,
3588e718be9SToomas Soome 		    CT_NONE);
35930e7468fSPeter Dunlap 		while ((ic->ic_state != CS_S9_INIT_ERROR) &&
36030e7468fSPeter Dunlap 		    (ic->ic_state != CS_S11_COMPLETE))
36130e7468fSPeter Dunlap 			cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex);
362a6d42e7dSPeter Dunlap 	}
363a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_state_mutex);
364a6d42e7dSPeter Dunlap }
365a6d42e7dSPeter Dunlap 
366a6d42e7dSPeter Dunlap /*
367a6d42e7dSPeter Dunlap  * idm_tgt_svc_create
368a6d42e7dSPeter Dunlap  *
369a6d42e7dSPeter Dunlap  * The target calls this service to obtain a service context for each available
370a6d42e7dSPeter Dunlap  * transport, starting a service of each type related to the IP address and port
371a6d42e7dSPeter Dunlap  * passed. The idm_svc_req_t contains the service parameters.
372a6d42e7dSPeter Dunlap  */
373a6d42e7dSPeter Dunlap idm_status_t
idm_tgt_svc_create(idm_svc_req_t * sr,idm_svc_t ** new_svc)374a6d42e7dSPeter Dunlap idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc)
375a6d42e7dSPeter Dunlap {
376a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
377a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
378a6d42e7dSPeter Dunlap 	idm_svc_t		*is;
379a6d42e7dSPeter Dunlap 	int			rc;
380a6d42e7dSPeter Dunlap 
381a6d42e7dSPeter Dunlap 	*new_svc = NULL;
382a6d42e7dSPeter Dunlap 	is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP);
383a6d42e7dSPeter Dunlap 
384a6d42e7dSPeter Dunlap 	/* Initialize transport-agnostic components of the service handle */
385a6d42e7dSPeter Dunlap 	is->is_svc_req = *sr;
386a6d42e7dSPeter Dunlap 	mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL);
387a6d42e7dSPeter Dunlap 	cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL);
388a6d42e7dSPeter Dunlap 	mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL);
389a6d42e7dSPeter Dunlap 	cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL);
390a6d42e7dSPeter Dunlap 	idm_refcnt_init(&is->is_refcnt, is);
391a6d42e7dSPeter Dunlap 
392a6d42e7dSPeter Dunlap 	/*
393a6d42e7dSPeter Dunlap 	 * Make sure all available transports are setup.  We call this now
394a6d42e7dSPeter Dunlap 	 * instead of at initialization time in case IB has become available
395a6d42e7dSPeter Dunlap 	 * since we started (hotplug, etc).
396a6d42e7dSPeter Dunlap 	 */
397bbe72583SJack Meng 	idm_transport_setup(sr->sr_li, B_FALSE);
398a6d42e7dSPeter Dunlap 
399a6d42e7dSPeter Dunlap 	/*
400a6d42e7dSPeter Dunlap 	 * Loop through the transports, configuring the transport-specific
401a6d42e7dSPeter Dunlap 	 * components of each one.
402a6d42e7dSPeter Dunlap 	 */
403a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
404a6d42e7dSPeter Dunlap 
405a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
406a6d42e7dSPeter Dunlap 		/*
407a6d42e7dSPeter Dunlap 		 * If it_ops is NULL then the transport is unconfigured
408a6d42e7dSPeter Dunlap 		 * and we shouldn't try to start the service.
409a6d42e7dSPeter Dunlap 		 */
410a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
411a6d42e7dSPeter Dunlap 			continue;
412a6d42e7dSPeter Dunlap 		}
413a6d42e7dSPeter Dunlap 
414a6d42e7dSPeter Dunlap 		rc = it->it_ops->it_tgt_svc_create(sr, is);
415a6d42e7dSPeter Dunlap 		if (rc != IDM_STATUS_SUCCESS) {
416a6d42e7dSPeter Dunlap 			/* Teardown any configured services */
417a6d42e7dSPeter Dunlap 			while (type--) {
418a6d42e7dSPeter Dunlap 				it = &idm_transport_list[type];
419a6d42e7dSPeter Dunlap 				if (it->it_ops == NULL) {
420a6d42e7dSPeter Dunlap 					continue;
421a6d42e7dSPeter Dunlap 				}
422a6d42e7dSPeter Dunlap 				it->it_ops->it_tgt_svc_destroy(is);
423a6d42e7dSPeter Dunlap 			}
424a6d42e7dSPeter Dunlap 			/* Free the svc context and return */
425a6d42e7dSPeter Dunlap 			kmem_free(is, sizeof (idm_svc_t));
426a6d42e7dSPeter Dunlap 			return (rc);
427a6d42e7dSPeter Dunlap 		}
428a6d42e7dSPeter Dunlap 	}
429a6d42e7dSPeter Dunlap 
430a6d42e7dSPeter Dunlap 	*new_svc = is;
431a6d42e7dSPeter Dunlap 
432a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
433a6d42e7dSPeter Dunlap 	list_insert_tail(&idm.idm_tgt_svc_list, is);
434a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
435a6d42e7dSPeter Dunlap 
436a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
437a6d42e7dSPeter Dunlap }
438a6d42e7dSPeter Dunlap 
439a6d42e7dSPeter Dunlap /*
440a6d42e7dSPeter Dunlap  * idm_tgt_svc_destroy
441a6d42e7dSPeter Dunlap  *
442a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
443a6d42e7dSPeter Dunlap  *
444a6d42e7dSPeter Dunlap  * Cleanup any resources associated with the idm_svc_t.
445a6d42e7dSPeter Dunlap  */
446a6d42e7dSPeter Dunlap void
idm_tgt_svc_destroy(idm_svc_t * is)447a6d42e7dSPeter Dunlap idm_tgt_svc_destroy(idm_svc_t *is)
448a6d42e7dSPeter Dunlap {
449a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
450a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
451a6d42e7dSPeter Dunlap 
452a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
453a6d42e7dSPeter Dunlap 	/* remove this service from the global list */
454a6d42e7dSPeter Dunlap 	list_remove(&idm.idm_tgt_svc_list, is);
455a6d42e7dSPeter Dunlap 	/* wakeup any waiters for service change */
456a6d42e7dSPeter Dunlap 	cv_broadcast(&idm.idm_tgt_svc_cv);
457a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
458a6d42e7dSPeter Dunlap 
459a6d42e7dSPeter Dunlap 	/* teardown each transport-specific service */
460a6d42e7dSPeter Dunlap 	for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
461a6d42e7dSPeter Dunlap 		it = &idm_transport_list[type];
462a6d42e7dSPeter Dunlap 		if (it->it_ops == NULL) {
463a6d42e7dSPeter Dunlap 			continue;
464a6d42e7dSPeter Dunlap 		}
465a6d42e7dSPeter Dunlap 
466a6d42e7dSPeter Dunlap 		it->it_ops->it_tgt_svc_destroy(is);
467a6d42e7dSPeter Dunlap 	}
468a6d42e7dSPeter Dunlap 
46930e7468fSPeter Dunlap 	/* tear down the svc resources */
47030e7468fSPeter Dunlap 	idm_refcnt_destroy(&is->is_refcnt);
47130e7468fSPeter Dunlap 	cv_destroy(&is->is_count_cv);
47230e7468fSPeter Dunlap 	mutex_destroy(&is->is_count_mutex);
47330e7468fSPeter Dunlap 	cv_destroy(&is->is_cv);
47430e7468fSPeter Dunlap 	mutex_destroy(&is->is_mutex);
47530e7468fSPeter Dunlap 
476a6d42e7dSPeter Dunlap 	/* free the svc handle */
477a6d42e7dSPeter Dunlap 	kmem_free(is, sizeof (idm_svc_t));
478a6d42e7dSPeter Dunlap }
479a6d42e7dSPeter Dunlap 
480a6d42e7dSPeter Dunlap void
idm_tgt_svc_hold(idm_svc_t * is)481a6d42e7dSPeter Dunlap idm_tgt_svc_hold(idm_svc_t *is)
482a6d42e7dSPeter Dunlap {
483a6d42e7dSPeter Dunlap 	idm_refcnt_hold(&is->is_refcnt);
484a6d42e7dSPeter Dunlap }
485a6d42e7dSPeter Dunlap 
486a6d42e7dSPeter Dunlap void
idm_tgt_svc_rele_and_destroy(idm_svc_t * is)487a6d42e7dSPeter Dunlap idm_tgt_svc_rele_and_destroy(idm_svc_t *is)
488a6d42e7dSPeter Dunlap {
489a6d42e7dSPeter Dunlap 	idm_refcnt_rele_and_destroy(&is->is_refcnt,
490a6d42e7dSPeter Dunlap 	    (idm_refcnt_cb_t *)&idm_tgt_svc_destroy);
491a6d42e7dSPeter Dunlap }
492a6d42e7dSPeter Dunlap 
493a6d42e7dSPeter Dunlap /*
494a6d42e7dSPeter Dunlap  * idm_tgt_svc_online
495a6d42e7dSPeter Dunlap  *
496a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
497a6d42e7dSPeter Dunlap  *
498a6d42e7dSPeter Dunlap  * Online each transport service, as we want this target to be accessible
499a6d42e7dSPeter Dunlap  * via any configured transport.
500a6d42e7dSPeter Dunlap  *
501a6d42e7dSPeter Dunlap  * When the initiator establishes a new connection to the target, IDM will
502a6d42e7dSPeter Dunlap  * call the "new connect" callback defined in the idm_svc_req_t structure
503a6d42e7dSPeter Dunlap  * and it will pass an idm_conn_t structure representing that new connection.
504a6d42e7dSPeter Dunlap  */
505a6d42e7dSPeter Dunlap idm_status_t
idm_tgt_svc_online(idm_svc_t * is)506a6d42e7dSPeter Dunlap idm_tgt_svc_online(idm_svc_t *is)
507a6d42e7dSPeter Dunlap {
508a6d42e7dSPeter Dunlap 
50930e7468fSPeter Dunlap 	idm_transport_type_t	type, last_type;
510a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
51130e7468fSPeter Dunlap 	int			rc = IDM_STATUS_SUCCESS;
512a6d42e7dSPeter Dunlap 
513a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
514a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
51530e7468fSPeter Dunlap 		/* Walk through each of the transports and online them */
516a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
517a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
518a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
519a6d42e7dSPeter Dunlap 				/* transport is not registered */
520a6d42e7dSPeter Dunlap 				continue;
521a6d42e7dSPeter Dunlap 			}
522a6d42e7dSPeter Dunlap 
523a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
524a6d42e7dSPeter Dunlap 			rc = it->it_ops->it_tgt_svc_online(is);
525a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
52630e7468fSPeter Dunlap 			if (rc != IDM_STATUS_SUCCESS) {
52730e7468fSPeter Dunlap 				last_type = type;
52830e7468fSPeter Dunlap 				break;
52930e7468fSPeter Dunlap 			}
53030e7468fSPeter Dunlap 		}
53130e7468fSPeter Dunlap 		if (rc != IDM_STATUS_SUCCESS) {
53230e7468fSPeter Dunlap 			/*
53330e7468fSPeter Dunlap 			 * The last transport failed to online.
53430e7468fSPeter Dunlap 			 * Offline any transport onlined above and
53530e7468fSPeter Dunlap 			 * do not online the target.
53630e7468fSPeter Dunlap 			 */
53730e7468fSPeter Dunlap 			for (type = 0; type < last_type; type++) {
53830e7468fSPeter Dunlap 				it = &idm_transport_list[type];
53930e7468fSPeter Dunlap 				if (it->it_ops == NULL) {
54030e7468fSPeter Dunlap 					/* transport is not registered */
54130e7468fSPeter Dunlap 					continue;
54230e7468fSPeter Dunlap 				}
54330e7468fSPeter Dunlap 
54430e7468fSPeter Dunlap 				mutex_exit(&is->is_mutex);
54530e7468fSPeter Dunlap 				it->it_ops->it_tgt_svc_offline(is);
54630e7468fSPeter Dunlap 				mutex_enter(&is->is_mutex);
547a6d42e7dSPeter Dunlap 			}
54830e7468fSPeter Dunlap 		} else {
54930e7468fSPeter Dunlap 			/* Target service now online */
55030e7468fSPeter Dunlap 			is->is_online = 1;
551a6d42e7dSPeter Dunlap 		}
552a6d42e7dSPeter Dunlap 	} else {
55330e7468fSPeter Dunlap 		/* Target service already online, just bump the count */
554a6d42e7dSPeter Dunlap 		is->is_online++;
55530e7468fSPeter Dunlap 	}
556a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
557a6d42e7dSPeter Dunlap 
55830e7468fSPeter Dunlap 	return (rc);
559a6d42e7dSPeter Dunlap }
560a6d42e7dSPeter Dunlap 
561a6d42e7dSPeter Dunlap /*
562a6d42e7dSPeter Dunlap  * idm_tgt_svc_offline
563a6d42e7dSPeter Dunlap  *
564a6d42e7dSPeter Dunlap  * is - idm_svc_t returned by the call to idm_tgt_svc_create
565a6d42e7dSPeter Dunlap  *
566a6d42e7dSPeter Dunlap  * Shutdown any online target services.
567a6d42e7dSPeter Dunlap  */
568a6d42e7dSPeter Dunlap void
idm_tgt_svc_offline(idm_svc_t * is)569a6d42e7dSPeter Dunlap idm_tgt_svc_offline(idm_svc_t *is)
570a6d42e7dSPeter Dunlap {
571a6d42e7dSPeter Dunlap 	idm_transport_type_t	type;
572a6d42e7dSPeter Dunlap 	idm_transport_t		*it;
573a6d42e7dSPeter Dunlap 
574a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
575a6d42e7dSPeter Dunlap 	is->is_online--;
576a6d42e7dSPeter Dunlap 	if (is->is_online == 0) {
577a6d42e7dSPeter Dunlap 		/* Walk through each of the transports and offline them */
578a6d42e7dSPeter Dunlap 		for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) {
579a6d42e7dSPeter Dunlap 			it = &idm_transport_list[type];
580a6d42e7dSPeter Dunlap 			if (it->it_ops == NULL) {
581a6d42e7dSPeter Dunlap 				/* transport is not registered */
582a6d42e7dSPeter Dunlap 				continue;
583a6d42e7dSPeter Dunlap 			}
584a6d42e7dSPeter Dunlap 
585a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
586a6d42e7dSPeter Dunlap 			it->it_ops->it_tgt_svc_offline(is);
587a6d42e7dSPeter Dunlap 			mutex_enter(&is->is_mutex);
588a6d42e7dSPeter Dunlap 		}
589a6d42e7dSPeter Dunlap 	}
590a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
591a6d42e7dSPeter Dunlap }
592a6d42e7dSPeter Dunlap 
593a6d42e7dSPeter Dunlap /*
594a6d42e7dSPeter Dunlap  * idm_tgt_svc_lookup
595a6d42e7dSPeter Dunlap  *
596a6d42e7dSPeter Dunlap  * Lookup a service instance listening on the specified port
597a6d42e7dSPeter Dunlap  */
598a6d42e7dSPeter Dunlap 
599a6d42e7dSPeter Dunlap idm_svc_t *
idm_tgt_svc_lookup(uint16_t port)600a6d42e7dSPeter Dunlap idm_tgt_svc_lookup(uint16_t port)
601a6d42e7dSPeter Dunlap {
602a6d42e7dSPeter Dunlap 	idm_svc_t *result;
603a6d42e7dSPeter Dunlap 
604a6d42e7dSPeter Dunlap retry:
605a6d42e7dSPeter Dunlap 	mutex_enter(&idm.idm_global_mutex);
606a6d42e7dSPeter Dunlap 	for (result = list_head(&idm.idm_tgt_svc_list);
607a6d42e7dSPeter Dunlap 	    result != NULL;
608a6d42e7dSPeter Dunlap 	    result = list_next(&idm.idm_tgt_svc_list, result)) {
609a6d42e7dSPeter Dunlap 		if (result->is_svc_req.sr_port == port) {
610a6d42e7dSPeter Dunlap 			if (result->is_online == 0) {
611a6d42e7dSPeter Dunlap 				/*
612a6d42e7dSPeter Dunlap 				 * A service exists on this port, but it
613a6d42e7dSPeter Dunlap 				 * is going away, wait for it to cleanup.
614a6d42e7dSPeter Dunlap 				 */
615a6d42e7dSPeter Dunlap 				cv_wait(&idm.idm_tgt_svc_cv,
616a6d42e7dSPeter Dunlap 				    &idm.idm_global_mutex);
617a6d42e7dSPeter Dunlap 				mutex_exit(&idm.idm_global_mutex);
618a6d42e7dSPeter Dunlap 				goto retry;
619a6d42e7dSPeter Dunlap 			}
620a6d42e7dSPeter Dunlap 			idm_tgt_svc_hold(result);
621a6d42e7dSPeter Dunlap 			mutex_exit(&idm.idm_global_mutex);
622a6d42e7dSPeter Dunlap 			return (result);
623a6d42e7dSPeter Dunlap 		}
624a6d42e7dSPeter Dunlap 	}
625a6d42e7dSPeter Dunlap 	mutex_exit(&idm.idm_global_mutex);
626a6d42e7dSPeter Dunlap 
627a6d42e7dSPeter Dunlap 	return (NULL);
628a6d42e7dSPeter Dunlap }
629a6d42e7dSPeter Dunlap 
630a6d42e7dSPeter Dunlap /*
631a6d42e7dSPeter Dunlap  * idm_negotiate_key_values()
632a6d42e7dSPeter Dunlap  * Give IDM level a chance to negotiate any login parameters it should own.
633a6d42e7dSPeter Dunlap  *  -- leave unhandled parameters alone on request_nvl
634a6d42e7dSPeter Dunlap  *  -- move all handled parameters to response_nvl with an appropriate response
635a6d42e7dSPeter Dunlap  *  -- also add an entry to negotiated_nvl for any accepted parameters
636a6d42e7dSPeter Dunlap  */
637a6d42e7dSPeter Dunlap kv_status_t
idm_negotiate_key_values(idm_conn_t * ic,nvlist_t * request_nvl,nvlist_t * response_nvl,nvlist_t * negotiated_nvl)638a6d42e7dSPeter Dunlap idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl,
639a6d42e7dSPeter Dunlap     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
640a6d42e7dSPeter Dunlap {
641a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
642a6d42e7dSPeter Dunlap 	return (ic->ic_transport_ops->it_negotiate_key_values(ic,
643a6d42e7dSPeter Dunlap 	    request_nvl, response_nvl, negotiated_nvl));
644a6d42e7dSPeter Dunlap }
645a6d42e7dSPeter Dunlap 
646a6d42e7dSPeter Dunlap /*
647a6d42e7dSPeter Dunlap  * idm_notice_key_values()
648a6d42e7dSPeter Dunlap  * Activate at the IDM level any parameters that have been negotiated.
649a6d42e7dSPeter Dunlap  * Passes the set of key value pairs to the transport for activation.
650a6d42e7dSPeter Dunlap  * This will be invoked as the connection is entering full-feature mode.
651a6d42e7dSPeter Dunlap  */
65230e7468fSPeter Dunlap void
idm_notice_key_values(idm_conn_t * ic,nvlist_t * negotiated_nvl)653a6d42e7dSPeter Dunlap idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl)
654a6d42e7dSPeter Dunlap {
655a6d42e7dSPeter Dunlap 	ASSERT(ic->ic_transport_ops != NULL);
65630e7468fSPeter Dunlap 	ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl);
657a6d42e7dSPeter Dunlap }
658a6d42e7dSPeter Dunlap 
65956261083SCharles Ting /*
66056261083SCharles Ting  * idm_declare_key_values()
66156261083SCharles Ting  * Activate an operational set of declarative parameters from the config_nvl,
66256261083SCharles Ting  * and return the selected values in the outgoing_nvl.
66356261083SCharles Ting  */
66456261083SCharles Ting kv_status_t
idm_declare_key_values(idm_conn_t * ic,nvlist_t * config_nvl,nvlist_t * outgoing_nvl)66556261083SCharles Ting idm_declare_key_values(idm_conn_t *ic, nvlist_t *config_nvl,
66656261083SCharles Ting     nvlist_t *outgoing_nvl)
66756261083SCharles Ting {
66856261083SCharles Ting 	ASSERT(ic->ic_transport_ops != NULL);
66956261083SCharles Ting 	return (ic->ic_transport_ops->it_declare_key_values(ic, config_nvl,
67056261083SCharles Ting 	    outgoing_nvl));
67156261083SCharles Ting }
67256261083SCharles Ting 
673a6d42e7dSPeter Dunlap /*
674a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini
675a6d42e7dSPeter Dunlap  *
676a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Put_Data' operational primitive.
677a6d42e7dSPeter Dunlap  *
678a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
679a6d42e7dSPeter Dunlap  * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer
680a6d42e7dSPeter Dunlap  * on the remote iSCSI node. The I/O buffer represented by 'idb' is
681a6d42e7dSPeter Dunlap  * transferred to the initiator associated with task 'idt'. The connection
682a6d42e7dSPeter Dunlap  * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS,
683a6d42e7dSPeter Dunlap  * and the callback (idb->idb_buf_cb) at transfer completion are
684a6d42e7dSPeter Dunlap  * provided as input.
685a6d42e7dSPeter Dunlap  *
686a6d42e7dSPeter Dunlap  * This data transfer takes place transparently to the remote iSCSI layer,
687a6d42e7dSPeter Dunlap  * i.e. without its participation.
688a6d42e7dSPeter Dunlap  *
689a6d42e7dSPeter Dunlap  * Using sockets, IDM implements the data transfer by segmenting the data
690a6d42e7dSPeter Dunlap  * buffer into appropriately sized iSCSI PDUs and transmitting them to the
691a6d42e7dSPeter Dunlap  * initiator. iSER performs the transfer using RDMA write.
692a6d42e7dSPeter Dunlap  *
693a6d42e7dSPeter Dunlap  */
694a6d42e7dSPeter Dunlap idm_status_t
idm_buf_tx_to_ini(idm_task_t * idt,idm_buf_t * idb,uint32_t offset,uint32_t xfer_len,idm_buf_cb_t idb_buf_cb,void * cb_arg)695a6d42e7dSPeter Dunlap idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb,
696a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
697a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
698a6d42e7dSPeter Dunlap {
699a6d42e7dSPeter Dunlap 	idm_status_t rc;
700a6d42e7dSPeter Dunlap 
701a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
702a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
703a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
704a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
70530e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_start);
70630e7468fSPeter Dunlap 
70730e7468fSPeter Dunlap 	/*
70830e7468fSPeter Dunlap 	 * Buffer should not contain the pattern.  If the pattern is
70930e7468fSPeter Dunlap 	 * present then we've been asked to transmit initialized data
71030e7468fSPeter Dunlap 	 */
71130e7468fSPeter Dunlap 	IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT);
712a6d42e7dSPeter Dunlap 
713a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
714a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
715a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
716a6d42e7dSPeter Dunlap 		idt->idt_tx_to_ini_start++;
717a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
718a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
719a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
720a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini)
721a6d42e7dSPeter Dunlap 		    (idt, idb);
722a6d42e7dSPeter Dunlap 		return (rc);
723a6d42e7dSPeter Dunlap 
724a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
725a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
726a6d42e7dSPeter Dunlap 		/*
727a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
728a6d42e7dSPeter Dunlap 		 * is suspended
729a6d42e7dSPeter Dunlap 		 */
730a6d42e7dSPeter Dunlap 		idm_buf_bind_in_locked(idt, idb);
731a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
732a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
733a6d42e7dSPeter Dunlap 
734a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
735a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
736a6d42e7dSPeter Dunlap 		/*
737a6d42e7dSPeter Dunlap 		 * Once the task is aborted, any buffers added to the
738a6d42e7dSPeter Dunlap 		 * idt_inbufv will never get cleaned up, so just return
739a6d42e7dSPeter Dunlap 		 * SUCCESS.  The buffer should get cleaned up by the
740a6d42e7dSPeter Dunlap 		 * client or framework once task_aborted has completed.
741a6d42e7dSPeter Dunlap 		 */
742a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
743a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
744a6d42e7dSPeter Dunlap 
745a6d42e7dSPeter Dunlap 	default:
746a6d42e7dSPeter Dunlap 		ASSERT(0);
747a6d42e7dSPeter Dunlap 		break;
748a6d42e7dSPeter Dunlap 	}
749a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
750a6d42e7dSPeter Dunlap 
751a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
752a6d42e7dSPeter Dunlap }
753a6d42e7dSPeter Dunlap 
754a6d42e7dSPeter Dunlap /*
755a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini
756a6d42e7dSPeter Dunlap  *
757a6d42e7dSPeter Dunlap  * This is IDM's implementation of the 'Get_Data' operational primitive.
758a6d42e7dSPeter Dunlap  *
759a6d42e7dSPeter Dunlap  * This function is invoked by a target iSCSI layer to request its local
760a6d42e7dSPeter Dunlap  * Datamover layer to retrieve certain data identified by the R2T PDU from the
761a6d42e7dSPeter Dunlap  * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be
762a6d42e7dSPeter Dunlap  * mapped to the respective buffer by the task tags (ITT & TTT).
763a6d42e7dSPeter Dunlap  * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and
764a6d42e7dSPeter Dunlap  * the callback (idb->idb_buf_cb) notification for data transfer completion are
765a6d42e7dSPeter Dunlap  * are provided as input.
766a6d42e7dSPeter Dunlap  *
767a6d42e7dSPeter Dunlap  * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local
768a6d42e7dSPeter Dunlap  * Datamover layer, the local and remote Datamover layers transparently bring
769a6d42e7dSPeter Dunlap  * about the data transfer requested by the R2T PDU, without the participation
770a6d42e7dSPeter Dunlap  * of the iSCSI layers.
771a6d42e7dSPeter Dunlap  *
772a6d42e7dSPeter Dunlap  * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out()
773a6d42e7dSPeter Dunlap  * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read.
774a6d42e7dSPeter Dunlap  *
775a6d42e7dSPeter Dunlap  */
776a6d42e7dSPeter Dunlap idm_status_t
idm_buf_rx_from_ini(idm_task_t * idt,idm_buf_t * idb,uint32_t offset,uint32_t xfer_len,idm_buf_cb_t idb_buf_cb,void * cb_arg)777a6d42e7dSPeter Dunlap idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb,
778a6d42e7dSPeter Dunlap     uint32_t offset, uint32_t xfer_len,
779a6d42e7dSPeter Dunlap     idm_buf_cb_t idb_buf_cb, void *cb_arg)
780a6d42e7dSPeter Dunlap {
781a6d42e7dSPeter Dunlap 	idm_status_t rc;
782a6d42e7dSPeter Dunlap 
783a6d42e7dSPeter Dunlap 	idb->idb_bufoffset = offset;
784a6d42e7dSPeter Dunlap 	idb->idb_xfer_len = xfer_len;
785a6d42e7dSPeter Dunlap 	idb->idb_buf_cb = idb_buf_cb;
786a6d42e7dSPeter Dunlap 	idb->idb_cb_arg = cb_arg;
78730e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_start);
788a6d42e7dSPeter Dunlap 
789a6d42e7dSPeter Dunlap 	/*
790a6d42e7dSPeter Dunlap 	 * "In" buf list is for "Data In" PDU's, "Out" buf list is for
791a6d42e7dSPeter Dunlap 	 * "Data Out" PDU's
792a6d42e7dSPeter Dunlap 	 */
793a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
794a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
795a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
796a6d42e7dSPeter Dunlap 		idt->idt_rx_from_ini_start++;
797a6d42e7dSPeter Dunlap 		idm_task_hold(idt);
798a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
799a6d42e7dSPeter Dunlap 		idb->idb_in_transport = B_TRUE;
800a6d42e7dSPeter Dunlap 		rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini)
801a6d42e7dSPeter Dunlap 		    (idt, idb);
802a6d42e7dSPeter Dunlap 		return (rc);
803a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
804a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
805a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
806a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
807a6d42e7dSPeter Dunlap 		/*
808a6d42e7dSPeter Dunlap 		 * Bind buffer but don't start a transfer since the task
809a6d42e7dSPeter Dunlap 		 * is suspended
810a6d42e7dSPeter Dunlap 		 */
811a6d42e7dSPeter Dunlap 		idm_buf_bind_out_locked(idt, idb);
812a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
813a6d42e7dSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
814a6d42e7dSPeter Dunlap 	default:
815a6d42e7dSPeter Dunlap 		ASSERT(0);
816a6d42e7dSPeter Dunlap 		break;
817a6d42e7dSPeter Dunlap 	}
818a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
819a6d42e7dSPeter Dunlap 
820a6d42e7dSPeter Dunlap 	return (IDM_STATUS_FAIL);
821a6d42e7dSPeter Dunlap }
822a6d42e7dSPeter Dunlap 
823a6d42e7dSPeter Dunlap /*
824a6d42e7dSPeter Dunlap  * idm_buf_tx_to_ini_done
825a6d42e7dSPeter Dunlap  *
826a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
827a6d42e7dSPeter Dunlap  * a call to transport_buf_tx_to_ini
828a6d42e7dSPeter Dunlap  *
829a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
830a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
831a6d42e7dSPeter Dunlap  */
832a6d42e7dSPeter Dunlap void
idm_buf_tx_to_ini_done(idm_task_t * idt,idm_buf_t * idb,idm_status_t status)833a6d42e7dSPeter Dunlap idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
834a6d42e7dSPeter Dunlap {
835a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
836a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
837a6d42e7dSPeter Dunlap 	idb->idb_tx_thread = B_FALSE;
838a6d42e7dSPeter Dunlap 	idt->idt_tx_to_ini_done++;
83930e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_done);
840a6d42e7dSPeter Dunlap 
841a6d42e7dSPeter Dunlap 	/*
842a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
843a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
844a6d42e7dSPeter Dunlap 	 * to 0.
845a6d42e7dSPeter Dunlap 	 */
846a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
847a6d42e7dSPeter Dunlap 	idb->idb_status = status;
848a6d42e7dSPeter Dunlap 
849a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
850a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
85172cf3143Speter dunlap 		idt->idt_ic->ic_timestamp = ddi_get_lbolt();
852a6d42e7dSPeter Dunlap 		idm_buf_unbind_in_locked(idt, idb);
853a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
854a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
855a6d42e7dSPeter Dunlap 		return;
856a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
857a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
858a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
859a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
860a6d42e7dSPeter Dunlap 		/*
861a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
862a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
863a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
864a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
865a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
866a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
867a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
868a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
869a6d42e7dSPeter Dunlap 		 *
870a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
871a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
872a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
873a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want
874a6d42e7dSPeter Dunlap 		 * the client to here from us again after the call to
875a6d42e7dSPeter Dunlap 		 * client_task_aborted() but we don't want to give it a bunch
876a6d42e7dSPeter Dunlap 		 * of failed buffer transfers until we've called
877a6d42e7dSPeter Dunlap 		 * client_task_aborted().  Instead we'll just leave all the
878a6d42e7dSPeter Dunlap 		 * buffers bound and allow the client to cleanup.
879a6d42e7dSPeter Dunlap 		 */
880a6d42e7dSPeter Dunlap 		break;
881a6d42e7dSPeter Dunlap 	default:
882a6d42e7dSPeter Dunlap 		ASSERT(0);
883a6d42e7dSPeter Dunlap 	}
884a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
885a6d42e7dSPeter Dunlap }
886a6d42e7dSPeter Dunlap 
887a6d42e7dSPeter Dunlap /*
888a6d42e7dSPeter Dunlap  * idm_buf_rx_from_ini_done
889a6d42e7dSPeter Dunlap  *
890a6d42e7dSPeter Dunlap  * The transport calls this after it has completed a transfer requested by
891a6d42e7dSPeter Dunlap  * a call totransport_buf_tx_to_ini
892a6d42e7dSPeter Dunlap  *
893a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning.
894a6d42e7dSPeter Dunlap  * idt may be freed after the call to idb->idb_buf_cb.
895a6d42e7dSPeter Dunlap  */
896a6d42e7dSPeter Dunlap void
idm_buf_rx_from_ini_done(idm_task_t * idt,idm_buf_t * idb,idm_status_t status)897a6d42e7dSPeter Dunlap idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status)
898a6d42e7dSPeter Dunlap {
899a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
900a6d42e7dSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
901a6d42e7dSPeter Dunlap 	idt->idt_rx_from_ini_done++;
90230e7468fSPeter Dunlap 	gethrestime(&idb->idb_xfer_done);
903a6d42e7dSPeter Dunlap 
904a6d42e7dSPeter Dunlap 	/*
905a6d42e7dSPeter Dunlap 	 * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or
906a6d42e7dSPeter Dunlap 	 * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes
907a6d42e7dSPeter Dunlap 	 * to 0.
908a6d42e7dSPeter Dunlap 	 */
909a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
910a6d42e7dSPeter Dunlap 	idb->idb_status = status;
911a6d42e7dSPeter Dunlap 
91230e7468fSPeter Dunlap 	if (status == IDM_STATUS_SUCCESS) {
91330e7468fSPeter Dunlap 		/*
91430e7468fSPeter Dunlap 		 * Buffer should not contain the pattern.  If it does then
91530e7468fSPeter Dunlap 		 * we did not get the data from the remote host.
91630e7468fSPeter Dunlap 		 */
91730e7468fSPeter Dunlap 		IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT);
91830e7468fSPeter Dunlap 	}
91930e7468fSPeter Dunlap 
920a6d42e7dSPeter Dunlap 	switch (idt->idt_state) {
921a6d42e7dSPeter Dunlap 	case TASK_ACTIVE:
92272cf3143Speter dunlap 		idt->idt_ic->ic_timestamp = ddi_get_lbolt();
923a6d42e7dSPeter Dunlap 		idm_buf_unbind_out_locked(idt, idb);
924a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
925a6d42e7dSPeter Dunlap 		(*idb->idb_buf_cb)(idb, status);
926a6d42e7dSPeter Dunlap 		return;
927a6d42e7dSPeter Dunlap 	case TASK_SUSPENDING:
928a6d42e7dSPeter Dunlap 	case TASK_SUSPENDED:
929a6d42e7dSPeter Dunlap 	case TASK_ABORTING:
930a6d42e7dSPeter Dunlap 	case TASK_ABORTED:
931a6d42e7dSPeter Dunlap 		/*
932a6d42e7dSPeter Dunlap 		 * To keep things simple we will ignore the case where the
933a6d42e7dSPeter Dunlap 		 * transfer was successful and leave all buffers bound to the
934a6d42e7dSPeter Dunlap 		 * task.  This allows us to also ignore the case where we've
935a6d42e7dSPeter Dunlap 		 * been asked to abort a task but the last transfer of the
936a6d42e7dSPeter Dunlap 		 * task has completed.  IDM has no idea whether this was, in
937a6d42e7dSPeter Dunlap 		 * fact, the last transfer of the task so it would be difficult
938a6d42e7dSPeter Dunlap 		 * to handle this case.  Everything should get sorted out again
939a6d42e7dSPeter Dunlap 		 * after task reassignment is complete.
940a6d42e7dSPeter Dunlap 		 *
941a6d42e7dSPeter Dunlap 		 * In the case of TASK_ABORTING we could conceivably call the
942a6d42e7dSPeter Dunlap 		 * buffer callback here but the timing of when the client's
943a6d42e7dSPeter Dunlap 		 * client_task_aborted callback is invoked vs. when the client's
944a6d42e7dSPeter Dunlap 		 * buffer callback gets invoked gets sticky.  We don't want