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); 73*9162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, 74*9162SPeter.Dunlap@Sun.COM idm_buf_t *idb, uint32_t offset, uint32_t length); 75*9162SPeter.Dunlap@Sun.COM static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb); 76*9162SPeter.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); 97*9162SPeter.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 1707978SPeter.Dunlap@Sun.COM /* Set the sockets transport ops */ 1717978SPeter.Dunlap@Sun.COM it->it_ops = &idm_so_transport_ops; 1727978SPeter.Dunlap@Sun.COM } 1737978SPeter.Dunlap@Sun.COM 1747978SPeter.Dunlap@Sun.COM /* 1757978SPeter.Dunlap@Sun.COM * idm_so_fini() 1767978SPeter.Dunlap@Sun.COM * Sockets transport teardown 1777978SPeter.Dunlap@Sun.COM */ 1787978SPeter.Dunlap@Sun.COM void 1797978SPeter.Dunlap@Sun.COM idm_so_fini(void) 1807978SPeter.Dunlap@Sun.COM { 1817978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_sotx_pdu_cache); 1827978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_sorx_pdu_cache); 1837978SPeter.Dunlap@Sun.COM } 1847978SPeter.Dunlap@Sun.COM 1858348SEric.Yu@Sun.COM ksocket_t 1867978SPeter.Dunlap@Sun.COM idm_socreate(int domain, int type, int protocol) 1877978SPeter.Dunlap@Sun.COM { 1888348SEric.Yu@Sun.COM ksocket_t ks; 1897978SPeter.Dunlap@Sun.COM 1908348SEric.Yu@Sun.COM if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP, 1918348SEric.Yu@Sun.COM CRED())) { 1928348SEric.Yu@Sun.COM return (ks); 1938348SEric.Yu@Sun.COM } else { 1948348SEric.Yu@Sun.COM return (NULL); 1957978SPeter.Dunlap@Sun.COM } 1967978SPeter.Dunlap@Sun.COM } 1977978SPeter.Dunlap@Sun.COM 1987978SPeter.Dunlap@Sun.COM /* 1997978SPeter.Dunlap@Sun.COM * idm_soshutdown will disconnect the socket and prevent subsequent PDU 2007978SPeter.Dunlap@Sun.COM * reception and transmission. The sonode still exists but its state 2017978SPeter.Dunlap@Sun.COM * gets modified to indicate it is no longer connected. Calls to 2027978SPeter.Dunlap@Sun.COM * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used 2037978SPeter.Dunlap@Sun.COM * regain control of a thread stuck in idm_sorecv. 2047978SPeter.Dunlap@Sun.COM */ 2057978SPeter.Dunlap@Sun.COM void 2068348SEric.Yu@Sun.COM idm_soshutdown(ksocket_t so) 2077978SPeter.Dunlap@Sun.COM { 2088348SEric.Yu@Sun.COM (void) ksocket_shutdown(so, SHUT_RDWR, CRED()); 2097978SPeter.Dunlap@Sun.COM } 2107978SPeter.Dunlap@Sun.COM 2117978SPeter.Dunlap@Sun.COM /* 2127978SPeter.Dunlap@Sun.COM * idm_sodestroy releases all resources associated with a socket previously 2137978SPeter.Dunlap@Sun.COM * created with idm_socreate. The socket must be shutdown using 2147978SPeter.Dunlap@Sun.COM * idm_soshutdown before the socket is destroyed with idm_sodestroy, 2157978SPeter.Dunlap@Sun.COM * otherwise undefined behavior will result. 2167978SPeter.Dunlap@Sun.COM */ 2177978SPeter.Dunlap@Sun.COM void 2188348SEric.Yu@Sun.COM idm_sodestroy(ksocket_t ks) 2197978SPeter.Dunlap@Sun.COM { 2208348SEric.Yu@Sun.COM (void) ksocket_close(ks, CRED()); 2217978SPeter.Dunlap@Sun.COM } 2227978SPeter.Dunlap@Sun.COM 2237978SPeter.Dunlap@Sun.COM /* 2247978SPeter.Dunlap@Sun.COM * IP address filter functions to flag addresses that should not 2257978SPeter.Dunlap@Sun.COM * go out to initiators through discovery. 2267978SPeter.Dunlap@Sun.COM */ 2277978SPeter.Dunlap@Sun.COM static boolean_t 2287978SPeter.Dunlap@Sun.COM idm_v4_addr_okay(struct in_addr *in_addr) 2297978SPeter.Dunlap@Sun.COM { 2307978SPeter.Dunlap@Sun.COM in_addr_t addr = ntohl(in_addr->s_addr); 2317978SPeter.Dunlap@Sun.COM 2327978SPeter.Dunlap@Sun.COM if ((INADDR_NONE == addr) || 2337978SPeter.Dunlap@Sun.COM (IN_MULTICAST(addr)) || 2347978SPeter.Dunlap@Sun.COM ((addr >> IN_CLASSA_NSHIFT) == 0) || 2357978SPeter.Dunlap@Sun.COM ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) { 2367978SPeter.Dunlap@Sun.COM return (B_FALSE); 2377978SPeter.Dunlap@Sun.COM } 2387978SPeter.Dunlap@Sun.COM return (B_TRUE); 2397978SPeter.Dunlap@Sun.COM } 2407978SPeter.Dunlap@Sun.COM 2417978SPeter.Dunlap@Sun.COM static boolean_t 2427978SPeter.Dunlap@Sun.COM idm_v6_addr_okay(struct in6_addr *addr6) 2437978SPeter.Dunlap@Sun.COM { 2447978SPeter.Dunlap@Sun.COM 2457978SPeter.Dunlap@Sun.COM if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) || 2467978SPeter.Dunlap@Sun.COM (IN6_IS_ADDR_LOOPBACK(addr6)) || 2477978SPeter.Dunlap@Sun.COM (IN6_IS_ADDR_MULTICAST(addr6)) || 2487978SPeter.Dunlap@Sun.COM (IN6_IS_ADDR_V4MAPPED(addr6)) || 2497978SPeter.Dunlap@Sun.COM (IN6_IS_ADDR_V4COMPAT(addr6)) || 2507978SPeter.Dunlap@Sun.COM (IN6_IS_ADDR_LINKLOCAL(addr6))) { 2517978SPeter.Dunlap@Sun.COM return (B_FALSE); 2527978SPeter.Dunlap@Sun.COM } 2537978SPeter.Dunlap@Sun.COM return (B_TRUE); 2547978SPeter.Dunlap@Sun.COM } 2557978SPeter.Dunlap@Sun.COM 2567978SPeter.Dunlap@Sun.COM /* 2577978SPeter.Dunlap@Sun.COM * idm_get_ipaddr will retrieve a list of IP Addresses which the host is 2587978SPeter.Dunlap@Sun.COM * configured with by sending down a sequence of kernel ioctl to IP STREAMS. 2597978SPeter.Dunlap@Sun.COM */ 2607978SPeter.Dunlap@Sun.COM int 2617978SPeter.Dunlap@Sun.COM idm_get_ipaddr(idm_addr_list_t **ipaddr_p) 2627978SPeter.Dunlap@Sun.COM { 2638348SEric.Yu@Sun.COM ksocket_t so4, so6; 2647978SPeter.Dunlap@Sun.COM struct lifnum lifn; 2657978SPeter.Dunlap@Sun.COM struct lifconf lifc; 2667978SPeter.Dunlap@Sun.COM struct lifreq *lp; 2677978SPeter.Dunlap@Sun.COM int rval; 2687978SPeter.Dunlap@Sun.COM int numifs; 2697978SPeter.Dunlap@Sun.COM int bufsize; 2707978SPeter.Dunlap@Sun.COM void *buf; 2717978SPeter.Dunlap@Sun.COM int i, j, n, rc; 2727978SPeter.Dunlap@Sun.COM struct sockaddr_storage ss; 2737978SPeter.Dunlap@Sun.COM struct sockaddr_in *sin; 2747978SPeter.Dunlap@Sun.COM struct sockaddr_in6 *sin6; 2757978SPeter.Dunlap@Sun.COM idm_addr_t *ip; 2767978SPeter.Dunlap@Sun.COM idm_addr_list_t *ipaddr; 2777978SPeter.Dunlap@Sun.COM int size_ipaddr; 2787978SPeter.Dunlap@Sun.COM 2797978SPeter.Dunlap@Sun.COM *ipaddr_p = NULL; 2807978SPeter.Dunlap@Sun.COM size_ipaddr = 0; 2817978SPeter.Dunlap@Sun.COM buf = NULL; 2827978SPeter.Dunlap@Sun.COM 2837978SPeter.Dunlap@Sun.COM /* create an ipv4 and ipv6 UDP socket */ 2847978SPeter.Dunlap@Sun.COM if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL) 2857978SPeter.Dunlap@Sun.COM return (0); 2867978SPeter.Dunlap@Sun.COM if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) { 2877978SPeter.Dunlap@Sun.COM idm_sodestroy(so6); 2887978SPeter.Dunlap@Sun.COM return (0); 2897978SPeter.Dunlap@Sun.COM } 2907978SPeter.Dunlap@Sun.COM 2917978SPeter.Dunlap@Sun.COM 2927978SPeter.Dunlap@Sun.COM retry_count: 2937978SPeter.Dunlap@Sun.COM /* snapshot the current number of interfaces */ 2947978SPeter.Dunlap@Sun.COM lifn.lifn_family = PF_UNSPEC; 2957978SPeter.Dunlap@Sun.COM lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 2967978SPeter.Dunlap@Sun.COM lifn.lifn_count = 0; 2978348SEric.Yu@Sun.COM /* use vp6 for ioctls with unspecified families by default */ 2988348SEric.Yu@Sun.COM if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED()) 2998348SEric.Yu@Sun.COM != 0) { 3007978SPeter.Dunlap@Sun.COM goto cleanup; 3017978SPeter.Dunlap@Sun.COM } 3027978SPeter.Dunlap@Sun.COM 3037978SPeter.Dunlap@Sun.COM numifs = lifn.lifn_count; 3047978SPeter.Dunlap@Sun.COM if (numifs <= 0) { 3057978SPeter.Dunlap@Sun.COM goto cleanup; 3067978SPeter.Dunlap@Sun.COM } 3077978SPeter.Dunlap@Sun.COM 3087978SPeter.Dunlap@Sun.COM /* allocate extra room in case more interfaces appear */ 3097978SPeter.Dunlap@Sun.COM numifs += 10; 3107978SPeter.Dunlap@Sun.COM 3117978SPeter.Dunlap@Sun.COM /* get the interface names and ip addresses */ 3127978SPeter.Dunlap@Sun.COM bufsize = numifs * sizeof (struct lifreq); 3137978SPeter.Dunlap@Sun.COM buf = kmem_alloc(bufsize, KM_SLEEP); 3147978SPeter.Dunlap@Sun.COM 3157978SPeter.Dunlap@Sun.COM lifc.lifc_family = AF_UNSPEC; 3167978SPeter.Dunlap@Sun.COM lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 3177978SPeter.Dunlap@Sun.COM lifc.lifc_len = bufsize; 3187978SPeter.Dunlap@Sun.COM lifc.lifc_buf = buf; 3198348SEric.Yu@Sun.COM rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED()); 3207978SPeter.Dunlap@Sun.COM if (rc != 0) { 3217978SPeter.Dunlap@Sun.COM goto cleanup; 3227978SPeter.Dunlap@Sun.COM } 3237978SPeter.Dunlap@Sun.COM /* if our extra room is used up, try again */ 3247978SPeter.Dunlap@Sun.COM if (bufsize <= lifc.lifc_len) { 3257978SPeter.Dunlap@Sun.COM kmem_free(buf, bufsize); 3267978SPeter.Dunlap@Sun.COM buf = NULL; 3277978SPeter.Dunlap@Sun.COM goto retry_count; 3287978SPeter.Dunlap@Sun.COM } 3297978SPeter.Dunlap@Sun.COM /* calc actual number of ifconfs */ 3307978SPeter.Dunlap@Sun.COM n = lifc.lifc_len / sizeof (struct lifreq); 3317978SPeter.Dunlap@Sun.COM 3327978SPeter.Dunlap@Sun.COM /* get ip address */ 3337978SPeter.Dunlap@Sun.COM if (n > 0) { 3347978SPeter.Dunlap@Sun.COM size_ipaddr = sizeof (idm_addr_list_t) + 3357978SPeter.Dunlap@Sun.COM (n - 1) * sizeof (idm_addr_t); 3367978SPeter.Dunlap@Sun.COM ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP); 3377978SPeter.Dunlap@Sun.COM } else { 3387978SPeter.Dunlap@Sun.COM goto cleanup; 3397978SPeter.Dunlap@Sun.COM } 3407978SPeter.Dunlap@Sun.COM 3417978SPeter.Dunlap@Sun.COM /* 3427978SPeter.Dunlap@Sun.COM * Examine the array of interfaces and filter uninteresting ones 3437978SPeter.Dunlap@Sun.COM */ 3447978SPeter.Dunlap@Sun.COM for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) { 3457978SPeter.Dunlap@Sun.COM 3467978SPeter.Dunlap@Sun.COM /* 3477978SPeter.Dunlap@Sun.COM * Copy the address as the SIOCGLIFFLAGS ioctl is destructive 3487978SPeter.Dunlap@Sun.COM */ 3497978SPeter.Dunlap@Sun.COM ss = lp->lifr_addr; 3507978SPeter.Dunlap@Sun.COM /* 3517978SPeter.Dunlap@Sun.COM * fetch the flags using the socket of the correct family 3527978SPeter.Dunlap@Sun.COM */ 3537978SPeter.Dunlap@Sun.COM switch (ss.ss_family) { 3547978SPeter.Dunlap@Sun.COM case AF_INET: 3558348SEric.Yu@Sun.COM rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp, 3568348SEric.Yu@Sun.COM &rval, CRED()); 3577978SPeter.Dunlap@Sun.COM break; 3587978SPeter.Dunlap@Sun.COM case AF_INET6: 3598348SEric.Yu@Sun.COM rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp, 3608348SEric.Yu@Sun.COM &rval, CRED()); 3617978SPeter.Dunlap@Sun.COM break; 3627978SPeter.Dunlap@Sun.COM default: 3637978SPeter.Dunlap@Sun.COM continue; 3647978SPeter.Dunlap@Sun.COM } 3657978SPeter.Dunlap@Sun.COM if (rc == 0) { 3667978SPeter.Dunlap@Sun.COM /* 3677978SPeter.Dunlap@Sun.COM * If we got the flags, skip uninteresting 3687978SPeter.Dunlap@Sun.COM * interfaces based on flags 3697978SPeter.Dunlap@Sun.COM */ 3707978SPeter.Dunlap@Sun.COM if ((lp->lifr_flags & IFF_UP) != IFF_UP) 3717978SPeter.Dunlap@Sun.COM continue; 3727978SPeter.Dunlap@Sun.COM if (lp->lifr_flags & 3737978SPeter.Dunlap@Sun.COM (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED)) 3747978SPeter.Dunlap@Sun.COM continue; 3757978SPeter.Dunlap@Sun.COM } 3767978SPeter.Dunlap@Sun.COM 3777978SPeter.Dunlap@Sun.COM /* save ip address */ 3787978SPeter.Dunlap@Sun.COM ip = &ipaddr->al_addrs[j]; 3797978SPeter.Dunlap@Sun.COM switch (ss.ss_family) { 3807978SPeter.Dunlap@Sun.COM case AF_INET: 3817978SPeter.Dunlap@Sun.COM sin = (struct sockaddr_in *)&ss; 3827978SPeter.Dunlap@Sun.COM if (!idm_v4_addr_okay(&sin->sin_addr)) 3837978SPeter.Dunlap@Sun.COM continue; 3847978SPeter.Dunlap@Sun.COM ip->a_addr.i_addr.in4 = sin->sin_addr; 3857978SPeter.Dunlap@Sun.COM ip->a_addr.i_insize = sizeof (struct in_addr); 3867978SPeter.Dunlap@Sun.COM break; 3877978SPeter.Dunlap@Sun.COM case AF_INET6: 3887978SPeter.Dunlap@Sun.COM sin6 = (struct sockaddr_in6 *)&ss; 3897978SPeter.Dunlap@Sun.COM if (!idm_v6_addr_okay(&sin6->sin6_addr)) 3907978SPeter.Dunlap@Sun.COM continue; 3917978SPeter.Dunlap@Sun.COM ip->a_addr.i_addr.in6 = sin6->sin6_addr; 3927978SPeter.Dunlap@Sun.COM ip->a_addr.i_insize = sizeof (struct in6_addr); 3937978SPeter.Dunlap@Sun.COM break; 3947978SPeter.Dunlap@Sun.COM default: 3957978SPeter.Dunlap@Sun.COM continue; 3967978SPeter.Dunlap@Sun.COM } 3977978SPeter.Dunlap@Sun.COM j++; 3987978SPeter.Dunlap@Sun.COM } 3997978SPeter.Dunlap@Sun.COM 4007978SPeter.Dunlap@Sun.COM if (j == 0) { 4017978SPeter.Dunlap@Sun.COM /* no valid ifaddr */ 4027978SPeter.Dunlap@Sun.COM kmem_free(ipaddr, size_ipaddr); 4037978SPeter.Dunlap@Sun.COM size_ipaddr = 0; 4047978SPeter.Dunlap@Sun.COM ipaddr = NULL; 4057978SPeter.Dunlap@Sun.COM } else { 4067978SPeter.Dunlap@Sun.COM ipaddr->al_out_cnt = j; 4077978SPeter.Dunlap@Sun.COM } 4087978SPeter.Dunlap@Sun.COM 4097978SPeter.Dunlap@Sun.COM 4107978SPeter.Dunlap@Sun.COM cleanup: 4117978SPeter.Dunlap@Sun.COM idm_sodestroy(so6); 4127978SPeter.Dunlap@Sun.COM idm_sodestroy(so4); 4137978SPeter.Dunlap@Sun.COM 4147978SPeter.Dunlap@Sun.COM if (buf != NULL) 4157978SPeter.Dunlap@Sun.COM kmem_free(buf, bufsize); 4167978SPeter.Dunlap@Sun.COM 4177978SPeter.Dunlap@Sun.COM *ipaddr_p = ipaddr; 4187978SPeter.Dunlap@Sun.COM return (size_ipaddr); 4197978SPeter.Dunlap@Sun.COM } 4207978SPeter.Dunlap@Sun.COM 4217978SPeter.Dunlap@Sun.COM int 4228348SEric.Yu@Sun.COM idm_sorecv(ksocket_t so, void *msg, size_t len) 4237978SPeter.Dunlap@Sun.COM { 4247978SPeter.Dunlap@Sun.COM iovec_t iov; 4257978SPeter.Dunlap@Sun.COM 4267978SPeter.Dunlap@Sun.COM ASSERT(so != NULL); 4277978SPeter.Dunlap@Sun.COM ASSERT(len != 0); 4287978SPeter.Dunlap@Sun.COM 4297978SPeter.Dunlap@Sun.COM /* 4307978SPeter.Dunlap@Sun.COM * Fill in iovec and receive data 4317978SPeter.Dunlap@Sun.COM */ 4327978SPeter.Dunlap@Sun.COM iov.iov_base = msg; 4337978SPeter.Dunlap@Sun.COM iov.iov_len = len; 4347978SPeter.Dunlap@Sun.COM 4357978SPeter.Dunlap@Sun.COM return (idm_iov_sorecv(so, &iov, 1, len)); 4367978SPeter.Dunlap@Sun.COM } 4377978SPeter.Dunlap@Sun.COM 4387978SPeter.Dunlap@Sun.COM /* 4397978SPeter.Dunlap@Sun.COM * idm_sosendto - Sends a buffered data on a non-connected socket. 4407978SPeter.Dunlap@Sun.COM * 4417978SPeter.Dunlap@Sun.COM * This function puts the data provided on the wire by calling sosendmsg. 4427978SPeter.Dunlap@Sun.COM * It will return only when all the data has been sent or if an error 4437978SPeter.Dunlap@Sun.COM * occurs. 4447978SPeter.Dunlap@Sun.COM * 4457978SPeter.Dunlap@Sun.COM * Returns 0 for success, the socket errno value if sosendmsg fails, and 4467978SPeter.Dunlap@Sun.COM * -1 if sosendmsg returns success but uio_resid != 0 4477978SPeter.Dunlap@Sun.COM */ 4487978SPeter.Dunlap@Sun.COM int 4498348SEric.Yu@Sun.COM idm_sosendto(ksocket_t so, void *buff, size_t len, 4507978SPeter.Dunlap@Sun.COM struct sockaddr *name, socklen_t namelen) 4517978SPeter.Dunlap@Sun.COM { 4527978SPeter.Dunlap@Sun.COM struct msghdr msg; 4537978SPeter.Dunlap@Sun.COM struct iovec iov[1]; 4547978SPeter.Dunlap@Sun.COM int error; 4558348SEric.Yu@Sun.COM size_t sent = 0; 4567978SPeter.Dunlap@Sun.COM 4577978SPeter.Dunlap@Sun.COM iov[0].iov_base = buff; 4587978SPeter.Dunlap@Sun.COM iov[0].iov_len = len; 4597978SPeter.Dunlap@Sun.COM 4607978SPeter.Dunlap@Sun.COM /* Initialization of the message header. */ 4617978SPeter.Dunlap@Sun.COM bzero(&msg, sizeof (msg)); 4627978SPeter.Dunlap@Sun.COM msg.msg_iov = iov; 4637978SPeter.Dunlap@Sun.COM msg.msg_iovlen = 1; 4647978SPeter.Dunlap@Sun.COM msg.msg_name = name; 4657978SPeter.Dunlap@Sun.COM msg.msg_namelen = namelen; 4667978SPeter.Dunlap@Sun.COM 4678348SEric.Yu@Sun.COM if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) { 4687978SPeter.Dunlap@Sun.COM /* Data sent */ 4698348SEric.Yu@Sun.COM if (sent == len) { 4707978SPeter.Dunlap@Sun.COM /* All data sent. Success. */ 4717978SPeter.Dunlap@Sun.COM return (0); 4727978SPeter.Dunlap@Sun.COM } else { 4737978SPeter.Dunlap@Sun.COM /* Not all data was sent. Failure */ 4747978SPeter.Dunlap@Sun.COM return (-1); 4757978SPeter.Dunlap@Sun.COM } 4767978SPeter.Dunlap@Sun.COM } 4777978SPeter.Dunlap@Sun.COM 4787978SPeter.Dunlap@Sun.COM /* Send failed */ 4797978SPeter.Dunlap@Sun.COM return (error); 4807978SPeter.Dunlap@Sun.COM } 4817978SPeter.Dunlap@Sun.COM 4827978SPeter.Dunlap@Sun.COM /* 4837978SPeter.Dunlap@Sun.COM * idm_iov_sosend - Sends an iovec on a connection. 4847978SPeter.Dunlap@Sun.COM * 4857978SPeter.Dunlap@Sun.COM * This function puts the data provided on the wire by calling sosendmsg. 4867978SPeter.Dunlap@Sun.COM * It will return only when all the data has been sent or if an error 4877978SPeter.Dunlap@Sun.COM * occurs. 4887978SPeter.Dunlap@Sun.COM * 4897978SPeter.Dunlap@Sun.COM * Returns 0 for success, the socket errno value if sosendmsg fails, and 4907978SPeter.Dunlap@Sun.COM * -1 if sosendmsg returns success but uio_resid != 0 4917978SPeter.Dunlap@Sun.COM */ 4927978SPeter.Dunlap@Sun.COM int 4938348SEric.Yu@Sun.COM idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len) 4947978SPeter.Dunlap@Sun.COM { 4957978SPeter.Dunlap@Sun.COM struct msghdr msg; 4967978SPeter.Dunlap@Sun.COM int error; 4978348SEric.Yu@Sun.COM size_t sent = 0; 4987978SPeter.Dunlap@Sun.COM 4997978SPeter.Dunlap@Sun.COM ASSERT(iop != NULL); 5007978SPeter.Dunlap@Sun.COM 5017978SPeter.Dunlap@Sun.COM /* Initialization of the message header. */ 5027978SPeter.Dunlap@Sun.COM bzero(&msg, sizeof (msg)); 5037978SPeter.Dunlap@Sun.COM msg.msg_iov = iop; 5047978SPeter.Dunlap@Sun.COM msg.msg_iovlen = iovlen; 5057978SPeter.Dunlap@Sun.COM 5068348SEric.Yu@Sun.COM if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) 5078348SEric.Yu@Sun.COM == 0) { 5087978SPeter.Dunlap@Sun.COM /* Data sent */ 5098348SEric.Yu@Sun.COM if (sent == total_len) { 5107978SPeter.Dunlap@Sun.COM /* All data sent. Success. */ 5117978SPeter.Dunlap@Sun.COM return (0); 5127978SPeter.Dunlap@Sun.COM } else { 5137978SPeter.Dunlap@Sun.COM /* Not all data was sent. Failure */ 5147978SPeter.Dunlap@Sun.COM return (-1); 5157978SPeter.Dunlap@Sun.COM } 5167978SPeter.Dunlap@Sun.COM } 5177978SPeter.Dunlap@Sun.COM 5187978SPeter.Dunlap@Sun.COM /* Send failed */ 5197978SPeter.Dunlap@Sun.COM return (error); 5207978SPeter.Dunlap@Sun.COM } 5217978SPeter.Dunlap@Sun.COM 5227978SPeter.Dunlap@Sun.COM /* 5237978SPeter.Dunlap@Sun.COM * idm_iov_sorecv - Receives an iovec from a connection 5247978SPeter.Dunlap@Sun.COM * 5257978SPeter.Dunlap@Sun.COM * This function gets the data asked for from the socket. It will return 5267978SPeter.Dunlap@Sun.COM * only when all the requested data has been retrieved or if an error 5277978SPeter.Dunlap@Sun.COM * occurs. 5287978SPeter.Dunlap@Sun.COM * 5297978SPeter.Dunlap@Sun.COM * Returns 0 for success, the socket errno value if sorecvmsg fails, and 5307978SPeter.Dunlap@Sun.COM * -1 if sorecvmsg returns success but uio_resid != 0 5317978SPeter.Dunlap@Sun.COM */ 5327978SPeter.Dunlap@Sun.COM int 5338348SEric.Yu@Sun.COM idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len) 5347978SPeter.Dunlap@Sun.COM { 5357978SPeter.Dunlap@Sun.COM struct msghdr msg; 5367978SPeter.Dunlap@Sun.COM int error; 5378348SEric.Yu@Sun.COM size_t recv; 5388348SEric.Yu@Sun.COM int flags; 5397978SPeter.Dunlap@Sun.COM 5407978SPeter.Dunlap@Sun.COM ASSERT(iop != NULL); 5417978SPeter.Dunlap@Sun.COM 5427978SPeter.Dunlap@Sun.COM /* Initialization of the message header. */ 5437978SPeter.Dunlap@Sun.COM bzero(&msg, sizeof (msg)); 5447978SPeter.Dunlap@Sun.COM msg.msg_iov = iop; 5457978SPeter.Dunlap@Sun.COM msg.msg_iovlen = iovlen; 5468348SEric.Yu@Sun.COM flags = MSG_WAITALL; 5477978SPeter.Dunlap@Sun.COM 5488348SEric.Yu@Sun.COM if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED())) 5498348SEric.Yu@Sun.COM == 0) { 5507978SPeter.Dunlap@Sun.COM /* Received data */ 5518348SEric.Yu@Sun.COM if (recv == total_len) { 5527978SPeter.Dunlap@Sun.COM /* All requested data received. Success */ 5537978SPeter.Dunlap@Sun.COM return (0); 5547978SPeter.Dunlap@Sun.COM } else { 5557978SPeter.Dunlap@Sun.COM /* 5567978SPeter.Dunlap@Sun.COM * Not all data was received. The connection has 5577978SPeter.Dunlap@Sun.COM * probably failed. 5587978SPeter.Dunlap@Sun.COM */ 5597978SPeter.Dunlap@Sun.COM return (-1); 5607978SPeter.Dunlap@Sun.COM } 5617978SPeter.Dunlap@Sun.COM } 5627978SPeter.Dunlap@Sun.COM 5637978SPeter.Dunlap@Sun.COM /* Receive failed */ 5647978SPeter.Dunlap@Sun.COM return (error); 5657978SPeter.Dunlap@Sun.COM } 5667978SPeter.Dunlap@Sun.COM 5677978SPeter.Dunlap@Sun.COM static void 5687978SPeter.Dunlap@Sun.COM idm_set_ini_preconnect_options(idm_so_conn_t *sc) 5697978SPeter.Dunlap@Sun.COM { 5707978SPeter.Dunlap@Sun.COM int conn_abort = 10000; 5717978SPeter.Dunlap@Sun.COM int conn_notify = 2000; 5727978SPeter.Dunlap@Sun.COM int abort = 30000; 5737978SPeter.Dunlap@Sun.COM 5747978SPeter.Dunlap@Sun.COM /* Pre-connect socket options */ 5758348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, 5768348SEric.Yu@Sun.COM TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int), 5778348SEric.Yu@Sun.COM CRED()); 5788348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, 5798348SEric.Yu@Sun.COM TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int), 5808348SEric.Yu@Sun.COM CRED()); 5818348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD, 5828348SEric.Yu@Sun.COM (char *)&abort, sizeof (int), CRED()); 5837978SPeter.Dunlap@Sun.COM } 5847978SPeter.Dunlap@Sun.COM 5857978SPeter.Dunlap@Sun.COM static void 5867978SPeter.Dunlap@Sun.COM idm_set_ini_postconnect_options(idm_so_conn_t *sc) 5877978SPeter.Dunlap@Sun.COM { 5887978SPeter.Dunlap@Sun.COM int32_t rcvbuf = IDM_RCVBUF_SIZE; 5897978SPeter.Dunlap@Sun.COM int32_t sndbuf = IDM_SNDBUF_SIZE; 5907978SPeter.Dunlap@Sun.COM const int on = 1; 5917978SPeter.Dunlap@Sun.COM 5927978SPeter.Dunlap@Sun.COM /* Set postconnect options */ 5938348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY, 5948348SEric.Yu@Sun.COM (char *)&on, sizeof (int), CRED()); 5958348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF, 5968348SEric.Yu@Sun.COM (char *)&rcvbuf, sizeof (int), CRED()); 5978348SEric.Yu@Sun.COM (void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF, 5988348SEric.Yu@Sun.COM (char *)&sndbuf, sizeof (int), CRED()); 5997978SPeter.Dunlap@Sun.COM } 6007978SPeter.Dunlap@Sun.COM 6017978SPeter.Dunlap@Sun.COM static void 6028348SEric.Yu@Sun.COM idm_set_tgt_connect_options(ksocket_t ks) 6037978SPeter.Dunlap@Sun.COM { 6047978SPeter.Dunlap@Sun.COM int32_t rcvbuf = IDM_RCVBUF_SIZE; 6057978SPeter.Dunlap@Sun.COM int32_t sndbuf = IDM_SNDBUF_SIZE; 6067978SPeter.Dunlap@Sun.COM const int on = 1; 6077978SPeter.Dunlap@Sun.COM 6087978SPeter.Dunlap@Sun.COM /* Set connect options */ 6098348SEric.Yu@Sun.COM (void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF, 6108348SEric.Yu@Sun.COM (char *)&rcvbuf, sizeof (int), CRED()); 6118348SEric.Yu@Sun.COM (void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF, 6128348SEric.Yu@Sun.COM (char *)&sndbuf, sizeof (int), CRED()); 6138348SEric.Yu@Sun.COM (void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY, 6148348SEric.Yu@Sun.COM (char *)&on, sizeof (on), CRED()); 6157978SPeter.Dunlap@Sun.COM } 6167978SPeter.Dunlap@Sun.COM 6177978SPeter.Dunlap@Sun.COM static uint32_t 6187978SPeter.Dunlap@Sun.COM n2h24(const uchar_t *ptr) 6197978SPeter.Dunlap@Sun.COM { 6207978SPeter.Dunlap@Sun.COM return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]); 6217978SPeter.Dunlap@Sun.COM } 6227978SPeter.Dunlap@Sun.COM 6237978SPeter.Dunlap@Sun.COM 6247978SPeter.Dunlap@Sun.COM static idm_status_t 6257978SPeter.Dunlap@Sun.COM idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu) 6267978SPeter.Dunlap@Sun.COM { 6277978SPeter.Dunlap@Sun.COM iscsi_hdr_t *bhs; 6287978SPeter.Dunlap@Sun.COM uint32_t hdr_digest_crc; 6297978SPeter.Dunlap@Sun.COM uint32_t crc_calculated; 6307978SPeter.Dunlap@Sun.COM void *new_hdr; 6317978SPeter.Dunlap@Sun.COM int ahslen = 0; 6327978SPeter.Dunlap@Sun.COM int total_len = 0; 6337978SPeter.Dunlap@Sun.COM int iovlen = 0; 6347978SPeter.Dunlap@Sun.COM struct iovec iov[2]; 6357978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 6367978SPeter.Dunlap@Sun.COM int rc; 6377978SPeter.Dunlap@Sun.COM 6387978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 6397978SPeter.Dunlap@Sun.COM 6407978SPeter.Dunlap@Sun.COM /* 6417978SPeter.Dunlap@Sun.COM * Read BHS 6427978SPeter.Dunlap@Sun.COM */ 6437978SPeter.Dunlap@Sun.COM bhs = pdu->isp_hdr; 6447978SPeter.Dunlap@Sun.COM rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t)); 6457978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 6467978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 6477978SPeter.Dunlap@Sun.COM } 6487978SPeter.Dunlap@Sun.COM 6497978SPeter.Dunlap@Sun.COM /* 6507978SPeter.Dunlap@Sun.COM * Check actual AHS length against the amount available in the buffer 6517978SPeter.Dunlap@Sun.COM */ 6527978SPeter.Dunlap@Sun.COM pdu->isp_hdrlen = sizeof (iscsi_hdr_t) + 6537978SPeter.Dunlap@Sun.COM (bhs->hlength * sizeof (uint32_t)); 6547978SPeter.Dunlap@Sun.COM pdu->isp_datalen = n2h24(bhs->dlength); 6557978SPeter.Dunlap@Sun.COM if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) { 6567978SPeter.Dunlap@Sun.COM /* Allocate a new header segment and change the callback */ 6577978SPeter.Dunlap@Sun.COM new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP); 6587978SPeter.Dunlap@Sun.COM bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t)); 6597978SPeter.Dunlap@Sun.COM pdu->isp_hdr = new_hdr; 6607978SPeter.Dunlap@Sun.COM pdu->isp_flags |= IDM_PDU_ADDL_HDR; 6617978SPeter.Dunlap@Sun.COM 6627978SPeter.Dunlap@Sun.COM /* 6637978SPeter.Dunlap@Sun.COM * This callback will restore the expected values after 6647978SPeter.Dunlap@Sun.COM * the RX PDU has been processed. 6657978SPeter.Dunlap@Sun.COM */ 6667978SPeter.Dunlap@Sun.COM pdu->isp_callback = idm_sorx_addl_pdu_cb; 6677978SPeter.Dunlap@Sun.COM } 6687978SPeter.Dunlap@Sun.COM 6697978SPeter.Dunlap@Sun.COM /* 6707978SPeter.Dunlap@Sun.COM * Setup receipt of additional header and header digest (if enabled). 6717978SPeter.Dunlap@Sun.COM */ 6727978SPeter.Dunlap@Sun.COM if (bhs->hlength > 0) { 6737978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1); 6747978SPeter.Dunlap@Sun.COM ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t); 6757978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = ahslen; 6767978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 6777978SPeter.Dunlap@Sun.COM iovlen++; 6787978SPeter.Dunlap@Sun.COM } 6797978SPeter.Dunlap@Sun.COM 6807978SPeter.Dunlap@Sun.COM if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) { 6817978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc; 6827978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = sizeof (hdr_digest_crc); 6837978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 6847978SPeter.Dunlap@Sun.COM iovlen++; 6857978SPeter.Dunlap@Sun.COM } 6867978SPeter.Dunlap@Sun.COM 6877978SPeter.Dunlap@Sun.COM if ((iovlen != 0) && 6887978SPeter.Dunlap@Sun.COM (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen, 6897978SPeter.Dunlap@Sun.COM total_len) != 0)) { 6907978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 6917978SPeter.Dunlap@Sun.COM } 6927978SPeter.Dunlap@Sun.COM 6937978SPeter.Dunlap@Sun.COM /* 6947978SPeter.Dunlap@Sun.COM * Validate header digest if enabled 6957978SPeter.Dunlap@Sun.COM */ 6967978SPeter.Dunlap@Sun.COM if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) { 6977978SPeter.Dunlap@Sun.COM crc_calculated = idm_crc32c(pdu->isp_hdr, 6987978SPeter.Dunlap@Sun.COM sizeof (iscsi_hdr_t) + ahslen); 6997978SPeter.Dunlap@Sun.COM if (crc_calculated != hdr_digest_crc) { 7007978SPeter.Dunlap@Sun.COM /* Invalid Header Digest */ 7017978SPeter.Dunlap@Sun.COM return (IDM_STATUS_HEADER_DIGEST); 7027978SPeter.Dunlap@Sun.COM } 7037978SPeter.Dunlap@Sun.COM } 7047978SPeter.Dunlap@Sun.COM 7057978SPeter.Dunlap@Sun.COM return (0); 7067978SPeter.Dunlap@Sun.COM } 7077978SPeter.Dunlap@Sun.COM 7087978SPeter.Dunlap@Sun.COM /* 7097978SPeter.Dunlap@Sun.COM * idm_so_ini_conn_create() 7107978SPeter.Dunlap@Sun.COM * Allocate the sockets transport connection resources. 7117978SPeter.Dunlap@Sun.COM */ 7127978SPeter.Dunlap@Sun.COM static idm_status_t 7137978SPeter.Dunlap@Sun.COM idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic) 7147978SPeter.Dunlap@Sun.COM { 7158348SEric.Yu@Sun.COM ksocket_t so; 7167978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 7177978SPeter.Dunlap@Sun.COM idm_status_t idmrc; 7187978SPeter.Dunlap@Sun.COM 7197978SPeter.Dunlap@Sun.COM so = idm_socreate(cr->cr_domain, cr->cr_type, 7207978SPeter.Dunlap@Sun.COM cr->cr_protocol); 7217978SPeter.Dunlap@Sun.COM if (so == NULL) { 7227978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7237978SPeter.Dunlap@Sun.COM } 7247978SPeter.Dunlap@Sun.COM 7257978SPeter.Dunlap@Sun.COM /* Bind the socket if configured to do so */ 7267978SPeter.Dunlap@Sun.COM if (cr->cr_bound) { 7278348SEric.Yu@Sun.COM if (ksocket_bind(so, &cr->cr_bound_addr.sin, 7288348SEric.Yu@Sun.COM SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) { 7297978SPeter.Dunlap@Sun.COM idm_sodestroy(so); 7307978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7317978SPeter.Dunlap@Sun.COM } 7327978SPeter.Dunlap@Sun.COM } 7337978SPeter.Dunlap@Sun.COM 7347978SPeter.Dunlap@Sun.COM idmrc = idm_so_conn_create_common(ic, so); 7357978SPeter.Dunlap@Sun.COM if (idmrc != IDM_STATUS_SUCCESS) { 7367978SPeter.Dunlap@Sun.COM idm_soshutdown(so); 7377978SPeter.Dunlap@Sun.COM idm_sodestroy(so); 7387978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7397978SPeter.Dunlap@Sun.COM } 7407978SPeter.Dunlap@Sun.COM 7417978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 7427978SPeter.Dunlap@Sun.COM /* Set up socket options */ 7437978SPeter.Dunlap@Sun.COM idm_set_ini_preconnect_options(so_conn); 7447978SPeter.Dunlap@Sun.COM 7457978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 7467978SPeter.Dunlap@Sun.COM } 7477978SPeter.Dunlap@Sun.COM 7487978SPeter.Dunlap@Sun.COM /* 7497978SPeter.Dunlap@Sun.COM * idm_so_ini_conn_destroy() 7507978SPeter.Dunlap@Sun.COM * Tear down the sockets transport connection resources. 7517978SPeter.Dunlap@Sun.COM */ 7527978SPeter.Dunlap@Sun.COM static void 7537978SPeter.Dunlap@Sun.COM idm_so_ini_conn_destroy(idm_conn_t *ic) 7547978SPeter.Dunlap@Sun.COM { 7557978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(ic); 7567978SPeter.Dunlap@Sun.COM } 7577978SPeter.Dunlap@Sun.COM 7587978SPeter.Dunlap@Sun.COM /* 7597978SPeter.Dunlap@Sun.COM * idm_so_ini_conn_connect() 7607978SPeter.Dunlap@Sun.COM * Establish the connection referred to by the handle previously allocated via 7617978SPeter.Dunlap@Sun.COM * idm_so_ini_conn_create(). 7627978SPeter.Dunlap@Sun.COM */ 7637978SPeter.Dunlap@Sun.COM static idm_status_t 7647978SPeter.Dunlap@Sun.COM idm_so_ini_conn_connect(idm_conn_t *ic) 7657978SPeter.Dunlap@Sun.COM { 7667978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 7677978SPeter.Dunlap@Sun.COM 7687978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 7697978SPeter.Dunlap@Sun.COM 7708348SEric.Yu@Sun.COM if (ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin, 7718348SEric.Yu@Sun.COM (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED()) != 0) { 7727978SPeter.Dunlap@Sun.COM idm_soshutdown(so_conn->ic_so); 7737978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7747978SPeter.Dunlap@Sun.COM } 7757978SPeter.Dunlap@Sun.COM 7767978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(ic); 7777978SPeter.Dunlap@Sun.COM 7787978SPeter.Dunlap@Sun.COM idm_set_ini_postconnect_options(so_conn); 7797978SPeter.Dunlap@Sun.COM 7807978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 7817978SPeter.Dunlap@Sun.COM } 7827978SPeter.Dunlap@Sun.COM 7837978SPeter.Dunlap@Sun.COM idm_status_t 7848348SEric.Yu@Sun.COM idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so) 7857978SPeter.Dunlap@Sun.COM { 7867978SPeter.Dunlap@Sun.COM idm_status_t idmrc; 7877978SPeter.Dunlap@Sun.COM 7887978SPeter.Dunlap@Sun.COM idmrc = idm_so_conn_create_common(ic, new_so); 7897978SPeter.Dunlap@Sun.COM 7907978SPeter.Dunlap@Sun.COM return (idmrc); 7917978SPeter.Dunlap@Sun.COM } 7927978SPeter.Dunlap@Sun.COM 7937978SPeter.Dunlap@Sun.COM static void 7947978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_destroy(idm_conn_t *ic) 7957978SPeter.Dunlap@Sun.COM { 7967978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(ic); 7977978SPeter.Dunlap@Sun.COM } 7987978SPeter.Dunlap@Sun.COM 7997978SPeter.Dunlap@Sun.COM /* 8007978SPeter.Dunlap@Sun.COM * idm_so_tgt_conn_connect() 8017978SPeter.Dunlap@Sun.COM * Establish the connection in ic, passed from idm_tgt_conn_finish(), which 8027978SPeter.Dunlap@Sun.COM * is invoked from the SM as a result of an inbound connection request. 8037978SPeter.Dunlap@Sun.COM */ 8047978SPeter.Dunlap@Sun.COM static idm_status_t 8057978SPeter.Dunlap@Sun.COM idm_so_tgt_conn_connect(idm_conn_t *ic) 8067978SPeter.Dunlap@Sun.COM { 8077978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(ic); 8087978SPeter.Dunlap@Sun.COM 8097978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 8107978SPeter.Dunlap@Sun.COM } 8117978SPeter.Dunlap@Sun.COM 8127978SPeter.Dunlap@Sun.COM static idm_status_t 8138348SEric.Yu@Sun.COM idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so) 8147978SPeter.Dunlap@Sun.COM { 8157978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 8167978SPeter.Dunlap@Sun.COM 8177978SPeter.Dunlap@Sun.COM so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP); 8187978SPeter.Dunlap@Sun.COM so_conn->ic_so = new_so; 8197978SPeter.Dunlap@Sun.COM 8207978SPeter.Dunlap@Sun.COM ic->ic_transport_private = so_conn; 8217978SPeter.Dunlap@Sun.COM ic->ic_transport_hdrlen = 0; 8227978SPeter.Dunlap@Sun.COM 8237978SPeter.Dunlap@Sun.COM /* Set the scoreboarding flag on this connection */ 8247978SPeter.Dunlap@Sun.COM ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD; 8257978SPeter.Dunlap@Sun.COM 8267978SPeter.Dunlap@Sun.COM /* 8277978SPeter.Dunlap@Sun.COM * Initialize tx thread mutex and list 8287978SPeter.Dunlap@Sun.COM */ 8297978SPeter.Dunlap@Sun.COM mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL); 8307978SPeter.Dunlap@Sun.COM cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL); 8317978SPeter.Dunlap@Sun.COM list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t), 8327978SPeter.Dunlap@Sun.COM offsetof(idm_pdu_t, idm_tx_link)); 8337978SPeter.Dunlap@Sun.COM 8347978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 8357978SPeter.Dunlap@Sun.COM } 8367978SPeter.Dunlap@Sun.COM 8377978SPeter.Dunlap@Sun.COM static void 8387978SPeter.Dunlap@Sun.COM idm_so_conn_destroy_common(idm_conn_t *ic) 8397978SPeter.Dunlap@Sun.COM { 8407978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn = ic->ic_transport_private; 8417978SPeter.Dunlap@Sun.COM 8427978SPeter.Dunlap@Sun.COM ic->ic_transport_private = NULL; 8437978SPeter.Dunlap@Sun.COM idm_sodestroy(so_conn->ic_so); 8447978SPeter.Dunlap@Sun.COM list_destroy(&so_conn->ic_tx_list); 8457978SPeter.Dunlap@Sun.COM mutex_destroy(&so_conn->ic_tx_mutex); 8467978SPeter.Dunlap@Sun.COM cv_destroy(&so_conn->ic_tx_cv); 8477978SPeter.Dunlap@Sun.COM 8487978SPeter.Dunlap@Sun.COM kmem_free(so_conn, sizeof (idm_so_conn_t)); 8497978SPeter.Dunlap@Sun.COM } 8507978SPeter.Dunlap@Sun.COM 8517978SPeter.Dunlap@Sun.COM static void 8527978SPeter.Dunlap@Sun.COM idm_so_conn_connect_common(idm_conn_t *ic) 8537978SPeter.Dunlap@Sun.COM { 8547978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 8558348SEric.Yu@Sun.COM struct sockaddr_in6 t_addr; 8568348SEric.Yu@Sun.COM socklen_t t_addrlen = 0; 8577978SPeter.Dunlap@Sun.COM 8587978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 8598348SEric.Yu@Sun.COM bzero(&t_addr, sizeof (struct sockaddr_in6)); 8608348SEric.Yu@Sun.COM t_addrlen = sizeof (struct sockaddr_in6); 8617978SPeter.Dunlap@Sun.COM 8627978SPeter.Dunlap@Sun.COM /* Set the local and remote addresses in the idm conn handle */ 8638348SEric.Yu@Sun.COM ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr, 8648348SEric.Yu@Sun.COM &t_addrlen, CRED()); 8658348SEric.Yu@Sun.COM bcopy(&t_addr, &ic->ic_laddr, t_addrlen); 8668348SEric.Yu@Sun.COM ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr, 8678348SEric.Yu@Sun.COM &t_addrlen, CRED()); 8688348SEric.Yu@Sun.COM bcopy(&t_addr, &ic->ic_raddr, t_addrlen); 8697978SPeter.Dunlap@Sun.COM 8707978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 8717978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0, 8727978SPeter.Dunlap@Sun.COM &p0, TS_RUN, minclsyspri); 8737978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0, 8747978SPeter.Dunlap@Sun.COM &p0, TS_RUN, minclsyspri); 8757978SPeter.Dunlap@Sun.COM 8767978SPeter.Dunlap@Sun.COM while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running) 8777978SPeter.Dunlap@Sun.COM cv_wait(&ic->ic_cv, &ic->ic_mutex); 8787978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_mutex); 8797978SPeter.Dunlap@Sun.COM } 8807978SPeter.Dunlap@Sun.COM 8817978SPeter.Dunlap@Sun.COM /* 8827978SPeter.Dunlap@Sun.COM * idm_so_conn_disconnect() 8837978SPeter.Dunlap@Sun.COM * Shutdown the socket connection and stop the thread 8847978SPeter.Dunlap@Sun.COM */ 8857978SPeter.Dunlap@Sun.COM static void 8867978SPeter.Dunlap@Sun.COM idm_so_conn_disconnect(idm_conn_t *ic) 8877978SPeter.Dunlap@Sun.COM { 8887978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 8897978SPeter.Dunlap@Sun.COM 8907978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 8917978SPeter.Dunlap@Sun.COM 8927978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 8937978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread_running = B_FALSE; 8947978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread_running = B_FALSE; 8957978SPeter.Dunlap@Sun.COM /* We need to wakeup the TX thread */ 8967978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 8977978SPeter.Dunlap@Sun.COM cv_signal(&so_conn->ic_tx_cv); 8987978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 8997978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_mutex); 9007978SPeter.Dunlap@Sun.COM 9017978SPeter.Dunlap@Sun.COM /* This should wakeup the RX thread if it is sleeping */ 9027978SPeter.Dunlap@Sun.COM idm_soshutdown(so_conn->ic_so); 9037978SPeter.Dunlap@Sun.COM 9047978SPeter.Dunlap@Sun.COM thread_join(so_conn->ic_tx_thread_did); 9057978SPeter.Dunlap@Sun.COM thread_join(so_conn->ic_rx_thread_did); 9067978SPeter.Dunlap@Sun.COM } 9077978SPeter.Dunlap@Sun.COM 9087978SPeter.Dunlap@Sun.COM /* 9097978SPeter.Dunlap@Sun.COM * idm_so_tgt_svc_create() 9107978SPeter.Dunlap@Sun.COM * Establish a service on an IP address and port. idm_svc_req_t contains 9117978SPeter.Dunlap@Sun.COM * the service parameters. 9127978SPeter.Dunlap@Sun.COM */ 9137978SPeter.Dunlap@Sun.COM /*ARGSUSED*/ 9147978SPeter.Dunlap@Sun.COM static idm_status_t 9157978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is) 9167978SPeter.Dunlap@Sun.COM { 9177978SPeter.Dunlap@Sun.COM idm_so_svc_t *so_svc; 9187978SPeter.Dunlap@Sun.COM 9197978SPeter.Dunlap@Sun.COM so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP); 9207978SPeter.Dunlap@Sun.COM 9217978SPeter.Dunlap@Sun.COM /* Set the new sockets service in svc handle */ 9227978SPeter.Dunlap@Sun.COM is->is_so_svc = (void *)so_svc; 9237978SPeter.Dunlap@Sun.COM 9247978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 9257978SPeter.Dunlap@Sun.COM } 9267978SPeter.Dunlap@Sun.COM 9277978SPeter.Dunlap@Sun.COM /* 9287978SPeter.Dunlap@Sun.COM * idm_so_tgt_svc_destroy() 9297978SPeter.Dunlap@Sun.COM * Teardown sockets resources allocated in idm_so_tgt_svc_create() 9307978SPeter.Dunlap@Sun.COM */ 9317978SPeter.Dunlap@Sun.COM static void 9327978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_destroy(idm_svc_t *is) 9337978SPeter.Dunlap@Sun.COM { 9347978SPeter.Dunlap@Sun.COM /* the socket will have been torn down; free the service */ 9357978SPeter.Dunlap@Sun.COM kmem_free(is->is_so_svc, sizeof (idm_so_svc_t)); 9367978SPeter.Dunlap@Sun.COM } 9377978SPeter.Dunlap@Sun.COM 9387978SPeter.Dunlap@Sun.COM /* 9397978SPeter.Dunlap@Sun.COM * idm_so_tgt_svc_online() 9407978SPeter.Dunlap@Sun.COM * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create() 9417978SPeter.Dunlap@Sun.COM */ 9427978SPeter.Dunlap@Sun.COM 9437978SPeter.Dunlap@Sun.COM static idm_status_t 9447978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_online(idm_svc_t *is) 9457978SPeter.Dunlap@Sun.COM { 9467978SPeter.Dunlap@Sun.COM idm_so_svc_t *so_svc; 9477978SPeter.Dunlap@Sun.COM idm_svc_req_t *sr = &is->is_svc_req; 9487978SPeter.Dunlap@Sun.COM struct sockaddr_in6 sin6_ip; 9497978SPeter.Dunlap@Sun.COM const uint32_t on = 1; 9507978SPeter.Dunlap@Sun.COM const uint32_t off = 0; 9517978SPeter.Dunlap@Sun.COM 9527978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 9537978SPeter.Dunlap@Sun.COM so_svc = (idm_so_svc_t *)is->is_so_svc; 9547978SPeter.Dunlap@Sun.COM 9557978SPeter.Dunlap@Sun.COM /* 9567978SPeter.Dunlap@Sun.COM * Try creating an IPv6 socket first 9577978SPeter.Dunlap@Sun.COM */ 9587978SPeter.Dunlap@Sun.COM if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) { 9597978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 9607978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 9617978SPeter.Dunlap@Sun.COM } else { 9627978SPeter.Dunlap@Sun.COM bzero(&sin6_ip, sizeof (sin6_ip)); 9637978SPeter.Dunlap@Sun.COM sin6_ip.sin6_family = AF_INET6; 9647978SPeter.Dunlap@Sun.COM sin6_ip.sin6_port = htons(sr->sr_port); 9657978SPeter.Dunlap@Sun.COM sin6_ip.sin6_addr = in6addr_any; 9667978SPeter.Dunlap@Sun.COM 9678348SEric.Yu@Sun.COM (void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET, 9688348SEric.Yu@Sun.COM SO_REUSEADDR, (char *)&on, sizeof (on), CRED()); 9697978SPeter.Dunlap@Sun.COM /* 9707978SPeter.Dunlap@Sun.COM * Turn off SO_MAC_EXEMPT so future sobinds succeed 9717978SPeter.Dunlap@Sun.COM */ 9728348SEric.Yu@Sun.COM (void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET, 9738348SEric.Yu@Sun.COM SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED()); 9747978SPeter.Dunlap@Sun.COM 9758348SEric.Yu@Sun.COM if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip, 9768348SEric.Yu@Sun.COM sizeof (sin6_ip), CRED()) != 0) { 9777978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 9787978SPeter.Dunlap@Sun.COM idm_sodestroy(so_svc->is_so); 9797978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 9807978SPeter.Dunlap@Sun.COM } 9817978SPeter.Dunlap@Sun.COM } 9827978SPeter.Dunlap@Sun.COM 9837978SPeter.Dunlap@Sun.COM idm_set_tgt_connect_options(so_svc->is_so); 9847978SPeter.Dunlap@Sun.COM 9858348SEric.Yu@Sun.COM if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) { 9867978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 9877978SPeter.Dunlap@Sun.COM idm_soshutdown(so_svc->is_so); 9887978SPeter.Dunlap@Sun.COM idm_sodestroy(so_svc->is_so); 9897978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 9907978SPeter.Dunlap@Sun.COM } 9917978SPeter.Dunlap@Sun.COM 9927978SPeter.Dunlap@Sun.COM /* Launch a watch thread */ 9937978SPeter.Dunlap@Sun.COM so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher, 9947978SPeter.Dunlap@Sun.COM is, 0, &p0, TS_RUN, minclsyspri); 9957978SPeter.Dunlap@Sun.COM 9967978SPeter.Dunlap@Sun.COM if (so_svc->is_thread == NULL) { 9977978SPeter.Dunlap@Sun.COM /* Failure to launch; teardown the socket */ 9987978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 9997978SPeter.Dunlap@Sun.COM idm_soshutdown(so_svc->is_so); 10007978SPeter.Dunlap@Sun.COM idm_sodestroy(so_svc->is_so); 10017978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 10027978SPeter.Dunlap@Sun.COM } 10038348SEric.Yu@Sun.COM ksocket_hold(so_svc->is_so); 10047978SPeter.Dunlap@Sun.COM /* Wait for the port watcher thread to start */ 10057978SPeter.Dunlap@Sun.COM while (!so_svc->is_thread_running) 10067978SPeter.Dunlap@Sun.COM cv_wait(&is->is_cv, &is->is_mutex); 10077978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 10087978SPeter.Dunlap@Sun.COM 10097978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 10107978SPeter.Dunlap@Sun.COM } 10117978SPeter.Dunlap@Sun.COM 10127978SPeter.Dunlap@Sun.COM /* 10137978SPeter.Dunlap@Sun.COM * idm_so_tgt_svc_offline 10147978SPeter.Dunlap@Sun.COM * 10157978SPeter.Dunlap@Sun.COM * Stop listening on the IP address and port identified by idm_svc_t. 10167978SPeter.Dunlap@Sun.COM */ 10177978SPeter.Dunlap@Sun.COM static void 10187978SPeter.Dunlap@Sun.COM idm_so_tgt_svc_offline(idm_svc_t *is) 10197978SPeter.Dunlap@Sun.COM { 10207978SPeter.Dunlap@Sun.COM idm_so_svc_t *so_svc; 10217978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 10227978SPeter.Dunlap@Sun.COM so_svc = (idm_so_svc_t *)is->is_so_svc; 10237978SPeter.Dunlap@Sun.COM so_svc->is_thread_running = B_FALSE; 10247978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 10257978SPeter.Dunlap@Sun.COM 10267978SPeter.Dunlap@Sun.COM /* 10278348SEric.Yu@Sun.COM * Teardown socket 10287978SPeter.Dunlap@Sun.COM */ 10298348SEric.Yu@Sun.COM idm_sodestroy(so_svc->is_so); 10307978SPeter.Dunlap@Sun.COM 10317978SPeter.Dunlap@Sun.COM /* 10327978SPeter.Dunlap@Sun.COM * Now we expect the port watcher thread to terminate 10337978SPeter.Dunlap@Sun.COM */ 10347978SPeter.Dunlap@Sun.COM thread_join(so_svc->is_thread_did); 10357978SPeter.Dunlap@Sun.COM } 10367978SPeter.Dunlap@Sun.COM 10377978SPeter.Dunlap@Sun.COM /* 10387978SPeter.Dunlap@Sun.COM * Watch thread for target service connection establishment. 10397978SPeter.Dunlap@Sun.COM */ 10407978SPeter.Dunlap@Sun.COM void 10417978SPeter.Dunlap@Sun.COM idm_so_svc_port_watcher(void *arg) 10427978SPeter.Dunlap@Sun.COM { 10437978SPeter.Dunlap@Sun.COM idm_svc_t *svc = arg; 10448348SEric.Yu@Sun.COM ksocket_t new_so; 10457978SPeter.Dunlap@Sun.COM idm_conn_t *ic; 10467978SPeter.Dunlap@Sun.COM idm_status_t idmrc; 10477978SPeter.Dunlap@Sun.COM idm_so_svc_t *so_svc; 10487978SPeter.Dunlap@Sun.COM int rc; 10497978SPeter.Dunlap@Sun.COM const uint32_t off = 0; 10508348SEric.Yu@Sun.COM struct sockaddr_in6 t_addr; 10518348SEric.Yu@Sun.COM socklen_t t_addrlen; 10527978SPeter.Dunlap@Sun.COM 10538348SEric.Yu@Sun.COM bzero(&t_addr, sizeof (struct sockaddr_in6)); 10548348SEric.Yu@Sun.COM t_addrlen = sizeof (struct sockaddr_in6); 10557978SPeter.Dunlap@Sun.COM mutex_enter(&svc->is_mutex); 10567978SPeter.Dunlap@Sun.COM 10577978SPeter.Dunlap@Sun.COM so_svc = svc->is_so_svc; 10587978SPeter.Dunlap@Sun.COM so_svc->is_thread_running = B_TRUE; 10597978SPeter.Dunlap@Sun.COM so_svc->is_thread_did = so_svc->is_thread->t_did; 10607978SPeter.Dunlap@Sun.COM 10617978SPeter.Dunlap@Sun.COM cv_signal(&svc->is_cv); 10627978SPeter.Dunlap@Sun.COM 10637978SPeter.Dunlap@Sun.COM IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc, 10647978SPeter.Dunlap@Sun.COM svc->is_svc_req.sr_port); 10657978SPeter.Dunlap@Sun.COM 10667978SPeter.Dunlap@Sun.COM while (so_svc->is_thread_running) { 10677978SPeter.Dunlap@Sun.COM mutex_exit(&svc->is_mutex); 10687978SPeter.Dunlap@Sun.COM 10698348SEric.Yu@Sun.COM if ((rc = ksocket_accept(so_svc->is_so, 10708348SEric.Yu@Sun.COM (struct sockaddr *)&t_addr, &t_addrlen, 10718348SEric.Yu@Sun.COM &new_so, CRED())) != 0) { 10727978SPeter.Dunlap@Sun.COM mutex_enter(&svc->is_mutex); 10737978SPeter.Dunlap@Sun.COM if (rc == ECONNABORTED) 10747978SPeter.Dunlap@Sun.COM continue; 10757978SPeter.Dunlap@Sun.COM /* Connection problem */ 10767978SPeter.Dunlap@Sun.COM break; 10777978SPeter.Dunlap@Sun.COM } 10787978SPeter.Dunlap@Sun.COM /* 10797978SPeter.Dunlap@Sun.COM * Turn off SO_MAC_EXEMPT so future sobinds succeed 10807978SPeter.Dunlap@Sun.COM */ 10818348SEric.Yu@Sun.COM (void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT, 10828348SEric.Yu@Sun.COM (char *)&off, sizeof (off), CRED()); 10837978SPeter.Dunlap@Sun.COM 10847978SPeter.Dunlap@Sun.COM idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS, 10857978SPeter.Dunlap@Sun.COM &ic); 10867978SPeter.Dunlap@Sun.COM if (idmrc != IDM_STATUS_SUCCESS) { 10877978SPeter.Dunlap@Sun.COM /* Drop connection */ 10887978SPeter.Dunlap@Sun.COM idm_soshutdown(new_so); 10897978SPeter.Dunlap@Sun.COM idm_sodestroy(new_so); 10907978SPeter.Dunlap@Sun.COM mutex_enter(&svc->is_mutex); 10917978SPeter.Dunlap@Sun.COM continue; 10927978SPeter.Dunlap@Sun.COM } 10937978SPeter.Dunlap@Sun.COM 10947978SPeter.Dunlap@Sun.COM idmrc = idm_so_tgt_conn_create(ic, new_so); 10957978SPeter.Dunlap@Sun.COM if (idmrc != IDM_STATUS_SUCCESS) { 10967978SPeter.Dunlap@Sun.COM idm_svc_conn_destroy(ic); 10977978SPeter.Dunlap@Sun.COM idm_soshutdown(new_so); 10987978SPeter.Dunlap@Sun.COM idm_sodestroy(new_so); 10997978SPeter.Dunlap@Sun.COM mutex_enter(&svc->is_mutex); 11007978SPeter.Dunlap@Sun.COM continue; 11017978SPeter.Dunlap@Sun.COM } 11027978SPeter.Dunlap@Sun.COM 11037978SPeter.Dunlap@Sun.COM /* 11047978SPeter.Dunlap@Sun.COM * Kick the state machine. At CS_S3_XPT_UP the state machine 11057978SPeter.Dunlap@Sun.COM * will notify the client (target) about the new connection. 11067978SPeter.Dunlap@Sun.COM */ 11077978SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL); 11087978SPeter.Dunlap@Sun.COM 11097978SPeter.Dunlap@Sun.COM mutex_enter(&svc->is_mutex); 11107978SPeter.Dunlap@Sun.COM } 11118348SEric.Yu@Sun.COM ksocket_rele(so_svc->is_so); 11127978SPeter.Dunlap@Sun.COM so_svc->is_thread_running = B_FALSE; 11137978SPeter.Dunlap@Sun.COM mutex_exit(&svc->is_mutex); 11147978SPeter.Dunlap@Sun.COM 11157978SPeter.Dunlap@Sun.COM IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc, 11167978SPeter.Dunlap@Sun.COM svc->is_svc_req.sr_port); 11177978SPeter.Dunlap@Sun.COM 11187978SPeter.Dunlap@Sun.COM thread_exit(); 11197978SPeter.Dunlap@Sun.COM } 11207978SPeter.Dunlap@Sun.COM 11217978SPeter.Dunlap@Sun.COM /* 11227978SPeter.Dunlap@Sun.COM * idm_so_free_task_rsrc() stops any ongoing processing of the task and 11237978SPeter.Dunlap@Sun.COM * frees resources associated with the task. 11247978SPeter.Dunlap@Sun.COM * 11257978SPeter.Dunlap@Sun.COM * It's not clear that this should return idm_status_t. What do we do 11267978SPeter.Dunlap@Sun.COM * if it fails? 11277978SPeter.Dunlap@Sun.COM */ 11287978SPeter.Dunlap@Sun.COM static idm_status_t 11297978SPeter.Dunlap@Sun.COM idm_so_free_task_rsrc(idm_task_t *idt) 11307978SPeter.Dunlap@Sun.COM { 11317978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 11327978SPeter.Dunlap@Sun.COM 11337978SPeter.Dunlap@Sun.COM /* 1134*9162SPeter.Dunlap@Sun.COM * There is nothing to cleanup on initiator connections 1135*9162SPeter.Dunlap@Sun.COM */ 1136*9162SPeter.Dunlap@Sun.COM if (IDM_CONN_ISINI(idt->idt_ic)) 1137*9162SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 1138*9162SPeter.Dunlap@Sun.COM 1139*9162SPeter.Dunlap@Sun.COM /* 11407978SPeter.Dunlap@Sun.COM * If this is a target connection, call idm_buf_rx_from_ini_done for 11417978SPeter.Dunlap@Sun.COM * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE. 11427978SPeter.Dunlap@Sun.COM * 11437978SPeter.Dunlap@Sun.COM * In addition, remove any buffers associated with this task from 11447978SPeter.Dunlap@Sun.COM * the ic_tx_list. We'll do this by walking the idt_inbufv list, but 11457978SPeter.Dunlap@Sun.COM * items don't actually get removed from that list (and completion 11467978SPeter.Dunlap@Sun.COM * routines called) until idm_task_cleanup. 11477978SPeter.Dunlap@Sun.COM */ 11487978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11497978SPeter.Dunlap@Sun.COM 11507978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_outbufv); idb != NULL; 11517978SPeter.Dunlap@Sun.COM idb = list_next(&idt->idt_outbufv, idb)) { 11527978SPeter.Dunlap@Sun.COM if (idb->idb_in_transport) { 11537978SPeter.Dunlap@Sun.COM /* 11547978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini_done releases idt->idt_mutex 11557978SPeter.Dunlap@Sun.COM */ 11567978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED); 11577978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11587978SPeter.Dunlap@Sun.COM } 11597978SPeter.Dunlap@Sun.COM } 11607978SPeter.Dunlap@Sun.COM 11617978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_inbufv); idb != NULL; 11627978SPeter.Dunlap@Sun.COM idb = list_next(&idt->idt_inbufv, idb)) { 11637978SPeter.Dunlap@Sun.COM /* 11647978SPeter.Dunlap@Sun.COM * We want to remove these items from the tx_list as well, 11657978SPeter.Dunlap@Sun.COM * but knowing it's in the idt_inbufv list is not a guarantee 11667978SPeter.Dunlap@Sun.COM * that it's in the tx_list. If it's on the tx list then 11677978SPeter.Dunlap@Sun.COM * let idm_sotx_thread() clean it up. 11687978SPeter.Dunlap@Sun.COM */ 11697978SPeter.Dunlap@Sun.COM if (idb->idb_in_transport && !idb->idb_tx_thread) { 11707978SPeter.Dunlap@Sun.COM /* 11717978SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini_done releases idt->idt_mutex 11727978SPeter.Dunlap@Sun.COM */ 11737978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED); 11747978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11757978SPeter.Dunlap@Sun.COM } 11767978SPeter.Dunlap@Sun.COM } 11777978SPeter.Dunlap@Sun.COM 11787978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 11797978SPeter.Dunlap@Sun.COM 11807978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 11817978SPeter.Dunlap@Sun.COM } 11827978SPeter.Dunlap@Sun.COM 11837978SPeter.Dunlap@Sun.COM /* 11847978SPeter.Dunlap@Sun.COM * idm_so_negotiate_key_values() validates the key values for this connection 11857978SPeter.Dunlap@Sun.COM */ 11867978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 11877978SPeter.Dunlap@Sun.COM static kv_status_t 11887978SPeter.Dunlap@Sun.COM idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl, 11897978SPeter.Dunlap@Sun.COM nvlist_t *response_nvl, nvlist_t *negotiated_nvl) 11907978SPeter.Dunlap@Sun.COM { 11917978SPeter.Dunlap@Sun.COM /* All parameters are negotiated at the iscsit level */ 11927978SPeter.Dunlap@Sun.COM return (KV_HANDLED); 11937978SPeter.Dunlap@Sun.COM } 11947978SPeter.Dunlap@Sun.COM 11957978SPeter.Dunlap@Sun.COM /* 11967978SPeter.Dunlap@Sun.COM * idm_so_notice_key_values() activates the negotiated key values for 11977978SPeter.Dunlap@Sun.COM * this connection. 11987978SPeter.Dunlap@Sun.COM */ 1199*9162SPeter.Dunlap@Sun.COM static void 12007978SPeter.Dunlap@Sun.COM idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl) 12017978SPeter.Dunlap@Sun.COM { 12027978SPeter.Dunlap@Sun.COM char *nvp_name; 12037978SPeter.Dunlap@Sun.COM nvpair_t *nvp; 12047978SPeter.Dunlap@Sun.COM nvpair_t *next_nvp; 12057978SPeter.Dunlap@Sun.COM int nvrc; 12067978SPeter.Dunlap@Sun.COM idm_status_t idm_status; 12077978SPeter.Dunlap@Sun.COM const idm_kv_xlate_t *ikvx; 12087978SPeter.Dunlap@Sun.COM 12097978SPeter.Dunlap@Sun.COM for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL); 12107978SPeter.Dunlap@Sun.COM nvp != NULL; nvp = next_nvp) { 12117978SPeter.Dunlap@Sun.COM next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp); 12127978SPeter.Dunlap@Sun.COM nvp_name = nvpair_name(nvp); 12137978SPeter.Dunlap@Sun.COM 12147978SPeter.Dunlap@Sun.COM ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name)); 12157978SPeter.Dunlap@Sun.COM switch (ikvx->ik_key_id) { 12167978SPeter.Dunlap@Sun.COM case KI_HEADER_DIGEST: 12177978SPeter.Dunlap@Sun.COM case KI_DATA_DIGEST: 12187978SPeter.Dunlap@Sun.COM idm_status = idm_so_handle_digest(it, nvp, ikvx); 12197978SPeter.Dunlap@Sun.COM ASSERT(idm_status == 0); 12207978SPeter.Dunlap@Sun.COM 12217978SPeter.Dunlap@Sun.COM /* Remove processed item from negotiated_nvl list */ 12227978SPeter.Dunlap@Sun.COM nvrc = nvlist_remove_all( 12237978SPeter.Dunlap@Sun.COM negotiated_nvl, ikvx->ik_key_name); 12247978SPeter.Dunlap@Sun.COM ASSERT(nvrc == 0); 12257978SPeter.Dunlap@Sun.COM break; 12267978SPeter.Dunlap@Sun.COM default: 12277978SPeter.Dunlap@Sun.COM break; 12287978SPeter.Dunlap@Sun.COM } 12297978SPeter.Dunlap@Sun.COM } 12307978SPeter.Dunlap@Sun.COM } 12317978SPeter.Dunlap@Sun.COM 12327978SPeter.Dunlap@Sun.COM 12337978SPeter.Dunlap@Sun.COM static idm_status_t 12347978SPeter.Dunlap@Sun.COM idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice, 12357978SPeter.Dunlap@Sun.COM const idm_kv_xlate_t *ikvx) 12367978SPeter.Dunlap@Sun.COM { 12377978SPeter.Dunlap@Sun.COM int nvrc; 12387978SPeter.Dunlap@Sun.COM char *digest_choice_string; 12397978SPeter.Dunlap@Sun.COM 12407978SPeter.Dunlap@Sun.COM nvrc = nvpair_value_string(digest_choice, 12417978SPeter.Dunlap@Sun.COM &digest_choice_string); 12427978SPeter.Dunlap@Sun.COM ASSERT(nvrc == 0); 12437978SPeter.Dunlap@Sun.COM if (strcasecmp(digest_choice_string, "crc32c") == 0) { 12447978SPeter.Dunlap@Sun.COM switch (ikvx->ik_key_id) { 12457978SPeter.Dunlap@Sun.COM case KI_HEADER_DIGEST: 12467978SPeter.Dunlap@Sun.COM it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST; 12477978SPeter.Dunlap@Sun.COM break; 12487978SPeter.Dunlap@Sun.COM case KI_DATA_DIGEST: 12497978SPeter.Dunlap@Sun.COM it->ic_conn_flags |= IDM_CONN_DATA_DIGEST; 12507978SPeter.Dunlap@Sun.COM break; 12517978SPeter.Dunlap@Sun.COM default: 12527978SPeter.Dunlap@Sun.COM ASSERT(0); 12537978SPeter.Dunlap@Sun.COM break; 12547978SPeter.Dunlap@Sun.COM } 12557978SPeter.Dunlap@Sun.COM } else if (strcasecmp(digest_choice_string, "none") == 0) { 12567978SPeter.Dunlap@Sun.COM switch (ikvx->ik_key_id) { 12577978SPeter.Dunlap@Sun.COM case KI_HEADER_DIGEST: 12587978SPeter.Dunlap@Sun.COM it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST; 12597978SPeter.Dunlap@Sun.COM break; 12607978SPeter.Dunlap@Sun.COM case KI_DATA_DIGEST: 12617978SPeter.Dunlap@Sun.COM it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST; 12627978SPeter.Dunlap@Sun.COM break; 12637978SPeter.Dunlap@Sun.COM default: 12647978SPeter.Dunlap@Sun.COM ASSERT(0); 12657978SPeter.Dunlap@Sun.COM break; 12667978SPeter.Dunlap@Sun.COM } 12677978SPeter.Dunlap@Sun.COM } else { 12687978SPeter.Dunlap@Sun.COM ASSERT(0); 12697978SPeter.Dunlap@Sun.COM } 12707978SPeter.Dunlap@Sun.COM 12717978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 12727978SPeter.Dunlap@Sun.COM } 12737978SPeter.Dunlap@Sun.COM 12747978SPeter.Dunlap@Sun.COM 12757978SPeter.Dunlap@Sun.COM /* 12767978SPeter.Dunlap@Sun.COM * idm_so_conn_is_capable() verifies that the passed connection is provided 12777978SPeter.Dunlap@Sun.COM * for by the sockets interface. 12787978SPeter.Dunlap@Sun.COM */ 12797978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 12807978SPeter.Dunlap@Sun.COM static boolean_t 12817978SPeter.Dunlap@Sun.COM idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps) 12827978SPeter.Dunlap@Sun.COM { 12837978SPeter.Dunlap@Sun.COM return (B_TRUE); 12847978SPeter.Dunlap@Sun.COM } 12857978SPeter.Dunlap@Sun.COM 12867978SPeter.Dunlap@Sun.COM /* 12877978SPeter.Dunlap@Sun.COM * idm_so_rx_datain() validates the Data Sequence number of the PDU. The 12887978SPeter.Dunlap@Sun.COM * idm_sorecv_scsidata() function invoked earlier actually reads the data 12897978SPeter.Dunlap@Sun.COM * off the socket into the appropriate buffers. 12907978SPeter.Dunlap@Sun.COM */ 12917978SPeter.Dunlap@Sun.COM static void 12927978SPeter.Dunlap@Sun.COM idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu) 12937978SPeter.Dunlap@Sun.COM { 12947978SPeter.Dunlap@Sun.COM iscsi_data_hdr_t *bhs; 12957978SPeter.Dunlap@Sun.COM idm_task_t *idt; 12967978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 12977978SPeter.Dunlap@Sun.COM uint32_t datasn; 12987978SPeter.Dunlap@Sun.COM size_t offset; 12997978SPeter.Dunlap@Sun.COM iscsi_hdr_t *ihp = (iscsi_hdr_t *)pdu->isp_hdr; 13007978SPeter.Dunlap@Sun.COM iscsi_data_rsp_hdr_t *idrhp = (iscsi_data_rsp_hdr_t *)ihp; 13017978SPeter.Dunlap@Sun.COM 13027978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 13037978SPeter.Dunlap@Sun.COM ASSERT(pdu != NULL); 13047978SPeter.Dunlap@Sun.COM 13057978SPeter.Dunlap@Sun.COM bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 13067978SPeter.Dunlap@Sun.COM datasn = ntohl(bhs->datasn); 13077978SPeter.Dunlap@Sun.COM offset = ntohl(bhs->offset); 13087978SPeter.Dunlap@Sun.COM 13097978SPeter.Dunlap@Sun.COM ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP); 13107978SPeter.Dunlap@Sun.COM 13117978SPeter.Dunlap@Sun.COM /* 13127978SPeter.Dunlap@Sun.COM * Look up the task corresponding to the initiator task tag 13137978SPeter.Dunlap@Sun.COM * to get the buffers affiliated with the task. 13147978SPeter.Dunlap@Sun.COM */ 13157978SPeter.Dunlap@Sun.COM idt = idm_task_find(ic, bhs->itt, bhs->ttt); 13167978SPeter.Dunlap@Sun.COM if (idt == NULL) { 13177978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task"); 13187978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 13197978SPeter.Dunlap@Sun.COM return; 13207978SPeter.Dunlap@Sun.COM } 13217978SPeter.Dunlap@Sun.COM 13227978SPeter.Dunlap@Sun.COM idb = pdu->isp_sorx_buf; 13237978SPeter.Dunlap@Sun.COM if (idb == NULL) { 13247978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 13257978SPeter.Dunlap@Sun.COM "idm_so_rx_datain: failed to find buffer"); 13267978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 13277978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 13287978SPeter.Dunlap@Sun.COM return; 13297978SPeter.Dunlap@Sun.COM } 13307978SPeter.Dunlap@Sun.COM 13317978SPeter.Dunlap@Sun.COM /* 13327978SPeter.Dunlap@Sun.COM * DataSN values should be sequential and should not have any gaps or 13337978SPeter.Dunlap@Sun.COM * repetitions. Check the DataSN with the one stored in the task. 13347978SPeter.Dunlap@Sun.COM */ 13357978SPeter.Dunlap@Sun.COM if (datasn == idt->idt_exp_datasn) { 13367978SPeter.Dunlap@Sun.COM idt->idt_exp_datasn++; /* keep track of DataSN received */ 13377978SPeter.Dunlap@Sun.COM } else { 13387978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order"); 13397978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 13407978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 13417978SPeter.Dunlap@Sun.COM return; 13427978SPeter.Dunlap@Sun.COM } 13437978SPeter.Dunlap@Sun.COM 13447978SPeter.Dunlap@Sun.COM /* 13457978SPeter.Dunlap@Sun.COM * PDUs in a sequence should be in continuously increasing 13467978SPeter.Dunlap@Sun.COM * address offset 13477978SPeter.Dunlap@Sun.COM */ 13487978SPeter.Dunlap@Sun.COM if (offset != idb->idb_exp_offset) { 13497978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset"); 1350*9162SPeter.Dunlap@Sun.COM idm_task_rele(idt); 13517978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 13527978SPeter.Dunlap@Sun.COM return; 13537978SPeter.Dunlap@Sun.COM } 13547978SPeter.Dunlap@Sun.COM /* Expected next relative buffer offset */ 13557978SPeter.Dunlap@Sun.COM idb->idb_exp_offset += n2h24(bhs->dlength); 1356*9162SPeter.Dunlap@Sun.COM idt->idt_rx_bytes += n2h24(bhs->dlength); 1357*9162SPeter.Dunlap@Sun.COM 1358*9162SPeter.Dunlap@Sun.COM idm_task_rele(idt); 13597978SPeter.Dunlap@Sun.COM 13607978SPeter.Dunlap@Sun.COM /* 13617978SPeter.Dunlap@Sun.COM * For now call scsi_rsp which will process the data rsp 13627978SPeter.Dunlap@Sun.COM * Revisit, need to provide an explicit client entry point for 13637978SPeter.Dunlap@Sun.COM * phase collapse completions. 13647978SPeter.Dunlap@Sun.COM */ 13657978SPeter.Dunlap@Sun.COM if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) && 13667978SPeter.Dunlap@Sun.COM (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) { 13677978SPeter.Dunlap@Sun.COM (*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu); 13687978SPeter.Dunlap@Sun.COM } 13697978SPeter.Dunlap@Sun.COM 13707978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 13717978SPeter.Dunlap@Sun.COM } 13727978SPeter.Dunlap@Sun.COM 13737978SPeter.Dunlap@Sun.COM /* 13747978SPeter.Dunlap@Sun.COM * The idm_so_rx_dataout() function is used by the iSCSI target to read 13757978SPeter.Dunlap@Sun.COM * data from the Data-Out PDU sent by the iSCSI initiator. 13767978SPeter.Dunlap@Sun.COM * 13777978SPeter.Dunlap@Sun.COM * This function gets the Initiator Task Tag from the PDU BHS and looks up the 13787978SPeter.Dunlap@Sun.COM * task to get the buffers associated with the PDU. A PDU might span buffers. 13797978SPeter.Dunlap@Sun.COM * The data is then read into the respective buffer. 13807978SPeter.Dunlap@Sun.COM */ 13817978SPeter.Dunlap@Sun.COM static void 13827978SPeter.Dunlap@Sun.COM idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu) 13837978SPeter.Dunlap@Sun.COM { 13847978SPeter.Dunlap@Sun.COM 13857978SPeter.Dunlap@Sun.COM iscsi_data_hdr_t *bhs; 13867978SPeter.Dunlap@Sun.COM idm_task_t *idt; 13877978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 13887978SPeter.Dunlap@Sun.COM size_t offset; 13897978SPeter.Dunlap@Sun.COM 13907978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 13917978SPeter.Dunlap@Sun.COM ASSERT(pdu != NULL); 13927978SPeter.Dunlap@Sun.COM 13937978SPeter.Dunlap@Sun.COM bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 13947978SPeter.Dunlap@Sun.COM offset = ntohl(bhs->offset); 13957978SPeter.Dunlap@Sun.COM ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA); 13967978SPeter.Dunlap@Sun.COM 13977978SPeter.Dunlap@Sun.COM /* 13987978SPeter.Dunlap@Sun.COM * Look up the task corresponding to the initiator task tag 13997978SPeter.Dunlap@Sun.COM * to get the buffers affiliated with the task. 14007978SPeter.Dunlap@Sun.COM */ 14017978SPeter.Dunlap@Sun.COM idt = idm_task_find(ic, bhs->itt, bhs->ttt); 14027978SPeter.Dunlap@Sun.COM if (idt == NULL) { 14037978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 14047978SPeter.Dunlap@Sun.COM "idm_so_rx_dataout: failed to find task"); 14057978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 14067978SPeter.Dunlap@Sun.COM return; 14077978SPeter.Dunlap@Sun.COM } 14087978SPeter.Dunlap@Sun.COM 14097978SPeter.Dunlap@Sun.COM idb = pdu->isp_sorx_buf; 14107978SPeter.Dunlap@Sun.COM if (idb == NULL) { 14117978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 14127978SPeter.Dunlap@Sun.COM "idm_so_rx_dataout: failed to find buffer"); 14137978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 14147978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 14157978SPeter.Dunlap@Sun.COM return; 14167978SPeter.Dunlap@Sun.COM } 14177978SPeter.Dunlap@Sun.COM 14187978SPeter.Dunlap@Sun.COM /* Keep track of data transferred - check data offsets */ 14197978SPeter.Dunlap@Sun.COM if (offset != idb->idb_exp_offset) { 14207978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: " 14217978SPeter.Dunlap@Sun.COM "%ld, %d", offset, idb->idb_exp_offset); 14227978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 14237978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 14247978SPeter.Dunlap@Sun.COM return; 14257978SPeter.Dunlap@Sun.COM } 14267978SPeter.Dunlap@Sun.COM /* Expected next relative offset */ 14277978SPeter.Dunlap@Sun.COM idb->idb_exp_offset += ntoh24(bhs->dlength); 1428*9162SPeter.Dunlap@Sun.COM idt->idt_rx_bytes += n2h24(bhs->dlength); 14297978SPeter.Dunlap@Sun.COM 14307978SPeter.Dunlap@Sun.COM /* 14317978SPeter.Dunlap@Sun.COM * Call the buffer callback when the transfer is complete 14327978SPeter.Dunlap@Sun.COM * 14337978SPeter.Dunlap@Sun.COM * The connection state machine should only abort tasks after 14347978SPeter.Dunlap@Sun.COM * shutting down the connection so we are assured that there 14357978SPeter.Dunlap@Sun.COM * won't be a simultaneous attempt to abort this task at the 14367978SPeter.Dunlap@Sun.COM * same time as we are processing this PDU (due to a connection 14377978SPeter.Dunlap@Sun.COM * state change). 14387978SPeter.Dunlap@Sun.COM */ 14397978SPeter.Dunlap@Sun.COM if (bhs->flags & ISCSI_FLAG_FINAL) { 14407978SPeter.Dunlap@Sun.COM /* 14417978SPeter.Dunlap@Sun.COM * We only want to call idm_buf_rx_from_ini_done once 14427978SPeter.Dunlap@Sun.COM * per transfer. It's possible that this task has 14437978SPeter.Dunlap@Sun.COM * already been aborted in which case 14447978SPeter.Dunlap@Sun.COM * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done 14457978SPeter.Dunlap@Sun.COM * for each buffer with idb_in_transport==B_TRUE. To 14467978SPeter.Dunlap@Sun.COM * close this window and ensure that this doesn't happen, 14477978SPeter.Dunlap@Sun.COM * we'll clear idb->idb_in_transport now while holding 14487978SPeter.Dunlap@Sun.COM * the task mutex. This is only really an issue for 14497978SPeter.Dunlap@Sun.COM * SCSI task abort -- if tasks were being aborted because 14507978SPeter.Dunlap@Sun.COM * of a connection state change the state machine would 14517978SPeter.Dunlap@Sun.COM * have already stopped the receive thread. 14527978SPeter.Dunlap@Sun.COM */ 14537978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 14547978SPeter.Dunlap@Sun.COM 14557978SPeter.Dunlap@Sun.COM /* 14567978SPeter.Dunlap@Sun.COM * Release the task hold here (obtained in idm_task_find) 14577978SPeter.Dunlap@Sun.COM * because the task may complete synchronously during 14587978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini_done. Since we still have an active 14597978SPeter.Dunlap@Sun.COM * buffer we know there is at least one additional hold on idt. 14607978SPeter.Dunlap@Sun.COM */ 14617978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 14627978SPeter.Dunlap@Sun.COM 14637978SPeter.Dunlap@Sun.COM /* 14647978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini_done releases idt->idt_mutex 14657978SPeter.Dunlap@Sun.COM */ 14667978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS); 14677978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 14687978SPeter.Dunlap@Sun.COM return; 14697978SPeter.Dunlap@Sun.COM } 14707978SPeter.Dunlap@Sun.COM 14717978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 14727978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 14737978SPeter.Dunlap@Sun.COM } 14747978SPeter.Dunlap@Sun.COM 14757978SPeter.Dunlap@Sun.COM /* 14767978SPeter.Dunlap@Sun.COM * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle 14777978SPeter.Dunlap@Sun.COM * the R2T PDU sent by the iSCSI target indicating that it is ready to 14787978SPeter.Dunlap@Sun.COM * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS 14797978SPeter.Dunlap@Sun.COM * and looks up the task in the task tree using the itt to get the output 14807978SPeter.Dunlap@Sun.COM * buffers associated the task. The R2T PDU contains the offset of the 14817978SPeter.Dunlap@Sun.COM * requested data and the data length. This function then constructs a 14827978SPeter.Dunlap@Sun.COM * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out 14837978SPeter.Dunlap@Sun.COM * PDU is associated with the R2T by the Target Transfer Tag (ttt). 14847978SPeter.Dunlap@Sun.COM */ 1485*9162SPeter.Dunlap@Sun.COM 14867978SPeter.Dunlap@Sun.COM static void 14877978SPeter.Dunlap@Sun.COM idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu) 14887978SPeter.Dunlap@Sun.COM { 14897978SPeter.Dunlap@Sun.COM idm_task_t *idt; 14907978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 14917978SPeter.Dunlap@Sun.COM iscsi_rtt_hdr_t *rtt_hdr; 14927978SPeter.Dunlap@Sun.COM uint32_t data_offset; 1493*9162SPeter.Dunlap@Sun.COM uint32_t data_length; 14947978SPeter.Dunlap@Sun.COM 14957978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 14967978SPeter.Dunlap@Sun.COM ASSERT(pdu != NULL); 14977978SPeter.Dunlap@Sun.COM 14987978SPeter.Dunlap@Sun.COM rtt_hdr = (iscsi_rtt_hdr_t *)pdu->isp_hdr; 14997978SPeter.Dunlap@Sun.COM data_offset = ntohl(rtt_hdr->data_offset); 1500*9162SPeter.Dunlap@Sun.COM data_length = ntohl(rtt_hdr->data_length); 15017978SPeter.Dunlap@Sun.COM idt = idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt); 15027978SPeter.Dunlap@Sun.COM 15037978SPeter.Dunlap@Sun.COM if (idt == NULL) { 15047978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task"); 15057978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 15067978SPeter.Dunlap@Sun.COM return; 15077978SPeter.Dunlap@Sun.COM } 15087978SPeter.Dunlap@Sun.COM 15097978SPeter.Dunlap@Sun.COM /* Find the buffer bound to the task by the iSCSI initiator */ 15107978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 15117978SPeter.Dunlap@Sun.COM idb = idm_buf_find(&idt->idt_outbufv, data_offset); 15127978SPeter.Dunlap@Sun.COM if (idb == NULL) { 15137978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15147978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 15157978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer"); 15167978SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 15177978SPeter.Dunlap@Sun.COM return; 15187978SPeter.Dunlap@Sun.COM } 15197978SPeter.Dunlap@Sun.COM 1520*9162SPeter.Dunlap@Sun.COM /* return buffer contains this data */ 1521*9162SPeter.Dunlap@Sun.COM if (data_offset + data_length > idb->idb_buflen) { 1522*9162SPeter.Dunlap@Sun.COM /* Overflow */ 1523*9162SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 1524*9162SPeter.Dunlap@Sun.COM idm_task_rele(idt); 1525*9162SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside " 1526*9162SPeter.Dunlap@Sun.COM "buffer"); 1527*9162SPeter.Dunlap@Sun.COM idm_pdu_rx_protocol_error(ic, pdu); 1528*9162SPeter.Dunlap@Sun.COM return; 1529*9162SPeter.Dunlap@Sun.COM } 1530*9162SPeter.Dunlap@Sun.COM 1531*9162SPeter.Dunlap@Sun.COM idt->idt_r2t_ttt = rtt_hdr->ttt; 1532*9162SPeter.Dunlap@Sun.COM idt->idt_exp_datasn = 0; 1533*9162SPeter.Dunlap@Sun.COM 1534*9162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data(ic, idt, idb, data_offset, 1535*9162SPeter.Dunlap@Sun.COM ntohl(rtt_hdr->data_length)); 15367978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15377978SPeter.Dunlap@Sun.COM 15387978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 15397978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 15407978SPeter.Dunlap@Sun.COM 15417978SPeter.Dunlap@Sun.COM } 15427978SPeter.Dunlap@Sun.COM 15437978SPeter.Dunlap@Sun.COM idm_status_t 15447978SPeter.Dunlap@Sun.COM idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu) 15457978SPeter.Dunlap@Sun.COM { 15467978SPeter.Dunlap@Sun.COM uint8_t pad[ISCSI_PAD_WORD_LEN]; 15477978SPeter.Dunlap@Sun.COM int pad_len; 15487978SPeter.Dunlap@Sun.COM uint32_t data_digest_crc; 15497978SPeter.Dunlap@Sun.COM uint32_t crc_calculated; 15507978SPeter.Dunlap@Sun.COM int total_len; 15517978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 15527978SPeter.Dunlap@Sun.COM 15537978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 15547978SPeter.Dunlap@Sun.COM 15557978SPeter.Dunlap@Sun.COM pad_len = ((ISCSI_PAD_WORD_LEN - 15567978SPeter.Dunlap@Sun.COM (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) & 15577978SPeter.Dunlap@Sun.COM (ISCSI_PAD_WORD_LEN - 1)); 15587978SPeter.Dunlap@Sun.COM 15597978SPeter.Dunlap@Sun.COM ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */ 15607978SPeter.Dunlap@Sun.COM 15617978SPeter.Dunlap@Sun.COM total_len = pdu->isp_datalen; 15627978SPeter.Dunlap@Sun.COM 15637978SPeter.Dunlap@Sun.COM if (pad_len) { 15647978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_base = (char *)&pad; 15657978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_len = pad_len; 15667978SPeter.Dunlap@Sun.COM total_len += pad_len; 15677978SPeter.Dunlap@Sun.COM pdu->isp_iovlen++; 15687978SPeter.Dunlap@Sun.COM } 15697978SPeter.Dunlap@Sun.COM 15707978SPeter.Dunlap@Sun.COM /* setup data digest */ 15717978SPeter.Dunlap@Sun.COM if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) { 15727978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_base = 15737978SPeter.Dunlap@Sun.COM (char *)&data_digest_crc; 15747978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_len = 15757978SPeter.Dunlap@Sun.COM sizeof (data_digest_crc); 15767978SPeter.Dunlap@Sun.COM total_len += sizeof (data_digest_crc); 15777978SPeter.Dunlap@Sun.COM pdu->isp_iovlen++; 15787978SPeter.Dunlap@Sun.COM } 15797978SPeter.Dunlap@Sun.COM 1580*9162SPeter.Dunlap@Sun.COM pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base; 1581*9162SPeter.Dunlap@Sun.COM 15827978SPeter.Dunlap@Sun.COM if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0], 15837978SPeter.Dunlap@Sun.COM pdu->isp_iovlen, total_len) != 0) { 15847978SPeter.Dunlap@Sun.COM return (IDM_STATUS_IO); 15857978SPeter.Dunlap@Sun.COM } 15867978SPeter.Dunlap@Sun.COM 15877978SPeter.Dunlap@Sun.COM if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) { 15887978SPeter.Dunlap@Sun.COM crc_calculated = idm_crc32c(pdu->isp_data, 15897978SPeter.Dunlap@Sun.COM pdu->isp_datalen); 15907978SPeter.Dunlap@Sun.COM if (pad_len) { 15917978SPeter.Dunlap@Sun.COM crc_calculated = idm_crc32c_continued((char *)&pad, 15927978SPeter.Dunlap@Sun.COM pad_len, crc_calculated); 15937978SPeter.Dunlap@Sun.COM } 15947978SPeter.Dunlap@Sun.COM if (crc_calculated != data_digest_crc) { 15957978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 15967978SPeter.Dunlap@Sun.COM "idm_sorecvdata: " 15977978SPeter.Dunlap@Sun.COM "CRC error: actual 0x%x, calc 0x%x", 15987978SPeter.Dunlap@Sun.COM data_digest_crc, crc_calculated); 15997978SPeter.Dunlap@Sun.COM 16007978SPeter.Dunlap@Sun.COM /* Invalid Data Digest */ 16017978SPeter.Dunlap@Sun.COM return (IDM_STATUS_DATA_DIGEST); 16027978SPeter.Dunlap@Sun.COM } 16037978SPeter.Dunlap@Sun.COM } 16047978SPeter.Dunlap@Sun.COM 16057978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 16067978SPeter.Dunlap@Sun.COM } 16077978SPeter.Dunlap@Sun.COM 16087978SPeter.Dunlap@Sun.COM /* 16097978SPeter.Dunlap@Sun.COM * idm_sorecv_scsidata() is used to receive scsi data from the socket. The 16107978SPeter.Dunlap@Sun.COM * Data-type PDU header must be read into the idm_pdu_t structure prior to 16117978SPeter.Dunlap@Sun.COM * calling this function. 16127978SPeter.Dunlap@Sun.COM */ 16137978SPeter.Dunlap@Sun.COM idm_status_t 16147978SPeter.Dunlap@Sun.COM idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu) 16157978SPeter.Dunlap@Sun.COM { 16167978SPeter.Dunlap@Sun.COM iscsi_data_hdr_t *bhs; 16177978SPeter.Dunlap@Sun.COM idm_task_t *task; 16187978SPeter.Dunlap@Sun.COM uint32_t offset; 16197978SPeter.Dunlap@Sun.COM uint8_t opcode; 16207978SPeter.Dunlap@Sun.COM uint32_t dlength; 16217978SPeter.Dunlap@Sun.COM list_t *buflst; 16227978SPeter.Dunlap@Sun.COM uint32_t xfer_bytes; 16237978SPeter.Dunlap@Sun.COM idm_status_t status; 16247978SPeter.Dunlap@Sun.COM 16257978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 16267978SPeter.Dunlap@Sun.COM ASSERT(pdu != NULL); 16277978SPeter.Dunlap@Sun.COM 16287978SPeter.Dunlap@Sun.COM bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 16297978SPeter.Dunlap@Sun.COM 16307978SPeter.Dunlap@Sun.COM offset = ntohl(bhs->offset); 16317978SPeter.Dunlap@Sun.COM opcode = bhs->opcode; 16327978SPeter.Dunlap@Sun.COM dlength = n2h24(bhs->dlength); 16337978SPeter.Dunlap@Sun.COM 16347978SPeter.Dunlap@Sun.COM ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) || 16357978SPeter.Dunlap@Sun.COM (opcode == ISCSI_OP_SCSI_DATA)); 16367978SPeter.Dunlap@Sun.COM 16377978SPeter.Dunlap@Sun.COM /* 16387978SPeter.Dunlap@Sun.COM * Successful lookup implicitly gets a "hold" on the task. This 16397978SPeter.Dunlap@Sun.COM * hold must be released before leaving this function. At one 16407978SPeter.Dunlap@Sun.COM * point we were caching this task context and retaining the hold 16417978SPeter.Dunlap@Sun.COM * but it turned out to be very difficult to release the hold properly. 16427978SPeter.Dunlap@Sun.COM * The task can be aborted and the connection shutdown between this 16437978SPeter.Dunlap@Sun.COM * call and the subsequent expected call to idm_so_rx_datain/ 16447978SPeter.Dunlap@Sun.COM * idm_so_rx_dataout (in which case those functions are not called). 16457978SPeter.Dunlap@Sun.COM * Releasing the hold in the PDU callback doesn't work well either 16467978SPeter.Dunlap@Sun.COM * because the whole task may be completed by then at which point 16477978SPeter.Dunlap@Sun.COM * it is too late to release the hold -- for better or worse this 16487978SPeter.Dunlap@Sun.COM * code doesn't wait on the refcnts during normal operation. 16497978SPeter.Dunlap@Sun.COM * idm_task_find() is very fast and it is not a huge burden if we 16507978SPeter.Dunlap@Sun.COM * have to do it twice. 16517978SPeter.Dunlap@Sun.COM */ 16527978SPeter.Dunlap@Sun.COM task = idm_task_find(ic, bhs->itt, bhs->ttt); 16537978SPeter.Dunlap@Sun.COM if (task == NULL) { 16547978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 16557978SPeter.Dunlap@Sun.COM "idm_sorecv_scsidata: could not find task"); 16567978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 16577978SPeter.Dunlap@Sun.COM } 16587978SPeter.Dunlap@Sun.COM 16597978SPeter.Dunlap@Sun.COM mutex_enter(&task->idt_mutex); 16607978SPeter.Dunlap@Sun.COM buflst = (opcode == ISCSI_OP_SCSI_DATA_RSP) ? 16617978SPeter.Dunlap@Sun.COM &task->idt_inbufv : &task->idt_outbufv; 16627978SPeter.Dunlap@Sun.COM pdu->isp_sorx_buf = idm_buf_find(buflst, offset); 16637978SPeter.Dunlap@Sun.COM mutex_exit(&task->idt_mutex); 16647978SPeter.Dunlap@Sun.COM 16657978SPeter.Dunlap@Sun.COM if (pdu->isp_sorx_buf == NULL) { 16667978SPeter.Dunlap@Sun.COM idm_task_rele(task); 16677978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find " 16687978SPeter.Dunlap@Sun.COM "buffer for offset %x opcode=%x", 16697978SPeter.Dunlap@Sun.COM offset, opcode); 16707978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 16717978SPeter.Dunlap@Sun.COM } 16727978SPeter.Dunlap@Sun.COM 16737978SPeter.Dunlap@Sun.COM xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength); 16747978SPeter.Dunlap@Sun.COM ASSERT(xfer_bytes != 0); 16757978SPeter.Dunlap@Sun.COM if (xfer_bytes != dlength) { 16767978SPeter.Dunlap@Sun.COM idm_task_rele(task); 16777978SPeter.Dunlap@Sun.COM /* 16787978SPeter.Dunlap@Sun.COM * Buffer overflow, connection error. The PDU data is still 16797978SPeter.Dunlap@Sun.COM * sitting in the socket so we can't use the connection 16807978SPeter.Dunlap@Sun.COM * again until that data is drained. 16817978SPeter.Dunlap@Sun.COM */ 16827978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 16837978SPeter.Dunlap@Sun.COM } 16847978SPeter.Dunlap@Sun.COM 16857978SPeter.Dunlap@Sun.COM status = idm_sorecvdata(ic, pdu); 16867978SPeter.Dunlap@Sun.COM 16877978SPeter.Dunlap@Sun.COM idm_task_rele(task); 16887978SPeter.Dunlap@Sun.COM 16897978SPeter.Dunlap@Sun.COM return (status); 16907978SPeter.Dunlap@Sun.COM } 16917978SPeter.Dunlap@Sun.COM 16927978SPeter.Dunlap@Sun.COM static uint32_t 16937978SPeter.Dunlap@Sun.COM idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength) 16947978SPeter.Dunlap@Sun.COM { 16957978SPeter.Dunlap@Sun.COM uint32_t buf_ro = ro - idb->idb_bufoffset; 16967978SPeter.Dunlap@Sun.COM uint32_t xfer_len = min(dlength, idb->idb_buflen - buf_ro); 16977978SPeter.Dunlap@Sun.COM 16987978SPeter.Dunlap@Sun.COM ASSERT(ro >= idb->idb_bufoffset); 16997978SPeter.Dunlap@Sun.COM 17007978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_base = 17017978SPeter.Dunlap@Sun.COM (caddr_t)idb->idb_buf + buf_ro; 17027978SPeter.Dunlap@Sun.COM pdu->isp_iov[pdu->isp_iovlen].iov_len = xfer_len; 17037978SPeter.Dunlap@Sun.COM pdu->isp_iovlen++; 17047978SPeter.Dunlap@Sun.COM 17057978SPeter.Dunlap@Sun.COM return (xfer_len); 17067978SPeter.Dunlap@Sun.COM } 17077978SPeter.Dunlap@Sun.COM 17087978SPeter.Dunlap@Sun.COM int 17097978SPeter.Dunlap@Sun.COM idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu) 17107978SPeter.Dunlap@Sun.COM { 17117978SPeter.Dunlap@Sun.COM pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP); 17127978SPeter.Dunlap@Sun.COM ASSERT(pdu->isp_data != NULL); 17137978SPeter.Dunlap@Sun.COM 17147978SPeter.Dunlap@Sun.COM pdu->isp_databuflen = pdu->isp_datalen; 17157978SPeter.Dunlap@Sun.COM pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data; 17167978SPeter.Dunlap@Sun.COM pdu->isp_iov[0].iov_len = pdu->isp_datalen; 17177978SPeter.Dunlap@Sun.COM pdu->isp_iovlen = 1; 17187978SPeter.Dunlap@Sun.COM /* 17197978SPeter.Dunlap@Sun.COM * Since we are associating a new data buffer with this received 17207978SPeter.Dunlap@Sun.COM * PDU we need to set a specific callback to free the data 17217978SPeter.Dunlap@Sun.COM * after the PDU is processed. 17227978SPeter.Dunlap@Sun.COM */ 17237978SPeter.Dunlap@Sun.COM pdu->isp_flags |= IDM_PDU_ADDL_DATA; 17247978SPeter.Dunlap@Sun.COM pdu->isp_callback = idm_sorx_addl_pdu_cb; 17257978SPeter.Dunlap@Sun.COM 17267978SPeter.Dunlap@Sun.COM return (idm_sorecvdata(ic, pdu)); 17277978SPeter.Dunlap@Sun.COM } 17287978SPeter.Dunlap@Sun.COM 17297978SPeter.Dunlap@Sun.COM void 17307978SPeter.Dunlap@Sun.COM idm_sorx_thread(void *arg) 17317978SPeter.Dunlap@Sun.COM { 17327978SPeter.Dunlap@Sun.COM boolean_t conn_failure = B_FALSE; 17337978SPeter.Dunlap@Sun.COM idm_conn_t *ic = (idm_conn_t *)arg; 17347978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 17357978SPeter.Dunlap@Sun.COM idm_pdu_t *pdu; 17367978SPeter.Dunlap@Sun.COM idm_status_t rc; 17377978SPeter.Dunlap@Sun.COM 17387978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 17397978SPeter.Dunlap@Sun.COM 17407978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 17417978SPeter.Dunlap@Sun.COM 17427978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 17437978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread_running = B_TRUE; 17447978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did; 17457978SPeter.Dunlap@Sun.COM cv_signal(&ic->ic_cv); 17467978SPeter.Dunlap@Sun.COM 17477978SPeter.Dunlap@Sun.COM while (so_conn->ic_rx_thread_running) { 17487978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_mutex); 17497978SPeter.Dunlap@Sun.COM 17507978SPeter.Dunlap@Sun.COM /* 17517978SPeter.Dunlap@Sun.COM * Get PDU with default header size (large enough for 17527978SPeter.Dunlap@Sun.COM * BHS plus any anticipated AHS). PDU from 17537978SPeter.Dunlap@Sun.COM * the cache will have all values set correctly 17547978SPeter.Dunlap@Sun.COM * for sockets RX including callback. 17557978SPeter.Dunlap@Sun.COM */ 17567978SPeter.Dunlap@Sun.COM pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP); 17577978SPeter.Dunlap@Sun.COM pdu->isp_ic = ic; 17587978SPeter.Dunlap@Sun.COM pdu->isp_flags = 0; 17597978SPeter.Dunlap@Sun.COM pdu->isp_transport_hdrlen = 0; 17607978SPeter.Dunlap@Sun.COM 17617978SPeter.Dunlap@Sun.COM if ((rc = idm_sorecvhdr(ic, pdu)) != 0) { 17627978SPeter.Dunlap@Sun.COM /* 17637978SPeter.Dunlap@Sun.COM * Call idm_pdu_complete so that we call the callback 17647978SPeter.Dunlap@Sun.COM * and ensure any memory allocated in idm_sorecvhdr 17657978SPeter.Dunlap@Sun.COM * gets freed up. 17667978SPeter.Dunlap@Sun.COM */ 17677978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_FAIL); 17687978SPeter.Dunlap@Sun.COM 17697978SPeter.Dunlap@Sun.COM /* 17707978SPeter.Dunlap@Sun.COM * If ic_rx_thread_running is still set then 17717978SPeter.Dunlap@Sun.COM * this is some kind of connection problem 17727978SPeter.Dunlap@Sun.COM * on the socket. In this case we want to 17737978SPeter.Dunlap@Sun.COM * generate an event. Otherwise some other 17747978SPeter.Dunlap@Sun.COM * thread closed the socket due to another 17757978SPeter.Dunlap@Sun.COM * issue in which case we don't need to 17767978SPeter.Dunlap@Sun.COM * generate an event. 17777978SPeter.Dunlap@Sun.COM */ 17787978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 17797978SPeter.Dunlap@Sun.COM if (so_conn->ic_rx_thread_running) { 17807978SPeter.Dunlap@Sun.COM conn_failure = B_TRUE; 17817978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread_running = B_FALSE; 17827978SPeter.Dunlap@Sun.COM } 17837978SPeter.Dunlap@Sun.COM 17847978SPeter.Dunlap@Sun.COM continue; 17857978SPeter.Dunlap@Sun.COM } 17867978SPeter.Dunlap@Sun.COM 17877978SPeter.Dunlap@Sun.COM /* 17887978SPeter.Dunlap@Sun.COM * Header has been read and validated. Now we need 17897978SPeter.Dunlap@Sun.COM * to read the PDU data payload (if present). SCSI data 17907978SPeter.Dunlap@Sun.COM * need to be transferred from the socket directly into 17917978SPeter.Dunlap@Sun.COM * the associated transfer buffer for the SCSI task. 17927978SPeter.Dunlap@Sun.COM */ 17937978SPeter.Dunlap@Sun.COM if (pdu->isp_datalen != 0) { 17947978SPeter.Dunlap@Sun.COM if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) || 17957978SPeter.Dunlap@Sun.COM (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) { 17967978SPeter.Dunlap@Sun.COM rc = idm_sorecv_scsidata(ic, pdu); 17977978SPeter.Dunlap@Sun.COM /* 17987978SPeter.Dunlap@Sun.COM * All SCSI errors are fatal to the 17997978SPeter.Dunlap@Sun.COM * connection right now since we have no 18007978SPeter.Dunlap@Sun.COM * place to put the data. What we need 18017978SPeter.Dunlap@Sun.COM * is some kind of sink to dispose of unwanted 18027978SPeter.Dunlap@Sun.COM * SCSI data. For example an invalid task tag 18037978SPeter.Dunlap@Sun.COM * should not kill the connection (although 18047978SPeter.Dunlap@Sun.COM * we may want to drop the connection). 18057978SPeter.Dunlap@Sun.COM */ 18067978SPeter.Dunlap@Sun.COM } else { 18077978SPeter.Dunlap@Sun.COM /* 18087978SPeter.Dunlap@Sun.COM * Not data PDUs so allocate a buffer for the 18097978SPeter.Dunlap@Sun.COM * data segment and read the remaining data. 18107978SPeter.Dunlap@Sun.COM */ 18117978SPeter.Dunlap@Sun.COM rc = idm_sorecv_nonscsidata(ic, pdu); 18127978SPeter.Dunlap@Sun.COM } 18137978SPeter.Dunlap@Sun.COM if (rc != 0) { 18147978SPeter.Dunlap@Sun.COM /* 18157978SPeter.Dunlap@Sun.COM * Call idm_pdu_complete so that we call the 18167978SPeter.Dunlap@Sun.COM * callback and ensure any memory allocated 18177978SPeter.Dunlap@Sun.COM * in idm_sorecvhdr gets freed up. 18187978SPeter.Dunlap@Sun.COM */ 18197978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_FAIL); 18207978SPeter.Dunlap@Sun.COM 18217978SPeter.Dunlap@Sun.COM /* 18227978SPeter.Dunlap@Sun.COM * If ic_rx_thread_running is still set then 18237978SPeter.Dunlap@Sun.COM * this is some kind of connection problem 18247978SPeter.Dunlap@Sun.COM * on the socket. In this case we want to 18257978SPeter.Dunlap@Sun.COM * generate an event. Otherwise some other 18267978SPeter.Dunlap@Sun.COM * thread closed the socket due to another 18277978SPeter.Dunlap@Sun.COM * issue in which case we don't need to 18287978SPeter.Dunlap@Sun.COM * generate an event. 18297978SPeter.Dunlap@Sun.COM */ 18307978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 18317978SPeter.Dunlap@Sun.COM if (so_conn->ic_rx_thread_running) { 18327978SPeter.Dunlap@Sun.COM conn_failure = B_TRUE; 18337978SPeter.Dunlap@Sun.COM so_conn->ic_rx_thread_running = B_FALSE; 18347978SPeter.Dunlap@Sun.COM } 18357978SPeter.Dunlap@Sun.COM continue; 18367978SPeter.Dunlap@Sun.COM } 18377978SPeter.Dunlap@Sun.COM } 18387978SPeter.Dunlap@Sun.COM 18397978SPeter.Dunlap@Sun.COM /* 18407978SPeter.Dunlap@Sun.COM * Process RX PDU 18417978SPeter.Dunlap@Sun.COM */ 18427978SPeter.Dunlap@Sun.COM idm_pdu_rx(ic, pdu); 18437978SPeter.Dunlap@Sun.COM 18447978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 18457978SPeter.Dunlap@Sun.COM } 18467978SPeter.Dunlap@Sun.COM 18477978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_mutex); 18487978SPeter.Dunlap@Sun.COM 18497978SPeter.Dunlap@Sun.COM /* 18507978SPeter.Dunlap@Sun.COM * If we dropped out of the RX processing loop because of 18517978SPeter.Dunlap@Sun.COM * a socket problem or other connection failure (including 18527978SPeter.Dunlap@Sun.COM * digest errors) then we need to generate a state machine 18537978SPeter.Dunlap@Sun.COM * event to shut the connection down. 18547978SPeter.Dunlap@Sun.COM * If the state machine is already in, for example, INIT_ERROR, this 18557978SPeter.Dunlap@Sun.COM * event will get dropped, and the TX thread will never be notified 18567978SPeter.Dunlap@Sun.COM * to shut down. To be safe, we'll just notify it here. 18577978SPeter.Dunlap@Sun.COM */ 18587978SPeter.Dunlap@Sun.COM if (conn_failure) { 18597978SPeter.Dunlap@Sun.COM if (so_conn->ic_tx_thread_running) { 18607978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread_running = B_FALSE; 18617978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 18627978SPeter.Dunlap@Sun.COM cv_signal(&so_conn->ic_tx_cv); 18637978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 18647978SPeter.Dunlap@Sun.COM } 18657978SPeter.Dunlap@Sun.COM 18667978SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_TRANSPORT_FAIL, rc); 18677978SPeter.Dunlap@Sun.COM } 18687978SPeter.Dunlap@Sun.COM 18697978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 18707978SPeter.Dunlap@Sun.COM 18717978SPeter.Dunlap@Sun.COM thread_exit(); 18727978SPeter.Dunlap@Sun.COM } 18737978SPeter.Dunlap@Sun.COM 18747978SPeter.Dunlap@Sun.COM /* 18757978SPeter.Dunlap@Sun.COM * idm_so_tx 18767978SPeter.Dunlap@Sun.COM * 18777978SPeter.Dunlap@Sun.COM * This is the implementation of idm_transport_ops_t's it_tx_pdu entry 18787978SPeter.Dunlap@Sun.COM * point. By definition, it is supposed to be fast. So, simply queue 18797978SPeter.Dunlap@Sun.COM * the entry and return. The real work is done by idm_i_so_tx() via 18807978SPeter.Dunlap@Sun.COM * idm_sotx_thread(). 18817978SPeter.Dunlap@Sun.COM */ 18827978SPeter.Dunlap@Sun.COM 18837978SPeter.Dunlap@Sun.COM static void 18847978SPeter.Dunlap@Sun.COM idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu) 18857978SPeter.Dunlap@Sun.COM { 18867978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn = ic->ic_transport_private; 18877978SPeter.Dunlap@Sun.COM 18887978SPeter.Dunlap@Sun.COM ASSERT(pdu->isp_ic == ic); 18897978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 18907978SPeter.Dunlap@Sun.COM 18917978SPeter.Dunlap@Sun.COM if (!so_conn->ic_tx_thread_running) { 18927978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 18937978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, IDM_STATUS_ABORTED); 18947978SPeter.Dunlap@Sun.COM return; 18957978SPeter.Dunlap@Sun.COM } 18967978SPeter.Dunlap@Sun.COM 18977978SPeter.Dunlap@Sun.COM list_insert_tail(&so_conn->ic_tx_list, (void *)pdu); 18987978SPeter.Dunlap@Sun.COM cv_signal(&so_conn->ic_tx_cv); 18997978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 19007978SPeter.Dunlap@Sun.COM } 19017978SPeter.Dunlap@Sun.COM 19027978SPeter.Dunlap@Sun.COM static idm_status_t 19037978SPeter.Dunlap@Sun.COM idm_i_so_tx(idm_pdu_t *pdu) 19047978SPeter.Dunlap@Sun.COM { 19057978SPeter.Dunlap@Sun.COM idm_conn_t *ic = pdu->isp_ic; 19067978SPeter.Dunlap@Sun.COM idm_status_t status = IDM_STATUS_SUCCESS; 19077978SPeter.Dunlap@Sun.COM uint8_t pad[ISCSI_PAD_WORD_LEN]; 19087978SPeter.Dunlap@Sun.COM int pad_len; 19097978SPeter.Dunlap@Sun.COM uint32_t hdr_digest_crc; 19107978SPeter.Dunlap@Sun.COM uint32_t data_digest_crc = 0; 19117978SPeter.Dunlap@Sun.COM int total_len = 0; 19127978SPeter.Dunlap@Sun.COM int iovlen = 0; 19137978SPeter.Dunlap@Sun.COM struct iovec iov[6]; 19147978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 19157978SPeter.Dunlap@Sun.COM 19167978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 19177978SPeter.Dunlap@Sun.COM 19187978SPeter.Dunlap@Sun.COM /* Setup BHS */ 19197978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)pdu->isp_hdr; 19207978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = pdu->isp_hdrlen; 19217978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 19227978SPeter.Dunlap@Sun.COM iovlen++; 19237978SPeter.Dunlap@Sun.COM 19247978SPeter.Dunlap@Sun.COM /* Setup header digest */ 19257978SPeter.Dunlap@Sun.COM if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) && 19267978SPeter.Dunlap@Sun.COM (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) { 19277978SPeter.Dunlap@Sun.COM hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen); 19287978SPeter.Dunlap@Sun.COM 19297978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc; 19307978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = sizeof (hdr_digest_crc); 19317978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 19327978SPeter.Dunlap@Sun.COM iovlen++; 19337978SPeter.Dunlap@Sun.COM } 19347978SPeter.Dunlap@Sun.COM 19357978SPeter.Dunlap@Sun.COM /* Setup the data */ 19367978SPeter.Dunlap@Sun.COM if (pdu->isp_datalen) { 19377978SPeter.Dunlap@Sun.COM idm_task_t *idt; 19387978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 19397978SPeter.Dunlap@Sun.COM iscsi_data_hdr_t *ihp; 19407978SPeter.Dunlap@Sun.COM ihp = (iscsi_data_hdr_t *)pdu->isp_hdr; 19417978SPeter.Dunlap@Sun.COM /* Write of immediate data */ 19427978SPeter.Dunlap@Sun.COM if (ic->ic_ffp && 19437978SPeter.Dunlap@Sun.COM (ihp->opcode == ISCSI_OP_SCSI_CMD || 19447978SPeter.Dunlap@Sun.COM ihp->opcode == ISCSI_OP_SCSI_DATA)) { 19457978SPeter.Dunlap@Sun.COM idt = idm_task_find(ic, ihp->itt, ihp->ttt); 19467978SPeter.Dunlap@Sun.COM if (idt) { 19477978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 19487978SPeter.Dunlap@Sun.COM idb = idm_buf_find(&idt->idt_outbufv, 0); 19497978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 1950*9162SPeter.Dunlap@Sun.COM /* 1951*9162SPeter.Dunlap@Sun.COM * If the initiator call to idm_buf_alloc 1952*9162SPeter.Dunlap@Sun.COM * failed then we can get to this point 1953*9162SPeter.Dunlap@Sun.COM * without a bound buffer. The associated 1954*9162SPeter.Dunlap@Sun.COM * connection failure will clean things up 1955*9162SPeter.Dunlap@Sun.COM * later. It would be nice to come up with 1956*9162SPeter.Dunlap@Sun.COM * a cleaner way to handle this. In 1957*9162SPeter.Dunlap@Sun.COM * particular it seems absurd to look up 1958*9162SPeter.Dunlap@Sun.COM * the task and the buffer just to update 1959*9162SPeter.Dunlap@Sun.COM * this counter. 1960*9162SPeter.Dunlap@Sun.COM */ 1961*9162SPeter.Dunlap@Sun.COM if (idb) 1962*9162SPeter.Dunlap@Sun.COM idb->idb_xfer_len += pdu->isp_datalen; 1963*9162SPeter.Dunlap@Sun.COM idm_task_rele(idt); 19647978SPeter.Dunlap@Sun.COM } 19657978SPeter.Dunlap@Sun.COM } 19667978SPeter.Dunlap@Sun.COM 19677978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)pdu->isp_data; 19687978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = pdu->isp_datalen; 19697978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 19707978SPeter.Dunlap@Sun.COM iovlen++; 19717978SPeter.Dunlap@Sun.COM } 19727978SPeter.Dunlap@Sun.COM 19737978SPeter.Dunlap@Sun.COM /* Setup the data pad if necessary */ 19747978SPeter.Dunlap@Sun.COM pad_len = ((ISCSI_PAD_WORD_LEN - 19757978SPeter.Dunlap@Sun.COM (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) & 19767978SPeter.Dunlap@Sun.COM (ISCSI_PAD_WORD_LEN - 1)); 19777978SPeter.Dunlap@Sun.COM 19787978SPeter.Dunlap@Sun.COM if (pad_len) { 19797978SPeter.Dunlap@Sun.COM bzero(pad, sizeof (pad)); 19807978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (void *)&pad; 19817978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = pad_len; 19827978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 19837978SPeter.Dunlap@Sun.COM iovlen++; 19847978SPeter.Dunlap@Sun.COM } 19857978SPeter.Dunlap@Sun.COM 19867978SPeter.Dunlap@Sun.COM /* 19877978SPeter.Dunlap@Sun.COM * Setup the data digest if enabled. Data-digest is not sent 19887978SPeter.Dunlap@Sun.COM * for login-phase PDUs. 19897978SPeter.Dunlap@Sun.COM */ 19907978SPeter.Dunlap@Sun.COM if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) && 19917978SPeter.Dunlap@Sun.COM ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) && 19927978SPeter.Dunlap@Sun.COM (pdu->isp_datalen || pad_len)) { 19937978SPeter.Dunlap@Sun.COM /* 19947978SPeter.Dunlap@Sun.COM * RFC3720/10.2.3: A zero-length Data Segment also 19957978SPeter.Dunlap@Sun.COM * implies a zero-length data digest. 19967978SPeter.Dunlap@Sun.COM */ 19977978SPeter.Dunlap@Sun.COM if (pdu->isp_datalen) { 19987978SPeter.Dunlap@Sun.COM data_digest_crc = idm_crc32c(pdu->isp_data, 19997978SPeter.Dunlap@Sun.COM pdu->isp_datalen); 20007978SPeter.Dunlap@Sun.COM } 20017978SPeter.Dunlap@Sun.COM if (pad_len) { 20027978SPeter.Dunlap@Sun.COM data_digest_crc = idm_crc32c_continued(&pad, 20037978SPeter.Dunlap@Sun.COM pad_len, data_digest_crc); 20047978SPeter.Dunlap@Sun.COM } 20057978SPeter.Dunlap@Sun.COM 20067978SPeter.Dunlap@Sun.COM iov[iovlen].iov_base = (caddr_t)&data_digest_crc; 20077978SPeter.Dunlap@Sun.COM iov[iovlen].iov_len = sizeof (data_digest_crc); 20087978SPeter.Dunlap@Sun.COM total_len += iov[iovlen].iov_len; 20097978SPeter.Dunlap@Sun.COM iovlen++; 20107978SPeter.Dunlap@Sun.COM } 20117978SPeter.Dunlap@Sun.COM 20127978SPeter.Dunlap@Sun.COM /* Transmit the PDU */ 20137978SPeter.Dunlap@Sun.COM if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen, 20147978SPeter.Dunlap@Sun.COM total_len) != 0) { 20157978SPeter.Dunlap@Sun.COM /* Set error status */ 20167978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 20177978SPeter.Dunlap@Sun.COM "idm_so_tx: failed to transmit the PDU, so: %p ic: %p " 20187978SPeter.Dunlap@Sun.COM "data: %p", (void *) so_conn->ic_so, (void *) ic, 20197978SPeter.Dunlap@Sun.COM (void *) pdu->isp_data); 20207978SPeter.Dunlap@Sun.COM status = IDM_STATUS_IO; 20217978SPeter.Dunlap@Sun.COM } 20227978SPeter.Dunlap@Sun.COM 20237978SPeter.Dunlap@Sun.COM /* 20247978SPeter.Dunlap@Sun.COM * Success does not mean that the PDU actually reached the 20257978SPeter.Dunlap@Sun.COM * remote node since it could get dropped along the way. 20267978SPeter.Dunlap@Sun.COM */ 20277978SPeter.Dunlap@Sun.COM idm_pdu_complete(pdu, status); 20287978SPeter.Dunlap@Sun.COM 20297978SPeter.Dunlap@Sun.COM return (status); 20307978SPeter.Dunlap@Sun.COM } 20317978SPeter.Dunlap@Sun.COM 20327978SPeter.Dunlap@Sun.COM /* 20337978SPeter.Dunlap@Sun.COM * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the 20347978SPeter.Dunlap@Sun.COM * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength, 20357978SPeter.Dunlap@Sun.COM * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN. 20367978SPeter.Dunlap@Sun.COM * A target can invoke this function multiple times for a single read command 20377978SPeter.Dunlap@Sun.COM * (identified by the same ITT) to split the input into several sequences. 20387978SPeter.Dunlap@Sun.COM * 20397978SPeter.Dunlap@Sun.COM * DataSN starts with 0 for the first data PDU of an input command and advances 20407978SPeter.Dunlap@Sun.COM * by 1 for each subsequent data PDU. Each sequence will have its own F bit, 20417978SPeter.Dunlap@Sun.COM * which is set to 1 for the last data PDU of a sequence. 20427978SPeter.Dunlap@Sun.COM * 20437978SPeter.Dunlap@Sun.COM * Scope for Prototype build: 20447978SPeter.Dunlap@Sun.COM * The data PDUs within a sequence will be sent in order with the buffer offset 20457978SPeter.Dunlap@Sun.COM * in increasing order. i.e. initiator and target must have negotiated the 20467978SPeter.Dunlap@Sun.COM * "DataPDUInOrder" to "Yes". The order between sequences is not enforced. 20477978SPeter.Dunlap@Sun.COM * 20487978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex 20497978SPeter.Dunlap@Sun.COM */ 20507978SPeter.Dunlap@Sun.COM static idm_status_t 20517978SPeter.Dunlap@Sun.COM idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb) 20527978SPeter.Dunlap@Sun.COM { 20537978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn = idb->idb_ic->ic_transport_private; 20547978SPeter.Dunlap@Sun.COM idm_pdu_t tmppdu; 20557978SPeter.Dunlap@Sun.COM 20567978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 20577978SPeter.Dunlap@Sun.COM 20587978SPeter.Dunlap@Sun.COM /* 20597978SPeter.Dunlap@Sun.COM * Put the idm_buf_t on the tx queue. It will be transmitted by 20607978SPeter.Dunlap@Sun.COM * idm_sotx_thread. 20617978SPeter.Dunlap@Sun.COM */ 20627978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 20637978SPeter.Dunlap@Sun.COM 20647978SPeter.Dunlap@Sun.COM if (!so_conn->ic_tx_thread_running) { 20657978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 20667978SPeter.Dunlap@Sun.COM /* 20677978SPeter.Dunlap@Sun.COM * Don't release idt->idt_mutex since we're supposed to hold 20687978SPeter.Dunlap@Sun.COM * in when calling idm_buf_tx_to_ini_done 20697978SPeter.Dunlap@Sun.COM */ 20707978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED); 20717978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 20727978SPeter.Dunlap@Sun.COM } 20737978SPeter.Dunlap@Sun.COM 20747978SPeter.Dunlap@Sun.COM /* 20757978SPeter.Dunlap@Sun.COM * Build a template for the data PDU headers we will use so that 20767978SPeter.Dunlap@Sun.COM * the SN values will stay consistent with other PDU's we are 20777978SPeter.Dunlap@Sun.COM * transmitting like R2T and SCSI status. 20787978SPeter.Dunlap@Sun.COM */ 20797978SPeter.Dunlap@Sun.COM bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t)); 20807978SPeter.Dunlap@Sun.COM tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl; 20817978SPeter.Dunlap@Sun.COM (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu, 20827978SPeter.Dunlap@Sun.COM ISCSI_OP_SCSI_DATA_RSP); 20837978SPeter.Dunlap@Sun.COM idb->idb_tx_thread = B_TRUE; 20847978SPeter.Dunlap@Sun.COM list_insert_tail(&so_conn->ic_tx_list, (void *)idb); 20857978SPeter.Dunlap@Sun.COM cv_signal(&so_conn->ic_tx_cv); 20867978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 20877978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 20887978SPeter.Dunlap@Sun.COM 20897978SPeter.Dunlap@Sun.COM /* 20907978SPeter.Dunlap@Sun.COM * Returning success here indicates the transfer was successfully 20917978SPeter.Dunlap@Sun.COM * dispatched -- it does not mean that the transfer completed 20927978SPeter.Dunlap@Sun.COM * successfully. 20937978SPeter.Dunlap@Sun.COM */ 20947978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 20957978SPeter.Dunlap@Sun.COM } 20967978SPeter.Dunlap@Sun.COM 20977978SPeter.Dunlap@Sun.COM /* 20987978SPeter.Dunlap@Sun.COM * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the 20997978SPeter.Dunlap@Sun.COM * data blocks it is ready to receive from the initiator in response to a WRITE 21007978SPeter.Dunlap@Sun.COM * SCSI command. The target iSCSI layer passes the information about the desired 21017978SPeter.Dunlap@Sun.COM * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer 21027978SPeter.Dunlap@Sun.COM * offset and datalen are passed via the 'idb' argument. 21037978SPeter.Dunlap@Sun.COM * 21047978SPeter.Dunlap@Sun.COM * Scope for Prototype build: 21057978SPeter.Dunlap@Sun.COM * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have 21067978SPeter.Dunlap@Sun.COM * negotiated the "InitialR2T" to "Yes". 21077978SPeter.Dunlap@Sun.COM * 21087978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex 21097978SPeter.Dunlap@Sun.COM */ 21107978SPeter.Dunlap@Sun.COM static idm_status_t 21117978SPeter.Dunlap@Sun.COM idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb) 21127978SPeter.Dunlap@Sun.COM { 21137978SPeter.Dunlap@Sun.COM idm_pdu_t *pdu; 21147978SPeter.Dunlap@Sun.COM iscsi_rtt_hdr_t *rtt; 21157978SPeter.Dunlap@Sun.COM 21167978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 21177978SPeter.Dunlap@Sun.COM 21187978SPeter.Dunlap@Sun.COM pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP); 21197978SPeter.Dunlap@Sun.COM pdu->isp_ic = idt->idt_ic; 21207978SPeter.Dunlap@Sun.COM bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t)); 21217978SPeter.Dunlap@Sun.COM 21227978SPeter.Dunlap@Sun.COM /* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */ 21237978SPeter.Dunlap@Sun.COM (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP); 21247978SPeter.Dunlap@Sun.COM 21257978SPeter.Dunlap@Sun.COM /* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */ 21267978SPeter.Dunlap@Sun.COM rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr); 21277978SPeter.Dunlap@Sun.COM 21287978SPeter.Dunlap@Sun.COM rtt->opcode = ISCSI_OP_RTT_RSP; 21297978SPeter.Dunlap@Sun.COM rtt->flags = ISCSI_FLAG_FINAL; 21307978SPeter.Dunlap@Sun.COM rtt->data_offset = htonl(idb->idb_bufoffset); 21317978SPeter.Dunlap@Sun.COM rtt->data_length = htonl(idb->idb_xfer_len); 21327978SPeter.Dunlap@Sun.COM rtt->rttsn = htonl(idt->idt_exp_rttsn++); 21337978SPeter.Dunlap@Sun.COM 21347978SPeter.Dunlap@Sun.COM /* Keep track of buffer offsets */ 21357978SPeter.Dunlap@Sun.COM idb->idb_exp_offset = idb->idb_bufoffset; 21367978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 21377978SPeter.Dunlap@Sun.COM 21387978SPeter.Dunlap@Sun.COM /* 21398607SJames.Moore@Sun.COM * Transmit the PDU. 21407978SPeter.Dunlap@Sun.COM */ 21418607SJames.Moore@Sun.COM idm_pdu_tx(pdu); 21427978SPeter.Dunlap@Sun.COM 21437978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 21447978SPeter.Dunlap@Sun.COM } 21457978SPeter.Dunlap@Sun.COM 21467978SPeter.Dunlap@Sun.COM static idm_status_t 21477978SPeter.Dunlap@Sun.COM idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen) 21487978SPeter.Dunlap@Sun.COM { 21497978SPeter.Dunlap@Sun.COM idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP); 21507978SPeter.Dunlap@Sun.COM if (idb->idb_buf == NULL) { 21517978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_NOTE, 21527978SPeter.Dunlap@Sun.COM "idm_so_buf_alloc: failed buffer allocation"); 21537978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 21547978SPeter.Dunlap@Sun.COM } 21557978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 21567978SPeter.Dunlap@Sun.COM } 21577978SPeter.Dunlap@Sun.COM 21587978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 21597978SPeter.Dunlap@Sun.COM static idm_status_t 21607978SPeter.Dunlap@Sun.COM idm_so_buf_setup(idm_buf_t *idb) 21617978SPeter.Dunlap@Sun.COM { 2162*9162SPeter.Dunlap@Sun.COM /* Ensure bufalloc'd flag is unset */ 2163*9162SPeter.Dunlap@Sun.COM idb->idb_bufalloc = B_FALSE; 2164*9162SPeter.Dunlap@Sun.COM 21657978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 21667978SPeter.Dunlap@Sun.COM } 21677978SPeter.Dunlap@Sun.COM 21687978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 21697978SPeter.Dunlap@Sun.COM static void 21707978SPeter.Dunlap@Sun.COM idm_so_buf_teardown(idm_buf_t *idb) 21717978SPeter.Dunlap@Sun.COM { 21727978SPeter.Dunlap@Sun.COM /* nothing to do here */ 21737978SPeter.Dunlap@Sun.COM } 21747978SPeter.Dunlap@Sun.COM 21757978SPeter.Dunlap@Sun.COM static void 21767978SPeter.Dunlap@Sun.COM idm_so_buf_free(idm_buf_t *idb) 21777978SPeter.Dunlap@Sun.COM { 21787978SPeter.Dunlap@Sun.COM kmem_free(idb->idb_buf, idb->idb_buflen); 21797978SPeter.Dunlap@Sun.COM } 21807978SPeter.Dunlap@Sun.COM 2181*9162SPeter.Dunlap@Sun.COM static void 2182*9162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb, 2183*9162SPeter.Dunlap@Sun.COM uint32_t offset, uint32_t length) 2184*9162SPeter.Dunlap@Sun.COM { 2185*9162SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn = ic->ic_transport_private; 2186*9162SPeter.Dunlap@Sun.COM idm_pdu_t tmppdu; 2187*9162SPeter.Dunlap@Sun.COM idm_buf_t *rtt_buf; 2188*9162SPeter.Dunlap@Sun.COM 2189*9162SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 2190*9162SPeter.Dunlap@Sun.COM 2191*9162SPeter.Dunlap@Sun.COM /* 2192*9162SPeter.Dunlap@Sun.COM * Allocate a buffer to represent the RTT transfer. We could further 2193*9162SPeter.Dunlap@Sun.COM * optimize this by allocating the buffers internally from an rtt 2194*9162SPeter.Dunlap@Sun.COM * specific buffer cache since this is socket-specific code but for 2195*9162SPeter.Dunlap@Sun.COM * now we will keep it simple. 2196*9162SPeter.Dunlap@Sun.COM */ 2197*9162SPeter.Dunlap@Sun.COM rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length); 2198*9162SPeter.Dunlap@Sun.COM if (rtt_buf == NULL) { 2199*9162SPeter.Dunlap@Sun.COM /* 2200*9162SPeter.Dunlap@Sun.COM * If we're in FFP then the failure was likely a resource 2201*9162SPeter.Dunlap@Sun.COM * allocation issue and we should close the connection by 2202*9162SPeter.Dunlap@Sun.COM * sending a CE_TRANSPORT_FAIL event. 2203*9162SPeter.Dunlap@Sun.COM * 2204*9162SPeter.Dunlap@Sun.COM * If we're not in FFP then idm_buf_alloc will always 2205*9162SPeter.Dunlap@Sun.COM * fail and the state is transitioning to "complete" anyway 2206*9162SPeter.Dunlap@Sun.COM * so we won't bother to send an event. 2207*9162SPeter.Dunlap@Sun.COM */ 2208*9162SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 2209*9162SPeter.Dunlap@Sun.COM if (ic->ic_ffp) 2210*9162SPeter.Dunlap@Sun.COM idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, 2211*9162SPeter.Dunlap@Sun.COM NULL, CT_NONE); 2212*9162SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 2213*9162SPeter.Dunlap@Sun.COM return; 2214*9162SPeter.Dunlap@Sun.COM } 2215*9162SPeter.Dunlap@Sun.COM 2216*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_buf_cb = NULL; 2217*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_cb_arg = NULL; 2218*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_bufoffset = offset; 2219*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_xfer_len = length; 2220*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_ic = idt->idt_ic; 2221*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_task_binding = idt; 2222*9162SPeter.Dunlap@Sun.COM 2223*9162SPeter.Dunlap@Sun.COM /* 2224*9162SPeter.Dunlap@Sun.COM * Put the idm_buf_t on the tx queue. It will be transmitted by 2225*9162SPeter.Dunlap@Sun.COM * idm_sotx_thread. 2226*9162SPeter.Dunlap@Sun.COM */ 2227*9162SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 2228*9162SPeter.Dunlap@Sun.COM 2229*9162SPeter.Dunlap@Sun.COM if (!so_conn->ic_tx_thread_running) { 2230*9162SPeter.Dunlap@Sun.COM idm_buf_free(rtt_buf); 2231*9162SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 2232*9162SPeter.Dunlap@Sun.COM return; 2233*9162SPeter.Dunlap@Sun.COM } 2234*9162SPeter.Dunlap@Sun.COM 2235*9162SPeter.Dunlap@Sun.COM /* 2236*9162SPeter.Dunlap@Sun.COM * This new buffer represents an additional reference on the task 2237*9162SPeter.Dunlap@Sun.COM */ 2238*9162SPeter.Dunlap@Sun.COM idm_task_hold(idt); 2239*9162SPeter.Dunlap@Sun.COM 2240*9162SPeter.Dunlap@Sun.COM /* 2241*9162SPeter.Dunlap@Sun.COM * Build a template for the data PDU headers we will use so that 2242*9162SPeter.Dunlap@Sun.COM * the SN values will stay consistent with other PDU's we are 2243*9162SPeter.Dunlap@Sun.COM * transmitting like R2T and SCSI status. 2244*9162SPeter.Dunlap@Sun.COM */ 2245*9162SPeter.Dunlap@Sun.COM bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t)); 2246*9162SPeter.Dunlap@Sun.COM tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl; 2247*9162SPeter.Dunlap@Sun.COM (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu, 2248*9162SPeter.Dunlap@Sun.COM ISCSI_OP_SCSI_DATA); 2249*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_tx_thread = B_TRUE; 2250*9162SPeter.Dunlap@Sun.COM rtt_buf->idb_in_transport = B_TRUE; 2251*9162SPeter.Dunlap@Sun.COM list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf); 2252*9162SPeter.Dunlap@Sun.COM cv_signal(&so_conn->ic_tx_cv); 2253*9162SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 2254*9162SPeter.Dunlap@Sun.COM } 2255*9162SPeter.Dunlap@Sun.COM 2256*9162SPeter.Dunlap@Sun.COM static void 2257*9162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb) 2258*9162SPeter.Dunlap@Sun.COM { 2259*9162SPeter.Dunlap@Sun.COM /* 2260*9162SPeter.Dunlap@Sun.COM * Don't worry about status -- we assume any error handling 2261*9162SPeter.Dunlap@Sun.COM * is performed by the caller (idm_sotx_thread). 2262*9162SPeter.Dunlap@Sun.COM */ 2263*9162SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_FALSE; 2264*9162SPeter.Dunlap@Sun.COM idm_task_rele(idt); 2265*9162SPeter.Dunlap@Sun.COM idm_buf_free(idb); 2266*9162SPeter.Dunlap@Sun.COM } 2267*9162SPeter.Dunlap@Sun.COM 2268*9162SPeter.Dunlap@Sun.COM static idm_status_t 2269*9162SPeter.Dunlap@Sun.COM idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb, 22707978SPeter.Dunlap@Sun.COM uint32_t buf_region_offset, uint32_t buf_region_length) 22717978SPeter.Dunlap@Sun.COM { 22727978SPeter.Dunlap@Sun.COM idm_conn_t *ic; 22737978SPeter.Dunlap@Sun.COM uint32_t max_dataseglen; 22747978SPeter.Dunlap@Sun.COM size_t remainder, chunk; 22757978SPeter.Dunlap@Sun.COM uint32_t data_offset = buf_region_offset; 22767978SPeter.Dunlap@Sun.COM iscsi_data_hdr_t *bhs; 22777978SPeter.Dunlap@Sun.COM idm_pdu_t *pdu; 2278*9162SPeter.Dunlap@Sun.COM idm_status_t tx_status; 22797978SPeter.Dunlap@Sun.COM 22807978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 22817978SPeter.Dunlap@Sun.COM 22827978SPeter.Dunlap@Sun.COM ic = idt->idt_ic; 22837978SPeter.Dunlap@Sun.COM 22847978SPeter.Dunlap@Sun.COM max_dataseglen = 8192; /* Need value from login negotiation */ 22857978SPeter.Dunlap@Sun.COM remainder = buf_region_length; 22867978SPeter.Dunlap@Sun.COM 22877978SPeter.Dunlap@Sun.COM while (remainder) { 22887978SPeter.Dunlap@Sun.COM if (idt->idt_state != TASK_ACTIVE) { 22897978SPeter.Dunlap@Sun.COM ASSERT((idt->idt_state != TASK_IDLE) && 22907978SPeter.Dunlap@Sun.COM (idt->idt_state != TASK_COMPLETE)); 22917978SPeter.Dunlap@Sun.COM return (IDM_STATUS_ABORTED); 22927978SPeter.Dunlap@Sun.COM } 22937978SPeter.Dunlap@Sun.COM 22947978SPeter.Dunlap@Sun.COM /* check to see if we need to chunk the data */ 22957978SPeter.Dunlap@Sun.COM if (remainder > max_dataseglen) { 22967978SPeter.Dunlap@Sun.COM chunk = max_dataseglen; 22977978SPeter.Dunlap@Sun.COM } else { 22987978SPeter.Dunlap@Sun.COM chunk = remainder; 22997978SPeter.Dunlap@Sun.COM } 23007978SPeter.Dunlap@Sun.COM 23017978SPeter.Dunlap@Sun.COM /* Data PDU headers will always be sizeof (iscsi_hdr_t) */ 23027978SPeter.Dunlap@Sun.COM pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP); 23037978SPeter.Dunlap@Sun.COM pdu->isp_ic = ic; 23047978SPeter.Dunlap@Sun.COM 23057978SPeter.Dunlap@Sun.COM /* 2306*9162SPeter.Dunlap@Sun.COM * We've already built a build a header template 23077978SPeter.Dunlap@Sun.COM * to use during the transfer. Use this template so that 23087978SPeter.Dunlap@Sun.COM * the SN values stay consistent with any unrelated PDU's 23097978SPeter.Dunlap@Sun.COM * being transmitted. 23107978SPeter.Dunlap@Sun.COM */ 2311*9162SPeter.Dunlap@Sun.COM bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr, 2312*9162SPeter.Dunlap@Sun.COM sizeof (iscsi_hdr_t)); 23137978SPeter.Dunlap@Sun.COM 23147978SPeter.Dunlap@Sun.COM /* 23157978SPeter.Dunlap@Sun.COM * Set DataSN, data offset, and flags in BHS 23167978SPeter.Dunlap@Sun.COM * For the prototype build, A = 0, S = 0, U = 0 23177978SPeter.Dunlap@Sun.COM */ 23187978SPeter.Dunlap@Sun.COM bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr); 23197978SPeter.Dunlap@Sun.COM 23207978SPeter.Dunlap@Sun.COM bhs->datasn = htonl(idt->idt_exp_datasn++); 23217978SPeter.Dunlap@Sun.COM 23227978SPeter.Dunlap@Sun.COM hton24(bhs->dlength, chunk); 23237978SPeter.Dunlap@Sun.COM bhs->offset = htonl(idb->idb_bufoffset + data_offset); 23247978SPeter.Dunlap@Sun.COM 23257978SPeter.Dunlap@Sun.COM if (chunk == remainder) { 23267978SPeter.Dunlap@Sun.COM bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */ 23277978SPeter.Dunlap@Sun.COM } 23287978SPeter.Dunlap@Sun.COM 23297978SPeter.Dunlap@Sun.COM /* setup data */ 23307978SPeter.Dunlap@Sun.COM pdu->isp_data = (uint8_t *)idb->idb_buf + data_offset; 23317978SPeter.Dunlap@Sun.COM pdu->isp_datalen = (uint_t)chunk; 23327978SPeter.Dunlap@Sun.COM remainder -= chunk; 23337978SPeter.Dunlap@Sun.COM data_offset += chunk; 23347978SPeter.Dunlap@Sun.COM 23357978SPeter.Dunlap@Sun.COM /* 23367978SPeter.Dunlap@Sun.COM * Now that we're done working with idt_exp_datasn, 23377978SPeter.Dunlap@Sun.COM * idt->idt_state and idb->idb_bufoffset we can release 23387978SPeter.Dunlap@Sun.COM * the task lock -- don't want to hold it across the 23397978SPeter.Dunlap@Sun.COM * call to idm_i_so_tx since we could block. 23407978SPeter.Dunlap@Sun.COM */ 23417978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 23427978SPeter.Dunlap@Sun.COM 23437978SPeter.Dunlap@Sun.COM /* 23447978SPeter.Dunlap@Sun.COM * Transmit the PDU. Call the internal routine directly 23457978SPeter.Dunlap@Sun.COM * as there is already implicit ordering. 23467978SPeter.Dunlap@Sun.COM */ 2347*9162SPeter.Dunlap@Sun.COM if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) { 2348*9162SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 2349*9162SPeter.Dunlap@Sun.COM return (tx_status); 2350*9162SPeter.Dunlap@Sun.COM } 23517978SPeter.Dunlap@Sun.COM 23527978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 2353*9162SPeter.Dunlap@Sun.COM idt->idt_tx_bytes += chunk; 23547978SPeter.Dunlap@Sun.COM } 23557978SPeter.Dunlap@Sun.COM 23567978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 23577978SPeter.Dunlap@Sun.COM } 23587978SPeter.Dunlap@Sun.COM 23597978SPeter.Dunlap@Sun.COM /* 23607978SPeter.Dunlap@Sun.COM * TX PDU cache 23617978SPeter.Dunlap@Sun.COM */ 23627978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 23637978SPeter.Dunlap@Sun.COM int 23647978SPeter.Dunlap@Sun.COM idm_sotx_pdu_constructor(void *hdl, void *arg, int flags) 23657978SPeter.Dunlap@Sun.COM { 23667978SPeter.Dunlap@Sun.COM idm_pdu_t *pdu = hdl; 23677978SPeter.Dunlap@Sun.COM 23687978SPeter.Dunlap@Sun.COM bzero(pdu, sizeof (idm_pdu_t)); 23697978SPeter.Dunlap@Sun.COM pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */ 23707978SPeter.Dunlap@Sun.COM pdu->isp_hdrlen = sizeof (iscsi_hdr_t); 23717978SPeter.Dunlap@Sun.COM pdu->isp_callback = idm_sotx_cache_pdu_cb; 23727978SPeter.Dunlap@Sun.COM pdu->isp_magic = IDM_PDU_MAGIC; 23737978SPeter.Dunlap@Sun.COM bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t)); 23747978SPeter.Dunlap@Sun.COM 23757978SPeter.Dunlap@Sun.COM return (0); 23767978SPeter.Dunlap@Sun.COM } 23777978SPeter.Dunlap@Sun.COM 23787978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 23797978SPeter.Dunlap@Sun.COM void 23807978SPeter.Dunlap@Sun.COM idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 23817978SPeter.Dunlap@Sun.COM { 23827978SPeter.Dunlap@Sun.COM /* reset values between use */ 23837978SPeter.Dunlap@Sun.COM pdu->isp_datalen = 0; 23847978SPeter.Dunlap@Sun.COM 23857978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_sotx_pdu_cache, pdu); 23867978SPeter.Dunlap@Sun.COM } 23877978SPeter.Dunlap@Sun.COM 23887978SPeter.Dunlap@Sun.COM /* 23897978SPeter.Dunlap@Sun.COM * RX PDU cache 23907978SPeter.Dunlap@Sun.COM */ 23917978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 23927978SPeter.Dunlap@Sun.COM int 23937978SPeter.Dunlap@Sun.COM idm_sorx_pdu_constructor(void *hdl, void *arg, int flags) 23947978SPeter.Dunlap@Sun.COM { 23957978SPeter.Dunlap@Sun.COM idm_pdu_t *pdu = hdl; 23967978SPeter.Dunlap@Sun.COM 23977978SPeter.Dunlap@Sun.COM bzero(pdu, sizeof (idm_pdu_t)); 23987978SPeter.Dunlap@Sun.COM pdu->isp_magic = IDM_PDU_MAGIC; 23997978SPeter.Dunlap@Sun.COM pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */ 24007978SPeter.Dunlap@Sun.COM pdu->isp_callback = idm_sorx_cache_pdu_cb; 24017978SPeter.Dunlap@Sun.COM 24027978SPeter.Dunlap@Sun.COM return (0); 24037978SPeter.Dunlap@Sun.COM } 24047978SPeter.Dunlap@Sun.COM 24057978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 24067978SPeter.Dunlap@Sun.COM static void 24077978SPeter.Dunlap@Sun.COM idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 24087978SPeter.Dunlap@Sun.COM { 24097978SPeter.Dunlap@Sun.COM pdu->isp_iovlen = 0; 24107978SPeter.Dunlap@Sun.COM pdu->isp_sorx_buf = 0; 24117978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_sorx_pdu_cache, pdu); 24127978SPeter.Dunlap@Sun.COM } 24137978SPeter.Dunlap@Sun.COM 24147978SPeter.Dunlap@Sun.COM static void 24157978SPeter.Dunlap@Sun.COM idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 24167978SPeter.Dunlap@Sun.COM { 24177978SPeter.Dunlap@Sun.COM /* 24187978SPeter.Dunlap@Sun.COM * We had to modify our cached RX PDU with a longer header buffer 24197978SPeter.Dunlap@Sun.COM * and/or a longer data buffer. Release the new buffers and fix 24207978SPeter.Dunlap@Sun.COM * the fields back to what we would expect for a cached RX PDU. 24217978SPeter.Dunlap@Sun.COM */ 24227978SPeter.Dunlap@Sun.COM if (pdu->isp_flags & IDM_PDU_ADDL_HDR) { 24237978SPeter.Dunlap@Sun.COM kmem_free(pdu->isp_hdr, pdu->isp_hdrlen); 24247978SPeter.Dunlap@Sun.COM } 24257978SPeter.Dunlap@Sun.COM if (pdu->isp_flags & IDM_PDU_ADDL_DATA) { 24267978SPeter.Dunlap@Sun.COM kmem_free(pdu->isp_data, pdu->isp_datalen); 24277978SPeter.Dunlap@Sun.COM } 24287978SPeter.Dunlap@Sun.COM pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); 24297978SPeter.Dunlap@Sun.COM pdu->isp_hdrlen = sizeof (iscsi_hdr_t); 24307978SPeter.Dunlap@Sun.COM pdu->isp_data = NULL; 24317978SPeter.Dunlap@Sun.COM pdu->isp_datalen = 0; 24327978SPeter.Dunlap@Sun.COM pdu->isp_sorx_buf = 0; 24337978SPeter.Dunlap@Sun.COM pdu->isp_callback = idm_sorx_cache_pdu_cb; 24347978SPeter.Dunlap@Sun.COM idm_sorx_cache_pdu_cb(pdu, status); 24357978SPeter.Dunlap@Sun.COM } 24367978SPeter.Dunlap@Sun.COM 24377978SPeter.Dunlap@Sun.COM /* 24387978SPeter.Dunlap@Sun.COM * This thread is only active when I/O is queued for transmit 24397978SPeter.Dunlap@Sun.COM * because the socket is busy. 24407978SPeter.Dunlap@Sun.COM */ 24417978SPeter.Dunlap@Sun.COM void 24427978SPeter.Dunlap@Sun.COM idm_sotx_thread(void *arg) 24437978SPeter.Dunlap@Sun.COM { 24447978SPeter.Dunlap@Sun.COM idm_conn_t *ic = arg; 24457978SPeter.Dunlap@Sun.COM idm_tx_obj_t *object, *next; 24467978SPeter.Dunlap@Sun.COM idm_so_conn_t *so_conn; 24477978SPeter.Dunlap@Sun.COM idm_status_t status = IDM_STATUS_SUCCESS; 24487978SPeter.Dunlap@Sun.COM 24497978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 24507978SPeter.Dunlap@Sun.COM 24517978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_mutex); 24527978SPeter.Dunlap@Sun.COM so_conn = ic->ic_transport_private; 24537978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread_running = B_TRUE; 24547978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did; 24557978SPeter.Dunlap@Sun.COM cv_signal(&ic->ic_cv); 24567978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_mutex); 24577978SPeter.Dunlap@Sun.COM 24587978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 24597978SPeter.Dunlap@Sun.COM 24607978SPeter.Dunlap@Sun.COM while (so_conn->ic_tx_thread_running) { 24617978SPeter.Dunlap@Sun.COM while (list_is_empty(&so_conn->ic_tx_list)) { 24627978SPeter.Dunlap@Sun.COM DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic); 24637978SPeter.Dunlap@Sun.COM cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex); 24647978SPeter.Dunlap@Sun.COM DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic); 24657978SPeter.Dunlap@Sun.COM 24667978SPeter.Dunlap@Sun.COM if (!so_conn->ic_tx_thread_running) { 24677978SPeter.Dunlap@Sun.COM goto tx_bail; 24687978SPeter.Dunlap@Sun.COM } 24697978SPeter.Dunlap@Sun.COM } 24707978SPeter.Dunlap@Sun.COM 24717978SPeter.Dunlap@Sun.COM object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list); 24727978SPeter.Dunlap@Sun.COM list_remove(&so_conn->ic_tx_list, object); 24737978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 24747978SPeter.Dunlap@Sun.COM 24757978SPeter.Dunlap@Sun.COM switch (object->idm_tx_obj_magic) { 24767978SPeter.Dunlap@Sun.COM case IDM_PDU_MAGIC: 24777978SPeter.Dunlap@Sun.COM DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic, 24787978SPeter.Dunlap@Sun.COM idm_pdu_t *, (idm_pdu_t *)object); 24797978SPeter.Dunlap@Sun.COM 24807978SPeter.Dunlap@Sun.COM status = idm_i_so_tx((idm_pdu_t *)object); 24817978SPeter.Dunlap@Sun.COM break; 24827978SPeter.Dunlap@Sun.COM 24837978SPeter.Dunlap@Sun.COM case IDM_BUF_MAGIC: { 24847978SPeter.Dunlap@Sun.COM idm_buf_t *idb = (idm_buf_t *)object; 24857978SPeter.Dunlap@Sun.COM idm_task_t *idt = idb->idb_task_binding; 24867978SPeter.Dunlap@Sun.COM 24877978SPeter.Dunlap@Sun.COM DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic, 24887978SPeter.Dunlap@Sun.COM idm_buf_t *, idb); 24897978SPeter.Dunlap@Sun.COM 24907978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 24917978SPeter.Dunlap@Sun.COM status = idm_so_send_buf_region(idt, 2492*9162SPeter.Dunlap@Sun.COM idb, 0, idb->idb_xfer_len); 24937978SPeter.Dunlap@Sun.COM 24947978SPeter.Dunlap@Sun.COM /* 24957978SPeter.Dunlap@Sun.COM * TX thread owns the buffer so we expect it to 24967978SPeter.Dunlap@Sun.COM * be "in transport" 24977978SPeter.Dunlap@Sun.COM */ 24987978SPeter.Dunlap@Sun.COM ASSERT(idb->idb_in_transport); 2499*9162SPeter.Dunlap@Sun.COM if (IDM_CONN_ISTGT(ic)) { 2500*9162SPeter.Dunlap@Sun.COM /* 2501*9162SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini_done releases 2502*9162SPeter.Dunlap@Sun.COM * idt->idt_mutex 2503*9162SPeter.Dunlap@Sun.COM */ 2504*9162SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idt, idb, status); 2505*9162SPeter.Dunlap@Sun.COM } else { 2506*9162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data_done(idt, idb); 2507*9162SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 2508*9162SPeter.Dunlap@Sun.COM } 25097978SPeter.Dunlap@Sun.COM break; 25107978SPeter.Dunlap@Sun.COM } 25117978SPeter.Dunlap@Sun.COM 25127978SPeter.Dunlap@Sun.COM default: 25137978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic " 25147978SPeter.Dunlap@Sun.COM "(0x%08x)", object->idm_tx_obj_magic); 25157978SPeter.Dunlap@Sun.COM status = IDM_STATUS_FAIL; 25167978SPeter.Dunlap@Sun.COM } 25177978SPeter.Dunlap@Sun.COM 25187978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 25197978SPeter.Dunlap@Sun.COM 25207978SPeter.Dunlap@Sun.COM if (status != IDM_STATUS_SUCCESS) { 25217978SPeter.Dunlap@Sun.COM so_conn->ic_tx_thread_running = B_FALSE; 25227978SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_TRANSPORT_FAIL, status); 25237978SPeter.Dunlap@Sun.COM } 25247978SPeter.Dunlap@Sun.COM } 25257978SPeter.Dunlap@Sun.COM 25267978SPeter.Dunlap@Sun.COM /* 25277978SPeter.Dunlap@Sun.COM * Before we leave, we need to abort every item remaining in the 25287978SPeter.Dunlap@Sun.COM * TX list. 25297978SPeter.Dunlap@Sun.COM */ 25307978SPeter.Dunlap@Sun.COM 25317978SPeter.Dunlap@Sun.COM tx_bail: 25327978SPeter.Dunlap@Sun.COM object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list); 25337978SPeter.Dunlap@Sun.COM 25347978SPeter.Dunlap@Sun.COM while (object != NULL) { 25357978SPeter.Dunlap@Sun.COM next = list_next(&so_conn->ic_tx_list, object); 25367978SPeter.Dunlap@Sun.COM 25377978SPeter.Dunlap@Sun.COM list_remove(&so_conn->ic_tx_list, object); 25387978SPeter.Dunlap@Sun.COM switch (object->idm_tx_obj_magic) { 25397978SPeter.Dunlap@Sun.COM case IDM_PDU_MAGIC: 25407978SPeter.Dunlap@Sun.COM idm_pdu_complete((idm_pdu_t *)object, 25417978SPeter.Dunlap@Sun.COM IDM_STATUS_ABORTED); 25427978SPeter.Dunlap@Sun.COM break; 25437978SPeter.Dunlap@Sun.COM 25447978SPeter.Dunlap@Sun.COM case IDM_BUF_MAGIC: { 25457978SPeter.Dunlap@Sun.COM idm_buf_t *idb = (idm_buf_t *)object; 25467978SPeter.Dunlap@Sun.COM idm_task_t *idt = idb->idb_task_binding; 25477978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 25487978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 25497978SPeter.Dunlap@Sun.COM /* 25507978SPeter.Dunlap@Sun.COM * TX thread owns the buffer so we expect it to 25517978SPeter.Dunlap@Sun.COM * be "in transport" 25527978SPeter.Dunlap@Sun.COM */ 25537978SPeter.Dunlap@Sun.COM ASSERT(idb->idb_in_transport); 2554*9162SPeter.Dunlap@Sun.COM if (IDM_CONN_ISTGT(ic)) { 2555*9162SPeter.Dunlap@Sun.COM /* 2556*9162SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini_done releases 2557*9162SPeter.Dunlap@Sun.COM * idt->idt_mutex 2558*9162SPeter.Dunlap@Sun.COM */ 2559*9162SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idt, idb, 2560*9162SPeter.Dunlap@Sun.COM IDM_STATUS_ABORTED); 2561*9162SPeter.Dunlap@Sun.COM } else { 2562*9162SPeter.Dunlap@Sun.COM idm_so_send_rtt_data_done(idt, idb); 2563*9162SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 2564*9162SPeter.Dunlap@Sun.COM } 25657978SPeter.Dunlap@Sun.COM mutex_enter(&so_conn->ic_tx_mutex); 25667978SPeter.Dunlap@Sun.COM break; 25677978SPeter.Dunlap@Sun.COM } 25687978SPeter.Dunlap@Sun.COM default: 25697978SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 25707978SPeter.Dunlap@Sun.COM "idm_sotx_thread: Unexpected magic " 25717978SPeter.Dunlap@Sun.COM "(0x%08x)", object->idm_tx_obj_magic); 25727978SPeter.Dunlap@Sun.COM } 25737978SPeter.Dunlap@Sun.COM 25747978SPeter.Dunlap@Sun.COM object = next; 25757978SPeter.Dunlap@Sun.COM } 25767978SPeter.Dunlap@Sun.COM 25777978SPeter.Dunlap@Sun.COM mutex_exit(&so_conn->ic_tx_mutex); 25787978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 25797978SPeter.Dunlap@Sun.COM thread_exit(); 25807978SPeter.Dunlap@Sun.COM /*NOTREACHED*/ 25817978SPeter.Dunlap@Sun.COM } 2582