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