1a6d42e7dSPeter Dunlap /* 2a6d42e7dSPeter Dunlap * CDDL HEADER START 3a6d42e7dSPeter Dunlap * 4a6d42e7dSPeter Dunlap * The contents of this file are subject to the terms of the 5a6d42e7dSPeter Dunlap * Common Development and Distribution License (the "License"). 6a6d42e7dSPeter Dunlap * You may not use this file except in compliance with the License. 7a6d42e7dSPeter Dunlap * 8a6d42e7dSPeter Dunlap * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9a6d42e7dSPeter Dunlap * or http://www.opensolaris.org/os/licensing. 10a6d42e7dSPeter Dunlap * See the License for the specific language governing permissions 11a6d42e7dSPeter Dunlap * and limitations under the License. 12a6d42e7dSPeter Dunlap * 13a6d42e7dSPeter Dunlap * When distributing Covered Code, include this CDDL HEADER in each 14a6d42e7dSPeter Dunlap * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15a6d42e7dSPeter Dunlap * If applicable, add the following below this CDDL HEADER, with the 16a6d42e7dSPeter Dunlap * fields enclosed by brackets "[]" replaced with your own identifying 17a6d42e7dSPeter Dunlap * information: Portions Copyright [yyyy] [name of copyright owner] 18a6d42e7dSPeter Dunlap * 19a6d42e7dSPeter Dunlap * CDDL HEADER END 20a6d42e7dSPeter Dunlap */ 21a6d42e7dSPeter Dunlap /* 2263528ae4SJames Moore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23a6d42e7dSPeter Dunlap * Use is subject to license terms. 24a6d42e7dSPeter Dunlap */ 25a6d42e7dSPeter Dunlap 26a6d42e7dSPeter Dunlap #include <sys/conf.h> 27a6d42e7dSPeter Dunlap #include <sys/stat.h> 28a6d42e7dSPeter Dunlap #include <sys/file.h> 29a6d42e7dSPeter Dunlap #include <sys/ddi.h> 30a6d42e7dSPeter Dunlap #include <sys/sunddi.h> 31a6d42e7dSPeter Dunlap #include <sys/modctl.h> 32a6d42e7dSPeter Dunlap #include <sys/priv.h> 33a6d42e7dSPeter Dunlap #include <sys/cpuvar.h> 34a6d42e7dSPeter Dunlap #include <sys/socket.h> 35a6d42e7dSPeter Dunlap #include <sys/strsubr.h> 36a6d42e7dSPeter Dunlap #include <sys/sysmacros.h> 37a6d42e7dSPeter Dunlap #include <sys/sdt.h> 38a6d42e7dSPeter Dunlap #include <netinet/tcp.h> 39a6d42e7dSPeter Dunlap #include <inet/tcp.h> 40a6d42e7dSPeter Dunlap #include <sys/socketvar.h> 41a6d42e7dSPeter Dunlap #include <sys/pathname.h> 42a6d42e7dSPeter Dunlap #include <sys/fs/snode.h> 43a6d42e7dSPeter Dunlap #include <sys/fs/dv_node.h> 44a6d42e7dSPeter Dunlap #include <sys/vnode.h> 45a6d42e7dSPeter Dunlap #include <netinet/in.h> 46a6d42e7dSPeter Dunlap #include <net/if.h> 47a6d42e7dSPeter Dunlap #include <sys/sockio.h> 480f1702c5SYu Xiangning #include <sys/ksocket.h> 49a6d42e7dSPeter Dunlap #include <sys/idm/idm.h> 50a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h> 51a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h> 52a6d42e7dSPeter Dunlap 53a6d42e7dSPeter Dunlap /* 54a6d42e7dSPeter Dunlap * in6addr_any is currently all zeroes, but use the macro in case this 55a6d42e7dSPeter Dunlap * ever changes. 56a6d42e7dSPeter Dunlap */ 57a6d42e7dSPeter Dunlap const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; 58a6d42e7dSPeter Dunlap 59a6d42e7dSPeter Dunlap static void idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status); 60a6d42e7dSPeter Dunlap static void idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status); 61a6d42e7dSPeter Dunlap static void idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status); 62a6d42e7dSPeter Dunlap 630f1702c5SYu Xiangning static idm_status_t idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so); 64a6d42e7dSPeter Dunlap static void idm_so_conn_destroy_common(idm_conn_t *ic); 65a6d42e7dSPeter Dunlap static void idm_so_conn_connect_common(idm_conn_t *ic); 66a6d42e7dSPeter Dunlap 67a6d42e7dSPeter Dunlap static void idm_set_ini_preconnect_options(idm_so_conn_t *sc); 68a6d42e7dSPeter Dunlap static void idm_set_ini_postconnect_options(idm_so_conn_t *sc); 690f1702c5SYu Xiangning static void idm_set_tgt_connect_options(ksocket_t so); 70a6d42e7dSPeter Dunlap static idm_status_t idm_i_so_tx(idm_pdu_t *pdu); 71a6d42e7dSPeter Dunlap 72a6d42e7dSPeter Dunlap static idm_status_t idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu); 7330e7468fSPeter Dunlap static void idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, 7430e7468fSPeter Dunlap idm_buf_t *idb, uint32_t offset, uint32_t length); 7530e7468fSPeter Dunlap static void idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb); 7630e7468fSPeter Dunlap static idm_status_t idm_so_send_buf_region(idm_task_t *idt, 77a6d42e7dSPeter Dunlap idm_buf_t *idb, uint32_t buf_region_offset, uint32_t buf_region_length); 78a6d42e7dSPeter Dunlap 79a6d42e7dSPeter Dunlap static uint32_t idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, 80a6d42e7dSPeter Dunlap uint32_t ro, uint32_t dlength); 81a6d42e7dSPeter Dunlap 82a6d42e7dSPeter Dunlap static idm_status_t idm_so_handle_digest(idm_conn_t *it, 83a6d42e7dSPeter Dunlap nvpair_t *digest_choice, const idm_kv_xlate_t *ikvx); 84a6d42e7dSPeter Dunlap 85a6d42e7dSPeter Dunlap /* 86a6d42e7dSPeter Dunlap * Transport ops prototypes 87a6d42e7dSPeter Dunlap */ 88a6d42e7dSPeter Dunlap static void idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu); 89a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb); 90a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb); 91a6d42e7dSPeter Dunlap static void idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu); 92a6d42e7dSPeter Dunlap static void idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu); 93a6d42e7dSPeter Dunlap static void idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu); 94a6d42e7dSPeter Dunlap static idm_status_t idm_so_free_task_rsrc(idm_task_t *idt); 95a6d42e7dSPeter Dunlap static kv_status_t idm_so_negotiate_key_values(idm_conn_t *it, 96a6d42e7dSPeter Dunlap nvlist_t *request_nvl, nvlist_t *response_nvl, nvlist_t *negotiated_nvl); 9730e7468fSPeter Dunlap static void idm_so_notice_key_values(idm_conn_t *it, 98a6d42e7dSPeter Dunlap nvlist_t *negotiated_nvl); 99a6d42e7dSPeter Dunlap static boolean_t idm_so_conn_is_capable(idm_conn_req_t *ic, 100a6d42e7dSPeter Dunlap idm_transport_caps_t *caps); 101a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen); 102a6d42e7dSPeter Dunlap static void idm_so_buf_free(idm_buf_t *idb); 103a6d42e7dSPeter Dunlap static idm_status_t idm_so_buf_setup(idm_buf_t *idb); 104a6d42e7dSPeter Dunlap static void idm_so_buf_teardown(idm_buf_t *idb); 105a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is); 106a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_destroy(idm_svc_t *is); 107a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_svc_online(idm_svc_t *is); 108a6d42e7dSPeter Dunlap static void idm_so_tgt_svc_offline(idm_svc_t *is); 109a6d42e7dSPeter Dunlap static void idm_so_tgt_conn_destroy(idm_conn_t *ic); 110a6d42e7dSPeter Dunlap static idm_status_t idm_so_tgt_conn_connect(idm_conn_t *ic); 111a6d42e7dSPeter Dunlap static void idm_so_conn_disconnect(idm_conn_t *ic); 112a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic); 113a6d42e7dSPeter Dunlap static void idm_so_ini_conn_destroy(idm_conn_t *ic); 114a6d42e7dSPeter Dunlap static idm_status_t idm_so_ini_conn_connect(idm_conn_t *ic); 115a6d42e7dSPeter Dunlap 116a6d42e7dSPeter Dunlap /* 117a6d42e7dSPeter Dunlap * IDM Native Sockets transport operations 118a6d42e7dSPeter Dunlap */ 119a6d42e7dSPeter Dunlap static 120a6d42e7dSPeter Dunlap idm_transport_ops_t idm_so_transport_ops = { 121a6d42e7dSPeter Dunlap idm_so_tx, /* it_tx_pdu */ 122a6d42e7dSPeter Dunlap idm_so_buf_tx_to_ini, /* it_buf_tx_to_ini */ 123a6d42e7dSPeter Dunlap idm_so_buf_rx_from_ini, /* it_buf_rx_from_ini */ 124a6d42e7dSPeter Dunlap idm_so_rx_datain, /* it_rx_datain */ 125a6d42e7dSPeter Dunlap idm_so_rx_rtt, /* it_rx_rtt */ 126a6d42e7dSPeter Dunlap idm_so_rx_dataout, /* it_rx_dataout */ 127a6d42e7dSPeter Dunlap NULL, /* it_alloc_conn_rsrc */ 128a6d42e7dSPeter Dunlap NULL, /* it_free_conn_rsrc */ 129a6d42e7dSPeter Dunlap NULL, /* it_tgt_enable_datamover */ 130a6d42e7dSPeter Dunlap NULL, /* it_ini_enable_datamover */ 131a6d42e7dSPeter Dunlap NULL, /* it_conn_terminate */ 132a6d42e7dSPeter Dunlap idm_so_free_task_rsrc, /* it_free_task_rsrc */ 133a6d42e7dSPeter Dunlap idm_so_negotiate_key_values, /* it_negotiate_key_values */ 134a6d42e7dSPeter Dunlap idm_so_notice_key_values, /* it_notice_key_values */ 135a6d42e7dSPeter Dunlap idm_so_conn_is_capable, /* it_conn_is_capable */ 136a6d42e7dSPeter Dunlap idm_so_buf_alloc, /* it_buf_alloc */ 137a6d42e7dSPeter Dunlap idm_so_buf_free, /* it_buf_free */ 138a6d42e7dSPeter Dunlap idm_so_buf_setup, /* it_buf_setup */ 139a6d42e7dSPeter Dunlap idm_so_buf_teardown, /* it_buf_teardown */ 140a6d42e7dSPeter Dunlap idm_so_tgt_svc_create, /* it_tgt_svc_create */ 141a6d42e7dSPeter Dunlap idm_so_tgt_svc_destroy, /* it_tgt_svc_destroy */ 142a6d42e7dSPeter Dunlap idm_so_tgt_svc_online, /* it_tgt_svc_online */ 143a6d42e7dSPeter Dunlap idm_so_tgt_svc_offline, /* it_tgt_svc_offline */ 144a6d42e7dSPeter Dunlap idm_so_tgt_conn_destroy, /* it_tgt_conn_destroy */ 145a6d42e7dSPeter Dunlap idm_so_tgt_conn_connect, /* it_tgt_conn_connect */ 146a6d42e7dSPeter Dunlap idm_so_conn_disconnect, /* it_tgt_conn_disconnect */ 147a6d42e7dSPeter Dunlap idm_so_ini_conn_create, /* it_ini_conn_create */ 148a6d42e7dSPeter Dunlap idm_so_ini_conn_destroy, /* it_ini_conn_destroy */ 149a6d42e7dSPeter Dunlap idm_so_ini_conn_connect, /* it_ini_conn_connect */ 150a6d42e7dSPeter Dunlap idm_so_conn_disconnect /* it_ini_conn_disconnect */ 151a6d42e7dSPeter Dunlap }; 152a6d42e7dSPeter Dunlap 153a6d42e7dSPeter Dunlap /* 154a6d42e7dSPeter Dunlap * idm_so_init() 155a6d42e7dSPeter Dunlap * Sockets transport initialization 156a6d42e7dSPeter Dunlap */ 157a6d42e7dSPeter Dunlap void 158a6d42e7dSPeter Dunlap idm_so_init(idm_transport_t *it) 159a6d42e7dSPeter Dunlap { 160a6d42e7dSPeter Dunlap /* Cache for IDM Data and R2T Transmit PDU's */ 161a6d42e7dSPeter Dunlap idm.idm_sotx_pdu_cache = kmem_cache_create("idm_tx_pdu_cache", 162a6d42e7dSPeter Dunlap sizeof (idm_pdu_t) + sizeof (iscsi_hdr_t), 8, 163a6d42e7dSPeter Dunlap &idm_sotx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP); 164a6d42e7dSPeter Dunlap 165a6d42e7dSPeter Dunlap /* Cache for IDM Receive PDU's */ 166a6d42e7dSPeter Dunlap idm.idm_sorx_pdu_cache = kmem_cache_create("idm_rx_pdu_cache", 167a6d42e7dSPeter Dunlap sizeof (idm_pdu_t) + IDM_SORX_CACHE_HDRLEN, 8, 168a6d42e7dSPeter Dunlap &idm_sorx_pdu_constructor, NULL, NULL, NULL, NULL, KM_SLEEP); 169a6d42e7dSPeter Dunlap 170*cf8c0ebaSPeter Dunlap /* 128k buffer cache */ 171*cf8c0ebaSPeter Dunlap idm.idm_so_128k_buf_cache = kmem_cache_create("idm_128k_buf_cache", 172*cf8c0ebaSPeter Dunlap IDM_SO_BUF_CACHE_UB, 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP); 173*cf8c0ebaSPeter Dunlap 174a6d42e7dSPeter Dunlap /* Set the sockets transport ops */ 175a6d42e7dSPeter Dunlap it->it_ops = &idm_so_transport_ops; 176a6d42e7dSPeter Dunlap } 177a6d42e7dSPeter Dunlap 178a6d42e7dSPeter Dunlap /* 179a6d42e7dSPeter Dunlap * idm_so_fini() 180a6d42e7dSPeter Dunlap * Sockets transport teardown 181a6d42e7dSPeter Dunlap */ 182a6d42e7dSPeter Dunlap void 183a6d42e7dSPeter Dunlap idm_so_fini(void) 184a6d42e7dSPeter Dunlap { 185*cf8c0ebaSPeter Dunlap kmem_cache_destroy(idm.idm_so_128k_buf_cache); 186a6d42e7dSPeter Dunlap kmem_cache_destroy(idm.idm_sotx_pdu_cache); 187a6d42e7dSPeter Dunlap kmem_cache_destroy(idm.idm_sorx_pdu_cache); 188a6d42e7dSPeter Dunlap } 189a6d42e7dSPeter Dunlap 1900f1702c5SYu Xiangning ksocket_t 191a6d42e7dSPeter Dunlap idm_socreate(int domain, int type, int protocol) 192a6d42e7dSPeter Dunlap { 1930f1702c5SYu Xiangning ksocket_t ks; 194a6d42e7dSPeter Dunlap 1950f1702c5SYu Xiangning if (!ksocket_socket(&ks, domain, type, protocol, KSOCKET_NOSLEEP, 1960f1702c5SYu Xiangning CRED())) { 1970f1702c5SYu Xiangning return (ks); 1980f1702c5SYu Xiangning } else { 1990f1702c5SYu Xiangning return (NULL); 200a6d42e7dSPeter Dunlap } 201a6d42e7dSPeter Dunlap } 202a6d42e7dSPeter Dunlap 203a6d42e7dSPeter Dunlap /* 204a6d42e7dSPeter Dunlap * idm_soshutdown will disconnect the socket and prevent subsequent PDU 205a6d42e7dSPeter Dunlap * reception and transmission. The sonode still exists but its state 206a6d42e7dSPeter Dunlap * gets modified to indicate it is no longer connected. Calls to 207a6d42e7dSPeter Dunlap * idm_sorecv/idm_iov_sorecv will return so idm_soshutdown can be used 208a6d42e7dSPeter Dunlap * regain control of a thread stuck in idm_sorecv. 209a6d42e7dSPeter Dunlap */ 210a6d42e7dSPeter Dunlap void 2110f1702c5SYu Xiangning idm_soshutdown(ksocket_t so) 212a6d42e7dSPeter Dunlap { 2130f1702c5SYu Xiangning (void) ksocket_shutdown(so, SHUT_RDWR, CRED()); 214a6d42e7dSPeter Dunlap } 215a6d42e7dSPeter Dunlap 216a6d42e7dSPeter Dunlap /* 217a6d42e7dSPeter Dunlap * idm_sodestroy releases all resources associated with a socket previously 218a6d42e7dSPeter Dunlap * created with idm_socreate. The socket must be shutdown using 219a6d42e7dSPeter Dunlap * idm_soshutdown before the socket is destroyed with idm_sodestroy, 220a6d42e7dSPeter Dunlap * otherwise undefined behavior will result. 221a6d42e7dSPeter Dunlap */ 222a6d42e7dSPeter Dunlap void 2230f1702c5SYu Xiangning idm_sodestroy(ksocket_t ks) 224a6d42e7dSPeter Dunlap { 2250f1702c5SYu Xiangning (void) ksocket_close(ks, CRED()); 226a6d42e7dSPeter Dunlap } 227a6d42e7dSPeter Dunlap 228a6d42e7dSPeter Dunlap /* 229a6d42e7dSPeter Dunlap * IP address filter functions to flag addresses that should not 230a6d42e7dSPeter Dunlap * go out to initiators through discovery. 231a6d42e7dSPeter Dunlap */ 232a6d42e7dSPeter Dunlap static boolean_t 233a6d42e7dSPeter Dunlap idm_v4_addr_okay(struct in_addr *in_addr) 234a6d42e7dSPeter Dunlap { 235a6d42e7dSPeter Dunlap in_addr_t addr = ntohl(in_addr->s_addr); 236a6d42e7dSPeter Dunlap 237a6d42e7dSPeter Dunlap if ((INADDR_NONE == addr) || 238a6d42e7dSPeter Dunlap (IN_MULTICAST(addr)) || 239a6d42e7dSPeter Dunlap ((addr >> IN_CLASSA_NSHIFT) == 0) || 240a6d42e7dSPeter Dunlap ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) { 241a6d42e7dSPeter Dunlap return (B_FALSE); 242a6d42e7dSPeter Dunlap } 243a6d42e7dSPeter Dunlap return (B_TRUE); 244a6d42e7dSPeter Dunlap } 245a6d42e7dSPeter Dunlap 246a6d42e7dSPeter Dunlap static boolean_t 247a6d42e7dSPeter Dunlap idm_v6_addr_okay(struct in6_addr *addr6) 248a6d42e7dSPeter Dunlap { 249a6d42e7dSPeter Dunlap 250a6d42e7dSPeter Dunlap if ((IN6_IS_ADDR_UNSPECIFIED(addr6)) || 251a6d42e7dSPeter Dunlap (IN6_IS_ADDR_LOOPBACK(addr6)) || 252a6d42e7dSPeter Dunlap (IN6_IS_ADDR_MULTICAST(addr6)) || 253a6d42e7dSPeter Dunlap (IN6_IS_ADDR_V4MAPPED(addr6)) || 254a6d42e7dSPeter Dunlap (IN6_IS_ADDR_V4COMPAT(addr6)) || 255a6d42e7dSPeter Dunlap (IN6_IS_ADDR_LINKLOCAL(addr6))) { 256a6d42e7dSPeter Dunlap return (B_FALSE); 257a6d42e7dSPeter Dunlap } 258a6d42e7dSPeter Dunlap return (B_TRUE); 259a6d42e7dSPeter Dunlap } 260a6d42e7dSPeter Dunlap 261a6d42e7dSPeter Dunlap /* 262a6d42e7dSPeter Dunlap * idm_get_ipaddr will retrieve a list of IP Addresses which the host is 263a6d42e7dSPeter Dunlap * configured with by sending down a sequence of kernel ioctl to IP STREAMS. 264a6d42e7dSPeter Dunlap */ 265a6d42e7dSPeter Dunlap int 266a6d42e7dSPeter Dunlap idm_get_ipaddr(idm_addr_list_t **ipaddr_p) 267a6d42e7dSPeter Dunlap { 2680f1702c5SYu Xiangning ksocket_t so4, so6; 269a6d42e7dSPeter Dunlap struct lifnum lifn; 270a6d42e7dSPeter Dunlap struct lifconf lifc; 271a6d42e7dSPeter Dunlap struct lifreq *lp; 272a6d42e7dSPeter Dunlap int rval; 273a6d42e7dSPeter Dunlap int numifs; 274a6d42e7dSPeter Dunlap int bufsize; 275a6d42e7dSPeter Dunlap void *buf; 276a6d42e7dSPeter Dunlap int i, j, n, rc; 277a6d42e7dSPeter Dunlap struct sockaddr_storage ss; 278a6d42e7dSPeter Dunlap struct sockaddr_in *sin; 279a6d42e7dSPeter Dunlap struct sockaddr_in6 *sin6; 280a6d42e7dSPeter Dunlap idm_addr_t *ip; 281a6d42e7dSPeter Dunlap idm_addr_list_t *ipaddr; 282a6d42e7dSPeter Dunlap int size_ipaddr; 283a6d42e7dSPeter Dunlap 284a6d42e7dSPeter Dunlap *ipaddr_p = NULL; 285a6d42e7dSPeter Dunlap size_ipaddr = 0; 286a6d42e7dSPeter Dunlap buf = NULL; 287a6d42e7dSPeter Dunlap 288a6d42e7dSPeter Dunlap /* create an ipv4 and ipv6 UDP socket */ 289a6d42e7dSPeter Dunlap if ((so6 = idm_socreate(PF_INET6, SOCK_DGRAM, 0)) == NULL) 290a6d42e7dSPeter Dunlap return (0); 291a6d42e7dSPeter Dunlap if ((so4 = idm_socreate(PF_INET, SOCK_DGRAM, 0)) == NULL) { 292a6d42e7dSPeter Dunlap idm_sodestroy(so6); 293a6d42e7dSPeter Dunlap return (0); 294a6d42e7dSPeter Dunlap } 295a6d42e7dSPeter Dunlap 296a6d42e7dSPeter Dunlap 297a6d42e7dSPeter Dunlap retry_count: 298a6d42e7dSPeter Dunlap /* snapshot the current number of interfaces */ 299a6d42e7dSPeter Dunlap lifn.lifn_family = PF_UNSPEC; 300a6d42e7dSPeter Dunlap lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 301a6d42e7dSPeter Dunlap lifn.lifn_count = 0; 3020f1702c5SYu Xiangning /* use vp6 for ioctls with unspecified families by default */ 3030f1702c5SYu Xiangning if (ksocket_ioctl(so6, SIOCGLIFNUM, (intptr_t)&lifn, &rval, CRED()) 3040f1702c5SYu Xiangning != 0) { 305a6d42e7dSPeter Dunlap goto cleanup; 306a6d42e7dSPeter Dunlap } 307a6d42e7dSPeter Dunlap 308a6d42e7dSPeter Dunlap numifs = lifn.lifn_count; 309a6d42e7dSPeter Dunlap if (numifs <= 0) { 310a6d42e7dSPeter Dunlap goto cleanup; 311a6d42e7dSPeter Dunlap } 312a6d42e7dSPeter Dunlap 313a6d42e7dSPeter Dunlap /* allocate extra room in case more interfaces appear */ 314a6d42e7dSPeter Dunlap numifs += 10; 315a6d42e7dSPeter Dunlap 316a6d42e7dSPeter Dunlap /* get the interface names and ip addresses */ 317a6d42e7dSPeter Dunlap bufsize = numifs * sizeof (struct lifreq); 318a6d42e7dSPeter Dunlap buf = kmem_alloc(bufsize, KM_SLEEP); 319a6d42e7dSPeter Dunlap 320a6d42e7dSPeter Dunlap lifc.lifc_family = AF_UNSPEC; 321a6d42e7dSPeter Dunlap lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 322a6d42e7dSPeter Dunlap lifc.lifc_len = bufsize; 323a6d42e7dSPeter Dunlap lifc.lifc_buf = buf; 3240f1702c5SYu Xiangning rc = ksocket_ioctl(so6, SIOCGLIFCONF, (intptr_t)&lifc, &rval, CRED()); 325a6d42e7dSPeter Dunlap if (rc != 0) { 326a6d42e7dSPeter Dunlap goto cleanup; 327a6d42e7dSPeter Dunlap } 328a6d42e7dSPeter Dunlap /* if our extra room is used up, try again */ 329a6d42e7dSPeter Dunlap if (bufsize <= lifc.lifc_len) { 330a6d42e7dSPeter Dunlap kmem_free(buf, bufsize); 331a6d42e7dSPeter Dunlap buf = NULL; 332a6d42e7dSPeter Dunlap goto retry_count; 333a6d42e7dSPeter Dunlap } 334a6d42e7dSPeter Dunlap /* calc actual number of ifconfs */ 335a6d42e7dSPeter Dunlap n = lifc.lifc_len / sizeof (struct lifreq); 336a6d42e7dSPeter Dunlap 337a6d42e7dSPeter Dunlap /* get ip address */ 338a6d42e7dSPeter Dunlap if (n > 0) { 339a6d42e7dSPeter Dunlap size_ipaddr = sizeof (idm_addr_list_t) + 340a6d42e7dSPeter Dunlap (n - 1) * sizeof (idm_addr_t); 341a6d42e7dSPeter Dunlap ipaddr = kmem_zalloc(size_ipaddr, KM_SLEEP); 342a6d42e7dSPeter Dunlap } else { 343a6d42e7dSPeter Dunlap goto cleanup; 344a6d42e7dSPeter Dunlap } 345a6d42e7dSPeter Dunlap 346a6d42e7dSPeter Dunlap /* 347a6d42e7dSPeter Dunlap * Examine the array of interfaces and filter uninteresting ones 348a6d42e7dSPeter Dunlap */ 349a6d42e7dSPeter Dunlap for (i = 0, j = 0, lp = lifc.lifc_req; i < n; i++, lp++) { 350a6d42e7dSPeter Dunlap 351a6d42e7dSPeter Dunlap /* 352a6d42e7dSPeter Dunlap * Copy the address as the SIOCGLIFFLAGS ioctl is destructive 353a6d42e7dSPeter Dunlap */ 354a6d42e7dSPeter Dunlap ss = lp->lifr_addr; 355a6d42e7dSPeter Dunlap /* 356a6d42e7dSPeter Dunlap * fetch the flags using the socket of the correct family 357a6d42e7dSPeter Dunlap */ 358a6d42e7dSPeter Dunlap switch (ss.ss_family) { 359a6d42e7dSPeter Dunlap case AF_INET: 3600f1702c5SYu Xiangning rc = ksocket_ioctl(so4, SIOCGLIFFLAGS, (intptr_t)lp, 3610f1702c5SYu Xiangning &rval, CRED()); 362a6d42e7dSPeter Dunlap break; 363a6d42e7dSPeter Dunlap case AF_INET6: 3640f1702c5SYu Xiangning rc = ksocket_ioctl(so6, SIOCGLIFFLAGS, (intptr_t)lp, 3650f1702c5SYu Xiangning &rval, CRED()); 366a6d42e7dSPeter Dunlap break; 367a6d42e7dSPeter Dunlap default: 368a6d42e7dSPeter Dunlap continue; 369a6d42e7dSPeter Dunlap } 370a6d42e7dSPeter Dunlap if (rc == 0) { 371a6d42e7dSPeter Dunlap /* 372a6d42e7dSPeter Dunlap * If we got the flags, skip uninteresting 373a6d42e7dSPeter Dunlap * interfaces based on flags 374a6d42e7dSPeter Dunlap */ 375a6d42e7dSPeter Dunlap if ((lp->lifr_flags & IFF_UP) != IFF_UP) 376a6d42e7dSPeter Dunlap continue; 377a6d42e7dSPeter Dunlap if (lp->lifr_flags & 378a6d42e7dSPeter Dunlap (IFF_ANYCAST|IFF_NOLOCAL|IFF_DEPRECATED)) 379a6d42e7dSPeter Dunlap continue; 380a6d42e7dSPeter Dunlap } 381a6d42e7dSPeter Dunlap 382a6d42e7dSPeter Dunlap /* save ip address */ 383a6d42e7dSPeter Dunlap ip = &ipaddr->al_addrs[j]; 384a6d42e7dSPeter Dunlap switch (ss.ss_family) { 385a6d42e7dSPeter Dunlap case AF_INET: 386a6d42e7dSPeter Dunlap sin = (struct sockaddr_in *)&ss; 387a6d42e7dSPeter Dunlap if (!idm_v4_addr_okay(&sin->sin_addr)) 388a6d42e7dSPeter Dunlap continue; 389a6d42e7dSPeter Dunlap ip->a_addr.i_addr.in4 = sin->sin_addr; 390a6d42e7dSPeter Dunlap ip->a_addr.i_insize = sizeof (struct in_addr); 391a6d42e7dSPeter Dunlap break; 392a6d42e7dSPeter Dunlap case AF_INET6: 393a6d42e7dSPeter Dunlap sin6 = (struct sockaddr_in6 *)&ss; 394a6d42e7dSPeter Dunlap if (!idm_v6_addr_okay(&sin6->sin6_addr)) 395a6d42e7dSPeter Dunlap continue; 396a6d42e7dSPeter Dunlap ip->a_addr.i_addr.in6 = sin6->sin6_addr; 397a6d42e7dSPeter Dunlap ip->a_addr.i_insize = sizeof (struct in6_addr); 398a6d42e7dSPeter Dunlap break; 399a6d42e7dSPeter Dunlap default: 400a6d42e7dSPeter Dunlap continue; 401a6d42e7dSPeter Dunlap } 402a6d42e7dSPeter Dunlap j++; 403a6d42e7dSPeter Dunlap } 404a6d42e7dSPeter Dunlap 405a6d42e7dSPeter Dunlap if (j == 0) { 406a6d42e7dSPeter Dunlap /* no valid ifaddr */ 407a6d42e7dSPeter Dunlap kmem_free(ipaddr, size_ipaddr); 408a6d42e7dSPeter Dunlap size_ipaddr = 0; 409a6d42e7dSPeter Dunlap ipaddr = NULL; 410a6d42e7dSPeter Dunlap } else { 411a6d42e7dSPeter Dunlap ipaddr->al_out_cnt = j; 412a6d42e7dSPeter Dunlap } 413a6d42e7dSPeter Dunlap 414a6d42e7dSPeter Dunlap 415a6d42e7dSPeter Dunlap cleanup: 416a6d42e7dSPeter Dunlap idm_sodestroy(so6); 417a6d42e7dSPeter Dunlap idm_sodestroy(so4); 418a6d42e7dSPeter Dunlap 419a6d42e7dSPeter Dunlap if (buf != NULL) 420a6d42e7dSPeter Dunlap kmem_free(buf, bufsize); 421a6d42e7dSPeter Dunlap 422a6d42e7dSPeter Dunlap *ipaddr_p = ipaddr; 423a6d42e7dSPeter Dunlap return (size_ipaddr); 424a6d42e7dSPeter Dunlap } 425a6d42e7dSPeter Dunlap 426a6d42e7dSPeter Dunlap int 4270f1702c5SYu Xiangning idm_sorecv(ksocket_t so, void *msg, size_t len) 428a6d42e7dSPeter Dunlap { 429a6d42e7dSPeter Dunlap iovec_t iov; 430a6d42e7dSPeter Dunlap 431a6d42e7dSPeter Dunlap ASSERT(so != NULL); 432a6d42e7dSPeter Dunlap ASSERT(len != 0); 433a6d42e7dSPeter Dunlap 434a6d42e7dSPeter Dunlap /* 435a6d42e7dSPeter Dunlap * Fill in iovec and receive data 436a6d42e7dSPeter Dunlap */ 437a6d42e7dSPeter Dunlap iov.iov_base = msg; 438a6d42e7dSPeter Dunlap iov.iov_len = len; 439a6d42e7dSPeter Dunlap 440a6d42e7dSPeter Dunlap return (idm_iov_sorecv(so, &iov, 1, len)); 441a6d42e7dSPeter Dunlap } 442a6d42e7dSPeter Dunlap 443a6d42e7dSPeter Dunlap /* 444a6d42e7dSPeter Dunlap * idm_sosendto - Sends a buffered data on a non-connected socket. 445a6d42e7dSPeter Dunlap * 446a6d42e7dSPeter Dunlap * This function puts the data provided on the wire by calling sosendmsg. 447a6d42e7dSPeter Dunlap * It will return only when all the data has been sent or if an error 448a6d42e7dSPeter Dunlap * occurs. 449a6d42e7dSPeter Dunlap * 450a6d42e7dSPeter Dunlap * Returns 0 for success, the socket errno value if sosendmsg fails, and 451a6d42e7dSPeter Dunlap * -1 if sosendmsg returns success but uio_resid != 0 452a6d42e7dSPeter Dunlap */ 453a6d42e7dSPeter Dunlap int 4540f1702c5SYu Xiangning idm_sosendto(ksocket_t so, void *buff, size_t len, 455a6d42e7dSPeter Dunlap struct sockaddr *name, socklen_t namelen) 456a6d42e7dSPeter Dunlap { 457a6d42e7dSPeter Dunlap struct msghdr msg; 458a6d42e7dSPeter Dunlap struct iovec iov[1]; 459a6d42e7dSPeter Dunlap int error; 4600f1702c5SYu Xiangning size_t sent = 0; 461a6d42e7dSPeter Dunlap 462a6d42e7dSPeter Dunlap iov[0].iov_base = buff; 463a6d42e7dSPeter Dunlap iov[0].iov_len = len; 464a6d42e7dSPeter Dunlap 465a6d42e7dSPeter Dunlap /* Initialization of the message header. */ 466a6d42e7dSPeter Dunlap bzero(&msg, sizeof (msg)); 467a6d42e7dSPeter Dunlap msg.msg_iov = iov; 468a6d42e7dSPeter Dunlap msg.msg_iovlen = 1; 469a6d42e7dSPeter Dunlap msg.msg_name = name; 470a6d42e7dSPeter Dunlap msg.msg_namelen = namelen; 471a6d42e7dSPeter Dunlap 4720f1702c5SYu Xiangning if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) == 0) { 473a6d42e7dSPeter Dunlap /* Data sent */ 4740f1702c5SYu Xiangning if (sent == len) { 475a6d42e7dSPeter Dunlap /* All data sent. Success. */ 476a6d42e7dSPeter Dunlap return (0); 477a6d42e7dSPeter Dunlap } else { 478a6d42e7dSPeter Dunlap /* Not all data was sent. Failure */ 479a6d42e7dSPeter Dunlap return (-1); 480a6d42e7dSPeter Dunlap } 481a6d42e7dSPeter Dunlap } 482a6d42e7dSPeter Dunlap 483a6d42e7dSPeter Dunlap /* Send failed */ 484a6d42e7dSPeter Dunlap return (error); 485a6d42e7dSPeter Dunlap } 486a6d42e7dSPeter Dunlap 487a6d42e7dSPeter Dunlap /* 488a6d42e7dSPeter Dunlap * idm_iov_sosend - Sends an iovec on a connection. 489a6d42e7dSPeter Dunlap * 490a6d42e7dSPeter Dunlap * This function puts the data provided on the wire by calling sosendmsg. 491a6d42e7dSPeter Dunlap * It will return only when all the data has been sent or if an error 492a6d42e7dSPeter Dunlap * occurs. 493a6d42e7dSPeter Dunlap * 494a6d42e7dSPeter Dunlap * Returns 0 for success, the socket errno value if sosendmsg fails, and 495a6d42e7dSPeter Dunlap * -1 if sosendmsg returns success but uio_resid != 0 496a6d42e7dSPeter Dunlap */ 497a6d42e7dSPeter Dunlap int 4980f1702c5SYu Xiangning idm_iov_sosend(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len) 499a6d42e7dSPeter Dunlap { 500a6d42e7dSPeter Dunlap struct msghdr msg; 501a6d42e7dSPeter Dunlap int error; 5020f1702c5SYu Xiangning size_t sent = 0; 503a6d42e7dSPeter Dunlap 504a6d42e7dSPeter Dunlap ASSERT(iop != NULL); 505a6d42e7dSPeter Dunlap 506a6d42e7dSPeter Dunlap /* Initialization of the message header. */ 507a6d42e7dSPeter Dunlap bzero(&msg, sizeof (msg)); 508a6d42e7dSPeter Dunlap msg.msg_iov = iop; 509a6d42e7dSPeter Dunlap msg.msg_iovlen = iovlen; 510a6d42e7dSPeter Dunlap 5110f1702c5SYu Xiangning if ((error = ksocket_sendmsg(so, &msg, 0, &sent, CRED())) 5120f1702c5SYu Xiangning == 0) { 513a6d42e7dSPeter Dunlap /* Data sent */ 5140f1702c5SYu Xiangning if (sent == total_len) { 515a6d42e7dSPeter Dunlap /* All data sent. Success. */ 516a6d42e7dSPeter Dunlap return (0); 517a6d42e7dSPeter Dunlap } else { 518a6d42e7dSPeter Dunlap /* Not all data was sent. Failure */ 519a6d42e7dSPeter Dunlap return (-1); 520a6d42e7dSPeter Dunlap } 521a6d42e7dSPeter Dunlap } 522a6d42e7dSPeter Dunlap 523a6d42e7dSPeter Dunlap /* Send failed */ 524a6d42e7dSPeter Dunlap return (error); 525a6d42e7dSPeter Dunlap } 526a6d42e7dSPeter Dunlap 527a6d42e7dSPeter Dunlap /* 528a6d42e7dSPeter Dunlap * idm_iov_sorecv - Receives an iovec from a connection 529a6d42e7dSPeter Dunlap * 530a6d42e7dSPeter Dunlap * This function gets the data asked for from the socket. It will return 531a6d42e7dSPeter Dunlap * only when all the requested data has been retrieved or if an error 532a6d42e7dSPeter Dunlap * occurs. 533a6d42e7dSPeter Dunlap * 534a6d42e7dSPeter Dunlap * Returns 0 for success, the socket errno value if sorecvmsg fails, and 535a6d42e7dSPeter Dunlap * -1 if sorecvmsg returns success but uio_resid != 0 536a6d42e7dSPeter Dunlap */ 537a6d42e7dSPeter Dunlap int 5380f1702c5SYu Xiangning idm_iov_sorecv(ksocket_t so, iovec_t *iop, int iovlen, size_t total_len) 539a6d42e7dSPeter Dunlap { 540a6d42e7dSPeter Dunlap struct msghdr msg; 541a6d42e7dSPeter Dunlap int error; 5420f1702c5SYu Xiangning size_t recv; 5430f1702c5SYu Xiangning int flags; 544a6d42e7dSPeter Dunlap 545a6d42e7dSPeter Dunlap ASSERT(iop != NULL); 546a6d42e7dSPeter Dunlap 547a6d42e7dSPeter Dunlap /* Initialization of the message header. */ 548a6d42e7dSPeter Dunlap bzero(&msg, sizeof (msg)); 549a6d42e7dSPeter Dunlap msg.msg_iov = iop; 550a6d42e7dSPeter Dunlap msg.msg_iovlen = iovlen; 5510f1702c5SYu Xiangning flags = MSG_WAITALL; 552a6d42e7dSPeter Dunlap 5530f1702c5SYu Xiangning if ((error = ksocket_recvmsg(so, &msg, flags, &recv, CRED())) 5540f1702c5SYu Xiangning == 0) { 555a6d42e7dSPeter Dunlap /* Received data */ 5560f1702c5SYu Xiangning if (recv == total_len) { 557a6d42e7dSPeter Dunlap /* All requested data received. Success */ 558a6d42e7dSPeter Dunlap return (0); 559a6d42e7dSPeter Dunlap } else { 560a6d42e7dSPeter Dunlap /* 561a6d42e7dSPeter Dunlap * Not all data was received. The connection has 562a6d42e7dSPeter Dunlap * probably failed. 563a6d42e7dSPeter Dunlap */ 564a6d42e7dSPeter Dunlap return (-1); 565a6d42e7dSPeter Dunlap } 566a6d42e7dSPeter Dunlap } 567a6d42e7dSPeter Dunlap 568a6d42e7dSPeter Dunlap /* Receive failed */ 569a6d42e7dSPeter Dunlap return (error); 570a6d42e7dSPeter Dunlap } 571a6d42e7dSPeter Dunlap 572a6d42e7dSPeter Dunlap static void 573a6d42e7dSPeter Dunlap idm_set_ini_preconnect_options(idm_so_conn_t *sc) 574a6d42e7dSPeter Dunlap { 575a6d42e7dSPeter Dunlap int conn_abort = 10000; 576a6d42e7dSPeter Dunlap int conn_notify = 2000; 577a6d42e7dSPeter Dunlap int abort = 30000; 578a6d42e7dSPeter Dunlap 579a6d42e7dSPeter Dunlap /* Pre-connect socket options */ 5800f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, 5810f1702c5SYu Xiangning TCP_CONN_NOTIFY_THRESHOLD, (char *)&conn_notify, sizeof (int), 5820f1702c5SYu Xiangning CRED()); 5830f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, 5840f1702c5SYu Xiangning TCP_CONN_ABORT_THRESHOLD, (char *)&conn_abort, sizeof (int), 5850f1702c5SYu Xiangning CRED()); 5860f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_ABORT_THRESHOLD, 5870f1702c5SYu Xiangning (char *)&abort, sizeof (int), CRED()); 588a6d42e7dSPeter Dunlap } 589a6d42e7dSPeter Dunlap 590a6d42e7dSPeter Dunlap static void 591a6d42e7dSPeter Dunlap idm_set_ini_postconnect_options(idm_so_conn_t *sc) 592a6d42e7dSPeter Dunlap { 593a6d42e7dSPeter Dunlap int32_t rcvbuf = IDM_RCVBUF_SIZE; 594a6d42e7dSPeter Dunlap int32_t sndbuf = IDM_SNDBUF_SIZE; 595a6d42e7dSPeter Dunlap const int on = 1; 596a6d42e7dSPeter Dunlap 597a6d42e7dSPeter Dunlap /* Set postconnect options */ 5980f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, IPPROTO_TCP, TCP_NODELAY, 5990f1702c5SYu Xiangning (char *)&on, sizeof (int), CRED()); 6000f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_RCVBUF, 6010f1702c5SYu Xiangning (char *)&rcvbuf, sizeof (int), CRED()); 6020f1702c5SYu Xiangning (void) ksocket_setsockopt(sc->ic_so, SOL_SOCKET, SO_SNDBUF, 6030f1702c5SYu Xiangning (char *)&sndbuf, sizeof (int), CRED()); 604a6d42e7dSPeter Dunlap } 605a6d42e7dSPeter Dunlap 606a6d42e7dSPeter Dunlap static void 6070f1702c5SYu Xiangning idm_set_tgt_connect_options(ksocket_t ks) 608a6d42e7dSPeter Dunlap { 609a6d42e7dSPeter Dunlap int32_t rcvbuf = IDM_RCVBUF_SIZE; 610a6d42e7dSPeter Dunlap int32_t sndbuf = IDM_SNDBUF_SIZE; 611a6d42e7dSPeter Dunlap const int on = 1; 612a6d42e7dSPeter Dunlap 613a6d42e7dSPeter Dunlap /* Set connect options */ 6140f1702c5SYu Xiangning (void) ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVBUF, 6150f1702c5SYu Xiangning (char *)&rcvbuf, sizeof (int), CRED()); 6160f1702c5SYu Xiangning (void) ksocket_setsockopt(ks, SOL_SOCKET, SO_SNDBUF, 6170f1702c5SYu Xiangning (char *)&sndbuf, sizeof (int), CRED()); 6180f1702c5SYu Xiangning (void) ksocket_setsockopt(ks, IPPROTO_TCP, TCP_NODELAY, 6190f1702c5SYu Xiangning (char *)&on, sizeof (on), CRED()); 620a6d42e7dSPeter Dunlap } 621a6d42e7dSPeter Dunlap 622a6d42e7dSPeter Dunlap static uint32_t 623a6d42e7dSPeter Dunlap n2h24(const uchar_t *ptr) 624a6d42e7dSPeter Dunlap { 625a6d42e7dSPeter Dunlap return ((ptr[0] << 16) | (ptr[1] << 8) | ptr[2]); 626a6d42e7dSPeter Dunlap } 627a6d42e7dSPeter Dunlap 628a6d42e7dSPeter Dunlap 629a6d42e7dSPeter Dunlap static idm_status_t 630a6d42e7dSPeter Dunlap idm_sorecvhdr(idm_conn_t *ic, idm_pdu_t *pdu) 631a6d42e7dSPeter Dunlap { 632a6d42e7dSPeter Dunlap iscsi_hdr_t *bhs; 633a6d42e7dSPeter Dunlap uint32_t hdr_digest_crc; 634a6d42e7dSPeter Dunlap uint32_t crc_calculated; 635a6d42e7dSPeter Dunlap void *new_hdr; 636a6d42e7dSPeter Dunlap int ahslen = 0; 637a6d42e7dSPeter Dunlap int total_len = 0; 638a6d42e7dSPeter Dunlap int iovlen = 0; 639a6d42e7dSPeter Dunlap struct iovec iov[2]; 640a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 641a6d42e7dSPeter Dunlap int rc; 642a6d42e7dSPeter Dunlap 643a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 644a6d42e7dSPeter Dunlap 645a6d42e7dSPeter Dunlap /* 646a6d42e7dSPeter Dunlap * Read BHS 647a6d42e7dSPeter Dunlap */ 648a6d42e7dSPeter Dunlap bhs = pdu->isp_hdr; 649a6d42e7dSPeter Dunlap rc = idm_sorecv(so_conn->ic_so, pdu->isp_hdr, sizeof (iscsi_hdr_t)); 650a6d42e7dSPeter Dunlap if (rc != IDM_STATUS_SUCCESS) { 651a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 652a6d42e7dSPeter Dunlap } 653a6d42e7dSPeter Dunlap 654a6d42e7dSPeter Dunlap /* 655a6d42e7dSPeter Dunlap * Check actual AHS length against the amount available in the buffer 656a6d42e7dSPeter Dunlap */ 657a6d42e7dSPeter Dunlap pdu->isp_hdrlen = sizeof (iscsi_hdr_t) + 658a6d42e7dSPeter Dunlap (bhs->hlength * sizeof (uint32_t)); 659a6d42e7dSPeter Dunlap pdu->isp_datalen = n2h24(bhs->dlength); 660a6d42e7dSPeter Dunlap if (bhs->hlength > IDM_SORX_CACHE_AHSLEN) { 661a6d42e7dSPeter Dunlap /* Allocate a new header segment and change the callback */ 662a6d42e7dSPeter Dunlap new_hdr = kmem_alloc(pdu->isp_hdrlen, KM_SLEEP); 663a6d42e7dSPeter Dunlap bcopy(pdu->isp_hdr, new_hdr, sizeof (iscsi_hdr_t)); 664a6d42e7dSPeter Dunlap pdu->isp_hdr = new_hdr; 665a6d42e7dSPeter Dunlap pdu->isp_flags |= IDM_PDU_ADDL_HDR; 666a6d42e7dSPeter Dunlap 667a6d42e7dSPeter Dunlap /* 668a6d42e7dSPeter Dunlap * This callback will restore the expected values after 669a6d42e7dSPeter Dunlap * the RX PDU has been processed. 670a6d42e7dSPeter Dunlap */ 671a6d42e7dSPeter Dunlap pdu->isp_callback = idm_sorx_addl_pdu_cb; 672a6d42e7dSPeter Dunlap } 673a6d42e7dSPeter Dunlap 674a6d42e7dSPeter Dunlap /* 675a6d42e7dSPeter Dunlap * Setup receipt of additional header and header digest (if enabled). 676a6d42e7dSPeter Dunlap */ 677a6d42e7dSPeter Dunlap if (bhs->hlength > 0) { 678a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)(pdu->isp_hdr + 1); 679a6d42e7dSPeter Dunlap ahslen = pdu->isp_hdrlen - sizeof (iscsi_hdr_t); 680a6d42e7dSPeter Dunlap iov[iovlen].iov_len = ahslen; 681a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 682a6d42e7dSPeter Dunlap iovlen++; 683a6d42e7dSPeter Dunlap } 684a6d42e7dSPeter Dunlap 685a6d42e7dSPeter Dunlap if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) { 686a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc; 687a6d42e7dSPeter Dunlap iov[iovlen].iov_len = sizeof (hdr_digest_crc); 688a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 689a6d42e7dSPeter Dunlap iovlen++; 690a6d42e7dSPeter Dunlap } 691a6d42e7dSPeter Dunlap 692a6d42e7dSPeter Dunlap if ((iovlen != 0) && 693a6d42e7dSPeter Dunlap (idm_iov_sorecv(so_conn->ic_so, &iov[0], iovlen, 694a6d42e7dSPeter Dunlap total_len) != 0)) { 695a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 696a6d42e7dSPeter Dunlap } 697a6d42e7dSPeter Dunlap 698a6d42e7dSPeter Dunlap /* 699a6d42e7dSPeter Dunlap * Validate header digest if enabled 700a6d42e7dSPeter Dunlap */ 701a6d42e7dSPeter Dunlap if (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST) { 702a6d42e7dSPeter Dunlap crc_calculated = idm_crc32c(pdu->isp_hdr, 703a6d42e7dSPeter Dunlap sizeof (iscsi_hdr_t) + ahslen); 704a6d42e7dSPeter Dunlap if (crc_calculated != hdr_digest_crc) { 705a6d42e7dSPeter Dunlap /* Invalid Header Digest */ 706a6d42e7dSPeter Dunlap return (IDM_STATUS_HEADER_DIGEST); 707a6d42e7dSPeter Dunlap } 708a6d42e7dSPeter Dunlap } 709a6d42e7dSPeter Dunlap 710a6d42e7dSPeter Dunlap return (0); 711a6d42e7dSPeter Dunlap } 712a6d42e7dSPeter Dunlap 713a6d42e7dSPeter Dunlap /* 714a6d42e7dSPeter Dunlap * idm_so_ini_conn_create() 715a6d42e7dSPeter Dunlap * Allocate the sockets transport connection resources. 716a6d42e7dSPeter Dunlap */ 717a6d42e7dSPeter Dunlap static idm_status_t 718a6d42e7dSPeter Dunlap idm_so_ini_conn_create(idm_conn_req_t *cr, idm_conn_t *ic) 719a6d42e7dSPeter Dunlap { 7200f1702c5SYu Xiangning ksocket_t so; 721a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 722a6d42e7dSPeter Dunlap idm_status_t idmrc; 723a6d42e7dSPeter Dunlap 724a6d42e7dSPeter Dunlap so = idm_socreate(cr->cr_domain, cr->cr_type, 725a6d42e7dSPeter Dunlap cr->cr_protocol); 726a6d42e7dSPeter Dunlap if (so == NULL) { 727a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 728a6d42e7dSPeter Dunlap } 729a6d42e7dSPeter Dunlap 730a6d42e7dSPeter Dunlap /* Bind the socket if configured to do so */ 731a6d42e7dSPeter Dunlap if (cr->cr_bound) { 7320f1702c5SYu Xiangning if (ksocket_bind(so, &cr->cr_bound_addr.sin, 7330f1702c5SYu Xiangning SIZEOF_SOCKADDR(&cr->cr_bound_addr.sin), CRED()) != 0) { 734a6d42e7dSPeter Dunlap idm_sodestroy(so); 735a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 736a6d42e7dSPeter Dunlap } 737a6d42e7dSPeter Dunlap } 738a6d42e7dSPeter Dunlap 739a6d42e7dSPeter Dunlap idmrc = idm_so_conn_create_common(ic, so); 740a6d42e7dSPeter Dunlap if (idmrc != IDM_STATUS_SUCCESS) { 741a6d42e7dSPeter Dunlap idm_soshutdown(so); 742a6d42e7dSPeter Dunlap idm_sodestroy(so); 743a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 744a6d42e7dSPeter Dunlap } 745a6d42e7dSPeter Dunlap 746a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 747a6d42e7dSPeter Dunlap /* Set up socket options */ 748a6d42e7dSPeter Dunlap idm_set_ini_preconnect_options(so_conn); 749a6d42e7dSPeter Dunlap 750a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 751a6d42e7dSPeter Dunlap } 752a6d42e7dSPeter Dunlap 753a6d42e7dSPeter Dunlap /* 754a6d42e7dSPeter Dunlap * idm_so_ini_conn_destroy() 755a6d42e7dSPeter Dunlap * Tear down the sockets transport connection resources. 756a6d42e7dSPeter Dunlap */ 757a6d42e7dSPeter Dunlap static void 758a6d42e7dSPeter Dunlap idm_so_ini_conn_destroy(idm_conn_t *ic) 759a6d42e7dSPeter Dunlap { 760a6d42e7dSPeter Dunlap idm_so_conn_destroy_common(ic); 761a6d42e7dSPeter Dunlap } 762a6d42e7dSPeter Dunlap 763a6d42e7dSPeter Dunlap /* 764a6d42e7dSPeter Dunlap * idm_so_ini_conn_connect() 765a6d42e7dSPeter Dunlap * Establish the connection referred to by the handle previously allocated via 766a6d42e7dSPeter Dunlap * idm_so_ini_conn_create(). 767a6d42e7dSPeter Dunlap */ 768a6d42e7dSPeter Dunlap static idm_status_t 769a6d42e7dSPeter Dunlap idm_so_ini_conn_connect(idm_conn_t *ic) 770a6d42e7dSPeter Dunlap { 771a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 772a6d42e7dSPeter Dunlap 773a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 774a6d42e7dSPeter Dunlap 7750f1702c5SYu Xiangning if (ksocket_connect(so_conn->ic_so, &ic->ic_ini_dst_addr.sin, 7760f1702c5SYu Xiangning (SIZEOF_SOCKADDR(&ic->ic_ini_dst_addr.sin)), CRED()) != 0) { 777a6d42e7dSPeter Dunlap idm_soshutdown(so_conn->ic_so); 778a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 779a6d42e7dSPeter Dunlap } 780a6d42e7dSPeter Dunlap 781a6d42e7dSPeter Dunlap idm_so_conn_connect_common(ic); 782a6d42e7dSPeter Dunlap 783a6d42e7dSPeter Dunlap idm_set_ini_postconnect_options(so_conn); 784a6d42e7dSPeter Dunlap 785a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 786a6d42e7dSPeter Dunlap } 787a6d42e7dSPeter Dunlap 788a6d42e7dSPeter Dunlap idm_status_t 7890f1702c5SYu Xiangning idm_so_tgt_conn_create(idm_conn_t *ic, ksocket_t new_so) 790a6d42e7dSPeter Dunlap { 791a6d42e7dSPeter Dunlap idm_status_t idmrc; 792a6d42e7dSPeter Dunlap 793a6d42e7dSPeter Dunlap idmrc = idm_so_conn_create_common(ic, new_so); 794a6d42e7dSPeter Dunlap 795a6d42e7dSPeter Dunlap return (idmrc); 796a6d42e7dSPeter Dunlap } 797a6d42e7dSPeter Dunlap 798a6d42e7dSPeter Dunlap static void 799a6d42e7dSPeter Dunlap idm_so_tgt_conn_destroy(idm_conn_t *ic) 800a6d42e7dSPeter Dunlap { 801a6d42e7dSPeter Dunlap idm_so_conn_destroy_common(ic); 802a6d42e7dSPeter Dunlap } 803a6d42e7dSPeter Dunlap 804a6d42e7dSPeter Dunlap /* 805a6d42e7dSPeter Dunlap * idm_so_tgt_conn_connect() 806a6d42e7dSPeter Dunlap * Establish the connection in ic, passed from idm_tgt_conn_finish(), which 807a6d42e7dSPeter Dunlap * is invoked from the SM as a result of an inbound connection request. 808a6d42e7dSPeter Dunlap */ 809a6d42e7dSPeter Dunlap static idm_status_t 810a6d42e7dSPeter Dunlap idm_so_tgt_conn_connect(idm_conn_t *ic) 811a6d42e7dSPeter Dunlap { 812a6d42e7dSPeter Dunlap idm_so_conn_connect_common(ic); 813a6d42e7dSPeter Dunlap 814a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 815a6d42e7dSPeter Dunlap } 816a6d42e7dSPeter Dunlap 817a6d42e7dSPeter Dunlap static idm_status_t 8180f1702c5SYu Xiangning idm_so_conn_create_common(idm_conn_t *ic, ksocket_t new_so) 819a6d42e7dSPeter Dunlap { 820a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 821a6d42e7dSPeter Dunlap 822a6d42e7dSPeter Dunlap so_conn = kmem_zalloc(sizeof (idm_so_conn_t), KM_SLEEP); 823a6d42e7dSPeter Dunlap so_conn->ic_so = new_so; 824a6d42e7dSPeter Dunlap 825a6d42e7dSPeter Dunlap ic->ic_transport_private = so_conn; 826a6d42e7dSPeter Dunlap ic->ic_transport_hdrlen = 0; 827a6d42e7dSPeter Dunlap 828a6d42e7dSPeter Dunlap /* Set the scoreboarding flag on this connection */ 829a6d42e7dSPeter Dunlap ic->ic_conn_flags |= IDM_CONN_USE_SCOREBOARD; 830a6d42e7dSPeter Dunlap 831a6d42e7dSPeter Dunlap /* 832a6d42e7dSPeter Dunlap * Initialize tx thread mutex and list 833a6d42e7dSPeter Dunlap */ 834a6d42e7dSPeter Dunlap mutex_init(&so_conn->ic_tx_mutex, NULL, MUTEX_DEFAULT, NULL); 835a6d42e7dSPeter Dunlap cv_init(&so_conn->ic_tx_cv, NULL, CV_DEFAULT, NULL); 836a6d42e7dSPeter Dunlap list_create(&so_conn->ic_tx_list, sizeof (idm_pdu_t), 837a6d42e7dSPeter Dunlap offsetof(idm_pdu_t, idm_tx_link)); 838a6d42e7dSPeter Dunlap 839a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 840a6d42e7dSPeter Dunlap } 841a6d42e7dSPeter Dunlap 842a6d42e7dSPeter Dunlap static void 843a6d42e7dSPeter Dunlap idm_so_conn_destroy_common(idm_conn_t *ic) 844a6d42e7dSPeter Dunlap { 845a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn = ic->ic_transport_private; 846a6d42e7dSPeter Dunlap 847a6d42e7dSPeter Dunlap ic->ic_transport_private = NULL; 848a6d42e7dSPeter Dunlap idm_sodestroy(so_conn->ic_so); 849a6d42e7dSPeter Dunlap list_destroy(&so_conn->ic_tx_list); 850a6d42e7dSPeter Dunlap mutex_destroy(&so_conn->ic_tx_mutex); 851a6d42e7dSPeter Dunlap cv_destroy(&so_conn->ic_tx_cv); 852a6d42e7dSPeter Dunlap 853a6d42e7dSPeter Dunlap kmem_free(so_conn, sizeof (idm_so_conn_t)); 854a6d42e7dSPeter Dunlap } 855a6d42e7dSPeter Dunlap 856a6d42e7dSPeter Dunlap static void 857a6d42e7dSPeter Dunlap idm_so_conn_connect_common(idm_conn_t *ic) 858a6d42e7dSPeter Dunlap { 859a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 8600f1702c5SYu Xiangning struct sockaddr_in6 t_addr; 8610f1702c5SYu Xiangning socklen_t t_addrlen = 0; 862a6d42e7dSPeter Dunlap 863a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 8640f1702c5SYu Xiangning bzero(&t_addr, sizeof (struct sockaddr_in6)); 8650f1702c5SYu Xiangning t_addrlen = sizeof (struct sockaddr_in6); 866a6d42e7dSPeter Dunlap 867a6d42e7dSPeter Dunlap /* Set the local and remote addresses in the idm conn handle */ 8680f1702c5SYu Xiangning ksocket_getsockname(so_conn->ic_so, (struct sockaddr *)&t_addr, 8690f1702c5SYu Xiangning &t_addrlen, CRED()); 8700f1702c5SYu Xiangning bcopy(&t_addr, &ic->ic_laddr, t_addrlen); 8710f1702c5SYu Xiangning ksocket_getpeername(so_conn->ic_so, (struct sockaddr *)&t_addr, 8720f1702c5SYu Xiangning &t_addrlen, CRED()); 8730f1702c5SYu Xiangning bcopy(&t_addr, &ic->ic_raddr, t_addrlen); 874a6d42e7dSPeter Dunlap 875a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 876a6d42e7dSPeter Dunlap so_conn->ic_tx_thread = thread_create(NULL, 0, idm_sotx_thread, ic, 0, 877a6d42e7dSPeter Dunlap &p0, TS_RUN, minclsyspri); 878a6d42e7dSPeter Dunlap so_conn->ic_rx_thread = thread_create(NULL, 0, idm_sorx_thread, ic, 0, 879a6d42e7dSPeter Dunlap &p0, TS_RUN, minclsyspri); 880a6d42e7dSPeter Dunlap 881a6d42e7dSPeter Dunlap while (!so_conn->ic_rx_thread_running || !so_conn->ic_tx_thread_running) 882a6d42e7dSPeter Dunlap cv_wait(&ic->ic_cv, &ic->ic_mutex); 883a6d42e7dSPeter Dunlap mutex_exit(&ic->ic_mutex); 884a6d42e7dSPeter Dunlap } 885a6d42e7dSPeter Dunlap 886a6d42e7dSPeter Dunlap /* 887a6d42e7dSPeter Dunlap * idm_so_conn_disconnect() 888a6d42e7dSPeter Dunlap * Shutdown the socket connection and stop the thread 889a6d42e7dSPeter Dunlap */ 890a6d42e7dSPeter Dunlap static void 891a6d42e7dSPeter Dunlap idm_so_conn_disconnect(idm_conn_t *ic) 892a6d42e7dSPeter Dunlap { 893a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 894a6d42e7dSPeter Dunlap 895a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 896a6d42e7dSPeter Dunlap 897a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 898a6d42e7dSPeter Dunlap so_conn->ic_rx_thread_running = B_FALSE; 899a6d42e7dSPeter Dunlap so_conn->ic_tx_thread_running = B_FALSE; 900a6d42e7dSPeter Dunlap /* We need to wakeup the TX thread */ 901a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 902a6d42e7dSPeter Dunlap cv_signal(&so_conn->ic_tx_cv); 903a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 904a6d42e7dSPeter Dunlap mutex_exit(&ic->ic_mutex); 905a6d42e7dSPeter Dunlap 906a6d42e7dSPeter Dunlap /* This should wakeup the RX thread if it is sleeping */ 907a6d42e7dSPeter Dunlap idm_soshutdown(so_conn->ic_so); 908a6d42e7dSPeter Dunlap 909a6d42e7dSPeter Dunlap thread_join(so_conn->ic_tx_thread_did); 910a6d42e7dSPeter Dunlap thread_join(so_conn->ic_rx_thread_did); 911a6d42e7dSPeter Dunlap } 912a6d42e7dSPeter Dunlap 913a6d42e7dSPeter Dunlap /* 914a6d42e7dSPeter Dunlap * idm_so_tgt_svc_create() 915a6d42e7dSPeter Dunlap * Establish a service on an IP address and port. idm_svc_req_t contains 916a6d42e7dSPeter Dunlap * the service parameters. 917a6d42e7dSPeter Dunlap */ 918a6d42e7dSPeter Dunlap /*ARGSUSED*/ 919a6d42e7dSPeter Dunlap static idm_status_t 920a6d42e7dSPeter Dunlap idm_so_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t *is) 921a6d42e7dSPeter Dunlap { 922a6d42e7dSPeter Dunlap idm_so_svc_t *so_svc; 923a6d42e7dSPeter Dunlap 924a6d42e7dSPeter Dunlap so_svc = kmem_zalloc(sizeof (idm_so_svc_t), KM_SLEEP); 925a6d42e7dSPeter Dunlap 926a6d42e7dSPeter Dunlap /* Set the new sockets service in svc handle */ 927a6d42e7dSPeter Dunlap is->is_so_svc = (void *)so_svc; 928a6d42e7dSPeter Dunlap 929a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 930a6d42e7dSPeter Dunlap } 931a6d42e7dSPeter Dunlap 932a6d42e7dSPeter Dunlap /* 933a6d42e7dSPeter Dunlap * idm_so_tgt_svc_destroy() 934a6d42e7dSPeter Dunlap * Teardown sockets resources allocated in idm_so_tgt_svc_create() 935a6d42e7dSPeter Dunlap */ 936a6d42e7dSPeter Dunlap static void 937a6d42e7dSPeter Dunlap idm_so_tgt_svc_destroy(idm_svc_t *is) 938a6d42e7dSPeter Dunlap { 939a6d42e7dSPeter Dunlap /* the socket will have been torn down; free the service */ 940a6d42e7dSPeter Dunlap kmem_free(is->is_so_svc, sizeof (idm_so_svc_t)); 941a6d42e7dSPeter Dunlap } 942a6d42e7dSPeter Dunlap 943a6d42e7dSPeter Dunlap /* 944a6d42e7dSPeter Dunlap * idm_so_tgt_svc_online() 945a6d42e7dSPeter Dunlap * Launch a watch thread on the svc allocated in idm_so_tgt_svc_create() 946a6d42e7dSPeter Dunlap */ 947a6d42e7dSPeter Dunlap 948a6d42e7dSPeter Dunlap static idm_status_t 949a6d42e7dSPeter Dunlap idm_so_tgt_svc_online(idm_svc_t *is) 950a6d42e7dSPeter Dunlap { 951a6d42e7dSPeter Dunlap idm_so_svc_t *so_svc; 952a6d42e7dSPeter Dunlap idm_svc_req_t *sr = &is->is_svc_req; 953a6d42e7dSPeter Dunlap struct sockaddr_in6 sin6_ip; 954a6d42e7dSPeter Dunlap const uint32_t on = 1; 955a6d42e7dSPeter Dunlap const uint32_t off = 0; 956a6d42e7dSPeter Dunlap 957a6d42e7dSPeter Dunlap mutex_enter(&is->is_mutex); 958a6d42e7dSPeter Dunlap so_svc = (idm_so_svc_t *)is->is_so_svc; 959a6d42e7dSPeter Dunlap 960a6d42e7dSPeter Dunlap /* 961a6d42e7dSPeter Dunlap * Try creating an IPv6 socket first 962a6d42e7dSPeter Dunlap */ 963a6d42e7dSPeter Dunlap if ((so_svc->is_so = idm_socreate(PF_INET6, SOCK_STREAM, 0)) == NULL) { 964a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 965a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 966a6d42e7dSPeter Dunlap } else { 967a6d42e7dSPeter Dunlap bzero(&sin6_ip, sizeof (sin6_ip)); 968a6d42e7dSPeter Dunlap sin6_ip.sin6_family = AF_INET6; 969a6d42e7dSPeter Dunlap sin6_ip.sin6_port = htons(sr->sr_port); 970a6d42e7dSPeter Dunlap sin6_ip.sin6_addr = in6addr_any; 971a6d42e7dSPeter Dunlap 9720f1702c5SYu Xiangning (void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET, 9730f1702c5SYu Xiangning SO_REUSEADDR, (char *)&on, sizeof (on), CRED()); 974a6d42e7dSPeter Dunlap /* 975a6d42e7dSPeter Dunlap * Turn off SO_MAC_EXEMPT so future sobinds succeed 976a6d42e7dSPeter Dunlap */ 9770f1702c5SYu Xiangning (void) ksocket_setsockopt(so_svc->is_so, SOL_SOCKET, 9780f1702c5SYu Xiangning SO_MAC_EXEMPT, (char *)&off, sizeof (off), CRED()); 979a6d42e7dSPeter Dunlap 9800f1702c5SYu Xiangning if (ksocket_bind(so_svc->is_so, (struct sockaddr *)&sin6_ip, 9810f1702c5SYu Xiangning sizeof (sin6_ip), CRED()) != 0) { 982a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 983a6d42e7dSPeter Dunlap idm_sodestroy(so_svc->is_so); 984a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 985a6d42e7dSPeter Dunlap } 986a6d42e7dSPeter Dunlap } 987a6d42e7dSPeter Dunlap 988a6d42e7dSPeter Dunlap idm_set_tgt_connect_options(so_svc->is_so); 989a6d42e7dSPeter Dunlap 9900f1702c5SYu Xiangning if (ksocket_listen(so_svc->is_so, 5, CRED()) != 0) { 991a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 992a6d42e7dSPeter Dunlap idm_soshutdown(so_svc->is_so); 993a6d42e7dSPeter Dunlap idm_sodestroy(so_svc->is_so); 994a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 995a6d42e7dSPeter Dunlap } 996a6d42e7dSPeter Dunlap 997a6d42e7dSPeter Dunlap /* Launch a watch thread */ 998a6d42e7dSPeter Dunlap so_svc->is_thread = thread_create(NULL, 0, idm_so_svc_port_watcher, 999a6d42e7dSPeter Dunlap is, 0, &p0, TS_RUN, minclsyspri); 1000a6d42e7dSPeter Dunlap 1001a6d42e7dSPeter Dunlap if (so_svc->is_thread == NULL) { 1002a6d42e7dSPeter Dunlap /* Failure to launch; teardown the socket */ 1003a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 1004a6d42e7dSPeter Dunlap idm_soshutdown(so_svc->is_so); 1005a6d42e7dSPeter Dunlap idm_sodestroy(so_svc->is_so); 1006a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 1007a6d42e7dSPeter Dunlap } 10080f1702c5SYu Xiangning ksocket_hold(so_svc->is_so); 1009a6d42e7dSPeter Dunlap /* Wait for the port watcher thread to start */ 1010a6d42e7dSPeter Dunlap while (!so_svc->is_thread_running) 1011a6d42e7dSPeter Dunlap cv_wait(&is->is_cv, &is->is_mutex); 1012a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 1013a6d42e7dSPeter Dunlap 1014a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 1015a6d42e7dSPeter Dunlap } 1016a6d42e7dSPeter Dunlap 1017a6d42e7dSPeter Dunlap /* 1018a6d42e7dSPeter Dunlap * idm_so_tgt_svc_offline 1019a6d42e7dSPeter Dunlap * 1020a6d42e7dSPeter Dunlap * Stop listening on the IP address and port identified by idm_svc_t. 1021a6d42e7dSPeter Dunlap */ 1022a6d42e7dSPeter Dunlap static void 1023a6d42e7dSPeter Dunlap idm_so_tgt_svc_offline(idm_svc_t *is) 1024a6d42e7dSPeter Dunlap { 1025a6d42e7dSPeter Dunlap idm_so_svc_t *so_svc; 1026a6d42e7dSPeter Dunlap mutex_enter(&is->is_mutex); 1027a6d42e7dSPeter Dunlap so_svc = (idm_so_svc_t *)is->is_so_svc; 1028a6d42e7dSPeter Dunlap so_svc->is_thread_running = B_FALSE; 1029a6d42e7dSPeter Dunlap mutex_exit(&is->is_mutex); 1030a6d42e7dSPeter Dunlap 1031a6d42e7dSPeter Dunlap /* 10320f1702c5SYu Xiangning * Teardown socket 1033a6d42e7dSPeter Dunlap */ 10340f1702c5SYu Xiangning idm_sodestroy(so_svc->is_so); 1035a6d42e7dSPeter Dunlap 1036a6d42e7dSPeter Dunlap /* 1037a6d42e7dSPeter Dunlap * Now we expect the port watcher thread to terminate 1038a6d42e7dSPeter Dunlap */ 1039a6d42e7dSPeter Dunlap thread_join(so_svc->is_thread_did); 1040a6d42e7dSPeter Dunlap } 1041a6d42e7dSPeter Dunlap 1042a6d42e7dSPeter Dunlap /* 1043a6d42e7dSPeter Dunlap * Watch thread for target service connection establishment. 1044a6d42e7dSPeter Dunlap */ 1045a6d42e7dSPeter Dunlap void 1046a6d42e7dSPeter Dunlap idm_so_svc_port_watcher(void *arg) 1047a6d42e7dSPeter Dunlap { 1048a6d42e7dSPeter Dunlap idm_svc_t *svc = arg; 10490f1702c5SYu Xiangning ksocket_t new_so; 1050a6d42e7dSPeter Dunlap idm_conn_t *ic; 1051a6d42e7dSPeter Dunlap idm_status_t idmrc; 1052a6d42e7dSPeter Dunlap idm_so_svc_t *so_svc; 1053a6d42e7dSPeter Dunlap int rc; 1054a6d42e7dSPeter Dunlap const uint32_t off = 0; 10550f1702c5SYu Xiangning struct sockaddr_in6 t_addr; 10560f1702c5SYu Xiangning socklen_t t_addrlen; 1057a6d42e7dSPeter Dunlap 10580f1702c5SYu Xiangning bzero(&t_addr, sizeof (struct sockaddr_in6)); 10590f1702c5SYu Xiangning t_addrlen = sizeof (struct sockaddr_in6); 1060a6d42e7dSPeter Dunlap mutex_enter(&svc->is_mutex); 1061a6d42e7dSPeter Dunlap 1062a6d42e7dSPeter Dunlap so_svc = svc->is_so_svc; 1063a6d42e7dSPeter Dunlap so_svc->is_thread_running = B_TRUE; 1064a6d42e7dSPeter Dunlap so_svc->is_thread_did = so_svc->is_thread->t_did; 1065a6d42e7dSPeter Dunlap 1066a6d42e7dSPeter Dunlap cv_signal(&svc->is_cv); 1067a6d42e7dSPeter Dunlap 1068a6d42e7dSPeter Dunlap IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) online", (void *)svc, 1069a6d42e7dSPeter Dunlap svc->is_svc_req.sr_port); 1070a6d42e7dSPeter Dunlap 1071a6d42e7dSPeter Dunlap while (so_svc->is_thread_running) { 1072a6d42e7dSPeter Dunlap mutex_exit(&svc->is_mutex); 1073a6d42e7dSPeter Dunlap 10740f1702c5SYu Xiangning if ((rc = ksocket_accept(so_svc->is_so, 10750f1702c5SYu Xiangning (struct sockaddr *)&t_addr, &t_addrlen, 10760f1702c5SYu Xiangning &new_so, CRED())) != 0) { 1077a6d42e7dSPeter Dunlap mutex_enter(&svc->is_mutex); 1078a6d42e7dSPeter Dunlap if (rc == ECONNABORTED) 1079a6d42e7dSPeter Dunlap continue; 1080a6d42e7dSPeter Dunlap /* Connection problem */ 1081a6d42e7dSPeter Dunlap break; 1082a6d42e7dSPeter Dunlap } 1083a6d42e7dSPeter Dunlap /* 1084a6d42e7dSPeter Dunlap * Turn off SO_MAC_EXEMPT so future sobinds succeed 1085a6d42e7dSPeter Dunlap */ 10860f1702c5SYu Xiangning (void) ksocket_setsockopt(new_so, SOL_SOCKET, SO_MAC_EXEMPT, 10870f1702c5SYu Xiangning (char *)&off, sizeof (off), CRED()); 1088a6d42e7dSPeter Dunlap 1089a6d42e7dSPeter Dunlap idmrc = idm_svc_conn_create(svc, IDM_TRANSPORT_TYPE_SOCKETS, 1090a6d42e7dSPeter Dunlap &ic); 1091a6d42e7dSPeter Dunlap if (idmrc != IDM_STATUS_SUCCESS) { 1092a6d42e7dSPeter Dunlap /* Drop connection */ 1093a6d42e7dSPeter Dunlap idm_soshutdown(new_so); 1094a6d42e7dSPeter Dunlap idm_sodestroy(new_so); 1095a6d42e7dSPeter Dunlap mutex_enter(&svc->is_mutex); 1096a6d42e7dSPeter Dunlap continue; 1097a6d42e7dSPeter Dunlap } 1098a6d42e7dSPeter Dunlap 1099a6d42e7dSPeter Dunlap idmrc = idm_so_tgt_conn_create(ic, new_so); 1100a6d42e7dSPeter Dunlap if (idmrc != IDM_STATUS_SUCCESS) { 1101a6d42e7dSPeter Dunlap idm_svc_conn_destroy(ic); 1102a6d42e7dSPeter Dunlap idm_soshutdown(new_so); 1103a6d42e7dSPeter Dunlap idm_sodestroy(new_so); 1104a6d42e7dSPeter Dunlap mutex_enter(&svc->is_mutex); 1105a6d42e7dSPeter Dunlap continue; 1106a6d42e7dSPeter Dunlap } 1107a6d42e7dSPeter Dunlap 1108a6d42e7dSPeter Dunlap /* 1109a6d42e7dSPeter Dunlap * Kick the state machine. At CS_S3_XPT_UP the state machine 1110a6d42e7dSPeter Dunlap * will notify the client (target) about the new connection. 1111a6d42e7dSPeter Dunlap */ 1112a6d42e7dSPeter Dunlap idm_conn_event(ic, CE_CONNECT_ACCEPT, NULL); 1113a6d42e7dSPeter Dunlap 1114a6d42e7dSPeter Dunlap mutex_enter(&svc->is_mutex); 1115a6d42e7dSPeter Dunlap } 11160f1702c5SYu Xiangning ksocket_rele(so_svc->is_so); 1117a6d42e7dSPeter Dunlap so_svc->is_thread_running = B_FALSE; 1118a6d42e7dSPeter Dunlap mutex_exit(&svc->is_mutex); 1119a6d42e7dSPeter Dunlap 1120a6d42e7dSPeter Dunlap IDM_SVC_LOG(CE_NOTE, "iSCSI service (%p/%d) offline", (void *)svc, 1121a6d42e7dSPeter Dunlap svc->is_svc_req.sr_port); 1122a6d42e7dSPeter Dunlap 1123a6d42e7dSPeter Dunlap thread_exit(); 1124a6d42e7dSPeter Dunlap } 1125a6d42e7dSPeter Dunlap 1126a6d42e7dSPeter Dunlap /* 1127a6d42e7dSPeter Dunlap * idm_so_free_task_rsrc() stops any ongoing processing of the task and 1128a6d42e7dSPeter Dunlap * frees resources associated with the task. 1129a6d42e7dSPeter Dunlap * 1130a6d42e7dSPeter Dunlap * It's not clear that this should return idm_status_t. What do we do 1131a6d42e7dSPeter Dunlap * if it fails? 1132a6d42e7dSPeter Dunlap */ 1133a6d42e7dSPeter Dunlap static idm_status_t 1134a6d42e7dSPeter Dunlap idm_so_free_task_rsrc(idm_task_t *idt) 1135a6d42e7dSPeter Dunlap { 1136a6d42e7dSPeter Dunlap idm_buf_t *idb; 1137a6d42e7dSPeter Dunlap 113830e7468fSPeter Dunlap /* 113930e7468fSPeter Dunlap * There is nothing to cleanup on initiator connections 114030e7468fSPeter Dunlap */ 114130e7468fSPeter Dunlap if (IDM_CONN_ISINI(idt->idt_ic)) 114230e7468fSPeter Dunlap return (IDM_STATUS_SUCCESS); 114330e7468fSPeter Dunlap 1144a6d42e7dSPeter Dunlap /* 1145a6d42e7dSPeter Dunlap * If this is a target connection, call idm_buf_rx_from_ini_done for 1146a6d42e7dSPeter Dunlap * any buffer on the "outbufv" list with idb->idb_in_transport==B_TRUE. 1147a6d42e7dSPeter Dunlap * 1148a6d42e7dSPeter Dunlap * In addition, remove any buffers associated with this task from 1149a6d42e7dSPeter Dunlap * the ic_tx_list. We'll do this by walking the idt_inbufv list, but 1150a6d42e7dSPeter Dunlap * items don't actually get removed from that list (and completion 1151a6d42e7dSPeter Dunlap * routines called) until idm_task_cleanup. 1152a6d42e7dSPeter Dunlap */ 1153a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1154a6d42e7dSPeter Dunlap 1155a6d42e7dSPeter Dunlap for (idb = list_head(&idt->idt_outbufv); idb != NULL; 1156a6d42e7dSPeter Dunlap idb = list_next(&idt->idt_outbufv, idb)) { 1157a6d42e7dSPeter Dunlap if (idb->idb_in_transport) { 1158a6d42e7dSPeter Dunlap /* 1159a6d42e7dSPeter Dunlap * idm_buf_rx_from_ini_done releases idt->idt_mutex 1160a6d42e7dSPeter Dunlap */ 1161a6d42e7dSPeter Dunlap idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_ABORTED); 1162a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1163a6d42e7dSPeter Dunlap } 1164a6d42e7dSPeter Dunlap } 1165a6d42e7dSPeter Dunlap 1166a6d42e7dSPeter Dunlap for (idb = list_head(&idt->idt_inbufv); idb != NULL; 1167a6d42e7dSPeter Dunlap idb = list_next(&idt->idt_inbufv, idb)) { 1168a6d42e7dSPeter Dunlap /* 1169a6d42e7dSPeter Dunlap * We want to remove these items from the tx_list as well, 1170a6d42e7dSPeter Dunlap * but knowing it's in the idt_inbufv list is not a guarantee 1171a6d42e7dSPeter Dunlap * that it's in the tx_list. If it's on the tx list then 1172a6d42e7dSPeter Dunlap * let idm_sotx_thread() clean it up. 1173a6d42e7dSPeter Dunlap */ 1174a6d42e7dSPeter Dunlap if (idb->idb_in_transport && !idb->idb_tx_thread) { 1175a6d42e7dSPeter Dunlap /* 1176a6d42e7dSPeter Dunlap * idm_buf_tx_to_ini_done releases idt->idt_mutex 1177a6d42e7dSPeter Dunlap */ 1178a6d42e7dSPeter Dunlap idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED); 1179a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1180a6d42e7dSPeter Dunlap } 1181a6d42e7dSPeter Dunlap } 1182a6d42e7dSPeter Dunlap 1183a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 1184a6d42e7dSPeter Dunlap 1185a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 1186a6d42e7dSPeter Dunlap } 1187a6d42e7dSPeter Dunlap 1188a6d42e7dSPeter Dunlap /* 1189a6d42e7dSPeter Dunlap * idm_so_negotiate_key_values() validates the key values for this connection 1190a6d42e7dSPeter Dunlap */ 1191a6d42e7dSPeter Dunlap /* ARGSUSED */ 1192a6d42e7dSPeter Dunlap static kv_status_t 1193a6d42e7dSPeter Dunlap idm_so_negotiate_key_values(idm_conn_t *it, nvlist_t *request_nvl, 1194a6d42e7dSPeter Dunlap nvlist_t *response_nvl, nvlist_t *negotiated_nvl) 1195a6d42e7dSPeter Dunlap { 1196a6d42e7dSPeter Dunlap /* All parameters are negotiated at the iscsit level */ 1197a6d42e7dSPeter Dunlap return (KV_HANDLED); 1198a6d42e7dSPeter Dunlap } 1199a6d42e7dSPeter Dunlap 1200a6d42e7dSPeter Dunlap /* 1201a6d42e7dSPeter Dunlap * idm_so_notice_key_values() activates the negotiated key values for 1202a6d42e7dSPeter Dunlap * this connection. 1203a6d42e7dSPeter Dunlap */ 120430e7468fSPeter Dunlap static void 1205a6d42e7dSPeter Dunlap idm_so_notice_key_values(idm_conn_t *it, nvlist_t *negotiated_nvl) 1206a6d42e7dSPeter Dunlap { 1207a6d42e7dSPeter Dunlap char *nvp_name; 1208a6d42e7dSPeter Dunlap nvpair_t *nvp; 1209a6d42e7dSPeter Dunlap nvpair_t *next_nvp; 1210a6d42e7dSPeter Dunlap int nvrc; 1211a6d42e7dSPeter Dunlap idm_status_t idm_status; 1212a6d42e7dSPeter Dunlap const idm_kv_xlate_t *ikvx; 1213a6d42e7dSPeter Dunlap 1214a6d42e7dSPeter Dunlap for (nvp = nvlist_next_nvpair(negotiated_nvl, NULL); 1215a6d42e7dSPeter Dunlap nvp != NULL; nvp = next_nvp) { 1216a6d42e7dSPeter Dunlap next_nvp = nvlist_next_nvpair(negotiated_nvl, nvp); 1217a6d42e7dSPeter Dunlap nvp_name = nvpair_name(nvp); 1218a6d42e7dSPeter Dunlap 1219a6d42e7dSPeter Dunlap ikvx = idm_lookup_kv_xlate(nvp_name, strlen(nvp_name)); 1220a6d42e7dSPeter Dunlap switch (ikvx->ik_key_id) { 1221a6d42e7dSPeter Dunlap case KI_HEADER_DIGEST: 1222a6d42e7dSPeter Dunlap case KI_DATA_DIGEST: 1223a6d42e7dSPeter Dunlap idm_status = idm_so_handle_digest(it, nvp, ikvx); 1224a6d42e7dSPeter Dunlap ASSERT(idm_status == 0); 1225a6d42e7dSPeter Dunlap 1226a6d42e7dSPeter Dunlap /* Remove processed item from negotiated_nvl list */ 1227a6d42e7dSPeter Dunlap nvrc = nvlist_remove_all( 1228a6d42e7dSPeter Dunlap negotiated_nvl, ikvx->ik_key_name); 1229a6d42e7dSPeter Dunlap ASSERT(nvrc == 0); 1230a6d42e7dSPeter Dunlap break; 1231a6d42e7dSPeter Dunlap default: 1232a6d42e7dSPeter Dunlap break; 1233a6d42e7dSPeter Dunlap } 1234a6d42e7dSPeter Dunlap } 1235a6d42e7dSPeter Dunlap } 1236a6d42e7dSPeter Dunlap 1237a6d42e7dSPeter Dunlap 1238a6d42e7dSPeter Dunlap static idm_status_t 1239a6d42e7dSPeter Dunlap idm_so_handle_digest(idm_conn_t *it, nvpair_t *digest_choice, 1240a6d42e7dSPeter Dunlap const idm_kv_xlate_t *ikvx) 1241a6d42e7dSPeter Dunlap { 1242a6d42e7dSPeter Dunlap int nvrc; 1243a6d42e7dSPeter Dunlap char *digest_choice_string; 1244a6d42e7dSPeter Dunlap 1245a6d42e7dSPeter Dunlap nvrc = nvpair_value_string(digest_choice, 1246a6d42e7dSPeter Dunlap &digest_choice_string); 1247a6d42e7dSPeter Dunlap ASSERT(nvrc == 0); 1248a6d42e7dSPeter Dunlap if (strcasecmp(digest_choice_string, "crc32c") == 0) { 1249a6d42e7dSPeter Dunlap switch (ikvx->ik_key_id) { 1250a6d42e7dSPeter Dunlap case KI_HEADER_DIGEST: 1251a6d42e7dSPeter Dunlap it->ic_conn_flags |= IDM_CONN_HEADER_DIGEST; 1252a6d42e7dSPeter Dunlap break; 1253a6d42e7dSPeter Dunlap case KI_DATA_DIGEST: 1254a6d42e7dSPeter Dunlap it->ic_conn_flags |= IDM_CONN_DATA_DIGEST; 1255a6d42e7dSPeter Dunlap break; 1256a6d42e7dSPeter Dunlap default: 1257a6d42e7dSPeter Dunlap ASSERT(0); 1258a6d42e7dSPeter Dunlap break; 1259a6d42e7dSPeter Dunlap } 1260a6d42e7dSPeter Dunlap } else if (strcasecmp(digest_choice_string, "none") == 0) { 1261a6d42e7dSPeter Dunlap switch (ikvx->ik_key_id) { 1262a6d42e7dSPeter Dunlap case KI_HEADER_DIGEST: 1263a6d42e7dSPeter Dunlap it->ic_conn_flags &= ~IDM_CONN_HEADER_DIGEST; 1264a6d42e7dSPeter Dunlap break; 1265a6d42e7dSPeter Dunlap case KI_DATA_DIGEST: 1266a6d42e7dSPeter Dunlap it->ic_conn_flags &= ~IDM_CONN_DATA_DIGEST; 1267a6d42e7dSPeter Dunlap break; 1268a6d42e7dSPeter Dunlap default: 1269a6d42e7dSPeter Dunlap ASSERT(0); 1270a6d42e7dSPeter Dunlap break; 1271a6d42e7dSPeter Dunlap } 1272a6d42e7dSPeter Dunlap } else { 1273a6d42e7dSPeter Dunlap ASSERT(0); 1274a6d42e7dSPeter Dunlap } 1275a6d42e7dSPeter Dunlap 1276a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 1277a6d42e7dSPeter Dunlap } 1278a6d42e7dSPeter Dunlap 1279a6d42e7dSPeter Dunlap 1280a6d42e7dSPeter Dunlap /* 1281a6d42e7dSPeter Dunlap * idm_so_conn_is_capable() verifies that the passed connection is provided 1282a6d42e7dSPeter Dunlap * for by the sockets interface. 1283a6d42e7dSPeter Dunlap */ 1284a6d42e7dSPeter Dunlap /* ARGSUSED */ 1285a6d42e7dSPeter Dunlap static boolean_t 1286a6d42e7dSPeter Dunlap idm_so_conn_is_capable(idm_conn_req_t *ic, idm_transport_caps_t *caps) 1287a6d42e7dSPeter Dunlap { 1288a6d42e7dSPeter Dunlap return (B_TRUE); 1289a6d42e7dSPeter Dunlap } 1290a6d42e7dSPeter Dunlap 1291a6d42e7dSPeter Dunlap /* 1292a6d42e7dSPeter Dunlap * idm_so_rx_datain() validates the Data Sequence number of the PDU. The 1293a6d42e7dSPeter Dunlap * idm_sorecv_scsidata() function invoked earlier actually reads the data 1294a6d42e7dSPeter Dunlap * off the socket into the appropriate buffers. 1295a6d42e7dSPeter Dunlap */ 1296a6d42e7dSPeter Dunlap static void 1297a6d42e7dSPeter Dunlap idm_so_rx_datain(idm_conn_t *ic, idm_pdu_t *pdu) 1298a6d42e7dSPeter Dunlap { 1299a6d42e7dSPeter Dunlap iscsi_data_hdr_t *bhs; 1300a6d42e7dSPeter Dunlap idm_task_t *idt; 1301a6d42e7dSPeter Dunlap idm_buf_t *idb; 1302a6d42e7dSPeter Dunlap uint32_t datasn; 1303a6d42e7dSPeter Dunlap size_t offset; 1304a6d42e7dSPeter Dunlap iscsi_hdr_t *ihp = (iscsi_hdr_t *)pdu->isp_hdr; 1305a6d42e7dSPeter Dunlap iscsi_data_rsp_hdr_t *idrhp = (iscsi_data_rsp_hdr_t *)ihp; 1306a6d42e7dSPeter Dunlap 1307a6d42e7dSPeter Dunlap ASSERT(ic != NULL); 1308a6d42e7dSPeter Dunlap ASSERT(pdu != NULL); 1309a6d42e7dSPeter Dunlap 1310a6d42e7dSPeter Dunlap bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 1311a6d42e7dSPeter Dunlap datasn = ntohl(bhs->datasn); 1312a6d42e7dSPeter Dunlap offset = ntohl(bhs->offset); 1313a6d42e7dSPeter Dunlap 1314a6d42e7dSPeter Dunlap ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA_RSP); 1315a6d42e7dSPeter Dunlap 1316a6d42e7dSPeter Dunlap /* 1317a6d42e7dSPeter Dunlap * Look up the task corresponding to the initiator task tag 1318a6d42e7dSPeter Dunlap * to get the buffers affiliated with the task. 1319a6d42e7dSPeter Dunlap */ 1320a6d42e7dSPeter Dunlap idt = idm_task_find(ic, bhs->itt, bhs->ttt); 1321a6d42e7dSPeter Dunlap if (idt == NULL) { 1322a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: failed to find task"); 1323a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1324a6d42e7dSPeter Dunlap return; 1325a6d42e7dSPeter Dunlap } 1326a6d42e7dSPeter Dunlap 1327a6d42e7dSPeter Dunlap idb = pdu->isp_sorx_buf; 1328a6d42e7dSPeter Dunlap if (idb == NULL) { 1329a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 1330a6d42e7dSPeter Dunlap "idm_so_rx_datain: failed to find buffer"); 1331a6d42e7dSPeter Dunlap idm_task_rele(idt); 1332a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1333a6d42e7dSPeter Dunlap return; 1334a6d42e7dSPeter Dunlap } 1335a6d42e7dSPeter Dunlap 1336a6d42e7dSPeter Dunlap /* 1337a6d42e7dSPeter Dunlap * DataSN values should be sequential and should not have any gaps or 1338a6d42e7dSPeter Dunlap * repetitions. Check the DataSN with the one stored in the task. 1339a6d42e7dSPeter Dunlap */ 1340a6d42e7dSPeter Dunlap if (datasn == idt->idt_exp_datasn) { 1341a6d42e7dSPeter Dunlap idt->idt_exp_datasn++; /* keep track of DataSN received */ 1342a6d42e7dSPeter Dunlap } else { 1343a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: datasn out of order"); 1344a6d42e7dSPeter Dunlap idm_task_rele(idt); 1345a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1346a6d42e7dSPeter Dunlap return; 1347a6d42e7dSPeter Dunlap } 1348a6d42e7dSPeter Dunlap 1349a6d42e7dSPeter Dunlap /* 1350a6d42e7dSPeter Dunlap * PDUs in a sequence should be in continuously increasing 1351a6d42e7dSPeter Dunlap * address offset 1352a6d42e7dSPeter Dunlap */ 1353a6d42e7dSPeter Dunlap if (offset != idb->idb_exp_offset) { 1354a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_datain: unexpected offset"); 135530e7468fSPeter Dunlap idm_task_rele(idt); 1356a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1357a6d42e7dSPeter Dunlap return; 1358a6d42e7dSPeter Dunlap } 1359a6d42e7dSPeter Dunlap /* Expected next relative buffer offset */ 1360a6d42e7dSPeter Dunlap idb->idb_exp_offset += n2h24(bhs->dlength); 136130e7468fSPeter Dunlap idt->idt_rx_bytes += n2h24(bhs->dlength); 136230e7468fSPeter Dunlap 136330e7468fSPeter Dunlap idm_task_rele(idt); 1364a6d42e7dSPeter Dunlap 1365a6d42e7dSPeter Dunlap /* 1366a6d42e7dSPeter Dunlap * For now call scsi_rsp which will process the data rsp 1367a6d42e7dSPeter Dunlap * Revisit, need to provide an explicit client entry point for 1368a6d42e7dSPeter Dunlap * phase collapse completions. 1369a6d42e7dSPeter Dunlap */ 1370a6d42e7dSPeter Dunlap if (((ihp->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) && 1371a6d42e7dSPeter Dunlap (idrhp->flags & ISCSI_FLAG_DATA_STATUS)) { 1372a6d42e7dSPeter Dunlap (*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu); 1373a6d42e7dSPeter Dunlap } 1374a6d42e7dSPeter Dunlap 1375a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 1376a6d42e7dSPeter Dunlap } 1377a6d42e7dSPeter Dunlap 1378a6d42e7dSPeter Dunlap /* 1379a6d42e7dSPeter Dunlap * The idm_so_rx_dataout() function is used by the iSCSI target to read 1380a6d42e7dSPeter Dunlap * data from the Data-Out PDU sent by the iSCSI initiator. 1381a6d42e7dSPeter Dunlap * 1382a6d42e7dSPeter Dunlap * This function gets the Initiator Task Tag from the PDU BHS and looks up the 1383a6d42e7dSPeter Dunlap * task to get the buffers associated with the PDU. A PDU might span buffers. 1384a6d42e7dSPeter Dunlap * The data is then read into the respective buffer. 1385a6d42e7dSPeter Dunlap */ 1386a6d42e7dSPeter Dunlap static void 1387a6d42e7dSPeter Dunlap idm_so_rx_dataout(idm_conn_t *ic, idm_pdu_t *pdu) 1388a6d42e7dSPeter Dunlap { 1389a6d42e7dSPeter Dunlap 1390a6d42e7dSPeter Dunlap iscsi_data_hdr_t *bhs; 1391a6d42e7dSPeter Dunlap idm_task_t *idt; 1392a6d42e7dSPeter Dunlap idm_buf_t *idb; 1393a6d42e7dSPeter Dunlap size_t offset; 1394a6d42e7dSPeter Dunlap 1395a6d42e7dSPeter Dunlap ASSERT(ic != NULL); 1396a6d42e7dSPeter Dunlap ASSERT(pdu != NULL); 1397a6d42e7dSPeter Dunlap 1398a6d42e7dSPeter Dunlap bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 1399a6d42e7dSPeter Dunlap offset = ntohl(bhs->offset); 1400a6d42e7dSPeter Dunlap ASSERT(bhs->opcode == ISCSI_OP_SCSI_DATA); 1401a6d42e7dSPeter Dunlap 1402a6d42e7dSPeter Dunlap /* 1403a6d42e7dSPeter Dunlap * Look up the task corresponding to the initiator task tag 1404a6d42e7dSPeter Dunlap * to get the buffers affiliated with the task. 1405a6d42e7dSPeter Dunlap */ 1406a6d42e7dSPeter Dunlap idt = idm_task_find(ic, bhs->itt, bhs->ttt); 1407a6d42e7dSPeter Dunlap if (idt == NULL) { 1408a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 1409a6d42e7dSPeter Dunlap "idm_so_rx_dataout: failed to find task"); 1410a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1411a6d42e7dSPeter Dunlap return; 1412a6d42e7dSPeter Dunlap } 1413a6d42e7dSPeter Dunlap 1414a6d42e7dSPeter Dunlap idb = pdu->isp_sorx_buf; 1415a6d42e7dSPeter Dunlap if (idb == NULL) { 1416a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 1417a6d42e7dSPeter Dunlap "idm_so_rx_dataout: failed to find buffer"); 1418a6d42e7dSPeter Dunlap idm_task_rele(idt); 1419a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1420a6d42e7dSPeter Dunlap return; 1421a6d42e7dSPeter Dunlap } 1422a6d42e7dSPeter Dunlap 1423a6d42e7dSPeter Dunlap /* Keep track of data transferred - check data offsets */ 1424a6d42e7dSPeter Dunlap if (offset != idb->idb_exp_offset) { 1425a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_NOTE, "idm_so_rx_dataout: offset out of seq: " 1426a6d42e7dSPeter Dunlap "%ld, %d", offset, idb->idb_exp_offset); 1427a6d42e7dSPeter Dunlap idm_task_rele(idt); 1428a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1429a6d42e7dSPeter Dunlap return; 1430a6d42e7dSPeter Dunlap } 1431a6d42e7dSPeter Dunlap /* Expected next relative offset */ 1432a6d42e7dSPeter Dunlap idb->idb_exp_offset += ntoh24(bhs->dlength); 143330e7468fSPeter Dunlap idt->idt_rx_bytes += n2h24(bhs->dlength); 1434a6d42e7dSPeter Dunlap 1435a6d42e7dSPeter Dunlap /* 1436a6d42e7dSPeter Dunlap * Call the buffer callback when the transfer is complete 1437a6d42e7dSPeter Dunlap * 1438a6d42e7dSPeter Dunlap * The connection state machine should only abort tasks after 1439a6d42e7dSPeter Dunlap * shutting down the connection so we are assured that there 1440a6d42e7dSPeter Dunlap * won't be a simultaneous attempt to abort this task at the 1441a6d42e7dSPeter Dunlap * same time as we are processing this PDU (due to a connection 1442a6d42e7dSPeter Dunlap * state change). 1443a6d42e7dSPeter Dunlap */ 1444a6d42e7dSPeter Dunlap if (bhs->flags & ISCSI_FLAG_FINAL) { 1445a6d42e7dSPeter Dunlap /* 1446a6d42e7dSPeter Dunlap * We only want to call idm_buf_rx_from_ini_done once 1447a6d42e7dSPeter Dunlap * per transfer. It's possible that this task has 1448a6d42e7dSPeter Dunlap * already been aborted in which case 1449a6d42e7dSPeter Dunlap * idm_so_free_task_rsrc will call idm_buf_rx_from_ini_done 1450a6d42e7dSPeter Dunlap * for each buffer with idb_in_transport==B_TRUE. To 1451a6d42e7dSPeter Dunlap * close this window and ensure that this doesn't happen, 1452a6d42e7dSPeter Dunlap * we'll clear idb->idb_in_transport now while holding 1453a6d42e7dSPeter Dunlap * the task mutex. This is only really an issue for 1454a6d42e7dSPeter Dunlap * SCSI task abort -- if tasks were being aborted because 1455a6d42e7dSPeter Dunlap * of a connection state change the state machine would 1456a6d42e7dSPeter Dunlap * have already stopped the receive thread. 1457a6d42e7dSPeter Dunlap */ 1458a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1459a6d42e7dSPeter Dunlap 1460a6d42e7dSPeter Dunlap /* 1461a6d42e7dSPeter Dunlap * Release the task hold here (obtained in idm_task_find) 1462a6d42e7dSPeter Dunlap * because the task may complete synchronously during 1463a6d42e7dSPeter Dunlap * idm_buf_rx_from_ini_done. Since we still have an active 1464a6d42e7dSPeter Dunlap * buffer we know there is at least one additional hold on idt. 1465a6d42e7dSPeter Dunlap */ 1466a6d42e7dSPeter Dunlap idm_task_rele(idt); 1467a6d42e7dSPeter Dunlap 1468a6d42e7dSPeter Dunlap /* 1469a6d42e7dSPeter Dunlap * idm_buf_rx_from_ini_done releases idt->idt_mutex 1470a6d42e7dSPeter Dunlap */ 1471a6d42e7dSPeter Dunlap idm_buf_rx_from_ini_done(idt, idb, IDM_STATUS_SUCCESS); 1472a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 1473a6d42e7dSPeter Dunlap return; 1474a6d42e7dSPeter Dunlap } 1475a6d42e7dSPeter Dunlap 1476a6d42e7dSPeter Dunlap idm_task_rele(idt); 1477a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 1478a6d42e7dSPeter Dunlap } 1479a6d42e7dSPeter Dunlap 1480a6d42e7dSPeter Dunlap /* 1481a6d42e7dSPeter Dunlap * The idm_so_rx_rtt() function is used by the iSCSI initiator to handle 1482a6d42e7dSPeter Dunlap * the R2T PDU sent by the iSCSI target indicating that it is ready to 1483a6d42e7dSPeter Dunlap * accept data. This gets the Initiator Task Tag (itt) from the PDU BHS 1484a6d42e7dSPeter Dunlap * and looks up the task in the task tree using the itt to get the output 1485a6d42e7dSPeter Dunlap * buffers associated the task. The R2T PDU contains the offset of the 1486a6d42e7dSPeter Dunlap * requested data and the data length. This function then constructs a 1487a6d42e7dSPeter Dunlap * sequence of iSCSI PDUs and outputs the requested data. Each Data-Out 1488a6d42e7dSPeter Dunlap * PDU is associated with the R2T by the Target Transfer Tag (ttt). 1489a6d42e7dSPeter Dunlap */ 149030e7468fSPeter Dunlap 1491a6d42e7dSPeter Dunlap static void 1492a6d42e7dSPeter Dunlap idm_so_rx_rtt(idm_conn_t *ic, idm_pdu_t *pdu) 1493a6d42e7dSPeter Dunlap { 1494a6d42e7dSPeter Dunlap idm_task_t *idt; 1495a6d42e7dSPeter Dunlap idm_buf_t *idb; 1496a6d42e7dSPeter Dunlap iscsi_rtt_hdr_t *rtt_hdr; 1497a6d42e7dSPeter Dunlap uint32_t data_offset; 149830e7468fSPeter Dunlap uint32_t data_length; 1499a6d42e7dSPeter Dunlap 1500a6d42e7dSPeter Dunlap ASSERT(ic != NULL); 1501a6d42e7dSPeter Dunlap ASSERT(pdu != NULL); 1502a6d42e7dSPeter Dunlap 1503a6d42e7dSPeter Dunlap rtt_hdr = (iscsi_rtt_hdr_t *)pdu->isp_hdr; 1504a6d42e7dSPeter Dunlap data_offset = ntohl(rtt_hdr->data_offset); 150530e7468fSPeter Dunlap data_length = ntohl(rtt_hdr->data_length); 1506a6d42e7dSPeter Dunlap idt = idm_task_find(ic, rtt_hdr->itt, rtt_hdr->ttt); 1507a6d42e7dSPeter Dunlap 1508a6d42e7dSPeter Dunlap if (idt == NULL) { 1509a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find task"); 1510a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1511a6d42e7dSPeter Dunlap return; 1512a6d42e7dSPeter Dunlap } 1513a6d42e7dSPeter Dunlap 1514a6d42e7dSPeter Dunlap /* Find the buffer bound to the task by the iSCSI initiator */ 1515a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1516a6d42e7dSPeter Dunlap idb = idm_buf_find(&idt->idt_outbufv, data_offset); 1517a6d42e7dSPeter Dunlap if (idb == NULL) { 1518a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 1519a6d42e7dSPeter Dunlap idm_task_rele(idt); 1520a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: could not find buffer"); 1521a6d42e7dSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 1522a6d42e7dSPeter Dunlap return; 1523a6d42e7dSPeter Dunlap } 1524a6d42e7dSPeter Dunlap 152530e7468fSPeter Dunlap /* return buffer contains this data */ 152630e7468fSPeter Dunlap if (data_offset + data_length > idb->idb_buflen) { 152730e7468fSPeter Dunlap /* Overflow */ 152830e7468fSPeter Dunlap mutex_exit(&idt->idt_mutex); 152930e7468fSPeter Dunlap idm_task_rele(idt); 153030e7468fSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_so_rx_rtt: read from outside " 153130e7468fSPeter Dunlap "buffer"); 153230e7468fSPeter Dunlap idm_pdu_rx_protocol_error(ic, pdu); 153330e7468fSPeter Dunlap return; 153430e7468fSPeter Dunlap } 153530e7468fSPeter Dunlap 153630e7468fSPeter Dunlap idt->idt_r2t_ttt = rtt_hdr->ttt; 153730e7468fSPeter Dunlap idt->idt_exp_datasn = 0; 153830e7468fSPeter Dunlap 153930e7468fSPeter Dunlap idm_so_send_rtt_data(ic, idt, idb, data_offset, 154030e7468fSPeter Dunlap ntohl(rtt_hdr->data_length)); 1541a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 1542a6d42e7dSPeter Dunlap 1543a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_SUCCESS); 1544a6d42e7dSPeter Dunlap idm_task_rele(idt); 1545a6d42e7dSPeter Dunlap 1546a6d42e7dSPeter Dunlap } 1547a6d42e7dSPeter Dunlap 1548a6d42e7dSPeter Dunlap idm_status_t 1549a6d42e7dSPeter Dunlap idm_sorecvdata(idm_conn_t *ic, idm_pdu_t *pdu) 1550a6d42e7dSPeter Dunlap { 1551a6d42e7dSPeter Dunlap uint8_t pad[ISCSI_PAD_WORD_LEN]; 1552a6d42e7dSPeter Dunlap int pad_len; 1553a6d42e7dSPeter Dunlap uint32_t data_digest_crc; 1554a6d42e7dSPeter Dunlap uint32_t crc_calculated; 1555a6d42e7dSPeter Dunlap int total_len; 1556a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 1557a6d42e7dSPeter Dunlap 1558a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 1559a6d42e7dSPeter Dunlap 1560a6d42e7dSPeter Dunlap pad_len = ((ISCSI_PAD_WORD_LEN - 1561a6d42e7dSPeter Dunlap (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) & 1562a6d42e7dSPeter Dunlap (ISCSI_PAD_WORD_LEN - 1)); 1563a6d42e7dSPeter Dunlap 1564a6d42e7dSPeter Dunlap ASSERT(pdu->isp_iovlen < (PDU_MAX_IOVLEN - 2)); /* pad + data digest */ 1565a6d42e7dSPeter Dunlap 1566a6d42e7dSPeter Dunlap total_len = pdu->isp_datalen; 1567a6d42e7dSPeter Dunlap 1568a6d42e7dSPeter Dunlap if (pad_len) { 1569a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_base = (char *)&pad; 1570a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_len = pad_len; 1571a6d42e7dSPeter Dunlap total_len += pad_len; 1572a6d42e7dSPeter Dunlap pdu->isp_iovlen++; 1573a6d42e7dSPeter Dunlap } 1574a6d42e7dSPeter Dunlap 1575a6d42e7dSPeter Dunlap /* setup data digest */ 1576a6d42e7dSPeter Dunlap if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) { 1577a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_base = 1578a6d42e7dSPeter Dunlap (char *)&data_digest_crc; 1579a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_len = 1580a6d42e7dSPeter Dunlap sizeof (data_digest_crc); 1581a6d42e7dSPeter Dunlap total_len += sizeof (data_digest_crc); 1582a6d42e7dSPeter Dunlap pdu->isp_iovlen++; 1583a6d42e7dSPeter Dunlap } 1584a6d42e7dSPeter Dunlap 158530e7468fSPeter Dunlap pdu->isp_data = (uint8_t *)(uintptr_t)pdu->isp_iov[0].iov_base; 158630e7468fSPeter Dunlap 1587a6d42e7dSPeter Dunlap if (idm_iov_sorecv(so_conn->ic_so, &pdu->isp_iov[0], 1588a6d42e7dSPeter Dunlap pdu->isp_iovlen, total_len) != 0) { 1589a6d42e7dSPeter Dunlap return (IDM_STATUS_IO); 1590a6d42e7dSPeter Dunlap } 1591a6d42e7dSPeter Dunlap 1592a6d42e7dSPeter Dunlap if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) != 0) { 1593a6d42e7dSPeter Dunlap crc_calculated = idm_crc32c(pdu->isp_data, 1594a6d42e7dSPeter Dunlap pdu->isp_datalen); 1595a6d42e7dSPeter Dunlap if (pad_len) { 1596a6d42e7dSPeter Dunlap crc_calculated = idm_crc32c_continued((char *)&pad, 1597a6d42e7dSPeter Dunlap pad_len, crc_calculated); 1598a6d42e7dSPeter Dunlap } 1599a6d42e7dSPeter Dunlap if (crc_calculated != data_digest_crc) { 1600a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 1601a6d42e7dSPeter Dunlap "idm_sorecvdata: " 1602a6d42e7dSPeter Dunlap "CRC error: actual 0x%x, calc 0x%x", 1603a6d42e7dSPeter Dunlap data_digest_crc, crc_calculated); 1604a6d42e7dSPeter Dunlap 1605a6d42e7dSPeter Dunlap /* Invalid Data Digest */ 1606a6d42e7dSPeter Dunlap return (IDM_STATUS_DATA_DIGEST); 1607a6d42e7dSPeter Dunlap } 1608a6d42e7dSPeter Dunlap } 1609a6d42e7dSPeter Dunlap 1610a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 1611a6d42e7dSPeter Dunlap } 1612a6d42e7dSPeter Dunlap 1613a6d42e7dSPeter Dunlap /* 1614a6d42e7dSPeter Dunlap * idm_sorecv_scsidata() is used to receive scsi data from the socket. The 1615a6d42e7dSPeter Dunlap * Data-type PDU header must be read into the idm_pdu_t structure prior to 1616a6d42e7dSPeter Dunlap * calling this function. 1617a6d42e7dSPeter Dunlap */ 1618a6d42e7dSPeter Dunlap idm_status_t 1619a6d42e7dSPeter Dunlap idm_sorecv_scsidata(idm_conn_t *ic, idm_pdu_t *pdu) 1620a6d42e7dSPeter Dunlap { 1621a6d42e7dSPeter Dunlap iscsi_data_hdr_t *bhs; 1622a6d42e7dSPeter Dunlap idm_task_t *task; 1623a6d42e7dSPeter Dunlap uint32_t offset; 1624a6d42e7dSPeter Dunlap uint8_t opcode; 1625a6d42e7dSPeter Dunlap uint32_t dlength; 1626a6d42e7dSPeter Dunlap list_t *buflst; 1627a6d42e7dSPeter Dunlap uint32_t xfer_bytes; 1628a6d42e7dSPeter Dunlap idm_status_t status; 1629a6d42e7dSPeter Dunlap 1630a6d42e7dSPeter Dunlap ASSERT(ic != NULL); 1631a6d42e7dSPeter Dunlap ASSERT(pdu != NULL); 1632a6d42e7dSPeter Dunlap 1633a6d42e7dSPeter Dunlap bhs = (iscsi_data_hdr_t *)pdu->isp_hdr; 1634a6d42e7dSPeter Dunlap 1635a6d42e7dSPeter Dunlap offset = ntohl(bhs->offset); 1636a6d42e7dSPeter Dunlap opcode = bhs->opcode; 1637a6d42e7dSPeter Dunlap dlength = n2h24(bhs->dlength); 1638a6d42e7dSPeter Dunlap 1639a6d42e7dSPeter Dunlap ASSERT((opcode == ISCSI_OP_SCSI_DATA_RSP) || 1640a6d42e7dSPeter Dunlap (opcode == ISCSI_OP_SCSI_DATA)); 1641a6d42e7dSPeter Dunlap 1642a6d42e7dSPeter Dunlap /* 1643a6d42e7dSPeter Dunlap * Successful lookup implicitly gets a "hold" on the task. This 1644a6d42e7dSPeter Dunlap * hold must be released before leaving this function. At one 1645a6d42e7dSPeter Dunlap * point we were caching this task context and retaining the hold 1646a6d42e7dSPeter Dunlap * but it turned out to be very difficult to release the hold properly. 1647a6d42e7dSPeter Dunlap * The task can be aborted and the connection shutdown between this 1648a6d42e7dSPeter Dunlap * call and the subsequent expected call to idm_so_rx_datain/ 1649a6d42e7dSPeter Dunlap * idm_so_rx_dataout (in which case those functions are not called). 1650a6d42e7dSPeter Dunlap * Releasing the hold in the PDU callback doesn't work well either 1651a6d42e7dSPeter Dunlap * because the whole task may be completed by then at which point 1652a6d42e7dSPeter Dunlap * it is too late to release the hold -- for better or worse this 1653a6d42e7dSPeter Dunlap * code doesn't wait on the refcnts during normal operation. 1654a6d42e7dSPeter Dunlap * idm_task_find() is very fast and it is not a huge burden if we 1655a6d42e7dSPeter Dunlap * have to do it twice. 1656a6d42e7dSPeter Dunlap */ 1657a6d42e7dSPeter Dunlap task = idm_task_find(ic, bhs->itt, bhs->ttt); 1658a6d42e7dSPeter Dunlap if (task == NULL) { 1659a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 1660a6d42e7dSPeter Dunlap "idm_sorecv_scsidata: could not find task"); 1661a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 1662a6d42e7dSPeter Dunlap } 1663a6d42e7dSPeter Dunlap 1664a6d42e7dSPeter Dunlap mutex_enter(&task->idt_mutex); 1665a6d42e7dSPeter Dunlap buflst = (opcode == ISCSI_OP_SCSI_DATA_RSP) ? 1666a6d42e7dSPeter Dunlap &task->idt_inbufv : &task->idt_outbufv; 1667a6d42e7dSPeter Dunlap pdu->isp_sorx_buf = idm_buf_find(buflst, offset); 1668a6d42e7dSPeter Dunlap mutex_exit(&task->idt_mutex); 1669a6d42e7dSPeter Dunlap 1670a6d42e7dSPeter Dunlap if (pdu->isp_sorx_buf == NULL) { 1671a6d42e7dSPeter Dunlap idm_task_rele(task); 1672a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_sorecv_scsidata: could not find " 1673a6d42e7dSPeter Dunlap "buffer for offset %x opcode=%x", 1674a6d42e7dSPeter Dunlap offset, opcode); 1675a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 1676a6d42e7dSPeter Dunlap } 1677a6d42e7dSPeter Dunlap 1678a6d42e7dSPeter Dunlap xfer_bytes = idm_fill_iov(pdu, pdu->isp_sorx_buf, offset, dlength); 1679a6d42e7dSPeter Dunlap ASSERT(xfer_bytes != 0); 1680a6d42e7dSPeter Dunlap if (xfer_bytes != dlength) { 1681a6d42e7dSPeter Dunlap idm_task_rele(task); 1682a6d42e7dSPeter Dunlap /* 1683a6d42e7dSPeter Dunlap * Buffer overflow, connection error. The PDU data is still 1684a6d42e7dSPeter Dunlap * sitting in the socket so we can't use the connection 1685a6d42e7dSPeter Dunlap * again until that data is drained. 1686a6d42e7dSPeter Dunlap */ 1687a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 1688a6d42e7dSPeter Dunlap } 1689a6d42e7dSPeter Dunlap 1690a6d42e7dSPeter Dunlap status = idm_sorecvdata(ic, pdu); 1691a6d42e7dSPeter Dunlap 1692a6d42e7dSPeter Dunlap idm_task_rele(task); 1693a6d42e7dSPeter Dunlap 1694a6d42e7dSPeter Dunlap return (status); 1695a6d42e7dSPeter Dunlap } 1696a6d42e7dSPeter Dunlap 1697a6d42e7dSPeter Dunlap static uint32_t 1698a6d42e7dSPeter Dunlap idm_fill_iov(idm_pdu_t *pdu, idm_buf_t *idb, uint32_t ro, uint32_t dlength) 1699a6d42e7dSPeter Dunlap { 1700a6d42e7dSPeter Dunlap uint32_t buf_ro = ro - idb->idb_bufoffset; 1701a6d42e7dSPeter Dunlap uint32_t xfer_len = min(dlength, idb->idb_buflen - buf_ro); 1702a6d42e7dSPeter Dunlap 1703a6d42e7dSPeter Dunlap ASSERT(ro >= idb->idb_bufoffset); 1704a6d42e7dSPeter Dunlap 1705a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_base = 1706a6d42e7dSPeter Dunlap (caddr_t)idb->idb_buf + buf_ro; 1707a6d42e7dSPeter Dunlap pdu->isp_iov[pdu->isp_iovlen].iov_len = xfer_len; 1708a6d42e7dSPeter Dunlap pdu->isp_iovlen++; 1709a6d42e7dSPeter Dunlap 1710a6d42e7dSPeter Dunlap return (xfer_len); 1711a6d42e7dSPeter Dunlap } 1712a6d42e7dSPeter Dunlap 1713a6d42e7dSPeter Dunlap int 1714a6d42e7dSPeter Dunlap idm_sorecv_nonscsidata(idm_conn_t *ic, idm_pdu_t *pdu) 1715a6d42e7dSPeter Dunlap { 1716a6d42e7dSPeter Dunlap pdu->isp_data = kmem_alloc(pdu->isp_datalen, KM_SLEEP); 1717a6d42e7dSPeter Dunlap ASSERT(pdu->isp_data != NULL); 1718a6d42e7dSPeter Dunlap 1719a6d42e7dSPeter Dunlap pdu->isp_databuflen = pdu->isp_datalen; 1720a6d42e7dSPeter Dunlap pdu->isp_iov[0].iov_base = (caddr_t)pdu->isp_data; 1721a6d42e7dSPeter Dunlap pdu->isp_iov[0].iov_len = pdu->isp_datalen; 1722a6d42e7dSPeter Dunlap pdu->isp_iovlen = 1; 1723a6d42e7dSPeter Dunlap /* 1724a6d42e7dSPeter Dunlap * Since we are associating a new data buffer with this received 1725a6d42e7dSPeter Dunlap * PDU we need to set a specific callback to free the data 1726a6d42e7dSPeter Dunlap * after the PDU is processed. 1727a6d42e7dSPeter Dunlap */ 1728a6d42e7dSPeter Dunlap pdu->isp_flags |= IDM_PDU_ADDL_DATA; 1729a6d42e7dSPeter Dunlap pdu->isp_callback = idm_sorx_addl_pdu_cb; 1730a6d42e7dSPeter Dunlap 1731a6d42e7dSPeter Dunlap return (idm_sorecvdata(ic, pdu)); 1732a6d42e7dSPeter Dunlap } 1733a6d42e7dSPeter Dunlap 1734a6d42e7dSPeter Dunlap void 1735a6d42e7dSPeter Dunlap idm_sorx_thread(void *arg) 1736a6d42e7dSPeter Dunlap { 1737a6d42e7dSPeter Dunlap boolean_t conn_failure = B_FALSE; 1738a6d42e7dSPeter Dunlap idm_conn_t *ic = (idm_conn_t *)arg; 1739a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 1740a6d42e7dSPeter Dunlap idm_pdu_t *pdu; 1741a6d42e7dSPeter Dunlap idm_status_t rc; 1742a6d42e7dSPeter Dunlap 1743a6d42e7dSPeter Dunlap idm_conn_hold(ic); 1744a6d42e7dSPeter Dunlap 1745a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 1746a6d42e7dSPeter Dunlap 1747a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 1748a6d42e7dSPeter Dunlap so_conn->ic_rx_thread_running = B_TRUE; 1749a6d42e7dSPeter Dunlap so_conn->ic_rx_thread_did = so_conn->ic_rx_thread->t_did; 1750a6d42e7dSPeter Dunlap cv_signal(&ic->ic_cv); 1751a6d42e7dSPeter Dunlap 1752a6d42e7dSPeter Dunlap while (so_conn->ic_rx_thread_running) { 1753a6d42e7dSPeter Dunlap mutex_exit(&ic->ic_mutex); 1754a6d42e7dSPeter Dunlap 1755a6d42e7dSPeter Dunlap /* 1756a6d42e7dSPeter Dunlap * Get PDU with default header size (large enough for 1757a6d42e7dSPeter Dunlap * BHS plus any anticipated AHS). PDU from 1758a6d42e7dSPeter Dunlap * the cache will have all values set correctly 1759a6d42e7dSPeter Dunlap * for sockets RX including callback. 1760a6d42e7dSPeter Dunlap */ 1761a6d42e7dSPeter Dunlap pdu = kmem_cache_alloc(idm.idm_sorx_pdu_cache, KM_SLEEP); 1762a6d42e7dSPeter Dunlap pdu->isp_ic = ic; 1763a6d42e7dSPeter Dunlap pdu->isp_flags = 0; 1764a6d42e7dSPeter Dunlap pdu->isp_transport_hdrlen = 0; 1765a6d42e7dSPeter Dunlap 1766a6d42e7dSPeter Dunlap if ((rc = idm_sorecvhdr(ic, pdu)) != 0) { 1767a6d42e7dSPeter Dunlap /* 1768a6d42e7dSPeter Dunlap * Call idm_pdu_complete so that we call the callback 1769a6d42e7dSPeter Dunlap * and ensure any memory allocated in idm_sorecvhdr 1770a6d42e7dSPeter Dunlap * gets freed up. 1771a6d42e7dSPeter Dunlap */ 1772a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_FAIL); 1773a6d42e7dSPeter Dunlap 1774a6d42e7dSPeter Dunlap /* 1775a6d42e7dSPeter Dunlap * If ic_rx_thread_running is still set then 1776a6d42e7dSPeter Dunlap * this is some kind of connection problem 1777a6d42e7dSPeter Dunlap * on the socket. In this case we want to 1778a6d42e7dSPeter Dunlap * generate an event. Otherwise some other 1779a6d42e7dSPeter Dunlap * thread closed the socket due to another 1780a6d42e7dSPeter Dunlap * issue in which case we don't need to 1781a6d42e7dSPeter Dunlap * generate an event. 1782a6d42e7dSPeter Dunlap */ 1783a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 1784a6d42e7dSPeter Dunlap if (so_conn->ic_rx_thread_running) { 1785a6d42e7dSPeter Dunlap conn_failure = B_TRUE; 1786a6d42e7dSPeter Dunlap so_conn->ic_rx_thread_running = B_FALSE; 1787a6d42e7dSPeter Dunlap } 1788a6d42e7dSPeter Dunlap 1789a6d42e7dSPeter Dunlap continue; 1790a6d42e7dSPeter Dunlap } 1791a6d42e7dSPeter Dunlap 1792a6d42e7dSPeter Dunlap /* 1793a6d42e7dSPeter Dunlap * Header has been read and validated. Now we need 1794a6d42e7dSPeter Dunlap * to read the PDU data payload (if present). SCSI data 1795a6d42e7dSPeter Dunlap * need to be transferred from the socket directly into 1796a6d42e7dSPeter Dunlap * the associated transfer buffer for the SCSI task. 1797a6d42e7dSPeter Dunlap */ 1798a6d42e7dSPeter Dunlap if (pdu->isp_datalen != 0) { 1799a6d42e7dSPeter Dunlap if ((IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA) || 1800a6d42e7dSPeter Dunlap (IDM_PDU_OPCODE(pdu) == ISCSI_OP_SCSI_DATA_RSP)) { 1801a6d42e7dSPeter Dunlap rc = idm_sorecv_scsidata(ic, pdu); 1802a6d42e7dSPeter Dunlap /* 1803a6d42e7dSPeter Dunlap * All SCSI errors are fatal to the 1804a6d42e7dSPeter Dunlap * connection right now since we have no 1805a6d42e7dSPeter Dunlap * place to put the data. What we need 1806a6d42e7dSPeter Dunlap * is some kind of sink to dispose of unwanted 1807a6d42e7dSPeter Dunlap * SCSI data. For example an invalid task tag 1808a6d42e7dSPeter Dunlap * should not kill the connection (although 1809a6d42e7dSPeter Dunlap * we may want to drop the connection). 1810a6d42e7dSPeter Dunlap */ 1811a6d42e7dSPeter Dunlap } else { 1812a6d42e7dSPeter Dunlap /* 1813a6d42e7dSPeter Dunlap * Not data PDUs so allocate a buffer for the 1814a6d42e7dSPeter Dunlap * data segment and read the remaining data. 1815a6d42e7dSPeter Dunlap */ 1816a6d42e7dSPeter Dunlap rc = idm_sorecv_nonscsidata(ic, pdu); 1817a6d42e7dSPeter Dunlap } 1818a6d42e7dSPeter Dunlap if (rc != 0) { 1819a6d42e7dSPeter Dunlap /* 1820a6d42e7dSPeter Dunlap * Call idm_pdu_complete so that we call the 1821a6d42e7dSPeter Dunlap * callback and ensure any memory allocated 1822a6d42e7dSPeter Dunlap * in idm_sorecvhdr gets freed up. 1823a6d42e7dSPeter Dunlap */ 1824a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_FAIL); 1825a6d42e7dSPeter Dunlap 1826a6d42e7dSPeter Dunlap /* 1827a6d42e7dSPeter Dunlap * If ic_rx_thread_running is still set then 1828a6d42e7dSPeter Dunlap * this is some kind of connection problem 1829a6d42e7dSPeter Dunlap * on the socket. In this case we want to 1830a6d42e7dSPeter Dunlap * generate an event. Otherwise some other 1831a6d42e7dSPeter Dunlap * thread closed the socket due to another 1832a6d42e7dSPeter Dunlap * issue in which case we don't need to 1833a6d42e7dSPeter Dunlap * generate an event. 1834a6d42e7dSPeter Dunlap */ 1835a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 1836a6d42e7dSPeter Dunlap if (so_conn->ic_rx_thread_running) { 1837a6d42e7dSPeter Dunlap conn_failure = B_TRUE; 1838a6d42e7dSPeter Dunlap so_conn->ic_rx_thread_running = B_FALSE; 1839a6d42e7dSPeter Dunlap } 1840a6d42e7dSPeter Dunlap continue; 1841a6d42e7dSPeter Dunlap } 1842a6d42e7dSPeter Dunlap } 1843a6d42e7dSPeter Dunlap 1844a6d42e7dSPeter Dunlap /* 1845a6d42e7dSPeter Dunlap * Process RX PDU 1846a6d42e7dSPeter Dunlap */ 1847a6d42e7dSPeter Dunlap idm_pdu_rx(ic, pdu); 1848a6d42e7dSPeter Dunlap 1849a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 1850a6d42e7dSPeter Dunlap } 1851a6d42e7dSPeter Dunlap 1852a6d42e7dSPeter Dunlap mutex_exit(&ic->ic_mutex); 1853a6d42e7dSPeter Dunlap 1854a6d42e7dSPeter Dunlap /* 1855a6d42e7dSPeter Dunlap * If we dropped out of the RX processing loop because of 1856a6d42e7dSPeter Dunlap * a socket problem or other connection failure (including 1857a6d42e7dSPeter Dunlap * digest errors) then we need to generate a state machine 1858a6d42e7dSPeter Dunlap * event to shut the connection down. 1859a6d42e7dSPeter Dunlap * If the state machine is already in, for example, INIT_ERROR, this 1860a6d42e7dSPeter Dunlap * event will get dropped, and the TX thread will never be notified 1861a6d42e7dSPeter Dunlap * to shut down. To be safe, we'll just notify it here. 1862a6d42e7dSPeter Dunlap */ 1863a6d42e7dSPeter Dunlap if (conn_failure) { 1864a6d42e7dSPeter Dunlap if (so_conn->ic_tx_thread_running) { 1865a6d42e7dSPeter Dunlap so_conn->ic_tx_thread_running = B_FALSE; 1866a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 1867a6d42e7dSPeter Dunlap cv_signal(&so_conn->ic_tx_cv); 1868a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 1869a6d42e7dSPeter Dunlap } 1870a6d42e7dSPeter Dunlap 1871a6d42e7dSPeter Dunlap idm_conn_event(ic, CE_TRANSPORT_FAIL, rc); 1872a6d42e7dSPeter Dunlap } 1873a6d42e7dSPeter Dunlap 1874a6d42e7dSPeter Dunlap idm_conn_rele(ic); 1875a6d42e7dSPeter Dunlap 1876a6d42e7dSPeter Dunlap thread_exit(); 1877a6d42e7dSPeter Dunlap } 1878a6d42e7dSPeter Dunlap 1879a6d42e7dSPeter Dunlap /* 1880a6d42e7dSPeter Dunlap * idm_so_tx 1881a6d42e7dSPeter Dunlap * 1882a6d42e7dSPeter Dunlap * This is the implementation of idm_transport_ops_t's it_tx_pdu entry 1883a6d42e7dSPeter Dunlap * point. By definition, it is supposed to be fast. So, simply queue 1884a6d42e7dSPeter Dunlap * the entry and return. The real work is done by idm_i_so_tx() via 1885a6d42e7dSPeter Dunlap * idm_sotx_thread(). 1886a6d42e7dSPeter Dunlap */ 1887a6d42e7dSPeter Dunlap 1888a6d42e7dSPeter Dunlap static void 1889a6d42e7dSPeter Dunlap idm_so_tx(idm_conn_t *ic, idm_pdu_t *pdu) 1890a6d42e7dSPeter Dunlap { 1891a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn = ic->ic_transport_private; 1892a6d42e7dSPeter Dunlap 1893a6d42e7dSPeter Dunlap ASSERT(pdu->isp_ic == ic); 1894a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 1895a6d42e7dSPeter Dunlap 1896a6d42e7dSPeter Dunlap if (!so_conn->ic_tx_thread_running) { 1897a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 1898a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, IDM_STATUS_ABORTED); 1899a6d42e7dSPeter Dunlap return; 1900a6d42e7dSPeter Dunlap } 1901a6d42e7dSPeter Dunlap 1902a6d42e7dSPeter Dunlap list_insert_tail(&so_conn->ic_tx_list, (void *)pdu); 1903a6d42e7dSPeter Dunlap cv_signal(&so_conn->ic_tx_cv); 1904a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 1905a6d42e7dSPeter Dunlap } 1906a6d42e7dSPeter Dunlap 1907a6d42e7dSPeter Dunlap static idm_status_t 1908a6d42e7dSPeter Dunlap idm_i_so_tx(idm_pdu_t *pdu) 1909a6d42e7dSPeter Dunlap { 1910a6d42e7dSPeter Dunlap idm_conn_t *ic = pdu->isp_ic; 1911a6d42e7dSPeter Dunlap idm_status_t status = IDM_STATUS_SUCCESS; 1912a6d42e7dSPeter Dunlap uint8_t pad[ISCSI_PAD_WORD_LEN]; 1913a6d42e7dSPeter Dunlap int pad_len; 1914a6d42e7dSPeter Dunlap uint32_t hdr_digest_crc; 1915a6d42e7dSPeter Dunlap uint32_t data_digest_crc = 0; 1916a6d42e7dSPeter Dunlap int total_len = 0; 1917a6d42e7dSPeter Dunlap int iovlen = 0; 1918a6d42e7dSPeter Dunlap struct iovec iov[6]; 1919a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 1920a6d42e7dSPeter Dunlap 1921a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 1922a6d42e7dSPeter Dunlap 1923a6d42e7dSPeter Dunlap /* Setup BHS */ 1924a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)pdu->isp_hdr; 1925a6d42e7dSPeter Dunlap iov[iovlen].iov_len = pdu->isp_hdrlen; 1926a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 1927a6d42e7dSPeter Dunlap iovlen++; 1928a6d42e7dSPeter Dunlap 1929a6d42e7dSPeter Dunlap /* Setup header digest */ 1930a6d42e7dSPeter Dunlap if (((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) && 1931a6d42e7dSPeter Dunlap (ic->ic_conn_flags & IDM_CONN_HEADER_DIGEST)) { 1932a6d42e7dSPeter Dunlap hdr_digest_crc = idm_crc32c(pdu->isp_hdr, pdu->isp_hdrlen); 1933a6d42e7dSPeter Dunlap 1934a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)&hdr_digest_crc; 1935a6d42e7dSPeter Dunlap iov[iovlen].iov_len = sizeof (hdr_digest_crc); 1936a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 1937a6d42e7dSPeter Dunlap iovlen++; 1938a6d42e7dSPeter Dunlap } 1939a6d42e7dSPeter Dunlap 1940a6d42e7dSPeter Dunlap /* Setup the data */ 1941a6d42e7dSPeter Dunlap if (pdu->isp_datalen) { 1942a6d42e7dSPeter Dunlap idm_task_t *idt; 1943a6d42e7dSPeter Dunlap idm_buf_t *idb; 1944a6d42e7dSPeter Dunlap iscsi_data_hdr_t *ihp; 1945a6d42e7dSPeter Dunlap ihp = (iscsi_data_hdr_t *)pdu->isp_hdr; 1946a6d42e7dSPeter Dunlap /* Write of immediate data */ 1947a6d42e7dSPeter Dunlap if (ic->ic_ffp && 1948a6d42e7dSPeter Dunlap (ihp->opcode == ISCSI_OP_SCSI_CMD || 1949a6d42e7dSPeter Dunlap ihp->opcode == ISCSI_OP_SCSI_DATA)) { 1950a6d42e7dSPeter Dunlap idt = idm_task_find(ic, ihp->itt, ihp->ttt); 1951a6d42e7dSPeter Dunlap if (idt) { 1952a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 1953a6d42e7dSPeter Dunlap idb = idm_buf_find(&idt->idt_outbufv, 0); 1954a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 195530e7468fSPeter Dunlap /* 195630e7468fSPeter Dunlap * If the initiator call to idm_buf_alloc 195730e7468fSPeter Dunlap * failed then we can get to this point 195830e7468fSPeter Dunlap * without a bound buffer. The associated 195930e7468fSPeter Dunlap * connection failure will clean things up 196030e7468fSPeter Dunlap * later. It would be nice to come up with 196130e7468fSPeter Dunlap * a cleaner way to handle this. In 196230e7468fSPeter Dunlap * particular it seems absurd to look up 196330e7468fSPeter Dunlap * the task and the buffer just to update 196430e7468fSPeter Dunlap * this counter. 196530e7468fSPeter Dunlap */ 196630e7468fSPeter Dunlap if (idb) 196730e7468fSPeter Dunlap idb->idb_xfer_len += pdu->isp_datalen; 196830e7468fSPeter Dunlap idm_task_rele(idt); 1969a6d42e7dSPeter Dunlap } 1970a6d42e7dSPeter Dunlap } 1971a6d42e7dSPeter Dunlap 1972a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)pdu->isp_data; 1973a6d42e7dSPeter Dunlap iov[iovlen].iov_len = pdu->isp_datalen; 1974a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 1975a6d42e7dSPeter Dunlap iovlen++; 1976a6d42e7dSPeter Dunlap } 1977a6d42e7dSPeter Dunlap 1978a6d42e7dSPeter Dunlap /* Setup the data pad if necessary */ 1979a6d42e7dSPeter Dunlap pad_len = ((ISCSI_PAD_WORD_LEN - 1980a6d42e7dSPeter Dunlap (pdu->isp_datalen & (ISCSI_PAD_WORD_LEN - 1))) & 1981a6d42e7dSPeter Dunlap (ISCSI_PAD_WORD_LEN - 1)); 1982a6d42e7dSPeter Dunlap 1983a6d42e7dSPeter Dunlap if (pad_len) { 1984a6d42e7dSPeter Dunlap bzero(pad, sizeof (pad)); 1985a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (void *)&pad; 1986a6d42e7dSPeter Dunlap iov[iovlen].iov_len = pad_len; 1987a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 1988a6d42e7dSPeter Dunlap iovlen++; 1989a6d42e7dSPeter Dunlap } 1990a6d42e7dSPeter Dunlap 1991a6d42e7dSPeter Dunlap /* 1992a6d42e7dSPeter Dunlap * Setup the data digest if enabled. Data-digest is not sent 1993a6d42e7dSPeter Dunlap * for login-phase PDUs. 1994a6d42e7dSPeter Dunlap */ 1995a6d42e7dSPeter Dunlap if ((ic->ic_conn_flags & IDM_CONN_DATA_DIGEST) && 1996a6d42e7dSPeter Dunlap ((pdu->isp_flags & IDM_PDU_LOGIN_TX) == 0) && 1997a6d42e7dSPeter Dunlap (pdu->isp_datalen || pad_len)) { 1998a6d42e7dSPeter Dunlap /* 1999a6d42e7dSPeter Dunlap * RFC3720/10.2.3: A zero-length Data Segment also 2000a6d42e7dSPeter Dunlap * implies a zero-length data digest. 2001a6d42e7dSPeter Dunlap */ 2002a6d42e7dSPeter Dunlap if (pdu->isp_datalen) { 2003a6d42e7dSPeter Dunlap data_digest_crc = idm_crc32c(pdu->isp_data, 2004a6d42e7dSPeter Dunlap pdu->isp_datalen); 2005a6d42e7dSPeter Dunlap } 2006a6d42e7dSPeter Dunlap if (pad_len) { 2007a6d42e7dSPeter Dunlap data_digest_crc = idm_crc32c_continued(&pad, 2008a6d42e7dSPeter Dunlap pad_len, data_digest_crc); 2009a6d42e7dSPeter Dunlap } 2010a6d42e7dSPeter Dunlap 2011a6d42e7dSPeter Dunlap iov[iovlen].iov_base = (caddr_t)&data_digest_crc; 2012a6d42e7dSPeter Dunlap iov[iovlen].iov_len = sizeof (data_digest_crc); 2013a6d42e7dSPeter Dunlap total_len += iov[iovlen].iov_len; 2014a6d42e7dSPeter Dunlap iovlen++; 2015a6d42e7dSPeter Dunlap } 2016a6d42e7dSPeter Dunlap 2017a6d42e7dSPeter Dunlap /* Transmit the PDU */ 2018a6d42e7dSPeter Dunlap if (idm_iov_sosend(so_conn->ic_so, &iov[0], iovlen, 2019a6d42e7dSPeter Dunlap total_len) != 0) { 2020a6d42e7dSPeter Dunlap /* Set error status */ 2021a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 2022a6d42e7dSPeter Dunlap "idm_so_tx: failed to transmit the PDU, so: %p ic: %p " 2023a6d42e7dSPeter Dunlap "data: %p", (void *) so_conn->ic_so, (void *) ic, 2024a6d42e7dSPeter Dunlap (void *) pdu->isp_data); 2025a6d42e7dSPeter Dunlap status = IDM_STATUS_IO; 2026a6d42e7dSPeter Dunlap } 2027a6d42e7dSPeter Dunlap 2028a6d42e7dSPeter Dunlap /* 2029a6d42e7dSPeter Dunlap * Success does not mean that the PDU actually reached the 2030a6d42e7dSPeter Dunlap * remote node since it could get dropped along the way. 2031a6d42e7dSPeter Dunlap */ 2032a6d42e7dSPeter Dunlap idm_pdu_complete(pdu, status); 2033a6d42e7dSPeter Dunlap 2034a6d42e7dSPeter Dunlap return (status); 2035a6d42e7dSPeter Dunlap } 2036a6d42e7dSPeter Dunlap 2037a6d42e7dSPeter Dunlap /* 2038a6d42e7dSPeter Dunlap * The idm_so_buf_tx_to_ini() is used by the target iSCSI layer to transmit the 2039a6d42e7dSPeter Dunlap * Data-In PDUs using sockets. Based on the negotiated MaxRecvDataSegmentLength, 2040a6d42e7dSPeter Dunlap * the buffer is segmented into a sequence of Data-In PDUs, ordered by DataSN. 2041a6d42e7dSPeter Dunlap * A target can invoke this function multiple times for a single read command 2042a6d42e7dSPeter Dunlap * (identified by the same ITT) to split the input into several sequences. 2043a6d42e7dSPeter Dunlap * 2044a6d42e7dSPeter Dunlap * DataSN starts with 0 for the first data PDU of an input command and advances 2045a6d42e7dSPeter Dunlap * by 1 for each subsequent data PDU. Each sequence will have its own F bit, 2046a6d42e7dSPeter Dunlap * which is set to 1 for the last data PDU of a sequence. 2047a6d42e7dSPeter Dunlap * 2048a6d42e7dSPeter Dunlap * Scope for Prototype build: 2049a6d42e7dSPeter Dunlap * The data PDUs within a sequence will be sent in order with the buffer offset 2050a6d42e7dSPeter Dunlap * in increasing order. i.e. initiator and target must have negotiated the 2051a6d42e7dSPeter Dunlap * "DataPDUInOrder" to "Yes". The order between sequences is not enforced. 2052a6d42e7dSPeter Dunlap * 2053a6d42e7dSPeter Dunlap * Caller holds idt->idt_mutex 2054a6d42e7dSPeter Dunlap */ 2055a6d42e7dSPeter Dunlap static idm_status_t 2056a6d42e7dSPeter Dunlap idm_so_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb) 2057a6d42e7dSPeter Dunlap { 2058a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn = idb->idb_ic->ic_transport_private; 2059a6d42e7dSPeter Dunlap idm_pdu_t tmppdu; 2060a6d42e7dSPeter Dunlap 2061a6d42e7dSPeter Dunlap ASSERT(mutex_owned(&idt->idt_mutex)); 2062a6d42e7dSPeter Dunlap 2063a6d42e7dSPeter Dunlap /* 2064a6d42e7dSPeter Dunlap * Put the idm_buf_t on the tx queue. It will be transmitted by 2065a6d42e7dSPeter Dunlap * idm_sotx_thread. 2066a6d42e7dSPeter Dunlap */ 2067a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 2068a6d42e7dSPeter Dunlap 2069a6d42e7dSPeter Dunlap if (!so_conn->ic_tx_thread_running) { 2070a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 2071a6d42e7dSPeter Dunlap /* 2072a6d42e7dSPeter Dunlap * Don't release idt->idt_mutex since we're supposed to hold 2073a6d42e7dSPeter Dunlap * in when calling idm_buf_tx_to_ini_done 2074a6d42e7dSPeter Dunlap */ 2075a6d42e7dSPeter Dunlap idm_buf_tx_to_ini_done(idt, idb, IDM_STATUS_ABORTED); 2076a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 2077a6d42e7dSPeter Dunlap } 2078a6d42e7dSPeter Dunlap 2079a6d42e7dSPeter Dunlap /* 2080a6d42e7dSPeter Dunlap * Build a template for the data PDU headers we will use so that 2081a6d42e7dSPeter Dunlap * the SN values will stay consistent with other PDU's we are 2082a6d42e7dSPeter Dunlap * transmitting like R2T and SCSI status. 2083a6d42e7dSPeter Dunlap */ 2084a6d42e7dSPeter Dunlap bzero(&idb->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t)); 2085a6d42e7dSPeter Dunlap tmppdu.isp_hdr = &idb->idb_data_hdr_tmpl; 2086a6d42e7dSPeter Dunlap (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu, 2087a6d42e7dSPeter Dunlap ISCSI_OP_SCSI_DATA_RSP); 2088a6d42e7dSPeter Dunlap idb->idb_tx_thread = B_TRUE; 2089a6d42e7dSPeter Dunlap list_insert_tail(&so_conn->ic_tx_list, (void *)idb); 2090a6d42e7dSPeter Dunlap cv_signal(&so_conn->ic_tx_cv); 2091a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 2092a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 2093a6d42e7dSPeter Dunlap 2094a6d42e7dSPeter Dunlap /* 2095a6d42e7dSPeter Dunlap * Returning success here indicates the transfer was successfully 2096a6d42e7dSPeter Dunlap * dispatched -- it does not mean that the transfer completed 2097a6d42e7dSPeter Dunlap * successfully. 2098a6d42e7dSPeter Dunlap */ 2099a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 2100a6d42e7dSPeter Dunlap } 2101a6d42e7dSPeter Dunlap 2102a6d42e7dSPeter Dunlap /* 2103a6d42e7dSPeter Dunlap * The idm_so_buf_rx_from_ini() is used by the target iSCSI layer to specify the 2104a6d42e7dSPeter Dunlap * data blocks it is ready to receive from the initiator in response to a WRITE 2105a6d42e7dSPeter Dunlap * SCSI command. The target iSCSI layer passes the information about the desired 2106a6d42e7dSPeter Dunlap * data blocks to the initiator in one R2T PDU. The receiving buffer, the buffer 2107a6d42e7dSPeter Dunlap * offset and datalen are passed via the 'idb' argument. 2108a6d42e7dSPeter Dunlap * 2109a6d42e7dSPeter Dunlap * Scope for Prototype build: 2110a6d42e7dSPeter Dunlap * R2Ts are required for any Data-Out PDU, i.e. initiator and target must have 2111a6d42e7dSPeter Dunlap * negotiated the "InitialR2T" to "Yes". 2112a6d42e7dSPeter Dunlap * 2113a6d42e7dSPeter Dunlap * Caller holds idt->idt_mutex 2114a6d42e7dSPeter Dunlap */ 2115a6d42e7dSPeter Dunlap static idm_status_t 2116a6d42e7dSPeter Dunlap idm_so_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb) 2117a6d42e7dSPeter Dunlap { 2118a6d42e7dSPeter Dunlap idm_pdu_t *pdu; 2119a6d42e7dSPeter Dunlap iscsi_rtt_hdr_t *rtt; 2120a6d42e7dSPeter Dunlap 2121a6d42e7dSPeter Dunlap ASSERT(mutex_owned(&idt->idt_mutex)); 2122a6d42e7dSPeter Dunlap 2123a6d42e7dSPeter Dunlap pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP); 2124a6d42e7dSPeter Dunlap pdu->isp_ic = idt->idt_ic; 2125a6d42e7dSPeter Dunlap bzero(pdu->isp_hdr, sizeof (iscsi_rtt_hdr_t)); 2126a6d42e7dSPeter Dunlap 2127a6d42e7dSPeter Dunlap /* iSCSI layer fills the TTT, ITT, StatSN, ExpCmdSN, MaxCmdSN */ 2128a6d42e7dSPeter Dunlap (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, pdu, ISCSI_OP_RTT_RSP); 2129a6d42e7dSPeter Dunlap 2130a6d42e7dSPeter Dunlap /* set the rttsn, rtt.flags, rtt.data_offset and rtt.data_length */ 2131a6d42e7dSPeter Dunlap rtt = (iscsi_rtt_hdr_t *)(pdu->isp_hdr); 2132a6d42e7dSPeter Dunlap 2133a6d42e7dSPeter Dunlap rtt->opcode = ISCSI_OP_RTT_RSP; 2134a6d42e7dSPeter Dunlap rtt->flags = ISCSI_FLAG_FINAL; 2135a6d42e7dSPeter Dunlap rtt->data_offset = htonl(idb->idb_bufoffset); 2136a6d42e7dSPeter Dunlap rtt->data_length = htonl(idb->idb_xfer_len); 2137a6d42e7dSPeter Dunlap rtt->rttsn = htonl(idt->idt_exp_rttsn++); 2138a6d42e7dSPeter Dunlap 2139a6d42e7dSPeter Dunlap /* Keep track of buffer offsets */ 2140a6d42e7dSPeter Dunlap idb->idb_exp_offset = idb->idb_bufoffset; 2141a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 2142a6d42e7dSPeter Dunlap 2143a6d42e7dSPeter Dunlap /* 214463528ae4SJames Moore * Transmit the PDU. 2145a6d42e7dSPeter Dunlap */ 214663528ae4SJames Moore idm_pdu_tx(pdu); 2147a6d42e7dSPeter Dunlap 2148a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 2149a6d42e7dSPeter Dunlap } 2150a6d42e7dSPeter Dunlap 2151a6d42e7dSPeter Dunlap static idm_status_t 2152a6d42e7dSPeter Dunlap idm_so_buf_alloc(idm_buf_t *idb, uint64_t buflen) 2153a6d42e7dSPeter Dunlap { 2154*cf8c0ebaSPeter Dunlap if ((buflen > IDM_SO_BUF_CACHE_LB) && (buflen <= IDM_SO_BUF_CACHE_UB)) { 2155*cf8c0ebaSPeter Dunlap idb->idb_buf = kmem_cache_alloc(idm.idm_so_128k_buf_cache, 2156*cf8c0ebaSPeter Dunlap KM_NOSLEEP); 2157*cf8c0ebaSPeter Dunlap idb->idb_buf_private = idm.idm_so_128k_buf_cache; 2158*cf8c0ebaSPeter Dunlap } else { 2159*cf8c0ebaSPeter Dunlap idb->idb_buf = kmem_alloc(buflen, KM_NOSLEEP); 2160*cf8c0ebaSPeter Dunlap idb->idb_buf_private = NULL; 2161*cf8c0ebaSPeter Dunlap } 2162*cf8c0ebaSPeter Dunlap 2163a6d42e7dSPeter Dunlap if (idb->idb_buf == NULL) { 2164a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_NOTE, 2165a6d42e7dSPeter Dunlap "idm_so_buf_alloc: failed buffer allocation"); 2166a6d42e7dSPeter Dunlap return (IDM_STATUS_FAIL); 2167a6d42e7dSPeter Dunlap } 2168*cf8c0ebaSPeter Dunlap 2169a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 2170a6d42e7dSPeter Dunlap } 2171a6d42e7dSPeter Dunlap 2172a6d42e7dSPeter Dunlap /* ARGSUSED */ 2173a6d42e7dSPeter Dunlap static idm_status_t 2174a6d42e7dSPeter Dunlap idm_so_buf_setup(idm_buf_t *idb) 2175a6d42e7dSPeter Dunlap { 217630e7468fSPeter Dunlap /* Ensure bufalloc'd flag is unset */ 217730e7468fSPeter Dunlap idb->idb_bufalloc = B_FALSE; 217830e7468fSPeter Dunlap 2179a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 2180a6d42e7dSPeter Dunlap } 2181a6d42e7dSPeter Dunlap 2182a6d42e7dSPeter Dunlap /* ARGSUSED */ 2183a6d42e7dSPeter Dunlap static void 2184a6d42e7dSPeter Dunlap idm_so_buf_teardown(idm_buf_t *idb) 2185a6d42e7dSPeter Dunlap { 2186a6d42e7dSPeter Dunlap /* nothing to do here */ 2187a6d42e7dSPeter Dunlap } 2188a6d42e7dSPeter Dunlap 2189a6d42e7dSPeter Dunlap static void 2190a6d42e7dSPeter Dunlap idm_so_buf_free(idm_buf_t *idb) 2191a6d42e7dSPeter Dunlap { 2192*cf8c0ebaSPeter Dunlap if (idb->idb_buf_private == NULL) { 2193*cf8c0ebaSPeter Dunlap kmem_free(idb->idb_buf, idb->idb_buflen); 2194*cf8c0ebaSPeter Dunlap } else { 2195*cf8c0ebaSPeter Dunlap kmem_cache_free(idb->idb_buf_private, idb->idb_buf); 2196*cf8c0ebaSPeter Dunlap } 2197a6d42e7dSPeter Dunlap } 2198a6d42e7dSPeter Dunlap 219930e7468fSPeter Dunlap static void 220030e7468fSPeter Dunlap idm_so_send_rtt_data(idm_conn_t *ic, idm_task_t *idt, idm_buf_t *idb, 220130e7468fSPeter Dunlap uint32_t offset, uint32_t length) 220230e7468fSPeter Dunlap { 220330e7468fSPeter Dunlap idm_so_conn_t *so_conn = ic->ic_transport_private; 220430e7468fSPeter Dunlap idm_pdu_t tmppdu; 220530e7468fSPeter Dunlap idm_buf_t *rtt_buf; 220630e7468fSPeter Dunlap 220730e7468fSPeter Dunlap ASSERT(mutex_owned(&idt->idt_mutex)); 220830e7468fSPeter Dunlap 220930e7468fSPeter Dunlap /* 221030e7468fSPeter Dunlap * Allocate a buffer to represent the RTT transfer. We could further 221130e7468fSPeter Dunlap * optimize this by allocating the buffers internally from an rtt 221230e7468fSPeter Dunlap * specific buffer cache since this is socket-specific code but for 221330e7468fSPeter Dunlap * now we will keep it simple. 221430e7468fSPeter Dunlap */ 221530e7468fSPeter Dunlap rtt_buf = idm_buf_alloc(ic, (uint8_t *)idb->idb_buf + offset, length); 221630e7468fSPeter Dunlap if (rtt_buf == NULL) { 221730e7468fSPeter Dunlap /* 221830e7468fSPeter Dunlap * If we're in FFP then the failure was likely a resource 221930e7468fSPeter Dunlap * allocation issue and we should close the connection by 222030e7468fSPeter Dunlap * sending a CE_TRANSPORT_FAIL event. 222130e7468fSPeter Dunlap * 222230e7468fSPeter Dunlap * If we're not in FFP then idm_buf_alloc will always 222330e7468fSPeter Dunlap * fail and the state is transitioning to "complete" anyway 222430e7468fSPeter Dunlap * so we won't bother to send an event. 222530e7468fSPeter Dunlap */ 222630e7468fSPeter Dunlap mutex_enter(&ic->ic_state_mutex); 222730e7468fSPeter Dunlap if (ic->ic_ffp) 222830e7468fSPeter Dunlap idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, 222930e7468fSPeter Dunlap NULL, CT_NONE); 223030e7468fSPeter Dunlap mutex_exit(&ic->ic_state_mutex); 223130e7468fSPeter Dunlap return; 223230e7468fSPeter Dunlap } 223330e7468fSPeter Dunlap 223430e7468fSPeter Dunlap rtt_buf->idb_buf_cb = NULL; 223530e7468fSPeter Dunlap rtt_buf->idb_cb_arg = NULL; 223630e7468fSPeter Dunlap rtt_buf->idb_bufoffset = offset; 223730e7468fSPeter Dunlap rtt_buf->idb_xfer_len = length; 223830e7468fSPeter Dunlap rtt_buf->idb_ic = idt->idt_ic; 223930e7468fSPeter Dunlap rtt_buf->idb_task_binding = idt; 224030e7468fSPeter Dunlap 224130e7468fSPeter Dunlap /* 224230e7468fSPeter Dunlap * Put the idm_buf_t on the tx queue. It will be transmitted by 224330e7468fSPeter Dunlap * idm_sotx_thread. 224430e7468fSPeter Dunlap */ 224530e7468fSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 224630e7468fSPeter Dunlap 224730e7468fSPeter Dunlap if (!so_conn->ic_tx_thread_running) { 224830e7468fSPeter Dunlap idm_buf_free(rtt_buf); 224930e7468fSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 225030e7468fSPeter Dunlap return; 225130e7468fSPeter Dunlap } 225230e7468fSPeter Dunlap 225330e7468fSPeter Dunlap /* 225430e7468fSPeter Dunlap * This new buffer represents an additional reference on the task 225530e7468fSPeter Dunlap */ 225630e7468fSPeter Dunlap idm_task_hold(idt); 225730e7468fSPeter Dunlap 225830e7468fSPeter Dunlap /* 225930e7468fSPeter Dunlap * Build a template for the data PDU headers we will use so that 226030e7468fSPeter Dunlap * the SN values will stay consistent with other PDU's we are 226130e7468fSPeter Dunlap * transmitting like R2T and SCSI status. 226230e7468fSPeter Dunlap */ 226330e7468fSPeter Dunlap bzero(&rtt_buf->idb_data_hdr_tmpl, sizeof (iscsi_hdr_t)); 226430e7468fSPeter Dunlap tmppdu.isp_hdr = &rtt_buf->idb_data_hdr_tmpl; 226530e7468fSPeter Dunlap (*idt->idt_ic->ic_conn_ops.icb_build_hdr)(idt, &tmppdu, 226630e7468fSPeter Dunlap ISCSI_OP_SCSI_DATA); 226730e7468fSPeter Dunlap rtt_buf->idb_tx_thread = B_TRUE; 226830e7468fSPeter Dunlap rtt_buf->idb_in_transport = B_TRUE; 226930e7468fSPeter Dunlap list_insert_tail(&so_conn->ic_tx_list, (void *)rtt_buf); 227030e7468fSPeter Dunlap cv_signal(&so_conn->ic_tx_cv); 227130e7468fSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 227230e7468fSPeter Dunlap } 227330e7468fSPeter Dunlap 227430e7468fSPeter Dunlap static void 227530e7468fSPeter Dunlap idm_so_send_rtt_data_done(idm_task_t *idt, idm_buf_t *idb) 227630e7468fSPeter Dunlap { 227730e7468fSPeter Dunlap /* 227830e7468fSPeter Dunlap * Don't worry about status -- we assume any error handling 227930e7468fSPeter Dunlap * is performed by the caller (idm_sotx_thread). 228030e7468fSPeter Dunlap */ 228130e7468fSPeter Dunlap idb->idb_in_transport = B_FALSE; 228230e7468fSPeter Dunlap idm_task_rele(idt); 228330e7468fSPeter Dunlap idm_buf_free(idb); 228430e7468fSPeter Dunlap } 228530e7468fSPeter Dunlap 228630e7468fSPeter Dunlap static idm_status_t 228730e7468fSPeter Dunlap idm_so_send_buf_region(idm_task_t *idt, idm_buf_t *idb, 2288a6d42e7dSPeter Dunlap uint32_t buf_region_offset, uint32_t buf_region_length) 2289a6d42e7dSPeter Dunlap { 2290a6d42e7dSPeter Dunlap idm_conn_t *ic; 2291a6d42e7dSPeter Dunlap uint32_t max_dataseglen; 2292a6d42e7dSPeter Dunlap size_t remainder, chunk; 2293a6d42e7dSPeter Dunlap uint32_t data_offset = buf_region_offset; 2294a6d42e7dSPeter Dunlap iscsi_data_hdr_t *bhs; 2295a6d42e7dSPeter Dunlap idm_pdu_t *pdu; 229630e7468fSPeter Dunlap idm_status_t tx_status; 2297a6d42e7dSPeter Dunlap 2298a6d42e7dSPeter Dunlap ASSERT(mutex_owned(&idt->idt_mutex)); 2299a6d42e7dSPeter Dunlap 2300a6d42e7dSPeter Dunlap ic = idt->idt_ic; 2301a6d42e7dSPeter Dunlap 2302a6d42e7dSPeter Dunlap max_dataseglen = 8192; /* Need value from login negotiation */ 2303a6d42e7dSPeter Dunlap remainder = buf_region_length; 2304a6d42e7dSPeter Dunlap 2305a6d42e7dSPeter Dunlap while (remainder) { 2306a6d42e7dSPeter Dunlap if (idt->idt_state != TASK_ACTIVE) { 2307a6d42e7dSPeter Dunlap ASSERT((idt->idt_state != TASK_IDLE) && 2308a6d42e7dSPeter Dunlap (idt->idt_state != TASK_COMPLETE)); 2309a6d42e7dSPeter Dunlap return (IDM_STATUS_ABORTED); 2310a6d42e7dSPeter Dunlap } 2311a6d42e7dSPeter Dunlap 2312a6d42e7dSPeter Dunlap /* check to see if we need to chunk the data */ 2313a6d42e7dSPeter Dunlap if (remainder > max_dataseglen) { 2314a6d42e7dSPeter Dunlap chunk = max_dataseglen; 2315a6d42e7dSPeter Dunlap } else { 2316a6d42e7dSPeter Dunlap chunk = remainder; 2317a6d42e7dSPeter Dunlap } 2318a6d42e7dSPeter Dunlap 2319a6d42e7dSPeter Dunlap /* Data PDU headers will always be sizeof (iscsi_hdr_t) */ 2320a6d42e7dSPeter Dunlap pdu = kmem_cache_alloc(idm.idm_sotx_pdu_cache, KM_SLEEP); 2321a6d42e7dSPeter Dunlap pdu->isp_ic = ic; 2322a6d42e7dSPeter Dunlap 2323a6d42e7dSPeter Dunlap /* 232430e7468fSPeter Dunlap * We've already built a build a header template 2325a6d42e7dSPeter Dunlap * to use during the transfer. Use this template so that 2326a6d42e7dSPeter Dunlap * the SN values stay consistent with any unrelated PDU's 2327a6d42e7dSPeter Dunlap * being transmitted. 2328a6d42e7dSPeter Dunlap */ 232930e7468fSPeter Dunlap bcopy(&idb->idb_data_hdr_tmpl, pdu->isp_hdr, 233030e7468fSPeter Dunlap sizeof (iscsi_hdr_t)); 2331a6d42e7dSPeter Dunlap 2332a6d42e7dSPeter Dunlap /* 2333a6d42e7dSPeter Dunlap * Set DataSN, data offset, and flags in BHS 2334a6d42e7dSPeter Dunlap * For the prototype build, A = 0, S = 0, U = 0 2335a6d42e7dSPeter Dunlap */ 2336a6d42e7dSPeter Dunlap bhs = (iscsi_data_hdr_t *)(pdu->isp_hdr); 2337a6d42e7dSPeter Dunlap 2338a6d42e7dSPeter Dunlap bhs->datasn = htonl(idt->idt_exp_datasn++); 2339a6d42e7dSPeter Dunlap 2340a6d42e7dSPeter Dunlap hton24(bhs->dlength, chunk); 2341a6d42e7dSPeter Dunlap bhs->offset = htonl(idb->idb_bufoffset + data_offset); 2342a6d42e7dSPeter Dunlap 2343a6d42e7dSPeter Dunlap if (chunk == remainder) { 2344a6d42e7dSPeter Dunlap bhs->flags = ISCSI_FLAG_FINAL; /* F bit set to 1 */ 2345a6d42e7dSPeter Dunlap } 2346a6d42e7dSPeter Dunlap 2347a6d42e7dSPeter Dunlap /* setup data */ 2348a6d42e7dSPeter Dunlap pdu->isp_data = (uint8_t *)idb->idb_buf + data_offset; 2349a6d42e7dSPeter Dunlap pdu->isp_datalen = (uint_t)chunk; 2350a6d42e7dSPeter Dunlap remainder -= chunk; 2351a6d42e7dSPeter Dunlap data_offset += chunk; 2352a6d42e7dSPeter Dunlap 2353a6d42e7dSPeter Dunlap /* 2354a6d42e7dSPeter Dunlap * Now that we're done working with idt_exp_datasn, 2355a6d42e7dSPeter Dunlap * idt->idt_state and idb->idb_bufoffset we can release 2356a6d42e7dSPeter Dunlap * the task lock -- don't want to hold it across the 2357a6d42e7dSPeter Dunlap * call to idm_i_so_tx since we could block. 2358a6d42e7dSPeter Dunlap */ 2359a6d42e7dSPeter Dunlap mutex_exit(&idt->idt_mutex); 2360a6d42e7dSPeter Dunlap 2361a6d42e7dSPeter Dunlap /* 2362a6d42e7dSPeter Dunlap * Transmit the PDU. Call the internal routine directly 2363a6d42e7dSPeter Dunlap * as there is already implicit ordering. 2364a6d42e7dSPeter Dunlap */ 236530e7468fSPeter Dunlap if ((tx_status = idm_i_so_tx(pdu)) != IDM_STATUS_SUCCESS) { 236630e7468fSPeter Dunlap mutex_enter(&idt->idt_mutex); 236730e7468fSPeter Dunlap return (tx_status); 236830e7468fSPeter Dunlap } 2369a6d42e7dSPeter Dunlap 2370a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 237130e7468fSPeter Dunlap idt->idt_tx_bytes += chunk; 2372a6d42e7dSPeter Dunlap } 2373a6d42e7dSPeter Dunlap 2374a6d42e7dSPeter Dunlap return (IDM_STATUS_SUCCESS); 2375a6d42e7dSPeter Dunlap } 2376a6d42e7dSPeter Dunlap 2377a6d42e7dSPeter Dunlap /* 2378a6d42e7dSPeter Dunlap * TX PDU cache 2379a6d42e7dSPeter Dunlap */ 2380a6d42e7dSPeter Dunlap /* ARGSUSED */ 2381a6d42e7dSPeter Dunlap int 2382a6d42e7dSPeter Dunlap idm_sotx_pdu_constructor(void *hdl, void *arg, int flags) 2383a6d42e7dSPeter Dunlap { 2384a6d42e7dSPeter Dunlap idm_pdu_t *pdu = hdl; 2385a6d42e7dSPeter Dunlap 2386a6d42e7dSPeter Dunlap bzero(pdu, sizeof (idm_pdu_t)); 2387a6d42e7dSPeter Dunlap pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */ 2388a6d42e7dSPeter Dunlap pdu->isp_hdrlen = sizeof (iscsi_hdr_t); 2389a6d42e7dSPeter Dunlap pdu->isp_callback = idm_sotx_cache_pdu_cb; 2390a6d42e7dSPeter Dunlap pdu->isp_magic = IDM_PDU_MAGIC; 2391a6d42e7dSPeter Dunlap bzero(pdu->isp_hdr, sizeof (iscsi_hdr_t)); 2392a6d42e7dSPeter Dunlap 2393a6d42e7dSPeter Dunlap return (0); 2394a6d42e7dSPeter Dunlap } 2395a6d42e7dSPeter Dunlap 2396a6d42e7dSPeter Dunlap /* ARGSUSED */ 2397a6d42e7dSPeter Dunlap void 2398a6d42e7dSPeter Dunlap idm_sotx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 2399a6d42e7dSPeter Dunlap { 2400a6d42e7dSPeter Dunlap /* reset values between use */ 2401a6d42e7dSPeter Dunlap pdu->isp_datalen = 0; 2402a6d42e7dSPeter Dunlap 2403a6d42e7dSPeter Dunlap kmem_cache_free(idm.idm_sotx_pdu_cache, pdu); 2404a6d42e7dSPeter Dunlap } 2405a6d42e7dSPeter Dunlap 2406a6d42e7dSPeter Dunlap /* 2407a6d42e7dSPeter Dunlap * RX PDU cache 2408a6d42e7dSPeter Dunlap */ 2409a6d42e7dSPeter Dunlap /* ARGSUSED */ 2410a6d42e7dSPeter Dunlap int 2411a6d42e7dSPeter Dunlap idm_sorx_pdu_constructor(void *hdl, void *arg, int flags) 2412a6d42e7dSPeter Dunlap { 2413a6d42e7dSPeter Dunlap idm_pdu_t *pdu = hdl; 2414a6d42e7dSPeter Dunlap 2415a6d42e7dSPeter Dunlap bzero(pdu, sizeof (idm_pdu_t)); 2416a6d42e7dSPeter Dunlap pdu->isp_magic = IDM_PDU_MAGIC; 2417a6d42e7dSPeter Dunlap pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */ 2418a6d42e7dSPeter Dunlap pdu->isp_callback = idm_sorx_cache_pdu_cb; 2419a6d42e7dSPeter Dunlap 2420a6d42e7dSPeter Dunlap return (0); 2421a6d42e7dSPeter Dunlap } 2422a6d42e7dSPeter Dunlap 2423a6d42e7dSPeter Dunlap /* ARGSUSED */ 2424a6d42e7dSPeter Dunlap static void 2425a6d42e7dSPeter Dunlap idm_sorx_cache_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 2426a6d42e7dSPeter Dunlap { 2427a6d42e7dSPeter Dunlap pdu->isp_iovlen = 0; 2428a6d42e7dSPeter Dunlap pdu->isp_sorx_buf = 0; 2429a6d42e7dSPeter Dunlap kmem_cache_free(idm.idm_sorx_pdu_cache, pdu); 2430a6d42e7dSPeter Dunlap } 2431a6d42e7dSPeter Dunlap 2432a6d42e7dSPeter Dunlap static void 2433a6d42e7dSPeter Dunlap idm_sorx_addl_pdu_cb(idm_pdu_t *pdu, idm_status_t status) 2434a6d42e7dSPeter Dunlap { 2435a6d42e7dSPeter Dunlap /* 2436a6d42e7dSPeter Dunlap * We had to modify our cached RX PDU with a longer header buffer 2437a6d42e7dSPeter Dunlap * and/or a longer data buffer. Release the new buffers and fix 2438a6d42e7dSPeter Dunlap * the fields back to what we would expect for a cached RX PDU. 2439a6d42e7dSPeter Dunlap */ 2440a6d42e7dSPeter Dunlap if (pdu->isp_flags & IDM_PDU_ADDL_HDR) { 2441a6d42e7dSPeter Dunlap kmem_free(pdu->isp_hdr, pdu->isp_hdrlen); 2442a6d42e7dSPeter Dunlap } 2443a6d42e7dSPeter Dunlap if (pdu->isp_flags & IDM_PDU_ADDL_DATA) { 2444a6d42e7dSPeter Dunlap kmem_free(pdu->isp_data, pdu->isp_datalen); 2445a6d42e7dSPeter Dunlap } 2446a6d42e7dSPeter Dunlap pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); 2447a6d42e7dSPeter Dunlap pdu->isp_hdrlen = sizeof (iscsi_hdr_t); 2448a6d42e7dSPeter Dunlap pdu->isp_data = NULL; 2449a6d42e7dSPeter Dunlap pdu->isp_datalen = 0; 2450a6d42e7dSPeter Dunlap pdu->isp_sorx_buf = 0; 2451a6d42e7dSPeter Dunlap pdu->isp_callback = idm_sorx_cache_pdu_cb; 2452a6d42e7dSPeter Dunlap idm_sorx_cache_pdu_cb(pdu, status); 2453a6d42e7dSPeter Dunlap } 2454a6d42e7dSPeter Dunlap 2455a6d42e7dSPeter Dunlap /* 2456a6d42e7dSPeter Dunlap * This thread is only active when I/O is queued for transmit 2457a6d42e7dSPeter Dunlap * because the socket is busy. 2458a6d42e7dSPeter Dunlap */ 2459a6d42e7dSPeter Dunlap void 2460a6d42e7dSPeter Dunlap idm_sotx_thread(void *arg) 2461a6d42e7dSPeter Dunlap { 2462a6d42e7dSPeter Dunlap idm_conn_t *ic = arg; 2463a6d42e7dSPeter Dunlap idm_tx_obj_t *object, *next; 2464a6d42e7dSPeter Dunlap idm_so_conn_t *so_conn; 2465a6d42e7dSPeter Dunlap idm_status_t status = IDM_STATUS_SUCCESS; 2466a6d42e7dSPeter Dunlap 2467a6d42e7dSPeter Dunlap idm_conn_hold(ic); 2468a6d42e7dSPeter Dunlap 2469a6d42e7dSPeter Dunlap mutex_enter(&ic->ic_mutex); 2470a6d42e7dSPeter Dunlap so_conn = ic->ic_transport_private; 2471a6d42e7dSPeter Dunlap so_conn->ic_tx_thread_running = B_TRUE; 2472a6d42e7dSPeter Dunlap so_conn->ic_tx_thread_did = so_conn->ic_tx_thread->t_did; 2473a6d42e7dSPeter Dunlap cv_signal(&ic->ic_cv); 2474a6d42e7dSPeter Dunlap mutex_exit(&ic->ic_mutex); 2475a6d42e7dSPeter Dunlap 2476a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 2477a6d42e7dSPeter Dunlap 2478a6d42e7dSPeter Dunlap while (so_conn->ic_tx_thread_running) { 2479a6d42e7dSPeter Dunlap while (list_is_empty(&so_conn->ic_tx_list)) { 2480a6d42e7dSPeter Dunlap DTRACE_PROBE1(soconn__tx__sleep, idm_conn_t *, ic); 2481a6d42e7dSPeter Dunlap cv_wait(&so_conn->ic_tx_cv, &so_conn->ic_tx_mutex); 2482a6d42e7dSPeter Dunlap DTRACE_PROBE1(soconn__tx__wakeup, idm_conn_t *, ic); 2483a6d42e7dSPeter Dunlap 2484a6d42e7dSPeter Dunlap if (!so_conn->ic_tx_thread_running) { 2485a6d42e7dSPeter Dunlap goto tx_bail; 2486a6d42e7dSPeter Dunlap } 2487a6d42e7dSPeter Dunlap } 2488a6d42e7dSPeter Dunlap 2489a6d42e7dSPeter Dunlap object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list); 2490a6d42e7dSPeter Dunlap list_remove(&so_conn->ic_tx_list, object); 2491a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 2492a6d42e7dSPeter Dunlap 2493a6d42e7dSPeter Dunlap switch (object->idm_tx_obj_magic) { 2494a6d42e7dSPeter Dunlap case IDM_PDU_MAGIC: 2495a6d42e7dSPeter Dunlap DTRACE_PROBE2(soconn__tx__pdu, idm_conn_t *, ic, 2496a6d42e7dSPeter Dunlap idm_pdu_t *, (idm_pdu_t *)object); 2497a6d42e7dSPeter Dunlap 2498a6d42e7dSPeter Dunlap status = idm_i_so_tx((idm_pdu_t *)object); 2499a6d42e7dSPeter Dunlap break; 2500a6d42e7dSPeter Dunlap 2501a6d42e7dSPeter Dunlap case IDM_BUF_MAGIC: { 2502a6d42e7dSPeter Dunlap idm_buf_t *idb = (idm_buf_t *)object; 2503a6d42e7dSPeter Dunlap idm_task_t *idt = idb->idb_task_binding; 2504a6d42e7dSPeter Dunlap 2505a6d42e7dSPeter Dunlap DTRACE_PROBE2(soconn__tx__buf, idm_conn_t *, ic, 2506a6d42e7dSPeter Dunlap idm_buf_t *, idb); 2507a6d42e7dSPeter Dunlap 2508a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 2509a6d42e7dSPeter Dunlap status = idm_so_send_buf_region(idt, 251030e7468fSPeter Dunlap idb, 0, idb->idb_xfer_len); 2511a6d42e7dSPeter Dunlap 2512a6d42e7dSPeter Dunlap /* 2513a6d42e7dSPeter Dunlap * TX thread owns the buffer so we expect it to 2514a6d42e7dSPeter Dunlap * be "in transport" 2515a6d42e7dSPeter Dunlap */ 2516a6d42e7dSPeter Dunlap ASSERT(idb->idb_in_transport); 251730e7468fSPeter Dunlap if (IDM_CONN_ISTGT(ic)) { 251830e7468fSPeter Dunlap /* 251930e7468fSPeter Dunlap * idm_buf_tx_to_ini_done releases 252030e7468fSPeter Dunlap * idt->idt_mutex 252130e7468fSPeter Dunlap */ 252230e7468fSPeter Dunlap idm_buf_tx_to_ini_done(idt, idb, status); 252330e7468fSPeter Dunlap } else { 252430e7468fSPeter Dunlap idm_so_send_rtt_data_done(idt, idb); 252530e7468fSPeter Dunlap mutex_exit(&idt->idt_mutex); 252630e7468fSPeter Dunlap } 2527a6d42e7dSPeter Dunlap break; 2528a6d42e7dSPeter Dunlap } 2529a6d42e7dSPeter Dunlap 2530a6d42e7dSPeter Dunlap default: 2531a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, "idm_sotx_thread: Unknown magic " 2532a6d42e7dSPeter Dunlap "(0x%08x)", object->idm_tx_obj_magic); 2533a6d42e7dSPeter Dunlap status = IDM_STATUS_FAIL; 2534a6d42e7dSPeter Dunlap } 2535a6d42e7dSPeter Dunlap 2536a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 2537a6d42e7dSPeter Dunlap 2538a6d42e7dSPeter Dunlap if (status != IDM_STATUS_SUCCESS) { 2539a6d42e7dSPeter Dunlap so_conn->ic_tx_thread_running = B_FALSE; 2540a6d42e7dSPeter Dunlap idm_conn_event(ic, CE_TRANSPORT_FAIL, status); 2541a6d42e7dSPeter Dunlap } 2542a6d42e7dSPeter Dunlap } 2543a6d42e7dSPeter Dunlap 2544a6d42e7dSPeter Dunlap /* 2545a6d42e7dSPeter Dunlap * Before we leave, we need to abort every item remaining in the 2546a6d42e7dSPeter Dunlap * TX list. 2547a6d42e7dSPeter Dunlap */ 2548a6d42e7dSPeter Dunlap 2549a6d42e7dSPeter Dunlap tx_bail: 2550a6d42e7dSPeter Dunlap object = (idm_tx_obj_t *)list_head(&so_conn->ic_tx_list); 2551a6d42e7dSPeter Dunlap 2552a6d42e7dSPeter Dunlap while (object != NULL) { 2553a6d42e7dSPeter Dunlap next = list_next(&so_conn->ic_tx_list, object); 2554a6d42e7dSPeter Dunlap 2555a6d42e7dSPeter Dunlap list_remove(&so_conn->ic_tx_list, object); 2556a6d42e7dSPeter Dunlap switch (object->idm_tx_obj_magic) { 2557a6d42e7dSPeter Dunlap case IDM_PDU_MAGIC: 2558a6d42e7dSPeter Dunlap idm_pdu_complete((idm_pdu_t *)object, 2559a6d42e7dSPeter Dunlap IDM_STATUS_ABORTED); 2560a6d42e7dSPeter Dunlap break; 2561a6d42e7dSPeter Dunlap 2562a6d42e7dSPeter Dunlap case IDM_BUF_MAGIC: { 2563a6d42e7dSPeter Dunlap idm_buf_t *idb = (idm_buf_t *)object; 2564a6d42e7dSPeter Dunlap idm_task_t *idt = idb->idb_task_binding; 2565a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 2566a6d42e7dSPeter Dunlap mutex_enter(&idt->idt_mutex); 2567a6d42e7dSPeter Dunlap /* 2568a6d42e7dSPeter Dunlap * TX thread owns the buffer so we expect it to 2569a6d42e7dSPeter Dunlap * be "in transport" 2570a6d42e7dSPeter Dunlap */ 2571a6d42e7dSPeter Dunlap ASSERT(idb->idb_in_transport); 257230e7468fSPeter Dunlap if (IDM_CONN_ISTGT(ic)) { 257330e7468fSPeter Dunlap /* 257430e7468fSPeter Dunlap * idm_buf_tx_to_ini_done releases 257530e7468fSPeter Dunlap * idt->idt_mutex 257630e7468fSPeter Dunlap */ 257730e7468fSPeter Dunlap idm_buf_tx_to_ini_done(idt, idb, 257830e7468fSPeter Dunlap IDM_STATUS_ABORTED); 257930e7468fSPeter Dunlap } else { 258030e7468fSPeter Dunlap idm_so_send_rtt_data_done(idt, idb); 258130e7468fSPeter Dunlap mutex_exit(&idt->idt_mutex); 258230e7468fSPeter Dunlap } 2583a6d42e7dSPeter Dunlap mutex_enter(&so_conn->ic_tx_mutex); 2584a6d42e7dSPeter Dunlap break; 2585a6d42e7dSPeter Dunlap } 2586a6d42e7dSPeter Dunlap default: 2587a6d42e7dSPeter Dunlap IDM_CONN_LOG(CE_WARN, 2588a6d42e7dSPeter Dunlap "idm_sotx_thread: Unexpected magic " 2589a6d42e7dSPeter Dunlap "(0x%08x)", object->idm_tx_obj_magic); 2590a6d42e7dSPeter Dunlap } 2591a6d42e7dSPeter Dunlap 2592a6d42e7dSPeter Dunlap object = next; 2593a6d42e7dSPeter Dunlap } 2594a6d42e7dSPeter Dunlap 2595a6d42e7dSPeter Dunlap mutex_exit(&so_conn->ic_tx_mutex); 2596a6d42e7dSPeter Dunlap idm_conn_rele(ic); 2597a6d42e7dSPeter Dunlap thread_exit(); 2598a6d42e7dSPeter Dunlap /*NOTREACHED*/ 2599a6d42e7dSPeter Dunlap } 2600