vnet_gen.c revision 7636cb21f250f0485ca6052ffadc80ace93e6358
/*
* 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 2006 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);
/*
* 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 */
#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 uint32_t vnet_ntxds;
extern uint32_t vnet_reclaim_lowat;
extern uint32_t vnet_reclaim_hiwat;
extern uint32_t vnet_ldcwd_interval;
extern uint32_t vnet_ldcwd_txtimeout;
extern uint32_t vnet_ldc_mtu;
extern uint32_t vnet_nrbufs;
extern int _vnet_dbglevel;
#ifdef DEBUG
/*
* XXX: definitions below need to be in sync with those in vnet.c
*/
/*
* debug levels:
* DBG_LEVEL2: Info messages
* DBG_LEVEL3: Warning messages
* DBG_LEVEL4: Error messages
*/
DBG_LEVEL4 = 0x08 };
if ((_vnet_dbglevel & DBG_LEVEL1) != 0) { \
} \
if ((_vnet_dbglevel & DBG_LEVEL2) != 0) { \
} \
if ((_vnet_dbglevel & DBG_LEVEL3) != 0) { \
} \
if ((_vnet_dbglevel & DBG_LEVEL4) != 0) { \
} \
#else
#endif
#ifdef DEBUG
/* 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)
{
void *vnetp;
int instance;
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 status;
if (status != VGEN_SUCCESS) {
/* failure */
return (mp);
}
/* success */
return (NULL);
}
/* transmit packets over the given port */
static int
{
int status;
/*
* NOTE: for now, we will assume we have a single channel.
*/
return (VGEN_FAILURE);
}
if (ldcp->need_resched) {
/* out of tx resources, see vgen_ldcsend() for details. */
return (VGEN_FAILURE);
}
if (status != VGEN_TX_SUCCESS)
return (VGEN_FAILURE);
return (VGEN_SUCCESS);
}
/* channel transmit function */
static int
{
void *vnetp;
int rv;
struct ether_header *ehp;
/* drop the packet if ldc is not up or handshake is not done */
"vgen_ldcsend: id(%lx) status(%d), dropping packet\n",
/* retry ldc_up() if needed */
goto vgen_tx_exit;
}
"vgen_ldcsend: id(%lx) hphase(%x), dropping packet\n",
goto vgen_tx_exit;
}
goto vgen_tx_exit;
}
/*
* allocate a descriptor
*/
statsp->tx_no_desc++;
return (VGEN_TX_NORESOURCES);
}
}
/* copy data into pre-allocated transmit buffer */
}
/* initialize the corresponding public descriptor (txd) */
if (need_intr)
/* send dring datamsg to the peer */
if (ldcp->resched_peer) {
if (rv != 0) {
/* vgen_send_dring_data() error: drop the packet */
"vgen_ldcsend: vgen_send_dring_data(): failed: "
"id(%lx) rv(%d) len (%d)\n",
goto vgen_tx_exit;
}
}
/* update next available tbuf in the ring */
/* update tx index */
/* update stats */
if (is_bcast)
else if (is_mcast)
return (VGEN_TX_SUCCESS);
}
int
{
void *vnetp;
struct ether_addr *addrp;
int rv;
uint32_t i;
goto vgen_mcast_exit;
}
goto vgen_mcast_exit;
}
/*
* the multicast address.
*/
B_FALSE);
if (rv != VGEN_SUCCESS) {
}
} else {
/* set the flag to send a msg to vsw after handshake is done */
}
if (add) {
/* expand multicast table if necessary */
struct ether_addr *newtab;
sizeof (struct ether_addr), KM_NOSLEEP);
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;
}
}
}
return (DDI_SUCCESS);
}
/* 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;
"vgen_port_detach: enter: port_num(%d)\n", port_num));
/* remove it from port list */
/* detach channels from this port */
}
}
"vgen_port_detach: exit: port_num(%d)\n", port_num));
}
/* 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);
}
"vgen_mdeg_cb: ports: removed(%x), added(%x), updated(%x)\n",
}
/*
* 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.
*/
(void) vgen_add_port(vgenp,
break;
}
}
}
if (vsw_idx == -1) {
"can't find vsw_port\n"));
return (MDEG_FAILURE);
}
}
continue;
}
}
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;
struct ether_addr ea;
/* read "id" property to get the port number */
"vgen_add_port: prop(%s) not found\n", id_propname));
return (DDI_FAILURE);
}
/*
* Find the channel endpoint node(s) under this port node.
*/
"vgen_add_port: invalid number of nodes found (%d)",
num_nodes));
return (DDI_FAILURE);
}
/* allocate space for node list */
return (DDI_FAILURE);
if (num_ldcs <= 0) {
"vgen_add_port: can't find %s nodes", channel_propname));
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
for (i = 0; i < num_ldcs; i++) {
/* read channel ids */
"vgen_add_port: prop(%s) not found\n",
id_propname));
return (DDI_FAILURE);
}
ldc_ids[i]));
}
&addrsz)) {
"vgen_add_port: prop(%s) not found\n", rmacaddr_propname));
return (DDI_FAILURE);
}
if (addrsz < ETHERADDRL) {
"vgen_add_port: invalid address size (%d)\n", addrsz));
return (DDI_FAILURE);
}
macaddr));
for (i = ETHERADDRL - 1; i >= 0; i--) {
macaddr >>= 8;
}
if (val == 0) {
/* This port is connected to the vsw on dom0 */
}
}
}
return (DDI_SUCCESS);
}
/* remove a port from the device */
static int
{
/* read "id" property to get the port number */
"vgen_remove_port: prop(%s) not found\n", id_propname));
return (DDI_FAILURE);
}
port_num));
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++) {
ldcids[i]));
}
/* 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
{
/* XXX: 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;
AST_create_rxmblks = 0x20}
goto ldc_attach_failed;
}
if (status != 0) {
goto ldc_attach_failed;
}
if (status != 0) {
"ldc_reg_callback failed, id (%lx) rv (%d)\n",
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 */
if (status != VGEN_SUCCESS) {
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_create_rxmblks) {
}
if (attach_state & AST_alloc_tx_ring) {
}
if (attach_state & AST_ldc_reg_cb) {
}
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;
}
"vgen_ldc_detach: ldc_status is not INIT id(%lx)\n",
}
/* free receive resources */
/*
* if we cannot reclaim all mblks, put this
* on the list of pools 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;
ST_cb_enable = 0x8
}
if (rv != 0) {
"vgen_ldcinit: ldc_open failed: id<%lx> rv(%d)\n",
goto ldcinit_failed;
}
goto ldcinit_failed;
}
if (rv != 0) {
"vgen_ldcinit: vgen_init_tbufs() failed: id(%lx)\n",
goto ldcinit_failed;
}
/* Bind descriptor ring to the channel */
if (rv != 0) {
goto ldcinit_failed;
}
if (rv != 0) {
goto ldcinit_failed;
}
do {
"vgen_ldcinit: ldc_up err id(%lx) rv(%d)\n",
}
if (retries++ >= vgen_ldcup_retries)
break;
} while (rv == EWOULDBLOCK);
}
/* initialize transmit watchdog timeout */
return (DDI_SUCCESS);
if (init_state & ST_cb_enable) {
}
if (init_state & ST_dring_bind) {
}
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) {
}
if (rv != 0) {
}
/* clear handshake done bit and wait for pending tx and cb to finish */
drv_usecwait(1000);
/* reset transmit watchdog timeout */
}
/* unbind tx descriptor ring from the channel */
if (rv != 0) {
}
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_DBLK_SZ;
}
/* reset tbuf walking pointers */
/* initialize tx seqnum and index */
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 */
#ifdef DEBUG
"vgen_clobber_tbufs: id(0x%lx) num descrs done (%d)\n",
#endif
}
/* 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);
}
/* Interrupt handler for the channel */
static uint_t
{
void *vnetp;
int rv;
return (LDC_SUCCESS);
}
/* check ldc status change events first */
switch (istatus) {
case LDC_UP:
"vgen_ldc_cb: id(%lx) status(%d) is LDC_UP\n",
/*
* 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 */
break;
case LDC_OPEN:
case LDC_READY:
/*
* 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()).
*/
}
/* clear sids */
}
"vgen_ldc_cb: id(%lx) status is (%d)\n",
break;
default:
"vgen_ldc_cb: id(%lx) istatus=(%d) status(%d) is"
" *UNKNOWN*\n",
break;
}
}
return (LDC_SUCCESS);
}
/* if ldc_status is UP, receive all packets */
do {
if (rv != 0) {
"vgen_ldc_cb:ldc_read err id(%lx) rv(%d) "
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 sid mismatch is detected,
* reset the channel.
*/
return (LDC_SUCCESS);
}
}
switch (tagp->vio_msgtype) {
case VIO_TYPE_CTRL:
break;
case VIO_TYPE_DATA:
/* build a chain of received packets */
} else {
}
}
break;
case VIO_TYPE_ERR:
break;
default:
"vgen_ldc_cb: Unknown VIO_TYPE(%x)\n",
tagp->vio_msgtype));
break;
}
} while (msglen);
/* send up the received packets to MAC layer */
}
return (LDC_SUCCESS);
}
/* 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) {
}
"vgen_sendmsg: ldc_write failed: id(%lx) rv(%d)"
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 (VGEN_FAILURE);
}
"vgen_send_version_negotiate: VER_INFO_SENT id (%lx) ver(%d,%d)\n",
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 (VGEN_FAILURE);
}
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 (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
static int
{
int rv;
if (rv != VGEN_SUCCESS) {
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/* send descriptor ring data message to the peer over ldc */
static int
{
int rv;
if (rv != VGEN_SUCCESS) {
return (VGEN_FAILURE);
}
ldcp->next_txseq++;
return (VGEN_SUCCESS);
}
/* send multicast addr info message to vsw */
static int
{
void *vnetp;
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 (VGEN_FAILURE);
}
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
return (rv);
}
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
{
/* reset hstate and hphase */
/* reset handshake watchdog timeout */
}
/*
* Unmap drings, if dring_ready is set.
*/
/* do not unbind our dring */
}
/* Unmap peer's dring */
}
/*
* clear local handshake params and initialize.
*/
/* set version to the highest version supported */
/* set attr_info params */
/*
* set dring_info params.
* Note: dring is already created and bound.
*/
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().
*/
/* clear peer_hparams */
/* reset the channel if required */
if (ldcp->need_ldc_reset) {
"vgen_reset_hphase: id (%lx), Doing Channel Reset...\n",
"vgen_reset_hphase: id (%lx), RESET Done,ldc_status(%x)\n",
/* clear sids */
}
}
/* 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
{
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:
/* reset handshake watchdog timeout */
}
#if 0
#endif
if (ldcp->need_mcast_sync) {
/* need to sync multicast table with vsw */
(void) vgen_send_mcast_info(ldcp);
}
break;
default:
break;
}
}
/*
* 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 */
if (vgen_max_hretries) { /* handshake retry is specified */
}
}
/*
* to a version info msg that we sent.
*/
static void
{
int ack = 0;
int failed = 0;
int idx;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* Cache sid of peer if this is the first time */
"vgen_handle_version_negotiate: id (%lx) Caching"
}
/*
* 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 */
"vgen_handle_version_negotiate: Version"
return;
}
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) != VGEN_SUCCESS) {
return;
}
if (ack) {
" VER_ACK_SENT, id (%lx) ver(%d,%d) \n",
}
if (failed) {
" Version Negotiation Failed id (%lx)\n",
return;
}
/* 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. */
"vgen_handle_version_negotiate:"
" VER_ACK_RCVD id (%lx) Invalid Phase(%u)\n",
return;
}
/* SUCCESS - we have agreed on a version */
" VER_ACK_RCVD, id (%lx) ver(%d,%d) \n",
/* 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. */
"vgen_handle_version_negotiate:"
" VER_NACK_RCVD id (%lx) Invalid Phase(%u)\n",
return;
}
" VER_NACK_RCVD id(%lx) next ver(%d,%d)\n",
/* check if version in NACK is zero */
/*
* Version Negotiation has failed.
*/
" Version Negotiation Failed id (%lx)\n",
return;
}
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 id (%lx)\n",
return;
}
}
return;
}
break;
}
}
/* Check if the attributes are supported */
static int
{
#if 0
#endif
/*
* 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.
*/
#if 0
#endif
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/*
* to an attr info msg that we sent.
*/
static void
{
int ack = 0;
"vgen_handle_attr_info: Rcvd ATTR_INFO id(%lx)"
return;
}
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) != VGEN_SUCCESS) {
return;
}
if (ack) {
} else {
/* failed */
return;
}
}
break;
case VIO_SUBTYPE_ACK:
}
break;
case VIO_SUBTYPE_NACK:
break;
}
}
/* 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 void
{
int ack = 0;
int rv = 0;
/* dring_info can be rcvd in any of the phases after Phase1 */
"vgen_handle_dring_reg: Rcvd DRING_INFO, id (%lx)"
return;
}
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) != VGEN_SUCCESS) {
return;
}
if (ack) {
} else {
return;
}
}
break;
case VIO_SUBTYPE_ACK:
/* local dring is now ready */
/* save dring_ident acked by peer */
}
}
break;
case VIO_SUBTYPE_NACK:
break;
}
}
/*
* from the peer to a rdx info msg that we sent.
*/
static void
{
"vgen_handle_rdx_info: Rcvd RDX_INFO, id (%lx)"
return;
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/* send reply msg back to peer */
return;
}
}
break;
case VIO_SUBTYPE_ACK:
}
break;
case VIO_SUBTYPE_NACK:
break;
}
}
static void
{
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 */
"vgen_handle_mcast_info: rcvd SET_MCAST_INFO id (%lx)\n",
break;
case VIO_SUBTYPE_ACK:
"vgen_handle_mcast_info: rcvd SET_MCAST_ACK id (%lx)\n",
break;
case VIO_SUBTYPE_NACK:
"vgen_handle_mcast_info: rcvd SET_MCAST_NACK id (%lx)\n",
/* multicast remove request failed */
break;
}
/* multicast add request failed */
/* delete address from the table */
}
break;
}
}
}
break;
}
}
/* handler for control messages received from the peer ldc end-point */
static void
{
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;
}
}
/* handler for data messages received from the peer ldc end-point */
static void
{
return;
switch (tagp->vio_subtype_env) {
case VIO_DRING_DATA:
break;
default:
break;
}
}
static void
{
}
}
static void
{
struct ether_header *ehp;
int rv;
#ifdef VGEN_HANDLE_LOST_PKTS
int n;
#endif
#ifdef VGEN_REXMIT
#endif
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
/*
* received a data msg, which contains the start and end
* indeces 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.
*/
"vgen_handle_dring_data: INFO: start(%d), end(%d)\n",
/* validate rx start and end indeces */
/* drop the message if invalid index */
break;
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
break;
}
#ifdef DEBUG
if (vgen_trigger_rxlost) {
/* drop this msg to simulate lost pkts for debugging */
vgen_trigger_rxlost = 0;
break;
}
#endif
#ifdef VGEN_HANDLE_LOST_PKTS
/* receive start index doesn't match expected index */
"next_rxi(%d) != start(%d)\n",
/* 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.
*/
"dropping pkts, expected rxseq(0x%lx) "
"> recvd(0x%lx)\n",
/*
* sender?? drop this msg.
*/
break;
}
/*
* 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 */
"vgen_handle_dring_data: id(%lx) "
"vgen_sendmsg failed, "
}
#ifdef VGEN_REXMIT
/*
* stop further processing until peer
* retransmits with the right index.
* update next_rxseq expected.
*/
break;
#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.
*/
"vgen_handle_dring_data: id(%lx) "
"next_rxseq(0x%lx) == seq_num(0x%lx)\n",
}
} else {
/* expected and start dring indeces match */
/* seqnums don't match */
"vgen_handle_dring_data: id(%lx) "
"next_rxseq(0x%lx) != seq_num(0x%lx)\n",
}
}
#endif /* VGEN_HANDLE_LOST_PKTS */
/*
* 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 {
"id(%lx), ldc_mem_dring_acquire() failed\n",
break;
}
/*
* 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 */
/*
* sender needs ack for this packet,
* ack pkts upto this index.
*/
/* need to set new ack start index */
}
goto vgen_next_rxi;
}
/* set done bit irrespective of rv of ldc_mem_copy() */
/*
* sender needs ack for this packet,
* ack pkts upto this index.
*/
/* need to set new ack start index */
}
/* if ldc_mem_copy() failed */
if (rv) {
"vgen_handle_dring_data: id(%lx) "
goto vgen_next_rxi;
}
"vgen_handle_dring_data: id(%lx) "
"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 {
}
/* 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.
*/
}
/* save new recv index and expected seqnum of next dring msg */
break;
case VIO_SUBTYPE_ACK:
/*
* 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.
*/
"vgen_handle_dring_data: ACK: start(%d), end(%d)\n",
/* validate start and end indeces in the tx ack msg */
/* drop the message if invalid index */
break;
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
break;
}
/* reclaim descriptors that are done */
/*
* receiver continued processing descriptors after
* sending us the ack.
*/
break;
}
/* 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.
*/
break;
}
}
}
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 != 0) {
}
} else {
/*
* no ready tx descriptors. set the flag to send a
* message to peer when tx descriptors are ready in
* transmit routine.
*/
}
break;
case VIO_SUBTYPE_NACK:
/*
* 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.
*/
"vgen_handle_dring_data: NACK: start(%d), end(%d)\n",
/* validate start and end indeces in the tx nack msg */
/* drop the message if invalid index */
break;
}
/* validate dring_ident */
/* invalid dring_ident, drop the msg */
break;
}
/* no busy descriptors, bogus nack ? */
break;
}
#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
*/
"vgen_handle_dring_data: "
"vgen_send_dring_data failed :"
}
/* update next pointer */
}
"vgen_handle_dring_data: rexmit: start(%d) end(%d)\n",
#else /* VGEN_REXMIT */
/* we just mark the descrs as done so they can be reclaimed */
}
#endif /* VGEN_REXMIT */
break;
}
}
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
{
#ifdef DEBUG
return;
#endif
}
/*
* Check if mac layer should be notified to restart transmissions
*/
if (ldcp->need_resched) {
}
}
/* 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)
{
void *vnetp;
int rv;
if (rv == VGEN_SUCCESS) {
"vgen_ldc_watchdog: transmit timeout ldcid(%lx)\n",
#ifdef DEBUG
if (vgen_trigger_txtimeout) {
/* tx timeout triggered for debugging */
}
#endif
if (ldcp->need_resched) {
}
}
}
static int
{
int instance;
char name[MAXNAMELEN];
return (VGEN_FAILURE);
}
size, 0);
return (VGEN_FAILURE);
}
/* MIB II kstat variables */
/* Tx stats */
/* Rx stats */
/* Interrupt stats */
return (VGEN_SUCCESS);
}
static void
{
}
static int
{
if (rw == KSTAT_READ) {
/*
* MIB II kstat variables
*/
} else {
/*
* MIB II kstat variables
*/
}
return (VGEN_SUCCESS);
}
/* 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
{
"sid mismatch: expected(%x), rcvd(%x)\n",
return (VGEN_FAILURE);
}
else
return (VGEN_SUCCESS);
}
/* convert mac address from string to uint64_t */
static uint64_t
{
int i;
for (i = 0; i < ETHERADDRL; i++) {
val <<= 8;
}
return (val);
}
/* convert mac address from uint64_t to string */
static int
{
int i;
for (i = ETHERADDRL - 1; i >= 0; i--) {
value >>= 8;
}
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)
{
"vgen_hwatchdog: handshake timeout ldc(%lx) phase(%x) state(%x)\n",
}
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",
}