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 /* 227978SPeter.Dunlap@Sun.COM * Copyright 2008 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); 697978SPeter.Dunlap@Sun.COM 707978SPeter.Dunlap@Sun.COM boolean_t idm_conn_logging = 0; 717978SPeter.Dunlap@Sun.COM boolean_t idm_svc_logging = 0; 727978SPeter.Dunlap@Sun.COM 737978SPeter.Dunlap@Sun.COM /* 747978SPeter.Dunlap@Sun.COM * Potential tuneable for the maximum number of tasks. Default to 757978SPeter.Dunlap@Sun.COM * IDM_TASKIDS_MAX 767978SPeter.Dunlap@Sun.COM */ 777978SPeter.Dunlap@Sun.COM 787978SPeter.Dunlap@Sun.COM uint32_t idm_max_taskids = IDM_TASKIDS_MAX; 797978SPeter.Dunlap@Sun.COM 807978SPeter.Dunlap@Sun.COM /* 817978SPeter.Dunlap@Sun.COM * Global list of transport handles 827978SPeter.Dunlap@Sun.COM * These are listed in preferential order, so we can simply take the 837978SPeter.Dunlap@Sun.COM * first "it_conn_is_capable" hit. Note also that the order maps to 847978SPeter.Dunlap@Sun.COM * the order of the idm_transport_type_t list. 857978SPeter.Dunlap@Sun.COM */ 867978SPeter.Dunlap@Sun.COM idm_transport_t idm_transport_list[] = { 877978SPeter.Dunlap@Sun.COM 887978SPeter.Dunlap@Sun.COM /* iSER on InfiniBand transport handle */ 897978SPeter.Dunlap@Sun.COM {IDM_TRANSPORT_TYPE_ISER, /* type */ 907978SPeter.Dunlap@Sun.COM "/devices/ib/iser@0:iser", /* device path */ 917978SPeter.Dunlap@Sun.COM NULL, /* LDI handle */ 927978SPeter.Dunlap@Sun.COM NULL, /* transport ops */ 937978SPeter.Dunlap@Sun.COM NULL}, /* transport caps */ 947978SPeter.Dunlap@Sun.COM 957978SPeter.Dunlap@Sun.COM /* IDM native sockets transport handle */ 967978SPeter.Dunlap@Sun.COM {IDM_TRANSPORT_TYPE_SOCKETS, /* type */ 977978SPeter.Dunlap@Sun.COM NULL, /* 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 }; 1037978SPeter.Dunlap@Sun.COM 1047978SPeter.Dunlap@Sun.COM int 1057978SPeter.Dunlap@Sun.COM _init(void) 1067978SPeter.Dunlap@Sun.COM { 1077978SPeter.Dunlap@Sun.COM int rc; 1087978SPeter.Dunlap@Sun.COM 1097978SPeter.Dunlap@Sun.COM if ((rc = _idm_init()) != 0) { 1107978SPeter.Dunlap@Sun.COM return (rc); 1117978SPeter.Dunlap@Sun.COM } 1127978SPeter.Dunlap@Sun.COM 1137978SPeter.Dunlap@Sun.COM return (mod_install(&modlinkage)); 1147978SPeter.Dunlap@Sun.COM } 1157978SPeter.Dunlap@Sun.COM 1167978SPeter.Dunlap@Sun.COM int 1177978SPeter.Dunlap@Sun.COM _fini(void) 1187978SPeter.Dunlap@Sun.COM { 1197978SPeter.Dunlap@Sun.COM int rc; 1207978SPeter.Dunlap@Sun.COM 1217978SPeter.Dunlap@Sun.COM if ((rc = _idm_fini()) != 0) { 1227978SPeter.Dunlap@Sun.COM return (rc); 1237978SPeter.Dunlap@Sun.COM } 1247978SPeter.Dunlap@Sun.COM 1257978SPeter.Dunlap@Sun.COM if ((rc = mod_remove(&modlinkage)) != 0) { 1267978SPeter.Dunlap@Sun.COM return (rc); 1277978SPeter.Dunlap@Sun.COM } 1287978SPeter.Dunlap@Sun.COM 1297978SPeter.Dunlap@Sun.COM return (rc); 1307978SPeter.Dunlap@Sun.COM } 1317978SPeter.Dunlap@Sun.COM 1327978SPeter.Dunlap@Sun.COM int 1337978SPeter.Dunlap@Sun.COM _info(struct modinfo *modinfop) 1347978SPeter.Dunlap@Sun.COM { 1357978SPeter.Dunlap@Sun.COM return (mod_info(&modlinkage, modinfop)); 1367978SPeter.Dunlap@Sun.COM } 1377978SPeter.Dunlap@Sun.COM 1387978SPeter.Dunlap@Sun.COM /* 1397978SPeter.Dunlap@Sun.COM * idm_transport_register() 1407978SPeter.Dunlap@Sun.COM * 1417978SPeter.Dunlap@Sun.COM * Provides a mechanism for an IDM transport driver to register its 1427978SPeter.Dunlap@Sun.COM * transport ops and caps with the IDM kernel module. Invoked during 1437978SPeter.Dunlap@Sun.COM * a transport driver's attach routine. 1447978SPeter.Dunlap@Sun.COM */ 1457978SPeter.Dunlap@Sun.COM idm_status_t 1467978SPeter.Dunlap@Sun.COM idm_transport_register(idm_transport_attr_t *attr) 1477978SPeter.Dunlap@Sun.COM { 1487978SPeter.Dunlap@Sun.COM ASSERT(attr->it_ops != NULL); 1497978SPeter.Dunlap@Sun.COM ASSERT(attr->it_caps != NULL); 1507978SPeter.Dunlap@Sun.COM 1517978SPeter.Dunlap@Sun.COM switch (attr->type) { 1527978SPeter.Dunlap@Sun.COM /* All known non-native transports here; for now, iSER */ 1537978SPeter.Dunlap@Sun.COM case IDM_TRANSPORT_TYPE_ISER: 1547978SPeter.Dunlap@Sun.COM idm_transport_list[attr->type].it_ops = attr->it_ops; 1557978SPeter.Dunlap@Sun.COM idm_transport_list[attr->type].it_caps = attr->it_caps; 1567978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 1577978SPeter.Dunlap@Sun.COM 1587978SPeter.Dunlap@Sun.COM default: 1597978SPeter.Dunlap@Sun.COM cmn_err(CE_NOTE, "idm: unknown transport type (0x%x) in " 1607978SPeter.Dunlap@Sun.COM "idm_transport_register", attr->type); 1617978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 1627978SPeter.Dunlap@Sun.COM } 1637978SPeter.Dunlap@Sun.COM } 1647978SPeter.Dunlap@Sun.COM 1657978SPeter.Dunlap@Sun.COM /* 1667978SPeter.Dunlap@Sun.COM * idm_ini_conn_create 1677978SPeter.Dunlap@Sun.COM * 1687978SPeter.Dunlap@Sun.COM * This function is invoked by the iSCSI layer to create a connection context. 1697978SPeter.Dunlap@Sun.COM * This does not actually establish the socket connection. 1707978SPeter.Dunlap@Sun.COM * 1717978SPeter.Dunlap@Sun.COM * cr - Connection request parameters 1727978SPeter.Dunlap@Sun.COM * new_con - Output parameter that contains the new request if successful 1737978SPeter.Dunlap@Sun.COM * 1747978SPeter.Dunlap@Sun.COM */ 1757978SPeter.Dunlap@Sun.COM idm_status_t 1767978SPeter.Dunlap@Sun.COM idm_ini_conn_create(idm_conn_req_t *cr, idm_conn_t **new_con) 1777978SPeter.Dunlap@Sun.COM { 1787978SPeter.Dunlap@Sun.COM idm_transport_t *it; 1797978SPeter.Dunlap@Sun.COM idm_conn_t *ic; 1807978SPeter.Dunlap@Sun.COM int rc; 1817978SPeter.Dunlap@Sun.COM 1827978SPeter.Dunlap@Sun.COM it = idm_transport_lookup(cr); 1837978SPeter.Dunlap@Sun.COM 1847978SPeter.Dunlap@Sun.COM retry: 1857978SPeter.Dunlap@Sun.COM ic = idm_conn_create_common(CONN_TYPE_INI, it->it_type, 1867978SPeter.Dunlap@Sun.COM &cr->icr_conn_ops); 1877978SPeter.Dunlap@Sun.COM 1887978SPeter.Dunlap@Sun.COM bcopy(&cr->cr_ini_dst_addr, &ic->ic_ini_dst_addr, 1897978SPeter.Dunlap@Sun.COM sizeof (cr->cr_ini_dst_addr)); 1907978SPeter.Dunlap@Sun.COM 1917978SPeter.Dunlap@Sun.COM /* create the transport-specific connection components */ 1927978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_ini_conn_create(cr, ic); 1937978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 1947978SPeter.Dunlap@Sun.COM /* cleanup the failed connection */ 1957978SPeter.Dunlap@Sun.COM idm_conn_destroy_common(ic); 1967978SPeter.Dunlap@Sun.COM kmem_free(ic, sizeof (idm_conn_t)); 1977978SPeter.Dunlap@Sun.COM 1987978SPeter.Dunlap@Sun.COM /* 1997978SPeter.Dunlap@Sun.COM * It is possible for an IB client to connect to 2007978SPeter.Dunlap@Sun.COM * an ethernet-only client via an IB-eth gateway. 2017978SPeter.Dunlap@Sun.COM * Therefore, if we are attempting to use iSER and 2027978SPeter.Dunlap@Sun.COM * fail, retry with sockets before ultimately 2037978SPeter.Dunlap@Sun.COM * failing the connection. 2047978SPeter.Dunlap@Sun.COM */ 2057978SPeter.Dunlap@Sun.COM if (it->it_type == IDM_TRANSPORT_TYPE_ISER) { 2067978SPeter.Dunlap@Sun.COM it = &idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]; 2077978SPeter.Dunlap@Sun.COM goto retry; 2087978SPeter.Dunlap@Sun.COM } 2097978SPeter.Dunlap@Sun.COM 2107978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 2117978SPeter.Dunlap@Sun.COM } 2127978SPeter.Dunlap@Sun.COM 2137978SPeter.Dunlap@Sun.COM *new_con = ic; 2147978SPeter.Dunlap@Sun.COM 2157978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 2167978SPeter.Dunlap@Sun.COM list_insert_tail(&idm.idm_ini_conn_list, ic); 2177978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 2187978SPeter.Dunlap@Sun.COM 2197978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 2207978SPeter.Dunlap@Sun.COM } 2217978SPeter.Dunlap@Sun.COM 2227978SPeter.Dunlap@Sun.COM /* 2237978SPeter.Dunlap@Sun.COM * idm_ini_conn_destroy 2247978SPeter.Dunlap@Sun.COM * 2257978SPeter.Dunlap@Sun.COM * Releases any resources associated with the connection. This is the 2267978SPeter.Dunlap@Sun.COM * complement to idm_ini_conn_create. 2277978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 2287978SPeter.Dunlap@Sun.COM * 2297978SPeter.Dunlap@Sun.COM */ 2307978SPeter.Dunlap@Sun.COM void 2317978SPeter.Dunlap@Sun.COM idm_ini_conn_destroy(idm_conn_t *ic) 2327978SPeter.Dunlap@Sun.COM { 2337978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 2347978SPeter.Dunlap@Sun.COM list_remove(&idm.idm_ini_conn_list, ic); 2357978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 2367978SPeter.Dunlap@Sun.COM 2377978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_ini_conn_destroy(ic); 2387978SPeter.Dunlap@Sun.COM idm_conn_destroy_common(ic); 2397978SPeter.Dunlap@Sun.COM } 2407978SPeter.Dunlap@Sun.COM 2417978SPeter.Dunlap@Sun.COM /* 2427978SPeter.Dunlap@Sun.COM * idm_ini_conn_connect 2437978SPeter.Dunlap@Sun.COM * 2447978SPeter.Dunlap@Sun.COM * Establish connection to the remote system identified in idm_conn_t. 2457978SPeter.Dunlap@Sun.COM * The connection parameters including the remote IP address were established 2467978SPeter.Dunlap@Sun.COM * in the call to idm_ini_conn_create. 2477978SPeter.Dunlap@Sun.COM * 2487978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 2497978SPeter.Dunlap@Sun.COM * 2507978SPeter.Dunlap@Sun.COM * Returns success if the connection was established, otherwise some kind 2517978SPeter.Dunlap@Sun.COM * of meaningful error code. 2527978SPeter.Dunlap@Sun.COM * 2537978SPeter.Dunlap@Sun.COM * Upon return the initiator can send a "login" request when it is ready. 2547978SPeter.Dunlap@Sun.COM */ 2557978SPeter.Dunlap@Sun.COM idm_status_t 2567978SPeter.Dunlap@Sun.COM idm_ini_conn_connect(idm_conn_t *ic) 2577978SPeter.Dunlap@Sun.COM { 2587978SPeter.Dunlap@Sun.COM idm_status_t rc; 2597978SPeter.Dunlap@Sun.COM 2607978SPeter.Dunlap@Sun.COM rc = idm_conn_sm_init(ic); 2617978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 2627978SPeter.Dunlap@Sun.COM return (ic->ic_conn_sm_status); 2637978SPeter.Dunlap@Sun.COM } 2647978SPeter.Dunlap@Sun.COM /* Kick state machine */ 2657978SPeter.Dunlap@Sun.COM idm_conn_event(ic, CE_CONNECT_REQ, NULL); 2667978SPeter.Dunlap@Sun.COM 2677978SPeter.Dunlap@Sun.COM /* Wait for login flag */ 2687978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 2697978SPeter.Dunlap@Sun.COM while (!(ic->ic_state_flags & CF_LOGIN_READY) && 2707978SPeter.Dunlap@Sun.COM !(ic->ic_state_flags & CF_ERROR)) { 2717978SPeter.Dunlap@Sun.COM cv_wait(&ic->ic_state_cv, &ic->ic_state_mutex); 2727978SPeter.Dunlap@Sun.COM } 2737978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 2747978SPeter.Dunlap@Sun.COM 2757978SPeter.Dunlap@Sun.COM if (ic->ic_state_flags & CF_ERROR) { 2767978SPeter.Dunlap@Sun.COM /* ic->ic_conn_sm_status will contains failure status */ 2777978SPeter.Dunlap@Sun.COM return (ic->ic_conn_sm_status); 2787978SPeter.Dunlap@Sun.COM } 2797978SPeter.Dunlap@Sun.COM 2807978SPeter.Dunlap@Sun.COM /* Ready to login */ 2817978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_state_flags & CF_LOGIN_READY); 2827978SPeter.Dunlap@Sun.COM (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL); 2837978SPeter.Dunlap@Sun.COM 2847978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 2857978SPeter.Dunlap@Sun.COM } 2867978SPeter.Dunlap@Sun.COM 2877978SPeter.Dunlap@Sun.COM /* 2887978SPeter.Dunlap@Sun.COM * idm_ini_conn_sm_fini_task() 2897978SPeter.Dunlap@Sun.COM * 2907978SPeter.Dunlap@Sun.COM * Dispatch a thread on the global taskq to tear down an initiator connection's 2917978SPeter.Dunlap@Sun.COM * state machine. Note: We cannot do this from the disconnect thread as we will 2927978SPeter.Dunlap@Sun.COM * end up in a situation wherein the thread is running on a taskq that it then 2937978SPeter.Dunlap@Sun.COM * attempts to destroy. 2947978SPeter.Dunlap@Sun.COM */ 2957978SPeter.Dunlap@Sun.COM static void 2967978SPeter.Dunlap@Sun.COM idm_ini_conn_sm_fini_task(void *ic_void) 2977978SPeter.Dunlap@Sun.COM { 2987978SPeter.Dunlap@Sun.COM idm_conn_sm_fini((idm_conn_t *)ic_void); 2997978SPeter.Dunlap@Sun.COM } 3007978SPeter.Dunlap@Sun.COM 3017978SPeter.Dunlap@Sun.COM /* 3027978SPeter.Dunlap@Sun.COM * idm_ini_conn_disconnect 3037978SPeter.Dunlap@Sun.COM * 3047978SPeter.Dunlap@Sun.COM * Forces a connection (previously established using idm_ini_conn_connect) 3057978SPeter.Dunlap@Sun.COM * to perform a controlled shutdown, cleaning up any outstanding requests. 3067978SPeter.Dunlap@Sun.COM * 3077978SPeter.Dunlap@Sun.COM * ic - idm_conn_t structure representing the relevant connection 3087978SPeter.Dunlap@Sun.COM * 3097978SPeter.Dunlap@Sun.COM * This is synchronous and it will return when the connection has been 3107978SPeter.Dunlap@Sun.COM * properly shutdown. 3117978SPeter.Dunlap@Sun.COM */ 3127978SPeter.Dunlap@Sun.COM /* ARGSUSED */ 3137978SPeter.Dunlap@Sun.COM void 3147978SPeter.Dunlap@Sun.COM idm_ini_conn_disconnect(idm_conn_t *ic) 3157978SPeter.Dunlap@Sun.COM { 3167978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 3177978SPeter.Dunlap@Sun.COM 3187978SPeter.Dunlap@Sun.COM if (ic->ic_state_flags == 0) { 3197978SPeter.Dunlap@Sun.COM /* already disconnected */ 3207978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 3217978SPeter.Dunlap@Sun.COM return; 3227978SPeter.Dunlap@Sun.COM } 3237978SPeter.Dunlap@Sun.COM ic->ic_state_flags = 0; 3247978SPeter.Dunlap@Sun.COM ic->ic_conn_sm_status = 0; 3257978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 3267978SPeter.Dunlap@Sun.COM 3277978SPeter.Dunlap@Sun.COM /* invoke the transport-specific conn_destroy */ 3287978SPeter.Dunlap@Sun.COM (void) ic->ic_transport_ops->it_ini_conn_disconnect(ic); 3297978SPeter.Dunlap@Sun.COM 3307978SPeter.Dunlap@Sun.COM /* teardown the connection sm */ 3317978SPeter.Dunlap@Sun.COM (void) taskq_dispatch(idm.idm_global_taskq, &idm_ini_conn_sm_fini_task, 3327978SPeter.Dunlap@Sun.COM (void *)ic, TQ_SLEEP); 3337978SPeter.Dunlap@Sun.COM } 3347978SPeter.Dunlap@Sun.COM 3357978SPeter.Dunlap@Sun.COM /* 3367978SPeter.Dunlap@Sun.COM * idm_tgt_svc_create 3377978SPeter.Dunlap@Sun.COM * 3387978SPeter.Dunlap@Sun.COM * The target calls this service to obtain a service context for each available 3397978SPeter.Dunlap@Sun.COM * transport, starting a service of each type related to the IP address and port 3407978SPeter.Dunlap@Sun.COM * passed. The idm_svc_req_t contains the service parameters. 3417978SPeter.Dunlap@Sun.COM */ 3427978SPeter.Dunlap@Sun.COM idm_status_t 3437978SPeter.Dunlap@Sun.COM idm_tgt_svc_create(idm_svc_req_t *sr, idm_svc_t **new_svc) 3447978SPeter.Dunlap@Sun.COM { 3457978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 3467978SPeter.Dunlap@Sun.COM idm_transport_t *it; 3477978SPeter.Dunlap@Sun.COM idm_svc_t *is; 3487978SPeter.Dunlap@Sun.COM int rc; 3497978SPeter.Dunlap@Sun.COM 3507978SPeter.Dunlap@Sun.COM *new_svc = NULL; 3517978SPeter.Dunlap@Sun.COM is = kmem_zalloc(sizeof (idm_svc_t), KM_SLEEP); 3527978SPeter.Dunlap@Sun.COM 3537978SPeter.Dunlap@Sun.COM /* Initialize transport-agnostic components of the service handle */ 3547978SPeter.Dunlap@Sun.COM is->is_svc_req = *sr; 3557978SPeter.Dunlap@Sun.COM mutex_init(&is->is_mutex, NULL, MUTEX_DEFAULT, NULL); 3567978SPeter.Dunlap@Sun.COM cv_init(&is->is_cv, NULL, CV_DEFAULT, NULL); 3577978SPeter.Dunlap@Sun.COM mutex_init(&is->is_count_mutex, NULL, MUTEX_DEFAULT, NULL); 3587978SPeter.Dunlap@Sun.COM cv_init(&is->is_count_cv, NULL, CV_DEFAULT, NULL); 3597978SPeter.Dunlap@Sun.COM idm_refcnt_init(&is->is_refcnt, is); 3607978SPeter.Dunlap@Sun.COM 3617978SPeter.Dunlap@Sun.COM /* 3627978SPeter.Dunlap@Sun.COM * Make sure all available transports are setup. We call this now 3637978SPeter.Dunlap@Sun.COM * instead of at initialization time in case IB has become available 3647978SPeter.Dunlap@Sun.COM * since we started (hotplug, etc). 3657978SPeter.Dunlap@Sun.COM */ 3667978SPeter.Dunlap@Sun.COM idm_transport_setup(sr->sr_li); 3677978SPeter.Dunlap@Sun.COM 3687978SPeter.Dunlap@Sun.COM /* 3697978SPeter.Dunlap@Sun.COM * Loop through the transports, configuring the transport-specific 3707978SPeter.Dunlap@Sun.COM * components of each one. 3717978SPeter.Dunlap@Sun.COM */ 3727978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 3737978SPeter.Dunlap@Sun.COM 3747978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 3757978SPeter.Dunlap@Sun.COM /* 3767978SPeter.Dunlap@Sun.COM * If it_ops is NULL then the transport is unconfigured 3777978SPeter.Dunlap@Sun.COM * and we shouldn't try to start the service. 3787978SPeter.Dunlap@Sun.COM */ 3797978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 3807978SPeter.Dunlap@Sun.COM continue; 3817978SPeter.Dunlap@Sun.COM } 3827978SPeter.Dunlap@Sun.COM 3837978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_tgt_svc_create(sr, is); 3847978SPeter.Dunlap@Sun.COM if (rc != IDM_STATUS_SUCCESS) { 3857978SPeter.Dunlap@Sun.COM /* Teardown any configured services */ 3867978SPeter.Dunlap@Sun.COM while (type--) { 3877978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 3887978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 3897978SPeter.Dunlap@Sun.COM continue; 3907978SPeter.Dunlap@Sun.COM } 3917978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_destroy(is); 3927978SPeter.Dunlap@Sun.COM } 3937978SPeter.Dunlap@Sun.COM /* Free the svc context and return */ 3947978SPeter.Dunlap@Sun.COM kmem_free(is, sizeof (idm_svc_t)); 3957978SPeter.Dunlap@Sun.COM return (rc); 3967978SPeter.Dunlap@Sun.COM } 3977978SPeter.Dunlap@Sun.COM } 3987978SPeter.Dunlap@Sun.COM 3997978SPeter.Dunlap@Sun.COM *new_svc = is; 4007978SPeter.Dunlap@Sun.COM 4017978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 4027978SPeter.Dunlap@Sun.COM list_insert_tail(&idm.idm_tgt_svc_list, is); 4037978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 4047978SPeter.Dunlap@Sun.COM 4057978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 4067978SPeter.Dunlap@Sun.COM } 4077978SPeter.Dunlap@Sun.COM 4087978SPeter.Dunlap@Sun.COM /* 4097978SPeter.Dunlap@Sun.COM * idm_tgt_svc_destroy 4107978SPeter.Dunlap@Sun.COM * 4117978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 4127978SPeter.Dunlap@Sun.COM * 4137978SPeter.Dunlap@Sun.COM * Cleanup any resources associated with the idm_svc_t. 4147978SPeter.Dunlap@Sun.COM */ 4157978SPeter.Dunlap@Sun.COM void 4167978SPeter.Dunlap@Sun.COM idm_tgt_svc_destroy(idm_svc_t *is) 4177978SPeter.Dunlap@Sun.COM { 4187978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 4197978SPeter.Dunlap@Sun.COM idm_transport_t *it; 4207978SPeter.Dunlap@Sun.COM 4217978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 4227978SPeter.Dunlap@Sun.COM /* remove this service from the global list */ 4237978SPeter.Dunlap@Sun.COM list_remove(&idm.idm_tgt_svc_list, is); 4247978SPeter.Dunlap@Sun.COM /* wakeup any waiters for service change */ 4257978SPeter.Dunlap@Sun.COM cv_broadcast(&idm.idm_tgt_svc_cv); 4267978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 4277978SPeter.Dunlap@Sun.COM 4287978SPeter.Dunlap@Sun.COM /* tear down the svc resources */ 4297978SPeter.Dunlap@Sun.COM idm_refcnt_destroy(&is->is_refcnt); 4307978SPeter.Dunlap@Sun.COM cv_destroy(&is->is_count_cv); 4317978SPeter.Dunlap@Sun.COM mutex_destroy(&is->is_count_mutex); 4327978SPeter.Dunlap@Sun.COM cv_destroy(&is->is_cv); 4337978SPeter.Dunlap@Sun.COM mutex_destroy(&is->is_mutex); 4347978SPeter.Dunlap@Sun.COM 4357978SPeter.Dunlap@Sun.COM /* teardown each transport-specific service */ 4367978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 4377978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 4387978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 4397978SPeter.Dunlap@Sun.COM continue; 4407978SPeter.Dunlap@Sun.COM } 4417978SPeter.Dunlap@Sun.COM 4427978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_destroy(is); 4437978SPeter.Dunlap@Sun.COM } 4447978SPeter.Dunlap@Sun.COM 4457978SPeter.Dunlap@Sun.COM /* free the svc handle */ 4467978SPeter.Dunlap@Sun.COM kmem_free(is, sizeof (idm_svc_t)); 4477978SPeter.Dunlap@Sun.COM } 4487978SPeter.Dunlap@Sun.COM 4497978SPeter.Dunlap@Sun.COM void 4507978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(idm_svc_t *is) 4517978SPeter.Dunlap@Sun.COM { 4527978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&is->is_refcnt); 4537978SPeter.Dunlap@Sun.COM } 4547978SPeter.Dunlap@Sun.COM 4557978SPeter.Dunlap@Sun.COM void 4567978SPeter.Dunlap@Sun.COM idm_tgt_svc_rele_and_destroy(idm_svc_t *is) 4577978SPeter.Dunlap@Sun.COM { 4587978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(&is->is_refcnt, 4597978SPeter.Dunlap@Sun.COM (idm_refcnt_cb_t *)&idm_tgt_svc_destroy); 4607978SPeter.Dunlap@Sun.COM } 4617978SPeter.Dunlap@Sun.COM 4627978SPeter.Dunlap@Sun.COM /* 4637978SPeter.Dunlap@Sun.COM * idm_tgt_svc_online 4647978SPeter.Dunlap@Sun.COM * 4657978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 4667978SPeter.Dunlap@Sun.COM * 4677978SPeter.Dunlap@Sun.COM * Online each transport service, as we want this target to be accessible 4687978SPeter.Dunlap@Sun.COM * via any configured transport. 4697978SPeter.Dunlap@Sun.COM * 4707978SPeter.Dunlap@Sun.COM * When the initiator establishes a new connection to the target, IDM will 4717978SPeter.Dunlap@Sun.COM * call the "new connect" callback defined in the idm_svc_req_t structure 4727978SPeter.Dunlap@Sun.COM * and it will pass an idm_conn_t structure representing that new connection. 4737978SPeter.Dunlap@Sun.COM */ 4747978SPeter.Dunlap@Sun.COM idm_status_t 4757978SPeter.Dunlap@Sun.COM idm_tgt_svc_online(idm_svc_t *is) 4767978SPeter.Dunlap@Sun.COM { 4777978SPeter.Dunlap@Sun.COM 4787978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 4797978SPeter.Dunlap@Sun.COM idm_transport_t *it; 4807978SPeter.Dunlap@Sun.COM int rc; 4817978SPeter.Dunlap@Sun.COM int svc_found; 4827978SPeter.Dunlap@Sun.COM 4837978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 4847978SPeter.Dunlap@Sun.COM /* Walk through each of the transports and online them */ 4857978SPeter.Dunlap@Sun.COM if (is->is_online == 0) { 4867978SPeter.Dunlap@Sun.COM svc_found = 0; 4877978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 4887978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 4897978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 4907978SPeter.Dunlap@Sun.COM /* transport is not registered */ 4917978SPeter.Dunlap@Sun.COM continue; 4927978SPeter.Dunlap@Sun.COM } 4937978SPeter.Dunlap@Sun.COM 4947978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 4957978SPeter.Dunlap@Sun.COM rc = it->it_ops->it_tgt_svc_online(is); 4967978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 4977978SPeter.Dunlap@Sun.COM if (rc == IDM_STATUS_SUCCESS) { 4987978SPeter.Dunlap@Sun.COM /* We have at least one service running. */ 4997978SPeter.Dunlap@Sun.COM svc_found = 1; 5007978SPeter.Dunlap@Sun.COM } 5017978SPeter.Dunlap@Sun.COM } 5027978SPeter.Dunlap@Sun.COM } else { 5037978SPeter.Dunlap@Sun.COM svc_found = 1; 5047978SPeter.Dunlap@Sun.COM } 5057978SPeter.Dunlap@Sun.COM if (svc_found) 5067978SPeter.Dunlap@Sun.COM is->is_online++; 5077978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5087978SPeter.Dunlap@Sun.COM 5097978SPeter.Dunlap@Sun.COM return (svc_found ? IDM_STATUS_SUCCESS : IDM_STATUS_FAIL); 5107978SPeter.Dunlap@Sun.COM } 5117978SPeter.Dunlap@Sun.COM 5127978SPeter.Dunlap@Sun.COM /* 5137978SPeter.Dunlap@Sun.COM * idm_tgt_svc_offline 5147978SPeter.Dunlap@Sun.COM * 5157978SPeter.Dunlap@Sun.COM * is - idm_svc_t returned by the call to idm_tgt_svc_create 5167978SPeter.Dunlap@Sun.COM * 5177978SPeter.Dunlap@Sun.COM * Shutdown any online target services. 5187978SPeter.Dunlap@Sun.COM */ 5197978SPeter.Dunlap@Sun.COM void 5207978SPeter.Dunlap@Sun.COM idm_tgt_svc_offline(idm_svc_t *is) 5217978SPeter.Dunlap@Sun.COM { 5227978SPeter.Dunlap@Sun.COM idm_transport_type_t type; 5237978SPeter.Dunlap@Sun.COM idm_transport_t *it; 5247978SPeter.Dunlap@Sun.COM 5257978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 5267978SPeter.Dunlap@Sun.COM is->is_online--; 5277978SPeter.Dunlap@Sun.COM if (is->is_online == 0) { 5287978SPeter.Dunlap@Sun.COM /* Walk through each of the transports and offline them */ 5297978SPeter.Dunlap@Sun.COM for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 5307978SPeter.Dunlap@Sun.COM it = &idm_transport_list[type]; 5317978SPeter.Dunlap@Sun.COM if (it->it_ops == NULL) { 5327978SPeter.Dunlap@Sun.COM /* transport is not registered */ 5337978SPeter.Dunlap@Sun.COM continue; 5347978SPeter.Dunlap@Sun.COM } 5357978SPeter.Dunlap@Sun.COM 5367978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5377978SPeter.Dunlap@Sun.COM it->it_ops->it_tgt_svc_offline(is); 5387978SPeter.Dunlap@Sun.COM mutex_enter(&is->is_mutex); 5397978SPeter.Dunlap@Sun.COM } 5407978SPeter.Dunlap@Sun.COM } 5417978SPeter.Dunlap@Sun.COM mutex_exit(&is->is_mutex); 5427978SPeter.Dunlap@Sun.COM } 5437978SPeter.Dunlap@Sun.COM 5447978SPeter.Dunlap@Sun.COM /* 5457978SPeter.Dunlap@Sun.COM * idm_tgt_svc_lookup 5467978SPeter.Dunlap@Sun.COM * 5477978SPeter.Dunlap@Sun.COM * Lookup a service instance listening on the specified port 5487978SPeter.Dunlap@Sun.COM */ 5497978SPeter.Dunlap@Sun.COM 5507978SPeter.Dunlap@Sun.COM idm_svc_t * 5517978SPeter.Dunlap@Sun.COM idm_tgt_svc_lookup(uint16_t port) 5527978SPeter.Dunlap@Sun.COM { 5537978SPeter.Dunlap@Sun.COM idm_svc_t *result; 5547978SPeter.Dunlap@Sun.COM 5557978SPeter.Dunlap@Sun.COM retry: 5567978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 5577978SPeter.Dunlap@Sun.COM for (result = list_head(&idm.idm_tgt_svc_list); 5587978SPeter.Dunlap@Sun.COM result != NULL; 5597978SPeter.Dunlap@Sun.COM result = list_next(&idm.idm_tgt_svc_list, result)) { 5607978SPeter.Dunlap@Sun.COM if (result->is_svc_req.sr_port == port) { 5617978SPeter.Dunlap@Sun.COM if (result->is_online == 0) { 5627978SPeter.Dunlap@Sun.COM /* 5637978SPeter.Dunlap@Sun.COM * A service exists on this port, but it 5647978SPeter.Dunlap@Sun.COM * is going away, wait for it to cleanup. 5657978SPeter.Dunlap@Sun.COM */ 5667978SPeter.Dunlap@Sun.COM cv_wait(&idm.idm_tgt_svc_cv, 5677978SPeter.Dunlap@Sun.COM &idm.idm_global_mutex); 5687978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 5697978SPeter.Dunlap@Sun.COM goto retry; 5707978SPeter.Dunlap@Sun.COM } 5717978SPeter.Dunlap@Sun.COM idm_tgt_svc_hold(result); 5727978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 5737978SPeter.Dunlap@Sun.COM return (result); 5747978SPeter.Dunlap@Sun.COM } 5757978SPeter.Dunlap@Sun.COM } 5767978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 5777978SPeter.Dunlap@Sun.COM 5787978SPeter.Dunlap@Sun.COM return (NULL); 5797978SPeter.Dunlap@Sun.COM } 5807978SPeter.Dunlap@Sun.COM 5817978SPeter.Dunlap@Sun.COM /* 5827978SPeter.Dunlap@Sun.COM * idm_negotiate_key_values() 5837978SPeter.Dunlap@Sun.COM * Give IDM level a chance to negotiate any login parameters it should own. 5847978SPeter.Dunlap@Sun.COM * -- leave unhandled parameters alone on request_nvl 5857978SPeter.Dunlap@Sun.COM * -- move all handled parameters to response_nvl with an appropriate response 5867978SPeter.Dunlap@Sun.COM * -- also add an entry to negotiated_nvl for any accepted parameters 5877978SPeter.Dunlap@Sun.COM */ 5887978SPeter.Dunlap@Sun.COM kv_status_t 5897978SPeter.Dunlap@Sun.COM idm_negotiate_key_values(idm_conn_t *ic, nvlist_t *request_nvl, 5907978SPeter.Dunlap@Sun.COM nvlist_t *response_nvl, nvlist_t *negotiated_nvl) 5917978SPeter.Dunlap@Sun.COM { 5927978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_transport_ops != NULL); 5937978SPeter.Dunlap@Sun.COM return (ic->ic_transport_ops->it_negotiate_key_values(ic, 5947978SPeter.Dunlap@Sun.COM request_nvl, response_nvl, negotiated_nvl)); 5957978SPeter.Dunlap@Sun.COM } 5967978SPeter.Dunlap@Sun.COM 5977978SPeter.Dunlap@Sun.COM /* 5987978SPeter.Dunlap@Sun.COM * idm_notice_key_values() 5997978SPeter.Dunlap@Sun.COM * Activate at the IDM level any parameters that have been negotiated. 6007978SPeter.Dunlap@Sun.COM * Passes the set of key value pairs to the transport for activation. 6017978SPeter.Dunlap@Sun.COM * This will be invoked as the connection is entering full-feature mode. 6027978SPeter.Dunlap@Sun.COM */ 6037978SPeter.Dunlap@Sun.COM idm_status_t 6047978SPeter.Dunlap@Sun.COM idm_notice_key_values(idm_conn_t *ic, nvlist_t *negotiated_nvl) 6057978SPeter.Dunlap@Sun.COM { 6067978SPeter.Dunlap@Sun.COM ASSERT(ic->ic_transport_ops != NULL); 6077978SPeter.Dunlap@Sun.COM return (ic->ic_transport_ops->it_notice_key_values(ic, 6087978SPeter.Dunlap@Sun.COM negotiated_nvl)); 6097978SPeter.Dunlap@Sun.COM } 6107978SPeter.Dunlap@Sun.COM 6117978SPeter.Dunlap@Sun.COM /* 6127978SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini 6137978SPeter.Dunlap@Sun.COM * 6147978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Put_Data' operational primitive. 6157978SPeter.Dunlap@Sun.COM * 6167978SPeter.Dunlap@Sun.COM * This function is invoked by a target iSCSI layer to request its local 6177978SPeter.Dunlap@Sun.COM * Datamover layer to transmit the Data-In PDU to the peer iSCSI layer 6187978SPeter.Dunlap@Sun.COM * on the remote iSCSI node. The I/O buffer represented by 'idb' is 6197978SPeter.Dunlap@Sun.COM * transferred to the initiator associated with task 'idt'. The connection 6207978SPeter.Dunlap@Sun.COM * info, contents of the Data-In PDU header, the DataDescriptorIn, BHS, 6217978SPeter.Dunlap@Sun.COM * and the callback (idb->idb_buf_cb) at transfer completion are 6227978SPeter.Dunlap@Sun.COM * provided as input. 6237978SPeter.Dunlap@Sun.COM * 6247978SPeter.Dunlap@Sun.COM * This data transfer takes place transparently to the remote iSCSI layer, 6257978SPeter.Dunlap@Sun.COM * i.e. without its participation. 6267978SPeter.Dunlap@Sun.COM * 6277978SPeter.Dunlap@Sun.COM * Using sockets, IDM implements the data transfer by segmenting the data 6287978SPeter.Dunlap@Sun.COM * buffer into appropriately sized iSCSI PDUs and transmitting them to the 6297978SPeter.Dunlap@Sun.COM * initiator. iSER performs the transfer using RDMA write. 6307978SPeter.Dunlap@Sun.COM * 6317978SPeter.Dunlap@Sun.COM */ 6327978SPeter.Dunlap@Sun.COM idm_status_t 6337978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini(idm_task_t *idt, idm_buf_t *idb, 6347978SPeter.Dunlap@Sun.COM uint32_t offset, uint32_t xfer_len, 6357978SPeter.Dunlap@Sun.COM idm_buf_cb_t idb_buf_cb, void *cb_arg) 6367978SPeter.Dunlap@Sun.COM { 6377978SPeter.Dunlap@Sun.COM idm_status_t rc; 6387978SPeter.Dunlap@Sun.COM 6397978SPeter.Dunlap@Sun.COM idb->idb_bufoffset = offset; 6407978SPeter.Dunlap@Sun.COM idb->idb_xfer_len = xfer_len; 6417978SPeter.Dunlap@Sun.COM idb->idb_buf_cb = idb_buf_cb; 6427978SPeter.Dunlap@Sun.COM idb->idb_cb_arg = cb_arg; 6437978SPeter.Dunlap@Sun.COM 6447978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 6457978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 6467978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 6477978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_start++; 6487978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 6497978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, idb); 6507978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_TRUE; 6517978SPeter.Dunlap@Sun.COM rc = (*idt->idt_ic->ic_transport_ops->it_buf_tx_to_ini) 6527978SPeter.Dunlap@Sun.COM (idt, idb); 6537978SPeter.Dunlap@Sun.COM return (rc); 6547978SPeter.Dunlap@Sun.COM 6557978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 6567978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 6577978SPeter.Dunlap@Sun.COM /* 6587978SPeter.Dunlap@Sun.COM * Bind buffer but don't start a transfer since the task 6597978SPeter.Dunlap@Sun.COM * is suspended 6607978SPeter.Dunlap@Sun.COM */ 6617978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, idb); 6627978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 6637978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 6647978SPeter.Dunlap@Sun.COM 6657978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 6667978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 6677978SPeter.Dunlap@Sun.COM /* 6687978SPeter.Dunlap@Sun.COM * Once the task is aborted, any buffers added to the 6697978SPeter.Dunlap@Sun.COM * idt_inbufv will never get cleaned up, so just return 6707978SPeter.Dunlap@Sun.COM * SUCCESS. The buffer should get cleaned up by the 6717978SPeter.Dunlap@Sun.COM * client or framework once task_aborted has completed. 6727978SPeter.Dunlap@Sun.COM */ 6737978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 6747978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 6757978SPeter.Dunlap@Sun.COM 6767978SPeter.Dunlap@Sun.COM default: 6777978SPeter.Dunlap@Sun.COM ASSERT(0); 6787978SPeter.Dunlap@Sun.COM break; 6797978SPeter.Dunlap@Sun.COM } 6807978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 6817978SPeter.Dunlap@Sun.COM 6827978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 6837978SPeter.Dunlap@Sun.COM } 6847978SPeter.Dunlap@Sun.COM 6857978SPeter.Dunlap@Sun.COM /* 6867978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini 6877978SPeter.Dunlap@Sun.COM * 6887978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Get_Data' operational primitive. 6897978SPeter.Dunlap@Sun.COM * 6907978SPeter.Dunlap@Sun.COM * This function is invoked by a target iSCSI layer to request its local 6917978SPeter.Dunlap@Sun.COM * Datamover layer to retrieve certain data identified by the R2T PDU from the 6927978SPeter.Dunlap@Sun.COM * peer iSCSI layer on the remote node. The retrieved Data-Out PDU will be 6937978SPeter.Dunlap@Sun.COM * mapped to the respective buffer by the task tags (ITT & TTT). 6947978SPeter.Dunlap@Sun.COM * The connection information, contents of an R2T PDU, DataDescriptor, BHS, and 6957978SPeter.Dunlap@Sun.COM * the callback (idb->idb_buf_cb) notification for data transfer completion are 6967978SPeter.Dunlap@Sun.COM * are provided as input. 6977978SPeter.Dunlap@Sun.COM * 6987978SPeter.Dunlap@Sun.COM * When an iSCSI node sends an R2T PDU to its local Datamover layer, the local 6997978SPeter.Dunlap@Sun.COM * Datamover layer, the local and remote Datamover layers transparently bring 7007978SPeter.Dunlap@Sun.COM * about the data transfer requested by the R2T PDU, without the participation 7017978SPeter.Dunlap@Sun.COM * of the iSCSI layers. 7027978SPeter.Dunlap@Sun.COM * 7037978SPeter.Dunlap@Sun.COM * Using sockets, IDM transmits an R2T PDU for each buffer and the rx_data_out() 7047978SPeter.Dunlap@Sun.COM * assembles the Data-Out PDUs into the buffer. iSER uses RDMA read. 7057978SPeter.Dunlap@Sun.COM * 7067978SPeter.Dunlap@Sun.COM */ 7077978SPeter.Dunlap@Sun.COM idm_status_t 7087978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini(idm_task_t *idt, idm_buf_t *idb, 7097978SPeter.Dunlap@Sun.COM uint32_t offset, uint32_t xfer_len, 7107978SPeter.Dunlap@Sun.COM idm_buf_cb_t idb_buf_cb, void *cb_arg) 7117978SPeter.Dunlap@Sun.COM { 7127978SPeter.Dunlap@Sun.COM idm_status_t rc; 7137978SPeter.Dunlap@Sun.COM 7147978SPeter.Dunlap@Sun.COM idb->idb_bufoffset = offset; 7157978SPeter.Dunlap@Sun.COM idb->idb_xfer_len = xfer_len; 7167978SPeter.Dunlap@Sun.COM idb->idb_buf_cb = idb_buf_cb; 7177978SPeter.Dunlap@Sun.COM idb->idb_cb_arg = cb_arg; 7187978SPeter.Dunlap@Sun.COM 7197978SPeter.Dunlap@Sun.COM /* 7207978SPeter.Dunlap@Sun.COM * "In" buf list is for "Data In" PDU's, "Out" buf list is for 7217978SPeter.Dunlap@Sun.COM * "Data Out" PDU's 7227978SPeter.Dunlap@Sun.COM */ 7237978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 7247978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 7257978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 7267978SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_start++; 7277978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 7287978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, idb); 7297978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_TRUE; 7307978SPeter.Dunlap@Sun.COM rc = (*idt->idt_ic->ic_transport_ops->it_buf_rx_from_ini) 7317978SPeter.Dunlap@Sun.COM (idt, idb); 7327978SPeter.Dunlap@Sun.COM return (rc); 7337978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 7347978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 7357978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 7367978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 7377978SPeter.Dunlap@Sun.COM /* 7387978SPeter.Dunlap@Sun.COM * Bind buffer but don't start a transfer since the task 7397978SPeter.Dunlap@Sun.COM * is suspended 7407978SPeter.Dunlap@Sun.COM */ 7417978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, idb); 7427978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7437978SPeter.Dunlap@Sun.COM return (IDM_STATUS_SUCCESS); 7447978SPeter.Dunlap@Sun.COM default: 7457978SPeter.Dunlap@Sun.COM ASSERT(0); 7467978SPeter.Dunlap@Sun.COM break; 7477978SPeter.Dunlap@Sun.COM } 7487978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7497978SPeter.Dunlap@Sun.COM 7507978SPeter.Dunlap@Sun.COM return (IDM_STATUS_FAIL); 7517978SPeter.Dunlap@Sun.COM } 7527978SPeter.Dunlap@Sun.COM 7537978SPeter.Dunlap@Sun.COM /* 7547978SPeter.Dunlap@Sun.COM * idm_buf_tx_to_ini_done 7557978SPeter.Dunlap@Sun.COM * 7567978SPeter.Dunlap@Sun.COM * The transport calls this after it has completed a transfer requested by 7577978SPeter.Dunlap@Sun.COM * a call to transport_buf_tx_to_ini 7587978SPeter.Dunlap@Sun.COM * 7597978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning. 7607978SPeter.Dunlap@Sun.COM * idt may be freed after the call to idb->idb_buf_cb. 7617978SPeter.Dunlap@Sun.COM */ 7627978SPeter.Dunlap@Sun.COM void 7637978SPeter.Dunlap@Sun.COM idm_buf_tx_to_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status) 7647978SPeter.Dunlap@Sun.COM { 7657978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 7667978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_FALSE; 7677978SPeter.Dunlap@Sun.COM idb->idb_tx_thread = B_FALSE; 7687978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_done++; 7697978SPeter.Dunlap@Sun.COM 7707978SPeter.Dunlap@Sun.COM /* 7717978SPeter.Dunlap@Sun.COM * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or 7727978SPeter.Dunlap@Sun.COM * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes 7737978SPeter.Dunlap@Sun.COM * to 0. 7747978SPeter.Dunlap@Sun.COM */ 7757978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 7767978SPeter.Dunlap@Sun.COM idb->idb_status = status; 7777978SPeter.Dunlap@Sun.COM 7787978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 7797978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 7807978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, idb); 7817978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 7827978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, status); 7837978SPeter.Dunlap@Sun.COM return; 7847978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 7857978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 7867978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 7877978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 7887978SPeter.Dunlap@Sun.COM /* 7897978SPeter.Dunlap@Sun.COM * To keep things simple we will ignore the case where the 7907978SPeter.Dunlap@Sun.COM * transfer was successful and leave all buffers bound to the 7917978SPeter.Dunlap@Sun.COM * task. This allows us to also ignore the case where we've 7927978SPeter.Dunlap@Sun.COM * been asked to abort a task but the last transfer of the 7937978SPeter.Dunlap@Sun.COM * task has completed. IDM has no idea whether this was, in 7947978SPeter.Dunlap@Sun.COM * fact, the last transfer of the task so it would be difficult 7957978SPeter.Dunlap@Sun.COM * to handle this case. Everything should get sorted out again 7967978SPeter.Dunlap@Sun.COM * after task reassignment is complete. 7977978SPeter.Dunlap@Sun.COM * 7987978SPeter.Dunlap@Sun.COM * In the case of TASK_ABORTING we could conceivably call the 7997978SPeter.Dunlap@Sun.COM * buffer callback here but the timing of when the client's 8007978SPeter.Dunlap@Sun.COM * client_task_aborted callback is invoked vs. when the client's 8017978SPeter.Dunlap@Sun.COM * buffer callback gets invoked gets sticky. We don't want 8027978SPeter.Dunlap@Sun.COM * the client to here from us again after the call to 8037978SPeter.Dunlap@Sun.COM * client_task_aborted() but we don't want to give it a bunch 8047978SPeter.Dunlap@Sun.COM * of failed buffer transfers until we've called 8057978SPeter.Dunlap@Sun.COM * client_task_aborted(). Instead we'll just leave all the 8067978SPeter.Dunlap@Sun.COM * buffers bound and allow the client to cleanup. 8077978SPeter.Dunlap@Sun.COM */ 8087978SPeter.Dunlap@Sun.COM break; 8097978SPeter.Dunlap@Sun.COM default: 8107978SPeter.Dunlap@Sun.COM ASSERT(0); 8117978SPeter.Dunlap@Sun.COM } 8127978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8137978SPeter.Dunlap@Sun.COM } 8147978SPeter.Dunlap@Sun.COM 8157978SPeter.Dunlap@Sun.COM /* 8167978SPeter.Dunlap@Sun.COM * idm_buf_rx_from_ini_done 8177978SPeter.Dunlap@Sun.COM * 8187978SPeter.Dunlap@Sun.COM * The transport calls this after it has completed a transfer requested by 8197978SPeter.Dunlap@Sun.COM * a call totransport_buf_tx_to_ini 8207978SPeter.Dunlap@Sun.COM * 8217978SPeter.Dunlap@Sun.COM * Caller holds idt->idt_mutex, idt->idt_mutex is released before returning. 8227978SPeter.Dunlap@Sun.COM * idt may be freed after the call to idb->idb_buf_cb. 8237978SPeter.Dunlap@Sun.COM */ 8247978SPeter.Dunlap@Sun.COM void 8257978SPeter.Dunlap@Sun.COM idm_buf_rx_from_ini_done(idm_task_t *idt, idm_buf_t *idb, idm_status_t status) 8267978SPeter.Dunlap@Sun.COM { 8277978SPeter.Dunlap@Sun.COM ASSERT(mutex_owned(&idt->idt_mutex)); 8287978SPeter.Dunlap@Sun.COM idb->idb_in_transport = B_FALSE; 8297978SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_done++; 8307978SPeter.Dunlap@Sun.COM 8317978SPeter.Dunlap@Sun.COM /* 8327978SPeter.Dunlap@Sun.COM * idm_refcnt_rele may cause TASK_SUSPENDING --> TASK_SUSPENDED or 8337978SPeter.Dunlap@Sun.COM * TASK_ABORTING --> TASK_ABORTED transistion if the refcount goes 8347978SPeter.Dunlap@Sun.COM * to 0. 8357978SPeter.Dunlap@Sun.COM */ 8367978SPeter.Dunlap@Sun.COM idm_task_rele(idt); 8377978SPeter.Dunlap@Sun.COM idb->idb_status = status; 8387978SPeter.Dunlap@Sun.COM 8397978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 8407978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 8417978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, idb); 8427978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8437978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, status); 8447978SPeter.Dunlap@Sun.COM return; 8457978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 8467978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 8477978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 8487978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 8497978SPeter.Dunlap@Sun.COM /* 8507978SPeter.Dunlap@Sun.COM * To keep things simple we will ignore the case where the 8517978SPeter.Dunlap@Sun.COM * transfer was successful and leave all buffers bound to the 8527978SPeter.Dunlap@Sun.COM * task. This allows us to also ignore the case where we've 8537978SPeter.Dunlap@Sun.COM * been asked to abort a task but the last transfer of the 8547978SPeter.Dunlap@Sun.COM * task has completed. IDM has no idea whether this was, in 8557978SPeter.Dunlap@Sun.COM * fact, the last transfer of the task so it would be difficult 8567978SPeter.Dunlap@Sun.COM * to handle this case. Everything should get sorted out again 8577978SPeter.Dunlap@Sun.COM * after task reassignment is complete. 8587978SPeter.Dunlap@Sun.COM * 8597978SPeter.Dunlap@Sun.COM * In the case of TASK_ABORTING we could conceivably call the 8607978SPeter.Dunlap@Sun.COM * buffer callback here but the timing of when the client's 8617978SPeter.Dunlap@Sun.COM * client_task_aborted callback is invoked vs. when the client's 8627978SPeter.Dunlap@Sun.COM * buffer callback gets invoked gets sticky. We don't want 8637978SPeter.Dunlap@Sun.COM * the client to here from us again after the call to 8647978SPeter.Dunlap@Sun.COM * client_task_aborted() but we don't want to give it a bunch 8657978SPeter.Dunlap@Sun.COM * of failed buffer transfers until we've called 8667978SPeter.Dunlap@Sun.COM * client_task_aborted(). Instead we'll just leave all the 8677978SPeter.Dunlap@Sun.COM * buffers bound and allow the client to cleanup. 8687978SPeter.Dunlap@Sun.COM */ 8697978SPeter.Dunlap@Sun.COM break; 8707978SPeter.Dunlap@Sun.COM default: 8717978SPeter.Dunlap@Sun.COM ASSERT(0); 8727978SPeter.Dunlap@Sun.COM } 8737978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 8747978SPeter.Dunlap@Sun.COM } 8757978SPeter.Dunlap@Sun.COM 8767978SPeter.Dunlap@Sun.COM /* 8777978SPeter.Dunlap@Sun.COM * idm_buf_alloc 8787978SPeter.Dunlap@Sun.COM * 8797978SPeter.Dunlap@Sun.COM * Allocates a buffer handle and registers it for use with the transport 8807978SPeter.Dunlap@Sun.COM * layer. If a buffer is not passed on bufptr, the buffer will be allocated 8817978SPeter.Dunlap@Sun.COM * as well as the handle. 8827978SPeter.Dunlap@Sun.COM * 8837978SPeter.Dunlap@Sun.COM * ic - connection on which the buffer will be transferred 8847978SPeter.Dunlap@Sun.COM * bufptr - allocate memory for buffer if NULL, else assign to buffer 8857978SPeter.Dunlap@Sun.COM * buflen - length of buffer 8867978SPeter.Dunlap@Sun.COM * 8877978SPeter.Dunlap@Sun.COM * Returns idm_buf_t handle if successful, otherwise NULL 8887978SPeter.Dunlap@Sun.COM */ 8897978SPeter.Dunlap@Sun.COM idm_buf_t * 8907978SPeter.Dunlap@Sun.COM idm_buf_alloc(idm_conn_t *ic, void *bufptr, uint64_t buflen) 8917978SPeter.Dunlap@Sun.COM { 8927978SPeter.Dunlap@Sun.COM idm_buf_t *buf = NULL; 8937978SPeter.Dunlap@Sun.COM int rc; 8947978SPeter.Dunlap@Sun.COM 8957978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 8967978SPeter.Dunlap@Sun.COM ASSERT(idm.idm_buf_cache != NULL); 8977978SPeter.Dunlap@Sun.COM ASSERT(buflen > 0); 8987978SPeter.Dunlap@Sun.COM 8997978SPeter.Dunlap@Sun.COM /* Don't allocate new buffers if we are not in FFP */ 9007978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 9017978SPeter.Dunlap@Sun.COM if (!ic->ic_ffp) { 9027978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 9037978SPeter.Dunlap@Sun.COM return (NULL); 9047978SPeter.Dunlap@Sun.COM } 9057978SPeter.Dunlap@Sun.COM 9067978SPeter.Dunlap@Sun.COM 9077978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 9087978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 9097978SPeter.Dunlap@Sun.COM 9107978SPeter.Dunlap@Sun.COM buf = kmem_cache_alloc(idm.idm_buf_cache, KM_NOSLEEP); 9117978SPeter.Dunlap@Sun.COM if (buf == NULL) { 9127978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 9137978SPeter.Dunlap@Sun.COM return (NULL); 9147978SPeter.Dunlap@Sun.COM } 9157978SPeter.Dunlap@Sun.COM 9167978SPeter.Dunlap@Sun.COM buf->idb_ic = ic; 9177978SPeter.Dunlap@Sun.COM buf->idb_buflen = buflen; 9187978SPeter.Dunlap@Sun.COM buf->idb_exp_offset = 0; 9197978SPeter.Dunlap@Sun.COM buf->idb_bufoffset = 0; 9207978SPeter.Dunlap@Sun.COM buf->idb_xfer_len = 0; 9217978SPeter.Dunlap@Sun.COM buf->idb_magic = IDM_BUF_MAGIC; 9227978SPeter.Dunlap@Sun.COM 9237978SPeter.Dunlap@Sun.COM /* 9247978SPeter.Dunlap@Sun.COM * If bufptr is NULL, we have an implicit request to allocate 9257978SPeter.Dunlap@Sun.COM * memory for this IDM buffer handle and register it for use 9267978SPeter.Dunlap@Sun.COM * with the transport. To simplify this, and to give more freedom 9277978SPeter.Dunlap@Sun.COM * to the transport layer for it's own buffer management, both of 9287978SPeter.Dunlap@Sun.COM * these actions will take place in the transport layer. 9297978SPeter.Dunlap@Sun.COM * If bufptr is set, then the caller has allocated memory (or more 9307978SPeter.Dunlap@Sun.COM * likely it's been passed from an upper layer), and we need only 9317978SPeter.Dunlap@Sun.COM * register the buffer for use with the transport layer. 9327978SPeter.Dunlap@Sun.COM */ 9337978SPeter.Dunlap@Sun.COM if (bufptr == NULL) { 9347978SPeter.Dunlap@Sun.COM /* 9357978SPeter.Dunlap@Sun.COM * Allocate a buffer from the transport layer (which 9367978SPeter.Dunlap@Sun.COM * will also register the buffer for use). 9377978SPeter.Dunlap@Sun.COM */ 9387978SPeter.Dunlap@Sun.COM rc = ic->ic_transport_ops->it_buf_alloc(buf, buflen); 9397978SPeter.Dunlap@Sun.COM if (rc != 0) { 9407978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 9417978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 9427978SPeter.Dunlap@Sun.COM return (NULL); 9437978SPeter.Dunlap@Sun.COM } 9447978SPeter.Dunlap@Sun.COM /* Set the bufalloc'd flag */ 9457978SPeter.Dunlap@Sun.COM buf->idb_bufalloc = B_TRUE; 9467978SPeter.Dunlap@Sun.COM } else { 9477978SPeter.Dunlap@Sun.COM /* 9487978SPeter.Dunlap@Sun.COM * Set the passed bufptr into the buf handle, and 9497978SPeter.Dunlap@Sun.COM * register the handle with the transport layer. 9507978SPeter.Dunlap@Sun.COM */ 9517978SPeter.Dunlap@Sun.COM buf->idb_buf = bufptr; 9527978SPeter.Dunlap@Sun.COM 9537978SPeter.Dunlap@Sun.COM rc = ic->ic_transport_ops->it_buf_setup(buf); 9547978SPeter.Dunlap@Sun.COM if (rc != 0) { 9557978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 9567978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 9577978SPeter.Dunlap@Sun.COM return (NULL); 9587978SPeter.Dunlap@Sun.COM } 9597978SPeter.Dunlap@Sun.COM /* Ensure bufalloc'd flag is unset */ 9607978SPeter.Dunlap@Sun.COM buf->idb_bufalloc = B_FALSE; 9617978SPeter.Dunlap@Sun.COM } 9627978SPeter.Dunlap@Sun.COM 9637978SPeter.Dunlap@Sun.COM return (buf); 9647978SPeter.Dunlap@Sun.COM 9657978SPeter.Dunlap@Sun.COM } 9667978SPeter.Dunlap@Sun.COM 9677978SPeter.Dunlap@Sun.COM /* 9687978SPeter.Dunlap@Sun.COM * idm_buf_free 9697978SPeter.Dunlap@Sun.COM * 9707978SPeter.Dunlap@Sun.COM * Release a buffer handle along with the associated buffer that was allocated 9717978SPeter.Dunlap@Sun.COM * or assigned with idm_buf_alloc 9727978SPeter.Dunlap@Sun.COM */ 9737978SPeter.Dunlap@Sun.COM void 9747978SPeter.Dunlap@Sun.COM idm_buf_free(idm_buf_t *buf) 9757978SPeter.Dunlap@Sun.COM { 9767978SPeter.Dunlap@Sun.COM idm_conn_t *ic = buf->idb_ic; 9777978SPeter.Dunlap@Sun.COM 9787978SPeter.Dunlap@Sun.COM 9797978SPeter.Dunlap@Sun.COM buf->idb_task_binding = NULL; 9807978SPeter.Dunlap@Sun.COM 9817978SPeter.Dunlap@Sun.COM if (buf->idb_bufalloc) { 9827978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_buf_free(buf); 9837978SPeter.Dunlap@Sun.COM } else { 9847978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_buf_teardown(buf); 9857978SPeter.Dunlap@Sun.COM } 9867978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_buf_cache, buf); 9877978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 9887978SPeter.Dunlap@Sun.COM } 9897978SPeter.Dunlap@Sun.COM 9907978SPeter.Dunlap@Sun.COM /* 9917978SPeter.Dunlap@Sun.COM * idm_buf_bind_in 9927978SPeter.Dunlap@Sun.COM * 9937978SPeter.Dunlap@Sun.COM * This function associates a buffer with a task. This is only for use by the 9947978SPeter.Dunlap@Sun.COM * iSCSI initiator that will have only one buffer per transfer direction 9957978SPeter.Dunlap@Sun.COM * 9967978SPeter.Dunlap@Sun.COM */ 9977978SPeter.Dunlap@Sun.COM void 9987978SPeter.Dunlap@Sun.COM idm_buf_bind_in(idm_task_t *idt, idm_buf_t *buf) 9997978SPeter.Dunlap@Sun.COM { 10007978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 10017978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idt, buf); 10027978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 10037978SPeter.Dunlap@Sun.COM } 10047978SPeter.Dunlap@Sun.COM 10057978SPeter.Dunlap@Sun.COM static void 10067978SPeter.Dunlap@Sun.COM idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf) 10077978SPeter.Dunlap@Sun.COM { 10087978SPeter.Dunlap@Sun.COM buf->idb_task_binding = idt; 10097978SPeter.Dunlap@Sun.COM buf->idb_ic = idt->idt_ic; 10107978SPeter.Dunlap@Sun.COM idm_listbuf_insert(&idt->idt_inbufv, buf); 10117978SPeter.Dunlap@Sun.COM } 10127978SPeter.Dunlap@Sun.COM 10137978SPeter.Dunlap@Sun.COM void 10147978SPeter.Dunlap@Sun.COM idm_buf_bind_out(idm_task_t *idt, idm_buf_t *buf) 10157978SPeter.Dunlap@Sun.COM { 10167978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 10177978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idt, buf); 10187978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 10197978SPeter.Dunlap@Sun.COM } 10207978SPeter.Dunlap@Sun.COM 10217978SPeter.Dunlap@Sun.COM static void 10227978SPeter.Dunlap@Sun.COM idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf) 10237978SPeter.Dunlap@Sun.COM { 10247978SPeter.Dunlap@Sun.COM buf->idb_task_binding = idt; 10257978SPeter.Dunlap@Sun.COM buf->idb_ic = idt->idt_ic; 10267978SPeter.Dunlap@Sun.COM idm_listbuf_insert(&idt->idt_outbufv, buf); 10277978SPeter.Dunlap@Sun.COM } 10287978SPeter.Dunlap@Sun.COM 10297978SPeter.Dunlap@Sun.COM void 10307978SPeter.Dunlap@Sun.COM idm_buf_unbind_in(idm_task_t *idt, idm_buf_t *buf) 10317978SPeter.Dunlap@Sun.COM { 10327978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 10337978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, buf); 10347978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 10357978SPeter.Dunlap@Sun.COM } 10367978SPeter.Dunlap@Sun.COM 10377978SPeter.Dunlap@Sun.COM static void 10387978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf) 10397978SPeter.Dunlap@Sun.COM { 10407978SPeter.Dunlap@Sun.COM list_remove(&idt->idt_inbufv, buf); 10417978SPeter.Dunlap@Sun.COM } 10427978SPeter.Dunlap@Sun.COM 10437978SPeter.Dunlap@Sun.COM void 10447978SPeter.Dunlap@Sun.COM idm_buf_unbind_out(idm_task_t *idt, idm_buf_t *buf) 10457978SPeter.Dunlap@Sun.COM { 10467978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 10477978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, buf); 10487978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 10497978SPeter.Dunlap@Sun.COM } 10507978SPeter.Dunlap@Sun.COM 10517978SPeter.Dunlap@Sun.COM static void 10527978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf) 10537978SPeter.Dunlap@Sun.COM { 10547978SPeter.Dunlap@Sun.COM list_remove(&idt->idt_outbufv, buf); 10557978SPeter.Dunlap@Sun.COM } 10567978SPeter.Dunlap@Sun.COM 10577978SPeter.Dunlap@Sun.COM /* 10587978SPeter.Dunlap@Sun.COM * idm_buf_find() will lookup the idm_buf_t based on the relative offset in the 10597978SPeter.Dunlap@Sun.COM * iSCSI PDU 10607978SPeter.Dunlap@Sun.COM */ 10617978SPeter.Dunlap@Sun.COM idm_buf_t * 10627978SPeter.Dunlap@Sun.COM idm_buf_find(void *lbuf, size_t data_offset) 10637978SPeter.Dunlap@Sun.COM { 10647978SPeter.Dunlap@Sun.COM idm_buf_t *idb; 10657978SPeter.Dunlap@Sun.COM list_t *lst = (list_t *)lbuf; 10667978SPeter.Dunlap@Sun.COM 10677978SPeter.Dunlap@Sun.COM /* iterate through the list to find the buffer */ 10687978SPeter.Dunlap@Sun.COM for (idb = list_head(lst); idb != NULL; idb = list_next(lst, idb)) { 10697978SPeter.Dunlap@Sun.COM 10707978SPeter.Dunlap@Sun.COM ASSERT((idb->idb_ic->ic_conn_type == CONN_TYPE_TGT) || 10717978SPeter.Dunlap@Sun.COM (idb->idb_bufoffset == 0)); 10727978SPeter.Dunlap@Sun.COM 10737978SPeter.Dunlap@Sun.COM if ((data_offset >= idb->idb_bufoffset) && 10747978SPeter.Dunlap@Sun.COM (data_offset < (idb->idb_bufoffset + idb->idb_buflen))) { 10757978SPeter.Dunlap@Sun.COM 10767978SPeter.Dunlap@Sun.COM return (idb); 10777978SPeter.Dunlap@Sun.COM } 10787978SPeter.Dunlap@Sun.COM } 10797978SPeter.Dunlap@Sun.COM 10807978SPeter.Dunlap@Sun.COM return (NULL); 10817978SPeter.Dunlap@Sun.COM } 10827978SPeter.Dunlap@Sun.COM 10837978SPeter.Dunlap@Sun.COM /* 10847978SPeter.Dunlap@Sun.COM * idm_task_alloc 10857978SPeter.Dunlap@Sun.COM * 10867978SPeter.Dunlap@Sun.COM * This function will allocate a idm_task_t structure. A task tag is also 10877978SPeter.Dunlap@Sun.COM * generated and saved in idt_tt. The task is not active. 10887978SPeter.Dunlap@Sun.COM */ 10897978SPeter.Dunlap@Sun.COM idm_task_t * 10907978SPeter.Dunlap@Sun.COM idm_task_alloc(idm_conn_t *ic) 10917978SPeter.Dunlap@Sun.COM { 10927978SPeter.Dunlap@Sun.COM idm_task_t *idt; 10937978SPeter.Dunlap@Sun.COM 10947978SPeter.Dunlap@Sun.COM ASSERT(ic != NULL); 10957978SPeter.Dunlap@Sun.COM 10967978SPeter.Dunlap@Sun.COM /* Don't allocate new tasks if we are not in FFP */ 10977978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 10987978SPeter.Dunlap@Sun.COM if (!ic->ic_ffp) { 10997978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 11007978SPeter.Dunlap@Sun.COM return (NULL); 11017978SPeter.Dunlap@Sun.COM } 11027978SPeter.Dunlap@Sun.COM idt = kmem_cache_alloc(idm.idm_task_cache, KM_NOSLEEP); 11037978SPeter.Dunlap@Sun.COM if (idt == NULL) { 11047978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 11057978SPeter.Dunlap@Sun.COM return (NULL); 11067978SPeter.Dunlap@Sun.COM } 11077978SPeter.Dunlap@Sun.COM 11087978SPeter.Dunlap@Sun.COM ASSERT(list_is_empty(&idt->idt_inbufv)); 11097978SPeter.Dunlap@Sun.COM ASSERT(list_is_empty(&idt->idt_outbufv)); 11107978SPeter.Dunlap@Sun.COM 11117978SPeter.Dunlap@Sun.COM idm_conn_hold(ic); 11127978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 11137978SPeter.Dunlap@Sun.COM 11147978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_IDLE; 11157978SPeter.Dunlap@Sun.COM idt->idt_ic = ic; 11167978SPeter.Dunlap@Sun.COM idt->idt_private = NULL; 11177978SPeter.Dunlap@Sun.COM idt->idt_exp_datasn = 0; 11187978SPeter.Dunlap@Sun.COM idt->idt_exp_rttsn = 0; 11197978SPeter.Dunlap@Sun.COM 11207978SPeter.Dunlap@Sun.COM return (idt); 11217978SPeter.Dunlap@Sun.COM } 11227978SPeter.Dunlap@Sun.COM 11237978SPeter.Dunlap@Sun.COM /* 11247978SPeter.Dunlap@Sun.COM * idm_task_start 11257978SPeter.Dunlap@Sun.COM * 11267978SPeter.Dunlap@Sun.COM * Add the task to an AVL tree to notify IDM about a new task. The caller 11277978SPeter.Dunlap@Sun.COM * sets up the idm_task_t structure with a prior call to idm_task_alloc(). 11287978SPeter.Dunlap@Sun.COM * The task service does not function as a task/work engine, it is the 11297978SPeter.Dunlap@Sun.COM * responsibility of the initiator to start the data transfer and free the 11307978SPeter.Dunlap@Sun.COM * resources. 11317978SPeter.Dunlap@Sun.COM */ 11327978SPeter.Dunlap@Sun.COM void 11337978SPeter.Dunlap@Sun.COM idm_task_start(idm_task_t *idt, uintptr_t handle) 11347978SPeter.Dunlap@Sun.COM { 11357978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 11367978SPeter.Dunlap@Sun.COM 11377978SPeter.Dunlap@Sun.COM /* mark the task as ACTIVE */ 11387978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ACTIVE; 11397978SPeter.Dunlap@Sun.COM idt->idt_client_handle = handle; 11407978SPeter.Dunlap@Sun.COM idt->idt_tx_to_ini_start = idt->idt_tx_to_ini_done = 11417978SPeter.Dunlap@Sun.COM idt->idt_rx_from_ini_start = idt->idt_rx_from_ini_done = 0; 11427978SPeter.Dunlap@Sun.COM } 11437978SPeter.Dunlap@Sun.COM 11447978SPeter.Dunlap@Sun.COM /* 11457978SPeter.Dunlap@Sun.COM * idm_task_done 11467978SPeter.Dunlap@Sun.COM * 11477978SPeter.Dunlap@Sun.COM * This function will remove the task from the AVL tree indicating that the 11487978SPeter.Dunlap@Sun.COM * task is no longer active. 11497978SPeter.Dunlap@Sun.COM */ 11507978SPeter.Dunlap@Sun.COM void 11517978SPeter.Dunlap@Sun.COM idm_task_done(idm_task_t *idt) 11527978SPeter.Dunlap@Sun.COM { 11537978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 11547978SPeter.Dunlap@Sun.COM ASSERT(idt->idt_refcnt.ir_refcnt == 0); 11557978SPeter.Dunlap@Sun.COM 11567978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_IDLE; 11577978SPeter.Dunlap@Sun.COM idm_refcnt_reset(&idt->idt_refcnt); 11587978SPeter.Dunlap@Sun.COM } 11597978SPeter.Dunlap@Sun.COM 11607978SPeter.Dunlap@Sun.COM /* 11617978SPeter.Dunlap@Sun.COM * idm_task_free 11627978SPeter.Dunlap@Sun.COM * 11637978SPeter.Dunlap@Sun.COM * This function will free the Task Tag and the memory allocated for the task 11647978SPeter.Dunlap@Sun.COM * idm_task_done should be called prior to this call 11657978SPeter.Dunlap@Sun.COM */ 11667978SPeter.Dunlap@Sun.COM void 11677978SPeter.Dunlap@Sun.COM idm_task_free(idm_task_t *idt) 11687978SPeter.Dunlap@Sun.COM { 11697978SPeter.Dunlap@Sun.COM idm_conn_t *ic = idt->idt_ic; 11707978SPeter.Dunlap@Sun.COM 11717978SPeter.Dunlap@Sun.COM ASSERT(idt != NULL); 11727978SPeter.Dunlap@Sun.COM ASSERT(idt->idt_state == TASK_IDLE); 11737978SPeter.Dunlap@Sun.COM 11747978SPeter.Dunlap@Sun.COM /* 11757978SPeter.Dunlap@Sun.COM * It's possible for items to still be in the idt_inbufv list if 11767978SPeter.Dunlap@Sun.COM * they were added after idm_task_cleanup was called. We rely on 11777978SPeter.Dunlap@Sun.COM * STMF to free all buffers associated with the task however STMF 11787978SPeter.Dunlap@Sun.COM * doesn't know that we have this reference to the buffers. 11797978SPeter.Dunlap@Sun.COM * Use list_create so that we don't end up with stale references 11807978SPeter.Dunlap@Sun.COM * to these buffers. 11817978SPeter.Dunlap@Sun.COM */ 11827978SPeter.Dunlap@Sun.COM list_create(&idt->idt_inbufv, sizeof (idm_buf_t), 11837978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 11847978SPeter.Dunlap@Sun.COM list_create(&idt->idt_outbufv, sizeof (idm_buf_t), 11857978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 11867978SPeter.Dunlap@Sun.COM 11877978SPeter.Dunlap@Sun.COM kmem_cache_free(idm.idm_task_cache, idt); 11887978SPeter.Dunlap@Sun.COM 11897978SPeter.Dunlap@Sun.COM idm_conn_rele(ic); 11907978SPeter.Dunlap@Sun.COM } 11917978SPeter.Dunlap@Sun.COM 11927978SPeter.Dunlap@Sun.COM /* 11937978SPeter.Dunlap@Sun.COM * idm_task_find 11947978SPeter.Dunlap@Sun.COM * 11957978SPeter.Dunlap@Sun.COM * This function looks up a task by task tag 11967978SPeter.Dunlap@Sun.COM */ 11977978SPeter.Dunlap@Sun.COM /*ARGSUSED*/ 11987978SPeter.Dunlap@Sun.COM idm_task_t * 11997978SPeter.Dunlap@Sun.COM idm_task_find(idm_conn_t *ic, uint32_t itt, uint32_t ttt) 12007978SPeter.Dunlap@Sun.COM { 12017978SPeter.Dunlap@Sun.COM uint32_t tt, client_handle; 12027978SPeter.Dunlap@Sun.COM idm_task_t *idt; 12037978SPeter.Dunlap@Sun.COM 12047978SPeter.Dunlap@Sun.COM /* 12057978SPeter.Dunlap@Sun.COM * Must match both itt and ttt. The table is indexed by itt 12067978SPeter.Dunlap@Sun.COM * for initiator connections and ttt for target connections. 12077978SPeter.Dunlap@Sun.COM */ 12087978SPeter.Dunlap@Sun.COM if (IDM_CONN_ISTGT(ic)) { 12097978SPeter.Dunlap@Sun.COM tt = ttt; 12107978SPeter.Dunlap@Sun.COM client_handle = itt; 12117978SPeter.Dunlap@Sun.COM } else { 12127978SPeter.Dunlap@Sun.COM tt = itt; 12137978SPeter.Dunlap@Sun.COM client_handle = ttt; 12147978SPeter.Dunlap@Sun.COM } 12157978SPeter.Dunlap@Sun.COM 12167978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 12177978SPeter.Dunlap@Sun.COM if (tt >= idm.idm_taskid_max) { 12187978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 12197978SPeter.Dunlap@Sun.COM return (NULL); 12207978SPeter.Dunlap@Sun.COM } 12217978SPeter.Dunlap@Sun.COM 12227978SPeter.Dunlap@Sun.COM idt = idm.idm_taskid_table[tt]; 12237978SPeter.Dunlap@Sun.COM 12247978SPeter.Dunlap@Sun.COM if (idt != NULL) { 12257978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 12267978SPeter.Dunlap@Sun.COM if ((idt->idt_state != TASK_ACTIVE) || 12277978SPeter.Dunlap@Sun.COM (IDM_CONN_ISTGT(ic) && 12287978SPeter.Dunlap@Sun.COM (idt->idt_client_handle != client_handle))) { 12297978SPeter.Dunlap@Sun.COM /* 12307978SPeter.Dunlap@Sun.COM * Task is aborting, we don't want any more references. 12317978SPeter.Dunlap@Sun.COM */ 12327978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 12337978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 12347978SPeter.Dunlap@Sun.COM return (NULL); 12357978SPeter.Dunlap@Sun.COM } 12367978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 12377978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 12387978SPeter.Dunlap@Sun.COM } 12397978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 12407978SPeter.Dunlap@Sun.COM 12417978SPeter.Dunlap@Sun.COM return (idt); 12427978SPeter.Dunlap@Sun.COM } 12437978SPeter.Dunlap@Sun.COM 12447978SPeter.Dunlap@Sun.COM /* 12457978SPeter.Dunlap@Sun.COM * idm_task_find_by_handle 12467978SPeter.Dunlap@Sun.COM * 12477978SPeter.Dunlap@Sun.COM * This function looks up a task by the client-private idt_client_handle. 12487978SPeter.Dunlap@Sun.COM * 12497978SPeter.Dunlap@Sun.COM * This function should NEVER be called in the performance path. It is 12507978SPeter.Dunlap@Sun.COM * intended strictly for error recovery/task management. 12517978SPeter.Dunlap@Sun.COM */ 12527978SPeter.Dunlap@Sun.COM /*ARGSUSED*/ 12537978SPeter.Dunlap@Sun.COM void * 12547978SPeter.Dunlap@Sun.COM idm_task_find_by_handle(idm_conn_t *ic, uintptr_t handle) 12557978SPeter.Dunlap@Sun.COM { 12567978SPeter.Dunlap@Sun.COM idm_task_t *idt = NULL; 12577978SPeter.Dunlap@Sun.COM int idx = 0; 12587978SPeter.Dunlap@Sun.COM 12597978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 12607978SPeter.Dunlap@Sun.COM 12617978SPeter.Dunlap@Sun.COM for (idx = 0; idx < idm.idm_taskid_max; idx++) { 12627978SPeter.Dunlap@Sun.COM idt = idm.idm_taskid_table[idx]; 12637978SPeter.Dunlap@Sun.COM 12647978SPeter.Dunlap@Sun.COM if (idt == NULL) 12657978SPeter.Dunlap@Sun.COM continue; 12667978SPeter.Dunlap@Sun.COM 12677978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 12687978SPeter.Dunlap@Sun.COM 12697978SPeter.Dunlap@Sun.COM if (idt->idt_state != TASK_ACTIVE) { 12707978SPeter.Dunlap@Sun.COM /* 12717978SPeter.Dunlap@Sun.COM * Task is either in suspend, abort, or already 12727978SPeter.Dunlap@Sun.COM * complete. 12737978SPeter.Dunlap@Sun.COM */ 12747978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 12757978SPeter.Dunlap@Sun.COM continue; 12767978SPeter.Dunlap@Sun.COM } 12777978SPeter.Dunlap@Sun.COM 12787978SPeter.Dunlap@Sun.COM if (idt->idt_client_handle == handle) { 12797978SPeter.Dunlap@Sun.COM idm_task_hold(idt); 12807978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 12817978SPeter.Dunlap@Sun.COM break; 12827978SPeter.Dunlap@Sun.COM } 12837978SPeter.Dunlap@Sun.COM 12847978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 12857978SPeter.Dunlap@Sun.COM } 12867978SPeter.Dunlap@Sun.COM 12877978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 12887978SPeter.Dunlap@Sun.COM 12897978SPeter.Dunlap@Sun.COM if ((idt == NULL) || (idx == idm.idm_taskid_max)) 12907978SPeter.Dunlap@Sun.COM return (NULL); 12917978SPeter.Dunlap@Sun.COM 12927978SPeter.Dunlap@Sun.COM return (idt->idt_private); 12937978SPeter.Dunlap@Sun.COM } 12947978SPeter.Dunlap@Sun.COM 12957978SPeter.Dunlap@Sun.COM void 12967978SPeter.Dunlap@Sun.COM idm_task_hold(idm_task_t *idt) 12977978SPeter.Dunlap@Sun.COM { 12987978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&idt->idt_refcnt); 12997978SPeter.Dunlap@Sun.COM } 13007978SPeter.Dunlap@Sun.COM 13017978SPeter.Dunlap@Sun.COM void 13027978SPeter.Dunlap@Sun.COM idm_task_rele(idm_task_t *idt) 13037978SPeter.Dunlap@Sun.COM { 13047978SPeter.Dunlap@Sun.COM idm_refcnt_rele(&idt->idt_refcnt); 13057978SPeter.Dunlap@Sun.COM } 13067978SPeter.Dunlap@Sun.COM 13077978SPeter.Dunlap@Sun.COM void 13087978SPeter.Dunlap@Sun.COM idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type) 13097978SPeter.Dunlap@Sun.COM { 13107978SPeter.Dunlap@Sun.COM idm_task_t *task; 13117978SPeter.Dunlap@Sun.COM int idx; 13127978SPeter.Dunlap@Sun.COM 13137978SPeter.Dunlap@Sun.COM /* 13147978SPeter.Dunlap@Sun.COM * Passing NULL as the task indicates that all tasks 13157978SPeter.Dunlap@Sun.COM * for this connection should be aborted. 13167978SPeter.Dunlap@Sun.COM */ 13177978SPeter.Dunlap@Sun.COM if (idt == NULL) { 13187978SPeter.Dunlap@Sun.COM /* 13197978SPeter.Dunlap@Sun.COM * Only the connection state machine should ask for 13207978SPeter.Dunlap@Sun.COM * all tasks to abort and this should never happen in FFP. 13217978SPeter.Dunlap@Sun.COM */ 13227978SPeter.Dunlap@Sun.COM ASSERT(!ic->ic_ffp); 13237978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 13247978SPeter.Dunlap@Sun.COM for (idx = 0; idx < idm.idm_taskid_max; idx++) { 13257978SPeter.Dunlap@Sun.COM task = idm.idm_taskid_table[idx]; 13267978SPeter.Dunlap@Sun.COM if (task && (task->idt_state != TASK_IDLE) && 13277978SPeter.Dunlap@Sun.COM (task->idt_ic == ic)) { 13287978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 13297978SPeter.Dunlap@Sun.COM idm_task_abort_one(ic, task, abort_type); 13307978SPeter.Dunlap@Sun.COM rw_enter(&idm.idm_taskid_table_lock, RW_READER); 13317978SPeter.Dunlap@Sun.COM } 13327978SPeter.Dunlap@Sun.COM } 13337978SPeter.Dunlap@Sun.COM rw_exit(&idm.idm_taskid_table_lock); 13347978SPeter.Dunlap@Sun.COM } else { 13357978SPeter.Dunlap@Sun.COM idm_task_abort_one(ic, idt, abort_type); 13367978SPeter.Dunlap@Sun.COM } 13377978SPeter.Dunlap@Sun.COM } 13387978SPeter.Dunlap@Sun.COM 13397978SPeter.Dunlap@Sun.COM static void 13407978SPeter.Dunlap@Sun.COM idm_task_abort_unref_cb(void *ref) 13417978SPeter.Dunlap@Sun.COM { 13427978SPeter.Dunlap@Sun.COM idm_task_t *idt = ref; 13437978SPeter.Dunlap@Sun.COM 13447978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 13457978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 13467978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 13477978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_SUSPENDED; 13487978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13497978SPeter.Dunlap@Sun.COM idm_task_aborted(idt, IDM_STATUS_SUSPENDED); 13507978SPeter.Dunlap@Sun.COM return; 13517978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 13527978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTED; 13537978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13547978SPeter.Dunlap@Sun.COM idm_task_aborted(idt, IDM_STATUS_ABORTED); 13557978SPeter.Dunlap@Sun.COM return; 13567978SPeter.Dunlap@Sun.COM default: 13577978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13587978SPeter.Dunlap@Sun.COM ASSERT(0); 13597978SPeter.Dunlap@Sun.COM break; 13607978SPeter.Dunlap@Sun.COM } 13617978SPeter.Dunlap@Sun.COM } 13627978SPeter.Dunlap@Sun.COM 13637978SPeter.Dunlap@Sun.COM static void 13647978SPeter.Dunlap@Sun.COM idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type) 13657978SPeter.Dunlap@Sun.COM { 13667978SPeter.Dunlap@Sun.COM /* Caller must hold connection mutex */ 13677978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 13687978SPeter.Dunlap@Sun.COM switch (idt->idt_state) { 13697978SPeter.Dunlap@Sun.COM case TASK_ACTIVE: 13707978SPeter.Dunlap@Sun.COM switch (abort_type) { 13717978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 13727978SPeter.Dunlap@Sun.COM /* Call transport to release any resources */ 13737978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_SUSPENDING; 13747978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13757978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_free_task_rsrc(idt); 13767978SPeter.Dunlap@Sun.COM 13777978SPeter.Dunlap@Sun.COM /* 13787978SPeter.Dunlap@Sun.COM * Wait for outstanding references. When all 13797978SPeter.Dunlap@Sun.COM * references are released the callback will call 13807978SPeter.Dunlap@Sun.COM * idm_task_aborted(). 13817978SPeter.Dunlap@Sun.COM */ 13827978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 13837978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 13847978SPeter.Dunlap@Sun.COM return; 13857978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 13867978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 13877978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 13887978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 13897978SPeter.Dunlap@Sun.COM ic->ic_transport_ops->it_free_task_rsrc(idt); 13907978SPeter.Dunlap@Sun.COM 13917978SPeter.Dunlap@Sun.COM /* 13927978SPeter.Dunlap@Sun.COM * Wait for outstanding references. When all 13937978SPeter.Dunlap@Sun.COM * references are released the callback will call 13947978SPeter.Dunlap@Sun.COM * idm_task_aborted(). 13957978SPeter.Dunlap@Sun.COM */ 13967978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 13977978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 13987978SPeter.Dunlap@Sun.COM return; 13997978SPeter.Dunlap@Sun.COM default: 14007978SPeter.Dunlap@Sun.COM ASSERT(0); 14017978SPeter.Dunlap@Sun.COM } 14027978SPeter.Dunlap@Sun.COM break; 14037978SPeter.Dunlap@Sun.COM case TASK_SUSPENDING: 14047978SPeter.Dunlap@Sun.COM /* Already called transport_free_task_rsrc(); */ 14057978SPeter.Dunlap@Sun.COM switch (abort_type) { 14067978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 14077978SPeter.Dunlap@Sun.COM /* Already doing it */ 14087978SPeter.Dunlap@Sun.COM break; 14097978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 14107978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 14117978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 14127978SPeter.Dunlap@Sun.COM break; 14137978SPeter.Dunlap@Sun.COM default: 14147978SPeter.Dunlap@Sun.COM ASSERT(0); 14157978SPeter.Dunlap@Sun.COM } 14167978SPeter.Dunlap@Sun.COM break; 14177978SPeter.Dunlap@Sun.COM case TASK_SUSPENDED: 14187978SPeter.Dunlap@Sun.COM /* Already called transport_free_task_rsrc(); */ 14197978SPeter.Dunlap@Sun.COM switch (abort_type) { 14207978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 14217978SPeter.Dunlap@Sun.COM /* Already doing it */ 14227978SPeter.Dunlap@Sun.COM break; 14237978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 14247978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 14257978SPeter.Dunlap@Sun.COM idt->idt_state = TASK_ABORTING; 14267978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14277978SPeter.Dunlap@Sun.COM 14287978SPeter.Dunlap@Sun.COM /* 14297978SPeter.Dunlap@Sun.COM * We could probably call idm_task_aborted directly 14307978SPeter.Dunlap@Sun.COM * here but we may be holding the conn lock. It's 14317978SPeter.Dunlap@Sun.COM * easier to just switch contexts. Even though 14327978SPeter.Dunlap@Sun.COM * we shouldn't really have any references we'll 14337978SPeter.Dunlap@Sun.COM * set the state to TASK_ABORTING instead of 14347978SPeter.Dunlap@Sun.COM * TASK_ABORTED so we can use the same code path. 14357978SPeter.Dunlap@Sun.COM */ 14367978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(&idt->idt_refcnt, 14377978SPeter.Dunlap@Sun.COM &idm_task_abort_unref_cb); 14387978SPeter.Dunlap@Sun.COM return; 14397978SPeter.Dunlap@Sun.COM default: 14407978SPeter.Dunlap@Sun.COM ASSERT(0); 14417978SPeter.Dunlap@Sun.COM } 14427978SPeter.Dunlap@Sun.COM break; 14437978SPeter.Dunlap@Sun.COM case TASK_ABORTING: 14447978SPeter.Dunlap@Sun.COM case TASK_ABORTED: 14457978SPeter.Dunlap@Sun.COM switch (abort_type) { 14467978SPeter.Dunlap@Sun.COM case AT_INTERNAL_SUSPEND: 14477978SPeter.Dunlap@Sun.COM /* We're already past this point... */ 14487978SPeter.Dunlap@Sun.COM case AT_INTERNAL_ABORT: 14497978SPeter.Dunlap@Sun.COM case AT_TASK_MGMT_ABORT: 14507978SPeter.Dunlap@Sun.COM /* Already doing it */ 14517978SPeter.Dunlap@Sun.COM break; 14527978SPeter.Dunlap@Sun.COM default: 14537978SPeter.Dunlap@Sun.COM ASSERT(0); 14547978SPeter.Dunlap@Sun.COM } 14557978SPeter.Dunlap@Sun.COM break; 14567978SPeter.Dunlap@Sun.COM case TASK_COMPLETE: 14577978SPeter.Dunlap@Sun.COM /* 14587978SPeter.Dunlap@Sun.COM * In this case, let it go. The status has already been 14597978SPeter.Dunlap@Sun.COM * sent (which may or may not get successfully transmitted) 14607978SPeter.Dunlap@Sun.COM * and we don't want to end up in a race between completing 14617978SPeter.Dunlap@Sun.COM * the status PDU and marking the task suspended. 14627978SPeter.Dunlap@Sun.COM */ 14637978SPeter.Dunlap@Sun.COM break; 14647978SPeter.Dunlap@Sun.COM default: 14657978SPeter.Dunlap@Sun.COM ASSERT(0); 14667978SPeter.Dunlap@Sun.COM } 14677978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 14687978SPeter.Dunlap@Sun.COM } 14697978SPeter.Dunlap@Sun.COM 14707978SPeter.Dunlap@Sun.COM static void 14717978SPeter.Dunlap@Sun.COM idm_task_aborted(idm_task_t *idt, idm_status_t status) 14727978SPeter.Dunlap@Sun.COM { 14737978SPeter.Dunlap@Sun.COM (*idt->idt_ic->ic_conn_ops.icb_task_aborted)(idt, status); 14747978SPeter.Dunlap@Sun.COM } 14757978SPeter.Dunlap@Sun.COM 14767978SPeter.Dunlap@Sun.COM void 14777978SPeter.Dunlap@Sun.COM idm_task_cleanup(idm_task_t *idt) 14787978SPeter.Dunlap@Sun.COM { 14797978SPeter.Dunlap@Sun.COM idm_buf_t *idb, *next_idb; 14807978SPeter.Dunlap@Sun.COM list_t tmp_buflist; 14817978SPeter.Dunlap@Sun.COM ASSERT((idt->idt_state == TASK_SUSPENDED) || 14827978SPeter.Dunlap@Sun.COM (idt->idt_state == TASK_ABORTED)); 14837978SPeter.Dunlap@Sun.COM 14847978SPeter.Dunlap@Sun.COM list_create(&tmp_buflist, sizeof (idm_buf_t), 14857978SPeter.Dunlap@Sun.COM offsetof(idm_buf_t, idb_buflink)); 14867978SPeter.Dunlap@Sun.COM 14877978SPeter.Dunlap@Sun.COM /* 14887978SPeter.Dunlap@Sun.COM * Remove all the buffers from the task and add them to a 14897978SPeter.Dunlap@Sun.COM * temporary local list -- we do this so that we can hold 14907978SPeter.Dunlap@Sun.COM * the task lock and prevent the task from going away if 14917978SPeter.Dunlap@Sun.COM * the client decides to call idm_task_done/idm_task_free. 14927978SPeter.Dunlap@Sun.COM * This could happen during abort in iscsit. 14937978SPeter.Dunlap@Sun.COM */ 14947978SPeter.Dunlap@Sun.COM mutex_enter(&idt->idt_mutex); 14957978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_inbufv); 14967978SPeter.Dunlap@Sun.COM idb != NULL; 14977978SPeter.Dunlap@Sun.COM idb = next_idb) { 14987978SPeter.Dunlap@Sun.COM next_idb = list_next(&idt->idt_inbufv, idb); 14997978SPeter.Dunlap@Sun.COM idm_buf_unbind_in_locked(idt, idb); 15007978SPeter.Dunlap@Sun.COM list_insert_tail(&tmp_buflist, idb); 15017978SPeter.Dunlap@Sun.COM } 15027978SPeter.Dunlap@Sun.COM 15037978SPeter.Dunlap@Sun.COM for (idb = list_head(&idt->idt_outbufv); 15047978SPeter.Dunlap@Sun.COM idb != NULL; 15057978SPeter.Dunlap@Sun.COM idb = next_idb) { 15067978SPeter.Dunlap@Sun.COM next_idb = list_next(&idt->idt_outbufv, idb); 15077978SPeter.Dunlap@Sun.COM idm_buf_unbind_out_locked(idt, idb); 15087978SPeter.Dunlap@Sun.COM list_insert_tail(&tmp_buflist, idb); 15097978SPeter.Dunlap@Sun.COM } 15107978SPeter.Dunlap@Sun.COM mutex_exit(&idt->idt_mutex); 15117978SPeter.Dunlap@Sun.COM 15127978SPeter.Dunlap@Sun.COM for (idb = list_head(&tmp_buflist); idb != NULL; idb = next_idb) { 15137978SPeter.Dunlap@Sun.COM next_idb = list_next(&tmp_buflist, idb); 15147978SPeter.Dunlap@Sun.COM list_remove(&tmp_buflist, idb); 15157978SPeter.Dunlap@Sun.COM (*idb->idb_buf_cb)(idb, IDM_STATUS_ABORTED); 15167978SPeter.Dunlap@Sun.COM } 15177978SPeter.Dunlap@Sun.COM list_destroy(&tmp_buflist); 15187978SPeter.Dunlap@Sun.COM } 15197978SPeter.Dunlap@Sun.COM 15207978SPeter.Dunlap@Sun.COM 15217978SPeter.Dunlap@Sun.COM /* 15227978SPeter.Dunlap@Sun.COM * idm_pdu_tx 15237978SPeter.Dunlap@Sun.COM * 15247978SPeter.Dunlap@Sun.COM * This is IDM's implementation of the 'Send_Control' operational primitive. 15257978SPeter.Dunlap@Sun.COM * This function is invoked by an initiator iSCSI layer requesting the transfer 15267978SPeter.Dunlap@Sun.COM * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a 15277978SPeter.Dunlap@Sun.COM * iSCSI response PDU. The PDU will be transmitted as-is by the local Datamover 15287978SPeter.Dunlap@Sun.COM * layer to the peer iSCSI layer in the remote iSCSI node. The connection info 15297978SPeter.Dunlap@Sun.COM * and iSCSI PDU-specific qualifiers namely BHS, AHS, DataDescriptor and Size 15307978SPeter.Dunlap@Sun.COM * are provided as input. 15317978SPeter.Dunlap@Sun.COM * 15327978SPeter.Dunlap@Sun.COM */ 15337978SPeter.Dunlap@Sun.COM void 15347978SPeter.Dunlap@Sun.COM idm_pdu_tx(idm_pdu_t *pdu) 15357978SPeter.Dunlap@Sun.COM { 15367978SPeter.Dunlap@Sun.COM idm_conn_t *ic = pdu->isp_ic; 15377978SPeter.Dunlap@Sun.COM iscsi_async_evt_hdr_t *async_evt; 15387978SPeter.Dunlap@Sun.COM 15397978SPeter.Dunlap@Sun.COM /* 15407978SPeter.Dunlap@Sun.COM * If we are in full-featured mode then route SCSI-related 15417978SPeter.Dunlap@Sun.COM * commands to the appropriate function vector without checking 15427978SPeter.Dunlap@Sun.COM * the connection state. We will only be in full-feature mode 15437978SPeter.Dunlap@Sun.COM * when we are in an acceptable state for SCSI PDU's. 15447978SPeter.Dunlap@Sun.COM * 15457978SPeter.Dunlap@Sun.COM * We also need to ensure that there are no PDU events outstanding 15467978SPeter.Dunlap@Sun.COM * on the state machine. Any non-SCSI PDU's received in full-feature 15477978SPeter.Dunlap@Sun.COM * mode will result in PDU events and until these have been handled 15487978SPeter.Dunlap@Sun.COM * we need to route all PDU's through the state machine as PDU 15497978SPeter.Dunlap@Sun.COM * events to maintain ordering. 15507978SPeter.Dunlap@Sun.COM * 15517978SPeter.Dunlap@Sun.COM * Note that IDM cannot enter FFP mode until it processes in 15527978SPeter.Dunlap@Sun.COM * its state machine the last xmit of the login process. 15537978SPeter.Dunlap@Sun.COM * Hence, checking the IDM_PDU_LOGIN_TX flag here would be 15547978SPeter.Dunlap@Sun.COM * superfluous. 15557978SPeter.Dunlap@Sun.COM */ 15567978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 15577978SPeter.Dunlap@Sun.COM if (ic->ic_ffp && (ic->ic_pdu_events == 0)) { 15587978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 15597978SPeter.Dunlap@Sun.COM switch (IDM_PDU_OPCODE(pdu)) { 15607978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_RSP: 15617978SPeter.Dunlap@Sun.COM /* Target only */ 15627978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15637978SPeter.Dunlap@Sun.COM return; 15647978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_RSP: 15657978SPeter.Dunlap@Sun.COM /* Target only */ 15667978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15677978SPeter.Dunlap@Sun.COM return; 15687978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA_RSP: 15697978SPeter.Dunlap@Sun.COM /* Target only */ 15707978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15717978SPeter.Dunlap@Sun.COM return; 15727978SPeter.Dunlap@Sun.COM case ISCSI_OP_RTT_RSP: 15737978SPeter.Dunlap@Sun.COM /* Target only */ 15747978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15757978SPeter.Dunlap@Sun.COM return; 15767978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_IN: 15777978SPeter.Dunlap@Sun.COM /* Target only */ 15787978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15797978SPeter.Dunlap@Sun.COM return; 15807978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_RSP: 15817978SPeter.Dunlap@Sun.COM /* Target only */ 15827978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15837978SPeter.Dunlap@Sun.COM return; 15847978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_CMD: 15857978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_OUT: 15867978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_CMD: 15877978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA: 15887978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_MSG: 15897978SPeter.Dunlap@Sun.COM /* Initiator only */ 15907978SPeter.Dunlap@Sun.COM idm_pdu_tx_forward(ic, pdu); 15917978SPeter.Dunlap@Sun.COM return; 15927978SPeter.Dunlap@Sun.COM default: 15937978SPeter.Dunlap@Sun.COM break; 15947978SPeter.Dunlap@Sun.COM } 15957978SPeter.Dunlap@Sun.COM 15967978SPeter.Dunlap@Sun.COM mutex_enter(&ic->ic_state_mutex); 15977978SPeter.Dunlap@Sun.COM } 15987978SPeter.Dunlap@Sun.COM 15997978SPeter.Dunlap@Sun.COM /* 16007978SPeter.Dunlap@Sun.COM * Any PDU's processed outside of full-feature mode and non-SCSI 16017978SPeter.Dunlap@Sun.COM * PDU's in full-feature mode are handled by generating an 16027978SPeter.Dunlap@Sun.COM * event to the connection state machine. The state machine 16037978SPeter.Dunlap@Sun.COM * will validate the PDU against the current state and either 16047978SPeter.Dunlap@Sun.COM * transmit the PDU if the opcode is allowed or handle an 16057978SPeter.Dunlap@Sun.COM * error if the PDU is not allowed. 16067978SPeter.Dunlap@Sun.COM * 16077978SPeter.Dunlap@Sun.COM * This code-path will also generate any events that are implied 16087978SPeter.Dunlap@Sun.COM * by the PDU opcode. For example a "login response" with success 16097978SPeter.Dunlap@Sun.COM * status generates a CE_LOGOUT_SUCCESS_SND event. 16107978SPeter.Dunlap@Sun.COM */ 16117978SPeter.Dunlap@Sun.COM switch (IDM_PDU_OPCODE(pdu)) { 16127978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGIN_CMD: 16137978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_LOGIN_SND, (uintptr_t)pdu); 16147978SPeter.Dunlap@Sun.COM break; 16157978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGIN_RSP: 16167978SPeter.Dunlap@Sun.COM idm_parse_login_rsp(ic, pdu, /* Is RX */ B_FALSE); 16177978SPeter.Dunlap@Sun.COM break; 16187978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGOUT_CMD: 16197978SPeter.Dunlap@Sun.COM idm_parse_logout_req(ic, pdu, /* Is RX */ B_FALSE); 16207978SPeter.Dunlap@Sun.COM break; 16217978SPeter.Dunlap@Sun.COM case ISCSI_OP_LOGOUT_RSP: 16227978SPeter.Dunlap@Sun.COM idm_parse_logout_rsp(ic, pdu, /* Is RX */ B_FALSE); 16237978SPeter.Dunlap@Sun.COM break; 16247978SPeter.Dunlap@Sun.COM case ISCSI_OP_ASYNC_EVENT: 16257978SPeter.Dunlap@Sun.COM async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr; 16267978SPeter.Dunlap@Sun.COM switch (async_evt->async_event) { 16277978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT: 16287978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_LOGOUT_SND, 16297978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 16307978SPeter.Dunlap@Sun.COM break; 16317978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION: 16327978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_CONN_SND, 16337978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 16347978SPeter.Dunlap@Sun.COM break; 16357978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS: 16367978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_SND, 16377978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 16387978SPeter.Dunlap@Sun.COM break; 16397978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_SCSI_EVENT: 16407978SPeter.Dunlap@Sun.COM case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION: 16417978SPeter.Dunlap@Sun.COM default: 16427978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, 16437978SPeter.Dunlap@Sun.COM (uintptr_t)pdu); 16447978SPeter.Dunlap@Sun.COM break; 16457978SPeter.Dunlap@Sun.COM } 16467978SPeter.Dunlap@Sun.COM break; 16477978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_RSP: 16487978SPeter.Dunlap@Sun.COM /* Target only */ 16497978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16507978SPeter.Dunlap@Sun.COM break; 16517978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_RSP: 16527978SPeter.Dunlap@Sun.COM /* Target only */ 16537978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16547978SPeter.Dunlap@Sun.COM break; 16557978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA_RSP: 16567978SPeter.Dunlap@Sun.COM /* Target only */ 16577978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16587978SPeter.Dunlap@Sun.COM break; 16597978SPeter.Dunlap@Sun.COM case ISCSI_OP_RTT_RSP: 16607978SPeter.Dunlap@Sun.COM /* Target only */ 16617978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16627978SPeter.Dunlap@Sun.COM break; 16637978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_IN: 16647978SPeter.Dunlap@Sun.COM /* Target only */ 16657978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16667978SPeter.Dunlap@Sun.COM break; 16677978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_RSP: 16687978SPeter.Dunlap@Sun.COM /* Target only */ 16697978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16707978SPeter.Dunlap@Sun.COM break; 16717978SPeter.Dunlap@Sun.COM /* Initiator only */ 16727978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_CMD: 16737978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_TASK_MGT_MSG: 16747978SPeter.Dunlap@Sun.COM case ISCSI_OP_SCSI_DATA: 16757978SPeter.Dunlap@Sun.COM case ISCSI_OP_NOOP_OUT: 16767978SPeter.Dunlap@Sun.COM case ISCSI_OP_TEXT_CMD: 16777978SPeter.Dunlap@Sun.COM case ISCSI_OP_SNACK_CMD: 16787978SPeter.Dunlap@Sun.COM case ISCSI_OP_REJECT_MSG: 16797978SPeter.Dunlap@Sun.COM default: 16807978SPeter.Dunlap@Sun.COM /* 16817978SPeter.Dunlap@Sun.COM * Connection state machine will validate these PDU's against 16827978SPeter.Dunlap@Sun.COM * the current state. A PDU not allowed in the current 16837978SPeter.Dunlap@Sun.COM * state will cause a protocol error. 16847978SPeter.Dunlap@Sun.COM */ 16857978SPeter.Dunlap@Sun.COM idm_conn_tx_pdu_event(ic, CE_MISC_TX, (uintptr_t)pdu); 16867978SPeter.Dunlap@Sun.COM break; 16877978SPeter.Dunlap@Sun.COM } 16887978SPeter.Dunlap@Sun.COM mutex_exit(&ic->ic_state_mutex); 16897978SPeter.Dunlap@Sun.COM } 16907978SPeter.Dunlap@Sun.COM 16917978SPeter.Dunlap@Sun.COM /* 16927978SPeter.Dunlap@Sun.COM * Allocates a PDU along with memory for header and data. 16937978SPeter.Dunlap@Sun.COM */ 16947978SPeter.Dunlap@Sun.COM 16957978SPeter.Dunlap@Sun.COM idm_pdu_t * 16967978SPeter.Dunlap@Sun.COM idm_pdu_alloc(uint_t hdrlen, uint_t datalen) 16977978SPeter.Dunlap@Sun.COM { 16987978SPeter.Dunlap@Sun.COM idm_pdu_t *result; 16997978SPeter.Dunlap@Sun.COM 17007978SPeter.Dunlap@Sun.COM /* 17017978SPeter.Dunlap@Sun.COM * IDM clients should cache these structures for performance 17027978SPeter.Dunlap@Sun.COM * critical paths. We can't cache effectively in IDM because we 17037978SPeter.Dunlap@Sun.COM * don't know the correct header and data size. 17047978SPeter.Dunlap@Sun.COM * 17057978SPeter.Dunlap@Sun.COM * Valid header length is assumed to be hdrlen and valid data 17067978SPeter.Dunlap@Sun.COM * length is assumed to be datalen. isp_hdrlen and isp_datalen 17077978SPeter.Dunlap@Sun.COM * can be adjusted after the PDU is returned if necessary. 17087978SPeter.Dunlap@Sun.COM */ 17097978SPeter.Dunlap@Sun.COM result = kmem_zalloc(sizeof (idm_pdu_t) + hdrlen + datalen, KM_SLEEP); 17107978SPeter.Dunlap@Sun.COM result->isp_flags |= IDM_PDU_ALLOC; /* For idm_pdu_free sanity check */ 17117978SPeter.Dunlap@Sun.COM result->isp_hdr = (iscsi_hdr_t *)(result + 1); /* Ptr. Arithmetic */ 17127978SPeter.Dunlap@Sun.COM result->isp_hdrlen = hdrlen; 17137978SPeter.Dunlap@Sun.COM result->isp_hdrbuflen = hdrlen; 17147978SPeter.Dunlap@Sun.COM result->isp_transport_hdrlen = 0; 17157978SPeter.Dunlap@Sun.COM result->isp_data = (uint8_t *)result->isp_hdr + hdrlen; 17167978SPeter.Dunlap@Sun.COM result->isp_datalen = datalen; 17177978SPeter.Dunlap@Sun.COM result->isp_databuflen = datalen; 17187978SPeter.Dunlap@Sun.COM result->isp_magic = IDM_PDU_MAGIC; 17197978SPeter.Dunlap@Sun.COM 17207978SPeter.Dunlap@Sun.COM return (result); 17217978SPeter.Dunlap@Sun.COM } 17227978SPeter.Dunlap@Sun.COM 17237978SPeter.Dunlap@Sun.COM /* 17247978SPeter.Dunlap@Sun.COM * Free a PDU previously allocated with idm_pdu_alloc() including any 17257978SPeter.Dunlap@Sun.COM * header and data space allocated as part of the original request. 17267978SPeter.Dunlap@Sun.COM * Additional memory regions referenced by subsequent modification of 17277978SPeter.Dunlap@Sun.COM * the isp_hdr and/or isp_data fields will not be freed. 17287978SPeter.Dunlap@Sun.COM */ 17297978SPeter.Dunlap@Sun.COM void 17307978SPeter.Dunlap@Sun.COM idm_pdu_free(idm_pdu_t *pdu) 17317978SPeter.Dunlap@Sun.COM { 17327978SPeter.Dunlap@Sun.COM /* Make sure the structure was allocated using idm_pdu_alloc() */ 17337978SPeter.Dunlap@Sun.COM ASSERT(pdu->isp_flags & IDM_PDU_ALLOC); 17347978SPeter.Dunlap@Sun.COM kmem_free(pdu, 17357978SPeter.Dunlap@Sun.COM sizeof (idm_pdu_t) + pdu->isp_hdrbuflen + pdu->isp_databuflen); 17367978SPeter.Dunlap@Sun.COM } 17377978SPeter.Dunlap@Sun.COM 17387978SPeter.Dunlap@Sun.COM /* 17397978SPeter.Dunlap@Sun.COM * Initialize the connection, private and callback fields in a PDU. 17407978SPeter.Dunlap@Sun.COM */ 17417978SPeter.Dunlap@Sun.COM void 17427978SPeter.Dunlap@Sun.COM idm_pdu_init(idm_pdu_t *pdu, idm_conn_t *ic, void *private, idm_pdu_cb_t *cb) 17437978SPeter.Dunlap@Sun.COM { 17447978SPeter.Dunlap@Sun.COM /* 17457978SPeter.Dunlap@Sun.COM * idm_pdu_complete() will call idm_pdu_free if the callback is 17467978SPeter.Dunlap@Sun.COM * NULL. This will only work if the PDU was originally allocated 17477978SPeter.Dunlap@Sun.COM * with idm_pdu_alloc(). 17487978SPeter.Dunlap@Sun.COM */ 17497978SPeter.Dunlap@Sun.COM ASSERT((pdu->isp_flags & IDM_PDU_ALLOC) || 17507978SPeter.Dunlap@Sun.COM (cb != NULL)); 17517978SPeter.Dunlap@Sun.COM pdu->isp_magic = IDM_PDU_MAGIC; 17527978SPeter.Dunlap@Sun.COM pdu->isp_ic = ic; 17537978SPeter.Dunlap@Sun.COM pdu->isp_private = private; 17547978SPeter.Dunlap@Sun.COM pdu->isp_callback = cb; 17557978SPeter.Dunlap@Sun.COM } 17567978SPeter.Dunlap@Sun.COM 17577978SPeter.Dunlap@Sun.COM /* 17587978SPeter.Dunlap@Sun.COM * Initialize the header and header length field. This function should 17597978SPeter.Dunlap@Sun.COM * not be used to adjust the header length in a buffer allocated via 17607978SPeter.Dunlap@Sun.COM * pdu_pdu_alloc since it overwrites the existing header pointer. 17617978SPeter.Dunlap@Sun.COM */ 17627978SPeter.Dunlap@Sun.COM void 17637978SPeter.Dunlap@Sun.COM idm_pdu_init_hdr(idm_pdu_t *pdu, uint8_t *hdr, uint_t hdrlen) 17647978SPeter.Dunlap@Sun.COM { 17657978SPeter.Dunlap@Sun.COM pdu->isp_hdr = (iscsi_hdr_t *)((void *)hdr); 17667978SPeter.Dunlap@Sun.COM pdu->isp_hdrlen = hdrlen; 17677978SPeter.Dunlap@Sun.COM } 17687978SPeter.Dunlap@Sun.COM 17697978SPeter.Dunlap@Sun.COM /* 17707978SPeter.Dunlap@Sun.COM * Initialize the data and data length fields. This function should 17717978SPeter.Dunlap@Sun.COM * not be used to adjust the data length of a buffer allocated via 17727978SPeter.Dunlap@Sun.COM * idm_pdu_alloc since it overwrites the existing data pointer. 17737978SPeter.Dunlap@Sun.COM */ 17747978SPeter.Dunlap@Sun.COM void 17757978SPeter.Dunlap@Sun.COM idm_pdu_init_data(idm_pdu_t *pdu, uint8_t *data, uint_t datalen) 17767978SPeter.Dunlap@Sun.COM { 17777978SPeter.Dunlap@Sun.COM pdu->isp_data = data; 17787978SPeter.Dunlap@Sun.COM pdu->isp_datalen = datalen; 17797978SPeter.Dunlap@Sun.COM } 17807978SPeter.Dunlap@Sun.COM 17817978SPeter.Dunlap@Sun.COM void 17827978SPeter.Dunlap@Sun.COM idm_pdu_complete(idm_pdu_t *pdu, idm_status_t status) 17837978SPeter.Dunlap@Sun.COM { 17847978SPeter.Dunlap@Sun.COM if (pdu->isp_callback) { 17857978SPeter.Dunlap@Sun.COM pdu->isp_status = status; 17867978SPeter.Dunlap@Sun.COM (*pdu->isp_callback)(pdu, status); 17877978SPeter.Dunlap@Sun.COM } else { 17887978SPeter.Dunlap@Sun.COM idm_pdu_free(pdu); 17897978SPeter.Dunlap@Sun.COM } 17907978SPeter.Dunlap@Sun.COM } 17917978SPeter.Dunlap@Sun.COM 17927978SPeter.Dunlap@Sun.COM /* 17937978SPeter.Dunlap@Sun.COM * State machine auditing 17947978SPeter.Dunlap@Sun.COM */ 17957978SPeter.Dunlap@Sun.COM 17967978SPeter.Dunlap@Sun.COM void 17977978SPeter.Dunlap@Sun.COM idm_sm_audit_init(sm_audit_buf_t *audit_buf) 17987978SPeter.Dunlap@Sun.COM { 17997978SPeter.Dunlap@Sun.COM bzero(audit_buf, sizeof (sm_audit_buf_t)); 18007978SPeter.Dunlap@Sun.COM audit_buf->sab_max_index = SM_AUDIT_BUF_MAX_REC - 1; 18017978SPeter.Dunlap@Sun.COM } 18027978SPeter.Dunlap@Sun.COM 18037978SPeter.Dunlap@Sun.COM static 18047978SPeter.Dunlap@Sun.COM sm_audit_record_t * 18057978SPeter.Dunlap@Sun.COM idm_sm_audit_common(sm_audit_buf_t *audit_buf, sm_audit_record_type_t r_type, 18067978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, 18077978SPeter.Dunlap@Sun.COM int current_state) 18087978SPeter.Dunlap@Sun.COM { 18097978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 18107978SPeter.Dunlap@Sun.COM 18117978SPeter.Dunlap@Sun.COM sar = audit_buf->sab_records; 18127978SPeter.Dunlap@Sun.COM sar += audit_buf->sab_index; 18137978SPeter.Dunlap@Sun.COM audit_buf->sab_index++; 18147978SPeter.Dunlap@Sun.COM audit_buf->sab_index &= audit_buf->sab_max_index; 18157978SPeter.Dunlap@Sun.COM 18167978SPeter.Dunlap@Sun.COM sar->sar_type = r_type; 18177978SPeter.Dunlap@Sun.COM gethrestime(&sar->sar_timestamp); 18187978SPeter.Dunlap@Sun.COM sar->sar_sm_type = sm_type; 18197978SPeter.Dunlap@Sun.COM sar->sar_state = current_state; 18207978SPeter.Dunlap@Sun.COM 18217978SPeter.Dunlap@Sun.COM return (sar); 18227978SPeter.Dunlap@Sun.COM } 18237978SPeter.Dunlap@Sun.COM 18247978SPeter.Dunlap@Sun.COM void 18257978SPeter.Dunlap@Sun.COM idm_sm_audit_event(sm_audit_buf_t *audit_buf, 18267978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, int current_state, 18277978SPeter.Dunlap@Sun.COM int event, uintptr_t event_info) 18287978SPeter.Dunlap@Sun.COM { 18297978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 18307978SPeter.Dunlap@Sun.COM 18317978SPeter.Dunlap@Sun.COM sar = idm_sm_audit_common(audit_buf, SAR_STATE_EVENT, 18327978SPeter.Dunlap@Sun.COM sm_type, current_state); 18337978SPeter.Dunlap@Sun.COM sar->sar_event = event; 18347978SPeter.Dunlap@Sun.COM sar->sar_event_info = event_info; 18357978SPeter.Dunlap@Sun.COM } 18367978SPeter.Dunlap@Sun.COM 18377978SPeter.Dunlap@Sun.COM void 18387978SPeter.Dunlap@Sun.COM idm_sm_audit_state_change(sm_audit_buf_t *audit_buf, 18397978SPeter.Dunlap@Sun.COM sm_audit_sm_type_t sm_type, int current_state, int new_state) 18407978SPeter.Dunlap@Sun.COM { 18417978SPeter.Dunlap@Sun.COM sm_audit_record_t *sar; 18427978SPeter.Dunlap@Sun.COM 18437978SPeter.Dunlap@Sun.COM sar = idm_sm_audit_common(audit_buf, SAR_STATE_CHANGE, 18447978SPeter.Dunlap@Sun.COM sm_type, current_state); 18457978SPeter.Dunlap@Sun.COM sar->sar_new_state = new_state; 18467978SPeter.Dunlap@Sun.COM } 18477978SPeter.Dunlap@Sun.COM 18487978SPeter.Dunlap@Sun.COM 18497978SPeter.Dunlap@Sun.COM /* 18507978SPeter.Dunlap@Sun.COM * Object reference tracking 18517978SPeter.Dunlap@Sun.COM */ 18527978SPeter.Dunlap@Sun.COM 18537978SPeter.Dunlap@Sun.COM void 18547978SPeter.Dunlap@Sun.COM idm_refcnt_init(idm_refcnt_t *refcnt, void *referenced_obj) 18557978SPeter.Dunlap@Sun.COM { 18567978SPeter.Dunlap@Sun.COM bzero(refcnt, sizeof (*refcnt)); 18577978SPeter.Dunlap@Sun.COM idm_refcnt_reset(refcnt); 18587978SPeter.Dunlap@Sun.COM refcnt->ir_referenced_obj = referenced_obj; 18597978SPeter.Dunlap@Sun.COM bzero(&refcnt->ir_audit_buf, sizeof (refcnt_audit_buf_t)); 18607978SPeter.Dunlap@Sun.COM refcnt->ir_audit_buf.anb_max_index = REFCNT_AUDIT_BUF_MAX_REC - 1; 18617978SPeter.Dunlap@Sun.COM mutex_init(&refcnt->ir_mutex, NULL, MUTEX_DEFAULT, NULL); 18627978SPeter.Dunlap@Sun.COM cv_init(&refcnt->ir_cv, NULL, CV_DEFAULT, NULL); 18637978SPeter.Dunlap@Sun.COM } 18647978SPeter.Dunlap@Sun.COM 18657978SPeter.Dunlap@Sun.COM void 18667978SPeter.Dunlap@Sun.COM idm_refcnt_destroy(idm_refcnt_t *refcnt) 18677978SPeter.Dunlap@Sun.COM { 18687978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt == 0); 18697978SPeter.Dunlap@Sun.COM cv_destroy(&refcnt->ir_cv); 18707978SPeter.Dunlap@Sun.COM mutex_destroy(&refcnt->ir_mutex); 18717978SPeter.Dunlap@Sun.COM } 18727978SPeter.Dunlap@Sun.COM 18737978SPeter.Dunlap@Sun.COM void 18747978SPeter.Dunlap@Sun.COM idm_refcnt_reset(idm_refcnt_t *refcnt) 18757978SPeter.Dunlap@Sun.COM { 18767978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_NOWAIT; 18777978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt = 0; 18787978SPeter.Dunlap@Sun.COM } 18797978SPeter.Dunlap@Sun.COM 18807978SPeter.Dunlap@Sun.COM void 18817978SPeter.Dunlap@Sun.COM idm_refcnt_hold(idm_refcnt_t *refcnt) 18827978SPeter.Dunlap@Sun.COM { 18837978SPeter.Dunlap@Sun.COM /* 18847978SPeter.Dunlap@Sun.COM * Nothing should take a hold on an object after a call to 18857978SPeter.Dunlap@Sun.COM * idm_refcnt_wait_ref or idm_refcnd_async_wait_ref 18867978SPeter.Dunlap@Sun.COM */ 18877978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_waiting == REF_NOWAIT); 18887978SPeter.Dunlap@Sun.COM 18897978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 18907978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt++; 18917978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 18927978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 18937978SPeter.Dunlap@Sun.COM } 18947978SPeter.Dunlap@Sun.COM 18957978SPeter.Dunlap@Sun.COM static void 18967978SPeter.Dunlap@Sun.COM idm_refcnt_unref_task(void *refcnt_void) 18977978SPeter.Dunlap@Sun.COM { 18987978SPeter.Dunlap@Sun.COM idm_refcnt_t *refcnt = refcnt_void; 18997978SPeter.Dunlap@Sun.COM 19007978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 19017978SPeter.Dunlap@Sun.COM (*refcnt->ir_cb)(refcnt->ir_referenced_obj); 19027978SPeter.Dunlap@Sun.COM } 19037978SPeter.Dunlap@Sun.COM 19047978SPeter.Dunlap@Sun.COM void 19057978SPeter.Dunlap@Sun.COM idm_refcnt_rele(idm_refcnt_t *refcnt) 19067978SPeter.Dunlap@Sun.COM { 19077978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 19087978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt > 0); 19097978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt--; 19107978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 19117978SPeter.Dunlap@Sun.COM if (refcnt->ir_waiting == REF_NOWAIT) { 19127978SPeter.Dunlap@Sun.COM /* No one is waiting on this object */ 19137978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 19147978SPeter.Dunlap@Sun.COM return; 19157978SPeter.Dunlap@Sun.COM } 19167978SPeter.Dunlap@Sun.COM 19177978SPeter.Dunlap@Sun.COM /* 19187978SPeter.Dunlap@Sun.COM * Someone is waiting for this object to go idle so check if 19197978SPeter.Dunlap@Sun.COM * refcnt is 0. Waiting on an object then later grabbing another 19207978SPeter.Dunlap@Sun.COM * reference is not allowed so we don't need to handle that case. 19217978SPeter.Dunlap@Sun.COM */ 19227978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 19237978SPeter.Dunlap@Sun.COM if (refcnt->ir_waiting == REF_WAIT_ASYNC) { 19247978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 19257978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 19267978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 19277978SPeter.Dunlap@Sun.COM "idm_refcnt_rele: Couldn't dispatch task"); 19287978SPeter.Dunlap@Sun.COM } 19297978SPeter.Dunlap@Sun.COM } else if (refcnt->ir_waiting == REF_WAIT_SYNC) { 19307978SPeter.Dunlap@Sun.COM cv_signal(&refcnt->ir_cv); 19317978SPeter.Dunlap@Sun.COM } 19327978SPeter.Dunlap@Sun.COM } 19337978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 19347978SPeter.Dunlap@Sun.COM } 19357978SPeter.Dunlap@Sun.COM 19367978SPeter.Dunlap@Sun.COM void 19377978SPeter.Dunlap@Sun.COM idm_refcnt_rele_and_destroy(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func) 19387978SPeter.Dunlap@Sun.COM { 19397978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 19407978SPeter.Dunlap@Sun.COM ASSERT(refcnt->ir_refcnt > 0); 19417978SPeter.Dunlap@Sun.COM refcnt->ir_refcnt--; 19427978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 19437978SPeter.Dunlap@Sun.COM 19447978SPeter.Dunlap@Sun.COM /* 19457978SPeter.Dunlap@Sun.COM * Someone is waiting for this object to go idle so check if 19467978SPeter.Dunlap@Sun.COM * refcnt is 0. Waiting on an object then later grabbing another 19477978SPeter.Dunlap@Sun.COM * reference is not allowed so we don't need to handle that case. 19487978SPeter.Dunlap@Sun.COM */ 19497978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 19507978SPeter.Dunlap@Sun.COM refcnt->ir_cb = cb_func; 19517978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_ASYNC; 19527978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 19537978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 19547978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 19557978SPeter.Dunlap@Sun.COM "idm_refcnt_rele: Couldn't dispatch task"); 19567978SPeter.Dunlap@Sun.COM } 19577978SPeter.Dunlap@Sun.COM } 19587978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 19597978SPeter.Dunlap@Sun.COM } 19607978SPeter.Dunlap@Sun.COM 19617978SPeter.Dunlap@Sun.COM void 19627978SPeter.Dunlap@Sun.COM idm_refcnt_wait_ref(idm_refcnt_t *refcnt) 19637978SPeter.Dunlap@Sun.COM { 19647978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 19657978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_SYNC; 19667978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 19677978SPeter.Dunlap@Sun.COM while (refcnt->ir_refcnt != 0) 19687978SPeter.Dunlap@Sun.COM cv_wait(&refcnt->ir_cv, &refcnt->ir_mutex); 19697978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 19707978SPeter.Dunlap@Sun.COM } 19717978SPeter.Dunlap@Sun.COM 19727978SPeter.Dunlap@Sun.COM void 19737978SPeter.Dunlap@Sun.COM idm_refcnt_async_wait_ref(idm_refcnt_t *refcnt, idm_refcnt_cb_t *cb_func) 19747978SPeter.Dunlap@Sun.COM { 19757978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 19767978SPeter.Dunlap@Sun.COM refcnt->ir_waiting = REF_WAIT_ASYNC; 19777978SPeter.Dunlap@Sun.COM refcnt->ir_cb = cb_func; 19787978SPeter.Dunlap@Sun.COM REFCNT_AUDIT(refcnt); 19797978SPeter.Dunlap@Sun.COM /* 19807978SPeter.Dunlap@Sun.COM * It's possible we don't have any references. To make things easier 19817978SPeter.Dunlap@Sun.COM * on the caller use a taskq to call the callback instead of 19827978SPeter.Dunlap@Sun.COM * calling it synchronously 19837978SPeter.Dunlap@Sun.COM */ 19847978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 19857978SPeter.Dunlap@Sun.COM if (taskq_dispatch(idm.idm_global_taskq, 19867978SPeter.Dunlap@Sun.COM &idm_refcnt_unref_task, refcnt, TQ_SLEEP) == NULL) { 19877978SPeter.Dunlap@Sun.COM cmn_err(CE_WARN, 19887978SPeter.Dunlap@Sun.COM "idm_refcnt_async_wait_ref: " 19897978SPeter.Dunlap@Sun.COM "Couldn't dispatch task"); 19907978SPeter.Dunlap@Sun.COM } 19917978SPeter.Dunlap@Sun.COM } 19927978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 19937978SPeter.Dunlap@Sun.COM } 19947978SPeter.Dunlap@Sun.COM 19957978SPeter.Dunlap@Sun.COM void 19967978SPeter.Dunlap@Sun.COM idm_refcnt_destroy_unref_obj(idm_refcnt_t *refcnt, 19977978SPeter.Dunlap@Sun.COM idm_refcnt_cb_t *cb_func) 19987978SPeter.Dunlap@Sun.COM { 19997978SPeter.Dunlap@Sun.COM mutex_enter(&refcnt->ir_mutex); 20007978SPeter.Dunlap@Sun.COM if (refcnt->ir_refcnt == 0) { 20017978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 20027978SPeter.Dunlap@Sun.COM (*cb_func)(refcnt->ir_referenced_obj); 20037978SPeter.Dunlap@Sun.COM return; 20047978SPeter.Dunlap@Sun.COM } 20057978SPeter.Dunlap@Sun.COM mutex_exit(&refcnt->ir_mutex); 20067978SPeter.Dunlap@Sun.COM } 20077978SPeter.Dunlap@Sun.COM 20087978SPeter.Dunlap@Sun.COM void 20097978SPeter.Dunlap@Sun.COM idm_conn_hold(idm_conn_t *ic) 20107978SPeter.Dunlap@Sun.COM { 20117978SPeter.Dunlap@Sun.COM idm_refcnt_hold(&ic->ic_refcnt); 20127978SPeter.Dunlap@Sun.COM } 20137978SPeter.Dunlap@Sun.COM 20147978SPeter.Dunlap@Sun.COM void 20157978SPeter.Dunlap@Sun.COM idm_conn_rele(idm_conn_t *ic) 20167978SPeter.Dunlap@Sun.COM { 20177978SPeter.Dunlap@Sun.COM idm_refcnt_rele(&ic->ic_refcnt); 20187978SPeter.Dunlap@Sun.COM } 20197978SPeter.Dunlap@Sun.COM 20207978SPeter.Dunlap@Sun.COM 20217978SPeter.Dunlap@Sun.COM static int 20227978SPeter.Dunlap@Sun.COM _idm_init(void) 20237978SPeter.Dunlap@Sun.COM { 20247978SPeter.Dunlap@Sun.COM /* Initialize the rwlock for the taskid table */ 20257978SPeter.Dunlap@Sun.COM rw_init(&idm.idm_taskid_table_lock, NULL, RW_DRIVER, NULL); 20267978SPeter.Dunlap@Sun.COM 20277978SPeter.Dunlap@Sun.COM /* Initialize the global mutex and taskq */ 20287978SPeter.Dunlap@Sun.COM mutex_init(&idm.idm_global_mutex, NULL, MUTEX_DEFAULT, NULL); 20297978SPeter.Dunlap@Sun.COM 20307978SPeter.Dunlap@Sun.COM cv_init(&idm.idm_tgt_svc_cv, NULL, CV_DEFAULT, NULL); 20317978SPeter.Dunlap@Sun.COM cv_init(&idm.idm_wd_cv, NULL, CV_DEFAULT, NULL); 20327978SPeter.Dunlap@Sun.COM 20337978SPeter.Dunlap@Sun.COM idm.idm_global_taskq = taskq_create("idm_global_taskq", 1, minclsyspri, 20347978SPeter.Dunlap@Sun.COM 4, 4, TASKQ_PREPOPULATE); 20357978SPeter.Dunlap@Sun.COM if (idm.idm_global_taskq == NULL) { 20367978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 20377978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 20387978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 20397978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 20407978SPeter.Dunlap@Sun.COM return (ENOMEM); 20417978SPeter.Dunlap@Sun.COM } 20427978SPeter.Dunlap@Sun.COM 2043*8209SJames.Moore@Sun.COM /* Start watchdog thread */ 20447978SPeter.Dunlap@Sun.COM idm.idm_wd_thread = thread_create(NULL, 0, 20457978SPeter.Dunlap@Sun.COM idm_wd_thread, NULL, 0, &p0, TS_RUN, minclsyspri); 20467978SPeter.Dunlap@Sun.COM if (idm.idm_wd_thread == NULL) { 20477978SPeter.Dunlap@Sun.COM /* Couldn't create the watchdog thread */ 20487978SPeter.Dunlap@Sun.COM taskq_destroy(idm.idm_global_taskq); 20497978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 20507978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 20517978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 20527978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 20537978SPeter.Dunlap@Sun.COM return (ENOMEM); 20547978SPeter.Dunlap@Sun.COM } 20557978SPeter.Dunlap@Sun.COM 2056*8209SJames.Moore@Sun.COM /* Pause until the watchdog thread is running */ 20577978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 20587978SPeter.Dunlap@Sun.COM while (!idm.idm_wd_thread_running) 20597978SPeter.Dunlap@Sun.COM cv_wait(&idm.idm_wd_cv, &idm.idm_global_mutex); 20607978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 20617978SPeter.Dunlap@Sun.COM 20627978SPeter.Dunlap@Sun.COM /* 20637978SPeter.Dunlap@Sun.COM * Allocate the task ID table and set "next" to 0. 20647978SPeter.Dunlap@Sun.COM */ 20657978SPeter.Dunlap@Sun.COM 20667978SPeter.Dunlap@Sun.COM idm.idm_taskid_max = idm_max_taskids; 20677978SPeter.Dunlap@Sun.COM idm.idm_taskid_table = (idm_task_t **) 20687978SPeter.Dunlap@Sun.COM kmem_zalloc(idm.idm_taskid_max * sizeof (idm_task_t *), KM_SLEEP); 20697978SPeter.Dunlap@Sun.COM idm.idm_taskid_next = 0; 20707978SPeter.Dunlap@Sun.COM 20717978SPeter.Dunlap@Sun.COM /* Create the global buffer and task kmem caches */ 20727978SPeter.Dunlap@Sun.COM idm.idm_buf_cache = kmem_cache_create("idm_buf_cache", 20737978SPeter.Dunlap@Sun.COM sizeof (idm_buf_t), 8, NULL, NULL, NULL, NULL, NULL, KM_SLEEP); 20747978SPeter.Dunlap@Sun.COM 20757978SPeter.Dunlap@Sun.COM /* 20767978SPeter.Dunlap@Sun.COM * Note, we're explicitly allocating an additional iSER header- 20777978SPeter.Dunlap@Sun.COM * sized chunk for each of these elements. See idm_task_constructor(). 20787978SPeter.Dunlap@Sun.COM */ 20797978SPeter.Dunlap@Sun.COM idm.idm_task_cache = kmem_cache_create("idm_task_cache", 20807978SPeter.Dunlap@Sun.COM sizeof (idm_task_t) + IDM_TRANSPORT_HEADER_LENGTH, 8, 20817978SPeter.Dunlap@Sun.COM &idm_task_constructor, &idm_task_destructor, 20827978SPeter.Dunlap@Sun.COM NULL, NULL, NULL, KM_SLEEP); 20837978SPeter.Dunlap@Sun.COM 20847978SPeter.Dunlap@Sun.COM /* Create the service and connection context lists */ 20857978SPeter.Dunlap@Sun.COM list_create(&idm.idm_tgt_svc_list, sizeof (idm_svc_t), 20867978SPeter.Dunlap@Sun.COM offsetof(idm_svc_t, is_list_node)); 20877978SPeter.Dunlap@Sun.COM list_create(&idm.idm_tgt_conn_list, sizeof (idm_conn_t), 20887978SPeter.Dunlap@Sun.COM offsetof(idm_conn_t, ic_list_node)); 20897978SPeter.Dunlap@Sun.COM list_create(&idm.idm_ini_conn_list, sizeof (idm_conn_t), 20907978SPeter.Dunlap@Sun.COM offsetof(idm_conn_t, ic_list_node)); 20917978SPeter.Dunlap@Sun.COM 20927978SPeter.Dunlap@Sun.COM /* Initialize the native sockets transport */ 20937978SPeter.Dunlap@Sun.COM idm_so_init(&idm_transport_list[IDM_TRANSPORT_TYPE_SOCKETS]); 20947978SPeter.Dunlap@Sun.COM 20957978SPeter.Dunlap@Sun.COM /* Create connection ID pool */ 20967978SPeter.Dunlap@Sun.COM (void) idm_idpool_create(&idm.idm_conn_id_pool); 20977978SPeter.Dunlap@Sun.COM 20987978SPeter.Dunlap@Sun.COM return (DDI_SUCCESS); 20997978SPeter.Dunlap@Sun.COM } 21007978SPeter.Dunlap@Sun.COM 21017978SPeter.Dunlap@Sun.COM static int 21027978SPeter.Dunlap@Sun.COM _idm_fini(void) 21037978SPeter.Dunlap@Sun.COM { 21047978SPeter.Dunlap@Sun.COM if (!list_is_empty(&idm.idm_ini_conn_list) || 21057978SPeter.Dunlap@Sun.COM !list_is_empty(&idm.idm_tgt_conn_list) || 21067978SPeter.Dunlap@Sun.COM !list_is_empty(&idm.idm_tgt_svc_list)) { 21077978SPeter.Dunlap@Sun.COM return (EBUSY); 21087978SPeter.Dunlap@Sun.COM } 21097978SPeter.Dunlap@Sun.COM 21107978SPeter.Dunlap@Sun.COM mutex_enter(&idm.idm_global_mutex); 21117978SPeter.Dunlap@Sun.COM idm.idm_wd_thread_running = B_FALSE; 21127978SPeter.Dunlap@Sun.COM cv_signal(&idm.idm_wd_cv); 21137978SPeter.Dunlap@Sun.COM mutex_exit(&idm.idm_global_mutex); 21147978SPeter.Dunlap@Sun.COM 21157978SPeter.Dunlap@Sun.COM thread_join(idm.idm_wd_thread_did); 21167978SPeter.Dunlap@Sun.COM 21177978SPeter.Dunlap@Sun.COM idm_idpool_destroy(&idm.idm_conn_id_pool); 21187978SPeter.Dunlap@Sun.COM idm_so_fini(); 21197978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_ini_conn_list); 21207978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_tgt_conn_list); 21217978SPeter.Dunlap@Sun.COM list_destroy(&idm.idm_tgt_svc_list); 21227978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_task_cache); 21237978SPeter.Dunlap@Sun.COM kmem_cache_destroy(idm.idm_buf_cache); 21247978SPeter.Dunlap@Sun.COM kmem_free(idm.idm_taskid_table, 21257978SPeter.Dunlap@Sun.COM idm.idm_taskid_max * sizeof (idm_task_t *)); 21267978SPeter.Dunlap@Sun.COM mutex_destroy(&idm.idm_global_mutex); 21277978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_wd_cv); 21287978SPeter.Dunlap@Sun.COM cv_destroy(&idm.idm_tgt_svc_cv); 21297978SPeter.Dunlap@Sun.COM rw_destroy(&idm.idm_taskid_table_lock); 21307978SPeter.Dunlap@Sun.COM 21317978SPeter.Dunlap@Sun.COM return (0); 21327978SPeter.Dunlap@Sun.COM } 2133