11ae08745Sheppo /* 21ae08745Sheppo * CDDL HEADER START 31ae08745Sheppo * 41ae08745Sheppo * The contents of this file are subject to the terms of the 51ae08745Sheppo * Common Development and Distribution License (the "License"). 61ae08745Sheppo * You may not use this file except in compliance with the License. 71ae08745Sheppo * 81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91ae08745Sheppo * or http://www.opensolaris.org/os/licensing. 101ae08745Sheppo * See the License for the specific language governing permissions 111ae08745Sheppo * and limitations under the License. 121ae08745Sheppo * 131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the 161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181ae08745Sheppo * 191ae08745Sheppo * CDDL HEADER END 201ae08745Sheppo */ 211ae08745Sheppo 221ae08745Sheppo /* 2383d3bc6fSnarayan * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 241ae08745Sheppo * Use is subject to license terms. 251ae08745Sheppo */ 261ae08745Sheppo 271ae08745Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 281ae08745Sheppo 291ae08745Sheppo /* 30e1ebb9ecSlm66018 * sun4v LDC Link Layer 311ae08745Sheppo */ 321ae08745Sheppo #include <sys/types.h> 331ae08745Sheppo #include <sys/file.h> 341ae08745Sheppo #include <sys/errno.h> 351ae08745Sheppo #include <sys/open.h> 361ae08745Sheppo #include <sys/cred.h> 371ae08745Sheppo #include <sys/kmem.h> 381ae08745Sheppo #include <sys/conf.h> 391ae08745Sheppo #include <sys/cmn_err.h> 401ae08745Sheppo #include <sys/ksynch.h> 411ae08745Sheppo #include <sys/modctl.h> 421ae08745Sheppo #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */ 431ae08745Sheppo #include <sys/debug.h> 441ae08745Sheppo #include <sys/types.h> 451ae08745Sheppo #include <sys/cred.h> 461ae08745Sheppo #include <sys/promif.h> 471ae08745Sheppo #include <sys/ddi.h> 481ae08745Sheppo #include <sys/sunddi.h> 491ae08745Sheppo #include <sys/cyclic.h> 501ae08745Sheppo #include <sys/machsystm.h> 511ae08745Sheppo #include <sys/vm.h> 521ae08745Sheppo #include <sys/cpu.h> 531ae08745Sheppo #include <sys/intreg.h> 541ae08745Sheppo #include <sys/machcpuvar.h> 554bac2208Snarayan #include <sys/mmu.h> 564bac2208Snarayan #include <sys/pte.h> 574bac2208Snarayan #include <vm/hat.h> 584bac2208Snarayan #include <vm/as.h> 594bac2208Snarayan #include <vm/hat_sfmmu.h> 604bac2208Snarayan #include <sys/vm_machparam.h> 614bac2208Snarayan #include <vm/seg_kmem.h> 624bac2208Snarayan #include <vm/seg_kpm.h> 631ae08745Sheppo #include <sys/note.h> 641ae08745Sheppo #include <sys/ivintr.h> 651ae08745Sheppo #include <sys/hypervisor_api.h> 661ae08745Sheppo #include <sys/ldc.h> 671ae08745Sheppo #include <sys/ldc_impl.h> 681ae08745Sheppo #include <sys/cnex.h> 691ae08745Sheppo #include <sys/hsvc.h> 701ae08745Sheppo 711ae08745Sheppo /* Core internal functions */ 721ae08745Sheppo static int i_ldc_h2v_error(int h_error); 731ae08745Sheppo static int i_ldc_txq_reconf(ldc_chan_t *ldcp); 743af08d82Slm66018 static int i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset); 75a8ea4edeSnarayan static int i_ldc_rxq_drain(ldc_chan_t *ldcp); 761ae08745Sheppo static void i_ldc_reset_state(ldc_chan_t *ldcp); 773af08d82Slm66018 static void i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset); 781ae08745Sheppo 791ae08745Sheppo static int i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail); 8022f747efSnarayan static void i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head); 811ae08745Sheppo static int i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail); 821ae08745Sheppo static int i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head); 831ae08745Sheppo static int i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype, 841ae08745Sheppo uint8_t ctrlmsg); 851ae08745Sheppo 861ae08745Sheppo /* Interrupt handling functions */ 871ae08745Sheppo static uint_t i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2); 881ae08745Sheppo static uint_t i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2); 891ae08745Sheppo static void i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype); 901ae08745Sheppo 911ae08745Sheppo /* Read method functions */ 921ae08745Sheppo static int i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep); 931ae08745Sheppo static int i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, 941ae08745Sheppo size_t *sizep); 951ae08745Sheppo static int i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp, 961ae08745Sheppo size_t *sizep); 971ae08745Sheppo 981ae08745Sheppo /* Write method functions */ 991ae08745Sheppo static int i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t target_bufp, 1001ae08745Sheppo size_t *sizep); 1011ae08745Sheppo static int i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t target_bufp, 1021ae08745Sheppo size_t *sizep); 1031ae08745Sheppo static int i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t target_bufp, 1041ae08745Sheppo size_t *sizep); 1051ae08745Sheppo 1061ae08745Sheppo /* Pkt processing internal functions */ 1071ae08745Sheppo static int i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg); 1081ae08745Sheppo static int i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg); 1091ae08745Sheppo static int i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg); 1101ae08745Sheppo static int i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg); 1111ae08745Sheppo static int i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg); 1121ae08745Sheppo static int i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg); 1131ae08745Sheppo static int i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg); 1141ae08745Sheppo 1151ae08745Sheppo /* Memory synchronization internal functions */ 1161ae08745Sheppo static int i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle, 1171ae08745Sheppo uint8_t direction, uint64_t offset, size_t size); 1181ae08745Sheppo static int i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle, 1191ae08745Sheppo uint8_t direction, uint64_t start, uint64_t end); 1201ae08745Sheppo 1211ae08745Sheppo /* LDC Version */ 1221ae08745Sheppo static ldc_ver_t ldc_versions[] = { {1, 0} }; 1231ae08745Sheppo 1241ae08745Sheppo /* number of supported versions */ 1251ae08745Sheppo #define LDC_NUM_VERS (sizeof (ldc_versions) / sizeof (ldc_versions[0])) 1261ae08745Sheppo 1271ae08745Sheppo /* Module State Pointer */ 1281ae08745Sheppo static ldc_soft_state_t *ldcssp; 1291ae08745Sheppo 1301ae08745Sheppo static struct modldrv md = { 1311ae08745Sheppo &mod_miscops, /* This is a misc module */ 1321ae08745Sheppo "sun4v LDC module v%I%", /* Name of the module */ 1331ae08745Sheppo }; 1341ae08745Sheppo 1351ae08745Sheppo static struct modlinkage ml = { 1361ae08745Sheppo MODREV_1, 1371ae08745Sheppo &md, 1381ae08745Sheppo NULL 1391ae08745Sheppo }; 1401ae08745Sheppo 1411ae08745Sheppo static uint64_t ldc_sup_minor; /* Supported minor number */ 1421ae08745Sheppo static hsvc_info_t ldc_hsvc = { 1431ae08745Sheppo HSVC_REV_1, NULL, HSVC_GROUP_LDC, 1, 0, "ldc" 1441ae08745Sheppo }; 1451ae08745Sheppo 1464bac2208Snarayan /* 1474bac2208Snarayan * LDC framework supports mapping remote domain's memory 1484bac2208Snarayan * either directly or via shadow memory pages. Default 1494bac2208Snarayan * support is currently implemented via shadow copy. 1504bac2208Snarayan * Direct map can be enabled by setting 'ldc_shmem_enabled' 1514bac2208Snarayan */ 1524bac2208Snarayan int ldc_shmem_enabled = 0; 153e1ebb9ecSlm66018 1540a55fbb7Slm66018 /* 155e1ebb9ecSlm66018 * The no. of MTU size messages that can be stored in 156e1ebb9ecSlm66018 * the LDC Tx queue. The number of Tx queue entries is 157e1ebb9ecSlm66018 * then computed as (mtu * mtu_msgs)/sizeof(queue_entry) 158e1ebb9ecSlm66018 */ 159e1ebb9ecSlm66018 uint64_t ldc_mtu_msgs = LDC_MTU_MSGS; 160e1ebb9ecSlm66018 161e1ebb9ecSlm66018 /* 162e1ebb9ecSlm66018 * The minimum queue length. This is the size of the smallest 163e1ebb9ecSlm66018 * LDC queue. If the computed value is less than this default, 164e1ebb9ecSlm66018 * the queue length is rounded up to 'ldc_queue_entries'. 165e1ebb9ecSlm66018 */ 166e1ebb9ecSlm66018 uint64_t ldc_queue_entries = LDC_QUEUE_ENTRIES; 167e1ebb9ecSlm66018 168e1ebb9ecSlm66018 /* 169e1ebb9ecSlm66018 * Pages exported for remote access over each channel is 170e1ebb9ecSlm66018 * maintained in a table registered with the Hypervisor. 171e1ebb9ecSlm66018 * The default number of entries in the table is set to 172e1ebb9ecSlm66018 * 'ldc_mtbl_entries'. 173e1ebb9ecSlm66018 */ 174e1ebb9ecSlm66018 uint64_t ldc_maptable_entries = LDC_MTBL_ENTRIES; 175e1ebb9ecSlm66018 176e1ebb9ecSlm66018 /* 177e1ebb9ecSlm66018 * LDC retry count and delay - when the HV returns EWOULDBLOCK 178e1ebb9ecSlm66018 * the operation is retried 'ldc_max_retries' times with a 179e1ebb9ecSlm66018 * wait of 'ldc_delay' usecs between each retry. 1800a55fbb7Slm66018 */ 1810a55fbb7Slm66018 int ldc_max_retries = LDC_MAX_RETRIES; 1820a55fbb7Slm66018 clock_t ldc_delay = LDC_DELAY; 1830a55fbb7Slm66018 1844d39be2bSsg70180 /* 1854d39be2bSsg70180 * delay between each retry of channel unregistration in 1864d39be2bSsg70180 * ldc_close(), to wait for pending interrupts to complete. 1874d39be2bSsg70180 */ 1884d39be2bSsg70180 clock_t ldc_close_delay = LDC_CLOSE_DELAY; 1894d39be2bSsg70180 1901ae08745Sheppo #ifdef DEBUG 1911ae08745Sheppo 1921ae08745Sheppo /* 1931ae08745Sheppo * Print debug messages 1941ae08745Sheppo * 1951ae08745Sheppo * set ldcdbg to 0x7 for enabling all msgs 1961ae08745Sheppo * 0x4 - Warnings 1971ae08745Sheppo * 0x2 - All debug messages 1981ae08745Sheppo * 0x1 - Minimal debug messages 1991ae08745Sheppo * 2001ae08745Sheppo * set ldcdbgchan to the channel number you want to debug 2011ae08745Sheppo * setting it to -1 prints debug messages for all channels 2021ae08745Sheppo * NOTE: ldcdbgchan has no effect on error messages 2031ae08745Sheppo */ 2041ae08745Sheppo 2051ae08745Sheppo #define DBG_ALL_LDCS -1 2061ae08745Sheppo 2071ae08745Sheppo int ldcdbg = 0x0; 2081ae08745Sheppo int64_t ldcdbgchan = DBG_ALL_LDCS; 20983d3bc6fSnarayan uint64_t ldc_inject_err_flag = 0; 2101ae08745Sheppo 2111ae08745Sheppo static void 2121ae08745Sheppo ldcdebug(int64_t id, const char *fmt, ...) 2131ae08745Sheppo { 2141ae08745Sheppo char buf[512]; 2151ae08745Sheppo va_list ap; 2161ae08745Sheppo 2171ae08745Sheppo /* 2181ae08745Sheppo * Do not return if, 2191ae08745Sheppo * caller wants to print it anyway - (id == DBG_ALL_LDCS) 2201ae08745Sheppo * debug channel is set to all LDCs - (ldcdbgchan == DBG_ALL_LDCS) 2211ae08745Sheppo * debug channel = caller specified channel 2221ae08745Sheppo */ 2231ae08745Sheppo if ((id != DBG_ALL_LDCS) && 2241ae08745Sheppo (ldcdbgchan != DBG_ALL_LDCS) && 2251ae08745Sheppo (ldcdbgchan != id)) { 2261ae08745Sheppo return; 2271ae08745Sheppo } 2281ae08745Sheppo 2291ae08745Sheppo va_start(ap, fmt); 2301ae08745Sheppo (void) vsprintf(buf, fmt, ap); 2311ae08745Sheppo va_end(ap); 2321ae08745Sheppo 2333af08d82Slm66018 cmn_err(CE_CONT, "?%s", buf); 2343af08d82Slm66018 } 2353af08d82Slm66018 23683d3bc6fSnarayan #define LDC_ERR_RESET 0x1 23783d3bc6fSnarayan #define LDC_ERR_PKTLOSS 0x2 23883d3bc6fSnarayan 2393af08d82Slm66018 static boolean_t 24083d3bc6fSnarayan ldc_inject_error(ldc_chan_t *ldcp, uint64_t error) 2413af08d82Slm66018 { 2423af08d82Slm66018 if ((ldcdbgchan != DBG_ALL_LDCS) && (ldcdbgchan != ldcp->id)) 2433af08d82Slm66018 return (B_FALSE); 2443af08d82Slm66018 24583d3bc6fSnarayan if ((ldc_inject_err_flag & error) == 0) 2463af08d82Slm66018 return (B_FALSE); 2473af08d82Slm66018 2483af08d82Slm66018 /* clear the injection state */ 24983d3bc6fSnarayan ldc_inject_err_flag &= ~error; 2503af08d82Slm66018 2513af08d82Slm66018 return (B_TRUE); 2521ae08745Sheppo } 2531ae08745Sheppo 2541ae08745Sheppo #define D1 \ 2551ae08745Sheppo if (ldcdbg & 0x01) \ 2561ae08745Sheppo ldcdebug 2571ae08745Sheppo 2581ae08745Sheppo #define D2 \ 2591ae08745Sheppo if (ldcdbg & 0x02) \ 2601ae08745Sheppo ldcdebug 2611ae08745Sheppo 2621ae08745Sheppo #define DWARN \ 2631ae08745Sheppo if (ldcdbg & 0x04) \ 2641ae08745Sheppo ldcdebug 2651ae08745Sheppo 2661ae08745Sheppo #define DUMP_PAYLOAD(id, addr) \ 2671ae08745Sheppo { \ 2681ae08745Sheppo char buf[65*3]; \ 2691ae08745Sheppo int i; \ 2701ae08745Sheppo uint8_t *src = (uint8_t *)addr; \ 2711ae08745Sheppo for (i = 0; i < 64; i++, src++) \ 2721ae08745Sheppo (void) sprintf(&buf[i * 3], "|%02x", *src); \ 2731ae08745Sheppo (void) sprintf(&buf[i * 3], "|\n"); \ 2741ae08745Sheppo D2((id), "payload: %s", buf); \ 2751ae08745Sheppo } 2761ae08745Sheppo 2771ae08745Sheppo #define DUMP_LDC_PKT(c, s, addr) \ 2781ae08745Sheppo { \ 2791ae08745Sheppo ldc_msg_t *msg = (ldc_msg_t *)(addr); \ 2801ae08745Sheppo uint32_t mid = ((c)->mode != LDC_MODE_RAW) ? msg->seqid : 0; \ 2811ae08745Sheppo if (msg->type == LDC_DATA) { \ 2821ae08745Sheppo D2((c)->id, "%s: msg%d (/%x/%x/%x/,env[%c%c,sz=%d])", \ 2831ae08745Sheppo (s), mid, msg->type, msg->stype, msg->ctrl, \ 2841ae08745Sheppo (msg->env & LDC_FRAG_START) ? 'B' : ' ', \ 2851ae08745Sheppo (msg->env & LDC_FRAG_STOP) ? 'E' : ' ', \ 2861ae08745Sheppo (msg->env & LDC_LEN_MASK)); \ 2871ae08745Sheppo } else { \ 2881ae08745Sheppo D2((c)->id, "%s: msg%d (/%x/%x/%x/,env=%x)", (s), \ 2891ae08745Sheppo mid, msg->type, msg->stype, msg->ctrl, msg->env); \ 2901ae08745Sheppo } \ 2911ae08745Sheppo } 2921ae08745Sheppo 29383d3bc6fSnarayan #define LDC_INJECT_RESET(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_RESET) 29483d3bc6fSnarayan #define LDC_INJECT_PKTLOSS(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_PKTLOSS) 2953af08d82Slm66018 2961ae08745Sheppo #else 2971ae08745Sheppo 2981ae08745Sheppo #define DBG_ALL_LDCS -1 2991ae08745Sheppo 3001ae08745Sheppo #define D1 3011ae08745Sheppo #define D2 3021ae08745Sheppo #define DWARN 3031ae08745Sheppo 3041ae08745Sheppo #define DUMP_PAYLOAD(id, addr) 3051ae08745Sheppo #define DUMP_LDC_PKT(c, s, addr) 3061ae08745Sheppo 3073af08d82Slm66018 #define LDC_INJECT_RESET(_ldcp) (B_FALSE) 30883d3bc6fSnarayan #define LDC_INJECT_PKTLOSS(_ldcp) (B_FALSE) 3093af08d82Slm66018 3101ae08745Sheppo #endif 3111ae08745Sheppo 3121ae08745Sheppo #define ZERO_PKT(p) \ 3131ae08745Sheppo bzero((p), sizeof (ldc_msg_t)); 3141ae08745Sheppo 3151ae08745Sheppo #define IDX2COOKIE(idx, pg_szc, pg_shift) \ 3161ae08745Sheppo (((pg_szc) << LDC_COOKIE_PGSZC_SHIFT) | ((idx) << (pg_shift))) 3171ae08745Sheppo 3181ae08745Sheppo int 3191ae08745Sheppo _init(void) 3201ae08745Sheppo { 3211ae08745Sheppo int status; 3221ae08745Sheppo 3231ae08745Sheppo status = hsvc_register(&ldc_hsvc, &ldc_sup_minor); 3241ae08745Sheppo if (status != 0) { 325d66f8315Sjb145095 cmn_err(CE_NOTE, "!%s: cannot negotiate hypervisor LDC services" 3261ae08745Sheppo " group: 0x%lx major: %ld minor: %ld errno: %d", 3271ae08745Sheppo ldc_hsvc.hsvc_modname, ldc_hsvc.hsvc_group, 3281ae08745Sheppo ldc_hsvc.hsvc_major, ldc_hsvc.hsvc_minor, status); 3291ae08745Sheppo return (-1); 3301ae08745Sheppo } 3311ae08745Sheppo 3321ae08745Sheppo /* allocate soft state structure */ 3331ae08745Sheppo ldcssp = kmem_zalloc(sizeof (ldc_soft_state_t), KM_SLEEP); 3341ae08745Sheppo 3351ae08745Sheppo /* Link the module into the system */ 3361ae08745Sheppo status = mod_install(&ml); 3371ae08745Sheppo if (status != 0) { 3381ae08745Sheppo kmem_free(ldcssp, sizeof (ldc_soft_state_t)); 3391ae08745Sheppo return (status); 3401ae08745Sheppo } 3411ae08745Sheppo 3421ae08745Sheppo /* Initialize the LDC state structure */ 3431ae08745Sheppo mutex_init(&ldcssp->lock, NULL, MUTEX_DRIVER, NULL); 3441ae08745Sheppo 3451ae08745Sheppo mutex_enter(&ldcssp->lock); 3461ae08745Sheppo 3474bac2208Snarayan /* Create a cache for memory handles */ 3484bac2208Snarayan ldcssp->memhdl_cache = kmem_cache_create("ldc_memhdl_cache", 3494bac2208Snarayan sizeof (ldc_mhdl_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 3504bac2208Snarayan if (ldcssp->memhdl_cache == NULL) { 3514bac2208Snarayan DWARN(DBG_ALL_LDCS, "_init: ldc_memhdl cache create failed\n"); 3524bac2208Snarayan mutex_exit(&ldcssp->lock); 3534bac2208Snarayan return (-1); 3544bac2208Snarayan } 3554bac2208Snarayan 3564bac2208Snarayan /* Create cache for memory segment structures */ 3574bac2208Snarayan ldcssp->memseg_cache = kmem_cache_create("ldc_memseg_cache", 3584bac2208Snarayan sizeof (ldc_memseg_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 3594bac2208Snarayan if (ldcssp->memseg_cache == NULL) { 3604bac2208Snarayan DWARN(DBG_ALL_LDCS, "_init: ldc_memseg cache create failed\n"); 3614bac2208Snarayan mutex_exit(&ldcssp->lock); 3624bac2208Snarayan return (-1); 3634bac2208Snarayan } 3644bac2208Snarayan 3654bac2208Snarayan 3661ae08745Sheppo ldcssp->channel_count = 0; 3671ae08745Sheppo ldcssp->channels_open = 0; 3681ae08745Sheppo ldcssp->chan_list = NULL; 3691ae08745Sheppo ldcssp->dring_list = NULL; 3701ae08745Sheppo 3711ae08745Sheppo mutex_exit(&ldcssp->lock); 3721ae08745Sheppo 3731ae08745Sheppo return (0); 3741ae08745Sheppo } 3751ae08745Sheppo 3761ae08745Sheppo int 3771ae08745Sheppo _info(struct modinfo *modinfop) 3781ae08745Sheppo { 3791ae08745Sheppo /* Report status of the dynamically loadable driver module */ 3801ae08745Sheppo return (mod_info(&ml, modinfop)); 3811ae08745Sheppo } 3821ae08745Sheppo 3831ae08745Sheppo int 3841ae08745Sheppo _fini(void) 3851ae08745Sheppo { 3861ae08745Sheppo int rv, status; 38722f747efSnarayan ldc_chan_t *tmp_ldcp, *ldcp; 38822f747efSnarayan ldc_dring_t *tmp_dringp, *dringp; 3891ae08745Sheppo ldc_mem_info_t minfo; 3901ae08745Sheppo 3911ae08745Sheppo /* Unlink the driver module from the system */ 3921ae08745Sheppo status = mod_remove(&ml); 3931ae08745Sheppo if (status) { 3941ae08745Sheppo DWARN(DBG_ALL_LDCS, "_fini: mod_remove failed\n"); 3951ae08745Sheppo return (EIO); 3961ae08745Sheppo } 3971ae08745Sheppo 3981ae08745Sheppo /* Free descriptor rings */ 3991ae08745Sheppo dringp = ldcssp->dring_list; 4001ae08745Sheppo while (dringp != NULL) { 40122f747efSnarayan tmp_dringp = dringp->next; 4021ae08745Sheppo 4031ae08745Sheppo rv = ldc_mem_dring_info((ldc_dring_handle_t)dringp, &minfo); 4041ae08745Sheppo if (rv == 0 && minfo.status != LDC_UNBOUND) { 4051ae08745Sheppo if (minfo.status == LDC_BOUND) { 4061ae08745Sheppo (void) ldc_mem_dring_unbind( 4071ae08745Sheppo (ldc_dring_handle_t)dringp); 4081ae08745Sheppo } 4091ae08745Sheppo if (minfo.status == LDC_MAPPED) { 4101ae08745Sheppo (void) ldc_mem_dring_unmap( 4111ae08745Sheppo (ldc_dring_handle_t)dringp); 4121ae08745Sheppo } 4131ae08745Sheppo } 4141ae08745Sheppo 4151ae08745Sheppo (void) ldc_mem_dring_destroy((ldc_dring_handle_t)dringp); 41622f747efSnarayan dringp = tmp_dringp; 4171ae08745Sheppo } 4181ae08745Sheppo ldcssp->dring_list = NULL; 4191ae08745Sheppo 42022f747efSnarayan /* close and finalize channels */ 42122f747efSnarayan ldcp = ldcssp->chan_list; 42222f747efSnarayan while (ldcp != NULL) { 42322f747efSnarayan tmp_ldcp = ldcp->next; 42422f747efSnarayan 42522f747efSnarayan (void) ldc_close((ldc_handle_t)ldcp); 42622f747efSnarayan (void) ldc_fini((ldc_handle_t)ldcp); 42722f747efSnarayan 42822f747efSnarayan ldcp = tmp_ldcp; 42922f747efSnarayan } 43022f747efSnarayan ldcssp->chan_list = NULL; 43122f747efSnarayan 4324bac2208Snarayan /* Destroy kmem caches */ 4334bac2208Snarayan kmem_cache_destroy(ldcssp->memhdl_cache); 4344bac2208Snarayan kmem_cache_destroy(ldcssp->memseg_cache); 4354bac2208Snarayan 4361ae08745Sheppo /* 4371ae08745Sheppo * We have successfully "removed" the driver. 4381ae08745Sheppo * Destroying soft states 4391ae08745Sheppo */ 4401ae08745Sheppo mutex_destroy(&ldcssp->lock); 4411ae08745Sheppo kmem_free(ldcssp, sizeof (ldc_soft_state_t)); 4421ae08745Sheppo 4431ae08745Sheppo (void) hsvc_unregister(&ldc_hsvc); 4441ae08745Sheppo 4451ae08745Sheppo return (status); 4461ae08745Sheppo } 4471ae08745Sheppo 4481ae08745Sheppo /* -------------------------------------------------------------------------- */ 4491ae08745Sheppo 4501ae08745Sheppo /* 451e1ebb9ecSlm66018 * LDC Link Layer Internal Functions 4521ae08745Sheppo */ 4531ae08745Sheppo 4541ae08745Sheppo /* 4551ae08745Sheppo * Translate HV Errors to sun4v error codes 4561ae08745Sheppo */ 4571ae08745Sheppo static int 4581ae08745Sheppo i_ldc_h2v_error(int h_error) 4591ae08745Sheppo { 4601ae08745Sheppo switch (h_error) { 4611ae08745Sheppo 4621ae08745Sheppo case H_EOK: 4631ae08745Sheppo return (0); 4641ae08745Sheppo 4651ae08745Sheppo case H_ENORADDR: 4661ae08745Sheppo return (EFAULT); 4671ae08745Sheppo 4681ae08745Sheppo case H_EBADPGSZ: 4691ae08745Sheppo case H_EINVAL: 4701ae08745Sheppo return (EINVAL); 4711ae08745Sheppo 4721ae08745Sheppo case H_EWOULDBLOCK: 4731ae08745Sheppo return (EWOULDBLOCK); 4741ae08745Sheppo 4751ae08745Sheppo case H_ENOACCESS: 4761ae08745Sheppo case H_ENOMAP: 4771ae08745Sheppo return (EACCES); 4781ae08745Sheppo 4791ae08745Sheppo case H_EIO: 4801ae08745Sheppo case H_ECPUERROR: 4811ae08745Sheppo return (EIO); 4821ae08745Sheppo 4831ae08745Sheppo case H_ENOTSUPPORTED: 4841ae08745Sheppo return (ENOTSUP); 4851ae08745Sheppo 4861ae08745Sheppo case H_ETOOMANY: 4871ae08745Sheppo return (ENOSPC); 4881ae08745Sheppo 4891ae08745Sheppo case H_ECHANNEL: 4901ae08745Sheppo return (ECHRNG); 4911ae08745Sheppo default: 4921ae08745Sheppo break; 4931ae08745Sheppo } 4941ae08745Sheppo 4951ae08745Sheppo return (EIO); 4961ae08745Sheppo } 4971ae08745Sheppo 4981ae08745Sheppo /* 4991ae08745Sheppo * Reconfigure the transmit queue 5001ae08745Sheppo */ 5011ae08745Sheppo static int 5021ae08745Sheppo i_ldc_txq_reconf(ldc_chan_t *ldcp) 5031ae08745Sheppo { 5041ae08745Sheppo int rv; 5051ae08745Sheppo 5061ae08745Sheppo ASSERT(MUTEX_HELD(&ldcp->lock)); 507d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 508d10e4ef2Snarayan 5091ae08745Sheppo rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries); 5101ae08745Sheppo if (rv) { 5111ae08745Sheppo cmn_err(CE_WARN, 5123af08d82Slm66018 "i_ldc_txq_reconf: (0x%lx) cannot set qconf", ldcp->id); 5131ae08745Sheppo return (EIO); 5141ae08745Sheppo } 5151ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, &(ldcp->tx_head), 5161ae08745Sheppo &(ldcp->tx_tail), &(ldcp->link_state)); 5171ae08745Sheppo if (rv) { 5181ae08745Sheppo cmn_err(CE_WARN, 5193af08d82Slm66018 "i_ldc_txq_reconf: (0x%lx) cannot get qptrs", ldcp->id); 5201ae08745Sheppo return (EIO); 5211ae08745Sheppo } 5223af08d82Slm66018 D1(ldcp->id, "i_ldc_txq_reconf: (0x%llx) h=0x%llx,t=0x%llx," 5231ae08745Sheppo "s=0x%llx\n", ldcp->id, ldcp->tx_head, ldcp->tx_tail, 5241ae08745Sheppo ldcp->link_state); 5251ae08745Sheppo 5261ae08745Sheppo return (0); 5271ae08745Sheppo } 5281ae08745Sheppo 5291ae08745Sheppo /* 5301ae08745Sheppo * Reconfigure the receive queue 5311ae08745Sheppo */ 5321ae08745Sheppo static int 5333af08d82Slm66018 i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset) 5341ae08745Sheppo { 5351ae08745Sheppo int rv; 5361ae08745Sheppo uint64_t rx_head, rx_tail; 5371ae08745Sheppo 5381ae08745Sheppo ASSERT(MUTEX_HELD(&ldcp->lock)); 5391ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 5401ae08745Sheppo &(ldcp->link_state)); 5411ae08745Sheppo if (rv) { 5421ae08745Sheppo cmn_err(CE_WARN, 5433af08d82Slm66018 "i_ldc_rxq_reconf: (0x%lx) cannot get state", 5441ae08745Sheppo ldcp->id); 5451ae08745Sheppo return (EIO); 5461ae08745Sheppo } 5471ae08745Sheppo 5483af08d82Slm66018 if (force_reset || (ldcp->tstate & ~TS_IN_RESET) == TS_UP) { 5491ae08745Sheppo rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra, 5501ae08745Sheppo ldcp->rx_q_entries); 5511ae08745Sheppo if (rv) { 5521ae08745Sheppo cmn_err(CE_WARN, 5533af08d82Slm66018 "i_ldc_rxq_reconf: (0x%lx) cannot set qconf", 5541ae08745Sheppo ldcp->id); 5551ae08745Sheppo return (EIO); 5561ae08745Sheppo } 5573af08d82Slm66018 D1(ldcp->id, "i_ldc_rxq_reconf: (0x%llx) completed q reconf", 5581ae08745Sheppo ldcp->id); 5591ae08745Sheppo } 5601ae08745Sheppo 5611ae08745Sheppo return (0); 5621ae08745Sheppo } 5631ae08745Sheppo 564a8ea4edeSnarayan 565a8ea4edeSnarayan /* 566a8ea4edeSnarayan * Drain the contents of the receive queue 567a8ea4edeSnarayan */ 568a8ea4edeSnarayan static int 569a8ea4edeSnarayan i_ldc_rxq_drain(ldc_chan_t *ldcp) 570a8ea4edeSnarayan { 571a8ea4edeSnarayan int rv; 572a8ea4edeSnarayan uint64_t rx_head, rx_tail; 573a8ea4edeSnarayan 574a8ea4edeSnarayan ASSERT(MUTEX_HELD(&ldcp->lock)); 575a8ea4edeSnarayan rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 576a8ea4edeSnarayan &(ldcp->link_state)); 577a8ea4edeSnarayan if (rv) { 578a8ea4edeSnarayan cmn_err(CE_WARN, "i_ldc_rxq_drain: (0x%lx) cannot get state", 579a8ea4edeSnarayan ldcp->id); 580a8ea4edeSnarayan return (EIO); 581a8ea4edeSnarayan } 582a8ea4edeSnarayan 583a8ea4edeSnarayan /* flush contents by setting the head = tail */ 584a8ea4edeSnarayan return (i_ldc_set_rx_head(ldcp, rx_tail)); 585a8ea4edeSnarayan } 586a8ea4edeSnarayan 587a8ea4edeSnarayan 5881ae08745Sheppo /* 5891ae08745Sheppo * Reset LDC state structure and its contents 5901ae08745Sheppo */ 5911ae08745Sheppo static void 5921ae08745Sheppo i_ldc_reset_state(ldc_chan_t *ldcp) 5931ae08745Sheppo { 5941ae08745Sheppo ASSERT(MUTEX_HELD(&ldcp->lock)); 5951ae08745Sheppo ldcp->last_msg_snt = LDC_INIT_SEQID; 5961ae08745Sheppo ldcp->last_ack_rcd = 0; 5971ae08745Sheppo ldcp->last_msg_rcd = 0; 5981ae08745Sheppo ldcp->tx_ackd_head = ldcp->tx_head; 5991ae08745Sheppo ldcp->next_vidx = 0; 6001ae08745Sheppo ldcp->hstate = 0; 6011ae08745Sheppo ldcp->tstate = TS_OPEN; 6021ae08745Sheppo ldcp->status = LDC_OPEN; 6031ae08745Sheppo 6041ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_UP || 6051ae08745Sheppo ldcp->link_state == LDC_CHANNEL_RESET) { 6061ae08745Sheppo 6071ae08745Sheppo if (ldcp->mode == LDC_MODE_RAW) { 6081ae08745Sheppo ldcp->status = LDC_UP; 6091ae08745Sheppo ldcp->tstate = TS_UP; 6101ae08745Sheppo } else { 6111ae08745Sheppo ldcp->status = LDC_READY; 6121ae08745Sheppo ldcp->tstate |= TS_LINK_READY; 6131ae08745Sheppo } 6141ae08745Sheppo } 6151ae08745Sheppo } 6161ae08745Sheppo 6171ae08745Sheppo /* 6181ae08745Sheppo * Reset a LDC channel 6191ae08745Sheppo */ 6201ae08745Sheppo static void 6213af08d82Slm66018 i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset) 6221ae08745Sheppo { 62383d3bc6fSnarayan DWARN(ldcp->id, "i_ldc_reset: (0x%llx) channel reset\n", ldcp->id); 6241ae08745Sheppo 625d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->lock)); 626d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 627d10e4ef2Snarayan 6283af08d82Slm66018 /* reconfig Tx and Rx queues */ 6291ae08745Sheppo (void) i_ldc_txq_reconf(ldcp); 6303af08d82Slm66018 (void) i_ldc_rxq_reconf(ldcp, force_reset); 6313af08d82Slm66018 6323af08d82Slm66018 /* Clear Tx and Rx interrupts */ 6333af08d82Slm66018 (void) i_ldc_clear_intr(ldcp, CNEX_TX_INTR); 6343af08d82Slm66018 (void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 6353af08d82Slm66018 6363af08d82Slm66018 /* Reset channel state */ 6371ae08745Sheppo i_ldc_reset_state(ldcp); 6383af08d82Slm66018 6393af08d82Slm66018 /* Mark channel in reset */ 6403af08d82Slm66018 ldcp->tstate |= TS_IN_RESET; 6411ae08745Sheppo } 6421ae08745Sheppo 6434bac2208Snarayan 6441ae08745Sheppo /* 6451ae08745Sheppo * Clear pending interrupts 6461ae08745Sheppo */ 6471ae08745Sheppo static void 6481ae08745Sheppo i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype) 6491ae08745Sheppo { 6501ae08745Sheppo ldc_cnex_t *cinfo = &ldcssp->cinfo; 6511ae08745Sheppo 6521ae08745Sheppo ASSERT(MUTEX_HELD(&ldcp->lock)); 6533af08d82Slm66018 ASSERT(cinfo->dip != NULL); 6544bac2208Snarayan 6553af08d82Slm66018 switch (itype) { 6563af08d82Slm66018 case CNEX_TX_INTR: 6574bac2208Snarayan /* check Tx interrupt */ 6583af08d82Slm66018 if (ldcp->tx_intr_state) 6593af08d82Slm66018 ldcp->tx_intr_state = LDC_INTR_NONE; 6604bac2208Snarayan else 6614bac2208Snarayan return; 6623af08d82Slm66018 break; 6633af08d82Slm66018 6643af08d82Slm66018 case CNEX_RX_INTR: 6654bac2208Snarayan /* check Rx interrupt */ 6663af08d82Slm66018 if (ldcp->rx_intr_state) 6673af08d82Slm66018 ldcp->rx_intr_state = LDC_INTR_NONE; 6684bac2208Snarayan else 6694bac2208Snarayan return; 6703af08d82Slm66018 break; 6714bac2208Snarayan } 6724bac2208Snarayan 6731ae08745Sheppo (void) cinfo->clr_intr(cinfo->dip, ldcp->id, itype); 6744bac2208Snarayan D2(ldcp->id, 6754bac2208Snarayan "i_ldc_clear_intr: (0x%llx) cleared 0x%x intr\n", 6764bac2208Snarayan ldcp->id, itype); 6771ae08745Sheppo } 6781ae08745Sheppo 6791ae08745Sheppo /* 6801ae08745Sheppo * Set the receive queue head 6810a55fbb7Slm66018 * Resets connection and returns an error if it fails. 6821ae08745Sheppo */ 6831ae08745Sheppo static int 6841ae08745Sheppo i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head) 6851ae08745Sheppo { 6861ae08745Sheppo int rv; 6870a55fbb7Slm66018 int retries; 6881ae08745Sheppo 6891ae08745Sheppo ASSERT(MUTEX_HELD(&ldcp->lock)); 6900a55fbb7Slm66018 for (retries = 0; retries < ldc_max_retries; retries++) { 6910a55fbb7Slm66018 6920a55fbb7Slm66018 if ((rv = hv_ldc_rx_set_qhead(ldcp->id, head)) == 0) 6930a55fbb7Slm66018 return (0); 6940a55fbb7Slm66018 6950a55fbb7Slm66018 if (rv != H_EWOULDBLOCK) 6960a55fbb7Slm66018 break; 6970a55fbb7Slm66018 6980a55fbb7Slm66018 /* wait for ldc_delay usecs */ 6990a55fbb7Slm66018 drv_usecwait(ldc_delay); 7001ae08745Sheppo } 7011ae08745Sheppo 7020a55fbb7Slm66018 cmn_err(CE_WARN, "ldc_rx_set_qhead: (0x%lx) cannot set qhead 0x%lx", 7030a55fbb7Slm66018 ldcp->id, head); 704d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 7053af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 706d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 7070a55fbb7Slm66018 7080a55fbb7Slm66018 return (ECONNRESET); 7091ae08745Sheppo } 7101ae08745Sheppo 71122f747efSnarayan /* 71222f747efSnarayan * Returns the tx_head to be used for transfer 71322f747efSnarayan */ 71422f747efSnarayan static void 71522f747efSnarayan i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head) 71622f747efSnarayan { 71722f747efSnarayan ldc_msg_t *pkt; 71822f747efSnarayan 71922f747efSnarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 72022f747efSnarayan 72122f747efSnarayan /* get current Tx head */ 72222f747efSnarayan *head = ldcp->tx_head; 72322f747efSnarayan 72422f747efSnarayan /* 72522f747efSnarayan * Reliable mode will use the ACKd head instead of the regular tx_head. 72622f747efSnarayan * Also in Reliable mode, advance ackd_head for all non DATA/INFO pkts, 72722f747efSnarayan * up to the current location of tx_head. This needs to be done 72822f747efSnarayan * as the peer will only ACK DATA/INFO pkts. 72922f747efSnarayan */ 73022f747efSnarayan if (ldcp->mode == LDC_MODE_RELIABLE || ldcp->mode == LDC_MODE_STREAM) { 73122f747efSnarayan while (ldcp->tx_ackd_head != ldcp->tx_head) { 73222f747efSnarayan pkt = (ldc_msg_t *)(ldcp->tx_q_va + ldcp->tx_ackd_head); 73322f747efSnarayan if ((pkt->type & LDC_DATA) && (pkt->stype & LDC_INFO)) { 73422f747efSnarayan break; 73522f747efSnarayan } 73622f747efSnarayan /* advance ACKd head */ 73722f747efSnarayan ldcp->tx_ackd_head = 73822f747efSnarayan (ldcp->tx_ackd_head + LDC_PACKET_SIZE) % 73922f747efSnarayan (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 74022f747efSnarayan } 74122f747efSnarayan *head = ldcp->tx_ackd_head; 74222f747efSnarayan } 74322f747efSnarayan } 7441ae08745Sheppo 7451ae08745Sheppo /* 7461ae08745Sheppo * Returns the tx_tail to be used for transfer 7471ae08745Sheppo * Re-reads the TX queue ptrs if and only if the 7481ae08745Sheppo * the cached head and tail are equal (queue is full) 7491ae08745Sheppo */ 7501ae08745Sheppo static int 7511ae08745Sheppo i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail) 7521ae08745Sheppo { 7531ae08745Sheppo int rv; 7541ae08745Sheppo uint64_t current_head, new_tail; 7551ae08745Sheppo 756d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 7571ae08745Sheppo /* Read the head and tail ptrs from HV */ 7581ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 7591ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 7601ae08745Sheppo if (rv) { 7611ae08745Sheppo cmn_err(CE_WARN, 7621ae08745Sheppo "i_ldc_get_tx_tail: (0x%lx) cannot read qptrs\n", 7631ae08745Sheppo ldcp->id); 7641ae08745Sheppo return (EIO); 7651ae08745Sheppo } 7661ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_DOWN) { 767cb112a14Slm66018 D1(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) channel not ready\n", 7681ae08745Sheppo ldcp->id); 7691ae08745Sheppo return (ECONNRESET); 7701ae08745Sheppo } 7711ae08745Sheppo 77222f747efSnarayan i_ldc_get_tx_head(ldcp, ¤t_head); 7731ae08745Sheppo 7741ae08745Sheppo /* increment the tail */ 7751ae08745Sheppo new_tail = (ldcp->tx_tail + LDC_PACKET_SIZE) % 7761ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 7771ae08745Sheppo 7781ae08745Sheppo if (new_tail == current_head) { 7791ae08745Sheppo DWARN(ldcp->id, 7801ae08745Sheppo "i_ldc_get_tx_tail: (0x%llx) TX queue is full\n", 7811ae08745Sheppo ldcp->id); 7821ae08745Sheppo return (EWOULDBLOCK); 7831ae08745Sheppo } 7841ae08745Sheppo 7851ae08745Sheppo D2(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) head=0x%llx, tail=0x%llx\n", 7861ae08745Sheppo ldcp->id, ldcp->tx_head, ldcp->tx_tail); 7871ae08745Sheppo 7881ae08745Sheppo *tail = ldcp->tx_tail; 7891ae08745Sheppo return (0); 7901ae08745Sheppo } 7911ae08745Sheppo 7921ae08745Sheppo /* 7931ae08745Sheppo * Set the tail pointer. If HV returns EWOULDBLOCK, it will back off 7940a55fbb7Slm66018 * and retry ldc_max_retries times before returning an error. 7951ae08745Sheppo * Returns 0, EWOULDBLOCK or EIO 7961ae08745Sheppo */ 7971ae08745Sheppo static int 7981ae08745Sheppo i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail) 7991ae08745Sheppo { 8001ae08745Sheppo int rv, retval = EWOULDBLOCK; 8010a55fbb7Slm66018 int retries; 8021ae08745Sheppo 803d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 8040a55fbb7Slm66018 for (retries = 0; retries < ldc_max_retries; retries++) { 8051ae08745Sheppo 8061ae08745Sheppo if ((rv = hv_ldc_tx_set_qtail(ldcp->id, tail)) == 0) { 8071ae08745Sheppo retval = 0; 8081ae08745Sheppo break; 8091ae08745Sheppo } 8101ae08745Sheppo if (rv != H_EWOULDBLOCK) { 8111ae08745Sheppo DWARN(ldcp->id, "i_ldc_set_tx_tail: (0x%llx) set " 8121ae08745Sheppo "qtail=0x%llx failed, rv=%d\n", ldcp->id, tail, rv); 8131ae08745Sheppo retval = EIO; 8141ae08745Sheppo break; 8151ae08745Sheppo } 8161ae08745Sheppo 8170a55fbb7Slm66018 /* wait for ldc_delay usecs */ 8180a55fbb7Slm66018 drv_usecwait(ldc_delay); 8191ae08745Sheppo } 8201ae08745Sheppo return (retval); 8211ae08745Sheppo } 8221ae08745Sheppo 8231ae08745Sheppo /* 8241ae08745Sheppo * Send a LDC message 8251ae08745Sheppo */ 8261ae08745Sheppo static int 8271ae08745Sheppo i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype, 8281ae08745Sheppo uint8_t ctrlmsg) 8291ae08745Sheppo { 8301ae08745Sheppo int rv; 8311ae08745Sheppo ldc_msg_t *pkt; 8321ae08745Sheppo uint64_t tx_tail; 83322f747efSnarayan uint32_t curr_seqid; 8341ae08745Sheppo 835d10e4ef2Snarayan /* Obtain Tx lock */ 836d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 837d10e4ef2Snarayan 83822f747efSnarayan curr_seqid = ldcp->last_msg_snt; 83922f747efSnarayan 8401ae08745Sheppo /* get the current tail for the message */ 8411ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 8421ae08745Sheppo if (rv) { 8431ae08745Sheppo DWARN(ldcp->id, 8441ae08745Sheppo "i_ldc_send_pkt: (0x%llx) error sending pkt, " 8451ae08745Sheppo "type=0x%x,subtype=0x%x,ctrl=0x%x\n", 8461ae08745Sheppo ldcp->id, pkttype, subtype, ctrlmsg); 847d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 8481ae08745Sheppo return (rv); 8491ae08745Sheppo } 8501ae08745Sheppo 8511ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 8521ae08745Sheppo ZERO_PKT(pkt); 8531ae08745Sheppo 8541ae08745Sheppo /* Initialize the packet */ 8551ae08745Sheppo pkt->type = pkttype; 8561ae08745Sheppo pkt->stype = subtype; 8571ae08745Sheppo pkt->ctrl = ctrlmsg; 8581ae08745Sheppo 8591ae08745Sheppo /* Store ackid/seqid iff it is RELIABLE mode & not a RTS/RTR message */ 8601ae08745Sheppo if (((ctrlmsg & LDC_CTRL_MASK) != LDC_RTS) && 8611ae08745Sheppo ((ctrlmsg & LDC_CTRL_MASK) != LDC_RTR)) { 8621ae08745Sheppo curr_seqid++; 8631ae08745Sheppo if (ldcp->mode != LDC_MODE_RAW) { 8641ae08745Sheppo pkt->seqid = curr_seqid; 8651ae08745Sheppo pkt->ackid = ldcp->last_msg_rcd; 8661ae08745Sheppo } 8671ae08745Sheppo } 8681ae08745Sheppo DUMP_LDC_PKT(ldcp, "i_ldc_send_pkt", (uint64_t)pkt); 8691ae08745Sheppo 8701ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 8711ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 8721ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 8731ae08745Sheppo 8741ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 8751ae08745Sheppo if (rv) { 8761ae08745Sheppo DWARN(ldcp->id, 8771ae08745Sheppo "i_ldc_send_pkt:(0x%llx) error sending pkt, " 8781ae08745Sheppo "type=0x%x,stype=0x%x,ctrl=0x%x\n", 8791ae08745Sheppo ldcp->id, pkttype, subtype, ctrlmsg); 880d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 8811ae08745Sheppo return (EIO); 8821ae08745Sheppo } 8831ae08745Sheppo 8841ae08745Sheppo ldcp->last_msg_snt = curr_seqid; 8851ae08745Sheppo ldcp->tx_tail = tx_tail; 8861ae08745Sheppo 887d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 8881ae08745Sheppo return (0); 8891ae08745Sheppo } 8901ae08745Sheppo 8911ae08745Sheppo /* 8921ae08745Sheppo * Checks if packet was received in right order 893e1ebb9ecSlm66018 * in the case of a reliable link. 8941ae08745Sheppo * Returns 0 if in order, else EIO 8951ae08745Sheppo */ 8961ae08745Sheppo static int 8971ae08745Sheppo i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *msg) 8981ae08745Sheppo { 8991ae08745Sheppo /* No seqid checking for RAW mode */ 9001ae08745Sheppo if (ldcp->mode == LDC_MODE_RAW) 9011ae08745Sheppo return (0); 9021ae08745Sheppo 9031ae08745Sheppo /* No seqid checking for version, RTS, RTR message */ 9041ae08745Sheppo if (msg->ctrl == LDC_VER || 9051ae08745Sheppo msg->ctrl == LDC_RTS || 9061ae08745Sheppo msg->ctrl == LDC_RTR) 9071ae08745Sheppo return (0); 9081ae08745Sheppo 9091ae08745Sheppo /* Initial seqid to use is sent in RTS/RTR and saved in last_msg_rcd */ 9101ae08745Sheppo if (msg->seqid != (ldcp->last_msg_rcd + 1)) { 9111ae08745Sheppo DWARN(ldcp->id, 9121ae08745Sheppo "i_ldc_check_seqid: (0x%llx) out-of-order pkt, got 0x%x, " 9131ae08745Sheppo "expecting 0x%x\n", ldcp->id, msg->seqid, 9141ae08745Sheppo (ldcp->last_msg_rcd + 1)); 9151ae08745Sheppo return (EIO); 9161ae08745Sheppo } 9171ae08745Sheppo 91883d3bc6fSnarayan #ifdef DEBUG 91983d3bc6fSnarayan if (LDC_INJECT_PKTLOSS(ldcp)) { 92083d3bc6fSnarayan DWARN(ldcp->id, 92183d3bc6fSnarayan "i_ldc_check_seqid: (0x%llx) inject pkt loss\n", ldcp->id); 92283d3bc6fSnarayan return (EIO); 92383d3bc6fSnarayan } 92483d3bc6fSnarayan #endif 92583d3bc6fSnarayan 9261ae08745Sheppo return (0); 9271ae08745Sheppo } 9281ae08745Sheppo 9291ae08745Sheppo 9301ae08745Sheppo /* 9311ae08745Sheppo * Process an incoming version ctrl message 9321ae08745Sheppo */ 9331ae08745Sheppo static int 9341ae08745Sheppo i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) 9351ae08745Sheppo { 9361ae08745Sheppo int rv = 0, idx = ldcp->next_vidx; 9371ae08745Sheppo ldc_msg_t *pkt; 9381ae08745Sheppo uint64_t tx_tail; 9391ae08745Sheppo ldc_ver_t *rcvd_ver; 9401ae08745Sheppo 9411ae08745Sheppo /* get the received version */ 9421ae08745Sheppo rcvd_ver = (ldc_ver_t *)((uint64_t)msg + LDC_PAYLOAD_VER_OFF); 9431ae08745Sheppo 9441ae08745Sheppo D2(ldcp->id, "i_ldc_process_VER: (0x%llx) received VER v%u.%u\n", 9451ae08745Sheppo ldcp->id, rcvd_ver->major, rcvd_ver->minor); 9461ae08745Sheppo 947d10e4ef2Snarayan /* Obtain Tx lock */ 948d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 949d10e4ef2Snarayan 9501ae08745Sheppo switch (msg->stype) { 9511ae08745Sheppo case LDC_INFO: 9521ae08745Sheppo 9533af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) { 9543af08d82Slm66018 (void) i_ldc_txq_reconf(ldcp); 9553af08d82Slm66018 i_ldc_reset_state(ldcp); 9563af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 9573af08d82Slm66018 return (EAGAIN); 9583af08d82Slm66018 } 9593af08d82Slm66018 9601ae08745Sheppo /* get the current tail and pkt for the response */ 9611ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 9621ae08745Sheppo if (rv != 0) { 9631ae08745Sheppo DWARN(ldcp->id, 9641ae08745Sheppo "i_ldc_process_VER: (0x%llx) err sending " 9651ae08745Sheppo "version ACK/NACK\n", ldcp->id); 9663af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 967d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 9681ae08745Sheppo return (ECONNRESET); 9691ae08745Sheppo } 9701ae08745Sheppo 9711ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 9721ae08745Sheppo ZERO_PKT(pkt); 9731ae08745Sheppo 9741ae08745Sheppo /* initialize the packet */ 9751ae08745Sheppo pkt->type = LDC_CTRL; 9761ae08745Sheppo pkt->ctrl = LDC_VER; 9771ae08745Sheppo 9781ae08745Sheppo for (;;) { 9791ae08745Sheppo 9801ae08745Sheppo D1(ldcp->id, "i_ldc_process_VER: got %u.%u chk %u.%u\n", 9811ae08745Sheppo rcvd_ver->major, rcvd_ver->minor, 9821ae08745Sheppo ldc_versions[idx].major, ldc_versions[idx].minor); 9831ae08745Sheppo 9841ae08745Sheppo if (rcvd_ver->major == ldc_versions[idx].major) { 9851ae08745Sheppo /* major version match - ACK version */ 9861ae08745Sheppo pkt->stype = LDC_ACK; 9871ae08745Sheppo 9881ae08745Sheppo /* 9891ae08745Sheppo * lower minor version to the one this endpt 9901ae08745Sheppo * supports, if necessary 9911ae08745Sheppo */ 9921ae08745Sheppo if (rcvd_ver->minor > ldc_versions[idx].minor) 9931ae08745Sheppo rcvd_ver->minor = 9941ae08745Sheppo ldc_versions[idx].minor; 9951ae08745Sheppo bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver)); 9961ae08745Sheppo 9971ae08745Sheppo break; 9981ae08745Sheppo } 9991ae08745Sheppo 10001ae08745Sheppo if (rcvd_ver->major > ldc_versions[idx].major) { 10011ae08745Sheppo 10021ae08745Sheppo D1(ldcp->id, "i_ldc_process_VER: using next" 10031ae08745Sheppo " lower idx=%d, v%u.%u\n", idx, 10041ae08745Sheppo ldc_versions[idx].major, 10051ae08745Sheppo ldc_versions[idx].minor); 10061ae08745Sheppo 10071ae08745Sheppo /* nack with next lower version */ 10081ae08745Sheppo pkt->stype = LDC_NACK; 10091ae08745Sheppo bcopy(&ldc_versions[idx], pkt->udata, 10101ae08745Sheppo sizeof (ldc_versions[idx])); 10111ae08745Sheppo ldcp->next_vidx = idx; 10121ae08745Sheppo break; 10131ae08745Sheppo } 10141ae08745Sheppo 10151ae08745Sheppo /* next major version */ 10161ae08745Sheppo idx++; 10171ae08745Sheppo 10181ae08745Sheppo D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx); 10191ae08745Sheppo 10201ae08745Sheppo if (idx == LDC_NUM_VERS) { 10211ae08745Sheppo /* no version match - send NACK */ 10221ae08745Sheppo pkt->stype = LDC_NACK; 10231ae08745Sheppo bzero(pkt->udata, sizeof (ldc_ver_t)); 10241ae08745Sheppo ldcp->next_vidx = 0; 10251ae08745Sheppo break; 10261ae08745Sheppo } 10271ae08745Sheppo } 10281ae08745Sheppo 10291ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 10301ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 10311ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 10321ae08745Sheppo 10331ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 10341ae08745Sheppo if (rv == 0) { 10351ae08745Sheppo ldcp->tx_tail = tx_tail; 10361ae08745Sheppo if (pkt->stype == LDC_ACK) { 10371ae08745Sheppo D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent" 10381ae08745Sheppo " version ACK\n", ldcp->id); 10391ae08745Sheppo /* Save the ACK'd version */ 10401ae08745Sheppo ldcp->version.major = rcvd_ver->major; 10411ae08745Sheppo ldcp->version.minor = rcvd_ver->minor; 10420a55fbb7Slm66018 ldcp->hstate |= TS_RCVD_VER; 10431ae08745Sheppo ldcp->tstate |= TS_VER_DONE; 104483d3bc6fSnarayan D1(DBG_ALL_LDCS, 10453af08d82Slm66018 "(0x%llx) Sent ACK, " 10463af08d82Slm66018 "Agreed on version v%u.%u\n", 10471ae08745Sheppo ldcp->id, rcvd_ver->major, rcvd_ver->minor); 10481ae08745Sheppo } 10491ae08745Sheppo } else { 10501ae08745Sheppo DWARN(ldcp->id, 10511ae08745Sheppo "i_ldc_process_VER: (0x%llx) error sending " 10521ae08745Sheppo "ACK/NACK\n", ldcp->id); 10533af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1054d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 10551ae08745Sheppo return (ECONNRESET); 10561ae08745Sheppo } 10571ae08745Sheppo 10581ae08745Sheppo break; 10591ae08745Sheppo 10601ae08745Sheppo case LDC_ACK: 10613af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) { 10623af08d82Slm66018 if (ldcp->version.major != rcvd_ver->major || 10633af08d82Slm66018 ldcp->version.minor != rcvd_ver->minor) { 10643af08d82Slm66018 10653af08d82Slm66018 /* mismatched version - reset connection */ 10663af08d82Slm66018 DWARN(ldcp->id, 10673af08d82Slm66018 "i_ldc_process_VER: (0x%llx) recvd" 10683af08d82Slm66018 " ACK ver != sent ACK ver\n", ldcp->id); 10693af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 10703af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 10713af08d82Slm66018 return (ECONNRESET); 10723af08d82Slm66018 } 10733af08d82Slm66018 } else { 10741ae08745Sheppo /* SUCCESS - we have agreed on a version */ 10751ae08745Sheppo ldcp->version.major = rcvd_ver->major; 10761ae08745Sheppo ldcp->version.minor = rcvd_ver->minor; 10771ae08745Sheppo ldcp->tstate |= TS_VER_DONE; 10783af08d82Slm66018 } 10791ae08745Sheppo 1080cb112a14Slm66018 D1(ldcp->id, "(0x%llx) Got ACK, Agreed on version v%u.%u\n", 10811ae08745Sheppo ldcp->id, rcvd_ver->major, rcvd_ver->minor); 10821ae08745Sheppo 10831ae08745Sheppo /* initiate RTS-RTR-RDX handshake */ 10841ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 10851ae08745Sheppo if (rv) { 10861ae08745Sheppo DWARN(ldcp->id, 10871ae08745Sheppo "i_ldc_process_VER: (0x%llx) cannot send RTS\n", 10881ae08745Sheppo ldcp->id); 10893af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1090d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 10911ae08745Sheppo return (ECONNRESET); 10921ae08745Sheppo } 10931ae08745Sheppo 10941ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 10951ae08745Sheppo ZERO_PKT(pkt); 10961ae08745Sheppo 10971ae08745Sheppo pkt->type = LDC_CTRL; 10981ae08745Sheppo pkt->stype = LDC_INFO; 10991ae08745Sheppo pkt->ctrl = LDC_RTS; 11001ae08745Sheppo pkt->env = ldcp->mode; 11011ae08745Sheppo if (ldcp->mode != LDC_MODE_RAW) 11021ae08745Sheppo pkt->seqid = LDC_INIT_SEQID; 11031ae08745Sheppo 11041ae08745Sheppo ldcp->last_msg_rcd = LDC_INIT_SEQID; 11051ae08745Sheppo 11061ae08745Sheppo DUMP_LDC_PKT(ldcp, "i_ldc_process_VER snd rts", (uint64_t)pkt); 11071ae08745Sheppo 11081ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 11091ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 11101ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 11111ae08745Sheppo 11121ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 11131ae08745Sheppo if (rv) { 11141ae08745Sheppo D2(ldcp->id, 11151ae08745Sheppo "i_ldc_process_VER: (0x%llx) no listener\n", 11161ae08745Sheppo ldcp->id); 11173af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1118d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 11191ae08745Sheppo return (ECONNRESET); 11201ae08745Sheppo } 11211ae08745Sheppo 11221ae08745Sheppo ldcp->tx_tail = tx_tail; 11231ae08745Sheppo ldcp->hstate |= TS_SENT_RTS; 11241ae08745Sheppo 11251ae08745Sheppo break; 11261ae08745Sheppo 11271ae08745Sheppo case LDC_NACK: 11281ae08745Sheppo /* check if version in NACK is zero */ 11291ae08745Sheppo if (rcvd_ver->major == 0 && rcvd_ver->minor == 0) { 11301ae08745Sheppo /* version handshake failure */ 11311ae08745Sheppo DWARN(DBG_ALL_LDCS, 11321ae08745Sheppo "i_ldc_process_VER: (0x%llx) no version match\n", 11331ae08745Sheppo ldcp->id); 11343af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1135d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 11361ae08745Sheppo return (ECONNRESET); 11371ae08745Sheppo } 11381ae08745Sheppo 11391ae08745Sheppo /* get the current tail and pkt for the response */ 11401ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 11411ae08745Sheppo if (rv != 0) { 11421ae08745Sheppo cmn_err(CE_NOTE, 11431ae08745Sheppo "i_ldc_process_VER: (0x%lx) err sending " 11441ae08745Sheppo "version ACK/NACK\n", ldcp->id); 11453af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1146d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 11471ae08745Sheppo return (ECONNRESET); 11481ae08745Sheppo } 11491ae08745Sheppo 11501ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 11511ae08745Sheppo ZERO_PKT(pkt); 11521ae08745Sheppo 11531ae08745Sheppo /* initialize the packet */ 11541ae08745Sheppo pkt->type = LDC_CTRL; 11551ae08745Sheppo pkt->ctrl = LDC_VER; 11561ae08745Sheppo pkt->stype = LDC_INFO; 11571ae08745Sheppo 11581ae08745Sheppo /* check ver in NACK msg has a match */ 11591ae08745Sheppo for (;;) { 11601ae08745Sheppo if (rcvd_ver->major == ldc_versions[idx].major) { 11611ae08745Sheppo /* 11621ae08745Sheppo * major version match - resubmit request 11631ae08745Sheppo * if lower minor version to the one this endpt 11641ae08745Sheppo * supports, if necessary 11651ae08745Sheppo */ 11661ae08745Sheppo if (rcvd_ver->minor > ldc_versions[idx].minor) 11671ae08745Sheppo rcvd_ver->minor = 11681ae08745Sheppo ldc_versions[idx].minor; 11691ae08745Sheppo bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver)); 11701ae08745Sheppo break; 11711ae08745Sheppo } 11721ae08745Sheppo 11731ae08745Sheppo if (rcvd_ver->major > ldc_versions[idx].major) { 11741ae08745Sheppo 11751ae08745Sheppo D1(ldcp->id, "i_ldc_process_VER: using next" 11761ae08745Sheppo " lower idx=%d, v%u.%u\n", idx, 11771ae08745Sheppo ldc_versions[idx].major, 11781ae08745Sheppo ldc_versions[idx].minor); 11791ae08745Sheppo 11801ae08745Sheppo /* send next lower version */ 11811ae08745Sheppo bcopy(&ldc_versions[idx], pkt->udata, 11821ae08745Sheppo sizeof (ldc_versions[idx])); 11831ae08745Sheppo ldcp->next_vidx = idx; 11841ae08745Sheppo break; 11851ae08745Sheppo } 11861ae08745Sheppo 11871ae08745Sheppo /* next version */ 11881ae08745Sheppo idx++; 11891ae08745Sheppo 11901ae08745Sheppo D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx); 11911ae08745Sheppo 11921ae08745Sheppo if (idx == LDC_NUM_VERS) { 11931ae08745Sheppo /* no version match - terminate */ 11941ae08745Sheppo ldcp->next_vidx = 0; 1195d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 11961ae08745Sheppo return (ECONNRESET); 11971ae08745Sheppo } 11981ae08745Sheppo } 11991ae08745Sheppo 12001ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 12011ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 12021ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 12031ae08745Sheppo 12041ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 12051ae08745Sheppo if (rv == 0) { 12061ae08745Sheppo D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent version" 12071ae08745Sheppo "INFO v%u.%u\n", ldcp->id, ldc_versions[idx].major, 12081ae08745Sheppo ldc_versions[idx].minor); 12091ae08745Sheppo ldcp->tx_tail = tx_tail; 12101ae08745Sheppo } else { 12111ae08745Sheppo cmn_err(CE_NOTE, 12121ae08745Sheppo "i_ldc_process_VER: (0x%lx) error sending version" 12131ae08745Sheppo "INFO\n", ldcp->id); 12143af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1215d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 12161ae08745Sheppo return (ECONNRESET); 12171ae08745Sheppo } 12181ae08745Sheppo 12191ae08745Sheppo break; 12201ae08745Sheppo } 12211ae08745Sheppo 1222d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 12231ae08745Sheppo return (rv); 12241ae08745Sheppo } 12251ae08745Sheppo 12261ae08745Sheppo 12271ae08745Sheppo /* 12281ae08745Sheppo * Process an incoming RTS ctrl message 12291ae08745Sheppo */ 12301ae08745Sheppo static int 12311ae08745Sheppo i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) 12321ae08745Sheppo { 12331ae08745Sheppo int rv = 0; 12341ae08745Sheppo ldc_msg_t *pkt; 12351ae08745Sheppo uint64_t tx_tail; 12361ae08745Sheppo boolean_t sent_NACK = B_FALSE; 12371ae08745Sheppo 12381ae08745Sheppo D2(ldcp->id, "i_ldc_process_RTS: (0x%llx) received RTS\n", ldcp->id); 12391ae08745Sheppo 12401ae08745Sheppo switch (msg->stype) { 12411ae08745Sheppo case LDC_NACK: 12421ae08745Sheppo DWARN(ldcp->id, 12431ae08745Sheppo "i_ldc_process_RTS: (0x%llx) RTS NACK received\n", 12441ae08745Sheppo ldcp->id); 12451ae08745Sheppo 12461ae08745Sheppo /* Reset the channel -- as we cannot continue */ 1247d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 12483af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1249d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 12501ae08745Sheppo rv = ECONNRESET; 12511ae08745Sheppo break; 12521ae08745Sheppo 12531ae08745Sheppo case LDC_INFO: 12541ae08745Sheppo 12551ae08745Sheppo /* check mode */ 12561ae08745Sheppo if (ldcp->mode != (ldc_mode_t)msg->env) { 12571ae08745Sheppo cmn_err(CE_NOTE, 12581ae08745Sheppo "i_ldc_process_RTS: (0x%lx) mode mismatch\n", 12591ae08745Sheppo ldcp->id); 12601ae08745Sheppo /* 12611ae08745Sheppo * send NACK in response to MODE message 12621ae08745Sheppo * get the current tail for the response 12631ae08745Sheppo */ 12641ae08745Sheppo rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTS); 12651ae08745Sheppo if (rv) { 12661ae08745Sheppo /* if cannot send NACK - reset channel */ 1267d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 12683af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1269d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 12701ae08745Sheppo rv = ECONNRESET; 12711ae08745Sheppo break; 12721ae08745Sheppo } 12731ae08745Sheppo sent_NACK = B_TRUE; 12741ae08745Sheppo } 12751ae08745Sheppo break; 12761ae08745Sheppo default: 12771ae08745Sheppo DWARN(ldcp->id, "i_ldc_process_RTS: (0x%llx) unexp ACK\n", 12781ae08745Sheppo ldcp->id); 1279d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 12803af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1281d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 12821ae08745Sheppo rv = ECONNRESET; 12831ae08745Sheppo break; 12841ae08745Sheppo } 12851ae08745Sheppo 12861ae08745Sheppo /* 12871ae08745Sheppo * If either the connection was reset (when rv != 0) or 12881ae08745Sheppo * a NACK was sent, we return. In the case of a NACK 12891ae08745Sheppo * we dont want to consume the packet that came in but 12901ae08745Sheppo * not record that we received the RTS 12911ae08745Sheppo */ 12921ae08745Sheppo if (rv || sent_NACK) 12931ae08745Sheppo return (rv); 12941ae08745Sheppo 12951ae08745Sheppo /* record RTS received */ 12961ae08745Sheppo ldcp->hstate |= TS_RCVD_RTS; 12971ae08745Sheppo 12981ae08745Sheppo /* store initial SEQID info */ 12991ae08745Sheppo ldcp->last_msg_snt = msg->seqid; 13001ae08745Sheppo 1301d10e4ef2Snarayan /* Obtain Tx lock */ 1302d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 1303d10e4ef2Snarayan 13041ae08745Sheppo /* get the current tail for the response */ 13051ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 13061ae08745Sheppo if (rv != 0) { 13071ae08745Sheppo cmn_err(CE_NOTE, 13081ae08745Sheppo "i_ldc_process_RTS: (0x%lx) err sending RTR\n", 13091ae08745Sheppo ldcp->id); 13103af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1311d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 13121ae08745Sheppo return (ECONNRESET); 13131ae08745Sheppo } 13141ae08745Sheppo 13151ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 13161ae08745Sheppo ZERO_PKT(pkt); 13171ae08745Sheppo 13181ae08745Sheppo /* initialize the packet */ 13191ae08745Sheppo pkt->type = LDC_CTRL; 13201ae08745Sheppo pkt->stype = LDC_INFO; 13211ae08745Sheppo pkt->ctrl = LDC_RTR; 13221ae08745Sheppo pkt->env = ldcp->mode; 13231ae08745Sheppo if (ldcp->mode != LDC_MODE_RAW) 13241ae08745Sheppo pkt->seqid = LDC_INIT_SEQID; 13251ae08745Sheppo 13261ae08745Sheppo ldcp->last_msg_rcd = msg->seqid; 13271ae08745Sheppo 13281ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 13291ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 13301ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 13311ae08745Sheppo 13321ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 13331ae08745Sheppo if (rv == 0) { 13341ae08745Sheppo D2(ldcp->id, 13351ae08745Sheppo "i_ldc_process_RTS: (0x%llx) sent RTR\n", ldcp->id); 13361ae08745Sheppo DUMP_LDC_PKT(ldcp, "i_ldc_process_RTS sent rtr", (uint64_t)pkt); 13371ae08745Sheppo 13381ae08745Sheppo ldcp->tx_tail = tx_tail; 13391ae08745Sheppo ldcp->hstate |= TS_SENT_RTR; 13401ae08745Sheppo 13411ae08745Sheppo } else { 13421ae08745Sheppo cmn_err(CE_NOTE, 13431ae08745Sheppo "i_ldc_process_RTS: (0x%lx) error sending RTR\n", 13441ae08745Sheppo ldcp->id); 13453af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1346d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 13471ae08745Sheppo return (ECONNRESET); 13481ae08745Sheppo } 13491ae08745Sheppo 1350d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 13511ae08745Sheppo return (0); 13521ae08745Sheppo } 13531ae08745Sheppo 13541ae08745Sheppo /* 13551ae08745Sheppo * Process an incoming RTR ctrl message 13561ae08745Sheppo */ 13571ae08745Sheppo static int 13581ae08745Sheppo i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg) 13591ae08745Sheppo { 13601ae08745Sheppo int rv = 0; 13611ae08745Sheppo boolean_t sent_NACK = B_FALSE; 13621ae08745Sheppo 13631ae08745Sheppo D2(ldcp->id, "i_ldc_process_RTR: (0x%llx) received RTR\n", ldcp->id); 13641ae08745Sheppo 13651ae08745Sheppo switch (msg->stype) { 13661ae08745Sheppo case LDC_NACK: 13671ae08745Sheppo /* RTR NACK received */ 13681ae08745Sheppo DWARN(ldcp->id, 13691ae08745Sheppo "i_ldc_process_RTR: (0x%llx) RTR NACK received\n", 13701ae08745Sheppo ldcp->id); 13711ae08745Sheppo 13721ae08745Sheppo /* Reset the channel -- as we cannot continue */ 1373d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 13743af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1375d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 13761ae08745Sheppo rv = ECONNRESET; 13771ae08745Sheppo 13781ae08745Sheppo break; 13791ae08745Sheppo 13801ae08745Sheppo case LDC_INFO: 13811ae08745Sheppo 13821ae08745Sheppo /* check mode */ 13831ae08745Sheppo if (ldcp->mode != (ldc_mode_t)msg->env) { 13841ae08745Sheppo DWARN(ldcp->id, 1385cb112a14Slm66018 "i_ldc_process_RTR: (0x%llx) mode mismatch, " 1386cb112a14Slm66018 "expecting 0x%x, got 0x%x\n", 1387cb112a14Slm66018 ldcp->id, ldcp->mode, (ldc_mode_t)msg->env); 13881ae08745Sheppo /* 13891ae08745Sheppo * send NACK in response to MODE message 13901ae08745Sheppo * get the current tail for the response 13911ae08745Sheppo */ 13921ae08745Sheppo rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTR); 13931ae08745Sheppo if (rv) { 13941ae08745Sheppo /* if cannot send NACK - reset channel */ 1395d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 13963af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1397d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 13981ae08745Sheppo rv = ECONNRESET; 13991ae08745Sheppo break; 14001ae08745Sheppo } 14011ae08745Sheppo sent_NACK = B_TRUE; 14021ae08745Sheppo } 14031ae08745Sheppo break; 14041ae08745Sheppo 14051ae08745Sheppo default: 14061ae08745Sheppo DWARN(ldcp->id, "i_ldc_process_RTR: (0x%llx) unexp ACK\n", 14071ae08745Sheppo ldcp->id); 14081ae08745Sheppo 14091ae08745Sheppo /* Reset the channel -- as we cannot continue */ 1410d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 14113af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1412d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 14131ae08745Sheppo rv = ECONNRESET; 14141ae08745Sheppo break; 14151ae08745Sheppo } 14161ae08745Sheppo 14171ae08745Sheppo /* 14181ae08745Sheppo * If either the connection was reset (when rv != 0) or 14191ae08745Sheppo * a NACK was sent, we return. In the case of a NACK 14201ae08745Sheppo * we dont want to consume the packet that came in but 14211ae08745Sheppo * not record that we received the RTR 14221ae08745Sheppo */ 14231ae08745Sheppo if (rv || sent_NACK) 14241ae08745Sheppo return (rv); 14251ae08745Sheppo 14261ae08745Sheppo ldcp->last_msg_snt = msg->seqid; 14271ae08745Sheppo ldcp->hstate |= TS_RCVD_RTR; 14281ae08745Sheppo 14291ae08745Sheppo rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_INFO, LDC_RDX); 14301ae08745Sheppo if (rv) { 14311ae08745Sheppo cmn_err(CE_NOTE, 14321ae08745Sheppo "i_ldc_process_RTR: (0x%lx) cannot send RDX\n", 14331ae08745Sheppo ldcp->id); 1434d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 14353af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1436d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 14371ae08745Sheppo return (ECONNRESET); 14381ae08745Sheppo } 14391ae08745Sheppo D2(ldcp->id, 14401ae08745Sheppo "i_ldc_process_RTR: (0x%llx) sent RDX\n", ldcp->id); 14411ae08745Sheppo 14421ae08745Sheppo ldcp->hstate |= TS_SENT_RDX; 14431ae08745Sheppo ldcp->tstate |= TS_HSHAKE_DONE; 14443af08d82Slm66018 if ((ldcp->tstate & TS_IN_RESET) == 0) 14451ae08745Sheppo ldcp->status = LDC_UP; 14461ae08745Sheppo 1447cb112a14Slm66018 D1(ldcp->id, "(0x%llx) Handshake Complete\n", ldcp->id); 14481ae08745Sheppo 14491ae08745Sheppo return (0); 14501ae08745Sheppo } 14511ae08745Sheppo 14521ae08745Sheppo 14531ae08745Sheppo /* 14541ae08745Sheppo * Process an incoming RDX ctrl message 14551ae08745Sheppo */ 14561ae08745Sheppo static int 14571ae08745Sheppo i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg) 14581ae08745Sheppo { 14591ae08745Sheppo int rv = 0; 14601ae08745Sheppo 14611ae08745Sheppo D2(ldcp->id, "i_ldc_process_RDX: (0x%llx) received RDX\n", ldcp->id); 14621ae08745Sheppo 14631ae08745Sheppo switch (msg->stype) { 14641ae08745Sheppo case LDC_NACK: 14651ae08745Sheppo /* RDX NACK received */ 14661ae08745Sheppo DWARN(ldcp->id, 14671ae08745Sheppo "i_ldc_process_RDX: (0x%llx) RDX NACK received\n", 14681ae08745Sheppo ldcp->id); 14691ae08745Sheppo 14701ae08745Sheppo /* Reset the channel -- as we cannot continue */ 1471d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 14723af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1473d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 14741ae08745Sheppo rv = ECONNRESET; 14751ae08745Sheppo 14761ae08745Sheppo break; 14771ae08745Sheppo 14781ae08745Sheppo case LDC_INFO: 14791ae08745Sheppo 14801ae08745Sheppo /* 14811ae08745Sheppo * if channel is UP and a RDX received after data transmission 14821ae08745Sheppo * has commenced it is an error 14831ae08745Sheppo */ 14841ae08745Sheppo if ((ldcp->tstate == TS_UP) && (ldcp->hstate & TS_RCVD_RDX)) { 14851ae08745Sheppo DWARN(DBG_ALL_LDCS, 14861ae08745Sheppo "i_ldc_process_RDX: (0x%llx) unexpected RDX" 14871ae08745Sheppo " - LDC reset\n", ldcp->id); 1488d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 14893af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1490d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 14911ae08745Sheppo return (ECONNRESET); 14921ae08745Sheppo } 14931ae08745Sheppo 14941ae08745Sheppo ldcp->hstate |= TS_RCVD_RDX; 14951ae08745Sheppo ldcp->tstate |= TS_HSHAKE_DONE; 14963af08d82Slm66018 if ((ldcp->tstate & TS_IN_RESET) == 0) 14971ae08745Sheppo ldcp->status = LDC_UP; 14981ae08745Sheppo 14991ae08745Sheppo D1(DBG_ALL_LDCS, "(0x%llx) Handshake Complete\n", ldcp->id); 15001ae08745Sheppo break; 15011ae08745Sheppo 15021ae08745Sheppo default: 15031ae08745Sheppo DWARN(ldcp->id, "i_ldc_process_RDX: (0x%llx) unexp ACK\n", 15041ae08745Sheppo ldcp->id); 15051ae08745Sheppo 15061ae08745Sheppo /* Reset the channel -- as we cannot continue */ 1507d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 15083af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1509d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 15101ae08745Sheppo rv = ECONNRESET; 15111ae08745Sheppo break; 15121ae08745Sheppo } 15131ae08745Sheppo 15141ae08745Sheppo return (rv); 15151ae08745Sheppo } 15161ae08745Sheppo 15171ae08745Sheppo /* 15181ae08745Sheppo * Process an incoming ACK for a data packet 15191ae08745Sheppo */ 15201ae08745Sheppo static int 15211ae08745Sheppo i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg) 15221ae08745Sheppo { 15231ae08745Sheppo int rv; 15241ae08745Sheppo uint64_t tx_head; 15251ae08745Sheppo ldc_msg_t *pkt; 15261ae08745Sheppo 1527d10e4ef2Snarayan /* Obtain Tx lock */ 1528d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 1529d10e4ef2Snarayan 15301ae08745Sheppo /* 1531d10e4ef2Snarayan * Read the current Tx head and tail 15321ae08745Sheppo */ 15331ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 15341ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 15351ae08745Sheppo if (rv != 0) { 15361ae08745Sheppo cmn_err(CE_WARN, 15371ae08745Sheppo "i_ldc_process_data_ACK: (0x%lx) cannot read qptrs\n", 15381ae08745Sheppo ldcp->id); 1539d10e4ef2Snarayan 1540d10e4ef2Snarayan /* Reset the channel -- as we cannot continue */ 15413af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1542d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 1543d10e4ef2Snarayan return (ECONNRESET); 15441ae08745Sheppo } 15451ae08745Sheppo 15461ae08745Sheppo /* 15471ae08745Sheppo * loop from where the previous ACK location was to the 15481ae08745Sheppo * current head location. This is how far the HV has 15491ae08745Sheppo * actually send pkts. Pkts between head and tail are 15501ae08745Sheppo * yet to be sent by HV. 15511ae08745Sheppo */ 15521ae08745Sheppo tx_head = ldcp->tx_ackd_head; 15531ae08745Sheppo for (;;) { 15541ae08745Sheppo pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_head); 15551ae08745Sheppo tx_head = (tx_head + LDC_PACKET_SIZE) % 15561ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 15571ae08745Sheppo 15581ae08745Sheppo if (pkt->seqid == msg->ackid) { 15591ae08745Sheppo D2(ldcp->id, 15601ae08745Sheppo "i_ldc_process_data_ACK: (0x%llx) found packet\n", 15611ae08745Sheppo ldcp->id); 15621ae08745Sheppo ldcp->last_ack_rcd = msg->ackid; 15631ae08745Sheppo ldcp->tx_ackd_head = tx_head; 15641ae08745Sheppo break; 15651ae08745Sheppo } 15661ae08745Sheppo if (tx_head == ldcp->tx_head) { 15671ae08745Sheppo /* could not find packet */ 15681ae08745Sheppo DWARN(ldcp->id, 15691ae08745Sheppo "i_ldc_process_data_ACK: (0x%llx) invalid ACKid\n", 15701ae08745Sheppo ldcp->id); 1571d10e4ef2Snarayan 1572d10e4ef2Snarayan /* Reset the channel -- as we cannot continue */ 15733af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 1574d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 1575d10e4ef2Snarayan return (ECONNRESET); 15761ae08745Sheppo } 15771ae08745Sheppo } 15781ae08745Sheppo 1579d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 15801ae08745Sheppo return (0); 15811ae08745Sheppo } 15821ae08745Sheppo 15831ae08745Sheppo /* 15841ae08745Sheppo * Process incoming control message 15851ae08745Sheppo * Return 0 - session can continue 15861ae08745Sheppo * EAGAIN - reprocess packet - state was changed 15871ae08745Sheppo * ECONNRESET - channel was reset 15881ae08745Sheppo */ 15891ae08745Sheppo static int 15901ae08745Sheppo i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg) 15911ae08745Sheppo { 15921ae08745Sheppo int rv = 0; 15931ae08745Sheppo 15943af08d82Slm66018 D1(ldcp->id, "i_ldc_ctrlmsg: (%llx) tstate = %lx, hstate = %lx\n", 15953af08d82Slm66018 ldcp->id, ldcp->tstate, ldcp->hstate); 15963af08d82Slm66018 15973af08d82Slm66018 switch (ldcp->tstate & ~TS_IN_RESET) { 15981ae08745Sheppo 15991ae08745Sheppo case TS_OPEN: 16001ae08745Sheppo case TS_READY: 16011ae08745Sheppo 16021ae08745Sheppo switch (msg->ctrl & LDC_CTRL_MASK) { 16031ae08745Sheppo case LDC_VER: 16041ae08745Sheppo /* process version message */ 16051ae08745Sheppo rv = i_ldc_process_VER(ldcp, msg); 16061ae08745Sheppo break; 16071ae08745Sheppo default: 16081ae08745Sheppo DWARN(ldcp->id, 16091ae08745Sheppo "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x " 16101ae08745Sheppo "tstate=0x%x\n", ldcp->id, 16111ae08745Sheppo (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate); 16121ae08745Sheppo break; 16131ae08745Sheppo } 16141ae08745Sheppo 16151ae08745Sheppo break; 16161ae08745Sheppo 16171ae08745Sheppo case TS_VREADY: 16181ae08745Sheppo 16191ae08745Sheppo switch (msg->ctrl & LDC_CTRL_MASK) { 16201ae08745Sheppo case LDC_VER: 16213af08d82Slm66018 /* process version message */ 16223af08d82Slm66018 rv = i_ldc_process_VER(ldcp, msg); 16231ae08745Sheppo break; 16241ae08745Sheppo case LDC_RTS: 16251ae08745Sheppo /* process RTS message */ 16261ae08745Sheppo rv = i_ldc_process_RTS(ldcp, msg); 16271ae08745Sheppo break; 16281ae08745Sheppo case LDC_RTR: 16291ae08745Sheppo /* process RTR message */ 16301ae08745Sheppo rv = i_ldc_process_RTR(ldcp, msg); 16311ae08745Sheppo break; 16321ae08745Sheppo case LDC_RDX: 16331ae08745Sheppo /* process RDX message */ 16341ae08745Sheppo rv = i_ldc_process_RDX(ldcp, msg); 16351ae08745Sheppo break; 16361ae08745Sheppo default: 16371ae08745Sheppo DWARN(ldcp->id, 16381ae08745Sheppo "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x " 16391ae08745Sheppo "tstate=0x%x\n", ldcp->id, 16401ae08745Sheppo (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate); 16411ae08745Sheppo break; 16421ae08745Sheppo } 16431ae08745Sheppo 16441ae08745Sheppo break; 16451ae08745Sheppo 16461ae08745Sheppo case TS_UP: 16471ae08745Sheppo 16481ae08745Sheppo switch (msg->ctrl & LDC_CTRL_MASK) { 16491ae08745Sheppo case LDC_VER: 16501ae08745Sheppo DWARN(ldcp->id, 16511ae08745Sheppo "i_ldc_ctrlmsg: (0x%llx) unexpected VER " 16521ae08745Sheppo "- LDC reset\n", ldcp->id); 16531ae08745Sheppo /* peer is redoing version negotiation */ 1654d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 16551ae08745Sheppo (void) i_ldc_txq_reconf(ldcp); 16561ae08745Sheppo i_ldc_reset_state(ldcp); 1657d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 16581ae08745Sheppo rv = EAGAIN; 16591ae08745Sheppo break; 16601ae08745Sheppo 16611ae08745Sheppo case LDC_RDX: 16621ae08745Sheppo /* process RDX message */ 16631ae08745Sheppo rv = i_ldc_process_RDX(ldcp, msg); 16641ae08745Sheppo break; 16651ae08745Sheppo 16661ae08745Sheppo default: 16671ae08745Sheppo DWARN(ldcp->id, 16681ae08745Sheppo "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x " 16691ae08745Sheppo "tstate=0x%x\n", ldcp->id, 16701ae08745Sheppo (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate); 16711ae08745Sheppo break; 16721ae08745Sheppo } 16731ae08745Sheppo } 16741ae08745Sheppo 16751ae08745Sheppo return (rv); 16761ae08745Sheppo } 16771ae08745Sheppo 16781ae08745Sheppo /* 16791ae08745Sheppo * Register channel with the channel nexus 16801ae08745Sheppo */ 16811ae08745Sheppo static int 16821ae08745Sheppo i_ldc_register_channel(ldc_chan_t *ldcp) 16831ae08745Sheppo { 16841ae08745Sheppo int rv = 0; 16851ae08745Sheppo ldc_cnex_t *cinfo = &ldcssp->cinfo; 16861ae08745Sheppo 16871ae08745Sheppo if (cinfo->dip == NULL) { 16881ae08745Sheppo DWARN(ldcp->id, 16891ae08745Sheppo "i_ldc_register_channel: cnex has not registered\n"); 16901ae08745Sheppo return (EAGAIN); 16911ae08745Sheppo } 16921ae08745Sheppo 16931ae08745Sheppo rv = cinfo->reg_chan(cinfo->dip, ldcp->id, ldcp->devclass); 16941ae08745Sheppo if (rv) { 16951ae08745Sheppo DWARN(ldcp->id, 16961ae08745Sheppo "i_ldc_register_channel: cannot register channel\n"); 16971ae08745Sheppo return (rv); 16981ae08745Sheppo } 16991ae08745Sheppo 17001ae08745Sheppo rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR, 17011ae08745Sheppo i_ldc_tx_hdlr, ldcp, NULL); 17021ae08745Sheppo if (rv) { 17031ae08745Sheppo DWARN(ldcp->id, 17041ae08745Sheppo "i_ldc_register_channel: cannot add Tx interrupt\n"); 17051ae08745Sheppo (void) cinfo->unreg_chan(cinfo->dip, ldcp->id); 17061ae08745Sheppo return (rv); 17071ae08745Sheppo } 17081ae08745Sheppo 17091ae08745Sheppo rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR, 17101ae08745Sheppo i_ldc_rx_hdlr, ldcp, NULL); 17111ae08745Sheppo if (rv) { 17121ae08745Sheppo DWARN(ldcp->id, 17131ae08745Sheppo "i_ldc_register_channel: cannot add Rx interrupt\n"); 17141ae08745Sheppo (void) cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR); 17151ae08745Sheppo (void) cinfo->unreg_chan(cinfo->dip, ldcp->id); 17161ae08745Sheppo return (rv); 17171ae08745Sheppo } 17181ae08745Sheppo 17191ae08745Sheppo ldcp->tstate |= TS_CNEX_RDY; 17201ae08745Sheppo 17211ae08745Sheppo return (0); 17221ae08745Sheppo } 17231ae08745Sheppo 17241ae08745Sheppo /* 17251ae08745Sheppo * Unregister a channel with the channel nexus 17261ae08745Sheppo */ 17271ae08745Sheppo static int 17281ae08745Sheppo i_ldc_unregister_channel(ldc_chan_t *ldcp) 17291ae08745Sheppo { 17301ae08745Sheppo int rv = 0; 17311ae08745Sheppo ldc_cnex_t *cinfo = &ldcssp->cinfo; 17321ae08745Sheppo 17331ae08745Sheppo if (cinfo->dip == NULL) { 17341ae08745Sheppo DWARN(ldcp->id, 17351ae08745Sheppo "i_ldc_unregister_channel: cnex has not registered\n"); 17361ae08745Sheppo return (EAGAIN); 17371ae08745Sheppo } 17381ae08745Sheppo 17391ae08745Sheppo if (ldcp->tstate & TS_CNEX_RDY) { 17401ae08745Sheppo 1741d10e4ef2Snarayan /* Remove the Rx interrupt */ 17421ae08745Sheppo rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR); 17431ae08745Sheppo if (rv) { 17443af08d82Slm66018 if (rv != EAGAIN) { 17451ae08745Sheppo DWARN(ldcp->id, 17463af08d82Slm66018 "i_ldc_unregister_channel: err removing " 17473af08d82Slm66018 "Rx intr\n"); 1748d10e4ef2Snarayan return (rv); 17491ae08745Sheppo } 1750d10e4ef2Snarayan 17513af08d82Slm66018 /* 17523af08d82Slm66018 * If interrupts are pending and handler has 17533af08d82Slm66018 * finished running, clear interrupt and try 17543af08d82Slm66018 * again 17553af08d82Slm66018 */ 17563af08d82Slm66018 if (ldcp->rx_intr_state != LDC_INTR_PEND) 17573af08d82Slm66018 return (rv); 17583af08d82Slm66018 17593af08d82Slm66018 (void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 17603af08d82Slm66018 rv = cinfo->rem_intr(cinfo->dip, ldcp->id, 17613af08d82Slm66018 CNEX_RX_INTR); 17623af08d82Slm66018 if (rv) { 17633af08d82Slm66018 DWARN(ldcp->id, "i_ldc_unregister_channel: " 17643af08d82Slm66018 "err removing Rx interrupt\n"); 17653af08d82Slm66018 return (rv); 17663af08d82Slm66018 } 17673af08d82Slm66018 } 17683af08d82Slm66018 1769d10e4ef2Snarayan /* Remove the Tx interrupt */ 17701ae08745Sheppo rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR); 17711ae08745Sheppo if (rv) { 17721ae08745Sheppo DWARN(ldcp->id, 17731ae08745Sheppo "i_ldc_unregister_channel: err removing Tx intr\n"); 1774d10e4ef2Snarayan return (rv); 17751ae08745Sheppo } 1776d10e4ef2Snarayan 1777d10e4ef2Snarayan /* Unregister the channel */ 17781ae08745Sheppo rv = cinfo->unreg_chan(ldcssp->cinfo.dip, ldcp->id); 17791ae08745Sheppo if (rv) { 17801ae08745Sheppo DWARN(ldcp->id, 17811ae08745Sheppo "i_ldc_unregister_channel: cannot unreg channel\n"); 1782d10e4ef2Snarayan return (rv); 17831ae08745Sheppo } 17841ae08745Sheppo 17851ae08745Sheppo ldcp->tstate &= ~TS_CNEX_RDY; 17861ae08745Sheppo } 17871ae08745Sheppo 17881ae08745Sheppo return (0); 17891ae08745Sheppo } 17901ae08745Sheppo 17911ae08745Sheppo 17921ae08745Sheppo /* 17931ae08745Sheppo * LDC transmit interrupt handler 17941ae08745Sheppo * triggered for chanel up/down/reset events 17951ae08745Sheppo * and Tx queue content changes 17961ae08745Sheppo */ 17971ae08745Sheppo static uint_t 17981ae08745Sheppo i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2) 17991ae08745Sheppo { 18001ae08745Sheppo _NOTE(ARGUNUSED(arg2)) 18011ae08745Sheppo 18021ae08745Sheppo int rv; 18031ae08745Sheppo ldc_chan_t *ldcp; 18041ae08745Sheppo boolean_t notify_client = B_FALSE; 18053af08d82Slm66018 uint64_t notify_event = 0, link_state; 18061ae08745Sheppo 18071ae08745Sheppo /* Get the channel for which interrupt was received */ 18081ae08745Sheppo ASSERT(arg1 != NULL); 18091ae08745Sheppo ldcp = (ldc_chan_t *)arg1; 18101ae08745Sheppo 18111ae08745Sheppo D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n", 18121ae08745Sheppo ldcp->id, ldcp); 18131ae08745Sheppo 18141ae08745Sheppo /* Lock channel */ 18151ae08745Sheppo mutex_enter(&ldcp->lock); 18161ae08745Sheppo 1817d10e4ef2Snarayan /* Obtain Tx lock */ 1818d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 1819d10e4ef2Snarayan 18204bac2208Snarayan /* mark interrupt as pending */ 18213af08d82Slm66018 ldcp->tx_intr_state = LDC_INTR_ACTIVE; 18223af08d82Slm66018 18233af08d82Slm66018 /* save current link state */ 18243af08d82Slm66018 link_state = ldcp->link_state; 18254bac2208Snarayan 18261ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail, 18271ae08745Sheppo &ldcp->link_state); 18281ae08745Sheppo if (rv) { 18291ae08745Sheppo cmn_err(CE_WARN, 18301ae08745Sheppo "i_ldc_tx_hdlr: (0x%lx) cannot read queue ptrs rv=0x%d\n", 18311ae08745Sheppo ldcp->id, rv); 18324bac2208Snarayan i_ldc_clear_intr(ldcp, CNEX_TX_INTR); 1833d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 18341ae08745Sheppo mutex_exit(&ldcp->lock); 18351ae08745Sheppo return (DDI_INTR_CLAIMED); 18361ae08745Sheppo } 18371ae08745Sheppo 18381ae08745Sheppo /* 18391ae08745Sheppo * reset the channel state if the channel went down 18401ae08745Sheppo * (other side unconfigured queue) or channel was reset 18411ae08745Sheppo * (other side reconfigured its queue) 18421ae08745Sheppo */ 18433af08d82Slm66018 if (link_state != ldcp->link_state && 18443af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_DOWN) { 18451ae08745Sheppo D1(ldcp->id, "i_ldc_tx_hdlr: channel link down\n", ldcp->id); 18463af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 18471ae08745Sheppo notify_client = B_TRUE; 18481ae08745Sheppo notify_event = LDC_EVT_DOWN; 18491ae08745Sheppo } 18501ae08745Sheppo 18513af08d82Slm66018 if (link_state != ldcp->link_state && 18523af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_RESET) { 18531ae08745Sheppo D1(ldcp->id, "i_ldc_tx_hdlr: channel link reset\n", ldcp->id); 18543af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 18551ae08745Sheppo notify_client = B_TRUE; 18561ae08745Sheppo notify_event = LDC_EVT_RESET; 18571ae08745Sheppo } 18581ae08745Sheppo 18593af08d82Slm66018 if (link_state != ldcp->link_state && 18603af08d82Slm66018 (ldcp->tstate & ~TS_IN_RESET) == TS_OPEN && 18613af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_UP) { 18621ae08745Sheppo D1(ldcp->id, "i_ldc_tx_hdlr: channel link up\n", ldcp->id); 18631ae08745Sheppo notify_client = B_TRUE; 18641ae08745Sheppo notify_event = LDC_EVT_RESET; 18651ae08745Sheppo ldcp->tstate |= TS_LINK_READY; 18661ae08745Sheppo ldcp->status = LDC_READY; 18671ae08745Sheppo } 18681ae08745Sheppo 18691ae08745Sheppo /* if callbacks are disabled, do not notify */ 18701ae08745Sheppo if (!ldcp->cb_enabled) 18711ae08745Sheppo notify_client = B_FALSE; 18721ae08745Sheppo 18734d39be2bSsg70180 i_ldc_clear_intr(ldcp, CNEX_TX_INTR); 187422f747efSnarayan mutex_exit(&ldcp->tx_lock); 18751ae08745Sheppo 18761ae08745Sheppo if (notify_client) { 18773af08d82Slm66018 ldcp->cb_inprogress = B_TRUE; 18783af08d82Slm66018 mutex_exit(&ldcp->lock); 18791ae08745Sheppo rv = ldcp->cb(notify_event, ldcp->cb_arg); 18801ae08745Sheppo if (rv) { 18811ae08745Sheppo DWARN(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) callback " 18821ae08745Sheppo "failure", ldcp->id); 18831ae08745Sheppo } 18841ae08745Sheppo mutex_enter(&ldcp->lock); 18851ae08745Sheppo ldcp->cb_inprogress = B_FALSE; 18861ae08745Sheppo } 18871ae08745Sheppo 18881ae08745Sheppo mutex_exit(&ldcp->lock); 18891ae08745Sheppo 18901ae08745Sheppo D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) exiting handler", ldcp->id); 18911ae08745Sheppo 18921ae08745Sheppo return (DDI_INTR_CLAIMED); 18931ae08745Sheppo } 18941ae08745Sheppo 18951ae08745Sheppo /* 18961ae08745Sheppo * LDC receive interrupt handler 18971ae08745Sheppo * triggered for channel with data pending to read 18981ae08745Sheppo * i.e. Rx queue content changes 18991ae08745Sheppo */ 19001ae08745Sheppo static uint_t 19011ae08745Sheppo i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) 19021ae08745Sheppo { 19031ae08745Sheppo _NOTE(ARGUNUSED(arg2)) 19041ae08745Sheppo 19051ae08745Sheppo int rv; 19061ae08745Sheppo uint64_t rx_head, rx_tail; 19071ae08745Sheppo ldc_msg_t *msg; 19081ae08745Sheppo ldc_chan_t *ldcp; 19091ae08745Sheppo boolean_t notify_client = B_FALSE; 19101ae08745Sheppo uint64_t notify_event = 0; 19113af08d82Slm66018 uint64_t link_state, first_fragment = 0; 19123af08d82Slm66018 19131ae08745Sheppo 19141ae08745Sheppo /* Get the channel for which interrupt was received */ 19151ae08745Sheppo if (arg1 == NULL) { 19161ae08745Sheppo cmn_err(CE_WARN, "i_ldc_rx_hdlr: invalid arg\n"); 19171ae08745Sheppo return (DDI_INTR_UNCLAIMED); 19181ae08745Sheppo } 19191ae08745Sheppo 19201ae08745Sheppo ldcp = (ldc_chan_t *)arg1; 19211ae08745Sheppo 19221ae08745Sheppo D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n", 19231ae08745Sheppo ldcp->id, ldcp); 19243af08d82Slm66018 D1(ldcp->id, "i_ldc_rx_hdlr: (%llx) USR%lx/TS%lx/HS%lx, LSTATE=%lx\n", 19253af08d82Slm66018 ldcp->id, ldcp->status, ldcp->tstate, ldcp->hstate, 19263af08d82Slm66018 ldcp->link_state); 19271ae08745Sheppo 19281ae08745Sheppo /* Lock channel */ 19291ae08745Sheppo mutex_enter(&ldcp->lock); 19301ae08745Sheppo 19311ae08745Sheppo /* mark interrupt as pending */ 19323af08d82Slm66018 ldcp->rx_intr_state = LDC_INTR_ACTIVE; 19331ae08745Sheppo 19341ae08745Sheppo /* 19351ae08745Sheppo * Read packet(s) from the queue 19361ae08745Sheppo */ 19371ae08745Sheppo for (;;) { 19381ae08745Sheppo 19393af08d82Slm66018 link_state = ldcp->link_state; 19401ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 19411ae08745Sheppo &ldcp->link_state); 19421ae08745Sheppo if (rv) { 19431ae08745Sheppo cmn_err(CE_WARN, 19441ae08745Sheppo "i_ldc_rx_hdlr: (0x%lx) cannot read " 19451ae08745Sheppo "queue ptrs, rv=0x%d\n", ldcp->id, rv); 19461ae08745Sheppo i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 19471ae08745Sheppo mutex_exit(&ldcp->lock); 19481ae08745Sheppo return (DDI_INTR_CLAIMED); 19491ae08745Sheppo } 19501ae08745Sheppo 19511ae08745Sheppo /* 19521ae08745Sheppo * reset the channel state if the channel went down 19531ae08745Sheppo * (other side unconfigured queue) or channel was reset 19543af08d82Slm66018 * (other side reconfigured its queue) 19551ae08745Sheppo */ 19563af08d82Slm66018 19573af08d82Slm66018 if (link_state != ldcp->link_state) { 1958cb112a14Slm66018 19593af08d82Slm66018 switch (ldcp->link_state) { 19603af08d82Slm66018 case LDC_CHANNEL_DOWN: 19613af08d82Slm66018 D1(ldcp->id, "i_ldc_rx_hdlr: channel " 19623af08d82Slm66018 "link down\n", ldcp->id); 1963d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 19643af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 1965d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 19661ae08745Sheppo notify_client = B_TRUE; 19671ae08745Sheppo notify_event = LDC_EVT_DOWN; 19683af08d82Slm66018 goto loop_exit; 19691ae08745Sheppo 19703af08d82Slm66018 case LDC_CHANNEL_UP: 19713af08d82Slm66018 D1(ldcp->id, "i_ldc_rx_hdlr: " 19723af08d82Slm66018 "channel link up\n", ldcp->id); 19733af08d82Slm66018 19743af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) == TS_OPEN) { 19751ae08745Sheppo notify_client = B_TRUE; 19761ae08745Sheppo notify_event = LDC_EVT_RESET; 19771ae08745Sheppo ldcp->tstate |= TS_LINK_READY; 19781ae08745Sheppo ldcp->status = LDC_READY; 19791ae08745Sheppo } 19803af08d82Slm66018 break; 19813af08d82Slm66018 19823af08d82Slm66018 case LDC_CHANNEL_RESET: 19833af08d82Slm66018 default: 19843af08d82Slm66018 #ifdef DEBUG 19853af08d82Slm66018 force_reset: 19863af08d82Slm66018 #endif 19873af08d82Slm66018 D1(ldcp->id, "i_ldc_rx_hdlr: channel " 19883af08d82Slm66018 "link reset\n", ldcp->id); 19893af08d82Slm66018 mutex_enter(&ldcp->tx_lock); 19903af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 19913af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 19923af08d82Slm66018 notify_client = B_TRUE; 19933af08d82Slm66018 notify_event = LDC_EVT_RESET; 19943af08d82Slm66018 break; 19953af08d82Slm66018 } 19963af08d82Slm66018 } 19973af08d82Slm66018 19983af08d82Slm66018 #ifdef DEBUG 19993af08d82Slm66018 if (LDC_INJECT_RESET(ldcp)) 20003af08d82Slm66018 goto force_reset; 20013af08d82Slm66018 #endif 20021ae08745Sheppo 20031ae08745Sheppo if (rx_head == rx_tail) { 20041ae08745Sheppo D2(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) No packets\n", 20051ae08745Sheppo ldcp->id); 20061ae08745Sheppo break; 20071ae08745Sheppo } 20083af08d82Slm66018 20091ae08745Sheppo D2(ldcp->id, "i_ldc_rx_hdlr: head=0x%llx, tail=0x%llx\n", 20101ae08745Sheppo rx_head, rx_tail); 20111ae08745Sheppo DUMP_LDC_PKT(ldcp, "i_ldc_rx_hdlr rcd", 20121ae08745Sheppo ldcp->rx_q_va + rx_head); 20131ae08745Sheppo 20141ae08745Sheppo /* get the message */ 20151ae08745Sheppo msg = (ldc_msg_t *)(ldcp->rx_q_va + rx_head); 20161ae08745Sheppo 20171ae08745Sheppo /* if channel is in RAW mode or data pkt, notify and return */ 20181ae08745Sheppo if (ldcp->mode == LDC_MODE_RAW) { 20191ae08745Sheppo notify_client = B_TRUE; 20201ae08745Sheppo notify_event |= LDC_EVT_READ; 20211ae08745Sheppo break; 20221ae08745Sheppo } 20231ae08745Sheppo 20241ae08745Sheppo if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) { 20251ae08745Sheppo 20261ae08745Sheppo /* discard packet if channel is not up */ 20273af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) != TS_UP) { 20281ae08745Sheppo 20291ae08745Sheppo /* move the head one position */ 20301ae08745Sheppo rx_head = (rx_head + LDC_PACKET_SIZE) % 20311ae08745Sheppo (ldcp->rx_q_entries << LDC_PACKET_SHIFT); 20321ae08745Sheppo 20331ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, rx_head)) 20341ae08745Sheppo break; 20351ae08745Sheppo 20361ae08745Sheppo continue; 20371ae08745Sheppo } else { 20383af08d82Slm66018 if ((ldcp->tstate & TS_IN_RESET) == 0) 20391ae08745Sheppo notify_client = B_TRUE; 20401ae08745Sheppo notify_event |= LDC_EVT_READ; 20411ae08745Sheppo break; 20421ae08745Sheppo } 20431ae08745Sheppo } 20441ae08745Sheppo 20451ae08745Sheppo /* Check the sequence ID for the message received */ 20463af08d82Slm66018 rv = i_ldc_check_seqid(ldcp, msg); 20473af08d82Slm66018 if (rv != 0) { 20481ae08745Sheppo 20491ae08745Sheppo DWARN(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) seqid error, " 20501ae08745Sheppo "q_ptrs=0x%lx,0x%lx", ldcp->id, rx_head, rx_tail); 20511ae08745Sheppo 20521ae08745Sheppo /* Reset last_msg_rcd to start of message */ 2053d10e4ef2Snarayan if (first_fragment != 0) { 2054d10e4ef2Snarayan ldcp->last_msg_rcd = first_fragment - 1; 2055d10e4ef2Snarayan first_fragment = 0; 20561ae08745Sheppo } 2057d10e4ef2Snarayan 20581ae08745Sheppo /* 20591ae08745Sheppo * Send a NACK due to seqid mismatch 20601ae08745Sheppo */ 206122f747efSnarayan rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK, 20621ae08745Sheppo (msg->ctrl & LDC_CTRL_MASK)); 20631ae08745Sheppo 20641ae08745Sheppo if (rv) { 20651ae08745Sheppo cmn_err(CE_NOTE, 20661ae08745Sheppo "i_ldc_rx_hdlr: (0x%lx) err sending " 206722f747efSnarayan "CTRL/DATA NACK msg\n", ldcp->id); 2068d10e4ef2Snarayan 2069d10e4ef2Snarayan /* if cannot send NACK - reset channel */ 2070d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 20713af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 2072d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 207383d3bc6fSnarayan 207483d3bc6fSnarayan notify_client = B_TRUE; 207583d3bc6fSnarayan notify_event = LDC_EVT_RESET; 2076d10e4ef2Snarayan break; 20771ae08745Sheppo } 20781ae08745Sheppo 20791ae08745Sheppo /* purge receive queue */ 20801ae08745Sheppo (void) i_ldc_set_rx_head(ldcp, rx_tail); 20811ae08745Sheppo break; 20821ae08745Sheppo } 20831ae08745Sheppo 20841ae08745Sheppo /* record the message ID */ 20851ae08745Sheppo ldcp->last_msg_rcd = msg->seqid; 20861ae08745Sheppo 20871ae08745Sheppo /* process control messages */ 20881ae08745Sheppo if (msg->type & LDC_CTRL) { 20891ae08745Sheppo /* save current internal state */ 20901ae08745Sheppo uint64_t tstate = ldcp->tstate; 20911ae08745Sheppo 20921ae08745Sheppo rv = i_ldc_ctrlmsg(ldcp, msg); 20931ae08745Sheppo if (rv == EAGAIN) { 20941ae08745Sheppo /* re-process pkt - state was adjusted */ 20951ae08745Sheppo continue; 20961ae08745Sheppo } 20971ae08745Sheppo if (rv == ECONNRESET) { 20981ae08745Sheppo notify_client = B_TRUE; 20991ae08745Sheppo notify_event = LDC_EVT_RESET; 21001ae08745Sheppo break; 21011ae08745Sheppo } 21021ae08745Sheppo 21031ae08745Sheppo /* 21041ae08745Sheppo * control message processing was successful 21051ae08745Sheppo * channel transitioned to ready for communication 21061ae08745Sheppo */ 21071ae08745Sheppo if (rv == 0 && ldcp->tstate == TS_UP && 21083af08d82Slm66018 (tstate & ~TS_IN_RESET) != 21093af08d82Slm66018 (ldcp->tstate & ~TS_IN_RESET)) { 21101ae08745Sheppo notify_client = B_TRUE; 21111ae08745Sheppo notify_event = LDC_EVT_UP; 21121ae08745Sheppo } 21131ae08745Sheppo } 21141ae08745Sheppo 211583d3bc6fSnarayan /* process data NACKs */ 211683d3bc6fSnarayan if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) { 211783d3bc6fSnarayan DWARN(ldcp->id, 211883d3bc6fSnarayan "i_ldc_rx_hdlr: (0x%llx) received DATA/NACK", 211983d3bc6fSnarayan ldcp->id); 212083d3bc6fSnarayan mutex_enter(&ldcp->tx_lock); 212183d3bc6fSnarayan i_ldc_reset(ldcp, B_TRUE); 212283d3bc6fSnarayan mutex_exit(&ldcp->tx_lock); 212383d3bc6fSnarayan notify_client = B_TRUE; 212483d3bc6fSnarayan notify_event = LDC_EVT_RESET; 212583d3bc6fSnarayan break; 212683d3bc6fSnarayan } 212783d3bc6fSnarayan 21281ae08745Sheppo /* process data ACKs */ 21291ae08745Sheppo if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) { 2130d10e4ef2Snarayan if (rv = i_ldc_process_data_ACK(ldcp, msg)) { 2131d10e4ef2Snarayan notify_client = B_TRUE; 2132d10e4ef2Snarayan notify_event = LDC_EVT_RESET; 2133d10e4ef2Snarayan break; 2134d10e4ef2Snarayan } 21351ae08745Sheppo } 21361ae08745Sheppo 21371ae08745Sheppo /* move the head one position */ 21381ae08745Sheppo rx_head = (rx_head + LDC_PACKET_SIZE) % 21391ae08745Sheppo (ldcp->rx_q_entries << LDC_PACKET_SHIFT); 21400a55fbb7Slm66018 if (rv = i_ldc_set_rx_head(ldcp, rx_head)) { 21410a55fbb7Slm66018 notify_client = B_TRUE; 21420a55fbb7Slm66018 notify_event = LDC_EVT_RESET; 21431ae08745Sheppo break; 21440a55fbb7Slm66018 } 21451ae08745Sheppo 21461ae08745Sheppo } /* for */ 21471ae08745Sheppo 21483af08d82Slm66018 loop_exit: 21493af08d82Slm66018 21501ae08745Sheppo /* if callbacks are disabled, do not notify */ 21511ae08745Sheppo if (!ldcp->cb_enabled) 21521ae08745Sheppo notify_client = B_FALSE; 21531ae08745Sheppo 21543af08d82Slm66018 /* 21553af08d82Slm66018 * If there are data packets in the queue, the ldc_read will 21563af08d82Slm66018 * clear interrupts after draining the queue, else clear interrupts 21573af08d82Slm66018 */ 21583af08d82Slm66018 if ((notify_event & LDC_EVT_READ) == 0) { 21593af08d82Slm66018 i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 21603af08d82Slm66018 } else 21613af08d82Slm66018 ldcp->rx_intr_state = LDC_INTR_PEND; 21621ae08745Sheppo 21631ae08745Sheppo 21641ae08745Sheppo if (notify_client) { 21654d39be2bSsg70180 ldcp->cb_inprogress = B_TRUE; 21664d39be2bSsg70180 mutex_exit(&ldcp->lock); 21671ae08745Sheppo rv = ldcp->cb(notify_event, ldcp->cb_arg); 21681ae08745Sheppo if (rv) { 21691ae08745Sheppo DWARN(ldcp->id, 21701ae08745Sheppo "i_ldc_rx_hdlr: (0x%llx) callback failure", 21711ae08745Sheppo ldcp->id); 21721ae08745Sheppo } 21734d39be2bSsg70180 mutex_enter(&ldcp->lock); 21744d39be2bSsg70180 ldcp->cb_inprogress = B_FALSE; 21751ae08745Sheppo } 21761ae08745Sheppo 21774d39be2bSsg70180 mutex_exit(&ldcp->lock); 21784d39be2bSsg70180 21791ae08745Sheppo D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) exiting handler", ldcp->id); 21801ae08745Sheppo return (DDI_INTR_CLAIMED); 21811ae08745Sheppo } 21821ae08745Sheppo 21831ae08745Sheppo 21841ae08745Sheppo /* -------------------------------------------------------------------------- */ 21851ae08745Sheppo 21861ae08745Sheppo /* 21871ae08745Sheppo * LDC API functions 21881ae08745Sheppo */ 21891ae08745Sheppo 21901ae08745Sheppo /* 21911ae08745Sheppo * Initialize the channel. Allocate internal structure and memory for 21921ae08745Sheppo * TX/RX queues, and initialize locks. 21931ae08745Sheppo */ 21941ae08745Sheppo int 21951ae08745Sheppo ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle) 21961ae08745Sheppo { 21971ae08745Sheppo ldc_chan_t *ldcp; 21981ae08745Sheppo int rv, exit_val; 21991ae08745Sheppo uint64_t ra_base, nentries; 2200e1ebb9ecSlm66018 uint64_t qlen; 22011ae08745Sheppo 22021ae08745Sheppo exit_val = EINVAL; /* guarantee an error if exit on failure */ 22031ae08745Sheppo 22041ae08745Sheppo if (attr == NULL) { 22051ae08745Sheppo DWARN(id, "ldc_init: (0x%llx) invalid attr\n", id); 22061ae08745Sheppo return (EINVAL); 22071ae08745Sheppo } 22081ae08745Sheppo if (handle == NULL) { 22091ae08745Sheppo DWARN(id, "ldc_init: (0x%llx) invalid handle\n", id); 22101ae08745Sheppo return (EINVAL); 22111ae08745Sheppo } 22121ae08745Sheppo 22131ae08745Sheppo /* check if channel is valid */ 22141ae08745Sheppo rv = hv_ldc_tx_qinfo(id, &ra_base, &nentries); 22151ae08745Sheppo if (rv == H_ECHANNEL) { 22161ae08745Sheppo DWARN(id, "ldc_init: (0x%llx) invalid channel id\n", id); 22171ae08745Sheppo return (EINVAL); 22181ae08745Sheppo } 22191ae08745Sheppo 22201ae08745Sheppo /* check if the channel has already been initialized */ 22211ae08745Sheppo mutex_enter(&ldcssp->lock); 22221ae08745Sheppo ldcp = ldcssp->chan_list; 22231ae08745Sheppo while (ldcp != NULL) { 22241ae08745Sheppo if (ldcp->id == id) { 22251ae08745Sheppo DWARN(id, "ldc_init: (0x%llx) already initialized\n", 22261ae08745Sheppo id); 22271ae08745Sheppo mutex_exit(&ldcssp->lock); 22281ae08745Sheppo return (EADDRINUSE); 22291ae08745Sheppo } 22301ae08745Sheppo ldcp = ldcp->next; 22311ae08745Sheppo } 22321ae08745Sheppo mutex_exit(&ldcssp->lock); 22331ae08745Sheppo 22341ae08745Sheppo ASSERT(ldcp == NULL); 22351ae08745Sheppo 22361ae08745Sheppo *handle = 0; 22371ae08745Sheppo 22381ae08745Sheppo /* Allocate an ldcp structure */ 22391ae08745Sheppo ldcp = kmem_zalloc(sizeof (ldc_chan_t), KM_SLEEP); 22401ae08745Sheppo 2241d10e4ef2Snarayan /* 2242d10e4ef2Snarayan * Initialize the channel and Tx lock 2243d10e4ef2Snarayan * 2244d10e4ef2Snarayan * The channel 'lock' protects the entire channel and 2245d10e4ef2Snarayan * should be acquired before initializing, resetting, 2246d10e4ef2Snarayan * destroying or reading from a channel. 2247d10e4ef2Snarayan * 2248d10e4ef2Snarayan * The 'tx_lock' should be acquired prior to transmitting 2249d10e4ef2Snarayan * data over the channel. The lock should also be acquired 2250d10e4ef2Snarayan * prior to channel reconfiguration (in order to prevent 2251d10e4ef2Snarayan * concurrent writes). 2252d10e4ef2Snarayan * 2253d10e4ef2Snarayan * ORDERING: When both locks are being acquired, to prevent 2254d10e4ef2Snarayan * deadlocks, the channel lock should be always acquired prior 2255d10e4ef2Snarayan * to the tx_lock. 2256d10e4ef2Snarayan */ 22571ae08745Sheppo mutex_init(&ldcp->lock, NULL, MUTEX_DRIVER, NULL); 2258d10e4ef2Snarayan mutex_init(&ldcp->tx_lock, NULL, MUTEX_DRIVER, NULL); 22591ae08745Sheppo 22601ae08745Sheppo /* Initialize the channel */ 22611ae08745Sheppo ldcp->id = id; 22621ae08745Sheppo ldcp->cb = NULL; 22631ae08745Sheppo ldcp->cb_arg = NULL; 22641ae08745Sheppo ldcp->cb_inprogress = B_FALSE; 22651ae08745Sheppo ldcp->cb_enabled = B_FALSE; 22661ae08745Sheppo ldcp->next = NULL; 22671ae08745Sheppo 22681ae08745Sheppo /* Read attributes */ 22691ae08745Sheppo ldcp->mode = attr->mode; 22701ae08745Sheppo ldcp->devclass = attr->devclass; 22711ae08745Sheppo ldcp->devinst = attr->instance; 2272e1ebb9ecSlm66018 ldcp->mtu = (attr->mtu > 0) ? attr->mtu : LDC_DEFAULT_MTU; 22731ae08745Sheppo 22741ae08745Sheppo D1(ldcp->id, 22751ae08745Sheppo "ldc_init: (0x%llx) channel attributes, class=0x%x, " 2276e1ebb9ecSlm66018 "instance=0x%llx, mode=%d, mtu=%d\n", 2277e1ebb9ecSlm66018 ldcp->id, ldcp->devclass, ldcp->devinst, ldcp->mode, ldcp->mtu); 22781ae08745Sheppo 22791ae08745Sheppo ldcp->next_vidx = 0; 22803af08d82Slm66018 ldcp->tstate = TS_IN_RESET; 22811ae08745Sheppo ldcp->hstate = 0; 22821ae08745Sheppo ldcp->last_msg_snt = LDC_INIT_SEQID; 22831ae08745Sheppo ldcp->last_ack_rcd = 0; 22841ae08745Sheppo ldcp->last_msg_rcd = 0; 22851ae08745Sheppo 22861ae08745Sheppo ldcp->stream_bufferp = NULL; 22871ae08745Sheppo ldcp->exp_dring_list = NULL; 22881ae08745Sheppo ldcp->imp_dring_list = NULL; 22891ae08745Sheppo ldcp->mhdl_list = NULL; 22901ae08745Sheppo 22913af08d82Slm66018 ldcp->tx_intr_state = LDC_INTR_NONE; 22923af08d82Slm66018 ldcp->rx_intr_state = LDC_INTR_NONE; 22933af08d82Slm66018 22941ae08745Sheppo /* Initialize payload size depending on whether channel is reliable */ 22951ae08745Sheppo switch (ldcp->mode) { 22961ae08745Sheppo case LDC_MODE_RAW: 22971ae08745Sheppo ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RAW; 22981ae08745Sheppo ldcp->read_p = i_ldc_read_raw; 22991ae08745Sheppo ldcp->write_p = i_ldc_write_raw; 23001ae08745Sheppo break; 23011ae08745Sheppo case LDC_MODE_UNRELIABLE: 23021ae08745Sheppo ldcp->pkt_payload = LDC_PAYLOAD_SIZE_UNRELIABLE; 23031ae08745Sheppo ldcp->read_p = i_ldc_read_packet; 23041ae08745Sheppo ldcp->write_p = i_ldc_write_packet; 23051ae08745Sheppo break; 23061ae08745Sheppo case LDC_MODE_RELIABLE: 23071ae08745Sheppo ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE; 23081ae08745Sheppo ldcp->read_p = i_ldc_read_packet; 23091ae08745Sheppo ldcp->write_p = i_ldc_write_packet; 23101ae08745Sheppo break; 23111ae08745Sheppo case LDC_MODE_STREAM: 23121ae08745Sheppo ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE; 23131ae08745Sheppo 23141ae08745Sheppo ldcp->stream_remains = 0; 23151ae08745Sheppo ldcp->stream_offset = 0; 23161ae08745Sheppo ldcp->stream_bufferp = kmem_alloc(ldcp->mtu, KM_SLEEP); 23171ae08745Sheppo ldcp->read_p = i_ldc_read_stream; 23181ae08745Sheppo ldcp->write_p = i_ldc_write_stream; 23191ae08745Sheppo break; 23201ae08745Sheppo default: 23211ae08745Sheppo exit_val = EINVAL; 23221ae08745Sheppo goto cleanup_on_exit; 23231ae08745Sheppo } 23241ae08745Sheppo 2325e1ebb9ecSlm66018 /* 2326e1ebb9ecSlm66018 * qlen is (mtu * ldc_mtu_msgs) / pkt_payload. If this 2327e1ebb9ecSlm66018 * value is smaller than default length of ldc_queue_entries, 232822f747efSnarayan * qlen is set to ldc_queue_entries. Ensure that computed 232922f747efSnarayan * length is a power-of-two value. 2330e1ebb9ecSlm66018 */ 2331e1ebb9ecSlm66018 qlen = (ldcp->mtu * ldc_mtu_msgs) / ldcp->pkt_payload; 233222f747efSnarayan if (!ISP2(qlen)) { 233322f747efSnarayan uint64_t tmp = 1; 233422f747efSnarayan while (qlen) { 233522f747efSnarayan qlen >>= 1; tmp <<= 1; 233622f747efSnarayan } 233722f747efSnarayan qlen = tmp; 233822f747efSnarayan } 233922f747efSnarayan 2340e1ebb9ecSlm66018 ldcp->rx_q_entries = 2341e1ebb9ecSlm66018 (qlen < ldc_queue_entries) ? ldc_queue_entries : qlen; 2342e1ebb9ecSlm66018 ldcp->tx_q_entries = ldcp->rx_q_entries; 2343e1ebb9ecSlm66018 234422f747efSnarayan D1(ldcp->id, "ldc_init: queue length = 0x%llx\n", ldcp->rx_q_entries); 2345e1ebb9ecSlm66018 23461ae08745Sheppo /* Create a transmit queue */ 23471ae08745Sheppo ldcp->tx_q_va = (uint64_t) 23481ae08745Sheppo contig_mem_alloc(ldcp->tx_q_entries << LDC_PACKET_SHIFT); 23491ae08745Sheppo if (ldcp->tx_q_va == NULL) { 23501ae08745Sheppo cmn_err(CE_WARN, 23511ae08745Sheppo "ldc_init: (0x%lx) TX queue allocation failed\n", 23521ae08745Sheppo ldcp->id); 23531ae08745Sheppo exit_val = ENOMEM; 23541ae08745Sheppo goto cleanup_on_exit; 23551ae08745Sheppo } 23561ae08745Sheppo ldcp->tx_q_ra = va_to_pa((caddr_t)ldcp->tx_q_va); 23571ae08745Sheppo 23581ae08745Sheppo D2(ldcp->id, "ldc_init: txq_va=0x%llx, txq_ra=0x%llx, entries=0x%llx\n", 23591ae08745Sheppo ldcp->tx_q_va, ldcp->tx_q_ra, ldcp->tx_q_entries); 23601ae08745Sheppo 23611ae08745Sheppo ldcp->tstate |= TS_TXQ_RDY; 23621ae08745Sheppo 23631ae08745Sheppo /* Create a receive queue */ 23641ae08745Sheppo ldcp->rx_q_va = (uint64_t) 23651ae08745Sheppo contig_mem_alloc(ldcp->rx_q_entries << LDC_PACKET_SHIFT); 23661ae08745Sheppo if (ldcp->rx_q_va == NULL) { 23671ae08745Sheppo cmn_err(CE_WARN, 23681ae08745Sheppo "ldc_init: (0x%lx) RX queue allocation failed\n", 23691ae08745Sheppo ldcp->id); 23701ae08745Sheppo exit_val = ENOMEM; 23711ae08745Sheppo goto cleanup_on_exit; 23721ae08745Sheppo } 23731ae08745Sheppo ldcp->rx_q_ra = va_to_pa((caddr_t)ldcp->rx_q_va); 23741ae08745Sheppo 23751ae08745Sheppo D2(ldcp->id, "ldc_init: rxq_va=0x%llx, rxq_ra=0x%llx, entries=0x%llx\n", 23761ae08745Sheppo ldcp->rx_q_va, ldcp->rx_q_ra, ldcp->rx_q_entries); 23771ae08745Sheppo 23781ae08745Sheppo ldcp->tstate |= TS_RXQ_RDY; 23791ae08745Sheppo 23801ae08745Sheppo /* Init descriptor ring and memory handle list lock */ 23811ae08745Sheppo mutex_init(&ldcp->exp_dlist_lock, NULL, MUTEX_DRIVER, NULL); 23821ae08745Sheppo mutex_init(&ldcp->imp_dlist_lock, NULL, MUTEX_DRIVER, NULL); 23831ae08745Sheppo mutex_init(&ldcp->mlist_lock, NULL, MUTEX_DRIVER, NULL); 23841ae08745Sheppo 23851ae08745Sheppo /* mark status as INITialized */ 23861ae08745Sheppo ldcp->status = LDC_INIT; 23871ae08745Sheppo 23881ae08745Sheppo /* Add to channel list */ 23891ae08745Sheppo mutex_enter(&ldcssp->lock); 23901ae08745Sheppo ldcp->next = ldcssp->chan_list; 23911ae08745Sheppo ldcssp->chan_list = ldcp; 23921ae08745Sheppo ldcssp->channel_count++; 23931ae08745Sheppo mutex_exit(&ldcssp->lock); 23941ae08745Sheppo 23951ae08745Sheppo /* set the handle */ 23961ae08745Sheppo *handle = (ldc_handle_t)ldcp; 23971ae08745Sheppo 23981ae08745Sheppo D1(ldcp->id, "ldc_init: (0x%llx) channel initialized\n", ldcp->id); 23991ae08745Sheppo 24001ae08745Sheppo return (0); 24011ae08745Sheppo 24021ae08745Sheppo cleanup_on_exit: 24031ae08745Sheppo 24041ae08745Sheppo if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp) 24051ae08745Sheppo kmem_free(ldcp->stream_bufferp, ldcp->mtu); 24061ae08745Sheppo 24071ae08745Sheppo if (ldcp->tstate & TS_TXQ_RDY) 24081ae08745Sheppo contig_mem_free((caddr_t)ldcp->tx_q_va, 24091ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT)); 24101ae08745Sheppo 24111ae08745Sheppo if (ldcp->tstate & TS_RXQ_RDY) 24121ae08745Sheppo contig_mem_free((caddr_t)ldcp->rx_q_va, 24131ae08745Sheppo (ldcp->rx_q_entries << LDC_PACKET_SHIFT)); 24141ae08745Sheppo 2415d10e4ef2Snarayan mutex_destroy(&ldcp->tx_lock); 24161ae08745Sheppo mutex_destroy(&ldcp->lock); 24171ae08745Sheppo 24181ae08745Sheppo if (ldcp) 24191ae08745Sheppo kmem_free(ldcp, sizeof (ldc_chan_t)); 24201ae08745Sheppo 24211ae08745Sheppo return (exit_val); 24221ae08745Sheppo } 24231ae08745Sheppo 24241ae08745Sheppo /* 24251ae08745Sheppo * Finalizes the LDC connection. It will return EBUSY if the 24261ae08745Sheppo * channel is open. A ldc_close() has to be done prior to 24271ae08745Sheppo * a ldc_fini operation. It frees TX/RX queues, associated 24281ae08745Sheppo * with the channel 24291ae08745Sheppo */ 24301ae08745Sheppo int 24311ae08745Sheppo ldc_fini(ldc_handle_t handle) 24321ae08745Sheppo { 24331ae08745Sheppo ldc_chan_t *ldcp; 24341ae08745Sheppo ldc_chan_t *tmp_ldcp; 24351ae08745Sheppo uint64_t id; 24361ae08745Sheppo 24371ae08745Sheppo if (handle == NULL) { 24381ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel handle\n"); 24391ae08745Sheppo return (EINVAL); 24401ae08745Sheppo } 24411ae08745Sheppo ldcp = (ldc_chan_t *)handle; 24421ae08745Sheppo id = ldcp->id; 24431ae08745Sheppo 24441ae08745Sheppo mutex_enter(&ldcp->lock); 24451ae08745Sheppo 24463af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) > TS_INIT) { 24471ae08745Sheppo DWARN(ldcp->id, "ldc_fini: (0x%llx) channel is open\n", 24481ae08745Sheppo ldcp->id); 24491ae08745Sheppo mutex_exit(&ldcp->lock); 24501ae08745Sheppo return (EBUSY); 24511ae08745Sheppo } 24521ae08745Sheppo 24531ae08745Sheppo /* Remove from the channel list */ 24541ae08745Sheppo mutex_enter(&ldcssp->lock); 24551ae08745Sheppo tmp_ldcp = ldcssp->chan_list; 24561ae08745Sheppo if (tmp_ldcp == ldcp) { 24571ae08745Sheppo ldcssp->chan_list = ldcp->next; 24581ae08745Sheppo ldcp->next = NULL; 24591ae08745Sheppo } else { 24601ae08745Sheppo while (tmp_ldcp != NULL) { 24611ae08745Sheppo if (tmp_ldcp->next == ldcp) { 24621ae08745Sheppo tmp_ldcp->next = ldcp->next; 24631ae08745Sheppo ldcp->next = NULL; 24641ae08745Sheppo break; 24651ae08745Sheppo } 24661ae08745Sheppo tmp_ldcp = tmp_ldcp->next; 24671ae08745Sheppo } 24681ae08745Sheppo if (tmp_ldcp == NULL) { 24691ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel hdl\n"); 24701ae08745Sheppo mutex_exit(&ldcssp->lock); 24711ae08745Sheppo mutex_exit(&ldcp->lock); 24721ae08745Sheppo return (EINVAL); 24731ae08745Sheppo } 24741ae08745Sheppo } 24751ae08745Sheppo 24761ae08745Sheppo ldcssp->channel_count--; 24771ae08745Sheppo 24781ae08745Sheppo mutex_exit(&ldcssp->lock); 24791ae08745Sheppo 24801ae08745Sheppo /* Free the map table for this channel */ 24811ae08745Sheppo if (ldcp->mtbl) { 24821ae08745Sheppo (void) hv_ldc_set_map_table(ldcp->id, NULL, NULL); 24833af08d82Slm66018 if (ldcp->mtbl->contigmem) 24841ae08745Sheppo contig_mem_free(ldcp->mtbl->table, ldcp->mtbl->size); 24853af08d82Slm66018 else 24863af08d82Slm66018 kmem_free(ldcp->mtbl->table, ldcp->mtbl->size); 24871ae08745Sheppo mutex_destroy(&ldcp->mtbl->lock); 24881ae08745Sheppo kmem_free(ldcp->mtbl, sizeof (ldc_mtbl_t)); 24891ae08745Sheppo } 24901ae08745Sheppo 24911ae08745Sheppo /* Destroy descriptor ring and memory handle list lock */ 24921ae08745Sheppo mutex_destroy(&ldcp->exp_dlist_lock); 24931ae08745Sheppo mutex_destroy(&ldcp->imp_dlist_lock); 24941ae08745Sheppo mutex_destroy(&ldcp->mlist_lock); 24951ae08745Sheppo 24961ae08745Sheppo /* Free the stream buffer for STREAM_MODE */ 24971ae08745Sheppo if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp) 24981ae08745Sheppo kmem_free(ldcp->stream_bufferp, ldcp->mtu); 24991ae08745Sheppo 25001ae08745Sheppo /* Free the RX queue */ 25011ae08745Sheppo contig_mem_free((caddr_t)ldcp->rx_q_va, 25021ae08745Sheppo (ldcp->rx_q_entries << LDC_PACKET_SHIFT)); 25031ae08745Sheppo ldcp->tstate &= ~TS_RXQ_RDY; 25041ae08745Sheppo 25051ae08745Sheppo /* Free the TX queue */ 25061ae08745Sheppo contig_mem_free((caddr_t)ldcp->tx_q_va, 25071ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT)); 25081ae08745Sheppo ldcp->tstate &= ~TS_TXQ_RDY; 25091ae08745Sheppo 25101ae08745Sheppo mutex_exit(&ldcp->lock); 25111ae08745Sheppo 25121ae08745Sheppo /* Destroy mutex */ 2513d10e4ef2Snarayan mutex_destroy(&ldcp->tx_lock); 25141ae08745Sheppo mutex_destroy(&ldcp->lock); 25151ae08745Sheppo 25161ae08745Sheppo /* free channel structure */ 25171ae08745Sheppo kmem_free(ldcp, sizeof (ldc_chan_t)); 25181ae08745Sheppo 25191ae08745Sheppo D1(id, "ldc_fini: (0x%llx) channel finalized\n", id); 25201ae08745Sheppo 25211ae08745Sheppo return (0); 25221ae08745Sheppo } 25231ae08745Sheppo 25241ae08745Sheppo /* 25251ae08745Sheppo * Open the LDC channel for use. It registers the TX/RX queues 25261ae08745Sheppo * with the Hypervisor. It also specifies the interrupt number 25271ae08745Sheppo * and target CPU for this channel 25281ae08745Sheppo */ 25291ae08745Sheppo int 25301ae08745Sheppo ldc_open(ldc_handle_t handle) 25311ae08745Sheppo { 25321ae08745Sheppo ldc_chan_t *ldcp; 25331ae08745Sheppo int rv; 25341ae08745Sheppo 25351ae08745Sheppo if (handle == NULL) { 25361ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_open: invalid channel handle\n"); 25371ae08745Sheppo return (EINVAL); 25381ae08745Sheppo } 25391ae08745Sheppo 25401ae08745Sheppo ldcp = (ldc_chan_t *)handle; 25411ae08745Sheppo 25421ae08745Sheppo mutex_enter(&ldcp->lock); 25431ae08745Sheppo 25441ae08745Sheppo if (ldcp->tstate < TS_INIT) { 25451ae08745Sheppo DWARN(ldcp->id, 25461ae08745Sheppo "ldc_open: (0x%llx) channel not initialized\n", ldcp->id); 25471ae08745Sheppo mutex_exit(&ldcp->lock); 25481ae08745Sheppo return (EFAULT); 25491ae08745Sheppo } 25503af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) >= TS_OPEN) { 25511ae08745Sheppo DWARN(ldcp->id, 25521ae08745Sheppo "ldc_open: (0x%llx) channel is already open\n", ldcp->id); 25531ae08745Sheppo mutex_exit(&ldcp->lock); 25541ae08745Sheppo return (EFAULT); 25551ae08745Sheppo } 25561ae08745Sheppo 25571ae08745Sheppo /* 25581ae08745Sheppo * Unregister/Register the tx queue with the hypervisor 25591ae08745Sheppo */ 25601ae08745Sheppo rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL); 25611ae08745Sheppo if (rv) { 25621ae08745Sheppo cmn_err(CE_WARN, 25631ae08745Sheppo "ldc_open: (0x%lx) channel tx queue unconf failed\n", 25641ae08745Sheppo ldcp->id); 25651ae08745Sheppo mutex_exit(&ldcp->lock); 25661ae08745Sheppo return (EIO); 25671ae08745Sheppo } 25681ae08745Sheppo 25691ae08745Sheppo rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries); 25701ae08745Sheppo if (rv) { 25711ae08745Sheppo cmn_err(CE_WARN, 25721ae08745Sheppo "ldc_open: (0x%lx) channel tx queue conf failed\n", 25731ae08745Sheppo ldcp->id); 25741ae08745Sheppo mutex_exit(&ldcp->lock); 25751ae08745Sheppo return (EIO); 25761ae08745Sheppo } 25771ae08745Sheppo 25781ae08745Sheppo D2(ldcp->id, "ldc_open: (0x%llx) registered tx queue with LDC\n", 25791ae08745Sheppo ldcp->id); 25801ae08745Sheppo 25811ae08745Sheppo /* 25821ae08745Sheppo * Unregister/Register the rx queue with the hypervisor 25831ae08745Sheppo */ 25841ae08745Sheppo rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL); 25851ae08745Sheppo if (rv) { 25861ae08745Sheppo cmn_err(CE_WARN, 25871ae08745Sheppo "ldc_open: (0x%lx) channel rx queue unconf failed\n", 25881ae08745Sheppo ldcp->id); 25891ae08745Sheppo mutex_exit(&ldcp->lock); 25901ae08745Sheppo return (EIO); 25911ae08745Sheppo } 25921ae08745Sheppo 25931ae08745Sheppo rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra, ldcp->rx_q_entries); 25941ae08745Sheppo if (rv) { 25951ae08745Sheppo cmn_err(CE_WARN, 25961ae08745Sheppo "ldc_open: (0x%lx) channel rx queue conf failed\n", 25971ae08745Sheppo ldcp->id); 25981ae08745Sheppo mutex_exit(&ldcp->lock); 25991ae08745Sheppo return (EIO); 26001ae08745Sheppo } 26011ae08745Sheppo 26021ae08745Sheppo D2(ldcp->id, "ldc_open: (0x%llx) registered rx queue with LDC\n", 26031ae08745Sheppo ldcp->id); 26041ae08745Sheppo 26051ae08745Sheppo ldcp->tstate |= TS_QCONF_RDY; 26061ae08745Sheppo 26071ae08745Sheppo /* Register the channel with the channel nexus */ 26081ae08745Sheppo rv = i_ldc_register_channel(ldcp); 26091ae08745Sheppo if (rv && rv != EAGAIN) { 26101ae08745Sheppo cmn_err(CE_WARN, 26111ae08745Sheppo "ldc_open: (0x%lx) channel register failed\n", ldcp->id); 26121ae08745Sheppo (void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL); 26131ae08745Sheppo (void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL); 26141ae08745Sheppo mutex_exit(&ldcp->lock); 26151ae08745Sheppo return (EIO); 26161ae08745Sheppo } 26171ae08745Sheppo 26181ae08745Sheppo /* mark channel in OPEN state */ 26191ae08745Sheppo ldcp->status = LDC_OPEN; 26201ae08745Sheppo 26211ae08745Sheppo /* Read channel state */ 26221ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 26231ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 26241ae08745Sheppo if (rv) { 26251ae08745Sheppo cmn_err(CE_WARN, 26261ae08745Sheppo "ldc_open: (0x%lx) cannot read channel state\n", 26271ae08745Sheppo ldcp->id); 26281ae08745Sheppo (void) i_ldc_unregister_channel(ldcp); 26291ae08745Sheppo (void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL); 26301ae08745Sheppo (void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL); 26311ae08745Sheppo mutex_exit(&ldcp->lock); 26321ae08745Sheppo return (EIO); 26331ae08745Sheppo } 26341ae08745Sheppo 26351ae08745Sheppo /* 26361ae08745Sheppo * set the ACKd head to current head location for reliable & 26371ae08745Sheppo * streaming mode 26381ae08745Sheppo */ 26391ae08745Sheppo ldcp->tx_ackd_head = ldcp->tx_head; 26401ae08745Sheppo 26411ae08745Sheppo /* mark channel ready if HV report link is UP (peer alloc'd Rx queue) */ 26421ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_UP || 26431ae08745Sheppo ldcp->link_state == LDC_CHANNEL_RESET) { 26441ae08745Sheppo ldcp->tstate |= TS_LINK_READY; 26451ae08745Sheppo ldcp->status = LDC_READY; 26461ae08745Sheppo } 26471ae08745Sheppo 26481ae08745Sheppo /* 26491ae08745Sheppo * if channel is being opened in RAW mode - no handshake is needed 26501ae08745Sheppo * switch the channel READY and UP state 26511ae08745Sheppo */ 26521ae08745Sheppo if (ldcp->mode == LDC_MODE_RAW) { 26531ae08745Sheppo ldcp->tstate = TS_UP; /* set bits associated with LDC UP */ 26541ae08745Sheppo ldcp->status = LDC_UP; 26551ae08745Sheppo } 26561ae08745Sheppo 26571ae08745Sheppo mutex_exit(&ldcp->lock); 26581ae08745Sheppo 26591ae08745Sheppo /* 26601ae08745Sheppo * Increment number of open channels 26611ae08745Sheppo */ 26621ae08745Sheppo mutex_enter(&ldcssp->lock); 26631ae08745Sheppo ldcssp->channels_open++; 26641ae08745Sheppo mutex_exit(&ldcssp->lock); 26651ae08745Sheppo 2666cb112a14Slm66018 D1(ldcp->id, 26673af08d82Slm66018 "ldc_open: (0x%llx) channel (0x%p) open for use " 26683af08d82Slm66018 "(tstate=0x%x, status=0x%x)\n", 26693af08d82Slm66018 ldcp->id, ldcp, ldcp->tstate, ldcp->status); 26701ae08745Sheppo 26711ae08745Sheppo return (0); 26721ae08745Sheppo } 26731ae08745Sheppo 26741ae08745Sheppo /* 26751ae08745Sheppo * Close the LDC connection. It will return EBUSY if there 26761ae08745Sheppo * are memory segments or descriptor rings either bound to or 26771ae08745Sheppo * mapped over the channel 26781ae08745Sheppo */ 26791ae08745Sheppo int 26801ae08745Sheppo ldc_close(ldc_handle_t handle) 26811ae08745Sheppo { 26821ae08745Sheppo ldc_chan_t *ldcp; 2683d10e4ef2Snarayan int rv = 0, retries = 0; 26841ae08745Sheppo boolean_t chk_done = B_FALSE; 26851ae08745Sheppo 26861ae08745Sheppo if (handle == NULL) { 26871ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_close: invalid channel handle\n"); 26881ae08745Sheppo return (EINVAL); 26891ae08745Sheppo } 26901ae08745Sheppo ldcp = (ldc_chan_t *)handle; 26911ae08745Sheppo 26921ae08745Sheppo mutex_enter(&ldcp->lock); 26931ae08745Sheppo 26941ae08745Sheppo /* return error if channel is not open */ 26953af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) < TS_OPEN) { 26961ae08745Sheppo DWARN(ldcp->id, 26971ae08745Sheppo "ldc_close: (0x%llx) channel is not open\n", ldcp->id); 26981ae08745Sheppo mutex_exit(&ldcp->lock); 26991ae08745Sheppo return (EFAULT); 27001ae08745Sheppo } 27011ae08745Sheppo 27021ae08745Sheppo /* if any memory handles, drings, are bound or mapped cannot close */ 27031ae08745Sheppo if (ldcp->mhdl_list != NULL) { 27041ae08745Sheppo DWARN(ldcp->id, 27051ae08745Sheppo "ldc_close: (0x%llx) channel has bound memory handles\n", 27061ae08745Sheppo ldcp->id); 27071ae08745Sheppo mutex_exit(&ldcp->lock); 27081ae08745Sheppo return (EBUSY); 27091ae08745Sheppo } 27101ae08745Sheppo if (ldcp->exp_dring_list != NULL) { 27111ae08745Sheppo DWARN(ldcp->id, 27121ae08745Sheppo "ldc_close: (0x%llx) channel has bound descriptor rings\n", 27131ae08745Sheppo ldcp->id); 27141ae08745Sheppo mutex_exit(&ldcp->lock); 27151ae08745Sheppo return (EBUSY); 27161ae08745Sheppo } 27171ae08745Sheppo if (ldcp->imp_dring_list != NULL) { 27181ae08745Sheppo DWARN(ldcp->id, 27191ae08745Sheppo "ldc_close: (0x%llx) channel has mapped descriptor rings\n", 27201ae08745Sheppo ldcp->id); 27211ae08745Sheppo mutex_exit(&ldcp->lock); 27221ae08745Sheppo return (EBUSY); 27231ae08745Sheppo } 27241ae08745Sheppo 27254d39be2bSsg70180 if (ldcp->cb_inprogress) { 27264d39be2bSsg70180 DWARN(ldcp->id, "ldc_close: (0x%llx) callback active\n", 27274d39be2bSsg70180 ldcp->id); 27284d39be2bSsg70180 mutex_exit(&ldcp->lock); 27294d39be2bSsg70180 return (EWOULDBLOCK); 27304d39be2bSsg70180 } 27314d39be2bSsg70180 2732d10e4ef2Snarayan /* Obtain Tx lock */ 2733d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 2734d10e4ef2Snarayan 27351ae08745Sheppo /* 27361ae08745Sheppo * Wait for pending transmits to complete i.e Tx queue to drain 27371ae08745Sheppo * if there are pending pkts - wait 1 ms and retry again 27381ae08745Sheppo */ 27391ae08745Sheppo for (;;) { 27401ae08745Sheppo 27411ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 27421ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 27431ae08745Sheppo if (rv) { 27441ae08745Sheppo cmn_err(CE_WARN, 27451ae08745Sheppo "ldc_close: (0x%lx) cannot read qptrs\n", ldcp->id); 2746d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 27471ae08745Sheppo mutex_exit(&ldcp->lock); 27481ae08745Sheppo return (EIO); 27491ae08745Sheppo } 27501ae08745Sheppo 27511ae08745Sheppo if (ldcp->tx_head == ldcp->tx_tail || 27521ae08745Sheppo ldcp->link_state != LDC_CHANNEL_UP) { 27531ae08745Sheppo break; 27541ae08745Sheppo } 27551ae08745Sheppo 27561ae08745Sheppo if (chk_done) { 27571ae08745Sheppo DWARN(ldcp->id, 27581ae08745Sheppo "ldc_close: (0x%llx) Tx queue drain timeout\n", 27591ae08745Sheppo ldcp->id); 27601ae08745Sheppo break; 27611ae08745Sheppo } 27621ae08745Sheppo 27631ae08745Sheppo /* wait for one ms and try again */ 27641ae08745Sheppo delay(drv_usectohz(1000)); 27651ae08745Sheppo chk_done = B_TRUE; 27661ae08745Sheppo } 27671ae08745Sheppo 27681ae08745Sheppo /* 2769a8ea4edeSnarayan * Drain the Tx and Rx queues as we are closing the 2770a8ea4edeSnarayan * channel. We dont care about any pending packets. 2771a8ea4edeSnarayan * We have to also drain the queue prior to clearing 2772a8ea4edeSnarayan * pending interrupts, otherwise the HV will trigger 2773a8ea4edeSnarayan * an interrupt the moment the interrupt state is 2774a8ea4edeSnarayan * cleared. 27753af08d82Slm66018 */ 27763af08d82Slm66018 (void) i_ldc_txq_reconf(ldcp); 2777a8ea4edeSnarayan (void) i_ldc_rxq_drain(ldcp); 27783af08d82Slm66018 27793af08d82Slm66018 /* 27801ae08745Sheppo * Unregister the channel with the nexus 27811ae08745Sheppo */ 2782d10e4ef2Snarayan while ((rv = i_ldc_unregister_channel(ldcp)) != 0) { 2783d10e4ef2Snarayan 2784d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 27851ae08745Sheppo mutex_exit(&ldcp->lock); 2786d10e4ef2Snarayan 2787d10e4ef2Snarayan /* if any error other than EAGAIN return back */ 2788a8ea4edeSnarayan if (rv != EAGAIN || retries >= ldc_max_retries) { 2789d10e4ef2Snarayan cmn_err(CE_WARN, 2790d10e4ef2Snarayan "ldc_close: (0x%lx) unregister failed, %d\n", 2791d10e4ef2Snarayan ldcp->id, rv); 27921ae08745Sheppo return (rv); 27931ae08745Sheppo } 27941ae08745Sheppo 27951ae08745Sheppo /* 2796d10e4ef2Snarayan * As there could be pending interrupts we need 2797d10e4ef2Snarayan * to wait and try again 2798d10e4ef2Snarayan */ 27994d39be2bSsg70180 drv_usecwait(ldc_close_delay); 2800d10e4ef2Snarayan mutex_enter(&ldcp->lock); 2801d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 2802d10e4ef2Snarayan retries++; 2803d10e4ef2Snarayan } 2804d10e4ef2Snarayan 2805d10e4ef2Snarayan /* 28061ae08745Sheppo * Unregister queues 28071ae08745Sheppo */ 28081ae08745Sheppo rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL); 28091ae08745Sheppo if (rv) { 28101ae08745Sheppo cmn_err(CE_WARN, 28111ae08745Sheppo "ldc_close: (0x%lx) channel TX queue unconf failed\n", 28121ae08745Sheppo ldcp->id); 2813d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 28141ae08745Sheppo mutex_exit(&ldcp->lock); 28151ae08745Sheppo return (EIO); 28161ae08745Sheppo } 28171ae08745Sheppo rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL); 28181ae08745Sheppo if (rv) { 28191ae08745Sheppo cmn_err(CE_WARN, 28201ae08745Sheppo "ldc_close: (0x%lx) channel RX queue unconf failed\n", 28211ae08745Sheppo ldcp->id); 2822d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 28231ae08745Sheppo mutex_exit(&ldcp->lock); 28241ae08745Sheppo return (EIO); 28251ae08745Sheppo } 28261ae08745Sheppo 28271ae08745Sheppo ldcp->tstate &= ~TS_QCONF_RDY; 28281ae08745Sheppo 28291ae08745Sheppo /* Reset channel state information */ 28301ae08745Sheppo i_ldc_reset_state(ldcp); 28311ae08745Sheppo 28321ae08745Sheppo /* Mark channel as down and in initialized state */ 28331ae08745Sheppo ldcp->tx_ackd_head = 0; 28341ae08745Sheppo ldcp->tx_head = 0; 28353af08d82Slm66018 ldcp->tstate = TS_IN_RESET|TS_INIT; 28361ae08745Sheppo ldcp->status = LDC_INIT; 28371ae08745Sheppo 2838d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 28391ae08745Sheppo mutex_exit(&ldcp->lock); 28401ae08745Sheppo 28411ae08745Sheppo /* Decrement number of open channels */ 28421ae08745Sheppo mutex_enter(&ldcssp->lock); 28431ae08745Sheppo ldcssp->channels_open--; 28441ae08745Sheppo mutex_exit(&ldcssp->lock); 28451ae08745Sheppo 28461ae08745Sheppo D1(ldcp->id, "ldc_close: (0x%llx) channel closed\n", ldcp->id); 28471ae08745Sheppo 28481ae08745Sheppo return (0); 28491ae08745Sheppo } 28501ae08745Sheppo 28511ae08745Sheppo /* 28521ae08745Sheppo * Register channel callback 28531ae08745Sheppo */ 28541ae08745Sheppo int 28551ae08745Sheppo ldc_reg_callback(ldc_handle_t handle, 28561ae08745Sheppo uint_t(*cb)(uint64_t event, caddr_t arg), caddr_t arg) 28571ae08745Sheppo { 28581ae08745Sheppo ldc_chan_t *ldcp; 28591ae08745Sheppo 28601ae08745Sheppo if (handle == NULL) { 28611ae08745Sheppo DWARN(DBG_ALL_LDCS, 28621ae08745Sheppo "ldc_reg_callback: invalid channel handle\n"); 28631ae08745Sheppo return (EINVAL); 28641ae08745Sheppo } 28651ae08745Sheppo if (((uint64_t)cb) < KERNELBASE) { 28661ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_reg_callback: invalid callback\n"); 28671ae08745Sheppo return (EINVAL); 28681ae08745Sheppo } 28691ae08745Sheppo ldcp = (ldc_chan_t *)handle; 28701ae08745Sheppo 28711ae08745Sheppo mutex_enter(&ldcp->lock); 28721ae08745Sheppo 28731ae08745Sheppo if (ldcp->cb) { 28741ae08745Sheppo DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback exists\n", 28751ae08745Sheppo ldcp->id); 28761ae08745Sheppo mutex_exit(&ldcp->lock); 28771ae08745Sheppo return (EIO); 28781ae08745Sheppo } 28791ae08745Sheppo if (ldcp->cb_inprogress) { 28801ae08745Sheppo DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback active\n", 28811ae08745Sheppo ldcp->id); 28821ae08745Sheppo mutex_exit(&ldcp->lock); 28831ae08745Sheppo return (EWOULDBLOCK); 28841ae08745Sheppo } 28851ae08745Sheppo 28861ae08745Sheppo ldcp->cb = cb; 28871ae08745Sheppo ldcp->cb_arg = arg; 28881ae08745Sheppo ldcp->cb_enabled = B_TRUE; 28891ae08745Sheppo 28901ae08745Sheppo D1(ldcp->id, 28911ae08745Sheppo "ldc_reg_callback: (0x%llx) registered callback for channel\n", 28921ae08745Sheppo ldcp->id); 28931ae08745Sheppo 28941ae08745Sheppo mutex_exit(&ldcp->lock); 28951ae08745Sheppo 28961ae08745Sheppo return (0); 28971ae08745Sheppo } 28981ae08745Sheppo 28991ae08745Sheppo /* 29001ae08745Sheppo * Unregister channel callback 29011ae08745Sheppo */ 29021ae08745Sheppo int 29031ae08745Sheppo ldc_unreg_callback(ldc_handle_t handle) 29041ae08745Sheppo { 29051ae08745Sheppo ldc_chan_t *ldcp; 29061ae08745Sheppo 29071ae08745Sheppo if (handle == NULL) { 29081ae08745Sheppo DWARN(DBG_ALL_LDCS, 29091ae08745Sheppo "ldc_unreg_callback: invalid channel handle\n"); 29101ae08745Sheppo return (EINVAL); 29111ae08745Sheppo } 29121ae08745Sheppo ldcp = (ldc_chan_t *)handle; 29131ae08745Sheppo 29141ae08745Sheppo mutex_enter(&ldcp->lock); 29151ae08745Sheppo 29161ae08745Sheppo if (ldcp->cb == NULL) { 29171ae08745Sheppo DWARN(ldcp->id, 29181ae08745Sheppo "ldc_unreg_callback: (0x%llx) no callback exists\n", 29191ae08745Sheppo ldcp->id); 29201ae08745Sheppo mutex_exit(&ldcp->lock); 29211ae08745Sheppo return (EIO); 29221ae08745Sheppo } 29231ae08745Sheppo if (ldcp->cb_inprogress) { 29241ae08745Sheppo DWARN(ldcp->id, 29251ae08745Sheppo "ldc_unreg_callback: (0x%llx) callback active\n", 29261ae08745Sheppo ldcp->id); 29271ae08745Sheppo mutex_exit(&ldcp->lock); 29281ae08745Sheppo return (EWOULDBLOCK); 29291ae08745Sheppo } 29301ae08745Sheppo 29311ae08745Sheppo ldcp->cb = NULL; 29321ae08745Sheppo ldcp->cb_arg = NULL; 29331ae08745Sheppo ldcp->cb_enabled = B_FALSE; 29341ae08745Sheppo 29351ae08745Sheppo D1(ldcp->id, 29361ae08745Sheppo "ldc_unreg_callback: (0x%llx) unregistered callback for channel\n", 29371ae08745Sheppo ldcp->id); 29381ae08745Sheppo 29391ae08745Sheppo mutex_exit(&ldcp->lock); 29401ae08745Sheppo 29411ae08745Sheppo return (0); 29421ae08745Sheppo } 29431ae08745Sheppo 29441ae08745Sheppo 29451ae08745Sheppo /* 29461ae08745Sheppo * Bring a channel up by initiating a handshake with the peer 29471ae08745Sheppo * This call is asynchronous. It will complete at a later point 29481ae08745Sheppo * in time when the peer responds back with an RTR. 29491ae08745Sheppo */ 29501ae08745Sheppo int 29511ae08745Sheppo ldc_up(ldc_handle_t handle) 29521ae08745Sheppo { 29531ae08745Sheppo int rv; 29541ae08745Sheppo ldc_chan_t *ldcp; 29551ae08745Sheppo ldc_msg_t *ldcmsg; 295657e6a936Ssb155480 uint64_t tx_tail, tstate, link_state; 29571ae08745Sheppo 29581ae08745Sheppo if (handle == NULL) { 29591ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_up: invalid channel handle\n"); 29601ae08745Sheppo return (EINVAL); 29611ae08745Sheppo } 29621ae08745Sheppo ldcp = (ldc_chan_t *)handle; 29631ae08745Sheppo 29641ae08745Sheppo mutex_enter(&ldcp->lock); 29651ae08745Sheppo 29663af08d82Slm66018 D1(ldcp->id, "ldc_up: (0x%llx) doing channel UP\n", ldcp->id); 29673af08d82Slm66018 29683af08d82Slm66018 /* clear the reset state */ 29693af08d82Slm66018 tstate = ldcp->tstate; 29703af08d82Slm66018 ldcp->tstate &= ~TS_IN_RESET; 29713af08d82Slm66018 29721ae08745Sheppo if (ldcp->tstate == TS_UP) { 29733af08d82Slm66018 DWARN(ldcp->id, 29741ae08745Sheppo "ldc_up: (0x%llx) channel is already in UP state\n", 29751ae08745Sheppo ldcp->id); 29763af08d82Slm66018 29773af08d82Slm66018 /* mark channel as up */ 29783af08d82Slm66018 ldcp->status = LDC_UP; 29793af08d82Slm66018 29803af08d82Slm66018 /* 29813af08d82Slm66018 * if channel was in reset state and there was 29823af08d82Slm66018 * pending data clear interrupt state. this will 29833af08d82Slm66018 * trigger an interrupt, causing the RX handler to 29843af08d82Slm66018 * to invoke the client's callback 29853af08d82Slm66018 */ 29863af08d82Slm66018 if ((tstate & TS_IN_RESET) && 29873af08d82Slm66018 ldcp->rx_intr_state == LDC_INTR_PEND) { 2988cb112a14Slm66018 D1(ldcp->id, 29893af08d82Slm66018 "ldc_up: (0x%llx) channel has pending data, " 29903af08d82Slm66018 "clearing interrupt\n", ldcp->id); 29913af08d82Slm66018 i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 29923af08d82Slm66018 } 29933af08d82Slm66018 29941ae08745Sheppo mutex_exit(&ldcp->lock); 29951ae08745Sheppo return (0); 29961ae08745Sheppo } 29971ae08745Sheppo 29981ae08745Sheppo /* if the channel is in RAW mode - mark it as UP, if READY */ 29991ae08745Sheppo if (ldcp->mode == LDC_MODE_RAW && ldcp->tstate >= TS_READY) { 30001ae08745Sheppo ldcp->tstate = TS_UP; 30011ae08745Sheppo mutex_exit(&ldcp->lock); 30021ae08745Sheppo return (0); 30031ae08745Sheppo } 30041ae08745Sheppo 30051ae08745Sheppo /* Don't start another handshake if there is one in progress */ 30061ae08745Sheppo if (ldcp->hstate) { 30073af08d82Slm66018 D1(ldcp->id, 30081ae08745Sheppo "ldc_up: (0x%llx) channel handshake in progress\n", 30091ae08745Sheppo ldcp->id); 30101ae08745Sheppo mutex_exit(&ldcp->lock); 30111ae08745Sheppo return (0); 30121ae08745Sheppo } 30131ae08745Sheppo 3014d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 3015d10e4ef2Snarayan 301657e6a936Ssb155480 /* save current link state */ 301757e6a936Ssb155480 link_state = ldcp->link_state; 301857e6a936Ssb155480 30191ae08745Sheppo /* get the current tail for the LDC msg */ 30201ae08745Sheppo rv = i_ldc_get_tx_tail(ldcp, &tx_tail); 30211ae08745Sheppo if (rv) { 3022cb112a14Slm66018 D1(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake\n", 30231ae08745Sheppo ldcp->id); 3024d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 30251ae08745Sheppo mutex_exit(&ldcp->lock); 30261ae08745Sheppo return (ECONNREFUSED); 30271ae08745Sheppo } 30281ae08745Sheppo 302957e6a936Ssb155480 /* 303057e6a936Ssb155480 * If i_ldc_get_tx_tail() changed link_state to either RESET or UP, 303157e6a936Ssb155480 * from a previous state of DOWN, then mark the channel as 303257e6a936Ssb155480 * being ready for handshake. 303357e6a936Ssb155480 */ 303457e6a936Ssb155480 if ((link_state == LDC_CHANNEL_DOWN) && 303557e6a936Ssb155480 (link_state != ldcp->link_state)) { 303657e6a936Ssb155480 303757e6a936Ssb155480 ASSERT((ldcp->link_state == LDC_CHANNEL_RESET) || 303857e6a936Ssb155480 (ldcp->link_state == LDC_CHANNEL_UP)); 303957e6a936Ssb155480 304057e6a936Ssb155480 if (ldcp->mode == LDC_MODE_RAW) { 304157e6a936Ssb155480 ldcp->status = LDC_UP; 304257e6a936Ssb155480 ldcp->tstate = TS_UP; 304357e6a936Ssb155480 mutex_exit(&ldcp->tx_lock); 304457e6a936Ssb155480 mutex_exit(&ldcp->lock); 304557e6a936Ssb155480 return (0); 304657e6a936Ssb155480 } else { 304757e6a936Ssb155480 ldcp->status = LDC_READY; 304857e6a936Ssb155480 ldcp->tstate |= TS_LINK_READY; 304957e6a936Ssb155480 } 305057e6a936Ssb155480 305157e6a936Ssb155480 } 305257e6a936Ssb155480 30531ae08745Sheppo ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 30541ae08745Sheppo ZERO_PKT(ldcmsg); 30551ae08745Sheppo 30561ae08745Sheppo ldcmsg->type = LDC_CTRL; 30571ae08745Sheppo ldcmsg->stype = LDC_INFO; 30581ae08745Sheppo ldcmsg->ctrl = LDC_VER; 30591ae08745Sheppo ldcp->next_vidx = 0; 30601ae08745Sheppo bcopy(&ldc_versions[0], ldcmsg->udata, sizeof (ldc_versions[0])); 30611ae08745Sheppo 30621ae08745Sheppo DUMP_LDC_PKT(ldcp, "ldc_up snd ver", (uint64_t)ldcmsg); 30631ae08745Sheppo 30641ae08745Sheppo /* initiate the send by calling into HV and set the new tail */ 30651ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) % 30661ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 30671ae08745Sheppo 30681ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 30691ae08745Sheppo if (rv) { 30701ae08745Sheppo DWARN(ldcp->id, 30711ae08745Sheppo "ldc_up: (0x%llx) cannot initiate handshake rv=%d\n", 30721ae08745Sheppo ldcp->id, rv); 3073d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 30741ae08745Sheppo mutex_exit(&ldcp->lock); 30751ae08745Sheppo return (rv); 30761ae08745Sheppo } 30771ae08745Sheppo 30780a55fbb7Slm66018 ldcp->hstate |= TS_SENT_VER; 30791ae08745Sheppo ldcp->tx_tail = tx_tail; 30801ae08745Sheppo D1(ldcp->id, "ldc_up: (0x%llx) channel up initiated\n", ldcp->id); 30811ae08745Sheppo 3082d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 30831ae08745Sheppo mutex_exit(&ldcp->lock); 30841ae08745Sheppo 30851ae08745Sheppo return (rv); 30861ae08745Sheppo } 30871ae08745Sheppo 30881ae08745Sheppo 30891ae08745Sheppo /* 3090e1ebb9ecSlm66018 * Bring a channel down by resetting its state and queues 30911ae08745Sheppo */ 30921ae08745Sheppo int 3093e1ebb9ecSlm66018 ldc_down(ldc_handle_t handle) 30941ae08745Sheppo { 30951ae08745Sheppo ldc_chan_t *ldcp; 30961ae08745Sheppo 30971ae08745Sheppo if (handle == NULL) { 3098e1ebb9ecSlm66018 DWARN(DBG_ALL_LDCS, "ldc_down: invalid channel handle\n"); 30991ae08745Sheppo return (EINVAL); 31001ae08745Sheppo } 31011ae08745Sheppo ldcp = (ldc_chan_t *)handle; 31021ae08745Sheppo mutex_enter(&ldcp->lock); 3103d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 31043af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 3105d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 31061ae08745Sheppo mutex_exit(&ldcp->lock); 31071ae08745Sheppo 31081ae08745Sheppo return (0); 31091ae08745Sheppo } 31101ae08745Sheppo 31111ae08745Sheppo /* 31121ae08745Sheppo * Get the current channel status 31131ae08745Sheppo */ 31141ae08745Sheppo int 31151ae08745Sheppo ldc_status(ldc_handle_t handle, ldc_status_t *status) 31161ae08745Sheppo { 31171ae08745Sheppo ldc_chan_t *ldcp; 31181ae08745Sheppo 31191ae08745Sheppo if (handle == NULL || status == NULL) { 31201ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_status: invalid argument\n"); 31211ae08745Sheppo return (EINVAL); 31221ae08745Sheppo } 31231ae08745Sheppo ldcp = (ldc_chan_t *)handle; 31241ae08745Sheppo 31251ae08745Sheppo *status = ((ldc_chan_t *)handle)->status; 31261ae08745Sheppo 3127cb112a14Slm66018 D1(ldcp->id, 31281ae08745Sheppo "ldc_status: (0x%llx) returned status %d\n", ldcp->id, *status); 31291ae08745Sheppo return (0); 31301ae08745Sheppo } 31311ae08745Sheppo 31321ae08745Sheppo 31331ae08745Sheppo /* 31341ae08745Sheppo * Set the channel's callback mode - enable/disable callbacks 31351ae08745Sheppo */ 31361ae08745Sheppo int 31371ae08745Sheppo ldc_set_cb_mode(ldc_handle_t handle, ldc_cb_mode_t cmode) 31381ae08745Sheppo { 31391ae08745Sheppo ldc_chan_t *ldcp; 31401ae08745Sheppo 31411ae08745Sheppo if (handle == NULL) { 31421ae08745Sheppo DWARN(DBG_ALL_LDCS, 31431ae08745Sheppo "ldc_set_intr_mode: invalid channel handle\n"); 31441ae08745Sheppo return (EINVAL); 31451ae08745Sheppo } 31461ae08745Sheppo ldcp = (ldc_chan_t *)handle; 31471ae08745Sheppo 31481ae08745Sheppo /* 31491ae08745Sheppo * Record no callbacks should be invoked 31501ae08745Sheppo */ 31511ae08745Sheppo mutex_enter(&ldcp->lock); 31521ae08745Sheppo 31531ae08745Sheppo switch (cmode) { 31541ae08745Sheppo case LDC_CB_DISABLE: 31551ae08745Sheppo if (!ldcp->cb_enabled) { 31561ae08745Sheppo DWARN(ldcp->id, 31571ae08745Sheppo "ldc_set_cb_mode: (0x%llx) callbacks disabled\n", 31581ae08745Sheppo ldcp->id); 31591ae08745Sheppo break; 31601ae08745Sheppo } 31611ae08745Sheppo ldcp->cb_enabled = B_FALSE; 31621ae08745Sheppo 31631ae08745Sheppo D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) disabled callbacks\n", 31641ae08745Sheppo ldcp->id); 31651ae08745Sheppo break; 31661ae08745Sheppo 31671ae08745Sheppo case LDC_CB_ENABLE: 31681ae08745Sheppo if (ldcp->cb_enabled) { 31691ae08745Sheppo DWARN(ldcp->id, 31701ae08745Sheppo "ldc_set_cb_mode: (0x%llx) callbacks enabled\n", 31711ae08745Sheppo ldcp->id); 31721ae08745Sheppo break; 31731ae08745Sheppo } 31741ae08745Sheppo ldcp->cb_enabled = B_TRUE; 31751ae08745Sheppo 31761ae08745Sheppo D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) enabled callbacks\n", 31771ae08745Sheppo ldcp->id); 31781ae08745Sheppo break; 31791ae08745Sheppo } 31801ae08745Sheppo 31811ae08745Sheppo mutex_exit(&ldcp->lock); 31821ae08745Sheppo 31831ae08745Sheppo return (0); 31841ae08745Sheppo } 31851ae08745Sheppo 31861ae08745Sheppo /* 31871ae08745Sheppo * Check to see if there are packets on the incoming queue 3188e1ebb9ecSlm66018 * Will return hasdata = B_FALSE if there are no packets 31891ae08745Sheppo */ 31901ae08745Sheppo int 3191e1ebb9ecSlm66018 ldc_chkq(ldc_handle_t handle, boolean_t *hasdata) 31921ae08745Sheppo { 31931ae08745Sheppo int rv; 31941ae08745Sheppo uint64_t rx_head, rx_tail; 31951ae08745Sheppo ldc_chan_t *ldcp; 31961ae08745Sheppo 31971ae08745Sheppo if (handle == NULL) { 31981ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_chkq: invalid channel handle\n"); 31991ae08745Sheppo return (EINVAL); 32001ae08745Sheppo } 32011ae08745Sheppo ldcp = (ldc_chan_t *)handle; 32021ae08745Sheppo 3203e1ebb9ecSlm66018 *hasdata = B_FALSE; 32041ae08745Sheppo 32051ae08745Sheppo mutex_enter(&ldcp->lock); 32061ae08745Sheppo 32071ae08745Sheppo if (ldcp->tstate != TS_UP) { 32081ae08745Sheppo D1(ldcp->id, 32091ae08745Sheppo "ldc_chkq: (0x%llx) channel is not up\n", ldcp->id); 32101ae08745Sheppo mutex_exit(&ldcp->lock); 32111ae08745Sheppo return (ECONNRESET); 32121ae08745Sheppo } 32131ae08745Sheppo 32141ae08745Sheppo /* Read packet(s) from the queue */ 32151ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 32161ae08745Sheppo &ldcp->link_state); 32171ae08745Sheppo if (rv != 0) { 32181ae08745Sheppo cmn_err(CE_WARN, 32191ae08745Sheppo "ldc_chkq: (0x%lx) unable to read queue ptrs", ldcp->id); 32201ae08745Sheppo mutex_exit(&ldcp->lock); 32211ae08745Sheppo return (EIO); 32221ae08745Sheppo } 32231ae08745Sheppo /* reset the channel state if the channel went down */ 32241ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_DOWN || 32251ae08745Sheppo ldcp->link_state == LDC_CHANNEL_RESET) { 3226d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 32273af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3228d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 32291ae08745Sheppo mutex_exit(&ldcp->lock); 32301ae08745Sheppo return (ECONNRESET); 32311ae08745Sheppo } 32321ae08745Sheppo 32334bac2208Snarayan if ((rx_head != rx_tail) || 32344bac2208Snarayan (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_remains > 0)) { 32354bac2208Snarayan D1(ldcp->id, 32364bac2208Snarayan "ldc_chkq: (0x%llx) queue has pkt(s) or buffered data\n", 32374bac2208Snarayan ldcp->id); 3238e1ebb9ecSlm66018 *hasdata = B_TRUE; 32391ae08745Sheppo } 32401ae08745Sheppo 32411ae08745Sheppo mutex_exit(&ldcp->lock); 32421ae08745Sheppo 32431ae08745Sheppo return (0); 32441ae08745Sheppo } 32451ae08745Sheppo 32461ae08745Sheppo 32471ae08745Sheppo /* 32481ae08745Sheppo * Read 'size' amount of bytes or less. If incoming buffer 32491ae08745Sheppo * is more than 'size', ENOBUFS is returned. 32501ae08745Sheppo * 32511ae08745Sheppo * On return, size contains the number of bytes read. 32521ae08745Sheppo */ 32531ae08745Sheppo int 32541ae08745Sheppo ldc_read(ldc_handle_t handle, caddr_t bufp, size_t *sizep) 32551ae08745Sheppo { 32561ae08745Sheppo ldc_chan_t *ldcp; 32571ae08745Sheppo uint64_t rx_head = 0, rx_tail = 0; 32581ae08745Sheppo int rv = 0, exit_val; 32591ae08745Sheppo 32601ae08745Sheppo if (handle == NULL) { 32611ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_read: invalid channel handle\n"); 32621ae08745Sheppo return (EINVAL); 32631ae08745Sheppo } 32641ae08745Sheppo 32651ae08745Sheppo ldcp = (ldc_chan_t *)handle; 32661ae08745Sheppo 32671ae08745Sheppo /* channel lock */ 32681ae08745Sheppo mutex_enter(&ldcp->lock); 32691ae08745Sheppo 32701ae08745Sheppo if (ldcp->tstate != TS_UP) { 32711ae08745Sheppo DWARN(ldcp->id, 32721ae08745Sheppo "ldc_read: (0x%llx) channel is not in UP state\n", 32731ae08745Sheppo ldcp->id); 32741ae08745Sheppo exit_val = ECONNRESET; 32751ae08745Sheppo } else { 32761ae08745Sheppo exit_val = ldcp->read_p(ldcp, bufp, sizep); 32771ae08745Sheppo } 32781ae08745Sheppo 32791ae08745Sheppo /* 32801ae08745Sheppo * if queue has been drained - clear interrupt 32811ae08745Sheppo */ 32821ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 32831ae08745Sheppo &ldcp->link_state); 3284cb112a14Slm66018 if (rv != 0) { 3285cb112a14Slm66018 cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs", 3286cb112a14Slm66018 ldcp->id); 3287cb112a14Slm66018 mutex_enter(&ldcp->tx_lock); 3288cb112a14Slm66018 i_ldc_reset(ldcp, B_TRUE); 3289cb112a14Slm66018 mutex_exit(&ldcp->tx_lock); 3290bd8f0338Snarayan mutex_exit(&ldcp->lock); 3291cb112a14Slm66018 return (ECONNRESET); 3292cb112a14Slm66018 } 32933af08d82Slm66018 32943af08d82Slm66018 if (exit_val == 0) { 32953af08d82Slm66018 if (ldcp->link_state == LDC_CHANNEL_DOWN || 32963af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_RESET) { 32973af08d82Slm66018 mutex_enter(&ldcp->tx_lock); 32983af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 32993af08d82Slm66018 exit_val = ECONNRESET; 33003af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 33013af08d82Slm66018 } 33023af08d82Slm66018 if ((rv == 0) && 33033af08d82Slm66018 (ldcp->rx_intr_state == LDC_INTR_PEND) && 33043af08d82Slm66018 (rx_head == rx_tail)) { 33051ae08745Sheppo i_ldc_clear_intr(ldcp, CNEX_RX_INTR); 33061ae08745Sheppo } 33073af08d82Slm66018 } 33081ae08745Sheppo 33091ae08745Sheppo mutex_exit(&ldcp->lock); 33101ae08745Sheppo return (exit_val); 33111ae08745Sheppo } 33121ae08745Sheppo 33131ae08745Sheppo /* 33141ae08745Sheppo * Basic raw mondo read - 33151ae08745Sheppo * no interpretation of mondo contents at all. 33161ae08745Sheppo * 33171ae08745Sheppo * Enter and exit with ldcp->lock held by caller 33181ae08745Sheppo */ 33191ae08745Sheppo static int 33201ae08745Sheppo i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) 33211ae08745Sheppo { 33221ae08745Sheppo uint64_t q_size_mask; 33231ae08745Sheppo ldc_msg_t *msgp; 33241ae08745Sheppo uint8_t *msgbufp; 33251ae08745Sheppo int rv = 0, space; 33261ae08745Sheppo uint64_t rx_head, rx_tail; 33271ae08745Sheppo 33281ae08745Sheppo space = *sizep; 33291ae08745Sheppo 33301ae08745Sheppo if (space < LDC_PAYLOAD_SIZE_RAW) 33311ae08745Sheppo return (ENOBUFS); 33321ae08745Sheppo 33331ae08745Sheppo ASSERT(mutex_owned(&ldcp->lock)); 33341ae08745Sheppo 33351ae08745Sheppo /* compute mask for increment */ 33361ae08745Sheppo q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT; 33371ae08745Sheppo 33381ae08745Sheppo /* 33391ae08745Sheppo * Read packet(s) from the queue 33401ae08745Sheppo */ 33411ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail, 33421ae08745Sheppo &ldcp->link_state); 33431ae08745Sheppo if (rv != 0) { 33441ae08745Sheppo cmn_err(CE_WARN, 33451ae08745Sheppo "ldc_read_raw: (0x%lx) unable to read queue ptrs", 33461ae08745Sheppo ldcp->id); 33471ae08745Sheppo return (EIO); 33481ae08745Sheppo } 33491ae08745Sheppo D1(ldcp->id, "ldc_read_raw: (0x%llx) rxh=0x%llx," 33501ae08745Sheppo " rxt=0x%llx, st=0x%llx\n", 33511ae08745Sheppo ldcp->id, rx_head, rx_tail, ldcp->link_state); 33521ae08745Sheppo 33531ae08745Sheppo /* reset the channel state if the channel went down */ 33543af08d82Slm66018 if (ldcp->link_state == LDC_CHANNEL_DOWN || 33553af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_RESET) { 3356d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 33573af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3358d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 33591ae08745Sheppo return (ECONNRESET); 33601ae08745Sheppo } 33611ae08745Sheppo 33621ae08745Sheppo /* 33631ae08745Sheppo * Check for empty queue 33641ae08745Sheppo */ 33651ae08745Sheppo if (rx_head == rx_tail) { 33661ae08745Sheppo *sizep = 0; 33671ae08745Sheppo return (0); 33681ae08745Sheppo } 33691ae08745Sheppo 33701ae08745Sheppo /* get the message */ 33711ae08745Sheppo msgp = (ldc_msg_t *)(ldcp->rx_q_va + rx_head); 33721ae08745Sheppo 33731ae08745Sheppo /* if channel is in RAW mode, copy data and return */ 33741ae08745Sheppo msgbufp = (uint8_t *)&(msgp->raw[0]); 33751ae08745Sheppo 33761ae08745Sheppo bcopy(msgbufp, target_bufp, LDC_PAYLOAD_SIZE_RAW); 33771ae08745Sheppo 33781ae08745Sheppo DUMP_PAYLOAD(ldcp->id, msgbufp); 33791ae08745Sheppo 33801ae08745Sheppo *sizep = LDC_PAYLOAD_SIZE_RAW; 33811ae08745Sheppo 33821ae08745Sheppo rx_head = (rx_head + LDC_PACKET_SIZE) & q_size_mask; 33830a55fbb7Slm66018 rv = i_ldc_set_rx_head(ldcp, rx_head); 33841ae08745Sheppo 33851ae08745Sheppo return (rv); 33861ae08745Sheppo } 33871ae08745Sheppo 33881ae08745Sheppo /* 33891ae08745Sheppo * Process LDC mondos to build larger packets 33901ae08745Sheppo * with either un-reliable or reliable delivery. 33911ae08745Sheppo * 33921ae08745Sheppo * Enter and exit with ldcp->lock held by caller 33931ae08745Sheppo */ 33941ae08745Sheppo static int 33951ae08745Sheppo i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) 33961ae08745Sheppo { 33971ae08745Sheppo int rv = 0; 33981ae08745Sheppo uint64_t rx_head = 0, rx_tail = 0; 33991ae08745Sheppo uint64_t curr_head = 0; 34001ae08745Sheppo ldc_msg_t *msg; 34011ae08745Sheppo caddr_t target; 34021ae08745Sheppo size_t len = 0, bytes_read = 0; 34030a55fbb7Slm66018 int retries = 0; 34041ae08745Sheppo uint64_t q_size_mask; 3405d10e4ef2Snarayan uint64_t first_fragment = 0; 34061ae08745Sheppo 34071ae08745Sheppo target = target_bufp; 34081ae08745Sheppo 34091ae08745Sheppo ASSERT(mutex_owned(&ldcp->lock)); 34101ae08745Sheppo 34113af08d82Slm66018 /* check if the buffer and size are valid */ 34123af08d82Slm66018 if (target_bufp == NULL || *sizep == 0) { 34133af08d82Slm66018 DWARN(ldcp->id, "ldc_read: (0x%llx) invalid buffer/size\n", 34143af08d82Slm66018 ldcp->id); 34153af08d82Slm66018 return (EINVAL); 34163af08d82Slm66018 } 34173af08d82Slm66018 34181ae08745Sheppo /* compute mask for increment */ 34191ae08745Sheppo q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT; 34201ae08745Sheppo 34211ae08745Sheppo /* 34221ae08745Sheppo * Read packet(s) from the queue 34231ae08745Sheppo */ 34241ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, &curr_head, &rx_tail, 34251ae08745Sheppo &ldcp->link_state); 34261ae08745Sheppo if (rv != 0) { 34273af08d82Slm66018 cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs", 34281ae08745Sheppo ldcp->id); 34293af08d82Slm66018 mutex_enter(&ldcp->tx_lock); 34303af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 34313af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 34323af08d82Slm66018 return (ECONNRESET); 34331ae08745Sheppo } 34341ae08745Sheppo D1(ldcp->id, "ldc_read: (0x%llx) chd=0x%llx, tl=0x%llx, st=0x%llx\n", 34351ae08745Sheppo ldcp->id, curr_head, rx_tail, ldcp->link_state); 34361ae08745Sheppo 34371ae08745Sheppo /* reset the channel state if the channel went down */ 34383af08d82Slm66018 if (ldcp->link_state != LDC_CHANNEL_UP) 34393af08d82Slm66018 goto channel_is_reset; 34401ae08745Sheppo 34411ae08745Sheppo for (;;) { 34421ae08745Sheppo 34431ae08745Sheppo if (curr_head == rx_tail) { 34441ae08745Sheppo rv = hv_ldc_rx_get_state(ldcp->id, 34451ae08745Sheppo &rx_head, &rx_tail, &ldcp->link_state); 34461ae08745Sheppo if (rv != 0) { 34471ae08745Sheppo cmn_err(CE_WARN, 34481ae08745Sheppo "ldc_read: (0x%lx) cannot read queue ptrs", 34491ae08745Sheppo ldcp->id); 3450d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 34513af08d82Slm66018 i_ldc_reset(ldcp, B_TRUE); 3452d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 34531ae08745Sheppo return (ECONNRESET); 34541ae08745Sheppo } 34553af08d82Slm66018 if (ldcp->link_state != LDC_CHANNEL_UP) 34563af08d82Slm66018 goto channel_is_reset; 34571ae08745Sheppo 34581ae08745Sheppo if (curr_head == rx_tail) { 34591ae08745Sheppo 34601ae08745Sheppo /* If in the middle of a fragmented xfer */ 3461d10e4ef2Snarayan if (first_fragment != 0) { 34620a55fbb7Slm66018 34630a55fbb7Slm66018 /* wait for ldc_delay usecs */ 34640a55fbb7Slm66018 drv_usecwait(ldc_delay); 34650a55fbb7Slm66018 34660a55fbb7Slm66018 if (++retries < ldc_max_retries) 34671ae08745Sheppo continue; 34680a55fbb7Slm66018 34691ae08745Sheppo *sizep = 0; 3470d10e4ef2Snarayan ldcp->last_msg_rcd = first_fragment - 1; 34713af08d82Slm66018 DWARN(DBG_ALL_LDCS, "ldc_read: " 347222f747efSnarayan "(0x%llx) read timeout", ldcp->id); 34733af08d82Slm66018 return (EAGAIN); 34741ae08745Sheppo } 34751ae08745Sheppo *sizep = 0; 34761ae08745Sheppo break; 34771ae08745Sheppo } 34783af08d82Slm66018 } 34790a55fbb7Slm66018 retries = 0; 34801ae08745Sheppo 34811ae08745Sheppo D2(ldcp->id, 34821ae08745Sheppo "ldc_read: (0x%llx) chd=0x%llx, rxhd=0x%llx, rxtl=0x%llx\n", 34831ae08745Sheppo ldcp->id, curr_head, rx_head, rx_tail); 34841ae08745Sheppo 34851ae08745Sheppo /* get the message */ 34861ae08745Sheppo msg = (ldc_msg_t *)(ldcp->rx_q_va + curr_head); 34871ae08745Sheppo 34881ae08745Sheppo DUMP_LDC_PKT(ldcp, "ldc_read received pkt", 34891ae08745Sheppo ldcp->rx_q_va + curr_head); 34901ae08745Sheppo 34911ae08745Sheppo /* Check the message ID for the message received */ 34921ae08745Sheppo if ((rv = i_ldc_check_seqid(ldcp, msg)) != 0) { 34931ae08745Sheppo 34941ae08745Sheppo DWARN(ldcp->id, "ldc_read: (0x%llx) seqid error, " 34951ae08745Sheppo "q_ptrs=0x%lx,0x%lx", ldcp->id, rx_head, rx_tail); 34961ae08745Sheppo 34970a55fbb7Slm66018 /* throw away data */ 34980a55fbb7Slm66018 bytes_read = 0; 34990a55fbb7Slm66018 35001ae08745Sheppo /* Reset last_msg_rcd to start of message */ 3501d10e4ef2Snarayan if (first_fragment != 0) { 3502d10e4ef2Snarayan ldcp->last_msg_rcd = first_fragment - 1; 3503d10e4ef2Snarayan first_fragment = 0; 35041ae08745Sheppo } 35051ae08745Sheppo /* 35061ae08745Sheppo * Send a NACK -- invalid seqid 35071ae08745Sheppo * get the current tail for the response 35081ae08745Sheppo */ 35091ae08745Sheppo rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK, 35101ae08745Sheppo (msg->ctrl & LDC_CTRL_MASK)); 35111ae08745Sheppo if (rv) { 35121ae08745Sheppo cmn_err(CE_NOTE, 35131ae08745Sheppo "ldc_read: (0x%lx) err sending " 35141ae08745Sheppo "NACK msg\n", ldcp->id); 3515d10e4ef2Snarayan 3516d10e4ef2Snarayan /* if cannot send NACK - reset channel */ 3517d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 35183af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3519d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 3520d10e4ef2Snarayan rv = ECONNRESET; 3521d10e4ef2Snarayan break; 35221ae08745Sheppo } 35231ae08745Sheppo 35241ae08745Sheppo /* purge receive queue */ 35250a55fbb7Slm66018 rv = i_ldc_set_rx_head(ldcp, rx_tail); 35261ae08745Sheppo 35271ae08745Sheppo break; 35281ae08745Sheppo } 35291ae08745Sheppo 35301ae08745Sheppo /* 35311ae08745Sheppo * Process any messages of type CTRL messages 3532e1ebb9ecSlm66018 * Future implementations should try to pass these 3533e1ebb9ecSlm66018 * to LDC link by resetting the intr state. 35341ae08745Sheppo * 35351ae08745Sheppo * NOTE: not done as a switch() as type can be both ctrl+data 35361ae08745Sheppo */ 35371ae08745Sheppo if (msg->type & LDC_CTRL) { 35381ae08745Sheppo if (rv = i_ldc_ctrlmsg(ldcp, msg)) { 35391ae08745Sheppo if (rv == EAGAIN) 35401ae08745Sheppo continue; 35410a55fbb7Slm66018 rv = i_ldc_set_rx_head(ldcp, rx_tail); 35421ae08745Sheppo *sizep = 0; 35431ae08745Sheppo bytes_read = 0; 35441ae08745Sheppo break; 35451ae08745Sheppo } 35461ae08745Sheppo } 35471ae08745Sheppo 35481ae08745Sheppo /* process data ACKs */ 35491ae08745Sheppo if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) { 3550d10e4ef2Snarayan if (rv = i_ldc_process_data_ACK(ldcp, msg)) { 3551d10e4ef2Snarayan *sizep = 0; 3552d10e4ef2Snarayan bytes_read = 0; 3553d10e4ef2Snarayan break; 3554d10e4ef2Snarayan } 35551ae08745Sheppo } 35561ae08745Sheppo 355783d3bc6fSnarayan /* process data NACKs */ 355883d3bc6fSnarayan if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) { 355983d3bc6fSnarayan DWARN(ldcp->id, 356083d3bc6fSnarayan "ldc_read: (0x%llx) received DATA/NACK", ldcp->id); 356183d3bc6fSnarayan mutex_enter(&ldcp->tx_lock); 356283d3bc6fSnarayan i_ldc_reset(ldcp, B_TRUE); 356383d3bc6fSnarayan mutex_exit(&ldcp->tx_lock); 356483d3bc6fSnarayan return (ECONNRESET); 356583d3bc6fSnarayan } 356683d3bc6fSnarayan 35671ae08745Sheppo /* process data messages */ 35681ae08745Sheppo if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) { 35691ae08745Sheppo 35701ae08745Sheppo uint8_t *msgbuf = (uint8_t *)( 35711ae08745Sheppo (ldcp->mode == LDC_MODE_RELIABLE || 357222f747efSnarayan ldcp->mode == LDC_MODE_STREAM) ? 357322f747efSnarayan msg->rdata : msg->udata); 35741ae08745Sheppo 35751ae08745Sheppo D2(ldcp->id, 35761ae08745Sheppo "ldc_read: (0x%llx) received data msg\n", ldcp->id); 35771ae08745Sheppo 35781ae08745Sheppo /* get the packet length */ 35791ae08745Sheppo len = (msg->env & LDC_LEN_MASK); 35801ae08745Sheppo 35811ae08745Sheppo /* 35821ae08745Sheppo * FUTURE OPTIMIZATION: 35831ae08745Sheppo * dont need to set q head for every 35841ae08745Sheppo * packet we read just need to do this when 35851ae08745Sheppo * we are done or need to wait for more 35861ae08745Sheppo * mondos to make a full packet - this is 35871ae08745Sheppo * currently expensive. 35881ae08745Sheppo */ 35891ae08745Sheppo 3590d10e4ef2Snarayan if (first_fragment == 0) { 35911ae08745Sheppo 35921ae08745Sheppo /* 35931ae08745Sheppo * first packets should always have the start 35941ae08745Sheppo * bit set (even for a single packet). If not 35951ae08745Sheppo * throw away the packet 35961ae08745Sheppo */ 35971ae08745Sheppo if (!(msg->env & LDC_FRAG_START)) { 35981ae08745Sheppo 35991ae08745Sheppo DWARN(DBG_ALL_LDCS, 36001ae08745Sheppo "ldc_read: (0x%llx) not start - " 36011ae08745Sheppo "frag=%x\n", ldcp->id, 36021ae08745Sheppo (msg->env) & LDC_FRAG_MASK); 36031ae08745Sheppo 36041ae08745Sheppo /* toss pkt, inc head, cont reading */ 36051ae08745Sheppo bytes_read = 0; 36061ae08745Sheppo target = target_bufp; 36071ae08745Sheppo curr_head = 36081ae08745Sheppo (curr_head + LDC_PACKET_SIZE) 36091ae08745Sheppo & q_size_mask; 36101ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, 36111ae08745Sheppo curr_head)) 36121ae08745Sheppo break; 36131ae08745Sheppo 36141ae08745Sheppo continue; 36151ae08745Sheppo } 36161ae08745Sheppo 3617d10e4ef2Snarayan first_fragment = msg->seqid; 36181ae08745Sheppo } else { 36191ae08745Sheppo /* check to see if this is a pkt w/ START bit */ 36201ae08745Sheppo if (msg->env & LDC_FRAG_START) { 36211ae08745Sheppo DWARN(DBG_ALL_LDCS, 36221ae08745Sheppo "ldc_read:(0x%llx) unexpected pkt" 36231ae08745Sheppo " env=0x%x discarding %d bytes," 36241ae08745Sheppo " lastmsg=%d, currentmsg=%d\n", 36251ae08745Sheppo ldcp->id, msg->env&LDC_FRAG_MASK, 36261ae08745Sheppo bytes_read, ldcp->last_msg_rcd, 36271ae08745Sheppo msg->seqid); 36281ae08745Sheppo 36291ae08745Sheppo /* throw data we have read so far */ 36301ae08745Sheppo bytes_read = 0; 36311ae08745Sheppo target = target_bufp; 3632d10e4ef2Snarayan first_fragment = msg->seqid; 36331ae08745Sheppo 36341ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, 36351ae08745Sheppo curr_head)) 36361ae08745Sheppo break; 36371ae08745Sheppo } 36381ae08745Sheppo } 36391ae08745Sheppo 36401ae08745Sheppo /* copy (next) pkt into buffer */ 36411ae08745Sheppo if (len <= (*sizep - bytes_read)) { 36421ae08745Sheppo bcopy(msgbuf, target, len); 36431ae08745Sheppo target += len; 36441ae08745Sheppo bytes_read += len; 36451ae08745Sheppo } else { 36461ae08745Sheppo /* 36471ae08745Sheppo * there is not enough space in the buffer to 36481ae08745Sheppo * read this pkt. throw message away & continue 36491ae08745Sheppo * reading data from queue 36501ae08745Sheppo */ 36511ae08745Sheppo DWARN(DBG_ALL_LDCS, 36521ae08745Sheppo "ldc_read: (0x%llx) buffer too small, " 36531ae08745Sheppo "head=0x%lx, expect=%d, got=%d\n", ldcp->id, 36541ae08745Sheppo curr_head, *sizep, bytes_read+len); 36551ae08745Sheppo 3656d10e4ef2Snarayan first_fragment = 0; 36571ae08745Sheppo target = target_bufp; 36581ae08745Sheppo bytes_read = 0; 36591ae08745Sheppo 36601ae08745Sheppo /* throw away everything received so far */ 36611ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, curr_head)) 36621ae08745Sheppo break; 36631ae08745Sheppo 36641ae08745Sheppo /* continue reading remaining pkts */ 36651ae08745Sheppo continue; 36661ae08745Sheppo } 36671ae08745Sheppo } 36681ae08745Sheppo 36691ae08745Sheppo /* set the message id */ 36701ae08745Sheppo ldcp->last_msg_rcd = msg->seqid; 36711ae08745Sheppo 36721ae08745Sheppo /* move the head one position */ 36731ae08745Sheppo curr_head = (curr_head + LDC_PACKET_SIZE) & q_size_mask; 36741ae08745Sheppo 36751ae08745Sheppo if (msg->env & LDC_FRAG_STOP) { 36761ae08745Sheppo 36771ae08745Sheppo /* 36781ae08745Sheppo * All pkts that are part of this fragmented transfer 36791ae08745Sheppo * have been read or this was a single pkt read 36801ae08745Sheppo * or there was an error 36811ae08745Sheppo */ 36821ae08745Sheppo 36831ae08745Sheppo /* set the queue head */ 36841ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, curr_head)) 36851ae08745Sheppo bytes_read = 0; 36861ae08745Sheppo 36871ae08745Sheppo *sizep = bytes_read; 36881ae08745Sheppo 36891ae08745Sheppo break; 36901ae08745Sheppo } 36911ae08745Sheppo 3692*332608acSnarayan /* advance head if it is a CTRL packet or a DATA ACK packet */ 3693*332608acSnarayan if ((msg->type & LDC_CTRL) || 3694*332608acSnarayan ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK))) { 36951ae08745Sheppo 36961ae08745Sheppo /* set the queue head */ 36971ae08745Sheppo if (rv = i_ldc_set_rx_head(ldcp, curr_head)) { 36981ae08745Sheppo bytes_read = 0; 36991ae08745Sheppo break; 37001ae08745Sheppo } 37011ae08745Sheppo 37021ae08745Sheppo D2(ldcp->id, "ldc_read: (0x%llx) set ACK qhead 0x%llx", 37031ae08745Sheppo ldcp->id, curr_head); 37041ae08745Sheppo } 37051ae08745Sheppo 37061ae08745Sheppo } /* for (;;) */ 37071ae08745Sheppo 37081ae08745Sheppo 37091ae08745Sheppo /* 37101ae08745Sheppo * If useful data was read - Send msg ACK 37111ae08745Sheppo * OPTIMIZE: do not send ACK for all msgs - use some frequency 37121ae08745Sheppo */ 37131ae08745Sheppo if ((bytes_read > 0) && (ldcp->mode == LDC_MODE_RELIABLE || 37141ae08745Sheppo ldcp->mode == LDC_MODE_STREAM)) { 37151ae08745Sheppo 37161ae08745Sheppo rv = i_ldc_send_pkt(ldcp, LDC_DATA, LDC_ACK, 0); 37173af08d82Slm66018 if (rv && rv != EWOULDBLOCK) { 37181ae08745Sheppo cmn_err(CE_NOTE, 37191ae08745Sheppo "ldc_read: (0x%lx) cannot send ACK\n", ldcp->id); 3720d10e4ef2Snarayan 3721d10e4ef2Snarayan /* if cannot send ACK - reset channel */ 37223af08d82Slm66018 goto channel_is_reset; 37231ae08745Sheppo } 37241ae08745Sheppo } 37251ae08745Sheppo 37261ae08745Sheppo D2(ldcp->id, "ldc_read: (0x%llx) end size=%d", ldcp->id, *sizep); 37271ae08745Sheppo 37281ae08745Sheppo return (rv); 37293af08d82Slm66018 37303af08d82Slm66018 channel_is_reset: 37313af08d82Slm66018 mutex_enter(&ldcp->tx_lock); 37323af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 37333af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 37343af08d82Slm66018 return (ECONNRESET); 37351ae08745Sheppo } 37361ae08745Sheppo 37371ae08745Sheppo /* 37381ae08745Sheppo * Use underlying reliable packet mechanism to fetch 37391ae08745Sheppo * and buffer incoming packets so we can hand them back as 37401ae08745Sheppo * a basic byte stream. 37411ae08745Sheppo * 37421ae08745Sheppo * Enter and exit with ldcp->lock held by caller 37431ae08745Sheppo */ 37441ae08745Sheppo static int 37451ae08745Sheppo i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) 37461ae08745Sheppo { 37471ae08745Sheppo int rv; 37481ae08745Sheppo size_t size; 37491ae08745Sheppo 37501ae08745Sheppo ASSERT(mutex_owned(&ldcp->lock)); 37511ae08745Sheppo 37521ae08745Sheppo D2(ldcp->id, "i_ldc_read_stream: (0x%llx) buffer size=%d", 37531ae08745Sheppo ldcp->id, *sizep); 37541ae08745Sheppo 37551ae08745Sheppo if (ldcp->stream_remains == 0) { 37561ae08745Sheppo size = ldcp->mtu; 37571ae08745Sheppo rv = i_ldc_read_packet(ldcp, 37581ae08745Sheppo (caddr_t)ldcp->stream_bufferp, &size); 37591ae08745Sheppo D2(ldcp->id, "i_ldc_read_stream: read packet (0x%llx) size=%d", 37601ae08745Sheppo ldcp->id, size); 37611ae08745Sheppo 37621ae08745Sheppo if (rv != 0) 37631ae08745Sheppo return (rv); 37641ae08745Sheppo 37651ae08745Sheppo ldcp->stream_remains = size; 37661ae08745Sheppo ldcp->stream_offset = 0; 37671ae08745Sheppo } 37681ae08745Sheppo 37691ae08745Sheppo size = MIN(ldcp->stream_remains, *sizep); 37701ae08745Sheppo 37711ae08745Sheppo bcopy(ldcp->stream_bufferp + ldcp->stream_offset, target_bufp, size); 37721ae08745Sheppo ldcp->stream_offset += size; 37731ae08745Sheppo ldcp->stream_remains -= size; 37741ae08745Sheppo 37751ae08745Sheppo D2(ldcp->id, "i_ldc_read_stream: (0x%llx) fill from buffer size=%d", 37761ae08745Sheppo ldcp->id, size); 37771ae08745Sheppo 37781ae08745Sheppo *sizep = size; 37791ae08745Sheppo return (0); 37801ae08745Sheppo } 37811ae08745Sheppo 37821ae08745Sheppo /* 37831ae08745Sheppo * Write specified amount of bytes to the channel 37841ae08745Sheppo * in multiple pkts of pkt_payload size. Each 37851ae08745Sheppo * packet is tagged with an unique packet ID in 3786e1ebb9ecSlm66018 * the case of a reliable link. 37871ae08745Sheppo * 37881ae08745Sheppo * On return, size contains the number of bytes written. 37891ae08745Sheppo */ 37901ae08745Sheppo int 37911ae08745Sheppo ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep) 37921ae08745Sheppo { 37931ae08745Sheppo ldc_chan_t *ldcp; 37941ae08745Sheppo int rv = 0; 37951ae08745Sheppo 37961ae08745Sheppo if (handle == NULL) { 37971ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_write: invalid channel handle\n"); 37981ae08745Sheppo return (EINVAL); 37991ae08745Sheppo } 38001ae08745Sheppo ldcp = (ldc_chan_t *)handle; 38011ae08745Sheppo 3802d10e4ef2Snarayan /* check if writes can occur */ 3803d10e4ef2Snarayan if (!mutex_tryenter(&ldcp->tx_lock)) { 3804d10e4ef2Snarayan /* 3805d10e4ef2Snarayan * Could not get the lock - channel could 3806d10e4ef2Snarayan * be in the process of being unconfigured 3807d10e4ef2Snarayan * or reader has encountered an error 3808d10e4ef2Snarayan */ 3809d10e4ef2Snarayan return (EAGAIN); 3810d10e4ef2Snarayan } 38111ae08745Sheppo 38121ae08745Sheppo /* check if non-zero data to write */ 38131ae08745Sheppo if (buf == NULL || sizep == NULL) { 38141ae08745Sheppo DWARN(ldcp->id, "ldc_write: (0x%llx) invalid data write\n", 38151ae08745Sheppo ldcp->id); 3816d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 38171ae08745Sheppo return (EINVAL); 38181ae08745Sheppo } 38191ae08745Sheppo 38201ae08745Sheppo if (*sizep == 0) { 38211ae08745Sheppo DWARN(ldcp->id, "ldc_write: (0x%llx) write size of zero\n", 38221ae08745Sheppo ldcp->id); 3823d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 38241ae08745Sheppo return (0); 38251ae08745Sheppo } 38261ae08745Sheppo 38271ae08745Sheppo /* Check if channel is UP for data exchange */ 38281ae08745Sheppo if (ldcp->tstate != TS_UP) { 38291ae08745Sheppo DWARN(ldcp->id, 38301ae08745Sheppo "ldc_write: (0x%llx) channel is not in UP state\n", 38311ae08745Sheppo ldcp->id); 38321ae08745Sheppo *sizep = 0; 38331ae08745Sheppo rv = ECONNRESET; 38341ae08745Sheppo } else { 38351ae08745Sheppo rv = ldcp->write_p(ldcp, buf, sizep); 38361ae08745Sheppo } 38371ae08745Sheppo 3838d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 38391ae08745Sheppo 38401ae08745Sheppo return (rv); 38411ae08745Sheppo } 38421ae08745Sheppo 38431ae08745Sheppo /* 38441ae08745Sheppo * Write a raw packet to the channel 38451ae08745Sheppo * On return, size contains the number of bytes written. 38461ae08745Sheppo */ 38471ae08745Sheppo static int 38481ae08745Sheppo i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) 38491ae08745Sheppo { 38501ae08745Sheppo ldc_msg_t *ldcmsg; 38511ae08745Sheppo uint64_t tx_head, tx_tail, new_tail; 38521ae08745Sheppo int rv = 0; 38531ae08745Sheppo size_t size; 38541ae08745Sheppo 3855d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 38561ae08745Sheppo ASSERT(ldcp->mode == LDC_MODE_RAW); 38571ae08745Sheppo 38581ae08745Sheppo size = *sizep; 38591ae08745Sheppo 38601ae08745Sheppo /* 38611ae08745Sheppo * Check to see if the packet size is less than or 38621ae08745Sheppo * equal to packet size support in raw mode 38631ae08745Sheppo */ 38641ae08745Sheppo if (size > ldcp->pkt_payload) { 38651ae08745Sheppo DWARN(ldcp->id, 38661ae08745Sheppo "ldc_write: (0x%llx) invalid size (0x%llx) for RAW mode\n", 38671ae08745Sheppo ldcp->id, *sizep); 38681ae08745Sheppo *sizep = 0; 38691ae08745Sheppo return (EMSGSIZE); 38701ae08745Sheppo } 38711ae08745Sheppo 38721ae08745Sheppo /* get the qptrs for the tx queue */ 38731ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 38741ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 38751ae08745Sheppo if (rv != 0) { 38761ae08745Sheppo cmn_err(CE_WARN, 38771ae08745Sheppo "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id); 38781ae08745Sheppo *sizep = 0; 38791ae08745Sheppo return (EIO); 38801ae08745Sheppo } 38811ae08745Sheppo 38821ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_DOWN || 38831ae08745Sheppo ldcp->link_state == LDC_CHANNEL_RESET) { 38841ae08745Sheppo DWARN(ldcp->id, 38851ae08745Sheppo "ldc_write: (0x%llx) channel down/reset\n", ldcp->id); 3886d10e4ef2Snarayan 38871ae08745Sheppo *sizep = 0; 3888d10e4ef2Snarayan if (mutex_tryenter(&ldcp->lock)) { 38893af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3890d10e4ef2Snarayan mutex_exit(&ldcp->lock); 3891d10e4ef2Snarayan } else { 3892d10e4ef2Snarayan /* 3893d10e4ef2Snarayan * Release Tx lock, and then reacquire channel 3894d10e4ef2Snarayan * and Tx lock in correct order 3895d10e4ef2Snarayan */ 3896d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 3897d10e4ef2Snarayan mutex_enter(&ldcp->lock); 3898d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 38993af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3900d10e4ef2Snarayan mutex_exit(&ldcp->lock); 3901d10e4ef2Snarayan } 39021ae08745Sheppo return (ECONNRESET); 39031ae08745Sheppo } 39041ae08745Sheppo 39051ae08745Sheppo tx_tail = ldcp->tx_tail; 39061ae08745Sheppo tx_head = ldcp->tx_head; 39071ae08745Sheppo new_tail = (tx_tail + LDC_PACKET_SIZE) & 39081ae08745Sheppo ((ldcp->tx_q_entries-1) << LDC_PACKET_SHIFT); 39091ae08745Sheppo 39101ae08745Sheppo if (new_tail == tx_head) { 39111ae08745Sheppo DWARN(DBG_ALL_LDCS, 39121ae08745Sheppo "ldc_write: (0x%llx) TX queue is full\n", ldcp->id); 39131ae08745Sheppo *sizep = 0; 39141ae08745Sheppo return (EWOULDBLOCK); 39151ae08745Sheppo } 39161ae08745Sheppo 39171ae08745Sheppo D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d", 39181ae08745Sheppo ldcp->id, size); 39191ae08745Sheppo 39201ae08745Sheppo /* Send the data now */ 39211ae08745Sheppo ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 39221ae08745Sheppo 39231ae08745Sheppo /* copy the data into pkt */ 39241ae08745Sheppo bcopy((uint8_t *)buf, ldcmsg, size); 39251ae08745Sheppo 39261ae08745Sheppo /* increment tail */ 39271ae08745Sheppo tx_tail = new_tail; 39281ae08745Sheppo 39291ae08745Sheppo /* 39301ae08745Sheppo * All packets have been copied into the TX queue 39311ae08745Sheppo * update the tail ptr in the HV 39321ae08745Sheppo */ 39331ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 39341ae08745Sheppo if (rv) { 39351ae08745Sheppo if (rv == EWOULDBLOCK) { 39361ae08745Sheppo DWARN(ldcp->id, "ldc_write: (0x%llx) write timed out\n", 39371ae08745Sheppo ldcp->id); 39381ae08745Sheppo *sizep = 0; 39391ae08745Sheppo return (EWOULDBLOCK); 39401ae08745Sheppo } 39411ae08745Sheppo 39421ae08745Sheppo *sizep = 0; 3943d10e4ef2Snarayan if (mutex_tryenter(&ldcp->lock)) { 39443af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3945d10e4ef2Snarayan mutex_exit(&ldcp->lock); 3946d10e4ef2Snarayan } else { 3947d10e4ef2Snarayan /* 3948d10e4ef2Snarayan * Release Tx lock, and then reacquire channel 3949d10e4ef2Snarayan * and Tx lock in correct order 3950d10e4ef2Snarayan */ 3951d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 3952d10e4ef2Snarayan mutex_enter(&ldcp->lock); 3953d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 39543af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 3955d10e4ef2Snarayan mutex_exit(&ldcp->lock); 3956d10e4ef2Snarayan } 39571ae08745Sheppo return (ECONNRESET); 39581ae08745Sheppo } 39591ae08745Sheppo 39601ae08745Sheppo ldcp->tx_tail = tx_tail; 39611ae08745Sheppo *sizep = size; 39621ae08745Sheppo 39631ae08745Sheppo D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, size); 39641ae08745Sheppo 39651ae08745Sheppo return (rv); 39661ae08745Sheppo } 39671ae08745Sheppo 39681ae08745Sheppo 39691ae08745Sheppo /* 39701ae08745Sheppo * Write specified amount of bytes to the channel 39711ae08745Sheppo * in multiple pkts of pkt_payload size. Each 39721ae08745Sheppo * packet is tagged with an unique packet ID in 3973e1ebb9ecSlm66018 * the case of a reliable link. 39741ae08745Sheppo * 39751ae08745Sheppo * On return, size contains the number of bytes written. 39761ae08745Sheppo * This function needs to ensure that the write size is < MTU size 39771ae08745Sheppo */ 39781ae08745Sheppo static int 39791ae08745Sheppo i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size) 39801ae08745Sheppo { 39811ae08745Sheppo ldc_msg_t *ldcmsg; 39821ae08745Sheppo uint64_t tx_head, tx_tail, new_tail, start; 39831ae08745Sheppo uint64_t txq_size_mask, numavail; 39841ae08745Sheppo uint8_t *msgbuf, *source = (uint8_t *)buf; 39851ae08745Sheppo size_t len, bytes_written = 0, remaining; 39861ae08745Sheppo int rv; 39871ae08745Sheppo uint32_t curr_seqid; 39881ae08745Sheppo 3989d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 39901ae08745Sheppo 39911ae08745Sheppo ASSERT(ldcp->mode == LDC_MODE_RELIABLE || 39921ae08745Sheppo ldcp->mode == LDC_MODE_UNRELIABLE || 39931ae08745Sheppo ldcp->mode == LDC_MODE_STREAM); 39941ae08745Sheppo 39951ae08745Sheppo /* compute mask for increment */ 39961ae08745Sheppo txq_size_mask = (ldcp->tx_q_entries - 1) << LDC_PACKET_SHIFT; 39971ae08745Sheppo 39981ae08745Sheppo /* get the qptrs for the tx queue */ 39991ae08745Sheppo rv = hv_ldc_tx_get_state(ldcp->id, 40001ae08745Sheppo &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); 40011ae08745Sheppo if (rv != 0) { 40021ae08745Sheppo cmn_err(CE_WARN, 40031ae08745Sheppo "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id); 40041ae08745Sheppo *size = 0; 40051ae08745Sheppo return (EIO); 40061ae08745Sheppo } 40071ae08745Sheppo 40081ae08745Sheppo if (ldcp->link_state == LDC_CHANNEL_DOWN || 40091ae08745Sheppo ldcp->link_state == LDC_CHANNEL_RESET) { 40101ae08745Sheppo DWARN(ldcp->id, 40111ae08745Sheppo "ldc_write: (0x%llx) channel down/reset\n", ldcp->id); 40121ae08745Sheppo *size = 0; 4013d10e4ef2Snarayan if (mutex_tryenter(&ldcp->lock)) { 40143af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 4015d10e4ef2Snarayan mutex_exit(&ldcp->lock); 4016d10e4ef2Snarayan } else { 4017d10e4ef2Snarayan /* 4018d10e4ef2Snarayan * Release Tx lock, and then reacquire channel 4019d10e4ef2Snarayan * and Tx lock in correct order 4020d10e4ef2Snarayan */ 4021d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 4022d10e4ef2Snarayan mutex_enter(&ldcp->lock); 4023d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 40243af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 4025d10e4ef2Snarayan mutex_exit(&ldcp->lock); 4026d10e4ef2Snarayan } 40271ae08745Sheppo return (ECONNRESET); 40281ae08745Sheppo } 40291ae08745Sheppo 40301ae08745Sheppo tx_tail = ldcp->tx_tail; 40311ae08745Sheppo new_tail = (tx_tail + LDC_PACKET_SIZE) % 40321ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT); 40331ae08745Sheppo 40341ae08745Sheppo /* 403522f747efSnarayan * Check to see if the queue is full. The check is done using 403622f747efSnarayan * the appropriate head based on the link mode. 40371ae08745Sheppo */ 403822f747efSnarayan i_ldc_get_tx_head(ldcp, &tx_head); 403922f747efSnarayan 40401ae08745Sheppo if (new_tail == tx_head) { 40411ae08745Sheppo DWARN(DBG_ALL_LDCS, 40421ae08745Sheppo "ldc_write: (0x%llx) TX queue is full\n", ldcp->id); 40431ae08745Sheppo *size = 0; 40441ae08745Sheppo return (EWOULDBLOCK); 40451ae08745Sheppo } 40461ae08745Sheppo 40471ae08745Sheppo /* 40481ae08745Sheppo * Make sure that the LDC Tx queue has enough space 40491ae08745Sheppo */ 40501ae08745Sheppo numavail = (tx_head >> LDC_PACKET_SHIFT) - (tx_tail >> LDC_PACKET_SHIFT) 40511ae08745Sheppo + ldcp->tx_q_entries - 1; 40521ae08745Sheppo numavail %= ldcp->tx_q_entries; 40531ae08745Sheppo 40541ae08745Sheppo if (*size > (numavail * ldcp->pkt_payload)) { 40551ae08745Sheppo DWARN(DBG_ALL_LDCS, 40561ae08745Sheppo "ldc_write: (0x%llx) TX queue has no space\n", ldcp->id); 40571ae08745Sheppo return (EWOULDBLOCK); 40581ae08745Sheppo } 40591ae08745Sheppo 40601ae08745Sheppo D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d", 40611ae08745Sheppo ldcp->id, *size); 40621ae08745Sheppo 40631ae08745Sheppo /* Send the data now */ 40641ae08745Sheppo bytes_written = 0; 40651ae08745Sheppo curr_seqid = ldcp->last_msg_snt; 40661ae08745Sheppo start = tx_tail; 40671ae08745Sheppo 40681ae08745Sheppo while (*size > bytes_written) { 40691ae08745Sheppo 40701ae08745Sheppo ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); 40711ae08745Sheppo 40721ae08745Sheppo msgbuf = (uint8_t *)((ldcp->mode == LDC_MODE_RELIABLE || 407322f747efSnarayan ldcp->mode == LDC_MODE_STREAM) ? 407422f747efSnarayan ldcmsg->rdata : ldcmsg->udata); 40751ae08745Sheppo 40761ae08745Sheppo ldcmsg->type = LDC_DATA; 40771ae08745Sheppo ldcmsg->stype = LDC_INFO; 40781ae08745Sheppo ldcmsg->ctrl = 0; 40791ae08745Sheppo 40801ae08745Sheppo remaining = *size - bytes_written; 40811ae08745Sheppo len = min(ldcp->pkt_payload, remaining); 40821ae08745Sheppo ldcmsg->env = (uint8_t)len; 40831ae08745Sheppo 40841ae08745Sheppo curr_seqid++; 40851ae08745Sheppo ldcmsg->seqid = curr_seqid; 40861ae08745Sheppo 40871ae08745Sheppo /* copy the data into pkt */ 40881ae08745Sheppo bcopy(source, msgbuf, len); 40891ae08745Sheppo 40901ae08745Sheppo source += len; 40911ae08745Sheppo bytes_written += len; 40921ae08745Sheppo 40931ae08745Sheppo /* increment tail */ 40941ae08745Sheppo tx_tail = (tx_tail + LDC_PACKET_SIZE) & txq_size_mask; 40951ae08745Sheppo 40961ae08745Sheppo ASSERT(tx_tail != tx_head); 40971ae08745Sheppo } 40981ae08745Sheppo 40991ae08745Sheppo /* Set the start and stop bits */ 41001ae08745Sheppo ldcmsg->env |= LDC_FRAG_STOP; 41011ae08745Sheppo ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + start); 41021ae08745Sheppo ldcmsg->env |= LDC_FRAG_START; 41031ae08745Sheppo 41041ae08745Sheppo /* 41051ae08745Sheppo * All packets have been copied into the TX queue 41061ae08745Sheppo * update the tail ptr in the HV 41071ae08745Sheppo */ 41081ae08745Sheppo rv = i_ldc_set_tx_tail(ldcp, tx_tail); 41091ae08745Sheppo if (rv == 0) { 41101ae08745Sheppo ldcp->tx_tail = tx_tail; 41111ae08745Sheppo ldcp->last_msg_snt = curr_seqid; 41121ae08745Sheppo *size = bytes_written; 41131ae08745Sheppo } else { 41141ae08745Sheppo int rv2; 41151ae08745Sheppo 41161ae08745Sheppo if (rv != EWOULDBLOCK) { 41171ae08745Sheppo *size = 0; 4118d10e4ef2Snarayan if (mutex_tryenter(&ldcp->lock)) { 41193af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 4120d10e4ef2Snarayan mutex_exit(&ldcp->lock); 4121d10e4ef2Snarayan } else { 4122d10e4ef2Snarayan /* 4123d10e4ef2Snarayan * Release Tx lock, and then reacquire channel 4124d10e4ef2Snarayan * and Tx lock in correct order 4125d10e4ef2Snarayan */ 4126d10e4ef2Snarayan mutex_exit(&ldcp->tx_lock); 4127d10e4ef2Snarayan mutex_enter(&ldcp->lock); 4128d10e4ef2Snarayan mutex_enter(&ldcp->tx_lock); 41293af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 4130d10e4ef2Snarayan mutex_exit(&ldcp->lock); 4131d10e4ef2Snarayan } 41321ae08745Sheppo return (ECONNRESET); 41331ae08745Sheppo } 41341ae08745Sheppo 4135cb112a14Slm66018 D1(ldcp->id, "hv_tx_set_tail returns 0x%x (head 0x%x, " 41361ae08745Sheppo "old tail 0x%x, new tail 0x%x, qsize=0x%x)\n", 41371ae08745Sheppo rv, ldcp->tx_head, ldcp->tx_tail, tx_tail, 41381ae08745Sheppo (ldcp->tx_q_entries << LDC_PACKET_SHIFT)); 41391ae08745Sheppo 41401ae08745Sheppo rv2 = hv_ldc_tx_get_state(ldcp->id, 41411ae08745Sheppo &tx_head, &tx_tail, &ldcp->link_state); 41421ae08745Sheppo 4143cb112a14Slm66018 D1(ldcp->id, "hv_ldc_tx_get_state returns 0x%x " 41441ae08745Sheppo "(head 0x%x, tail 0x%x state 0x%x)\n", 41451ae08745Sheppo rv2, tx_head, tx_tail, ldcp->link_state); 41461ae08745Sheppo 41471ae08745Sheppo *size = 0; 41481ae08745Sheppo } 41491ae08745Sheppo 41501ae08745Sheppo D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, *size); 41511ae08745Sheppo 41521ae08745Sheppo return (rv); 41531ae08745Sheppo } 41541ae08745Sheppo 41551ae08745Sheppo /* 41561ae08745Sheppo * Write specified amount of bytes to the channel 41571ae08745Sheppo * in multiple pkts of pkt_payload size. Each 41581ae08745Sheppo * packet is tagged with an unique packet ID in 4159e1ebb9ecSlm66018 * the case of a reliable link. 41601ae08745Sheppo * 41611ae08745Sheppo * On return, size contains the number of bytes written. 41621ae08745Sheppo * This function needs to ensure that the write size is < MTU size 41631ae08745Sheppo */ 41641ae08745Sheppo static int 41651ae08745Sheppo i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) 41661ae08745Sheppo { 4167d10e4ef2Snarayan ASSERT(MUTEX_HELD(&ldcp->tx_lock)); 41681ae08745Sheppo ASSERT(ldcp->mode == LDC_MODE_STREAM); 41691ae08745Sheppo 41701ae08745Sheppo /* Truncate packet to max of MTU size */ 41711ae08745Sheppo if (*sizep > ldcp->mtu) *sizep = ldcp->mtu; 41721ae08745Sheppo return (i_ldc_write_packet(ldcp, buf, sizep)); 41731ae08745Sheppo } 41741ae08745Sheppo 41751ae08745Sheppo 41761ae08745Sheppo /* 41771ae08745Sheppo * Interfaces for channel nexus to register/unregister with LDC module 41781ae08745Sheppo * The nexus will register functions to be used to register individual 41791ae08745Sheppo * channels with the nexus and enable interrupts for the channels 41801ae08745Sheppo */ 41811ae08745Sheppo int 41821ae08745Sheppo ldc_register(ldc_cnex_t *cinfo) 41831ae08745Sheppo { 41841ae08745Sheppo ldc_chan_t *ldcp; 41851ae08745Sheppo 41861ae08745Sheppo if (cinfo == NULL || cinfo->dip == NULL || 41871ae08745Sheppo cinfo->reg_chan == NULL || cinfo->unreg_chan == NULL || 41881ae08745Sheppo cinfo->add_intr == NULL || cinfo->rem_intr == NULL || 41891ae08745Sheppo cinfo->clr_intr == NULL) { 41901ae08745Sheppo 41911ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_register: invalid nexus info\n"); 41921ae08745Sheppo return (EINVAL); 41931ae08745Sheppo } 41941ae08745Sheppo 41951ae08745Sheppo mutex_enter(&ldcssp->lock); 41961ae08745Sheppo 41971ae08745Sheppo /* nexus registration */ 41981ae08745Sheppo ldcssp->cinfo.dip = cinfo->dip; 41991ae08745Sheppo ldcssp->cinfo.reg_chan = cinfo->reg_chan; 42001ae08745Sheppo ldcssp->cinfo.unreg_chan = cinfo->unreg_chan; 42011ae08745Sheppo ldcssp->cinfo.add_intr = cinfo->add_intr; 42021ae08745Sheppo ldcssp->cinfo.rem_intr = cinfo->rem_intr; 42031ae08745Sheppo ldcssp->cinfo.clr_intr = cinfo->clr_intr; 42041ae08745Sheppo 42051ae08745Sheppo /* register any channels that might have been previously initialized */ 42061ae08745Sheppo ldcp = ldcssp->chan_list; 42071ae08745Sheppo while (ldcp) { 42081ae08745Sheppo if ((ldcp->tstate & TS_QCONF_RDY) && 42091ae08745Sheppo (ldcp->tstate & TS_CNEX_RDY) == 0) 42101ae08745Sheppo (void) i_ldc_register_channel(ldcp); 42111ae08745Sheppo 42121ae08745Sheppo ldcp = ldcp->next; 42131ae08745Sheppo } 42141ae08745Sheppo 42151ae08745Sheppo mutex_exit(&ldcssp->lock); 42161ae08745Sheppo 42171ae08745Sheppo return (0); 42181ae08745Sheppo } 42191ae08745Sheppo 42201ae08745Sheppo int 42211ae08745Sheppo ldc_unregister(ldc_cnex_t *cinfo) 42221ae08745Sheppo { 42231ae08745Sheppo if (cinfo == NULL || cinfo->dip == NULL) { 42241ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid nexus info\n"); 42251ae08745Sheppo return (EINVAL); 42261ae08745Sheppo } 42271ae08745Sheppo 42281ae08745Sheppo mutex_enter(&ldcssp->lock); 42291ae08745Sheppo 42301ae08745Sheppo if (cinfo->dip != ldcssp->cinfo.dip) { 42311ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid dip\n"); 42321ae08745Sheppo mutex_exit(&ldcssp->lock); 42331ae08745Sheppo return (EINVAL); 42341ae08745Sheppo } 42351ae08745Sheppo 42361ae08745Sheppo /* nexus unregister */ 42371ae08745Sheppo ldcssp->cinfo.dip = NULL; 42381ae08745Sheppo ldcssp->cinfo.reg_chan = NULL; 42391ae08745Sheppo ldcssp->cinfo.unreg_chan = NULL; 42401ae08745Sheppo ldcssp->cinfo.add_intr = NULL; 42411ae08745Sheppo ldcssp->cinfo.rem_intr = NULL; 42421ae08745Sheppo ldcssp->cinfo.clr_intr = NULL; 42431ae08745Sheppo 42441ae08745Sheppo mutex_exit(&ldcssp->lock); 42451ae08745Sheppo 42461ae08745Sheppo return (0); 42471ae08745Sheppo } 42481ae08745Sheppo 42491ae08745Sheppo 42501ae08745Sheppo /* ------------------------------------------------------------------------- */ 42511ae08745Sheppo 42521ae08745Sheppo /* 42531ae08745Sheppo * Allocate a memory handle for the channel and link it into the list 42541ae08745Sheppo * Also choose which memory table to use if this is the first handle 42551ae08745Sheppo * being assigned to this channel 42561ae08745Sheppo */ 42571ae08745Sheppo int 42581ae08745Sheppo ldc_mem_alloc_handle(ldc_handle_t handle, ldc_mem_handle_t *mhandle) 42591ae08745Sheppo { 42601ae08745Sheppo ldc_chan_t *ldcp; 42611ae08745Sheppo ldc_mhdl_t *mhdl; 42621ae08745Sheppo 42631ae08745Sheppo if (handle == NULL) { 42641ae08745Sheppo DWARN(DBG_ALL_LDCS, 42651ae08745Sheppo "ldc_mem_alloc_handle: invalid channel handle\n"); 42661ae08745Sheppo return (EINVAL); 42671ae08745Sheppo } 42681ae08745Sheppo ldcp = (ldc_chan_t *)handle; 42691ae08745Sheppo 42701ae08745Sheppo mutex_enter(&ldcp->lock); 42711ae08745Sheppo 42721ae08745Sheppo /* check to see if channel is initalized */ 42733af08d82Slm66018 if ((ldcp->tstate & ~TS_IN_RESET) < TS_INIT) { 42741ae08745Sheppo DWARN(ldcp->id, 42751ae08745Sheppo "ldc_mem_alloc_handle: (0x%llx) channel not initialized\n", 42761ae08745Sheppo ldcp->id); 42771ae08745Sheppo mutex_exit(&ldcp->lock); 42781ae08745Sheppo return (EINVAL); 42791ae08745Sheppo } 42801ae08745Sheppo 42811ae08745Sheppo /* allocate handle for channel */ 42824bac2208Snarayan mhdl = kmem_cache_alloc(ldcssp->memhdl_cache, KM_SLEEP); 42831ae08745Sheppo 42841ae08745Sheppo /* initialize the lock */ 42851ae08745Sheppo mutex_init(&mhdl->lock, NULL, MUTEX_DRIVER, NULL); 42861ae08745Sheppo 42874bac2208Snarayan mhdl->myshadow = B_FALSE; 42884bac2208Snarayan mhdl->memseg = NULL; 42891ae08745Sheppo mhdl->ldcp = ldcp; 42904bac2208Snarayan mhdl->status = LDC_UNBOUND; 42911ae08745Sheppo 42921ae08745Sheppo /* insert memory handle (@ head) into list */ 42931ae08745Sheppo if (ldcp->mhdl_list == NULL) { 42941ae08745Sheppo ldcp->mhdl_list = mhdl; 42951ae08745Sheppo mhdl->next = NULL; 42961ae08745Sheppo } else { 42971ae08745Sheppo /* insert @ head */ 42981ae08745Sheppo mhdl->next = ldcp->mhdl_list; 42991ae08745Sheppo ldcp->mhdl_list = mhdl; 43001ae08745Sheppo } 43011ae08745Sheppo 43021ae08745Sheppo /* return the handle */ 43031ae08745Sheppo *mhandle = (ldc_mem_handle_t)mhdl; 43041ae08745Sheppo 43051ae08745Sheppo mutex_exit(&ldcp->lock); 43061ae08745Sheppo 43071ae08745Sheppo D1(ldcp->id, "ldc_mem_alloc_handle: (0x%llx) allocated handle 0x%llx\n", 43081ae08745Sheppo ldcp->id, mhdl); 43091ae08745Sheppo 43101ae08745Sheppo return (0); 43111ae08745Sheppo } 43121ae08745Sheppo 43131ae08745Sheppo /* 43141ae08745Sheppo * Free memory handle for the channel and unlink it from the list 43151ae08745Sheppo */ 43161ae08745Sheppo int 43171ae08745Sheppo ldc_mem_free_handle(ldc_mem_handle_t mhandle) 43181ae08745Sheppo { 43191ae08745Sheppo ldc_mhdl_t *mhdl, *phdl; 43201ae08745Sheppo ldc_chan_t *ldcp; 43211ae08745Sheppo 43221ae08745Sheppo if (mhandle == NULL) { 43231ae08745Sheppo DWARN(DBG_ALL_LDCS, 43241ae08745Sheppo "ldc_mem_free_handle: invalid memory handle\n"); 43251ae08745Sheppo return (EINVAL); 43261ae08745Sheppo } 43271ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 43281ae08745Sheppo 43291ae08745Sheppo mutex_enter(&mhdl->lock); 43301ae08745Sheppo 43311ae08745Sheppo ldcp = mhdl->ldcp; 43321ae08745Sheppo 43331ae08745Sheppo if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) { 43341ae08745Sheppo DWARN(ldcp->id, 43351ae08745Sheppo "ldc_mem_free_handle: cannot free, 0x%llx hdl bound\n", 43361ae08745Sheppo mhdl); 43371ae08745Sheppo mutex_exit(&mhdl->lock); 43381ae08745Sheppo return (EINVAL); 43391ae08745Sheppo } 43401ae08745Sheppo mutex_exit(&mhdl->lock); 43411ae08745Sheppo 43421ae08745Sheppo mutex_enter(&ldcp->mlist_lock); 43431ae08745Sheppo 43441ae08745Sheppo phdl = ldcp->mhdl_list; 43451ae08745Sheppo 43461ae08745Sheppo /* first handle */ 43471ae08745Sheppo if (phdl == mhdl) { 43481ae08745Sheppo ldcp->mhdl_list = mhdl->next; 43491ae08745Sheppo mutex_destroy(&mhdl->lock); 43504bac2208Snarayan kmem_cache_free(ldcssp->memhdl_cache, mhdl); 43514bac2208Snarayan 43521ae08745Sheppo D1(ldcp->id, 43531ae08745Sheppo "ldc_mem_free_handle: (0x%llx) freed handle 0x%llx\n", 43541ae08745Sheppo ldcp->id, mhdl); 43551ae08745Sheppo } else { 43561ae08745Sheppo /* walk the list - unlink and free */ 43571ae08745Sheppo while (phdl != NULL) { 43581ae08745Sheppo if (phdl->next == mhdl) { 43591ae08745Sheppo phdl->next = mhdl->next; 43601ae08745Sheppo mutex_destroy(&mhdl->lock); 43614bac2208Snarayan kmem_cache_free(ldcssp->memhdl_cache, mhdl); 43621ae08745Sheppo D1(ldcp->id, 43631ae08745Sheppo "ldc_mem_free_handle: (0x%llx) freed " 43641ae08745Sheppo "handle 0x%llx\n", ldcp->id, mhdl); 43651ae08745Sheppo break; 43661ae08745Sheppo } 43671ae08745Sheppo phdl = phdl->next; 43681ae08745Sheppo } 43691ae08745Sheppo } 43701ae08745Sheppo 43711ae08745Sheppo if (phdl == NULL) { 43721ae08745Sheppo DWARN(ldcp->id, 43731ae08745Sheppo "ldc_mem_free_handle: invalid handle 0x%llx\n", mhdl); 43741ae08745Sheppo mutex_exit(&ldcp->mlist_lock); 43751ae08745Sheppo return (EINVAL); 43761ae08745Sheppo } 43771ae08745Sheppo 43781ae08745Sheppo mutex_exit(&ldcp->mlist_lock); 43791ae08745Sheppo 43801ae08745Sheppo return (0); 43811ae08745Sheppo } 43821ae08745Sheppo 43831ae08745Sheppo /* 43841ae08745Sheppo * Bind a memory handle to a virtual address. 43851ae08745Sheppo * The virtual address is converted to the corresponding real addresses. 43861ae08745Sheppo * Returns pointer to the first ldc_mem_cookie and the total number 43871ae08745Sheppo * of cookies for this virtual address. Other cookies can be obtained 43881ae08745Sheppo * using the ldc_mem_nextcookie() call. If the pages are stored in 43891ae08745Sheppo * consecutive locations in the table, a single cookie corresponding to 43901ae08745Sheppo * the first location is returned. The cookie size spans all the entries. 43911ae08745Sheppo * 43921ae08745Sheppo * If the VA corresponds to a page that is already being exported, reuse 43931ae08745Sheppo * the page and do not export it again. Bump the page's use count. 43941ae08745Sheppo */ 43951ae08745Sheppo int 43961ae08745Sheppo ldc_mem_bind_handle(ldc_mem_handle_t mhandle, caddr_t vaddr, size_t len, 43971ae08745Sheppo uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount) 43981ae08745Sheppo { 43991ae08745Sheppo ldc_mhdl_t *mhdl; 44001ae08745Sheppo ldc_chan_t *ldcp; 44011ae08745Sheppo ldc_mtbl_t *mtbl; 44021ae08745Sheppo ldc_memseg_t *memseg; 44031ae08745Sheppo ldc_mte_t tmp_mte; 44041ae08745Sheppo uint64_t index, prev_index = 0; 44051ae08745Sheppo int64_t cookie_idx; 44061ae08745Sheppo uintptr_t raddr, ra_aligned; 44071ae08745Sheppo uint64_t psize, poffset, v_offset; 44081ae08745Sheppo uint64_t pg_shift, pg_size, pg_size_code, pg_mask; 44091ae08745Sheppo pgcnt_t npages; 44101ae08745Sheppo caddr_t v_align, addr; 44114bac2208Snarayan int i, rv; 44121ae08745Sheppo 44131ae08745Sheppo if (mhandle == NULL) { 44141ae08745Sheppo DWARN(DBG_ALL_LDCS, 44151ae08745Sheppo "ldc_mem_bind_handle: invalid memory handle\n"); 44161ae08745Sheppo return (EINVAL); 44171ae08745Sheppo } 44181ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 44191ae08745Sheppo ldcp = mhdl->ldcp; 44201ae08745Sheppo 44211ae08745Sheppo /* clear count */ 44221ae08745Sheppo *ccount = 0; 44231ae08745Sheppo 44241ae08745Sheppo mutex_enter(&mhdl->lock); 44251ae08745Sheppo 44261ae08745Sheppo if (mhdl->status == LDC_BOUND || mhdl->memseg != NULL) { 44271ae08745Sheppo DWARN(ldcp->id, 44281ae08745Sheppo "ldc_mem_bind_handle: (0x%x) handle already bound\n", 44291ae08745Sheppo mhandle); 44301ae08745Sheppo mutex_exit(&mhdl->lock); 44311ae08745Sheppo return (EINVAL); 44321ae08745Sheppo } 44331ae08745Sheppo 44341ae08745Sheppo /* Force address and size to be 8-byte aligned */ 44351ae08745Sheppo if ((((uintptr_t)vaddr | len) & 0x7) != 0) { 44361ae08745Sheppo DWARN(ldcp->id, 44371ae08745Sheppo "ldc_mem_bind_handle: addr/size is not 8-byte aligned\n"); 44381ae08745Sheppo mutex_exit(&mhdl->lock); 44391ae08745Sheppo return (EINVAL); 44401ae08745Sheppo } 44411ae08745Sheppo 44424bac2208Snarayan /* 44434bac2208Snarayan * If this channel is binding a memory handle for the 44444bac2208Snarayan * first time allocate it a memory map table and initialize it 44454bac2208Snarayan */ 44464bac2208Snarayan if ((mtbl = ldcp->mtbl) == NULL) { 44474bac2208Snarayan 44484bac2208Snarayan mutex_enter(&ldcp->lock); 44494bac2208Snarayan 44504bac2208Snarayan /* Allocate and initialize the map table structure */ 44514bac2208Snarayan mtbl = kmem_zalloc(sizeof (ldc_mtbl_t), KM_SLEEP); 44524bac2208Snarayan mtbl->num_entries = mtbl->num_avail = ldc_maptable_entries; 44534bac2208Snarayan mtbl->size = ldc_maptable_entries * sizeof (ldc_mte_slot_t); 44544bac2208Snarayan mtbl->next_entry = NULL; 44553af08d82Slm66018 mtbl->contigmem = B_TRUE; 44564bac2208Snarayan 44574bac2208Snarayan /* Allocate the table itself */ 44584bac2208Snarayan mtbl->table = (ldc_mte_slot_t *) 44594bac2208Snarayan contig_mem_alloc_align(mtbl->size, MMU_PAGESIZE); 44604bac2208Snarayan if (mtbl->table == NULL) { 44613af08d82Slm66018 44623af08d82Slm66018 /* allocate a page of memory using kmem_alloc */ 44633af08d82Slm66018 mtbl->table = kmem_alloc(MMU_PAGESIZE, KM_SLEEP); 44643af08d82Slm66018 mtbl->size = MMU_PAGESIZE; 44653af08d82Slm66018 mtbl->contigmem = B_FALSE; 44663af08d82Slm66018 mtbl->num_entries = mtbl->num_avail = 44673af08d82Slm66018 mtbl->size / sizeof (ldc_mte_slot_t); 44683af08d82Slm66018 DWARN(ldcp->id, 44693af08d82Slm66018 "ldc_mem_bind_handle: (0x%llx) reduced tbl size " 44703af08d82Slm66018 "to %lx entries\n", ldcp->id, mtbl->num_entries); 44714bac2208Snarayan } 44724bac2208Snarayan 44734bac2208Snarayan /* zero out the memory */ 44744bac2208Snarayan bzero(mtbl->table, mtbl->size); 44754bac2208Snarayan 44764bac2208Snarayan /* initialize the lock */ 44774bac2208Snarayan mutex_init(&mtbl->lock, NULL, MUTEX_DRIVER, NULL); 44784bac2208Snarayan 44794bac2208Snarayan /* register table for this channel */ 44804bac2208Snarayan rv = hv_ldc_set_map_table(ldcp->id, 44814bac2208Snarayan va_to_pa(mtbl->table), mtbl->num_entries); 44824bac2208Snarayan if (rv != 0) { 44834bac2208Snarayan cmn_err(CE_WARN, 44844bac2208Snarayan "ldc_mem_bind_handle: (0x%lx) err %d mapping tbl", 44854bac2208Snarayan ldcp->id, rv); 44863af08d82Slm66018 if (mtbl->contigmem) 44874bac2208Snarayan contig_mem_free(mtbl->table, mtbl->size); 44883af08d82Slm66018 else 44893af08d82Slm66018 kmem_free(mtbl->table, mtbl->size); 44904bac2208Snarayan mutex_destroy(&mtbl->lock); 44914bac2208Snarayan kmem_free(mtbl, sizeof (ldc_mtbl_t)); 44924bac2208Snarayan mutex_exit(&ldcp->lock); 44934bac2208Snarayan mutex_exit(&mhdl->lock); 44944bac2208Snarayan return (EIO); 44954bac2208Snarayan } 44964bac2208Snarayan 44974bac2208Snarayan ldcp->mtbl = mtbl; 44984bac2208Snarayan mutex_exit(&ldcp->lock); 44994bac2208Snarayan 45004bac2208Snarayan D1(ldcp->id, 45014bac2208Snarayan "ldc_mem_bind_handle: (0x%llx) alloc'd map table 0x%llx\n", 45024bac2208Snarayan ldcp->id, ldcp->mtbl->table); 45034bac2208Snarayan } 45044bac2208Snarayan 45051ae08745Sheppo /* FUTURE: get the page size, pgsz code, and shift */ 45061ae08745Sheppo pg_size = MMU_PAGESIZE; 45071ae08745Sheppo pg_size_code = page_szc(pg_size); 45081ae08745Sheppo pg_shift = page_get_shift(pg_size_code); 45091ae08745Sheppo pg_mask = ~(pg_size - 1); 45101ae08745Sheppo 45111ae08745Sheppo D1(ldcp->id, "ldc_mem_bind_handle: (0x%llx) binding " 45121ae08745Sheppo "va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n", 45131ae08745Sheppo ldcp->id, vaddr, pg_size, pg_size_code, pg_shift); 45141ae08745Sheppo 45151ae08745Sheppo /* aligned VA and its offset */ 45161ae08745Sheppo v_align = (caddr_t)(((uintptr_t)vaddr) & ~(pg_size - 1)); 45171ae08745Sheppo v_offset = ((uintptr_t)vaddr) & (pg_size - 1); 45181ae08745Sheppo 45191ae08745Sheppo npages = (len+v_offset)/pg_size; 45201ae08745Sheppo npages = ((len+v_offset)%pg_size == 0) ? npages : npages+1; 45211ae08745Sheppo 45221ae08745Sheppo D1(ldcp->id, "ldc_mem_bind_handle: binding " 45231ae08745Sheppo "(0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n", 45241ae08745Sheppo ldcp->id, vaddr, v_align, v_offset, npages); 45251ae08745Sheppo 45261ae08745Sheppo /* lock the memory table - exclusive access to channel */ 45271ae08745Sheppo mutex_enter(&mtbl->lock); 45281ae08745Sheppo 45291ae08745Sheppo if (npages > mtbl->num_avail) { 45303af08d82Slm66018 D1(ldcp->id, "ldc_mem_bind_handle: (0x%llx) no table entries\n", 45311ae08745Sheppo ldcp->id); 45321ae08745Sheppo mutex_exit(&mtbl->lock); 45331ae08745Sheppo mutex_exit(&mhdl->lock); 45341ae08745Sheppo return (ENOMEM); 45351ae08745Sheppo } 45361ae08745Sheppo 45371ae08745Sheppo /* Allocate a memseg structure */ 45384bac2208Snarayan memseg = mhdl->memseg = 45394bac2208Snarayan kmem_cache_alloc(ldcssp->memseg_cache, KM_SLEEP); 45401ae08745Sheppo 45411ae08745Sheppo /* Allocate memory to store all pages and cookies */ 45421ae08745Sheppo memseg->pages = kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP); 45431ae08745Sheppo memseg->cookies = 45441ae08745Sheppo kmem_zalloc((sizeof (ldc_mem_cookie_t) * npages), KM_SLEEP); 45451ae08745Sheppo 45461ae08745Sheppo D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) processing 0x%llx pages\n", 45471ae08745Sheppo ldcp->id, npages); 45481ae08745Sheppo 45491ae08745Sheppo addr = v_align; 45501ae08745Sheppo 45511ae08745Sheppo /* 45524bac2208Snarayan * Check if direct shared memory map is enabled, if not change 45534bac2208Snarayan * the mapping type to include SHADOW_MAP. 45544bac2208Snarayan */ 45554bac2208Snarayan if (ldc_shmem_enabled == 0) 45564bac2208Snarayan mtype = LDC_SHADOW_MAP; 45574bac2208Snarayan 45584bac2208Snarayan /* 45591ae08745Sheppo * Table slots are used in a round-robin manner. The algorithm permits 45601ae08745Sheppo * inserting duplicate entries. Slots allocated earlier will typically 45611ae08745Sheppo * get freed before we get back to reusing the slot.Inserting duplicate 45621ae08745Sheppo * entries should be OK as we only lookup entries using the cookie addr 45631ae08745Sheppo * i.e. tbl index, during export, unexport and copy operation. 45641ae08745Sheppo * 45651ae08745Sheppo * One implementation what was tried was to search for a duplicate 45661ae08745Sheppo * page entry first and reuse it. The search overhead is very high and 45671ae08745Sheppo * in the vnet case dropped the perf by almost half, 50 to 24 mbps. 45681ae08745Sheppo * So it does make sense to avoid searching for duplicates. 45691ae08745Sheppo * 45701ae08745Sheppo * But during the process of searching for a free slot, if we find a 45711ae08745Sheppo * duplicate entry we will go ahead and use it, and bump its use count. 45721ae08745Sheppo */ 45731ae08745Sheppo 45741ae08745Sheppo /* index to start searching from */ 45751ae08745Sheppo index = mtbl->next_entry; 45761ae08745Sheppo cookie_idx = -1; 45771ae08745Sheppo 45781ae08745Sheppo tmp_mte.ll = 0; /* initialise fields to 0 */ 45791ae08745Sheppo 45801ae08745Sheppo if (mtype & LDC_DIRECT_MAP) { 45811ae08745Sheppo tmp_mte.mte_r = (perm & LDC_MEM_R) ? 1 : 0; 45821ae08745Sheppo tmp_mte.mte_w = (perm & LDC_MEM_W) ? 1 : 0; 45831ae08745Sheppo tmp_mte.mte_x = (perm & LDC_MEM_X) ? 1 : 0; 45841ae08745Sheppo } 45851ae08745Sheppo 45861ae08745Sheppo if (mtype & LDC_SHADOW_MAP) { 45871ae08745Sheppo tmp_mte.mte_cr = (perm & LDC_MEM_R) ? 1 : 0; 45881ae08745Sheppo tmp_mte.mte_cw = (perm & LDC_MEM_W) ? 1 : 0; 45891ae08745Sheppo } 45901ae08745Sheppo 45911ae08745Sheppo if (mtype & LDC_IO_MAP) { 45921ae08745Sheppo tmp_mte.mte_ir = (perm & LDC_MEM_R) ? 1 : 0; 45931ae08745Sheppo tmp_mte.mte_iw = (perm & LDC_MEM_W) ? 1 : 0; 45941ae08745Sheppo } 45951ae08745Sheppo 45961ae08745Sheppo D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll); 45971ae08745Sheppo 45981ae08745Sheppo tmp_mte.mte_pgszc = pg_size_code; 45991ae08745Sheppo 46001ae08745Sheppo /* initialize each mem table entry */ 46011ae08745Sheppo for (i = 0; i < npages; i++) { 46021ae08745Sheppo 46031ae08745Sheppo /* check if slot is available in the table */ 46041ae08745Sheppo while (mtbl->table[index].entry.ll != 0) { 46051ae08745Sheppo 46061ae08745Sheppo index = (index + 1) % mtbl->num_entries; 46071ae08745Sheppo 46081ae08745Sheppo if (index == mtbl->next_entry) { 46091ae08745Sheppo /* we have looped around */ 46101ae08745Sheppo DWARN(DBG_ALL_LDCS, 46111ae08745Sheppo "ldc_mem_bind_handle: (0x%llx) cannot find " 46121ae08745Sheppo "entry\n", ldcp->id); 46131ae08745Sheppo *ccount = 0; 46141ae08745Sheppo 46151ae08745Sheppo /* NOTE: free memory, remove previous entries */ 46161ae08745Sheppo /* this shouldnt happen as num_avail was ok */ 46171ae08745Sheppo 46181ae08745Sheppo mutex_exit(&mtbl->lock); 46191ae08745Sheppo mutex_exit(&mhdl->lock); 46201ae08745Sheppo return (ENOMEM); 46211ae08745Sheppo } 46221ae08745Sheppo } 46231ae08745Sheppo 46241ae08745Sheppo /* get the real address */ 46251ae08745Sheppo raddr = va_to_pa((void *)addr); 46261ae08745Sheppo ra_aligned = ((uintptr_t)raddr & pg_mask); 46271ae08745Sheppo 46281ae08745Sheppo /* build the mte */ 46291ae08745Sheppo tmp_mte.mte_rpfn = ra_aligned >> pg_shift; 46301ae08745Sheppo 46311ae08745Sheppo D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll); 46321ae08745Sheppo 46331ae08745Sheppo /* update entry in table */ 46341ae08745Sheppo mtbl->table[index].entry = tmp_mte; 46351ae08745Sheppo 46361ae08745Sheppo D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) stored MTE 0x%llx" 46371ae08745Sheppo " into loc 0x%llx\n", ldcp->id, tmp_mte.ll, index); 46381ae08745Sheppo 46391ae08745Sheppo /* calculate the size and offset for this export range */ 46401ae08745Sheppo if (i == 0) { 46411ae08745Sheppo /* first page */ 46421ae08745Sheppo psize = min((pg_size - v_offset), len); 46431ae08745Sheppo poffset = v_offset; 46441ae08745Sheppo 46451ae08745Sheppo } else if (i == (npages - 1)) { 46461ae08745Sheppo /* last page */ 46471ae08745Sheppo psize = (((uintptr_t)(vaddr + len)) & 46481ae08745Sheppo ((uint64_t)(pg_size-1))); 46491ae08745Sheppo if (psize == 0) 46501ae08745Sheppo psize = pg_size; 46511ae08745Sheppo poffset = 0; 46521ae08745Sheppo 46531ae08745Sheppo } else { 46541ae08745Sheppo /* middle pages */ 46551ae08745Sheppo psize = pg_size; 46561ae08745Sheppo poffset = 0; 46571ae08745Sheppo } 46581ae08745Sheppo 46591ae08745Sheppo /* store entry for this page */ 46601ae08745Sheppo memseg->pages[i].index = index; 46611ae08745Sheppo memseg->pages[i].raddr = raddr; 46621ae08745Sheppo memseg->pages[i].offset = poffset; 46631ae08745Sheppo memseg->pages[i].size = psize; 46641ae08745Sheppo memseg->pages[i].mte = &(mtbl->table[index]); 46651ae08745Sheppo 46661ae08745Sheppo /* create the cookie */ 46671ae08745Sheppo if (i == 0 || (index != prev_index + 1)) { 46681ae08745Sheppo cookie_idx++; 46691ae08745Sheppo memseg->cookies[cookie_idx].addr = 46701ae08745Sheppo IDX2COOKIE(index, pg_size_code, pg_shift); 46711ae08745Sheppo memseg->cookies[cookie_idx].addr |= poffset; 46721ae08745Sheppo memseg->cookies[cookie_idx].size = psize; 46731ae08745Sheppo 46741ae08745Sheppo } else { 46751ae08745Sheppo memseg->cookies[cookie_idx].size += psize; 46761ae08745Sheppo } 46771ae08745Sheppo 46781ae08745Sheppo D1(ldcp->id, "ldc_mem_bind_handle: bound " 46791ae08745Sheppo "(0x%llx) va=0x%llx, idx=0x%llx, " 46801ae08745Sheppo "ra=0x%llx(sz=0x%x,off=0x%x)\n", 46811ae08745Sheppo ldcp->id, addr, index, raddr, psize, poffset); 46821ae08745Sheppo 46831ae08745Sheppo /* decrement number of available entries */ 46841ae08745Sheppo mtbl->num_avail--; 46851ae08745Sheppo 46861ae08745Sheppo /* increment va by page size */ 46871ae08745Sheppo addr += pg_size; 46881ae08745Sheppo 46891ae08745Sheppo /* increment index */ 46901ae08745Sheppo prev_index = index; 46911ae08745Sheppo index = (index + 1) % mtbl->num_entries; 46921ae08745Sheppo 46931ae08745Sheppo /* save the next slot */ 46941ae08745Sheppo mtbl->next_entry = index; 46951ae08745Sheppo } 46961ae08745Sheppo 46971ae08745Sheppo mutex_exit(&mtbl->lock); 46981ae08745Sheppo 46991ae08745Sheppo /* memory handle = bound */ 47001ae08745Sheppo mhdl->mtype = mtype; 47011ae08745Sheppo mhdl->perm = perm; 47021ae08745Sheppo mhdl->status = LDC_BOUND; 47031ae08745Sheppo 47041ae08745Sheppo /* update memseg_t */ 47051ae08745Sheppo memseg->vaddr = vaddr; 47061ae08745Sheppo memseg->raddr = memseg->pages[0].raddr; 47071ae08745Sheppo memseg->size = len; 47081ae08745Sheppo memseg->npages = npages; 47091ae08745Sheppo memseg->ncookies = cookie_idx + 1; 47101ae08745Sheppo memseg->next_cookie = (memseg->ncookies > 1) ? 1 : 0; 47111ae08745Sheppo 47121ae08745Sheppo /* return count and first cookie */ 47131ae08745Sheppo *ccount = memseg->ncookies; 47141ae08745Sheppo cookie->addr = memseg->cookies[0].addr; 47151ae08745Sheppo cookie->size = memseg->cookies[0].size; 47161ae08745Sheppo 47171ae08745Sheppo D1(ldcp->id, 47181ae08745Sheppo "ldc_mem_bind_handle: (0x%llx) bound 0x%llx, va=0x%llx, " 47191ae08745Sheppo "pgs=0x%llx cookies=0x%llx\n", 47201ae08745Sheppo ldcp->id, mhdl, vaddr, npages, memseg->ncookies); 47211ae08745Sheppo 47221ae08745Sheppo mutex_exit(&mhdl->lock); 47231ae08745Sheppo return (0); 47241ae08745Sheppo } 47251ae08745Sheppo 47261ae08745Sheppo /* 47271ae08745Sheppo * Return the next cookie associated with the specified memory handle 47281ae08745Sheppo */ 47291ae08745Sheppo int 47301ae08745Sheppo ldc_mem_nextcookie(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie) 47311ae08745Sheppo { 47321ae08745Sheppo ldc_mhdl_t *mhdl; 47331ae08745Sheppo ldc_chan_t *ldcp; 47341ae08745Sheppo ldc_memseg_t *memseg; 47351ae08745Sheppo 47361ae08745Sheppo if (mhandle == NULL) { 47371ae08745Sheppo DWARN(DBG_ALL_LDCS, 47381ae08745Sheppo "ldc_mem_nextcookie: invalid memory handle\n"); 47391ae08745Sheppo return (EINVAL); 47401ae08745Sheppo } 47411ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 47421ae08745Sheppo 47431ae08745Sheppo mutex_enter(&mhdl->lock); 47441ae08745Sheppo 47451ae08745Sheppo ldcp = mhdl->ldcp; 47461ae08745Sheppo memseg = mhdl->memseg; 47471ae08745Sheppo 47481ae08745Sheppo if (cookie == 0) { 47491ae08745Sheppo DWARN(ldcp->id, 47501ae08745Sheppo "ldc_mem_nextcookie:(0x%llx) invalid cookie arg\n", 47511ae08745Sheppo ldcp->id); 47521ae08745Sheppo mutex_exit(&mhdl->lock); 47531ae08745Sheppo return (EINVAL); 47541ae08745Sheppo } 47551ae08745Sheppo 47561ae08745Sheppo if (memseg->next_cookie != 0) { 47571ae08745Sheppo cookie->addr = memseg->cookies[memseg->next_cookie].addr; 47581ae08745Sheppo cookie->size = memseg->cookies[memseg->next_cookie].size; 47591ae08745Sheppo memseg->next_cookie++; 47601ae08745Sheppo if (memseg->next_cookie == memseg->ncookies) 47611ae08745Sheppo memseg->next_cookie = 0; 47621ae08745Sheppo 47631ae08745Sheppo } else { 47641ae08745Sheppo DWARN(ldcp->id, 47651ae08745Sheppo "ldc_mem_nextcookie:(0x%llx) no more cookies\n", ldcp->id); 47661ae08745Sheppo cookie->addr = 0; 47671ae08745Sheppo cookie->size = 0; 47681ae08745Sheppo mutex_exit(&mhdl->lock); 47691ae08745Sheppo return (EINVAL); 47701ae08745Sheppo } 47711ae08745Sheppo 47721ae08745Sheppo D1(ldcp->id, 47731ae08745Sheppo "ldc_mem_nextcookie: (0x%llx) cookie addr=0x%llx,sz=0x%llx\n", 47741ae08745Sheppo ldcp->id, cookie->addr, cookie->size); 47751ae08745Sheppo 47761ae08745Sheppo mutex_exit(&mhdl->lock); 47771ae08745Sheppo return (0); 47781ae08745Sheppo } 47791ae08745Sheppo 47801ae08745Sheppo /* 47811ae08745Sheppo * Unbind the virtual memory region associated with the specified 47821ae08745Sheppo * memory handle. Allassociated cookies are freed and the corresponding 47831ae08745Sheppo * RA space is no longer exported. 47841ae08745Sheppo */ 47851ae08745Sheppo int 47861ae08745Sheppo ldc_mem_unbind_handle(ldc_mem_handle_t mhandle) 47871ae08745Sheppo { 47881ae08745Sheppo ldc_mhdl_t *mhdl; 47891ae08745Sheppo ldc_chan_t *ldcp; 47901ae08745Sheppo ldc_mtbl_t *mtbl; 47911ae08745Sheppo ldc_memseg_t *memseg; 47924bac2208Snarayan uint64_t cookie_addr; 47934bac2208Snarayan uint64_t pg_shift, pg_size_code; 47944bac2208Snarayan int i, rv; 47951ae08745Sheppo 47961ae08745Sheppo if (mhandle == NULL) { 47971ae08745Sheppo DWARN(DBG_ALL_LDCS, 47981ae08745Sheppo "ldc_mem_unbind_handle: invalid memory handle\n"); 47991ae08745Sheppo return (EINVAL); 48001ae08745Sheppo } 48011ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 48021ae08745Sheppo 48031ae08745Sheppo mutex_enter(&mhdl->lock); 48041ae08745Sheppo 48051ae08745Sheppo if (mhdl->status == LDC_UNBOUND) { 48061ae08745Sheppo DWARN(DBG_ALL_LDCS, 48071ae08745Sheppo "ldc_mem_unbind_handle: (0x%x) handle is not bound\n", 48081ae08745Sheppo mhandle); 48091ae08745Sheppo mutex_exit(&mhdl->lock); 48101ae08745Sheppo return (EINVAL); 48111ae08745Sheppo } 48121ae08745Sheppo 48131ae08745Sheppo ldcp = mhdl->ldcp; 48141ae08745Sheppo mtbl = ldcp->mtbl; 48151ae08745Sheppo 48161ae08745Sheppo memseg = mhdl->memseg; 48171ae08745Sheppo 48181ae08745Sheppo /* lock the memory table - exclusive access to channel */ 48191ae08745Sheppo mutex_enter(&mtbl->lock); 48201ae08745Sheppo 48211ae08745Sheppo /* undo the pages exported */ 48221ae08745Sheppo for (i = 0; i < memseg->npages; i++) { 48231ae08745Sheppo 48244bac2208Snarayan /* check for mapped pages, revocation cookie != 0 */ 48251ae08745Sheppo if (memseg->pages[i].mte->cookie) { 48264bac2208Snarayan 48274bac2208Snarayan pg_size_code = page_szc(memseg->pages[i].size); 48284bac2208Snarayan pg_shift = page_get_shift(memseg->pages[i].size); 48294bac2208Snarayan cookie_addr = IDX2COOKIE(memseg->pages[i].index, 48304bac2208Snarayan pg_size_code, pg_shift); 48314bac2208Snarayan 48324bac2208Snarayan D1(ldcp->id, "ldc_mem_unbind_handle: (0x%llx) revoke " 48334bac2208Snarayan "cookie 0x%llx, rcookie 0x%llx\n", ldcp->id, 48344bac2208Snarayan cookie_addr, memseg->pages[i].mte->cookie); 48354bac2208Snarayan rv = hv_ldc_revoke(ldcp->id, cookie_addr, 48364bac2208Snarayan memseg->pages[i].mte->cookie); 48374bac2208Snarayan if (rv) { 48384bac2208Snarayan DWARN(ldcp->id, 48394bac2208Snarayan "ldc_mem_unbind_handle: (0x%llx) cannot " 48404bac2208Snarayan "revoke mapping, cookie %llx\n", ldcp->id, 48414bac2208Snarayan cookie_addr); 48424bac2208Snarayan } 48431ae08745Sheppo } 48441ae08745Sheppo 48451ae08745Sheppo /* clear the entry from the table */ 48461ae08745Sheppo memseg->pages[i].mte->entry.ll = 0; 48471ae08745Sheppo mtbl->num_avail++; 48481ae08745Sheppo } 48491ae08745Sheppo mutex_exit(&mtbl->lock); 48501ae08745Sheppo 48511ae08745Sheppo /* free the allocated memseg and page structures */ 48521ae08745Sheppo kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages)); 48531ae08745Sheppo kmem_free(memseg->cookies, 48541ae08745Sheppo (sizeof (ldc_mem_cookie_t) * memseg->npages)); 48554bac2208Snarayan kmem_cache_free(ldcssp->memseg_cache, memseg); 48561ae08745Sheppo 48571ae08745Sheppo /* uninitialize the memory handle */ 48581ae08745Sheppo mhdl->memseg = NULL; 48591ae08745Sheppo mhdl->status = LDC_UNBOUND; 48601ae08745Sheppo 48611ae08745Sheppo D1(ldcp->id, "ldc_mem_unbind_handle: (0x%llx) unbound handle 0x%llx\n", 48621ae08745Sheppo ldcp->id, mhdl); 48631ae08745Sheppo 48641ae08745Sheppo mutex_exit(&mhdl->lock); 48651ae08745Sheppo return (0); 48661ae08745Sheppo } 48671ae08745Sheppo 48681ae08745Sheppo /* 48691ae08745Sheppo * Get information about the dring. The base address of the descriptor 48701ae08745Sheppo * ring along with the type and permission are returned back. 48711ae08745Sheppo */ 48721ae08745Sheppo int 48731ae08745Sheppo ldc_mem_info(ldc_mem_handle_t mhandle, ldc_mem_info_t *minfo) 48741ae08745Sheppo { 48751ae08745Sheppo ldc_mhdl_t *mhdl; 48761ae08745Sheppo 48771ae08745Sheppo if (mhandle == NULL) { 48781ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid memory handle\n"); 48791ae08745Sheppo return (EINVAL); 48801ae08745Sheppo } 48811ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 48821ae08745Sheppo 48831ae08745Sheppo if (minfo == NULL) { 48841ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid args\n"); 48851ae08745Sheppo return (EINVAL); 48861ae08745Sheppo } 48871ae08745Sheppo 48881ae08745Sheppo mutex_enter(&mhdl->lock); 48891ae08745Sheppo 48901ae08745Sheppo minfo->status = mhdl->status; 48911ae08745Sheppo if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) { 48921ae08745Sheppo minfo->vaddr = mhdl->memseg->vaddr; 48931ae08745Sheppo minfo->raddr = mhdl->memseg->raddr; 48941ae08745Sheppo minfo->mtype = mhdl->mtype; 48951ae08745Sheppo minfo->perm = mhdl->perm; 48961ae08745Sheppo } 48971ae08745Sheppo mutex_exit(&mhdl->lock); 48981ae08745Sheppo 48991ae08745Sheppo return (0); 49001ae08745Sheppo } 49011ae08745Sheppo 49021ae08745Sheppo /* 49031ae08745Sheppo * Copy data either from or to the client specified virtual address 49041ae08745Sheppo * space to or from the exported memory associated with the cookies. 49051ae08745Sheppo * The direction argument determines whether the data is read from or 49061ae08745Sheppo * written to exported memory. 49071ae08745Sheppo */ 49081ae08745Sheppo int 49091ae08745Sheppo ldc_mem_copy(ldc_handle_t handle, caddr_t vaddr, uint64_t off, size_t *size, 49101ae08745Sheppo ldc_mem_cookie_t *cookies, uint32_t ccount, uint8_t direction) 49111ae08745Sheppo { 49121ae08745Sheppo ldc_chan_t *ldcp; 49131ae08745Sheppo uint64_t local_voff, local_valign; 49141ae08745Sheppo uint64_t cookie_addr, cookie_size; 49151ae08745Sheppo uint64_t pg_shift, pg_size, pg_size_code; 49161ae08745Sheppo uint64_t export_caddr, export_poff, export_psize, export_size; 49171ae08745Sheppo uint64_t local_ra, local_poff, local_psize; 49181ae08745Sheppo uint64_t copy_size, copied_len = 0, total_bal = 0, idx = 0; 49191ae08745Sheppo pgcnt_t npages; 49201ae08745Sheppo size_t len = *size; 49211ae08745Sheppo int i, rv = 0; 49221ae08745Sheppo 49233af08d82Slm66018 uint64_t chid; 49243af08d82Slm66018 49251ae08745Sheppo if (handle == NULL) { 49261ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_copy: invalid channel handle\n"); 49271ae08745Sheppo return (EINVAL); 49281ae08745Sheppo } 49291ae08745Sheppo ldcp = (ldc_chan_t *)handle; 49303af08d82Slm66018 chid = ldcp->id; 49311ae08745Sheppo 49321ae08745Sheppo /* check to see if channel is UP */ 49331ae08745Sheppo if (ldcp->tstate != TS_UP) { 49343af08d82Slm66018 DWARN(chid, "ldc_mem_copy: (0x%llx) channel is not UP\n", 49353af08d82Slm66018 chid); 49363af08d82Slm66018 return (ECONNRESET); 49371ae08745Sheppo } 49381ae08745Sheppo 49391ae08745Sheppo /* Force address and size to be 8-byte aligned */ 49401ae08745Sheppo if ((((uintptr_t)vaddr | len) & 0x7) != 0) { 49413af08d82Slm66018 DWARN(chid, 49421ae08745Sheppo "ldc_mem_copy: addr/sz is not 8-byte aligned\n"); 49431ae08745Sheppo return (EINVAL); 49441ae08745Sheppo } 49451ae08745Sheppo 49461ae08745Sheppo /* Find the size of the exported memory */ 49471ae08745Sheppo export_size = 0; 49481ae08745Sheppo for (i = 0; i < ccount; i++) 49491ae08745Sheppo export_size += cookies[i].size; 49501ae08745Sheppo 49511ae08745Sheppo /* check to see if offset is valid */ 49521ae08745Sheppo if (off > export_size) { 49533af08d82Slm66018 DWARN(chid, 49541ae08745Sheppo "ldc_mem_copy: (0x%llx) start offset > export mem size\n", 49553af08d82Slm66018 chid); 49561ae08745Sheppo return (EINVAL); 49571ae08745Sheppo } 49581ae08745Sheppo 49591ae08745Sheppo /* 49601ae08745Sheppo * Check to see if the export size is smaller than the size we 49611ae08745Sheppo * are requesting to copy - if so flag an error 49621ae08745Sheppo */ 49631ae08745Sheppo if ((export_size - off) < *size) { 49643af08d82Slm66018 DWARN(chid, 49651ae08745Sheppo "ldc_mem_copy: (0x%llx) copy size > export mem size\n", 49663af08d82Slm66018 chid); 49671ae08745Sheppo return (EINVAL); 49681ae08745Sheppo } 49691ae08745Sheppo 49701ae08745Sheppo total_bal = min(export_size, *size); 49711ae08745Sheppo 49721ae08745Sheppo /* FUTURE: get the page size, pgsz code, and shift */ 49731ae08745Sheppo pg_size = MMU_PAGESIZE; 49741ae08745Sheppo pg_size_code = page_szc(pg_size); 49751ae08745Sheppo pg_shift = page_get_shift(pg_size_code); 49761ae08745Sheppo 49773af08d82Slm66018 D1(chid, "ldc_mem_copy: copying data " 49781ae08745Sheppo "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n", 49793af08d82Slm66018 chid, vaddr, pg_size, pg_size_code, pg_shift); 49801ae08745Sheppo 49811ae08745Sheppo /* aligned VA and its offset */ 49821ae08745Sheppo local_valign = (((uintptr_t)vaddr) & ~(pg_size - 1)); 49831ae08745Sheppo local_voff = ((uintptr_t)vaddr) & (pg_size - 1); 49841ae08745Sheppo 49851ae08745Sheppo npages = (len+local_voff)/pg_size; 49861ae08745Sheppo npages = ((len+local_voff)%pg_size == 0) ? npages : npages+1; 49871ae08745Sheppo 49883af08d82Slm66018 D1(chid, 49891ae08745Sheppo "ldc_mem_copy: (0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n", 49903af08d82Slm66018 chid, vaddr, local_valign, local_voff, npages); 49911ae08745Sheppo 49921ae08745Sheppo local_ra = va_to_pa((void *)local_valign); 49931ae08745Sheppo local_poff = local_voff; 49941ae08745Sheppo local_psize = min(len, (pg_size - local_voff)); 49951ae08745Sheppo 49961ae08745Sheppo len -= local_psize; 49971ae08745Sheppo 49981ae08745Sheppo /* 49991ae08745Sheppo * find the first cookie in the list of cookies 50001ae08745Sheppo * if the offset passed in is not zero 50011ae08745Sheppo */ 50021ae08745Sheppo for (idx = 0; idx < ccount; idx++) { 50031ae08745Sheppo cookie_size = cookies[idx].size; 50041ae08745Sheppo if (off < cookie_size) 50051ae08745Sheppo break; 50061ae08745Sheppo off -= cookie_size; 50071ae08745Sheppo } 50081ae08745Sheppo 50091ae08745Sheppo cookie_addr = cookies[idx].addr + off; 50101ae08745Sheppo cookie_size = cookies[idx].size - off; 50111ae08745Sheppo 50121ae08745Sheppo export_caddr = cookie_addr & ~(pg_size - 1); 50131ae08745Sheppo export_poff = cookie_addr & (pg_size - 1); 50141ae08745Sheppo export_psize = min(cookie_size, (pg_size - export_poff)); 50151ae08745Sheppo 50161ae08745Sheppo for (;;) { 50171ae08745Sheppo 50181ae08745Sheppo copy_size = min(export_psize, local_psize); 50191ae08745Sheppo 50203af08d82Slm66018 D1(chid, 50211ae08745Sheppo "ldc_mem_copy:(0x%llx) dir=0x%x, caddr=0x%llx," 50221ae08745Sheppo " loc_ra=0x%llx, exp_poff=0x%llx, loc_poff=0x%llx," 50231ae08745Sheppo " exp_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx," 50241ae08745Sheppo " total_bal=0x%llx\n", 50253af08d82Slm66018 chid, direction, export_caddr, local_ra, export_poff, 50261ae08745Sheppo local_poff, export_psize, local_psize, copy_size, 50271ae08745Sheppo total_bal); 50281ae08745Sheppo 50293af08d82Slm66018 rv = hv_ldc_copy(chid, direction, 50301ae08745Sheppo (export_caddr + export_poff), (local_ra + local_poff), 50311ae08745Sheppo copy_size, &copied_len); 50321ae08745Sheppo 50331ae08745Sheppo if (rv != 0) { 50343af08d82Slm66018 int error = EIO; 50353af08d82Slm66018 uint64_t rx_hd, rx_tl; 50363af08d82Slm66018 50373af08d82Slm66018 DWARN(chid, 50383af08d82Slm66018 "ldc_mem_copy: (0x%llx) err %d during copy\n", 50393af08d82Slm66018 (unsigned long long)chid, rv); 50403af08d82Slm66018 DWARN(chid, 50413af08d82Slm66018 "ldc_mem_copy: (0x%llx) dir=0x%x, caddr=0x%lx, " 50424bac2208Snarayan "loc_ra=0x%lx, exp_poff=0x%lx, loc_poff=0x%lx," 50434bac2208Snarayan " exp_psz=0x%lx, loc_psz=0x%lx, copy_sz=0x%lx," 50444bac2208Snarayan " copied_len=0x%lx, total_bal=0x%lx\n", 50453af08d82Slm66018 chid, direction, export_caddr, local_ra, 50461ae08745Sheppo export_poff, local_poff, export_psize, local_psize, 50471ae08745Sheppo copy_size, copied_len, total_bal); 50481ae08745Sheppo 50491ae08745Sheppo *size = *size - total_bal; 50503af08d82Slm66018 50513af08d82Slm66018 /* 50523af08d82Slm66018 * check if reason for copy error was due to 50533af08d82Slm66018 * a channel reset. we need to grab the lock 50543af08d82Slm66018 * just in case we have to do a reset. 50553af08d82Slm66018 */ 50563af08d82Slm66018 mutex_enter(&ldcp->lock); 50573af08d82Slm66018 mutex_enter(&ldcp->tx_lock); 50583af08d82Slm66018 50593af08d82Slm66018 rv = hv_ldc_rx_get_state(ldcp->id, 50603af08d82Slm66018 &rx_hd, &rx_tl, &(ldcp->link_state)); 50613af08d82Slm66018 if (ldcp->link_state == LDC_CHANNEL_DOWN || 50623af08d82Slm66018 ldcp->link_state == LDC_CHANNEL_RESET) { 50633af08d82Slm66018 i_ldc_reset(ldcp, B_FALSE); 50643af08d82Slm66018 error = ECONNRESET; 50653af08d82Slm66018 } 50663af08d82Slm66018 50673af08d82Slm66018 mutex_exit(&ldcp->tx_lock); 50681ae08745Sheppo mutex_exit(&ldcp->lock); 50693af08d82Slm66018 50703af08d82Slm66018 return (error); 50711ae08745Sheppo } 50721ae08745Sheppo 50731ae08745Sheppo ASSERT(copied_len <= copy_size); 50741ae08745Sheppo 50753af08d82Slm66018 D2(chid, "ldc_mem_copy: copied=0x%llx\n", copied_len); 50761ae08745Sheppo export_poff += copied_len; 50771ae08745Sheppo local_poff += copied_len; 50781ae08745Sheppo export_psize -= copied_len; 50791ae08745Sheppo local_psize -= copied_len; 50801ae08745Sheppo cookie_size -= copied_len; 50811ae08745Sheppo 50821ae08745Sheppo total_bal -= copied_len; 50831ae08745Sheppo 50841ae08745Sheppo if (copy_size != copied_len) 50851ae08745Sheppo continue; 50861ae08745Sheppo 50871ae08745Sheppo if (export_psize == 0 && total_bal != 0) { 50881ae08745Sheppo 50891ae08745Sheppo if (cookie_size == 0) { 50901ae08745Sheppo idx++; 50911ae08745Sheppo cookie_addr = cookies[idx].addr; 50921ae08745Sheppo cookie_size = cookies[idx].size; 50931ae08745Sheppo 50941ae08745Sheppo export_caddr = cookie_addr & ~(pg_size - 1); 50951ae08745Sheppo export_poff = cookie_addr & (pg_size - 1); 50961ae08745Sheppo export_psize = 50971ae08745Sheppo min(cookie_size, (pg_size-export_poff)); 50981ae08745Sheppo } else { 50991ae08745Sheppo export_caddr += pg_size; 51001ae08745Sheppo export_poff = 0; 51011ae08745Sheppo export_psize = min(cookie_size, pg_size); 51021ae08745Sheppo } 51031ae08745Sheppo } 51041ae08745Sheppo 51051ae08745Sheppo if (local_psize == 0 && total_bal != 0) { 51061ae08745Sheppo local_valign += pg_size; 51071ae08745Sheppo local_ra = va_to_pa((void *)local_valign); 51081ae08745Sheppo local_poff = 0; 51091ae08745Sheppo local_psize = min(pg_size, len); 51101ae08745Sheppo len -= local_psize; 51111ae08745Sheppo } 51121ae08745Sheppo 51131ae08745Sheppo /* check if we are all done */ 51141ae08745Sheppo if (total_bal == 0) 51151ae08745Sheppo break; 51161ae08745Sheppo } 51171ae08745Sheppo 51181ae08745Sheppo 51193af08d82Slm66018 D1(chid, 51201ae08745Sheppo "ldc_mem_copy: (0x%llx) done copying sz=0x%llx\n", 51213af08d82Slm66018 chid, *size); 51221ae08745Sheppo 51231ae08745Sheppo return (0); 51241ae08745Sheppo } 51251ae08745Sheppo 51261ae08745Sheppo /* 51271ae08745Sheppo * Copy data either from or to the client specified virtual address 51281ae08745Sheppo * space to or from HV physical memory. 51291ae08745Sheppo * 51301ae08745Sheppo * The direction argument determines whether the data is read from or 51311ae08745Sheppo * written to HV memory. direction values are LDC_COPY_IN/OUT similar 51321ae08745Sheppo * to the ldc_mem_copy interface 51331ae08745Sheppo */ 51341ae08745Sheppo int 51353af08d82Slm66018 ldc_mem_rdwr_cookie(ldc_handle_t handle, caddr_t vaddr, size_t *size, 51361ae08745Sheppo caddr_t paddr, uint8_t direction) 51371ae08745Sheppo { 51381ae08745Sheppo ldc_chan_t *ldcp; 51391ae08745Sheppo uint64_t local_voff, local_valign; 51401ae08745Sheppo uint64_t pg_shift, pg_size, pg_size_code; 51411ae08745Sheppo uint64_t target_pa, target_poff, target_psize, target_size; 51421ae08745Sheppo uint64_t local_ra, local_poff, local_psize; 51431ae08745Sheppo uint64_t copy_size, copied_len = 0; 51441ae08745Sheppo pgcnt_t npages; 51451ae08745Sheppo size_t len = *size; 51461ae08745Sheppo int rv = 0; 51471ae08745Sheppo 51481ae08745Sheppo if (handle == NULL) { 51491ae08745Sheppo DWARN(DBG_ALL_LDCS, 51503af08d82Slm66018 "ldc_mem_rdwr_cookie: invalid channel handle\n"); 51511ae08745Sheppo return (EINVAL); 51521ae08745Sheppo } 51531ae08745Sheppo ldcp = (ldc_chan_t *)handle; 51541ae08745Sheppo 51551ae08745Sheppo mutex_enter(&ldcp->lock); 51561ae08745Sheppo 51571ae08745Sheppo /* check to see if channel is UP */ 51581ae08745Sheppo if (ldcp->tstate != TS_UP) { 51591ae08745Sheppo DWARN(ldcp->id, 51603af08d82Slm66018 "ldc_mem_rdwr_cookie: (0x%llx) channel is not UP\n", 51611ae08745Sheppo ldcp->id); 51621ae08745Sheppo mutex_exit(&ldcp->lock); 51633af08d82Slm66018 return (ECONNRESET); 51641ae08745Sheppo } 51651ae08745Sheppo 51661ae08745Sheppo /* Force address and size to be 8-byte aligned */ 51671ae08745Sheppo if ((((uintptr_t)vaddr | len) & 0x7) != 0) { 51681ae08745Sheppo DWARN(ldcp->id, 51693af08d82Slm66018 "ldc_mem_rdwr_cookie: addr/size is not 8-byte aligned\n"); 51701ae08745Sheppo mutex_exit(&ldcp->lock); 51711ae08745Sheppo return (EINVAL); 51721ae08745Sheppo } 51731ae08745Sheppo 51741ae08745Sheppo target_size = *size; 51751ae08745Sheppo 51761ae08745Sheppo /* FUTURE: get the page size, pgsz code, and shift */ 51771ae08745Sheppo pg_size = MMU_PAGESIZE; 51781ae08745Sheppo pg_size_code = page_szc(pg_size); 51791ae08745Sheppo pg_shift = page_get_shift(pg_size_code); 51801ae08745Sheppo 51813af08d82Slm66018 D1(ldcp->id, "ldc_mem_rdwr_cookie: copying data " 51821ae08745Sheppo "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n", 51831ae08745Sheppo ldcp->id, vaddr, pg_size, pg_size_code, pg_shift); 51841ae08745Sheppo 51851ae08745Sheppo /* aligned VA and its offset */ 51861ae08745Sheppo local_valign = ((uintptr_t)vaddr) & ~(pg_size - 1); 51871ae08745Sheppo local_voff = ((uintptr_t)vaddr) & (pg_size - 1); 51881ae08745Sheppo 51891ae08745Sheppo npages = (len + local_voff) / pg_size; 51901ae08745Sheppo npages = ((len + local_voff) % pg_size == 0) ? npages : npages+1; 51911ae08745Sheppo 51923af08d82Slm66018 D1(ldcp->id, "ldc_mem_rdwr_cookie: (0x%llx) v=0x%llx, " 51933af08d82Slm66018 "val=0x%llx,off=0x%x,pgs=0x%x\n", 51941ae08745Sheppo ldcp->id, vaddr, local_valign, local_voff, npages); 51951ae08745Sheppo 51961ae08745Sheppo local_ra = va_to_pa((void *)local_valign); 51971ae08745Sheppo local_poff = local_voff; 51981ae08745Sheppo local_psize = min(len, (pg_size - local_voff)); 51991ae08745Sheppo 52001ae08745Sheppo len -= local_psize; 52011ae08745Sheppo 52021ae08745Sheppo target_pa = ((uintptr_t)paddr) & ~(pg_size - 1); 52031ae08745Sheppo target_poff = ((uintptr_t)paddr) & (pg_size - 1); 52041ae08745Sheppo target_psize = pg_size - target_poff; 52051ae08745Sheppo 52061ae08745Sheppo for (;;) { 52071ae08745Sheppo 52081ae08745Sheppo copy_size = min(target_psize, local_psize); 52091ae08745Sheppo 52101ae08745Sheppo D1(ldcp->id, 52113af08d82Slm66018 "ldc_mem_rdwr_cookie: (0x%llx) dir=0x%x, tar_pa=0x%llx," 52121ae08745Sheppo " loc_ra=0x%llx, tar_poff=0x%llx, loc_poff=0x%llx," 52131ae08745Sheppo " tar_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx," 52141ae08745Sheppo " total_bal=0x%llx\n", 52151ae08745Sheppo ldcp->id, direction, target_pa, local_ra, target_poff, 52161ae08745Sheppo local_poff, target_psize, local_psize, copy_size, 52171ae08745Sheppo target_size); 52181ae08745Sheppo 52191ae08745Sheppo rv = hv_ldc_copy(ldcp->id, direction, 52201ae08745Sheppo (target_pa + target_poff), (local_ra + local_poff), 52211ae08745Sheppo copy_size, &copied_len); 52221ae08745Sheppo 52231ae08745Sheppo if (rv != 0) { 52243af08d82Slm66018 DWARN(DBG_ALL_LDCS, 52253af08d82Slm66018 "ldc_mem_rdwr_cookie: (0x%lx) err %d during copy\n", 52261ae08745Sheppo ldcp->id, rv); 52271ae08745Sheppo DWARN(DBG_ALL_LDCS, 52283af08d82Slm66018 "ldc_mem_rdwr_cookie: (0x%llx) dir=%lld, " 52293af08d82Slm66018 "tar_pa=0x%llx, loc_ra=0x%llx, tar_poff=0x%llx, " 52303af08d82Slm66018 "loc_poff=0x%llx, tar_psz=0x%llx, loc_psz=0x%llx, " 52313af08d82Slm66018 "copy_sz=0x%llx, total_bal=0x%llx\n", 52321ae08745Sheppo ldcp->id, direction, target_pa, local_ra, 52331ae08745Sheppo target_poff, local_poff, target_psize, local_psize, 52341ae08745Sheppo copy_size, target_size); 52351ae08745Sheppo 52361ae08745Sheppo *size = *size - target_size; 52371ae08745Sheppo mutex_exit(&ldcp->lock); 52381ae08745Sheppo return (i_ldc_h2v_error(rv)); 52391ae08745Sheppo } 52401ae08745Sheppo 52413af08d82Slm66018 D2(ldcp->id, "ldc_mem_rdwr_cookie: copied=0x%llx\n", 52423af08d82Slm66018 copied_len); 52431ae08745Sheppo target_poff += copied_len; 52441ae08745Sheppo local_poff += copied_len; 52451ae08745Sheppo target_psize -= copied_len; 52461ae08745Sheppo local_psize -= copied_len; 52471ae08745Sheppo 52481ae08745Sheppo target_size -= copied_len; 52491ae08745Sheppo 52501ae08745Sheppo if (copy_size != copied_len) 52511ae08745Sheppo continue; 52521ae08745Sheppo 52531ae08745Sheppo if (target_psize == 0 && target_size != 0) { 52541ae08745Sheppo target_pa += pg_size; 52551ae08745Sheppo target_poff = 0; 52561ae08745Sheppo target_psize = min(pg_size, target_size); 52571ae08745Sheppo } 52581ae08745Sheppo 52591ae08745Sheppo if (local_psize == 0 && target_size != 0) { 52601ae08745Sheppo local_valign += pg_size; 52611ae08745Sheppo local_ra = va_to_pa((void *)local_valign); 52621ae08745Sheppo local_poff = 0; 52631ae08745Sheppo local_psize = min(pg_size, len); 52641ae08745Sheppo len -= local_psize; 52651ae08745Sheppo } 52661ae08745Sheppo 52671ae08745Sheppo /* check if we are all done */ 52681ae08745Sheppo if (target_size == 0) 52691ae08745Sheppo break; 52701ae08745Sheppo } 52711ae08745Sheppo 52721ae08745Sheppo mutex_exit(&ldcp->lock); 52731ae08745Sheppo 52743af08d82Slm66018 D1(ldcp->id, "ldc_mem_rdwr_cookie: (0x%llx) done copying sz=0x%llx\n", 52751ae08745Sheppo ldcp->id, *size); 52761ae08745Sheppo 52771ae08745Sheppo return (0); 52781ae08745Sheppo } 52791ae08745Sheppo 52801ae08745Sheppo /* 52811ae08745Sheppo * Map an exported memory segment into the local address space. If the 52821ae08745Sheppo * memory range was exported for direct map access, a HV call is made 52831ae08745Sheppo * to allocate a RA range. If the map is done via a shadow copy, local 52841ae08745Sheppo * shadow memory is allocated and the base VA is returned in 'vaddr'. If 52851ae08745Sheppo * the mapping is a direct map then the RA is returned in 'raddr'. 52861ae08745Sheppo */ 52871ae08745Sheppo int 52881ae08745Sheppo ldc_mem_map(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie, uint32_t ccount, 52894bac2208Snarayan uint8_t mtype, uint8_t perm, caddr_t *vaddr, caddr_t *raddr) 52901ae08745Sheppo { 52914bac2208Snarayan int i, j, idx, rv, retries; 52921ae08745Sheppo ldc_chan_t *ldcp; 52931ae08745Sheppo ldc_mhdl_t *mhdl; 52941ae08745Sheppo ldc_memseg_t *memseg; 52954bac2208Snarayan caddr_t tmpaddr; 52964bac2208Snarayan uint64_t map_perm = perm; 52974bac2208Snarayan uint64_t pg_size, pg_shift, pg_size_code, pg_mask; 52984bac2208Snarayan uint64_t exp_size = 0, base_off, map_size, npages; 52994bac2208Snarayan uint64_t cookie_addr, cookie_off, cookie_size; 53004bac2208Snarayan tte_t ldc_tte; 53011ae08745Sheppo 53021ae08745Sheppo if (mhandle == NULL) { 53031ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_map: invalid memory handle\n"); 53041ae08745Sheppo return (EINVAL); 53051ae08745Sheppo } 53061ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 53071ae08745Sheppo 53081ae08745Sheppo mutex_enter(&mhdl->lock); 53091ae08745Sheppo 53101ae08745Sheppo if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED || 53111ae08745Sheppo mhdl->memseg != NULL) { 53121ae08745Sheppo DWARN(DBG_ALL_LDCS, 53131ae08745Sheppo "ldc_mem_map: (0x%llx) handle bound/mapped\n", mhandle); 53141ae08745Sheppo mutex_exit(&mhdl->lock); 53151ae08745Sheppo return (EINVAL); 53161ae08745Sheppo } 53171ae08745Sheppo 53181ae08745Sheppo ldcp = mhdl->ldcp; 53191ae08745Sheppo 53201ae08745Sheppo mutex_enter(&ldcp->lock); 53211ae08745Sheppo 53221ae08745Sheppo if (ldcp->tstate != TS_UP) { 53231ae08745Sheppo DWARN(ldcp->id, 53241ae08745Sheppo "ldc_mem_dring_map: (0x%llx) channel is not UP\n", 53251ae08745Sheppo ldcp->id); 53261ae08745Sheppo mutex_exit(&ldcp->lock); 53271ae08745Sheppo mutex_exit(&mhdl->lock); 53283af08d82Slm66018 return (ECONNRESET); 53291ae08745Sheppo } 53301ae08745Sheppo 53311ae08745Sheppo if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) { 53321ae08745Sheppo DWARN(ldcp->id, "ldc_mem_map: invalid map type\n"); 53331ae08745Sheppo mutex_exit(&ldcp->lock); 53341ae08745Sheppo mutex_exit(&mhdl->lock); 53351ae08745Sheppo return (EINVAL); 53361ae08745Sheppo } 53371ae08745Sheppo 53381ae08745Sheppo D1(ldcp->id, "ldc_mem_map: (0x%llx) cookie = 0x%llx,0x%llx\n", 5339d10e4ef2Snarayan ldcp->id, cookie->addr, cookie->size); 53401ae08745Sheppo 53411ae08745Sheppo /* FUTURE: get the page size, pgsz code, and shift */ 53421ae08745Sheppo pg_size = MMU_PAGESIZE; 53431ae08745Sheppo pg_size_code = page_szc(pg_size); 53441ae08745Sheppo pg_shift = page_get_shift(pg_size_code); 53454bac2208Snarayan pg_mask = ~(pg_size - 1); 53461ae08745Sheppo 53471ae08745Sheppo /* calculate the number of pages in the exported cookie */ 53484bac2208Snarayan base_off = cookie[0].addr & (pg_size - 1); 53494bac2208Snarayan for (idx = 0; idx < ccount; idx++) 53501ae08745Sheppo exp_size += cookie[idx].size; 53514bac2208Snarayan map_size = P2ROUNDUP((exp_size + base_off), pg_size); 53524bac2208Snarayan npages = (map_size >> pg_shift); 53531ae08745Sheppo 53541ae08745Sheppo /* Allocate memseg structure */ 53554bac2208Snarayan memseg = mhdl->memseg = 53564bac2208Snarayan kmem_cache_alloc(ldcssp->memseg_cache, KM_SLEEP); 53571ae08745Sheppo 53581ae08745Sheppo /* Allocate memory to store all pages and cookies */ 53591ae08745Sheppo memseg->pages = kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP); 53601ae08745Sheppo memseg->cookies = 53611ae08745Sheppo kmem_zalloc((sizeof (ldc_mem_cookie_t) * ccount), KM_SLEEP); 53621ae08745Sheppo 53634bac2208Snarayan D2(ldcp->id, "ldc_mem_map: (0x%llx) exp_size=0x%llx, map_size=0x%llx," 53644bac2208Snarayan "pages=0x%llx\n", ldcp->id, exp_size, map_size, npages); 53651ae08745Sheppo 53664bac2208Snarayan /* 53674bac2208Snarayan * Check if direct map over shared memory is enabled, if not change 53684bac2208Snarayan * the mapping type to SHADOW_MAP. 53694bac2208Snarayan */ 53704bac2208Snarayan if (ldc_shmem_enabled == 0) 53714bac2208Snarayan mtype = LDC_SHADOW_MAP; 53724bac2208Snarayan 53734bac2208Snarayan /* 53744bac2208Snarayan * Check to see if the client is requesting direct or shadow map 53754bac2208Snarayan * If direct map is requested, try to map remote memory first, 53764bac2208Snarayan * and if that fails, revert to shadow map 53774bac2208Snarayan */ 53784bac2208Snarayan if (mtype == LDC_DIRECT_MAP) { 53794bac2208Snarayan 53804bac2208Snarayan /* Allocate kernel virtual space for mapping */ 53814bac2208Snarayan memseg->vaddr = vmem_xalloc(heap_arena, map_size, 53824bac2208Snarayan pg_size, 0, 0, NULL, NULL, VM_NOSLEEP); 53834bac2208Snarayan if (memseg->vaddr == NULL) { 53844bac2208Snarayan cmn_err(CE_WARN, 53854bac2208Snarayan "ldc_mem_map: (0x%lx) memory map failed\n", 53864bac2208Snarayan ldcp->id); 53874bac2208Snarayan kmem_free(memseg->cookies, 53884bac2208Snarayan (sizeof (ldc_mem_cookie_t) * ccount)); 53894bac2208Snarayan kmem_free(memseg->pages, 53904bac2208Snarayan (sizeof (ldc_page_t) * npages)); 53914bac2208Snarayan kmem_cache_free(ldcssp->memseg_cache, memseg); 53924bac2208Snarayan 53934bac2208Snarayan mutex_exit(&ldcp->lock); 53944bac2208Snarayan mutex_exit(&mhdl->lock); 53954bac2208Snarayan return (ENOMEM); 53964bac2208Snarayan } 53974bac2208Snarayan 53984bac2208Snarayan /* Unload previous mapping */ 53994bac2208Snarayan hat_unload(kas.a_hat, memseg->vaddr, map_size, 54004bac2208Snarayan HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK); 54014bac2208Snarayan 54024bac2208Snarayan /* for each cookie passed in - map into address space */ 54034bac2208Snarayan idx = 0; 54044bac2208Snarayan cookie_size = 0; 54054bac2208Snarayan tmpaddr = memseg->vaddr; 54064bac2208Snarayan 54074bac2208Snarayan for (i = 0; i < npages; i++) { 54084bac2208Snarayan 54094bac2208Snarayan if (cookie_size == 0) { 54104bac2208Snarayan ASSERT(idx < ccount); 54114bac2208Snarayan cookie_addr = cookie[idx].addr & pg_mask; 54124bac2208Snarayan cookie_off = cookie[idx].addr & (pg_size - 1); 54134bac2208Snarayan cookie_size = 54144bac2208Snarayan P2ROUNDUP((cookie_off + cookie[idx].size), 54154bac2208Snarayan pg_size); 54164bac2208Snarayan idx++; 54174bac2208Snarayan } 54184bac2208Snarayan 54194bac2208Snarayan D1(ldcp->id, "ldc_mem_map: (0x%llx) mapping " 54204bac2208Snarayan "cookie 0x%llx, bal=0x%llx\n", ldcp->id, 54214bac2208Snarayan cookie_addr, cookie_size); 54224bac2208Snarayan 54234bac2208Snarayan /* map the cookie into address space */ 54244bac2208Snarayan for (retries = 0; retries < ldc_max_retries; 54254bac2208Snarayan retries++) { 54264bac2208Snarayan 54274bac2208Snarayan rv = hv_ldc_mapin(ldcp->id, cookie_addr, 54284bac2208Snarayan &memseg->pages[i].raddr, &map_perm); 54294bac2208Snarayan if (rv != H_EWOULDBLOCK && rv != H_ETOOMANY) 54304bac2208Snarayan break; 54314bac2208Snarayan 54324bac2208Snarayan drv_usecwait(ldc_delay); 54334bac2208Snarayan } 54344bac2208Snarayan 54354bac2208Snarayan if (rv || memseg->pages[i].raddr == 0) { 54364bac2208Snarayan DWARN(ldcp->id, 54374bac2208Snarayan "ldc_mem_map: (0x%llx) hv mapin err %d\n", 54384bac2208Snarayan ldcp->id, rv); 54394bac2208Snarayan 54404bac2208Snarayan /* remove previous mapins */ 54414bac2208Snarayan hat_unload(kas.a_hat, memseg->vaddr, map_size, 54424bac2208Snarayan HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK); 54434bac2208Snarayan for (j = 0; j < i; j++) { 54444bac2208Snarayan rv = hv_ldc_unmap( 54454bac2208Snarayan memseg->pages[j].raddr); 54464bac2208Snarayan if (rv) { 54474bac2208Snarayan DWARN(ldcp->id, 54484bac2208Snarayan "ldc_mem_map: (0x%llx) " 54494bac2208Snarayan "cannot unmap ra=0x%llx\n", 54504bac2208Snarayan ldcp->id, 54514bac2208Snarayan memseg->pages[j].raddr); 54524bac2208Snarayan } 54534bac2208Snarayan } 54544bac2208Snarayan 54554bac2208Snarayan /* free kernel virtual space */ 54564bac2208Snarayan vmem_free(heap_arena, (void *)memseg->vaddr, 54577636cb21Slm66018 map_size); 54584bac2208Snarayan 54594bac2208Snarayan /* direct map failed - revert to shadow map */ 54604bac2208Snarayan mtype = LDC_SHADOW_MAP; 54614bac2208Snarayan break; 54624bac2208Snarayan 54634bac2208Snarayan } else { 54644bac2208Snarayan 54654bac2208Snarayan D1(ldcp->id, 54664bac2208Snarayan "ldc_mem_map: (0x%llx) vtop map 0x%llx -> " 54674bac2208Snarayan "0x%llx, cookie=0x%llx, perm=0x%llx\n", 54684bac2208Snarayan ldcp->id, tmpaddr, memseg->pages[i].raddr, 54694bac2208Snarayan cookie_addr, perm); 54704bac2208Snarayan 54714bac2208Snarayan /* 54724bac2208Snarayan * NOTE: Calling hat_devload directly, causes it 54734bac2208Snarayan * to look for page_t using the pfn. Since this 54744bac2208Snarayan * addr is greater than the memlist, it treates 54754bac2208Snarayan * it as non-memory 54764bac2208Snarayan */ 54774bac2208Snarayan sfmmu_memtte(&ldc_tte, 54784bac2208Snarayan (pfn_t)(memseg->pages[i].raddr >> pg_shift), 54794bac2208Snarayan PROT_READ | PROT_WRITE | HAT_NOSYNC, TTE8K); 54804bac2208Snarayan 54814bac2208Snarayan D1(ldcp->id, 54824bac2208Snarayan "ldc_mem_map: (0x%llx) ra 0x%llx -> " 54834bac2208Snarayan "tte 0x%llx\n", ldcp->id, 54844bac2208Snarayan memseg->pages[i].raddr, ldc_tte); 54854bac2208Snarayan 54864bac2208Snarayan sfmmu_tteload(kas.a_hat, &ldc_tte, tmpaddr, 54874bac2208Snarayan NULL, HAT_LOAD_LOCK); 54884bac2208Snarayan 54894bac2208Snarayan cookie_size -= pg_size; 54904bac2208Snarayan cookie_addr += pg_size; 54914bac2208Snarayan tmpaddr += pg_size; 54924bac2208Snarayan } 54934bac2208Snarayan } 54944bac2208Snarayan } 54954bac2208Snarayan 54961ae08745Sheppo if (mtype == LDC_SHADOW_MAP) { 54971ae08745Sheppo if (*vaddr == NULL) { 54983af08d82Slm66018 memseg->vaddr = kmem_zalloc(exp_size, KM_SLEEP); 54991ae08745Sheppo mhdl->myshadow = B_TRUE; 55001ae08745Sheppo 55011ae08745Sheppo D1(ldcp->id, "ldc_mem_map: (0x%llx) allocated " 55024bac2208Snarayan "shadow page va=0x%llx\n", ldcp->id, memseg->vaddr); 55031ae08745Sheppo } else { 55041ae08745Sheppo /* 55054bac2208Snarayan * Use client supplied memory for memseg->vaddr 55061ae08745Sheppo * WARNING: assuming that client mem is >= exp_size 55071ae08745Sheppo */ 55084bac2208Snarayan memseg->vaddr = *vaddr; 55091ae08745Sheppo } 55101ae08745Sheppo 55111ae08745Sheppo /* Save all page and cookie information */ 55124bac2208Snarayan for (i = 0, tmpaddr = memseg->vaddr; i < npages; i++) { 55131ae08745Sheppo memseg->pages[i].raddr = va_to_pa(tmpaddr); 55141ae08745Sheppo memseg->pages[i].size = pg_size; 55151ae08745Sheppo tmpaddr += pg_size; 55161ae08745Sheppo } 55174bac2208Snarayan 55181ae08745Sheppo } 55191ae08745Sheppo 55204bac2208Snarayan /* save all cookies */ 55214bac2208Snarayan bcopy(cookie, memseg->cookies, ccount * sizeof (ldc_mem_cookie_t)); 55224bac2208Snarayan 55231ae08745Sheppo /* update memseg_t */ 55241ae08745Sheppo memseg->raddr = memseg->pages[0].raddr; 55254bac2208Snarayan memseg->size = (mtype == LDC_SHADOW_MAP) ? exp_size : map_size; 55261ae08745Sheppo memseg->npages = npages; 55271ae08745Sheppo memseg->ncookies = ccount; 55281ae08745Sheppo memseg->next_cookie = 0; 55291ae08745Sheppo 55301ae08745Sheppo /* memory handle = mapped */ 55311ae08745Sheppo mhdl->mtype = mtype; 55324bac2208Snarayan mhdl->perm = perm; 55331ae08745Sheppo mhdl->status = LDC_MAPPED; 55341ae08745Sheppo 55351ae08745Sheppo D1(ldcp->id, "ldc_mem_map: (0x%llx) mapped 0x%llx, ra=0x%llx, " 55361ae08745Sheppo "va=0x%llx, pgs=0x%llx cookies=0x%llx\n", 55371ae08745Sheppo ldcp->id, mhdl, memseg->raddr, memseg->vaddr, 55381ae08745Sheppo memseg->npages, memseg->ncookies); 55391ae08745Sheppo 55404bac2208Snarayan if (mtype == LDC_SHADOW_MAP) 55414bac2208Snarayan base_off = 0; 55421ae08745Sheppo if (raddr) 55434bac2208Snarayan *raddr = (caddr_t)(memseg->raddr | base_off); 55441ae08745Sheppo if (vaddr) 55454bac2208Snarayan *vaddr = (caddr_t)((uintptr_t)memseg->vaddr | base_off); 55461ae08745Sheppo 55471ae08745Sheppo mutex_exit(&ldcp->lock); 55481ae08745Sheppo mutex_exit(&mhdl->lock); 55491ae08745Sheppo return (0); 55501ae08745Sheppo } 55511ae08745Sheppo 55521ae08745Sheppo /* 55531ae08745Sheppo * Unmap a memory segment. Free shadow memory (if any). 55541ae08745Sheppo */ 55551ae08745Sheppo int 55561ae08745Sheppo ldc_mem_unmap(ldc_mem_handle_t mhandle) 55571ae08745Sheppo { 55584bac2208Snarayan int i, rv; 55591ae08745Sheppo ldc_mhdl_t *mhdl = (ldc_mhdl_t *)mhandle; 55601ae08745Sheppo ldc_chan_t *ldcp; 55611ae08745Sheppo ldc_memseg_t *memseg; 55621ae08745Sheppo 55631ae08745Sheppo if (mhdl == 0 || mhdl->status != LDC_MAPPED) { 55641ae08745Sheppo DWARN(DBG_ALL_LDCS, 55651ae08745Sheppo "ldc_mem_unmap: (0x%llx) handle is not mapped\n", 55661ae08745Sheppo mhandle); 55671ae08745Sheppo return (EINVAL); 55681ae08745Sheppo } 55691ae08745Sheppo 55701ae08745Sheppo mutex_enter(&mhdl->lock); 55711ae08745Sheppo 55721ae08745Sheppo ldcp = mhdl->ldcp; 55731ae08745Sheppo memseg = mhdl->memseg; 55741ae08745Sheppo 55751ae08745Sheppo D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapping handle 0x%llx\n", 55761ae08745Sheppo ldcp->id, mhdl); 55771ae08745Sheppo 55781ae08745Sheppo /* if we allocated shadow memory - free it */ 55791ae08745Sheppo if (mhdl->mtype == LDC_SHADOW_MAP && mhdl->myshadow) { 55803af08d82Slm66018 kmem_free(memseg->vaddr, memseg->size); 55814bac2208Snarayan } else if (mhdl->mtype == LDC_DIRECT_MAP) { 55824bac2208Snarayan 55834bac2208Snarayan /* unmap in the case of DIRECT_MAP */ 55844bac2208Snarayan hat_unload(kas.a_hat, memseg->vaddr, memseg->size, 55854bac2208Snarayan HAT_UNLOAD_UNLOCK); 55864bac2208Snarayan 55874bac2208Snarayan for (i = 0; i < memseg->npages; i++) { 55884bac2208Snarayan rv = hv_ldc_unmap(memseg->pages[i].raddr); 55894bac2208Snarayan if (rv) { 55904bac2208Snarayan cmn_err(CE_WARN, 55914bac2208Snarayan "ldc_mem_map: (0x%lx) hv unmap err %d\n", 55924bac2208Snarayan ldcp->id, rv); 55934bac2208Snarayan } 55944bac2208Snarayan } 55954bac2208Snarayan 55964bac2208Snarayan vmem_free(heap_arena, (void *)memseg->vaddr, memseg->size); 55971ae08745Sheppo } 55981ae08745Sheppo 55991ae08745Sheppo /* free the allocated memseg and page structures */ 56001ae08745Sheppo kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages)); 56011ae08745Sheppo kmem_free(memseg->cookies, 56021ae08745Sheppo (sizeof (ldc_mem_cookie_t) * memseg->ncookies)); 56034bac2208Snarayan kmem_cache_free(ldcssp->memseg_cache, memseg); 56041ae08745Sheppo 56051ae08745Sheppo /* uninitialize the memory handle */ 56061ae08745Sheppo mhdl->memseg = NULL; 56071ae08745Sheppo mhdl->status = LDC_UNBOUND; 56081ae08745Sheppo 56091ae08745Sheppo D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapped handle 0x%llx\n", 56101ae08745Sheppo ldcp->id, mhdl); 56111ae08745Sheppo 56121ae08745Sheppo mutex_exit(&mhdl->lock); 56131ae08745Sheppo return (0); 56141ae08745Sheppo } 56151ae08745Sheppo 56161ae08745Sheppo /* 56171ae08745Sheppo * Internal entry point for LDC mapped memory entry consistency 56181ae08745Sheppo * semantics. Acquire copies the contents of the remote memory 56191ae08745Sheppo * into the local shadow copy. The release operation copies the local 56201ae08745Sheppo * contents into the remote memory. The offset and size specify the 56211ae08745Sheppo * bounds for the memory range being synchronized. 56221ae08745Sheppo */ 56231ae08745Sheppo static int 56241ae08745Sheppo i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle, uint8_t direction, 56251ae08745Sheppo uint64_t offset, size_t size) 56261ae08745Sheppo { 56271ae08745Sheppo int err; 56281ae08745Sheppo ldc_mhdl_t *mhdl; 56291ae08745Sheppo ldc_chan_t *ldcp; 56301ae08745Sheppo ldc_memseg_t *memseg; 56311ae08745Sheppo caddr_t local_vaddr; 56321ae08745Sheppo size_t copy_size; 56331ae08745Sheppo 56341ae08745Sheppo if (mhandle == NULL) { 56351ae08745Sheppo DWARN(DBG_ALL_LDCS, 56361ae08745Sheppo "i_ldc_mem_acquire_release: invalid memory handle\n"); 56371ae08745Sheppo return (EINVAL); 56381ae08745Sheppo } 56391ae08745Sheppo mhdl = (ldc_mhdl_t *)mhandle; 56401ae08745Sheppo 56411ae08745Sheppo mutex_enter(&mhdl->lock); 56421ae08745Sheppo 56431ae08745Sheppo if (mhdl->status != LDC_MAPPED || mhdl->ldcp == NULL) { 56441ae08745Sheppo DWARN(DBG_ALL_LDCS, 56451ae08745Sheppo "i_ldc_mem_acquire_release: not mapped memory\n"); 56461ae08745Sheppo mutex_exit(&mhdl->lock); 56471ae08745Sheppo return (EINVAL); 56481ae08745Sheppo } 56491ae08745Sheppo 56504bac2208Snarayan /* do nothing for direct map */ 56514bac2208Snarayan if (mhdl->mtype == LDC_DIRECT_MAP) { 56524bac2208Snarayan mutex_exit(&mhdl->lock); 56534bac2208Snarayan return (0); 56544bac2208Snarayan } 56554bac2208Snarayan 56564bac2208Snarayan /* do nothing if COPY_IN+MEM_W and COPY_OUT+MEM_R */ 56574bac2208Snarayan if ((direction == LDC_COPY_IN && (mhdl->perm & LDC_MEM_R) == 0) || 56584bac2208Snarayan (direction == LDC_COPY_OUT && (mhdl->perm & LDC_MEM_W) == 0)) { 56594bac2208Snarayan mutex_exit(&mhdl->lock); 56604bac2208Snarayan return (0); 56614bac2208Snarayan } 56624bac2208Snarayan 56631ae08745Sheppo if (offset >= mhdl->memseg->size || 56641ae08745Sheppo (offset + size) > mhdl->memseg->size) { 56651ae08745Sheppo DWARN(DBG_ALL_LDCS, 56661ae08745Sheppo "i_ldc_mem_acquire_release: memory out of range\n"); 56671ae08745Sheppo mutex_exit(&mhdl->lock); 56681ae08745Sheppo return (EINVAL); 56691ae08745Sheppo } 56701ae08745Sheppo 56711ae08745Sheppo /* get the channel handle and memory segment */ 56721ae08745Sheppo ldcp = mhdl->ldcp; 56731ae08745Sheppo memseg = mhdl->memseg; 56741ae08745Sheppo 56751ae08745Sheppo if (mhdl->mtype == LDC_SHADOW_MAP) { 56761ae08745Sheppo 56771ae08745Sheppo local_vaddr = memseg->vaddr + offset; 56781ae08745Sheppo copy_size = size; 56791ae08745Sheppo 56801ae08745Sheppo /* copy to/from remote from/to local memory */ 56811ae08745Sheppo err = ldc_mem_copy((ldc_handle_t)ldcp, local_vaddr, offset, 56821ae08745Sheppo ©_size, memseg->cookies, memseg->ncookies, 56831ae08745Sheppo direction); 56841ae08745Sheppo if (err || copy_size != size) { 5685cb112a14Slm66018 DWARN(ldcp->id, 56861ae08745Sheppo "i_ldc_mem_acquire_release: copy failed\n"); 56871ae08745Sheppo mutex_exit(&mhdl->lock); 56881ae08745Sheppo return (err); 56891ae08745Sheppo } 56901ae08745Sheppo } 56911ae08745Sheppo 56921ae08745Sheppo mutex_exit(&mhdl->lock); 56931ae08745Sheppo 56941ae08745Sheppo return (0); 56951ae08745Sheppo } 56961ae08745Sheppo 56971ae08745Sheppo /* 56981ae08745Sheppo * Ensure that the contents in the remote memory seg are consistent 56991ae08745Sheppo * with the contents if of local segment 57001ae08745Sheppo */ 57011ae08745Sheppo int 57021ae08745Sheppo ldc_mem_acquire(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size) 57031ae08745Sheppo { 57041ae08745Sheppo return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_IN, offset, size)); 57051ae08745Sheppo } 57061ae08745Sheppo 57071ae08745Sheppo 57081ae08745Sheppo /* 57091ae08745Sheppo * Ensure that the contents in the local memory seg are consistent 57101ae08745Sheppo * with the contents if of remote segment 57111ae08745Sheppo */ 57121ae08745Sheppo int 57131ae08745Sheppo ldc_mem_release(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size) 57141ae08745Sheppo { 57151ae08745Sheppo return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_OUT, offset, size)); 57161ae08745Sheppo } 57171ae08745Sheppo 57181ae08745Sheppo /* 57191ae08745Sheppo * Allocate a descriptor ring. The size of each each descriptor 57201ae08745Sheppo * must be 8-byte aligned and the entire ring should be a multiple 57211ae08745Sheppo * of MMU_PAGESIZE. 57221ae08745Sheppo */ 57231ae08745Sheppo int 57241ae08745Sheppo ldc_mem_dring_create(uint32_t len, uint32_t dsize, ldc_dring_handle_t *dhandle) 57251ae08745Sheppo { 57261ae08745Sheppo ldc_dring_t *dringp; 57271ae08745Sheppo size_t size = (dsize * len); 57281ae08745Sheppo 57291ae08745Sheppo D1(DBG_ALL_LDCS, "ldc_mem_dring_create: len=0x%x, size=0x%x\n", 57301ae08745Sheppo len, dsize); 57311ae08745Sheppo 57321ae08745Sheppo if (dhandle == NULL) { 57331ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid dhandle\n"); 57341ae08745Sheppo return (EINVAL); 57351ae08745Sheppo } 57361ae08745Sheppo 57371ae08745Sheppo if (len == 0) { 57381ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid length\n"); 57391ae08745Sheppo return (EINVAL); 57401ae08745Sheppo } 57411ae08745Sheppo 57421ae08745Sheppo /* descriptor size should be 8-byte aligned */ 57431ae08745Sheppo if (dsize == 0 || (dsize & 0x7)) { 57441ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid size\n"); 57451ae08745Sheppo return (EINVAL); 57461ae08745Sheppo } 57471ae08745Sheppo 57481ae08745Sheppo *dhandle = 0; 57491ae08745Sheppo 57501ae08745Sheppo /* Allocate a desc ring structure */ 57511ae08745Sheppo dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP); 57521ae08745Sheppo 57531ae08745Sheppo /* Initialize dring */ 57541ae08745Sheppo dringp->length = len; 57551ae08745Sheppo dringp->dsize = dsize; 57561ae08745Sheppo 57571ae08745Sheppo /* round off to multiple of pagesize */ 57581ae08745Sheppo dringp->size = (size & MMU_PAGEMASK); 57591ae08745Sheppo if (size & MMU_PAGEOFFSET) 57601ae08745Sheppo dringp->size += MMU_PAGESIZE; 57611ae08745Sheppo 57621ae08745Sheppo dringp->status = LDC_UNBOUND; 57631ae08745Sheppo 57641ae08745Sheppo /* allocate descriptor ring memory */ 57653af08d82Slm66018 dringp->base = kmem_zalloc(dringp->size, KM_SLEEP); 57661ae08745Sheppo 57671ae08745Sheppo /* initialize the desc ring lock */ 57681ae08745Sheppo mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL); 57691ae08745Sheppo 57701ae08745Sheppo /* Add descriptor ring to the head of global list */ 57711ae08745Sheppo mutex_enter(&ldcssp->lock); 57721ae08745Sheppo dringp->next = ldcssp->dring_list; 57731ae08745Sheppo ldcssp->dring_list = dringp; 57741ae08745Sheppo mutex_exit(&ldcssp->lock); 57751ae08745Sheppo 57761ae08745Sheppo *dhandle = (ldc_dring_handle_t)dringp; 57771ae08745Sheppo 57781ae08745Sheppo D1(DBG_ALL_LDCS, "ldc_mem_dring_create: dring allocated\n"); 57791ae08745Sheppo 57801ae08745Sheppo return (0); 57811ae08745Sheppo } 57821ae08745Sheppo 57831ae08745Sheppo 57841ae08745Sheppo /* 57851ae08745Sheppo * Destroy a descriptor ring. 57861ae08745Sheppo */ 57871ae08745Sheppo int 57881ae08745Sheppo ldc_mem_dring_destroy(ldc_dring_handle_t dhandle) 57891ae08745Sheppo { 57901ae08745Sheppo ldc_dring_t *dringp; 57911ae08745Sheppo ldc_dring_t *tmp_dringp; 57921ae08745Sheppo 57931ae08745Sheppo D1(DBG_ALL_LDCS, "ldc_mem_dring_destroy: entered\n"); 57941ae08745Sheppo 57951ae08745Sheppo if (dhandle == NULL) { 57961ae08745Sheppo DWARN(DBG_ALL_LDCS, 57971ae08745Sheppo "ldc_mem_dring_destroy: invalid desc ring handle\n"); 57981ae08745Sheppo return (EINVAL); 57991ae08745Sheppo } 58001ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 58011ae08745Sheppo 58021ae08745Sheppo if (dringp->status == LDC_BOUND) { 58031ae08745Sheppo DWARN(DBG_ALL_LDCS, 58041ae08745Sheppo "ldc_mem_dring_destroy: desc ring is bound\n"); 58051ae08745Sheppo return (EACCES); 58061ae08745Sheppo } 58071ae08745Sheppo 58081ae08745Sheppo mutex_enter(&dringp->lock); 58091ae08745Sheppo mutex_enter(&ldcssp->lock); 58101ae08745Sheppo 58111ae08745Sheppo /* remove from linked list - if not bound */ 58121ae08745Sheppo tmp_dringp = ldcssp->dring_list; 58131ae08745Sheppo if (tmp_dringp == dringp) { 58141ae08745Sheppo ldcssp->dring_list = dringp->next; 58151ae08745Sheppo dringp->next = NULL; 58161ae08745Sheppo 58171ae08745Sheppo } else { 58181ae08745Sheppo while (tmp_dringp != NULL) { 58191ae08745Sheppo if (tmp_dringp->next == dringp) { 58201ae08745Sheppo tmp_dringp->next = dringp->next; 58211ae08745Sheppo dringp->next = NULL; 58221ae08745Sheppo break; 58231ae08745Sheppo } 58241ae08745Sheppo tmp_dringp = tmp_dringp->next; 58251ae08745Sheppo } 58261ae08745Sheppo if (tmp_dringp == NULL) { 58271ae08745Sheppo DWARN(DBG_ALL_LDCS, 58281ae08745Sheppo "ldc_mem_dring_destroy: invalid descriptor\n"); 58291ae08745Sheppo mutex_exit(&ldcssp->lock); 58301ae08745Sheppo mutex_exit(&dringp->lock); 58311ae08745Sheppo return (EINVAL); 58321ae08745Sheppo } 58331ae08745Sheppo } 58341ae08745Sheppo 58351ae08745Sheppo mutex_exit(&ldcssp->lock); 58361ae08745Sheppo 58371ae08745Sheppo /* free the descriptor ring */ 58383af08d82Slm66018 kmem_free(dringp->base, dringp->size); 58391ae08745Sheppo 58401ae08745Sheppo mutex_exit(&dringp->lock); 58411ae08745Sheppo 58421ae08745Sheppo /* destroy dring lock */ 58431ae08745Sheppo mutex_destroy(&dringp->lock); 58441ae08745Sheppo 58451ae08745Sheppo /* free desc ring object */ 58461ae08745Sheppo kmem_free(dringp, sizeof (ldc_dring_t)); 58471ae08745Sheppo 58481ae08745Sheppo return (0); 58491ae08745Sheppo } 58501ae08745Sheppo 58511ae08745Sheppo /* 58521ae08745Sheppo * Bind a previously allocated dring to a channel. The channel should 58531ae08745Sheppo * be OPEN in order to bind the ring to the channel. Returns back a 58541ae08745Sheppo * descriptor ring cookie. The descriptor ring is exported for remote 58551ae08745Sheppo * access by the client at the other end of the channel. An entry for 58561ae08745Sheppo * dring pages is stored in map table (via call to ldc_mem_bind_handle). 58571ae08745Sheppo */ 58581ae08745Sheppo int 58591ae08745Sheppo ldc_mem_dring_bind(ldc_handle_t handle, ldc_dring_handle_t dhandle, 58601ae08745Sheppo uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount) 58611ae08745Sheppo { 58621ae08745Sheppo int err; 58631ae08745Sheppo ldc_chan_t *ldcp; 58641ae08745Sheppo ldc_dring_t *dringp; 58651ae08745Sheppo ldc_mem_handle_t mhandle; 58661ae08745Sheppo 58671ae08745Sheppo /* check to see if channel is initalized */ 58681ae08745Sheppo if (handle == NULL) { 58691ae08745Sheppo DWARN(DBG_ALL_LDCS, 58701ae08745Sheppo "ldc_mem_dring_bind: invalid channel handle\n"); 58711ae08745Sheppo return (EINVAL); 58721ae08745Sheppo } 58731ae08745Sheppo ldcp = (ldc_chan_t *)handle; 58741ae08745Sheppo 58751ae08745Sheppo if (dhandle == NULL) { 58761ae08745Sheppo DWARN(DBG_ALL_LDCS, 58771ae08745Sheppo "ldc_mem_dring_bind: invalid desc ring handle\n"); 58781ae08745Sheppo return (EINVAL); 58791ae08745Sheppo } 58801ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 58811ae08745Sheppo 58821ae08745Sheppo if (cookie == NULL) { 58831ae08745Sheppo DWARN(ldcp->id, 58841ae08745Sheppo "ldc_mem_dring_bind: invalid cookie arg\n"); 58851ae08745Sheppo return (EINVAL); 58861ae08745Sheppo } 58871ae08745Sheppo 58881ae08745Sheppo mutex_enter(&dringp->lock); 58891ae08745Sheppo 58901ae08745Sheppo if (dringp->status == LDC_BOUND) { 58911ae08745Sheppo DWARN(DBG_ALL_LDCS, 58921ae08745Sheppo "ldc_mem_dring_bind: (0x%llx) descriptor ring is bound\n", 58931ae08745Sheppo ldcp->id); 58941ae08745Sheppo mutex_exit(&dringp->lock); 58951ae08745Sheppo return (EINVAL); 58961ae08745Sheppo } 58971ae08745Sheppo 58981ae08745Sheppo if ((perm & LDC_MEM_RW) == 0) { 58991ae08745Sheppo DWARN(DBG_ALL_LDCS, 59001ae08745Sheppo "ldc_mem_dring_bind: invalid permissions\n"); 59011ae08745Sheppo mutex_exit(&dringp->lock); 59021ae08745Sheppo return (EINVAL); 59031ae08745Sheppo } 59041ae08745Sheppo 59051ae08745Sheppo if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) { 59061ae08745Sheppo DWARN(DBG_ALL_LDCS, "ldc_mem_dring_bind: invalid type\n"); 59071ae08745Sheppo mutex_exit(&dringp->lock); 59081ae08745Sheppo return (EINVAL); 59091ae08745Sheppo } 59101ae08745Sheppo 59111ae08745Sheppo dringp->ldcp = ldcp; 59121ae08745Sheppo 59131ae08745Sheppo /* create an memory handle */ 59141ae08745Sheppo err = ldc_mem_alloc_handle(handle, &mhandle); 59151ae08745Sheppo if (err || mhandle == NULL) { 59161ae08745Sheppo DWARN(DBG_ALL_LDCS, 59171ae08745Sheppo "ldc_mem_dring_bind: (0x%llx) error allocating mhandle\n", 59181ae08745Sheppo ldcp->id); 59191ae08745Sheppo mutex_exit(&dringp->lock); 59201ae08745Sheppo return (err); 59211ae08745Sheppo } 59221ae08745Sheppo dringp->mhdl = mhandle; 59231ae08745Sheppo 59241ae08745Sheppo /* bind the descriptor ring to channel */ 59251ae08745Sheppo err = ldc_mem_bind_handle(mhandle, dringp->base, dringp->size, 59261ae08745Sheppo mtype, perm, cookie, ccount); 59271ae08745Sheppo if (err) { 59281ae08745Sheppo DWARN(ldcp->id, 59291ae08745Sheppo "ldc_mem_dring_bind: (0x%llx) error binding mhandle\n", 59301ae08745Sheppo ldcp->id); 59311ae08745Sheppo mutex_exit(&dringp->lock); 59321ae08745Sheppo return (err); 59331ae08745Sheppo } 59341ae08745Sheppo 59351ae08745Sheppo /* 59361ae08745Sheppo * For now return error if we get more than one cookie 59371ae08745Sheppo * FUTURE: Return multiple cookies .. 59381ae08745Sheppo */ 59391ae08745Sheppo if (*ccount > 1) { 59401ae08745Sheppo (void) ldc_mem_unbind_handle(mhandle); 59411ae08745Sheppo (void) ldc_mem_free_handle(mhandle); 59421ae08745Sheppo 59431ae08745Sheppo dringp->ldcp = NULL; 59441ae08745Sheppo dringp->mhdl = NULL; 59451ae08745Sheppo *ccount = 0; 59461ae08745Sheppo 59471ae08745Sheppo mutex_exit(&dringp->lock); 59481ae08745Sheppo return (EAGAIN); 59491ae08745Sheppo } 59501ae08745Sheppo 59511ae08745Sheppo /* Add descriptor ring to channel's exported dring list */ 59521ae08745Sheppo mutex_enter(&ldcp->exp_dlist_lock); 59531ae08745Sheppo dringp->ch_next = ldcp->exp_dring_list; 59541ae08745Sheppo ldcp->exp_dring_list = dringp; 59551ae08745Sheppo mutex_exit(&ldcp->exp_dlist_lock); 59561ae08745Sheppo 59571ae08745Sheppo dringp->status = LDC_BOUND; 59581ae08745Sheppo 59591ae08745Sheppo mutex_exit(&dringp->lock); 59601ae08745Sheppo 59611ae08745Sheppo return (0); 59621ae08745Sheppo } 59631ae08745Sheppo 59641ae08745Sheppo /* 59651ae08745Sheppo * Return the next cookie associated with the specified dring handle 59661ae08745Sheppo */ 59671ae08745Sheppo int 59681ae08745Sheppo ldc_mem_dring_nextcookie(ldc_dring_handle_t dhandle, ldc_mem_cookie_t *cookie) 59691ae08745Sheppo { 59701ae08745Sheppo int rv = 0; 59711ae08745Sheppo ldc_dring_t *dringp; 59721ae08745Sheppo ldc_chan_t *ldcp; 59731ae08745Sheppo 59741ae08745Sheppo if (dhandle == NULL) { 59751ae08745Sheppo DWARN(DBG_ALL_LDCS, 59761ae08745Sheppo "ldc_mem_dring_nextcookie: invalid desc ring handle\n"); 59771ae08745Sheppo return (EINVAL); 59781ae08745Sheppo } 59791ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 59801ae08745Sheppo mutex_enter(&dringp->lock); 59811ae08745Sheppo 59821ae08745Sheppo if (dringp->status != LDC_BOUND) { 59831ae08745Sheppo DWARN(DBG_ALL_LDCS, 59841ae08745Sheppo "ldc_mem_dring_nextcookie: descriptor ring 0x%llx " 59851ae08745Sheppo "is not bound\n", dringp); 59861ae08745Sheppo mutex_exit(&dringp->lock); 59871ae08745Sheppo return (EINVAL); 59881ae08745Sheppo } 59891ae08745Sheppo 59901ae08745Sheppo ldcp = dringp->ldcp; 59911ae08745Sheppo 59921ae08745Sheppo if (cookie == NULL) { 59931ae08745Sheppo DWARN(ldcp->id, 59941ae08745Sheppo "ldc_mem_dring_nextcookie:(0x%llx) invalid cookie arg\n", 59951ae08745Sheppo ldcp->id); 59961ae08745Sheppo mutex_exit(&dringp->lock); 59971ae08745Sheppo return (EINVAL); 59981ae08745Sheppo } 59991ae08745Sheppo 60001ae08745Sheppo rv = ldc_mem_nextcookie((ldc_mem_handle_t)dringp->mhdl, cookie); 60011ae08745Sheppo mutex_exit(&dringp->lock); 60021ae08745Sheppo 60031ae08745Sheppo return (rv); 60041ae08745Sheppo } 60051ae08745Sheppo /* 60061ae08745Sheppo * Unbind a previously bound dring from a channel. 60071ae08745Sheppo */ 60081ae08745Sheppo int 60091ae08745Sheppo ldc_mem_dring_unbind(ldc_dring_handle_t dhandle) 60101ae08745Sheppo { 60111ae08745Sheppo ldc_dring_t *dringp; 60121ae08745Sheppo ldc_dring_t *tmp_dringp; 60131ae08745Sheppo ldc_chan_t *ldcp; 60141ae08745Sheppo 60151ae08745Sheppo if (dhandle == NULL) { 60161ae08745Sheppo DWARN(DBG_ALL_LDCS, 60171ae08745Sheppo "ldc_mem_dring_unbind: invalid desc ring handle\n"); 60181ae08745Sheppo return (EINVAL); 60191ae08745Sheppo } 60201ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 60211ae08745Sheppo 60221ae08745Sheppo mutex_enter(&dringp->lock); 60231ae08745Sheppo 60241ae08745Sheppo if (dringp->status == LDC_UNBOUND) { 60251ae08745Sheppo DWARN(DBG_ALL_LDCS, 60261ae08745Sheppo "ldc_mem_dring_bind: descriptor ring 0x%llx is unbound\n", 60271ae08745Sheppo dringp); 60281ae08745Sheppo mutex_exit(&dringp->lock); 60291ae08745Sheppo return (EINVAL); 60301ae08745Sheppo } 60311ae08745Sheppo ldcp = dringp->ldcp; 60321ae08745Sheppo 60331ae08745Sheppo mutex_enter(&ldcp->exp_dlist_lock); 60341ae08745Sheppo 60351ae08745Sheppo tmp_dringp = ldcp->exp_dring_list; 60361ae08745Sheppo if (tmp_dringp == dringp) { 60371ae08745Sheppo ldcp->exp_dring_list = dringp->ch_next; 60381ae08745Sheppo dringp->ch_next = NULL; 60391ae08745Sheppo 60401ae08745Sheppo } else { 60411ae08745Sheppo while (tmp_dringp != NULL) { 60421ae08745Sheppo if (tmp_dringp->ch_next == dringp) { 60431ae08745Sheppo tmp_dringp->ch_next = dringp->ch_next; 60441ae08745Sheppo dringp->ch_next = NULL; 60451ae08745Sheppo break; 60461ae08745Sheppo } 60471ae08745Sheppo tmp_dringp = tmp_dringp->ch_next; 60481ae08745Sheppo } 60491ae08745Sheppo if (tmp_dringp == NULL) { 60501ae08745Sheppo DWARN(DBG_ALL_LDCS, 60511ae08745Sheppo "ldc_mem_dring_unbind: invalid descriptor\n"); 60521ae08745Sheppo mutex_exit(&ldcp->exp_dlist_lock); 60531ae08745Sheppo mutex_exit(&dringp->lock); 60541ae08745Sheppo return (EINVAL); 60551ae08745Sheppo } 60561ae08745Sheppo } 60571ae08745Sheppo 60581ae08745Sheppo mutex_exit(&ldcp->exp_dlist_lock); 60591ae08745Sheppo 60601ae08745Sheppo (void) ldc_mem_unbind_handle((ldc_mem_handle_t)dringp->mhdl); 60611ae08745Sheppo (void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl); 60621ae08745Sheppo 60631ae08745Sheppo dringp->ldcp = NULL; 60641ae08745Sheppo dringp->mhdl = NULL; 60651ae08745Sheppo dringp->status = LDC_UNBOUND; 60661ae08745Sheppo 60671ae08745Sheppo mutex_exit(&dringp->lock); 60681ae08745Sheppo 60691ae08745Sheppo return (0); 60701ae08745Sheppo } 60711ae08745Sheppo 60721ae08745Sheppo /* 60731ae08745Sheppo * Get information about the dring. The base address of the descriptor 60741ae08745Sheppo * ring along with the type and permission are returned back. 60751ae08745Sheppo */ 60761ae08745Sheppo int 60771ae08745Sheppo ldc_mem_dring_info(ldc_dring_handle_t dhandle, ldc_mem_info_t *minfo) 60781ae08745Sheppo { 60791ae08745Sheppo ldc_dring_t *dringp; 60801ae08745Sheppo int rv; 60811ae08745Sheppo 60821ae08745Sheppo if (dhandle == NULL) { 60831ae08745Sheppo DWARN(DBG_ALL_LDCS, 60841ae08745Sheppo "ldc_mem_dring_info: invalid desc ring handle\n"); 60851ae08745Sheppo return (EINVAL); 60861ae08745Sheppo } 60871ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 60881ae08745Sheppo 60891ae08745Sheppo mutex_enter(&dringp->lock); 60901ae08745Sheppo 60911ae08745Sheppo if (dringp->mhdl) { 60921ae08745Sheppo rv = ldc_mem_info(dringp->mhdl, minfo); 60931ae08745Sheppo if (rv) { 60941ae08745Sheppo DWARN(DBG_ALL_LDCS, 60951ae08745Sheppo "ldc_mem_dring_info: error reading mem info\n"); 60961ae08745Sheppo mutex_exit(&dringp->lock); 60971ae08745Sheppo return (rv); 60981ae08745Sheppo } 60991ae08745Sheppo } else { 61001ae08745Sheppo minfo->vaddr = dringp->base; 61011ae08745Sheppo minfo->raddr = NULL; 61021ae08745Sheppo minfo->status = dringp->status; 61031ae08745Sheppo } 61041ae08745Sheppo 61051ae08745Sheppo mutex_exit(&dringp->lock); 61061ae08745Sheppo 61071ae08745Sheppo return (0); 61081ae08745Sheppo } 61091ae08745Sheppo 61101ae08745Sheppo /* 61111ae08745Sheppo * Map an exported descriptor ring into the local address space. If the 61121ae08745Sheppo * descriptor ring was exported for direct map access, a HV call is made 61131ae08745Sheppo * to allocate a RA range. If the map is done via a shadow copy, local 61141ae08745Sheppo * shadow memory is allocated. 61151ae08745Sheppo */ 61161ae08745Sheppo int 61171ae08745Sheppo ldc_mem_dring_map(ldc_handle_t handle, ldc_mem_cookie_t *cookie, 61181ae08745Sheppo uint32_t ccount, uint32_t len, uint32_t dsize, uint8_t mtype, 61191ae08745Sheppo ldc_dring_handle_t *dhandle) 61201ae08745Sheppo { 61211ae08745Sheppo int err; 61221ae08745Sheppo ldc_chan_t *ldcp = (ldc_chan_t *)handle; 61231ae08745Sheppo ldc_mem_handle_t mhandle; 61241ae08745Sheppo ldc_dring_t *dringp; 61251ae08745Sheppo size_t dring_size; 61261ae08745Sheppo 61271ae08745Sheppo if (dhandle == NULL) { 61281ae08745Sheppo DWARN(DBG_ALL_LDCS, 61291ae08745Sheppo "ldc_mem_dring_map: invalid dhandle\n"); 61301ae08745Sheppo return (EINVAL); 61311ae08745Sheppo } 61321ae08745Sheppo 61331ae08745Sheppo /* check to see if channel is initalized */ 61341ae08745Sheppo if (handle == NULL) { 61351ae08745Sheppo DWARN(DBG_ALL_LDCS, 61361ae08745Sheppo "ldc_mem_dring_map: invalid channel handle\n"); 61371ae08745Sheppo return (EINVAL); 61381ae08745Sheppo } 61391ae08745Sheppo ldcp = (ldc_chan_t *)handle; 61401ae08745Sheppo 61411ae08745Sheppo if (cookie == NULL) { 61421ae08745Sheppo DWARN(ldcp->id, 61431ae08745Sheppo "ldc_mem_dring_map: (0x%llx) invalid cookie\n", 61441ae08745Sheppo ldcp->id); 61451ae08745Sheppo return (EINVAL); 61461ae08745Sheppo } 61471ae08745Sheppo 61481ae08745Sheppo /* FUTURE: For now we support only one cookie per dring */ 61491ae08745Sheppo ASSERT(ccount == 1); 61501ae08745Sheppo 61511ae08745Sheppo if (cookie->size < (dsize * len)) { 61521ae08745Sheppo DWARN(ldcp->id, 61531ae08745Sheppo "ldc_mem_dring_map: (0x%llx) invalid dsize/len\n", 61541ae08745Sheppo ldcp->id); 61551ae08745Sheppo return (EINVAL); 61561ae08745Sheppo } 61571ae08745Sheppo 61581ae08745Sheppo *dhandle = 0; 61591ae08745Sheppo 61601ae08745Sheppo /* Allocate an dring structure */ 61611ae08745Sheppo dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP); 61621ae08745Sheppo 61631ae08745Sheppo D1(ldcp->id, 61641ae08745Sheppo "ldc_mem_dring_map: 0x%x,0x%x,0x%x,0x%llx,0x%llx\n", 61651ae08745Sheppo mtype, len, dsize, cookie->addr, cookie->size); 61661ae08745Sheppo 61671ae08745Sheppo /* Initialize dring */ 61681ae08745Sheppo dringp->length = len; 61691ae08745Sheppo dringp->dsize = dsize; 61701ae08745Sheppo 61711ae08745Sheppo /* round of to multiple of page size */ 61721ae08745Sheppo dring_size = len * dsize; 61731ae08745Sheppo dringp->size = (dring_size & MMU_PAGEMASK); 61741ae08745Sheppo if (dring_size & MMU_PAGEOFFSET) 61751ae08745Sheppo dringp->size += MMU_PAGESIZE; 61761ae08745Sheppo 61771ae08745Sheppo dringp->ldcp = ldcp; 61781ae08745Sheppo 61791ae08745Sheppo /* create an memory handle */ 61801ae08745Sheppo err = ldc_mem_alloc_handle(handle, &mhandle); 61811ae08745Sheppo if (err || mhandle == NULL) { 61821ae08745Sheppo DWARN(DBG_ALL_LDCS, 61831ae08745Sheppo "ldc_mem_dring_map: cannot alloc hdl err=%d\n", 61841ae08745Sheppo err); 61851ae08745Sheppo kmem_free(dringp, sizeof (ldc_dring_t)); 61861ae08745Sheppo return (ENOMEM); 61871ae08745Sheppo } 61881ae08745Sheppo 61891ae08745Sheppo dringp->mhdl = mhandle; 61901ae08745Sheppo dringp->base = NULL; 61911ae08745Sheppo 61921ae08745Sheppo /* map the dring into local memory */ 61934bac2208Snarayan err = ldc_mem_map(mhandle, cookie, ccount, mtype, LDC_MEM_RW, 61941ae08745Sheppo &(dringp->base), NULL); 61951ae08745Sheppo if (err || dringp->base == NULL) { 61961ae08745Sheppo cmn_err(CE_WARN, 61971ae08745Sheppo "ldc_mem_dring_map: cannot map desc ring err=%d\n", err); 61981ae08745Sheppo (void) ldc_mem_free_handle(mhandle); 61991ae08745Sheppo kmem_free(dringp, sizeof (ldc_dring_t)); 62001ae08745Sheppo return (ENOMEM); 62011ae08745Sheppo } 62021ae08745Sheppo 62031ae08745Sheppo /* initialize the desc ring lock */ 62041ae08745Sheppo mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL); 62051ae08745Sheppo 62061ae08745Sheppo /* Add descriptor ring to channel's imported dring list */ 62071ae08745Sheppo mutex_enter(&ldcp->imp_dlist_lock); 62081ae08745Sheppo dringp->ch_next = ldcp->imp_dring_list; 62091ae08745Sheppo ldcp->imp_dring_list = dringp; 62101ae08745Sheppo mutex_exit(&ldcp->imp_dlist_lock); 62111ae08745Sheppo 62121ae08745Sheppo dringp->status = LDC_MAPPED; 62131ae08745Sheppo 62141ae08745Sheppo *dhandle = (ldc_dring_handle_t)dringp; 62151ae08745Sheppo 62161ae08745Sheppo return (0); 62171ae08745Sheppo } 62181ae08745Sheppo 62191ae08745Sheppo /* 62201ae08745Sheppo * Unmap a descriptor ring. Free shadow memory (if any). 62211ae08745Sheppo */ 62221ae08745Sheppo int 62231ae08745Sheppo ldc_mem_dring_unmap(ldc_dring_handle_t dhandle) 62241ae08745Sheppo { 62251ae08745Sheppo ldc_dring_t *dringp; 62261ae08745Sheppo ldc_dring_t *tmp_dringp; 62271ae08745Sheppo ldc_chan_t *ldcp; 62281ae08745Sheppo 62291ae08745Sheppo if (dhandle == NULL) { 62301ae08745Sheppo DWARN(DBG_ALL_LDCS, 62311ae08745Sheppo "ldc_mem_dring_unmap: invalid desc ring handle\n"); 62321ae08745Sheppo return (EINVAL); 62331ae08745Sheppo } 62341ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 62351ae08745Sheppo 62361ae08745Sheppo if (dringp->status != LDC_MAPPED) { 62371ae08745Sheppo DWARN(DBG_ALL_LDCS, 62381ae08745Sheppo "ldc_mem_dring_unmap: not a mapped desc ring\n"); 62391ae08745Sheppo return (EINVAL); 62401ae08745Sheppo } 62411ae08745Sheppo 62421ae08745Sheppo mutex_enter(&dringp->lock); 62431ae08745Sheppo 62441ae08745Sheppo ldcp = dringp->ldcp; 62451ae08745Sheppo 62461ae08745Sheppo mutex_enter(&ldcp->imp_dlist_lock); 62471ae08745Sheppo 62481ae08745Sheppo /* find and unlink the desc ring from channel import list */ 62491ae08745Sheppo tmp_dringp = ldcp->imp_dring_list; 62501ae08745Sheppo if (tmp_dringp == dringp) { 62511ae08745Sheppo ldcp->imp_dring_list = dringp->ch_next; 62521ae08745Sheppo dringp->ch_next = NULL; 62531ae08745Sheppo 62541ae08745Sheppo } else { 62551ae08745Sheppo while (tmp_dringp != NULL) { 62561ae08745Sheppo if (tmp_dringp->ch_next == dringp) { 62571ae08745Sheppo tmp_dringp->ch_next = dringp->ch_next; 62581ae08745Sheppo dringp->ch_next = NULL; 62591ae08745Sheppo break; 62601ae08745Sheppo } 62611ae08745Sheppo tmp_dringp = tmp_dringp->ch_next; 62621ae08745Sheppo } 62631ae08745Sheppo if (tmp_dringp == NULL) { 62641ae08745Sheppo DWARN(DBG_ALL_LDCS, 62651ae08745Sheppo "ldc_mem_dring_unmap: invalid descriptor\n"); 62661ae08745Sheppo mutex_exit(&ldcp->imp_dlist_lock); 62671ae08745Sheppo mutex_exit(&dringp->lock); 62681ae08745Sheppo return (EINVAL); 62691ae08745Sheppo } 62701ae08745Sheppo } 62711ae08745Sheppo 62721ae08745Sheppo mutex_exit(&ldcp->imp_dlist_lock); 62731ae08745Sheppo 62741ae08745Sheppo /* do a LDC memory handle unmap and free */ 62751ae08745Sheppo (void) ldc_mem_unmap(dringp->mhdl); 62761ae08745Sheppo (void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl); 62771ae08745Sheppo 62781ae08745Sheppo dringp->status = 0; 62791ae08745Sheppo dringp->ldcp = NULL; 62801ae08745Sheppo 62811ae08745Sheppo mutex_exit(&dringp->lock); 62821ae08745Sheppo 62831ae08745Sheppo /* destroy dring lock */ 62841ae08745Sheppo mutex_destroy(&dringp->lock); 62851ae08745Sheppo 62861ae08745Sheppo /* free desc ring object */ 62871ae08745Sheppo kmem_free(dringp, sizeof (ldc_dring_t)); 62881ae08745Sheppo 62891ae08745Sheppo return (0); 62901ae08745Sheppo } 62911ae08745Sheppo 62921ae08745Sheppo /* 62931ae08745Sheppo * Internal entry point for descriptor ring access entry consistency 62941ae08745Sheppo * semantics. Acquire copies the contents of the remote descriptor ring 62951ae08745Sheppo * into the local shadow copy. The release operation copies the local 62961ae08745Sheppo * contents into the remote dring. The start and end locations specify 62971ae08745Sheppo * bounds for the entries being synchronized. 62981ae08745Sheppo */ 62991ae08745Sheppo static int 63001ae08745Sheppo i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle, 63011ae08745Sheppo uint8_t direction, uint64_t start, uint64_t end) 63021ae08745Sheppo { 63031ae08745Sheppo int err; 63041ae08745Sheppo ldc_dring_t *dringp; 63051ae08745Sheppo ldc_chan_t *ldcp; 63061ae08745Sheppo uint64_t soff; 63071ae08745Sheppo size_t copy_size; 63081ae08745Sheppo 63091ae08745Sheppo if (dhandle == NULL) { 63101ae08745Sheppo DWARN(DBG_ALL_LDCS, 63111ae08745Sheppo "i_ldc_dring_acquire_release: invalid desc ring handle\n"); 63121ae08745Sheppo return (EINVAL); 63131ae08745Sheppo } 63141ae08745Sheppo dringp = (ldc_dring_t *)dhandle; 63151ae08745Sheppo mutex_enter(&dringp->lock); 63161ae08745Sheppo 63171ae08745Sheppo if (dringp->status != LDC_MAPPED || dringp->ldcp == NULL) { 63181ae08745Sheppo DWARN(DBG_ALL_LDCS, 63191ae08745Sheppo "i_ldc_dring_acquire_release: not a mapped desc ring\n"); 63201ae08745Sheppo mutex_exit(&dringp->lock); 63211ae08745Sheppo return (EINVAL); 63221ae08745Sheppo } 63231ae08745Sheppo 63241ae08745Sheppo if (start >= dringp->length || end >= dringp->length) { 63251ae08745Sheppo DWARN(DBG_ALL_LDCS, 63261ae08745Sheppo "i_ldc_dring_acquire_release: index out of range\n"); 63271ae08745Sheppo mutex_exit(&dringp->lock); 63281ae08745Sheppo return (EINVAL); 63291ae08745Sheppo } 63301ae08745Sheppo 63311ae08745Sheppo /* get the channel handle */ 63321ae08745Sheppo ldcp = dringp->ldcp; 63331ae08745Sheppo 63341ae08745Sheppo copy_size = (start <= end) ? (((end - start) + 1) * dringp->dsize) : 63351ae08745Sheppo ((dringp->length - start) * dringp->dsize); 63361ae08745Sheppo 63371ae08745Sheppo /* Calculate the relative offset for the first desc */ 63381ae08745Sheppo soff = (start * dringp->dsize); 63391ae08745Sheppo 63401ae08745Sheppo /* copy to/from remote from/to local memory */ 63411ae08745Sheppo D1(ldcp->id, "i_ldc_dring_acquire_release: c1 off=0x%llx sz=0x%llx\n", 63421ae08745Sheppo soff, copy_size); 63431ae08745Sheppo err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl, 63441ae08745Sheppo direction, soff, copy_size); 63451ae08745Sheppo if (err) { 63461ae08745Sheppo DWARN(ldcp->id, 63471ae08745Sheppo "i_ldc_dring_acquire_release: copy failed\n"); 63481ae08745Sheppo mutex_exit(&dringp->lock); 63491ae08745Sheppo return (err); 63501ae08745Sheppo } 63511ae08745Sheppo 63521ae08745Sheppo /* do the balance */ 63531ae08745Sheppo if (start > end) { 63541ae08745Sheppo copy_size = ((end + 1) * dringp->dsize); 63551ae08745Sheppo soff = 0; 63561ae08745Sheppo 63571ae08745Sheppo /* copy to/from remote from/to local memory */ 63581ae08745Sheppo D1(ldcp->id, "i_ldc_dring_acquire_release: c2 " 63591ae08745Sheppo "off=0x%llx sz=0x%llx\n", soff, copy_size); 63601ae08745Sheppo err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl, 63611ae08745Sheppo direction, soff, copy_size); 63621ae08745Sheppo if (err) { 63631ae08745Sheppo DWARN(ldcp->id, 63641ae08745Sheppo "i_ldc_dring_acquire_release: copy failed\n"); 63651ae08745Sheppo mutex_exit(&dringp->lock); 63661ae08745Sheppo return (err); 63671ae08745Sheppo } 63681ae08745Sheppo } 63691ae08745Sheppo 63701ae08745Sheppo mutex_exit(&dringp->lock); 63711ae08745Sheppo 63721ae08745Sheppo return (0); 63731ae08745Sheppo } 63741ae08745Sheppo 63751ae08745Sheppo /* 63761ae08745Sheppo * Ensure that the contents in the local dring are consistent 63771ae08745Sheppo * with the contents if of remote dring 63781ae08745Sheppo */ 63791ae08745Sheppo int 63801ae08745Sheppo ldc_mem_dring_acquire(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end) 63811ae08745Sheppo { 63821ae08745Sheppo return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_IN, start, end)); 63831ae08745Sheppo } 63841ae08745Sheppo 63851ae08745Sheppo /* 63861ae08745Sheppo * Ensure that the contents in the remote dring are consistent 63871ae08745Sheppo * with the contents if of local dring 63881ae08745Sheppo */ 63891ae08745Sheppo int 63901ae08745Sheppo ldc_mem_dring_release(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end) 63911ae08745Sheppo { 63921ae08745Sheppo return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_OUT, start, end)); 63931ae08745Sheppo } 63941ae08745Sheppo 63951ae08745Sheppo 63961ae08745Sheppo /* ------------------------------------------------------------------------- */ 6397