/*
* 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/machsystm.h>
#include <sys/ethernet.h>
#include <sys/mac_provider.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 provider functionality for vnet using the
* generic(default) transport layer of sun4v Logical Domain Channels(LDC).
*/
/* Entry Points */
int vgen_init_mdeg(void *arg);
void vgen_uninit(void *arg);
int vgen_enable_intr(void *arg);
int vgen_disable_intr(void *arg);
static int vgen_start(void *arg);
#ifdef VNET_IOC_DEBUG
#endif
int port_num);
/* I/O Processing */
static void vgen_tx_watchdog(void *arg);
/* Dring Configuration */
/* VIO Message Processing */
static void vgen_hwatchdog(void *arg);
/* VLANs */
/* Exported functions */
void vgen_destroy_rxpools(void *arg);
/* Externs */
extern void vgen_ldc_msg_worker(void *arg);
/*
* Property names
*/
/*
* 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.
*/
/* Tunables */
/*
* Max # of channel resets allowed during handshake.
*/
/*
* See comments in vsw.c for details on the dring modes supported.
* In RxDringData mode, # of buffers is determined by multiplying the # of
* descriptors with the factor below. Note that the factor must be > 1; i.e,
* the # of buffers must always be > # of descriptors. This is needed because,
* while the shared memory buffers are sent up the stack on the receiver, the
* sender needs additional buffers that can be used for further transmits.
* See vgen_create_rx_dring() for details.
*/
/*
* Retry delay used while destroying rx mblk pools. Used in both Dring modes.
*/
/*
* Delay when rx descr not ready; used in TxDring mode only.
*/
/*
* Retry when rx descr not ready; used in TxDring mode only.
*/
/*
* Max # of packets accumulated prior to sending them up. It is best
* to keep this at 60% of the number of receive buffers. Used in TxDring mode
* by the msg worker thread. Used in RxDringData mode while in interrupt mode
* (not used in polled mode).
*/
/*
* Internal tunables for receive buffer pools, that is, the size and number of
* mblks for each pool. At least 3 sizes must be specified if these are used.
* The sizes must be specified in increasing order. Non-zero value of the first
* size will be used as a hint to use these values instead of the algorithm
* that determines the sizes based on MTU. Used in TxDring mode only.
*/
/*
* In the absence of "priority-ether-types" property in MD, the following
* internal tunable can be set to specify a single priority ethertype.
*/
/*
* Number of transmit priority buffers that are preallocated per device.
* This number is chosen to be a small value to throttle transmission
* of priority packets. Note: Must be a power of 2 for vio_create_mblks().
*/
/*
* Matching criteria passed to the MDEG to register interest
* in changes to 'virtual-device' nodes (i.e. vnet nodes) identified
* by their 'name' and 'cfg-handle' properties.
*/
{ MDET_PROP_STR, "name" },
{ MDET_PROP_VAL, "cfg-handle" },
{ MDET_LIST_END, NULL }
};
/* MD update matching structure */
{ MDET_PROP_VAL, "id" },
{ MDET_LIST_END, NULL }
};
/* Template for matching a particular vnet instance */
};
#ifdef VNET_IOC_DEBUG
#else
#define VGEN_M_CALLBACK_FLAGS (0)
#endif
NULL,
NULL,
};
/* Externs */
extern pri_t maxclsyspri;
extern uint32_t vnet_ethermtu;
extern uint16_t vnet_default_vlan_id;
extern uint32_t vnet_num_descriptors;
#ifdef DEBUG
extern int vnet_dbglevel;
/* -1 for all LDCs info, or ldc_id for a specific LDC info */
/* Flags to simulate error conditions for debugging */
int vgen_inject_err_flag = 0;
{
(vgen_inject_err_flag & error)) {
return (B_TRUE);
}
return (B_FALSE);
}
#endif
/*
* vgen_init() is called by an instance of vnet driver to initialize the
* corresponding generic transport layer. This layer uses Logical Domain
* Channels (LDCs) to communicate with the virtual switch in the service domain
* and also with peer vnets in other guest domains in the system.
*
* Arguments:
* vnetp: an opaque pointer to the vnet instance
* regprop: frame to be transmitted
* vnetdip: dip of the vnet device
* macaddr: mac address of the vnet device
*
* Returns:
* Sucess: a handle to the vgen instance (vgen_t)
* Failure: NULL
*/
int
{
int instance;
int rv;
return (DDI_FAILURE);
/* allocate multicast table */
sizeof (struct ether_addr), KM_SLEEP);
instance);
TASKQ_DEFAULTPRI, 0)) == NULL) {
instance);
goto vgen_init_fail;
}
if (rv != 0) {
goto vgen_init_fail;
}
return (DDI_SUCCESS);
sizeof (struct ether_addr));
if (VGEN_PRI_ETH_DEFINED(vgenp)) {
}
}
return (DDI_FAILURE);
}
int
{
/* register with MD event generator */
return (vgen_mdeg_reg(vgenp));
}
/*
* Called by vnet to undo the initializations done by vgen_init().
* The handle provided by generic transport during vgen_init() is the argument.
*/
void
{
return;
}
/* Unregister with MD event generator */
/*
* Detach all ports from the device; note that the device should have
* been unplumbed by this time (See vnet_unattach() for the sequence)
* and thus vgen_stop() has already been invoked on all the ports.
*/
/*
* We now destroy the taskq used to clean up rx mblk pools that
* We implicitly wait for those tasks to complete in
* ddi_taskq_destroy().
*/
}
/* Free multicast table */
/* Free pri_types table */
if (VGEN_PRI_ETH_DEFINED(vgenp)) {
}
}
int
{
return (DDI_SUCCESS);
}
void
{
}
}
/* vgen transmit function */
static mblk_t *
{
int status;
if (status != VGEN_SUCCESS) {
/* failure */
return (mp);
}
/* success */
return (NULL);
}
/*
* that are being transmitted over the port. It first verifies the vlan
* membership of the destination(port) and drops the packet if the
* destination doesn't belong to the given vlan.
*
* Arguments:
* portp: port over which the frames should be transmitted
* mp: frame to be transmitted
* is_tagged:
* B_TRUE: indicates frame header contains the vlan tag already.
* B_FALSE: indicates frame is untagged.
* vid: vlan in which the frame should be transmitted.
*
* Returns:
* Failure: NULL
*/
static mblk_t *
{
int rv;
/*
* If the packet is going to a vnet:
* Check if the destination vnet is in the same vlan.
* Check the frame header if tag or untag is needed.
*
* We do not check the above conditions if the packet is going to vsw:
* vsw must be present implicitly in all the vlans that a vnet device
* is configured into; even if vsw itself is not assigned to those
* vlans as an interface. For instance, the packet might be destined
* to another vnet(indirectly through vsw) or to an external host
* which is in the same vlan as this vnet and vsw itself may not be
* present in that vlan. Similarly packets going to vsw must be
* always tagged(unless in the default-vlan) if not already tagged,
* as we do not know the final destination. This is needed because
* vsw must always invoke its switching function only after tagging
* the packet; otherwise after switching function determines the
* destination we cannot figure out if the destination belongs to the
* the same vlan that the frame originated from and if it needs tag/
* untag. Note that vsw will tag the packet itself when it receives
* it over the channel from a client if needed. However, that is
* needed only in the case of vlan unaware clients such as obp or
* earlier versions of vnet.
*
*/
/*
* Packet going to a vnet. Check if the destination vnet is in
* needed.
*/
/* drop the packet */
return (NULL);
}
/* is the destination tagged or untagged in this vlan? */
(dst_tagged = B_TRUE);
if (is_tagged == dst_tagged) {
return (mp);
}
/* frame is tagged; destination needs untagged */
return (mp);
}
/* (is_tagged == B_FALSE): fallthru to tag tx packet: */
}
/*
* Packet going to a vnet needs tagging.
* OR
* If the packet is going to vsw, then it must be tagged in all cases:
*/
}
return (mp);
}
/* transmit packets over the given port */
static int
{
int status;
return (VGEN_FAILURE);
}
if (portp->use_vsw_port) {
dec_refcnt = B_TRUE;
}
/*
* Determine the vlan id that the frame belongs to.
*/
/* Frames in default vlan must be untagged */
/*
* If the destination is a vnet-port verify it belongs to the
* default vlan; otherwise drop the packet. We do not need
* this check for vsw-port, as it should implicitly belong to
* this vlan; see comments in vgen_vlan_frame_fixtag().
*/
goto portsend_ret;
}
} else { /* frame not in default-vlan */
goto portsend_ret;
}
}
if (status != VGEN_TX_SUCCESS) {
rv = VGEN_FAILURE;
}
if (dec_refcnt == B_TRUE) {
}
return (rv);
}
/*
*/
static int
{
int status;
int i;
for (i = 0; i < num_types; i++) {
/* priority frame, use pri tx function */
return (VGEN_SUCCESS);
}
}
return (VGEN_SUCCESS);
}
return (status);
}
/*
* 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;
/* drop the packet if ldc is not up or handshake is not done */
ldcp->ldc_status);
goto send_pkt_exit;
}
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 != VGEN_SUCCESS) {
if (rv == ECONNRESET) {
}
goto send_pkt_exit;
}
/* update stats */
}
/*
* note that the cblock of the ldc channel connected to the vsw is used for
* synchronization of the mctab.
*/
int
{
uint32_t i;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* 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) {
rv = DDI_FAILURE;
goto vgen_mcast_exit;
}
}
if (add) {
/* expand multicast table if necessary */
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
{
return (0);
}
/* vgen internal functions */
/* detach all ports from the device */
static void
{
}
}
/*
* detach the given port.
*/
static void
{
int port_num;
/*
* If this port is connected to the vswitch, then
* potentially there could be ports that may be using
* this port to transmit packets. To address this do
* the following:
* - First set vgenp->vsw_portp to NULL, so that
* its not used after that.
* - Then wait for the refcnt to go down to 0.
* - Now we can safely detach this port.
*/
while (vgenp->vsw_port_refcnt > 0) {
}
}
}
/* 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
{
/* Add the port to the specified vlans */
/* Bring up the channel */
}
static void
{
/* remove the port from vlans it has been assigned to */
}
/*
* Scan the machine description for this instance of vnet
* and read its properties. Called only from vgen_init().
* Returns: 0 on success, 1 on failure.
*/
static int
{
char *name;
int num_nodes = 0;
int num_devs = 0;
int listsz = 0;
int i;
return (rv);
}
/* search for all "virtual_device" nodes */
if (num_devs <= 0) {
goto vgen_readmd_exit;
}
/*
* Now loop through the list of virtual-devices looking for
* devices with name "network" and for each such device compare
* its instance with what we have from the 'reg' property to
* find the right node in MD and then read all its properties.
*/
for (i = 0; i < num_devs; i++) {
goto vgen_readmd_exit;
}
/* is this a "network" device? */
continue;
goto vgen_readmd_exit;
}
/* is this the required instance of vnet? */
continue;
/*
* Read the 'linkprop' property to know if this vnet
* device should get physical link updates from vswitch.
*/
&vnetp->pls_update);
/*
* Read the mtu. Note that we set the mtu of vnet device within
* this routine itself, after validating the range.
*/
}
sizeof (struct ether_header) + VLAN_TAGSZ;
/* read priority ether types */
/* read vlan id properties of this vnet instance */
&vnetp->default_vlan_id);
rv = 0;
break;
}
(void) md_fini_handle(mdp);
return (rv);
}
/*
* Read vlan id properties of the given MD node.
* Arguments:
* arg: device argument(vnet device or a port)
* type: type of arg; VGEN_LOCAL(vnet device) or VGEN_PEER(port)
* mdp: machine description
* node: md node cookie
*
* Returns:
* pvidp: port-vlan-id of the node
* vidspp: list of vlan-ids of the node
* nvidsp: # of vlan-ids in the list
* default_idp: default-vlan-id of the node(if node is vnet device)
*/
static void
{
char *pvid_propname;
char *vid_propname;
int rv;
int i;
int size;
int inst;
if (type == VGEN_LOCAL) {
} else {
return;
}
if (rv != 0) {
} else {
inst, *default_idp);
}
}
if (rv != 0) {
} else {
}
&size);
if (rv != 0) {
size = 0;
} else {
}
if (nvids != 0) {
for (i = 0; i < nvids; i++) {
}
}
}
/*
* Create a vlan id hash table for the given port.
*/
static void
{
}
/*
* Destroy the vlan id hash table in the given port.
*/
static void
{
portp->vlan_nchains = 0;
}
}
/*
* Add a port to the vlans specified in its port properites.
*/
static void
{
int rv;
int i;
}
}
/*
* Remove a port from the vlans it has been assigned to.
*/
static void
{
int rv;
int i;
(mod_hash_val_t *)&vp);
(mod_hash_val_t *)&vp);
}
}
/*
* Lookup the vlan id of the given tx frame. If it is a vlan-tagged frame,
* then the vlan-id is available in the tag; otherwise, its vlan id is
* implicitly obtained from the port-vlan-id of the vnet device.
* The vlan id determined is returned in vidp.
* Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
*/
static boolean_t
{
/* If it's a tagged frame, get the vlan id from vlan header */
return (B_TRUE);
}
/* Untagged frame, vlan-id is the pvid of vnet device */
return (B_FALSE);
}
/*
* Find the given vlan id in the hash table.
* Return: B_TRUE if the id is found; B_FALSE if not found.
*/
static boolean_t
{
int rv;
if (rv != 0)
return (B_FALSE);
return (B_TRUE);
}
/*
* This function reads "priority-ether-types" property from md. This property
* is used to enable support for priority frames. Applications which need
* a vnet or vsw within ldoms, should configure this property by providing
* the ether type(s) for which the priority facility is needed.
* Normal data frames are delivered over a ldc channel using the descriptor
* ring mechanism which is constrained by factors such as descriptor ring size,
* the rate at which the ring is processed at the peer ldc end point, etc.
* as raw pkt data (VIO_PKT_DATA) messages over the channel, avoiding the
* descriptor ring path and enables a more reliable and timely delivery of
* frames to the peer.
*/
static void
{
int rv;
int size;
int i;
if (rv != 0) {
/*
* Property may not exist if we are running pre-ldoms1.1 f/w.
* Check if 'vgen_pri_eth_type' has been set in that case.
*/
if (vgen_pri_eth_type != 0) {
size = sizeof (vgen_pri_eth_type);
} else {
"prop(%s) not found", pri_types_propname);
size = 0;
}
}
if (size == 0) {
vgenp->pri_num_types = 0;
return;
}
/*
* we have some priority-ether-types defined;
* allocate a table of these types and also
* allocate a pool of mblks to transmit these
* priority packets.
*/
}
&vgenp->pri_tx_vmp);
}
static void
{
int rv;
char *mtu_propname;
if (rv != 0) {
*mtu = vnet_ethermtu;
} else {
}
}
static void
{
int rv;
char *linkpropname;
if (rv != 0) {
} else {
}
}
/* register with MD event generator */
static int
{
int rv;
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 */
/*
* Register an interest in 'virtual-device' nodes with a
* 'name' property of 'network'
*/
if (rv != MDEG_SUCCESS) {
goto mdeg_reg_fail;
}
/* Register an interest in 'port' nodes */
&port_hdl);
if (rv != MDEG_SUCCESS) {
goto mdeg_reg_fail;
}
/* save mdeg handle in vgen_t */
return (DDI_SUCCESS);
(void) mdeg_unregister(dev_hdl);
}
return (DDI_FAILURE);
}
/* unregister with MD event generator */
static void
{
}
}
sizeof (vgen_prop_template));
}
}
/* mdeg callback function for the port node */
static int
{
int idx;
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 service domain.
*/
if (vgen_add_port(vgenp,
DDI_SUCCESS) {
"not initialize virtual "
"switch port.",
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);
}
/* mdeg callback function for the vnet node */
static int
{
return (MDEG_FAILURE);
}
/*
* We get an initial callback for this node as 'added' after
* registering with mdeg. Note that we would have already gathered
* information about this vnet node by walking MD earlier during attach
* (in vgen_read_mdprops()). So, there is a window where the properties
* of this node might have changed when we get this initial 'added'
* callback. We handle this as if an update occured and invoke the same
* function which handles updates to the properties of this vnet-node
* if any. A non-zero 'match' value indicates that the MD has been
* updated and that a 'network' node is present which may or may not
* have been updated. It is up to the clients to examine their own
* nodes and determine if they have changed.
*/
goto vgen_mdeg_cb_err;
}
goto vgen_mdeg_cb_err;
}
} else {
goto vgen_mdeg_cb_err;
}
/* Validate name and instance */
goto vgen_mdeg_cb_err;
}
/* is this a virtual-network device? */
goto vgen_mdeg_cb_err;
}
goto vgen_mdeg_cb_err;
}
/* is this the right instance of vnet? */
goto vgen_mdeg_cb_err;
}
return (MDEG_SUCCESS);
return (MDEG_FAILURE);
}
/*
* Check to see if the relevant properties in the specified node have
* changed, and if so take the appropriate action.
*/
static void
{
int rv;
/* Read the vlan ids */
/* Determine if there are any vlan id updates */
}
/* Read mtu */
} else {
" as the specified value:%d is invalid\n",
}
}
/*
* Read the 'linkprop' property.
*/
}
/* Now process the updated props */
/* save the new vlan ids */
}
if (nvids != 0) {
}
/* reset vlan-unaware peers (ver < 1.3) and restart handshake */
} else {
if (nvids != 0) {
}
}
if (rv == 0) {
sizeof (struct ether_header) + VLAN_TAGSZ;
}
}
/* reset vsw-port to re-negotiate with the updated prop. */
}
}
/* add a new port to the device */
static int
{
int rv;
if (rv != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (rv != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* read properties of the port from its md node */
static int
{
int num_ldcs;
int i;
int addrsz;
int num_nodes = 0;
int listsz = 0;
/* 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);
}
if (num_ldcs > 1) {
}
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 vswitch */
} else {
}
}
/* now update all properties into the port */
/* read vlan id properties of this port node */
return (DDI_SUCCESS);
}
/* 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 rv;
/*
* attach the channel under the port using its channel id;
* note that we only support one channel per port for now.
*/
return (DDI_FAILURE);
}
/* create vlan id hash table */
/* This port is connected to the switch port */
} else {
}
return (DDI_FAILURE);
}
if (rv == 0) {
/* link it into the list of ports */
/* We now have the vswitch port attached */
}
} else {
portp);
}
return (DDI_SUCCESS);
}
/* detach a port from the device based on mdeg data */
static void
{
/* stop the port if needed */
}
}
static int
{
/*
* For now, we get port updates only if vlan ids changed.
* We read the port num and do some sanity check.
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/* Read the vlan ids */
/* Determine if there are any vlan id updates */
}
if (updated_vlans == B_FALSE) {
return (DDI_FAILURE);
}
/* remove the port from vlans it has been assigned to */
/* save the new vlan ids */
}
if (nvids != 0) {
}
/* add port to the new vlans */
/* reset the port if it is vlan unaware (ver < 1.3) */
return (DDI_SUCCESS);
}
static uint64_t
{
}
/* attach the channel corresponding to the given ldc_id to the port */
static int
{
int status;
int instance;
goto ldc_attach_failed;
}
if (status != 0) {
goto ldc_attach_failed;
}
if (status != 0) {
status);
goto ldc_attach_failed;
}
/*
* allocate a message for ldc_read()s, big enough to hold ctrl and
* data msgs, including raw data msgs used to recv priority frames.
*/
/* Setup kstats for the channel */
goto ldc_attach_failed;
}
/* initialize vgen_versions supported */
/* Link this channel to the port */
#ifdef VNET_IOC_DEBUG
#endif
return (DDI_SUCCESS);
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
{
}
}
}
static int
{
int rv;
int flag = 0;
if (rv != 0) {
goto ldcinit_failed;
}
goto ldcinit_failed;
}
if (rv != 0) {
goto ldcinit_failed;
}
}
/* if channel is already UP - start handshake */
/*
* As the channel is up, use this port from now on.
*/
(void) atomic_swap_32(
}
/* Initialize local session id */
/* clear peer session id */
if (rv != 0) {
}
} else {
}
return (DDI_SUCCESS);
if (init_state & ST_cb_enable) {
}
if (init_state & ST_ldc_open) {
}
return (DDI_FAILURE);
}
static void
{
return;
}
}
}
/*
* Create a descriptor ring, that will be exported to the peer for mapping.
*/
static int
{
int rv;
} else {
}
return (rv);
}
/*
* Destroy the descriptor ring.
*/
static void
{
} else {
}
}
/*
* Map the descriptor ring exported by the peer.
*/
static int
{
int rv;
/*
* 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 (rv);
}
/*
* Unmap the descriptor ring exported by the peer.
*/
static void
{
} else {
}
}
void
{
while (vio_destroy_mblks(poolp) != 0) {
}
}
}
/* 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.
*/
static void
{
/*
* As the channel is up, use this port from now on.
*/
}
/* 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.
*/
int
{
}
/* Set the flag to indicate reset is in progress */
/* another thread is already in the process of resetting */
return (EBUSY);
}
}
}
return (0);
}
/* 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) {
/* status couldn't be determined */
ret = LDC_FAILURE;
goto ldc_cb_ret;
}
" but ldc status is not UP(0x%x)\n",
ldcp->ldc_status);
/* spurious interrupt, return success */
goto ldc_cb_ret;
}
}
/* status couldn't be determined */
ret = LDC_FAILURE;
goto ldc_cb_ret;
}
/*
* but print a debug warning message.
*/
if (event & LDC_EVT_READ) {
event &= ~LDC_EVT_READ;
}
}
if (event & LDC_EVT_READ) {
/*
* If the receive thread is enabled, then
* wakeup the receive thread to process the
* LDC messages.
*/
}
} else {
}
}
return (ret);
}
int
{
int rv;
if (caller == VGEN_LDC_CB) {
} else if (caller == VGEN_MSG_THR) {
} else {
return (EINVAL);
}
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
/* simulate bad sid condition */
}
#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:
if (rv != 0) {
" failed rv(%d)\n", rv);
}
break;
case VIO_TYPE_DATA:
if (rv != 0) {
" failed rv(%d)\n", rv);
}
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_evtread;
}
if (rv != 0) {
/*
* We handle the error and then return the error value. If we
* are running in the context of the msg worker, the error
* tells the worker thread to exit, as the channel would have
* been reset.
*/
if (rv == ECONNRESET) {
} else {
}
} else {
}
}
if (caller == VGEN_MSG_THR) {
}
return (rv);
}
/* vgen handshake functions */
/* change the hphase for the channel to the next phase */
static vgen_ldc_t *
{
} else {
}
return (ldcp);
}
/* 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.
* Invoked in RxDringData mode.
*/
static int
{
int rv;
/* Initialize the common part of dring reg msg */
/* skip over dring cookies at the tail of common section */
/* Now setup the extended part, specific to RxDringData mode */
/* copy data_ncookies in the msg */
/* copy data area size in the msg */
/* copy data area cookies in the msg */
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/*
* Send descriptor ring register message to the peer over ldc.
* Invoked in TxDring mode.
*/
static int
{
int rv;
/*
* Initialize only the common part of dring reg msg in TxDring mode.
*/
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
static int
{
int rv;
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/* send multicast addr info message to vsw */
static int
{
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);
}
/*
* vgen_dds_rx -- post DDS messages to vnet.
*/
static int
{
return (EBADMSG);
}
return (0);
}
/*
* vgen_dds_tx -- an interface called by vnet to send DDS messages.
*/
int
{
goto vgen_dsend_exit;
}
if (rv != VGEN_SUCCESS) {
} else {
rv = 0;
}
return (rv);
}
/* Initiate Phase 2 of handshake */
static int
{
int rv;
#ifdef DEBUG
/* simulate out of state condition */
return (rv);
}
/* simulate timeout condition */
return (VGEN_SUCCESS);
}
#endif
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
static int
{
int rv;
/* dring mode has been negotiated in attr phase; save in stats */
if (!VGEN_PRI_ETH_DEFINED(vgenp)) {
/*
* If priority frames are not in use, we don't need a
* separate wrapper function for 'tx', so we set it to
* 'tx_dringdata'. If priority frames are configured,
* we leave the 'tx' pointer as is (initialized in
* vgen_set_vnet_proto_ops()).
*/
}
} else { /* TxDring mode */
}
if (rv != VGEN_SUCCESS) {
return (rv);
}
/* update local dring_info params */
sizeof (ldc_mem_cookie_t));
sizeof (vnet_rx_dringdata_desc_t);
} else {
sizeof (ldc_mem_cookie_t));
}
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_SUCCESS);
}
/*
* 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 only support 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,
* all msgs including dring data messages are handled directly by the
* callback (intr) thread. The dring data msgs (msgtype: VIO_TYPE_DATA,
* subtype: VIO_SUBTYPE_INFO, subtype_env: VIO_DRING_DATA) can also be
* disabled while the polling thread is active, in which case the
* polling thread processes the rcv descriptor ring.
*
* However, for versions >= 1.6, we can force to only use 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 vgen_send_attr_info().
*/
} else {
}
} else { /* Ver <= 1.5 */
}
/*
* If the version negotiated with vswitch is >= 1.5 (link
* status update support), set the required bits in our
* attributes if this vnet device has been configured to get
* physical link state updates.
*/
} else {
}
}
/*
* 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 the
* following conditions are true:
* - Only pvid is defined for our peer and there are no vids.
* - pvids are equal.
* untagged frames of max size ETHERMAX.
*/
}
}
/*
* Starting v1.2 we support priority frames; so set the
* dring processing routines and xfer modes based on the
* version. Note that the dring routines could be changed after
* attribute handshake phase for versions >= 1.6 (See
* vgen_handshake_phase3())
*/
if (VGEN_PRI_ETH_DEFINED(vgenp)) {
/*
* Enable priority routines and pkt mode only if
* at least one pri-eth-type is specified in MD.
*/
/* set xfer mode for vgen_send_attr_info() */
} else {
/* No priority eth types defined in MD */
/* Set xfer mode for vgen_send_attr_info() */
}
} else { /* Versions prior to 1.2 */
}
}
/*
* Reset vnet-protocol-version dependent functions to pre-v1.2.
*/
static void
{
/* set xfer mode for vgen_send_attr_info() */
}
static void
{
/*
* If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
* the connection. See comments in vgen_set_vnet_proto_ops().
*/
need_reset = B_TRUE;
}
if (need_reset == B_TRUE) {
}
}
static void
{
}
static void
{
}
}
static void
{
}
}
static void
{
/*
* clear local handshake params and initialize.
*/
/* set version to the highest version supported */
/* set attr_info params */
/* reset protocol version specific function pointers */
/* clear peer_hparams */
}
/*
* Process Channel Reset. We tear down the resources (timers, threads,
* descriptor rings etc) associated with the channel and reinitialize the
* channel based on the flags.
*
* Arguments:
* ldcp: The channel being processed.
*
* flags:
* VGEN_FLAG_EVT_RESET:
* A ECONNRESET error occured while doing ldc operations such as
* ldc_read() or ldc_write(); the channel is already reset and it
* needs to be handled.
* VGEN_FLAG_NEED_LDCRESET:
* Some other errors occured and the error handling code needs to
* explicitly reset the channel and restart handshake with the
* peer. The error could be either in ldc operations or other
* parts of the code such as timeouts or mdeg events etc.
* VGEN_FLAG_UNINIT:
* The channel is being torn down; no need to bring up the channel
* after resetting.
*/
static int
{
int rv;
}
/*
* Report that the channel is being reset; it ensures that any HybridIO
* configuration is torn down before we reset the channel if it is not
* already reset (flags == VGEN_FLAG_NEED_LDCRESET).
*/
if (is_vsw_port == B_TRUE) {
}
/* Clear hstate and hphase */
if (flags == VGEN_FLAG_UNINIT) {
/* disable further callbacks */
if (rv != 0) {
}
}
} else {
/* flags == VGEN_FLAG_EVT_RESET */
}
/*
* As the connection is now reset, mark the channel
* link_state as 'down' and notify the stack if needed.
*/
/*
* As the channel link is down, mark physical link also
* as down. After the channel comes back up and
* handshake completes, we will get an update on the
* physlink state from vswitch (if this device has been
* configured to get phys link updates).
*/
}
}
}
}
/* Update link state to the stack */
if (link_update == B_TRUE) {
}
/*
* As the channel is being reset, redirect traffic to the peer through
* vswitch, until the channel becomes ready to be used again.
*/
}
/* Cancel handshake watchdog timeout */
if (htid) {
}
/* Cancel transmit watchdog timeout */
if (wd_tid) {
}
/* Stop the msg worker thread */
}
/* Destroy the local dring which is exported to the peer */
/* Unmap the remote dring which is imported from the peer */
/*
* Bring up the channel and restart handshake
* only if the channel is not being torn down.
*/
if (flags != VGEN_FLAG_UNINIT) {
/* Setup handshake parameters to restart a new handshake */
/* Bring the channel up */
} else {
}
/* If the channel is UP, start handshake */
if (is_vsw_port == B_FALSE) {
/*
* Channel is up; use this port from now on.
*/
B_FALSE);
}
/* Initialize local session id */
/* clear peer session id */
/*
* Initiate Handshake process with peer ldc endpoint by
* sending version info vio message. If that fails we
* go back to the top of this function to process the
* error again. Note that we can be in this loop for
* 'vgen_ldc_max_resets' times, after which the channel
* is not brought up.
*/
if (rv != 0) {
if (rv == ECONNRESET) {
} else {
}
/*
* We still hold 'reset_in_progress'; so we can
* just loop back to the top to restart error
* processing.
*/
goto again;
}
} else {
}
} else { /* flags == VGEN_FLAG_UNINIT */
/* Close the channel - retry on EAGAIN */
if (++retries > vgen_ldccl_retries) {
break;
}
}
if (rv != 0) {
"!vnet%d: Error(%d) closing the channel(0x%lx)\n",
}
ldcp->ldc_reset_count = 0;
}
/* Done processing channel reset; clear the atomic flag */
ldcp->reset_in_progress = 0;
return (0);
}
/*
* Initiate handshake with the peer by sending various messages
* based on the handshake-phase that the channel is currently in.
*/
static int
{
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 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_PHASE4:
break;
case VH_DONE:
ldcp->ldc_reset_count = 0;
/*
* The channel is up and handshake is done successfully. Now we
* can mark the channel link_state as 'up'. We also notify the
* stack if the channel is connected to vswitch.
*/
/*
* If this channel(port) is connected to vsw,
* need to sync multicast table with vsw.
*/
if (rv != VGEN_SUCCESS)
break;
/*
* We haven't negotiated with vswitch to get
* physical link state updates. We can update
* update the stack at this point as the
* channel to vswitch is up and the handshake
* is done successfully.
*
* If we have negotiated to get physical link
* state updates, then we won't notify the
* the stack here; we do that as soon as
* vswitch sends us the initial phys link state
* (see vgen_handle_physlink_info()).
*/
}
}
}
/*
* Check if mac layer should be notified to restart
* transmissions. This can happen if the channel got
* reset and while tx_blocked is set.
*/
if (ldcp->tx_blocked) {
}
/* start transmit watchdog timer */
break;
default:
break;
}
return (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
* has been exchanged successfully.
*/
break;
case VH_PHASE3:
/*
* Phase 3 is done, if dring registration
* has been exchanged successfully.
*/
break;
case VH_PHASE4:
/* Phase 4 is done, if rdx msg has been exchanged */
break;
default:
break;
}
if (status == 0) {
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
/*
* Link State Update Notes:
* The link state of the channel connected to vswitch is reported as the link
* state of the vnet device, by default. If the channel is down or reset, then
* the link state is marked 'down'. If the channel is 'up' *and* handshake
* between the vnet and vswitch is successful, then the link state is marked
* 'up'. If physical network link state is desired, then the vnet device must
* be configured to get physical link updates and the 'linkprop' property
* in the virtual-device MD node indicates this. As part of attribute exchange
* the vnet device negotiates with the vswitch to obtain physical link state
* updates. If it successfully negotiates, vswitch sends an initial physlink
* msg once the handshake is done and further whenever the physical link state
* changes. Currently we don't have mac layer interfaces to report two distinct
* link states - virtual and physical. Thus, if the vnet has been configured to
* get physical link updates, then the link status will be reported as 'up'
* only when both the virtual and physical links are up.
*/
static void
{
}
/*
* 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.
*/
return (EINVAL);
}
/* 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 */
if (rv != 0) {
return (rv);
}
}
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 */
if (rv != 0) {
return (rv);
}
}
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);
}
static int
{
/* save peer's values */
/*
* Process address type, ack frequency and transfer mode attributes.
*/
return (VGEN_FAILURE);
}
/*
* 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.
*/
/*
* We are capable of handling RxDringData AND the peer
* is 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 (VGEN_FAILURE);
}
/*
* 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 local hparams). If they don't match, we fail the
* handshake.
*/
/* send NACK */
return (VGEN_FAILURE);
}
} 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 (VGEN_FAILURE);
}
/*
* 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 (VGEN_FAILURE);
}
} 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 (VGEN_FAILURE);
}
}
return (VGEN_SUCCESS);
}
static int
{
/*
* 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 local hparams) matches the
* mode that the peer has ack'd. If they don't match,
* we fail the handshake.
*/
return (VGEN_FAILURE);
}
} else {
/*
* Peer ack'd with a mode that we don't
* support; we fail the handshake.
*/
return (VGEN_FAILURE);
}
== (VIO_TX_DRING|VIO_RX_DRING_DATA)) {
/*
* Peer must ack with only one negotiated mode.
* Otherwise fail handshake.
*/
return (VGEN_FAILURE);
}
/*
* Save the negotiated mode, so we can validate it when
* we receive attr info from the peer.
*/
}
}
/*
* Process Physical Link Update attribute.
*/
/*
* Versions >= 1.5:
* If the vnet device has been configured to get
* physical link state updates, check the corresponding
* bits in the ack msg, if the peer is vswitch.
*/
} else {
}
}
/*
* 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 local hparams) matches the mtu that the
* peer has ack'd. If they don't match, we fail the
* handshake.
*/
return (VGEN_FAILURE);
}
} 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.
*/
return (VGEN_FAILURE);
}
}
}
}
return (VGEN_SUCCESS);
}
/*
* to an attr info msg that we sent.
*/
static int
{
int rv = 0;
" Invalid Phase(%u)\n",
return (VGEN_FAILURE);
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
if (rv == VGEN_SUCCESS) {
} else {
}
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
break;
}
if (rv != 0) {
return (rv);
}
}
break;
case VIO_SUBTYPE_ACK:
if (rv == VGEN_FAILURE) {
break;
}
if (rv != 0) {
return (rv);
}
}
break;
case VIO_SUBTYPE_NACK:
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
static int
{
int rv = 0;
/*
* 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.
*/
"Rcvd dring reg option (%d), negotiated mode (%d)\n",
return (VGEN_FAILURE);
}
/*
* Map dring exported by the peer.
*/
if (rv != VGEN_SUCCESS) {
return (rv);
}
/*
* Map data buffers exported by the peer if we are in RxDringData mode.
*/
if (rv != VGEN_SUCCESS) {
return (rv);
}
}
}
return (VGEN_SUCCESS);
}
static int
{
if (lp->dring_ready) {
return (VGEN_SUCCESS);
}
/* save dring_ident acked by peer */
/* local dring is now ready */
return (VGEN_SUCCESS);
}
/*
* the peer to a dring register msg that we sent.
*/
static int
{
int rv = 0;
int msgsize;
/* 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:
if (rv == VGEN_SUCCESS) {
} else {
}
msgsize =
} else {
msgsize = sizeof (vio_dring_reg_msg_t);
}
/* send reply msg back to peer */
B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
return (VGEN_FAILURE);
}
if (rv != 0) {
return (rv);
}
}
break;
case VIO_SUBTYPE_ACK:
if (rv == VGEN_FAILURE) {
return (rv);
}
if (rv != 0) {
return (rv);
}
}
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);
}
if (rv != 0) {
return (rv);
}
}
break;
case VIO_SUBTYPE_ACK:
if (rv != 0) {
return (rv);
}
}
break;
case VIO_SUBTYPE_NACK:
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}
static int
{
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);
}
/*
* Physical link information message from the peer. Only vswitch should send
* us this message; if the vnet device has been configured to get physical link
* state updates. Note that we must have already negotiated this with the
* vswitch during attribute exchange phase of handshake.
*/
static int
{
int rv;
/*
* drop the message and don't process; as we should
* receive physlink_info message from only vswitch.
*/
return (VGEN_SUCCESS);
}
/*
* drop the message and don't process; as we should receive
* physlink_info message only if physlink update is enabled for
* the device and negotiated with vswitch.
*/
return (VGEN_SUCCESS);
}
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
} else {
}
/* Now update the stack */
}
/* send reply msg back to peer */
sizeof (vnet_physlink_msg_t), B_FALSE);
if (rv != VGEN_SUCCESS) {
return (rv);
}
break;
case VIO_SUBTYPE_ACK:
/* vnet shouldn't recv physlink acks */
break;
case VIO_SUBTYPE_NACK:
/* vnet shouldn't recv physlink nacks */
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;
case VIO_DDS_INFO:
/*
* If we are in the process of resetting the vswitch channel,
* drop the dds message. A new handshake will be initiated
* when the channel comes back up after the reset and dds
* negotiation can then continue.
*/
break;
}
break;
case VNET_PHYSLINK_INFO:
break;
}
return (rv);
}
/* handler for error messages received from the peer ldc end-point */
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 sending it up the stack.
*/
void
{
return;
}
"unable to process priority frame\n");
return;
}
} else {
}
/* copy the frame from the payload of raw data msg into the mblk */
}
/* update stats */
/*
* If polling is currently enabled, add the packet to the priority
* packets list and return. It will be picked up by the polling thread.
*/
if (dring_mode == VIO_RX_DRING_DATA) {
} else {
}
} else {
}
} else {
}
if (dring_mode == VIO_RX_DRING_DATA) {
} else {
}
}
}
/*
* dummy pkt data handler function for vnet protocol version 1.0
*/
static void
{
}
/* handler for data messages received from the peer ldc end-point */
static int
{
int rv = 0;
return (0);
}
/*
* We check the data msg seqnum. This is needed only in TxDring mode.
*/
if (rv != 0) {
return (rv);
}
}
switch (tagp->vio_subtype_env) {
case VIO_DRING_DATA:
break;
case VIO_PKT_DATA:
break;
default:
break;
}
return (rv);
}
static int
{
int rv;
}
/* Set the flag to indicate reset is in progress */
/* another thread is already in the process of resetting */
return (EBUSY);
}
}
}
return (rv);
}
static void
{
int rv;
/*
* If the channel has been reset max # of times, without successfully
* completing handshake, stop and do not bring the channel up.
*/
" handshake attempts (%d) on channel %ld",
return;
}
ldcp->ldc_reset_count++;
do {
}
if (retries++ >= vgen_ldcup_retries)
break;
} while (rv == EWOULDBLOCK);
if (rv != 0) {
}
}
int
{
/*
* We send a stopped message to peer (sender) as we are turning
* off polled mode. This effectively restarts data interrupts
* by allowing the peer to send further dring data msgs to us.
*/
} else {
}
return (0);
}
int
{
} else {
}
return (0);
}
mblk_t *
{
} else {
}
return (mp);
}
/* transmit watchdog timeout handler */
static void
{
int rv;
if (vgen_txwd_timeout &&
(tx_blocked == B_TRUE) &&
((ddi_get_lbolt() - tx_blocked_lbolt) >
/*
* Something is wrong; the peer is not picking up the packets
* in the transmit dring. We now go ahead and reset the channel
* to break out of this condition.
*/
"tx_blocked_lbolt(%lx)\n",
#ifdef DEBUG
/* tx timeout triggered for debugging */
}
#endif
/*
* Clear tid before invoking vgen_ldc_reset(). Otherwise,
* it will result in a deadlock when vgen_process_reset() tries
* to untimeout() on seeing a non-zero tid, but it is being
* invoked by the timer itself in this case.
*/
/* Cancelled by vgen_process_reset() */
return;
}
/*
* Now reset the channel.
*/
if (rv == 0) {
/*
* We have successfully reset the channel. If we are
* in tx flow controlled state, clear it now and enable
* transmit in the upper layer.
*/
if (ldcp->tx_blocked) {
}
}
/*
* Channel has been reset by us or some other thread is already
* in the process of resetting. In either case, we return
* without restarting the timer. When handshake completes and
* new watchdog timer.
*/
return;
}
/* Restart the timer */
/* Cancelled by vgen_process_reset() */
return;
}
}
/* Handshake watchdog timeout handler */
static void
{
/* Cancelled by vgen_process_reset() */
return;
}
/*
* Something is wrong; handshake with the peer seems to be hung. We now
* go ahead and reset the channel to break out of this condition.
*/
}
/* Check if the session id in the received message is valid */
static int
{
return (VGEN_FAILURE);
}
else
return (VGEN_SUCCESS);
}
/*
* Initialize the common part of dring registration
* message; used in both TxDring and RxDringData modes.
*/
static void
{
/* 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;
}
static int
{
int rv;
if (rv != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
return (B_FALSE);
}
#if DEBUG
/*
* Print debug messages - set to 0xf to enable all msgs
*/
void
{
}
}
}
}
#endif
#ifdef VNET_IOC_DEBUG
static void
{
enum ioc_reply {
} status;
int rv;
goto vgen_ioc_exit;
}
case VNET_FORCE_LINK_DOWN:
case VNET_FORCE_LINK_UP:
break;
default:
break;
}
switch (status) {
default:
case IOC_INVAL:
/* Error, reply with a NAK and EINVAL error */
break;
case IOC_ACK:
/* OK, reply with an ACK */
break;
}
}
static int
{
int rv;
switch (cmd) {
case VNET_FORCE_LINK_DOWN:
break;
case VNET_FORCE_LINK_UP:
} else {
}
/* if channel is already UP - restart handshake */
}
break;
}
return (0);
}
#else
static void
{
return;
}
}
#endif