/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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); }