xref: /onnv-gate/usr/src/uts/common/io/idm/idm_so.c (revision 11857:64ac0ea7036d)
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 /*
2211552SPeter.Cudhea@Sun.COM  * Copyright 2010 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>
4910505SPeter.Cudhea@Sun.COM #include <sys/filio.h>		/* FIONBIO */
5010261SCharles.Ting@Sun.COM #include <sys/iscsi_protocol.h>
517978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h>
527978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_so.h>
537978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_text.h>
547978SPeter.Dunlap@Sun.COM 
5510156SZhang.Yi@Sun.COM #define	IN_PROGRESS_DELAY	1
5610156SZhang.Yi@Sun.COM 
577978SPeter.Dunlap@Sun.COM /*
587978SPeter.Dunlap@Sun.COM  * in6addr_any is currently all zeroes, but use the macro in case this
597978SPeter.Dunlap@Sun.COM  * ever changes.
607978SPeter.Dunlap@Sun.COM  */
619373SPeter.Dunlap@Sun.COM static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
627978SPeter.Dunlap@Sun.COM 
637978SPeter.Dunlap@Sun.COM static void idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
647978SPeter.Dunlap@Sun.COM static void idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
657978SPeter.Dunlap@Sun.COM static void idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status);
667978SPeter.Dunlap@Sun.COM 
678348SEric.Yu@Sun.COM static idm_status_t idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so);
687978SPeter.Dunlap@Sun.COM static void idm_so_conn_destroy_common(idm_conn_t *ic);
697978SPeter.Dunlap@Sun.COM static void idm_so_conn_connect_common(idm_conn_t *ic);
707978SPeter.Dunlap@Sun.COM 
7110822SJack.Meng@Sun.COM static void idm_set_ini_preconnect_options(idm_so_conn_t *sc,
7210822SJack.Meng@Sun.COM     boolean_t boot_conn);
737978SPeter.Dunlap@Sun.COM static void idm_set_ini_postconnect_options(idm_so_conn_t *sc);
748348SEric.Yu@Sun.COM static void idm_set_tgt_connect_options(ksocket_t so);
757978SPeter.Dunlap@Sun.COM static idm_status_t idm_i_so_tx(idm_pdu_t *pdu);
767978SPeter.Dunlap@Sun.COM 
777978SPeter.Dunlap@Sun.COM static idm_status_t idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu);
789162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt,
799162SPeter.Dunlap@Sun.COM     idm_buf_t *idb, uint32_t offset, uint32_t length);
809162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb);
819162SPeter.Dunlap@Sun.COM static idm_status_t idm_so_send_buf_region(idm_task_t *idt,
827978SPeter.Dunlap@Sun.COM     idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length);
837978SPeter.Dunlap@Sun.COM 
847978SPeter.Dunlap@Sun.COM static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb,
857978SPeter.Dunlap@Sun.COM     uint32_t ro, uint32_t dlength);
867978SPeter.Dunlap@Sun.COM 
877978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_handle_digest(idm_conn_t *it,
887978SPeter.Dunlap@Sun.COM     nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx);
897978SPeter.Dunlap@Sun.COM 
9010156SZhang.Yi@Sun.COM static void idm_so_socket_set_nonblock(struct sonode *node);
9110156SZhang.Yi@Sun.COM static void idm_so_socket_set_block(struct sonode *node);
9210156SZhang.Yi@Sun.COM 
937978SPeter.Dunlap@Sun.COM /*
947978SPeter.Dunlap@Sun.COM  * Transport ops prototypes
957978SPeter.Dunlap@Sun.COM  */
967978SPeter.Dunlap@Sun.COM static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu);
977978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb);
987978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb);
997978SPeter.Dunlap@Sun.COM static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu);
1007978SPeter.Dunlap@Sun.COM static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu);
1017978SPeter.Dunlap@Sun.COM static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu);
1027978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt);
1037978SPeter.Dunlap@Sun.COM static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it,
1047978SPeter.Dunlap@Sun.COM     nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl);
1059162SPeter.Dunlap@Sun.COM static void idm_so_notice_key_values(idm_conn_t *it,
1067978SPeter.Dunlap@Sun.COM     nvlist_t *negotiated_nvl);
10710261SCharles.Ting@Sun.COM static kv_status_t idm_so_declare_key_values(idm_conn_t *it,
10810261SCharles.Ting@Sun.COM     nvlist_t *config_nvl, nvlist_t *outgoing_nvl);
1097978SPeter.Dunlap@Sun.COM static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic,
1107978SPeter.Dunlap@Sun.COM     idm_transport_caps_t *caps);
1117978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen);
1127978SPeter.Dunlap@Sun.COM static void idm_so_buf_free(idm_buf_t *idb);
1137978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_buf_setup(idm_buf_t *idb);
1147978SPeter.Dunlap@Sun.COM static void idm_so_buf_teardown(idm_buf_t *idb);
1157978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is);
1167978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_destroy(idm_svc_t *is);
1177978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is);
1187978SPeter.Dunlap@Sun.COM static void idm_so_tgt_svc_offline(idm_svc_t *is);
1197978SPeter.Dunlap@Sun.COM static void idm_so_tgt_conn_destroy(idm_conn_t *ic);
1207978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic);
1217978SPeter.Dunlap@Sun.COM static void idm_so_conn_disconnect(idm_conn_t *ic);
1227978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic);
1237978SPeter.Dunlap@Sun.COM static void idm_so_ini_conn_destroy(idm_conn_t *ic);
1247978SPeter.Dunlap@Sun.COM static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic);
1257978SPeter.Dunlap@Sun.COM 
1267978SPeter.Dunlap@Sun.COM /*
1277978SPeter.Dunlap@Sun.COM  * IDM Native Sockets transport operations
1287978SPeter.Dunlap@Sun.COM  */
1297978SPeter.Dunlap@Sun.COM static
1307978SPeter.Dunlap@Sun.COM idm_transport_ops_t idm_so_transport_ops = {
1317978SPeter.Dunlap@Sun.COM 	idm_so_tx,			/* it_tx_pdu */
1327978SPeter.Dunlap@Sun.COM 	idm_so_buf_tx_to_ini,		/* it_buf_tx_to_ini */
1337978SPeter.Dunlap@Sun.COM 	idm_so_buf_rx_from_ini,		/* it_buf_rx_from_ini */
1347978SPeter.Dunlap@Sun.COM 	idm_so_rx_datain,		/* it_rx_datain */
1357978SPeter.Dunlap@Sun.COM 	idm_so_rx_rtt,			/* it_rx_rtt */
1367978SPeter.Dunlap@Sun.COM 	idm_so_rx_dataout,		/* it_rx_dataout */
1377978SPeter.Dunlap@Sun.COM 	NULL,				/* it_alloc_conn_rsrc */
1387978SPeter.Dunlap@Sun.COM 	NULL,				/* it_free_conn_rsrc */
1397978SPeter.Dunlap@Sun.COM 	NULL,				/* it_tgt_enable_datamover */
1407978SPeter.Dunlap@Sun.COM 	NULL,				/* it_ini_enable_datamover */
1417978SPeter.Dunlap@Sun.COM 	NULL,				/* it_conn_terminate */
1427978SPeter.Dunlap@Sun.COM 	idm_so_free_task_rsrc,		/* it_free_task_rsrc */
1437978SPeter.Dunlap@Sun.COM 	idm_so_negotiate_key_values,	/* it_negotiate_key_values */
1447978SPeter.Dunlap@Sun.COM 	idm_so_notice_key_values,	/* it_notice_key_values */
1457978SPeter.Dunlap@Sun.COM 	idm_so_conn_is_capable,		/* it_conn_is_capable */
1467978SPeter.Dunlap@Sun.COM 	idm_so_buf_alloc,		/* it_buf_alloc */
1477978SPeter.Dunlap@Sun.COM 	idm_so_buf_free,		/* it_buf_free */
1487978SPeter.Dunlap@Sun.COM 	idm_so_buf_setup,		/* it_buf_setup */
1497978SPeter.Dunlap@Sun.COM 	idm_so_buf_teardown,		/* it_buf_teardown */
1507978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_create,		/* it_tgt_svc_create */
1517978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_destroy,		/* it_tgt_svc_destroy */
1527978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_online,		/* it_tgt_svc_online */
1537978SPeter.Dunlap@Sun.COM 	idm_so_tgt_svc_offline,		/* it_tgt_svc_offline */
1547978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_destroy,	/* it_tgt_conn_destroy */
1557978SPeter.Dunlap@Sun.COM 	idm_so_tgt_conn_connect,	/* it_tgt_conn_connect */
1567978SPeter.Dunlap@Sun.COM 	idm_so_conn_disconnect,		/* it_tgt_conn_disconnect */
1577978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_create,		/* it_ini_conn_create */
1587978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_destroy,	/* it_ini_conn_destroy */
1597978SPeter.Dunlap@Sun.COM 	idm_so_ini_conn_connect,	/* it_ini_conn_connect */
16010261SCharles.Ting@Sun.COM 	idm_so_conn_disconnect,		/* it_ini_conn_disconnect */
16110261SCharles.Ting@Sun.COM 	idm_so_declare_key_values	/* it_declare_key_values */
1627978SPeter.Dunlap@Sun.COM };
1637978SPeter.Dunlap@Sun.COM 
16410505SPeter.Cudhea@Sun.COM kmutex_t	idm_so_timed_socket_mutex;
1657978SPeter.Dunlap@Sun.COM /*
1667978SPeter.Dunlap@Sun.COM  * idm_so_init()
1677978SPeter.Dunlap@Sun.COM  * Sockets transport initialization
1687978SPeter.Dunlap@Sun.COM  */
1697978SPeter.Dunlap@Sun.COM void
idm_so_init(idm_transport_t * it)1707978SPeter.Dunlap@Sun.COM idm_so_init(idm_transport_t *it)
1717978SPeter.Dunlap@Sun.COM {
1727978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Data and R2T Transmit PDU's */
1737978SPeter.Dunlap@Sun.COM 	idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache",
1747978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8,
1757978SPeter.Dunlap@Sun.COM 	    &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1767978SPeter.Dunlap@Sun.COM 
1777978SPeter.Dunlap@Sun.COM 	/* Cache for IDM Receive PDU's */
1787978SPeter.Dunlap@Sun.COM 	idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache",
1797978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8,
1807978SPeter.Dunlap@Sun.COM 	    &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP);
1817978SPeter.Dunlap@Sun.COM 
1829247SPeter.Dunlap@Sun.COM 	/* 128k buffer cache */
1839247SPeter.Dunlap@Sun.COM 	idm.idm_so_128k_buf_cache = kmem_cache_create("idm_128k_buf_cache",
1849247SPeter.Dunlap@Sun.COM 	    IDM_SO_BUF_CACHE_UB, 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP);
1859247SPeter.Dunlap@Sun.COM 
1867978SPeter.Dunlap@Sun.COM 	/* Set the sockets transport ops */
1877978SPeter.Dunlap@Sun.COM 	it->it_ops = &idm_so_transport_ops;
18810505SPeter.Cudhea@Sun.COM 
18910505SPeter.Cudhea@Sun.COM 	mutex_init(&idm_so_timed_socket_mutex, NULL, MUTEX_DEFAULT, NULL);
19010505SPeter.Cudhea@Sun.COM 
1917978SPeter.Dunlap@Sun.COM }
1927978SPeter.Dunlap@Sun.COM 
1937978SPeter.Dunlap@Sun.COM /*
1947978SPeter.Dunlap@Sun.COM  * idm_so_fini()
1957978SPeter.Dunlap@Sun.COM  * Sockets transport teardown
1967978SPeter.Dunlap@Sun.COM  */
1977978SPeter.Dunlap@Sun.COM void
idm_so_fini(void)1987978SPeter.Dunlap@Sun.COM idm_so_fini(void)
1997978SPeter.Dunlap@Sun.COM {
2009247SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_so_128k_buf_cache);
2017978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sotx_pdu_cache);
2027978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(idm.idm_sorx_pdu_cache);
20310505SPeter.Cudhea@Sun.COM 	mutex_destroy(&idm_so_timed_socket_mutex);
2047978SPeter.Dunlap@Sun.COM }
2057978SPeter.Dunlap@Sun.COM 
2068348SEric.Yu@Sun.COM ksocket_t
idm_socreate(int domain,int type,int protocol)2077978SPeter.Dunlap@Sun.COM idm_socreate(int domain, int type, int protocol)
2087978SPeter.Dunlap@Sun.COM {
2098348SEric.Yu@Sun.COM 	ksocket_t ks;
2107978SPeter.Dunlap@Sun.COM 
2118348SEric.Yu@Sun.COM 	if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP,
2128348SEric.Yu@Sun.COM 	    CRED())) {
2138348SEric.Yu@Sun.COM 		return (ks);
2148348SEric.Yu@Sun.COM 	} else {
2158348SEric.Yu@Sun.COM 		return (NULL);
2167978SPeter.Dunlap@Sun.COM 	}
2177978SPeter.Dunlap@Sun.COM }
2187978SPeter.Dunlap@Sun.COM 
2197978SPeter.Dunlap@Sun.COM /*
2207978SPeter.Dunlap@Sun.COM  * idm_soshutdown will disconnect the socket and prevent subsequent PDU
2217978SPeter.Dunlap@Sun.COM  * reception and transmission.  The sonode still exists but its state
2227978SPeter.Dunlap@Sun.COM  * gets modified to indicate it is no longer connected.  Calls to
2237978SPeter.Dunlap@Sun.COM  * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used
2247978SPeter.Dunlap@Sun.COM  * regain control of a thread stuck in idm_sorecv.
2257978SPeter.Dunlap@Sun.COM  */
2267978SPeter.Dunlap@Sun.COM void
idm_soshutdown(ksocket_t so)2278348SEric.Yu@Sun.COM idm_soshutdown(ksocket_t so)
2287978SPeter.Dunlap@Sun.COM {
2298348SEric.Yu@Sun.COM 	(void) ksocket_shutdown(so, SHUT_RDWR, CRED());
2307978SPeter.Dunlap@Sun.COM }
2317978SPeter.Dunlap@Sun.COM 
2327978SPeter.Dunlap@Sun.COM /*
2337978SPeter.Dunlap@Sun.COM  * idm_sodestroy releases all resources associated with a socket previously
2347978SPeter.Dunlap@Sun.COM  * created with idm_socreate.  The socket must be shutdown using
2357978SPeter.Dunlap@Sun.COM  * idm_soshutdown before the socket is destroyed with idm_sodestroy,
2367978SPeter.Dunlap@Sun.COM  * otherwise undefined behavior will result.
2377978SPeter.Dunlap@Sun.COM  */
2387978SPeter.Dunlap@Sun.COM void
idm_sodestroy(ksocket_t ks)2398348SEric.Yu@Sun.COM idm_sodestroy(ksocket_t ks)
2407978SPeter.Dunlap@Sun.COM {
2418348SEric.Yu@Sun.COM 	(void) ksocket_close(ks, CRED());
2427978SPeter.Dunlap@Sun.COM }
2437978SPeter.Dunlap@Sun.COM 
2447978SPeter.Dunlap@Sun.COM /*
2459373SPeter.Dunlap@Sun.COM  * Function to compare two addresses in sockaddr_storage format
2469373SPeter.Dunlap@Sun.COM  */
2479373SPeter.Dunlap@Sun.COM 
2489373SPeter.Dunlap@Sun.COM int
idm_ss_compare(const struct sockaddr_storage * cmp_ss1,const struct sockaddr_storage * cmp_ss2,boolean_t v4_mapped_as_v4,boolean_t compare_ports)2499373SPeter.Dunlap@Sun.COM idm_ss_compare(const struct sockaddr_storage *cmp_ss1,
2509373SPeter.Dunlap@Sun.COM     const struct sockaddr_storage *cmp_ss2,
25110505SPeter.Cudhea@Sun.COM     boolean_t v4_mapped_as_v4,
25210505SPeter.Cudhea@Sun.COM     boolean_t compare_ports)
2539373SPeter.Dunlap@Sun.COM {
2549373SPeter.Dunlap@Sun.COM 	struct sockaddr_storage			mapped_v4_ss1, mapped_v4_ss2;
2559373SPeter.Dunlap@Sun.COM 	const struct sockaddr_storage		*ss1, *ss2;
2569373SPeter.Dunlap@Sun.COM 	struct in_addr				*in1, *in2;
2579373SPeter.Dunlap@Sun.COM 	struct in6_addr				*in61, *in62;
2589373SPeter.Dunlap@Sun.COM 	int i;
2599373SPeter.Dunlap@Sun.COM 
2609373SPeter.Dunlap@Sun.COM 	/*
2619373SPeter.Dunlap@Sun.COM 	 * Normalize V4-mapped IPv6 addresses into V4 format if
2629373SPeter.Dunlap@Sun.COM 	 * v4_mapped_as_v4 is B_TRUE.
2639373SPeter.Dunlap@Sun.COM 	 */
2649373SPeter.Dunlap@Sun.COM 	ss1 = cmp_ss1;
2659373SPeter.Dunlap@Sun.COM 	ss2 = cmp_ss2;
2669373SPeter.Dunlap@Sun.COM 	if (v4_mapped_as_v4 && (ss1->ss_family == AF_INET6)) {
2679373SPeter.Dunlap@Sun.COM 		in61 = &((struct sockaddr_in6 *)ss1)->sin6_addr;
2689373SPeter.Dunlap@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(in61)) {
2699373SPeter.Dunlap@Sun.COM 			bzero(&mapped_v4_ss1, sizeof (mapped_v4_ss1));
2709373SPeter.Dunlap@Sun.COM 			mapped_v4_ss1.ss_family = AF_INET;
2719373SPeter.Dunlap@Sun.COM 			((struct sockaddr_in *)&mapped_v4_ss1)->sin_port =
2729373SPeter.Dunlap@Sun.COM 			    ((struct sockaddr_in *)ss1)->sin_port;
2739373SPeter.Dunlap@Sun.COM 			IN6_V4MAPPED_TO_INADDR(in61,
2749373SPeter.Dunlap@Sun.COM 			    &((struct sockaddr_in *)&mapped_v4_ss1)->sin_addr);
2759373SPeter.Dunlap@Sun.COM 			ss1 = &mapped_v4_ss1;
2769373SPeter.Dunlap@Sun.COM 		}
2779373SPeter.Dunlap@Sun.COM 	}
2789373SPeter.Dunlap@Sun.COM 	ss2 = cmp_ss2;
2799373SPeter.Dunlap@Sun.COM 	if (v4_mapped_as_v4 && (ss2->ss_family == AF_INET6)) {
2809373SPeter.Dunlap@Sun.COM 		in62 = &((struct sockaddr_in6 *)ss2)->sin6_addr;
2819373SPeter.Dunlap@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(in62)) {
2829373SPeter.Dunlap@Sun.COM 			bzero(&mapped_v4_ss2, sizeof (mapped_v4_ss2));
2839373SPeter.Dunlap@Sun.COM 			mapped_v4_ss2.ss_family = AF_INET;
2849373SPeter.Dunlap@Sun.COM 			((struct sockaddr_in *)&mapped_v4_ss2)->sin_port =
2859373SPeter.Dunlap@Sun.COM 			    ((struct sockaddr_in *)ss2)->sin_port;
2869373SPeter.Dunlap@Sun.COM 			IN6_V4MAPPED_TO_INADDR(in62,
2879373SPeter.Dunlap@Sun.COM 			    &((struct sockaddr_in *)&mapped_v4_ss2)->sin_addr);
2889373SPeter.Dunlap@Sun.COM 			ss2 = &mapped_v4_ss2;
2899373SPeter.Dunlap@Sun.COM 		}
2909373SPeter.Dunlap@Sun.COM 	}
2919373SPeter.Dunlap@Sun.COM 
2929373SPeter.Dunlap@Sun.COM 	/*
2939373SPeter.Dunlap@Sun.COM 	 * Compare ports, then address family, then ip address
2949373SPeter.Dunlap@Sun.COM 	 */
29510505SPeter.Cudhea@Sun.COM 	if (compare_ports &&
29610505SPeter.Cudhea@Sun.COM 	    (((struct sockaddr_in *)ss1)->sin_port !=
29710505SPeter.Cudhea@Sun.COM 	    ((struct sockaddr_in *)ss2)->sin_port)) {
2989373SPeter.Dunlap@Sun.COM 		if (((struct sockaddr_in *)ss1)->sin_port >
2999373SPeter.Dunlap@Sun.COM 		    ((struct sockaddr_in *)ss2)->sin_port)
3009373SPeter.Dunlap@Sun.COM 			return (1);
3019373SPeter.Dunlap@Sun.COM 		else
3029373SPeter.Dunlap@Sun.COM 			return (-1);
3039373SPeter.Dunlap@Sun.COM 	}
3049373SPeter.Dunlap@Sun.COM 
3059373SPeter.Dunlap@Sun.COM 	/*
3069373SPeter.Dunlap@Sun.COM 	 * ports are the same
3079373SPeter.Dunlap@Sun.COM 	 */
3089373SPeter.Dunlap@Sun.COM 	if (ss1->ss_family != ss2->ss_family) {
3099373SPeter.Dunlap@Sun.COM 		if (ss1->ss_family == AF_INET)
3109373SPeter.Dunlap@Sun.COM 			return (1);
3119373SPeter.Dunlap@Sun.COM 		else
3129373SPeter.Dunlap@Sun.COM 			return (-1);
3139373SPeter.Dunlap@Sun.COM 	}
3149373SPeter.Dunlap@Sun.COM 
3159373SPeter.Dunlap@Sun.COM 	/*
3169373SPeter.Dunlap@Sun.COM 	 * address families are the same
3179373SPeter.Dunlap@Sun.COM 	 */
3189373SPeter.Dunlap@Sun.COM 	if (ss1->ss_family == AF_INET) {
3199373SPeter.Dunlap@Sun.COM 		in1 = &((struct sockaddr_in *)ss1)->sin_addr;
3209373SPeter.Dunlap@Sun.COM 		in2 = &((struct sockaddr_in *)ss2)->sin_addr;
3219373SPeter.Dunlap@Sun.COM 
3229373SPeter.Dunlap@Sun.COM 		if (in1->s_addr > in2->s_addr)
3239373SPeter.Dunlap@Sun.COM 			return (1);
3249373SPeter.Dunlap@Sun.COM 		else if (in1->s_addr < in2->s_addr)
3259373SPeter.Dunlap@Sun.COM 			return (-1);
3269373SPeter.Dunlap@Sun.COM 		else
3279373SPeter.Dunlap@Sun.COM 			return (0);
3289373SPeter.Dunlap@Sun.COM 	} else if (ss1->ss_family == AF_INET6) {
3299373SPeter.Dunlap@Sun.COM 		in61 = &((struct sockaddr_in6 *)ss1)->sin6_addr;
3309373SPeter.Dunlap@Sun.COM 		in62 = &((struct sockaddr_in6 *)ss2)->sin6_addr;
3319373SPeter.Dunlap@Sun.COM 
3329373SPeter.Dunlap@Sun.COM 		for (i = 0; i < 4; i++) {
3339373SPeter.Dunlap@Sun.COM 			if (in61->s6_addr32[i] > in62->s6_addr32[i])
3349373SPeter.Dunlap@Sun.COM 				return (1);
3359373SPeter.Dunlap@Sun.COM 			else if (in61->s6_addr32[i] < in62->s6_addr32[i])
3369373SPeter.Dunlap@Sun.COM 				return (-1);
3379373SPeter.Dunlap@Sun.COM 		}
3389373SPeter.Dunlap@Sun.COM 		return (0);
3399373SPeter.Dunlap@Sun.COM 	}
3409373SPeter.Dunlap@Sun.COM 
3419373SPeter.Dunlap@Sun.COM 	return (1);
3429373SPeter.Dunlap@Sun.COM }
3439373SPeter.Dunlap@Sun.COM 
3449373SPeter.Dunlap@Sun.COM /*
3457978SPeter.Dunlap@Sun.COM  * IP address filter functions to flag addresses that should not
3467978SPeter.Dunlap@Sun.COM  * go out to initiators through discovery.
3477978SPeter.Dunlap@Sun.COM  */
3487978SPeter.Dunlap@Sun.COM static boolean_t
idm_v4_addr_okay(struct in_addr * in_addr)3497978SPeter.Dunlap@Sun.COM idm_v4_addr_okay(struct in_addr *in_addr)
3507978SPeter.Dunlap@Sun.COM {
3517978SPeter.Dunlap@Sun.COM 	in_addr_t addr = ntohl(in_addr->s_addr);
3527978SPeter.Dunlap@Sun.COM 
3537978SPeter.Dunlap@Sun.COM 	if ((INADDR_NONE == addr) ||
3547978SPeter.Dunlap@Sun.COM 	    (IN_MULTICAST(addr)) ||
3557978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == 0) ||
3567978SPeter.Dunlap@Sun.COM 	    ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
3577978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
3587978SPeter.Dunlap@Sun.COM 	}
3597978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
3607978SPeter.Dunlap@Sun.COM }
3617978SPeter.Dunlap@Sun.COM 
3627978SPeter.Dunlap@Sun.COM static boolean_t
idm_v6_addr_okay(struct in6_addr * addr6)3637978SPeter.Dunlap@Sun.COM idm_v6_addr_okay(struct in6_addr *addr6)
3647978SPeter.Dunlap@Sun.COM {
3657978SPeter.Dunlap@Sun.COM 
3667978SPeter.Dunlap@Sun.COM 	if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) ||
3677978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LOOPBACK(addr6)) ||
3687978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_MULTICAST(addr6)) ||
3697978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4MAPPED(addr6)) ||
3707978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_V4COMPAT(addr6)) ||
3717978SPeter.Dunlap@Sun.COM 	    (IN6_IS_ADDR_LINKLOCAL(addr6))) {
3727978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
3737978SPeter.Dunlap@Sun.COM 	}
3747978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
3757978SPeter.Dunlap@Sun.COM }
3767978SPeter.Dunlap@Sun.COM 
3777978SPeter.Dunlap@Sun.COM /*
3787978SPeter.Dunlap@Sun.COM  * idm_get_ipaddr will retrieve a list of IP Addresses which the host is
3797978SPeter.Dunlap@Sun.COM  * configured with by sending down a sequence of kernel ioctl to IP STREAMS.
3807978SPeter.Dunlap@Sun.COM  */
3817978SPeter.Dunlap@Sun.COM int
idm_get_ipaddr(idm_addr_list_t ** ipaddr_p)3827978SPeter.Dunlap@Sun.COM idm_get_ipaddr(idm_addr_list_t **ipaddr_p)
3837978SPeter.Dunlap@Sun.COM {
3848348SEric.Yu@Sun.COM 	ksocket_t 		so4, so6;
3857978SPeter.Dunlap@Sun.COM 	struct lifnum		lifn;
3867978SPeter.Dunlap@Sun.COM 	struct lifconf		lifc;
3877978SPeter.Dunlap@Sun.COM 	struct lifreq		*lp;
3887978SPeter.Dunlap@Sun.COM 	int			rval;
3897978SPeter.Dunlap@Sun.COM 	int			numifs;
3907978SPeter.Dunlap@Sun.COM 	int			bufsize;
3917978SPeter.Dunlap@Sun.COM 	void			*buf;
3927978SPeter.Dunlap@Sun.COM 	int			i, j, n, rc;
3937978SPeter.Dunlap@Sun.COM 	struct sockaddr_storage	ss;
3947978SPeter.Dunlap@Sun.COM 	struct sockaddr_in	*sin;
3957978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	*sin6;
3967978SPeter.Dunlap@Sun.COM 	idm_addr_t		*ip;
39710855SCharles.Ting@Sun.COM 	idm_addr_list_t		*ipaddr = NULL;
3987978SPeter.Dunlap@Sun.COM 	int			size_ipaddr;
3997978SPeter.Dunlap@Sun.COM 
4007978SPeter.Dunlap@Sun.COM 	*ipaddr_p = NULL;
4017978SPeter.Dunlap@Sun.COM 	size_ipaddr = 0;
4027978SPeter.Dunlap@Sun.COM 	buf = NULL;
4037978SPeter.Dunlap@Sun.COM 
4047978SPeter.Dunlap@Sun.COM 	/* create an ipv4 and ipv6 UDP socket */
4057978SPeter.Dunlap@Sun.COM 	if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL)
4067978SPeter.Dunlap@Sun.COM 		return (0);
4077978SPeter.Dunlap@Sun.COM 	if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) {
4087978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so6);
4097978SPeter.Dunlap@Sun.COM 		return (0);
4107978SPeter.Dunlap@Sun.COM 	}
4117978SPeter.Dunlap@Sun.COM 
4127978SPeter.Dunlap@Sun.COM 
4137978SPeter.Dunlap@Sun.COM retry_count:
4147978SPeter.Dunlap@Sun.COM 	/* snapshot the current number of interfaces */
4157978SPeter.Dunlap@Sun.COM 	lifn.lifn_family = PF_UNSPEC;
4167978SPeter.Dunlap@Sun.COM 	lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
4177978SPeter.Dunlap@Sun.COM 	lifn.lifn_count = 0;
4188348SEric.Yu@Sun.COM 	/* use vp6 for ioctls with unspecified families by default */
4198348SEric.Yu@Sun.COM 	if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED())
4208348SEric.Yu@Sun.COM 	    != 0) {
4217978SPeter.Dunlap@Sun.COM 		goto cleanup;
4227978SPeter.Dunlap@Sun.COM 	}
4237978SPeter.Dunlap@Sun.COM 
4247978SPeter.Dunlap@Sun.COM 	numifs = lifn.lifn_count;
4257978SPeter.Dunlap@Sun.COM 	if (numifs <= 0) {
4267978SPeter.Dunlap@Sun.COM 		goto cleanup;
4277978SPeter.Dunlap@Sun.COM 	}
4287978SPeter.Dunlap@Sun.COM 
4297978SPeter.Dunlap@Sun.COM 	/* allocate extra room in case more interfaces appear */
4307978SPeter.Dunlap@Sun.COM 	numifs += 10;
4317978SPeter.Dunlap@Sun.COM 
4327978SPeter.Dunlap@Sun.COM 	/* get the interface names and ip addresses */
4337978SPeter.Dunlap@Sun.COM 	bufsize = numifs * sizeof (struct lifreq);
4347978SPeter.Dunlap@Sun.COM 	buf = kmem_alloc(bufsize, KM_SLEEP);
4357978SPeter.Dunlap@Sun.COM 
4367978SPeter.Dunlap@Sun.COM 	lifc.lifc_family = AF_UNSPEC;
4377978SPeter.Dunlap@Sun.COM 	lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
4387978SPeter.Dunlap@Sun.COM 	lifc.lifc_len = bufsize;
4397978SPeter.Dunlap@Sun.COM 	lifc.lifc_buf = buf;
4408348SEric.Yu@Sun.COM 	rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED());
4417978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
4427978SPeter.Dunlap@Sun.COM 		goto cleanup;
4437978SPeter.Dunlap@Sun.COM 	}
4447978SPeter.Dunlap@Sun.COM 	/* if our extra room is used up, try again */
4457978SPeter.Dunlap@Sun.COM 	if (bufsize <= lifc.lifc_len) {
4467978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
4477978SPeter.Dunlap@Sun.COM 		buf = NULL;
4487978SPeter.Dunlap@Sun.COM 		goto retry_count;
4497978SPeter.Dunlap@Sun.COM 	}
4507978SPeter.Dunlap@Sun.COM 	/* calc actual number of ifconfs */
4517978SPeter.Dunlap@Sun.COM 	n = lifc.lifc_len / sizeof (struct lifreq);
4527978SPeter.Dunlap@Sun.COM 
4537978SPeter.Dunlap@Sun.COM 	/* get ip address */
4547978SPeter.Dunlap@Sun.COM 	if (n > 0) {
4557978SPeter.Dunlap@Sun.COM 		size_ipaddr = sizeof (idm_addr_list_t) +
4567978SPeter.Dunlap@Sun.COM 		    (n - 1) * sizeof (idm_addr_t);
4577978SPeter.Dunlap@Sun.COM 		ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP);
4587978SPeter.Dunlap@Sun.COM 	} else {
4597978SPeter.Dunlap@Sun.COM 		goto cleanup;
4607978SPeter.Dunlap@Sun.COM 	}
4617978SPeter.Dunlap@Sun.COM 
4627978SPeter.Dunlap@Sun.COM 	/*
4637978SPeter.Dunlap@Sun.COM 	 * Examine the array of interfaces and filter uninteresting ones
4647978SPeter.Dunlap@Sun.COM 	 */
4657978SPeter.Dunlap@Sun.COM 	for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) {
4667978SPeter.Dunlap@Sun.COM 
4677978SPeter.Dunlap@Sun.COM 		/*
4687978SPeter.Dunlap@Sun.COM 		 * Copy the address as the SIOCGLIFFLAGS ioctl is destructive
4697978SPeter.Dunlap@Sun.COM 		 */
4707978SPeter.Dunlap@Sun.COM 		ss = lp->lifr_addr;
4717978SPeter.Dunlap@Sun.COM 		/*
4727978SPeter.Dunlap@Sun.COM 		 * fetch the flags using the socket of the correct family
4737978SPeter.Dunlap@Sun.COM 		 */
4747978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
4757978SPeter.Dunlap@Sun.COM 		case AF_INET:
4768348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp,
4778348SEric.Yu@Sun.COM 			    &rval, CRED());
4787978SPeter.Dunlap@Sun.COM 			break;
4797978SPeter.Dunlap@Sun.COM 		case AF_INET6:
4808348SEric.Yu@Sun.COM 			rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp,
4818348SEric.Yu@Sun.COM 			    &rval, CRED());
4827978SPeter.Dunlap@Sun.COM 			break;
4837978SPeter.Dunlap@Sun.COM 		default:
4847978SPeter.Dunlap@Sun.COM 			continue;
4857978SPeter.Dunlap@Sun.COM 		}
4867978SPeter.Dunlap@Sun.COM 		if (rc == 0) {
4877978SPeter.Dunlap@Sun.COM 			/*
4887978SPeter.Dunlap@Sun.COM 			 * If we got the flags, skip uninteresting
4897978SPeter.Dunlap@Sun.COM 			 * interfaces based on flags
4907978SPeter.Dunlap@Sun.COM 			 */
4917978SPeter.Dunlap@Sun.COM 			if ((lp->lifr_flags & IFF_UP) != IFF_UP)
4927978SPeter.Dunlap@Sun.COM 				continue;
4937978SPeter.Dunlap@Sun.COM 			if (lp->lifr_flags &
4947978SPeter.Dunlap@Sun.COM 			    (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED))
4957978SPeter.Dunlap@Sun.COM 				continue;
4967978SPeter.Dunlap@Sun.COM 		}
4977978SPeter.Dunlap@Sun.COM 
4987978SPeter.Dunlap@Sun.COM 		/* save ip address */
4997978SPeter.Dunlap@Sun.COM 		ip = &ipaddr->al_addrs[j];
5007978SPeter.Dunlap@Sun.COM 		switch (ss.ss_family) {
5017978SPeter.Dunlap@Sun.COM 		case AF_INET:
5027978SPeter.Dunlap@Sun.COM 			sin = (struct sockaddr_in *)&ss;
5037978SPeter.Dunlap@Sun.COM 			if (!idm_v4_addr_okay(&sin->sin_addr))
5047978SPeter.Dunlap@Sun.COM 				continue;
5057978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in4 = sin->sin_addr;
5067978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in_addr);
5077978SPeter.Dunlap@Sun.COM 			break;
5087978SPeter.Dunlap@Sun.COM 		case AF_INET6:
5097978SPeter.Dunlap@Sun.COM 			sin6 = (struct sockaddr_in6 *)&ss;
5107978SPeter.Dunlap@Sun.COM 			if (!idm_v6_addr_okay(&sin6->sin6_addr))
5117978SPeter.Dunlap@Sun.COM 				continue;
5127978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_addr.in6 = sin6->sin6_addr;
5137978SPeter.Dunlap@Sun.COM 			ip->a_addr.i_insize = sizeof (struct in6_addr);
5147978SPeter.Dunlap@Sun.COM 			break;
5157978SPeter.Dunlap@Sun.COM 		default:
5167978SPeter.Dunlap@Sun.COM 			continue;
5177978SPeter.Dunlap@Sun.COM 		}
5187978SPeter.Dunlap@Sun.COM 		j++;
5197978SPeter.Dunlap@Sun.COM 	}
5207978SPeter.Dunlap@Sun.COM 
5217978SPeter.Dunlap@Sun.COM 	if (j == 0) {
5227978SPeter.Dunlap@Sun.COM 		/* no valid ifaddr */
5237978SPeter.Dunlap@Sun.COM 		kmem_free(ipaddr, size_ipaddr);
5247978SPeter.Dunlap@Sun.COM 		size_ipaddr = 0;
5257978SPeter.Dunlap@Sun.COM 		ipaddr = NULL;
5267978SPeter.Dunlap@Sun.COM 	} else {
5277978SPeter.Dunlap@Sun.COM 		ipaddr->al_out_cnt = j;
5287978SPeter.Dunlap@Sun.COM 	}
5297978SPeter.Dunlap@Sun.COM 
5307978SPeter.Dunlap@Sun.COM 
5317978SPeter.Dunlap@Sun.COM cleanup:
5327978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so6);
5337978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so4);
5347978SPeter.Dunlap@Sun.COM 
5357978SPeter.Dunlap@Sun.COM 	if (buf != NULL)
5367978SPeter.Dunlap@Sun.COM 		kmem_free(buf, bufsize);
5377978SPeter.Dunlap@Sun.COM 
5387978SPeter.Dunlap@Sun.COM 	*ipaddr_p = ipaddr;
5397978SPeter.Dunlap@Sun.COM 	return (size_ipaddr);
5407978SPeter.Dunlap@Sun.COM }
5417978SPeter.Dunlap@Sun.COM 
5427978SPeter.Dunlap@Sun.COM int
idm_sorecv(ksocket_t so,void * msg,size_t len)5438348SEric.Yu@Sun.COM idm_sorecv(ksocket_t so, void *msg, size_t len)
5447978SPeter.Dunlap@Sun.COM {
5457978SPeter.Dunlap@Sun.COM 	iovec_t iov;
5467978SPeter.Dunlap@Sun.COM 
5477978SPeter.Dunlap@Sun.COM 	ASSERT(so != NULL);
5487978SPeter.Dunlap@Sun.COM 	ASSERT(len != 0);
5497978SPeter.Dunlap@Sun.COM 
5507978SPeter.Dunlap@Sun.COM 	/*
5517978SPeter.Dunlap@Sun.COM 	 * Fill in iovec and receive data
5527978SPeter.Dunlap@Sun.COM 	 */
5537978SPeter.Dunlap@Sun.COM 	iov.iov_base = msg;
5547978SPeter.Dunlap@Sun.COM 	iov.iov_len = len;
5557978SPeter.Dunlap@Sun.COM 
5567978SPeter.Dunlap@Sun.COM 	return (idm_iov_sorecv(so, &iov, 1, len));
5577978SPeter.Dunlap@Sun.COM }
5587978SPeter.Dunlap@Sun.COM 
5597978SPeter.Dunlap@Sun.COM /*
5607978SPeter.Dunlap@Sun.COM  * idm_sosendto - Sends a buffered data on a non-connected socket.
5617978SPeter.Dunlap@Sun.COM  *
5627978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
5637978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
5647978SPeter.Dunlap@Sun.COM  * occurs.
5657978SPeter.Dunlap@Sun.COM  *
5667978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
5677978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
5687978SPeter.Dunlap@Sun.COM  */
5697978SPeter.Dunlap@Sun.COM int
idm_sosendto(ksocket_t so,void * buff,size_t len,struct sockaddr * name,socklen_t namelen)5708348SEric.Yu@Sun.COM idm_sosendto(ksocket_t so, void *buff, size_t len,
5717978SPeter.Dunlap@Sun.COM     struct sockaddr *name, socklen_t namelen)
5727978SPeter.Dunlap@Sun.COM {
5737978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
5747978SPeter.Dunlap@Sun.COM 	struct iovec		iov[1];
5757978SPeter.Dunlap@Sun.COM 	int			error;
5768348SEric.Yu@Sun.COM 	size_t			sent = 0;
5777978SPeter.Dunlap@Sun.COM 
5787978SPeter.Dunlap@Sun.COM 	iov[0].iov_base	= buff;
5797978SPeter.Dunlap@Sun.COM 	iov[0].iov_len	= len;
5807978SPeter.Dunlap@Sun.COM 
5817978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
5827978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
5837978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iov;
5847978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= 1;
5857978SPeter.Dunlap@Sun.COM 	msg.msg_name	= name;
5867978SPeter.Dunlap@Sun.COM 	msg.msg_namelen	= namelen;
5877978SPeter.Dunlap@Sun.COM 
5888348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) {
5897978SPeter.Dunlap@Sun.COM 		/* Data sent */
5908348SEric.Yu@Sun.COM 		if (sent == len) {
5917978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
5927978SPeter.Dunlap@Sun.COM 			return (0);
5937978SPeter.Dunlap@Sun.COM 		} else {
5947978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
5957978SPeter.Dunlap@Sun.COM 			return (-1);
5967978SPeter.Dunlap@Sun.COM 		}
5977978SPeter.Dunlap@Sun.COM 	}
5987978SPeter.Dunlap@Sun.COM 
5997978SPeter.Dunlap@Sun.COM 	/* Send failed */
6007978SPeter.Dunlap@Sun.COM 	return (error);
6017978SPeter.Dunlap@Sun.COM }
6027978SPeter.Dunlap@Sun.COM 
6037978SPeter.Dunlap@Sun.COM /*
6047978SPeter.Dunlap@Sun.COM  * idm_iov_sosend - Sends an iovec on a connection.
6057978SPeter.Dunlap@Sun.COM  *
6067978SPeter.Dunlap@Sun.COM  * This function puts the data provided on the wire by calling sosendmsg.
6077978SPeter.Dunlap@Sun.COM  * It will return only when all the data has been sent or if an error
6087978SPeter.Dunlap@Sun.COM  * occurs.
6097978SPeter.Dunlap@Sun.COM  *
6107978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sosendmsg fails, and
6117978SPeter.Dunlap@Sun.COM  * -1 if sosendmsg returns success but uio_resid != 0
6127978SPeter.Dunlap@Sun.COM  */
6137978SPeter.Dunlap@Sun.COM int
idm_iov_sosend(ksocket_t so,iovec_t * iop,int iovlen,size_t total_len)6148348SEric.Yu@Sun.COM idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
6157978SPeter.Dunlap@Sun.COM {
6167978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
6177978SPeter.Dunlap@Sun.COM 	int			error;
6188348SEric.Yu@Sun.COM 	size_t 			sent = 0;
6197978SPeter.Dunlap@Sun.COM 
6207978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
6217978SPeter.Dunlap@Sun.COM 
6227978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
6237978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
6247978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
6257978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
6267978SPeter.Dunlap@Sun.COM 
6278348SEric.Yu@Sun.COM 	if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED()))
6288348SEric.Yu@Sun.COM 	    == 0) {
6297978SPeter.Dunlap@Sun.COM 		/* Data sent */
6308348SEric.Yu@Sun.COM 		if (sent == total_len) {
6317978SPeter.Dunlap@Sun.COM 			/* All data sent.  Success. */
6327978SPeter.Dunlap@Sun.COM 			return (0);
6337978SPeter.Dunlap@Sun.COM 		} else {
6347978SPeter.Dunlap@Sun.COM 			/* Not all data was sent.  Failure */
6357978SPeter.Dunlap@Sun.COM 			return (-1);
6367978SPeter.Dunlap@Sun.COM 		}
6377978SPeter.Dunlap@Sun.COM 	}
6387978SPeter.Dunlap@Sun.COM 
6397978SPeter.Dunlap@Sun.COM 	/* Send failed */
6407978SPeter.Dunlap@Sun.COM 	return (error);
6417978SPeter.Dunlap@Sun.COM }
6427978SPeter.Dunlap@Sun.COM 
6437978SPeter.Dunlap@Sun.COM /*
6447978SPeter.Dunlap@Sun.COM  * idm_iov_sorecv - Receives an iovec from a connection
6457978SPeter.Dunlap@Sun.COM  *
6467978SPeter.Dunlap@Sun.COM  * This function gets the data asked for from the socket.  It will return
6477978SPeter.Dunlap@Sun.COM  * only when all the requested data has been retrieved or if an error
6487978SPeter.Dunlap@Sun.COM  * occurs.
6497978SPeter.Dunlap@Sun.COM  *
6507978SPeter.Dunlap@Sun.COM  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
6517978SPeter.Dunlap@Sun.COM  * -1 if sorecvmsg returns success but uio_resid != 0
6527978SPeter.Dunlap@Sun.COM  */
6537978SPeter.Dunlap@Sun.COM int
idm_iov_sorecv(ksocket_t so,iovec_t * iop,int iovlen,size_t total_len)6548348SEric.Yu@Sun.COM idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len)
6557978SPeter.Dunlap@Sun.COM {
6567978SPeter.Dunlap@Sun.COM 	struct msghdr		msg;
6577978SPeter.Dunlap@Sun.COM 	int			error;
6588348SEric.Yu@Sun.COM 	size_t			recv;
6598348SEric.Yu@Sun.COM 	int 			flags;
6607978SPeter.Dunlap@Sun.COM 
6617978SPeter.Dunlap@Sun.COM 	ASSERT(iop != NULL);
6627978SPeter.Dunlap@Sun.COM 
6637978SPeter.Dunlap@Sun.COM 	/* Initialization of the message header. */
6647978SPeter.Dunlap@Sun.COM 	bzero(&msg, sizeof (msg));
6657978SPeter.Dunlap@Sun.COM 	msg.msg_iov	= iop;
6667978SPeter.Dunlap@Sun.COM 	msg.msg_iovlen	= iovlen;
6678348SEric.Yu@Sun.COM 	flags		= MSG_WAITALL;
6687978SPeter.Dunlap@Sun.COM 
6698348SEric.Yu@Sun.COM 	if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED()))
6708348SEric.Yu@Sun.COM 	    == 0) {
6717978SPeter.Dunlap@Sun.COM 		/* Received data */
6728348SEric.Yu@Sun.COM 		if (recv == total_len) {
6737978SPeter.Dunlap@Sun.COM 			/* All requested data received.  Success */
6747978SPeter.Dunlap@Sun.COM 			return (0);
6757978SPeter.Dunlap@Sun.COM 		} else {
6767978SPeter.Dunlap@Sun.COM 			/*
6777978SPeter.Dunlap@Sun.COM 			 * Not all data was received.  The connection has
6787978SPeter.Dunlap@Sun.COM 			 * probably failed.
6797978SPeter.Dunlap@Sun.COM 			 */
6807978SPeter.Dunlap@Sun.COM 			return (-1);
6817978SPeter.Dunlap@Sun.COM 		}
6827978SPeter.Dunlap@Sun.COM 	}
6837978SPeter.Dunlap@Sun.COM 
6847978SPeter.Dunlap@Sun.COM 	/* Receive failed */
6857978SPeter.Dunlap@Sun.COM 	return (error);
6867978SPeter.Dunlap@Sun.COM }
6877978SPeter.Dunlap@Sun.COM 
6887978SPeter.Dunlap@Sun.COM static void
idm_set_ini_preconnect_options(idm_so_conn_t * sc,boolean_t boot_conn)68910822SJack.Meng@Sun.COM idm_set_ini_preconnect_options(idm_so_conn_t *sc, boolean_t boot_conn)
6907978SPeter.Dunlap@Sun.COM {
6917978SPeter.Dunlap@Sun.COM 	int	conn_abort = 10000;
6927978SPeter.Dunlap@Sun.COM 	int	conn_notify = 2000;
6937978SPeter.Dunlap@Sun.COM 	int	abort = 30000;
6947978SPeter.Dunlap@Sun.COM 
6957978SPeter.Dunlap@Sun.COM 	/* Pre-connect socket options */
6968348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
6978348SEric.Yu@Sun.COM 	    TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int),
6988348SEric.Yu@Sun.COM 	    CRED());
69910822SJack.Meng@Sun.COM 	if (boot_conn == B_FALSE) {
70010822SJack.Meng@Sun.COM 		(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
70110822SJack.Meng@Sun.COM 		    TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int),
70210822SJack.Meng@Sun.COM 		    CRED());
70310822SJack.Meng@Sun.COM 		(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP,
70410822SJack.Meng@Sun.COM 		    TCP_ABORT_THRESHOLD,
70510822SJack.Meng@Sun.COM 		    (char *)&abort, sizeof (int), CRED());
70610822SJack.Meng@Sun.COM 	}
7077978SPeter.Dunlap@Sun.COM }
7087978SPeter.Dunlap@Sun.COM 
7097978SPeter.Dunlap@Sun.COM static void
idm_set_ini_postconnect_options(idm_so_conn_t * sc)7107978SPeter.Dunlap@Sun.COM idm_set_ini_postconnect_options(idm_so_conn_t *sc)
7117978SPeter.Dunlap@Sun.COM {
7127978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
7137978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
7147978SPeter.Dunlap@Sun.COM 	const int	on = 1;
7157978SPeter.Dunlap@Sun.COM 
7167978SPeter.Dunlap@Sun.COM 	/* Set postconnect options */
7178348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY,
7188348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (int), CRED());
7198348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF,
7208348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
7218348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF,
7228348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
7237978SPeter.Dunlap@Sun.COM }
7247978SPeter.Dunlap@Sun.COM 
7257978SPeter.Dunlap@Sun.COM static void
idm_set_tgt_connect_options(ksocket_t ks)7268348SEric.Yu@Sun.COM idm_set_tgt_connect_options(ksocket_t ks)
7277978SPeter.Dunlap@Sun.COM {
7287978SPeter.Dunlap@Sun.COM 	int32_t		rcvbuf = IDM_RCVBUF_SIZE;
7297978SPeter.Dunlap@Sun.COM 	int32_t		sndbuf = IDM_SNDBUF_SIZE;
7307978SPeter.Dunlap@Sun.COM 	const int	on = 1;
7317978SPeter.Dunlap@Sun.COM 
7327978SPeter.Dunlap@Sun.COM 	/* Set connect options */
7338348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF,
7348348SEric.Yu@Sun.COM 	    (char *)&rcvbuf, sizeof (int), CRED());
7358348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF,
7368348SEric.Yu@Sun.COM 	    (char *)&sndbuf, sizeof (int), CRED());
7378348SEric.Yu@Sun.COM 	(void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY,
7388348SEric.Yu@Sun.COM 	    (char *)&on, sizeof (on), CRED());
7397978SPeter.Dunlap@Sun.COM }
7407978SPeter.Dunlap@Sun.COM 
7417978SPeter.Dunlap@Sun.COM static uint32_t
n2h24(const uchar_t * ptr)7427978SPeter.Dunlap@Sun.COM n2h24(const uchar_t *ptr)
7437978SPeter.Dunlap@Sun.COM {
7447978SPeter.Dunlap@Sun.COM 	return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]);
7457978SPeter.Dunlap@Sun.COM }
7467978SPeter.Dunlap@Sun.COM 
7477978SPeter.Dunlap@Sun.COM 
7487978SPeter.Dunlap@Sun.COM static idm_status_t
idm_sorecvhdr(idm_conn_t * ic,idm_pdu_t * pdu)7497978SPeter.Dunlap@Sun.COM idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu)
7507978SPeter.Dunlap@Sun.COM {
7517978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t	*bhs;
7527978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
7537978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
7547978SPeter.Dunlap@Sun.COM 	void		*new_hdr;
7557978SPeter.Dunlap@Sun.COM 	int		ahslen = 0;
7567978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
7577978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
7587978SPeter.Dunlap@Sun.COM 	struct iovec	iov[2];
7597978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
7607978SPeter.Dunlap@Sun.COM 	int		rc;
7617978SPeter.Dunlap@Sun.COM 
7627978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
7637978SPeter.Dunlap@Sun.COM 
7647978SPeter.Dunlap@Sun.COM 	/*
7657978SPeter.Dunlap@Sun.COM 	 * Read BHS
7667978SPeter.Dunlap@Sun.COM 	 */
7677978SPeter.Dunlap@Sun.COM 	bhs = pdu->isp_hdr;
7687978SPeter.Dunlap@Sun.COM 	rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t));
7697978SPeter.Dunlap@Sun.COM 	if (rc != IDM_STATUS_SUCCESS) {
7707978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
7717978SPeter.Dunlap@Sun.COM 	}
7727978SPeter.Dunlap@Sun.COM 
7737978SPeter.Dunlap@Sun.COM 	/*
7747978SPeter.Dunlap@Sun.COM 	 * Check actual AHS length against the amount available in the buffer
7757978SPeter.Dunlap@Sun.COM 	 */
7767978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t) +
7777978SPeter.Dunlap@Sun.COM 	    (bhs->hlength * sizeof (uint32_t));
7787978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = n2h24(bhs->dlength);
77910261SCharles.Ting@Sun.COM 	if (ic->ic_conn_type == CONN_TYPE_TGT &&
78010261SCharles.Ting@Sun.COM 	    pdu->isp_datalen > ic->ic_conn_params.max_recv_dataseglen) {
78110261SCharles.Ting@Sun.COM 		IDM_CONN_LOG(CE_WARN,
78210261SCharles.Ting@Sun.COM 		    "idm_sorecvhdr: exceeded the max data segment length");
78310261SCharles.Ting@Sun.COM 		return (IDM_STATUS_FAIL);
78410261SCharles.Ting@Sun.COM 	}
7857978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) {
7867978SPeter.Dunlap@Sun.COM 		/* Allocate a new header segment and change the callback */
7877978SPeter.Dunlap@Sun.COM 		new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP);
7887978SPeter.Dunlap@Sun.COM 		bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t));
7897978SPeter.Dunlap@Sun.COM 		pdu->isp_hdr = new_hdr;
7907978SPeter.Dunlap@Sun.COM 		pdu->isp_flags |= IDM_PDU_ADDL_HDR;
7917978SPeter.Dunlap@Sun.COM 
7927978SPeter.Dunlap@Sun.COM 		/*
7937978SPeter.Dunlap@Sun.COM 		 * This callback will restore the expected values after
7947978SPeter.Dunlap@Sun.COM 		 * the RX PDU has been processed.
7957978SPeter.Dunlap@Sun.COM 		 */
7967978SPeter.Dunlap@Sun.COM 		pdu->isp_callback = idm_sorx_addl_pdu_cb;
7977978SPeter.Dunlap@Sun.COM 	}
7987978SPeter.Dunlap@Sun.COM 
7997978SPeter.Dunlap@Sun.COM 	/*
8007978SPeter.Dunlap@Sun.COM 	 * Setup receipt of additional header and header digest (if enabled).
8017978SPeter.Dunlap@Sun.COM 	 */
8027978SPeter.Dunlap@Sun.COM 	if (bhs->hlength > 0) {
8037978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1);
8047978SPeter.Dunlap@Sun.COM 		ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t);
8057978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = ahslen;
8067978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
8077978SPeter.Dunlap@Sun.COM 		iovlen++;
8087978SPeter.Dunlap@Sun.COM 	}
8097978SPeter.Dunlap@Sun.COM 
8107978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
8117978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc;
8127978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len = sizeof (hdr_digest_crc);
8137978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
8147978SPeter.Dunlap@Sun.COM 		iovlen++;
8157978SPeter.Dunlap@Sun.COM 	}
8167978SPeter.Dunlap@Sun.COM 
8177978SPeter.Dunlap@Sun.COM 	if ((iovlen != 0) &&
8187978SPeter.Dunlap@Sun.COM 	    (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen,
8197978SPeter.Dunlap@Sun.COM 	    total_len) != 0)) {
8207978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
8217978SPeter.Dunlap@Sun.COM 	}
8227978SPeter.Dunlap@Sun.COM 
8237978SPeter.Dunlap@Sun.COM 	/*
8247978SPeter.Dunlap@Sun.COM 	 * Validate header digest if enabled
8257978SPeter.Dunlap@Sun.COM 	 */
8267978SPeter.Dunlap@Sun.COM 	if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) {
8277978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_hdr,
8287978SPeter.Dunlap@Sun.COM 		    sizeof (iscsi_hdr_t) + ahslen);
8297978SPeter.Dunlap@Sun.COM 		if (crc_calculated != hdr_digest_crc) {
8307978SPeter.Dunlap@Sun.COM 			/* Invalid Header Digest */
8317978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_HEADER_DIGEST);
8327978SPeter.Dunlap@Sun.COM 		}
8337978SPeter.Dunlap@Sun.COM 	}
8347978SPeter.Dunlap@Sun.COM 
8357978SPeter.Dunlap@Sun.COM 	return (0);
8367978SPeter.Dunlap@Sun.COM }
8377978SPeter.Dunlap@Sun.COM 
8387978SPeter.Dunlap@Sun.COM /*
8397978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create()
8407978SPeter.Dunlap@Sun.COM  * Allocate the sockets transport connection resources.
8417978SPeter.Dunlap@Sun.COM  */
8427978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_ini_conn_create(idm_conn_req_t * cr,idm_conn_t * ic)8437978SPeter.Dunlap@Sun.COM idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic)
8447978SPeter.Dunlap@Sun.COM {
8458348SEric.Yu@Sun.COM 	ksocket_t	so;
8467978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
8477978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
8487978SPeter.Dunlap@Sun.COM 
8497978SPeter.Dunlap@Sun.COM 	so = idm_socreate(cr->cr_domain, cr->cr_type,
8507978SPeter.Dunlap@Sun.COM 	    cr->cr_protocol);
8517978SPeter.Dunlap@Sun.COM 	if (so == NULL) {
8527978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
8537978SPeter.Dunlap@Sun.COM 	}
8547978SPeter.Dunlap@Sun.COM 
8557978SPeter.Dunlap@Sun.COM 	/* Bind the socket if configured to do so */
8567978SPeter.Dunlap@Sun.COM 	if (cr->cr_bound) {
8578348SEric.Yu@Sun.COM 		if (ksocket_bind(so, &cr->cr_bound_addr.sin,
8588348SEric.Yu@Sun.COM 		    SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) {
8597978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so);
8607978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
8617978SPeter.Dunlap@Sun.COM 		}
8627978SPeter.Dunlap@Sun.COM 	}
8637978SPeter.Dunlap@Sun.COM 
8647978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, so);
8657978SPeter.Dunlap@Sun.COM 	if (idmrc != IDM_STATUS_SUCCESS) {
8667978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so);
8677978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so);
8687978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
8697978SPeter.Dunlap@Sun.COM 	}
8707978SPeter.Dunlap@Sun.COM 
8717978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
8727978SPeter.Dunlap@Sun.COM 	/* Set up socket options */
87310822SJack.Meng@Sun.COM 	idm_set_ini_preconnect_options(so_conn, cr->cr_boot_conn);
8747978SPeter.Dunlap@Sun.COM 
8757978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
8767978SPeter.Dunlap@Sun.COM }
8777978SPeter.Dunlap@Sun.COM 
8787978SPeter.Dunlap@Sun.COM /*
8797978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_destroy()
8807978SPeter.Dunlap@Sun.COM  * Tear down the sockets transport connection resources.
8817978SPeter.Dunlap@Sun.COM  */
8827978SPeter.Dunlap@Sun.COM static void
idm_so_ini_conn_destroy(idm_conn_t * ic)8837978SPeter.Dunlap@Sun.COM idm_so_ini_conn_destroy(idm_conn_t *ic)
8847978SPeter.Dunlap@Sun.COM {
8857978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
8867978SPeter.Dunlap@Sun.COM }
8877978SPeter.Dunlap@Sun.COM 
8887978SPeter.Dunlap@Sun.COM /*
8897978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_connect()
8907978SPeter.Dunlap@Sun.COM  * Establish the connection referred to by the handle previously allocated via
8917978SPeter.Dunlap@Sun.COM  * idm_so_ini_conn_create().
8927978SPeter.Dunlap@Sun.COM  */
8937978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_ini_conn_connect(idm_conn_t * ic)8947978SPeter.Dunlap@Sun.COM idm_so_ini_conn_connect(idm_conn_t *ic)
8957978SPeter.Dunlap@Sun.COM {
8967978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
89710156SZhang.Yi@Sun.COM 	struct sonode	*node = NULL;
89810156SZhang.Yi@Sun.COM 	int 		rc;
89910156SZhang.Yi@Sun.COM 	clock_t		lbolt, conn_login_max, conn_login_interval;
90010156SZhang.Yi@Sun.COM 	boolean_t	nonblock;
9017978SPeter.Dunlap@Sun.COM 
9027978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
90310156SZhang.Yi@Sun.COM 	nonblock = ic->ic_conn_params.nonblock_socket;
90410156SZhang.Yi@Sun.COM 	conn_login_max = ic->ic_conn_params.conn_login_max;
90510156SZhang.Yi@Sun.COM 	conn_login_interval = ddi_get_lbolt() +
90610156SZhang.Yi@Sun.COM 	    SEC_TO_TICK(ic->ic_conn_params.conn_login_interval);
90710156SZhang.Yi@Sun.COM 
90810156SZhang.Yi@Sun.COM 	if (nonblock == B_TRUE) {
90910156SZhang.Yi@Sun.COM 		node = ((struct sonode *)(so_conn->ic_so));
91010156SZhang.Yi@Sun.COM 		/* Set to none block socket mode */
91110156SZhang.Yi@Sun.COM 		idm_so_socket_set_nonblock(node);
91210156SZhang.Yi@Sun.COM 		do {
91310156SZhang.Yi@Sun.COM 			rc = ksocket_connect(so_conn->ic_so,
91410156SZhang.Yi@Sun.COM 			    &ic->ic_ini_dst_addr.sin,
91510156SZhang.Yi@Sun.COM 			    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)),
91610156SZhang.Yi@Sun.COM 			    CRED());
91710156SZhang.Yi@Sun.COM 			if (rc == 0 || rc == EISCONN) {
91810156SZhang.Yi@Sun.COM 				/* socket success or already success */
91910156SZhang.Yi@Sun.COM 				rc = IDM_STATUS_SUCCESS;
92010156SZhang.Yi@Sun.COM 				break;
92110156SZhang.Yi@Sun.COM 			}
92210156SZhang.Yi@Sun.COM 			if ((rc == ETIMEDOUT) || (rc == ECONNREFUSED) ||
92310156SZhang.Yi@Sun.COM 			    (rc == ECONNRESET)) {
92410156SZhang.Yi@Sun.COM 				/* socket connection timeout or refuse */
92510156SZhang.Yi@Sun.COM 				break;
92610156SZhang.Yi@Sun.COM 			}
92710156SZhang.Yi@Sun.COM 			lbolt = ddi_get_lbolt();
92810156SZhang.Yi@Sun.COM 			if (lbolt > conn_login_max) {
92910156SZhang.Yi@Sun.COM 				/*
93010156SZhang.Yi@Sun.COM 				 * Connection retry timeout,
93110156SZhang.Yi@Sun.COM 				 * failed connect to target.
93210156SZhang.Yi@Sun.COM 				 */
93310156SZhang.Yi@Sun.COM 				break;
93410156SZhang.Yi@Sun.COM 			}
93510156SZhang.Yi@Sun.COM 			if (lbolt < conn_login_interval) {
93610156SZhang.Yi@Sun.COM 				if ((rc == EINPROGRESS) || (rc == EALREADY)) {
93710156SZhang.Yi@Sun.COM 					/* TCP connect still in progress */
93810156SZhang.Yi@Sun.COM 					delay(SEC_TO_TICK(IN_PROGRESS_DELAY));
93910156SZhang.Yi@Sun.COM 					continue;
94010156SZhang.Yi@Sun.COM 				} else {
94110156SZhang.Yi@Sun.COM 					delay(conn_login_interval - lbolt);
94210156SZhang.Yi@Sun.COM 				}
94310156SZhang.Yi@Sun.COM 			}
94410156SZhang.Yi@Sun.COM 			conn_login_interval = ddi_get_lbolt() +
94510156SZhang.Yi@Sun.COM 			    SEC_TO_TICK(ic->ic_conn_params.conn_login_interval);
94610156SZhang.Yi@Sun.COM 		} while (rc != 0);
94710156SZhang.Yi@Sun.COM 		/* resume to nonblock mode */
94810156SZhang.Yi@Sun.COM 		if (rc == IDM_STATUS_SUCCESS) {
94910156SZhang.Yi@Sun.COM 			idm_so_socket_set_block(node);
95010156SZhang.Yi@Sun.COM 		}
95110156SZhang.Yi@Sun.COM 	} else {
95210156SZhang.Yi@Sun.COM 		rc = ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin,
95310156SZhang.Yi@Sun.COM 		    (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED());
95410156SZhang.Yi@Sun.COM 	}
95510156SZhang.Yi@Sun.COM 
95610156SZhang.Yi@Sun.COM 	if (rc != 0) {
9577978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_conn->ic_so);
9587978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
9597978SPeter.Dunlap@Sun.COM 	}
9607978SPeter.Dunlap@Sun.COM 
9617978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
9627978SPeter.Dunlap@Sun.COM 
9637978SPeter.Dunlap@Sun.COM 	idm_set_ini_postconnect_options(so_conn);
9647978SPeter.Dunlap@Sun.COM 
9657978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
9667978SPeter.Dunlap@Sun.COM }
9677978SPeter.Dunlap@Sun.COM 
9687978SPeter.Dunlap@Sun.COM idm_status_t
idm_so_tgt_conn_create(idm_conn_t * ic,ksocket_t new_so)9698348SEric.Yu@Sun.COM idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so)
9707978SPeter.Dunlap@Sun.COM {
9717978SPeter.Dunlap@Sun.COM 	idm_status_t	idmrc;
9727978SPeter.Dunlap@Sun.COM 
9737978SPeter.Dunlap@Sun.COM 	idmrc = idm_so_conn_create_common(ic, new_so);
9747978SPeter.Dunlap@Sun.COM 
9757978SPeter.Dunlap@Sun.COM 	return (idmrc);
9767978SPeter.Dunlap@Sun.COM }
9777978SPeter.Dunlap@Sun.COM 
9787978SPeter.Dunlap@Sun.COM static void
idm_so_tgt_conn_destroy(idm_conn_t * ic)9797978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_destroy(idm_conn_t *ic)
9807978SPeter.Dunlap@Sun.COM {
9817978SPeter.Dunlap@Sun.COM 	idm_so_conn_destroy_common(ic);
9827978SPeter.Dunlap@Sun.COM }
9837978SPeter.Dunlap@Sun.COM 
9847978SPeter.Dunlap@Sun.COM /*
9857978SPeter.Dunlap@Sun.COM  * idm_so_tgt_conn_connect()
9867978SPeter.Dunlap@Sun.COM  * Establish the connection in ic, passed from idm_tgt_conn_finish(), which
9877978SPeter.Dunlap@Sun.COM  * is invoked from the SM as a result of an inbound connection request.
9887978SPeter.Dunlap@Sun.COM  */
9897978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_tgt_conn_connect(idm_conn_t * ic)9907978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_connect(idm_conn_t *ic)
9917978SPeter.Dunlap@Sun.COM {
9927978SPeter.Dunlap@Sun.COM 	idm_so_conn_connect_common(ic);
9937978SPeter.Dunlap@Sun.COM 
9947978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
9957978SPeter.Dunlap@Sun.COM }
9967978SPeter.Dunlap@Sun.COM 
9977978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_conn_create_common(idm_conn_t * ic,ksocket_t new_so)9988348SEric.Yu@Sun.COM idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so)
9997978SPeter.Dunlap@Sun.COM {
10007978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
10017978SPeter.Dunlap@Sun.COM 
10027978SPeter.Dunlap@Sun.COM 	so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP);
10037978SPeter.Dunlap@Sun.COM 	so_conn->ic_so = new_so;
10047978SPeter.Dunlap@Sun.COM 
10057978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = so_conn;
10067978SPeter.Dunlap@Sun.COM 	ic->ic_transport_hdrlen = 0;
10077978SPeter.Dunlap@Sun.COM 
10087978SPeter.Dunlap@Sun.COM 	/* Set the scoreboarding flag on this connection */
10097978SPeter.Dunlap@Sun.COM 	ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD;
101010261SCharles.Ting@Sun.COM 	ic->ic_conn_params.max_recv_dataseglen =
101110261SCharles.Ting@Sun.COM 	    ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
101210261SCharles.Ting@Sun.COM 	ic->ic_conn_params.max_xmit_dataseglen =
101310261SCharles.Ting@Sun.COM 	    ISCSI_DEFAULT_MAX_XMIT_SEG_LEN;
10147978SPeter.Dunlap@Sun.COM 
10157978SPeter.Dunlap@Sun.COM 	/*
10167978SPeter.Dunlap@Sun.COM 	 * Initialize tx thread mutex and list
10177978SPeter.Dunlap@Sun.COM 	 */
10187978SPeter.Dunlap@Sun.COM 	mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL);
10197978SPeter.Dunlap@Sun.COM 	cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL);
10207978SPeter.Dunlap@Sun.COM 	list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t),
10217978SPeter.Dunlap@Sun.COM 	    offsetof(idm_pdu_t, idm_tx_link));
10227978SPeter.Dunlap@Sun.COM 
10237978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
10247978SPeter.Dunlap@Sun.COM }
10257978SPeter.Dunlap@Sun.COM 
10267978SPeter.Dunlap@Sun.COM static void
idm_so_conn_destroy_common(idm_conn_t * ic)10277978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(idm_conn_t *ic)
10287978SPeter.Dunlap@Sun.COM {
10297978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
10307978SPeter.Dunlap@Sun.COM 
10317978SPeter.Dunlap@Sun.COM 	ic->ic_transport_private = NULL;
10327978SPeter.Dunlap@Sun.COM 	idm_sodestroy(so_conn->ic_so);
10337978SPeter.Dunlap@Sun.COM 	list_destroy(&so_conn->ic_tx_list);
10347978SPeter.Dunlap@Sun.COM 	mutex_destroy(&so_conn->ic_tx_mutex);
10357978SPeter.Dunlap@Sun.COM 	cv_destroy(&so_conn->ic_tx_cv);
10367978SPeter.Dunlap@Sun.COM 
10377978SPeter.Dunlap@Sun.COM 	kmem_free(so_conn, sizeof (idm_so_conn_t));
10387978SPeter.Dunlap@Sun.COM }
10397978SPeter.Dunlap@Sun.COM 
10407978SPeter.Dunlap@Sun.COM static void
idm_so_conn_connect_common(idm_conn_t * ic)10417978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(idm_conn_t *ic)
10427978SPeter.Dunlap@Sun.COM {
10437978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
10448348SEric.Yu@Sun.COM 	struct sockaddr_in6	t_addr;
10458348SEric.Yu@Sun.COM 	socklen_t	t_addrlen = 0;
10467978SPeter.Dunlap@Sun.COM 
10477978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
10488348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
10498348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
10507978SPeter.Dunlap@Sun.COM 
10517978SPeter.Dunlap@Sun.COM 	/* Set the local and remote addresses in the idm conn handle */
105211093SSrivijitha.Dugganapalli@Sun.COM 	(void) ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr,
10538348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
10548348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_laddr, t_addrlen);
105511093SSrivijitha.Dugganapalli@Sun.COM 	(void) ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr,
10568348SEric.Yu@Sun.COM 	    &t_addrlen, CRED());
10578348SEric.Yu@Sun.COM 	bcopy(&t_addr, &ic->ic_raddr, t_addrlen);
10587978SPeter.Dunlap@Sun.COM 
10597978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
10607978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0,
10617978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
10627978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0,
10637978SPeter.Dunlap@Sun.COM 	    &p0, TS_RUN, minclsyspri);
10647978SPeter.Dunlap@Sun.COM 
106511552SPeter.Cudhea@Sun.COM 	while (so_conn->ic_rx_thread_did == 0 ||
106611552SPeter.Cudhea@Sun.COM 	    so_conn->ic_tx_thread_did == 0)
10677978SPeter.Dunlap@Sun.COM 		cv_wait(&ic->ic_cv, &ic->ic_mutex);
10687978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
10697978SPeter.Dunlap@Sun.COM }
10707978SPeter.Dunlap@Sun.COM 
10717978SPeter.Dunlap@Sun.COM /*
10727978SPeter.Dunlap@Sun.COM  * idm_so_conn_disconnect()
10737978SPeter.Dunlap@Sun.COM  * Shutdown the socket connection and stop the thread
10747978SPeter.Dunlap@Sun.COM  */
10757978SPeter.Dunlap@Sun.COM static void
idm_so_conn_disconnect(idm_conn_t * ic)10767978SPeter.Dunlap@Sun.COM idm_so_conn_disconnect(idm_conn_t *ic)
10777978SPeter.Dunlap@Sun.COM {
10787978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
10797978SPeter.Dunlap@Sun.COM 
10807978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
10817978SPeter.Dunlap@Sun.COM 
10827978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
10837978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_FALSE;
10847978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_FALSE;
10857978SPeter.Dunlap@Sun.COM 	/* We need to wakeup the TX thread */
10867978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
10877978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
10887978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
10897978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
10907978SPeter.Dunlap@Sun.COM 
10917978SPeter.Dunlap@Sun.COM 	/* This should wakeup the RX thread if it is sleeping */
10927978SPeter.Dunlap@Sun.COM 	idm_soshutdown(so_conn->ic_so);
10937978SPeter.Dunlap@Sun.COM 
10947978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_tx_thread_did);
10957978SPeter.Dunlap@Sun.COM 	thread_join(so_conn->ic_rx_thread_did);
10967978SPeter.Dunlap@Sun.COM }
10977978SPeter.Dunlap@Sun.COM 
10987978SPeter.Dunlap@Sun.COM /*
10997978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_create()
11007978SPeter.Dunlap@Sun.COM  * Establish a service on an IP address and port.  idm_svc_req_t contains
11017978SPeter.Dunlap@Sun.COM  * the service parameters.
11027978SPeter.Dunlap@Sun.COM  */
11037978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
11047978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_tgt_svc_create(idm_svc_req_t * sr,idm_svc_t * is)11057978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is)
11067978SPeter.Dunlap@Sun.COM {
11077978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
11087978SPeter.Dunlap@Sun.COM 
11097978SPeter.Dunlap@Sun.COM 	so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP);
11107978SPeter.Dunlap@Sun.COM 
11117978SPeter.Dunlap@Sun.COM 	/* Set the new sockets service in svc handle */
11127978SPeter.Dunlap@Sun.COM 	is->is_so_svc = (void *)so_svc;
11137978SPeter.Dunlap@Sun.COM 
11147978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
11157978SPeter.Dunlap@Sun.COM }
11167978SPeter.Dunlap@Sun.COM 
11177978SPeter.Dunlap@Sun.COM /*
11187978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_destroy()
11197978SPeter.Dunlap@Sun.COM  * Teardown sockets resources allocated in idm_so_tgt_svc_create()
11207978SPeter.Dunlap@Sun.COM  */
11217978SPeter.Dunlap@Sun.COM static void
idm_so_tgt_svc_destroy(idm_svc_t * is)11227978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_destroy(idm_svc_t *is)
11237978SPeter.Dunlap@Sun.COM {
11247978SPeter.Dunlap@Sun.COM 	/* the socket will have been torn down; free the service */
11257978SPeter.Dunlap@Sun.COM 	kmem_free(is->is_so_svc, sizeof (idm_so_svc_t));
11267978SPeter.Dunlap@Sun.COM }
11277978SPeter.Dunlap@Sun.COM 
11287978SPeter.Dunlap@Sun.COM /*
11297978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_online()
11307978SPeter.Dunlap@Sun.COM  * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create()
11317978SPeter.Dunlap@Sun.COM  */
11327978SPeter.Dunlap@Sun.COM 
11337978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_tgt_svc_online(idm_svc_t * is)11347978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_online(idm_svc_t *is)
11357978SPeter.Dunlap@Sun.COM {
11367978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
11377978SPeter.Dunlap@Sun.COM 	idm_svc_req_t		*sr = &is->is_svc_req;
11387978SPeter.Dunlap@Sun.COM 	struct sockaddr_in6	sin6_ip;
11397978SPeter.Dunlap@Sun.COM 	const uint32_t		on = 1;
11407978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
11417978SPeter.Dunlap@Sun.COM 
11427978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
11437978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
11447978SPeter.Dunlap@Sun.COM 
11457978SPeter.Dunlap@Sun.COM 	/*
11467978SPeter.Dunlap@Sun.COM 	 * Try creating an IPv6 socket first
11477978SPeter.Dunlap@Sun.COM 	 */
11487978SPeter.Dunlap@Sun.COM 	if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) {
11497978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
11507978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
11517978SPeter.Dunlap@Sun.COM 	} else {
11527978SPeter.Dunlap@Sun.COM 		bzero(&sin6_ip, sizeof (sin6_ip));
11537978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_family = AF_INET6;
11547978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_port = htons(sr->sr_port);
11557978SPeter.Dunlap@Sun.COM 		sin6_ip.sin6_addr = in6addr_any;
11567978SPeter.Dunlap@Sun.COM 
11578348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
11588348SEric.Yu@Sun.COM 		    SO_REUSEADDR, (char *)&on, sizeof (on), CRED());
11597978SPeter.Dunlap@Sun.COM 		/*
11607978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
11617978SPeter.Dunlap@Sun.COM 		 */
11628348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET,
11638348SEric.Yu@Sun.COM 		    SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED());
11647978SPeter.Dunlap@Sun.COM 
11658348SEric.Yu@Sun.COM 		if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip,
11668348SEric.Yu@Sun.COM 		    sizeof (sin6_ip), CRED()) != 0) {
11677978SPeter.Dunlap@Sun.COM 			mutex_exit(&is->is_mutex);
11687978SPeter.Dunlap@Sun.COM 			idm_sodestroy(so_svc->is_so);
11697978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_FAIL);
11707978SPeter.Dunlap@Sun.COM 		}
11717978SPeter.Dunlap@Sun.COM 	}
11727978SPeter.Dunlap@Sun.COM 
11737978SPeter.Dunlap@Sun.COM 	idm_set_tgt_connect_options(so_svc->is_so);
11747978SPeter.Dunlap@Sun.COM 
11758348SEric.Yu@Sun.COM 	if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) {
11767978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
11777978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
11787978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
11797978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
11807978SPeter.Dunlap@Sun.COM 	}
11817978SPeter.Dunlap@Sun.COM 
11827978SPeter.Dunlap@Sun.COM 	/* Launch a watch thread */
11837978SPeter.Dunlap@Sun.COM 	so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher,
11847978SPeter.Dunlap@Sun.COM 	    is, 0, &p0, TS_RUN, minclsyspri);
11857978SPeter.Dunlap@Sun.COM 
11867978SPeter.Dunlap@Sun.COM 	if (so_svc->is_thread == NULL) {
11877978SPeter.Dunlap@Sun.COM 		/* Failure to launch; teardown the socket */
11887978SPeter.Dunlap@Sun.COM 		mutex_exit(&is->is_mutex);
11897978SPeter.Dunlap@Sun.COM 		idm_soshutdown(so_svc->is_so);
11907978SPeter.Dunlap@Sun.COM 		idm_sodestroy(so_svc->is_so);
11917978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
11927978SPeter.Dunlap@Sun.COM 	}
11938348SEric.Yu@Sun.COM 	ksocket_hold(so_svc->is_so);
11947978SPeter.Dunlap@Sun.COM 	/* Wait for the port watcher thread to start */
11957978SPeter.Dunlap@Sun.COM 	while (!so_svc->is_thread_running)
11967978SPeter.Dunlap@Sun.COM 		cv_wait(&is->is_cv, &is->is_mutex);
11977978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
11987978SPeter.Dunlap@Sun.COM 
11997978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12007978SPeter.Dunlap@Sun.COM }
12017978SPeter.Dunlap@Sun.COM 
12027978SPeter.Dunlap@Sun.COM /*
12037978SPeter.Dunlap@Sun.COM  * idm_so_tgt_svc_offline
12047978SPeter.Dunlap@Sun.COM  *
12057978SPeter.Dunlap@Sun.COM  * Stop listening on the IP address and port identified by idm_svc_t.
12067978SPeter.Dunlap@Sun.COM  */
12077978SPeter.Dunlap@Sun.COM static void
idm_so_tgt_svc_offline(idm_svc_t * is)12087978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_offline(idm_svc_t *is)
12097978SPeter.Dunlap@Sun.COM {
12107978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
12117978SPeter.Dunlap@Sun.COM 	mutex_enter(&is->is_mutex);
12127978SPeter.Dunlap@Sun.COM 	so_svc = (idm_so_svc_t *)is->is_so_svc;
12137978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
12147978SPeter.Dunlap@Sun.COM 	mutex_exit(&is->is_mutex);
12157978SPeter.Dunlap@Sun.COM 
12167978SPeter.Dunlap@Sun.COM 	/*
12178348SEric.Yu@Sun.COM 	 * Teardown socket
12187978SPeter.Dunlap@Sun.COM 	 */
12198348SEric.Yu@Sun.COM 	idm_sodestroy(so_svc->is_so);
12207978SPeter.Dunlap@Sun.COM 
12217978SPeter.Dunlap@Sun.COM 	/*
12227978SPeter.Dunlap@Sun.COM 	 * Now we expect the port watcher thread to terminate
12237978SPeter.Dunlap@Sun.COM 	 */
12247978SPeter.Dunlap@Sun.COM 	thread_join(so_svc->is_thread_did);
12257978SPeter.Dunlap@Sun.COM }
12267978SPeter.Dunlap@Sun.COM 
12277978SPeter.Dunlap@Sun.COM /*
12287978SPeter.Dunlap@Sun.COM  * Watch thread for target service connection establishment.
12297978SPeter.Dunlap@Sun.COM  */
12307978SPeter.Dunlap@Sun.COM void
idm_so_svc_port_watcher(void * arg)12317978SPeter.Dunlap@Sun.COM idm_so_svc_port_watcher(void *arg)
12327978SPeter.Dunlap@Sun.COM {
12337978SPeter.Dunlap@Sun.COM 	idm_svc_t		*svc = arg;
12348348SEric.Yu@Sun.COM 	ksocket_t		new_so;
12357978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
12367978SPeter.Dunlap@Sun.COM 	idm_status_t		idmrc;
12377978SPeter.Dunlap@Sun.COM 	idm_so_svc_t		*so_svc;
12387978SPeter.Dunlap@Sun.COM 	int			rc;
12397978SPeter.Dunlap@Sun.COM 	const uint32_t		off = 0;
12408348SEric.Yu@Sun.COM 	struct sockaddr_in6 	t_addr;
12418348SEric.Yu@Sun.COM 	socklen_t		t_addrlen;
12427978SPeter.Dunlap@Sun.COM 
12438348SEric.Yu@Sun.COM 	bzero(&t_addr, sizeof (struct sockaddr_in6));
12448348SEric.Yu@Sun.COM 	t_addrlen = sizeof (struct sockaddr_in6);
12457978SPeter.Dunlap@Sun.COM 	mutex_enter(&svc->is_mutex);
12467978SPeter.Dunlap@Sun.COM 
12477978SPeter.Dunlap@Sun.COM 	so_svc = svc->is_so_svc;
12487978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_TRUE;
12497978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_did = so_svc->is_thread->t_did;
12507978SPeter.Dunlap@Sun.COM 
12517978SPeter.Dunlap@Sun.COM 	cv_signal(&svc->is_cv);
12527978SPeter.Dunlap@Sun.COM 
12537978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc,
12547978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
12557978SPeter.Dunlap@Sun.COM 
12567978SPeter.Dunlap@Sun.COM 	while (so_svc->is_thread_running) {
12577978SPeter.Dunlap@Sun.COM 		mutex_exit(&svc->is_mutex);
12587978SPeter.Dunlap@Sun.COM 
12598348SEric.Yu@Sun.COM 		if ((rc = ksocket_accept(so_svc->is_so,
12608348SEric.Yu@Sun.COM 		    (struct sockaddr *)&t_addr, &t_addrlen,
12618348SEric.Yu@Sun.COM 		    &new_so, CRED())) != 0) {
12627978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
12637978SPeter.Dunlap@Sun.COM 			if (rc == ECONNABORTED)
12647978SPeter.Dunlap@Sun.COM 				continue;
12657978SPeter.Dunlap@Sun.COM 			/* Connection problem */
12667978SPeter.Dunlap@Sun.COM 			break;
12677978SPeter.Dunlap@Sun.COM 		}
12687978SPeter.Dunlap@Sun.COM 		/*
12697978SPeter.Dunlap@Sun.COM 		 * Turn off SO_MAC_EXEMPT so future sobinds succeed
12707978SPeter.Dunlap@Sun.COM 		 */
12718348SEric.Yu@Sun.COM 		(void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT,
12728348SEric.Yu@Sun.COM 		    (char *)&off, sizeof (off), CRED());
12737978SPeter.Dunlap@Sun.COM 
12747978SPeter.Dunlap@Sun.COM 		idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS,
12757978SPeter.Dunlap@Sun.COM 		    &ic);
12767978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
12777978SPeter.Dunlap@Sun.COM 			/* Drop connection */
12787978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
12797978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
12807978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
12817978SPeter.Dunlap@Sun.COM 			continue;
12827978SPeter.Dunlap@Sun.COM 		}
12837978SPeter.Dunlap@Sun.COM 
12847978SPeter.Dunlap@Sun.COM 		idmrc = idm_so_tgt_conn_create(ic, new_so);
12857978SPeter.Dunlap@Sun.COM 		if (idmrc != IDM_STATUS_SUCCESS) {
12867978SPeter.Dunlap@Sun.COM 			idm_svc_conn_destroy(ic);
12877978SPeter.Dunlap@Sun.COM 			idm_soshutdown(new_so);
12887978SPeter.Dunlap@Sun.COM 			idm_sodestroy(new_so);
12897978SPeter.Dunlap@Sun.COM 			mutex_enter(&svc->is_mutex);
12907978SPeter.Dunlap@Sun.COM 			continue;
12917978SPeter.Dunlap@Sun.COM 		}
12927978SPeter.Dunlap@Sun.COM 
12937978SPeter.Dunlap@Sun.COM 		/*
12947978SPeter.Dunlap@Sun.COM 		 * Kick the state machine.  At CS_S3_XPT_UP the state machine
12957978SPeter.Dunlap@Sun.COM 		 * will notify the client (target) about the new connection.
12967978SPeter.Dunlap@Sun.COM 		 */
12977978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL);
12987978SPeter.Dunlap@Sun.COM 
12997978SPeter.Dunlap@Sun.COM 		mutex_enter(&svc->is_mutex);
13007978SPeter.Dunlap@Sun.COM 	}
13018348SEric.Yu@Sun.COM 	ksocket_rele(so_svc->is_so);
13027978SPeter.Dunlap@Sun.COM 	so_svc->is_thread_running = B_FALSE;
13037978SPeter.Dunlap@Sun.COM 	mutex_exit(&svc->is_mutex);
13047978SPeter.Dunlap@Sun.COM 
13057978SPeter.Dunlap@Sun.COM 	IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc,
13067978SPeter.Dunlap@Sun.COM 	    svc->is_svc_req.sr_port);
13077978SPeter.Dunlap@Sun.COM 
13087978SPeter.Dunlap@Sun.COM 	thread_exit();
13097978SPeter.Dunlap@Sun.COM }
13107978SPeter.Dunlap@Sun.COM 
13117978SPeter.Dunlap@Sun.COM /*
13127978SPeter.Dunlap@Sun.COM  * idm_so_free_task_rsrc() stops any ongoing processing of the task and
13137978SPeter.Dunlap@Sun.COM  * frees resources associated with the task.
13147978SPeter.Dunlap@Sun.COM  *
13157978SPeter.Dunlap@Sun.COM  * It's not clear that this should return idm_status_t.  What do we do
13167978SPeter.Dunlap@Sun.COM  * if it fails?
13177978SPeter.Dunlap@Sun.COM  */
13187978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_free_task_rsrc(idm_task_t * idt)13197978SPeter.Dunlap@Sun.COM idm_so_free_task_rsrc(idm_task_t *idt)
13207978SPeter.Dunlap@Sun.COM {
132111078SPeter.Cudhea@Sun.COM 	idm_buf_t	*idb, *next_idb;
13227978SPeter.Dunlap@Sun.COM 
13237978SPeter.Dunlap@Sun.COM 	/*
13249162SPeter.Dunlap@Sun.COM 	 * There is nothing to cleanup on initiator connections
13259162SPeter.Dunlap@Sun.COM 	 */
13269162SPeter.Dunlap@Sun.COM 	if (IDM_CONN_ISINI(idt->idt_ic))
13279162SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
13289162SPeter.Dunlap@Sun.COM 
13299162SPeter.Dunlap@Sun.COM 	/*
13307978SPeter.Dunlap@Sun.COM 	 * If this is a target connection, call idm_buf_rx_from_ini_done for
13317978SPeter.Dunlap@Sun.COM 	 * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE.
13327978SPeter.Dunlap@Sun.COM 	 *
13337978SPeter.Dunlap@Sun.COM 	 * In addition, remove any buffers associated with this task from
13347978SPeter.Dunlap@Sun.COM 	 * the ic_tx_list.  We'll do this by walking the idt_inbufv list, but
13357978SPeter.Dunlap@Sun.COM 	 * items don't actually get removed from that list (and completion
13367978SPeter.Dunlap@Sun.COM 	 * routines called) until idm_task_cleanup.
13377978SPeter.Dunlap@Sun.COM 	 */
13387978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
13397978SPeter.Dunlap@Sun.COM 
134011078SPeter.Cudhea@Sun.COM 	for (idb = list_head(&idt->idt_outbufv); idb != NULL; idb = next_idb) {
134111078SPeter.Cudhea@Sun.COM 		next_idb = list_next(&idt->idt_outbufv, idb);
13427978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport) {
13437978SPeter.Dunlap@Sun.COM 			/*
13447978SPeter.Dunlap@Sun.COM 			 * idm_buf_rx_from_ini_done releases idt->idt_mutex
13457978SPeter.Dunlap@Sun.COM 			 */
13469721SPriya.Krishnan@Sun.COM 			DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
13479721SPriya.Krishnan@Sun.COM 			    uintptr_t, idb->idb_buf,
13489721SPriya.Krishnan@Sun.COM 			    uint32_t, idb->idb_bufoffset,
13499721SPriya.Krishnan@Sun.COM 			    uint64_t, 0, uint32_t, 0, uint32_t, 0,
13509721SPriya.Krishnan@Sun.COM 			    uint32_t, idb->idb_xfer_len,
13519721SPriya.Krishnan@Sun.COM 			    int, XFER_BUF_RX_FROM_INI);
13527978SPeter.Dunlap@Sun.COM 			idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED);
13537978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
13547978SPeter.Dunlap@Sun.COM 		}
13557978SPeter.Dunlap@Sun.COM 	}
13567978SPeter.Dunlap@Sun.COM 
135711078SPeter.Cudhea@Sun.COM 	for (idb = list_head(&idt->idt_inbufv); idb != NULL; idb = next_idb) {
135811078SPeter.Cudhea@Sun.COM 		next_idb = list_next(&idt->idt_inbufv, idb);
13597978SPeter.Dunlap@Sun.COM 		/*
13607978SPeter.Dunlap@Sun.COM 		 * We want to remove these items from the tx_list as well,
13617978SPeter.Dunlap@Sun.COM 		 * but knowing it's in the idt_inbufv list is not a guarantee
13627978SPeter.Dunlap@Sun.COM 		 * that it's in the tx_list.  If it's on the tx list then
13637978SPeter.Dunlap@Sun.COM 		 * let idm_sotx_thread() clean it up.
13647978SPeter.Dunlap@Sun.COM 		 */
13657978SPeter.Dunlap@Sun.COM 		if (idb->idb_in_transport && !idb->idb_tx_thread) {
13667978SPeter.Dunlap@Sun.COM 			/*
13677978SPeter.Dunlap@Sun.COM 			 * idm_buf_tx_to_ini_done releases idt->idt_mutex
13687978SPeter.Dunlap@Sun.COM 			 */
13699721SPriya.Krishnan@Sun.COM 			DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
13709721SPriya.Krishnan@Sun.COM 			    uintptr_t, idb->idb_buf,
13719721SPriya.Krishnan@Sun.COM 			    uint32_t, idb->idb_bufoffset,
13729721SPriya.Krishnan@Sun.COM 			    uint64_t, 0, uint32_t, 0, uint32_t, 0,
13739721SPriya.Krishnan@Sun.COM 			    uint32_t, idb->idb_xfer_len,
13749721SPriya.Krishnan@Sun.COM 			    int, XFER_BUF_TX_TO_INI);
13757978SPeter.Dunlap@Sun.COM 			idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
13767978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
13777978SPeter.Dunlap@Sun.COM 		}
13787978SPeter.Dunlap@Sun.COM 	}
13797978SPeter.Dunlap@Sun.COM 
13807978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
13817978SPeter.Dunlap@Sun.COM 
13827978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
13837978SPeter.Dunlap@Sun.COM }
13847978SPeter.Dunlap@Sun.COM 
13857978SPeter.Dunlap@Sun.COM /*
13867978SPeter.Dunlap@Sun.COM  * idm_so_negotiate_key_values() validates the key values for this connection
13877978SPeter.Dunlap@Sun.COM  */
13887978SPeter.Dunlap@Sun.COM /* ARGSUSED */
13897978SPeter.Dunlap@Sun.COM static kv_status_t
idm_so_negotiate_key_values(idm_conn_t * it,nvlist_t * request_nvl,nvlist_t * response_nvl,nvlist_t * negotiated_nvl)13907978SPeter.Dunlap@Sun.COM idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl,
13917978SPeter.Dunlap@Sun.COM     nvlist_t *response_nvl, nvlist_t *negotiated_nvl)
13927978SPeter.Dunlap@Sun.COM {
13937978SPeter.Dunlap@Sun.COM 	/* All parameters are negotiated at the iscsit level */
13947978SPeter.Dunlap@Sun.COM 	return (KV_HANDLED);
13957978SPeter.Dunlap@Sun.COM }
13967978SPeter.Dunlap@Sun.COM 
13977978SPeter.Dunlap@Sun.COM /*
13987978SPeter.Dunlap@Sun.COM  * idm_so_notice_key_values() activates the negotiated key values for
13997978SPeter.Dunlap@Sun.COM  * this connection.
14007978SPeter.Dunlap@Sun.COM  */
14019162SPeter.Dunlap@Sun.COM static void
idm_so_notice_key_values(idm_conn_t * it,nvlist_t * negotiated_nvl)14027978SPeter.Dunlap@Sun.COM idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl)
14037978SPeter.Dunlap@Sun.COM {
14047978SPeter.Dunlap@Sun.COM 	char			*nvp_name;
14057978SPeter.Dunlap@Sun.COM 	nvpair_t		*nvp;
14067978SPeter.Dunlap@Sun.COM 	nvpair_t		*next_nvp;
14077978SPeter.Dunlap@Sun.COM 	int			nvrc;
14087978SPeter.Dunlap@Sun.COM 	idm_status_t		idm_status;
14097978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t	*ikvx;
141010261SCharles.Ting@Sun.COM 	uint64_t		num_val;
14117978SPeter.Dunlap@Sun.COM 
14127978SPeter.Dunlap@Sun.COM 	for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL);
14137978SPeter.Dunlap@Sun.COM 	    nvp != NULL; nvp = next_nvp) {
14147978SPeter.Dunlap@Sun.COM 		next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp);
14157978SPeter.Dunlap@Sun.COM 		nvp_name = nvpair_name(nvp);
14167978SPeter.Dunlap@Sun.COM 
14177978SPeter.Dunlap@Sun.COM 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
14187978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
14197978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
14207978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
14217978SPeter.Dunlap@Sun.COM 			idm_status = idm_so_handle_digest(it, nvp, ikvx);
14227978SPeter.Dunlap@Sun.COM 			ASSERT(idm_status == 0);
14237978SPeter.Dunlap@Sun.COM 
14247978SPeter.Dunlap@Sun.COM 			/* Remove processed item from negotiated_nvl list */
14257978SPeter.Dunlap@Sun.COM 			nvrc = nvlist_remove_all(
14267978SPeter.Dunlap@Sun.COM 			    negotiated_nvl, ikvx->ik_key_name);
14277978SPeter.Dunlap@Sun.COM 			ASSERT(nvrc == 0);
14287978SPeter.Dunlap@Sun.COM 			break;
142910261SCharles.Ting@Sun.COM 		case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
143010261SCharles.Ting@Sun.COM 			/*
143110261SCharles.Ting@Sun.COM 			 * Just pass the value down to idm layer.
143210261SCharles.Ting@Sun.COM 			 * No need to remove it from negotiated_nvl list here.
143310261SCharles.Ting@Sun.COM 			 */
143410261SCharles.Ting@Sun.COM 			nvrc = nvpair_value_uint64(nvp, &num_val);
143510261SCharles.Ting@Sun.COM 			ASSERT(nvrc == 0);
143610261SCharles.Ting@Sun.COM 			it->ic_conn_params.max_xmit_dataseglen =
143710261SCharles.Ting@Sun.COM 			    (uint32_t)num_val;
143810261SCharles.Ting@Sun.COM 			break;
14397978SPeter.Dunlap@Sun.COM 		default:
14407978SPeter.Dunlap@Sun.COM 			break;
14417978SPeter.Dunlap@Sun.COM 		}
14427978SPeter.Dunlap@Sun.COM 	}
14437978SPeter.Dunlap@Sun.COM }
14447978SPeter.Dunlap@Sun.COM 
144510261SCharles.Ting@Sun.COM /*
144610261SCharles.Ting@Sun.COM  * idm_so_declare_key_values() declares the key values for this connection
144710261SCharles.Ting@Sun.COM  */
144810261SCharles.Ting@Sun.COM /* ARGSUSED */
144910261SCharles.Ting@Sun.COM static kv_status_t
idm_so_declare_key_values(idm_conn_t * it,nvlist_t * config_nvl,nvlist_t * outgoing_nvl)145010261SCharles.Ting@Sun.COM idm_so_declare_key_values(idm_conn_t *it, nvlist_t *config_nvl,
145110261SCharles.Ting@Sun.COM     nvlist_t *outgoing_nvl)
145210261SCharles.Ting@Sun.COM {
145310261SCharles.Ting@Sun.COM 	char			*nvp_name;
145410261SCharles.Ting@Sun.COM 	nvpair_t		*nvp;
145510261SCharles.Ting@Sun.COM 	nvpair_t		*next_nvp;
145610261SCharles.Ting@Sun.COM 	kv_status_t		kvrc;
145710261SCharles.Ting@Sun.COM 	int			nvrc = 0;
145810261SCharles.Ting@Sun.COM 	const idm_kv_xlate_t	*ikvx;
145910261SCharles.Ting@Sun.COM 	uint64_t		num_val;
146010261SCharles.Ting@Sun.COM 
146110261SCharles.Ting@Sun.COM 	for (nvp = nvlist_next_nvpair(config_nvl, NULL);
146210261SCharles.Ting@Sun.COM 	    nvp != NULL && nvrc == 0; nvp = next_nvp) {
146310261SCharles.Ting@Sun.COM 		next_nvp = nvlist_next_nvpair(config_nvl, nvp);
146410261SCharles.Ting@Sun.COM 		nvp_name = nvpair_name(nvp);
146510261SCharles.Ting@Sun.COM 
146610261SCharles.Ting@Sun.COM 		ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name));
146710261SCharles.Ting@Sun.COM 		switch (ikvx->ik_key_id) {
146810261SCharles.Ting@Sun.COM 		case KI_MAX_RECV_DATA_SEGMENT_LENGTH:
146910261SCharles.Ting@Sun.COM 			if ((nvrc = nvpair_value_uint64(nvp, &num_val)) != 0) {
147010261SCharles.Ting@Sun.COM 				break;
147110261SCharles.Ting@Sun.COM 			}
147210261SCharles.Ting@Sun.COM 			if (outgoing_nvl &&
147310261SCharles.Ting@Sun.COM 			    (nvrc = nvlist_add_uint64(outgoing_nvl,
147410261SCharles.Ting@Sun.COM 			    nvp_name, num_val)) != 0) {
147510261SCharles.Ting@Sun.COM 				break;
147610261SCharles.Ting@Sun.COM 			}
147710261SCharles.Ting@Sun.COM 			it->ic_conn_params.max_recv_dataseglen =
147810261SCharles.Ting@Sun.COM 			    (uint32_t)num_val;
147910261SCharles.Ting@Sun.COM 			break;
148010261SCharles.Ting@Sun.COM 		default:
148110261SCharles.Ting@Sun.COM 			break;
148210261SCharles.Ting@Sun.COM 		}
148310261SCharles.Ting@Sun.COM 	}
148410261SCharles.Ting@Sun.COM 	kvrc = idm_nvstat_to_kvstat(nvrc);
148510261SCharles.Ting@Sun.COM 	return (kvrc);
148610261SCharles.Ting@Sun.COM }
14877978SPeter.Dunlap@Sun.COM 
14887978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_handle_digest(idm_conn_t * it,nvpair_t * digest_choice,const idm_kv_xlate_t * ikvx)14897978SPeter.Dunlap@Sun.COM idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice,
14907978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx)
14917978SPeter.Dunlap@Sun.COM {
14927978SPeter.Dunlap@Sun.COM 	int			nvrc;
14937978SPeter.Dunlap@Sun.COM 	char			*digest_choice_string;
14947978SPeter.Dunlap@Sun.COM 
14957978SPeter.Dunlap@Sun.COM 	nvrc = nvpair_value_string(digest_choice,
14967978SPeter.Dunlap@Sun.COM 	    &digest_choice_string);
14977978SPeter.Dunlap@Sun.COM 	ASSERT(nvrc == 0);
14987978SPeter.Dunlap@Sun.COM 	if (strcasecmp(digest_choice_string, "crc32c") == 0) {
14997978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
15007978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
15017978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST;
15027978SPeter.Dunlap@Sun.COM 			break;
15037978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
15047978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags |= IDM_CONN_DATA_DIGEST;
15057978SPeter.Dunlap@Sun.COM 			break;
15067978SPeter.Dunlap@Sun.COM 		default:
15077978SPeter.Dunlap@Sun.COM 			ASSERT(0);
15087978SPeter.Dunlap@Sun.COM 			break;
15097978SPeter.Dunlap@Sun.COM 		}
15107978SPeter.Dunlap@Sun.COM 	} else if (strcasecmp(digest_choice_string, "none") == 0) {
15117978SPeter.Dunlap@Sun.COM 		switch (ikvx->ik_key_id) {
15127978SPeter.Dunlap@Sun.COM 		case KI_HEADER_DIGEST:
15137978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST;
15147978SPeter.Dunlap@Sun.COM 			break;
15157978SPeter.Dunlap@Sun.COM 		case KI_DATA_DIGEST:
15167978SPeter.Dunlap@Sun.COM 			it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST;
15177978SPeter.Dunlap@Sun.COM 			break;
15187978SPeter.Dunlap@Sun.COM 		default:
15197978SPeter.Dunlap@Sun.COM 			ASSERT(0);
15207978SPeter.Dunlap@Sun.COM 			break;
15217978SPeter.Dunlap@Sun.COM 		}
15227978SPeter.Dunlap@Sun.COM 	} else {
15237978SPeter.Dunlap@Sun.COM 		ASSERT(0);
15247978SPeter.Dunlap@Sun.COM 	}
15257978SPeter.Dunlap@Sun.COM 
15267978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
15277978SPeter.Dunlap@Sun.COM }
15287978SPeter.Dunlap@Sun.COM 
15297978SPeter.Dunlap@Sun.COM 
15307978SPeter.Dunlap@Sun.COM /*
15317978SPeter.Dunlap@Sun.COM  * idm_so_conn_is_capable() verifies that the passed connection is provided
15327978SPeter.Dunlap@Sun.COM  * for by the sockets interface.
15337978SPeter.Dunlap@Sun.COM  */
15347978SPeter.Dunlap@Sun.COM /* ARGSUSED */
15357978SPeter.Dunlap@Sun.COM static boolean_t
idm_so_conn_is_capable(idm_conn_req_t * ic,idm_transport_caps_t * caps)15367978SPeter.Dunlap@Sun.COM idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps)
15377978SPeter.Dunlap@Sun.COM {
15387978SPeter.Dunlap@Sun.COM 	return (B_TRUE);
15397978SPeter.Dunlap@Sun.COM }
15407978SPeter.Dunlap@Sun.COM 
15417978SPeter.Dunlap@Sun.COM /*
15427978SPeter.Dunlap@Sun.COM  * idm_so_rx_datain() validates the Data Sequence number of the PDU. The
15437978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() function invoked earlier actually reads the data
15447978SPeter.Dunlap@Sun.COM  * off the socket into the appropriate buffers.
15457978SPeter.Dunlap@Sun.COM  */
15467978SPeter.Dunlap@Sun.COM static void
idm_so_rx_datain(idm_conn_t * ic,idm_pdu_t * pdu)15477978SPeter.Dunlap@Sun.COM idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu)
15487978SPeter.Dunlap@Sun.COM {
15497978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
15507978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
15517978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
15527978SPeter.Dunlap@Sun.COM 	uint32_t		datasn;
15537978SPeter.Dunlap@Sun.COM 	size_t			offset;
15547978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t		*ihp = (iscsi_hdr_t *)pdu->isp_hdr;
15557978SPeter.Dunlap@Sun.COM 	iscsi_data_rsp_hdr_t    *idrhp = (iscsi_data_rsp_hdr_t *)ihp;
15567978SPeter.Dunlap@Sun.COM 
15577978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
15587978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
15597978SPeter.Dunlap@Sun.COM 
15607978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
15617978SPeter.Dunlap@Sun.COM 	datasn	= ntohl(bhs->datasn);
15627978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
15637978SPeter.Dunlap@Sun.COM 
15647978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP);
15657978SPeter.Dunlap@Sun.COM 
15667978SPeter.Dunlap@Sun.COM 	/*
15677978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
15687978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
15697978SPeter.Dunlap@Sun.COM 	 */
15707978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
15717978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
15727978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task");
15737978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15747978SPeter.Dunlap@Sun.COM 		return;
15757978SPeter.Dunlap@Sun.COM 	}
15767978SPeter.Dunlap@Sun.COM 
15777978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
15787978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
15797978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
15807978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_datain: failed to find buffer");
15817978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
15827978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15837978SPeter.Dunlap@Sun.COM 		return;
15847978SPeter.Dunlap@Sun.COM 	}
15857978SPeter.Dunlap@Sun.COM 
15867978SPeter.Dunlap@Sun.COM 	/*
15877978SPeter.Dunlap@Sun.COM 	 * DataSN values should be sequential and should not have any gaps or
15887978SPeter.Dunlap@Sun.COM 	 * repetitions. Check the DataSN with the one stored in the task.
15897978SPeter.Dunlap@Sun.COM 	 */
15907978SPeter.Dunlap@Sun.COM 	if (datasn == idt->idt_exp_datasn) {
15917978SPeter.Dunlap@Sun.COM 		idt->idt_exp_datasn++; /* keep track of DataSN received */
15927978SPeter.Dunlap@Sun.COM 	} else {
15937978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order");
15947978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
15957978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
15967978SPeter.Dunlap@Sun.COM 		return;
15977978SPeter.Dunlap@Sun.COM 	}
15987978SPeter.Dunlap@Sun.COM 
15997978SPeter.Dunlap@Sun.COM 	/*
16007978SPeter.Dunlap@Sun.COM 	 * PDUs in a sequence should be in continuously increasing
16017978SPeter.Dunlap@Sun.COM 	 * address offset
16027978SPeter.Dunlap@Sun.COM 	 */
16037978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
16047978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset");
16059162SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
16067978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
16077978SPeter.Dunlap@Sun.COM 		return;
16087978SPeter.Dunlap@Sun.COM 	}
16097978SPeter.Dunlap@Sun.COM 	/* Expected next relative buffer offset */
16107978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += n2h24(bhs->dlength);
16119162SPeter.Dunlap@Sun.COM 	idt->idt_rx_bytes += n2h24(bhs->dlength);
16129162SPeter.Dunlap@Sun.COM 
16139162SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
16147978SPeter.Dunlap@Sun.COM 
16157978SPeter.Dunlap@Sun.COM 	/*
16167978SPeter.Dunlap@Sun.COM 	 * For now call scsi_rsp which will process the data rsp
16177978SPeter.Dunlap@Sun.COM 	 * Revisit, need to provide an explicit client entry point for
16187978SPeter.Dunlap@Sun.COM 	 * phase collapse completions.
16197978SPeter.Dunlap@Sun.COM 	 */
16207978SPeter.Dunlap@Sun.COM 	if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) &&
16217978SPeter.Dunlap@Sun.COM 	    (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) {
16227978SPeter.Dunlap@Sun.COM 		(*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu);
16237978SPeter.Dunlap@Sun.COM 	}
16247978SPeter.Dunlap@Sun.COM 
16257978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
16267978SPeter.Dunlap@Sun.COM }
16277978SPeter.Dunlap@Sun.COM 
16287978SPeter.Dunlap@Sun.COM /*
16297978SPeter.Dunlap@Sun.COM  * The idm_so_rx_dataout() function is used by the iSCSI target to read
16307978SPeter.Dunlap@Sun.COM  * data from the Data-Out PDU sent by the iSCSI initiator.
16317978SPeter.Dunlap@Sun.COM  *
16327978SPeter.Dunlap@Sun.COM  * This function gets the Initiator Task Tag from the PDU BHS and looks up the
16337978SPeter.Dunlap@Sun.COM  * task to get the buffers associated with the PDU. A PDU might span buffers.
16347978SPeter.Dunlap@Sun.COM  * The data is then read into the respective buffer.
16357978SPeter.Dunlap@Sun.COM  */
16367978SPeter.Dunlap@Sun.COM static void
idm_so_rx_dataout(idm_conn_t * ic,idm_pdu_t * pdu)16377978SPeter.Dunlap@Sun.COM idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu)
16387978SPeter.Dunlap@Sun.COM {
16397978SPeter.Dunlap@Sun.COM 
16407978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
16417978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
16427978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
16437978SPeter.Dunlap@Sun.COM 	size_t			offset;
16447978SPeter.Dunlap@Sun.COM 
16457978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
16467978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
16477978SPeter.Dunlap@Sun.COM 
16487978SPeter.Dunlap@Sun.COM 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
16497978SPeter.Dunlap@Sun.COM 	offset = ntohl(bhs->offset);
16507978SPeter.Dunlap@Sun.COM 	ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA);
16517978SPeter.Dunlap@Sun.COM 
16527978SPeter.Dunlap@Sun.COM 	/*
16537978SPeter.Dunlap@Sun.COM 	 * Look up the task corresponding to the initiator task tag
16547978SPeter.Dunlap@Sun.COM 	 * to get the buffers affiliated with the task.
16557978SPeter.Dunlap@Sun.COM 	 */
16567978SPeter.Dunlap@Sun.COM 	idt = idm_task_find(ic, bhs->itt, bhs->ttt);
16577978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
16587978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
16597978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find task");
16607978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
16617978SPeter.Dunlap@Sun.COM 		return;
16627978SPeter.Dunlap@Sun.COM 	}
16637978SPeter.Dunlap@Sun.COM 
16647978SPeter.Dunlap@Sun.COM 	idb = pdu->isp_sorx_buf;
16657978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
16667978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
16677978SPeter.Dunlap@Sun.COM 		    "idm_so_rx_dataout: failed to find buffer");
16687978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
16697978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
16707978SPeter.Dunlap@Sun.COM 		return;
16717978SPeter.Dunlap@Sun.COM 	}
16727978SPeter.Dunlap@Sun.COM 
16737978SPeter.Dunlap@Sun.COM 	/* Keep track of data transferred - check data offsets */
16747978SPeter.Dunlap@Sun.COM 	if (offset != idb->idb_exp_offset) {
16757978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: "
16767978SPeter.Dunlap@Sun.COM 		    "%ld, %d", offset, idb->idb_exp_offset);
16777978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
16787978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
16797978SPeter.Dunlap@Sun.COM 		return;
16807978SPeter.Dunlap@Sun.COM 	}
16817978SPeter.Dunlap@Sun.COM 	/* Expected next relative offset */
16827978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset += ntoh24(bhs->dlength);
16839162SPeter.Dunlap@Sun.COM 	idt->idt_rx_bytes += n2h24(bhs->dlength);
16847978SPeter.Dunlap@Sun.COM 
16857978SPeter.Dunlap@Sun.COM 	/*
16867978SPeter.Dunlap@Sun.COM 	 * Call the buffer callback when the transfer is complete
16877978SPeter.Dunlap@Sun.COM 	 *
16887978SPeter.Dunlap@Sun.COM 	 * The connection state machine should only abort tasks after
16897978SPeter.Dunlap@Sun.COM 	 * shutting down the connection so we are assured that there
16907978SPeter.Dunlap@Sun.COM 	 * won't be a simultaneous attempt to abort this task at the
16917978SPeter.Dunlap@Sun.COM 	 * same time as we are processing this PDU (due to a connection
16927978SPeter.Dunlap@Sun.COM 	 * state change).
16937978SPeter.Dunlap@Sun.COM 	 */
16947978SPeter.Dunlap@Sun.COM 	if (bhs->flags & ISCSI_FLAG_FINAL) {
16957978SPeter.Dunlap@Sun.COM 		/*
16967978SPeter.Dunlap@Sun.COM 		 * We only want to call idm_buf_rx_from_ini_done once
16977978SPeter.Dunlap@Sun.COM 		 * per transfer.  It's possible that this task has
16987978SPeter.Dunlap@Sun.COM 		 * already been aborted in which case
16997978SPeter.Dunlap@Sun.COM 		 * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done
17007978SPeter.Dunlap@Sun.COM 		 * for each buffer with idb_in_transport==B_TRUE.  To
17017978SPeter.Dunlap@Sun.COM 		 * close this window and ensure that this doesn't happen,
17027978SPeter.Dunlap@Sun.COM 		 * we'll clear idb->idb_in_transport now while holding
17037978SPeter.Dunlap@Sun.COM 		 * the task mutex.   This is only really an issue for
17047978SPeter.Dunlap@Sun.COM 		 * SCSI task abort -- if tasks were being aborted because
17057978SPeter.Dunlap@Sun.COM 		 * of a connection state change the state machine would
17067978SPeter.Dunlap@Sun.COM 		 * have already stopped the receive thread.
17077978SPeter.Dunlap@Sun.COM 		 */
17087978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
17097978SPeter.Dunlap@Sun.COM 
17107978SPeter.Dunlap@Sun.COM 		/*
17117978SPeter.Dunlap@Sun.COM 		 * Release the task hold here (obtained in idm_task_find)
17127978SPeter.Dunlap@Sun.COM 		 * because the task may complete synchronously during
17137978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done.  Since we still have an active
17147978SPeter.Dunlap@Sun.COM 		 * buffer we know there is at least one additional hold on idt.
17157978SPeter.Dunlap@Sun.COM 		 */
17167978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
17177978SPeter.Dunlap@Sun.COM 
17187978SPeter.Dunlap@Sun.COM 		/*
17197978SPeter.Dunlap@Sun.COM 		 * idm_buf_rx_from_ini_done releases idt->idt_mutex
17207978SPeter.Dunlap@Sun.COM 		 */
17219721SPriya.Krishnan@Sun.COM 		DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
17229721SPriya.Krishnan@Sun.COM 		    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
17239721SPriya.Krishnan@Sun.COM 		    uint64_t, 0, uint32_t, 0, uint32_t, 0,
17249721SPriya.Krishnan@Sun.COM 		    uint32_t, idb->idb_xfer_len,
17259721SPriya.Krishnan@Sun.COM 		    int, XFER_BUF_RX_FROM_INI);
17267978SPeter.Dunlap@Sun.COM 		idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS);
17277978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
17287978SPeter.Dunlap@Sun.COM 		return;
17297978SPeter.Dunlap@Sun.COM 	}
17307978SPeter.Dunlap@Sun.COM 
17317978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
17327978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
17337978SPeter.Dunlap@Sun.COM }
17347978SPeter.Dunlap@Sun.COM 
17357978SPeter.Dunlap@Sun.COM /*
17367978SPeter.Dunlap@Sun.COM  * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle
17377978SPeter.Dunlap@Sun.COM  * the R2T PDU sent by the iSCSI target indicating that it is ready to
17387978SPeter.Dunlap@Sun.COM  * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS
17397978SPeter.Dunlap@Sun.COM  * and looks up the task in the task tree using the itt to get the output
17407978SPeter.Dunlap@Sun.COM  * buffers associated the task. The R2T PDU contains the offset of the
17417978SPeter.Dunlap@Sun.COM  * requested data and the data length. This function then constructs a
17427978SPeter.Dunlap@Sun.COM  * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out
17437978SPeter.Dunlap@Sun.COM  * PDU is associated with the R2T by the Target Transfer Tag  (ttt).
17447978SPeter.Dunlap@Sun.COM  */
17459162SPeter.Dunlap@Sun.COM 
17467978SPeter.Dunlap@Sun.COM static void
idm_so_rx_rtt(idm_conn_t * ic,idm_pdu_t * pdu)17477978SPeter.Dunlap@Sun.COM idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu)
17487978SPeter.Dunlap@Sun.COM {
17497978SPeter.Dunlap@Sun.COM 	idm_task_t		*idt;
17507978SPeter.Dunlap@Sun.COM 	idm_buf_t		*idb;
17517978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt_hdr;
17527978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset;
17539162SPeter.Dunlap@Sun.COM 	uint32_t		data_length;
17547978SPeter.Dunlap@Sun.COM 
17557978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
17567978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
17577978SPeter.Dunlap@Sun.COM 
17587978SPeter.Dunlap@Sun.COM 	rtt_hdr	= (iscsi_rtt_hdr_t *)pdu->isp_hdr;
17597978SPeter.Dunlap@Sun.COM 	data_offset = ntohl(rtt_hdr->data_offset);
17609162SPeter.Dunlap@Sun.COM 	data_length = ntohl(rtt_hdr->data_length);
17617978SPeter.Dunlap@Sun.COM 	idt	= idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt);
17627978SPeter.Dunlap@Sun.COM 
17637978SPeter.Dunlap@Sun.COM 	if (idt == NULL) {
17647978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task");
17657978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
17667978SPeter.Dunlap@Sun.COM 		return;
17677978SPeter.Dunlap@Sun.COM 	}
17687978SPeter.Dunlap@Sun.COM 
17697978SPeter.Dunlap@Sun.COM 	/* Find the buffer bound to the task by the iSCSI initiator */
17707978SPeter.Dunlap@Sun.COM 	mutex_enter(&idt->idt_mutex);
17717978SPeter.Dunlap@Sun.COM 	idb = idm_buf_find(&idt->idt_outbufv, data_offset);
17727978SPeter.Dunlap@Sun.COM 	if (idb == NULL) {
17737978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
17747978SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
17757978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer");
17767978SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
17777978SPeter.Dunlap@Sun.COM 		return;
17787978SPeter.Dunlap@Sun.COM 	}
17797978SPeter.Dunlap@Sun.COM 
17809162SPeter.Dunlap@Sun.COM 	/* return buffer contains this data */
17819162SPeter.Dunlap@Sun.COM 	if (data_offset + data_length > idb->idb_buflen) {
17829162SPeter.Dunlap@Sun.COM 		/* Overflow */
17839162SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
17849162SPeter.Dunlap@Sun.COM 		idm_task_rele(idt);
17859162SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside "
17869162SPeter.Dunlap@Sun.COM 		    "buffer");
17879162SPeter.Dunlap@Sun.COM 		idm_pdu_rx_protocol_error(ic, pdu);
17889162SPeter.Dunlap@Sun.COM 		return;
17899162SPeter.Dunlap@Sun.COM 	}
17909162SPeter.Dunlap@Sun.COM 
17919162SPeter.Dunlap@Sun.COM 	idt->idt_r2t_ttt = rtt_hdr->ttt;
17929162SPeter.Dunlap@Sun.COM 	idt->idt_exp_datasn = 0;
17939162SPeter.Dunlap@Sun.COM 
17949162SPeter.Dunlap@Sun.COM 	idm_so_send_rtt_data(ic, idt, idb, data_offset,
17959162SPeter.Dunlap@Sun.COM 	    ntohl(rtt_hdr->data_length));
1796*11857SJack.Meng@Sun.COM 	/*
1797*11857SJack.Meng@Sun.COM 	 * the idt_mutex is released in idm_so_send_rtt_data
1798*11857SJack.Meng@Sun.COM 	 */
17997978SPeter.Dunlap@Sun.COM 
18007978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
18017978SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
18027978SPeter.Dunlap@Sun.COM 
18037978SPeter.Dunlap@Sun.COM }
18047978SPeter.Dunlap@Sun.COM 
18057978SPeter.Dunlap@Sun.COM idm_status_t
idm_sorecvdata(idm_conn_t * ic,idm_pdu_t * pdu)18067978SPeter.Dunlap@Sun.COM idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu)
18077978SPeter.Dunlap@Sun.COM {
18087978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
18097978SPeter.Dunlap@Sun.COM 	int		pad_len;
18107978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc;
18117978SPeter.Dunlap@Sun.COM 	uint32_t	crc_calculated;
18127978SPeter.Dunlap@Sun.COM 	int		total_len;
18137978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
18147978SPeter.Dunlap@Sun.COM 
18157978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
18167978SPeter.Dunlap@Sun.COM 
18177978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
18187978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
18197978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
18207978SPeter.Dunlap@Sun.COM 
18217978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */
18227978SPeter.Dunlap@Sun.COM 
18237978SPeter.Dunlap@Sun.COM 	total_len = pdu->isp_datalen;
18247978SPeter.Dunlap@Sun.COM 
18257978SPeter.Dunlap@Sun.COM 	if (pad_len) {
18267978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base	= (char *)&pad;
18277978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len	= pad_len;
18287978SPeter.Dunlap@Sun.COM 		total_len		+= pad_len;
18297978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
18307978SPeter.Dunlap@Sun.COM 	}
18317978SPeter.Dunlap@Sun.COM 
18327978SPeter.Dunlap@Sun.COM 	/* setup data digest */
18337978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
18347978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_base =
18357978SPeter.Dunlap@Sun.COM 		    (char *)&data_digest_crc;
18367978SPeter.Dunlap@Sun.COM 		pdu->isp_iov[pdu->isp_iovlen].iov_len =
18377978SPeter.Dunlap@Sun.COM 		    sizeof (data_digest_crc);
18387978SPeter.Dunlap@Sun.COM 		total_len		+= sizeof (data_digest_crc);
18397978SPeter.Dunlap@Sun.COM 		pdu->isp_iovlen++;
18407978SPeter.Dunlap@Sun.COM 	}
18417978SPeter.Dunlap@Sun.COM 
18429162SPeter.Dunlap@Sun.COM 	pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base;
18439162SPeter.Dunlap@Sun.COM 
18447978SPeter.Dunlap@Sun.COM 	if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0],
18457978SPeter.Dunlap@Sun.COM 	    pdu->isp_iovlen, total_len) != 0) {
18467978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_IO);
18477978SPeter.Dunlap@Sun.COM 	}
18487978SPeter.Dunlap@Sun.COM 
18497978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) {
18507978SPeter.Dunlap@Sun.COM 		crc_calculated = idm_crc32c(pdu->isp_data,
18517978SPeter.Dunlap@Sun.COM 		    pdu->isp_datalen);
18527978SPeter.Dunlap@Sun.COM 		if (pad_len) {
18537978SPeter.Dunlap@Sun.COM 			crc_calculated = idm_crc32c_continued((char *)&pad,
18547978SPeter.Dunlap@Sun.COM 			    pad_len, crc_calculated);
18557978SPeter.Dunlap@Sun.COM 		}
18567978SPeter.Dunlap@Sun.COM 		if (crc_calculated != data_digest_crc) {
18577978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
18587978SPeter.Dunlap@Sun.COM 			    "idm_sorecvdata: "
18597978SPeter.Dunlap@Sun.COM 			    "CRC error: actual 0x%x, calc 0x%x",
18607978SPeter.Dunlap@Sun.COM 			    data_digest_crc, crc_calculated);
18617978SPeter.Dunlap@Sun.COM 
18627978SPeter.Dunlap@Sun.COM 			/* Invalid Data Digest */
18637978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_DATA_DIGEST);
18647978SPeter.Dunlap@Sun.COM 		}
18657978SPeter.Dunlap@Sun.COM 	}
18667978SPeter.Dunlap@Sun.COM 
18677978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
18687978SPeter.Dunlap@Sun.COM }
18697978SPeter.Dunlap@Sun.COM 
18707978SPeter.Dunlap@Sun.COM /*
18717978SPeter.Dunlap@Sun.COM  * idm_sorecv_scsidata() is used to receive scsi data from the socket. The
18727978SPeter.Dunlap@Sun.COM  * Data-type PDU header must be read into the idm_pdu_t structure prior to
18737978SPeter.Dunlap@Sun.COM  * calling this function.
18747978SPeter.Dunlap@Sun.COM  */
18757978SPeter.Dunlap@Sun.COM idm_status_t
idm_sorecv_scsidata(idm_conn_t * ic,idm_pdu_t * pdu)18767978SPeter.Dunlap@Sun.COM idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu)
18777978SPeter.Dunlap@Sun.COM {
18787978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
18797978SPeter.Dunlap@Sun.COM 	idm_task_t		*task;
18807978SPeter.Dunlap@Sun.COM 	uint32_t		offset;
18817978SPeter.Dunlap@Sun.COM 	uint8_t			opcode;
18827978SPeter.Dunlap@Sun.COM 	uint32_t		dlength;
18837978SPeter.Dunlap@Sun.COM 	list_t			*buflst;
18847978SPeter.Dunlap@Sun.COM 	uint32_t		xfer_bytes;
18857978SPeter.Dunlap@Sun.COM 	idm_status_t		status;
18867978SPeter.Dunlap@Sun.COM 
18877978SPeter.Dunlap@Sun.COM 	ASSERT(ic != NULL);
18887978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
18897978SPeter.Dunlap@Sun.COM 
18907978SPeter.Dunlap@Sun.COM 	bhs	= (iscsi_data_hdr_t *)pdu->isp_hdr;
18917978SPeter.Dunlap@Sun.COM 
18927978SPeter.Dunlap@Sun.COM 	offset	= ntohl(bhs->offset);
18937978SPeter.Dunlap@Sun.COM 	opcode	= bhs->opcode;
18947978SPeter.Dunlap@Sun.COM 	dlength = n2h24(bhs->dlength);
18957978SPeter.Dunlap@Sun.COM 
18967978SPeter.Dunlap@Sun.COM 	ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) ||
18977978SPeter.Dunlap@Sun.COM 	    (opcode == ISCSI_OP_SCSI_DATA));
18987978SPeter.Dunlap@Sun.COM 
18997978SPeter.Dunlap@Sun.COM 	/*
19007978SPeter.Dunlap@Sun.COM 	 * Successful lookup implicitly gets a "hold" on the task.  This
19017978SPeter.Dunlap@Sun.COM 	 * hold must be released before leaving this function.  At one
19027978SPeter.Dunlap@Sun.COM 	 * point we were caching this task context and retaining the hold
19037978SPeter.Dunlap@Sun.COM 	 * but it turned out to be very difficult to release the hold properly.
19047978SPeter.Dunlap@Sun.COM 	 * The task can be aborted and the connection shutdown between this
19057978SPeter.Dunlap@Sun.COM 	 * call and the subsequent expected call to idm_so_rx_datain/
19067978SPeter.Dunlap@Sun.COM 	 * idm_so_rx_dataout (in which case those functions are not called).
19077978SPeter.Dunlap@Sun.COM 	 * Releasing the hold in the PDU callback doesn't work well either
19087978SPeter.Dunlap@Sun.COM 	 * because the whole task may be completed by then at which point
19097978SPeter.Dunlap@Sun.COM 	 * it is too late to release the hold -- for better or worse this
19107978SPeter.Dunlap@Sun.COM 	 * code doesn't wait on the refcnts during normal operation.
19117978SPeter.Dunlap@Sun.COM 	 * idm_task_find() is very fast and it is not a huge burden if we
19127978SPeter.Dunlap@Sun.COM 	 * have to do it twice.
19137978SPeter.Dunlap@Sun.COM 	 */
19147978SPeter.Dunlap@Sun.COM 	task = idm_task_find(ic, bhs->itt, bhs->ttt);
19157978SPeter.Dunlap@Sun.COM 	if (task == NULL) {
19167978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
19177978SPeter.Dunlap@Sun.COM 		    "idm_sorecv_scsidata: could not find task");
19187978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
19197978SPeter.Dunlap@Sun.COM 	}
19207978SPeter.Dunlap@Sun.COM 
19217978SPeter.Dunlap@Sun.COM 	mutex_enter(&task->idt_mutex);
19227978SPeter.Dunlap@Sun.COM 	buflst	= (opcode == ISCSI_OP_SCSI_DATA_RSP) ?
19237978SPeter.Dunlap@Sun.COM 	    &task->idt_inbufv : &task->idt_outbufv;
19247978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = idm_buf_find(buflst, offset);
19257978SPeter.Dunlap@Sun.COM 	mutex_exit(&task->idt_mutex);
19267978SPeter.Dunlap@Sun.COM 
19277978SPeter.Dunlap@Sun.COM 	if (pdu->isp_sorx_buf == NULL) {
19287978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
19297978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find "
19307978SPeter.Dunlap@Sun.COM 		    "buffer for offset %x opcode=%x",
19317978SPeter.Dunlap@Sun.COM 		    offset, opcode);
19327978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
19337978SPeter.Dunlap@Sun.COM 	}
19347978SPeter.Dunlap@Sun.COM 
19357978SPeter.Dunlap@Sun.COM 	xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength);
19367978SPeter.Dunlap@Sun.COM 	ASSERT(xfer_bytes != 0);
19377978SPeter.Dunlap@Sun.COM 	if (xfer_bytes != dlength) {
19387978SPeter.Dunlap@Sun.COM 		idm_task_rele(task);
19397978SPeter.Dunlap@Sun.COM 		/*
19407978SPeter.Dunlap@Sun.COM 		 * Buffer overflow, connection error.  The PDU data is still
19417978SPeter.Dunlap@Sun.COM 		 * sitting in the socket so we can't use the connection
19427978SPeter.Dunlap@Sun.COM 		 * again until that data is drained.
19437978SPeter.Dunlap@Sun.COM 		 */
19447978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
19457978SPeter.Dunlap@Sun.COM 	}
19467978SPeter.Dunlap@Sun.COM 
19477978SPeter.Dunlap@Sun.COM 	status = idm_sorecvdata(ic, pdu);
19487978SPeter.Dunlap@Sun.COM 
19497978SPeter.Dunlap@Sun.COM 	idm_task_rele(task);
19507978SPeter.Dunlap@Sun.COM 
19517978SPeter.Dunlap@Sun.COM 	return (status);
19527978SPeter.Dunlap@Sun.COM }
19537978SPeter.Dunlap@Sun.COM 
19547978SPeter.Dunlap@Sun.COM static uint32_t
idm_fill_iov(idm_pdu_t * pdu,idm_buf_t * idb,uint32_t ro,uint32_t dlength)19557978SPeter.Dunlap@Sun.COM idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength)
19567978SPeter.Dunlap@Sun.COM {
19577978SPeter.Dunlap@Sun.COM 	uint32_t	buf_ro = ro - idb->idb_bufoffset;
19587978SPeter.Dunlap@Sun.COM 	uint32_t	xfer_len = min(dlength, idb->idb_buflen - buf_ro);
19597978SPeter.Dunlap@Sun.COM 
19607978SPeter.Dunlap@Sun.COM 	ASSERT(ro >= idb->idb_bufoffset);
19617978SPeter.Dunlap@Sun.COM 
19627978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_base	=
19637978SPeter.Dunlap@Sun.COM 	    (caddr_t)idb->idb_buf + buf_ro;
19647978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[pdu->isp_iovlen].iov_len	= xfer_len;
19657978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen++;
19667978SPeter.Dunlap@Sun.COM 
19677978SPeter.Dunlap@Sun.COM 	return (xfer_len);
19687978SPeter.Dunlap@Sun.COM }
19697978SPeter.Dunlap@Sun.COM 
19707978SPeter.Dunlap@Sun.COM int
idm_sorecv_nonscsidata(idm_conn_t * ic,idm_pdu_t * pdu)19717978SPeter.Dunlap@Sun.COM idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu)
19727978SPeter.Dunlap@Sun.COM {
19737978SPeter.Dunlap@Sun.COM 	pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP);
19747978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_data != NULL);
19757978SPeter.Dunlap@Sun.COM 
19767978SPeter.Dunlap@Sun.COM 	pdu->isp_databuflen = pdu->isp_datalen;
19777978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data;
19787978SPeter.Dunlap@Sun.COM 	pdu->isp_iov[0].iov_len = pdu->isp_datalen;
19797978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 1;
19807978SPeter.Dunlap@Sun.COM 	/*
19817978SPeter.Dunlap@Sun.COM 	 * Since we are associating a new data buffer with this received
19827978SPeter.Dunlap@Sun.COM 	 * PDU we need to set a specific callback to free the data
19837978SPeter.Dunlap@Sun.COM 	 * after the PDU is processed.
19847978SPeter.Dunlap@Sun.COM 	 */
19857978SPeter.Dunlap@Sun.COM 	pdu->isp_flags |= IDM_PDU_ADDL_DATA;
19867978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_addl_pdu_cb;
19877978SPeter.Dunlap@Sun.COM 
19887978SPeter.Dunlap@Sun.COM 	return (idm_sorecvdata(ic, pdu));
19897978SPeter.Dunlap@Sun.COM }
19907978SPeter.Dunlap@Sun.COM 
19917978SPeter.Dunlap@Sun.COM void
idm_sorx_thread(void * arg)19927978SPeter.Dunlap@Sun.COM idm_sorx_thread(void *arg)
19937978SPeter.Dunlap@Sun.COM {
19947978SPeter.Dunlap@Sun.COM 	boolean_t	conn_failure = B_FALSE;
19957978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = (idm_conn_t *)arg;
19967978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
19977978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu;
19987978SPeter.Dunlap@Sun.COM 	idm_status_t	rc;
19997978SPeter.Dunlap@Sun.COM 
20007978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
20017978SPeter.Dunlap@Sun.COM 
20027978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
20037978SPeter.Dunlap@Sun.COM 
20047978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
20057978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_running = B_TRUE;
20067978SPeter.Dunlap@Sun.COM 	so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did;
20077978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
20087978SPeter.Dunlap@Sun.COM 
20097978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_rx_thread_running) {
20107978SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_mutex);
20117978SPeter.Dunlap@Sun.COM 
20127978SPeter.Dunlap@Sun.COM 		/*
20137978SPeter.Dunlap@Sun.COM 		 * Get PDU with default header size (large enough for
20147978SPeter.Dunlap@Sun.COM 		 * BHS plus any anticipated AHS).  PDU from
20157978SPeter.Dunlap@Sun.COM 		 * the cache will have all values set correctly
20167978SPeter.Dunlap@Sun.COM 		 * for sockets RX including callback.
20177978SPeter.Dunlap@Sun.COM 		 */
20187978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP);
20197978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
20207978SPeter.Dunlap@Sun.COM 		pdu->isp_flags = 0;
20217978SPeter.Dunlap@Sun.COM 		pdu->isp_transport_hdrlen = 0;
20227978SPeter.Dunlap@Sun.COM 
20237978SPeter.Dunlap@Sun.COM 		if ((rc = idm_sorecvhdr(ic, pdu)) != 0) {
20247978SPeter.Dunlap@Sun.COM 			/*
20257978SPeter.Dunlap@Sun.COM 			 * Call idm_pdu_complete so that we call the callback
20267978SPeter.Dunlap@Sun.COM 			 * and ensure any memory allocated in idm_sorecvhdr
20277978SPeter.Dunlap@Sun.COM 			 * gets freed up.
20287978SPeter.Dunlap@Sun.COM 			 */
20297978SPeter.Dunlap@Sun.COM 			idm_pdu_complete(pdu, IDM_STATUS_FAIL);
20307978SPeter.Dunlap@Sun.COM 
20317978SPeter.Dunlap@Sun.COM 			/*
20327978SPeter.Dunlap@Sun.COM 			 * If ic_rx_thread_running is still set then
20337978SPeter.Dunlap@Sun.COM 			 * this is some kind of connection problem
20347978SPeter.Dunlap@Sun.COM 			 * on the socket.  In this case we want to
20357978SPeter.Dunlap@Sun.COM 			 * generate an event.  Otherwise some other
20367978SPeter.Dunlap@Sun.COM 			 * thread closed the socket due to another
20377978SPeter.Dunlap@Sun.COM 			 * issue in which case we don't need to
20387978SPeter.Dunlap@Sun.COM 			 * generate an event.
20397978SPeter.Dunlap@Sun.COM 			 */
20407978SPeter.Dunlap@Sun.COM 			mutex_enter(&ic->ic_mutex);
20417978SPeter.Dunlap@Sun.COM 			if (so_conn->ic_rx_thread_running) {
20427978SPeter.Dunlap@Sun.COM 				conn_failure = B_TRUE;
20437978SPeter.Dunlap@Sun.COM 				so_conn->ic_rx_thread_running = B_FALSE;
20447978SPeter.Dunlap@Sun.COM 			}
20457978SPeter.Dunlap@Sun.COM 
20467978SPeter.Dunlap@Sun.COM 			continue;
20477978SPeter.Dunlap@Sun.COM 		}
20487978SPeter.Dunlap@Sun.COM 
20497978SPeter.Dunlap@Sun.COM 		/*
20507978SPeter.Dunlap@Sun.COM 		 * Header has been read and validated.  Now we need
20517978SPeter.Dunlap@Sun.COM 		 * to read the PDU data payload (if present).  SCSI data
20527978SPeter.Dunlap@Sun.COM 		 * need to be transferred from the socket directly into
20537978SPeter.Dunlap@Sun.COM 		 * the associated transfer buffer for the SCSI task.
20547978SPeter.Dunlap@Sun.COM 		 */
20557978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen != 0) {
20567978SPeter.Dunlap@Sun.COM 			if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) ||
20577978SPeter.Dunlap@Sun.COM 			    (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) {
20587978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_scsidata(ic, pdu);
20597978SPeter.Dunlap@Sun.COM 				/*
20607978SPeter.Dunlap@Sun.COM 				 * All SCSI errors are fatal to the
20617978SPeter.Dunlap@Sun.COM 				 * connection right now since we have no
20627978SPeter.Dunlap@Sun.COM 				 * place to put the data.  What we need
20637978SPeter.Dunlap@Sun.COM 				 * is some kind of sink to dispose of unwanted
20647978SPeter.Dunlap@Sun.COM 				 * SCSI data.  For example an invalid task tag
20657978SPeter.Dunlap@Sun.COM 				 * should not kill the connection (although
20667978SPeter.Dunlap@Sun.COM 				 * we may want to drop the connection).
20677978SPeter.Dunlap@Sun.COM 				 */
20687978SPeter.Dunlap@Sun.COM 			} else {
20697978SPeter.Dunlap@Sun.COM 				/*
20707978SPeter.Dunlap@Sun.COM 				 * Not data PDUs so allocate a buffer for the
20717978SPeter.Dunlap@Sun.COM 				 * data segment and read the remaining data.
20727978SPeter.Dunlap@Sun.COM 				 */
20737978SPeter.Dunlap@Sun.COM 				rc = idm_sorecv_nonscsidata(ic, pdu);
20747978SPeter.Dunlap@Sun.COM 			}
20757978SPeter.Dunlap@Sun.COM 			if (rc != 0) {
20767978SPeter.Dunlap@Sun.COM 				/*
20777978SPeter.Dunlap@Sun.COM 				 * Call idm_pdu_complete so that we call the
20787978SPeter.Dunlap@Sun.COM 				 * callback and ensure any memory allocated
20797978SPeter.Dunlap@Sun.COM 				 * in idm_sorecvhdr gets freed up.
20807978SPeter.Dunlap@Sun.COM 				 */
20817978SPeter.Dunlap@Sun.COM 				idm_pdu_complete(pdu, IDM_STATUS_FAIL);
20827978SPeter.Dunlap@Sun.COM 
20837978SPeter.Dunlap@Sun.COM 				/*
20847978SPeter.Dunlap@Sun.COM 				 * If ic_rx_thread_running is still set then
20857978SPeter.Dunlap@Sun.COM 				 * this is some kind of connection problem
20867978SPeter.Dunlap@Sun.COM 				 * on the socket.  In this case we want to
20877978SPeter.Dunlap@Sun.COM 				 * generate an event.  Otherwise some other
20887978SPeter.Dunlap@Sun.COM 				 * thread closed the socket due to another
20897978SPeter.Dunlap@Sun.COM 				 * issue in which case we don't need to
20907978SPeter.Dunlap@Sun.COM 				 * generate an event.
20917978SPeter.Dunlap@Sun.COM 				 */
20927978SPeter.Dunlap@Sun.COM 				mutex_enter(&ic->ic_mutex);
20937978SPeter.Dunlap@Sun.COM 				if (so_conn->ic_rx_thread_running) {
20947978SPeter.Dunlap@Sun.COM 					conn_failure = B_TRUE;
20957978SPeter.Dunlap@Sun.COM 					so_conn->ic_rx_thread_running = B_FALSE;
20967978SPeter.Dunlap@Sun.COM 				}
20977978SPeter.Dunlap@Sun.COM 				continue;
20987978SPeter.Dunlap@Sun.COM 			}
20997978SPeter.Dunlap@Sun.COM 		}
21007978SPeter.Dunlap@Sun.COM 
21017978SPeter.Dunlap@Sun.COM 		/*
21027978SPeter.Dunlap@Sun.COM 		 * Process RX PDU
21037978SPeter.Dunlap@Sun.COM 		 */
21047978SPeter.Dunlap@Sun.COM 		idm_pdu_rx(ic, pdu);
21057978SPeter.Dunlap@Sun.COM 
21067978SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_mutex);
21077978SPeter.Dunlap@Sun.COM 	}
21087978SPeter.Dunlap@Sun.COM 
21097978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
21107978SPeter.Dunlap@Sun.COM 
21117978SPeter.Dunlap@Sun.COM 	/*
21127978SPeter.Dunlap@Sun.COM 	 * If we dropped out of the RX processing loop because of
21137978SPeter.Dunlap@Sun.COM 	 * a socket problem or other connection failure (including
21147978SPeter.Dunlap@Sun.COM 	 * digest errors) then we need to generate a state machine
21157978SPeter.Dunlap@Sun.COM 	 * event to shut the connection down.
21167978SPeter.Dunlap@Sun.COM 	 * If the state machine is already in, for example, INIT_ERROR, this
21177978SPeter.Dunlap@Sun.COM 	 * event will get dropped, and the TX thread will never be notified
21187978SPeter.Dunlap@Sun.COM 	 * to shut down.  To be safe, we'll just notify it here.
21197978SPeter.Dunlap@Sun.COM 	 */
21207978SPeter.Dunlap@Sun.COM 	if (conn_failure) {
21217978SPeter.Dunlap@Sun.COM 		if (so_conn->ic_tx_thread_running) {
21227978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
21237978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
21247978SPeter.Dunlap@Sun.COM 			cv_signal(&so_conn->ic_tx_cv);
21257978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
21267978SPeter.Dunlap@Sun.COM 		}
21277978SPeter.Dunlap@Sun.COM 
21287978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_TRANSPORT_FAIL, rc);
21297978SPeter.Dunlap@Sun.COM 	}
21307978SPeter.Dunlap@Sun.COM 
21317978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
21327978SPeter.Dunlap@Sun.COM 
21337978SPeter.Dunlap@Sun.COM 	thread_exit();
21347978SPeter.Dunlap@Sun.COM }
21357978SPeter.Dunlap@Sun.COM 
21367978SPeter.Dunlap@Sun.COM /*
21377978SPeter.Dunlap@Sun.COM  * idm_so_tx
21387978SPeter.Dunlap@Sun.COM  *
21397978SPeter.Dunlap@Sun.COM  * This is the implementation of idm_transport_ops_t's it_tx_pdu entry
21407978SPeter.Dunlap@Sun.COM  * point.  By definition, it is supposed to be fast.  So, simply queue
21417978SPeter.Dunlap@Sun.COM  * the entry and return.  The real work is done by idm_i_so_tx() via
21427978SPeter.Dunlap@Sun.COM  * idm_sotx_thread().
21437978SPeter.Dunlap@Sun.COM  */
21447978SPeter.Dunlap@Sun.COM 
21457978SPeter.Dunlap@Sun.COM static void
idm_so_tx(idm_conn_t * ic,idm_pdu_t * pdu)21467978SPeter.Dunlap@Sun.COM idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu)
21477978SPeter.Dunlap@Sun.COM {
21487978SPeter.Dunlap@Sun.COM 	idm_so_conn_t *so_conn = ic->ic_transport_private;
21497978SPeter.Dunlap@Sun.COM 
21507978SPeter.Dunlap@Sun.COM 	ASSERT(pdu->isp_ic == ic);
21517978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
21527978SPeter.Dunlap@Sun.COM 
21537978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
21547978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
21557978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_ABORTED);
21567978SPeter.Dunlap@Sun.COM 		return;
21577978SPeter.Dunlap@Sun.COM 	}
21587978SPeter.Dunlap@Sun.COM 
21597978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)pdu);
21607978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
21617978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
21627978SPeter.Dunlap@Sun.COM }
21637978SPeter.Dunlap@Sun.COM 
21647978SPeter.Dunlap@Sun.COM static idm_status_t
idm_i_so_tx(idm_pdu_t * pdu)21657978SPeter.Dunlap@Sun.COM idm_i_so_tx(idm_pdu_t *pdu)
21667978SPeter.Dunlap@Sun.COM {
21677978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = pdu->isp_ic;
21687978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
21697978SPeter.Dunlap@Sun.COM 	uint8_t		pad[ISCSI_PAD_WORD_LEN];
21707978SPeter.Dunlap@Sun.COM 	int		pad_len;
21717978SPeter.Dunlap@Sun.COM 	uint32_t	hdr_digest_crc;
21727978SPeter.Dunlap@Sun.COM 	uint32_t	data_digest_crc = 0;
21737978SPeter.Dunlap@Sun.COM 	int		total_len = 0;
21747978SPeter.Dunlap@Sun.COM 	int		iovlen = 0;
21757978SPeter.Dunlap@Sun.COM 	struct iovec	iov[6];
21767978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
21777978SPeter.Dunlap@Sun.COM 
21787978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
21797978SPeter.Dunlap@Sun.COM 
21807978SPeter.Dunlap@Sun.COM 	/* Setup BHS */
21817978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_base	= (caddr_t)pdu->isp_hdr;
21827978SPeter.Dunlap@Sun.COM 	iov[iovlen].iov_len	= pdu->isp_hdrlen;
21837978SPeter.Dunlap@Sun.COM 	total_len		+= iov[iovlen].iov_len;
21847978SPeter.Dunlap@Sun.COM 	iovlen++;
21857978SPeter.Dunlap@Sun.COM 
21867978SPeter.Dunlap@Sun.COM 	/* Setup header digest */
21877978SPeter.Dunlap@Sun.COM 	if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
21887978SPeter.Dunlap@Sun.COM 	    (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) {
21897978SPeter.Dunlap@Sun.COM 		hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen);
21907978SPeter.Dunlap@Sun.COM 
21917978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&hdr_digest_crc;
21927978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (hdr_digest_crc);
21937978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
21947978SPeter.Dunlap@Sun.COM 		iovlen++;
21957978SPeter.Dunlap@Sun.COM 	}
21967978SPeter.Dunlap@Sun.COM 
21977978SPeter.Dunlap@Sun.COM 	/* Setup the data */
21987978SPeter.Dunlap@Sun.COM 	if (pdu->isp_datalen) {
21997978SPeter.Dunlap@Sun.COM 		idm_task_t		*idt;
22007978SPeter.Dunlap@Sun.COM 		idm_buf_t		*idb;
22017978SPeter.Dunlap@Sun.COM 		iscsi_data_hdr_t	*ihp;
22027978SPeter.Dunlap@Sun.COM 		ihp = (iscsi_data_hdr_t *)pdu->isp_hdr;
22037978SPeter.Dunlap@Sun.COM 		/* Write of immediate data */
22047978SPeter.Dunlap@Sun.COM 		if (ic->ic_ffp &&
22057978SPeter.Dunlap@Sun.COM 		    (ihp->opcode == ISCSI_OP_SCSI_CMD ||
22067978SPeter.Dunlap@Sun.COM 		    ihp->opcode == ISCSI_OP_SCSI_DATA)) {
22077978SPeter.Dunlap@Sun.COM 			idt = idm_task_find(ic, ihp->itt, ihp->ttt);
22087978SPeter.Dunlap@Sun.COM 			if (idt) {
22097978SPeter.Dunlap@Sun.COM 				mutex_enter(&idt->idt_mutex);
22107978SPeter.Dunlap@Sun.COM 				idb = idm_buf_find(&idt->idt_outbufv, 0);
22117978SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
22129162SPeter.Dunlap@Sun.COM 				/*
22139162SPeter.Dunlap@Sun.COM 				 * If the initiator call to idm_buf_alloc
22149162SPeter.Dunlap@Sun.COM 				 * failed then we can get to this point
22159162SPeter.Dunlap@Sun.COM 				 * without a bound buffer.  The associated
22169162SPeter.Dunlap@Sun.COM 				 * connection failure will clean things up
22179162SPeter.Dunlap@Sun.COM 				 * later.  It would be nice to come up with
22189162SPeter.Dunlap@Sun.COM 				 * a cleaner way to handle this.  In
22199162SPeter.Dunlap@Sun.COM 				 * particular it seems absurd to look up
22209162SPeter.Dunlap@Sun.COM 				 * the task and the buffer just to update
22219162SPeter.Dunlap@Sun.COM 				 * this counter.
22229162SPeter.Dunlap@Sun.COM 				 */
22239162SPeter.Dunlap@Sun.COM 				if (idb)
22249162SPeter.Dunlap@Sun.COM 					idb->idb_xfer_len += pdu->isp_datalen;
22259162SPeter.Dunlap@Sun.COM 				idm_task_rele(idt);
22267978SPeter.Dunlap@Sun.COM 			}
22277978SPeter.Dunlap@Sun.COM 		}
22287978SPeter.Dunlap@Sun.COM 
22297978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (caddr_t)pdu->isp_data;
22307978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pdu->isp_datalen;
22317978SPeter.Dunlap@Sun.COM 		total_len += iov[iovlen].iov_len;
22327978SPeter.Dunlap@Sun.COM 		iovlen++;
22337978SPeter.Dunlap@Sun.COM 	}
22347978SPeter.Dunlap@Sun.COM 
22357978SPeter.Dunlap@Sun.COM 	/* Setup the data pad if necessary */
22367978SPeter.Dunlap@Sun.COM 	pad_len = ((ISCSI_PAD_WORD_LEN -
22377978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) &
22387978SPeter.Dunlap@Sun.COM 	    (ISCSI_PAD_WORD_LEN - 1));
22397978SPeter.Dunlap@Sun.COM 
22407978SPeter.Dunlap@Sun.COM 	if (pad_len) {
22417978SPeter.Dunlap@Sun.COM 		bzero(pad, sizeof (pad));
22427978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base = (void *)&pad;
22437978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len  = pad_len;
22447978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
22457978SPeter.Dunlap@Sun.COM 		iovlen++;
22467978SPeter.Dunlap@Sun.COM 	}
22477978SPeter.Dunlap@Sun.COM 
22487978SPeter.Dunlap@Sun.COM 	/*
22497978SPeter.Dunlap@Sun.COM 	 * Setup the data digest if enabled.  Data-digest is not sent
22507978SPeter.Dunlap@Sun.COM 	 * for login-phase PDUs.
22517978SPeter.Dunlap@Sun.COM 	 */
22527978SPeter.Dunlap@Sun.COM 	if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) &&
22537978SPeter.Dunlap@Sun.COM 	    ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) &&
22547978SPeter.Dunlap@Sun.COM 	    (pdu->isp_datalen || pad_len)) {
22557978SPeter.Dunlap@Sun.COM 		/*
22567978SPeter.Dunlap@Sun.COM 		 * RFC3720/10.2.3: A zero-length Data Segment also
22577978SPeter.Dunlap@Sun.COM 		 * implies a zero-length data digest.
22587978SPeter.Dunlap@Sun.COM 		 */
22597978SPeter.Dunlap@Sun.COM 		if (pdu->isp_datalen) {
22607978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c(pdu->isp_data,
22617978SPeter.Dunlap@Sun.COM 			    pdu->isp_datalen);
22627978SPeter.Dunlap@Sun.COM 		}
22637978SPeter.Dunlap@Sun.COM 		if (pad_len) {
22647978SPeter.Dunlap@Sun.COM 			data_digest_crc = idm_crc32c_continued(&pad,
22657978SPeter.Dunlap@Sun.COM 			    pad_len, data_digest_crc);
22667978SPeter.Dunlap@Sun.COM 		}
22677978SPeter.Dunlap@Sun.COM 
22687978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_base	= (caddr_t)&data_digest_crc;
22697978SPeter.Dunlap@Sun.COM 		iov[iovlen].iov_len	= sizeof (data_digest_crc);
22707978SPeter.Dunlap@Sun.COM 		total_len		+= iov[iovlen].iov_len;
22717978SPeter.Dunlap@Sun.COM 		iovlen++;
22727978SPeter.Dunlap@Sun.COM 	}
22737978SPeter.Dunlap@Sun.COM 
22747978SPeter.Dunlap@Sun.COM 	/* Transmit the PDU */
22757978SPeter.Dunlap@Sun.COM 	if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen,
22767978SPeter.Dunlap@Sun.COM 	    total_len) != 0) {
22777978SPeter.Dunlap@Sun.COM 		/* Set error status */
22787978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_WARN,
22797978SPeter.Dunlap@Sun.COM 		    "idm_so_tx: failed to transmit the PDU, so: %p ic: %p "
22807978SPeter.Dunlap@Sun.COM 		    "data: %p", (void *) so_conn->ic_so, (void *) ic,
22817978SPeter.Dunlap@Sun.COM 		    (void *) pdu->isp_data);
22827978SPeter.Dunlap@Sun.COM 		status = IDM_STATUS_IO;
22837978SPeter.Dunlap@Sun.COM 	}
22847978SPeter.Dunlap@Sun.COM 
22857978SPeter.Dunlap@Sun.COM 	/*
22867978SPeter.Dunlap@Sun.COM 	 * Success does not mean that the PDU actually reached the
22877978SPeter.Dunlap@Sun.COM 	 * remote node since it could get dropped along the way.
22887978SPeter.Dunlap@Sun.COM 	 */
22897978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(pdu, status);
22907978SPeter.Dunlap@Sun.COM 
22917978SPeter.Dunlap@Sun.COM 	return (status);
22927978SPeter.Dunlap@Sun.COM }
22937978SPeter.Dunlap@Sun.COM 
22947978SPeter.Dunlap@Sun.COM /*
22957978SPeter.Dunlap@Sun.COM  * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the
22967978SPeter.Dunlap@Sun.COM  * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength,
22977978SPeter.Dunlap@Sun.COM  * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN.
22987978SPeter.Dunlap@Sun.COM  * A target can invoke this function multiple times for a single read command
22997978SPeter.Dunlap@Sun.COM  * (identified by the same ITT) to split the input into several sequences.
23007978SPeter.Dunlap@Sun.COM  *
23017978SPeter.Dunlap@Sun.COM  * DataSN starts with 0 for the first data PDU of an input command and advances
23027978SPeter.Dunlap@Sun.COM  * by 1 for each subsequent data PDU. Each sequence will have its own F bit,
23037978SPeter.Dunlap@Sun.COM  * which is set to 1 for the last data PDU of a sequence.
230411081SPriya.Krishnan@Sun.COM  * If the initiator supports phase collapse, the status bit must be set along
230511081SPriya.Krishnan@Sun.COM  * with the F bit to indicate that the status is shipped together with the last
230611081SPriya.Krishnan@Sun.COM  * Data-In PDU.
23077978SPeter.Dunlap@Sun.COM  *
23087978SPeter.Dunlap@Sun.COM  * The data PDUs within a sequence will be sent in order with the buffer offset
23097978SPeter.Dunlap@Sun.COM  * in increasing order. i.e. initiator and target must have negotiated the
23107978SPeter.Dunlap@Sun.COM  * "DataPDUInOrder" to "Yes". The order between sequences is not enforced.
23117978SPeter.Dunlap@Sun.COM  *
23127978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
23137978SPeter.Dunlap@Sun.COM  */
23147978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_buf_tx_to_ini(idm_task_t * idt,idm_buf_t * idb)23157978SPeter.Dunlap@Sun.COM idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb)
23167978SPeter.Dunlap@Sun.COM {
23177978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = idb->idb_ic->ic_transport_private;
23187978SPeter.Dunlap@Sun.COM 	idm_pdu_t	tmppdu;
23197978SPeter.Dunlap@Sun.COM 
23207978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
23217978SPeter.Dunlap@Sun.COM 
23227978SPeter.Dunlap@Sun.COM 	/*
23237978SPeter.Dunlap@Sun.COM 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
23247978SPeter.Dunlap@Sun.COM 	 * idm_sotx_thread.
23257978SPeter.Dunlap@Sun.COM 	 */
23267978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
23277978SPeter.Dunlap@Sun.COM 
23289721SPriya.Krishnan@Sun.COM 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
23299721SPriya.Krishnan@Sun.COM 	    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
23309721SPriya.Krishnan@Sun.COM 	    uint64_t, 0, uint32_t, 0, uint32_t, 0,
23319721SPriya.Krishnan@Sun.COM 	    uint32_t, idb->idb_xfer_len, int, XFER_BUF_TX_TO_INI);
23329721SPriya.Krishnan@Sun.COM 
23337978SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
23347978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
23357978SPeter.Dunlap@Sun.COM 		/*
23367978SPeter.Dunlap@Sun.COM 		 * Don't release idt->idt_mutex since we're supposed to hold
23377978SPeter.Dunlap@Sun.COM 		 * in when calling idm_buf_tx_to_ini_done
23387978SPeter.Dunlap@Sun.COM 		 */
23399721SPriya.Krishnan@Sun.COM 		DTRACE_ISCSI_8(xfer__done, idm_conn_t *, idt->idt_ic,
23409721SPriya.Krishnan@Sun.COM 		    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
23419721SPriya.Krishnan@Sun.COM 		    uint64_t, 0, uint32_t, 0, uint32_t, 0,
23429721SPriya.Krishnan@Sun.COM 		    uint32_t, idb->idb_xfer_len,
23439721SPriya.Krishnan@Sun.COM 		    int, XFER_BUF_TX_TO_INI);
23447978SPeter.Dunlap@Sun.COM 		idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED);
23457978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
23467978SPeter.Dunlap@Sun.COM 	}
23477978SPeter.Dunlap@Sun.COM 
23487978SPeter.Dunlap@Sun.COM 	/*
23497978SPeter.Dunlap@Sun.COM 	 * Build a template for the data PDU headers we will use so that
23507978SPeter.Dunlap@Sun.COM 	 * the SN values will stay consistent with other PDU's we are
23517978SPeter.Dunlap@Sun.COM 	 * transmitting like R2T and SCSI status.
23527978SPeter.Dunlap@Sun.COM 	 */
23537978SPeter.Dunlap@Sun.COM 	bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
23547978SPeter.Dunlap@Sun.COM 	tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl;
23557978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
23567978SPeter.Dunlap@Sun.COM 	    ISCSI_OP_SCSI_DATA_RSP);
23577978SPeter.Dunlap@Sun.COM 	idb->idb_tx_thread = B_TRUE;
23587978SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)idb);
23597978SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
23607978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
23617978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
23627978SPeter.Dunlap@Sun.COM 
23637978SPeter.Dunlap@Sun.COM 	/*
23647978SPeter.Dunlap@Sun.COM 	 * Returning success here indicates the transfer was successfully
23657978SPeter.Dunlap@Sun.COM 	 * dispatched -- it does not mean that the transfer completed
23667978SPeter.Dunlap@Sun.COM 	 * successfully.
23677978SPeter.Dunlap@Sun.COM 	 */
23687978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
23697978SPeter.Dunlap@Sun.COM }
23707978SPeter.Dunlap@Sun.COM 
23717978SPeter.Dunlap@Sun.COM /*
23727978SPeter.Dunlap@Sun.COM  * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the
23737978SPeter.Dunlap@Sun.COM  * data blocks it is ready to receive from the initiator in response to a WRITE
23747978SPeter.Dunlap@Sun.COM  * SCSI command. The target iSCSI layer passes the information about the desired
23757978SPeter.Dunlap@Sun.COM  * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer
23767978SPeter.Dunlap@Sun.COM  * offset and datalen are passed via the 'idb' argument.
23777978SPeter.Dunlap@Sun.COM  *
23787978SPeter.Dunlap@Sun.COM  * Scope for Prototype build:
23797978SPeter.Dunlap@Sun.COM  * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have
23807978SPeter.Dunlap@Sun.COM  * negotiated the "InitialR2T" to "Yes".
23817978SPeter.Dunlap@Sun.COM  *
23827978SPeter.Dunlap@Sun.COM  * Caller holds idt->idt_mutex
23837978SPeter.Dunlap@Sun.COM  */
23847978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_buf_rx_from_ini(idm_task_t * idt,idm_buf_t * idb)23857978SPeter.Dunlap@Sun.COM idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb)
23867978SPeter.Dunlap@Sun.COM {
23877978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
23887978SPeter.Dunlap@Sun.COM 	iscsi_rtt_hdr_t		*rtt;
23897978SPeter.Dunlap@Sun.COM 
23907978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
23917978SPeter.Dunlap@Sun.COM 
23929721SPriya.Krishnan@Sun.COM 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
23939721SPriya.Krishnan@Sun.COM 	    uintptr_t, idb->idb_buf, uint32_t, idb->idb_bufoffset,
23949721SPriya.Krishnan@Sun.COM 	    uint64_t, 0, uint32_t, 0, uint32_t, 0,
23959721SPriya.Krishnan@Sun.COM 	    uint32_t, idb->idb_xfer_len, int, XFER_BUF_RX_FROM_INI);
23969721SPriya.Krishnan@Sun.COM 
23977978SPeter.Dunlap@Sun.COM 	pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
23987978SPeter.Dunlap@Sun.COM 	pdu->isp_ic = idt->idt_ic;
239911611SPriya.Krishnan@Sun.COM 	pdu->isp_flags = IDM_PDU_SET_STATSN;
24007978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t));
24017978SPeter.Dunlap@Sun.COM 
240211611SPriya.Krishnan@Sun.COM 	/* iSCSI layer fills the TTT, ITT, ExpCmdSN, MaxCmdSN */
24037978SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP);
24047978SPeter.Dunlap@Sun.COM 
24057978SPeter.Dunlap@Sun.COM 	/* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */
24067978SPeter.Dunlap@Sun.COM 	rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr);
24077978SPeter.Dunlap@Sun.COM 
24087978SPeter.Dunlap@Sun.COM 	rtt->opcode		= ISCSI_OP_RTT_RSP;
24097978SPeter.Dunlap@Sun.COM 	rtt->flags		= ISCSI_FLAG_FINAL;
24107978SPeter.Dunlap@Sun.COM 	rtt->data_offset	= htonl(idb->idb_bufoffset);
24117978SPeter.Dunlap@Sun.COM 	rtt->data_length	= htonl(idb->idb_xfer_len);
24127978SPeter.Dunlap@Sun.COM 	rtt->rttsn		= htonl(idt->idt_exp_rttsn++);
24137978SPeter.Dunlap@Sun.COM 
24147978SPeter.Dunlap@Sun.COM 	/* Keep track of buffer offsets */
24157978SPeter.Dunlap@Sun.COM 	idb->idb_exp_offset	= idb->idb_bufoffset;
24167978SPeter.Dunlap@Sun.COM 	mutex_exit(&idt->idt_mutex);
24177978SPeter.Dunlap@Sun.COM 
24187978SPeter.Dunlap@Sun.COM 	/*
24198607SJames.Moore@Sun.COM 	 * Transmit the PDU.
24207978SPeter.Dunlap@Sun.COM 	 */
24218607SJames.Moore@Sun.COM 	idm_pdu_tx(pdu);
24227978SPeter.Dunlap@Sun.COM 
24237978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
24247978SPeter.Dunlap@Sun.COM }
24257978SPeter.Dunlap@Sun.COM 
24267978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_buf_alloc(idm_buf_t * idb,uint64_t buflen)24277978SPeter.Dunlap@Sun.COM idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen)
24287978SPeter.Dunlap@Sun.COM {
24299247SPeter.Dunlap@Sun.COM 	if ((buflen > IDM_SO_BUF_CACHE_LB) && (buflen <= IDM_SO_BUF_CACHE_UB)) {
24309247SPeter.Dunlap@Sun.COM 		idb->idb_buf = kmem_cache_alloc(idm.idm_so_128k_buf_cache,
24319247SPeter.Dunlap@Sun.COM 		    KM_NOSLEEP);
24329247SPeter.Dunlap@Sun.COM 		idb->idb_buf_private = idm.idm_so_128k_buf_cache;
24339247SPeter.Dunlap@Sun.COM 	} else {
24349247SPeter.Dunlap@Sun.COM 		idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP);
24359247SPeter.Dunlap@Sun.COM 		idb->idb_buf_private = NULL;
24369247SPeter.Dunlap@Sun.COM 	}
24379247SPeter.Dunlap@Sun.COM 
24387978SPeter.Dunlap@Sun.COM 	if (idb->idb_buf == NULL) {
24397978SPeter.Dunlap@Sun.COM 		IDM_CONN_LOG(CE_NOTE,
24407978SPeter.Dunlap@Sun.COM 		    "idm_so_buf_alloc: failed buffer allocation");
24417978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
24427978SPeter.Dunlap@Sun.COM 	}
24439247SPeter.Dunlap@Sun.COM 
24447978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
24457978SPeter.Dunlap@Sun.COM }
24467978SPeter.Dunlap@Sun.COM 
24477978SPeter.Dunlap@Sun.COM /* ARGSUSED */
24487978SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_buf_setup(idm_buf_t * idb)24497978SPeter.Dunlap@Sun.COM idm_so_buf_setup(idm_buf_t *idb)
24507978SPeter.Dunlap@Sun.COM {
24519162SPeter.Dunlap@Sun.COM 	/* Ensure bufalloc'd flag is unset */
24529162SPeter.Dunlap@Sun.COM 	idb->idb_bufalloc = B_FALSE;
24539162SPeter.Dunlap@Sun.COM 
24547978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
24557978SPeter.Dunlap@Sun.COM }
24567978SPeter.Dunlap@Sun.COM 
24577978SPeter.Dunlap@Sun.COM /* ARGSUSED */
24587978SPeter.Dunlap@Sun.COM static void
idm_so_buf_teardown(idm_buf_t * idb)24597978SPeter.Dunlap@Sun.COM idm_so_buf_teardown(idm_buf_t *idb)
24607978SPeter.Dunlap@Sun.COM {
24617978SPeter.Dunlap@Sun.COM 	/* nothing to do here */
24627978SPeter.Dunlap@Sun.COM }
24637978SPeter.Dunlap@Sun.COM 
24647978SPeter.Dunlap@Sun.COM static void
idm_so_buf_free(idm_buf_t * idb)24657978SPeter.Dunlap@Sun.COM idm_so_buf_free(idm_buf_t *idb)
24667978SPeter.Dunlap@Sun.COM {
24679247SPeter.Dunlap@Sun.COM 	if (idb->idb_buf_private == NULL) {
24689247SPeter.Dunlap@Sun.COM 		kmem_free(idb->idb_buf, idb->idb_buflen);
24699247SPeter.Dunlap@Sun.COM 	} else {
24709247SPeter.Dunlap@Sun.COM 		kmem_cache_free(idb->idb_buf_private, idb->idb_buf);
24719247SPeter.Dunlap@Sun.COM 	}
24727978SPeter.Dunlap@Sun.COM }
24737978SPeter.Dunlap@Sun.COM 
24749162SPeter.Dunlap@Sun.COM static void
idm_so_send_rtt_data(idm_conn_t * ic,idm_task_t * idt,idm_buf_t * idb,uint32_t offset,uint32_t length)24759162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb,
24769162SPeter.Dunlap@Sun.COM     uint32_t offset, uint32_t length)
24779162SPeter.Dunlap@Sun.COM {
24789162SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn = ic->ic_transport_private;
24799162SPeter.Dunlap@Sun.COM 	idm_pdu_t	tmppdu;
24809162SPeter.Dunlap@Sun.COM 	idm_buf_t	*rtt_buf;
24819162SPeter.Dunlap@Sun.COM 
24829162SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
24839162SPeter.Dunlap@Sun.COM 
24849162SPeter.Dunlap@Sun.COM 	/*
24859162SPeter.Dunlap@Sun.COM 	 * Allocate a buffer to represent the RTT transfer.  We could further
24869162SPeter.Dunlap@Sun.COM 	 * optimize this by allocating the buffers internally from an rtt
24879162SPeter.Dunlap@Sun.COM 	 * specific buffer cache since this is socket-specific code but for
24889162SPeter.Dunlap@Sun.COM 	 * now we will keep it simple.
24899162SPeter.Dunlap@Sun.COM 	 */
24909162SPeter.Dunlap@Sun.COM 	rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length);
24919162SPeter.Dunlap@Sun.COM 	if (rtt_buf == NULL) {
24929162SPeter.Dunlap@Sun.COM 		/*
24939162SPeter.Dunlap@Sun.COM 		 * If we're in FFP then the failure was likely a resource
24949162SPeter.Dunlap@Sun.COM 		 * allocation issue and we should close the connection by
24959162SPeter.Dunlap@Sun.COM 		 * sending a CE_TRANSPORT_FAIL event.
24969162SPeter.Dunlap@Sun.COM 		 *
24979162SPeter.Dunlap@Sun.COM 		 * If we're not in FFP then idm_buf_alloc will always
24989162SPeter.Dunlap@Sun.COM 		 * fail and the state is transitioning to "complete" anyway
24999162SPeter.Dunlap@Sun.COM 		 * so we won't bother to send an event.
25009162SPeter.Dunlap@Sun.COM 		 */
25019162SPeter.Dunlap@Sun.COM 		mutex_enter(&ic->ic_state_mutex);
25029162SPeter.Dunlap@Sun.COM 		if (ic->ic_ffp)
25039162SPeter.Dunlap@Sun.COM 			idm_conn_event_locked(ic, CE_TRANSPORT_FAIL,
25049162SPeter.Dunlap@Sun.COM 			    NULL, CT_NONE);
25059162SPeter.Dunlap@Sun.COM 		mutex_exit(&ic->ic_state_mutex);
2506*11857SJack.Meng@Sun.COM 		mutex_exit(&idt->idt_mutex);
25079162SPeter.Dunlap@Sun.COM 		return;
25089162SPeter.Dunlap@Sun.COM 	}
25099162SPeter.Dunlap@Sun.COM 
25109162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_buf_cb = NULL;
25119162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_cb_arg = NULL;
25129162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_bufoffset = offset;
25139162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_xfer_len = length;
25149162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_ic = idt->idt_ic;
25159162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_task_binding = idt;
25169162SPeter.Dunlap@Sun.COM 
25179162SPeter.Dunlap@Sun.COM 	/*
2518*11857SJack.Meng@Sun.COM 	 * The new buffer (if any) represents an additional
2519*11857SJack.Meng@Sun.COM 	 * reference on the task
2520*11857SJack.Meng@Sun.COM 	 */
2521*11857SJack.Meng@Sun.COM 	idm_task_hold(idt);
2522*11857SJack.Meng@Sun.COM 	mutex_exit(&idt->idt_mutex);
2523*11857SJack.Meng@Sun.COM 
2524*11857SJack.Meng@Sun.COM 	/*
25259162SPeter.Dunlap@Sun.COM 	 * Put the idm_buf_t on the tx queue.  It will be transmitted by
25269162SPeter.Dunlap@Sun.COM 	 * idm_sotx_thread.
25279162SPeter.Dunlap@Sun.COM 	 */
25289162SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
25299162SPeter.Dunlap@Sun.COM 
25309162SPeter.Dunlap@Sun.COM 	if (!so_conn->ic_tx_thread_running) {
25319162SPeter.Dunlap@Sun.COM 		idm_buf_free(rtt_buf);
25329162SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
2533*11857SJack.Meng@Sun.COM 		idm_task_rele(idt);
25349162SPeter.Dunlap@Sun.COM 		return;
25359162SPeter.Dunlap@Sun.COM 	}
25369162SPeter.Dunlap@Sun.COM 
25379162SPeter.Dunlap@Sun.COM 	/*
25389162SPeter.Dunlap@Sun.COM 	 * Build a template for the data PDU headers we will use so that
25399162SPeter.Dunlap@Sun.COM 	 * the SN values will stay consistent with other PDU's we are
25409162SPeter.Dunlap@Sun.COM 	 * transmitting like R2T and SCSI status.
25419162SPeter.Dunlap@Sun.COM 	 */
25429162SPeter.Dunlap@Sun.COM 	bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t));
25439162SPeter.Dunlap@Sun.COM 	tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl;
25449162SPeter.Dunlap@Sun.COM 	(*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu,
25459162SPeter.Dunlap@Sun.COM 	    ISCSI_OP_SCSI_DATA);
25469162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_tx_thread = B_TRUE;
25479162SPeter.Dunlap@Sun.COM 	rtt_buf->idb_in_transport = B_TRUE;
25489162SPeter.Dunlap@Sun.COM 	list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf);
25499162SPeter.Dunlap@Sun.COM 	cv_signal(&so_conn->ic_tx_cv);
25509162SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
25519162SPeter.Dunlap@Sun.COM }
25529162SPeter.Dunlap@Sun.COM 
25539162SPeter.Dunlap@Sun.COM static void
idm_so_send_rtt_data_done(idm_task_t * idt,idm_buf_t * idb)25549162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb)
25559162SPeter.Dunlap@Sun.COM {
25569162SPeter.Dunlap@Sun.COM 	/*
25579162SPeter.Dunlap@Sun.COM 	 * Don't worry about status -- we assume any error handling
25589162SPeter.Dunlap@Sun.COM 	 * is performed by the caller (idm_sotx_thread).
25599162SPeter.Dunlap@Sun.COM 	 */
25609162SPeter.Dunlap@Sun.COM 	idb->idb_in_transport = B_FALSE;
25619162SPeter.Dunlap@Sun.COM 	idm_task_rele(idt);
25629162SPeter.Dunlap@Sun.COM 	idm_buf_free(idb);
25639162SPeter.Dunlap@Sun.COM }
25649162SPeter.Dunlap@Sun.COM 
25659162SPeter.Dunlap@Sun.COM static idm_status_t
idm_so_send_buf_region(idm_task_t * idt,idm_buf_t * idb,uint32_t buf_region_offset,uint32_t buf_region_length)25669162SPeter.Dunlap@Sun.COM idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb,
25677978SPeter.Dunlap@Sun.COM     uint32_t buf_region_offset, uint32_t buf_region_length)
25687978SPeter.Dunlap@Sun.COM {
25697978SPeter.Dunlap@Sun.COM 	idm_conn_t		*ic;
25707978SPeter.Dunlap@Sun.COM 	uint32_t		max_dataseglen;
25717978SPeter.Dunlap@Sun.COM 	size_t			remainder, chunk;
25727978SPeter.Dunlap@Sun.COM 	uint32_t		data_offset = buf_region_offset;
25737978SPeter.Dunlap@Sun.COM 	iscsi_data_hdr_t	*bhs;
25747978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu;
25759162SPeter.Dunlap@Sun.COM 	idm_status_t		tx_status;
25767978SPeter.Dunlap@Sun.COM 
25777978SPeter.Dunlap@Sun.COM 	ASSERT(mutex_owned(&idt->idt_mutex));
25787978SPeter.Dunlap@Sun.COM 
25797978SPeter.Dunlap@Sun.COM 	ic = idt->idt_ic;
25807978SPeter.Dunlap@Sun.COM 
258110261SCharles.Ting@Sun.COM 	max_dataseglen = ic->ic_conn_params.max_xmit_dataseglen;
25827978SPeter.Dunlap@Sun.COM 	remainder = buf_region_length;
25837978SPeter.Dunlap@Sun.COM 
25847978SPeter.Dunlap@Sun.COM 	while (remainder) {
25857978SPeter.Dunlap@Sun.COM 		if (idt->idt_state != TASK_ACTIVE) {
25867978SPeter.Dunlap@Sun.COM 			ASSERT((idt->idt_state != TASK_IDLE) &&
25877978SPeter.Dunlap@Sun.COM 			    (idt->idt_state != TASK_COMPLETE));
25887978SPeter.Dunlap@Sun.COM 			return (IDM_STATUS_ABORTED);
25897978SPeter.Dunlap@Sun.COM 		}
25907978SPeter.Dunlap@Sun.COM 
25917978SPeter.Dunlap@Sun.COM 		/* check to see if we need to chunk the data */
25927978SPeter.Dunlap@Sun.COM 		if (remainder > max_dataseglen) {
25937978SPeter.Dunlap@Sun.COM 			chunk = max_dataseglen;
25947978SPeter.Dunlap@Sun.COM 		} else {
25957978SPeter.Dunlap@Sun.COM 			chunk = remainder;
25967978SPeter.Dunlap@Sun.COM 		}
25977978SPeter.Dunlap@Sun.COM 
25987978SPeter.Dunlap@Sun.COM 		/* Data PDU headers will always be sizeof (iscsi_hdr_t) */
25997978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP);
26007978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = ic;
260111081SPriya.Krishnan@Sun.COM 		pdu->isp_flags = 0;	/* initialize isp_flags */
26027978SPeter.Dunlap@Sun.COM 
26037978SPeter.Dunlap@Sun.COM 		/*
26049162SPeter.Dunlap@Sun.COM 		 * We've already built a build a header template
26057978SPeter.Dunlap@Sun.COM 		 * to use during the transfer.  Use this template so that
26067978SPeter.Dunlap@Sun.COM 		 * the SN values stay consistent with any unrelated PDU's
26077978SPeter.Dunlap@Sun.COM 		 * being transmitted.
26087978SPeter.Dunlap@Sun.COM 		 */
26099162SPeter.Dunlap@Sun.COM 		bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr,
26109162SPeter.Dunlap@Sun.COM 		    sizeof (iscsi_hdr_t));
26117978SPeter.Dunlap@Sun.COM 
26127978SPeter.Dunlap@Sun.COM 		/*
26137978SPeter.Dunlap@Sun.COM 		 * Set DataSN, data offset, and flags in BHS
26147978SPeter.Dunlap@Sun.COM 		 * For the prototype build, A = 0, S = 0, U = 0
26157978SPeter.Dunlap@Sun.COM 		 */
26167978SPeter.Dunlap@Sun.COM 		bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr);
26177978SPeter.Dunlap@Sun.COM 
26187978SPeter.Dunlap@Sun.COM 		bhs->datasn		= htonl(idt->idt_exp_datasn++);
26197978SPeter.Dunlap@Sun.COM 
26207978SPeter.Dunlap@Sun.COM 		hton24(bhs->dlength, chunk);
26217978SPeter.Dunlap@Sun.COM 		bhs->offset = htonl(idb->idb_bufoffset + data_offset);
26227978SPeter.Dunlap@Sun.COM 
262311081SPriya.Krishnan@Sun.COM 		/* setup data */
262411081SPriya.Krishnan@Sun.COM 		pdu->isp_data	=  (uint8_t *)idb->idb_buf + data_offset;
262511081SPriya.Krishnan@Sun.COM 		pdu->isp_datalen = (uint_t)chunk;
262611081SPriya.Krishnan@Sun.COM 
26277978SPeter.Dunlap@Sun.COM 		if (chunk == remainder) {
26287978SPeter.Dunlap@Sun.COM 			bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */
262911081SPriya.Krishnan@Sun.COM 			/* Piggyback the status with the last data PDU */
263011081SPriya.Krishnan@Sun.COM 			if (idt->idt_flags & IDM_TASK_PHASECOLLAPSE_REQ) {
263111081SPriya.Krishnan@Sun.COM 				pdu->isp_flags |= IDM_PDU_SET_STATSN |
263211081SPriya.Krishnan@Sun.COM 				    IDM_PDU_ADVANCE_STATSN;
263311081SPriya.Krishnan@Sun.COM 				(*idt->idt_ic->ic_conn_ops.icb_update_statsn)
263411081SPriya.Krishnan@Sun.COM 				    (idt, pdu);
263511081SPriya.Krishnan@Sun.COM 				idt->idt_flags |=
263611081SPriya.Krishnan@Sun.COM 				    IDM_TASK_PHASECOLLAPSE_SUCCESS;
263711081SPriya.Krishnan@Sun.COM 
263811081SPriya.Krishnan@Sun.COM 			}
26397978SPeter.Dunlap@Sun.COM 		}
26407978SPeter.Dunlap@Sun.COM 
264111081SPriya.Krishnan@Sun.COM 		remainder	-= chunk;
264211081SPriya.Krishnan@Sun.COM 		data_offset	+= chunk;
264311081SPriya.Krishnan@Sun.COM 
26449721SPriya.Krishnan@Sun.COM 		/* Instrument the data-send DTrace probe. */
26459721SPriya.Krishnan@Sun.COM 		if (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP) {
26469721SPriya.Krishnan@Sun.COM 			DTRACE_ISCSI_2(data__send,
26479721SPriya.Krishnan@Sun.COM 			    idm_conn_t *, idt->idt_ic,
26489721SPriya.Krishnan@Sun.COM 			    iscsi_data_rsp_hdr_t *,
26499721SPriya.Krishnan@Sun.COM 			    (iscsi_data_rsp_hdr_t *)pdu->isp_hdr);
26509721SPriya.Krishnan@Sun.COM 		}
26517978SPeter.Dunlap@Sun.COM 
26527978SPeter.Dunlap@Sun.COM 		/*
26537978SPeter.Dunlap@Sun.COM 		 * Now that we're done working with idt_exp_datasn,
26547978SPeter.Dunlap@Sun.COM 		 * idt->idt_state and idb->idb_bufoffset we can release
26557978SPeter.Dunlap@Sun.COM 		 * the task lock -- don't want to hold it across the
26567978SPeter.Dunlap@Sun.COM 		 * call to idm_i_so_tx since we could block.
26577978SPeter.Dunlap@Sun.COM 		 */
26587978SPeter.Dunlap@Sun.COM 		mutex_exit(&idt->idt_mutex);
26597978SPeter.Dunlap@Sun.COM 
26607978SPeter.Dunlap@Sun.COM 		/*
26617978SPeter.Dunlap@Sun.COM 		 * Transmit the PDU.  Call the internal routine directly
26627978SPeter.Dunlap@Sun.COM 		 * as there is already implicit ordering.
26637978SPeter.Dunlap@Sun.COM 		 */
26649162SPeter.Dunlap@Sun.COM 		if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) {
26659162SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
26669162SPeter.Dunlap@Sun.COM 			return (tx_status);
26679162SPeter.Dunlap@Sun.COM 		}
26687978SPeter.Dunlap@Sun.COM 
26697978SPeter.Dunlap@Sun.COM 		mutex_enter(&idt->idt_mutex);
26709162SPeter.Dunlap@Sun.COM 		idt->idt_tx_bytes += chunk;
26717978SPeter.Dunlap@Sun.COM 	}
26727978SPeter.Dunlap@Sun.COM 
26737978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
26747978SPeter.Dunlap@Sun.COM }
26757978SPeter.Dunlap@Sun.COM 
26767978SPeter.Dunlap@Sun.COM /*
26777978SPeter.Dunlap@Sun.COM  * TX PDU cache
26787978SPeter.Dunlap@Sun.COM  */
26797978SPeter.Dunlap@Sun.COM /* ARGSUSED */
26807978SPeter.Dunlap@Sun.COM int
idm_sotx_pdu_constructor(void * hdl,void * arg,int flags)26817978SPeter.Dunlap@Sun.COM idm_sotx_pdu_constructor(void *hdl, void *arg, int flags)
26827978SPeter.Dunlap@Sun.COM {
26837978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
26847978SPeter.Dunlap@Sun.COM 
26857978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
26867978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
26877978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
26887978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sotx_cache_pdu_cb;
26897978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
26907978SPeter.Dunlap@Sun.COM 	bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t));
26917978SPeter.Dunlap@Sun.COM 
26927978SPeter.Dunlap@Sun.COM 	return (0);
26937978SPeter.Dunlap@Sun.COM }
26947978SPeter.Dunlap@Sun.COM 
26957978SPeter.Dunlap@Sun.COM /* ARGSUSED */
26967978SPeter.Dunlap@Sun.COM void
idm_sotx_cache_pdu_cb(idm_pdu_t * pdu,idm_status_t status)26977978SPeter.Dunlap@Sun.COM idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
26987978SPeter.Dunlap@Sun.COM {
26997978SPeter.Dunlap@Sun.COM 	/* reset values between use */
27007978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
27017978SPeter.Dunlap@Sun.COM 
27027978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sotx_pdu_cache, pdu);
27037978SPeter.Dunlap@Sun.COM }
27047978SPeter.Dunlap@Sun.COM 
27057978SPeter.Dunlap@Sun.COM /*
27067978SPeter.Dunlap@Sun.COM  * RX PDU cache
27077978SPeter.Dunlap@Sun.COM  */
27087978SPeter.Dunlap@Sun.COM /* ARGSUSED */
27097978SPeter.Dunlap@Sun.COM int
idm_sorx_pdu_constructor(void * hdl,void * arg,int flags)27107978SPeter.Dunlap@Sun.COM idm_sorx_pdu_constructor(void *hdl, void *arg, int flags)
27117978SPeter.Dunlap@Sun.COM {
27127978SPeter.Dunlap@Sun.COM 	idm_pdu_t	*pdu = hdl;
27137978SPeter.Dunlap@Sun.COM 
27147978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
27157978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
27167978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
27177978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
27187978SPeter.Dunlap@Sun.COM 
27197978SPeter.Dunlap@Sun.COM 	return (0);
27207978SPeter.Dunlap@Sun.COM }
27217978SPeter.Dunlap@Sun.COM 
27227978SPeter.Dunlap@Sun.COM /* ARGSUSED */
27237978SPeter.Dunlap@Sun.COM static void
idm_sorx_cache_pdu_cb(idm_pdu_t * pdu,idm_status_t status)27247978SPeter.Dunlap@Sun.COM idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
27257978SPeter.Dunlap@Sun.COM {
27267978SPeter.Dunlap@Sun.COM 	pdu->isp_iovlen = 0;
27277978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
27287978SPeter.Dunlap@Sun.COM 	kmem_cache_free(idm.idm_sorx_pdu_cache, pdu);
27297978SPeter.Dunlap@Sun.COM }
27307978SPeter.Dunlap@Sun.COM 
27317978SPeter.Dunlap@Sun.COM static void
idm_sorx_addl_pdu_cb(idm_pdu_t * pdu,idm_status_t status)27327978SPeter.Dunlap@Sun.COM idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status)
27337978SPeter.Dunlap@Sun.COM {
27347978SPeter.Dunlap@Sun.COM 	/*
27357978SPeter.Dunlap@Sun.COM 	 * We had to modify our cached RX PDU with a longer header buffer
27367978SPeter.Dunlap@Sun.COM 	 * and/or a longer data buffer.  Release the new buffers and fix
27377978SPeter.Dunlap@Sun.COM 	 * the fields back to what we would expect for a cached RX PDU.
27387978SPeter.Dunlap@Sun.COM 	 */
27397978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_HDR) {
27407978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_hdr, pdu->isp_hdrlen);
27417978SPeter.Dunlap@Sun.COM 	}
27427978SPeter.Dunlap@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADDL_DATA) {
27437978SPeter.Dunlap@Sun.COM 		kmem_free(pdu->isp_data, pdu->isp_datalen);
27447978SPeter.Dunlap@Sun.COM 	}
27457978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1);
27467978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
27477978SPeter.Dunlap@Sun.COM 	pdu->isp_data = NULL;
27487978SPeter.Dunlap@Sun.COM 	pdu->isp_datalen = 0;
27497978SPeter.Dunlap@Sun.COM 	pdu->isp_sorx_buf = 0;
27507978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = idm_sorx_cache_pdu_cb;
27517978SPeter.Dunlap@Sun.COM 	idm_sorx_cache_pdu_cb(pdu, status);
27527978SPeter.Dunlap@Sun.COM }
27537978SPeter.Dunlap@Sun.COM 
27547978SPeter.Dunlap@Sun.COM /*
27557978SPeter.Dunlap@Sun.COM  * This thread is only active when I/O is queued for transmit
27567978SPeter.Dunlap@Sun.COM  * because the socket is busy.
27577978SPeter.Dunlap@Sun.COM  */
27587978SPeter.Dunlap@Sun.COM void
idm_sotx_thread(void * arg)27597978SPeter.Dunlap@Sun.COM idm_sotx_thread(void *arg)
27607978SPeter.Dunlap@Sun.COM {
27617978SPeter.Dunlap@Sun.COM 	idm_conn_t	*ic = arg;
27627978SPeter.Dunlap@Sun.COM 	idm_tx_obj_t	*object, *next;
27637978SPeter.Dunlap@Sun.COM 	idm_so_conn_t	*so_conn;
27647978SPeter.Dunlap@Sun.COM 	idm_status_t	status = IDM_STATUS_SUCCESS;
27657978SPeter.Dunlap@Sun.COM 
27667978SPeter.Dunlap@Sun.COM 	idm_conn_hold(ic);
27677978SPeter.Dunlap@Sun.COM 
27687978SPeter.Dunlap@Sun.COM 	mutex_enter(&ic->ic_mutex);
27697978SPeter.Dunlap@Sun.COM 	so_conn = ic->ic_transport_private;
27707978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_running = B_TRUE;
27717978SPeter.Dunlap@Sun.COM 	so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did;
27727978SPeter.Dunlap@Sun.COM 	cv_signal(&ic->ic_cv);
27737978SPeter.Dunlap@Sun.COM 	mutex_exit(&ic->ic_mutex);
27747978SPeter.Dunlap@Sun.COM 
27757978SPeter.Dunlap@Sun.COM 	mutex_enter(&so_conn->ic_tx_mutex);
27767978SPeter.Dunlap@Sun.COM 
27777978SPeter.Dunlap@Sun.COM 	while (so_conn->ic_tx_thread_running) {
27787978SPeter.Dunlap@Sun.COM 		while (list_is_empty(&so_conn->ic_tx_list)) {
27797978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic);
27807978SPeter.Dunlap@Sun.COM 			cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex);
27817978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic);
27827978SPeter.Dunlap@Sun.COM 
27837978SPeter.Dunlap@Sun.COM 			if (!so_conn->ic_tx_thread_running) {
27847978SPeter.Dunlap@Sun.COM 				goto tx_bail;
27857978SPeter.Dunlap@Sun.COM 			}
27867978SPeter.Dunlap@Sun.COM 		}
27877978SPeter.Dunlap@Sun.COM 
27887978SPeter.Dunlap@Sun.COM 		object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
27897978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
27907978SPeter.Dunlap@Sun.COM 		mutex_exit(&so_conn->ic_tx_mutex);
27917978SPeter.Dunlap@Sun.COM 
27927978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
279311081SPriya.Krishnan@Sun.COM 		case IDM_PDU_MAGIC: {
279411081SPriya.Krishnan@Sun.COM 			idm_pdu_t *pdu = (idm_pdu_t *)object;
27957978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic,
27967978SPeter.Dunlap@Sun.COM 			    idm_pdu_t *, (idm_pdu_t *)object);
27977978SPeter.Dunlap@Sun.COM 
279811081SPriya.Krishnan@Sun.COM 			if (pdu->isp_flags & IDM_PDU_SET_STATSN) {
279911081SPriya.Krishnan@Sun.COM 				/* No IDM task */
280011081SPriya.Krishnan@Sun.COM 				(ic->ic_conn_ops.icb_update_statsn)(NULL, pdu);
280111081SPriya.Krishnan@Sun.COM 			}
28027978SPeter.Dunlap@Sun.COM 			status = idm_i_so_tx((idm_pdu_t *)object);
28037978SPeter.Dunlap@Sun.COM 			break;
280411081SPriya.Krishnan@Sun.COM 		}
28057978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
28067978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
28077978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
28087978SPeter.Dunlap@Sun.COM 
28097978SPeter.Dunlap@Sun.COM 			DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic,
28107978SPeter.Dunlap@Sun.COM 			    idm_buf_t *, idb);
28117978SPeter.Dunlap@Sun.COM 
28127978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
28137978SPeter.Dunlap@Sun.COM 			status = idm_so_send_buf_region(idt,
28149162SPeter.Dunlap@Sun.COM 			    idb, 0, idb->idb_xfer_len);
28157978SPeter.Dunlap@Sun.COM 
28167978SPeter.Dunlap@Sun.COM 			/*
28177978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
28187978SPeter.Dunlap@Sun.COM 			 * be "in transport"
28197978SPeter.Dunlap@Sun.COM 			 */
28207978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
28219162SPeter.Dunlap@Sun.COM 			if (IDM_CONN_ISTGT(ic)) {
28229162SPeter.Dunlap@Sun.COM 				/*
28239162SPeter.Dunlap@Sun.COM 				 * idm_buf_tx_to_ini_done releases
28249162SPeter.Dunlap@Sun.COM 				 * idt->idt_mutex
28259162SPeter.Dunlap@Sun.COM 				 */
28269721SPriya.Krishnan@Sun.COM 				DTRACE_ISCSI_8(xfer__done,
28279721SPriya.Krishnan@Sun.COM 				    idm_conn_t *, idt->idt_ic,
28289721SPriya.Krishnan@Sun.COM 				    uintptr_t, idb->idb_buf,
28299721SPriya.Krishnan@Sun.COM 				    uint32_t, idb->idb_bufoffset,
28309721SPriya.Krishnan@Sun.COM 				    uint64_t, 0, uint32_t, 0, uint32_t, 0,
28319721SPriya.Krishnan@Sun.COM 				    uint32_t, idb->idb_xfer_len,
28329721SPriya.Krishnan@Sun.COM 				    int, XFER_BUF_TX_TO_INI);
28339162SPeter.Dunlap@Sun.COM 				idm_buf_tx_to_ini_done(idt, idb, status);
28349162SPeter.Dunlap@Sun.COM 			} else {
28359162SPeter.Dunlap@Sun.COM 				idm_so_send_rtt_data_done(idt, idb);
28369162SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
28379162SPeter.Dunlap@Sun.COM 			}
28387978SPeter.Dunlap@Sun.COM 			break;
28397978SPeter.Dunlap@Sun.COM 		}
28407978SPeter.Dunlap@Sun.COM 
28417978SPeter.Dunlap@Sun.COM 		default:
28427978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic "
28437978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
28447978SPeter.Dunlap@Sun.COM 			status = IDM_STATUS_FAIL;
28457978SPeter.Dunlap@Sun.COM 		}
28467978SPeter.Dunlap@Sun.COM 
28477978SPeter.Dunlap@Sun.COM 		mutex_enter(&so_conn->ic_tx_mutex);
28487978SPeter.Dunlap@Sun.COM 
28497978SPeter.Dunlap@Sun.COM 		if (status != IDM_STATUS_SUCCESS) {
28507978SPeter.Dunlap@Sun.COM 			so_conn->ic_tx_thread_running = B_FALSE;
28517978SPeter.Dunlap@Sun.COM 			idm_conn_event(ic, CE_TRANSPORT_FAIL, status);
28527978SPeter.Dunlap@Sun.COM 		}
28537978SPeter.Dunlap@Sun.COM 	}
28547978SPeter.Dunlap@Sun.COM 
28557978SPeter.Dunlap@Sun.COM 	/*
28567978SPeter.Dunlap@Sun.COM 	 * Before we leave, we need to abort every item remaining in the
28577978SPeter.Dunlap@Sun.COM 	 * TX list.
28587978SPeter.Dunlap@Sun.COM 	 */
28597978SPeter.Dunlap@Sun.COM 
28607978SPeter.Dunlap@Sun.COM tx_bail:
28617978SPeter.Dunlap@Sun.COM 	object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list);
28627978SPeter.Dunlap@Sun.COM 
28637978SPeter.Dunlap@Sun.COM 	while (object != NULL) {
28647978SPeter.Dunlap@Sun.COM 		next = list_next(&so_conn->ic_tx_list, object);
28657978SPeter.Dunlap@Sun.COM 
28667978SPeter.Dunlap@Sun.COM 		list_remove(&so_conn->ic_tx_list, object);
28677978SPeter.Dunlap@Sun.COM 		switch (object->idm_tx_obj_magic) {
28687978SPeter.Dunlap@Sun.COM 		case IDM_PDU_MAGIC:
28697978SPeter.Dunlap@Sun.COM 			idm_pdu_complete((idm_pdu_t *)object,
28707978SPeter.Dunlap@Sun.COM 			    IDM_STATUS_ABORTED);
28717978SPeter.Dunlap@Sun.COM 			break;
28727978SPeter.Dunlap@Sun.COM 
28737978SPeter.Dunlap@Sun.COM 		case IDM_BUF_MAGIC: {
28747978SPeter.Dunlap@Sun.COM 			idm_buf_t *idb = (idm_buf_t *)object;
28757978SPeter.Dunlap@Sun.COM 			idm_task_t *idt = idb->idb_task_binding;
28767978SPeter.Dunlap@Sun.COM 			mutex_exit(&so_conn->ic_tx_mutex);
28777978SPeter.Dunlap@Sun.COM 			mutex_enter(&idt->idt_mutex);
28787978SPeter.Dunlap@Sun.COM 			/*
28797978SPeter.Dunlap@Sun.COM 			 * TX thread owns the buffer so we expect it to
28807978SPeter.Dunlap@Sun.COM 			 * be "in transport"
28817978SPeter.Dunlap@Sun.COM 			 */
28827978SPeter.Dunlap@Sun.COM 			ASSERT(idb->idb_in_transport);
28839162SPeter.Dunlap@Sun.COM 			if (IDM_CONN_ISTGT(ic)) {
28849162SPeter.Dunlap@Sun.COM 				/*
28859162SPeter.Dunlap@Sun.COM 				 * idm_buf_tx_to_ini_done releases
28869162SPeter.Dunlap@Sun.COM 				 * idt->idt_mutex
28879162SPeter.Dunlap@Sun.COM 				 */
28889721SPriya.Krishnan@Sun.COM 				DTRACE_ISCSI_8(xfer__done,
28899721SPriya.Krishnan@Sun.COM 				    idm_conn_t *, idt->idt_ic,
28909721SPriya.Krishnan@Sun.COM 				    uintptr_t, idb->idb_buf,
28919721SPriya.Krishnan@Sun.COM 				    uint32_t, idb->idb_bufoffset,
28929721SPriya.Krishnan@Sun.COM 				    uint64_t, 0, uint32_t, 0, uint32_t, 0,
28939721SPriya.Krishnan@Sun.COM 				    uint32_t, idb->idb_xfer_len,
28949721SPriya.Krishnan@Sun.COM 				    int, XFER_BUF_TX_TO_INI);
28959162SPeter.Dunlap@Sun.COM 				idm_buf_tx_to_ini_done(idt, idb,
28969162SPeter.Dunlap@Sun.COM 				    IDM_STATUS_ABORTED);
28979162SPeter.Dunlap@Sun.COM 			} else {
28989162SPeter.Dunlap@Sun.COM 				idm_so_send_rtt_data_done(idt, idb);
28999162SPeter.Dunlap@Sun.COM 				mutex_exit(&idt->idt_mutex);
29009162SPeter.Dunlap@Sun.COM 			}
29017978SPeter.Dunlap@Sun.COM 			mutex_enter(&so_conn->ic_tx_mutex);
29027978SPeter.Dunlap@Sun.COM 			break;
29037978SPeter.Dunlap@Sun.COM 		}
29047978SPeter.Dunlap@Sun.COM 		default:
29057978SPeter.Dunlap@Sun.COM 			IDM_CONN_LOG(CE_WARN,
29067978SPeter.Dunlap@Sun.COM 			    "idm_sotx_thread: Unexpected magic "
29077978SPeter.Dunlap@Sun.COM 			    "(0x%08x)", object->idm_tx_obj_magic);
29087978SPeter.Dunlap@Sun.COM 		}
29097978SPeter.Dunlap@Sun.COM 
29107978SPeter.Dunlap@Sun.COM 		object = next;
29117978SPeter.Dunlap@Sun.COM 	}
29127978SPeter.Dunlap@Sun.COM 
29137978SPeter.Dunlap@Sun.COM 	mutex_exit(&so_conn->ic_tx_mutex);
29147978SPeter.Dunlap@Sun.COM 	idm_conn_rele(ic);
29157978SPeter.Dunlap@Sun.COM 	thread_exit();
29167978SPeter.Dunlap@Sun.COM 	/*NOTREACHED*/
29177978SPeter.Dunlap@Sun.COM }
291810156SZhang.Yi@Sun.COM 
291910156SZhang.Yi@Sun.COM static void
idm_so_socket_set_nonblock(struct sonode * node)292010156SZhang.Yi@Sun.COM idm_so_socket_set_nonblock(struct sonode *node)
292110156SZhang.Yi@Sun.COM {
292210156SZhang.Yi@Sun.COM 	(void) VOP_SETFL(node->so_vnode, node->so_flag,
292310156SZhang.Yi@Sun.COM 	    (node->so_state | FNONBLOCK), CRED(), NULL);
292410156SZhang.Yi@Sun.COM }
292510156SZhang.Yi@Sun.COM 
292610156SZhang.Yi@Sun.COM static void
idm_so_socket_set_block(struct sonode * node)292710156SZhang.Yi@Sun.COM idm_so_socket_set_block(struct sonode *node)
292810156SZhang.Yi@Sun.COM {
292910156SZhang.Yi@Sun.COM 	(void) VOP_SETFL(node->so_vnode, node->so_flag,
293010156SZhang.Yi@Sun.COM 	    (node->so_state & (~FNONBLOCK)), CRED(), NULL);
293110156SZhang.Yi@Sun.COM }
293210505SPeter.Cudhea@Sun.COM 
293310505SPeter.Cudhea@Sun.COM 
293410505SPeter.Cudhea@Sun.COM /*
293510505SPeter.Cudhea@Sun.COM  * Called by kernel sockets when the connection has been accepted or
293610505SPeter.Cudhea@Sun.COM  * rejected. In early volo, a "disconnect" callback was sent instead of
293710505SPeter.Cudhea@Sun.COM  * "connectfailed", so we check for both.
293810505SPeter.Cudhea@Sun.COM  */
293910505SPeter.Cudhea@Sun.COM /* ARGSUSED */
294010505SPeter.Cudhea@Sun.COM void
idm_so_timed_socket_connect_cb(ksocket_t ks,ksocket_callback_event_t ev,void * arg,uintptr_t info)294110505SPeter.Cudhea@Sun.COM idm_so_timed_socket_connect_cb(ksocket_t ks,
294210505SPeter.Cudhea@Sun.COM     ksocket_callback_event_t ev, void *arg, uintptr_t info)
294310505SPeter.Cudhea@Sun.COM {
294410505SPeter.Cudhea@Sun.COM 	idm_so_timed_socket_t	*itp = arg;
294510505SPeter.Cudhea@Sun.COM 	ASSERT(itp != NULL);
294610505SPeter.Cudhea@Sun.COM 	ASSERT(ev == KSOCKET_EV_CONNECTED ||
294710505SPeter.Cudhea@Sun.COM 	    ev == KSOCKET_EV_CONNECTFAILED ||
294810505SPeter.Cudhea@Sun.COM 	    ev == KSOCKET_EV_DISCONNECTED);
294910505SPeter.Cudhea@Sun.COM 
295010505SPeter.Cudhea@Sun.COM 	mutex_enter(&idm_so_timed_socket_mutex);
295110505SPeter.Cudhea@Sun.COM 	itp->it_callback_called = B_TRUE;
295210505SPeter.Cudhea@Sun.COM 	if (ev == KSOCKET_EV_CONNECTED) {
295310505SPeter.Cudhea@Sun.COM 		itp->it_socket_error_code = 0;
295410505SPeter.Cudhea@Sun.COM 	} else {
295510505SPeter.Cudhea@Sun.COM 		/* Make sure the error code is non-zero on error */
295610505SPeter.Cudhea@Sun.COM 		if (info == 0)
295710505SPeter.Cudhea@Sun.COM 			info = ECONNRESET;
295810505SPeter.Cudhea@Sun.COM 		itp->it_socket_error_code = (int)info;
295910505SPeter.Cudhea@Sun.COM 	}
296010505SPeter.Cudhea@Sun.COM 	cv_signal(&itp->it_cv);
296110505SPeter.Cudhea@Sun.COM 	mutex_exit(&idm_so_timed_socket_mutex);
296210505SPeter.Cudhea@Sun.COM }
296310505SPeter.Cudhea@Sun.COM 
296410505SPeter.Cudhea@Sun.COM int
idm_so_timed_socket_connect(ksocket_t ks,struct sockaddr_storage * sa,int sa_sz,int login_max_usec)296510505SPeter.Cudhea@Sun.COM idm_so_timed_socket_connect(ksocket_t ks,
296610505SPeter.Cudhea@Sun.COM     struct sockaddr_storage *sa, int sa_sz, int login_max_usec)
296710505SPeter.Cudhea@Sun.COM {
296810505SPeter.Cudhea@Sun.COM 	clock_t			conn_login_max;
296910505SPeter.Cudhea@Sun.COM 	int			rc, nonblocking, rval;
297010505SPeter.Cudhea@Sun.COM 	idm_so_timed_socket_t	it;
297110505SPeter.Cudhea@Sun.COM 	ksocket_callbacks_t	ks_cb;
297210505SPeter.Cudhea@Sun.COM 
297310505SPeter.Cudhea@Sun.COM 	conn_login_max = ddi_get_lbolt() + drv_usectohz(login_max_usec);
297410505SPeter.Cudhea@Sun.COM 
297510505SPeter.Cudhea@Sun.COM 	/*
297610505SPeter.Cudhea@Sun.COM 	 * Set to non-block socket mode, with callback on connect
297710505SPeter.Cudhea@Sun.COM 	 * Early volo used "disconnected" instead of "connectfailed",
297810505SPeter.Cudhea@Sun.COM 	 * so set callback to look for both.
297910505SPeter.Cudhea@Sun.COM 	 */
298010505SPeter.Cudhea@Sun.COM 	bzero(&it, sizeof (it));
298110505SPeter.Cudhea@Sun.COM 	ks_cb.ksock_cb_flags = KSOCKET_CB_CONNECTED |
298210505SPeter.Cudhea@Sun.COM 	    KSOCKET_CB_CONNECTFAILED | KSOCKET_CB_DISCONNECTED;
298310505SPeter.Cudhea@Sun.COM 	ks_cb.ksock_cb_connected = idm_so_timed_socket_connect_cb;
298410505SPeter.Cudhea@Sun.COM 	ks_cb.ksock_cb_connectfailed = idm_so_timed_socket_connect_cb;
298510505SPeter.Cudhea@Sun.COM 	ks_cb.ksock_cb_disconnected = idm_so_timed_socket_connect_cb;
298610505SPeter.Cudhea@Sun.COM 	cv_init(&it.it_cv, NULL, CV_DEFAULT, NULL);
298710505SPeter.Cudhea@Sun.COM 	rc = ksocket_setcallbacks(ks, &ks_cb, &it, CRED());
298810505SPeter.Cudhea@Sun.COM 	if (rc != 0)
298910505SPeter.Cudhea@Sun.COM 		return (rc);
299010505SPeter.Cudhea@Sun.COM 
299110505SPeter.Cudhea@Sun.COM 	/* Set to non-blocking mode */
299210505SPeter.Cudhea@Sun.COM 	nonblocking = 1;
299310505SPeter.Cudhea@Sun.COM 	rc = ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
299410505SPeter.Cudhea@Sun.COM 	    CRED());
299510505SPeter.Cudhea@Sun.COM 	if (rc != 0)
299610505SPeter.Cudhea@Sun.COM 		goto cleanup;
299710505SPeter.Cudhea@Sun.COM 
299810505SPeter.Cudhea@Sun.COM 	bzero(&it, sizeof (it));
299910505SPeter.Cudhea@Sun.COM 	for (;;) {
300010505SPeter.Cudhea@Sun.COM 		/*
300110505SPeter.Cudhea@Sun.COM 		 * Warning -- in a loopback scenario, the call to
300210505SPeter.Cudhea@Sun.COM 		 * the connect_cb can occur inside the call to
300310505SPeter.Cudhea@Sun.COM 		 * ksocket_connect. Do not hold the mutex around the
300410505SPeter.Cudhea@Sun.COM 		 * call to ksocket_connect.
300510505SPeter.Cudhea@Sun.COM 		 */
300610505SPeter.Cudhea@Sun.COM 		rc = ksocket_connect(ks, (struct sockaddr *)sa, sa_sz, CRED());
300710505SPeter.Cudhea@Sun.COM 		if (rc == 0 || rc == EISCONN) {
300810505SPeter.Cudhea@Sun.COM 			/* socket success or already success */
300910505SPeter.Cudhea@Sun.COM 			rc = 0;
301010505SPeter.Cudhea@Sun.COM 			break;
301110505SPeter.Cudhea@Sun.COM 		}
301210505SPeter.Cudhea@Sun.COM 		if ((rc != EINPROGRESS) && (rc != EALREADY)) {
301310505SPeter.Cudhea@Sun.COM 			break;
301410505SPeter.Cudhea@Sun.COM 		}
301510505SPeter.Cudhea@Sun.COM 
301610505SPeter.Cudhea@Sun.COM 		/* TCP connect still in progress. See if out of time. */
301710505SPeter.Cudhea@Sun.COM 		if (ddi_get_lbolt() > conn_login_max) {
301810505SPeter.Cudhea@Sun.COM 			/*
301910505SPeter.Cudhea@Sun.COM 			 * Connection retry timeout,
302010505SPeter.Cudhea@Sun.COM 			 * failed connect to target.
302110505SPeter.Cudhea@Sun.COM 			 */
302210505SPeter.Cudhea@Sun.COM 			rc = ETIMEDOUT;
302310505SPeter.Cudhea@Sun.COM 			break;
302410505SPeter.Cudhea@Sun.COM 		}
302510505SPeter.Cudhea@Sun.COM 
302610505SPeter.Cudhea@Sun.COM 		/*
302710505SPeter.Cudhea@Sun.COM 		 * TCP connect still in progress.  Sleep until callback.
302810505SPeter.Cudhea@Sun.COM 		 * Do NOT go to sleep if the callback already occurred!
302910505SPeter.Cudhea@Sun.COM 		 */
303010505SPeter.Cudhea@Sun.COM 		mutex_enter(&idm_so_timed_socket_mutex);
303110505SPeter.Cudhea@Sun.COM 		if (!it.it_callback_called) {
303210505SPeter.Cudhea@Sun.COM 			(void) cv_timedwait(&it.it_cv,
303310505SPeter.Cudhea@Sun.COM 			    &idm_so_timed_socket_mutex, conn_login_max);
303410505SPeter.Cudhea@Sun.COM 		}
303510505SPeter.Cudhea@Sun.COM 		if (it.it_callback_called) {
303610505SPeter.Cudhea@Sun.COM 			rc = it.it_socket_error_code;
303710505SPeter.Cudhea@Sun.COM 			mutex_exit(&idm_so_timed_socket_mutex);
303810505SPeter.Cudhea@Sun.COM 			break;
303910505SPeter.Cudhea@Sun.COM 		}
304010505SPeter.Cudhea@Sun.COM 		/* If timer expires, go call ksocket_connect one last time. */
304110505SPeter.Cudhea@Sun.COM 		mutex_exit(&idm_so_timed_socket_mutex);
304210505SPeter.Cudhea@Sun.COM 	}
304310505SPeter.Cudhea@Sun.COM 
304410505SPeter.Cudhea@Sun.COM 	/* resume blocking mode */
304510505SPeter.Cudhea@Sun.COM 	nonblocking = 0;
304611093SSrivijitha.Dugganapalli@Sun.COM 	(void) ksocket_ioctl(ks, FIONBIO, (intptr_t)&nonblocking, &rval,
304710505SPeter.Cudhea@Sun.COM 	    CRED());
304810505SPeter.Cudhea@Sun.COM cleanup:
304911093SSrivijitha.Dugganapalli@Sun.COM 	(void) ksocket_setcallbacks(ks, NULL, NULL, CRED());
305010505SPeter.Cudhea@Sun.COM 	cv_destroy(&it.it_cv);
305110505SPeter.Cudhea@Sun.COM 	if (rc != 0) {
305210505SPeter.Cudhea@Sun.COM 		idm_soshutdown(ks);
305310505SPeter.Cudhea@Sun.COM 	}
305410505SPeter.Cudhea@Sun.COM 	return (rc);
305510505SPeter.Cudhea@Sun.COM }
305610505SPeter.Cudhea@Sun.COM 
305710505SPeter.Cudhea@Sun.COM 
305810505SPeter.Cudhea@Sun.COM void
idm_addr_to_sa(idm_addr_t * dportal,struct sockaddr_storage * sa)305910505SPeter.Cudhea@Sun.COM idm_addr_to_sa(idm_addr_t *dportal, struct sockaddr_storage *sa)
306010505SPeter.Cudhea@Sun.COM {
306110505SPeter.Cudhea@Sun.COM 	int			dp_addr_size;
306210505SPeter.Cudhea@Sun.COM 	struct sockaddr_in	*sin;
306310505SPeter.Cudhea@Sun.COM 	struct sockaddr_in6	*sin6;
306410505SPeter.Cudhea@Sun.COM 
306510505SPeter.Cudhea@Sun.COM 	/* Build sockaddr_storage for this portal (idm_addr_t) */
306610505SPeter.Cudhea@Sun.COM 	bzero(sa, sizeof (*sa));
306710505SPeter.Cudhea@Sun.COM 	dp_addr_size = dportal->a_addr.i_insize;
306810505SPeter.Cudhea@Sun.COM 	if (dp_addr_size == sizeof (struct in_addr)) {
306910505SPeter.Cudhea@Sun.COM 		/* IPv4 */
307010505SPeter.Cudhea@Sun.COM 		sa->ss_family = AF_INET;
307110505SPeter.Cudhea@Sun.COM 		sin = (struct sockaddr_in *)sa;
307210505SPeter.Cudhea@Sun.COM 		sin->sin_port = htons(dportal->a_port);
307310505SPeter.Cudhea@Sun.COM 		bcopy(&dportal->a_addr.i_addr.in4,
307410505SPeter.Cudhea@Sun.COM 		    &sin->sin_addr, sizeof (struct in_addr));
307510505SPeter.Cudhea@Sun.COM 	} else if (dp_addr_size == sizeof (struct in6_addr)) {
307610505SPeter.Cudhea@Sun.COM 		/* IPv6 */
307710505SPeter.Cudhea@Sun.COM 		sa->ss_family = AF_INET6;
307810505SPeter.Cudhea@Sun.COM 		sin6 = (struct sockaddr_in6 *)sa;
307910505SPeter.Cudhea@Sun.COM 		sin6->sin6_port = htons(dportal->a_port);
308010505SPeter.Cudhea@Sun.COM 		bcopy(&dportal->a_addr.i_addr.in6,
308110505SPeter.Cudhea@Sun.COM 		    &sin6->sin6_addr, sizeof (struct in6_addr));
308210505SPeter.Cudhea@Sun.COM 	} else {
308310505SPeter.Cudhea@Sun.COM 		ASSERT(0);
308410505SPeter.Cudhea@Sun.COM 	}
308510505SPeter.Cudhea@Sun.COM }
308610505SPeter.Cudhea@Sun.COM 
308710505SPeter.Cudhea@Sun.COM 
308810505SPeter.Cudhea@Sun.COM /*
308910505SPeter.Cudhea@Sun.COM  * return a human-readable form of a sockaddr_storage, in the form
309010505SPeter.Cudhea@Sun.COM  * [ip-address]:port.  This is used in calls to logging functions.
309110505SPeter.Cudhea@Sun.COM  * If several calls to idm_sa_ntop are made within the same invocation
309210505SPeter.Cudhea@Sun.COM  * of a logging function, then each one needs its own buf.
309310505SPeter.Cudhea@Sun.COM  */
309410505SPeter.Cudhea@Sun.COM const char *
idm_sa_ntop(const struct sockaddr_storage * sa,char * buf,size_t size)309510505SPeter.Cudhea@Sun.COM idm_sa_ntop(const struct sockaddr_storage *sa,
309610505SPeter.Cudhea@Sun.COM     char *buf, size_t size)
309710505SPeter.Cudhea@Sun.COM {
309810505SPeter.Cudhea@Sun.COM 	static const char bogus_ip[] = "[0].-1";
309910505SPeter.Cudhea@Sun.COM 	char tmp[INET6_ADDRSTRLEN];
310010505SPeter.Cudhea@Sun.COM 
310110505SPeter.Cudhea@Sun.COM 	switch (sa->ss_family) {
310210505SPeter.Cudhea@Sun.COM 	case AF_INET6:
310310505SPeter.Cudhea@Sun.COM 		{
310410505SPeter.Cudhea@Sun.COM 			const struct sockaddr_in6 *in6 =
310510505SPeter.Cudhea@Sun.COM 			    (const struct sockaddr_in6 *) sa;
310610505SPeter.Cudhea@Sun.COM 
310710505SPeter.Cudhea@Sun.COM 			if (inet_ntop(in6->sin6_family,
310810505SPeter.Cudhea@Sun.COM 			    &in6->sin6_addr, tmp, sizeof (tmp)) == NULL) {
310910505SPeter.Cudhea@Sun.COM 				goto err;
311010505SPeter.Cudhea@Sun.COM 			}
311110505SPeter.Cudhea@Sun.COM 			if (strlen(tmp) + sizeof ("[].65535") > size) {
311210505SPeter.Cudhea@Sun.COM 				goto err;
311310505SPeter.Cudhea@Sun.COM 			}
311410505SPeter.Cudhea@Sun.COM 			/* struct sockaddr_storage gets port info from v4 loc */
311510505SPeter.Cudhea@Sun.COM 			(void) snprintf(buf, size, "[%s].%u", tmp,
311610505SPeter.Cudhea@Sun.COM 			    ntohs(in6->sin6_port));
311710505SPeter.Cudhea@Sun.COM 			return (buf);
311810505SPeter.Cudhea@Sun.COM 		}
311910505SPeter.Cudhea@Sun.COM 	case AF_INET:
312010505SPeter.Cudhea@Sun.COM 		{
312110505SPeter.Cudhea@Sun.COM 			const struct sockaddr_in *in =
312210505SPeter.Cudhea@Sun.COM 			    (const struct sockaddr_in *) sa;
312310505SPeter.Cudhea@Sun.COM 
312410505SPeter.Cudhea@Sun.COM 			if (inet_ntop(in->sin_family, &in->sin_addr,
312510505SPeter.Cudhea@Sun.COM 			    tmp, sizeof (tmp)) == NULL) {
312610505SPeter.Cudhea@Sun.COM 				goto err;
312710505SPeter.Cudhea@Sun.COM 			}
312810505SPeter.Cudhea@Sun.COM 			if (strlen(tmp) + sizeof ("[].65535") > size) {
312910505SPeter.Cudhea@Sun.COM 				goto err;
313010505SPeter.Cudhea@Sun.COM 			}
313110505SPeter.Cudhea@Sun.COM 			(void) snprintf(buf, size,  "[%s].%u", tmp,
313210505SPeter.Cudhea@Sun.COM 			    ntohs(in->sin_port));
313310505SPeter.Cudhea@Sun.COM 			return (buf);
313410505SPeter.Cudhea@Sun.COM 		}
313510505SPeter.Cudhea@Sun.COM 	default:
313610505SPeter.Cudhea@Sun.COM 		break;
313710505SPeter.Cudhea@Sun.COM 	}
313810505SPeter.Cudhea@Sun.COM err:
313910505SPeter.Cudhea@Sun.COM 	(void) snprintf(buf, size, "%s", bogus_ip);
314010505SPeter.Cudhea@Sun.COM 	return (buf);
314110505SPeter.Cudhea@Sun.COM }
3142