summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4v/io/vnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4v/io/vnet.c')
-rw-r--r--usr/src/uts/sun4v/io/vnet.c1049
1 files changed, 1049 insertions, 0 deletions
diff --git a/usr/src/uts/sun4v/io/vnet.c b/usr/src/uts/sun4v/io/vnet.c
new file mode 100644
index 0000000000..ad625953e7
--- /dev/null
+++ b/usr/src/uts/sun4v/io/vnet.c
@@ -0,0 +1,1049 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/ksynch.h>
+#include <sys/stat.h>
+#include <sys/modctl.h>
+#include <sys/debug.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <net/if.h>
+#include <sys/mac.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/strsun.h>
+#include <sys/note.h>
+#include <sys/vnet.h>
+
+/*
+ * Function prototypes.
+ */
+
+/* DDI entrypoints */
+static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int vnetattach(dev_info_t *, ddi_attach_cmd_t);
+static int vnetdetach(dev_info_t *, ddi_detach_cmd_t);
+
+/* MAC entrypoints */
+static uint64_t vnet_m_stat(void *arg, enum mac_stat stat);
+static int vnet_m_start(void *);
+static void vnet_m_stop(void *);
+static int vnet_m_promisc(void *, boolean_t);
+static int vnet_m_multicst(void *, boolean_t, const uint8_t *);
+static int vnet_m_unicst(void *, const uint8_t *);
+static void vnet_m_resources(void *);
+static void vnet_m_ioctl(void *, queue_t *, mblk_t *);
+mblk_t *vnet_m_tx(void *, mblk_t *);
+
+/* vnet internal functions */
+static int vnet_mac_register(vnet_t *);
+static int vnet_read_mac_address(vnet_t *vnetp);
+static void vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp);
+static void vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp);
+static vp_tl_t *vnet_get_vptl(vnet_t *vnetp, const char *devname);
+static fdb_t *vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr);
+
+/* exported functions */
+void vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg);
+void vnet_del_fdb(void *arg, uint8_t *macaddr);
+void vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg);
+void vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg);
+void vnet_del_def_rte(void *arg);
+
+/* externs */
+extern int vgen_init(void *vnetp, dev_info_t *vnetdip, void *vnetmacp,
+ const uint8_t *macaddr, mac_t **vgenmacp);
+extern void vgen_uninit(void *arg);
+
+/*
+ * Linked list of "vnet_t" structures - one per instance.
+ */
+static vnet_t *vnet_headp = NULL;
+static krwlock_t vnet_rw;
+
+/* Tunables */
+uint32_t vnet_ntxds = VNET_NTXDS; /* power of 2 transmit descriptors */
+uint32_t vnet_reclaim_lowat = VNET_RECLAIM_LOWAT; /* tx recl low watermark */
+uint32_t vnet_reclaim_hiwat = VNET_RECLAIM_HIWAT; /* tx recl high watermark */
+uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */
+uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT; /* tx timeout in msec */
+uint32_t vnet_ldc_qlen = VNET_LDC_QLEN; /* ldc qlen */
+uint32_t vnet_nfdb_hash = VNET_NFDB_HASH; /* size of fdb hash table */
+
+/*
+ * Property names
+ */
+static char macaddr_propname[] = "local-mac-address";
+
+static struct ether_addr etherbroadcastaddr = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/*
+ * MIB II broadcast/multicast packets
+ */
+#define IS_BROADCAST(ehp) \
+ (ether_cmp(&ehp->ether_dhost, &etherbroadcastaddr) == 0)
+#define IS_MULTICAST(ehp) \
+ ((ehp->ether_dhost.ether_addr_octet[0] & 01) == 1)
+
+/*
+ * This is the string displayed by modinfo(1m).
+ */
+static char vnet_ident[] = "vnet driver v1.0";
+extern struct mod_ops mod_driverops;
+static struct cb_ops cb_vnetops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* cb_stream */
+ (int)(D_MP) /* cb_flag */
+};
+
+static struct dev_ops vnetops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ NULL, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ vnetattach, /* devo_attach */
+ vnetdetach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_vnetops, /* devo_cb_ops */
+ (struct bus_ops *)NULL /* devo_bus_ops */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ vnet_ident, /* ID string */
+ &vnetops /* driver specific ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modldrv, NULL
+};
+
+
+/*
+ * Print debug messages - set to 0xf to enable all msgs
+ */
+int _vnet_dbglevel = 0x8;
+
+void
+_vnetdebug_printf(void *arg, const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ vnet_t *vnetp = (vnet_t *)arg;
+
+ va_start(ap, fmt);
+ (void) vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ if (vnetp == NULL)
+ cmn_err(CE_CONT, "%s\n", buf);
+ else
+ cmn_err(CE_CONT, "vnet%d: %s\n", vnetp->instance, buf);
+}
+
+#ifdef DEBUG
+
+/*
+ * XXX: any changes to the definitions below need corresponding changes in
+ * vnet_gen.c
+ */
+
+/*
+ * debug levels:
+ * DBG_LEVEL1: Function entry/exit tracing
+ * DBG_LEVEL2: Info messages
+ * DBG_LEVEL3: Warning messages
+ * DBG_LEVEL4: Error messages
+ */
+
+enum { DBG_LEVEL1 = 0x01, DBG_LEVEL2 = 0x02, DBG_LEVEL3 = 0x04,
+ DBG_LEVEL4 = 0x08 };
+
+#define DBG1(_s) do { \
+ if ((_vnet_dbglevel & DBG_LEVEL1) != 0) { \
+ _vnetdebug_printf _s; \
+ } \
+ _NOTE(CONSTCOND) } while (0)
+
+#define DBG2(_s) do { \
+ if ((_vnet_dbglevel & DBG_LEVEL2) != 0) { \
+ _vnetdebug_printf _s; \
+ } \
+ _NOTE(CONSTCOND) } while (0)
+
+#define DWARN(_s) do { \
+ if ((_vnet_dbglevel & DBG_LEVEL3) != 0) { \
+ _vnetdebug_printf _s; \
+ } \
+ _NOTE(CONSTCOND) } while (0)
+
+#define DERR(_s) do { \
+ if ((_vnet_dbglevel & DBG_LEVEL4) != 0) { \
+ _vnetdebug_printf _s; \
+ } \
+ _NOTE(CONSTCOND) } while (0)
+
+#else
+
+#define DBG1(_s) if (0) _vnetdebug_printf _s
+#define DBG2(_s) if (0) _vnetdebug_printf _s
+#define DWARN(_s) if (0) _vnetdebug_printf _s
+#define DERR(_s) if (0) _vnetdebug_printf _s
+
+#endif
+
+/* _init(9E): initialize the loadable module */
+int
+_init(void)
+{
+ int status;
+
+ DBG1((NULL, "_init: enter\n"));
+
+ mac_init_ops(&vnetops, "vnet");
+ status = mod_install(&modlinkage);
+ if (status != 0) {
+ mac_fini_ops(&vnetops);
+ }
+
+ DBG1((NULL, "_init: exit\n"));
+ return (status);
+}
+
+/* _fini(9E): prepare the module for unloading. */
+int
+_fini(void)
+{
+ int status;
+
+ DBG1((NULL, "_fini: enter\n"));
+
+ status = mod_remove(&modlinkage);
+ if (status != 0)
+ return (status);
+ mac_fini_ops(&vnetops);
+
+ DBG1((NULL, "_fini: exit\n"));
+ return (status);
+}
+
+/* _info(9E): return information about the loadable module */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * attach(9E): attach a device to the system.
+ * called once for each instance of the device on the system.
+ */
+static int
+vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ mac_t *macp;
+ vnet_t *vnetp;
+ vp_tl_t *vp_tlp;
+ int instance;
+ int status;
+ enum { AST_init = 0x0, AST_vnet_alloc = 0x1,
+ AST_mac_alloc = 0x2, AST_read_macaddr = 0x4,
+ AST_vgen_init = 0x8, AST_vptl_alloc = 0x10,
+ AST_fdbh_alloc = 0x20 }
+ attach_state;
+ mac_t *vgenmacp = NULL;
+ uint32_t nfdbh = 0;
+
+ attach_state = AST_init;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ case DDI_PM_RESUME:
+ default:
+ goto vnet_attach_fail;
+ }
+
+ instance = ddi_get_instance(dip);
+ DBG1((NULL, "vnetattach: instance(%d) enter\n", instance));
+
+ /* allocate vnet_t and mac_t structures */
+ vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP);
+ attach_state |= AST_vnet_alloc;
+
+ macp = kmem_zalloc(sizeof (mac_t), KM_SLEEP);
+ attach_state |= AST_mac_alloc;
+
+ /* setup links to vnet_t from both devinfo and mac_t */
+ ddi_set_driver_private(dip, (caddr_t)vnetp);
+ macp->m_driver = vnetp;
+ vnetp->dip = dip;
+ vnetp->macp = macp;
+ vnetp->instance = instance;
+
+ /* read the mac address */
+ status = vnet_read_mac_address(vnetp);
+ if (status != DDI_SUCCESS) {
+ goto vnet_attach_fail;
+ }
+ attach_state |= AST_read_macaddr;
+
+ /*
+ * Initialize the generic vnet proxy transport. This is the first
+ * and default transport used by vnet. The generic transport
+ * is provided by using sun4v LDC (logical domain channel). On success,
+ * vgen_init() provides a pointer to mac_t of generic transport.
+ * Currently, this generic layer provides network connectivity to other
+ * vnets within ldoms and also to remote hosts oustide ldoms through
+ * the virtual switch (vsw) device on domain0. In the future, when
+ * physical adapters that are able to share their resources (such as
+ * dma channels) with guest domains become available, the vnet device
+ * will use hardware specific driver to communicate directly over the
+ * physical device to reach remote hosts without going through vswitch.
+ */
+ status = vgen_init(vnetp, vnetp->dip, vnetp->macp,
+ (uint8_t *)vnetp->curr_macaddr, &vgenmacp);
+ if (status != DDI_SUCCESS) {
+ DERR((vnetp, "vgen_init() failed\n"));
+ goto vnet_attach_fail;
+ }
+ attach_state |= AST_vgen_init;
+
+ vp_tlp = kmem_zalloc(sizeof (vp_tl_t), KM_SLEEP);
+ vp_tlp->macp = vgenmacp;
+ (void) snprintf(vp_tlp->name, MAXNAMELEN, "%s%u", "vgen", instance);
+ (void) strcpy(vnetp->vgen_name, vp_tlp->name);
+
+ /* add generic transport to the list of vnet proxy transports */
+ vnet_add_vptl(vnetp, vp_tlp);
+ attach_state |= AST_vptl_alloc;
+
+ nfdbh = vnet_nfdb_hash;
+ if ((nfdbh < VNET_NFDB_HASH) || (nfdbh > VNET_NFDB_HASH_MAX)) {
+ vnetp->nfdb_hash = VNET_NFDB_HASH;
+ }
+ else
+ vnetp->nfdb_hash = nfdbh;
+
+ /* allocate fdb hash table, with an extra slot for default route */
+ vnetp->fdbhp = kmem_zalloc(sizeof (fdb_fanout_t) *
+ (vnetp->nfdb_hash + 1), KM_SLEEP);
+ attach_state |= AST_fdbh_alloc;
+
+ /* register with MAC layer */
+ status = vnet_mac_register(vnetp);
+ if (status != DDI_SUCCESS) {
+ goto vnet_attach_fail;
+ }
+
+ /* add to the list of vnet devices */
+ WRITE_ENTER(&vnet_rw);
+ vnetp->nextp = vnet_headp;
+ vnet_headp = vnetp;
+ RW_EXIT(&vnet_rw);
+
+ DBG1((NULL, "vnetattach: instance(%d) exit\n", instance));
+ return (DDI_SUCCESS);
+
+vnet_attach_fail:
+ if (attach_state & AST_fdbh_alloc) {
+ kmem_free(vnetp->fdbhp,
+ sizeof (fdb_fanout_t) * (vnetp->nfdb_hash + 1));
+ }
+ if (attach_state & AST_vptl_alloc) {
+ WRITE_ENTER(&vnetp->trwlock);
+ vnet_del_vptl(vnetp, vp_tlp);
+ RW_EXIT(&vnetp->trwlock);
+ }
+ if (attach_state & AST_vgen_init) {
+ vgen_uninit(vgenmacp->m_driver);
+ }
+ if (attach_state & AST_mac_alloc) {
+ KMEM_FREE(macp);
+ }
+ if (attach_state & AST_vnet_alloc) {
+ KMEM_FREE(vnetp);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * detach(9E): detach a device from the system.
+ */
+static int
+vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ vnet_t *vnetp;
+ vnet_t **vnetpp;
+ vp_tl_t *vp_tlp;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ DBG1((NULL, "vnetdetach: instance(%d) enter\n", instance));
+
+ vnetp = ddi_get_driver_private(dip);
+ if (vnetp == NULL) {
+ goto vnet_detach_fail;
+ }
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ case DDI_PM_SUSPEND:
+ default:
+ goto vnet_detach_fail;
+ }
+
+ /*
+ * Unregister from the MAC subsystem. This can fail, in
+ * particular if there are DLPI style-2 streams still open -
+ * in which case we just return failure.
+ */
+ if (mac_unregister(vnetp->macp) != 0)
+ goto vnet_detach_fail;
+
+ /* unlink from instance(vnet_t) list */
+ WRITE_ENTER(&vnet_rw);
+ for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) {
+ if (*vnetpp == vnetp) {
+ *vnetpp = vnetp->nextp;
+ break;
+ }
+ }
+ RW_EXIT(&vnet_rw);
+
+ /* uninit and free vnet proxy transports */
+ WRITE_ENTER(&vnetp->trwlock);
+ while ((vp_tlp = vnetp->tlp) != NULL) {
+ if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) {
+ /* uninitialize generic transport */
+ vgen_uninit(vp_tlp->macp->m_driver);
+ }
+ vnet_del_vptl(vnetp, vp_tlp);
+ }
+ RW_EXIT(&vnetp->trwlock);
+
+ KMEM_FREE(vnetp->macp);
+ KMEM_FREE(vnetp);
+
+ return (DDI_SUCCESS);
+
+vnet_detach_fail:
+ return (DDI_FAILURE);
+}
+
+/* enable the device for transmit/receive */
+static int
+vnet_m_start(void *arg)
+{
+ vnet_t *vnetp = arg;
+ vp_tl_t *vp_tlp;
+ mac_t *vp_macp;
+
+ DBG1((vnetp, "vnet_m_start: enter\n"));
+
+ /*
+ * XXX
+ * Currently, we only have generic transport. m_start() invokes
+ * vgen_start() which enables ports/channels in vgen and
+ * initiates handshake with peer vnets and vsw. In the future when we
+ * have support for hardware specific transports, this information
+ * needs to be propagted back to vnet from vgen and we need to revisit
+ * this code (see comments in vnet_attach()).
+ *
+ */
+ WRITE_ENTER(&vnetp->trwlock);
+ for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
+ vp_macp = vp_tlp->macp;
+ vp_macp->m_start(vp_macp->m_driver);
+ }
+ RW_EXIT(&vnetp->trwlock);
+
+ DBG1((vnetp, "vnet_m_start: exit\n"));
+ return (VNET_SUCCESS);
+
+}
+
+/* stop transmit/receive for the device */
+static void
+vnet_m_stop(void *arg)
+{
+ vnet_t *vnetp = arg;
+ vp_tl_t *vp_tlp;
+ mac_t *vp_macp;
+
+ DBG1((vnetp, "vnet_m_stop: enter\n"));
+
+ WRITE_ENTER(&vnetp->trwlock);
+ for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
+ vp_macp = vp_tlp->macp;
+ vp_macp->m_stop(vp_macp->m_driver);
+ }
+ RW_EXIT(&vnetp->trwlock);
+
+ DBG1((vnetp, "vnet_m_stop: exit\n"));
+}
+
+/* set the unicast mac address of the device */
+static int
+vnet_m_unicst(void *arg, const uint8_t *macaddr)
+{
+ _NOTE(ARGUNUSED(macaddr))
+
+ vnet_t *vnetp = arg;
+
+ DBG1((vnetp, "vnet_m_unicst: enter\n"));
+ /*
+ * XXX: setting mac address dynamically is not supported.
+ */
+#if 0
+ bcopy(macaddr, vnetp->curr_macaddr, ETHERADDRL);
+#endif
+ DBG1((vnetp, "vnet_m_unicst: exit\n"));
+
+ return (VNET_SUCCESS);
+}
+
+/* enable/disable a multicast address */
+static int
+vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
+{
+ _NOTE(ARGUNUSED(add, mca))
+
+ vnet_t *vnetp = arg;
+ vp_tl_t *vp_tlp;
+ mac_t *vp_macp;
+ int rv = VNET_SUCCESS;
+
+ DBG1((vnetp, "vnet_m_multicst: enter\n"));
+ READ_ENTER(&vnetp->trwlock);
+ for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
+ if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) {
+ vp_macp = vp_tlp->macp;
+ rv = vp_macp->m_multicst(vp_macp->m_driver, add, mca);
+ break;
+ }
+ }
+ RW_EXIT(&vnetp->trwlock);
+ DBG1((vnetp, "vnet_m_multicst: exit\n"));
+ return (rv);
+}
+
+/* set or clear promiscuous mode on the device */
+static int
+vnet_m_promisc(void *arg, boolean_t on)
+{
+ _NOTE(ARGUNUSED(on))
+
+ vnet_t *vnetp = arg;
+ DBG1((vnetp, "vnet_m_promisc: enter\n"));
+ /*
+ * XXX: setting promiscuous mode is not supported, just return success.
+ */
+ DBG1((vnetp, "vnet_m_promisc: exit\n"));
+ return (VNET_SUCCESS);
+}
+
+/*
+ * Transmit a chain of packets. This function provides switching functionality
+ * based on the destination mac address to reach other guests (within ldoms) or
+ * external hosts.
+ */
+mblk_t *
+vnet_m_tx(void *arg, mblk_t *mp)
+{
+ vnet_t *vnetp;
+ mblk_t *next;
+ uint32_t fdbhash;
+ fdb_t *fdbp;
+ fdb_fanout_t *fdbhp;
+ struct ether_header *ehp;
+ uint8_t *macaddr;
+ mblk_t *resid_mp;
+
+ vnetp = (vnet_t *)arg;
+ DBG1((vnetp, "vnet_m_tx: enter\n"));
+ ASSERT(mp != NULL);
+
+ while (mp != NULL) {
+ next = mp->b_next;
+ mp->b_next = NULL;
+
+ /* get the destination mac address in the eth header */
+ ehp = (struct ether_header *)mp->b_rptr;
+ macaddr = (uint8_t *)&ehp->ether_dhost;
+
+ /* Calculate hash value and fdb fanout */
+ fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
+ fdbhp = &(vnetp->fdbhp[fdbhash]);
+
+ READ_ENTER(&fdbhp->rwlock);
+ fdbp = vnet_lookup_fdb(fdbhp, macaddr);
+ if (fdbp) {
+ /*
+ * If the destination is in FDB, the destination is
+ * a vnet device within ldoms and directly reachable,
+ * invoke the tx function in the fdb entry.
+ */
+ resid_mp = fdbp->m_tx(fdbp->txarg, mp);
+ if (resid_mp != NULL) {
+ /* m_tx failed */
+ mp->b_next = next;
+ RW_EXIT(&fdbhp->rwlock);
+ break;
+ }
+ RW_EXIT(&fdbhp->rwlock);
+ } else {
+ /* destination is not in FDB */
+ RW_EXIT(&fdbhp->rwlock);
+ /*
+ * If the destination is broadcast/multicast
+ * or an unknown unicast address, forward the
+ * packet to vsw, using the last slot in fdb which is
+ * reserved for default route.
+ */
+ fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
+ READ_ENTER(&fdbhp->rwlock);
+ fdbp = fdbhp->headp;
+ if (fdbp) {
+ resid_mp = fdbp->m_tx(fdbp->txarg, mp);
+ if (resid_mp != NULL) {
+ /* m_tx failed */
+ mp->b_next = next;
+ RW_EXIT(&fdbhp->rwlock);
+ break;
+ }
+ } else {
+ /* drop the packet */
+ freemsg(mp);
+ }
+ RW_EXIT(&fdbhp->rwlock);
+ }
+
+ mp = next;
+ }
+
+ DBG1((vnetp, "vnet_m_tx: exit\n"));
+ return (mp);
+}
+
+/* register resources with mac layer */
+static void
+vnet_m_resources(void *arg)
+{
+ vnet_t *vnetp = arg;
+ vp_tl_t *vp_tlp;
+ mac_t *vp_macp;
+
+ DBG1((vnetp, "vnet_m_resources: enter\n"));
+
+ WRITE_ENTER(&vnetp->trwlock);
+ for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
+ vp_macp = vp_tlp->macp;
+ vp_macp->m_resources(vp_macp->m_driver);
+ }
+ RW_EXIT(&vnetp->trwlock);
+
+ DBG1((vnetp, "vnet_m_resources: exit\n"));
+}
+
+/*
+ * vnet specific ioctls
+ */
+static void
+vnet_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ struct iocblk *iocp;
+ int cmd;
+
+ DBG1((vnetp, "vnet_m_ioctl: enter\n"));
+
+ iocp = (struct iocblk *)mp->b_rptr;
+ iocp->ioc_error = 0;
+ cmd = iocp->ioc_cmd;
+ switch (cmd) {
+ default:
+ miocnak(wq, mp, 0, EINVAL);
+ break;
+ }
+ DBG1((vnetp, "vnet_m_ioctl: exit\n"));
+}
+
+/* get statistics from the device */
+uint64_t
+vnet_m_stat(void *arg, enum mac_stat stat)
+{
+ vnet_t *vnetp = arg;
+ vp_tl_t *vp_tlp;
+ mac_t *vp_macp;
+ uint64_t val = 0;
+
+ DBG1((vnetp, "vnet_m_stat: enter\n"));
+
+ /*
+ * get the specified statistic from each transport
+ * and return the aggregate val
+ */
+ READ_ENTER(&vnetp->trwlock);
+ for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
+ vp_macp = vp_tlp->macp;
+ val += vp_macp->m_stat(vp_macp->m_driver, stat);
+ }
+ RW_EXIT(&vnetp->trwlock);
+
+ DBG1((vnetp, "vnet_m_stat: exit\n"));
+ return (val);
+}
+
+/* wrapper function for mac_register() */
+static int
+vnet_mac_register(vnet_t *vnetp)
+{
+ mac_info_t *mip;
+ mac_t *macp;
+
+ macp = vnetp->macp;
+
+ mip = &(macp->m_info);
+ mip->mi_media = DL_ETHER;
+ mip->mi_sdu_min = 0;
+ mip->mi_sdu_max = ETHERMTU;
+ mip->mi_cksum = 0;
+ mip->mi_poll = 0; /* DL_CAPAB_POLL ? */
+ mip->mi_addr_length = ETHERADDRL;
+ bcopy(&etherbroadcastaddr, mip->mi_brdcst_addr, ETHERADDRL);
+ bcopy(vnetp->curr_macaddr, mip->mi_unicst_addr, ETHERADDRL);
+
+ MAC_STAT_MIB(mip->mi_stat);
+ mip->mi_stat[MAC_STAT_UNKNOWNS] = B_FALSE;
+ MAC_STAT_ETHER(mip->mi_stat);
+ mip->mi_stat[MAC_STAT_SQE_ERRORS] = B_FALSE;
+ mip->mi_stat[MAC_STAT_MACRCV_ERRORS] = B_FALSE;
+
+ macp->m_stat = vnet_m_stat;
+ macp->m_start = vnet_m_start;
+ macp->m_stop = vnet_m_stop;
+ macp->m_promisc = vnet_m_promisc;
+ macp->m_multicst = vnet_m_multicst;
+ macp->m_unicst = vnet_m_unicst;
+ macp->m_resources = vnet_m_resources;
+ macp->m_ioctl = vnet_m_ioctl;
+ macp->m_tx = vnet_m_tx;
+
+ macp->m_dip = vnetp->dip;
+ macp->m_ident = MAC_IDENT;
+
+ /*
+ * Finally, we're ready to register ourselves with the MAC layer
+ * interface; if this succeeds, we're all ready to start()
+ */
+ if (mac_register(macp) != 0) {
+ KMEM_FREE(macp);
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/* add vp_tl to the list */
+static void
+vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp)
+{
+ vp_tl_t *ttlp;
+
+ WRITE_ENTER(&vnetp->trwlock);
+ if (vnetp->tlp == NULL) {
+ vnetp->tlp = vp_tlp;
+ } else {
+ ttlp = vnetp->tlp;
+ while (ttlp->nextp)
+ ttlp = ttlp->nextp;
+ ttlp->nextp = vp_tlp;
+ }
+ RW_EXIT(&vnetp->trwlock);
+}
+
+/* remove vp_tl from the list */
+static void
+vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp)
+{
+ vp_tl_t *ttlp, **pretlp;
+ boolean_t found = B_FALSE;
+
+ pretlp = &vnetp->tlp;
+ ttlp = *pretlp;
+ while (ttlp) {
+ if (ttlp == vp_tlp) {
+ found = B_TRUE;
+ (*pretlp) = ttlp->nextp;
+ ttlp->nextp = NULL;
+ break;
+ }
+ pretlp = &(ttlp->nextp);
+ ttlp = *pretlp;
+ }
+
+ if (found) {
+ KMEM_FREE(vp_tlp);
+ }
+}
+
+/* get vp_tl corresponding to the given name */
+static vp_tl_t *
+vnet_get_vptl(vnet_t *vnetp, const char *name)
+{
+ vp_tl_t *tlp;
+
+ tlp = vnetp->tlp;
+ while (tlp) {
+ if (strcmp(tlp->name, name) == 0) {
+ return (tlp);
+ }
+ tlp = tlp->nextp;
+ }
+ DWARN((vnetp,
+ "vnet_get_vptl: can't find vp_tl with name (%s)\n", name));
+ return (NULL);
+}
+
+/* read the mac address of the device */
+static int
+vnet_read_mac_address(vnet_t *vnetp)
+{
+ uchar_t *macaddr;
+ uint32_t size;
+ int rv;
+
+ rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip,
+ DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size);
+ if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) {
+ DWARN((vnetp,
+ "vnet_read_mac_address: prop_lookup failed (%s) err (%d)\n",
+ macaddr_propname, rv));
+ return (DDI_FAILURE);
+ }
+ bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL);
+ bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL);
+ ddi_prop_free(macaddr);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * Functions below are called only by generic transport to add/remove/modify
+ * entries in forwarding database. See comments in vgen_port_init(vnet_gen.c).
+ */
+
+/* add an entry into the forwarding database */
+void
+vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ uint32_t fdbhash;
+ fdb_t *fdbp;
+ fdb_fanout_t *fdbhp;
+
+ /* Calculate hash value and fdb fanout */
+ fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
+ fdbhp = &(vnetp->fdbhp[fdbhash]);
+
+ WRITE_ENTER(&fdbhp->rwlock);
+
+ fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP);
+ if (fdbp == NULL) {
+ RW_EXIT(&fdbhp->rwlock);
+ return;
+ }
+ bcopy(macaddr, (caddr_t)fdbp->macaddr, ETHERADDRL);
+ fdbp->m_tx = m_tx;
+ fdbp->txarg = txarg;
+ fdbp->nextp = fdbhp->headp;
+ fdbhp->headp = fdbp;
+
+ RW_EXIT(&fdbhp->rwlock);
+}
+
+/* delete an entry from the forwarding database */
+void
+vnet_del_fdb(void *arg, uint8_t *macaddr)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ uint32_t fdbhash;
+ fdb_t *fdbp;
+ fdb_t **pfdbp;
+ fdb_fanout_t *fdbhp;
+
+ /* Calculate hash value and fdb fanout */
+ fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
+ fdbhp = &(vnetp->fdbhp[fdbhash]);
+
+ WRITE_ENTER(&fdbhp->rwlock);
+
+ for (pfdbp = &fdbhp->headp; (fdbp = *pfdbp) != NULL;
+ pfdbp = &fdbp->nextp) {
+ if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
+ /* Unlink it from the list */
+ *pfdbp = fdbp->nextp;
+ KMEM_FREE(fdbp);
+ break;
+ }
+ }
+
+ RW_EXIT(&fdbhp->rwlock);
+}
+
+/* modify an existing entry in the forwarding database */
+void
+vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ uint32_t fdbhash;
+ fdb_t *fdbp;
+ fdb_fanout_t *fdbhp;
+
+ /* Calculate hash value and fdb fanout */
+ fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
+ fdbhp = &(vnetp->fdbhp[fdbhash]);
+
+ WRITE_ENTER(&fdbhp->rwlock);
+
+ for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) {
+ if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
+ /* change the entry to have new tx params */
+ fdbp->m_tx = m_tx;
+ fdbp->txarg = txarg;
+ break;
+ }
+ }
+
+ RW_EXIT(&fdbhp->rwlock);
+}
+
+/* look up an fdb entry based on the mac address, caller holds lock */
+static fdb_t *
+vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr)
+{
+ fdb_t *fdbp = NULL;
+
+ for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) {
+ if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
+ break;
+ }
+ }
+
+ return (fdbp);
+}
+
+/* add default route entry into the forwarding database */
+void
+vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ fdb_t *fdbp;
+ fdb_fanout_t *fdbhp;
+
+ /*
+ * The last hash list is reserved for default route entry,
+ * and for now, we have only one entry in this list.
+ */
+ fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
+
+ WRITE_ENTER(&fdbhp->rwlock);
+
+ if (fdbhp->headp) {
+ DWARN((vnetp,
+ "vnet_add_def_rte: default rte already exists\n"));
+ RW_EXIT(&fdbhp->rwlock);
+ return;
+ }
+ fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP);
+ if (fdbp == NULL) {
+ RW_EXIT(&fdbhp->rwlock);
+ return;
+ }
+ bzero(fdbp->macaddr, ETHERADDRL);
+ fdbp->m_tx = m_tx;
+ fdbp->txarg = txarg;
+ fdbp->nextp = NULL;
+ fdbhp->headp = fdbp;
+
+ RW_EXIT(&fdbhp->rwlock);
+}
+
+/* delete default route entry from the forwarding database */
+void
+vnet_del_def_rte(void *arg)
+{
+ vnet_t *vnetp = (vnet_t *)arg;
+ fdb_t *fdbp;
+ fdb_fanout_t *fdbhp;
+
+ /*
+ * The last hash list is reserved for default route entry,
+ * and for now, we have only one entry in this list.
+ */
+ fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
+
+ WRITE_ENTER(&fdbhp->rwlock);
+
+ if (fdbhp->headp == NULL) {
+ RW_EXIT(&fdbhp->rwlock);
+ return;
+ }
+ fdbp = fdbhp->headp;
+ KMEM_FREE(fdbp);
+ fdbhp->headp = NULL;
+
+ RW_EXIT(&fdbhp->rwlock);
+}