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