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