xref: /titanic_53/usr/src/uts/sun4v/io/ldc.c (revision 0a55fbb79ee31ed09f84a9ae28e9747bc23f4a08)
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 /*
231ae08745Sheppo  * Copyright 2006 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 /*
301ae08745Sheppo  * sun4v LDC Transport 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>
551ae08745Sheppo #include <sys/note.h>
561ae08745Sheppo #include <sys/ivintr.h>
571ae08745Sheppo #include <sys/hypervisor_api.h>
581ae08745Sheppo #include <sys/ldc.h>
591ae08745Sheppo #include <sys/ldc_impl.h>
601ae08745Sheppo #include <sys/cnex.h>
611ae08745Sheppo #include <sys/hsvc.h>
621ae08745Sheppo 
631ae08745Sheppo /* Core internal functions */
641ae08745Sheppo static int i_ldc_h2v_error(int h_error);
651ae08745Sheppo static int i_ldc_txq_reconf(ldc_chan_t *ldcp);
661ae08745Sheppo static int i_ldc_rxq_reconf(ldc_chan_t *ldcp);
671ae08745Sheppo static void i_ldc_reset_state(ldc_chan_t *ldcp);
681ae08745Sheppo static void i_ldc_reset(ldc_chan_t *ldcp);
691ae08745Sheppo 
701ae08745Sheppo static int i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail);
711ae08745Sheppo static int i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail);
721ae08745Sheppo static int i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head);
731ae08745Sheppo static int i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
741ae08745Sheppo     uint8_t ctrlmsg);
751ae08745Sheppo 
761ae08745Sheppo /* Interrupt handling functions */
771ae08745Sheppo static uint_t i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2);
781ae08745Sheppo static uint_t i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2);
791ae08745Sheppo static void i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype);
801ae08745Sheppo 
811ae08745Sheppo /* Read method functions */
821ae08745Sheppo static int i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep);
831ae08745Sheppo static int i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
841ae08745Sheppo 	size_t *sizep);
851ae08745Sheppo static int i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
861ae08745Sheppo 	size_t *sizep);
871ae08745Sheppo 
881ae08745Sheppo /* Write method functions */
891ae08745Sheppo static int i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t target_bufp,
901ae08745Sheppo 	size_t *sizep);
911ae08745Sheppo static int i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
921ae08745Sheppo 	size_t *sizep);
931ae08745Sheppo static int i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
941ae08745Sheppo 	size_t *sizep);
951ae08745Sheppo 
961ae08745Sheppo /* Pkt processing internal functions */
971ae08745Sheppo static int i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
981ae08745Sheppo static int i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
991ae08745Sheppo static int i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg);
1001ae08745Sheppo static int i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg);
1011ae08745Sheppo static int i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg);
1021ae08745Sheppo static int i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg);
1031ae08745Sheppo static int i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg);
1041ae08745Sheppo 
1051ae08745Sheppo /* Memory synchronization internal functions */
1061ae08745Sheppo static int i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle,
1071ae08745Sheppo     uint8_t direction, uint64_t offset, size_t size);
1081ae08745Sheppo static int i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle,
1091ae08745Sheppo     uint8_t direction, uint64_t start, uint64_t end);
1101ae08745Sheppo 
1111ae08745Sheppo /* LDC Version */
1121ae08745Sheppo static ldc_ver_t ldc_versions[] = { {1, 0} };
1131ae08745Sheppo 
1141ae08745Sheppo /* number of supported versions */
1151ae08745Sheppo #define	LDC_NUM_VERS	(sizeof (ldc_versions) / sizeof (ldc_versions[0]))
1161ae08745Sheppo 
1171ae08745Sheppo /* Module State Pointer */
1181ae08745Sheppo static ldc_soft_state_t *ldcssp;
1191ae08745Sheppo 
1201ae08745Sheppo static struct modldrv md = {
1211ae08745Sheppo 	&mod_miscops,			/* This is a misc module */
1221ae08745Sheppo 	"sun4v LDC module v%I%",	/* Name of the module */
1231ae08745Sheppo };
1241ae08745Sheppo 
1251ae08745Sheppo static struct modlinkage ml = {
1261ae08745Sheppo 	MODREV_1,
1271ae08745Sheppo 	&md,
1281ae08745Sheppo 	NULL
1291ae08745Sheppo };
1301ae08745Sheppo 
1311ae08745Sheppo static uint64_t ldc_sup_minor;		/* Supported minor number */
1321ae08745Sheppo static hsvc_info_t ldc_hsvc = {
1331ae08745Sheppo 	HSVC_REV_1, NULL, HSVC_GROUP_LDC, 1, 0, "ldc"
1341ae08745Sheppo };
1351ae08745Sheppo 
1361ae08745Sheppo static uint64_t intr_sup_minor;		/* Supported minor number */
1371ae08745Sheppo static hsvc_info_t intr_hsvc = {
1381ae08745Sheppo 	HSVC_REV_1, NULL, HSVC_GROUP_INTR, 1, 0, "ldc"
1391ae08745Sheppo };
1401ae08745Sheppo 
141*0a55fbb7Slm66018 /*
142*0a55fbb7Slm66018  * LDC retry count and delay
143*0a55fbb7Slm66018  */
144*0a55fbb7Slm66018 int ldc_max_retries = LDC_MAX_RETRIES;
145*0a55fbb7Slm66018 clock_t ldc_delay = LDC_DELAY;
146*0a55fbb7Slm66018 
1471ae08745Sheppo #ifdef DEBUG
1481ae08745Sheppo 
1491ae08745Sheppo /*
1501ae08745Sheppo  * Print debug messages
1511ae08745Sheppo  *
1521ae08745Sheppo  * set ldcdbg to 0x7 for enabling all msgs
1531ae08745Sheppo  * 0x4 - Warnings
1541ae08745Sheppo  * 0x2 - All debug messages
1551ae08745Sheppo  * 0x1 - Minimal debug messages
1561ae08745Sheppo  *
1571ae08745Sheppo  * set ldcdbgchan to the channel number you want to debug
1581ae08745Sheppo  * setting it to -1 prints debug messages for all channels
1591ae08745Sheppo  * NOTE: ldcdbgchan has no effect on error messages
1601ae08745Sheppo  */
1611ae08745Sheppo 
1621ae08745Sheppo #define	DBG_ALL_LDCS -1
1631ae08745Sheppo 
1641ae08745Sheppo int ldcdbg = 0x0;
1651ae08745Sheppo int64_t ldcdbgchan = DBG_ALL_LDCS;
1661ae08745Sheppo 
1671ae08745Sheppo static void
1681ae08745Sheppo ldcdebug(int64_t id, const char *fmt, ...)
1691ae08745Sheppo {
1701ae08745Sheppo 	char buf[512];
1711ae08745Sheppo 	va_list ap;
1721ae08745Sheppo 
1731ae08745Sheppo 	/*
1741ae08745Sheppo 	 * Do not return if,
1751ae08745Sheppo 	 * caller wants to print it anyway - (id == DBG_ALL_LDCS)
1761ae08745Sheppo 	 * debug channel is set to all LDCs - (ldcdbgchan == DBG_ALL_LDCS)
1771ae08745Sheppo 	 * debug channel = caller specified channel
1781ae08745Sheppo 	 */
1791ae08745Sheppo 	if ((id != DBG_ALL_LDCS) &&
1801ae08745Sheppo 	    (ldcdbgchan != DBG_ALL_LDCS) &&
1811ae08745Sheppo 	    (ldcdbgchan != id)) {
1821ae08745Sheppo 		return;
1831ae08745Sheppo 	}
1841ae08745Sheppo 
1851ae08745Sheppo 	va_start(ap, fmt);
1861ae08745Sheppo 	(void) vsprintf(buf, fmt, ap);
1871ae08745Sheppo 	va_end(ap);
1881ae08745Sheppo 
1891ae08745Sheppo 	cmn_err(CE_CONT, "?%s\n", buf);
1901ae08745Sheppo }
1911ae08745Sheppo 
1921ae08745Sheppo #define	D1		\
1931ae08745Sheppo if (ldcdbg & 0x01)	\
1941ae08745Sheppo 	ldcdebug
1951ae08745Sheppo 
1961ae08745Sheppo #define	D2		\
1971ae08745Sheppo if (ldcdbg & 0x02)	\
1981ae08745Sheppo 	ldcdebug
1991ae08745Sheppo 
2001ae08745Sheppo #define	DWARN		\
2011ae08745Sheppo if (ldcdbg & 0x04)	\
2021ae08745Sheppo 	ldcdebug
2031ae08745Sheppo 
2041ae08745Sheppo #define	DUMP_PAYLOAD(id, addr)						\
2051ae08745Sheppo {									\
2061ae08745Sheppo 	char buf[65*3];							\
2071ae08745Sheppo 	int i;								\
2081ae08745Sheppo 	uint8_t *src = (uint8_t *)addr;					\
2091ae08745Sheppo 	for (i = 0; i < 64; i++, src++)					\
2101ae08745Sheppo 		(void) sprintf(&buf[i * 3], "|%02x", *src);		\
2111ae08745Sheppo 	(void) sprintf(&buf[i * 3], "|\n");				\
2121ae08745Sheppo 	D2((id), "payload: %s", buf);					\
2131ae08745Sheppo }
2141ae08745Sheppo 
2151ae08745Sheppo #define	DUMP_LDC_PKT(c, s, addr)					\
2161ae08745Sheppo {									\
2171ae08745Sheppo 	ldc_msg_t *msg = (ldc_msg_t *)(addr);				\
2181ae08745Sheppo 	uint32_t mid = ((c)->mode != LDC_MODE_RAW) ? msg->seqid : 0;	\
2191ae08745Sheppo 	if (msg->type == LDC_DATA) {                                    \
2201ae08745Sheppo 	    D2((c)->id, "%s: msg%d (/%x/%x/%x/,env[%c%c,sz=%d])",	\
2211ae08745Sheppo 	    (s), mid, msg->type, msg->stype, msg->ctrl,			\
2221ae08745Sheppo 	    (msg->env & LDC_FRAG_START) ? 'B' : ' ',                    \
2231ae08745Sheppo 	    (msg->env & LDC_FRAG_STOP) ? 'E' : ' ',                     \
2241ae08745Sheppo 	    (msg->env & LDC_LEN_MASK));					\
2251ae08745Sheppo 	} else { 							\
2261ae08745Sheppo 	    D2((c)->id, "%s: msg%d (/%x/%x/%x/,env=%x)", (s),		\
2271ae08745Sheppo 	    mid, msg->type, msg->stype, msg->ctrl, msg->env);		\
2281ae08745Sheppo 	} 								\
2291ae08745Sheppo }
2301ae08745Sheppo 
2311ae08745Sheppo #else
2321ae08745Sheppo 
2331ae08745Sheppo #define	DBG_ALL_LDCS -1
2341ae08745Sheppo 
2351ae08745Sheppo #define	D1
2361ae08745Sheppo #define	D2
2371ae08745Sheppo #define	DWARN
2381ae08745Sheppo 
2391ae08745Sheppo #define	DUMP_PAYLOAD(id, addr)
2401ae08745Sheppo #define	DUMP_LDC_PKT(c, s, addr)
2411ae08745Sheppo 
2421ae08745Sheppo #endif
2431ae08745Sheppo 
2441ae08745Sheppo #define	ZERO_PKT(p)			\
2451ae08745Sheppo 	bzero((p), sizeof (ldc_msg_t));
2461ae08745Sheppo 
2471ae08745Sheppo #define	IDX2COOKIE(idx, pg_szc, pg_shift)				\
2481ae08745Sheppo 	(((pg_szc) << LDC_COOKIE_PGSZC_SHIFT) | ((idx) << (pg_shift)))
2491ae08745Sheppo 
2501ae08745Sheppo 
2511ae08745Sheppo int
2521ae08745Sheppo _init(void)
2531ae08745Sheppo {
2541ae08745Sheppo 	int status;
2551ae08745Sheppo 
2561ae08745Sheppo 	status = hsvc_register(&ldc_hsvc, &ldc_sup_minor);
2571ae08745Sheppo 	if (status != 0) {
2581ae08745Sheppo 		cmn_err(CE_WARN, "%s: cannot negotiate hypervisor LDC services"
2591ae08745Sheppo 		    " group: 0x%lx major: %ld minor: %ld errno: %d",
2601ae08745Sheppo 		    ldc_hsvc.hsvc_modname, ldc_hsvc.hsvc_group,
2611ae08745Sheppo 		    ldc_hsvc.hsvc_major, ldc_hsvc.hsvc_minor, status);
2621ae08745Sheppo 		return (-1);
2631ae08745Sheppo 	}
2641ae08745Sheppo 
2651ae08745Sheppo 	status = hsvc_register(&intr_hsvc, &intr_sup_minor);
2661ae08745Sheppo 	if (status != 0) {
2671ae08745Sheppo 		cmn_err(CE_WARN, "%s: cannot negotiate hypervisor interrupt "
2681ae08745Sheppo 		    "services group: 0x%lx major: %ld minor: %ld errno: %d",
2691ae08745Sheppo 		    intr_hsvc.hsvc_modname, intr_hsvc.hsvc_group,
2701ae08745Sheppo 		    intr_hsvc.hsvc_major, intr_hsvc.hsvc_minor, status);
2711ae08745Sheppo 		(void) hsvc_unregister(&ldc_hsvc);
2721ae08745Sheppo 		return (-1);
2731ae08745Sheppo 	}
2741ae08745Sheppo 
2751ae08745Sheppo 	/* allocate soft state structure */
2761ae08745Sheppo 	ldcssp = kmem_zalloc(sizeof (ldc_soft_state_t), KM_SLEEP);
2771ae08745Sheppo 
2781ae08745Sheppo 	/* Link the module into the system */
2791ae08745Sheppo 	status = mod_install(&ml);
2801ae08745Sheppo 	if (status != 0) {
2811ae08745Sheppo 		kmem_free(ldcssp, sizeof (ldc_soft_state_t));
2821ae08745Sheppo 		return (status);
2831ae08745Sheppo 	}
2841ae08745Sheppo 
2851ae08745Sheppo 	/* Initialize the LDC state structure */
2861ae08745Sheppo 	mutex_init(&ldcssp->lock, NULL, MUTEX_DRIVER, NULL);
2871ae08745Sheppo 
2881ae08745Sheppo 	mutex_enter(&ldcssp->lock);
2891ae08745Sheppo 
2901ae08745Sheppo 	ldcssp->channel_count = 0;
2911ae08745Sheppo 	ldcssp->channels_open = 0;
2921ae08745Sheppo 	ldcssp->chan_list = NULL;
2931ae08745Sheppo 	ldcssp->dring_list = NULL;
2941ae08745Sheppo 
2951ae08745Sheppo 	mutex_exit(&ldcssp->lock);
2961ae08745Sheppo 
2971ae08745Sheppo 	return (0);
2981ae08745Sheppo }
2991ae08745Sheppo 
3001ae08745Sheppo int
3011ae08745Sheppo _info(struct modinfo *modinfop)
3021ae08745Sheppo {
3031ae08745Sheppo 	/* Report status of the dynamically loadable driver module */
3041ae08745Sheppo 	return (mod_info(&ml, modinfop));
3051ae08745Sheppo }
3061ae08745Sheppo 
3071ae08745Sheppo int
3081ae08745Sheppo _fini(void)
3091ae08745Sheppo {
3101ae08745Sheppo 	int 		rv, status;
3111ae08745Sheppo 	ldc_chan_t 	*ldcp;
3121ae08745Sheppo 	ldc_dring_t 	*dringp;
3131ae08745Sheppo 	ldc_mem_info_t 	minfo;
3141ae08745Sheppo 
3151ae08745Sheppo 	/* Unlink the driver module from the system */
3161ae08745Sheppo 	status = mod_remove(&ml);
3171ae08745Sheppo 	if (status) {
3181ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "_fini: mod_remove failed\n");
3191ae08745Sheppo 		return (EIO);
3201ae08745Sheppo 	}
3211ae08745Sheppo 
3221ae08745Sheppo 	/* close and finalize channels */
3231ae08745Sheppo 	ldcp = ldcssp->chan_list;
3241ae08745Sheppo 	while (ldcp != NULL) {
3251ae08745Sheppo 		(void) ldc_close((ldc_handle_t)ldcp);
3261ae08745Sheppo 		(void) ldc_fini((ldc_handle_t)ldcp);
3271ae08745Sheppo 
3281ae08745Sheppo 		ldcp = ldcp->next;
3291ae08745Sheppo 	}
3301ae08745Sheppo 
3311ae08745Sheppo 	/* Free descriptor rings */
3321ae08745Sheppo 	dringp = ldcssp->dring_list;
3331ae08745Sheppo 	while (dringp != NULL) {
3341ae08745Sheppo 		dringp = dringp->next;
3351ae08745Sheppo 
3361ae08745Sheppo 		rv = ldc_mem_dring_info((ldc_dring_handle_t)dringp, &minfo);
3371ae08745Sheppo 		if (rv == 0 && minfo.status != LDC_UNBOUND) {
3381ae08745Sheppo 			if (minfo.status == LDC_BOUND) {
3391ae08745Sheppo 				(void) ldc_mem_dring_unbind(
3401ae08745Sheppo 						(ldc_dring_handle_t)dringp);
3411ae08745Sheppo 			}
3421ae08745Sheppo 			if (minfo.status == LDC_MAPPED) {
3431ae08745Sheppo 				(void) ldc_mem_dring_unmap(
3441ae08745Sheppo 						(ldc_dring_handle_t)dringp);
3451ae08745Sheppo 			}
3461ae08745Sheppo 		}
3471ae08745Sheppo 
3481ae08745Sheppo 		(void) ldc_mem_dring_destroy((ldc_dring_handle_t)dringp);
3491ae08745Sheppo 	}
3501ae08745Sheppo 	ldcssp->dring_list = NULL;
3511ae08745Sheppo 
3521ae08745Sheppo 	/*
3531ae08745Sheppo 	 * We have successfully "removed" the driver.
3541ae08745Sheppo 	 * Destroying soft states
3551ae08745Sheppo 	 */
3561ae08745Sheppo 	mutex_destroy(&ldcssp->lock);
3571ae08745Sheppo 	kmem_free(ldcssp, sizeof (ldc_soft_state_t));
3581ae08745Sheppo 
3591ae08745Sheppo 	(void) hsvc_unregister(&ldc_hsvc);
3601ae08745Sheppo 	(void) hsvc_unregister(&intr_hsvc);
3611ae08745Sheppo 
3621ae08745Sheppo 	return (status);
3631ae08745Sheppo }
3641ae08745Sheppo 
3651ae08745Sheppo /* -------------------------------------------------------------------------- */
3661ae08745Sheppo 
3671ae08745Sheppo /*
3681ae08745Sheppo  * LDC Transport Internal Functions
3691ae08745Sheppo  */
3701ae08745Sheppo 
3711ae08745Sheppo /*
3721ae08745Sheppo  * Translate HV Errors to sun4v error codes
3731ae08745Sheppo  */
3741ae08745Sheppo static int
3751ae08745Sheppo i_ldc_h2v_error(int h_error)
3761ae08745Sheppo {
3771ae08745Sheppo 	switch (h_error) {
3781ae08745Sheppo 
3791ae08745Sheppo 	case	H_EOK:
3801ae08745Sheppo 		return (0);
3811ae08745Sheppo 
3821ae08745Sheppo 	case	H_ENORADDR:
3831ae08745Sheppo 		return (EFAULT);
3841ae08745Sheppo 
3851ae08745Sheppo 	case	H_EBADPGSZ:
3861ae08745Sheppo 	case	H_EINVAL:
3871ae08745Sheppo 		return (EINVAL);
3881ae08745Sheppo 
3891ae08745Sheppo 	case	H_EWOULDBLOCK:
3901ae08745Sheppo 		return (EWOULDBLOCK);
3911ae08745Sheppo 
3921ae08745Sheppo 	case	H_ENOACCESS:
3931ae08745Sheppo 	case	H_ENOMAP:
3941ae08745Sheppo 		return (EACCES);
3951ae08745Sheppo 
3961ae08745Sheppo 	case	H_EIO:
3971ae08745Sheppo 	case	H_ECPUERROR:
3981ae08745Sheppo 		return (EIO);
3991ae08745Sheppo 
4001ae08745Sheppo 	case	H_ENOTSUPPORTED:
4011ae08745Sheppo 		return (ENOTSUP);
4021ae08745Sheppo 
4031ae08745Sheppo 	case 	H_ETOOMANY:
4041ae08745Sheppo 		return (ENOSPC);
4051ae08745Sheppo 
4061ae08745Sheppo 	case	H_ECHANNEL:
4071ae08745Sheppo 		return (ECHRNG);
4081ae08745Sheppo 	default:
4091ae08745Sheppo 		break;
4101ae08745Sheppo 	}
4111ae08745Sheppo 
4121ae08745Sheppo 	return (EIO);
4131ae08745Sheppo }
4141ae08745Sheppo 
4151ae08745Sheppo /*
4161ae08745Sheppo  * Reconfigure the transmit queue
4171ae08745Sheppo  */
4181ae08745Sheppo static int
4191ae08745Sheppo i_ldc_txq_reconf(ldc_chan_t *ldcp)
4201ae08745Sheppo {
4211ae08745Sheppo 	int rv;
4221ae08745Sheppo 
4231ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
4241ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
4251ae08745Sheppo 	if (rv) {
4261ae08745Sheppo 		cmn_err(CE_WARN,
4271ae08745Sheppo 		    "ldc_tx_qconf: (0x%lx) cannot set qconf", ldcp->id);
4281ae08745Sheppo 		return (EIO);
4291ae08745Sheppo 	}
4301ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id, &(ldcp->tx_head),
4311ae08745Sheppo 	    &(ldcp->tx_tail), &(ldcp->link_state));
4321ae08745Sheppo 	if (rv) {
4331ae08745Sheppo 		cmn_err(CE_WARN,
4341ae08745Sheppo 		    "ldc_tx_get_state: (0x%lx) cannot get qptrs", ldcp->id);
4351ae08745Sheppo 		return (EIO);
4361ae08745Sheppo 	}
4371ae08745Sheppo 	D1(ldcp->id, "ldc_tx_get_state: (0x%llx) h=0x%llx,t=0x%llx,"
4381ae08745Sheppo 	    "s=0x%llx\n", ldcp->id, ldcp->tx_head, ldcp->tx_tail,
4391ae08745Sheppo 	    ldcp->link_state);
4401ae08745Sheppo 
4411ae08745Sheppo 	return (0);
4421ae08745Sheppo }
4431ae08745Sheppo 
4441ae08745Sheppo /*
4451ae08745Sheppo  * Reconfigure the receive queue
4461ae08745Sheppo  */
4471ae08745Sheppo static int
4481ae08745Sheppo i_ldc_rxq_reconf(ldc_chan_t *ldcp)
4491ae08745Sheppo {
4501ae08745Sheppo 	int rv;
4511ae08745Sheppo 	uint64_t rx_head, rx_tail;
4521ae08745Sheppo 
4531ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
4541ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
4551ae08745Sheppo 	    &(ldcp->link_state));
4561ae08745Sheppo 	if (rv) {
4571ae08745Sheppo 		cmn_err(CE_WARN,
4581ae08745Sheppo 		    "ldc_rx_getstate: (0x%lx) cannot get state",
4591ae08745Sheppo 		    ldcp->id);
4601ae08745Sheppo 		return (EIO);
4611ae08745Sheppo 	}
4621ae08745Sheppo 
4631ae08745Sheppo 	if (rx_head != rx_tail || ldcp->tstate > TS_READY) {
4641ae08745Sheppo 		rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra,
4651ae08745Sheppo 			ldcp->rx_q_entries);
4661ae08745Sheppo 		if (rv) {
4671ae08745Sheppo 			cmn_err(CE_WARN,
4681ae08745Sheppo 			    "ldc_rx_qconf: (0x%lx) cannot set qconf",
4691ae08745Sheppo 			    ldcp->id);
4701ae08745Sheppo 			return (EIO);
4711ae08745Sheppo 		}
4721ae08745Sheppo 		D1(ldcp->id, "ldc_rx_qconf: (0x%llx) completed qconf",
4731ae08745Sheppo 		    ldcp->id);
4741ae08745Sheppo 	}
4751ae08745Sheppo 
4761ae08745Sheppo 	return (0);
4771ae08745Sheppo }
4781ae08745Sheppo 
4791ae08745Sheppo /*
4801ae08745Sheppo  * Reset LDC state structure and its contents
4811ae08745Sheppo  */
4821ae08745Sheppo static void
4831ae08745Sheppo i_ldc_reset_state(ldc_chan_t *ldcp)
4841ae08745Sheppo {
4851ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
4861ae08745Sheppo 	ldcp->last_msg_snt = LDC_INIT_SEQID;
4871ae08745Sheppo 	ldcp->last_ack_rcd = 0;
4881ae08745Sheppo 	ldcp->last_msg_rcd = 0;
4891ae08745Sheppo 	ldcp->tx_ackd_head = ldcp->tx_head;
4901ae08745Sheppo 	ldcp->next_vidx = 0;
4911ae08745Sheppo 	ldcp->hstate = 0;
4921ae08745Sheppo 	ldcp->tstate = TS_OPEN;
4931ae08745Sheppo 	ldcp->status = LDC_OPEN;
4941ae08745Sheppo 
4951ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_UP ||
4961ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
4971ae08745Sheppo 
4981ae08745Sheppo 		if (ldcp->mode == LDC_MODE_RAW) {
4991ae08745Sheppo 			ldcp->status = LDC_UP;
5001ae08745Sheppo 			ldcp->tstate = TS_UP;
5011ae08745Sheppo 		} else {
5021ae08745Sheppo 			ldcp->status = LDC_READY;
5031ae08745Sheppo 			ldcp->tstate |= TS_LINK_READY;
5041ae08745Sheppo 		}
5051ae08745Sheppo 	}
5061ae08745Sheppo }
5071ae08745Sheppo 
5081ae08745Sheppo /*
5091ae08745Sheppo  * Reset a LDC channel
5101ae08745Sheppo  */
5111ae08745Sheppo static void
5121ae08745Sheppo i_ldc_reset(ldc_chan_t *ldcp)
5131ae08745Sheppo {
5141ae08745Sheppo 	D2(ldcp->id, "i_ldc_reset: (0x%llx) channel reset\n", ldcp->id);
5151ae08745Sheppo 
5161ae08745Sheppo 	(void) i_ldc_txq_reconf(ldcp);
5171ae08745Sheppo 	(void) i_ldc_rxq_reconf(ldcp);
5181ae08745Sheppo 	i_ldc_reset_state(ldcp);
5191ae08745Sheppo }
5201ae08745Sheppo 
5211ae08745Sheppo /*
5221ae08745Sheppo  * Clear pending interrupts
5231ae08745Sheppo  */
5241ae08745Sheppo static void
5251ae08745Sheppo i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype)
5261ae08745Sheppo {
5271ae08745Sheppo 	ldc_cnex_t *cinfo = &ldcssp->cinfo;
5281ae08745Sheppo 
5291ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
5301ae08745Sheppo 	if (cinfo->dip && ldcp->intr_pending) {
5311ae08745Sheppo 		ldcp->intr_pending = B_FALSE;
5321ae08745Sheppo 		(void) cinfo->clr_intr(cinfo->dip, ldcp->id, itype);
5331ae08745Sheppo 	}
5341ae08745Sheppo }
5351ae08745Sheppo 
5361ae08745Sheppo /*
5371ae08745Sheppo  * Set the receive queue head
538*0a55fbb7Slm66018  * Resets connection and returns an error if it fails.
5391ae08745Sheppo  */
5401ae08745Sheppo static int
5411ae08745Sheppo i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head)
5421ae08745Sheppo {
5431ae08745Sheppo 	int 	rv;
544*0a55fbb7Slm66018 	int 	retries;
5451ae08745Sheppo 
5461ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
547*0a55fbb7Slm66018 	for (retries = 0; retries < ldc_max_retries; retries++) {
548*0a55fbb7Slm66018 
549*0a55fbb7Slm66018 		if ((rv = hv_ldc_rx_set_qhead(ldcp->id, head)) == 0)
550*0a55fbb7Slm66018 			return (0);
551*0a55fbb7Slm66018 
552*0a55fbb7Slm66018 		if (rv != H_EWOULDBLOCK)
553*0a55fbb7Slm66018 			break;
554*0a55fbb7Slm66018 
555*0a55fbb7Slm66018 		/* wait for ldc_delay usecs */
556*0a55fbb7Slm66018 		drv_usecwait(ldc_delay);
5571ae08745Sheppo 	}
5581ae08745Sheppo 
559*0a55fbb7Slm66018 	cmn_err(CE_WARN, "ldc_rx_set_qhead: (0x%lx) cannot set qhead 0x%lx",
560*0a55fbb7Slm66018 		ldcp->id, head);
561*0a55fbb7Slm66018 	i_ldc_reset(ldcp);
562*0a55fbb7Slm66018 
563*0a55fbb7Slm66018 	return (ECONNRESET);
5641ae08745Sheppo }
5651ae08745Sheppo 
5661ae08745Sheppo 
5671ae08745Sheppo /*
5681ae08745Sheppo  * Returns the tx_tail to be used for transfer
5691ae08745Sheppo  * Re-reads the TX queue ptrs if and only if the
5701ae08745Sheppo  * the cached head and tail are equal (queue is full)
5711ae08745Sheppo  */
5721ae08745Sheppo static int
5731ae08745Sheppo i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail)
5741ae08745Sheppo {
5751ae08745Sheppo 	int 		rv;
5761ae08745Sheppo 	uint64_t 	current_head, new_tail;
5771ae08745Sheppo 
5781ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
5791ae08745Sheppo 	/* Read the head and tail ptrs from HV */
5801ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
5811ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
5821ae08745Sheppo 	if (rv) {
5831ae08745Sheppo 		cmn_err(CE_WARN,
5841ae08745Sheppo 		    "i_ldc_get_tx_tail: (0x%lx) cannot read qptrs\n",
5851ae08745Sheppo 		    ldcp->id);
5861ae08745Sheppo 		return (EIO);
5871ae08745Sheppo 	}
5881ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN) {
5891ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
5901ae08745Sheppo 		    "i_ldc_get_tx_tail: (0x%llx) channel not ready\n",
5911ae08745Sheppo 		    ldcp->id);
5921ae08745Sheppo 		return (ECONNRESET);
5931ae08745Sheppo 	}
5941ae08745Sheppo 
5951ae08745Sheppo 	/* In reliable mode, check against last ACKd msg */
5961ae08745Sheppo 	current_head = (ldcp->mode == LDC_MODE_RELIABLE ||
5971ae08745Sheppo 		ldcp->mode == LDC_MODE_STREAM)
5981ae08745Sheppo 		? ldcp->tx_ackd_head : ldcp->tx_head;
5991ae08745Sheppo 
6001ae08745Sheppo 	/* increment the tail */
6011ae08745Sheppo 	new_tail = (ldcp->tx_tail + LDC_PACKET_SIZE) %
6021ae08745Sheppo 		(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
6031ae08745Sheppo 
6041ae08745Sheppo 	if (new_tail == current_head) {
6051ae08745Sheppo 		DWARN(ldcp->id,
6061ae08745Sheppo 		    "i_ldc_get_tx_tail: (0x%llx) TX queue is full\n",
6071ae08745Sheppo 		    ldcp->id);
6081ae08745Sheppo 		return (EWOULDBLOCK);
6091ae08745Sheppo 	}
6101ae08745Sheppo 
6111ae08745Sheppo 	D2(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) head=0x%llx, tail=0x%llx\n",
6121ae08745Sheppo 	    ldcp->id, ldcp->tx_head, ldcp->tx_tail);
6131ae08745Sheppo 
6141ae08745Sheppo 	*tail = ldcp->tx_tail;
6151ae08745Sheppo 	return (0);
6161ae08745Sheppo }
6171ae08745Sheppo 
6181ae08745Sheppo /*
6191ae08745Sheppo  * Set the tail pointer. If HV returns EWOULDBLOCK, it will back off
620*0a55fbb7Slm66018  * and retry ldc_max_retries times before returning an error.
6211ae08745Sheppo  * Returns 0, EWOULDBLOCK or EIO
6221ae08745Sheppo  */
6231ae08745Sheppo static int
6241ae08745Sheppo i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail)
6251ae08745Sheppo {
6261ae08745Sheppo 	int		rv, retval = EWOULDBLOCK;
627*0a55fbb7Slm66018 	int 		retries;
6281ae08745Sheppo 
6291ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
630*0a55fbb7Slm66018 	for (retries = 0; retries < ldc_max_retries; retries++) {
6311ae08745Sheppo 
6321ae08745Sheppo 		if ((rv = hv_ldc_tx_set_qtail(ldcp->id, tail)) == 0) {
6331ae08745Sheppo 			retval = 0;
6341ae08745Sheppo 			break;
6351ae08745Sheppo 		}
6361ae08745Sheppo 		if (rv != H_EWOULDBLOCK) {
6371ae08745Sheppo 			DWARN(ldcp->id, "i_ldc_set_tx_tail: (0x%llx) set "
6381ae08745Sheppo 			    "qtail=0x%llx failed, rv=%d\n", ldcp->id, tail, rv);
6391ae08745Sheppo 			retval = EIO;
6401ae08745Sheppo 			break;
6411ae08745Sheppo 		}
6421ae08745Sheppo 
643*0a55fbb7Slm66018 		/* wait for ldc_delay usecs */
644*0a55fbb7Slm66018 		drv_usecwait(ldc_delay);
6451ae08745Sheppo 	}
6461ae08745Sheppo 	return (retval);
6471ae08745Sheppo }
6481ae08745Sheppo 
6491ae08745Sheppo /*
6501ae08745Sheppo  * Send a LDC message
6511ae08745Sheppo  */
6521ae08745Sheppo static int
6531ae08745Sheppo i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
6541ae08745Sheppo     uint8_t ctrlmsg)
6551ae08745Sheppo {
6561ae08745Sheppo 	int		rv;
6571ae08745Sheppo 	ldc_msg_t 	*pkt;
6581ae08745Sheppo 	uint64_t	tx_tail;
6591ae08745Sheppo 	uint32_t	curr_seqid = ldcp->last_msg_snt;
6601ae08745Sheppo 
6611ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
6621ae08745Sheppo 	/* get the current tail for the message */
6631ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
6641ae08745Sheppo 	if (rv) {
6651ae08745Sheppo 		DWARN(ldcp->id,
6661ae08745Sheppo 		    "i_ldc_send_pkt: (0x%llx) error sending pkt, "
6671ae08745Sheppo 		    "type=0x%x,subtype=0x%x,ctrl=0x%x\n",
6681ae08745Sheppo 		    ldcp->id, pkttype, subtype, ctrlmsg);
6691ae08745Sheppo 		return (rv);
6701ae08745Sheppo 	}
6711ae08745Sheppo 
6721ae08745Sheppo 	pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
6731ae08745Sheppo 	ZERO_PKT(pkt);
6741ae08745Sheppo 
6751ae08745Sheppo 	/* Initialize the packet */
6761ae08745Sheppo 	pkt->type = pkttype;
6771ae08745Sheppo 	pkt->stype = subtype;
6781ae08745Sheppo 	pkt->ctrl = ctrlmsg;
6791ae08745Sheppo 
6801ae08745Sheppo 	/* Store ackid/seqid iff it is RELIABLE mode & not a RTS/RTR message */
6811ae08745Sheppo 	if (((ctrlmsg & LDC_CTRL_MASK) != LDC_RTS) &&
6821ae08745Sheppo 	    ((ctrlmsg & LDC_CTRL_MASK) != LDC_RTR)) {
6831ae08745Sheppo 		curr_seqid++;
6841ae08745Sheppo 		if (ldcp->mode != LDC_MODE_RAW) {
6851ae08745Sheppo 			pkt->seqid = curr_seqid;
6861ae08745Sheppo 			pkt->ackid = ldcp->last_msg_rcd;
6871ae08745Sheppo 		}
6881ae08745Sheppo 	}
6891ae08745Sheppo 	DUMP_LDC_PKT(ldcp, "i_ldc_send_pkt", (uint64_t)pkt);
6901ae08745Sheppo 
6911ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
6921ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
6931ae08745Sheppo 		(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
6941ae08745Sheppo 
6951ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
6961ae08745Sheppo 	if (rv) {
6971ae08745Sheppo 		DWARN(ldcp->id,
6981ae08745Sheppo 		    "i_ldc_send_pkt:(0x%llx) error sending pkt, "
6991ae08745Sheppo 		    "type=0x%x,stype=0x%x,ctrl=0x%x\n",
7001ae08745Sheppo 		    ldcp->id, pkttype, subtype, ctrlmsg);
7011ae08745Sheppo 		return (EIO);
7021ae08745Sheppo 	}
7031ae08745Sheppo 
7041ae08745Sheppo 	ldcp->last_msg_snt = curr_seqid;
7051ae08745Sheppo 	ldcp->tx_tail = tx_tail;
7061ae08745Sheppo 
7071ae08745Sheppo 	return (0);
7081ae08745Sheppo }
7091ae08745Sheppo 
7101ae08745Sheppo /*
7111ae08745Sheppo  * Checks if packet was received in right order
7121ae08745Sheppo  * in the case of a reliable transport.
7131ae08745Sheppo  * Returns 0 if in order, else EIO
7141ae08745Sheppo  */
7151ae08745Sheppo static int
7161ae08745Sheppo i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *msg)
7171ae08745Sheppo {
7181ae08745Sheppo 	/* No seqid checking for RAW mode */
7191ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW)
7201ae08745Sheppo 		return (0);
7211ae08745Sheppo 
7221ae08745Sheppo 	/* No seqid checking for version, RTS, RTR message */
7231ae08745Sheppo 	if (msg->ctrl == LDC_VER ||
7241ae08745Sheppo 	    msg->ctrl == LDC_RTS ||
7251ae08745Sheppo 	    msg->ctrl == LDC_RTR)
7261ae08745Sheppo 		return (0);
7271ae08745Sheppo 
7281ae08745Sheppo 	/* Initial seqid to use is sent in RTS/RTR and saved in last_msg_rcd */
7291ae08745Sheppo 	if (msg->seqid != (ldcp->last_msg_rcd + 1)) {
7301ae08745Sheppo 		DWARN(ldcp->id,
7311ae08745Sheppo 		    "i_ldc_check_seqid: (0x%llx) out-of-order pkt, got 0x%x, "
7321ae08745Sheppo 		    "expecting 0x%x\n", ldcp->id, msg->seqid,
7331ae08745Sheppo 		    (ldcp->last_msg_rcd + 1));
7341ae08745Sheppo 		return (EIO);
7351ae08745Sheppo 	}
7361ae08745Sheppo 
7371ae08745Sheppo 	return (0);
7381ae08745Sheppo }
7391ae08745Sheppo 
7401ae08745Sheppo 
7411ae08745Sheppo /*
7421ae08745Sheppo  * Process an incoming version ctrl message
7431ae08745Sheppo  */
7441ae08745Sheppo static int
7451ae08745Sheppo i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg)
7461ae08745Sheppo {
7471ae08745Sheppo 	int 		rv = 0, idx = ldcp->next_vidx;
7481ae08745Sheppo 	ldc_msg_t 	*pkt;
7491ae08745Sheppo 	uint64_t	tx_tail;
7501ae08745Sheppo 	ldc_ver_t	*rcvd_ver;
7511ae08745Sheppo 
7521ae08745Sheppo 	/* get the received version */
7531ae08745Sheppo 	rcvd_ver = (ldc_ver_t *)((uint64_t)msg + LDC_PAYLOAD_VER_OFF);
7541ae08745Sheppo 
7551ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_VER: (0x%llx) received VER v%u.%u\n",
7561ae08745Sheppo 	    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
7571ae08745Sheppo 
7581ae08745Sheppo 	switch (msg->stype) {
7591ae08745Sheppo 	case LDC_INFO:
7601ae08745Sheppo 
7611ae08745Sheppo 		/* get the current tail and pkt for the response */
7621ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
7631ae08745Sheppo 		if (rv != 0) {
7641ae08745Sheppo 			DWARN(ldcp->id,
7651ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) err sending "
7661ae08745Sheppo 			    "version ACK/NACK\n", ldcp->id);
7671ae08745Sheppo 			i_ldc_reset(ldcp);
7681ae08745Sheppo 			return (ECONNRESET);
7691ae08745Sheppo 		}
7701ae08745Sheppo 
7711ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
7721ae08745Sheppo 		ZERO_PKT(pkt);
7731ae08745Sheppo 
7741ae08745Sheppo 		/* initialize the packet */
7751ae08745Sheppo 		pkt->type = LDC_CTRL;
7761ae08745Sheppo 		pkt->ctrl = LDC_VER;
7771ae08745Sheppo 
7781ae08745Sheppo 		for (;;) {
7791ae08745Sheppo 
7801ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: got %u.%u chk %u.%u\n",
7811ae08745Sheppo 			    rcvd_ver->major, rcvd_ver->minor,
7821ae08745Sheppo 			    ldc_versions[idx].major, ldc_versions[idx].minor);
7831ae08745Sheppo 
7841ae08745Sheppo 			if (rcvd_ver->major == ldc_versions[idx].major) {
7851ae08745Sheppo 				/* major version match - ACK version */
7861ae08745Sheppo 				pkt->stype = LDC_ACK;
7871ae08745Sheppo 
7881ae08745Sheppo 				/*
7891ae08745Sheppo 				 * lower minor version to the one this endpt
7901ae08745Sheppo 				 * supports, if necessary
7911ae08745Sheppo 				 */
7921ae08745Sheppo 				if (rcvd_ver->minor > ldc_versions[idx].minor)
7931ae08745Sheppo 					rcvd_ver->minor =
7941ae08745Sheppo 						ldc_versions[idx].minor;
7951ae08745Sheppo 				bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
7961ae08745Sheppo 
7971ae08745Sheppo 				break;
7981ae08745Sheppo 			}
7991ae08745Sheppo 
8001ae08745Sheppo 			if (rcvd_ver->major > ldc_versions[idx].major) {
8011ae08745Sheppo 
8021ae08745Sheppo 				D1(ldcp->id, "i_ldc_process_VER: using next"
8031ae08745Sheppo 				    " lower idx=%d, v%u.%u\n", idx,
8041ae08745Sheppo 				    ldc_versions[idx].major,
8051ae08745Sheppo 				    ldc_versions[idx].minor);
8061ae08745Sheppo 
8071ae08745Sheppo 				/* nack with next lower version */
8081ae08745Sheppo 				pkt->stype = LDC_NACK;
8091ae08745Sheppo 				bcopy(&ldc_versions[idx], pkt->udata,
8101ae08745Sheppo 				    sizeof (ldc_versions[idx]));
8111ae08745Sheppo 				ldcp->next_vidx = idx;
8121ae08745Sheppo 				break;
8131ae08745Sheppo 			}
8141ae08745Sheppo 
8151ae08745Sheppo 			/* next major version */
8161ae08745Sheppo 			idx++;
8171ae08745Sheppo 
8181ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
8191ae08745Sheppo 
8201ae08745Sheppo 			if (idx == LDC_NUM_VERS) {
8211ae08745Sheppo 				/* no version match - send NACK */
8221ae08745Sheppo 				pkt->stype = LDC_NACK;
8231ae08745Sheppo 				bzero(pkt->udata, sizeof (ldc_ver_t));
8241ae08745Sheppo 				ldcp->next_vidx = 0;
8251ae08745Sheppo 				break;
8261ae08745Sheppo 			}
8271ae08745Sheppo 		}
8281ae08745Sheppo 
8291ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
8301ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
8311ae08745Sheppo 			(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
8321ae08745Sheppo 
8331ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
8341ae08745Sheppo 		if (rv == 0) {
8351ae08745Sheppo 			ldcp->tx_tail = tx_tail;
8361ae08745Sheppo 			if (pkt->stype == LDC_ACK) {
8371ae08745Sheppo 				D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent"
8381ae08745Sheppo 				    " version ACK\n", ldcp->id);
8391ae08745Sheppo 				/* Save the ACK'd version */
8401ae08745Sheppo 				ldcp->version.major = rcvd_ver->major;
8411ae08745Sheppo 				ldcp->version.minor = rcvd_ver->minor;
842*0a55fbb7Slm66018 				ldcp->hstate |= TS_RCVD_VER;
8431ae08745Sheppo 				ldcp->tstate |= TS_VER_DONE;
8441ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
8451ae08745Sheppo 				    "(0x%llx) Agreed on version v%u.%u\n",
8461ae08745Sheppo 				    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
8471ae08745Sheppo 			}
8481ae08745Sheppo 		} else {
8491ae08745Sheppo 			DWARN(ldcp->id,
8501ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) error sending "
8511ae08745Sheppo 			    "ACK/NACK\n", ldcp->id);
8521ae08745Sheppo 			i_ldc_reset(ldcp);
8531ae08745Sheppo 			return (ECONNRESET);
8541ae08745Sheppo 		}
8551ae08745Sheppo 
8561ae08745Sheppo 		break;
8571ae08745Sheppo 
8581ae08745Sheppo 	case LDC_ACK:
8591ae08745Sheppo 		/* SUCCESS - we have agreed on a version */
8601ae08745Sheppo 		ldcp->version.major = rcvd_ver->major;
8611ae08745Sheppo 		ldcp->version.minor = rcvd_ver->minor;
8621ae08745Sheppo 		ldcp->tstate |= TS_VER_DONE;
8631ae08745Sheppo 
8641ae08745Sheppo 		D1(DBG_ALL_LDCS, "(0x%llx) Agreed on version v%u.%u\n",
8651ae08745Sheppo 		    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
8661ae08745Sheppo 
8671ae08745Sheppo 		/* initiate RTS-RTR-RDX handshake */
8681ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
8691ae08745Sheppo 		if (rv) {
8701ae08745Sheppo 			DWARN(ldcp->id,
8711ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) cannot send RTS\n",
8721ae08745Sheppo 			    ldcp->id);
8731ae08745Sheppo 			i_ldc_reset(ldcp);
8741ae08745Sheppo 			return (ECONNRESET);
8751ae08745Sheppo 		}
8761ae08745Sheppo 
8771ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
8781ae08745Sheppo 		ZERO_PKT(pkt);
8791ae08745Sheppo 
8801ae08745Sheppo 		pkt->type = LDC_CTRL;
8811ae08745Sheppo 		pkt->stype = LDC_INFO;
8821ae08745Sheppo 		pkt->ctrl = LDC_RTS;
8831ae08745Sheppo 		pkt->env = ldcp->mode;
8841ae08745Sheppo 		if (ldcp->mode != LDC_MODE_RAW)
8851ae08745Sheppo 			pkt->seqid = LDC_INIT_SEQID;
8861ae08745Sheppo 
8871ae08745Sheppo 		ldcp->last_msg_rcd = LDC_INIT_SEQID;
8881ae08745Sheppo 
8891ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "i_ldc_process_VER snd rts", (uint64_t)pkt);
8901ae08745Sheppo 
8911ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
8921ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
8931ae08745Sheppo 			(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
8941ae08745Sheppo 
8951ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
8961ae08745Sheppo 		if (rv) {
8971ae08745Sheppo 			D2(ldcp->id,
8981ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) no listener\n",
8991ae08745Sheppo 			    ldcp->id);
9001ae08745Sheppo 			i_ldc_reset(ldcp);
9011ae08745Sheppo 			return (ECONNRESET);
9021ae08745Sheppo 		}
9031ae08745Sheppo 
9041ae08745Sheppo 		ldcp->tx_tail = tx_tail;
9051ae08745Sheppo 		ldcp->hstate |= TS_SENT_RTS;
9061ae08745Sheppo 
9071ae08745Sheppo 		break;
9081ae08745Sheppo 
9091ae08745Sheppo 	case LDC_NACK:
9101ae08745Sheppo 		/* check if version in NACK is zero */
9111ae08745Sheppo 		if (rcvd_ver->major == 0 && rcvd_ver->minor == 0) {
9121ae08745Sheppo 			/* version handshake failure */
9131ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
9141ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) no version match\n",
9151ae08745Sheppo 			    ldcp->id);
9161ae08745Sheppo 			i_ldc_reset(ldcp);
9171ae08745Sheppo 			return (ECONNRESET);
9181ae08745Sheppo 		}
9191ae08745Sheppo 
9201ae08745Sheppo 		/* get the current tail and pkt for the response */
9211ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
9221ae08745Sheppo 		if (rv != 0) {
9231ae08745Sheppo 			cmn_err(CE_NOTE,
9241ae08745Sheppo 			    "i_ldc_process_VER: (0x%lx) err sending "
9251ae08745Sheppo 			    "version ACK/NACK\n", ldcp->id);
9261ae08745Sheppo 			i_ldc_reset(ldcp);
9271ae08745Sheppo 			return (ECONNRESET);
9281ae08745Sheppo 		}
9291ae08745Sheppo 
9301ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
9311ae08745Sheppo 		ZERO_PKT(pkt);
9321ae08745Sheppo 
9331ae08745Sheppo 		/* initialize the packet */
9341ae08745Sheppo 		pkt->type = LDC_CTRL;
9351ae08745Sheppo 		pkt->ctrl = LDC_VER;
9361ae08745Sheppo 		pkt->stype = LDC_INFO;
9371ae08745Sheppo 
9381ae08745Sheppo 		/* check ver in NACK msg has a match */
9391ae08745Sheppo 		for (;;) {
9401ae08745Sheppo 			if (rcvd_ver->major == ldc_versions[idx].major) {
9411ae08745Sheppo 				/*
9421ae08745Sheppo 				 * major version match - resubmit request
9431ae08745Sheppo 				 * if lower minor version to the one this endpt
9441ae08745Sheppo 				 * supports, if necessary
9451ae08745Sheppo 				 */
9461ae08745Sheppo 				if (rcvd_ver->minor > ldc_versions[idx].minor)
9471ae08745Sheppo 					rcvd_ver->minor =
9481ae08745Sheppo 						ldc_versions[idx].minor;
9491ae08745Sheppo 				bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
9501ae08745Sheppo 				break;
9511ae08745Sheppo 
9521ae08745Sheppo 			}
9531ae08745Sheppo 
9541ae08745Sheppo 			if (rcvd_ver->major > ldc_versions[idx].major) {
9551ae08745Sheppo 
9561ae08745Sheppo 				D1(ldcp->id, "i_ldc_process_VER: using next"
9571ae08745Sheppo 				    " lower idx=%d, v%u.%u\n", idx,
9581ae08745Sheppo 				    ldc_versions[idx].major,
9591ae08745Sheppo 				    ldc_versions[idx].minor);
9601ae08745Sheppo 
9611ae08745Sheppo 				/* send next lower version */
9621ae08745Sheppo 				bcopy(&ldc_versions[idx], pkt->udata,
9631ae08745Sheppo 				    sizeof (ldc_versions[idx]));
9641ae08745Sheppo 				ldcp->next_vidx = idx;
9651ae08745Sheppo 				break;
9661ae08745Sheppo 			}
9671ae08745Sheppo 
9681ae08745Sheppo 			/* next version */
9691ae08745Sheppo 			idx++;
9701ae08745Sheppo 
9711ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
9721ae08745Sheppo 
9731ae08745Sheppo 			if (idx == LDC_NUM_VERS) {
9741ae08745Sheppo 				/* no version match - terminate */
9751ae08745Sheppo 				ldcp->next_vidx = 0;
9761ae08745Sheppo 				return (ECONNRESET);
9771ae08745Sheppo 			}
9781ae08745Sheppo 		}
9791ae08745Sheppo 
9801ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
9811ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
9821ae08745Sheppo 			(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
9831ae08745Sheppo 
9841ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
9851ae08745Sheppo 		if (rv == 0) {
9861ae08745Sheppo 			D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent version"
9871ae08745Sheppo 			    "INFO v%u.%u\n", ldcp->id, ldc_versions[idx].major,
9881ae08745Sheppo 			    ldc_versions[idx].minor);
9891ae08745Sheppo 			ldcp->tx_tail = tx_tail;
9901ae08745Sheppo 		} else {
9911ae08745Sheppo 			cmn_err(CE_NOTE,
9921ae08745Sheppo 			    "i_ldc_process_VER: (0x%lx) error sending version"
9931ae08745Sheppo 			    "INFO\n", ldcp->id);
9941ae08745Sheppo 			i_ldc_reset(ldcp);
9951ae08745Sheppo 			return (ECONNRESET);
9961ae08745Sheppo 		}
9971ae08745Sheppo 
9981ae08745Sheppo 		break;
9991ae08745Sheppo 	}
10001ae08745Sheppo 
10011ae08745Sheppo 	return (rv);
10021ae08745Sheppo }
10031ae08745Sheppo 
10041ae08745Sheppo 
10051ae08745Sheppo /*
10061ae08745Sheppo  * Process an incoming RTS ctrl message
10071ae08745Sheppo  */
10081ae08745Sheppo static int
10091ae08745Sheppo i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg)
10101ae08745Sheppo {
10111ae08745Sheppo 	int 		rv = 0;
10121ae08745Sheppo 	ldc_msg_t 	*pkt;
10131ae08745Sheppo 	uint64_t	tx_tail;
10141ae08745Sheppo 	boolean_t	sent_NACK = B_FALSE;
10151ae08745Sheppo 
10161ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RTS: (0x%llx) received RTS\n", ldcp->id);
10171ae08745Sheppo 
10181ae08745Sheppo 	switch (msg->stype) {
10191ae08745Sheppo 	case LDC_NACK:
10201ae08745Sheppo 		DWARN(ldcp->id,
10211ae08745Sheppo 		    "i_ldc_process_RTS: (0x%llx) RTS NACK received\n",
10221ae08745Sheppo 		    ldcp->id);
10231ae08745Sheppo 
10241ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
10251ae08745Sheppo 		i_ldc_reset(ldcp);
10261ae08745Sheppo 		rv = ECONNRESET;
10271ae08745Sheppo 		break;
10281ae08745Sheppo 
10291ae08745Sheppo 	case LDC_INFO:
10301ae08745Sheppo 
10311ae08745Sheppo 		/* check mode */
10321ae08745Sheppo 		if (ldcp->mode != (ldc_mode_t)msg->env) {
10331ae08745Sheppo 			cmn_err(CE_NOTE,
10341ae08745Sheppo 			    "i_ldc_process_RTS: (0x%lx) mode mismatch\n",
10351ae08745Sheppo 			    ldcp->id);
10361ae08745Sheppo 			/*
10371ae08745Sheppo 			 * send NACK in response to MODE message
10381ae08745Sheppo 			 * get the current tail for the response
10391ae08745Sheppo 			 */
10401ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTS);
10411ae08745Sheppo 			if (rv) {
10421ae08745Sheppo 				/* if cannot send NACK - reset channel */
10431ae08745Sheppo 				i_ldc_reset(ldcp);
10441ae08745Sheppo 				rv = ECONNRESET;
10451ae08745Sheppo 				break;
10461ae08745Sheppo 			}
10471ae08745Sheppo 			sent_NACK = B_TRUE;
10481ae08745Sheppo 		}
10491ae08745Sheppo 		break;
10501ae08745Sheppo 	default:
10511ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RTS: (0x%llx) unexp ACK\n",
10521ae08745Sheppo 		    ldcp->id);
10531ae08745Sheppo 		i_ldc_reset(ldcp);
10541ae08745Sheppo 		rv = ECONNRESET;
10551ae08745Sheppo 		break;
10561ae08745Sheppo 	}
10571ae08745Sheppo 
10581ae08745Sheppo 	/*
10591ae08745Sheppo 	 * If either the connection was reset (when rv != 0) or
10601ae08745Sheppo 	 * a NACK was sent, we return. In the case of a NACK
10611ae08745Sheppo 	 * we dont want to consume the packet that came in but
10621ae08745Sheppo 	 * not record that we received the RTS
10631ae08745Sheppo 	 */
10641ae08745Sheppo 	if (rv || sent_NACK)
10651ae08745Sheppo 		return (rv);
10661ae08745Sheppo 
10671ae08745Sheppo 	/* record RTS received */
10681ae08745Sheppo 	ldcp->hstate |= TS_RCVD_RTS;
10691ae08745Sheppo 
10701ae08745Sheppo 	/* store initial SEQID info */
10711ae08745Sheppo 	ldcp->last_msg_snt = msg->seqid;
10721ae08745Sheppo 
10731ae08745Sheppo 	/* get the current tail for the response */
10741ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
10751ae08745Sheppo 	if (rv != 0) {
10761ae08745Sheppo 		cmn_err(CE_NOTE,
10771ae08745Sheppo 		    "i_ldc_process_RTS: (0x%lx) err sending RTR\n",
10781ae08745Sheppo 		    ldcp->id);
10791ae08745Sheppo 		i_ldc_reset(ldcp);
10801ae08745Sheppo 		return (ECONNRESET);
10811ae08745Sheppo 	}
10821ae08745Sheppo 
10831ae08745Sheppo 	pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
10841ae08745Sheppo 	ZERO_PKT(pkt);
10851ae08745Sheppo 
10861ae08745Sheppo 	/* initialize the packet */
10871ae08745Sheppo 	pkt->type = LDC_CTRL;
10881ae08745Sheppo 	pkt->stype = LDC_INFO;
10891ae08745Sheppo 	pkt->ctrl = LDC_RTR;
10901ae08745Sheppo 	pkt->env = ldcp->mode;
10911ae08745Sheppo 	if (ldcp->mode != LDC_MODE_RAW)
10921ae08745Sheppo 		pkt->seqid = LDC_INIT_SEQID;
10931ae08745Sheppo 
10941ae08745Sheppo 	ldcp->last_msg_rcd = msg->seqid;
10951ae08745Sheppo 
10961ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
10971ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
10981ae08745Sheppo 		(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
10991ae08745Sheppo 
11001ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
11011ae08745Sheppo 	if (rv == 0) {
11021ae08745Sheppo 		D2(ldcp->id,
11031ae08745Sheppo 		    "i_ldc_process_RTS: (0x%llx) sent RTR\n", ldcp->id);
11041ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "i_ldc_process_RTS sent rtr", (uint64_t)pkt);
11051ae08745Sheppo 
11061ae08745Sheppo 		ldcp->tx_tail = tx_tail;
11071ae08745Sheppo 		ldcp->hstate |= TS_SENT_RTR;
11081ae08745Sheppo 
11091ae08745Sheppo 	} else {
11101ae08745Sheppo 		cmn_err(CE_NOTE,
11111ae08745Sheppo 		    "i_ldc_process_RTS: (0x%lx) error sending RTR\n",
11121ae08745Sheppo 		    ldcp->id);
11131ae08745Sheppo 		i_ldc_reset(ldcp);
11141ae08745Sheppo 		return (ECONNRESET);
11151ae08745Sheppo 	}
11161ae08745Sheppo 
11171ae08745Sheppo 	return (0);
11181ae08745Sheppo }
11191ae08745Sheppo 
11201ae08745Sheppo /*
11211ae08745Sheppo  * Process an incoming RTR ctrl message
11221ae08745Sheppo  */
11231ae08745Sheppo static int
11241ae08745Sheppo i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg)
11251ae08745Sheppo {
11261ae08745Sheppo 	int 		rv = 0;
11271ae08745Sheppo 	boolean_t	sent_NACK = B_FALSE;
11281ae08745Sheppo 
11291ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RTR: (0x%llx) received RTR\n", ldcp->id);
11301ae08745Sheppo 
11311ae08745Sheppo 	switch (msg->stype) {
11321ae08745Sheppo 	case LDC_NACK:
11331ae08745Sheppo 		/* RTR NACK received */
11341ae08745Sheppo 		DWARN(ldcp->id,
11351ae08745Sheppo 		    "i_ldc_process_RTR: (0x%llx) RTR NACK received\n",
11361ae08745Sheppo 		    ldcp->id);
11371ae08745Sheppo 
11381ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
11391ae08745Sheppo 		i_ldc_reset(ldcp);
11401ae08745Sheppo 		rv = ECONNRESET;
11411ae08745Sheppo 
11421ae08745Sheppo 		break;
11431ae08745Sheppo 
11441ae08745Sheppo 	case LDC_INFO:
11451ae08745Sheppo 
11461ae08745Sheppo 		/* check mode */
11471ae08745Sheppo 		if (ldcp->mode != (ldc_mode_t)msg->env) {
11481ae08745Sheppo 			DWARN(ldcp->id,
11491ae08745Sheppo 			    "i_ldc_process_RTR: (0x%llx) mode mismatch\n",
11501ae08745Sheppo 			    ldcp->id);
11511ae08745Sheppo 			/*
11521ae08745Sheppo 			 * send NACK in response to MODE message
11531ae08745Sheppo 			 * get the current tail for the response
11541ae08745Sheppo 			 */
11551ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTR);
11561ae08745Sheppo 			if (rv) {
11571ae08745Sheppo 				/* if cannot send NACK - reset channel */
11581ae08745Sheppo 				i_ldc_reset(ldcp);
11591ae08745Sheppo 				rv = ECONNRESET;
11601ae08745Sheppo 				break;
11611ae08745Sheppo 			}
11621ae08745Sheppo 			sent_NACK = B_TRUE;
11631ae08745Sheppo 		}
11641ae08745Sheppo 		break;
11651ae08745Sheppo 
11661ae08745Sheppo 	default:
11671ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RTR: (0x%llx) unexp ACK\n",
11681ae08745Sheppo 		    ldcp->id);
11691ae08745Sheppo 
11701ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
11711ae08745Sheppo 		i_ldc_reset(ldcp);
11721ae08745Sheppo 		rv = ECONNRESET;
11731ae08745Sheppo 		break;
11741ae08745Sheppo 	}
11751ae08745Sheppo 
11761ae08745Sheppo 	/*
11771ae08745Sheppo 	 * If either the connection was reset (when rv != 0) or
11781ae08745Sheppo 	 * a NACK was sent, we return. In the case of a NACK
11791ae08745Sheppo 	 * we dont want to consume the packet that came in but
11801ae08745Sheppo 	 * not record that we received the RTR
11811ae08745Sheppo 	 */
11821ae08745Sheppo 	if (rv || sent_NACK)
11831ae08745Sheppo 		return (rv);
11841ae08745Sheppo 
11851ae08745Sheppo 	ldcp->last_msg_snt = msg->seqid;
11861ae08745Sheppo 	ldcp->hstate |= TS_RCVD_RTR;
11871ae08745Sheppo 
11881ae08745Sheppo 	rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_INFO, LDC_RDX);
11891ae08745Sheppo 	if (rv) {
11901ae08745Sheppo 		cmn_err(CE_NOTE,
11911ae08745Sheppo 		    "i_ldc_process_RTR: (0x%lx) cannot send RDX\n",
11921ae08745Sheppo 		    ldcp->id);
11931ae08745Sheppo 		i_ldc_reset(ldcp);
11941ae08745Sheppo 		return (ECONNRESET);
11951ae08745Sheppo 	}
11961ae08745Sheppo 	D2(ldcp->id,
11971ae08745Sheppo 	    "i_ldc_process_RTR: (0x%llx) sent RDX\n", ldcp->id);
11981ae08745Sheppo 
11991ae08745Sheppo 	ldcp->hstate |= TS_SENT_RDX;
12001ae08745Sheppo 	ldcp->tstate |= TS_HSHAKE_DONE;
12011ae08745Sheppo 	ldcp->status = LDC_UP;
12021ae08745Sheppo 
12031ae08745Sheppo 	DWARN(DBG_ALL_LDCS, "(0x%llx) Handshake Complete\n", ldcp->id);
12041ae08745Sheppo 
12051ae08745Sheppo 	return (0);
12061ae08745Sheppo }
12071ae08745Sheppo 
12081ae08745Sheppo 
12091ae08745Sheppo /*
12101ae08745Sheppo  * Process an incoming RDX ctrl message
12111ae08745Sheppo  */
12121ae08745Sheppo static int
12131ae08745Sheppo i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg)
12141ae08745Sheppo {
12151ae08745Sheppo 	int	rv = 0;
12161ae08745Sheppo 
12171ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RDX: (0x%llx) received RDX\n", ldcp->id);
12181ae08745Sheppo 
12191ae08745Sheppo 	switch (msg->stype) {
12201ae08745Sheppo 	case LDC_NACK:
12211ae08745Sheppo 		/* RDX NACK received */
12221ae08745Sheppo 		DWARN(ldcp->id,
12231ae08745Sheppo 		    "i_ldc_process_RDX: (0x%llx) RDX NACK received\n",
12241ae08745Sheppo 		    ldcp->id);
12251ae08745Sheppo 
12261ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
12271ae08745Sheppo 		i_ldc_reset(ldcp);
12281ae08745Sheppo 		rv = ECONNRESET;
12291ae08745Sheppo 
12301ae08745Sheppo 		break;
12311ae08745Sheppo 
12321ae08745Sheppo 	case LDC_INFO:
12331ae08745Sheppo 
12341ae08745Sheppo 		/*
12351ae08745Sheppo 		 * if channel is UP and a RDX received after data transmission
12361ae08745Sheppo 		 * has commenced it is an error
12371ae08745Sheppo 		 */
12381ae08745Sheppo 		if ((ldcp->tstate == TS_UP) && (ldcp->hstate & TS_RCVD_RDX)) {
12391ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
12401ae08745Sheppo 			    "i_ldc_process_RDX: (0x%llx) unexpected RDX"
12411ae08745Sheppo 			    " - LDC reset\n", ldcp->id);
12421ae08745Sheppo 			i_ldc_reset(ldcp);
12431ae08745Sheppo 			return (ECONNRESET);
12441ae08745Sheppo 		}
12451ae08745Sheppo 
12461ae08745Sheppo 		ldcp->hstate |= TS_RCVD_RDX;
12471ae08745Sheppo 		ldcp->tstate |= TS_HSHAKE_DONE;
12481ae08745Sheppo 		ldcp->status = LDC_UP;
12491ae08745Sheppo 
12501ae08745Sheppo 		D1(DBG_ALL_LDCS, "(0x%llx) Handshake Complete\n", ldcp->id);
12511ae08745Sheppo 		break;
12521ae08745Sheppo 
12531ae08745Sheppo 	default:
12541ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RDX: (0x%llx) unexp ACK\n",
12551ae08745Sheppo 		    ldcp->id);
12561ae08745Sheppo 
12571ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
12581ae08745Sheppo 		i_ldc_reset(ldcp);
12591ae08745Sheppo 		rv = ECONNRESET;
12601ae08745Sheppo 		break;
12611ae08745Sheppo 	}
12621ae08745Sheppo 
12631ae08745Sheppo 	return (rv);
12641ae08745Sheppo }
12651ae08745Sheppo 
12661ae08745Sheppo /*
12671ae08745Sheppo  * Process an incoming ACK for a data packet
12681ae08745Sheppo  */
12691ae08745Sheppo static int
12701ae08745Sheppo i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg)
12711ae08745Sheppo {
12721ae08745Sheppo 	int		rv;
12731ae08745Sheppo 	uint64_t 	tx_head;
12741ae08745Sheppo 	ldc_msg_t	*pkt;
12751ae08745Sheppo 
12761ae08745Sheppo 	/*
12771ae08745Sheppo 	 * Read the curret Tx head and tail
12781ae08745Sheppo 	 */
12791ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
12801ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
12811ae08745Sheppo 	if (rv != 0) {
12821ae08745Sheppo 		cmn_err(CE_WARN,
12831ae08745Sheppo 		    "i_ldc_process_data_ACK: (0x%lx) cannot read qptrs\n",
12841ae08745Sheppo 		    ldcp->id);
12851ae08745Sheppo 		return (0);
12861ae08745Sheppo 	}
12871ae08745Sheppo 
12881ae08745Sheppo 	/*
12891ae08745Sheppo 	 * loop from where the previous ACK location was to the
12901ae08745Sheppo 	 * current head location. This is how far the HV has
12911ae08745Sheppo 	 * actually send pkts. Pkts between head and tail are
12921ae08745Sheppo 	 * yet to be sent by HV.
12931ae08745Sheppo 	 */
12941ae08745Sheppo 	tx_head = ldcp->tx_ackd_head;
12951ae08745Sheppo 	for (;;) {
12961ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_head);
12971ae08745Sheppo 		tx_head = (tx_head + LDC_PACKET_SIZE) %
12981ae08745Sheppo 			(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
12991ae08745Sheppo 
13001ae08745Sheppo 		if (pkt->seqid == msg->ackid) {
13011ae08745Sheppo 			D2(ldcp->id,
13021ae08745Sheppo 			    "i_ldc_process_data_ACK: (0x%llx) found packet\n",
13031ae08745Sheppo 			    ldcp->id);
13041ae08745Sheppo 			ldcp->last_ack_rcd = msg->ackid;
13051ae08745Sheppo 			ldcp->tx_ackd_head = tx_head;
13061ae08745Sheppo 			break;
13071ae08745Sheppo 		}
13081ae08745Sheppo 		if (tx_head == ldcp->tx_head) {
13091ae08745Sheppo 			/* could not find packet */
13101ae08745Sheppo 			DWARN(ldcp->id,
13111ae08745Sheppo 			    "i_ldc_process_data_ACK: (0x%llx) invalid ACKid\n",
13121ae08745Sheppo 			    ldcp->id);
13131ae08745Sheppo 			break;
13141ae08745Sheppo 		}
13151ae08745Sheppo 	}
13161ae08745Sheppo 
13171ae08745Sheppo 	return (0);
13181ae08745Sheppo }
13191ae08745Sheppo 
13201ae08745Sheppo /*
13211ae08745Sheppo  * Process incoming control message
13221ae08745Sheppo  * Return 0 - session can continue
13231ae08745Sheppo  *        EAGAIN - reprocess packet - state was changed
13241ae08745Sheppo  *	  ECONNRESET - channel was reset
13251ae08745Sheppo  */
13261ae08745Sheppo static int
13271ae08745Sheppo i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg)
13281ae08745Sheppo {
13291ae08745Sheppo 	int 		rv = 0;
13301ae08745Sheppo 
13311ae08745Sheppo 	switch (ldcp->tstate) {
13321ae08745Sheppo 
13331ae08745Sheppo 	case TS_OPEN:
13341ae08745Sheppo 	case TS_READY:
13351ae08745Sheppo 
13361ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
13371ae08745Sheppo 		case LDC_VER:
13381ae08745Sheppo 			/* process version message */
13391ae08745Sheppo 			rv = i_ldc_process_VER(ldcp, msg);
13401ae08745Sheppo 			break;
13411ae08745Sheppo 		default:
13421ae08745Sheppo 			DWARN(ldcp->id,
13431ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
13441ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
13451ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
13461ae08745Sheppo 			break;
13471ae08745Sheppo 		}
13481ae08745Sheppo 
13491ae08745Sheppo 		break;
13501ae08745Sheppo 
13511ae08745Sheppo 	case TS_VREADY:
13521ae08745Sheppo 
13531ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
13541ae08745Sheppo 		case LDC_VER:
13551ae08745Sheppo 			/* peer is redoing version negotiation */
13561ae08745Sheppo 			(void) i_ldc_txq_reconf(ldcp);
13571ae08745Sheppo 			i_ldc_reset_state(ldcp);
13581ae08745Sheppo 			rv = EAGAIN;
13591ae08745Sheppo 			break;
13601ae08745Sheppo 		case LDC_RTS:
13611ae08745Sheppo 			/* process RTS message */
13621ae08745Sheppo 			rv = i_ldc_process_RTS(ldcp, msg);
13631ae08745Sheppo 			break;
13641ae08745Sheppo 		case LDC_RTR:
13651ae08745Sheppo 			/* process RTR message */
13661ae08745Sheppo 			rv = i_ldc_process_RTR(ldcp, msg);
13671ae08745Sheppo 			break;
13681ae08745Sheppo 		case LDC_RDX:
13691ae08745Sheppo 			/* process RDX message */
13701ae08745Sheppo 			rv = i_ldc_process_RDX(ldcp, msg);
13711ae08745Sheppo 			break;
13721ae08745Sheppo 		default:
13731ae08745Sheppo 			DWARN(ldcp->id,
13741ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
13751ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
13761ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
13771ae08745Sheppo 			break;
13781ae08745Sheppo 		}
13791ae08745Sheppo 
13801ae08745Sheppo 		break;
13811ae08745Sheppo 
13821ae08745Sheppo 	case TS_UP:
13831ae08745Sheppo 
13841ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
13851ae08745Sheppo 		case LDC_VER:
13861ae08745Sheppo 			DWARN(ldcp->id,
13871ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexpected VER "
13881ae08745Sheppo 			    "- LDC reset\n", ldcp->id);
13891ae08745Sheppo 			/* peer is redoing version negotiation */
13901ae08745Sheppo 			(void) i_ldc_txq_reconf(ldcp);
13911ae08745Sheppo 			i_ldc_reset_state(ldcp);
13921ae08745Sheppo 			rv = EAGAIN;
13931ae08745Sheppo 			break;
13941ae08745Sheppo 
13951ae08745Sheppo 		case LDC_RDX:
13961ae08745Sheppo 			/* process RDX message */
13971ae08745Sheppo 			rv = i_ldc_process_RDX(ldcp, msg);
13981ae08745Sheppo 			break;
13991ae08745Sheppo 
14001ae08745Sheppo 		default:
14011ae08745Sheppo 			DWARN(ldcp->id,
14021ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
14031ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
14041ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
14051ae08745Sheppo 			break;
14061ae08745Sheppo 		}
14071ae08745Sheppo 	}
14081ae08745Sheppo 
14091ae08745Sheppo 	return (rv);
14101ae08745Sheppo }
14111ae08745Sheppo 
14121ae08745Sheppo /*
14131ae08745Sheppo  * Register channel with the channel nexus
14141ae08745Sheppo  */
14151ae08745Sheppo static int
14161ae08745Sheppo i_ldc_register_channel(ldc_chan_t *ldcp)
14171ae08745Sheppo {
14181ae08745Sheppo 	int		rv = 0;
14191ae08745Sheppo 	ldc_cnex_t	*cinfo = &ldcssp->cinfo;
14201ae08745Sheppo 
14211ae08745Sheppo 	if (cinfo->dip == NULL) {
14221ae08745Sheppo 		DWARN(ldcp->id,
14231ae08745Sheppo 		    "i_ldc_register_channel: cnex has not registered\n");
14241ae08745Sheppo 		return (EAGAIN);
14251ae08745Sheppo 	}
14261ae08745Sheppo 
14271ae08745Sheppo 	rv = cinfo->reg_chan(cinfo->dip, ldcp->id, ldcp->devclass);
14281ae08745Sheppo 	if (rv) {
14291ae08745Sheppo 		DWARN(ldcp->id,
14301ae08745Sheppo 		    "i_ldc_register_channel: cannot register channel\n");
14311ae08745Sheppo 		return (rv);
14321ae08745Sheppo 	}
14331ae08745Sheppo 
14341ae08745Sheppo 	rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR,
14351ae08745Sheppo 	    i_ldc_tx_hdlr, ldcp, NULL);
14361ae08745Sheppo 	if (rv) {
14371ae08745Sheppo 		DWARN(ldcp->id,
14381ae08745Sheppo 		    "i_ldc_register_channel: cannot add Tx interrupt\n");
14391ae08745Sheppo 		(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
14401ae08745Sheppo 		return (rv);
14411ae08745Sheppo 	}
14421ae08745Sheppo 
14431ae08745Sheppo 	rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR,
14441ae08745Sheppo 	    i_ldc_rx_hdlr, ldcp, NULL);
14451ae08745Sheppo 	if (rv) {
14461ae08745Sheppo 		DWARN(ldcp->id,
14471ae08745Sheppo 		    "i_ldc_register_channel: cannot add Rx interrupt\n");
14481ae08745Sheppo 		(void) cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
14491ae08745Sheppo 		(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
14501ae08745Sheppo 		return (rv);
14511ae08745Sheppo 	}
14521ae08745Sheppo 
14531ae08745Sheppo 	ldcp->tstate |= TS_CNEX_RDY;
14541ae08745Sheppo 
14551ae08745Sheppo 	return (0);
14561ae08745Sheppo }
14571ae08745Sheppo 
14581ae08745Sheppo /*
14591ae08745Sheppo  * Unregister a channel with the channel nexus
14601ae08745Sheppo  */
14611ae08745Sheppo static int
14621ae08745Sheppo i_ldc_unregister_channel(ldc_chan_t *ldcp)
14631ae08745Sheppo {
14641ae08745Sheppo 	int		rv = 0;
14651ae08745Sheppo 	ldc_cnex_t	*cinfo = &ldcssp->cinfo;
14661ae08745Sheppo 
14671ae08745Sheppo 	if (cinfo->dip == NULL) {
14681ae08745Sheppo 		DWARN(ldcp->id,
14691ae08745Sheppo 		    "i_ldc_unregister_channel: cnex has not registered\n");
14701ae08745Sheppo 		return (EAGAIN);
14711ae08745Sheppo 	}
14721ae08745Sheppo 
14731ae08745Sheppo 	if (ldcp->tstate & TS_CNEX_RDY) {
14741ae08745Sheppo 
14751ae08745Sheppo 		rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR);
14761ae08745Sheppo 		if (rv) {
14771ae08745Sheppo 			DWARN(ldcp->id,
14781ae08745Sheppo 			    "i_ldc_unregister_channel: err removing Rx intr\n");
14791ae08745Sheppo 		}
14801ae08745Sheppo 		rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
14811ae08745Sheppo 		if (rv) {
14821ae08745Sheppo 			DWARN(ldcp->id,
14831ae08745Sheppo 			    "i_ldc_unregister_channel: err removing Tx intr\n");
14841ae08745Sheppo 		}
14851ae08745Sheppo 		rv = cinfo->unreg_chan(ldcssp->cinfo.dip, ldcp->id);
14861ae08745Sheppo 		if (rv) {
14871ae08745Sheppo 			DWARN(ldcp->id,
14881ae08745Sheppo 			    "i_ldc_unregister_channel: cannot unreg channel\n");
14891ae08745Sheppo 		}
14901ae08745Sheppo 
14911ae08745Sheppo 		ldcp->tstate &= ~TS_CNEX_RDY;
14921ae08745Sheppo 	}
14931ae08745Sheppo 
14941ae08745Sheppo 	return (0);
14951ae08745Sheppo }
14961ae08745Sheppo 
14971ae08745Sheppo 
14981ae08745Sheppo /*
14991ae08745Sheppo  * LDC transmit interrupt handler
15001ae08745Sheppo  *    triggered for chanel up/down/reset events
15011ae08745Sheppo  *    and Tx queue content changes
15021ae08745Sheppo  */
15031ae08745Sheppo static uint_t
15041ae08745Sheppo i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2)
15051ae08745Sheppo {
15061ae08745Sheppo 	_NOTE(ARGUNUSED(arg2))
15071ae08745Sheppo 
15081ae08745Sheppo 	int 		rv;
15091ae08745Sheppo 	ldc_chan_t 	*ldcp;
15101ae08745Sheppo 	boolean_t 	notify_client = B_FALSE;
15111ae08745Sheppo 	uint64_t	notify_event = 0;
15121ae08745Sheppo 
15131ae08745Sheppo 	/* Get the channel for which interrupt was received */
15141ae08745Sheppo 	ASSERT(arg1 != NULL);
15151ae08745Sheppo 	ldcp = (ldc_chan_t *)arg1;
15161ae08745Sheppo 
15171ae08745Sheppo 	D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
15181ae08745Sheppo 	    ldcp->id, ldcp);
15191ae08745Sheppo 
15201ae08745Sheppo 	/* Lock channel */
15211ae08745Sheppo 	mutex_enter(&ldcp->lock);
15221ae08745Sheppo 
15231ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail,
15241ae08745Sheppo 	    &ldcp->link_state);
15251ae08745Sheppo 	if (rv) {
15261ae08745Sheppo 		cmn_err(CE_WARN,
15271ae08745Sheppo 		    "i_ldc_tx_hdlr: (0x%lx) cannot read queue ptrs rv=0x%d\n",
15281ae08745Sheppo 		    ldcp->id, rv);
15291ae08745Sheppo 		mutex_exit(&ldcp->lock);
15301ae08745Sheppo 		return (DDI_INTR_CLAIMED);
15311ae08745Sheppo 	}
15321ae08745Sheppo 
15331ae08745Sheppo 	/*
15341ae08745Sheppo 	 * reset the channel state if the channel went down
15351ae08745Sheppo 	 * (other side unconfigured queue) or channel was reset
15361ae08745Sheppo 	 * (other side reconfigured its queue)
15371ae08745Sheppo 	 */
15381ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN) {
15391ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link down\n", ldcp->id);
15401ae08745Sheppo 		i_ldc_reset(ldcp);
15411ae08745Sheppo 		notify_client = B_TRUE;
15421ae08745Sheppo 		notify_event = LDC_EVT_DOWN;
15431ae08745Sheppo 	}
15441ae08745Sheppo 
15451ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_RESET) {
15461ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link reset\n", ldcp->id);
15471ae08745Sheppo 		i_ldc_reset(ldcp);
15481ae08745Sheppo 		notify_client = B_TRUE;
15491ae08745Sheppo 		notify_event = LDC_EVT_RESET;
15501ae08745Sheppo 	}
15511ae08745Sheppo 
15521ae08745Sheppo 	if (ldcp->tstate == TS_OPEN && ldcp->link_state == LDC_CHANNEL_UP) {
15531ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link up\n", ldcp->id);
15541ae08745Sheppo 		notify_client = B_TRUE;
15551ae08745Sheppo 		notify_event = LDC_EVT_RESET;
15561ae08745Sheppo 		ldcp->tstate |= TS_LINK_READY;
15571ae08745Sheppo 		ldcp->status = LDC_READY;
15581ae08745Sheppo 	}
15591ae08745Sheppo 
15601ae08745Sheppo 	/* if callbacks are disabled, do not notify */
15611ae08745Sheppo 	if (!ldcp->cb_enabled)
15621ae08745Sheppo 		notify_client = B_FALSE;
15631ae08745Sheppo 
15641ae08745Sheppo 	if (notify_client)
15651ae08745Sheppo 		ldcp->cb_inprogress = B_TRUE;
15661ae08745Sheppo 
15671ae08745Sheppo 	/* Unlock channel */
15681ae08745Sheppo 	mutex_exit(&ldcp->lock);
15691ae08745Sheppo 
15701ae08745Sheppo 	if (notify_client) {
15711ae08745Sheppo 		rv = ldcp->cb(notify_event, ldcp->cb_arg);
15721ae08745Sheppo 		if (rv) {
15731ae08745Sheppo 			DWARN(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) callback "
15741ae08745Sheppo 			    "failure", ldcp->id);
15751ae08745Sheppo 		}
15761ae08745Sheppo 		mutex_enter(&ldcp->lock);
15771ae08745Sheppo 		ldcp->cb_inprogress = B_FALSE;
15781ae08745Sheppo 		mutex_exit(&ldcp->lock);
15791ae08745Sheppo 	}
15801ae08745Sheppo 
15811ae08745Sheppo 	mutex_enter(&ldcp->lock);
15821ae08745Sheppo 	i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
15831ae08745Sheppo 	mutex_exit(&ldcp->lock);
15841ae08745Sheppo 
15851ae08745Sheppo 	D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) exiting handler", ldcp->id);
15861ae08745Sheppo 
15871ae08745Sheppo 	return (DDI_INTR_CLAIMED);
15881ae08745Sheppo }
15891ae08745Sheppo 
15901ae08745Sheppo /*
15911ae08745Sheppo  * LDC receive interrupt handler
15921ae08745Sheppo  *    triggered for channel with data pending to read
15931ae08745Sheppo  *    i.e. Rx queue content changes
15941ae08745Sheppo  */
15951ae08745Sheppo static uint_t
15961ae08745Sheppo i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2)
15971ae08745Sheppo {
15981ae08745Sheppo 	_NOTE(ARGUNUSED(arg2))
15991ae08745Sheppo 
16001ae08745Sheppo 	int		rv;
16011ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
16021ae08745Sheppo 	ldc_msg_t 	*msg;
16031ae08745Sheppo 	ldc_chan_t 	*ldcp;
16041ae08745Sheppo 	boolean_t 	notify_client = B_FALSE;
16051ae08745Sheppo 	uint64_t	notify_event = 0;
16061ae08745Sheppo 
16071ae08745Sheppo 	/* Get the channel for which interrupt was received */
16081ae08745Sheppo 	if (arg1 == NULL) {
16091ae08745Sheppo 		cmn_err(CE_WARN, "i_ldc_rx_hdlr: invalid arg\n");
16101ae08745Sheppo 		return (DDI_INTR_UNCLAIMED);
16111ae08745Sheppo 	}
16121ae08745Sheppo 
16131ae08745Sheppo 	ldcp = (ldc_chan_t *)arg1;
16141ae08745Sheppo 
16151ae08745Sheppo 	D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
16161ae08745Sheppo 	    ldcp->id, ldcp);
16171ae08745Sheppo 
16181ae08745Sheppo 	/* Lock channel */
16191ae08745Sheppo 	mutex_enter(&ldcp->lock);
16201ae08745Sheppo 
16211ae08745Sheppo 	/* mark interrupt as pending */
16221ae08745Sheppo 	ldcp->intr_pending = B_TRUE;
16231ae08745Sheppo 
16241ae08745Sheppo 	/*
16251ae08745Sheppo 	 * Read packet(s) from the queue
16261ae08745Sheppo 	 */
16271ae08745Sheppo 	for (;;) {
16281ae08745Sheppo 
16291ae08745Sheppo 		rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
16301ae08745Sheppo 		    &ldcp->link_state);
16311ae08745Sheppo 		if (rv) {
16321ae08745Sheppo 			cmn_err(CE_WARN,
16331ae08745Sheppo 			    "i_ldc_rx_hdlr: (0x%lx) cannot read "
16341ae08745Sheppo 			    "queue ptrs, rv=0x%d\n", ldcp->id, rv);
16351ae08745Sheppo 			i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
16361ae08745Sheppo 			mutex_exit(&ldcp->lock);
16371ae08745Sheppo 			return (DDI_INTR_CLAIMED);
16381ae08745Sheppo 		}
16391ae08745Sheppo 
16401ae08745Sheppo 		/*
16411ae08745Sheppo 		 * reset the channel state if the channel went down
16421ae08745Sheppo 		 * (other side unconfigured queue) or channel was reset
16431ae08745Sheppo 		 * (other side reconfigured its queue
16441ae08745Sheppo 		 */
16451ae08745Sheppo 		if (ldcp->link_state == LDC_CHANNEL_DOWN) {
16461ae08745Sheppo 			D1(ldcp->id, "i_ldc_rx_hdlr: channel link down\n",
16471ae08745Sheppo 			    ldcp->id);
16481ae08745Sheppo 			i_ldc_reset(ldcp);
16491ae08745Sheppo 			notify_client = B_TRUE;
16501ae08745Sheppo 			notify_event = LDC_EVT_DOWN;
16511ae08745Sheppo 			break;
16521ae08745Sheppo 		}
16531ae08745Sheppo 		if (ldcp->link_state == LDC_CHANNEL_RESET) {
16541ae08745Sheppo 			D1(ldcp->id, "i_ldc_rx_hdlr: channel link reset\n",
16551ae08745Sheppo 			    ldcp->id);
16561ae08745Sheppo 			i_ldc_reset(ldcp);
16571ae08745Sheppo 			notify_client = B_TRUE;
16581ae08745Sheppo 			notify_event = LDC_EVT_RESET;
16591ae08745Sheppo 		}
16601ae08745Sheppo 
16611ae08745Sheppo 		if (ldcp->tstate == TS_OPEN &&
16621ae08745Sheppo 		    ldcp->link_state == LDC_CHANNEL_UP) {
16631ae08745Sheppo 			D1(ldcp->id, "i_ldc_rx_hdlr: channel link up\n",
16641ae08745Sheppo 			    ldcp->id);
16651ae08745Sheppo 			notify_client = B_TRUE;
16661ae08745Sheppo 			notify_event = LDC_EVT_RESET;
16671ae08745Sheppo 			ldcp->tstate |= TS_LINK_READY;
16681ae08745Sheppo 			ldcp->status = LDC_READY;
16691ae08745Sheppo 		}
16701ae08745Sheppo 
16711ae08745Sheppo 		if (rx_head == rx_tail) {
16721ae08745Sheppo 			D2(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) No packets\n",
16731ae08745Sheppo 			    ldcp->id);
16741ae08745Sheppo 			break;
16751ae08745Sheppo 		}
16761ae08745Sheppo 		D2(ldcp->id, "i_ldc_rx_hdlr: head=0x%llx, tail=0x%llx\n",
16771ae08745Sheppo 		    rx_head, rx_tail);
16781ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "i_ldc_rx_hdlr rcd",
16791ae08745Sheppo 		    ldcp->rx_q_va + rx_head);
16801ae08745Sheppo 
16811ae08745Sheppo 		/* get the message */
16821ae08745Sheppo 		msg = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
16831ae08745Sheppo 
16841ae08745Sheppo 		/* if channel is in RAW mode or data pkt, notify and return */
16851ae08745Sheppo 		if (ldcp->mode == LDC_MODE_RAW) {
16861ae08745Sheppo 			notify_client = B_TRUE;
16871ae08745Sheppo 			notify_event |= LDC_EVT_READ;
16881ae08745Sheppo 			break;
16891ae08745Sheppo 		}
16901ae08745Sheppo 
16911ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
16921ae08745Sheppo 
16931ae08745Sheppo 			/* discard packet if channel is not up */
16941ae08745Sheppo 			if (ldcp->tstate != TS_UP) {
16951ae08745Sheppo 
16961ae08745Sheppo 				/* move the head one position */
16971ae08745Sheppo 				rx_head = (rx_head + LDC_PACKET_SIZE) %
16981ae08745Sheppo 				(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
16991ae08745Sheppo 
17001ae08745Sheppo 				if (rv = i_ldc_set_rx_head(ldcp, rx_head))
17011ae08745Sheppo 					break;
17021ae08745Sheppo 
17031ae08745Sheppo 				continue;
17041ae08745Sheppo 			} else {
17051ae08745Sheppo 				notify_client = B_TRUE;
17061ae08745Sheppo 				notify_event |= LDC_EVT_READ;
17071ae08745Sheppo 				break;
17081ae08745Sheppo 			}
17091ae08745Sheppo 		}
17101ae08745Sheppo 
17111ae08745Sheppo 		/* Check the sequence ID for the message received */
17121ae08745Sheppo 		if ((rv = i_ldc_check_seqid(ldcp, msg)) != 0) {
17131ae08745Sheppo 
17141ae08745Sheppo 			DWARN(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) seqid error, "
17151ae08745Sheppo 			    "q_ptrs=0x%lx,0x%lx", ldcp->id, rx_head, rx_tail);
17161ae08745Sheppo 
17171ae08745Sheppo 			/* Reset last_msg_rcd to start of message */
17181ae08745Sheppo 			if (ldcp->first_fragment != 0) {
17191ae08745Sheppo 				ldcp->last_msg_rcd =
17201ae08745Sheppo 					ldcp->first_fragment - 1;
17211ae08745Sheppo 				ldcp->first_fragment = 0;
17221ae08745Sheppo 			}
17231ae08745Sheppo 			/*
17241ae08745Sheppo 			 * Send a NACK due to seqid mismatch
17251ae08745Sheppo 			 */
17261ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK,
17271ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK));
17281ae08745Sheppo 
17291ae08745Sheppo 			if (rv) {
17301ae08745Sheppo 				cmn_err(CE_NOTE,
17311ae08745Sheppo 				    "i_ldc_rx_hdlr: (0x%lx) err sending "
17321ae08745Sheppo 				    "CTRL/NACK msg\n", ldcp->id);
17331ae08745Sheppo 			}
17341ae08745Sheppo 
17351ae08745Sheppo 			/* purge receive queue */
17361ae08745Sheppo 			(void) i_ldc_set_rx_head(ldcp, rx_tail);
17371ae08745Sheppo 			break;
17381ae08745Sheppo 		}
17391ae08745Sheppo 
17401ae08745Sheppo 		/* record the message ID */
17411ae08745Sheppo 		ldcp->last_msg_rcd = msg->seqid;
17421ae08745Sheppo 
17431ae08745Sheppo 		/* process control messages */
17441ae08745Sheppo 		if (msg->type & LDC_CTRL) {
17451ae08745Sheppo 			/* save current internal state */
17461ae08745Sheppo 			uint64_t tstate = ldcp->tstate;
17471ae08745Sheppo 
17481ae08745Sheppo 			rv = i_ldc_ctrlmsg(ldcp, msg);
17491ae08745Sheppo 			if (rv == EAGAIN) {
17501ae08745Sheppo 				/* re-process pkt - state was adjusted */
17511ae08745Sheppo 				continue;
17521ae08745Sheppo 			}
17531ae08745Sheppo 			if (rv == ECONNRESET) {
17541ae08745Sheppo 				notify_client = B_TRUE;
17551ae08745Sheppo 				notify_event = LDC_EVT_RESET;
17561ae08745Sheppo 				break;
17571ae08745Sheppo 			}
17581ae08745Sheppo 
17591ae08745Sheppo 			/*
17601ae08745Sheppo 			 * control message processing was successful
17611ae08745Sheppo 			 * channel transitioned to ready for communication
17621ae08745Sheppo 			 */
17631ae08745Sheppo 			if (rv == 0 && ldcp->tstate == TS_UP &&
17641ae08745Sheppo 			    tstate != ldcp->tstate) {
17651ae08745Sheppo 				notify_client = B_TRUE;
17661ae08745Sheppo 				notify_event = LDC_EVT_UP;
17671ae08745Sheppo 			}
17681ae08745Sheppo 		}
17691ae08745Sheppo 
17701ae08745Sheppo 		/* process data ACKs */
17711ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
17721ae08745Sheppo 			(void) i_ldc_process_data_ACK(ldcp, msg);
17731ae08745Sheppo 		}
17741ae08745Sheppo 
17751ae08745Sheppo 		/* move the head one position */
17761ae08745Sheppo 		rx_head = (rx_head + LDC_PACKET_SIZE) %
17771ae08745Sheppo 			(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
1778*0a55fbb7Slm66018 		if (rv = i_ldc_set_rx_head(ldcp, rx_head)) {
1779*0a55fbb7Slm66018 			notify_client = B_TRUE;
1780*0a55fbb7Slm66018 			notify_event = LDC_EVT_RESET;
17811ae08745Sheppo 			break;
1782*0a55fbb7Slm66018 		}
17831ae08745Sheppo 
17841ae08745Sheppo 	} /* for */
17851ae08745Sheppo 
17861ae08745Sheppo 	/* if callbacks are disabled, do not notify */
17871ae08745Sheppo 	if (!ldcp->cb_enabled)
17881ae08745Sheppo 		notify_client = B_FALSE;
17891ae08745Sheppo 
17901ae08745Sheppo 	if (notify_client)
17911ae08745Sheppo 		ldcp->cb_inprogress = B_TRUE;
17921ae08745Sheppo 
17931ae08745Sheppo 	/* Unlock channel */
17941ae08745Sheppo 	mutex_exit(&ldcp->lock);
17951ae08745Sheppo 
17961ae08745Sheppo 	if (notify_client) {
17971ae08745Sheppo 		rv = ldcp->cb(notify_event, ldcp->cb_arg);
17981ae08745Sheppo 		if (rv) {
17991ae08745Sheppo 			DWARN(ldcp->id,
18001ae08745Sheppo 			    "i_ldc_rx_hdlr: (0x%llx) callback failure",
18011ae08745Sheppo 			    ldcp->id);
18021ae08745Sheppo 		}
18031ae08745Sheppo 		mutex_enter(&ldcp->lock);
18041ae08745Sheppo 		ldcp->cb_inprogress = B_FALSE;
18051ae08745Sheppo 		mutex_exit(&ldcp->lock);
18061ae08745Sheppo 	}
18071ae08745Sheppo 
18081ae08745Sheppo 	mutex_enter(&ldcp->lock);
18091ae08745Sheppo 
18101ae08745Sheppo 	/*
18111ae08745Sheppo 	 * If there are data packets in the queue, the ldc_read will
18121ae08745Sheppo 	 * clear interrupts after draining the queue, else clear interrupts
18131ae08745Sheppo 	 */
18141ae08745Sheppo 	if ((notify_event & LDC_EVT_READ) == 0) {
18151ae08745Sheppo 		i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
18161ae08745Sheppo 	}
18171ae08745Sheppo 
18181ae08745Sheppo 	mutex_exit(&ldcp->lock);
18191ae08745Sheppo 
18201ae08745Sheppo 	D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) exiting handler", ldcp->id);
18211ae08745Sheppo 	return (DDI_INTR_CLAIMED);
18221ae08745Sheppo }
18231ae08745Sheppo 
18241ae08745Sheppo 
18251ae08745Sheppo /* -------------------------------------------------------------------------- */
18261ae08745Sheppo 
18271ae08745Sheppo /*
18281ae08745Sheppo  * LDC API functions
18291ae08745Sheppo  */
18301ae08745Sheppo 
18311ae08745Sheppo /*
18321ae08745Sheppo  * Initialize the channel. Allocate internal structure and memory for
18331ae08745Sheppo  * TX/RX queues, and initialize locks.
18341ae08745Sheppo  */
18351ae08745Sheppo int
18361ae08745Sheppo ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle)
18371ae08745Sheppo {
18381ae08745Sheppo 	ldc_chan_t 	*ldcp;
18391ae08745Sheppo 	int		rv, exit_val;
18401ae08745Sheppo 	uint64_t	ra_base, nentries;
18411ae08745Sheppo 
18421ae08745Sheppo 	exit_val = EINVAL;	/* guarantee an error if exit on failure */
18431ae08745Sheppo 
18441ae08745Sheppo 	if (attr == NULL) {
18451ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid attr\n", id);
18461ae08745Sheppo 		return (EINVAL);
18471ae08745Sheppo 	}
18481ae08745Sheppo 	if (handle == NULL) {
18491ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid handle\n", id);
18501ae08745Sheppo 		return (EINVAL);
18511ae08745Sheppo 	}
18521ae08745Sheppo 
18531ae08745Sheppo 	/* check if channel is valid */
18541ae08745Sheppo 	rv = hv_ldc_tx_qinfo(id, &ra_base, &nentries);
18551ae08745Sheppo 	if (rv == H_ECHANNEL) {
18561ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid channel id\n", id);
18571ae08745Sheppo 		return (EINVAL);
18581ae08745Sheppo 	}
18591ae08745Sheppo 
18601ae08745Sheppo 	/* check if the channel has already been initialized */
18611ae08745Sheppo 	mutex_enter(&ldcssp->lock);
18621ae08745Sheppo 	ldcp = ldcssp->chan_list;
18631ae08745Sheppo 	while (ldcp != NULL) {
18641ae08745Sheppo 		if (ldcp->id == id) {
18651ae08745Sheppo 			DWARN(id, "ldc_init: (0x%llx) already initialized\n",
18661ae08745Sheppo 			    id);
18671ae08745Sheppo 			mutex_exit(&ldcssp->lock);
18681ae08745Sheppo 			return (EADDRINUSE);
18691ae08745Sheppo 		}
18701ae08745Sheppo 		ldcp = ldcp->next;
18711ae08745Sheppo 	}
18721ae08745Sheppo 	mutex_exit(&ldcssp->lock);
18731ae08745Sheppo 
18741ae08745Sheppo 	ASSERT(ldcp == NULL);
18751ae08745Sheppo 
18761ae08745Sheppo 	*handle = 0;
18771ae08745Sheppo 
18781ae08745Sheppo 	/* Allocate an ldcp structure */
18791ae08745Sheppo 	ldcp = kmem_zalloc(sizeof (ldc_chan_t), KM_SLEEP);
18801ae08745Sheppo 
18811ae08745Sheppo 	/* Initialize the channel lock */
18821ae08745Sheppo 	mutex_init(&ldcp->lock, NULL, MUTEX_DRIVER, NULL);
18831ae08745Sheppo 
18841ae08745Sheppo 	/* Channel specific processing */
18851ae08745Sheppo 	mutex_enter(&ldcp->lock);
18861ae08745Sheppo 
18871ae08745Sheppo 	/* Initialize the channel */
18881ae08745Sheppo 	ldcp->id = id;
18891ae08745Sheppo 	ldcp->cb = NULL;
18901ae08745Sheppo 	ldcp->cb_arg = NULL;
18911ae08745Sheppo 	ldcp->cb_inprogress = B_FALSE;
18921ae08745Sheppo 	ldcp->cb_enabled = B_FALSE;
18931ae08745Sheppo 	ldcp->next = NULL;
18941ae08745Sheppo 
18951ae08745Sheppo 	/* Read attributes */
18961ae08745Sheppo 	ldcp->mode = attr->mode;
18971ae08745Sheppo 	ldcp->devclass = attr->devclass;
18981ae08745Sheppo 	ldcp->devinst = attr->instance;
18991ae08745Sheppo 
19001ae08745Sheppo 	ldcp->rx_q_entries =
19011ae08745Sheppo 		(attr->qlen > 0) ? attr->qlen : LDC_QUEUE_ENTRIES;
19021ae08745Sheppo 	ldcp->tx_q_entries = ldcp->rx_q_entries;
19031ae08745Sheppo 
19041ae08745Sheppo 	D1(ldcp->id,
19051ae08745Sheppo 	    "ldc_init: (0x%llx) channel attributes, class=0x%x, "
19061ae08745Sheppo 	    "instance=0x%llx,mode=%d, qlen=%d\n",
19071ae08745Sheppo 	    ldcp->id, ldcp->devclass, ldcp->devinst,
19081ae08745Sheppo 	    ldcp->mode, ldcp->rx_q_entries);
19091ae08745Sheppo 
19101ae08745Sheppo 	ldcp->next_vidx = 0;
19111ae08745Sheppo 	ldcp->tstate = 0;
19121ae08745Sheppo 	ldcp->hstate = 0;
19131ae08745Sheppo 	ldcp->last_msg_snt = LDC_INIT_SEQID;
19141ae08745Sheppo 	ldcp->last_ack_rcd = 0;
19151ae08745Sheppo 	ldcp->last_msg_rcd = 0;
19161ae08745Sheppo 
19171ae08745Sheppo 	ldcp->stream_bufferp = NULL;
19181ae08745Sheppo 	ldcp->exp_dring_list = NULL;
19191ae08745Sheppo 	ldcp->imp_dring_list = NULL;
19201ae08745Sheppo 	ldcp->mhdl_list = NULL;
19211ae08745Sheppo 
19221ae08745Sheppo 	/* Initialize payload size depending on whether channel is reliable */
19231ae08745Sheppo 	switch (ldcp->mode) {
19241ae08745Sheppo 	case LDC_MODE_RAW:
19251ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RAW;
19261ae08745Sheppo 		ldcp->read_p = i_ldc_read_raw;
19271ae08745Sheppo 		ldcp->write_p = i_ldc_write_raw;
19281ae08745Sheppo 		ldcp->mtu = 0;
19291ae08745Sheppo 		break;
19301ae08745Sheppo 	case LDC_MODE_UNRELIABLE:
19311ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_UNRELIABLE;
19321ae08745Sheppo 		ldcp->read_p = i_ldc_read_packet;
19331ae08745Sheppo 		ldcp->write_p = i_ldc_write_packet;
19341ae08745Sheppo 		ldcp->mtu = 0;
19351ae08745Sheppo 		break;
19361ae08745Sheppo 	case LDC_MODE_RELIABLE:
19371ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE;
19381ae08745Sheppo 		ldcp->read_p = i_ldc_read_packet;
19391ae08745Sheppo 		ldcp->write_p = i_ldc_write_packet;
19401ae08745Sheppo 		ldcp->mtu = 0;
19411ae08745Sheppo 		break;
19421ae08745Sheppo 	case LDC_MODE_STREAM:
19431ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE;
19441ae08745Sheppo 
19451ae08745Sheppo 		ldcp->stream_remains = 0;
19461ae08745Sheppo 		ldcp->stream_offset = 0;
19471ae08745Sheppo 		ldcp->mtu = LDC_STREAM_MTU;
19481ae08745Sheppo 		ldcp->stream_bufferp = kmem_alloc(ldcp->mtu, KM_SLEEP);
19491ae08745Sheppo 		ldcp->read_p = i_ldc_read_stream;
19501ae08745Sheppo 		ldcp->write_p = i_ldc_write_stream;
19511ae08745Sheppo 		break;
19521ae08745Sheppo 	default:
19531ae08745Sheppo 		exit_val = EINVAL;
19541ae08745Sheppo 		goto cleanup_on_exit;
19551ae08745Sheppo 	}
19561ae08745Sheppo 
19571ae08745Sheppo 	/* Create a transmit queue */
19581ae08745Sheppo 	ldcp->tx_q_va = (uint64_t)
19591ae08745Sheppo 		contig_mem_alloc(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
19601ae08745Sheppo 	if (ldcp->tx_q_va == NULL) {
19611ae08745Sheppo 		cmn_err(CE_WARN,
19621ae08745Sheppo 		    "ldc_init: (0x%lx) TX queue allocation failed\n",
19631ae08745Sheppo 		    ldcp->id);
19641ae08745Sheppo 		exit_val = ENOMEM;
19651ae08745Sheppo 		goto cleanup_on_exit;
19661ae08745Sheppo 	}
19671ae08745Sheppo 	ldcp->tx_q_ra = va_to_pa((caddr_t)ldcp->tx_q_va);
19681ae08745Sheppo 
19691ae08745Sheppo 	D2(ldcp->id, "ldc_init: txq_va=0x%llx, txq_ra=0x%llx, entries=0x%llx\n",
19701ae08745Sheppo 	    ldcp->tx_q_va, ldcp->tx_q_ra, ldcp->tx_q_entries);
19711ae08745Sheppo 
19721ae08745Sheppo 	ldcp->tstate |= TS_TXQ_RDY;
19731ae08745Sheppo 
19741ae08745Sheppo 	/* Create a receive queue */
19751ae08745Sheppo 	ldcp->rx_q_va = (uint64_t)
19761ae08745Sheppo 		contig_mem_alloc(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
19771ae08745Sheppo 	if (ldcp->rx_q_va == NULL) {
19781ae08745Sheppo 		cmn_err(CE_WARN,
19791ae08745Sheppo 		    "ldc_init: (0x%lx) RX queue allocation failed\n",
19801ae08745Sheppo 		    ldcp->id);
19811ae08745Sheppo 		exit_val = ENOMEM;
19821ae08745Sheppo 		goto cleanup_on_exit;
19831ae08745Sheppo 	}
19841ae08745Sheppo 	ldcp->rx_q_ra = va_to_pa((caddr_t)ldcp->rx_q_va);
19851ae08745Sheppo 
19861ae08745Sheppo 	D2(ldcp->id, "ldc_init: rxq_va=0x%llx, rxq_ra=0x%llx, entries=0x%llx\n",
19871ae08745Sheppo 	    ldcp->rx_q_va, ldcp->rx_q_ra, ldcp->rx_q_entries);
19881ae08745Sheppo 
19891ae08745Sheppo 	ldcp->tstate |= TS_RXQ_RDY;
19901ae08745Sheppo 
19911ae08745Sheppo 	/* Init descriptor ring and memory handle list lock */
19921ae08745Sheppo 	mutex_init(&ldcp->exp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
19931ae08745Sheppo 	mutex_init(&ldcp->imp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
19941ae08745Sheppo 	mutex_init(&ldcp->mlist_lock, NULL, MUTEX_DRIVER, NULL);
19951ae08745Sheppo 
19961ae08745Sheppo 	/* mark status as INITialized */
19971ae08745Sheppo 	ldcp->status = LDC_INIT;
19981ae08745Sheppo 
19991ae08745Sheppo 	mutex_exit(&ldcp->lock);
20001ae08745Sheppo 
20011ae08745Sheppo 	/* Add to channel list */
20021ae08745Sheppo 	mutex_enter(&ldcssp->lock);
20031ae08745Sheppo 	ldcp->next = ldcssp->chan_list;
20041ae08745Sheppo 	ldcssp->chan_list = ldcp;
20051ae08745Sheppo 	ldcssp->channel_count++;
20061ae08745Sheppo 	mutex_exit(&ldcssp->lock);
20071ae08745Sheppo 
20081ae08745Sheppo 	/* set the handle */
20091ae08745Sheppo 	*handle = (ldc_handle_t)ldcp;
20101ae08745Sheppo 
20111ae08745Sheppo 	D1(ldcp->id, "ldc_init: (0x%llx) channel initialized\n", ldcp->id);
20121ae08745Sheppo 
20131ae08745Sheppo 	return (0);
20141ae08745Sheppo 
20151ae08745Sheppo cleanup_on_exit:
20161ae08745Sheppo 
20171ae08745Sheppo 	if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp)
20181ae08745Sheppo 		kmem_free(ldcp->stream_bufferp, ldcp->mtu);
20191ae08745Sheppo 
20201ae08745Sheppo 	if (ldcp->tstate & TS_TXQ_RDY)
20211ae08745Sheppo 		contig_mem_free((caddr_t)ldcp->tx_q_va,
20221ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT));
20231ae08745Sheppo 
20241ae08745Sheppo 	if (ldcp->tstate & TS_RXQ_RDY)
20251ae08745Sheppo 		contig_mem_free((caddr_t)ldcp->rx_q_va,
20261ae08745Sheppo 		    (ldcp->rx_q_entries << LDC_PACKET_SHIFT));
20271ae08745Sheppo 
20281ae08745Sheppo 	mutex_exit(&ldcp->lock);
20291ae08745Sheppo 	mutex_destroy(&ldcp->lock);
20301ae08745Sheppo 
20311ae08745Sheppo 	if (ldcp)
20321ae08745Sheppo 		kmem_free(ldcp, sizeof (ldc_chan_t));
20331ae08745Sheppo 
20341ae08745Sheppo 	return (exit_val);
20351ae08745Sheppo }
20361ae08745Sheppo 
20371ae08745Sheppo /*
20381ae08745Sheppo  * Finalizes the LDC connection. It will return EBUSY if the
20391ae08745Sheppo  * channel is open. A ldc_close() has to be done prior to
20401ae08745Sheppo  * a ldc_fini operation. It frees TX/RX queues, associated
20411ae08745Sheppo  * with the channel
20421ae08745Sheppo  */
20431ae08745Sheppo int
20441ae08745Sheppo ldc_fini(ldc_handle_t handle)
20451ae08745Sheppo {
20461ae08745Sheppo 	ldc_chan_t 	*ldcp;
20471ae08745Sheppo 	ldc_chan_t 	*tmp_ldcp;
20481ae08745Sheppo 	uint64_t 	id;
20491ae08745Sheppo 
20501ae08745Sheppo 	if (handle == NULL) {
20511ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel handle\n");
20521ae08745Sheppo 		return (EINVAL);
20531ae08745Sheppo 	}
20541ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
20551ae08745Sheppo 	id = ldcp->id;
20561ae08745Sheppo 
20571ae08745Sheppo 	mutex_enter(&ldcp->lock);
20581ae08745Sheppo 
20591ae08745Sheppo 	if (ldcp->tstate > TS_INIT) {
20601ae08745Sheppo 		DWARN(ldcp->id, "ldc_fini: (0x%llx) channel is open\n",
20611ae08745Sheppo 		    ldcp->id);
20621ae08745Sheppo 		mutex_exit(&ldcp->lock);
20631ae08745Sheppo 		return (EBUSY);
20641ae08745Sheppo 	}
20651ae08745Sheppo 
20661ae08745Sheppo 	/* Remove from the channel list */
20671ae08745Sheppo 	mutex_enter(&ldcssp->lock);
20681ae08745Sheppo 	tmp_ldcp = ldcssp->chan_list;
20691ae08745Sheppo 	if (tmp_ldcp == ldcp) {
20701ae08745Sheppo 		ldcssp->chan_list = ldcp->next;
20711ae08745Sheppo 		ldcp->next = NULL;
20721ae08745Sheppo 	} else {
20731ae08745Sheppo 		while (tmp_ldcp != NULL) {
20741ae08745Sheppo 			if (tmp_ldcp->next == ldcp) {
20751ae08745Sheppo 				tmp_ldcp->next = ldcp->next;
20761ae08745Sheppo 				ldcp->next = NULL;
20771ae08745Sheppo 				break;
20781ae08745Sheppo 			}
20791ae08745Sheppo 			tmp_ldcp = tmp_ldcp->next;
20801ae08745Sheppo 		}
20811ae08745Sheppo 		if (tmp_ldcp == NULL) {
20821ae08745Sheppo 			DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel hdl\n");
20831ae08745Sheppo 			mutex_exit(&ldcssp->lock);
20841ae08745Sheppo 			mutex_exit(&ldcp->lock);
20851ae08745Sheppo 			return (EINVAL);
20861ae08745Sheppo 		}
20871ae08745Sheppo 	}
20881ae08745Sheppo 
20891ae08745Sheppo 	ldcssp->channel_count--;
20901ae08745Sheppo 
20911ae08745Sheppo 	mutex_exit(&ldcssp->lock);
20921ae08745Sheppo 
20931ae08745Sheppo 	/* Free the map table for this channel */
20941ae08745Sheppo 	if (ldcp->mtbl) {
20951ae08745Sheppo 		(void) hv_ldc_set_map_table(ldcp->id, NULL, NULL);
20961ae08745Sheppo 		contig_mem_free(ldcp->mtbl->table, ldcp->mtbl->size);
20971ae08745Sheppo 		mutex_destroy(&ldcp->mtbl->lock);
20981ae08745Sheppo 		kmem_free(ldcp->mtbl, sizeof (ldc_mtbl_t));
20991ae08745Sheppo 	}
21001ae08745Sheppo 
21011ae08745Sheppo 	/* Destroy descriptor ring and memory handle list lock */
21021ae08745Sheppo 	mutex_destroy(&ldcp->exp_dlist_lock);
21031ae08745Sheppo 	mutex_destroy(&ldcp->imp_dlist_lock);
21041ae08745Sheppo 	mutex_destroy(&ldcp->mlist_lock);
21051ae08745Sheppo 
21061ae08745Sheppo 	/* Free the stream buffer for STREAM_MODE */
21071ae08745Sheppo 	if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp)
21081ae08745Sheppo 		kmem_free(ldcp->stream_bufferp, ldcp->mtu);
21091ae08745Sheppo 
21101ae08745Sheppo 	/* Free the RX queue */
21111ae08745Sheppo 	contig_mem_free((caddr_t)ldcp->rx_q_va,
21121ae08745Sheppo 	    (ldcp->rx_q_entries << LDC_PACKET_SHIFT));
21131ae08745Sheppo 	ldcp->tstate &= ~TS_RXQ_RDY;
21141ae08745Sheppo 
21151ae08745Sheppo 	/* Free the TX queue */
21161ae08745Sheppo 	contig_mem_free((caddr_t)ldcp->tx_q_va,
21171ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT));
21181ae08745Sheppo 	ldcp->tstate &= ~TS_TXQ_RDY;
21191ae08745Sheppo 
21201ae08745Sheppo 
21211ae08745Sheppo 	mutex_exit(&ldcp->lock);
21221ae08745Sheppo 
21231ae08745Sheppo 	/* Destroy mutex */
21241ae08745Sheppo 	mutex_destroy(&ldcp->lock);
21251ae08745Sheppo 
21261ae08745Sheppo 	/* free channel structure */
21271ae08745Sheppo 	kmem_free(ldcp, sizeof (ldc_chan_t));
21281ae08745Sheppo 
21291ae08745Sheppo 	D1(id, "ldc_fini: (0x%llx) channel finalized\n", id);
21301ae08745Sheppo 
21311ae08745Sheppo 	return (0);
21321ae08745Sheppo }
21331ae08745Sheppo 
21341ae08745Sheppo /*
21351ae08745Sheppo  * Open the LDC channel for use. It registers the TX/RX queues
21361ae08745Sheppo  * with the Hypervisor. It also specifies the interrupt number
21371ae08745Sheppo  * and target CPU for this channel
21381ae08745Sheppo  */
21391ae08745Sheppo int
21401ae08745Sheppo ldc_open(ldc_handle_t handle)
21411ae08745Sheppo {
21421ae08745Sheppo 	ldc_chan_t 	*ldcp;
21431ae08745Sheppo 	int 		rv;
21441ae08745Sheppo 
21451ae08745Sheppo 	if (handle == NULL) {
21461ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_open: invalid channel handle\n");
21471ae08745Sheppo 		return (EINVAL);
21481ae08745Sheppo 	}
21491ae08745Sheppo 
21501ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
21511ae08745Sheppo 
21521ae08745Sheppo 	mutex_enter(&ldcp->lock);
21531ae08745Sheppo 
21541ae08745Sheppo 	if (ldcp->tstate < TS_INIT) {
21551ae08745Sheppo 		DWARN(ldcp->id,
21561ae08745Sheppo 		    "ldc_open: (0x%llx) channel not initialized\n", ldcp->id);
21571ae08745Sheppo 		mutex_exit(&ldcp->lock);
21581ae08745Sheppo 		return (EFAULT);
21591ae08745Sheppo 	}
21601ae08745Sheppo 	if (ldcp->tstate >= TS_OPEN) {
21611ae08745Sheppo 		DWARN(ldcp->id,
21621ae08745Sheppo 		    "ldc_open: (0x%llx) channel is already open\n", ldcp->id);
21631ae08745Sheppo 		mutex_exit(&ldcp->lock);
21641ae08745Sheppo 		return (EFAULT);
21651ae08745Sheppo 	}
21661ae08745Sheppo 
21671ae08745Sheppo 	/*
21681ae08745Sheppo 	 * Unregister/Register the tx queue with the hypervisor
21691ae08745Sheppo 	 */
21701ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
21711ae08745Sheppo 	if (rv) {
21721ae08745Sheppo 		cmn_err(CE_WARN,
21731ae08745Sheppo 		    "ldc_open: (0x%lx) channel tx queue unconf failed\n",
21741ae08745Sheppo 		    ldcp->id);
21751ae08745Sheppo 		mutex_exit(&ldcp->lock);
21761ae08745Sheppo 		return (EIO);
21771ae08745Sheppo 	}
21781ae08745Sheppo 
21791ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
21801ae08745Sheppo 	if (rv) {
21811ae08745Sheppo 		cmn_err(CE_WARN,
21821ae08745Sheppo 		    "ldc_open: (0x%lx) channel tx queue conf failed\n",
21831ae08745Sheppo 		    ldcp->id);
21841ae08745Sheppo 		mutex_exit(&ldcp->lock);
21851ae08745Sheppo 		return (EIO);
21861ae08745Sheppo 	}
21871ae08745Sheppo 
21881ae08745Sheppo 	D2(ldcp->id, "ldc_open: (0x%llx) registered tx queue with LDC\n",
21891ae08745Sheppo 	    ldcp->id);
21901ae08745Sheppo 
21911ae08745Sheppo 	/*
21921ae08745Sheppo 	 * Unregister/Register the rx queue with the hypervisor
21931ae08745Sheppo 	 */
21941ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
21951ae08745Sheppo 	if (rv) {
21961ae08745Sheppo 		cmn_err(CE_WARN,
21971ae08745Sheppo 		    "ldc_open: (0x%lx) channel rx queue unconf failed\n",
21981ae08745Sheppo 		    ldcp->id);
21991ae08745Sheppo 		mutex_exit(&ldcp->lock);
22001ae08745Sheppo 		return (EIO);
22011ae08745Sheppo 	}
22021ae08745Sheppo 
22031ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra, ldcp->rx_q_entries);
22041ae08745Sheppo 	if (rv) {
22051ae08745Sheppo 		cmn_err(CE_WARN,
22061ae08745Sheppo 		    "ldc_open: (0x%lx) channel rx queue conf failed\n",
22071ae08745Sheppo 		    ldcp->id);
22081ae08745Sheppo 		mutex_exit(&ldcp->lock);
22091ae08745Sheppo 		return (EIO);
22101ae08745Sheppo 	}
22111ae08745Sheppo 
22121ae08745Sheppo 	D2(ldcp->id, "ldc_open: (0x%llx) registered rx queue with LDC\n",
22131ae08745Sheppo 	    ldcp->id);
22141ae08745Sheppo 
22151ae08745Sheppo 	ldcp->tstate |= TS_QCONF_RDY;
22161ae08745Sheppo 
22171ae08745Sheppo 	/* Register the channel with the channel nexus */
22181ae08745Sheppo 	rv = i_ldc_register_channel(ldcp);
22191ae08745Sheppo 	if (rv && rv != EAGAIN) {
22201ae08745Sheppo 		cmn_err(CE_WARN,
22211ae08745Sheppo 		    "ldc_open: (0x%lx) channel register failed\n", ldcp->id);
22221ae08745Sheppo 		(void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
22231ae08745Sheppo 		(void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
22241ae08745Sheppo 		mutex_exit(&ldcp->lock);
22251ae08745Sheppo 		return (EIO);
22261ae08745Sheppo 	}
22271ae08745Sheppo 
22281ae08745Sheppo 	/* mark channel in OPEN state */
22291ae08745Sheppo 	ldcp->status = LDC_OPEN;
22301ae08745Sheppo 
22311ae08745Sheppo 	/* Read channel state */
22321ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
22331ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
22341ae08745Sheppo 	if (rv) {
22351ae08745Sheppo 		cmn_err(CE_WARN,
22361ae08745Sheppo 		    "ldc_open: (0x%lx) cannot read channel state\n",
22371ae08745Sheppo 		    ldcp->id);
22381ae08745Sheppo 		(void) i_ldc_unregister_channel(ldcp);
22391ae08745Sheppo 		(void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
22401ae08745Sheppo 		(void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
22411ae08745Sheppo 		mutex_exit(&ldcp->lock);
22421ae08745Sheppo 		return (EIO);
22431ae08745Sheppo 	}
22441ae08745Sheppo 
22451ae08745Sheppo 	/*
22461ae08745Sheppo 	 * set the ACKd head to current head location for reliable &
22471ae08745Sheppo 	 * streaming mode
22481ae08745Sheppo 	 */
22491ae08745Sheppo 	ldcp->tx_ackd_head = ldcp->tx_head;
22501ae08745Sheppo 
22511ae08745Sheppo 	/* mark channel ready if HV report link is UP (peer alloc'd Rx queue) */
22521ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_UP ||
22531ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
22541ae08745Sheppo 		ldcp->tstate |= TS_LINK_READY;
22551ae08745Sheppo 		ldcp->status = LDC_READY;
22561ae08745Sheppo 	}
22571ae08745Sheppo 
22581ae08745Sheppo 	/*
22591ae08745Sheppo 	 * if channel is being opened in RAW mode - no handshake is needed
22601ae08745Sheppo 	 * switch the channel READY and UP state
22611ae08745Sheppo 	 */
22621ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW) {
22631ae08745Sheppo 		ldcp->tstate = TS_UP;	/* set bits associated with LDC UP */
22641ae08745Sheppo 		ldcp->status = LDC_UP;
22651ae08745Sheppo 	}
22661ae08745Sheppo 
22671ae08745Sheppo 	mutex_exit(&ldcp->lock);
22681ae08745Sheppo 
22691ae08745Sheppo 	/*
22701ae08745Sheppo 	 * Increment number of open channels
22711ae08745Sheppo 	 */
22721ae08745Sheppo 	mutex_enter(&ldcssp->lock);
22731ae08745Sheppo 	ldcssp->channels_open++;
22741ae08745Sheppo 	mutex_exit(&ldcssp->lock);
22751ae08745Sheppo 
22761ae08745Sheppo 	D1(ldcp->id,
22771ae08745Sheppo 	    "ldc_open: (0x%llx) channel (0x%p) open for use (tstate=0x%x)\n",
22781ae08745Sheppo 	    ldcp->id, ldcp, ldcp->tstate);
22791ae08745Sheppo 
22801ae08745Sheppo 	return (0);
22811ae08745Sheppo }
22821ae08745Sheppo 
22831ae08745Sheppo /*
22841ae08745Sheppo  * Close the LDC connection. It will return EBUSY if there
22851ae08745Sheppo  * are memory segments or descriptor rings either bound to or
22861ae08745Sheppo  * mapped over the channel
22871ae08745Sheppo  */
22881ae08745Sheppo int
22891ae08745Sheppo ldc_close(ldc_handle_t handle)
22901ae08745Sheppo {
22911ae08745Sheppo 	ldc_chan_t 	*ldcp;
22921ae08745Sheppo 	int		rv = 0;
22931ae08745Sheppo 	boolean_t	chk_done = B_FALSE;
22941ae08745Sheppo 
22951ae08745Sheppo 	if (handle == NULL) {
22961ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_close: invalid channel handle\n");
22971ae08745Sheppo 		return (EINVAL);
22981ae08745Sheppo 	}
22991ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
23001ae08745Sheppo 
23011ae08745Sheppo 	mutex_enter(&ldcp->lock);
23021ae08745Sheppo 
23031ae08745Sheppo 	/* return error if channel is not open */
23041ae08745Sheppo 	if (ldcp->tstate < TS_OPEN) {
23051ae08745Sheppo 		DWARN(ldcp->id,
23061ae08745Sheppo 		    "ldc_close: (0x%llx) channel is not open\n", ldcp->id);
23071ae08745Sheppo 		mutex_exit(&ldcp->lock);
23081ae08745Sheppo 		return (EFAULT);
23091ae08745Sheppo 	}
23101ae08745Sheppo 
23111ae08745Sheppo 	/* if any memory handles, drings, are bound or mapped cannot close */
23121ae08745Sheppo 	if (ldcp->mhdl_list != NULL) {
23131ae08745Sheppo 		DWARN(ldcp->id,
23141ae08745Sheppo 		    "ldc_close: (0x%llx) channel has bound memory handles\n",
23151ae08745Sheppo 		    ldcp->id);
23161ae08745Sheppo 		mutex_exit(&ldcp->lock);
23171ae08745Sheppo 		return (EBUSY);
23181ae08745Sheppo 	}
23191ae08745Sheppo 	if (ldcp->exp_dring_list != NULL) {
23201ae08745Sheppo 		DWARN(ldcp->id,
23211ae08745Sheppo 		    "ldc_close: (0x%llx) channel has bound descriptor rings\n",
23221ae08745Sheppo 		    ldcp->id);
23231ae08745Sheppo 		mutex_exit(&ldcp->lock);
23241ae08745Sheppo 		return (EBUSY);
23251ae08745Sheppo 	}
23261ae08745Sheppo 	if (ldcp->imp_dring_list != NULL) {
23271ae08745Sheppo 		DWARN(ldcp->id,
23281ae08745Sheppo 		    "ldc_close: (0x%llx) channel has mapped descriptor rings\n",
23291ae08745Sheppo 		    ldcp->id);
23301ae08745Sheppo 		mutex_exit(&ldcp->lock);
23311ae08745Sheppo 		return (EBUSY);
23321ae08745Sheppo 	}
23331ae08745Sheppo 
23341ae08745Sheppo 	/*
23351ae08745Sheppo 	 * Wait for pending transmits to complete i.e Tx queue to drain
23361ae08745Sheppo 	 * if there are pending pkts - wait 1 ms and retry again
23371ae08745Sheppo 	 */
23381ae08745Sheppo 	for (;;) {
23391ae08745Sheppo 
23401ae08745Sheppo 		rv = hv_ldc_tx_get_state(ldcp->id,
23411ae08745Sheppo 		    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
23421ae08745Sheppo 		if (rv) {
23431ae08745Sheppo 			cmn_err(CE_WARN,
23441ae08745Sheppo 			    "ldc_close: (0x%lx) cannot read qptrs\n", ldcp->id);
23451ae08745Sheppo 			mutex_exit(&ldcp->lock);
23461ae08745Sheppo 			return (EIO);
23471ae08745Sheppo 		}
23481ae08745Sheppo 
23491ae08745Sheppo 		if (ldcp->tx_head == ldcp->tx_tail ||
23501ae08745Sheppo 		    ldcp->link_state != LDC_CHANNEL_UP) {
23511ae08745Sheppo 			break;
23521ae08745Sheppo 		}
23531ae08745Sheppo 
23541ae08745Sheppo 		if (chk_done) {
23551ae08745Sheppo 			DWARN(ldcp->id,
23561ae08745Sheppo 			    "ldc_close: (0x%llx) Tx queue drain timeout\n",
23571ae08745Sheppo 			    ldcp->id);
23581ae08745Sheppo 			break;
23591ae08745Sheppo 		}
23601ae08745Sheppo 
23611ae08745Sheppo 		/* wait for one ms and try again */
23621ae08745Sheppo 		delay(drv_usectohz(1000));
23631ae08745Sheppo 		chk_done = B_TRUE;
23641ae08745Sheppo 	}
23651ae08745Sheppo 
23661ae08745Sheppo 	/*
23671ae08745Sheppo 	 * Unregister the channel with the nexus
23681ae08745Sheppo 	 */
23691ae08745Sheppo 	rv = i_ldc_unregister_channel(ldcp);
23701ae08745Sheppo 	if (rv && rv != EAGAIN) {
23711ae08745Sheppo 		cmn_err(CE_WARN,
23721ae08745Sheppo 		    "ldc_close: (0x%lx) channel unregister failed\n",
23731ae08745Sheppo 		    ldcp->id);
23741ae08745Sheppo 		mutex_exit(&ldcp->lock);
23751ae08745Sheppo 		return (rv);
23761ae08745Sheppo 	}
23771ae08745Sheppo 
23781ae08745Sheppo 	/*
23791ae08745Sheppo 	 * Unregister queues
23801ae08745Sheppo 	 */
23811ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
23821ae08745Sheppo 	if (rv) {
23831ae08745Sheppo 		cmn_err(CE_WARN,
23841ae08745Sheppo 		    "ldc_close: (0x%lx) channel TX queue unconf failed\n",
23851ae08745Sheppo 		    ldcp->id);
23861ae08745Sheppo 		mutex_exit(&ldcp->lock);
23871ae08745Sheppo 		return (EIO);
23881ae08745Sheppo 	}
23891ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
23901ae08745Sheppo 	if (rv) {
23911ae08745Sheppo 		cmn_err(CE_WARN,
23921ae08745Sheppo 		    "ldc_close: (0x%lx) channel RX queue unconf failed\n",
23931ae08745Sheppo 		    ldcp->id);
23941ae08745Sheppo 		mutex_exit(&ldcp->lock);
23951ae08745Sheppo 		return (EIO);
23961ae08745Sheppo 	}
23971ae08745Sheppo 
23981ae08745Sheppo 	ldcp->tstate &= ~TS_QCONF_RDY;
23991ae08745Sheppo 
24001ae08745Sheppo 	/* Reset channel state information */
24011ae08745Sheppo 	i_ldc_reset_state(ldcp);
24021ae08745Sheppo 
24031ae08745Sheppo 	/* Mark channel as down and in initialized state */
24041ae08745Sheppo 	ldcp->tx_ackd_head = 0;
24051ae08745Sheppo 	ldcp->tx_head = 0;
24061ae08745Sheppo 	ldcp->tstate = TS_INIT;
24071ae08745Sheppo 	ldcp->status = LDC_INIT;
24081ae08745Sheppo 
24091ae08745Sheppo 	mutex_exit(&ldcp->lock);
24101ae08745Sheppo 
24111ae08745Sheppo 	/* Decrement number of open channels */
24121ae08745Sheppo 	mutex_enter(&ldcssp->lock);
24131ae08745Sheppo 	ldcssp->channels_open--;
24141ae08745Sheppo 	mutex_exit(&ldcssp->lock);
24151ae08745Sheppo 
24161ae08745Sheppo 	D1(ldcp->id, "ldc_close: (0x%llx) channel closed\n", ldcp->id);
24171ae08745Sheppo 
24181ae08745Sheppo 	return (0);
24191ae08745Sheppo }
24201ae08745Sheppo 
24211ae08745Sheppo /*
24221ae08745Sheppo  * Register channel callback
24231ae08745Sheppo  */
24241ae08745Sheppo int
24251ae08745Sheppo ldc_reg_callback(ldc_handle_t handle,
24261ae08745Sheppo     uint_t(*cb)(uint64_t event, caddr_t arg), caddr_t arg)
24271ae08745Sheppo {
24281ae08745Sheppo 	ldc_chan_t *ldcp;
24291ae08745Sheppo 
24301ae08745Sheppo 	if (handle == NULL) {
24311ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
24321ae08745Sheppo 		    "ldc_reg_callback: invalid channel handle\n");
24331ae08745Sheppo 		return (EINVAL);
24341ae08745Sheppo 	}
24351ae08745Sheppo 	if (((uint64_t)cb) < KERNELBASE) {
24361ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_reg_callback: invalid callback\n");
24371ae08745Sheppo 		return (EINVAL);
24381ae08745Sheppo 	}
24391ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
24401ae08745Sheppo 
24411ae08745Sheppo 	mutex_enter(&ldcp->lock);
24421ae08745Sheppo 
24431ae08745Sheppo 	if (ldcp->cb) {
24441ae08745Sheppo 		DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback exists\n",
24451ae08745Sheppo 		    ldcp->id);
24461ae08745Sheppo 		mutex_exit(&ldcp->lock);
24471ae08745Sheppo 		return (EIO);
24481ae08745Sheppo 	}
24491ae08745Sheppo 	if (ldcp->cb_inprogress) {
24501ae08745Sheppo 		DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback active\n",
24511ae08745Sheppo 		    ldcp->id);
24521ae08745Sheppo 		mutex_exit(&ldcp->lock);
24531ae08745Sheppo 		return (EWOULDBLOCK);
24541ae08745Sheppo 	}
24551ae08745Sheppo 
24561ae08745Sheppo 	ldcp->cb = cb;
24571ae08745Sheppo 	ldcp->cb_arg = arg;
24581ae08745Sheppo 	ldcp->cb_enabled = B_TRUE;
24591ae08745Sheppo 
24601ae08745Sheppo 	D1(ldcp->id,
24611ae08745Sheppo 	    "ldc_reg_callback: (0x%llx) registered callback for channel\n",
24621ae08745Sheppo 	    ldcp->id);
24631ae08745Sheppo 
24641ae08745Sheppo 	mutex_exit(&ldcp->lock);
24651ae08745Sheppo 
24661ae08745Sheppo 	return (0);
24671ae08745Sheppo }
24681ae08745Sheppo 
24691ae08745Sheppo /*
24701ae08745Sheppo  * Unregister channel callback
24711ae08745Sheppo  */
24721ae08745Sheppo int
24731ae08745Sheppo ldc_unreg_callback(ldc_handle_t handle)
24741ae08745Sheppo {
24751ae08745Sheppo 	ldc_chan_t *ldcp;
24761ae08745Sheppo 
24771ae08745Sheppo 	if (handle == NULL) {
24781ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
24791ae08745Sheppo 		    "ldc_unreg_callback: invalid channel handle\n");
24801ae08745Sheppo 		return (EINVAL);
24811ae08745Sheppo 	}
24821ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
24831ae08745Sheppo 
24841ae08745Sheppo 	mutex_enter(&ldcp->lock);
24851ae08745Sheppo 
24861ae08745Sheppo 	if (ldcp->cb == NULL) {
24871ae08745Sheppo 		DWARN(ldcp->id,
24881ae08745Sheppo 		    "ldc_unreg_callback: (0x%llx) no callback exists\n",
24891ae08745Sheppo 		    ldcp->id);
24901ae08745Sheppo 		mutex_exit(&ldcp->lock);
24911ae08745Sheppo 		return (EIO);
24921ae08745Sheppo 	}
24931ae08745Sheppo 	if (ldcp->cb_inprogress) {
24941ae08745Sheppo 		DWARN(ldcp->id,
24951ae08745Sheppo 		    "ldc_unreg_callback: (0x%llx) callback active\n",
24961ae08745Sheppo 		    ldcp->id);
24971ae08745Sheppo 		mutex_exit(&ldcp->lock);
24981ae08745Sheppo 		return (EWOULDBLOCK);
24991ae08745Sheppo 	}
25001ae08745Sheppo 
25011ae08745Sheppo 	ldcp->cb = NULL;
25021ae08745Sheppo 	ldcp->cb_arg = NULL;
25031ae08745Sheppo 	ldcp->cb_enabled = B_FALSE;
25041ae08745Sheppo 
25051ae08745Sheppo 	D1(ldcp->id,
25061ae08745Sheppo 	    "ldc_unreg_callback: (0x%llx) unregistered callback for channel\n",
25071ae08745Sheppo 	    ldcp->id);
25081ae08745Sheppo 
25091ae08745Sheppo 	mutex_exit(&ldcp->lock);
25101ae08745Sheppo 
25111ae08745Sheppo 	return (0);
25121ae08745Sheppo }
25131ae08745Sheppo 
25141ae08745Sheppo 
25151ae08745Sheppo /*
25161ae08745Sheppo  * Bring a channel up by initiating a handshake with the peer
25171ae08745Sheppo  * This call is asynchronous. It will complete at a later point
25181ae08745Sheppo  * in time when the peer responds back with an RTR.
25191ae08745Sheppo  */
25201ae08745Sheppo int
25211ae08745Sheppo ldc_up(ldc_handle_t handle)
25221ae08745Sheppo {
25231ae08745Sheppo 	int 		rv;
25241ae08745Sheppo 	ldc_chan_t 	*ldcp;
25251ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
25261ae08745Sheppo 	uint64_t 	tx_tail;
25271ae08745Sheppo 
25281ae08745Sheppo 	if (handle == NULL) {
25291ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_up: invalid channel handle\n");
25301ae08745Sheppo 		return (EINVAL);
25311ae08745Sheppo 	}
25321ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
25331ae08745Sheppo 
25341ae08745Sheppo 	mutex_enter(&ldcp->lock);
25351ae08745Sheppo 
25361ae08745Sheppo 	if (ldcp->tstate == TS_UP) {
25371ae08745Sheppo 		D2(ldcp->id,
25381ae08745Sheppo 		    "ldc_up: (0x%llx) channel is already in UP state\n",
25391ae08745Sheppo 		    ldcp->id);
25401ae08745Sheppo 		mutex_exit(&ldcp->lock);
25411ae08745Sheppo 		return (0);
25421ae08745Sheppo 	}
25431ae08745Sheppo 
25441ae08745Sheppo 	/* if the channel is in RAW mode - mark it as UP, if READY */
25451ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW && ldcp->tstate >= TS_READY) {
25461ae08745Sheppo 		ldcp->tstate = TS_UP;
25471ae08745Sheppo 		mutex_exit(&ldcp->lock);
25481ae08745Sheppo 		return (0);
25491ae08745Sheppo 	}
25501ae08745Sheppo 
25511ae08745Sheppo 	/* Don't start another handshake if there is one in progress */
25521ae08745Sheppo 	if (ldcp->hstate) {
25531ae08745Sheppo 		D2(ldcp->id,
25541ae08745Sheppo 		    "ldc_up: (0x%llx) channel handshake in progress\n",
25551ae08745Sheppo 		    ldcp->id);
25561ae08745Sheppo 		mutex_exit(&ldcp->lock);
25571ae08745Sheppo 		return (0);
25581ae08745Sheppo 	}
25591ae08745Sheppo 
25601ae08745Sheppo 	/* get the current tail for the LDC msg */
25611ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
25621ae08745Sheppo 	if (rv) {
25631ae08745Sheppo 		DWARN(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake\n",
25641ae08745Sheppo 		    ldcp->id);
25651ae08745Sheppo 		mutex_exit(&ldcp->lock);
25661ae08745Sheppo 		return (ECONNREFUSED);
25671ae08745Sheppo 	}
25681ae08745Sheppo 
25691ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
25701ae08745Sheppo 	ZERO_PKT(ldcmsg);
25711ae08745Sheppo 
25721ae08745Sheppo 	ldcmsg->type = LDC_CTRL;
25731ae08745Sheppo 	ldcmsg->stype = LDC_INFO;
25741ae08745Sheppo 	ldcmsg->ctrl = LDC_VER;
25751ae08745Sheppo 	ldcp->next_vidx = 0;
25761ae08745Sheppo 	bcopy(&ldc_versions[0], ldcmsg->udata, sizeof (ldc_versions[0]));
25771ae08745Sheppo 
25781ae08745Sheppo 	DUMP_LDC_PKT(ldcp, "ldc_up snd ver", (uint64_t)ldcmsg);
25791ae08745Sheppo 
25801ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
25811ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
25821ae08745Sheppo 		(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
25831ae08745Sheppo 
25841ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
25851ae08745Sheppo 	if (rv) {
25861ae08745Sheppo 		DWARN(ldcp->id,
25871ae08745Sheppo 		    "ldc_up: (0x%llx) cannot initiate handshake rv=%d\n",
25881ae08745Sheppo 		    ldcp->id, rv);
25891ae08745Sheppo 		mutex_exit(&ldcp->lock);
25901ae08745Sheppo 		return (rv);
25911ae08745Sheppo 	}
25921ae08745Sheppo 
2593*0a55fbb7Slm66018 	ldcp->hstate |= TS_SENT_VER;
25941ae08745Sheppo 	ldcp->tx_tail = tx_tail;
25951ae08745Sheppo 	D1(ldcp->id, "ldc_up: (0x%llx) channel up initiated\n", ldcp->id);
25961ae08745Sheppo 
25971ae08745Sheppo 	mutex_exit(&ldcp->lock);
25981ae08745Sheppo 
25991ae08745Sheppo 	return (rv);
26001ae08745Sheppo }
26011ae08745Sheppo 
26021ae08745Sheppo 
26031ae08745Sheppo /*
26041ae08745Sheppo  * Reset a channel by re-registering the Rx queues
26051ae08745Sheppo  */
26061ae08745Sheppo int
26071ae08745Sheppo ldc_reset(ldc_handle_t handle)
26081ae08745Sheppo {
26091ae08745Sheppo 	ldc_chan_t 	*ldcp;
26101ae08745Sheppo 
26111ae08745Sheppo 	if (handle == NULL) {
26121ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_reset: invalid channel handle\n");
26131ae08745Sheppo 		return (EINVAL);
26141ae08745Sheppo 	}
26151ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
26161ae08745Sheppo 
26171ae08745Sheppo 	mutex_enter(&ldcp->lock);
26181ae08745Sheppo 	i_ldc_reset(ldcp);
26191ae08745Sheppo 	mutex_exit(&ldcp->lock);
26201ae08745Sheppo 
26211ae08745Sheppo 	return (0);
26221ae08745Sheppo }
26231ae08745Sheppo 
26241ae08745Sheppo /*
26251ae08745Sheppo  * Get the current channel status
26261ae08745Sheppo  */
26271ae08745Sheppo int
26281ae08745Sheppo ldc_status(ldc_handle_t handle, ldc_status_t *status)
26291ae08745Sheppo {
26301ae08745Sheppo 	ldc_chan_t *ldcp;
26311ae08745Sheppo 
26321ae08745Sheppo 	if (handle == NULL || status == NULL) {
26331ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_status: invalid argument\n");
26341ae08745Sheppo 		return (EINVAL);
26351ae08745Sheppo 	}
26361ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
26371ae08745Sheppo 
26381ae08745Sheppo 	*status = ((ldc_chan_t *)handle)->status;
26391ae08745Sheppo 
26401ae08745Sheppo 	D1(ldcp->id,
26411ae08745Sheppo 	    "ldc_status: (0x%llx) returned status %d\n", ldcp->id, *status);
26421ae08745Sheppo 	return (0);
26431ae08745Sheppo }
26441ae08745Sheppo 
26451ae08745Sheppo 
26461ae08745Sheppo /*
26471ae08745Sheppo  * Set the channel's callback mode - enable/disable callbacks
26481ae08745Sheppo  */
26491ae08745Sheppo int
26501ae08745Sheppo ldc_set_cb_mode(ldc_handle_t handle, ldc_cb_mode_t cmode)
26511ae08745Sheppo {
26521ae08745Sheppo 	ldc_chan_t 	*ldcp;
26531ae08745Sheppo 
26541ae08745Sheppo 	if (handle == NULL) {
26551ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
26561ae08745Sheppo 		    "ldc_set_intr_mode: invalid channel handle\n");
26571ae08745Sheppo 		return (EINVAL);
26581ae08745Sheppo 	}
26591ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
26601ae08745Sheppo 
26611ae08745Sheppo 	/*
26621ae08745Sheppo 	 * Record no callbacks should be invoked
26631ae08745Sheppo 	 */
26641ae08745Sheppo 	mutex_enter(&ldcp->lock);
26651ae08745Sheppo 
26661ae08745Sheppo 	switch (cmode) {
26671ae08745Sheppo 	case LDC_CB_DISABLE:
26681ae08745Sheppo 		if (!ldcp->cb_enabled) {
26691ae08745Sheppo 			DWARN(ldcp->id,
26701ae08745Sheppo 			    "ldc_set_cb_mode: (0x%llx) callbacks disabled\n",
26711ae08745Sheppo 			    ldcp->id);
26721ae08745Sheppo 			break;
26731ae08745Sheppo 		}
26741ae08745Sheppo 		ldcp->cb_enabled = B_FALSE;
26751ae08745Sheppo 
26761ae08745Sheppo 		D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) disabled callbacks\n",
26771ae08745Sheppo 		    ldcp->id);
26781ae08745Sheppo 		break;
26791ae08745Sheppo 
26801ae08745Sheppo 	case LDC_CB_ENABLE:
26811ae08745Sheppo 		if (ldcp->cb_enabled) {
26821ae08745Sheppo 			DWARN(ldcp->id,
26831ae08745Sheppo 			    "ldc_set_cb_mode: (0x%llx) callbacks enabled\n",
26841ae08745Sheppo 			    ldcp->id);
26851ae08745Sheppo 			break;
26861ae08745Sheppo 		}
26871ae08745Sheppo 		ldcp->cb_enabled = B_TRUE;
26881ae08745Sheppo 
26891ae08745Sheppo 		D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) enabled callbacks\n",
26901ae08745Sheppo 		    ldcp->id);
26911ae08745Sheppo 		break;
26921ae08745Sheppo 	}
26931ae08745Sheppo 
26941ae08745Sheppo 	mutex_exit(&ldcp->lock);
26951ae08745Sheppo 
26961ae08745Sheppo 	return (0);
26971ae08745Sheppo }
26981ae08745Sheppo 
26991ae08745Sheppo /*
27001ae08745Sheppo  * Check to see if there are packets on the incoming queue
27011ae08745Sheppo  * Will return isempty = B_FALSE if there are  packets
27021ae08745Sheppo  */
27031ae08745Sheppo int
27041ae08745Sheppo ldc_chkq(ldc_handle_t handle, boolean_t *isempty)
27051ae08745Sheppo {
27061ae08745Sheppo 	int 		rv;
27071ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
27081ae08745Sheppo 	ldc_chan_t 	*ldcp;
27091ae08745Sheppo 
27101ae08745Sheppo 	if (handle == NULL) {
27111ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_chkq: invalid channel handle\n");
27121ae08745Sheppo 		return (EINVAL);
27131ae08745Sheppo 	}
27141ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
27151ae08745Sheppo 
27161ae08745Sheppo 	*isempty = B_TRUE;
27171ae08745Sheppo 
27181ae08745Sheppo 	mutex_enter(&ldcp->lock);
27191ae08745Sheppo 
27201ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
27211ae08745Sheppo 		D1(ldcp->id,
27221ae08745Sheppo 		    "ldc_chkq: (0x%llx) channel is not up\n", ldcp->id);
27231ae08745Sheppo 		mutex_exit(&ldcp->lock);
27241ae08745Sheppo 		return (ECONNRESET);
27251ae08745Sheppo 	}
27261ae08745Sheppo 
27271ae08745Sheppo 	/* Read packet(s) from the queue */
27281ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
27291ae08745Sheppo 	    &ldcp->link_state);
27301ae08745Sheppo 	if (rv != 0) {
27311ae08745Sheppo 		cmn_err(CE_WARN,
27321ae08745Sheppo 		    "ldc_chkq: (0x%lx) unable to read queue ptrs", ldcp->id);
27331ae08745Sheppo 		mutex_exit(&ldcp->lock);
27341ae08745Sheppo 		return (EIO);
27351ae08745Sheppo 	}
27361ae08745Sheppo 	/* reset the channel state if the channel went down */
27371ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
27381ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
27391ae08745Sheppo 		i_ldc_reset(ldcp);
27401ae08745Sheppo 		mutex_exit(&ldcp->lock);
27411ae08745Sheppo 		return (ECONNRESET);
27421ae08745Sheppo 	}
27431ae08745Sheppo 
27441ae08745Sheppo 	if (rx_head != rx_tail) {
27451ae08745Sheppo 		D1(ldcp->id, "ldc_chkq: (0x%llx) queue has pkt(s)\n", ldcp->id);
27461ae08745Sheppo 		*isempty = B_FALSE;
27471ae08745Sheppo 	}
27481ae08745Sheppo 
27491ae08745Sheppo 	mutex_exit(&ldcp->lock);
27501ae08745Sheppo 
27511ae08745Sheppo 	return (0);
27521ae08745Sheppo }
27531ae08745Sheppo 
27541ae08745Sheppo 
27551ae08745Sheppo /*
27561ae08745Sheppo  * Read 'size' amount of bytes or less. If incoming buffer
27571ae08745Sheppo  * is more than 'size', ENOBUFS is returned.
27581ae08745Sheppo  *
27591ae08745Sheppo  * On return, size contains the number of bytes read.
27601ae08745Sheppo  */
27611ae08745Sheppo int
27621ae08745Sheppo ldc_read(ldc_handle_t handle, caddr_t bufp, size_t *sizep)
27631ae08745Sheppo {
27641ae08745Sheppo 	ldc_chan_t 	*ldcp;
27651ae08745Sheppo 	uint64_t 	rx_head = 0, rx_tail = 0;
27661ae08745Sheppo 	int		rv = 0, exit_val;
27671ae08745Sheppo 
27681ae08745Sheppo 	if (handle == NULL) {
27691ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_read: invalid channel handle\n");
27701ae08745Sheppo 		return (EINVAL);
27711ae08745Sheppo 	}
27721ae08745Sheppo 
27731ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
27741ae08745Sheppo 
27751ae08745Sheppo 	/* channel lock */
27761ae08745Sheppo 	mutex_enter(&ldcp->lock);
27771ae08745Sheppo 
27781ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
27791ae08745Sheppo 		DWARN(ldcp->id,
27801ae08745Sheppo 		    "ldc_read: (0x%llx) channel is not in UP state\n",
27811ae08745Sheppo 		    ldcp->id);
27821ae08745Sheppo 		exit_val = ECONNRESET;
27831ae08745Sheppo 	} else {
27841ae08745Sheppo 		exit_val = ldcp->read_p(ldcp, bufp, sizep);
27851ae08745Sheppo 	}
27861ae08745Sheppo 
27871ae08745Sheppo 	/*
27881ae08745Sheppo 	 * if queue has been drained - clear interrupt
27891ae08745Sheppo 	 */
27901ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
27911ae08745Sheppo 	    &ldcp->link_state);
27921ae08745Sheppo 	if (exit_val == 0 && rv == 0 && rx_head == rx_tail) {
27931ae08745Sheppo 		i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
27941ae08745Sheppo 	}
27951ae08745Sheppo 
27961ae08745Sheppo 	mutex_exit(&ldcp->lock);
27971ae08745Sheppo 	return (exit_val);
27981ae08745Sheppo }
27991ae08745Sheppo 
28001ae08745Sheppo /*
28011ae08745Sheppo  * Basic raw mondo read -
28021ae08745Sheppo  * no interpretation of mondo contents at all.
28031ae08745Sheppo  *
28041ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
28051ae08745Sheppo  */
28061ae08745Sheppo static int
28071ae08745Sheppo i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
28081ae08745Sheppo {
28091ae08745Sheppo 	uint64_t 	q_size_mask;
28101ae08745Sheppo 	ldc_msg_t 	*msgp;
28111ae08745Sheppo 	uint8_t		*msgbufp;
28121ae08745Sheppo 	int		rv = 0, space;
28131ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
28141ae08745Sheppo 
28151ae08745Sheppo 	space = *sizep;
28161ae08745Sheppo 
28171ae08745Sheppo 	if (space < LDC_PAYLOAD_SIZE_RAW)
28181ae08745Sheppo 		return (ENOBUFS);
28191ae08745Sheppo 
28201ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
28211ae08745Sheppo 
28221ae08745Sheppo 	/* compute mask for increment */
28231ae08745Sheppo 	q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
28241ae08745Sheppo 
28251ae08745Sheppo 	/*
28261ae08745Sheppo 	 * Read packet(s) from the queue
28271ae08745Sheppo 	 */
28281ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
28291ae08745Sheppo 	    &ldcp->link_state);
28301ae08745Sheppo 	if (rv != 0) {
28311ae08745Sheppo 		cmn_err(CE_WARN,
28321ae08745Sheppo 		    "ldc_read_raw: (0x%lx) unable to read queue ptrs",
28331ae08745Sheppo 		    ldcp->id);
28341ae08745Sheppo 		return (EIO);
28351ae08745Sheppo 	}
28361ae08745Sheppo 	D1(ldcp->id, "ldc_read_raw: (0x%llx) rxh=0x%llx,"
28371ae08745Sheppo 		" rxt=0x%llx, st=0x%llx\n",
28381ae08745Sheppo 		ldcp->id, rx_head, rx_tail, ldcp->link_state);
28391ae08745Sheppo 
28401ae08745Sheppo 	/* reset the channel state if the channel went down */
28411ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN) {
28421ae08745Sheppo 		i_ldc_reset(ldcp);
28431ae08745Sheppo 		return (ECONNRESET);
28441ae08745Sheppo 	}
28451ae08745Sheppo 
28461ae08745Sheppo 	/*
28471ae08745Sheppo 	 * Check for empty queue
28481ae08745Sheppo 	 */
28491ae08745Sheppo 	if (rx_head == rx_tail) {
28501ae08745Sheppo 		*sizep = 0;
28511ae08745Sheppo 		return (0);
28521ae08745Sheppo 	}
28531ae08745Sheppo 
28541ae08745Sheppo 	/* get the message */
28551ae08745Sheppo 	msgp = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
28561ae08745Sheppo 
28571ae08745Sheppo 	/* if channel is in RAW mode, copy data and return */
28581ae08745Sheppo 	msgbufp = (uint8_t *)&(msgp->raw[0]);
28591ae08745Sheppo 
28601ae08745Sheppo 	bcopy(msgbufp, target_bufp, LDC_PAYLOAD_SIZE_RAW);
28611ae08745Sheppo 
28621ae08745Sheppo 	DUMP_PAYLOAD(ldcp->id, msgbufp);
28631ae08745Sheppo 
28641ae08745Sheppo 	*sizep = LDC_PAYLOAD_SIZE_RAW;
28651ae08745Sheppo 
28661ae08745Sheppo 	rx_head = (rx_head + LDC_PACKET_SIZE) & q_size_mask;
2867*0a55fbb7Slm66018 	rv = i_ldc_set_rx_head(ldcp, rx_head);
28681ae08745Sheppo 
28691ae08745Sheppo 	return (rv);
28701ae08745Sheppo }
28711ae08745Sheppo 
28721ae08745Sheppo /*
28731ae08745Sheppo  * Process LDC mondos to build larger packets
28741ae08745Sheppo  * with either un-reliable or reliable delivery.
28751ae08745Sheppo  *
28761ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
28771ae08745Sheppo  */
28781ae08745Sheppo static int
28791ae08745Sheppo i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
28801ae08745Sheppo {
28811ae08745Sheppo 	int		rv = 0;
28821ae08745Sheppo 	uint64_t 	rx_head = 0, rx_tail = 0;
28831ae08745Sheppo 	uint64_t 	curr_head = 0;
28841ae08745Sheppo 	ldc_msg_t 	*msg;
28851ae08745Sheppo 	caddr_t 	target;
28861ae08745Sheppo 	size_t 		len = 0, bytes_read = 0;
2887*0a55fbb7Slm66018 	int 		retries = 0;
28881ae08745Sheppo 	uint64_t 	q_size_mask;
28891ae08745Sheppo 
28901ae08745Sheppo 	target = target_bufp;
28911ae08745Sheppo 
28921ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
28931ae08745Sheppo 
28941ae08745Sheppo 	/* reset first frag to 0 */
28951ae08745Sheppo 	ldcp->first_fragment = 0;
28961ae08745Sheppo 
28971ae08745Sheppo 	/* compute mask for increment */
28981ae08745Sheppo 	q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
28991ae08745Sheppo 
29001ae08745Sheppo 	/*
29011ae08745Sheppo 	 * Read packet(s) from the queue
29021ae08745Sheppo 	 */
29031ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &curr_head, &rx_tail,
29041ae08745Sheppo 	    &ldcp->link_state);
29051ae08745Sheppo 	if (rv != 0) {
29061ae08745Sheppo 		cmn_err(CE_WARN,
29071ae08745Sheppo 		    "ldc_read: (0x%lx) unable to read queue ptrs",
29081ae08745Sheppo 		    ldcp->id);
29091ae08745Sheppo 		return (EIO);
29101ae08745Sheppo 	}
29111ae08745Sheppo 	D1(ldcp->id, "ldc_read: (0x%llx) chd=0x%llx, tl=0x%llx, st=0x%llx\n",
29121ae08745Sheppo 	    ldcp->id, curr_head, rx_tail, ldcp->link_state);
29131ae08745Sheppo 
29141ae08745Sheppo 	/* reset the channel state if the channel went down */
29151ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN) {
29161ae08745Sheppo 		i_ldc_reset(ldcp);
29171ae08745Sheppo 		return (ECONNRESET);
29181ae08745Sheppo 	}
29191ae08745Sheppo 
29201ae08745Sheppo 	for (;;) {
29211ae08745Sheppo 
29221ae08745Sheppo 		if (curr_head == rx_tail) {
29231ae08745Sheppo 			rv = hv_ldc_rx_get_state(ldcp->id,
29241ae08745Sheppo 			    &rx_head, &rx_tail, &ldcp->link_state);
29251ae08745Sheppo 			if (rv != 0) {
29261ae08745Sheppo 				cmn_err(CE_WARN,
29271ae08745Sheppo 				    "ldc_read: (0x%lx) cannot read queue ptrs",
29281ae08745Sheppo 				    ldcp->id);
29291ae08745Sheppo 				return (EIO);
29301ae08745Sheppo 			}
29311ae08745Sheppo 			/* reset the channel state if the channel went down */
29321ae08745Sheppo 			if (ldcp->link_state == LDC_CHANNEL_DOWN) {
29331ae08745Sheppo 				i_ldc_reset(ldcp);
29341ae08745Sheppo 				return (ECONNRESET);
29351ae08745Sheppo 			}
29361ae08745Sheppo 		}
29371ae08745Sheppo 
29381ae08745Sheppo 		if (curr_head == rx_tail) {
29391ae08745Sheppo 
29401ae08745Sheppo 			/* If in the middle of a fragmented xfer */
29411ae08745Sheppo 			if (ldcp->first_fragment != 0) {
2942*0a55fbb7Slm66018 
2943*0a55fbb7Slm66018 				/* wait for ldc_delay usecs */
2944*0a55fbb7Slm66018 				drv_usecwait(ldc_delay);
2945*0a55fbb7Slm66018 
2946*0a55fbb7Slm66018 				if (++retries < ldc_max_retries)
29471ae08745Sheppo 					continue;
2948*0a55fbb7Slm66018 
29491ae08745Sheppo 				*sizep = 0;
2950*0a55fbb7Slm66018 				ldcp->last_msg_rcd = ldcp->first_fragment - 1;
29511ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
29521ae08745Sheppo 					"ldc_read: (0x%llx) read timeout",
29531ae08745Sheppo 					ldcp->id);
29541ae08745Sheppo 				return (ETIMEDOUT);
29551ae08745Sheppo 			}
29561ae08745Sheppo 			*sizep = 0;
29571ae08745Sheppo 			break;
29581ae08745Sheppo 		}
2959*0a55fbb7Slm66018 		retries = 0;
29601ae08745Sheppo 
29611ae08745Sheppo 		D2(ldcp->id,
29621ae08745Sheppo 		    "ldc_read: (0x%llx) chd=0x%llx, rxhd=0x%llx, rxtl=0x%llx\n",
29631ae08745Sheppo 		    ldcp->id, curr_head, rx_head, rx_tail);
29641ae08745Sheppo 
29651ae08745Sheppo 		/* get the message */
29661ae08745Sheppo 		msg = (ldc_msg_t *)(ldcp->rx_q_va + curr_head);
29671ae08745Sheppo 
29681ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "ldc_read received pkt",
29691ae08745Sheppo 		    ldcp->rx_q_va + curr_head);
29701ae08745Sheppo 
29711ae08745Sheppo 		/* Check the message ID for the message received */
29721ae08745Sheppo 		if ((rv = i_ldc_check_seqid(ldcp, msg)) != 0) {
29731ae08745Sheppo 
29741ae08745Sheppo 			DWARN(ldcp->id, "ldc_read: (0x%llx) seqid error, "
29751ae08745Sheppo 			    "q_ptrs=0x%lx,0x%lx", ldcp->id, rx_head, rx_tail);
29761ae08745Sheppo 
2977*0a55fbb7Slm66018 			/* throw away data */
2978*0a55fbb7Slm66018 			bytes_read = 0;
2979*0a55fbb7Slm66018 
29801ae08745Sheppo 			/* Reset last_msg_rcd to start of message */
29811ae08745Sheppo 			if (ldcp->first_fragment != 0) {
29821ae08745Sheppo 				ldcp->last_msg_rcd =
29831ae08745Sheppo 					ldcp->first_fragment - 1;
29841ae08745Sheppo 				ldcp->first_fragment = 0;
29851ae08745Sheppo 			}
29861ae08745Sheppo 			/*
29871ae08745Sheppo 			 * Send a NACK -- invalid seqid
29881ae08745Sheppo 			 * get the current tail for the response
29891ae08745Sheppo 			 */
29901ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK,
29911ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK));
29921ae08745Sheppo 			if (rv) {
29931ae08745Sheppo 				cmn_err(CE_NOTE,
29941ae08745Sheppo 				    "ldc_read: (0x%lx) err sending "
29951ae08745Sheppo 				    "NACK msg\n", ldcp->id);
29961ae08745Sheppo 			}
29971ae08745Sheppo 
29981ae08745Sheppo 			/* purge receive queue */
2999*0a55fbb7Slm66018 			rv = i_ldc_set_rx_head(ldcp, rx_tail);
30001ae08745Sheppo 
30011ae08745Sheppo 			break;
30021ae08745Sheppo 		}
30031ae08745Sheppo 
30041ae08745Sheppo 		/*
30051ae08745Sheppo 		 * Process any messages of type CTRL messages
30061ae08745Sheppo 		 * Future implementations should try to pass these to
30071ae08745Sheppo 		 * LDC transport by resetting the intr state.
30081ae08745Sheppo 		 *
30091ae08745Sheppo 		 * NOTE: not done as a switch() as type can be both ctrl+data
30101ae08745Sheppo 		 */
30111ae08745Sheppo 		if (msg->type & LDC_CTRL) {
30121ae08745Sheppo 			if (rv = i_ldc_ctrlmsg(ldcp, msg)) {
30131ae08745Sheppo 				if (rv == EAGAIN)
30141ae08745Sheppo 					continue;
3015*0a55fbb7Slm66018 				rv = i_ldc_set_rx_head(ldcp, rx_tail);
30161ae08745Sheppo 				*sizep = 0;
30171ae08745Sheppo 				bytes_read = 0;
30181ae08745Sheppo 				break;
30191ae08745Sheppo 			}
30201ae08745Sheppo 		}
30211ae08745Sheppo 
30221ae08745Sheppo 		/* process data ACKs */
30231ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
30241ae08745Sheppo 			(void) i_ldc_process_data_ACK(ldcp, msg);
30251ae08745Sheppo 		}
30261ae08745Sheppo 
30271ae08745Sheppo 		/* process data messages */
30281ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
30291ae08745Sheppo 
30301ae08745Sheppo 			uint8_t *msgbuf = (uint8_t *)(
30311ae08745Sheppo 				(ldcp->mode == LDC_MODE_RELIABLE ||
30321ae08745Sheppo 				ldcp->mode == LDC_MODE_STREAM)
30331ae08745Sheppo 				? msg->rdata : msg->udata);
30341ae08745Sheppo 
30351ae08745Sheppo 			D2(ldcp->id,
30361ae08745Sheppo 			    "ldc_read: (0x%llx) received data msg\n", ldcp->id);
30371ae08745Sheppo 
30381ae08745Sheppo 			/* get the packet length */
30391ae08745Sheppo 			len = (msg->env & LDC_LEN_MASK);
30401ae08745Sheppo 
30411ae08745Sheppo 				/*
30421ae08745Sheppo 				 * FUTURE OPTIMIZATION:
30431ae08745Sheppo 				 * dont need to set q head for every
30441ae08745Sheppo 				 * packet we read just need to do this when
30451ae08745Sheppo 				 * we are done or need to wait for more
30461ae08745Sheppo 				 * mondos to make a full packet - this is
30471ae08745Sheppo 				 * currently expensive.
30481ae08745Sheppo 				 */
30491ae08745Sheppo 
30501ae08745Sheppo 			if (ldcp->first_fragment == 0) {
30511ae08745Sheppo 
30521ae08745Sheppo 				/*
30531ae08745Sheppo 				 * first packets should always have the start
30541ae08745Sheppo 				 * bit set (even for a single packet). If not
30551ae08745Sheppo 				 * throw away the packet
30561ae08745Sheppo 				 */
30571ae08745Sheppo 				if (!(msg->env & LDC_FRAG_START)) {
30581ae08745Sheppo 
30591ae08745Sheppo 					DWARN(DBG_ALL_LDCS,
30601ae08745Sheppo 					    "ldc_read: (0x%llx) not start - "
30611ae08745Sheppo 					    "frag=%x\n", ldcp->id,
30621ae08745Sheppo 					    (msg->env) & LDC_FRAG_MASK);
30631ae08745Sheppo 
30641ae08745Sheppo 					/* toss pkt, inc head, cont reading */
30651ae08745Sheppo 					bytes_read = 0;
30661ae08745Sheppo 					target = target_bufp;
30671ae08745Sheppo 					curr_head =
30681ae08745Sheppo 						(curr_head + LDC_PACKET_SIZE)
30691ae08745Sheppo 						& q_size_mask;
30701ae08745Sheppo 					if (rv = i_ldc_set_rx_head(ldcp,
30711ae08745Sheppo 						curr_head))
30721ae08745Sheppo 						break;
30731ae08745Sheppo 
30741ae08745Sheppo 					continue;
30751ae08745Sheppo 				}
30761ae08745Sheppo 
30771ae08745Sheppo 				ldcp->first_fragment = msg->seqid;
30781ae08745Sheppo 			} else {
30791ae08745Sheppo 				/* check to see if this is a pkt w/ START bit */
30801ae08745Sheppo 				if (msg->env & LDC_FRAG_START) {
30811ae08745Sheppo 					DWARN(DBG_ALL_LDCS,
30821ae08745Sheppo 					    "ldc_read:(0x%llx) unexpected pkt"
30831ae08745Sheppo 					    " env=0x%x discarding %d bytes,"
30841ae08745Sheppo 					    " lastmsg=%d, currentmsg=%d\n",
30851ae08745Sheppo 					    ldcp->id, msg->env&LDC_FRAG_MASK,
30861ae08745Sheppo 					    bytes_read, ldcp->last_msg_rcd,
30871ae08745Sheppo 					    msg->seqid);
30881ae08745Sheppo 
30891ae08745Sheppo 					/* throw data we have read so far */
30901ae08745Sheppo 					bytes_read = 0;
30911ae08745Sheppo 					target = target_bufp;
30921ae08745Sheppo 					ldcp->first_fragment = msg->seqid;
30931ae08745Sheppo 
30941ae08745Sheppo 					if (rv = i_ldc_set_rx_head(ldcp,
30951ae08745Sheppo 						curr_head))
30961ae08745Sheppo 						break;
30971ae08745Sheppo 				}
30981ae08745Sheppo 			}
30991ae08745Sheppo 
31001ae08745Sheppo 			/* copy (next) pkt into buffer */
31011ae08745Sheppo 			if (len <= (*sizep - bytes_read)) {
31021ae08745Sheppo 				bcopy(msgbuf, target, len);
31031ae08745Sheppo 				target += len;
31041ae08745Sheppo 				bytes_read += len;
31051ae08745Sheppo 			} else {
31061ae08745Sheppo 				/*
31071ae08745Sheppo 				 * there is not enough space in the buffer to
31081ae08745Sheppo 				 * read this pkt. throw message away & continue
31091ae08745Sheppo 				 * reading data from queue
31101ae08745Sheppo 				 */
31111ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
31121ae08745Sheppo 				    "ldc_read: (0x%llx) buffer too small, "
31131ae08745Sheppo 				    "head=0x%lx, expect=%d, got=%d\n", ldcp->id,
31141ae08745Sheppo 				    curr_head, *sizep, bytes_read+len);
31151ae08745Sheppo 
31161ae08745Sheppo 				ldcp->first_fragment = 0;
31171ae08745Sheppo 				target = target_bufp;
31181ae08745Sheppo 				bytes_read = 0;
31191ae08745Sheppo 
31201ae08745Sheppo 				/* throw away everything received so far */
31211ae08745Sheppo 				if (rv = i_ldc_set_rx_head(ldcp, curr_head))
31221ae08745Sheppo 					break;
31231ae08745Sheppo 
31241ae08745Sheppo 				/* continue reading remaining pkts */
31251ae08745Sheppo 				continue;
31261ae08745Sheppo 			}
31271ae08745Sheppo 		}
31281ae08745Sheppo 
31291ae08745Sheppo 		/* set the message id */
31301ae08745Sheppo 		ldcp->last_msg_rcd = msg->seqid;
31311ae08745Sheppo 
31321ae08745Sheppo 		/* move the head one position */
31331ae08745Sheppo 		curr_head = (curr_head + LDC_PACKET_SIZE) & q_size_mask;
31341ae08745Sheppo 
31351ae08745Sheppo 		if (msg->env & LDC_FRAG_STOP) {
31361ae08745Sheppo 
31371ae08745Sheppo 			/*
31381ae08745Sheppo 			 * All pkts that are part of this fragmented transfer
31391ae08745Sheppo 			 * have been read or this was a single pkt read
31401ae08745Sheppo 			 * or there was an error
31411ae08745Sheppo 			 */
31421ae08745Sheppo 
31431ae08745Sheppo 			/* set the queue head */
31441ae08745Sheppo 			if (rv = i_ldc_set_rx_head(ldcp, curr_head))
31451ae08745Sheppo 				bytes_read = 0;
31461ae08745Sheppo 
31471ae08745Sheppo 			*sizep = bytes_read;
31481ae08745Sheppo 
31491ae08745Sheppo 			break;
31501ae08745Sheppo 		}
31511ae08745Sheppo 
31521ae08745Sheppo 		/* advance head if it is a DATA ACK */
31531ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
31541ae08745Sheppo 
31551ae08745Sheppo 			/* set the queue head */
31561ae08745Sheppo 			if (rv = i_ldc_set_rx_head(ldcp, curr_head)) {
31571ae08745Sheppo 				bytes_read = 0;
31581ae08745Sheppo 				break;
31591ae08745Sheppo 			}
31601ae08745Sheppo 
31611ae08745Sheppo 			D2(ldcp->id, "ldc_read: (0x%llx) set ACK qhead 0x%llx",
31621ae08745Sheppo 			    ldcp->id, curr_head);
31631ae08745Sheppo 		}
31641ae08745Sheppo 
31651ae08745Sheppo 	} /* for (;;) */
31661ae08745Sheppo 
31671ae08745Sheppo 
31681ae08745Sheppo 	/*
31691ae08745Sheppo 	 * If useful data was read - Send msg ACK
31701ae08745Sheppo 	 * OPTIMIZE: do not send ACK for all msgs - use some frequency
31711ae08745Sheppo 	 */
31721ae08745Sheppo 	if ((bytes_read > 0) && (ldcp->mode == LDC_MODE_RELIABLE ||
31731ae08745Sheppo 		ldcp->mode == LDC_MODE_STREAM)) {
31741ae08745Sheppo 
31751ae08745Sheppo 		rv = i_ldc_send_pkt(ldcp, LDC_DATA, LDC_ACK, 0);
31761ae08745Sheppo 		if (rv != 0) {
31771ae08745Sheppo 			cmn_err(CE_NOTE,
31781ae08745Sheppo 			    "ldc_read: (0x%lx) cannot send ACK\n", ldcp->id);
31791ae08745Sheppo 			return (0);
31801ae08745Sheppo 		}
31811ae08745Sheppo 	}
31821ae08745Sheppo 
31831ae08745Sheppo 	D2(ldcp->id, "ldc_read: (0x%llx) end size=%d", ldcp->id, *sizep);
31841ae08745Sheppo 
31851ae08745Sheppo 	return (rv);
31861ae08745Sheppo }
31871ae08745Sheppo 
31881ae08745Sheppo /*
31891ae08745Sheppo  * Use underlying reliable packet mechanism to fetch
31901ae08745Sheppo  * and buffer incoming packets so we can hand them back as
31911ae08745Sheppo  * a basic byte stream.
31921ae08745Sheppo  *
31931ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
31941ae08745Sheppo  */
31951ae08745Sheppo static int
31961ae08745Sheppo i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
31971ae08745Sheppo {
31981ae08745Sheppo 	int	rv;
31991ae08745Sheppo 	size_t	size;
32001ae08745Sheppo 
32011ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
32021ae08745Sheppo 
32031ae08745Sheppo 	D2(ldcp->id, "i_ldc_read_stream: (0x%llx) buffer size=%d",
32041ae08745Sheppo 		ldcp->id, *sizep);
32051ae08745Sheppo 
32061ae08745Sheppo 	if (ldcp->stream_remains == 0) {
32071ae08745Sheppo 		size = ldcp->mtu;
32081ae08745Sheppo 		rv = i_ldc_read_packet(ldcp,
32091ae08745Sheppo 			(caddr_t)ldcp->stream_bufferp, &size);
32101ae08745Sheppo 		D2(ldcp->id, "i_ldc_read_stream: read packet (0x%llx) size=%d",
32111ae08745Sheppo 			ldcp->id, size);
32121ae08745Sheppo 
32131ae08745Sheppo 		if (rv != 0)
32141ae08745Sheppo 			return (rv);
32151ae08745Sheppo 
32161ae08745Sheppo 		ldcp->stream_remains = size;
32171ae08745Sheppo 		ldcp->stream_offset = 0;
32181ae08745Sheppo 	}
32191ae08745Sheppo 
32201ae08745Sheppo 	size = MIN(ldcp->stream_remains, *sizep);
32211ae08745Sheppo 
32221ae08745Sheppo 	bcopy(ldcp->stream_bufferp + ldcp->stream_offset, target_bufp, size);
32231ae08745Sheppo 	ldcp->stream_offset += size;
32241ae08745Sheppo 	ldcp->stream_remains -= size;
32251ae08745Sheppo 
32261ae08745Sheppo 	D2(ldcp->id, "i_ldc_read_stream: (0x%llx) fill from buffer size=%d",
32271ae08745Sheppo 		ldcp->id, size);
32281ae08745Sheppo 
32291ae08745Sheppo 	*sizep = size;
32301ae08745Sheppo 	return (0);
32311ae08745Sheppo }
32321ae08745Sheppo 
32331ae08745Sheppo /*
32341ae08745Sheppo  * Write specified amount of bytes to the channel
32351ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
32361ae08745Sheppo  * packet is tagged with an unique packet ID in
32371ae08745Sheppo  * the case of a reliable transport.
32381ae08745Sheppo  *
32391ae08745Sheppo  * On return, size contains the number of bytes written.
32401ae08745Sheppo  */
32411ae08745Sheppo int
32421ae08745Sheppo ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep)
32431ae08745Sheppo {
32441ae08745Sheppo 	ldc_chan_t	*ldcp;
32451ae08745Sheppo 	int		rv = 0;
32461ae08745Sheppo 
32471ae08745Sheppo 	if (handle == NULL) {
32481ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_write: invalid channel handle\n");
32491ae08745Sheppo 		return (EINVAL);
32501ae08745Sheppo 	}
32511ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
32521ae08745Sheppo 
32531ae08745Sheppo 	mutex_enter(&ldcp->lock);
32541ae08745Sheppo 
32551ae08745Sheppo 	/* check if non-zero data to write */
32561ae08745Sheppo 	if (buf == NULL || sizep == NULL) {
32571ae08745Sheppo 		DWARN(ldcp->id, "ldc_write: (0x%llx) invalid data write\n",
32581ae08745Sheppo 		    ldcp->id);
32591ae08745Sheppo 		mutex_exit(&ldcp->lock);
32601ae08745Sheppo 		return (EINVAL);
32611ae08745Sheppo 	}
32621ae08745Sheppo 
32631ae08745Sheppo 	if (*sizep == 0) {
32641ae08745Sheppo 		DWARN(ldcp->id, "ldc_write: (0x%llx) write size of zero\n",
32651ae08745Sheppo 		    ldcp->id);
32661ae08745Sheppo 		mutex_exit(&ldcp->lock);
32671ae08745Sheppo 		return (0);
32681ae08745Sheppo 	}
32691ae08745Sheppo 
32701ae08745Sheppo 	/* Check if channel is UP for data exchange */
32711ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
32721ae08745Sheppo 		DWARN(ldcp->id,
32731ae08745Sheppo 		    "ldc_write: (0x%llx) channel is not in UP state\n",
32741ae08745Sheppo 		    ldcp->id);
32751ae08745Sheppo 		*sizep = 0;
32761ae08745Sheppo 		rv = ECONNRESET;
32771ae08745Sheppo 	} else {
32781ae08745Sheppo 		rv = ldcp->write_p(ldcp, buf, sizep);
32791ae08745Sheppo 	}
32801ae08745Sheppo 
32811ae08745Sheppo 	mutex_exit(&ldcp->lock);
32821ae08745Sheppo 
32831ae08745Sheppo 	return (rv);
32841ae08745Sheppo }
32851ae08745Sheppo 
32861ae08745Sheppo /*
32871ae08745Sheppo  * Write a raw packet to the channel
32881ae08745Sheppo  * On return, size contains the number of bytes written.
32891ae08745Sheppo  */
32901ae08745Sheppo static int
32911ae08745Sheppo i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
32921ae08745Sheppo {
32931ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
32941ae08745Sheppo 	uint64_t 	tx_head, tx_tail, new_tail;
32951ae08745Sheppo 	int		rv = 0;
32961ae08745Sheppo 	size_t		size;
32971ae08745Sheppo 
32981ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
32991ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_RAW);
33001ae08745Sheppo 
33011ae08745Sheppo 	size = *sizep;
33021ae08745Sheppo 
33031ae08745Sheppo 	/*
33041ae08745Sheppo 	 * Check to see if the packet size is less than or
33051ae08745Sheppo 	 * equal to packet size support in raw mode
33061ae08745Sheppo 	 */
33071ae08745Sheppo 	if (size > ldcp->pkt_payload) {
33081ae08745Sheppo 		DWARN(ldcp->id,
33091ae08745Sheppo 		    "ldc_write: (0x%llx) invalid size (0x%llx) for RAW mode\n",
33101ae08745Sheppo 		    ldcp->id, *sizep);
33111ae08745Sheppo 		*sizep = 0;
33121ae08745Sheppo 		return (EMSGSIZE);
33131ae08745Sheppo 	}
33141ae08745Sheppo 
33151ae08745Sheppo 	/* get the qptrs for the tx queue */
33161ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
33171ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
33181ae08745Sheppo 	if (rv != 0) {
33191ae08745Sheppo 		cmn_err(CE_WARN,
33201ae08745Sheppo 		    "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
33211ae08745Sheppo 		*sizep = 0;
33221ae08745Sheppo 		return (EIO);
33231ae08745Sheppo 	}
33241ae08745Sheppo 
33251ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
33261ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
33271ae08745Sheppo 		DWARN(ldcp->id,
33281ae08745Sheppo 		    "ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
33291ae08745Sheppo 		i_ldc_reset(ldcp);
33301ae08745Sheppo 		*sizep = 0;
33311ae08745Sheppo 		return (ECONNRESET);
33321ae08745Sheppo 	}
33331ae08745Sheppo 
33341ae08745Sheppo 	tx_tail = ldcp->tx_tail;
33351ae08745Sheppo 	tx_head = ldcp->tx_head;
33361ae08745Sheppo 	new_tail = (tx_tail + LDC_PACKET_SIZE) &
33371ae08745Sheppo 		((ldcp->tx_q_entries-1) << LDC_PACKET_SHIFT);
33381ae08745Sheppo 
33391ae08745Sheppo 	if (new_tail == tx_head) {
33401ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
33411ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
33421ae08745Sheppo 		*sizep = 0;
33431ae08745Sheppo 		return (EWOULDBLOCK);
33441ae08745Sheppo 	}
33451ae08745Sheppo 
33461ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
33471ae08745Sheppo 	    ldcp->id, size);
33481ae08745Sheppo 
33491ae08745Sheppo 	/* Send the data now */
33501ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
33511ae08745Sheppo 
33521ae08745Sheppo 		/* copy the data into pkt */
33531ae08745Sheppo 	bcopy((uint8_t *)buf, ldcmsg, size);
33541ae08745Sheppo 
33551ae08745Sheppo 		/* increment tail */
33561ae08745Sheppo 	tx_tail = new_tail;
33571ae08745Sheppo 
33581ae08745Sheppo 	/*
33591ae08745Sheppo 	 * All packets have been copied into the TX queue
33601ae08745Sheppo 	 * update the tail ptr in the HV
33611ae08745Sheppo 	 */
33621ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
33631ae08745Sheppo 	if (rv) {
33641ae08745Sheppo 		if (rv == EWOULDBLOCK) {
33651ae08745Sheppo 			DWARN(ldcp->id, "ldc_write: (0x%llx) write timed out\n",
33661ae08745Sheppo 			    ldcp->id);
33671ae08745Sheppo 			*sizep = 0;
33681ae08745Sheppo 			return (EWOULDBLOCK);
33691ae08745Sheppo 		}
33701ae08745Sheppo 
33711ae08745Sheppo 		/* cannot write data - reset channel */
33721ae08745Sheppo 		i_ldc_reset(ldcp);
33731ae08745Sheppo 		*sizep = 0;
33741ae08745Sheppo 		return (ECONNRESET);
33751ae08745Sheppo 	}
33761ae08745Sheppo 
33771ae08745Sheppo 	ldcp->tx_tail = tx_tail;
33781ae08745Sheppo 	*sizep = size;
33791ae08745Sheppo 
33801ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, size);
33811ae08745Sheppo 
33821ae08745Sheppo 	return (rv);
33831ae08745Sheppo }
33841ae08745Sheppo 
33851ae08745Sheppo 
33861ae08745Sheppo /*
33871ae08745Sheppo  * Write specified amount of bytes to the channel
33881ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
33891ae08745Sheppo  * packet is tagged with an unique packet ID in
33901ae08745Sheppo  * the case of a reliable transport.
33911ae08745Sheppo  *
33921ae08745Sheppo  * On return, size contains the number of bytes written.
33931ae08745Sheppo  * This function needs to ensure that the write size is < MTU size
33941ae08745Sheppo  */
33951ae08745Sheppo static int
33961ae08745Sheppo i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size)
33971ae08745Sheppo {
33981ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
33991ae08745Sheppo 	uint64_t 	tx_head, tx_tail, new_tail, start;
34001ae08745Sheppo 	uint64_t	txq_size_mask, numavail;
34011ae08745Sheppo 	uint8_t 	*msgbuf, *source = (uint8_t *)buf;
34021ae08745Sheppo 	size_t 		len, bytes_written = 0, remaining;
34031ae08745Sheppo 	int		rv;
34041ae08745Sheppo 	uint32_t	curr_seqid;
34051ae08745Sheppo 
34061ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
34071ae08745Sheppo 
34081ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_RELIABLE ||
34091ae08745Sheppo 		ldcp->mode == LDC_MODE_UNRELIABLE ||
34101ae08745Sheppo 		ldcp->mode == LDC_MODE_STREAM);
34111ae08745Sheppo 
34121ae08745Sheppo 	/* compute mask for increment */
34131ae08745Sheppo 	txq_size_mask = (ldcp->tx_q_entries - 1) << LDC_PACKET_SHIFT;
34141ae08745Sheppo 
34151ae08745Sheppo 	/* get the qptrs for the tx queue */
34161ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
34171ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
34181ae08745Sheppo 	if (rv != 0) {
34191ae08745Sheppo 		cmn_err(CE_WARN,
34201ae08745Sheppo 		    "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
34211ae08745Sheppo 		*size = 0;
34221ae08745Sheppo 		return (EIO);
34231ae08745Sheppo 	}
34241ae08745Sheppo 
34251ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
34261ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
34271ae08745Sheppo 		DWARN(ldcp->id,
34281ae08745Sheppo 		    "ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
34291ae08745Sheppo 		*size = 0;
34301ae08745Sheppo 		i_ldc_reset(ldcp);
34311ae08745Sheppo 		return (ECONNRESET);
34321ae08745Sheppo 	}
34331ae08745Sheppo 
34341ae08745Sheppo 	tx_tail = ldcp->tx_tail;
34351ae08745Sheppo 	new_tail = (tx_tail + LDC_PACKET_SIZE) %
34361ae08745Sheppo 		(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
34371ae08745Sheppo 
34381ae08745Sheppo 	/*
34391ae08745Sheppo 	 * Transport mode determines whether we use HV Tx head or the
34401ae08745Sheppo 	 * private protocol head (corresponding to last ACKd pkt) for
34411ae08745Sheppo 	 * determining how much we can write
34421ae08745Sheppo 	 */
34431ae08745Sheppo 	tx_head = (ldcp->mode == LDC_MODE_RELIABLE ||
34441ae08745Sheppo 		ldcp->mode == LDC_MODE_STREAM)
34451ae08745Sheppo 		? ldcp->tx_ackd_head : ldcp->tx_head;
34461ae08745Sheppo 	if (new_tail == tx_head) {
34471ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
34481ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
34491ae08745Sheppo 		*size = 0;
34501ae08745Sheppo 		return (EWOULDBLOCK);
34511ae08745Sheppo 	}
34521ae08745Sheppo 
34531ae08745Sheppo 	/*
34541ae08745Sheppo 	 * Make sure that the LDC Tx queue has enough space
34551ae08745Sheppo 	 */
34561ae08745Sheppo 	numavail = (tx_head >> LDC_PACKET_SHIFT) - (tx_tail >> LDC_PACKET_SHIFT)
34571ae08745Sheppo 		+ ldcp->tx_q_entries - 1;
34581ae08745Sheppo 	numavail %= ldcp->tx_q_entries;
34591ae08745Sheppo 
34601ae08745Sheppo 	if (*size > (numavail * ldcp->pkt_payload)) {
34611ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
34621ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue has no space\n", ldcp->id);
34631ae08745Sheppo 		return (EWOULDBLOCK);
34641ae08745Sheppo 	}
34651ae08745Sheppo 
34661ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
34671ae08745Sheppo 	    ldcp->id, *size);
34681ae08745Sheppo 
34691ae08745Sheppo 	/* Send the data now */
34701ae08745Sheppo 	bytes_written = 0;
34711ae08745Sheppo 	curr_seqid = ldcp->last_msg_snt;
34721ae08745Sheppo 	start = tx_tail;
34731ae08745Sheppo 
34741ae08745Sheppo 	while (*size > bytes_written) {
34751ae08745Sheppo 
34761ae08745Sheppo 		ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
34771ae08745Sheppo 
34781ae08745Sheppo 		msgbuf = (uint8_t *)((ldcp->mode == LDC_MODE_RELIABLE ||
34791ae08745Sheppo 			ldcp->mode == LDC_MODE_STREAM)
34801ae08745Sheppo 			? ldcmsg->rdata : ldcmsg->udata);
34811ae08745Sheppo 
34821ae08745Sheppo 		ldcmsg->type = LDC_DATA;
34831ae08745Sheppo 		ldcmsg->stype = LDC_INFO;
34841ae08745Sheppo 		ldcmsg->ctrl = 0;
34851ae08745Sheppo 
34861ae08745Sheppo 		remaining = *size - bytes_written;
34871ae08745Sheppo 		len = min(ldcp->pkt_payload, remaining);
34881ae08745Sheppo 		ldcmsg->env = (uint8_t)len;
34891ae08745Sheppo 
34901ae08745Sheppo 		curr_seqid++;
34911ae08745Sheppo 		ldcmsg->seqid = curr_seqid;
34921ae08745Sheppo 
34931ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "ldc_write snd data", (uint64_t)ldcmsg);
34941ae08745Sheppo 
34951ae08745Sheppo 		/* copy the data into pkt */
34961ae08745Sheppo 		bcopy(source, msgbuf, len);
34971ae08745Sheppo 
34981ae08745Sheppo 		source += len;
34991ae08745Sheppo 		bytes_written += len;
35001ae08745Sheppo 
35011ae08745Sheppo 		/* increment tail */
35021ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) & txq_size_mask;
35031ae08745Sheppo 
35041ae08745Sheppo 		ASSERT(tx_tail != tx_head);
35051ae08745Sheppo 	}
35061ae08745Sheppo 
35071ae08745Sheppo 	/* Set the start and stop bits */
35081ae08745Sheppo 	ldcmsg->env |= LDC_FRAG_STOP;
35091ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + start);
35101ae08745Sheppo 	ldcmsg->env |= LDC_FRAG_START;
35111ae08745Sheppo 
35121ae08745Sheppo 	/*
35131ae08745Sheppo 	 * All packets have been copied into the TX queue
35141ae08745Sheppo 	 * update the tail ptr in the HV
35151ae08745Sheppo 	 */
35161ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
35171ae08745Sheppo 	if (rv == 0) {
35181ae08745Sheppo 		ldcp->tx_tail = tx_tail;
35191ae08745Sheppo 		ldcp->last_msg_snt = curr_seqid;
35201ae08745Sheppo 		*size = bytes_written;
35211ae08745Sheppo 	} else {
35221ae08745Sheppo 		int rv2;
35231ae08745Sheppo 
35241ae08745Sheppo 		if (rv != EWOULDBLOCK) {
35251ae08745Sheppo 			/* cannot write data - reset channel */
35261ae08745Sheppo 			i_ldc_reset(ldcp);
35271ae08745Sheppo 			*size = 0;
35281ae08745Sheppo 			return (ECONNRESET);
35291ae08745Sheppo 		}
35301ae08745Sheppo 
35311ae08745Sheppo 		DWARN(ldcp->id, "hv_tx_set_tail returns 0x%x (head 0x%x, "
35321ae08745Sheppo 			"old tail 0x%x, new tail 0x%x, qsize=0x%x)\n",
35331ae08745Sheppo 			rv, ldcp->tx_head, ldcp->tx_tail, tx_tail,
35341ae08745Sheppo 			(ldcp->tx_q_entries << LDC_PACKET_SHIFT));
35351ae08745Sheppo 
35361ae08745Sheppo 		rv2 = hv_ldc_tx_get_state(ldcp->id,
35371ae08745Sheppo 		    &tx_head, &tx_tail, &ldcp->link_state);
35381ae08745Sheppo 
35391ae08745Sheppo 		DWARN(ldcp->id, "hv_ldc_tx_get_state returns 0x%x "
35401ae08745Sheppo 			"(head 0x%x, tail 0x%x state 0x%x)\n",
35411ae08745Sheppo 			rv2, tx_head, tx_tail, ldcp->link_state);
35421ae08745Sheppo 
35431ae08745Sheppo 		*size = 0;
35441ae08745Sheppo 	}
35451ae08745Sheppo 
35461ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, *size);
35471ae08745Sheppo 
35481ae08745Sheppo 	return (rv);
35491ae08745Sheppo }
35501ae08745Sheppo 
35511ae08745Sheppo /*
35521ae08745Sheppo  * Write specified amount of bytes to the channel
35531ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
35541ae08745Sheppo  * packet is tagged with an unique packet ID in
35551ae08745Sheppo  * the case of a reliable transport.
35561ae08745Sheppo  *
35571ae08745Sheppo  * On return, size contains the number of bytes written.
35581ae08745Sheppo  * This function needs to ensure that the write size is < MTU size
35591ae08745Sheppo  */
35601ae08745Sheppo static int
35611ae08745Sheppo i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
35621ae08745Sheppo {
35631ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
35641ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_STREAM);
35651ae08745Sheppo 
35661ae08745Sheppo 	/* Truncate packet to max of MTU size */
35671ae08745Sheppo 	if (*sizep > ldcp->mtu) *sizep = ldcp->mtu;
35681ae08745Sheppo 	return (i_ldc_write_packet(ldcp, buf, sizep));
35691ae08745Sheppo }
35701ae08745Sheppo 
35711ae08745Sheppo 
35721ae08745Sheppo /*
35731ae08745Sheppo  * Interfaces for channel nexus to register/unregister with LDC module
35741ae08745Sheppo  * The nexus will register functions to be used to register individual
35751ae08745Sheppo  * channels with the nexus and enable interrupts for the channels
35761ae08745Sheppo  */
35771ae08745Sheppo int
35781ae08745Sheppo ldc_register(ldc_cnex_t *cinfo)
35791ae08745Sheppo {
35801ae08745Sheppo 	ldc_chan_t	*ldcp;
35811ae08745Sheppo 
35821ae08745Sheppo 	if (cinfo == NULL || cinfo->dip == NULL ||
35831ae08745Sheppo 	    cinfo->reg_chan == NULL || cinfo->unreg_chan == NULL ||
35841ae08745Sheppo 	    cinfo->add_intr == NULL || cinfo->rem_intr == NULL ||
35851ae08745Sheppo 	    cinfo->clr_intr == NULL) {
35861ae08745Sheppo 
35871ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_register: invalid nexus info\n");
35881ae08745Sheppo 		return (EINVAL);
35891ae08745Sheppo 	}
35901ae08745Sheppo 
35911ae08745Sheppo 	mutex_enter(&ldcssp->lock);
35921ae08745Sheppo 
35931ae08745Sheppo 	/* nexus registration */
35941ae08745Sheppo 	ldcssp->cinfo.dip = cinfo->dip;
35951ae08745Sheppo 	ldcssp->cinfo.reg_chan = cinfo->reg_chan;
35961ae08745Sheppo 	ldcssp->cinfo.unreg_chan = cinfo->unreg_chan;
35971ae08745Sheppo 	ldcssp->cinfo.add_intr = cinfo->add_intr;
35981ae08745Sheppo 	ldcssp->cinfo.rem_intr = cinfo->rem_intr;
35991ae08745Sheppo 	ldcssp->cinfo.clr_intr = cinfo->clr_intr;
36001ae08745Sheppo 
36011ae08745Sheppo 	/* register any channels that might have been previously initialized */
36021ae08745Sheppo 	ldcp = ldcssp->chan_list;
36031ae08745Sheppo 	while (ldcp) {
36041ae08745Sheppo 		if ((ldcp->tstate & TS_QCONF_RDY) &&
36051ae08745Sheppo 		    (ldcp->tstate & TS_CNEX_RDY) == 0)
36061ae08745Sheppo 			(void) i_ldc_register_channel(ldcp);
36071ae08745Sheppo 
36081ae08745Sheppo 		ldcp = ldcp->next;
36091ae08745Sheppo 	}
36101ae08745Sheppo 
36111ae08745Sheppo 	mutex_exit(&ldcssp->lock);
36121ae08745Sheppo 
36131ae08745Sheppo 	return (0);
36141ae08745Sheppo }
36151ae08745Sheppo 
36161ae08745Sheppo int
36171ae08745Sheppo ldc_unregister(ldc_cnex_t *cinfo)
36181ae08745Sheppo {
36191ae08745Sheppo 	if (cinfo == NULL || cinfo->dip == NULL) {
36201ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid nexus info\n");
36211ae08745Sheppo 		return (EINVAL);
36221ae08745Sheppo 	}
36231ae08745Sheppo 
36241ae08745Sheppo 	mutex_enter(&ldcssp->lock);
36251ae08745Sheppo 
36261ae08745Sheppo 	if (cinfo->dip != ldcssp->cinfo.dip) {
36271ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid dip\n");
36281ae08745Sheppo 		mutex_exit(&ldcssp->lock);
36291ae08745Sheppo 		return (EINVAL);
36301ae08745Sheppo 	}
36311ae08745Sheppo 
36321ae08745Sheppo 	/* nexus unregister */
36331ae08745Sheppo 	ldcssp->cinfo.dip = NULL;
36341ae08745Sheppo 	ldcssp->cinfo.reg_chan = NULL;
36351ae08745Sheppo 	ldcssp->cinfo.unreg_chan = NULL;
36361ae08745Sheppo 	ldcssp->cinfo.add_intr = NULL;
36371ae08745Sheppo 	ldcssp->cinfo.rem_intr = NULL;
36381ae08745Sheppo 	ldcssp->cinfo.clr_intr = NULL;
36391ae08745Sheppo 
36401ae08745Sheppo 	mutex_exit(&ldcssp->lock);
36411ae08745Sheppo 
36421ae08745Sheppo 	return (0);
36431ae08745Sheppo }
36441ae08745Sheppo 
36451ae08745Sheppo 
36461ae08745Sheppo /* ------------------------------------------------------------------------- */
36471ae08745Sheppo 
36481ae08745Sheppo /*
36491ae08745Sheppo  * Allocate a memory handle for the channel and link it into the list
36501ae08745Sheppo  * Also choose which memory table to use if this is the first handle
36511ae08745Sheppo  * being assigned to this channel
36521ae08745Sheppo  */
36531ae08745Sheppo int
36541ae08745Sheppo ldc_mem_alloc_handle(ldc_handle_t handle, ldc_mem_handle_t *mhandle)
36551ae08745Sheppo {
36561ae08745Sheppo 	ldc_chan_t 	*ldcp;
36571ae08745Sheppo 	ldc_mhdl_t	*mhdl;
36581ae08745Sheppo 	int 		rv;
36591ae08745Sheppo 
36601ae08745Sheppo 	if (handle == NULL) {
36611ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
36621ae08745Sheppo 		    "ldc_mem_alloc_handle: invalid channel handle\n");
36631ae08745Sheppo 		return (EINVAL);
36641ae08745Sheppo 	}
36651ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
36661ae08745Sheppo 
36671ae08745Sheppo 	mutex_enter(&ldcp->lock);
36681ae08745Sheppo 
36691ae08745Sheppo 	/* check to see if channel is initalized */
36701ae08745Sheppo 	if (ldcp->tstate < TS_INIT) {
36711ae08745Sheppo 		DWARN(ldcp->id,
36721ae08745Sheppo 		    "ldc_mem_alloc_handle: (0x%llx) channel not initialized\n",
36731ae08745Sheppo 		    ldcp->id);
36741ae08745Sheppo 		mutex_exit(&ldcp->lock);
36751ae08745Sheppo 		return (EINVAL);
36761ae08745Sheppo 	}
36771ae08745Sheppo 
36781ae08745Sheppo 	/*
36791ae08745Sheppo 	 * If this channel is allocating a mem handle for the
36801ae08745Sheppo 	 * first time allocate it a memory map table and initialize it
36811ae08745Sheppo 	 */
36821ae08745Sheppo 	if (ldcp->mtbl == NULL) {
36831ae08745Sheppo 
36841ae08745Sheppo 		ldc_mtbl_t *mtbl;
36851ae08745Sheppo 
36861ae08745Sheppo 		/* Allocate and initialize the map table structure */
36871ae08745Sheppo 		mtbl = kmem_zalloc(sizeof (ldc_mtbl_t), KM_SLEEP);
36881ae08745Sheppo 		mtbl->size = MTBL_MAX_SIZE;
36891ae08745Sheppo 		mtbl->num_entries = mtbl->num_avail =
36901ae08745Sheppo 			(MTBL_MAX_SIZE/sizeof (ldc_mte_slot_t));
36911ae08745Sheppo 		mtbl->next_entry = NULL;
36921ae08745Sheppo 
36931ae08745Sheppo 		/* Allocate the table itself */
36941ae08745Sheppo 		mtbl->table = (ldc_mte_slot_t *)
36951ae08745Sheppo 			contig_mem_alloc_align(mtbl->size, MMU_PAGESIZE);
36961ae08745Sheppo 		if (mtbl->table == NULL) {
36971ae08745Sheppo 			cmn_err(CE_WARN,
36981ae08745Sheppo 			    "ldc_mem_alloc_handle: (0x%lx) error allocating "
36991ae08745Sheppo 			    "table memory", ldcp->id);
37001ae08745Sheppo 			kmem_free(mtbl, sizeof (ldc_mtbl_t));
37011ae08745Sheppo 			mutex_exit(&ldcp->lock);
37021ae08745Sheppo 			return (ENOMEM);
37031ae08745Sheppo 		}
37041ae08745Sheppo 
37051ae08745Sheppo 		/* zero out the memory */
37061ae08745Sheppo 		bzero(mtbl->table, mtbl->size);
37071ae08745Sheppo 
37081ae08745Sheppo 		/* initialize the lock */
37091ae08745Sheppo 		mutex_init(&mtbl->lock, NULL, MUTEX_DRIVER, NULL);
37101ae08745Sheppo 
37111ae08745Sheppo 		/* register table for this channel */
37121ae08745Sheppo 		rv = hv_ldc_set_map_table(ldcp->id,
37131ae08745Sheppo 		    va_to_pa(mtbl->table), mtbl->num_entries);
37141ae08745Sheppo 		if (rv != 0) {
37151ae08745Sheppo 			cmn_err(CE_WARN,
37161ae08745Sheppo 			    "ldc_mem_alloc_handle: (0x%lx) err %d mapping tbl",
37171ae08745Sheppo 			    ldcp->id, rv);
37181ae08745Sheppo 			contig_mem_free(mtbl->table, mtbl->size);
37191ae08745Sheppo 			mutex_destroy(&mtbl->lock);
37201ae08745Sheppo 			kmem_free(mtbl, sizeof (ldc_mtbl_t));
37211ae08745Sheppo 			mutex_exit(&ldcp->lock);
37221ae08745Sheppo 			return (EIO);
37231ae08745Sheppo 		}
37241ae08745Sheppo 
37251ae08745Sheppo 		ldcp->mtbl = mtbl;
37261ae08745Sheppo 
37271ae08745Sheppo 		D1(ldcp->id,
37281ae08745Sheppo 		    "ldc_mem_alloc_handle: (0x%llx) alloc'd map table 0x%llx\n",
37291ae08745Sheppo 		    ldcp->id, ldcp->mtbl->table);
37301ae08745Sheppo 	}
37311ae08745Sheppo 
37321ae08745Sheppo 	/* allocate handle for channel */
37331ae08745Sheppo 	mhdl = kmem_zalloc(sizeof (ldc_mhdl_t), KM_SLEEP);
37341ae08745Sheppo 
37351ae08745Sheppo 	/* initialize the lock */
37361ae08745Sheppo 	mutex_init(&mhdl->lock, NULL, MUTEX_DRIVER, NULL);
37371ae08745Sheppo 
37381ae08745Sheppo 	mhdl->status = LDC_UNBOUND;
37391ae08745Sheppo 	mhdl->ldcp = ldcp;
37401ae08745Sheppo 
37411ae08745Sheppo 	/* insert memory handle (@ head) into list */
37421ae08745Sheppo 	if (ldcp->mhdl_list == NULL) {
37431ae08745Sheppo 		ldcp->mhdl_list = mhdl;
37441ae08745Sheppo 		mhdl->next = NULL;
37451ae08745Sheppo 	} else {
37461ae08745Sheppo 		/* insert @ head */
37471ae08745Sheppo 		mhdl->next = ldcp->mhdl_list;
37481ae08745Sheppo 		ldcp->mhdl_list = mhdl;
37491ae08745Sheppo 	}
37501ae08745Sheppo 
37511ae08745Sheppo 	/* return the handle */
37521ae08745Sheppo 	*mhandle = (ldc_mem_handle_t)mhdl;
37531ae08745Sheppo 
37541ae08745Sheppo 	mutex_exit(&ldcp->lock);
37551ae08745Sheppo 
37561ae08745Sheppo 	D1(ldcp->id, "ldc_mem_alloc_handle: (0x%llx) allocated handle 0x%llx\n",
37571ae08745Sheppo 	    ldcp->id, mhdl);
37581ae08745Sheppo 
37591ae08745Sheppo 	return (0);
37601ae08745Sheppo }
37611ae08745Sheppo 
37621ae08745Sheppo /*
37631ae08745Sheppo  * Free memory handle for the channel and unlink it from the list
37641ae08745Sheppo  */
37651ae08745Sheppo int
37661ae08745Sheppo ldc_mem_free_handle(ldc_mem_handle_t mhandle)
37671ae08745Sheppo {
37681ae08745Sheppo 	ldc_mhdl_t 	*mhdl, *phdl;
37691ae08745Sheppo 	ldc_chan_t 	*ldcp;
37701ae08745Sheppo 
37711ae08745Sheppo 	if (mhandle == NULL) {
37721ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
37731ae08745Sheppo 		    "ldc_mem_free_handle: invalid memory handle\n");
37741ae08745Sheppo 		return (EINVAL);
37751ae08745Sheppo 	}
37761ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
37771ae08745Sheppo 
37781ae08745Sheppo 	mutex_enter(&mhdl->lock);
37791ae08745Sheppo 
37801ae08745Sheppo 	ldcp = mhdl->ldcp;
37811ae08745Sheppo 
37821ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) {
37831ae08745Sheppo 		DWARN(ldcp->id,
37841ae08745Sheppo 		    "ldc_mem_free_handle: cannot free, 0x%llx hdl bound\n",
37851ae08745Sheppo 		    mhdl);
37861ae08745Sheppo 		mutex_exit(&mhdl->lock);
37871ae08745Sheppo 		return (EINVAL);
37881ae08745Sheppo 	}
37891ae08745Sheppo 	mutex_exit(&mhdl->lock);
37901ae08745Sheppo 
37911ae08745Sheppo 	mutex_enter(&ldcp->mlist_lock);
37921ae08745Sheppo 
37931ae08745Sheppo 	phdl = ldcp->mhdl_list;
37941ae08745Sheppo 
37951ae08745Sheppo 	/* first handle */
37961ae08745Sheppo 	if (phdl == mhdl) {
37971ae08745Sheppo 		ldcp->mhdl_list = mhdl->next;
37981ae08745Sheppo 		mutex_destroy(&mhdl->lock);
37991ae08745Sheppo 		kmem_free(mhdl, sizeof (ldc_mhdl_t));
38001ae08745Sheppo 		D1(ldcp->id,
38011ae08745Sheppo 		    "ldc_mem_free_handle: (0x%llx) freed handle 0x%llx\n",
38021ae08745Sheppo 		    ldcp->id, mhdl);
38031ae08745Sheppo 	} else {
38041ae08745Sheppo 		/* walk the list - unlink and free */
38051ae08745Sheppo 		while (phdl != NULL) {
38061ae08745Sheppo 			if (phdl->next == mhdl) {
38071ae08745Sheppo 				phdl->next = mhdl->next;
38081ae08745Sheppo 				mutex_destroy(&mhdl->lock);
38091ae08745Sheppo 				kmem_free(mhdl, sizeof (ldc_mhdl_t));
38101ae08745Sheppo 				D1(ldcp->id,
38111ae08745Sheppo 				    "ldc_mem_free_handle: (0x%llx) freed "
38121ae08745Sheppo 				    "handle 0x%llx\n", ldcp->id, mhdl);
38131ae08745Sheppo 				break;
38141ae08745Sheppo 			}
38151ae08745Sheppo 			phdl = phdl->next;
38161ae08745Sheppo 		}
38171ae08745Sheppo 	}
38181ae08745Sheppo 
38191ae08745Sheppo 	if (phdl == NULL) {
38201ae08745Sheppo 		DWARN(ldcp->id,
38211ae08745Sheppo 		    "ldc_mem_free_handle: invalid handle 0x%llx\n", mhdl);
38221ae08745Sheppo 		mutex_exit(&ldcp->mlist_lock);
38231ae08745Sheppo 		return (EINVAL);
38241ae08745Sheppo 	}
38251ae08745Sheppo 
38261ae08745Sheppo 	mutex_exit(&ldcp->mlist_lock);
38271ae08745Sheppo 
38281ae08745Sheppo 	return (0);
38291ae08745Sheppo }
38301ae08745Sheppo 
38311ae08745Sheppo /*
38321ae08745Sheppo  * Bind a memory handle to a virtual address.
38331ae08745Sheppo  * The virtual address is converted to the corresponding real addresses.
38341ae08745Sheppo  * Returns pointer to the first ldc_mem_cookie and the total number
38351ae08745Sheppo  * of cookies for this virtual address. Other cookies can be obtained
38361ae08745Sheppo  * using the ldc_mem_nextcookie() call. If the pages are stored in
38371ae08745Sheppo  * consecutive locations in the table, a single cookie corresponding to
38381ae08745Sheppo  * the first location is returned. The cookie size spans all the entries.
38391ae08745Sheppo  *
38401ae08745Sheppo  * If the VA corresponds to a page that is already being exported, reuse
38411ae08745Sheppo  * the page and do not export it again. Bump the page's use count.
38421ae08745Sheppo  */
38431ae08745Sheppo int
38441ae08745Sheppo ldc_mem_bind_handle(ldc_mem_handle_t mhandle, caddr_t vaddr, size_t len,
38451ae08745Sheppo     uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount)
38461ae08745Sheppo {
38471ae08745Sheppo 	ldc_mhdl_t	*mhdl;
38481ae08745Sheppo 	ldc_chan_t 	*ldcp;
38491ae08745Sheppo 	ldc_mtbl_t	*mtbl;
38501ae08745Sheppo 	ldc_memseg_t	*memseg;
38511ae08745Sheppo 	ldc_mte_t	tmp_mte;
38521ae08745Sheppo 	uint64_t	index, prev_index = 0;
38531ae08745Sheppo 	int64_t		cookie_idx;
38541ae08745Sheppo 	uintptr_t	raddr, ra_aligned;
38551ae08745Sheppo 	uint64_t	psize, poffset, v_offset;
38561ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code, pg_mask;
38571ae08745Sheppo 	pgcnt_t		npages;
38581ae08745Sheppo 	caddr_t		v_align, addr;
38591ae08745Sheppo 	int 		i;
38601ae08745Sheppo 
38611ae08745Sheppo 	if (mhandle == NULL) {
38621ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
38631ae08745Sheppo 		    "ldc_mem_bind_handle: invalid memory handle\n");
38641ae08745Sheppo 		return (EINVAL);
38651ae08745Sheppo 	}
38661ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
38671ae08745Sheppo 	ldcp = mhdl->ldcp;
38681ae08745Sheppo 	mtbl = ldcp->mtbl;
38691ae08745Sheppo 
38701ae08745Sheppo 	/* clear count */
38711ae08745Sheppo 	*ccount = 0;
38721ae08745Sheppo 
38731ae08745Sheppo 	mutex_enter(&mhdl->lock);
38741ae08745Sheppo 
38751ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->memseg != NULL) {
38761ae08745Sheppo 		DWARN(ldcp->id,
38771ae08745Sheppo 		    "ldc_mem_bind_handle: (0x%x) handle already bound\n",
38781ae08745Sheppo 		    mhandle);
38791ae08745Sheppo 		mutex_exit(&mhdl->lock);
38801ae08745Sheppo 		return (EINVAL);
38811ae08745Sheppo 	}
38821ae08745Sheppo 
38831ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
38841ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
38851ae08745Sheppo 		DWARN(ldcp->id,
38861ae08745Sheppo 		    "ldc_mem_bind_handle: addr/size is not 8-byte aligned\n");
38871ae08745Sheppo 		mutex_exit(&mhdl->lock);
38881ae08745Sheppo 		return (EINVAL);
38891ae08745Sheppo 	}
38901ae08745Sheppo 
38911ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
38921ae08745Sheppo 	pg_size = MMU_PAGESIZE;
38931ae08745Sheppo 	pg_size_code = page_szc(pg_size);
38941ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
38951ae08745Sheppo 	pg_mask = ~(pg_size - 1);
38961ae08745Sheppo 
38971ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle: (0x%llx) binding "
38981ae08745Sheppo 	    "va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
38991ae08745Sheppo 	    ldcp->id, vaddr, pg_size, pg_size_code, pg_shift);
39001ae08745Sheppo 
39011ae08745Sheppo 	/* aligned VA and its offset */
39021ae08745Sheppo 	v_align = (caddr_t)(((uintptr_t)vaddr) & ~(pg_size - 1));
39031ae08745Sheppo 	v_offset = ((uintptr_t)vaddr) & (pg_size - 1);
39041ae08745Sheppo 
39051ae08745Sheppo 	npages = (len+v_offset)/pg_size;
39061ae08745Sheppo 	npages = ((len+v_offset)%pg_size == 0) ? npages : npages+1;
39071ae08745Sheppo 
39081ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle: binding "
39091ae08745Sheppo 	    "(0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n",
39101ae08745Sheppo 	    ldcp->id, vaddr, v_align, v_offset, npages);
39111ae08745Sheppo 
39121ae08745Sheppo 	/* lock the memory table - exclusive access to channel */
39131ae08745Sheppo 	mutex_enter(&mtbl->lock);
39141ae08745Sheppo 
39151ae08745Sheppo 	if (npages > mtbl->num_avail) {
39161ae08745Sheppo 		DWARN(ldcp->id,
39171ae08745Sheppo 		    "ldc_mem_bind_handle: (0x%llx) no table entries\n",
39181ae08745Sheppo 		    ldcp->id);
39191ae08745Sheppo 		mutex_exit(&mtbl->lock);
39201ae08745Sheppo 		mutex_exit(&mhdl->lock);
39211ae08745Sheppo 		return (ENOMEM);
39221ae08745Sheppo 	}
39231ae08745Sheppo 
39241ae08745Sheppo 	/* Allocate a memseg structure */
39251ae08745Sheppo 	memseg = mhdl->memseg = kmem_zalloc(sizeof (ldc_memseg_t), KM_SLEEP);
39261ae08745Sheppo 
39271ae08745Sheppo 	/* Allocate memory to store all pages and cookies */
39281ae08745Sheppo 	memseg->pages = kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP);
39291ae08745Sheppo 	memseg->cookies =
39301ae08745Sheppo 		kmem_zalloc((sizeof (ldc_mem_cookie_t) * npages), KM_SLEEP);
39311ae08745Sheppo 
39321ae08745Sheppo 	D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) processing 0x%llx pages\n",
39331ae08745Sheppo 	    ldcp->id, npages);
39341ae08745Sheppo 
39351ae08745Sheppo 	addr = v_align;
39361ae08745Sheppo 
39371ae08745Sheppo 	/*
39381ae08745Sheppo 	 * Table slots are used in a round-robin manner. The algorithm permits
39391ae08745Sheppo 	 * inserting duplicate entries. Slots allocated earlier will typically
39401ae08745Sheppo 	 * get freed before we get back to reusing the slot.Inserting duplicate
39411ae08745Sheppo 	 * entries should be OK as we only lookup entries using the cookie addr
39421ae08745Sheppo 	 * i.e. tbl index, during export, unexport and copy operation.
39431ae08745Sheppo 	 *
39441ae08745Sheppo 	 * One implementation what was tried was to search for a duplicate
39451ae08745Sheppo 	 * page entry first and reuse it. The search overhead is very high and
39461ae08745Sheppo 	 * in the vnet case dropped the perf by almost half, 50 to 24 mbps.
39471ae08745Sheppo 	 * So it does make sense to avoid searching for duplicates.
39481ae08745Sheppo 	 *
39491ae08745Sheppo 	 * But during the process of searching for a free slot, if we find a
39501ae08745Sheppo 	 * duplicate entry we will go ahead and use it, and bump its use count.
39511ae08745Sheppo 	 */
39521ae08745Sheppo 
39531ae08745Sheppo 	/* index to start searching from */
39541ae08745Sheppo 	index = mtbl->next_entry;
39551ae08745Sheppo 	cookie_idx = -1;
39561ae08745Sheppo 
39571ae08745Sheppo 	tmp_mte.ll = 0;	/* initialise fields to 0 */
39581ae08745Sheppo 
39591ae08745Sheppo 	if (mtype & LDC_DIRECT_MAP) {
39601ae08745Sheppo 		tmp_mte.mte_r = (perm & LDC_MEM_R) ? 1 : 0;
39611ae08745Sheppo 		tmp_mte.mte_w = (perm & LDC_MEM_W) ? 1 : 0;
39621ae08745Sheppo 		tmp_mte.mte_x = (perm & LDC_MEM_X) ? 1 : 0;
39631ae08745Sheppo 	}
39641ae08745Sheppo 
39651ae08745Sheppo 	if (mtype & LDC_SHADOW_MAP) {
39661ae08745Sheppo 		tmp_mte.mte_cr = (perm & LDC_MEM_R) ? 1 : 0;
39671ae08745Sheppo 		tmp_mte.mte_cw = (perm & LDC_MEM_W) ? 1 : 0;
39681ae08745Sheppo 	}
39691ae08745Sheppo 
39701ae08745Sheppo 	if (mtype & LDC_IO_MAP) {
39711ae08745Sheppo 		tmp_mte.mte_ir = (perm & LDC_MEM_R) ? 1 : 0;
39721ae08745Sheppo 		tmp_mte.mte_iw = (perm & LDC_MEM_W) ? 1 : 0;
39731ae08745Sheppo 	}
39741ae08745Sheppo 
39751ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll);
39761ae08745Sheppo 
39771ae08745Sheppo 	tmp_mte.mte_pgszc = pg_size_code;
39781ae08745Sheppo 
39791ae08745Sheppo 	/* initialize each mem table entry */
39801ae08745Sheppo 	for (i = 0; i < npages; i++) {
39811ae08745Sheppo 
39821ae08745Sheppo 		/* check if slot is available in the table */
39831ae08745Sheppo 		while (mtbl->table[index].entry.ll != 0) {
39841ae08745Sheppo 
39851ae08745Sheppo 			index = (index + 1) % mtbl->num_entries;
39861ae08745Sheppo 
39871ae08745Sheppo 			if (index == mtbl->next_entry) {
39881ae08745Sheppo 				/* we have looped around */
39891ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
39901ae08745Sheppo 				    "ldc_mem_bind_handle: (0x%llx) cannot find "
39911ae08745Sheppo 				    "entry\n", ldcp->id);
39921ae08745Sheppo 				*ccount = 0;
39931ae08745Sheppo 
39941ae08745Sheppo 				/* NOTE: free memory, remove previous entries */
39951ae08745Sheppo 				/* this shouldnt happen as num_avail was ok */
39961ae08745Sheppo 
39971ae08745Sheppo 				mutex_exit(&mtbl->lock);
39981ae08745Sheppo 				mutex_exit(&mhdl->lock);
39991ae08745Sheppo 				return (ENOMEM);
40001ae08745Sheppo 			}
40011ae08745Sheppo 		}
40021ae08745Sheppo 
40031ae08745Sheppo 		/* get the real address */
40041ae08745Sheppo 		raddr = va_to_pa((void *)addr);
40051ae08745Sheppo 		ra_aligned = ((uintptr_t)raddr & pg_mask);
40061ae08745Sheppo 
40071ae08745Sheppo 		/* build the mte */
40081ae08745Sheppo 		tmp_mte.mte_rpfn = ra_aligned >> pg_shift;
40091ae08745Sheppo 
40101ae08745Sheppo 		D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll);
40111ae08745Sheppo 
40121ae08745Sheppo 		/* update entry in table */
40131ae08745Sheppo 		mtbl->table[index].entry = tmp_mte;
40141ae08745Sheppo 
40151ae08745Sheppo 		D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) stored MTE 0x%llx"
40161ae08745Sheppo 		    " into loc 0x%llx\n", ldcp->id, tmp_mte.ll, index);
40171ae08745Sheppo 
40181ae08745Sheppo 		/* calculate the size and offset for this export range */
40191ae08745Sheppo 		if (i == 0) {
40201ae08745Sheppo 			/* first page */
40211ae08745Sheppo 			psize = min((pg_size - v_offset), len);
40221ae08745Sheppo 			poffset = v_offset;
40231ae08745Sheppo 
40241ae08745Sheppo 		} else if (i == (npages - 1)) {
40251ae08745Sheppo 			/* last page */
40261ae08745Sheppo 			psize =	(((uintptr_t)(vaddr + len)) &
40271ae08745Sheppo 				    ((uint64_t)(pg_size-1)));
40281ae08745Sheppo 			if (psize == 0)
40291ae08745Sheppo 				psize = pg_size;
40301ae08745Sheppo 			poffset = 0;
40311ae08745Sheppo 
40321ae08745Sheppo 		} else {
40331ae08745Sheppo 			/* middle pages */
40341ae08745Sheppo 			psize = pg_size;
40351ae08745Sheppo 			poffset = 0;
40361ae08745Sheppo 		}
40371ae08745Sheppo 
40381ae08745Sheppo 		/* store entry for this page */
40391ae08745Sheppo 		memseg->pages[i].index = index;
40401ae08745Sheppo 		memseg->pages[i].raddr = raddr;
40411ae08745Sheppo 		memseg->pages[i].offset = poffset;
40421ae08745Sheppo 		memseg->pages[i].size = psize;
40431ae08745Sheppo 		memseg->pages[i].mte = &(mtbl->table[index]);
40441ae08745Sheppo 
40451ae08745Sheppo 		/* create the cookie */
40461ae08745Sheppo 		if (i == 0 || (index != prev_index + 1)) {
40471ae08745Sheppo 			cookie_idx++;
40481ae08745Sheppo 			memseg->cookies[cookie_idx].addr =
40491ae08745Sheppo 				IDX2COOKIE(index, pg_size_code, pg_shift);
40501ae08745Sheppo 			memseg->cookies[cookie_idx].addr |= poffset;
40511ae08745Sheppo 			memseg->cookies[cookie_idx].size = psize;
40521ae08745Sheppo 
40531ae08745Sheppo 		} else {
40541ae08745Sheppo 			memseg->cookies[cookie_idx].size += psize;
40551ae08745Sheppo 		}
40561ae08745Sheppo 
40571ae08745Sheppo 		D1(ldcp->id, "ldc_mem_bind_handle: bound "
40581ae08745Sheppo 		    "(0x%llx) va=0x%llx, idx=0x%llx, "
40591ae08745Sheppo 		    "ra=0x%llx(sz=0x%x,off=0x%x)\n",
40601ae08745Sheppo 		    ldcp->id, addr, index, raddr, psize, poffset);
40611ae08745Sheppo 
40621ae08745Sheppo 		/* decrement number of available entries */
40631ae08745Sheppo 		mtbl->num_avail--;
40641ae08745Sheppo 
40651ae08745Sheppo 		/* increment va by page size */
40661ae08745Sheppo 		addr += pg_size;
40671ae08745Sheppo 
40681ae08745Sheppo 		/* increment index */
40691ae08745Sheppo 		prev_index = index;
40701ae08745Sheppo 		index = (index + 1) % mtbl->num_entries;
40711ae08745Sheppo 
40721ae08745Sheppo 		/* save the next slot */
40731ae08745Sheppo 		mtbl->next_entry = index;
40741ae08745Sheppo 	}
40751ae08745Sheppo 
40761ae08745Sheppo 	mutex_exit(&mtbl->lock);
40771ae08745Sheppo 
40781ae08745Sheppo 	/* memory handle = bound */
40791ae08745Sheppo 	mhdl->mtype = mtype;
40801ae08745Sheppo 	mhdl->perm = perm;
40811ae08745Sheppo 	mhdl->status = LDC_BOUND;
40821ae08745Sheppo 
40831ae08745Sheppo 	/* update memseg_t */
40841ae08745Sheppo 	memseg->vaddr = vaddr;
40851ae08745Sheppo 	memseg->raddr = memseg->pages[0].raddr;
40861ae08745Sheppo 	memseg->size = len;
40871ae08745Sheppo 	memseg->npages = npages;
40881ae08745Sheppo 	memseg->ncookies = cookie_idx + 1;
40891ae08745Sheppo 	memseg->next_cookie = (memseg->ncookies > 1) ? 1 : 0;
40901ae08745Sheppo 
40911ae08745Sheppo 	/* return count and first cookie */
40921ae08745Sheppo 	*ccount = memseg->ncookies;
40931ae08745Sheppo 	cookie->addr = memseg->cookies[0].addr;
40941ae08745Sheppo 	cookie->size = memseg->cookies[0].size;
40951ae08745Sheppo 
40961ae08745Sheppo 	D1(ldcp->id,
40971ae08745Sheppo 	    "ldc_mem_bind_handle: (0x%llx) bound 0x%llx, va=0x%llx, "
40981ae08745Sheppo 	    "pgs=0x%llx cookies=0x%llx\n",
40991ae08745Sheppo 	    ldcp->id, mhdl, vaddr, npages, memseg->ncookies);
41001ae08745Sheppo 
41011ae08745Sheppo 	mutex_exit(&mhdl->lock);
41021ae08745Sheppo 	return (0);
41031ae08745Sheppo }
41041ae08745Sheppo 
41051ae08745Sheppo /*
41061ae08745Sheppo  * Return the next cookie associated with the specified memory handle
41071ae08745Sheppo  */
41081ae08745Sheppo int
41091ae08745Sheppo ldc_mem_nextcookie(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie)
41101ae08745Sheppo {
41111ae08745Sheppo 	ldc_mhdl_t	*mhdl;
41121ae08745Sheppo 	ldc_chan_t 	*ldcp;
41131ae08745Sheppo 	ldc_memseg_t	*memseg;
41141ae08745Sheppo 
41151ae08745Sheppo 	if (mhandle == NULL) {
41161ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
41171ae08745Sheppo 		    "ldc_mem_nextcookie: invalid memory handle\n");
41181ae08745Sheppo 		return (EINVAL);
41191ae08745Sheppo 	}
41201ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
41211ae08745Sheppo 
41221ae08745Sheppo 	mutex_enter(&mhdl->lock);
41231ae08745Sheppo 
41241ae08745Sheppo 	ldcp = mhdl->ldcp;
41251ae08745Sheppo 	memseg = mhdl->memseg;
41261ae08745Sheppo 
41271ae08745Sheppo 	if (cookie == 0) {
41281ae08745Sheppo 		DWARN(ldcp->id,
41291ae08745Sheppo 		    "ldc_mem_nextcookie:(0x%llx) invalid cookie arg\n",
41301ae08745Sheppo 		    ldcp->id);
41311ae08745Sheppo 		mutex_exit(&mhdl->lock);
41321ae08745Sheppo 		return (EINVAL);
41331ae08745Sheppo 	}
41341ae08745Sheppo 
41351ae08745Sheppo 	if (memseg->next_cookie != 0) {
41361ae08745Sheppo 		cookie->addr = memseg->cookies[memseg->next_cookie].addr;
41371ae08745Sheppo 		cookie->size = memseg->cookies[memseg->next_cookie].size;
41381ae08745Sheppo 		memseg->next_cookie++;
41391ae08745Sheppo 		if (memseg->next_cookie == memseg->ncookies)
41401ae08745Sheppo 			memseg->next_cookie = 0;
41411ae08745Sheppo 
41421ae08745Sheppo 	} else {
41431ae08745Sheppo 		DWARN(ldcp->id,
41441ae08745Sheppo 		    "ldc_mem_nextcookie:(0x%llx) no more cookies\n", ldcp->id);
41451ae08745Sheppo 		cookie->addr = 0;
41461ae08745Sheppo 		cookie->size = 0;
41471ae08745Sheppo 		mutex_exit(&mhdl->lock);
41481ae08745Sheppo 		return (EINVAL);
41491ae08745Sheppo 	}
41501ae08745Sheppo 
41511ae08745Sheppo 	D1(ldcp->id,
41521ae08745Sheppo 	    "ldc_mem_nextcookie: (0x%llx) cookie addr=0x%llx,sz=0x%llx\n",
41531ae08745Sheppo 	    ldcp->id, cookie->addr, cookie->size);
41541ae08745Sheppo 
41551ae08745Sheppo 	mutex_exit(&mhdl->lock);
41561ae08745Sheppo 	return (0);
41571ae08745Sheppo }
41581ae08745Sheppo 
41591ae08745Sheppo /*
41601ae08745Sheppo  * Unbind the virtual memory region associated with the specified
41611ae08745Sheppo  * memory handle. Allassociated cookies are freed and the corresponding
41621ae08745Sheppo  * RA space is no longer exported.
41631ae08745Sheppo  */
41641ae08745Sheppo int
41651ae08745Sheppo ldc_mem_unbind_handle(ldc_mem_handle_t mhandle)
41661ae08745Sheppo {
41671ae08745Sheppo 	ldc_mhdl_t	*mhdl;
41681ae08745Sheppo 	ldc_chan_t 	*ldcp;
41691ae08745Sheppo 	ldc_mtbl_t	*mtbl;
41701ae08745Sheppo 	ldc_memseg_t	*memseg;
41711ae08745Sheppo 	int		i;
41721ae08745Sheppo 
41731ae08745Sheppo 	if (mhandle == NULL) {
41741ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
41751ae08745Sheppo 		    "ldc_mem_unbind_handle: invalid memory handle\n");
41761ae08745Sheppo 		return (EINVAL);
41771ae08745Sheppo 	}
41781ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
41791ae08745Sheppo 
41801ae08745Sheppo 	mutex_enter(&mhdl->lock);
41811ae08745Sheppo 
41821ae08745Sheppo 	if (mhdl->status == LDC_UNBOUND) {
41831ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
41841ae08745Sheppo 		    "ldc_mem_unbind_handle: (0x%x) handle is not bound\n",
41851ae08745Sheppo 		    mhandle);
41861ae08745Sheppo 		mutex_exit(&mhdl->lock);
41871ae08745Sheppo 		return (EINVAL);
41881ae08745Sheppo 	}
41891ae08745Sheppo 
41901ae08745Sheppo 	ldcp = mhdl->ldcp;
41911ae08745Sheppo 	mtbl = ldcp->mtbl;
41921ae08745Sheppo 
41931ae08745Sheppo 	memseg = mhdl->memseg;
41941ae08745Sheppo 
41951ae08745Sheppo 	/* lock the memory table - exclusive access to channel */
41961ae08745Sheppo 	mutex_enter(&mtbl->lock);
41971ae08745Sheppo 
41981ae08745Sheppo 	/* undo the pages exported */
41991ae08745Sheppo 	for (i = 0; i < memseg->npages; i++) {
42001ae08745Sheppo 
42011ae08745Sheppo 		/* FUTURE: check for mapped pages */
42021ae08745Sheppo 		if (memseg->pages[i].mte->cookie) {
42031ae08745Sheppo 			_NOTE(EMPTY)
42041ae08745Sheppo 		}
42051ae08745Sheppo 
42061ae08745Sheppo 		/* clear the entry from the table */
42071ae08745Sheppo 		memseg->pages[i].mte->entry.ll = 0;
42081ae08745Sheppo 		mtbl->num_avail++;
42091ae08745Sheppo 	}
42101ae08745Sheppo 	mutex_exit(&mtbl->lock);
42111ae08745Sheppo 
42121ae08745Sheppo 	/* free the allocated memseg and page structures */
42131ae08745Sheppo 	kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages));
42141ae08745Sheppo 	kmem_free(memseg->cookies,
42151ae08745Sheppo 	    (sizeof (ldc_mem_cookie_t) * memseg->npages));
42161ae08745Sheppo 	kmem_free(memseg, sizeof (ldc_memseg_t));
42171ae08745Sheppo 
42181ae08745Sheppo 	/* uninitialize the memory handle */
42191ae08745Sheppo 	mhdl->memseg = NULL;
42201ae08745Sheppo 	mhdl->status = LDC_UNBOUND;
42211ae08745Sheppo 
42221ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unbind_handle: (0x%llx) unbound handle 0x%llx\n",
42231ae08745Sheppo 	    ldcp->id, mhdl);
42241ae08745Sheppo 
42251ae08745Sheppo 	mutex_exit(&mhdl->lock);
42261ae08745Sheppo 	return (0);
42271ae08745Sheppo }
42281ae08745Sheppo 
42291ae08745Sheppo /*
42301ae08745Sheppo  * Get information about the dring. The base address of the descriptor
42311ae08745Sheppo  * ring along with the type and permission are returned back.
42321ae08745Sheppo  */
42331ae08745Sheppo int
42341ae08745Sheppo ldc_mem_info(ldc_mem_handle_t mhandle, ldc_mem_info_t *minfo)
42351ae08745Sheppo {
42361ae08745Sheppo 	ldc_mhdl_t	*mhdl;
42371ae08745Sheppo 
42381ae08745Sheppo 	if (mhandle == NULL) {
42391ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid memory handle\n");
42401ae08745Sheppo 		return (EINVAL);
42411ae08745Sheppo 	}
42421ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
42431ae08745Sheppo 
42441ae08745Sheppo 	if (minfo == NULL) {
42451ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid args\n");
42461ae08745Sheppo 		return (EINVAL);
42471ae08745Sheppo 	}
42481ae08745Sheppo 
42491ae08745Sheppo 	mutex_enter(&mhdl->lock);
42501ae08745Sheppo 
42511ae08745Sheppo 	minfo->status = mhdl->status;
42521ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) {
42531ae08745Sheppo 		minfo->vaddr = mhdl->memseg->vaddr;
42541ae08745Sheppo 		minfo->raddr = mhdl->memseg->raddr;
42551ae08745Sheppo 		minfo->mtype = mhdl->mtype;
42561ae08745Sheppo 		minfo->perm = mhdl->perm;
42571ae08745Sheppo 	}
42581ae08745Sheppo 	mutex_exit(&mhdl->lock);
42591ae08745Sheppo 
42601ae08745Sheppo 	return (0);
42611ae08745Sheppo }
42621ae08745Sheppo 
42631ae08745Sheppo /*
42641ae08745Sheppo  * Copy data either from or to the client specified virtual address
42651ae08745Sheppo  * space to or from the exported memory associated with the cookies.
42661ae08745Sheppo  * The direction argument determines whether the data is read from or
42671ae08745Sheppo  * written to exported memory.
42681ae08745Sheppo  */
42691ae08745Sheppo int
42701ae08745Sheppo ldc_mem_copy(ldc_handle_t handle, caddr_t vaddr, uint64_t off, size_t *size,
42711ae08745Sheppo     ldc_mem_cookie_t *cookies, uint32_t ccount, uint8_t direction)
42721ae08745Sheppo {
42731ae08745Sheppo 	ldc_chan_t 	*ldcp;
42741ae08745Sheppo 	uint64_t	local_voff, local_valign;
42751ae08745Sheppo 	uint64_t	cookie_addr, cookie_size;
42761ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code;
42771ae08745Sheppo 	uint64_t 	export_caddr, export_poff, export_psize, export_size;
42781ae08745Sheppo 	uint64_t	local_ra, local_poff, local_psize;
42791ae08745Sheppo 	uint64_t	copy_size, copied_len = 0, total_bal = 0, idx = 0;
42801ae08745Sheppo 	pgcnt_t		npages;
42811ae08745Sheppo 	size_t		len = *size;
42821ae08745Sheppo 	int 		i, rv = 0;
42831ae08745Sheppo 
42841ae08745Sheppo 	if (handle == NULL) {
42851ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_copy: invalid channel handle\n");
42861ae08745Sheppo 		return (EINVAL);
42871ae08745Sheppo 	}
42881ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
42891ae08745Sheppo 
42901ae08745Sheppo 	mutex_enter(&ldcp->lock);
42911ae08745Sheppo 
42921ae08745Sheppo 	/* check to see if channel is UP */
42931ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
42941ae08745Sheppo 		DWARN(ldcp->id, "ldc_mem_copy: (0x%llx) channel is not UP\n",
42951ae08745Sheppo 		    ldcp->id);
42961ae08745Sheppo 		mutex_exit(&ldcp->lock);
42971ae08745Sheppo 		return (EINVAL);
42981ae08745Sheppo 	}
42991ae08745Sheppo 
43001ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
43011ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
43021ae08745Sheppo 		DWARN(ldcp->id,
43031ae08745Sheppo 		    "ldc_mem_copy: addr/sz is not 8-byte aligned\n");
43041ae08745Sheppo 		mutex_exit(&ldcp->lock);
43051ae08745Sheppo 		return (EINVAL);
43061ae08745Sheppo 	}
43071ae08745Sheppo 
43081ae08745Sheppo 	/* Find the size of the exported memory */
43091ae08745Sheppo 	export_size = 0;
43101ae08745Sheppo 	for (i = 0; i < ccount; i++)
43111ae08745Sheppo 		export_size += cookies[i].size;
43121ae08745Sheppo 
43131ae08745Sheppo 	/* check to see if offset is valid */
43141ae08745Sheppo 	if (off > export_size) {
43151ae08745Sheppo 		DWARN(ldcp->id,
43161ae08745Sheppo 		    "ldc_mem_copy: (0x%llx) start offset > export mem size\n",
43171ae08745Sheppo 		    ldcp->id);
43181ae08745Sheppo 		mutex_exit(&ldcp->lock);
43191ae08745Sheppo 		return (EINVAL);
43201ae08745Sheppo 	}
43211ae08745Sheppo 
43221ae08745Sheppo 	/*
43231ae08745Sheppo 	 * Check to see if the export size is smaller than the size we
43241ae08745Sheppo 	 * are requesting to copy - if so flag an error
43251ae08745Sheppo 	 */
43261ae08745Sheppo 	if ((export_size - off) < *size) {
43271ae08745Sheppo 		DWARN(ldcp->id,
43281ae08745Sheppo 		    "ldc_mem_copy: (0x%llx) copy size > export mem size\n",
43291ae08745Sheppo 		    ldcp->id);
43301ae08745Sheppo 		mutex_exit(&ldcp->lock);
43311ae08745Sheppo 		return (EINVAL);
43321ae08745Sheppo 	}
43331ae08745Sheppo 
43341ae08745Sheppo 	total_bal = min(export_size, *size);
43351ae08745Sheppo 
43361ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
43371ae08745Sheppo 	pg_size = MMU_PAGESIZE;
43381ae08745Sheppo 	pg_size_code = page_szc(pg_size);
43391ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
43401ae08745Sheppo 
43411ae08745Sheppo 	D1(ldcp->id, "ldc_mem_copy: copying data "
43421ae08745Sheppo 	    "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
43431ae08745Sheppo 	    ldcp->id, vaddr, pg_size, pg_size_code, pg_shift);
43441ae08745Sheppo 
43451ae08745Sheppo 	/* aligned VA and its offset */
43461ae08745Sheppo 	local_valign = (((uintptr_t)vaddr) & ~(pg_size - 1));
43471ae08745Sheppo 	local_voff = ((uintptr_t)vaddr) & (pg_size - 1);
43481ae08745Sheppo 
43491ae08745Sheppo 	npages = (len+local_voff)/pg_size;
43501ae08745Sheppo 	npages = ((len+local_voff)%pg_size == 0) ? npages : npages+1;
43511ae08745Sheppo 
43521ae08745Sheppo 	D1(ldcp->id,
43531ae08745Sheppo 	    "ldc_mem_copy: (0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n",
43541ae08745Sheppo 	    ldcp->id, vaddr, local_valign, local_voff, npages);
43551ae08745Sheppo 
43561ae08745Sheppo 	local_ra = va_to_pa((void *)local_valign);
43571ae08745Sheppo 	local_poff = local_voff;
43581ae08745Sheppo 	local_psize = min(len, (pg_size - local_voff));
43591ae08745Sheppo 
43601ae08745Sheppo 	len -= local_psize;
43611ae08745Sheppo 
43621ae08745Sheppo 	/*
43631ae08745Sheppo 	 * find the first cookie in the list of cookies
43641ae08745Sheppo 	 * if the offset passed in is not zero
43651ae08745Sheppo 	 */
43661ae08745Sheppo 	for (idx = 0; idx < ccount; idx++) {
43671ae08745Sheppo 		cookie_size = cookies[idx].size;
43681ae08745Sheppo 		if (off < cookie_size)
43691ae08745Sheppo 			break;
43701ae08745Sheppo 		off -= cookie_size;
43711ae08745Sheppo 	}
43721ae08745Sheppo 
43731ae08745Sheppo 	cookie_addr = cookies[idx].addr + off;
43741ae08745Sheppo 	cookie_size = cookies[idx].size - off;
43751ae08745Sheppo 
43761ae08745Sheppo 	export_caddr = cookie_addr & ~(pg_size - 1);
43771ae08745Sheppo 	export_poff = cookie_addr & (pg_size - 1);
43781ae08745Sheppo 	export_psize = min(cookie_size, (pg_size - export_poff));
43791ae08745Sheppo 
43801ae08745Sheppo 	for (;;) {
43811ae08745Sheppo 
43821ae08745Sheppo 		copy_size = min(export_psize, local_psize);
43831ae08745Sheppo 
43841ae08745Sheppo 		D1(ldcp->id,
43851ae08745Sheppo 		    "ldc_mem_copy:(0x%llx) dir=0x%x, caddr=0x%llx,"
43861ae08745Sheppo 		    " loc_ra=0x%llx, exp_poff=0x%llx, loc_poff=0x%llx,"
43871ae08745Sheppo 		    " exp_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
43881ae08745Sheppo 		    " total_bal=0x%llx\n",
43891ae08745Sheppo 		    ldcp->id, direction, export_caddr, local_ra, export_poff,
43901ae08745Sheppo 		    local_poff, export_psize, local_psize, copy_size,
43911ae08745Sheppo 		    total_bal);
43921ae08745Sheppo 
43931ae08745Sheppo 		rv = hv_ldc_copy(ldcp->id, direction,
43941ae08745Sheppo 		    (export_caddr + export_poff), (local_ra + local_poff),
43951ae08745Sheppo 		    copy_size, &copied_len);
43961ae08745Sheppo 
43971ae08745Sheppo 		if (rv != 0) {
43981ae08745Sheppo 			cmn_err(CE_WARN,
43991ae08745Sheppo 			    "ldc_mem_copy: (0x%lx) err %d during copy\n",
44001ae08745Sheppo 			    ldcp->id, rv);
44011ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
44021ae08745Sheppo 			    "ldc_mem_copy: (0x%llx) dir=0x%x, caddr=0x%llx, "
44031ae08745Sheppo 			    "loc_ra=0x%llx, exp_poff=0x%llx, loc_poff=0x%llx,"
44041ae08745Sheppo 			    " exp_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
44051ae08745Sheppo 			    " copied_len=0x%llx, total_bal=0x%llx\n",
44061ae08745Sheppo 			    ldcp->id, direction, export_caddr, local_ra,
44071ae08745Sheppo 			    export_poff, local_poff, export_psize, local_psize,
44081ae08745Sheppo 			    copy_size, copied_len, total_bal);
44091ae08745Sheppo 
44101ae08745Sheppo 			*size = *size - total_bal;
44111ae08745Sheppo 			mutex_exit(&ldcp->lock);
44121ae08745Sheppo 			return (EIO);
44131ae08745Sheppo 		}
44141ae08745Sheppo 
44151ae08745Sheppo 		ASSERT(copied_len <= copy_size);
44161ae08745Sheppo 
44171ae08745Sheppo 		D2(ldcp->id, "ldc_mem_copy: copied=0x%llx\n", copied_len);
44181ae08745Sheppo 		export_poff += copied_len;
44191ae08745Sheppo 		local_poff += copied_len;
44201ae08745Sheppo 		export_psize -= copied_len;
44211ae08745Sheppo 		local_psize -= copied_len;
44221ae08745Sheppo 		cookie_size -= copied_len;
44231ae08745Sheppo 
44241ae08745Sheppo 		total_bal -= copied_len;
44251ae08745Sheppo 
44261ae08745Sheppo 		if (copy_size != copied_len)
44271ae08745Sheppo 			continue;
44281ae08745Sheppo 
44291ae08745Sheppo 		if (export_psize == 0 && total_bal != 0) {
44301ae08745Sheppo 
44311ae08745Sheppo 			if (cookie_size == 0) {
44321ae08745Sheppo 				idx++;
44331ae08745Sheppo 				cookie_addr = cookies[idx].addr;
44341ae08745Sheppo 				cookie_size = cookies[idx].size;
44351ae08745Sheppo 
44361ae08745Sheppo 				export_caddr = cookie_addr & ~(pg_size - 1);
44371ae08745Sheppo 				export_poff = cookie_addr & (pg_size - 1);
44381ae08745Sheppo 				export_psize =
44391ae08745Sheppo 					min(cookie_size, (pg_size-export_poff));
44401ae08745Sheppo 			} else {
44411ae08745Sheppo 				export_caddr += pg_size;
44421ae08745Sheppo 				export_poff = 0;
44431ae08745Sheppo 				export_psize = min(cookie_size, pg_size);
44441ae08745Sheppo 			}
44451ae08745Sheppo 		}
44461ae08745Sheppo 
44471ae08745Sheppo 		if (local_psize == 0 && total_bal != 0) {
44481ae08745Sheppo 			local_valign += pg_size;
44491ae08745Sheppo 			local_ra = va_to_pa((void *)local_valign);
44501ae08745Sheppo 			local_poff = 0;
44511ae08745Sheppo 			local_psize = min(pg_size, len);
44521ae08745Sheppo 			len -= local_psize;
44531ae08745Sheppo 		}
44541ae08745Sheppo 
44551ae08745Sheppo 		/* check if we are all done */
44561ae08745Sheppo 		if (total_bal == 0)
44571ae08745Sheppo 			break;
44581ae08745Sheppo 	}
44591ae08745Sheppo 
44601ae08745Sheppo 	mutex_exit(&ldcp->lock);
44611ae08745Sheppo 
44621ae08745Sheppo 	D1(ldcp->id,
44631ae08745Sheppo 	    "ldc_mem_copy: (0x%llx) done copying sz=0x%llx\n",
44641ae08745Sheppo 	    ldcp->id, *size);
44651ae08745Sheppo 
44661ae08745Sheppo 	return (0);
44671ae08745Sheppo }
44681ae08745Sheppo 
44691ae08745Sheppo /*
44701ae08745Sheppo  * Copy data either from or to the client specified virtual address
44711ae08745Sheppo  * space to or from HV physical memory.
44721ae08745Sheppo  *
44731ae08745Sheppo  * The direction argument determines whether the data is read from or
44741ae08745Sheppo  * written to HV memory. direction values are LDC_COPY_IN/OUT similar
44751ae08745Sheppo  * to the ldc_mem_copy interface
44761ae08745Sheppo  */
44771ae08745Sheppo int
44781ae08745Sheppo ldc_mem_rdwr_pa(ldc_handle_t handle, caddr_t vaddr, size_t *size,
44791ae08745Sheppo     caddr_t paddr, uint8_t direction)
44801ae08745Sheppo {
44811ae08745Sheppo 	ldc_chan_t 	*ldcp;
44821ae08745Sheppo 	uint64_t	local_voff, local_valign;
44831ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code;
44841ae08745Sheppo 	uint64_t 	target_pa, target_poff, target_psize, target_size;
44851ae08745Sheppo 	uint64_t	local_ra, local_poff, local_psize;
44861ae08745Sheppo 	uint64_t	copy_size, copied_len = 0;
44871ae08745Sheppo 	pgcnt_t		npages;
44881ae08745Sheppo 	size_t		len = *size;
44891ae08745Sheppo 	int 		rv = 0;
44901ae08745Sheppo 
44911ae08745Sheppo 	if (handle == NULL) {
44921ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
44931ae08745Sheppo 		    "ldc_mem_rdwr_pa: invalid channel handle\n");
44941ae08745Sheppo 		return (EINVAL);
44951ae08745Sheppo 	}
44961ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
44971ae08745Sheppo 
44981ae08745Sheppo 	mutex_enter(&ldcp->lock);
44991ae08745Sheppo 
45001ae08745Sheppo 	/* check to see if channel is UP */
45011ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
45021ae08745Sheppo 		DWARN(ldcp->id,
45031ae08745Sheppo 		    "ldc_mem_rdwr_pa: (0x%llx) channel is not UP\n",
45041ae08745Sheppo 		    ldcp->id);
45051ae08745Sheppo 		mutex_exit(&ldcp->lock);
45061ae08745Sheppo 		return (EINVAL);
45071ae08745Sheppo 	}
45081ae08745Sheppo 
45091ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
45101ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
45111ae08745Sheppo 		DWARN(ldcp->id,
45121ae08745Sheppo 		    "ldc_mem_rdwr_pa: addr/size is not 8-byte aligned\n");
45131ae08745Sheppo 		mutex_exit(&ldcp->lock);
45141ae08745Sheppo 		return (EINVAL);
45151ae08745Sheppo 	}
45161ae08745Sheppo 
45171ae08745Sheppo 	target_size = *size;
45181ae08745Sheppo 
45191ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
45201ae08745Sheppo 	pg_size = MMU_PAGESIZE;
45211ae08745Sheppo 	pg_size_code = page_szc(pg_size);
45221ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
45231ae08745Sheppo 
45241ae08745Sheppo 	D1(ldcp->id, "ldc_mem_rdwr_pa: copying data "
45251ae08745Sheppo 	    "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
45261ae08745Sheppo 	    ldcp->id, vaddr, pg_size, pg_size_code, pg_shift);
45271ae08745Sheppo 
45281ae08745Sheppo 	/* aligned VA and its offset */
45291ae08745Sheppo 	local_valign = ((uintptr_t)vaddr) & ~(pg_size - 1);
45301ae08745Sheppo 	local_voff = ((uintptr_t)vaddr) & (pg_size - 1);
45311ae08745Sheppo 
45321ae08745Sheppo 	npages = (len + local_voff) / pg_size;
45331ae08745Sheppo 	npages = ((len + local_voff) % pg_size == 0) ? npages : npages+1;
45341ae08745Sheppo 
45351ae08745Sheppo 	D1(ldcp->id,
45361ae08745Sheppo 	    "ldc_mem_rdwr_pa: (0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n",
45371ae08745Sheppo 	    ldcp->id, vaddr, local_valign, local_voff, npages);
45381ae08745Sheppo 
45391ae08745Sheppo 	local_ra = va_to_pa((void *)local_valign);
45401ae08745Sheppo 	local_poff = local_voff;
45411ae08745Sheppo 	local_psize = min(len, (pg_size - local_voff));
45421ae08745Sheppo 
45431ae08745Sheppo 	len -= local_psize;
45441ae08745Sheppo 
45451ae08745Sheppo 	target_pa = ((uintptr_t)paddr) & ~(pg_size - 1);
45461ae08745Sheppo 	target_poff = ((uintptr_t)paddr) & (pg_size - 1);
45471ae08745Sheppo 	target_psize = pg_size - target_poff;
45481ae08745Sheppo 
45491ae08745Sheppo 	for (;;) {
45501ae08745Sheppo 
45511ae08745Sheppo 		copy_size = min(target_psize, local_psize);
45521ae08745Sheppo 
45531ae08745Sheppo 		D1(ldcp->id,
45541ae08745Sheppo 		    "ldc_mem_rdwr_pa: (0x%llx) dir=0x%x, tar_pa=0x%llx,"
45551ae08745Sheppo 		    " loc_ra=0x%llx, tar_poff=0x%llx, loc_poff=0x%llx,"
45561ae08745Sheppo 		    " tar_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
45571ae08745Sheppo 		    " total_bal=0x%llx\n",
45581ae08745Sheppo 		    ldcp->id, direction, target_pa, local_ra, target_poff,
45591ae08745Sheppo 		    local_poff, target_psize, local_psize, copy_size,
45601ae08745Sheppo 		    target_size);
45611ae08745Sheppo 
45621ae08745Sheppo 		rv = hv_ldc_copy(ldcp->id, direction,
45631ae08745Sheppo 		    (target_pa + target_poff), (local_ra + local_poff),
45641ae08745Sheppo 		    copy_size, &copied_len);
45651ae08745Sheppo 
45661ae08745Sheppo 		if (rv != 0) {
45671ae08745Sheppo 			cmn_err(CE_WARN,
45681ae08745Sheppo 			    "ldc_mem_rdwr_pa: (0x%lx) err %d during copy\n",
45691ae08745Sheppo 			    ldcp->id, rv);
45701ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
45711ae08745Sheppo 			    "ldc_mem_rdwr_pa: (0x%llx) dir=%lld,tar_pa=0x%llx, "
45721ae08745Sheppo 			    "loc_ra=0x%llx, tar_poff=0x%llx, loc_poff=0x%llx,"
45731ae08745Sheppo 			    " tar_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
45741ae08745Sheppo 			    " total_bal=0x%llx\n",
45751ae08745Sheppo 			    ldcp->id, direction, target_pa, local_ra,
45761ae08745Sheppo 			    target_poff, local_poff, target_psize, local_psize,
45771ae08745Sheppo 			    copy_size, target_size);
45781ae08745Sheppo 
45791ae08745Sheppo 			*size = *size - target_size;
45801ae08745Sheppo 			mutex_exit(&ldcp->lock);
45811ae08745Sheppo 			return (i_ldc_h2v_error(rv));
45821ae08745Sheppo 		}
45831ae08745Sheppo 
45841ae08745Sheppo 		D2(ldcp->id, "ldc_mem_rdwr_pa: copied=0x%llx\n", copied_len);
45851ae08745Sheppo 		target_poff += copied_len;
45861ae08745Sheppo 		local_poff += copied_len;
45871ae08745Sheppo 		target_psize -= copied_len;
45881ae08745Sheppo 		local_psize -= copied_len;
45891ae08745Sheppo 
45901ae08745Sheppo 		target_size -= copied_len;
45911ae08745Sheppo 
45921ae08745Sheppo 		if (copy_size != copied_len)
45931ae08745Sheppo 			continue;
45941ae08745Sheppo 
45951ae08745Sheppo 		if (target_psize == 0 && target_size != 0) {
45961ae08745Sheppo 			target_pa += pg_size;
45971ae08745Sheppo 			target_poff = 0;
45981ae08745Sheppo 			target_psize = min(pg_size, target_size);
45991ae08745Sheppo 		}
46001ae08745Sheppo 
46011ae08745Sheppo 		if (local_psize == 0 && target_size != 0) {
46021ae08745Sheppo 			local_valign += pg_size;
46031ae08745Sheppo 			local_ra = va_to_pa((void *)local_valign);
46041ae08745Sheppo 			local_poff = 0;
46051ae08745Sheppo 			local_psize = min(pg_size, len);
46061ae08745Sheppo 			len -= local_psize;
46071ae08745Sheppo 		}
46081ae08745Sheppo 
46091ae08745Sheppo 		/* check if we are all done */
46101ae08745Sheppo 		if (target_size == 0)
46111ae08745Sheppo 			break;
46121ae08745Sheppo 	}
46131ae08745Sheppo 
46141ae08745Sheppo 	mutex_exit(&ldcp->lock);
46151ae08745Sheppo 
46161ae08745Sheppo 	D1(ldcp->id, "ldc_mem_rdwr_pa: (0x%llx) done copying sz=0x%llx\n",
46171ae08745Sheppo 	    ldcp->id, *size);
46181ae08745Sheppo 
46191ae08745Sheppo 	return (0);
46201ae08745Sheppo }
46211ae08745Sheppo 
46221ae08745Sheppo /*
46231ae08745Sheppo  * Map an exported memory segment into the local address space. If the
46241ae08745Sheppo  * memory range was exported for direct map access, a HV call is made
46251ae08745Sheppo  * to allocate a RA range. If the map is done via a shadow copy, local
46261ae08745Sheppo  * shadow memory is allocated and the base VA is returned in 'vaddr'. If
46271ae08745Sheppo  * the mapping is a direct map then the RA is returned in 'raddr'.
46281ae08745Sheppo  */
46291ae08745Sheppo int
46301ae08745Sheppo ldc_mem_map(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie, uint32_t ccount,
46311ae08745Sheppo     uint8_t mtype, caddr_t *vaddr, caddr_t *raddr)
46321ae08745Sheppo {
46331ae08745Sheppo 	int		i, idx;
46341ae08745Sheppo 	ldc_chan_t 	*ldcp;
46351ae08745Sheppo 	ldc_mhdl_t	*mhdl;
46361ae08745Sheppo 	ldc_memseg_t	*memseg;
46371ae08745Sheppo 	caddr_t		shadow_base = NULL, tmpaddr;
46381ae08745Sheppo 	uint64_t	pg_size, pg_shift, pg_size_code;
46391ae08745Sheppo 	uint64_t	exp_size = 0, npages;
46401ae08745Sheppo 
46411ae08745Sheppo 	if (mhandle == NULL) {
46421ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_map: invalid memory handle\n");
46431ae08745Sheppo 		return (EINVAL);
46441ae08745Sheppo 	}
46451ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
46461ae08745Sheppo 
46471ae08745Sheppo 	mutex_enter(&mhdl->lock);
46481ae08745Sheppo 
46491ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED ||
46501ae08745Sheppo 	    mhdl->memseg != NULL) {
46511ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
46521ae08745Sheppo 		    "ldc_mem_map: (0x%llx) handle bound/mapped\n", mhandle);
46531ae08745Sheppo 		mutex_exit(&mhdl->lock);
46541ae08745Sheppo 		return (EINVAL);
46551ae08745Sheppo 	}
46561ae08745Sheppo 
46571ae08745Sheppo 	ldcp = mhdl->ldcp;
46581ae08745Sheppo 
46591ae08745Sheppo 	mutex_enter(&ldcp->lock);
46601ae08745Sheppo 
46611ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
46621ae08745Sheppo 		DWARN(ldcp->id,
46631ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) channel is not UP\n",
46641ae08745Sheppo 		    ldcp->id);
46651ae08745Sheppo 		mutex_exit(&ldcp->lock);
46661ae08745Sheppo 		mutex_exit(&mhdl->lock);
46671ae08745Sheppo 		return (EINVAL);
46681ae08745Sheppo 	}
46691ae08745Sheppo 
46701ae08745Sheppo 	if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) {
46711ae08745Sheppo 		DWARN(ldcp->id, "ldc_mem_map: invalid map type\n");
46721ae08745Sheppo 		mutex_exit(&ldcp->lock);
46731ae08745Sheppo 		mutex_exit(&mhdl->lock);
46741ae08745Sheppo 		return (EINVAL);
46751ae08745Sheppo 	}
46761ae08745Sheppo 
46771ae08745Sheppo 	if (mtype == LDC_SHADOW_MAP && vaddr == NULL) {
46781ae08745Sheppo 		DWARN(ldcp->id,
46791ae08745Sheppo 		    "ldc_mem_map: invalid vaddr arg0x%llx\n", vaddr);
46801ae08745Sheppo 		mutex_exit(&ldcp->lock);
46811ae08745Sheppo 		mutex_exit(&mhdl->lock);
46821ae08745Sheppo 		return (EINVAL);
46831ae08745Sheppo 	}
46841ae08745Sheppo 
46851ae08745Sheppo 	if (mtype == LDC_SHADOW_MAP &&
46861ae08745Sheppo 	    (vaddr) && ((uintptr_t)(*vaddr) & MMU_PAGEOFFSET)) {
46871ae08745Sheppo 		DWARN(ldcp->id,
46881ae08745Sheppo 		    "ldc_mem_map: vaddr not page aligned, 0x%llx\n", *vaddr);
46891ae08745Sheppo 		mutex_exit(&ldcp->lock);
46901ae08745Sheppo 		mutex_exit(&mhdl->lock);
46911ae08745Sheppo 		return (EINVAL);
46921ae08745Sheppo 	}
46931ae08745Sheppo 
46941ae08745Sheppo 	D1(ldcp->id, "ldc_mem_map: (0x%llx) cookie = 0x%llx,0x%llx\n",
46951ae08745Sheppo 	    mhandle, cookie->addr, cookie->size);
46961ae08745Sheppo 
46971ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
46981ae08745Sheppo 	pg_size = MMU_PAGESIZE;
46991ae08745Sheppo 	pg_size_code = page_szc(pg_size);
47001ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
47011ae08745Sheppo 
47021ae08745Sheppo 	/* calculate the number of pages in the exported cookie */
47031ae08745Sheppo 	for (idx = 0; idx < ccount; idx++) {
47041ae08745Sheppo 		if (cookie[idx].addr & MMU_PAGEOFFSET ||
47051ae08745Sheppo 			cookie[idx].size & MMU_PAGEOFFSET) {
47061ae08745Sheppo 			DWARN(ldcp->id,
47071ae08745Sheppo 			    "ldc_mem_map: cookie addr/size not page aligned, "
47081ae08745Sheppo 			    "0x%llx\n", cookie[idx].addr);
47091ae08745Sheppo 			mutex_exit(&ldcp->lock);
47101ae08745Sheppo 			mutex_exit(&mhdl->lock);
47111ae08745Sheppo 			return (EINVAL);
47121ae08745Sheppo 		}
47131ae08745Sheppo 		exp_size += cookie[idx].size;
47141ae08745Sheppo 	}
47151ae08745Sheppo 	npages = (exp_size >> pg_shift);
47161ae08745Sheppo 
47171ae08745Sheppo 	/* Allocate memseg structure */
47181ae08745Sheppo 	memseg = mhdl->memseg =	kmem_zalloc(sizeof (ldc_memseg_t), KM_SLEEP);
47191ae08745Sheppo 
47201ae08745Sheppo 	/* Allocate memory to store all pages and cookies */
47211ae08745Sheppo 	memseg->pages =	kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP);
47221ae08745Sheppo 	memseg->cookies =
47231ae08745Sheppo 		kmem_zalloc((sizeof (ldc_mem_cookie_t) * ccount), KM_SLEEP);
47241ae08745Sheppo 
47251ae08745Sheppo 	D2(ldcp->id, "ldc_mem_map: (0x%llx) processing 0x%llx pages\n",
47261ae08745Sheppo 	    ldcp->id, npages);
47271ae08745Sheppo 
47281ae08745Sheppo 	/* Check to see if the client is requesting direct or shadow map */
47291ae08745Sheppo 	if (mtype == LDC_SHADOW_MAP) {
47301ae08745Sheppo 		if (*vaddr == NULL) {
47311ae08745Sheppo 			shadow_base =
47321ae08745Sheppo 				contig_mem_alloc_align(exp_size, PAGESIZE);
47331ae08745Sheppo 			if (shadow_base == NULL) {
47341ae08745Sheppo 				cmn_err(CE_WARN, "ldc_mem_map: shadow memory "
47351ae08745Sheppo 				    "allocation failed\n");
47361ae08745Sheppo 				kmem_free(memseg->cookies,
47371ae08745Sheppo 				    (sizeof (ldc_mem_cookie_t) * ccount));
47381ae08745Sheppo 				kmem_free(memseg->pages,
47391ae08745Sheppo 				    (sizeof (ldc_page_t) * npages));
47401ae08745Sheppo 				kmem_free(memseg, sizeof (ldc_memseg_t));
47411ae08745Sheppo 				mutex_exit(&ldcp->lock);
47421ae08745Sheppo 				mutex_exit(&mhdl->lock);
47431ae08745Sheppo 				return (ENOMEM);
47441ae08745Sheppo 			}
47451ae08745Sheppo 
47461ae08745Sheppo 			bzero(shadow_base, exp_size);
47471ae08745Sheppo 			mhdl->myshadow = B_TRUE;
47481ae08745Sheppo 
47491ae08745Sheppo 			D1(ldcp->id, "ldc_mem_map: (0x%llx) allocated "
47501ae08745Sheppo 			    "shadow page va=0x%llx\n", ldcp->id, shadow_base);
47511ae08745Sheppo 		} else {
47521ae08745Sheppo 			/*
47531ae08745Sheppo 			 * Use client supplied memory for shadow_base
47541ae08745Sheppo 			 * WARNING: assuming that client mem is >= exp_size
47551ae08745Sheppo 			 */
47561ae08745Sheppo 			shadow_base = *vaddr;
47571ae08745Sheppo 		}
47581ae08745Sheppo 	} else if (mtype == LDC_DIRECT_MAP) {
47591ae08745Sheppo 		/* FUTURE: Do a direct map by calling into HV */
47601ae08745Sheppo 		_NOTE(EMPTY)
47611ae08745Sheppo 	}
47621ae08745Sheppo 
47631ae08745Sheppo 	/* Save all page and cookie information */
47641ae08745Sheppo 	for (i = 0, tmpaddr = shadow_base; i < npages; i++) {
47651ae08745Sheppo 		memseg->pages[i].raddr = va_to_pa(tmpaddr);
47661ae08745Sheppo 		memseg->pages[i].size = pg_size;
47671ae08745Sheppo 		memseg->pages[i].index = 0;
47681ae08745Sheppo 		memseg->pages[i].offset = 0;
47691ae08745Sheppo 		memseg->pages[i].mte = NULL;
47701ae08745Sheppo 		tmpaddr += pg_size;
47711ae08745Sheppo 	}
47721ae08745Sheppo 	for (i = 0; i < ccount; i++) {
47731ae08745Sheppo 		memseg->cookies[i].addr = cookie[i].addr;
47741ae08745Sheppo 		memseg->cookies[i].size = cookie[i].size;
47751ae08745Sheppo 	}
47761ae08745Sheppo 
47771ae08745Sheppo 	/* update memseg_t */
47781ae08745Sheppo 	memseg->vaddr = shadow_base;
47791ae08745Sheppo 	memseg->raddr = memseg->pages[0].raddr;
47801ae08745Sheppo 	memseg->size = exp_size;
47811ae08745Sheppo 	memseg->npages = npages;
47821ae08745Sheppo 	memseg->ncookies = ccount;
47831ae08745Sheppo 	memseg->next_cookie = 0;
47841ae08745Sheppo 
47851ae08745Sheppo 	/* memory handle = mapped */
47861ae08745Sheppo 	mhdl->mtype = mtype;
47871ae08745Sheppo 	mhdl->perm = 0;
47881ae08745Sheppo 	mhdl->status = LDC_MAPPED;
47891ae08745Sheppo 
47901ae08745Sheppo 	D1(ldcp->id, "ldc_mem_map: (0x%llx) mapped 0x%llx, ra=0x%llx, "
47911ae08745Sheppo 	    "va=0x%llx, pgs=0x%llx cookies=0x%llx\n",
47921ae08745Sheppo 	    ldcp->id, mhdl, memseg->raddr, memseg->vaddr,
47931ae08745Sheppo 	    memseg->npages, memseg->ncookies);
47941ae08745Sheppo 
47951ae08745Sheppo 	if (raddr)
47961ae08745Sheppo 		*raddr = (caddr_t)memseg->raddr;
47971ae08745Sheppo 	if (vaddr)
47981ae08745Sheppo 		*vaddr = memseg->vaddr;
47991ae08745Sheppo 
48001ae08745Sheppo 	mutex_exit(&ldcp->lock);
48011ae08745Sheppo 	mutex_exit(&mhdl->lock);
48021ae08745Sheppo 	return (0);
48031ae08745Sheppo }
48041ae08745Sheppo 
48051ae08745Sheppo /*
48061ae08745Sheppo  * Unmap a memory segment. Free shadow memory (if any).
48071ae08745Sheppo  */
48081ae08745Sheppo int
48091ae08745Sheppo ldc_mem_unmap(ldc_mem_handle_t mhandle)
48101ae08745Sheppo {
48111ae08745Sheppo 	ldc_mhdl_t	*mhdl = (ldc_mhdl_t *)mhandle;
48121ae08745Sheppo 	ldc_chan_t 	*ldcp;
48131ae08745Sheppo 	ldc_memseg_t	*memseg;
48141ae08745Sheppo 
48151ae08745Sheppo 	if (mhdl == 0 || mhdl->status != LDC_MAPPED) {
48161ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
48171ae08745Sheppo 		    "ldc_mem_unmap: (0x%llx) handle is not mapped\n",
48181ae08745Sheppo 		    mhandle);
48191ae08745Sheppo 		return (EINVAL);
48201ae08745Sheppo 	}
48211ae08745Sheppo 
48221ae08745Sheppo 	mutex_enter(&mhdl->lock);
48231ae08745Sheppo 
48241ae08745Sheppo 	ldcp = mhdl->ldcp;
48251ae08745Sheppo 	memseg = mhdl->memseg;
48261ae08745Sheppo 
48271ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapping handle 0x%llx\n",
48281ae08745Sheppo 	    ldcp->id, mhdl);
48291ae08745Sheppo 
48301ae08745Sheppo 	/* if we allocated shadow memory - free it */
48311ae08745Sheppo 	if (mhdl->mtype == LDC_SHADOW_MAP && mhdl->myshadow) {
48321ae08745Sheppo 		contig_mem_free(memseg->vaddr, memseg->size);
48331ae08745Sheppo 	}
48341ae08745Sheppo 
48351ae08745Sheppo 	/* free the allocated memseg and page structures */
48361ae08745Sheppo 	kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages));
48371ae08745Sheppo 	kmem_free(memseg->cookies,
48381ae08745Sheppo 	    (sizeof (ldc_mem_cookie_t) * memseg->ncookies));
48391ae08745Sheppo 	kmem_free(memseg, sizeof (ldc_memseg_t));
48401ae08745Sheppo 
48411ae08745Sheppo 	/* uninitialize the memory handle */
48421ae08745Sheppo 	mhdl->memseg = NULL;
48431ae08745Sheppo 	mhdl->status = LDC_UNBOUND;
48441ae08745Sheppo 
48451ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapped handle 0x%llx\n",
48461ae08745Sheppo 	    ldcp->id, mhdl);
48471ae08745Sheppo 
48481ae08745Sheppo 	mutex_exit(&mhdl->lock);
48491ae08745Sheppo 	return (0);
48501ae08745Sheppo }
48511ae08745Sheppo 
48521ae08745Sheppo /*
48531ae08745Sheppo  * Internal entry point for LDC mapped memory entry consistency
48541ae08745Sheppo  * semantics. Acquire copies the contents of the remote memory
48551ae08745Sheppo  * into the local shadow copy. The release operation copies the local
48561ae08745Sheppo  * contents into the remote memory. The offset and size specify the
48571ae08745Sheppo  * bounds for the memory range being synchronized.
48581ae08745Sheppo  */
48591ae08745Sheppo static int
48601ae08745Sheppo i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle, uint8_t direction,
48611ae08745Sheppo     uint64_t offset, size_t size)
48621ae08745Sheppo {
48631ae08745Sheppo 	int 		err;
48641ae08745Sheppo 	ldc_mhdl_t	*mhdl;
48651ae08745Sheppo 	ldc_chan_t	*ldcp;
48661ae08745Sheppo 	ldc_memseg_t	*memseg;
48671ae08745Sheppo 	caddr_t		local_vaddr;
48681ae08745Sheppo 	size_t		copy_size;
48691ae08745Sheppo 
48701ae08745Sheppo 	if (mhandle == NULL) {
48711ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
48721ae08745Sheppo 		    "i_ldc_mem_acquire_release: invalid memory handle\n");
48731ae08745Sheppo 		return (EINVAL);
48741ae08745Sheppo 	}
48751ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
48761ae08745Sheppo 
48771ae08745Sheppo 	mutex_enter(&mhdl->lock);
48781ae08745Sheppo 
48791ae08745Sheppo 	if (mhdl->status != LDC_MAPPED || mhdl->ldcp == NULL) {
48801ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
48811ae08745Sheppo 		    "i_ldc_mem_acquire_release: not mapped memory\n");
48821ae08745Sheppo 		mutex_exit(&mhdl->lock);
48831ae08745Sheppo 		return (EINVAL);
48841ae08745Sheppo 	}
48851ae08745Sheppo 
48861ae08745Sheppo 	if (offset >= mhdl->memseg->size ||
48871ae08745Sheppo 	    (offset + size) > mhdl->memseg->size) {
48881ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
48891ae08745Sheppo 		    "i_ldc_mem_acquire_release: memory out of range\n");
48901ae08745Sheppo 		mutex_exit(&mhdl->lock);
48911ae08745Sheppo 		return (EINVAL);
48921ae08745Sheppo 	}
48931ae08745Sheppo 
48941ae08745Sheppo 	/* get the channel handle and memory segment */
48951ae08745Sheppo 	ldcp = mhdl->ldcp;
48961ae08745Sheppo 	memseg = mhdl->memseg;
48971ae08745Sheppo 
48981ae08745Sheppo 	if (mhdl->mtype == LDC_SHADOW_MAP) {
48991ae08745Sheppo 
49001ae08745Sheppo 		local_vaddr = memseg->vaddr + offset;
49011ae08745Sheppo 		copy_size = size;
49021ae08745Sheppo 
49031ae08745Sheppo 		/* copy to/from remote from/to local memory */
49041ae08745Sheppo 		err = ldc_mem_copy((ldc_handle_t)ldcp, local_vaddr, offset,
49051ae08745Sheppo 		    &copy_size, memseg->cookies, memseg->ncookies,
49061ae08745Sheppo 		    direction);
49071ae08745Sheppo 		if (err || copy_size != size) {
49081ae08745Sheppo 			cmn_err(CE_WARN,
49091ae08745Sheppo 			    "i_ldc_mem_acquire_release: copy failed\n");
49101ae08745Sheppo 			mutex_exit(&mhdl->lock);
49111ae08745Sheppo 			return (err);
49121ae08745Sheppo 		}
49131ae08745Sheppo 	}
49141ae08745Sheppo 
49151ae08745Sheppo 	mutex_exit(&mhdl->lock);
49161ae08745Sheppo 
49171ae08745Sheppo 	return (0);
49181ae08745Sheppo }
49191ae08745Sheppo 
49201ae08745Sheppo /*
49211ae08745Sheppo  * Ensure that the contents in the remote memory seg are consistent
49221ae08745Sheppo  * with the contents if of local segment
49231ae08745Sheppo  */
49241ae08745Sheppo int
49251ae08745Sheppo ldc_mem_acquire(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size)
49261ae08745Sheppo {
49271ae08745Sheppo 	return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_IN, offset, size));
49281ae08745Sheppo }
49291ae08745Sheppo 
49301ae08745Sheppo 
49311ae08745Sheppo /*
49321ae08745Sheppo  * Ensure that the contents in the local memory seg are consistent
49331ae08745Sheppo  * with the contents if of remote segment
49341ae08745Sheppo  */
49351ae08745Sheppo int
49361ae08745Sheppo ldc_mem_release(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size)
49371ae08745Sheppo {
49381ae08745Sheppo 	return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_OUT, offset, size));
49391ae08745Sheppo }
49401ae08745Sheppo 
49411ae08745Sheppo /*
49421ae08745Sheppo  * Allocate a descriptor ring. The size of each each descriptor
49431ae08745Sheppo  * must be 8-byte aligned and the entire ring should be a multiple
49441ae08745Sheppo  * of MMU_PAGESIZE.
49451ae08745Sheppo  */
49461ae08745Sheppo int
49471ae08745Sheppo ldc_mem_dring_create(uint32_t len, uint32_t dsize, ldc_dring_handle_t *dhandle)
49481ae08745Sheppo {
49491ae08745Sheppo 	ldc_dring_t *dringp;
49501ae08745Sheppo 	size_t size = (dsize * len);
49511ae08745Sheppo 
49521ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_create: len=0x%x, size=0x%x\n",
49531ae08745Sheppo 	    len, dsize);
49541ae08745Sheppo 
49551ae08745Sheppo 	if (dhandle == NULL) {
49561ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid dhandle\n");
49571ae08745Sheppo 		return (EINVAL);
49581ae08745Sheppo 	}
49591ae08745Sheppo 
49601ae08745Sheppo 	if (len == 0) {
49611ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid length\n");
49621ae08745Sheppo 		return (EINVAL);
49631ae08745Sheppo 	}
49641ae08745Sheppo 
49651ae08745Sheppo 	/* descriptor size should be 8-byte aligned */
49661ae08745Sheppo 	if (dsize == 0 || (dsize & 0x7)) {
49671ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid size\n");
49681ae08745Sheppo 		return (EINVAL);
49691ae08745Sheppo 	}
49701ae08745Sheppo 
49711ae08745Sheppo 	*dhandle = 0;
49721ae08745Sheppo 
49731ae08745Sheppo 	/* Allocate a desc ring structure */
49741ae08745Sheppo 	dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP);
49751ae08745Sheppo 
49761ae08745Sheppo 	/* Initialize dring */
49771ae08745Sheppo 	dringp->length = len;
49781ae08745Sheppo 	dringp->dsize = dsize;
49791ae08745Sheppo 
49801ae08745Sheppo 	/* round off to multiple of pagesize */
49811ae08745Sheppo 	dringp->size = (size & MMU_PAGEMASK);
49821ae08745Sheppo 	if (size & MMU_PAGEOFFSET)
49831ae08745Sheppo 		dringp->size += MMU_PAGESIZE;
49841ae08745Sheppo 
49851ae08745Sheppo 	dringp->status = LDC_UNBOUND;
49861ae08745Sheppo 
49871ae08745Sheppo 	/* allocate descriptor ring memory */
49881ae08745Sheppo 	dringp->base = contig_mem_alloc_align(dringp->size, PAGESIZE);
49891ae08745Sheppo 	if (dringp->base == NULL) {
49901ae08745Sheppo 		cmn_err(CE_WARN,
49911ae08745Sheppo 		    "ldc_mem_dring_create: unable to alloc desc\n");
49921ae08745Sheppo 		kmem_free(dringp, sizeof (ldc_dring_t));
49931ae08745Sheppo 		return (ENOMEM);
49941ae08745Sheppo 	}
49951ae08745Sheppo 
49961ae08745Sheppo 	bzero(dringp->base, dringp->size);
49971ae08745Sheppo 
49981ae08745Sheppo 	/* initialize the desc ring lock */
49991ae08745Sheppo 	mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL);
50001ae08745Sheppo 
50011ae08745Sheppo 	/* Add descriptor ring to the head of global list */
50021ae08745Sheppo 	mutex_enter(&ldcssp->lock);
50031ae08745Sheppo 	dringp->next = ldcssp->dring_list;
50041ae08745Sheppo 	ldcssp->dring_list = dringp;
50051ae08745Sheppo 	mutex_exit(&ldcssp->lock);
50061ae08745Sheppo 
50071ae08745Sheppo 	*dhandle = (ldc_dring_handle_t)dringp;
50081ae08745Sheppo 
50091ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_create: dring allocated\n");
50101ae08745Sheppo 
50111ae08745Sheppo 	return (0);
50121ae08745Sheppo }
50131ae08745Sheppo 
50141ae08745Sheppo 
50151ae08745Sheppo /*
50161ae08745Sheppo  * Destroy a descriptor ring.
50171ae08745Sheppo  */
50181ae08745Sheppo int
50191ae08745Sheppo ldc_mem_dring_destroy(ldc_dring_handle_t dhandle)
50201ae08745Sheppo {
50211ae08745Sheppo 	ldc_dring_t *dringp;
50221ae08745Sheppo 	ldc_dring_t *tmp_dringp;
50231ae08745Sheppo 
50241ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_destroy: entered\n");
50251ae08745Sheppo 
50261ae08745Sheppo 	if (dhandle == NULL) {
50271ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
50281ae08745Sheppo 		    "ldc_mem_dring_destroy: invalid desc ring handle\n");
50291ae08745Sheppo 		return (EINVAL);
50301ae08745Sheppo 	}
50311ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
50321ae08745Sheppo 
50331ae08745Sheppo 	if (dringp->status == LDC_BOUND) {
50341ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
50351ae08745Sheppo 		    "ldc_mem_dring_destroy: desc ring is bound\n");
50361ae08745Sheppo 		return (EACCES);
50371ae08745Sheppo 	}
50381ae08745Sheppo 
50391ae08745Sheppo 	mutex_enter(&dringp->lock);
50401ae08745Sheppo 	mutex_enter(&ldcssp->lock);
50411ae08745Sheppo 
50421ae08745Sheppo 	/* remove from linked list - if not bound */
50431ae08745Sheppo 	tmp_dringp = ldcssp->dring_list;
50441ae08745Sheppo 	if (tmp_dringp == dringp) {
50451ae08745Sheppo 		ldcssp->dring_list = dringp->next;
50461ae08745Sheppo 		dringp->next = NULL;
50471ae08745Sheppo 
50481ae08745Sheppo 	} else {
50491ae08745Sheppo 		while (tmp_dringp != NULL) {
50501ae08745Sheppo 			if (tmp_dringp->next == dringp) {
50511ae08745Sheppo 				tmp_dringp->next = dringp->next;
50521ae08745Sheppo 				dringp->next = NULL;
50531ae08745Sheppo 				break;
50541ae08745Sheppo 			}
50551ae08745Sheppo 			tmp_dringp = tmp_dringp->next;
50561ae08745Sheppo 		}
50571ae08745Sheppo 		if (tmp_dringp == NULL) {
50581ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
50591ae08745Sheppo 			    "ldc_mem_dring_destroy: invalid descriptor\n");
50601ae08745Sheppo 			mutex_exit(&ldcssp->lock);
50611ae08745Sheppo 			mutex_exit(&dringp->lock);
50621ae08745Sheppo 			return (EINVAL);
50631ae08745Sheppo 		}
50641ae08745Sheppo 	}
50651ae08745Sheppo 
50661ae08745Sheppo 	mutex_exit(&ldcssp->lock);
50671ae08745Sheppo 
50681ae08745Sheppo 	/* free the descriptor ring */
50691ae08745Sheppo 	contig_mem_free((caddr_t)dringp->base, dringp->size);
50701ae08745Sheppo 
50711ae08745Sheppo 	mutex_exit(&dringp->lock);
50721ae08745Sheppo 
50731ae08745Sheppo 	/* destroy dring lock */
50741ae08745Sheppo 	mutex_destroy(&dringp->lock);
50751ae08745Sheppo 
50761ae08745Sheppo 	/* free desc ring object */
50771ae08745Sheppo 	kmem_free(dringp, sizeof (ldc_dring_t));
50781ae08745Sheppo 
50791ae08745Sheppo 	return (0);
50801ae08745Sheppo }
50811ae08745Sheppo 
50821ae08745Sheppo /*
50831ae08745Sheppo  * Bind a previously allocated dring to a channel. The channel should
50841ae08745Sheppo  * be OPEN in order to bind the ring to the channel. Returns back a
50851ae08745Sheppo  * descriptor ring cookie. The descriptor ring is exported for remote
50861ae08745Sheppo  * access by the client at the other end of the channel. An entry for
50871ae08745Sheppo  * dring pages is stored in map table (via call to ldc_mem_bind_handle).
50881ae08745Sheppo  */
50891ae08745Sheppo int
50901ae08745Sheppo ldc_mem_dring_bind(ldc_handle_t handle, ldc_dring_handle_t dhandle,
50911ae08745Sheppo     uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount)
50921ae08745Sheppo {
50931ae08745Sheppo 	int		err;
50941ae08745Sheppo 	ldc_chan_t 	*ldcp;
50951ae08745Sheppo 	ldc_dring_t	*dringp;
50961ae08745Sheppo 	ldc_mem_handle_t mhandle;
50971ae08745Sheppo 
50981ae08745Sheppo 	/* check to see if channel is initalized */
50991ae08745Sheppo 	if (handle == NULL) {
51001ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51011ae08745Sheppo 		    "ldc_mem_dring_bind: invalid channel handle\n");
51021ae08745Sheppo 		return (EINVAL);
51031ae08745Sheppo 	}
51041ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
51051ae08745Sheppo 
51061ae08745Sheppo 	if (dhandle == NULL) {
51071ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51081ae08745Sheppo 		    "ldc_mem_dring_bind: invalid desc ring handle\n");
51091ae08745Sheppo 		return (EINVAL);
51101ae08745Sheppo 	}
51111ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
51121ae08745Sheppo 
51131ae08745Sheppo 	if (cookie == NULL) {
51141ae08745Sheppo 		DWARN(ldcp->id,
51151ae08745Sheppo 		    "ldc_mem_dring_bind: invalid cookie arg\n");
51161ae08745Sheppo 		return (EINVAL);
51171ae08745Sheppo 	}
51181ae08745Sheppo 
51191ae08745Sheppo 	mutex_enter(&dringp->lock);
51201ae08745Sheppo 
51211ae08745Sheppo 	if (dringp->status == LDC_BOUND) {
51221ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51231ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) descriptor ring is bound\n",
51241ae08745Sheppo 		    ldcp->id);
51251ae08745Sheppo 		mutex_exit(&dringp->lock);
51261ae08745Sheppo 		return (EINVAL);
51271ae08745Sheppo 	}
51281ae08745Sheppo 
51291ae08745Sheppo 	if ((perm & LDC_MEM_RW) == 0) {
51301ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51311ae08745Sheppo 		    "ldc_mem_dring_bind: invalid permissions\n");
51321ae08745Sheppo 		mutex_exit(&dringp->lock);
51331ae08745Sheppo 		return (EINVAL);
51341ae08745Sheppo 	}
51351ae08745Sheppo 
51361ae08745Sheppo 	if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) {
51371ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_bind: invalid type\n");
51381ae08745Sheppo 		mutex_exit(&dringp->lock);
51391ae08745Sheppo 		return (EINVAL);
51401ae08745Sheppo 	}
51411ae08745Sheppo 
51421ae08745Sheppo 	dringp->ldcp = ldcp;
51431ae08745Sheppo 
51441ae08745Sheppo 	/* create an memory handle */
51451ae08745Sheppo 	err = ldc_mem_alloc_handle(handle, &mhandle);
51461ae08745Sheppo 	if (err || mhandle == NULL) {
51471ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51481ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) error allocating mhandle\n",
51491ae08745Sheppo 		    ldcp->id);
51501ae08745Sheppo 		mutex_exit(&dringp->lock);
51511ae08745Sheppo 		return (err);
51521ae08745Sheppo 	}
51531ae08745Sheppo 	dringp->mhdl = mhandle;
51541ae08745Sheppo 
51551ae08745Sheppo 	/* bind the descriptor ring to channel */
51561ae08745Sheppo 	err = ldc_mem_bind_handle(mhandle, dringp->base, dringp->size,
51571ae08745Sheppo 	    mtype, perm, cookie, ccount);
51581ae08745Sheppo 	if (err) {
51591ae08745Sheppo 		DWARN(ldcp->id,
51601ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) error binding mhandle\n",
51611ae08745Sheppo 		    ldcp->id);
51621ae08745Sheppo 		mutex_exit(&dringp->lock);
51631ae08745Sheppo 		return (err);
51641ae08745Sheppo 	}
51651ae08745Sheppo 
51661ae08745Sheppo 	/*
51671ae08745Sheppo 	 * For now return error if we get more than one cookie
51681ae08745Sheppo 	 * FUTURE: Return multiple cookies ..
51691ae08745Sheppo 	 */
51701ae08745Sheppo 	if (*ccount > 1) {
51711ae08745Sheppo 		(void) ldc_mem_unbind_handle(mhandle);
51721ae08745Sheppo 		(void) ldc_mem_free_handle(mhandle);
51731ae08745Sheppo 
51741ae08745Sheppo 		dringp->ldcp = NULL;
51751ae08745Sheppo 		dringp->mhdl = NULL;
51761ae08745Sheppo 		*ccount = 0;
51771ae08745Sheppo 
51781ae08745Sheppo 		mutex_exit(&dringp->lock);
51791ae08745Sheppo 		return (EAGAIN);
51801ae08745Sheppo 	}
51811ae08745Sheppo 
51821ae08745Sheppo 	/* Add descriptor ring to channel's exported dring list */
51831ae08745Sheppo 	mutex_enter(&ldcp->exp_dlist_lock);
51841ae08745Sheppo 	dringp->ch_next = ldcp->exp_dring_list;
51851ae08745Sheppo 	ldcp->exp_dring_list = dringp;
51861ae08745Sheppo 	mutex_exit(&ldcp->exp_dlist_lock);
51871ae08745Sheppo 
51881ae08745Sheppo 	dringp->status = LDC_BOUND;
51891ae08745Sheppo 
51901ae08745Sheppo 	mutex_exit(&dringp->lock);
51911ae08745Sheppo 
51921ae08745Sheppo 	return (0);
51931ae08745Sheppo }
51941ae08745Sheppo 
51951ae08745Sheppo /*
51961ae08745Sheppo  * Return the next cookie associated with the specified dring handle
51971ae08745Sheppo  */
51981ae08745Sheppo int
51991ae08745Sheppo ldc_mem_dring_nextcookie(ldc_dring_handle_t dhandle, ldc_mem_cookie_t *cookie)
52001ae08745Sheppo {
52011ae08745Sheppo 	int		rv = 0;
52021ae08745Sheppo 	ldc_dring_t 	*dringp;
52031ae08745Sheppo 	ldc_chan_t	*ldcp;
52041ae08745Sheppo 
52051ae08745Sheppo 	if (dhandle == NULL) {
52061ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
52071ae08745Sheppo 		    "ldc_mem_dring_nextcookie: invalid desc ring handle\n");
52081ae08745Sheppo 		return (EINVAL);
52091ae08745Sheppo 	}
52101ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
52111ae08745Sheppo 	mutex_enter(&dringp->lock);
52121ae08745Sheppo 
52131ae08745Sheppo 	if (dringp->status != LDC_BOUND) {
52141ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
52151ae08745Sheppo 		    "ldc_mem_dring_nextcookie: descriptor ring 0x%llx "
52161ae08745Sheppo 		    "is not bound\n", dringp);
52171ae08745Sheppo 		mutex_exit(&dringp->lock);
52181ae08745Sheppo 		return (EINVAL);
52191ae08745Sheppo 	}
52201ae08745Sheppo 
52211ae08745Sheppo 	ldcp = dringp->ldcp;
52221ae08745Sheppo 
52231ae08745Sheppo 	if (cookie == NULL) {
52241ae08745Sheppo 		DWARN(ldcp->id,
52251ae08745Sheppo 		    "ldc_mem_dring_nextcookie:(0x%llx) invalid cookie arg\n",
52261ae08745Sheppo 		    ldcp->id);
52271ae08745Sheppo 		mutex_exit(&dringp->lock);
52281ae08745Sheppo 		return (EINVAL);
52291ae08745Sheppo 	}
52301ae08745Sheppo 
52311ae08745Sheppo 	rv = ldc_mem_nextcookie((ldc_mem_handle_t)dringp->mhdl, cookie);
52321ae08745Sheppo 	mutex_exit(&dringp->lock);
52331ae08745Sheppo 
52341ae08745Sheppo 	return (rv);
52351ae08745Sheppo }
52361ae08745Sheppo /*
52371ae08745Sheppo  * Unbind a previously bound dring from a channel.
52381ae08745Sheppo  */
52391ae08745Sheppo int
52401ae08745Sheppo ldc_mem_dring_unbind(ldc_dring_handle_t dhandle)
52411ae08745Sheppo {
52421ae08745Sheppo 	ldc_dring_t 	*dringp;
52431ae08745Sheppo 	ldc_dring_t	*tmp_dringp;
52441ae08745Sheppo 	ldc_chan_t	*ldcp;
52451ae08745Sheppo 
52461ae08745Sheppo 	if (dhandle == NULL) {
52471ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
52481ae08745Sheppo 		    "ldc_mem_dring_unbind: invalid desc ring handle\n");
52491ae08745Sheppo 		return (EINVAL);
52501ae08745Sheppo 	}
52511ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
52521ae08745Sheppo 
52531ae08745Sheppo 	mutex_enter(&dringp->lock);
52541ae08745Sheppo 
52551ae08745Sheppo 	if (dringp->status == LDC_UNBOUND) {
52561ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
52571ae08745Sheppo 		    "ldc_mem_dring_bind: descriptor ring 0x%llx is unbound\n",
52581ae08745Sheppo 		    dringp);
52591ae08745Sheppo 		mutex_exit(&dringp->lock);
52601ae08745Sheppo 		return (EINVAL);
52611ae08745Sheppo 	}
52621ae08745Sheppo 	ldcp = dringp->ldcp;
52631ae08745Sheppo 
52641ae08745Sheppo 	mutex_enter(&ldcp->exp_dlist_lock);
52651ae08745Sheppo 
52661ae08745Sheppo 	tmp_dringp = ldcp->exp_dring_list;
52671ae08745Sheppo 	if (tmp_dringp == dringp) {
52681ae08745Sheppo 		ldcp->exp_dring_list = dringp->ch_next;
52691ae08745Sheppo 		dringp->ch_next = NULL;
52701ae08745Sheppo 
52711ae08745Sheppo 	} else {
52721ae08745Sheppo 		while (tmp_dringp != NULL) {
52731ae08745Sheppo 			if (tmp_dringp->ch_next == dringp) {
52741ae08745Sheppo 				tmp_dringp->ch_next = dringp->ch_next;
52751ae08745Sheppo 				dringp->ch_next = NULL;
52761ae08745Sheppo 				break;
52771ae08745Sheppo 			}
52781ae08745Sheppo 			tmp_dringp = tmp_dringp->ch_next;
52791ae08745Sheppo 		}
52801ae08745Sheppo 		if (tmp_dringp == NULL) {
52811ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
52821ae08745Sheppo 			    "ldc_mem_dring_unbind: invalid descriptor\n");
52831ae08745Sheppo 			mutex_exit(&ldcp->exp_dlist_lock);
52841ae08745Sheppo 			mutex_exit(&dringp->lock);
52851ae08745Sheppo 			return (EINVAL);
52861ae08745Sheppo 		}
52871ae08745Sheppo 	}
52881ae08745Sheppo 
52891ae08745Sheppo 	mutex_exit(&ldcp->exp_dlist_lock);
52901ae08745Sheppo 
52911ae08745Sheppo 	(void) ldc_mem_unbind_handle((ldc_mem_handle_t)dringp->mhdl);
52921ae08745Sheppo 	(void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl);
52931ae08745Sheppo 
52941ae08745Sheppo 	dringp->ldcp = NULL;
52951ae08745Sheppo 	dringp->mhdl = NULL;
52961ae08745Sheppo 	dringp->status = LDC_UNBOUND;
52971ae08745Sheppo 
52981ae08745Sheppo 	mutex_exit(&dringp->lock);
52991ae08745Sheppo 
53001ae08745Sheppo 	return (0);
53011ae08745Sheppo }
53021ae08745Sheppo 
53031ae08745Sheppo /*
53041ae08745Sheppo  * Get information about the dring. The base address of the descriptor
53051ae08745Sheppo  * ring along with the type and permission are returned back.
53061ae08745Sheppo  */
53071ae08745Sheppo int
53081ae08745Sheppo ldc_mem_dring_info(ldc_dring_handle_t dhandle, ldc_mem_info_t *minfo)
53091ae08745Sheppo {
53101ae08745Sheppo 	ldc_dring_t	*dringp;
53111ae08745Sheppo 	int		rv;
53121ae08745Sheppo 
53131ae08745Sheppo 	if (dhandle == NULL) {
53141ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
53151ae08745Sheppo 		    "ldc_mem_dring_info: invalid desc ring handle\n");
53161ae08745Sheppo 		return (EINVAL);
53171ae08745Sheppo 	}
53181ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
53191ae08745Sheppo 
53201ae08745Sheppo 	mutex_enter(&dringp->lock);
53211ae08745Sheppo 
53221ae08745Sheppo 	if (dringp->mhdl) {
53231ae08745Sheppo 		rv = ldc_mem_info(dringp->mhdl, minfo);
53241ae08745Sheppo 		if (rv) {
53251ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
53261ae08745Sheppo 			    "ldc_mem_dring_info: error reading mem info\n");
53271ae08745Sheppo 			mutex_exit(&dringp->lock);
53281ae08745Sheppo 			return (rv);
53291ae08745Sheppo 		}
53301ae08745Sheppo 	} else {
53311ae08745Sheppo 		minfo->vaddr = dringp->base;
53321ae08745Sheppo 		minfo->raddr = NULL;
53331ae08745Sheppo 		minfo->status = dringp->status;
53341ae08745Sheppo 	}
53351ae08745Sheppo 
53361ae08745Sheppo 	mutex_exit(&dringp->lock);
53371ae08745Sheppo 
53381ae08745Sheppo 	return (0);
53391ae08745Sheppo }
53401ae08745Sheppo 
53411ae08745Sheppo /*
53421ae08745Sheppo  * Map an exported descriptor ring into the local address space. If the
53431ae08745Sheppo  * descriptor ring was exported for direct map access, a HV call is made
53441ae08745Sheppo  * to allocate a RA range. If the map is done via a shadow copy, local
53451ae08745Sheppo  * shadow memory is allocated.
53461ae08745Sheppo  */
53471ae08745Sheppo int
53481ae08745Sheppo ldc_mem_dring_map(ldc_handle_t handle, ldc_mem_cookie_t *cookie,
53491ae08745Sheppo     uint32_t ccount, uint32_t len, uint32_t dsize, uint8_t mtype,
53501ae08745Sheppo     ldc_dring_handle_t *dhandle)
53511ae08745Sheppo {
53521ae08745Sheppo 	int		err;
53531ae08745Sheppo 	ldc_chan_t 	*ldcp = (ldc_chan_t *)handle;
53541ae08745Sheppo 	ldc_mem_handle_t mhandle;
53551ae08745Sheppo 	ldc_dring_t	*dringp;
53561ae08745Sheppo 	size_t		dring_size;
53571ae08745Sheppo 
53581ae08745Sheppo 	if (dhandle == NULL) {
53591ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
53601ae08745Sheppo 		    "ldc_mem_dring_map: invalid dhandle\n");
53611ae08745Sheppo 		return (EINVAL);
53621ae08745Sheppo 	}
53631ae08745Sheppo 
53641ae08745Sheppo 	/* check to see if channel is initalized */
53651ae08745Sheppo 	if (handle == NULL) {
53661ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
53671ae08745Sheppo 		    "ldc_mem_dring_map: invalid channel handle\n");
53681ae08745Sheppo 		return (EINVAL);
53691ae08745Sheppo 	}
53701ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
53711ae08745Sheppo 
53721ae08745Sheppo 	if (cookie == NULL) {
53731ae08745Sheppo 		DWARN(ldcp->id,
53741ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) invalid cookie\n",
53751ae08745Sheppo 		    ldcp->id);
53761ae08745Sheppo 		return (EINVAL);
53771ae08745Sheppo 	}
53781ae08745Sheppo 
53791ae08745Sheppo 	/* FUTURE: For now we support only one cookie per dring */
53801ae08745Sheppo 	ASSERT(ccount == 1);
53811ae08745Sheppo 
53821ae08745Sheppo 	if (cookie->size < (dsize * len)) {
53831ae08745Sheppo 		DWARN(ldcp->id,
53841ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) invalid dsize/len\n",
53851ae08745Sheppo 		    ldcp->id);
53861ae08745Sheppo 		return (EINVAL);
53871ae08745Sheppo 	}
53881ae08745Sheppo 
53891ae08745Sheppo 	*dhandle = 0;
53901ae08745Sheppo 
53911ae08745Sheppo 	/* Allocate an dring structure */
53921ae08745Sheppo 	dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP);
53931ae08745Sheppo 
53941ae08745Sheppo 	D1(ldcp->id,
53951ae08745Sheppo 	    "ldc_mem_dring_map: 0x%x,0x%x,0x%x,0x%llx,0x%llx\n",
53961ae08745Sheppo 	    mtype, len, dsize, cookie->addr, cookie->size);
53971ae08745Sheppo 
53981ae08745Sheppo 	/* Initialize dring */
53991ae08745Sheppo 	dringp->length = len;
54001ae08745Sheppo 	dringp->dsize = dsize;
54011ae08745Sheppo 
54021ae08745Sheppo 	/* round of to multiple of page size */
54031ae08745Sheppo 	dring_size = len * dsize;
54041ae08745Sheppo 	dringp->size = (dring_size & MMU_PAGEMASK);
54051ae08745Sheppo 	if (dring_size & MMU_PAGEOFFSET)
54061ae08745Sheppo 		dringp->size += MMU_PAGESIZE;
54071ae08745Sheppo 
54081ae08745Sheppo 	dringp->ldcp = ldcp;
54091ae08745Sheppo 
54101ae08745Sheppo 	/* create an memory handle */
54111ae08745Sheppo 	err = ldc_mem_alloc_handle(handle, &mhandle);
54121ae08745Sheppo 	if (err || mhandle == NULL) {
54131ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
54141ae08745Sheppo 		    "ldc_mem_dring_map: cannot alloc hdl err=%d\n",
54151ae08745Sheppo 		    err);
54161ae08745Sheppo 		kmem_free(dringp, sizeof (ldc_dring_t));
54171ae08745Sheppo 		return (ENOMEM);
54181ae08745Sheppo 	}
54191ae08745Sheppo 
54201ae08745Sheppo 	dringp->mhdl = mhandle;
54211ae08745Sheppo 	dringp->base = NULL;
54221ae08745Sheppo 
54231ae08745Sheppo 	/* map the dring into local memory */
54241ae08745Sheppo 	err = ldc_mem_map(mhandle, cookie, ccount, mtype,
54251ae08745Sheppo 	    &(dringp->base), NULL);
54261ae08745Sheppo 	if (err || dringp->base == NULL) {
54271ae08745Sheppo 		cmn_err(CE_WARN,
54281ae08745Sheppo 		    "ldc_mem_dring_map: cannot map desc ring err=%d\n", err);
54291ae08745Sheppo 		(void) ldc_mem_free_handle(mhandle);
54301ae08745Sheppo 		kmem_free(dringp, sizeof (ldc_dring_t));
54311ae08745Sheppo 		return (ENOMEM);
54321ae08745Sheppo 	}
54331ae08745Sheppo 
54341ae08745Sheppo 	/* initialize the desc ring lock */
54351ae08745Sheppo 	mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL);
54361ae08745Sheppo 
54371ae08745Sheppo 	/* Add descriptor ring to channel's imported dring list */
54381ae08745Sheppo 	mutex_enter(&ldcp->imp_dlist_lock);
54391ae08745Sheppo 	dringp->ch_next = ldcp->imp_dring_list;
54401ae08745Sheppo 	ldcp->imp_dring_list = dringp;
54411ae08745Sheppo 	mutex_exit(&ldcp->imp_dlist_lock);
54421ae08745Sheppo 
54431ae08745Sheppo 	dringp->status = LDC_MAPPED;
54441ae08745Sheppo 
54451ae08745Sheppo 	*dhandle = (ldc_dring_handle_t)dringp;
54461ae08745Sheppo 
54471ae08745Sheppo 	return (0);
54481ae08745Sheppo }
54491ae08745Sheppo 
54501ae08745Sheppo /*
54511ae08745Sheppo  * Unmap a descriptor ring. Free shadow memory (if any).
54521ae08745Sheppo  */
54531ae08745Sheppo int
54541ae08745Sheppo ldc_mem_dring_unmap(ldc_dring_handle_t dhandle)
54551ae08745Sheppo {
54561ae08745Sheppo 	ldc_dring_t 	*dringp;
54571ae08745Sheppo 	ldc_dring_t	*tmp_dringp;
54581ae08745Sheppo 	ldc_chan_t	*ldcp;
54591ae08745Sheppo 
54601ae08745Sheppo 	if (dhandle == NULL) {
54611ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
54621ae08745Sheppo 		    "ldc_mem_dring_unmap: invalid desc ring handle\n");
54631ae08745Sheppo 		return (EINVAL);
54641ae08745Sheppo 	}
54651ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
54661ae08745Sheppo 
54671ae08745Sheppo 	if (dringp->status != LDC_MAPPED) {
54681ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
54691ae08745Sheppo 		    "ldc_mem_dring_unmap: not a mapped desc ring\n");
54701ae08745Sheppo 		return (EINVAL);
54711ae08745Sheppo 	}
54721ae08745Sheppo 
54731ae08745Sheppo 	mutex_enter(&dringp->lock);
54741ae08745Sheppo 
54751ae08745Sheppo 	ldcp = dringp->ldcp;
54761ae08745Sheppo 
54771ae08745Sheppo 	mutex_enter(&ldcp->imp_dlist_lock);
54781ae08745Sheppo 
54791ae08745Sheppo 	/* find and unlink the desc ring from channel import list */
54801ae08745Sheppo 	tmp_dringp = ldcp->imp_dring_list;
54811ae08745Sheppo 	if (tmp_dringp == dringp) {
54821ae08745Sheppo 		ldcp->imp_dring_list = dringp->ch_next;
54831ae08745Sheppo 		dringp->ch_next = NULL;
54841ae08745Sheppo 
54851ae08745Sheppo 	} else {
54861ae08745Sheppo 		while (tmp_dringp != NULL) {
54871ae08745Sheppo 			if (tmp_dringp->ch_next == dringp) {
54881ae08745Sheppo 				tmp_dringp->ch_next = dringp->ch_next;
54891ae08745Sheppo 				dringp->ch_next = NULL;
54901ae08745Sheppo 				break;
54911ae08745Sheppo 			}
54921ae08745Sheppo 			tmp_dringp = tmp_dringp->ch_next;
54931ae08745Sheppo 		}
54941ae08745Sheppo 		if (tmp_dringp == NULL) {
54951ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
54961ae08745Sheppo 			    "ldc_mem_dring_unmap: invalid descriptor\n");
54971ae08745Sheppo 			mutex_exit(&ldcp->imp_dlist_lock);
54981ae08745Sheppo 			mutex_exit(&dringp->lock);
54991ae08745Sheppo 			return (EINVAL);
55001ae08745Sheppo 		}
55011ae08745Sheppo 	}
55021ae08745Sheppo 
55031ae08745Sheppo 	mutex_exit(&ldcp->imp_dlist_lock);
55041ae08745Sheppo 
55051ae08745Sheppo 	/* do a LDC memory handle unmap and free */
55061ae08745Sheppo 	(void) ldc_mem_unmap(dringp->mhdl);
55071ae08745Sheppo 	(void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl);
55081ae08745Sheppo 
55091ae08745Sheppo 	dringp->status = 0;
55101ae08745Sheppo 	dringp->ldcp = NULL;
55111ae08745Sheppo 
55121ae08745Sheppo 	mutex_exit(&dringp->lock);
55131ae08745Sheppo 
55141ae08745Sheppo 	/* destroy dring lock */
55151ae08745Sheppo 	mutex_destroy(&dringp->lock);
55161ae08745Sheppo 
55171ae08745Sheppo 	/* free desc ring object */
55181ae08745Sheppo 	kmem_free(dringp, sizeof (ldc_dring_t));
55191ae08745Sheppo 
55201ae08745Sheppo 	return (0);
55211ae08745Sheppo }
55221ae08745Sheppo 
55231ae08745Sheppo /*
55241ae08745Sheppo  * Internal entry point for descriptor ring access entry consistency
55251ae08745Sheppo  * semantics. Acquire copies the contents of the remote descriptor ring
55261ae08745Sheppo  * into the local shadow copy. The release operation copies the local
55271ae08745Sheppo  * contents into the remote dring. The start and end locations specify
55281ae08745Sheppo  * bounds for the entries being synchronized.
55291ae08745Sheppo  */
55301ae08745Sheppo static int
55311ae08745Sheppo i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle,
55321ae08745Sheppo     uint8_t direction, uint64_t start, uint64_t end)
55331ae08745Sheppo {
55341ae08745Sheppo 	int 			err;
55351ae08745Sheppo 	ldc_dring_t		*dringp;
55361ae08745Sheppo 	ldc_chan_t		*ldcp;
55371ae08745Sheppo 	uint64_t		soff;
55381ae08745Sheppo 	size_t			copy_size;
55391ae08745Sheppo 
55401ae08745Sheppo 	if (dhandle == NULL) {
55411ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
55421ae08745Sheppo 		    "i_ldc_dring_acquire_release: invalid desc ring handle\n");
55431ae08745Sheppo 		return (EINVAL);
55441ae08745Sheppo 	}
55451ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
55461ae08745Sheppo 	mutex_enter(&dringp->lock);
55471ae08745Sheppo 
55481ae08745Sheppo 	if (dringp->status != LDC_MAPPED || dringp->ldcp == NULL) {
55491ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
55501ae08745Sheppo 		    "i_ldc_dring_acquire_release: not a mapped desc ring\n");
55511ae08745Sheppo 		mutex_exit(&dringp->lock);
55521ae08745Sheppo 		return (EINVAL);
55531ae08745Sheppo 	}
55541ae08745Sheppo 
55551ae08745Sheppo 	if (start >= dringp->length || end >= dringp->length) {
55561ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
55571ae08745Sheppo 		    "i_ldc_dring_acquire_release: index out of range\n");
55581ae08745Sheppo 		mutex_exit(&dringp->lock);
55591ae08745Sheppo 		return (EINVAL);
55601ae08745Sheppo 	}
55611ae08745Sheppo 
55621ae08745Sheppo 	/* get the channel handle */
55631ae08745Sheppo 	ldcp = dringp->ldcp;
55641ae08745Sheppo 
55651ae08745Sheppo 	copy_size = (start <= end) ? (((end - start) + 1) * dringp->dsize) :
55661ae08745Sheppo 		((dringp->length - start) * dringp->dsize);
55671ae08745Sheppo 
55681ae08745Sheppo 	/* Calculate the relative offset for the first desc */
55691ae08745Sheppo 	soff = (start * dringp->dsize);
55701ae08745Sheppo 
55711ae08745Sheppo 	/* copy to/from remote from/to local memory */
55721ae08745Sheppo 	D1(ldcp->id, "i_ldc_dring_acquire_release: c1 off=0x%llx sz=0x%llx\n",
55731ae08745Sheppo 	    soff, copy_size);
55741ae08745Sheppo 	err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl,
55751ae08745Sheppo 	    direction, soff, copy_size);
55761ae08745Sheppo 	if (err) {
55771ae08745Sheppo 		DWARN(ldcp->id,
55781ae08745Sheppo 		    "i_ldc_dring_acquire_release: copy failed\n");
55791ae08745Sheppo 		mutex_exit(&dringp->lock);
55801ae08745Sheppo 		return (err);
55811ae08745Sheppo 	}
55821ae08745Sheppo 
55831ae08745Sheppo 	/* do the balance */
55841ae08745Sheppo 	if (start > end) {
55851ae08745Sheppo 		copy_size = ((end + 1) * dringp->dsize);
55861ae08745Sheppo 		soff = 0;
55871ae08745Sheppo 
55881ae08745Sheppo 		/* copy to/from remote from/to local memory */
55891ae08745Sheppo 		D1(ldcp->id, "i_ldc_dring_acquire_release: c2 "
55901ae08745Sheppo 		    "off=0x%llx sz=0x%llx\n", soff, copy_size);
55911ae08745Sheppo 		err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl,
55921ae08745Sheppo 		    direction, soff, copy_size);
55931ae08745Sheppo 		if (err) {
55941ae08745Sheppo 			DWARN(ldcp->id,
55951ae08745Sheppo 			    "i_ldc_dring_acquire_release: copy failed\n");
55961ae08745Sheppo 			mutex_exit(&dringp->lock);
55971ae08745Sheppo 			return (err);
55981ae08745Sheppo 		}
55991ae08745Sheppo 	}
56001ae08745Sheppo 
56011ae08745Sheppo 	mutex_exit(&dringp->lock);
56021ae08745Sheppo 
56031ae08745Sheppo 	return (0);
56041ae08745Sheppo }
56051ae08745Sheppo 
56061ae08745Sheppo /*
56071ae08745Sheppo  * Ensure that the contents in the local dring are consistent
56081ae08745Sheppo  * with the contents if of remote dring
56091ae08745Sheppo  */
56101ae08745Sheppo int
56111ae08745Sheppo ldc_mem_dring_acquire(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end)
56121ae08745Sheppo {
56131ae08745Sheppo 	return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_IN, start, end));
56141ae08745Sheppo }
56151ae08745Sheppo 
56161ae08745Sheppo /*
56171ae08745Sheppo  * Ensure that the contents in the remote dring are consistent
56181ae08745Sheppo  * with the contents if of local dring
56191ae08745Sheppo  */
56201ae08745Sheppo int
56211ae08745Sheppo ldc_mem_dring_release(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end)
56221ae08745Sheppo {
56231ae08745Sheppo 	return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_OUT, start, end));
56241ae08745Sheppo }
56251ae08745Sheppo 
56261ae08745Sheppo 
56271ae08745Sheppo /* ------------------------------------------------------------------------- */
5628