/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
#include <sys/machsystm.h>
#include <sys/mac_ether.h>
#include <sys/mach_descrip.h>
#include <sys/vio_mailbox.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
#include <sys/vio_util.h>
/* Switching setup routines */
void vsw_setup_switching_thread(void *arg);
int vsw_setup_switching(vsw_t *);
static int vsw_setup_layer2(vsw_t *);
static int vsw_setup_layer3(vsw_t *);
/* VLAN routines */
/* Forwarding database (FDB) routines */
void vsw_del_mcst_vsw(vsw_t *);
/* Support functions */
/*
* Functions imported from other files.
*/
/*
* Tunables used in this file.
*/
extern int vsw_setup_switching_delay;
extern uint32_t vsw_vlan_nchains;
extern uint32_t vsw_fdbe_refcnt_delay;
#define VSW_FDBE_REFHOLD(p) \
{ \
atomic_inc_32(&(p)->refcnt); \
}
#define VSW_FDBE_REFRELE(p) \
{ \
atomic_dec_32(&(p)->refcnt); \
}
/*
* Thread to setup switching mode. This thread is created during vsw_attach()
* initially. It invokes vsw_setup_switching() and keeps retrying while the
* returned value is EAGAIN. The thread exits when the switching mode setup is
* done successfully or when the error returned is not EAGAIN. This thread may
* also get created from vsw_update_md_prop() if the switching mode needs to be
* updated.
*/
void
{
int rv;
/* wait time used on successive retries */
"vsw_setup_sw_thread");
/* Wait for sometime before (re)trying setup_switching() */
break;
}
}
/*
* If there is a stop request, process that first and
* exit the loop. Continue to hold the mutex which gets
* released in CALLB_CPR_EXIT().
*/
break;
}
if (rv == 0) {
}
break;
}
}
thread_exit();
}
/*
* Create a thread to setup the switching mode.
* Returns 0 on success; 1 on failure.
*/
int
{
return (1);
}
return (0);
}
/*
* Stop the thread to setup switching mode.
*/
void
{
/*
* Signal the setup_switching thread to stop and wait until it stops.
*/
}
if (tid != 0)
vswp->mac_open_retries = 0;
}
/*
* Setup the required switching mode.
* Returns:
* 0 on success.
* EAGAIN if retry is needed.
* 1 on all other failures.
*/
int
{
/*
* Select best switching mode.
* This is done as this routine can be called from the timeout
* handler to retry setting up a specific mode. Currently only
* if the underlying network device is not available yet, causing
* retries.
*/
} else {
rv = 1;
}
} else if (rv == 0) {
}
return (rv);
}
/*
* Setup for layer 2 switching.
*
* Returns:
* 0 on success.
* EAGAIN if retry is needed.
* EIO on all other failures.
*/
static int
{
int rv;
/*
* Until the network device is successfully opened,
* set the switching to use vsw_switch_l2_frame.
*/
if (rv == 0) {
/*
* Physical device name is NULL, which is
* required for layer 2.
*/
return (EIO);
}
if (rv != 0) {
}
return (rv);
}
/*
* Now we can use the mac client switching, so set the switching
* function to use vsw_switch_l2_frame_mac_client(), which simply
* sends the packets to MAC layer for switching.
*/
/* Initialize HybridIO related stuff */
return (0);
return (EIO);
}
static int
{
return (0);
}
/* ARGSUSED */
void
{
}
/*
* Use mac client for layer 2 switching .
*/
static void
{
/*
* This switching function is expected to be called by
* the ports or the interface only. The packets from
* physical interface already switched.
*/
"phys dev", __func__);
}
}
/*
* Switch the given ethernet frame when operating in layer 2 mode.
*
* vswp: pointer to the vsw instance
* mp: pointer to chain of ethernet frame(s) to be switched
* caller: identifies the source of this frame as:
* 1. VSW_VNETPORT - a vsw port (connected to a vnet).
* 2. VSW_PHYSDEV - the physical ethernet device
* 3. VSW_LOCALDEV - vsw configured as a virtual interface
* arg: argument provided by the caller.
* 1. for VNETPORT - pointer to the corresponding vsw_port_t.
* 2. for PHYSDEV - NULL
* 3. for LOCALDEV - pointer to to this vsw_t(self)
*/
void
{
/*
* PERF: rather than breaking up the chain here, scan it
* to find all mblks heading to same destination and then
* pass that sub-chain to the lower transmit functions.
*/
/* process the chain of packets */
while (bp) {
/*
* If destination is VSW_LOCALDEV (vsw as an eth
* interface) and if the device is up & running,
* send the packet up the stack on this host.
* If the virtual interface is down, drop the packet.
*/
if (caller != VSW_LOCALDEV) {
} else {
}
continue;
}
/*
* Find fdb entry for the destination
* and hold a reference to it.
*/
/*
* If plumbed and in promisc mode then copy msg
* and send up the stack.
*/
/*
* If the destination is in FDB, the packet
* should be forwarded to the correponding
* vsw_port (connected to a vnet device -
* VSW_VNETPORT)
*/
/* Release the reference on the fdb entry */
} else {
/*
* Destination not in FDB.
*
* If the destination is broadcast or
* multicast forward the packet to all
* (VNETPORTs, PHYSDEV, LOCALDEV),
* except the caller.
*/
if (IS_BROADCAST(ehp)) {
} else if (IS_MULTICAST(ehp)) {
} else {
/*
* If the destination is unicast, and came
* from either a logical network device or
* the switch itself when it is plumbed, then
* send it out on the physical device and also
* up the stack if the logical interface is
* in promiscious mode.
*
* NOTE: The assumption here is that if we
* cannot find the destination in our fdb, its
* a unicast address, and came from either a
* vnet or down the stack (when plumbed) it
* must be destinded for an ethernet device
* outside our ldoms.
*/
if (caller == VSW_VNETPORT) {
/* promisc check copy etc */
"phys dev", __func__);
}
} else if (caller == VSW_PHYSDEV) {
/*
* Pkt seen because card in promisc
* mode. Send up stack if plumbed in
* promisc mode, else drop it.
*/
} else if (caller == VSW_LOCALDEV) {
/*
* Pkt came down the stack, send out
* over physical device.
*/
"phys dev", __func__);
}
}
}
}
}
}
/*
* Switch ethernet frame when in layer 3 mode (i.e. using IP
* layer to do the routing).
*
* There is a large amount of overlap between this function and
* vsw_switch_l2_frame. At some stage we need to revisit and refactor
* both these functions.
*/
void
{
/*
* In layer 3 mode should only ever be switching packets
* between IP layer and vnet devices. So make sure thats
* who is invoking us.
*/
return;
}
/* process the chain of packets */
while (bp) {
/*
* Find fdb entry for the destination
* and hold a reference to it.
*/
/* Release the reference on the fdb entry */
} else {
/*
* Destination not in FDB
*
* If the destination is broadcast or
* multicast forward the packet to all
* (VNETPORTs, PHYSDEV, LOCALDEV),
* except the caller.
*/
if (IS_BROADCAST(ehp)) {
} else if (IS_MULTICAST(ehp)) {
} else {
/*
* Unicast pkt from vnet that we don't have
* an FDB entry for, so must be destinded for
* the outside world. Attempt to send up to the
* IP layer to allow it to deal with it.
*/
if (caller == VSW_VNETPORT) {
}
}
}
}
}
/*
* Additional initializations that are needed for the specific switching mode.
*/
void
{
/*
* Program unicst, mcst addrs of vsw
* interface and ports in the physdev.
*/
/* Start HIO for ports that have already connected */
}
/* Update physical link info to any ports already connected */
}
}
/*
* Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV),
* except the caller (port on which frame arrived).
*/
static int
{
int skip_port = 0;
/*
* Broadcast message from inside ldoms so send to outside
* world if in either of layer 2 modes.
*/
if (nmp) {
!= NULL) {
"consisting of %ld bytes of data for"
}
}
}
if (caller == VSW_VNETPORT)
skip_port = 1;
/*
* Broadcast message from other vnet (layer 2 or 3) or outside
* world (layer 2 only), send up stack if plumbed.
*/
}
/* send it to all VNETPORTs */
/*
* Caution ! - don't reorder these two checks as arg
* will be NULL if the caller is PHYSDEV. skip_port is
* only set if caller is VNETPORT.
*/
continue;
} else {
if (nmp) {
/*
* The plist->lockrw is protecting the
* portp from getting destroyed here.
* So, no ref_cnt is incremented here.
*/
} else {
}
}
}
return (0);
}
/*
* Forward pkts to any devices or interfaces which have registered
* an interest in them (i.e. multicast groups).
*/
static int
{
/*
* Convert address to hash table key
*/
/*
* If pkt came from either a vnet or down the stack (if we are
* plumbed) and we are in layer 2 mode, then we send the pkt out
* over the physical adapter, and then check to see if any other
* vnets are interested in it.
*/
if (nmp) {
!= NULL) {
"%ld bytes of data for physical device",
}
}
}
(mod_hash_val_t *)&entp) != 0) {
} else {
/*
* Send to list of devices associated with this address...
*/
/* dont send to ourselves */
if ((caller == VSW_VNETPORT) &&
continue;
} else if ((caller == VSW_LOCALDEV) &&
__func__);
continue;
}
if (nmp) {
/*
* The vswp->mfdbrw is protecting the
* portp from getting destroyed here.
* So, no ref_cnt is incremented here.
*/
}
} else {
}
}
}
/*
* If the pkt came from either a vnet or from physical device,
* and if we havent already sent the pkt up the stack then we
* and in promisc mode).
*/
if ((check_if) &&
}
return (0);
}
/*
* This function creates the vlan id hash table for the given vsw device or
* port. It then adds each vlan that the device or port has been assigned,
* into this hash table.
* Arguments:
* arg: vsw device or port.
* type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
*/
void
{
/* create vlan hash table */
/* add vlan ids of the vsw device into its hash table */
}
/*
* This function removes the vlan ids of the vsw device or port from its hash
* table. It then destroys the vlan hash table.
* Arguments:
* arg: vsw device or port.
* type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
*/
void
{
/* remove vlan ids from the hash table */
/* destroy vlan-hash-table */
}
/*
* Create a vlan-id hash table for the given vsw device or port.
*/
static void
{
if (type == VSW_LOCALDEV) {
} else if (type == VSW_VNETPORT) {
portp->p_instance);
} else {
return;
}
}
/*
* Destroy the vlan-id hash table for the given vsw device or port.
*/
static void
{
if (type == VSW_LOCALDEV) {
vswp->vlan_nchains = 0;
} else if (type == VSW_VNETPORT) {
portp->vlan_nchains = 0;
} else {
return;
}
}
/*
* Add vlan ids of the given vsw device or port into its hash table.
*/
void
{
int rv;
int i;
if (type == VSW_LOCALDEV) {
if (rv != 0) {
}
if (rv != 0) {
}
}
} else if (type == VSW_VNETPORT) {
if (rv != 0) {
portp->p_instance);
}
if (rv != 0) {
}
}
}
}
/*
* Remove vlan ids of the given vsw device or port from its hash table.
*/
void
{
int rv;
int i;
if (type == VSW_LOCALDEV) {
(mod_hash_val_t *)&vp);
}
(mod_hash_val_t *)&vp);
}
}
} else if (type == VSW_VNETPORT) {
(mod_hash_val_t *)&vp);
}
(mod_hash_val_t *)&vp);
}
}
} else {
return;
}
}
/*
* Find the given vlan id in the hash table.
* Return: B_TRUE if the id is found; B_FALSE if not found.
*/
{
int rv;
if (rv != 0)
return (B_FALSE);
return (B_TRUE);
}
/*
* Add an entry into FDB for the given vsw.
*/
void
{
int rv;
/*
* Note: duplicate keys will be rejected by mod_hash.
*/
(mod_hash_val_t)fp);
if (rv != 0) {
}
}
/*
* Remove an entry from FDB.
*/
void
{
int rv;
/*
* Remove the entry from fdb hash table.
* This prevents further references to this fdb entry.
*/
(mod_hash_val_t *)&fp);
if (rv != 0) {
/* invalid key? */
return;
}
/*
* If there are threads already ref holding before the entry was
* removed from hash table, then wait for ref count to drop to zero.
*/
}
}
/*
* Search fdb for a given mac address. If an entry is found, hold
* a reference to it and return the entry, else returns NULL.
*/
static vsw_fdbe_t *
{
int rv;
if (rv != 0)
return (NULL);
return (fp);
}
/*
* Callback function provided to mod_hash_find_cb(). After finding the fdb
* entry corresponding to the key (macaddr), this callback will be invoked by
* mod_hash_find_cb() to atomically increment the reference count on the fdb
* entry before returning the found entry.
*/
static void
{
}
/*
* A given frame must be always tagged with the appropriate vlan id (unless it
* is in the default-vlan) before the mac address switching function is called.
* Otherwise, after switching function determines the destination, we cannot
* figure out if the destination belongs to the the same vlan that the frame
* the external(physical) network over a vlan trunk link are always tagged.
* However frames which are received from a vnet-port over ldc or frames which
* are coming down the stack on the service domain over vsw interface may be
* untagged. These frames must be tagged with the appropriate pvid of the
* sender (vnet-port or vsw device), before invoking the switching function.
*
* Arguments:
* arg: caller of the function.
* type: type of arg(caller): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
* mp: frame(s) to be tagged.
*/
mblk_t *
{
if (type == VSW_LOCALDEV) {
} else {
/* VSW_VNETPORT */
}
/* Determine if it is an untagged frame */
/* no need to tag if the frame is in default vlan */
continue;
}
}
}
/* build a chain of processed packets */
} else {
}
}
return (bph);
}
/*
* Frames destined to a vnet-port or to the local vsw interface, must be
* untagged if necessary before sending. This function first checks that the
* frame can be sent to the destination in the vlan identified by the frame
* tag. Note that when this function is invoked the frame must have been
* already tagged (unless it is in the default-vlan). Because, this function is
* called when the switching function determines the destination and invokes
* its send function (vnet-port or vsw interface) and all frames would have
* been tagged by this time (see comments in vsw_vlan_frame_pretag()).
*
* Arguments:
* arg: destination device.
* type: type of arg(destination): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
* np: head of pkt chain to be validated and untagged.
* npt: tail of pkt chain to be validated and untagged.
*
* Returns:
* np: head of updated chain of packets
* npt: tail of updated chain of packets
* rv: count of the packets in the returned list
*/
{
if (type == VSW_LOCALDEV) {
} else {
/* type == VSW_VNETPORT */
}
/*
* If the MAC layer switching in place, then
* untagging required only if the pvid is not
* the same as default_vlan_id. This is because,
* the MAC layer will send packets for the
* registered vlans only.
*/
/* simply count and set the tail */
count = 1;
count++;
}
return (count);
}
count = 0;
/*
* Determine the vlan id that the frame belongs to.
*/
/*
* If MAC layer switching in place, then we
* need to untag only if the tagged packet has
* vlan-id same as the pvid.
*/
/* only tagged packets expected here */
/* packet dropped */
continue;
}
}
} else { /* No MAC layer switching */
/*
*/
/*
* Untagged frame. We shouldn't have an
* untagged packet at this point, unless
* the destination's vlan id is
* default-vlan-id; if it is not the
* default-vlan-id, we drop the packet.
*/
/* drop the packet */
continue;
}
} else { /* Tagged */
/*
* Tagged frame, untag if it's the
* destination's pvid.
*/
/* packet dropped */
continue;
}
} else {
/*
* Check if the destination is in the
* same vlan.
*/
vlan_id);
/* drop the packet */
continue;
}
}
}
}
/* build a chain of processed packets */
} else {
}
count++;
}
return (count);
}
/*
* Lookup the vlan id of the given frame. If it is a vlan-tagged frame,
* then the vlan-id is available in the tag; otherwise, its vlan id is
* implicitly obtained based on the caller (destination of the frame:
* VSW_VNETPORT or VSW_LOCALDEV).
* The vlan id determined is returned in vidp.
* Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
*/
{
/* If it's a tagged frame, get the vid from vlan header */
return (B_TRUE);
}
/* Untagged frame; determine vlan id based on caller */
switch (caller) {
case VSW_VNETPORT:
/*
* packet destined to a vnet; vlan-id is pvid of vnet-port.
*/
break;
case VSW_LOCALDEV:
/*
* packet destined to vsw interface;
* vlan-id is port-vlan-id of vsw device.
*/
break;
}
return (B_FALSE);
}
/*
* Add or remove multicast address(es).
*
* Returns 0 on success, 1 on failure.
*/
int
{
int i;
/*
* Convert address into form that can be used
* as hash table key.
*/
/*
*/
/*
* Update the list of multicast
* addresses contained within the
* port structure to include this new
* one.
*/
__func__);
(void) vsw_del_mcst(vswp,
return (1);
}
/*
* Program the address into HW. If the addr
* has already been programmed then the MAC
* just increments a ref counter (which is
* used when the address is being deleted)
*/
VSW_VNETPORT)) {
(void) vsw_del_mcst(vswp,
return (1);
}
} else {
"address 0x%llx for port %ld",
return (1);
}
} else {
/*
* Delete an entry from the multicast hash
* table and update the address list
* appropriately.
*/
port->p_instance);
/*
* Remove the address from HW. The address
* will actually only be removed once the ref
* count within the MAC layer has dropped to
* zero. I.e. we can safely call this fn even
* if other ports are interested in this
* address.
*/
} else {
"addr 0x%llx for port %ld",
return (1);
}
}
}
return (0);
}
/*
* Add a new multicast entry.
*
* Search hash table based on address. If match found then
* update associated val (which is chain of ports), otherwise
*/
int
{
int dup = 0;
int rv = 0;
if (devtype == VSW_VNETPORT) {
/*
* Being invoked from a vnet.
*/
} else {
/*
* We are being invoked via the m_multicst mac entry
* point.
*/
}
(mod_hash_val_t *)&ment) != 0) {
/* address not currently in table */
(mod_hash_val_t)ment) != 0) {
rv = 1;
} else {
}
} else {
/*
* Address in table. Check to see if specified port
* is already associated with the address. If not add
* it now.
*/
if (devtype == VSW_VNETPORT) {
"found for portid %ld and key "
"0x%llx", __func__,
addr);
} else {
}
rv = 1;
dup = 1;
break;
}
}
/*
* Port not on list so add it to end now.
*/
if (0 == dup) {
}
}
return (rv);
}
/*
* Remove a multicast entry from the hashtable.
*
* Search hash table based on address. If match found, scan
* list of ports associated with address. If specified port
* found remove it from list.
*/
int
{
if (devtype == VSW_VNETPORT) {
} else {
}
(mod_hash_val_t *)&ment) != 0) {
return (1);
}
if (devtype == VSW_VNETPORT) {
} else {
}
/*
* head of list, if no other element is in
* list then destroy this entry, otherwise
* just replace it with updated value.
*/
} else {
}
} else {
/*
* Not head of list, no need to do
* replacement, just adjust list pointers.
*/
}
break;
}
}
return (1);
return (0);
}
/*
* Port is being deleted, but has registered an interest in one
* or more multicast groups. Using the list of addresses maintained
* within the port structure find the appropriate entry in the hash
* table and remove this port from the list of interested ports.
*/
void
{
/*
* Remove the address from HW. The address
* will actually only be removed once the ref
* count within the MAC layer has dropped to
* zero. I.e. we can safely call this fn even
* if other ports are interested in this
* address.
*/
}
}
/*
* This vsw instance is detaching, but has registered an interest in one
* or more multicast groups. Using the list of addresses maintained
* within the vsw structure find the appropriate entry in the hash
* table and remove this instance from the list of interested ports.
*/
void
{
}
}
mblk_t *
{
/* process the chain of packets */
while (bp) {
} else {
}
} else {
}
} else {
}
}
return (head);
}
static mblk_t *
{
return (NULL);
}
}
return (nmp);
}