xref: /titanic_53/usr/src/uts/sun4v/io/vnet.c (revision c1c61f44e88f4c8c155272ee56d868043146096a)
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