#include <sys/types.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/time.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/strsubr.h>
#include <sys/cmn_err.h>
#include <sys/cpu.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/strsun.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <net/if.h>
#include <netinet/arp.h>
#include <inet/arp.h>
#include <sys/varargs.h>
#include <sys/machsystm.h>
#include <sys/modctl.h>
#include <sys/modhash.h>
#include <sys/mac_client.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_ether.h>
#include <sys/taskq.h>
#include <sys/note.h>
#include <sys/mach_descrip.h>
#include <sys/mac.h>
#include <sys/mac_flow.h>
#include <sys/mdeg.h>
#include <sys/vsw.h>
#include <sys/vlan.h>
static void vsw_port_rx_cb(void *, mac_resource_handle_t, mblk_t *,
boolean_t);
static void vsw_if_rx_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t);
static int vsw_set_port_hw_addr(vsw_port_t *port);
static int vsw_set_if_hw_addr(vsw_t *vswp);
static void vsw_unset_hw_addr(vsw_t *, vsw_port_t *, int);
static int vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type);
static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type);
static void vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type);
static void vsw_mac_multicast_remove_all(vsw_t *vswp,
vsw_port_t *portp, int type);
static void vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch,
uint8_t *macaddr, uint16_t flags, vsw_vlanid_t *vids, int nvids);
static void vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids,
int nvids);
static void vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu);
static void vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type,
uint64_t maxbw);
static int vsw_notify_add(vsw_t *vswp);
static int vsw_notify_rem(vsw_t *vswp);
static void vsw_notify_cb(void *arg, mac_notify_type_t type);
static void vsw_notify_link(vsw_t *vswp);
int vsw_set_hw(vsw_t *, vsw_port_t *, int);
void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
void vsw_reconfig_hw(vsw_t *);
int vsw_mac_open(vsw_t *vswp);
void vsw_mac_close(vsw_t *vswp);
int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
int type);
void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
mcst_addr_t *mcst_p, int type);
int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
void vsw_mac_cleanup_ports(vsw_t *vswp);
void vsw_unset_addrs(vsw_t *vswp);
void vsw_set_addrs(vsw_t *vswp);
mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
void vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
vsw_vlanid_t *new_vids, int new_nvids);
void vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
void vsw_update_bandwidth(vsw_t *vswp, vsw_port_t *port, int type,
uint64_t maxbw);
extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
extern void vsw_hio_stop_port(vsw_port_t *portp);
extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
extern uint32_t vsw_publish_macaddr_count;
extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
mblk_t **npt);
extern void vsw_physlink_state_update(vsw_t *vswp);
static char mac_mtu_propname[] = "mtu";
extern int vsw_mac_open_retries;
#define WRITE_MACCL_ENTER(vswp, port, type) \
(type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_WRITER) :\
rw_enter(&port->maccl_rwlock, RW_WRITER)
#define READ_MACCL_ENTER(vswp, port, type) \
(type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_READER) :\
rw_enter(&port->maccl_rwlock, RW_READER)
#define RW_MACCL_EXIT(vswp, port, type) \
(type == VSW_LOCALDEV) ? rw_exit(&vswp->maccl_rwlock) : \
rw_exit(&port->maccl_rwlock)
void
vsw_set_addrs(vsw_t *vswp)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *port;
int rv;
READ_ENTER(&vswp->if_lockrw);
if (vswp->if_state & VSW_IF_UP) {
rv = vsw_mac_client_init(vswp, NULL, VSW_LOCALDEV);
if (rv != 0) {
cmn_err(CE_NOTE,
"!vsw%d: failed to program interface "
"unicast address\n", vswp->instance);
}
if (rv == 0) {
mac_unicst_update(vswp->if_mh,
(uint8_t *)&vswp->if_addr);
}
}
RW_EXIT(&vswp->if_lockrw);
WRITE_ENTER(&plist->lockrw);
for (port = plist->head; port != NULL; port = port->p_next) {
if (port->addr_set)
continue;
rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT);
if (rv != 0) {
cmn_err(CE_NOTE,
"!vsw%d: failed to program port(%d) "
"unicast address\n", vswp->instance,
port->p_instance);
}
}
if (vsw_publish_macaddr_count != 0) {
for (port = plist->head; port != NULL; port = port->p_next) {
vsw_publish_macaddr(vswp, port);
}
}
RW_EXIT(&plist->lockrw);
}
void
vsw_unset_addrs(vsw_t *vswp)
{
READ_ENTER(&vswp->if_lockrw);
if (vswp->if_state & VSW_IF_UP) {
vsw_mac_client_cleanup(vswp, NULL, VSW_LOCALDEV);
}
RW_EXIT(&vswp->if_lockrw);
vsw_mac_cleanup_ports(vswp);
}
int
vsw_mac_open(vsw_t *vswp)
{
int rv;
ASSERT(MUTEX_HELD(&vswp->mac_lock));
if (vswp->mh != NULL) {
return (0);
}
if (vswp->mac_open_retries++ >= vsw_mac_open_retries) {
return (EIO);
}
if ((rv = mac_open_by_linkname(vswp->physname, &vswp->mh)) != 0) {
if (rv == ENOENT || rv == EBADF) {
return (EAGAIN);
} else {
cmn_err(CE_WARN, "!vsw%d: mac_open %s failed rv:%x\n",
vswp->instance, vswp->physname, rv);
return (EIO);
}
}
vswp->mac_open_retries = 0;
vsw_mac_set_mtu(vswp, vswp->mtu);
rv = vsw_notify_add(vswp);
if (rv != 0) {
cmn_err(CE_CONT, "!vsw%d: mac_notify_add %s failed rv:%x\n",
vswp->instance, vswp->physname, rv);
}
return (0);
}
void
vsw_mac_close(vsw_t *vswp)
{
ASSERT(MUTEX_HELD(&vswp->mac_lock));
if (vswp->mh != NULL) {
if (vswp->mnh != 0) {
(void) vsw_notify_rem(vswp);
vswp->mnh = 0;
}
if (vswp->mtu != vswp->mtu_physdev_orig) {
vsw_mac_set_mtu(vswp, vswp->mtu_physdev_orig);
}
mac_close(vswp->mh);
vswp->mh = NULL;
}
}
int
vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
int type)
{
int ret = 0;
mac_client_handle_t mch;
WRITE_MACCL_ENTER(vswp, port, type);
mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
if (mch != NULL) {
ret = mac_multicast_add(mch, mcst_p->mca.ether_addr_octet);
if (ret != 0) {
cmn_err(CE_WARN, "!vsw%d: unable to "
"program multicast address(%s) err=%d",
vswp->instance,
ether_sprintf((void *)&mcst_p->mca), ret);
RW_MACCL_EXIT(vswp, port, type);
return (ret);
}
mcst_p->mac_added = B_TRUE;
}
RW_MACCL_EXIT(vswp, port, type);
return (ret);
}
void
vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
int type)
{
mac_client_handle_t mch;
WRITE_MACCL_ENTER(vswp, port, type);
mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
if (mch != NULL && mcst_p->mac_added) {
mac_multicast_remove(mch, mcst_p->mca.ether_addr_octet);
mcst_p->mac_added = B_FALSE;
}
RW_MACCL_EXIT(vswp, port, type);
}
static void
vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type)
{
mcst_addr_t *mcap;
mac_client_handle_t mch;
kmutex_t *mca_lockp;
int rv;
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (type == VSW_LOCALDEV) {
ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
mch = vswp->mch;
mcap = vswp->mcap;
mca_lockp = &vswp->mca_lock;
} else {
ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
mch = portp->p_mch;
mcap = portp->mcap;
mca_lockp = &portp->mca_lock;
}
if (mch == NULL)
return;
mutex_enter(mca_lockp);
for (mcap = mcap; mcap != NULL; mcap = mcap->nextp) {
if (mcap->mac_added)
continue;
rv = mac_multicast_add(mch, (uchar_t *)&mcap->mca);
if (rv == 0) {
mcap->mac_added = B_TRUE;
} else {
cmn_err(CE_WARN, "!vsw%d: unable to program "
"multicast address(%s) err=%d", vswp->instance,
ether_sprintf((void *)&mcap->mca), rv);
}
}
mutex_exit(mca_lockp);
}
static void
vsw_mac_multicast_remove_all(vsw_t *vswp, vsw_port_t *portp, int type)
{
mac_client_handle_t mch;
mcst_addr_t *mcap;
kmutex_t *mca_lockp;
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (type == VSW_LOCALDEV) {
ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
mch = vswp->mch;
mcap = vswp->mcap;
mca_lockp = &vswp->mca_lock;
} else {
ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
mch = portp->p_mch;
mcap = portp->mcap;
mca_lockp = &portp->mca_lock;
}
if (mch == NULL)
return;
mutex_enter(mca_lockp);
for (; mcap != NULL; mcap = mcap->nextp) {
if (!mcap->mac_added)
continue;
(void) mac_multicast_remove(mch, (uchar_t *)&mcap->mca);
mcap->mac_added = B_FALSE;
}
mutex_exit(mca_lockp);
}
void
vsw_update_bandwidth(vsw_t *vswp, vsw_port_t *port, int type, uint64_t maxbw)
{
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
WRITE_MACCL_ENTER(vswp, port, type);
vsw_maccl_set_bandwidth(vswp, port, type, maxbw);
RW_MACCL_EXIT(vswp, port, type);
}
int
vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type)
{
int rv;
mutex_enter(&vswp->mac_lock);
WRITE_MACCL_ENTER(vswp, port, type);
rv = vsw_maccl_open(vswp, port, type);
mutex_exit(&vswp->mac_lock);
if (rv == 0) {
(void) vsw_set_hw(vswp, port, type);
vsw_mac_multicast_add_all(vswp, port, type);
}
RW_MACCL_EXIT(vswp, port, type);
return (rv);
}
static int
vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type)
{
int rv = 0;
int instance;
char mac_cl_name[MAXNAMELEN];
const char *dev_name;
mac_client_handle_t *mchp;
uint64_t flags = 0;
ASSERT(MUTEX_HELD(&vswp->mac_lock));
if (vswp->mh == NULL) {
return (0);
}
mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
if (*mchp != NULL) {
return (0);
}
dev_name = ddi_driver_name(vswp->dip);
instance = ddi_get_instance(vswp->dip);
if (type == VSW_VNETPORT) {
if (port->p_hio_enabled)
flags |= MAC_OPEN_FLAGS_SHARES_DESIRED;
(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name,
instance, "_port", port->p_instance);
} else {
(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d",
dev_name, "_if", instance);
}
rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags);
if (rv != 0) {
cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n",
vswp->instance, mac_cl_name);
}
if (type != VSW_VNETPORT || !port->p_hio_enabled)
mac_client_set_rings(*mchp, MAC_RXRINGS_NONE, MAC_TXRINGS_NONE);
return (rv);
}
void
vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type)
{
WRITE_MACCL_ENTER(vswp, port, type);
vsw_mac_multicast_remove_all(vswp, port, type);
vsw_unset_hw(vswp, port, type);
vsw_maccl_close(vswp, port, type);
RW_MACCL_EXIT(vswp, port, type);
}
static void
vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type)
{
mac_client_handle_t *mchp;
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
if (*mchp != NULL) {
mac_client_close(*mchp, 0);
*mchp = NULL;
}
}
void
vsw_mac_cleanup_ports(vsw_t *vswp)
{
vsw_port_list_t *plist = &vswp->plist;
vsw_port_t *port;
READ_ENTER(&plist->lockrw);
for (port = plist->head; port != NULL; port = port->p_next) {
vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
}
RW_EXIT(&plist->lockrw);
}
int
vsw_set_hw(vsw_t *vswp, vsw_port_t *port, int type)
{
int err = 1;
D1(vswp, "%s: enter", __func__);
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (vswp->smode == VSW_LAYER3)
return (0);
if (type == VSW_VNETPORT) {
ASSERT(port != NULL);
err = vsw_set_port_hw_addr(port);
} else {
err = vsw_set_if_hw_addr(vswp);
}
D1(vswp, "%s: exit", __func__);
return (err);
}
void
vsw_unset_hw(vsw_t *vswp, vsw_port_t *port, int type)
{
D1(vswp, "%s: enter", __func__);
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (vswp->smode == VSW_LAYER3)
return;
if (type == VSW_VNETPORT) {
ASSERT(port != NULL);
vsw_unset_hw_addr(vswp, port, type);
} else {
vsw_unset_hw_addr(vswp, NULL, type);
}
D1(vswp, "%s: exit", __func__);
}
static int
vsw_set_port_hw_addr(vsw_port_t *port)
{
vsw_t *vswp = port->p_vswp;
mac_diag_t diag;
uint8_t *macaddr;
uint16_t vid = VLAN_ID_NONE;
int rv;
uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE |
MAC_UNICAST_STRIP_DISABLE;
D1(vswp, "%s: enter", __func__);
ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
if (port->p_mch == NULL)
return (0);
if (port->pvid != vswp->default_vlan_id) {
vid = port->pvid;
}
macaddr = (uint8_t *)port->p_macaddr.ether_addr_octet;
if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
mac_flags |= MAC_UNICAST_HW;
}
if (port->addr_set == B_FALSE) {
port->p_muh = NULL;
rv = mac_unicast_add(port->p_mch, macaddr, mac_flags,
&port->p_muh, vid, &diag);
if (rv != 0) {
cmn_err(CE_WARN, "vsw%d: Failed to program"
"macaddr,vid(%s, %d) err=%d",
vswp->instance, ether_sprintf((void *)macaddr),
vid, rv);
return (rv);
}
port->addr_set = B_TRUE;
D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
__func__, ether_sprintf((void *)macaddr), vid,
vswp->physname);
}
vsw_mac_add_vlans(vswp, port->p_mch, macaddr,
mac_flags, port->vids, port->nvids);
vsw_maccl_set_bandwidth(NULL, port, VSW_VNETPORT, port->p_bandwidth);
mac_rx_set(port->p_mch, vsw_port_rx_cb, (void *)port);
D1(vswp, "%s: exit", __func__);
return (rv);
}
static int
vsw_set_if_hw_addr(vsw_t *vswp)
{
mac_diag_t diag;
uint8_t *macaddr;
uint8_t primary_addr[ETHERADDRL];
uint16_t vid = VLAN_ID_NONE;
int rv;
uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE |
MAC_UNICAST_STRIP_DISABLE;
D1(vswp, "%s: enter", __func__);
ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
if (vswp->mch == NULL)
return (0);
macaddr = (uint8_t *)vswp->if_addr.ether_addr_octet;
mac_unicast_primary_get(vswp->mh, primary_addr);
if (ether_cmp((void *)primary_addr, (void*)macaddr) == 0) {
mac_flags |= MAC_UNICAST_PRIMARY;
}
if (vswp->pvid != vswp->default_vlan_id) {
vid = vswp->pvid;
}
if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
mac_flags |= MAC_UNICAST_HW;
}
if (vswp->addr_set == B_FALSE) {
vswp->muh = NULL;
rv = mac_unicast_add(vswp->mch, macaddr, mac_flags,
&vswp->muh, vid, &diag);
if (rv != 0) {
cmn_err(CE_WARN, "vsw%d: Failed to program"
"macaddr,vid(%s, %d) err=%d",
vswp->instance, ether_sprintf((void *)macaddr),
vid, rv);
return (rv);
}
vswp->addr_set = B_TRUE;
D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
__func__, ether_sprintf((void *)macaddr), vid,
vswp->physname);
}
vsw_mac_add_vlans(vswp, vswp->mch, macaddr, mac_flags,
vswp->vids, vswp->nvids);
vsw_maccl_set_bandwidth(vswp, NULL, VSW_LOCALDEV, vswp->bandwidth);
mac_rx_set(vswp->mch, vsw_if_rx_cb, (void *)vswp);
D1(vswp, "%s: exit", __func__);
return (rv);
}
static void
vsw_unset_hw_addr(vsw_t *vswp, vsw_port_t *port, int type)
{
vsw_vlanid_t *vids;
int nvids;
mac_client_handle_t mch = NULL;
D1(vswp, "%s: enter", __func__);
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (type == VSW_VNETPORT) {
ASSERT(port != NULL);
ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
vids = port->vids;
nvids = port->nvids;
} else {
ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
vids = vswp->vids;
nvids = vswp->nvids;
}
if (type == VSW_LOCALDEV) {
mch = vswp->mch;
} else if (type == VSW_VNETPORT) {
mch = port->p_mch;
}
if (mch == NULL) {
return;
}
mac_rx_clear(mch);
vsw_mac_remove_vlans(mch, vids, nvids);
if ((type == VSW_LOCALDEV) && (vswp->addr_set == B_TRUE)) {
(void) mac_unicast_remove(vswp->mch, vswp->muh);
vswp->muh = NULL;
D2(vswp, "removed vsw interface mac-addr from "
"the device %s", vswp->physname);
vswp->addr_set = B_FALSE;
} else if ((type == VSW_VNETPORT) && (port->addr_set == B_TRUE)) {
(void) mac_unicast_remove(port->p_mch, port->p_muh);
port->p_muh = NULL;
D2(vswp, "removed port(0x%p) mac-addr from "
"the device %s", port, vswp->physname);
port->addr_set = B_FALSE;
}
D1(vswp, "%s: exit", __func__);
}
static void
vsw_if_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
boolean_t loopback)
{
_NOTE(ARGUNUSED(mrh))
vsw_t *vswp = (vsw_t *)arg;
mblk_t *mpt;
int count;
ASSERT(vswp != NULL);
D1(vswp, "%s: enter", __func__);
READ_ENTER(&vswp->if_lockrw);
if (vswp->if_state & VSW_IF_UP) {
RW_EXIT(&vswp->if_lockrw);
count = vsw_vlan_frame_untag(vswp, VSW_LOCALDEV, &mp, &mpt);
if (count != 0) {
mac_rx(vswp->if_mh, NULL, mp);
}
} else {
RW_EXIT(&vswp->if_lockrw);
freemsgchain(mp);
}
D1(vswp, "%s: exit", __func__);
}
static void
vsw_port_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
boolean_t loopback)
{
_NOTE(ARGUNUSED(mrh))
vsw_t *vswp;
vsw_port_t *port = arg;
ASSERT(port != NULL);
vswp = port->p_vswp;
D1(vswp, "vsw_port_rx_cb: enter");
(void) vsw_portsend(port, mp);
D1(vswp, "vsw_port_rx_cb: exit");
}
mblk_t *
vsw_tx_msg(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port)
{
mac_client_handle_t mch;
mac_unicast_handle_t muh;
READ_MACCL_ENTER(vswp, port, caller);
mch = (caller == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
muh = (caller == VSW_LOCALDEV) ? vswp->muh : port->p_muh;
if (mch == NULL || muh == NULL) {
RW_MACCL_EXIT(vswp, port, caller);
return (mp);
}
(void) mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
RW_MACCL_EXIT(vswp, port, caller);
return (NULL);
}
void
vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
{
vsw_t *vswp = portp->p_vswp;
int rv;
D1(vswp, "%s: enter", __func__);
mutex_enter(&vswp->mac_lock);
WRITE_ENTER(&portp->maccl_rwlock);
vsw_mac_multicast_remove_all(vswp, portp, VSW_VNETPORT);
vsw_unset_hw(vswp, portp, VSW_VNETPORT);
vsw_maccl_close(vswp, portp, VSW_VNETPORT);
if (update_vlans == B_TRUE) {
if (portp->nvids != 0) {
kmem_free(portp->vids,
sizeof (vsw_vlanid_t) * portp->nvids);
portp->vids = NULL;
portp->nvids = 0;
}
portp->vids = new_vids;
portp->nvids = new_nvids;
portp->pvid = new_pvid;
}
rv = vsw_maccl_open(vswp, portp, VSW_VNETPORT);
if (rv != 0) {
goto recret;
}
if (vsw_set_hw(vswp, portp, VSW_VNETPORT)) {
cmn_err(CE_NOTE, "!vsw%d: port:%d failed to "
"set unicast address\n", vswp->instance, portp->p_instance);
goto recret;
}
vsw_mac_multicast_add_all(vswp, portp, VSW_VNETPORT);
recret:
RW_EXIT(&portp->maccl_rwlock);
mutex_exit(&vswp->mac_lock);
D1(vswp, "%s: exit", __func__);
}
void
vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
{
int rv;
D1(vswp, "%s: enter", __func__);
mutex_enter(&vswp->mac_lock);
WRITE_ENTER(&vswp->maccl_rwlock);
vsw_mac_multicast_remove_all(vswp, NULL, VSW_LOCALDEV);
vsw_unset_hw(vswp, NULL, VSW_LOCALDEV);
vsw_maccl_close(vswp, NULL, VSW_LOCALDEV);
if (update_vlans == B_TRUE) {
if (vswp->nvids != 0) {
kmem_free(vswp->vids,
sizeof (vsw_vlanid_t) * vswp->nvids);
vswp->vids = NULL;
vswp->nvids = 0;
}
vswp->vids = new_vids;
vswp->nvids = new_nvids;
vswp->pvid = new_pvid;
}
rv = vsw_maccl_open(vswp, NULL, VSW_LOCALDEV);
if (rv != 0) {
goto ifrecret;
}
if (vsw_set_hw(vswp, NULL, VSW_LOCALDEV)) {
cmn_err(CE_NOTE, "!vsw%d:failed to set unicast address\n",
vswp->instance);
goto ifrecret;
}
vsw_mac_multicast_add_all(vswp, NULL, VSW_LOCALDEV);
ifrecret:
RW_EXIT(&vswp->maccl_rwlock);
mutex_exit(&vswp->mac_lock);
D1(vswp, "%s: exit", __func__);
}
void
vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
vsw_vlanid_t *new_vids, int new_nvids)
{
if (portp->p_hio_enabled == B_TRUE) {
vsw_hio_stop_port(portp);
}
vsw_port_mac_reconfig(portp, B_TRUE, new_pvid, new_vids, new_nvids);
if (portp->p_hio_enabled == B_TRUE) {
vsw_hio_port_reset(portp, B_FALSE);
}
}
static void
vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, uint8_t *macaddr,
uint16_t flags, vsw_vlanid_t *vids, int nvids)
{
vsw_vlanid_t *vidp;
mac_diag_t diag;
int rv;
int i;
flags |= MAC_UNICAST_TAG_DISABLE | MAC_UNICAST_STRIP_DISABLE;
for (i = 0; i < nvids; i++) {
vidp = &vids[i];
if (vidp->vl_set == B_TRUE) {
continue;
}
rv = mac_unicast_add(mch, macaddr, flags,
&vidp->vl_muh, vidp->vl_vid, &diag);
if (rv != 0) {
cmn_err(CE_WARN, "vsw%d: Failed to program"
"macaddr,vid(%s, %d) err=%d",
vswp->instance, ether_sprintf((void *)macaddr),
vidp->vl_vid, rv);
} else {
vidp->vl_set = B_TRUE;
D2(vswp, "%s:programmed macaddr(%s) vid(%d) "
"into device %s", __func__,
ether_sprintf((void *)macaddr),
vidp->vl_vid, vswp->physname);
}
}
}
static void
vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, int nvids)
{
int i;
vsw_vlanid_t *vidp;
for (i = 0; i < nvids; i++) {
vidp = &vids[i];
if (vidp->vl_set == B_FALSE) {
continue;
}
(void) mac_unicast_remove(mch, vidp->vl_muh);
vidp->vl_set = B_FALSE;
}
}
#define ARH_FIXED_LEN 8
void
vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp)
{
mblk_t *mp;
mblk_t *bp;
struct arphdr *arh;
struct ether_header *ehp;
int count = 0;
int plen = 4;
uint8_t *cp;
mp = allocb(ETHERMIN, BPRI_MED);
if (mp == NULL) {
return;
}
ehp = (struct ether_header *)mp->b_rptr;
bcopy(ðerbroadcastaddr, &ehp->ether_dhost, ETHERADDRL);
bcopy(&portp->p_macaddr, &ehp->ether_shost, ETHERADDRL);
ehp->ether_type = htons(ETHERTYPE_REVARP);
arh = (struct arphdr *)(mp->b_rptr + sizeof (struct ether_header));
cp = (uint8_t *)arh;
arh->ar_hrd = htons(ARPHRD_ETHER);
arh->ar_pro = htons(ETHERTYPE_IP);
arh->ar_hln = ETHERADDRL;
arh->ar_pln = plen;
arh->ar_op = htons(REVARP_REQUEST);
cp += ARH_FIXED_LEN;
bcopy(&portp->p_macaddr, cp, ETHERADDRL);
cp += ETHERADDRL;
bzero(cp, plen);
cp += plen;
bcopy(&portp->p_macaddr, cp, ETHERADDRL);
cp += ETHERADDRL;
bzero(cp, plen);
cp += plen;
mp->b_wptr += ETHERMIN;
for (count = 0; count < vsw_publish_macaddr_count; count++) {
bp = dupmsg(mp);
if (bp == NULL) {
continue;
}
bp = vsw_tx_msg(vswp, bp, VSW_VNETPORT, portp);
if (bp != NULL) {
freemsg(bp);
}
}
freemsg(mp);
}
static void
vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu)
{
uint_t mtu_orig;
int rv;
rv = mac_set_mtu(vswp->mh, mtu, &mtu_orig);
if (rv != 0) {
cmn_err(CE_NOTE,
"!vsw%d: Unable to set the mtu:%d, in the "
"physical device:%s\n",
vswp->instance, mtu, vswp->physname);
return;
}
vswp->mtu_physdev_orig = mtu_orig;
}
static int
vsw_notify_add(vsw_t *vswp)
{
mac_notify_handle_t mnh;
uint32_t note;
note = mac_no_notification(vswp->mh);
if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
vswp->phys_no_link_update = B_TRUE;
} else {
vswp->phys_no_link_update = B_FALSE;
}
vswp->phys_link_state = vswp->phys_no_link_update ? LINK_STATE_UP :
mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
if (vswp->phys_no_link_update == B_TRUE) {
return (0);
}
mnh = mac_notify_add(vswp->mh, vsw_notify_cb, vswp);
if (mnh == 0) {
return (1);
}
vswp->mnh = mnh;
return (0);
}
static int
vsw_notify_rem(vsw_t *vswp)
{
int rv;
rv = mac_notify_remove(vswp->mnh, B_FALSE);
return (rv);
}
static void
vsw_notify_cb(void *arg, mac_notify_type_t type)
{
vsw_t *vswp = arg;
switch (type) {
case MAC_NOTE_LINK:
vsw_notify_link(vswp);
break;
default:
break;
}
}
static void
vsw_notify_link(vsw_t *vswp)
{
link_state_t link_state;
link_state = mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
if (vswp->phys_link_state != link_state) {
D3(vswp, "%s: phys_link_state(%d)\n",
__func__, vswp->phys_link_state);
vswp->phys_link_state = link_state;
vsw_physlink_state_update(vswp);
}
}
static void
vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type, uint64_t maxbw)
{
int rv = 0;
uint64_t *bw;
mac_resource_props_t *mrp;
mac_client_handle_t mch;
ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
if (type == VSW_VNETPORT) {
ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
mch = port->p_mch;
bw = &port->p_bandwidth;
} else {
ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
mch = vswp->mch;
bw = &vswp->bandwidth;
}
if (mch == NULL) {
return;
}
if (maxbw >= MRP_MAXBW_MINVAL || maxbw == 0) {
mrp = kmem_zalloc(sizeof (*mrp), KM_SLEEP);
if (maxbw == 0) {
mrp->mrp_maxbw = MRP_MAXBW_RESETVAL;
} else {
mrp->mrp_maxbw = maxbw;
}
mrp->mrp_mask |= MRP_MAXBW;
rv = mac_client_set_resources(mch, mrp);
if (rv != 0) {
if (type == VSW_VNETPORT) {
cmn_err(CE_NOTE, "!port%d: cannot set "
"bandwidth limit to (%ld), error(%d)\n",
port->p_instance, maxbw, rv);
} else {
cmn_err(CE_NOTE, "!vsw%d: cannot set "
"bandwidth limit to (%ld), error(%d)\n",
vswp->instance, maxbw, rv);
}
} else {
*bw = maxbw;
}
kmem_free(mrp, sizeof (*mrp));
}
}