11ae08745Sheppo /* 21ae08745Sheppo * CDDL HEADER START 31ae08745Sheppo * 41ae08745Sheppo * The contents of this file are subject to the terms of the 51ae08745Sheppo * Common Development and Distribution License (the "License"). 61ae08745Sheppo * You may not use this file except in compliance with the License. 71ae08745Sheppo * 81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91ae08745Sheppo * or http://www.opensolaris.org/os/licensing. 101ae08745Sheppo * See the License for the specific language governing permissions 111ae08745Sheppo * and limitations under the License. 121ae08745Sheppo * 131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the 161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181ae08745Sheppo * 191ae08745Sheppo * CDDL HEADER END 201ae08745Sheppo */ 211ae08745Sheppo 221ae08745Sheppo /* 23*c1c61f44Ssb155480 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 241ae08745Sheppo * Use is subject to license terms. 251ae08745Sheppo */ 261ae08745Sheppo 271ae08745Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 281ae08745Sheppo 291ae08745Sheppo #include <sys/types.h> 301ae08745Sheppo #include <sys/errno.h> 311ae08745Sheppo #include <sys/param.h> 321ae08745Sheppo #include <sys/stream.h> 331ae08745Sheppo #include <sys/kmem.h> 341ae08745Sheppo #include <sys/conf.h> 351ae08745Sheppo #include <sys/devops.h> 361ae08745Sheppo #include <sys/ksynch.h> 371ae08745Sheppo #include <sys/stat.h> 381ae08745Sheppo #include <sys/modctl.h> 39*c1c61f44Ssb155480 #include <sys/modhash.h> 401ae08745Sheppo #include <sys/debug.h> 411ae08745Sheppo #include <sys/ethernet.h> 421ae08745Sheppo #include <sys/dlpi.h> 431ae08745Sheppo #include <net/if.h> 441ae08745Sheppo #include <sys/mac.h> 45ba2e4443Sseb #include <sys/mac_ether.h> 461ae08745Sheppo #include <sys/ddi.h> 471ae08745Sheppo #include <sys/sunddi.h> 481ae08745Sheppo #include <sys/strsun.h> 491ae08745Sheppo #include <sys/note.h> 50*c1c61f44Ssb155480 #include <sys/atomic.h> 511ae08745Sheppo #include <sys/vnet.h> 52*c1c61f44Ssb155480 #include <sys/vlan.h> 531ae08745Sheppo 541ae08745Sheppo /* 551ae08745Sheppo * Function prototypes. 561ae08745Sheppo */ 571ae08745Sheppo 581ae08745Sheppo /* DDI entrypoints */ 591ae08745Sheppo static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 601ae08745Sheppo static int vnetattach(dev_info_t *, ddi_attach_cmd_t); 611ae08745Sheppo static int vnetdetach(dev_info_t *, ddi_detach_cmd_t); 621ae08745Sheppo 631ae08745Sheppo /* MAC entrypoints */ 64ba2e4443Sseb static int vnet_m_stat(void *, uint_t, uint64_t *); 651ae08745Sheppo static int vnet_m_start(void *); 661ae08745Sheppo static void vnet_m_stop(void *); 671ae08745Sheppo static int vnet_m_promisc(void *, boolean_t); 681ae08745Sheppo static int vnet_m_multicst(void *, boolean_t, const uint8_t *); 691ae08745Sheppo static int vnet_m_unicst(void *, const uint8_t *); 701ae08745Sheppo mblk_t *vnet_m_tx(void *, mblk_t *); 711ae08745Sheppo 721ae08745Sheppo /* vnet internal functions */ 731ae08745Sheppo static int vnet_mac_register(vnet_t *); 741ae08745Sheppo static int vnet_read_mac_address(vnet_t *vnetp); 751ae08745Sheppo static void vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp); 761ae08745Sheppo static void vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp); 771ae08745Sheppo static vp_tl_t *vnet_get_vptl(vnet_t *vnetp, const char *devname); 781ae08745Sheppo 79*c1c61f44Ssb155480 /* Forwarding database (FDB) routines */ 80*c1c61f44Ssb155480 static void vnet_fdb_create(vnet_t *vnetp); 81*c1c61f44Ssb155480 static void vnet_fdb_destroy(vnet_t *vnetp); 82*c1c61f44Ssb155480 static vnet_fdbe_t *vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *eaddr); 83*c1c61f44Ssb155480 static void vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val); 84*c1c61f44Ssb155480 void vnet_fdbe_add(vnet_t *vnetp, struct ether_addr *macaddr, 85*c1c61f44Ssb155480 uint8_t type, mac_tx_t m_tx, void *port); 86*c1c61f44Ssb155480 void vnet_fdbe_del(vnet_t *vnetp, struct ether_addr *eaddr); 87*c1c61f44Ssb155480 void vnet_fdbe_modify(vnet_t *vnetp, struct ether_addr *macaddr, 88*c1c61f44Ssb155480 void *portp, boolean_t flag); 89*c1c61f44Ssb155480 90ba2e4443Sseb void vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp); 91ba2e4443Sseb void vnet_tx_update(void *arg); 921ae08745Sheppo 931ae08745Sheppo /* externs */ 94*c1c61f44Ssb155480 extern int vgen_init(vnet_t *vnetp, dev_info_t *vnetdip, const uint8_t *macaddr, 95ba2e4443Sseb mac_register_t **vgenmacp); 96d10e4ef2Snarayan extern int vgen_uninit(void *arg); 971ae08745Sheppo 98*c1c61f44Ssb155480 #define VNET_FDBE_REFHOLD(p) \ 99*c1c61f44Ssb155480 { \ 100*c1c61f44Ssb155480 atomic_inc_32(&(p)->refcnt); \ 101*c1c61f44Ssb155480 ASSERT((p)->refcnt != 0); \ 102*c1c61f44Ssb155480 } 103*c1c61f44Ssb155480 104*c1c61f44Ssb155480 #define VNET_FDBE_REFRELE(p) \ 105*c1c61f44Ssb155480 { \ 106*c1c61f44Ssb155480 ASSERT((p)->refcnt != 0); \ 107*c1c61f44Ssb155480 atomic_dec_32(&(p)->refcnt); \ 108*c1c61f44Ssb155480 } 109*c1c61f44Ssb155480 110ba2e4443Sseb static mac_callbacks_t vnet_m_callbacks = { 111ba2e4443Sseb 0, 112ba2e4443Sseb vnet_m_stat, 113ba2e4443Sseb vnet_m_start, 114ba2e4443Sseb vnet_m_stop, 115ba2e4443Sseb vnet_m_promisc, 116ba2e4443Sseb vnet_m_multicst, 117ba2e4443Sseb vnet_m_unicst, 118ba2e4443Sseb vnet_m_tx, 119ba2e4443Sseb NULL, 120ba2e4443Sseb NULL, 121ba2e4443Sseb NULL 122ba2e4443Sseb }; 123ba2e4443Sseb 1241ae08745Sheppo /* 1251ae08745Sheppo * Linked list of "vnet_t" structures - one per instance. 1261ae08745Sheppo */ 1271ae08745Sheppo static vnet_t *vnet_headp = NULL; 1281ae08745Sheppo static krwlock_t vnet_rw; 1291ae08745Sheppo 1301ae08745Sheppo /* Tunables */ 1311ae08745Sheppo uint32_t vnet_ntxds = VNET_NTXDS; /* power of 2 transmit descriptors */ 1321ae08745Sheppo uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */ 1331ae08745Sheppo uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT; /* tx timeout in msec */ 134e1ebb9ecSlm66018 uint32_t vnet_ldc_mtu = VNET_LDC_MTU; /* ldc mtu */ 135*c1c61f44Ssb155480 136*c1c61f44Ssb155480 /* # of chains in fdb hash table */ 137*c1c61f44Ssb155480 uint32_t vnet_fdb_nchains = VNET_NFDB_HASH; 138*c1c61f44Ssb155480 139*c1c61f44Ssb155480 /* Internal tunables */ 140*c1c61f44Ssb155480 uint32_t vnet_ethermtu = 1500; /* mtu of the device */ 141*c1c61f44Ssb155480 142*c1c61f44Ssb155480 /* 143*c1c61f44Ssb155480 * Default vlan id. This is only used internally when the "default-vlan-id" 144*c1c61f44Ssb155480 * property is not present in the MD device node. Therefore, this should not be 145*c1c61f44Ssb155480 * used as a tunable; if this value is changed, the corresponding variable 146*c1c61f44Ssb155480 * should be updated to the same value in vsw and also other vnets connected to 147*c1c61f44Ssb155480 * the same vsw. 148*c1c61f44Ssb155480 */ 149*c1c61f44Ssb155480 uint16_t vnet_default_vlan_id = 1; 150*c1c61f44Ssb155480 151*c1c61f44Ssb155480 /* delay in usec to wait for all references on a fdb entry to be dropped */ 152*c1c61f44Ssb155480 uint32_t vnet_fdbe_refcnt_delay = 10; 1531ae08745Sheppo 1541ae08745Sheppo /* 1551ae08745Sheppo * Property names 1561ae08745Sheppo */ 1571ae08745Sheppo static char macaddr_propname[] = "local-mac-address"; 1581ae08745Sheppo 1591ae08745Sheppo /* 1601ae08745Sheppo * This is the string displayed by modinfo(1m). 1611ae08745Sheppo */ 1628e6a2a04Slm66018 static char vnet_ident[] = "vnet driver v%I%"; 1631ae08745Sheppo extern struct mod_ops mod_driverops; 1641ae08745Sheppo static struct cb_ops cb_vnetops = { 1651ae08745Sheppo nulldev, /* cb_open */ 1661ae08745Sheppo nulldev, /* cb_close */ 1671ae08745Sheppo nodev, /* cb_strategy */ 1681ae08745Sheppo nodev, /* cb_print */ 1691ae08745Sheppo nodev, /* cb_dump */ 1701ae08745Sheppo nodev, /* cb_read */ 1711ae08745Sheppo nodev, /* cb_write */ 1721ae08745Sheppo nodev, /* cb_ioctl */ 1731ae08745Sheppo nodev, /* cb_devmap */ 1741ae08745Sheppo nodev, /* cb_mmap */ 1751ae08745Sheppo nodev, /* cb_segmap */ 1761ae08745Sheppo nochpoll, /* cb_chpoll */ 1771ae08745Sheppo ddi_prop_op, /* cb_prop_op */ 1781ae08745Sheppo NULL, /* cb_stream */ 1791ae08745Sheppo (int)(D_MP) /* cb_flag */ 1801ae08745Sheppo }; 1811ae08745Sheppo 1821ae08745Sheppo static struct dev_ops vnetops = { 1831ae08745Sheppo DEVO_REV, /* devo_rev */ 1841ae08745Sheppo 0, /* devo_refcnt */ 1851ae08745Sheppo NULL, /* devo_getinfo */ 1861ae08745Sheppo nulldev, /* devo_identify */ 1871ae08745Sheppo nulldev, /* devo_probe */ 1881ae08745Sheppo vnetattach, /* devo_attach */ 1891ae08745Sheppo vnetdetach, /* devo_detach */ 1901ae08745Sheppo nodev, /* devo_reset */ 1911ae08745Sheppo &cb_vnetops, /* devo_cb_ops */ 1921ae08745Sheppo (struct bus_ops *)NULL /* devo_bus_ops */ 1931ae08745Sheppo }; 1941ae08745Sheppo 1951ae08745Sheppo static struct modldrv modldrv = { 1961ae08745Sheppo &mod_driverops, /* Type of module. This one is a driver */ 1971ae08745Sheppo vnet_ident, /* ID string */ 1981ae08745Sheppo &vnetops /* driver specific ops */ 1991ae08745Sheppo }; 2001ae08745Sheppo 2011ae08745Sheppo static struct modlinkage modlinkage = { 2021ae08745Sheppo MODREV_1, (void *)&modldrv, NULL 2031ae08745Sheppo }; 2041ae08745Sheppo 205844e62a3Sraghuram #ifdef DEBUG 2061ae08745Sheppo 2071ae08745Sheppo /* 2081ae08745Sheppo * Print debug messages - set to 0xf to enable all msgs 2091ae08745Sheppo */ 210844e62a3Sraghuram int vnet_dbglevel = 0x8; 2111ae08745Sheppo 212844e62a3Sraghuram static void 213844e62a3Sraghuram debug_printf(const char *fname, void *arg, const char *fmt, ...) 2141ae08745Sheppo { 2151ae08745Sheppo char buf[512]; 2161ae08745Sheppo va_list ap; 2171ae08745Sheppo vnet_t *vnetp = (vnet_t *)arg; 218844e62a3Sraghuram char *bufp = buf; 2191ae08745Sheppo 220844e62a3Sraghuram if (vnetp == NULL) { 221844e62a3Sraghuram (void) sprintf(bufp, "%s: ", fname); 222844e62a3Sraghuram bufp += strlen(bufp); 223844e62a3Sraghuram } else { 224844e62a3Sraghuram (void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname); 225844e62a3Sraghuram bufp += strlen(bufp); 2261ae08745Sheppo } 227844e62a3Sraghuram va_start(ap, fmt); 228844e62a3Sraghuram (void) vsprintf(bufp, fmt, ap); 229844e62a3Sraghuram va_end(ap); 230844e62a3Sraghuram cmn_err(CE_CONT, "%s\n", buf); 231844e62a3Sraghuram } 2321ae08745Sheppo 2331ae08745Sheppo #endif 2341ae08745Sheppo 2351ae08745Sheppo /* _init(9E): initialize the loadable module */ 2361ae08745Sheppo int 2371ae08745Sheppo _init(void) 2381ae08745Sheppo { 2391ae08745Sheppo int status; 2401ae08745Sheppo 241844e62a3Sraghuram DBG1(NULL, "enter\n"); 2421ae08745Sheppo 2431ae08745Sheppo mac_init_ops(&vnetops, "vnet"); 2441ae08745Sheppo status = mod_install(&modlinkage); 2451ae08745Sheppo if (status != 0) { 2461ae08745Sheppo mac_fini_ops(&vnetops); 2471ae08745Sheppo } 2481ae08745Sheppo 249844e62a3Sraghuram DBG1(NULL, "exit(%d)\n", status); 2501ae08745Sheppo return (status); 2511ae08745Sheppo } 2521ae08745Sheppo 2531ae08745Sheppo /* _fini(9E): prepare the module for unloading. */ 2541ae08745Sheppo int 2551ae08745Sheppo _fini(void) 2561ae08745Sheppo { 2571ae08745Sheppo int status; 2581ae08745Sheppo 259844e62a3Sraghuram DBG1(NULL, "enter\n"); 2601ae08745Sheppo 2611ae08745Sheppo status = mod_remove(&modlinkage); 2621ae08745Sheppo if (status != 0) 2631ae08745Sheppo return (status); 2641ae08745Sheppo mac_fini_ops(&vnetops); 2651ae08745Sheppo 266844e62a3Sraghuram DBG1(NULL, "exit(%d)\n", status); 2671ae08745Sheppo return (status); 2681ae08745Sheppo } 2691ae08745Sheppo 2701ae08745Sheppo /* _info(9E): return information about the loadable module */ 2711ae08745Sheppo int 2721ae08745Sheppo _info(struct modinfo *modinfop) 2731ae08745Sheppo { 2741ae08745Sheppo return (mod_info(&modlinkage, modinfop)); 2751ae08745Sheppo } 2761ae08745Sheppo 2771ae08745Sheppo /* 2781ae08745Sheppo * attach(9E): attach a device to the system. 2791ae08745Sheppo * called once for each instance of the device on the system. 2801ae08745Sheppo */ 2811ae08745Sheppo static int 2821ae08745Sheppo vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2831ae08745Sheppo { 2841ae08745Sheppo vnet_t *vnetp; 2851ae08745Sheppo vp_tl_t *vp_tlp; 2861ae08745Sheppo int instance; 2871ae08745Sheppo int status; 288844e62a3Sraghuram mac_register_t *vgenmacp = NULL; 2891ae08745Sheppo enum { AST_init = 0x0, AST_vnet_alloc = 0x1, 290d10e4ef2Snarayan AST_mac_alloc = 0x2, AST_read_macaddr = 0x4, 291d10e4ef2Snarayan AST_vgen_init = 0x8, AST_vptl_alloc = 0x10, 292844e62a3Sraghuram AST_fdbh_alloc = 0x20 } attach_state; 2931ae08745Sheppo 2941ae08745Sheppo attach_state = AST_init; 2951ae08745Sheppo 2961ae08745Sheppo switch (cmd) { 2971ae08745Sheppo case DDI_ATTACH: 2981ae08745Sheppo break; 2991ae08745Sheppo case DDI_RESUME: 3001ae08745Sheppo case DDI_PM_RESUME: 3011ae08745Sheppo default: 3021ae08745Sheppo goto vnet_attach_fail; 3031ae08745Sheppo } 3041ae08745Sheppo 3051ae08745Sheppo instance = ddi_get_instance(dip); 306844e62a3Sraghuram DBG1(NULL, "instance(%d) enter\n", instance); 3071ae08745Sheppo 3081ae08745Sheppo /* allocate vnet_t and mac_t structures */ 3091ae08745Sheppo vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP); 3101ae08745Sheppo attach_state |= AST_vnet_alloc; 3111ae08745Sheppo 3121ae08745Sheppo /* setup links to vnet_t from both devinfo and mac_t */ 3131ae08745Sheppo ddi_set_driver_private(dip, (caddr_t)vnetp); 3141ae08745Sheppo vnetp->dip = dip; 3151ae08745Sheppo vnetp->instance = instance; 3161ae08745Sheppo 3171ae08745Sheppo /* read the mac address */ 3181ae08745Sheppo status = vnet_read_mac_address(vnetp); 3191ae08745Sheppo if (status != DDI_SUCCESS) { 3201ae08745Sheppo goto vnet_attach_fail; 3211ae08745Sheppo } 3221ae08745Sheppo attach_state |= AST_read_macaddr; 3231ae08745Sheppo 3241ae08745Sheppo /* 3251ae08745Sheppo * Initialize the generic vnet proxy transport. This is the first 3261ae08745Sheppo * and default transport used by vnet. The generic transport 3271ae08745Sheppo * is provided by using sun4v LDC (logical domain channel). On success, 3281ae08745Sheppo * vgen_init() provides a pointer to mac_t of generic transport. 3291ae08745Sheppo * Currently, this generic layer provides network connectivity to other 3301ae08745Sheppo * vnets within ldoms and also to remote hosts oustide ldoms through 3311ae08745Sheppo * the virtual switch (vsw) device on domain0. In the future, when 3321ae08745Sheppo * physical adapters that are able to share their resources (such as 3331ae08745Sheppo * dma channels) with guest domains become available, the vnet device 3341ae08745Sheppo * will use hardware specific driver to communicate directly over the 3351ae08745Sheppo * physical device to reach remote hosts without going through vswitch. 3361ae08745Sheppo */ 337ba2e4443Sseb status = vgen_init(vnetp, vnetp->dip, (uint8_t *)vnetp->curr_macaddr, 338ba2e4443Sseb &vgenmacp); 3391ae08745Sheppo if (status != DDI_SUCCESS) { 340844e62a3Sraghuram DERR(vnetp, "vgen_init() failed\n"); 3411ae08745Sheppo goto vnet_attach_fail; 3421ae08745Sheppo } 34393b13a42Swentaoy rw_init(&vnetp->trwlock, NULL, RW_DRIVER, NULL); 3441ae08745Sheppo attach_state |= AST_vgen_init; 3451ae08745Sheppo 3461ae08745Sheppo vp_tlp = kmem_zalloc(sizeof (vp_tl_t), KM_SLEEP); 3471ae08745Sheppo vp_tlp->macp = vgenmacp; 3481ae08745Sheppo (void) snprintf(vp_tlp->name, MAXNAMELEN, "%s%u", "vgen", instance); 3491ae08745Sheppo (void) strcpy(vnetp->vgen_name, vp_tlp->name); 3501ae08745Sheppo 3511ae08745Sheppo /* add generic transport to the list of vnet proxy transports */ 3521ae08745Sheppo vnet_add_vptl(vnetp, vp_tlp); 3531ae08745Sheppo attach_state |= AST_vptl_alloc; 3541ae08745Sheppo 355*c1c61f44Ssb155480 vnet_fdb_create(vnetp); 3561ae08745Sheppo attach_state |= AST_fdbh_alloc; 3571ae08745Sheppo 3581ae08745Sheppo /* register with MAC layer */ 3591ae08745Sheppo status = vnet_mac_register(vnetp); 3601ae08745Sheppo if (status != DDI_SUCCESS) { 3611ae08745Sheppo goto vnet_attach_fail; 3621ae08745Sheppo } 3631ae08745Sheppo 3641ae08745Sheppo /* add to the list of vnet devices */ 3651ae08745Sheppo WRITE_ENTER(&vnet_rw); 3661ae08745Sheppo vnetp->nextp = vnet_headp; 3671ae08745Sheppo vnet_headp = vnetp; 3681ae08745Sheppo RW_EXIT(&vnet_rw); 3691ae08745Sheppo 370844e62a3Sraghuram DBG1(NULL, "instance(%d) exit\n", instance); 3711ae08745Sheppo return (DDI_SUCCESS); 3721ae08745Sheppo 3731ae08745Sheppo vnet_attach_fail: 3741ae08745Sheppo if (attach_state & AST_fdbh_alloc) { 375*c1c61f44Ssb155480 vnet_fdb_destroy(vnetp); 3761ae08745Sheppo } 3771ae08745Sheppo if (attach_state & AST_vptl_alloc) { 3781ae08745Sheppo WRITE_ENTER(&vnetp->trwlock); 3791ae08745Sheppo vnet_del_vptl(vnetp, vp_tlp); 3801ae08745Sheppo RW_EXIT(&vnetp->trwlock); 3811ae08745Sheppo } 3821ae08745Sheppo if (attach_state & AST_vgen_init) { 383d10e4ef2Snarayan (void) vgen_uninit(vgenmacp->m_driver); 38493b13a42Swentaoy rw_destroy(&vnetp->trwlock); 3851ae08745Sheppo } 3861ae08745Sheppo if (attach_state & AST_vnet_alloc) { 3871ae08745Sheppo KMEM_FREE(vnetp); 3881ae08745Sheppo } 3891ae08745Sheppo return (DDI_FAILURE); 3901ae08745Sheppo } 3911ae08745Sheppo 3921ae08745Sheppo /* 3931ae08745Sheppo * detach(9E): detach a device from the system. 3941ae08745Sheppo */ 3951ae08745Sheppo static int 3961ae08745Sheppo vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3971ae08745Sheppo { 3981ae08745Sheppo vnet_t *vnetp; 3991ae08745Sheppo vnet_t **vnetpp; 4001ae08745Sheppo vp_tl_t *vp_tlp; 4011ae08745Sheppo int instance; 402d10e4ef2Snarayan int rv; 4031ae08745Sheppo 4041ae08745Sheppo instance = ddi_get_instance(dip); 405844e62a3Sraghuram DBG1(NULL, "instance(%d) enter\n", instance); 4061ae08745Sheppo 4071ae08745Sheppo vnetp = ddi_get_driver_private(dip); 4081ae08745Sheppo if (vnetp == NULL) { 4091ae08745Sheppo goto vnet_detach_fail; 4101ae08745Sheppo } 4111ae08745Sheppo 4121ae08745Sheppo switch (cmd) { 4131ae08745Sheppo case DDI_DETACH: 4141ae08745Sheppo break; 4151ae08745Sheppo case DDI_SUSPEND: 4161ae08745Sheppo case DDI_PM_SUSPEND: 4171ae08745Sheppo default: 4181ae08745Sheppo goto vnet_detach_fail; 4191ae08745Sheppo } 4201ae08745Sheppo 421d10e4ef2Snarayan /* uninit and free vnet proxy transports */ 422d10e4ef2Snarayan WRITE_ENTER(&vnetp->trwlock); 423d10e4ef2Snarayan while ((vp_tlp = vnetp->tlp) != NULL) { 424d10e4ef2Snarayan if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) { 425d10e4ef2Snarayan /* uninitialize generic transport */ 426d10e4ef2Snarayan rv = vgen_uninit(vp_tlp->macp->m_driver); 427d10e4ef2Snarayan if (rv != DDI_SUCCESS) { 428d10e4ef2Snarayan RW_EXIT(&vnetp->trwlock); 429d10e4ef2Snarayan goto vnet_detach_fail; 430d10e4ef2Snarayan } 431d10e4ef2Snarayan } 432d10e4ef2Snarayan vnet_del_vptl(vnetp, vp_tlp); 433d10e4ef2Snarayan } 434d10e4ef2Snarayan RW_EXIT(&vnetp->trwlock); 435d10e4ef2Snarayan 4361ae08745Sheppo /* 4371ae08745Sheppo * Unregister from the MAC subsystem. This can fail, in 4381ae08745Sheppo * particular if there are DLPI style-2 streams still open - 4391ae08745Sheppo * in which case we just return failure. 4401ae08745Sheppo */ 441ba2e4443Sseb if (mac_unregister(vnetp->mh) != 0) 4421ae08745Sheppo goto vnet_detach_fail; 4431ae08745Sheppo 4441ae08745Sheppo /* unlink from instance(vnet_t) list */ 4451ae08745Sheppo WRITE_ENTER(&vnet_rw); 4461ae08745Sheppo for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) { 4471ae08745Sheppo if (*vnetpp == vnetp) { 4481ae08745Sheppo *vnetpp = vnetp->nextp; 4491ae08745Sheppo break; 4501ae08745Sheppo } 4511ae08745Sheppo } 4521ae08745Sheppo RW_EXIT(&vnet_rw); 4531ae08745Sheppo 454*c1c61f44Ssb155480 /* destroy fdb */ 455*c1c61f44Ssb155480 vnet_fdb_destroy(vnetp); 456445b4c2eSsb155480 45793b13a42Swentaoy rw_destroy(&vnetp->trwlock); 4581ae08745Sheppo KMEM_FREE(vnetp); 4591ae08745Sheppo 4601ae08745Sheppo return (DDI_SUCCESS); 4611ae08745Sheppo 4621ae08745Sheppo vnet_detach_fail: 4631ae08745Sheppo return (DDI_FAILURE); 4641ae08745Sheppo } 4651ae08745Sheppo 4661ae08745Sheppo /* enable the device for transmit/receive */ 4671ae08745Sheppo static int 4681ae08745Sheppo vnet_m_start(void *arg) 4691ae08745Sheppo { 4701ae08745Sheppo vnet_t *vnetp = arg; 4711ae08745Sheppo vp_tl_t *vp_tlp; 472ba2e4443Sseb mac_register_t *vp_macp; 473ba2e4443Sseb mac_callbacks_t *cbp; 4741ae08745Sheppo 475844e62a3Sraghuram DBG1(vnetp, "enter\n"); 4761ae08745Sheppo 4771ae08745Sheppo /* 4783af08d82Slm66018 * NOTE: 4791ae08745Sheppo * Currently, we only have generic transport. m_start() invokes 4801ae08745Sheppo * vgen_start() which enables ports/channels in vgen and 4811ae08745Sheppo * initiates handshake with peer vnets and vsw. In the future when we 4821ae08745Sheppo * have support for hardware specific transports, this information 4831ae08745Sheppo * needs to be propagted back to vnet from vgen and we need to revisit 4841ae08745Sheppo * this code (see comments in vnet_attach()). 4851ae08745Sheppo * 4861ae08745Sheppo */ 4871ae08745Sheppo WRITE_ENTER(&vnetp->trwlock); 4881ae08745Sheppo for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 4891ae08745Sheppo vp_macp = vp_tlp->macp; 490ba2e4443Sseb cbp = vp_macp->m_callbacks; 491ba2e4443Sseb cbp->mc_start(vp_macp->m_driver); 4921ae08745Sheppo } 4931ae08745Sheppo RW_EXIT(&vnetp->trwlock); 4941ae08745Sheppo 495844e62a3Sraghuram DBG1(vnetp, "exit\n"); 4961ae08745Sheppo return (VNET_SUCCESS); 4971ae08745Sheppo 4981ae08745Sheppo } 4991ae08745Sheppo 5001ae08745Sheppo /* stop transmit/receive for the device */ 5011ae08745Sheppo static void 5021ae08745Sheppo vnet_m_stop(void *arg) 5031ae08745Sheppo { 5041ae08745Sheppo vnet_t *vnetp = arg; 5051ae08745Sheppo vp_tl_t *vp_tlp; 506ba2e4443Sseb mac_register_t *vp_macp; 507ba2e4443Sseb mac_callbacks_t *cbp; 5081ae08745Sheppo 509844e62a3Sraghuram DBG1(vnetp, "enter\n"); 5101ae08745Sheppo 5111ae08745Sheppo WRITE_ENTER(&vnetp->trwlock); 5121ae08745Sheppo for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 5131ae08745Sheppo vp_macp = vp_tlp->macp; 514ba2e4443Sseb cbp = vp_macp->m_callbacks; 515ba2e4443Sseb cbp->mc_stop(vp_macp->m_driver); 5161ae08745Sheppo } 5171ae08745Sheppo RW_EXIT(&vnetp->trwlock); 5181ae08745Sheppo 519844e62a3Sraghuram DBG1(vnetp, "exit\n"); 5201ae08745Sheppo } 5211ae08745Sheppo 5221ae08745Sheppo /* set the unicast mac address of the device */ 5231ae08745Sheppo static int 5241ae08745Sheppo vnet_m_unicst(void *arg, const uint8_t *macaddr) 5251ae08745Sheppo { 5261ae08745Sheppo _NOTE(ARGUNUSED(macaddr)) 5271ae08745Sheppo 5281ae08745Sheppo vnet_t *vnetp = arg; 5291ae08745Sheppo 530844e62a3Sraghuram DBG1(vnetp, "enter\n"); 5311ae08745Sheppo /* 5323af08d82Slm66018 * NOTE: setting mac address dynamically is not supported. 5331ae08745Sheppo */ 534844e62a3Sraghuram DBG1(vnetp, "exit\n"); 5351ae08745Sheppo 5368e6a2a04Slm66018 return (VNET_FAILURE); 5371ae08745Sheppo } 5381ae08745Sheppo 5391ae08745Sheppo /* enable/disable a multicast address */ 5401ae08745Sheppo static int 5411ae08745Sheppo vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 5421ae08745Sheppo { 5431ae08745Sheppo _NOTE(ARGUNUSED(add, mca)) 5441ae08745Sheppo 5451ae08745Sheppo vnet_t *vnetp = arg; 5461ae08745Sheppo vp_tl_t *vp_tlp; 547ba2e4443Sseb mac_register_t *vp_macp; 548ba2e4443Sseb mac_callbacks_t *cbp; 5491ae08745Sheppo int rv = VNET_SUCCESS; 5501ae08745Sheppo 551844e62a3Sraghuram DBG1(vnetp, "enter\n"); 5521ae08745Sheppo READ_ENTER(&vnetp->trwlock); 5531ae08745Sheppo for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 5541ae08745Sheppo if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) { 5551ae08745Sheppo vp_macp = vp_tlp->macp; 556ba2e4443Sseb cbp = vp_macp->m_callbacks; 557ba2e4443Sseb rv = cbp->mc_multicst(vp_macp->m_driver, add, mca); 5581ae08745Sheppo break; 5591ae08745Sheppo } 5601ae08745Sheppo } 5611ae08745Sheppo RW_EXIT(&vnetp->trwlock); 562844e62a3Sraghuram DBG1(vnetp, "exit(%d)\n", rv); 5631ae08745Sheppo return (rv); 5641ae08745Sheppo } 5651ae08745Sheppo 5661ae08745Sheppo /* set or clear promiscuous mode on the device */ 5671ae08745Sheppo static int 5681ae08745Sheppo vnet_m_promisc(void *arg, boolean_t on) 5691ae08745Sheppo { 5701ae08745Sheppo _NOTE(ARGUNUSED(on)) 5711ae08745Sheppo 5721ae08745Sheppo vnet_t *vnetp = arg; 573844e62a3Sraghuram DBG1(vnetp, "enter\n"); 5741ae08745Sheppo /* 5753af08d82Slm66018 * NOTE: setting promiscuous mode is not supported, just return success. 5761ae08745Sheppo */ 577844e62a3Sraghuram DBG1(vnetp, "exit\n"); 5781ae08745Sheppo return (VNET_SUCCESS); 5791ae08745Sheppo } 5801ae08745Sheppo 5811ae08745Sheppo /* 5821ae08745Sheppo * Transmit a chain of packets. This function provides switching functionality 5831ae08745Sheppo * based on the destination mac address to reach other guests (within ldoms) or 5841ae08745Sheppo * external hosts. 5851ae08745Sheppo */ 5861ae08745Sheppo mblk_t * 5871ae08745Sheppo vnet_m_tx(void *arg, mblk_t *mp) 5881ae08745Sheppo { 5891ae08745Sheppo vnet_t *vnetp; 590*c1c61f44Ssb155480 vnet_fdbe_t *fp; 5911ae08745Sheppo mblk_t *next; 5921ae08745Sheppo mblk_t *resid_mp; 593*c1c61f44Ssb155480 struct ether_header *ehp; 5941ae08745Sheppo 5951ae08745Sheppo vnetp = (vnet_t *)arg; 596844e62a3Sraghuram DBG1(vnetp, "enter\n"); 5971ae08745Sheppo ASSERT(mp != NULL); 5981ae08745Sheppo 5991ae08745Sheppo while (mp != NULL) { 600*c1c61f44Ssb155480 6011ae08745Sheppo next = mp->b_next; 6021ae08745Sheppo mp->b_next = NULL; 6031ae08745Sheppo 6041ae08745Sheppo /* 605*c1c61f44Ssb155480 * Find fdb entry for the destination 606*c1c61f44Ssb155480 * and hold a reference to it. 6071ae08745Sheppo */ 608*c1c61f44Ssb155480 ehp = (struct ether_header *)mp->b_rptr; 609*c1c61f44Ssb155480 fp = vnet_fdbe_find(vnetp, &ehp->ether_dhost); 610*c1c61f44Ssb155480 if (fp != NULL) { 611*c1c61f44Ssb155480 612*c1c61f44Ssb155480 /* 613*c1c61f44Ssb155480 * Destination found in FDB. 614*c1c61f44Ssb155480 * The destination is a vnet device within ldoms 615*c1c61f44Ssb155480 * and directly reachable, invoke the tx function 616*c1c61f44Ssb155480 * in the fdb entry. 617*c1c61f44Ssb155480 */ 618*c1c61f44Ssb155480 resid_mp = fp->m_tx(fp->txarg, mp); 619*c1c61f44Ssb155480 620*c1c61f44Ssb155480 /* tx done; now release ref on fdb entry */ 621*c1c61f44Ssb155480 VNET_FDBE_REFRELE(fp); 622*c1c61f44Ssb155480 6231ae08745Sheppo if (resid_mp != NULL) { 6241ae08745Sheppo /* m_tx failed */ 6251ae08745Sheppo mp->b_next = next; 6261ae08745Sheppo break; 6271ae08745Sheppo } 6281ae08745Sheppo } else { 6291ae08745Sheppo /* 630*c1c61f44Ssb155480 * Destination is not in FDB. 6311ae08745Sheppo * If the destination is broadcast/multicast 6321ae08745Sheppo * or an unknown unicast address, forward the 633*c1c61f44Ssb155480 * packet to vsw, using the cached fdb entry 634*c1c61f44Ssb155480 * to vsw. 6351ae08745Sheppo */ 636*c1c61f44Ssb155480 READ_ENTER(&vnetp->vsw_fp_rw); 637*c1c61f44Ssb155480 638*c1c61f44Ssb155480 fp = vnetp->vsw_fp; 639*c1c61f44Ssb155480 if (fp == NULL) { 640*c1c61f44Ssb155480 /* 641*c1c61f44Ssb155480 * no fdb entry to vsw? drop the packet. 642*c1c61f44Ssb155480 */ 643*c1c61f44Ssb155480 RW_EXIT(&vnetp->vsw_fp_rw); 644*c1c61f44Ssb155480 freemsg(mp); 645*c1c61f44Ssb155480 mp = next; 646*c1c61f44Ssb155480 continue; 647*c1c61f44Ssb155480 } 648*c1c61f44Ssb155480 649*c1c61f44Ssb155480 /* ref hold the fdb entry to vsw */ 650*c1c61f44Ssb155480 VNET_FDBE_REFHOLD(fp); 651*c1c61f44Ssb155480 652*c1c61f44Ssb155480 RW_EXIT(&vnetp->vsw_fp_rw); 653*c1c61f44Ssb155480 654*c1c61f44Ssb155480 resid_mp = fp->m_tx(fp->txarg, mp); 655*c1c61f44Ssb155480 656*c1c61f44Ssb155480 /* tx done; now release ref on fdb entry */ 657*c1c61f44Ssb155480 VNET_FDBE_REFRELE(fp); 658*c1c61f44Ssb155480 6591ae08745Sheppo if (resid_mp != NULL) { 6601ae08745Sheppo /* m_tx failed */ 6611ae08745Sheppo mp->b_next = next; 6621ae08745Sheppo break; 6631ae08745Sheppo } 6641ae08745Sheppo } 6651ae08745Sheppo 6661ae08745Sheppo mp = next; 6671ae08745Sheppo } 6681ae08745Sheppo 669844e62a3Sraghuram DBG1(vnetp, "exit\n"); 6701ae08745Sheppo return (mp); 6711ae08745Sheppo } 6721ae08745Sheppo 6731ae08745Sheppo /* get statistics from the device */ 674ba2e4443Sseb int 675ba2e4443Sseb vnet_m_stat(void *arg, uint_t stat, uint64_t *val) 6761ae08745Sheppo { 6771ae08745Sheppo vnet_t *vnetp = arg; 6781ae08745Sheppo vp_tl_t *vp_tlp; 679ba2e4443Sseb mac_register_t *vp_macp; 680ba2e4443Sseb mac_callbacks_t *cbp; 681ba2e4443Sseb uint64_t val_total = 0; 6821ae08745Sheppo 683844e62a3Sraghuram DBG1(vnetp, "enter\n"); 6841ae08745Sheppo 6851ae08745Sheppo /* 686ba2e4443Sseb * get the specified statistic from each transport and return the 687ba2e4443Sseb * aggregate val. This obviously only works for counters. 6881ae08745Sheppo */ 689ba2e4443Sseb if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) || 690ba2e4443Sseb (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) { 691ba2e4443Sseb return (ENOTSUP); 692ba2e4443Sseb } 6931ae08745Sheppo READ_ENTER(&vnetp->trwlock); 6941ae08745Sheppo for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) { 6951ae08745Sheppo vp_macp = vp_tlp->macp; 696ba2e4443Sseb cbp = vp_macp->m_callbacks; 697ba2e4443Sseb if (cbp->mc_getstat(vp_macp->m_driver, stat, val) == 0) 698ba2e4443Sseb val_total += *val; 6991ae08745Sheppo } 7001ae08745Sheppo RW_EXIT(&vnetp->trwlock); 7011ae08745Sheppo 702ba2e4443Sseb *val = val_total; 703ba2e4443Sseb 704844e62a3Sraghuram DBG1(vnetp, "exit\n"); 705ba2e4443Sseb return (0); 7061ae08745Sheppo } 7071ae08745Sheppo 7081ae08745Sheppo /* wrapper function for mac_register() */ 7091ae08745Sheppo static int 7101ae08745Sheppo vnet_mac_register(vnet_t *vnetp) 7111ae08745Sheppo { 712ba2e4443Sseb mac_register_t *macp; 713ba2e4443Sseb int err; 7141ae08745Sheppo 715ba2e4443Sseb if ((macp = mac_alloc(MAC_VERSION)) == NULL) 716ba2e4443Sseb return (DDI_FAILURE); 717ba2e4443Sseb macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 718ba2e4443Sseb macp->m_driver = vnetp; 7191ae08745Sheppo macp->m_dip = vnetp->dip; 720ba2e4443Sseb macp->m_src_addr = vnetp->curr_macaddr; 721ba2e4443Sseb macp->m_callbacks = &vnet_m_callbacks; 722ba2e4443Sseb macp->m_min_sdu = 0; 723*c1c61f44Ssb155480 macp->m_max_sdu = vnet_ethermtu; 724*c1c61f44Ssb155480 macp->m_margin = VLAN_TAGSZ; 7251ae08745Sheppo 7261ae08745Sheppo /* 7271ae08745Sheppo * Finally, we're ready to register ourselves with the MAC layer 7281ae08745Sheppo * interface; if this succeeds, we're all ready to start() 7291ae08745Sheppo */ 730ba2e4443Sseb err = mac_register(macp, &vnetp->mh); 731ba2e4443Sseb mac_free(macp); 732ba2e4443Sseb return (err == 0 ? DDI_SUCCESS : DDI_FAILURE); 7331ae08745Sheppo } 7341ae08745Sheppo 7351ae08745Sheppo /* add vp_tl to the list */ 7361ae08745Sheppo static void 7371ae08745Sheppo vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp) 7381ae08745Sheppo { 7391ae08745Sheppo vp_tl_t *ttlp; 7401ae08745Sheppo 7411ae08745Sheppo WRITE_ENTER(&vnetp->trwlock); 7421ae08745Sheppo if (vnetp->tlp == NULL) { 7431ae08745Sheppo vnetp->tlp = vp_tlp; 7441ae08745Sheppo } else { 7451ae08745Sheppo ttlp = vnetp->tlp; 7461ae08745Sheppo while (ttlp->nextp) 7471ae08745Sheppo ttlp = ttlp->nextp; 7481ae08745Sheppo ttlp->nextp = vp_tlp; 7491ae08745Sheppo } 7501ae08745Sheppo RW_EXIT(&vnetp->trwlock); 7511ae08745Sheppo } 7521ae08745Sheppo 7531ae08745Sheppo /* remove vp_tl from the list */ 7541ae08745Sheppo static void 7551ae08745Sheppo vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp) 7561ae08745Sheppo { 7571ae08745Sheppo vp_tl_t *ttlp, **pretlp; 7581ae08745Sheppo boolean_t found = B_FALSE; 7591ae08745Sheppo 7601ae08745Sheppo pretlp = &vnetp->tlp; 7611ae08745Sheppo ttlp = *pretlp; 7621ae08745Sheppo while (ttlp) { 7631ae08745Sheppo if (ttlp == vp_tlp) { 7641ae08745Sheppo found = B_TRUE; 7651ae08745Sheppo (*pretlp) = ttlp->nextp; 7661ae08745Sheppo ttlp->nextp = NULL; 7671ae08745Sheppo break; 7681ae08745Sheppo } 7691ae08745Sheppo pretlp = &(ttlp->nextp); 7701ae08745Sheppo ttlp = *pretlp; 7711ae08745Sheppo } 7721ae08745Sheppo 7731ae08745Sheppo if (found) { 7741ae08745Sheppo KMEM_FREE(vp_tlp); 7751ae08745Sheppo } 7761ae08745Sheppo } 7771ae08745Sheppo 7781ae08745Sheppo /* get vp_tl corresponding to the given name */ 7791ae08745Sheppo static vp_tl_t * 7801ae08745Sheppo vnet_get_vptl(vnet_t *vnetp, const char *name) 7811ae08745Sheppo { 7821ae08745Sheppo vp_tl_t *tlp; 7831ae08745Sheppo 7841ae08745Sheppo tlp = vnetp->tlp; 7851ae08745Sheppo while (tlp) { 7861ae08745Sheppo if (strcmp(tlp->name, name) == 0) { 7871ae08745Sheppo return (tlp); 7881ae08745Sheppo } 7891ae08745Sheppo tlp = tlp->nextp; 7901ae08745Sheppo } 791844e62a3Sraghuram DWARN(vnetp, "can't find vp_tl with name (%s)\n", name); 7921ae08745Sheppo return (NULL); 7931ae08745Sheppo } 7941ae08745Sheppo 7951ae08745Sheppo /* read the mac address of the device */ 7961ae08745Sheppo static int 7971ae08745Sheppo vnet_read_mac_address(vnet_t *vnetp) 7981ae08745Sheppo { 7991ae08745Sheppo uchar_t *macaddr; 8001ae08745Sheppo uint32_t size; 8011ae08745Sheppo int rv; 8021ae08745Sheppo 8031ae08745Sheppo rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip, 8041ae08745Sheppo DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size); 8051ae08745Sheppo if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) { 806844e62a3Sraghuram DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n", 807844e62a3Sraghuram macaddr_propname, rv); 8081ae08745Sheppo return (DDI_FAILURE); 8091ae08745Sheppo } 8101ae08745Sheppo bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL); 8111ae08745Sheppo bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL); 8121ae08745Sheppo ddi_prop_free(macaddr); 8131ae08745Sheppo 8141ae08745Sheppo return (DDI_SUCCESS); 8151ae08745Sheppo } 8161ae08745Sheppo 81793b13a42Swentaoy static void 818*c1c61f44Ssb155480 vnet_fdb_create(vnet_t *vnetp) 81993b13a42Swentaoy { 820*c1c61f44Ssb155480 char hashname[MAXNAMELEN]; 82193b13a42Swentaoy 822*c1c61f44Ssb155480 (void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash", 823*c1c61f44Ssb155480 vnetp->instance); 824*c1c61f44Ssb155480 vnetp->fdb_nchains = vnet_fdb_nchains; 825*c1c61f44Ssb155480 vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains, 826*c1c61f44Ssb155480 mod_hash_null_valdtor, sizeof (void *)); 82793b13a42Swentaoy } 82893b13a42Swentaoy 82993b13a42Swentaoy static void 830*c1c61f44Ssb155480 vnet_fdb_destroy(vnet_t *vnetp) 83193b13a42Swentaoy { 832*c1c61f44Ssb155480 /* destroy fdb-hash-table */ 833*c1c61f44Ssb155480 if (vnetp->fdb_hashp != NULL) { 834*c1c61f44Ssb155480 mod_hash_destroy_hash(vnetp->fdb_hashp); 835*c1c61f44Ssb155480 vnetp->fdb_hashp = NULL; 836*c1c61f44Ssb155480 vnetp->fdb_nchains = 0; 837*c1c61f44Ssb155480 } 83893b13a42Swentaoy } 83993b13a42Swentaoy 84093b13a42Swentaoy /* 841*c1c61f44Ssb155480 * Add an entry into the fdb. 84293b13a42Swentaoy */ 8431ae08745Sheppo void 844*c1c61f44Ssb155480 vnet_fdbe_add(vnet_t *vnetp, struct ether_addr *macaddr, uint8_t type, 845*c1c61f44Ssb155480 mac_tx_t m_tx, void *port) 8461ae08745Sheppo { 847*c1c61f44Ssb155480 uint64_t addr = 0; 848*c1c61f44Ssb155480 vnet_fdbe_t *fp; 849*c1c61f44Ssb155480 int rv; 850*c1c61f44Ssb155480 851*c1c61f44Ssb155480 KEY_HASH(addr, macaddr); 852*c1c61f44Ssb155480 853*c1c61f44Ssb155480 fp = kmem_zalloc(sizeof (vnet_fdbe_t), KM_SLEEP); 854*c1c61f44Ssb155480 fp->txarg = port; 855*c1c61f44Ssb155480 fp->type = type; 856*c1c61f44Ssb155480 fp->m_tx = m_tx; 8571ae08745Sheppo 8581ae08745Sheppo /* 859*c1c61f44Ssb155480 * If the entry being added corresponds to vsw-port, we cache that 860*c1c61f44Ssb155480 * entry and keep a permanent reference to it. This is done to avoid 861*c1c61f44Ssb155480 * searching this entry when we need to transmit a frame with an 862*c1c61f44Ssb155480 * unknown unicast destination, in vnet_m_tx(). 8631ae08745Sheppo */ 864*c1c61f44Ssb155480 (fp->type == VNET_VSWPORT) ? (fp->refcnt = 1) : (fp->refcnt = 0); 8651ae08745Sheppo 866*c1c61f44Ssb155480 /* 867*c1c61f44Ssb155480 * Note: duplicate keys will be rejected by mod_hash. 868*c1c61f44Ssb155480 */ 869*c1c61f44Ssb155480 rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr, 870*c1c61f44Ssb155480 (mod_hash_val_t)fp); 871*c1c61f44Ssb155480 if (rv != 0) { 872*c1c61f44Ssb155480 DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr); 873*c1c61f44Ssb155480 KMEM_FREE(fp); 8741ae08745Sheppo return; 8751ae08745Sheppo } 8761ae08745Sheppo 877*c1c61f44Ssb155480 if (type == VNET_VSWPORT) { 878*c1c61f44Ssb155480 /* Cache the fdb entry to vsw-port */ 879*c1c61f44Ssb155480 WRITE_ENTER(&vnetp->vsw_fp_rw); 880*c1c61f44Ssb155480 if (vnetp->vsw_fp == NULL) 881*c1c61f44Ssb155480 vnetp->vsw_fp = fp; 882*c1c61f44Ssb155480 RW_EXIT(&vnetp->vsw_fp_rw); 883*c1c61f44Ssb155480 } 8841ae08745Sheppo } 8851ae08745Sheppo 886*c1c61f44Ssb155480 /* 887*c1c61f44Ssb155480 * Remove an entry from fdb. 888*c1c61f44Ssb155480 */ 8891ae08745Sheppo void 890*c1c61f44Ssb155480 vnet_fdbe_del(vnet_t *vnetp, struct ether_addr *eaddr) 8911ae08745Sheppo { 892*c1c61f44Ssb155480 uint64_t addr = 0; 893*c1c61f44Ssb155480 vnet_fdbe_t *fp; 894*c1c61f44Ssb155480 int rv; 895*c1c61f44Ssb155480 uint32_t refcnt; 896*c1c61f44Ssb155480 897*c1c61f44Ssb155480 KEY_HASH(addr, eaddr); 8981ae08745Sheppo 8991ae08745Sheppo /* 900*c1c61f44Ssb155480 * Remove the entry from fdb hash table. 901*c1c61f44Ssb155480 * This prevents further references to this fdb entry. 9021ae08745Sheppo */ 903*c1c61f44Ssb155480 rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr, 904*c1c61f44Ssb155480 (mod_hash_val_t *)&fp); 905*c1c61f44Ssb155480 ASSERT(rv == 0); 9061ae08745Sheppo 907*c1c61f44Ssb155480 if (fp->type == VNET_VSWPORT) { 908*c1c61f44Ssb155480 WRITE_ENTER(&vnetp->vsw_fp_rw); 9091ae08745Sheppo 910*c1c61f44Ssb155480 ASSERT(fp == vnetp->vsw_fp); 911*c1c61f44Ssb155480 vnetp->vsw_fp = NULL; 912*c1c61f44Ssb155480 913*c1c61f44Ssb155480 RW_EXIT(&vnetp->vsw_fp_rw); 9141ae08745Sheppo } 9151ae08745Sheppo 916*c1c61f44Ssb155480 /* 917*c1c61f44Ssb155480 * If there are threads already ref holding before the entry was 918*c1c61f44Ssb155480 * removed from hash table, then wait for ref count to drop to zero. 919*c1c61f44Ssb155480 */ 920*c1c61f44Ssb155480 (fp->type == VNET_VSWPORT) ? (refcnt = 1) : (refcnt = 0); 921*c1c61f44Ssb155480 while (fp->refcnt > refcnt) { 922*c1c61f44Ssb155480 delay(drv_usectohz(vnet_fdbe_refcnt_delay)); 923*c1c61f44Ssb155480 } 924*c1c61f44Ssb155480 925*c1c61f44Ssb155480 kmem_free(fp, sizeof (*fp)); 926*c1c61f44Ssb155480 } 927*c1c61f44Ssb155480 928*c1c61f44Ssb155480 /* 929*c1c61f44Ssb155480 * Modify the fdb entry for the given macaddr, 930*c1c61f44Ssb155480 * to use the specified port for transmits. 931*c1c61f44Ssb155480 */ 932*c1c61f44Ssb155480 void 933*c1c61f44Ssb155480 vnet_fdbe_modify(vnet_t *vnetp, struct ether_addr *macaddr, void *portp, 934*c1c61f44Ssb155480 boolean_t flag) 935*c1c61f44Ssb155480 { 936*c1c61f44Ssb155480 vnet_fdbe_t *fp; 937*c1c61f44Ssb155480 uint64_t addr = 0; 938*c1c61f44Ssb155480 int rv; 939*c1c61f44Ssb155480 uint32_t refcnt; 940*c1c61f44Ssb155480 941*c1c61f44Ssb155480 KEY_HASH(addr, macaddr); 942*c1c61f44Ssb155480 943*c1c61f44Ssb155480 /* 944*c1c61f44Ssb155480 * Remove the entry from fdb hash table. 945*c1c61f44Ssb155480 * This prevents further references to this fdb entry. 946*c1c61f44Ssb155480 */ 947*c1c61f44Ssb155480 rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr, 948*c1c61f44Ssb155480 (mod_hash_val_t *)&fp); 949*c1c61f44Ssb155480 ASSERT(rv == 0); 950*c1c61f44Ssb155480 951*c1c61f44Ssb155480 /* fdb entry of vsw port must never be modified */ 952*c1c61f44Ssb155480 ASSERT(fp->type == VNET_VNETPORT); 953*c1c61f44Ssb155480 954*c1c61f44Ssb155480 /* 955*c1c61f44Ssb155480 * If there are threads already ref holding before the entry was 956*c1c61f44Ssb155480 * removed from hash table, then wait for reference count to drop to 957*c1c61f44Ssb155480 * zero. Note: flag indicates the context of caller. If we are in the 958*c1c61f44Ssb155480 * context of transmit routine, there is a reference held by the caller 959*c1c61f44Ssb155480 * too, in which case, wait for the refcnt to drop to 1. 960*c1c61f44Ssb155480 */ 961*c1c61f44Ssb155480 (flag == B_TRUE) ? (refcnt = 1) : (refcnt = 0); 962*c1c61f44Ssb155480 while (fp->refcnt > refcnt) { 963*c1c61f44Ssb155480 delay(drv_usectohz(vnet_fdbe_refcnt_delay)); 964*c1c61f44Ssb155480 } 965*c1c61f44Ssb155480 966*c1c61f44Ssb155480 /* update the portp in fdb entry with the new value */ 967*c1c61f44Ssb155480 fp->txarg = portp; 968*c1c61f44Ssb155480 969*c1c61f44Ssb155480 /* Reinsert the updated fdb entry into the table */ 970*c1c61f44Ssb155480 rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr, 971*c1c61f44Ssb155480 (mod_hash_val_t)fp); 972*c1c61f44Ssb155480 ASSERT(rv == 0); 973*c1c61f44Ssb155480 } 974*c1c61f44Ssb155480 975*c1c61f44Ssb155480 /* 976*c1c61f44Ssb155480 * Search fdb for a given mac address. If an entry is found, hold 977*c1c61f44Ssb155480 * a reference to it and return the entry; else returns NULL. 978*c1c61f44Ssb155480 */ 979*c1c61f44Ssb155480 static vnet_fdbe_t * 980*c1c61f44Ssb155480 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp) 981*c1c61f44Ssb155480 { 982*c1c61f44Ssb155480 uint64_t key = 0; 983*c1c61f44Ssb155480 vnet_fdbe_t *fp; 984*c1c61f44Ssb155480 int rv; 985*c1c61f44Ssb155480 986*c1c61f44Ssb155480 KEY_HASH(key, addrp); 987*c1c61f44Ssb155480 988*c1c61f44Ssb155480 rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key, 989*c1c61f44Ssb155480 (mod_hash_val_t *)&fp, vnet_fdbe_find_cb); 990*c1c61f44Ssb155480 991*c1c61f44Ssb155480 if (rv != 0) 992*c1c61f44Ssb155480 return (NULL); 993*c1c61f44Ssb155480 994*c1c61f44Ssb155480 return (fp); 995*c1c61f44Ssb155480 } 996*c1c61f44Ssb155480 997*c1c61f44Ssb155480 /* 998*c1c61f44Ssb155480 * Callback function provided to mod_hash_find_cb(). After finding the fdb 999*c1c61f44Ssb155480 * entry corresponding to the key (macaddr), this callback will be invoked by 1000*c1c61f44Ssb155480 * mod_hash_find_cb() to atomically increment the reference count on the fdb 1001*c1c61f44Ssb155480 * entry before returning the found entry. 1002*c1c61f44Ssb155480 */ 1003*c1c61f44Ssb155480 static void 1004*c1c61f44Ssb155480 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val) 1005*c1c61f44Ssb155480 { 1006*c1c61f44Ssb155480 _NOTE(ARGUNUSED(key)) 1007*c1c61f44Ssb155480 VNET_FDBE_REFHOLD((vnet_fdbe_t *)val); 10081ae08745Sheppo } 1009ba2e4443Sseb 1010ba2e4443Sseb void 1011ba2e4443Sseb vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp) 1012ba2e4443Sseb { 1013ba2e4443Sseb vnet_t *vnetp = arg; 1014ba2e4443Sseb mac_rx(vnetp->mh, mrh, mp); 1015ba2e4443Sseb } 1016ba2e4443Sseb 1017ba2e4443Sseb void 1018ba2e4443Sseb vnet_tx_update(void *arg) 1019ba2e4443Sseb { 1020ba2e4443Sseb vnet_t *vnetp = arg; 1021ba2e4443Sseb mac_tx_update(vnetp->mh); 1022ba2e4443Sseb } 1023