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