xref: /illumos-gate/usr/src/uts/common/io/idm/idm_so.c (revision cf8c0eba)
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 /*
2263528ae4SJames Moore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23a6d42e7dSPeter Dunlap  * Use is subject to license terms.
24a6d42e7dSPeter Dunlap  */
25a6d42e7dSPeter Dunlap 
26a6d42e7dSPeter Dunlap #include <sys/conf.h>
27a6d42e7dSPeter Dunlap #include <sys/stat.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 #include <sys/priv.h>
33a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
34a6d42e7dSPeter Dunlap #include <sys/socket.h>
35a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
36a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
37a6d42e7dSPeter Dunlap #include <sys/sdt.h>
38a6d42e7dSPeter Dunlap #include <netinet/tcp.h>
39a6d42e7dSPeter Dunlap #include <inet/tcp.h>
40a6d42e7dSPeter Dunlap #include <sys/socketvar.h>
41a6d42e7dSPeter Dunlap #include <sys/pathname.h>
42a6d42e7dSPeter Dunlap #include <sys/fs/snode.h>
43a6d42e7dSPeter Dunlap #include <sys/fs/dv_node.h>
44a6d42e7dSPeter Dunlap #include <sys/vnode.h>
45a6d42e7dSPeter Dunlap #include <netinet/in.h>
46a6d42e7dSPeter Dunlap #include <net/if.h>
47a6d42e7dSPeter Dunlap #include <sys/sockio.h>
480f1702c5SYu Xiangning #include <sys/ksocket.h>
49a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
50a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
51a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h>
52a6d42e7dSPeter Dunlap 
53a6d42e7dSPeter Dunlap /*
54a6d42e7dSPeter Dunlap  * in6addr_any is currently all zeroes, but use the macro in case this
55a6d42e7dSPeter Dunlap  * ever changes.
56a6d42e7dSPeter Dunlap  */
57a6d42e7dSPeter Dunlap const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
58a6d42e7dSPeter Dunlap 
59a6d42e7dSPeter Dunlap static void idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
60a6d42e7dSPeter Dunlap static void idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
61a6d42e7dSPeter Dunlap static void idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
62a6d42e7dSPeter Dunlap 
630f1702c5SYu Xiangning static idm_status_t idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so);
64a6d42e7dSPeter Dunlap static void idm_so_conn_destroy_common(idm_conn_t *ic);
65a6d42e7dSPeter Dunlap static void idm_so_conn_connect_common(idm_conn_t *ic);
66a6d42e7dSPeter Dunlap 
67a6d42e7dSPeter Dunlap static void idm_set_ini_preconnect_options(idm_so_conn_t *sc);
68a6d42e7dSPeter Dunlap static void idm_set_ini_postconnect_options(idm_so_conn_t *sc);
690f1702c5SYu Xiangning static void idm_set_tgt_connect_options(ksocket_t so);
70a6d42e7dSPeter Dunlap static idm_status_t idm_i_so_tx(idm_pdu_t *pdu);
71a6d42e7dSPeter Dunlap 
72a6d42e7dSPeter Dunlap static idm_status_t idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu);
7330e7468fSPeter Dunlap static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt,
7430e7468fSPeter Dunlap     idm_buf_t *idb, uint32_t offset, uint32_t length);
7530e7468fSPeter Dunlap static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb);
7630e7468fSPeter Dunlap static idm_status_t idm_so_send_buf_region(idm_task_t *idt,
77a6d42e7dSPeter Dunlap     idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length);
78a6d42e7dSPeter Dunlap 
79a6d42e7dSPeter Dunlap static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb,
80a6d42e7dSPeter Dunlap     uint32_t ro, uint32_t dlength);
81a6d42e7dSPeter Dunlap 
82a6d42e7dSPeter Dunlap static idm_status_t idm_so_handle_digest(idm_conn_t *it,
83a6d42e7dSPeter Dunlap     nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx);
84a6d42e7dSPeter Dunlap 
85a6d42e7dSPeter Dunlap /*
86a6d42e7dSPeter Dunlap  * Transport ops prototypes
87a6d42e7dSPeter Dunlap  */
88a6d42e7dSPeter Dunlap static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu);
89a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
90a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
91a6d42e7dSPeter Dunlap static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu);
92a6d42e7dSPeter Dunlap static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu);
93a6d42e7dSPeter Dunlap static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu);
94a6d42e7dSPeter Dunlap static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt);
95a6d42e7dSPeter Dunlap static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it,
96a6d42e7dSPeter Dunlap     nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
9730e7468fSPeter Dunlap static void idm_so_notice_key_values(idm_conn_t *it,
98a6d42e7dSPeter Dunlap     nvlist_t *negotiated_nvl);
99a6d42e7dSPeter Dunlap static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic,
100a6d42e7dSPeter Dunlap     idm_transport_caps_t *caps);
101a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen);
102a6d42e7dSPeter Dunlap static void idm_so_buf_free(idm_buf_t *idb);
103a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_setup(idm_buf_t *idb);
104a6d42e7dSPeter Dunlap static void idm_so_buf_teardown(idm_buf_t *idb);
105a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is);
106a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_destroy(idm_svc_t *is);
107a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is);
108a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_offline(idm_svc_t *is);
109a6d42e7dSPeter Dunlap static void idm_so_tgt_conn_destroy(idm_conn_t *ic);
110a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic);
111a6d42e7dSPeter Dunlap static void idm_so_conn_disconnect(idm_conn_t *ic);
112a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic);
113a6d42e7dSPeter Dunlap static void idm_so_ini_conn_destroy(idm_conn_t *ic);
114a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic);
115a6d42e7dSPeter Dunlap 
116a6d42e7dSPeter Dunlap /*
117a6d42e7dSPeter Dunlap  * IDM Native Sockets transport operations
118a6d42e7dSPeter Dunlap  */
119a6d42e7dSPeter Dunlap static
120a6d42e7dSPeter Dunlap idm_transport_ops_t idm_so_transport_ops = {
121a6d42e7dSPeter Dunlap 	idm_so_tx,			/* it_tx_pdu */
122a6d42e7dSPeter Dunlap 	idm_so_buf_tx_to_ini,		/* it_buf_tx_to_ini */
123a6d42e7dSPeter Dunlap 	idm_so_buf_rx_from_ini,		/* it_buf_rx_from_ini */
124a6d42e7dSPeter Dunlap 	idm_so_rx_datain,		/* it_rx_datain */
125a6d42e7dSPeter Dunlap 	idm_so_rx_rtt,			/* it_rx_rtt */
126a6d42e7dSPeter Dunlap 	idm_so_rx_dataout,		/* it_rx_dataout */
127a6d42e7dSPeter Dunlap 	NULL,				/* it_alloc_conn_rsrc */
128a6d42e7dSPeter Dunlap 	NULL,				/* it_free_conn_rsrc */
129a6d42e7dSPeter Dunlap 	NULL,				/* it_tgt_enable_datamover */
130a6d42e7dSPeter Dunlap 	NULL,				/* it_ini_enable_datamover */
131a6d42e7dSPeter Dunlap 	NULL,				/* it_conn_terminate */
132a6d42e7dSPeter Dunlap 	idm_so_free_task_rsrc,		/* it_free_task_rsrc */
133a6d42e7dSPeter Dunlap 	idm_so_negotiate_key_values,	/* it_negotiate_key_values */
134a6d42e7dSPeter Dunlap 	idm_so_notice_key_values,	/* it_notice_key_values */
135a6d42e7dSPeter Dunlap 	idm_so_conn_is_capable,		/* it_conn_is_capable */
136a6d42e7dSPeter Dunlap 	idm_so_buf_alloc,		/* it_buf_alloc */
137a6d42e7dSPeter Dunlap 	idm_so_buf_free,		/* it_buf_free */
138a6d42e7dSPeter Dunlap 	idm_so_buf_setup,		/* it_buf_setup */
139a6d42e7dSPeter Dunlap 	idm_so_buf_teardown,		/* it_buf_teardown */
140a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_create,		/* it_tgt_svc_create */
141a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_destroy,		/* it_tgt_svc_destroy */
142a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_online,		/* it_tgt_svc_online */
143a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_offline,		/* it_tgt_svc_offline */
144a6d42e7dSPeter Dunlap 	idm_so_tgt_conn_destroy,	/* it_tgt_conn_destroy */
145a6d42e7dSPeter Dunlap 	idm_so_tgt_conn_connect,	/* it_tgt_conn_connect */
146a6d42e7dSPeter Dunlap 	idm_so_conn_disconnect,		/* it_tgt_conn_disconnect */
147a6d42e7dSPeter Dunlap 	idm_so_ini_conn_create,		/* it_ini_conn_create */
148a6d42e7dSPeter Dunlap 	idm_so_ini_conn_destroy,	/* it_ini_conn_destroy */
149a6d42e7dSPeter Dunlap 	idm_so_ini_conn_connect,	/* it_ini_conn_connect */
150a6d42e7dSPeter Dunlap 	idm_so_conn_disconnect		/* it_ini_conn_disconnect */
151a6d42e7dSPeter Dunlap };
152a6d42e7dSPeter Dunlap 
153a6d42e7dSPeter Dunlap /*
154a6d42e7dSPeter Dunlap  * idm_so_init()
155a6d42e7dSPeter Dunlap  * Sockets transport initialization
156a6d42e7dSPeter Dunlap  */
157a6d42e7dSPeter Dunlap void
158a6d42e7dSPeter Dunlap idm_so_init(idm_transport_t *it)
159a6d42e7dSPeter Dunlap {
160a6d42e7dSPeter Dunlap 	/* Cache for IDM Data and R2T Transmit PDU's */
161a6d42e7dSPeter Dunlap 	idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache",
162a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
163a6d42e7dSPeter Dunlap 	    &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
164a6d42e7dSPeter Dunlap 
165a6d42e7dSPeter Dunlap 	/* Cache for IDM Receive PDU's */
166a6d42e7dSPeter Dunlap 	idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache",
167a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
168a6d42e7dSPeter Dunlap 	    &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
169a6d42e7dSPeter Dunlap 
170*cf8c0ebaSPeter Dunlap 	/* 128k buffer cache */
171*cf8c0ebaSPeter Dunlap 	idm.idm_so_128k_buf_cache = kmem_cache_create("idm_128k_buf_cache",
172*cf8c0ebaSPeter Dunlap 	    IDM_SO_BUF_CACHE_UB, 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
173*cf8c0ebaSPeter Dunlap 
174a6d42e7dSPeter Dunlap 	/* Set the sockets transport ops */
175a6d42e7dSPeter Dunlap 	it->it_ops = &idm_so_transport_ops;
176a6d42e7dSPeter Dunlap }
177a6d42e7dSPeter Dunlap 
178a6d42e7dSPeter Dunlap /*
179a6d42e7dSPeter Dunlap  * idm_so_fini()
180a6d42e7dSPeter Dunlap  * Sockets transport teardown
181a6d42e7dSPeter Dunlap  */
182a6d42e7dSPeter Dunlap void
183a6d42e7dSPeter Dunlap idm_so_fini(void)
184a6d42e7dSPeter Dunlap {
185*cf8c0ebaSPeter Dunlap 	kmem_cache_destroy(idm.idm_so_128k_buf_cache);
186a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_sotx_pdu_cache);
187a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_sorx_pdu_cache);
188a6d42e7dSPeter Dunlap }
189a6d42e7dSPeter Dunlap 
1900f1702c5SYu Xiangning ksocket_t
191a6d42e7dSPeter Dunlap idm_socreate(int domain, int type, int protocol)
192a6d42e7dSPeter Dunlap {
1930f1702c5SYu Xiangning 	ksocket_t ks;
194a6d42e7dSPeter Dunlap 
1950f1702c5SYu Xiangning 	if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP,
1960f1702c5SYu Xiangning 	    CRED())) {
1970f1702c5SYu Xiangning 		return (ks);
1980f1702c5SYu Xiangning 	} else {
1990f1702c5SYu Xiangning 		return (NULL);
200a6d42e7dSPeter Dunlap 	}
201a6d42e7dSPeter Dunlap }
202a6d42e7dSPeter Dunlap 
203a6d42e7dSPeter Dunlap /*
204a6d42e7dSPeter Dunlap  * idm_soshutdown will disconnect the socket and prevent subsequent PDU
205a6d42e7dSPeter Dunlap  * reception and transmission.  The sonode still exists but its state
206a6d42e7dSPeter Dunlap  * gets modified to indicate it is no longer connected.  Calls to
207a6d42e7dSPeter Dunlap  * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used
208a6d42e7dSPeter Dunlap  * regain control of a thread stuck in idm_sorecv.
209a6d42e7dSPeter Dunlap  */
210a6d42e7dSPeter Dunlap void
2110f1702c5SYu Xiangning idm_soshutdown(ksocket_t so)
212a6d42e7dSPeter Dunlap {
2130f1702c5SYu Xiangning 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
214a6d42e7dSPeter Dunlap }
215a6d42e7dSPeter Dunlap 
216a6d42e7dSPeter Dunlap /*
217a6d42e7dSPeter Dunlap  * idm_sodestroy releases all resources associated with a socket previously
218a6d42e7dSPeter Dunlap  * created with idm_socreate.  The socket must be shutdown using
219a6d42e7dSPeter Dunlap  * idm_soshutdown before the socket is destroyed with idm_sodestroy,
220a6d42e7dSPeter Dunlap  * otherwise undefined behavior will result.
221a6d42e7dSPeter Dunlap  */
222a6d42e7dSPeter Dunlap void
2230f1702c5SYu Xiangning idm_sodestroy(ksocket_t ks)
224a6d42e7dSPeter Dunlap {
2250f1702c5SYu Xiangning 	(void) ksocket_close(ks, CRED());
226a6d42e7dSPeter Dunlap }
227a6d42e7dSPeter Dunlap 
228a6d42e7dSPeter Dunlap /*
229a6d42e7dSPeter Dunlap  * IP address filter functions to flag addresses that should not
230a6d42e7dSPeter Dunlap  * go out to initiators through discovery.
231a6d42e7dSPeter Dunlap  */
232a6d42e7dSPeter Dunlap static boolean_t
233a6d42e7dSPeter Dunlap idm_v4_addr_okay(struct in_addr *in_addr)
234a6d42e7dSPeter Dunlap {
235a6d42e7dSPeter Dunlap 	in_addr_t addr = ntohl(in_addr->s_addr);
236a6d42e7dSPeter Dunlap 
237a6d42e7dSPeter Dunlap 	if ((INADDR_NONE == addr) ||
238a6d42e7dSPeter Dunlap 	    (IN_MULTICAST(addr)) ||
239a6d42e7dSPeter Dunlap 	    ((addr >> IN_CLASSA_NSHIFT) == 0) ||
240a6d42e7dSPeter Dunlap 	    ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
241a6d42e7dSPeter Dunlap 		return (B_FALSE);
242a6d42e7dSPeter Dunlap 	}
243a6d42e7dSPeter Dunlap 	return (B_TRUE);
244a6d42e7dSPeter Dunlap }
245a6d42e7dSPeter Dunlap 
246a6d42e7dSPeter Dunlap static boolean_t
247a6d42e7dSPeter Dunlap idm_v6_addr_okay(struct in6_addr *addr6)
248a6d42e7dSPeter Dunlap {
249a6d42e7dSPeter Dunlap 
250a6d42e7dSPeter Dunlap 	if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) ||
251a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_LOOPBACK(addr6)) ||
252a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_MULTICAST(addr6)) ||
253a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_V4MAPPED(addr6)) ||
254a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_V4COMPAT(addr6)) ||
255a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_LINKLOCAL(addr6))) {
256a6d42e7dSPeter Dunlap 		return (B_FALSE);
257a6d42e7dSPeter Dunlap 	}
258a6d42e7dSPeter Dunlap 	return (B_TRUE);
259a6d42e7dSPeter Dunlap }
260a6d42e7dSPeter Dunlap 
261a6d42e7dSPeter Dunlap /*
262a6d42e7dSPeter Dunlap  * idm_get_ipaddr will retrieve a list of IP Addresses which the host is
263a6d42e7dSPeter Dunlap  * configured with by sending down a sequence of kernel ioctl to IP STREAMS.
264a6d42e7dSPeter Dunlap  */
265a6d42e7dSPeter Dunlap int
266a6d42e7dSPeter Dunlap idm_get_ipaddr(idm_addr_list_t **ipaddr_p)
267a6d42e7dSPeter Dunlap {
2680f1702c5SYu Xiangning 	ksocket_t 		so4, so6;
269a6d42e7dSPeter Dunlap 	struct lifnum		lifn;
270a6d42e7dSPeter Dunlap 	struct lifconf		lifc;
271a6d42e7dSPeter Dunlap 	struct lifreq		*lp;
272a6d42e7dSPeter Dunlap 	int			rval;
273a6d42e7dSPeter Dunlap 	int			numifs;
274a6d42e7dSPeter Dunlap 	int			bufsize;
275a6d42e7dSPeter Dunlap 	void			*buf;
276a6d42e7dSPeter Dunlap 	int			i, j, n, rc;
277a6d42e7dSPeter Dunlap 	struct sockaddr_storage	ss;
278a6d42e7dSPeter Dunlap 	struct sockaddr_in	*sin;
279a6d42e7dSPeter Dunlap 	struct sockaddr_in6	*sin6;
280a6d42e7dSPeter Dunlap 	idm_addr_t		*ip;
281a6d42e7dSPeter Dunlap 	idm_addr_list_t		*ipaddr;
282a6d42e7dSPeter Dunlap 	int			size_ipaddr;
283a6d42e7dSPeter Dunlap 
284a6d42e7dSPeter Dunlap 	*ipaddr_p = NULL;
285a6d42e7dSPeter Dunlap 	size_ipaddr = 0;
286a6d42e7dSPeter Dunlap 	buf = NULL;
287a6d42e7dSPeter Dunlap 
288a6d42e7dSPeter Dunlap 	/* create an ipv4 and ipv6 UDP socket */
289a6d42e7dSPeter Dunlap 	if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL)
290a6d42e7dSPeter Dunlap 		return (0);
291a6d42e7dSPeter Dunlap 	if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) {
292a6d42e7dSPeter Dunlap 		idm_sodestroy(so6);
293a6d42e7dSPeter Dunlap 		return (0);
294a6d42e7dSPeter Dunlap 	}
295a6d42e7dSPeter Dunlap 
296a6d42e7dSPeter Dunlap 
297a6d42e7dSPeter Dunlap retry_count:
298a6d42e7dSPeter Dunlap 	/* snapshot the current number of interfaces */
299a6d42e7dSPeter Dunlap 	lifn.lifn_family = PF_UNSPEC;
300a6d42e7dSPeter Dunlap 	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
301a6d42e7dSPeter Dunlap 	lifn.lifn_count = 0;
3020f1702c5SYu Xiangning 	/* use vp6 for ioctls with unspecified families by default */
3030f1702c5SYu Xiangning 	if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED())
3040f1702c5SYu Xiangning 	    != 0) {
305a6d42e7dSPeter Dunlap 		goto cleanup;
306a6d42e7dSPeter Dunlap 	}
307a6d42e7dSPeter Dunlap 
308a6d42e7dSPeter Dunlap 	numifs = lifn.lifn_count;
309a6d42e7dSPeter Dunlap 	if (numifs <= 0) {
310a6d42e7dSPeter Dunlap 		goto cleanup;
311a6d42e7dSPeter Dunlap 	}
312a6d42e7dSPeter Dunlap 
313a6d42e7dSPeter Dunlap 	/* allocate extra room in case more interfaces appear */
314a6d42e7dSPeter Dunlap 	numifs += 10;
315a6d42e7dSPeter Dunlap 
316a6d42e7dSPeter Dunlap 	/* get the interface names and ip addresses */
317a6d42e7dSPeter Dunlap 	bufsize = numifs * sizeof (struct lifreq);
318a6d42e7dSPeter Dunlap 	buf = kmem_alloc(bufsize, KM_SLEEP);
319a6d42e7dSPeter Dunlap 
320a6d42e7dSPeter Dunlap 	lifc.lifc_family = AF_UNSPEC;
321a6d42e7dSPeter Dunlap 	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
322a6d42e7dSPeter Dunlap 	lifc.lifc_len = bufsize;
323a6d42e7dSPeter Dunlap 	lifc.lifc_buf = buf;
3240f1702c5SYu Xiangning 	rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED());
325a6d42e7dSPeter Dunlap 	if (rc != 0) {
326a6d42e7dSPeter Dunlap 		goto cleanup;
327a6d42e7dSPeter Dunlap 	}
328a6d42e7dSPeter Dunlap 	/* if our extra room is used up, try again */
329a6d42e7dSPeter Dunlap 	if (bufsize <= lifc.lifc_len) {
330a6d42e7dSPeter Dunlap 		kmem_free(buf, bufsize);
331a6d42e7dSPeter Dunlap 		buf = NULL;
332a6d42e7dSPeter Dunlap 		goto retry_count;
333a6d42e7dSPeter Dunlap 	}
334a6d42e7dSPeter Dunlap 	/* calc actual number of ifconfs */
335a6d42e7dSPeter Dunlap 	n = lifc.lifc_len / sizeof (struct lifreq);
336a6d42e7dSPeter Dunlap 
337a6d42e7dSPeter Dunlap 	/* get ip address */
338a6d42e7dSPeter Dunlap 	if (n > 0) {
339a6d42e7dSPeter Dunlap 		size_ipaddr = sizeof (idm_addr_list_t) +
340a6d42e7dSPeter Dunlap 		    (n - 1) * sizeof (idm_addr_t);
341a6d42e7dSPeter Dunlap 		ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP);
342a6d42e7dSPeter Dunlap 	} else {
343a6d42e7dSPeter Dunlap 		goto cleanup;
344a6d42e7dSPeter Dunlap 	}
345a6d42e7dSPeter Dunlap 
346a6d42e7dSPeter Dunlap 	/*
347a6d42e7dSPeter Dunlap 	 * Examine the array of interfaces and filter uninteresting ones
348a6d42e7dSPeter Dunlap 	 */
349a6d42e7dSPeter Dunlap 	for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) {
350a6d42e7dSPeter Dunlap 
351a6d42e7dSPeter Dunlap 		/*
352a6d42e7dSPeter Dunlap 		 * Copy the address as the SIOCGLIFFLAGS ioctl is destructive
353a6d42e7dSPeter Dunlap 		 */
354a6d42e7dSPeter Dunlap 		ss = lp->lifr_addr;
355a6d42e7dSPeter Dunlap 		/*
356a6d42e7dSPeter Dunlap 		 * fetch the flags using the socket of the correct family
357a6d42e7dSPeter Dunlap 		 */
358a6d42e7dSPeter Dunlap 		switch (ss.ss_family) {
359a6d42e7dSPeter Dunlap 		case AF_INET:
3600f1702c5SYu Xiangning 			rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp,
3610f1702c5SYu Xiangning 			    &rval, CRED());
362a6d42e7dSPeter Dunlap 			break;
363a6d42e7dSPeter Dunlap 		case AF_INET6:
3640f1702c5SYu Xiangning 			rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp,
3650f1702c5SYu Xiangning 			    &rval, CRED());
366a6d42e7dSPeter Dunlap 			break;
367a6d42e7dSPeter Dunlap 		default:
368a6d42e7dSPeter Dunlap 			continue;
369a6d42e7dSPeter Dunlap 		}
370a6d42e7dSPeter Dunlap 		if (rc == 0) {
371a6d42e7dSPeter Dunlap 			/*
372a6d42e7dSPeter Dunlap 			 * If we got the flags, skip uninteresting
373a6d42e7dSPeter Dunlap 			 * interfaces based on flags
374a6d42e7dSPeter Dunlap 			 */
375a6d42e7dSPeter Dunlap 			if ((lp->lifr_flags & IFF_UP) != IFF_UP)
376a6d42e7dSPeter Dunlap 				continue;
377a6d42e7dSPeter Dunlap 			if (lp->lifr_flags &
378a6d42e7dSPeter Dunlap 			    (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED))
379a6d42e7dSPeter Dunlap 				continue;
380a6d42e7dSPeter Dunlap 		}
381a6d42e7dSPeter Dunlap 
382a6d42e7dSPeter Dunlap 		/* save ip address */
383a6d42e7dSPeter Dunlap 		ip = &ipaddr->al_addrs[j];
384a6d42e7dSPeter Dunlap 		switch (ss.ss_family) {
385a6d42e7dSPeter Dunlap 		case AF_INET:
386a6d42e7dSPeter Dunlap 			sin = (struct sockaddr_in *)&ss;
387a6d42e7dSPeter Dunlap 			if (!idm_v4_addr_okay(&sin->sin_addr))
388a6d42e7dSPeter Dunlap 				continue;
389a6d42e7dSPeter Dunlap 			ip->a_addr.i_addr.in4 = sin->sin_addr;
390a6d42e7dSPeter Dunlap 			ip->a_addr.i_insize = sizeof (struct in_addr);
391a6d42e7dSPeter Dunlap 			break;
392a6d42e7dSPeter Dunlap 		case AF_INET6:
393a6d42e7dSPeter Dunlap 			sin6 = (struct sockaddr_in6 *)&ss;
394a6d42e7dSPeter Dunlap 			if (!idm_v6_addr_okay(&sin6->sin6_addr))
395a6d42e7dSPeter Dunlap 				continue;
396a6d42e7dSPeter Dunlap 			ip->a_addr.i_addr.in6 = sin6->sin6_addr;
397a6d42e7dSPeter Dunlap 			ip->a_addr.i_insize = sizeof (struct in6_addr);
398a6d42e7dSPeter Dunlap 			break;
399a6d42e7dSPeter Dunlap 		default:
400a6d42e7dSPeter Dunlap 			continue;
401a6d42e7dSPeter Dunlap 		}
402a6d42e7dSPeter Dunlap 		j++;
403a6d42e7dSPeter Dunlap 	}
404a6d42e7dSPeter Dunlap 
405a6d42e7dSPeter Dunlap 	if (j == 0) {
406a6d42e7dSPeter Dunlap 		/* no valid ifaddr */
407a6d42e7dSPeter Dunlap 		kmem_free(ipaddr, size_ipaddr);
408a6d42e7dSPeter Dunlap 		size_ipaddr = 0;
409a6d42e7dSPeter Dunlap 		ipaddr = NULL;
410a6d42e7dSPeter Dunlap 	} else {
411a6d42e7dSPeter Dunlap 		ipaddr->al_out_cnt = j;
412a6d42e7dSPeter Dunlap 	}
413a6d42e7dSPeter Dunlap 
414a6d42e7dSPeter Dunlap 
415a6d42e7dSPeter Dunlap cleanup:
416a6d42e7dSPeter Dunlap 	idm_sodestroy(so6);
417a6d42e7dSPeter Dunlap 	idm_sodestroy(so4);
418a6d42e7dSPeter Dunlap 
419a6d42e7dSPeter Dunlap 	if (buf != NULL)
420a6d42e7dSPeter Dunlap 		kmem_free(buf, bufsize);
421a6d42e7dSPeter Dunlap 
422a6d42e7dSPeter Dunlap 	*ipaddr_p = ipaddr;
423a6d42e7dSPeter Dunlap 	return (size_ipaddr);
424a6d42e7dSPeter Dunlap }
425a6d42e7dSPeter Dunlap 
426a6d42e7dSPeter Dunlap int
4270f1702c5SYu Xiangning idm_sorecv(ksocket_t so, void *msg, size_t len)
428a6d42e7dSPeter Dunlap {
429a6d42e7dSPeter Dunlap 	iovec_t iov;
430a6d42e7dSPeter Dunlap 
431a6d42e7dSPeter Dunlap 	ASSERT(so != NULL);
432a6d42e7dSPeter Dunlap 	ASSERT(len != 0);
433a6d42e7dSPeter Dunlap 
434a6d42e7dSPeter Dunlap 	/*
435a6d42e7dSPeter Dunlap 	 * Fill in iovec and receive data
436a6d42e7dSPeter Dunlap 	 */
437a6d42e7dSPeter Dunlap 	iov.iov_base = msg;
438a6d42e7dSPeter Dunlap 	iov.iov_len = len;
439a6d42e7dSPeter Dunlap 
440a6d42e7dSPeter Dunlap 	return (idm_iov_sorecv(so, &iov, 1, len));
441a6d42e7dSPeter Dunlap }
442a6d42e7dSPeter Dunlap 
443a6d42e7dSPeter Dunlap /*
444a6d42e7dSPeter Dunlap  * idm_sosendto - Sends a buffered data on a non-connected socket.
445a6d42e7dSPeter Dunlap  *
446a6d42e7dSPeter Dunlap  * This function puts the data provided on the wire by calling sosendmsg.
447a6d42e7dSPeter Dunlap  * It will return only when all the data has been sent or if an error
448a6d42e7dSPeter Dunlap  * occurs.
449a6d42e7dSPeter Dunlap  *
450a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sosendmsg fails, and
451a6d42e7dSPeter Dunlap  * -1 if sosendmsg returns success but uio_resid != 0
452a6d42e7dSPeter Dunlap  */
453a6d42e7dSPeter Dunlap int
4540f1702c5SYu Xiangning idm_sosendto(ksocket_t so, void *buff, size_t len,
455a6d42e7dSPeter Dunlap     struct sockaddr *name, socklen_t namelen)
456a6d42e7dSPeter Dunlap {
457a6d42e7dSPeter Dunlap 	struct msghdr		msg;
458a6d42e7dSPeter Dunlap 	struct iovec		iov[1];
459a6d42e7dSPeter Dunlap 	int			error;
4600f1702c5SYu Xiangning 	size_t			sent = 0;
461a6d42e7dSPeter Dunlap 
462a6d42e7dSPeter Dunlap 	iov[0].iov_base	= buff;
463a6d42e7dSPeter Dunlap 	iov[0].iov_len	= len;
464a6d42e7dSPeter Dunlap 
465a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
466a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
467a6d42e7dSPeter Dunlap 	msg.msg_iov	= iov;
468a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= 1;
469a6d42e7dSPeter Dunlap 	msg.msg_name	= name;
470a6d42e7dSPeter Dunlap 	msg.msg_namelen	= namelen;
471a6d42e7dSPeter Dunlap 
4720f1702c5SYu Xiangning 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) {
473a6d42e7dSPeter Dunlap 		/* Data sent */
4740f1702c5SYu Xiangning 		if (sent == len) {
475a6d42e7dSPeter Dunlap 			/* All data sent.  Success. */
476a6d42e7dSPeter Dunlap 			return (0);
477a6d42e7dSPeter Dunlap 		} else {
478a6d42e7dSPeter Dunlap 			/* Not all data was sent.  Failure */
479a6d42e7dSPeter Dunlap 			return (-1);
480a6d42e7dSPeter Dunlap 		}
481a6d42e7dSPeter Dunlap 	}
482a6d42e7dSPeter Dunlap 
483a6d42e7dSPeter Dunlap 	/* Send failed */
484a6d42e7dSPeter Dunlap 	return (error);
485a6d42e7dSPeter Dunlap }
486a6d42e7dSPeter Dunlap 
487a6d42e7dSPeter Dunlap /*
488a6d42e7dSPeter Dunlap  * idm_iov_sosend - Sends an iovec on a connection.
489a6d42e7dSPeter Dunlap  *
490a6d42e7dSPeter Dunlap  * This function puts the data provided on the wire by calling sosendmsg.
491a6d42e7dSPeter Dunlap  * It will return only when all the data has been sent or if an error
492a6d42e7dSPeter Dunlap  * occurs.
493a6d42e7dSPeter Dunlap  *
494a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sosendmsg fails, and
495a6d42e7dSPeter Dunlap  * -1 if sosendmsg returns success but uio_resid != 0
496a6d42e7dSPeter Dunlap  */
497a6d42e7dSPeter Dunlap int
4980f1702c5SYu Xiangning idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
499a6d42e7dSPeter Dunlap {
500a6d42e7dSPeter Dunlap 	struct msghdr		msg;
501a6d42e7dSPeter Dunlap 	int			error;
5020f1702c5SYu Xiangning 	size_t 			sent = 0;
503a6d42e7dSPeter Dunlap 
504a6d42e7dSPeter Dunlap 	ASSERT(iop != NULL);
505a6d42e7dSPeter Dunlap 
506a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
507a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
508a6d42e7dSPeter Dunlap 	msg.msg_iov	= iop;
509a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= iovlen;
510a6d42e7dSPeter Dunlap 
5110f1702c5SYu Xiangning 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED()))
5120f1702c5SYu Xiangning 	    == 0) {
513a6d42e7dSPeter Dunlap 		/* Data sent */
5140f1702c5SYu Xiangning 		if (sent == total_len) {
515a6d42e7dSPeter Dunlap 			/* All data sent.  Success. */
516a6d42e7dSPeter Dunlap 			return (0);
517a6d42e7dSPeter Dunlap 		} else {
518a6d42e7dSPeter Dunlap 			/* Not all data was sent.  Failure */
519a6d42e7dSPeter Dunlap 			return (-1);
520a6d42e7dSPeter Dunlap 		}
521a6d42e7dSPeter Dunlap 	}
522a6d42e7dSPeter Dunlap 
523a6d42e7dSPeter Dunlap 	/* Send failed */
524a6d42e7dSPeter Dunlap 	return (error);
525a6d42e7dSPeter Dunlap }
526a6d42e7dSPeter Dunlap 
527a6d42e7dSPeter Dunlap /*
528a6d42e7dSPeter Dunlap  * idm_iov_sorecv - Receives an iovec from a connection
529a6d42e7dSPeter Dunlap  *
530a6d42e7dSPeter Dunlap  * This function gets the data asked for from the socket.  It will return
531a6d42e7dSPeter Dunlap  * only when all the requested data has been retrieved or if an error
532a6d42e7dSPeter Dunlap  * occurs.
533a6d42e7dSPeter Dunlap  *
534a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
535a6d42e7dSPeter Dunlap  * -1 if sorecvmsg returns success but uio_resid != 0
536a6d42e7dSPeter Dunlap  */
537a6d42e7dSPeter Dunlap int
5380f1702c5SYu Xiangning idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
539a6d42e7dSPeter Dunlap {
540a6d42e7dSPeter Dunlap 	struct msghdr		msg;
541a6d42e7dSPeter Dunlap 	int			error;
5420f1702c5SYu Xiangning 	size_t			recv;
5430f1702c5SYu Xiangning 	int 			flags;
544a6d42e7dSPeter Dunlap 
545a6d42e7dSPeter Dunlap 	ASSERT(iop != NULL);
546a6d42e7dSPeter Dunlap 
547a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
548a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
549a6d42e7dSPeter Dunlap 	msg.msg_iov	= iop;
550a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= iovlen;
5510f1702c5SYu Xiangning 	flags		= MSG_WAITALL;
552a6d42e7dSPeter Dunlap 
5530f1702c5SYu Xiangning 	if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED()))
5540f1702c5SYu Xiangning 	    == 0) {
555a6d42e7dSPeter Dunlap 		/* Received data */
5560f1702c5SYu Xiangning 		if (recv == total_len) {
557a6d42e7dSPeter Dunlap 			/* All requested data received.  Success */
558a6d42e7dSPeter Dunlap 			return (0);
559a6d42e7dSPeter Dunlap 		} else {
560a6d42e7dSPeter Dunlap 			/*
561a6d42e7dSPeter Dunlap 			 * Not all data was received.  The connection has
562a6d42e7dSPeter Dunlap 			 * probably failed.
563a6d42e7dSPeter Dunlap 			 */
564a6d42e7dSPeter Dunlap 			return (-1);
565a6d42e7dSPeter Dunlap 		}
566a6d42e7dSPeter Dunlap 	}
567a6d42e7dSPeter Dunlap 
568a6d42e7dSPeter Dunlap 	/* Receive failed */
569a6d42e7dSPeter Dunlap 	return (error);
570a6d42e7dSPeter Dunlap }
571a6d42e7dSPeter Dunlap 
572a6d42e7dSPeter Dunlap static void
573a6d42e7dSPeter Dunlap idm_set_ini_preconnect_options(idm_so_conn_t *sc)
574a6d42e7dSPeter Dunlap {
575a6d42e7dSPeter Dunlap 	int	conn_abort = 10000;
576a6d42e7dSPeter Dunlap 	int	conn_notify = 2000;
577a6d42e7dSPeter Dunlap 	int	abort = 30000;
578a6d42e7dSPeter Dunlap 
579a6d42e7dSPeter Dunlap 	/* Pre-connect socket options */
5800f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5810f1702c5SYu Xiangning 	    TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int),
5820f1702c5SYu Xiangning 	    CRED());
5830f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5840f1702c5SYu Xiangning 	    TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int),
5850f1702c5SYu Xiangning 	    CRED());
5860f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
5870f1702c5SYu Xiangning 	    (char *)&abort, sizeof (int), CRED());
588a6d42e7dSPeter Dunlap }
589a6d42e7dSPeter Dunlap 
590a6d42e7dSPeter Dunlap static void
591a6d42e7dSPeter Dunlap idm_set_ini_postconnect_options(idm_so_conn_t *sc)
592a6d42e7dSPeter Dunlap {
593a6d42e7dSPeter Dunlap 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
594a6d42e7dSPeter Dunlap 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
595a6d42e7dSPeter Dunlap 	const int	on = 1;
596a6d42e7dSPeter Dunlap 
597a6d42e7dSPeter Dunlap 	/* Set postconnect options */
5980f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY,
5990f1702c5SYu Xiangning 	    (char *)&on, sizeof (int), CRED());
6000f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF,
6010f1702c5SYu Xiangning 	    (char *)&rcvbuf, sizeof (int), CRED());
6020f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF,
6030f1702c5SYu Xiangning 	    (char *)&sndbuf, sizeof (int), CRED());
604a6d42e7dSPeter Dunlap }
605a6d42e7dSPeter Dunlap 
606a6d42e7dSPeter Dunlap static void
6070f1702c5SYu Xiangning idm_set_tgt_connect_options(ksocket_t ks)
608a6d42e7dSPeter Dunlap {
609a6d42e7dSPeter Dunlap 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
610a6d42e7dSPeter Dunlap 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
611a6d42e7dSPeter Dunlap 	const int	on = 1;
612a6d42e7dSPeter Dunlap 
613a6d42e7dSPeter Dunlap 	/* Set connect options */
6140f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF,
6150f1702c5SYu Xiangning 	    (char *)&rcvbuf, sizeof (int), CRED());
6160f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF,
6170f1702c5SYu Xiangning 	    (char *)&sndbuf, sizeof (int), CRED());
6180f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY,
6190f1702c5SYu Xiangning 	    (char *)&on, sizeof (on), CRED());
620a6d42e7dSPeter Dunlap }
621a6d42e7dSPeter Dunlap 
622a6d42e7dSPeter Dunlap static uint32_t
623a6d42e7dSPeter Dunlap n2h24(const uchar_t *ptr)
624a6d42e7dSPeter Dunlap {
625a6d42e7dSPeter Dunlap 	return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
626a6d42e7dSPeter Dunlap }
627a6d42e7dSPeter Dunlap 
628a6d42e7dSPeter Dunlap 
629a6d42e7dSPeter Dunlap static idm_status_t
630a6d42e7dSPeter Dunlap idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu)
631a6d42e7dSPeter Dunlap {
632a6d42e7dSPeter Dunlap 	iscsi_hdr_t	*bhs;
633a6d42e7dSPeter Dunlap 	uint32_t	hdr_digest_crc;
634a6d42e7dSPeter Dunlap 	uint32_t	crc_calculated;
635a6d42e7dSPeter Dunlap 	void		*new_hdr;
636a6d42e7dSPeter Dunlap 	int		ahslen = 0;
637a6d42e7dSPeter Dunlap 	int		total_len = 0;
638a6d42e7dSPeter Dunlap 	int		iovlen = 0;
639a6d42e7dSPeter Dunlap 	struct iovec	iov[2];
640a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
641a6d42e7dSPeter Dunlap 	int		rc;
642a6d42e7dSPeter Dunlap 
643a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
644a6d42e7dSPeter Dunlap 
645a6d42e7dSPeter Dunlap 	/*
646a6d42e7dSPeter Dunlap 	 * Read BHS
647a6d42e7dSPeter Dunlap 	 */
648a6d42e7dSPeter Dunlap 	bhs = pdu->isp_hdr;
649a6d42e7dSPeter Dunlap 	rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t));
650a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
651a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
652a6d42e7dSPeter Dunlap 	}
653a6d42e7dSPeter Dunlap 
654a6d42e7dSPeter Dunlap 	/*
655a6d42e7dSPeter Dunlap 	 * Check actual AHS length against the amount available in the buffer
656a6d42e7dSPeter Dunlap 	 */
657a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
658a6d42e7dSPeter Dunlap 	    (bhs->hlength * sizeof (uint32_t));
659a6d42e7dSPeter Dunlap 	pdu->isp_datalen = n2h24(bhs->dlength);
660a6d42e7dSPeter Dunlap 	if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) {
661a6d42e7dSPeter Dunlap 		/* Allocate a new header segment and change the callback */
662a6d42e7dSPeter Dunlap 		new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP);
663a6d42e7dSPeter Dunlap 		bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t));
664a6d42e7dSPeter Dunlap 		pdu->isp_hdr = new_hdr;
665a6d42e7dSPeter Dunlap 		pdu->isp_flags |= IDM_PDU_ADDL_HDR;
666a6d42e7dSPeter Dunlap 
667a6d42e7dSPeter Dunlap 		/*
668a6d42e7dSPeter Dunlap 		 * This callback will restore the expected values after
669a6d42e7dSPeter Dunlap 		 * the RX PDU has been processed.
670a6d42e7dSPeter Dunlap 		 */
671a6d42e7dSPeter Dunlap 		pdu->isp_callback = idm_sorx_addl_pdu_cb;
672a6d42e7dSPeter Dunlap 	}
673a6d42e7dSPeter Dunlap 
674a6d42e7dSPeter Dunlap 	/*
675a6d42e7dSPeter Dunlap 	 * Setup receipt of additional header and header digest (if enabled).
676a6d42e7dSPeter Dunlap 	 */
677a6d42e7dSPeter Dunlap 	if (bhs->hlength > 0) {
678a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1);
679a6d42e7dSPeter Dunlap 		ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t);
680a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len = ahslen;
681a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
682a6d42e7dSPeter Dunlap 		iovlen++;
683a6d42e7dSPeter Dunlap 	}
684a6d42e7dSPeter Dunlap 
685a6d42e7dSPeter Dunlap 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
686a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc;
687a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len = sizeof (hdr_digest_crc);
688a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
689a6d42e7dSPeter Dunlap 		iovlen++;
690a6d42e7dSPeter Dunlap 	}
691a6d42e7dSPeter Dunlap 
692a6d42e7dSPeter Dunlap 	if ((iovlen != 0) &&
693a6d42e7dSPeter Dunlap 	    (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen,
694a6d42e7dSPeter Dunlap 	    total_len) != 0)) {
695a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
696a6d42e7dSPeter Dunlap 	}
697a6d42e7dSPeter Dunlap 
698a6d42e7dSPeter Dunlap 	/*
699a6d42e7dSPeter Dunlap 	 * Validate header digest if enabled
700a6d42e7dSPeter Dunlap 	 */
701a6d42e7dSPeter Dunlap 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
702a6d42e7dSPeter Dunlap 		crc_calculated = idm_crc32c(pdu->isp_hdr,
703a6d42e7dSPeter Dunlap 		    sizeof (iscsi_hdr_t) + ahslen);
704a6d42e7dSPeter Dunlap 		if (crc_calculated != hdr_digest_crc) {
705a6d42e7dSPeter Dunlap 			/* Invalid Header Digest */
706a6d42e7dSPeter Dunlap 			return (IDM_STATUS_HEADER_DIGEST);
707a6d42e7dSPeter Dunlap 		}
708a6d42e7dSPeter Dunlap 	}
709a6d42e7dSPeter Dunlap 
710a6d42e7dSPeter Dunlap 	return (0);
711a6d42e7dSPeter Dunlap }
712a6d42e7dSPeter Dunlap 
713a6d42e7dSPeter Dunlap /*
714a6d42e7dSPeter Dunlap  * idm_so_ini_conn_create()
715a6d42e7dSPeter Dunlap  * Allocate the sockets transport connection resources.
716a6d42e7dSPeter Dunlap  */
717a6d42e7dSPeter Dunlap static idm_status_t
718a6d42e7dSPeter Dunlap idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic)
719a6d42e7dSPeter Dunlap {
7200f1702c5SYu Xiangning 	ksocket_t	so;
721a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
722a6d42e7dSPeter Dunlap 	idm_status_t	idmrc;
723a6d42e7dSPeter Dunlap 
724a6d42e7dSPeter Dunlap 	so = idm_socreate(cr->cr_domain, cr->cr_type,
725a6d42e7dSPeter Dunlap 	    cr->cr_protocol);
726a6d42e7dSPeter Dunlap 	if (so == NULL) {
727a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
728a6d42e7dSPeter Dunlap 	}
729a6d42e7dSPeter Dunlap 
730a6d42e7dSPeter Dunlap 	/* Bind the socket if configured to do so */
731a6d42e7dSPeter Dunlap 	if (cr->cr_bound) {
7320f1702c5SYu Xiangning 		if (ksocket_bind(so, &cr->cr_bound_addr.sin,
7330f1702c5SYu Xiangning 		    SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) {
734a6d42e7dSPeter Dunlap 			idm_sodestroy(so);
735a6d42e7dSPeter Dunlap 			return (IDM_STATUS_FAIL);
736a6d42e7dSPeter Dunlap 		}
737a6d42e7dSPeter Dunlap 	}
738a6d42e7dSPeter Dunlap 
739a6d42e7dSPeter Dunlap 	idmrc = idm_so_conn_create_common(ic, so);
740a6d42e7dSPeter Dunlap 	if (idmrc != IDM_STATUS_SUCCESS) {
741a6d42e7dSPeter Dunlap 		idm_soshutdown(so);
742a6d42e7dSPeter Dunlap 		idm_sodestroy(so);
743a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
744a6d42e7dSPeter Dunlap 	}
745a6d42e7dSPeter Dunlap 
746a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
747a6d42e7dSPeter Dunlap 	/* Set up socket options */
748a6d42e7dSPeter Dunlap 	idm_set_ini_preconnect_options(so_conn);
749a6d42e7dSPeter Dunlap 
750a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
751a6d42e7dSPeter Dunlap }
752a6d42e7dSPeter Dunlap 
753a6d42e7dSPeter Dunlap /*
754a6d42e7dSPeter Dunlap  * idm_so_ini_conn_destroy()
755a6d42e7dSPeter Dunlap  * Tear down the sockets transport connection resources.
756a6d42e7dSPeter Dunlap  */
757a6d42e7dSPeter Dunlap static void
758a6d42e7dSPeter Dunlap idm_so_ini_conn_destroy(idm_conn_t *ic)
759a6d42e7dSPeter Dunlap {
760a6d42e7dSPeter Dunlap 	idm_so_conn_destroy_common(ic);
761a6d42e7dSPeter Dunlap }
762a6d42e7dSPeter Dunlap 
763a6d42e7dSPeter Dunlap /*
764a6d42e7dSPeter Dunlap  * idm_so_ini_conn_connect()
765a6d42e7dSPeter Dunlap  * Establish the connection referred to by the handle previously allocated via
766a6d42e7dSPeter Dunlap  * idm_so_ini_conn_create().
767a6d42e7dSPeter Dunlap  */
768a6d42e7dSPeter Dunlap static idm_status_t
769a6d42e7dSPeter Dunlap idm_so_ini_conn_connect(idm_conn_t *ic)
770a6d42e7dSPeter Dunlap {
771a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
772a6d42e7dSPeter Dunlap 
773a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
774a6d42e7dSPeter Dunlap 
7750f1702c5SYu Xiangning 	if (ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin,
7760f1702c5SYu Xiangning 	    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED()) != 0) {
777a6d42e7dSPeter Dunlap 		idm_soshutdown(so_conn->ic_so);
778a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
779a6d42e7dSPeter Dunlap 	}
780a6d42e7dSPeter Dunlap 
781a6d42e7dSPeter Dunlap 	idm_so_conn_connect_common(ic);
782a6d42e7dSPeter Dunlap 
783a6d42e7dSPeter Dunlap 	idm_set_ini_postconnect_options(so_conn);
784a6d42e7dSPeter Dunlap 
785a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
786a6d42e7dSPeter Dunlap }
787a6d42e7dSPeter Dunlap 
788a6d42e7dSPeter Dunlap idm_status_t
7890f1702c5SYu Xiangning idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so)
790a6d42e7dSPeter Dunlap {
791a6d42e7dSPeter Dunlap 	idm_status_t	idmrc;
792a6d42e7dSPeter Dunlap 
793a6d42e7dSPeter Dunlap 	idmrc = idm_so_conn_create_common(ic, new_so);
794a6d42e7dSPeter Dunlap 
795a6d42e7dSPeter Dunlap 	return (idmrc);
796a6d42e7dSPeter Dunlap }
797a6d42e7dSPeter Dunlap 
798a6d42e7dSPeter Dunlap static void
799a6d42e7dSPeter Dunlap idm_so_tgt_conn_destroy(idm_conn_t *ic)
800a6d42e7dSPeter Dunlap {
801a6d42e7dSPeter Dunlap 	idm_so_conn_destroy_common(ic);
802a6d42e7dSPeter Dunlap }
803a6d42e7dSPeter Dunlap 
804a6d42e7dSPeter Dunlap /*
805a6d42e7dSPeter Dunlap  * idm_so_tgt_conn_connect()
806a6d42e7dSPeter Dunlap  * Establish the connection in ic, passed from idm_tgt_conn_finish(), which
807a6d42e7dSPeter Dunlap  * is invoked from the SM as a result of an inbound connection request.
808a6d42e7dSPeter Dunlap  */
809a6d42e7dSPeter Dunlap static idm_status_t
810a6d42e7dSPeter Dunlap idm_so_tgt_conn_connect(idm_conn_t *ic)
811a6d42e7dSPeter Dunlap {
812a6d42e7dSPeter Dunlap 	idm_so_conn_connect_common(ic);
813a6d42e7dSPeter Dunlap 
814a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
815a6d42e7dSPeter Dunlap }
816a6d42e7dSPeter Dunlap 
817a6d42e7dSPeter Dunlap static idm_status_t
8180f1702c5SYu Xiangning idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so)
819a6d42e7dSPeter Dunlap {
820a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
821a6d42e7dSPeter Dunlap 
822a6d42e7dSPeter Dunlap 	so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP);
823a6d42e7dSPeter Dunlap 	so_conn->ic_so = new_so;
824a6d42e7dSPeter Dunlap 
825a6d42e7dSPeter Dunlap 	ic->ic_transport_private = so_conn;
826a6d42e7dSPeter Dunlap 	ic->ic_transport_hdrlen = 0;
827a6d42e7dSPeter Dunlap 
828a6d42e7dSPeter Dunlap 	/* Set the scoreboarding flag on this connection */
829a6d42e7dSPeter Dunlap 	ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD;
830a6d42e7dSPeter Dunlap 
831a6d42e7dSPeter Dunlap 	/*
832a6d42e7dSPeter Dunlap 	 * Initialize tx thread mutex and list
833a6d42e7dSPeter Dunlap 	 */
834a6d42e7dSPeter Dunlap 	mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL);
835a6d42e7dSPeter Dunlap 	cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL);
836a6d42e7dSPeter Dunlap 	list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t),
837a6d42e7dSPeter Dunlap 	    offsetof(idm_pdu_t, idm_tx_link));
838a6d42e7dSPeter Dunlap 
839a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
840a6d42e7dSPeter Dunlap }
841a6d42e7dSPeter Dunlap 
842a6d42e7dSPeter Dunlap static void
843a6d42e7dSPeter Dunlap idm_so_conn_destroy_common(idm_conn_t *ic)
844a6d42e7dSPeter Dunlap {
845a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
846a6d42e7dSPeter Dunlap 
847a6d42e7dSPeter Dunlap 	ic->ic_transport_private = NULL;
848a6d42e7dSPeter Dunlap 	idm_sodestroy(so_conn->ic_so);
849a6d42e7dSPeter Dunlap 	list_destroy(&so_conn->ic_tx_list);
850a6d42e7dSPeter Dunlap 	mutex_destroy(&so_conn->ic_tx_mutex);
851a6d42e7dSPeter Dunlap 	cv_destroy(&so_conn->ic_tx_cv);
852a6d42e7dSPeter Dunlap 
853a6d42e7dSPeter Dunlap 	kmem_free(so_conn, sizeof (idm_so_conn_t));
854a6d42e7dSPeter Dunlap }
855a6d42e7dSPeter Dunlap 
856a6d42e7dSPeter Dunlap static void
857a6d42e7dSPeter Dunlap idm_so_conn_connect_common(idm_conn_t *ic)
858a6d42e7dSPeter Dunlap {
859a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
8600f1702c5SYu Xiangning 	struct sockaddr_in6	t_addr;
8610f1702c5SYu Xiangning 	socklen_t	t_addrlen = 0;
862a6d42e7dSPeter Dunlap 
863a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
8640f1702c5SYu Xiangning 	bzero(&t_addr, sizeof (struct sockaddr_in6));
8650f1702c5SYu Xiangning 	t_addrlen = sizeof (struct sockaddr_in6);
866a6d42e7dSPeter Dunlap 
867a6d42e7dSPeter Dunlap 	/* Set the local and remote addresses in the idm conn handle */
8680f1702c5SYu Xiangning 	ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr,
8690f1702c5SYu Xiangning 	    &t_addrlen, CRED());
8700f1702c5SYu Xiangning 	bcopy(&t_addr, &ic->ic_laddr, t_addrlen);
8710f1702c5SYu Xiangning 	ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr,
8720f1702c5SYu Xiangning 	    &t_addrlen, CRED());
8730f1702c5SYu Xiangning 	bcopy(&t_addr, &ic->ic_raddr, t_addrlen);
874a6d42e7dSPeter Dunlap 
875a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
876a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0,
877a6d42e7dSPeter Dunlap 	    &p0, TS_RUN, minclsyspri);
878a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0,
879a6d42e7dSPeter Dunlap 	    &p0, TS_RUN, minclsyspri);
880a6d42e7dSPeter Dunlap 
881a6d42e7dSPeter Dunlap 	while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running)
882a6d42e7dSPeter Dunlap 		cv_wait(&ic->ic_cv, &ic->ic_mutex);
883a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
884a6d42e7dSPeter Dunlap }
885a6d42e7dSPeter Dunlap 
886a6d42e7dSPeter Dunlap /*
887a6d42e7dSPeter Dunlap  * idm_so_conn_disconnect()
888a6d42e7dSPeter Dunlap  * Shutdown the socket connection and stop the thread
889a6d42e7dSPeter Dunlap  */
890a6d42e7dSPeter Dunlap static void
891a6d42e7dSPeter Dunlap idm_so_conn_disconnect(idm_conn_t *ic)
892a6d42e7dSPeter Dunlap {
893a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
894a6d42e7dSPeter Dunlap 
895a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
896a6d42e7dSPeter Dunlap 
897a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
898a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_running = B_FALSE;
899a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_running = B_FALSE;
900a6d42e7dSPeter Dunlap 	/* We need to wakeup the TX thread */
901a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
902a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
903a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
904a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
905a6d42e7dSPeter Dunlap 
906a6d42e7dSPeter Dunlap 	/* This should wakeup the RX thread if it is sleeping */
907a6d42e7dSPeter Dunlap 	idm_soshutdown(so_conn->ic_so);
908a6d42e7dSPeter Dunlap 
909a6d42e7dSPeter Dunlap 	thread_join(so_conn->ic_tx_thread_did);
910a6d42e7dSPeter Dunlap 	thread_join(so_conn->ic_rx_thread_did);
911a6d42e7dSPeter Dunlap }
912a6d42e7dSPeter Dunlap 
913a6d42e7dSPeter Dunlap /*
914a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_create()
915a6d42e7dSPeter Dunlap  * Establish a service on an IP address and port.  idm_svc_req_t contains
916a6d42e7dSPeter Dunlap  * the service parameters.
917a6d42e7dSPeter Dunlap  */
918a6d42e7dSPeter Dunlap /*ARGSUSED*/
919a6d42e7dSPeter Dunlap static idm_status_t
920a6d42e7dSPeter Dunlap idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is)
921a6d42e7dSPeter Dunlap {
922a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
923a6d42e7dSPeter Dunlap 
924a6d42e7dSPeter Dunlap 	so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP);
925a6d42e7dSPeter Dunlap 
926a6d42e7dSPeter Dunlap 	/* Set the new sockets service in svc handle */
927a6d42e7dSPeter Dunlap 	is->is_so_svc = (void *)so_svc;
928a6d42e7dSPeter Dunlap 
929a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
930a6d42e7dSPeter Dunlap }
931a6d42e7dSPeter Dunlap 
932a6d42e7dSPeter Dunlap /*
933a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_destroy()
934a6d42e7dSPeter Dunlap  * Teardown sockets resources allocated in idm_so_tgt_svc_create()
935a6d42e7dSPeter Dunlap  */
936a6d42e7dSPeter Dunlap static void
937a6d42e7dSPeter Dunlap idm_so_tgt_svc_destroy(idm_svc_t *is)
938a6d42e7dSPeter Dunlap {
939a6d42e7dSPeter Dunlap 	/* the socket will have been torn down; free the service */
940a6d42e7dSPeter Dunlap 	kmem_free(is->is_so_svc, sizeof (idm_so_svc_t));
941a6d42e7dSPeter Dunlap }
942a6d42e7dSPeter Dunlap 
943a6d42e7dSPeter Dunlap /*
944a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_online()
945a6d42e7dSPeter Dunlap  * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create()
946a6d42e7dSPeter Dunlap  */
947a6d42e7dSPeter Dunlap 
948a6d42e7dSPeter Dunlap static idm_status_t
949a6d42e7dSPeter Dunlap idm_so_tgt_svc_online(idm_svc_t *is)
950a6d42e7dSPeter Dunlap {
951a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
952a6d42e7dSPeter Dunlap 	idm_svc_req_t		*sr = &is->is_svc_req;
953a6d42e7dSPeter Dunlap 	struct sockaddr_in6	sin6_ip;
954a6d42e7dSPeter Dunlap 	const uint32_t		on = 1;
955a6d42e7dSPeter Dunlap 	const uint32_t		off = 0;
956a6d42e7dSPeter Dunlap 
957a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
958a6d42e7dSPeter Dunlap 	so_svc = (idm_so_svc_t *)is->is_so_svc;
959a6d42e7dSPeter Dunlap 
960a6d42e7dSPeter Dunlap 	/*
961a6d42e7dSPeter Dunlap 	 * Try creating an IPv6 socket first
962a6d42e7dSPeter Dunlap 	 */
963a6d42e7dSPeter Dunlap 	if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) {
964a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
965a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
966a6d42e7dSPeter Dunlap 	} else {
967a6d42e7dSPeter Dunlap 		bzero(&sin6_ip, sizeof (sin6_ip));
968a6d42e7dSPeter Dunlap 		sin6_ip.sin6_family = AF_INET6;
969a6d42e7dSPeter Dunlap 		sin6_ip.sin6_port = htons(sr->sr_port);
970a6d42e7dSPeter Dunlap 		sin6_ip.sin6_addr = in6addr_any;
971a6d42e7dSPeter Dunlap 
9720f1702c5SYu Xiangning 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9730f1702c5SYu Xiangning 		    SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
974a6d42e7dSPeter Dunlap 		/*
975a6d42e7dSPeter Dunlap 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
976a6d42e7dSPeter Dunlap 		 */
9770f1702c5SYu Xiangning 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9780f1702c5SYu Xiangning 		    SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED());
979a6d42e7dSPeter Dunlap 
9800f1702c5SYu Xiangning 		if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip,
9810f1702c5SYu Xiangning 		    sizeof (sin6_ip), CRED()) != 0) {
982a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
983a6d42e7dSPeter Dunlap 			idm_sodestroy(so_svc->is_so);
984a6d42e7dSPeter Dunlap 			return (IDM_STATUS_FAIL);
985a6d42e7dSPeter Dunlap 		}
986a6d42e7dSPeter Dunlap 	}
987a6d42e7dSPeter Dunlap 
988a6d42e7dSPeter Dunlap 	idm_set_tgt_connect_options(so_svc->is_so);
989a6d42e7dSPeter Dunlap 
9900f1702c5SYu Xiangning 	if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) {
991a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
992a6d42e7dSPeter Dunlap 		idm_soshutdown(so_svc->is_so);
993a6d42e7dSPeter Dunlap 		idm_sodestroy(so_svc->is_so);
994a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
995a6d42e7dSPeter Dunlap 	}
996a6d42e7dSPeter Dunlap 
997a6d42e7dSPeter Dunlap 	/* Launch a watch thread */
998a6d42e7dSPeter Dunlap 	so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher,
999a6d42e7dSPeter Dunlap 	    is, 0, &p0, TS_RUN, minclsyspri);
1000a6d42e7dSPeter Dunlap 
1001a6d42e7dSPeter Dunlap 	if (so_svc->is_thread == NULL) {
1002a6d42e7dSPeter Dunlap 		/* Failure to launch; teardown the socket */
1003a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
1004a6d42e7dSPeter Dunlap 		idm_soshutdown(so_svc->is_so);
1005a6d42e7dSPeter Dunlap 		idm_sodestroy(so_svc->is_so);
1006a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1007a6d42e7dSPeter Dunlap 	}
10080f1702c5SYu Xiangning 	ksocket_hold(so_svc->is_so);
1009a6d42e7dSPeter Dunlap 	/* Wait for the port watcher thread to start */
1010a6d42e7dSPeter Dunlap 	while (!so_svc->is_thread_running)
1011a6d42e7dSPeter Dunlap 		cv_wait(&is->is_cv, &is->is_mutex);
1012a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
1013a6d42e7dSPeter Dunlap 
1014a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1015a6d42e7dSPeter Dunlap }
1016a6d42e7dSPeter Dunlap 
1017a6d42e7dSPeter Dunlap /*
1018a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_offline
1019a6d42e7dSPeter Dunlap  *
1020a6d42e7dSPeter Dunlap  * Stop listening on the IP address and port identified by idm_svc_t.
1021a6d42e7dSPeter Dunlap  */
1022a6d42e7dSPeter Dunlap static void
1023a6d42e7dSPeter Dunlap idm_so_tgt_svc_offline(idm_svc_t *is)
1024a6d42e7dSPeter Dunlap {
1025a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1026a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
1027a6d42e7dSPeter Dunlap 	so_svc = (idm_so_svc_t *)is->is_so_svc;
1028a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_FALSE;
1029a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
1030a6d42e7dSPeter Dunlap 
1031a6d42e7dSPeter Dunlap 	/*
10320f1702c5SYu Xiangning 	 * Teardown socket
1033a6d42e7dSPeter Dunlap 	 */
10340f1702c5SYu Xiangning 	idm_sodestroy(so_svc->is_so);
1035a6d42e7dSPeter Dunlap 
1036a6d42e7dSPeter Dunlap 	/*
1037a6d42e7dSPeter Dunlap 	 * Now we expect the port watcher thread to terminate
1038a6d42e7dSPeter Dunlap 	 */
1039a6d42e7dSPeter Dunlap 	thread_join(so_svc->is_thread_did);
1040a6d42e7dSPeter Dunlap }
1041a6d42e7dSPeter Dunlap 
1042a6d42e7dSPeter Dunlap /*
1043a6d42e7dSPeter Dunlap  * Watch thread for target service connection establishment.
1044a6d42e7dSPeter Dunlap  */
1045a6d42e7dSPeter Dunlap void
1046a6d42e7dSPeter Dunlap idm_so_svc_port_watcher(void *arg)
1047a6d42e7dSPeter Dunlap {
1048a6d42e7dSPeter Dunlap 	idm_svc_t		*svc = arg;
10490f1702c5SYu Xiangning 	ksocket_t		new_so;
1050a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
1051a6d42e7dSPeter Dunlap 	idm_status_t		idmrc;
1052a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1053a6d42e7dSPeter Dunlap 	int			rc;
1054a6d42e7dSPeter Dunlap 	const uint32_t		off = 0;
10550f1702c5SYu Xiangning 	struct sockaddr_in6 	t_addr;
10560f1702c5SYu Xiangning 	socklen_t		t_addrlen;
1057a6d42e7dSPeter Dunlap 
10580f1702c5SYu Xiangning 	bzero(&t_addr, sizeof (struct sockaddr_in6));
10590f1702c5SYu Xiangning 	t_addrlen = sizeof (struct sockaddr_in6);
1060a6d42e7dSPeter Dunlap 	mutex_enter(&svc->is_mutex);
1061a6d42e7dSPeter Dunlap 
1062a6d42e7dSPeter Dunlap 	so_svc = svc->is_so_svc;
1063a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_TRUE;
1064a6d42e7dSPeter Dunlap 	so_svc->is_thread_did = so_svc->is_thread->t_did;
1065a6d42e7dSPeter Dunlap 
1066a6d42e7dSPeter Dunlap 	cv_signal(&svc->is_cv);
1067a6d42e7dSPeter Dunlap 
1068a6d42e7dSPeter Dunlap 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc,
1069a6d42e7dSPeter Dunlap 	    svc->is_svc_req.sr_port);
1070a6d42e7dSPeter Dunlap 
1071a6d42e7dSPeter Dunlap 	while (so_svc->is_thread_running) {
1072a6d42e7dSPeter Dunlap 		mutex_exit(&svc->is_mutex);
1073a6d42e7dSPeter Dunlap 
10740f1702c5SYu Xiangning 		if ((rc = ksocket_accept(so_svc->is_so,
10750f1702c5SYu Xiangning 		    (struct sockaddr *)&t_addr, &t_addrlen,
10760f1702c5SYu Xiangning 		    &new_so, CRED())) != 0) {
1077a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1078a6d42e7dSPeter Dunlap 			if (rc == ECONNABORTED)
1079a6d42e7dSPeter Dunlap 				continue;
1080a6d42e7dSPeter Dunlap 			/* Connection problem */
1081a6d42e7dSPeter Dunlap 			break;
1082a6d42e7dSPeter Dunlap 		}
1083a6d42e7dSPeter Dunlap 		/*
1084a6d42e7dSPeter Dunlap 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
1085a6d42e7dSPeter Dunlap 		 */
10860f1702c5SYu Xiangning 		(void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT,
10870f1702c5SYu Xiangning 		    (char *)&off, sizeof (off), CRED());
1088a6d42e7dSPeter Dunlap 
1089a6d42e7dSPeter Dunlap 		idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS,
1090a6d42e7dSPeter Dunlap 		    &ic);
1091a6d42e7dSPeter Dunlap 		if (idmrc != IDM_STATUS_SUCCESS) {
1092a6d42e7dSPeter Dunlap 			/* Drop connection */
1093a6d42e7dSPeter Dunlap 			idm_soshutdown(new_so);
1094a6d42e7dSPeter Dunlap 			idm_sodestroy(new_so);
1095a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1096a6d42e7dSPeter Dunlap 			continue;
1097a6d42e7dSPeter Dunlap 		}
1098a6d42e7dSPeter Dunlap 
1099a6d42e7dSPeter Dunlap 		idmrc = idm_so_tgt_conn_create(ic, new_so);
1100a6d42e7dSPeter Dunlap 		if (idmrc != IDM_STATUS_SUCCESS) {
1101a6d42e7dSPeter Dunlap 			idm_svc_conn_destroy(ic);
1102a6d42e7dSPeter Dunlap 			idm_soshutdown(new_so);
1103a6d42e7dSPeter Dunlap 			idm_sodestroy(new_so);
1104a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1105a6d42e7dSPeter Dunlap 			continue;
1106a6d42e7dSPeter Dunlap 		}
1107a6d42e7dSPeter Dunlap 
1108a6d42e7dSPeter Dunlap 		/*
1109a6d42e7dSPeter Dunlap 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
1110a6d42e7dSPeter Dunlap 		 * will notify the client (target) about the new connection.
1111a6d42e7dSPeter Dunlap 		 */
1112a6d42e7dSPeter Dunlap 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
1113a6d42e7dSPeter Dunlap 
1114a6d42e7dSPeter Dunlap 		mutex_enter(&svc->is_mutex);
1115a6d42e7dSPeter Dunlap 	}
11160f1702c5SYu Xiangning 	ksocket_rele(so_svc->is_so);
1117a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_FALSE;
1118a6d42e7dSPeter Dunlap 	mutex_exit(&svc->is_mutex);
1119a6d42e7dSPeter Dunlap 
1120a6d42e7dSPeter Dunlap 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc,
1121a6d42e7dSPeter Dunlap 	    svc->is_svc_req.sr_port);
1122a6d42e7dSPeter Dunlap 
1123a6d42e7dSPeter Dunlap 	thread_exit();
1124a6d42e7dSPeter Dunlap }
1125a6d42e7dSPeter Dunlap 
1126a6d42e7dSPeter Dunlap /*
1127a6d42e7dSPeter Dunlap  * idm_so_free_task_rsrc() stops any ongoing processing of the task and
1128a6d42e7dSPeter Dunlap  * frees resources associated with the task.
1129a6d42e7dSPeter Dunlap  *
1130a6d42e7dSPeter Dunlap  * It's not clear that this should return idm_status_t.  What do we do
1131a6d42e7dSPeter Dunlap  * if it fails?
1132a6d42e7dSPeter Dunlap  */
1133a6d42e7dSPeter Dunlap static idm_status_t
1134a6d42e7dSPeter Dunlap idm_so_free_task_rsrc(idm_task_t *idt)
1135a6d42e7dSPeter Dunlap {
1136a6d42e7dSPeter Dunlap 	idm_buf_t	*idb;
1137a6d42e7dSPeter Dunlap 
113830e7468fSPeter Dunlap 	/*
113930e7468fSPeter Dunlap 	 * There is nothing to cleanup on initiator connections
114030e7468fSPeter Dunlap 	 */
114130e7468fSPeter Dunlap 	if (IDM_CONN_ISINI(idt->idt_ic))
114230e7468fSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
114330e7468fSPeter Dunlap 
1144a6d42e7dSPeter Dunlap 	/*
1145a6d42e7dSPeter Dunlap 	 * If this is a target connection, call idm_buf_rx_from_ini_done for
1146a6d42e7dSPeter Dunlap 	 * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE.
1147a6d42e7dSPeter Dunlap 	 *
1148a6d42e7dSPeter Dunlap 	 * In addition, remove any buffers associated with this task from
1149a6d42e7dSPeter Dunlap 	 * the ic_tx_list.  We'll do this by walking the idt_inbufv list, but
1150a6d42e7dSPeter Dunlap 	 * items don't actually get removed from that list (and completion
1151a6d42e7dSPeter Dunlap 	 * routines called) until idm_task_cleanup.
1152a6d42e7dSPeter Dunlap 	 */
1153a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1154a6d42e7dSPeter Dunlap 
1155a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_outbufv); idb != NULL;
1156a6d42e7dSPeter Dunlap 	    idb = list_next(&idt->idt_outbufv, idb)) {
1157a6d42e7dSPeter Dunlap 		if (idb->idb_in_transport) {
1158a6d42e7dSPeter Dunlap 			/*
1159a6d42e7dSPeter Dunlap 			 * idm_buf_rx_from_ini_done releases idt->idt_mutex
1160a6d42e7dSPeter Dunlap 			 */
1161a6d42e7dSPeter Dunlap 			idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED);
1162a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
1163a6d42e7dSPeter Dunlap 		}
1164a6d42e7dSPeter Dunlap 	}
1165a6d42e7dSPeter Dunlap 
1166a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_inbufv); idb != NULL;
1167a6d42e7dSPeter Dunlap 	    idb = list_next(&idt->idt_inbufv, idb)) {
1168a6d42e7dSPeter Dunlap 		/*
1169a6d42e7dSPeter Dunlap 		 * We want to remove these items from the tx_list as well,
1170a6d42e7dSPeter Dunlap 		 * but knowing it's in the idt_inbufv list is not a guarantee
1171a6d42e7dSPeter Dunlap 		 * that it's in the tx_list.  If it's on the tx list then
1172a6d42e7dSPeter Dunlap 		 * let idm_sotx_thread() clean it up.
1173a6d42e7dSPeter Dunlap 		 */
1174a6d42e7dSPeter Dunlap 		if (idb->idb_in_transport && !idb->idb_tx_thread) {
1175a6d42e7dSPeter Dunlap 			/*
1176a6d42e7dSPeter Dunlap 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
1177a6d42e7dSPeter Dunlap 			 */
1178a6d42e7dSPeter Dunlap 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
1179a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
1180a6d42e7dSPeter Dunlap 		}
1181a6d42e7dSPeter Dunlap 	}
1182a6d42e7dSPeter Dunlap 
1183a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1184a6d42e7dSPeter Dunlap 
1185a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1186a6d42e7dSPeter Dunlap }
1187a6d42e7dSPeter Dunlap 
1188a6d42e7dSPeter Dunlap /*
1189a6d42e7dSPeter Dunlap  * idm_so_negotiate_key_values() validates the key values for this connection
1190a6d42e7dSPeter Dunlap  */
1191a6d42e7dSPeter Dunlap /* ARGSUSED */
1192a6d42e7dSPeter Dunlap static kv_status_t
1193a6d42e7dSPeter Dunlap idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl,
1194a6d42e7dSPeter Dunlap     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
1195a6d42e7dSPeter Dunlap {
1196a6d42e7dSPeter Dunlap 	/* All parameters are negotiated at the iscsit level */
1197a6d42e7dSPeter Dunlap 	return (KV_HANDLED);
1198a6d42e7dSPeter Dunlap }
1199a6d42e7dSPeter Dunlap 
1200a6d42e7dSPeter Dunlap /*
1201a6d42e7dSPeter Dunlap  * idm_so_notice_key_values() activates the negotiated key values for
1202a6d42e7dSPeter Dunlap  * this connection.
1203a6d42e7dSPeter Dunlap  */
120430e7468fSPeter Dunlap static void
1205a6d42e7dSPeter Dunlap idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl)
1206a6d42e7dSPeter Dunlap {
1207a6d42e7dSPeter Dunlap 	char			*nvp_name;
1208a6d42e7dSPeter Dunlap 	nvpair_t		*nvp;
1209a6d42e7dSPeter Dunlap 	nvpair_t		*next_nvp;
1210a6d42e7dSPeter Dunlap 	int			nvrc;
1211a6d42e7dSPeter Dunlap 	idm_status_t		idm_status;
1212a6d42e7dSPeter Dunlap 	const idm_kv_xlate_t	*ikvx;
1213a6d42e7dSPeter Dunlap 
1214a6d42e7dSPeter Dunlap 	for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL);
1215a6d42e7dSPeter Dunlap 	    nvp != NULL; nvp = next_nvp) {
1216a6d42e7dSPeter Dunlap 		next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp);
1217a6d42e7dSPeter Dunlap 		nvp_name = nvpair_name(nvp);
1218a6d42e7dSPeter Dunlap 
1219a6d42e7dSPeter Dunlap 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
1220a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1221a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1222a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1223a6d42e7dSPeter Dunlap 			idm_status = idm_so_handle_digest(it, nvp, ikvx);
1224a6d42e7dSPeter Dunlap 			ASSERT(idm_status == 0);
1225a6d42e7dSPeter Dunlap 
1226a6d42e7dSPeter Dunlap 			/* Remove processed item from negotiated_nvl list */
1227a6d42e7dSPeter Dunlap 			nvrc = nvlist_remove_all(
1228a6d42e7dSPeter Dunlap 			    negotiated_nvl, ikvx->ik_key_name);
1229a6d42e7dSPeter Dunlap 			ASSERT(nvrc == 0);
1230a6d42e7dSPeter Dunlap 			break;
1231a6d42e7dSPeter Dunlap 		default:
1232a6d42e7dSPeter Dunlap 			break;
1233a6d42e7dSPeter Dunlap 		}
1234a6d42e7dSPeter Dunlap 	}
1235a6d42e7dSPeter Dunlap }
1236a6d42e7dSPeter Dunlap 
1237a6d42e7dSPeter Dunlap 
1238a6d42e7dSPeter Dunlap static idm_status_t
1239a6d42e7dSPeter Dunlap idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice,
1240a6d42e7dSPeter Dunlap     const idm_kv_xlate_t *ikvx)
1241a6d42e7dSPeter Dunlap {
1242a6d42e7dSPeter Dunlap 	int			nvrc;
1243a6d42e7dSPeter Dunlap 	char			*digest_choice_string;
1244a6d42e7dSPeter Dunlap 
1245a6d42e7dSPeter Dunlap 	nvrc = nvpair_value_string(digest_choice,
1246a6d42e7dSPeter Dunlap 	    &digest_choice_string);
1247a6d42e7dSPeter Dunlap 	ASSERT(nvrc == 0);
1248a6d42e7dSPeter Dunlap 	if (strcasecmp(digest_choice_string, "crc32c") == 0) {
1249a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1250a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1251a6d42e7dSPeter Dunlap 			it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST;
1252a6d42e7dSPeter Dunlap 			break;
1253a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1254a6d42e7dSPeter Dunlap 			it->ic_conn_flags |= IDM_CONN_DATA_DIGEST;
1255a6d42e7dSPeter Dunlap 			break;
1256a6d42e7dSPeter Dunlap 		default:
1257a6d42e7dSPeter Dunlap 			ASSERT(0);
1258a6d42e7dSPeter Dunlap 			break;
1259a6d42e7dSPeter Dunlap 		}
1260a6d42e7dSPeter Dunlap 	} else if (strcasecmp(digest_choice_string, "none") == 0) {
1261a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1262a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1263a6d42e7dSPeter Dunlap 			it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST;
1264a6d42e7dSPeter Dunlap 			break;
1265a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1266a6d42e7dSPeter Dunlap 			it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST;
1267a6d42e7dSPeter Dunlap 			break;
1268a6d42e7dSPeter Dunlap 		default:
1269a6d42e7dSPeter Dunlap 			ASSERT(0);
1270a6d42e7dSPeter Dunlap 			break;
1271a6d42e7dSPeter Dunlap 		}
1272a6d42e7dSPeter Dunlap 	} else {
1273a6d42e7dSPeter Dunlap 		ASSERT(0);
1274a6d42e7dSPeter Dunlap 	}
1275a6d42e7dSPeter Dunlap 
1276a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1277a6d42e7dSPeter Dunlap }
1278a6d42e7dSPeter Dunlap 
1279a6d42e7dSPeter Dunlap 
1280a6d42e7dSPeter Dunlap /*
1281a6d42e7dSPeter Dunlap  * idm_so_conn_is_capable() verifies that the passed connection is provided
1282a6d42e7dSPeter Dunlap  * for by the sockets interface.
1283a6d42e7dSPeter Dunlap  */
1284a6d42e7dSPeter Dunlap /* ARGSUSED */
1285a6d42e7dSPeter Dunlap static boolean_t
1286a6d42e7dSPeter Dunlap idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps)
1287a6d42e7dSPeter Dunlap {
1288a6d42e7dSPeter Dunlap 	return (B_TRUE);
1289a6d42e7dSPeter Dunlap }
1290a6d42e7dSPeter Dunlap 
1291a6d42e7dSPeter Dunlap /*
1292a6d42e7dSPeter Dunlap  * idm_so_rx_datain() validates the Data Sequence number of the PDU. The
1293a6d42e7dSPeter Dunlap  * idm_sorecv_scsidata() function invoked earlier actually reads the data
1294a6d42e7dSPeter Dunlap  * off the socket into the appropriate buffers.
1295a6d42e7dSPeter Dunlap  */
1296a6d42e7dSPeter Dunlap static void
1297a6d42e7dSPeter Dunlap idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu)
1298a6d42e7dSPeter Dunlap {
1299a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1300a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1301a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1302a6d42e7dSPeter Dunlap 	uint32_t		datasn;
1303a6d42e7dSPeter Dunlap 	size_t			offset;
1304a6d42e7dSPeter Dunlap 	iscsi_hdr_t		*ihp = (iscsi_hdr_t *)pdu->isp_hdr;
1305a6d42e7dSPeter Dunlap 	iscsi_data_rsp_hdr_t    *idrhp = (iscsi_data_rsp_hdr_t *)ihp;
1306a6d42e7dSPeter Dunlap 
1307a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1308a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1309a6d42e7dSPeter Dunlap 
1310a6d42e7dSPeter Dunlap 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
1311a6d42e7dSPeter Dunlap 	datasn	= ntohl(bhs->datasn);
1312a6d42e7dSPeter Dunlap 	offset	= ntohl(bhs->offset);
1313a6d42e7dSPeter Dunlap 
1314a6d42e7dSPeter Dunlap 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP);
1315a6d42e7dSPeter Dunlap 
1316a6d42e7dSPeter Dunlap 	/*
1317a6d42e7dSPeter Dunlap 	 * Look up the task corresponding to the initiator task tag
1318a6d42e7dSPeter Dunlap 	 * to get the buffers affiliated with the task.
1319a6d42e7dSPeter Dunlap 	 */
1320a6d42e7dSPeter Dunlap 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
1321a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1322a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task");
1323a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1324a6d42e7dSPeter Dunlap 		return;
1325a6d42e7dSPeter Dunlap 	}
1326a6d42e7dSPeter Dunlap 
1327a6d42e7dSPeter Dunlap 	idb = pdu->isp_sorx_buf;
1328a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1329a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1330a6d42e7dSPeter Dunlap 		    "idm_so_rx_datain: failed to find buffer");
1331a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1332a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1333a6d42e7dSPeter Dunlap 		return;
1334a6d42e7dSPeter Dunlap 	}
1335a6d42e7dSPeter Dunlap 
1336a6d42e7dSPeter Dunlap 	/*
1337a6d42e7dSPeter Dunlap 	 * DataSN values should be sequential and should not have any gaps or
1338a6d42e7dSPeter Dunlap 	 * repetitions. Check the DataSN with the one stored in the task.
1339a6d42e7dSPeter Dunlap 	 */
1340a6d42e7dSPeter Dunlap 	if (datasn == idt->idt_exp_datasn) {
1341a6d42e7dSPeter Dunlap 		idt->idt_exp_datasn++; /* keep track of DataSN received */
1342a6d42e7dSPeter Dunlap 	} else {
1343a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order");
1344a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1345a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1346a6d42e7dSPeter Dunlap 		return;
1347a6d42e7dSPeter Dunlap 	}
1348a6d42e7dSPeter Dunlap 
1349a6d42e7dSPeter Dunlap 	/*
1350a6d42e7dSPeter Dunlap 	 * PDUs in a sequence should be in continuously increasing
1351a6d42e7dSPeter Dunlap 	 * address offset
1352a6d42e7dSPeter Dunlap 	 */
1353a6d42e7dSPeter Dunlap 	if (offset != idb->idb_exp_offset) {
1354a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset");
135530e7468fSPeter Dunlap 		idm_task_rele(idt);
1356a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1357a6d42e7dSPeter Dunlap 		return;
1358a6d42e7dSPeter Dunlap 	}
1359a6d42e7dSPeter Dunlap 	/* Expected next relative buffer offset */
1360a6d42e7dSPeter Dunlap 	idb->idb_exp_offset += n2h24(bhs->dlength);
136130e7468fSPeter Dunlap 	idt->idt_rx_bytes += n2h24(bhs->dlength);
136230e7468fSPeter Dunlap 
136330e7468fSPeter Dunlap 	idm_task_rele(idt);
1364a6d42e7dSPeter Dunlap 
1365a6d42e7dSPeter Dunlap 	/*
1366a6d42e7dSPeter Dunlap 	 * For now call scsi_rsp which will process the data rsp
1367a6d42e7dSPeter Dunlap 	 * Revisit, need to provide an explicit client entry point for
1368a6d42e7dSPeter Dunlap 	 * phase collapse completions.
1369a6d42e7dSPeter Dunlap 	 */
1370a6d42e7dSPeter Dunlap 	if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) &&
1371a6d42e7dSPeter Dunlap 	    (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) {
1372a6d42e7dSPeter Dunlap 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
1373a6d42e7dSPeter Dunlap 	}
1374a6d42e7dSPeter Dunlap 
1375a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1376a6d42e7dSPeter Dunlap }
1377a6d42e7dSPeter Dunlap 
1378a6d42e7dSPeter Dunlap /*
1379a6d42e7dSPeter Dunlap  * The idm_so_rx_dataout() function is used by the iSCSI target to read
1380a6d42e7dSPeter Dunlap  * data from the Data-Out PDU sent by the iSCSI initiator.
1381a6d42e7dSPeter Dunlap  *
1382a6d42e7dSPeter Dunlap  * This function gets the Initiator Task Tag from the PDU BHS and looks up the
1383a6d42e7dSPeter Dunlap  * task to get the buffers associated with the PDU. A PDU might span buffers.
1384a6d42e7dSPeter Dunlap  * The data is then read into the respective buffer.
1385a6d42e7dSPeter Dunlap  */
1386a6d42e7dSPeter Dunlap static void
1387a6d42e7dSPeter Dunlap idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu)
1388a6d42e7dSPeter Dunlap {
1389a6d42e7dSPeter Dunlap 
1390a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1391a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1392a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1393a6d42e7dSPeter Dunlap 	size_t			offset;
1394a6d42e7dSPeter Dunlap 
1395a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1396a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1397a6d42e7dSPeter Dunlap 
1398a6d42e7dSPeter Dunlap 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
1399a6d42e7dSPeter Dunlap 	offset = ntohl(bhs->offset);
1400a6d42e7dSPeter Dunlap 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA);
1401a6d42e7dSPeter Dunlap 
1402a6d42e7dSPeter Dunlap 	/*
1403a6d42e7dSPeter Dunlap 	 * Look up the task corresponding to the initiator task tag
1404a6d42e7dSPeter Dunlap 	 * to get the buffers affiliated with the task.
1405a6d42e7dSPeter Dunlap 	 */
1406a6d42e7dSPeter Dunlap 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
1407a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1408a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1409a6d42e7dSPeter Dunlap 		    "idm_so_rx_dataout: failed to find task");
1410a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1411a6d42e7dSPeter Dunlap 		return;
1412a6d42e7dSPeter Dunlap 	}
1413a6d42e7dSPeter Dunlap 
1414a6d42e7dSPeter Dunlap 	idb = pdu->isp_sorx_buf;
1415a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1416a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1417a6d42e7dSPeter Dunlap 		    "idm_so_rx_dataout: failed to find buffer");
1418a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1419a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1420a6d42e7dSPeter Dunlap 		return;
1421a6d42e7dSPeter Dunlap 	}
1422a6d42e7dSPeter Dunlap 
1423a6d42e7dSPeter Dunlap 	/* Keep track of data transferred - check data offsets */
1424a6d42e7dSPeter Dunlap 	if (offset != idb->idb_exp_offset) {
1425a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: "
1426a6d42e7dSPeter Dunlap 		    "%ld, %d", offset, idb->idb_exp_offset);
1427a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1428a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1429a6d42e7dSPeter Dunlap 		return;
1430a6d42e7dSPeter Dunlap 	}
1431a6d42e7dSPeter Dunlap 	/* Expected next relative offset */
1432a6d42e7dSPeter Dunlap 	idb->idb_exp_offset += ntoh24(bhs->dlength);
143330e7468fSPeter Dunlap 	idt->idt_rx_bytes += n2h24(bhs->dlength);
1434a6d42e7dSPeter Dunlap 
1435a6d42e7dSPeter Dunlap 	/*
1436a6d42e7dSPeter Dunlap 	 * Call the buffer callback when the transfer is complete
1437a6d42e7dSPeter Dunlap 	 *
1438a6d42e7dSPeter Dunlap 	 * The connection state machine should only abort tasks after
1439a6d42e7dSPeter Dunlap 	 * shutting down the connection so we are assured that there
1440a6d42e7dSPeter Dunlap 	 * won't be a simultaneous attempt to abort this task at the
1441a6d42e7dSPeter Dunlap 	 * same time as we are processing this PDU (due to a connection
1442a6d42e7dSPeter Dunlap 	 * state change).
1443a6d42e7dSPeter Dunlap 	 */
1444a6d42e7dSPeter Dunlap 	if (bhs->flags & ISCSI_FLAG_FINAL) {
1445a6d42e7dSPeter Dunlap 		/*
1446a6d42e7dSPeter Dunlap 		 * We only want to call idm_buf_rx_from_ini_done once
1447a6d42e7dSPeter Dunlap 		 * per transfer.  It's possible that this task has
1448a6d42e7dSPeter Dunlap 		 * already been aborted in which case
1449a6d42e7dSPeter Dunlap 		 * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done
1450a6d42e7dSPeter Dunlap 		 * for each buffer with idb_in_transport==B_TRUE.  To
1451a6d42e7dSPeter Dunlap 		 * close this window and ensure that this doesn't happen,
1452a6d42e7dSPeter Dunlap 		 * we'll clear idb->idb_in_transport now while holding
1453a6d42e7dSPeter Dunlap 		 * the task mutex.   This is only really an issue for
1454a6d42e7dSPeter Dunlap 		 * SCSI task abort -- if tasks were being aborted because
1455a6d42e7dSPeter Dunlap 		 * of a connection state change the state machine would
1456a6d42e7dSPeter Dunlap 		 * have already stopped the receive thread.
1457a6d42e7dSPeter Dunlap 		 */
1458a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1459a6d42e7dSPeter Dunlap 
1460a6d42e7dSPeter Dunlap 		/*
1461a6d42e7dSPeter Dunlap 		 * Release the task hold here (obtained in idm_task_find)
1462a6d42e7dSPeter Dunlap 		 * because the task may complete synchronously during
1463a6d42e7dSPeter Dunlap 		 * idm_buf_rx_from_ini_done.  Since we still have an active
1464a6d42e7dSPeter Dunlap 		 * buffer we know there is at least one additional hold on idt.
1465a6d42e7dSPeter Dunlap 		 */
1466a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1467a6d42e7dSPeter Dunlap 
1468a6d42e7dSPeter Dunlap 		/*
1469a6d42e7dSPeter Dunlap 		 * idm_buf_rx_from_ini_done releases idt->idt_mutex
1470a6d42e7dSPeter Dunlap 		 */
1471a6d42e7dSPeter Dunlap 		idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS);
1472a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1473a6d42e7dSPeter Dunlap 		return;
1474a6d42e7dSPeter Dunlap 	}
1475a6d42e7dSPeter Dunlap 
1476a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
1477a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1478a6d42e7dSPeter Dunlap }
1479a6d42e7dSPeter Dunlap 
1480a6d42e7dSPeter Dunlap /*
1481a6d42e7dSPeter Dunlap  * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle
1482a6d42e7dSPeter Dunlap  * the R2T PDU sent by the iSCSI target indicating that it is ready to
1483a6d42e7dSPeter Dunlap  * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS
1484a6d42e7dSPeter Dunlap  * and looks up the task in the task tree using the itt to get the output
1485a6d42e7dSPeter Dunlap  * buffers associated the task. The R2T PDU contains the offset of the
1486a6d42e7dSPeter Dunlap  * requested data and the data length. This function then constructs a
1487a6d42e7dSPeter Dunlap  * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out
1488a6d42e7dSPeter Dunlap  * PDU is associated with the R2T by the Target Transfer Tag  (ttt).
1489a6d42e7dSPeter Dunlap  */
149030e7468fSPeter Dunlap 
1491a6d42e7dSPeter Dunlap static void
1492a6d42e7dSPeter Dunlap idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu)
1493a6d42e7dSPeter Dunlap {
1494a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1495a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1496a6d42e7dSPeter Dunlap 	iscsi_rtt_hdr_t		*rtt_hdr;
1497a6d42e7dSPeter Dunlap 	uint32_t		data_offset;
149830e7468fSPeter Dunlap 	uint32_t		data_length;
1499a6d42e7dSPeter Dunlap 
1500a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1501a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1502a6d42e7dSPeter Dunlap 
1503a6d42e7dSPeter Dunlap 	rtt_hdr	= (iscsi_rtt_hdr_t *)pdu->isp_hdr;
1504a6d42e7dSPeter Dunlap 	data_offset = ntohl(rtt_hdr->data_offset);
150530e7468fSPeter Dunlap 	data_length = ntohl(rtt_hdr->data_length);
1506a6d42e7dSPeter Dunlap 	idt	= idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt);
1507a6d42e7dSPeter Dunlap 
1508a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1509a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task");
1510a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1511a6d42e7dSPeter Dunlap 		return;
1512a6d42e7dSPeter Dunlap 	}
1513a6d42e7dSPeter Dunlap 
1514a6d42e7dSPeter Dunlap 	/* Find the buffer bound to the task by the iSCSI initiator */
1515a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1516a6d42e7dSPeter Dunlap 	idb = idm_buf_find(&idt->idt_outbufv, data_offset);
1517a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1518a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1519a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1520a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer");
1521a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1522a6d42e7dSPeter Dunlap 		return;
1523a6d42e7dSPeter Dunlap 	}
1524a6d42e7dSPeter Dunlap 
152530e7468fSPeter Dunlap 	/* return buffer contains this data */
152630e7468fSPeter Dunlap 	if (data_offset + data_length > idb->idb_buflen) {
152730e7468fSPeter Dunlap 		/* Overflow */
152830e7468fSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
152930e7468fSPeter Dunlap 		idm_task_rele(idt);
153030e7468fSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside "
153130e7468fSPeter Dunlap 		    "buffer");
153230e7468fSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
153330e7468fSPeter Dunlap 		return;
153430e7468fSPeter Dunlap 	}
153530e7468fSPeter Dunlap 
153630e7468fSPeter Dunlap 	idt->idt_r2t_ttt = rtt_hdr->ttt;
153730e7468fSPeter Dunlap 	idt->idt_exp_datasn = 0;
153830e7468fSPeter Dunlap 
153930e7468fSPeter Dunlap 	idm_so_send_rtt_data(ic, idt, idb, data_offset,
154030e7468fSPeter Dunlap 	    ntohl(rtt_hdr->data_length));
1541a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1542a6d42e7dSPeter Dunlap 
1543a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1544a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
1545a6d42e7dSPeter Dunlap 
1546a6d42e7dSPeter Dunlap }
1547a6d42e7dSPeter Dunlap 
1548a6d42e7dSPeter Dunlap idm_status_t
1549a6d42e7dSPeter Dunlap idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu)
1550a6d42e7dSPeter Dunlap {
1551a6d42e7dSPeter Dunlap 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
1552a6d42e7dSPeter Dunlap 	int		pad_len;
1553a6d42e7dSPeter Dunlap 	uint32_t	data_digest_crc;
1554a6d42e7dSPeter Dunlap 	uint32_t	crc_calculated;
1555a6d42e7dSPeter Dunlap 	int		total_len;
1556a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1557a6d42e7dSPeter Dunlap 
1558a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1559a6d42e7dSPeter Dunlap 
1560a6d42e7dSPeter Dunlap 	pad_len = ((ISCSI_PAD_WORD_LEN -
1561a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
1562a6d42e7dSPeter Dunlap 	    (ISCSI_PAD_WORD_LEN - 1));
1563a6d42e7dSPeter Dunlap 
1564a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */
1565a6d42e7dSPeter Dunlap 
1566a6d42e7dSPeter Dunlap 	total_len = pdu->isp_datalen;
1567a6d42e7dSPeter Dunlap 
1568a6d42e7dSPeter Dunlap 	if (pad_len) {
1569a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_base	= (char *)&pad;
1570a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_len	= pad_len;
1571a6d42e7dSPeter Dunlap 		total_len		+= pad_len;
1572a6d42e7dSPeter Dunlap 		pdu->isp_iovlen++;
1573a6d42e7dSPeter Dunlap 	}
1574a6d42e7dSPeter Dunlap 
1575a6d42e7dSPeter Dunlap 	/* setup data digest */
1576a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
1577a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_base =
1578a6d42e7dSPeter Dunlap 		    (char *)&data_digest_crc;
1579a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_len =
1580a6d42e7dSPeter Dunlap 		    sizeof (data_digest_crc);
1581a6d42e7dSPeter Dunlap 		total_len		+= sizeof (data_digest_crc);
1582a6d42e7dSPeter Dunlap 		pdu->isp_iovlen++;
1583a6d42e7dSPeter Dunlap 	}
1584a6d42e7dSPeter Dunlap 
158530e7468fSPeter Dunlap 	pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base;
158630e7468fSPeter Dunlap 
1587a6d42e7dSPeter Dunlap 	if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0],
1588a6d42e7dSPeter Dunlap 	    pdu->isp_iovlen, total_len) != 0) {
1589a6d42e7dSPeter Dunlap 		return (IDM_STATUS_IO);
1590a6d42e7dSPeter Dunlap 	}
1591a6d42e7dSPeter Dunlap 
1592a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
1593a6d42e7dSPeter Dunlap 		crc_calculated = idm_crc32c(pdu->isp_data,
1594a6d42e7dSPeter Dunlap 		    pdu->isp_datalen);
1595a6d42e7dSPeter Dunlap 		if (pad_len) {
1596a6d42e7dSPeter Dunlap 			crc_calculated = idm_crc32c_continued((char *)&pad,
1597a6d42e7dSPeter Dunlap 			    pad_len, crc_calculated);
1598a6d42e7dSPeter Dunlap 		}
1599a6d42e7dSPeter Dunlap 		if (crc_calculated != data_digest_crc) {
1600a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN,
1601a6d42e7dSPeter Dunlap 			    "idm_sorecvdata: "
1602a6d42e7dSPeter Dunlap 			    "CRC error: actual 0x%x, calc 0x%x",
1603a6d42e7dSPeter Dunlap 			    data_digest_crc, crc_calculated);
1604a6d42e7dSPeter Dunlap 
1605a6d42e7dSPeter Dunlap 			/* Invalid Data Digest */
1606a6d42e7dSPeter Dunlap 			return (IDM_STATUS_DATA_DIGEST);
1607a6d42e7dSPeter Dunlap 		}
1608a6d42e7dSPeter Dunlap 	}
1609a6d42e7dSPeter Dunlap 
1610a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1611a6d42e7dSPeter Dunlap }
1612a6d42e7dSPeter Dunlap 
1613a6d42e7dSPeter Dunlap /*
1614a6d42e7dSPeter Dunlap  * idm_sorecv_scsidata() is used to receive scsi data from the socket. The
1615a6d42e7dSPeter Dunlap  * Data-type PDU header must be read into the idm_pdu_t structure prior to
1616a6d42e7dSPeter Dunlap  * calling this function.
1617a6d42e7dSPeter Dunlap  */
1618a6d42e7dSPeter Dunlap idm_status_t
1619a6d42e7dSPeter Dunlap idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu)
1620a6d42e7dSPeter Dunlap {
1621a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1622a6d42e7dSPeter Dunlap 	idm_task_t		*task;
1623a6d42e7dSPeter Dunlap 	uint32_t		offset;
1624a6d42e7dSPeter Dunlap 	uint8_t			opcode;
1625a6d42e7dSPeter Dunlap 	uint32_t		dlength;
1626a6d42e7dSPeter Dunlap 	list_t			*buflst;
1627a6d42e7dSPeter Dunlap 	uint32_t		xfer_bytes;
1628a6d42e7dSPeter Dunlap 	idm_status_t		status;
1629a6d42e7dSPeter Dunlap 
1630a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1631a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1632a6d42e7dSPeter Dunlap 
1633a6d42e7dSPeter Dunlap 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
1634a6d42e7dSPeter Dunlap 
1635a6d42e7dSPeter Dunlap 	offset	= ntohl(bhs->offset);
1636a6d42e7dSPeter Dunlap 	opcode	= bhs->opcode;
1637a6d42e7dSPeter Dunlap 	dlength = n2h24(bhs->dlength);
1638a6d42e7dSPeter Dunlap 
1639a6d42e7dSPeter Dunlap 	ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
1640a6d42e7dSPeter Dunlap 	    (opcode == ISCSI_OP_SCSI_DATA));
1641a6d42e7dSPeter Dunlap 
1642a6d42e7dSPeter Dunlap 	/*
1643a6d42e7dSPeter Dunlap 	 * Successful lookup implicitly gets a "hold" on the task.  This
1644a6d42e7dSPeter Dunlap 	 * hold must be released before leaving this function.  At one
1645a6d42e7dSPeter Dunlap 	 * point we were caching this task context and retaining the hold
1646a6d42e7dSPeter Dunlap 	 * but it turned out to be very difficult to release the hold properly.
1647a6d42e7dSPeter Dunlap 	 * The task can be aborted and the connection shutdown between this
1648a6d42e7dSPeter Dunlap 	 * call and the subsequent expected call to idm_so_rx_datain/
1649a6d42e7dSPeter Dunlap 	 * idm_so_rx_dataout (in which case those functions are not called).
1650a6d42e7dSPeter Dunlap 	 * Releasing the hold in the PDU callback doesn't work well either
1651a6d42e7dSPeter Dunlap 	 * because the whole task may be completed by then at which point
1652a6d42e7dSPeter Dunlap 	 * it is too late to release the hold -- for better or worse this
1653a6d42e7dSPeter Dunlap 	 * code doesn't wait on the refcnts during normal operation.
1654a6d42e7dSPeter Dunlap 	 * idm_task_find() is very fast and it is not a huge burden if we
1655a6d42e7dSPeter Dunlap 	 * have to do it twice.
1656a6d42e7dSPeter Dunlap 	 */
1657a6d42e7dSPeter Dunlap 	task = idm_task_find(ic, bhs->itt, bhs->ttt);
1658a6d42e7dSPeter Dunlap 	if (task == NULL) {
1659a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1660a6d42e7dSPeter Dunlap 		    "idm_sorecv_scsidata: could not find task");
1661a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1662a6d42e7dSPeter Dunlap 	}
1663a6d42e7dSPeter Dunlap 
1664a6d42e7dSPeter Dunlap 	mutex_enter(&task->idt_mutex);
1665a6d42e7dSPeter Dunlap 	buflst	= (opcode == ISCSI_OP_SCSI_DATA_RSP) ?
1666a6d42e7dSPeter Dunlap 	    &task->idt_inbufv : &task->idt_outbufv;
1667a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = idm_buf_find(buflst, offset);
1668a6d42e7dSPeter Dunlap 	mutex_exit(&task->idt_mutex);
1669a6d42e7dSPeter Dunlap 
1670a6d42e7dSPeter Dunlap 	if (pdu->isp_sorx_buf == NULL) {
1671a6d42e7dSPeter Dunlap 		idm_task_rele(task);
1672a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find "
1673a6d42e7dSPeter Dunlap 		    "buffer for offset %x opcode=%x",
1674a6d42e7dSPeter Dunlap 		    offset, opcode);
1675a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1676a6d42e7dSPeter Dunlap 	}
1677a6d42e7dSPeter Dunlap 
1678a6d42e7dSPeter Dunlap 	xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength);
1679a6d42e7dSPeter Dunlap 	ASSERT(xfer_bytes != 0);
1680a6d42e7dSPeter Dunlap 	if (xfer_bytes != dlength) {
1681a6d42e7dSPeter Dunlap 		idm_task_rele(task);
1682a6d42e7dSPeter Dunlap 		/*
1683a6d42e7dSPeter Dunlap 		 * Buffer overflow, connection error.  The PDU data is still
1684a6d42e7dSPeter Dunlap 		 * sitting in the socket so we can't use the connection
1685a6d42e7dSPeter Dunlap 		 * again until that data is drained.
1686a6d42e7dSPeter Dunlap 		 */
1687a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1688a6d42e7dSPeter Dunlap 	}
1689a6d42e7dSPeter Dunlap 
1690a6d42e7dSPeter Dunlap 	status = idm_sorecvdata(ic, pdu);
1691a6d42e7dSPeter Dunlap 
1692a6d42e7dSPeter Dunlap 	idm_task_rele(task);
1693a6d42e7dSPeter Dunlap 
1694a6d42e7dSPeter Dunlap 	return (status);
1695a6d42e7dSPeter Dunlap }
1696a6d42e7dSPeter Dunlap 
1697a6d42e7dSPeter Dunlap static uint32_t
1698a6d42e7dSPeter Dunlap idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength)
1699a6d42e7dSPeter Dunlap {
1700a6d42e7dSPeter Dunlap 	uint32_t	buf_ro = ro - idb->idb_bufoffset;
1701a6d42e7dSPeter Dunlap 	uint32_t	xfer_len = min(dlength, idb->idb_buflen - buf_ro);
1702a6d42e7dSPeter Dunlap 
1703a6d42e7dSPeter Dunlap 	ASSERT(ro >= idb->idb_bufoffset);
1704a6d42e7dSPeter Dunlap 
1705a6d42e7dSPeter Dunlap 	pdu->isp_iov[pdu->isp_iovlen].iov_base	=
1706a6d42e7dSPeter Dunlap 	    (caddr_t)idb->idb_buf + buf_ro;
1707a6d42e7dSPeter Dunlap 	pdu->isp_iov[pdu->isp_iovlen].iov_len	= xfer_len;
1708a6d42e7dSPeter Dunlap 	pdu->isp_iovlen++;
1709a6d42e7dSPeter Dunlap 
1710a6d42e7dSPeter Dunlap 	return (xfer_len);
1711a6d42e7dSPeter Dunlap }
1712a6d42e7dSPeter Dunlap 
1713a6d42e7dSPeter Dunlap int
1714a6d42e7dSPeter Dunlap idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu)
1715a6d42e7dSPeter Dunlap {
1716a6d42e7dSPeter Dunlap 	pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP);
1717a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_data != NULL);
1718a6d42e7dSPeter Dunlap 
1719a6d42e7dSPeter Dunlap 	pdu->isp_databuflen = pdu->isp_datalen;
1720a6d42e7dSPeter Dunlap 	pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data;
1721a6d42e7dSPeter Dunlap 	pdu->isp_iov[0].iov_len = pdu->isp_datalen;
1722a6d42e7dSPeter Dunlap 	pdu->isp_iovlen = 1;
1723a6d42e7dSPeter Dunlap 	/*
1724a6d42e7dSPeter Dunlap 	 * Since we are associating a new data buffer with this received
1725a6d42e7dSPeter Dunlap 	 * PDU we need to set a specific callback to free the data
1726a6d42e7dSPeter Dunlap 	 * after the PDU is processed.
1727a6d42e7dSPeter Dunlap 	 */
1728a6d42e7dSPeter Dunlap 	pdu->isp_flags |= IDM_PDU_ADDL_DATA;
1729a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_addl_pdu_cb;
1730a6d42e7dSPeter Dunlap 
1731a6d42e7dSPeter Dunlap 	return (idm_sorecvdata(ic, pdu));
1732a6d42e7dSPeter Dunlap }
1733a6d42e7dSPeter Dunlap 
1734a6d42e7dSPeter Dunlap void
1735a6d42e7dSPeter Dunlap idm_sorx_thread(void *arg)
1736a6d42e7dSPeter Dunlap {
1737a6d42e7dSPeter Dunlap 	boolean_t	conn_failure = B_FALSE;
1738a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = (idm_conn_t *)arg;
1739a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1740a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu;
1741a6d42e7dSPeter Dunlap 	idm_status_t	rc;
1742a6d42e7dSPeter Dunlap 
1743a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
1744a6d42e7dSPeter Dunlap 
1745a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
1746a6d42e7dSPeter Dunlap 
1747a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1748a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_running = B_TRUE;
1749a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did;
1750a6d42e7dSPeter Dunlap 	cv_signal(&ic->ic_cv);
1751a6d42e7dSPeter Dunlap 
1752a6d42e7dSPeter Dunlap 	while (so_conn->ic_rx_thread_running) {
1753a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_mutex);
1754a6d42e7dSPeter Dunlap 
1755a6d42e7dSPeter Dunlap 		/*
1756a6d42e7dSPeter Dunlap 		 * Get PDU with default header size (large enough for
1757a6d42e7dSPeter Dunlap 		 * BHS plus any anticipated AHS).  PDU from
1758a6d42e7dSPeter Dunlap 		 * the cache will have all values set correctly
1759a6d42e7dSPeter Dunlap 		 * for sockets RX including callback.
1760a6d42e7dSPeter Dunlap 		 */
1761a6d42e7dSPeter Dunlap 		pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP);
1762a6d42e7dSPeter Dunlap 		pdu->isp_ic = ic;
1763a6d42e7dSPeter Dunlap 		pdu->isp_flags = 0;
1764a6d42e7dSPeter Dunlap 		pdu->isp_transport_hdrlen = 0;
1765a6d42e7dSPeter Dunlap 
1766a6d42e7dSPeter Dunlap 		if ((rc = idm_sorecvhdr(ic, pdu)) != 0) {
1767a6d42e7dSPeter Dunlap 			/*
1768a6d42e7dSPeter Dunlap 			 * Call idm_pdu_complete so that we call the callback
1769a6d42e7dSPeter Dunlap 			 * and ensure any memory allocated in idm_sorecvhdr
1770a6d42e7dSPeter Dunlap 			 * gets freed up.
1771a6d42e7dSPeter Dunlap 			 */
1772a6d42e7dSPeter Dunlap 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
1773a6d42e7dSPeter Dunlap 
1774a6d42e7dSPeter Dunlap 			/*
1775a6d42e7dSPeter Dunlap 			 * If ic_rx_thread_running is still set then
1776a6d42e7dSPeter Dunlap 			 * this is some kind of connection problem
1777a6d42e7dSPeter Dunlap 			 * on the socket.  In this case we want to
1778a6d42e7dSPeter Dunlap 			 * generate an event.  Otherwise some other
1779a6d42e7dSPeter Dunlap 			 * thread closed the socket due to another
1780a6d42e7dSPeter Dunlap 			 * issue in which case we don't need to
1781a6d42e7dSPeter Dunlap 			 * generate an event.
1782a6d42e7dSPeter Dunlap 			 */
1783a6d42e7dSPeter Dunlap 			mutex_enter(&ic->ic_mutex);
1784a6d42e7dSPeter Dunlap 			if (so_conn->ic_rx_thread_running) {
1785a6d42e7dSPeter Dunlap 				conn_failure = B_TRUE;
1786a6d42e7dSPeter Dunlap 				so_conn->ic_rx_thread_running = B_FALSE;
1787a6d42e7dSPeter Dunlap 			}
1788a6d42e7dSPeter Dunlap 
1789a6d42e7dSPeter Dunlap 			continue;
1790a6d42e7dSPeter Dunlap 		}
1791a6d42e7dSPeter Dunlap 
1792a6d42e7dSPeter Dunlap 		/*
1793a6d42e7dSPeter Dunlap 		 * Header has been read and validated.  Now we need
1794a6d42e7dSPeter Dunlap 		 * to read the PDU data payload (if present).  SCSI data
1795a6d42e7dSPeter Dunlap 		 * need to be transferred from the socket directly into
1796a6d42e7dSPeter Dunlap 		 * the associated transfer buffer for the SCSI task.
1797a6d42e7dSPeter Dunlap 		 */
1798a6d42e7dSPeter Dunlap 		if (pdu->isp_datalen != 0) {
1799a6d42e7dSPeter Dunlap 			if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) ||
1800a6d42e7dSPeter Dunlap 			    (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) {
1801a6d42e7dSPeter Dunlap 				rc = idm_sorecv_scsidata(ic, pdu);
1802a6d42e7dSPeter Dunlap 				/*
1803a6d42e7dSPeter Dunlap 				 * All SCSI errors are fatal to the
1804a6d42e7dSPeter Dunlap 				 * connection right now since we have no
1805a6d42e7dSPeter Dunlap 				 * place to put the data.  What we need
1806a6d42e7dSPeter Dunlap 				 * is some kind of sink to dispose of unwanted
1807a6d42e7dSPeter Dunlap 				 * SCSI data.  For example an invalid task tag
1808a6d42e7dSPeter Dunlap 				 * should not kill the connection (although
1809a6d42e7dSPeter Dunlap 				 * we may want to drop the connection).
1810a6d42e7dSPeter Dunlap 				 */
1811a6d42e7dSPeter Dunlap 			} else {
1812a6d42e7dSPeter Dunlap 				/*
1813a6d42e7dSPeter Dunlap 				 * Not data PDUs so allocate a buffer for the
1814a6d42e7dSPeter Dunlap 				 * data segment and read the remaining data.
1815a6d42e7dSPeter Dunlap 				 */
1816a6d42e7dSPeter Dunlap 				rc = idm_sorecv_nonscsidata(ic, pdu);
1817a6d42e7dSPeter Dunlap 			}
1818a6d42e7dSPeter Dunlap 			if (rc != 0) {
1819a6d42e7dSPeter Dunlap 				/*
1820a6d42e7dSPeter Dunlap 				 * Call idm_pdu_complete so that we call the
1821a6d42e7dSPeter Dunlap 				 * callback and ensure any memory allocated
1822a6d42e7dSPeter Dunlap 				 * in idm_sorecvhdr gets freed up.
1823a6d42e7dSPeter Dunlap 				 */
1824a6d42e7dSPeter Dunlap 				idm_pdu_complete(pdu, IDM_STATUS_FAIL);
1825a6d42e7dSPeter Dunlap 
1826a6d42e7dSPeter Dunlap 				/*
1827a6d42e7dSPeter Dunlap 				 * If ic_rx_thread_running is still set then
1828a6d42e7dSPeter Dunlap 				 * this is some kind of connection problem
1829a6d42e7dSPeter Dunlap 				 * on the socket.  In this case we want to
1830a6d42e7dSPeter Dunlap 				 * generate an event.  Otherwise some other
1831a6d42e7dSPeter Dunlap 				 * thread closed the socket due to another
1832a6d42e7dSPeter Dunlap 				 * issue in which case we don't need to
1833a6d42e7dSPeter Dunlap 				 * generate an event.
1834a6d42e7dSPeter Dunlap 				 */
1835a6d42e7dSPeter Dunlap 				mutex_enter(&ic->ic_mutex);
1836a6d42e7dSPeter Dunlap 				if (so_conn->ic_rx_thread_running) {
1837a6d42e7dSPeter Dunlap 					conn_failure = B_TRUE;
1838a6d42e7dSPeter Dunlap 					so_conn->ic_rx_thread_running = B_FALSE;
1839a6d42e7dSPeter Dunlap 				}
1840a6d42e7dSPeter Dunlap 				continue;
1841a6d42e7dSPeter Dunlap 			}
1842a6d42e7dSPeter Dunlap 		}
1843a6d42e7dSPeter Dunlap 
1844a6d42e7dSPeter Dunlap 		/*
1845a6d42e7dSPeter Dunlap 		 * Process RX PDU
1846a6d42e7dSPeter Dunlap 		 */
1847a6d42e7dSPeter Dunlap 		idm_pdu_rx(ic, pdu);
1848a6d42e7dSPeter Dunlap 
1849a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_mutex);
1850a6d42e7dSPeter Dunlap 	}
1851a6d42e7dSPeter Dunlap 
1852a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
1853a6d42e7dSPeter Dunlap 
1854a6d42e7dSPeter Dunlap 	/*
1855a6d42e7dSPeter Dunlap 	 * If we dropped out of the RX processing loop because of
1856a6d42e7dSPeter Dunlap 	 * a socket problem or other connection failure (including
1857a6d42e7dSPeter Dunlap 	 * digest errors) then we need to generate a state machine
1858a6d42e7dSPeter Dunlap 	 * event to shut the connection down.
1859a6d42e7dSPeter Dunlap 	 * If the state machine is already in, for example, INIT_ERROR, this
1860a6d42e7dSPeter Dunlap 	 * event will get dropped, and the TX thread will never be notified
1861a6d42e7dSPeter Dunlap 	 * to shut down.  To be safe, we'll just notify it here.
1862a6d42e7dSPeter Dunlap 	 */
1863a6d42e7dSPeter Dunlap 	if (conn_failure) {
1864a6d42e7dSPeter Dunlap 		if (so_conn->ic_tx_thread_running) {
1865a6d42e7dSPeter Dunlap 			so_conn->ic_tx_thread_running = B_FALSE;
1866a6d42e7dSPeter Dunlap 			mutex_enter(&so_conn->ic_tx_mutex);
1867a6d42e7dSPeter Dunlap 			cv_signal(&so_conn->ic_tx_cv);
1868a6d42e7dSPeter Dunlap 			mutex_exit(&so_conn->ic_tx_mutex);
1869a6d42e7dSPeter Dunlap 		}
1870a6d42e7dSPeter Dunlap 
1871a6d42e7dSPeter Dunlap 		idm_conn_event(ic, CE_TRANSPORT_FAIL, rc);
1872a6d42e7dSPeter Dunlap 	}
1873a6d42e7dSPeter Dunlap 
1874a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
1875a6d42e7dSPeter Dunlap 
1876a6d42e7dSPeter Dunlap 	thread_exit();
1877a6d42e7dSPeter Dunlap }
1878a6d42e7dSPeter Dunlap 
1879a6d42e7dSPeter Dunlap /*
1880a6d42e7dSPeter Dunlap  * idm_so_tx
1881a6d42e7dSPeter Dunlap  *
1882a6d42e7dSPeter Dunlap  * This is the implementation of idm_transport_ops_t's it_tx_pdu entry
1883a6d42e7dSPeter Dunlap  * point.  By definition, it is supposed to be fast.  So, simply queue
1884a6d42e7dSPeter Dunlap  * the entry and return.  The real work is done by idm_i_so_tx() via
1885a6d42e7dSPeter Dunlap  * idm_sotx_thread().
1886a6d42e7dSPeter Dunlap  */
1887a6d42e7dSPeter Dunlap 
1888a6d42e7dSPeter Dunlap static void
1889a6d42e7dSPeter Dunlap idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu)
1890a6d42e7dSPeter Dunlap {
1891a6d42e7dSPeter Dunlap 	idm_so_conn_t *so_conn = ic->ic_transport_private;
1892a6d42e7dSPeter Dunlap 
1893a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_ic == ic);
1894a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
1895a6d42e7dSPeter Dunlap 
1896a6d42e7dSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
1897a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
1898a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, IDM_STATUS_ABORTED);
1899a6d42e7dSPeter Dunlap 		return;
1900a6d42e7dSPeter Dunlap 	}
1901a6d42e7dSPeter Dunlap 
1902a6d42e7dSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)pdu);
1903a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
1904a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
1905a6d42e7dSPeter Dunlap }
1906a6d42e7dSPeter Dunlap 
1907a6d42e7dSPeter Dunlap static idm_status_t
1908a6d42e7dSPeter Dunlap idm_i_so_tx(idm_pdu_t *pdu)
1909a6d42e7dSPeter Dunlap {
1910a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = pdu->isp_ic;
1911a6d42e7dSPeter Dunlap 	idm_status_t	status = IDM_STATUS_SUCCESS;
1912a6d42e7dSPeter Dunlap 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
1913a6d42e7dSPeter Dunlap 	int		pad_len;
1914a6d42e7dSPeter Dunlap 	uint32_t	hdr_digest_crc;
1915a6d42e7dSPeter Dunlap 	uint32_t	data_digest_crc = 0;
1916a6d42e7dSPeter Dunlap 	int		total_len = 0;
1917a6d42e7dSPeter Dunlap 	int		iovlen = 0;
1918a6d42e7dSPeter Dunlap 	struct iovec	iov[6];
1919a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1920a6d42e7dSPeter Dunlap 
1921a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1922a6d42e7dSPeter Dunlap 
1923a6d42e7dSPeter Dunlap 	/* Setup BHS */
1924a6d42e7dSPeter Dunlap 	iov[iovlen].iov_base	= (caddr_t)pdu->isp_hdr;
1925a6d42e7dSPeter Dunlap 	iov[iovlen].iov_len	= pdu->isp_hdrlen;
1926a6d42e7dSPeter Dunlap 	total_len		+= iov[iovlen].iov_len;
1927a6d42e7dSPeter Dunlap 	iovlen++;
1928a6d42e7dSPeter Dunlap 
1929a6d42e7dSPeter Dunlap 	/* Setup header digest */
1930a6d42e7dSPeter Dunlap 	if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
1931a6d42e7dSPeter Dunlap 	    (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) {
1932a6d42e7dSPeter Dunlap 		hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen);
1933a6d42e7dSPeter Dunlap 
1934a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base	= (caddr_t)&hdr_digest_crc;
1935a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len	= sizeof (hdr_digest_crc);
1936a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
1937a6d42e7dSPeter Dunlap 		iovlen++;
1938a6d42e7dSPeter Dunlap 	}
1939a6d42e7dSPeter Dunlap 
1940a6d42e7dSPeter Dunlap 	/* Setup the data */
1941a6d42e7dSPeter Dunlap 	if (pdu->isp_datalen) {
1942a6d42e7dSPeter Dunlap 		idm_task_t		*idt;
1943a6d42e7dSPeter Dunlap 		idm_buf_t		*idb;
1944a6d42e7dSPeter Dunlap 		iscsi_data_hdr_t	*ihp;
1945a6d42e7dSPeter Dunlap 		ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
1946a6d42e7dSPeter Dunlap 		/* Write of immediate data */
1947a6d42e7dSPeter Dunlap 		if (ic->ic_ffp &&
1948a6d42e7dSPeter Dunlap 		    (ihp->opcode == ISCSI_OP_SCSI_CMD ||
1949a6d42e7dSPeter Dunlap 		    ihp->opcode == ISCSI_OP_SCSI_DATA)) {
1950a6d42e7dSPeter Dunlap 			idt = idm_task_find(ic, ihp->itt, ihp->ttt);
1951a6d42e7dSPeter Dunlap 			if (idt) {
1952a6d42e7dSPeter Dunlap 				mutex_enter(&idt->idt_mutex);
1953a6d42e7dSPeter Dunlap 				idb = idm_buf_find(&idt->idt_outbufv, 0);
1954a6d42e7dSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
195530e7468fSPeter Dunlap 				/*
195630e7468fSPeter Dunlap 				 * If the initiator call to idm_buf_alloc
195730e7468fSPeter Dunlap 				 * failed then we can get to this point
195830e7468fSPeter Dunlap 				 * without a bound buffer.  The associated
195930e7468fSPeter Dunlap 				 * connection failure will clean things up
196030e7468fSPeter Dunlap 				 * later.  It would be nice to come up with
196130e7468fSPeter Dunlap 				 * a cleaner way to handle this.  In
196230e7468fSPeter Dunlap 				 * particular it seems absurd to look up
196330e7468fSPeter Dunlap 				 * the task and the buffer just to update
196430e7468fSPeter Dunlap 				 * this counter.
196530e7468fSPeter Dunlap 				 */
196630e7468fSPeter Dunlap 				if (idb)
196730e7468fSPeter Dunlap 					idb->idb_xfer_len += pdu->isp_datalen;
196830e7468fSPeter Dunlap 				idm_task_rele(idt);
1969a6d42e7dSPeter Dunlap 			}
1970a6d42e7dSPeter Dunlap 		}
1971a6d42e7dSPeter Dunlap 
1972a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)pdu->isp_data;
1973a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len  = pdu->isp_datalen;
1974a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
1975a6d42e7dSPeter Dunlap 		iovlen++;
1976a6d42e7dSPeter Dunlap 	}
1977a6d42e7dSPeter Dunlap 
1978a6d42e7dSPeter Dunlap 	/* Setup the data pad if necessary */
1979a6d42e7dSPeter Dunlap 	pad_len = ((ISCSI_PAD_WORD_LEN -
1980a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
1981a6d42e7dSPeter Dunlap 	    (ISCSI_PAD_WORD_LEN - 1));
1982a6d42e7dSPeter Dunlap 
1983a6d42e7dSPeter Dunlap 	if (pad_len) {
1984a6d42e7dSPeter Dunlap 		bzero(pad, sizeof (pad));
1985a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (void *)&pad;
1986a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len  = pad_len;
1987a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
1988a6d42e7dSPeter Dunlap 		iovlen++;
1989a6d42e7dSPeter Dunlap 	}
1990a6d42e7dSPeter Dunlap 
1991a6d42e7dSPeter Dunlap 	/*
1992a6d42e7dSPeter Dunlap 	 * Setup the data digest if enabled.  Data-digest is not sent
1993a6d42e7dSPeter Dunlap 	 * for login-phase PDUs.
1994a6d42e7dSPeter Dunlap 	 */
1995a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) &&
1996a6d42e7dSPeter Dunlap 	    ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
1997a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen || pad_len)) {
1998a6d42e7dSPeter Dunlap 		/*
1999a6d42e7dSPeter Dunlap 		 * RFC3720/10.2.3: A zero-length Data Segment also
2000a6d42e7dSPeter Dunlap 		 * implies a zero-length data digest.
2001a6d42e7dSPeter Dunlap 		 */
2002a6d42e7dSPeter Dunlap 		if (pdu->isp_datalen) {
2003a6d42e7dSPeter Dunlap 			data_digest_crc = idm_crc32c(pdu->isp_data,
2004a6d42e7dSPeter Dunlap 			    pdu->isp_datalen);
2005a6d42e7dSPeter Dunlap 		}
2006a6d42e7dSPeter Dunlap 		if (pad_len) {
2007a6d42e7dSPeter Dunlap 			data_digest_crc = idm_crc32c_continued(&pad,
2008a6d42e7dSPeter Dunlap 			    pad_len, data_digest_crc);
2009a6d42e7dSPeter Dunlap 		}
2010a6d42e7dSPeter Dunlap 
2011a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base	= (caddr_t)&data_digest_crc;
2012a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len	= sizeof (data_digest_crc);
2013a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
2014a6d42e7dSPeter Dunlap 		iovlen++;
2015a6d42e7dSPeter Dunlap 	}
2016a6d42e7dSPeter Dunlap 
2017a6d42e7dSPeter Dunlap 	/* Transmit the PDU */
2018a6d42e7dSPeter Dunlap 	if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen,
2019a6d42e7dSPeter Dunlap 	    total_len) != 0) {
2020a6d42e7dSPeter Dunlap 		/* Set error status */
2021a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
2022a6d42e7dSPeter Dunlap 		    "idm_so_tx: failed to transmit the PDU, so: %p ic: %p "
2023a6d42e7dSPeter Dunlap 		    "data: %p", (void *) so_conn->ic_so, (void *) ic,
2024a6d42e7dSPeter Dunlap 		    (void *) pdu->isp_data);
2025a6d42e7dSPeter Dunlap 		status = IDM_STATUS_IO;
2026a6d42e7dSPeter Dunlap 	}
2027a6d42e7dSPeter Dunlap 
2028a6d42e7dSPeter Dunlap 	/*
2029a6d42e7dSPeter Dunlap 	 * Success does not mean that the PDU actually reached the
2030a6d42e7dSPeter Dunlap 	 * remote node since it could get dropped along the way.
2031a6d42e7dSPeter Dunlap 	 */
2032a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, status);
2033a6d42e7dSPeter Dunlap 
2034a6d42e7dSPeter Dunlap 	return (status);
2035a6d42e7dSPeter Dunlap }
2036a6d42e7dSPeter Dunlap 
2037a6d42e7dSPeter Dunlap /*
2038a6d42e7dSPeter Dunlap  * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the
2039a6d42e7dSPeter Dunlap  * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength,
2040a6d42e7dSPeter Dunlap  * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN.
2041a6d42e7dSPeter Dunlap  * A target can invoke this function multiple times for a single read command
2042a6d42e7dSPeter Dunlap  * (identified by the same ITT) to split the input into several sequences.
2043a6d42e7dSPeter Dunlap  *
2044a6d42e7dSPeter Dunlap  * DataSN starts with 0 for the first data PDU of an input command and advances
2045a6d42e7dSPeter Dunlap  * by 1 for each subsequent data PDU. Each sequence will have its own F bit,
2046a6d42e7dSPeter Dunlap  * which is set to 1 for the last data PDU of a sequence.
2047a6d42e7dSPeter Dunlap  *
2048a6d42e7dSPeter Dunlap  * Scope for Prototype build:
2049a6d42e7dSPeter Dunlap  * The data PDUs within a sequence will be sent in order with the buffer offset
2050a6d42e7dSPeter Dunlap  * in increasing order. i.e. initiator and target must have negotiated the
2051a6d42e7dSPeter Dunlap  * "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
2052a6d42e7dSPeter Dunlap  *
2053a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex
2054a6d42e7dSPeter Dunlap  */
2055a6d42e7dSPeter Dunlap static idm_status_t
2056a6d42e7dSPeter Dunlap idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb)
2057a6d42e7dSPeter Dunlap {
2058a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn = idb->idb_ic->ic_transport_private;
2059a6d42e7dSPeter Dunlap 	idm_pdu_t	tmppdu;
2060a6d42e7dSPeter Dunlap 
2061a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2062a6d42e7dSPeter Dunlap 
2063a6d42e7dSPeter Dunlap 	/*
2064a6d42e7dSPeter Dunlap 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
2065a6d42e7dSPeter Dunlap 	 * idm_sotx_thread.
2066a6d42e7dSPeter Dunlap 	 */
2067a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
2068a6d42e7dSPeter Dunlap 
2069a6d42e7dSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
2070a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
2071a6d42e7dSPeter Dunlap 		/*
2072a6d42e7dSPeter Dunlap 		 * Don't release idt->idt_mutex since we're supposed to hold
2073a6d42e7dSPeter Dunlap 		 * in when calling idm_buf_tx_to_ini_done
2074a6d42e7dSPeter Dunlap 		 */
2075a6d42e7dSPeter Dunlap 		idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
2076a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
2077a6d42e7dSPeter Dunlap 	}
2078a6d42e7dSPeter Dunlap 
2079a6d42e7dSPeter Dunlap 	/*
2080a6d42e7dSPeter Dunlap 	 * Build a template for the data PDU headers we will use so that
2081a6d42e7dSPeter Dunlap 	 * the SN values will stay consistent with other PDU's we are
2082a6d42e7dSPeter Dunlap 	 * transmitting like R2T and SCSI status.
2083a6d42e7dSPeter Dunlap 	 */
2084a6d42e7dSPeter Dunlap 	bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
2085a6d42e7dSPeter Dunlap 	tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl;
2086a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
2087a6d42e7dSPeter Dunlap 	    ISCSI_OP_SCSI_DATA_RSP);
2088a6d42e7dSPeter Dunlap 	idb->idb_tx_thread = B_TRUE;
2089a6d42e7dSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)idb);
2090a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
2091a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
2092a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
2093a6d42e7dSPeter Dunlap 
2094a6d42e7dSPeter Dunlap 	/*
2095a6d42e7dSPeter Dunlap 	 * Returning success here indicates the transfer was successfully
2096a6d42e7dSPeter Dunlap 	 * dispatched -- it does not mean that the transfer completed
2097a6d42e7dSPeter Dunlap 	 * successfully.
2098a6d42e7dSPeter Dunlap 	 */
2099a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2100a6d42e7dSPeter Dunlap }
2101a6d42e7dSPeter Dunlap 
2102a6d42e7dSPeter Dunlap /*
2103a6d42e7dSPeter Dunlap  * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the
2104a6d42e7dSPeter Dunlap  * data blocks it is ready to receive from the initiator in response to a WRITE
2105a6d42e7dSPeter Dunlap  * SCSI command. The target iSCSI layer passes the information about the desired
2106a6d42e7dSPeter Dunlap  * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer
2107a6d42e7dSPeter Dunlap  * offset and datalen are passed via the 'idb' argument.
2108a6d42e7dSPeter Dunlap  *
2109a6d42e7dSPeter Dunlap  * Scope for Prototype build:
2110a6d42e7dSPeter Dunlap  * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have
2111a6d42e7dSPeter Dunlap  * negotiated the "InitialR2T" to "Yes".
2112a6d42e7dSPeter Dunlap  *
2113a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex
2114a6d42e7dSPeter Dunlap  */
2115a6d42e7dSPeter Dunlap static idm_status_t
2116a6d42e7dSPeter Dunlap idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
2117a6d42e7dSPeter Dunlap {
2118a6d42e7dSPeter Dunlap 	idm_pdu_t		*pdu;
2119a6d42e7dSPeter Dunlap 	iscsi_rtt_hdr_t		*rtt;
2120a6d42e7dSPeter Dunlap 
2121a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2122a6d42e7dSPeter Dunlap 
2123a6d42e7dSPeter Dunlap 	pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
2124a6d42e7dSPeter Dunlap 	pdu->isp_ic = idt->idt_ic;
2125a6d42e7dSPeter Dunlap 	bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
2126a6d42e7dSPeter Dunlap 
2127a6d42e7dSPeter Dunlap 	/* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */
2128a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP);
2129a6d42e7dSPeter Dunlap 
2130a6d42e7dSPeter Dunlap 	/* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */
2131a6d42e7dSPeter Dunlap 	rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr);
2132a6d42e7dSPeter Dunlap 
2133a6d42e7dSPeter Dunlap 	rtt->opcode		= ISCSI_OP_RTT_RSP;
2134a6d42e7dSPeter Dunlap 	rtt->flags		= ISCSI_FLAG_FINAL;
2135a6d42e7dSPeter Dunlap 	rtt->data_offset	= htonl(idb->idb_bufoffset);
2136a6d42e7dSPeter Dunlap 	rtt->data_length	= htonl(idb->idb_xfer_len);
2137a6d42e7dSPeter Dunlap 	rtt->rttsn		= htonl(idt->idt_exp_rttsn++);
2138a6d42e7dSPeter Dunlap 
2139a6d42e7dSPeter Dunlap 	/* Keep track of buffer offsets */
2140a6d42e7dSPeter Dunlap 	idb->idb_exp_offset	= idb->idb_bufoffset;
2141a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
2142a6d42e7dSPeter Dunlap 
2143a6d42e7dSPeter Dunlap 	/*
214463528ae4SJames Moore 	 * Transmit the PDU.
2145a6d42e7dSPeter Dunlap 	 */
214663528ae4SJames Moore 	idm_pdu_tx(pdu);
2147a6d42e7dSPeter Dunlap 
2148a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2149a6d42e7dSPeter Dunlap }
2150a6d42e7dSPeter Dunlap 
2151a6d42e7dSPeter Dunlap static idm_status_t
2152a6d42e7dSPeter Dunlap idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen)
2153a6d42e7dSPeter Dunlap {
2154*cf8c0ebaSPeter Dunlap 	if ((buflen > IDM_SO_BUF_CACHE_LB) && (buflen <= IDM_SO_BUF_CACHE_UB)) {
2155*cf8c0ebaSPeter Dunlap 		idb->idb_buf = kmem_cache_alloc(idm.idm_so_128k_buf_cache,
2156*cf8c0ebaSPeter Dunlap 		    KM_NOSLEEP);
2157*cf8c0ebaSPeter Dunlap 		idb->idb_buf_private = idm.idm_so_128k_buf_cache;
2158*cf8c0ebaSPeter Dunlap 	} else {
2159*cf8c0ebaSPeter Dunlap 		idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP);
2160*cf8c0ebaSPeter Dunlap 		idb->idb_buf_private = NULL;
2161*cf8c0ebaSPeter Dunlap 	}
2162*cf8c0ebaSPeter Dunlap 
2163a6d42e7dSPeter Dunlap 	if (idb->idb_buf == NULL) {
2164a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_NOTE,
2165a6d42e7dSPeter Dunlap 		    "idm_so_buf_alloc: failed buffer allocation");
2166a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
2167a6d42e7dSPeter Dunlap 	}
2168*cf8c0ebaSPeter Dunlap 
2169a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2170a6d42e7dSPeter Dunlap }
2171a6d42e7dSPeter Dunlap 
2172a6d42e7dSPeter Dunlap /* ARGSUSED */
2173a6d42e7dSPeter Dunlap static idm_status_t
2174a6d42e7dSPeter Dunlap idm_so_buf_setup(idm_buf_t *idb)
2175a6d42e7dSPeter Dunlap {
217630e7468fSPeter Dunlap 	/* Ensure bufalloc'd flag is unset */
217730e7468fSPeter Dunlap 	idb->idb_bufalloc = B_FALSE;
217830e7468fSPeter Dunlap 
2179a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2180a6d42e7dSPeter Dunlap }
2181a6d42e7dSPeter Dunlap 
2182a6d42e7dSPeter Dunlap /* ARGSUSED */
2183a6d42e7dSPeter Dunlap static void
2184a6d42e7dSPeter Dunlap idm_so_buf_teardown(idm_buf_t *idb)
2185a6d42e7dSPeter Dunlap {
2186a6d42e7dSPeter Dunlap 	/* nothing to do here */
2187a6d42e7dSPeter Dunlap }
2188a6d42e7dSPeter Dunlap 
2189a6d42e7dSPeter Dunlap static void
2190a6d42e7dSPeter Dunlap idm_so_buf_free(idm_buf_t *idb)
2191a6d42e7dSPeter Dunlap {
2192*cf8c0ebaSPeter Dunlap 	if (idb->idb_buf_private == NULL) {
2193*cf8c0ebaSPeter Dunlap 		kmem_free(idb->idb_buf, idb->idb_buflen);
2194*cf8c0ebaSPeter Dunlap 	} else {
2195*cf8c0ebaSPeter Dunlap 		kmem_cache_free(idb->idb_buf_private, idb->idb_buf);
2196*cf8c0ebaSPeter Dunlap 	}
2197a6d42e7dSPeter Dunlap }
2198a6d42e7dSPeter Dunlap 
219930e7468fSPeter Dunlap static void
220030e7468fSPeter Dunlap idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb,
220130e7468fSPeter Dunlap     uint32_t offset, uint32_t length)
220230e7468fSPeter Dunlap {
220330e7468fSPeter Dunlap 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
220430e7468fSPeter Dunlap 	idm_pdu_t	tmppdu;
220530e7468fSPeter Dunlap 	idm_buf_t	*rtt_buf;
220630e7468fSPeter Dunlap 
220730e7468fSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
220830e7468fSPeter Dunlap 
220930e7468fSPeter Dunlap 	/*
221030e7468fSPeter Dunlap 	 * Allocate a buffer to represent the RTT transfer.  We could further
221130e7468fSPeter Dunlap 	 * optimize this by allocating the buffers internally from an rtt
221230e7468fSPeter Dunlap 	 * specific buffer cache since this is socket-specific code but for
221330e7468fSPeter Dunlap 	 * now we will keep it simple.
221430e7468fSPeter Dunlap 	 */
221530e7468fSPeter Dunlap 	rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length);
221630e7468fSPeter Dunlap 	if (rtt_buf == NULL) {
221730e7468fSPeter Dunlap 		/*
221830e7468fSPeter Dunlap 		 * If we're in FFP then the failure was likely a resource
221930e7468fSPeter Dunlap 		 * allocation issue and we should close the connection by
222030e7468fSPeter Dunlap 		 * sending a CE_TRANSPORT_FAIL event.
222130e7468fSPeter Dunlap 		 *
222230e7468fSPeter Dunlap 		 * If we're not in FFP then idm_buf_alloc will always
222330e7468fSPeter Dunlap 		 * fail and the state is transitioning to "complete" anyway
222430e7468fSPeter Dunlap 		 * so we won't bother to send an event.
222530e7468fSPeter Dunlap 		 */
222630e7468fSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
222730e7468fSPeter Dunlap 		if (ic->ic_ffp)
222830e7468fSPeter Dunlap 			idm_conn_event_locked(ic, CE_TRANSPORT_FAIL,
222930e7468fSPeter Dunlap 			    NULL, CT_NONE);
223030e7468fSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
223130e7468fSPeter Dunlap 		return;
223230e7468fSPeter Dunlap 	}
223330e7468fSPeter Dunlap 
223430e7468fSPeter Dunlap 	rtt_buf->idb_buf_cb = NULL;
223530e7468fSPeter Dunlap 	rtt_buf->idb_cb_arg = NULL;
223630e7468fSPeter Dunlap 	rtt_buf->idb_bufoffset = offset;
223730e7468fSPeter Dunlap 	rtt_buf->idb_xfer_len = length;
223830e7468fSPeter Dunlap 	rtt_buf->idb_ic = idt->idt_ic;
223930e7468fSPeter Dunlap 	rtt_buf->idb_task_binding = idt;
224030e7468fSPeter Dunlap 
224130e7468fSPeter Dunlap 	/*
224230e7468fSPeter Dunlap 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
224330e7468fSPeter Dunlap 	 * idm_sotx_thread.
224430e7468fSPeter Dunlap 	 */
224530e7468fSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
224630e7468fSPeter Dunlap 
224730e7468fSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
224830e7468fSPeter Dunlap 		idm_buf_free(rtt_buf);
224930e7468fSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
225030e7468fSPeter Dunlap 		return;
225130e7468fSPeter Dunlap 	}
225230e7468fSPeter Dunlap 
225330e7468fSPeter Dunlap 	/*
225430e7468fSPeter Dunlap 	 * This new buffer represents an additional reference on the task
225530e7468fSPeter Dunlap 	 */
225630e7468fSPeter Dunlap 	idm_task_hold(idt);
225730e7468fSPeter Dunlap 
225830e7468fSPeter Dunlap 	/*
225930e7468fSPeter Dunlap 	 * Build a template for the data PDU headers we will use so that
226030e7468fSPeter Dunlap 	 * the SN values will stay consistent with other PDU's we are
226130e7468fSPeter Dunlap 	 * transmitting like R2T and SCSI status.
226230e7468fSPeter Dunlap 	 */
226330e7468fSPeter Dunlap 	bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
226430e7468fSPeter Dunlap 	tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl;
226530e7468fSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
226630e7468fSPeter Dunlap 	    ISCSI_OP_SCSI_DATA);
226730e7468fSPeter Dunlap 	rtt_buf->idb_tx_thread = B_TRUE;
226830e7468fSPeter Dunlap 	rtt_buf->idb_in_transport = B_TRUE;
226930e7468fSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf);
227030e7468fSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
227130e7468fSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
227230e7468fSPeter Dunlap }
227330e7468fSPeter Dunlap 
227430e7468fSPeter Dunlap static void
227530e7468fSPeter Dunlap idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb)
227630e7468fSPeter Dunlap {
227730e7468fSPeter Dunlap 	/*
227830e7468fSPeter Dunlap 	 * Don't worry about status -- we assume any error handling
227930e7468fSPeter Dunlap 	 * is performed by the caller (idm_sotx_thread).
228030e7468fSPeter Dunlap 	 */
228130e7468fSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
228230e7468fSPeter Dunlap 	idm_task_rele(idt);
228330e7468fSPeter Dunlap 	idm_buf_free(idb);
228430e7468fSPeter Dunlap }
228530e7468fSPeter Dunlap 
228630e7468fSPeter Dunlap static idm_status_t
228730e7468fSPeter Dunlap idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
2288a6d42e7dSPeter Dunlap     uint32_t buf_region_offset, uint32_t buf_region_length)
2289a6d42e7dSPeter Dunlap {
2290a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
2291a6d42e7dSPeter Dunlap 	uint32_t		max_dataseglen;
2292a6d42e7dSPeter Dunlap 	size_t			remainder, chunk;
2293a6d42e7dSPeter Dunlap 	uint32_t		data_offset = buf_region_offset;
2294a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
2295a6d42e7dSPeter Dunlap 	idm_pdu_t		*pdu;
229630e7468fSPeter Dunlap 	idm_status_t		tx_status;
2297a6d42e7dSPeter Dunlap 
2298a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2299a6d42e7dSPeter Dunlap 
2300a6d42e7dSPeter Dunlap 	ic = idt->idt_ic;
2301a6d42e7dSPeter Dunlap 
2302a6d42e7dSPeter Dunlap 	max_dataseglen = 8192; /* Need value from login negotiation */
2303a6d42e7dSPeter Dunlap 	remainder = buf_region_length;
2304a6d42e7dSPeter Dunlap 
2305a6d42e7dSPeter Dunlap 	while (remainder) {
2306a6d42e7dSPeter Dunlap 		if (idt->idt_state != TASK_ACTIVE) {
2307a6d42e7dSPeter Dunlap 			ASSERT((idt->idt_state != TASK_IDLE) &&
2308a6d42e7dSPeter Dunlap 			    (idt->idt_state != TASK_COMPLETE));
2309a6d42e7dSPeter Dunlap 			return (IDM_STATUS_ABORTED);
2310a6d42e7dSPeter Dunlap 		}
2311a6d42e7dSPeter Dunlap 
2312a6d42e7dSPeter Dunlap 		/* check to see if we need to chunk the data */
2313a6d42e7dSPeter Dunlap 		if (remainder > max_dataseglen) {
2314a6d42e7dSPeter Dunlap 			chunk = max_dataseglen;
2315a6d42e7dSPeter Dunlap 		} else {
2316a6d42e7dSPeter Dunlap 			chunk = remainder;
2317a6d42e7dSPeter Dunlap 		}
2318a6d42e7dSPeter Dunlap 
2319a6d42e7dSPeter Dunlap 		/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
2320a6d42e7dSPeter Dunlap 		pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
2321a6d42e7dSPeter Dunlap 		pdu->isp_ic = ic;
2322a6d42e7dSPeter Dunlap 
2323a6d42e7dSPeter Dunlap 		/*
232430e7468fSPeter Dunlap 		 * We've already built a build a header template
2325a6d42e7dSPeter Dunlap 		 * to use during the transfer.  Use this template so that
2326a6d42e7dSPeter Dunlap 		 * the SN values stay consistent with any unrelated PDU's
2327a6d42e7dSPeter Dunlap 		 * being transmitted.
2328a6d42e7dSPeter Dunlap 		 */
232930e7468fSPeter Dunlap 		bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr,
233030e7468fSPeter Dunlap 		    sizeof (iscsi_hdr_t));
2331a6d42e7dSPeter Dunlap 
2332a6d42e7dSPeter Dunlap 		/*
2333a6d42e7dSPeter Dunlap 		 * Set DataSN, data offset, and flags in BHS
2334a6d42e7dSPeter Dunlap 		 * For the prototype build, A = 0, S = 0, U = 0
2335a6d42e7dSPeter Dunlap 		 */
2336a6d42e7dSPeter Dunlap 		bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr);
2337a6d42e7dSPeter Dunlap 
2338a6d42e7dSPeter Dunlap 		bhs->datasn		= htonl(idt->idt_exp_datasn++);
2339a6d42e7dSPeter Dunlap 
2340a6d42e7dSPeter Dunlap 		hton24(bhs->dlength, chunk);
2341a6d42e7dSPeter Dunlap 		bhs->offset = htonl(idb->idb_bufoffset + data_offset);
2342a6d42e7dSPeter Dunlap 
2343a6d42e7dSPeter Dunlap 		if (chunk == remainder) {
2344a6d42e7dSPeter Dunlap 			bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
2345a6d42e7dSPeter Dunlap 		}
2346a6d42e7dSPeter Dunlap 
2347a6d42e7dSPeter Dunlap 		/* setup data */
2348a6d42e7dSPeter Dunlap 		pdu->isp_data	=  (uint8_t *)idb->idb_buf + data_offset;
2349a6d42e7dSPeter Dunlap 		pdu->isp_datalen = (uint_t)chunk;
2350a6d42e7dSPeter Dunlap 		remainder	-= chunk;
2351a6d42e7dSPeter Dunlap 		data_offset	+= chunk;
2352a6d42e7dSPeter Dunlap 
2353a6d42e7dSPeter Dunlap 		/*
2354a6d42e7dSPeter Dunlap 		 * Now that we're done working with idt_exp_datasn,
2355a6d42e7dSPeter Dunlap 		 * idt->idt_state and idb->idb_bufoffset we can release
2356a6d42e7dSPeter Dunlap 		 * the task lock -- don't want to hold it across the
2357a6d42e7dSPeter Dunlap 		 * call to idm_i_so_tx since we could block.
2358a6d42e7dSPeter Dunlap 		 */
2359a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
2360a6d42e7dSPeter Dunlap 
2361a6d42e7dSPeter Dunlap 		/*
2362a6d42e7dSPeter Dunlap 		 * Transmit the PDU.  Call the internal routine directly
2363a6d42e7dSPeter Dunlap 		 * as there is already implicit ordering.
2364a6d42e7dSPeter Dunlap 		 */
236530e7468fSPeter Dunlap 		if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) {
236630e7468fSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
236730e7468fSPeter Dunlap 			return (tx_status);
236830e7468fSPeter Dunlap 		}
2369a6d42e7dSPeter Dunlap 
2370a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
237130e7468fSPeter Dunlap 		idt->idt_tx_bytes += chunk;
2372a6d42e7dSPeter Dunlap 	}
2373a6d42e7dSPeter Dunlap 
2374a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2375a6d42e7dSPeter Dunlap }
2376a6d42e7dSPeter Dunlap 
2377a6d42e7dSPeter Dunlap /*
2378a6d42e7dSPeter Dunlap  * TX PDU cache
2379a6d42e7dSPeter Dunlap  */
2380a6d42e7dSPeter Dunlap /* ARGSUSED */
2381a6d42e7dSPeter Dunlap int
2382a6d42e7dSPeter Dunlap idm_sotx_pdu_constructor(void *hdl, void *arg, int flags)
2383a6d42e7dSPeter Dunlap {
2384a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu = hdl;
2385a6d42e7dSPeter Dunlap 
2386a6d42e7dSPeter Dunlap 	bzero(pdu, sizeof (idm_pdu_t));
2387a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
2388a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
2389a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sotx_cache_pdu_cb;
2390a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
2391a6d42e7dSPeter Dunlap 	bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
2392a6d42e7dSPeter Dunlap 
2393a6d42e7dSPeter Dunlap 	return (0);
2394a6d42e7dSPeter Dunlap }
2395a6d42e7dSPeter Dunlap 
2396a6d42e7dSPeter Dunlap /* ARGSUSED */
2397a6d42e7dSPeter Dunlap void
2398a6d42e7dSPeter Dunlap idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2399a6d42e7dSPeter Dunlap {
2400a6d42e7dSPeter Dunlap 	/* reset values between use */
2401a6d42e7dSPeter Dunlap 	pdu->isp_datalen = 0;
2402a6d42e7dSPeter Dunlap 
2403a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_sotx_pdu_cache, pdu);
2404a6d42e7dSPeter Dunlap }
2405a6d42e7dSPeter Dunlap 
2406a6d42e7dSPeter Dunlap /*
2407a6d42e7dSPeter Dunlap  * RX PDU cache
2408a6d42e7dSPeter Dunlap  */
2409a6d42e7dSPeter Dunlap /* ARGSUSED */
2410a6d42e7dSPeter Dunlap int
2411a6d42e7dSPeter Dunlap idm_sorx_pdu_constructor(void *hdl, void *arg, int flags)
2412a6d42e7dSPeter Dunlap {
2413a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu = hdl;
2414a6d42e7dSPeter Dunlap 
2415a6d42e7dSPeter Dunlap 	bzero(pdu, sizeof (idm_pdu_t));
2416a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
2417a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
2418a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
2419a6d42e7dSPeter Dunlap 
2420a6d42e7dSPeter Dunlap 	return (0);
2421a6d42e7dSPeter Dunlap }
2422a6d42e7dSPeter Dunlap 
2423a6d42e7dSPeter Dunlap /* ARGSUSED */
2424a6d42e7dSPeter Dunlap static void
2425a6d42e7dSPeter Dunlap idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2426a6d42e7dSPeter Dunlap {
2427a6d42e7dSPeter Dunlap 	pdu->isp_iovlen = 0;
2428a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = 0;
2429a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_sorx_pdu_cache, pdu);
2430a6d42e7dSPeter Dunlap }
2431a6d42e7dSPeter Dunlap 
2432a6d42e7dSPeter Dunlap static void
2433a6d42e7dSPeter Dunlap idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2434a6d42e7dSPeter Dunlap {
2435a6d42e7dSPeter Dunlap 	/*
2436a6d42e7dSPeter Dunlap 	 * We had to modify our cached RX PDU with a longer header buffer
2437a6d42e7dSPeter Dunlap 	 * and/or a longer data buffer.  Release the new buffers and fix
2438a6d42e7dSPeter Dunlap 	 * the fields back to what we would expect for a cached RX PDU.
2439a6d42e7dSPeter Dunlap 	 */
2440a6d42e7dSPeter Dunlap 	if (pdu->isp_flags & IDM_PDU_ADDL_HDR) {
2441a6d42e7dSPeter Dunlap 		kmem_free(pdu->isp_hdr, pdu->isp_hdrlen);
2442a6d42e7dSPeter Dunlap 	}
2443a6d42e7dSPeter Dunlap 	if (pdu->isp_flags & IDM_PDU_ADDL_DATA) {
2444a6d42e7dSPeter Dunlap 		kmem_free(pdu->isp_data, pdu->isp_datalen);
2445a6d42e7dSPeter Dunlap 	}
2446a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1);
2447a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
2448a6d42e7dSPeter Dunlap 	pdu->isp_data = NULL;
2449a6d42e7dSPeter Dunlap 	pdu->isp_datalen = 0;
2450a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = 0;
2451a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
2452a6d42e7dSPeter Dunlap 	idm_sorx_cache_pdu_cb(pdu, status);
2453a6d42e7dSPeter Dunlap }
2454a6d42e7dSPeter Dunlap 
2455a6d42e7dSPeter Dunlap /*
2456a6d42e7dSPeter Dunlap  * This thread is only active when I/O is queued for transmit
2457a6d42e7dSPeter Dunlap  * because the socket is busy.
2458a6d42e7dSPeter Dunlap  */
2459a6d42e7dSPeter Dunlap void
2460a6d42e7dSPeter Dunlap idm_sotx_thread(void *arg)
2461a6d42e7dSPeter Dunlap {
2462a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = arg;
2463a6d42e7dSPeter Dunlap 	idm_tx_obj_t	*object, *next;
2464a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
2465a6d42e7dSPeter Dunlap 	idm_status_t	status = IDM_STATUS_SUCCESS;
2466a6d42e7dSPeter Dunlap 
2467a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
2468a6d42e7dSPeter Dunlap 
2469a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
2470a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
2471a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_running = B_TRUE;
2472a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did;
2473a6d42e7dSPeter Dunlap 	cv_signal(&ic->ic_cv);
2474a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
2475a6d42e7dSPeter Dunlap 
2476a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
2477a6d42e7dSPeter Dunlap 
2478a6d42e7dSPeter Dunlap 	while (so_conn->ic_tx_thread_running) {
2479a6d42e7dSPeter Dunlap 		while (list_is_empty(&so_conn->ic_tx_list)) {
2480a6d42e7dSPeter Dunlap 			DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic);
2481a6d42e7dSPeter Dunlap 			cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex);
2482a6d42e7dSPeter Dunlap 			DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic);
2483a6d42e7dSPeter Dunlap 
2484a6d42e7dSPeter Dunlap 			if (!so_conn->ic_tx_thread_running) {
2485a6d42e7dSPeter Dunlap 				goto tx_bail;
2486a6d42e7dSPeter Dunlap 			}
2487a6d42e7dSPeter Dunlap 		}
2488a6d42e7dSPeter Dunlap 
2489a6d42e7dSPeter Dunlap 		object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
2490a6d42e7dSPeter Dunlap 		list_remove(&so_conn->ic_tx_list, object);
2491a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
2492a6d42e7dSPeter Dunlap 
2493a6d42e7dSPeter Dunlap 		switch (object->idm_tx_obj_magic) {
2494a6d42e7dSPeter Dunlap 		case IDM_PDU_MAGIC:
2495a6d42e7dSPeter Dunlap 			DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
2496a6d42e7dSPeter Dunlap 			    idm_pdu_t *, (idm_pdu_t *)object);
2497a6d42e7dSPeter Dunlap 
2498a6d42e7dSPeter Dunlap 			status = idm_i_so_tx((idm_pdu_t *)object);
2499a6d42e7dSPeter Dunlap 			break;
2500a6d42e7dSPeter Dunlap 
2501a6d42e7dSPeter Dunlap 		case IDM_BUF_MAGIC: {
2502a6d42e7dSPeter Dunlap 			idm_buf_t *idb = (idm_buf_t *)object;
2503a6d42e7dSPeter Dunlap 			idm_task_t *idt = idb->idb_task_binding;
2504a6d42e7dSPeter Dunlap 
2505a6d42e7dSPeter Dunlap 			DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic,
2506a6d42e7dSPeter Dunlap 			    idm_buf_t *, idb);
2507a6d42e7dSPeter Dunlap 
2508a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
2509a6d42e7dSPeter Dunlap 			status = idm_so_send_buf_region(idt,
251030e7468fSPeter Dunlap 			    idb, 0, idb->idb_xfer_len);
2511a6d42e7dSPeter Dunlap 
2512a6d42e7dSPeter Dunlap 			/*
2513a6d42e7dSPeter Dunlap 			 * TX thread owns the buffer so we expect it to
2514a6d42e7dSPeter Dunlap 			 * be "in transport"
2515a6d42e7dSPeter Dunlap 			 */
2516a6d42e7dSPeter Dunlap 			ASSERT(idb->idb_in_transport);
251730e7468fSPeter Dunlap 			if (IDM_CONN_ISTGT(ic)) {
251830e7468fSPeter Dunlap 				/*
251930e7468fSPeter Dunlap 				 * idm_buf_tx_to_ini_done releases
252030e7468fSPeter Dunlap 				 * idt->idt_mutex
252130e7468fSPeter Dunlap 				 */
252230e7468fSPeter Dunlap 				idm_buf_tx_to_ini_done(idt, idb, status);
252330e7468fSPeter Dunlap 			} else {
252430e7468fSPeter Dunlap 				idm_so_send_rtt_data_done(idt, idb);
252530e7468fSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
252630e7468fSPeter Dunlap 			}
2527a6d42e7dSPeter Dunlap 			break;
2528a6d42e7dSPeter Dunlap 		}
2529a6d42e7dSPeter Dunlap 
2530a6d42e7dSPeter Dunlap 		default:
2531a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic "
2532a6d42e7dSPeter Dunlap 			    "(0x%08x)", object->idm_tx_obj_magic);
2533a6d42e7dSPeter Dunlap 			status = IDM_STATUS_FAIL;
2534a6d42e7dSPeter Dunlap 		}
2535a6d42e7dSPeter Dunlap 
2536a6d42e7dSPeter Dunlap 		mutex_enter(&so_conn->ic_tx_mutex);
2537a6d42e7dSPeter Dunlap 
2538a6d42e7dSPeter Dunlap 		if (status != IDM_STATUS_SUCCESS) {
2539a6d42e7dSPeter Dunlap 			so_conn->ic_tx_thread_running = B_FALSE;
2540a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_TRANSPORT_FAIL, status);
2541a6d42e7dSPeter Dunlap 		}
2542a6d42e7dSPeter Dunlap 	}
2543a6d42e7dSPeter Dunlap 
2544a6d42e7dSPeter Dunlap 	/*
2545a6d42e7dSPeter Dunlap 	 * Before we leave, we need to abort every item remaining in the
2546a6d42e7dSPeter Dunlap 	 * TX list.
2547a6d42e7dSPeter Dunlap 	 */
2548a6d42e7dSPeter Dunlap 
2549a6d42e7dSPeter Dunlap tx_bail:
2550a6d42e7dSPeter Dunlap 	object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
2551a6d42e7dSPeter Dunlap 
2552a6d42e7dSPeter Dunlap 	while (object != NULL) {
2553a6d42e7dSPeter Dunlap 		next = list_next(&so_conn->ic_tx_list, object);
2554a6d42e7dSPeter Dunlap 
2555a6d42e7dSPeter Dunlap 		list_remove(&so_conn->ic_tx_list, object);
2556a6d42e7dSPeter Dunlap 		switch (object->idm_tx_obj_magic) {
2557a6d42e7dSPeter Dunlap 		case IDM_PDU_MAGIC:
2558a6d42e7dSPeter Dunlap 			idm_pdu_complete((idm_pdu_t *)object,
2559a6d42e7dSPeter Dunlap 			    IDM_STATUS_ABORTED);
2560a6d42e7dSPeter Dunlap 			break;
2561a6d42e7dSPeter Dunlap 
2562a6d42e7dSPeter Dunlap 		case IDM_BUF_MAGIC: {
2563a6d42e7dSPeter Dunlap 			idm_buf_t *idb = (idm_buf_t *)object;
2564a6d42e7dSPeter Dunlap 			idm_task_t *idt = idb->idb_task_binding;
2565a6d42e7dSPeter Dunlap 			mutex_exit(&so_conn->ic_tx_mutex);
2566a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
2567a6d42e7dSPeter Dunlap 			/*
2568a6d42e7dSPeter Dunlap 			 * TX thread owns the buffer so we expect it to
2569a6d42e7dSPeter Dunlap 			 * be "in transport"
2570a6d42e7dSPeter Dunlap 			 */
2571a6d42e7dSPeter Dunlap 			ASSERT(idb->idb_in_transport);
257230e7468fSPeter Dunlap 			if (IDM_CONN_ISTGT(ic)) {
257330e7468fSPeter Dunlap 				/*
257430e7468fSPeter Dunlap 				 * idm_buf_tx_to_ini_done releases
257530e7468fSPeter Dunlap 				 * idt->idt_mutex
257630e7468fSPeter Dunlap 				 */
257730e7468fSPeter Dunlap 				idm_buf_tx_to_ini_done(idt, idb,
257830e7468fSPeter Dunlap 				    IDM_STATUS_ABORTED);
257930e7468fSPeter Dunlap 			} else {
258030e7468fSPeter Dunlap 				idm_so_send_rtt_data_done(idt, idb);
258130e7468fSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
258230e7468fSPeter Dunlap 			}
2583a6d42e7dSPeter Dunlap 			mutex_enter(&so_conn->ic_tx_mutex);
2584a6d42e7dSPeter Dunlap 			break;
2585a6d42e7dSPeter Dunlap 		}
2586a6d42e7dSPeter Dunlap 		default:
2587a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN,
2588a6d42e7dSPeter Dunlap 			    "idm_sotx_thread: Unexpected magic "
2589a6d42e7dSPeter Dunlap 			    "(0x%08x)", object->idm_tx_obj_magic);
2590a6d42e7dSPeter Dunlap 		}
2591a6d42e7dSPeter Dunlap 
2592a6d42e7dSPeter Dunlap 		object = next;
2593a6d42e7dSPeter Dunlap 	}
2594a6d42e7dSPeter Dunlap 
2595a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
2596a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
2597a6d42e7dSPeter Dunlap 	thread_exit();
2598a6d42e7dSPeter Dunlap 	/*NOTREACHED*/
2599a6d42e7dSPeter Dunlap }
2600