xref: /illumos-gate/usr/src/uts/common/io/idm/idm_so.c (revision 56261083)
1a6d42e7dSPeter Dunlap /*
2a6d42e7dSPeter Dunlap  * CDDL HEADER START
3a6d42e7dSPeter Dunlap  *
4a6d42e7dSPeter Dunlap  * The contents of this file are subject to the terms of the
5a6d42e7dSPeter Dunlap  * Common Development and Distribution License (the "License").
6a6d42e7dSPeter Dunlap  * You may not use this file except in compliance with the License.
7a6d42e7dSPeter Dunlap  *
8a6d42e7dSPeter Dunlap  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a6d42e7dSPeter Dunlap  * or http://www.opensolaris.org/os/licensing.
10a6d42e7dSPeter Dunlap  * See the License for the specific language governing permissions
11a6d42e7dSPeter Dunlap  * and limitations under the License.
12a6d42e7dSPeter Dunlap  *
13a6d42e7dSPeter Dunlap  * When distributing Covered Code, include this CDDL HEADER in each
14a6d42e7dSPeter Dunlap  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a6d42e7dSPeter Dunlap  * If applicable, add the following below this CDDL HEADER, with the
16a6d42e7dSPeter Dunlap  * fields enclosed by brackets "[]" replaced with your own identifying
17a6d42e7dSPeter Dunlap  * information: Portions Copyright [yyyy] [name of copyright owner]
18a6d42e7dSPeter Dunlap  *
19a6d42e7dSPeter Dunlap  * CDDL HEADER END
20a6d42e7dSPeter Dunlap  */
21a6d42e7dSPeter Dunlap /*
2263528ae4SJames Moore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23a6d42e7dSPeter Dunlap  * Use is subject to license terms.
24a6d42e7dSPeter Dunlap  */
25a6d42e7dSPeter Dunlap 
26a6d42e7dSPeter Dunlap #include <sys/conf.h>
27a6d42e7dSPeter Dunlap #include <sys/stat.h>
28a6d42e7dSPeter Dunlap #include <sys/file.h>
29a6d42e7dSPeter Dunlap #include <sys/ddi.h>
30a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
31a6d42e7dSPeter Dunlap #include <sys/modctl.h>
32a6d42e7dSPeter Dunlap #include <sys/priv.h>
33a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
34a6d42e7dSPeter Dunlap #include <sys/socket.h>
35a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
36a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
37a6d42e7dSPeter Dunlap #include <sys/sdt.h>
38a6d42e7dSPeter Dunlap #include <netinet/tcp.h>
39a6d42e7dSPeter Dunlap #include <inet/tcp.h>
40a6d42e7dSPeter Dunlap #include <sys/socketvar.h>
41a6d42e7dSPeter Dunlap #include <sys/pathname.h>
42a6d42e7dSPeter Dunlap #include <sys/fs/snode.h>
43a6d42e7dSPeter Dunlap #include <sys/fs/dv_node.h>
44a6d42e7dSPeter Dunlap #include <sys/vnode.h>
45a6d42e7dSPeter Dunlap #include <netinet/in.h>
46a6d42e7dSPeter Dunlap #include <net/if.h>
47a6d42e7dSPeter Dunlap #include <sys/sockio.h>
480f1702c5SYu Xiangning #include <sys/ksocket.h>
49*56261083SCharles Ting #include <sys/iscsi_protocol.h>
50a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
51a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
52a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h>
53a6d42e7dSPeter Dunlap 
54aff4bce5Syi zhang - Sun Microsystems - Beijing China #define	IN_PROGRESS_DELAY	1
55aff4bce5Syi zhang - Sun Microsystems - Beijing China 
56a6d42e7dSPeter Dunlap /*
57a6d42e7dSPeter Dunlap  * in6addr_any is currently all zeroes, but use the macro in case this
58a6d42e7dSPeter Dunlap  * ever changes.
59a6d42e7dSPeter Dunlap  */
60e42a0851Speter dunlap static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
61a6d42e7dSPeter Dunlap 
62a6d42e7dSPeter Dunlap static void idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
63a6d42e7dSPeter Dunlap static void idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
64a6d42e7dSPeter Dunlap static void idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
65a6d42e7dSPeter Dunlap 
660f1702c5SYu Xiangning static idm_status_t idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so);
67a6d42e7dSPeter Dunlap static void idm_so_conn_destroy_common(idm_conn_t *ic);
68a6d42e7dSPeter Dunlap static void idm_so_conn_connect_common(idm_conn_t *ic);
69a6d42e7dSPeter Dunlap 
70a6d42e7dSPeter Dunlap static void idm_set_ini_preconnect_options(idm_so_conn_t *sc);
71a6d42e7dSPeter Dunlap static void idm_set_ini_postconnect_options(idm_so_conn_t *sc);
720f1702c5SYu Xiangning static void idm_set_tgt_connect_options(ksocket_t so);
73a6d42e7dSPeter Dunlap static idm_status_t idm_i_so_tx(idm_pdu_t *pdu);
74a6d42e7dSPeter Dunlap 
75a6d42e7dSPeter Dunlap static idm_status_t idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu);
7630e7468fSPeter Dunlap static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt,
7730e7468fSPeter Dunlap     idm_buf_t *idb, uint32_t offset, uint32_t length);
7830e7468fSPeter Dunlap static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb);
7930e7468fSPeter Dunlap static idm_status_t idm_so_send_buf_region(idm_task_t *idt,
80a6d42e7dSPeter Dunlap     idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length);
81a6d42e7dSPeter Dunlap 
82a6d42e7dSPeter Dunlap static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb,
83a6d42e7dSPeter Dunlap     uint32_t ro, uint32_t dlength);
84a6d42e7dSPeter Dunlap 
85a6d42e7dSPeter Dunlap static idm_status_t idm_so_handle_digest(idm_conn_t *it,
86a6d42e7dSPeter Dunlap     nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx);
87a6d42e7dSPeter Dunlap 
88aff4bce5Syi zhang - Sun Microsystems - Beijing China static void idm_so_socket_set_nonblock(struct sonode *node);
89aff4bce5Syi zhang - Sun Microsystems - Beijing China static void idm_so_socket_set_block(struct sonode *node);
90aff4bce5Syi zhang - Sun Microsystems - Beijing China 
91a6d42e7dSPeter Dunlap /*
92a6d42e7dSPeter Dunlap  * Transport ops prototypes
93a6d42e7dSPeter Dunlap  */
94a6d42e7dSPeter Dunlap static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu);
95a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
96a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
97a6d42e7dSPeter Dunlap static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu);
98a6d42e7dSPeter Dunlap static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu);
99a6d42e7dSPeter Dunlap static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu);
100a6d42e7dSPeter Dunlap static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt);
101a6d42e7dSPeter Dunlap static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it,
102a6d42e7dSPeter Dunlap     nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
10330e7468fSPeter Dunlap static void idm_so_notice_key_values(idm_conn_t *it,
104a6d42e7dSPeter Dunlap     nvlist_t *negotiated_nvl);
105*56261083SCharles Ting static kv_status_t idm_so_declare_key_values(idm_conn_t *it,
106*56261083SCharles Ting     nvlist_t *config_nvl, nvlist_t *outgoing_nvl);
107a6d42e7dSPeter Dunlap static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic,
108a6d42e7dSPeter Dunlap     idm_transport_caps_t *caps);
109a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen);
110a6d42e7dSPeter Dunlap static void idm_so_buf_free(idm_buf_t *idb);
111a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_setup(idm_buf_t *idb);
112a6d42e7dSPeter Dunlap static void idm_so_buf_teardown(idm_buf_t *idb);
113a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is);
114a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_destroy(idm_svc_t *is);
115a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is);
116a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_offline(idm_svc_t *is);
117a6d42e7dSPeter Dunlap static void idm_so_tgt_conn_destroy(idm_conn_t *ic);
118a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic);
119a6d42e7dSPeter Dunlap static void idm_so_conn_disconnect(idm_conn_t *ic);
120a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic);
121a6d42e7dSPeter Dunlap static void idm_so_ini_conn_destroy(idm_conn_t *ic);
122a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic);
123a6d42e7dSPeter Dunlap 
124a6d42e7dSPeter Dunlap /*
125a6d42e7dSPeter Dunlap  * IDM Native Sockets transport operations
126a6d42e7dSPeter Dunlap  */
127a6d42e7dSPeter Dunlap static
128a6d42e7dSPeter Dunlap idm_transport_ops_t idm_so_transport_ops = {
129a6d42e7dSPeter Dunlap 	idm_so_tx,			/* it_tx_pdu */
130a6d42e7dSPeter Dunlap 	idm_so_buf_tx_to_ini,		/* it_buf_tx_to_ini */
131a6d42e7dSPeter Dunlap 	idm_so_buf_rx_from_ini,		/* it_buf_rx_from_ini */
132a6d42e7dSPeter Dunlap 	idm_so_rx_datain,		/* it_rx_datain */
133a6d42e7dSPeter Dunlap 	idm_so_rx_rtt,			/* it_rx_rtt */
134a6d42e7dSPeter Dunlap 	idm_so_rx_dataout,		/* it_rx_dataout */
135a6d42e7dSPeter Dunlap 	NULL,				/* it_alloc_conn_rsrc */
136a6d42e7dSPeter Dunlap 	NULL,				/* it_free_conn_rsrc */
137a6d42e7dSPeter Dunlap 	NULL,				/* it_tgt_enable_datamover */
138a6d42e7dSPeter Dunlap 	NULL,				/* it_ini_enable_datamover */
139a6d42e7dSPeter Dunlap 	NULL,				/* it_conn_terminate */
140a6d42e7dSPeter Dunlap 	idm_so_free_task_rsrc,		/* it_free_task_rsrc */
141a6d42e7dSPeter Dunlap 	idm_so_negotiate_key_values,	/* it_negotiate_key_values */
142a6d42e7dSPeter Dunlap 	idm_so_notice_key_values,	/* it_notice_key_values */
143a6d42e7dSPeter Dunlap 	idm_so_conn_is_capable,		/* it_conn_is_capable */
144a6d42e7dSPeter Dunlap 	idm_so_buf_alloc,		/* it_buf_alloc */
145a6d42e7dSPeter Dunlap 	idm_so_buf_free,		/* it_buf_free */
146a6d42e7dSPeter Dunlap 	idm_so_buf_setup,		/* it_buf_setup */
147a6d42e7dSPeter Dunlap 	idm_so_buf_teardown,		/* it_buf_teardown */
148a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_create,		/* it_tgt_svc_create */
149a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_destroy,		/* it_tgt_svc_destroy */
150a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_online,		/* it_tgt_svc_online */
151a6d42e7dSPeter Dunlap 	idm_so_tgt_svc_offline,		/* it_tgt_svc_offline */
152a6d42e7dSPeter Dunlap 	idm_so_tgt_conn_destroy,	/* it_tgt_conn_destroy */
153a6d42e7dSPeter Dunlap 	idm_so_tgt_conn_connect,	/* it_tgt_conn_connect */
154a6d42e7dSPeter Dunlap 	idm_so_conn_disconnect,		/* it_tgt_conn_disconnect */
155a6d42e7dSPeter Dunlap 	idm_so_ini_conn_create,		/* it_ini_conn_create */
156a6d42e7dSPeter Dunlap 	idm_so_ini_conn_destroy,	/* it_ini_conn_destroy */
157a6d42e7dSPeter Dunlap 	idm_so_ini_conn_connect,	/* it_ini_conn_connect */
158*56261083SCharles Ting 	idm_so_conn_disconnect,		/* it_ini_conn_disconnect */
159*56261083SCharles Ting 	idm_so_declare_key_values	/* it_declare_key_values */
160a6d42e7dSPeter Dunlap };
161a6d42e7dSPeter Dunlap 
162a6d42e7dSPeter Dunlap /*
163a6d42e7dSPeter Dunlap  * idm_so_init()
164a6d42e7dSPeter Dunlap  * Sockets transport initialization
165a6d42e7dSPeter Dunlap  */
166a6d42e7dSPeter Dunlap void
167a6d42e7dSPeter Dunlap idm_so_init(idm_transport_t *it)
168a6d42e7dSPeter Dunlap {
169a6d42e7dSPeter Dunlap 	/* Cache for IDM Data and R2T Transmit PDU's */
170a6d42e7dSPeter Dunlap 	idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache",
171a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
172a6d42e7dSPeter Dunlap 	    &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
173a6d42e7dSPeter Dunlap 
174a6d42e7dSPeter Dunlap 	/* Cache for IDM Receive PDU's */
175a6d42e7dSPeter Dunlap 	idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache",
176a6d42e7dSPeter Dunlap 	    sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
177a6d42e7dSPeter Dunlap 	    &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
178a6d42e7dSPeter Dunlap 
179cf8c0ebaSPeter Dunlap 	/* 128k buffer cache */
180cf8c0ebaSPeter Dunlap 	idm.idm_so_128k_buf_cache = kmem_cache_create("idm_128k_buf_cache",
181cf8c0ebaSPeter Dunlap 	    IDM_SO_BUF_CACHE_UB, 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
182cf8c0ebaSPeter Dunlap 
183a6d42e7dSPeter Dunlap 	/* Set the sockets transport ops */
184a6d42e7dSPeter Dunlap 	it->it_ops = &idm_so_transport_ops;
185a6d42e7dSPeter Dunlap }
186a6d42e7dSPeter Dunlap 
187a6d42e7dSPeter Dunlap /*
188a6d42e7dSPeter Dunlap  * idm_so_fini()
189a6d42e7dSPeter Dunlap  * Sockets transport teardown
190a6d42e7dSPeter Dunlap  */
191a6d42e7dSPeter Dunlap void
192a6d42e7dSPeter Dunlap idm_so_fini(void)
193a6d42e7dSPeter Dunlap {
194cf8c0ebaSPeter Dunlap 	kmem_cache_destroy(idm.idm_so_128k_buf_cache);
195a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_sotx_pdu_cache);
196a6d42e7dSPeter Dunlap 	kmem_cache_destroy(idm.idm_sorx_pdu_cache);
197a6d42e7dSPeter Dunlap }
198a6d42e7dSPeter Dunlap 
1990f1702c5SYu Xiangning ksocket_t
200a6d42e7dSPeter Dunlap idm_socreate(int domain, int type, int protocol)
201a6d42e7dSPeter Dunlap {
2020f1702c5SYu Xiangning 	ksocket_t ks;
203a6d42e7dSPeter Dunlap 
2040f1702c5SYu Xiangning 	if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP,
2050f1702c5SYu Xiangning 	    CRED())) {
2060f1702c5SYu Xiangning 		return (ks);
2070f1702c5SYu Xiangning 	} else {
2080f1702c5SYu Xiangning 		return (NULL);
209a6d42e7dSPeter Dunlap 	}
210a6d42e7dSPeter Dunlap }
211a6d42e7dSPeter Dunlap 
212a6d42e7dSPeter Dunlap /*
213a6d42e7dSPeter Dunlap  * idm_soshutdown will disconnect the socket and prevent subsequent PDU
214a6d42e7dSPeter Dunlap  * reception and transmission.  The sonode still exists but its state
215a6d42e7dSPeter Dunlap  * gets modified to indicate it is no longer connected.  Calls to
216a6d42e7dSPeter Dunlap  * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used
217a6d42e7dSPeter Dunlap  * regain control of a thread stuck in idm_sorecv.
218a6d42e7dSPeter Dunlap  */
219a6d42e7dSPeter Dunlap void
2200f1702c5SYu Xiangning idm_soshutdown(ksocket_t so)
221a6d42e7dSPeter Dunlap {
2220f1702c5SYu Xiangning 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
223a6d42e7dSPeter Dunlap }
224a6d42e7dSPeter Dunlap 
225a6d42e7dSPeter Dunlap /*
226a6d42e7dSPeter Dunlap  * idm_sodestroy releases all resources associated with a socket previously
227a6d42e7dSPeter Dunlap  * created with idm_socreate.  The socket must be shutdown using
228a6d42e7dSPeter Dunlap  * idm_soshutdown before the socket is destroyed with idm_sodestroy,
229a6d42e7dSPeter Dunlap  * otherwise undefined behavior will result.
230a6d42e7dSPeter Dunlap  */
231a6d42e7dSPeter Dunlap void
2320f1702c5SYu Xiangning idm_sodestroy(ksocket_t ks)
233a6d42e7dSPeter Dunlap {
2340f1702c5SYu Xiangning 	(void) ksocket_close(ks, CRED());
235a6d42e7dSPeter Dunlap }
236a6d42e7dSPeter Dunlap 
237e42a0851Speter dunlap /*
238e42a0851Speter dunlap  * Function to compare two addresses in sockaddr_storage format
239e42a0851Speter dunlap  */
240e42a0851Speter dunlap 
241e42a0851Speter dunlap int
242e42a0851Speter dunlap idm_ss_compare(const struct sockaddr_storage *cmp_ss1,
243e42a0851Speter dunlap     const struct sockaddr_storage *cmp_ss2,
244e42a0851Speter dunlap     boolean_t v4_mapped_as_v4)
245e42a0851Speter dunlap {
246e42a0851Speter dunlap 	struct sockaddr_storage			mapped_v4_ss1, mapped_v4_ss2;
247e42a0851Speter dunlap 	const struct sockaddr_storage		*ss1, *ss2;
248e42a0851Speter dunlap 	struct in_addr				*in1, *in2;
249e42a0851Speter dunlap 	struct in6_addr				*in61, *in62;
250e42a0851Speter dunlap 	int i;
251e42a0851Speter dunlap 
252e42a0851Speter dunlap 	/*
253e42a0851Speter dunlap 	 * Normalize V4-mapped IPv6 addresses into V4 format if
254e42a0851Speter dunlap 	 * v4_mapped_as_v4 is B_TRUE.
255e42a0851Speter dunlap 	 */
256e42a0851Speter dunlap 	ss1 = cmp_ss1;
257e42a0851Speter dunlap 	ss2 = cmp_ss2;
258e42a0851Speter dunlap 	if (v4_mapped_as_v4 && (ss1->ss_family == AF_INET6)) {
259e42a0851Speter dunlap 		in61 = &((struct sockaddr_in6 *)ss1)->sin6_addr;
260e42a0851Speter dunlap 		if (IN6_IS_ADDR_V4MAPPED(in61)) {
261e42a0851Speter dunlap 			bzero(&mapped_v4_ss1, sizeof (mapped_v4_ss1));
262e42a0851Speter dunlap 			mapped_v4_ss1.ss_family = AF_INET;
263e42a0851Speter dunlap 			((struct sockaddr_in *)&mapped_v4_ss1)->sin_port =
264e42a0851Speter dunlap 			    ((struct sockaddr_in *)ss1)->sin_port;
265e42a0851Speter dunlap 			IN6_V4MAPPED_TO_INADDR(in61,
266e42a0851Speter dunlap 			    &((struct sockaddr_in *)&mapped_v4_ss1)->sin_addr);
267e42a0851Speter dunlap 			ss1 = &mapped_v4_ss1;
268e42a0851Speter dunlap 		}
269e42a0851Speter dunlap 	}
270e42a0851Speter dunlap 	ss2 = cmp_ss2;
271e42a0851Speter dunlap 	if (v4_mapped_as_v4 && (ss2->ss_family == AF_INET6)) {
272e42a0851Speter dunlap 		in62 = &((struct sockaddr_in6 *)ss2)->sin6_addr;
273e42a0851Speter dunlap 		if (IN6_IS_ADDR_V4MAPPED(in62)) {
274e42a0851Speter dunlap 			bzero(&mapped_v4_ss2, sizeof (mapped_v4_ss2));
275e42a0851Speter dunlap 			mapped_v4_ss2.ss_family = AF_INET;
276e42a0851Speter dunlap 			((struct sockaddr_in *)&mapped_v4_ss2)->sin_port =
277e42a0851Speter dunlap 			    ((struct sockaddr_in *)ss2)->sin_port;
278e42a0851Speter dunlap 			IN6_V4MAPPED_TO_INADDR(in62,
279e42a0851Speter dunlap 			    &((struct sockaddr_in *)&mapped_v4_ss2)->sin_addr);
280e42a0851Speter dunlap 			ss2 = &mapped_v4_ss2;
281e42a0851Speter dunlap 		}
282e42a0851Speter dunlap 	}
283e42a0851Speter dunlap 
284e42a0851Speter dunlap 	/*
285e42a0851Speter dunlap 	 * Compare ports, then address family, then ip address
286e42a0851Speter dunlap 	 */
287e42a0851Speter dunlap 	if (((struct sockaddr_in *)ss1)->sin_port !=
288e42a0851Speter dunlap 	    ((struct sockaddr_in *)ss2)->sin_port) {
289e42a0851Speter dunlap 		if (((struct sockaddr_in *)ss1)->sin_port >
290e42a0851Speter dunlap 		    ((struct sockaddr_in *)ss2)->sin_port)
291e42a0851Speter dunlap 			return (1);
292e42a0851Speter dunlap 		else
293e42a0851Speter dunlap 			return (-1);
294e42a0851Speter dunlap 	}
295e42a0851Speter dunlap 
296e42a0851Speter dunlap 	/*
297e42a0851Speter dunlap 	 * ports are the same
298e42a0851Speter dunlap 	 */
299e42a0851Speter dunlap 	if (ss1->ss_family != ss2->ss_family) {
300e42a0851Speter dunlap 		if (ss1->ss_family == AF_INET)
301e42a0851Speter dunlap 			return (1);
302e42a0851Speter dunlap 		else
303e42a0851Speter dunlap 			return (-1);
304e42a0851Speter dunlap 	}
305e42a0851Speter dunlap 
306e42a0851Speter dunlap 	/*
307e42a0851Speter dunlap 	 * address families are the same
308e42a0851Speter dunlap 	 */
309e42a0851Speter dunlap 	if (ss1->ss_family == AF_INET) {
310e42a0851Speter dunlap 		in1 = &((struct sockaddr_in *)ss1)->sin_addr;
311e42a0851Speter dunlap 		in2 = &((struct sockaddr_in *)ss2)->sin_addr;
312e42a0851Speter dunlap 
313e42a0851Speter dunlap 		if (in1->s_addr > in2->s_addr)
314e42a0851Speter dunlap 			return (1);
315e42a0851Speter dunlap 		else if (in1->s_addr < in2->s_addr)
316e42a0851Speter dunlap 			return (-1);
317e42a0851Speter dunlap 		else
318e42a0851Speter dunlap 			return (0);
319e42a0851Speter dunlap 	} else if (ss1->ss_family == AF_INET6) {
320e42a0851Speter dunlap 		in61 = &((struct sockaddr_in6 *)ss1)->sin6_addr;
321e42a0851Speter dunlap 		in62 = &((struct sockaddr_in6 *)ss2)->sin6_addr;
322e42a0851Speter dunlap 
323e42a0851Speter dunlap 		for (i = 0; i < 4; i++) {
324e42a0851Speter dunlap 			if (in61->s6_addr32[i] > in62->s6_addr32[i])
325e42a0851Speter dunlap 				return (1);
326e42a0851Speter dunlap 			else if (in61->s6_addr32[i] < in62->s6_addr32[i])
327e42a0851Speter dunlap 				return (-1);
328e42a0851Speter dunlap 		}
329e42a0851Speter dunlap 		return (0);
330e42a0851Speter dunlap 	}
331e42a0851Speter dunlap 
332e42a0851Speter dunlap 	return (1);
333e42a0851Speter dunlap }
334e42a0851Speter dunlap 
335a6d42e7dSPeter Dunlap /*
336a6d42e7dSPeter Dunlap  * IP address filter functions to flag addresses that should not
337a6d42e7dSPeter Dunlap  * go out to initiators through discovery.
338a6d42e7dSPeter Dunlap  */
339a6d42e7dSPeter Dunlap static boolean_t
340a6d42e7dSPeter Dunlap idm_v4_addr_okay(struct in_addr *in_addr)
341a6d42e7dSPeter Dunlap {
342a6d42e7dSPeter Dunlap 	in_addr_t addr = ntohl(in_addr->s_addr);
343a6d42e7dSPeter Dunlap 
344a6d42e7dSPeter Dunlap 	if ((INADDR_NONE == addr) ||
345a6d42e7dSPeter Dunlap 	    (IN_MULTICAST(addr)) ||
346a6d42e7dSPeter Dunlap 	    ((addr >> IN_CLASSA_NSHIFT) == 0) ||
347a6d42e7dSPeter Dunlap 	    ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
348a6d42e7dSPeter Dunlap 		return (B_FALSE);
349a6d42e7dSPeter Dunlap 	}
350a6d42e7dSPeter Dunlap 	return (B_TRUE);
351a6d42e7dSPeter Dunlap }
352a6d42e7dSPeter Dunlap 
353a6d42e7dSPeter Dunlap static boolean_t
354a6d42e7dSPeter Dunlap idm_v6_addr_okay(struct in6_addr *addr6)
355a6d42e7dSPeter Dunlap {
356a6d42e7dSPeter Dunlap 
357a6d42e7dSPeter Dunlap 	if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) ||
358a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_LOOPBACK(addr6)) ||
359a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_MULTICAST(addr6)) ||
360a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_V4MAPPED(addr6)) ||
361a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_V4COMPAT(addr6)) ||
362a6d42e7dSPeter Dunlap 	    (IN6_IS_ADDR_LINKLOCAL(addr6))) {
363a6d42e7dSPeter Dunlap 		return (B_FALSE);
364a6d42e7dSPeter Dunlap 	}
365a6d42e7dSPeter Dunlap 	return (B_TRUE);
366a6d42e7dSPeter Dunlap }
367a6d42e7dSPeter Dunlap 
368a6d42e7dSPeter Dunlap /*
369a6d42e7dSPeter Dunlap  * idm_get_ipaddr will retrieve a list of IP Addresses which the host is
370a6d42e7dSPeter Dunlap  * configured with by sending down a sequence of kernel ioctl to IP STREAMS.
371a6d42e7dSPeter Dunlap  */
372a6d42e7dSPeter Dunlap int
373a6d42e7dSPeter Dunlap idm_get_ipaddr(idm_addr_list_t **ipaddr_p)
374a6d42e7dSPeter Dunlap {
3750f1702c5SYu Xiangning 	ksocket_t 		so4, so6;
376a6d42e7dSPeter Dunlap 	struct lifnum		lifn;
377a6d42e7dSPeter Dunlap 	struct lifconf		lifc;
378a6d42e7dSPeter Dunlap 	struct lifreq		*lp;
379a6d42e7dSPeter Dunlap 	int			rval;
380a6d42e7dSPeter Dunlap 	int			numifs;
381a6d42e7dSPeter Dunlap 	int			bufsize;
382a6d42e7dSPeter Dunlap 	void			*buf;
383a6d42e7dSPeter Dunlap 	int			i, j, n, rc;
384a6d42e7dSPeter Dunlap 	struct sockaddr_storage	ss;
385a6d42e7dSPeter Dunlap 	struct sockaddr_in	*sin;
386a6d42e7dSPeter Dunlap 	struct sockaddr_in6	*sin6;
387a6d42e7dSPeter Dunlap 	idm_addr_t		*ip;
388a6d42e7dSPeter Dunlap 	idm_addr_list_t		*ipaddr;
389a6d42e7dSPeter Dunlap 	int			size_ipaddr;
390a6d42e7dSPeter Dunlap 
391a6d42e7dSPeter Dunlap 	*ipaddr_p = NULL;
392a6d42e7dSPeter Dunlap 	size_ipaddr = 0;
393a6d42e7dSPeter Dunlap 	buf = NULL;
394a6d42e7dSPeter Dunlap 
395a6d42e7dSPeter Dunlap 	/* create an ipv4 and ipv6 UDP socket */
396a6d42e7dSPeter Dunlap 	if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL)
397a6d42e7dSPeter Dunlap 		return (0);
398a6d42e7dSPeter Dunlap 	if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) {
399a6d42e7dSPeter Dunlap 		idm_sodestroy(so6);
400a6d42e7dSPeter Dunlap 		return (0);
401a6d42e7dSPeter Dunlap 	}
402a6d42e7dSPeter Dunlap 
403a6d42e7dSPeter Dunlap 
404a6d42e7dSPeter Dunlap retry_count:
405a6d42e7dSPeter Dunlap 	/* snapshot the current number of interfaces */
406a6d42e7dSPeter Dunlap 	lifn.lifn_family = PF_UNSPEC;
407a6d42e7dSPeter Dunlap 	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
408a6d42e7dSPeter Dunlap 	lifn.lifn_count = 0;
4090f1702c5SYu Xiangning 	/* use vp6 for ioctls with unspecified families by default */
4100f1702c5SYu Xiangning 	if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED())
4110f1702c5SYu Xiangning 	    != 0) {
412a6d42e7dSPeter Dunlap 		goto cleanup;
413a6d42e7dSPeter Dunlap 	}
414a6d42e7dSPeter Dunlap 
415a6d42e7dSPeter Dunlap 	numifs = lifn.lifn_count;
416a6d42e7dSPeter Dunlap 	if (numifs <= 0) {
417a6d42e7dSPeter Dunlap 		goto cleanup;
418a6d42e7dSPeter Dunlap 	}
419a6d42e7dSPeter Dunlap 
420a6d42e7dSPeter Dunlap 	/* allocate extra room in case more interfaces appear */
421a6d42e7dSPeter Dunlap 	numifs += 10;
422a6d42e7dSPeter Dunlap 
423a6d42e7dSPeter Dunlap 	/* get the interface names and ip addresses */
424a6d42e7dSPeter Dunlap 	bufsize = numifs * sizeof (struct lifreq);
425a6d42e7dSPeter Dunlap 	buf = kmem_alloc(bufsize, KM_SLEEP);
426a6d42e7dSPeter Dunlap 
427a6d42e7dSPeter Dunlap 	lifc.lifc_family = AF_UNSPEC;
428a6d42e7dSPeter Dunlap 	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
429a6d42e7dSPeter Dunlap 	lifc.lifc_len = bufsize;
430a6d42e7dSPeter Dunlap 	lifc.lifc_buf = buf;
4310f1702c5SYu Xiangning 	rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED());
432a6d42e7dSPeter Dunlap 	if (rc != 0) {
433a6d42e7dSPeter Dunlap 		goto cleanup;
434a6d42e7dSPeter Dunlap 	}
435a6d42e7dSPeter Dunlap 	/* if our extra room is used up, try again */
436a6d42e7dSPeter Dunlap 	if (bufsize <= lifc.lifc_len) {
437a6d42e7dSPeter Dunlap 		kmem_free(buf, bufsize);
438a6d42e7dSPeter Dunlap 		buf = NULL;
439a6d42e7dSPeter Dunlap 		goto retry_count;
440a6d42e7dSPeter Dunlap 	}
441a6d42e7dSPeter Dunlap 	/* calc actual number of ifconfs */
442a6d42e7dSPeter Dunlap 	n = lifc.lifc_len / sizeof (struct lifreq);
443a6d42e7dSPeter Dunlap 
444a6d42e7dSPeter Dunlap 	/* get ip address */
445a6d42e7dSPeter Dunlap 	if (n > 0) {
446a6d42e7dSPeter Dunlap 		size_ipaddr = sizeof (idm_addr_list_t) +
447a6d42e7dSPeter Dunlap 		    (n - 1) * sizeof (idm_addr_t);
448a6d42e7dSPeter Dunlap 		ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP);
449a6d42e7dSPeter Dunlap 	} else {
450a6d42e7dSPeter Dunlap 		goto cleanup;
451a6d42e7dSPeter Dunlap 	}
452a6d42e7dSPeter Dunlap 
453a6d42e7dSPeter Dunlap 	/*
454a6d42e7dSPeter Dunlap 	 * Examine the array of interfaces and filter uninteresting ones
455a6d42e7dSPeter Dunlap 	 */
456a6d42e7dSPeter Dunlap 	for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) {
457a6d42e7dSPeter Dunlap 
458a6d42e7dSPeter Dunlap 		/*
459a6d42e7dSPeter Dunlap 		 * Copy the address as the SIOCGLIFFLAGS ioctl is destructive
460a6d42e7dSPeter Dunlap 		 */
461a6d42e7dSPeter Dunlap 		ss = lp->lifr_addr;
462a6d42e7dSPeter Dunlap 		/*
463a6d42e7dSPeter Dunlap 		 * fetch the flags using the socket of the correct family
464a6d42e7dSPeter Dunlap 		 */
465a6d42e7dSPeter Dunlap 		switch (ss.ss_family) {
466a6d42e7dSPeter Dunlap 		case AF_INET:
4670f1702c5SYu Xiangning 			rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp,
4680f1702c5SYu Xiangning 			    &rval, CRED());
469a6d42e7dSPeter Dunlap 			break;
470a6d42e7dSPeter Dunlap 		case AF_INET6:
4710f1702c5SYu Xiangning 			rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp,
4720f1702c5SYu Xiangning 			    &rval, CRED());
473a6d42e7dSPeter Dunlap 			break;
474a6d42e7dSPeter Dunlap 		default:
475a6d42e7dSPeter Dunlap 			continue;
476a6d42e7dSPeter Dunlap 		}
477a6d42e7dSPeter Dunlap 		if (rc == 0) {
478a6d42e7dSPeter Dunlap 			/*
479a6d42e7dSPeter Dunlap 			 * If we got the flags, skip uninteresting
480a6d42e7dSPeter Dunlap 			 * interfaces based on flags
481a6d42e7dSPeter Dunlap 			 */
482a6d42e7dSPeter Dunlap 			if ((lp->lifr_flags & IFF_UP) != IFF_UP)
483a6d42e7dSPeter Dunlap 				continue;
484a6d42e7dSPeter Dunlap 			if (lp->lifr_flags &
485a6d42e7dSPeter Dunlap 			    (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED))
486a6d42e7dSPeter Dunlap 				continue;
487a6d42e7dSPeter Dunlap 		}
488a6d42e7dSPeter Dunlap 
489a6d42e7dSPeter Dunlap 		/* save ip address */
490a6d42e7dSPeter Dunlap 		ip = &ipaddr->al_addrs[j];
491a6d42e7dSPeter Dunlap 		switch (ss.ss_family) {
492a6d42e7dSPeter Dunlap 		case AF_INET:
493a6d42e7dSPeter Dunlap 			sin = (struct sockaddr_in *)&ss;
494a6d42e7dSPeter Dunlap 			if (!idm_v4_addr_okay(&sin->sin_addr))
495a6d42e7dSPeter Dunlap 				continue;
496a6d42e7dSPeter Dunlap 			ip->a_addr.i_addr.in4 = sin->sin_addr;
497a6d42e7dSPeter Dunlap 			ip->a_addr.i_insize = sizeof (struct in_addr);
498a6d42e7dSPeter Dunlap 			break;
499a6d42e7dSPeter Dunlap 		case AF_INET6:
500a6d42e7dSPeter Dunlap 			sin6 = (struct sockaddr_in6 *)&ss;
501a6d42e7dSPeter Dunlap 			if (!idm_v6_addr_okay(&sin6->sin6_addr))
502a6d42e7dSPeter Dunlap 				continue;
503a6d42e7dSPeter Dunlap 			ip->a_addr.i_addr.in6 = sin6->sin6_addr;
504a6d42e7dSPeter Dunlap 			ip->a_addr.i_insize = sizeof (struct in6_addr);
505a6d42e7dSPeter Dunlap 			break;
506a6d42e7dSPeter Dunlap 		default:
507a6d42e7dSPeter Dunlap 			continue;
508a6d42e7dSPeter Dunlap 		}
509a6d42e7dSPeter Dunlap 		j++;
510a6d42e7dSPeter Dunlap 	}
511a6d42e7dSPeter Dunlap 
512a6d42e7dSPeter Dunlap 	if (j == 0) {
513a6d42e7dSPeter Dunlap 		/* no valid ifaddr */
514a6d42e7dSPeter Dunlap 		kmem_free(ipaddr, size_ipaddr);
515a6d42e7dSPeter Dunlap 		size_ipaddr = 0;
516a6d42e7dSPeter Dunlap 		ipaddr = NULL;
517a6d42e7dSPeter Dunlap 	} else {
518a6d42e7dSPeter Dunlap 		ipaddr->al_out_cnt = j;
519a6d42e7dSPeter Dunlap 	}
520a6d42e7dSPeter Dunlap 
521a6d42e7dSPeter Dunlap 
522a6d42e7dSPeter Dunlap cleanup:
523a6d42e7dSPeter Dunlap 	idm_sodestroy(so6);
524a6d42e7dSPeter Dunlap 	idm_sodestroy(so4);
525a6d42e7dSPeter Dunlap 
526a6d42e7dSPeter Dunlap 	if (buf != NULL)
527a6d42e7dSPeter Dunlap 		kmem_free(buf, bufsize);
528a6d42e7dSPeter Dunlap 
529a6d42e7dSPeter Dunlap 	*ipaddr_p = ipaddr;
530a6d42e7dSPeter Dunlap 	return (size_ipaddr);
531a6d42e7dSPeter Dunlap }
532a6d42e7dSPeter Dunlap 
533a6d42e7dSPeter Dunlap int
5340f1702c5SYu Xiangning idm_sorecv(ksocket_t so, void *msg, size_t len)
535a6d42e7dSPeter Dunlap {
536a6d42e7dSPeter Dunlap 	iovec_t iov;
537a6d42e7dSPeter Dunlap 
538a6d42e7dSPeter Dunlap 	ASSERT(so != NULL);
539a6d42e7dSPeter Dunlap 	ASSERT(len != 0);
540a6d42e7dSPeter Dunlap 
541a6d42e7dSPeter Dunlap 	/*
542a6d42e7dSPeter Dunlap 	 * Fill in iovec and receive data
543a6d42e7dSPeter Dunlap 	 */
544a6d42e7dSPeter Dunlap 	iov.iov_base = msg;
545a6d42e7dSPeter Dunlap 	iov.iov_len = len;
546a6d42e7dSPeter Dunlap 
547a6d42e7dSPeter Dunlap 	return (idm_iov_sorecv(so, &iov, 1, len));
548a6d42e7dSPeter Dunlap }
549a6d42e7dSPeter Dunlap 
550a6d42e7dSPeter Dunlap /*
551a6d42e7dSPeter Dunlap  * idm_sosendto - Sends a buffered data on a non-connected socket.
552a6d42e7dSPeter Dunlap  *
553a6d42e7dSPeter Dunlap  * This function puts the data provided on the wire by calling sosendmsg.
554a6d42e7dSPeter Dunlap  * It will return only when all the data has been sent or if an error
555a6d42e7dSPeter Dunlap  * occurs.
556a6d42e7dSPeter Dunlap  *
557a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sosendmsg fails, and
558a6d42e7dSPeter Dunlap  * -1 if sosendmsg returns success but uio_resid != 0
559a6d42e7dSPeter Dunlap  */
560a6d42e7dSPeter Dunlap int
5610f1702c5SYu Xiangning idm_sosendto(ksocket_t so, void *buff, size_t len,
562a6d42e7dSPeter Dunlap     struct sockaddr *name, socklen_t namelen)
563a6d42e7dSPeter Dunlap {
564a6d42e7dSPeter Dunlap 	struct msghdr		msg;
565a6d42e7dSPeter Dunlap 	struct iovec		iov[1];
566a6d42e7dSPeter Dunlap 	int			error;
5670f1702c5SYu Xiangning 	size_t			sent = 0;
568a6d42e7dSPeter Dunlap 
569a6d42e7dSPeter Dunlap 	iov[0].iov_base	= buff;
570a6d42e7dSPeter Dunlap 	iov[0].iov_len	= len;
571a6d42e7dSPeter Dunlap 
572a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
573a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
574a6d42e7dSPeter Dunlap 	msg.msg_iov	= iov;
575a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= 1;
576a6d42e7dSPeter Dunlap 	msg.msg_name	= name;
577a6d42e7dSPeter Dunlap 	msg.msg_namelen	= namelen;
578a6d42e7dSPeter Dunlap 
5790f1702c5SYu Xiangning 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) {
580a6d42e7dSPeter Dunlap 		/* Data sent */
5810f1702c5SYu Xiangning 		if (sent == len) {
582a6d42e7dSPeter Dunlap 			/* All data sent.  Success. */
583a6d42e7dSPeter Dunlap 			return (0);
584a6d42e7dSPeter Dunlap 		} else {
585a6d42e7dSPeter Dunlap 			/* Not all data was sent.  Failure */
586a6d42e7dSPeter Dunlap 			return (-1);
587a6d42e7dSPeter Dunlap 		}
588a6d42e7dSPeter Dunlap 	}
589a6d42e7dSPeter Dunlap 
590a6d42e7dSPeter Dunlap 	/* Send failed */
591a6d42e7dSPeter Dunlap 	return (error);
592a6d42e7dSPeter Dunlap }
593a6d42e7dSPeter Dunlap 
594a6d42e7dSPeter Dunlap /*
595a6d42e7dSPeter Dunlap  * idm_iov_sosend - Sends an iovec on a connection.
596a6d42e7dSPeter Dunlap  *
597a6d42e7dSPeter Dunlap  * This function puts the data provided on the wire by calling sosendmsg.
598a6d42e7dSPeter Dunlap  * It will return only when all the data has been sent or if an error
599a6d42e7dSPeter Dunlap  * occurs.
600a6d42e7dSPeter Dunlap  *
601a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sosendmsg fails, and
602a6d42e7dSPeter Dunlap  * -1 if sosendmsg returns success but uio_resid != 0
603a6d42e7dSPeter Dunlap  */
604a6d42e7dSPeter Dunlap int
6050f1702c5SYu Xiangning idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
606a6d42e7dSPeter Dunlap {
607a6d42e7dSPeter Dunlap 	struct msghdr		msg;
608a6d42e7dSPeter Dunlap 	int			error;
6090f1702c5SYu Xiangning 	size_t 			sent = 0;
610a6d42e7dSPeter Dunlap 
611a6d42e7dSPeter Dunlap 	ASSERT(iop != NULL);
612a6d42e7dSPeter Dunlap 
613a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
614a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
615a6d42e7dSPeter Dunlap 	msg.msg_iov	= iop;
616a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= iovlen;
617a6d42e7dSPeter Dunlap 
6180f1702c5SYu Xiangning 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED()))
6190f1702c5SYu Xiangning 	    == 0) {
620a6d42e7dSPeter Dunlap 		/* Data sent */
6210f1702c5SYu Xiangning 		if (sent == total_len) {
622a6d42e7dSPeter Dunlap 			/* All data sent.  Success. */
623a6d42e7dSPeter Dunlap 			return (0);
624a6d42e7dSPeter Dunlap 		} else {
625a6d42e7dSPeter Dunlap 			/* Not all data was sent.  Failure */
626a6d42e7dSPeter Dunlap 			return (-1);
627a6d42e7dSPeter Dunlap 		}
628a6d42e7dSPeter Dunlap 	}
629a6d42e7dSPeter Dunlap 
630a6d42e7dSPeter Dunlap 	/* Send failed */
631a6d42e7dSPeter Dunlap 	return (error);
632a6d42e7dSPeter Dunlap }
633a6d42e7dSPeter Dunlap 
634a6d42e7dSPeter Dunlap /*
635a6d42e7dSPeter Dunlap  * idm_iov_sorecv - Receives an iovec from a connection
636a6d42e7dSPeter Dunlap  *
637a6d42e7dSPeter Dunlap  * This function gets the data asked for from the socket.  It will return
638a6d42e7dSPeter Dunlap  * only when all the requested data has been retrieved or if an error
639a6d42e7dSPeter Dunlap  * occurs.
640a6d42e7dSPeter Dunlap  *
641a6d42e7dSPeter Dunlap  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
642a6d42e7dSPeter Dunlap  * -1 if sorecvmsg returns success but uio_resid != 0
643a6d42e7dSPeter Dunlap  */
644a6d42e7dSPeter Dunlap int
6450f1702c5SYu Xiangning idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
646a6d42e7dSPeter Dunlap {
647a6d42e7dSPeter Dunlap 	struct msghdr		msg;
648a6d42e7dSPeter Dunlap 	int			error;
6490f1702c5SYu Xiangning 	size_t			recv;
6500f1702c5SYu Xiangning 	int 			flags;
651a6d42e7dSPeter Dunlap 
652a6d42e7dSPeter Dunlap 	ASSERT(iop != NULL);
653a6d42e7dSPeter Dunlap 
654a6d42e7dSPeter Dunlap 	/* Initialization of the message header. */
655a6d42e7dSPeter Dunlap 	bzero(&msg, sizeof (msg));
656a6d42e7dSPeter Dunlap 	msg.msg_iov	= iop;
657a6d42e7dSPeter Dunlap 	msg.msg_iovlen	= iovlen;
6580f1702c5SYu Xiangning 	flags		= MSG_WAITALL;
659a6d42e7dSPeter Dunlap 
6600f1702c5SYu Xiangning 	if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED()))
6610f1702c5SYu Xiangning 	    == 0) {
662a6d42e7dSPeter Dunlap 		/* Received data */
6630f1702c5SYu Xiangning 		if (recv == total_len) {
664a6d42e7dSPeter Dunlap 			/* All requested data received.  Success */
665a6d42e7dSPeter Dunlap 			return (0);
666a6d42e7dSPeter Dunlap 		} else {
667a6d42e7dSPeter Dunlap 			/*
668a6d42e7dSPeter Dunlap 			 * Not all data was received.  The connection has
669a6d42e7dSPeter Dunlap 			 * probably failed.
670a6d42e7dSPeter Dunlap 			 */
671a6d42e7dSPeter Dunlap 			return (-1);
672a6d42e7dSPeter Dunlap 		}
673a6d42e7dSPeter Dunlap 	}
674a6d42e7dSPeter Dunlap 
675a6d42e7dSPeter Dunlap 	/* Receive failed */
676a6d42e7dSPeter Dunlap 	return (error);
677a6d42e7dSPeter Dunlap }
678a6d42e7dSPeter Dunlap 
679a6d42e7dSPeter Dunlap static void
680a6d42e7dSPeter Dunlap idm_set_ini_preconnect_options(idm_so_conn_t *sc)
681a6d42e7dSPeter Dunlap {
682a6d42e7dSPeter Dunlap 	int	conn_abort = 10000;
683a6d42e7dSPeter Dunlap 	int	conn_notify = 2000;
684a6d42e7dSPeter Dunlap 	int	abort = 30000;
685a6d42e7dSPeter Dunlap 
686a6d42e7dSPeter Dunlap 	/* Pre-connect socket options */
6870f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
6880f1702c5SYu Xiangning 	    TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int),
6890f1702c5SYu Xiangning 	    CRED());
6900f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
6910f1702c5SYu Xiangning 	    TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int),
6920f1702c5SYu Xiangning 	    CRED());
6930f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
6940f1702c5SYu Xiangning 	    (char *)&abort, sizeof (int), CRED());
695a6d42e7dSPeter Dunlap }
696a6d42e7dSPeter Dunlap 
697a6d42e7dSPeter Dunlap static void
698a6d42e7dSPeter Dunlap idm_set_ini_postconnect_options(idm_so_conn_t *sc)
699a6d42e7dSPeter Dunlap {
700a6d42e7dSPeter Dunlap 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
701a6d42e7dSPeter Dunlap 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
702a6d42e7dSPeter Dunlap 	const int	on = 1;
703a6d42e7dSPeter Dunlap 
704a6d42e7dSPeter Dunlap 	/* Set postconnect options */
7050f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY,
7060f1702c5SYu Xiangning 	    (char *)&on, sizeof (int), CRED());
7070f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF,
7080f1702c5SYu Xiangning 	    (char *)&rcvbuf, sizeof (int), CRED());
7090f1702c5SYu Xiangning 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF,
7100f1702c5SYu Xiangning 	    (char *)&sndbuf, sizeof (int), CRED());
711a6d42e7dSPeter Dunlap }
712a6d42e7dSPeter Dunlap 
713a6d42e7dSPeter Dunlap static void
7140f1702c5SYu Xiangning idm_set_tgt_connect_options(ksocket_t ks)
715a6d42e7dSPeter Dunlap {
716a6d42e7dSPeter Dunlap 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
717a6d42e7dSPeter Dunlap 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
718a6d42e7dSPeter Dunlap 	const int	on = 1;
719a6d42e7dSPeter Dunlap 
720a6d42e7dSPeter Dunlap 	/* Set connect options */
7210f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF,
7220f1702c5SYu Xiangning 	    (char *)&rcvbuf, sizeof (int), CRED());
7230f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF,
7240f1702c5SYu Xiangning 	    (char *)&sndbuf, sizeof (int), CRED());
7250f1702c5SYu Xiangning 	(void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY,
7260f1702c5SYu Xiangning 	    (char *)&on, sizeof (on), CRED());
727a6d42e7dSPeter Dunlap }
728a6d42e7dSPeter Dunlap 
729a6d42e7dSPeter Dunlap static uint32_t
730a6d42e7dSPeter Dunlap n2h24(const uchar_t *ptr)
731a6d42e7dSPeter Dunlap {
732a6d42e7dSPeter Dunlap 	return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
733a6d42e7dSPeter Dunlap }
734a6d42e7dSPeter Dunlap 
735a6d42e7dSPeter Dunlap 
736a6d42e7dSPeter Dunlap static idm_status_t
737a6d42e7dSPeter Dunlap idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu)
738a6d42e7dSPeter Dunlap {
739a6d42e7dSPeter Dunlap 	iscsi_hdr_t	*bhs;
740a6d42e7dSPeter Dunlap 	uint32_t	hdr_digest_crc;
741a6d42e7dSPeter Dunlap 	uint32_t	crc_calculated;
742a6d42e7dSPeter Dunlap 	void		*new_hdr;
743a6d42e7dSPeter Dunlap 	int		ahslen = 0;
744a6d42e7dSPeter Dunlap 	int		total_len = 0;
745a6d42e7dSPeter Dunlap 	int		iovlen = 0;
746a6d42e7dSPeter Dunlap 	struct iovec	iov[2];
747a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
748a6d42e7dSPeter Dunlap 	int		rc;
749a6d42e7dSPeter Dunlap 
750a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
751a6d42e7dSPeter Dunlap 
752a6d42e7dSPeter Dunlap 	/*
753a6d42e7dSPeter Dunlap 	 * Read BHS
754a6d42e7dSPeter Dunlap 	 */
755a6d42e7dSPeter Dunlap 	bhs = pdu->isp_hdr;
756a6d42e7dSPeter Dunlap 	rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t));
757a6d42e7dSPeter Dunlap 	if (rc != IDM_STATUS_SUCCESS) {
758a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
759a6d42e7dSPeter Dunlap 	}
760a6d42e7dSPeter Dunlap 
761a6d42e7dSPeter Dunlap 	/*
762a6d42e7dSPeter Dunlap 	 * Check actual AHS length against the amount available in the buffer
763a6d42e7dSPeter Dunlap 	 */
764a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
765a6d42e7dSPeter Dunlap 	    (bhs->hlength * sizeof (uint32_t));
766a6d42e7dSPeter Dunlap 	pdu->isp_datalen = n2h24(bhs->dlength);
767*56261083SCharles Ting 	if (ic->ic_conn_type == CONN_TYPE_TGT &&
768*56261083SCharles Ting 	    pdu->isp_datalen > ic->ic_conn_params.max_recv_dataseglen) {
769*56261083SCharles Ting 		IDM_CONN_LOG(CE_WARN,
770*56261083SCharles Ting 		    "idm_sorecvhdr: exceeded the max data segment length");
771*56261083SCharles Ting 		return (IDM_STATUS_FAIL);
772*56261083SCharles Ting 	}
773a6d42e7dSPeter Dunlap 	if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) {
774a6d42e7dSPeter Dunlap 		/* Allocate a new header segment and change the callback */
775a6d42e7dSPeter Dunlap 		new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP);
776a6d42e7dSPeter Dunlap 		bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t));
777a6d42e7dSPeter Dunlap 		pdu->isp_hdr = new_hdr;
778a6d42e7dSPeter Dunlap 		pdu->isp_flags |= IDM_PDU_ADDL_HDR;
779a6d42e7dSPeter Dunlap 
780a6d42e7dSPeter Dunlap 		/*
781a6d42e7dSPeter Dunlap 		 * This callback will restore the expected values after
782a6d42e7dSPeter Dunlap 		 * the RX PDU has been processed.
783a6d42e7dSPeter Dunlap 		 */
784a6d42e7dSPeter Dunlap 		pdu->isp_callback = idm_sorx_addl_pdu_cb;
785a6d42e7dSPeter Dunlap 	}
786a6d42e7dSPeter Dunlap 
787a6d42e7dSPeter Dunlap 	/*
788a6d42e7dSPeter Dunlap 	 * Setup receipt of additional header and header digest (if enabled).
789a6d42e7dSPeter Dunlap 	 */
790a6d42e7dSPeter Dunlap 	if (bhs->hlength > 0) {
791a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1);
792a6d42e7dSPeter Dunlap 		ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t);
793a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len = ahslen;
794a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
795a6d42e7dSPeter Dunlap 		iovlen++;
796a6d42e7dSPeter Dunlap 	}
797a6d42e7dSPeter Dunlap 
798a6d42e7dSPeter Dunlap 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
799a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc;
800a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len = sizeof (hdr_digest_crc);
801a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
802a6d42e7dSPeter Dunlap 		iovlen++;
803a6d42e7dSPeter Dunlap 	}
804a6d42e7dSPeter Dunlap 
805a6d42e7dSPeter Dunlap 	if ((iovlen != 0) &&
806a6d42e7dSPeter Dunlap 	    (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen,
807a6d42e7dSPeter Dunlap 	    total_len) != 0)) {
808a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
809a6d42e7dSPeter Dunlap 	}
810a6d42e7dSPeter Dunlap 
811a6d42e7dSPeter Dunlap 	/*
812a6d42e7dSPeter Dunlap 	 * Validate header digest if enabled
813a6d42e7dSPeter Dunlap 	 */
814a6d42e7dSPeter Dunlap 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
815a6d42e7dSPeter Dunlap 		crc_calculated = idm_crc32c(pdu->isp_hdr,
816a6d42e7dSPeter Dunlap 		    sizeof (iscsi_hdr_t) + ahslen);
817a6d42e7dSPeter Dunlap 		if (crc_calculated != hdr_digest_crc) {
818a6d42e7dSPeter Dunlap 			/* Invalid Header Digest */
819a6d42e7dSPeter Dunlap 			return (IDM_STATUS_HEADER_DIGEST);
820a6d42e7dSPeter Dunlap 		}
821a6d42e7dSPeter Dunlap 	}
822a6d42e7dSPeter Dunlap 
823a6d42e7dSPeter Dunlap 	return (0);
824a6d42e7dSPeter Dunlap }
825a6d42e7dSPeter Dunlap 
826a6d42e7dSPeter Dunlap /*
827a6d42e7dSPeter Dunlap  * idm_so_ini_conn_create()
828a6d42e7dSPeter Dunlap  * Allocate the sockets transport connection resources.
829a6d42e7dSPeter Dunlap  */
830a6d42e7dSPeter Dunlap static idm_status_t
831a6d42e7dSPeter Dunlap idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic)
832a6d42e7dSPeter Dunlap {
8330f1702c5SYu Xiangning 	ksocket_t	so;
834a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
835a6d42e7dSPeter Dunlap 	idm_status_t	idmrc;
836a6d42e7dSPeter Dunlap 
837a6d42e7dSPeter Dunlap 	so = idm_socreate(cr->cr_domain, cr->cr_type,
838a6d42e7dSPeter Dunlap 	    cr->cr_protocol);
839a6d42e7dSPeter Dunlap 	if (so == NULL) {
840a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
841a6d42e7dSPeter Dunlap 	}
842a6d42e7dSPeter Dunlap 
843a6d42e7dSPeter Dunlap 	/* Bind the socket if configured to do so */
844a6d42e7dSPeter Dunlap 	if (cr->cr_bound) {
8450f1702c5SYu Xiangning 		if (ksocket_bind(so, &cr->cr_bound_addr.sin,
8460f1702c5SYu Xiangning 		    SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) {
847a6d42e7dSPeter Dunlap 			idm_sodestroy(so);
848a6d42e7dSPeter Dunlap 			return (IDM_STATUS_FAIL);
849a6d42e7dSPeter Dunlap 		}
850a6d42e7dSPeter Dunlap 	}
851a6d42e7dSPeter Dunlap 
852a6d42e7dSPeter Dunlap 	idmrc = idm_so_conn_create_common(ic, so);
853a6d42e7dSPeter Dunlap 	if (idmrc != IDM_STATUS_SUCCESS) {
854a6d42e7dSPeter Dunlap 		idm_soshutdown(so);
855a6d42e7dSPeter Dunlap 		idm_sodestroy(so);
856a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
857a6d42e7dSPeter Dunlap 	}
858a6d42e7dSPeter Dunlap 
859a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
860a6d42e7dSPeter Dunlap 	/* Set up socket options */
861a6d42e7dSPeter Dunlap 	idm_set_ini_preconnect_options(so_conn);
862a6d42e7dSPeter Dunlap 
863a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
864a6d42e7dSPeter Dunlap }
865a6d42e7dSPeter Dunlap 
866a6d42e7dSPeter Dunlap /*
867a6d42e7dSPeter Dunlap  * idm_so_ini_conn_destroy()
868a6d42e7dSPeter Dunlap  * Tear down the sockets transport connection resources.
869a6d42e7dSPeter Dunlap  */
870a6d42e7dSPeter Dunlap static void
871a6d42e7dSPeter Dunlap idm_so_ini_conn_destroy(idm_conn_t *ic)
872a6d42e7dSPeter Dunlap {
873a6d42e7dSPeter Dunlap 	idm_so_conn_destroy_common(ic);
874a6d42e7dSPeter Dunlap }
875a6d42e7dSPeter Dunlap 
876a6d42e7dSPeter Dunlap /*
877a6d42e7dSPeter Dunlap  * idm_so_ini_conn_connect()
878a6d42e7dSPeter Dunlap  * Establish the connection referred to by the handle previously allocated via
879a6d42e7dSPeter Dunlap  * idm_so_ini_conn_create().
880a6d42e7dSPeter Dunlap  */
881a6d42e7dSPeter Dunlap static idm_status_t
882a6d42e7dSPeter Dunlap idm_so_ini_conn_connect(idm_conn_t *ic)
883a6d42e7dSPeter Dunlap {
884a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
885aff4bce5Syi zhang - Sun Microsystems - Beijing China 	struct sonode	*node = NULL;
886aff4bce5Syi zhang - Sun Microsystems - Beijing China 	int 		rc;
887aff4bce5Syi zhang - Sun Microsystems - Beijing China 	clock_t		lbolt, conn_login_max, conn_login_interval;
888aff4bce5Syi zhang - Sun Microsystems - Beijing China 	boolean_t	nonblock;
889a6d42e7dSPeter Dunlap 
890a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
891aff4bce5Syi zhang - Sun Microsystems - Beijing China 	nonblock = ic->ic_conn_params.nonblock_socket;
892aff4bce5Syi zhang - Sun Microsystems - Beijing China 	conn_login_max = ic->ic_conn_params.conn_login_max;
893aff4bce5Syi zhang - Sun Microsystems - Beijing China 	conn_login_interval = ddi_get_lbolt() +
894aff4bce5Syi zhang - Sun Microsystems - Beijing China 	    SEC_TO_TICK(ic->ic_conn_params.conn_login_interval);
895aff4bce5Syi zhang - Sun Microsystems - Beijing China 
896aff4bce5Syi zhang - Sun Microsystems - Beijing China 	if (nonblock == B_TRUE) {
897aff4bce5Syi zhang - Sun Microsystems - Beijing China 		node = ((struct sonode *)(so_conn->ic_so));
898aff4bce5Syi zhang - Sun Microsystems - Beijing China 		/* Set to none block socket mode */
899aff4bce5Syi zhang - Sun Microsystems - Beijing China 		idm_so_socket_set_nonblock(node);
900aff4bce5Syi zhang - Sun Microsystems - Beijing China 		do {
901aff4bce5Syi zhang - Sun Microsystems - Beijing China 			rc = ksocket_connect(so_conn->ic_so,
902aff4bce5Syi zhang - Sun Microsystems - Beijing China 			    &ic->ic_ini_dst_addr.sin,
903aff4bce5Syi zhang - Sun Microsystems - Beijing China 			    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)),
904aff4bce5Syi zhang - Sun Microsystems - Beijing China 			    CRED());
905aff4bce5Syi zhang - Sun Microsystems - Beijing China 			if (rc == 0 || rc == EISCONN) {
906aff4bce5Syi zhang - Sun Microsystems - Beijing China 				/* socket success or already success */
907aff4bce5Syi zhang - Sun Microsystems - Beijing China 				rc = IDM_STATUS_SUCCESS;
908aff4bce5Syi zhang - Sun Microsystems - Beijing China 				break;
909aff4bce5Syi zhang - Sun Microsystems - Beijing China 			}
910aff4bce5Syi zhang - Sun Microsystems - Beijing China 			if ((rc == ETIMEDOUT) || (rc == ECONNREFUSED) ||
911aff4bce5Syi zhang - Sun Microsystems - Beijing China 			    (rc == ECONNRESET)) {
912aff4bce5Syi zhang - Sun Microsystems - Beijing China 				/* socket connection timeout or refuse */
913aff4bce5Syi zhang - Sun Microsystems - Beijing China 				break;
914aff4bce5Syi zhang - Sun Microsystems - Beijing China 			}
915aff4bce5Syi zhang - Sun Microsystems - Beijing China 			lbolt = ddi_get_lbolt();
916aff4bce5Syi zhang - Sun Microsystems - Beijing China 			if (lbolt > conn_login_max) {
917aff4bce5Syi zhang - Sun Microsystems - Beijing China 				/*
918aff4bce5Syi zhang - Sun Microsystems - Beijing China 				 * Connection retry timeout,
919aff4bce5Syi zhang - Sun Microsystems - Beijing China 				 * failed connect to target.
920aff4bce5Syi zhang - Sun Microsystems - Beijing China 				 */
921aff4bce5Syi zhang - Sun Microsystems - Beijing China 				break;
922aff4bce5Syi zhang - Sun Microsystems - Beijing China 			}
923aff4bce5Syi zhang - Sun Microsystems - Beijing China 			if (lbolt < conn_login_interval) {
924aff4bce5Syi zhang - Sun Microsystems - Beijing China 				if ((rc == EINPROGRESS) || (rc == EALREADY)) {
925aff4bce5Syi zhang - Sun Microsystems - Beijing China 					/* TCP connect still in progress */
926aff4bce5Syi zhang - Sun Microsystems - Beijing China 					delay(SEC_TO_TICK(IN_PROGRESS_DELAY));
927aff4bce5Syi zhang - Sun Microsystems - Beijing China 					continue;
928aff4bce5Syi zhang - Sun Microsystems - Beijing China 				} else {
929aff4bce5Syi zhang - Sun Microsystems - Beijing China 					delay(conn_login_interval - lbolt);
930aff4bce5Syi zhang - Sun Microsystems - Beijing China 				}
931aff4bce5Syi zhang - Sun Microsystems - Beijing China 			}
932aff4bce5Syi zhang - Sun Microsystems - Beijing China 			conn_login_interval = ddi_get_lbolt() +
933aff4bce5Syi zhang - Sun Microsystems - Beijing China 			    SEC_TO_TICK(ic->ic_conn_params.conn_login_interval);
934aff4bce5Syi zhang - Sun Microsystems - Beijing China 		} while (rc != 0);
935aff4bce5Syi zhang - Sun Microsystems - Beijing China 		/* resume to nonblock mode */
936aff4bce5Syi zhang - Sun Microsystems - Beijing China 		if (rc == IDM_STATUS_SUCCESS) {
937aff4bce5Syi zhang - Sun Microsystems - Beijing China 			idm_so_socket_set_block(node);
938aff4bce5Syi zhang - Sun Microsystems - Beijing China 		}
939aff4bce5Syi zhang - Sun Microsystems - Beijing China 	} else {
940aff4bce5Syi zhang - Sun Microsystems - Beijing China 		rc = ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin,
941aff4bce5Syi zhang - Sun Microsystems - Beijing China 		    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED());
942aff4bce5Syi zhang - Sun Microsystems - Beijing China 	}
943a6d42e7dSPeter Dunlap 
944aff4bce5Syi zhang - Sun Microsystems - Beijing China 	if (rc != 0) {
945a6d42e7dSPeter Dunlap 		idm_soshutdown(so_conn->ic_so);
946a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
947a6d42e7dSPeter Dunlap 	}
948a6d42e7dSPeter Dunlap 
949a6d42e7dSPeter Dunlap 	idm_so_conn_connect_common(ic);
950a6d42e7dSPeter Dunlap 
951a6d42e7dSPeter Dunlap 	idm_set_ini_postconnect_options(so_conn);
952a6d42e7dSPeter Dunlap 
953a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
954a6d42e7dSPeter Dunlap }
955a6d42e7dSPeter Dunlap 
956a6d42e7dSPeter Dunlap idm_status_t
9570f1702c5SYu Xiangning idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so)
958a6d42e7dSPeter Dunlap {
959a6d42e7dSPeter Dunlap 	idm_status_t	idmrc;
960a6d42e7dSPeter Dunlap 
961a6d42e7dSPeter Dunlap 	idmrc = idm_so_conn_create_common(ic, new_so);
962a6d42e7dSPeter Dunlap 
963a6d42e7dSPeter Dunlap 	return (idmrc);
964a6d42e7dSPeter Dunlap }
965a6d42e7dSPeter Dunlap 
966a6d42e7dSPeter Dunlap static void
967a6d42e7dSPeter Dunlap idm_so_tgt_conn_destroy(idm_conn_t *ic)
968a6d42e7dSPeter Dunlap {
969a6d42e7dSPeter Dunlap 	idm_so_conn_destroy_common(ic);
970a6d42e7dSPeter Dunlap }
971a6d42e7dSPeter Dunlap 
972a6d42e7dSPeter Dunlap /*
973a6d42e7dSPeter Dunlap  * idm_so_tgt_conn_connect()
974a6d42e7dSPeter Dunlap  * Establish the connection in ic, passed from idm_tgt_conn_finish(), which
975a6d42e7dSPeter Dunlap  * is invoked from the SM as a result of an inbound connection request.
976a6d42e7dSPeter Dunlap  */
977a6d42e7dSPeter Dunlap static idm_status_t
978a6d42e7dSPeter Dunlap idm_so_tgt_conn_connect(idm_conn_t *ic)
979a6d42e7dSPeter Dunlap {
980a6d42e7dSPeter Dunlap 	idm_so_conn_connect_common(ic);
981a6d42e7dSPeter Dunlap 
982a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
983a6d42e7dSPeter Dunlap }
984a6d42e7dSPeter Dunlap 
985a6d42e7dSPeter Dunlap static idm_status_t
9860f1702c5SYu Xiangning idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so)
987a6d42e7dSPeter Dunlap {
988a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
989a6d42e7dSPeter Dunlap 
990a6d42e7dSPeter Dunlap 	so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP);
991a6d42e7dSPeter Dunlap 	so_conn->ic_so = new_so;
992a6d42e7dSPeter Dunlap 
993a6d42e7dSPeter Dunlap 	ic->ic_transport_private = so_conn;
994a6d42e7dSPeter Dunlap 	ic->ic_transport_hdrlen = 0;
995a6d42e7dSPeter Dunlap 
996a6d42e7dSPeter Dunlap 	/* Set the scoreboarding flag on this connection */
997a6d42e7dSPeter Dunlap 	ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD;
998*56261083SCharles Ting 	ic->ic_conn_params.max_recv_dataseglen =
999*56261083SCharles Ting 	    ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
1000*56261083SCharles Ting 	ic->ic_conn_params.max_xmit_dataseglen =
1001*56261083SCharles Ting 	    ISCSI_DEFAULT_MAX_XMIT_SEG_LEN;
1002a6d42e7dSPeter Dunlap 
1003a6d42e7dSPeter Dunlap 	/*
1004a6d42e7dSPeter Dunlap 	 * Initialize tx thread mutex and list
1005a6d42e7dSPeter Dunlap 	 */
1006a6d42e7dSPeter Dunlap 	mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL);
1007a6d42e7dSPeter Dunlap 	cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL);
1008a6d42e7dSPeter Dunlap 	list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t),
1009a6d42e7dSPeter Dunlap 	    offsetof(idm_pdu_t, idm_tx_link));
1010a6d42e7dSPeter Dunlap 
1011a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1012a6d42e7dSPeter Dunlap }
1013a6d42e7dSPeter Dunlap 
1014a6d42e7dSPeter Dunlap static void
1015a6d42e7dSPeter Dunlap idm_so_conn_destroy_common(idm_conn_t *ic)
1016a6d42e7dSPeter Dunlap {
1017a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
1018a6d42e7dSPeter Dunlap 
1019a6d42e7dSPeter Dunlap 	ic->ic_transport_private = NULL;
1020a6d42e7dSPeter Dunlap 	idm_sodestroy(so_conn->ic_so);
1021a6d42e7dSPeter Dunlap 	list_destroy(&so_conn->ic_tx_list);
1022a6d42e7dSPeter Dunlap 	mutex_destroy(&so_conn->ic_tx_mutex);
1023a6d42e7dSPeter Dunlap 	cv_destroy(&so_conn->ic_tx_cv);
1024a6d42e7dSPeter Dunlap 
1025a6d42e7dSPeter Dunlap 	kmem_free(so_conn, sizeof (idm_so_conn_t));
1026a6d42e7dSPeter Dunlap }
1027a6d42e7dSPeter Dunlap 
1028a6d42e7dSPeter Dunlap static void
1029a6d42e7dSPeter Dunlap idm_so_conn_connect_common(idm_conn_t *ic)
1030a6d42e7dSPeter Dunlap {
1031a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
10320f1702c5SYu Xiangning 	struct sockaddr_in6	t_addr;
10330f1702c5SYu Xiangning 	socklen_t	t_addrlen = 0;
1034a6d42e7dSPeter Dunlap 
1035a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
10360f1702c5SYu Xiangning 	bzero(&t_addr, sizeof (struct sockaddr_in6));
10370f1702c5SYu Xiangning 	t_addrlen = sizeof (struct sockaddr_in6);
1038a6d42e7dSPeter Dunlap 
1039a6d42e7dSPeter Dunlap 	/* Set the local and remote addresses in the idm conn handle */
10400f1702c5SYu Xiangning 	ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr,
10410f1702c5SYu Xiangning 	    &t_addrlen, CRED());
10420f1702c5SYu Xiangning 	bcopy(&t_addr, &ic->ic_laddr, t_addrlen);
10430f1702c5SYu Xiangning 	ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr,
10440f1702c5SYu Xiangning 	    &t_addrlen, CRED());
10450f1702c5SYu Xiangning 	bcopy(&t_addr, &ic->ic_raddr, t_addrlen);
1046a6d42e7dSPeter Dunlap 
1047a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
1048a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0,
1049a6d42e7dSPeter Dunlap 	    &p0, TS_RUN, minclsyspri);
1050a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0,
1051a6d42e7dSPeter Dunlap 	    &p0, TS_RUN, minclsyspri);
1052a6d42e7dSPeter Dunlap 
1053a6d42e7dSPeter Dunlap 	while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running)
1054a6d42e7dSPeter Dunlap 		cv_wait(&ic->ic_cv, &ic->ic_mutex);
1055a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
1056a6d42e7dSPeter Dunlap }
1057a6d42e7dSPeter Dunlap 
1058a6d42e7dSPeter Dunlap /*
1059a6d42e7dSPeter Dunlap  * idm_so_conn_disconnect()
1060a6d42e7dSPeter Dunlap  * Shutdown the socket connection and stop the thread
1061a6d42e7dSPeter Dunlap  */
1062a6d42e7dSPeter Dunlap static void
1063a6d42e7dSPeter Dunlap idm_so_conn_disconnect(idm_conn_t *ic)
1064a6d42e7dSPeter Dunlap {
1065a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1066a6d42e7dSPeter Dunlap 
1067a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1068a6d42e7dSPeter Dunlap 
1069a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
1070a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_running = B_FALSE;
1071a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_running = B_FALSE;
1072a6d42e7dSPeter Dunlap 	/* We need to wakeup the TX thread */
1073a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
1074a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
1075a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
1076a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
1077a6d42e7dSPeter Dunlap 
1078a6d42e7dSPeter Dunlap 	/* This should wakeup the RX thread if it is sleeping */
1079a6d42e7dSPeter Dunlap 	idm_soshutdown(so_conn->ic_so);
1080a6d42e7dSPeter Dunlap 
1081a6d42e7dSPeter Dunlap 	thread_join(so_conn->ic_tx_thread_did);
1082a6d42e7dSPeter Dunlap 	thread_join(so_conn->ic_rx_thread_did);
1083a6d42e7dSPeter Dunlap }
1084a6d42e7dSPeter Dunlap 
1085a6d42e7dSPeter Dunlap /*
1086a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_create()
1087a6d42e7dSPeter Dunlap  * Establish a service on an IP address and port.  idm_svc_req_t contains
1088a6d42e7dSPeter Dunlap  * the service parameters.
1089a6d42e7dSPeter Dunlap  */
1090a6d42e7dSPeter Dunlap /*ARGSUSED*/
1091a6d42e7dSPeter Dunlap static idm_status_t
1092a6d42e7dSPeter Dunlap idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is)
1093a6d42e7dSPeter Dunlap {
1094a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1095a6d42e7dSPeter Dunlap 
1096a6d42e7dSPeter Dunlap 	so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP);
1097a6d42e7dSPeter Dunlap 
1098a6d42e7dSPeter Dunlap 	/* Set the new sockets service in svc handle */
1099a6d42e7dSPeter Dunlap 	is->is_so_svc = (void *)so_svc;
1100a6d42e7dSPeter Dunlap 
1101a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1102a6d42e7dSPeter Dunlap }
1103a6d42e7dSPeter Dunlap 
1104a6d42e7dSPeter Dunlap /*
1105a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_destroy()
1106a6d42e7dSPeter Dunlap  * Teardown sockets resources allocated in idm_so_tgt_svc_create()
1107a6d42e7dSPeter Dunlap  */
1108a6d42e7dSPeter Dunlap static void
1109a6d42e7dSPeter Dunlap idm_so_tgt_svc_destroy(idm_svc_t *is)
1110a6d42e7dSPeter Dunlap {
1111a6d42e7dSPeter Dunlap 	/* the socket will have been torn down; free the service */
1112a6d42e7dSPeter Dunlap 	kmem_free(is->is_so_svc, sizeof (idm_so_svc_t));
1113a6d42e7dSPeter Dunlap }
1114a6d42e7dSPeter Dunlap 
1115a6d42e7dSPeter Dunlap /*
1116a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_online()
1117a6d42e7dSPeter Dunlap  * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create()
1118a6d42e7dSPeter Dunlap  */
1119a6d42e7dSPeter Dunlap 
1120a6d42e7dSPeter Dunlap static idm_status_t
1121a6d42e7dSPeter Dunlap idm_so_tgt_svc_online(idm_svc_t *is)
1122a6d42e7dSPeter Dunlap {
1123a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1124a6d42e7dSPeter Dunlap 	idm_svc_req_t		*sr = &is->is_svc_req;
1125a6d42e7dSPeter Dunlap 	struct sockaddr_in6	sin6_ip;
1126a6d42e7dSPeter Dunlap 	const uint32_t		on = 1;
1127a6d42e7dSPeter Dunlap 	const uint32_t		off = 0;
1128a6d42e7dSPeter Dunlap 
1129a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
1130a6d42e7dSPeter Dunlap 	so_svc = (idm_so_svc_t *)is->is_so_svc;
1131a6d42e7dSPeter Dunlap 
1132a6d42e7dSPeter Dunlap 	/*
1133a6d42e7dSPeter Dunlap 	 * Try creating an IPv6 socket first
1134a6d42e7dSPeter Dunlap 	 */
1135a6d42e7dSPeter Dunlap 	if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) {
1136a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
1137a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1138a6d42e7dSPeter Dunlap 	} else {
1139a6d42e7dSPeter Dunlap 		bzero(&sin6_ip, sizeof (sin6_ip));
1140a6d42e7dSPeter Dunlap 		sin6_ip.sin6_family = AF_INET6;
1141a6d42e7dSPeter Dunlap 		sin6_ip.sin6_port = htons(sr->sr_port);
1142a6d42e7dSPeter Dunlap 		sin6_ip.sin6_addr = in6addr_any;
1143a6d42e7dSPeter Dunlap 
11440f1702c5SYu Xiangning 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
11450f1702c5SYu Xiangning 		    SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
1146a6d42e7dSPeter Dunlap 		/*
1147a6d42e7dSPeter Dunlap 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
1148a6d42e7dSPeter Dunlap 		 */
11490f1702c5SYu Xiangning 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
11500f1702c5SYu Xiangning 		    SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED());
1151a6d42e7dSPeter Dunlap 
11520f1702c5SYu Xiangning 		if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip,
11530f1702c5SYu Xiangning 		    sizeof (sin6_ip), CRED()) != 0) {
1154a6d42e7dSPeter Dunlap 			mutex_exit(&is->is_mutex);
1155a6d42e7dSPeter Dunlap 			idm_sodestroy(so_svc->is_so);
1156a6d42e7dSPeter Dunlap 			return (IDM_STATUS_FAIL);
1157a6d42e7dSPeter Dunlap 		}
1158a6d42e7dSPeter Dunlap 	}
1159a6d42e7dSPeter Dunlap 
1160a6d42e7dSPeter Dunlap 	idm_set_tgt_connect_options(so_svc->is_so);
1161a6d42e7dSPeter Dunlap 
11620f1702c5SYu Xiangning 	if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) {
1163a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
1164a6d42e7dSPeter Dunlap 		idm_soshutdown(so_svc->is_so);
1165a6d42e7dSPeter Dunlap 		idm_sodestroy(so_svc->is_so);
1166a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1167a6d42e7dSPeter Dunlap 	}
1168a6d42e7dSPeter Dunlap 
1169a6d42e7dSPeter Dunlap 	/* Launch a watch thread */
1170a6d42e7dSPeter Dunlap 	so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher,
1171a6d42e7dSPeter Dunlap 	    is, 0, &p0, TS_RUN, minclsyspri);
1172a6d42e7dSPeter Dunlap 
1173a6d42e7dSPeter Dunlap 	if (so_svc->is_thread == NULL) {
1174a6d42e7dSPeter Dunlap 		/* Failure to launch; teardown the socket */
1175a6d42e7dSPeter Dunlap 		mutex_exit(&is->is_mutex);
1176a6d42e7dSPeter Dunlap 		idm_soshutdown(so_svc->is_so);
1177a6d42e7dSPeter Dunlap 		idm_sodestroy(so_svc->is_so);
1178a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1179a6d42e7dSPeter Dunlap 	}
11800f1702c5SYu Xiangning 	ksocket_hold(so_svc->is_so);
1181a6d42e7dSPeter Dunlap 	/* Wait for the port watcher thread to start */
1182a6d42e7dSPeter Dunlap 	while (!so_svc->is_thread_running)
1183a6d42e7dSPeter Dunlap 		cv_wait(&is->is_cv, &is->is_mutex);
1184a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
1185a6d42e7dSPeter Dunlap 
1186a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1187a6d42e7dSPeter Dunlap }
1188a6d42e7dSPeter Dunlap 
1189a6d42e7dSPeter Dunlap /*
1190a6d42e7dSPeter Dunlap  * idm_so_tgt_svc_offline
1191a6d42e7dSPeter Dunlap  *
1192a6d42e7dSPeter Dunlap  * Stop listening on the IP address and port identified by idm_svc_t.
1193a6d42e7dSPeter Dunlap  */
1194a6d42e7dSPeter Dunlap static void
1195a6d42e7dSPeter Dunlap idm_so_tgt_svc_offline(idm_svc_t *is)
1196a6d42e7dSPeter Dunlap {
1197a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1198a6d42e7dSPeter Dunlap 	mutex_enter(&is->is_mutex);
1199a6d42e7dSPeter Dunlap 	so_svc = (idm_so_svc_t *)is->is_so_svc;
1200a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_FALSE;
1201a6d42e7dSPeter Dunlap 	mutex_exit(&is->is_mutex);
1202a6d42e7dSPeter Dunlap 
1203a6d42e7dSPeter Dunlap 	/*
12040f1702c5SYu Xiangning 	 * Teardown socket
1205a6d42e7dSPeter Dunlap 	 */
12060f1702c5SYu Xiangning 	idm_sodestroy(so_svc->is_so);
1207a6d42e7dSPeter Dunlap 
1208a6d42e7dSPeter Dunlap 	/*
1209a6d42e7dSPeter Dunlap 	 * Now we expect the port watcher thread to terminate
1210a6d42e7dSPeter Dunlap 	 */
1211a6d42e7dSPeter Dunlap 	thread_join(so_svc->is_thread_did);
1212a6d42e7dSPeter Dunlap }
1213a6d42e7dSPeter Dunlap 
1214a6d42e7dSPeter Dunlap /*
1215a6d42e7dSPeter Dunlap  * Watch thread for target service connection establishment.
1216a6d42e7dSPeter Dunlap  */
1217a6d42e7dSPeter Dunlap void
1218a6d42e7dSPeter Dunlap idm_so_svc_port_watcher(void *arg)
1219a6d42e7dSPeter Dunlap {
1220a6d42e7dSPeter Dunlap 	idm_svc_t		*svc = arg;
12210f1702c5SYu Xiangning 	ksocket_t		new_so;
1222a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
1223a6d42e7dSPeter Dunlap 	idm_status_t		idmrc;
1224a6d42e7dSPeter Dunlap 	idm_so_svc_t		*so_svc;
1225a6d42e7dSPeter Dunlap 	int			rc;
1226a6d42e7dSPeter Dunlap 	const uint32_t		off = 0;
12270f1702c5SYu Xiangning 	struct sockaddr_in6 	t_addr;
12280f1702c5SYu Xiangning 	socklen_t		t_addrlen;
1229a6d42e7dSPeter Dunlap 
12300f1702c5SYu Xiangning 	bzero(&t_addr, sizeof (struct sockaddr_in6));
12310f1702c5SYu Xiangning 	t_addrlen = sizeof (struct sockaddr_in6);
1232a6d42e7dSPeter Dunlap 	mutex_enter(&svc->is_mutex);
1233a6d42e7dSPeter Dunlap 
1234a6d42e7dSPeter Dunlap 	so_svc = svc->is_so_svc;
1235a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_TRUE;
1236a6d42e7dSPeter Dunlap 	so_svc->is_thread_did = so_svc->is_thread->t_did;
1237a6d42e7dSPeter Dunlap 
1238a6d42e7dSPeter Dunlap 	cv_signal(&svc->is_cv);
1239a6d42e7dSPeter Dunlap 
1240a6d42e7dSPeter Dunlap 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc,
1241a6d42e7dSPeter Dunlap 	    svc->is_svc_req.sr_port);
1242a6d42e7dSPeter Dunlap 
1243a6d42e7dSPeter Dunlap 	while (so_svc->is_thread_running) {
1244a6d42e7dSPeter Dunlap 		mutex_exit(&svc->is_mutex);
1245a6d42e7dSPeter Dunlap 
12460f1702c5SYu Xiangning 		if ((rc = ksocket_accept(so_svc->is_so,
12470f1702c5SYu Xiangning 		    (struct sockaddr *)&t_addr, &t_addrlen,
12480f1702c5SYu Xiangning 		    &new_so, CRED())) != 0) {
1249a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1250a6d42e7dSPeter Dunlap 			if (rc == ECONNABORTED)
1251a6d42e7dSPeter Dunlap 				continue;
1252a6d42e7dSPeter Dunlap 			/* Connection problem */
1253a6d42e7dSPeter Dunlap 			break;
1254a6d42e7dSPeter Dunlap 		}
1255a6d42e7dSPeter Dunlap 		/*
1256a6d42e7dSPeter Dunlap 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
1257a6d42e7dSPeter Dunlap 		 */
12580f1702c5SYu Xiangning 		(void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT,
12590f1702c5SYu Xiangning 		    (char *)&off, sizeof (off), CRED());
1260a6d42e7dSPeter Dunlap 
1261a6d42e7dSPeter Dunlap 		idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS,
1262a6d42e7dSPeter Dunlap 		    &ic);
1263a6d42e7dSPeter Dunlap 		if (idmrc != IDM_STATUS_SUCCESS) {
1264a6d42e7dSPeter Dunlap 			/* Drop connection */
1265a6d42e7dSPeter Dunlap 			idm_soshutdown(new_so);
1266a6d42e7dSPeter Dunlap 			idm_sodestroy(new_so);
1267a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1268a6d42e7dSPeter Dunlap 			continue;
1269a6d42e7dSPeter Dunlap 		}
1270a6d42e7dSPeter Dunlap 
1271a6d42e7dSPeter Dunlap 		idmrc = idm_so_tgt_conn_create(ic, new_so);
1272a6d42e7dSPeter Dunlap 		if (idmrc != IDM_STATUS_SUCCESS) {
1273a6d42e7dSPeter Dunlap 			idm_svc_conn_destroy(ic);
1274a6d42e7dSPeter Dunlap 			idm_soshutdown(new_so);
1275a6d42e7dSPeter Dunlap 			idm_sodestroy(new_so);
1276a6d42e7dSPeter Dunlap 			mutex_enter(&svc->is_mutex);
1277a6d42e7dSPeter Dunlap 			continue;
1278a6d42e7dSPeter Dunlap 		}
1279a6d42e7dSPeter Dunlap 
1280a6d42e7dSPeter Dunlap 		/*
1281a6d42e7dSPeter Dunlap 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
1282a6d42e7dSPeter Dunlap 		 * will notify the client (target) about the new connection.
1283a6d42e7dSPeter Dunlap 		 */
1284a6d42e7dSPeter Dunlap 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
1285a6d42e7dSPeter Dunlap 
1286a6d42e7dSPeter Dunlap 		mutex_enter(&svc->is_mutex);
1287a6d42e7dSPeter Dunlap 	}
12880f1702c5SYu Xiangning 	ksocket_rele(so_svc->is_so);
1289a6d42e7dSPeter Dunlap 	so_svc->is_thread_running = B_FALSE;
1290a6d42e7dSPeter Dunlap 	mutex_exit(&svc->is_mutex);
1291a6d42e7dSPeter Dunlap 
1292a6d42e7dSPeter Dunlap 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc,
1293a6d42e7dSPeter Dunlap 	    svc->is_svc_req.sr_port);
1294a6d42e7dSPeter Dunlap 
1295a6d42e7dSPeter Dunlap 	thread_exit();
1296a6d42e7dSPeter Dunlap }
1297a6d42e7dSPeter Dunlap 
1298a6d42e7dSPeter Dunlap /*
1299a6d42e7dSPeter Dunlap  * idm_so_free_task_rsrc() stops any ongoing processing of the task and
1300a6d42e7dSPeter Dunlap  * frees resources associated with the task.
1301a6d42e7dSPeter Dunlap  *
1302a6d42e7dSPeter Dunlap  * It's not clear that this should return idm_status_t.  What do we do
1303a6d42e7dSPeter Dunlap  * if it fails?
1304a6d42e7dSPeter Dunlap  */
1305a6d42e7dSPeter Dunlap static idm_status_t
1306a6d42e7dSPeter Dunlap idm_so_free_task_rsrc(idm_task_t *idt)
1307a6d42e7dSPeter Dunlap {
1308a6d42e7dSPeter Dunlap 	idm_buf_t	*idb;
1309a6d42e7dSPeter Dunlap 
131030e7468fSPeter Dunlap 	/*
131130e7468fSPeter Dunlap 	 * There is nothing to cleanup on initiator connections
131230e7468fSPeter Dunlap 	 */
131330e7468fSPeter Dunlap 	if (IDM_CONN_ISINI(idt->idt_ic))
131430e7468fSPeter Dunlap 		return (IDM_STATUS_SUCCESS);
131530e7468fSPeter Dunlap 
1316a6d42e7dSPeter Dunlap 	/*
1317a6d42e7dSPeter Dunlap 	 * If this is a target connection, call idm_buf_rx_from_ini_done for
1318a6d42e7dSPeter Dunlap 	 * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE.
1319a6d42e7dSPeter Dunlap 	 *
1320a6d42e7dSPeter Dunlap 	 * In addition, remove any buffers associated with this task from
1321a6d42e7dSPeter Dunlap 	 * the ic_tx_list.  We'll do this by walking the idt_inbufv list, but
1322a6d42e7dSPeter Dunlap 	 * items don't actually get removed from that list (and completion
1323a6d42e7dSPeter Dunlap 	 * routines called) until idm_task_cleanup.
1324a6d42e7dSPeter Dunlap 	 */
1325a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1326a6d42e7dSPeter Dunlap 
1327a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_outbufv); idb != NULL;
1328a6d42e7dSPeter Dunlap 	    idb = list_next(&idt->idt_outbufv, idb)) {
1329a6d42e7dSPeter Dunlap 		if (idb->idb_in_transport) {
1330a6d42e7dSPeter Dunlap 			/*
1331a6d42e7dSPeter Dunlap 			 * idm_buf_rx_from_ini_done releases idt->idt_mutex
1332a6d42e7dSPeter Dunlap 			 */
1333a668b114SPriya Krishnan 			DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
1334a668b114SPriya Krishnan 			    uintptr_t, idb->idb_buf,
1335a668b114SPriya Krishnan 			    uint32_t, idb->idb_bufoffset,
1336a668b114SPriya Krishnan 			    uint64_t, 0, uint32_t, 0, uint32_t, 0,
1337a668b114SPriya Krishnan 			    uint32_t, idb->idb_xfer_len,
1338a668b114SPriya Krishnan 			    int, XFER_BUF_RX_FROM_INI);
1339a6d42e7dSPeter Dunlap 			idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED);
1340a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
1341a6d42e7dSPeter Dunlap 		}
1342a6d42e7dSPeter Dunlap 	}
1343a6d42e7dSPeter Dunlap 
1344a6d42e7dSPeter Dunlap 	for (idb = list_head(&idt->idt_inbufv); idb != NULL;
1345a6d42e7dSPeter Dunlap 	    idb = list_next(&idt->idt_inbufv, idb)) {
1346a6d42e7dSPeter Dunlap 		/*
1347a6d42e7dSPeter Dunlap 		 * We want to remove these items from the tx_list as well,
1348a6d42e7dSPeter Dunlap 		 * but knowing it's in the idt_inbufv list is not a guarantee
1349a6d42e7dSPeter Dunlap 		 * that it's in the tx_list.  If it's on the tx list then
1350a6d42e7dSPeter Dunlap 		 * let idm_sotx_thread() clean it up.
1351a6d42e7dSPeter Dunlap 		 */
1352a6d42e7dSPeter Dunlap 		if (idb->idb_in_transport && !idb->idb_tx_thread) {
1353a6d42e7dSPeter Dunlap 			/*
1354a6d42e7dSPeter Dunlap 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
1355a6d42e7dSPeter Dunlap 			 */
1356a668b114SPriya Krishnan 			DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
1357a668b114SPriya Krishnan 			    uintptr_t, idb->idb_buf,
1358a668b114SPriya Krishnan 			    uint32_t, idb->idb_bufoffset,
1359a668b114SPriya Krishnan 			    uint64_t, 0, uint32_t, 0, uint32_t, 0,
1360a668b114SPriya Krishnan 			    uint32_t, idb->idb_xfer_len,
1361a668b114SPriya Krishnan 			    int, XFER_BUF_TX_TO_INI);
1362a6d42e7dSPeter Dunlap 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
1363a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
1364a6d42e7dSPeter Dunlap 		}
1365a6d42e7dSPeter Dunlap 	}
1366a6d42e7dSPeter Dunlap 
1367a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1368a6d42e7dSPeter Dunlap 
1369a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1370a6d42e7dSPeter Dunlap }
1371a6d42e7dSPeter Dunlap 
1372a6d42e7dSPeter Dunlap /*
1373a6d42e7dSPeter Dunlap  * idm_so_negotiate_key_values() validates the key values for this connection
1374a6d42e7dSPeter Dunlap  */
1375a6d42e7dSPeter Dunlap /* ARGSUSED */
1376a6d42e7dSPeter Dunlap static kv_status_t
1377a6d42e7dSPeter Dunlap idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl,
1378a6d42e7dSPeter Dunlap     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
1379a6d42e7dSPeter Dunlap {
1380a6d42e7dSPeter Dunlap 	/* All parameters are negotiated at the iscsit level */
1381a6d42e7dSPeter Dunlap 	return (KV_HANDLED);
1382a6d42e7dSPeter Dunlap }
1383a6d42e7dSPeter Dunlap 
1384a6d42e7dSPeter Dunlap /*
1385a6d42e7dSPeter Dunlap  * idm_so_notice_key_values() activates the negotiated key values for
1386a6d42e7dSPeter Dunlap  * this connection.
1387a6d42e7dSPeter Dunlap  */
138830e7468fSPeter Dunlap static void
1389a6d42e7dSPeter Dunlap idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl)
1390a6d42e7dSPeter Dunlap {
1391a6d42e7dSPeter Dunlap 	char			*nvp_name;
1392a6d42e7dSPeter Dunlap 	nvpair_t		*nvp;
1393a6d42e7dSPeter Dunlap 	nvpair_t		*next_nvp;
1394a6d42e7dSPeter Dunlap 	int			nvrc;
1395a6d42e7dSPeter Dunlap 	idm_status_t		idm_status;
1396a6d42e7dSPeter Dunlap 	const idm_kv_xlate_t	*ikvx;
1397*56261083SCharles Ting 	uint64_t		num_val;
1398a6d42e7dSPeter Dunlap 
1399a6d42e7dSPeter Dunlap 	for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL);
1400a6d42e7dSPeter Dunlap 	    nvp != NULL; nvp = next_nvp) {
1401a6d42e7dSPeter Dunlap 		next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp);
1402a6d42e7dSPeter Dunlap 		nvp_name = nvpair_name(nvp);
1403a6d42e7dSPeter Dunlap 
1404a6d42e7dSPeter Dunlap 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
1405a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1406a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1407a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1408a6d42e7dSPeter Dunlap 			idm_status = idm_so_handle_digest(it, nvp, ikvx);
1409a6d42e7dSPeter Dunlap 			ASSERT(idm_status == 0);
1410a6d42e7dSPeter Dunlap 
1411a6d42e7dSPeter Dunlap 			/* Remove processed item from negotiated_nvl list */
1412a6d42e7dSPeter Dunlap 			nvrc = nvlist_remove_all(
1413a6d42e7dSPeter Dunlap 			    negotiated_nvl, ikvx->ik_key_name);
1414a6d42e7dSPeter Dunlap 			ASSERT(nvrc == 0);
1415a6d42e7dSPeter Dunlap 			break;
1416*56261083SCharles Ting 		case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
1417*56261083SCharles Ting 			/*
1418*56261083SCharles Ting 			 * Just pass the value down to idm layer.
1419*56261083SCharles Ting 			 * No need to remove it from negotiated_nvl list here.
1420*56261083SCharles Ting 			 */
1421*56261083SCharles Ting 			nvrc = nvpair_value_uint64(nvp, &num_val);
1422*56261083SCharles Ting 			ASSERT(nvrc == 0);
1423*56261083SCharles Ting 			it->ic_conn_params.max_xmit_dataseglen =
1424*56261083SCharles Ting 			    (uint32_t)num_val;
1425*56261083SCharles Ting 			break;
1426a6d42e7dSPeter Dunlap 		default:
1427a6d42e7dSPeter Dunlap 			break;
1428a6d42e7dSPeter Dunlap 		}
1429a6d42e7dSPeter Dunlap 	}
1430a6d42e7dSPeter Dunlap }
1431a6d42e7dSPeter Dunlap 
1432*56261083SCharles Ting /*
1433*56261083SCharles Ting  * idm_so_declare_key_values() declares the key values for this connection
1434*56261083SCharles Ting  */
1435*56261083SCharles Ting /* ARGSUSED */
1436*56261083SCharles Ting static kv_status_t
1437*56261083SCharles Ting idm_so_declare_key_values(idm_conn_t *it, nvlist_t *config_nvl,
1438*56261083SCharles Ting     nvlist_t *outgoing_nvl)
1439*56261083SCharles Ting {
1440*56261083SCharles Ting 	char			*nvp_name;
1441*56261083SCharles Ting 	nvpair_t		*nvp;
1442*56261083SCharles Ting 	nvpair_t		*next_nvp;
1443*56261083SCharles Ting 	kv_status_t		kvrc;
1444*56261083SCharles Ting 	int			nvrc = 0;
1445*56261083SCharles Ting 	const idm_kv_xlate_t	*ikvx;
1446*56261083SCharles Ting 	uint64_t		num_val;
1447*56261083SCharles Ting 
1448*56261083SCharles Ting 	for (nvp = nvlist_next_nvpair(config_nvl, NULL);
1449*56261083SCharles Ting 	    nvp != NULL && nvrc == 0; nvp = next_nvp) {
1450*56261083SCharles Ting 		next_nvp = nvlist_next_nvpair(config_nvl, nvp);
1451*56261083SCharles Ting 		nvp_name = nvpair_name(nvp);
1452*56261083SCharles Ting 
1453*56261083SCharles Ting 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
1454*56261083SCharles Ting 		switch (ikvx->ik_key_id) {
1455*56261083SCharles Ting 		case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
1456*56261083SCharles Ting 			if ((nvrc = nvpair_value_uint64(nvp, &num_val)) != 0) {
1457*56261083SCharles Ting 				break;
1458*56261083SCharles Ting 			}
1459*56261083SCharles Ting 			if (outgoing_nvl &&
1460*56261083SCharles Ting 			    (nvrc = nvlist_add_uint64(outgoing_nvl,
1461*56261083SCharles Ting 			    nvp_name, num_val)) != 0) {
1462*56261083SCharles Ting 				break;
1463*56261083SCharles Ting 			}
1464*56261083SCharles Ting 			it->ic_conn_params.max_recv_dataseglen =
1465*56261083SCharles Ting 			    (uint32_t)num_val;
1466*56261083SCharles Ting 			break;
1467*56261083SCharles Ting 		default:
1468*56261083SCharles Ting 			break;
1469*56261083SCharles Ting 		}
1470*56261083SCharles Ting 	}
1471*56261083SCharles Ting 	kvrc = idm_nvstat_to_kvstat(nvrc);
1472*56261083SCharles Ting 	return (kvrc);
1473*56261083SCharles Ting }
1474a6d42e7dSPeter Dunlap 
1475a6d42e7dSPeter Dunlap static idm_status_t
1476a6d42e7dSPeter Dunlap idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice,
1477a6d42e7dSPeter Dunlap     const idm_kv_xlate_t *ikvx)
1478a6d42e7dSPeter Dunlap {
1479a6d42e7dSPeter Dunlap 	int			nvrc;
1480a6d42e7dSPeter Dunlap 	char			*digest_choice_string;
1481a6d42e7dSPeter Dunlap 
1482a6d42e7dSPeter Dunlap 	nvrc = nvpair_value_string(digest_choice,
1483a6d42e7dSPeter Dunlap 	    &digest_choice_string);
1484a6d42e7dSPeter Dunlap 	ASSERT(nvrc == 0);
1485a6d42e7dSPeter Dunlap 	if (strcasecmp(digest_choice_string, "crc32c") == 0) {
1486a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1487a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1488a6d42e7dSPeter Dunlap 			it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST;
1489a6d42e7dSPeter Dunlap 			break;
1490a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1491a6d42e7dSPeter Dunlap 			it->ic_conn_flags |= IDM_CONN_DATA_DIGEST;
1492a6d42e7dSPeter Dunlap 			break;
1493a6d42e7dSPeter Dunlap 		default:
1494a6d42e7dSPeter Dunlap 			ASSERT(0);
1495a6d42e7dSPeter Dunlap 			break;
1496a6d42e7dSPeter Dunlap 		}
1497a6d42e7dSPeter Dunlap 	} else if (strcasecmp(digest_choice_string, "none") == 0) {
1498a6d42e7dSPeter Dunlap 		switch (ikvx->ik_key_id) {
1499a6d42e7dSPeter Dunlap 		case KI_HEADER_DIGEST:
1500a6d42e7dSPeter Dunlap 			it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST;
1501a6d42e7dSPeter Dunlap 			break;
1502a6d42e7dSPeter Dunlap 		case KI_DATA_DIGEST:
1503a6d42e7dSPeter Dunlap 			it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST;
1504a6d42e7dSPeter Dunlap 			break;
1505a6d42e7dSPeter Dunlap 		default:
1506a6d42e7dSPeter Dunlap 			ASSERT(0);
1507a6d42e7dSPeter Dunlap 			break;
1508a6d42e7dSPeter Dunlap 		}
1509a6d42e7dSPeter Dunlap 	} else {
1510a6d42e7dSPeter Dunlap 		ASSERT(0);
1511a6d42e7dSPeter Dunlap 	}
1512a6d42e7dSPeter Dunlap 
1513a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1514a6d42e7dSPeter Dunlap }
1515a6d42e7dSPeter Dunlap 
1516a6d42e7dSPeter Dunlap 
1517a6d42e7dSPeter Dunlap /*
1518a6d42e7dSPeter Dunlap  * idm_so_conn_is_capable() verifies that the passed connection is provided
1519a6d42e7dSPeter Dunlap  * for by the sockets interface.
1520a6d42e7dSPeter Dunlap  */
1521a6d42e7dSPeter Dunlap /* ARGSUSED */
1522a6d42e7dSPeter Dunlap static boolean_t
1523a6d42e7dSPeter Dunlap idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps)
1524a6d42e7dSPeter Dunlap {
1525a6d42e7dSPeter Dunlap 	return (B_TRUE);
1526a6d42e7dSPeter Dunlap }
1527a6d42e7dSPeter Dunlap 
1528a6d42e7dSPeter Dunlap /*
1529a6d42e7dSPeter Dunlap  * idm_so_rx_datain() validates the Data Sequence number of the PDU. The
1530a6d42e7dSPeter Dunlap  * idm_sorecv_scsidata() function invoked earlier actually reads the data
1531a6d42e7dSPeter Dunlap  * off the socket into the appropriate buffers.
1532a6d42e7dSPeter Dunlap  */
1533a6d42e7dSPeter Dunlap static void
1534a6d42e7dSPeter Dunlap idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu)
1535a6d42e7dSPeter Dunlap {
1536a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1537a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1538a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1539a6d42e7dSPeter Dunlap 	uint32_t		datasn;
1540a6d42e7dSPeter Dunlap 	size_t			offset;
1541a6d42e7dSPeter Dunlap 	iscsi_hdr_t		*ihp = (iscsi_hdr_t *)pdu->isp_hdr;
1542a6d42e7dSPeter Dunlap 	iscsi_data_rsp_hdr_t    *idrhp = (iscsi_data_rsp_hdr_t *)ihp;
1543a6d42e7dSPeter Dunlap 
1544a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1545a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1546a6d42e7dSPeter Dunlap 
1547a6d42e7dSPeter Dunlap 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
1548a6d42e7dSPeter Dunlap 	datasn	= ntohl(bhs->datasn);
1549a6d42e7dSPeter Dunlap 	offset	= ntohl(bhs->offset);
1550a6d42e7dSPeter Dunlap 
1551a6d42e7dSPeter Dunlap 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP);
1552a6d42e7dSPeter Dunlap 
1553a6d42e7dSPeter Dunlap 	/*
1554a6d42e7dSPeter Dunlap 	 * Look up the task corresponding to the initiator task tag
1555a6d42e7dSPeter Dunlap 	 * to get the buffers affiliated with the task.
1556a6d42e7dSPeter Dunlap 	 */
1557a6d42e7dSPeter Dunlap 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
1558a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1559a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task");
1560a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1561a6d42e7dSPeter Dunlap 		return;
1562a6d42e7dSPeter Dunlap 	}
1563a6d42e7dSPeter Dunlap 
1564a6d42e7dSPeter Dunlap 	idb = pdu->isp_sorx_buf;
1565a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1566a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1567a6d42e7dSPeter Dunlap 		    "idm_so_rx_datain: failed to find buffer");
1568a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1569a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1570a6d42e7dSPeter Dunlap 		return;
1571a6d42e7dSPeter Dunlap 	}
1572a6d42e7dSPeter Dunlap 
1573a6d42e7dSPeter Dunlap 	/*
1574a6d42e7dSPeter Dunlap 	 * DataSN values should be sequential and should not have any gaps or
1575a6d42e7dSPeter Dunlap 	 * repetitions. Check the DataSN with the one stored in the task.
1576a6d42e7dSPeter Dunlap 	 */
1577a6d42e7dSPeter Dunlap 	if (datasn == idt->idt_exp_datasn) {
1578a6d42e7dSPeter Dunlap 		idt->idt_exp_datasn++; /* keep track of DataSN received */
1579a6d42e7dSPeter Dunlap 	} else {
1580a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order");
1581a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1582a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1583a6d42e7dSPeter Dunlap 		return;
1584a6d42e7dSPeter Dunlap 	}
1585a6d42e7dSPeter Dunlap 
1586a6d42e7dSPeter Dunlap 	/*
1587a6d42e7dSPeter Dunlap 	 * PDUs in a sequence should be in continuously increasing
1588a6d42e7dSPeter Dunlap 	 * address offset
1589a6d42e7dSPeter Dunlap 	 */
1590a6d42e7dSPeter Dunlap 	if (offset != idb->idb_exp_offset) {
1591a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset");
159230e7468fSPeter Dunlap 		idm_task_rele(idt);
1593a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1594a6d42e7dSPeter Dunlap 		return;
1595a6d42e7dSPeter Dunlap 	}
1596a6d42e7dSPeter Dunlap 	/* Expected next relative buffer offset */
1597a6d42e7dSPeter Dunlap 	idb->idb_exp_offset += n2h24(bhs->dlength);
159830e7468fSPeter Dunlap 	idt->idt_rx_bytes += n2h24(bhs->dlength);
159930e7468fSPeter Dunlap 
160030e7468fSPeter Dunlap 	idm_task_rele(idt);
1601a6d42e7dSPeter Dunlap 
1602a6d42e7dSPeter Dunlap 	/*
1603a6d42e7dSPeter Dunlap 	 * For now call scsi_rsp which will process the data rsp
1604a6d42e7dSPeter Dunlap 	 * Revisit, need to provide an explicit client entry point for
1605a6d42e7dSPeter Dunlap 	 * phase collapse completions.
1606a6d42e7dSPeter Dunlap 	 */
1607a6d42e7dSPeter Dunlap 	if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) &&
1608a6d42e7dSPeter Dunlap 	    (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) {
1609a6d42e7dSPeter Dunlap 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
1610a6d42e7dSPeter Dunlap 	}
1611a6d42e7dSPeter Dunlap 
1612a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1613a6d42e7dSPeter Dunlap }
1614a6d42e7dSPeter Dunlap 
1615a6d42e7dSPeter Dunlap /*
1616a6d42e7dSPeter Dunlap  * The idm_so_rx_dataout() function is used by the iSCSI target to read
1617a6d42e7dSPeter Dunlap  * data from the Data-Out PDU sent by the iSCSI initiator.
1618a6d42e7dSPeter Dunlap  *
1619a6d42e7dSPeter Dunlap  * This function gets the Initiator Task Tag from the PDU BHS and looks up the
1620a6d42e7dSPeter Dunlap  * task to get the buffers associated with the PDU. A PDU might span buffers.
1621a6d42e7dSPeter Dunlap  * The data is then read into the respective buffer.
1622a6d42e7dSPeter Dunlap  */
1623a6d42e7dSPeter Dunlap static void
1624a6d42e7dSPeter Dunlap idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu)
1625a6d42e7dSPeter Dunlap {
1626a6d42e7dSPeter Dunlap 
1627a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1628a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1629a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1630a6d42e7dSPeter Dunlap 	size_t			offset;
1631a6d42e7dSPeter Dunlap 
1632a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1633a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1634a6d42e7dSPeter Dunlap 
1635a6d42e7dSPeter Dunlap 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
1636a6d42e7dSPeter Dunlap 	offset = ntohl(bhs->offset);
1637a6d42e7dSPeter Dunlap 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA);
1638a6d42e7dSPeter Dunlap 
1639a6d42e7dSPeter Dunlap 	/*
1640a6d42e7dSPeter Dunlap 	 * Look up the task corresponding to the initiator task tag
1641a6d42e7dSPeter Dunlap 	 * to get the buffers affiliated with the task.
1642a6d42e7dSPeter Dunlap 	 */
1643a6d42e7dSPeter Dunlap 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
1644a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1645a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1646a6d42e7dSPeter Dunlap 		    "idm_so_rx_dataout: failed to find task");
1647a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1648a6d42e7dSPeter Dunlap 		return;
1649a6d42e7dSPeter Dunlap 	}
1650a6d42e7dSPeter Dunlap 
1651a6d42e7dSPeter Dunlap 	idb = pdu->isp_sorx_buf;
1652a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1653a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1654a6d42e7dSPeter Dunlap 		    "idm_so_rx_dataout: failed to find buffer");
1655a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1656a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1657a6d42e7dSPeter Dunlap 		return;
1658a6d42e7dSPeter Dunlap 	}
1659a6d42e7dSPeter Dunlap 
1660a6d42e7dSPeter Dunlap 	/* Keep track of data transferred - check data offsets */
1661a6d42e7dSPeter Dunlap 	if (offset != idb->idb_exp_offset) {
1662a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: "
1663a6d42e7dSPeter Dunlap 		    "%ld, %d", offset, idb->idb_exp_offset);
1664a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1665a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1666a6d42e7dSPeter Dunlap 		return;
1667a6d42e7dSPeter Dunlap 	}
1668a6d42e7dSPeter Dunlap 	/* Expected next relative offset */
1669a6d42e7dSPeter Dunlap 	idb->idb_exp_offset += ntoh24(bhs->dlength);
167030e7468fSPeter Dunlap 	idt->idt_rx_bytes += n2h24(bhs->dlength);
1671a6d42e7dSPeter Dunlap 
1672a6d42e7dSPeter Dunlap 	/*
1673a6d42e7dSPeter Dunlap 	 * Call the buffer callback when the transfer is complete
1674a6d42e7dSPeter Dunlap 	 *
1675a6d42e7dSPeter Dunlap 	 * The connection state machine should only abort tasks after
1676a6d42e7dSPeter Dunlap 	 * shutting down the connection so we are assured that there
1677a6d42e7dSPeter Dunlap 	 * won't be a simultaneous attempt to abort this task at the
1678a6d42e7dSPeter Dunlap 	 * same time as we are processing this PDU (due to a connection
1679a6d42e7dSPeter Dunlap 	 * state change).
1680a6d42e7dSPeter Dunlap 	 */
1681a6d42e7dSPeter Dunlap 	if (bhs->flags & ISCSI_FLAG_FINAL) {
1682a6d42e7dSPeter Dunlap 		/*
1683a6d42e7dSPeter Dunlap 		 * We only want to call idm_buf_rx_from_ini_done once
1684a6d42e7dSPeter Dunlap 		 * per transfer.  It's possible that this task has
1685a6d42e7dSPeter Dunlap 		 * already been aborted in which case
1686a6d42e7dSPeter Dunlap 		 * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done
1687a6d42e7dSPeter Dunlap 		 * for each buffer with idb_in_transport==B_TRUE.  To
1688a6d42e7dSPeter Dunlap 		 * close this window and ensure that this doesn't happen,
1689a6d42e7dSPeter Dunlap 		 * we'll clear idb->idb_in_transport now while holding
1690a6d42e7dSPeter Dunlap 		 * the task mutex.   This is only really an issue for
1691a6d42e7dSPeter Dunlap 		 * SCSI task abort -- if tasks were being aborted because
1692a6d42e7dSPeter Dunlap 		 * of a connection state change the state machine would
1693a6d42e7dSPeter Dunlap 		 * have already stopped the receive thread.
1694a6d42e7dSPeter Dunlap 		 */
1695a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
1696a6d42e7dSPeter Dunlap 
1697a6d42e7dSPeter Dunlap 		/*
1698a6d42e7dSPeter Dunlap 		 * Release the task hold here (obtained in idm_task_find)
1699a6d42e7dSPeter Dunlap 		 * because the task may complete synchronously during
1700a6d42e7dSPeter Dunlap 		 * idm_buf_rx_from_ini_done.  Since we still have an active
1701a6d42e7dSPeter Dunlap 		 * buffer we know there is at least one additional hold on idt.
1702a6d42e7dSPeter Dunlap 		 */
1703a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1704a6d42e7dSPeter Dunlap 
1705a6d42e7dSPeter Dunlap 		/*
1706a6d42e7dSPeter Dunlap 		 * idm_buf_rx_from_ini_done releases idt->idt_mutex
1707a6d42e7dSPeter Dunlap 		 */
1708a668b114SPriya Krishnan 		DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
1709a668b114SPriya Krishnan 		    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
1710a668b114SPriya Krishnan 		    uint64_t, 0, uint32_t, 0, uint32_t, 0,
1711a668b114SPriya Krishnan 		    uint32_t, idb->idb_xfer_len,
1712a668b114SPriya Krishnan 		    int, XFER_BUF_RX_FROM_INI);
1713a6d42e7dSPeter Dunlap 		idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS);
1714a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1715a6d42e7dSPeter Dunlap 		return;
1716a6d42e7dSPeter Dunlap 	}
1717a6d42e7dSPeter Dunlap 
1718a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
1719a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1720a6d42e7dSPeter Dunlap }
1721a6d42e7dSPeter Dunlap 
1722a6d42e7dSPeter Dunlap /*
1723a6d42e7dSPeter Dunlap  * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle
1724a6d42e7dSPeter Dunlap  * the R2T PDU sent by the iSCSI target indicating that it is ready to
1725a6d42e7dSPeter Dunlap  * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS
1726a6d42e7dSPeter Dunlap  * and looks up the task in the task tree using the itt to get the output
1727a6d42e7dSPeter Dunlap  * buffers associated the task. The R2T PDU contains the offset of the
1728a6d42e7dSPeter Dunlap  * requested data and the data length. This function then constructs a
1729a6d42e7dSPeter Dunlap  * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out
1730a6d42e7dSPeter Dunlap  * PDU is associated with the R2T by the Target Transfer Tag  (ttt).
1731a6d42e7dSPeter Dunlap  */
173230e7468fSPeter Dunlap 
1733a6d42e7dSPeter Dunlap static void
1734a6d42e7dSPeter Dunlap idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu)
1735a6d42e7dSPeter Dunlap {
1736a6d42e7dSPeter Dunlap 	idm_task_t		*idt;
1737a6d42e7dSPeter Dunlap 	idm_buf_t		*idb;
1738a6d42e7dSPeter Dunlap 	iscsi_rtt_hdr_t		*rtt_hdr;
1739a6d42e7dSPeter Dunlap 	uint32_t		data_offset;
174030e7468fSPeter Dunlap 	uint32_t		data_length;
1741a6d42e7dSPeter Dunlap 
1742a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1743a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1744a6d42e7dSPeter Dunlap 
1745a6d42e7dSPeter Dunlap 	rtt_hdr	= (iscsi_rtt_hdr_t *)pdu->isp_hdr;
1746a6d42e7dSPeter Dunlap 	data_offset = ntohl(rtt_hdr->data_offset);
174730e7468fSPeter Dunlap 	data_length = ntohl(rtt_hdr->data_length);
1748a6d42e7dSPeter Dunlap 	idt	= idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt);
1749a6d42e7dSPeter Dunlap 
1750a6d42e7dSPeter Dunlap 	if (idt == NULL) {
1751a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task");
1752a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1753a6d42e7dSPeter Dunlap 		return;
1754a6d42e7dSPeter Dunlap 	}
1755a6d42e7dSPeter Dunlap 
1756a6d42e7dSPeter Dunlap 	/* Find the buffer bound to the task by the iSCSI initiator */
1757a6d42e7dSPeter Dunlap 	mutex_enter(&idt->idt_mutex);
1758a6d42e7dSPeter Dunlap 	idb = idm_buf_find(&idt->idt_outbufv, data_offset);
1759a6d42e7dSPeter Dunlap 	if (idb == NULL) {
1760a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
1761a6d42e7dSPeter Dunlap 		idm_task_rele(idt);
1762a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer");
1763a6d42e7dSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
1764a6d42e7dSPeter Dunlap 		return;
1765a6d42e7dSPeter Dunlap 	}
1766a6d42e7dSPeter Dunlap 
176730e7468fSPeter Dunlap 	/* return buffer contains this data */
176830e7468fSPeter Dunlap 	if (data_offset + data_length > idb->idb_buflen) {
176930e7468fSPeter Dunlap 		/* Overflow */
177030e7468fSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
177130e7468fSPeter Dunlap 		idm_task_rele(idt);
177230e7468fSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside "
177330e7468fSPeter Dunlap 		    "buffer");
177430e7468fSPeter Dunlap 		idm_pdu_rx_protocol_error(ic, pdu);
177530e7468fSPeter Dunlap 		return;
177630e7468fSPeter Dunlap 	}
177730e7468fSPeter Dunlap 
177830e7468fSPeter Dunlap 	idt->idt_r2t_ttt = rtt_hdr->ttt;
177930e7468fSPeter Dunlap 	idt->idt_exp_datasn = 0;
178030e7468fSPeter Dunlap 
178130e7468fSPeter Dunlap 	idm_so_send_rtt_data(ic, idt, idb, data_offset,
178230e7468fSPeter Dunlap 	    ntohl(rtt_hdr->data_length));
1783a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
1784a6d42e7dSPeter Dunlap 
1785a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
1786a6d42e7dSPeter Dunlap 	idm_task_rele(idt);
1787a6d42e7dSPeter Dunlap 
1788a6d42e7dSPeter Dunlap }
1789a6d42e7dSPeter Dunlap 
1790a6d42e7dSPeter Dunlap idm_status_t
1791a6d42e7dSPeter Dunlap idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu)
1792a6d42e7dSPeter Dunlap {
1793a6d42e7dSPeter Dunlap 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
1794a6d42e7dSPeter Dunlap 	int		pad_len;
1795a6d42e7dSPeter Dunlap 	uint32_t	data_digest_crc;
1796a6d42e7dSPeter Dunlap 	uint32_t	crc_calculated;
1797a6d42e7dSPeter Dunlap 	int		total_len;
1798a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1799a6d42e7dSPeter Dunlap 
1800a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1801a6d42e7dSPeter Dunlap 
1802a6d42e7dSPeter Dunlap 	pad_len = ((ISCSI_PAD_WORD_LEN -
1803a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
1804a6d42e7dSPeter Dunlap 	    (ISCSI_PAD_WORD_LEN - 1));
1805a6d42e7dSPeter Dunlap 
1806a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */
1807a6d42e7dSPeter Dunlap 
1808a6d42e7dSPeter Dunlap 	total_len = pdu->isp_datalen;
1809a6d42e7dSPeter Dunlap 
1810a6d42e7dSPeter Dunlap 	if (pad_len) {
1811a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_base	= (char *)&pad;
1812a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_len	= pad_len;
1813a6d42e7dSPeter Dunlap 		total_len		+= pad_len;
1814a6d42e7dSPeter Dunlap 		pdu->isp_iovlen++;
1815a6d42e7dSPeter Dunlap 	}
1816a6d42e7dSPeter Dunlap 
1817a6d42e7dSPeter Dunlap 	/* setup data digest */
1818a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
1819a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_base =
1820a6d42e7dSPeter Dunlap 		    (char *)&data_digest_crc;
1821a6d42e7dSPeter Dunlap 		pdu->isp_iov[pdu->isp_iovlen].iov_len =
1822a6d42e7dSPeter Dunlap 		    sizeof (data_digest_crc);
1823a6d42e7dSPeter Dunlap 		total_len		+= sizeof (data_digest_crc);
1824a6d42e7dSPeter Dunlap 		pdu->isp_iovlen++;
1825a6d42e7dSPeter Dunlap 	}
1826a6d42e7dSPeter Dunlap 
182730e7468fSPeter Dunlap 	pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base;
182830e7468fSPeter Dunlap 
1829a6d42e7dSPeter Dunlap 	if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0],
1830a6d42e7dSPeter Dunlap 	    pdu->isp_iovlen, total_len) != 0) {
1831a6d42e7dSPeter Dunlap 		return (IDM_STATUS_IO);
1832a6d42e7dSPeter Dunlap 	}
1833a6d42e7dSPeter Dunlap 
1834a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
1835a6d42e7dSPeter Dunlap 		crc_calculated = idm_crc32c(pdu->isp_data,
1836a6d42e7dSPeter Dunlap 		    pdu->isp_datalen);
1837a6d42e7dSPeter Dunlap 		if (pad_len) {
1838a6d42e7dSPeter Dunlap 			crc_calculated = idm_crc32c_continued((char *)&pad,
1839a6d42e7dSPeter Dunlap 			    pad_len, crc_calculated);
1840a6d42e7dSPeter Dunlap 		}
1841a6d42e7dSPeter Dunlap 		if (crc_calculated != data_digest_crc) {
1842a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN,
1843a6d42e7dSPeter Dunlap 			    "idm_sorecvdata: "
1844a6d42e7dSPeter Dunlap 			    "CRC error: actual 0x%x, calc 0x%x",
1845a6d42e7dSPeter Dunlap 			    data_digest_crc, crc_calculated);
1846a6d42e7dSPeter Dunlap 
1847a6d42e7dSPeter Dunlap 			/* Invalid Data Digest */
1848a6d42e7dSPeter Dunlap 			return (IDM_STATUS_DATA_DIGEST);
1849a6d42e7dSPeter Dunlap 		}
1850a6d42e7dSPeter Dunlap 	}
1851a6d42e7dSPeter Dunlap 
1852a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
1853a6d42e7dSPeter Dunlap }
1854a6d42e7dSPeter Dunlap 
1855a6d42e7dSPeter Dunlap /*
1856a6d42e7dSPeter Dunlap  * idm_sorecv_scsidata() is used to receive scsi data from the socket. The
1857a6d42e7dSPeter Dunlap  * Data-type PDU header must be read into the idm_pdu_t structure prior to
1858a6d42e7dSPeter Dunlap  * calling this function.
1859a6d42e7dSPeter Dunlap  */
1860a6d42e7dSPeter Dunlap idm_status_t
1861a6d42e7dSPeter Dunlap idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu)
1862a6d42e7dSPeter Dunlap {
1863a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
1864a6d42e7dSPeter Dunlap 	idm_task_t		*task;
1865a6d42e7dSPeter Dunlap 	uint32_t		offset;
1866a6d42e7dSPeter Dunlap 	uint8_t			opcode;
1867a6d42e7dSPeter Dunlap 	uint32_t		dlength;
1868a6d42e7dSPeter Dunlap 	list_t			*buflst;
1869a6d42e7dSPeter Dunlap 	uint32_t		xfer_bytes;
1870a6d42e7dSPeter Dunlap 	idm_status_t		status;
1871a6d42e7dSPeter Dunlap 
1872a6d42e7dSPeter Dunlap 	ASSERT(ic != NULL);
1873a6d42e7dSPeter Dunlap 	ASSERT(pdu != NULL);
1874a6d42e7dSPeter Dunlap 
1875a6d42e7dSPeter Dunlap 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
1876a6d42e7dSPeter Dunlap 
1877a6d42e7dSPeter Dunlap 	offset	= ntohl(bhs->offset);
1878a6d42e7dSPeter Dunlap 	opcode	= bhs->opcode;
1879a6d42e7dSPeter Dunlap 	dlength = n2h24(bhs->dlength);
1880a6d42e7dSPeter Dunlap 
1881a6d42e7dSPeter Dunlap 	ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
1882a6d42e7dSPeter Dunlap 	    (opcode == ISCSI_OP_SCSI_DATA));
1883a6d42e7dSPeter Dunlap 
1884a6d42e7dSPeter Dunlap 	/*
1885a6d42e7dSPeter Dunlap 	 * Successful lookup implicitly gets a "hold" on the task.  This
1886a6d42e7dSPeter Dunlap 	 * hold must be released before leaving this function.  At one
1887a6d42e7dSPeter Dunlap 	 * point we were caching this task context and retaining the hold
1888a6d42e7dSPeter Dunlap 	 * but it turned out to be very difficult to release the hold properly.
1889a6d42e7dSPeter Dunlap 	 * The task can be aborted and the connection shutdown between this
1890a6d42e7dSPeter Dunlap 	 * call and the subsequent expected call to idm_so_rx_datain/
1891a6d42e7dSPeter Dunlap 	 * idm_so_rx_dataout (in which case those functions are not called).
1892a6d42e7dSPeter Dunlap 	 * Releasing the hold in the PDU callback doesn't work well either
1893a6d42e7dSPeter Dunlap 	 * because the whole task may be completed by then at which point
1894a6d42e7dSPeter Dunlap 	 * it is too late to release the hold -- for better or worse this
1895a6d42e7dSPeter Dunlap 	 * code doesn't wait on the refcnts during normal operation.
1896a6d42e7dSPeter Dunlap 	 * idm_task_find() is very fast and it is not a huge burden if we
1897a6d42e7dSPeter Dunlap 	 * have to do it twice.
1898a6d42e7dSPeter Dunlap 	 */
1899a6d42e7dSPeter Dunlap 	task = idm_task_find(ic, bhs->itt, bhs->ttt);
1900a6d42e7dSPeter Dunlap 	if (task == NULL) {
1901a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
1902a6d42e7dSPeter Dunlap 		    "idm_sorecv_scsidata: could not find task");
1903a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1904a6d42e7dSPeter Dunlap 	}
1905a6d42e7dSPeter Dunlap 
1906a6d42e7dSPeter Dunlap 	mutex_enter(&task->idt_mutex);
1907a6d42e7dSPeter Dunlap 	buflst	= (opcode == ISCSI_OP_SCSI_DATA_RSP) ?
1908a6d42e7dSPeter Dunlap 	    &task->idt_inbufv : &task->idt_outbufv;
1909a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = idm_buf_find(buflst, offset);
1910a6d42e7dSPeter Dunlap 	mutex_exit(&task->idt_mutex);
1911a6d42e7dSPeter Dunlap 
1912a6d42e7dSPeter Dunlap 	if (pdu->isp_sorx_buf == NULL) {
1913a6d42e7dSPeter Dunlap 		idm_task_rele(task);
1914a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find "
1915a6d42e7dSPeter Dunlap 		    "buffer for offset %x opcode=%x",
1916a6d42e7dSPeter Dunlap 		    offset, opcode);
1917a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1918a6d42e7dSPeter Dunlap 	}
1919a6d42e7dSPeter Dunlap 
1920a6d42e7dSPeter Dunlap 	xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength);
1921a6d42e7dSPeter Dunlap 	ASSERT(xfer_bytes != 0);
1922a6d42e7dSPeter Dunlap 	if (xfer_bytes != dlength) {
1923a6d42e7dSPeter Dunlap 		idm_task_rele(task);
1924a6d42e7dSPeter Dunlap 		/*
1925a6d42e7dSPeter Dunlap 		 * Buffer overflow, connection error.  The PDU data is still
1926a6d42e7dSPeter Dunlap 		 * sitting in the socket so we can't use the connection
1927a6d42e7dSPeter Dunlap 		 * again until that data is drained.
1928a6d42e7dSPeter Dunlap 		 */
1929a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
1930a6d42e7dSPeter Dunlap 	}
1931a6d42e7dSPeter Dunlap 
1932a6d42e7dSPeter Dunlap 	status = idm_sorecvdata(ic, pdu);
1933a6d42e7dSPeter Dunlap 
1934a6d42e7dSPeter Dunlap 	idm_task_rele(task);
1935a6d42e7dSPeter Dunlap 
1936a6d42e7dSPeter Dunlap 	return (status);
1937a6d42e7dSPeter Dunlap }
1938a6d42e7dSPeter Dunlap 
1939a6d42e7dSPeter Dunlap static uint32_t
1940a6d42e7dSPeter Dunlap idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength)
1941a6d42e7dSPeter Dunlap {
1942a6d42e7dSPeter Dunlap 	uint32_t	buf_ro = ro - idb->idb_bufoffset;
1943a6d42e7dSPeter Dunlap 	uint32_t	xfer_len = min(dlength, idb->idb_buflen - buf_ro);
1944a6d42e7dSPeter Dunlap 
1945a6d42e7dSPeter Dunlap 	ASSERT(ro >= idb->idb_bufoffset);
1946a6d42e7dSPeter Dunlap 
1947a6d42e7dSPeter Dunlap 	pdu->isp_iov[pdu->isp_iovlen].iov_base	=
1948a6d42e7dSPeter Dunlap 	    (caddr_t)idb->idb_buf + buf_ro;
1949a6d42e7dSPeter Dunlap 	pdu->isp_iov[pdu->isp_iovlen].iov_len	= xfer_len;
1950a6d42e7dSPeter Dunlap 	pdu->isp_iovlen++;
1951a6d42e7dSPeter Dunlap 
1952a6d42e7dSPeter Dunlap 	return (xfer_len);
1953a6d42e7dSPeter Dunlap }
1954a6d42e7dSPeter Dunlap 
1955a6d42e7dSPeter Dunlap int
1956a6d42e7dSPeter Dunlap idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu)
1957a6d42e7dSPeter Dunlap {
1958a6d42e7dSPeter Dunlap 	pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP);
1959a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_data != NULL);
1960a6d42e7dSPeter Dunlap 
1961a6d42e7dSPeter Dunlap 	pdu->isp_databuflen = pdu->isp_datalen;
1962a6d42e7dSPeter Dunlap 	pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data;
1963a6d42e7dSPeter Dunlap 	pdu->isp_iov[0].iov_len = pdu->isp_datalen;
1964a6d42e7dSPeter Dunlap 	pdu->isp_iovlen = 1;
1965a6d42e7dSPeter Dunlap 	/*
1966a6d42e7dSPeter Dunlap 	 * Since we are associating a new data buffer with this received
1967a6d42e7dSPeter Dunlap 	 * PDU we need to set a specific callback to free the data
1968a6d42e7dSPeter Dunlap 	 * after the PDU is processed.
1969a6d42e7dSPeter Dunlap 	 */
1970a6d42e7dSPeter Dunlap 	pdu->isp_flags |= IDM_PDU_ADDL_DATA;
1971a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_addl_pdu_cb;
1972a6d42e7dSPeter Dunlap 
1973a6d42e7dSPeter Dunlap 	return (idm_sorecvdata(ic, pdu));
1974a6d42e7dSPeter Dunlap }
1975a6d42e7dSPeter Dunlap 
1976a6d42e7dSPeter Dunlap void
1977a6d42e7dSPeter Dunlap idm_sorx_thread(void *arg)
1978a6d42e7dSPeter Dunlap {
1979a6d42e7dSPeter Dunlap 	boolean_t	conn_failure = B_FALSE;
1980a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = (idm_conn_t *)arg;
1981a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
1982a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu;
1983a6d42e7dSPeter Dunlap 	idm_status_t	rc;
1984a6d42e7dSPeter Dunlap 
1985a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
1986a6d42e7dSPeter Dunlap 
1987a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
1988a6d42e7dSPeter Dunlap 
1989a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
1990a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_running = B_TRUE;
1991a6d42e7dSPeter Dunlap 	so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did;
1992a6d42e7dSPeter Dunlap 	cv_signal(&ic->ic_cv);
1993a6d42e7dSPeter Dunlap 
1994a6d42e7dSPeter Dunlap 	while (so_conn->ic_rx_thread_running) {
1995a6d42e7dSPeter Dunlap 		mutex_exit(&ic->ic_mutex);
1996a6d42e7dSPeter Dunlap 
1997a6d42e7dSPeter Dunlap 		/*
1998a6d42e7dSPeter Dunlap 		 * Get PDU with default header size (large enough for
1999a6d42e7dSPeter Dunlap 		 * BHS plus any anticipated AHS).  PDU from
2000a6d42e7dSPeter Dunlap 		 * the cache will have all values set correctly
2001a6d42e7dSPeter Dunlap 		 * for sockets RX including callback.
2002a6d42e7dSPeter Dunlap 		 */
2003a6d42e7dSPeter Dunlap 		pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP);
2004a6d42e7dSPeter Dunlap 		pdu->isp_ic = ic;
2005a6d42e7dSPeter Dunlap 		pdu->isp_flags = 0;
2006a6d42e7dSPeter Dunlap 		pdu->isp_transport_hdrlen = 0;
2007a6d42e7dSPeter Dunlap 
2008a6d42e7dSPeter Dunlap 		if ((rc = idm_sorecvhdr(ic, pdu)) != 0) {
2009a6d42e7dSPeter Dunlap 			/*
2010a6d42e7dSPeter Dunlap 			 * Call idm_pdu_complete so that we call the callback
2011a6d42e7dSPeter Dunlap 			 * and ensure any memory allocated in idm_sorecvhdr
2012a6d42e7dSPeter Dunlap 			 * gets freed up.
2013a6d42e7dSPeter Dunlap 			 */
2014a6d42e7dSPeter Dunlap 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
2015a6d42e7dSPeter Dunlap 
2016a6d42e7dSPeter Dunlap 			/*
2017a6d42e7dSPeter Dunlap 			 * If ic_rx_thread_running is still set then
2018a6d42e7dSPeter Dunlap 			 * this is some kind of connection problem
2019a6d42e7dSPeter Dunlap 			 * on the socket.  In this case we want to
2020a6d42e7dSPeter Dunlap 			 * generate an event.  Otherwise some other
2021a6d42e7dSPeter Dunlap 			 * thread closed the socket due to another
2022a6d42e7dSPeter Dunlap 			 * issue in which case we don't need to
2023a6d42e7dSPeter Dunlap 			 * generate an event.
2024a6d42e7dSPeter Dunlap 			 */
2025a6d42e7dSPeter Dunlap 			mutex_enter(&ic->ic_mutex);
2026a6d42e7dSPeter Dunlap 			if (so_conn->ic_rx_thread_running) {
2027a6d42e7dSPeter Dunlap 				conn_failure = B_TRUE;
2028a6d42e7dSPeter Dunlap 				so_conn->ic_rx_thread_running = B_FALSE;
2029a6d42e7dSPeter Dunlap 			}
2030a6d42e7dSPeter Dunlap 
2031a6d42e7dSPeter Dunlap 			continue;
2032a6d42e7dSPeter Dunlap 		}
2033a6d42e7dSPeter Dunlap 
2034a6d42e7dSPeter Dunlap 		/*
2035a6d42e7dSPeter Dunlap 		 * Header has been read and validated.  Now we need
2036a6d42e7dSPeter Dunlap 		 * to read the PDU data payload (if present).  SCSI data
2037a6d42e7dSPeter Dunlap 		 * need to be transferred from the socket directly into
2038a6d42e7dSPeter Dunlap 		 * the associated transfer buffer for the SCSI task.
2039a6d42e7dSPeter Dunlap 		 */
2040a6d42e7dSPeter Dunlap 		if (pdu->isp_datalen != 0) {
2041a6d42e7dSPeter Dunlap 			if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) ||
2042a6d42e7dSPeter Dunlap 			    (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) {
2043a6d42e7dSPeter Dunlap 				rc = idm_sorecv_scsidata(ic, pdu);
2044a6d42e7dSPeter Dunlap 				/*
2045a6d42e7dSPeter Dunlap 				 * All SCSI errors are fatal to the
2046a6d42e7dSPeter Dunlap 				 * connection right now since we have no
2047a6d42e7dSPeter Dunlap 				 * place to put the data.  What we need
2048a6d42e7dSPeter Dunlap 				 * is some kind of sink to dispose of unwanted
2049a6d42e7dSPeter Dunlap 				 * SCSI data.  For example an invalid task tag
2050a6d42e7dSPeter Dunlap 				 * should not kill the connection (although
2051a6d42e7dSPeter Dunlap 				 * we may want to drop the connection).
2052a6d42e7dSPeter Dunlap 				 */
2053a6d42e7dSPeter Dunlap 			} else {
2054a6d42e7dSPeter Dunlap 				/*
2055a6d42e7dSPeter Dunlap 				 * Not data PDUs so allocate a buffer for the
2056a6d42e7dSPeter Dunlap 				 * data segment and read the remaining data.
2057a6d42e7dSPeter Dunlap 				 */
2058a6d42e7dSPeter Dunlap 				rc = idm_sorecv_nonscsidata(ic, pdu);
2059a6d42e7dSPeter Dunlap 			}
2060a6d42e7dSPeter Dunlap 			if (rc != 0) {
2061a6d42e7dSPeter Dunlap 				/*
2062a6d42e7dSPeter Dunlap 				 * Call idm_pdu_complete so that we call the
2063a6d42e7dSPeter Dunlap 				 * callback and ensure any memory allocated
2064a6d42e7dSPeter Dunlap 				 * in idm_sorecvhdr gets freed up.
2065a6d42e7dSPeter Dunlap 				 */
2066a6d42e7dSPeter Dunlap 				idm_pdu_complete(pdu, IDM_STATUS_FAIL);
2067a6d42e7dSPeter Dunlap 
2068a6d42e7dSPeter Dunlap 				/*
2069a6d42e7dSPeter Dunlap 				 * If ic_rx_thread_running is still set then
2070a6d42e7dSPeter Dunlap 				 * this is some kind of connection problem
2071a6d42e7dSPeter Dunlap 				 * on the socket.  In this case we want to
2072a6d42e7dSPeter Dunlap 				 * generate an event.  Otherwise some other
2073a6d42e7dSPeter Dunlap 				 * thread closed the socket due to another
2074a6d42e7dSPeter Dunlap 				 * issue in which case we don't need to
2075a6d42e7dSPeter Dunlap 				 * generate an event.
2076a6d42e7dSPeter Dunlap 				 */
2077a6d42e7dSPeter Dunlap 				mutex_enter(&ic->ic_mutex);
2078a6d42e7dSPeter Dunlap 				if (so_conn->ic_rx_thread_running) {
2079a6d42e7dSPeter Dunlap 					conn_failure = B_TRUE;
2080a6d42e7dSPeter Dunlap 					so_conn->ic_rx_thread_running = B_FALSE;
2081a6d42e7dSPeter Dunlap 				}
2082a6d42e7dSPeter Dunlap 				continue;
2083a6d42e7dSPeter Dunlap 			}
2084a6d42e7dSPeter Dunlap 		}
2085a6d42e7dSPeter Dunlap 
2086a6d42e7dSPeter Dunlap 		/*
2087a6d42e7dSPeter Dunlap 		 * Process RX PDU
2088a6d42e7dSPeter Dunlap 		 */
2089a6d42e7dSPeter Dunlap 		idm_pdu_rx(ic, pdu);
2090a6d42e7dSPeter Dunlap 
2091a6d42e7dSPeter Dunlap 		mutex_enter(&ic->ic_mutex);
2092a6d42e7dSPeter Dunlap 	}
2093a6d42e7dSPeter Dunlap 
2094a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
2095a6d42e7dSPeter Dunlap 
2096a6d42e7dSPeter Dunlap 	/*
2097a6d42e7dSPeter Dunlap 	 * If we dropped out of the RX processing loop because of
2098a6d42e7dSPeter Dunlap 	 * a socket problem or other connection failure (including
2099a6d42e7dSPeter Dunlap 	 * digest errors) then we need to generate a state machine
2100a6d42e7dSPeter Dunlap 	 * event to shut the connection down.
2101a6d42e7dSPeter Dunlap 	 * If the state machine is already in, for example, INIT_ERROR, this
2102a6d42e7dSPeter Dunlap 	 * event will get dropped, and the TX thread will never be notified
2103a6d42e7dSPeter Dunlap 	 * to shut down.  To be safe, we'll just notify it here.
2104a6d42e7dSPeter Dunlap 	 */
2105a6d42e7dSPeter Dunlap 	if (conn_failure) {
2106a6d42e7dSPeter Dunlap 		if (so_conn->ic_tx_thread_running) {
2107a6d42e7dSPeter Dunlap 			so_conn->ic_tx_thread_running = B_FALSE;
2108a6d42e7dSPeter Dunlap 			mutex_enter(&so_conn->ic_tx_mutex);
2109a6d42e7dSPeter Dunlap 			cv_signal(&so_conn->ic_tx_cv);
2110a6d42e7dSPeter Dunlap 			mutex_exit(&so_conn->ic_tx_mutex);
2111a6d42e7dSPeter Dunlap 		}
2112a6d42e7dSPeter Dunlap 
2113a6d42e7dSPeter Dunlap 		idm_conn_event(ic, CE_TRANSPORT_FAIL, rc);
2114a6d42e7dSPeter Dunlap 	}
2115a6d42e7dSPeter Dunlap 
2116a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
2117a6d42e7dSPeter Dunlap 
2118a6d42e7dSPeter Dunlap 	thread_exit();
2119a6d42e7dSPeter Dunlap }
2120a6d42e7dSPeter Dunlap 
2121a6d42e7dSPeter Dunlap /*
2122a6d42e7dSPeter Dunlap  * idm_so_tx
2123a6d42e7dSPeter Dunlap  *
2124a6d42e7dSPeter Dunlap  * This is the implementation of idm_transport_ops_t's it_tx_pdu entry
2125a6d42e7dSPeter Dunlap  * point.  By definition, it is supposed to be fast.  So, simply queue
2126a6d42e7dSPeter Dunlap  * the entry and return.  The real work is done by idm_i_so_tx() via
2127a6d42e7dSPeter Dunlap  * idm_sotx_thread().
2128a6d42e7dSPeter Dunlap  */
2129a6d42e7dSPeter Dunlap 
2130a6d42e7dSPeter Dunlap static void
2131a6d42e7dSPeter Dunlap idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu)
2132a6d42e7dSPeter Dunlap {
2133a6d42e7dSPeter Dunlap 	idm_so_conn_t *so_conn = ic->ic_transport_private;
2134a6d42e7dSPeter Dunlap 
2135a6d42e7dSPeter Dunlap 	ASSERT(pdu->isp_ic == ic);
2136a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
2137a6d42e7dSPeter Dunlap 
2138a6d42e7dSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
2139a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
2140a6d42e7dSPeter Dunlap 		idm_pdu_complete(pdu, IDM_STATUS_ABORTED);
2141a6d42e7dSPeter Dunlap 		return;
2142a6d42e7dSPeter Dunlap 	}
2143a6d42e7dSPeter Dunlap 
2144a6d42e7dSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)pdu);
2145a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
2146a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
2147a6d42e7dSPeter Dunlap }
2148a6d42e7dSPeter Dunlap 
2149a6d42e7dSPeter Dunlap static idm_status_t
2150a6d42e7dSPeter Dunlap idm_i_so_tx(idm_pdu_t *pdu)
2151a6d42e7dSPeter Dunlap {
2152a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = pdu->isp_ic;
2153a6d42e7dSPeter Dunlap 	idm_status_t	status = IDM_STATUS_SUCCESS;
2154a6d42e7dSPeter Dunlap 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
2155a6d42e7dSPeter Dunlap 	int		pad_len;
2156a6d42e7dSPeter Dunlap 	uint32_t	hdr_digest_crc;
2157a6d42e7dSPeter Dunlap 	uint32_t	data_digest_crc = 0;
2158a6d42e7dSPeter Dunlap 	int		total_len = 0;
2159a6d42e7dSPeter Dunlap 	int		iovlen = 0;
2160a6d42e7dSPeter Dunlap 	struct iovec	iov[6];
2161a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
2162a6d42e7dSPeter Dunlap 
2163a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
2164a6d42e7dSPeter Dunlap 
2165a6d42e7dSPeter Dunlap 	/* Setup BHS */
2166a6d42e7dSPeter Dunlap 	iov[iovlen].iov_base	= (caddr_t)pdu->isp_hdr;
2167a6d42e7dSPeter Dunlap 	iov[iovlen].iov_len	= pdu->isp_hdrlen;
2168a6d42e7dSPeter Dunlap 	total_len		+= iov[iovlen].iov_len;
2169a6d42e7dSPeter Dunlap 	iovlen++;
2170a6d42e7dSPeter Dunlap 
2171a6d42e7dSPeter Dunlap 	/* Setup header digest */
2172a6d42e7dSPeter Dunlap 	if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
2173a6d42e7dSPeter Dunlap 	    (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) {
2174a6d42e7dSPeter Dunlap 		hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen);
2175a6d42e7dSPeter Dunlap 
2176a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base	= (caddr_t)&hdr_digest_crc;
2177a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len	= sizeof (hdr_digest_crc);
2178a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
2179a6d42e7dSPeter Dunlap 		iovlen++;
2180a6d42e7dSPeter Dunlap 	}
2181a6d42e7dSPeter Dunlap 
2182a6d42e7dSPeter Dunlap 	/* Setup the data */
2183a6d42e7dSPeter Dunlap 	if (pdu->isp_datalen) {
2184a6d42e7dSPeter Dunlap 		idm_task_t		*idt;
2185a6d42e7dSPeter Dunlap 		idm_buf_t		*idb;
2186a6d42e7dSPeter Dunlap 		iscsi_data_hdr_t	*ihp;
2187a6d42e7dSPeter Dunlap 		ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
2188a6d42e7dSPeter Dunlap 		/* Write of immediate data */
2189a6d42e7dSPeter Dunlap 		if (ic->ic_ffp &&
2190a6d42e7dSPeter Dunlap 		    (ihp->opcode == ISCSI_OP_SCSI_CMD ||
2191a6d42e7dSPeter Dunlap 		    ihp->opcode == ISCSI_OP_SCSI_DATA)) {
2192a6d42e7dSPeter Dunlap 			idt = idm_task_find(ic, ihp->itt, ihp->ttt);
2193a6d42e7dSPeter Dunlap 			if (idt) {
2194a6d42e7dSPeter Dunlap 				mutex_enter(&idt->idt_mutex);
2195a6d42e7dSPeter Dunlap 				idb = idm_buf_find(&idt->idt_outbufv, 0);
2196a6d42e7dSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
219730e7468fSPeter Dunlap 				/*
219830e7468fSPeter Dunlap 				 * If the initiator call to idm_buf_alloc
219930e7468fSPeter Dunlap 				 * failed then we can get to this point
220030e7468fSPeter Dunlap 				 * without a bound buffer.  The associated
220130e7468fSPeter Dunlap 				 * connection failure will clean things up
220230e7468fSPeter Dunlap 				 * later.  It would be nice to come up with
220330e7468fSPeter Dunlap 				 * a cleaner way to handle this.  In
220430e7468fSPeter Dunlap 				 * particular it seems absurd to look up
220530e7468fSPeter Dunlap 				 * the task and the buffer just to update
220630e7468fSPeter Dunlap 				 * this counter.
220730e7468fSPeter Dunlap 				 */
220830e7468fSPeter Dunlap 				if (idb)
220930e7468fSPeter Dunlap 					idb->idb_xfer_len += pdu->isp_datalen;
221030e7468fSPeter Dunlap 				idm_task_rele(idt);
2211a6d42e7dSPeter Dunlap 			}
2212a6d42e7dSPeter Dunlap 		}
2213a6d42e7dSPeter Dunlap 
2214a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (caddr_t)pdu->isp_data;
2215a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len  = pdu->isp_datalen;
2216a6d42e7dSPeter Dunlap 		total_len += iov[iovlen].iov_len;
2217a6d42e7dSPeter Dunlap 		iovlen++;
2218a6d42e7dSPeter Dunlap 	}
2219a6d42e7dSPeter Dunlap 
2220a6d42e7dSPeter Dunlap 	/* Setup the data pad if necessary */
2221a6d42e7dSPeter Dunlap 	pad_len = ((ISCSI_PAD_WORD_LEN -
2222a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
2223a6d42e7dSPeter Dunlap 	    (ISCSI_PAD_WORD_LEN - 1));
2224a6d42e7dSPeter Dunlap 
2225a6d42e7dSPeter Dunlap 	if (pad_len) {
2226a6d42e7dSPeter Dunlap 		bzero(pad, sizeof (pad));
2227a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base = (void *)&pad;
2228a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len  = pad_len;
2229a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
2230a6d42e7dSPeter Dunlap 		iovlen++;
2231a6d42e7dSPeter Dunlap 	}
2232a6d42e7dSPeter Dunlap 
2233a6d42e7dSPeter Dunlap 	/*
2234a6d42e7dSPeter Dunlap 	 * Setup the data digest if enabled.  Data-digest is not sent
2235a6d42e7dSPeter Dunlap 	 * for login-phase PDUs.
2236a6d42e7dSPeter Dunlap 	 */
2237a6d42e7dSPeter Dunlap 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) &&
2238a6d42e7dSPeter Dunlap 	    ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
2239a6d42e7dSPeter Dunlap 	    (pdu->isp_datalen || pad_len)) {
2240a6d42e7dSPeter Dunlap 		/*
2241a6d42e7dSPeter Dunlap 		 * RFC3720/10.2.3: A zero-length Data Segment also
2242a6d42e7dSPeter Dunlap 		 * implies a zero-length data digest.
2243a6d42e7dSPeter Dunlap 		 */
2244a6d42e7dSPeter Dunlap 		if (pdu->isp_datalen) {
2245a6d42e7dSPeter Dunlap 			data_digest_crc = idm_crc32c(pdu->isp_data,
2246a6d42e7dSPeter Dunlap 			    pdu->isp_datalen);
2247a6d42e7dSPeter Dunlap 		}
2248a6d42e7dSPeter Dunlap 		if (pad_len) {
2249a6d42e7dSPeter Dunlap 			data_digest_crc = idm_crc32c_continued(&pad,
2250a6d42e7dSPeter Dunlap 			    pad_len, data_digest_crc);
2251a6d42e7dSPeter Dunlap 		}
2252a6d42e7dSPeter Dunlap 
2253a6d42e7dSPeter Dunlap 		iov[iovlen].iov_base	= (caddr_t)&data_digest_crc;
2254a6d42e7dSPeter Dunlap 		iov[iovlen].iov_len	= sizeof (data_digest_crc);
2255a6d42e7dSPeter Dunlap 		total_len		+= iov[iovlen].iov_len;
2256a6d42e7dSPeter Dunlap 		iovlen++;
2257a6d42e7dSPeter Dunlap 	}
2258a6d42e7dSPeter Dunlap 
2259a6d42e7dSPeter Dunlap 	/* Transmit the PDU */
2260a6d42e7dSPeter Dunlap 	if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen,
2261a6d42e7dSPeter Dunlap 	    total_len) != 0) {
2262a6d42e7dSPeter Dunlap 		/* Set error status */
2263a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_WARN,
2264a6d42e7dSPeter Dunlap 		    "idm_so_tx: failed to transmit the PDU, so: %p ic: %p "
2265a6d42e7dSPeter Dunlap 		    "data: %p", (void *) so_conn->ic_so, (void *) ic,
2266a6d42e7dSPeter Dunlap 		    (void *) pdu->isp_data);
2267a6d42e7dSPeter Dunlap 		status = IDM_STATUS_IO;
2268a6d42e7dSPeter Dunlap 	}
2269a6d42e7dSPeter Dunlap 
2270a6d42e7dSPeter Dunlap 	/*
2271a6d42e7dSPeter Dunlap 	 * Success does not mean that the PDU actually reached the
2272a6d42e7dSPeter Dunlap 	 * remote node since it could get dropped along the way.
2273a6d42e7dSPeter Dunlap 	 */
2274a6d42e7dSPeter Dunlap 	idm_pdu_complete(pdu, status);
2275a6d42e7dSPeter Dunlap 
2276a6d42e7dSPeter Dunlap 	return (status);
2277a6d42e7dSPeter Dunlap }
2278a6d42e7dSPeter Dunlap 
2279a6d42e7dSPeter Dunlap /*
2280a6d42e7dSPeter Dunlap  * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the
2281a6d42e7dSPeter Dunlap  * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength,
2282a6d42e7dSPeter Dunlap  * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN.
2283a6d42e7dSPeter Dunlap  * A target can invoke this function multiple times for a single read command
2284a6d42e7dSPeter Dunlap  * (identified by the same ITT) to split the input into several sequences.
2285a6d42e7dSPeter Dunlap  *
2286a6d42e7dSPeter Dunlap  * DataSN starts with 0 for the first data PDU of an input command and advances
2287a6d42e7dSPeter Dunlap  * by 1 for each subsequent data PDU. Each sequence will have its own F bit,
2288a6d42e7dSPeter Dunlap  * which is set to 1 for the last data PDU of a sequence.
2289a6d42e7dSPeter Dunlap  *
2290a6d42e7dSPeter Dunlap  * Scope for Prototype build:
2291a6d42e7dSPeter Dunlap  * The data PDUs within a sequence will be sent in order with the buffer offset
2292a6d42e7dSPeter Dunlap  * in increasing order. i.e. initiator and target must have negotiated the
2293a6d42e7dSPeter Dunlap  * "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
2294a6d42e7dSPeter Dunlap  *
2295a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex
2296a6d42e7dSPeter Dunlap  */
2297a6d42e7dSPeter Dunlap static idm_status_t
2298a6d42e7dSPeter Dunlap idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb)
2299a6d42e7dSPeter Dunlap {
2300a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn = idb->idb_ic->ic_transport_private;
2301a6d42e7dSPeter Dunlap 	idm_pdu_t	tmppdu;
2302a6d42e7dSPeter Dunlap 
2303a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2304a6d42e7dSPeter Dunlap 
2305a6d42e7dSPeter Dunlap 	/*
2306a6d42e7dSPeter Dunlap 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
2307a6d42e7dSPeter Dunlap 	 * idm_sotx_thread.
2308a6d42e7dSPeter Dunlap 	 */
2309a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
2310a6d42e7dSPeter Dunlap 
2311a668b114SPriya Krishnan 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
2312a668b114SPriya Krishnan 	    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
2313a668b114SPriya Krishnan 	    uint64_t, 0, uint32_t, 0, uint32_t, 0,
2314a668b114SPriya Krishnan 	    uint32_t, idb->idb_xfer_len, int, XFER_BUF_TX_TO_INI);
2315a668b114SPriya Krishnan 
2316a6d42e7dSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
2317a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
2318a6d42e7dSPeter Dunlap 		/*
2319a6d42e7dSPeter Dunlap 		 * Don't release idt->idt_mutex since we're supposed to hold
2320a6d42e7dSPeter Dunlap 		 * in when calling idm_buf_tx_to_ini_done
2321a6d42e7dSPeter Dunlap 		 */
2322a668b114SPriya Krishnan 		DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
2323a668b114SPriya Krishnan 		    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
2324a668b114SPriya Krishnan 		    uint64_t, 0, uint32_t, 0, uint32_t, 0,
2325a668b114SPriya Krishnan 		    uint32_t, idb->idb_xfer_len,
2326a668b114SPriya Krishnan 		    int, XFER_BUF_TX_TO_INI);
2327a6d42e7dSPeter Dunlap 		idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
2328a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
2329a6d42e7dSPeter Dunlap 	}
2330a6d42e7dSPeter Dunlap 
2331a6d42e7dSPeter Dunlap 	/*
2332a6d42e7dSPeter Dunlap 	 * Build a template for the data PDU headers we will use so that
2333a6d42e7dSPeter Dunlap 	 * the SN values will stay consistent with other PDU's we are
2334a6d42e7dSPeter Dunlap 	 * transmitting like R2T and SCSI status.
2335a6d42e7dSPeter Dunlap 	 */
2336a6d42e7dSPeter Dunlap 	bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
2337a6d42e7dSPeter Dunlap 	tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl;
2338a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
2339a6d42e7dSPeter Dunlap 	    ISCSI_OP_SCSI_DATA_RSP);
2340a6d42e7dSPeter Dunlap 	idb->idb_tx_thread = B_TRUE;
2341a6d42e7dSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)idb);
2342a6d42e7dSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
2343a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
2344a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
2345a6d42e7dSPeter Dunlap 
2346a6d42e7dSPeter Dunlap 	/*
2347a6d42e7dSPeter Dunlap 	 * Returning success here indicates the transfer was successfully
2348a6d42e7dSPeter Dunlap 	 * dispatched -- it does not mean that the transfer completed
2349a6d42e7dSPeter Dunlap 	 * successfully.
2350a6d42e7dSPeter Dunlap 	 */
2351a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2352a6d42e7dSPeter Dunlap }
2353a6d42e7dSPeter Dunlap 
2354a6d42e7dSPeter Dunlap /*
2355a6d42e7dSPeter Dunlap  * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the
2356a6d42e7dSPeter Dunlap  * data blocks it is ready to receive from the initiator in response to a WRITE
2357a6d42e7dSPeter Dunlap  * SCSI command. The target iSCSI layer passes the information about the desired
2358a6d42e7dSPeter Dunlap  * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer
2359a6d42e7dSPeter Dunlap  * offset and datalen are passed via the 'idb' argument.
2360a6d42e7dSPeter Dunlap  *
2361a6d42e7dSPeter Dunlap  * Scope for Prototype build:
2362a6d42e7dSPeter Dunlap  * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have
2363a6d42e7dSPeter Dunlap  * negotiated the "InitialR2T" to "Yes".
2364a6d42e7dSPeter Dunlap  *
2365a6d42e7dSPeter Dunlap  * Caller holds idt->idt_mutex
2366a6d42e7dSPeter Dunlap  */
2367a6d42e7dSPeter Dunlap static idm_status_t
2368a6d42e7dSPeter Dunlap idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
2369a6d42e7dSPeter Dunlap {
2370a6d42e7dSPeter Dunlap 	idm_pdu_t		*pdu;
2371a6d42e7dSPeter Dunlap 	iscsi_rtt_hdr_t		*rtt;
2372a6d42e7dSPeter Dunlap 
2373a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2374a6d42e7dSPeter Dunlap 
2375a668b114SPriya Krishnan 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
2376a668b114SPriya Krishnan 	    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
2377a668b114SPriya Krishnan 	    uint64_t, 0, uint32_t, 0, uint32_t, 0,
2378a668b114SPriya Krishnan 	    uint32_t, idb->idb_xfer_len, int, XFER_BUF_RX_FROM_INI);
2379a668b114SPriya Krishnan 
2380a6d42e7dSPeter Dunlap 	pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
2381a6d42e7dSPeter Dunlap 	pdu->isp_ic = idt->idt_ic;
2382a6d42e7dSPeter Dunlap 	bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
2383a6d42e7dSPeter Dunlap 
2384a6d42e7dSPeter Dunlap 	/* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */
2385a6d42e7dSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP);
2386a6d42e7dSPeter Dunlap 
2387a6d42e7dSPeter Dunlap 	/* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */
2388a6d42e7dSPeter Dunlap 	rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr);
2389a6d42e7dSPeter Dunlap 
2390a6d42e7dSPeter Dunlap 	rtt->opcode		= ISCSI_OP_RTT_RSP;
2391a6d42e7dSPeter Dunlap 	rtt->flags		= ISCSI_FLAG_FINAL;
2392a6d42e7dSPeter Dunlap 	rtt->data_offset	= htonl(idb->idb_bufoffset);
2393a6d42e7dSPeter Dunlap 	rtt->data_length	= htonl(idb->idb_xfer_len);
2394a6d42e7dSPeter Dunlap 	rtt->rttsn		= htonl(idt->idt_exp_rttsn++);
2395a6d42e7dSPeter Dunlap 
2396a6d42e7dSPeter Dunlap 	/* Keep track of buffer offsets */
2397a6d42e7dSPeter Dunlap 	idb->idb_exp_offset	= idb->idb_bufoffset;
2398a6d42e7dSPeter Dunlap 	mutex_exit(&idt->idt_mutex);
2399a6d42e7dSPeter Dunlap 
2400a6d42e7dSPeter Dunlap 	/*
240163528ae4SJames Moore 	 * Transmit the PDU.
2402a6d42e7dSPeter Dunlap 	 */
240363528ae4SJames Moore 	idm_pdu_tx(pdu);
2404a6d42e7dSPeter Dunlap 
2405a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2406a6d42e7dSPeter Dunlap }
2407a6d42e7dSPeter Dunlap 
2408a6d42e7dSPeter Dunlap static idm_status_t
2409a6d42e7dSPeter Dunlap idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen)
2410a6d42e7dSPeter Dunlap {
2411cf8c0ebaSPeter Dunlap 	if ((buflen > IDM_SO_BUF_CACHE_LB) && (buflen <= IDM_SO_BUF_CACHE_UB)) {
2412cf8c0ebaSPeter Dunlap 		idb->idb_buf = kmem_cache_alloc(idm.idm_so_128k_buf_cache,
2413cf8c0ebaSPeter Dunlap 		    KM_NOSLEEP);
2414cf8c0ebaSPeter Dunlap 		idb->idb_buf_private = idm.idm_so_128k_buf_cache;
2415cf8c0ebaSPeter Dunlap 	} else {
2416cf8c0ebaSPeter Dunlap 		idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP);
2417cf8c0ebaSPeter Dunlap 		idb->idb_buf_private = NULL;
2418cf8c0ebaSPeter Dunlap 	}
2419cf8c0ebaSPeter Dunlap 
2420a6d42e7dSPeter Dunlap 	if (idb->idb_buf == NULL) {
2421a6d42e7dSPeter Dunlap 		IDM_CONN_LOG(CE_NOTE,
2422a6d42e7dSPeter Dunlap 		    "idm_so_buf_alloc: failed buffer allocation");
2423a6d42e7dSPeter Dunlap 		return (IDM_STATUS_FAIL);
2424a6d42e7dSPeter Dunlap 	}
2425cf8c0ebaSPeter Dunlap 
2426a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2427a6d42e7dSPeter Dunlap }
2428a6d42e7dSPeter Dunlap 
2429a6d42e7dSPeter Dunlap /* ARGSUSED */
2430a6d42e7dSPeter Dunlap static idm_status_t
2431a6d42e7dSPeter Dunlap idm_so_buf_setup(idm_buf_t *idb)
2432a6d42e7dSPeter Dunlap {
243330e7468fSPeter Dunlap 	/* Ensure bufalloc'd flag is unset */
243430e7468fSPeter Dunlap 	idb->idb_bufalloc = B_FALSE;
243530e7468fSPeter Dunlap 
2436a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2437a6d42e7dSPeter Dunlap }
2438a6d42e7dSPeter Dunlap 
2439a6d42e7dSPeter Dunlap /* ARGSUSED */
2440a6d42e7dSPeter Dunlap static void
2441a6d42e7dSPeter Dunlap idm_so_buf_teardown(idm_buf_t *idb)
2442a6d42e7dSPeter Dunlap {
2443a6d42e7dSPeter Dunlap 	/* nothing to do here */
2444a6d42e7dSPeter Dunlap }
2445a6d42e7dSPeter Dunlap 
2446a6d42e7dSPeter Dunlap static void
2447a6d42e7dSPeter Dunlap idm_so_buf_free(idm_buf_t *idb)
2448a6d42e7dSPeter Dunlap {
2449cf8c0ebaSPeter Dunlap 	if (idb->idb_buf_private == NULL) {
2450cf8c0ebaSPeter Dunlap 		kmem_free(idb->idb_buf, idb->idb_buflen);
2451cf8c0ebaSPeter Dunlap 	} else {
2452cf8c0ebaSPeter Dunlap 		kmem_cache_free(idb->idb_buf_private, idb->idb_buf);
2453cf8c0ebaSPeter Dunlap 	}
2454a6d42e7dSPeter Dunlap }
2455a6d42e7dSPeter Dunlap 
245630e7468fSPeter Dunlap static void
245730e7468fSPeter Dunlap idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb,
245830e7468fSPeter Dunlap     uint32_t offset, uint32_t length)
245930e7468fSPeter Dunlap {
246030e7468fSPeter Dunlap 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
246130e7468fSPeter Dunlap 	idm_pdu_t	tmppdu;
246230e7468fSPeter Dunlap 	idm_buf_t	*rtt_buf;
246330e7468fSPeter Dunlap 
246430e7468fSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
246530e7468fSPeter Dunlap 
246630e7468fSPeter Dunlap 	/*
246730e7468fSPeter Dunlap 	 * Allocate a buffer to represent the RTT transfer.  We could further
246830e7468fSPeter Dunlap 	 * optimize this by allocating the buffers internally from an rtt
246930e7468fSPeter Dunlap 	 * specific buffer cache since this is socket-specific code but for
247030e7468fSPeter Dunlap 	 * now we will keep it simple.
247130e7468fSPeter Dunlap 	 */
247230e7468fSPeter Dunlap 	rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length);
247330e7468fSPeter Dunlap 	if (rtt_buf == NULL) {
247430e7468fSPeter Dunlap 		/*
247530e7468fSPeter Dunlap 		 * If we're in FFP then the failure was likely a resource
247630e7468fSPeter Dunlap 		 * allocation issue and we should close the connection by
247730e7468fSPeter Dunlap 		 * sending a CE_TRANSPORT_FAIL event.
247830e7468fSPeter Dunlap 		 *
247930e7468fSPeter Dunlap 		 * If we're not in FFP then idm_buf_alloc will always
248030e7468fSPeter Dunlap 		 * fail and the state is transitioning to "complete" anyway
248130e7468fSPeter Dunlap 		 * so we won't bother to send an event.
248230e7468fSPeter Dunlap 		 */
248330e7468fSPeter Dunlap 		mutex_enter(&ic->ic_state_mutex);
248430e7468fSPeter Dunlap 		if (ic->ic_ffp)
248530e7468fSPeter Dunlap 			idm_conn_event_locked(ic, CE_TRANSPORT_FAIL,
248630e7468fSPeter Dunlap 			    NULL, CT_NONE);
248730e7468fSPeter Dunlap 		mutex_exit(&ic->ic_state_mutex);
248830e7468fSPeter Dunlap 		return;
248930e7468fSPeter Dunlap 	}
249030e7468fSPeter Dunlap 
249130e7468fSPeter Dunlap 	rtt_buf->idb_buf_cb = NULL;
249230e7468fSPeter Dunlap 	rtt_buf->idb_cb_arg = NULL;
249330e7468fSPeter Dunlap 	rtt_buf->idb_bufoffset = offset;
249430e7468fSPeter Dunlap 	rtt_buf->idb_xfer_len = length;
249530e7468fSPeter Dunlap 	rtt_buf->idb_ic = idt->idt_ic;
249630e7468fSPeter Dunlap 	rtt_buf->idb_task_binding = idt;
249730e7468fSPeter Dunlap 
249830e7468fSPeter Dunlap 	/*
249930e7468fSPeter Dunlap 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
250030e7468fSPeter Dunlap 	 * idm_sotx_thread.
250130e7468fSPeter Dunlap 	 */
250230e7468fSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
250330e7468fSPeter Dunlap 
250430e7468fSPeter Dunlap 	if (!so_conn->ic_tx_thread_running) {
250530e7468fSPeter Dunlap 		idm_buf_free(rtt_buf);
250630e7468fSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
250730e7468fSPeter Dunlap 		return;
250830e7468fSPeter Dunlap 	}
250930e7468fSPeter Dunlap 
251030e7468fSPeter Dunlap 	/*
251130e7468fSPeter Dunlap 	 * This new buffer represents an additional reference on the task
251230e7468fSPeter Dunlap 	 */
251330e7468fSPeter Dunlap 	idm_task_hold(idt);
251430e7468fSPeter Dunlap 
251530e7468fSPeter Dunlap 	/*
251630e7468fSPeter Dunlap 	 * Build a template for the data PDU headers we will use so that
251730e7468fSPeter Dunlap 	 * the SN values will stay consistent with other PDU's we are
251830e7468fSPeter Dunlap 	 * transmitting like R2T and SCSI status.
251930e7468fSPeter Dunlap 	 */
252030e7468fSPeter Dunlap 	bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
252130e7468fSPeter Dunlap 	tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl;
252230e7468fSPeter Dunlap 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
252330e7468fSPeter Dunlap 	    ISCSI_OP_SCSI_DATA);
252430e7468fSPeter Dunlap 	rtt_buf->idb_tx_thread = B_TRUE;
252530e7468fSPeter Dunlap 	rtt_buf->idb_in_transport = B_TRUE;
252630e7468fSPeter Dunlap 	list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf);
252730e7468fSPeter Dunlap 	cv_signal(&so_conn->ic_tx_cv);
252830e7468fSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
252930e7468fSPeter Dunlap }
253030e7468fSPeter Dunlap 
253130e7468fSPeter Dunlap static void
253230e7468fSPeter Dunlap idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb)
253330e7468fSPeter Dunlap {
253430e7468fSPeter Dunlap 	/*
253530e7468fSPeter Dunlap 	 * Don't worry about status -- we assume any error handling
253630e7468fSPeter Dunlap 	 * is performed by the caller (idm_sotx_thread).
253730e7468fSPeter Dunlap 	 */
253830e7468fSPeter Dunlap 	idb->idb_in_transport = B_FALSE;
253930e7468fSPeter Dunlap 	idm_task_rele(idt);
254030e7468fSPeter Dunlap 	idm_buf_free(idb);
254130e7468fSPeter Dunlap }
254230e7468fSPeter Dunlap 
254330e7468fSPeter Dunlap static idm_status_t
254430e7468fSPeter Dunlap idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
2545a6d42e7dSPeter Dunlap     uint32_t buf_region_offset, uint32_t buf_region_length)
2546a6d42e7dSPeter Dunlap {
2547a6d42e7dSPeter Dunlap 	idm_conn_t		*ic;
2548a6d42e7dSPeter Dunlap 	uint32_t		max_dataseglen;
2549a6d42e7dSPeter Dunlap 	size_t			remainder, chunk;
2550a6d42e7dSPeter Dunlap 	uint32_t		data_offset = buf_region_offset;
2551a6d42e7dSPeter Dunlap 	iscsi_data_hdr_t	*bhs;
2552a6d42e7dSPeter Dunlap 	idm_pdu_t		*pdu;
255330e7468fSPeter Dunlap 	idm_status_t		tx_status;
2554a6d42e7dSPeter Dunlap 
2555a6d42e7dSPeter Dunlap 	ASSERT(mutex_owned(&idt->idt_mutex));
2556a6d42e7dSPeter Dunlap 
2557a6d42e7dSPeter Dunlap 	ic = idt->idt_ic;
2558a6d42e7dSPeter Dunlap 
2559*56261083SCharles Ting 	max_dataseglen = ic->ic_conn_params.max_xmit_dataseglen;
2560a6d42e7dSPeter Dunlap 	remainder = buf_region_length;
2561a6d42e7dSPeter Dunlap 
2562a6d42e7dSPeter Dunlap 	while (remainder) {
2563a6d42e7dSPeter Dunlap 		if (idt->idt_state != TASK_ACTIVE) {
2564a6d42e7dSPeter Dunlap 			ASSERT((idt->idt_state != TASK_IDLE) &&
2565a6d42e7dSPeter Dunlap 			    (idt->idt_state != TASK_COMPLETE));
2566a6d42e7dSPeter Dunlap 			return (IDM_STATUS_ABORTED);
2567a6d42e7dSPeter Dunlap 		}
2568a6d42e7dSPeter Dunlap 
2569a6d42e7dSPeter Dunlap 		/* check to see if we need to chunk the data */
2570a6d42e7dSPeter Dunlap 		if (remainder > max_dataseglen) {
2571a6d42e7dSPeter Dunlap 			chunk = max_dataseglen;
2572a6d42e7dSPeter Dunlap 		} else {
2573a6d42e7dSPeter Dunlap 			chunk = remainder;
2574a6d42e7dSPeter Dunlap 		}
2575a6d42e7dSPeter Dunlap 
2576a6d42e7dSPeter Dunlap 		/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
2577a6d42e7dSPeter Dunlap 		pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
2578a6d42e7dSPeter Dunlap 		pdu->isp_ic = ic;
2579a6d42e7dSPeter Dunlap 
2580a6d42e7dSPeter Dunlap 		/*
258130e7468fSPeter Dunlap 		 * We've already built a build a header template
2582a6d42e7dSPeter Dunlap 		 * to use during the transfer.  Use this template so that
2583a6d42e7dSPeter Dunlap 		 * the SN values stay consistent with any unrelated PDU's
2584a6d42e7dSPeter Dunlap 		 * being transmitted.
2585a6d42e7dSPeter Dunlap 		 */
258630e7468fSPeter Dunlap 		bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr,
258730e7468fSPeter Dunlap 		    sizeof (iscsi_hdr_t));
2588a6d42e7dSPeter Dunlap 
2589a6d42e7dSPeter Dunlap 		/*
2590a6d42e7dSPeter Dunlap 		 * Set DataSN, data offset, and flags in BHS
2591a6d42e7dSPeter Dunlap 		 * For the prototype build, A = 0, S = 0, U = 0
2592a6d42e7dSPeter Dunlap 		 */
2593a6d42e7dSPeter Dunlap 		bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr);
2594a6d42e7dSPeter Dunlap 
2595a6d42e7dSPeter Dunlap 		bhs->datasn		= htonl(idt->idt_exp_datasn++);
2596a6d42e7dSPeter Dunlap 
2597a6d42e7dSPeter Dunlap 		hton24(bhs->dlength, chunk);
2598a6d42e7dSPeter Dunlap 		bhs->offset = htonl(idb->idb_bufoffset + data_offset);
2599a6d42e7dSPeter Dunlap 
2600a6d42e7dSPeter Dunlap 		if (chunk == remainder) {
2601a6d42e7dSPeter Dunlap 			bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
2602a6d42e7dSPeter Dunlap 		}
2603a6d42e7dSPeter Dunlap 
2604a668b114SPriya Krishnan 		/* Instrument the data-send DTrace probe. */
2605a668b114SPriya Krishnan 		if (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP) {
2606a668b114SPriya Krishnan 			DTRACE_ISCSI_2(data__send,
2607a668b114SPriya Krishnan 			    idm_conn_t *, idt->idt_ic,
2608a668b114SPriya Krishnan 			    iscsi_data_rsp_hdr_t *,
2609a668b114SPriya Krishnan 			    (iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
2610a668b114SPriya Krishnan 		}
2611a6d42e7dSPeter Dunlap 		/* setup data */
2612a6d42e7dSPeter Dunlap 		pdu->isp_data	=  (uint8_t *)idb->idb_buf + data_offset;
2613a6d42e7dSPeter Dunlap 		pdu->isp_datalen = (uint_t)chunk;
2614a6d42e7dSPeter Dunlap 		remainder	-= chunk;
2615a6d42e7dSPeter Dunlap 		data_offset	+= chunk;
2616a6d42e7dSPeter Dunlap 
2617a6d42e7dSPeter Dunlap 		/*
2618a6d42e7dSPeter Dunlap 		 * Now that we're done working with idt_exp_datasn,
2619a6d42e7dSPeter Dunlap 		 * idt->idt_state and idb->idb_bufoffset we can release
2620a6d42e7dSPeter Dunlap 		 * the task lock -- don't want to hold it across the
2621a6d42e7dSPeter Dunlap 		 * call to idm_i_so_tx since we could block.
2622a6d42e7dSPeter Dunlap 		 */
2623a6d42e7dSPeter Dunlap 		mutex_exit(&idt->idt_mutex);
2624a6d42e7dSPeter Dunlap 
2625a6d42e7dSPeter Dunlap 		/*
2626a6d42e7dSPeter Dunlap 		 * Transmit the PDU.  Call the internal routine directly
2627a6d42e7dSPeter Dunlap 		 * as there is already implicit ordering.
2628a6d42e7dSPeter Dunlap 		 */
262930e7468fSPeter Dunlap 		if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) {
263030e7468fSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
263130e7468fSPeter Dunlap 			return (tx_status);
263230e7468fSPeter Dunlap 		}
2633a6d42e7dSPeter Dunlap 
2634a6d42e7dSPeter Dunlap 		mutex_enter(&idt->idt_mutex);
263530e7468fSPeter Dunlap 		idt->idt_tx_bytes += chunk;
2636a6d42e7dSPeter Dunlap 	}
2637a6d42e7dSPeter Dunlap 
2638a6d42e7dSPeter Dunlap 	return (IDM_STATUS_SUCCESS);
2639a6d42e7dSPeter Dunlap }
2640a6d42e7dSPeter Dunlap 
2641a6d42e7dSPeter Dunlap /*
2642a6d42e7dSPeter Dunlap  * TX PDU cache
2643a6d42e7dSPeter Dunlap  */
2644a6d42e7dSPeter Dunlap /* ARGSUSED */
2645a6d42e7dSPeter Dunlap int
2646a6d42e7dSPeter Dunlap idm_sotx_pdu_constructor(void *hdl, void *arg, int flags)
2647a6d42e7dSPeter Dunlap {
2648a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu = hdl;
2649a6d42e7dSPeter Dunlap 
2650a6d42e7dSPeter Dunlap 	bzero(pdu, sizeof (idm_pdu_t));
2651a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
2652a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
2653a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sotx_cache_pdu_cb;
2654a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
2655a6d42e7dSPeter Dunlap 	bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
2656a6d42e7dSPeter Dunlap 
2657a6d42e7dSPeter Dunlap 	return (0);
2658a6d42e7dSPeter Dunlap }
2659a6d42e7dSPeter Dunlap 
2660a6d42e7dSPeter Dunlap /* ARGSUSED */
2661a6d42e7dSPeter Dunlap void
2662a6d42e7dSPeter Dunlap idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2663a6d42e7dSPeter Dunlap {
2664a6d42e7dSPeter Dunlap 	/* reset values between use */
2665a6d42e7dSPeter Dunlap 	pdu->isp_datalen = 0;
2666a6d42e7dSPeter Dunlap 
2667a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_sotx_pdu_cache, pdu);
2668a6d42e7dSPeter Dunlap }
2669a6d42e7dSPeter Dunlap 
2670a6d42e7dSPeter Dunlap /*
2671a6d42e7dSPeter Dunlap  * RX PDU cache
2672a6d42e7dSPeter Dunlap  */
2673a6d42e7dSPeter Dunlap /* ARGSUSED */
2674a6d42e7dSPeter Dunlap int
2675a6d42e7dSPeter Dunlap idm_sorx_pdu_constructor(void *hdl, void *arg, int flags)
2676a6d42e7dSPeter Dunlap {
2677a6d42e7dSPeter Dunlap 	idm_pdu_t	*pdu = hdl;
2678a6d42e7dSPeter Dunlap 
2679a6d42e7dSPeter Dunlap 	bzero(pdu, sizeof (idm_pdu_t));
2680a6d42e7dSPeter Dunlap 	pdu->isp_magic = IDM_PDU_MAGIC;
2681a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
2682a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
2683a6d42e7dSPeter Dunlap 
2684a6d42e7dSPeter Dunlap 	return (0);
2685a6d42e7dSPeter Dunlap }
2686a6d42e7dSPeter Dunlap 
2687a6d42e7dSPeter Dunlap /* ARGSUSED */
2688a6d42e7dSPeter Dunlap static void
2689a6d42e7dSPeter Dunlap idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2690a6d42e7dSPeter Dunlap {
2691a6d42e7dSPeter Dunlap 	pdu->isp_iovlen = 0;
2692a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = 0;
2693a6d42e7dSPeter Dunlap 	kmem_cache_free(idm.idm_sorx_pdu_cache, pdu);
2694a6d42e7dSPeter Dunlap }
2695a6d42e7dSPeter Dunlap 
2696a6d42e7dSPeter Dunlap static void
2697a6d42e7dSPeter Dunlap idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
2698a6d42e7dSPeter Dunlap {
2699a6d42e7dSPeter Dunlap 	/*
2700a6d42e7dSPeter Dunlap 	 * We had to modify our cached RX PDU with a longer header buffer
2701a6d42e7dSPeter Dunlap 	 * and/or a longer data buffer.  Release the new buffers and fix
2702a6d42e7dSPeter Dunlap 	 * the fields back to what we would expect for a cached RX PDU.
2703a6d42e7dSPeter Dunlap 	 */
2704a6d42e7dSPeter Dunlap 	if (pdu->isp_flags & IDM_PDU_ADDL_HDR) {
2705a6d42e7dSPeter Dunlap 		kmem_free(pdu->isp_hdr, pdu->isp_hdrlen);
2706a6d42e7dSPeter Dunlap 	}
2707a6d42e7dSPeter Dunlap 	if (pdu->isp_flags & IDM_PDU_ADDL_DATA) {
2708a6d42e7dSPeter Dunlap 		kmem_free(pdu->isp_data, pdu->isp_datalen);
2709a6d42e7dSPeter Dunlap 	}
2710a6d42e7dSPeter Dunlap 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1);
2711a6d42e7dSPeter Dunlap 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
2712a6d42e7dSPeter Dunlap 	pdu->isp_data = NULL;
2713a6d42e7dSPeter Dunlap 	pdu->isp_datalen = 0;
2714a6d42e7dSPeter Dunlap 	pdu->isp_sorx_buf = 0;
2715a6d42e7dSPeter Dunlap 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
2716a6d42e7dSPeter Dunlap 	idm_sorx_cache_pdu_cb(pdu, status);
2717a6d42e7dSPeter Dunlap }
2718a6d42e7dSPeter Dunlap 
2719a6d42e7dSPeter Dunlap /*
2720a6d42e7dSPeter Dunlap  * This thread is only active when I/O is queued for transmit
2721a6d42e7dSPeter Dunlap  * because the socket is busy.
2722a6d42e7dSPeter Dunlap  */
2723a6d42e7dSPeter Dunlap void
2724a6d42e7dSPeter Dunlap idm_sotx_thread(void *arg)
2725a6d42e7dSPeter Dunlap {
2726a6d42e7dSPeter Dunlap 	idm_conn_t	*ic = arg;
2727a6d42e7dSPeter Dunlap 	idm_tx_obj_t	*object, *next;
2728a6d42e7dSPeter Dunlap 	idm_so_conn_t	*so_conn;
2729a6d42e7dSPeter Dunlap 	idm_status_t	status = IDM_STATUS_SUCCESS;
2730a6d42e7dSPeter Dunlap 
2731a6d42e7dSPeter Dunlap 	idm_conn_hold(ic);
2732a6d42e7dSPeter Dunlap 
2733a6d42e7dSPeter Dunlap 	mutex_enter(&ic->ic_mutex);
2734a6d42e7dSPeter Dunlap 	so_conn = ic->ic_transport_private;
2735a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_running = B_TRUE;
2736a6d42e7dSPeter Dunlap 	so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did;
2737a6d42e7dSPeter Dunlap 	cv_signal(&ic->ic_cv);
2738a6d42e7dSPeter Dunlap 	mutex_exit(&ic->ic_mutex);
2739a6d42e7dSPeter Dunlap 
2740a6d42e7dSPeter Dunlap 	mutex_enter(&so_conn->ic_tx_mutex);
2741a6d42e7dSPeter Dunlap 
2742a6d42e7dSPeter Dunlap 	while (so_conn->ic_tx_thread_running) {
2743a6d42e7dSPeter Dunlap 		while (list_is_empty(&so_conn->ic_tx_list)) {
2744a6d42e7dSPeter Dunlap 			DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic);
2745a6d42e7dSPeter Dunlap 			cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex);
2746a6d42e7dSPeter Dunlap 			DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic);
2747a6d42e7dSPeter Dunlap 
2748a6d42e7dSPeter Dunlap 			if (!so_conn->ic_tx_thread_running) {
2749a6d42e7dSPeter Dunlap 				goto tx_bail;
2750a6d42e7dSPeter Dunlap 			}
2751a6d42e7dSPeter Dunlap 		}
2752a6d42e7dSPeter Dunlap 
2753a6d42e7dSPeter Dunlap 		object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
2754a6d42e7dSPeter Dunlap 		list_remove(&so_conn->ic_tx_list, object);
2755a6d42e7dSPeter Dunlap 		mutex_exit(&so_conn->ic_tx_mutex);
2756a6d42e7dSPeter Dunlap 
2757a6d42e7dSPeter Dunlap 		switch (object->idm_tx_obj_magic) {
2758a6d42e7dSPeter Dunlap 		case IDM_PDU_MAGIC:
2759a6d42e7dSPeter Dunlap 			DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
2760a6d42e7dSPeter Dunlap 			    idm_pdu_t *, (idm_pdu_t *)object);
2761a6d42e7dSPeter Dunlap 
2762a6d42e7dSPeter Dunlap 			status = idm_i_so_tx((idm_pdu_t *)object);
2763a6d42e7dSPeter Dunlap 			break;
2764a6d42e7dSPeter Dunlap 
2765a6d42e7dSPeter Dunlap 		case IDM_BUF_MAGIC: {
2766a6d42e7dSPeter Dunlap 			idm_buf_t *idb = (idm_buf_t *)object;
2767a6d42e7dSPeter Dunlap 			idm_task_t *idt = idb->idb_task_binding;
2768a6d42e7dSPeter Dunlap 
2769a6d42e7dSPeter Dunlap 			DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic,
2770a6d42e7dSPeter Dunlap 			    idm_buf_t *, idb);
2771a6d42e7dSPeter Dunlap 
2772a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
2773a6d42e7dSPeter Dunlap 			status = idm_so_send_buf_region(idt,
277430e7468fSPeter Dunlap 			    idb, 0, idb->idb_xfer_len);
2775a6d42e7dSPeter Dunlap 
2776a6d42e7dSPeter Dunlap 			/*
2777a6d42e7dSPeter Dunlap 			 * TX thread owns the buffer so we expect it to
2778a6d42e7dSPeter Dunlap 			 * be "in transport"
2779a6d42e7dSPeter Dunlap 			 */
2780a6d42e7dSPeter Dunlap 			ASSERT(idb->idb_in_transport);
278130e7468fSPeter Dunlap 			if (IDM_CONN_ISTGT(ic)) {
278230e7468fSPeter Dunlap 				/*
278330e7468fSPeter Dunlap 				 * idm_buf_tx_to_ini_done releases
278430e7468fSPeter Dunlap 				 * idt->idt_mutex
278530e7468fSPeter Dunlap 				 */
2786a668b114SPriya Krishnan 				DTRACE_ISCSI_8(xfer__done,
2787a668b114SPriya Krishnan 				    idm_conn_t *, idt->idt_ic,
2788a668b114SPriya Krishnan 				    uintptr_t, idb->idb_buf,
2789a668b114SPriya Krishnan 				    uint32_t, idb->idb_bufoffset,
2790a668b114SPriya Krishnan 				    uint64_t, 0, uint32_t, 0, uint32_t, 0,
2791a668b114SPriya Krishnan 				    uint32_t, idb->idb_xfer_len,
2792a668b114SPriya Krishnan 				    int, XFER_BUF_TX_TO_INI);
279330e7468fSPeter Dunlap 				idm_buf_tx_to_ini_done(idt, idb, status);
279430e7468fSPeter Dunlap 			} else {
279530e7468fSPeter Dunlap 				idm_so_send_rtt_data_done(idt, idb);
279630e7468fSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
279730e7468fSPeter Dunlap 			}
2798a6d42e7dSPeter Dunlap 			break;
2799a6d42e7dSPeter Dunlap 		}
2800a6d42e7dSPeter Dunlap 
2801a6d42e7dSPeter Dunlap 		default:
2802a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic "
2803a6d42e7dSPeter Dunlap 			    "(0x%08x)", object->idm_tx_obj_magic);
2804a6d42e7dSPeter Dunlap 			status = IDM_STATUS_FAIL;
2805a6d42e7dSPeter Dunlap 		}
2806a6d42e7dSPeter Dunlap 
2807a6d42e7dSPeter Dunlap 		mutex_enter(&so_conn->ic_tx_mutex);
2808a6d42e7dSPeter Dunlap 
2809a6d42e7dSPeter Dunlap 		if (status != IDM_STATUS_SUCCESS) {
2810a6d42e7dSPeter Dunlap 			so_conn->ic_tx_thread_running = B_FALSE;
2811a6d42e7dSPeter Dunlap 			idm_conn_event(ic, CE_TRANSPORT_FAIL, status);
2812a6d42e7dSPeter Dunlap 		}
2813a6d42e7dSPeter Dunlap 	}
2814a6d42e7dSPeter Dunlap 
2815a6d42e7dSPeter Dunlap 	/*
2816a6d42e7dSPeter Dunlap 	 * Before we leave, we need to abort every item remaining in the
2817a6d42e7dSPeter Dunlap 	 * TX list.
2818a6d42e7dSPeter Dunlap 	 */
2819a6d42e7dSPeter Dunlap 
2820a6d42e7dSPeter Dunlap tx_bail:
2821a6d42e7dSPeter Dunlap 	object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
2822a6d42e7dSPeter Dunlap 
2823a6d42e7dSPeter Dunlap 	while (object != NULL) {
2824a6d42e7dSPeter Dunlap 		next = list_next(&so_conn->ic_tx_list, object);
2825a6d42e7dSPeter Dunlap 
2826a6d42e7dSPeter Dunlap 		list_remove(&so_conn->ic_tx_list, object);
2827a6d42e7dSPeter Dunlap 		switch (object->idm_tx_obj_magic) {
2828a6d42e7dSPeter Dunlap 		case IDM_PDU_MAGIC:
2829a6d42e7dSPeter Dunlap 			idm_pdu_complete((idm_pdu_t *)object,
2830a6d42e7dSPeter Dunlap 			    IDM_STATUS_ABORTED);
2831a6d42e7dSPeter Dunlap 			break;
2832a6d42e7dSPeter Dunlap 
2833a6d42e7dSPeter Dunlap 		case IDM_BUF_MAGIC: {
2834a6d42e7dSPeter Dunlap 			idm_buf_t *idb = (idm_buf_t *)object;
2835a6d42e7dSPeter Dunlap 			idm_task_t *idt = idb->idb_task_binding;
2836a6d42e7dSPeter Dunlap 			mutex_exit(&so_conn->ic_tx_mutex);
2837a6d42e7dSPeter Dunlap 			mutex_enter(&idt->idt_mutex);
2838a6d42e7dSPeter Dunlap 			/*
2839a6d42e7dSPeter Dunlap 			 * TX thread owns the buffer so we expect it to
2840a6d42e7dSPeter Dunlap 			 * be "in transport"
2841a6d42e7dSPeter Dunlap 			 */
2842a6d42e7dSPeter Dunlap 			ASSERT(idb->idb_in_transport);
284330e7468fSPeter Dunlap 			if (IDM_CONN_ISTGT(ic)) {
284430e7468fSPeter Dunlap 				/*
284530e7468fSPeter Dunlap 				 * idm_buf_tx_to_ini_done releases
284630e7468fSPeter Dunlap 				 * idt->idt_mutex
284730e7468fSPeter Dunlap 				 */
2848a668b114SPriya Krishnan 				DTRACE_ISCSI_8(xfer__done,
2849a668b114SPriya Krishnan 				    idm_conn_t *, idt->idt_ic,
2850a668b114SPriya Krishnan 				    uintptr_t, idb->idb_buf,
2851a668b114SPriya Krishnan 				    uint32_t, idb->idb_bufoffset,
2852a668b114SPriya Krishnan 				    uint64_t, 0, uint32_t, 0, uint32_t, 0,
2853a668b114SPriya Krishnan 				    uint32_t, idb->idb_xfer_len,
2854a668b114SPriya Krishnan 				    int, XFER_BUF_TX_TO_INI);
285530e7468fSPeter Dunlap 				idm_buf_tx_to_ini_done(idt, idb,
285630e7468fSPeter Dunlap 				    IDM_STATUS_ABORTED);
285730e7468fSPeter Dunlap 			} else {
285830e7468fSPeter Dunlap 				idm_so_send_rtt_data_done(idt, idb);
285930e7468fSPeter Dunlap 				mutex_exit(&idt->idt_mutex);
286030e7468fSPeter Dunlap 			}
2861a6d42e7dSPeter Dunlap 			mutex_enter(&so_conn->ic_tx_mutex);
2862a6d42e7dSPeter Dunlap 			break;
2863a6d42e7dSPeter Dunlap 		}
2864a6d42e7dSPeter Dunlap 		default:
2865a6d42e7dSPeter Dunlap 			IDM_CONN_LOG(CE_WARN,
2866a6d42e7dSPeter Dunlap 			    "idm_sotx_thread: Unexpected magic "
2867a6d42e7dSPeter Dunlap 			    "(0x%08x)", object->idm_tx_obj_magic);
2868a6d42e7dSPeter Dunlap 		}
2869a6d42e7dSPeter Dunlap 
2870a6d42e7dSPeter Dunlap 		object = next;
2871a6d42e7dSPeter Dunlap 	}
2872a6d42e7dSPeter Dunlap 
2873a6d42e7dSPeter Dunlap 	mutex_exit(&so_conn->ic_tx_mutex);
2874a6d42e7dSPeter Dunlap 	idm_conn_rele(ic);
2875a6d42e7dSPeter Dunlap 	thread_exit();
2876a6d42e7dSPeter Dunlap 	/*NOTREACHED*/
2877a6d42e7dSPeter Dunlap }
2878aff4bce5Syi zhang - Sun Microsystems - Beijing China 
2879aff4bce5Syi zhang - Sun Microsystems - Beijing China static void
2880aff4bce5Syi zhang - Sun Microsystems - Beijing China idm_so_socket_set_nonblock(struct sonode *node)
2881aff4bce5Syi zhang - Sun Microsystems - Beijing China {
2882aff4bce5Syi zhang - Sun Microsystems - Beijing China 	(void) VOP_SETFL(node->so_vnode, node->so_flag,
2883aff4bce5Syi zhang - Sun Microsystems - Beijing China 	    (node->so_state | FNONBLOCK), CRED(), NULL);
2884aff4bce5Syi zhang - Sun Microsystems - Beijing China }
2885aff4bce5Syi zhang - Sun Microsystems - Beijing China 
2886aff4bce5Syi zhang - Sun Microsystems - Beijing China static void
2887aff4bce5Syi zhang - Sun Microsystems - Beijing China idm_so_socket_set_block(struct sonode *node)
2888aff4bce5Syi zhang - Sun Microsystems - Beijing China {
2889aff4bce5Syi zhang - Sun Microsystems - Beijing China 	(void) VOP_SETFL(node->so_vnode, node->so_flag,
2890aff4bce5Syi zhang - Sun Microsystems - Beijing China 	    (node->so_state & (~FNONBLOCK)), CRED(), NULL);
2891aff4bce5Syi zhang - Sun Microsystems - Beijing China }
2892