xref: /onnv-gate/usr/src/uts/common/io/idm/idm_so.c (revision 9247:331d3de1fc0f)
17978SPeter.Dunlap@Sun.COM /*
27978SPeter.Dunlap@Sun.COM  * CDDL HEADER START
37978SPeter.Dunlap@Sun.COM  *
47978SPeter.Dunlap@Sun.COM  * The contents of this file are subject to the terms of the
57978SPeter.Dunlap@Sun.COM  * Common Development and Distribution License (the "License").
67978SPeter.Dunlap@Sun.COM  * You may not use this file except in compliance with the License.
77978SPeter.Dunlap@Sun.COM  *
87978SPeter.Dunlap@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97978SPeter.Dunlap@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107978SPeter.Dunlap@Sun.COM  * See the License for the specific language governing permissions
117978SPeter.Dunlap@Sun.COM  * and limitations under the License.
127978SPeter.Dunlap@Sun.COM  *
137978SPeter.Dunlap@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147978SPeter.Dunlap@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157978SPeter.Dunlap@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167978SPeter.Dunlap@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177978SPeter.Dunlap@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187978SPeter.Dunlap@Sun.COM  *
197978SPeter.Dunlap@Sun.COM  * CDDL HEADER END
207978SPeter.Dunlap@Sun.COM  */
217978SPeter.Dunlap@Sun.COM /*
228607SJames.Moore@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237978SPeter.Dunlap@Sun.COM  * Use is subject to license terms.
247978SPeter.Dunlap@Sun.COM  */
257978SPeter.Dunlap@Sun.COM 
267978SPeter.Dunlap@Sun.COM #include <sys/conf.h>
277978SPeter.Dunlap@Sun.COM #include <sys/stat.h>
287978SPeter.Dunlap@Sun.COM #include <sys/file.h>
297978SPeter.Dunlap@Sun.COM #include <sys/ddi.h>
307978SPeter.Dunlap@Sun.COM #include <sys/sunddi.h>
317978SPeter.Dunlap@Sun.COM #include <sys/modctl.h>
327978SPeter.Dunlap@Sun.COM #include <sys/priv.h>
337978SPeter.Dunlap@Sun.COM #include <sys/cpuvar.h>
347978SPeter.Dunlap@Sun.COM #include <sys/socket.h>
357978SPeter.Dunlap@Sun.COM #include <sys/strsubr.h>
367978SPeter.Dunlap@Sun.COM #include <sys/sysmacros.h>
377978SPeter.Dunlap@Sun.COM #include <sys/sdt.h>
387978SPeter.Dunlap@Sun.COM #include <netinet/tcp.h>
397978SPeter.Dunlap@Sun.COM #include <inet/tcp.h>
407978SPeter.Dunlap@Sun.COM #include <sys/socketvar.h>
417978SPeter.Dunlap@Sun.COM #include <sys/pathname.h>
427978SPeter.Dunlap@Sun.COM #include <sys/fs/snode.h>
437978SPeter.Dunlap@Sun.COM #include <sys/fs/dv_node.h>
447978SPeter.Dunlap@Sun.COM #include <sys/vnode.h>
457978SPeter.Dunlap@Sun.COM #include <netinet/in.h>
467978SPeter.Dunlap@Sun.COM #include <net/if.h>
477978SPeter.Dunlap@Sun.COM #include <sys/sockio.h>
488348SEric.Yu@Sun.COM #include <sys/ksocket.h>
497978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h>
507978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_so.h>
517978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_text.h>
527978SPeter.Dunlap@Sun.COM 
537978SPeter.Dunlap@Sun.COM /*
547978SPeter.Dunlap@Sun.COM  * in6addr_any is currently all zeroes, but use the macro in case this
557978SPeter.Dunlap@Sun.COM  * ever changes.
567978SPeter.Dunlap@Sun.COM  */
577978SPeter.Dunlap@Sun.COM const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
587978SPeter.Dunlap@Sun.COM 
597978SPeter.Dunlap@Sun.COM static void idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
607978SPeter.Dunlap@Sun.COM static void idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
617978SPeter.Dunlap@Sun.COM static void idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
627978SPeter.Dunlap@Sun.COM 
638348SEric.Yu@Sun.COM static idm_status_t idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so);
647978SPeter.Dunlap@Sun.COM static void idm_so_conn_destroy_common(idm_conn_t *ic);
657978SPeter.Dunlap@Sun.COM static void idm_so_conn_connect_common(idm_conn_t *ic);
667978SPeter.Dunlap@Sun.COM 
677978SPeter.Dunlap@Sun.COM static void idm_set_ini_preconnect_options(idm_so_conn_t *sc);
687978SPeter.Dunlap@Sun.COM static void idm_set_ini_postconnect_options(idm_so_conn_t *sc);
698348SEric.Yu@Sun.COM static void idm_set_tgt_connect_options(ksocket_t so);
707978SPeter.Dunlap@Sun.COM static idm_status_t idm_i_so_tx(idm_pdu_t *pdu);
717978SPeter.Dunlap@Sun.COM 
727978SPeter.Dunlap@Sun.COM static idm_status_t idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu);
739162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt,
749162SPeter.Dunlap@Sun.COM     idm_buf_t *idb, uint32_t offset, uint32_t length);
759162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb);
769162SPeter.Dunlap@Sun.COM static idm_status_t idm_so_send_buf_region(idm_task_t *idt,
777978SPeter.Dunlap@Sun.COM     idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length);
787978SPeter.Dunlap@Sun.COM 
797978SPeter.Dunlap@Sun.COM static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb,
807978SPeter.Dunlap@Sun.COM     uint32_t ro, uint32_t dlength);
817978SPeter.Dunlap@Sun.COM 
827978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_handle_digest(idm_conn_t *it,
837978SPeter.Dunlap@Sun.COM     nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx);
847978SPeter.Dunlap@Sun.COM 
857978SPeter.Dunlap@Sun.COM /*
867978SPeter.Dunlap@Sun.COM  * Transport ops prototypes
877978SPeter.Dunlap@Sun.COM  */
887978SPeter.Dunlap@Sun.COM static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu);
897978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
907978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
917978SPeter.Dunlap@Sun.COM static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu);
927978SPeter.Dunlap@Sun.COM static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu);
937978SPeter.Dunlap@Sun.COM static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu);
947978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt);
957978SPeter.Dunlap@Sun.COM static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it,
967978SPeter.Dunlap@Sun.COM     nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
979162SPeter.Dunlap@Sun.COM static void idm_so_notice_key_values(idm_conn_t *it,
987978SPeter.Dunlap@Sun.COM     nvlist_t *negotiated_nvl);
997978SPeter.Dunlap@Sun.COM static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic,
1007978SPeter.Dunlap@Sun.COM     idm_transport_caps_t *caps);
1017978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen);
1027978SPeter.Dunlap@Sun.COM static void idm_so_buf_free(idm_buf_t *idb);
1037978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_setup(idm_buf_t *idb);
1047978SPeter.Dunlap@Sun.COM static void idm_so_buf_teardown(idm_buf_t *idb);
1057978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is);
1067978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_destroy(idm_svc_t *is);
1077978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is);
1087978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_offline(idm_svc_t *is);
1097978SPeter.Dunlap@Sun.COM static void idm_so_tgt_conn_destroy(idm_conn_t *ic);
1107978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic);
1117978SPeter.Dunlap@Sun.COM static void idm_so_conn_disconnect(idm_conn_t *ic);
1127978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic);
1137978SPeter.Dunlap@Sun.COM static void idm_so_ini_conn_destroy(idm_conn_t *ic);
1147978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic);
1157978SPeter.Dunlap@Sun.COM 
1167978SPeter.Dunlap@Sun.COM /*
1177978SPeter.Dunlap@Sun.COM  * IDM Native Sockets transport operations
1187978SPeter.Dunlap@Sun.COM  */
1197978SPeter.Dunlap@Sun.COM static
1207978SPeter.Dunlap@Sun.COM idm_transport_ops_t idm_so_transport_ops = {
1217978SPeter.Dunlap@Sun.COM 	idm_so_tx,			/* it_tx_pdu */
1227978SPeter.Dunlap@Sun.COM 	idm_so_buf_tx_to_ini,		/* it_buf_tx_to_ini */
1237978SPeter.Dunlap@Sun.COM 	idm_so_buf_rx_from_ini,		/* it_buf_rx_from_ini */
1247978SPeter.Dunlap@Sun.COM 	idm_so_rx_datain,		/* it_rx_datain */
1257978SPeter.Dunlap@Sun.COM 	idm_so_rx_rtt,			/* it_rx_rtt */
1267978SPeter.Dunlap@Sun.COM 	idm_so_rx_dataout,		/* it_rx_dataout */
1277978SPeter.Dunlap@Sun.COM 	NULL,				/* it_alloc_conn_rsrc */
1287978SPeter.Dunlap@Sun.COM 	NULL,				/* it_free_conn_rsrc */
1297978SPeter.Dunlap@Sun.COM 	NULL,				/* it_tgt_enable_datamover */
1307978SPeter.Dunlap@Sun.COM 	NULL,				/* it_ini_enable_datamover */
1317978SPeter.Dunlap@Sun.COM 	NULL,				/* it_conn_terminate */
1327978SPeter.Dunlap@Sun.COM 	idm_so_free_task_rsrc,		/* it_free_task_rsrc */
1337978SPeter.Dunlap@Sun.COM 	idm_so_negotiate_key_values,	/* it_negotiate_key_values */
1347978SPeter.Dunlap@Sun.COM 	idm_so_notice_key_values,	/* it_notice_key_values */
1357978SPeter.Dunlap@Sun.COM 	idm_so_conn_is_capable,		/* it_conn_is_capable */
1367978SPeter.Dunlap@Sun.COM 	idm_so_buf_alloc,		/* it_buf_alloc */
1377978SPeter.Dunlap@Sun.COM 	idm_so_buf_free,		/* it_buf_free */
1387978SPeter.Dunlap@Sun.COM 	idm_so_buf_setup,		/* it_buf_setup */
1397978SPeter.Dunlap@Sun.COM 	idm_so_buf_teardown,		/* it_buf_teardown */
1407978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_create,		/* it_tgt_svc_create */
1417978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_destroy,		/* it_tgt_svc_destroy */
1427978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_online,		/* it_tgt_svc_online */
1437978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_offline,		/* it_tgt_svc_offline */
1447978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_destroy,	/* it_tgt_conn_destroy */
1457978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_connect,	/* it_tgt_conn_connect */
1467978SPeter.Dunlap@Sun.COM 	idm_so_conn_disconnect,		/* it_tgt_conn_disconnect */
1477978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_create,		/* it_ini_conn_create */
1487978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_destroy,	/* it_ini_conn_destroy */
1497978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_connect,	/* it_ini_conn_connect */
1507978SPeter.Dunlap@Sun.COM 	idm_so_conn_disconnect		/* it_ini_conn_disconnect */
1517978SPeter.Dunlap@Sun.COM };
1527978SPeter.Dunlap@Sun.COM 
1537978SPeter.Dunlap@Sun.COM /*
1547978SPeter.Dunlap@Sun.COM  * idm_so_init()
1557978SPeter.Dunlap@Sun.COM  * Sockets transport initialization
1567978SPeter.Dunlap@Sun.COM  */
1577978SPeter.Dunlap@Sun.COM void
1587978SPeter.Dunlap@Sun.COM idm_so_init(idm_transport_t *it)
1597978SPeter.Dunlap@Sun.COM {
1607978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Data and R2T Transmit PDU's */
1617978SPeter.Dunlap@Sun.COM 	idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache",
1627978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
1637978SPeter.Dunlap@Sun.COM 	    &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1647978SPeter.Dunlap@Sun.COM 
1657978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Receive PDU's */
1667978SPeter.Dunlap@Sun.COM 	idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache",
1677978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
1687978SPeter.Dunlap@Sun.COM 	    &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1697978SPeter.Dunlap@Sun.COM 
170*9247SPeter.Dunlap@Sun.COM 	/* 128k buffer cache */
171*9247SPeter.Dunlap@Sun.COM 	idm.idm_so_128k_buf_cache = kmem_cache_create("idm_128k_buf_cache",
172*9247SPeter.Dunlap@Sun.COM 	    IDM_SO_BUF_CACHE_UB, 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
173*9247SPeter.Dunlap@Sun.COM 
1747978SPeter.Dunlap@Sun.COM 	/* Set the sockets transport ops */
1757978SPeter.Dunlap@Sun.COM 	it->it_ops = &idm_so_transport_ops;
1767978SPeter.Dunlap@Sun.COM }
1777978SPeter.Dunlap@Sun.COM 
1787978SPeter.Dunlap@Sun.COM /*
1797978SPeter.Dunlap@Sun.COM  * idm_so_fini()
1807978SPeter.Dunlap@Sun.COM  * Sockets transport teardown
1817978SPeter.Dunlap@Sun.COM  */
1827978SPeter.Dunlap@Sun.COM void
1837978SPeter.Dunlap@Sun.COM idm_so_fini(void)
1847978SPeter.Dunlap@Sun.COM {
185*9247SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_so_128k_buf_cache);
1867978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sotx_pdu_cache);
1877978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sorx_pdu_cache);
1887978SPeter.Dunlap@Sun.COM }
1897978SPeter.Dunlap@Sun.COM 
1908348SEric.Yu@Sun.COM ksocket_t
1917978SPeter.Dunlap@Sun.COM idm_socreate(int domain, int type, int protocol)
1927978SPeter.Dunlap@Sun.COM {
1938348SEric.Yu@Sun.COM 	ksocket_t ks;
1947978SPeter.Dunlap@Sun.COM 
1958348SEric.Yu@Sun.COM 	if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP,
1968348SEric.Yu@Sun.COM 	    CRED())) {
1978348SEric.Yu@Sun.COM 		return (ks);
1988348SEric.Yu@Sun.COM 	} else {
1998348SEric.Yu@Sun.COM 		return (NULL);
2007978SPeter.Dunlap@Sun.COM 	}
2017978SPeter.Dunlap@Sun.COM }
2027978SPeter.Dunlap@Sun.COM 
2037978SPeter.Dunlap@Sun.COM /*
2047978SPeter.Dunlap@Sun.COM  * idm_soshutdown will disconnect the socket and prevent subsequent PDU
2057978SPeter.Dunlap@Sun.COM  * reception and transmission.  The sonode still exists but its state
2067978SPeter.Dunlap@Sun.COM  * gets modified to indicate it is no longer connected.  Calls to
2077978SPeter.Dunlap@Sun.COM  * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used
2087978SPeter.Dunlap@Sun.COM  * regain control of a thread stuck in idm_sorecv.
2097978SPeter.Dunlap@Sun.COM  */
2107978SPeter.Dunlap@Sun.COM void
2118348SEric.Yu@Sun.COM idm_soshutdown(ksocket_t so)
2127978SPeter.Dunlap@Sun.COM {
2138348SEric.Yu@Sun.COM 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
2147978SPeter.Dunlap@Sun.COM }
2157978SPeter.Dunlap@Sun.COM 
2167978SPeter.Dunlap@Sun.COM /*
2177978SPeter.Dunlap@Sun.COM  * idm_sodestroy releases all resources associated with a socket previously
2187978SPeter.Dunlap@Sun.COM  * created with idm_socreate.  The socket must be shutdown using
2197978SPeter.Dunlap@Sun.COM  * idm_soshutdown before the socket is destroyed with idm_sodestroy,
2207978SPeter.Dunlap@Sun.COM  * otherwise undefined behavior will result.
2217978SPeter.Dunlap@Sun.COM  */
2227978SPeter.Dunlap@Sun.COM void
2238348SEric.Yu@Sun.COM idm_sodestroy(ksocket_t ks)
2247978SPeter.Dunlap@Sun.COM {
2258348SEric.Yu@Sun.COM 	(void) ksocket_close(ks, CRED());
2267978SPeter.Dunlap@Sun.COM }
2277978SPeter.Dunlap@Sun.COM 
2287978SPeter.Dunlap@Sun.COM /*
2297978SPeter.Dunlap@Sun.COM  * IP address filter functions to flag addresses that should not
2307978SPeter.Dunlap@Sun.COM  * go out to initiators through discovery.
2317978SPeter.Dunlap@Sun.COM  */
2327978SPeter.Dunlap@Sun.COM static boolean_t
2337978SPeter.Dunlap@Sun.COM idm_v4_addr_okay(struct in_addr *in_addr)
2347978SPeter.Dunlap@Sun.COM {
2357978SPeter.Dunlap@Sun.COM 	in_addr_t addr = ntohl(in_addr->s_addr);
2367978SPeter.Dunlap@Sun.COM 
2377978SPeter.Dunlap@Sun.COM 	if ((INADDR_NONE == addr) ||
2387978SPeter.Dunlap@Sun.COM 	    (IN_MULTICAST(addr)) ||
2397978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == 0) ||
2407978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
2417978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
2427978SPeter.Dunlap@Sun.COM 	}
2437978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
2447978SPeter.Dunlap@Sun.COM }
2457978SPeter.Dunlap@Sun.COM 
2467978SPeter.Dunlap@Sun.COM static boolean_t
2477978SPeter.Dunlap@Sun.COM idm_v6_addr_okay(struct in6_addr *addr6)
2487978SPeter.Dunlap@Sun.COM {
2497978SPeter.Dunlap@Sun.COM 
2507978SPeter.Dunlap@Sun.COM 	if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) ||
2517978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LOOPBACK(addr6)) ||
2527978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_MULTICAST(addr6)) ||
2537978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4MAPPED(addr6)) ||
2547978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4COMPAT(addr6)) ||
2557978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LINKLOCAL(addr6))) {
2567978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
2577978SPeter.Dunlap@Sun.COM 	}
2587978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
2597978SPeter.Dunlap@Sun.COM }
2607978SPeter.Dunlap@Sun.COM 
2617978SPeter.Dunlap@Sun.COM /*
2627978SPeter.Dunlap@Sun.COM  * idm_get_ipaddr will retrieve a list of IP Addresses which the host is
2637978SPeter.Dunlap@Sun.COM  * configured with by sending down a sequence of kernel ioctl to IP STREAMS.
2647978SPeter.Dunlap@Sun.COM  */
2657978SPeter.Dunlap@Sun.COM int
2667978SPeter.Dunlap@Sun.COM idm_get_ipaddr(idm_addr_list_t **ipaddr_p)
2677978SPeter.Dunlap@Sun.COM {
2688348SEric.Yu@Sun.COM 	ksocket_t 		so4, so6;
2697978SPeter.Dunlap@Sun.COM 	struct lifnum		lifn;
2707978SPeter.Dunlap@Sun.COM 	struct lifconf		lifc;
2717978SPeter.Dunlap@Sun.COM 	struct lifreq		*lp;
2727978SPeter.Dunlap@Sun.COM 	int			rval;
2737978SPeter.Dunlap@Sun.COM 	int			numifs;
2747978SPeter.Dunlap@Sun.COM 	int			bufsize;
2757978SPeter.Dunlap@Sun.COM 	void			*buf;
2767978SPeter.Dunlap@Sun.COM 	int			i, j, n, rc;
2777978SPeter.Dunlap@Sun.COM 	struct sockaddr_storage	ss;
2787978SPeter.Dunlap@Sun.COM 	struct sockaddr_in	*sin;
2797978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	*sin6;
2807978SPeter.Dunlap@Sun.COM 	idm_addr_t		*ip;
2817978SPeter.Dunlap@Sun.COM 	idm_addr_list_t		*ipaddr;
2827978SPeter.Dunlap@Sun.COM 	int			size_ipaddr;
2837978SPeter.Dunlap@Sun.COM 
2847978SPeter.Dunlap@Sun.COM 	*ipaddr_p = NULL;
2857978SPeter.Dunlap@Sun.COM 	size_ipaddr = 0;
2867978SPeter.Dunlap@Sun.COM 	buf = NULL;
2877978SPeter.Dunlap@Sun.COM 
2887978SPeter.Dunlap@Sun.COM 	/* create an ipv4 and ipv6 UDP socket */
2897978SPeter.Dunlap@Sun.COM 	if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL)
2907978SPeter.Dunlap@Sun.COM 		return (0);
2917978SPeter.Dunlap@Sun.COM 	if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) {
2927978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so6);
2937978SPeter.Dunlap@Sun.COM 		return (0);
2947978SPeter.Dunlap@Sun.COM 	}
2957978SPeter.Dunlap@Sun.COM 
2967978SPeter.Dunlap@Sun.COM 
2977978SPeter.Dunlap@Sun.COM retry_count:
2987978SPeter.Dunlap@Sun.COM 	/* snapshot the current number of interfaces */
2997978SPeter.Dunlap@Sun.COM 	lifn.lifn_family = PF_UNSPEC;
3007978SPeter.Dunlap@Sun.COM 	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
3017978SPeter.Dunlap@Sun.COM 	lifn.lifn_count = 0;
3028348SEric.Yu@Sun.COM 	/* use vp6 for ioctls with unspecified families by default */
3038348SEric.Yu@Sun.COM 	if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED())
3048348SEric.Yu@Sun.COM 	    != 0) {
3057978SPeter.Dunlap@Sun.COM 		goto cleanup;
3067978SPeter.Dunlap@Sun.COM 	}
3077978SPeter.Dunlap@Sun.COM 
3087978SPeter.Dunlap@Sun.COM 	numifs = lifn.lifn_count;
3097978SPeter.Dunlap@Sun.COM 	if (numifs <= 0) {
3107978SPeter.Dunlap@Sun.COM 		goto cleanup;
3117978SPeter.Dunlap@Sun.COM 	}
3127978SPeter.Dunlap@Sun.COM 
3137978SPeter.Dunlap@Sun.COM 	/* allocate extra room in case more interfaces appear */
3147978SPeter.Dunlap@Sun.COM 	numifs += 10;
3157978SPeter.Dunlap@Sun.COM 
3167978SPeter.Dunlap@Sun.COM 	/* get the interface names and ip addresses */
3177978SPeter.Dunlap@Sun.COM 	bufsize = numifs * sizeof (struct lifreq);
3187978SPeter.Dunlap@Sun.COM 	buf = kmem_alloc(bufsize, KM_SLEEP);
3197978SPeter.Dunlap@Sun.COM 
3207978SPeter.Dunlap@Sun.COM 	lifc.lifc_family = AF_UNSPEC;
3217978SPeter.Dunlap@Sun.COM 	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
3227978SPeter.Dunlap@Sun.COM 	lifc.lifc_len = bufsize;
3237978SPeter.Dunlap@Sun.COM 	lifc.lifc_buf = buf;
3248348SEric.Yu@Sun.COM 	rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED());
3257978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
3267978SPeter.Dunlap@Sun.COM 		goto cleanup;
3277978SPeter.Dunlap@Sun.COM 	}
3287978SPeter.Dunlap@Sun.COM 	/* if our extra room is used up, try again */
3297978SPeter.Dunlap@Sun.COM 	if (bufsize <= lifc.lifc_len) {
3307978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
3317978SPeter.Dunlap@Sun.COM 		buf = NULL;
3327978SPeter.Dunlap@Sun.COM 		goto retry_count;
3337978SPeter.Dunlap@Sun.COM 	}
3347978SPeter.Dunlap@Sun.COM 	/* calc actual number of ifconfs */
3357978SPeter.Dunlap@Sun.COM 	n = lifc.lifc_len / sizeof (struct lifreq);
3367978SPeter.Dunlap@Sun.COM 
3377978SPeter.Dunlap@Sun.COM 	/* get ip address */
3387978SPeter.Dunlap@Sun.COM 	if (n > 0) {
3397978SPeter.Dunlap@Sun.COM 		size_ipaddr = sizeof (idm_addr_list_t) +
3407978SPeter.Dunlap@Sun.COM 		    (n - 1) * sizeof (idm_addr_t);
3417978SPeter.Dunlap@Sun.COM 		ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP);
3427978SPeter.Dunlap@Sun.COM 	} else {
3437978SPeter.Dunlap@Sun.COM 		goto cleanup;
3447978SPeter.Dunlap@Sun.COM 	}
3457978SPeter.Dunlap@Sun.COM 
3467978SPeter.Dunlap@Sun.COM 	/*
3477978SPeter.Dunlap@Sun.COM 	 * Examine the array of interfaces and filter uninteresting ones
3487978SPeter.Dunlap@Sun.COM 	 */
3497978SPeter.Dunlap@Sun.COM 	for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) {
3507978SPeter.Dunlap@Sun.COM 
3517978SPeter.Dunlap@Sun.COM 		/*
3527978SPeter.Dunlap@Sun.COM 		 * Copy the address as the SIOCGLIFFLAGS ioctl is destructive
3537978SPeter.Dunlap@Sun.COM 		 */
3547978SPeter.Dunlap@Sun.COM 		ss = lp->lifr_addr;
3557978SPeter.Dunlap@Sun.COM 		/*
3567978SPeter.Dunlap@Sun.COM 		 * fetch the flags using the socket of the correct family
3577978SPeter.Dunlap@Sun.COM 		 */
3587978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
3597978SPeter.Dunlap@Sun.COM 		case AF_INET:
3608348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp,
3618348SEric.Yu@Sun.COM 			    &rval, CRED());
3627978SPeter.Dunlap@Sun.COM 			break;
3637978SPeter.Dunlap@Sun.COM 		case AF_INET6:
3648348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp,
3658348SEric.Yu@Sun.COM 			    &rval, CRED());
3667978SPeter.Dunlap@Sun.COM 			break;
3677978SPeter.Dunlap@Sun.COM 		default:
3687978SPeter.Dunlap@Sun.COM 			continue;
3697978SPeter.Dunlap@Sun.COM 		}
3707978SPeter.Dunlap@Sun.COM 		if (rc == 0) {
3717978SPeter.Dunlap@Sun.COM 			/*
3727978SPeter.Dunlap@Sun.COM 			 * If we got the flags, skip uninteresting
3737978SPeter.Dunlap@Sun.COM 			 * interfaces based on flags
3747978SPeter.Dunlap@Sun.COM 			 */
3757978SPeter.Dunlap@Sun.COM 			if ((lp->lifr_flags & IFF_UP) != IFF_UP)
3767978SPeter.Dunlap@Sun.COM 				continue;
3777978SPeter.Dunlap@Sun.COM 			if (lp->lifr_flags &
3787978SPeter.Dunlap@Sun.COM 			    (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED))
3797978SPeter.Dunlap@Sun.COM 				continue;
3807978SPeter.Dunlap@Sun.COM 		}
3817978SPeter.Dunlap@Sun.COM 
3827978SPeter.Dunlap@Sun.COM 		/* save ip address */
3837978SPeter.Dunlap@Sun.COM 		ip = &ipaddr->al_addrs[j];
3847978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
3857978SPeter.Dunlap@Sun.COM 		case AF_INET:
3867978SPeter.Dunlap@Sun.COM 			sin = (struct sockaddr_in *)&ss;
3877978SPeter.Dunlap@Sun.COM 			if (!idm_v4_addr_okay(&sin->sin_addr))
3887978SPeter.Dunlap@Sun.COM 				continue;
3897978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in4 = sin->sin_addr;
3907978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in_addr);
3917978SPeter.Dunlap@Sun.COM 			break;
3927978SPeter.Dunlap@Sun.COM 		case AF_INET6:
3937978SPeter.Dunlap@Sun.COM 			sin6 = (struct sockaddr_in6 *)&ss;
3947978SPeter.Dunlap@Sun.COM 			if (!idm_v6_addr_okay(&sin6->sin6_addr))
3957978SPeter.Dunlap@Sun.COM 				continue;
3967978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in6 = sin6->sin6_addr;
3977978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in6_addr);
3987978SPeter.Dunlap@Sun.COM 			break;
3997978SPeter.Dunlap@Sun.COM 		default:
4007978SPeter.Dunlap@Sun.COM 			continue;
4017978SPeter.Dunlap@Sun.COM 		}
4027978SPeter.Dunlap@Sun.COM 		j++;
4037978SPeter.Dunlap@Sun.COM 	}
4047978SPeter.Dunlap@Sun.COM 
4057978SPeter.Dunlap@Sun.COM 	if (j == 0) {
4067978SPeter.Dunlap@Sun.COM 		/* no valid ifaddr */
4077978SPeter.Dunlap@Sun.COM 		kmem_free(ipaddr, size_ipaddr);
4087978SPeter.Dunlap@Sun.COM 		size_ipaddr = 0;
4097978SPeter.Dunlap@Sun.COM 		ipaddr = NULL;
4107978SPeter.Dunlap@Sun.COM 	} else {
4117978SPeter.Dunlap@Sun.COM 		ipaddr->al_out_cnt = j;
4127978SPeter.Dunlap@Sun.COM 	}
4137978SPeter.Dunlap@Sun.COM 
4147978SPeter.Dunlap@Sun.COM 
4157978SPeter.Dunlap@Sun.COM cleanup:
4167978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so6);
4177978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so4);
4187978SPeter.Dunlap@Sun.COM 
4197978SPeter.Dunlap@Sun.COM 	if (buf != NULL)
4207978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
4217978SPeter.Dunlap@Sun.COM 
4227978SPeter.Dunlap@Sun.COM 	*ipaddr_p = ipaddr;
4237978SPeter.Dunlap@Sun.COM 	return (size_ipaddr);
4247978SPeter.Dunlap@Sun.COM }
4257978SPeter.Dunlap@Sun.COM 
4267978SPeter.Dunlap@Sun.COM int
4278348SEric.Yu@Sun.COM idm_sorecv(ksocket_t so, void *msg, size_t len)
4287978SPeter.Dunlap@Sun.COM {
4297978SPeter.Dunlap@Sun.COM 	iovec_t iov;
4307978SPeter.Dunlap@Sun.COM 
4317978SPeter.Dunlap@Sun.COM 	ASSERT(so != NULL);
4327978SPeter.Dunlap@Sun.COM 	ASSERT(len != 0);
4337978SPeter.Dunlap@Sun.COM 
4347978SPeter.Dunlap@Sun.COM 	/*
4357978SPeter.Dunlap@Sun.COM 	 * Fill in iovec and receive data
4367978SPeter.Dunlap@Sun.COM 	 */
4377978SPeter.Dunlap@Sun.COM 	iov.iov_base = msg;
4387978SPeter.Dunlap@Sun.COM 	iov.iov_len = len;
4397978SPeter.Dunlap@Sun.COM 
4407978SPeter.Dunlap@Sun.COM 	return (idm_iov_sorecv(so, &iov, 1, len));
4417978SPeter.Dunlap@Sun.COM }
4427978SPeter.Dunlap@Sun.COM 
4437978SPeter.Dunlap@Sun.COM /*
4447978SPeter.Dunlap@Sun.COM  * idm_sosendto - Sends a buffered data on a non-connected socket.
4457978SPeter.Dunlap@Sun.COM  *
4467978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
4477978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
4487978SPeter.Dunlap@Sun.COM  * occurs.
4497978SPeter.Dunlap@Sun.COM  *
4507978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
4517978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
4527978SPeter.Dunlap@Sun.COM  */
4537978SPeter.Dunlap@Sun.COM int
4548348SEric.Yu@Sun.COM idm_sosendto(ksocket_t so, void *buff, size_t len,
4557978SPeter.Dunlap@Sun.COM     struct sockaddr *name, socklen_t namelen)
4567978SPeter.Dunlap@Sun.COM {
4577978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
4587978SPeter.Dunlap@Sun.COM 	struct iovec		iov[1];
4597978SPeter.Dunlap@Sun.COM 	int			error;
4608348SEric.Yu@Sun.COM 	size_t			sent = 0;
4617978SPeter.Dunlap@Sun.COM 
4627978SPeter.Dunlap@Sun.COM 	iov[0].iov_base	= buff;
4637978SPeter.Dunlap@Sun.COM 	iov[0].iov_len	= len;
4647978SPeter.Dunlap@Sun.COM 
4657978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
4667978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
4677978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iov;
4687978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= 1;
4697978SPeter.Dunlap@Sun.COM 	msg.msg_name	= name;
4707978SPeter.Dunlap@Sun.COM 	msg.msg_namelen	= namelen;
4717978SPeter.Dunlap@Sun.COM 
4728348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) {
4737978SPeter.Dunlap@Sun.COM 		/* Data sent */
4748348SEric.Yu@Sun.COM 		if (sent == len) {
4757978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
4767978SPeter.Dunlap@Sun.COM 			return (0);
4777978SPeter.Dunlap@Sun.COM 		} else {
4787978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
4797978SPeter.Dunlap@Sun.COM 			return (-1);
4807978SPeter.Dunlap@Sun.COM 		}
4817978SPeter.Dunlap@Sun.COM 	}
4827978SPeter.Dunlap@Sun.COM 
4837978SPeter.Dunlap@Sun.COM 	/* Send failed */
4847978SPeter.Dunlap@Sun.COM 	return (error);
4857978SPeter.Dunlap@Sun.COM }
4867978SPeter.Dunlap@Sun.COM 
4877978SPeter.Dunlap@Sun.COM /*
4887978SPeter.Dunlap@Sun.COM  * idm_iov_sosend - Sends an iovec on a connection.
4897978SPeter.Dunlap@Sun.COM  *
4907978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
4917978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
4927978SPeter.Dunlap@Sun.COM  * occurs.
4937978SPeter.Dunlap@Sun.COM  *
4947978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
4957978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
4967978SPeter.Dunlap@Sun.COM  */
4977978SPeter.Dunlap@Sun.COM int
4988348SEric.Yu@Sun.COM idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
4997978SPeter.Dunlap@Sun.COM {
5007978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
5017978SPeter.Dunlap@Sun.COM 	int			error;
5028348SEric.Yu@Sun.COM 	size_t 			sent = 0;
5037978SPeter.Dunlap@Sun.COM 
5047978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
5057978SPeter.Dunlap@Sun.COM 
5067978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
5077978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
5087978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
5097978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
5107978SPeter.Dunlap@Sun.COM 
5118348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED()))
5128348SEric.Yu@Sun.COM 	    == 0) {
5137978SPeter.Dunlap@Sun.COM 		/* Data sent */
5148348SEric.Yu@Sun.COM 		if (sent == total_len) {
5157978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
5167978SPeter.Dunlap@Sun.COM 			return (0);
5177978SPeter.Dunlap@Sun.COM 		} else {
5187978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
5197978SPeter.Dunlap@Sun.COM 			return (-1);
5207978SPeter.Dunlap@Sun.COM 		}
5217978SPeter.Dunlap@Sun.COM 	}
5227978SPeter.Dunlap@Sun.COM 
5237978SPeter.Dunlap@Sun.COM 	/* Send failed */
5247978SPeter.Dunlap@Sun.COM 	return (error);
5257978SPeter.Dunlap@Sun.COM }
5267978SPeter.Dunlap@Sun.COM 
5277978SPeter.Dunlap@Sun.COM /*
5287978SPeter.Dunlap@Sun.COM  * idm_iov_sorecv - Receives an iovec from a connection
5297978SPeter.Dunlap@Sun.COM  *
5307978SPeter.Dunlap@Sun.COM  * This function gets the data asked for from the socket.  It will return
5317978SPeter.Dunlap@Sun.COM  * only when all the requested data has been retrieved or if an error
5327978SPeter.Dunlap@Sun.COM  * occurs.
5337978SPeter.Dunlap@Sun.COM  *
5347978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
5357978SPeter.Dunlap@Sun.COM  * -1 if sorecvmsg returns success but uio_resid != 0
5367978SPeter.Dunlap@Sun.COM  */
5377978SPeter.Dunlap@Sun.COM int
5388348SEric.Yu@Sun.COM idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
5397978SPeter.Dunlap@Sun.COM {
5407978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
5417978SPeter.Dunlap@Sun.COM 	int			error;
5428348SEric.Yu@Sun.COM 	size_t			recv;
5438348SEric.Yu@Sun.COM 	int 			flags;
5447978SPeter.Dunlap@Sun.COM 
5457978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
5467978SPeter.Dunlap@Sun.COM 
5477978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
5487978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
5497978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
5507978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
5518348SEric.Yu@Sun.COM 	flags		= MSG_WAITALL;
5527978SPeter.Dunlap@Sun.COM 
5538348SEric.Yu@Sun.COM 	if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED()))
5548348SEric.Yu@Sun.COM 	    == 0) {
5557978SPeter.Dunlap@Sun.COM 		/* Received data */
5568348SEric.Yu@Sun.COM 		if (recv == total_len) {
5577978SPeter.Dunlap@Sun.COM 			/* All requested data received.  Success */
5587978SPeter.Dunlap@Sun.COM 			return (0);
5597978SPeter.Dunlap@Sun.COM 		} else {
5607978SPeter.Dunlap@Sun.COM 			/*
5617978SPeter.Dunlap@Sun.COM 			 * Not all data was received.  The connection has
5627978SPeter.Dunlap@Sun.COM 			 * probably failed.
5637978SPeter.Dunlap@Sun.COM 			 */
5647978SPeter.Dunlap@Sun.COM 			return (-1);
5657978SPeter.Dunlap@Sun.COM 		}
5667978SPeter.Dunlap@Sun.COM 	}
5677978SPeter.Dunlap@Sun.COM 
5687978SPeter.Dunlap@Sun.COM 	/* Receive failed */
5697978SPeter.Dunlap@Sun.COM 	return (error);
5707978SPeter.Dunlap@Sun.COM }
5717978SPeter.Dunlap@Sun.COM 
5727978SPeter.Dunlap@Sun.COM static void
5737978SPeter.Dunlap@Sun.COM idm_set_ini_preconnect_options(idm_so_conn_t *sc)
5747978SPeter.Dunlap@Sun.COM {
5757978SPeter.Dunlap@Sun.COM 	int	conn_abort = 10000;
5767978SPeter.Dunlap@Sun.COM 	int	conn_notify = 2000;
5777978SPeter.Dunlap@Sun.COM 	int	abort = 30000;
5787978SPeter.Dunlap@Sun.COM 
5797978SPeter.Dunlap@Sun.COM 	/* Pre-connect socket options */
5808348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5818348SEric.Yu@Sun.COM 	    TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int),
5828348SEric.Yu@Sun.COM 	    CRED());
5838348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5848348SEric.Yu@Sun.COM 	    TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int),
5858348SEric.Yu@Sun.COM 	    CRED());
5868348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
5878348SEric.Yu@Sun.COM 	    (char *)&abort, sizeof (int), CRED());
5887978SPeter.Dunlap@Sun.COM }
5897978SPeter.Dunlap@Sun.COM 
5907978SPeter.Dunlap@Sun.COM static void
5917978SPeter.Dunlap@Sun.COM idm_set_ini_postconnect_options(idm_so_conn_t *sc)
5927978SPeter.Dunlap@Sun.COM {
5937978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
5947978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
5957978SPeter.Dunlap@Sun.COM 	const int	on = 1;
5967978SPeter.Dunlap@Sun.COM 
5977978SPeter.Dunlap@Sun.COM 	/* Set postconnect options */
5988348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY,
5998348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (int), CRED());
6008348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF,
6018348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
6028348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF,
6038348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
6047978SPeter.Dunlap@Sun.COM }
6057978SPeter.Dunlap@Sun.COM 
6067978SPeter.Dunlap@Sun.COM static void
6078348SEric.Yu@Sun.COM idm_set_tgt_connect_options(ksocket_t ks)
6087978SPeter.Dunlap@Sun.COM {
6097978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
6107978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
6117978SPeter.Dunlap@Sun.COM 	const int	on = 1;
6127978SPeter.Dunlap@Sun.COM 
6137978SPeter.Dunlap@Sun.COM 	/* Set connect options */
6148348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF,
6158348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
6168348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF,
6178348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
6188348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY,
6198348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (on), CRED());
6207978SPeter.Dunlap@Sun.COM }
6217978SPeter.Dunlap@Sun.COM 
6227978SPeter.Dunlap@Sun.COM static uint32_t
6237978SPeter.Dunlap@Sun.COM n2h24(const uchar_t *ptr)
6247978SPeter.Dunlap@Sun.COM {
6257978SPeter.Dunlap@Sun.COM 	return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
6267978SPeter.Dunlap@Sun.COM }
6277978SPeter.Dunlap@Sun.COM 
6287978SPeter.Dunlap@Sun.COM 
6297978SPeter.Dunlap@Sun.COM static idm_status_t
6307978SPeter.Dunlap@Sun.COM idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu)
6317978SPeter.Dunlap@Sun.COM {
6327978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t	*bhs;
6337978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
6347978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
6357978SPeter.Dunlap@Sun.COM 	void		*new_hdr;
6367978SPeter.Dunlap@Sun.COM 	int		ahslen = 0;
6377978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
6387978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
6397978SPeter.Dunlap@Sun.COM 	struct iovec	iov[2];
6407978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
6417978SPeter.Dunlap@Sun.COM 	int		rc;
6427978SPeter.Dunlap@Sun.COM 
6437978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
6447978SPeter.Dunlap@Sun.COM 
6457978SPeter.Dunlap@Sun.COM 	/*
6467978SPeter.Dunlap@Sun.COM 	 * Read BHS
6477978SPeter.Dunlap@Sun.COM 	 */
6487978SPeter.Dunlap@Sun.COM 	bhs = pdu->isp_hdr;
6497978SPeter.Dunlap@Sun.COM 	rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t));
6507978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
6517978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
6527978SPeter.Dunlap@Sun.COM 	}
6537978SPeter.Dunlap@Sun.COM 
6547978SPeter.Dunlap@Sun.COM 	/*
6557978SPeter.Dunlap@Sun.COM 	 * Check actual AHS length against the amount available in the buffer
6567978SPeter.Dunlap@Sun.COM 	 */
6577978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
6587978SPeter.Dunlap@Sun.COM 	    (bhs->hlength * sizeof (uint32_t));
6597978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = n2h24(bhs->dlength);
6607978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) {
6617978SPeter.Dunlap@Sun.COM 		/* Allocate a new header segment and change the callback */
6627978SPeter.Dunlap@Sun.COM 		new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP);
6637978SPeter.Dunlap@Sun.COM 		bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t));
6647978SPeter.Dunlap@Sun.COM 		pdu->isp_hdr = new_hdr;
6657978SPeter.Dunlap@Sun.COM 		pdu->isp_flags |= IDM_PDU_ADDL_HDR;
6667978SPeter.Dunlap@Sun.COM 
6677978SPeter.Dunlap@Sun.COM 		/*
6687978SPeter.Dunlap@Sun.COM 		 * This callback will restore the expected values after
6697978SPeter.Dunlap@Sun.COM 		 * the RX PDU has been processed.
6707978SPeter.Dunlap@Sun.COM 		 */
6717978SPeter.Dunlap@Sun.COM 		pdu->isp_callback = idm_sorx_addl_pdu_cb;
6727978SPeter.Dunlap@Sun.COM 	}
6737978SPeter.Dunlap@Sun.COM 
6747978SPeter.Dunlap@Sun.COM 	/*
6757978SPeter.Dunlap@Sun.COM 	 * Setup receipt of additional header and header digest (if enabled).
6767978SPeter.Dunlap@Sun.COM 	 */
6777978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > 0) {
6787978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1);
6797978SPeter.Dunlap@Sun.COM 		ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t);
6807978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = ahslen;
6817978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
6827978SPeter.Dunlap@Sun.COM 		iovlen++;
6837978SPeter.Dunlap@Sun.COM 	}
6847978SPeter.Dunlap@Sun.COM 
6857978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
6867978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc;
6877978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = sizeof (hdr_digest_crc);
6887978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
6897978SPeter.Dunlap@Sun.COM 		iovlen++;
6907978SPeter.Dunlap@Sun.COM 	}
6917978SPeter.Dunlap@Sun.COM 
6927978SPeter.Dunlap@Sun.COM 	if ((iovlen != 0) &&
6937978SPeter.Dunlap@Sun.COM 	    (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen,
6947978SPeter.Dunlap@Sun.COM 	    total_len) != 0)) {
6957978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
6967978SPeter.Dunlap@Sun.COM 	}
6977978SPeter.Dunlap@Sun.COM 
6987978SPeter.Dunlap@Sun.COM 	/*
6997978SPeter.Dunlap@Sun.COM 	 * Validate header digest if enabled
7007978SPeter.Dunlap@Sun.COM 	 */
7017978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
7027978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_hdr,
7037978SPeter.Dunlap@Sun.COM 		    sizeof (iscsi_hdr_t) + ahslen);
7047978SPeter.Dunlap@Sun.COM 		if (crc_calculated != hdr_digest_crc) {
7057978SPeter.Dunlap@Sun.COM 			/* Invalid Header Digest */
7067978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_HEADER_DIGEST);
7077978SPeter.Dunlap@Sun.COM 		}
7087978SPeter.Dunlap@Sun.COM 	}
7097978SPeter.Dunlap@Sun.COM 
7107978SPeter.Dunlap@Sun.COM 	return (0);
7117978SPeter.Dunlap@Sun.COM }
7127978SPeter.Dunlap@Sun.COM 
7137978SPeter.Dunlap@Sun.COM /*
7147978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create()
7157978SPeter.Dunlap@Sun.COM  * Allocate the sockets transport connection resources.
7167978SPeter.Dunlap@Sun.COM  */
7177978SPeter.Dunlap@Sun.COM static idm_status_t
7187978SPeter.Dunlap@Sun.COM idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic)
7197978SPeter.Dunlap@Sun.COM {
7208348SEric.Yu@Sun.COM 	ksocket_t	so;
7217978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
7227978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
7237978SPeter.Dunlap@Sun.COM 
7247978SPeter.Dunlap@Sun.COM 	so = idm_socreate(cr->cr_domain, cr->cr_type,
7257978SPeter.Dunlap@Sun.COM 	    cr->cr_protocol);
7267978SPeter.Dunlap@Sun.COM 	if (so == NULL) {
7277978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7287978SPeter.Dunlap@Sun.COM 	}
7297978SPeter.Dunlap@Sun.COM 
7307978SPeter.Dunlap@Sun.COM 	/* Bind the socket if configured to do so */
7317978SPeter.Dunlap@Sun.COM 	if (cr->cr_bound) {
7328348SEric.Yu@Sun.COM 		if (ksocket_bind(so, &cr->cr_bound_addr.sin,
7338348SEric.Yu@Sun.COM 		    SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) {
7347978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so);
7357978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
7367978SPeter.Dunlap@Sun.COM 		}
7377978SPeter.Dunlap@Sun.COM 	}
7387978SPeter.Dunlap@Sun.COM 
7397978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, so);
7407978SPeter.Dunlap@Sun.COM 	if (idmrc != IDM_STATUS_SUCCESS) {
7417978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so);
7427978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so);
7437978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7447978SPeter.Dunlap@Sun.COM 	}
7457978SPeter.Dunlap@Sun.COM 
7467978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
7477978SPeter.Dunlap@Sun.COM 	/* Set up socket options */
7487978SPeter.Dunlap@Sun.COM 	idm_set_ini_preconnect_options(so_conn);
7497978SPeter.Dunlap@Sun.COM 
7507978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
7517978SPeter.Dunlap@Sun.COM }
7527978SPeter.Dunlap@Sun.COM 
7537978SPeter.Dunlap@Sun.COM /*
7547978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_destroy()
7557978SPeter.Dunlap@Sun.COM  * Tear down the sockets transport connection resources.
7567978SPeter.Dunlap@Sun.COM  */
7577978SPeter.Dunlap@Sun.COM static void
7587978SPeter.Dunlap@Sun.COM idm_so_ini_conn_destroy(idm_conn_t *ic)
7597978SPeter.Dunlap@Sun.COM {
7607978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
7617978SPeter.Dunlap@Sun.COM }
7627978SPeter.Dunlap@Sun.COM 
7637978SPeter.Dunlap@Sun.COM /*
7647978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_connect()
7657978SPeter.Dunlap@Sun.COM  * Establish the connection referred to by the handle previously allocated via
7667978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create().
7677978SPeter.Dunlap@Sun.COM  */
7687978SPeter.Dunlap@Sun.COM static idm_status_t
7697978SPeter.Dunlap@Sun.COM idm_so_ini_conn_connect(idm_conn_t *ic)
7707978SPeter.Dunlap@Sun.COM {
7717978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
7727978SPeter.Dunlap@Sun.COM 
7737978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
7747978SPeter.Dunlap@Sun.COM 
7758348SEric.Yu@Sun.COM 	if (ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin,
7768348SEric.Yu@Sun.COM 	    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED()) != 0) {
7777978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_conn->ic_so);
7787978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7797978SPeter.Dunlap@Sun.COM 	}
7807978SPeter.Dunlap@Sun.COM 
7817978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
7827978SPeter.Dunlap@Sun.COM 
7837978SPeter.Dunlap@Sun.COM 	idm_set_ini_postconnect_options(so_conn);
7847978SPeter.Dunlap@Sun.COM 
7857978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
7867978SPeter.Dunlap@Sun.COM }
7877978SPeter.Dunlap@Sun.COM 
7887978SPeter.Dunlap@Sun.COM idm_status_t
7898348SEric.Yu@Sun.COM idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so)
7907978SPeter.Dunlap@Sun.COM {
7917978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
7927978SPeter.Dunlap@Sun.COM 
7937978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, new_so);
7947978SPeter.Dunlap@Sun.COM 
7957978SPeter.Dunlap@Sun.COM 	return (idmrc);
7967978SPeter.Dunlap@Sun.COM }
7977978SPeter.Dunlap@Sun.COM 
7987978SPeter.Dunlap@Sun.COM static void
7997978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_destroy(idm_conn_t *ic)
8007978SPeter.Dunlap@Sun.COM {
8017978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
8027978SPeter.Dunlap@Sun.COM }
8037978SPeter.Dunlap@Sun.COM 
8047978SPeter.Dunlap@Sun.COM /*
8057978SPeter.Dunlap@Sun.COM  * idm_so_tgt_conn_connect()
8067978SPeter.Dunlap@Sun.COM  * Establish the connection in ic, passed from idm_tgt_conn_finish(), which
8077978SPeter.Dunlap@Sun.COM  * is invoked from the SM as a result of an inbound connection request.
8087978SPeter.Dunlap@Sun.COM  */
8097978SPeter.Dunlap@Sun.COM static idm_status_t
8107978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_connect(idm_conn_t *ic)
8117978SPeter.Dunlap@Sun.COM {
8127978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
8137978SPeter.Dunlap@Sun.COM 
8147978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
8157978SPeter.Dunlap@Sun.COM }
8167978SPeter.Dunlap@Sun.COM 
8177978SPeter.Dunlap@Sun.COM static idm_status_t
8188348SEric.Yu@Sun.COM idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so)
8197978SPeter.Dunlap@Sun.COM {
8207978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8217978SPeter.Dunlap@Sun.COM 
8227978SPeter.Dunlap@Sun.COM 	so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP);
8237978SPeter.Dunlap@Sun.COM 	so_conn->ic_so = new_so;
8247978SPeter.Dunlap@Sun.COM 
8257978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = so_conn;
8267978SPeter.Dunlap@Sun.COM 	ic->ic_transport_hdrlen = 0;
8277978SPeter.Dunlap@Sun.COM 
8287978SPeter.Dunlap@Sun.COM 	/* Set the scoreboarding flag on this connection */
8297978SPeter.Dunlap@Sun.COM 	ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD;
8307978SPeter.Dunlap@Sun.COM 
8317978SPeter.Dunlap@Sun.COM 	/*
8327978SPeter.Dunlap@Sun.COM 	 * Initialize tx thread mutex and list
8337978SPeter.Dunlap@Sun.COM 	 */
8347978SPeter.Dunlap@Sun.COM 	mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL);
8357978SPeter.Dunlap@Sun.COM 	cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL);
8367978SPeter.Dunlap@Sun.COM 	list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t),
8377978SPeter.Dunlap@Sun.COM 	    offsetof(idm_pdu_t, idm_tx_link));
8387978SPeter.Dunlap@Sun.COM 
8397978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
8407978SPeter.Dunlap@Sun.COM }
8417978SPeter.Dunlap@Sun.COM 
8427978SPeter.Dunlap@Sun.COM static void
8437978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(idm_conn_t *ic)
8447978SPeter.Dunlap@Sun.COM {
8457978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
8467978SPeter.Dunlap@Sun.COM 
8477978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = NULL;
8487978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so_conn->ic_so);
8497978SPeter.Dunlap@Sun.COM 	list_destroy(&so_conn->ic_tx_list);
8507978SPeter.Dunlap@Sun.COM 	mutex_destroy(&so_conn->ic_tx_mutex);
8517978SPeter.Dunlap@Sun.COM 	cv_destroy(&so_conn->ic_tx_cv);
8527978SPeter.Dunlap@Sun.COM 
8537978SPeter.Dunlap@Sun.COM 	kmem_free(so_conn, sizeof (idm_so_conn_t));
8547978SPeter.Dunlap@Sun.COM }
8557978SPeter.Dunlap@Sun.COM 
8567978SPeter.Dunlap@Sun.COM static void
8577978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(idm_conn_t *ic)
8587978SPeter.Dunlap@Sun.COM {
8597978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8608348SEric.Yu@Sun.COM 	struct sockaddr_in6	t_addr;
8618348SEric.Yu@Sun.COM 	socklen_t	t_addrlen = 0;
8627978SPeter.Dunlap@Sun.COM 
8637978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
8648348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
8658348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
8667978SPeter.Dunlap@Sun.COM 
8677978SPeter.Dunlap@Sun.COM 	/* Set the local and remote addresses in the idm conn handle */
8688348SEric.Yu@Sun.COM 	ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr,
8698348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
8708348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_laddr, t_addrlen);
8718348SEric.Yu@Sun.COM 	ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr,
8728348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
8738348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_raddr, t_addrlen);
8747978SPeter.Dunlap@Sun.COM 
8757978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
8767978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0,
8777978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
8787978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0,
8797978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
8807978SPeter.Dunlap@Sun.COM 
8817978SPeter.Dunlap@Sun.COM 	while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running)
8827978SPeter.Dunlap@Sun.COM 		cv_wait(&ic->ic_cv, &ic->ic_mutex);
8837978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
8847978SPeter.Dunlap@Sun.COM }
8857978SPeter.Dunlap@Sun.COM 
8867978SPeter.Dunlap@Sun.COM /*
8877978SPeter.Dunlap@Sun.COM  * idm_so_conn_disconnect()
8887978SPeter.Dunlap@Sun.COM  * Shutdown the socket connection and stop the thread
8897978SPeter.Dunlap@Sun.COM  */
8907978SPeter.Dunlap@Sun.COM static void
8917978SPeter.Dunlap@Sun.COM idm_so_conn_disconnect(idm_conn_t *ic)
8927978SPeter.Dunlap@Sun.COM {
8937978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8947978SPeter.Dunlap@Sun.COM 
8957978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
8967978SPeter.Dunlap@Sun.COM 
8977978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
8987978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_FALSE;
8997978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_FALSE;
9007978SPeter.Dunlap@Sun.COM 	/* We need to wakeup the TX thread */
9017978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
9027978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
9037978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
9047978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
9057978SPeter.Dunlap@Sun.COM 
9067978SPeter.Dunlap@Sun.COM 	/* This should wakeup the RX thread if it is sleeping */
9077978SPeter.Dunlap@Sun.COM 	idm_soshutdown(so_conn->ic_so);
9087978SPeter.Dunlap@Sun.COM 
9097978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_tx_thread_did);
9107978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_rx_thread_did);
9117978SPeter.Dunlap@Sun.COM }
9127978SPeter.Dunlap@Sun.COM 
9137978SPeter.Dunlap@Sun.COM /*
9147978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_create()
9157978SPeter.Dunlap@Sun.COM  * Establish a service on an IP address and port.  idm_svc_req_t contains
9167978SPeter.Dunlap@Sun.COM  * the service parameters.
9177978SPeter.Dunlap@Sun.COM  */
9187978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
9197978SPeter.Dunlap@Sun.COM static idm_status_t
9207978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is)
9217978SPeter.Dunlap@Sun.COM {
9227978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
9237978SPeter.Dunlap@Sun.COM 
9247978SPeter.Dunlap@Sun.COM 	so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP);
9257978SPeter.Dunlap@Sun.COM 
9267978SPeter.Dunlap@Sun.COM 	/* Set the new sockets service in svc handle */
9277978SPeter.Dunlap@Sun.COM 	is->is_so_svc = (void *)so_svc;
9287978SPeter.Dunlap@Sun.COM 
9297978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
9307978SPeter.Dunlap@Sun.COM }
9317978SPeter.Dunlap@Sun.COM 
9327978SPeter.Dunlap@Sun.COM /*
9337978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_destroy()
9347978SPeter.Dunlap@Sun.COM  * Teardown sockets resources allocated in idm_so_tgt_svc_create()
9357978SPeter.Dunlap@Sun.COM  */
9367978SPeter.Dunlap@Sun.COM static void
9377978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_destroy(idm_svc_t *is)
9387978SPeter.Dunlap@Sun.COM {
9397978SPeter.Dunlap@Sun.COM 	/* the socket will have been torn down; free the service */
9407978SPeter.Dunlap@Sun.COM 	kmem_free(is->is_so_svc, sizeof (idm_so_svc_t));
9417978SPeter.Dunlap@Sun.COM }
9427978SPeter.Dunlap@Sun.COM 
9437978SPeter.Dunlap@Sun.COM /*
9447978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_online()
9457978SPeter.Dunlap@Sun.COM  * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create()
9467978SPeter.Dunlap@Sun.COM  */
9477978SPeter.Dunlap@Sun.COM 
9487978SPeter.Dunlap@Sun.COM static idm_status_t
9497978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_online(idm_svc_t *is)
9507978SPeter.Dunlap@Sun.COM {
9517978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
9527978SPeter.Dunlap@Sun.COM 	idm_svc_req_t		*sr = &is->is_svc_req;
9537978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	sin6_ip;
9547978SPeter.Dunlap@Sun.COM 	const uint32_t		on = 1;
9557978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
9567978SPeter.Dunlap@Sun.COM 
9577978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
9587978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
9597978SPeter.Dunlap@Sun.COM 
9607978SPeter.Dunlap@Sun.COM 	/*
9617978SPeter.Dunlap@Sun.COM 	 * Try creating an IPv6 socket first
9627978SPeter.Dunlap@Sun.COM 	 */
9637978SPeter.Dunlap@Sun.COM 	if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) {
9647978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
9657978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9667978SPeter.Dunlap@Sun.COM 	} else {
9677978SPeter.Dunlap@Sun.COM 		bzero(&sin6_ip, sizeof (sin6_ip));
9687978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_family = AF_INET6;
9697978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_port = htons(sr->sr_port);
9707978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_addr = in6addr_any;
9717978SPeter.Dunlap@Sun.COM 
9728348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9738348SEric.Yu@Sun.COM 		    SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
9747978SPeter.Dunlap@Sun.COM 		/*
9757978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
9767978SPeter.Dunlap@Sun.COM 		 */
9778348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9788348SEric.Yu@Sun.COM 		    SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED());
9797978SPeter.Dunlap@Sun.COM 
9808348SEric.Yu@Sun.COM 		if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip,
9818348SEric.Yu@Sun.COM 		    sizeof (sin6_ip), CRED()) != 0) {
9827978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
9837978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so_svc->is_so);
9847978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
9857978SPeter.Dunlap@Sun.COM 		}
9867978SPeter.Dunlap@Sun.COM 	}
9877978SPeter.Dunlap@Sun.COM 
9887978SPeter.Dunlap@Sun.COM 	idm_set_tgt_connect_options(so_svc->is_so);
9897978SPeter.Dunlap@Sun.COM 
9908348SEric.Yu@Sun.COM 	if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) {
9917978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
9927978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
9937978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
9947978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9957978SPeter.Dunlap@Sun.COM 	}
9967978SPeter.Dunlap@Sun.COM 
9977978SPeter.Dunlap@Sun.COM 	/* Launch a watch thread */
9987978SPeter.Dunlap@Sun.COM 	so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher,
9997978SPeter.Dunlap@Sun.COM 	    is, 0, &p0, TS_RUN, minclsyspri);
10007978SPeter.Dunlap@Sun.COM 
10017978SPeter.Dunlap@Sun.COM 	if (so_svc->is_thread == NULL) {
10027978SPeter.Dunlap@Sun.COM 		/* Failure to launch; teardown the socket */
10037978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
10047978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
10057978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
10067978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
10077978SPeter.Dunlap@Sun.COM 	}
10088348SEric.Yu@Sun.COM 	ksocket_hold(so_svc->is_so);
10097978SPeter.Dunlap@Sun.COM 	/* Wait for the port watcher thread to start */
10107978SPeter.Dunlap@Sun.COM 	while (!so_svc->is_thread_running)
10117978SPeter.Dunlap@Sun.COM 		cv_wait(&is->is_cv, &is->is_mutex);
10127978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
10137978SPeter.Dunlap@Sun.COM 
10147978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
10157978SPeter.Dunlap@Sun.COM }
10167978SPeter.Dunlap@Sun.COM 
10177978SPeter.Dunlap@Sun.COM /*
10187978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_offline
10197978SPeter.Dunlap@Sun.COM  *
10207978SPeter.Dunlap@Sun.COM  * Stop listening on the IP address and port identified by idm_svc_t.
10217978SPeter.Dunlap@Sun.COM  */
10227978SPeter.Dunlap@Sun.COM static void
10237978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_offline(idm_svc_t *is)
10247978SPeter.Dunlap@Sun.COM {
10257978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
10267978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
10277978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
10287978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
10297978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
10307978SPeter.Dunlap@Sun.COM 
10317978SPeter.Dunlap@Sun.COM 	/*
10328348SEric.Yu@Sun.COM 	 * Teardown socket
10337978SPeter.Dunlap@Sun.COM 	 */
10348348SEric.Yu@Sun.COM 	idm_sodestroy(so_svc->is_so);
10357978SPeter.Dunlap@Sun.COM 
10367978SPeter.Dunlap@Sun.COM 	/*
10377978SPeter.Dunlap@Sun.COM 	 * Now we expect the port watcher thread to terminate
10387978SPeter.Dunlap@Sun.COM 	 */
10397978SPeter.Dunlap@Sun.COM 	thread_join(so_svc->is_thread_did);
10407978SPeter.Dunlap@Sun.COM }
10417978SPeter.Dunlap@Sun.COM 
10427978SPeter.Dunlap@Sun.COM /*
10437978SPeter.Dunlap@Sun.COM  * Watch thread for target service connection establishment.
10447978SPeter.Dunlap@Sun.COM  */
10457978SPeter.Dunlap@Sun.COM void
10467978SPeter.Dunlap@Sun.COM idm_so_svc_port_watcher(void *arg)
10477978SPeter.Dunlap@Sun.COM {
10487978SPeter.Dunlap@Sun.COM 	idm_svc_t		*svc = arg;
10498348SEric.Yu@Sun.COM 	ksocket_t		new_so;
10507978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
10517978SPeter.Dunlap@Sun.COM 	idm_status_t		idmrc;
10527978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
10537978SPeter.Dunlap@Sun.COM 	int			rc;
10547978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
10558348SEric.Yu@Sun.COM 	struct sockaddr_in6 	t_addr;
10568348SEric.Yu@Sun.COM 	socklen_t		t_addrlen;
10577978SPeter.Dunlap@Sun.COM 
10588348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
10598348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
10607978SPeter.Dunlap@Sun.COM 	mutex_enter(&svc->is_mutex);
10617978SPeter.Dunlap@Sun.COM 
10627978SPeter.Dunlap@Sun.COM 	so_svc = svc->is_so_svc;
10637978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_TRUE;
10647978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_did = so_svc->is_thread->t_did;
10657978SPeter.Dunlap@Sun.COM 
10667978SPeter.Dunlap@Sun.COM 	cv_signal(&svc->is_cv);
10677978SPeter.Dunlap@Sun.COM 
10687978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc,
10697978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
10707978SPeter.Dunlap@Sun.COM 
10717978SPeter.Dunlap@Sun.COM 	while (so_svc->is_thread_running) {
10727978SPeter.Dunlap@Sun.COM 		mutex_exit(&svc->is_mutex);
10737978SPeter.Dunlap@Sun.COM 
10748348SEric.Yu@Sun.COM 		if ((rc = ksocket_accept(so_svc->is_so,
10758348SEric.Yu@Sun.COM 		    (struct sockaddr *)&t_addr, &t_addrlen,
10768348SEric.Yu@Sun.COM 		    &new_so, CRED())) != 0) {
10777978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
10787978SPeter.Dunlap@Sun.COM 			if (rc == ECONNABORTED)
10797978SPeter.Dunlap@Sun.COM 				continue;
10807978SPeter.Dunlap@Sun.COM 			/* Connection problem */
10817978SPeter.Dunlap@Sun.COM 			break;
10827978SPeter.Dunlap@Sun.COM 		}
10837978SPeter.Dunlap@Sun.COM 		/*
10847978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
10857978SPeter.Dunlap@Sun.COM 		 */
10868348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT,
10878348SEric.Yu@Sun.COM 		    (char *)&off, sizeof (off), CRED());
10887978SPeter.Dunlap@Sun.COM 
10897978SPeter.Dunlap@Sun.COM 		idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS,
10907978SPeter.Dunlap@Sun.COM 		    &ic);
10917978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
10927978SPeter.Dunlap@Sun.COM 			/* Drop connection */
10937978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
10947978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
10957978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
10967978SPeter.Dunlap@Sun.COM 			continue;
10977978SPeter.Dunlap@Sun.COM 		}
10987978SPeter.Dunlap@Sun.COM 
10997978SPeter.Dunlap@Sun.COM 		idmrc = idm_so_tgt_conn_create(ic, new_so);
11007978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
11017978SPeter.Dunlap@Sun.COM 			idm_svc_conn_destroy(ic);
11027978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
11037978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
11047978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
11057978SPeter.Dunlap@Sun.COM 			continue;
11067978SPeter.Dunlap@Sun.COM 		}
11077978SPeter.Dunlap@Sun.COM 
11087978SPeter.Dunlap@Sun.COM 		/*
11097978SPeter.Dunlap@Sun.COM 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
11107978SPeter.Dunlap@Sun.COM 		 * will notify the client (target) about the new connection.
11117978SPeter.Dunlap@Sun.COM 		 */
11127978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
11137978SPeter.Dunlap@Sun.COM 
11147978SPeter.Dunlap@Sun.COM 		mutex_enter(&svc->is_mutex);
11157978SPeter.Dunlap@Sun.COM 	}
11168348SEric.Yu@Sun.COM 	ksocket_rele(so_svc->is_so);
11177978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
11187978SPeter.Dunlap@Sun.COM 	mutex_exit(&svc->is_mutex);
11197978SPeter.Dunlap@Sun.COM 
11207978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc,
11217978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
11227978SPeter.Dunlap@Sun.COM 
11237978SPeter.Dunlap@Sun.COM 	thread_exit();
11247978SPeter.Dunlap@Sun.COM }
11257978SPeter.Dunlap@Sun.COM 
11267978SPeter.Dunlap@Sun.COM /*
11277978SPeter.Dunlap@Sun.COM  * idm_so_free_task_rsrc() stops any ongoing processing of the task and
11287978SPeter.Dunlap@Sun.COM  * frees resources associated with the task.
11297978SPeter.Dunlap@Sun.COM  *
11307978SPeter.Dunlap@Sun.COM  * It's not clear that this should return idm_status_t.  What do we do
11317978SPeter.Dunlap@Sun.COM  * if it fails?
11327978SPeter.Dunlap@Sun.COM  */
11337978SPeter.Dunlap@Sun.COM static idm_status_t
11347978SPeter.Dunlap@Sun.COM idm_so_free_task_rsrc(idm_task_t *idt)
11357978SPeter.Dunlap@Sun.COM {
11367978SPeter.Dunlap@Sun.COM 	idm_buf_t	*idb;
11377978SPeter.Dunlap@Sun.COM 
11387978SPeter.Dunlap@Sun.COM 	/*
11399162SPeter.Dunlap@Sun.COM 	 * There is nothing to cleanup on initiator connections
11409162SPeter.Dunlap@Sun.COM 	 */
11419162SPeter.Dunlap@Sun.COM 	if (IDM_CONN_ISINI(idt->idt_ic))
11429162SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
11439162SPeter.Dunlap@Sun.COM 
11449162SPeter.Dunlap@Sun.COM 	/*
11457978SPeter.Dunlap@Sun.COM 	 * If this is a target connection, call idm_buf_rx_from_ini_done for
11467978SPeter.Dunlap@Sun.COM 	 * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE.
11477978SPeter.Dunlap@Sun.COM 	 *
11487978SPeter.Dunlap@Sun.COM 	 * In addition, remove any buffers associated with this task from
11497978SPeter.Dunlap@Sun.COM 	 * the ic_tx_list.  We'll do this by walking the idt_inbufv list, but
11507978SPeter.Dunlap@Sun.COM 	 * items don't actually get removed from that list (and completion
11517978SPeter.Dunlap@Sun.COM 	 * routines called) until idm_task_cleanup.
11527978SPeter.Dunlap@Sun.COM 	 */
11537978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
11547978SPeter.Dunlap@Sun.COM 
11557978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_outbufv); idb != NULL;
11567978SPeter.Dunlap@Sun.COM 	    idb = list_next(&idt->idt_outbufv, idb)) {
11577978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport) {
11587978SPeter.Dunlap@Sun.COM 			/*
11597978SPeter.Dunlap@Sun.COM 			 * idm_buf_rx_from_ini_done releases idt->idt_mutex
11607978SPeter.Dunlap@Sun.COM 			 */
11617978SPeter.Dunlap@Sun.COM 			idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED);
11627978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
11637978SPeter.Dunlap@Sun.COM 		}
11647978SPeter.Dunlap@Sun.COM 	}
11657978SPeter.Dunlap@Sun.COM 
11667978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_inbufv); idb != NULL;
11677978SPeter.Dunlap@Sun.COM 	    idb = list_next(&idt->idt_inbufv, idb)) {
11687978SPeter.Dunlap@Sun.COM 		/*
11697978SPeter.Dunlap@Sun.COM 		 * We want to remove these items from the tx_list as well,
11707978SPeter.Dunlap@Sun.COM 		 * but knowing it's in the idt_inbufv list is not a guarantee
11717978SPeter.Dunlap@Sun.COM 		 * that it's in the tx_list.  If it's on the tx list then
11727978SPeter.Dunlap@Sun.COM 		 * let idm_sotx_thread() clean it up.
11737978SPeter.Dunlap@Sun.COM 		 */
11747978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport && !idb->idb_tx_thread) {
11757978SPeter.Dunlap@Sun.COM 			/*
11767978SPeter.Dunlap@Sun.COM 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
11777978SPeter.Dunlap@Sun.COM 			 */
11787978SPeter.Dunlap@Sun.COM 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
11797978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
11807978SPeter.Dunlap@Sun.COM 		}
11817978SPeter.Dunlap@Sun.COM 	}
11827978SPeter.Dunlap@Sun.COM 
11837978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
11847978SPeter.Dunlap@Sun.COM 
11857978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
11867978SPeter.Dunlap@Sun.COM }
11877978SPeter.Dunlap@Sun.COM 
11887978SPeter.Dunlap@Sun.COM /*
11897978SPeter.Dunlap@Sun.COM  * idm_so_negotiate_key_values() validates the key values for this connection
11907978SPeter.Dunlap@Sun.COM  */
11917978SPeter.Dunlap@Sun.COM /* ARGSUSED */
11927978SPeter.Dunlap@Sun.COM static kv_status_t
11937978SPeter.Dunlap@Sun.COM idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl,
11947978SPeter.Dunlap@Sun.COM     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
11957978SPeter.Dunlap@Sun.COM {
11967978SPeter.Dunlap@Sun.COM 	/* All parameters are negotiated at the iscsit level */
11977978SPeter.Dunlap@Sun.COM 	return (KV_HANDLED);
11987978SPeter.Dunlap@Sun.COM }
11997978SPeter.Dunlap@Sun.COM 
12007978SPeter.Dunlap@Sun.COM /*
12017978SPeter.Dunlap@Sun.COM  * idm_so_notice_key_values() activates the negotiated key values for
12027978SPeter.Dunlap@Sun.COM  * this connection.
12037978SPeter.Dunlap@Sun.COM  */
12049162SPeter.Dunlap@Sun.COM static void
12057978SPeter.Dunlap@Sun.COM idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl)
12067978SPeter.Dunlap@Sun.COM {
12077978SPeter.Dunlap@Sun.COM 	char			*nvp_name;
12087978SPeter.Dunlap@Sun.COM 	nvpair_t		*nvp;
12097978SPeter.Dunlap@Sun.COM 	nvpair_t		*next_nvp;
12107978SPeter.Dunlap@Sun.COM 	int			nvrc;
12117978SPeter.Dunlap@Sun.COM 	idm_status_t		idm_status;
12127978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t	*ikvx;
12137978SPeter.Dunlap@Sun.COM 
12147978SPeter.Dunlap@Sun.COM 	for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL);
12157978SPeter.Dunlap@Sun.COM 	    nvp != NULL; nvp = next_nvp) {
12167978SPeter.Dunlap@Sun.COM 		next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp);
12177978SPeter.Dunlap@Sun.COM 		nvp_name = nvpair_name(nvp);
12187978SPeter.Dunlap@Sun.COM 
12197978SPeter.Dunlap@Sun.COM 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
12207978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12217978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12227978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12237978SPeter.Dunlap@Sun.COM 			idm_status = idm_so_handle_digest(it, nvp, ikvx);
12247978SPeter.Dunlap@Sun.COM 			ASSERT(idm_status == 0);
12257978SPeter.Dunlap@Sun.COM 
12267978SPeter.Dunlap@Sun.COM 			/* Remove processed item from negotiated_nvl list */
12277978SPeter.Dunlap@Sun.COM 			nvrc = nvlist_remove_all(
12287978SPeter.Dunlap@Sun.COM 			    negotiated_nvl, ikvx->ik_key_name);
12297978SPeter.Dunlap@Sun.COM 			ASSERT(nvrc == 0);
12307978SPeter.Dunlap@Sun.COM 			break;
12317978SPeter.Dunlap@Sun.COM 		default:
12327978SPeter.Dunlap@Sun.COM 			break;
12337978SPeter.Dunlap@Sun.COM 		}
12347978SPeter.Dunlap@Sun.COM 	}
12357978SPeter.Dunlap@Sun.COM }
12367978SPeter.Dunlap@Sun.COM 
12377978SPeter.Dunlap@Sun.COM 
12387978SPeter.Dunlap@Sun.COM static idm_status_t
12397978SPeter.Dunlap@Sun.COM idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice,
12407978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx)
12417978SPeter.Dunlap@Sun.COM {
12427978SPeter.Dunlap@Sun.COM 	int			nvrc;
12437978SPeter.Dunlap@Sun.COM 	char			*digest_choice_string;
12447978SPeter.Dunlap@Sun.COM 
12457978SPeter.Dunlap@Sun.COM 	nvrc = nvpair_value_string(digest_choice,
12467978SPeter.Dunlap@Sun.COM 	    &digest_choice_string);
12477978SPeter.Dunlap@Sun.COM 	ASSERT(nvrc == 0);
12487978SPeter.Dunlap@Sun.COM 	if (strcasecmp(digest_choice_string, "crc32c") == 0) {
12497978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12507978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12517978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST;
12527978SPeter.Dunlap@Sun.COM 			break;
12537978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12547978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_DATA_DIGEST;
12557978SPeter.Dunlap@Sun.COM 			break;
12567978SPeter.Dunlap@Sun.COM 		default:
12577978SPeter.Dunlap@Sun.COM 			ASSERT(0);
12587978SPeter.Dunlap@Sun.COM 			break;
12597978SPeter.Dunlap@Sun.COM 		}
12607978SPeter.Dunlap@Sun.COM 	} else if (strcasecmp(digest_choice_string, "none") == 0) {
12617978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12627978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12637978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST;
12647978SPeter.Dunlap@Sun.COM 			break;
12657978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12667978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST;
12677978SPeter.Dunlap@Sun.COM 			break;
12687978SPeter.Dunlap@Sun.COM 		default:
12697978SPeter.Dunlap@Sun.COM 			ASSERT(0);
12707978SPeter.Dunlap@Sun.COM 			break;
12717978SPeter.Dunlap@Sun.COM 		}
12727978SPeter.Dunlap@Sun.COM 	} else {
12737978SPeter.Dunlap@Sun.COM 		ASSERT(0);
12747978SPeter.Dunlap@Sun.COM 	}
12757978SPeter.Dunlap@Sun.COM 
12767978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12777978SPeter.Dunlap@Sun.COM }
12787978SPeter.Dunlap@Sun.COM 
12797978SPeter.Dunlap@Sun.COM 
12807978SPeter.Dunlap@Sun.COM /*
12817978SPeter.Dunlap@Sun.COM  * idm_so_conn_is_capable() verifies that the passed connection is provided
12827978SPeter.Dunlap@Sun.COM  * for by the sockets interface.
12837978SPeter.Dunlap@Sun.COM  */
12847978SPeter.Dunlap@Sun.COM /* ARGSUSED */
12857978SPeter.Dunlap@Sun.COM static boolean_t
12867978SPeter.Dunlap@Sun.COM idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps)
12877978SPeter.Dunlap@Sun.COM {
12887978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
12897978SPeter.Dunlap@Sun.COM }
12907978SPeter.Dunlap@Sun.COM 
12917978SPeter.Dunlap@Sun.COM /*
12927978SPeter.Dunlap@Sun.COM  * idm_so_rx_datain() validates the Data Sequence number of the PDU. The
12937978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() function invoked earlier actually reads the data
12947978SPeter.Dunlap@Sun.COM  * off the socket into the appropriate buffers.
12957978SPeter.Dunlap@Sun.COM  */
12967978SPeter.Dunlap@Sun.COM static void
12977978SPeter.Dunlap@Sun.COM idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu)
12987978SPeter.Dunlap@Sun.COM {
12997978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
13007978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
13017978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
13027978SPeter.Dunlap@Sun.COM 	uint32_t		datasn;
13037978SPeter.Dunlap@Sun.COM 	size_t			offset;
13047978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t		*ihp = (iscsi_hdr_t *)pdu->isp_hdr;
13057978SPeter.Dunlap@Sun.COM 	iscsi_data_rsp_hdr_t    *idrhp = (iscsi_data_rsp_hdr_t *)ihp;
13067978SPeter.Dunlap@Sun.COM 
13077978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
13087978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
13097978SPeter.Dunlap@Sun.COM 
13107978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
13117978SPeter.Dunlap@Sun.COM 	datasn	= ntohl(bhs->datasn);
13127978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
13137978SPeter.Dunlap@Sun.COM 
13147978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP);
13157978SPeter.Dunlap@Sun.COM 
13167978SPeter.Dunlap@Sun.COM 	/*
13177978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
13187978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
13197978SPeter.Dunlap@Sun.COM 	 */
13207978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
13217978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
13227978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task");
13237978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13247978SPeter.Dunlap@Sun.COM 		return;
13257978SPeter.Dunlap@Sun.COM 	}
13267978SPeter.Dunlap@Sun.COM 
13277978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
13287978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
13297978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
13307978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_datain: failed to find buffer");
13317978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
13327978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13337978SPeter.Dunlap@Sun.COM 		return;
13347978SPeter.Dunlap@Sun.COM 	}
13357978SPeter.Dunlap@Sun.COM 
13367978SPeter.Dunlap@Sun.COM 	/*
13377978SPeter.Dunlap@Sun.COM 	 * DataSN values should be sequential and should not have any gaps or
13387978SPeter.Dunlap@Sun.COM 	 * repetitions. Check the DataSN with the one stored in the task.
13397978SPeter.Dunlap@Sun.COM 	 */
13407978SPeter.Dunlap@Sun.COM 	if (datasn == idt->idt_exp_datasn) {
13417978SPeter.Dunlap@Sun.COM 		idt->idt_exp_datasn++; /* keep track of DataSN received */
13427978SPeter.Dunlap@Sun.COM 	} else {
13437978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order");
13447978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
13457978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13467978SPeter.Dunlap@Sun.COM 		return;
13477978SPeter.Dunlap@Sun.COM 	}
13487978SPeter.Dunlap@Sun.COM 
13497978SPeter.Dunlap@Sun.COM 	/*
13507978SPeter.Dunlap@Sun.COM 	 * PDUs in a sequence should be in continuously increasing
13517978SPeter.Dunlap@Sun.COM 	 * address offset
13527978SPeter.Dunlap@Sun.COM 	 */
13537978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
13547978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset");
13559162SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
13567978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13577978SPeter.Dunlap@Sun.COM 		return;
13587978SPeter.Dunlap@Sun.COM 	}
13597978SPeter.Dunlap@Sun.COM 	/* Expected next relative buffer offset */
13607978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += n2h24(bhs->dlength);
13619162SPeter.Dunlap@Sun.COM 	idt->idt_rx_bytes += n2h24(bhs->dlength);
13629162SPeter.Dunlap@Sun.COM 
13639162SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
13647978SPeter.Dunlap@Sun.COM 
13657978SPeter.Dunlap@Sun.COM 	/*
13667978SPeter.Dunlap@Sun.COM 	 * For now call scsi_rsp which will process the data rsp
13677978SPeter.Dunlap@Sun.COM 	 * Revisit, need to provide an explicit client entry point for
13687978SPeter.Dunlap@Sun.COM 	 * phase collapse completions.
13697978SPeter.Dunlap@Sun.COM 	 */
13707978SPeter.Dunlap@Sun.COM 	if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) &&
13717978SPeter.Dunlap@Sun.COM 	    (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) {
13727978SPeter.Dunlap@Sun.COM 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
13737978SPeter.Dunlap@Sun.COM 	}
13747978SPeter.Dunlap@Sun.COM 
13757978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
13767978SPeter.Dunlap@Sun.COM }
13777978SPeter.Dunlap@Sun.COM 
13787978SPeter.Dunlap@Sun.COM /*
13797978SPeter.Dunlap@Sun.COM  * The idm_so_rx_dataout() function is used by the iSCSI target to read
13807978SPeter.Dunlap@Sun.COM  * data from the Data-Out PDU sent by the iSCSI initiator.
13817978SPeter.Dunlap@Sun.COM  *
13827978SPeter.Dunlap@Sun.COM  * This function gets the Initiator Task Tag from the PDU BHS and looks up the
13837978SPeter.Dunlap@Sun.COM  * task to get the buffers associated with the PDU. A PDU might span buffers.
13847978SPeter.Dunlap@Sun.COM  * The data is then read into the respective buffer.
13857978SPeter.Dunlap@Sun.COM  */
13867978SPeter.Dunlap@Sun.COM static void
13877978SPeter.Dunlap@Sun.COM idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu)
13887978SPeter.Dunlap@Sun.COM {
13897978SPeter.Dunlap@Sun.COM 
13907978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
13917978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
13927978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
13937978SPeter.Dunlap@Sun.COM 	size_t			offset;
13947978SPeter.Dunlap@Sun.COM 
13957978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
13967978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
13977978SPeter.Dunlap@Sun.COM 
13987978SPeter.Dunlap@Sun.COM 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
13997978SPeter.Dunlap@Sun.COM 	offset = ntohl(bhs->offset);
14007978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA);
14017978SPeter.Dunlap@Sun.COM 
14027978SPeter.Dunlap@Sun.COM 	/*
14037978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
14047978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
14057978SPeter.Dunlap@Sun.COM 	 */
14067978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
14077978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
14087978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
14097978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find task");
14107978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14117978SPeter.Dunlap@Sun.COM 		return;
14127978SPeter.Dunlap@Sun.COM 	}
14137978SPeter.Dunlap@Sun.COM 
14147978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
14157978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
14167978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
14177978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find buffer");
14187978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14197978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14207978SPeter.Dunlap@Sun.COM 		return;
14217978SPeter.Dunlap@Sun.COM 	}
14227978SPeter.Dunlap@Sun.COM 
14237978SPeter.Dunlap@Sun.COM 	/* Keep track of data transferred - check data offsets */
14247978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
14257978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: "
14267978SPeter.Dunlap@Sun.COM 		    "%ld, %d", offset, idb->idb_exp_offset);
14277978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14287978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14297978SPeter.Dunlap@Sun.COM 		return;
14307978SPeter.Dunlap@Sun.COM 	}
14317978SPeter.Dunlap@Sun.COM 	/* Expected next relative offset */
14327978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += ntoh24(bhs->dlength);
14339162SPeter.Dunlap@Sun.COM 	idt->idt_rx_bytes += n2h24(bhs->dlength);
14347978SPeter.Dunlap@Sun.COM 
14357978SPeter.Dunlap@Sun.COM 	/*
14367978SPeter.Dunlap@Sun.COM 	 * Call the buffer callback when the transfer is complete
14377978SPeter.Dunlap@Sun.COM 	 *
14387978SPeter.Dunlap@Sun.COM 	 * The connection state machine should only abort tasks after
14397978SPeter.Dunlap@Sun.COM 	 * shutting down the connection so we are assured that there
14407978SPeter.Dunlap@Sun.COM 	 * won't be a simultaneous attempt to abort this task at the
14417978SPeter.Dunlap@Sun.COM 	 * same time as we are processing this PDU (due to a connection
14427978SPeter.Dunlap@Sun.COM 	 * state change).
14437978SPeter.Dunlap@Sun.COM 	 */
14447978SPeter.Dunlap@Sun.COM 	if (bhs->flags & ISCSI_FLAG_FINAL) {
14457978SPeter.Dunlap@Sun.COM 		/*
14467978SPeter.Dunlap@Sun.COM 		 * We only want to call idm_buf_rx_from_ini_done once
14477978SPeter.Dunlap@Sun.COM 		 * per transfer.  It's possible that this task has
14487978SPeter.Dunlap@Sun.COM 		 * already been aborted in which case
14497978SPeter.Dunlap@Sun.COM 		 * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done
14507978SPeter.Dunlap@Sun.COM 		 * for each buffer with idb_in_transport==B_TRUE.  To
14517978SPeter.Dunlap@Sun.COM 		 * close this window and ensure that this doesn't happen,
14527978SPeter.Dunlap@Sun.COM 		 * we'll clear idb->idb_in_transport now while holding
14537978SPeter.Dunlap@Sun.COM 		 * the task mutex.   This is only really an issue for
14547978SPeter.Dunlap@Sun.COM 		 * SCSI task abort -- if tasks were being aborted because
14557978SPeter.Dunlap@Sun.COM 		 * of a connection state change the state machine would
14567978SPeter.Dunlap@Sun.COM 		 * have already stopped the receive thread.
14577978SPeter.Dunlap@Sun.COM 		 */
14587978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
14597978SPeter.Dunlap@Sun.COM 
14607978SPeter.Dunlap@Sun.COM 		/*
14617978SPeter.Dunlap@Sun.COM 		 * Release the task hold here (obtained in idm_task_find)
14627978SPeter.Dunlap@Sun.COM 		 * because the task may complete synchronously during
14637978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done.  Since we still have an active
14647978SPeter.Dunlap@Sun.COM 		 * buffer we know there is at least one additional hold on idt.
14657978SPeter.Dunlap@Sun.COM 		 */
14667978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14677978SPeter.Dunlap@Sun.COM 
14687978SPeter.Dunlap@Sun.COM 		/*
14697978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done releases idt->idt_mutex
14707978SPeter.Dunlap@Sun.COM 		 */
14717978SPeter.Dunlap@Sun.COM 		idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS);
14727978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
14737978SPeter.Dunlap@Sun.COM 		return;
14747978SPeter.Dunlap@Sun.COM 	}
14757978SPeter.Dunlap@Sun.COM 
14767978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
14777978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
14787978SPeter.Dunlap@Sun.COM }
14797978SPeter.Dunlap@Sun.COM 
14807978SPeter.Dunlap@Sun.COM /*
14817978SPeter.Dunlap@Sun.COM  * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle
14827978SPeter.Dunlap@Sun.COM  * the R2T PDU sent by the iSCSI target indicating that it is ready to
14837978SPeter.Dunlap@Sun.COM  * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS
14847978SPeter.Dunlap@Sun.COM  * and looks up the task in the task tree using the itt to get the output
14857978SPeter.Dunlap@Sun.COM  * buffers associated the task. The R2T PDU contains the offset of the
14867978SPeter.Dunlap@Sun.COM  * requested data and the data length. This function then constructs a
14877978SPeter.Dunlap@Sun.COM  * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out
14887978SPeter.Dunlap@Sun.COM  * PDU is associated with the R2T by the Target Transfer Tag  (ttt).
14897978SPeter.Dunlap@Sun.COM  */
14909162SPeter.Dunlap@Sun.COM 
14917978SPeter.Dunlap@Sun.COM static void
14927978SPeter.Dunlap@Sun.COM idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu)
14937978SPeter.Dunlap@Sun.COM {
14947978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
14957978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
14967978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt_hdr;
14977978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset;
14989162SPeter.Dunlap@Sun.COM 	uint32_t		data_length;
14997978SPeter.Dunlap@Sun.COM 
15007978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
15017978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
15027978SPeter.Dunlap@Sun.COM 
15037978SPeter.Dunlap@Sun.COM 	rtt_hdr	= (iscsi_rtt_hdr_t *)pdu->isp_hdr;
15047978SPeter.Dunlap@Sun.COM 	data_offset = ntohl(rtt_hdr->data_offset);
15059162SPeter.Dunlap@Sun.COM 	data_length = ntohl(rtt_hdr->data_length);
15067978SPeter.Dunlap@Sun.COM 	idt	= idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt);
15077978SPeter.Dunlap@Sun.COM 
15087978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
15097978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task");
15107978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15117978SPeter.Dunlap@Sun.COM 		return;
15127978SPeter.Dunlap@Sun.COM 	}
15137978SPeter.Dunlap@Sun.COM 
15147978SPeter.Dunlap@Sun.COM 	/* Find the buffer bound to the task by the iSCSI initiator */
15157978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
15167978SPeter.Dunlap@Sun.COM 	idb = idm_buf_find(&idt->idt_outbufv, data_offset);
15177978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
15187978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15197978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
15207978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer");
15217978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15227978SPeter.Dunlap@Sun.COM 		return;
15237978SPeter.Dunlap@Sun.COM 	}
15247978SPeter.Dunlap@Sun.COM 
15259162SPeter.Dunlap@Sun.COM 	/* return buffer contains this data */
15269162SPeter.Dunlap@Sun.COM 	if (data_offset + data_length > idb->idb_buflen) {
15279162SPeter.Dunlap@Sun.COM 		/* Overflow */
15289162SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15299162SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
15309162SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside "
15319162SPeter.Dunlap@Sun.COM 		    "buffer");
15329162SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15339162SPeter.Dunlap@Sun.COM 		return;
15349162SPeter.Dunlap@Sun.COM 	}
15359162SPeter.Dunlap@Sun.COM 
15369162SPeter.Dunlap@Sun.COM 	idt->idt_r2t_ttt = rtt_hdr->ttt;
15379162SPeter.Dunlap@Sun.COM 	idt->idt_exp_datasn = 0;
15389162SPeter.Dunlap@Sun.COM 
15399162SPeter.Dunlap@Sun.COM 	idm_so_send_rtt_data(ic, idt, idb, data_offset,
15409162SPeter.Dunlap@Sun.COM 	    ntohl(rtt_hdr->data_length));
15417978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
15427978SPeter.Dunlap@Sun.COM 
15437978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
15447978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
15457978SPeter.Dunlap@Sun.COM 
15467978SPeter.Dunlap@Sun.COM }
15477978SPeter.Dunlap@Sun.COM 
15487978SPeter.Dunlap@Sun.COM idm_status_t
15497978SPeter.Dunlap@Sun.COM idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu)
15507978SPeter.Dunlap@Sun.COM {
15517978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
15527978SPeter.Dunlap@Sun.COM 	int		pad_len;
15537978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc;
15547978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
15557978SPeter.Dunlap@Sun.COM 	int		total_len;
15567978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
15577978SPeter.Dunlap@Sun.COM 
15587978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
15597978SPeter.Dunlap@Sun.COM 
15607978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
15617978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
15627978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
15637978SPeter.Dunlap@Sun.COM 
15647978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */
15657978SPeter.Dunlap@Sun.COM 
15667978SPeter.Dunlap@Sun.COM 	total_len = pdu->isp_datalen;
15677978SPeter.Dunlap@Sun.COM 
15687978SPeter.Dunlap@Sun.COM 	if (pad_len) {
15697978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base	= (char *)&pad;
15707978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len	= pad_len;
15717978SPeter.Dunlap@Sun.COM 		total_len		+= pad_len;
15727978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
15737978SPeter.Dunlap@Sun.COM 	}
15747978SPeter.Dunlap@Sun.COM 
15757978SPeter.Dunlap@Sun.COM 	/* setup data digest */
15767978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
15777978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base =
15787978SPeter.Dunlap@Sun.COM 		    (char *)&data_digest_crc;
15797978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len =
15807978SPeter.Dunlap@Sun.COM 		    sizeof (data_digest_crc);
15817978SPeter.Dunlap@Sun.COM 		total_len		+= sizeof (data_digest_crc);
15827978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
15837978SPeter.Dunlap@Sun.COM 	}
15847978SPeter.Dunlap@Sun.COM 
15859162SPeter.Dunlap@Sun.COM 	pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base;
15869162SPeter.Dunlap@Sun.COM 
15877978SPeter.Dunlap@Sun.COM 	if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0],
15887978SPeter.Dunlap@Sun.COM 	    pdu->isp_iovlen, total_len) != 0) {
15897978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_IO);
15907978SPeter.Dunlap@Sun.COM 	}
15917978SPeter.Dunlap@Sun.COM 
15927978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
15937978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_data,
15947978SPeter.Dunlap@Sun.COM 		    pdu->isp_datalen);
15957978SPeter.Dunlap@Sun.COM 		if (pad_len) {
15967978SPeter.Dunlap@Sun.COM 			crc_calculated = idm_crc32c_continued((char *)&pad,
15977978SPeter.Dunlap@Sun.COM 			    pad_len, crc_calculated);
15987978SPeter.Dunlap@Sun.COM 		}
15997978SPeter.Dunlap@Sun.COM 		if (crc_calculated != data_digest_crc) {
16007978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
16017978SPeter.Dunlap@Sun.COM 			    "idm_sorecvdata: "
16027978SPeter.Dunlap@Sun.COM 			    "CRC error: actual 0x%x, calc 0x%x",
16037978SPeter.Dunlap@Sun.COM 			    data_digest_crc, crc_calculated);
16047978SPeter.Dunlap@Sun.COM 
16057978SPeter.Dunlap@Sun.COM 			/* Invalid Data Digest */
16067978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_DATA_DIGEST);
16077978SPeter.Dunlap@Sun.COM 		}
16087978SPeter.Dunlap@Sun.COM 	}
16097978SPeter.Dunlap@Sun.COM 
16107978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
16117978SPeter.Dunlap@Sun.COM }
16127978SPeter.Dunlap@Sun.COM 
16137978SPeter.Dunlap@Sun.COM /*
16147978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() is used to receive scsi data from the socket. The
16157978SPeter.Dunlap@Sun.COM  * Data-type PDU header must be read into the idm_pdu_t structure prior to
16167978SPeter.Dunlap@Sun.COM  * calling this function.
16177978SPeter.Dunlap@Sun.COM  */
16187978SPeter.Dunlap@Sun.COM idm_status_t
16197978SPeter.Dunlap@Sun.COM idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu)
16207978SPeter.Dunlap@Sun.COM {
16217978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
16227978SPeter.Dunlap@Sun.COM 	idm_task_t		*task;
16237978SPeter.Dunlap@Sun.COM 	uint32_t		offset;
16247978SPeter.Dunlap@Sun.COM 	uint8_t			opcode;
16257978SPeter.Dunlap@Sun.COM 	uint32_t		dlength;
16267978SPeter.Dunlap@Sun.COM 	list_t			*buflst;
16277978SPeter.Dunlap@Sun.COM 	uint32_t		xfer_bytes;
16287978SPeter.Dunlap@Sun.COM 	idm_status_t		status;
16297978SPeter.Dunlap@Sun.COM 
16307978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
16317978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
16327978SPeter.Dunlap@Sun.COM 
16337978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
16347978SPeter.Dunlap@Sun.COM 
16357978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
16367978SPeter.Dunlap@Sun.COM 	opcode	= bhs->opcode;
16377978SPeter.Dunlap@Sun.COM 	dlength = n2h24(bhs->dlength);
16387978SPeter.Dunlap@Sun.COM 
16397978SPeter.Dunlap@Sun.COM 	ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
16407978SPeter.Dunlap@Sun.COM 	    (opcode == ISCSI_OP_SCSI_DATA));
16417978SPeter.Dunlap@Sun.COM 
16427978SPeter.Dunlap@Sun.COM 	/*
16437978SPeter.Dunlap@Sun.COM 	 * Successful lookup implicitly gets a "hold" on the task.  This
16447978SPeter.Dunlap@Sun.COM 	 * hold must be released before leaving this function.  At one
16457978SPeter.Dunlap@Sun.COM 	 * point we were caching this task context and retaining the hold
16467978SPeter.Dunlap@Sun.COM 	 * but it turned out to be very difficult to release the hold properly.
16477978SPeter.Dunlap@Sun.COM 	 * The task can be aborted and the connection shutdown between this
16487978SPeter.Dunlap@Sun.COM 	 * call and the subsequent expected call to idm_so_rx_datain/
16497978SPeter.Dunlap@Sun.COM 	 * idm_so_rx_dataout (in which case those functions are not called).
16507978SPeter.Dunlap@Sun.COM 	 * Releasing the hold in the PDU callback doesn't work well either
16517978SPeter.Dunlap@Sun.COM 	 * because the whole task may be completed by then at which point
16527978SPeter.Dunlap@Sun.COM 	 * it is too late to release the hold -- for better or worse this
16537978SPeter.Dunlap@Sun.COM 	 * code doesn't wait on the refcnts during normal operation.
16547978SPeter.Dunlap@Sun.COM 	 * idm_task_find() is very fast and it is not a huge burden if we
16557978SPeter.Dunlap@Sun.COM 	 * have to do it twice.
16567978SPeter.Dunlap@Sun.COM 	 */
16577978SPeter.Dunlap@Sun.COM 	task = idm_task_find(ic, bhs->itt, bhs->ttt);
16587978SPeter.Dunlap@Sun.COM 	if (task == NULL) {
16597978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
16607978SPeter.Dunlap@Sun.COM 		    "idm_sorecv_scsidata: could not find task");
16617978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16627978SPeter.Dunlap@Sun.COM 	}
16637978SPeter.Dunlap@Sun.COM 
16647978SPeter.Dunlap@Sun.COM 	mutex_enter(&task->idt_mutex);
16657978SPeter.Dunlap@Sun.COM 	buflst	= (opcode == ISCSI_OP_SCSI_DATA_RSP) ?
16667978SPeter.Dunlap@Sun.COM 	    &task->idt_inbufv : &task->idt_outbufv;
16677978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = idm_buf_find(buflst, offset);
16687978SPeter.Dunlap@Sun.COM 	mutex_exit(&task->idt_mutex);
16697978SPeter.Dunlap@Sun.COM 
16707978SPeter.Dunlap@Sun.COM 	if (pdu->isp_sorx_buf == NULL) {
16717978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
16727978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find "
16737978SPeter.Dunlap@Sun.COM 		    "buffer for offset %x opcode=%x",
16747978SPeter.Dunlap@Sun.COM 		    offset, opcode);
16757978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16767978SPeter.Dunlap@Sun.COM 	}
16777978SPeter.Dunlap@Sun.COM 
16787978SPeter.Dunlap@Sun.COM 	xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength);
16797978SPeter.Dunlap@Sun.COM 	ASSERT(xfer_bytes != 0);
16807978SPeter.Dunlap@Sun.COM 	if (xfer_bytes != dlength) {
16817978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
16827978SPeter.Dunlap@Sun.COM 		/*
16837978SPeter.Dunlap@Sun.COM 		 * Buffer overflow, connection error.  The PDU data is still
16847978SPeter.Dunlap@Sun.COM 		 * sitting in the socket so we can't use the connection
16857978SPeter.Dunlap@Sun.COM 		 * again until that data is drained.
16867978SPeter.Dunlap@Sun.COM 		 */
16877978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16887978SPeter.Dunlap@Sun.COM 	}
16897978SPeter.Dunlap@Sun.COM 
16907978SPeter.Dunlap@Sun.COM 	status = idm_sorecvdata(ic, pdu);
16917978SPeter.Dunlap@Sun.COM 
16927978SPeter.Dunlap@Sun.COM 	idm_task_rele(task);
16937978SPeter.Dunlap@Sun.COM 
16947978SPeter.Dunlap@Sun.COM 	return (status);
16957978SPeter.Dunlap@Sun.COM }
16967978SPeter.Dunlap@Sun.COM 
16977978SPeter.Dunlap@Sun.COM static uint32_t
16987978SPeter.Dunlap@Sun.COM idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength)
16997978SPeter.Dunlap@Sun.COM {
17007978SPeter.Dunlap@Sun.COM 	uint32_t	buf_ro = ro - idb->idb_bufoffset;
17017978SPeter.Dunlap@Sun.COM 	uint32_t	xfer_len = min(dlength, idb->idb_buflen - buf_ro);
17027978SPeter.Dunlap@Sun.COM 
17037978SPeter.Dunlap@Sun.COM 	ASSERT(ro >= idb->idb_bufoffset);
17047978SPeter.Dunlap@Sun.COM 
17057978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_base	=
17067978SPeter.Dunlap@Sun.COM 	    (caddr_t)idb->idb_buf + buf_ro;
17077978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_len	= xfer_len;
17087978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen++;
17097978SPeter.Dunlap@Sun.COM 
17107978SPeter.Dunlap@Sun.COM 	return (xfer_len);
17117978SPeter.Dunlap@Sun.COM }
17127978SPeter.Dunlap@Sun.COM 
17137978SPeter.Dunlap@Sun.COM int
17147978SPeter.Dunlap@Sun.COM idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu)
17157978SPeter.Dunlap@Sun.COM {
17167978SPeter.Dunlap@Sun.COM 	pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP);
17177978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_data != NULL);
17187978SPeter.Dunlap@Sun.COM 
17197978SPeter.Dunlap@Sun.COM 	pdu->isp_databuflen = pdu->isp_datalen;
17207978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data;
17217978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_len = pdu->isp_datalen;
17227978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 1;
17237978SPeter.Dunlap@Sun.COM 	/*
17247978SPeter.Dunlap@Sun.COM 	 * Since we are associating a new data buffer with this received
17257978SPeter.Dunlap@Sun.COM 	 * PDU we need to set a specific callback to free the data
17267978SPeter.Dunlap@Sun.COM 	 * after the PDU is processed.
17277978SPeter.Dunlap@Sun.COM 	 */
17287978SPeter.Dunlap@Sun.COM 	pdu->isp_flags |= IDM_PDU_ADDL_DATA;
17297978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_addl_pdu_cb;
17307978SPeter.Dunlap@Sun.COM 
17317978SPeter.Dunlap@Sun.COM 	return (idm_sorecvdata(ic, pdu));
17327978SPeter.Dunlap@Sun.COM }
17337978SPeter.Dunlap@Sun.COM 
17347978SPeter.Dunlap@Sun.COM void
17357978SPeter.Dunlap@Sun.COM idm_sorx_thread(void *arg)
17367978SPeter.Dunlap@Sun.COM {
17377978SPeter.Dunlap@Sun.COM 	boolean_t	conn_failure = B_FALSE;
17387978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = (idm_conn_t *)arg;
17397978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
17407978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu;
17417978SPeter.Dunlap@Sun.COM 	idm_status_t	rc;
17427978SPeter.Dunlap@Sun.COM 
17437978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
17447978SPeter.Dunlap@Sun.COM 
17457978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
17467978SPeter.Dunlap@Sun.COM 
17477978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
17487978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_TRUE;
17497978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did;
17507978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
17517978SPeter.Dunlap@Sun.COM 
17527978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_rx_thread_running) {
17537978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_mutex);
17547978SPeter.Dunlap@Sun.COM 
17557978SPeter.Dunlap@Sun.COM 		/*
17567978SPeter.Dunlap@Sun.COM 		 * Get PDU with default header size (large enough for
17577978SPeter.Dunlap@Sun.COM 		 * BHS plus any anticipated AHS).  PDU from
17587978SPeter.Dunlap@Sun.COM 		 * the cache will have all values set correctly
17597978SPeter.Dunlap@Sun.COM 		 * for sockets RX including callback.
17607978SPeter.Dunlap@Sun.COM 		 */
17617978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP);
17627978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
17637978SPeter.Dunlap@Sun.COM 		pdu->isp_flags = 0;
17647978SPeter.Dunlap@Sun.COM 		pdu->isp_transport_hdrlen = 0;
17657978SPeter.Dunlap@Sun.COM 
17667978SPeter.Dunlap@Sun.COM 		if ((rc = idm_sorecvhdr(ic, pdu)) != 0) {
17677978SPeter.Dunlap@Sun.COM 			/*
17687978SPeter.Dunlap@Sun.COM 			 * Call idm_pdu_complete so that we call the callback
17697978SPeter.Dunlap@Sun.COM 			 * and ensure any memory allocated in idm_sorecvhdr
17707978SPeter.Dunlap@Sun.COM 			 * gets freed up.
17717978SPeter.Dunlap@Sun.COM 			 */
17727978SPeter.Dunlap@Sun.COM 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
17737978SPeter.Dunlap@Sun.COM 
17747978SPeter.Dunlap@Sun.COM 			/*
17757978SPeter.Dunlap@Sun.COM 			 * If ic_rx_thread_running is still set then
17767978SPeter.Dunlap@Sun.COM 			 * this is some kind of connection problem
17777978SPeter.Dunlap@Sun.COM 			 * on the socket.  In this case we want to
17787978SPeter.Dunlap@Sun.COM 			 * generate an event.  Otherwise some other
17797978SPeter.Dunlap@Sun.COM 			 * thread closed the socket due to another
17807978SPeter.Dunlap@Sun.COM 			 * issue in which case we don't need to
17817978SPeter.Dunlap@Sun.COM 			 * generate an event.
17827978SPeter.Dunlap@Sun.COM 			 */
17837978SPeter.Dunlap@Sun.COM 			mutex_enter(&ic->ic_mutex);
17847978SPeter.Dunlap@Sun.COM 			if (so_conn->ic_rx_thread_running) {
17857978SPeter.Dunlap@Sun.COM 				conn_failure = B_TRUE;
17867978SPeter.Dunlap@Sun.COM 				so_conn->ic_rx_thread_running = B_FALSE;
17877978SPeter.Dunlap@Sun.COM 			}
17887978SPeter.Dunlap@Sun.COM 
17897978SPeter.Dunlap@Sun.COM 			continue;
17907978SPeter.Dunlap@Sun.COM 		}
17917978SPeter.Dunlap@Sun.COM 
17927978SPeter.Dunlap@Sun.COM 		/*
17937978SPeter.Dunlap@Sun.COM 		 * Header has been read and validated.  Now we need
17947978SPeter.Dunlap@Sun.COM 		 * to read the PDU data payload (if present).  SCSI data
17957978SPeter.Dunlap@Sun.COM 		 * need to be transferred from the socket directly into
17967978SPeter.Dunlap@Sun.COM 		 * the associated transfer buffer for the SCSI task.
17977978SPeter.Dunlap@Sun.COM 		 */
17987978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen != 0) {
17997978SPeter.Dunlap@Sun.COM 			if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) ||
18007978SPeter.Dunlap@Sun.COM 			    (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) {
18017978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_scsidata(ic, pdu);
18027978SPeter.Dunlap@Sun.COM 				/*
18037978SPeter.Dunlap@Sun.COM 				 * All SCSI errors are fatal to the
18047978SPeter.Dunlap@Sun.COM 				 * connection right now since we have no
18057978SPeter.Dunlap@Sun.COM 				 * place to put the data.  What we need
18067978SPeter.Dunlap@Sun.COM 				 * is some kind of sink to dispose of unwanted
18077978SPeter.Dunlap@Sun.COM 				 * SCSI data.  For example an invalid task tag
18087978SPeter.Dunlap@Sun.COM 				 * should not kill the connection (although
18097978SPeter.Dunlap@Sun.COM 				 * we may want to drop the connection).
18107978SPeter.Dunlap@Sun.COM 				 */
18117978SPeter.Dunlap@Sun.COM 			} else {
18127978SPeter.Dunlap@Sun.COM 				/*
18137978SPeter.Dunlap@Sun.COM 				 * Not data PDUs so allocate a buffer for the
18147978SPeter.Dunlap@Sun.COM 				 * data segment and read the remaining data.
18157978SPeter.Dunlap@Sun.COM 				 */
18167978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_nonscsidata(ic, pdu);
18177978SPeter.Dunlap@Sun.COM 			}
18187978SPeter.Dunlap@Sun.COM 			if (rc != 0) {
18197978SPeter.Dunlap@Sun.COM 				/*
18207978SPeter.Dunlap@Sun.COM 				 * Call idm_pdu_complete so that we call the
18217978SPeter.Dunlap@Sun.COM 				 * callback and ensure any memory allocated
18227978SPeter.Dunlap@Sun.COM 				 * in idm_sorecvhdr gets freed up.
18237978SPeter.Dunlap@Sun.COM 				 */
18247978SPeter.Dunlap@Sun.COM 				idm_pdu_complete(pdu, IDM_STATUS_FAIL);
18257978SPeter.Dunlap@Sun.COM 
18267978SPeter.Dunlap@Sun.COM 				/*
18277978SPeter.Dunlap@Sun.COM 				 * If ic_rx_thread_running is still set then
18287978SPeter.Dunlap@Sun.COM 				 * this is some kind of connection problem
18297978SPeter.Dunlap@Sun.COM 				 * on the socket.  In this case we want to
18307978SPeter.Dunlap@Sun.COM 				 * generate an event.  Otherwise some other
18317978SPeter.Dunlap@Sun.COM 				 * thread closed the socket due to another
18327978SPeter.Dunlap@Sun.COM 				 * issue in which case we don't need to
18337978SPeter.Dunlap@Sun.COM 				 * generate an event.
18347978SPeter.Dunlap@Sun.COM 				 */
18357978SPeter.Dunlap@Sun.COM 				mutex_enter(&ic->ic_mutex);
18367978SPeter.Dunlap@Sun.COM 				if (so_conn->ic_rx_thread_running) {
18377978SPeter.Dunlap@Sun.COM 					conn_failure = B_TRUE;
18387978SPeter.Dunlap@Sun.COM 					so_conn->ic_rx_thread_running = B_FALSE;
18397978SPeter.Dunlap@Sun.COM 				}
18407978SPeter.Dunlap@Sun.COM 				continue;
18417978SPeter.Dunlap@Sun.COM 			}
18427978SPeter.Dunlap@Sun.COM 		}
18437978SPeter.Dunlap@Sun.COM 
18447978SPeter.Dunlap@Sun.COM 		/*
18457978SPeter.Dunlap@Sun.COM 		 * Process RX PDU
18467978SPeter.Dunlap@Sun.COM 		 */
18477978SPeter.Dunlap@Sun.COM 		idm_pdu_rx(ic, pdu);
18487978SPeter.Dunlap@Sun.COM 
18497978SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_mutex);
18507978SPeter.Dunlap@Sun.COM 	}
18517978SPeter.Dunlap@Sun.COM 
18527978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
18537978SPeter.Dunlap@Sun.COM 
18547978SPeter.Dunlap@Sun.COM 	/*
18557978SPeter.Dunlap@Sun.COM 	 * If we dropped out of the RX processing loop because of
18567978SPeter.Dunlap@Sun.COM 	 * a socket problem or other connection failure (including
18577978SPeter.Dunlap@Sun.COM 	 * digest errors) then we need to generate a state machine
18587978SPeter.Dunlap@Sun.COM 	 * event to shut the connection down.
18597978SPeter.Dunlap@Sun.COM 	 * If the state machine is already in, for example, INIT_ERROR, this
18607978SPeter.Dunlap@Sun.COM 	 * event will get dropped, and the TX thread will never be notified
18617978SPeter.Dunlap@Sun.COM 	 * to shut down.  To be safe, we'll just notify it here.
18627978SPeter.Dunlap@Sun.COM 	 */
18637978SPeter.Dunlap@Sun.COM 	if (conn_failure) {
18647978SPeter.Dunlap@Sun.COM 		if (so_conn->ic_tx_thread_running) {
18657978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
18667978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
18677978SPeter.Dunlap@Sun.COM 			cv_signal(&so_conn->ic_tx_cv);
18687978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
18697978SPeter.Dunlap@Sun.COM 		}
18707978SPeter.Dunlap@Sun.COM 
18717978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_TRANSPORT_FAIL, rc);
18727978SPeter.Dunlap@Sun.COM 	}
18737978SPeter.Dunlap@Sun.COM 
18747978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
18757978SPeter.Dunlap@Sun.COM 
18767978SPeter.Dunlap@Sun.COM 	thread_exit();
18777978SPeter.Dunlap@Sun.COM }
18787978SPeter.Dunlap@Sun.COM 
18797978SPeter.Dunlap@Sun.COM /*
18807978SPeter.Dunlap@Sun.COM  * idm_so_tx
18817978SPeter.Dunlap@Sun.COM  *
18827978SPeter.Dunlap@Sun.COM  * This is the implementation of idm_transport_ops_t's it_tx_pdu entry
18837978SPeter.Dunlap@Sun.COM  * point.  By definition, it is supposed to be fast.  So, simply queue
18847978SPeter.Dunlap@Sun.COM  * the entry and return.  The real work is done by idm_i_so_tx() via
18857978SPeter.Dunlap@Sun.COM  * idm_sotx_thread().
18867978SPeter.Dunlap@Sun.COM  */
18877978SPeter.Dunlap@Sun.COM 
18887978SPeter.Dunlap@Sun.COM static void
18897978SPeter.Dunlap@Sun.COM idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu)
18907978SPeter.Dunlap@Sun.COM {
18917978SPeter.Dunlap@Sun.COM 	idm_so_conn_t *so_conn = ic->ic_transport_private;
18927978SPeter.Dunlap@Sun.COM 
18937978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_ic == ic);
18947978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
18957978SPeter.Dunlap@Sun.COM 
18967978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
18977978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
18987978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_ABORTED);
18997978SPeter.Dunlap@Sun.COM 		return;
19007978SPeter.Dunlap@Sun.COM 	}
19017978SPeter.Dunlap@Sun.COM 
19027978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)pdu);
19037978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
19047978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
19057978SPeter.Dunlap@Sun.COM }
19067978SPeter.Dunlap@Sun.COM 
19077978SPeter.Dunlap@Sun.COM static idm_status_t
19087978SPeter.Dunlap@Sun.COM idm_i_so_tx(idm_pdu_t *pdu)
19097978SPeter.Dunlap@Sun.COM {
19107978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = pdu->isp_ic;
19117978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
19127978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
19137978SPeter.Dunlap@Sun.COM 	int		pad_len;
19147978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
19157978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc = 0;
19167978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
19177978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
19187978SPeter.Dunlap@Sun.COM 	struct iovec	iov[6];
19197978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
19207978SPeter.Dunlap@Sun.COM 
19217978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
19227978SPeter.Dunlap@Sun.COM 
19237978SPeter.Dunlap@Sun.COM 	/* Setup BHS */
19247978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_base	= (caddr_t)pdu->isp_hdr;
19257978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_len	= pdu->isp_hdrlen;
19267978SPeter.Dunlap@Sun.COM 	total_len		+= iov[iovlen].iov_len;
19277978SPeter.Dunlap@Sun.COM 	iovlen++;
19287978SPeter.Dunlap@Sun.COM 
19297978SPeter.Dunlap@Sun.COM 	/* Setup header digest */
19307978SPeter.Dunlap@Sun.COM 	if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
19317978SPeter.Dunlap@Sun.COM 	    (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) {
19327978SPeter.Dunlap@Sun.COM 		hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen);
19337978SPeter.Dunlap@Sun.COM 
19347978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&hdr_digest_crc;
19357978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (hdr_digest_crc);
19367978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
19377978SPeter.Dunlap@Sun.COM 		iovlen++;
19387978SPeter.Dunlap@Sun.COM 	}
19397978SPeter.Dunlap@Sun.COM 
19407978SPeter.Dunlap@Sun.COM 	/* Setup the data */
19417978SPeter.Dunlap@Sun.COM 	if (pdu->isp_datalen) {
19427978SPeter.Dunlap@Sun.COM 		idm_task_t		*idt;
19437978SPeter.Dunlap@Sun.COM 		idm_buf_t		*idb;
19447978SPeter.Dunlap@Sun.COM 		iscsi_data_hdr_t	*ihp;
19457978SPeter.Dunlap@Sun.COM 		ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
19467978SPeter.Dunlap@Sun.COM 		/* Write of immediate data */
19477978SPeter.Dunlap@Sun.COM 		if (ic->ic_ffp &&
19487978SPeter.Dunlap@Sun.COM 		    (ihp->opcode == ISCSI_OP_SCSI_CMD ||
19497978SPeter.Dunlap@Sun.COM 		    ihp->opcode == ISCSI_OP_SCSI_DATA)) {
19507978SPeter.Dunlap@Sun.COM 			idt = idm_task_find(ic, ihp->itt, ihp->ttt);
19517978SPeter.Dunlap@Sun.COM 			if (idt) {
19527978SPeter.Dunlap@Sun.COM 				mutex_enter(&idt->idt_mutex);
19537978SPeter.Dunlap@Sun.COM 				idb = idm_buf_find(&idt->idt_outbufv, 0);
19547978SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
19559162SPeter.Dunlap@Sun.COM 				/*
19569162SPeter.Dunlap@Sun.COM 				 * If the initiator call to idm_buf_alloc
19579162SPeter.Dunlap@Sun.COM 				 * failed then we can get to this point
19589162SPeter.Dunlap@Sun.COM 				 * without a bound buffer.  The associated
19599162SPeter.Dunlap@Sun.COM 				 * connection failure will clean things up
19609162SPeter.Dunlap@Sun.COM 				 * later.  It would be nice to come up with
19619162SPeter.Dunlap@Sun.COM 				 * a cleaner way to handle this.  In
19629162SPeter.Dunlap@Sun.COM 				 * particular it seems absurd to look up
19639162SPeter.Dunlap@Sun.COM 				 * the task and the buffer just to update
19649162SPeter.Dunlap@Sun.COM 				 * this counter.
19659162SPeter.Dunlap@Sun.COM 				 */
19669162SPeter.Dunlap@Sun.COM 				if (idb)
19679162SPeter.Dunlap@Sun.COM 					idb->idb_xfer_len += pdu->isp_datalen;
19689162SPeter.Dunlap@Sun.COM 				idm_task_rele(idt);
19697978SPeter.Dunlap@Sun.COM 			}
19707978SPeter.Dunlap@Sun.COM 		}
19717978SPeter.Dunlap@Sun.COM 
19727978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)pdu->isp_data;
19737978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pdu->isp_datalen;
19747978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
19757978SPeter.Dunlap@Sun.COM 		iovlen++;
19767978SPeter.Dunlap@Sun.COM 	}
19777978SPeter.Dunlap@Sun.COM 
19787978SPeter.Dunlap@Sun.COM 	/* Setup the data pad if necessary */
19797978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
19807978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
19817978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
19827978SPeter.Dunlap@Sun.COM 
19837978SPeter.Dunlap@Sun.COM 	if (pad_len) {
19847978SPeter.Dunlap@Sun.COM 		bzero(pad, sizeof (pad));
19857978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (void *)&pad;
19867978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pad_len;
19877978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
19887978SPeter.Dunlap@Sun.COM 		iovlen++;
19897978SPeter.Dunlap@Sun.COM 	}
19907978SPeter.Dunlap@Sun.COM 
19917978SPeter.Dunlap@Sun.COM 	/*
19927978SPeter.Dunlap@Sun.COM 	 * Setup the data digest if enabled.  Data-digest is not sent
19937978SPeter.Dunlap@Sun.COM 	 * for login-phase PDUs.
19947978SPeter.Dunlap@Sun.COM 	 */
19957978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) &&
19967978SPeter.Dunlap@Sun.COM 	    ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
19977978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen || pad_len)) {
19987978SPeter.Dunlap@Sun.COM 		/*
19997978SPeter.Dunlap@Sun.COM 		 * RFC3720/10.2.3: A zero-length Data Segment also
20007978SPeter.Dunlap@Sun.COM 		 * implies a zero-length data digest.
20017978SPeter.Dunlap@Sun.COM 		 */
20027978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen) {
20037978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c(pdu->isp_data,
20047978SPeter.Dunlap@Sun.COM 			    pdu->isp_datalen);
20057978SPeter.Dunlap@Sun.COM 		}
20067978SPeter.Dunlap@Sun.COM 		if (pad_len) {
20077978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c_continued(&pad,
20087978SPeter.Dunlap@Sun.COM 			    pad_len, data_digest_crc);
20097978SPeter.Dunlap@Sun.COM 		}
20107978SPeter.Dunlap@Sun.COM 
20117978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&data_digest_crc;
20127978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (data_digest_crc);
20137978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
20147978SPeter.Dunlap@Sun.COM 		iovlen++;
20157978SPeter.Dunlap@Sun.COM 	}
20167978SPeter.Dunlap@Sun.COM 
20177978SPeter.Dunlap@Sun.COM 	/* Transmit the PDU */
20187978SPeter.Dunlap@Sun.COM 	if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen,
20197978SPeter.Dunlap@Sun.COM 	    total_len) != 0) {
20207978SPeter.Dunlap@Sun.COM 		/* Set error status */
20217978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
20227978SPeter.Dunlap@Sun.COM 		    "idm_so_tx: failed to transmit the PDU, so: %p ic: %p "
20237978SPeter.Dunlap@Sun.COM 		    "data: %p", (void *) so_conn->ic_so, (void *) ic,
20247978SPeter.Dunlap@Sun.COM 		    (void *) pdu->isp_data);
20257978SPeter.Dunlap@Sun.COM 		status = IDM_STATUS_IO;
20267978SPeter.Dunlap@Sun.COM 	}
20277978SPeter.Dunlap@Sun.COM 
20287978SPeter.Dunlap@Sun.COM 	/*
20297978SPeter.Dunlap@Sun.COM 	 * Success does not mean that the PDU actually reached the
20307978SPeter.Dunlap@Sun.COM 	 * remote node since it could get dropped along the way.
20317978SPeter.Dunlap@Sun.COM 	 */
20327978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, status);
20337978SPeter.Dunlap@Sun.COM 
20347978SPeter.Dunlap@Sun.COM 	return (status);
20357978SPeter.Dunlap@Sun.COM }
20367978SPeter.Dunlap@Sun.COM 
20377978SPeter.Dunlap@Sun.COM /*
20387978SPeter.Dunlap@Sun.COM  * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the
20397978SPeter.Dunlap@Sun.COM  * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength,
20407978SPeter.Dunlap@Sun.COM  * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN.
20417978SPeter.Dunlap@Sun.COM  * A target can invoke this function multiple times for a single read command
20427978SPeter.Dunlap@Sun.COM  * (identified by the same ITT) to split the input into several sequences.
20437978SPeter.Dunlap@Sun.COM  *
20447978SPeter.Dunlap@Sun.COM  * DataSN starts with 0 for the first data PDU of an input command and advances
20457978SPeter.Dunlap@Sun.COM  * by 1 for each subsequent data PDU. Each sequence will have its own F bit,
20467978SPeter.Dunlap@Sun.COM  * which is set to 1 for the last data PDU of a sequence.
20477978SPeter.Dunlap@Sun.COM  *
20487978SPeter.Dunlap@Sun.COM  * Scope for Prototype build:
20497978SPeter.Dunlap@Sun.COM  * The data PDUs within a sequence will be sent in order with the buffer offset
20507978SPeter.Dunlap@Sun.COM  * in increasing order. i.e. initiator and target must have negotiated the
20517978SPeter.Dunlap@Sun.COM  * "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
20527978SPeter.Dunlap@Sun.COM  *
20537978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
20547978SPeter.Dunlap@Sun.COM  */
20557978SPeter.Dunlap@Sun.COM static idm_status_t
20567978SPeter.Dunlap@Sun.COM idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb)
20577978SPeter.Dunlap@Sun.COM {
20587978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = idb->idb_ic->ic_transport_private;
20597978SPeter.Dunlap@Sun.COM 	idm_pdu_t	tmppdu;
20607978SPeter.Dunlap@Sun.COM 
20617978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
20627978SPeter.Dunlap@Sun.COM 
20637978SPeter.Dunlap@Sun.COM 	/*
20647978SPeter.Dunlap@Sun.COM 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
20657978SPeter.Dunlap@Sun.COM 	 * idm_sotx_thread.
20667978SPeter.Dunlap@Sun.COM 	 */
20677978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
20687978SPeter.Dunlap@Sun.COM 
20697978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
20707978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
20717978SPeter.Dunlap@Sun.COM 		/*
20727978SPeter.Dunlap@Sun.COM 		 * Don't release idt->idt_mutex since we're supposed to hold
20737978SPeter.Dunlap@Sun.COM 		 * in when calling idm_buf_tx_to_ini_done
20747978SPeter.Dunlap@Sun.COM 		 */
20757978SPeter.Dunlap@Sun.COM 		idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
20767978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
20777978SPeter.Dunlap@Sun.COM 	}
20787978SPeter.Dunlap@Sun.COM 
20797978SPeter.Dunlap@Sun.COM 	/*
20807978SPeter.Dunlap@Sun.COM 	 * Build a template for the data PDU headers we will use so that
20817978SPeter.Dunlap@Sun.COM 	 * the SN values will stay consistent with other PDU's we are
20827978SPeter.Dunlap@Sun.COM 	 * transmitting like R2T and SCSI status.
20837978SPeter.Dunlap@Sun.COM 	 */
20847978SPeter.Dunlap@Sun.COM 	bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
20857978SPeter.Dunlap@Sun.COM 	tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl;
20867978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
20877978SPeter.Dunlap@Sun.COM 	    ISCSI_OP_SCSI_DATA_RSP);
20887978SPeter.Dunlap@Sun.COM 	idb->idb_tx_thread = B_TRUE;
20897978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)idb);
20907978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
20917978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
20927978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
20937978SPeter.Dunlap@Sun.COM 
20947978SPeter.Dunlap@Sun.COM 	/*
20957978SPeter.Dunlap@Sun.COM 	 * Returning success here indicates the transfer was successfully
20967978SPeter.Dunlap@Sun.COM 	 * dispatched -- it does not mean that the transfer completed
20977978SPeter.Dunlap@Sun.COM 	 * successfully.
20987978SPeter.Dunlap@Sun.COM 	 */
20997978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21007978SPeter.Dunlap@Sun.COM }
21017978SPeter.Dunlap@Sun.COM 
21027978SPeter.Dunlap@Sun.COM /*
21037978SPeter.Dunlap@Sun.COM  * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the
21047978SPeter.Dunlap@Sun.COM  * data blocks it is ready to receive from the initiator in response to a WRITE
21057978SPeter.Dunlap@Sun.COM  * SCSI command. The target iSCSI layer passes the information about the desired
21067978SPeter.Dunlap@Sun.COM  * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer
21077978SPeter.Dunlap@Sun.COM  * offset and datalen are passed via the 'idb' argument.
21087978SPeter.Dunlap@Sun.COM  *
21097978SPeter.Dunlap@Sun.COM  * Scope for Prototype build:
21107978SPeter.Dunlap@Sun.COM  * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have
21117978SPeter.Dunlap@Sun.COM  * negotiated the "InitialR2T" to "Yes".
21127978SPeter.Dunlap@Sun.COM  *
21137978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
21147978SPeter.Dunlap@Sun.COM  */
21157978SPeter.Dunlap@Sun.COM static idm_status_t
21167978SPeter.Dunlap@Sun.COM idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
21177978SPeter.Dunlap@Sun.COM {
21187978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
21197978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt;
21207978SPeter.Dunlap@Sun.COM 
21217978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
21227978SPeter.Dunlap@Sun.COM 
21237978SPeter.Dunlap@Sun.COM 	pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
21247978SPeter.Dunlap@Sun.COM 	pdu->isp_ic = idt->idt_ic;
21257978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
21267978SPeter.Dunlap@Sun.COM 
21277978SPeter.Dunlap@Sun.COM 	/* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */
21287978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP);
21297978SPeter.Dunlap@Sun.COM 
21307978SPeter.Dunlap@Sun.COM 	/* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */
21317978SPeter.Dunlap@Sun.COM 	rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr);
21327978SPeter.Dunlap@Sun.COM 
21337978SPeter.Dunlap@Sun.COM 	rtt->opcode		= ISCSI_OP_RTT_RSP;
21347978SPeter.Dunlap@Sun.COM 	rtt->flags		= ISCSI_FLAG_FINAL;
21357978SPeter.Dunlap@Sun.COM 	rtt->data_offset	= htonl(idb->idb_bufoffset);
21367978SPeter.Dunlap@Sun.COM 	rtt->data_length	= htonl(idb->idb_xfer_len);
21377978SPeter.Dunlap@Sun.COM 	rtt->rttsn		= htonl(idt->idt_exp_rttsn++);
21387978SPeter.Dunlap@Sun.COM 
21397978SPeter.Dunlap@Sun.COM 	/* Keep track of buffer offsets */
21407978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset	= idb->idb_bufoffset;
21417978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
21427978SPeter.Dunlap@Sun.COM 
21437978SPeter.Dunlap@Sun.COM 	/*
21448607SJames.Moore@Sun.COM 	 * Transmit the PDU.
21457978SPeter.Dunlap@Sun.COM 	 */
21468607SJames.Moore@Sun.COM 	idm_pdu_tx(pdu);
21477978SPeter.Dunlap@Sun.COM 
21487978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21497978SPeter.Dunlap@Sun.COM }
21507978SPeter.Dunlap@Sun.COM 
21517978SPeter.Dunlap@Sun.COM static idm_status_t
21527978SPeter.Dunlap@Sun.COM idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen)
21537978SPeter.Dunlap@Sun.COM {
2154*9247SPeter.Dunlap@Sun.COM 	if ((buflen > IDM_SO_BUF_CACHE_LB) && (buflen <= IDM_SO_BUF_CACHE_UB)) {
2155*9247SPeter.Dunlap@Sun.COM 		idb->idb_buf = kmem_cache_alloc(idm.idm_so_128k_buf_cache,
2156*9247SPeter.Dunlap@Sun.COM 		    KM_NOSLEEP);
2157*9247SPeter.Dunlap@Sun.COM 		idb->idb_buf_private = idm.idm_so_128k_buf_cache;
2158*9247SPeter.Dunlap@Sun.COM 	} else {
2159*9247SPeter.Dunlap@Sun.COM 		idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP);
2160*9247SPeter.Dunlap@Sun.COM 		idb->idb_buf_private = NULL;
2161*9247SPeter.Dunlap@Sun.COM 	}
2162*9247SPeter.Dunlap@Sun.COM 
21637978SPeter.Dunlap@Sun.COM 	if (idb->idb_buf == NULL) {
21647978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE,
21657978SPeter.Dunlap@Sun.COM 		    "idm_so_buf_alloc: failed buffer allocation");
21667978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
21677978SPeter.Dunlap@Sun.COM 	}
2168*9247SPeter.Dunlap@Sun.COM 
21697978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21707978SPeter.Dunlap@Sun.COM }
21717978SPeter.Dunlap@Sun.COM 
21727978SPeter.Dunlap@Sun.COM /* ARGSUSED */
21737978SPeter.Dunlap@Sun.COM static idm_status_t
21747978SPeter.Dunlap@Sun.COM idm_so_buf_setup(idm_buf_t *idb)
21757978SPeter.Dunlap@Sun.COM {
21769162SPeter.Dunlap@Sun.COM 	/* Ensure bufalloc'd flag is unset */
21779162SPeter.Dunlap@Sun.COM 	idb->idb_bufalloc = B_FALSE;
21789162SPeter.Dunlap@Sun.COM 
21797978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21807978SPeter.Dunlap@Sun.COM }
21817978SPeter.Dunlap@Sun.COM 
21827978SPeter.Dunlap@Sun.COM /* ARGSUSED */
21837978SPeter.Dunlap@Sun.COM static void
21847978SPeter.Dunlap@Sun.COM idm_so_buf_teardown(idm_buf_t *idb)
21857978SPeter.Dunlap@Sun.COM {
21867978SPeter.Dunlap@Sun.COM 	/* nothing to do here */
21877978SPeter.Dunlap@Sun.COM }
21887978SPeter.Dunlap@Sun.COM 
21897978SPeter.Dunlap@Sun.COM static void
21907978SPeter.Dunlap@Sun.COM idm_so_buf_free(idm_buf_t *idb)
21917978SPeter.Dunlap@Sun.COM {
2192*9247SPeter.Dunlap@Sun.COM 	if (idb->idb_buf_private == NULL) {
2193*9247SPeter.Dunlap@Sun.COM 		kmem_free(idb->idb_buf, idb->idb_buflen);
2194*9247SPeter.Dunlap@Sun.COM 	} else {
2195*9247SPeter.Dunlap@Sun.COM 		kmem_cache_free(idb->idb_buf_private, idb->idb_buf);
2196*9247SPeter.Dunlap@Sun.COM 	}
21977978SPeter.Dunlap@Sun.COM }
21987978SPeter.Dunlap@Sun.COM 
21999162SPeter.Dunlap@Sun.COM static void
22009162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb,
22019162SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t length)
22029162SPeter.Dunlap@Sun.COM {
22039162SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
22049162SPeter.Dunlap@Sun.COM 	idm_pdu_t	tmppdu;
22059162SPeter.Dunlap@Sun.COM 	idm_buf_t	*rtt_buf;
22069162SPeter.Dunlap@Sun.COM 
22079162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
22089162SPeter.Dunlap@Sun.COM 
22099162SPeter.Dunlap@Sun.COM 	/*
22109162SPeter.Dunlap@Sun.COM 	 * Allocate a buffer to represent the RTT transfer.  We could further
22119162SPeter.Dunlap@Sun.COM 	 * optimize this by allocating the buffers internally from an rtt
22129162SPeter.Dunlap@Sun.COM 	 * specific buffer cache since this is socket-specific code but for
22139162SPeter.Dunlap@Sun.COM 	 * now we will keep it simple.
22149162SPeter.Dunlap@Sun.COM 	 */
22159162SPeter.Dunlap@Sun.COM 	rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length);
22169162SPeter.Dunlap@Sun.COM 	if (rtt_buf == NULL) {
22179162SPeter.Dunlap@Sun.COM 		/*
22189162SPeter.Dunlap@Sun.COM 		 * If we're in FFP then the failure was likely a resource
22199162SPeter.Dunlap@Sun.COM 		 * allocation issue and we should close the connection by
22209162SPeter.Dunlap@Sun.COM 		 * sending a CE_TRANSPORT_FAIL event.
22219162SPeter.Dunlap@Sun.COM 		 *
22229162SPeter.Dunlap@Sun.COM 		 * If we're not in FFP then idm_buf_alloc will always
22239162SPeter.Dunlap@Sun.COM 		 * fail and the state is transitioning to "complete" anyway
22249162SPeter.Dunlap@Sun.COM 		 * so we won't bother to send an event.
22259162SPeter.Dunlap@Sun.COM 		 */
22269162SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_state_mutex);
22279162SPeter.Dunlap@Sun.COM 		if (ic->ic_ffp)
22289162SPeter.Dunlap@Sun.COM 			idm_conn_event_locked(ic, CE_TRANSPORT_FAIL,
22299162SPeter.Dunlap@Sun.COM 			    NULL, CT_NONE);
22309162SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
22319162SPeter.Dunlap@Sun.COM 		return;
22329162SPeter.Dunlap@Sun.COM 	}
22339162SPeter.Dunlap@Sun.COM 
22349162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_buf_cb = NULL;
22359162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_cb_arg = NULL;
22369162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_bufoffset = offset;
22379162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_xfer_len = length;
22389162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_ic = idt->idt_ic;
22399162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_task_binding = idt;
22409162SPeter.Dunlap@Sun.COM 
22419162SPeter.Dunlap@Sun.COM 	/*
22429162SPeter.Dunlap@Sun.COM 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
22439162SPeter.Dunlap@Sun.COM 	 * idm_sotx_thread.
22449162SPeter.Dunlap@Sun.COM 	 */
22459162SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
22469162SPeter.Dunlap@Sun.COM 
22479162SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
22489162SPeter.Dunlap@Sun.COM 		idm_buf_free(rtt_buf);
22499162SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
22509162SPeter.Dunlap@Sun.COM 		return;
22519162SPeter.Dunlap@Sun.COM 	}
22529162SPeter.Dunlap@Sun.COM 
22539162SPeter.Dunlap@Sun.COM 	/*
22549162SPeter.Dunlap@Sun.COM 	 * This new buffer represents an additional reference on the task
22559162SPeter.Dunlap@Sun.COM 	 */
22569162SPeter.Dunlap@Sun.COM 	idm_task_hold(idt);
22579162SPeter.Dunlap@Sun.COM 
22589162SPeter.Dunlap@Sun.COM 	/*
22599162SPeter.Dunlap@Sun.COM 	 * Build a template for the data PDU headers we will use so that
22609162SPeter.Dunlap@Sun.COM 	 * the SN values will stay consistent with other PDU's we are
22619162SPeter.Dunlap@Sun.COM 	 * transmitting like R2T and SCSI status.
22629162SPeter.Dunlap@Sun.COM 	 */
22639162SPeter.Dunlap@Sun.COM 	bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
22649162SPeter.Dunlap@Sun.COM 	tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl;
22659162SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
22669162SPeter.Dunlap@Sun.COM 	    ISCSI_OP_SCSI_DATA);
22679162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_tx_thread = B_TRUE;
22689162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_in_transport = B_TRUE;
22699162SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf);
22709162SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
22719162SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
22729162SPeter.Dunlap@Sun.COM }
22739162SPeter.Dunlap@Sun.COM 
22749162SPeter.Dunlap@Sun.COM static void
22759162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb)
22769162SPeter.Dunlap@Sun.COM {
22779162SPeter.Dunlap@Sun.COM 	/*
22789162SPeter.Dunlap@Sun.COM 	 * Don't worry about status -- we assume any error handling
22799162SPeter.Dunlap@Sun.COM 	 * is performed by the caller (idm_sotx_thread).
22809162SPeter.Dunlap@Sun.COM 	 */
22819162SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
22829162SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
22839162SPeter.Dunlap@Sun.COM 	idm_buf_free(idb);
22849162SPeter.Dunlap@Sun.COM }
22859162SPeter.Dunlap@Sun.COM 
22869162SPeter.Dunlap@Sun.COM static idm_status_t
22879162SPeter.Dunlap@Sun.COM idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
22887978SPeter.Dunlap@Sun.COM     uint32_t buf_region_offset, uint32_t buf_region_length)
22897978SPeter.Dunlap@Sun.COM {
22907978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
22917978SPeter.Dunlap@Sun.COM 	uint32_t		max_dataseglen;
22927978SPeter.Dunlap@Sun.COM 	size_t			remainder, chunk;
22937978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset = buf_region_offset;
22947978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
22957978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
22969162SPeter.Dunlap@Sun.COM 	idm_status_t		tx_status;
22977978SPeter.Dunlap@Sun.COM 
22987978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
22997978SPeter.Dunlap@Sun.COM 
23007978SPeter.Dunlap@Sun.COM 	ic = idt->idt_ic;
23017978SPeter.Dunlap@Sun.COM 
23027978SPeter.Dunlap@Sun.COM 	max_dataseglen = 8192; /* Need value from login negotiation */
23037978SPeter.Dunlap@Sun.COM 	remainder = buf_region_length;
23047978SPeter.Dunlap@Sun.COM 
23057978SPeter.Dunlap@Sun.COM 	while (remainder) {
23067978SPeter.Dunlap@Sun.COM 		if (idt->idt_state != TASK_ACTIVE) {
23077978SPeter.Dunlap@Sun.COM 			ASSERT((idt->idt_state != TASK_IDLE) &&
23087978SPeter.Dunlap@Sun.COM 			    (idt->idt_state != TASK_COMPLETE));
23097978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_ABORTED);
23107978SPeter.Dunlap@Sun.COM 		}
23117978SPeter.Dunlap@Sun.COM 
23127978SPeter.Dunlap@Sun.COM 		/* check to see if we need to chunk the data */
23137978SPeter.Dunlap@Sun.COM 		if (remainder > max_dataseglen) {
23147978SPeter.Dunlap@Sun.COM 			chunk = max_dataseglen;
23157978SPeter.Dunlap@Sun.COM 		} else {
23167978SPeter.Dunlap@Sun.COM 			chunk = remainder;
23177978SPeter.Dunlap@Sun.COM 		}
23187978SPeter.Dunlap@Sun.COM 
23197978SPeter.Dunlap@Sun.COM 		/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
23207978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
23217978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
23227978SPeter.Dunlap@Sun.COM 
23237978SPeter.Dunlap@Sun.COM 		/*
23249162SPeter.Dunlap@Sun.COM 		 * We've already built a build a header template
23257978SPeter.Dunlap@Sun.COM 		 * to use during the transfer.  Use this template so that
23267978SPeter.Dunlap@Sun.COM 		 * the SN values stay consistent with any unrelated PDU's
23277978SPeter.Dunlap@Sun.COM 		 * being transmitted.
23287978SPeter.Dunlap@Sun.COM 		 */
23299162SPeter.Dunlap@Sun.COM 		bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr,
23309162SPeter.Dunlap@Sun.COM 		    sizeof (iscsi_hdr_t));
23317978SPeter.Dunlap@Sun.COM 
23327978SPeter.Dunlap@Sun.COM 		/*
23337978SPeter.Dunlap@Sun.COM 		 * Set DataSN, data offset, and flags in BHS
23347978SPeter.Dunlap@Sun.COM 		 * For the prototype build, A = 0, S = 0, U = 0
23357978SPeter.Dunlap@Sun.COM 		 */
23367978SPeter.Dunlap@Sun.COM 		bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr);
23377978SPeter.Dunlap@Sun.COM 
23387978SPeter.Dunlap@Sun.COM 		bhs->datasn		= htonl(idt->idt_exp_datasn++);
23397978SPeter.Dunlap@Sun.COM 
23407978SPeter.Dunlap@Sun.COM 		hton24(bhs->dlength, chunk);
23417978SPeter.Dunlap@Sun.COM 		bhs->offset = htonl(idb->idb_bufoffset + data_offset);
23427978SPeter.Dunlap@Sun.COM 
23437978SPeter.Dunlap@Sun.COM 		if (chunk == remainder) {
23447978SPeter.Dunlap@Sun.COM 			bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
23457978SPeter.Dunlap@Sun.COM 		}
23467978SPeter.Dunlap@Sun.COM 
23477978SPeter.Dunlap@Sun.COM 		/* setup data */
23487978SPeter.Dunlap@Sun.COM 		pdu->isp_data	=  (uint8_t *)idb->idb_buf + data_offset;
23497978SPeter.Dunlap@Sun.COM 		pdu->isp_datalen = (uint_t)chunk;
23507978SPeter.Dunlap@Sun.COM 		remainder	-= chunk;
23517978SPeter.Dunlap@Sun.COM 		data_offset	+= chunk;
23527978SPeter.Dunlap@Sun.COM 
23537978SPeter.Dunlap@Sun.COM 		/*
23547978SPeter.Dunlap@Sun.COM 		 * Now that we're done working with idt_exp_datasn,
23557978SPeter.Dunlap@Sun.COM 		 * idt->idt_state and idb->idb_bufoffset we can release
23567978SPeter.Dunlap@Sun.COM 		 * the task lock -- don't want to hold it across the
23577978SPeter.Dunlap@Sun.COM 		 * call to idm_i_so_tx since we could block.
23587978SPeter.Dunlap@Sun.COM 		 */
23597978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
23607978SPeter.Dunlap@Sun.COM 
23617978SPeter.Dunlap@Sun.COM 		/*
23627978SPeter.Dunlap@Sun.COM 		 * Transmit the PDU.  Call the internal routine directly
23637978SPeter.Dunlap@Sun.COM 		 * as there is already implicit ordering.
23647978SPeter.Dunlap@Sun.COM 		 */
23659162SPeter.Dunlap@Sun.COM 		if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) {
23669162SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
23679162SPeter.Dunlap@Sun.COM 			return (tx_status);
23689162SPeter.Dunlap@Sun.COM 		}
23697978SPeter.Dunlap@Sun.COM 
23707978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
23719162SPeter.Dunlap@Sun.COM 		idt->idt_tx_bytes += chunk;
23727978SPeter.Dunlap@Sun.COM 	}
23737978SPeter.Dunlap@Sun.COM 
23747978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
23757978SPeter.Dunlap@Sun.COM }
23767978SPeter.Dunlap@Sun.COM 
23777978SPeter.Dunlap@Sun.COM /*
23787978SPeter.Dunlap@Sun.COM  * TX PDU cache
23797978SPeter.Dunlap@Sun.COM  */
23807978SPeter.Dunlap@Sun.COM /* ARGSUSED */
23817978SPeter.Dunlap@Sun.COM int
23827978SPeter.Dunlap@Sun.COM idm_sotx_pdu_constructor(void *hdl, void *arg, int flags)
23837978SPeter.Dunlap@Sun.COM {
23847978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
23857978SPeter.Dunlap@Sun.COM 
23867978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
23877978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
23887978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
23897978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sotx_cache_pdu_cb;
23907978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
23917978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
23927978SPeter.Dunlap@Sun.COM 
23937978SPeter.Dunlap@Sun.COM 	return (0);
23947978SPeter.Dunlap@Sun.COM }
23957978SPeter.Dunlap@Sun.COM 
23967978SPeter.Dunlap@Sun.COM /* ARGSUSED */
23977978SPeter.Dunlap@Sun.COM void
23987978SPeter.Dunlap@Sun.COM idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
23997978SPeter.Dunlap@Sun.COM {
24007978SPeter.Dunlap@Sun.COM 	/* reset values between use */
24017978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
24027978SPeter.Dunlap@Sun.COM 
24037978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sotx_pdu_cache, pdu);
24047978SPeter.Dunlap@Sun.COM }
24057978SPeter.Dunlap@Sun.COM 
24067978SPeter.Dunlap@Sun.COM /*
24077978SPeter.Dunlap@Sun.COM  * RX PDU cache
24087978SPeter.Dunlap@Sun.COM  */
24097978SPeter.Dunlap@Sun.COM /* ARGSUSED */
24107978SPeter.Dunlap@Sun.COM int
24117978SPeter.Dunlap@Sun.COM idm_sorx_pdu_constructor(void *hdl, void *arg, int flags)
24127978SPeter.Dunlap@Sun.COM {
24137978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
24147978SPeter.Dunlap@Sun.COM 
24157978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
24167978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
24177978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
24187978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
24197978SPeter.Dunlap@Sun.COM 
24207978SPeter.Dunlap@Sun.COM 	return (0);
24217978SPeter.Dunlap@Sun.COM }
24227978SPeter.Dunlap@Sun.COM 
24237978SPeter.Dunlap@Sun.COM /* ARGSUSED */
24247978SPeter.Dunlap@Sun.COM static void
24257978SPeter.Dunlap@Sun.COM idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
24267978SPeter.Dunlap@Sun.COM {
24277978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 0;
24287978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
24297978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sorx_pdu_cache, pdu);
24307978SPeter.Dunlap@Sun.COM }
24317978SPeter.Dunlap@Sun.COM 
24327978SPeter.Dunlap@Sun.COM static void
24337978SPeter.Dunlap@Sun.COM idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
24347978SPeter.Dunlap@Sun.COM {
24357978SPeter.Dunlap@Sun.COM 	/*
24367978SPeter.Dunlap@Sun.COM 	 * We had to modify our cached RX PDU with a longer header buffer
24377978SPeter.Dunlap@Sun.COM 	 * and/or a longer data buffer.  Release the new buffers and fix
24387978SPeter.Dunlap@Sun.COM 	 * the fields back to what we would expect for a cached RX PDU.
24397978SPeter.Dunlap@Sun.COM 	 */
24407978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_HDR) {
24417978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_hdr, pdu->isp_hdrlen);
24427978SPeter.Dunlap@Sun.COM 	}
24437978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_DATA) {
24447978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_data, pdu->isp_datalen);
24457978SPeter.Dunlap@Sun.COM 	}
24467978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1);
24477978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
24487978SPeter.Dunlap@Sun.COM 	pdu->isp_data = NULL;
24497978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
24507978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
24517978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
24527978SPeter.Dunlap@Sun.COM 	idm_sorx_cache_pdu_cb(pdu, status);
24537978SPeter.Dunlap@Sun.COM }
24547978SPeter.Dunlap@Sun.COM 
24557978SPeter.Dunlap@Sun.COM /*
24567978SPeter.Dunlap@Sun.COM  * This thread is only active when I/O is queued for transmit
24577978SPeter.Dunlap@Sun.COM  * because the socket is busy.
24587978SPeter.Dunlap@Sun.COM  */
24597978SPeter.Dunlap@Sun.COM void
24607978SPeter.Dunlap@Sun.COM idm_sotx_thread(void *arg)
24617978SPeter.Dunlap@Sun.COM {
24627978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = arg;
24637978SPeter.Dunlap@Sun.COM 	idm_tx_obj_t	*object, *next;
24647978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
24657978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
24667978SPeter.Dunlap@Sun.COM 
24677978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
24687978SPeter.Dunlap@Sun.COM 
24697978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
24707978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
24717978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_TRUE;
24727978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did;
24737978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
24747978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
24757978SPeter.Dunlap@Sun.COM 
24767978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
24777978SPeter.Dunlap@Sun.COM 
24787978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_tx_thread_running) {
24797978SPeter.Dunlap@Sun.COM 		while (list_is_empty(&so_conn->ic_tx_list)) {
24807978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic);
24817978SPeter.Dunlap@Sun.COM 			cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex);
24827978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic);
24837978SPeter.Dunlap@Sun.COM 
24847978SPeter.Dunlap@Sun.COM 			if (!so_conn->ic_tx_thread_running) {
24857978SPeter.Dunlap@Sun.COM 				goto tx_bail;
24867978SPeter.Dunlap@Sun.COM 			}
24877978SPeter.Dunlap@Sun.COM 		}
24887978SPeter.Dunlap@Sun.COM 
24897978SPeter.Dunlap@Sun.COM 		object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
24907978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
24917978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
24927978SPeter.Dunlap@Sun.COM 
24937978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
24947978SPeter.Dunlap@Sun.COM 		case IDM_PDU_MAGIC:
24957978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
24967978SPeter.Dunlap@Sun.COM 			    idm_pdu_t *, (idm_pdu_t *)object);
24977978SPeter.Dunlap@Sun.COM 
24987978SPeter.Dunlap@Sun.COM 			status = idm_i_so_tx((idm_pdu_t *)object);
24997978SPeter.Dunlap@Sun.COM 			break;
25007978SPeter.Dunlap@Sun.COM 
25017978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
25027978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
25037978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
25047978SPeter.Dunlap@Sun.COM 
25057978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic,
25067978SPeter.Dunlap@Sun.COM 			    idm_buf_t *, idb);
25077978SPeter.Dunlap@Sun.COM 
25087978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
25097978SPeter.Dunlap@Sun.COM 			status = idm_so_send_buf_region(idt,
25109162SPeter.Dunlap@Sun.COM 			    idb, 0, idb->idb_xfer_len);
25117978SPeter.Dunlap@Sun.COM 
25127978SPeter.Dunlap@Sun.COM 			/*
25137978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
25147978SPeter.Dunlap@Sun.COM 			 * be "in transport"
25157978SPeter.Dunlap@Sun.COM 			 */
25167978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
25179162SPeter.Dunlap@Sun.COM 			if (IDM_CONN_ISTGT(ic)) {
25189162SPeter.Dunlap@Sun.COM 				/*
25199162SPeter.Dunlap@Sun.COM 				 * idm_buf_tx_to_ini_done releases
25209162SPeter.Dunlap@Sun.COM 				 * idt->idt_mutex
25219162SPeter.Dunlap@Sun.COM 				 */
25229162SPeter.Dunlap@Sun.COM 				idm_buf_tx_to_ini_done(idt, idb, status);
25239162SPeter.Dunlap@Sun.COM 			} else {
25249162SPeter.Dunlap@Sun.COM 				idm_so_send_rtt_data_done(idt, idb);
25259162SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
25269162SPeter.Dunlap@Sun.COM 			}
25277978SPeter.Dunlap@Sun.COM 			break;
25287978SPeter.Dunlap@Sun.COM 		}
25297978SPeter.Dunlap@Sun.COM 
25307978SPeter.Dunlap@Sun.COM 		default:
25317978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic "
25327978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
25337978SPeter.Dunlap@Sun.COM 			status = IDM_STATUS_FAIL;
25347978SPeter.Dunlap@Sun.COM 		}
25357978SPeter.Dunlap@Sun.COM 
25367978SPeter.Dunlap@Sun.COM 		mutex_enter(&so_conn->ic_tx_mutex);
25377978SPeter.Dunlap@Sun.COM 
25387978SPeter.Dunlap@Sun.COM 		if (status != IDM_STATUS_SUCCESS) {
25397978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
25407978SPeter.Dunlap@Sun.COM 			idm_conn_event(ic, CE_TRANSPORT_FAIL, status);
25417978SPeter.Dunlap@Sun.COM 		}
25427978SPeter.Dunlap@Sun.COM 	}
25437978SPeter.Dunlap@Sun.COM 
25447978SPeter.Dunlap@Sun.COM 	/*
25457978SPeter.Dunlap@Sun.COM 	 * Before we leave, we need to abort every item remaining in the
25467978SPeter.Dunlap@Sun.COM 	 * TX list.
25477978SPeter.Dunlap@Sun.COM 	 */
25487978SPeter.Dunlap@Sun.COM 
25497978SPeter.Dunlap@Sun.COM tx_bail:
25507978SPeter.Dunlap@Sun.COM 	object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
25517978SPeter.Dunlap@Sun.COM 
25527978SPeter.Dunlap@Sun.COM 	while (object != NULL) {
25537978SPeter.Dunlap@Sun.COM 		next = list_next(&so_conn->ic_tx_list, object);
25547978SPeter.Dunlap@Sun.COM 
25557978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
25567978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
25577978SPeter.Dunlap@Sun.COM 		case IDM_PDU_MAGIC:
25587978SPeter.Dunlap@Sun.COM 			idm_pdu_complete((idm_pdu_t *)object,
25597978SPeter.Dunlap@Sun.COM 			    IDM_STATUS_ABORTED);
25607978SPeter.Dunlap@Sun.COM 			break;
25617978SPeter.Dunlap@Sun.COM 
25627978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
25637978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
25647978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
25657978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
25667978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
25677978SPeter.Dunlap@Sun.COM 			/*
25687978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
25697978SPeter.Dunlap@Sun.COM 			 * be "in transport"
25707978SPeter.Dunlap@Sun.COM 			 */
25717978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
25729162SPeter.Dunlap@Sun.COM 			if (IDM_CONN_ISTGT(ic)) {
25739162SPeter.Dunlap@Sun.COM 				/*
25749162SPeter.Dunlap@Sun.COM 				 * idm_buf_tx_to_ini_done releases
25759162SPeter.Dunlap@Sun.COM 				 * idt->idt_mutex
25769162SPeter.Dunlap@Sun.COM 				 */
25779162SPeter.Dunlap@Sun.COM 				idm_buf_tx_to_ini_done(idt, idb,
25789162SPeter.Dunlap@Sun.COM 				    IDM_STATUS_ABORTED);
25799162SPeter.Dunlap@Sun.COM 			} else {
25809162SPeter.Dunlap@Sun.COM 				idm_so_send_rtt_data_done(idt, idb);
25819162SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
25829162SPeter.Dunlap@Sun.COM 			}
25837978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
25847978SPeter.Dunlap@Sun.COM 			break;
25857978SPeter.Dunlap@Sun.COM 		}
25867978SPeter.Dunlap@Sun.COM 		default:
25877978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
25887978SPeter.Dunlap@Sun.COM 			    "idm_sotx_thread: Unexpected magic "
25897978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
25907978SPeter.Dunlap@Sun.COM 		}
25917978SPeter.Dunlap@Sun.COM 
25927978SPeter.Dunlap@Sun.COM 		object = next;
25937978SPeter.Dunlap@Sun.COM 	}
25947978SPeter.Dunlap@Sun.COM 
25957978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
25967978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
25977978SPeter.Dunlap@Sun.COM 	thread_exit();
25987978SPeter.Dunlap@Sun.COM 	/*NOTREACHED*/
25997978SPeter.Dunlap@Sun.COM }
2600