xref: /onnv-gate/usr/src/uts/common/io/idm/idm_so.c (revision 8607:fbc1b5e97f5c)
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 /*
22*8607SJames.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);
737978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_send_buf_region(idm_task_t *idt, uint8_t opcode,
747978SPeter.Dunlap@Sun.COM     idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length);
757978SPeter.Dunlap@Sun.COM 
767978SPeter.Dunlap@Sun.COM static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb,
777978SPeter.Dunlap@Sun.COM     uint32_t ro, uint32_t dlength);
787978SPeter.Dunlap@Sun.COM 
797978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_handle_digest(idm_conn_t *it,
807978SPeter.Dunlap@Sun.COM     nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx);
817978SPeter.Dunlap@Sun.COM 
827978SPeter.Dunlap@Sun.COM /*
837978SPeter.Dunlap@Sun.COM  * Transport ops prototypes
847978SPeter.Dunlap@Sun.COM  */
857978SPeter.Dunlap@Sun.COM static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu);
867978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
877978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
887978SPeter.Dunlap@Sun.COM static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu);
897978SPeter.Dunlap@Sun.COM static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu);
907978SPeter.Dunlap@Sun.COM static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu);
917978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt);
927978SPeter.Dunlap@Sun.COM static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it,
937978SPeter.Dunlap@Sun.COM     nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
947978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_notice_key_values(idm_conn_t *it,
957978SPeter.Dunlap@Sun.COM     nvlist_t *negotiated_nvl);
967978SPeter.Dunlap@Sun.COM static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic,
977978SPeter.Dunlap@Sun.COM     idm_transport_caps_t *caps);
987978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen);
997978SPeter.Dunlap@Sun.COM static void idm_so_buf_free(idm_buf_t *idb);
1007978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_setup(idm_buf_t *idb);
1017978SPeter.Dunlap@Sun.COM static void idm_so_buf_teardown(idm_buf_t *idb);
1027978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is);
1037978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_destroy(idm_svc_t *is);
1047978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is);
1057978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_offline(idm_svc_t *is);
1067978SPeter.Dunlap@Sun.COM static void idm_so_tgt_conn_destroy(idm_conn_t *ic);
1077978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic);
1087978SPeter.Dunlap@Sun.COM static void idm_so_conn_disconnect(idm_conn_t *ic);
1097978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic);
1107978SPeter.Dunlap@Sun.COM static void idm_so_ini_conn_destroy(idm_conn_t *ic);
1117978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic);
1127978SPeter.Dunlap@Sun.COM 
1137978SPeter.Dunlap@Sun.COM /*
1147978SPeter.Dunlap@Sun.COM  * IDM Native Sockets transport operations
1157978SPeter.Dunlap@Sun.COM  */
1167978SPeter.Dunlap@Sun.COM static
1177978SPeter.Dunlap@Sun.COM idm_transport_ops_t idm_so_transport_ops = {
1187978SPeter.Dunlap@Sun.COM 	idm_so_tx,			/* it_tx_pdu */
1197978SPeter.Dunlap@Sun.COM 	idm_so_buf_tx_to_ini,		/* it_buf_tx_to_ini */
1207978SPeter.Dunlap@Sun.COM 	idm_so_buf_rx_from_ini,		/* it_buf_rx_from_ini */
1217978SPeter.Dunlap@Sun.COM 	idm_so_rx_datain,		/* it_rx_datain */
1227978SPeter.Dunlap@Sun.COM 	idm_so_rx_rtt,			/* it_rx_rtt */
1237978SPeter.Dunlap@Sun.COM 	idm_so_rx_dataout,		/* it_rx_dataout */
1247978SPeter.Dunlap@Sun.COM 	NULL,				/* it_alloc_conn_rsrc */
1257978SPeter.Dunlap@Sun.COM 	NULL,				/* it_free_conn_rsrc */
1267978SPeter.Dunlap@Sun.COM 	NULL,				/* it_tgt_enable_datamover */
1277978SPeter.Dunlap@Sun.COM 	NULL,				/* it_ini_enable_datamover */
1287978SPeter.Dunlap@Sun.COM 	NULL,				/* it_conn_terminate */
1297978SPeter.Dunlap@Sun.COM 	idm_so_free_task_rsrc,		/* it_free_task_rsrc */
1307978SPeter.Dunlap@Sun.COM 	idm_so_negotiate_key_values,	/* it_negotiate_key_values */
1317978SPeter.Dunlap@Sun.COM 	idm_so_notice_key_values,	/* it_notice_key_values */
1327978SPeter.Dunlap@Sun.COM 	idm_so_conn_is_capable,		/* it_conn_is_capable */
1337978SPeter.Dunlap@Sun.COM 	idm_so_buf_alloc,		/* it_buf_alloc */
1347978SPeter.Dunlap@Sun.COM 	idm_so_buf_free,		/* it_buf_free */
1357978SPeter.Dunlap@Sun.COM 	idm_so_buf_setup,		/* it_buf_setup */
1367978SPeter.Dunlap@Sun.COM 	idm_so_buf_teardown,		/* it_buf_teardown */
1377978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_create,		/* it_tgt_svc_create */
1387978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_destroy,		/* it_tgt_svc_destroy */
1397978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_online,		/* it_tgt_svc_online */
1407978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_offline,		/* it_tgt_svc_offline */
1417978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_destroy,	/* it_tgt_conn_destroy */
1427978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_connect,	/* it_tgt_conn_connect */
1437978SPeter.Dunlap@Sun.COM 	idm_so_conn_disconnect,		/* it_tgt_conn_disconnect */
1447978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_create,		/* it_ini_conn_create */
1457978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_destroy,	/* it_ini_conn_destroy */
1467978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_connect,	/* it_ini_conn_connect */
1477978SPeter.Dunlap@Sun.COM 	idm_so_conn_disconnect		/* it_ini_conn_disconnect */
1487978SPeter.Dunlap@Sun.COM };
1497978SPeter.Dunlap@Sun.COM 
1507978SPeter.Dunlap@Sun.COM /*
1517978SPeter.Dunlap@Sun.COM  * idm_so_init()
1527978SPeter.Dunlap@Sun.COM  * Sockets transport initialization
1537978SPeter.Dunlap@Sun.COM  */
1547978SPeter.Dunlap@Sun.COM void
1557978SPeter.Dunlap@Sun.COM idm_so_init(idm_transport_t *it)
1567978SPeter.Dunlap@Sun.COM {
1577978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Data and R2T Transmit PDU's */
1587978SPeter.Dunlap@Sun.COM 	idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache",
1597978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
1607978SPeter.Dunlap@Sun.COM 	    &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1617978SPeter.Dunlap@Sun.COM 
1627978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Receive PDU's */
1637978SPeter.Dunlap@Sun.COM 	idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache",
1647978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
1657978SPeter.Dunlap@Sun.COM 	    &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1667978SPeter.Dunlap@Sun.COM 
1677978SPeter.Dunlap@Sun.COM 	/* Set the sockets transport ops */
1687978SPeter.Dunlap@Sun.COM 	it->it_ops = &idm_so_transport_ops;
1697978SPeter.Dunlap@Sun.COM }
1707978SPeter.Dunlap@Sun.COM 
1717978SPeter.Dunlap@Sun.COM /*
1727978SPeter.Dunlap@Sun.COM  * idm_so_fini()
1737978SPeter.Dunlap@Sun.COM  * Sockets transport teardown
1747978SPeter.Dunlap@Sun.COM  */
1757978SPeter.Dunlap@Sun.COM void
1767978SPeter.Dunlap@Sun.COM idm_so_fini(void)
1777978SPeter.Dunlap@Sun.COM {
1787978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sotx_pdu_cache);
1797978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sorx_pdu_cache);
1807978SPeter.Dunlap@Sun.COM }
1817978SPeter.Dunlap@Sun.COM 
1828348SEric.Yu@Sun.COM ksocket_t
1837978SPeter.Dunlap@Sun.COM idm_socreate(int domain, int type, int protocol)
1847978SPeter.Dunlap@Sun.COM {
1858348SEric.Yu@Sun.COM 	ksocket_t ks;
1867978SPeter.Dunlap@Sun.COM 
1878348SEric.Yu@Sun.COM 	if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP,
1888348SEric.Yu@Sun.COM 	    CRED())) {
1898348SEric.Yu@Sun.COM 		return (ks);
1908348SEric.Yu@Sun.COM 	} else {
1918348SEric.Yu@Sun.COM 		return (NULL);
1927978SPeter.Dunlap@Sun.COM 	}
1937978SPeter.Dunlap@Sun.COM }
1947978SPeter.Dunlap@Sun.COM 
1957978SPeter.Dunlap@Sun.COM /*
1967978SPeter.Dunlap@Sun.COM  * idm_soshutdown will disconnect the socket and prevent subsequent PDU
1977978SPeter.Dunlap@Sun.COM  * reception and transmission.  The sonode still exists but its state
1987978SPeter.Dunlap@Sun.COM  * gets modified to indicate it is no longer connected.  Calls to
1997978SPeter.Dunlap@Sun.COM  * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used
2007978SPeter.Dunlap@Sun.COM  * regain control of a thread stuck in idm_sorecv.
2017978SPeter.Dunlap@Sun.COM  */
2027978SPeter.Dunlap@Sun.COM void
2038348SEric.Yu@Sun.COM idm_soshutdown(ksocket_t so)
2047978SPeter.Dunlap@Sun.COM {
2058348SEric.Yu@Sun.COM 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
2067978SPeter.Dunlap@Sun.COM }
2077978SPeter.Dunlap@Sun.COM 
2087978SPeter.Dunlap@Sun.COM /*
2097978SPeter.Dunlap@Sun.COM  * idm_sodestroy releases all resources associated with a socket previously
2107978SPeter.Dunlap@Sun.COM  * created with idm_socreate.  The socket must be shutdown using
2117978SPeter.Dunlap@Sun.COM  * idm_soshutdown before the socket is destroyed with idm_sodestroy,
2127978SPeter.Dunlap@Sun.COM  * otherwise undefined behavior will result.
2137978SPeter.Dunlap@Sun.COM  */
2147978SPeter.Dunlap@Sun.COM void
2158348SEric.Yu@Sun.COM idm_sodestroy(ksocket_t ks)
2167978SPeter.Dunlap@Sun.COM {
2178348SEric.Yu@Sun.COM 	(void) ksocket_close(ks, CRED());
2187978SPeter.Dunlap@Sun.COM }
2197978SPeter.Dunlap@Sun.COM 
2207978SPeter.Dunlap@Sun.COM /*
2217978SPeter.Dunlap@Sun.COM  * IP address filter functions to flag addresses that should not
2227978SPeter.Dunlap@Sun.COM  * go out to initiators through discovery.
2237978SPeter.Dunlap@Sun.COM  */
2247978SPeter.Dunlap@Sun.COM static boolean_t
2257978SPeter.Dunlap@Sun.COM idm_v4_addr_okay(struct in_addr *in_addr)
2267978SPeter.Dunlap@Sun.COM {
2277978SPeter.Dunlap@Sun.COM 	in_addr_t addr = ntohl(in_addr->s_addr);
2287978SPeter.Dunlap@Sun.COM 
2297978SPeter.Dunlap@Sun.COM 	if ((INADDR_NONE == addr) ||
2307978SPeter.Dunlap@Sun.COM 	    (IN_MULTICAST(addr)) ||
2317978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == 0) ||
2327978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
2337978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
2347978SPeter.Dunlap@Sun.COM 	}
2357978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
2367978SPeter.Dunlap@Sun.COM }
2377978SPeter.Dunlap@Sun.COM 
2387978SPeter.Dunlap@Sun.COM static boolean_t
2397978SPeter.Dunlap@Sun.COM idm_v6_addr_okay(struct in6_addr *addr6)
2407978SPeter.Dunlap@Sun.COM {
2417978SPeter.Dunlap@Sun.COM 
2427978SPeter.Dunlap@Sun.COM 	if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) ||
2437978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LOOPBACK(addr6)) ||
2447978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_MULTICAST(addr6)) ||
2457978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4MAPPED(addr6)) ||
2467978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4COMPAT(addr6)) ||
2477978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LINKLOCAL(addr6))) {
2487978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
2497978SPeter.Dunlap@Sun.COM 	}
2507978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
2517978SPeter.Dunlap@Sun.COM }
2527978SPeter.Dunlap@Sun.COM 
2537978SPeter.Dunlap@Sun.COM /*
2547978SPeter.Dunlap@Sun.COM  * idm_get_ipaddr will retrieve a list of IP Addresses which the host is
2557978SPeter.Dunlap@Sun.COM  * configured with by sending down a sequence of kernel ioctl to IP STREAMS.
2567978SPeter.Dunlap@Sun.COM  */
2577978SPeter.Dunlap@Sun.COM int
2587978SPeter.Dunlap@Sun.COM idm_get_ipaddr(idm_addr_list_t **ipaddr_p)
2597978SPeter.Dunlap@Sun.COM {
2608348SEric.Yu@Sun.COM 	ksocket_t 		so4, so6;
2617978SPeter.Dunlap@Sun.COM 	struct lifnum		lifn;
2627978SPeter.Dunlap@Sun.COM 	struct lifconf		lifc;
2637978SPeter.Dunlap@Sun.COM 	struct lifreq		*lp;
2647978SPeter.Dunlap@Sun.COM 	int			rval;
2657978SPeter.Dunlap@Sun.COM 	int			numifs;
2667978SPeter.Dunlap@Sun.COM 	int			bufsize;
2677978SPeter.Dunlap@Sun.COM 	void			*buf;
2687978SPeter.Dunlap@Sun.COM 	int			i, j, n, rc;
2697978SPeter.Dunlap@Sun.COM 	struct sockaddr_storage	ss;
2707978SPeter.Dunlap@Sun.COM 	struct sockaddr_in	*sin;
2717978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	*sin6;
2727978SPeter.Dunlap@Sun.COM 	idm_addr_t		*ip;
2737978SPeter.Dunlap@Sun.COM 	idm_addr_list_t		*ipaddr;
2747978SPeter.Dunlap@Sun.COM 	int			size_ipaddr;
2757978SPeter.Dunlap@Sun.COM 
2767978SPeter.Dunlap@Sun.COM 	*ipaddr_p = NULL;
2777978SPeter.Dunlap@Sun.COM 	size_ipaddr = 0;
2787978SPeter.Dunlap@Sun.COM 	buf = NULL;
2797978SPeter.Dunlap@Sun.COM 
2807978SPeter.Dunlap@Sun.COM 	/* create an ipv4 and ipv6 UDP socket */
2817978SPeter.Dunlap@Sun.COM 	if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL)
2827978SPeter.Dunlap@Sun.COM 		return (0);
2837978SPeter.Dunlap@Sun.COM 	if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) {
2847978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so6);
2857978SPeter.Dunlap@Sun.COM 		return (0);
2867978SPeter.Dunlap@Sun.COM 	}
2877978SPeter.Dunlap@Sun.COM 
2887978SPeter.Dunlap@Sun.COM 
2897978SPeter.Dunlap@Sun.COM retry_count:
2907978SPeter.Dunlap@Sun.COM 	/* snapshot the current number of interfaces */
2917978SPeter.Dunlap@Sun.COM 	lifn.lifn_family = PF_UNSPEC;
2927978SPeter.Dunlap@Sun.COM 	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
2937978SPeter.Dunlap@Sun.COM 	lifn.lifn_count = 0;
2948348SEric.Yu@Sun.COM 	/* use vp6 for ioctls with unspecified families by default */
2958348SEric.Yu@Sun.COM 	if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED())
2968348SEric.Yu@Sun.COM 	    != 0) {
2977978SPeter.Dunlap@Sun.COM 		goto cleanup;
2987978SPeter.Dunlap@Sun.COM 	}
2997978SPeter.Dunlap@Sun.COM 
3007978SPeter.Dunlap@Sun.COM 	numifs = lifn.lifn_count;
3017978SPeter.Dunlap@Sun.COM 	if (numifs <= 0) {
3027978SPeter.Dunlap@Sun.COM 		goto cleanup;
3037978SPeter.Dunlap@Sun.COM 	}
3047978SPeter.Dunlap@Sun.COM 
3057978SPeter.Dunlap@Sun.COM 	/* allocate extra room in case more interfaces appear */
3067978SPeter.Dunlap@Sun.COM 	numifs += 10;
3077978SPeter.Dunlap@Sun.COM 
3087978SPeter.Dunlap@Sun.COM 	/* get the interface names and ip addresses */
3097978SPeter.Dunlap@Sun.COM 	bufsize = numifs * sizeof (struct lifreq);
3107978SPeter.Dunlap@Sun.COM 	buf = kmem_alloc(bufsize, KM_SLEEP);
3117978SPeter.Dunlap@Sun.COM 
3127978SPeter.Dunlap@Sun.COM 	lifc.lifc_family = AF_UNSPEC;
3137978SPeter.Dunlap@Sun.COM 	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
3147978SPeter.Dunlap@Sun.COM 	lifc.lifc_len = bufsize;
3157978SPeter.Dunlap@Sun.COM 	lifc.lifc_buf = buf;
3168348SEric.Yu@Sun.COM 	rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED());
3177978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
3187978SPeter.Dunlap@Sun.COM 		goto cleanup;
3197978SPeter.Dunlap@Sun.COM 	}
3207978SPeter.Dunlap@Sun.COM 	/* if our extra room is used up, try again */
3217978SPeter.Dunlap@Sun.COM 	if (bufsize <= lifc.lifc_len) {
3227978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
3237978SPeter.Dunlap@Sun.COM 		buf = NULL;
3247978SPeter.Dunlap@Sun.COM 		goto retry_count;
3257978SPeter.Dunlap@Sun.COM 	}
3267978SPeter.Dunlap@Sun.COM 	/* calc actual number of ifconfs */
3277978SPeter.Dunlap@Sun.COM 	n = lifc.lifc_len / sizeof (struct lifreq);
3287978SPeter.Dunlap@Sun.COM 
3297978SPeter.Dunlap@Sun.COM 	/* get ip address */
3307978SPeter.Dunlap@Sun.COM 	if (n > 0) {
3317978SPeter.Dunlap@Sun.COM 		size_ipaddr = sizeof (idm_addr_list_t) +
3327978SPeter.Dunlap@Sun.COM 		    (n - 1) * sizeof (idm_addr_t);
3337978SPeter.Dunlap@Sun.COM 		ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP);
3347978SPeter.Dunlap@Sun.COM 	} else {
3357978SPeter.Dunlap@Sun.COM 		goto cleanup;
3367978SPeter.Dunlap@Sun.COM 	}
3377978SPeter.Dunlap@Sun.COM 
3387978SPeter.Dunlap@Sun.COM 	/*
3397978SPeter.Dunlap@Sun.COM 	 * Examine the array of interfaces and filter uninteresting ones
3407978SPeter.Dunlap@Sun.COM 	 */
3417978SPeter.Dunlap@Sun.COM 	for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) {
3427978SPeter.Dunlap@Sun.COM 
3437978SPeter.Dunlap@Sun.COM 		/*
3447978SPeter.Dunlap@Sun.COM 		 * Copy the address as the SIOCGLIFFLAGS ioctl is destructive
3457978SPeter.Dunlap@Sun.COM 		 */
3467978SPeter.Dunlap@Sun.COM 		ss = lp->lifr_addr;
3477978SPeter.Dunlap@Sun.COM 		/*
3487978SPeter.Dunlap@Sun.COM 		 * fetch the flags using the socket of the correct family
3497978SPeter.Dunlap@Sun.COM 		 */
3507978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
3517978SPeter.Dunlap@Sun.COM 		case AF_INET:
3528348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp,
3538348SEric.Yu@Sun.COM 			    &rval, CRED());
3547978SPeter.Dunlap@Sun.COM 			break;
3557978SPeter.Dunlap@Sun.COM 		case AF_INET6:
3568348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp,
3578348SEric.Yu@Sun.COM 			    &rval, CRED());
3587978SPeter.Dunlap@Sun.COM 			break;
3597978SPeter.Dunlap@Sun.COM 		default:
3607978SPeter.Dunlap@Sun.COM 			continue;
3617978SPeter.Dunlap@Sun.COM 		}
3627978SPeter.Dunlap@Sun.COM 		if (rc == 0) {
3637978SPeter.Dunlap@Sun.COM 			/*
3647978SPeter.Dunlap@Sun.COM 			 * If we got the flags, skip uninteresting
3657978SPeter.Dunlap@Sun.COM 			 * interfaces based on flags
3667978SPeter.Dunlap@Sun.COM 			 */
3677978SPeter.Dunlap@Sun.COM 			if ((lp->lifr_flags & IFF_UP) != IFF_UP)
3687978SPeter.Dunlap@Sun.COM 				continue;
3697978SPeter.Dunlap@Sun.COM 			if (lp->lifr_flags &
3707978SPeter.Dunlap@Sun.COM 			    (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED))
3717978SPeter.Dunlap@Sun.COM 				continue;
3727978SPeter.Dunlap@Sun.COM 		}
3737978SPeter.Dunlap@Sun.COM 
3747978SPeter.Dunlap@Sun.COM 		/* save ip address */
3757978SPeter.Dunlap@Sun.COM 		ip = &ipaddr->al_addrs[j];
3767978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
3777978SPeter.Dunlap@Sun.COM 		case AF_INET:
3787978SPeter.Dunlap@Sun.COM 			sin = (struct sockaddr_in *)&ss;
3797978SPeter.Dunlap@Sun.COM 			if (!idm_v4_addr_okay(&sin->sin_addr))
3807978SPeter.Dunlap@Sun.COM 				continue;
3817978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in4 = sin->sin_addr;
3827978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in_addr);
3837978SPeter.Dunlap@Sun.COM 			break;
3847978SPeter.Dunlap@Sun.COM 		case AF_INET6:
3857978SPeter.Dunlap@Sun.COM 			sin6 = (struct sockaddr_in6 *)&ss;
3867978SPeter.Dunlap@Sun.COM 			if (!idm_v6_addr_okay(&sin6->sin6_addr))
3877978SPeter.Dunlap@Sun.COM 				continue;
3887978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in6 = sin6->sin6_addr;
3897978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in6_addr);
3907978SPeter.Dunlap@Sun.COM 			break;
3917978SPeter.Dunlap@Sun.COM 		default:
3927978SPeter.Dunlap@Sun.COM 			continue;
3937978SPeter.Dunlap@Sun.COM 		}
3947978SPeter.Dunlap@Sun.COM 		j++;
3957978SPeter.Dunlap@Sun.COM 	}
3967978SPeter.Dunlap@Sun.COM 
3977978SPeter.Dunlap@Sun.COM 	if (j == 0) {
3987978SPeter.Dunlap@Sun.COM 		/* no valid ifaddr */
3997978SPeter.Dunlap@Sun.COM 		kmem_free(ipaddr, size_ipaddr);
4007978SPeter.Dunlap@Sun.COM 		size_ipaddr = 0;
4017978SPeter.Dunlap@Sun.COM 		ipaddr = NULL;
4027978SPeter.Dunlap@Sun.COM 	} else {
4037978SPeter.Dunlap@Sun.COM 		ipaddr->al_out_cnt = j;
4047978SPeter.Dunlap@Sun.COM 	}
4057978SPeter.Dunlap@Sun.COM 
4067978SPeter.Dunlap@Sun.COM 
4077978SPeter.Dunlap@Sun.COM cleanup:
4087978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so6);
4097978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so4);
4107978SPeter.Dunlap@Sun.COM 
4117978SPeter.Dunlap@Sun.COM 	if (buf != NULL)
4127978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
4137978SPeter.Dunlap@Sun.COM 
4147978SPeter.Dunlap@Sun.COM 	*ipaddr_p = ipaddr;
4157978SPeter.Dunlap@Sun.COM 	return (size_ipaddr);
4167978SPeter.Dunlap@Sun.COM }
4177978SPeter.Dunlap@Sun.COM 
4187978SPeter.Dunlap@Sun.COM int
4198348SEric.Yu@Sun.COM idm_sorecv(ksocket_t so, void *msg, size_t len)
4207978SPeter.Dunlap@Sun.COM {
4217978SPeter.Dunlap@Sun.COM 	iovec_t iov;
4227978SPeter.Dunlap@Sun.COM 
4237978SPeter.Dunlap@Sun.COM 	ASSERT(so != NULL);
4247978SPeter.Dunlap@Sun.COM 	ASSERT(len != 0);
4257978SPeter.Dunlap@Sun.COM 
4267978SPeter.Dunlap@Sun.COM 	/*
4277978SPeter.Dunlap@Sun.COM 	 * Fill in iovec and receive data
4287978SPeter.Dunlap@Sun.COM 	 */
4297978SPeter.Dunlap@Sun.COM 	iov.iov_base = msg;
4307978SPeter.Dunlap@Sun.COM 	iov.iov_len = len;
4317978SPeter.Dunlap@Sun.COM 
4327978SPeter.Dunlap@Sun.COM 	return (idm_iov_sorecv(so, &iov, 1, len));
4337978SPeter.Dunlap@Sun.COM }
4347978SPeter.Dunlap@Sun.COM 
4357978SPeter.Dunlap@Sun.COM /*
4367978SPeter.Dunlap@Sun.COM  * idm_sosendto - Sends a buffered data on a non-connected socket.
4377978SPeter.Dunlap@Sun.COM  *
4387978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
4397978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
4407978SPeter.Dunlap@Sun.COM  * occurs.
4417978SPeter.Dunlap@Sun.COM  *
4427978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
4437978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
4447978SPeter.Dunlap@Sun.COM  */
4457978SPeter.Dunlap@Sun.COM int
4468348SEric.Yu@Sun.COM idm_sosendto(ksocket_t so, void *buff, size_t len,
4477978SPeter.Dunlap@Sun.COM     struct sockaddr *name, socklen_t namelen)
4487978SPeter.Dunlap@Sun.COM {
4497978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
4507978SPeter.Dunlap@Sun.COM 	struct iovec		iov[1];
4517978SPeter.Dunlap@Sun.COM 	int			error;
4528348SEric.Yu@Sun.COM 	size_t			sent = 0;
4537978SPeter.Dunlap@Sun.COM 
4547978SPeter.Dunlap@Sun.COM 	iov[0].iov_base	= buff;
4557978SPeter.Dunlap@Sun.COM 	iov[0].iov_len	= len;
4567978SPeter.Dunlap@Sun.COM 
4577978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
4587978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
4597978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iov;
4607978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= 1;
4617978SPeter.Dunlap@Sun.COM 	msg.msg_name	= name;
4627978SPeter.Dunlap@Sun.COM 	msg.msg_namelen	= namelen;
4637978SPeter.Dunlap@Sun.COM 
4648348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) {
4657978SPeter.Dunlap@Sun.COM 		/* Data sent */
4668348SEric.Yu@Sun.COM 		if (sent == len) {
4677978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
4687978SPeter.Dunlap@Sun.COM 			return (0);
4697978SPeter.Dunlap@Sun.COM 		} else {
4707978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
4717978SPeter.Dunlap@Sun.COM 			return (-1);
4727978SPeter.Dunlap@Sun.COM 		}
4737978SPeter.Dunlap@Sun.COM 	}
4747978SPeter.Dunlap@Sun.COM 
4757978SPeter.Dunlap@Sun.COM 	/* Send failed */
4767978SPeter.Dunlap@Sun.COM 	return (error);
4777978SPeter.Dunlap@Sun.COM }
4787978SPeter.Dunlap@Sun.COM 
4797978SPeter.Dunlap@Sun.COM /*
4807978SPeter.Dunlap@Sun.COM  * idm_iov_sosend - Sends an iovec on a connection.
4817978SPeter.Dunlap@Sun.COM  *
4827978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
4837978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
4847978SPeter.Dunlap@Sun.COM  * occurs.
4857978SPeter.Dunlap@Sun.COM  *
4867978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
4877978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
4887978SPeter.Dunlap@Sun.COM  */
4897978SPeter.Dunlap@Sun.COM int
4908348SEric.Yu@Sun.COM idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
4917978SPeter.Dunlap@Sun.COM {
4927978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
4937978SPeter.Dunlap@Sun.COM 	int			error;
4948348SEric.Yu@Sun.COM 	size_t 			sent = 0;
4957978SPeter.Dunlap@Sun.COM 
4967978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
4977978SPeter.Dunlap@Sun.COM 
4987978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
4997978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
5007978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
5017978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
5027978SPeter.Dunlap@Sun.COM 
5038348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED()))
5048348SEric.Yu@Sun.COM 	    == 0) {
5057978SPeter.Dunlap@Sun.COM 		/* Data sent */
5068348SEric.Yu@Sun.COM 		if (sent == total_len) {
5077978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
5087978SPeter.Dunlap@Sun.COM 			return (0);
5097978SPeter.Dunlap@Sun.COM 		} else {
5107978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
5117978SPeter.Dunlap@Sun.COM 			return (-1);
5127978SPeter.Dunlap@Sun.COM 		}
5137978SPeter.Dunlap@Sun.COM 	}
5147978SPeter.Dunlap@Sun.COM 
5157978SPeter.Dunlap@Sun.COM 	/* Send failed */
5167978SPeter.Dunlap@Sun.COM 	return (error);
5177978SPeter.Dunlap@Sun.COM }
5187978SPeter.Dunlap@Sun.COM 
5197978SPeter.Dunlap@Sun.COM /*
5207978SPeter.Dunlap@Sun.COM  * idm_iov_sorecv - Receives an iovec from a connection
5217978SPeter.Dunlap@Sun.COM  *
5227978SPeter.Dunlap@Sun.COM  * This function gets the data asked for from the socket.  It will return
5237978SPeter.Dunlap@Sun.COM  * only when all the requested data has been retrieved or if an error
5247978SPeter.Dunlap@Sun.COM  * occurs.
5257978SPeter.Dunlap@Sun.COM  *
5267978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
5277978SPeter.Dunlap@Sun.COM  * -1 if sorecvmsg returns success but uio_resid != 0
5287978SPeter.Dunlap@Sun.COM  */
5297978SPeter.Dunlap@Sun.COM int
5308348SEric.Yu@Sun.COM idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
5317978SPeter.Dunlap@Sun.COM {
5327978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
5337978SPeter.Dunlap@Sun.COM 	int			error;
5348348SEric.Yu@Sun.COM 	size_t			recv;
5358348SEric.Yu@Sun.COM 	int 			flags;
5367978SPeter.Dunlap@Sun.COM 
5377978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
5387978SPeter.Dunlap@Sun.COM 
5397978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
5407978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
5417978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
5427978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
5438348SEric.Yu@Sun.COM 	flags		= MSG_WAITALL;
5447978SPeter.Dunlap@Sun.COM 
5458348SEric.Yu@Sun.COM 	if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED()))
5468348SEric.Yu@Sun.COM 	    == 0) {
5477978SPeter.Dunlap@Sun.COM 		/* Received data */
5488348SEric.Yu@Sun.COM 		if (recv == total_len) {
5497978SPeter.Dunlap@Sun.COM 			/* All requested data received.  Success */
5507978SPeter.Dunlap@Sun.COM 			return (0);
5517978SPeter.Dunlap@Sun.COM 		} else {
5527978SPeter.Dunlap@Sun.COM 			/*
5537978SPeter.Dunlap@Sun.COM 			 * Not all data was received.  The connection has
5547978SPeter.Dunlap@Sun.COM 			 * probably failed.
5557978SPeter.Dunlap@Sun.COM 			 */
5567978SPeter.Dunlap@Sun.COM 			return (-1);
5577978SPeter.Dunlap@Sun.COM 		}
5587978SPeter.Dunlap@Sun.COM 	}
5597978SPeter.Dunlap@Sun.COM 
5607978SPeter.Dunlap@Sun.COM 	/* Receive failed */
5617978SPeter.Dunlap@Sun.COM 	return (error);
5627978SPeter.Dunlap@Sun.COM }
5637978SPeter.Dunlap@Sun.COM 
5647978SPeter.Dunlap@Sun.COM static void
5657978SPeter.Dunlap@Sun.COM idm_set_ini_preconnect_options(idm_so_conn_t *sc)
5667978SPeter.Dunlap@Sun.COM {
5677978SPeter.Dunlap@Sun.COM 	int	conn_abort = 10000;
5687978SPeter.Dunlap@Sun.COM 	int	conn_notify = 2000;
5697978SPeter.Dunlap@Sun.COM 	int	abort = 30000;
5707978SPeter.Dunlap@Sun.COM 
5717978SPeter.Dunlap@Sun.COM 	/* Pre-connect socket options */
5728348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5738348SEric.Yu@Sun.COM 	    TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int),
5748348SEric.Yu@Sun.COM 	    CRED());
5758348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
5768348SEric.Yu@Sun.COM 	    TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int),
5778348SEric.Yu@Sun.COM 	    CRED());
5788348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
5798348SEric.Yu@Sun.COM 	    (char *)&abort, sizeof (int), CRED());
5807978SPeter.Dunlap@Sun.COM }
5817978SPeter.Dunlap@Sun.COM 
5827978SPeter.Dunlap@Sun.COM static void
5837978SPeter.Dunlap@Sun.COM idm_set_ini_postconnect_options(idm_so_conn_t *sc)
5847978SPeter.Dunlap@Sun.COM {
5857978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
5867978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
5877978SPeter.Dunlap@Sun.COM 	const int	on = 1;
5887978SPeter.Dunlap@Sun.COM 
5897978SPeter.Dunlap@Sun.COM 	/* Set postconnect options */
5908348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY,
5918348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (int), CRED());
5928348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF,
5938348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
5948348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF,
5958348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
5967978SPeter.Dunlap@Sun.COM }
5977978SPeter.Dunlap@Sun.COM 
5987978SPeter.Dunlap@Sun.COM static void
5998348SEric.Yu@Sun.COM idm_set_tgt_connect_options(ksocket_t ks)
6007978SPeter.Dunlap@Sun.COM {
6017978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
6027978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
6037978SPeter.Dunlap@Sun.COM 	const int	on = 1;
6047978SPeter.Dunlap@Sun.COM 
6057978SPeter.Dunlap@Sun.COM 	/* Set connect options */
6068348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF,
6078348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
6088348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF,
6098348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
6108348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY,
6118348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (on), CRED());
6127978SPeter.Dunlap@Sun.COM }
6137978SPeter.Dunlap@Sun.COM 
6147978SPeter.Dunlap@Sun.COM static uint32_t
6157978SPeter.Dunlap@Sun.COM n2h24(const uchar_t *ptr)
6167978SPeter.Dunlap@Sun.COM {
6177978SPeter.Dunlap@Sun.COM 	return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
6187978SPeter.Dunlap@Sun.COM }
6197978SPeter.Dunlap@Sun.COM 
6207978SPeter.Dunlap@Sun.COM 
6217978SPeter.Dunlap@Sun.COM static idm_status_t
6227978SPeter.Dunlap@Sun.COM idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu)
6237978SPeter.Dunlap@Sun.COM {
6247978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t	*bhs;
6257978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
6267978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
6277978SPeter.Dunlap@Sun.COM 	void		*new_hdr;
6287978SPeter.Dunlap@Sun.COM 	int		ahslen = 0;
6297978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
6307978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
6317978SPeter.Dunlap@Sun.COM 	struct iovec	iov[2];
6327978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
6337978SPeter.Dunlap@Sun.COM 	int		rc;
6347978SPeter.Dunlap@Sun.COM 
6357978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
6367978SPeter.Dunlap@Sun.COM 
6377978SPeter.Dunlap@Sun.COM 	/*
6387978SPeter.Dunlap@Sun.COM 	 * Read BHS
6397978SPeter.Dunlap@Sun.COM 	 */
6407978SPeter.Dunlap@Sun.COM 	bhs = pdu->isp_hdr;
6417978SPeter.Dunlap@Sun.COM 	rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t));
6427978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
6437978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
6447978SPeter.Dunlap@Sun.COM 	}
6457978SPeter.Dunlap@Sun.COM 
6467978SPeter.Dunlap@Sun.COM 	/*
6477978SPeter.Dunlap@Sun.COM 	 * Check actual AHS length against the amount available in the buffer
6487978SPeter.Dunlap@Sun.COM 	 */
6497978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
6507978SPeter.Dunlap@Sun.COM 	    (bhs->hlength * sizeof (uint32_t));
6517978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = n2h24(bhs->dlength);
6527978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) {
6537978SPeter.Dunlap@Sun.COM 		/* Allocate a new header segment and change the callback */
6547978SPeter.Dunlap@Sun.COM 		new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP);
6557978SPeter.Dunlap@Sun.COM 		bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t));
6567978SPeter.Dunlap@Sun.COM 		pdu->isp_hdr = new_hdr;
6577978SPeter.Dunlap@Sun.COM 		pdu->isp_flags |= IDM_PDU_ADDL_HDR;
6587978SPeter.Dunlap@Sun.COM 
6597978SPeter.Dunlap@Sun.COM 		/*
6607978SPeter.Dunlap@Sun.COM 		 * This callback will restore the expected values after
6617978SPeter.Dunlap@Sun.COM 		 * the RX PDU has been processed.
6627978SPeter.Dunlap@Sun.COM 		 */
6637978SPeter.Dunlap@Sun.COM 		pdu->isp_callback = idm_sorx_addl_pdu_cb;
6647978SPeter.Dunlap@Sun.COM 	}
6657978SPeter.Dunlap@Sun.COM 
6667978SPeter.Dunlap@Sun.COM 	/*
6677978SPeter.Dunlap@Sun.COM 	 * Setup receipt of additional header and header digest (if enabled).
6687978SPeter.Dunlap@Sun.COM 	 */
6697978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > 0) {
6707978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1);
6717978SPeter.Dunlap@Sun.COM 		ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t);
6727978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = ahslen;
6737978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
6747978SPeter.Dunlap@Sun.COM 		iovlen++;
6757978SPeter.Dunlap@Sun.COM 	}
6767978SPeter.Dunlap@Sun.COM 
6777978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
6787978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc;
6797978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = sizeof (hdr_digest_crc);
6807978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
6817978SPeter.Dunlap@Sun.COM 		iovlen++;
6827978SPeter.Dunlap@Sun.COM 	}
6837978SPeter.Dunlap@Sun.COM 
6847978SPeter.Dunlap@Sun.COM 	if ((iovlen != 0) &&
6857978SPeter.Dunlap@Sun.COM 	    (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen,
6867978SPeter.Dunlap@Sun.COM 	    total_len) != 0)) {
6877978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
6887978SPeter.Dunlap@Sun.COM 	}
6897978SPeter.Dunlap@Sun.COM 
6907978SPeter.Dunlap@Sun.COM 	/*
6917978SPeter.Dunlap@Sun.COM 	 * Validate header digest if enabled
6927978SPeter.Dunlap@Sun.COM 	 */
6937978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
6947978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_hdr,
6957978SPeter.Dunlap@Sun.COM 		    sizeof (iscsi_hdr_t) + ahslen);
6967978SPeter.Dunlap@Sun.COM 		if (crc_calculated != hdr_digest_crc) {
6977978SPeter.Dunlap@Sun.COM 			/* Invalid Header Digest */
6987978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_HEADER_DIGEST);
6997978SPeter.Dunlap@Sun.COM 		}
7007978SPeter.Dunlap@Sun.COM 	}
7017978SPeter.Dunlap@Sun.COM 
7027978SPeter.Dunlap@Sun.COM 	return (0);
7037978SPeter.Dunlap@Sun.COM }
7047978SPeter.Dunlap@Sun.COM 
7057978SPeter.Dunlap@Sun.COM /*
7067978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create()
7077978SPeter.Dunlap@Sun.COM  * Allocate the sockets transport connection resources.
7087978SPeter.Dunlap@Sun.COM  */
7097978SPeter.Dunlap@Sun.COM static idm_status_t
7107978SPeter.Dunlap@Sun.COM idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic)
7117978SPeter.Dunlap@Sun.COM {
7128348SEric.Yu@Sun.COM 	ksocket_t	so;
7137978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
7147978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
7157978SPeter.Dunlap@Sun.COM 
7167978SPeter.Dunlap@Sun.COM 	so = idm_socreate(cr->cr_domain, cr->cr_type,
7177978SPeter.Dunlap@Sun.COM 	    cr->cr_protocol);
7187978SPeter.Dunlap@Sun.COM 	if (so == NULL) {
7197978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7207978SPeter.Dunlap@Sun.COM 	}
7217978SPeter.Dunlap@Sun.COM 
7227978SPeter.Dunlap@Sun.COM 	/* Bind the socket if configured to do so */
7237978SPeter.Dunlap@Sun.COM 	if (cr->cr_bound) {
7248348SEric.Yu@Sun.COM 		if (ksocket_bind(so, &cr->cr_bound_addr.sin,
7258348SEric.Yu@Sun.COM 		    SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) {
7267978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so);
7277978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
7287978SPeter.Dunlap@Sun.COM 		}
7297978SPeter.Dunlap@Sun.COM 	}
7307978SPeter.Dunlap@Sun.COM 
7317978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, so);
7327978SPeter.Dunlap@Sun.COM 	if (idmrc != IDM_STATUS_SUCCESS) {
7337978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so);
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 	so_conn = ic->ic_transport_private;
7397978SPeter.Dunlap@Sun.COM 	/* Set up socket options */
7407978SPeter.Dunlap@Sun.COM 	idm_set_ini_preconnect_options(so_conn);
7417978SPeter.Dunlap@Sun.COM 
7427978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
7437978SPeter.Dunlap@Sun.COM }
7447978SPeter.Dunlap@Sun.COM 
7457978SPeter.Dunlap@Sun.COM /*
7467978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_destroy()
7477978SPeter.Dunlap@Sun.COM  * Tear down the sockets transport connection resources.
7487978SPeter.Dunlap@Sun.COM  */
7497978SPeter.Dunlap@Sun.COM static void
7507978SPeter.Dunlap@Sun.COM idm_so_ini_conn_destroy(idm_conn_t *ic)
7517978SPeter.Dunlap@Sun.COM {
7527978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
7537978SPeter.Dunlap@Sun.COM }
7547978SPeter.Dunlap@Sun.COM 
7557978SPeter.Dunlap@Sun.COM /*
7567978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_connect()
7577978SPeter.Dunlap@Sun.COM  * Establish the connection referred to by the handle previously allocated via
7587978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create().
7597978SPeter.Dunlap@Sun.COM  */
7607978SPeter.Dunlap@Sun.COM static idm_status_t
7617978SPeter.Dunlap@Sun.COM idm_so_ini_conn_connect(idm_conn_t *ic)
7627978SPeter.Dunlap@Sun.COM {
7637978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
7647978SPeter.Dunlap@Sun.COM 
7657978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
7667978SPeter.Dunlap@Sun.COM 
7678348SEric.Yu@Sun.COM 	if (ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin,
7688348SEric.Yu@Sun.COM 	    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED()) != 0) {
7697978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_conn->ic_so);
7707978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7717978SPeter.Dunlap@Sun.COM 	}
7727978SPeter.Dunlap@Sun.COM 
7737978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
7747978SPeter.Dunlap@Sun.COM 
7757978SPeter.Dunlap@Sun.COM 	idm_set_ini_postconnect_options(so_conn);
7767978SPeter.Dunlap@Sun.COM 
7777978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
7787978SPeter.Dunlap@Sun.COM }
7797978SPeter.Dunlap@Sun.COM 
7807978SPeter.Dunlap@Sun.COM idm_status_t
7818348SEric.Yu@Sun.COM idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so)
7827978SPeter.Dunlap@Sun.COM {
7837978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
7847978SPeter.Dunlap@Sun.COM 
7857978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, new_so);
7867978SPeter.Dunlap@Sun.COM 
7877978SPeter.Dunlap@Sun.COM 	return (idmrc);
7887978SPeter.Dunlap@Sun.COM }
7897978SPeter.Dunlap@Sun.COM 
7907978SPeter.Dunlap@Sun.COM static void
7917978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_destroy(idm_conn_t *ic)
7927978SPeter.Dunlap@Sun.COM {
7937978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
7947978SPeter.Dunlap@Sun.COM }
7957978SPeter.Dunlap@Sun.COM 
7967978SPeter.Dunlap@Sun.COM /*
7977978SPeter.Dunlap@Sun.COM  * idm_so_tgt_conn_connect()
7987978SPeter.Dunlap@Sun.COM  * Establish the connection in ic, passed from idm_tgt_conn_finish(), which
7997978SPeter.Dunlap@Sun.COM  * is invoked from the SM as a result of an inbound connection request.
8007978SPeter.Dunlap@Sun.COM  */
8017978SPeter.Dunlap@Sun.COM static idm_status_t
8027978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_connect(idm_conn_t *ic)
8037978SPeter.Dunlap@Sun.COM {
8047978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
8057978SPeter.Dunlap@Sun.COM 
8067978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
8077978SPeter.Dunlap@Sun.COM }
8087978SPeter.Dunlap@Sun.COM 
8097978SPeter.Dunlap@Sun.COM static idm_status_t
8108348SEric.Yu@Sun.COM idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so)
8117978SPeter.Dunlap@Sun.COM {
8127978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8137978SPeter.Dunlap@Sun.COM 
8147978SPeter.Dunlap@Sun.COM 	so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP);
8157978SPeter.Dunlap@Sun.COM 	so_conn->ic_so = new_so;
8167978SPeter.Dunlap@Sun.COM 
8177978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = so_conn;
8187978SPeter.Dunlap@Sun.COM 	ic->ic_transport_hdrlen = 0;
8197978SPeter.Dunlap@Sun.COM 
8207978SPeter.Dunlap@Sun.COM 	/* Set the scoreboarding flag on this connection */
8217978SPeter.Dunlap@Sun.COM 	ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD;
8227978SPeter.Dunlap@Sun.COM 
8237978SPeter.Dunlap@Sun.COM 	/*
8247978SPeter.Dunlap@Sun.COM 	 * Initialize tx thread mutex and list
8257978SPeter.Dunlap@Sun.COM 	 */
8267978SPeter.Dunlap@Sun.COM 	mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL);
8277978SPeter.Dunlap@Sun.COM 	cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL);
8287978SPeter.Dunlap@Sun.COM 	list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t),
8297978SPeter.Dunlap@Sun.COM 	    offsetof(idm_pdu_t, idm_tx_link));
8307978SPeter.Dunlap@Sun.COM 
8317978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
8327978SPeter.Dunlap@Sun.COM }
8337978SPeter.Dunlap@Sun.COM 
8347978SPeter.Dunlap@Sun.COM static void
8357978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(idm_conn_t *ic)
8367978SPeter.Dunlap@Sun.COM {
8377978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
8387978SPeter.Dunlap@Sun.COM 
8397978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = NULL;
8407978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so_conn->ic_so);
8417978SPeter.Dunlap@Sun.COM 	list_destroy(&so_conn->ic_tx_list);
8427978SPeter.Dunlap@Sun.COM 	mutex_destroy(&so_conn->ic_tx_mutex);
8437978SPeter.Dunlap@Sun.COM 	cv_destroy(&so_conn->ic_tx_cv);
8447978SPeter.Dunlap@Sun.COM 
8457978SPeter.Dunlap@Sun.COM 	kmem_free(so_conn, sizeof (idm_so_conn_t));
8467978SPeter.Dunlap@Sun.COM }
8477978SPeter.Dunlap@Sun.COM 
8487978SPeter.Dunlap@Sun.COM static void
8497978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(idm_conn_t *ic)
8507978SPeter.Dunlap@Sun.COM {
8517978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8528348SEric.Yu@Sun.COM 	struct sockaddr_in6	t_addr;
8538348SEric.Yu@Sun.COM 	socklen_t	t_addrlen = 0;
8547978SPeter.Dunlap@Sun.COM 
8557978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
8568348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
8578348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
8587978SPeter.Dunlap@Sun.COM 
8597978SPeter.Dunlap@Sun.COM 	/* Set the local and remote addresses in the idm conn handle */
8608348SEric.Yu@Sun.COM 	ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr,
8618348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
8628348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_laddr, t_addrlen);
8638348SEric.Yu@Sun.COM 	ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr,
8648348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
8658348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_raddr, t_addrlen);
8667978SPeter.Dunlap@Sun.COM 
8677978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
8687978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0,
8697978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
8707978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0,
8717978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
8727978SPeter.Dunlap@Sun.COM 
8737978SPeter.Dunlap@Sun.COM 	while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running)
8747978SPeter.Dunlap@Sun.COM 		cv_wait(&ic->ic_cv, &ic->ic_mutex);
8757978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
8767978SPeter.Dunlap@Sun.COM }
8777978SPeter.Dunlap@Sun.COM 
8787978SPeter.Dunlap@Sun.COM /*
8797978SPeter.Dunlap@Sun.COM  * idm_so_conn_disconnect()
8807978SPeter.Dunlap@Sun.COM  * Shutdown the socket connection and stop the thread
8817978SPeter.Dunlap@Sun.COM  */
8827978SPeter.Dunlap@Sun.COM static void
8837978SPeter.Dunlap@Sun.COM idm_so_conn_disconnect(idm_conn_t *ic)
8847978SPeter.Dunlap@Sun.COM {
8857978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8867978SPeter.Dunlap@Sun.COM 
8877978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
8887978SPeter.Dunlap@Sun.COM 
8897978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
8907978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_FALSE;
8917978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_FALSE;
8927978SPeter.Dunlap@Sun.COM 	/* We need to wakeup the TX thread */
8937978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
8947978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
8957978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
8967978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
8977978SPeter.Dunlap@Sun.COM 
8987978SPeter.Dunlap@Sun.COM 	/* This should wakeup the RX thread if it is sleeping */
8997978SPeter.Dunlap@Sun.COM 	idm_soshutdown(so_conn->ic_so);
9007978SPeter.Dunlap@Sun.COM 
9017978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_tx_thread_did);
9027978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_rx_thread_did);
9037978SPeter.Dunlap@Sun.COM }
9047978SPeter.Dunlap@Sun.COM 
9057978SPeter.Dunlap@Sun.COM /*
9067978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_create()
9077978SPeter.Dunlap@Sun.COM  * Establish a service on an IP address and port.  idm_svc_req_t contains
9087978SPeter.Dunlap@Sun.COM  * the service parameters.
9097978SPeter.Dunlap@Sun.COM  */
9107978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
9117978SPeter.Dunlap@Sun.COM static idm_status_t
9127978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is)
9137978SPeter.Dunlap@Sun.COM {
9147978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
9157978SPeter.Dunlap@Sun.COM 
9167978SPeter.Dunlap@Sun.COM 	so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP);
9177978SPeter.Dunlap@Sun.COM 
9187978SPeter.Dunlap@Sun.COM 	/* Set the new sockets service in svc handle */
9197978SPeter.Dunlap@Sun.COM 	is->is_so_svc = (void *)so_svc;
9207978SPeter.Dunlap@Sun.COM 
9217978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
9227978SPeter.Dunlap@Sun.COM }
9237978SPeter.Dunlap@Sun.COM 
9247978SPeter.Dunlap@Sun.COM /*
9257978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_destroy()
9267978SPeter.Dunlap@Sun.COM  * Teardown sockets resources allocated in idm_so_tgt_svc_create()
9277978SPeter.Dunlap@Sun.COM  */
9287978SPeter.Dunlap@Sun.COM static void
9297978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_destroy(idm_svc_t *is)
9307978SPeter.Dunlap@Sun.COM {
9317978SPeter.Dunlap@Sun.COM 	/* the socket will have been torn down; free the service */
9327978SPeter.Dunlap@Sun.COM 	kmem_free(is->is_so_svc, sizeof (idm_so_svc_t));
9337978SPeter.Dunlap@Sun.COM }
9347978SPeter.Dunlap@Sun.COM 
9357978SPeter.Dunlap@Sun.COM /*
9367978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_online()
9377978SPeter.Dunlap@Sun.COM  * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create()
9387978SPeter.Dunlap@Sun.COM  */
9397978SPeter.Dunlap@Sun.COM 
9407978SPeter.Dunlap@Sun.COM static idm_status_t
9417978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_online(idm_svc_t *is)
9427978SPeter.Dunlap@Sun.COM {
9437978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
9447978SPeter.Dunlap@Sun.COM 	idm_svc_req_t		*sr = &is->is_svc_req;
9457978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	sin6_ip;
9467978SPeter.Dunlap@Sun.COM 	const uint32_t		on = 1;
9477978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
9487978SPeter.Dunlap@Sun.COM 
9497978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
9507978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
9517978SPeter.Dunlap@Sun.COM 
9527978SPeter.Dunlap@Sun.COM 	/*
9537978SPeter.Dunlap@Sun.COM 	 * Try creating an IPv6 socket first
9547978SPeter.Dunlap@Sun.COM 	 */
9557978SPeter.Dunlap@Sun.COM 	if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) {
9567978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
9577978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9587978SPeter.Dunlap@Sun.COM 	} else {
9597978SPeter.Dunlap@Sun.COM 		bzero(&sin6_ip, sizeof (sin6_ip));
9607978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_family = AF_INET6;
9617978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_port = htons(sr->sr_port);
9627978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_addr = in6addr_any;
9637978SPeter.Dunlap@Sun.COM 
9648348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9658348SEric.Yu@Sun.COM 		    SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
9667978SPeter.Dunlap@Sun.COM 		/*
9677978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
9687978SPeter.Dunlap@Sun.COM 		 */
9698348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
9708348SEric.Yu@Sun.COM 		    SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED());
9717978SPeter.Dunlap@Sun.COM 
9728348SEric.Yu@Sun.COM 		if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip,
9738348SEric.Yu@Sun.COM 		    sizeof (sin6_ip), CRED()) != 0) {
9747978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
9757978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so_svc->is_so);
9767978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
9777978SPeter.Dunlap@Sun.COM 		}
9787978SPeter.Dunlap@Sun.COM 	}
9797978SPeter.Dunlap@Sun.COM 
9807978SPeter.Dunlap@Sun.COM 	idm_set_tgt_connect_options(so_svc->is_so);
9817978SPeter.Dunlap@Sun.COM 
9828348SEric.Yu@Sun.COM 	if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) {
9837978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
9847978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
9857978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
9867978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9877978SPeter.Dunlap@Sun.COM 	}
9887978SPeter.Dunlap@Sun.COM 
9897978SPeter.Dunlap@Sun.COM 	/* Launch a watch thread */
9907978SPeter.Dunlap@Sun.COM 	so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher,
9917978SPeter.Dunlap@Sun.COM 	    is, 0, &p0, TS_RUN, minclsyspri);
9927978SPeter.Dunlap@Sun.COM 
9937978SPeter.Dunlap@Sun.COM 	if (so_svc->is_thread == NULL) {
9947978SPeter.Dunlap@Sun.COM 		/* Failure to launch; teardown the socket */
9957978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
9967978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
9977978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
9987978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9997978SPeter.Dunlap@Sun.COM 	}
10008348SEric.Yu@Sun.COM 	ksocket_hold(so_svc->is_so);
10017978SPeter.Dunlap@Sun.COM 	/* Wait for the port watcher thread to start */
10027978SPeter.Dunlap@Sun.COM 	while (!so_svc->is_thread_running)
10037978SPeter.Dunlap@Sun.COM 		cv_wait(&is->is_cv, &is->is_mutex);
10047978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
10057978SPeter.Dunlap@Sun.COM 
10067978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
10077978SPeter.Dunlap@Sun.COM }
10087978SPeter.Dunlap@Sun.COM 
10097978SPeter.Dunlap@Sun.COM /*
10107978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_offline
10117978SPeter.Dunlap@Sun.COM  *
10127978SPeter.Dunlap@Sun.COM  * Stop listening on the IP address and port identified by idm_svc_t.
10137978SPeter.Dunlap@Sun.COM  */
10147978SPeter.Dunlap@Sun.COM static void
10157978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_offline(idm_svc_t *is)
10167978SPeter.Dunlap@Sun.COM {
10177978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
10187978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
10197978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
10207978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
10217978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
10227978SPeter.Dunlap@Sun.COM 
10237978SPeter.Dunlap@Sun.COM 	/*
10248348SEric.Yu@Sun.COM 	 * Teardown socket
10257978SPeter.Dunlap@Sun.COM 	 */
10268348SEric.Yu@Sun.COM 	idm_sodestroy(so_svc->is_so);
10277978SPeter.Dunlap@Sun.COM 
10287978SPeter.Dunlap@Sun.COM 	/*
10297978SPeter.Dunlap@Sun.COM 	 * Now we expect the port watcher thread to terminate
10307978SPeter.Dunlap@Sun.COM 	 */
10317978SPeter.Dunlap@Sun.COM 	thread_join(so_svc->is_thread_did);
10327978SPeter.Dunlap@Sun.COM }
10337978SPeter.Dunlap@Sun.COM 
10347978SPeter.Dunlap@Sun.COM /*
10357978SPeter.Dunlap@Sun.COM  * Watch thread for target service connection establishment.
10367978SPeter.Dunlap@Sun.COM  */
10377978SPeter.Dunlap@Sun.COM void
10387978SPeter.Dunlap@Sun.COM idm_so_svc_port_watcher(void *arg)
10397978SPeter.Dunlap@Sun.COM {
10407978SPeter.Dunlap@Sun.COM 	idm_svc_t		*svc = arg;
10418348SEric.Yu@Sun.COM 	ksocket_t		new_so;
10427978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
10437978SPeter.Dunlap@Sun.COM 	idm_status_t		idmrc;
10447978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
10457978SPeter.Dunlap@Sun.COM 	int			rc;
10467978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
10478348SEric.Yu@Sun.COM 	struct sockaddr_in6 	t_addr;
10488348SEric.Yu@Sun.COM 	socklen_t		t_addrlen;
10497978SPeter.Dunlap@Sun.COM 
10508348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
10518348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
10527978SPeter.Dunlap@Sun.COM 	mutex_enter(&svc->is_mutex);
10537978SPeter.Dunlap@Sun.COM 
10547978SPeter.Dunlap@Sun.COM 	so_svc = svc->is_so_svc;
10557978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_TRUE;
10567978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_did = so_svc->is_thread->t_did;
10577978SPeter.Dunlap@Sun.COM 
10587978SPeter.Dunlap@Sun.COM 	cv_signal(&svc->is_cv);
10597978SPeter.Dunlap@Sun.COM 
10607978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc,
10617978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
10627978SPeter.Dunlap@Sun.COM 
10637978SPeter.Dunlap@Sun.COM 	while (so_svc->is_thread_running) {
10647978SPeter.Dunlap@Sun.COM 		mutex_exit(&svc->is_mutex);
10657978SPeter.Dunlap@Sun.COM 
10668348SEric.Yu@Sun.COM 		if ((rc = ksocket_accept(so_svc->is_so,
10678348SEric.Yu@Sun.COM 		    (struct sockaddr *)&t_addr, &t_addrlen,
10688348SEric.Yu@Sun.COM 		    &new_so, CRED())) != 0) {
10697978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
10707978SPeter.Dunlap@Sun.COM 			if (rc == ECONNABORTED)
10717978SPeter.Dunlap@Sun.COM 				continue;
10727978SPeter.Dunlap@Sun.COM 			/* Connection problem */
10737978SPeter.Dunlap@Sun.COM 			break;
10747978SPeter.Dunlap@Sun.COM 		}
10757978SPeter.Dunlap@Sun.COM 		/*
10767978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
10777978SPeter.Dunlap@Sun.COM 		 */
10788348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT,
10798348SEric.Yu@Sun.COM 		    (char *)&off, sizeof (off), CRED());
10807978SPeter.Dunlap@Sun.COM 
10817978SPeter.Dunlap@Sun.COM 		idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS,
10827978SPeter.Dunlap@Sun.COM 		    &ic);
10837978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
10847978SPeter.Dunlap@Sun.COM 			/* Drop connection */
10857978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
10867978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
10877978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
10887978SPeter.Dunlap@Sun.COM 			continue;
10897978SPeter.Dunlap@Sun.COM 		}
10907978SPeter.Dunlap@Sun.COM 
10917978SPeter.Dunlap@Sun.COM 		idmrc = idm_so_tgt_conn_create(ic, new_so);
10927978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
10937978SPeter.Dunlap@Sun.COM 			idm_svc_conn_destroy(ic);
10947978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
10957978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
10967978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
10977978SPeter.Dunlap@Sun.COM 			continue;
10987978SPeter.Dunlap@Sun.COM 		}
10997978SPeter.Dunlap@Sun.COM 
11007978SPeter.Dunlap@Sun.COM 		/*
11017978SPeter.Dunlap@Sun.COM 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
11027978SPeter.Dunlap@Sun.COM 		 * will notify the client (target) about the new connection.
11037978SPeter.Dunlap@Sun.COM 		 */
11047978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
11057978SPeter.Dunlap@Sun.COM 
11067978SPeter.Dunlap@Sun.COM 		mutex_enter(&svc->is_mutex);
11077978SPeter.Dunlap@Sun.COM 	}
11088348SEric.Yu@Sun.COM 	ksocket_rele(so_svc->is_so);
11097978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
11107978SPeter.Dunlap@Sun.COM 	mutex_exit(&svc->is_mutex);
11117978SPeter.Dunlap@Sun.COM 
11127978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc,
11137978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
11147978SPeter.Dunlap@Sun.COM 
11157978SPeter.Dunlap@Sun.COM 	thread_exit();
11167978SPeter.Dunlap@Sun.COM }
11177978SPeter.Dunlap@Sun.COM 
11187978SPeter.Dunlap@Sun.COM /*
11197978SPeter.Dunlap@Sun.COM  * idm_so_free_task_rsrc() stops any ongoing processing of the task and
11207978SPeter.Dunlap@Sun.COM  * frees resources associated with the task.
11217978SPeter.Dunlap@Sun.COM  *
11227978SPeter.Dunlap@Sun.COM  * It's not clear that this should return idm_status_t.  What do we do
11237978SPeter.Dunlap@Sun.COM  * if it fails?
11247978SPeter.Dunlap@Sun.COM  */
11257978SPeter.Dunlap@Sun.COM static idm_status_t
11267978SPeter.Dunlap@Sun.COM idm_so_free_task_rsrc(idm_task_t *idt)
11277978SPeter.Dunlap@Sun.COM {
11287978SPeter.Dunlap@Sun.COM 	idm_buf_t	*idb;
11297978SPeter.Dunlap@Sun.COM 
11307978SPeter.Dunlap@Sun.COM 	/*
11317978SPeter.Dunlap@Sun.COM 	 * If this is a target connection, call idm_buf_rx_from_ini_done for
11327978SPeter.Dunlap@Sun.COM 	 * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE.
11337978SPeter.Dunlap@Sun.COM 	 *
11347978SPeter.Dunlap@Sun.COM 	 * In addition, remove any buffers associated with this task from
11357978SPeter.Dunlap@Sun.COM 	 * the ic_tx_list.  We'll do this by walking the idt_inbufv list, but
11367978SPeter.Dunlap@Sun.COM 	 * items don't actually get removed from that list (and completion
11377978SPeter.Dunlap@Sun.COM 	 * routines called) until idm_task_cleanup.
11387978SPeter.Dunlap@Sun.COM 	 */
11397978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
11407978SPeter.Dunlap@Sun.COM 
11417978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_outbufv); idb != NULL;
11427978SPeter.Dunlap@Sun.COM 	    idb = list_next(&idt->idt_outbufv, idb)) {
11437978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport) {
11447978SPeter.Dunlap@Sun.COM 			/*
11457978SPeter.Dunlap@Sun.COM 			 * idm_buf_rx_from_ini_done releases idt->idt_mutex
11467978SPeter.Dunlap@Sun.COM 			 */
11477978SPeter.Dunlap@Sun.COM 			idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED);
11487978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
11497978SPeter.Dunlap@Sun.COM 		}
11507978SPeter.Dunlap@Sun.COM 	}
11517978SPeter.Dunlap@Sun.COM 
11527978SPeter.Dunlap@Sun.COM 	for (idb = list_head(&idt->idt_inbufv); idb != NULL;
11537978SPeter.Dunlap@Sun.COM 	    idb = list_next(&idt->idt_inbufv, idb)) {
11547978SPeter.Dunlap@Sun.COM 		/*
11557978SPeter.Dunlap@Sun.COM 		 * We want to remove these items from the tx_list as well,
11567978SPeter.Dunlap@Sun.COM 		 * but knowing it's in the idt_inbufv list is not a guarantee
11577978SPeter.Dunlap@Sun.COM 		 * that it's in the tx_list.  If it's on the tx list then
11587978SPeter.Dunlap@Sun.COM 		 * let idm_sotx_thread() clean it up.
11597978SPeter.Dunlap@Sun.COM 		 */
11607978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport && !idb->idb_tx_thread) {
11617978SPeter.Dunlap@Sun.COM 			/*
11627978SPeter.Dunlap@Sun.COM 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
11637978SPeter.Dunlap@Sun.COM 			 */
11647978SPeter.Dunlap@Sun.COM 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
11657978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
11667978SPeter.Dunlap@Sun.COM 		}
11677978SPeter.Dunlap@Sun.COM 	}
11687978SPeter.Dunlap@Sun.COM 
11697978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
11707978SPeter.Dunlap@Sun.COM 
11717978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
11727978SPeter.Dunlap@Sun.COM }
11737978SPeter.Dunlap@Sun.COM 
11747978SPeter.Dunlap@Sun.COM /*
11757978SPeter.Dunlap@Sun.COM  * idm_so_negotiate_key_values() validates the key values for this connection
11767978SPeter.Dunlap@Sun.COM  */
11777978SPeter.Dunlap@Sun.COM /* ARGSUSED */
11787978SPeter.Dunlap@Sun.COM static kv_status_t
11797978SPeter.Dunlap@Sun.COM idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl,
11807978SPeter.Dunlap@Sun.COM     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
11817978SPeter.Dunlap@Sun.COM {
11827978SPeter.Dunlap@Sun.COM 	/* All parameters are negotiated at the iscsit level */
11837978SPeter.Dunlap@Sun.COM 	return (KV_HANDLED);
11847978SPeter.Dunlap@Sun.COM }
11857978SPeter.Dunlap@Sun.COM 
11867978SPeter.Dunlap@Sun.COM /*
11877978SPeter.Dunlap@Sun.COM  * idm_so_notice_key_values() activates the negotiated key values for
11887978SPeter.Dunlap@Sun.COM  * this connection.
11897978SPeter.Dunlap@Sun.COM  */
11907978SPeter.Dunlap@Sun.COM static idm_status_t
11917978SPeter.Dunlap@Sun.COM idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl)
11927978SPeter.Dunlap@Sun.COM {
11937978SPeter.Dunlap@Sun.COM 	char			*nvp_name;
11947978SPeter.Dunlap@Sun.COM 	nvpair_t		*nvp;
11957978SPeter.Dunlap@Sun.COM 	nvpair_t		*next_nvp;
11967978SPeter.Dunlap@Sun.COM 	int			nvrc;
11977978SPeter.Dunlap@Sun.COM 	idm_status_t		idm_status;
11987978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t	*ikvx;
11997978SPeter.Dunlap@Sun.COM 
12007978SPeter.Dunlap@Sun.COM 	for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL);
12017978SPeter.Dunlap@Sun.COM 	    nvp != NULL; nvp = next_nvp) {
12027978SPeter.Dunlap@Sun.COM 		next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp);
12037978SPeter.Dunlap@Sun.COM 		nvp_name = nvpair_name(nvp);
12047978SPeter.Dunlap@Sun.COM 
12057978SPeter.Dunlap@Sun.COM 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
12067978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12077978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12087978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12097978SPeter.Dunlap@Sun.COM 			idm_status = idm_so_handle_digest(it, nvp, ikvx);
12107978SPeter.Dunlap@Sun.COM 			ASSERT(idm_status == 0);
12117978SPeter.Dunlap@Sun.COM 
12127978SPeter.Dunlap@Sun.COM 			/* Remove processed item from negotiated_nvl list */
12137978SPeter.Dunlap@Sun.COM 			nvrc = nvlist_remove_all(
12147978SPeter.Dunlap@Sun.COM 			    negotiated_nvl, ikvx->ik_key_name);
12157978SPeter.Dunlap@Sun.COM 			ASSERT(nvrc == 0);
12167978SPeter.Dunlap@Sun.COM 			break;
12177978SPeter.Dunlap@Sun.COM 		default:
12187978SPeter.Dunlap@Sun.COM 			break;
12197978SPeter.Dunlap@Sun.COM 		}
12207978SPeter.Dunlap@Sun.COM 	}
12217978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12227978SPeter.Dunlap@Sun.COM }
12237978SPeter.Dunlap@Sun.COM 
12247978SPeter.Dunlap@Sun.COM 
12257978SPeter.Dunlap@Sun.COM static idm_status_t
12267978SPeter.Dunlap@Sun.COM idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice,
12277978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx)
12287978SPeter.Dunlap@Sun.COM {
12297978SPeter.Dunlap@Sun.COM 	int			nvrc;
12307978SPeter.Dunlap@Sun.COM 	char			*digest_choice_string;
12317978SPeter.Dunlap@Sun.COM 
12327978SPeter.Dunlap@Sun.COM 	nvrc = nvpair_value_string(digest_choice,
12337978SPeter.Dunlap@Sun.COM 	    &digest_choice_string);
12347978SPeter.Dunlap@Sun.COM 	ASSERT(nvrc == 0);
12357978SPeter.Dunlap@Sun.COM 	if (strcasecmp(digest_choice_string, "crc32c") == 0) {
12367978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12377978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12387978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST;
12397978SPeter.Dunlap@Sun.COM 			break;
12407978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12417978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_DATA_DIGEST;
12427978SPeter.Dunlap@Sun.COM 			break;
12437978SPeter.Dunlap@Sun.COM 		default:
12447978SPeter.Dunlap@Sun.COM 			ASSERT(0);
12457978SPeter.Dunlap@Sun.COM 			break;
12467978SPeter.Dunlap@Sun.COM 		}
12477978SPeter.Dunlap@Sun.COM 	} else if (strcasecmp(digest_choice_string, "none") == 0) {
12487978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
12497978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
12507978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST;
12517978SPeter.Dunlap@Sun.COM 			break;
12527978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
12537978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST;
12547978SPeter.Dunlap@Sun.COM 			break;
12557978SPeter.Dunlap@Sun.COM 		default:
12567978SPeter.Dunlap@Sun.COM 			ASSERT(0);
12577978SPeter.Dunlap@Sun.COM 			break;
12587978SPeter.Dunlap@Sun.COM 		}
12597978SPeter.Dunlap@Sun.COM 	} else {
12607978SPeter.Dunlap@Sun.COM 		ASSERT(0);
12617978SPeter.Dunlap@Sun.COM 	}
12627978SPeter.Dunlap@Sun.COM 
12637978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12647978SPeter.Dunlap@Sun.COM }
12657978SPeter.Dunlap@Sun.COM 
12667978SPeter.Dunlap@Sun.COM 
12677978SPeter.Dunlap@Sun.COM /*
12687978SPeter.Dunlap@Sun.COM  * idm_so_conn_is_capable() verifies that the passed connection is provided
12697978SPeter.Dunlap@Sun.COM  * for by the sockets interface.
12707978SPeter.Dunlap@Sun.COM  */
12717978SPeter.Dunlap@Sun.COM /* ARGSUSED */
12727978SPeter.Dunlap@Sun.COM static boolean_t
12737978SPeter.Dunlap@Sun.COM idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps)
12747978SPeter.Dunlap@Sun.COM {
12757978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
12767978SPeter.Dunlap@Sun.COM }
12777978SPeter.Dunlap@Sun.COM 
12787978SPeter.Dunlap@Sun.COM /*
12797978SPeter.Dunlap@Sun.COM  * idm_so_rx_datain() validates the Data Sequence number of the PDU. The
12807978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() function invoked earlier actually reads the data
12817978SPeter.Dunlap@Sun.COM  * off the socket into the appropriate buffers.
12827978SPeter.Dunlap@Sun.COM  */
12837978SPeter.Dunlap@Sun.COM static void
12847978SPeter.Dunlap@Sun.COM idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu)
12857978SPeter.Dunlap@Sun.COM {
12867978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
12877978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
12887978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
12897978SPeter.Dunlap@Sun.COM 	uint32_t		datasn;
12907978SPeter.Dunlap@Sun.COM 	size_t			offset;
12917978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t		*ihp = (iscsi_hdr_t *)pdu->isp_hdr;
12927978SPeter.Dunlap@Sun.COM 	iscsi_data_rsp_hdr_t    *idrhp = (iscsi_data_rsp_hdr_t *)ihp;
12937978SPeter.Dunlap@Sun.COM 
12947978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
12957978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
12967978SPeter.Dunlap@Sun.COM 
12977978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
12987978SPeter.Dunlap@Sun.COM 	datasn	= ntohl(bhs->datasn);
12997978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
13007978SPeter.Dunlap@Sun.COM 
13017978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP);
13027978SPeter.Dunlap@Sun.COM 
13037978SPeter.Dunlap@Sun.COM 	/*
13047978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
13057978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
13067978SPeter.Dunlap@Sun.COM 	 */
13077978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
13087978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
13097978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task");
13107978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13117978SPeter.Dunlap@Sun.COM 		return;
13127978SPeter.Dunlap@Sun.COM 	}
13137978SPeter.Dunlap@Sun.COM 
13147978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
13157978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
13167978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
13177978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_datain: failed to find buffer");
13187978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
13197978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13207978SPeter.Dunlap@Sun.COM 		return;
13217978SPeter.Dunlap@Sun.COM 	}
13227978SPeter.Dunlap@Sun.COM 
13237978SPeter.Dunlap@Sun.COM 	/*
13247978SPeter.Dunlap@Sun.COM 	 * DataSN values should be sequential and should not have any gaps or
13257978SPeter.Dunlap@Sun.COM 	 * repetitions. Check the DataSN with the one stored in the task.
13267978SPeter.Dunlap@Sun.COM 	 */
13277978SPeter.Dunlap@Sun.COM 	if (datasn == idt->idt_exp_datasn) {
13287978SPeter.Dunlap@Sun.COM 		idt->idt_exp_datasn++; /* keep track of DataSN received */
13297978SPeter.Dunlap@Sun.COM 	} else {
13307978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order");
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 	idm_task_rele(idt);
13367978SPeter.Dunlap@Sun.COM 
13377978SPeter.Dunlap@Sun.COM 	/*
13387978SPeter.Dunlap@Sun.COM 	 * PDUs in a sequence should be in continuously increasing
13397978SPeter.Dunlap@Sun.COM 	 * address offset
13407978SPeter.Dunlap@Sun.COM 	 */
13417978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
13427978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset");
13437978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13447978SPeter.Dunlap@Sun.COM 		return;
13457978SPeter.Dunlap@Sun.COM 	}
13467978SPeter.Dunlap@Sun.COM 	/* Expected next relative buffer offset */
13477978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += n2h24(bhs->dlength);
13487978SPeter.Dunlap@Sun.COM 
13497978SPeter.Dunlap@Sun.COM 	/*
13507978SPeter.Dunlap@Sun.COM 	 * For now call scsi_rsp which will process the data rsp
13517978SPeter.Dunlap@Sun.COM 	 * Revisit, need to provide an explicit client entry point for
13527978SPeter.Dunlap@Sun.COM 	 * phase collapse completions.
13537978SPeter.Dunlap@Sun.COM 	 */
13547978SPeter.Dunlap@Sun.COM 	if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) &&
13557978SPeter.Dunlap@Sun.COM 	    (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) {
13567978SPeter.Dunlap@Sun.COM 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
13577978SPeter.Dunlap@Sun.COM 	}
13587978SPeter.Dunlap@Sun.COM 
13597978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
13607978SPeter.Dunlap@Sun.COM }
13617978SPeter.Dunlap@Sun.COM 
13627978SPeter.Dunlap@Sun.COM /*
13637978SPeter.Dunlap@Sun.COM  * The idm_so_rx_dataout() function is used by the iSCSI target to read
13647978SPeter.Dunlap@Sun.COM  * data from the Data-Out PDU sent by the iSCSI initiator.
13657978SPeter.Dunlap@Sun.COM  *
13667978SPeter.Dunlap@Sun.COM  * This function gets the Initiator Task Tag from the PDU BHS and looks up the
13677978SPeter.Dunlap@Sun.COM  * task to get the buffers associated with the PDU. A PDU might span buffers.
13687978SPeter.Dunlap@Sun.COM  * The data is then read into the respective buffer.
13697978SPeter.Dunlap@Sun.COM  */
13707978SPeter.Dunlap@Sun.COM static void
13717978SPeter.Dunlap@Sun.COM idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu)
13727978SPeter.Dunlap@Sun.COM {
13737978SPeter.Dunlap@Sun.COM 
13747978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
13757978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
13767978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
13777978SPeter.Dunlap@Sun.COM 	size_t			offset;
13787978SPeter.Dunlap@Sun.COM 
13797978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
13807978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
13817978SPeter.Dunlap@Sun.COM 
13827978SPeter.Dunlap@Sun.COM 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
13837978SPeter.Dunlap@Sun.COM 	offset = ntohl(bhs->offset);
13847978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA);
13857978SPeter.Dunlap@Sun.COM 
13867978SPeter.Dunlap@Sun.COM 	/*
13877978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
13887978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
13897978SPeter.Dunlap@Sun.COM 	 */
13907978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
13917978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
13927978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
13937978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find task");
13947978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
13957978SPeter.Dunlap@Sun.COM 		return;
13967978SPeter.Dunlap@Sun.COM 	}
13977978SPeter.Dunlap@Sun.COM 
13987978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
13997978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
14007978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
14017978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find buffer");
14027978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14037978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14047978SPeter.Dunlap@Sun.COM 		return;
14057978SPeter.Dunlap@Sun.COM 	}
14067978SPeter.Dunlap@Sun.COM 
14077978SPeter.Dunlap@Sun.COM 	/* Keep track of data transferred - check data offsets */
14087978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
14097978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: "
14107978SPeter.Dunlap@Sun.COM 		    "%ld, %d", offset, idb->idb_exp_offset);
14117978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14127978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14137978SPeter.Dunlap@Sun.COM 		return;
14147978SPeter.Dunlap@Sun.COM 	}
14157978SPeter.Dunlap@Sun.COM 	/* Expected next relative offset */
14167978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += ntoh24(bhs->dlength);
14177978SPeter.Dunlap@Sun.COM 
14187978SPeter.Dunlap@Sun.COM 	/*
14197978SPeter.Dunlap@Sun.COM 	 * Call the buffer callback when the transfer is complete
14207978SPeter.Dunlap@Sun.COM 	 *
14217978SPeter.Dunlap@Sun.COM 	 * The connection state machine should only abort tasks after
14227978SPeter.Dunlap@Sun.COM 	 * shutting down the connection so we are assured that there
14237978SPeter.Dunlap@Sun.COM 	 * won't be a simultaneous attempt to abort this task at the
14247978SPeter.Dunlap@Sun.COM 	 * same time as we are processing this PDU (due to a connection
14257978SPeter.Dunlap@Sun.COM 	 * state change).
14267978SPeter.Dunlap@Sun.COM 	 */
14277978SPeter.Dunlap@Sun.COM 	if (bhs->flags & ISCSI_FLAG_FINAL) {
14287978SPeter.Dunlap@Sun.COM 		/*
14297978SPeter.Dunlap@Sun.COM 		 * We only want to call idm_buf_rx_from_ini_done once
14307978SPeter.Dunlap@Sun.COM 		 * per transfer.  It's possible that this task has
14317978SPeter.Dunlap@Sun.COM 		 * already been aborted in which case
14327978SPeter.Dunlap@Sun.COM 		 * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done
14337978SPeter.Dunlap@Sun.COM 		 * for each buffer with idb_in_transport==B_TRUE.  To
14347978SPeter.Dunlap@Sun.COM 		 * close this window and ensure that this doesn't happen,
14357978SPeter.Dunlap@Sun.COM 		 * we'll clear idb->idb_in_transport now while holding
14367978SPeter.Dunlap@Sun.COM 		 * the task mutex.   This is only really an issue for
14377978SPeter.Dunlap@Sun.COM 		 * SCSI task abort -- if tasks were being aborted because
14387978SPeter.Dunlap@Sun.COM 		 * of a connection state change the state machine would
14397978SPeter.Dunlap@Sun.COM 		 * have already stopped the receive thread.
14407978SPeter.Dunlap@Sun.COM 		 */
14417978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
14427978SPeter.Dunlap@Sun.COM 
14437978SPeter.Dunlap@Sun.COM 		/*
14447978SPeter.Dunlap@Sun.COM 		 * Release the task hold here (obtained in idm_task_find)
14457978SPeter.Dunlap@Sun.COM 		 * because the task may complete synchronously during
14467978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done.  Since we still have an active
14477978SPeter.Dunlap@Sun.COM 		 * buffer we know there is at least one additional hold on idt.
14487978SPeter.Dunlap@Sun.COM 		 */
14497978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
14507978SPeter.Dunlap@Sun.COM 
14517978SPeter.Dunlap@Sun.COM 		/*
14527978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done releases idt->idt_mutex
14537978SPeter.Dunlap@Sun.COM 		 */
14547978SPeter.Dunlap@Sun.COM 		idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS);
14557978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
14567978SPeter.Dunlap@Sun.COM 		return;
14577978SPeter.Dunlap@Sun.COM 	}
14587978SPeter.Dunlap@Sun.COM 
14597978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
14607978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
14617978SPeter.Dunlap@Sun.COM }
14627978SPeter.Dunlap@Sun.COM 
14637978SPeter.Dunlap@Sun.COM /*
14647978SPeter.Dunlap@Sun.COM  * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle
14657978SPeter.Dunlap@Sun.COM  * the R2T PDU sent by the iSCSI target indicating that it is ready to
14667978SPeter.Dunlap@Sun.COM  * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS
14677978SPeter.Dunlap@Sun.COM  * and looks up the task in the task tree using the itt to get the output
14687978SPeter.Dunlap@Sun.COM  * buffers associated the task. The R2T PDU contains the offset of the
14697978SPeter.Dunlap@Sun.COM  * requested data and the data length. This function then constructs a
14707978SPeter.Dunlap@Sun.COM  * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out
14717978SPeter.Dunlap@Sun.COM  * PDU is associated with the R2T by the Target Transfer Tag  (ttt).
14727978SPeter.Dunlap@Sun.COM  */
14737978SPeter.Dunlap@Sun.COM static void
14747978SPeter.Dunlap@Sun.COM idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu)
14757978SPeter.Dunlap@Sun.COM {
14767978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
14777978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
14787978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt_hdr;
14797978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset;
14807978SPeter.Dunlap@Sun.COM 
14817978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
14827978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
14837978SPeter.Dunlap@Sun.COM 
14847978SPeter.Dunlap@Sun.COM 	rtt_hdr	= (iscsi_rtt_hdr_t *)pdu->isp_hdr;
14857978SPeter.Dunlap@Sun.COM 	data_offset = ntohl(rtt_hdr->data_offset);
14867978SPeter.Dunlap@Sun.COM 
14877978SPeter.Dunlap@Sun.COM 	idt	= idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt);
14887978SPeter.Dunlap@Sun.COM 
14897978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
14907978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task");
14917978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
14927978SPeter.Dunlap@Sun.COM 		return;
14937978SPeter.Dunlap@Sun.COM 	}
14947978SPeter.Dunlap@Sun.COM 
14957978SPeter.Dunlap@Sun.COM 	/* Find the buffer bound to the task by the iSCSI initiator */
14967978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
14977978SPeter.Dunlap@Sun.COM 	idb = idm_buf_find(&idt->idt_outbufv, data_offset);
14987978SPeter.Dunlap@Sun.COM 	idt->idt_r2t_ttt = rtt_hdr->ttt;
14997978SPeter.Dunlap@Sun.COM 	/* reset to zero */
15007978SPeter.Dunlap@Sun.COM 	idt->idt_exp_datasn = 0;
15017978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
15027978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
15037978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
15047978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer");
15057978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15067978SPeter.Dunlap@Sun.COM 		return;
15077978SPeter.Dunlap@Sun.COM 	}
15087978SPeter.Dunlap@Sun.COM 
15097978SPeter.Dunlap@Sun.COM 	(void) idm_so_send_buf_region(idt, ISCSI_OP_SCSI_DATA, idb,
15107978SPeter.Dunlap@Sun.COM 	    data_offset, ntohl(rtt_hdr->data_length));
15117978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
15127978SPeter.Dunlap@Sun.COM 
15137978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
15147978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
15157978SPeter.Dunlap@Sun.COM 
15167978SPeter.Dunlap@Sun.COM }
15177978SPeter.Dunlap@Sun.COM 
15187978SPeter.Dunlap@Sun.COM idm_status_t
15197978SPeter.Dunlap@Sun.COM idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu)
15207978SPeter.Dunlap@Sun.COM {
15217978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
15227978SPeter.Dunlap@Sun.COM 	int		pad_len;
15237978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc;
15247978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
15257978SPeter.Dunlap@Sun.COM 	int		total_len;
15267978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
15277978SPeter.Dunlap@Sun.COM 
15287978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
15297978SPeter.Dunlap@Sun.COM 
15307978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
15317978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
15327978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
15337978SPeter.Dunlap@Sun.COM 
15347978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */
15357978SPeter.Dunlap@Sun.COM 
15367978SPeter.Dunlap@Sun.COM 	total_len = pdu->isp_datalen;
15377978SPeter.Dunlap@Sun.COM 
15387978SPeter.Dunlap@Sun.COM 	if (pad_len) {
15397978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base	= (char *)&pad;
15407978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len	= pad_len;
15417978SPeter.Dunlap@Sun.COM 		total_len		+= pad_len;
15427978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
15437978SPeter.Dunlap@Sun.COM 	}
15447978SPeter.Dunlap@Sun.COM 
15457978SPeter.Dunlap@Sun.COM 	/* setup data digest */
15467978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
15477978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base =
15487978SPeter.Dunlap@Sun.COM 		    (char *)&data_digest_crc;
15497978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len =
15507978SPeter.Dunlap@Sun.COM 		    sizeof (data_digest_crc);
15517978SPeter.Dunlap@Sun.COM 		total_len		+= sizeof (data_digest_crc);
15527978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
15537978SPeter.Dunlap@Sun.COM 	}
15547978SPeter.Dunlap@Sun.COM 
15557978SPeter.Dunlap@Sun.COM 	if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0],
15567978SPeter.Dunlap@Sun.COM 	    pdu->isp_iovlen, total_len) != 0) {
15577978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_IO);
15587978SPeter.Dunlap@Sun.COM 	}
15597978SPeter.Dunlap@Sun.COM 
15607978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
15617978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_data,
15627978SPeter.Dunlap@Sun.COM 		    pdu->isp_datalen);
15637978SPeter.Dunlap@Sun.COM 		if (pad_len) {
15647978SPeter.Dunlap@Sun.COM 			crc_calculated = idm_crc32c_continued((char *)&pad,
15657978SPeter.Dunlap@Sun.COM 			    pad_len, crc_calculated);
15667978SPeter.Dunlap@Sun.COM 		}
15677978SPeter.Dunlap@Sun.COM 		if (crc_calculated != data_digest_crc) {
15687978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
15697978SPeter.Dunlap@Sun.COM 			    "idm_sorecvdata: "
15707978SPeter.Dunlap@Sun.COM 			    "CRC error: actual 0x%x, calc 0x%x",
15717978SPeter.Dunlap@Sun.COM 			    data_digest_crc, crc_calculated);
15727978SPeter.Dunlap@Sun.COM 
15737978SPeter.Dunlap@Sun.COM 			/* Invalid Data Digest */
15747978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_DATA_DIGEST);
15757978SPeter.Dunlap@Sun.COM 		}
15767978SPeter.Dunlap@Sun.COM 	}
15777978SPeter.Dunlap@Sun.COM 
15787978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
15797978SPeter.Dunlap@Sun.COM }
15807978SPeter.Dunlap@Sun.COM 
15817978SPeter.Dunlap@Sun.COM /*
15827978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() is used to receive scsi data from the socket. The
15837978SPeter.Dunlap@Sun.COM  * Data-type PDU header must be read into the idm_pdu_t structure prior to
15847978SPeter.Dunlap@Sun.COM  * calling this function.
15857978SPeter.Dunlap@Sun.COM  */
15867978SPeter.Dunlap@Sun.COM idm_status_t
15877978SPeter.Dunlap@Sun.COM idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu)
15887978SPeter.Dunlap@Sun.COM {
15897978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
15907978SPeter.Dunlap@Sun.COM 	idm_task_t		*task;
15917978SPeter.Dunlap@Sun.COM 	uint32_t		offset;
15927978SPeter.Dunlap@Sun.COM 	uint8_t			opcode;
15937978SPeter.Dunlap@Sun.COM 	uint32_t		dlength;
15947978SPeter.Dunlap@Sun.COM 	list_t			*buflst;
15957978SPeter.Dunlap@Sun.COM 	uint32_t		xfer_bytes;
15967978SPeter.Dunlap@Sun.COM 	idm_status_t		status;
15977978SPeter.Dunlap@Sun.COM 
15987978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
15997978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
16007978SPeter.Dunlap@Sun.COM 
16017978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
16027978SPeter.Dunlap@Sun.COM 
16037978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
16047978SPeter.Dunlap@Sun.COM 	opcode	= bhs->opcode;
16057978SPeter.Dunlap@Sun.COM 	dlength = n2h24(bhs->dlength);
16067978SPeter.Dunlap@Sun.COM 
16077978SPeter.Dunlap@Sun.COM 	ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
16087978SPeter.Dunlap@Sun.COM 	    (opcode == ISCSI_OP_SCSI_DATA));
16097978SPeter.Dunlap@Sun.COM 
16107978SPeter.Dunlap@Sun.COM 	/*
16117978SPeter.Dunlap@Sun.COM 	 * Successful lookup implicitly gets a "hold" on the task.  This
16127978SPeter.Dunlap@Sun.COM 	 * hold must be released before leaving this function.  At one
16137978SPeter.Dunlap@Sun.COM 	 * point we were caching this task context and retaining the hold
16147978SPeter.Dunlap@Sun.COM 	 * but it turned out to be very difficult to release the hold properly.
16157978SPeter.Dunlap@Sun.COM 	 * The task can be aborted and the connection shutdown between this
16167978SPeter.Dunlap@Sun.COM 	 * call and the subsequent expected call to idm_so_rx_datain/
16177978SPeter.Dunlap@Sun.COM 	 * idm_so_rx_dataout (in which case those functions are not called).
16187978SPeter.Dunlap@Sun.COM 	 * Releasing the hold in the PDU callback doesn't work well either
16197978SPeter.Dunlap@Sun.COM 	 * because the whole task may be completed by then at which point
16207978SPeter.Dunlap@Sun.COM 	 * it is too late to release the hold -- for better or worse this
16217978SPeter.Dunlap@Sun.COM 	 * code doesn't wait on the refcnts during normal operation.
16227978SPeter.Dunlap@Sun.COM 	 * idm_task_find() is very fast and it is not a huge burden if we
16237978SPeter.Dunlap@Sun.COM 	 * have to do it twice.
16247978SPeter.Dunlap@Sun.COM 	 */
16257978SPeter.Dunlap@Sun.COM 	task = idm_task_find(ic, bhs->itt, bhs->ttt);
16267978SPeter.Dunlap@Sun.COM 	if (task == NULL) {
16277978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
16287978SPeter.Dunlap@Sun.COM 		    "idm_sorecv_scsidata: could not find task");
16297978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16307978SPeter.Dunlap@Sun.COM 	}
16317978SPeter.Dunlap@Sun.COM 
16327978SPeter.Dunlap@Sun.COM 	mutex_enter(&task->idt_mutex);
16337978SPeter.Dunlap@Sun.COM 	buflst	= (opcode == ISCSI_OP_SCSI_DATA_RSP) ?
16347978SPeter.Dunlap@Sun.COM 	    &task->idt_inbufv : &task->idt_outbufv;
16357978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = idm_buf_find(buflst, offset);
16367978SPeter.Dunlap@Sun.COM 	mutex_exit(&task->idt_mutex);
16377978SPeter.Dunlap@Sun.COM 
16387978SPeter.Dunlap@Sun.COM 	if (pdu->isp_sorx_buf == NULL) {
16397978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
16407978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find "
16417978SPeter.Dunlap@Sun.COM 		    "buffer for offset %x opcode=%x",
16427978SPeter.Dunlap@Sun.COM 		    offset, opcode);
16437978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16447978SPeter.Dunlap@Sun.COM 	}
16457978SPeter.Dunlap@Sun.COM 
16467978SPeter.Dunlap@Sun.COM 	xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength);
16477978SPeter.Dunlap@Sun.COM 	ASSERT(xfer_bytes != 0);
16487978SPeter.Dunlap@Sun.COM 	if (xfer_bytes != dlength) {
16497978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
16507978SPeter.Dunlap@Sun.COM 		/*
16517978SPeter.Dunlap@Sun.COM 		 * Buffer overflow, connection error.  The PDU data is still
16527978SPeter.Dunlap@Sun.COM 		 * sitting in the socket so we can't use the connection
16537978SPeter.Dunlap@Sun.COM 		 * again until that data is drained.
16547978SPeter.Dunlap@Sun.COM 		 */
16557978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
16567978SPeter.Dunlap@Sun.COM 	}
16577978SPeter.Dunlap@Sun.COM 
16587978SPeter.Dunlap@Sun.COM 	status = idm_sorecvdata(ic, pdu);
16597978SPeter.Dunlap@Sun.COM 
16607978SPeter.Dunlap@Sun.COM 	idm_task_rele(task);
16617978SPeter.Dunlap@Sun.COM 
16627978SPeter.Dunlap@Sun.COM 	return (status);
16637978SPeter.Dunlap@Sun.COM }
16647978SPeter.Dunlap@Sun.COM 
16657978SPeter.Dunlap@Sun.COM static uint32_t
16667978SPeter.Dunlap@Sun.COM idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength)
16677978SPeter.Dunlap@Sun.COM {
16687978SPeter.Dunlap@Sun.COM 	uint32_t	buf_ro = ro - idb->idb_bufoffset;
16697978SPeter.Dunlap@Sun.COM 	uint32_t	xfer_len = min(dlength, idb->idb_buflen - buf_ro);
16707978SPeter.Dunlap@Sun.COM 
16717978SPeter.Dunlap@Sun.COM 	ASSERT(ro >= idb->idb_bufoffset);
16727978SPeter.Dunlap@Sun.COM 
16737978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_base	=
16747978SPeter.Dunlap@Sun.COM 	    (caddr_t)idb->idb_buf + buf_ro;
16757978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_len	= xfer_len;
16767978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen++;
16777978SPeter.Dunlap@Sun.COM 
16787978SPeter.Dunlap@Sun.COM 	return (xfer_len);
16797978SPeter.Dunlap@Sun.COM }
16807978SPeter.Dunlap@Sun.COM 
16817978SPeter.Dunlap@Sun.COM int
16827978SPeter.Dunlap@Sun.COM idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu)
16837978SPeter.Dunlap@Sun.COM {
16847978SPeter.Dunlap@Sun.COM 	pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP);
16857978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_data != NULL);
16867978SPeter.Dunlap@Sun.COM 
16877978SPeter.Dunlap@Sun.COM 	pdu->isp_databuflen = pdu->isp_datalen;
16887978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data;
16897978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_len = pdu->isp_datalen;
16907978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 1;
16917978SPeter.Dunlap@Sun.COM 	/*
16927978SPeter.Dunlap@Sun.COM 	 * Since we are associating a new data buffer with this received
16937978SPeter.Dunlap@Sun.COM 	 * PDU we need to set a specific callback to free the data
16947978SPeter.Dunlap@Sun.COM 	 * after the PDU is processed.
16957978SPeter.Dunlap@Sun.COM 	 */
16967978SPeter.Dunlap@Sun.COM 	pdu->isp_flags |= IDM_PDU_ADDL_DATA;
16977978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_addl_pdu_cb;
16987978SPeter.Dunlap@Sun.COM 
16997978SPeter.Dunlap@Sun.COM 	return (idm_sorecvdata(ic, pdu));
17007978SPeter.Dunlap@Sun.COM }
17017978SPeter.Dunlap@Sun.COM 
17027978SPeter.Dunlap@Sun.COM void
17037978SPeter.Dunlap@Sun.COM idm_sorx_thread(void *arg)
17047978SPeter.Dunlap@Sun.COM {
17057978SPeter.Dunlap@Sun.COM 	boolean_t	conn_failure = B_FALSE;
17067978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = (idm_conn_t *)arg;
17077978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
17087978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu;
17097978SPeter.Dunlap@Sun.COM 	idm_status_t	rc;
17107978SPeter.Dunlap@Sun.COM 
17117978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
17127978SPeter.Dunlap@Sun.COM 
17137978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
17147978SPeter.Dunlap@Sun.COM 
17157978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
17167978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_TRUE;
17177978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did;
17187978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
17197978SPeter.Dunlap@Sun.COM 
17207978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_rx_thread_running) {
17217978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_mutex);
17227978SPeter.Dunlap@Sun.COM 
17237978SPeter.Dunlap@Sun.COM 		/*
17247978SPeter.Dunlap@Sun.COM 		 * Get PDU with default header size (large enough for
17257978SPeter.Dunlap@Sun.COM 		 * BHS plus any anticipated AHS).  PDU from
17267978SPeter.Dunlap@Sun.COM 		 * the cache will have all values set correctly
17277978SPeter.Dunlap@Sun.COM 		 * for sockets RX including callback.
17287978SPeter.Dunlap@Sun.COM 		 */
17297978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP);
17307978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
17317978SPeter.Dunlap@Sun.COM 		pdu->isp_flags = 0;
17327978SPeter.Dunlap@Sun.COM 		pdu->isp_transport_hdrlen = 0;
17337978SPeter.Dunlap@Sun.COM 
17347978SPeter.Dunlap@Sun.COM 		if ((rc = idm_sorecvhdr(ic, pdu)) != 0) {
17357978SPeter.Dunlap@Sun.COM 			/*
17367978SPeter.Dunlap@Sun.COM 			 * Call idm_pdu_complete so that we call the callback
17377978SPeter.Dunlap@Sun.COM 			 * and ensure any memory allocated in idm_sorecvhdr
17387978SPeter.Dunlap@Sun.COM 			 * gets freed up.
17397978SPeter.Dunlap@Sun.COM 			 */
17407978SPeter.Dunlap@Sun.COM 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
17417978SPeter.Dunlap@Sun.COM 
17427978SPeter.Dunlap@Sun.COM 			/*
17437978SPeter.Dunlap@Sun.COM 			 * If ic_rx_thread_running is still set then
17447978SPeter.Dunlap@Sun.COM 			 * this is some kind of connection problem
17457978SPeter.Dunlap@Sun.COM 			 * on the socket.  In this case we want to
17467978SPeter.Dunlap@Sun.COM 			 * generate an event.  Otherwise some other
17477978SPeter.Dunlap@Sun.COM 			 * thread closed the socket due to another
17487978SPeter.Dunlap@Sun.COM 			 * issue in which case we don't need to
17497978SPeter.Dunlap@Sun.COM 			 * generate an event.
17507978SPeter.Dunlap@Sun.COM 			 */
17517978SPeter.Dunlap@Sun.COM 			mutex_enter(&ic->ic_mutex);
17527978SPeter.Dunlap@Sun.COM 			if (so_conn->ic_rx_thread_running) {
17537978SPeter.Dunlap@Sun.COM 				conn_failure = B_TRUE;
17547978SPeter.Dunlap@Sun.COM 				so_conn->ic_rx_thread_running = B_FALSE;
17557978SPeter.Dunlap@Sun.COM 			}
17567978SPeter.Dunlap@Sun.COM 
17577978SPeter.Dunlap@Sun.COM 			continue;
17587978SPeter.Dunlap@Sun.COM 		}
17597978SPeter.Dunlap@Sun.COM 
17607978SPeter.Dunlap@Sun.COM 		/*
17617978SPeter.Dunlap@Sun.COM 		 * Header has been read and validated.  Now we need
17627978SPeter.Dunlap@Sun.COM 		 * to read the PDU data payload (if present).  SCSI data
17637978SPeter.Dunlap@Sun.COM 		 * need to be transferred from the socket directly into
17647978SPeter.Dunlap@Sun.COM 		 * the associated transfer buffer for the SCSI task.
17657978SPeter.Dunlap@Sun.COM 		 */
17667978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen != 0) {
17677978SPeter.Dunlap@Sun.COM 			if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) ||
17687978SPeter.Dunlap@Sun.COM 			    (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) {
17697978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_scsidata(ic, pdu);
17707978SPeter.Dunlap@Sun.COM 				/*
17717978SPeter.Dunlap@Sun.COM 				 * All SCSI errors are fatal to the
17727978SPeter.Dunlap@Sun.COM 				 * connection right now since we have no
17737978SPeter.Dunlap@Sun.COM 				 * place to put the data.  What we need
17747978SPeter.Dunlap@Sun.COM 				 * is some kind of sink to dispose of unwanted
17757978SPeter.Dunlap@Sun.COM 				 * SCSI data.  For example an invalid task tag
17767978SPeter.Dunlap@Sun.COM 				 * should not kill the connection (although
17777978SPeter.Dunlap@Sun.COM 				 * we may want to drop the connection).
17787978SPeter.Dunlap@Sun.COM 				 */
17797978SPeter.Dunlap@Sun.COM 			} else {
17807978SPeter.Dunlap@Sun.COM 				/*
17817978SPeter.Dunlap@Sun.COM 				 * Not data PDUs so allocate a buffer for the
17827978SPeter.Dunlap@Sun.COM 				 * data segment and read the remaining data.
17837978SPeter.Dunlap@Sun.COM 				 */
17847978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_nonscsidata(ic, pdu);
17857978SPeter.Dunlap@Sun.COM 			}
17867978SPeter.Dunlap@Sun.COM 			if (rc != 0) {
17877978SPeter.Dunlap@Sun.COM 				/*
17887978SPeter.Dunlap@Sun.COM 				 * Call idm_pdu_complete so that we call the
17897978SPeter.Dunlap@Sun.COM 				 * callback and ensure any memory allocated
17907978SPeter.Dunlap@Sun.COM 				 * in idm_sorecvhdr gets freed up.
17917978SPeter.Dunlap@Sun.COM 				 */
17927978SPeter.Dunlap@Sun.COM 				idm_pdu_complete(pdu, IDM_STATUS_FAIL);
17937978SPeter.Dunlap@Sun.COM 
17947978SPeter.Dunlap@Sun.COM 				/*
17957978SPeter.Dunlap@Sun.COM 				 * If ic_rx_thread_running is still set then
17967978SPeter.Dunlap@Sun.COM 				 * this is some kind of connection problem
17977978SPeter.Dunlap@Sun.COM 				 * on the socket.  In this case we want to
17987978SPeter.Dunlap@Sun.COM 				 * generate an event.  Otherwise some other
17997978SPeter.Dunlap@Sun.COM 				 * thread closed the socket due to another
18007978SPeter.Dunlap@Sun.COM 				 * issue in which case we don't need to
18017978SPeter.Dunlap@Sun.COM 				 * generate an event.
18027978SPeter.Dunlap@Sun.COM 				 */
18037978SPeter.Dunlap@Sun.COM 				mutex_enter(&ic->ic_mutex);
18047978SPeter.Dunlap@Sun.COM 				if (so_conn->ic_rx_thread_running) {
18057978SPeter.Dunlap@Sun.COM 					conn_failure = B_TRUE;
18067978SPeter.Dunlap@Sun.COM 					so_conn->ic_rx_thread_running = B_FALSE;
18077978SPeter.Dunlap@Sun.COM 				}
18087978SPeter.Dunlap@Sun.COM 				continue;
18097978SPeter.Dunlap@Sun.COM 			}
18107978SPeter.Dunlap@Sun.COM 		}
18117978SPeter.Dunlap@Sun.COM 
18127978SPeter.Dunlap@Sun.COM 		/*
18137978SPeter.Dunlap@Sun.COM 		 * Process RX PDU
18147978SPeter.Dunlap@Sun.COM 		 */
18157978SPeter.Dunlap@Sun.COM 		idm_pdu_rx(ic, pdu);
18167978SPeter.Dunlap@Sun.COM 
18177978SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_mutex);
18187978SPeter.Dunlap@Sun.COM 	}
18197978SPeter.Dunlap@Sun.COM 
18207978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
18217978SPeter.Dunlap@Sun.COM 
18227978SPeter.Dunlap@Sun.COM 	/*
18237978SPeter.Dunlap@Sun.COM 	 * If we dropped out of the RX processing loop because of
18247978SPeter.Dunlap@Sun.COM 	 * a socket problem or other connection failure (including
18257978SPeter.Dunlap@Sun.COM 	 * digest errors) then we need to generate a state machine
18267978SPeter.Dunlap@Sun.COM 	 * event to shut the connection down.
18277978SPeter.Dunlap@Sun.COM 	 * If the state machine is already in, for example, INIT_ERROR, this
18287978SPeter.Dunlap@Sun.COM 	 * event will get dropped, and the TX thread will never be notified
18297978SPeter.Dunlap@Sun.COM 	 * to shut down.  To be safe, we'll just notify it here.
18307978SPeter.Dunlap@Sun.COM 	 */
18317978SPeter.Dunlap@Sun.COM 	if (conn_failure) {
18327978SPeter.Dunlap@Sun.COM 		if (so_conn->ic_tx_thread_running) {
18337978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
18347978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
18357978SPeter.Dunlap@Sun.COM 			cv_signal(&so_conn->ic_tx_cv);
18367978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
18377978SPeter.Dunlap@Sun.COM 		}
18387978SPeter.Dunlap@Sun.COM 
18397978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_TRANSPORT_FAIL, rc);
18407978SPeter.Dunlap@Sun.COM 	}
18417978SPeter.Dunlap@Sun.COM 
18427978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
18437978SPeter.Dunlap@Sun.COM 
18447978SPeter.Dunlap@Sun.COM 	thread_exit();
18457978SPeter.Dunlap@Sun.COM }
18467978SPeter.Dunlap@Sun.COM 
18477978SPeter.Dunlap@Sun.COM /*
18487978SPeter.Dunlap@Sun.COM  * idm_so_tx
18497978SPeter.Dunlap@Sun.COM  *
18507978SPeter.Dunlap@Sun.COM  * This is the implementation of idm_transport_ops_t's it_tx_pdu entry
18517978SPeter.Dunlap@Sun.COM  * point.  By definition, it is supposed to be fast.  So, simply queue
18527978SPeter.Dunlap@Sun.COM  * the entry and return.  The real work is done by idm_i_so_tx() via
18537978SPeter.Dunlap@Sun.COM  * idm_sotx_thread().
18547978SPeter.Dunlap@Sun.COM  */
18557978SPeter.Dunlap@Sun.COM 
18567978SPeter.Dunlap@Sun.COM static void
18577978SPeter.Dunlap@Sun.COM idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu)
18587978SPeter.Dunlap@Sun.COM {
18597978SPeter.Dunlap@Sun.COM 	idm_so_conn_t *so_conn = ic->ic_transport_private;
18607978SPeter.Dunlap@Sun.COM 
18617978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_ic == ic);
18627978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
18637978SPeter.Dunlap@Sun.COM 
18647978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
18657978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
18667978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_ABORTED);
18677978SPeter.Dunlap@Sun.COM 		return;
18687978SPeter.Dunlap@Sun.COM 	}
18697978SPeter.Dunlap@Sun.COM 
18707978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)pdu);
18717978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
18727978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
18737978SPeter.Dunlap@Sun.COM }
18747978SPeter.Dunlap@Sun.COM 
18757978SPeter.Dunlap@Sun.COM static idm_status_t
18767978SPeter.Dunlap@Sun.COM idm_i_so_tx(idm_pdu_t *pdu)
18777978SPeter.Dunlap@Sun.COM {
18787978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = pdu->isp_ic;
18797978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
18807978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
18817978SPeter.Dunlap@Sun.COM 	int		pad_len;
18827978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
18837978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc = 0;
18847978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
18857978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
18867978SPeter.Dunlap@Sun.COM 	struct iovec	iov[6];
18877978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
18887978SPeter.Dunlap@Sun.COM 
18897978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
18907978SPeter.Dunlap@Sun.COM 
18917978SPeter.Dunlap@Sun.COM 	/* Setup BHS */
18927978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_base	= (caddr_t)pdu->isp_hdr;
18937978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_len	= pdu->isp_hdrlen;
18947978SPeter.Dunlap@Sun.COM 	total_len		+= iov[iovlen].iov_len;
18957978SPeter.Dunlap@Sun.COM 	iovlen++;
18967978SPeter.Dunlap@Sun.COM 
18977978SPeter.Dunlap@Sun.COM 	/* Setup header digest */
18987978SPeter.Dunlap@Sun.COM 	if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
18997978SPeter.Dunlap@Sun.COM 	    (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) {
19007978SPeter.Dunlap@Sun.COM 		hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen);
19017978SPeter.Dunlap@Sun.COM 
19027978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&hdr_digest_crc;
19037978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (hdr_digest_crc);
19047978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
19057978SPeter.Dunlap@Sun.COM 		iovlen++;
19067978SPeter.Dunlap@Sun.COM 	}
19077978SPeter.Dunlap@Sun.COM 
19087978SPeter.Dunlap@Sun.COM 	/* Setup the data */
19097978SPeter.Dunlap@Sun.COM 	if (pdu->isp_datalen) {
19107978SPeter.Dunlap@Sun.COM 		idm_task_t		*idt;
19117978SPeter.Dunlap@Sun.COM 		idm_buf_t		*idb;
19127978SPeter.Dunlap@Sun.COM 		iscsi_data_hdr_t	*ihp;
19137978SPeter.Dunlap@Sun.COM 		ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
19147978SPeter.Dunlap@Sun.COM 		/* Write of immediate data */
19157978SPeter.Dunlap@Sun.COM 		if (ic->ic_ffp &&
19167978SPeter.Dunlap@Sun.COM 		    (ihp->opcode == ISCSI_OP_SCSI_CMD ||
19177978SPeter.Dunlap@Sun.COM 		    ihp->opcode == ISCSI_OP_SCSI_DATA)) {
19187978SPeter.Dunlap@Sun.COM 			idt = idm_task_find(ic, ihp->itt, ihp->ttt);
19197978SPeter.Dunlap@Sun.COM 			if (idt) {
19207978SPeter.Dunlap@Sun.COM 				mutex_enter(&idt->idt_mutex);
19217978SPeter.Dunlap@Sun.COM 				idb = idm_buf_find(&idt->idt_outbufv, 0);
19227978SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
19237978SPeter.Dunlap@Sun.COM 				idb->idb_xfer_len += pdu->isp_datalen;
19247978SPeter.Dunlap@Sun.COM 			}
19257978SPeter.Dunlap@Sun.COM 		}
19267978SPeter.Dunlap@Sun.COM 
19277978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)pdu->isp_data;
19287978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pdu->isp_datalen;
19297978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
19307978SPeter.Dunlap@Sun.COM 		iovlen++;
19317978SPeter.Dunlap@Sun.COM 	}
19327978SPeter.Dunlap@Sun.COM 
19337978SPeter.Dunlap@Sun.COM 	/* Setup the data pad if necessary */
19347978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
19357978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
19367978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
19377978SPeter.Dunlap@Sun.COM 
19387978SPeter.Dunlap@Sun.COM 	if (pad_len) {
19397978SPeter.Dunlap@Sun.COM 		bzero(pad, sizeof (pad));
19407978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (void *)&pad;
19417978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pad_len;
19427978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
19437978SPeter.Dunlap@Sun.COM 		iovlen++;
19447978SPeter.Dunlap@Sun.COM 	}
19457978SPeter.Dunlap@Sun.COM 
19467978SPeter.Dunlap@Sun.COM 	/*
19477978SPeter.Dunlap@Sun.COM 	 * Setup the data digest if enabled.  Data-digest is not sent
19487978SPeter.Dunlap@Sun.COM 	 * for login-phase PDUs.
19497978SPeter.Dunlap@Sun.COM 	 */
19507978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) &&
19517978SPeter.Dunlap@Sun.COM 	    ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
19527978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen || pad_len)) {
19537978SPeter.Dunlap@Sun.COM 		/*
19547978SPeter.Dunlap@Sun.COM 		 * RFC3720/10.2.3: A zero-length Data Segment also
19557978SPeter.Dunlap@Sun.COM 		 * implies a zero-length data digest.
19567978SPeter.Dunlap@Sun.COM 		 */
19577978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen) {
19587978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c(pdu->isp_data,
19597978SPeter.Dunlap@Sun.COM 			    pdu->isp_datalen);
19607978SPeter.Dunlap@Sun.COM 		}
19617978SPeter.Dunlap@Sun.COM 		if (pad_len) {
19627978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c_continued(&pad,
19637978SPeter.Dunlap@Sun.COM 			    pad_len, data_digest_crc);
19647978SPeter.Dunlap@Sun.COM 		}
19657978SPeter.Dunlap@Sun.COM 
19667978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&data_digest_crc;
19677978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (data_digest_crc);
19687978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
19697978SPeter.Dunlap@Sun.COM 		iovlen++;
19707978SPeter.Dunlap@Sun.COM 	}
19717978SPeter.Dunlap@Sun.COM 
19727978SPeter.Dunlap@Sun.COM 	/* Transmit the PDU */
19737978SPeter.Dunlap@Sun.COM 	if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen,
19747978SPeter.Dunlap@Sun.COM 	    total_len) != 0) {
19757978SPeter.Dunlap@Sun.COM 		/* Set error status */
19767978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
19777978SPeter.Dunlap@Sun.COM 		    "idm_so_tx: failed to transmit the PDU, so: %p ic: %p "
19787978SPeter.Dunlap@Sun.COM 		    "data: %p", (void *) so_conn->ic_so, (void *) ic,
19797978SPeter.Dunlap@Sun.COM 		    (void *) pdu->isp_data);
19807978SPeter.Dunlap@Sun.COM 		status = IDM_STATUS_IO;
19817978SPeter.Dunlap@Sun.COM 	}
19827978SPeter.Dunlap@Sun.COM 
19837978SPeter.Dunlap@Sun.COM 	/*
19847978SPeter.Dunlap@Sun.COM 	 * Success does not mean that the PDU actually reached the
19857978SPeter.Dunlap@Sun.COM 	 * remote node since it could get dropped along the way.
19867978SPeter.Dunlap@Sun.COM 	 */
19877978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, status);
19887978SPeter.Dunlap@Sun.COM 
19897978SPeter.Dunlap@Sun.COM 	return (status);
19907978SPeter.Dunlap@Sun.COM }
19917978SPeter.Dunlap@Sun.COM 
19927978SPeter.Dunlap@Sun.COM /*
19937978SPeter.Dunlap@Sun.COM  * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the
19947978SPeter.Dunlap@Sun.COM  * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength,
19957978SPeter.Dunlap@Sun.COM  * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN.
19967978SPeter.Dunlap@Sun.COM  * A target can invoke this function multiple times for a single read command
19977978SPeter.Dunlap@Sun.COM  * (identified by the same ITT) to split the input into several sequences.
19987978SPeter.Dunlap@Sun.COM  *
19997978SPeter.Dunlap@Sun.COM  * DataSN starts with 0 for the first data PDU of an input command and advances
20007978SPeter.Dunlap@Sun.COM  * by 1 for each subsequent data PDU. Each sequence will have its own F bit,
20017978SPeter.Dunlap@Sun.COM  * which is set to 1 for the last data PDU of a sequence.
20027978SPeter.Dunlap@Sun.COM  *
20037978SPeter.Dunlap@Sun.COM  * Scope for Prototype build:
20047978SPeter.Dunlap@Sun.COM  * The data PDUs within a sequence will be sent in order with the buffer offset
20057978SPeter.Dunlap@Sun.COM  * in increasing order. i.e. initiator and target must have negotiated the
20067978SPeter.Dunlap@Sun.COM  * "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
20077978SPeter.Dunlap@Sun.COM  *
20087978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
20097978SPeter.Dunlap@Sun.COM  */
20107978SPeter.Dunlap@Sun.COM static idm_status_t
20117978SPeter.Dunlap@Sun.COM idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb)
20127978SPeter.Dunlap@Sun.COM {
20137978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = idb->idb_ic->ic_transport_private;
20147978SPeter.Dunlap@Sun.COM 	idm_pdu_t	tmppdu;
20157978SPeter.Dunlap@Sun.COM 
20167978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
20177978SPeter.Dunlap@Sun.COM 
20187978SPeter.Dunlap@Sun.COM 	/*
20197978SPeter.Dunlap@Sun.COM 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
20207978SPeter.Dunlap@Sun.COM 	 * idm_sotx_thread.
20217978SPeter.Dunlap@Sun.COM 	 */
20227978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
20237978SPeter.Dunlap@Sun.COM 
20247978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
20257978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
20267978SPeter.Dunlap@Sun.COM 		/*
20277978SPeter.Dunlap@Sun.COM 		 * Don't release idt->idt_mutex since we're supposed to hold
20287978SPeter.Dunlap@Sun.COM 		 * in when calling idm_buf_tx_to_ini_done
20297978SPeter.Dunlap@Sun.COM 		 */
20307978SPeter.Dunlap@Sun.COM 		idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
20317978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
20327978SPeter.Dunlap@Sun.COM 	}
20337978SPeter.Dunlap@Sun.COM 
20347978SPeter.Dunlap@Sun.COM 	/*
20357978SPeter.Dunlap@Sun.COM 	 * Build a template for the data PDU headers we will use so that
20367978SPeter.Dunlap@Sun.COM 	 * the SN values will stay consistent with other PDU's we are
20377978SPeter.Dunlap@Sun.COM 	 * transmitting like R2T and SCSI status.
20387978SPeter.Dunlap@Sun.COM 	 */
20397978SPeter.Dunlap@Sun.COM 	bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
20407978SPeter.Dunlap@Sun.COM 	tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl;
20417978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
20427978SPeter.Dunlap@Sun.COM 	    ISCSI_OP_SCSI_DATA_RSP);
20437978SPeter.Dunlap@Sun.COM 	idb->idb_tx_thread = B_TRUE;
20447978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)idb);
20457978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
20467978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
20477978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
20487978SPeter.Dunlap@Sun.COM 
20497978SPeter.Dunlap@Sun.COM 	/*
20507978SPeter.Dunlap@Sun.COM 	 * Returning success here indicates the transfer was successfully
20517978SPeter.Dunlap@Sun.COM 	 * dispatched -- it does not mean that the transfer completed
20527978SPeter.Dunlap@Sun.COM 	 * successfully.
20537978SPeter.Dunlap@Sun.COM 	 */
20547978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
20557978SPeter.Dunlap@Sun.COM }
20567978SPeter.Dunlap@Sun.COM 
20577978SPeter.Dunlap@Sun.COM /*
20587978SPeter.Dunlap@Sun.COM  * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the
20597978SPeter.Dunlap@Sun.COM  * data blocks it is ready to receive from the initiator in response to a WRITE
20607978SPeter.Dunlap@Sun.COM  * SCSI command. The target iSCSI layer passes the information about the desired
20617978SPeter.Dunlap@Sun.COM  * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer
20627978SPeter.Dunlap@Sun.COM  * offset and datalen are passed via the 'idb' argument.
20637978SPeter.Dunlap@Sun.COM  *
20647978SPeter.Dunlap@Sun.COM  * Scope for Prototype build:
20657978SPeter.Dunlap@Sun.COM  * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have
20667978SPeter.Dunlap@Sun.COM  * negotiated the "InitialR2T" to "Yes".
20677978SPeter.Dunlap@Sun.COM  *
20687978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
20697978SPeter.Dunlap@Sun.COM  */
20707978SPeter.Dunlap@Sun.COM static idm_status_t
20717978SPeter.Dunlap@Sun.COM idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
20727978SPeter.Dunlap@Sun.COM {
20737978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
20747978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt;
20757978SPeter.Dunlap@Sun.COM 
20767978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
20777978SPeter.Dunlap@Sun.COM 
20787978SPeter.Dunlap@Sun.COM 	pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
20797978SPeter.Dunlap@Sun.COM 	pdu->isp_ic = idt->idt_ic;
20807978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
20817978SPeter.Dunlap@Sun.COM 
20827978SPeter.Dunlap@Sun.COM 	/* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */
20837978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP);
20847978SPeter.Dunlap@Sun.COM 
20857978SPeter.Dunlap@Sun.COM 	/* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */
20867978SPeter.Dunlap@Sun.COM 	rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr);
20877978SPeter.Dunlap@Sun.COM 
20887978SPeter.Dunlap@Sun.COM 	rtt->opcode		= ISCSI_OP_RTT_RSP;
20897978SPeter.Dunlap@Sun.COM 	rtt->flags		= ISCSI_FLAG_FINAL;
20907978SPeter.Dunlap@Sun.COM 	rtt->data_offset	= htonl(idb->idb_bufoffset);
20917978SPeter.Dunlap@Sun.COM 	rtt->data_length	= htonl(idb->idb_xfer_len);
20927978SPeter.Dunlap@Sun.COM 	rtt->rttsn		= htonl(idt->idt_exp_rttsn++);
20937978SPeter.Dunlap@Sun.COM 
20947978SPeter.Dunlap@Sun.COM 	/* Keep track of buffer offsets */
20957978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset	= idb->idb_bufoffset;
20967978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
20977978SPeter.Dunlap@Sun.COM 
20987978SPeter.Dunlap@Sun.COM 	/*
2099*8607SJames.Moore@Sun.COM 	 * Transmit the PDU.
21007978SPeter.Dunlap@Sun.COM 	 */
2101*8607SJames.Moore@Sun.COM 	idm_pdu_tx(pdu);
21027978SPeter.Dunlap@Sun.COM 
21037978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21047978SPeter.Dunlap@Sun.COM }
21057978SPeter.Dunlap@Sun.COM 
21067978SPeter.Dunlap@Sun.COM static idm_status_t
21077978SPeter.Dunlap@Sun.COM idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen)
21087978SPeter.Dunlap@Sun.COM {
21097978SPeter.Dunlap@Sun.COM 	idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP);
21107978SPeter.Dunlap@Sun.COM 	if (idb->idb_buf == NULL) {
21117978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE,
21127978SPeter.Dunlap@Sun.COM 		    "idm_so_buf_alloc: failed buffer allocation");
21137978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
21147978SPeter.Dunlap@Sun.COM 	}
21157978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21167978SPeter.Dunlap@Sun.COM }
21177978SPeter.Dunlap@Sun.COM 
21187978SPeter.Dunlap@Sun.COM /* ARGSUSED */
21197978SPeter.Dunlap@Sun.COM static idm_status_t
21207978SPeter.Dunlap@Sun.COM idm_so_buf_setup(idm_buf_t *idb)
21217978SPeter.Dunlap@Sun.COM {
21227978SPeter.Dunlap@Sun.COM 	/* nothing to do here */
21237978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
21247978SPeter.Dunlap@Sun.COM }
21257978SPeter.Dunlap@Sun.COM 
21267978SPeter.Dunlap@Sun.COM /* ARGSUSED */
21277978SPeter.Dunlap@Sun.COM static void
21287978SPeter.Dunlap@Sun.COM idm_so_buf_teardown(idm_buf_t *idb)
21297978SPeter.Dunlap@Sun.COM {
21307978SPeter.Dunlap@Sun.COM 	/* nothing to do here */
21317978SPeter.Dunlap@Sun.COM }
21327978SPeter.Dunlap@Sun.COM 
21337978SPeter.Dunlap@Sun.COM static void
21347978SPeter.Dunlap@Sun.COM idm_so_buf_free(idm_buf_t *idb)
21357978SPeter.Dunlap@Sun.COM {
21367978SPeter.Dunlap@Sun.COM 	kmem_free(idb->idb_buf, idb->idb_buflen);
21377978SPeter.Dunlap@Sun.COM }
21387978SPeter.Dunlap@Sun.COM 
21397978SPeter.Dunlap@Sun.COM idm_status_t
21407978SPeter.Dunlap@Sun.COM idm_so_send_buf_region(idm_task_t *idt, uint8_t opcode, idm_buf_t *idb,
21417978SPeter.Dunlap@Sun.COM     uint32_t buf_region_offset, uint32_t buf_region_length)
21427978SPeter.Dunlap@Sun.COM {
21437978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
21447978SPeter.Dunlap@Sun.COM 	uint32_t		max_dataseglen;
21457978SPeter.Dunlap@Sun.COM 	size_t			remainder, chunk;
21467978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset = buf_region_offset;
21477978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
21487978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
21497978SPeter.Dunlap@Sun.COM 
21507978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
21517978SPeter.Dunlap@Sun.COM 
21527978SPeter.Dunlap@Sun.COM 	ic = idt->idt_ic;
21537978SPeter.Dunlap@Sun.COM 
21547978SPeter.Dunlap@Sun.COM 	max_dataseglen = 8192; /* Need value from login negotiation */
21557978SPeter.Dunlap@Sun.COM 	remainder = buf_region_length;
21567978SPeter.Dunlap@Sun.COM 
21577978SPeter.Dunlap@Sun.COM 	while (remainder) {
21587978SPeter.Dunlap@Sun.COM 		if (idt->idt_state != TASK_ACTIVE) {
21597978SPeter.Dunlap@Sun.COM 			ASSERT((idt->idt_state != TASK_IDLE) &&
21607978SPeter.Dunlap@Sun.COM 			    (idt->idt_state != TASK_COMPLETE));
21617978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_ABORTED);
21627978SPeter.Dunlap@Sun.COM 		}
21637978SPeter.Dunlap@Sun.COM 
21647978SPeter.Dunlap@Sun.COM 		/* check to see if we need to chunk the data */
21657978SPeter.Dunlap@Sun.COM 		if (remainder > max_dataseglen) {
21667978SPeter.Dunlap@Sun.COM 			chunk = max_dataseglen;
21677978SPeter.Dunlap@Sun.COM 		} else {
21687978SPeter.Dunlap@Sun.COM 			chunk = remainder;
21697978SPeter.Dunlap@Sun.COM 		}
21707978SPeter.Dunlap@Sun.COM 
21717978SPeter.Dunlap@Sun.COM 		/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
21727978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
21737978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
21747978SPeter.Dunlap@Sun.COM 
21757978SPeter.Dunlap@Sun.COM 		/*
21767978SPeter.Dunlap@Sun.COM 		 * For target we've already built a build a header template
21777978SPeter.Dunlap@Sun.COM 		 * to use during the transfer.  Use this template so that
21787978SPeter.Dunlap@Sun.COM 		 * the SN values stay consistent with any unrelated PDU's
21797978SPeter.Dunlap@Sun.COM 		 * being transmitted.
21807978SPeter.Dunlap@Sun.COM 		 */
21817978SPeter.Dunlap@Sun.COM 		if (opcode == ISCSI_OP_SCSI_DATA_RSP) {
21827978SPeter.Dunlap@Sun.COM 			bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr,
21837978SPeter.Dunlap@Sun.COM 			    sizeof (iscsi_hdr_t));
21847978SPeter.Dunlap@Sun.COM 		} else {
21857978SPeter.Dunlap@Sun.COM 			/*
21867978SPeter.Dunlap@Sun.COM 			 * OK for now, but we should remove this bzero and
21877978SPeter.Dunlap@Sun.COM 			 * make sure the build_hdr function is initializing the
21887978SPeter.Dunlap@Sun.COM 			 * header properly
21897978SPeter.Dunlap@Sun.COM 			 */
21907978SPeter.Dunlap@Sun.COM 			bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
21917978SPeter.Dunlap@Sun.COM 
21927978SPeter.Dunlap@Sun.COM 			/*
21937978SPeter.Dunlap@Sun.COM 			 * setup iscsi data hdr
21947978SPeter.Dunlap@Sun.COM 			 * callback to the iSCSI layer to fill in the BHS
21957978SPeter.Dunlap@Sun.COM 			 * CmdSN, StatSN, ExpCmdSN, MaxCmdSN, TTT, ITT and
21967978SPeter.Dunlap@Sun.COM 			 * opcode
21977978SPeter.Dunlap@Sun.COM 			 */
21987978SPeter.Dunlap@Sun.COM 			(*ic->ic_conn_ops.icb_build_hdr)(idt, pdu, opcode);
21997978SPeter.Dunlap@Sun.COM 		}
22007978SPeter.Dunlap@Sun.COM 
22017978SPeter.Dunlap@Sun.COM 		/*
22027978SPeter.Dunlap@Sun.COM 		 * Set DataSN, data offset, and flags in BHS
22037978SPeter.Dunlap@Sun.COM 		 * For the prototype build, A = 0, S = 0, U = 0
22047978SPeter.Dunlap@Sun.COM 		 */
22057978SPeter.Dunlap@Sun.COM 		bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr);
22067978SPeter.Dunlap@Sun.COM 
22077978SPeter.Dunlap@Sun.COM 		bhs->datasn		= htonl(idt->idt_exp_datasn++);
22087978SPeter.Dunlap@Sun.COM 
22097978SPeter.Dunlap@Sun.COM 		hton24(bhs->dlength, chunk);
22107978SPeter.Dunlap@Sun.COM 		bhs->offset = htonl(idb->idb_bufoffset + data_offset);
22117978SPeter.Dunlap@Sun.COM 
22127978SPeter.Dunlap@Sun.COM 		if (chunk == remainder) {
22137978SPeter.Dunlap@Sun.COM 			bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
22147978SPeter.Dunlap@Sun.COM 		}
22157978SPeter.Dunlap@Sun.COM 
22167978SPeter.Dunlap@Sun.COM 		/* setup data */
22177978SPeter.Dunlap@Sun.COM 		pdu->isp_data	=  (uint8_t *)idb->idb_buf + data_offset;
22187978SPeter.Dunlap@Sun.COM 		pdu->isp_datalen = (uint_t)chunk;
22197978SPeter.Dunlap@Sun.COM 		remainder	-= chunk;
22207978SPeter.Dunlap@Sun.COM 		data_offset	+= chunk;
22217978SPeter.Dunlap@Sun.COM 
22227978SPeter.Dunlap@Sun.COM 		/*
22237978SPeter.Dunlap@Sun.COM 		 * Now that we're done working with idt_exp_datasn,
22247978SPeter.Dunlap@Sun.COM 		 * idt->idt_state and idb->idb_bufoffset we can release
22257978SPeter.Dunlap@Sun.COM 		 * the task lock -- don't want to hold it across the
22267978SPeter.Dunlap@Sun.COM 		 * call to idm_i_so_tx since we could block.
22277978SPeter.Dunlap@Sun.COM 		 */
22287978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
22297978SPeter.Dunlap@Sun.COM 
22307978SPeter.Dunlap@Sun.COM 		/*
22317978SPeter.Dunlap@Sun.COM 		 * Transmit the PDU.  Call the internal routine directly
22327978SPeter.Dunlap@Sun.COM 		 * as there is already implicit ordering.
22337978SPeter.Dunlap@Sun.COM 		 */
22347978SPeter.Dunlap@Sun.COM 		(void) idm_i_so_tx(pdu);
22357978SPeter.Dunlap@Sun.COM 
22367978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
22377978SPeter.Dunlap@Sun.COM 	}
22387978SPeter.Dunlap@Sun.COM 
22397978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
22407978SPeter.Dunlap@Sun.COM }
22417978SPeter.Dunlap@Sun.COM 
22427978SPeter.Dunlap@Sun.COM /*
22437978SPeter.Dunlap@Sun.COM  * TX PDU cache
22447978SPeter.Dunlap@Sun.COM  */
22457978SPeter.Dunlap@Sun.COM /* ARGSUSED */
22467978SPeter.Dunlap@Sun.COM int
22477978SPeter.Dunlap@Sun.COM idm_sotx_pdu_constructor(void *hdl, void *arg, int flags)
22487978SPeter.Dunlap@Sun.COM {
22497978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
22507978SPeter.Dunlap@Sun.COM 
22517978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
22527978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
22537978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
22547978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sotx_cache_pdu_cb;
22557978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
22567978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
22577978SPeter.Dunlap@Sun.COM 
22587978SPeter.Dunlap@Sun.COM 	return (0);
22597978SPeter.Dunlap@Sun.COM }
22607978SPeter.Dunlap@Sun.COM 
22617978SPeter.Dunlap@Sun.COM /* ARGSUSED */
22627978SPeter.Dunlap@Sun.COM void
22637978SPeter.Dunlap@Sun.COM idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
22647978SPeter.Dunlap@Sun.COM {
22657978SPeter.Dunlap@Sun.COM 	/* reset values between use */
22667978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
22677978SPeter.Dunlap@Sun.COM 
22687978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sotx_pdu_cache, pdu);
22697978SPeter.Dunlap@Sun.COM }
22707978SPeter.Dunlap@Sun.COM 
22717978SPeter.Dunlap@Sun.COM /*
22727978SPeter.Dunlap@Sun.COM  * RX PDU cache
22737978SPeter.Dunlap@Sun.COM  */
22747978SPeter.Dunlap@Sun.COM /* ARGSUSED */
22757978SPeter.Dunlap@Sun.COM int
22767978SPeter.Dunlap@Sun.COM idm_sorx_pdu_constructor(void *hdl, void *arg, int flags)
22777978SPeter.Dunlap@Sun.COM {
22787978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
22797978SPeter.Dunlap@Sun.COM 
22807978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
22817978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
22827978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
22837978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
22847978SPeter.Dunlap@Sun.COM 
22857978SPeter.Dunlap@Sun.COM 	return (0);
22867978SPeter.Dunlap@Sun.COM }
22877978SPeter.Dunlap@Sun.COM 
22887978SPeter.Dunlap@Sun.COM /* ARGSUSED */
22897978SPeter.Dunlap@Sun.COM static void
22907978SPeter.Dunlap@Sun.COM idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
22917978SPeter.Dunlap@Sun.COM {
22927978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 0;
22937978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
22947978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sorx_pdu_cache, pdu);
22957978SPeter.Dunlap@Sun.COM }
22967978SPeter.Dunlap@Sun.COM 
22977978SPeter.Dunlap@Sun.COM static void
22987978SPeter.Dunlap@Sun.COM idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
22997978SPeter.Dunlap@Sun.COM {
23007978SPeter.Dunlap@Sun.COM 	/*
23017978SPeter.Dunlap@Sun.COM 	 * We had to modify our cached RX PDU with a longer header buffer
23027978SPeter.Dunlap@Sun.COM 	 * and/or a longer data buffer.  Release the new buffers and fix
23037978SPeter.Dunlap@Sun.COM 	 * the fields back to what we would expect for a cached RX PDU.
23047978SPeter.Dunlap@Sun.COM 	 */
23057978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_HDR) {
23067978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_hdr, pdu->isp_hdrlen);
23077978SPeter.Dunlap@Sun.COM 	}
23087978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_DATA) {
23097978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_data, pdu->isp_datalen);
23107978SPeter.Dunlap@Sun.COM 	}
23117978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1);
23127978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
23137978SPeter.Dunlap@Sun.COM 	pdu->isp_data = NULL;
23147978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
23157978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
23167978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
23177978SPeter.Dunlap@Sun.COM 	idm_sorx_cache_pdu_cb(pdu, status);
23187978SPeter.Dunlap@Sun.COM }
23197978SPeter.Dunlap@Sun.COM 
23207978SPeter.Dunlap@Sun.COM /*
23217978SPeter.Dunlap@Sun.COM  * This thread is only active when I/O is queued for transmit
23227978SPeter.Dunlap@Sun.COM  * because the socket is busy.
23237978SPeter.Dunlap@Sun.COM  */
23247978SPeter.Dunlap@Sun.COM void
23257978SPeter.Dunlap@Sun.COM idm_sotx_thread(void *arg)
23267978SPeter.Dunlap@Sun.COM {
23277978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = arg;
23287978SPeter.Dunlap@Sun.COM 	idm_tx_obj_t	*object, *next;
23297978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
23307978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
23317978SPeter.Dunlap@Sun.COM 
23327978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
23337978SPeter.Dunlap@Sun.COM 
23347978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
23357978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
23367978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_TRUE;
23377978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did;
23387978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
23397978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
23407978SPeter.Dunlap@Sun.COM 
23417978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
23427978SPeter.Dunlap@Sun.COM 
23437978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_tx_thread_running) {
23447978SPeter.Dunlap@Sun.COM 		while (list_is_empty(&so_conn->ic_tx_list)) {
23457978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic);
23467978SPeter.Dunlap@Sun.COM 			cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex);
23477978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic);
23487978SPeter.Dunlap@Sun.COM 
23497978SPeter.Dunlap@Sun.COM 			if (!so_conn->ic_tx_thread_running) {
23507978SPeter.Dunlap@Sun.COM 				goto tx_bail;
23517978SPeter.Dunlap@Sun.COM 			}
23527978SPeter.Dunlap@Sun.COM 		}
23537978SPeter.Dunlap@Sun.COM 
23547978SPeter.Dunlap@Sun.COM 		object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
23557978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
23567978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
23577978SPeter.Dunlap@Sun.COM 
23587978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
23597978SPeter.Dunlap@Sun.COM 		case IDM_PDU_MAGIC:
23607978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
23617978SPeter.Dunlap@Sun.COM 			    idm_pdu_t *, (idm_pdu_t *)object);
23627978SPeter.Dunlap@Sun.COM 
23637978SPeter.Dunlap@Sun.COM 			status = idm_i_so_tx((idm_pdu_t *)object);
23647978SPeter.Dunlap@Sun.COM 			break;
23657978SPeter.Dunlap@Sun.COM 
23667978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
23677978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
23687978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
23697978SPeter.Dunlap@Sun.COM 
23707978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic,
23717978SPeter.Dunlap@Sun.COM 			    idm_buf_t *, idb);
23727978SPeter.Dunlap@Sun.COM 
23737978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
23747978SPeter.Dunlap@Sun.COM 			status = idm_so_send_buf_region(idt,
23757978SPeter.Dunlap@Sun.COM 			    ISCSI_OP_SCSI_DATA_RSP, idb, 0, idb->idb_xfer_len);
23767978SPeter.Dunlap@Sun.COM 
23777978SPeter.Dunlap@Sun.COM 			/*
23787978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
23797978SPeter.Dunlap@Sun.COM 			 * be "in transport"
23807978SPeter.Dunlap@Sun.COM 			 */
23817978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
23827978SPeter.Dunlap@Sun.COM 			/*
23837978SPeter.Dunlap@Sun.COM 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
23847978SPeter.Dunlap@Sun.COM 			 */
23857978SPeter.Dunlap@Sun.COM 			idm_buf_tx_to_ini_done(idt, idb, status);
23867978SPeter.Dunlap@Sun.COM 			break;
23877978SPeter.Dunlap@Sun.COM 		}
23887978SPeter.Dunlap@Sun.COM 
23897978SPeter.Dunlap@Sun.COM 		default:
23907978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic "
23917978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
23927978SPeter.Dunlap@Sun.COM 			status = IDM_STATUS_FAIL;
23937978SPeter.Dunlap@Sun.COM 		}
23947978SPeter.Dunlap@Sun.COM 
23957978SPeter.Dunlap@Sun.COM 		mutex_enter(&so_conn->ic_tx_mutex);
23967978SPeter.Dunlap@Sun.COM 
23977978SPeter.Dunlap@Sun.COM 		if (status != IDM_STATUS_SUCCESS) {
23987978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
23997978SPeter.Dunlap@Sun.COM 			idm_conn_event(ic, CE_TRANSPORT_FAIL, status);
24007978SPeter.Dunlap@Sun.COM 		}
24017978SPeter.Dunlap@Sun.COM 	}
24027978SPeter.Dunlap@Sun.COM 
24037978SPeter.Dunlap@Sun.COM 	/*
24047978SPeter.Dunlap@Sun.COM 	 * Before we leave, we need to abort every item remaining in the
24057978SPeter.Dunlap@Sun.COM 	 * TX list.
24067978SPeter.Dunlap@Sun.COM 	 */
24077978SPeter.Dunlap@Sun.COM 
24087978SPeter.Dunlap@Sun.COM tx_bail:
24097978SPeter.Dunlap@Sun.COM 	object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
24107978SPeter.Dunlap@Sun.COM 
24117978SPeter.Dunlap@Sun.COM 	while (object != NULL) {
24127978SPeter.Dunlap@Sun.COM 		next = list_next(&so_conn->ic_tx_list, object);
24137978SPeter.Dunlap@Sun.COM 
24147978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
24157978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
24167978SPeter.Dunlap@Sun.COM 		case IDM_PDU_MAGIC:
24177978SPeter.Dunlap@Sun.COM 			idm_pdu_complete((idm_pdu_t *)object,
24187978SPeter.Dunlap@Sun.COM 			    IDM_STATUS_ABORTED);
24197978SPeter.Dunlap@Sun.COM 			break;
24207978SPeter.Dunlap@Sun.COM 
24217978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
24227978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
24237978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
24247978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
24257978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
24267978SPeter.Dunlap@Sun.COM 			/*
24277978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
24287978SPeter.Dunlap@Sun.COM 			 * be "in transport"
24297978SPeter.Dunlap@Sun.COM 			 */
24307978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
24317978SPeter.Dunlap@Sun.COM 			/*
24327978SPeter.Dunlap@Sun.COM 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
24337978SPeter.Dunlap@Sun.COM 			 */
24347978SPeter.Dunlap@Sun.COM 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
24357978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
24367978SPeter.Dunlap@Sun.COM 			break;
24377978SPeter.Dunlap@Sun.COM 		}
24387978SPeter.Dunlap@Sun.COM 		default:
24397978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
24407978SPeter.Dunlap@Sun.COM 			    "idm_sotx_thread: Unexpected magic "
24417978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
24427978SPeter.Dunlap@Sun.COM 		}
24437978SPeter.Dunlap@Sun.COM 
24447978SPeter.Dunlap@Sun.COM 		object = next;
24457978SPeter.Dunlap@Sun.COM 	}
24467978SPeter.Dunlap@Sun.COM 
24477978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
24487978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
24497978SPeter.Dunlap@Sun.COM 	thread_exit();
24507978SPeter.Dunlap@Sun.COM 	/*NOTREACHED*/
24517978SPeter.Dunlap@Sun.COM }
2452