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