/*
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
#include <sys/machsystm.h>
#include <sys/mac_ether.h>
#include <sys/mach_descrip.h>
#include <sys/vio_mailbox.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
#include <sys/vio_util.h>
static void vsw_marker_task(void *);
/* Interrupt routines */
/* Handshake routines */
static void vsw_ldc_reinit(vsw_ldc_t *);
static void vsw_conn_task(void *);
static void vsw_next_milestone(vsw_ldc_t *);
static int vsw_supported_version(vio_ver_msg_t *);
/* Data processing routines */
void vsw_process_pkt(void *);
static void vsw_process_ctrl_pkt(void *);
static void vsw_process_ctrl_ver_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_attr_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_mcst_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_rdx_pkt(vsw_ldc_t *, void *);
static void vsw_process_physlink_msg(vsw_ldc_t *, void *);
uint32_t);
static void vsw_process_pkt_data_nop(void *, void *, uint32_t);
static void vsw_process_pkt_data(void *, void *, uint32_t);
static void vsw_process_data_ibnd_pkt(vsw_ldc_t *, void *);
/* Packet creation routines */
static void vsw_send_ver(void *);
static void vsw_send_attr(vsw_ldc_t *);
static void vsw_send_dring_info(vsw_ldc_t *);
static void vsw_send_rdx(vsw_ldc_t *);
/* Dring routines */
static void vsw_create_privring(vsw_ldc_t *);
static void vsw_ldc_tx_worker(void *arg);
/* Misc support routines */
/* Debugging routines */
static void dump_flags(uint64_t);
static void display_state(void);
static void display_lane(lane_t *);
static void display_ring(dring_info_t *);
/*
* Functions imported from other files.
*/
extern void vsw_destroy_rxpools(void *arg);
int *);
extern void vsw_ldc_msg_worker(void *arg);
extern void vsw_process_dringdata(void *, void *);
extern void vsw_ldc_rcv_worker(void *arg);
extern void vsw_process_dringdata_shm(void *, void *);
/*
* Tunables used in this file.
*/
extern int vsw_num_handshakes;
extern int vsw_ldc_tx_delay;
extern int vsw_ldc_tx_retries;
extern int vsw_ldc_retries;
extern int vsw_ldc_delay;
extern boolean_t vsw_ldc_rxthr_enabled;
extern boolean_t vsw_ldc_txthr_enabled;
extern uint32_t vsw_num_descriptors;
extern uint8_t vsw_dring_mode;
extern uint32_t vsw_max_tx_qcount;
extern uint32_t vsw_publish_macaddr_count;
extern uint32_t vsw_nrbufs_factor;
/*
* VIO Protocol Version Info:
*
* The version specified below represents the version of protocol currently
* supported in the driver. It means the driver can negotiate with peers with
* versions <= this version. Here is a summary of the feature(s) that are
* supported at each version of the protocol:
*
* 1.0 Basic VIO protocol.
* 1.1 vDisk protocol update (no virtual network update).
* 1.2 Support for priority frames (priority-ether-types).
* 1.3 VLAN and HybridIO support.
* 1.4 Jumbo Frame support.
* 1.5 Link State Notification support with optional support
* for Physical Link information.
* 1.6 Support for RxDringData mode.
*/
/*
* For the moment the state dump routines have their own
* private flag.
*/
#define DUMP_STATE 0
#if DUMP_STATE
{ \
}
{ \
}
#else
#define DISPLAY_STATE()
#endif /* DUMP_STATE */
/*
* Attach the specified port.
*
* Returns 0 on success, 1 on failure.
*/
int
{
int rv;
/* port already exists? */
__func__, p->p_instance);
return (1);
}
}
goto exit_error;
}
/*
* If the underlying network device has been setup,
* then open a mac client and porgram the mac address
* for this port.
*/
if (rv != 0) {
goto exit_error;
}
}
/* link it into the list of ports for this vsw instance */
/*
* Initialise the port and any ldc's under it.
*/
/* announce macaddr of vnet to the physical switch */
if (vsw_publish_macaddr_count != 0) { /* enabled */
}
return (0);
return (1);
}
/*
* Detach the specified port.
*
* Returns 0 on success, 1 on failure.
*/
int
{
return (1);
}
return (1);
}
/* cleanup any HybridIO for this port */
/*
* No longer need to hold writer lock on port list now
* that we have unlinked the target port from the list.
*/
/* Cleanup and close the mac client */
/* Remove any multicast addresses.. */
return (0);
}
/*
* Detach all active ports.
*/
void
{
/* cleanup any HybridIO for this port */
/* Cleanup and close the mac client */
/* Remove any multicast addresses.. */
/*
* No longer need to hold the lock on the port list
* now that we have unlinked the target port from the
* list.
*/
}
}
/*
* Delete the specified port.
*/
static void
{
/*
* Wait for any pending ctrl msg tasks which reference this
* port to finish.
*/
/*
* Wait for any active callbacks to finish
*/
}
}
}
/*
* Attach a logical domain channel (ldc) under a specified port.
*
* Returns 0 on success, 1 on failure.
*/
static int
{
return (1);
}
ldcp->msg_thr_flags = 0;
ldcp->rcv_thr_flags = 0;
/* required for handshake with peer */
ldcp->peer_session = 0;
ldcp->session_status = 0;
/* only set for outbound lane, inbound set by peer */
if (status != 0) {
goto ldc_attach_fail;
}
if (vsw_ldc_txthr_enabled) {
ldcp->tx_thr_flags = 0;
goto ldc_attach_fail;
}
}
if (status != 0) {
goto ldc_attach_fail;
}
/*
* allocate a message for ldc_read()s, big enough to hold ctrl and
* data msgs, including raw data msgs used to recv priority frames.
*/
goto ldc_attach_fail;
}
goto ldc_attach_fail;
}
/* link it into this port */
return (0);
if (progress & PROG_callback) {
}
if (progress & PROG_tx_thread) {
}
}
}
return (1);
}
/*
* Detach a logical domain channel (ldc) belonging to a
* particular port.
*/
static void
{
int rv;
int retries = 0;
}
/* Stop the tx thread */
}
}
/* Destory kstats */
/*
* Before we can close the channel we must release any mapped
* resources (e.g. drings).
*/
/*
* Close the channel, retry on EAAGIN.
*/
if (++retries > vsw_ldc_retries) {
break;
}
}
if (rv != 0) {
"!vsw%d: Error(%d) closing the channel(0x%lx)\n",
}
}
/*
* Open and attempt to bring up the channel. Note that channel
* can only be brought up if peer has also opened channel.
*
* Returns 0 if can open and bring up channel, otherwise
* returns 1.
*/
static int
{
int rv;
/* don't start at 0 in case clients don't like that */
if (rv != 0) {
return (1);
}
return (1);
return (1);
}
if (rv != 0) {
/*
* Not a fatal error for ldc_up() to fail, as peer
* end point may simply not be ready yet.
*/
return (1);
}
/*
* ldc_up() call is non-blocking so need to explicitly
* check channel status to see if in fact the channel
* is UP.
*/
return (1);
}
return (0);
}
return (0);
}
/* disable callbacks on the channel */
static void
{
int rv;
if (rv != 0) {
}
}
/*
* Wait until the callback(s) associated with the ldcs under the specified
* port have completed.
*
* Prior to this function being invoked each channel under this port
* should have been quiesced via ldc_set_cb_mode(DISABLE).
*
* A short explaination of what we are doing below..
*
* The simplest approach would be to have a reference counter in
* the ldc structure which is increment/decremented by the callbacks as
* they use the channel. The drain function could then simply disable any
* further callbacks and do a cv_wait for the ref to hit zero. Unfortunately
* there is a tiny window here - before the callback is able to get the lock
* on the channel it is interrupted and this function gets to execute. It
* sees that the ref count is zero and believes its free to delete the
* associated data structures.
*
* We get around this by taking advantage of the fact that before the ldc
* framework invokes a callback it sets a flag to indicate that there is a
* callback active (or about to become active). If when we attempt to
* unregister a callback when this active flag is set then the unregister
* will fail with EWOULDBLOCK.
*
* If the unregister fails we do a cv_timedwait. We will either be signaled
* by the callback as it is exiting (note we have to wait a short period to
* allow the callback to return fully to the ldc framework and it to clear
* the active flag), or by the timer expiring. In either case we again attempt
* the unregister. We repeat this until we can succesfully unregister the
* callback.
*
* The reason we use a cv_timedwait rather than a simple cv_wait is to catch
* the case where the callback has finished but the ldc framework has not yet
* cleared the active flag. In this case we would never get a cv_signal.
*/
static void
{
/*
* If we can unregister the channel callback then we
* know that there is no callback either running or
* scheduled to run for this channel so move on to next
* channel in the list.
*/
/* prompt active callbacks to quit */
} else {
/*
* If we end up here we know that either 1) a callback
* is currently executing, 2) is about to start (i.e.
* the ldc framework has set the active flag but
* has not actually invoked the callback yet, or 3)
* has finished and has returned to the ldc framework
* but the ldc framework has not yet cleared the
* active bit.
*
* Wait for it to finish.
*/
}
}
}
/*
* Wait until all tasks which reference this port have completed.
*
* Prior to this function being invoked each channel under this port
* should have been quiesced via ldc_set_cb_mode(DISABLE).
*/
static void
{
/*
* Mark the port as in the process of being detached, and
* dispatch a marker task to the queue so we know when all
* relevant tasks have completed.
*/
return;
}
/*
* Wait for the marker task to finish.
*/
}
static void
{
/*
* No further tasks should be dispatched which reference
* this port so ok to mark it as safe to detach.
*/
}
{
return (port);
}
}
return (NULL);
}
void
{
/*
* If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
* the connection. See comments in vsw_set_vnet_proto_ops().
*/
}
}
void
{
/*
* If the peer is HybridIO capable (ver >= 1.3), reset channel
* to trigger re-negotiation, which inturn trigger HybridIO
*/
} else {
}
}
}
void
{
/*
* reset channel and terminate the connection.
*/
}
void
{
}
}
}
static void
{
if (plink_state == LINK_STATE_UP) {
} else {
}
}
static void
{
/*
* If handshake has completed successfully and if the vnet device
* has negotiated to get physical link state updates, send a message
* with the current state.
*/
}
}
void
{
}
}
/*
* Search for and remove the specified port from the port
* list. Returns 0 if able to locate and remove port, otherwise
* returns 1.
*/
static int
{
return (1);
} else {
}
break;
} else {
}
}
return (0);
}
/*
* Interrupt handler for ldc messages.
*/
static uint_t
{
return (LDC_SUCCESS);
}
if (event & LDC_EVT_UP) {
/*
* Channel has come up.
*/
}
if (event & LDC_EVT_READ) {
/*
* Data available for reading.
*/
goto vsw_cb_exit;
}
}
/*
* Catch either LDC_EVT_WRITE which we don't support or any
* unknown event.
*/
if (event &
}
/*
* Let the drain function know we are finishing if it
* is waiting.
*/
return (LDC_SUCCESS);
}
/*
* Reinitialise data structures associated with the channel.
*/
static void
{
/*
* Remove parent port from any multicast groups
* it may have registered with. Client must resend
* multicast add command after handshake completes.
*/
ldcp->peer_session = 0;
ldcp->session_status = 0;
}
/*
* Process a connection event.
*/
void
{
/*
* Check if either a reset or restart event is pending
* or in progress. If so just return.
*
* A VSW_CONN_RESET event originates either with a LDC_RESET_EVT
* being received by the callback handler, or a ECONNRESET error
* code being returned from a ldc_read() or ldc_write() call.
*
* A VSW_CONN_RESTART event occurs when some error checking code
* decides that there is a problem with data from the channel,
* and that the handshake should be restarted.
*/
return;
/*
* If it is an LDC_UP event we first check the recorded
* state of the channel. If this is UP then we know that
* the channel moving to the UP state has already been dealt
* with and don't need to dispatch a new task.
*
* The reason for this check is that when we do a ldc_up(),
* depending on the state of the peer, we may or may not get
* a LDC_UP event. As we can't depend on getting a LDC_UP evt
* every time we do ldc_up() we explicitly check the channel
* status to see has it come up (ldc_up() is asynch and will
* complete at some undefined time), and take the appropriate
* action.
*
* The flip side of this is that we may get a LDC_UP event
* when we have already seen that the channel is up and have
* dealt with that.
*/
if (evt == VSW_CONN_UP) {
return;
}
}
/*
* The transaction group id allows us to identify and discard
* any tasks which are still pending on the taskq and refer
* to the handshake session we are about to restart or reset.
* These stale messages no longer have any real meaning.
*/
goto err_exit;
}
DDI_NOSLEEP) != DDI_SUCCESS) {
goto err_exit;
}
return;
/*
* Have mostly likely failed due to memory shortage. Clear the flag so
* that future requests will at least be attempted and will hopefully
* succeed.
*/
ldcp->reset_active = 0;
}
/*
* Deal with events relating to a connection. Invoked from a taskq.
*/
static void
{
/* can safely free now have copied out data */
}
return;
}
/*
* If we wish to restart the handshake on this channel, then if
* the channel is UP we bring it DOWN to flush the underlying
* ldc queue.
*/
}
/*
* re-init all the associated data structures.
*/
/*
* Bring the channel back up (note it does no harm to
* do this even if the channel is already UP, Just
* becomes effectively a no-op).
*/
/*
* Check if channel is now UP. This will only happen if
* peer has also done a ldc_up().
*/
return;
}
/* channel UP so restart handshake by sending version info */
if (curr_status == LDC_UP) {
" handshake attempts (%d) on channel %ld",
return;
}
if (vsw_obp_ver_proto_workaround == B_FALSE &&
DDI_NOSLEEP) != DDI_SUCCESS)) {
/*
* Don't count as valid restart attempt if couldn't
* send version msg.
*/
}
}
/*
* Mark that the process is complete by clearing the flag.
*
* Note is it possible that the taskq dispatch above may have failed,
* most likely due to memory shortage. We still clear the flag so
* future attempts will at least be attempted and will hopefully
* succeed.
*/
ldcp->reset_active = 0;
}
/*
* returns 0 if legal for event signified by flag to have
* occured at the time it did. Otherwise returns 1.
*/
int
{
else
switch (flag) {
case VSW_VER_INFO_RECV:
if (phase > VSW_MILESTONE0) {
return (1);
}
break;
case VSW_VER_ACK_RECV:
case VSW_VER_NACK_RECV:
if (!(state & VSW_VER_INFO_SENT)) {
return (1);
} else
state &= ~VSW_VER_INFO_SENT;
break;
case VSW_ATTR_INFO_RECV:
return (1);
}
break;
case VSW_ATTR_ACK_RECV:
case VSW_ATTR_NACK_RECV:
if (!(state & VSW_ATTR_INFO_SENT)) {
" or ATTR_NACK when in state %d\n",
return (1);
} else
state &= ~VSW_ATTR_INFO_SENT;
break;
case VSW_DRING_INFO_RECV:
if (phase < VSW_MILESTONE1) {
return (1);
}
break;
case VSW_DRING_ACK_RECV:
case VSW_DRING_NACK_RECV:
if (!(state & VSW_DRING_INFO_SENT)) {
" or DRING_NACK when in state %d\n",
return (1);
} else
state &= ~VSW_DRING_INFO_SENT;
break;
case VSW_RDX_INFO_RECV:
if (phase < VSW_MILESTONE3) {
return (1);
}
break;
case VSW_RDX_ACK_RECV:
case VSW_RDX_NACK_RECV:
if (!(state & VSW_RDX_INFO_SENT)) {
return (1);
} else
state &= ~VSW_RDX_INFO_SENT;
break;
case VSW_MCST_INFO_RECV:
if (phase < VSW_MILESTONE3) {
return (1);
}
break;
default:
return (1);
}
else
return (0);
}
void
{
case VSW_MILESTONE0:
/*
* If we haven't started to handshake with our peer,
* start to do so now.
*/
}
/*
* Only way to pass this milestone is to have successfully
* negotiated version info.
*/
/*
* Next milestone is passed when attribute
* information has been successfully exchanged.
*/
}
break;
case VSW_MILESTONE1:
/*
* Only way to pass this milestone is to have successfully
* negotiated attribute information, in both directions.
*/
break;
}
/*
* If the peer device has said it wishes to
* use descriptor rings then we send it our ring
* info, otherwise we just set up a private ring
* which we use an internal buffer
*/
break;
}
/*
* The peer doesn't operate in dring mode; we
* can simply fallthru to the RDX phase from
* here.
*/
/*FALLTHRU*/
case VSW_MILESTONE2:
/*
* If peer has indicated in its attribute message that
* it wishes to use descriptor rings then the only way
* to pass this milestone is for us to have received
* valid dring info.
*
* If peer is not using descriptor rings then just fall
* through.
*/
VIO_DRING_MODE_V1_0))) {
break;
}
break;
case VSW_MILESTONE3:
/*
* Pass this milestone when all paramaters have been
* successfully exchanged and RDX sent in both directions.
*
* Mark the relevant lane as available to transmit data. In
* RxDringData mode, lane_in is associated with transmit and
* lane_out is associated with receive. It is the reverse in
* TxDring mode.
*/
} else {
}
/* Start HIO if enabled and capable */
}
/*
* The vnet device has negotiated to get phys
* link updates. Now that the handshake with
* the vnet device is complete, send an initial
* update with the current physical link state.
*/
}
} else {
}
break;
case VSW_MILESTONE4:
break;
default:
}
}
/*
* Check if major version is supported.
*
* Returns 0 if finds supported major number, and if necessary
* adjusts the minor field.
*
* to next lowest support values, or to zero if no other values possible.
*/
static int
{
int i;
for (i = 0; i < VSW_NUM_VER; i++) {
/*
* Matching or lower major version found. Update
* minor number if necessary.
*/
vsw_versions[i].ver_minor);
}
return (0);
}
/*
* If the message contains a higher major version number, set
* and return false, so this message will get resent with
* these values.
*/
"values to %d, %d\n",
vsw_versions[i].ver_minor);
return (1);
}
}
/* No match was possible, zero out fields */
return (1);
}
/*
* Set vnet-protocol-version dependent functions based on version.
*/
static void
{
/*
* Setup the appropriate dring data processing routine and any
* associated thread based on the version.
*
* In versions < 1.6, we support only TxDring mode. In this mode, the
* msg worker thread processes all types of VIO msgs (ctrl and data).
*
* In versions >= 1.6, we also support RxDringData mode. In this mode,
* the rcv worker thread processes dring data messages (msgtype:
* VIO_TYPE_DATA, subtype: VIO_SUBTYPE_INFO, env: VIO_DRING_DATA). The
* rest of the data messages (including acks) and ctrl messages are
* handled directly by the callback (intr) thread.
*
* However, for versions >= 1.6, we could still fallback to TxDring
* mode. This could happen if RxDringData mode has been disabled (see
* below) on this guest or on the peer guest. This info is determined
* as part of attr exchange phase of handshake. Hence, we setup these
* pointers for v1.6 after attr msg phase completes during handshake.
*/
/*
* Set data dring mode for vsw_send_attr(). We setup msg worker
* thread in TxDring mode or rcv worker thread in RxDringData
* mode when attr phase of handshake completes.
*/
} else {
}
} else {
}
/*
* Setup the MTU for attribute negotiation based on the version.
*/
/*
* If the version negotiated with peer is >= 1.4(Jumbo Frame
* Support), set the mtu in our attributes to max_frame_size.
*/
/*
* If the version negotiated with peer is == 1.3 (Vlan Tag
* Support) set the attr.mtu to ETHERMAX + VLAN_TAGSZ.
*/
} else {
/*
* Pre-1.3 peers expect max frame size of ETHERMAX.
* We can negotiate that size with those peers provided only
* pvid is defined for our peer and there are no vids. Then we
* Note that pvid of the peer can be different, as vsw has to
* serve the vnet in that vlan even if itself is not assigned
* to that vlan.
*/
}
}
/*
* Setup version dependent data processing functions.
*/
/* Versions >= 1.2 */
if (VSW_PRI_ETH_DEFINED(vswp)) {
/*
* enable priority routines and pkt mode only if
* at least one pri-eth-type is specified in MD.
*/
/* set xfer mode for vsw_send_attr() */
} else {
/* no priority eth types defined in MD */
/* set xfer mode for vsw_send_attr() */
}
} else {
/* Versions prior to 1.2 */
}
}
/*
* Reset vnet-protocol-version dependent functions to v1.0.
*/
static void
{
/* set xfer mode for vsw_send_attr() */
}
static void
{
/*
* TxDring mode; wakeup message worker
* thread to process the VIO messages.
*/
}
} else {
/*
* We invoke vsw_process_pkt() in the context of the LDC
* callback (vsw_ldc_cb()) during handshake, until the dring
* mode is negotiated. After the dring mode is negotiated, the
* msgs are processed by the msg worker thread (above case) if
* the dring mode is TxDring. Otherwise (in RxDringData mode)
* we continue to process the msgs directly in the callback
* context.
*/
}
}
/*
* Main routine for processing messages received over LDC.
*/
void
{
int rv = 0;
/*
* If channel is up read messages until channel is empty.
*/
do {
if (rv != 0) {
}
/* channel has been reset */
if (rv == ECONNRESET) {
break;
}
if (msglen == 0) {
break;
}
/*
* Figure out what sort of packet we have gotten by
* examining the msg tag, and then switch it appropriately.
*/
switch (tagp->vio_msgtype) {
case VIO_TYPE_CTRL:
break;
case VIO_TYPE_DATA:
break;
case VIO_TYPE_ERR:
break;
default:
break;
}
} while (msglen);
}
/*
* Dispatch a task to process a VIO control message.
*/
static void
int msglen)
{
/*
* We need to handle RDX ACK messages in-band as once they
* are exchanged it is possible that we will get an
* immediate (legitimate) data packet.
*/
return;
"(ostate 0x%llx : hphase %d)", __func__,
return;
}
return;
}
/*
* Dispatch task to processing taskq if port is not in
* the process of being detached.
*/
__func__);
return;
}
} else {
}
}
/*
* Process a VIO ctrl message. Invoked from taskq.
*/
static void
{
/* stale pkt check */
return;
}
/* session id check */
return;
}
}
/*
* Switch on vio_subtype envelope, then let lower routines
* decide if its an INFO, ACK or NACK packet.
*/
switch (env) {
case VIO_VER_INFO:
break;
case VIO_DRING_REG:
break;
case VIO_DRING_UNREG:
break;
case VIO_ATTR_INFO:
break;
case VNET_MCAST_INFO:
break;
case VIO_RDX:
break;
case VIO_DDS_INFO:
break;
case VNET_PHYSLINK_INFO:
break;
default:
}
}
/*
* Version negotiation. We can end up here either because our peer
* has responded to a handshake message we have sent it, or our peer
* has initiated a handshake with us. If its the former then can only
* be ACK or NACK, if its the later can only be INFO.
*
* If its an ACK we move to the next stage of the handshake, namely
* attribute exchange. If its a NACK we see if we can specify another
* version, if we can't we stop.
*
* If it is an INFO we reset all params associated with communication
* in that direction over this channel (remember connection is
* essentially 2 independent simplex channels).
*/
void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
/*
* Record the session id, which we will use from now
* until we see another VER_INFO msg. Even then the
* session id in most cases will be unchanged, execpt
* if channel was reset.
*/
}
/* Legal message at this time ? */
return;
/*
* First check the device class. Currently only expect
* to be talking to a network device. In the future may
* also talk to another switch.
*/
return;
} else {
}
/*
* Now check the version.
*/
if (vsw_supported_version(ver_pkt) == 0) {
/*
* Support this major version and possibly
* adjusted minor version.
*/
/* Store accepted values */
if (vsw_obp_ver_proto_workaround == B_TRUE) {
/*
* Send a version info message
* using the accepted version that
* we are about to ack. Also note that
* we send our ver info before we ack.
* Otherwise, as soon as receiving the
* ack, obp sends attr info msg, which
* breaks vsw_check_flag() invoked
* from vsw_process_ctrl_attr_pkt();
* as we also need VSW_VER_ACK_RECV to
* be set in lane_out.lstate, before
* we can receive attr info.
*/
}
} else {
/*
* pairing we support (if don't suuport any more
* versions then they will be set to zero.
*/
/* Store updated values */
}
break;
case VIO_SUBTYPE_ACK:
return;
/* Store updated values */
break;
case VIO_SUBTYPE_NACK:
return;
/*
* If our peer sent us a NACK with the ver fields set to
* zero then there is nothing more we can do. Otherwise see
* if we support either the version suggested, or a lesser
* one.
*/
"further.", __func__);
return;
}
/*
* Check to see if we support this major version or
* to zero.
*/
(void) vsw_supported_version(ver_pkt);
/* Nothing more we can do */
__func__);
} else {
/* found a supported major version */
}
break;
default:
}
}
static int
{
int i;
return (1);
}
return (1);
}
/* Only support MAC addresses at moment. */
return (1);
}
/*
* MAC address supplied by device should match that stored
* in the vsw-port OBP node. Need to decide what to do if they
* don't match, for the moment just warn but don't fail.
*/
"0x%llx doesn't match node address 0x%llx\n",
}
/*
* Ack freq only makes sense in pkt mode, in shared
* mode the ring descriptors say whether or not to
* send back an ACK.
*/
__func__);
return (1);
}
}
/*
* Process dring mode attribute.
*/
/*
* Versions >= 1.6:
* Though we are operating in v1.6 mode, it is possible that
* RxDringData mode has been disabled either on this guest or
* on the peer guest. If so, we revert to pre v1.6 behavior of
* TxDring mode. But this must be agreed upon in both
* directions of attr exchange. We first determine the mode
* that can be negotiated.
*/
/*
* The peer is capable of handling RxDringData AND we
* are also capable of it; we enable RxDringData mode
* on this channel.
*/
/*
* If the peer is capable of TxDring mode, we
* negotiate TxDring mode on this channel.
*/
} else {
/*
* We support only VIO_TX_DRING and VIO_RX_DRING_DATA
* modes. We don't support VIO_RX_DRING mode.
*/
return (1);
}
/*
* If we have received an ack for the attr info that we sent,
* then check if the dring mode matches what the peer had ack'd
* (saved in lane_out). If they don't match, we fail the
* handshake.
*/
/* send NACK */
return (1);
}
} else {
/*
* Save the negotiated dring mode in our attr
* parameters, so it gets sent in the attr info from us
* to the peer.
*/
}
/* save the negotiated dring mode in the msg to be replied */
}
/*
* Process MTU attribute.
*/
/*
* Versions >= 1.4:
* Validate mtu of the peer is at least ETHERMAX. Then, the mtu
* is negotiated down to the minimum of our mtu and peer's mtu.
*/
return (1);
}
/*
* If we have received an ack for the attr info
* that we sent, then check if the mtu computed
* above matches the mtu that the peer had ack'd
* (saved in local hparams). If they don't
* match, we fail the handshake.
*/
/* send NACK */
return (1);
}
} else {
/*
* Save the mtu computed above in our
* attr parameters, so it gets sent in
* the attr info from us to the peer.
*/
}
/* save the MIN mtu in the msg to be replied */
} else {
/* Versions < 1.4, mtu must match */
return (1);
}
}
/*
* Otherwise store attributes for this lane and update
* lane state.
*/
/*
* Check if the client has requested physlink state updates.
* If there is a physical device bound to this vswitch (L2
* mode), set the ack bits to indicate it is supported.
* Otherwise, set the nack bits.
*/
/* Does the vnet need phys link state updates ? */
if ((lane_in->physlink_update &
/* is a net-dev assigned to us ? */
} else {
/* not in L2 mode */
}
} else {
}
} else {
/*
* physlink_update bits are ignored
* if set by clients < v1.5 protocol.
*/
}
for (i = ETHERADDRL - 1; i >= 0; i--) {
macaddr >>= 8;
}
/*
* Setup device specific xmit routines. Note this could be changed
* further in vsw_send_dring_info() for versions >= 1.6 if operating in
* RxDringData mode.
*/
}
/*
* HybridIO is supported only vnet, not by OBP.
* So, set hio_capable to true only when in DRING mode.
*/
} else {
}
return (0);
}
static int
{
return (1);
}
/*
* Process dring mode attribute.
*/
/*
* Versions >= 1.6:
* The ack msg sent by the peer contains the negotiated dring
* mode between our capability (that we had sent in our attr
* info) and the peer's capability.
*/
/*
* If we have sent an ack for the attr info msg from
* the peer, check if the dring mode that was
* negotiated then (saved in lane_out) matches the
* mode that the peer has ack'd. If they don't match,
* we fail the handshake.
*/
return (1);
}
} else {
/*
* Peer ack'd with a mode that we don't
* support; we fail the handshake.
*/
return (1);
}
== (VIO_TX_DRING|VIO_RX_DRING_DATA)) {
/*
* Peer must ack with only one negotiated mode.
* Otherwise fail handshake.
*/
return (1);
}
/*
* Save the negotiated mode, so we can validate it when
* we receive attr info from the peer.
*/
}
}
/*
* Process MTU attribute.
*/
/*
* Versions >= 1.4:
* The ack msg sent by the peer contains the minimum of
* our mtu (that we had sent in our attr info) and the
* peer's mtu.
*
* If we have sent an ack for the attr info msg from
* the peer, check if the mtu that was computed then
* (saved in lane_out params) matches the mtu that the
* peer has ack'd. If they don't match, we fail the
* handshake.
*/
return (1);
}
} else {
/*
* If the mtu ack'd by the peer is > our mtu
* fail handshake. Otherwise, save the mtu, so
* we can validate it when we receive attr info
* from our peer.
*/
} else {
return (1);
}
}
}
return (0);
}
/*
* Process an attribute packet. We can end up here either because our peer
* peer has sent us an attribute INFO message
*
* If its an ACK we then move to the next stage of the handshake which
* is to send our descriptor ring info to our peer. If its a NACK then
* there is nothing more we can (currently) do.
*
* If we get a valid/acceptable INFO packet (and we have already negotiated
* a version) we ACK back and set channel state to ATTR_RECV, otherwise we
* NACK back and reset channel state to INACTIV.
*
* FUTURE: in time we will probably negotiate over attributes, but for
* the moment unacceptable attributes are regarded as a fatal error.
*
*/
void
{
int rv;
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
if (rv != 0) {
} else {
}
break;
case VIO_SUBTYPE_ACK:
if (rv != 0) {
return;
}
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
}
}
static int
{
int rv;
if (rv != 0) {
return (1);
}
/*
* The earlier version of Solaris vnet driver doesn't set the
* option (VIO_TX_DRING in its case) correctly in its dring reg
* message. We workaround that here by doing the check only
* for versions >= v1.6.
*/
return (1);
}
/*
* Map dring exported by the peer.
*/
return (1);
}
/*
* Map data buffers exported by the peer if we are in RxDringData mode.
*/
if (rv != 0) {
return (1);
}
}
return (0);
}
static int
{
return (1);
}
/* save dring_ident acked by peer */
return (0);
}
/*
* Process a dring info packet. We can end up here either because our peer
* peer has sent us a dring INFO message.
*
* If we get a valid/acceptable INFO packet (and we have already negotiated
* a version) we ACK back and update the lane state, otherwise we NACK back.
*
* FUTURE: nothing to stop client from sending us info on multiple dring's
* but for the moment we will just use the first one we are given.
*
*/
void
{
int rv;
int msgsize;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
if (rv != 0) {
} else {
}
msgsize =
} else {
msgsize = sizeof (vio_dring_reg_msg_t);
}
break;
case VIO_SUBTYPE_ACK:
if (rv != 0) {
return;
}
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
tagp->vio_subtype);
}
}
/*
* Process a request from peer to unregister a dring.
*
* For the moment we just restart the handshake if our
* peer endpoint attempts to unregister a dring.
*/
void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
break;
case VIO_SUBTYPE_ACK:
break;
case VIO_SUBTYPE_NACK:
break;
default:
}
}
sizeof (vnet_mcast_msg_t), B_TRUE);
/*
* Process a multicast request from a vnet.
*
* Vnet's specify a multicast address that they are interested in. This
* address is used as a key into the hash table which forms the multicast
* forwarding database (mFDB).
*
* The table keys are the multicast addresses, while the table entries
* are pointers to lists of ports which wish to receive packets for the
* specified multicast address.
*
* When a multicast packet is being switched we use the address as a key
* into the hash table, and then walk the appropriate port list forwarding
* the pkt to each port in turn.
*
* If a vnet is no longer interested in a particular multicast grouping
* we simply find the correct location in the hash table and then delete
* the relevant port from the port list.
*
* To deal with the case whereby a port is being deleted without first
* removing itself from the lists in the hash table, we maintain a list
* of multicast addresses the port has registered an interest in, within
* the port structure itself. We then simply walk that list of addresses
* using them as keys into the hash table and remove the port from the
* appropriate lists.
*/
static void
{
int i;
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
/*
* Check if in correct state to receive a multicast
* message (i.e. handshake complete). If not reset
* the handshake.
*/
return;
/*
* Before attempting to add or remove address check
* that they are valid multicast addresses.
* If not, then NACK back.
*/
__func__);
return;
}
}
/*
* NACK back.
*/
return;
}
break;
case VIO_SUBTYPE_ACK:
/*
* We shouldn't ever get a multicast ACK message as
* at the moment we never request multicast addresses
* to be set on some other device. This may change in
* the future if we have cascading switches.
*/
return;
/* Do nothing */
break;
case VIO_SUBTYPE_NACK:
/*
* We shouldn't get a multicast NACK packet for the
* same reasons as we shouldn't get a ACK packet.
*/
return;
/* Do nothing */
break;
default:
}
}
static void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
return;
break;
case VIO_SUBTYPE_ACK:
/*
* Should be handled in-band by callback handler.
*/
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
}
}
static void
{
case VIO_SUBTYPE_INFO:
/* vsw shouldn't recv physlink info */
break;
case VIO_SUBTYPE_ACK:
break;
case VIO_SUBTYPE_NACK:
break;
default:
}
}
static void
{
/* session id check */
return;
}
}
/*
* It is an error for us to be getting data packets
* before the handshake has completed.
*/
return;
}
if (dring_mode == VIO_TX_DRING) {
/*
* To reduce the locking contention, release the ldc_cblock
* here and re-acquire it once we are done receiving packets.
* We do this only in TxDring mode to allow further callbaks to
* continue while the msg worker thread processes the messages.
* In RxDringData mode, we process the messages in the callback
* itself and wake up rcv worker thread to process only data
* info messages.
*/
}
/*
* Switch on vio_subtype envelope, then let lower routines
* decide if its an INFO, ACK or NACK packet.
*/
if (env == VIO_DRING_DATA) {
} else if (env == VIO_PKT_DATA) {
} else if (env == VIO_DESC_DATA) {
} else {
}
if (dring_mode == VIO_TX_DRING) {
}
}
/*
* dummy pkt data handler function for vnet protocol version 1.0
*/
static void
{
}
/*
* This function handles raw pkt data messages received over the channel.
* Currently, only priority-eth-type frames are received through this mechanism.
* In this case, the frame(data) is present within the message itself which
* is copied into an mblk before switching it.
*/
static void
{
return;
}
"unable to process priority frame\n", __func__,
return;
}
} else {
}
/* skip over the extra space for vlan tag */
/* copy the frame from the payload of raw data msg into the mblk */
}
/* update stats */
/*
* VLAN_TAGSZ of extra space has been pre-alloc'd if tag is needed.
*/
/* switch the frame to destination */
}
/*
* Process an in-band descriptor message (most likely from
* OBP).
*/
static void
{
int i, rv;
int j = 0;
case VIO_SUBTYPE_INFO:
return;
/*
* Data is padded to align on a 8 byte boundary,
* nbytes is actual data length, i.e. minus that
* padding.
*/
/*
* allocb(9F) returns an aligned data block. We
* need to ensure that we ask ldc for an aligned
* number of bytes also.
*/
if (nbytes & 0x7) {
}
/* alloc extra space for VLAN_TAG */
return;
}
/* skip over the extra space for VLAN_TAG */
if (rv != 0) {
return;
}
/* point to the actual end of data */
/*
* We ACK back every in-band descriptor message we process
*/
/*
* there is extra space alloc'd for VLAN_TAG
*/
/* send the packet to be switched */
break;
case VIO_SUBTYPE_ACK:
/* Verify the ACK is valid */
if (idx >= vsw_num_descriptors) {
return;
}
return;
}
/*
* If the descriptor we are being ACK'ed for is not the
* one we expected, then pkts were lost somwhere, either
* when we tried to send a msg, or a previous ACK msg from
* our peer. In either case we now reclaim the descriptors
* in the range from the last ACK we received up to the
* current ACK.
*/
}
/*
* When we sent the in-band message to our peer we
* marked the copy in our private ring as READY. We now
* check that the descriptor we are being ACK'ed for is in
* fact READY, i.e. it is one we have shared with our peer.
*
* If its not we flag an error, but still reset the descr
* back to FREE.
*/
"READY (0x%lx)", __func__,
"datalen %ld", __func__,
}
/* release resources associated with sent msg */
}
/* update to next expected value */
break;
case VIO_SUBTYPE_NACK:
/*
* We should only get a NACK if our peer doesn't like
* something about a message we have sent it. If this
* happens we just release the resources associated with
* the message. (We are relying on higher layers to decide
* whether or not to resend.
*/
/* limit check */
if (idx >= vsw_num_descriptors) {
return;
}
return;
}
/* move to correct location in ring */
/* release resources associated with sent msg */
break;
default:
}
}
static void
{
/*
* Error vio_subtypes have yet to be defined. So for
* the moment we can't do anything.
*/
}
/* transmit the packet over the given port */
int
{
int count;
int status = 0;
if (count != 0) {
}
return (status);
}
/*
* Break up frames into 2 seperate chains: normal and
* priority, based on the frame type. The number of
* priority frames is also counted and returned.
*
* Params:
* vswp: pointer to the instance of vsw
* np: head of packet chain to be broken
* npt: tail of packet chain to be broken
*
* Returns:
* np: head of normal data packets
* npt: tail of normal data packets
* hp: head of high priority packets
* hpt: tail of high priority packets
*/
static uint32_t
{
int i;
for (i = 0; i < num_types; i++) {
/* high priority frame */
} else {
}
count++;
break;
}
}
if (i == num_types) {
/* normal data frame */
} else {
}
}
}
return (count);
}
/*
*/
static int
{
uint32_t n = 0;
/* gather any priority frames from the chain of packets */
/* transmit priority frames */
}
count -= n;
if (count == 0) {
/* no normal data frames to process */
return (0);
}
}
/*
* Wrapper function to transmit normal frames over the channel.
*/
static int
{
/*
* If the TX thread is enabled, then queue the
* ordinary frames and signal the tx thread.
*/
/*
* If we reached queue limit,
* do not queue new packets,
* drop them.
*/
goto exit;
}
} else {
}
} else {
}
}
exit:
return (0);
}
/*
* This function transmits the frame in the payload of a raw data
* (VIO_PKT_DATA) message. Thus, it provides an Out-Of-Band path to
* send special frames with high priorities, without going through
* the normal data path which uses descriptor ring mechanism.
*/
static void
{
int rv;
goto send_pkt_exit;
}
/* frame size bigger than available payload len of raw data msg ? */
goto send_pkt_exit;
}
/* alloc space for a raw data message */
goto send_pkt_exit;
} else {
}
/* copy frame into the payload of raw data message */
}
/* setup the raw data msg */
/* send the msg over ldc */
if (rv != 0) {
goto send_pkt_exit;
}
/* update stats */
}
/*
* Transmit the packet over the given LDC channel.
*
* The 'retries' argument indicates how many times a packet
* is retried before it is dropped. Note, the retry is done
* only for a resource related failure, for all other failures
* the packet is dropped immediately.
*/
static int
{
int i;
int rc;
int status = 0;
for (i = 0; i < retries; ) {
/*
* Send the message out using the appropriate
* transmit function which will free mblock when it
* is finished with it.
*/
}
if (status == LDC_TX_SUCCESS) {
break;
}
i++; /* increment the counter here */
/* If its the last retry, then update the oerror */
}
if (status != LDC_TX_NORESOURCES) {
/*
* No retrying required for errors un-related
* to resources.
*/
break;
}
/* Need to reclaim in TxDring mode. */
}
} else {
/*
* If there is no dring or the xfer_mode is
* set to DESC_MODE(ie., OBP), then simply break here.
*/
break;
}
/*
* Delay only if none were reclaimed
* and its not the last retry.
*/
}
}
return (status);
}
/*
* Send an in-band descriptor message over ldc.
*/
static int
{
int idx, i;
return (LDC_TX_FAILURE);
}
/*
* The dring here is as an internal buffer,
* rather than a transfer channel.
*/
return (LDC_TX_FAILURE);
}
return (LDC_TX_FAILURE);
}
/*
* Find a free descriptor in our buffer ring
*/
if (warn_msg) {
warn_msg = 0;
}
/* nothing more we can do */
goto vsw_descrsend_free_exit;
} else {
warn_msg = 1;
}
/* copy data into the descriptor */
bufp += n;
}
/* create and send the in-band descp msg */
/*
* Copy the mem cookies describing the data from the
* private region of the descriptor ring into the inband
* descriptor.
*/
sizeof (ldc_mem_cookie_t));
}
return (status);
}
static void
{
if (vsw_obp_ver_proto_workaround == B_FALSE) {
} else {
/* use the major,minor that we've ack'd */
}
}
static void
{
/*
* Subtype is set to INFO by default
*/
/* payload copied from default settings for lane */
}
static void
{
int msgsize;
void *msg;
/* dring mode has been negotiated in attr phase; save in stats */
/*
* Change the transmit routine for RxDringData mode.
*/
return;
}
msgsize =
} else {
return;
}
msgsize = sizeof (vio_dring_reg_msg_t);
}
}
static void
{
}
/*
* Remove the specified address from the list of address maintained
* in this port node.
*/
{
if (devtype == VSW_VNETPORT) {
} else {
}
/* match found */
/* list head */
if (devtype == VSW_VNETPORT)
else
} else {
}
break;
} else {
}
}
if (devtype == VSW_VNETPORT)
else
return (curr_p);
}
/*
* Create a ring consisting of just a private portion and link
* it into the list of rings for the outbound lane.
*
* These type of rings are used primarily for temporary data
* storage (i.e. as data buffers).
*/
void
{
/* no public section */
return;
}
/* haven't used any descriptors yet */
}
/*
* Set the default lane attributes. These are copied into
* the attr msg we send to our peer. If they are not acceptable
* then (currently) the handshake ends.
*/
static void
{
}
/*
* Map the descriptor ring exported by the peer.
*/
static dring_info_t *
{
/*
* In RxDringData mode, dring that we map in
* becomes our transmit descriptor ring.
*/
} else {
/*
* In TxDring mode, dring that we map in
* becomes our receive descriptor ring.
*/
}
return (dp);
}
/*
* Common dring mapping function used in both TxDring and RxDringData modes.
*/
{
int rv;
/*
* If the dring params are unacceptable then we NACK back.
*/
if ((dring_pkt->num_descriptors == 0) ||
(dring_pkt->descriptor_size == 0) ||
return (NULL);
}
/*
* Note: should only get one cookie. Enforced in
* the ldc layer.
*/
sizeof (ldc_mem_cookie_t));
if (rv != 0) {
goto fail;
}
if (rv != 0) {
goto fail;
}
/* store the address of the ring */
/* cache the dring mtype */
/* no private section as we are importing */
/*
* Using simple mono increasing int for ident at the moment.
*/
ldcp->next_ident++;
/*
* Acknowledge it; we send back a unique dring identifier that
* the sending side will use in future to refer to this
* descriptor ring.
*/
return (dp);
fail:
}
return (NULL);
}
/*
* Unmap the descriptor ring exported by the peer.
*/
static void
{
} else {
}
}
/*
* Map the shared memory data buffer area exported by the peer.
* Used in RxDringData mode only.
*/
static int
{
int rv;
/* skip over dring cookies */
return (1);
}
/* save # of data area cookies */
/* save data area size */
/* allocate ldc mem handle for data area */
if (rv != 0) {
return (1);
}
/* map the data area */
if (rv != 0) {
return (1);
}
/* get the map info */
if (rv != 0) {
return (1);
}
return (1);
}
/* allocate memory for data area cookies */
sizeof (ldc_mem_cookie_t), KM_SLEEP);
/* save data area cookies */
return (0);
}
/*
* Reset and free all the resources associated with the channel.
*/
static void
{
} else {
}
/* Unmap the remote dring which is imported from the peer */
} else {
/* Destroy the local dring which is exported to the peer */
}
}
/*
* Destroy the descriptor ring.
*/
static void
{
} else {
}
}
/*
* vsw_ldc_tx_worker -- A per LDC worker thread to transmit data.
* This thread is woken up by the vsw_portsend to transmit
* packets.
*/
static void
{
"vnet_tx_thread");
/*
* Wait until the data is received or a stop
* request is received.
*/
}
/*
* First process the stop request.
*/
break;
}
}
}
/*
* Update the run status and wakeup the thread that
* has sent the stop request.
*/
thread_exit();
}
/* vsw_stop_tx_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.
*/
}
if (tid != 0) {
}
}
static int
{
int rv;
if (rv != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Debugging routines
*/
static void
display_state(void)
{
"status %d : phase %u\n",
}
}
}
static void
{
}
static void
{
uint64_t i;
for (i = 0; i < vsw_num_descriptors; i++) {
pub_count++;
}
priv_count++;
}
}
i, priv_count, pub_count);
}
static void
{
int i;
typedef struct flag_name {
int flag_val;
char *flag_name;
} flag_name_t;
VSW_VER_INFO_SENT, "VSW_VER_INFO_SENT",
VSW_VER_INFO_RECV, "VSW_VER_INFO_RECV",
VSW_VER_ACK_RECV, "VSW_VER_ACK_RECV",
VSW_VER_ACK_SENT, "VSW_VER_ACK_SENT",
VSW_VER_NACK_RECV, "VSW_VER_NACK_RECV",
VSW_VER_NACK_SENT, "VSW_VER_NACK_SENT",
VSW_ATTR_INFO_SENT, "VSW_ATTR_INFO_SENT",
VSW_ATTR_INFO_RECV, "VSW_ATTR_INFO_RECV",
VSW_ATTR_ACK_SENT, "VSW_ATTR_ACK_SENT",
VSW_ATTR_ACK_RECV, "VSW_ATTR_ACK_RECV",
VSW_ATTR_NACK_SENT, "VSW_ATTR_NACK_SENT",
VSW_ATTR_NACK_RECV, "VSW_ATTR_NACK_RECV",
VSW_DRING_INFO_SENT, "VSW_DRING_INFO_SENT",
VSW_DRING_INFO_RECV, "VSW_DRING_INFO_RECV",
VSW_DRING_ACK_SENT, "VSW_DRING_ACK_SENT",
VSW_DRING_ACK_RECV, "VSW_DRING_ACK_RECV",
VSW_DRING_NACK_SENT, "VSW_DRING_NACK_SENT",
VSW_DRING_NACK_RECV, "VSW_DRING_NACK_RECV",
VSW_RDX_INFO_SENT, "VSW_RDX_INFO_SENT",
VSW_RDX_INFO_RECV, "VSW_RDX_INFO_RECV",
VSW_RDX_ACK_SENT, "VSW_RDX_ACK_SENT",
VSW_RDX_ACK_RECV, "VSW_RDX_ACK_RECV",
VSW_RDX_NACK_SENT, "VSW_RDX_NACK_SENT",
VSW_RDX_NACK_RECV, "VSW_RDX_NACK_RECV",
VSW_MCST_INFO_SENT, "VSW_MCST_INFO_SENT",
VSW_MCST_INFO_RECV, "VSW_MCST_INFO_RECV",
VSW_MCST_ACK_SENT, "VSW_MCST_ACK_SENT",
VSW_MCST_ACK_RECV, "VSW_MCST_ACK_RECV",
VSW_MCST_NACK_SENT, "VSW_MCST_NACK_SENT",
VSW_MCST_NACK_RECV, "VSW_MCST_NACK_RECV",
VSW_LANE_ACTIVE, "VSW_LANE_ACTIVE"};
for (i = 0; i < sizeof (flags)/sizeof (flag_name_t); i++) {
}
}