vnet_gen.c revision f2b610cf6e03184d9538e3aaec99bcaf65124714
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include <sys/mach_descrip.h>
#include <sys/vio_mailbox.h>
#include <sys/vio_common.h>
#include <sys/vnet_common.h>
#include <sys/vnet_mailbox.h>
#include <sys/vio_util.h>
#include <sys/vnet_gen.h>
/*
* Implementation of the mac functionality for vnet using the
* generic(default) transport layer of sun4v Logical Domain Channels(LDC).
*/
/*
* Function prototypes.
*/
/* vgen proxy entry points */
int vgen_uninit(void *arg);
static int vgen_start(void *arg);
void vnet_del_def_rte(void *arg);
void vnet_tx_update(void *arg);
/* vgen internal functions */
int port_num);
static void vgen_ldc_watchdog(void *arg);
/* vgen handshake functions */
static void vgen_hwatchdog(void *arg);
static void vgen_ldc_rcv_worker(void *arg);
/*
* The handshake process consists of 5 phases defined below, with VH_PHASE0
* being the pre-handshake phase and VH_DONE is the phase to indicate
* successful completion of all phases.
* Each phase may have one to several handshake states which are required
* to complete successfully to move to the next phase.
* Refer to the functions vgen_handshake() and vgen_handshake_done() for
* more details.
*/
/* handshake phases */
/* handshake states */
enum {
VER_INFO_SENT = 0x1,
VER_ACK_RCVD = 0x2,
VER_INFO_RCVD = 0x4,
VER_ACK_SENT = 0x8,
ATTR_INFO_SENT = 0x10,
ATTR_ACK_RCVD = 0x20,
ATTR_INFO_RCVD = 0x40,
ATTR_ACK_SENT = 0x80,
DRING_INFO_SENT = 0x100,
DRING_ACK_RCVD = 0x200,
DRING_INFO_RCVD = 0x400,
DRING_ACK_SENT = 0x800,
RDX_INFO_SENT = 0x1000,
RDX_ACK_RCVD = 0x2000,
RDX_INFO_RCVD = 0x4000,
RDX_ACK_SENT = 0x8000,
};
#define LDC_UNLOCK(ldcp) \
static struct ether_addr etherbroadcastaddr = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
*/
#define IS_BROADCAST(ehp) \
#define IS_MULTICAST(ehp) \
/*
* Property names
*/
static char macaddr_propname[] = "mac-address";
static char rmacaddr_propname[] = "remote-mac-address";
static char channel_propname[] = "channel-endpoint";
static char reg_propname[] = "reg";
static char port_propname[] = "port";
static char swport_propname[] = "switch-port";
static char id_propname[] = "id";
/* versions supported - in decreasing order */
/* Tunables */
/*
* max # of packets accumulated prior to sending them up. It is best
* to keep this at 60% of the number of recieve buffers.
*/
/*
* Tunables for each receive buffer size and number of buffers for
* each buffer size.
*/
#ifdef DEBUG
/* flags to simulate error conditions for debugging */
int vgen_trigger_txtimeout = 0;
int vgen_trigger_rxlost = 0;
#endif
/* MD update matching structure */
static md_prop_match_t vport_prop_match[] = {
{ MDET_PROP_VAL, "id" },
{ MDET_LIST_END, NULL }
};
/* template for matching a particular vnet instance */
static mdeg_prop_spec_t vgen_prop_template[] = {
};
static mac_callbacks_t vgen_m_callbacks = {
0,
NULL,
NULL,
};
/* externs */
extern pri_t maxclsyspri;
extern uint32_t vnet_ntxds;
extern uint32_t vnet_ldcwd_interval;
extern uint32_t vnet_ldcwd_txtimeout;
extern uint32_t vnet_ldc_mtu;
extern uint32_t vnet_nrbufs;
#ifdef DEBUG
extern int vnet_dbglevel;
/* -1 for all LDCs info, or ldc_id for a specific LDC info */
int vgendbg_ldcid = -1;
/* simulate handshake error conditions for debug */
#define HDBG_VERSION 0x1
#define HDBG_TIMEOUT 0x2
#define HDBG_BAD_SID 0x4
#define HDBG_OUT_STATE 0x8
#endif
/*
* vgen_init() is called by an instance of vnet driver to initialize the
* corresponding generic proxy transport layer. The arguments passed by vnet
* are - an opaque pointer to the vnet instance, pointers to dev_info_t and
* the mac address of the vnet device, and a pointer to mac_register_t of
* the generic transport is returned in the last argument.
*/
int
{
int instance;
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/* allocate multicast table */
sizeof (struct ether_addr), KM_SLEEP);
/* register with MD event generator */
sizeof (struct ether_addr));
return (DDI_FAILURE);
}
/* register macp of this vgen_t with vnet */
return (DDI_SUCCESS);
}
/*
* Called by vnet to undo the initializations done by vgen_init().
* The handle provided by generic transport during vgen_init() is the argument.
*/
int
vgen_uninit(void *arg)
{
return (DDI_FAILURE);
}
/* unregister with MD event generator */
/* detach all ports from the device */
/*
* free any pending rx mblk pools,
* that couldn't be freed previously during channel detach.
*/
if (vio_destroy_mblks(rp)) {
return (DDI_FAILURE);
}
}
/* free multicast table */
return (DDI_SUCCESS);
}
int
vgen_start(void *arg)
{
return (DDI_SUCCESS);
}
void
{
}
/* vgen transmit function */
static mblk_t *
{
int i;
int status = VGEN_FAILURE;
/*
* Retry so that we avoid reporting a failure
* to the upper layer. Returning a failure may cause the
* upper layer to go into single threaded mode there by
* causing performance degradation, especially for a large
* number of connections.
*/
for (i = 0; i < vgen_tx_retries; ) {
if (status == VGEN_SUCCESS) {
break;
}
if (++i < vgen_tx_retries)
}
if (status != VGEN_SUCCESS) {
/* failure */
return (mp);
}
/* success */
return (NULL);
}
/* transmit packets over the given port */
static int
{
int status;
int rv = VGEN_SUCCESS;
/*
* NOTE: for now, we will assume we have a single channel.
*/
return (VGEN_FAILURE);
}
if (status != VGEN_TX_SUCCESS) {
rv = VGEN_FAILURE;
}
return (rv);
}
/* channel transmit function */
static int
{
struct ether_header *ehp;
int rv = 0;
ldcp->ldc_status);
/* retry ldc_up() if needed */
goto vgen_tx_exit;
}
/* drop the packet if ldc is not up or handshake is not done */
goto vgen_tx_exit;
}
goto vgen_tx_exit;
}
/*
* allocate a descriptor
*/
/* Try reclaiming now */
statsp->tx_no_desc++;
return (VGEN_TX_NORESOURCES);
}
}
/* update next available tbuf in the ring and update tx index */
/* Mark the buffer busy before releasing the lock */
/* copy data into pre-allocated transmit buffer */
}
/* initialize the corresponding public descriptor (txd) */
/*
* If the flags not set to BUSY, it implies that the clobber
* was done while we were copying the data. In such case,
* discard the packet and return.
*/
goto vgen_tx_exit;
}
/* update stats */
if (is_bcast)
else if (is_mcast)
/* send dring datamsg to the peer */
if (ldcp->resched_peer) {
if (rv != 0) {
/* error: drop the packet */
"failed: rv(%d) len(%d)\n",
} else {
}
}
}
if (rv == ECONNRESET) {
/*
* Check if either callback thread or another tx thread is
* already running. Calling mutex_enter() will result in a
* deadlock if the other thread already holds cblock and is
* blocked in vnet_modify_fdb() (which is called from
* vgen_handle_evt_reset()) waiting for write access on rwlock,
* as this transmit thread already holds that lock as a reader
* in vnet_m_tx(). See comments in vnet_modify_fdb() in vnet.c.
*/
} else {
}
/*
* Second arg is TRUE, as we know that
* the caller of this function - vnet_m_tx(),
* already holds fdb-rwlock as a reader.
*/
}
}
}
return (VGEN_TX_SUCCESS);
}
int
{
struct ether_addr *addrp;
int rv = DDI_FAILURE;
uint32_t i;
return (rv);
}
goto vgen_mcast_exit;
/*
* the multicast address. Otherwise, we just update this
* mcast address in our table and the table will be sync'd
* with vsw when handshake completes.
*/
B_FALSE) != VGEN_SUCCESS) {
goto vgen_mcast_exit;
}
}
if (add) {
/* expand multicast table if necessary */
struct ether_addr *newtab;
sizeof (struct ether_addr), KM_NOSLEEP);
goto vgen_mcast_exit;
sizeof (struct ether_addr));
}
/* add address to the table */
} else {
/* delete address from the table */
/*
* If there's more than one address in this
* table, delete the unwanted one by moving
* the last one in the list over top of it;
* otherwise, just remove it.
*/
}
break;
}
}
}
rv = DDI_SUCCESS;
return (rv);
}
/* set or clear promiscuous mode on the device */
static int
{
return (DDI_SUCCESS);
}
/* set the unicast mac address of the device */
static int
{
return (DDI_SUCCESS);
}
/* get device statistics */
int
{
*val = 0;
}
return (0);
}
static void
{
}
/* vgen internal functions */
/* detach all ports from the device */
static void
{
}
}
/*
* detach the given port.
*/
static void
{
int port_num;
/* remove it from port list */
/* detach channels from this port */
}
}
}
/* add a port to port list */
static void
{
} else {
}
}
/* remove a port from port list */
static void
{
return;
} else {
;
}
}
}
/* lookup a port in the list based on port_num */
static vgen_port_t *
{
break;
}
}
return (portp);
}
static void
{
}
}
static void
{
/*
* Create fdb entry in vnet, corresponding to the mac
* address of this port. Note that the port specified
* is vsw-port. This is done so that vsw-port acts
* as the route to reach this macaddr, until the
* channel for this port comes up (LDC_UP) and
* handshake is done successfully.
* eg, if the peer is OBP-vnet, it may not bring the
* channel up for this port and may communicate via
* vsw to reach this port.
* Later, when Solaris-vnet comes up at the other end
* of the channel for this port and brings up the channel,
* it is an indication that peer vnet is capable of
* distributed switching, so the direct route through this
* port is specified in fdb, using vnet_modify_fdb(macaddr);
*/
/*
* create the default route entry in vnet's fdb.
* This is the entry used by vnet to reach
* unknown destinations, which basically goes
* through vsw on domain0 and out through the
* physical device bound to vsw.
*/
}
/* Bring up the channels of this port */
}
static void
{
}
}
static void
{
/* delete the entry in vnet's fdb for this port */
/*
* if this is vsw-port, then delete the default
* route entry in vnet's fdb.
*/
}
}
/* register with MD event generator */
static int
{
int rv;
int i;
if (i == -1) {
return (DDI_FAILURE);
}
templatesz = sizeof (vgen_prop_template);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* NOTE: The instance here refers to the value of "reg" property and
* not the dev_info instance (ddi_get_instance()) of vnet.
*/
/* save parentp in vgen_t */
if (rv != MDEG_SUCCESS) {
return (DDI_FAILURE);
}
/* save mdeg handle in vgen_t */
return (DDI_SUCCESS);
}
/* unregister with MD event generator */
static void
{
}
/* callback function registered with MD event generator */
static int
{
int idx;
int vsw_idx = -1;
return (MDEG_FAILURE);
}
}
/*
* find vsw_port and add it first, because other ports need
* this when adding fdb entry (see vgen_port_init()).
*/
if (val == 0) {
/*
* This port is connected to the
* vsw on dom0.
*/
if (vgen_add_port(vgenp,
DDI_SUCCESS) {
"not initialize virtual "
"switch port.",
vnetdip));
return (MDEG_FAILURE);
}
break;
}
}
}
if (vsw_idx == -1) {
return (MDEG_FAILURE);
}
}
continue;
/* If this port can't be added just skip it. */
}
}
return (MDEG_SUCCESS);
}
/* add a new port to the device */
static int
{
int num_ldcs;
int i;
int addrsz;
int num_nodes = 0;
int listsz = 0;
int rv = DDI_SUCCESS;
struct ether_addr ea;
/* read "id" property to get the port number */
return (DDI_FAILURE);
}
/*
* Find the channel endpoint node(s) under this port node.
*/
return (DDI_FAILURE);
}
/* allocate space for node list */
return (DDI_FAILURE);
if (num_ldcs <= 0) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
for (i = 0; i < num_ldcs; i++) {
/* read channel ids */
return (DDI_FAILURE);
}
}
&addrsz)) {
return (DDI_FAILURE);
}
if (addrsz < ETHERADDRL) {
return (DDI_FAILURE);
}
for (i = ETHERADDRL - 1; i >= 0; i--) {
macaddr >>= 8;
}
if (val == 0) {
/* This port is connected to the vsw on dom0 */
}
}
}
rv = DDI_FAILURE;
}
return (rv);
}
/* remove a port from the device */
static int
{
/* read "id" property to get the port number */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* attach a port to the device based on mdeg data */
static int
{
int i;
return (DDI_FAILURE);
}
for (i = 0; i < num_ids; i++) {
return (DDI_FAILURE);
}
}
/* link it into the list of ports */
/* This port is connected to the vsw on domain0 */
if (vsw_port)
}
return (DDI_SUCCESS);
}
/* detach a port from the device based on mdeg data */
static void
{
/* stop the port if needed */
}
}
static int
{
/* NOTE: TBD */
return (DDI_SUCCESS);
}
static uint64_t
{
val = 0;
}
return (val);
}
/* attach the channel corresponding to the given ldc_id to the port */
static int
{
int status;
char kname[MAXNAMELEN];
int instance;
goto ldc_attach_failed;
}
if (status != 0) {
goto ldc_attach_failed;
}
if (vgen_rcv_thread_enabled) {
ldcp->rcv_thr_flags = 0;
vgen_ldc_rcv_softintr, (void *)ldcp);
if (status != DDI_SUCCESS) {
status);
goto ldc_attach_failed;
}
/*
* Initialize the soft_lock with the same priority as
* the soft interrupt to protect from the soft interrupt.
*/
goto ldc_attach_failed;
}
}
if (status != 0) {
status);
goto ldc_attach_failed;
}
/* allocate transmit resources */
if (status != 0) {
goto ldc_attach_failed;
}
/* allocate receive resources */
if (status != 0) {
goto ldc_attach_failed;
}
/* Setup kstats for the channel */
goto ldc_attach_failed;
}
/* initialize vgen_versions supported */
/* link it into the list of channels for this port */
return (DDI_SUCCESS);
if (attach_state & AST_ldc_reg_cb) {
}
if (attach_state & AST_add_softintr) {
}
if (attach_state & AST_create_rcv_thread) {
}
}
if (attach_state & AST_create_rxmblks) {
}
if (attach_state & AST_alloc_tx_ring) {
}
if (attach_state & AST_ldc_init) {
}
if (attach_state & AST_mutex_init) {
}
if (attach_state & AST_ldc_alloc) {
}
return (DDI_FAILURE);
}
/* detach a channel from the port */
static void
{
break;
}
}
/* invalid ldcp? */
return;
}
}
/* First stop the receive thread */
}
/* Free any queued messages */
}
/*
* if we cannot reclaim all mblks, put this
* on the list of pools(vgenp->rmp) to be reclaimed when the
* device gets detached (see vgen_uninit()).
*/
/* free transmit resources */
/* unlink it from the list */
}
}
/*
* This function allocates transmit resources for the channel.
* The resources consist of a transmit descriptor ring and an associated
* transmit buffer ring.
*/
static int
{
void *tbufp;
int status;
txdsize = sizeof (vnet_public_desc_t);
tbufsize = sizeof (vgen_private_desc_t);
/* allocate transmit buffer ring */
return (DDI_FAILURE);
}
/* create transmit descriptor ring */
&ldcp->tx_dhandle);
if (status) {
return (DDI_FAILURE);
}
/* get the addr of descripror ring */
if (status) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* Free transmit resources for the channel */
static void
{
int tbufsize = sizeof (vgen_private_desc_t);
/* free transmit descriptor ring */
/* free transmit buffer ring */
}
static void
{
(void) vgen_ldc_init(ldcp);
}
}
static void
{
}
}
static int
{
int rv;
if (rv != 0) {
goto ldcinit_failed;
}
goto ldcinit_failed;
}
if (rv != 0) {
goto ldcinit_failed;
}
if (rv != 0) {
goto ldcinit_failed;
}
do {
}
if (retries++ >= vgen_ldcup_retries)
break;
} while (rv == EWOULDBLOCK);
}
/* initialize transmit watchdog timeout */
/* if channel is already UP - start handshake */
/*
* modify fdb entry to use this port as the
* channel is up, instead of going through the
* vsw-port (see comments in vgen_port_init())
*/
}
/* Initialize local session id */
/* clear peer session id */
/* Initiate Handshake process with peer ldc endpoint */
} else {
}
return (DDI_SUCCESS);
if (init_state & ST_cb_enable) {
}
if (init_state & ST_init_tbufs) {
}
if (init_state & ST_ldc_open) {
}
return (DDI_FAILURE);
}
static void
{
int rv;
return;
}
/* disable further callbacks */
if (rv != 0) {
}
/*
* clear handshake done bit and wait for pending tx and cb to finish.
* release locks before untimeout(9F) is invoked to cancel timeouts.
*/
/* cancel handshake watchdog timeout */
}
/* cancel transmit watchdog timeout */
}
drv_usecwait(1000);
/* acquire locks again; any pending transmits and callbacks are done */
if (rv != 0) {
}
}
/* Initialize the transmit buffer ring for the channel */
static int
{
int i;
int rv;
int ci;
/*
* for each private descriptor, allocate a ldc mem_handle which is
* required to map the data during transmit, set the flags
* to free (available for use by transmit routine).
*/
if (rv) {
goto init_tbufs_failed;
}
/*
* bind ldc memhandle to the corresponding transmit buffer.
*/
if (rv != 0) {
goto init_tbufs_failed;
}
/*
* successful in binding the handle to tx data buffer.
* set datap in the private descr to this buffer.
*/
if ((ncookies == 0) ||
(ncookies > MAX_COOKIES)) {
goto init_tbufs_failed;
}
if (rv != 0) {
goto init_tbufs_failed;
}
}
datap += VGEN_TXDBLK_SZ;
}
/* reset tbuf walking pointers */
/* initialize tx seqnum and index */
ldcp->resched_peer_txi = 0;
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/* Uninitialize transmit buffer ring for the channel */
static void
{
int i;
/* for each tbuf (priv_desc), free ldc mem_handle */
}
}
}
/* prealloc'd tx data buffer */
}
}
/* clobber tx descriptor ring */
static void
{
int i;
#ifdef DEBUG
int ndone = 0;
#endif
#ifdef DEBUG
ndone++;
#endif
}
}
/* reset tbuf walking pointers */
/* reset tx seqnum and index */
ldcp->resched_peer_txi = 0;
}
/* clobber receive descriptor ring */
static void
{
ldcp->rx_dhandle = 0;
}
/* initialize receive descriptor ring */
static int
{
int rv;
if (rv != 0) {
return (DDI_FAILURE);
}
/*
* sucessfully mapped, now try to
* get info about the mapped dring
*/
if (rv != 0) {
return (DDI_FAILURE);
}
/*
* save ring address, number of descriptors.
*/
return (DDI_SUCCESS);
}
/* get channel statistics */
static uint64_t
{
val = 0;
switch (stat) {
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
/* stats not relevant to ldc, return 0 */
case MAC_STAT_IFSPEED:
case ETHER_STAT_ALIGN_ERRORS:
case ETHER_STAT_FCS_ERRORS:
case ETHER_STAT_DEFER_XMTS:
case ETHER_STAT_EX_COLLISIONS:
case ETHER_STAT_MACXMT_ERRORS:
case ETHER_STAT_XCVR_ADDR:
case ETHER_STAT_XCVR_ID:
case ETHER_STAT_XCVR_INUSE:
case ETHER_STAT_CAP_1000FDX:
case ETHER_STAT_CAP_1000HDX:
case ETHER_STAT_CAP_100FDX:
case ETHER_STAT_CAP_100HDX:
case ETHER_STAT_CAP_10FDX:
case ETHER_STAT_CAP_10HDX:
case ETHER_STAT_CAP_ASMPAUSE:
case ETHER_STAT_CAP_PAUSE:
case ETHER_STAT_CAP_AUTONEG:
case ETHER_STAT_ADV_CAP_10FDX:
case ETHER_STAT_ADV_CAP_10HDX:
case ETHER_STAT_ADV_CAP_PAUSE:
case ETHER_STAT_LP_CAP_100FDX:
case ETHER_STAT_LP_CAP_100HDX:
case ETHER_STAT_LP_CAP_10FDX:
case ETHER_STAT_LP_CAP_10HDX:
case ETHER_STAT_LP_CAP_PAUSE:
case ETHER_STAT_LINK_ASMPAUSE:
case ETHER_STAT_LINK_PAUSE:
case ETHER_STAT_LINK_AUTONEG:
case ETHER_STAT_LINK_DUPLEX:
default:
val = 0;
break;
}
return (val);
}
/*
* LDC channel is UP, start handshake process with peer.
* Flag tells vnet_modify_fdb() about the context: set to B_TRUE if this
* function is being called from transmit routine, otherwise B_FALSE.
*/
static void
{
/*
* modify fdb entry to use this port as the
* channel is up, instead of going through the
* vsw-port (see comments in vgen_port_init())
*/
}
/* Initialize local session id */
/* clear peer session id */
}
/* Initiate Handshake process with peer ldc endpoint */
}
/*
* LDC channel is Reset, terminate connection with peer and try to
* bring the channel up again.
* Flag tells vnet_modify_fdb() about the context: set to B_TRUE if this
* function is being called from transmit routine, otherwise B_FALSE.
*/
static void
{
int rv;
/*
* modify fdb entry to use vsw-port as the
* channel is reset and we don't have a direct
* link to the destination (see comments
* in vgen_port_init()).
*/
}
}
/* try to bring the channel up */
if (rv != 0) {
}
} else {
}
/* if channel is already UP - restart handshake */
}
}
/* Interrupt handler for the channel */
static uint_t
{
ldcp->ldc_status);
return (LDC_SUCCESS);
}
/*
* NOTE: not using switch() as event could be triggered by
* a state change and a read request. Also the ordering of the
* check for the event types is deliberate.
*/
if (event & LDC_EVT_UP) {
} else {
}
}
if (event & LDC_EVT_READ) {
/*
* If the receive thread is enabled, then
* wakeup the receive thread to process the
* LDC messages.
*/
}
} else {
}
}
} else {
}
}
/* send up the received packets to MAC layer */
}
if (ldcp->cancel_htid) {
/*
* Cancel handshake timer.
* untimeout(9F) will not return until the pending callback is
* cancelled or has run. No problems will result from calling
* untimeout if the handler has already completed.
* If the timeout handler did run, then it would just
* return as cancel_htid is set.
*/
ldcp->cancel_htid = 0;
}
return (LDC_SUCCESS);
}
static void
{
int rv;
/*
* If the receive thread is enabled, then the cblock
* need to be acquired here. If not, the vgen_ldc_cb()
* calls this function with cblock held already.
*/
} else {
}
do {
if (rv != 0) {
if (rv == ECONNRESET)
goto vgen_evtread_error;
break;
}
if (msglen == 0) {
break;
}
/*
* check sid only after we have received peer's sid
* in the version negotiate msg.
*/
#ifdef DEBUG
if (vgen_hdbg & HDBG_BAD_SID) {
/* simulate bad sid condition */
vgen_hdbg &= ~(HDBG_BAD_SID);
}
#endif
if (rv != VGEN_SUCCESS) {
/*
* If sid mismatch is detected,
* reset the channel.
*/
goto vgen_evtread_error;
}
}
switch (tagp->vio_msgtype) {
case VIO_TYPE_CTRL:
break;
case VIO_TYPE_DATA:
break;
case VIO_TYPE_ERR:
break;
default:
tagp->vio_msgtype);
break;
}
/*
* If an error is encountered, stop processing and
* handle the error.
*/
if (rv != 0) {
goto vgen_evtread_error;
}
} while (msglen);
/* check once more before exiting */
goto vgen_evt_read;
}
if (rv == ECONNRESET) {
} else {
}
} else if (rv) {
}
/*
* If the receive thread is not enabled, then cancel the
* handshake timeout here.
*/
if (ldcp->cancel_htid) {
/*
* Cancel handshake timer. untimeout(9F) will
* not return until the pending callback is cancelled
* or has run. No problems will result from calling
* untimeout if the handler has already completed.
* If the timeout handler did run, then it would just
* return as cancel_htid is set.
*/
ldcp->cancel_htid = 0;
}
}
}
/* vgen handshake functions */
/* change the hphase for the channel to the next phase */
static vgen_ldc_t *
{
} else {
}
return (ldcp);
}
/*
* Check whether the given version is supported or not and
* return VGEN_SUCCESS if supported.
*/
static int
{
int i = 0;
while (i < VGEN_NUM_VER) {
break;
}
return (VGEN_SUCCESS);
}
i++;
}
return (VGEN_FAILURE);
}
/*
* Given a version, return VGEN_SUCCESS if a lower version is supported.
*/
static int
{
int i = 0;
while (i < VGEN_NUM_VER) {
break;
}
/*
* if we support a lower minor version within the same major
* version, or if we support a lower major version,
* update the verp parameter with this lower version and
* return success.
*/
return (VGEN_SUCCESS);
}
i++;
}
return (VGEN_FAILURE);
}
/*
* wrapper routine to send the given message over ldc using ldc_write().
*/
static int
{
int rv;
return (VGEN_FAILURE);
if (!caller_holds_lock) {
}
do {
if (retries++ >= vgen_ldcwr_retries)
break;
} while (rv == EWOULDBLOCK);
if (!caller_holds_lock) {
}
if (rv != 0) {
return (rv);
}
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/* send version negotiate message to the peer over ldc */
static int
{
int rv;
/* get version msg payload from ldcp->local */
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/* send attr info message to the peer over ldc */
static int
{
int rv;
/* get attr msg payload from ldcp->local */
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/* send descriptor ring register message to the peer over ldc */
static int
{
int rv;
/* get dring info msg payload from ldcp->local */
sizeof (ldc_mem_cookie_t));
/*
* dring_ident is set to 0. After mapping the dring, peer sets this
* value and sends it in the ack, which is saved in
* vgen_handle_dring_reg().
*/
msg.dring_ident = 0;
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
static int
{
int rv;
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/* send descriptor ring data message to the peer over ldc */
static int
{
int rv;
if (rv != VGEN_SUCCESS) {
return (rv);
}
ldcp->next_txseq++;
return (VGEN_SUCCESS);
}
/* send multicast addr info message to vsw */
static int
{
struct ether_addr *mca;
int rv;
int i;
uint32_t n;
i = 0;
do {
size = n * sizeof (struct ether_addr);
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
mccount -= n;
i += n;
} while (mccount);
return (VGEN_SUCCESS);
}
/* Initiate Phase 2 of handshake */
static int
{
int rv;
#ifdef DEBUG
if (vgen_hdbg & HDBG_OUT_STATE) {
/* simulate out of state condition */
vgen_hdbg &= ~(HDBG_OUT_STATE);
return (rv);
}
if (vgen_hdbg & HDBG_TIMEOUT) {
/* simulate timeout condition */
vgen_hdbg &= ~(HDBG_TIMEOUT);
return (VGEN_SUCCESS);
}
#endif
if (rv != VGEN_SUCCESS) {
return (rv);
}
/* Bind descriptor ring to the channel */
if (ldcp->num_txdcookies == 0) {
if (rv != 0) {
"rv(%x)\n", rv);
return (rv);
}
}
/* update local dring_info params */
sizeof (ldc_mem_cookie_t));
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/*
* This function resets the handshake phase to VH_PHASE0(pre-handshake phase).
* This can happen after a channel comes up (status: LDC_UP) or
* when handshake gets terminated due to various conditions.
*/
static void
{
int rv;
/* reset hstate and hphase */
/*
* Save the id of pending handshake timer in cancel_htid.
* This will be checked in vgen_ldc_cb() and the handshake timer will
* be cancelled after releasing cblock.
*/
}
}
/* Unbind tx descriptor ring from the channel */
if (ldcp->num_txdcookies) {
if (rv != 0) {
}
ldcp->num_txdcookies = 0;
}
/* Unmap peer's dring */
}
/*
* clear local handshake params and initialize.
*/
/* set version to the highest version supported */
/* set attr_info params */
/*
* Note: dring is created, but not bound yet.
* local dring_info params will be updated when we bind the dring in
* vgen_handshake_phase2().
* dring_ident is set to 0. After mapping the dring, peer sets this
* value and sends it in the ack, which is saved in
* vgen_handle_dring_reg().
*/
/* clear peer_hparams */
/* reset the channel if required */
if (ldcp->need_ldc_reset) {
/* clear sids */
/* try to bring the channel up */
if (rv != 0) {
}
} else {
}
}
}
/* wrapper function for vgen_reset_hphase */
static void
{
}
/*
* Initiate handshake with the peer by sending various messages
* based on the handshake-phase that the channel is currently in.
*/
static void
{
int rv = 0;
switch (hphase) {
case VH_PHASE1:
/*
* start timer, for entire handshake process, turn this timer
* off if all phases of handshake complete successfully and
* hphase goes to VH_DONE(below) or
* vgen_reset_hphase() gets called or
* channel is reset due to errors or
* vgen_ldc_uninit() is invoked(vgen_stop).
*/
/* Phase 1 involves negotiating the version */
break;
case VH_PHASE2:
break;
case VH_PHASE3:
break;
case VH_DONE:
/*
* Save the id of pending handshake timer in cancel_htid.
* This will be checked in vgen_ldc_cb() and the handshake
* timer will be cancelled after releasing cblock.
*/
}
/*
* If this channel(port) is connected to vsw,
* need to sync multicast table with vsw.
*/
if (rv != VGEN_SUCCESS)
break;
}
/*
* Check if mac layer should be notified to restart
* transmissions. This can happen if the channel got
* reset and vgen_clobber_tbufs() is called, while
* need_resched is set.
*/
if (ldcp->need_resched) {
}
break;
default:
break;
}
if (rv == ECONNRESET) {
} else {
}
} else if (rv) {
}
}
/*
* Check if the current handshake phase has completed successfully and
* return the status.
*/
static int
{
int status = 0;
switch (hphase) {
case VH_PHASE1:
/*
* Phase1 is done, if version negotiation
* completed successfully.
*/
break;
case VH_PHASE2:
/*
* Phase 2 is done, if attr info and dring info
* have been exchanged successfully.
*/
break;
case VH_PHASE3:
/* Phase 3 is done, if rdx msg has been exchanged */
break;
default:
break;
}
if (status == 0) {
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/* retry handshake on failure */
static void
{
/* reset handshake phase */
/* handshake retry is specified and the channel is UP */
}
}
}
/*
* to a version info msg that we sent.
*/
static int
{
int ack = 0;
int failed = 0;
int idx;
int rv = 0;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* Cache sid of peer if this is the first time */
}
/*
* If we are not already in VH_PHASE1, reset to
* pre-handshake state, and initiate handshake
* to the peer too.
*/
}
/* save peer's requested values */
/* unsupported dev_class, send NACK */
/* send reply msg back to peer */
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_FAILURE);
}
idx = 0;
for (;;) {
/* nack with next lower version */
break;
}
/* major version match - ACK version */
ack = 1;
/*
* lower minor version to the one this endpt
* supports, if necessary
*/
}
break;
}
idx++;
if (idx == VGEN_NUM_VER) {
/* no version match - send NACK */
failed = 1;
break;
}
}
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
if (ack) {
}
if (failed) {
return (VGEN_FAILURE);
}
/* VER_ACK_SENT and VER_ACK_RCVD */
/* local and peer versions match? */
/* move to the next phase */
}
break;
case VIO_SUBTYPE_ACK:
/* This should not happen. */
return (VGEN_FAILURE);
}
/* SUCCESS - we have agreed on a version */
/* VER_ACK_SENT and VER_ACK_RCVD */
/* local and peer versions match? */
/* move to the next phase */
}
break;
case VIO_SUBTYPE_NACK:
/* This should not happen. */
return (VGEN_FAILURE);
}
/* check if version in NACK is zero */
/*
* Version Negotiation has failed.
*/
return (VGEN_FAILURE);
}
idx = 0;
for (;;) {
/* select next lower version */
break;
}
/* major version match */
break;
}
idx++;
if (idx == VGEN_NUM_VER) {
/*
* no version match.
* Version Negotiation has failed.
*/
"Version Negotiation Failed\n");
return (VGEN_FAILURE);
}
}
if (rv != VGEN_SUCCESS) {
return (rv);
}
break;
}
return (VGEN_SUCCESS);
}
/* Check if the attributes are supported */
static int
{
/*
* currently, we support these attr values:
* mtu of ethernet, addr_type of mac, xfer_mode of
* ldc shared memory, ack_freq of 0 (data is acked if
* the ack bit is set in the descriptor) and the address should
* match the address in the port node.
*/
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/*
* to an attr info msg that we sent.
*/
static int
{
int ack = 0;
int rv = 0;
" Invalid Phase(%u)\n",
return (VGEN_FAILURE);
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* save peer's values */
/* unsupported attr, send NACK */
} else {
ack = 1;
}
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
if (ack) {
} else {
/* failed */
return (VGEN_FAILURE);
}
}
break;
case VIO_SUBTYPE_ACK:
}
break;
case VIO_SUBTYPE_NACK:
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/* Check if the dring info msg is ok */
static int
{
/* check if msg contents are ok */
sizeof (vnet_public_desc_t))) {
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/*
* the peer to a dring register msg that we sent.
*/
static int
{
int ack = 0;
int rv = 0;
/* dring_info can be rcvd in any of the phases after Phase1 */
"Rcvd DRING_INFO Subtype (%d), Invalid Phase(%u)\n",
return (VGEN_FAILURE);
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/*
* verified dring info msg to be ok,
* now try to map the remote dring.
*/
if (rv == DDI_SUCCESS) {
/* now we can ack the peer */
ack = 1;
}
}
if (ack == 0) {
/* failed, send NACK */
} else {
/* save peer's dring_info values */
sizeof (dcookie));
/* set dring_ident for the peer */
/* return the dring_ident in ack msg */
msg->dring_ident =
}
}
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
if (ack) {
} else {
return (VGEN_FAILURE);
}
}
break;
case VIO_SUBTYPE_ACK:
/* local dring is now ready */
/* save dring_ident acked by peer */
}
}
break;
case VIO_SUBTYPE_NACK:
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/*
* from the peer to a rdx info msg that we sent.
*/
static int
{
int rv = 0;
"Rcvd RDX_INFO Subtype (%d), Invalid Phase(%u)\n",
return (VGEN_FAILURE);
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
}
break;
case VIO_SUBTYPE_ACK:
}
break;
case VIO_SUBTYPE_NACK:
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
static int
{
struct ether_addr *addrp;
int count;
int i;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* vnet shouldn't recv set mcast msg, only vsw handles it */
break;
case VIO_SUBTYPE_ACK:
break;
case VIO_SUBTYPE_NACK:
/* multicast remove request failed */
break;
}
/* multicast add request failed */
/* delete address from the table */
}
break;
}
}
}
break;
}
return (VGEN_SUCCESS);
}
/* handler for control messages received from the peer ldc end-point */
static int
{
int rv = 0;
switch (tagp->vio_subtype_env) {
case VIO_VER_INFO:
break;
case VIO_ATTR_INFO:
break;
case VIO_DRING_REG:
break;
case VIO_RDX:
break;
case VNET_MCAST_INFO:
break;
}
return (rv);
}
/* handler for data messages received from the peer ldc end-point */
static int
{
int rv = 0;
return (rv);
switch (tagp->vio_subtype_env) {
case VIO_DRING_DATA:
break;
default:
break;
}
return (rv);
}
static int
{
int rv = 0;
if (rv != VGEN_SUCCESS) {
}
return (rv);
}
static int
{
int rv = 0;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/*
* To reduce the locking contention, release the
* cblock here and re-acquire it once we are done
* receiving packets.
*/
break;
case VIO_SUBTYPE_ACK:
break;
case VIO_SUBTYPE_NACK:
break;
}
return (rv);
}
static int
{
int rv = 0;
#ifdef VGEN_HANDLE_LOST_PKTS
int n;
#endif
/*
* received a data msg, which contains the start and end
* indices of the descriptors within the rx ring holding data,
* the seq_num of data packet corresponding to the start index,
* and the dring_ident.
* We can now read the contents of each of these descriptors
* and gather data from it.
*/
/* validate rx start and end indeces */
/* drop the message if invalid index */
return (rv);
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
return (rv);
}
#ifdef DEBUG
if (vgen_trigger_rxlost) {
/* drop this msg to simulate lost pkts for debugging */
vgen_trigger_rxlost = 0;
return (rv);
}
#endif
#ifdef VGEN_HANDLE_LOST_PKTS
/* receive start index doesn't match expected index */
/* calculate the number of pkts lost */
} else {
}
/*
* sequence number of dring data message
* is less than the next sequence number that
* is expected:
*
* drop the message and the corresponding packets.
*/
"rxseq(0x%lx) > recvd(0x%lx)\n",
/*
* sender?? drop this msg.
*/
return (rv);
}
/*
* sequence number of dring data message
* is greater than the next expected sequence number
*
* send a NACK back to the peer to indicate lost
* packets.
*/
statsp->rx_lost_pkts += n;
/* indicate the range of lost descriptors */
/* dring ident is left unchanged */
if (rv != VGEN_SUCCESS) {
"vgen_sendmsg failed, stype:NACK\n");
return (rv);
}
#ifdef VGEN_REXMIT
/*
* stop further processing until peer
* retransmits with the right index.
* update next_rxseq expected.
*/
return (rv);
#else /* VGEN_REXMIT */
/*
* and set the new expected values for next_rxi
* and next_rxseq. continue(below) to process
* from the new start index.
*/
#endif /* VGEN_REXMIT */
/*
* expected and received seqnums match, but
* the descriptor indeces don't?
*
* restart handshake with peer.
*/
}
} else {
/* expected and start dring indeces match */
/* seqnums don't match */
"next_rxseq(0x%lx) != seq_num(0x%lx)\n",
}
}
#endif /* VGEN_HANDLE_LOST_PKTS */
/* Now receive messages */
return (rv);
}
static int
{
int count = 0;
int rv = 0;
struct ether_header *ehp;
/*
* start processing the descriptors from the specified
* start index, up to the index a descriptor is not ready
* to be processed or we process the entire descriptor ring
* and wrap around upto the start index.
*/
/* need to set the start index of descriptors to be ack'd */
/* index upto which we have ack'd */
do {
if (rv != 0) {
" rv(%d)\n", rv);
return (rv);
}
/*
* Before waiting and retry here, queue
* the messages that are received already.
* This will help the soft interrupt to
* send them up with less latency.
*/
count = 0;
}
/*
* descriptor is not ready.
* retry descriptor acquire, stop processing
* after max # retries.
*/
if (retries == vgen_recv_retries)
break;
retries++;
goto vgen_recv_retry;
}
retries = 0;
if (set_ack_start) {
/*
* initialize the start index of the range
* of descriptors to be ack'd.
*/
}
(ncookies == 0) ||
(ncookies > MAX_COOKIES)) {
} else {
/*
* Try to allocate an mblk from the free pool
* of recv mblks for the channel.
* If this fails, use allocb().
*/
if (!mp) {
/*
* The data buffer returned by
* allocb(9F) is 8byte aligned. We
* allocate extra 8 bytes to ensure
* size is multiple of 8 bytes for
* ldc_mem_copy().
*/
BPRI_MED);
}
}
/*
* rxd_err or allocb() failure,
* drop this packet, get next.
*/
if (rxd_err) {
} else {
statsp->rx_allocb_fail++;
}
/* set descriptor done bit */
if (rv != 0) {
"ldc_mem_dring_release err rv(%d)\n", rv);
return (rv);
}
if (ack_needed) {
/*
* sender needs ack for this packet,
* ack pkts upto this index.
*/
if (rv != VGEN_SUCCESS) {
goto error_ret;
}
/* need to set new ack start index */
}
goto vgen_next_rxi;
}
/* if ldc_mem_copy() failed */
if (rv) {
goto error_ret;
}
if (rv != 0) {
"ldc_mem_dring_release err rv(%d)\n", rv);
goto error_ret;
}
if (ack_needed) {
/*
* sender needs ack for this packet,
* ack pkts upto this index.
*/
if (rv != VGEN_SUCCESS) {
goto error_ret;
}
/* need to set new ack start index */
}
"ldc_mem_copy nread(%lx), nbytes(%lx)\n",
goto vgen_next_rxi;
}
/* point to the actual end of data */
/* update stats */
if (IS_BROADCAST(ehp))
else if (IS_MULTICAST(ehp))
/* build a chain of received packets */
/* first pkt */
} else {
}
if (count++ > vgen_chain_len) {
count = 0;
}
/* update end index of range of descrs to be ack'd */
/* update the next index to be processed */
/*
* processed the entire descriptor ring upto
* the index at which we started.
*/
break;
}
} while (1);
/*
* send an ack message to peer indicating that we have stopped
* processing descriptors.
*/
if (set_ack_start) {
/*
* We have ack'd upto some index and we have not
* processed any descriptors beyond that index.
* Use the last ack'd index as both the start and
* end of range of descrs being ack'd.
* Note: This results in acking the last index twice
* and should be harmless.
*/
}
if (rv != VGEN_SUCCESS) {
goto error_ret;
}
/* save new recv index and expected seqnum of next dring msg */
/* queue the packets received so far */
}
return (rv);
}
static int
{
int rv = 0;
/*
* received an ack corresponding to a specific descriptor for
* which we had set the ACK bit in the descriptor (during
* transmit). This enables us to reclaim descriptors.
*/
/* validate start and end indeces in the tx ack msg */
/* drop the message if invalid index */
return (rv);
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
return (rv);
}
/* reclaim descriptors that are done */
/*
* receiver continued processing descriptors after
* sending us the ack.
*/
return (rv);
}
/* receiver stopped processing descriptors */
/*
* determine if there are any pending tx descriptors
* ready to be processed by the receiver(peer) and if so,
* send a message to the peer to restart receiving.
*/
/*
* using the end index of the descriptor range for which
* we received the ack, check if the next descriptor is
* ready.
*/
} else {
/*
* descr next to the end of ack'd descr range is not
* ready.
* starting from the current reclaim index, check
* if any descriptor is ready.
*/
}
}
if (ready_txd) {
/*
* we have tx descriptor(s) ready to be
* processed by the receiver.
* send a message to the peer with the start index
* of ready descriptors.
*/
if (rv != VGEN_SUCCESS) {
return (rv);
}
} else {
/*
* no ready tx descriptors. set the flag to send a
* message to peer when tx descriptors are ready in
* transmit routine.
*/
}
return (rv);
}
static int
{
int rv = 0;
#ifdef VGEN_REXMIT
#endif
/*
* peer sent a NACK msg to indicate lost packets.
* The start and end correspond to the range of descriptors
* for which the peer didn't receive a dring data msg and so
* didn't receive the corresponding data.
*/
/* validate start and end indeces in the tx nack msg */
/* drop the message if invalid index */
return (rv);
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
return (rv);
}
/* no busy descriptors, bogus nack ? */
return (rv);
}
#ifdef VGEN_REXMIT
/* send a new dring data msg including the lost descrs */
if (rv != 0) {
/*
* vgen_send_dring_data() error: drop all packets
* in this descr range
*/
}
/* update next pointer */
}
#else /* VGEN_REXMIT */
/* we just mark the descrs as done so they can be reclaimed */
}
#endif /* VGEN_REXMIT */
return (rv);
}
static void
{
}
/*
* transmit reclaim function. starting from the current reclaim index
* look for descriptors marked DONE and reclaim the descriptor and the
* corresponding buffers (tbuf).
*/
static void
{
int count = 0;
#ifdef DEBUG
return;
#endif
count++;
}
/*
* Check if mac layer should be notified to restart transmissions
*/
}
}
/* return the number of pending transmits for the channel */
static int
{
int n;
} else {
/* cur_tbufp > next_tbufp */
}
return (n);
}
/* determine if the transmit descriptor ring is full */
static int
{
return (VGEN_SUCCESS);
}
return (VGEN_FAILURE);
}
/* determine if timeout condition has occured */
static int
{
(vnet_ldcwd_txtimeout) &&
return (VGEN_SUCCESS);
} else {
return (VGEN_FAILURE);
}
}
/* transmit watchdog timeout handler */
static void
vgen_ldc_watchdog(void *arg)
{
int rv;
if (rv == VGEN_SUCCESS) {
#ifdef DEBUG
if (vgen_trigger_txtimeout) {
/* tx timeout triggered for debugging */
}
#endif
if (ldcp->need_resched) {
}
}
}
/* handler for error messages received from the peer ldc end-point */
static void
{
}
/* Check if the session id in the received message is valid */
static int
{
return (VGEN_FAILURE);
}
else
return (VGEN_SUCCESS);
}
static caddr_t
{
"%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5]);
return (ebuf);
}
/* Handshake watchdog timeout handler */
static void
vgen_hwatchdog(void *arg)
{
"handshake timeout ldc(%lx) phase(%x) state(%x)\n",
if (ldcp->cancel_htid) {
ldcp->cancel_htid = 0;
return;
}
}
static void
{
char ea[6];
"\tver_major: %d, ver_minor: %d, dev_class: %d\n",
"\taddr_type: %x, xfer_mode: %x, ack_freq: %x\n",
"\tldc_addr: 0x%lx, ldc_size: %ld\n",
}
static void
{
"\tldc_id: 0x%lx, ldc_status: 0x%x\n",
"\tlocal_sid: 0x%x, peer_sid: 0x%x\n",
"\thphase: 0x%x, hstate: 0x%x\n",
}
/*
* vgen_ldc_queue_data -- Queue data in the LDC.
*/
static void
{
/*
* If the receive thread is enabled, then the queue
* is protected by the soft_lock. After queuing, trigger
* the soft interrupt so that the interrupt handler sends these
* messages up the stack.
*
* If the receive thread is not enabled, then the list is
* automatically protected by the cblock lock, so no need
* to hold any additional locks.
*/
}
} else {
}
}
}
/*
* vgen_ldc_rcv_worker -- A per LDC worker thread to receive data.
* This thread is woken up by the LDC interrupt handler to process
* LDC packets and receive data.
*/
static void
vgen_ldc_rcv_worker(void *arg)
{
"vnet_rcv_thread");
/*
* Wait until the data is received or a stop
* request is received.
*/
while (!(ldcp->rcv_thr_flags &
(VGEN_WTHR_DATARCVD | VGEN_WTHR_STOP))) {
}
/*
* First process the stop request.
*/
break;
}
}
/*
* Update the run status and wakeup the thread that
* has sent the stop request.
*/
thread_exit();
}
/* vgen_stop_rcv_thread -- Co-ordinate with receive thread to stop it */
static void
{
/*
* Send a stop request by setting the stop flag and
* wait until the receive thread stops.
*/
}
}
}
/*
* vgen_ldc_rcv_softintr -- LDC Soft interrupt handler function.
* Its job is to pickup the recieved packets that are queued in the
* LDC and send them up.
*
* NOTE: An interrupt handler is being used to handle the upper
* layer(s) requirement to send up only at interrupt context.
*/
/* ARGSUSED */
static uint_t
{
}
return (DDI_INTR_CLAIMED);
}
#if DEBUG
/*
* Print debug messages - set to 0xf to enable all msgs
*/
static void
{
char buf[256];
}
}
}
}
#endif