diff options
Diffstat (limited to 'usr/src/uts/sun4v/io/vnet.c')
| -rw-r--r-- | usr/src/uts/sun4v/io/vnet.c | 1049 |
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, ðerbroadcastaddr) == 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(ðerbroadcastaddr, 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); +} |
