xref: /titanic_53/usr/src/uts/sun4v/io/ldc.c (revision 582832867ac00db0b1619c1bb890285984d338c9)
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 /*
23*58283286Sha137994  * Copyright 2008 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>
70*58283286Sha137994 #include <sys/sdt.h>
711ae08745Sheppo 
721ae08745Sheppo /* Core internal functions */
731ae08745Sheppo static int i_ldc_h2v_error(int h_error);
741ae08745Sheppo static int i_ldc_txq_reconf(ldc_chan_t *ldcp);
753af08d82Slm66018 static int i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset);
76a8ea4edeSnarayan static int i_ldc_rxq_drain(ldc_chan_t *ldcp);
771ae08745Sheppo static void i_ldc_reset_state(ldc_chan_t *ldcp);
783af08d82Slm66018 static void i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset);
791ae08745Sheppo 
801ae08745Sheppo static int i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail);
8122f747efSnarayan static void i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head);
821ae08745Sheppo static int i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail);
831ae08745Sheppo static int i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head);
841ae08745Sheppo static int i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
851ae08745Sheppo     uint8_t ctrlmsg);
861ae08745Sheppo 
87*58283286Sha137994 static int  i_ldc_set_rxdq_head(ldc_chan_t *ldcp, uint64_t head);
88*58283286Sha137994 static void i_ldc_rxdq_copy(ldc_chan_t *ldcp, uint64_t *head);
89*58283286Sha137994 static uint64_t i_ldc_dq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head,
90*58283286Sha137994     uint64_t *tail, uint64_t *link_state);
91*58283286Sha137994 static uint64_t i_ldc_hvq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head,
92*58283286Sha137994     uint64_t *tail, uint64_t *link_state);
93*58283286Sha137994 static int i_ldc_rx_ackpeek(ldc_chan_t *ldcp, uint64_t rx_head,
94*58283286Sha137994     uint64_t rx_tail);
95*58283286Sha137994 static uint_t i_ldc_chkq(ldc_chan_t *ldcp);
96*58283286Sha137994 
971ae08745Sheppo /* Interrupt handling functions */
981ae08745Sheppo static uint_t i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2);
991ae08745Sheppo static uint_t i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2);
100*58283286Sha137994 static uint_t i_ldc_rx_process_hvq(ldc_chan_t *ldcp, boolean_t *notify_client,
101*58283286Sha137994     uint64_t *notify_event);
1021ae08745Sheppo static void i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype);
1031ae08745Sheppo 
1041ae08745Sheppo /* Read method functions */
1051ae08745Sheppo static int i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep);
1061ae08745Sheppo static int i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
1071ae08745Sheppo 	size_t *sizep);
1081ae08745Sheppo static int i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
1091ae08745Sheppo 	size_t *sizep);
1101ae08745Sheppo 
1111ae08745Sheppo /* Write method functions */
1121ae08745Sheppo static int i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t target_bufp,
1131ae08745Sheppo 	size_t *sizep);
1141ae08745Sheppo static int i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t target_bufp,
1151ae08745Sheppo 	size_t *sizep);
1161ae08745Sheppo static int i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t target_bufp,
1171ae08745Sheppo 	size_t *sizep);
1181ae08745Sheppo 
1191ae08745Sheppo /* Pkt processing internal functions */
1201ae08745Sheppo static int i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
1211ae08745Sheppo static int i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *ldcmsg);
1221ae08745Sheppo static int i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg);
1231ae08745Sheppo static int i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg);
1241ae08745Sheppo static int i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg);
1251ae08745Sheppo static int i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg);
1261ae08745Sheppo static int i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg);
1271ae08745Sheppo 
1281ae08745Sheppo /* Memory synchronization internal functions */
1291ae08745Sheppo static int i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle,
1301ae08745Sheppo     uint8_t direction, uint64_t offset, size_t size);
1311ae08745Sheppo static int i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle,
1321ae08745Sheppo     uint8_t direction, uint64_t start, uint64_t end);
1331ae08745Sheppo 
1341ae08745Sheppo /* LDC Version */
1351ae08745Sheppo static ldc_ver_t ldc_versions[] = { {1, 0} };
1361ae08745Sheppo 
1371ae08745Sheppo /* number of supported versions */
1381ae08745Sheppo #define	LDC_NUM_VERS	(sizeof (ldc_versions) / sizeof (ldc_versions[0]))
1391ae08745Sheppo 
140*58283286Sha137994 /* Invalid value for the ldc_chan_t rx_ack_head field */
141*58283286Sha137994 #define	ACKPEEK_HEAD_INVALID	((uint64_t)-1)
142*58283286Sha137994 
143*58283286Sha137994 
1441ae08745Sheppo /* Module State Pointer */
1451ae08745Sheppo static ldc_soft_state_t *ldcssp;
1461ae08745Sheppo 
1471ae08745Sheppo static struct modldrv md = {
1481ae08745Sheppo 	&mod_miscops,			/* This is a misc module */
1491ae08745Sheppo 	"sun4v LDC module v%I%",	/* Name of the module */
1501ae08745Sheppo };
1511ae08745Sheppo 
1521ae08745Sheppo static struct modlinkage ml = {
1531ae08745Sheppo 	MODREV_1,
1541ae08745Sheppo 	&md,
1551ae08745Sheppo 	NULL
1561ae08745Sheppo };
1571ae08745Sheppo 
1581ae08745Sheppo static uint64_t ldc_sup_minor;		/* Supported minor number */
1591ae08745Sheppo static hsvc_info_t ldc_hsvc = {
1601ae08745Sheppo 	HSVC_REV_1, NULL, HSVC_GROUP_LDC, 1, 0, "ldc"
1611ae08745Sheppo };
1621ae08745Sheppo 
1634bac2208Snarayan /*
1644bac2208Snarayan  * LDC framework supports mapping remote domain's memory
1654bac2208Snarayan  * either directly or via shadow memory pages. Default
1664bac2208Snarayan  * support is currently implemented via shadow copy.
1674bac2208Snarayan  * Direct map can be enabled by setting 'ldc_shmem_enabled'
1684bac2208Snarayan  */
1694bac2208Snarayan int ldc_shmem_enabled = 0;
170e1ebb9ecSlm66018 
1710a55fbb7Slm66018 /*
172e1ebb9ecSlm66018  * The no. of MTU size messages that can be stored in
173e1ebb9ecSlm66018  * the LDC Tx queue. The number of Tx queue entries is
174e1ebb9ecSlm66018  * then computed as (mtu * mtu_msgs)/sizeof(queue_entry)
175e1ebb9ecSlm66018  */
176e1ebb9ecSlm66018 uint64_t ldc_mtu_msgs = LDC_MTU_MSGS;
177e1ebb9ecSlm66018 
178e1ebb9ecSlm66018 /*
179e1ebb9ecSlm66018  * The minimum queue length. This is the size of the smallest
180e1ebb9ecSlm66018  * LDC queue. If the computed value is less than this default,
181e1ebb9ecSlm66018  * the queue length is rounded up to 'ldc_queue_entries'.
182e1ebb9ecSlm66018  */
183e1ebb9ecSlm66018 uint64_t ldc_queue_entries = LDC_QUEUE_ENTRIES;
184e1ebb9ecSlm66018 
185e1ebb9ecSlm66018 /*
186*58283286Sha137994  * The length of the reliable-mode data queue in terms of the LDC
187*58283286Sha137994  * receive queue length. i.e., the number of times larger than the
188*58283286Sha137994  * LDC receive queue that the data queue should be. The HV receive
189*58283286Sha137994  * queue is required to be a power of 2 and this implementation
190*58283286Sha137994  * assumes the data queue will also be a power of 2. By making the
191*58283286Sha137994  * multiplier a power of 2, we ensure the data queue will be a
192*58283286Sha137994  * power of 2. We use a multiplier because the receive queue is
193*58283286Sha137994  * sized to be sane relative to the MTU and the same is needed for
194*58283286Sha137994  * the data queue.
195*58283286Sha137994  */
196*58283286Sha137994 uint64_t ldc_rxdq_multiplier = LDC_RXDQ_MULTIPLIER;
197*58283286Sha137994 
198*58283286Sha137994 /*
199e1ebb9ecSlm66018  * Pages exported for remote access over each channel is
200e1ebb9ecSlm66018  * maintained in a table registered with the Hypervisor.
201e1ebb9ecSlm66018  * The default number of entries in the table is set to
202e1ebb9ecSlm66018  * 'ldc_mtbl_entries'.
203e1ebb9ecSlm66018  */
204e1ebb9ecSlm66018 uint64_t ldc_maptable_entries = LDC_MTBL_ENTRIES;
205e1ebb9ecSlm66018 
206e1ebb9ecSlm66018 /*
207e1ebb9ecSlm66018  * LDC retry count and delay - when the HV returns EWOULDBLOCK
208e1ebb9ecSlm66018  * the operation is retried 'ldc_max_retries' times with a
209e1ebb9ecSlm66018  * wait of 'ldc_delay' usecs between each retry.
2100a55fbb7Slm66018  */
2110a55fbb7Slm66018 int ldc_max_retries = LDC_MAX_RETRIES;
2120a55fbb7Slm66018 clock_t ldc_delay = LDC_DELAY;
2130a55fbb7Slm66018 
2144d39be2bSsg70180 /*
2154d39be2bSsg70180  * delay between each retry of channel unregistration in
2164d39be2bSsg70180  * ldc_close(), to wait for pending interrupts to complete.
2174d39be2bSsg70180  */
2184d39be2bSsg70180 clock_t ldc_close_delay = LDC_CLOSE_DELAY;
2194d39be2bSsg70180 
2201ae08745Sheppo #ifdef DEBUG
2211ae08745Sheppo 
2221ae08745Sheppo /*
2231ae08745Sheppo  * Print debug messages
2241ae08745Sheppo  *
2251ae08745Sheppo  * set ldcdbg to 0x7 for enabling all msgs
2261ae08745Sheppo  * 0x4 - Warnings
2271ae08745Sheppo  * 0x2 - All debug messages
2281ae08745Sheppo  * 0x1 - Minimal debug messages
2291ae08745Sheppo  *
2301ae08745Sheppo  * set ldcdbgchan to the channel number you want to debug
2311ae08745Sheppo  * setting it to -1 prints debug messages for all channels
2321ae08745Sheppo  * NOTE: ldcdbgchan has no effect on error messages
2331ae08745Sheppo  */
2341ae08745Sheppo 
2351ae08745Sheppo #define	DBG_ALL_LDCS -1
2361ae08745Sheppo 
2371ae08745Sheppo int ldcdbg = 0x0;
2381ae08745Sheppo int64_t ldcdbgchan = DBG_ALL_LDCS;
23983d3bc6fSnarayan uint64_t ldc_inject_err_flag = 0;
2401ae08745Sheppo 
2411ae08745Sheppo static void
2421ae08745Sheppo ldcdebug(int64_t id, const char *fmt, ...)
2431ae08745Sheppo {
2441ae08745Sheppo 	char buf[512];
2451ae08745Sheppo 	va_list ap;
2461ae08745Sheppo 
2471ae08745Sheppo 	/*
2481ae08745Sheppo 	 * Do not return if,
2491ae08745Sheppo 	 * caller wants to print it anyway - (id == DBG_ALL_LDCS)
2501ae08745Sheppo 	 * debug channel is set to all LDCs - (ldcdbgchan == DBG_ALL_LDCS)
2511ae08745Sheppo 	 * debug channel = caller specified channel
2521ae08745Sheppo 	 */
2531ae08745Sheppo 	if ((id != DBG_ALL_LDCS) &&
2541ae08745Sheppo 	    (ldcdbgchan != DBG_ALL_LDCS) &&
2551ae08745Sheppo 	    (ldcdbgchan != id)) {
2561ae08745Sheppo 		return;
2571ae08745Sheppo 	}
2581ae08745Sheppo 
2591ae08745Sheppo 	va_start(ap, fmt);
2601ae08745Sheppo 	(void) vsprintf(buf, fmt, ap);
2611ae08745Sheppo 	va_end(ap);
2621ae08745Sheppo 
2633af08d82Slm66018 	cmn_err(CE_CONT, "?%s", buf);
2643af08d82Slm66018 }
2653af08d82Slm66018 
26683d3bc6fSnarayan #define	LDC_ERR_RESET	0x1
26783d3bc6fSnarayan #define	LDC_ERR_PKTLOSS	0x2
268*58283286Sha137994 #define	LDC_ERR_DQFULL	0x4
26983d3bc6fSnarayan 
2703af08d82Slm66018 static boolean_t
27183d3bc6fSnarayan ldc_inject_error(ldc_chan_t *ldcp, uint64_t error)
2723af08d82Slm66018 {
2733af08d82Slm66018 	if ((ldcdbgchan != DBG_ALL_LDCS) && (ldcdbgchan != ldcp->id))
2743af08d82Slm66018 		return (B_FALSE);
2753af08d82Slm66018 
27683d3bc6fSnarayan 	if ((ldc_inject_err_flag & error) == 0)
2773af08d82Slm66018 		return (B_FALSE);
2783af08d82Slm66018 
2793af08d82Slm66018 	/* clear the injection state */
28083d3bc6fSnarayan 	ldc_inject_err_flag &= ~error;
2813af08d82Slm66018 
2823af08d82Slm66018 	return (B_TRUE);
2831ae08745Sheppo }
2841ae08745Sheppo 
2851ae08745Sheppo #define	D1		\
2861ae08745Sheppo if (ldcdbg & 0x01)	\
2871ae08745Sheppo 	ldcdebug
2881ae08745Sheppo 
2891ae08745Sheppo #define	D2		\
2901ae08745Sheppo if (ldcdbg & 0x02)	\
2911ae08745Sheppo 	ldcdebug
2921ae08745Sheppo 
2931ae08745Sheppo #define	DWARN		\
2941ae08745Sheppo if (ldcdbg & 0x04)	\
2951ae08745Sheppo 	ldcdebug
2961ae08745Sheppo 
2971ae08745Sheppo #define	DUMP_PAYLOAD(id, addr)						\
2981ae08745Sheppo {									\
2991ae08745Sheppo 	char buf[65*3];							\
3001ae08745Sheppo 	int i;								\
3011ae08745Sheppo 	uint8_t *src = (uint8_t *)addr;					\
3021ae08745Sheppo 	for (i = 0; i < 64; i++, src++)					\
3031ae08745Sheppo 		(void) sprintf(&buf[i * 3], "|%02x", *src);		\
3041ae08745Sheppo 	(void) sprintf(&buf[i * 3], "|\n");				\
3051ae08745Sheppo 	D2((id), "payload: %s", buf);					\
3061ae08745Sheppo }
3071ae08745Sheppo 
3081ae08745Sheppo #define	DUMP_LDC_PKT(c, s, addr)					\
3091ae08745Sheppo {									\
3101ae08745Sheppo 	ldc_msg_t *msg = (ldc_msg_t *)(addr);				\
3111ae08745Sheppo 	uint32_t mid = ((c)->mode != LDC_MODE_RAW) ? msg->seqid : 0;	\
3121ae08745Sheppo 	if (msg->type == LDC_DATA) {                                    \
3131ae08745Sheppo 	    D2((c)->id, "%s: msg%d (/%x/%x/%x/,env[%c%c,sz=%d])",	\
3141ae08745Sheppo 	    (s), mid, msg->type, msg->stype, msg->ctrl,			\
3151ae08745Sheppo 	    (msg->env & LDC_FRAG_START) ? 'B' : ' ',                    \
3161ae08745Sheppo 	    (msg->env & LDC_FRAG_STOP) ? 'E' : ' ',                     \
3171ae08745Sheppo 	    (msg->env & LDC_LEN_MASK));					\
3181ae08745Sheppo 	} else { 							\
3191ae08745Sheppo 	    D2((c)->id, "%s: msg%d (/%x/%x/%x/,env=%x)", (s),		\
3201ae08745Sheppo 	    mid, msg->type, msg->stype, msg->ctrl, msg->env);		\
3211ae08745Sheppo 	} 								\
3221ae08745Sheppo }
3231ae08745Sheppo 
32483d3bc6fSnarayan #define	LDC_INJECT_RESET(_ldcp)	ldc_inject_error(_ldcp, LDC_ERR_RESET)
32583d3bc6fSnarayan #define	LDC_INJECT_PKTLOSS(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_PKTLOSS)
326*58283286Sha137994 #define	LDC_INJECT_DQFULL(_ldcp) ldc_inject_error(_ldcp, LDC_ERR_DQFULL)
3273af08d82Slm66018 
3281ae08745Sheppo #else
3291ae08745Sheppo 
3301ae08745Sheppo #define	DBG_ALL_LDCS -1
3311ae08745Sheppo 
3321ae08745Sheppo #define	D1
3331ae08745Sheppo #define	D2
3341ae08745Sheppo #define	DWARN
3351ae08745Sheppo 
3361ae08745Sheppo #define	DUMP_PAYLOAD(id, addr)
3371ae08745Sheppo #define	DUMP_LDC_PKT(c, s, addr)
3381ae08745Sheppo 
3393af08d82Slm66018 #define	LDC_INJECT_RESET(_ldcp)	(B_FALSE)
34083d3bc6fSnarayan #define	LDC_INJECT_PKTLOSS(_ldcp) (B_FALSE)
341*58283286Sha137994 #define	LDC_INJECT_DQFULL(_ldcp) (B_FALSE)
3423af08d82Slm66018 
3431ae08745Sheppo #endif
3441ae08745Sheppo 
345*58283286Sha137994 /*
346*58283286Sha137994  * dtrace SDT probes to ease tracing of the rx data queue and HV queue
347*58283286Sha137994  * lengths. Just pass the head, tail, and entries values so that the
348*58283286Sha137994  * length can be calculated in a dtrace script when the probe is enabled.
349*58283286Sha137994  */
350*58283286Sha137994 #define	TRACE_RXDQ_LENGTH(ldcp)						\
351*58283286Sha137994 	DTRACE_PROBE4(rxdq__size,					\
352*58283286Sha137994 	uint64_t, ldcp->id,						\
353*58283286Sha137994 	uint64_t, ldcp->rx_dq_head,					\
354*58283286Sha137994 	uint64_t, ldcp->rx_dq_tail,					\
355*58283286Sha137994 	uint64_t, ldcp->rx_dq_entries)
356*58283286Sha137994 
357*58283286Sha137994 #define	TRACE_RXHVQ_LENGTH(ldcp, head, tail)				\
358*58283286Sha137994 	DTRACE_PROBE4(rxhvq__size,					\
359*58283286Sha137994 	uint64_t, ldcp->id,						\
360*58283286Sha137994 	uint64_t, head,							\
361*58283286Sha137994 	uint64_t, tail,							\
362*58283286Sha137994 	uint64_t, ldcp->rx_q_entries)
363*58283286Sha137994 
364*58283286Sha137994 /* A dtrace SDT probe to ease tracing of data queue copy operations */
365*58283286Sha137994 #define	TRACE_RXDQ_COPY(ldcp, bytes)					\
366*58283286Sha137994 	DTRACE_PROBE2(rxdq__copy, uint64_t, ldcp->id, uint64_t, bytes)	\
367*58283286Sha137994 
368*58283286Sha137994 /* The amount of contiguous space at the tail of the queue */
369*58283286Sha137994 #define	Q_CONTIG_SPACE(head, tail, size)				\
370*58283286Sha137994 	((head) <= (tail) ? ((size) - (tail)) :				\
371*58283286Sha137994 	((head) - (tail) - LDC_PACKET_SIZE))
372*58283286Sha137994 
3731ae08745Sheppo #define	ZERO_PKT(p)			\
3741ae08745Sheppo 	bzero((p), sizeof (ldc_msg_t));
3751ae08745Sheppo 
3761ae08745Sheppo #define	IDX2COOKIE(idx, pg_szc, pg_shift)				\
3771ae08745Sheppo 	(((pg_szc) << LDC_COOKIE_PGSZC_SHIFT) | ((idx) << (pg_shift)))
3781ae08745Sheppo 
3791ae08745Sheppo int
3801ae08745Sheppo _init(void)
3811ae08745Sheppo {
3821ae08745Sheppo 	int status;
3831ae08745Sheppo 
3841ae08745Sheppo 	status = hsvc_register(&ldc_hsvc, &ldc_sup_minor);
3851ae08745Sheppo 	if (status != 0) {
386d66f8315Sjb145095 		cmn_err(CE_NOTE, "!%s: cannot negotiate hypervisor LDC services"
3871ae08745Sheppo 		    " group: 0x%lx major: %ld minor: %ld errno: %d",
3881ae08745Sheppo 		    ldc_hsvc.hsvc_modname, ldc_hsvc.hsvc_group,
3891ae08745Sheppo 		    ldc_hsvc.hsvc_major, ldc_hsvc.hsvc_minor, status);
3901ae08745Sheppo 		return (-1);
3911ae08745Sheppo 	}
3921ae08745Sheppo 
3931ae08745Sheppo 	/* allocate soft state structure */
3941ae08745Sheppo 	ldcssp = kmem_zalloc(sizeof (ldc_soft_state_t), KM_SLEEP);
3951ae08745Sheppo 
3961ae08745Sheppo 	/* Link the module into the system */
3971ae08745Sheppo 	status = mod_install(&ml);
3981ae08745Sheppo 	if (status != 0) {
3991ae08745Sheppo 		kmem_free(ldcssp, sizeof (ldc_soft_state_t));
4001ae08745Sheppo 		return (status);
4011ae08745Sheppo 	}
4021ae08745Sheppo 
4031ae08745Sheppo 	/* Initialize the LDC state structure */
4041ae08745Sheppo 	mutex_init(&ldcssp->lock, NULL, MUTEX_DRIVER, NULL);
4051ae08745Sheppo 
4061ae08745Sheppo 	mutex_enter(&ldcssp->lock);
4071ae08745Sheppo 
4084bac2208Snarayan 	/* Create a cache for memory handles */
4094bac2208Snarayan 	ldcssp->memhdl_cache = kmem_cache_create("ldc_memhdl_cache",
4104bac2208Snarayan 	    sizeof (ldc_mhdl_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
4114bac2208Snarayan 	if (ldcssp->memhdl_cache == NULL) {
4124bac2208Snarayan 		DWARN(DBG_ALL_LDCS, "_init: ldc_memhdl cache create failed\n");
4134bac2208Snarayan 		mutex_exit(&ldcssp->lock);
4144bac2208Snarayan 		return (-1);
4154bac2208Snarayan 	}
4164bac2208Snarayan 
4174bac2208Snarayan 	/* Create cache for memory segment structures */
4184bac2208Snarayan 	ldcssp->memseg_cache = kmem_cache_create("ldc_memseg_cache",
4194bac2208Snarayan 	    sizeof (ldc_memseg_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
4204bac2208Snarayan 	if (ldcssp->memseg_cache == NULL) {
4214bac2208Snarayan 		DWARN(DBG_ALL_LDCS, "_init: ldc_memseg cache create failed\n");
4224bac2208Snarayan 		mutex_exit(&ldcssp->lock);
4234bac2208Snarayan 		return (-1);
4244bac2208Snarayan 	}
4254bac2208Snarayan 
4264bac2208Snarayan 
4271ae08745Sheppo 	ldcssp->channel_count = 0;
4281ae08745Sheppo 	ldcssp->channels_open = 0;
4291ae08745Sheppo 	ldcssp->chan_list = NULL;
4301ae08745Sheppo 	ldcssp->dring_list = NULL;
4311ae08745Sheppo 
4321ae08745Sheppo 	mutex_exit(&ldcssp->lock);
4331ae08745Sheppo 
4341ae08745Sheppo 	return (0);
4351ae08745Sheppo }
4361ae08745Sheppo 
4371ae08745Sheppo int
4381ae08745Sheppo _info(struct modinfo *modinfop)
4391ae08745Sheppo {
4401ae08745Sheppo 	/* Report status of the dynamically loadable driver module */
4411ae08745Sheppo 	return (mod_info(&ml, modinfop));
4421ae08745Sheppo }
4431ae08745Sheppo 
4441ae08745Sheppo int
4451ae08745Sheppo _fini(void)
4461ae08745Sheppo {
4471ae08745Sheppo 	int 		rv, status;
44822f747efSnarayan 	ldc_chan_t 	*tmp_ldcp, *ldcp;
44922f747efSnarayan 	ldc_dring_t 	*tmp_dringp, *dringp;
4501ae08745Sheppo 	ldc_mem_info_t 	minfo;
4511ae08745Sheppo 
4521ae08745Sheppo 	/* Unlink the driver module from the system */
4531ae08745Sheppo 	status = mod_remove(&ml);
4541ae08745Sheppo 	if (status) {
4551ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "_fini: mod_remove failed\n");
4561ae08745Sheppo 		return (EIO);
4571ae08745Sheppo 	}
4581ae08745Sheppo 
4591ae08745Sheppo 	/* Free descriptor rings */
4601ae08745Sheppo 	dringp = ldcssp->dring_list;
4611ae08745Sheppo 	while (dringp != NULL) {
46222f747efSnarayan 		tmp_dringp = dringp->next;
4631ae08745Sheppo 
4641ae08745Sheppo 		rv = ldc_mem_dring_info((ldc_dring_handle_t)dringp, &minfo);
4651ae08745Sheppo 		if (rv == 0 && minfo.status != LDC_UNBOUND) {
4661ae08745Sheppo 			if (minfo.status == LDC_BOUND) {
4671ae08745Sheppo 				(void) ldc_mem_dring_unbind(
4681ae08745Sheppo 				    (ldc_dring_handle_t)dringp);
4691ae08745Sheppo 			}
4701ae08745Sheppo 			if (minfo.status == LDC_MAPPED) {
4711ae08745Sheppo 				(void) ldc_mem_dring_unmap(
4721ae08745Sheppo 				    (ldc_dring_handle_t)dringp);
4731ae08745Sheppo 			}
4741ae08745Sheppo 		}
4751ae08745Sheppo 
4761ae08745Sheppo 		(void) ldc_mem_dring_destroy((ldc_dring_handle_t)dringp);
47722f747efSnarayan 		dringp = tmp_dringp;
4781ae08745Sheppo 	}
4791ae08745Sheppo 	ldcssp->dring_list = NULL;
4801ae08745Sheppo 
48122f747efSnarayan 	/* close and finalize channels */
48222f747efSnarayan 	ldcp = ldcssp->chan_list;
48322f747efSnarayan 	while (ldcp != NULL) {
48422f747efSnarayan 		tmp_ldcp = ldcp->next;
48522f747efSnarayan 
48622f747efSnarayan 		(void) ldc_close((ldc_handle_t)ldcp);
48722f747efSnarayan 		(void) ldc_fini((ldc_handle_t)ldcp);
48822f747efSnarayan 
48922f747efSnarayan 		ldcp = tmp_ldcp;
49022f747efSnarayan 	}
49122f747efSnarayan 	ldcssp->chan_list = NULL;
49222f747efSnarayan 
4934bac2208Snarayan 	/* Destroy kmem caches */
4944bac2208Snarayan 	kmem_cache_destroy(ldcssp->memhdl_cache);
4954bac2208Snarayan 	kmem_cache_destroy(ldcssp->memseg_cache);
4964bac2208Snarayan 
4971ae08745Sheppo 	/*
4981ae08745Sheppo 	 * We have successfully "removed" the driver.
4991ae08745Sheppo 	 * Destroying soft states
5001ae08745Sheppo 	 */
5011ae08745Sheppo 	mutex_destroy(&ldcssp->lock);
5021ae08745Sheppo 	kmem_free(ldcssp, sizeof (ldc_soft_state_t));
5031ae08745Sheppo 
5041ae08745Sheppo 	(void) hsvc_unregister(&ldc_hsvc);
5051ae08745Sheppo 
5061ae08745Sheppo 	return (status);
5071ae08745Sheppo }
5081ae08745Sheppo 
5091ae08745Sheppo /* -------------------------------------------------------------------------- */
5101ae08745Sheppo 
5111ae08745Sheppo /*
512e1ebb9ecSlm66018  * LDC Link Layer Internal Functions
5131ae08745Sheppo  */
5141ae08745Sheppo 
5151ae08745Sheppo /*
5161ae08745Sheppo  * Translate HV Errors to sun4v error codes
5171ae08745Sheppo  */
5181ae08745Sheppo static int
5191ae08745Sheppo i_ldc_h2v_error(int h_error)
5201ae08745Sheppo {
5211ae08745Sheppo 	switch (h_error) {
5221ae08745Sheppo 
5231ae08745Sheppo 	case	H_EOK:
5241ae08745Sheppo 		return (0);
5251ae08745Sheppo 
5261ae08745Sheppo 	case	H_ENORADDR:
5271ae08745Sheppo 		return (EFAULT);
5281ae08745Sheppo 
5291ae08745Sheppo 	case	H_EBADPGSZ:
5301ae08745Sheppo 	case	H_EINVAL:
5311ae08745Sheppo 		return (EINVAL);
5321ae08745Sheppo 
5331ae08745Sheppo 	case	H_EWOULDBLOCK:
5341ae08745Sheppo 		return (EWOULDBLOCK);
5351ae08745Sheppo 
5361ae08745Sheppo 	case	H_ENOACCESS:
5371ae08745Sheppo 	case	H_ENOMAP:
5381ae08745Sheppo 		return (EACCES);
5391ae08745Sheppo 
5401ae08745Sheppo 	case	H_EIO:
5411ae08745Sheppo 	case	H_ECPUERROR:
5421ae08745Sheppo 		return (EIO);
5431ae08745Sheppo 
5441ae08745Sheppo 	case	H_ENOTSUPPORTED:
5451ae08745Sheppo 		return (ENOTSUP);
5461ae08745Sheppo 
5471ae08745Sheppo 	case 	H_ETOOMANY:
5481ae08745Sheppo 		return (ENOSPC);
5491ae08745Sheppo 
5501ae08745Sheppo 	case	H_ECHANNEL:
5511ae08745Sheppo 		return (ECHRNG);
5521ae08745Sheppo 	default:
5531ae08745Sheppo 		break;
5541ae08745Sheppo 	}
5551ae08745Sheppo 
5561ae08745Sheppo 	return (EIO);
5571ae08745Sheppo }
5581ae08745Sheppo 
5591ae08745Sheppo /*
5601ae08745Sheppo  * Reconfigure the transmit queue
5611ae08745Sheppo  */
5621ae08745Sheppo static int
5631ae08745Sheppo i_ldc_txq_reconf(ldc_chan_t *ldcp)
5641ae08745Sheppo {
5651ae08745Sheppo 	int rv;
5661ae08745Sheppo 
5671ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
568d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
569d10e4ef2Snarayan 
5701ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
5711ae08745Sheppo 	if (rv) {
5721ae08745Sheppo 		cmn_err(CE_WARN,
5733af08d82Slm66018 		    "i_ldc_txq_reconf: (0x%lx) cannot set qconf", ldcp->id);
5741ae08745Sheppo 		return (EIO);
5751ae08745Sheppo 	}
5761ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id, &(ldcp->tx_head),
5771ae08745Sheppo 	    &(ldcp->tx_tail), &(ldcp->link_state));
5781ae08745Sheppo 	if (rv) {
5791ae08745Sheppo 		cmn_err(CE_WARN,
5803af08d82Slm66018 		    "i_ldc_txq_reconf: (0x%lx) cannot get qptrs", ldcp->id);
5811ae08745Sheppo 		return (EIO);
5821ae08745Sheppo 	}
5833af08d82Slm66018 	D1(ldcp->id, "i_ldc_txq_reconf: (0x%llx) h=0x%llx,t=0x%llx,"
5841ae08745Sheppo 	    "s=0x%llx\n", ldcp->id, ldcp->tx_head, ldcp->tx_tail,
5851ae08745Sheppo 	    ldcp->link_state);
5861ae08745Sheppo 
5871ae08745Sheppo 	return (0);
5881ae08745Sheppo }
5891ae08745Sheppo 
5901ae08745Sheppo /*
5911ae08745Sheppo  * Reconfigure the receive queue
5921ae08745Sheppo  */
5931ae08745Sheppo static int
5943af08d82Slm66018 i_ldc_rxq_reconf(ldc_chan_t *ldcp, boolean_t force_reset)
5951ae08745Sheppo {
5961ae08745Sheppo 	int rv;
5971ae08745Sheppo 	uint64_t rx_head, rx_tail;
5981ae08745Sheppo 
5991ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
6001ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
6011ae08745Sheppo 	    &(ldcp->link_state));
6021ae08745Sheppo 	if (rv) {
6031ae08745Sheppo 		cmn_err(CE_WARN,
6043af08d82Slm66018 		    "i_ldc_rxq_reconf: (0x%lx) cannot get state",
6051ae08745Sheppo 		    ldcp->id);
6061ae08745Sheppo 		return (EIO);
6071ae08745Sheppo 	}
6081ae08745Sheppo 
6093af08d82Slm66018 	if (force_reset || (ldcp->tstate & ~TS_IN_RESET) == TS_UP) {
6101ae08745Sheppo 		rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra,
6111ae08745Sheppo 		    ldcp->rx_q_entries);
6121ae08745Sheppo 		if (rv) {
6131ae08745Sheppo 			cmn_err(CE_WARN,
6143af08d82Slm66018 			    "i_ldc_rxq_reconf: (0x%lx) cannot set qconf",
6151ae08745Sheppo 			    ldcp->id);
6161ae08745Sheppo 			return (EIO);
6171ae08745Sheppo 		}
6183af08d82Slm66018 		D1(ldcp->id, "i_ldc_rxq_reconf: (0x%llx) completed q reconf",
6191ae08745Sheppo 		    ldcp->id);
6201ae08745Sheppo 	}
6211ae08745Sheppo 
6221ae08745Sheppo 	return (0);
6231ae08745Sheppo }
6241ae08745Sheppo 
625a8ea4edeSnarayan 
626a8ea4edeSnarayan /*
627a8ea4edeSnarayan  * Drain the contents of the receive queue
628a8ea4edeSnarayan  */
629a8ea4edeSnarayan static int
630a8ea4edeSnarayan i_ldc_rxq_drain(ldc_chan_t *ldcp)
631a8ea4edeSnarayan {
632a8ea4edeSnarayan 	int rv;
633a8ea4edeSnarayan 	uint64_t rx_head, rx_tail;
634a8ea4edeSnarayan 
635a8ea4edeSnarayan 	ASSERT(MUTEX_HELD(&ldcp->lock));
636a8ea4edeSnarayan 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
637a8ea4edeSnarayan 	    &(ldcp->link_state));
638a8ea4edeSnarayan 	if (rv) {
639a8ea4edeSnarayan 		cmn_err(CE_WARN, "i_ldc_rxq_drain: (0x%lx) cannot get state",
640a8ea4edeSnarayan 		    ldcp->id);
641a8ea4edeSnarayan 		return (EIO);
642a8ea4edeSnarayan 	}
643a8ea4edeSnarayan 
644a8ea4edeSnarayan 	/* flush contents by setting the head = tail */
645a8ea4edeSnarayan 	return (i_ldc_set_rx_head(ldcp, rx_tail));
646a8ea4edeSnarayan }
647a8ea4edeSnarayan 
648a8ea4edeSnarayan 
6491ae08745Sheppo /*
6501ae08745Sheppo  * Reset LDC state structure and its contents
6511ae08745Sheppo  */
6521ae08745Sheppo static void
6531ae08745Sheppo i_ldc_reset_state(ldc_chan_t *ldcp)
6541ae08745Sheppo {
6551ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
6561ae08745Sheppo 	ldcp->last_msg_snt = LDC_INIT_SEQID;
6571ae08745Sheppo 	ldcp->last_ack_rcd = 0;
6581ae08745Sheppo 	ldcp->last_msg_rcd = 0;
6591ae08745Sheppo 	ldcp->tx_ackd_head = ldcp->tx_head;
660*58283286Sha137994 	ldcp->stream_remains = 0;
6611ae08745Sheppo 	ldcp->next_vidx = 0;
6621ae08745Sheppo 	ldcp->hstate = 0;
6631ae08745Sheppo 	ldcp->tstate = TS_OPEN;
6641ae08745Sheppo 	ldcp->status = LDC_OPEN;
665*58283286Sha137994 	ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
666*58283286Sha137994 	ldcp->rx_dq_head = 0;
667*58283286Sha137994 	ldcp->rx_dq_tail = 0;
6681ae08745Sheppo 
6691ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_UP ||
6701ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
6711ae08745Sheppo 
6721ae08745Sheppo 		if (ldcp->mode == LDC_MODE_RAW) {
6731ae08745Sheppo 			ldcp->status = LDC_UP;
6741ae08745Sheppo 			ldcp->tstate = TS_UP;
6751ae08745Sheppo 		} else {
6761ae08745Sheppo 			ldcp->status = LDC_READY;
6771ae08745Sheppo 			ldcp->tstate |= TS_LINK_READY;
6781ae08745Sheppo 		}
6791ae08745Sheppo 	}
6801ae08745Sheppo }
6811ae08745Sheppo 
6821ae08745Sheppo /*
6831ae08745Sheppo  * Reset a LDC channel
6841ae08745Sheppo  */
6851ae08745Sheppo static void
6863af08d82Slm66018 i_ldc_reset(ldc_chan_t *ldcp, boolean_t force_reset)
6871ae08745Sheppo {
68883d3bc6fSnarayan 	DWARN(ldcp->id, "i_ldc_reset: (0x%llx) channel reset\n", ldcp->id);
6891ae08745Sheppo 
690d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->lock));
691d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
692d10e4ef2Snarayan 
6933af08d82Slm66018 	/* reconfig Tx and Rx queues */
6941ae08745Sheppo 	(void) i_ldc_txq_reconf(ldcp);
6953af08d82Slm66018 	(void) i_ldc_rxq_reconf(ldcp, force_reset);
6963af08d82Slm66018 
6973af08d82Slm66018 	/* Clear Tx and Rx interrupts */
6983af08d82Slm66018 	(void) i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
6993af08d82Slm66018 	(void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
7003af08d82Slm66018 
7013af08d82Slm66018 	/* Reset channel state */
7021ae08745Sheppo 	i_ldc_reset_state(ldcp);
7033af08d82Slm66018 
7043af08d82Slm66018 	/* Mark channel in reset */
7053af08d82Slm66018 	ldcp->tstate |= TS_IN_RESET;
7061ae08745Sheppo }
7071ae08745Sheppo 
7084bac2208Snarayan 
7091ae08745Sheppo /*
7101ae08745Sheppo  * Clear pending interrupts
7111ae08745Sheppo  */
7121ae08745Sheppo static void
7131ae08745Sheppo i_ldc_clear_intr(ldc_chan_t *ldcp, cnex_intrtype_t itype)
7141ae08745Sheppo {
7151ae08745Sheppo 	ldc_cnex_t *cinfo = &ldcssp->cinfo;
7161ae08745Sheppo 
7171ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
7183af08d82Slm66018 	ASSERT(cinfo->dip != NULL);
7194bac2208Snarayan 
7203af08d82Slm66018 	switch (itype) {
7213af08d82Slm66018 	case CNEX_TX_INTR:
7224bac2208Snarayan 		/* check Tx interrupt */
7233af08d82Slm66018 		if (ldcp->tx_intr_state)
7243af08d82Slm66018 			ldcp->tx_intr_state = LDC_INTR_NONE;
7254bac2208Snarayan 		else
7264bac2208Snarayan 			return;
7273af08d82Slm66018 		break;
7283af08d82Slm66018 
7293af08d82Slm66018 	case CNEX_RX_INTR:
7304bac2208Snarayan 		/* check Rx interrupt */
7313af08d82Slm66018 		if (ldcp->rx_intr_state)
7323af08d82Slm66018 			ldcp->rx_intr_state = LDC_INTR_NONE;
7334bac2208Snarayan 		else
7344bac2208Snarayan 			return;
7353af08d82Slm66018 		break;
7364bac2208Snarayan 	}
7374bac2208Snarayan 
7381ae08745Sheppo 	(void) cinfo->clr_intr(cinfo->dip, ldcp->id, itype);
7394bac2208Snarayan 	D2(ldcp->id,
7404bac2208Snarayan 	    "i_ldc_clear_intr: (0x%llx) cleared 0x%x intr\n",
7414bac2208Snarayan 	    ldcp->id, itype);
7421ae08745Sheppo }
7431ae08745Sheppo 
7441ae08745Sheppo /*
7451ae08745Sheppo  * Set the receive queue head
7460a55fbb7Slm66018  * Resets connection and returns an error if it fails.
7471ae08745Sheppo  */
7481ae08745Sheppo static int
7491ae08745Sheppo i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head)
7501ae08745Sheppo {
7511ae08745Sheppo 	int 	rv;
7520a55fbb7Slm66018 	int 	retries;
7531ae08745Sheppo 
7541ae08745Sheppo 	ASSERT(MUTEX_HELD(&ldcp->lock));
7550a55fbb7Slm66018 	for (retries = 0; retries < ldc_max_retries; retries++) {
7560a55fbb7Slm66018 
7570a55fbb7Slm66018 		if ((rv = hv_ldc_rx_set_qhead(ldcp->id, head)) == 0)
7580a55fbb7Slm66018 			return (0);
7590a55fbb7Slm66018 
7600a55fbb7Slm66018 		if (rv != H_EWOULDBLOCK)
7610a55fbb7Slm66018 			break;
7620a55fbb7Slm66018 
7630a55fbb7Slm66018 		/* wait for ldc_delay usecs */
7640a55fbb7Slm66018 		drv_usecwait(ldc_delay);
7651ae08745Sheppo 	}
7661ae08745Sheppo 
7670a55fbb7Slm66018 	cmn_err(CE_WARN, "ldc_rx_set_qhead: (0x%lx) cannot set qhead 0x%lx",
7680a55fbb7Slm66018 	    ldcp->id, head);
769d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
7703af08d82Slm66018 	i_ldc_reset(ldcp, B_TRUE);
771d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
7720a55fbb7Slm66018 
7730a55fbb7Slm66018 	return (ECONNRESET);
7741ae08745Sheppo }
7751ae08745Sheppo 
77622f747efSnarayan /*
77722f747efSnarayan  * Returns the tx_head to be used for transfer
77822f747efSnarayan  */
77922f747efSnarayan static void
78022f747efSnarayan i_ldc_get_tx_head(ldc_chan_t *ldcp, uint64_t *head)
78122f747efSnarayan {
78222f747efSnarayan 	ldc_msg_t 	*pkt;
78322f747efSnarayan 
78422f747efSnarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
78522f747efSnarayan 
78622f747efSnarayan 	/* get current Tx head */
78722f747efSnarayan 	*head = ldcp->tx_head;
78822f747efSnarayan 
78922f747efSnarayan 	/*
79022f747efSnarayan 	 * Reliable mode will use the ACKd head instead of the regular tx_head.
79122f747efSnarayan 	 * Also in Reliable mode, advance ackd_head for all non DATA/INFO pkts,
79222f747efSnarayan 	 * up to the current location of tx_head. This needs to be done
79322f747efSnarayan 	 * as the peer will only ACK DATA/INFO pkts.
79422f747efSnarayan 	 */
79522f747efSnarayan 	if (ldcp->mode == LDC_MODE_RELIABLE || ldcp->mode == LDC_MODE_STREAM) {
79622f747efSnarayan 		while (ldcp->tx_ackd_head != ldcp->tx_head) {
79722f747efSnarayan 			pkt = (ldc_msg_t *)(ldcp->tx_q_va + ldcp->tx_ackd_head);
79822f747efSnarayan 			if ((pkt->type & LDC_DATA) && (pkt->stype & LDC_INFO)) {
79922f747efSnarayan 				break;
80022f747efSnarayan 			}
80122f747efSnarayan 			/* advance ACKd head */
80222f747efSnarayan 			ldcp->tx_ackd_head =
80322f747efSnarayan 			    (ldcp->tx_ackd_head + LDC_PACKET_SIZE) %
80422f747efSnarayan 			    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
80522f747efSnarayan 		}
80622f747efSnarayan 		*head = ldcp->tx_ackd_head;
80722f747efSnarayan 	}
80822f747efSnarayan }
8091ae08745Sheppo 
8101ae08745Sheppo /*
8111ae08745Sheppo  * Returns the tx_tail to be used for transfer
8121ae08745Sheppo  * Re-reads the TX queue ptrs if and only if the
8131ae08745Sheppo  * the cached head and tail are equal (queue is full)
8141ae08745Sheppo  */
8151ae08745Sheppo static int
8161ae08745Sheppo i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail)
8171ae08745Sheppo {
8181ae08745Sheppo 	int 		rv;
8191ae08745Sheppo 	uint64_t 	current_head, new_tail;
8201ae08745Sheppo 
821d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
8221ae08745Sheppo 	/* Read the head and tail ptrs from HV */
8231ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
8241ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
8251ae08745Sheppo 	if (rv) {
8261ae08745Sheppo 		cmn_err(CE_WARN,
8271ae08745Sheppo 		    "i_ldc_get_tx_tail: (0x%lx) cannot read qptrs\n",
8281ae08745Sheppo 		    ldcp->id);
8291ae08745Sheppo 		return (EIO);
8301ae08745Sheppo 	}
8311ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN) {
832cb112a14Slm66018 		D1(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) channel not ready\n",
8331ae08745Sheppo 		    ldcp->id);
8341ae08745Sheppo 		return (ECONNRESET);
8351ae08745Sheppo 	}
8361ae08745Sheppo 
83722f747efSnarayan 	i_ldc_get_tx_head(ldcp, &current_head);
8381ae08745Sheppo 
8391ae08745Sheppo 	/* increment the tail */
8401ae08745Sheppo 	new_tail = (ldcp->tx_tail + LDC_PACKET_SIZE) %
8411ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
8421ae08745Sheppo 
8431ae08745Sheppo 	if (new_tail == current_head) {
8441ae08745Sheppo 		DWARN(ldcp->id,
8451ae08745Sheppo 		    "i_ldc_get_tx_tail: (0x%llx) TX queue is full\n",
8461ae08745Sheppo 		    ldcp->id);
8471ae08745Sheppo 		return (EWOULDBLOCK);
8481ae08745Sheppo 	}
8491ae08745Sheppo 
8501ae08745Sheppo 	D2(ldcp->id, "i_ldc_get_tx_tail: (0x%llx) head=0x%llx, tail=0x%llx\n",
8511ae08745Sheppo 	    ldcp->id, ldcp->tx_head, ldcp->tx_tail);
8521ae08745Sheppo 
8531ae08745Sheppo 	*tail = ldcp->tx_tail;
8541ae08745Sheppo 	return (0);
8551ae08745Sheppo }
8561ae08745Sheppo 
8571ae08745Sheppo /*
8581ae08745Sheppo  * Set the tail pointer. If HV returns EWOULDBLOCK, it will back off
8590a55fbb7Slm66018  * and retry ldc_max_retries times before returning an error.
8601ae08745Sheppo  * Returns 0, EWOULDBLOCK or EIO
8611ae08745Sheppo  */
8621ae08745Sheppo static int
8631ae08745Sheppo i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail)
8641ae08745Sheppo {
8651ae08745Sheppo 	int		rv, retval = EWOULDBLOCK;
8660a55fbb7Slm66018 	int 		retries;
8671ae08745Sheppo 
868d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
8690a55fbb7Slm66018 	for (retries = 0; retries < ldc_max_retries; retries++) {
8701ae08745Sheppo 
8711ae08745Sheppo 		if ((rv = hv_ldc_tx_set_qtail(ldcp->id, tail)) == 0) {
8721ae08745Sheppo 			retval = 0;
8731ae08745Sheppo 			break;
8741ae08745Sheppo 		}
8751ae08745Sheppo 		if (rv != H_EWOULDBLOCK) {
8761ae08745Sheppo 			DWARN(ldcp->id, "i_ldc_set_tx_tail: (0x%llx) set "
8771ae08745Sheppo 			    "qtail=0x%llx failed, rv=%d\n", ldcp->id, tail, rv);
8781ae08745Sheppo 			retval = EIO;
8791ae08745Sheppo 			break;
8801ae08745Sheppo 		}
8811ae08745Sheppo 
8820a55fbb7Slm66018 		/* wait for ldc_delay usecs */
8830a55fbb7Slm66018 		drv_usecwait(ldc_delay);
8841ae08745Sheppo 	}
8851ae08745Sheppo 	return (retval);
8861ae08745Sheppo }
8871ae08745Sheppo 
8881ae08745Sheppo /*
889*58283286Sha137994  * Copy a data packet from the HV receive queue to the data queue.
890*58283286Sha137994  * Caller must ensure that the data queue is not already full.
891*58283286Sha137994  *
892*58283286Sha137994  * The *head argument represents the current head pointer for the HV
893*58283286Sha137994  * receive queue. After copying a packet from the HV receive queue,
894*58283286Sha137994  * the *head pointer will be updated. This allows the caller to update
895*58283286Sha137994  * the head pointer in HV using the returned *head value.
896*58283286Sha137994  */
897*58283286Sha137994 void
898*58283286Sha137994 i_ldc_rxdq_copy(ldc_chan_t *ldcp, uint64_t *head)
899*58283286Sha137994 {
900*58283286Sha137994 	uint64_t	q_size, dq_size;
901*58283286Sha137994 
902*58283286Sha137994 	ASSERT(MUTEX_HELD(&ldcp->lock));
903*58283286Sha137994 
904*58283286Sha137994 	q_size  = ldcp->rx_q_entries << LDC_PACKET_SHIFT;
905*58283286Sha137994 	dq_size = ldcp->rx_dq_entries << LDC_PACKET_SHIFT;
906*58283286Sha137994 
907*58283286Sha137994 	ASSERT(Q_CONTIG_SPACE(ldcp->rx_dq_head, ldcp->rx_dq_tail,
908*58283286Sha137994 	    dq_size) >= LDC_PACKET_SIZE);
909*58283286Sha137994 
910*58283286Sha137994 	bcopy((void *)(ldcp->rx_q_va + *head),
911*58283286Sha137994 	    (void *)(ldcp->rx_dq_va + ldcp->rx_dq_tail), LDC_PACKET_SIZE);
912*58283286Sha137994 	TRACE_RXDQ_COPY(ldcp, LDC_PACKET_SIZE);
913*58283286Sha137994 
914*58283286Sha137994 	/* Update rx head */
915*58283286Sha137994 	*head = (*head + LDC_PACKET_SIZE) % q_size;
916*58283286Sha137994 
917*58283286Sha137994 	/* Update dq tail */
918*58283286Sha137994 	ldcp->rx_dq_tail = (ldcp->rx_dq_tail + LDC_PACKET_SIZE) % dq_size;
919*58283286Sha137994 }
920*58283286Sha137994 
921*58283286Sha137994 /*
922*58283286Sha137994  * Update the Rx data queue head pointer
923*58283286Sha137994  */
924*58283286Sha137994 static int
925*58283286Sha137994 i_ldc_set_rxdq_head(ldc_chan_t *ldcp, uint64_t head)
926*58283286Sha137994 {
927*58283286Sha137994 	ldcp->rx_dq_head = head;
928*58283286Sha137994 	return (0);
929*58283286Sha137994 }
930*58283286Sha137994 
931*58283286Sha137994 /*
932*58283286Sha137994  * Get the Rx data queue head and tail pointers
933*58283286Sha137994  */
934*58283286Sha137994 static uint64_t
935*58283286Sha137994 i_ldc_dq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head, uint64_t *tail,
936*58283286Sha137994     uint64_t *link_state)
937*58283286Sha137994 {
938*58283286Sha137994 	_NOTE(ARGUNUSED(link_state))
939*58283286Sha137994 	*head = ldcp->rx_dq_head;
940*58283286Sha137994 	*tail = ldcp->rx_dq_tail;
941*58283286Sha137994 	return (0);
942*58283286Sha137994 }
943*58283286Sha137994 
944*58283286Sha137994 /*
945*58283286Sha137994  * Wrapper for the Rx HV queue set head function. Giving the
946*58283286Sha137994  * data queue and HV queue set head functions the same type.
947*58283286Sha137994  */
948*58283286Sha137994 static uint64_t
949*58283286Sha137994 i_ldc_hvq_rx_get_state(ldc_chan_t *ldcp, uint64_t *head, uint64_t *tail,
950*58283286Sha137994     uint64_t *link_state)
951*58283286Sha137994 {
952*58283286Sha137994 	return (i_ldc_h2v_error(hv_ldc_rx_get_state(ldcp->id, head, tail,
953*58283286Sha137994 	    link_state)));
954*58283286Sha137994 }
955*58283286Sha137994 
956*58283286Sha137994 /*
957*58283286Sha137994  * LDC receive interrupt handler
958*58283286Sha137994  *    triggered for channel with data pending to read
959*58283286Sha137994  *    i.e. Rx queue content changes
960*58283286Sha137994  */
961*58283286Sha137994 static uint_t
962*58283286Sha137994 i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2)
963*58283286Sha137994 {
964*58283286Sha137994 	_NOTE(ARGUNUSED(arg2))
965*58283286Sha137994 
966*58283286Sha137994 	ldc_chan_t	*ldcp;
967*58283286Sha137994 	boolean_t	notify;
968*58283286Sha137994 	uint64_t	event;
969*58283286Sha137994 	int		rv;
970*58283286Sha137994 
971*58283286Sha137994 	/* Get the channel for which interrupt was received */
972*58283286Sha137994 	if (arg1 == NULL) {
973*58283286Sha137994 		cmn_err(CE_WARN, "i_ldc_rx_hdlr: invalid arg\n");
974*58283286Sha137994 		return (DDI_INTR_UNCLAIMED);
975*58283286Sha137994 	}
976*58283286Sha137994 
977*58283286Sha137994 	ldcp = (ldc_chan_t *)arg1;
978*58283286Sha137994 
979*58283286Sha137994 	D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
980*58283286Sha137994 	    ldcp->id, ldcp);
981*58283286Sha137994 	D1(ldcp->id, "i_ldc_rx_hdlr: (%llx) USR%lx/TS%lx/HS%lx, LSTATE=%lx\n",
982*58283286Sha137994 	    ldcp->id, ldcp->status, ldcp->tstate, ldcp->hstate,
983*58283286Sha137994 	    ldcp->link_state);
984*58283286Sha137994 
985*58283286Sha137994 	/* Lock channel */
986*58283286Sha137994 	mutex_enter(&ldcp->lock);
987*58283286Sha137994 
988*58283286Sha137994 	/* Mark the interrupt as being actively handled */
989*58283286Sha137994 	ldcp->rx_intr_state = LDC_INTR_ACTIVE;
990*58283286Sha137994 
991*58283286Sha137994 	(void) i_ldc_rx_process_hvq(ldcp, &notify, &event);
992*58283286Sha137994 
993*58283286Sha137994 	if (ldcp->mode != LDC_MODE_STREAM) {
994*58283286Sha137994 		/*
995*58283286Sha137994 		 * If there are no data packets on the queue, clear
996*58283286Sha137994 		 * the interrupt. Otherwise, the ldc_read will clear
997*58283286Sha137994 		 * interrupts after draining the queue. To indicate the
998*58283286Sha137994 		 * interrupt has not yet been cleared, it is marked
999*58283286Sha137994 		 * as pending.
1000*58283286Sha137994 		 */
1001*58283286Sha137994 		if ((event & LDC_EVT_READ) == 0) {
1002*58283286Sha137994 			i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
1003*58283286Sha137994 		} else {
1004*58283286Sha137994 			ldcp->rx_intr_state = LDC_INTR_PEND;
1005*58283286Sha137994 		}
1006*58283286Sha137994 	}
1007*58283286Sha137994 
1008*58283286Sha137994 	/* if callbacks are disabled, do not notify */
1009*58283286Sha137994 	if (notify && ldcp->cb_enabled) {
1010*58283286Sha137994 		ldcp->cb_inprogress = B_TRUE;
1011*58283286Sha137994 		mutex_exit(&ldcp->lock);
1012*58283286Sha137994 		rv = ldcp->cb(event, ldcp->cb_arg);
1013*58283286Sha137994 		if (rv) {
1014*58283286Sha137994 			DWARN(ldcp->id,
1015*58283286Sha137994 			    "i_ldc_rx_hdlr: (0x%llx) callback failure",
1016*58283286Sha137994 			    ldcp->id);
1017*58283286Sha137994 		}
1018*58283286Sha137994 		mutex_enter(&ldcp->lock);
1019*58283286Sha137994 		ldcp->cb_inprogress = B_FALSE;
1020*58283286Sha137994 	}
1021*58283286Sha137994 
1022*58283286Sha137994 	if (ldcp->mode == LDC_MODE_STREAM) {
1023*58283286Sha137994 		/*
1024*58283286Sha137994 		 * If we are using a secondary data queue, clear the
1025*58283286Sha137994 		 * interrupt. We should have processed all CTRL packets
1026*58283286Sha137994 		 * and copied all DATA packets to the secondary queue.
1027*58283286Sha137994 		 * Even if secondary queue filled up, clear the interrupts,
1028*58283286Sha137994 		 * this will trigger another interrupt and force the
1029*58283286Sha137994 		 * handler to copy more data.
1030*58283286Sha137994 		 */
1031*58283286Sha137994 		i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
1032*58283286Sha137994 	}
1033*58283286Sha137994 
1034*58283286Sha137994 	mutex_exit(&ldcp->lock);
1035*58283286Sha137994 
1036*58283286Sha137994 	D1(ldcp->id, "i_ldc_rx_hdlr: (0x%llx) exiting handler", ldcp->id);
1037*58283286Sha137994 
1038*58283286Sha137994 	return (DDI_INTR_CLAIMED);
1039*58283286Sha137994 }
1040*58283286Sha137994 
1041*58283286Sha137994 /*
1042*58283286Sha137994  * Wrapper for the Rx HV queue processing function to be used when
1043*58283286Sha137994  * checking the Rx HV queue for data packets. Unlike the interrupt
1044*58283286Sha137994  * handler code flow, the Rx interrupt is not cleared here and
1045*58283286Sha137994  * callbacks are not made.
1046*58283286Sha137994  */
1047*58283286Sha137994 static uint_t
1048*58283286Sha137994 i_ldc_chkq(ldc_chan_t *ldcp)
1049*58283286Sha137994 {
1050*58283286Sha137994 	boolean_t	notify;
1051*58283286Sha137994 	uint64_t	event;
1052*58283286Sha137994 
1053*58283286Sha137994 	return (i_ldc_rx_process_hvq(ldcp, &notify, &event));
1054*58283286Sha137994 }
1055*58283286Sha137994 
1056*58283286Sha137994 /*
10571ae08745Sheppo  * Send a LDC message
10581ae08745Sheppo  */
10591ae08745Sheppo static int
10601ae08745Sheppo i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype,
10611ae08745Sheppo     uint8_t ctrlmsg)
10621ae08745Sheppo {
10631ae08745Sheppo 	int		rv;
10641ae08745Sheppo 	ldc_msg_t 	*pkt;
10651ae08745Sheppo 	uint64_t	tx_tail;
106622f747efSnarayan 	uint32_t	curr_seqid;
10671ae08745Sheppo 
1068d10e4ef2Snarayan 	/* Obtain Tx lock */
1069d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
1070d10e4ef2Snarayan 
107122f747efSnarayan 	curr_seqid = ldcp->last_msg_snt;
107222f747efSnarayan 
10731ae08745Sheppo 	/* get the current tail for the message */
10741ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
10751ae08745Sheppo 	if (rv) {
10761ae08745Sheppo 		DWARN(ldcp->id,
10771ae08745Sheppo 		    "i_ldc_send_pkt: (0x%llx) error sending pkt, "
10781ae08745Sheppo 		    "type=0x%x,subtype=0x%x,ctrl=0x%x\n",
10791ae08745Sheppo 		    ldcp->id, pkttype, subtype, ctrlmsg);
1080d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
10811ae08745Sheppo 		return (rv);
10821ae08745Sheppo 	}
10831ae08745Sheppo 
10841ae08745Sheppo 	pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
10851ae08745Sheppo 	ZERO_PKT(pkt);
10861ae08745Sheppo 
10871ae08745Sheppo 	/* Initialize the packet */
10881ae08745Sheppo 	pkt->type = pkttype;
10891ae08745Sheppo 	pkt->stype = subtype;
10901ae08745Sheppo 	pkt->ctrl = ctrlmsg;
10911ae08745Sheppo 
10921ae08745Sheppo 	/* Store ackid/seqid iff it is RELIABLE mode & not a RTS/RTR message */
10931ae08745Sheppo 	if (((ctrlmsg & LDC_CTRL_MASK) != LDC_RTS) &&
10941ae08745Sheppo 	    ((ctrlmsg & LDC_CTRL_MASK) != LDC_RTR)) {
10951ae08745Sheppo 		curr_seqid++;
10961ae08745Sheppo 		if (ldcp->mode != LDC_MODE_RAW) {
10971ae08745Sheppo 			pkt->seqid = curr_seqid;
10981ae08745Sheppo 			pkt->ackid = ldcp->last_msg_rcd;
10991ae08745Sheppo 		}
11001ae08745Sheppo 	}
11011ae08745Sheppo 	DUMP_LDC_PKT(ldcp, "i_ldc_send_pkt", (uint64_t)pkt);
11021ae08745Sheppo 
11031ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
11041ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
11051ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
11061ae08745Sheppo 
11071ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
11081ae08745Sheppo 	if (rv) {
11091ae08745Sheppo 		DWARN(ldcp->id,
11101ae08745Sheppo 		    "i_ldc_send_pkt:(0x%llx) error sending pkt, "
11111ae08745Sheppo 		    "type=0x%x,stype=0x%x,ctrl=0x%x\n",
11121ae08745Sheppo 		    ldcp->id, pkttype, subtype, ctrlmsg);
1113d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
11141ae08745Sheppo 		return (EIO);
11151ae08745Sheppo 	}
11161ae08745Sheppo 
11171ae08745Sheppo 	ldcp->last_msg_snt = curr_seqid;
11181ae08745Sheppo 	ldcp->tx_tail = tx_tail;
11191ae08745Sheppo 
1120d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
11211ae08745Sheppo 	return (0);
11221ae08745Sheppo }
11231ae08745Sheppo 
11241ae08745Sheppo /*
11251ae08745Sheppo  * Checks if packet was received in right order
1126e1ebb9ecSlm66018  * in the case of a reliable link.
11271ae08745Sheppo  * Returns 0 if in order, else EIO
11281ae08745Sheppo  */
11291ae08745Sheppo static int
11301ae08745Sheppo i_ldc_check_seqid(ldc_chan_t *ldcp, ldc_msg_t *msg)
11311ae08745Sheppo {
11321ae08745Sheppo 	/* No seqid checking for RAW mode */
11331ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW)
11341ae08745Sheppo 		return (0);
11351ae08745Sheppo 
11361ae08745Sheppo 	/* No seqid checking for version, RTS, RTR message */
11371ae08745Sheppo 	if (msg->ctrl == LDC_VER ||
11381ae08745Sheppo 	    msg->ctrl == LDC_RTS ||
11391ae08745Sheppo 	    msg->ctrl == LDC_RTR)
11401ae08745Sheppo 		return (0);
11411ae08745Sheppo 
11421ae08745Sheppo 	/* Initial seqid to use is sent in RTS/RTR and saved in last_msg_rcd */
11431ae08745Sheppo 	if (msg->seqid != (ldcp->last_msg_rcd + 1)) {
11441ae08745Sheppo 		DWARN(ldcp->id,
11451ae08745Sheppo 		    "i_ldc_check_seqid: (0x%llx) out-of-order pkt, got 0x%x, "
11461ae08745Sheppo 		    "expecting 0x%x\n", ldcp->id, msg->seqid,
11471ae08745Sheppo 		    (ldcp->last_msg_rcd + 1));
11481ae08745Sheppo 		return (EIO);
11491ae08745Sheppo 	}
11501ae08745Sheppo 
115183d3bc6fSnarayan #ifdef DEBUG
115283d3bc6fSnarayan 	if (LDC_INJECT_PKTLOSS(ldcp)) {
115383d3bc6fSnarayan 		DWARN(ldcp->id,
115483d3bc6fSnarayan 		    "i_ldc_check_seqid: (0x%llx) inject pkt loss\n", ldcp->id);
115583d3bc6fSnarayan 		return (EIO);
115683d3bc6fSnarayan 	}
115783d3bc6fSnarayan #endif
115883d3bc6fSnarayan 
11591ae08745Sheppo 	return (0);
11601ae08745Sheppo }
11611ae08745Sheppo 
11621ae08745Sheppo 
11631ae08745Sheppo /*
11641ae08745Sheppo  * Process an incoming version ctrl message
11651ae08745Sheppo  */
11661ae08745Sheppo static int
11671ae08745Sheppo i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg)
11681ae08745Sheppo {
11691ae08745Sheppo 	int 		rv = 0, idx = ldcp->next_vidx;
11701ae08745Sheppo 	ldc_msg_t 	*pkt;
11711ae08745Sheppo 	uint64_t	tx_tail;
11721ae08745Sheppo 	ldc_ver_t	*rcvd_ver;
11731ae08745Sheppo 
11741ae08745Sheppo 	/* get the received version */
11751ae08745Sheppo 	rcvd_ver = (ldc_ver_t *)((uint64_t)msg + LDC_PAYLOAD_VER_OFF);
11761ae08745Sheppo 
11771ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_VER: (0x%llx) received VER v%u.%u\n",
11781ae08745Sheppo 	    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
11791ae08745Sheppo 
1180d10e4ef2Snarayan 	/* Obtain Tx lock */
1181d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
1182d10e4ef2Snarayan 
11831ae08745Sheppo 	switch (msg->stype) {
11841ae08745Sheppo 	case LDC_INFO:
11851ae08745Sheppo 
11863af08d82Slm66018 		if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) {
11873af08d82Slm66018 			(void) i_ldc_txq_reconf(ldcp);
11883af08d82Slm66018 			i_ldc_reset_state(ldcp);
11893af08d82Slm66018 			mutex_exit(&ldcp->tx_lock);
11903af08d82Slm66018 			return (EAGAIN);
11913af08d82Slm66018 		}
11923af08d82Slm66018 
11931ae08745Sheppo 		/* get the current tail and pkt for the response */
11941ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
11951ae08745Sheppo 		if (rv != 0) {
11961ae08745Sheppo 			DWARN(ldcp->id,
11971ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) err sending "
11981ae08745Sheppo 			    "version ACK/NACK\n", ldcp->id);
11993af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1200d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
12011ae08745Sheppo 			return (ECONNRESET);
12021ae08745Sheppo 		}
12031ae08745Sheppo 
12041ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
12051ae08745Sheppo 		ZERO_PKT(pkt);
12061ae08745Sheppo 
12071ae08745Sheppo 		/* initialize the packet */
12081ae08745Sheppo 		pkt->type = LDC_CTRL;
12091ae08745Sheppo 		pkt->ctrl = LDC_VER;
12101ae08745Sheppo 
12111ae08745Sheppo 		for (;;) {
12121ae08745Sheppo 
12131ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: got %u.%u chk %u.%u\n",
12141ae08745Sheppo 			    rcvd_ver->major, rcvd_ver->minor,
12151ae08745Sheppo 			    ldc_versions[idx].major, ldc_versions[idx].minor);
12161ae08745Sheppo 
12171ae08745Sheppo 			if (rcvd_ver->major == ldc_versions[idx].major) {
12181ae08745Sheppo 				/* major version match - ACK version */
12191ae08745Sheppo 				pkt->stype = LDC_ACK;
12201ae08745Sheppo 
12211ae08745Sheppo 				/*
12221ae08745Sheppo 				 * lower minor version to the one this endpt
12231ae08745Sheppo 				 * supports, if necessary
12241ae08745Sheppo 				 */
12251ae08745Sheppo 				if (rcvd_ver->minor > ldc_versions[idx].minor)
12261ae08745Sheppo 					rcvd_ver->minor =
12271ae08745Sheppo 					    ldc_versions[idx].minor;
12281ae08745Sheppo 				bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
12291ae08745Sheppo 
12301ae08745Sheppo 				break;
12311ae08745Sheppo 			}
12321ae08745Sheppo 
12331ae08745Sheppo 			if (rcvd_ver->major > ldc_versions[idx].major) {
12341ae08745Sheppo 
12351ae08745Sheppo 				D1(ldcp->id, "i_ldc_process_VER: using next"
12361ae08745Sheppo 				    " lower idx=%d, v%u.%u\n", idx,
12371ae08745Sheppo 				    ldc_versions[idx].major,
12381ae08745Sheppo 				    ldc_versions[idx].minor);
12391ae08745Sheppo 
12401ae08745Sheppo 				/* nack with next lower version */
12411ae08745Sheppo 				pkt->stype = LDC_NACK;
12421ae08745Sheppo 				bcopy(&ldc_versions[idx], pkt->udata,
12431ae08745Sheppo 				    sizeof (ldc_versions[idx]));
12441ae08745Sheppo 				ldcp->next_vidx = idx;
12451ae08745Sheppo 				break;
12461ae08745Sheppo 			}
12471ae08745Sheppo 
12481ae08745Sheppo 			/* next major version */
12491ae08745Sheppo 			idx++;
12501ae08745Sheppo 
12511ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
12521ae08745Sheppo 
12531ae08745Sheppo 			if (idx == LDC_NUM_VERS) {
12541ae08745Sheppo 				/* no version match - send NACK */
12551ae08745Sheppo 				pkt->stype = LDC_NACK;
12561ae08745Sheppo 				bzero(pkt->udata, sizeof (ldc_ver_t));
12571ae08745Sheppo 				ldcp->next_vidx = 0;
12581ae08745Sheppo 				break;
12591ae08745Sheppo 			}
12601ae08745Sheppo 		}
12611ae08745Sheppo 
12621ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
12631ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
12641ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
12651ae08745Sheppo 
12661ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
12671ae08745Sheppo 		if (rv == 0) {
12681ae08745Sheppo 			ldcp->tx_tail = tx_tail;
12691ae08745Sheppo 			if (pkt->stype == LDC_ACK) {
12701ae08745Sheppo 				D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent"
12711ae08745Sheppo 				    " version ACK\n", ldcp->id);
12721ae08745Sheppo 				/* Save the ACK'd version */
12731ae08745Sheppo 				ldcp->version.major = rcvd_ver->major;
12741ae08745Sheppo 				ldcp->version.minor = rcvd_ver->minor;
12750a55fbb7Slm66018 				ldcp->hstate |= TS_RCVD_VER;
12761ae08745Sheppo 				ldcp->tstate |= TS_VER_DONE;
127783d3bc6fSnarayan 				D1(DBG_ALL_LDCS,
12783af08d82Slm66018 				    "(0x%llx) Sent ACK, "
12793af08d82Slm66018 				    "Agreed on version v%u.%u\n",
12801ae08745Sheppo 				    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
12811ae08745Sheppo 			}
12821ae08745Sheppo 		} else {
12831ae08745Sheppo 			DWARN(ldcp->id,
12841ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) error sending "
12851ae08745Sheppo 			    "ACK/NACK\n", ldcp->id);
12863af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1287d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
12881ae08745Sheppo 			return (ECONNRESET);
12891ae08745Sheppo 		}
12901ae08745Sheppo 
12911ae08745Sheppo 		break;
12921ae08745Sheppo 
12931ae08745Sheppo 	case LDC_ACK:
12943af08d82Slm66018 		if ((ldcp->tstate & ~TS_IN_RESET) == TS_VREADY) {
12953af08d82Slm66018 			if (ldcp->version.major != rcvd_ver->major ||
12963af08d82Slm66018 			    ldcp->version.minor != rcvd_ver->minor) {
12973af08d82Slm66018 
12983af08d82Slm66018 				/* mismatched version - reset connection */
12993af08d82Slm66018 				DWARN(ldcp->id,
13003af08d82Slm66018 				    "i_ldc_process_VER: (0x%llx) recvd"
13013af08d82Slm66018 				    " ACK ver != sent ACK ver\n", ldcp->id);
13023af08d82Slm66018 				i_ldc_reset(ldcp, B_TRUE);
13033af08d82Slm66018 				mutex_exit(&ldcp->tx_lock);
13043af08d82Slm66018 				return (ECONNRESET);
13053af08d82Slm66018 			}
13063af08d82Slm66018 		} else {
13071ae08745Sheppo 			/* SUCCESS - we have agreed on a version */
13081ae08745Sheppo 			ldcp->version.major = rcvd_ver->major;
13091ae08745Sheppo 			ldcp->version.minor = rcvd_ver->minor;
13101ae08745Sheppo 			ldcp->tstate |= TS_VER_DONE;
13113af08d82Slm66018 		}
13121ae08745Sheppo 
1313cb112a14Slm66018 		D1(ldcp->id, "(0x%llx) Got ACK, Agreed on version v%u.%u\n",
13141ae08745Sheppo 		    ldcp->id, rcvd_ver->major, rcvd_ver->minor);
13151ae08745Sheppo 
13161ae08745Sheppo 		/* initiate RTS-RTR-RDX handshake */
13171ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
13181ae08745Sheppo 		if (rv) {
13191ae08745Sheppo 			DWARN(ldcp->id,
13201ae08745Sheppo 		    "i_ldc_process_VER: (0x%llx) cannot send RTS\n",
13211ae08745Sheppo 			    ldcp->id);
13223af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1323d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
13241ae08745Sheppo 			return (ECONNRESET);
13251ae08745Sheppo 		}
13261ae08745Sheppo 
13271ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
13281ae08745Sheppo 		ZERO_PKT(pkt);
13291ae08745Sheppo 
13301ae08745Sheppo 		pkt->type = LDC_CTRL;
13311ae08745Sheppo 		pkt->stype = LDC_INFO;
13321ae08745Sheppo 		pkt->ctrl = LDC_RTS;
13331ae08745Sheppo 		pkt->env = ldcp->mode;
13341ae08745Sheppo 		if (ldcp->mode != LDC_MODE_RAW)
13351ae08745Sheppo 			pkt->seqid = LDC_INIT_SEQID;
13361ae08745Sheppo 
13371ae08745Sheppo 		ldcp->last_msg_rcd = LDC_INIT_SEQID;
13381ae08745Sheppo 
13391ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "i_ldc_process_VER snd rts", (uint64_t)pkt);
13401ae08745Sheppo 
13411ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
13421ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
13431ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
13441ae08745Sheppo 
13451ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
13461ae08745Sheppo 		if (rv) {
13471ae08745Sheppo 			D2(ldcp->id,
13481ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) no listener\n",
13491ae08745Sheppo 			    ldcp->id);
13503af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1351d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
13521ae08745Sheppo 			return (ECONNRESET);
13531ae08745Sheppo 		}
13541ae08745Sheppo 
13551ae08745Sheppo 		ldcp->tx_tail = tx_tail;
13561ae08745Sheppo 		ldcp->hstate |= TS_SENT_RTS;
13571ae08745Sheppo 
13581ae08745Sheppo 		break;
13591ae08745Sheppo 
13601ae08745Sheppo 	case LDC_NACK:
13611ae08745Sheppo 		/* check if version in NACK is zero */
13621ae08745Sheppo 		if (rcvd_ver->major == 0 && rcvd_ver->minor == 0) {
13631ae08745Sheppo 			/* version handshake failure */
13641ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
13651ae08745Sheppo 			    "i_ldc_process_VER: (0x%llx) no version match\n",
13661ae08745Sheppo 			    ldcp->id);
13673af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1368d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
13691ae08745Sheppo 			return (ECONNRESET);
13701ae08745Sheppo 		}
13711ae08745Sheppo 
13721ae08745Sheppo 		/* get the current tail and pkt for the response */
13731ae08745Sheppo 		rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
13741ae08745Sheppo 		if (rv != 0) {
13751ae08745Sheppo 			cmn_err(CE_NOTE,
13761ae08745Sheppo 			    "i_ldc_process_VER: (0x%lx) err sending "
13771ae08745Sheppo 			    "version ACK/NACK\n", ldcp->id);
13783af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1379d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
13801ae08745Sheppo 			return (ECONNRESET);
13811ae08745Sheppo 		}
13821ae08745Sheppo 
13831ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
13841ae08745Sheppo 		ZERO_PKT(pkt);
13851ae08745Sheppo 
13861ae08745Sheppo 		/* initialize the packet */
13871ae08745Sheppo 		pkt->type = LDC_CTRL;
13881ae08745Sheppo 		pkt->ctrl = LDC_VER;
13891ae08745Sheppo 		pkt->stype = LDC_INFO;
13901ae08745Sheppo 
13911ae08745Sheppo 		/* check ver in NACK msg has a match */
13921ae08745Sheppo 		for (;;) {
13931ae08745Sheppo 			if (rcvd_ver->major == ldc_versions[idx].major) {
13941ae08745Sheppo 				/*
13951ae08745Sheppo 				 * major version match - resubmit request
13961ae08745Sheppo 				 * if lower minor version to the one this endpt
13971ae08745Sheppo 				 * supports, if necessary
13981ae08745Sheppo 				 */
13991ae08745Sheppo 				if (rcvd_ver->minor > ldc_versions[idx].minor)
14001ae08745Sheppo 					rcvd_ver->minor =
14011ae08745Sheppo 					    ldc_versions[idx].minor;
14021ae08745Sheppo 				bcopy(rcvd_ver, pkt->udata, sizeof (*rcvd_ver));
14031ae08745Sheppo 				break;
14041ae08745Sheppo 			}
14051ae08745Sheppo 
14061ae08745Sheppo 			if (rcvd_ver->major > ldc_versions[idx].major) {
14071ae08745Sheppo 
14081ae08745Sheppo 				D1(ldcp->id, "i_ldc_process_VER: using next"
14091ae08745Sheppo 				    " lower idx=%d, v%u.%u\n", idx,
14101ae08745Sheppo 				    ldc_versions[idx].major,
14111ae08745Sheppo 				    ldc_versions[idx].minor);
14121ae08745Sheppo 
14131ae08745Sheppo 				/* send next lower version */
14141ae08745Sheppo 				bcopy(&ldc_versions[idx], pkt->udata,
14151ae08745Sheppo 				    sizeof (ldc_versions[idx]));
14161ae08745Sheppo 				ldcp->next_vidx = idx;
14171ae08745Sheppo 				break;
14181ae08745Sheppo 			}
14191ae08745Sheppo 
14201ae08745Sheppo 			/* next version */
14211ae08745Sheppo 			idx++;
14221ae08745Sheppo 
14231ae08745Sheppo 			D1(ldcp->id, "i_ldc_process_VER: inc idx %x\n", idx);
14241ae08745Sheppo 
14251ae08745Sheppo 			if (idx == LDC_NUM_VERS) {
14261ae08745Sheppo 				/* no version match - terminate */
14271ae08745Sheppo 				ldcp->next_vidx = 0;
1428d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
14291ae08745Sheppo 				return (ECONNRESET);
14301ae08745Sheppo 			}
14311ae08745Sheppo 		}
14321ae08745Sheppo 
14331ae08745Sheppo 		/* initiate the send by calling into HV and set the new tail */
14341ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) %
14351ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
14361ae08745Sheppo 
14371ae08745Sheppo 		rv = i_ldc_set_tx_tail(ldcp, tx_tail);
14381ae08745Sheppo 		if (rv == 0) {
14391ae08745Sheppo 			D2(ldcp->id, "i_ldc_process_VER: (0x%llx) sent version"
14401ae08745Sheppo 			    "INFO v%u.%u\n", ldcp->id, ldc_versions[idx].major,
14411ae08745Sheppo 			    ldc_versions[idx].minor);
14421ae08745Sheppo 			ldcp->tx_tail = tx_tail;
14431ae08745Sheppo 		} else {
14441ae08745Sheppo 			cmn_err(CE_NOTE,
14451ae08745Sheppo 			    "i_ldc_process_VER: (0x%lx) error sending version"
14461ae08745Sheppo 			    "INFO\n", ldcp->id);
14473af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1448d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
14491ae08745Sheppo 			return (ECONNRESET);
14501ae08745Sheppo 		}
14511ae08745Sheppo 
14521ae08745Sheppo 		break;
14531ae08745Sheppo 	}
14541ae08745Sheppo 
1455d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
14561ae08745Sheppo 	return (rv);
14571ae08745Sheppo }
14581ae08745Sheppo 
14591ae08745Sheppo 
14601ae08745Sheppo /*
14611ae08745Sheppo  * Process an incoming RTS ctrl message
14621ae08745Sheppo  */
14631ae08745Sheppo static int
14641ae08745Sheppo i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg)
14651ae08745Sheppo {
14661ae08745Sheppo 	int 		rv = 0;
14671ae08745Sheppo 	ldc_msg_t 	*pkt;
14681ae08745Sheppo 	uint64_t	tx_tail;
14691ae08745Sheppo 	boolean_t	sent_NACK = B_FALSE;
14701ae08745Sheppo 
14711ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RTS: (0x%llx) received RTS\n", ldcp->id);
14721ae08745Sheppo 
14731ae08745Sheppo 	switch (msg->stype) {
14741ae08745Sheppo 	case LDC_NACK:
14751ae08745Sheppo 		DWARN(ldcp->id,
14761ae08745Sheppo 		    "i_ldc_process_RTS: (0x%llx) RTS NACK received\n",
14771ae08745Sheppo 		    ldcp->id);
14781ae08745Sheppo 
14791ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
1480d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
14813af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1482d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
14831ae08745Sheppo 		rv = ECONNRESET;
14841ae08745Sheppo 		break;
14851ae08745Sheppo 
14861ae08745Sheppo 	case LDC_INFO:
14871ae08745Sheppo 
14881ae08745Sheppo 		/* check mode */
14891ae08745Sheppo 		if (ldcp->mode != (ldc_mode_t)msg->env) {
14901ae08745Sheppo 			cmn_err(CE_NOTE,
14911ae08745Sheppo 			    "i_ldc_process_RTS: (0x%lx) mode mismatch\n",
14921ae08745Sheppo 			    ldcp->id);
14931ae08745Sheppo 			/*
14941ae08745Sheppo 			 * send NACK in response to MODE message
14951ae08745Sheppo 			 * get the current tail for the response
14961ae08745Sheppo 			 */
14971ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTS);
14981ae08745Sheppo 			if (rv) {
14991ae08745Sheppo 				/* if cannot send NACK - reset channel */
1500d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
15013af08d82Slm66018 				i_ldc_reset(ldcp, B_TRUE);
1502d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
15031ae08745Sheppo 				rv = ECONNRESET;
15041ae08745Sheppo 				break;
15051ae08745Sheppo 			}
15061ae08745Sheppo 			sent_NACK = B_TRUE;
15071ae08745Sheppo 		}
15081ae08745Sheppo 		break;
15091ae08745Sheppo 	default:
15101ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RTS: (0x%llx) unexp ACK\n",
15111ae08745Sheppo 		    ldcp->id);
1512d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
15133af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1514d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
15151ae08745Sheppo 		rv = ECONNRESET;
15161ae08745Sheppo 		break;
15171ae08745Sheppo 	}
15181ae08745Sheppo 
15191ae08745Sheppo 	/*
15201ae08745Sheppo 	 * If either the connection was reset (when rv != 0) or
15211ae08745Sheppo 	 * a NACK was sent, we return. In the case of a NACK
15221ae08745Sheppo 	 * we dont want to consume the packet that came in but
15231ae08745Sheppo 	 * not record that we received the RTS
15241ae08745Sheppo 	 */
15251ae08745Sheppo 	if (rv || sent_NACK)
15261ae08745Sheppo 		return (rv);
15271ae08745Sheppo 
15281ae08745Sheppo 	/* record RTS received */
15291ae08745Sheppo 	ldcp->hstate |= TS_RCVD_RTS;
15301ae08745Sheppo 
15311ae08745Sheppo 	/* store initial SEQID info */
15321ae08745Sheppo 	ldcp->last_msg_snt = msg->seqid;
15331ae08745Sheppo 
1534d10e4ef2Snarayan 	/* Obtain Tx lock */
1535d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
1536d10e4ef2Snarayan 
15371ae08745Sheppo 	/* get the current tail for the response */
15381ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
15391ae08745Sheppo 	if (rv != 0) {
15401ae08745Sheppo 		cmn_err(CE_NOTE,
15411ae08745Sheppo 		    "i_ldc_process_RTS: (0x%lx) err sending RTR\n",
15421ae08745Sheppo 		    ldcp->id);
15433af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1544d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
15451ae08745Sheppo 		return (ECONNRESET);
15461ae08745Sheppo 	}
15471ae08745Sheppo 
15481ae08745Sheppo 	pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
15491ae08745Sheppo 	ZERO_PKT(pkt);
15501ae08745Sheppo 
15511ae08745Sheppo 	/* initialize the packet */
15521ae08745Sheppo 	pkt->type = LDC_CTRL;
15531ae08745Sheppo 	pkt->stype = LDC_INFO;
15541ae08745Sheppo 	pkt->ctrl = LDC_RTR;
15551ae08745Sheppo 	pkt->env = ldcp->mode;
15561ae08745Sheppo 	if (ldcp->mode != LDC_MODE_RAW)
15571ae08745Sheppo 		pkt->seqid = LDC_INIT_SEQID;
15581ae08745Sheppo 
15591ae08745Sheppo 	ldcp->last_msg_rcd = msg->seqid;
15601ae08745Sheppo 
15611ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
15621ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
15631ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
15641ae08745Sheppo 
15651ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
15661ae08745Sheppo 	if (rv == 0) {
15671ae08745Sheppo 		D2(ldcp->id,
15681ae08745Sheppo 		    "i_ldc_process_RTS: (0x%llx) sent RTR\n", ldcp->id);
15691ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "i_ldc_process_RTS sent rtr", (uint64_t)pkt);
15701ae08745Sheppo 
15711ae08745Sheppo 		ldcp->tx_tail = tx_tail;
15721ae08745Sheppo 		ldcp->hstate |= TS_SENT_RTR;
15731ae08745Sheppo 
15741ae08745Sheppo 	} else {
15751ae08745Sheppo 		cmn_err(CE_NOTE,
15761ae08745Sheppo 		    "i_ldc_process_RTS: (0x%lx) error sending RTR\n",
15771ae08745Sheppo 		    ldcp->id);
15783af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1579d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
15801ae08745Sheppo 		return (ECONNRESET);
15811ae08745Sheppo 	}
15821ae08745Sheppo 
1583d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
15841ae08745Sheppo 	return (0);
15851ae08745Sheppo }
15861ae08745Sheppo 
15871ae08745Sheppo /*
15881ae08745Sheppo  * Process an incoming RTR ctrl message
15891ae08745Sheppo  */
15901ae08745Sheppo static int
15911ae08745Sheppo i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg)
15921ae08745Sheppo {
15931ae08745Sheppo 	int 		rv = 0;
15941ae08745Sheppo 	boolean_t	sent_NACK = B_FALSE;
15951ae08745Sheppo 
15961ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RTR: (0x%llx) received RTR\n", ldcp->id);
15971ae08745Sheppo 
15981ae08745Sheppo 	switch (msg->stype) {
15991ae08745Sheppo 	case LDC_NACK:
16001ae08745Sheppo 		/* RTR NACK received */
16011ae08745Sheppo 		DWARN(ldcp->id,
16021ae08745Sheppo 		    "i_ldc_process_RTR: (0x%llx) RTR NACK received\n",
16031ae08745Sheppo 		    ldcp->id);
16041ae08745Sheppo 
16051ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
1606d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
16073af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1608d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
16091ae08745Sheppo 		rv = ECONNRESET;
16101ae08745Sheppo 
16111ae08745Sheppo 		break;
16121ae08745Sheppo 
16131ae08745Sheppo 	case LDC_INFO:
16141ae08745Sheppo 
16151ae08745Sheppo 		/* check mode */
16161ae08745Sheppo 		if (ldcp->mode != (ldc_mode_t)msg->env) {
16171ae08745Sheppo 			DWARN(ldcp->id,
1618cb112a14Slm66018 			    "i_ldc_process_RTR: (0x%llx) mode mismatch, "
1619cb112a14Slm66018 			    "expecting 0x%x, got 0x%x\n",
1620cb112a14Slm66018 			    ldcp->id, ldcp->mode, (ldc_mode_t)msg->env);
16211ae08745Sheppo 			/*
16221ae08745Sheppo 			 * send NACK in response to MODE message
16231ae08745Sheppo 			 * get the current tail for the response
16241ae08745Sheppo 			 */
16251ae08745Sheppo 			rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTR);
16261ae08745Sheppo 			if (rv) {
16271ae08745Sheppo 				/* if cannot send NACK - reset channel */
1628d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
16293af08d82Slm66018 				i_ldc_reset(ldcp, B_TRUE);
1630d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
16311ae08745Sheppo 				rv = ECONNRESET;
16321ae08745Sheppo 				break;
16331ae08745Sheppo 			}
16341ae08745Sheppo 			sent_NACK = B_TRUE;
16351ae08745Sheppo 		}
16361ae08745Sheppo 		break;
16371ae08745Sheppo 
16381ae08745Sheppo 	default:
16391ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RTR: (0x%llx) unexp ACK\n",
16401ae08745Sheppo 		    ldcp->id);
16411ae08745Sheppo 
16421ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
1643d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
16443af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1645d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
16461ae08745Sheppo 		rv = ECONNRESET;
16471ae08745Sheppo 		break;
16481ae08745Sheppo 	}
16491ae08745Sheppo 
16501ae08745Sheppo 	/*
16511ae08745Sheppo 	 * If either the connection was reset (when rv != 0) or
16521ae08745Sheppo 	 * a NACK was sent, we return. In the case of a NACK
16531ae08745Sheppo 	 * we dont want to consume the packet that came in but
16541ae08745Sheppo 	 * not record that we received the RTR
16551ae08745Sheppo 	 */
16561ae08745Sheppo 	if (rv || sent_NACK)
16571ae08745Sheppo 		return (rv);
16581ae08745Sheppo 
16591ae08745Sheppo 	ldcp->last_msg_snt = msg->seqid;
16601ae08745Sheppo 	ldcp->hstate |= TS_RCVD_RTR;
16611ae08745Sheppo 
16621ae08745Sheppo 	rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_INFO, LDC_RDX);
16631ae08745Sheppo 	if (rv) {
16641ae08745Sheppo 		cmn_err(CE_NOTE,
16651ae08745Sheppo 		    "i_ldc_process_RTR: (0x%lx) cannot send RDX\n",
16661ae08745Sheppo 		    ldcp->id);
1667d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
16683af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1669d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
16701ae08745Sheppo 		return (ECONNRESET);
16711ae08745Sheppo 	}
16721ae08745Sheppo 	D2(ldcp->id,
16731ae08745Sheppo 	    "i_ldc_process_RTR: (0x%llx) sent RDX\n", ldcp->id);
16741ae08745Sheppo 
16751ae08745Sheppo 	ldcp->hstate |= TS_SENT_RDX;
16761ae08745Sheppo 	ldcp->tstate |= TS_HSHAKE_DONE;
16773af08d82Slm66018 	if ((ldcp->tstate & TS_IN_RESET) == 0)
16781ae08745Sheppo 		ldcp->status = LDC_UP;
16791ae08745Sheppo 
1680cb112a14Slm66018 	D1(ldcp->id, "(0x%llx) Handshake Complete\n", ldcp->id);
16811ae08745Sheppo 
16821ae08745Sheppo 	return (0);
16831ae08745Sheppo }
16841ae08745Sheppo 
16851ae08745Sheppo 
16861ae08745Sheppo /*
16871ae08745Sheppo  * Process an incoming RDX ctrl message
16881ae08745Sheppo  */
16891ae08745Sheppo static int
16901ae08745Sheppo i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg)
16911ae08745Sheppo {
16921ae08745Sheppo 	int	rv = 0;
16931ae08745Sheppo 
16941ae08745Sheppo 	D2(ldcp->id, "i_ldc_process_RDX: (0x%llx) received RDX\n", ldcp->id);
16951ae08745Sheppo 
16961ae08745Sheppo 	switch (msg->stype) {
16971ae08745Sheppo 	case LDC_NACK:
16981ae08745Sheppo 		/* RDX NACK received */
16991ae08745Sheppo 		DWARN(ldcp->id,
17001ae08745Sheppo 		    "i_ldc_process_RDX: (0x%llx) RDX NACK received\n",
17011ae08745Sheppo 		    ldcp->id);
17021ae08745Sheppo 
17031ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
1704d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
17053af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1706d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
17071ae08745Sheppo 		rv = ECONNRESET;
17081ae08745Sheppo 
17091ae08745Sheppo 		break;
17101ae08745Sheppo 
17111ae08745Sheppo 	case LDC_INFO:
17121ae08745Sheppo 
17131ae08745Sheppo 		/*
17141ae08745Sheppo 		 * if channel is UP and a RDX received after data transmission
17151ae08745Sheppo 		 * has commenced it is an error
17161ae08745Sheppo 		 */
17171ae08745Sheppo 		if ((ldcp->tstate == TS_UP) && (ldcp->hstate & TS_RCVD_RDX)) {
17181ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
17191ae08745Sheppo 			    "i_ldc_process_RDX: (0x%llx) unexpected RDX"
17201ae08745Sheppo 			    " - LDC reset\n", ldcp->id);
1721d10e4ef2Snarayan 			mutex_enter(&ldcp->tx_lock);
17223af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1723d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
17241ae08745Sheppo 			return (ECONNRESET);
17251ae08745Sheppo 		}
17261ae08745Sheppo 
17271ae08745Sheppo 		ldcp->hstate |= TS_RCVD_RDX;
17281ae08745Sheppo 		ldcp->tstate |= TS_HSHAKE_DONE;
17293af08d82Slm66018 		if ((ldcp->tstate & TS_IN_RESET) == 0)
17301ae08745Sheppo 			ldcp->status = LDC_UP;
17311ae08745Sheppo 
17321ae08745Sheppo 		D1(DBG_ALL_LDCS, "(0x%llx) Handshake Complete\n", ldcp->id);
17331ae08745Sheppo 		break;
17341ae08745Sheppo 
17351ae08745Sheppo 	default:
17361ae08745Sheppo 		DWARN(ldcp->id, "i_ldc_process_RDX: (0x%llx) unexp ACK\n",
17371ae08745Sheppo 		    ldcp->id);
17381ae08745Sheppo 
17391ae08745Sheppo 		/* Reset the channel -- as we cannot continue */
1740d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
17413af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1742d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
17431ae08745Sheppo 		rv = ECONNRESET;
17441ae08745Sheppo 		break;
17451ae08745Sheppo 	}
17461ae08745Sheppo 
17471ae08745Sheppo 	return (rv);
17481ae08745Sheppo }
17491ae08745Sheppo 
17501ae08745Sheppo /*
17511ae08745Sheppo  * Process an incoming ACK for a data packet
17521ae08745Sheppo  */
17531ae08745Sheppo static int
17541ae08745Sheppo i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg)
17551ae08745Sheppo {
17561ae08745Sheppo 	int		rv;
17571ae08745Sheppo 	uint64_t 	tx_head;
17581ae08745Sheppo 	ldc_msg_t	*pkt;
17591ae08745Sheppo 
1760d10e4ef2Snarayan 	/* Obtain Tx lock */
1761d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
1762d10e4ef2Snarayan 
17631ae08745Sheppo 	/*
1764d10e4ef2Snarayan 	 * Read the current Tx head and tail
17651ae08745Sheppo 	 */
17661ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
17671ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
17681ae08745Sheppo 	if (rv != 0) {
17691ae08745Sheppo 		cmn_err(CE_WARN,
17701ae08745Sheppo 		    "i_ldc_process_data_ACK: (0x%lx) cannot read qptrs\n",
17711ae08745Sheppo 		    ldcp->id);
1772d10e4ef2Snarayan 
1773d10e4ef2Snarayan 		/* Reset the channel -- as we cannot continue */
17743af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
1775d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
1776d10e4ef2Snarayan 		return (ECONNRESET);
17771ae08745Sheppo 	}
17781ae08745Sheppo 
17791ae08745Sheppo 	/*
17801ae08745Sheppo 	 * loop from where the previous ACK location was to the
17811ae08745Sheppo 	 * current head location. This is how far the HV has
17821ae08745Sheppo 	 * actually send pkts. Pkts between head and tail are
17831ae08745Sheppo 	 * yet to be sent by HV.
17841ae08745Sheppo 	 */
17851ae08745Sheppo 	tx_head = ldcp->tx_ackd_head;
17861ae08745Sheppo 	for (;;) {
17871ae08745Sheppo 		pkt = (ldc_msg_t *)(ldcp->tx_q_va + tx_head);
17881ae08745Sheppo 		tx_head = (tx_head + LDC_PACKET_SIZE) %
17891ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
17901ae08745Sheppo 
17911ae08745Sheppo 		if (pkt->seqid == msg->ackid) {
17921ae08745Sheppo 			D2(ldcp->id,
17931ae08745Sheppo 			    "i_ldc_process_data_ACK: (0x%llx) found packet\n",
17941ae08745Sheppo 			    ldcp->id);
17951ae08745Sheppo 			ldcp->last_ack_rcd = msg->ackid;
17961ae08745Sheppo 			ldcp->tx_ackd_head = tx_head;
17971ae08745Sheppo 			break;
17981ae08745Sheppo 		}
17991ae08745Sheppo 		if (tx_head == ldcp->tx_head) {
18001ae08745Sheppo 			/* could not find packet */
18011ae08745Sheppo 			DWARN(ldcp->id,
18021ae08745Sheppo 			    "i_ldc_process_data_ACK: (0x%llx) invalid ACKid\n",
18031ae08745Sheppo 			    ldcp->id);
1804d10e4ef2Snarayan 
1805d10e4ef2Snarayan 			/* Reset the channel -- as we cannot continue */
18063af08d82Slm66018 			i_ldc_reset(ldcp, B_TRUE);
1807d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
1808d10e4ef2Snarayan 			return (ECONNRESET);
18091ae08745Sheppo 		}
18101ae08745Sheppo 	}
18111ae08745Sheppo 
1812d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
18131ae08745Sheppo 	return (0);
18141ae08745Sheppo }
18151ae08745Sheppo 
18161ae08745Sheppo /*
18171ae08745Sheppo  * Process incoming control message
18181ae08745Sheppo  * Return 0 - session can continue
18191ae08745Sheppo  *        EAGAIN - reprocess packet - state was changed
18201ae08745Sheppo  *	  ECONNRESET - channel was reset
18211ae08745Sheppo  */
18221ae08745Sheppo static int
18231ae08745Sheppo i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg)
18241ae08745Sheppo {
18251ae08745Sheppo 	int 		rv = 0;
18261ae08745Sheppo 
18273af08d82Slm66018 	D1(ldcp->id, "i_ldc_ctrlmsg: (%llx) tstate = %lx, hstate = %lx\n",
18283af08d82Slm66018 	    ldcp->id, ldcp->tstate, ldcp->hstate);
18293af08d82Slm66018 
18303af08d82Slm66018 	switch (ldcp->tstate & ~TS_IN_RESET) {
18311ae08745Sheppo 
18321ae08745Sheppo 	case TS_OPEN:
18331ae08745Sheppo 	case TS_READY:
18341ae08745Sheppo 
18351ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
18361ae08745Sheppo 		case LDC_VER:
18371ae08745Sheppo 			/* process version message */
18381ae08745Sheppo 			rv = i_ldc_process_VER(ldcp, msg);
18391ae08745Sheppo 			break;
18401ae08745Sheppo 		default:
18411ae08745Sheppo 			DWARN(ldcp->id,
18421ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
18431ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
18441ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
18451ae08745Sheppo 			break;
18461ae08745Sheppo 		}
18471ae08745Sheppo 
18481ae08745Sheppo 		break;
18491ae08745Sheppo 
18501ae08745Sheppo 	case TS_VREADY:
18511ae08745Sheppo 
18521ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
18531ae08745Sheppo 		case LDC_VER:
18543af08d82Slm66018 			/* process version message */
18553af08d82Slm66018 			rv = i_ldc_process_VER(ldcp, msg);
18561ae08745Sheppo 			break;
18571ae08745Sheppo 		case LDC_RTS:
18581ae08745Sheppo 			/* process RTS message */
18591ae08745Sheppo 			rv = i_ldc_process_RTS(ldcp, msg);
18601ae08745Sheppo 			break;
18611ae08745Sheppo 		case LDC_RTR:
18621ae08745Sheppo 			/* process RTR message */
18631ae08745Sheppo 			rv = i_ldc_process_RTR(ldcp, msg);
18641ae08745Sheppo 			break;
18651ae08745Sheppo 		case LDC_RDX:
18661ae08745Sheppo 			/* process RDX message */
18671ae08745Sheppo 			rv = i_ldc_process_RDX(ldcp, msg);
18681ae08745Sheppo 			break;
18691ae08745Sheppo 		default:
18701ae08745Sheppo 			DWARN(ldcp->id,
18711ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
18721ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
18731ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
18741ae08745Sheppo 			break;
18751ae08745Sheppo 		}
18761ae08745Sheppo 
18771ae08745Sheppo 		break;
18781ae08745Sheppo 
18791ae08745Sheppo 	case TS_UP:
18801ae08745Sheppo 
18811ae08745Sheppo 		switch (msg->ctrl & LDC_CTRL_MASK) {
18821ae08745Sheppo 		case LDC_VER:
18831ae08745Sheppo 			DWARN(ldcp->id,
18841ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexpected VER "
18851ae08745Sheppo 			    "- LDC reset\n", ldcp->id);
18861ae08745Sheppo 			/* peer is redoing version negotiation */
1887d10e4ef2Snarayan 			mutex_enter(&ldcp->tx_lock);
18881ae08745Sheppo 			(void) i_ldc_txq_reconf(ldcp);
18891ae08745Sheppo 			i_ldc_reset_state(ldcp);
1890d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
18911ae08745Sheppo 			rv = EAGAIN;
18921ae08745Sheppo 			break;
18931ae08745Sheppo 
18941ae08745Sheppo 		case LDC_RDX:
18951ae08745Sheppo 			/* process RDX message */
18961ae08745Sheppo 			rv = i_ldc_process_RDX(ldcp, msg);
18971ae08745Sheppo 			break;
18981ae08745Sheppo 
18991ae08745Sheppo 		default:
19001ae08745Sheppo 			DWARN(ldcp->id,
19011ae08745Sheppo 			    "i_ldc_ctrlmsg: (0x%llx) unexp ctrl 0x%x "
19021ae08745Sheppo 			    "tstate=0x%x\n", ldcp->id,
19031ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK), ldcp->tstate);
19041ae08745Sheppo 			break;
19051ae08745Sheppo 		}
19061ae08745Sheppo 	}
19071ae08745Sheppo 
19081ae08745Sheppo 	return (rv);
19091ae08745Sheppo }
19101ae08745Sheppo 
19111ae08745Sheppo /*
19121ae08745Sheppo  * Register channel with the channel nexus
19131ae08745Sheppo  */
19141ae08745Sheppo static int
19151ae08745Sheppo i_ldc_register_channel(ldc_chan_t *ldcp)
19161ae08745Sheppo {
19171ae08745Sheppo 	int		rv = 0;
19181ae08745Sheppo 	ldc_cnex_t	*cinfo = &ldcssp->cinfo;
19191ae08745Sheppo 
19201ae08745Sheppo 	if (cinfo->dip == NULL) {
19211ae08745Sheppo 		DWARN(ldcp->id,
19221ae08745Sheppo 		    "i_ldc_register_channel: cnex has not registered\n");
19231ae08745Sheppo 		return (EAGAIN);
19241ae08745Sheppo 	}
19251ae08745Sheppo 
19261ae08745Sheppo 	rv = cinfo->reg_chan(cinfo->dip, ldcp->id, ldcp->devclass);
19271ae08745Sheppo 	if (rv) {
19281ae08745Sheppo 		DWARN(ldcp->id,
19291ae08745Sheppo 		    "i_ldc_register_channel: cannot register channel\n");
19301ae08745Sheppo 		return (rv);
19311ae08745Sheppo 	}
19321ae08745Sheppo 
19331ae08745Sheppo 	rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR,
19341ae08745Sheppo 	    i_ldc_tx_hdlr, ldcp, NULL);
19351ae08745Sheppo 	if (rv) {
19361ae08745Sheppo 		DWARN(ldcp->id,
19371ae08745Sheppo 		    "i_ldc_register_channel: cannot add Tx interrupt\n");
19381ae08745Sheppo 		(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
19391ae08745Sheppo 		return (rv);
19401ae08745Sheppo 	}
19411ae08745Sheppo 
19421ae08745Sheppo 	rv = cinfo->add_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR,
19431ae08745Sheppo 	    i_ldc_rx_hdlr, ldcp, NULL);
19441ae08745Sheppo 	if (rv) {
19451ae08745Sheppo 		DWARN(ldcp->id,
19461ae08745Sheppo 		    "i_ldc_register_channel: cannot add Rx interrupt\n");
19471ae08745Sheppo 		(void) cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
19481ae08745Sheppo 		(void) cinfo->unreg_chan(cinfo->dip, ldcp->id);
19491ae08745Sheppo 		return (rv);
19501ae08745Sheppo 	}
19511ae08745Sheppo 
19521ae08745Sheppo 	ldcp->tstate |= TS_CNEX_RDY;
19531ae08745Sheppo 
19541ae08745Sheppo 	return (0);
19551ae08745Sheppo }
19561ae08745Sheppo 
19571ae08745Sheppo /*
19581ae08745Sheppo  * Unregister a channel with the channel nexus
19591ae08745Sheppo  */
19601ae08745Sheppo static int
19611ae08745Sheppo i_ldc_unregister_channel(ldc_chan_t *ldcp)
19621ae08745Sheppo {
19631ae08745Sheppo 	int		rv = 0;
19641ae08745Sheppo 	ldc_cnex_t	*cinfo = &ldcssp->cinfo;
19651ae08745Sheppo 
19661ae08745Sheppo 	if (cinfo->dip == NULL) {
19671ae08745Sheppo 		DWARN(ldcp->id,
19681ae08745Sheppo 		    "i_ldc_unregister_channel: cnex has not registered\n");
19691ae08745Sheppo 		return (EAGAIN);
19701ae08745Sheppo 	}
19711ae08745Sheppo 
19721ae08745Sheppo 	if (ldcp->tstate & TS_CNEX_RDY) {
19731ae08745Sheppo 
1974d10e4ef2Snarayan 		/* Remove the Rx interrupt */
19751ae08745Sheppo 		rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR);
19761ae08745Sheppo 		if (rv) {
19773af08d82Slm66018 			if (rv != EAGAIN) {
19781ae08745Sheppo 				DWARN(ldcp->id,
19793af08d82Slm66018 				    "i_ldc_unregister_channel: err removing "
19803af08d82Slm66018 				    "Rx intr\n");
1981d10e4ef2Snarayan 				return (rv);
19821ae08745Sheppo 			}
1983d10e4ef2Snarayan 
19843af08d82Slm66018 			/*
19853af08d82Slm66018 			 * If interrupts are pending and handler has
19863af08d82Slm66018 			 * finished running, clear interrupt and try
19873af08d82Slm66018 			 * again
19883af08d82Slm66018 			 */
19893af08d82Slm66018 			if (ldcp->rx_intr_state != LDC_INTR_PEND)
19903af08d82Slm66018 				return (rv);
19913af08d82Slm66018 
19923af08d82Slm66018 			(void) i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
19933af08d82Slm66018 			rv = cinfo->rem_intr(cinfo->dip, ldcp->id,
19943af08d82Slm66018 			    CNEX_RX_INTR);
19953af08d82Slm66018 			if (rv) {
19963af08d82Slm66018 				DWARN(ldcp->id, "i_ldc_unregister_channel: "
19973af08d82Slm66018 				    "err removing Rx interrupt\n");
19983af08d82Slm66018 				return (rv);
19993af08d82Slm66018 			}
20003af08d82Slm66018 		}
20013af08d82Slm66018 
2002d10e4ef2Snarayan 		/* Remove the Tx interrupt */
20031ae08745Sheppo 		rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR);
20041ae08745Sheppo 		if (rv) {
20051ae08745Sheppo 			DWARN(ldcp->id,
20061ae08745Sheppo 			    "i_ldc_unregister_channel: err removing Tx intr\n");
2007d10e4ef2Snarayan 			return (rv);
20081ae08745Sheppo 		}
2009d10e4ef2Snarayan 
2010d10e4ef2Snarayan 		/* Unregister the channel */
20111ae08745Sheppo 		rv = cinfo->unreg_chan(ldcssp->cinfo.dip, ldcp->id);
20121ae08745Sheppo 		if (rv) {
20131ae08745Sheppo 			DWARN(ldcp->id,
20141ae08745Sheppo 			    "i_ldc_unregister_channel: cannot unreg channel\n");
2015d10e4ef2Snarayan 			return (rv);
20161ae08745Sheppo 		}
20171ae08745Sheppo 
20181ae08745Sheppo 		ldcp->tstate &= ~TS_CNEX_RDY;
20191ae08745Sheppo 	}
20201ae08745Sheppo 
20211ae08745Sheppo 	return (0);
20221ae08745Sheppo }
20231ae08745Sheppo 
20241ae08745Sheppo 
20251ae08745Sheppo /*
20261ae08745Sheppo  * LDC transmit interrupt handler
20271ae08745Sheppo  *    triggered for chanel up/down/reset events
20281ae08745Sheppo  *    and Tx queue content changes
20291ae08745Sheppo  */
20301ae08745Sheppo static uint_t
20311ae08745Sheppo i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2)
20321ae08745Sheppo {
20331ae08745Sheppo 	_NOTE(ARGUNUSED(arg2))
20341ae08745Sheppo 
20351ae08745Sheppo 	int 		rv;
20361ae08745Sheppo 	ldc_chan_t 	*ldcp;
20371ae08745Sheppo 	boolean_t 	notify_client = B_FALSE;
20383af08d82Slm66018 	uint64_t	notify_event = 0, link_state;
20391ae08745Sheppo 
20401ae08745Sheppo 	/* Get the channel for which interrupt was received */
20411ae08745Sheppo 	ASSERT(arg1 != NULL);
20421ae08745Sheppo 	ldcp = (ldc_chan_t *)arg1;
20431ae08745Sheppo 
20441ae08745Sheppo 	D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) Received intr, ldcp=0x%p\n",
20451ae08745Sheppo 	    ldcp->id, ldcp);
20461ae08745Sheppo 
20471ae08745Sheppo 	/* Lock channel */
20481ae08745Sheppo 	mutex_enter(&ldcp->lock);
20491ae08745Sheppo 
2050d10e4ef2Snarayan 	/* Obtain Tx lock */
2051d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
2052d10e4ef2Snarayan 
20534bac2208Snarayan 	/* mark interrupt as pending */
20543af08d82Slm66018 	ldcp->tx_intr_state = LDC_INTR_ACTIVE;
20553af08d82Slm66018 
20563af08d82Slm66018 	/* save current link state */
20573af08d82Slm66018 	link_state = ldcp->link_state;
20584bac2208Snarayan 
20591ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail,
20601ae08745Sheppo 	    &ldcp->link_state);
20611ae08745Sheppo 	if (rv) {
20621ae08745Sheppo 		cmn_err(CE_WARN,
20631ae08745Sheppo 		    "i_ldc_tx_hdlr: (0x%lx) cannot read queue ptrs rv=0x%d\n",
20641ae08745Sheppo 		    ldcp->id, rv);
20654bac2208Snarayan 		i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
2066d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
20671ae08745Sheppo 		mutex_exit(&ldcp->lock);
20681ae08745Sheppo 		return (DDI_INTR_CLAIMED);
20691ae08745Sheppo 	}
20701ae08745Sheppo 
20711ae08745Sheppo 	/*
20721ae08745Sheppo 	 * reset the channel state if the channel went down
20731ae08745Sheppo 	 * (other side unconfigured queue) or channel was reset
20741ae08745Sheppo 	 * (other side reconfigured its queue)
20751ae08745Sheppo 	 */
20763af08d82Slm66018 	if (link_state != ldcp->link_state &&
20773af08d82Slm66018 	    ldcp->link_state == LDC_CHANNEL_DOWN) {
20781ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link down\n", ldcp->id);
20793af08d82Slm66018 		i_ldc_reset(ldcp, B_FALSE);
20801ae08745Sheppo 		notify_client = B_TRUE;
20811ae08745Sheppo 		notify_event = LDC_EVT_DOWN;
20821ae08745Sheppo 	}
20831ae08745Sheppo 
20843af08d82Slm66018 	if (link_state != ldcp->link_state &&
20853af08d82Slm66018 	    ldcp->link_state == LDC_CHANNEL_RESET) {
20861ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link reset\n", ldcp->id);
20873af08d82Slm66018 		i_ldc_reset(ldcp, B_FALSE);
20881ae08745Sheppo 		notify_client = B_TRUE;
20891ae08745Sheppo 		notify_event = LDC_EVT_RESET;
20901ae08745Sheppo 	}
20911ae08745Sheppo 
20923af08d82Slm66018 	if (link_state != ldcp->link_state &&
20933af08d82Slm66018 	    (ldcp->tstate & ~TS_IN_RESET) == TS_OPEN &&
20943af08d82Slm66018 	    ldcp->link_state == LDC_CHANNEL_UP) {
20951ae08745Sheppo 		D1(ldcp->id, "i_ldc_tx_hdlr: channel link up\n", ldcp->id);
20961ae08745Sheppo 		notify_client = B_TRUE;
20971ae08745Sheppo 		notify_event = LDC_EVT_RESET;
20981ae08745Sheppo 		ldcp->tstate |= TS_LINK_READY;
20991ae08745Sheppo 		ldcp->status = LDC_READY;
21001ae08745Sheppo 	}
21011ae08745Sheppo 
21021ae08745Sheppo 	/* if callbacks are disabled, do not notify */
21031ae08745Sheppo 	if (!ldcp->cb_enabled)
21041ae08745Sheppo 		notify_client = B_FALSE;
21051ae08745Sheppo 
21064d39be2bSsg70180 	i_ldc_clear_intr(ldcp, CNEX_TX_INTR);
210722f747efSnarayan 	mutex_exit(&ldcp->tx_lock);
21081ae08745Sheppo 
21091ae08745Sheppo 	if (notify_client) {
21103af08d82Slm66018 		ldcp->cb_inprogress = B_TRUE;
21113af08d82Slm66018 		mutex_exit(&ldcp->lock);
21121ae08745Sheppo 		rv = ldcp->cb(notify_event, ldcp->cb_arg);
21131ae08745Sheppo 		if (rv) {
21141ae08745Sheppo 			DWARN(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) callback "
21151ae08745Sheppo 			    "failure", ldcp->id);
21161ae08745Sheppo 		}
21171ae08745Sheppo 		mutex_enter(&ldcp->lock);
21181ae08745Sheppo 		ldcp->cb_inprogress = B_FALSE;
21191ae08745Sheppo 	}
21201ae08745Sheppo 
21211ae08745Sheppo 	mutex_exit(&ldcp->lock);
21221ae08745Sheppo 
21231ae08745Sheppo 	D1(ldcp->id, "i_ldc_tx_hdlr: (0x%llx) exiting handler", ldcp->id);
21241ae08745Sheppo 
21251ae08745Sheppo 	return (DDI_INTR_CLAIMED);
21261ae08745Sheppo }
21271ae08745Sheppo 
21281ae08745Sheppo /*
2129*58283286Sha137994  * Process the Rx HV queue.
2130*58283286Sha137994  *
2131*58283286Sha137994  * Returns 0 if data packets were found and no errors were encountered,
2132*58283286Sha137994  * otherwise returns an error. In either case, the *notify argument is
2133*58283286Sha137994  * set to indicate whether or not the client callback function should
2134*58283286Sha137994  * be invoked. The *event argument is set to contain the callback event.
2135*58283286Sha137994  *
2136*58283286Sha137994  * Depending on the channel mode, packets are handled differently:
2137*58283286Sha137994  *
2138*58283286Sha137994  * RAW MODE
2139*58283286Sha137994  * For raw mode channels, when a data packet is encountered,
2140*58283286Sha137994  * processing stops and all packets are left on the queue to be removed
2141*58283286Sha137994  * and processed by the ldc_read code path.
2142*58283286Sha137994  *
2143*58283286Sha137994  * UNRELIABLE MODE
2144*58283286Sha137994  * For unreliable mode, when a data packet is encountered, processing
2145*58283286Sha137994  * stops, and all packets are left on the queue to be removed and
2146*58283286Sha137994  * processed by the ldc_read code path. Control packets are processed
2147*58283286Sha137994  * inline if they are encountered before any data packets.
2148*58283286Sha137994  *
2149*58283286Sha137994  * STEAMING MODE
2150*58283286Sha137994  * For streaming mode channels, all packets on the receive queue
2151*58283286Sha137994  * are processed: data packets are copied to the data queue and
2152*58283286Sha137994  * control packets are processed inline. Packets are only left on
2153*58283286Sha137994  * the receive queue when the data queue is full.
21541ae08745Sheppo  */
21551ae08745Sheppo static uint_t
2156*58283286Sha137994 i_ldc_rx_process_hvq(ldc_chan_t *ldcp, boolean_t *notify_client,
2157*58283286Sha137994     uint64_t *notify_event)
21581ae08745Sheppo {
21591ae08745Sheppo 	int		rv;
21601ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
21611ae08745Sheppo 	ldc_msg_t 	*msg;
21623af08d82Slm66018 	uint64_t	link_state, first_fragment = 0;
2163*58283286Sha137994 	boolean_t	trace_length = B_TRUE;
21643af08d82Slm66018 
2165*58283286Sha137994 	ASSERT(MUTEX_HELD(&ldcp->lock));
2166*58283286Sha137994 	*notify_client = B_FALSE;
2167*58283286Sha137994 	*notify_event = 0;
21681ae08745Sheppo 
21691ae08745Sheppo 	/*
21701ae08745Sheppo 	 * Read packet(s) from the queue
21711ae08745Sheppo 	 */
21721ae08745Sheppo 	for (;;) {
21731ae08745Sheppo 
21743af08d82Slm66018 		link_state = ldcp->link_state;
21751ae08745Sheppo 		rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
21761ae08745Sheppo 		    &ldcp->link_state);
21771ae08745Sheppo 		if (rv) {
21781ae08745Sheppo 			cmn_err(CE_WARN,
2179*58283286Sha137994 			    "i_ldc_rx_process_hvq: (0x%lx) cannot read "
21801ae08745Sheppo 			    "queue ptrs, rv=0x%d\n", ldcp->id, rv);
21811ae08745Sheppo 			i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
2182*58283286Sha137994 			return (EIO);
21831ae08745Sheppo 		}
21841ae08745Sheppo 
21851ae08745Sheppo 		/*
21861ae08745Sheppo 		 * reset the channel state if the channel went down
21871ae08745Sheppo 		 * (other side unconfigured queue) or channel was reset
21883af08d82Slm66018 		 * (other side reconfigured its queue)
21891ae08745Sheppo 		 */
21903af08d82Slm66018 
21913af08d82Slm66018 		if (link_state != ldcp->link_state) {
2192cb112a14Slm66018 
21933af08d82Slm66018 			switch (ldcp->link_state) {
21943af08d82Slm66018 			case LDC_CHANNEL_DOWN:
2195*58283286Sha137994 				D1(ldcp->id, "i_ldc_rx_process_hvq: channel "
21963af08d82Slm66018 				    "link down\n", ldcp->id);
2197d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
21983af08d82Slm66018 				i_ldc_reset(ldcp, B_FALSE);
2199d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
2200*58283286Sha137994 				*notify_client = B_TRUE;
2201*58283286Sha137994 				*notify_event = LDC_EVT_DOWN;
22023af08d82Slm66018 				goto loop_exit;
22031ae08745Sheppo 
22043af08d82Slm66018 			case LDC_CHANNEL_UP:
2205*58283286Sha137994 				D1(ldcp->id, "i_ldc_rx_process_hvq: "
22063af08d82Slm66018 				    "channel link up\n", ldcp->id);
22073af08d82Slm66018 
22083af08d82Slm66018 				if ((ldcp->tstate & ~TS_IN_RESET) == TS_OPEN) {
2209*58283286Sha137994 					*notify_client = B_TRUE;
2210*58283286Sha137994 					*notify_event = LDC_EVT_RESET;
22111ae08745Sheppo 					ldcp->tstate |= TS_LINK_READY;
22121ae08745Sheppo 					ldcp->status = LDC_READY;
22131ae08745Sheppo 				}
22143af08d82Slm66018 				break;
22153af08d82Slm66018 
22163af08d82Slm66018 			case LDC_CHANNEL_RESET:
22173af08d82Slm66018 			default:
22183af08d82Slm66018 #ifdef DEBUG
22193af08d82Slm66018 force_reset:
22203af08d82Slm66018 #endif
2221*58283286Sha137994 				D1(ldcp->id, "i_ldc_rx_process_hvq: channel "
22223af08d82Slm66018 				    "link reset\n", ldcp->id);
22233af08d82Slm66018 				mutex_enter(&ldcp->tx_lock);
22243af08d82Slm66018 				i_ldc_reset(ldcp, B_FALSE);
22253af08d82Slm66018 				mutex_exit(&ldcp->tx_lock);
2226*58283286Sha137994 				*notify_client = B_TRUE;
2227*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
22283af08d82Slm66018 				break;
22293af08d82Slm66018 			}
22303af08d82Slm66018 		}
22313af08d82Slm66018 
22323af08d82Slm66018 #ifdef DEBUG
22333af08d82Slm66018 		if (LDC_INJECT_RESET(ldcp))
22343af08d82Slm66018 			goto force_reset;
22353af08d82Slm66018 #endif
2236*58283286Sha137994 		if (trace_length) {
2237*58283286Sha137994 			TRACE_RXHVQ_LENGTH(ldcp, rx_head, rx_tail);
2238*58283286Sha137994 			trace_length = B_FALSE;
2239*58283286Sha137994 		}
22401ae08745Sheppo 
22411ae08745Sheppo 		if (rx_head == rx_tail) {
2242*58283286Sha137994 			D2(ldcp->id, "i_ldc_rx_process_hvq: (0x%llx) "
2243*58283286Sha137994 			    "No packets\n", ldcp->id);
22441ae08745Sheppo 			break;
22451ae08745Sheppo 		}
22463af08d82Slm66018 
2247*58283286Sha137994 		D2(ldcp->id, "i_ldc_rx_process_hvq: head=0x%llx, "
2248*58283286Sha137994 		    "tail=0x%llx\n", rx_head, rx_tail);
2249*58283286Sha137994 		DUMP_LDC_PKT(ldcp, "i_ldc_rx_process_hvq rcd",
22501ae08745Sheppo 		    ldcp->rx_q_va + rx_head);
22511ae08745Sheppo 
22521ae08745Sheppo 		/* get the message */
22531ae08745Sheppo 		msg = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
22541ae08745Sheppo 
22551ae08745Sheppo 		/* if channel is in RAW mode or data pkt, notify and return */
22561ae08745Sheppo 		if (ldcp->mode == LDC_MODE_RAW) {
2257*58283286Sha137994 			*notify_client = B_TRUE;
2258*58283286Sha137994 			*notify_event |= LDC_EVT_READ;
22591ae08745Sheppo 			break;
22601ae08745Sheppo 		}
22611ae08745Sheppo 
22621ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
22631ae08745Sheppo 
22641ae08745Sheppo 			/* discard packet if channel is not up */
22653af08d82Slm66018 			if ((ldcp->tstate & ~TS_IN_RESET) != TS_UP) {
22661ae08745Sheppo 
22671ae08745Sheppo 				/* move the head one position */
22681ae08745Sheppo 				rx_head = (rx_head + LDC_PACKET_SIZE) %
22691ae08745Sheppo 				    (ldcp->rx_q_entries << LDC_PACKET_SHIFT);
22701ae08745Sheppo 
22711ae08745Sheppo 				if (rv = i_ldc_set_rx_head(ldcp, rx_head))
22721ae08745Sheppo 					break;
22731ae08745Sheppo 
22741ae08745Sheppo 				continue;
22751ae08745Sheppo 			} else {
2276*58283286Sha137994 				uint64_t dq_head, dq_tail;
2277*58283286Sha137994 
2278*58283286Sha137994 				/* process only STREAM mode data packets */
2279*58283286Sha137994 				if (ldcp->mode != LDC_MODE_STREAM) {
22803af08d82Slm66018 					if ((ldcp->tstate & TS_IN_RESET) == 0)
2281*58283286Sha137994 						*notify_client = B_TRUE;
2282*58283286Sha137994 					*notify_event |= LDC_EVT_READ;
22831ae08745Sheppo 					break;
22841ae08745Sheppo 				}
2285*58283286Sha137994 
2286*58283286Sha137994 				/* don't process packet if queue full */
2287*58283286Sha137994 				(void) i_ldc_dq_rx_get_state(ldcp, &dq_head,
2288*58283286Sha137994 				    &dq_tail, NULL);
2289*58283286Sha137994 				dq_tail = (dq_tail + LDC_PACKET_SIZE) %
2290*58283286Sha137994 				    (ldcp->rx_dq_entries << LDC_PACKET_SHIFT);
2291*58283286Sha137994 				if (dq_tail == dq_head ||
2292*58283286Sha137994 				    LDC_INJECT_DQFULL(ldcp)) {
2293*58283286Sha137994 					rv = ENOSPC;
2294*58283286Sha137994 					break;
2295*58283286Sha137994 				}
2296*58283286Sha137994 			}
22971ae08745Sheppo 		}
22981ae08745Sheppo 
22991ae08745Sheppo 		/* Check the sequence ID for the message received */
23003af08d82Slm66018 		rv = i_ldc_check_seqid(ldcp, msg);
23013af08d82Slm66018 		if (rv != 0) {
23021ae08745Sheppo 
2303*58283286Sha137994 			DWARN(ldcp->id, "i_ldc_rx_process_hvq: (0x%llx) "
2304*58283286Sha137994 			    "seqid error, q_ptrs=0x%lx,0x%lx", ldcp->id,
2305*58283286Sha137994 			    rx_head, rx_tail);
23061ae08745Sheppo 
23071ae08745Sheppo 			/* Reset last_msg_rcd to start of message */
2308d10e4ef2Snarayan 			if (first_fragment != 0) {
2309d10e4ef2Snarayan 				ldcp->last_msg_rcd = first_fragment - 1;
2310d10e4ef2Snarayan 				first_fragment = 0;
23111ae08745Sheppo 			}
2312d10e4ef2Snarayan 
23131ae08745Sheppo 			/*
23141ae08745Sheppo 			 * Send a NACK due to seqid mismatch
23151ae08745Sheppo 			 */
231622f747efSnarayan 			rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK,
23171ae08745Sheppo 			    (msg->ctrl & LDC_CTRL_MASK));
23181ae08745Sheppo 
23191ae08745Sheppo 			if (rv) {
2320*58283286Sha137994 				cmn_err(CE_NOTE, "i_ldc_rx_process_hvq: "
2321*58283286Sha137994 				    "(0x%lx) err sending CTRL/DATA NACK msg\n",
2322*58283286Sha137994 				    ldcp->id);
2323d10e4ef2Snarayan 
2324d10e4ef2Snarayan 				/* if cannot send NACK - reset channel */
2325d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
23263af08d82Slm66018 				i_ldc_reset(ldcp, B_TRUE);
2327d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
232883d3bc6fSnarayan 
2329*58283286Sha137994 				*notify_client = B_TRUE;
2330*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
2331d10e4ef2Snarayan 				break;
23321ae08745Sheppo 			}
23331ae08745Sheppo 
23341ae08745Sheppo 			/* purge receive queue */
23351ae08745Sheppo 			(void) i_ldc_set_rx_head(ldcp, rx_tail);
23361ae08745Sheppo 			break;
23371ae08745Sheppo 		}
23381ae08745Sheppo 
23391ae08745Sheppo 		/* record the message ID */
23401ae08745Sheppo 		ldcp->last_msg_rcd = msg->seqid;
23411ae08745Sheppo 
23421ae08745Sheppo 		/* process control messages */
23431ae08745Sheppo 		if (msg->type & LDC_CTRL) {
23441ae08745Sheppo 			/* save current internal state */
23451ae08745Sheppo 			uint64_t tstate = ldcp->tstate;
23461ae08745Sheppo 
23471ae08745Sheppo 			rv = i_ldc_ctrlmsg(ldcp, msg);
23481ae08745Sheppo 			if (rv == EAGAIN) {
23491ae08745Sheppo 				/* re-process pkt - state was adjusted */
23501ae08745Sheppo 				continue;
23511ae08745Sheppo 			}
23521ae08745Sheppo 			if (rv == ECONNRESET) {
2353*58283286Sha137994 				*notify_client = B_TRUE;
2354*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
23551ae08745Sheppo 				break;
23561ae08745Sheppo 			}
23571ae08745Sheppo 
23581ae08745Sheppo 			/*
23591ae08745Sheppo 			 * control message processing was successful
23601ae08745Sheppo 			 * channel transitioned to ready for communication
23611ae08745Sheppo 			 */
23621ae08745Sheppo 			if (rv == 0 && ldcp->tstate == TS_UP &&
23633af08d82Slm66018 			    (tstate & ~TS_IN_RESET) !=
23643af08d82Slm66018 			    (ldcp->tstate & ~TS_IN_RESET)) {
2365*58283286Sha137994 				*notify_client = B_TRUE;
2366*58283286Sha137994 				*notify_event = LDC_EVT_UP;
23671ae08745Sheppo 			}
23681ae08745Sheppo 		}
23691ae08745Sheppo 
237083d3bc6fSnarayan 		/* process data NACKs */
237183d3bc6fSnarayan 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) {
237283d3bc6fSnarayan 			DWARN(ldcp->id,
2373*58283286Sha137994 			    "i_ldc_rx_process_hvq: (0x%llx) received DATA/NACK",
237483d3bc6fSnarayan 			    ldcp->id);
237583d3bc6fSnarayan 			mutex_enter(&ldcp->tx_lock);
237683d3bc6fSnarayan 			i_ldc_reset(ldcp, B_TRUE);
237783d3bc6fSnarayan 			mutex_exit(&ldcp->tx_lock);
2378*58283286Sha137994 			*notify_client = B_TRUE;
2379*58283286Sha137994 			*notify_event = LDC_EVT_RESET;
238083d3bc6fSnarayan 			break;
238183d3bc6fSnarayan 		}
238283d3bc6fSnarayan 
23831ae08745Sheppo 		/* process data ACKs */
23841ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
2385d10e4ef2Snarayan 			if (rv = i_ldc_process_data_ACK(ldcp, msg)) {
2386*58283286Sha137994 				*notify_client = B_TRUE;
2387*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
2388d10e4ef2Snarayan 				break;
2389d10e4ef2Snarayan 			}
23901ae08745Sheppo 		}
23911ae08745Sheppo 
2392*58283286Sha137994 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
2393*58283286Sha137994 			ASSERT(ldcp->mode == LDC_MODE_STREAM);
2394*58283286Sha137994 
2395*58283286Sha137994 			/*
2396*58283286Sha137994 			 * Copy the data packet to the data queue. Note
2397*58283286Sha137994 			 * that the copy routine updates the rx_head pointer.
2398*58283286Sha137994 			 */
2399*58283286Sha137994 			i_ldc_rxdq_copy(ldcp, &rx_head);
2400*58283286Sha137994 
2401*58283286Sha137994 			if ((ldcp->tstate & TS_IN_RESET) == 0)
2402*58283286Sha137994 				*notify_client = B_TRUE;
2403*58283286Sha137994 			*notify_event |= LDC_EVT_READ;
2404*58283286Sha137994 		} else {
24051ae08745Sheppo 			rx_head = (rx_head + LDC_PACKET_SIZE) %
24061ae08745Sheppo 			    (ldcp->rx_q_entries << LDC_PACKET_SHIFT);
2407*58283286Sha137994 		}
2408*58283286Sha137994 
2409*58283286Sha137994 		/* move the head one position */
24100a55fbb7Slm66018 		if (rv = i_ldc_set_rx_head(ldcp, rx_head)) {
2411*58283286Sha137994 			*notify_client = B_TRUE;
2412*58283286Sha137994 			*notify_event = LDC_EVT_RESET;
24131ae08745Sheppo 			break;
24140a55fbb7Slm66018 		}
24151ae08745Sheppo 
24161ae08745Sheppo 	} /* for */
24171ae08745Sheppo 
24183af08d82Slm66018 loop_exit:
24193af08d82Slm66018 
2420*58283286Sha137994 	if (ldcp->mode == LDC_MODE_STREAM) {
2421*58283286Sha137994 		/* ACK data packets */
2422*58283286Sha137994 		if ((*notify_event &
2423*58283286Sha137994 		    (LDC_EVT_READ | LDC_EVT_RESET)) == LDC_EVT_READ) {
2424*58283286Sha137994 			int ack_rv;
2425*58283286Sha137994 			ack_rv = i_ldc_send_pkt(ldcp, LDC_DATA, LDC_ACK, 0);
2426*58283286Sha137994 			if (ack_rv && ack_rv != EWOULDBLOCK) {
2427*58283286Sha137994 				cmn_err(CE_NOTE,
2428*58283286Sha137994 				    "i_ldc_rx_process_hvq: (0x%lx) cannot "
2429*58283286Sha137994 				    "send ACK\n", ldcp->id);
2430*58283286Sha137994 
2431*58283286Sha137994 				mutex_enter(&ldcp->tx_lock);
2432*58283286Sha137994 				i_ldc_reset(ldcp, B_FALSE);
2433*58283286Sha137994 				mutex_exit(&ldcp->tx_lock);
2434*58283286Sha137994 
2435*58283286Sha137994 				*notify_client = B_TRUE;
2436*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
2437*58283286Sha137994 				goto skip_ackpeek;
2438*58283286Sha137994 			}
2439*58283286Sha137994 		}
24401ae08745Sheppo 
24413af08d82Slm66018 		/*
2442*58283286Sha137994 		 * If we have no more space on the data queue, make sure
2443*58283286Sha137994 		 * there are no ACKs on the rx queue waiting to be processed.
24443af08d82Slm66018 		 */
2445*58283286Sha137994 		if (rv == ENOSPC) {
2446*58283286Sha137994 			if (i_ldc_rx_ackpeek(ldcp, rx_head, rx_tail) != 0) {
2447*58283286Sha137994 				ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
2448*58283286Sha137994 				*notify_client = B_TRUE;
2449*58283286Sha137994 				*notify_event = LDC_EVT_RESET;
24501ae08745Sheppo 			}
2451*58283286Sha137994 		} else {
2452*58283286Sha137994 			ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
2453*58283286Sha137994 		}
24541ae08745Sheppo 	}
24551ae08745Sheppo 
2456*58283286Sha137994 skip_ackpeek:
24574d39be2bSsg70180 
2458*58283286Sha137994 	/* Return, indicating whether or not data packets were found */
2459*58283286Sha137994 	if ((*notify_event & (LDC_EVT_READ | LDC_EVT_RESET)) == LDC_EVT_READ)
2460*58283286Sha137994 		return (0);
2461*58283286Sha137994 
2462*58283286Sha137994 	return (ENOMSG);
24631ae08745Sheppo }
24641ae08745Sheppo 
2465*58283286Sha137994 /*
2466*58283286Sha137994  * Process any ACK packets on the HV receive queue.
2467*58283286Sha137994  *
2468*58283286Sha137994  * This function is only used by STREAMING mode channels when the
2469*58283286Sha137994  * secondary data queue fills up and there are packets remaining on
2470*58283286Sha137994  * the HV receive queue.
2471*58283286Sha137994  */
2472*58283286Sha137994 int
2473*58283286Sha137994 i_ldc_rx_ackpeek(ldc_chan_t *ldcp, uint64_t rx_head, uint64_t rx_tail)
2474*58283286Sha137994 {
2475*58283286Sha137994 	int		rv = 0;
2476*58283286Sha137994 	ldc_msg_t	*msg;
2477*58283286Sha137994 
2478*58283286Sha137994 	if (ldcp->rx_ack_head == ACKPEEK_HEAD_INVALID)
2479*58283286Sha137994 		ldcp->rx_ack_head = rx_head;
2480*58283286Sha137994 
2481*58283286Sha137994 	while (ldcp->rx_ack_head != rx_tail) {
2482*58283286Sha137994 		msg = (ldc_msg_t *)(ldcp->rx_q_va + ldcp->rx_ack_head);
2483*58283286Sha137994 
2484*58283286Sha137994 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
2485*58283286Sha137994 			if (rv = i_ldc_process_data_ACK(ldcp, msg))
2486*58283286Sha137994 				break;
2487*58283286Sha137994 			msg->stype &= ~LDC_ACK;
2488*58283286Sha137994 		}
2489*58283286Sha137994 
2490*58283286Sha137994 		ldcp->rx_ack_head =
2491*58283286Sha137994 		    (ldcp->rx_ack_head + LDC_PACKET_SIZE) %
2492*58283286Sha137994 		    (ldcp->rx_q_entries << LDC_PACKET_SHIFT);
2493*58283286Sha137994 	}
2494*58283286Sha137994 	return (rv);
2495*58283286Sha137994 }
24961ae08745Sheppo 
24971ae08745Sheppo /* -------------------------------------------------------------------------- */
24981ae08745Sheppo 
24991ae08745Sheppo /*
25001ae08745Sheppo  * LDC API functions
25011ae08745Sheppo  */
25021ae08745Sheppo 
25031ae08745Sheppo /*
25041ae08745Sheppo  * Initialize the channel. Allocate internal structure and memory for
25051ae08745Sheppo  * TX/RX queues, and initialize locks.
25061ae08745Sheppo  */
25071ae08745Sheppo int
25081ae08745Sheppo ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle)
25091ae08745Sheppo {
25101ae08745Sheppo 	ldc_chan_t 	*ldcp;
25111ae08745Sheppo 	int		rv, exit_val;
25121ae08745Sheppo 	uint64_t	ra_base, nentries;
2513e1ebb9ecSlm66018 	uint64_t	qlen;
25141ae08745Sheppo 
25151ae08745Sheppo 	exit_val = EINVAL;	/* guarantee an error if exit on failure */
25161ae08745Sheppo 
25171ae08745Sheppo 	if (attr == NULL) {
25181ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid attr\n", id);
25191ae08745Sheppo 		return (EINVAL);
25201ae08745Sheppo 	}
25211ae08745Sheppo 	if (handle == NULL) {
25221ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid handle\n", id);
25231ae08745Sheppo 		return (EINVAL);
25241ae08745Sheppo 	}
25251ae08745Sheppo 
25261ae08745Sheppo 	/* check if channel is valid */
25271ae08745Sheppo 	rv = hv_ldc_tx_qinfo(id, &ra_base, &nentries);
25281ae08745Sheppo 	if (rv == H_ECHANNEL) {
25291ae08745Sheppo 		DWARN(id, "ldc_init: (0x%llx) invalid channel id\n", id);
25301ae08745Sheppo 		return (EINVAL);
25311ae08745Sheppo 	}
25321ae08745Sheppo 
25331ae08745Sheppo 	/* check if the channel has already been initialized */
25341ae08745Sheppo 	mutex_enter(&ldcssp->lock);
25351ae08745Sheppo 	ldcp = ldcssp->chan_list;
25361ae08745Sheppo 	while (ldcp != NULL) {
25371ae08745Sheppo 		if (ldcp->id == id) {
25381ae08745Sheppo 			DWARN(id, "ldc_init: (0x%llx) already initialized\n",
25391ae08745Sheppo 			    id);
25401ae08745Sheppo 			mutex_exit(&ldcssp->lock);
25411ae08745Sheppo 			return (EADDRINUSE);
25421ae08745Sheppo 		}
25431ae08745Sheppo 		ldcp = ldcp->next;
25441ae08745Sheppo 	}
25451ae08745Sheppo 	mutex_exit(&ldcssp->lock);
25461ae08745Sheppo 
25471ae08745Sheppo 	ASSERT(ldcp == NULL);
25481ae08745Sheppo 
25491ae08745Sheppo 	*handle = 0;
25501ae08745Sheppo 
25511ae08745Sheppo 	/* Allocate an ldcp structure */
25521ae08745Sheppo 	ldcp = kmem_zalloc(sizeof (ldc_chan_t), KM_SLEEP);
25531ae08745Sheppo 
2554d10e4ef2Snarayan 	/*
2555d10e4ef2Snarayan 	 * Initialize the channel and Tx lock
2556d10e4ef2Snarayan 	 *
2557d10e4ef2Snarayan 	 * The channel 'lock' protects the entire channel and
2558d10e4ef2Snarayan 	 * should be acquired before initializing, resetting,
2559d10e4ef2Snarayan 	 * destroying or reading from a channel.
2560d10e4ef2Snarayan 	 *
2561d10e4ef2Snarayan 	 * The 'tx_lock' should be acquired prior to transmitting
2562d10e4ef2Snarayan 	 * data over the channel. The lock should also be acquired
2563d10e4ef2Snarayan 	 * prior to channel reconfiguration (in order to prevent
2564d10e4ef2Snarayan 	 * concurrent writes).
2565d10e4ef2Snarayan 	 *
2566d10e4ef2Snarayan 	 * ORDERING: When both locks are being acquired, to prevent
2567d10e4ef2Snarayan 	 * deadlocks, the channel lock should be always acquired prior
2568d10e4ef2Snarayan 	 * to the tx_lock.
2569d10e4ef2Snarayan 	 */
25701ae08745Sheppo 	mutex_init(&ldcp->lock, NULL, MUTEX_DRIVER, NULL);
2571d10e4ef2Snarayan 	mutex_init(&ldcp->tx_lock, NULL, MUTEX_DRIVER, NULL);
25721ae08745Sheppo 
25731ae08745Sheppo 	/* Initialize the channel */
25741ae08745Sheppo 	ldcp->id = id;
25751ae08745Sheppo 	ldcp->cb = NULL;
25761ae08745Sheppo 	ldcp->cb_arg = NULL;
25771ae08745Sheppo 	ldcp->cb_inprogress = B_FALSE;
25781ae08745Sheppo 	ldcp->cb_enabled = B_FALSE;
25791ae08745Sheppo 	ldcp->next = NULL;
25801ae08745Sheppo 
25811ae08745Sheppo 	/* Read attributes */
25821ae08745Sheppo 	ldcp->mode = attr->mode;
25831ae08745Sheppo 	ldcp->devclass = attr->devclass;
25841ae08745Sheppo 	ldcp->devinst = attr->instance;
2585e1ebb9ecSlm66018 	ldcp->mtu = (attr->mtu > 0) ? attr->mtu : LDC_DEFAULT_MTU;
25861ae08745Sheppo 
25871ae08745Sheppo 	D1(ldcp->id,
25881ae08745Sheppo 	    "ldc_init: (0x%llx) channel attributes, class=0x%x, "
2589e1ebb9ecSlm66018 	    "instance=0x%llx, mode=%d, mtu=%d\n",
2590e1ebb9ecSlm66018 	    ldcp->id, ldcp->devclass, ldcp->devinst, ldcp->mode, ldcp->mtu);
25911ae08745Sheppo 
25921ae08745Sheppo 	ldcp->next_vidx = 0;
25933af08d82Slm66018 	ldcp->tstate = TS_IN_RESET;
25941ae08745Sheppo 	ldcp->hstate = 0;
25951ae08745Sheppo 	ldcp->last_msg_snt = LDC_INIT_SEQID;
25961ae08745Sheppo 	ldcp->last_ack_rcd = 0;
25971ae08745Sheppo 	ldcp->last_msg_rcd = 0;
2598*58283286Sha137994 	ldcp->rx_ack_head = ACKPEEK_HEAD_INVALID;
25991ae08745Sheppo 
26001ae08745Sheppo 	ldcp->stream_bufferp = NULL;
26011ae08745Sheppo 	ldcp->exp_dring_list = NULL;
26021ae08745Sheppo 	ldcp->imp_dring_list = NULL;
26031ae08745Sheppo 	ldcp->mhdl_list = NULL;
26041ae08745Sheppo 
26053af08d82Slm66018 	ldcp->tx_intr_state = LDC_INTR_NONE;
26063af08d82Slm66018 	ldcp->rx_intr_state = LDC_INTR_NONE;
26073af08d82Slm66018 
26081ae08745Sheppo 	/* Initialize payload size depending on whether channel is reliable */
26091ae08745Sheppo 	switch (ldcp->mode) {
26101ae08745Sheppo 	case LDC_MODE_RAW:
26111ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RAW;
26121ae08745Sheppo 		ldcp->read_p = i_ldc_read_raw;
26131ae08745Sheppo 		ldcp->write_p = i_ldc_write_raw;
26141ae08745Sheppo 		break;
26151ae08745Sheppo 	case LDC_MODE_UNRELIABLE:
26161ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_UNRELIABLE;
26171ae08745Sheppo 		ldcp->read_p = i_ldc_read_packet;
26181ae08745Sheppo 		ldcp->write_p = i_ldc_write_packet;
26191ae08745Sheppo 		break;
26201ae08745Sheppo 	case LDC_MODE_RELIABLE:
26211ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE;
26221ae08745Sheppo 		ldcp->read_p = i_ldc_read_packet;
26231ae08745Sheppo 		ldcp->write_p = i_ldc_write_packet;
26241ae08745Sheppo 		break;
26251ae08745Sheppo 	case LDC_MODE_STREAM:
26261ae08745Sheppo 		ldcp->pkt_payload = LDC_PAYLOAD_SIZE_RELIABLE;
26271ae08745Sheppo 
26281ae08745Sheppo 		ldcp->stream_remains = 0;
26291ae08745Sheppo 		ldcp->stream_offset = 0;
26301ae08745Sheppo 		ldcp->stream_bufferp = kmem_alloc(ldcp->mtu, KM_SLEEP);
26311ae08745Sheppo 		ldcp->read_p = i_ldc_read_stream;
26321ae08745Sheppo 		ldcp->write_p = i_ldc_write_stream;
26331ae08745Sheppo 		break;
26341ae08745Sheppo 	default:
26351ae08745Sheppo 		exit_val = EINVAL;
26361ae08745Sheppo 		goto cleanup_on_exit;
26371ae08745Sheppo 	}
26381ae08745Sheppo 
2639e1ebb9ecSlm66018 	/*
2640e1ebb9ecSlm66018 	 * qlen is (mtu * ldc_mtu_msgs) / pkt_payload. If this
2641e1ebb9ecSlm66018 	 * value is smaller than default length of ldc_queue_entries,
264222f747efSnarayan 	 * qlen is set to ldc_queue_entries. Ensure that computed
264322f747efSnarayan 	 * length is a power-of-two value.
2644e1ebb9ecSlm66018 	 */
2645e1ebb9ecSlm66018 	qlen = (ldcp->mtu * ldc_mtu_msgs) / ldcp->pkt_payload;
264622f747efSnarayan 	if (!ISP2(qlen)) {
264722f747efSnarayan 		uint64_t	tmp = 1;
264822f747efSnarayan 		while (qlen) {
264922f747efSnarayan 			qlen >>= 1; tmp <<= 1;
265022f747efSnarayan 		}
265122f747efSnarayan 		qlen = tmp;
265222f747efSnarayan 	}
265322f747efSnarayan 
2654e1ebb9ecSlm66018 	ldcp->rx_q_entries =
2655e1ebb9ecSlm66018 	    (qlen < ldc_queue_entries) ? ldc_queue_entries : qlen;
2656e1ebb9ecSlm66018 	ldcp->tx_q_entries = ldcp->rx_q_entries;
2657e1ebb9ecSlm66018 
265822f747efSnarayan 	D1(ldcp->id, "ldc_init: queue length = 0x%llx\n", ldcp->rx_q_entries);
2659e1ebb9ecSlm66018 
26601ae08745Sheppo 	/* Create a transmit queue */
26611ae08745Sheppo 	ldcp->tx_q_va = (uint64_t)
26621ae08745Sheppo 	    contig_mem_alloc(ldcp->tx_q_entries << LDC_PACKET_SHIFT);
26631ae08745Sheppo 	if (ldcp->tx_q_va == NULL) {
26641ae08745Sheppo 		cmn_err(CE_WARN,
26651ae08745Sheppo 		    "ldc_init: (0x%lx) TX queue allocation failed\n",
26661ae08745Sheppo 		    ldcp->id);
26671ae08745Sheppo 		exit_val = ENOMEM;
26681ae08745Sheppo 		goto cleanup_on_exit;
26691ae08745Sheppo 	}
26701ae08745Sheppo 	ldcp->tx_q_ra = va_to_pa((caddr_t)ldcp->tx_q_va);
26711ae08745Sheppo 
26721ae08745Sheppo 	D2(ldcp->id, "ldc_init: txq_va=0x%llx, txq_ra=0x%llx, entries=0x%llx\n",
26731ae08745Sheppo 	    ldcp->tx_q_va, ldcp->tx_q_ra, ldcp->tx_q_entries);
26741ae08745Sheppo 
26751ae08745Sheppo 	ldcp->tstate |= TS_TXQ_RDY;
26761ae08745Sheppo 
26771ae08745Sheppo 	/* Create a receive queue */
26781ae08745Sheppo 	ldcp->rx_q_va = (uint64_t)
26791ae08745Sheppo 	    contig_mem_alloc(ldcp->rx_q_entries << LDC_PACKET_SHIFT);
26801ae08745Sheppo 	if (ldcp->rx_q_va == NULL) {
26811ae08745Sheppo 		cmn_err(CE_WARN,
26821ae08745Sheppo 		    "ldc_init: (0x%lx) RX queue allocation failed\n",
26831ae08745Sheppo 		    ldcp->id);
26841ae08745Sheppo 		exit_val = ENOMEM;
26851ae08745Sheppo 		goto cleanup_on_exit;
26861ae08745Sheppo 	}
26871ae08745Sheppo 	ldcp->rx_q_ra = va_to_pa((caddr_t)ldcp->rx_q_va);
26881ae08745Sheppo 
26891ae08745Sheppo 	D2(ldcp->id, "ldc_init: rxq_va=0x%llx, rxq_ra=0x%llx, entries=0x%llx\n",
26901ae08745Sheppo 	    ldcp->rx_q_va, ldcp->rx_q_ra, ldcp->rx_q_entries);
26911ae08745Sheppo 
26921ae08745Sheppo 	ldcp->tstate |= TS_RXQ_RDY;
26931ae08745Sheppo 
2694*58283286Sha137994 	/* Setup a separate read data queue */
2695*58283286Sha137994 	if (ldcp->mode == LDC_MODE_STREAM) {
2696*58283286Sha137994 		ldcp->readq_get_state = i_ldc_dq_rx_get_state;
2697*58283286Sha137994 		ldcp->readq_set_head  = i_ldc_set_rxdq_head;
2698*58283286Sha137994 
2699*58283286Sha137994 		/* Make sure the data queue multiplier is a power of 2 */
2700*58283286Sha137994 		if (!ISP2(ldc_rxdq_multiplier)) {
2701*58283286Sha137994 			D1(ldcp->id, "ldc_init: (0x%llx) ldc_rxdq_multiplier "
2702*58283286Sha137994 			    "not a power of 2, resetting", ldcp->id);
2703*58283286Sha137994 			ldc_rxdq_multiplier = LDC_RXDQ_MULTIPLIER;
2704*58283286Sha137994 		}
2705*58283286Sha137994 
2706*58283286Sha137994 		ldcp->rx_dq_entries = ldc_rxdq_multiplier * ldcp->rx_q_entries;
2707*58283286Sha137994 		ldcp->rx_dq_va = (uint64_t)
2708*58283286Sha137994 		    kmem_alloc(ldcp->rx_dq_entries << LDC_PACKET_SHIFT,
2709*58283286Sha137994 		    KM_SLEEP);
2710*58283286Sha137994 		if (ldcp->rx_dq_va == NULL) {
2711*58283286Sha137994 			cmn_err(CE_WARN,
2712*58283286Sha137994 			    "ldc_init: (0x%lx) RX data queue "
2713*58283286Sha137994 			    "allocation failed\n", ldcp->id);
2714*58283286Sha137994 			exit_val = ENOMEM;
2715*58283286Sha137994 			goto cleanup_on_exit;
2716*58283286Sha137994 		}
2717*58283286Sha137994 
2718*58283286Sha137994 		ldcp->rx_dq_head = ldcp->rx_dq_tail = 0;
2719*58283286Sha137994 
2720*58283286Sha137994 		D2(ldcp->id, "ldc_init: rx_dq_va=0x%llx, "
2721*58283286Sha137994 		    "rx_dq_entries=0x%llx\n", ldcp->rx_dq_va,
2722*58283286Sha137994 		    ldcp->rx_dq_entries);
2723*58283286Sha137994 	} else {
2724*58283286Sha137994 		ldcp->readq_get_state = i_ldc_hvq_rx_get_state;
2725*58283286Sha137994 		ldcp->readq_set_head  = i_ldc_set_rx_head;
2726*58283286Sha137994 	}
2727*58283286Sha137994 
27281ae08745Sheppo 	/* Init descriptor ring and memory handle list lock */
27291ae08745Sheppo 	mutex_init(&ldcp->exp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
27301ae08745Sheppo 	mutex_init(&ldcp->imp_dlist_lock, NULL, MUTEX_DRIVER, NULL);
27311ae08745Sheppo 	mutex_init(&ldcp->mlist_lock, NULL, MUTEX_DRIVER, NULL);
27321ae08745Sheppo 
27331ae08745Sheppo 	/* mark status as INITialized */
27341ae08745Sheppo 	ldcp->status = LDC_INIT;
27351ae08745Sheppo 
27361ae08745Sheppo 	/* Add to channel list */
27371ae08745Sheppo 	mutex_enter(&ldcssp->lock);
27381ae08745Sheppo 	ldcp->next = ldcssp->chan_list;
27391ae08745Sheppo 	ldcssp->chan_list = ldcp;
27401ae08745Sheppo 	ldcssp->channel_count++;
27411ae08745Sheppo 	mutex_exit(&ldcssp->lock);
27421ae08745Sheppo 
27431ae08745Sheppo 	/* set the handle */
27441ae08745Sheppo 	*handle = (ldc_handle_t)ldcp;
27451ae08745Sheppo 
27461ae08745Sheppo 	D1(ldcp->id, "ldc_init: (0x%llx) channel initialized\n", ldcp->id);
27471ae08745Sheppo 
27481ae08745Sheppo 	return (0);
27491ae08745Sheppo 
27501ae08745Sheppo cleanup_on_exit:
27511ae08745Sheppo 
27521ae08745Sheppo 	if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp)
27531ae08745Sheppo 		kmem_free(ldcp->stream_bufferp, ldcp->mtu);
27541ae08745Sheppo 
27551ae08745Sheppo 	if (ldcp->tstate & TS_TXQ_RDY)
27561ae08745Sheppo 		contig_mem_free((caddr_t)ldcp->tx_q_va,
27571ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT));
27581ae08745Sheppo 
27591ae08745Sheppo 	if (ldcp->tstate & TS_RXQ_RDY)
27601ae08745Sheppo 		contig_mem_free((caddr_t)ldcp->rx_q_va,
27611ae08745Sheppo 		    (ldcp->rx_q_entries << LDC_PACKET_SHIFT));
27621ae08745Sheppo 
2763d10e4ef2Snarayan 	mutex_destroy(&ldcp->tx_lock);
27641ae08745Sheppo 	mutex_destroy(&ldcp->lock);
27651ae08745Sheppo 
27661ae08745Sheppo 	if (ldcp)
27671ae08745Sheppo 		kmem_free(ldcp, sizeof (ldc_chan_t));
27681ae08745Sheppo 
27691ae08745Sheppo 	return (exit_val);
27701ae08745Sheppo }
27711ae08745Sheppo 
27721ae08745Sheppo /*
27731ae08745Sheppo  * Finalizes the LDC connection. It will return EBUSY if the
27741ae08745Sheppo  * channel is open. A ldc_close() has to be done prior to
27751ae08745Sheppo  * a ldc_fini operation. It frees TX/RX queues, associated
27761ae08745Sheppo  * with the channel
27771ae08745Sheppo  */
27781ae08745Sheppo int
27791ae08745Sheppo ldc_fini(ldc_handle_t handle)
27801ae08745Sheppo {
27811ae08745Sheppo 	ldc_chan_t 	*ldcp;
27821ae08745Sheppo 	ldc_chan_t 	*tmp_ldcp;
27831ae08745Sheppo 	uint64_t 	id;
27841ae08745Sheppo 
27851ae08745Sheppo 	if (handle == NULL) {
27861ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel handle\n");
27871ae08745Sheppo 		return (EINVAL);
27881ae08745Sheppo 	}
27891ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
27901ae08745Sheppo 	id = ldcp->id;
27911ae08745Sheppo 
27921ae08745Sheppo 	mutex_enter(&ldcp->lock);
27931ae08745Sheppo 
27943af08d82Slm66018 	if ((ldcp->tstate & ~TS_IN_RESET) > TS_INIT) {
27951ae08745Sheppo 		DWARN(ldcp->id, "ldc_fini: (0x%llx) channel is open\n",
27961ae08745Sheppo 		    ldcp->id);
27971ae08745Sheppo 		mutex_exit(&ldcp->lock);
27981ae08745Sheppo 		return (EBUSY);
27991ae08745Sheppo 	}
28001ae08745Sheppo 
28011ae08745Sheppo 	/* Remove from the channel list */
28021ae08745Sheppo 	mutex_enter(&ldcssp->lock);
28031ae08745Sheppo 	tmp_ldcp = ldcssp->chan_list;
28041ae08745Sheppo 	if (tmp_ldcp == ldcp) {
28051ae08745Sheppo 		ldcssp->chan_list = ldcp->next;
28061ae08745Sheppo 		ldcp->next = NULL;
28071ae08745Sheppo 	} else {
28081ae08745Sheppo 		while (tmp_ldcp != NULL) {
28091ae08745Sheppo 			if (tmp_ldcp->next == ldcp) {
28101ae08745Sheppo 				tmp_ldcp->next = ldcp->next;
28111ae08745Sheppo 				ldcp->next = NULL;
28121ae08745Sheppo 				break;
28131ae08745Sheppo 			}
28141ae08745Sheppo 			tmp_ldcp = tmp_ldcp->next;
28151ae08745Sheppo 		}
28161ae08745Sheppo 		if (tmp_ldcp == NULL) {
28171ae08745Sheppo 			DWARN(DBG_ALL_LDCS, "ldc_fini: invalid channel hdl\n");
28181ae08745Sheppo 			mutex_exit(&ldcssp->lock);
28191ae08745Sheppo 			mutex_exit(&ldcp->lock);
28201ae08745Sheppo 			return (EINVAL);
28211ae08745Sheppo 		}
28221ae08745Sheppo 	}
28231ae08745Sheppo 
28241ae08745Sheppo 	ldcssp->channel_count--;
28251ae08745Sheppo 
28261ae08745Sheppo 	mutex_exit(&ldcssp->lock);
28271ae08745Sheppo 
28281ae08745Sheppo 	/* Free the map table for this channel */
28291ae08745Sheppo 	if (ldcp->mtbl) {
28301ae08745Sheppo 		(void) hv_ldc_set_map_table(ldcp->id, NULL, NULL);
28313af08d82Slm66018 		if (ldcp->mtbl->contigmem)
28321ae08745Sheppo 			contig_mem_free(ldcp->mtbl->table, ldcp->mtbl->size);
28333af08d82Slm66018 		else
28343af08d82Slm66018 			kmem_free(ldcp->mtbl->table, ldcp->mtbl->size);
28351ae08745Sheppo 		mutex_destroy(&ldcp->mtbl->lock);
28361ae08745Sheppo 		kmem_free(ldcp->mtbl, sizeof (ldc_mtbl_t));
28371ae08745Sheppo 	}
28381ae08745Sheppo 
28391ae08745Sheppo 	/* Destroy descriptor ring and memory handle list lock */
28401ae08745Sheppo 	mutex_destroy(&ldcp->exp_dlist_lock);
28411ae08745Sheppo 	mutex_destroy(&ldcp->imp_dlist_lock);
28421ae08745Sheppo 	mutex_destroy(&ldcp->mlist_lock);
28431ae08745Sheppo 
28441ae08745Sheppo 	/* Free the stream buffer for STREAM_MODE */
28451ae08745Sheppo 	if (ldcp->mode == LDC_MODE_STREAM && ldcp->stream_bufferp)
28461ae08745Sheppo 		kmem_free(ldcp->stream_bufferp, ldcp->mtu);
28471ae08745Sheppo 
28481ae08745Sheppo 	/* Free the RX queue */
28491ae08745Sheppo 	contig_mem_free((caddr_t)ldcp->rx_q_va,
28501ae08745Sheppo 	    (ldcp->rx_q_entries << LDC_PACKET_SHIFT));
28511ae08745Sheppo 	ldcp->tstate &= ~TS_RXQ_RDY;
28521ae08745Sheppo 
2853*58283286Sha137994 	/* Free the RX data queue */
2854*58283286Sha137994 	if (ldcp->mode == LDC_MODE_STREAM) {
2855*58283286Sha137994 		kmem_free((caddr_t)ldcp->rx_dq_va,
2856*58283286Sha137994 		    (ldcp->rx_dq_entries << LDC_PACKET_SHIFT));
2857*58283286Sha137994 	}
2858*58283286Sha137994 
28591ae08745Sheppo 	/* Free the TX queue */
28601ae08745Sheppo 	contig_mem_free((caddr_t)ldcp->tx_q_va,
28611ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT));
28621ae08745Sheppo 	ldcp->tstate &= ~TS_TXQ_RDY;
28631ae08745Sheppo 
28641ae08745Sheppo 	mutex_exit(&ldcp->lock);
28651ae08745Sheppo 
28661ae08745Sheppo 	/* Destroy mutex */
2867d10e4ef2Snarayan 	mutex_destroy(&ldcp->tx_lock);
28681ae08745Sheppo 	mutex_destroy(&ldcp->lock);
28691ae08745Sheppo 
28701ae08745Sheppo 	/* free channel structure */
28711ae08745Sheppo 	kmem_free(ldcp, sizeof (ldc_chan_t));
28721ae08745Sheppo 
28731ae08745Sheppo 	D1(id, "ldc_fini: (0x%llx) channel finalized\n", id);
28741ae08745Sheppo 
28751ae08745Sheppo 	return (0);
28761ae08745Sheppo }
28771ae08745Sheppo 
28781ae08745Sheppo /*
28791ae08745Sheppo  * Open the LDC channel for use. It registers the TX/RX queues
28801ae08745Sheppo  * with the Hypervisor. It also specifies the interrupt number
28811ae08745Sheppo  * and target CPU for this channel
28821ae08745Sheppo  */
28831ae08745Sheppo int
28841ae08745Sheppo ldc_open(ldc_handle_t handle)
28851ae08745Sheppo {
28861ae08745Sheppo 	ldc_chan_t 	*ldcp;
28871ae08745Sheppo 	int 		rv;
28881ae08745Sheppo 
28891ae08745Sheppo 	if (handle == NULL) {
28901ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_open: invalid channel handle\n");
28911ae08745Sheppo 		return (EINVAL);
28921ae08745Sheppo 	}
28931ae08745Sheppo 
28941ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
28951ae08745Sheppo 
28961ae08745Sheppo 	mutex_enter(&ldcp->lock);
28971ae08745Sheppo 
28981ae08745Sheppo 	if (ldcp->tstate < TS_INIT) {
28991ae08745Sheppo 		DWARN(ldcp->id,
29001ae08745Sheppo 		    "ldc_open: (0x%llx) channel not initialized\n", ldcp->id);
29011ae08745Sheppo 		mutex_exit(&ldcp->lock);
29021ae08745Sheppo 		return (EFAULT);
29031ae08745Sheppo 	}
29043af08d82Slm66018 	if ((ldcp->tstate & ~TS_IN_RESET) >= TS_OPEN) {
29051ae08745Sheppo 		DWARN(ldcp->id,
29061ae08745Sheppo 		    "ldc_open: (0x%llx) channel is already open\n", ldcp->id);
29071ae08745Sheppo 		mutex_exit(&ldcp->lock);
29081ae08745Sheppo 		return (EFAULT);
29091ae08745Sheppo 	}
29101ae08745Sheppo 
29111ae08745Sheppo 	/*
29121ae08745Sheppo 	 * Unregister/Register the tx queue with the hypervisor
29131ae08745Sheppo 	 */
29141ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
29151ae08745Sheppo 	if (rv) {
29161ae08745Sheppo 		cmn_err(CE_WARN,
29171ae08745Sheppo 		    "ldc_open: (0x%lx) channel tx queue unconf failed\n",
29181ae08745Sheppo 		    ldcp->id);
29191ae08745Sheppo 		mutex_exit(&ldcp->lock);
29201ae08745Sheppo 		return (EIO);
29211ae08745Sheppo 	}
29221ae08745Sheppo 
29231ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries);
29241ae08745Sheppo 	if (rv) {
29251ae08745Sheppo 		cmn_err(CE_WARN,
29261ae08745Sheppo 		    "ldc_open: (0x%lx) channel tx queue conf failed\n",
29271ae08745Sheppo 		    ldcp->id);
29281ae08745Sheppo 		mutex_exit(&ldcp->lock);
29291ae08745Sheppo 		return (EIO);
29301ae08745Sheppo 	}
29311ae08745Sheppo 
29321ae08745Sheppo 	D2(ldcp->id, "ldc_open: (0x%llx) registered tx queue with LDC\n",
29331ae08745Sheppo 	    ldcp->id);
29341ae08745Sheppo 
29351ae08745Sheppo 	/*
29361ae08745Sheppo 	 * Unregister/Register the rx queue with the hypervisor
29371ae08745Sheppo 	 */
29381ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
29391ae08745Sheppo 	if (rv) {
29401ae08745Sheppo 		cmn_err(CE_WARN,
29411ae08745Sheppo 		    "ldc_open: (0x%lx) channel rx queue unconf failed\n",
29421ae08745Sheppo 		    ldcp->id);
29431ae08745Sheppo 		mutex_exit(&ldcp->lock);
29441ae08745Sheppo 		return (EIO);
29451ae08745Sheppo 	}
29461ae08745Sheppo 
29471ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, ldcp->rx_q_ra, ldcp->rx_q_entries);
29481ae08745Sheppo 	if (rv) {
29491ae08745Sheppo 		cmn_err(CE_WARN,
29501ae08745Sheppo 		    "ldc_open: (0x%lx) channel rx queue conf failed\n",
29511ae08745Sheppo 		    ldcp->id);
29521ae08745Sheppo 		mutex_exit(&ldcp->lock);
29531ae08745Sheppo 		return (EIO);
29541ae08745Sheppo 	}
29551ae08745Sheppo 
29561ae08745Sheppo 	D2(ldcp->id, "ldc_open: (0x%llx) registered rx queue with LDC\n",
29571ae08745Sheppo 	    ldcp->id);
29581ae08745Sheppo 
29591ae08745Sheppo 	ldcp->tstate |= TS_QCONF_RDY;
29601ae08745Sheppo 
29611ae08745Sheppo 	/* Register the channel with the channel nexus */
29621ae08745Sheppo 	rv = i_ldc_register_channel(ldcp);
29631ae08745Sheppo 	if (rv && rv != EAGAIN) {
29641ae08745Sheppo 		cmn_err(CE_WARN,
29651ae08745Sheppo 		    "ldc_open: (0x%lx) channel register failed\n", ldcp->id);
29661ae08745Sheppo 		(void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
29671ae08745Sheppo 		(void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
29681ae08745Sheppo 		mutex_exit(&ldcp->lock);
29691ae08745Sheppo 		return (EIO);
29701ae08745Sheppo 	}
29711ae08745Sheppo 
29721ae08745Sheppo 	/* mark channel in OPEN state */
29731ae08745Sheppo 	ldcp->status = LDC_OPEN;
29741ae08745Sheppo 
29751ae08745Sheppo 	/* Read channel state */
29761ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
29771ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
29781ae08745Sheppo 	if (rv) {
29791ae08745Sheppo 		cmn_err(CE_WARN,
29801ae08745Sheppo 		    "ldc_open: (0x%lx) cannot read channel state\n",
29811ae08745Sheppo 		    ldcp->id);
29821ae08745Sheppo 		(void) i_ldc_unregister_channel(ldcp);
29831ae08745Sheppo 		(void) hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
29841ae08745Sheppo 		(void) hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
29851ae08745Sheppo 		mutex_exit(&ldcp->lock);
29861ae08745Sheppo 		return (EIO);
29871ae08745Sheppo 	}
29881ae08745Sheppo 
29891ae08745Sheppo 	/*
29901ae08745Sheppo 	 * set the ACKd head to current head location for reliable &
29911ae08745Sheppo 	 * streaming mode
29921ae08745Sheppo 	 */
29931ae08745Sheppo 	ldcp->tx_ackd_head = ldcp->tx_head;
29941ae08745Sheppo 
29951ae08745Sheppo 	/* mark channel ready if HV report link is UP (peer alloc'd Rx queue) */
29961ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_UP ||
29971ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
29981ae08745Sheppo 		ldcp->tstate |= TS_LINK_READY;
29991ae08745Sheppo 		ldcp->status = LDC_READY;
30001ae08745Sheppo 	}
30011ae08745Sheppo 
30021ae08745Sheppo 	/*
30031ae08745Sheppo 	 * if channel is being opened in RAW mode - no handshake is needed
30041ae08745Sheppo 	 * switch the channel READY and UP state
30051ae08745Sheppo 	 */
30061ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW) {
30071ae08745Sheppo 		ldcp->tstate = TS_UP;	/* set bits associated with LDC UP */
30081ae08745Sheppo 		ldcp->status = LDC_UP;
30091ae08745Sheppo 	}
30101ae08745Sheppo 
30111ae08745Sheppo 	mutex_exit(&ldcp->lock);
30121ae08745Sheppo 
30131ae08745Sheppo 	/*
30141ae08745Sheppo 	 * Increment number of open channels
30151ae08745Sheppo 	 */
30161ae08745Sheppo 	mutex_enter(&ldcssp->lock);
30171ae08745Sheppo 	ldcssp->channels_open++;
30181ae08745Sheppo 	mutex_exit(&ldcssp->lock);
30191ae08745Sheppo 
3020cb112a14Slm66018 	D1(ldcp->id,
30213af08d82Slm66018 	    "ldc_open: (0x%llx) channel (0x%p) open for use "
30223af08d82Slm66018 	    "(tstate=0x%x, status=0x%x)\n",
30233af08d82Slm66018 	    ldcp->id, ldcp, ldcp->tstate, ldcp->status);
30241ae08745Sheppo 
30251ae08745Sheppo 	return (0);
30261ae08745Sheppo }
30271ae08745Sheppo 
30281ae08745Sheppo /*
30291ae08745Sheppo  * Close the LDC connection. It will return EBUSY if there
30301ae08745Sheppo  * are memory segments or descriptor rings either bound to or
30311ae08745Sheppo  * mapped over the channel
30321ae08745Sheppo  */
30331ae08745Sheppo int
30341ae08745Sheppo ldc_close(ldc_handle_t handle)
30351ae08745Sheppo {
30361ae08745Sheppo 	ldc_chan_t 	*ldcp;
3037d10e4ef2Snarayan 	int		rv = 0, retries = 0;
30381ae08745Sheppo 	boolean_t	chk_done = B_FALSE;
30391ae08745Sheppo 
30401ae08745Sheppo 	if (handle == NULL) {
30411ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_close: invalid channel handle\n");
30421ae08745Sheppo 		return (EINVAL);
30431ae08745Sheppo 	}
30441ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
30451ae08745Sheppo 
30461ae08745Sheppo 	mutex_enter(&ldcp->lock);
30471ae08745Sheppo 
30481ae08745Sheppo 	/* return error if channel is not open */
30493af08d82Slm66018 	if ((ldcp->tstate & ~TS_IN_RESET) < TS_OPEN) {
30501ae08745Sheppo 		DWARN(ldcp->id,
30511ae08745Sheppo 		    "ldc_close: (0x%llx) channel is not open\n", ldcp->id);
30521ae08745Sheppo 		mutex_exit(&ldcp->lock);
30531ae08745Sheppo 		return (EFAULT);
30541ae08745Sheppo 	}
30551ae08745Sheppo 
30561ae08745Sheppo 	/* if any memory handles, drings, are bound or mapped cannot close */
30571ae08745Sheppo 	if (ldcp->mhdl_list != NULL) {
30581ae08745Sheppo 		DWARN(ldcp->id,
30591ae08745Sheppo 		    "ldc_close: (0x%llx) channel has bound memory handles\n",
30601ae08745Sheppo 		    ldcp->id);
30611ae08745Sheppo 		mutex_exit(&ldcp->lock);
30621ae08745Sheppo 		return (EBUSY);
30631ae08745Sheppo 	}
30641ae08745Sheppo 	if (ldcp->exp_dring_list != NULL) {
30651ae08745Sheppo 		DWARN(ldcp->id,
30661ae08745Sheppo 		    "ldc_close: (0x%llx) channel has bound descriptor rings\n",
30671ae08745Sheppo 		    ldcp->id);
30681ae08745Sheppo 		mutex_exit(&ldcp->lock);
30691ae08745Sheppo 		return (EBUSY);
30701ae08745Sheppo 	}
30711ae08745Sheppo 	if (ldcp->imp_dring_list != NULL) {
30721ae08745Sheppo 		DWARN(ldcp->id,
30731ae08745Sheppo 		    "ldc_close: (0x%llx) channel has mapped descriptor rings\n",
30741ae08745Sheppo 		    ldcp->id);
30751ae08745Sheppo 		mutex_exit(&ldcp->lock);
30761ae08745Sheppo 		return (EBUSY);
30771ae08745Sheppo 	}
30781ae08745Sheppo 
30794d39be2bSsg70180 	if (ldcp->cb_inprogress) {
30804d39be2bSsg70180 		DWARN(ldcp->id, "ldc_close: (0x%llx) callback active\n",
30814d39be2bSsg70180 		    ldcp->id);
30824d39be2bSsg70180 		mutex_exit(&ldcp->lock);
30834d39be2bSsg70180 		return (EWOULDBLOCK);
30844d39be2bSsg70180 	}
30854d39be2bSsg70180 
3086d10e4ef2Snarayan 	/* Obtain Tx lock */
3087d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
3088d10e4ef2Snarayan 
30891ae08745Sheppo 	/*
30901ae08745Sheppo 	 * Wait for pending transmits to complete i.e Tx queue to drain
30911ae08745Sheppo 	 * if there are pending pkts - wait 1 ms and retry again
30921ae08745Sheppo 	 */
30931ae08745Sheppo 	for (;;) {
30941ae08745Sheppo 
30951ae08745Sheppo 		rv = hv_ldc_tx_get_state(ldcp->id,
30961ae08745Sheppo 		    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
30971ae08745Sheppo 		if (rv) {
30981ae08745Sheppo 			cmn_err(CE_WARN,
30991ae08745Sheppo 			    "ldc_close: (0x%lx) cannot read qptrs\n", ldcp->id);
3100d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
31011ae08745Sheppo 			mutex_exit(&ldcp->lock);
31021ae08745Sheppo 			return (EIO);
31031ae08745Sheppo 		}
31041ae08745Sheppo 
31051ae08745Sheppo 		if (ldcp->tx_head == ldcp->tx_tail ||
31061ae08745Sheppo 		    ldcp->link_state != LDC_CHANNEL_UP) {
31071ae08745Sheppo 			break;
31081ae08745Sheppo 		}
31091ae08745Sheppo 
31101ae08745Sheppo 		if (chk_done) {
31111ae08745Sheppo 			DWARN(ldcp->id,
31121ae08745Sheppo 			    "ldc_close: (0x%llx) Tx queue drain timeout\n",
31131ae08745Sheppo 			    ldcp->id);
31141ae08745Sheppo 			break;
31151ae08745Sheppo 		}
31161ae08745Sheppo 
31171ae08745Sheppo 		/* wait for one ms and try again */
31181ae08745Sheppo 		delay(drv_usectohz(1000));
31191ae08745Sheppo 		chk_done = B_TRUE;
31201ae08745Sheppo 	}
31211ae08745Sheppo 
31221ae08745Sheppo 	/*
3123a8ea4edeSnarayan 	 * Drain the Tx and Rx queues as we are closing the
3124a8ea4edeSnarayan 	 * channel. We dont care about any pending packets.
3125a8ea4edeSnarayan 	 * We have to also drain the queue prior to clearing
3126a8ea4edeSnarayan 	 * pending interrupts, otherwise the HV will trigger
3127a8ea4edeSnarayan 	 * an interrupt the moment the interrupt state is
3128a8ea4edeSnarayan 	 * cleared.
31293af08d82Slm66018 	 */
31303af08d82Slm66018 	(void) i_ldc_txq_reconf(ldcp);
3131a8ea4edeSnarayan 	(void) i_ldc_rxq_drain(ldcp);
31323af08d82Slm66018 
31333af08d82Slm66018 	/*
31341ae08745Sheppo 	 * Unregister the channel with the nexus
31351ae08745Sheppo 	 */
3136d10e4ef2Snarayan 	while ((rv = i_ldc_unregister_channel(ldcp)) != 0) {
3137d10e4ef2Snarayan 
3138d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
31391ae08745Sheppo 		mutex_exit(&ldcp->lock);
3140d10e4ef2Snarayan 
3141d10e4ef2Snarayan 		/* if any error other than EAGAIN return back */
3142a8ea4edeSnarayan 		if (rv != EAGAIN || retries >= ldc_max_retries) {
3143d10e4ef2Snarayan 			cmn_err(CE_WARN,
3144d10e4ef2Snarayan 			    "ldc_close: (0x%lx) unregister failed, %d\n",
3145d10e4ef2Snarayan 			    ldcp->id, rv);
31461ae08745Sheppo 			return (rv);
31471ae08745Sheppo 		}
31481ae08745Sheppo 
31491ae08745Sheppo 		/*
3150d10e4ef2Snarayan 		 * As there could be pending interrupts we need
3151d10e4ef2Snarayan 		 * to wait and try again
3152d10e4ef2Snarayan 		 */
31534d39be2bSsg70180 		drv_usecwait(ldc_close_delay);
3154d10e4ef2Snarayan 		mutex_enter(&ldcp->lock);
3155d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
3156d10e4ef2Snarayan 		retries++;
3157d10e4ef2Snarayan 	}
3158d10e4ef2Snarayan 
3159d10e4ef2Snarayan 	/*
31601ae08745Sheppo 	 * Unregister queues
31611ae08745Sheppo 	 */
31621ae08745Sheppo 	rv = hv_ldc_tx_qconf(ldcp->id, NULL, NULL);
31631ae08745Sheppo 	if (rv) {
31641ae08745Sheppo 		cmn_err(CE_WARN,
31651ae08745Sheppo 		    "ldc_close: (0x%lx) channel TX queue unconf failed\n",
31661ae08745Sheppo 		    ldcp->id);
3167d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
31681ae08745Sheppo 		mutex_exit(&ldcp->lock);
31691ae08745Sheppo 		return (EIO);
31701ae08745Sheppo 	}
31711ae08745Sheppo 	rv = hv_ldc_rx_qconf(ldcp->id, NULL, NULL);
31721ae08745Sheppo 	if (rv) {
31731ae08745Sheppo 		cmn_err(CE_WARN,
31741ae08745Sheppo 		    "ldc_close: (0x%lx) channel RX queue unconf failed\n",
31751ae08745Sheppo 		    ldcp->id);
3176d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
31771ae08745Sheppo 		mutex_exit(&ldcp->lock);
31781ae08745Sheppo 		return (EIO);
31791ae08745Sheppo 	}
31801ae08745Sheppo 
31811ae08745Sheppo 	ldcp->tstate &= ~TS_QCONF_RDY;
31821ae08745Sheppo 
31831ae08745Sheppo 	/* Reset channel state information */
31841ae08745Sheppo 	i_ldc_reset_state(ldcp);
31851ae08745Sheppo 
31861ae08745Sheppo 	/* Mark channel as down and in initialized state */
31871ae08745Sheppo 	ldcp->tx_ackd_head = 0;
31881ae08745Sheppo 	ldcp->tx_head = 0;
31893af08d82Slm66018 	ldcp->tstate = TS_IN_RESET|TS_INIT;
31901ae08745Sheppo 	ldcp->status = LDC_INIT;
31911ae08745Sheppo 
3192d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
31931ae08745Sheppo 	mutex_exit(&ldcp->lock);
31941ae08745Sheppo 
31951ae08745Sheppo 	/* Decrement number of open channels */
31961ae08745Sheppo 	mutex_enter(&ldcssp->lock);
31971ae08745Sheppo 	ldcssp->channels_open--;
31981ae08745Sheppo 	mutex_exit(&ldcssp->lock);
31991ae08745Sheppo 
32001ae08745Sheppo 	D1(ldcp->id, "ldc_close: (0x%llx) channel closed\n", ldcp->id);
32011ae08745Sheppo 
32021ae08745Sheppo 	return (0);
32031ae08745Sheppo }
32041ae08745Sheppo 
32051ae08745Sheppo /*
32061ae08745Sheppo  * Register channel callback
32071ae08745Sheppo  */
32081ae08745Sheppo int
32091ae08745Sheppo ldc_reg_callback(ldc_handle_t handle,
32101ae08745Sheppo     uint_t(*cb)(uint64_t event, caddr_t arg), caddr_t arg)
32111ae08745Sheppo {
32121ae08745Sheppo 	ldc_chan_t *ldcp;
32131ae08745Sheppo 
32141ae08745Sheppo 	if (handle == NULL) {
32151ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
32161ae08745Sheppo 		    "ldc_reg_callback: invalid channel handle\n");
32171ae08745Sheppo 		return (EINVAL);
32181ae08745Sheppo 	}
32191ae08745Sheppo 	if (((uint64_t)cb) < KERNELBASE) {
32201ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_reg_callback: invalid callback\n");
32211ae08745Sheppo 		return (EINVAL);
32221ae08745Sheppo 	}
32231ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
32241ae08745Sheppo 
32251ae08745Sheppo 	mutex_enter(&ldcp->lock);
32261ae08745Sheppo 
32271ae08745Sheppo 	if (ldcp->cb) {
32281ae08745Sheppo 		DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback exists\n",
32291ae08745Sheppo 		    ldcp->id);
32301ae08745Sheppo 		mutex_exit(&ldcp->lock);
32311ae08745Sheppo 		return (EIO);
32321ae08745Sheppo 	}
32331ae08745Sheppo 	if (ldcp->cb_inprogress) {
32341ae08745Sheppo 		DWARN(ldcp->id, "ldc_reg_callback: (0x%llx) callback active\n",
32351ae08745Sheppo 		    ldcp->id);
32361ae08745Sheppo 		mutex_exit(&ldcp->lock);
32371ae08745Sheppo 		return (EWOULDBLOCK);
32381ae08745Sheppo 	}
32391ae08745Sheppo 
32401ae08745Sheppo 	ldcp->cb = cb;
32411ae08745Sheppo 	ldcp->cb_arg = arg;
32421ae08745Sheppo 	ldcp->cb_enabled = B_TRUE;
32431ae08745Sheppo 
32441ae08745Sheppo 	D1(ldcp->id,
32451ae08745Sheppo 	    "ldc_reg_callback: (0x%llx) registered callback for channel\n",
32461ae08745Sheppo 	    ldcp->id);
32471ae08745Sheppo 
32481ae08745Sheppo 	mutex_exit(&ldcp->lock);
32491ae08745Sheppo 
32501ae08745Sheppo 	return (0);
32511ae08745Sheppo }
32521ae08745Sheppo 
32531ae08745Sheppo /*
32541ae08745Sheppo  * Unregister channel callback
32551ae08745Sheppo  */
32561ae08745Sheppo int
32571ae08745Sheppo ldc_unreg_callback(ldc_handle_t handle)
32581ae08745Sheppo {
32591ae08745Sheppo 	ldc_chan_t *ldcp;
32601ae08745Sheppo 
32611ae08745Sheppo 	if (handle == NULL) {
32621ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
32631ae08745Sheppo 		    "ldc_unreg_callback: invalid channel handle\n");
32641ae08745Sheppo 		return (EINVAL);
32651ae08745Sheppo 	}
32661ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
32671ae08745Sheppo 
32681ae08745Sheppo 	mutex_enter(&ldcp->lock);
32691ae08745Sheppo 
32701ae08745Sheppo 	if (ldcp->cb == NULL) {
32711ae08745Sheppo 		DWARN(ldcp->id,
32721ae08745Sheppo 		    "ldc_unreg_callback: (0x%llx) no callback exists\n",
32731ae08745Sheppo 		    ldcp->id);
32741ae08745Sheppo 		mutex_exit(&ldcp->lock);
32751ae08745Sheppo 		return (EIO);
32761ae08745Sheppo 	}
32771ae08745Sheppo 	if (ldcp->cb_inprogress) {
32781ae08745Sheppo 		DWARN(ldcp->id,
32791ae08745Sheppo 		    "ldc_unreg_callback: (0x%llx) callback active\n",
32801ae08745Sheppo 		    ldcp->id);
32811ae08745Sheppo 		mutex_exit(&ldcp->lock);
32821ae08745Sheppo 		return (EWOULDBLOCK);
32831ae08745Sheppo 	}
32841ae08745Sheppo 
32851ae08745Sheppo 	ldcp->cb = NULL;
32861ae08745Sheppo 	ldcp->cb_arg = NULL;
32871ae08745Sheppo 	ldcp->cb_enabled = B_FALSE;
32881ae08745Sheppo 
32891ae08745Sheppo 	D1(ldcp->id,
32901ae08745Sheppo 	    "ldc_unreg_callback: (0x%llx) unregistered callback for channel\n",
32911ae08745Sheppo 	    ldcp->id);
32921ae08745Sheppo 
32931ae08745Sheppo 	mutex_exit(&ldcp->lock);
32941ae08745Sheppo 
32951ae08745Sheppo 	return (0);
32961ae08745Sheppo }
32971ae08745Sheppo 
32981ae08745Sheppo 
32991ae08745Sheppo /*
33001ae08745Sheppo  * Bring a channel up by initiating a handshake with the peer
33011ae08745Sheppo  * This call is asynchronous. It will complete at a later point
33021ae08745Sheppo  * in time when the peer responds back with an RTR.
33031ae08745Sheppo  */
33041ae08745Sheppo int
33051ae08745Sheppo ldc_up(ldc_handle_t handle)
33061ae08745Sheppo {
33071ae08745Sheppo 	int 		rv;
33081ae08745Sheppo 	ldc_chan_t 	*ldcp;
33091ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
331057e6a936Ssb155480 	uint64_t 	tx_tail, tstate, link_state;
33111ae08745Sheppo 
33121ae08745Sheppo 	if (handle == NULL) {
33131ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_up: invalid channel handle\n");
33141ae08745Sheppo 		return (EINVAL);
33151ae08745Sheppo 	}
33161ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
33171ae08745Sheppo 
33181ae08745Sheppo 	mutex_enter(&ldcp->lock);
33191ae08745Sheppo 
33203af08d82Slm66018 	D1(ldcp->id, "ldc_up: (0x%llx) doing channel UP\n", ldcp->id);
33213af08d82Slm66018 
33223af08d82Slm66018 	/* clear the reset state */
33233af08d82Slm66018 	tstate = ldcp->tstate;
33243af08d82Slm66018 	ldcp->tstate &= ~TS_IN_RESET;
33253af08d82Slm66018 
33261ae08745Sheppo 	if (ldcp->tstate == TS_UP) {
33273af08d82Slm66018 		DWARN(ldcp->id,
33281ae08745Sheppo 		    "ldc_up: (0x%llx) channel is already in UP state\n",
33291ae08745Sheppo 		    ldcp->id);
33303af08d82Slm66018 
33313af08d82Slm66018 		/* mark channel as up */
33323af08d82Slm66018 		ldcp->status = LDC_UP;
33333af08d82Slm66018 
33343af08d82Slm66018 		/*
33353af08d82Slm66018 		 * if channel was in reset state and there was
33363af08d82Slm66018 		 * pending data clear interrupt state. this will
33373af08d82Slm66018 		 * trigger an interrupt, causing the RX handler to
33383af08d82Slm66018 		 * to invoke the client's callback
33393af08d82Slm66018 		 */
33403af08d82Slm66018 		if ((tstate & TS_IN_RESET) &&
33413af08d82Slm66018 		    ldcp->rx_intr_state == LDC_INTR_PEND) {
3342cb112a14Slm66018 			D1(ldcp->id,
33433af08d82Slm66018 			    "ldc_up: (0x%llx) channel has pending data, "
33443af08d82Slm66018 			    "clearing interrupt\n", ldcp->id);
33453af08d82Slm66018 			i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
33463af08d82Slm66018 		}
33473af08d82Slm66018 
33481ae08745Sheppo 		mutex_exit(&ldcp->lock);
33491ae08745Sheppo 		return (0);
33501ae08745Sheppo 	}
33511ae08745Sheppo 
33521ae08745Sheppo 	/* if the channel is in RAW mode - mark it as UP, if READY */
33531ae08745Sheppo 	if (ldcp->mode == LDC_MODE_RAW && ldcp->tstate >= TS_READY) {
33541ae08745Sheppo 		ldcp->tstate = TS_UP;
33551ae08745Sheppo 		mutex_exit(&ldcp->lock);
33561ae08745Sheppo 		return (0);
33571ae08745Sheppo 	}
33581ae08745Sheppo 
33591ae08745Sheppo 	/* Don't start another handshake if there is one in progress */
33601ae08745Sheppo 	if (ldcp->hstate) {
33613af08d82Slm66018 		D1(ldcp->id,
33621ae08745Sheppo 		    "ldc_up: (0x%llx) channel handshake in progress\n",
33631ae08745Sheppo 		    ldcp->id);
33641ae08745Sheppo 		mutex_exit(&ldcp->lock);
33651ae08745Sheppo 		return (0);
33661ae08745Sheppo 	}
33671ae08745Sheppo 
3368d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
3369d10e4ef2Snarayan 
337057e6a936Ssb155480 	/* save current link state */
337157e6a936Ssb155480 	link_state = ldcp->link_state;
337257e6a936Ssb155480 
33731ae08745Sheppo 	/* get the current tail for the LDC msg */
33741ae08745Sheppo 	rv = i_ldc_get_tx_tail(ldcp, &tx_tail);
33751ae08745Sheppo 	if (rv) {
3376cb112a14Slm66018 		D1(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake\n",
33771ae08745Sheppo 		    ldcp->id);
3378d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
33791ae08745Sheppo 		mutex_exit(&ldcp->lock);
33801ae08745Sheppo 		return (ECONNREFUSED);
33811ae08745Sheppo 	}
33821ae08745Sheppo 
338357e6a936Ssb155480 	/*
338457e6a936Ssb155480 	 * If i_ldc_get_tx_tail() changed link_state to either RESET or UP,
338557e6a936Ssb155480 	 * from a previous state of DOWN, then mark the channel as
338657e6a936Ssb155480 	 * being ready for handshake.
338757e6a936Ssb155480 	 */
338857e6a936Ssb155480 	if ((link_state == LDC_CHANNEL_DOWN) &&
338957e6a936Ssb155480 	    (link_state != ldcp->link_state)) {
339057e6a936Ssb155480 
339157e6a936Ssb155480 		ASSERT((ldcp->link_state == LDC_CHANNEL_RESET) ||
339257e6a936Ssb155480 		    (ldcp->link_state == LDC_CHANNEL_UP));
339357e6a936Ssb155480 
339457e6a936Ssb155480 		if (ldcp->mode == LDC_MODE_RAW) {
339557e6a936Ssb155480 			ldcp->status = LDC_UP;
339657e6a936Ssb155480 			ldcp->tstate = TS_UP;
339757e6a936Ssb155480 			mutex_exit(&ldcp->tx_lock);
339857e6a936Ssb155480 			mutex_exit(&ldcp->lock);
339957e6a936Ssb155480 			return (0);
340057e6a936Ssb155480 		} else {
340157e6a936Ssb155480 			ldcp->status = LDC_READY;
340257e6a936Ssb155480 			ldcp->tstate |= TS_LINK_READY;
340357e6a936Ssb155480 		}
340457e6a936Ssb155480 
340557e6a936Ssb155480 	}
340657e6a936Ssb155480 
34071ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
34081ae08745Sheppo 	ZERO_PKT(ldcmsg);
34091ae08745Sheppo 
34101ae08745Sheppo 	ldcmsg->type = LDC_CTRL;
34111ae08745Sheppo 	ldcmsg->stype = LDC_INFO;
34121ae08745Sheppo 	ldcmsg->ctrl = LDC_VER;
34131ae08745Sheppo 	ldcp->next_vidx = 0;
34141ae08745Sheppo 	bcopy(&ldc_versions[0], ldcmsg->udata, sizeof (ldc_versions[0]));
34151ae08745Sheppo 
34161ae08745Sheppo 	DUMP_LDC_PKT(ldcp, "ldc_up snd ver", (uint64_t)ldcmsg);
34171ae08745Sheppo 
34181ae08745Sheppo 	/* initiate the send by calling into HV and set the new tail */
34191ae08745Sheppo 	tx_tail = (tx_tail + LDC_PACKET_SIZE) %
34201ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
34211ae08745Sheppo 
34221ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
34231ae08745Sheppo 	if (rv) {
34241ae08745Sheppo 		DWARN(ldcp->id,
34251ae08745Sheppo 		    "ldc_up: (0x%llx) cannot initiate handshake rv=%d\n",
34261ae08745Sheppo 		    ldcp->id, rv);
3427d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
34281ae08745Sheppo 		mutex_exit(&ldcp->lock);
34291ae08745Sheppo 		return (rv);
34301ae08745Sheppo 	}
34311ae08745Sheppo 
34320a55fbb7Slm66018 	ldcp->hstate |= TS_SENT_VER;
34331ae08745Sheppo 	ldcp->tx_tail = tx_tail;
34341ae08745Sheppo 	D1(ldcp->id, "ldc_up: (0x%llx) channel up initiated\n", ldcp->id);
34351ae08745Sheppo 
3436d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
34371ae08745Sheppo 	mutex_exit(&ldcp->lock);
34381ae08745Sheppo 
34391ae08745Sheppo 	return (rv);
34401ae08745Sheppo }
34411ae08745Sheppo 
34421ae08745Sheppo 
34431ae08745Sheppo /*
3444e1ebb9ecSlm66018  * Bring a channel down by resetting its state and queues
34451ae08745Sheppo  */
34461ae08745Sheppo int
3447e1ebb9ecSlm66018 ldc_down(ldc_handle_t handle)
34481ae08745Sheppo {
34491ae08745Sheppo 	ldc_chan_t 	*ldcp;
34501ae08745Sheppo 
34511ae08745Sheppo 	if (handle == NULL) {
3452e1ebb9ecSlm66018 		DWARN(DBG_ALL_LDCS, "ldc_down: invalid channel handle\n");
34531ae08745Sheppo 		return (EINVAL);
34541ae08745Sheppo 	}
34551ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
34561ae08745Sheppo 	mutex_enter(&ldcp->lock);
3457d10e4ef2Snarayan 	mutex_enter(&ldcp->tx_lock);
34583af08d82Slm66018 	i_ldc_reset(ldcp, B_TRUE);
3459d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
34601ae08745Sheppo 	mutex_exit(&ldcp->lock);
34611ae08745Sheppo 
34621ae08745Sheppo 	return (0);
34631ae08745Sheppo }
34641ae08745Sheppo 
34651ae08745Sheppo /*
34661ae08745Sheppo  * Get the current channel status
34671ae08745Sheppo  */
34681ae08745Sheppo int
34691ae08745Sheppo ldc_status(ldc_handle_t handle, ldc_status_t *status)
34701ae08745Sheppo {
34711ae08745Sheppo 	ldc_chan_t *ldcp;
34721ae08745Sheppo 
34731ae08745Sheppo 	if (handle == NULL || status == NULL) {
34741ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_status: invalid argument\n");
34751ae08745Sheppo 		return (EINVAL);
34761ae08745Sheppo 	}
34771ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
34781ae08745Sheppo 
34791ae08745Sheppo 	*status = ((ldc_chan_t *)handle)->status;
34801ae08745Sheppo 
3481cb112a14Slm66018 	D1(ldcp->id,
34821ae08745Sheppo 	    "ldc_status: (0x%llx) returned status %d\n", ldcp->id, *status);
34831ae08745Sheppo 	return (0);
34841ae08745Sheppo }
34851ae08745Sheppo 
34861ae08745Sheppo 
34871ae08745Sheppo /*
34881ae08745Sheppo  * Set the channel's callback mode - enable/disable callbacks
34891ae08745Sheppo  */
34901ae08745Sheppo int
34911ae08745Sheppo ldc_set_cb_mode(ldc_handle_t handle, ldc_cb_mode_t cmode)
34921ae08745Sheppo {
34931ae08745Sheppo 	ldc_chan_t 	*ldcp;
34941ae08745Sheppo 
34951ae08745Sheppo 	if (handle == NULL) {
34961ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
34971ae08745Sheppo 		    "ldc_set_intr_mode: invalid channel handle\n");
34981ae08745Sheppo 		return (EINVAL);
34991ae08745Sheppo 	}
35001ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
35011ae08745Sheppo 
35021ae08745Sheppo 	/*
35031ae08745Sheppo 	 * Record no callbacks should be invoked
35041ae08745Sheppo 	 */
35051ae08745Sheppo 	mutex_enter(&ldcp->lock);
35061ae08745Sheppo 
35071ae08745Sheppo 	switch (cmode) {
35081ae08745Sheppo 	case LDC_CB_DISABLE:
35091ae08745Sheppo 		if (!ldcp->cb_enabled) {
35101ae08745Sheppo 			DWARN(ldcp->id,
35111ae08745Sheppo 			    "ldc_set_cb_mode: (0x%llx) callbacks disabled\n",
35121ae08745Sheppo 			    ldcp->id);
35131ae08745Sheppo 			break;
35141ae08745Sheppo 		}
35151ae08745Sheppo 		ldcp->cb_enabled = B_FALSE;
35161ae08745Sheppo 
35171ae08745Sheppo 		D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) disabled callbacks\n",
35181ae08745Sheppo 		    ldcp->id);
35191ae08745Sheppo 		break;
35201ae08745Sheppo 
35211ae08745Sheppo 	case LDC_CB_ENABLE:
35221ae08745Sheppo 		if (ldcp->cb_enabled) {
35231ae08745Sheppo 			DWARN(ldcp->id,
35241ae08745Sheppo 			    "ldc_set_cb_mode: (0x%llx) callbacks enabled\n",
35251ae08745Sheppo 			    ldcp->id);
35261ae08745Sheppo 			break;
35271ae08745Sheppo 		}
35281ae08745Sheppo 		ldcp->cb_enabled = B_TRUE;
35291ae08745Sheppo 
35301ae08745Sheppo 		D1(ldcp->id, "ldc_set_cb_mode: (0x%llx) enabled callbacks\n",
35311ae08745Sheppo 		    ldcp->id);
35321ae08745Sheppo 		break;
35331ae08745Sheppo 	}
35341ae08745Sheppo 
35351ae08745Sheppo 	mutex_exit(&ldcp->lock);
35361ae08745Sheppo 
35371ae08745Sheppo 	return (0);
35381ae08745Sheppo }
35391ae08745Sheppo 
35401ae08745Sheppo /*
35411ae08745Sheppo  * Check to see if there are packets on the incoming queue
3542e1ebb9ecSlm66018  * Will return hasdata = B_FALSE if there are no packets
35431ae08745Sheppo  */
35441ae08745Sheppo int
3545e1ebb9ecSlm66018 ldc_chkq(ldc_handle_t handle, boolean_t *hasdata)
35461ae08745Sheppo {
35471ae08745Sheppo 	int 		rv;
35481ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
35491ae08745Sheppo 	ldc_chan_t 	*ldcp;
35501ae08745Sheppo 
35511ae08745Sheppo 	if (handle == NULL) {
35521ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_chkq: invalid channel handle\n");
35531ae08745Sheppo 		return (EINVAL);
35541ae08745Sheppo 	}
35551ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
35561ae08745Sheppo 
3557e1ebb9ecSlm66018 	*hasdata = B_FALSE;
35581ae08745Sheppo 
35591ae08745Sheppo 	mutex_enter(&ldcp->lock);
35601ae08745Sheppo 
35611ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
35621ae08745Sheppo 		D1(ldcp->id,
35631ae08745Sheppo 		    "ldc_chkq: (0x%llx) channel is not up\n", ldcp->id);
35641ae08745Sheppo 		mutex_exit(&ldcp->lock);
35651ae08745Sheppo 		return (ECONNRESET);
35661ae08745Sheppo 	}
35671ae08745Sheppo 
35681ae08745Sheppo 	/* Read packet(s) from the queue */
35691ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
35701ae08745Sheppo 	    &ldcp->link_state);
35711ae08745Sheppo 	if (rv != 0) {
35721ae08745Sheppo 		cmn_err(CE_WARN,
35731ae08745Sheppo 		    "ldc_chkq: (0x%lx) unable to read queue ptrs", ldcp->id);
35741ae08745Sheppo 		mutex_exit(&ldcp->lock);
35751ae08745Sheppo 		return (EIO);
35761ae08745Sheppo 	}
3577*58283286Sha137994 
35781ae08745Sheppo 	/* reset the channel state if the channel went down */
35791ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
35801ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
3581d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
35823af08d82Slm66018 		i_ldc_reset(ldcp, B_FALSE);
3583d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
35841ae08745Sheppo 		mutex_exit(&ldcp->lock);
35851ae08745Sheppo 		return (ECONNRESET);
35861ae08745Sheppo 	}
35871ae08745Sheppo 
3588*58283286Sha137994 	switch (ldcp->mode) {
3589*58283286Sha137994 	case LDC_MODE_RAW:
3590*58283286Sha137994 		/*
3591*58283286Sha137994 		 * In raw mode, there are no ctrl packets, so checking
3592*58283286Sha137994 		 * if the queue is non-empty is sufficient.
3593*58283286Sha137994 		 */
3594*58283286Sha137994 		*hasdata = (rx_head != rx_tail);
3595*58283286Sha137994 		break;
3596*58283286Sha137994 
3597*58283286Sha137994 	case LDC_MODE_UNRELIABLE:
3598*58283286Sha137994 		/*
3599*58283286Sha137994 		 * In unreliable mode, if the queue is non-empty, we need
3600*58283286Sha137994 		 * to check if it actually contains unread data packets.
3601*58283286Sha137994 		 * The queue may just contain ctrl packets.
3602*58283286Sha137994 		 */
3603*58283286Sha137994 		if (rx_head != rx_tail)
3604*58283286Sha137994 			*hasdata = (i_ldc_chkq(ldcp) == 0);
3605*58283286Sha137994 		break;
3606*58283286Sha137994 
3607*58283286Sha137994 	case LDC_MODE_STREAM:
3608*58283286Sha137994 		/*
3609*58283286Sha137994 		 * In stream mode, first check for 'stream_remains' > 0.
3610*58283286Sha137994 		 * Otherwise, if the data queue head and tail pointers
3611*58283286Sha137994 		 * differ, there must be data to read.
3612*58283286Sha137994 		 */
3613*58283286Sha137994 		if (ldcp->stream_remains > 0)
3614e1ebb9ecSlm66018 			*hasdata = B_TRUE;
3615*58283286Sha137994 		else
3616*58283286Sha137994 			*hasdata = (ldcp->rx_dq_head != ldcp->rx_dq_tail);
3617*58283286Sha137994 		break;
3618*58283286Sha137994 
3619*58283286Sha137994 	default:
3620*58283286Sha137994 		cmn_err(CE_WARN, "ldc_chkq: (0x%lx) unexpected channel mode "
3621*58283286Sha137994 		    "(0x%x)", ldcp->id, ldcp->mode);
3622*58283286Sha137994 		mutex_exit(&ldcp->lock);
3623*58283286Sha137994 		return (EIO);
36241ae08745Sheppo 	}
36251ae08745Sheppo 
36261ae08745Sheppo 	mutex_exit(&ldcp->lock);
36271ae08745Sheppo 
36281ae08745Sheppo 	return (0);
36291ae08745Sheppo }
36301ae08745Sheppo 
36311ae08745Sheppo 
36321ae08745Sheppo /*
36331ae08745Sheppo  * Read 'size' amount of bytes or less. If incoming buffer
36341ae08745Sheppo  * is more than 'size', ENOBUFS is returned.
36351ae08745Sheppo  *
36361ae08745Sheppo  * On return, size contains the number of bytes read.
36371ae08745Sheppo  */
36381ae08745Sheppo int
36391ae08745Sheppo ldc_read(ldc_handle_t handle, caddr_t bufp, size_t *sizep)
36401ae08745Sheppo {
36411ae08745Sheppo 	ldc_chan_t 	*ldcp;
36421ae08745Sheppo 	uint64_t 	rx_head = 0, rx_tail = 0;
36431ae08745Sheppo 	int		rv = 0, exit_val;
36441ae08745Sheppo 
36451ae08745Sheppo 	if (handle == NULL) {
36461ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_read: invalid channel handle\n");
36471ae08745Sheppo 		return (EINVAL);
36481ae08745Sheppo 	}
36491ae08745Sheppo 
36501ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
36511ae08745Sheppo 
36521ae08745Sheppo 	/* channel lock */
36531ae08745Sheppo 	mutex_enter(&ldcp->lock);
36541ae08745Sheppo 
36551ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
36561ae08745Sheppo 		DWARN(ldcp->id,
36571ae08745Sheppo 		    "ldc_read: (0x%llx) channel is not in UP state\n",
36581ae08745Sheppo 		    ldcp->id);
36591ae08745Sheppo 		exit_val = ECONNRESET;
3660*58283286Sha137994 	} else if (ldcp->mode == LDC_MODE_STREAM) {
3661*58283286Sha137994 		TRACE_RXDQ_LENGTH(ldcp);
3662*58283286Sha137994 		exit_val = ldcp->read_p(ldcp, bufp, sizep);
3663*58283286Sha137994 		mutex_exit(&ldcp->lock);
3664*58283286Sha137994 		return (exit_val);
36651ae08745Sheppo 	} else {
36661ae08745Sheppo 		exit_val = ldcp->read_p(ldcp, bufp, sizep);
36671ae08745Sheppo 	}
36681ae08745Sheppo 
36691ae08745Sheppo 	/*
36701ae08745Sheppo 	 * if queue has been drained - clear interrupt
36711ae08745Sheppo 	 */
36721ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
36731ae08745Sheppo 	    &ldcp->link_state);
3674cb112a14Slm66018 	if (rv != 0) {
3675cb112a14Slm66018 		cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs",
3676cb112a14Slm66018 		    ldcp->id);
3677cb112a14Slm66018 		mutex_enter(&ldcp->tx_lock);
3678cb112a14Slm66018 		i_ldc_reset(ldcp, B_TRUE);
3679cb112a14Slm66018 		mutex_exit(&ldcp->tx_lock);
3680bd8f0338Snarayan 		mutex_exit(&ldcp->lock);
3681cb112a14Slm66018 		return (ECONNRESET);
3682cb112a14Slm66018 	}
36833af08d82Slm66018 
36843af08d82Slm66018 	if (exit_val == 0) {
36853af08d82Slm66018 		if (ldcp->link_state == LDC_CHANNEL_DOWN ||
36863af08d82Slm66018 		    ldcp->link_state == LDC_CHANNEL_RESET) {
36873af08d82Slm66018 			mutex_enter(&ldcp->tx_lock);
36883af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
36893af08d82Slm66018 			exit_val = ECONNRESET;
36903af08d82Slm66018 			mutex_exit(&ldcp->tx_lock);
36913af08d82Slm66018 		}
36923af08d82Slm66018 		if ((rv == 0) &&
36933af08d82Slm66018 		    (ldcp->rx_intr_state == LDC_INTR_PEND) &&
36943af08d82Slm66018 		    (rx_head == rx_tail)) {
36951ae08745Sheppo 			i_ldc_clear_intr(ldcp, CNEX_RX_INTR);
36961ae08745Sheppo 		}
36973af08d82Slm66018 	}
36981ae08745Sheppo 
36991ae08745Sheppo 	mutex_exit(&ldcp->lock);
37001ae08745Sheppo 	return (exit_val);
37011ae08745Sheppo }
37021ae08745Sheppo 
37031ae08745Sheppo /*
37041ae08745Sheppo  * Basic raw mondo read -
37051ae08745Sheppo  * no interpretation of mondo contents at all.
37061ae08745Sheppo  *
37071ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
37081ae08745Sheppo  */
37091ae08745Sheppo static int
37101ae08745Sheppo i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
37111ae08745Sheppo {
37121ae08745Sheppo 	uint64_t 	q_size_mask;
37131ae08745Sheppo 	ldc_msg_t 	*msgp;
37141ae08745Sheppo 	uint8_t		*msgbufp;
37151ae08745Sheppo 	int		rv = 0, space;
37161ae08745Sheppo 	uint64_t 	rx_head, rx_tail;
37171ae08745Sheppo 
37181ae08745Sheppo 	space = *sizep;
37191ae08745Sheppo 
37201ae08745Sheppo 	if (space < LDC_PAYLOAD_SIZE_RAW)
37211ae08745Sheppo 		return (ENOBUFS);
37221ae08745Sheppo 
37231ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
37241ae08745Sheppo 
37251ae08745Sheppo 	/* compute mask for increment */
37261ae08745Sheppo 	q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
37271ae08745Sheppo 
37281ae08745Sheppo 	/*
37291ae08745Sheppo 	 * Read packet(s) from the queue
37301ae08745Sheppo 	 */
37311ae08745Sheppo 	rv = hv_ldc_rx_get_state(ldcp->id, &rx_head, &rx_tail,
37321ae08745Sheppo 	    &ldcp->link_state);
37331ae08745Sheppo 	if (rv != 0) {
37341ae08745Sheppo 		cmn_err(CE_WARN,
37351ae08745Sheppo 		    "ldc_read_raw: (0x%lx) unable to read queue ptrs",
37361ae08745Sheppo 		    ldcp->id);
37371ae08745Sheppo 		return (EIO);
37381ae08745Sheppo 	}
37391ae08745Sheppo 	D1(ldcp->id, "ldc_read_raw: (0x%llx) rxh=0x%llx,"
37401ae08745Sheppo 	    " rxt=0x%llx, st=0x%llx\n",
37411ae08745Sheppo 	    ldcp->id, rx_head, rx_tail, ldcp->link_state);
37421ae08745Sheppo 
37431ae08745Sheppo 	/* reset the channel state if the channel went down */
37443af08d82Slm66018 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
37453af08d82Slm66018 	    ldcp->link_state == LDC_CHANNEL_RESET) {
3746d10e4ef2Snarayan 		mutex_enter(&ldcp->tx_lock);
37473af08d82Slm66018 		i_ldc_reset(ldcp, B_FALSE);
3748d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
37491ae08745Sheppo 		return (ECONNRESET);
37501ae08745Sheppo 	}
37511ae08745Sheppo 
37521ae08745Sheppo 	/*
37531ae08745Sheppo 	 * Check for empty queue
37541ae08745Sheppo 	 */
37551ae08745Sheppo 	if (rx_head == rx_tail) {
37561ae08745Sheppo 		*sizep = 0;
37571ae08745Sheppo 		return (0);
37581ae08745Sheppo 	}
37591ae08745Sheppo 
37601ae08745Sheppo 	/* get the message */
37611ae08745Sheppo 	msgp = (ldc_msg_t *)(ldcp->rx_q_va + rx_head);
37621ae08745Sheppo 
37631ae08745Sheppo 	/* if channel is in RAW mode, copy data and return */
37641ae08745Sheppo 	msgbufp = (uint8_t *)&(msgp->raw[0]);
37651ae08745Sheppo 
37661ae08745Sheppo 	bcopy(msgbufp, target_bufp, LDC_PAYLOAD_SIZE_RAW);
37671ae08745Sheppo 
37681ae08745Sheppo 	DUMP_PAYLOAD(ldcp->id, msgbufp);
37691ae08745Sheppo 
37701ae08745Sheppo 	*sizep = LDC_PAYLOAD_SIZE_RAW;
37711ae08745Sheppo 
37721ae08745Sheppo 	rx_head = (rx_head + LDC_PACKET_SIZE) & q_size_mask;
37730a55fbb7Slm66018 	rv = i_ldc_set_rx_head(ldcp, rx_head);
37741ae08745Sheppo 
37751ae08745Sheppo 	return (rv);
37761ae08745Sheppo }
37771ae08745Sheppo 
37781ae08745Sheppo /*
37791ae08745Sheppo  * Process LDC mondos to build larger packets
37801ae08745Sheppo  * with either un-reliable or reliable delivery.
37811ae08745Sheppo  *
37821ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
37831ae08745Sheppo  */
37841ae08745Sheppo static int
37851ae08745Sheppo i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
37861ae08745Sheppo {
37871ae08745Sheppo 	int		rv = 0;
37881ae08745Sheppo 	uint64_t 	rx_head = 0, rx_tail = 0;
37891ae08745Sheppo 	uint64_t 	curr_head = 0;
37901ae08745Sheppo 	ldc_msg_t 	*msg;
37911ae08745Sheppo 	caddr_t 	target;
37921ae08745Sheppo 	size_t 		len = 0, bytes_read = 0;
37930a55fbb7Slm66018 	int 		retries = 0;
3794*58283286Sha137994 	uint64_t 	q_va, q_size_mask;
3795d10e4ef2Snarayan 	uint64_t	first_fragment = 0;
37961ae08745Sheppo 
37971ae08745Sheppo 	target = target_bufp;
37981ae08745Sheppo 
37991ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
38001ae08745Sheppo 
38013af08d82Slm66018 	/* check if the buffer and size are valid */
38023af08d82Slm66018 	if (target_bufp == NULL || *sizep == 0) {
38033af08d82Slm66018 		DWARN(ldcp->id, "ldc_read: (0x%llx) invalid buffer/size\n",
38043af08d82Slm66018 		    ldcp->id);
38053af08d82Slm66018 		return (EINVAL);
38063af08d82Slm66018 	}
38073af08d82Slm66018 
3808*58283286Sha137994 	/* Set q_va and compute increment mask for the appropriate queue */
3809*58283286Sha137994 	if (ldcp->mode == LDC_MODE_STREAM) {
3810*58283286Sha137994 		q_va	    = ldcp->rx_dq_va;
3811*58283286Sha137994 		q_size_mask = (ldcp->rx_dq_entries-1)<<LDC_PACKET_SHIFT;
3812*58283286Sha137994 	} else {
3813*58283286Sha137994 		q_va	    = ldcp->rx_q_va;
38141ae08745Sheppo 		q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT;
3815*58283286Sha137994 	}
38161ae08745Sheppo 
38171ae08745Sheppo 	/*
38181ae08745Sheppo 	 * Read packet(s) from the queue
38191ae08745Sheppo 	 */
3820*58283286Sha137994 	rv = ldcp->readq_get_state(ldcp, &curr_head, &rx_tail,
38211ae08745Sheppo 	    &ldcp->link_state);
38221ae08745Sheppo 	if (rv != 0) {
38233af08d82Slm66018 		cmn_err(CE_WARN, "ldc_read: (0x%lx) unable to read queue ptrs",
38241ae08745Sheppo 		    ldcp->id);
38253af08d82Slm66018 		mutex_enter(&ldcp->tx_lock);
38263af08d82Slm66018 		i_ldc_reset(ldcp, B_TRUE);
38273af08d82Slm66018 		mutex_exit(&ldcp->tx_lock);
38283af08d82Slm66018 		return (ECONNRESET);
38291ae08745Sheppo 	}
38301ae08745Sheppo 	D1(ldcp->id, "ldc_read: (0x%llx) chd=0x%llx, tl=0x%llx, st=0x%llx\n",
38311ae08745Sheppo 	    ldcp->id, curr_head, rx_tail, ldcp->link_state);
38321ae08745Sheppo 
38331ae08745Sheppo 	/* reset the channel state if the channel went down */
38343af08d82Slm66018 	if (ldcp->link_state != LDC_CHANNEL_UP)
38353af08d82Slm66018 		goto channel_is_reset;
38361ae08745Sheppo 
38371ae08745Sheppo 	for (;;) {
38381ae08745Sheppo 
38391ae08745Sheppo 		if (curr_head == rx_tail) {
3840*58283286Sha137994 			/*
3841*58283286Sha137994 			 * If a data queue is being used, check the Rx HV
3842*58283286Sha137994 			 * queue. This will copy over any new data packets
3843*58283286Sha137994 			 * that have arrived.
3844*58283286Sha137994 			 */
3845*58283286Sha137994 			if (ldcp->mode == LDC_MODE_STREAM)
3846*58283286Sha137994 				(void) i_ldc_chkq(ldcp);
3847*58283286Sha137994 
3848*58283286Sha137994 			rv = ldcp->readq_get_state(ldcp,
38491ae08745Sheppo 			    &rx_head, &rx_tail, &ldcp->link_state);
38501ae08745Sheppo 			if (rv != 0) {
38511ae08745Sheppo 				cmn_err(CE_WARN,
38521ae08745Sheppo 				    "ldc_read: (0x%lx) cannot read queue ptrs",
38531ae08745Sheppo 				    ldcp->id);
3854d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
38553af08d82Slm66018 				i_ldc_reset(ldcp, B_TRUE);
3856d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
38571ae08745Sheppo 				return (ECONNRESET);
38581ae08745Sheppo 			}
3859*58283286Sha137994 
38603af08d82Slm66018 			if (ldcp->link_state != LDC_CHANNEL_UP)
38613af08d82Slm66018 				goto channel_is_reset;
38621ae08745Sheppo 
38631ae08745Sheppo 			if (curr_head == rx_tail) {
38641ae08745Sheppo 
38651ae08745Sheppo 				/* If in the middle of a fragmented xfer */
3866d10e4ef2Snarayan 				if (first_fragment != 0) {
38670a55fbb7Slm66018 
38680a55fbb7Slm66018 					/* wait for ldc_delay usecs */
38690a55fbb7Slm66018 					drv_usecwait(ldc_delay);
38700a55fbb7Slm66018 
38710a55fbb7Slm66018 					if (++retries < ldc_max_retries)
38721ae08745Sheppo 						continue;
38730a55fbb7Slm66018 
38741ae08745Sheppo 					*sizep = 0;
3875*58283286Sha137994 					if (ldcp->mode != LDC_MODE_STREAM)
3876*58283286Sha137994 						ldcp->last_msg_rcd =
3877*58283286Sha137994 						    first_fragment - 1;
38783af08d82Slm66018 					DWARN(DBG_ALL_LDCS, "ldc_read: "
387922f747efSnarayan 					    "(0x%llx) read timeout", ldcp->id);
38803af08d82Slm66018 					return (EAGAIN);
38811ae08745Sheppo 				}
38821ae08745Sheppo 				*sizep = 0;
38831ae08745Sheppo 				break;
38841ae08745Sheppo 			}
38853af08d82Slm66018 		}
38860a55fbb7Slm66018 		retries = 0;
38871ae08745Sheppo 
38881ae08745Sheppo 		D2(ldcp->id,
38891ae08745Sheppo 		    "ldc_read: (0x%llx) chd=0x%llx, rxhd=0x%llx, rxtl=0x%llx\n",
38901ae08745Sheppo 		    ldcp->id, curr_head, rx_head, rx_tail);
38911ae08745Sheppo 
38921ae08745Sheppo 		/* get the message */
3893*58283286Sha137994 		msg = (ldc_msg_t *)(q_va + curr_head);
38941ae08745Sheppo 
38951ae08745Sheppo 		DUMP_LDC_PKT(ldcp, "ldc_read received pkt",
38961ae08745Sheppo 		    ldcp->rx_q_va + curr_head);
38971ae08745Sheppo 
38981ae08745Sheppo 		/* Check the message ID for the message received */
3899*58283286Sha137994 		if (ldcp->mode != LDC_MODE_STREAM) {
39001ae08745Sheppo 			if ((rv = i_ldc_check_seqid(ldcp, msg)) != 0) {
39011ae08745Sheppo 
3902*58283286Sha137994 				DWARN(ldcp->id, "ldc_read: (0x%llx) seqid "
3903*58283286Sha137994 				    "error, q_ptrs=0x%lx,0x%lx",
3904*58283286Sha137994 				    ldcp->id, rx_head, rx_tail);
39051ae08745Sheppo 
39060a55fbb7Slm66018 				/* throw away data */
39070a55fbb7Slm66018 				bytes_read = 0;
39080a55fbb7Slm66018 
39091ae08745Sheppo 				/* Reset last_msg_rcd to start of message */
3910d10e4ef2Snarayan 				if (first_fragment != 0) {
3911d10e4ef2Snarayan 					ldcp->last_msg_rcd = first_fragment - 1;
3912d10e4ef2Snarayan 					first_fragment = 0;
39131ae08745Sheppo 				}
39141ae08745Sheppo 				/*
39151ae08745Sheppo 				 * Send a NACK -- invalid seqid
39161ae08745Sheppo 				 * get the current tail for the response
39171ae08745Sheppo 				 */
39181ae08745Sheppo 				rv = i_ldc_send_pkt(ldcp, msg->type, LDC_NACK,
39191ae08745Sheppo 				    (msg->ctrl & LDC_CTRL_MASK));
39201ae08745Sheppo 				if (rv) {
39211ae08745Sheppo 					cmn_err(CE_NOTE,
39221ae08745Sheppo 					    "ldc_read: (0x%lx) err sending "
39231ae08745Sheppo 					    "NACK msg\n", ldcp->id);
3924d10e4ef2Snarayan 
3925*58283286Sha137994 					/* if cannot send NACK - reset chan */
3926d10e4ef2Snarayan 					mutex_enter(&ldcp->tx_lock);
39273af08d82Slm66018 					i_ldc_reset(ldcp, B_FALSE);
3928d10e4ef2Snarayan 					mutex_exit(&ldcp->tx_lock);
3929d10e4ef2Snarayan 					rv = ECONNRESET;
3930d10e4ef2Snarayan 					break;
39311ae08745Sheppo 				}
39321ae08745Sheppo 
39331ae08745Sheppo 				/* purge receive queue */
39340a55fbb7Slm66018 				rv = i_ldc_set_rx_head(ldcp, rx_tail);
39351ae08745Sheppo 
39361ae08745Sheppo 				break;
39371ae08745Sheppo 			}
39381ae08745Sheppo 
39391ae08745Sheppo 			/*
39401ae08745Sheppo 			 * Process any messages of type CTRL messages
3941e1ebb9ecSlm66018 			 * Future implementations should try to pass these
3942e1ebb9ecSlm66018 			 * to LDC link by resetting the intr state.
39431ae08745Sheppo 			 *
3944*58283286Sha137994 			 * NOTE: not done as a switch() as type can be
3945*58283286Sha137994 			 * both ctrl+data
39461ae08745Sheppo 			 */
39471ae08745Sheppo 			if (msg->type & LDC_CTRL) {
39481ae08745Sheppo 				if (rv = i_ldc_ctrlmsg(ldcp, msg)) {
39491ae08745Sheppo 					if (rv == EAGAIN)
39501ae08745Sheppo 						continue;
39510a55fbb7Slm66018 					rv = i_ldc_set_rx_head(ldcp, rx_tail);
39521ae08745Sheppo 					*sizep = 0;
39531ae08745Sheppo 					bytes_read = 0;
39541ae08745Sheppo 					break;
39551ae08745Sheppo 				}
39561ae08745Sheppo 			}
39571ae08745Sheppo 
39581ae08745Sheppo 			/* process data ACKs */
39591ae08745Sheppo 			if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) {
3960d10e4ef2Snarayan 				if (rv = i_ldc_process_data_ACK(ldcp, msg)) {
3961d10e4ef2Snarayan 					*sizep = 0;
3962d10e4ef2Snarayan 					bytes_read = 0;
3963d10e4ef2Snarayan 					break;
3964d10e4ef2Snarayan 				}
39651ae08745Sheppo 			}
39661ae08745Sheppo 
396783d3bc6fSnarayan 			/* process data NACKs */
396883d3bc6fSnarayan 			if ((msg->type & LDC_DATA) && (msg->stype & LDC_NACK)) {
396983d3bc6fSnarayan 				DWARN(ldcp->id,
3970*58283286Sha137994 				    "ldc_read: (0x%llx) received DATA/NACK",
3971*58283286Sha137994 				    ldcp->id);
397283d3bc6fSnarayan 				mutex_enter(&ldcp->tx_lock);
397383d3bc6fSnarayan 				i_ldc_reset(ldcp, B_TRUE);
397483d3bc6fSnarayan 				mutex_exit(&ldcp->tx_lock);
397583d3bc6fSnarayan 				return (ECONNRESET);
397683d3bc6fSnarayan 			}
3977*58283286Sha137994 		}
397883d3bc6fSnarayan 
39791ae08745Sheppo 		/* process data messages */
39801ae08745Sheppo 		if ((msg->type & LDC_DATA) && (msg->stype & LDC_INFO)) {
39811ae08745Sheppo 
39821ae08745Sheppo 			uint8_t *msgbuf = (uint8_t *)(
39831ae08745Sheppo 			    (ldcp->mode == LDC_MODE_RELIABLE ||
398422f747efSnarayan 			    ldcp->mode == LDC_MODE_STREAM) ?
398522f747efSnarayan 			    msg->rdata : msg->udata);
39861ae08745Sheppo 
39871ae08745Sheppo 			D2(ldcp->id,
39881ae08745Sheppo 			    "ldc_read: (0x%llx) received data msg\n", ldcp->id);
39891ae08745Sheppo 
39901ae08745Sheppo 			/* get the packet length */
39911ae08745Sheppo 			len = (msg->env & LDC_LEN_MASK);
39921ae08745Sheppo 
39931ae08745Sheppo 				/*
39941ae08745Sheppo 				 * FUTURE OPTIMIZATION:
39951ae08745Sheppo 				 * dont need to set q head for every
39961ae08745Sheppo 				 * packet we read just need to do this when
39971ae08745Sheppo 				 * we are done or need to wait for more
39981ae08745Sheppo 				 * mondos to make a full packet - this is
39991ae08745Sheppo 				 * currently expensive.
40001ae08745Sheppo 				 */
40011ae08745Sheppo 
4002d10e4ef2Snarayan 			if (first_fragment == 0) {
40031ae08745Sheppo 
40041ae08745Sheppo 				/*
40051ae08745Sheppo 				 * first packets should always have the start
40061ae08745Sheppo 				 * bit set (even for a single packet). If not
40071ae08745Sheppo 				 * throw away the packet
40081ae08745Sheppo 				 */
40091ae08745Sheppo 				if (!(msg->env & LDC_FRAG_START)) {
40101ae08745Sheppo 
40111ae08745Sheppo 					DWARN(DBG_ALL_LDCS,
40121ae08745Sheppo 					    "ldc_read: (0x%llx) not start - "
40131ae08745Sheppo 					    "frag=%x\n", ldcp->id,
40141ae08745Sheppo 					    (msg->env) & LDC_FRAG_MASK);
40151ae08745Sheppo 
40161ae08745Sheppo 					/* toss pkt, inc head, cont reading */
40171ae08745Sheppo 					bytes_read = 0;
40181ae08745Sheppo 					target = target_bufp;
40191ae08745Sheppo 					curr_head =
40201ae08745Sheppo 					    (curr_head + LDC_PACKET_SIZE)
40211ae08745Sheppo 					    & q_size_mask;
4022*58283286Sha137994 					if (rv = ldcp->readq_set_head(ldcp,
40231ae08745Sheppo 					    curr_head))
40241ae08745Sheppo 						break;
40251ae08745Sheppo 
40261ae08745Sheppo 					continue;
40271ae08745Sheppo 				}
40281ae08745Sheppo 
4029d10e4ef2Snarayan 				first_fragment = msg->seqid;
40301ae08745Sheppo 			} else {
40311ae08745Sheppo 				/* check to see if this is a pkt w/ START bit */
40321ae08745Sheppo 				if (msg->env & LDC_FRAG_START) {
40331ae08745Sheppo 					DWARN(DBG_ALL_LDCS,
40341ae08745Sheppo 					    "ldc_read:(0x%llx) unexpected pkt"
40351ae08745Sheppo 					    " env=0x%x discarding %d bytes,"
40361ae08745Sheppo 					    " lastmsg=%d, currentmsg=%d\n",
40371ae08745Sheppo 					    ldcp->id, msg->env&LDC_FRAG_MASK,
40381ae08745Sheppo 					    bytes_read, ldcp->last_msg_rcd,
40391ae08745Sheppo 					    msg->seqid);
40401ae08745Sheppo 
40411ae08745Sheppo 					/* throw data we have read so far */
40421ae08745Sheppo 					bytes_read = 0;
40431ae08745Sheppo 					target = target_bufp;
4044d10e4ef2Snarayan 					first_fragment = msg->seqid;
40451ae08745Sheppo 
4046*58283286Sha137994 					if (rv = ldcp->readq_set_head(ldcp,
40471ae08745Sheppo 					    curr_head))
40481ae08745Sheppo 						break;
40491ae08745Sheppo 				}
40501ae08745Sheppo 			}
40511ae08745Sheppo 
40521ae08745Sheppo 			/* copy (next) pkt into buffer */
40531ae08745Sheppo 			if (len <= (*sizep - bytes_read)) {
40541ae08745Sheppo 				bcopy(msgbuf, target, len);
40551ae08745Sheppo 				target += len;
40561ae08745Sheppo 				bytes_read += len;
40571ae08745Sheppo 			} else {
40581ae08745Sheppo 				/*
40591ae08745Sheppo 				 * there is not enough space in the buffer to
40601ae08745Sheppo 				 * read this pkt. throw message away & continue
40611ae08745Sheppo 				 * reading data from queue
40621ae08745Sheppo 				 */
40631ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
40641ae08745Sheppo 				    "ldc_read: (0x%llx) buffer too small, "
40651ae08745Sheppo 				    "head=0x%lx, expect=%d, got=%d\n", ldcp->id,
40661ae08745Sheppo 				    curr_head, *sizep, bytes_read+len);
40671ae08745Sheppo 
4068d10e4ef2Snarayan 				first_fragment = 0;
40691ae08745Sheppo 				target = target_bufp;
40701ae08745Sheppo 				bytes_read = 0;
40711ae08745Sheppo 
40721ae08745Sheppo 				/* throw away everything received so far */
4073*58283286Sha137994 				if (rv = ldcp->readq_set_head(ldcp, curr_head))
40741ae08745Sheppo 					break;
40751ae08745Sheppo 
40761ae08745Sheppo 				/* continue reading remaining pkts */
40771ae08745Sheppo 				continue;
40781ae08745Sheppo 			}
40791ae08745Sheppo 		}
40801ae08745Sheppo 
40811ae08745Sheppo 		/* set the message id */
4082*58283286Sha137994 		if (ldcp->mode != LDC_MODE_STREAM)
40831ae08745Sheppo 			ldcp->last_msg_rcd = msg->seqid;
40841ae08745Sheppo 
40851ae08745Sheppo 		/* move the head one position */
40861ae08745Sheppo 		curr_head = (curr_head + LDC_PACKET_SIZE) & q_size_mask;
40871ae08745Sheppo 
40881ae08745Sheppo 		if (msg->env & LDC_FRAG_STOP) {
40891ae08745Sheppo 
40901ae08745Sheppo 			/*
40911ae08745Sheppo 			 * All pkts that are part of this fragmented transfer
40921ae08745Sheppo 			 * have been read or this was a single pkt read
40931ae08745Sheppo 			 * or there was an error
40941ae08745Sheppo 			 */
40951ae08745Sheppo 
40961ae08745Sheppo 			/* set the queue head */
4097*58283286Sha137994 			if (rv = ldcp->readq_set_head(ldcp, curr_head))
40981ae08745Sheppo 				bytes_read = 0;
40991ae08745Sheppo 
41001ae08745Sheppo 			*sizep = bytes_read;
41011ae08745Sheppo 
41021ae08745Sheppo 			break;
41031ae08745Sheppo 		}
41041ae08745Sheppo 
4105332608acSnarayan 		/* advance head if it is a CTRL packet or a DATA ACK packet */
4106332608acSnarayan 		if ((msg->type & LDC_CTRL) ||
4107332608acSnarayan 		    ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK))) {
41081ae08745Sheppo 
41091ae08745Sheppo 			/* set the queue head */
4110*58283286Sha137994 			if (rv = ldcp->readq_set_head(ldcp, curr_head)) {
41111ae08745Sheppo 				bytes_read = 0;
41121ae08745Sheppo 				break;
41131ae08745Sheppo 			}
41141ae08745Sheppo 
41151ae08745Sheppo 			D2(ldcp->id, "ldc_read: (0x%llx) set ACK qhead 0x%llx",
41161ae08745Sheppo 			    ldcp->id, curr_head);
41171ae08745Sheppo 		}
41181ae08745Sheppo 
41191ae08745Sheppo 	} /* for (;;) */
41201ae08745Sheppo 
41211ae08745Sheppo 	D2(ldcp->id, "ldc_read: (0x%llx) end size=%d", ldcp->id, *sizep);
41221ae08745Sheppo 
41231ae08745Sheppo 	return (rv);
41243af08d82Slm66018 
41253af08d82Slm66018 channel_is_reset:
41263af08d82Slm66018 	mutex_enter(&ldcp->tx_lock);
41273af08d82Slm66018 	i_ldc_reset(ldcp, B_FALSE);
41283af08d82Slm66018 	mutex_exit(&ldcp->tx_lock);
41293af08d82Slm66018 	return (ECONNRESET);
41301ae08745Sheppo }
41311ae08745Sheppo 
41321ae08745Sheppo /*
41331ae08745Sheppo  * Use underlying reliable packet mechanism to fetch
41341ae08745Sheppo  * and buffer incoming packets so we can hand them back as
41351ae08745Sheppo  * a basic byte stream.
41361ae08745Sheppo  *
41371ae08745Sheppo  * Enter and exit with ldcp->lock held by caller
41381ae08745Sheppo  */
41391ae08745Sheppo static int
41401ae08745Sheppo i_ldc_read_stream(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep)
41411ae08745Sheppo {
41421ae08745Sheppo 	int	rv;
41431ae08745Sheppo 	size_t	size;
41441ae08745Sheppo 
41451ae08745Sheppo 	ASSERT(mutex_owned(&ldcp->lock));
41461ae08745Sheppo 
41471ae08745Sheppo 	D2(ldcp->id, "i_ldc_read_stream: (0x%llx) buffer size=%d",
41481ae08745Sheppo 	    ldcp->id, *sizep);
41491ae08745Sheppo 
41501ae08745Sheppo 	if (ldcp->stream_remains == 0) {
41511ae08745Sheppo 		size = ldcp->mtu;
41521ae08745Sheppo 		rv = i_ldc_read_packet(ldcp,
41531ae08745Sheppo 		    (caddr_t)ldcp->stream_bufferp, &size);
41541ae08745Sheppo 		D2(ldcp->id, "i_ldc_read_stream: read packet (0x%llx) size=%d",
41551ae08745Sheppo 		    ldcp->id, size);
41561ae08745Sheppo 
41571ae08745Sheppo 		if (rv != 0)
41581ae08745Sheppo 			return (rv);
41591ae08745Sheppo 
41601ae08745Sheppo 		ldcp->stream_remains = size;
41611ae08745Sheppo 		ldcp->stream_offset = 0;
41621ae08745Sheppo 	}
41631ae08745Sheppo 
41641ae08745Sheppo 	size = MIN(ldcp->stream_remains, *sizep);
41651ae08745Sheppo 
41661ae08745Sheppo 	bcopy(ldcp->stream_bufferp + ldcp->stream_offset, target_bufp, size);
41671ae08745Sheppo 	ldcp->stream_offset += size;
41681ae08745Sheppo 	ldcp->stream_remains -= size;
41691ae08745Sheppo 
41701ae08745Sheppo 	D2(ldcp->id, "i_ldc_read_stream: (0x%llx) fill from buffer size=%d",
41711ae08745Sheppo 	    ldcp->id, size);
41721ae08745Sheppo 
41731ae08745Sheppo 	*sizep = size;
41741ae08745Sheppo 	return (0);
41751ae08745Sheppo }
41761ae08745Sheppo 
41771ae08745Sheppo /*
41781ae08745Sheppo  * Write specified amount of bytes to the channel
41791ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
41801ae08745Sheppo  * packet is tagged with an unique packet ID in
4181e1ebb9ecSlm66018  * the case of a reliable link.
41821ae08745Sheppo  *
41831ae08745Sheppo  * On return, size contains the number of bytes written.
41841ae08745Sheppo  */
41851ae08745Sheppo int
41861ae08745Sheppo ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep)
41871ae08745Sheppo {
41881ae08745Sheppo 	ldc_chan_t	*ldcp;
41891ae08745Sheppo 	int		rv = 0;
41901ae08745Sheppo 
41911ae08745Sheppo 	if (handle == NULL) {
41921ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_write: invalid channel handle\n");
41931ae08745Sheppo 		return (EINVAL);
41941ae08745Sheppo 	}
41951ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
41961ae08745Sheppo 
4197d10e4ef2Snarayan 	/* check if writes can occur */
4198d10e4ef2Snarayan 	if (!mutex_tryenter(&ldcp->tx_lock)) {
4199d10e4ef2Snarayan 		/*
4200d10e4ef2Snarayan 		 * Could not get the lock - channel could
4201d10e4ef2Snarayan 		 * be in the process of being unconfigured
4202d10e4ef2Snarayan 		 * or reader has encountered an error
4203d10e4ef2Snarayan 		 */
4204d10e4ef2Snarayan 		return (EAGAIN);
4205d10e4ef2Snarayan 	}
42061ae08745Sheppo 
42071ae08745Sheppo 	/* check if non-zero data to write */
42081ae08745Sheppo 	if (buf == NULL || sizep == NULL) {
42091ae08745Sheppo 		DWARN(ldcp->id, "ldc_write: (0x%llx) invalid data write\n",
42101ae08745Sheppo 		    ldcp->id);
4211d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
42121ae08745Sheppo 		return (EINVAL);
42131ae08745Sheppo 	}
42141ae08745Sheppo 
42151ae08745Sheppo 	if (*sizep == 0) {
42161ae08745Sheppo 		DWARN(ldcp->id, "ldc_write: (0x%llx) write size of zero\n",
42171ae08745Sheppo 		    ldcp->id);
4218d10e4ef2Snarayan 		mutex_exit(&ldcp->tx_lock);
42191ae08745Sheppo 		return (0);
42201ae08745Sheppo 	}
42211ae08745Sheppo 
42221ae08745Sheppo 	/* Check if channel is UP for data exchange */
42231ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
42241ae08745Sheppo 		DWARN(ldcp->id,
42251ae08745Sheppo 		    "ldc_write: (0x%llx) channel is not in UP state\n",
42261ae08745Sheppo 		    ldcp->id);
42271ae08745Sheppo 		*sizep = 0;
42281ae08745Sheppo 		rv = ECONNRESET;
42291ae08745Sheppo 	} else {
42301ae08745Sheppo 		rv = ldcp->write_p(ldcp, buf, sizep);
42311ae08745Sheppo 	}
42321ae08745Sheppo 
4233d10e4ef2Snarayan 	mutex_exit(&ldcp->tx_lock);
42341ae08745Sheppo 
42351ae08745Sheppo 	return (rv);
42361ae08745Sheppo }
42371ae08745Sheppo 
42381ae08745Sheppo /*
42391ae08745Sheppo  * Write a raw packet to the channel
42401ae08745Sheppo  * On return, size contains the number of bytes written.
42411ae08745Sheppo  */
42421ae08745Sheppo static int
42431ae08745Sheppo i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
42441ae08745Sheppo {
42451ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
42461ae08745Sheppo 	uint64_t 	tx_head, tx_tail, new_tail;
42471ae08745Sheppo 	int		rv = 0;
42481ae08745Sheppo 	size_t		size;
42491ae08745Sheppo 
4250d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
42511ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_RAW);
42521ae08745Sheppo 
42531ae08745Sheppo 	size = *sizep;
42541ae08745Sheppo 
42551ae08745Sheppo 	/*
42561ae08745Sheppo 	 * Check to see if the packet size is less than or
42571ae08745Sheppo 	 * equal to packet size support in raw mode
42581ae08745Sheppo 	 */
42591ae08745Sheppo 	if (size > ldcp->pkt_payload) {
42601ae08745Sheppo 		DWARN(ldcp->id,
42611ae08745Sheppo 		    "ldc_write: (0x%llx) invalid size (0x%llx) for RAW mode\n",
42621ae08745Sheppo 		    ldcp->id, *sizep);
42631ae08745Sheppo 		*sizep = 0;
42641ae08745Sheppo 		return (EMSGSIZE);
42651ae08745Sheppo 	}
42661ae08745Sheppo 
42671ae08745Sheppo 	/* get the qptrs for the tx queue */
42681ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
42691ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
42701ae08745Sheppo 	if (rv != 0) {
42711ae08745Sheppo 		cmn_err(CE_WARN,
42721ae08745Sheppo 		    "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
42731ae08745Sheppo 		*sizep = 0;
42741ae08745Sheppo 		return (EIO);
42751ae08745Sheppo 	}
42761ae08745Sheppo 
42771ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
42781ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
42791ae08745Sheppo 		DWARN(ldcp->id,
42801ae08745Sheppo 		    "ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
4281d10e4ef2Snarayan 
42821ae08745Sheppo 		*sizep = 0;
4283d10e4ef2Snarayan 		if (mutex_tryenter(&ldcp->lock)) {
42843af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4285d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4286d10e4ef2Snarayan 		} else {
4287d10e4ef2Snarayan 			/*
4288d10e4ef2Snarayan 			 * Release Tx lock, and then reacquire channel
4289d10e4ef2Snarayan 			 * and Tx lock in correct order
4290d10e4ef2Snarayan 			 */
4291d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
4292d10e4ef2Snarayan 			mutex_enter(&ldcp->lock);
4293d10e4ef2Snarayan 			mutex_enter(&ldcp->tx_lock);
42943af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4295d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4296d10e4ef2Snarayan 		}
42971ae08745Sheppo 		return (ECONNRESET);
42981ae08745Sheppo 	}
42991ae08745Sheppo 
43001ae08745Sheppo 	tx_tail = ldcp->tx_tail;
43011ae08745Sheppo 	tx_head = ldcp->tx_head;
43021ae08745Sheppo 	new_tail = (tx_tail + LDC_PACKET_SIZE) &
43031ae08745Sheppo 	    ((ldcp->tx_q_entries-1) << LDC_PACKET_SHIFT);
43041ae08745Sheppo 
43051ae08745Sheppo 	if (new_tail == tx_head) {
43061ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
43071ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
43081ae08745Sheppo 		*sizep = 0;
43091ae08745Sheppo 		return (EWOULDBLOCK);
43101ae08745Sheppo 	}
43111ae08745Sheppo 
43121ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
43131ae08745Sheppo 	    ldcp->id, size);
43141ae08745Sheppo 
43151ae08745Sheppo 	/* Send the data now */
43161ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
43171ae08745Sheppo 
43181ae08745Sheppo 	/* copy the data into pkt */
43191ae08745Sheppo 	bcopy((uint8_t *)buf, ldcmsg, size);
43201ae08745Sheppo 
43211ae08745Sheppo 	/* increment tail */
43221ae08745Sheppo 	tx_tail = new_tail;
43231ae08745Sheppo 
43241ae08745Sheppo 	/*
43251ae08745Sheppo 	 * All packets have been copied into the TX queue
43261ae08745Sheppo 	 * update the tail ptr in the HV
43271ae08745Sheppo 	 */
43281ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
43291ae08745Sheppo 	if (rv) {
43301ae08745Sheppo 		if (rv == EWOULDBLOCK) {
43311ae08745Sheppo 			DWARN(ldcp->id, "ldc_write: (0x%llx) write timed out\n",
43321ae08745Sheppo 			    ldcp->id);
43331ae08745Sheppo 			*sizep = 0;
43341ae08745Sheppo 			return (EWOULDBLOCK);
43351ae08745Sheppo 		}
43361ae08745Sheppo 
43371ae08745Sheppo 		*sizep = 0;
4338d10e4ef2Snarayan 		if (mutex_tryenter(&ldcp->lock)) {
43393af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4340d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4341d10e4ef2Snarayan 		} else {
4342d10e4ef2Snarayan 			/*
4343d10e4ef2Snarayan 			 * Release Tx lock, and then reacquire channel
4344d10e4ef2Snarayan 			 * and Tx lock in correct order
4345d10e4ef2Snarayan 			 */
4346d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
4347d10e4ef2Snarayan 			mutex_enter(&ldcp->lock);
4348d10e4ef2Snarayan 			mutex_enter(&ldcp->tx_lock);
43493af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4350d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4351d10e4ef2Snarayan 		}
43521ae08745Sheppo 		return (ECONNRESET);
43531ae08745Sheppo 	}
43541ae08745Sheppo 
43551ae08745Sheppo 	ldcp->tx_tail = tx_tail;
43561ae08745Sheppo 	*sizep = size;
43571ae08745Sheppo 
43581ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, size);
43591ae08745Sheppo 
43601ae08745Sheppo 	return (rv);
43611ae08745Sheppo }
43621ae08745Sheppo 
43631ae08745Sheppo 
43641ae08745Sheppo /*
43651ae08745Sheppo  * Write specified amount of bytes to the channel
43661ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
43671ae08745Sheppo  * packet is tagged with an unique packet ID in
4368e1ebb9ecSlm66018  * the case of a reliable link.
43691ae08745Sheppo  *
43701ae08745Sheppo  * On return, size contains the number of bytes written.
43711ae08745Sheppo  * This function needs to ensure that the write size is < MTU size
43721ae08745Sheppo  */
43731ae08745Sheppo static int
43741ae08745Sheppo i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size)
43751ae08745Sheppo {
43761ae08745Sheppo 	ldc_msg_t 	*ldcmsg;
43771ae08745Sheppo 	uint64_t 	tx_head, tx_tail, new_tail, start;
43781ae08745Sheppo 	uint64_t	txq_size_mask, numavail;
43791ae08745Sheppo 	uint8_t 	*msgbuf, *source = (uint8_t *)buf;
43801ae08745Sheppo 	size_t 		len, bytes_written = 0, remaining;
43811ae08745Sheppo 	int		rv;
43821ae08745Sheppo 	uint32_t	curr_seqid;
43831ae08745Sheppo 
4384d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
43851ae08745Sheppo 
43861ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_RELIABLE ||
43871ae08745Sheppo 	    ldcp->mode == LDC_MODE_UNRELIABLE ||
43881ae08745Sheppo 	    ldcp->mode == LDC_MODE_STREAM);
43891ae08745Sheppo 
43901ae08745Sheppo 	/* compute mask for increment */
43911ae08745Sheppo 	txq_size_mask = (ldcp->tx_q_entries - 1) << LDC_PACKET_SHIFT;
43921ae08745Sheppo 
43931ae08745Sheppo 	/* get the qptrs for the tx queue */
43941ae08745Sheppo 	rv = hv_ldc_tx_get_state(ldcp->id,
43951ae08745Sheppo 	    &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state);
43961ae08745Sheppo 	if (rv != 0) {
43971ae08745Sheppo 		cmn_err(CE_WARN,
43981ae08745Sheppo 		    "ldc_write: (0x%lx) cannot read queue ptrs\n", ldcp->id);
43991ae08745Sheppo 		*size = 0;
44001ae08745Sheppo 		return (EIO);
44011ae08745Sheppo 	}
44021ae08745Sheppo 
44031ae08745Sheppo 	if (ldcp->link_state == LDC_CHANNEL_DOWN ||
44041ae08745Sheppo 	    ldcp->link_state == LDC_CHANNEL_RESET) {
44051ae08745Sheppo 		DWARN(ldcp->id,
44061ae08745Sheppo 		    "ldc_write: (0x%llx) channel down/reset\n", ldcp->id);
44071ae08745Sheppo 		*size = 0;
4408d10e4ef2Snarayan 		if (mutex_tryenter(&ldcp->lock)) {
44093af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4410d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4411d10e4ef2Snarayan 		} else {
4412d10e4ef2Snarayan 			/*
4413d10e4ef2Snarayan 			 * Release Tx lock, and then reacquire channel
4414d10e4ef2Snarayan 			 * and Tx lock in correct order
4415d10e4ef2Snarayan 			 */
4416d10e4ef2Snarayan 			mutex_exit(&ldcp->tx_lock);
4417d10e4ef2Snarayan 			mutex_enter(&ldcp->lock);
4418d10e4ef2Snarayan 			mutex_enter(&ldcp->tx_lock);
44193af08d82Slm66018 			i_ldc_reset(ldcp, B_FALSE);
4420d10e4ef2Snarayan 			mutex_exit(&ldcp->lock);
4421d10e4ef2Snarayan 		}
44221ae08745Sheppo 		return (ECONNRESET);
44231ae08745Sheppo 	}
44241ae08745Sheppo 
44251ae08745Sheppo 	tx_tail = ldcp->tx_tail;
44261ae08745Sheppo 	new_tail = (tx_tail + LDC_PACKET_SIZE) %
44271ae08745Sheppo 	    (ldcp->tx_q_entries << LDC_PACKET_SHIFT);
44281ae08745Sheppo 
44291ae08745Sheppo 	/*
443022f747efSnarayan 	 * Check to see if the queue is full. The check is done using
443122f747efSnarayan 	 * the appropriate head based on the link mode.
44321ae08745Sheppo 	 */
443322f747efSnarayan 	i_ldc_get_tx_head(ldcp, &tx_head);
443422f747efSnarayan 
44351ae08745Sheppo 	if (new_tail == tx_head) {
44361ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
44371ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue is full\n", ldcp->id);
44381ae08745Sheppo 		*size = 0;
44391ae08745Sheppo 		return (EWOULDBLOCK);
44401ae08745Sheppo 	}
44411ae08745Sheppo 
44421ae08745Sheppo 	/*
44431ae08745Sheppo 	 * Make sure that the LDC Tx queue has enough space
44441ae08745Sheppo 	 */
44451ae08745Sheppo 	numavail = (tx_head >> LDC_PACKET_SHIFT) - (tx_tail >> LDC_PACKET_SHIFT)
44461ae08745Sheppo 	    + ldcp->tx_q_entries - 1;
44471ae08745Sheppo 	numavail %= ldcp->tx_q_entries;
44481ae08745Sheppo 
44491ae08745Sheppo 	if (*size > (numavail * ldcp->pkt_payload)) {
44501ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
44511ae08745Sheppo 		    "ldc_write: (0x%llx) TX queue has no space\n", ldcp->id);
44521ae08745Sheppo 		return (EWOULDBLOCK);
44531ae08745Sheppo 	}
44541ae08745Sheppo 
44551ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) start xfer size=%d",
44561ae08745Sheppo 	    ldcp->id, *size);
44571ae08745Sheppo 
44581ae08745Sheppo 	/* Send the data now */
44591ae08745Sheppo 	bytes_written = 0;
44601ae08745Sheppo 	curr_seqid = ldcp->last_msg_snt;
44611ae08745Sheppo 	start = tx_tail;
44621ae08745Sheppo 
44631ae08745Sheppo 	while (*size > bytes_written) {
44641ae08745Sheppo 
44651ae08745Sheppo 		ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail);
44661ae08745Sheppo 
44671ae08745Sheppo 		msgbuf = (uint8_t *)((ldcp->mode == LDC_MODE_RELIABLE ||
446822f747efSnarayan 		    ldcp->mode == LDC_MODE_STREAM) ?
446922f747efSnarayan 		    ldcmsg->rdata : ldcmsg->udata);
44701ae08745Sheppo 
44711ae08745Sheppo 		ldcmsg->type = LDC_DATA;
44721ae08745Sheppo 		ldcmsg->stype = LDC_INFO;
44731ae08745Sheppo 		ldcmsg->ctrl = 0;
44741ae08745Sheppo 
44751ae08745Sheppo 		remaining = *size - bytes_written;
44761ae08745Sheppo 		len = min(ldcp->pkt_payload, remaining);
44771ae08745Sheppo 		ldcmsg->env = (uint8_t)len;
44781ae08745Sheppo 
44791ae08745Sheppo 		curr_seqid++;
44801ae08745Sheppo 		ldcmsg->seqid = curr_seqid;
44811ae08745Sheppo 
44821ae08745Sheppo 		/* copy the data into pkt */
44831ae08745Sheppo 		bcopy(source, msgbuf, len);
44841ae08745Sheppo 
44851ae08745Sheppo 		source += len;
44861ae08745Sheppo 		bytes_written += len;
44871ae08745Sheppo 
44881ae08745Sheppo 		/* increment tail */
44891ae08745Sheppo 		tx_tail = (tx_tail + LDC_PACKET_SIZE) & txq_size_mask;
44901ae08745Sheppo 
44911ae08745Sheppo 		ASSERT(tx_tail != tx_head);
44921ae08745Sheppo 	}
44931ae08745Sheppo 
44941ae08745Sheppo 	/* Set the start and stop bits */
44951ae08745Sheppo 	ldcmsg->env |= LDC_FRAG_STOP;
44961ae08745Sheppo 	ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + start);
44971ae08745Sheppo 	ldcmsg->env |= LDC_FRAG_START;
44981ae08745Sheppo 
44991ae08745Sheppo 	/*
45001ae08745Sheppo 	 * All packets have been copied into the TX queue
45011ae08745Sheppo 	 * update the tail ptr in the HV
45021ae08745Sheppo 	 */
45031ae08745Sheppo 	rv = i_ldc_set_tx_tail(ldcp, tx_tail);
45041ae08745Sheppo 	if (rv == 0) {
45051ae08745Sheppo 		ldcp->tx_tail = tx_tail;
45061ae08745Sheppo 		ldcp->last_msg_snt = curr_seqid;
45071ae08745Sheppo 		*size = bytes_written;
45081ae08745Sheppo 	} else {
45091ae08745Sheppo 		int rv2;
45101ae08745Sheppo 
45111ae08745Sheppo 		if (rv != EWOULDBLOCK) {
45121ae08745Sheppo 			*size = 0;
4513d10e4ef2Snarayan 			if (mutex_tryenter(&ldcp->lock)) {
45143af08d82Slm66018 				i_ldc_reset(ldcp, B_FALSE);
4515d10e4ef2Snarayan 				mutex_exit(&ldcp->lock);
4516d10e4ef2Snarayan 			} else {
4517d10e4ef2Snarayan 				/*
4518d10e4ef2Snarayan 				 * Release Tx lock, and then reacquire channel
4519d10e4ef2Snarayan 				 * and Tx lock in correct order
4520d10e4ef2Snarayan 				 */
4521d10e4ef2Snarayan 				mutex_exit(&ldcp->tx_lock);
4522d10e4ef2Snarayan 				mutex_enter(&ldcp->lock);
4523d10e4ef2Snarayan 				mutex_enter(&ldcp->tx_lock);
45243af08d82Slm66018 				i_ldc_reset(ldcp, B_FALSE);
4525d10e4ef2Snarayan 				mutex_exit(&ldcp->lock);
4526d10e4ef2Snarayan 			}
45271ae08745Sheppo 			return (ECONNRESET);
45281ae08745Sheppo 		}
45291ae08745Sheppo 
4530cb112a14Slm66018 		D1(ldcp->id, "hv_tx_set_tail returns 0x%x (head 0x%x, "
45311ae08745Sheppo 		    "old tail 0x%x, new tail 0x%x, qsize=0x%x)\n",
45321ae08745Sheppo 		    rv, ldcp->tx_head, ldcp->tx_tail, tx_tail,
45331ae08745Sheppo 		    (ldcp->tx_q_entries << LDC_PACKET_SHIFT));
45341ae08745Sheppo 
45351ae08745Sheppo 		rv2 = hv_ldc_tx_get_state(ldcp->id,
45361ae08745Sheppo 		    &tx_head, &tx_tail, &ldcp->link_state);
45371ae08745Sheppo 
4538cb112a14Slm66018 		D1(ldcp->id, "hv_ldc_tx_get_state returns 0x%x "
45391ae08745Sheppo 		    "(head 0x%x, tail 0x%x state 0x%x)\n",
45401ae08745Sheppo 		    rv2, tx_head, tx_tail, ldcp->link_state);
45411ae08745Sheppo 
45421ae08745Sheppo 		*size = 0;
45431ae08745Sheppo 	}
45441ae08745Sheppo 
45451ae08745Sheppo 	D2(ldcp->id, "ldc_write: (0x%llx) end xfer size=%d", ldcp->id, *size);
45461ae08745Sheppo 
45471ae08745Sheppo 	return (rv);
45481ae08745Sheppo }
45491ae08745Sheppo 
45501ae08745Sheppo /*
45511ae08745Sheppo  * Write specified amount of bytes to the channel
45521ae08745Sheppo  * in multiple pkts of pkt_payload size. Each
45531ae08745Sheppo  * packet is tagged with an unique packet ID in
4554e1ebb9ecSlm66018  * the case of a reliable link.
45551ae08745Sheppo  *
45561ae08745Sheppo  * On return, size contains the number of bytes written.
45571ae08745Sheppo  * This function needs to ensure that the write size is < MTU size
45581ae08745Sheppo  */
45591ae08745Sheppo static int
45601ae08745Sheppo i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep)
45611ae08745Sheppo {
4562d10e4ef2Snarayan 	ASSERT(MUTEX_HELD(&ldcp->tx_lock));
45631ae08745Sheppo 	ASSERT(ldcp->mode == LDC_MODE_STREAM);
45641ae08745Sheppo 
45651ae08745Sheppo 	/* Truncate packet to max of MTU size */
45661ae08745Sheppo 	if (*sizep > ldcp->mtu) *sizep = ldcp->mtu;
45671ae08745Sheppo 	return (i_ldc_write_packet(ldcp, buf, sizep));
45681ae08745Sheppo }
45691ae08745Sheppo 
45701ae08745Sheppo 
45711ae08745Sheppo /*
45721ae08745Sheppo  * Interfaces for channel nexus to register/unregister with LDC module
45731ae08745Sheppo  * The nexus will register functions to be used to register individual
45741ae08745Sheppo  * channels with the nexus and enable interrupts for the channels
45751ae08745Sheppo  */
45761ae08745Sheppo int
45771ae08745Sheppo ldc_register(ldc_cnex_t *cinfo)
45781ae08745Sheppo {
45791ae08745Sheppo 	ldc_chan_t	*ldcp;
45801ae08745Sheppo 
45811ae08745Sheppo 	if (cinfo == NULL || cinfo->dip == NULL ||
45821ae08745Sheppo 	    cinfo->reg_chan == NULL || cinfo->unreg_chan == NULL ||
45831ae08745Sheppo 	    cinfo->add_intr == NULL || cinfo->rem_intr == NULL ||
45841ae08745Sheppo 	    cinfo->clr_intr == NULL) {
45851ae08745Sheppo 
45861ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_register: invalid nexus info\n");
45871ae08745Sheppo 		return (EINVAL);
45881ae08745Sheppo 	}
45891ae08745Sheppo 
45901ae08745Sheppo 	mutex_enter(&ldcssp->lock);
45911ae08745Sheppo 
45921ae08745Sheppo 	/* nexus registration */
45931ae08745Sheppo 	ldcssp->cinfo.dip = cinfo->dip;
45941ae08745Sheppo 	ldcssp->cinfo.reg_chan = cinfo->reg_chan;
45951ae08745Sheppo 	ldcssp->cinfo.unreg_chan = cinfo->unreg_chan;
45961ae08745Sheppo 	ldcssp->cinfo.add_intr = cinfo->add_intr;
45971ae08745Sheppo 	ldcssp->cinfo.rem_intr = cinfo->rem_intr;
45981ae08745Sheppo 	ldcssp->cinfo.clr_intr = cinfo->clr_intr;
45991ae08745Sheppo 
46001ae08745Sheppo 	/* register any channels that might have been previously initialized */
46011ae08745Sheppo 	ldcp = ldcssp->chan_list;
46021ae08745Sheppo 	while (ldcp) {
46031ae08745Sheppo 		if ((ldcp->tstate & TS_QCONF_RDY) &&
46041ae08745Sheppo 		    (ldcp->tstate & TS_CNEX_RDY) == 0)
46051ae08745Sheppo 			(void) i_ldc_register_channel(ldcp);
46061ae08745Sheppo 
46071ae08745Sheppo 		ldcp = ldcp->next;
46081ae08745Sheppo 	}
46091ae08745Sheppo 
46101ae08745Sheppo 	mutex_exit(&ldcssp->lock);
46111ae08745Sheppo 
46121ae08745Sheppo 	return (0);
46131ae08745Sheppo }
46141ae08745Sheppo 
46151ae08745Sheppo int
46161ae08745Sheppo ldc_unregister(ldc_cnex_t *cinfo)
46171ae08745Sheppo {
46181ae08745Sheppo 	if (cinfo == NULL || cinfo->dip == NULL) {
46191ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid nexus info\n");
46201ae08745Sheppo 		return (EINVAL);
46211ae08745Sheppo 	}
46221ae08745Sheppo 
46231ae08745Sheppo 	mutex_enter(&ldcssp->lock);
46241ae08745Sheppo 
46251ae08745Sheppo 	if (cinfo->dip != ldcssp->cinfo.dip) {
46261ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_unregister: invalid dip\n");
46271ae08745Sheppo 		mutex_exit(&ldcssp->lock);
46281ae08745Sheppo 		return (EINVAL);
46291ae08745Sheppo 	}
46301ae08745Sheppo 
46311ae08745Sheppo 	/* nexus unregister */
46321ae08745Sheppo 	ldcssp->cinfo.dip = NULL;
46331ae08745Sheppo 	ldcssp->cinfo.reg_chan = NULL;
46341ae08745Sheppo 	ldcssp->cinfo.unreg_chan = NULL;
46351ae08745Sheppo 	ldcssp->cinfo.add_intr = NULL;
46361ae08745Sheppo 	ldcssp->cinfo.rem_intr = NULL;
46371ae08745Sheppo 	ldcssp->cinfo.clr_intr = NULL;
46381ae08745Sheppo 
46391ae08745Sheppo 	mutex_exit(&ldcssp->lock);
46401ae08745Sheppo 
46411ae08745Sheppo 	return (0);
46421ae08745Sheppo }
46431ae08745Sheppo 
46441ae08745Sheppo 
46451ae08745Sheppo /* ------------------------------------------------------------------------- */
46461ae08745Sheppo 
46471ae08745Sheppo /*
46481ae08745Sheppo  * Allocate a memory handle for the channel and link it into the list
46491ae08745Sheppo  * Also choose which memory table to use if this is the first handle
46501ae08745Sheppo  * being assigned to this channel
46511ae08745Sheppo  */
46521ae08745Sheppo int
46531ae08745Sheppo ldc_mem_alloc_handle(ldc_handle_t handle, ldc_mem_handle_t *mhandle)
46541ae08745Sheppo {
46551ae08745Sheppo 	ldc_chan_t 	*ldcp;
46561ae08745Sheppo 	ldc_mhdl_t	*mhdl;
46571ae08745Sheppo 
46581ae08745Sheppo 	if (handle == NULL) {
46591ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
46601ae08745Sheppo 		    "ldc_mem_alloc_handle: invalid channel handle\n");
46611ae08745Sheppo 		return (EINVAL);
46621ae08745Sheppo 	}
46631ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
46641ae08745Sheppo 
46651ae08745Sheppo 	mutex_enter(&ldcp->lock);
46661ae08745Sheppo 
46671ae08745Sheppo 	/* check to see if channel is initalized */
46683af08d82Slm66018 	if ((ldcp->tstate & ~TS_IN_RESET) < TS_INIT) {
46691ae08745Sheppo 		DWARN(ldcp->id,
46701ae08745Sheppo 		    "ldc_mem_alloc_handle: (0x%llx) channel not initialized\n",
46711ae08745Sheppo 		    ldcp->id);
46721ae08745Sheppo 		mutex_exit(&ldcp->lock);
46731ae08745Sheppo 		return (EINVAL);
46741ae08745Sheppo 	}
46751ae08745Sheppo 
46761ae08745Sheppo 	/* allocate handle for channel */
46774bac2208Snarayan 	mhdl = kmem_cache_alloc(ldcssp->memhdl_cache, KM_SLEEP);
46781ae08745Sheppo 
46791ae08745Sheppo 	/* initialize the lock */
46801ae08745Sheppo 	mutex_init(&mhdl->lock, NULL, MUTEX_DRIVER, NULL);
46811ae08745Sheppo 
46824bac2208Snarayan 	mhdl->myshadow = B_FALSE;
46834bac2208Snarayan 	mhdl->memseg = NULL;
46841ae08745Sheppo 	mhdl->ldcp = ldcp;
46854bac2208Snarayan 	mhdl->status = LDC_UNBOUND;
46861ae08745Sheppo 
46871ae08745Sheppo 	/* insert memory handle (@ head) into list */
46881ae08745Sheppo 	if (ldcp->mhdl_list == NULL) {
46891ae08745Sheppo 		ldcp->mhdl_list = mhdl;
46901ae08745Sheppo 		mhdl->next = NULL;
46911ae08745Sheppo 	} else {
46921ae08745Sheppo 		/* insert @ head */
46931ae08745Sheppo 		mhdl->next = ldcp->mhdl_list;
46941ae08745Sheppo 		ldcp->mhdl_list = mhdl;
46951ae08745Sheppo 	}
46961ae08745Sheppo 
46971ae08745Sheppo 	/* return the handle */
46981ae08745Sheppo 	*mhandle = (ldc_mem_handle_t)mhdl;
46991ae08745Sheppo 
47001ae08745Sheppo 	mutex_exit(&ldcp->lock);
47011ae08745Sheppo 
47021ae08745Sheppo 	D1(ldcp->id, "ldc_mem_alloc_handle: (0x%llx) allocated handle 0x%llx\n",
47031ae08745Sheppo 	    ldcp->id, mhdl);
47041ae08745Sheppo 
47051ae08745Sheppo 	return (0);
47061ae08745Sheppo }
47071ae08745Sheppo 
47081ae08745Sheppo /*
47091ae08745Sheppo  * Free memory handle for the channel and unlink it from the list
47101ae08745Sheppo  */
47111ae08745Sheppo int
47121ae08745Sheppo ldc_mem_free_handle(ldc_mem_handle_t mhandle)
47131ae08745Sheppo {
47141ae08745Sheppo 	ldc_mhdl_t 	*mhdl, *phdl;
47151ae08745Sheppo 	ldc_chan_t 	*ldcp;
47161ae08745Sheppo 
47171ae08745Sheppo 	if (mhandle == NULL) {
47181ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
47191ae08745Sheppo 		    "ldc_mem_free_handle: invalid memory handle\n");
47201ae08745Sheppo 		return (EINVAL);
47211ae08745Sheppo 	}
47221ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
47231ae08745Sheppo 
47241ae08745Sheppo 	mutex_enter(&mhdl->lock);
47251ae08745Sheppo 
47261ae08745Sheppo 	ldcp = mhdl->ldcp;
47271ae08745Sheppo 
47281ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) {
47291ae08745Sheppo 		DWARN(ldcp->id,
47301ae08745Sheppo 		    "ldc_mem_free_handle: cannot free, 0x%llx hdl bound\n",
47311ae08745Sheppo 		    mhdl);
47321ae08745Sheppo 		mutex_exit(&mhdl->lock);
47331ae08745Sheppo 		return (EINVAL);
47341ae08745Sheppo 	}
47351ae08745Sheppo 	mutex_exit(&mhdl->lock);
47361ae08745Sheppo 
47371ae08745Sheppo 	mutex_enter(&ldcp->mlist_lock);
47381ae08745Sheppo 
47391ae08745Sheppo 	phdl = ldcp->mhdl_list;
47401ae08745Sheppo 
47411ae08745Sheppo 	/* first handle */
47421ae08745Sheppo 	if (phdl == mhdl) {
47431ae08745Sheppo 		ldcp->mhdl_list = mhdl->next;
47441ae08745Sheppo 		mutex_destroy(&mhdl->lock);
47454bac2208Snarayan 		kmem_cache_free(ldcssp->memhdl_cache, mhdl);
47464bac2208Snarayan 
47471ae08745Sheppo 		D1(ldcp->id,
47481ae08745Sheppo 		    "ldc_mem_free_handle: (0x%llx) freed handle 0x%llx\n",
47491ae08745Sheppo 		    ldcp->id, mhdl);
47501ae08745Sheppo 	} else {
47511ae08745Sheppo 		/* walk the list - unlink and free */
47521ae08745Sheppo 		while (phdl != NULL) {
47531ae08745Sheppo 			if (phdl->next == mhdl) {
47541ae08745Sheppo 				phdl->next = mhdl->next;
47551ae08745Sheppo 				mutex_destroy(&mhdl->lock);
47564bac2208Snarayan 				kmem_cache_free(ldcssp->memhdl_cache, mhdl);
47571ae08745Sheppo 				D1(ldcp->id,
47581ae08745Sheppo 				    "ldc_mem_free_handle: (0x%llx) freed "
47591ae08745Sheppo 				    "handle 0x%llx\n", ldcp->id, mhdl);
47601ae08745Sheppo 				break;
47611ae08745Sheppo 			}
47621ae08745Sheppo 			phdl = phdl->next;
47631ae08745Sheppo 		}
47641ae08745Sheppo 	}
47651ae08745Sheppo 
47661ae08745Sheppo 	if (phdl == NULL) {
47671ae08745Sheppo 		DWARN(ldcp->id,
47681ae08745Sheppo 		    "ldc_mem_free_handle: invalid handle 0x%llx\n", mhdl);
47691ae08745Sheppo 		mutex_exit(&ldcp->mlist_lock);
47701ae08745Sheppo 		return (EINVAL);
47711ae08745Sheppo 	}
47721ae08745Sheppo 
47731ae08745Sheppo 	mutex_exit(&ldcp->mlist_lock);
47741ae08745Sheppo 
47751ae08745Sheppo 	return (0);
47761ae08745Sheppo }
47771ae08745Sheppo 
47781ae08745Sheppo /*
47791ae08745Sheppo  * Bind a memory handle to a virtual address.
47801ae08745Sheppo  * The virtual address is converted to the corresponding real addresses.
47811ae08745Sheppo  * Returns pointer to the first ldc_mem_cookie and the total number
47821ae08745Sheppo  * of cookies for this virtual address. Other cookies can be obtained
47831ae08745Sheppo  * using the ldc_mem_nextcookie() call. If the pages are stored in
47841ae08745Sheppo  * consecutive locations in the table, a single cookie corresponding to
47851ae08745Sheppo  * the first location is returned. The cookie size spans all the entries.
47861ae08745Sheppo  *
47871ae08745Sheppo  * If the VA corresponds to a page that is already being exported, reuse
47881ae08745Sheppo  * the page and do not export it again. Bump the page's use count.
47891ae08745Sheppo  */
47901ae08745Sheppo int
47911ae08745Sheppo ldc_mem_bind_handle(ldc_mem_handle_t mhandle, caddr_t vaddr, size_t len,
47921ae08745Sheppo     uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount)
47931ae08745Sheppo {
47941ae08745Sheppo 	ldc_mhdl_t	*mhdl;
47951ae08745Sheppo 	ldc_chan_t 	*ldcp;
47961ae08745Sheppo 	ldc_mtbl_t	*mtbl;
47971ae08745Sheppo 	ldc_memseg_t	*memseg;
47981ae08745Sheppo 	ldc_mte_t	tmp_mte;
47991ae08745Sheppo 	uint64_t	index, prev_index = 0;
48001ae08745Sheppo 	int64_t		cookie_idx;
48011ae08745Sheppo 	uintptr_t	raddr, ra_aligned;
48021ae08745Sheppo 	uint64_t	psize, poffset, v_offset;
48031ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code, pg_mask;
48041ae08745Sheppo 	pgcnt_t		npages;
48051ae08745Sheppo 	caddr_t		v_align, addr;
48064bac2208Snarayan 	int 		i, rv;
48071ae08745Sheppo 
48081ae08745Sheppo 	if (mhandle == NULL) {
48091ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
48101ae08745Sheppo 		    "ldc_mem_bind_handle: invalid memory handle\n");
48111ae08745Sheppo 		return (EINVAL);
48121ae08745Sheppo 	}
48131ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
48141ae08745Sheppo 	ldcp = mhdl->ldcp;
48151ae08745Sheppo 
48161ae08745Sheppo 	/* clear count */
48171ae08745Sheppo 	*ccount = 0;
48181ae08745Sheppo 
48191ae08745Sheppo 	mutex_enter(&mhdl->lock);
48201ae08745Sheppo 
48211ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->memseg != NULL) {
48221ae08745Sheppo 		DWARN(ldcp->id,
48231ae08745Sheppo 		    "ldc_mem_bind_handle: (0x%x) handle already bound\n",
48241ae08745Sheppo 		    mhandle);
48251ae08745Sheppo 		mutex_exit(&mhdl->lock);
48261ae08745Sheppo 		return (EINVAL);
48271ae08745Sheppo 	}
48281ae08745Sheppo 
48291ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
48301ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
48311ae08745Sheppo 		DWARN(ldcp->id,
48321ae08745Sheppo 		    "ldc_mem_bind_handle: addr/size is not 8-byte aligned\n");
48331ae08745Sheppo 		mutex_exit(&mhdl->lock);
48341ae08745Sheppo 		return (EINVAL);
48351ae08745Sheppo 	}
48361ae08745Sheppo 
48374bac2208Snarayan 	/*
48384bac2208Snarayan 	 * If this channel is binding a memory handle for the
48394bac2208Snarayan 	 * first time allocate it a memory map table and initialize it
48404bac2208Snarayan 	 */
48414bac2208Snarayan 	if ((mtbl = ldcp->mtbl) == NULL) {
48424bac2208Snarayan 
48434bac2208Snarayan 		mutex_enter(&ldcp->lock);
48444bac2208Snarayan 
48454bac2208Snarayan 		/* Allocate and initialize the map table structure */
48464bac2208Snarayan 		mtbl = kmem_zalloc(sizeof (ldc_mtbl_t), KM_SLEEP);
48474bac2208Snarayan 		mtbl->num_entries = mtbl->num_avail = ldc_maptable_entries;
48484bac2208Snarayan 		mtbl->size = ldc_maptable_entries * sizeof (ldc_mte_slot_t);
48494bac2208Snarayan 		mtbl->next_entry = NULL;
48503af08d82Slm66018 		mtbl->contigmem = B_TRUE;
48514bac2208Snarayan 
48524bac2208Snarayan 		/* Allocate the table itself */
48534bac2208Snarayan 		mtbl->table = (ldc_mte_slot_t *)
48544bac2208Snarayan 		    contig_mem_alloc_align(mtbl->size, MMU_PAGESIZE);
48554bac2208Snarayan 		if (mtbl->table == NULL) {
48563af08d82Slm66018 
48573af08d82Slm66018 			/* allocate a page of memory using kmem_alloc */
48583af08d82Slm66018 			mtbl->table = kmem_alloc(MMU_PAGESIZE, KM_SLEEP);
48593af08d82Slm66018 			mtbl->size = MMU_PAGESIZE;
48603af08d82Slm66018 			mtbl->contigmem = B_FALSE;
48613af08d82Slm66018 			mtbl->num_entries = mtbl->num_avail =
48623af08d82Slm66018 			    mtbl->size / sizeof (ldc_mte_slot_t);
48633af08d82Slm66018 			DWARN(ldcp->id,
48643af08d82Slm66018 			    "ldc_mem_bind_handle: (0x%llx) reduced tbl size "
48653af08d82Slm66018 			    "to %lx entries\n", ldcp->id, mtbl->num_entries);
48664bac2208Snarayan 		}
48674bac2208Snarayan 
48684bac2208Snarayan 		/* zero out the memory */
48694bac2208Snarayan 		bzero(mtbl->table, mtbl->size);
48704bac2208Snarayan 
48714bac2208Snarayan 		/* initialize the lock */
48724bac2208Snarayan 		mutex_init(&mtbl->lock, NULL, MUTEX_DRIVER, NULL);
48734bac2208Snarayan 
48744bac2208Snarayan 		/* register table for this channel */
48754bac2208Snarayan 		rv = hv_ldc_set_map_table(ldcp->id,
48764bac2208Snarayan 		    va_to_pa(mtbl->table), mtbl->num_entries);
48774bac2208Snarayan 		if (rv != 0) {
48784bac2208Snarayan 			cmn_err(CE_WARN,
48794bac2208Snarayan 			    "ldc_mem_bind_handle: (0x%lx) err %d mapping tbl",
48804bac2208Snarayan 			    ldcp->id, rv);
48813af08d82Slm66018 			if (mtbl->contigmem)
48824bac2208Snarayan 				contig_mem_free(mtbl->table, mtbl->size);
48833af08d82Slm66018 			else
48843af08d82Slm66018 				kmem_free(mtbl->table, mtbl->size);
48854bac2208Snarayan 			mutex_destroy(&mtbl->lock);
48864bac2208Snarayan 			kmem_free(mtbl, sizeof (ldc_mtbl_t));
48874bac2208Snarayan 			mutex_exit(&ldcp->lock);
48884bac2208Snarayan 			mutex_exit(&mhdl->lock);
48894bac2208Snarayan 			return (EIO);
48904bac2208Snarayan 		}
48914bac2208Snarayan 
48924bac2208Snarayan 		ldcp->mtbl = mtbl;
48934bac2208Snarayan 		mutex_exit(&ldcp->lock);
48944bac2208Snarayan 
48954bac2208Snarayan 		D1(ldcp->id,
48964bac2208Snarayan 		    "ldc_mem_bind_handle: (0x%llx) alloc'd map table 0x%llx\n",
48974bac2208Snarayan 		    ldcp->id, ldcp->mtbl->table);
48984bac2208Snarayan 	}
48994bac2208Snarayan 
49001ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
49011ae08745Sheppo 	pg_size = MMU_PAGESIZE;
49021ae08745Sheppo 	pg_size_code = page_szc(pg_size);
49031ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
49041ae08745Sheppo 	pg_mask = ~(pg_size - 1);
49051ae08745Sheppo 
49061ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle: (0x%llx) binding "
49071ae08745Sheppo 	    "va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
49081ae08745Sheppo 	    ldcp->id, vaddr, pg_size, pg_size_code, pg_shift);
49091ae08745Sheppo 
49101ae08745Sheppo 	/* aligned VA and its offset */
49111ae08745Sheppo 	v_align = (caddr_t)(((uintptr_t)vaddr) & ~(pg_size - 1));
49121ae08745Sheppo 	v_offset = ((uintptr_t)vaddr) & (pg_size - 1);
49131ae08745Sheppo 
49141ae08745Sheppo 	npages = (len+v_offset)/pg_size;
49151ae08745Sheppo 	npages = ((len+v_offset)%pg_size == 0) ? npages : npages+1;
49161ae08745Sheppo 
49171ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle: binding "
49181ae08745Sheppo 	    "(0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n",
49191ae08745Sheppo 	    ldcp->id, vaddr, v_align, v_offset, npages);
49201ae08745Sheppo 
49211ae08745Sheppo 	/* lock the memory table - exclusive access to channel */
49221ae08745Sheppo 	mutex_enter(&mtbl->lock);
49231ae08745Sheppo 
49241ae08745Sheppo 	if (npages > mtbl->num_avail) {
49253af08d82Slm66018 		D1(ldcp->id, "ldc_mem_bind_handle: (0x%llx) no table entries\n",
49261ae08745Sheppo 		    ldcp->id);
49271ae08745Sheppo 		mutex_exit(&mtbl->lock);
49281ae08745Sheppo 		mutex_exit(&mhdl->lock);
49291ae08745Sheppo 		return (ENOMEM);
49301ae08745Sheppo 	}
49311ae08745Sheppo 
49321ae08745Sheppo 	/* Allocate a memseg structure */
49334bac2208Snarayan 	memseg = mhdl->memseg =
49344bac2208Snarayan 	    kmem_cache_alloc(ldcssp->memseg_cache, KM_SLEEP);
49351ae08745Sheppo 
49361ae08745Sheppo 	/* Allocate memory to store all pages and cookies */
49371ae08745Sheppo 	memseg->pages = kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP);
49381ae08745Sheppo 	memseg->cookies =
49391ae08745Sheppo 	    kmem_zalloc((sizeof (ldc_mem_cookie_t) * npages), KM_SLEEP);
49401ae08745Sheppo 
49411ae08745Sheppo 	D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) processing 0x%llx pages\n",
49421ae08745Sheppo 	    ldcp->id, npages);
49431ae08745Sheppo 
49441ae08745Sheppo 	addr = v_align;
49451ae08745Sheppo 
49461ae08745Sheppo 	/*
49474bac2208Snarayan 	 * Check if direct shared memory map is enabled, if not change
49484bac2208Snarayan 	 * the mapping type to include SHADOW_MAP.
49494bac2208Snarayan 	 */
49504bac2208Snarayan 	if (ldc_shmem_enabled == 0)
49514bac2208Snarayan 		mtype = LDC_SHADOW_MAP;
49524bac2208Snarayan 
49534bac2208Snarayan 	/*
49541ae08745Sheppo 	 * Table slots are used in a round-robin manner. The algorithm permits
49551ae08745Sheppo 	 * inserting duplicate entries. Slots allocated earlier will typically
49561ae08745Sheppo 	 * get freed before we get back to reusing the slot.Inserting duplicate
49571ae08745Sheppo 	 * entries should be OK as we only lookup entries using the cookie addr
49581ae08745Sheppo 	 * i.e. tbl index, during export, unexport and copy operation.
49591ae08745Sheppo 	 *
49601ae08745Sheppo 	 * One implementation what was tried was to search for a duplicate
49611ae08745Sheppo 	 * page entry first and reuse it. The search overhead is very high and
49621ae08745Sheppo 	 * in the vnet case dropped the perf by almost half, 50 to 24 mbps.
49631ae08745Sheppo 	 * So it does make sense to avoid searching for duplicates.
49641ae08745Sheppo 	 *
49651ae08745Sheppo 	 * But during the process of searching for a free slot, if we find a
49661ae08745Sheppo 	 * duplicate entry we will go ahead and use it, and bump its use count.
49671ae08745Sheppo 	 */
49681ae08745Sheppo 
49691ae08745Sheppo 	/* index to start searching from */
49701ae08745Sheppo 	index = mtbl->next_entry;
49711ae08745Sheppo 	cookie_idx = -1;
49721ae08745Sheppo 
49731ae08745Sheppo 	tmp_mte.ll = 0;	/* initialise fields to 0 */
49741ae08745Sheppo 
49751ae08745Sheppo 	if (mtype & LDC_DIRECT_MAP) {
49761ae08745Sheppo 		tmp_mte.mte_r = (perm & LDC_MEM_R) ? 1 : 0;
49771ae08745Sheppo 		tmp_mte.mte_w = (perm & LDC_MEM_W) ? 1 : 0;
49781ae08745Sheppo 		tmp_mte.mte_x = (perm & LDC_MEM_X) ? 1 : 0;
49791ae08745Sheppo 	}
49801ae08745Sheppo 
49811ae08745Sheppo 	if (mtype & LDC_SHADOW_MAP) {
49821ae08745Sheppo 		tmp_mte.mte_cr = (perm & LDC_MEM_R) ? 1 : 0;
49831ae08745Sheppo 		tmp_mte.mte_cw = (perm & LDC_MEM_W) ? 1 : 0;
49841ae08745Sheppo 	}
49851ae08745Sheppo 
49861ae08745Sheppo 	if (mtype & LDC_IO_MAP) {
49871ae08745Sheppo 		tmp_mte.mte_ir = (perm & LDC_MEM_R) ? 1 : 0;
49881ae08745Sheppo 		tmp_mte.mte_iw = (perm & LDC_MEM_W) ? 1 : 0;
49891ae08745Sheppo 	}
49901ae08745Sheppo 
49911ae08745Sheppo 	D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll);
49921ae08745Sheppo 
49931ae08745Sheppo 	tmp_mte.mte_pgszc = pg_size_code;
49941ae08745Sheppo 
49951ae08745Sheppo 	/* initialize each mem table entry */
49961ae08745Sheppo 	for (i = 0; i < npages; i++) {
49971ae08745Sheppo 
49981ae08745Sheppo 		/* check if slot is available in the table */
49991ae08745Sheppo 		while (mtbl->table[index].entry.ll != 0) {
50001ae08745Sheppo 
50011ae08745Sheppo 			index = (index + 1) % mtbl->num_entries;
50021ae08745Sheppo 
50031ae08745Sheppo 			if (index == mtbl->next_entry) {
50041ae08745Sheppo 				/* we have looped around */
50051ae08745Sheppo 				DWARN(DBG_ALL_LDCS,
50061ae08745Sheppo 				    "ldc_mem_bind_handle: (0x%llx) cannot find "
50071ae08745Sheppo 				    "entry\n", ldcp->id);
50081ae08745Sheppo 				*ccount = 0;
50091ae08745Sheppo 
50101ae08745Sheppo 				/* NOTE: free memory, remove previous entries */
50111ae08745Sheppo 				/* this shouldnt happen as num_avail was ok */
50121ae08745Sheppo 
50131ae08745Sheppo 				mutex_exit(&mtbl->lock);
50141ae08745Sheppo 				mutex_exit(&mhdl->lock);
50151ae08745Sheppo 				return (ENOMEM);
50161ae08745Sheppo 			}
50171ae08745Sheppo 		}
50181ae08745Sheppo 
50191ae08745Sheppo 		/* get the real address */
50201ae08745Sheppo 		raddr = va_to_pa((void *)addr);
50211ae08745Sheppo 		ra_aligned = ((uintptr_t)raddr & pg_mask);
50221ae08745Sheppo 
50231ae08745Sheppo 		/* build the mte */
50241ae08745Sheppo 		tmp_mte.mte_rpfn = ra_aligned >> pg_shift;
50251ae08745Sheppo 
50261ae08745Sheppo 		D1(ldcp->id, "ldc_mem_bind_handle mte=0x%llx\n", tmp_mte.ll);
50271ae08745Sheppo 
50281ae08745Sheppo 		/* update entry in table */
50291ae08745Sheppo 		mtbl->table[index].entry = tmp_mte;
50301ae08745Sheppo 
50311ae08745Sheppo 		D2(ldcp->id, "ldc_mem_bind_handle: (0x%llx) stored MTE 0x%llx"
50321ae08745Sheppo 		    " into loc 0x%llx\n", ldcp->id, tmp_mte.ll, index);
50331ae08745Sheppo 
50341ae08745Sheppo 		/* calculate the size and offset for this export range */
50351ae08745Sheppo 		if (i == 0) {
50361ae08745Sheppo 			/* first page */
50371ae08745Sheppo 			psize = min((pg_size - v_offset), len);
50381ae08745Sheppo 			poffset = v_offset;
50391ae08745Sheppo 
50401ae08745Sheppo 		} else if (i == (npages - 1)) {
50411ae08745Sheppo 			/* last page */
50421ae08745Sheppo 			psize =	(((uintptr_t)(vaddr + len)) &
50431ae08745Sheppo 			    ((uint64_t)(pg_size-1)));
50441ae08745Sheppo 			if (psize == 0)
50451ae08745Sheppo 				psize = pg_size;
50461ae08745Sheppo 			poffset = 0;
50471ae08745Sheppo 
50481ae08745Sheppo 		} else {
50491ae08745Sheppo 			/* middle pages */
50501ae08745Sheppo 			psize = pg_size;
50511ae08745Sheppo 			poffset = 0;
50521ae08745Sheppo 		}
50531ae08745Sheppo 
50541ae08745Sheppo 		/* store entry for this page */
50551ae08745Sheppo 		memseg->pages[i].index = index;
50561ae08745Sheppo 		memseg->pages[i].raddr = raddr;
50571ae08745Sheppo 		memseg->pages[i].offset = poffset;
50581ae08745Sheppo 		memseg->pages[i].size = psize;
50591ae08745Sheppo 		memseg->pages[i].mte = &(mtbl->table[index]);
50601ae08745Sheppo 
50611ae08745Sheppo 		/* create the cookie */
50621ae08745Sheppo 		if (i == 0 || (index != prev_index + 1)) {
50631ae08745Sheppo 			cookie_idx++;
50641ae08745Sheppo 			memseg->cookies[cookie_idx].addr =
50651ae08745Sheppo 			    IDX2COOKIE(index, pg_size_code, pg_shift);
50661ae08745Sheppo 			memseg->cookies[cookie_idx].addr |= poffset;
50671ae08745Sheppo 			memseg->cookies[cookie_idx].size = psize;
50681ae08745Sheppo 
50691ae08745Sheppo 		} else {
50701ae08745Sheppo 			memseg->cookies[cookie_idx].size += psize;
50711ae08745Sheppo 		}
50721ae08745Sheppo 
50731ae08745Sheppo 		D1(ldcp->id, "ldc_mem_bind_handle: bound "
50741ae08745Sheppo 		    "(0x%llx) va=0x%llx, idx=0x%llx, "
50751ae08745Sheppo 		    "ra=0x%llx(sz=0x%x,off=0x%x)\n",
50761ae08745Sheppo 		    ldcp->id, addr, index, raddr, psize, poffset);
50771ae08745Sheppo 
50781ae08745Sheppo 		/* decrement number of available entries */
50791ae08745Sheppo 		mtbl->num_avail--;
50801ae08745Sheppo 
50811ae08745Sheppo 		/* increment va by page size */
50821ae08745Sheppo 		addr += pg_size;
50831ae08745Sheppo 
50841ae08745Sheppo 		/* increment index */
50851ae08745Sheppo 		prev_index = index;
50861ae08745Sheppo 		index = (index + 1) % mtbl->num_entries;
50871ae08745Sheppo 
50881ae08745Sheppo 		/* save the next slot */
50891ae08745Sheppo 		mtbl->next_entry = index;
50901ae08745Sheppo 	}
50911ae08745Sheppo 
50921ae08745Sheppo 	mutex_exit(&mtbl->lock);
50931ae08745Sheppo 
50941ae08745Sheppo 	/* memory handle = bound */
50951ae08745Sheppo 	mhdl->mtype = mtype;
50961ae08745Sheppo 	mhdl->perm = perm;
50971ae08745Sheppo 	mhdl->status = LDC_BOUND;
50981ae08745Sheppo 
50991ae08745Sheppo 	/* update memseg_t */
51001ae08745Sheppo 	memseg->vaddr = vaddr;
51011ae08745Sheppo 	memseg->raddr = memseg->pages[0].raddr;
51021ae08745Sheppo 	memseg->size = len;
51031ae08745Sheppo 	memseg->npages = npages;
51041ae08745Sheppo 	memseg->ncookies = cookie_idx + 1;
51051ae08745Sheppo 	memseg->next_cookie = (memseg->ncookies > 1) ? 1 : 0;
51061ae08745Sheppo 
51071ae08745Sheppo 	/* return count and first cookie */
51081ae08745Sheppo 	*ccount = memseg->ncookies;
51091ae08745Sheppo 	cookie->addr = memseg->cookies[0].addr;
51101ae08745Sheppo 	cookie->size = memseg->cookies[0].size;
51111ae08745Sheppo 
51121ae08745Sheppo 	D1(ldcp->id,
51131ae08745Sheppo 	    "ldc_mem_bind_handle: (0x%llx) bound 0x%llx, va=0x%llx, "
51141ae08745Sheppo 	    "pgs=0x%llx cookies=0x%llx\n",
51151ae08745Sheppo 	    ldcp->id, mhdl, vaddr, npages, memseg->ncookies);
51161ae08745Sheppo 
51171ae08745Sheppo 	mutex_exit(&mhdl->lock);
51181ae08745Sheppo 	return (0);
51191ae08745Sheppo }
51201ae08745Sheppo 
51211ae08745Sheppo /*
51221ae08745Sheppo  * Return the next cookie associated with the specified memory handle
51231ae08745Sheppo  */
51241ae08745Sheppo int
51251ae08745Sheppo ldc_mem_nextcookie(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie)
51261ae08745Sheppo {
51271ae08745Sheppo 	ldc_mhdl_t	*mhdl;
51281ae08745Sheppo 	ldc_chan_t 	*ldcp;
51291ae08745Sheppo 	ldc_memseg_t	*memseg;
51301ae08745Sheppo 
51311ae08745Sheppo 	if (mhandle == NULL) {
51321ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51331ae08745Sheppo 		    "ldc_mem_nextcookie: invalid memory handle\n");
51341ae08745Sheppo 		return (EINVAL);
51351ae08745Sheppo 	}
51361ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
51371ae08745Sheppo 
51381ae08745Sheppo 	mutex_enter(&mhdl->lock);
51391ae08745Sheppo 
51401ae08745Sheppo 	ldcp = mhdl->ldcp;
51411ae08745Sheppo 	memseg = mhdl->memseg;
51421ae08745Sheppo 
51431ae08745Sheppo 	if (cookie == 0) {
51441ae08745Sheppo 		DWARN(ldcp->id,
51451ae08745Sheppo 		    "ldc_mem_nextcookie:(0x%llx) invalid cookie arg\n",
51461ae08745Sheppo 		    ldcp->id);
51471ae08745Sheppo 		mutex_exit(&mhdl->lock);
51481ae08745Sheppo 		return (EINVAL);
51491ae08745Sheppo 	}
51501ae08745Sheppo 
51511ae08745Sheppo 	if (memseg->next_cookie != 0) {
51521ae08745Sheppo 		cookie->addr = memseg->cookies[memseg->next_cookie].addr;
51531ae08745Sheppo 		cookie->size = memseg->cookies[memseg->next_cookie].size;
51541ae08745Sheppo 		memseg->next_cookie++;
51551ae08745Sheppo 		if (memseg->next_cookie == memseg->ncookies)
51561ae08745Sheppo 			memseg->next_cookie = 0;
51571ae08745Sheppo 
51581ae08745Sheppo 	} else {
51591ae08745Sheppo 		DWARN(ldcp->id,
51601ae08745Sheppo 		    "ldc_mem_nextcookie:(0x%llx) no more cookies\n", ldcp->id);
51611ae08745Sheppo 		cookie->addr = 0;
51621ae08745Sheppo 		cookie->size = 0;
51631ae08745Sheppo 		mutex_exit(&mhdl->lock);
51641ae08745Sheppo 		return (EINVAL);
51651ae08745Sheppo 	}
51661ae08745Sheppo 
51671ae08745Sheppo 	D1(ldcp->id,
51681ae08745Sheppo 	    "ldc_mem_nextcookie: (0x%llx) cookie addr=0x%llx,sz=0x%llx\n",
51691ae08745Sheppo 	    ldcp->id, cookie->addr, cookie->size);
51701ae08745Sheppo 
51711ae08745Sheppo 	mutex_exit(&mhdl->lock);
51721ae08745Sheppo 	return (0);
51731ae08745Sheppo }
51741ae08745Sheppo 
51751ae08745Sheppo /*
51761ae08745Sheppo  * Unbind the virtual memory region associated with the specified
51771ae08745Sheppo  * memory handle. Allassociated cookies are freed and the corresponding
51781ae08745Sheppo  * RA space is no longer exported.
51791ae08745Sheppo  */
51801ae08745Sheppo int
51811ae08745Sheppo ldc_mem_unbind_handle(ldc_mem_handle_t mhandle)
51821ae08745Sheppo {
51831ae08745Sheppo 	ldc_mhdl_t	*mhdl;
51841ae08745Sheppo 	ldc_chan_t 	*ldcp;
51851ae08745Sheppo 	ldc_mtbl_t	*mtbl;
51861ae08745Sheppo 	ldc_memseg_t	*memseg;
51874bac2208Snarayan 	uint64_t	cookie_addr;
51884bac2208Snarayan 	uint64_t	pg_shift, pg_size_code;
51894bac2208Snarayan 	int		i, rv;
51901ae08745Sheppo 
51911ae08745Sheppo 	if (mhandle == NULL) {
51921ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
51931ae08745Sheppo 		    "ldc_mem_unbind_handle: invalid memory handle\n");
51941ae08745Sheppo 		return (EINVAL);
51951ae08745Sheppo 	}
51961ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
51971ae08745Sheppo 
51981ae08745Sheppo 	mutex_enter(&mhdl->lock);
51991ae08745Sheppo 
52001ae08745Sheppo 	if (mhdl->status == LDC_UNBOUND) {
52011ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
52021ae08745Sheppo 		    "ldc_mem_unbind_handle: (0x%x) handle is not bound\n",
52031ae08745Sheppo 		    mhandle);
52041ae08745Sheppo 		mutex_exit(&mhdl->lock);
52051ae08745Sheppo 		return (EINVAL);
52061ae08745Sheppo 	}
52071ae08745Sheppo 
52081ae08745Sheppo 	ldcp = mhdl->ldcp;
52091ae08745Sheppo 	mtbl = ldcp->mtbl;
52101ae08745Sheppo 
52111ae08745Sheppo 	memseg = mhdl->memseg;
52121ae08745Sheppo 
52131ae08745Sheppo 	/* lock the memory table - exclusive access to channel */
52141ae08745Sheppo 	mutex_enter(&mtbl->lock);
52151ae08745Sheppo 
52161ae08745Sheppo 	/* undo the pages exported */
52171ae08745Sheppo 	for (i = 0; i < memseg->npages; i++) {
52181ae08745Sheppo 
52194bac2208Snarayan 		/* check for mapped pages, revocation cookie != 0 */
52201ae08745Sheppo 		if (memseg->pages[i].mte->cookie) {
52214bac2208Snarayan 
52224bac2208Snarayan 			pg_size_code = page_szc(memseg->pages[i].size);
52234bac2208Snarayan 			pg_shift = page_get_shift(memseg->pages[i].size);
52244bac2208Snarayan 			cookie_addr = IDX2COOKIE(memseg->pages[i].index,
52254bac2208Snarayan 			    pg_size_code, pg_shift);
52264bac2208Snarayan 
52274bac2208Snarayan 			D1(ldcp->id, "ldc_mem_unbind_handle: (0x%llx) revoke "
52284bac2208Snarayan 			    "cookie 0x%llx, rcookie 0x%llx\n", ldcp->id,
52294bac2208Snarayan 			    cookie_addr, memseg->pages[i].mte->cookie);
52304bac2208Snarayan 			rv = hv_ldc_revoke(ldcp->id, cookie_addr,
52314bac2208Snarayan 			    memseg->pages[i].mte->cookie);
52324bac2208Snarayan 			if (rv) {
52334bac2208Snarayan 				DWARN(ldcp->id,
52344bac2208Snarayan 				    "ldc_mem_unbind_handle: (0x%llx) cannot "
52354bac2208Snarayan 				    "revoke mapping, cookie %llx\n", ldcp->id,
52364bac2208Snarayan 				    cookie_addr);
52374bac2208Snarayan 			}
52381ae08745Sheppo 		}
52391ae08745Sheppo 
52401ae08745Sheppo 		/* clear the entry from the table */
52411ae08745Sheppo 		memseg->pages[i].mte->entry.ll = 0;
52421ae08745Sheppo 		mtbl->num_avail++;
52431ae08745Sheppo 	}
52441ae08745Sheppo 	mutex_exit(&mtbl->lock);
52451ae08745Sheppo 
52461ae08745Sheppo 	/* free the allocated memseg and page structures */
52471ae08745Sheppo 	kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages));
52481ae08745Sheppo 	kmem_free(memseg->cookies,
52491ae08745Sheppo 	    (sizeof (ldc_mem_cookie_t) * memseg->npages));
52504bac2208Snarayan 	kmem_cache_free(ldcssp->memseg_cache, memseg);
52511ae08745Sheppo 
52521ae08745Sheppo 	/* uninitialize the memory handle */
52531ae08745Sheppo 	mhdl->memseg = NULL;
52541ae08745Sheppo 	mhdl->status = LDC_UNBOUND;
52551ae08745Sheppo 
52561ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unbind_handle: (0x%llx) unbound handle 0x%llx\n",
52571ae08745Sheppo 	    ldcp->id, mhdl);
52581ae08745Sheppo 
52591ae08745Sheppo 	mutex_exit(&mhdl->lock);
52601ae08745Sheppo 	return (0);
52611ae08745Sheppo }
52621ae08745Sheppo 
52631ae08745Sheppo /*
52641ae08745Sheppo  * Get information about the dring. The base address of the descriptor
52651ae08745Sheppo  * ring along with the type and permission are returned back.
52661ae08745Sheppo  */
52671ae08745Sheppo int
52681ae08745Sheppo ldc_mem_info(ldc_mem_handle_t mhandle, ldc_mem_info_t *minfo)
52691ae08745Sheppo {
52701ae08745Sheppo 	ldc_mhdl_t	*mhdl;
52711ae08745Sheppo 
52721ae08745Sheppo 	if (mhandle == NULL) {
52731ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid memory handle\n");
52741ae08745Sheppo 		return (EINVAL);
52751ae08745Sheppo 	}
52761ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
52771ae08745Sheppo 
52781ae08745Sheppo 	if (minfo == NULL) {
52791ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_info: invalid args\n");
52801ae08745Sheppo 		return (EINVAL);
52811ae08745Sheppo 	}
52821ae08745Sheppo 
52831ae08745Sheppo 	mutex_enter(&mhdl->lock);
52841ae08745Sheppo 
52851ae08745Sheppo 	minfo->status = mhdl->status;
52861ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED) {
52871ae08745Sheppo 		minfo->vaddr = mhdl->memseg->vaddr;
52881ae08745Sheppo 		minfo->raddr = mhdl->memseg->raddr;
52891ae08745Sheppo 		minfo->mtype = mhdl->mtype;
52901ae08745Sheppo 		minfo->perm = mhdl->perm;
52911ae08745Sheppo 	}
52921ae08745Sheppo 	mutex_exit(&mhdl->lock);
52931ae08745Sheppo 
52941ae08745Sheppo 	return (0);
52951ae08745Sheppo }
52961ae08745Sheppo 
52971ae08745Sheppo /*
52981ae08745Sheppo  * Copy data either from or to the client specified virtual address
52991ae08745Sheppo  * space to or from the exported memory associated with the cookies.
53001ae08745Sheppo  * The direction argument determines whether the data is read from or
53011ae08745Sheppo  * written to exported memory.
53021ae08745Sheppo  */
53031ae08745Sheppo int
53041ae08745Sheppo ldc_mem_copy(ldc_handle_t handle, caddr_t vaddr, uint64_t off, size_t *size,
53051ae08745Sheppo     ldc_mem_cookie_t *cookies, uint32_t ccount, uint8_t direction)
53061ae08745Sheppo {
53071ae08745Sheppo 	ldc_chan_t 	*ldcp;
53081ae08745Sheppo 	uint64_t	local_voff, local_valign;
53091ae08745Sheppo 	uint64_t	cookie_addr, cookie_size;
53101ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code;
53111ae08745Sheppo 	uint64_t 	export_caddr, export_poff, export_psize, export_size;
53121ae08745Sheppo 	uint64_t	local_ra, local_poff, local_psize;
53131ae08745Sheppo 	uint64_t	copy_size, copied_len = 0, total_bal = 0, idx = 0;
53141ae08745Sheppo 	pgcnt_t		npages;
53151ae08745Sheppo 	size_t		len = *size;
53161ae08745Sheppo 	int 		i, rv = 0;
53171ae08745Sheppo 
53183af08d82Slm66018 	uint64_t	chid;
53193af08d82Slm66018 
53201ae08745Sheppo 	if (handle == NULL) {
53211ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_copy: invalid channel handle\n");
53221ae08745Sheppo 		return (EINVAL);
53231ae08745Sheppo 	}
53241ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
53253af08d82Slm66018 	chid = ldcp->id;
53261ae08745Sheppo 
53271ae08745Sheppo 	/* check to see if channel is UP */
53281ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
53293af08d82Slm66018 		DWARN(chid, "ldc_mem_copy: (0x%llx) channel is not UP\n",
53303af08d82Slm66018 		    chid);
53313af08d82Slm66018 		return (ECONNRESET);
53321ae08745Sheppo 	}
53331ae08745Sheppo 
53341ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
53351ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
53363af08d82Slm66018 		DWARN(chid,
53371ae08745Sheppo 		    "ldc_mem_copy: addr/sz is not 8-byte aligned\n");
53381ae08745Sheppo 		return (EINVAL);
53391ae08745Sheppo 	}
53401ae08745Sheppo 
53411ae08745Sheppo 	/* Find the size of the exported memory */
53421ae08745Sheppo 	export_size = 0;
53431ae08745Sheppo 	for (i = 0; i < ccount; i++)
53441ae08745Sheppo 		export_size += cookies[i].size;
53451ae08745Sheppo 
53461ae08745Sheppo 	/* check to see if offset is valid */
53471ae08745Sheppo 	if (off > export_size) {
53483af08d82Slm66018 		DWARN(chid,
53491ae08745Sheppo 		    "ldc_mem_copy: (0x%llx) start offset > export mem size\n",
53503af08d82Slm66018 		    chid);
53511ae08745Sheppo 		return (EINVAL);
53521ae08745Sheppo 	}
53531ae08745Sheppo 
53541ae08745Sheppo 	/*
53551ae08745Sheppo 	 * Check to see if the export size is smaller than the size we
53561ae08745Sheppo 	 * are requesting to copy - if so flag an error
53571ae08745Sheppo 	 */
53581ae08745Sheppo 	if ((export_size - off) < *size) {
53593af08d82Slm66018 		DWARN(chid,
53601ae08745Sheppo 		    "ldc_mem_copy: (0x%llx) copy size > export mem size\n",
53613af08d82Slm66018 		    chid);
53621ae08745Sheppo 		return (EINVAL);
53631ae08745Sheppo 	}
53641ae08745Sheppo 
53651ae08745Sheppo 	total_bal = min(export_size, *size);
53661ae08745Sheppo 
53671ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
53681ae08745Sheppo 	pg_size = MMU_PAGESIZE;
53691ae08745Sheppo 	pg_size_code = page_szc(pg_size);
53701ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
53711ae08745Sheppo 
53723af08d82Slm66018 	D1(chid, "ldc_mem_copy: copying data "
53731ae08745Sheppo 	    "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
53743af08d82Slm66018 	    chid, vaddr, pg_size, pg_size_code, pg_shift);
53751ae08745Sheppo 
53761ae08745Sheppo 	/* aligned VA and its offset */
53771ae08745Sheppo 	local_valign = (((uintptr_t)vaddr) & ~(pg_size - 1));
53781ae08745Sheppo 	local_voff = ((uintptr_t)vaddr) & (pg_size - 1);
53791ae08745Sheppo 
53801ae08745Sheppo 	npages = (len+local_voff)/pg_size;
53811ae08745Sheppo 	npages = ((len+local_voff)%pg_size == 0) ? npages : npages+1;
53821ae08745Sheppo 
53833af08d82Slm66018 	D1(chid,
53841ae08745Sheppo 	    "ldc_mem_copy: (0x%llx) v=0x%llx,val=0x%llx,off=0x%x,pgs=0x%x\n",
53853af08d82Slm66018 	    chid, vaddr, local_valign, local_voff, npages);
53861ae08745Sheppo 
53871ae08745Sheppo 	local_ra = va_to_pa((void *)local_valign);
53881ae08745Sheppo 	local_poff = local_voff;
53891ae08745Sheppo 	local_psize = min(len, (pg_size - local_voff));
53901ae08745Sheppo 
53911ae08745Sheppo 	len -= local_psize;
53921ae08745Sheppo 
53931ae08745Sheppo 	/*
53941ae08745Sheppo 	 * find the first cookie in the list of cookies
53951ae08745Sheppo 	 * if the offset passed in is not zero
53961ae08745Sheppo 	 */
53971ae08745Sheppo 	for (idx = 0; idx < ccount; idx++) {
53981ae08745Sheppo 		cookie_size = cookies[idx].size;
53991ae08745Sheppo 		if (off < cookie_size)
54001ae08745Sheppo 			break;
54011ae08745Sheppo 		off -= cookie_size;
54021ae08745Sheppo 	}
54031ae08745Sheppo 
54041ae08745Sheppo 	cookie_addr = cookies[idx].addr + off;
54051ae08745Sheppo 	cookie_size = cookies[idx].size - off;
54061ae08745Sheppo 
54071ae08745Sheppo 	export_caddr = cookie_addr & ~(pg_size - 1);
54081ae08745Sheppo 	export_poff = cookie_addr & (pg_size - 1);
54091ae08745Sheppo 	export_psize = min(cookie_size, (pg_size - export_poff));
54101ae08745Sheppo 
54111ae08745Sheppo 	for (;;) {
54121ae08745Sheppo 
54131ae08745Sheppo 		copy_size = min(export_psize, local_psize);
54141ae08745Sheppo 
54153af08d82Slm66018 		D1(chid,
54161ae08745Sheppo 		    "ldc_mem_copy:(0x%llx) dir=0x%x, caddr=0x%llx,"
54171ae08745Sheppo 		    " loc_ra=0x%llx, exp_poff=0x%llx, loc_poff=0x%llx,"
54181ae08745Sheppo 		    " exp_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
54191ae08745Sheppo 		    " total_bal=0x%llx\n",
54203af08d82Slm66018 		    chid, direction, export_caddr, local_ra, export_poff,
54211ae08745Sheppo 		    local_poff, export_psize, local_psize, copy_size,
54221ae08745Sheppo 		    total_bal);
54231ae08745Sheppo 
54243af08d82Slm66018 		rv = hv_ldc_copy(chid, direction,
54251ae08745Sheppo 		    (export_caddr + export_poff), (local_ra + local_poff),
54261ae08745Sheppo 		    copy_size, &copied_len);
54271ae08745Sheppo 
54281ae08745Sheppo 		if (rv != 0) {
54293af08d82Slm66018 			int 		error = EIO;
54303af08d82Slm66018 			uint64_t	rx_hd, rx_tl;
54313af08d82Slm66018 
54323af08d82Slm66018 			DWARN(chid,
54333af08d82Slm66018 			    "ldc_mem_copy: (0x%llx) err %d during copy\n",
54343af08d82Slm66018 			    (unsigned long long)chid, rv);
54353af08d82Slm66018 			DWARN(chid,
54363af08d82Slm66018 			    "ldc_mem_copy: (0x%llx) dir=0x%x, caddr=0x%lx, "
54374bac2208Snarayan 			    "loc_ra=0x%lx, exp_poff=0x%lx, loc_poff=0x%lx,"
54384bac2208Snarayan 			    " exp_psz=0x%lx, loc_psz=0x%lx, copy_sz=0x%lx,"
54394bac2208Snarayan 			    " copied_len=0x%lx, total_bal=0x%lx\n",
54403af08d82Slm66018 			    chid, direction, export_caddr, local_ra,
54411ae08745Sheppo 			    export_poff, local_poff, export_psize, local_psize,
54421ae08745Sheppo 			    copy_size, copied_len, total_bal);
54431ae08745Sheppo 
54441ae08745Sheppo 			*size = *size - total_bal;
54453af08d82Slm66018 
54463af08d82Slm66018 			/*
54473af08d82Slm66018 			 * check if reason for copy error was due to
54483af08d82Slm66018 			 * a channel reset. we need to grab the lock
54493af08d82Slm66018 			 * just in case we have to do a reset.
54503af08d82Slm66018 			 */
54513af08d82Slm66018 			mutex_enter(&ldcp->lock);
54523af08d82Slm66018 			mutex_enter(&ldcp->tx_lock);
54533af08d82Slm66018 
54543af08d82Slm66018 			rv = hv_ldc_rx_get_state(ldcp->id,
54553af08d82Slm66018 			    &rx_hd, &rx_tl, &(ldcp->link_state));
54563af08d82Slm66018 			if (ldcp->link_state == LDC_CHANNEL_DOWN ||
54573af08d82Slm66018 			    ldcp->link_state == LDC_CHANNEL_RESET) {
54583af08d82Slm66018 				i_ldc_reset(ldcp, B_FALSE);
54593af08d82Slm66018 				error = ECONNRESET;
54603af08d82Slm66018 			}
54613af08d82Slm66018 
54623af08d82Slm66018 			mutex_exit(&ldcp->tx_lock);
54631ae08745Sheppo 			mutex_exit(&ldcp->lock);
54643af08d82Slm66018 
54653af08d82Slm66018 			return (error);
54661ae08745Sheppo 		}
54671ae08745Sheppo 
54681ae08745Sheppo 		ASSERT(copied_len <= copy_size);
54691ae08745Sheppo 
54703af08d82Slm66018 		D2(chid, "ldc_mem_copy: copied=0x%llx\n", copied_len);
54711ae08745Sheppo 		export_poff += copied_len;
54721ae08745Sheppo 		local_poff += copied_len;
54731ae08745Sheppo 		export_psize -= copied_len;
54741ae08745Sheppo 		local_psize -= copied_len;
54751ae08745Sheppo 		cookie_size -= copied_len;
54761ae08745Sheppo 
54771ae08745Sheppo 		total_bal -= copied_len;
54781ae08745Sheppo 
54791ae08745Sheppo 		if (copy_size != copied_len)
54801ae08745Sheppo 			continue;
54811ae08745Sheppo 
54821ae08745Sheppo 		if (export_psize == 0 && total_bal != 0) {
54831ae08745Sheppo 
54841ae08745Sheppo 			if (cookie_size == 0) {
54851ae08745Sheppo 				idx++;
54861ae08745Sheppo 				cookie_addr = cookies[idx].addr;
54871ae08745Sheppo 				cookie_size = cookies[idx].size;
54881ae08745Sheppo 
54891ae08745Sheppo 				export_caddr = cookie_addr & ~(pg_size - 1);
54901ae08745Sheppo 				export_poff = cookie_addr & (pg_size - 1);
54911ae08745Sheppo 				export_psize =
54921ae08745Sheppo 				    min(cookie_size, (pg_size-export_poff));
54931ae08745Sheppo 			} else {
54941ae08745Sheppo 				export_caddr += pg_size;
54951ae08745Sheppo 				export_poff = 0;
54961ae08745Sheppo 				export_psize = min(cookie_size, pg_size);
54971ae08745Sheppo 			}
54981ae08745Sheppo 		}
54991ae08745Sheppo 
55001ae08745Sheppo 		if (local_psize == 0 && total_bal != 0) {
55011ae08745Sheppo 			local_valign += pg_size;
55021ae08745Sheppo 			local_ra = va_to_pa((void *)local_valign);
55031ae08745Sheppo 			local_poff = 0;
55041ae08745Sheppo 			local_psize = min(pg_size, len);
55051ae08745Sheppo 			len -= local_psize;
55061ae08745Sheppo 		}
55071ae08745Sheppo 
55081ae08745Sheppo 		/* check if we are all done */
55091ae08745Sheppo 		if (total_bal == 0)
55101ae08745Sheppo 			break;
55111ae08745Sheppo 	}
55121ae08745Sheppo 
55131ae08745Sheppo 
55143af08d82Slm66018 	D1(chid,
55151ae08745Sheppo 	    "ldc_mem_copy: (0x%llx) done copying sz=0x%llx\n",
55163af08d82Slm66018 	    chid, *size);
55171ae08745Sheppo 
55181ae08745Sheppo 	return (0);
55191ae08745Sheppo }
55201ae08745Sheppo 
55211ae08745Sheppo /*
55221ae08745Sheppo  * Copy data either from or to the client specified virtual address
55231ae08745Sheppo  * space to or from HV physical memory.
55241ae08745Sheppo  *
55251ae08745Sheppo  * The direction argument determines whether the data is read from or
55261ae08745Sheppo  * written to HV memory. direction values are LDC_COPY_IN/OUT similar
55271ae08745Sheppo  * to the ldc_mem_copy interface
55281ae08745Sheppo  */
55291ae08745Sheppo int
55303af08d82Slm66018 ldc_mem_rdwr_cookie(ldc_handle_t handle, caddr_t vaddr, size_t *size,
55311ae08745Sheppo     caddr_t paddr, uint8_t direction)
55321ae08745Sheppo {
55331ae08745Sheppo 	ldc_chan_t 	*ldcp;
55341ae08745Sheppo 	uint64_t	local_voff, local_valign;
55351ae08745Sheppo 	uint64_t	pg_shift, pg_size, pg_size_code;
55361ae08745Sheppo 	uint64_t 	target_pa, target_poff, target_psize, target_size;
55371ae08745Sheppo 	uint64_t	local_ra, local_poff, local_psize;
55381ae08745Sheppo 	uint64_t	copy_size, copied_len = 0;
55391ae08745Sheppo 	pgcnt_t		npages;
55401ae08745Sheppo 	size_t		len = *size;
55411ae08745Sheppo 	int 		rv = 0;
55421ae08745Sheppo 
55431ae08745Sheppo 	if (handle == NULL) {
55441ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
55453af08d82Slm66018 		    "ldc_mem_rdwr_cookie: invalid channel handle\n");
55461ae08745Sheppo 		return (EINVAL);
55471ae08745Sheppo 	}
55481ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
55491ae08745Sheppo 
55501ae08745Sheppo 	mutex_enter(&ldcp->lock);
55511ae08745Sheppo 
55521ae08745Sheppo 	/* check to see if channel is UP */
55531ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
55541ae08745Sheppo 		DWARN(ldcp->id,
55553af08d82Slm66018 		    "ldc_mem_rdwr_cookie: (0x%llx) channel is not UP\n",
55561ae08745Sheppo 		    ldcp->id);
55571ae08745Sheppo 		mutex_exit(&ldcp->lock);
55583af08d82Slm66018 		return (ECONNRESET);
55591ae08745Sheppo 	}
55601ae08745Sheppo 
55611ae08745Sheppo 	/* Force address and size to be 8-byte aligned */
55621ae08745Sheppo 	if ((((uintptr_t)vaddr | len) & 0x7) != 0) {
55631ae08745Sheppo 		DWARN(ldcp->id,
55643af08d82Slm66018 		    "ldc_mem_rdwr_cookie: addr/size is not 8-byte aligned\n");
55651ae08745Sheppo 		mutex_exit(&ldcp->lock);
55661ae08745Sheppo 		return (EINVAL);
55671ae08745Sheppo 	}
55681ae08745Sheppo 
55691ae08745Sheppo 	target_size = *size;
55701ae08745Sheppo 
55711ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
55721ae08745Sheppo 	pg_size = MMU_PAGESIZE;
55731ae08745Sheppo 	pg_size_code = page_szc(pg_size);
55741ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
55751ae08745Sheppo 
55763af08d82Slm66018 	D1(ldcp->id, "ldc_mem_rdwr_cookie: copying data "
55771ae08745Sheppo 	    "(0x%llx) va 0x%llx pgsz=0x%llx, pgszc=0x%llx, pg_shift=0x%llx\n",
55781ae08745Sheppo 	    ldcp->id, vaddr, pg_size, pg_size_code, pg_shift);
55791ae08745Sheppo 
55801ae08745Sheppo 	/* aligned VA and its offset */
55811ae08745Sheppo 	local_valign = ((uintptr_t)vaddr) & ~(pg_size - 1);
55821ae08745Sheppo 	local_voff = ((uintptr_t)vaddr) & (pg_size - 1);
55831ae08745Sheppo 
55841ae08745Sheppo 	npages = (len + local_voff) / pg_size;
55851ae08745Sheppo 	npages = ((len + local_voff) % pg_size == 0) ? npages : npages+1;
55861ae08745Sheppo 
55873af08d82Slm66018 	D1(ldcp->id, "ldc_mem_rdwr_cookie: (0x%llx) v=0x%llx, "
55883af08d82Slm66018 	    "val=0x%llx,off=0x%x,pgs=0x%x\n",
55891ae08745Sheppo 	    ldcp->id, vaddr, local_valign, local_voff, npages);
55901ae08745Sheppo 
55911ae08745Sheppo 	local_ra = va_to_pa((void *)local_valign);
55921ae08745Sheppo 	local_poff = local_voff;
55931ae08745Sheppo 	local_psize = min(len, (pg_size - local_voff));
55941ae08745Sheppo 
55951ae08745Sheppo 	len -= local_psize;
55961ae08745Sheppo 
55971ae08745Sheppo 	target_pa = ((uintptr_t)paddr) & ~(pg_size - 1);
55981ae08745Sheppo 	target_poff = ((uintptr_t)paddr) & (pg_size - 1);
55991ae08745Sheppo 	target_psize = pg_size - target_poff;
56001ae08745Sheppo 
56011ae08745Sheppo 	for (;;) {
56021ae08745Sheppo 
56031ae08745Sheppo 		copy_size = min(target_psize, local_psize);
56041ae08745Sheppo 
56051ae08745Sheppo 		D1(ldcp->id,
56063af08d82Slm66018 		    "ldc_mem_rdwr_cookie: (0x%llx) dir=0x%x, tar_pa=0x%llx,"
56071ae08745Sheppo 		    " loc_ra=0x%llx, tar_poff=0x%llx, loc_poff=0x%llx,"
56081ae08745Sheppo 		    " tar_psz=0x%llx, loc_psz=0x%llx, copy_sz=0x%llx,"
56091ae08745Sheppo 		    " total_bal=0x%llx\n",
56101ae08745Sheppo 		    ldcp->id, direction, target_pa, local_ra, target_poff,
56111ae08745Sheppo 		    local_poff, target_psize, local_psize, copy_size,
56121ae08745Sheppo 		    target_size);
56131ae08745Sheppo 
56141ae08745Sheppo 		rv = hv_ldc_copy(ldcp->id, direction,
56151ae08745Sheppo 		    (target_pa + target_poff), (local_ra + local_poff),
56161ae08745Sheppo 		    copy_size, &copied_len);
56171ae08745Sheppo 
56181ae08745Sheppo 		if (rv != 0) {
56193af08d82Slm66018 			DWARN(DBG_ALL_LDCS,
56203af08d82Slm66018 			    "ldc_mem_rdwr_cookie: (0x%lx) err %d during copy\n",
56211ae08745Sheppo 			    ldcp->id, rv);
56221ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
56233af08d82Slm66018 			    "ldc_mem_rdwr_cookie: (0x%llx) dir=%lld, "
56243af08d82Slm66018 			    "tar_pa=0x%llx, loc_ra=0x%llx, tar_poff=0x%llx, "
56253af08d82Slm66018 			    "loc_poff=0x%llx, tar_psz=0x%llx, loc_psz=0x%llx, "
56263af08d82Slm66018 			    "copy_sz=0x%llx, total_bal=0x%llx\n",
56271ae08745Sheppo 			    ldcp->id, direction, target_pa, local_ra,
56281ae08745Sheppo 			    target_poff, local_poff, target_psize, local_psize,
56291ae08745Sheppo 			    copy_size, target_size);
56301ae08745Sheppo 
56311ae08745Sheppo 			*size = *size - target_size;
56321ae08745Sheppo 			mutex_exit(&ldcp->lock);
56331ae08745Sheppo 			return (i_ldc_h2v_error(rv));
56341ae08745Sheppo 		}
56351ae08745Sheppo 
56363af08d82Slm66018 		D2(ldcp->id, "ldc_mem_rdwr_cookie: copied=0x%llx\n",
56373af08d82Slm66018 		    copied_len);
56381ae08745Sheppo 		target_poff += copied_len;
56391ae08745Sheppo 		local_poff += copied_len;
56401ae08745Sheppo 		target_psize -= copied_len;
56411ae08745Sheppo 		local_psize -= copied_len;
56421ae08745Sheppo 
56431ae08745Sheppo 		target_size -= copied_len;
56441ae08745Sheppo 
56451ae08745Sheppo 		if (copy_size != copied_len)
56461ae08745Sheppo 			continue;
56471ae08745Sheppo 
56481ae08745Sheppo 		if (target_psize == 0 && target_size != 0) {
56491ae08745Sheppo 			target_pa += pg_size;
56501ae08745Sheppo 			target_poff = 0;
56511ae08745Sheppo 			target_psize = min(pg_size, target_size);
56521ae08745Sheppo 		}
56531ae08745Sheppo 
56541ae08745Sheppo 		if (local_psize == 0 && target_size != 0) {
56551ae08745Sheppo 			local_valign += pg_size;
56561ae08745Sheppo 			local_ra = va_to_pa((void *)local_valign);
56571ae08745Sheppo 			local_poff = 0;
56581ae08745Sheppo 			local_psize = min(pg_size, len);
56591ae08745Sheppo 			len -= local_psize;
56601ae08745Sheppo 		}
56611ae08745Sheppo 
56621ae08745Sheppo 		/* check if we are all done */
56631ae08745Sheppo 		if (target_size == 0)
56641ae08745Sheppo 			break;
56651ae08745Sheppo 	}
56661ae08745Sheppo 
56671ae08745Sheppo 	mutex_exit(&ldcp->lock);
56681ae08745Sheppo 
56693af08d82Slm66018 	D1(ldcp->id, "ldc_mem_rdwr_cookie: (0x%llx) done copying sz=0x%llx\n",
56701ae08745Sheppo 	    ldcp->id, *size);
56711ae08745Sheppo 
56721ae08745Sheppo 	return (0);
56731ae08745Sheppo }
56741ae08745Sheppo 
56751ae08745Sheppo /*
56761ae08745Sheppo  * Map an exported memory segment into the local address space. If the
56771ae08745Sheppo  * memory range was exported for direct map access, a HV call is made
56781ae08745Sheppo  * to allocate a RA range. If the map is done via a shadow copy, local
56791ae08745Sheppo  * shadow memory is allocated and the base VA is returned in 'vaddr'. If
56801ae08745Sheppo  * the mapping is a direct map then the RA is returned in 'raddr'.
56811ae08745Sheppo  */
56821ae08745Sheppo int
56831ae08745Sheppo ldc_mem_map(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie, uint32_t ccount,
56844bac2208Snarayan     uint8_t mtype, uint8_t perm, caddr_t *vaddr, caddr_t *raddr)
56851ae08745Sheppo {
56864bac2208Snarayan 	int		i, j, idx, rv, retries;
56871ae08745Sheppo 	ldc_chan_t 	*ldcp;
56881ae08745Sheppo 	ldc_mhdl_t	*mhdl;
56891ae08745Sheppo 	ldc_memseg_t	*memseg;
56904bac2208Snarayan 	caddr_t		tmpaddr;
56914bac2208Snarayan 	uint64_t	map_perm = perm;
56924bac2208Snarayan 	uint64_t	pg_size, pg_shift, pg_size_code, pg_mask;
56934bac2208Snarayan 	uint64_t	exp_size = 0, base_off, map_size, npages;
56944bac2208Snarayan 	uint64_t	cookie_addr, cookie_off, cookie_size;
56954bac2208Snarayan 	tte_t		ldc_tte;
56961ae08745Sheppo 
56971ae08745Sheppo 	if (mhandle == NULL) {
56981ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_map: invalid memory handle\n");
56991ae08745Sheppo 		return (EINVAL);
57001ae08745Sheppo 	}
57011ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
57021ae08745Sheppo 
57031ae08745Sheppo 	mutex_enter(&mhdl->lock);
57041ae08745Sheppo 
57051ae08745Sheppo 	if (mhdl->status == LDC_BOUND || mhdl->status == LDC_MAPPED ||
57061ae08745Sheppo 	    mhdl->memseg != NULL) {
57071ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
57081ae08745Sheppo 		    "ldc_mem_map: (0x%llx) handle bound/mapped\n", mhandle);
57091ae08745Sheppo 		mutex_exit(&mhdl->lock);
57101ae08745Sheppo 		return (EINVAL);
57111ae08745Sheppo 	}
57121ae08745Sheppo 
57131ae08745Sheppo 	ldcp = mhdl->ldcp;
57141ae08745Sheppo 
57151ae08745Sheppo 	mutex_enter(&ldcp->lock);
57161ae08745Sheppo 
57171ae08745Sheppo 	if (ldcp->tstate != TS_UP) {
57181ae08745Sheppo 		DWARN(ldcp->id,
57191ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) channel is not UP\n",
57201ae08745Sheppo 		    ldcp->id);
57211ae08745Sheppo 		mutex_exit(&ldcp->lock);
57221ae08745Sheppo 		mutex_exit(&mhdl->lock);
57233af08d82Slm66018 		return (ECONNRESET);
57241ae08745Sheppo 	}
57251ae08745Sheppo 
57261ae08745Sheppo 	if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) {
57271ae08745Sheppo 		DWARN(ldcp->id, "ldc_mem_map: invalid map type\n");
57281ae08745Sheppo 		mutex_exit(&ldcp->lock);
57291ae08745Sheppo 		mutex_exit(&mhdl->lock);
57301ae08745Sheppo 		return (EINVAL);
57311ae08745Sheppo 	}
57321ae08745Sheppo 
57331ae08745Sheppo 	D1(ldcp->id, "ldc_mem_map: (0x%llx) cookie = 0x%llx,0x%llx\n",
5734d10e4ef2Snarayan 	    ldcp->id, cookie->addr, cookie->size);
57351ae08745Sheppo 
57361ae08745Sheppo 	/* FUTURE: get the page size, pgsz code, and shift */
57371ae08745Sheppo 	pg_size = MMU_PAGESIZE;
57381ae08745Sheppo 	pg_size_code = page_szc(pg_size);
57391ae08745Sheppo 	pg_shift = page_get_shift(pg_size_code);
57404bac2208Snarayan 	pg_mask = ~(pg_size - 1);
57411ae08745Sheppo 
57421ae08745Sheppo 	/* calculate the number of pages in the exported cookie */
57434bac2208Snarayan 	base_off = cookie[0].addr & (pg_size - 1);
57444bac2208Snarayan 	for (idx = 0; idx < ccount; idx++)
57451ae08745Sheppo 		exp_size += cookie[idx].size;
57464bac2208Snarayan 	map_size = P2ROUNDUP((exp_size + base_off), pg_size);
57474bac2208Snarayan 	npages = (map_size >> pg_shift);
57481ae08745Sheppo 
57491ae08745Sheppo 	/* Allocate memseg structure */
57504bac2208Snarayan 	memseg = mhdl->memseg =
57514bac2208Snarayan 	    kmem_cache_alloc(ldcssp->memseg_cache, KM_SLEEP);
57521ae08745Sheppo 
57531ae08745Sheppo 	/* Allocate memory to store all pages and cookies */
57541ae08745Sheppo 	memseg->pages =	kmem_zalloc((sizeof (ldc_page_t) * npages), KM_SLEEP);
57551ae08745Sheppo 	memseg->cookies =
57561ae08745Sheppo 	    kmem_zalloc((sizeof (ldc_mem_cookie_t) * ccount), KM_SLEEP);
57571ae08745Sheppo 
57584bac2208Snarayan 	D2(ldcp->id, "ldc_mem_map: (0x%llx) exp_size=0x%llx, map_size=0x%llx,"
57594bac2208Snarayan 	    "pages=0x%llx\n", ldcp->id, exp_size, map_size, npages);
57601ae08745Sheppo 
57614bac2208Snarayan 	/*
57624bac2208Snarayan 	 * Check if direct map over shared memory is enabled, if not change
57634bac2208Snarayan 	 * the mapping type to SHADOW_MAP.
57644bac2208Snarayan 	 */
57654bac2208Snarayan 	if (ldc_shmem_enabled == 0)
57664bac2208Snarayan 		mtype = LDC_SHADOW_MAP;
57674bac2208Snarayan 
57684bac2208Snarayan 	/*
57694bac2208Snarayan 	 * Check to see if the client is requesting direct or shadow map
57704bac2208Snarayan 	 * If direct map is requested, try to map remote memory first,
57714bac2208Snarayan 	 * and if that fails, revert to shadow map
57724bac2208Snarayan 	 */
57734bac2208Snarayan 	if (mtype == LDC_DIRECT_MAP) {
57744bac2208Snarayan 
57754bac2208Snarayan 		/* Allocate kernel virtual space for mapping */
57764bac2208Snarayan 		memseg->vaddr = vmem_xalloc(heap_arena, map_size,
57774bac2208Snarayan 		    pg_size, 0, 0, NULL, NULL, VM_NOSLEEP);
57784bac2208Snarayan 		if (memseg->vaddr == NULL) {
57794bac2208Snarayan 			cmn_err(CE_WARN,
57804bac2208Snarayan 			    "ldc_mem_map: (0x%lx) memory map failed\n",
57814bac2208Snarayan 			    ldcp->id);
57824bac2208Snarayan 			kmem_free(memseg->cookies,
57834bac2208Snarayan 			    (sizeof (ldc_mem_cookie_t) * ccount));
57844bac2208Snarayan 			kmem_free(memseg->pages,
57854bac2208Snarayan 			    (sizeof (ldc_page_t) * npages));
57864bac2208Snarayan 			kmem_cache_free(ldcssp->memseg_cache, memseg);
57874bac2208Snarayan 
57884bac2208Snarayan 			mutex_exit(&ldcp->lock);
57894bac2208Snarayan 			mutex_exit(&mhdl->lock);
57904bac2208Snarayan 			return (ENOMEM);
57914bac2208Snarayan 		}
57924bac2208Snarayan 
57934bac2208Snarayan 		/* Unload previous mapping */
57944bac2208Snarayan 		hat_unload(kas.a_hat, memseg->vaddr, map_size,
57954bac2208Snarayan 		    HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK);
57964bac2208Snarayan 
57974bac2208Snarayan 		/* for each cookie passed in - map into address space */
57984bac2208Snarayan 		idx = 0;
57994bac2208Snarayan 		cookie_size = 0;
58004bac2208Snarayan 		tmpaddr = memseg->vaddr;
58014bac2208Snarayan 
58024bac2208Snarayan 		for (i = 0; i < npages; i++) {
58034bac2208Snarayan 
58044bac2208Snarayan 			if (cookie_size == 0) {
58054bac2208Snarayan 				ASSERT(idx < ccount);
58064bac2208Snarayan 				cookie_addr = cookie[idx].addr & pg_mask;
58074bac2208Snarayan 				cookie_off = cookie[idx].addr & (pg_size - 1);
58084bac2208Snarayan 				cookie_size =
58094bac2208Snarayan 				    P2ROUNDUP((cookie_off + cookie[idx].size),
58104bac2208Snarayan 				    pg_size);
58114bac2208Snarayan 				idx++;
58124bac2208Snarayan 			}
58134bac2208Snarayan 
58144bac2208Snarayan 			D1(ldcp->id, "ldc_mem_map: (0x%llx) mapping "
58154bac2208Snarayan 			    "cookie 0x%llx, bal=0x%llx\n", ldcp->id,
58164bac2208Snarayan 			    cookie_addr, cookie_size);
58174bac2208Snarayan 
58184bac2208Snarayan 			/* map the cookie into address space */
58194bac2208Snarayan 			for (retries = 0; retries < ldc_max_retries;
58204bac2208Snarayan 			    retries++) {
58214bac2208Snarayan 
58224bac2208Snarayan 				rv = hv_ldc_mapin(ldcp->id, cookie_addr,
58234bac2208Snarayan 				    &memseg->pages[i].raddr, &map_perm);
58244bac2208Snarayan 				if (rv != H_EWOULDBLOCK && rv != H_ETOOMANY)
58254bac2208Snarayan 					break;
58264bac2208Snarayan 
58274bac2208Snarayan 				drv_usecwait(ldc_delay);
58284bac2208Snarayan 			}
58294bac2208Snarayan 
58304bac2208Snarayan 			if (rv || memseg->pages[i].raddr == 0) {
58314bac2208Snarayan 				DWARN(ldcp->id,
58324bac2208Snarayan 				    "ldc_mem_map: (0x%llx) hv mapin err %d\n",
58334bac2208Snarayan 				    ldcp->id, rv);
58344bac2208Snarayan 
58354bac2208Snarayan 				/* remove previous mapins */
58364bac2208Snarayan 				hat_unload(kas.a_hat, memseg->vaddr, map_size,
58374bac2208Snarayan 				    HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK);
58384bac2208Snarayan 				for (j = 0; j < i; j++) {
58394bac2208Snarayan 					rv = hv_ldc_unmap(
58404bac2208Snarayan 					    memseg->pages[j].raddr);
58414bac2208Snarayan 					if (rv) {
58424bac2208Snarayan 						DWARN(ldcp->id,
58434bac2208Snarayan 						    "ldc_mem_map: (0x%llx) "
58444bac2208Snarayan 						    "cannot unmap ra=0x%llx\n",
58454bac2208Snarayan 						    ldcp->id,
58464bac2208Snarayan 						    memseg->pages[j].raddr);
58474bac2208Snarayan 					}
58484bac2208Snarayan 				}
58494bac2208Snarayan 
58504bac2208Snarayan 				/* free kernel virtual space */
58514bac2208Snarayan 				vmem_free(heap_arena, (void *)memseg->vaddr,
58527636cb21Slm66018 				    map_size);
58534bac2208Snarayan 
58544bac2208Snarayan 				/* direct map failed - revert to shadow map */
58554bac2208Snarayan 				mtype = LDC_SHADOW_MAP;
58564bac2208Snarayan 				break;
58574bac2208Snarayan 
58584bac2208Snarayan 			} else {
58594bac2208Snarayan 
58604bac2208Snarayan 				D1(ldcp->id,
58614bac2208Snarayan 				    "ldc_mem_map: (0x%llx) vtop map 0x%llx -> "
58624bac2208Snarayan 				    "0x%llx, cookie=0x%llx, perm=0x%llx\n",
58634bac2208Snarayan 				    ldcp->id, tmpaddr, memseg->pages[i].raddr,
58644bac2208Snarayan 				    cookie_addr, perm);
58654bac2208Snarayan 
58664bac2208Snarayan 				/*
58674bac2208Snarayan 				 * NOTE: Calling hat_devload directly, causes it
58684bac2208Snarayan 				 * to look for page_t using the pfn. Since this
58694bac2208Snarayan 				 * addr is greater than the memlist, it treates
58704bac2208Snarayan 				 * it as non-memory
58714bac2208Snarayan 				 */
58724bac2208Snarayan 				sfmmu_memtte(&ldc_tte,
58734bac2208Snarayan 				    (pfn_t)(memseg->pages[i].raddr >> pg_shift),
58744bac2208Snarayan 				    PROT_READ | PROT_WRITE | HAT_NOSYNC, TTE8K);
58754bac2208Snarayan 
58764bac2208Snarayan 				D1(ldcp->id,
58774bac2208Snarayan 				    "ldc_mem_map: (0x%llx) ra 0x%llx -> "
58784bac2208Snarayan 				    "tte 0x%llx\n", ldcp->id,
58794bac2208Snarayan 				    memseg->pages[i].raddr, ldc_tte);
58804bac2208Snarayan 
58814bac2208Snarayan 				sfmmu_tteload(kas.a_hat, &ldc_tte, tmpaddr,
58824bac2208Snarayan 				    NULL, HAT_LOAD_LOCK);
58834bac2208Snarayan 
58844bac2208Snarayan 				cookie_size -= pg_size;
58854bac2208Snarayan 				cookie_addr += pg_size;
58864bac2208Snarayan 				tmpaddr += pg_size;
58874bac2208Snarayan 			}
58884bac2208Snarayan 		}
58894bac2208Snarayan 	}
58904bac2208Snarayan 
58911ae08745Sheppo 	if (mtype == LDC_SHADOW_MAP) {
58921ae08745Sheppo 		if (*vaddr == NULL) {
58933af08d82Slm66018 			memseg->vaddr = kmem_zalloc(exp_size, KM_SLEEP);
58941ae08745Sheppo 			mhdl->myshadow = B_TRUE;
58951ae08745Sheppo 
58961ae08745Sheppo 			D1(ldcp->id, "ldc_mem_map: (0x%llx) allocated "
58974bac2208Snarayan 			    "shadow page va=0x%llx\n", ldcp->id, memseg->vaddr);
58981ae08745Sheppo 		} else {
58991ae08745Sheppo 			/*
59004bac2208Snarayan 			 * Use client supplied memory for memseg->vaddr
59011ae08745Sheppo 			 * WARNING: assuming that client mem is >= exp_size
59021ae08745Sheppo 			 */
59034bac2208Snarayan 			memseg->vaddr = *vaddr;
59041ae08745Sheppo 		}
59051ae08745Sheppo 
59061ae08745Sheppo 		/* Save all page and cookie information */
59074bac2208Snarayan 		for (i = 0, tmpaddr = memseg->vaddr; i < npages; i++) {
59081ae08745Sheppo 			memseg->pages[i].raddr = va_to_pa(tmpaddr);
59091ae08745Sheppo 			memseg->pages[i].size = pg_size;
59101ae08745Sheppo 			tmpaddr += pg_size;
59111ae08745Sheppo 		}
59124bac2208Snarayan 
59131ae08745Sheppo 	}
59141ae08745Sheppo 
59154bac2208Snarayan 	/* save all cookies */
59164bac2208Snarayan 	bcopy(cookie, memseg->cookies, ccount * sizeof (ldc_mem_cookie_t));
59174bac2208Snarayan 
59181ae08745Sheppo 	/* update memseg_t */
59191ae08745Sheppo 	memseg->raddr = memseg->pages[0].raddr;
59204bac2208Snarayan 	memseg->size = (mtype == LDC_SHADOW_MAP) ? exp_size : map_size;
59211ae08745Sheppo 	memseg->npages = npages;
59221ae08745Sheppo 	memseg->ncookies = ccount;
59231ae08745Sheppo 	memseg->next_cookie = 0;
59241ae08745Sheppo 
59251ae08745Sheppo 	/* memory handle = mapped */
59261ae08745Sheppo 	mhdl->mtype = mtype;
59274bac2208Snarayan 	mhdl->perm = perm;
59281ae08745Sheppo 	mhdl->status = LDC_MAPPED;
59291ae08745Sheppo 
59301ae08745Sheppo 	D1(ldcp->id, "ldc_mem_map: (0x%llx) mapped 0x%llx, ra=0x%llx, "
59311ae08745Sheppo 	    "va=0x%llx, pgs=0x%llx cookies=0x%llx\n",
59321ae08745Sheppo 	    ldcp->id, mhdl, memseg->raddr, memseg->vaddr,
59331ae08745Sheppo 	    memseg->npages, memseg->ncookies);
59341ae08745Sheppo 
59354bac2208Snarayan 	if (mtype == LDC_SHADOW_MAP)
59364bac2208Snarayan 		base_off = 0;
59371ae08745Sheppo 	if (raddr)
59384bac2208Snarayan 		*raddr = (caddr_t)(memseg->raddr | base_off);
59391ae08745Sheppo 	if (vaddr)
59404bac2208Snarayan 		*vaddr = (caddr_t)((uintptr_t)memseg->vaddr | base_off);
59411ae08745Sheppo 
59421ae08745Sheppo 	mutex_exit(&ldcp->lock);
59431ae08745Sheppo 	mutex_exit(&mhdl->lock);
59441ae08745Sheppo 	return (0);
59451ae08745Sheppo }
59461ae08745Sheppo 
59471ae08745Sheppo /*
59481ae08745Sheppo  * Unmap a memory segment. Free shadow memory (if any).
59491ae08745Sheppo  */
59501ae08745Sheppo int
59511ae08745Sheppo ldc_mem_unmap(ldc_mem_handle_t mhandle)
59521ae08745Sheppo {
59534bac2208Snarayan 	int		i, rv;
59541ae08745Sheppo 	ldc_mhdl_t	*mhdl = (ldc_mhdl_t *)mhandle;
59551ae08745Sheppo 	ldc_chan_t 	*ldcp;
59561ae08745Sheppo 	ldc_memseg_t	*memseg;
59571ae08745Sheppo 
59581ae08745Sheppo 	if (mhdl == 0 || mhdl->status != LDC_MAPPED) {
59591ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
59601ae08745Sheppo 		    "ldc_mem_unmap: (0x%llx) handle is not mapped\n",
59611ae08745Sheppo 		    mhandle);
59621ae08745Sheppo 		return (EINVAL);
59631ae08745Sheppo 	}
59641ae08745Sheppo 
59651ae08745Sheppo 	mutex_enter(&mhdl->lock);
59661ae08745Sheppo 
59671ae08745Sheppo 	ldcp = mhdl->ldcp;
59681ae08745Sheppo 	memseg = mhdl->memseg;
59691ae08745Sheppo 
59701ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapping handle 0x%llx\n",
59711ae08745Sheppo 	    ldcp->id, mhdl);
59721ae08745Sheppo 
59731ae08745Sheppo 	/* if we allocated shadow memory - free it */
59741ae08745Sheppo 	if (mhdl->mtype == LDC_SHADOW_MAP && mhdl->myshadow) {
59753af08d82Slm66018 		kmem_free(memseg->vaddr, memseg->size);
59764bac2208Snarayan 	} else if (mhdl->mtype == LDC_DIRECT_MAP) {
59774bac2208Snarayan 
59784bac2208Snarayan 		/* unmap in the case of DIRECT_MAP */
59794bac2208Snarayan 		hat_unload(kas.a_hat, memseg->vaddr, memseg->size,
59804bac2208Snarayan 		    HAT_UNLOAD_UNLOCK);
59814bac2208Snarayan 
59824bac2208Snarayan 		for (i = 0; i < memseg->npages; i++) {
59834bac2208Snarayan 			rv = hv_ldc_unmap(memseg->pages[i].raddr);
59844bac2208Snarayan 			if (rv) {
59854bac2208Snarayan 				cmn_err(CE_WARN,
59864bac2208Snarayan 				    "ldc_mem_map: (0x%lx) hv unmap err %d\n",
59874bac2208Snarayan 				    ldcp->id, rv);
59884bac2208Snarayan 			}
59894bac2208Snarayan 		}
59904bac2208Snarayan 
59914bac2208Snarayan 		vmem_free(heap_arena, (void *)memseg->vaddr, memseg->size);
59921ae08745Sheppo 	}
59931ae08745Sheppo 
59941ae08745Sheppo 	/* free the allocated memseg and page structures */
59951ae08745Sheppo 	kmem_free(memseg->pages, (sizeof (ldc_page_t) * memseg->npages));
59961ae08745Sheppo 	kmem_free(memseg->cookies,
59971ae08745Sheppo 	    (sizeof (ldc_mem_cookie_t) * memseg->ncookies));
59984bac2208Snarayan 	kmem_cache_free(ldcssp->memseg_cache, memseg);
59991ae08745Sheppo 
60001ae08745Sheppo 	/* uninitialize the memory handle */
60011ae08745Sheppo 	mhdl->memseg = NULL;
60021ae08745Sheppo 	mhdl->status = LDC_UNBOUND;
60031ae08745Sheppo 
60041ae08745Sheppo 	D1(ldcp->id, "ldc_mem_unmap: (0x%llx) unmapped handle 0x%llx\n",
60051ae08745Sheppo 	    ldcp->id, mhdl);
60061ae08745Sheppo 
60071ae08745Sheppo 	mutex_exit(&mhdl->lock);
60081ae08745Sheppo 	return (0);
60091ae08745Sheppo }
60101ae08745Sheppo 
60111ae08745Sheppo /*
60121ae08745Sheppo  * Internal entry point for LDC mapped memory entry consistency
60131ae08745Sheppo  * semantics. Acquire copies the contents of the remote memory
60141ae08745Sheppo  * into the local shadow copy. The release operation copies the local
60151ae08745Sheppo  * contents into the remote memory. The offset and size specify the
60161ae08745Sheppo  * bounds for the memory range being synchronized.
60171ae08745Sheppo  */
60181ae08745Sheppo static int
60191ae08745Sheppo i_ldc_mem_acquire_release(ldc_mem_handle_t mhandle, uint8_t direction,
60201ae08745Sheppo     uint64_t offset, size_t size)
60211ae08745Sheppo {
60221ae08745Sheppo 	int 		err;
60231ae08745Sheppo 	ldc_mhdl_t	*mhdl;
60241ae08745Sheppo 	ldc_chan_t	*ldcp;
60251ae08745Sheppo 	ldc_memseg_t	*memseg;
60261ae08745Sheppo 	caddr_t		local_vaddr;
60271ae08745Sheppo 	size_t		copy_size;
60281ae08745Sheppo 
60291ae08745Sheppo 	if (mhandle == NULL) {
60301ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
60311ae08745Sheppo 		    "i_ldc_mem_acquire_release: invalid memory handle\n");
60321ae08745Sheppo 		return (EINVAL);
60331ae08745Sheppo 	}
60341ae08745Sheppo 	mhdl = (ldc_mhdl_t *)mhandle;
60351ae08745Sheppo 
60361ae08745Sheppo 	mutex_enter(&mhdl->lock);
60371ae08745Sheppo 
60381ae08745Sheppo 	if (mhdl->status != LDC_MAPPED || mhdl->ldcp == NULL) {
60391ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
60401ae08745Sheppo 		    "i_ldc_mem_acquire_release: not mapped memory\n");
60411ae08745Sheppo 		mutex_exit(&mhdl->lock);
60421ae08745Sheppo 		return (EINVAL);
60431ae08745Sheppo 	}
60441ae08745Sheppo 
60454bac2208Snarayan 	/* do nothing for direct map */
60464bac2208Snarayan 	if (mhdl->mtype == LDC_DIRECT_MAP) {
60474bac2208Snarayan 		mutex_exit(&mhdl->lock);
60484bac2208Snarayan 		return (0);
60494bac2208Snarayan 	}
60504bac2208Snarayan 
60514bac2208Snarayan 	/* do nothing if COPY_IN+MEM_W and COPY_OUT+MEM_R */
60524bac2208Snarayan 	if ((direction == LDC_COPY_IN && (mhdl->perm & LDC_MEM_R) == 0) ||
60534bac2208Snarayan 	    (direction == LDC_COPY_OUT && (mhdl->perm & LDC_MEM_W) == 0)) {
60544bac2208Snarayan 		mutex_exit(&mhdl->lock);
60554bac2208Snarayan 		return (0);
60564bac2208Snarayan 	}
60574bac2208Snarayan 
60581ae08745Sheppo 	if (offset >= mhdl->memseg->size ||
60591ae08745Sheppo 	    (offset + size) > mhdl->memseg->size) {
60601ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
60611ae08745Sheppo 		    "i_ldc_mem_acquire_release: memory out of range\n");
60621ae08745Sheppo 		mutex_exit(&mhdl->lock);
60631ae08745Sheppo 		return (EINVAL);
60641ae08745Sheppo 	}
60651ae08745Sheppo 
60661ae08745Sheppo 	/* get the channel handle and memory segment */
60671ae08745Sheppo 	ldcp = mhdl->ldcp;
60681ae08745Sheppo 	memseg = mhdl->memseg;
60691ae08745Sheppo 
60701ae08745Sheppo 	if (mhdl->mtype == LDC_SHADOW_MAP) {
60711ae08745Sheppo 
60721ae08745Sheppo 		local_vaddr = memseg->vaddr + offset;
60731ae08745Sheppo 		copy_size = size;
60741ae08745Sheppo 
60751ae08745Sheppo 		/* copy to/from remote from/to local memory */
60761ae08745Sheppo 		err = ldc_mem_copy((ldc_handle_t)ldcp, local_vaddr, offset,
60771ae08745Sheppo 		    &copy_size, memseg->cookies, memseg->ncookies,
60781ae08745Sheppo 		    direction);
60791ae08745Sheppo 		if (err || copy_size != size) {
6080cb112a14Slm66018 			DWARN(ldcp->id,
60811ae08745Sheppo 			    "i_ldc_mem_acquire_release: copy failed\n");
60821ae08745Sheppo 			mutex_exit(&mhdl->lock);
60831ae08745Sheppo 			return (err);
60841ae08745Sheppo 		}
60851ae08745Sheppo 	}
60861ae08745Sheppo 
60871ae08745Sheppo 	mutex_exit(&mhdl->lock);
60881ae08745Sheppo 
60891ae08745Sheppo 	return (0);
60901ae08745Sheppo }
60911ae08745Sheppo 
60921ae08745Sheppo /*
60931ae08745Sheppo  * Ensure that the contents in the remote memory seg are consistent
60941ae08745Sheppo  * with the contents if of local segment
60951ae08745Sheppo  */
60961ae08745Sheppo int
60971ae08745Sheppo ldc_mem_acquire(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size)
60981ae08745Sheppo {
60991ae08745Sheppo 	return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_IN, offset, size));
61001ae08745Sheppo }
61011ae08745Sheppo 
61021ae08745Sheppo 
61031ae08745Sheppo /*
61041ae08745Sheppo  * Ensure that the contents in the local memory seg are consistent
61051ae08745Sheppo  * with the contents if of remote segment
61061ae08745Sheppo  */
61071ae08745Sheppo int
61081ae08745Sheppo ldc_mem_release(ldc_mem_handle_t mhandle, uint64_t offset, uint64_t size)
61091ae08745Sheppo {
61101ae08745Sheppo 	return (i_ldc_mem_acquire_release(mhandle, LDC_COPY_OUT, offset, size));
61111ae08745Sheppo }
61121ae08745Sheppo 
61131ae08745Sheppo /*
61141ae08745Sheppo  * Allocate a descriptor ring. The size of each each descriptor
61151ae08745Sheppo  * must be 8-byte aligned and the entire ring should be a multiple
61161ae08745Sheppo  * of MMU_PAGESIZE.
61171ae08745Sheppo  */
61181ae08745Sheppo int
61191ae08745Sheppo ldc_mem_dring_create(uint32_t len, uint32_t dsize, ldc_dring_handle_t *dhandle)
61201ae08745Sheppo {
61211ae08745Sheppo 	ldc_dring_t *dringp;
61221ae08745Sheppo 	size_t size = (dsize * len);
61231ae08745Sheppo 
61241ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_create: len=0x%x, size=0x%x\n",
61251ae08745Sheppo 	    len, dsize);
61261ae08745Sheppo 
61271ae08745Sheppo 	if (dhandle == NULL) {
61281ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid dhandle\n");
61291ae08745Sheppo 		return (EINVAL);
61301ae08745Sheppo 	}
61311ae08745Sheppo 
61321ae08745Sheppo 	if (len == 0) {
61331ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid length\n");
61341ae08745Sheppo 		return (EINVAL);
61351ae08745Sheppo 	}
61361ae08745Sheppo 
61371ae08745Sheppo 	/* descriptor size should be 8-byte aligned */
61381ae08745Sheppo 	if (dsize == 0 || (dsize & 0x7)) {
61391ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_create: invalid size\n");
61401ae08745Sheppo 		return (EINVAL);
61411ae08745Sheppo 	}
61421ae08745Sheppo 
61431ae08745Sheppo 	*dhandle = 0;
61441ae08745Sheppo 
61451ae08745Sheppo 	/* Allocate a desc ring structure */
61461ae08745Sheppo 	dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP);
61471ae08745Sheppo 
61481ae08745Sheppo 	/* Initialize dring */
61491ae08745Sheppo 	dringp->length = len;
61501ae08745Sheppo 	dringp->dsize = dsize;
61511ae08745Sheppo 
61521ae08745Sheppo 	/* round off to multiple of pagesize */
61531ae08745Sheppo 	dringp->size = (size & MMU_PAGEMASK);
61541ae08745Sheppo 	if (size & MMU_PAGEOFFSET)
61551ae08745Sheppo 		dringp->size += MMU_PAGESIZE;
61561ae08745Sheppo 
61571ae08745Sheppo 	dringp->status = LDC_UNBOUND;
61581ae08745Sheppo 
61591ae08745Sheppo 	/* allocate descriptor ring memory */
61603af08d82Slm66018 	dringp->base = kmem_zalloc(dringp->size, KM_SLEEP);
61611ae08745Sheppo 
61621ae08745Sheppo 	/* initialize the desc ring lock */
61631ae08745Sheppo 	mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL);
61641ae08745Sheppo 
61651ae08745Sheppo 	/* Add descriptor ring to the head of global list */
61661ae08745Sheppo 	mutex_enter(&ldcssp->lock);
61671ae08745Sheppo 	dringp->next = ldcssp->dring_list;
61681ae08745Sheppo 	ldcssp->dring_list = dringp;
61691ae08745Sheppo 	mutex_exit(&ldcssp->lock);
61701ae08745Sheppo 
61711ae08745Sheppo 	*dhandle = (ldc_dring_handle_t)dringp;
61721ae08745Sheppo 
61731ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_create: dring allocated\n");
61741ae08745Sheppo 
61751ae08745Sheppo 	return (0);
61761ae08745Sheppo }
61771ae08745Sheppo 
61781ae08745Sheppo 
61791ae08745Sheppo /*
61801ae08745Sheppo  * Destroy a descriptor ring.
61811ae08745Sheppo  */
61821ae08745Sheppo int
61831ae08745Sheppo ldc_mem_dring_destroy(ldc_dring_handle_t dhandle)
61841ae08745Sheppo {
61851ae08745Sheppo 	ldc_dring_t *dringp;
61861ae08745Sheppo 	ldc_dring_t *tmp_dringp;
61871ae08745Sheppo 
61881ae08745Sheppo 	D1(DBG_ALL_LDCS, "ldc_mem_dring_destroy: entered\n");
61891ae08745Sheppo 
61901ae08745Sheppo 	if (dhandle == NULL) {
61911ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
61921ae08745Sheppo 		    "ldc_mem_dring_destroy: invalid desc ring handle\n");
61931ae08745Sheppo 		return (EINVAL);
61941ae08745Sheppo 	}
61951ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
61961ae08745Sheppo 
61971ae08745Sheppo 	if (dringp->status == LDC_BOUND) {
61981ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
61991ae08745Sheppo 		    "ldc_mem_dring_destroy: desc ring is bound\n");
62001ae08745Sheppo 		return (EACCES);
62011ae08745Sheppo 	}
62021ae08745Sheppo 
62031ae08745Sheppo 	mutex_enter(&dringp->lock);
62041ae08745Sheppo 	mutex_enter(&ldcssp->lock);
62051ae08745Sheppo 
62061ae08745Sheppo 	/* remove from linked list - if not bound */
62071ae08745Sheppo 	tmp_dringp = ldcssp->dring_list;
62081ae08745Sheppo 	if (tmp_dringp == dringp) {
62091ae08745Sheppo 		ldcssp->dring_list = dringp->next;
62101ae08745Sheppo 		dringp->next = NULL;
62111ae08745Sheppo 
62121ae08745Sheppo 	} else {
62131ae08745Sheppo 		while (tmp_dringp != NULL) {
62141ae08745Sheppo 			if (tmp_dringp->next == dringp) {
62151ae08745Sheppo 				tmp_dringp->next = dringp->next;
62161ae08745Sheppo 				dringp->next = NULL;
62171ae08745Sheppo 				break;
62181ae08745Sheppo 			}
62191ae08745Sheppo 			tmp_dringp = tmp_dringp->next;
62201ae08745Sheppo 		}
62211ae08745Sheppo 		if (tmp_dringp == NULL) {
62221ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
62231ae08745Sheppo 			    "ldc_mem_dring_destroy: invalid descriptor\n");
62241ae08745Sheppo 			mutex_exit(&ldcssp->lock);
62251ae08745Sheppo 			mutex_exit(&dringp->lock);
62261ae08745Sheppo 			return (EINVAL);
62271ae08745Sheppo 		}
62281ae08745Sheppo 	}
62291ae08745Sheppo 
62301ae08745Sheppo 	mutex_exit(&ldcssp->lock);
62311ae08745Sheppo 
62321ae08745Sheppo 	/* free the descriptor ring */
62333af08d82Slm66018 	kmem_free(dringp->base, dringp->size);
62341ae08745Sheppo 
62351ae08745Sheppo 	mutex_exit(&dringp->lock);
62361ae08745Sheppo 
62371ae08745Sheppo 	/* destroy dring lock */
62381ae08745Sheppo 	mutex_destroy(&dringp->lock);
62391ae08745Sheppo 
62401ae08745Sheppo 	/* free desc ring object */
62411ae08745Sheppo 	kmem_free(dringp, sizeof (ldc_dring_t));
62421ae08745Sheppo 
62431ae08745Sheppo 	return (0);
62441ae08745Sheppo }
62451ae08745Sheppo 
62461ae08745Sheppo /*
62471ae08745Sheppo  * Bind a previously allocated dring to a channel. The channel should
62481ae08745Sheppo  * be OPEN in order to bind the ring to the channel. Returns back a
62491ae08745Sheppo  * descriptor ring cookie. The descriptor ring is exported for remote
62501ae08745Sheppo  * access by the client at the other end of the channel. An entry for
62511ae08745Sheppo  * dring pages is stored in map table (via call to ldc_mem_bind_handle).
62521ae08745Sheppo  */
62531ae08745Sheppo int
62541ae08745Sheppo ldc_mem_dring_bind(ldc_handle_t handle, ldc_dring_handle_t dhandle,
62551ae08745Sheppo     uint8_t mtype, uint8_t perm, ldc_mem_cookie_t *cookie, uint32_t *ccount)
62561ae08745Sheppo {
62571ae08745Sheppo 	int		err;
62581ae08745Sheppo 	ldc_chan_t 	*ldcp;
62591ae08745Sheppo 	ldc_dring_t	*dringp;
62601ae08745Sheppo 	ldc_mem_handle_t mhandle;
62611ae08745Sheppo 
62621ae08745Sheppo 	/* check to see if channel is initalized */
62631ae08745Sheppo 	if (handle == NULL) {
62641ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
62651ae08745Sheppo 		    "ldc_mem_dring_bind: invalid channel handle\n");
62661ae08745Sheppo 		return (EINVAL);
62671ae08745Sheppo 	}
62681ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
62691ae08745Sheppo 
62701ae08745Sheppo 	if (dhandle == NULL) {
62711ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
62721ae08745Sheppo 		    "ldc_mem_dring_bind: invalid desc ring handle\n");
62731ae08745Sheppo 		return (EINVAL);
62741ae08745Sheppo 	}
62751ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
62761ae08745Sheppo 
62771ae08745Sheppo 	if (cookie == NULL) {
62781ae08745Sheppo 		DWARN(ldcp->id,
62791ae08745Sheppo 		    "ldc_mem_dring_bind: invalid cookie arg\n");
62801ae08745Sheppo 		return (EINVAL);
62811ae08745Sheppo 	}
62821ae08745Sheppo 
62831ae08745Sheppo 	mutex_enter(&dringp->lock);
62841ae08745Sheppo 
62851ae08745Sheppo 	if (dringp->status == LDC_BOUND) {
62861ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
62871ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) descriptor ring is bound\n",
62881ae08745Sheppo 		    ldcp->id);
62891ae08745Sheppo 		mutex_exit(&dringp->lock);
62901ae08745Sheppo 		return (EINVAL);
62911ae08745Sheppo 	}
62921ae08745Sheppo 
62931ae08745Sheppo 	if ((perm & LDC_MEM_RW) == 0) {
62941ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
62951ae08745Sheppo 		    "ldc_mem_dring_bind: invalid permissions\n");
62961ae08745Sheppo 		mutex_exit(&dringp->lock);
62971ae08745Sheppo 		return (EINVAL);
62981ae08745Sheppo 	}
62991ae08745Sheppo 
63001ae08745Sheppo 	if ((mtype & (LDC_SHADOW_MAP|LDC_DIRECT_MAP|LDC_IO_MAP)) == 0) {
63011ae08745Sheppo 		DWARN(DBG_ALL_LDCS, "ldc_mem_dring_bind: invalid type\n");
63021ae08745Sheppo 		mutex_exit(&dringp->lock);
63031ae08745Sheppo 		return (EINVAL);
63041ae08745Sheppo 	}
63051ae08745Sheppo 
63061ae08745Sheppo 	dringp->ldcp = ldcp;
63071ae08745Sheppo 
63081ae08745Sheppo 	/* create an memory handle */
63091ae08745Sheppo 	err = ldc_mem_alloc_handle(handle, &mhandle);
63101ae08745Sheppo 	if (err || mhandle == NULL) {
63111ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
63121ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) error allocating mhandle\n",
63131ae08745Sheppo 		    ldcp->id);
63141ae08745Sheppo 		mutex_exit(&dringp->lock);
63151ae08745Sheppo 		return (err);
63161ae08745Sheppo 	}
63171ae08745Sheppo 	dringp->mhdl = mhandle;
63181ae08745Sheppo 
63191ae08745Sheppo 	/* bind the descriptor ring to channel */
63201ae08745Sheppo 	err = ldc_mem_bind_handle(mhandle, dringp->base, dringp->size,
63211ae08745Sheppo 	    mtype, perm, cookie, ccount);
63221ae08745Sheppo 	if (err) {
63231ae08745Sheppo 		DWARN(ldcp->id,
63241ae08745Sheppo 		    "ldc_mem_dring_bind: (0x%llx) error binding mhandle\n",
63251ae08745Sheppo 		    ldcp->id);
63261ae08745Sheppo 		mutex_exit(&dringp->lock);
63271ae08745Sheppo 		return (err);
63281ae08745Sheppo 	}
63291ae08745Sheppo 
63301ae08745Sheppo 	/*
63311ae08745Sheppo 	 * For now return error if we get more than one cookie
63321ae08745Sheppo 	 * FUTURE: Return multiple cookies ..
63331ae08745Sheppo 	 */
63341ae08745Sheppo 	if (*ccount > 1) {
63351ae08745Sheppo 		(void) ldc_mem_unbind_handle(mhandle);
63361ae08745Sheppo 		(void) ldc_mem_free_handle(mhandle);
63371ae08745Sheppo 
63381ae08745Sheppo 		dringp->ldcp = NULL;
63391ae08745Sheppo 		dringp->mhdl = NULL;
63401ae08745Sheppo 		*ccount = 0;
63411ae08745Sheppo 
63421ae08745Sheppo 		mutex_exit(&dringp->lock);
63431ae08745Sheppo 		return (EAGAIN);
63441ae08745Sheppo 	}
63451ae08745Sheppo 
63461ae08745Sheppo 	/* Add descriptor ring to channel's exported dring list */
63471ae08745Sheppo 	mutex_enter(&ldcp->exp_dlist_lock);
63481ae08745Sheppo 	dringp->ch_next = ldcp->exp_dring_list;
63491ae08745Sheppo 	ldcp->exp_dring_list = dringp;
63501ae08745Sheppo 	mutex_exit(&ldcp->exp_dlist_lock);
63511ae08745Sheppo 
63521ae08745Sheppo 	dringp->status = LDC_BOUND;
63531ae08745Sheppo 
63541ae08745Sheppo 	mutex_exit(&dringp->lock);
63551ae08745Sheppo 
63561ae08745Sheppo 	return (0);
63571ae08745Sheppo }
63581ae08745Sheppo 
63591ae08745Sheppo /*
63601ae08745Sheppo  * Return the next cookie associated with the specified dring handle
63611ae08745Sheppo  */
63621ae08745Sheppo int
63631ae08745Sheppo ldc_mem_dring_nextcookie(ldc_dring_handle_t dhandle, ldc_mem_cookie_t *cookie)
63641ae08745Sheppo {
63651ae08745Sheppo 	int		rv = 0;
63661ae08745Sheppo 	ldc_dring_t 	*dringp;
63671ae08745Sheppo 	ldc_chan_t	*ldcp;
63681ae08745Sheppo 
63691ae08745Sheppo 	if (dhandle == NULL) {
63701ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
63711ae08745Sheppo 		    "ldc_mem_dring_nextcookie: invalid desc ring handle\n");
63721ae08745Sheppo 		return (EINVAL);
63731ae08745Sheppo 	}
63741ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
63751ae08745Sheppo 	mutex_enter(&dringp->lock);
63761ae08745Sheppo 
63771ae08745Sheppo 	if (dringp->status != LDC_BOUND) {
63781ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
63791ae08745Sheppo 		    "ldc_mem_dring_nextcookie: descriptor ring 0x%llx "
63801ae08745Sheppo 		    "is not bound\n", dringp);
63811ae08745Sheppo 		mutex_exit(&dringp->lock);
63821ae08745Sheppo 		return (EINVAL);
63831ae08745Sheppo 	}
63841ae08745Sheppo 
63851ae08745Sheppo 	ldcp = dringp->ldcp;
63861ae08745Sheppo 
63871ae08745Sheppo 	if (cookie == NULL) {
63881ae08745Sheppo 		DWARN(ldcp->id,
63891ae08745Sheppo 		    "ldc_mem_dring_nextcookie:(0x%llx) invalid cookie arg\n",
63901ae08745Sheppo 		    ldcp->id);
63911ae08745Sheppo 		mutex_exit(&dringp->lock);
63921ae08745Sheppo 		return (EINVAL);
63931ae08745Sheppo 	}
63941ae08745Sheppo 
63951ae08745Sheppo 	rv = ldc_mem_nextcookie((ldc_mem_handle_t)dringp->mhdl, cookie);
63961ae08745Sheppo 	mutex_exit(&dringp->lock);
63971ae08745Sheppo 
63981ae08745Sheppo 	return (rv);
63991ae08745Sheppo }
64001ae08745Sheppo /*
64011ae08745Sheppo  * Unbind a previously bound dring from a channel.
64021ae08745Sheppo  */
64031ae08745Sheppo int
64041ae08745Sheppo ldc_mem_dring_unbind(ldc_dring_handle_t dhandle)
64051ae08745Sheppo {
64061ae08745Sheppo 	ldc_dring_t 	*dringp;
64071ae08745Sheppo 	ldc_dring_t	*tmp_dringp;
64081ae08745Sheppo 	ldc_chan_t	*ldcp;
64091ae08745Sheppo 
64101ae08745Sheppo 	if (dhandle == NULL) {
64111ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
64121ae08745Sheppo 		    "ldc_mem_dring_unbind: invalid desc ring handle\n");
64131ae08745Sheppo 		return (EINVAL);
64141ae08745Sheppo 	}
64151ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
64161ae08745Sheppo 
64171ae08745Sheppo 	mutex_enter(&dringp->lock);
64181ae08745Sheppo 
64191ae08745Sheppo 	if (dringp->status == LDC_UNBOUND) {
64201ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
64211ae08745Sheppo 		    "ldc_mem_dring_bind: descriptor ring 0x%llx is unbound\n",
64221ae08745Sheppo 		    dringp);
64231ae08745Sheppo 		mutex_exit(&dringp->lock);
64241ae08745Sheppo 		return (EINVAL);
64251ae08745Sheppo 	}
64261ae08745Sheppo 	ldcp = dringp->ldcp;
64271ae08745Sheppo 
64281ae08745Sheppo 	mutex_enter(&ldcp->exp_dlist_lock);
64291ae08745Sheppo 
64301ae08745Sheppo 	tmp_dringp = ldcp->exp_dring_list;
64311ae08745Sheppo 	if (tmp_dringp == dringp) {
64321ae08745Sheppo 		ldcp->exp_dring_list = dringp->ch_next;
64331ae08745Sheppo 		dringp->ch_next = NULL;
64341ae08745Sheppo 
64351ae08745Sheppo 	} else {
64361ae08745Sheppo 		while (tmp_dringp != NULL) {
64371ae08745Sheppo 			if (tmp_dringp->ch_next == dringp) {
64381ae08745Sheppo 				tmp_dringp->ch_next = dringp->ch_next;
64391ae08745Sheppo 				dringp->ch_next = NULL;
64401ae08745Sheppo 				break;
64411ae08745Sheppo 			}
64421ae08745Sheppo 			tmp_dringp = tmp_dringp->ch_next;
64431ae08745Sheppo 		}
64441ae08745Sheppo 		if (tmp_dringp == NULL) {
64451ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
64461ae08745Sheppo 			    "ldc_mem_dring_unbind: invalid descriptor\n");
64471ae08745Sheppo 			mutex_exit(&ldcp->exp_dlist_lock);
64481ae08745Sheppo 			mutex_exit(&dringp->lock);
64491ae08745Sheppo 			return (EINVAL);
64501ae08745Sheppo 		}
64511ae08745Sheppo 	}
64521ae08745Sheppo 
64531ae08745Sheppo 	mutex_exit(&ldcp->exp_dlist_lock);
64541ae08745Sheppo 
64551ae08745Sheppo 	(void) ldc_mem_unbind_handle((ldc_mem_handle_t)dringp->mhdl);
64561ae08745Sheppo 	(void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl);
64571ae08745Sheppo 
64581ae08745Sheppo 	dringp->ldcp = NULL;
64591ae08745Sheppo 	dringp->mhdl = NULL;
64601ae08745Sheppo 	dringp->status = LDC_UNBOUND;
64611ae08745Sheppo 
64621ae08745Sheppo 	mutex_exit(&dringp->lock);
64631ae08745Sheppo 
64641ae08745Sheppo 	return (0);
64651ae08745Sheppo }
64661ae08745Sheppo 
64671ae08745Sheppo /*
64681ae08745Sheppo  * Get information about the dring. The base address of the descriptor
64691ae08745Sheppo  * ring along with the type and permission are returned back.
64701ae08745Sheppo  */
64711ae08745Sheppo int
64721ae08745Sheppo ldc_mem_dring_info(ldc_dring_handle_t dhandle, ldc_mem_info_t *minfo)
64731ae08745Sheppo {
64741ae08745Sheppo 	ldc_dring_t	*dringp;
64751ae08745Sheppo 	int		rv;
64761ae08745Sheppo 
64771ae08745Sheppo 	if (dhandle == NULL) {
64781ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
64791ae08745Sheppo 		    "ldc_mem_dring_info: invalid desc ring handle\n");
64801ae08745Sheppo 		return (EINVAL);
64811ae08745Sheppo 	}
64821ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
64831ae08745Sheppo 
64841ae08745Sheppo 	mutex_enter(&dringp->lock);
64851ae08745Sheppo 
64861ae08745Sheppo 	if (dringp->mhdl) {
64871ae08745Sheppo 		rv = ldc_mem_info(dringp->mhdl, minfo);
64881ae08745Sheppo 		if (rv) {
64891ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
64901ae08745Sheppo 			    "ldc_mem_dring_info: error reading mem info\n");
64911ae08745Sheppo 			mutex_exit(&dringp->lock);
64921ae08745Sheppo 			return (rv);
64931ae08745Sheppo 		}
64941ae08745Sheppo 	} else {
64951ae08745Sheppo 		minfo->vaddr = dringp->base;
64961ae08745Sheppo 		minfo->raddr = NULL;
64971ae08745Sheppo 		minfo->status = dringp->status;
64981ae08745Sheppo 	}
64991ae08745Sheppo 
65001ae08745Sheppo 	mutex_exit(&dringp->lock);
65011ae08745Sheppo 
65021ae08745Sheppo 	return (0);
65031ae08745Sheppo }
65041ae08745Sheppo 
65051ae08745Sheppo /*
65061ae08745Sheppo  * Map an exported descriptor ring into the local address space. If the
65071ae08745Sheppo  * descriptor ring was exported for direct map access, a HV call is made
65081ae08745Sheppo  * to allocate a RA range. If the map is done via a shadow copy, local
65091ae08745Sheppo  * shadow memory is allocated.
65101ae08745Sheppo  */
65111ae08745Sheppo int
65121ae08745Sheppo ldc_mem_dring_map(ldc_handle_t handle, ldc_mem_cookie_t *cookie,
65131ae08745Sheppo     uint32_t ccount, uint32_t len, uint32_t dsize, uint8_t mtype,
65141ae08745Sheppo     ldc_dring_handle_t *dhandle)
65151ae08745Sheppo {
65161ae08745Sheppo 	int		err;
65171ae08745Sheppo 	ldc_chan_t 	*ldcp = (ldc_chan_t *)handle;
65181ae08745Sheppo 	ldc_mem_handle_t mhandle;
65191ae08745Sheppo 	ldc_dring_t	*dringp;
65201ae08745Sheppo 	size_t		dring_size;
65211ae08745Sheppo 
65221ae08745Sheppo 	if (dhandle == NULL) {
65231ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
65241ae08745Sheppo 		    "ldc_mem_dring_map: invalid dhandle\n");
65251ae08745Sheppo 		return (EINVAL);
65261ae08745Sheppo 	}
65271ae08745Sheppo 
65281ae08745Sheppo 	/* check to see if channel is initalized */
65291ae08745Sheppo 	if (handle == NULL) {
65301ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
65311ae08745Sheppo 		    "ldc_mem_dring_map: invalid channel handle\n");
65321ae08745Sheppo 		return (EINVAL);
65331ae08745Sheppo 	}
65341ae08745Sheppo 	ldcp = (ldc_chan_t *)handle;
65351ae08745Sheppo 
65361ae08745Sheppo 	if (cookie == NULL) {
65371ae08745Sheppo 		DWARN(ldcp->id,
65381ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) invalid cookie\n",
65391ae08745Sheppo 		    ldcp->id);
65401ae08745Sheppo 		return (EINVAL);
65411ae08745Sheppo 	}
65421ae08745Sheppo 
65431ae08745Sheppo 	/* FUTURE: For now we support only one cookie per dring */
65441ae08745Sheppo 	ASSERT(ccount == 1);
65451ae08745Sheppo 
65461ae08745Sheppo 	if (cookie->size < (dsize * len)) {
65471ae08745Sheppo 		DWARN(ldcp->id,
65481ae08745Sheppo 		    "ldc_mem_dring_map: (0x%llx) invalid dsize/len\n",
65491ae08745Sheppo 		    ldcp->id);
65501ae08745Sheppo 		return (EINVAL);
65511ae08745Sheppo 	}
65521ae08745Sheppo 
65531ae08745Sheppo 	*dhandle = 0;
65541ae08745Sheppo 
65551ae08745Sheppo 	/* Allocate an dring structure */
65561ae08745Sheppo 	dringp = kmem_zalloc(sizeof (ldc_dring_t), KM_SLEEP);
65571ae08745Sheppo 
65581ae08745Sheppo 	D1(ldcp->id,
65591ae08745Sheppo 	    "ldc_mem_dring_map: 0x%x,0x%x,0x%x,0x%llx,0x%llx\n",
65601ae08745Sheppo 	    mtype, len, dsize, cookie->addr, cookie->size);
65611ae08745Sheppo 
65621ae08745Sheppo 	/* Initialize dring */
65631ae08745Sheppo 	dringp->length = len;
65641ae08745Sheppo 	dringp->dsize = dsize;
65651ae08745Sheppo 
65661ae08745Sheppo 	/* round of to multiple of page size */
65671ae08745Sheppo 	dring_size = len * dsize;
65681ae08745Sheppo 	dringp->size = (dring_size & MMU_PAGEMASK);
65691ae08745Sheppo 	if (dring_size & MMU_PAGEOFFSET)
65701ae08745Sheppo 		dringp->size += MMU_PAGESIZE;
65711ae08745Sheppo 
65721ae08745Sheppo 	dringp->ldcp = ldcp;
65731ae08745Sheppo 
65741ae08745Sheppo 	/* create an memory handle */
65751ae08745Sheppo 	err = ldc_mem_alloc_handle(handle, &mhandle);
65761ae08745Sheppo 	if (err || mhandle == NULL) {
65771ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
65781ae08745Sheppo 		    "ldc_mem_dring_map: cannot alloc hdl err=%d\n",
65791ae08745Sheppo 		    err);
65801ae08745Sheppo 		kmem_free(dringp, sizeof (ldc_dring_t));
65811ae08745Sheppo 		return (ENOMEM);
65821ae08745Sheppo 	}
65831ae08745Sheppo 
65841ae08745Sheppo 	dringp->mhdl = mhandle;
65851ae08745Sheppo 	dringp->base = NULL;
65861ae08745Sheppo 
65871ae08745Sheppo 	/* map the dring into local memory */
65884bac2208Snarayan 	err = ldc_mem_map(mhandle, cookie, ccount, mtype, LDC_MEM_RW,
65891ae08745Sheppo 	    &(dringp->base), NULL);
65901ae08745Sheppo 	if (err || dringp->base == NULL) {
65911ae08745Sheppo 		cmn_err(CE_WARN,
65921ae08745Sheppo 		    "ldc_mem_dring_map: cannot map desc ring err=%d\n", err);
65931ae08745Sheppo 		(void) ldc_mem_free_handle(mhandle);
65941ae08745Sheppo 		kmem_free(dringp, sizeof (ldc_dring_t));
65951ae08745Sheppo 		return (ENOMEM);
65961ae08745Sheppo 	}
65971ae08745Sheppo 
65981ae08745Sheppo 	/* initialize the desc ring lock */
65991ae08745Sheppo 	mutex_init(&dringp->lock, NULL, MUTEX_DRIVER, NULL);
66001ae08745Sheppo 
66011ae08745Sheppo 	/* Add descriptor ring to channel's imported dring list */
66021ae08745Sheppo 	mutex_enter(&ldcp->imp_dlist_lock);
66031ae08745Sheppo 	dringp->ch_next = ldcp->imp_dring_list;
66041ae08745Sheppo 	ldcp->imp_dring_list = dringp;
66051ae08745Sheppo 	mutex_exit(&ldcp->imp_dlist_lock);
66061ae08745Sheppo 
66071ae08745Sheppo 	dringp->status = LDC_MAPPED;
66081ae08745Sheppo 
66091ae08745Sheppo 	*dhandle = (ldc_dring_handle_t)dringp;
66101ae08745Sheppo 
66111ae08745Sheppo 	return (0);
66121ae08745Sheppo }
66131ae08745Sheppo 
66141ae08745Sheppo /*
66151ae08745Sheppo  * Unmap a descriptor ring. Free shadow memory (if any).
66161ae08745Sheppo  */
66171ae08745Sheppo int
66181ae08745Sheppo ldc_mem_dring_unmap(ldc_dring_handle_t dhandle)
66191ae08745Sheppo {
66201ae08745Sheppo 	ldc_dring_t 	*dringp;
66211ae08745Sheppo 	ldc_dring_t	*tmp_dringp;
66221ae08745Sheppo 	ldc_chan_t	*ldcp;
66231ae08745Sheppo 
66241ae08745Sheppo 	if (dhandle == NULL) {
66251ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
66261ae08745Sheppo 		    "ldc_mem_dring_unmap: invalid desc ring handle\n");
66271ae08745Sheppo 		return (EINVAL);
66281ae08745Sheppo 	}
66291ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
66301ae08745Sheppo 
66311ae08745Sheppo 	if (dringp->status != LDC_MAPPED) {
66321ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
66331ae08745Sheppo 		    "ldc_mem_dring_unmap: not a mapped desc ring\n");
66341ae08745Sheppo 		return (EINVAL);
66351ae08745Sheppo 	}
66361ae08745Sheppo 
66371ae08745Sheppo 	mutex_enter(&dringp->lock);
66381ae08745Sheppo 
66391ae08745Sheppo 	ldcp = dringp->ldcp;
66401ae08745Sheppo 
66411ae08745Sheppo 	mutex_enter(&ldcp->imp_dlist_lock);
66421ae08745Sheppo 
66431ae08745Sheppo 	/* find and unlink the desc ring from channel import list */
66441ae08745Sheppo 	tmp_dringp = ldcp->imp_dring_list;
66451ae08745Sheppo 	if (tmp_dringp == dringp) {
66461ae08745Sheppo 		ldcp->imp_dring_list = dringp->ch_next;
66471ae08745Sheppo 		dringp->ch_next = NULL;
66481ae08745Sheppo 
66491ae08745Sheppo 	} else {
66501ae08745Sheppo 		while (tmp_dringp != NULL) {
66511ae08745Sheppo 			if (tmp_dringp->ch_next == dringp) {
66521ae08745Sheppo 				tmp_dringp->ch_next = dringp->ch_next;
66531ae08745Sheppo 				dringp->ch_next = NULL;
66541ae08745Sheppo 				break;
66551ae08745Sheppo 			}
66561ae08745Sheppo 			tmp_dringp = tmp_dringp->ch_next;
66571ae08745Sheppo 		}
66581ae08745Sheppo 		if (tmp_dringp == NULL) {
66591ae08745Sheppo 			DWARN(DBG_ALL_LDCS,
66601ae08745Sheppo 			    "ldc_mem_dring_unmap: invalid descriptor\n");
66611ae08745Sheppo 			mutex_exit(&ldcp->imp_dlist_lock);
66621ae08745Sheppo 			mutex_exit(&dringp->lock);
66631ae08745Sheppo 			return (EINVAL);
66641ae08745Sheppo 		}
66651ae08745Sheppo 	}
66661ae08745Sheppo 
66671ae08745Sheppo 	mutex_exit(&ldcp->imp_dlist_lock);
66681ae08745Sheppo 
66691ae08745Sheppo 	/* do a LDC memory handle unmap and free */
66701ae08745Sheppo 	(void) ldc_mem_unmap(dringp->mhdl);
66711ae08745Sheppo 	(void) ldc_mem_free_handle((ldc_mem_handle_t)dringp->mhdl);
66721ae08745Sheppo 
66731ae08745Sheppo 	dringp->status = 0;
66741ae08745Sheppo 	dringp->ldcp = NULL;
66751ae08745Sheppo 
66761ae08745Sheppo 	mutex_exit(&dringp->lock);
66771ae08745Sheppo 
66781ae08745Sheppo 	/* destroy dring lock */
66791ae08745Sheppo 	mutex_destroy(&dringp->lock);
66801ae08745Sheppo 
66811ae08745Sheppo 	/* free desc ring object */
66821ae08745Sheppo 	kmem_free(dringp, sizeof (ldc_dring_t));
66831ae08745Sheppo 
66841ae08745Sheppo 	return (0);
66851ae08745Sheppo }
66861ae08745Sheppo 
66871ae08745Sheppo /*
66881ae08745Sheppo  * Internal entry point for descriptor ring access entry consistency
66891ae08745Sheppo  * semantics. Acquire copies the contents of the remote descriptor ring
66901ae08745Sheppo  * into the local shadow copy. The release operation copies the local
66911ae08745Sheppo  * contents into the remote dring. The start and end locations specify
66921ae08745Sheppo  * bounds for the entries being synchronized.
66931ae08745Sheppo  */
66941ae08745Sheppo static int
66951ae08745Sheppo i_ldc_dring_acquire_release(ldc_dring_handle_t dhandle,
66961ae08745Sheppo     uint8_t direction, uint64_t start, uint64_t end)
66971ae08745Sheppo {
66981ae08745Sheppo 	int 			err;
66991ae08745Sheppo 	ldc_dring_t		*dringp;
67001ae08745Sheppo 	ldc_chan_t		*ldcp;
67011ae08745Sheppo 	uint64_t		soff;
67021ae08745Sheppo 	size_t			copy_size;
67031ae08745Sheppo 
67041ae08745Sheppo 	if (dhandle == NULL) {
67051ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
67061ae08745Sheppo 		    "i_ldc_dring_acquire_release: invalid desc ring handle\n");
67071ae08745Sheppo 		return (EINVAL);
67081ae08745Sheppo 	}
67091ae08745Sheppo 	dringp = (ldc_dring_t *)dhandle;
67101ae08745Sheppo 	mutex_enter(&dringp->lock);
67111ae08745Sheppo 
67121ae08745Sheppo 	if (dringp->status != LDC_MAPPED || dringp->ldcp == NULL) {
67131ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
67141ae08745Sheppo 		    "i_ldc_dring_acquire_release: not a mapped desc ring\n");
67151ae08745Sheppo 		mutex_exit(&dringp->lock);
67161ae08745Sheppo 		return (EINVAL);
67171ae08745Sheppo 	}
67181ae08745Sheppo 
67191ae08745Sheppo 	if (start >= dringp->length || end >= dringp->length) {
67201ae08745Sheppo 		DWARN(DBG_ALL_LDCS,
67211ae08745Sheppo 		    "i_ldc_dring_acquire_release: index out of range\n");
67221ae08745Sheppo 		mutex_exit(&dringp->lock);
67231ae08745Sheppo 		return (EINVAL);
67241ae08745Sheppo 	}
67251ae08745Sheppo 
67261ae08745Sheppo 	/* get the channel handle */
67271ae08745Sheppo 	ldcp = dringp->ldcp;
67281ae08745Sheppo 
67291ae08745Sheppo 	copy_size = (start <= end) ? (((end - start) + 1) * dringp->dsize) :
67301ae08745Sheppo 	    ((dringp->length - start) * dringp->dsize);
67311ae08745Sheppo 
67321ae08745Sheppo 	/* Calculate the relative offset for the first desc */
67331ae08745Sheppo 	soff = (start * dringp->dsize);
67341ae08745Sheppo 
67351ae08745Sheppo 	/* copy to/from remote from/to local memory */
67361ae08745Sheppo 	D1(ldcp->id, "i_ldc_dring_acquire_release: c1 off=0x%llx sz=0x%llx\n",
67371ae08745Sheppo 	    soff, copy_size);
67381ae08745Sheppo 	err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl,
67391ae08745Sheppo 	    direction, soff, copy_size);
67401ae08745Sheppo 	if (err) {
67411ae08745Sheppo 		DWARN(ldcp->id,
67421ae08745Sheppo 		    "i_ldc_dring_acquire_release: copy failed\n");
67431ae08745Sheppo 		mutex_exit(&dringp->lock);
67441ae08745Sheppo 		return (err);
67451ae08745Sheppo 	}
67461ae08745Sheppo 
67471ae08745Sheppo 	/* do the balance */
67481ae08745Sheppo 	if (start > end) {
67491ae08745Sheppo 		copy_size = ((end + 1) * dringp->dsize);
67501ae08745Sheppo 		soff = 0;
67511ae08745Sheppo 
67521ae08745Sheppo 		/* copy to/from remote from/to local memory */
67531ae08745Sheppo 		D1(ldcp->id, "i_ldc_dring_acquire_release: c2 "
67541ae08745Sheppo 		    "off=0x%llx sz=0x%llx\n", soff, copy_size);
67551ae08745Sheppo 		err = i_ldc_mem_acquire_release((ldc_mem_handle_t)dringp->mhdl,
67561ae08745Sheppo 		    direction, soff, copy_size);
67571ae08745Sheppo 		if (err) {
67581ae08745Sheppo 			DWARN(ldcp->id,
67591ae08745Sheppo 			    "i_ldc_dring_acquire_release: copy failed\n");
67601ae08745Sheppo 			mutex_exit(&dringp->lock);
67611ae08745Sheppo 			return (err);
67621ae08745Sheppo 		}
67631ae08745Sheppo 	}
67641ae08745Sheppo 
67651ae08745Sheppo 	mutex_exit(&dringp->lock);
67661ae08745Sheppo 
67671ae08745Sheppo 	return (0);
67681ae08745Sheppo }
67691ae08745Sheppo 
67701ae08745Sheppo /*
67711ae08745Sheppo  * Ensure that the contents in the local dring are consistent
67721ae08745Sheppo  * with the contents if of remote dring
67731ae08745Sheppo  */
67741ae08745Sheppo int
67751ae08745Sheppo ldc_mem_dring_acquire(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end)
67761ae08745Sheppo {
67771ae08745Sheppo 	return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_IN, start, end));
67781ae08745Sheppo }
67791ae08745Sheppo 
67801ae08745Sheppo /*
67811ae08745Sheppo  * Ensure that the contents in the remote dring are consistent
67821ae08745Sheppo  * with the contents if of local dring
67831ae08745Sheppo  */
67841ae08745Sheppo int
67851ae08745Sheppo ldc_mem_dring_release(ldc_dring_handle_t dhandle, uint64_t start, uint64_t end)
67861ae08745Sheppo {
67871ae08745Sheppo 	return (i_ldc_dring_acquire_release(dhandle, LDC_COPY_OUT, start, end));
67881ae08745Sheppo }
67891ae08745Sheppo 
67901ae08745Sheppo 
67911ae08745Sheppo /* ------------------------------------------------------------------------- */
6792