17978SPeter.Dunlap@Sun.COM /* 27978SPeter.Dunlap@Sun.COM * CDDL HEADER START 37978SPeter.Dunlap@Sun.COM * 47978SPeter.Dunlap@Sun.COM * The contents of this file are subject to the terms of the 57978SPeter.Dunlap@Sun.COM * Common Development and Distribution License (the "License"). 67978SPeter.Dunlap@Sun.COM * You may not use this file except in compliance with the License. 77978SPeter.Dunlap@Sun.COM * 87978SPeter.Dunlap@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97978SPeter.Dunlap@Sun.COM * or http://www.opensolaris.org/os/licensing. 107978SPeter.Dunlap@Sun.COM * See the License for the specific language governing permissions 117978SPeter.Dunlap@Sun.COM * and limitations under the License. 127978SPeter.Dunlap@Sun.COM * 137978SPeter.Dunlap@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 147978SPeter.Dunlap@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157978SPeter.Dunlap@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 167978SPeter.Dunlap@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 177978SPeter.Dunlap@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 187978SPeter.Dunlap@Sun.COM * 197978SPeter.Dunlap@Sun.COM * CDDL HEADER END 207978SPeter.Dunlap@Sun.COM */ 217978SPeter.Dunlap@Sun.COM /* 22*9162SPeter.Dunlap@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237978SPeter.Dunlap@Sun.COM * Use is subject to license terms. 247978SPeter.Dunlap@Sun.COM */ 257978SPeter.Dunlap@Sun.COM 267978SPeter.Dunlap@Sun.COM #include <sys/cpuvar.h> 277978SPeter.Dunlap@Sun.COM #include <sys/conf.h> 287978SPeter.Dunlap@Sun.COM #include <sys/file.h> 297978SPeter.Dunlap@Sun.COM #include <sys/ddi.h> 307978SPeter.Dunlap@Sun.COM #include <sys/sunddi.h> 317978SPeter.Dunlap@Sun.COM #include <sys/modctl.h> 327978SPeter.Dunlap@Sun.COM 337978SPeter.Dunlap@Sun.COM #include <sys/socket.h> 347978SPeter.Dunlap@Sun.COM #include <sys/strsubr.h> 357978SPeter.Dunlap@Sun.COM #include <sys/sysmacros.h> 367978SPeter.Dunlap@Sun.COM 377978SPeter.Dunlap@Sun.COM #include <sys/socketvar.h> 387978SPeter.Dunlap@Sun.COM #include <netinet/in.h> 397978SPeter.Dunlap@Sun.COM 407978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h> 417978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_so.h> 427978SPeter.Dunlap@Sun.COM 437978SPeter.Dunlap@Sun.COM #define IDM_NAME_VERSION "iSCSI Data Mover" 447978SPeter.Dunlap@Sun.COM 457978SPeter.Dunlap@Sun.COM extern struct mod_ops mod_miscops; 467978SPeter.Dunlap@Sun.COM extern struct mod_ops mod_miscops; 477978SPeter.Dunlap@Sun.COM 487978SPeter.Dunlap@Sun.COM static struct modlmisc modlmisc = { 497978SPeter.Dunlap@Sun.COM &mod_miscops, /* Type of module */ 507978SPeter.Dunlap@Sun.COM IDM_NAME_VERSION 517978SPeter.Dunlap@Sun.COM }; 527978SPeter.Dunlap@Sun.COM 537978SPeter.Dunlap@Sun.COM static struct modlinkage modlinkage = { 547978SPeter.Dunlap@Sun.COM MODREV_1, (void *)&modlmisc, NULL 557978SPeter.Dunlap@Sun.COM }; 567978SPeter.Dunlap@Sun.COM 577978SPeter.Dunlap@Sun.COM extern int idm_task_compare(const void *t1, const void *t2); 587978SPeter.Dunlap@Sun.COM extern void idm_wd_thread(void *arg); 597978SPeter.Dunlap@Sun.COM 607978SPeter.Dunlap@Sun.COM static int _idm_init(void); 617978SPeter.Dunlap@Sun.COM static int _idm_fini(void); 627978SPeter.Dunlap@Sun.COM static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf); 637978SPeter.Dunlap@Sun.COM static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf); 647978SPeter.Dunlap@Sun.COM static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf); 657978SPeter.Dunlap@Sun.COM static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf); 667978SPeter.Dunlap@Sun.COM static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, 677978SPeter.Dunlap@Sun.COM idm_abort_type_t abort_type); 687978SPeter.Dunlap@Sun.COM static void idm_task_aborted(idm_task_t *idt, idm_status_t status); 69*9162SPeter.Dunlap@Sun.COM static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, 70*9162SPeter.Dunlap@Sun.COM int sleepflag); 717978SPeter.Dunlap@Sun.COM 727978SPeter.Dunlap@Sun.COM boolean_t idm_conn_logging = 0; 737978SPeter.Dunlap@Sun.COM boolean_t idm_svc_logging = 0; 74*9162SPeter.Dunlap@Sun.COM #ifdef DEBUG 75*9162SPeter.Dunlap@Sun.COM boolean_t idm_pattern_checking = 1; 76*9162SPeter.Dunlap@Sun.COM #else 77*9162SPeter.Dunlap@Sun.COM boolean_t idm_pattern_checking = 0; 78*9162SPeter.Dunlap@Sun.COM #endif 797978SPeter.Dunlap@Sun.COM 807978SPeter.Dunlap@Sun.COM /* 817978SPeter.Dunlap@Sun.COM * Potential tuneable for the maximum number of tasks. Default to 827978SPeter.Dunlap@Sun.COM * IDM_TASKIDS_MAX 837978SPeter.Dunlap@Sun.COM */ 847978SPeter.Dunlap@Sun.COM 857978SPeter.Dunlap@Sun.COM uint32_t idm_max_taskids = IDM_TASKIDS_MAX; 867978SPeter.Dunlap@Sun.COM 877978SPeter.Dunlap@Sun.COM /* 887978SPeter.Dunlap@Sun.COM * Global list of transport handles 897978SPeter.Dunlap@Sun.COM * These are listed in preferential order, so we can simply take the 907978SPeter.Dunlap@Sun.COM * first "it_conn_is_capable" hit. Note also that the order maps to 917978SPeter.Dunlap@Sun.COM * the order of the idm_transport_type_t list. 927978SPeter.Dunlap@Sun.COM */ 937978SPeter.Dunlap@Sun.COM idm_transport_t idm_transport_list[] = { 947978SPeter.Dunlap@Sun.COM 957978SPeter.Dunlap@Sun.COM /* iSER on InfiniBand transport handle */ 967978SPeter.Dunlap@Sun.COM {IDM_TRANSPORT_TYPE_ISER, /* type */ 977978SPeter.Dunlap@Sun.COM "/devices/ib/iser@0:iser", /* device path */ 987978SPeter.Dunlap@Sun.COM NULL, /* LDI handle */ 997978SPeter.Dunlap@Sun.COM NULL, /* transport ops */ 1007978SPeter.Dunlap@Sun.COM NULL}, /* transport caps */ 1017978SPeter.Dunlap@Sun.COM 1027978SPeter.Dunlap@Sun.COM /* IDM native sockets transport handle */ 1037978SPeter.Dunlap@Sun.COM {IDM_TRANSPORT_TYPE_SOCKETS, /* type */ 1047978SPeter.Dunlap@Sun.COM NULL, /* device path */ 1057978SPeter.Dunlap@Sun.COM NULL, /* LDI handle */ 1067978SPeter.Dunlap@Sun.COM NULL, /* transport ops */ 1077978SPeter.Dunlap@Sun.COM NULL} /* transport caps */ 1087978SPeter.Dunlap@Sun.COM 1097978SPeter.Dunlap@Sun.COM }; 1107978SPeter.Dunlap@Sun.COM 1117978SPeter.Dunlap@Sun.COM int 1127978SPeter.Dunlap@Sun.COM _init(void) 1137978SPeter.Dunlap@Sun.COM { 1147978SPeter.Dunlap@Sun.COM int rc; 1157978SPeter.Dunlap@Sun.COM 1167978SPeter.Dunlap@Sun.COM if ((rc = _idm_init()) != 0) { 1177978SPeter.Dunlap@Sun.COM return (rc); 1187978SPeter.Dunlap@Sun.COM } 1197978SPeter.Dunlap@Sun.COM 1207978SPeter.Dunlap@Sun.COM return (mod_install(&modlinkage)); 1217978SPeter.Dunlap@Sun.COM } 1227978SPeter.Dunlap@Sun.COM 1237978SPeter.Dunlap@Sun.COM int 1247978SPeter.Dunlap@Sun.COM _fini(void) 1257978SPeter.Dunlap@Sun.COM { 1267978SPeter.Dunlap@Sun.COM int rc; 1277978SPeter.Dunlap@Sun.COM 1287978SPeter.Dunlap@Sun.COM if ((rc = _idm_fini()) != 0) { 1297978SPeter.Dunlap@Sun.COM return (rc); 1307978SPeter.Dunlap@Sun.COM } 1317978SPeter.Dunlap@Sun.COM 1327978SPeter.Dunlap@Sun.COM if ((rc = mod_remove(&modlinkage)) != 0) { 1337978SPeter.Dunlap@Sun.COM return (rc); 1347978SPeter.Dunlap@Sun.COM } 1357978SPeter.Dunlap@Sun.COM 1367978SPeter.Dunlap@Sun.COM return (rc); 1377978SPeter.Dunlap@Sun.COM } 1387978SPeter.Dunlap@Sun.COM 1397978SPeter.Dunlap@Sun.COM int 1407978SPeter.Dunlap@Sun.COM _info(struct modinfo *modinfop) 1417978SPeter.Dunlap@Sun.COM { 1427978SPeter.Dunlap@Sun.COM return (mod_info(&modlinkage, modinfop)); 1437978SPeter.Dunlap@Sun.COM } 1447978SPeter.Dunlap@Sun.COM 1457978SPeter.Dunlap@Sun.COM /* 1467978SPeter.Dunlap@Sun.COM * idm_transport_register() 1477978SPeter.Dunlap@Sun.COM * 1487978SPeter.Dunlap@Sun.COM * Provides a mechanism for an IDM transport driver to register its 1497978SPeter.Dunlap@Sun.COM * transport ops and caps with the IDM kernel module. Invoked during 1507978SPeter.Dunlap@Sun.COM * a transport driver's attach routine. 1517978SPeter.Dunlap@Sun.COM */ 1527978SPeter.Dunlap@Sun.COM idm_status_t 1537978SPeter.Dunlap@Sun.COM idm_transport_register(idm_transport_attr_t *attr) 1547978SPeter.Dunlap@Sun.COM { 1557978SPeter.Dunlap@Sun.COM ASSERT(attr->it_ops != NULL); 1567978SPeter.Dunlap@Sun.COM ASSERT(attr->it_caps != NULL); 1577978SPeter.Dunlap@Sun.COM 1587978SPeter.Dunlap@Sun.COM switch (attr->type) { 1597978SPeter.Dunlap@Sun.COM /* All known non-native transports here; for now, iSER */ 1607978SPeter.Dunlap@Sun.COM case IDM_TRANSPORT_TYPE_ISER: 1617978SPeter.Dunlap@Sun.COM idm_transport_list[attr->type].it_ops = attr->it_ops; 1627978SPeter.Dunlap@Sun.COM idm_transport_list[attr->type].it_caps = attr->it_caps; 1637978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 1647978SPeter.Dunlap@Sun.COM 1657978SPeter.Dunlap@Sun.COM default: 1667978SPeter.Dunlap@Sun.COM cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in " 1677978SPeter.Dunlap@Sun.COM "idm_transport_register", attr->type); 1687978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 1697978SPeter.Dunlap@Sun.COM } 1707978SPeter.Dunlap@Sun.COM } 1717978SPeter.Dunlap@Sun.COM 1727978SPeter.Dunlap@Sun.COM /* 1737978SPeter.Dunlap@Sun.COM * idm_ini_conn_create 1747978SPeter.Dunlap@Sun.COM * 1757978SPeter.Dunlap@Sun.COM * This function is invoked by the iSCSI layer to create a connection context. 1767978SPeter.Dunlap@Sun.COM * This does not actually establish the socket connection. 1777978SPeter.Dunlap@Sun.COM * 1787978SPeter.Dunlap@Sun.COM * cr - Connection request parameters 1797978SPeter.Dunlap@Sun.COM * new_con - Output parameter that contains the new request if successful 1807978SPeter.Dunlap@Sun.COM * 1817978SPeter.Dunlap@Sun.COM */ 1827978SPeter.Dunlap@Sun.COM idm_status_t 1837978SPeter.Dunlap@Sun.COM idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con) 1847978SPeter.Dunlap@Sun.COM { 1857978SPeter.Dunlap@Sun.COM idm_transport_t *it; 1867978SPeter.Dunlap@Sun.COM idm_conn_t *ic; 1877978SPeter.Dunlap@Sun.COM int rc; 1887978SPeter.Dunlap@Sun.COM 1897978SPeter.Dunlap@Sun.COM it = idm_transport_lookup(cr); 1907978SPeter.Dunlap@Sun.COM 1917978SPeter.Dunlap@Sun.COM retry: 1927978SPeter.Dunlap@Sun.COM ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type, 1937978SPeter.Dunlap@Sun.COM &cr->icr_conn_ops); 1947978SPeter.Dunlap@Sun.COM 1957978SPeter.Dunlap@Sun.COM bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr, 1967978SPeter.Dunlap@Sun.COM sizeof (cr->cr_ini_dst_addr)); 1977978SPeter.Dunlap@Sun.COM 1987978SPeter.Dunlap@Sun.COM /* create the transport-specific connection components */ 1997978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_ini_conn_create(cr, ic); 2007978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 2017978SPeter.Dunlap@Sun.COM /* cleanup the failed connection */ 2027978SPeter.Dunlap@Sun.COM idm_conn_destroy_common(ic); 2037978SPeter.Dunlap@Sun.COM kmem_free(ic, sizeof (idm_conn_t)); 2047978SPeter.Dunlap@Sun.COM 2057978SPeter.Dunlap@Sun.COM /* 2067978SPeter.Dunlap@Sun.COM * It is possible for an IB client to connect to 2077978SPeter.Dunlap@Sun.COM * an ethernet-only client via an IB-eth gateway. 2087978SPeter.Dunlap@Sun.COM * Therefore, if we are attempting to use iSER and 2097978SPeter.Dunlap@Sun.COM * fail, retry with sockets before ultimately 2107978SPeter.Dunlap@Sun.COM * failing the connection. 2117978SPeter.Dunlap@Sun.COM */ 2127978SPeter.Dunlap@Sun.COM if (it->it_type == IDM_TRANSPORT_TYPE_ISER) { 2137978SPeter.Dunlap@Sun.COM it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]; 2147978SPeter.Dunlap@Sun.COM goto retry; 2157978SPeter.Dunlap@Sun.COM } 2167978SPeter.Dunlap@Sun.COM 2177978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 2187978SPeter.Dunlap@Sun.COM } 2197978SPeter.Dunlap@Sun.COM 2207978SPeter.Dunlap@Sun.COM *new_con = ic; 2217978SPeter.Dunlap@Sun.COM 2227978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 2237978SPeter.Dunlap@Sun.COM list_insert_tail(&idm.idm_ini_conn_list, ic); 2247978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 2257978SPeter.Dunlap@Sun.COM 2267978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 2277978SPeter.Dunlap@Sun.COM } 2287978SPeter.Dunlap@Sun.COM 2297978SPeter.Dunlap@Sun.COM /* 2307978SPeter.Dunlap@Sun.COM * idm_ini_conn_destroy 2317978SPeter.Dunlap@Sun.COM * 2327978SPeter.Dunlap@Sun.COM * Releases any resources associated with the connection. This is the 2337978SPeter.Dunlap@Sun.COM * complement to idm_ini_conn_create. 2347978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 2357978SPeter.Dunlap@Sun.COM * 2367978SPeter.Dunlap@Sun.COM */ 2377978SPeter.Dunlap@Sun.COM void 238*9162SPeter.Dunlap@Sun.COM idm_ini_conn_destroy_task(void *ic_void) 239*9162SPeter.Dunlap@Sun.COM { 240*9162SPeter.Dunlap@Sun.COM idm_conn_t *ic = ic_void; 241*9162SPeter.Dunlap@Sun.COM 242*9162SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_ini_conn_destroy(ic); 243*9162SPeter.Dunlap@Sun.COM idm_conn_destroy_common(ic); 244*9162SPeter.Dunlap@Sun.COM } 245*9162SPeter.Dunlap@Sun.COM 246*9162SPeter.Dunlap@Sun.COM void 2477978SPeter.Dunlap@Sun.COM idm_ini_conn_destroy(idm_conn_t *ic) 2487978SPeter.Dunlap@Sun.COM { 249*9162SPeter.Dunlap@Sun.COM /* 250*9162SPeter.Dunlap@Sun.COM * It's reasonable for the initiator to call idm_ini_conn_destroy 251*9162SPeter.Dunlap@Sun.COM * from within the context of the CN_CONNECT_DESTROY notification. 252*9162SPeter.Dunlap@Sun.COM * That's a problem since we want to destroy the taskq for the 253*9162SPeter.Dunlap@Sun.COM * state machine associated with the connection. Remove the 254*9162SPeter.Dunlap@Sun.COM * connection from the list right away then handle the remaining 255*9162SPeter.Dunlap@Sun.COM * work via the idm_global_taskq. 256*9162SPeter.Dunlap@Sun.COM */ 2577978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 2587978SPeter.Dunlap@Sun.COM list_remove(&idm.idm_ini_conn_list, ic); 2597978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 2607978SPeter.Dunlap@Sun.COM 261*9162SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 262*9162SPeter.Dunlap@Sun.COM &idm_ini_conn_destroy_task, ic, TQ_SLEEP) == NULL) { 263*9162SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 264*9162SPeter.Dunlap@Sun.COM "idm_ini_conn_destroy: Couldn't dispatch task"); 265*9162SPeter.Dunlap@Sun.COM } 2667978SPeter.Dunlap@Sun.COM } 2677978SPeter.Dunlap@Sun.COM 2687978SPeter.Dunlap@Sun.COM /* 2697978SPeter.Dunlap@Sun.COM * idm_ini_conn_connect 2707978SPeter.Dunlap@Sun.COM * 2717978SPeter.Dunlap@Sun.COM * Establish connection to the remote system identified in idm_conn_t. 2727978SPeter.Dunlap@Sun.COM * The connection parameters including the remote IP address were established 273*9162SPeter.Dunlap@Sun.COM * in the call to idm_ini_conn_create. The IDM state machine will 274*9162SPeter.Dunlap@Sun.COM * perform client notifications as necessary to prompt the initiator through 275*9162SPeter.Dunlap@Sun.COM * the login process. IDM also keeps a timer running so that if the login 276*9162SPeter.Dunlap@Sun.COM * process doesn't complete in a timely manner it will fail. 2777978SPeter.Dunlap@Sun.COM * 2787978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 2797978SPeter.Dunlap@Sun.COM * 2807978SPeter.Dunlap@Sun.COM * Returns success if the connection was established, otherwise some kind 2817978SPeter.Dunlap@Sun.COM * of meaningful error code. 2827978SPeter.Dunlap@Sun.COM * 283*9162SPeter.Dunlap@Sun.COM * Upon return the login has either failed or is loggin in (ffp) 2847978SPeter.Dunlap@Sun.COM */ 2857978SPeter.Dunlap@Sun.COM idm_status_t 2867978SPeter.Dunlap@Sun.COM idm_ini_conn_connect(idm_conn_t *ic) 2877978SPeter.Dunlap@Sun.COM { 288*9162SPeter.Dunlap@Sun.COM idm_status_t rc = IDM_STATUS_SUCCESS; 2897978SPeter.Dunlap@Sun.COM 2907978SPeter.Dunlap@Sun.COM rc = idm_conn_sm_init(ic); 2917978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 2927978SPeter.Dunlap@Sun.COM return (ic->ic_conn_sm_status); 2937978SPeter.Dunlap@Sun.COM } 294*9162SPeter.Dunlap@Sun.COM 295*9162SPeter.Dunlap@Sun.COM /* Hold connection until we return */ 296*9162SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 297*9162SPeter.Dunlap@Sun.COM 2987978SPeter.Dunlap@Sun.COM /* Kick state machine */ 2997978SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_CONNECT_REQ, NULL); 3007978SPeter.Dunlap@Sun.COM 3017978SPeter.Dunlap@Sun.COM /* Wait for login flag */ 3027978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 3037978SPeter.Dunlap@Sun.COM while (!(ic->ic_state_flags & CF_LOGIN_READY) && 3047978SPeter.Dunlap@Sun.COM !(ic->ic_state_flags & CF_ERROR)) { 3057978SPeter.Dunlap@Sun.COM cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex); 3067978SPeter.Dunlap@Sun.COM } 3077978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 3087978SPeter.Dunlap@Sun.COM 3097978SPeter.Dunlap@Sun.COM if (ic->ic_state_flags & CF_ERROR) { 3107978SPeter.Dunlap@Sun.COM /* ic->ic_conn_sm_status will contains failure status */ 311*9162SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 3127978SPeter.Dunlap@Sun.COM return (ic->ic_conn_sm_status); 3137978SPeter.Dunlap@Sun.COM } 3147978SPeter.Dunlap@Sun.COM 3157978SPeter.Dunlap@Sun.COM /* Ready to login */ 3167978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_state_flags & CF_LOGIN_READY); 3177978SPeter.Dunlap@Sun.COM (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL); 3187978SPeter.Dunlap@Sun.COM 319*9162SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 3207978SPeter.Dunlap@Sun.COM 321*9162SPeter.Dunlap@Sun.COM return (rc); 3227978SPeter.Dunlap@Sun.COM } 3237978SPeter.Dunlap@Sun.COM 3247978SPeter.Dunlap@Sun.COM /* 3257978SPeter.Dunlap@Sun.COM * idm_ini_conn_disconnect 3267978SPeter.Dunlap@Sun.COM * 3277978SPeter.Dunlap@Sun.COM * Forces a connection (previously established using idm_ini_conn_connect) 3287978SPeter.Dunlap@Sun.COM * to perform a controlled shutdown, cleaning up any outstanding requests. 3297978SPeter.Dunlap@Sun.COM * 3307978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 3317978SPeter.Dunlap@Sun.COM * 332*9162SPeter.Dunlap@Sun.COM * This is asynchronous and will return before the connection is properly 333*9162SPeter.Dunlap@Sun.COM * shutdown 3347978SPeter.Dunlap@Sun.COM */ 3357978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 3367978SPeter.Dunlap@Sun.COM void 3377978SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect(idm_conn_t *ic) 3387978SPeter.Dunlap@Sun.COM { 339*9162SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL); 340*9162SPeter.Dunlap@Sun.COM } 3417978SPeter.Dunlap@Sun.COM 342*9162SPeter.Dunlap@Sun.COM /* 343*9162SPeter.Dunlap@Sun.COM * idm_ini_conn_disconnect_wait 344*9162SPeter.Dunlap@Sun.COM * 345*9162SPeter.Dunlap@Sun.COM * Forces a connection (previously established using idm_ini_conn_connect) 346*9162SPeter.Dunlap@Sun.COM * to perform a controlled shutdown. Blocks until the connection is 347*9162SPeter.Dunlap@Sun.COM * disconnected. 348*9162SPeter.Dunlap@Sun.COM * 349*9162SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 350*9162SPeter.Dunlap@Sun.COM */ 351*9162SPeter.Dunlap@Sun.COM /* ARGSUSED */ 352*9162SPeter.Dunlap@Sun.COM void 353*9162SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect_sync(idm_conn_t *ic) 354*9162SPeter.Dunlap@Sun.COM { 355*9162SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 356*9162SPeter.Dunlap@Sun.COM if ((ic->ic_state != CS_S9_INIT_ERROR) && 357*9162SPeter.Dunlap@Sun.COM (ic->ic_state != CS_S11_COMPLETE)) { 358*9162SPeter.Dunlap@Sun.COM idm_conn_event_locked(ic, CE_TRANSPORT_FAIL, NULL, CT_NONE); 359*9162SPeter.Dunlap@Sun.COM while ((ic->ic_state != CS_S9_INIT_ERROR) && 360*9162SPeter.Dunlap@Sun.COM (ic->ic_state != CS_S11_COMPLETE)) 361*9162SPeter.Dunlap@Sun.COM cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex); 3627978SPeter.Dunlap@Sun.COM } 3637978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 3647978SPeter.Dunlap@Sun.COM } 3657978SPeter.Dunlap@Sun.COM 3667978SPeter.Dunlap@Sun.COM /* 3677978SPeter.Dunlap@Sun.COM * idm_tgt_svc_create 3687978SPeter.Dunlap@Sun.COM * 3697978SPeter.Dunlap@Sun.COM * The target calls this service to obtain a service context for each available 3707978SPeter.Dunlap@Sun.COM * transport, starting a service of each type related to the IP address and port 3717978SPeter.Dunlap@Sun.COM * passed. The idm_svc_req_t contains the service parameters. 3727978SPeter.Dunlap@Sun.COM */ 3737978SPeter.Dunlap@Sun.COM idm_status_t 3747978SPeter.Dunlap@Sun.COM idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc) 3757978SPeter.Dunlap@Sun.COM { 3767978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 3777978SPeter.Dunlap@Sun.COM idm_transport_t *it; 3787978SPeter.Dunlap@Sun.COM idm_svc_t *is; 3797978SPeter.Dunlap@Sun.COM int rc; 3807978SPeter.Dunlap@Sun.COM 3817978SPeter.Dunlap@Sun.COM *new_svc = NULL; 3827978SPeter.Dunlap@Sun.COM is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP); 3837978SPeter.Dunlap@Sun.COM 3847978SPeter.Dunlap@Sun.COM /* Initialize transport-agnostic components of the service handle */ 3857978SPeter.Dunlap@Sun.COM is->is_svc_req = *sr; 3867978SPeter.Dunlap@Sun.COM mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL); 3877978SPeter.Dunlap@Sun.COM cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL); 3887978SPeter.Dunlap@Sun.COM mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL); 3897978SPeter.Dunlap@Sun.COM cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL); 3907978SPeter.Dunlap@Sun.COM idm_refcnt_init(&is->is_refcnt, is); 3917978SPeter.Dunlap@Sun.COM 3927978SPeter.Dunlap@Sun.COM /* 3937978SPeter.Dunlap@Sun.COM * Make sure all available transports are setup. We call this now 3947978SPeter.Dunlap@Sun.COM * instead of at initialization time in case IB has become available 3957978SPeter.Dunlap@Sun.COM * since we started (hotplug, etc). 3967978SPeter.Dunlap@Sun.COM */ 3977978SPeter.Dunlap@Sun.COM idm_transport_setup(sr->sr_li); 3987978SPeter.Dunlap@Sun.COM 3997978SPeter.Dunlap@Sun.COM /* 4007978SPeter.Dunlap@Sun.COM * Loop through the transports, configuring the transport-specific 4017978SPeter.Dunlap@Sun.COM * components of each one. 4027978SPeter.Dunlap@Sun.COM */ 4037978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 4047978SPeter.Dunlap@Sun.COM 4057978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 4067978SPeter.Dunlap@Sun.COM /* 4077978SPeter.Dunlap@Sun.COM * If it_ops is NULL then the transport is unconfigured 4087978SPeter.Dunlap@Sun.COM * and we shouldn't try to start the service. 4097978SPeter.Dunlap@Sun.COM */ 4107978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 4117978SPeter.Dunlap@Sun.COM continue; 4127978SPeter.Dunlap@Sun.COM } 4137978SPeter.Dunlap@Sun.COM 4147978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_tgt_svc_create(sr, is); 4157978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 4167978SPeter.Dunlap@Sun.COM /* Teardown any configured services */ 4177978SPeter.Dunlap@Sun.COM while (type--) { 4187978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 4197978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 4207978SPeter.Dunlap@Sun.COM continue; 4217978SPeter.Dunlap@Sun.COM } 4227978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_destroy(is); 4237978SPeter.Dunlap@Sun.COM } 4247978SPeter.Dunlap@Sun.COM /* Free the svc context and return */ 4257978SPeter.Dunlap@Sun.COM kmem_free(is, sizeof (idm_svc_t)); 4267978SPeter.Dunlap@Sun.COM return (rc); 4277978SPeter.Dunlap@Sun.COM } 4287978SPeter.Dunlap@Sun.COM } 4297978SPeter.Dunlap@Sun.COM 4307978SPeter.Dunlap@Sun.COM *new_svc = is; 4317978SPeter.Dunlap@Sun.COM 4327978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 4337978SPeter.Dunlap@Sun.COM list_insert_tail(&idm.idm_tgt_svc_list, is); 4347978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 4357978SPeter.Dunlap@Sun.COM 4367978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 4377978SPeter.Dunlap@Sun.COM } 4387978SPeter.Dunlap@Sun.COM 4397978SPeter.Dunlap@Sun.COM /* 4407978SPeter.Dunlap@Sun.COM * idm_tgt_svc_destroy 4417978SPeter.Dunlap@Sun.COM * 4427978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 4437978SPeter.Dunlap@Sun.COM * 4447978SPeter.Dunlap@Sun.COM * Cleanup any resources associated with the idm_svc_t. 4457978SPeter.Dunlap@Sun.COM */ 4467978SPeter.Dunlap@Sun.COM void 4477978SPeter.Dunlap@Sun.COM idm_tgt_svc_destroy(idm_svc_t *is) 4487978SPeter.Dunlap@Sun.COM { 4497978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 4507978SPeter.Dunlap@Sun.COM idm_transport_t *it; 4517978SPeter.Dunlap@Sun.COM 4527978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 4537978SPeter.Dunlap@Sun.COM /* remove this service from the global list */ 4547978SPeter.Dunlap@Sun.COM list_remove(&idm.idm_tgt_svc_list, is); 4557978SPeter.Dunlap@Sun.COM /* wakeup any waiters for service change */ 4567978SPeter.Dunlap@Sun.COM cv_broadcast(&idm.idm_tgt_svc_cv); 4577978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 4587978SPeter.Dunlap@Sun.COM 4597978SPeter.Dunlap@Sun.COM /* teardown each transport-specific service */ 4607978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 4617978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 4627978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 4637978SPeter.Dunlap@Sun.COM continue; 4647978SPeter.Dunlap@Sun.COM } 4657978SPeter.Dunlap@Sun.COM 4667978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_destroy(is); 4677978SPeter.Dunlap@Sun.COM } 4687978SPeter.Dunlap@Sun.COM 469*9162SPeter.Dunlap@Sun.COM /* tear down the svc resources */ 470*9162SPeter.Dunlap@Sun.COM idm_refcnt_destroy(&is->is_refcnt); 471*9162SPeter.Dunlap@Sun.COM cv_destroy(&is->is_count_cv); 472*9162SPeter.Dunlap@Sun.COM mutex_destroy(&is->is_count_mutex); 473*9162SPeter.Dunlap@Sun.COM cv_destroy(&is->is_cv); 474*9162SPeter.Dunlap@Sun.COM mutex_destroy(&is->is_mutex); 475*9162SPeter.Dunlap@Sun.COM 4767978SPeter.Dunlap@Sun.COM /* free the svc handle */ 4777978SPeter.Dunlap@Sun.COM kmem_free(is, sizeof (idm_svc_t)); 4787978SPeter.Dunlap@Sun.COM } 4797978SPeter.Dunlap@Sun.COM 4807978SPeter.Dunlap@Sun.COM void 4817978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(idm_svc_t *is) 4827978SPeter.Dunlap@Sun.COM { 4837978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&is->is_refcnt); 4847978SPeter.Dunlap@Sun.COM } 4857978SPeter.Dunlap@Sun.COM 4867978SPeter.Dunlap@Sun.COM void 4877978SPeter.Dunlap@Sun.COM idm_tgt_svc_rele_and_destroy(idm_svc_t *is) 4887978SPeter.Dunlap@Sun.COM { 4897978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(&is->is_refcnt, 4907978SPeter.Dunlap@Sun.COM (idm_refcnt_cb_t *)&idm_tgt_svc_destroy); 4917978SPeter.Dunlap@Sun.COM } 4927978SPeter.Dunlap@Sun.COM 4937978SPeter.Dunlap@Sun.COM /* 4947978SPeter.Dunlap@Sun.COM * idm_tgt_svc_online 4957978SPeter.Dunlap@Sun.COM * 4967978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 4977978SPeter.Dunlap@Sun.COM * 4987978SPeter.Dunlap@Sun.COM * Online each transport service, as we want this target to be accessible 4997978SPeter.Dunlap@Sun.COM * via any configured transport. 5007978SPeter.Dunlap@Sun.COM * 5017978SPeter.Dunlap@Sun.COM * When the initiator establishes a new connection to the target, IDM will 5027978SPeter.Dunlap@Sun.COM * call the "new connect" callback defined in the idm_svc_req_t structure 5037978SPeter.Dunlap@Sun.COM * and it will pass an idm_conn_t structure representing that new connection. 5047978SPeter.Dunlap@Sun.COM */ 5057978SPeter.Dunlap@Sun.COM idm_status_t 5067978SPeter.Dunlap@Sun.COM idm_tgt_svc_online(idm_svc_t *is) 5077978SPeter.Dunlap@Sun.COM { 5087978SPeter.Dunlap@Sun.COM 509*9162SPeter.Dunlap@Sun.COM idm_transport_type_t type, last_type; 5107978SPeter.Dunlap@Sun.COM idm_transport_t *it; 511*9162SPeter.Dunlap@Sun.COM int rc = IDM_STATUS_SUCCESS; 5127978SPeter.Dunlap@Sun.COM 5137978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 5147978SPeter.Dunlap@Sun.COM if (is->is_online == 0) { 515*9162SPeter.Dunlap@Sun.COM /* Walk through each of the transports and online them */ 5167978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 5177978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 5187978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 5197978SPeter.Dunlap@Sun.COM /* transport is not registered */ 5207978SPeter.Dunlap@Sun.COM continue; 5217978SPeter.Dunlap@Sun.COM } 5227978SPeter.Dunlap@Sun.COM 5237978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5247978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_tgt_svc_online(is); 5257978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 526*9162SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 527*9162SPeter.Dunlap@Sun.COM last_type = type; 528*9162SPeter.Dunlap@Sun.COM break; 5297978SPeter.Dunlap@Sun.COM } 5307978SPeter.Dunlap@Sun.COM } 531*9162SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 532*9162SPeter.Dunlap@Sun.COM /* 533*9162SPeter.Dunlap@Sun.COM * The last transport failed to online. 534*9162SPeter.Dunlap@Sun.COM * Offline any transport onlined above and 535*9162SPeter.Dunlap@Sun.COM * do not online the target. 536*9162SPeter.Dunlap@Sun.COM */ 537*9162SPeter.Dunlap@Sun.COM for (type = 0; type < last_type; type++) { 538*9162SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 539*9162SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 540*9162SPeter.Dunlap@Sun.COM /* transport is not registered */ 541*9162SPeter.Dunlap@Sun.COM continue; 542*9162SPeter.Dunlap@Sun.COM } 543*9162SPeter.Dunlap@Sun.COM 544*9162SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 545*9162SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_offline(is); 546*9162SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 547*9162SPeter.Dunlap@Sun.COM } 548*9162SPeter.Dunlap@Sun.COM } else { 549*9162SPeter.Dunlap@Sun.COM /* Target service now online */ 550*9162SPeter.Dunlap@Sun.COM is->is_online = 1; 551*9162SPeter.Dunlap@Sun.COM } 5527978SPeter.Dunlap@Sun.COM } else { 553*9162SPeter.Dunlap@Sun.COM /* Target service already online, just bump the count */ 554*9162SPeter.Dunlap@Sun.COM is->is_online++; 5557978SPeter.Dunlap@Sun.COM } 5567978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5577978SPeter.Dunlap@Sun.COM 558*9162SPeter.Dunlap@Sun.COM return (rc); 5597978SPeter.Dunlap@Sun.COM } 5607978SPeter.Dunlap@Sun.COM 5617978SPeter.Dunlap@Sun.COM /* 5627978SPeter.Dunlap@Sun.COM * idm_tgt_svc_offline 5637978SPeter.Dunlap@Sun.COM * 5647978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 5657978SPeter.Dunlap@Sun.COM * 5667978SPeter.Dunlap@Sun.COM * Shutdown any online target services. 5677978SPeter.Dunlap@Sun.COM */ 5687978SPeter.Dunlap@Sun.COM void 5697978SPeter.Dunlap@Sun.COM idm_tgt_svc_offline(idm_svc_t *is) 5707978SPeter.Dunlap@Sun.COM { 5717978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 5727978SPeter.Dunlap@Sun.COM idm_transport_t *it; 5737978SPeter.Dunlap@Sun.COM 5747978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 5757978SPeter.Dunlap@Sun.COM is->is_online--; 5767978SPeter.Dunlap@Sun.COM if (is->is_online == 0) { 5777978SPeter.Dunlap@Sun.COM /* Walk through each of the transports and offline them */ 5787978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 5797978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 5807978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 5817978SPeter.Dunlap@Sun.COM /* transport is not registered */ 5827978SPeter.Dunlap@Sun.COM continue; 5837978SPeter.Dunlap@Sun.COM } 5847978SPeter.Dunlap@Sun.COM 5857978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5867978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_offline(is); 5877978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 5887978SPeter.Dunlap@Sun.COM } 5897978SPeter.Dunlap@Sun.COM } 5907978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5917978SPeter.Dunlap@Sun.COM } 5927978SPeter.Dunlap@Sun.COM 5937978SPeter.Dunlap@Sun.COM /* 5947978SPeter.Dunlap@Sun.COM * idm_tgt_svc_lookup 5957978SPeter.Dunlap@Sun.COM * 5967978SPeter.Dunlap@Sun.COM * Lookup a service instance listening on the specified port 5977978SPeter.Dunlap@Sun.COM */ 5987978SPeter.Dunlap@Sun.COM 5997978SPeter.Dunlap@Sun.COM idm_svc_t * 6007978SPeter.Dunlap@Sun.COM idm_tgt_svc_lookup(uint16_t port) 6017978SPeter.Dunlap@Sun.COM { 6027978SPeter.Dunlap@Sun.COM idm_svc_t *result; 6037978SPeter.Dunlap@Sun.COM 6047978SPeter.Dunlap@Sun.COM retry: 6057978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 6067978SPeter.Dunlap@Sun.COM for (result = list_head(&idm.idm_tgt_svc_list); 6077978SPeter.Dunlap@Sun.COM result != NULL; 6087978SPeter.Dunlap@Sun.COM result = list_next(&idm.idm_tgt_svc_list, result)) { 6097978SPeter.Dunlap@Sun.COM if (result->is_svc_req.sr_port == port) { 6107978SPeter.Dunlap@Sun.COM if (result->is_online == 0) { 6117978SPeter.Dunlap@Sun.COM /* 6127978SPeter.Dunlap@Sun.COM * A service exists on this port, but it 6137978SPeter.Dunlap@Sun.COM * is going away, wait for it to cleanup. 6147978SPeter.Dunlap@Sun.COM */ 6157978SPeter.Dunlap@Sun.COM cv_wait(&idm.idm_tgt_svc_cv, 6167978SPeter.Dunlap@Sun.COM &idm.idm_global_mutex); 6177978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 6187978SPeter.Dunlap@Sun.COM goto retry; 6197978SPeter.Dunlap@Sun.COM } 6207978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(result); 6217978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 6227978SPeter.Dunlap@Sun.COM return (result); 6237978SPeter.Dunlap@Sun.COM } 6247978SPeter.Dunlap@Sun.COM } 6257978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 6267978SPeter.Dunlap@Sun.COM 6277978SPeter.Dunlap@Sun.COM return (NULL); 6287978SPeter.Dunlap@Sun.COM } 6297978SPeter.Dunlap@Sun.COM 6307978SPeter.Dunlap@Sun.COM /* 6317978SPeter.Dunlap@Sun.COM * idm_negotiate_key_values() 6327978SPeter.Dunlap@Sun.COM * Give IDM level a chance to negotiate any login parameters it should own. 6337978SPeter.Dunlap@Sun.COM * -- leave unhandled parameters alone on request_nvl 6347978SPeter.Dunlap@Sun.COM * -- move all handled parameters to response_nvl with an appropriate response 6357978SPeter.Dunlap@Sun.COM * -- also add an entry to negotiated_nvl for any accepted parameters 6367978SPeter.Dunlap@Sun.COM */ 6377978SPeter.Dunlap@Sun.COM kv_status_t 6387978SPeter.Dunlap@Sun.COM idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl, 6397978SPeter.Dunlap@Sun.COM nvlist_t *response_nvl, nvlist_t *negotiated_nvl) 6407978SPeter.Dunlap@Sun.COM { 6417978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_transport_ops != NULL); 6427978SPeter.Dunlap@Sun.COM return (ic->ic_transport_ops->it_negotiate_key_values(ic, 6437978SPeter.Dunlap@Sun.COM request_nvl, response_nvl, negotiated_nvl)); 6447978SPeter.Dunlap@Sun.COM } 6457978SPeter.Dunlap@Sun.COM 6467978SPeter.Dunlap@Sun.COM /* 6477978SPeter.Dunlap@Sun.COM * idm_notice_key_values() 6487978SPeter.Dunlap@Sun.COM * Activate at the IDM level any parameters that have been negotiated. 6497978SPeter.Dunlap@Sun.COM * Passes the set of key value pairs to the transport for activation. 6507978SPeter.Dunlap@Sun.COM * This will be invoked as the connection is entering full-feature mode. 6517978SPeter.Dunlap@Sun.COM */ 652*9162SPeter.Dunlap@Sun.COM void 6537978SPeter.Dunlap@Sun.COM idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl) 6547978SPeter.Dunlap@Sun.COM { 6557978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_transport_ops != NULL); 656*9162SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_notice_key_values(ic, negotiated_nvl); 6577978SPeter.Dunlap@Sun.COM } 6587978SPeter.Dunlap@Sun.COM 6597978SPeter.Dunlap@Sun.COM /* 6607978SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini 6617978SPeter.Dunlap@Sun.COM * 6627978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Put_Data' operational primitive. 6637978SPeter.Dunlap@Sun.COM * 6647978SPeter.Dunlap@Sun.COM * This function is invoked by a target iSCSI layer to request its local 6657978SPeter.Dunlap@Sun.COM * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer 6667978SPeter.Dunlap@Sun.COM * on the remote iSCSI node. The I/O buffer represented by 'idb' is 6677978SPeter.Dunlap@Sun.COM * transferred to the initiator associated with task 'idt'. The connection 6687978SPeter.Dunlap@Sun.COM * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS, 6697978SPeter.Dunlap@Sun.COM * and the callback (idb->idb_buf_cb) at transfer completion are 6707978SPeter.Dunlap@Sun.COM * provided as input. 6717978SPeter.Dunlap@Sun.COM * 6727978SPeter.Dunlap@Sun.COM * This data transfer takes place transparently to the remote iSCSI layer, 6737978SPeter.Dunlap@Sun.COM * i.e. without its participation. 6747978SPeter.Dunlap@Sun.COM * 6757978SPeter.Dunlap@Sun.COM * Using sockets, IDM implements the data transfer by segmenting the data 6767978SPeter.Dunlap@Sun.COM * buffer into appropriately sized iSCSI PDUs and transmitting them to the 6777978SPeter.Dunlap@Sun.COM * initiator. iSER performs the transfer using RDMA write. 6787978SPeter.Dunlap@Sun.COM * 6797978SPeter.Dunlap@Sun.COM */ 6807978SPeter.Dunlap@Sun.COM idm_status_t 6817978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb, 6827978SPeter.Dunlap@Sun.COM uint32_t offset, uint32_t xfer_len, 6837978SPeter.Dunlap@Sun.COM idm_buf_cb_t idb_buf_cb, void *cb_arg) 6847978SPeter.Dunlap@Sun.COM { 6857978SPeter.Dunlap@Sun.COM idm_status_t rc; 6867978SPeter.Dunlap@Sun.COM 6877978SPeter.Dunlap@Sun.COM idb->idb_bufoffset = offset; 6887978SPeter.Dunlap@Sun.COM idb->idb_xfer_len = xfer_len; 6897978SPeter.Dunlap@Sun.COM idb->idb_buf_cb = idb_buf_cb; 6907978SPeter.Dunlap@Sun.COM idb->idb_cb_arg = cb_arg; 691*9162SPeter.Dunlap@Sun.COM gethrestime(&idb->idb_xfer_start); 692*9162SPeter.Dunlap@Sun.COM 693*9162SPeter.Dunlap@Sun.COM /* 694*9162SPeter.Dunlap@Sun.COM * Buffer should not contain the pattern. If the pattern is 695*9162SPeter.Dunlap@Sun.COM * present then we've been asked to transmit initialized data 696*9162SPeter.Dunlap@Sun.COM */ 697*9162SPeter.Dunlap@Sun.COM IDM_BUFPAT_CHECK(idb, xfer_len, BP_CHECK_ASSERT); 6987978SPeter.Dunlap@Sun.COM 6997978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 7007978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 7017978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 7027978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_start++; 7037978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 7047978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, idb); 7057978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_TRUE; 7067978SPeter.Dunlap@Sun.COM rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini) 7077978SPeter.Dunlap@Sun.COM (idt, idb); 7087978SPeter.Dunlap@Sun.COM return (rc); 7097978SPeter.Dunlap@Sun.COM 7107978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 7117978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 7127978SPeter.Dunlap@Sun.COM /* 7137978SPeter.Dunlap@Sun.COM * Bind buffer but don't start a transfer since the task 7147978SPeter.Dunlap@Sun.COM * is suspended 7157978SPeter.Dunlap@Sun.COM */ 7167978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, idb); 7177978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7187978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 7197978SPeter.Dunlap@Sun.COM 7207978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 7217978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 7227978SPeter.Dunlap@Sun.COM /* 7237978SPeter.Dunlap@Sun.COM * Once the task is aborted, any buffers added to the 7247978SPeter.Dunlap@Sun.COM * idt_inbufv will never get cleaned up, so just return 7257978SPeter.Dunlap@Sun.COM * SUCCESS. The buffer should get cleaned up by the 7267978SPeter.Dunlap@Sun.COM * client or framework once task_aborted has completed. 7277978SPeter.Dunlap@Sun.COM */ 7287978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7297978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 7307978SPeter.Dunlap@Sun.COM 7317978SPeter.Dunlap@Sun.COM default: 7327978SPeter.Dunlap@Sun.COM ASSERT(0); 7337978SPeter.Dunlap@Sun.COM break; 7347978SPeter.Dunlap@Sun.COM } 7357978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7367978SPeter.Dunlap@Sun.COM 7377978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7387978SPeter.Dunlap@Sun.COM } 7397978SPeter.Dunlap@Sun.COM 7407978SPeter.Dunlap@Sun.COM /* 7417978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini 7427978SPeter.Dunlap@Sun.COM * 7437978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Get_Data' operational primitive. 7447978SPeter.Dunlap@Sun.COM * 7457978SPeter.Dunlap@Sun.COM * This function is invoked by a target iSCSI layer to request its local 7467978SPeter.Dunlap@Sun.COM * Datamover layer to retrieve certain data identified by the R2T PDU from the 7477978SPeter.Dunlap@Sun.COM * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be 7487978SPeter.Dunlap@Sun.COM * mapped to the respective buffer by the task tags (ITT & TTT). 7497978SPeter.Dunlap@Sun.COM * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and 7507978SPeter.Dunlap@Sun.COM * the callback (idb->idb_buf_cb) notification for data transfer completion are 7517978SPeter.Dunlap@Sun.COM * are provided as input. 7527978SPeter.Dunlap@Sun.COM * 7537978SPeter.Dunlap@Sun.COM * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local 7547978SPeter.Dunlap@Sun.COM * Datamover layer, the local and remote Datamover layers transparently bring 7557978SPeter.Dunlap@Sun.COM * about the data transfer requested by the R2T PDU, without the participation 7567978SPeter.Dunlap@Sun.COM * of the iSCSI layers. 7577978SPeter.Dunlap@Sun.COM * 7587978SPeter.Dunlap@Sun.COM * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out() 7597978SPeter.Dunlap@Sun.COM * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read. 7607978SPeter.Dunlap@Sun.COM * 7617978SPeter.Dunlap@Sun.COM */ 7627978SPeter.Dunlap@Sun.COM idm_status_t 7637978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb, 7647978SPeter.Dunlap@Sun.COM uint32_t offset, uint32_t xfer_len, 7657978SPeter.Dunlap@Sun.COM idm_buf_cb_t idb_buf_cb, void *cb_arg) 7667978SPeter.Dunlap@Sun.COM { 7677978SPeter.Dunlap@Sun.COM idm_status_t rc; 7687978SPeter.Dunlap@Sun.COM 7697978SPeter.Dunlap@Sun.COM idb->idb_bufoffset = offset; 7707978SPeter.Dunlap@Sun.COM idb->idb_xfer_len = xfer_len; 7717978SPeter.Dunlap@Sun.COM idb->idb_buf_cb = idb_buf_cb; 7727978SPeter.Dunlap@Sun.COM idb->idb_cb_arg = cb_arg; 773*9162SPeter.Dunlap@Sun.COM gethrestime(&idb->idb_xfer_start); 7747978SPeter.Dunlap@Sun.COM 7757978SPeter.Dunlap@Sun.COM /* 7767978SPeter.Dunlap@Sun.COM * "In" buf list is for "Data In" PDU's, "Out" buf list is for 7777978SPeter.Dunlap@Sun.COM * "Data Out" PDU's 7787978SPeter.Dunlap@Sun.COM */ 7797978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 7807978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 7817978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 7827978SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_start++; 7837978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 7847978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, idb); 7857978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_TRUE; 7867978SPeter.Dunlap@Sun.COM rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini) 7877978SPeter.Dunlap@Sun.COM (idt, idb); 7887978SPeter.Dunlap@Sun.COM return (rc); 7897978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 7907978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 7917978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 7927978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 7937978SPeter.Dunlap@Sun.COM /* 7947978SPeter.Dunlap@Sun.COM * Bind buffer but don't start a transfer since the task 7957978SPeter.Dunlap@Sun.COM * is suspended 7967978SPeter.Dunlap@Sun.COM */ 7977978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, idb); 7987978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7997978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 8007978SPeter.Dunlap@Sun.COM default: 8017978SPeter.Dunlap@Sun.COM ASSERT(0); 8027978SPeter.Dunlap@Sun.COM break; 8037978SPeter.Dunlap@Sun.COM } 8047978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8057978SPeter.Dunlap@Sun.COM 8067978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 8077978SPeter.Dunlap@Sun.COM } 8087978SPeter.Dunlap@Sun.COM 8097978SPeter.Dunlap@Sun.COM /* 8107978SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini_done 8117978SPeter.Dunlap@Sun.COM * 8127978SPeter.Dunlap@Sun.COM * The transport calls this after it has completed a transfer requested by 8137978SPeter.Dunlap@Sun.COM * a call to transport_buf_tx_to_ini 8147978SPeter.Dunlap@Sun.COM * 8157978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning. 8167978SPeter.Dunlap@Sun.COM * idt may be freed after the call to idb->idb_buf_cb. 8177978SPeter.Dunlap@Sun.COM */ 8187978SPeter.Dunlap@Sun.COM void 8197978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status) 8207978SPeter.Dunlap@Sun.COM { 8217978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 8227978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_FALSE; 8237978SPeter.Dunlap@Sun.COM idb->idb_tx_thread = B_FALSE; 8247978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_done++; 825*9162SPeter.Dunlap@Sun.COM gethrestime(&idb->idb_xfer_done); 8267978SPeter.Dunlap@Sun.COM 8277978SPeter.Dunlap@Sun.COM /* 8287978SPeter.Dunlap@Sun.COM * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or 8297978SPeter.Dunlap@Sun.COM * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes 8307978SPeter.Dunlap@Sun.COM * to 0. 8317978SPeter.Dunlap@Sun.COM */ 8327978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 8337978SPeter.Dunlap@Sun.COM idb->idb_status = status; 8347978SPeter.Dunlap@Sun.COM 8357978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 8367978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 8377978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, idb); 8387978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8397978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, status); 8407978SPeter.Dunlap@Sun.COM return; 8417978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 8427978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 8437978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 8447978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 8457978SPeter.Dunlap@Sun.COM /* 8467978SPeter.Dunlap@Sun.COM * To keep things simple we will ignore the case where the 8477978SPeter.Dunlap@Sun.COM * transfer was successful and leave all buffers bound to the 8487978SPeter.Dunlap@Sun.COM * task. This allows us to also ignore the case where we've 8497978SPeter.Dunlap@Sun.COM * been asked to abort a task but the last transfer of the 8507978SPeter.Dunlap@Sun.COM * task has completed. IDM has no idea whether this was, in 8517978SPeter.Dunlap@Sun.COM * fact, the last transfer of the task so it would be difficult 8527978SPeter.Dunlap@Sun.COM * to handle this case. Everything should get sorted out again 8537978SPeter.Dunlap@Sun.COM * after task reassignment is complete. 8547978SPeter.Dunlap@Sun.COM * 8557978SPeter.Dunlap@Sun.COM * In the case of TASK_ABORTING we could conceivably call the 8567978SPeter.Dunlap@Sun.COM * buffer callback here but the timing of when the client's 8577978SPeter.Dunlap@Sun.COM * client_task_aborted callback is invoked vs. when the client's 8587978SPeter.Dunlap@Sun.COM * buffer callback gets invoked gets sticky. We don't want 8597978SPeter.Dunlap@Sun.COM * the client to here from us again after the call to 8607978SPeter.Dunlap@Sun.COM * client_task_aborted() but we don't want to give it a bunch 8617978SPeter.Dunlap@Sun.COM * of failed buffer transfers until we've called 8627978SPeter.Dunlap@Sun.COM * client_task_aborted(). Instead we'll just leave all the 8637978SPeter.Dunlap@Sun.COM * buffers bound and allow the client to cleanup. 8647978SPeter.Dunlap@Sun.COM */ 8657978SPeter.Dunlap@Sun.COM break; 8667978SPeter.Dunlap@Sun.COM default: 8677978SPeter.Dunlap@Sun.COM ASSERT(0); 8687978SPeter.Dunlap@Sun.COM } 8697978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8707978SPeter.Dunlap@Sun.COM } 8717978SPeter.Dunlap@Sun.COM 8727978SPeter.Dunlap@Sun.COM /* 8737978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini_done 8747978SPeter.Dunlap@Sun.COM * 8757978SPeter.Dunlap@Sun.COM * The transport calls this after it has completed a transfer requested by 8767978SPeter.Dunlap@Sun.COM * a call totransport_buf_tx_to_ini 8777978SPeter.Dunlap@Sun.COM * 8787978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning. 8797978SPeter.Dunlap@Sun.COM * idt may be freed after the call to idb->idb_buf_cb. 8807978SPeter.Dunlap@Sun.COM */ 8817978SPeter.Dunlap@Sun.COM void 8827978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status) 8837978SPeter.Dunlap@Sun.COM { 8847978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 8857978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_FALSE; 8867978SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_done++; 887*9162SPeter.Dunlap@Sun.COM gethrestime(&idb->idb_xfer_done); 8887978SPeter.Dunlap@Sun.COM 8897978SPeter.Dunlap@Sun.COM /* 8907978SPeter.Dunlap@Sun.COM * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or 8917978SPeter.Dunlap@Sun.COM * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes 8927978SPeter.Dunlap@Sun.COM * to 0. 8937978SPeter.Dunlap@Sun.COM */ 8947978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 8957978SPeter.Dunlap@Sun.COM idb->idb_status = status; 8967978SPeter.Dunlap@Sun.COM 897*9162SPeter.Dunlap@Sun.COM if (status == IDM_STATUS_SUCCESS) { 898*9162SPeter.Dunlap@Sun.COM /* 899*9162SPeter.Dunlap@Sun.COM * Buffer should not contain the pattern. If it does then 900*9162SPeter.Dunlap@Sun.COM * we did not get the data from the remote host. 901*9162SPeter.Dunlap@Sun.COM */ 902*9162SPeter.Dunlap@Sun.COM IDM_BUFPAT_CHECK(idb, idb->idb_xfer_len, BP_CHECK_ASSERT); 903*9162SPeter.Dunlap@Sun.COM } 904*9162SPeter.Dunlap@Sun.COM 9057978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 9067978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 9077978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, idb); 9087978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 9097978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, status); 9107978SPeter.Dunlap@Sun.COM return; 9117978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 9127978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 9137978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 9147978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 9157978SPeter.Dunlap@Sun.COM /* 9167978SPeter.Dunlap@Sun.COM * To keep things simple we will ignore the case where the 9177978SPeter.Dunlap@Sun.COM * transfer was successful and leave all buffers bound to the 9187978SPeter.Dunlap@Sun.COM * task. This allows us to also ignore the case where we've 9197978SPeter.Dunlap@Sun.COM * been asked to abort a task but the last transfer of the 9207978SPeter.Dunlap@Sun.COM * task has completed. IDM has no idea whether this was, in 9217978SPeter.Dunlap@Sun.COM * fact, the last transfer of the task so it would be difficult 9227978SPeter.Dunlap@Sun.COM * to handle this case. Everything should get sorted out again 9237978SPeter.Dunlap@Sun.COM * after task reassignment is complete. 9247978SPeter.Dunlap@Sun.COM * 9257978SPeter.Dunlap@Sun.COM * In the case of TASK_ABORTING we could conceivably call the 9267978SPeter.Dunlap@Sun.COM * buffer callback here but the timing of when the client's 9277978SPeter.Dunlap@Sun.COM * client_task_aborted callback is invoked vs. when the client's 9287978SPeter.Dunlap@Sun.COM * buffer callback gets invoked gets sticky. We don't want 9297978SPeter.Dunlap@Sun.COM * the client to here from us again after the call to 9307978SPeter.Dunlap@Sun.COM * client_task_aborted() but we don't want to give it a bunch 9317978SPeter.Dunlap@Sun.COM * of failed buffer transfers until we've called 9327978SPeter.Dunlap@Sun.COM * client_task_aborted(). Instead we'll just leave all the 9337978SPeter.Dunlap@Sun.COM * buffers bound and allow the client to cleanup. 9347978SPeter.Dunlap@Sun.COM */ 9357978SPeter.Dunlap@Sun.COM break; 9367978SPeter.Dunlap@Sun.COM default: 9377978SPeter.Dunlap@Sun.COM ASSERT(0); 9387978SPeter.Dunlap@Sun.COM } 9397978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 9407978SPeter.Dunlap@Sun.COM } 9417978SPeter.Dunlap@Sun.COM 9427978SPeter.Dunlap@Sun.COM /* 9437978SPeter.Dunlap@Sun.COM * idm_buf_alloc 9447978SPeter.Dunlap@Sun.COM * 9457978SPeter.Dunlap@Sun.COM * Allocates a buffer handle and registers it for use with the transport 9467978SPeter.Dunlap@Sun.COM * layer. If a buffer is not passed on bufptr, the buffer will be allocated 9477978SPeter.Dunlap@Sun.COM * as well as the handle. 9487978SPeter.Dunlap@Sun.COM * 9497978SPeter.Dunlap@Sun.COM * ic - connection on which the buffer will be transferred 9507978SPeter.Dunlap@Sun.COM * bufptr - allocate memory for buffer if NULL, else assign to buffer 9517978SPeter.Dunlap@Sun.COM * buflen - length of buffer 9527978SPeter.Dunlap@Sun.COM * 9537978SPeter.Dunlap@Sun.COM * Returns idm_buf_t handle if successful, otherwise NULL 9547978SPeter.Dunlap@Sun.COM */ 9557978SPeter.Dunlap@Sun.COM idm_buf_t * 9567978SPeter.Dunlap@Sun.COM idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen) 9577978SPeter.Dunlap@Sun.COM { 9587978SPeter.Dunlap@Sun.COM idm_buf_t *buf = NULL; 9597978SPeter.Dunlap@Sun.COM int rc; 9607978SPeter.Dunlap@Sun.COM 9617978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 9627978SPeter.Dunlap@Sun.COM ASSERT(idm.idm_buf_cache != NULL); 9637978SPeter.Dunlap@Sun.COM ASSERT(buflen > 0); 9647978SPeter.Dunlap@Sun.COM 9657978SPeter.Dunlap@Sun.COM /* Don't allocate new buffers if we are not in FFP */ 9667978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 9677978SPeter.Dunlap@Sun.COM if (!ic->ic_ffp) { 9687978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 9697978SPeter.Dunlap@Sun.COM return (NULL); 9707978SPeter.Dunlap@Sun.COM } 9717978SPeter.Dunlap@Sun.COM 9727978SPeter.Dunlap@Sun.COM 9737978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 9747978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 9757978SPeter.Dunlap@Sun.COM 9767978SPeter.Dunlap@Sun.COM buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP); 9777978SPeter.Dunlap@Sun.COM if (buf == NULL) { 9787978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 9797978SPeter.Dunlap@Sun.COM return (NULL); 9807978SPeter.Dunlap@Sun.COM } 9817978SPeter.Dunlap@Sun.COM 9827978SPeter.Dunlap@Sun.COM buf->idb_ic = ic; 9837978SPeter.Dunlap@Sun.COM buf->idb_buflen = buflen; 9847978SPeter.Dunlap@Sun.COM buf->idb_exp_offset = 0; 9857978SPeter.Dunlap@Sun.COM buf->idb_bufoffset = 0; 9867978SPeter.Dunlap@Sun.COM buf->idb_xfer_len = 0; 9877978SPeter.Dunlap@Sun.COM buf->idb_magic = IDM_BUF_MAGIC; 988*9162SPeter.Dunlap@Sun.COM buf->idb_in_transport = B_FALSE; 989*9162SPeter.Dunlap@Sun.COM buf->idb_bufbcopy = B_FALSE; 9907978SPeter.Dunlap@Sun.COM 9917978SPeter.Dunlap@Sun.COM /* 9927978SPeter.Dunlap@Sun.COM * If bufptr is NULL, we have an implicit request to allocate 9937978SPeter.Dunlap@Sun.COM * memory for this IDM buffer handle and register it for use 9947978SPeter.Dunlap@Sun.COM * with the transport. To simplify this, and to give more freedom 9957978SPeter.Dunlap@Sun.COM * to the transport layer for it's own buffer management, both of 9967978SPeter.Dunlap@Sun.COM * these actions will take place in the transport layer. 9977978SPeter.Dunlap@Sun.COM * If bufptr is set, then the caller has allocated memory (or more 9987978SPeter.Dunlap@Sun.COM * likely it's been passed from an upper layer), and we need only 9997978SPeter.Dunlap@Sun.COM * register the buffer for use with the transport layer. 10007978SPeter.Dunlap@Sun.COM */ 10017978SPeter.Dunlap@Sun.COM if (bufptr == NULL) { 10027978SPeter.Dunlap@Sun.COM /* 10037978SPeter.Dunlap@Sun.COM * Allocate a buffer from the transport layer (which 10047978SPeter.Dunlap@Sun.COM * will also register the buffer for use). 10057978SPeter.Dunlap@Sun.COM */ 10067978SPeter.Dunlap@Sun.COM rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen); 10077978SPeter.Dunlap@Sun.COM if (rc != 0) { 10087978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 10097978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 10107978SPeter.Dunlap@Sun.COM return (NULL); 10117978SPeter.Dunlap@Sun.COM } 10127978SPeter.Dunlap@Sun.COM /* Set the bufalloc'd flag */ 10137978SPeter.Dunlap@Sun.COM buf->idb_bufalloc = B_TRUE; 10147978SPeter.Dunlap@Sun.COM } else { 10157978SPeter.Dunlap@Sun.COM /* 1016*9162SPeter.Dunlap@Sun.COM * For large transfers, Set the passed bufptr into 1017*9162SPeter.Dunlap@Sun.COM * the buf handle, and register the handle with the 1018*9162SPeter.Dunlap@Sun.COM * transport layer. As memory registration with the 1019*9162SPeter.Dunlap@Sun.COM * transport layer is a time/cpu intensive operation, 1020*9162SPeter.Dunlap@Sun.COM * for small transfers (up to a pre-defined bcopy 1021*9162SPeter.Dunlap@Sun.COM * threshold), use pre-registered memory buffers 1022*9162SPeter.Dunlap@Sun.COM * and bcopy data at the appropriate time. 10237978SPeter.Dunlap@Sun.COM */ 10247978SPeter.Dunlap@Sun.COM buf->idb_buf = bufptr; 10257978SPeter.Dunlap@Sun.COM 10267978SPeter.Dunlap@Sun.COM rc = ic->ic_transport_ops->it_buf_setup(buf); 10277978SPeter.Dunlap@Sun.COM if (rc != 0) { 10287978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 10297978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 10307978SPeter.Dunlap@Sun.COM return (NULL); 10317978SPeter.Dunlap@Sun.COM } 1032*9162SPeter.Dunlap@Sun.COM /* 1033*9162SPeter.Dunlap@Sun.COM * The transport layer is now expected to set the idb_bufalloc 1034*9162SPeter.Dunlap@Sun.COM * correctly to indicate if resources have been allocated. 1035*9162SPeter.Dunlap@Sun.COM */ 10367978SPeter.Dunlap@Sun.COM } 10377978SPeter.Dunlap@Sun.COM 1038*9162SPeter.Dunlap@Sun.COM IDM_BUFPAT_SET(buf); 1039*9162SPeter.Dunlap@Sun.COM 10407978SPeter.Dunlap@Sun.COM return (buf); 10417978SPeter.Dunlap@Sun.COM } 10427978SPeter.Dunlap@Sun.COM 10437978SPeter.Dunlap@Sun.COM /* 10447978SPeter.Dunlap@Sun.COM * idm_buf_free 10457978SPeter.Dunlap@Sun.COM * 10467978SPeter.Dunlap@Sun.COM * Release a buffer handle along with the associated buffer that was allocated 10477978SPeter.Dunlap@Sun.COM * or assigned with idm_buf_alloc 10487978SPeter.Dunlap@Sun.COM */ 10497978SPeter.Dunlap@Sun.COM void 10507978SPeter.Dunlap@Sun.COM idm_buf_free(idm_buf_t *buf) 10517978SPeter.Dunlap@Sun.COM { 10527978SPeter.Dunlap@Sun.COM idm_conn_t *ic = buf->idb_ic; 10537978SPeter.Dunlap@Sun.COM 10547978SPeter.Dunlap@Sun.COM 10557978SPeter.Dunlap@Sun.COM buf->idb_task_binding = NULL; 10567978SPeter.Dunlap@Sun.COM 10577978SPeter.Dunlap@Sun.COM if (buf->idb_bufalloc) { 10587978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_buf_free(buf); 10597978SPeter.Dunlap@Sun.COM } else { 10607978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_buf_teardown(buf); 10617978SPeter.Dunlap@Sun.COM } 10627978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 10637978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 10647978SPeter.Dunlap@Sun.COM } 10657978SPeter.Dunlap@Sun.COM 10667978SPeter.Dunlap@Sun.COM /* 10677978SPeter.Dunlap@Sun.COM * idm_buf_bind_in 10687978SPeter.Dunlap@Sun.COM * 10697978SPeter.Dunlap@Sun.COM * This function associates a buffer with a task. This is only for use by the 10707978SPeter.Dunlap@Sun.COM * iSCSI initiator that will have only one buffer per transfer direction 10717978SPeter.Dunlap@Sun.COM * 10727978SPeter.Dunlap@Sun.COM */ 10737978SPeter.Dunlap@Sun.COM void 10747978SPeter.Dunlap@Sun.COM idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf) 10757978SPeter.Dunlap@Sun.COM { 10767978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 10777978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, buf); 10787978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 10797978SPeter.Dunlap@Sun.COM } 10807978SPeter.Dunlap@Sun.COM 10817978SPeter.Dunlap@Sun.COM static void 10827978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf) 10837978SPeter.Dunlap@Sun.COM { 10847978SPeter.Dunlap@Sun.COM buf->idb_task_binding = idt; 10857978SPeter.Dunlap@Sun.COM buf->idb_ic = idt->idt_ic; 10867978SPeter.Dunlap@Sun.COM idm_listbuf_insert(&idt->idt_inbufv, buf); 10877978SPeter.Dunlap@Sun.COM } 10887978SPeter.Dunlap@Sun.COM 10897978SPeter.Dunlap@Sun.COM void 10907978SPeter.Dunlap@Sun.COM idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf) 10917978SPeter.Dunlap@Sun.COM { 1092*9162SPeter.Dunlap@Sun.COM /* 1093*9162SPeter.Dunlap@Sun.COM * For small transfers, the iSER transport delegates the IDM 1094*9162SPeter.Dunlap@Sun.COM * layer to bcopy the SCSI Write data for faster IOPS. 1095*9162SPeter.Dunlap@Sun.COM */ 1096*9162SPeter.Dunlap@Sun.COM if (buf->idb_bufbcopy == B_TRUE) { 1097*9162SPeter.Dunlap@Sun.COM 1098*9162SPeter.Dunlap@Sun.COM bcopy(buf->idb_bufptr, buf->idb_buf, buf->idb_buflen); 1099*9162SPeter.Dunlap@Sun.COM } 11007978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11017978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, buf); 11027978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 11037978SPeter.Dunlap@Sun.COM } 11047978SPeter.Dunlap@Sun.COM 11057978SPeter.Dunlap@Sun.COM static void 11067978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf) 11077978SPeter.Dunlap@Sun.COM { 11087978SPeter.Dunlap@Sun.COM buf->idb_task_binding = idt; 11097978SPeter.Dunlap@Sun.COM buf->idb_ic = idt->idt_ic; 11107978SPeter.Dunlap@Sun.COM idm_listbuf_insert(&idt->idt_outbufv, buf); 11117978SPeter.Dunlap@Sun.COM } 11127978SPeter.Dunlap@Sun.COM 11137978SPeter.Dunlap@Sun.COM void 11147978SPeter.Dunlap@Sun.COM idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf) 11157978SPeter.Dunlap@Sun.COM { 1116*9162SPeter.Dunlap@Sun.COM /* 1117*9162SPeter.Dunlap@Sun.COM * For small transfers, the iSER transport delegates the IDM 1118*9162SPeter.Dunlap@Sun.COM * layer to bcopy the SCSI Read data into the read buufer 1119*9162SPeter.Dunlap@Sun.COM * for faster IOPS. 1120*9162SPeter.Dunlap@Sun.COM */ 1121*9162SPeter.Dunlap@Sun.COM if (buf->idb_bufbcopy == B_TRUE) { 1122*9162SPeter.Dunlap@Sun.COM bcopy(buf->idb_buf, buf->idb_bufptr, buf->idb_buflen); 1123*9162SPeter.Dunlap@Sun.COM } 11247978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11257978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, buf); 11267978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 11277978SPeter.Dunlap@Sun.COM } 11287978SPeter.Dunlap@Sun.COM 11297978SPeter.Dunlap@Sun.COM static void 11307978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf) 11317978SPeter.Dunlap@Sun.COM { 11327978SPeter.Dunlap@Sun.COM list_remove(&idt->idt_inbufv, buf); 11337978SPeter.Dunlap@Sun.COM } 11347978SPeter.Dunlap@Sun.COM 11357978SPeter.Dunlap@Sun.COM void 11367978SPeter.Dunlap@Sun.COM idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf) 11377978SPeter.Dunlap@Sun.COM { 11387978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 11397978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, buf); 11407978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 11417978SPeter.Dunlap@Sun.COM } 11427978SPeter.Dunlap@Sun.COM 11437978SPeter.Dunlap@Sun.COM static void 11447978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf) 11457978SPeter.Dunlap@Sun.COM { 11467978SPeter.Dunlap@Sun.COM list_remove(&idt->idt_outbufv, buf); 11477978SPeter.Dunlap@Sun.COM } 11487978SPeter.Dunlap@Sun.COM 11497978SPeter.Dunlap@Sun.COM /* 11507978SPeter.Dunlap@Sun.COM * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the 11517978SPeter.Dunlap@Sun.COM * iSCSI PDU 11527978SPeter.Dunlap@Sun.COM */ 11537978SPeter.Dunlap@Sun.COM idm_buf_t * 11547978SPeter.Dunlap@Sun.COM idm_buf_find(void *lbuf, size_t data_offset) 11557978SPeter.Dunlap@Sun.COM { 11567978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 11577978SPeter.Dunlap@Sun.COM list_t *lst = (list_t *)lbuf; 11587978SPeter.Dunlap@Sun.COM 11597978SPeter.Dunlap@Sun.COM /* iterate through the list to find the buffer */ 11607978SPeter.Dunlap@Sun.COM for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) { 11617978SPeter.Dunlap@Sun.COM 11627978SPeter.Dunlap@Sun.COM ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) || 11637978SPeter.Dunlap@Sun.COM (idb->idb_bufoffset == 0)); 11647978SPeter.Dunlap@Sun.COM 11657978SPeter.Dunlap@Sun.COM if ((data_offset >= idb->idb_bufoffset) && 11667978SPeter.Dunlap@Sun.COM (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) { 11677978SPeter.Dunlap@Sun.COM 11687978SPeter.Dunlap@Sun.COM return (idb); 11697978SPeter.Dunlap@Sun.COM } 11707978SPeter.Dunlap@Sun.COM } 11717978SPeter.Dunlap@Sun.COM 11727978SPeter.Dunlap@Sun.COM return (NULL); 11737978SPeter.Dunlap@Sun.COM } 11747978SPeter.Dunlap@Sun.COM 1175*9162SPeter.Dunlap@Sun.COM void 1176*9162SPeter.Dunlap@Sun.COM idm_bufpat_set(idm_buf_t *idb) 1177*9162SPeter.Dunlap@Sun.COM { 1178*9162SPeter.Dunlap@Sun.COM idm_bufpat_t *bufpat; 1179*9162SPeter.Dunlap@Sun.COM int len, i; 1180*9162SPeter.Dunlap@Sun.COM 1181*9162SPeter.Dunlap@Sun.COM len = idb->idb_buflen; 1182*9162SPeter.Dunlap@Sun.COM len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t); 1183*9162SPeter.Dunlap@Sun.COM 1184*9162SPeter.Dunlap@Sun.COM bufpat = idb->idb_buf; 1185*9162SPeter.Dunlap@Sun.COM for (i = 0; i < len; i += sizeof (idm_bufpat_t)) { 1186*9162SPeter.Dunlap@Sun.COM bufpat->bufpat_idb = idb; 1187*9162SPeter.Dunlap@Sun.COM bufpat->bufpat_bufmagic = IDM_BUF_MAGIC; 1188*9162SPeter.Dunlap@Sun.COM bufpat->bufpat_offset = i; 1189*9162SPeter.Dunlap@Sun.COM bufpat++; 1190*9162SPeter.Dunlap@Sun.COM } 1191*9162SPeter.Dunlap@Sun.COM } 1192*9162SPeter.Dunlap@Sun.COM 1193*9162SPeter.Dunlap@Sun.COM boolean_t 1194*9162SPeter.Dunlap@Sun.COM idm_bufpat_check(idm_buf_t *idb, int check_len, idm_bufpat_check_type_t type) 1195*9162SPeter.Dunlap@Sun.COM { 1196*9162SPeter.Dunlap@Sun.COM idm_bufpat_t *bufpat; 1197*9162SPeter.Dunlap@Sun.COM int len, i; 1198*9162SPeter.Dunlap@Sun.COM 1199*9162SPeter.Dunlap@Sun.COM len = (type == BP_CHECK_QUICK) ? sizeof (idm_bufpat_t) : check_len; 1200*9162SPeter.Dunlap@Sun.COM len = (len / sizeof (idm_bufpat_t)) * sizeof (idm_bufpat_t); 1201*9162SPeter.Dunlap@Sun.COM ASSERT(len <= idb->idb_buflen); 1202*9162SPeter.Dunlap@Sun.COM bufpat = idb->idb_buf; 1203*9162SPeter.Dunlap@Sun.COM 1204*9162SPeter.Dunlap@Sun.COM /* 1205*9162SPeter.Dunlap@Sun.COM * Don't check the pattern in buffers that came from outside IDM 1206*9162SPeter.Dunlap@Sun.COM * (these will be buffers from the initiator that we opted not 1207*9162SPeter.Dunlap@Sun.COM * to double-buffer) 1208*9162SPeter.Dunlap@Sun.COM */ 1209*9162SPeter.Dunlap@Sun.COM if (!idb->idb_bufalloc) 1210*9162SPeter.Dunlap@Sun.COM return (B_FALSE); 1211*9162SPeter.Dunlap@Sun.COM 1212*9162SPeter.Dunlap@Sun.COM /* 1213*9162SPeter.Dunlap@Sun.COM * Return true if we find the pattern anywhere in the buffer 1214*9162SPeter.Dunlap@Sun.COM */ 1215*9162SPeter.Dunlap@Sun.COM for (i = 0; i < len; i += sizeof (idm_bufpat_t)) { 1216*9162SPeter.Dunlap@Sun.COM if (BUFPAT_MATCH(bufpat, idb)) { 1217*9162SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, "idm_bufpat_check found: " 1218*9162SPeter.Dunlap@Sun.COM "idb %p bufpat %p " 1219*9162SPeter.Dunlap@Sun.COM "bufpat_idb=%p bufmagic=%08x offset=%08x", 1220*9162SPeter.Dunlap@Sun.COM (void *)idb, (void *)bufpat, bufpat->bufpat_idb, 1221*9162SPeter.Dunlap@Sun.COM bufpat->bufpat_bufmagic, bufpat->bufpat_offset); 1222*9162SPeter.Dunlap@Sun.COM DTRACE_PROBE2(bufpat__pattern__found, 1223*9162SPeter.Dunlap@Sun.COM idm_buf_t *, idb, idm_bufpat_t *, bufpat); 1224*9162SPeter.Dunlap@Sun.COM if (type == BP_CHECK_ASSERT) { 1225*9162SPeter.Dunlap@Sun.COM ASSERT(0); 1226*9162SPeter.Dunlap@Sun.COM } 1227*9162SPeter.Dunlap@Sun.COM return (B_TRUE); 1228*9162SPeter.Dunlap@Sun.COM } 1229*9162SPeter.Dunlap@Sun.COM bufpat++; 1230*9162SPeter.Dunlap@Sun.COM } 1231*9162SPeter.Dunlap@Sun.COM 1232*9162SPeter.Dunlap@Sun.COM return (B_FALSE); 1233*9162SPeter.Dunlap@Sun.COM } 1234*9162SPeter.Dunlap@Sun.COM 12357978SPeter.Dunlap@Sun.COM /* 12367978SPeter.Dunlap@Sun.COM * idm_task_alloc 12377978SPeter.Dunlap@Sun.COM * 12387978SPeter.Dunlap@Sun.COM * This function will allocate a idm_task_t structure. A task tag is also 12397978SPeter.Dunlap@Sun.COM * generated and saved in idt_tt. The task is not active. 12407978SPeter.Dunlap@Sun.COM */ 12417978SPeter.Dunlap@Sun.COM idm_task_t * 12427978SPeter.Dunlap@Sun.COM idm_task_alloc(idm_conn_t *ic) 12437978SPeter.Dunlap@Sun.COM { 12447978SPeter.Dunlap@Sun.COM idm_task_t *idt; 12457978SPeter.Dunlap@Sun.COM 12467978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 12477978SPeter.Dunlap@Sun.COM 12487978SPeter.Dunlap@Sun.COM /* Don't allocate new tasks if we are not in FFP */ 12497978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 12507978SPeter.Dunlap@Sun.COM if (!ic->ic_ffp) { 12517978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 12527978SPeter.Dunlap@Sun.COM return (NULL); 12537978SPeter.Dunlap@Sun.COM } 12547978SPeter.Dunlap@Sun.COM idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP); 12557978SPeter.Dunlap@Sun.COM if (idt == NULL) { 12567978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 12577978SPeter.Dunlap@Sun.COM return (NULL); 12587978SPeter.Dunlap@Sun.COM } 12597978SPeter.Dunlap@Sun.COM 12607978SPeter.Dunlap@Sun.COM ASSERT(list_is_empty(&idt->idt_inbufv)); 12617978SPeter.Dunlap@Sun.COM ASSERT(list_is_empty(&idt->idt_outbufv)); 12627978SPeter.Dunlap@Sun.COM 12637978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 12647978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 12657978SPeter.Dunlap@Sun.COM 12667978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_IDLE; 12677978SPeter.Dunlap@Sun.COM idt->idt_ic = ic; 12687978SPeter.Dunlap@Sun.COM idt->idt_private = NULL; 12697978SPeter.Dunlap@Sun.COM idt->idt_exp_datasn = 0; 12707978SPeter.Dunlap@Sun.COM idt->idt_exp_rttsn = 0; 12717978SPeter.Dunlap@Sun.COM 12727978SPeter.Dunlap@Sun.COM return (idt); 12737978SPeter.Dunlap@Sun.COM } 12747978SPeter.Dunlap@Sun.COM 12757978SPeter.Dunlap@Sun.COM /* 12767978SPeter.Dunlap@Sun.COM * idm_task_start 12777978SPeter.Dunlap@Sun.COM * 1278*9162SPeter.Dunlap@Sun.COM * Mark the task active and initialize some stats. The caller 12797978SPeter.Dunlap@Sun.COM * sets up the idm_task_t structure with a prior call to idm_task_alloc(). 12807978SPeter.Dunlap@Sun.COM * The task service does not function as a task/work engine, it is the 12817978SPeter.Dunlap@Sun.COM * responsibility of the initiator to start the data transfer and free the 12827978SPeter.Dunlap@Sun.COM * resources. 12837978SPeter.Dunlap@Sun.COM */ 12847978SPeter.Dunlap@Sun.COM void 12857978SPeter.Dunlap@Sun.COM idm_task_start(idm_task_t *idt, uintptr_t handle) 12867978SPeter.Dunlap@Sun.COM { 12877978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 12887978SPeter.Dunlap@Sun.COM 12897978SPeter.Dunlap@Sun.COM /* mark the task as ACTIVE */ 12907978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ACTIVE; 12917978SPeter.Dunlap@Sun.COM idt->idt_client_handle = handle; 12927978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done = 1293*9162SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 1294*9162SPeter.Dunlap@Sun.COM idt->idt_tx_bytes = idt->idt_rx_bytes = 0; 12957978SPeter.Dunlap@Sun.COM } 12967978SPeter.Dunlap@Sun.COM 12977978SPeter.Dunlap@Sun.COM /* 12987978SPeter.Dunlap@Sun.COM * idm_task_done 12997978SPeter.Dunlap@Sun.COM * 1300*9162SPeter.Dunlap@Sun.COM * This function sets the state to indicate that the task is no longer active. 13017978SPeter.Dunlap@Sun.COM */ 13027978SPeter.Dunlap@Sun.COM void 13037978SPeter.Dunlap@Sun.COM idm_task_done(idm_task_t *idt) 13047978SPeter.Dunlap@Sun.COM { 13057978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 1306*9162SPeter.Dunlap@Sun.COM 1307*9162SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 1308*9162SPeter.Dunlap@Sun.COM idt->idt_state = TASK_IDLE; 1309*9162SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13107978SPeter.Dunlap@Sun.COM 1311*9162SPeter.Dunlap@Sun.COM /* 1312*9162SPeter.Dunlap@Sun.COM * Although unlikely it is possible for a reference to come in after 1313*9162SPeter.Dunlap@Sun.COM * the client has decided the task is over but before we've marked 1314*9162SPeter.Dunlap@Sun.COM * the task idle. One specific unavoidable scenario is the case where 1315*9162SPeter.Dunlap@Sun.COM * received PDU with the matching ITT/TTT results in a successful 1316*9162SPeter.Dunlap@Sun.COM * lookup of this task. We are at the mercy of the remote node in 1317*9162SPeter.Dunlap@Sun.COM * that case so we need to handle it. Now that the task state 1318*9162SPeter.Dunlap@Sun.COM * has changed no more references will occur so a simple call to 1319*9162SPeter.Dunlap@Sun.COM * idm_refcnt_wait_ref should deal with the situation. 1320*9162SPeter.Dunlap@Sun.COM */ 1321*9162SPeter.Dunlap@Sun.COM idm_refcnt_wait_ref(&idt->idt_refcnt); 13227978SPeter.Dunlap@Sun.COM idm_refcnt_reset(&idt->idt_refcnt); 13237978SPeter.Dunlap@Sun.COM } 13247978SPeter.Dunlap@Sun.COM 13257978SPeter.Dunlap@Sun.COM /* 13267978SPeter.Dunlap@Sun.COM * idm_task_free 13277978SPeter.Dunlap@Sun.COM * 13287978SPeter.Dunlap@Sun.COM * This function will free the Task Tag and the memory allocated for the task 13297978SPeter.Dunlap@Sun.COM * idm_task_done should be called prior to this call 13307978SPeter.Dunlap@Sun.COM */ 13317978SPeter.Dunlap@Sun.COM void 13327978SPeter.Dunlap@Sun.COM idm_task_free(idm_task_t *idt) 13337978SPeter.Dunlap@Sun.COM { 1334*9162SPeter.Dunlap@Sun.COM idm_conn_t *ic; 13357978SPeter.Dunlap@Sun.COM 13367978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 1337*9162SPeter.Dunlap@Sun.COM ASSERT(idt->idt_refcnt.ir_refcnt == 0); 13387978SPeter.Dunlap@Sun.COM ASSERT(idt->idt_state == TASK_IDLE); 13397978SPeter.Dunlap@Sun.COM 1340*9162SPeter.Dunlap@Sun.COM ic = idt->idt_ic; 1341*9162SPeter.Dunlap@Sun.COM 13427978SPeter.Dunlap@Sun.COM /* 13437978SPeter.Dunlap@Sun.COM * It's possible for items to still be in the idt_inbufv list if 13447978SPeter.Dunlap@Sun.COM * they were added after idm_task_cleanup was called. We rely on 13457978SPeter.Dunlap@Sun.COM * STMF to free all buffers associated with the task however STMF 13467978SPeter.Dunlap@Sun.COM * doesn't know that we have this reference to the buffers. 13477978SPeter.Dunlap@Sun.COM * Use list_create so that we don't end up with stale references 13487978SPeter.Dunlap@Sun.COM * to these buffers. 13497978SPeter.Dunlap@Sun.COM */ 13507978SPeter.Dunlap@Sun.COM list_create(&idt->idt_inbufv, sizeof (idm_buf_t), 13517978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 13527978SPeter.Dunlap@Sun.COM list_create(&idt->idt_outbufv, sizeof (idm_buf_t), 13537978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 13547978SPeter.Dunlap@Sun.COM 13557978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_task_cache, idt); 13567978SPeter.Dunlap@Sun.COM 13577978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 13587978SPeter.Dunlap@Sun.COM } 13597978SPeter.Dunlap@Sun.COM 13607978SPeter.Dunlap@Sun.COM /* 1361*9162SPeter.Dunlap@Sun.COM * idm_task_find_common 1362*9162SPeter.Dunlap@Sun.COM * common code for idm_task_find() and idm_task_find_and_complete() 13637978SPeter.Dunlap@Sun.COM */ 13647978SPeter.Dunlap@Sun.COM /*ARGSUSED*/ 1365*9162SPeter.Dunlap@Sun.COM static idm_task_t * 1366*9162SPeter.Dunlap@Sun.COM idm_task_find_common(idm_conn_t *ic, uint32_t itt, uint32_t ttt, 1367*9162SPeter.Dunlap@Sun.COM boolean_t complete) 13687978SPeter.Dunlap@Sun.COM { 13697978SPeter.Dunlap@Sun.COM uint32_t tt, client_handle; 13707978SPeter.Dunlap@Sun.COM idm_task_t *idt; 13717978SPeter.Dunlap@Sun.COM 13727978SPeter.Dunlap@Sun.COM /* 13737978SPeter.Dunlap@Sun.COM * Must match both itt and ttt. The table is indexed by itt 13747978SPeter.Dunlap@Sun.COM * for initiator connections and ttt for target connections. 13757978SPeter.Dunlap@Sun.COM */ 13767978SPeter.Dunlap@Sun.COM if (IDM_CONN_ISTGT(ic)) { 13777978SPeter.Dunlap@Sun.COM tt = ttt; 13787978SPeter.Dunlap@Sun.COM client_handle = itt; 13797978SPeter.Dunlap@Sun.COM } else { 13807978SPeter.Dunlap@Sun.COM tt = itt; 13817978SPeter.Dunlap@Sun.COM client_handle = ttt; 13827978SPeter.Dunlap@Sun.COM } 13837978SPeter.Dunlap@Sun.COM 13847978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 13857978SPeter.Dunlap@Sun.COM if (tt >= idm.idm_taskid_max) { 13867978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 13877978SPeter.Dunlap@Sun.COM return (NULL); 13887978SPeter.Dunlap@Sun.COM } 13897978SPeter.Dunlap@Sun.COM 13907978SPeter.Dunlap@Sun.COM idt = idm.idm_taskid_table[tt]; 13917978SPeter.Dunlap@Sun.COM 13927978SPeter.Dunlap@Sun.COM if (idt != NULL) { 13937978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 13947978SPeter.Dunlap@Sun.COM if ((idt->idt_state != TASK_ACTIVE) || 1395*9162SPeter.Dunlap@Sun.COM (idt->idt_ic != ic) || 13967978SPeter.Dunlap@Sun.COM (IDM_CONN_ISTGT(ic) && 13977978SPeter.Dunlap@Sun.COM (idt->idt_client_handle != client_handle))) { 13987978SPeter.Dunlap@Sun.COM /* 1399*9162SPeter.Dunlap@Sun.COM * Task doesn't match or task is aborting and 1400*9162SPeter.Dunlap@Sun.COM * we don't want any more references. 14017978SPeter.Dunlap@Sun.COM */ 1402*9162SPeter.Dunlap@Sun.COM if ((idt->idt_ic != ic) && 1403*9162SPeter.Dunlap@Sun.COM (idt->idt_state == TASK_ACTIVE) && 1404*9162SPeter.Dunlap@Sun.COM (IDM_CONN_ISINI(ic) || idt->idt_client_handle == 1405*9162SPeter.Dunlap@Sun.COM client_handle)) { 1406*9162SPeter.Dunlap@Sun.COM IDM_CONN_LOG(CE_WARN, 1407*9162SPeter.Dunlap@Sun.COM "idm_task_find: wrong connection %p != %p", 1408*9162SPeter.Dunlap@Sun.COM (void *)ic, (void *)idt->idt_ic); 1409*9162SPeter.Dunlap@Sun.COM } 14107978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14117978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 14127978SPeter.Dunlap@Sun.COM return (NULL); 14137978SPeter.Dunlap@Sun.COM } 14147978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 1415*9162SPeter.Dunlap@Sun.COM /* 1416*9162SPeter.Dunlap@Sun.COM * Set the task state to TASK_COMPLETE so it can no longer 1417*9162SPeter.Dunlap@Sun.COM * be found or aborted. 1418*9162SPeter.Dunlap@Sun.COM */ 1419*9162SPeter.Dunlap@Sun.COM if (B_TRUE == complete) 1420*9162SPeter.Dunlap@Sun.COM idt->idt_state = TASK_COMPLETE; 14217978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14227978SPeter.Dunlap@Sun.COM } 14237978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 14247978SPeter.Dunlap@Sun.COM 14257978SPeter.Dunlap@Sun.COM return (idt); 14267978SPeter.Dunlap@Sun.COM } 14277978SPeter.Dunlap@Sun.COM 14287978SPeter.Dunlap@Sun.COM /* 1429*9162SPeter.Dunlap@Sun.COM * This function looks up a task by task tag. 1430*9162SPeter.Dunlap@Sun.COM */ 1431*9162SPeter.Dunlap@Sun.COM idm_task_t * 1432*9162SPeter.Dunlap@Sun.COM idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt) 1433*9162SPeter.Dunlap@Sun.COM { 1434*9162SPeter.Dunlap@Sun.COM return (idm_task_find_common(ic, itt, ttt, B_FALSE)); 1435*9162SPeter.Dunlap@Sun.COM } 1436*9162SPeter.Dunlap@Sun.COM 1437*9162SPeter.Dunlap@Sun.COM /* 1438*9162SPeter.Dunlap@Sun.COM * This function looks up a task by task tag. If found, the task state 1439*9162SPeter.Dunlap@Sun.COM * is atomically set to TASK_COMPLETE so it can longer be found or aborted. 1440*9162SPeter.Dunlap@Sun.COM */ 1441*9162SPeter.Dunlap@Sun.COM idm_task_t * 1442*9162SPeter.Dunlap@Sun.COM idm_task_find_and_complete(idm_conn_t *ic, uint32_t itt, uint32_t ttt) 1443*9162SPeter.Dunlap@Sun.COM { 1444*9162SPeter.Dunlap@Sun.COM return (idm_task_find_common(ic, itt, ttt, B_TRUE)); 1445*9162SPeter.Dunlap@Sun.COM } 1446*9162SPeter.Dunlap@Sun.COM 1447*9162SPeter.Dunlap@Sun.COM /* 14487978SPeter.Dunlap@Sun.COM * idm_task_find_by_handle 14497978SPeter.Dunlap@Sun.COM * 14507978SPeter.Dunlap@Sun.COM * This function looks up a task by the client-private idt_client_handle. 14517978SPeter.Dunlap@Sun.COM * 14527978SPeter.Dunlap@Sun.COM * This function should NEVER be called in the performance path. It is 14537978SPeter.Dunlap@Sun.COM * intended strictly for error recovery/task management. 14547978SPeter.Dunlap@Sun.COM */ 14557978SPeter.Dunlap@Sun.COM /*ARGSUSED*/ 14567978SPeter.Dunlap@Sun.COM void * 14577978SPeter.Dunlap@Sun.COM idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle) 14587978SPeter.Dunlap@Sun.COM { 14597978SPeter.Dunlap@Sun.COM idm_task_t *idt = NULL; 14607978SPeter.Dunlap@Sun.COM int idx = 0; 14617978SPeter.Dunlap@Sun.COM 14627978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 14637978SPeter.Dunlap@Sun.COM 14647978SPeter.Dunlap@Sun.COM for (idx = 0; idx < idm.idm_taskid_max; idx++) { 14657978SPeter.Dunlap@Sun.COM idt = idm.idm_taskid_table[idx]; 14667978SPeter.Dunlap@Sun.COM 14677978SPeter.Dunlap@Sun.COM if (idt == NULL) 14687978SPeter.Dunlap@Sun.COM continue; 14697978SPeter.Dunlap@Sun.COM 14707978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 14717978SPeter.Dunlap@Sun.COM 14727978SPeter.Dunlap@Sun.COM if (idt->idt_state != TASK_ACTIVE) { 14737978SPeter.Dunlap@Sun.COM /* 14747978SPeter.Dunlap@Sun.COM * Task is either in suspend, abort, or already 14757978SPeter.Dunlap@Sun.COM * complete. 14767978SPeter.Dunlap@Sun.COM */ 14777978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14787978SPeter.Dunlap@Sun.COM continue; 14797978SPeter.Dunlap@Sun.COM } 14807978SPeter.Dunlap@Sun.COM 14817978SPeter.Dunlap@Sun.COM if (idt->idt_client_handle == handle) { 14827978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 14837978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14847978SPeter.Dunlap@Sun.COM break; 14857978SPeter.Dunlap@Sun.COM } 14867978SPeter.Dunlap@Sun.COM 14877978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14887978SPeter.Dunlap@Sun.COM } 14897978SPeter.Dunlap@Sun.COM 14907978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 14917978SPeter.Dunlap@Sun.COM 14927978SPeter.Dunlap@Sun.COM if ((idt == NULL) || (idx == idm.idm_taskid_max)) 14937978SPeter.Dunlap@Sun.COM return (NULL); 14947978SPeter.Dunlap@Sun.COM 14957978SPeter.Dunlap@Sun.COM return (idt->idt_private); 14967978SPeter.Dunlap@Sun.COM } 14977978SPeter.Dunlap@Sun.COM 14987978SPeter.Dunlap@Sun.COM void 14997978SPeter.Dunlap@Sun.COM idm_task_hold(idm_task_t *idt) 15007978SPeter.Dunlap@Sun.COM { 15017978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&idt->idt_refcnt); 15027978SPeter.Dunlap@Sun.COM } 15037978SPeter.Dunlap@Sun.COM 15047978SPeter.Dunlap@Sun.COM void 15057978SPeter.Dunlap@Sun.COM idm_task_rele(idm_task_t *idt) 15067978SPeter.Dunlap@Sun.COM { 15077978SPeter.Dunlap@Sun.COM idm_refcnt_rele(&idt->idt_refcnt); 15087978SPeter.Dunlap@Sun.COM } 15097978SPeter.Dunlap@Sun.COM 15107978SPeter.Dunlap@Sun.COM void 15117978SPeter.Dunlap@Sun.COM idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type) 15127978SPeter.Dunlap@Sun.COM { 15137978SPeter.Dunlap@Sun.COM idm_task_t *task; 15147978SPeter.Dunlap@Sun.COM int idx; 15157978SPeter.Dunlap@Sun.COM 15167978SPeter.Dunlap@Sun.COM /* 15177978SPeter.Dunlap@Sun.COM * Passing NULL as the task indicates that all tasks 15187978SPeter.Dunlap@Sun.COM * for this connection should be aborted. 15197978SPeter.Dunlap@Sun.COM */ 15207978SPeter.Dunlap@Sun.COM if (idt == NULL) { 15217978SPeter.Dunlap@Sun.COM /* 15227978SPeter.Dunlap@Sun.COM * Only the connection state machine should ask for 15237978SPeter.Dunlap@Sun.COM * all tasks to abort and this should never happen in FFP. 15247978SPeter.Dunlap@Sun.COM */ 15257978SPeter.Dunlap@Sun.COM ASSERT(!ic->ic_ffp); 15267978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 15277978SPeter.Dunlap@Sun.COM for (idx = 0; idx < idm.idm_taskid_max; idx++) { 15287978SPeter.Dunlap@Sun.COM task = idm.idm_taskid_table[idx]; 1529*9162SPeter.Dunlap@Sun.COM if (task == NULL) 1530*9162SPeter.Dunlap@Sun.COM continue; 1531*9162SPeter.Dunlap@Sun.COM mutex_enter(&task->idt_mutex); 1532*9162SPeter.Dunlap@Sun.COM if ((task->idt_state != TASK_IDLE) && 1533*9162SPeter.Dunlap@Sun.COM (task->idt_state != TASK_COMPLETE) && 15347978SPeter.Dunlap@Sun.COM (task->idt_ic == ic)) { 15357978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 15367978SPeter.Dunlap@Sun.COM idm_task_abort_one(ic, task, abort_type); 15377978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 1538*9162SPeter.Dunlap@Sun.COM } else 1539*9162SPeter.Dunlap@Sun.COM mutex_exit(&task->idt_mutex); 15407978SPeter.Dunlap@Sun.COM } 15417978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 15427978SPeter.Dunlap@Sun.COM } else { 1543*9162SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 15447978SPeter.Dunlap@Sun.COM idm_task_abort_one(ic, idt, abort_type); 15457978SPeter.Dunlap@Sun.COM } 15467978SPeter.Dunlap@Sun.COM } 15477978SPeter.Dunlap@Sun.COM 15487978SPeter.Dunlap@Sun.COM static void 15497978SPeter.Dunlap@Sun.COM idm_task_abort_unref_cb(void *ref) 15507978SPeter.Dunlap@Sun.COM { 15517978SPeter.Dunlap@Sun.COM idm_task_t *idt = ref; 15527978SPeter.Dunlap@Sun.COM 15537978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 15547978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 15557978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 15567978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_SUSPENDED; 15577978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15587978SPeter.Dunlap@Sun.COM idm_task_aborted(idt, IDM_STATUS_SUSPENDED); 15597978SPeter.Dunlap@Sun.COM return; 15607978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 15617978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTED; 15627978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15637978SPeter.Dunlap@Sun.COM idm_task_aborted(idt, IDM_STATUS_ABORTED); 15647978SPeter.Dunlap@Sun.COM return; 15657978SPeter.Dunlap@Sun.COM default: 15667978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15677978SPeter.Dunlap@Sun.COM ASSERT(0); 15687978SPeter.Dunlap@Sun.COM break; 15697978SPeter.Dunlap@Sun.COM } 15707978SPeter.Dunlap@Sun.COM } 15717978SPeter.Dunlap@Sun.COM 1572*9162SPeter.Dunlap@Sun.COM /* 1573*9162SPeter.Dunlap@Sun.COM * Abort the idm task. 1574*9162SPeter.Dunlap@Sun.COM * Caller must hold the task mutex, which will be released before return 1575*9162SPeter.Dunlap@Sun.COM */ 15767978SPeter.Dunlap@Sun.COM static void 15777978SPeter.Dunlap@Sun.COM idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type) 15787978SPeter.Dunlap@Sun.COM { 15797978SPeter.Dunlap@Sun.COM /* Caller must hold connection mutex */ 1580*9162SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 15817978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 15827978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 15837978SPeter.Dunlap@Sun.COM switch (abort_type) { 15847978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 15857978SPeter.Dunlap@Sun.COM /* Call transport to release any resources */ 15867978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_SUSPENDING; 15877978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15887978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_free_task_rsrc(idt); 15897978SPeter.Dunlap@Sun.COM 15907978SPeter.Dunlap@Sun.COM /* 15917978SPeter.Dunlap@Sun.COM * Wait for outstanding references. When all 15927978SPeter.Dunlap@Sun.COM * references are released the callback will call 15937978SPeter.Dunlap@Sun.COM * idm_task_aborted(). 15947978SPeter.Dunlap@Sun.COM */ 15957978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 15967978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 15977978SPeter.Dunlap@Sun.COM return; 15987978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 15997978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 16007978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 16017978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 16027978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_free_task_rsrc(idt); 16037978SPeter.Dunlap@Sun.COM 16047978SPeter.Dunlap@Sun.COM /* 16057978SPeter.Dunlap@Sun.COM * Wait for outstanding references. When all 16067978SPeter.Dunlap@Sun.COM * references are released the callback will call 16077978SPeter.Dunlap@Sun.COM * idm_task_aborted(). 16087978SPeter.Dunlap@Sun.COM */ 16097978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 16107978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 16117978SPeter.Dunlap@Sun.COM return; 16127978SPeter.Dunlap@Sun.COM default: 16137978SPeter.Dunlap@Sun.COM ASSERT(0); 16147978SPeter.Dunlap@Sun.COM } 16157978SPeter.Dunlap@Sun.COM break; 16167978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 16177978SPeter.Dunlap@Sun.COM /* Already called transport_free_task_rsrc(); */ 16187978SPeter.Dunlap@Sun.COM switch (abort_type) { 16197978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 16207978SPeter.Dunlap@Sun.COM /* Already doing it */ 16217978SPeter.Dunlap@Sun.COM break; 16227978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 16237978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 16247978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 16257978SPeter.Dunlap@Sun.COM break; 16267978SPeter.Dunlap@Sun.COM default: 16277978SPeter.Dunlap@Sun.COM ASSERT(0); 16287978SPeter.Dunlap@Sun.COM } 16297978SPeter.Dunlap@Sun.COM break; 16307978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 16317978SPeter.Dunlap@Sun.COM /* Already called transport_free_task_rsrc(); */ 16327978SPeter.Dunlap@Sun.COM switch (abort_type) { 16337978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 16347978SPeter.Dunlap@Sun.COM /* Already doing it */ 16357978SPeter.Dunlap@Sun.COM break; 16367978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 16377978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 16387978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 16397978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 16407978SPeter.Dunlap@Sun.COM 16417978SPeter.Dunlap@Sun.COM /* 16427978SPeter.Dunlap@Sun.COM * We could probably call idm_task_aborted directly 16437978SPeter.Dunlap@Sun.COM * here but we may be holding the conn lock. It's 16447978SPeter.Dunlap@Sun.COM * easier to just switch contexts. Even though 16457978SPeter.Dunlap@Sun.COM * we shouldn't really have any references we'll 16467978SPeter.Dunlap@Sun.COM * set the state to TASK_ABORTING instead of 16477978SPeter.Dunlap@Sun.COM * TASK_ABORTED so we can use the same code path. 16487978SPeter.Dunlap@Sun.COM */ 16497978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 16507978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 16517978SPeter.Dunlap@Sun.COM return; 16527978SPeter.Dunlap@Sun.COM default: 16537978SPeter.Dunlap@Sun.COM ASSERT(0); 16547978SPeter.Dunlap@Sun.COM } 16557978SPeter.Dunlap@Sun.COM break; 16567978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 16577978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 16587978SPeter.Dunlap@Sun.COM switch (abort_type) { 16597978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 16607978SPeter.Dunlap@Sun.COM /* We're already past this point... */ 16617978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 16627978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 16637978SPeter.Dunlap@Sun.COM /* Already doing it */ 16647978SPeter.Dunlap@Sun.COM break; 16657978SPeter.Dunlap@Sun.COM default: 16667978SPeter.Dunlap@Sun.COM ASSERT(0); 16677978SPeter.Dunlap@Sun.COM } 16687978SPeter.Dunlap@Sun.COM break; 16697978SPeter.Dunlap@Sun.COM case TASK_COMPLETE: 16707978SPeter.Dunlap@Sun.COM /* 16717978SPeter.Dunlap@Sun.COM * In this case, let it go. The status has already been 16727978SPeter.Dunlap@Sun.COM * sent (which may or may not get successfully transmitted) 16737978SPeter.Dunlap@Sun.COM * and we don't want to end up in a race between completing 16747978SPeter.Dunlap@Sun.COM * the status PDU and marking the task suspended. 16757978SPeter.Dunlap@Sun.COM */ 16767978SPeter.Dunlap@Sun.COM break; 16777978SPeter.Dunlap@Sun.COM default: 16787978SPeter.Dunlap@Sun.COM ASSERT(0); 16797978SPeter.Dunlap@Sun.COM } 16807978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 16817978SPeter.Dunlap@Sun.COM } 16827978SPeter.Dunlap@Sun.COM 16837978SPeter.Dunlap@Sun.COM static void 16847978SPeter.Dunlap@Sun.COM idm_task_aborted(idm_task_t *idt, idm_status_t status) 16857978SPeter.Dunlap@Sun.COM { 16867978SPeter.Dunlap@Sun.COM (*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status); 16877978SPeter.Dunlap@Sun.COM } 16887978SPeter.Dunlap@Sun.COM 16897978SPeter.Dunlap@Sun.COM void 16907978SPeter.Dunlap@Sun.COM idm_task_cleanup(idm_task_t *idt) 16917978SPeter.Dunlap@Sun.COM { 16927978SPeter.Dunlap@Sun.COM idm_buf_t *idb, *next_idb; 16937978SPeter.Dunlap@Sun.COM list_t tmp_buflist; 16947978SPeter.Dunlap@Sun.COM ASSERT((idt->idt_state == TASK_SUSPENDED) || 16957978SPeter.Dunlap@Sun.COM (idt->idt_state == TASK_ABORTED)); 16967978SPeter.Dunlap@Sun.COM 16977978SPeter.Dunlap@Sun.COM list_create(&tmp_buflist, sizeof (idm_buf_t), 16987978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 16997978SPeter.Dunlap@Sun.COM 17007978SPeter.Dunlap@Sun.COM /* 17017978SPeter.Dunlap@Sun.COM * Remove all the buffers from the task and add them to a 17027978SPeter.Dunlap@Sun.COM * temporary local list -- we do this so that we can hold 17037978SPeter.Dunlap@Sun.COM * the task lock and prevent the task from going away if 17047978SPeter.Dunlap@Sun.COM * the client decides to call idm_task_done/idm_task_free. 17057978SPeter.Dunlap@Sun.COM * This could happen during abort in iscsit. 17067978SPeter.Dunlap@Sun.COM */ 17077978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 17087978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_inbufv); 17097978SPeter.Dunlap@Sun.COM idb != NULL; 17107978SPeter.Dunlap@Sun.COM idb = next_idb) { 17117978SPeter.Dunlap@Sun.COM next_idb = list_next(&idt->idt_inbufv, idb); 17127978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, idb); 17137978SPeter.Dunlap@Sun.COM list_insert_tail(&tmp_buflist, idb); 17147978SPeter.Dunlap@Sun.COM } 17157978SPeter.Dunlap@Sun.COM 17167978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_outbufv); 17177978SPeter.Dunlap@Sun.COM idb != NULL; 17187978SPeter.Dunlap@Sun.COM idb = next_idb) { 17197978SPeter.Dunlap@Sun.COM next_idb = list_next(&idt->idt_outbufv, idb); 17207978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, idb); 17217978SPeter.Dunlap@Sun.COM list_insert_tail(&tmp_buflist, idb); 17227978SPeter.Dunlap@Sun.COM } 17237978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 17247978SPeter.Dunlap@Sun.COM 17257978SPeter.Dunlap@Sun.COM for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) { 17267978SPeter.Dunlap@Sun.COM next_idb = list_next(&tmp_buflist, idb); 17277978SPeter.Dunlap@Sun.COM list_remove(&tmp_buflist, idb); 17287978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED); 17297978SPeter.Dunlap@Sun.COM } 17307978SPeter.Dunlap@Sun.COM list_destroy(&tmp_buflist); 17317978SPeter.Dunlap@Sun.COM } 17327978SPeter.Dunlap@Sun.COM 17337978SPeter.Dunlap@Sun.COM 17347978SPeter.Dunlap@Sun.COM /* 17357978SPeter.Dunlap@Sun.COM * idm_pdu_tx 17367978SPeter.Dunlap@Sun.COM * 17377978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Send_Control' operational primitive. 17387978SPeter.Dunlap@Sun.COM * This function is invoked by an initiator iSCSI layer requesting the transfer 17397978SPeter.Dunlap@Sun.COM * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a 17407978SPeter.Dunlap@Sun.COM * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover 17417978SPeter.Dunlap@Sun.COM * layer to the peer iSCSI layer in the remote iSCSI node. The connection info 17427978SPeter.Dunlap@Sun.COM * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size 17437978SPeter.Dunlap@Sun.COM * are provided as input. 17447978SPeter.Dunlap@Sun.COM * 17457978SPeter.Dunlap@Sun.COM */ 17467978SPeter.Dunlap@Sun.COM void 17477978SPeter.Dunlap@Sun.COM idm_pdu_tx(idm_pdu_t *pdu) 17487978SPeter.Dunlap@Sun.COM { 17497978SPeter.Dunlap@Sun.COM idm_conn_t *ic = pdu->isp_ic; 17507978SPeter.Dunlap@Sun.COM iscsi_async_evt_hdr_t *async_evt; 17517978SPeter.Dunlap@Sun.COM 17527978SPeter.Dunlap@Sun.COM /* 17537978SPeter.Dunlap@Sun.COM * If we are in full-featured mode then route SCSI-related 17547978SPeter.Dunlap@Sun.COM * commands to the appropriate function vector without checking 17557978SPeter.Dunlap@Sun.COM * the connection state. We will only be in full-feature mode 17567978SPeter.Dunlap@Sun.COM * when we are in an acceptable state for SCSI PDU's. 17577978SPeter.Dunlap@Sun.COM * 17587978SPeter.Dunlap@Sun.COM * We also need to ensure that there are no PDU events outstanding 17597978SPeter.Dunlap@Sun.COM * on the state machine. Any non-SCSI PDU's received in full-feature 17607978SPeter.Dunlap@Sun.COM * mode will result in PDU events and until these have been handled 17617978SPeter.Dunlap@Sun.COM * we need to route all PDU's through the state machine as PDU 17627978SPeter.Dunlap@Sun.COM * events to maintain ordering. 17637978SPeter.Dunlap@Sun.COM * 17647978SPeter.Dunlap@Sun.COM * Note that IDM cannot enter FFP mode until it processes in 17657978SPeter.Dunlap@Sun.COM * its state machine the last xmit of the login process. 17667978SPeter.Dunlap@Sun.COM * Hence, checking the IDM_PDU_LOGIN_TX flag here would be 17677978SPeter.Dunlap@Sun.COM * superfluous. 17687978SPeter.Dunlap@Sun.COM */ 17697978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 17707978SPeter.Dunlap@Sun.COM if (ic->ic_ffp && (ic->ic_pdu_events == 0)) { 17717978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 17727978SPeter.Dunlap@Sun.COM switch (IDM_PDU_OPCODE(pdu)) { 17737978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_RSP: 17747978SPeter.Dunlap@Sun.COM /* Target only */ 17757978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17767978SPeter.Dunlap@Sun.COM return; 17777978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_RSP: 17787978SPeter.Dunlap@Sun.COM /* Target only */ 17797978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17807978SPeter.Dunlap@Sun.COM return; 17817978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA_RSP: 17827978SPeter.Dunlap@Sun.COM /* Target only */ 17837978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17847978SPeter.Dunlap@Sun.COM return; 17857978SPeter.Dunlap@Sun.COM case ISCSI_OP_RTT_RSP: 17867978SPeter.Dunlap@Sun.COM /* Target only */ 17877978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17887978SPeter.Dunlap@Sun.COM return; 17897978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_IN: 17907978SPeter.Dunlap@Sun.COM /* Target only */ 17917978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17927978SPeter.Dunlap@Sun.COM return; 17937978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_RSP: 17947978SPeter.Dunlap@Sun.COM /* Target only */ 17957978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 17967978SPeter.Dunlap@Sun.COM return; 17977978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_CMD: 17987978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_OUT: 17997978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_CMD: 18007978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA: 18017978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_MSG: 18027978SPeter.Dunlap@Sun.COM /* Initiator only */ 18037978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 18047978SPeter.Dunlap@Sun.COM return; 18057978SPeter.Dunlap@Sun.COM default: 18067978SPeter.Dunlap@Sun.COM break; 18077978SPeter.Dunlap@Sun.COM } 18087978SPeter.Dunlap@Sun.COM 18097978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 18107978SPeter.Dunlap@Sun.COM } 18117978SPeter.Dunlap@Sun.COM 18127978SPeter.Dunlap@Sun.COM /* 18137978SPeter.Dunlap@Sun.COM * Any PDU's processed outside of full-feature mode and non-SCSI 18147978SPeter.Dunlap@Sun.COM * PDU's in full-feature mode are handled by generating an 18157978SPeter.Dunlap@Sun.COM * event to the connection state machine. The state machine 18167978SPeter.Dunlap@Sun.COM * will validate the PDU against the current state and either 18177978SPeter.Dunlap@Sun.COM * transmit the PDU if the opcode is allowed or handle an 18187978SPeter.Dunlap@Sun.COM * error if the PDU is not allowed. 18197978SPeter.Dunlap@Sun.COM * 18207978SPeter.Dunlap@Sun.COM * This code-path will also generate any events that are implied 18217978SPeter.Dunlap@Sun.COM * by the PDU opcode. For example a "login response" with success 18227978SPeter.Dunlap@Sun.COM * status generates a CE_LOGOUT_SUCCESS_SND event. 18237978SPeter.Dunlap@Sun.COM */ 18247978SPeter.Dunlap@Sun.COM switch (IDM_PDU_OPCODE(pdu)) { 18257978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGIN_CMD: 18267978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu); 18277978SPeter.Dunlap@Sun.COM break; 18287978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGIN_RSP: 18297978SPeter.Dunlap@Sun.COM idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE); 18307978SPeter.Dunlap@Sun.COM break; 18317978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGOUT_CMD: 18327978SPeter.Dunlap@Sun.COM idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE); 18337978SPeter.Dunlap@Sun.COM break; 18347978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGOUT_RSP: 18357978SPeter.Dunlap@Sun.COM idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE); 18367978SPeter.Dunlap@Sun.COM break; 18377978SPeter.Dunlap@Sun.COM case ISCSI_OP_ASYNC_EVENT: 18387978SPeter.Dunlap@Sun.COM async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr; 18397978SPeter.Dunlap@Sun.COM switch (async_evt->async_event) { 18407978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT: 18417978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND, 18427978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 18437978SPeter.Dunlap@Sun.COM break; 18447978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION: 18457978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND, 18467978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 18477978SPeter.Dunlap@Sun.COM break; 18487978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS: 18497978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND, 18507978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 18517978SPeter.Dunlap@Sun.COM break; 18527978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_SCSI_EVENT: 18537978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION: 18547978SPeter.Dunlap@Sun.COM default: 18557978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, 18567978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 18577978SPeter.Dunlap@Sun.COM break; 18587978SPeter.Dunlap@Sun.COM } 18597978SPeter.Dunlap@Sun.COM break; 18607978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_RSP: 18617978SPeter.Dunlap@Sun.COM /* Target only */ 18627978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18637978SPeter.Dunlap@Sun.COM break; 18647978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_RSP: 18657978SPeter.Dunlap@Sun.COM /* Target only */ 18667978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18677978SPeter.Dunlap@Sun.COM break; 18687978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA_RSP: 18697978SPeter.Dunlap@Sun.COM /* Target only */ 18707978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18717978SPeter.Dunlap@Sun.COM break; 18727978SPeter.Dunlap@Sun.COM case ISCSI_OP_RTT_RSP: 18737978SPeter.Dunlap@Sun.COM /* Target only */ 18747978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18757978SPeter.Dunlap@Sun.COM break; 18767978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_IN: 18777978SPeter.Dunlap@Sun.COM /* Target only */ 18787978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18797978SPeter.Dunlap@Sun.COM break; 18807978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_RSP: 18817978SPeter.Dunlap@Sun.COM /* Target only */ 18827978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18837978SPeter.Dunlap@Sun.COM break; 18847978SPeter.Dunlap@Sun.COM /* Initiator only */ 18857978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_CMD: 18867978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_MSG: 18877978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA: 18887978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_OUT: 18897978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_CMD: 18907978SPeter.Dunlap@Sun.COM case ISCSI_OP_SNACK_CMD: 18917978SPeter.Dunlap@Sun.COM case ISCSI_OP_REJECT_MSG: 18927978SPeter.Dunlap@Sun.COM default: 18937978SPeter.Dunlap@Sun.COM /* 18947978SPeter.Dunlap@Sun.COM * Connection state machine will validate these PDU's against 18957978SPeter.Dunlap@Sun.COM * the current state. A PDU not allowed in the current 18967978SPeter.Dunlap@Sun.COM * state will cause a protocol error. 18977978SPeter.Dunlap@Sun.COM */ 18987978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 18997978SPeter.Dunlap@Sun.COM break; 19007978SPeter.Dunlap@Sun.COM } 19017978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 19027978SPeter.Dunlap@Sun.COM } 19037978SPeter.Dunlap@Sun.COM 19047978SPeter.Dunlap@Sun.COM /* 1905*9162SPeter.Dunlap@Sun.COM * Common allocation of a PDU along with memory for header and data. 19067978SPeter.Dunlap@Sun.COM */ 1907*9162SPeter.Dunlap@Sun.COM static idm_pdu_t * 1908*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen, int sleepflag) 19097978SPeter.Dunlap@Sun.COM { 19107978SPeter.Dunlap@Sun.COM idm_pdu_t *result; 19117978SPeter.Dunlap@Sun.COM 19127978SPeter.Dunlap@Sun.COM /* 19137978SPeter.Dunlap@Sun.COM * IDM clients should cache these structures for performance 19147978SPeter.Dunlap@Sun.COM * critical paths. We can't cache effectively in IDM because we 19157978SPeter.Dunlap@Sun.COM * don't know the correct header and data size. 19167978SPeter.Dunlap@Sun.COM * 19177978SPeter.Dunlap@Sun.COM * Valid header length is assumed to be hdrlen and valid data 19187978SPeter.Dunlap@Sun.COM * length is assumed to be datalen. isp_hdrlen and isp_datalen 19197978SPeter.Dunlap@Sun.COM * can be adjusted after the PDU is returned if necessary. 19207978SPeter.Dunlap@Sun.COM */ 1921*9162SPeter.Dunlap@Sun.COM result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, sleepflag); 1922*9162SPeter.Dunlap@Sun.COM if (result != NULL) { 1923*9162SPeter.Dunlap@Sun.COM /* For idm_pdu_free sanity check */ 1924*9162SPeter.Dunlap@Sun.COM result->isp_flags |= IDM_PDU_ALLOC; 1925*9162SPeter.Dunlap@Sun.COM /* pointer arithmetic */ 1926*9162SPeter.Dunlap@Sun.COM result->isp_hdr = (iscsi_hdr_t *)(result + 1); 1927*9162SPeter.Dunlap@Sun.COM result->isp_hdrlen = hdrlen; 1928*9162SPeter.Dunlap@Sun.COM result->isp_hdrbuflen = hdrlen; 1929*9162SPeter.Dunlap@Sun.COM result->isp_transport_hdrlen = 0; 1930*9162SPeter.Dunlap@Sun.COM result->isp_data = (uint8_t *)result->isp_hdr + hdrlen; 1931*9162SPeter.Dunlap@Sun.COM result->isp_datalen = datalen; 1932*9162SPeter.Dunlap@Sun.COM result->isp_databuflen = datalen; 1933*9162SPeter.Dunlap@Sun.COM result->isp_magic = IDM_PDU_MAGIC; 1934*9162SPeter.Dunlap@Sun.COM } 19357978SPeter.Dunlap@Sun.COM 19367978SPeter.Dunlap@Sun.COM return (result); 19377978SPeter.Dunlap@Sun.COM } 19387978SPeter.Dunlap@Sun.COM 19397978SPeter.Dunlap@Sun.COM /* 1940*9162SPeter.Dunlap@Sun.COM * Typical idm_pdu_alloc invocation, will block for resources. 1941*9162SPeter.Dunlap@Sun.COM */ 1942*9162SPeter.Dunlap@Sun.COM idm_pdu_t * 1943*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc(uint_t hdrlen, uint_t datalen) 1944*9162SPeter.Dunlap@Sun.COM { 1945*9162SPeter.Dunlap@Sun.COM return (idm_pdu_alloc_common(hdrlen, datalen, KM_SLEEP)); 1946*9162SPeter.Dunlap@Sun.COM } 1947*9162SPeter.Dunlap@Sun.COM 1948*9162SPeter.Dunlap@Sun.COM /* 1949*9162SPeter.Dunlap@Sun.COM * Non-blocking idm_pdu_alloc implementation, returns NULL if resources 1950*9162SPeter.Dunlap@Sun.COM * are not available. Needed for transport-layer allocations which may 1951*9162SPeter.Dunlap@Sun.COM * be invoking in interrupt context. 1952*9162SPeter.Dunlap@Sun.COM */ 1953*9162SPeter.Dunlap@Sun.COM idm_pdu_t * 1954*9162SPeter.Dunlap@Sun.COM idm_pdu_alloc_nosleep(uint_t hdrlen, uint_t datalen) 1955*9162SPeter.Dunlap@Sun.COM { 1956*9162SPeter.Dunlap@Sun.COM return (idm_pdu_alloc_common(hdrlen, datalen, KM_NOSLEEP)); 1957*9162SPeter.Dunlap@Sun.COM } 1958*9162SPeter.Dunlap@Sun.COM 1959*9162SPeter.Dunlap@Sun.COM /* 19607978SPeter.Dunlap@Sun.COM * Free a PDU previously allocated with idm_pdu_alloc() including any 19617978SPeter.Dunlap@Sun.COM * header and data space allocated as part of the original request. 19627978SPeter.Dunlap@Sun.COM * Additional memory regions referenced by subsequent modification of 19637978SPeter.Dunlap@Sun.COM * the isp_hdr and/or isp_data fields will not be freed. 19647978SPeter.Dunlap@Sun.COM */ 19657978SPeter.Dunlap@Sun.COM void 19667978SPeter.Dunlap@Sun.COM idm_pdu_free(idm_pdu_t *pdu) 19677978SPeter.Dunlap@Sun.COM { 19687978SPeter.Dunlap@Sun.COM /* Make sure the structure was allocated using idm_pdu_alloc() */ 19697978SPeter.Dunlap@Sun.COM ASSERT(pdu->isp_flags & IDM_PDU_ALLOC); 19707978SPeter.Dunlap@Sun.COM kmem_free(pdu, 19717978SPeter.Dunlap@Sun.COM sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen); 19727978SPeter.Dunlap@Sun.COM } 19737978SPeter.Dunlap@Sun.COM 19747978SPeter.Dunlap@Sun.COM /* 19757978SPeter.Dunlap@Sun.COM * Initialize the connection, private and callback fields in a PDU. 19767978SPeter.Dunlap@Sun.COM */ 19777978SPeter.Dunlap@Sun.COM void 19787978SPeter.Dunlap@Sun.COM idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb) 19797978SPeter.Dunlap@Sun.COM { 19807978SPeter.Dunlap@Sun.COM /* 19817978SPeter.Dunlap@Sun.COM * idm_pdu_complete() will call idm_pdu_free if the callback is 19827978SPeter.Dunlap@Sun.COM * NULL. This will only work if the PDU was originally allocated 19837978SPeter.Dunlap@Sun.COM * with idm_pdu_alloc(). 19847978SPeter.Dunlap@Sun.COM */ 19857978SPeter.Dunlap@Sun.COM ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) || 19867978SPeter.Dunlap@Sun.COM (cb != NULL)); 19877978SPeter.Dunlap@Sun.COM pdu->isp_magic = IDM_PDU_MAGIC; 19887978SPeter.Dunlap@Sun.COM pdu->isp_ic = ic; 19897978SPeter.Dunlap@Sun.COM pdu->isp_private = private; 19907978SPeter.Dunlap@Sun.COM pdu->isp_callback = cb; 19917978SPeter.Dunlap@Sun.COM } 19927978SPeter.Dunlap@Sun.COM 19937978SPeter.Dunlap@Sun.COM /* 19947978SPeter.Dunlap@Sun.COM * Initialize the header and header length field. This function should 19957978SPeter.Dunlap@Sun.COM * not be used to adjust the header length in a buffer allocated via 19967978SPeter.Dunlap@Sun.COM * pdu_pdu_alloc since it overwrites the existing header pointer. 19977978SPeter.Dunlap@Sun.COM */ 19987978SPeter.Dunlap@Sun.COM void 19997978SPeter.Dunlap@Sun.COM idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen) 20007978SPeter.Dunlap@Sun.COM { 20017978SPeter.Dunlap@Sun.COM pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr); 20027978SPeter.Dunlap@Sun.COM pdu->isp_hdrlen = hdrlen; 20037978SPeter.Dunlap@Sun.COM } 20047978SPeter.Dunlap@Sun.COM 20057978SPeter.Dunlap@Sun.COM /* 20067978SPeter.Dunlap@Sun.COM * Initialize the data and data length fields. This function should 20077978SPeter.Dunlap@Sun.COM * not be used to adjust the data length of a buffer allocated via 20087978SPeter.Dunlap@Sun.COM * idm_pdu_alloc since it overwrites the existing data pointer. 20097978SPeter.Dunlap@Sun.COM */ 20107978SPeter.Dunlap@Sun.COM void 20117978SPeter.Dunlap@Sun.COM idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen) 20127978SPeter.Dunlap@Sun.COM { 20137978SPeter.Dunlap@Sun.COM pdu->isp_data = data; 20147978SPeter.Dunlap@Sun.COM pdu->isp_datalen = datalen; 20157978SPeter.Dunlap@Sun.COM } 20167978SPeter.Dunlap@Sun.COM 20177978SPeter.Dunlap@Sun.COM void 20187978SPeter.Dunlap@Sun.COM idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status) 20197978SPeter.Dunlap@Sun.COM { 20207978SPeter.Dunlap@Sun.COM if (pdu->isp_callback) { 20217978SPeter.Dunlap@Sun.COM pdu->isp_status = status; 20227978SPeter.Dunlap@Sun.COM (*pdu->isp_callback)(pdu, status); 20237978SPeter.Dunlap@Sun.COM } else { 20247978SPeter.Dunlap@Sun.COM idm_pdu_free(pdu); 20257978SPeter.Dunlap@Sun.COM } 20267978SPeter.Dunlap@Sun.COM } 20277978SPeter.Dunlap@Sun.COM 20287978SPeter.Dunlap@Sun.COM /* 20297978SPeter.Dunlap@Sun.COM * State machine auditing 20307978SPeter.Dunlap@Sun.COM */ 20317978SPeter.Dunlap@Sun.COM 20327978SPeter.Dunlap@Sun.COM void 20337978SPeter.Dunlap@Sun.COM idm_sm_audit_init(sm_audit_buf_t *audit_buf) 20347978SPeter.Dunlap@Sun.COM { 20357978SPeter.Dunlap@Sun.COM bzero(audit_buf, sizeof (sm_audit_buf_t)); 20367978SPeter.Dunlap@Sun.COM audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1; 20377978SPeter.Dunlap@Sun.COM } 20387978SPeter.Dunlap@Sun.COM 20397978SPeter.Dunlap@Sun.COM static 20407978SPeter.Dunlap@Sun.COM sm_audit_record_t * 20417978SPeter.Dunlap@Sun.COM idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type, 20427978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, 20437978SPeter.Dunlap@Sun.COM int current_state) 20447978SPeter.Dunlap@Sun.COM { 20457978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 20467978SPeter.Dunlap@Sun.COM 20477978SPeter.Dunlap@Sun.COM sar = audit_buf->sab_records; 20487978SPeter.Dunlap@Sun.COM sar += audit_buf->sab_index; 20497978SPeter.Dunlap@Sun.COM audit_buf->sab_index++; 20507978SPeter.Dunlap@Sun.COM audit_buf->sab_index &= audit_buf->sab_max_index; 20517978SPeter.Dunlap@Sun.COM 20527978SPeter.Dunlap@Sun.COM sar->sar_type = r_type; 20537978SPeter.Dunlap@Sun.COM gethrestime(&sar->sar_timestamp); 20547978SPeter.Dunlap@Sun.COM sar->sar_sm_type = sm_type; 20557978SPeter.Dunlap@Sun.COM sar->sar_state = current_state; 20567978SPeter.Dunlap@Sun.COM 20577978SPeter.Dunlap@Sun.COM return (sar); 20587978SPeter.Dunlap@Sun.COM } 20597978SPeter.Dunlap@Sun.COM 20607978SPeter.Dunlap@Sun.COM void 20617978SPeter.Dunlap@Sun.COM idm_sm_audit_event(sm_audit_buf_t *audit_buf, 20627978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, int current_state, 20637978SPeter.Dunlap@Sun.COM int event, uintptr_t event_info) 20647978SPeter.Dunlap@Sun.COM { 20657978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 20667978SPeter.Dunlap@Sun.COM 20677978SPeter.Dunlap@Sun.COM sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT, 20687978SPeter.Dunlap@Sun.COM sm_type, current_state); 20697978SPeter.Dunlap@Sun.COM sar->sar_event = event; 20707978SPeter.Dunlap@Sun.COM sar->sar_event_info = event_info; 20717978SPeter.Dunlap@Sun.COM } 20727978SPeter.Dunlap@Sun.COM 20737978SPeter.Dunlap@Sun.COM void 20747978SPeter.Dunlap@Sun.COM idm_sm_audit_state_change(sm_audit_buf_t *audit_buf, 20757978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, int current_state, int new_state) 20767978SPeter.Dunlap@Sun.COM { 20777978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 20787978SPeter.Dunlap@Sun.COM 20797978SPeter.Dunlap@Sun.COM sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE, 20807978SPeter.Dunlap@Sun.COM sm_type, current_state); 20817978SPeter.Dunlap@Sun.COM sar->sar_new_state = new_state; 20827978SPeter.Dunlap@Sun.COM } 20837978SPeter.Dunlap@Sun.COM 20847978SPeter.Dunlap@Sun.COM 20857978SPeter.Dunlap@Sun.COM /* 20867978SPeter.Dunlap@Sun.COM * Object reference tracking 20877978SPeter.Dunlap@Sun.COM */ 20887978SPeter.Dunlap@Sun.COM 20897978SPeter.Dunlap@Sun.COM void 20907978SPeter.Dunlap@Sun.COM idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj) 20917978SPeter.Dunlap@Sun.COM { 20927978SPeter.Dunlap@Sun.COM bzero(refcnt, sizeof (*refcnt)); 20937978SPeter.Dunlap@Sun.COM idm_refcnt_reset(refcnt); 20947978SPeter.Dunlap@Sun.COM refcnt->ir_referenced_obj = referenced_obj; 20957978SPeter.Dunlap@Sun.COM bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t)); 20967978SPeter.Dunlap@Sun.COM refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1; 20977978SPeter.Dunlap@Sun.COM mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL); 20987978SPeter.Dunlap@Sun.COM cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL); 20997978SPeter.Dunlap@Sun.COM } 21007978SPeter.Dunlap@Sun.COM 21017978SPeter.Dunlap@Sun.COM void 21027978SPeter.Dunlap@Sun.COM idm_refcnt_destroy(idm_refcnt_t *refcnt) 21037978SPeter.Dunlap@Sun.COM { 21047978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt == 0); 21057978SPeter.Dunlap@Sun.COM cv_destroy(&refcnt->ir_cv); 21067978SPeter.Dunlap@Sun.COM mutex_destroy(&refcnt->ir_mutex); 21077978SPeter.Dunlap@Sun.COM } 21087978SPeter.Dunlap@Sun.COM 21097978SPeter.Dunlap@Sun.COM void 21107978SPeter.Dunlap@Sun.COM idm_refcnt_reset(idm_refcnt_t *refcnt) 21117978SPeter.Dunlap@Sun.COM { 21127978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_NOWAIT; 21137978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt = 0; 21147978SPeter.Dunlap@Sun.COM } 21157978SPeter.Dunlap@Sun.COM 21167978SPeter.Dunlap@Sun.COM void 21177978SPeter.Dunlap@Sun.COM idm_refcnt_hold(idm_refcnt_t *refcnt) 21187978SPeter.Dunlap@Sun.COM { 21197978SPeter.Dunlap@Sun.COM /* 21207978SPeter.Dunlap@Sun.COM * Nothing should take a hold on an object after a call to 21217978SPeter.Dunlap@Sun.COM * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref 21227978SPeter.Dunlap@Sun.COM */ 21237978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_waiting == REF_NOWAIT); 21247978SPeter.Dunlap@Sun.COM 21257978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 21267978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt++; 21277978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 21287978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 21297978SPeter.Dunlap@Sun.COM } 21307978SPeter.Dunlap@Sun.COM 21317978SPeter.Dunlap@Sun.COM static void 21327978SPeter.Dunlap@Sun.COM idm_refcnt_unref_task(void *refcnt_void) 21337978SPeter.Dunlap@Sun.COM { 21347978SPeter.Dunlap@Sun.COM idm_refcnt_t *refcnt = refcnt_void; 21357978SPeter.Dunlap@Sun.COM 21367978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 21377978SPeter.Dunlap@Sun.COM (*refcnt->ir_cb)(refcnt->ir_referenced_obj); 21387978SPeter.Dunlap@Sun.COM } 21397978SPeter.Dunlap@Sun.COM 21407978SPeter.Dunlap@Sun.COM void 21417978SPeter.Dunlap@Sun.COM idm_refcnt_rele(idm_refcnt_t *refcnt) 21427978SPeter.Dunlap@Sun.COM { 21437978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 21447978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt > 0); 21457978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt--; 21467978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 21477978SPeter.Dunlap@Sun.COM if (refcnt->ir_waiting == REF_NOWAIT) { 21487978SPeter.Dunlap@Sun.COM /* No one is waiting on this object */ 21497978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 21507978SPeter.Dunlap@Sun.COM return; 21517978SPeter.Dunlap@Sun.COM } 21527978SPeter.Dunlap@Sun.COM 21537978SPeter.Dunlap@Sun.COM /* 21547978SPeter.Dunlap@Sun.COM * Someone is waiting for this object to go idle so check if 21557978SPeter.Dunlap@Sun.COM * refcnt is 0. Waiting on an object then later grabbing another 21567978SPeter.Dunlap@Sun.COM * reference is not allowed so we don't need to handle that case. 21577978SPeter.Dunlap@Sun.COM */ 21587978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 21597978SPeter.Dunlap@Sun.COM if (refcnt->ir_waiting == REF_WAIT_ASYNC) { 21607978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 21617978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 21627978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 21637978SPeter.Dunlap@Sun.COM "idm_refcnt_rele: Couldn't dispatch task"); 21647978SPeter.Dunlap@Sun.COM } 21657978SPeter.Dunlap@Sun.COM } else if (refcnt->ir_waiting == REF_WAIT_SYNC) { 21667978SPeter.Dunlap@Sun.COM cv_signal(&refcnt->ir_cv); 21677978SPeter.Dunlap@Sun.COM } 21687978SPeter.Dunlap@Sun.COM } 21697978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 21707978SPeter.Dunlap@Sun.COM } 21717978SPeter.Dunlap@Sun.COM 21727978SPeter.Dunlap@Sun.COM void 21737978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func) 21747978SPeter.Dunlap@Sun.COM { 21757978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 21767978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt > 0); 21777978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt--; 21787978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 21797978SPeter.Dunlap@Sun.COM 21807978SPeter.Dunlap@Sun.COM /* 21817978SPeter.Dunlap@Sun.COM * Someone is waiting for this object to go idle so check if 21827978SPeter.Dunlap@Sun.COM * refcnt is 0. Waiting on an object then later grabbing another 21837978SPeter.Dunlap@Sun.COM * reference is not allowed so we don't need to handle that case. 21847978SPeter.Dunlap@Sun.COM */ 21857978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 21867978SPeter.Dunlap@Sun.COM refcnt->ir_cb = cb_func; 21877978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_ASYNC; 21887978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 21897978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 21907978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 21917978SPeter.Dunlap@Sun.COM "idm_refcnt_rele: Couldn't dispatch task"); 21927978SPeter.Dunlap@Sun.COM } 21937978SPeter.Dunlap@Sun.COM } 21947978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 21957978SPeter.Dunlap@Sun.COM } 21967978SPeter.Dunlap@Sun.COM 21977978SPeter.Dunlap@Sun.COM void 21987978SPeter.Dunlap@Sun.COM idm_refcnt_wait_ref(idm_refcnt_t *refcnt) 21997978SPeter.Dunlap@Sun.COM { 22007978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 22017978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_SYNC; 22027978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 22037978SPeter.Dunlap@Sun.COM while (refcnt->ir_refcnt != 0) 22047978SPeter.Dunlap@Sun.COM cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex); 22057978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 22067978SPeter.Dunlap@Sun.COM } 22077978SPeter.Dunlap@Sun.COM 22087978SPeter.Dunlap@Sun.COM void 22097978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func) 22107978SPeter.Dunlap@Sun.COM { 22117978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 22127978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_ASYNC; 22137978SPeter.Dunlap@Sun.COM refcnt->ir_cb = cb_func; 22147978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 22157978SPeter.Dunlap@Sun.COM /* 22167978SPeter.Dunlap@Sun.COM * It's possible we don't have any references. To make things easier 22177978SPeter.Dunlap@Sun.COM * on the caller use a taskq to call the callback instead of 22187978SPeter.Dunlap@Sun.COM * calling it synchronously 22197978SPeter.Dunlap@Sun.COM */ 22207978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 22217978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 22227978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 22237978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 22247978SPeter.Dunlap@Sun.COM "idm_refcnt_async_wait_ref: " 22257978SPeter.Dunlap@Sun.COM "Couldn't dispatch task"); 22267978SPeter.Dunlap@Sun.COM } 22277978SPeter.Dunlap@Sun.COM } 22287978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 22297978SPeter.Dunlap@Sun.COM } 22307978SPeter.Dunlap@Sun.COM 22317978SPeter.Dunlap@Sun.COM void 22327978SPeter.Dunlap@Sun.COM idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt, 22337978SPeter.Dunlap@Sun.COM idm_refcnt_cb_t *cb_func) 22347978SPeter.Dunlap@Sun.COM { 22357978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 22367978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 22377978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 22387978SPeter.Dunlap@Sun.COM (*cb_func)(refcnt->ir_referenced_obj); 22397978SPeter.Dunlap@Sun.COM return; 22407978SPeter.Dunlap@Sun.COM } 22417978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 22427978SPeter.Dunlap@Sun.COM } 22437978SPeter.Dunlap@Sun.COM 22447978SPeter.Dunlap@Sun.COM void 22457978SPeter.Dunlap@Sun.COM idm_conn_hold(idm_conn_t *ic) 22467978SPeter.Dunlap@Sun.COM { 22477978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&ic->ic_refcnt); 22487978SPeter.Dunlap@Sun.COM } 22497978SPeter.Dunlap@Sun.COM 22507978SPeter.Dunlap@Sun.COM void 22517978SPeter.Dunlap@Sun.COM idm_conn_rele(idm_conn_t *ic) 22527978SPeter.Dunlap@Sun.COM { 22537978SPeter.Dunlap@Sun.COM idm_refcnt_rele(&ic->ic_refcnt); 22547978SPeter.Dunlap@Sun.COM } 22557978SPeter.Dunlap@Sun.COM 22567978SPeter.Dunlap@Sun.COM 22577978SPeter.Dunlap@Sun.COM static int 22587978SPeter.Dunlap@Sun.COM _idm_init(void) 22597978SPeter.Dunlap@Sun.COM { 22607978SPeter.Dunlap@Sun.COM /* Initialize the rwlock for the taskid table */ 22617978SPeter.Dunlap@Sun.COM rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL); 22627978SPeter.Dunlap@Sun.COM 22637978SPeter.Dunlap@Sun.COM /* Initialize the global mutex and taskq */ 22647978SPeter.Dunlap@Sun.COM mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL); 22657978SPeter.Dunlap@Sun.COM 22667978SPeter.Dunlap@Sun.COM cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL); 22677978SPeter.Dunlap@Sun.COM cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL); 22687978SPeter.Dunlap@Sun.COM 2269*9162SPeter.Dunlap@Sun.COM /* 2270*9162SPeter.Dunlap@Sun.COM * The maximum allocation needs to be high here since there can be 2271*9162SPeter.Dunlap@Sun.COM * many concurrent tasks using the global taskq. 2272*9162SPeter.Dunlap@Sun.COM */ 22737978SPeter.Dunlap@Sun.COM idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri, 2274*9162SPeter.Dunlap@Sun.COM 128, 16384, TASKQ_PREPOPULATE); 22757978SPeter.Dunlap@Sun.COM if (idm.idm_global_taskq == NULL) { 22767978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 22777978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 22787978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 22797978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 22807978SPeter.Dunlap@Sun.COM return (ENOMEM); 22817978SPeter.Dunlap@Sun.COM } 22827978SPeter.Dunlap@Sun.COM 22838209SJames.Moore@Sun.COM /* Start watchdog thread */ 22847978SPeter.Dunlap@Sun.COM idm.idm_wd_thread = thread_create(NULL, 0, 22857978SPeter.Dunlap@Sun.COM idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri); 22867978SPeter.Dunlap@Sun.COM if (idm.idm_wd_thread == NULL) { 22877978SPeter.Dunlap@Sun.COM /* Couldn't create the watchdog thread */ 22887978SPeter.Dunlap@Sun.COM taskq_destroy(idm.idm_global_taskq); 22897978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 22907978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 22917978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 22927978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 22937978SPeter.Dunlap@Sun.COM return (ENOMEM); 22947978SPeter.Dunlap@Sun.COM } 22957978SPeter.Dunlap@Sun.COM 22968209SJames.Moore@Sun.COM /* Pause until the watchdog thread is running */ 22977978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 22987978SPeter.Dunlap@Sun.COM while (!idm.idm_wd_thread_running) 22997978SPeter.Dunlap@Sun.COM cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex); 23007978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 23017978SPeter.Dunlap@Sun.COM 23027978SPeter.Dunlap@Sun.COM /* 23037978SPeter.Dunlap@Sun.COM * Allocate the task ID table and set "next" to 0. 23047978SPeter.Dunlap@Sun.COM */ 23057978SPeter.Dunlap@Sun.COM 23067978SPeter.Dunlap@Sun.COM idm.idm_taskid_max = idm_max_taskids; 23077978SPeter.Dunlap@Sun.COM idm.idm_taskid_table = (idm_task_t **) 23087978SPeter.Dunlap@Sun.COM kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP); 23097978SPeter.Dunlap@Sun.COM idm.idm_taskid_next = 0; 23107978SPeter.Dunlap@Sun.COM 23117978SPeter.Dunlap@Sun.COM /* Create the global buffer and task kmem caches */ 23127978SPeter.Dunlap@Sun.COM idm.idm_buf_cache = kmem_cache_create("idm_buf_cache", 23137978SPeter.Dunlap@Sun.COM sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP); 23147978SPeter.Dunlap@Sun.COM 23157978SPeter.Dunlap@Sun.COM /* 23167978SPeter.Dunlap@Sun.COM * Note, we're explicitly allocating an additional iSER header- 23177978SPeter.Dunlap@Sun.COM * sized chunk for each of these elements. See idm_task_constructor(). 23187978SPeter.Dunlap@Sun.COM */ 23197978SPeter.Dunlap@Sun.COM idm.idm_task_cache = kmem_cache_create("idm_task_cache", 23207978SPeter.Dunlap@Sun.COM sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8, 23217978SPeter.Dunlap@Sun.COM &idm_task_constructor, &idm_task_destructor, 23227978SPeter.Dunlap@Sun.COM NULL, NULL, NULL, KM_SLEEP); 23237978SPeter.Dunlap@Sun.COM 23247978SPeter.Dunlap@Sun.COM /* Create the service and connection context lists */ 23257978SPeter.Dunlap@Sun.COM list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t), 23267978SPeter.Dunlap@Sun.COM offsetof(idm_svc_t, is_list_node)); 23277978SPeter.Dunlap@Sun.COM list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t), 23287978SPeter.Dunlap@Sun.COM offsetof(idm_conn_t, ic_list_node)); 23297978SPeter.Dunlap@Sun.COM list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t), 23307978SPeter.Dunlap@Sun.COM offsetof(idm_conn_t, ic_list_node)); 23317978SPeter.Dunlap@Sun.COM 23327978SPeter.Dunlap@Sun.COM /* Initialize the native sockets transport */ 23337978SPeter.Dunlap@Sun.COM idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]); 23347978SPeter.Dunlap@Sun.COM 23357978SPeter.Dunlap@Sun.COM /* Create connection ID pool */ 23367978SPeter.Dunlap@Sun.COM (void) idm_idpool_create(&idm.idm_conn_id_pool); 23377978SPeter.Dunlap@Sun.COM 23387978SPeter.Dunlap@Sun.COM return (DDI_SUCCESS); 23397978SPeter.Dunlap@Sun.COM } 23407978SPeter.Dunlap@Sun.COM 23417978SPeter.Dunlap@Sun.COM static int 23427978SPeter.Dunlap@Sun.COM _idm_fini(void) 23437978SPeter.Dunlap@Sun.COM { 23447978SPeter.Dunlap@Sun.COM if (!list_is_empty(&idm.idm_ini_conn_list) || 23457978SPeter.Dunlap@Sun.COM !list_is_empty(&idm.idm_tgt_conn_list) || 23467978SPeter.Dunlap@Sun.COM !list_is_empty(&idm.idm_tgt_svc_list)) { 23477978SPeter.Dunlap@Sun.COM return (EBUSY); 23487978SPeter.Dunlap@Sun.COM } 23497978SPeter.Dunlap@Sun.COM 23507978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 23517978SPeter.Dunlap@Sun.COM idm.idm_wd_thread_running = B_FALSE; 23527978SPeter.Dunlap@Sun.COM cv_signal(&idm.idm_wd_cv); 23537978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 23547978SPeter.Dunlap@Sun.COM 23557978SPeter.Dunlap@Sun.COM thread_join(idm.idm_wd_thread_did); 23567978SPeter.Dunlap@Sun.COM 23577978SPeter.Dunlap@Sun.COM idm_idpool_destroy(&idm.idm_conn_id_pool); 2358*9162SPeter.Dunlap@Sun.COM 2359*9162SPeter.Dunlap@Sun.COM /* Close any LDI handles we have open on transport drivers */ 2360*9162SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 2361*9162SPeter.Dunlap@Sun.COM idm_transport_teardown(); 2362*9162SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 2363*9162SPeter.Dunlap@Sun.COM 2364*9162SPeter.Dunlap@Sun.COM /* Teardown the native sockets transport */ 23657978SPeter.Dunlap@Sun.COM idm_so_fini(); 2366*9162SPeter.Dunlap@Sun.COM 23677978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_ini_conn_list); 23687978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_tgt_conn_list); 23697978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_tgt_svc_list); 23707978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_task_cache); 23717978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_buf_cache); 23727978SPeter.Dunlap@Sun.COM kmem_free(idm.idm_taskid_table, 23737978SPeter.Dunlap@Sun.COM idm.idm_taskid_max * sizeof (idm_task_t *)); 23747978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 23757978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 23767978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 23777978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 23787978SPeter.Dunlap@Sun.COM 23797978SPeter.Dunlap@Sun.COM return (0); 23807978SPeter.Dunlap@Sun.COM } 2381