xref: /onnv-gate/usr/src/uts/sun4v/io/vnet.c (revision 8160:5191ca9c4c04)
11991Sheppo /*
21991Sheppo  * CDDL HEADER START
31991Sheppo  *
41991Sheppo  * The contents of this file are subject to the terms of the
51991Sheppo  * Common Development and Distribution License (the "License").
61991Sheppo  * You may not use this file except in compliance with the License.
71991Sheppo  *
81991Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo  * or http://www.opensolaris.org/os/licensing.
101991Sheppo  * See the License for the specific language governing permissions
111991Sheppo  * and limitations under the License.
121991Sheppo  *
131991Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo  *
191991Sheppo  * CDDL HEADER END
201991Sheppo  */
211991Sheppo 
221991Sheppo /*
236419Ssb155480  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241991Sheppo  * Use is subject to license terms.
251991Sheppo  */
261991Sheppo 
271991Sheppo #include <sys/types.h>
281991Sheppo #include <sys/errno.h>
291991Sheppo #include <sys/param.h>
301991Sheppo #include <sys/stream.h>
311991Sheppo #include <sys/kmem.h>
321991Sheppo #include <sys/conf.h>
331991Sheppo #include <sys/devops.h>
341991Sheppo #include <sys/ksynch.h>
351991Sheppo #include <sys/stat.h>
361991Sheppo #include <sys/modctl.h>
376419Ssb155480 #include <sys/modhash.h>
381991Sheppo #include <sys/debug.h>
391991Sheppo #include <sys/ethernet.h>
401991Sheppo #include <sys/dlpi.h>
411991Sheppo #include <net/if.h>
421991Sheppo #include <sys/mac.h>
432311Sseb #include <sys/mac_ether.h>
441991Sheppo #include <sys/ddi.h>
451991Sheppo #include <sys/sunddi.h>
461991Sheppo #include <sys/strsun.h>
471991Sheppo #include <sys/note.h>
486419Ssb155480 #include <sys/atomic.h>
491991Sheppo #include <sys/vnet.h>
506419Ssb155480 #include <sys/vlan.h>
516495Sspeer #include <sys/vnet_mailbox.h>
526495Sspeer #include <sys/vnet_common.h>
536495Sspeer #include <sys/dds.h>
546495Sspeer #include <sys/strsubr.h>
556495Sspeer #include <sys/taskq.h>
561991Sheppo 
571991Sheppo /*
581991Sheppo  * Function prototypes.
591991Sheppo  */
601991Sheppo 
611991Sheppo /* DDI entrypoints */
621991Sheppo static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
631991Sheppo static int vnetattach(dev_info_t *, ddi_attach_cmd_t);
641991Sheppo static int vnetdetach(dev_info_t *, ddi_detach_cmd_t);
651991Sheppo 
661991Sheppo /* MAC entrypoints  */
672311Sseb static int vnet_m_stat(void *, uint_t, uint64_t *);
681991Sheppo static int vnet_m_start(void *);
691991Sheppo static void vnet_m_stop(void *);
701991Sheppo static int vnet_m_promisc(void *, boolean_t);
711991Sheppo static int vnet_m_multicst(void *, boolean_t, const uint8_t *);
721991Sheppo static int vnet_m_unicst(void *, const uint8_t *);
731991Sheppo mblk_t *vnet_m_tx(void *, mblk_t *);
741991Sheppo 
751991Sheppo /* vnet internal functions */
761991Sheppo static int vnet_mac_register(vnet_t *);
771991Sheppo static int vnet_read_mac_address(vnet_t *vnetp);
781991Sheppo 
796419Ssb155480 /* Forwarding database (FDB) routines */
806419Ssb155480 static void vnet_fdb_create(vnet_t *vnetp);
816419Ssb155480 static void vnet_fdb_destroy(vnet_t *vnetp);
826495Sspeer static vnet_res_t *vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp);
836419Ssb155480 static void vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val);
846495Sspeer void vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp);
856495Sspeer static void vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp);
866495Sspeer 
877896SSriharsha.Basavapatna@Sun.COM static void vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp);
886495Sspeer static void vnet_rx(vio_net_handle_t vrh, mblk_t *mp);
896495Sspeer static void vnet_tx_update(vio_net_handle_t vrh);
906495Sspeer static void vnet_res_start_task(void *arg);
916495Sspeer static void vnet_start_resources(vnet_t *vnetp);
926495Sspeer static void vnet_stop_resources(vnet_t *vnetp);
936495Sspeer static void vnet_dispatch_res_task(vnet_t *vnetp);
946495Sspeer static void vnet_res_start_task(void *arg);
956495Sspeer static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err);
967529SSriharsha.Basavapatna@Sun.COM int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu);
976419Ssb155480 
98*8160SWentao.Yang@Sun.COM static kstat_t *vnet_hio_setup_kstats(char *ks_mod, char *ks_name,
99*8160SWentao.Yang@Sun.COM     vnet_res_t *vresp);
100*8160SWentao.Yang@Sun.COM static int vnet_hio_update_kstats(kstat_t *ksp, int rw);
101*8160SWentao.Yang@Sun.COM static void vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp);
102*8160SWentao.Yang@Sun.COM static void vnet_hio_destroy_kstats(kstat_t *ksp);
103*8160SWentao.Yang@Sun.COM 
1046495Sspeer /* Exported to to vnet_dds */
1056495Sspeer int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg);
1061991Sheppo 
1076495Sspeer /* Externs that are imported from vnet_gen */
1086495Sspeer extern int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
1096495Sspeer     const uint8_t *macaddr, void **vgenhdl);
1102336Snarayan extern int vgen_uninit(void *arg);
1116495Sspeer extern int vgen_dds_tx(void *arg, void *dmsg);
1126495Sspeer 
1136495Sspeer /* Externs that are imported from vnet_dds */
1146495Sspeer extern void vdds_mod_init(void);
1156495Sspeer extern void vdds_mod_fini(void);
1166495Sspeer extern int vdds_init(vnet_t *vnetp);
1176495Sspeer extern void vdds_cleanup(vnet_t *vnetp);
1186495Sspeer extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg);
1197819SRaghuram.Kothakota@Sun.COM extern void vdds_cleanup_hybrid_res(void *arg);
1201991Sheppo 
121*8160SWentao.Yang@Sun.COM #define	DRV_NAME	"vnet"
1226419Ssb155480 #define	VNET_FDBE_REFHOLD(p)						\
1236419Ssb155480 {									\
1246419Ssb155480 	atomic_inc_32(&(p)->refcnt);					\
1256419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1266419Ssb155480 }
1276419Ssb155480 
1286419Ssb155480 #define	VNET_FDBE_REFRELE(p)						\
1296419Ssb155480 {									\
1306419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1316419Ssb155480 	atomic_dec_32(&(p)->refcnt);					\
1326419Ssb155480 }
1336419Ssb155480 
1342311Sseb static mac_callbacks_t vnet_m_callbacks = {
1352311Sseb 	0,
1362311Sseb 	vnet_m_stat,
1372311Sseb 	vnet_m_start,
1382311Sseb 	vnet_m_stop,
1392311Sseb 	vnet_m_promisc,
1402311Sseb 	vnet_m_multicst,
1412311Sseb 	vnet_m_unicst,
1422311Sseb 	vnet_m_tx,
1432311Sseb 	NULL,
1442311Sseb 	NULL,
1452311Sseb 	NULL
1462311Sseb };
1472311Sseb 
1481991Sheppo /*
1491991Sheppo  * Linked list of "vnet_t" structures - one per instance.
1501991Sheppo  */
1511991Sheppo static vnet_t	*vnet_headp = NULL;
1521991Sheppo static krwlock_t vnet_rw;
1531991Sheppo 
1541991Sheppo /* Tunables */
1551991Sheppo uint32_t vnet_ntxds = VNET_NTXDS;	/* power of 2 transmit descriptors */
1561991Sheppo uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */
1571991Sheppo uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT;  /* tx timeout in msec */
1582410Slm66018 uint32_t vnet_ldc_mtu = VNET_LDC_MTU;		/* ldc mtu */
1596419Ssb155480 
1607529SSriharsha.Basavapatna@Sun.COM /*
1617529SSriharsha.Basavapatna@Sun.COM  * Set this to non-zero to enable additional internal receive buffer pools
1627529SSriharsha.Basavapatna@Sun.COM  * based on the MTU of the device for better performance at the cost of more
1637529SSriharsha.Basavapatna@Sun.COM  * memory consumption. This is turned off by default, to use allocb(9F) for
1647529SSriharsha.Basavapatna@Sun.COM  * receive buffer allocations of sizes > 2K.
1657529SSriharsha.Basavapatna@Sun.COM  */
1667529SSriharsha.Basavapatna@Sun.COM boolean_t vnet_jumbo_rxpools = B_FALSE;
1677529SSriharsha.Basavapatna@Sun.COM 
1686419Ssb155480 /* # of chains in fdb hash table */
1696419Ssb155480 uint32_t	vnet_fdb_nchains = VNET_NFDB_HASH;
1706419Ssb155480 
1716419Ssb155480 /* Internal tunables */
1726419Ssb155480 uint32_t	vnet_ethermtu = 1500;	/* mtu of the device */
1736419Ssb155480 
1746419Ssb155480 /*
1756419Ssb155480  * Default vlan id. This is only used internally when the "default-vlan-id"
1766419Ssb155480  * property is not present in the MD device node. Therefore, this should not be
1776419Ssb155480  * used as a tunable; if this value is changed, the corresponding variable
1786419Ssb155480  * should be updated to the same value in vsw and also other vnets connected to
1796419Ssb155480  * the same vsw.
1806419Ssb155480  */
1816419Ssb155480 uint16_t	vnet_default_vlan_id = 1;
1826419Ssb155480 
1836419Ssb155480 /* delay in usec to wait for all references on a fdb entry to be dropped */
1846419Ssb155480 uint32_t vnet_fdbe_refcnt_delay = 10;
1851991Sheppo 
1866495Sspeer static struct ether_addr etherbroadcastaddr = {
1876495Sspeer 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1886495Sspeer };
1896495Sspeer 
1906495Sspeer 
1911991Sheppo /*
1921991Sheppo  * Property names
1931991Sheppo  */
1941991Sheppo static char macaddr_propname[] = "local-mac-address";
1951991Sheppo 
1961991Sheppo /*
1971991Sheppo  * This is the string displayed by modinfo(1m).
1981991Sheppo  */
1997529SSriharsha.Basavapatna@Sun.COM static char vnet_ident[] = "vnet driver";
2001991Sheppo extern struct mod_ops mod_driverops;
2011991Sheppo static struct cb_ops cb_vnetops = {
2021991Sheppo 	nulldev,		/* cb_open */
2031991Sheppo 	nulldev,		/* cb_close */
2041991Sheppo 	nodev,			/* cb_strategy */
2051991Sheppo 	nodev,			/* cb_print */
2061991Sheppo 	nodev,			/* cb_dump */
2071991Sheppo 	nodev,			/* cb_read */
2081991Sheppo 	nodev,			/* cb_write */
2091991Sheppo 	nodev,			/* cb_ioctl */
2101991Sheppo 	nodev,			/* cb_devmap */
2111991Sheppo 	nodev,			/* cb_mmap */
2121991Sheppo 	nodev,			/* cb_segmap */
2131991Sheppo 	nochpoll,		/* cb_chpoll */
2141991Sheppo 	ddi_prop_op,		/* cb_prop_op */
2151991Sheppo 	NULL,			/* cb_stream */
2161991Sheppo 	(int)(D_MP)		/* cb_flag */
2171991Sheppo };
2181991Sheppo 
2191991Sheppo static struct dev_ops vnetops = {
2201991Sheppo 	DEVO_REV,		/* devo_rev */
2211991Sheppo 	0,			/* devo_refcnt */
2221991Sheppo 	NULL,			/* devo_getinfo */
2231991Sheppo 	nulldev,		/* devo_identify */
2241991Sheppo 	nulldev,		/* devo_probe */
2251991Sheppo 	vnetattach,		/* devo_attach */
2261991Sheppo 	vnetdetach,		/* devo_detach */
2271991Sheppo 	nodev,			/* devo_reset */
2281991Sheppo 	&cb_vnetops,		/* devo_cb_ops */
2297656SSherry.Moore@Sun.COM 	(struct bus_ops *)NULL,	/* devo_bus_ops */
2307656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
2317656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
2321991Sheppo };
2331991Sheppo 
2341991Sheppo static struct modldrv modldrv = {
2351991Sheppo 	&mod_driverops,		/* Type of module.  This one is a driver */
2361991Sheppo 	vnet_ident,		/* ID string */
2371991Sheppo 	&vnetops		/* driver specific ops */
2381991Sheppo };
2391991Sheppo 
2401991Sheppo static struct modlinkage modlinkage = {
2411991Sheppo 	MODREV_1, (void *)&modldrv, NULL
2421991Sheppo };
2431991Sheppo 
2444647Sraghuram #ifdef DEBUG
2451991Sheppo 
2461991Sheppo /*
2471991Sheppo  * Print debug messages - set to 0xf to enable all msgs
2481991Sheppo  */
2494647Sraghuram int vnet_dbglevel = 0x8;
2501991Sheppo 
2514647Sraghuram static void
2524647Sraghuram debug_printf(const char *fname, void *arg, const char *fmt, ...)
2531991Sheppo {
2541991Sheppo 	char    buf[512];
2551991Sheppo 	va_list ap;
2561991Sheppo 	vnet_t *vnetp = (vnet_t *)arg;
2574647Sraghuram 	char    *bufp = buf;
2581991Sheppo 
2594647Sraghuram 	if (vnetp == NULL) {
2604647Sraghuram 		(void) sprintf(bufp, "%s: ", fname);
2614647Sraghuram 		bufp += strlen(bufp);
2624647Sraghuram 	} else {
2634647Sraghuram 		(void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname);
2644647Sraghuram 		bufp += strlen(bufp);
2654647Sraghuram 	}
2664647Sraghuram 	va_start(ap, fmt);
2674647Sraghuram 	(void) vsprintf(bufp, fmt, ap);
2684647Sraghuram 	va_end(ap);
2694647Sraghuram 	cmn_err(CE_CONT, "%s\n", buf);
2704647Sraghuram }
2711991Sheppo 
2721991Sheppo #endif
2731991Sheppo 
2741991Sheppo /* _init(9E): initialize the loadable module */
2751991Sheppo int
2761991Sheppo _init(void)
2771991Sheppo {
2781991Sheppo 	int status;
2791991Sheppo 
2804647Sraghuram 	DBG1(NULL, "enter\n");
2811991Sheppo 
2821991Sheppo 	mac_init_ops(&vnetops, "vnet");
2831991Sheppo 	status = mod_install(&modlinkage);
2841991Sheppo 	if (status != 0) {
2851991Sheppo 		mac_fini_ops(&vnetops);
2861991Sheppo 	}
2876495Sspeer 	vdds_mod_init();
2884647Sraghuram 	DBG1(NULL, "exit(%d)\n", status);
2891991Sheppo 	return (status);
2901991Sheppo }
2911991Sheppo 
2921991Sheppo /* _fini(9E): prepare the module for unloading. */
2931991Sheppo int
2941991Sheppo _fini(void)
2951991Sheppo {
2961991Sheppo 	int status;
2971991Sheppo 
2984647Sraghuram 	DBG1(NULL, "enter\n");
2991991Sheppo 
3001991Sheppo 	status = mod_remove(&modlinkage);
3011991Sheppo 	if (status != 0)
3021991Sheppo 		return (status);
3031991Sheppo 	mac_fini_ops(&vnetops);
3046495Sspeer 	vdds_mod_fini();
3051991Sheppo 
3064647Sraghuram 	DBG1(NULL, "exit(%d)\n", status);
3071991Sheppo 	return (status);
3081991Sheppo }
3091991Sheppo 
3101991Sheppo /* _info(9E): return information about the loadable module */
3111991Sheppo int
3121991Sheppo _info(struct modinfo *modinfop)
3131991Sheppo {
3141991Sheppo 	return (mod_info(&modlinkage, modinfop));
3151991Sheppo }
3161991Sheppo 
3171991Sheppo /*
3181991Sheppo  * attach(9E): attach a device to the system.
3191991Sheppo  * called once for each instance of the device on the system.
3201991Sheppo  */
3211991Sheppo static int
3221991Sheppo vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3231991Sheppo {
3241991Sheppo 	vnet_t		*vnetp;
3256495Sspeer 	int		status;
3261991Sheppo 	int		instance;
3276495Sspeer 	uint64_t	reg;
3286495Sspeer 	char		qname[TASKQ_NAMELEN];
3294647Sraghuram 	enum	{ AST_init = 0x0, AST_vnet_alloc = 0x1,
3304647Sraghuram 		AST_mac_alloc = 0x2, AST_read_macaddr = 0x4,
3316495Sspeer 		AST_vgen_init = 0x8, AST_fdbh_alloc = 0x10,
3326495Sspeer 		AST_vdds_init = 0x20, AST_taskq_create = 0x40,
3336495Sspeer 		AST_vnet_list = 0x80 } attach_state;
3341991Sheppo 
3351991Sheppo 	attach_state = AST_init;
3361991Sheppo 
3371991Sheppo 	switch (cmd) {
3381991Sheppo 	case DDI_ATTACH:
3391991Sheppo 		break;
3401991Sheppo 	case DDI_RESUME:
3411991Sheppo 	case DDI_PM_RESUME:
3421991Sheppo 	default:
3431991Sheppo 		goto vnet_attach_fail;
3441991Sheppo 	}
3451991Sheppo 
3461991Sheppo 	instance = ddi_get_instance(dip);
3474647Sraghuram 	DBG1(NULL, "instance(%d) enter\n", instance);
3481991Sheppo 
3491991Sheppo 	/* allocate vnet_t and mac_t structures */
3501991Sheppo 	vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP);
3516495Sspeer 	vnetp->dip = dip;
3526495Sspeer 	vnetp->instance = instance;
3536495Sspeer 	rw_init(&vnetp->vrwlock, NULL, RW_DRIVER, NULL);
3546495Sspeer 	rw_init(&vnetp->vsw_fp_rw, NULL, RW_DRIVER, NULL);
3551991Sheppo 	attach_state |= AST_vnet_alloc;
3561991Sheppo 
3576495Sspeer 	status = vdds_init(vnetp);
3586495Sspeer 	if (status != 0) {
3596495Sspeer 		goto vnet_attach_fail;
3606495Sspeer 	}
3616495Sspeer 	attach_state |= AST_vdds_init;
3626495Sspeer 
3631991Sheppo 	/* setup links to vnet_t from both devinfo and mac_t */
3641991Sheppo 	ddi_set_driver_private(dip, (caddr_t)vnetp);
3651991Sheppo 
3661991Sheppo 	/* read the mac address */
3671991Sheppo 	status = vnet_read_mac_address(vnetp);
3681991Sheppo 	if (status != DDI_SUCCESS) {
3691991Sheppo 		goto vnet_attach_fail;
3701991Sheppo 	}
3711991Sheppo 	attach_state |= AST_read_macaddr;
3721991Sheppo 
3736495Sspeer 	reg = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
3746495Sspeer 	    DDI_PROP_DONTPASS, "reg", -1);
3756495Sspeer 	if (reg == -1) {
3766495Sspeer 		goto vnet_attach_fail;
3776495Sspeer 	}
3786495Sspeer 	vnetp->reg = reg;
3796495Sspeer 
3806495Sspeer 	vnet_fdb_create(vnetp);
3816495Sspeer 	attach_state |= AST_fdbh_alloc;
3826495Sspeer 
3836495Sspeer 	(void) snprintf(qname, TASKQ_NAMELEN, "vnet_taskq%d", instance);
3846495Sspeer 	if ((vnetp->taskqp = ddi_taskq_create(dip, qname, 1,
3856495Sspeer 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
3866495Sspeer 		cmn_err(CE_WARN, "!vnet%d: Unable to create task queue",
3876495Sspeer 		    instance);
3886495Sspeer 		goto vnet_attach_fail;
3896495Sspeer 	}
3906495Sspeer 	attach_state |= AST_taskq_create;
3916495Sspeer 
3926495Sspeer 	/* add to the list of vnet devices */
3936495Sspeer 	WRITE_ENTER(&vnet_rw);
3946495Sspeer 	vnetp->nextp = vnet_headp;
3956495Sspeer 	vnet_headp = vnetp;
3966495Sspeer 	RW_EXIT(&vnet_rw);
3976495Sspeer 
3986495Sspeer 	attach_state |= AST_vnet_list;
3996495Sspeer 
4001991Sheppo 	/*
4016495Sspeer 	 * Initialize the generic vnet plugin which provides
4026495Sspeer 	 * communication via sun4v LDC (logical domain channel) based
4036495Sspeer 	 * resources. It will register the LDC resources as and when
4046495Sspeer 	 * they become available.
4051991Sheppo 	 */
4066495Sspeer 	status = vgen_init(vnetp, reg, vnetp->dip,
4076495Sspeer 	    (uint8_t *)vnetp->curr_macaddr, &vnetp->vgenhdl);
4081991Sheppo 	if (status != DDI_SUCCESS) {
4094647Sraghuram 		DERR(vnetp, "vgen_init() failed\n");
4101991Sheppo 		goto vnet_attach_fail;
4111991Sheppo 	}
4121991Sheppo 	attach_state |= AST_vgen_init;
4131991Sheppo 
4141991Sheppo 	/* register with MAC layer */
4151991Sheppo 	status = vnet_mac_register(vnetp);
4161991Sheppo 	if (status != DDI_SUCCESS) {
4171991Sheppo 		goto vnet_attach_fail;
4181991Sheppo 	}
4191991Sheppo 
4204647Sraghuram 	DBG1(NULL, "instance(%d) exit\n", instance);
4211991Sheppo 	return (DDI_SUCCESS);
4221991Sheppo 
4231991Sheppo vnet_attach_fail:
4246495Sspeer 
4256495Sspeer 	if (attach_state & AST_vnet_list) {
4266495Sspeer 		vnet_t		**vnetpp;
4276495Sspeer 		/* unlink from instance(vnet_t) list */
4286495Sspeer 		WRITE_ENTER(&vnet_rw);
4296495Sspeer 		for (vnetpp = &vnet_headp; *vnetpp;
4306495Sspeer 		    vnetpp = &(*vnetpp)->nextp) {
4316495Sspeer 			if (*vnetpp == vnetp) {
4326495Sspeer 				*vnetpp = vnetp->nextp;
4336495Sspeer 				break;
4346495Sspeer 			}
4356495Sspeer 		}
4366495Sspeer 		RW_EXIT(&vnet_rw);
4376495Sspeer 	}
4386495Sspeer 
4396495Sspeer 	if (attach_state & AST_vdds_init) {
4406495Sspeer 		vdds_cleanup(vnetp);
4416495Sspeer 	}
4426495Sspeer 	if (attach_state & AST_taskq_create) {
4436495Sspeer 		ddi_taskq_destroy(vnetp->taskqp);
4446495Sspeer 	}
4451991Sheppo 	if (attach_state & AST_fdbh_alloc) {
4466419Ssb155480 		vnet_fdb_destroy(vnetp);
4471991Sheppo 	}
4481991Sheppo 	if (attach_state & AST_vgen_init) {
4496495Sspeer 		(void) vgen_uninit(vnetp->vgenhdl);
4501991Sheppo 	}
4511991Sheppo 	if (attach_state & AST_vnet_alloc) {
4526495Sspeer 		rw_destroy(&vnetp->vrwlock);
4536495Sspeer 		rw_destroy(&vnetp->vsw_fp_rw);
4541991Sheppo 		KMEM_FREE(vnetp);
4551991Sheppo 	}
4561991Sheppo 	return (DDI_FAILURE);
4571991Sheppo }
4581991Sheppo 
4591991Sheppo /*
4601991Sheppo  * detach(9E): detach a device from the system.
4611991Sheppo  */
4621991Sheppo static int
4631991Sheppo vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4641991Sheppo {
4651991Sheppo 	vnet_t		*vnetp;
4661991Sheppo 	vnet_t		**vnetpp;
4671991Sheppo 	int		instance;
4682336Snarayan 	int		rv;
4691991Sheppo 
4701991Sheppo 	instance = ddi_get_instance(dip);
4714647Sraghuram 	DBG1(NULL, "instance(%d) enter\n", instance);
4721991Sheppo 
4731991Sheppo 	vnetp = ddi_get_driver_private(dip);
4741991Sheppo 	if (vnetp == NULL) {
4751991Sheppo 		goto vnet_detach_fail;
4761991Sheppo 	}
4771991Sheppo 
4781991Sheppo 	switch (cmd) {
4791991Sheppo 	case DDI_DETACH:
4801991Sheppo 		break;
4811991Sheppo 	case DDI_SUSPEND:
4821991Sheppo 	case DDI_PM_SUSPEND:
4831991Sheppo 	default:
4841991Sheppo 		goto vnet_detach_fail;
4851991Sheppo 	}
4861991Sheppo 
4876495Sspeer 	(void) vdds_cleanup(vnetp);
4886495Sspeer 	rv = vgen_uninit(vnetp->vgenhdl);
4896495Sspeer 	if (rv != DDI_SUCCESS) {
4906495Sspeer 		goto vnet_detach_fail;
4912336Snarayan 	}
4922336Snarayan 
4931991Sheppo 	/*
4941991Sheppo 	 * Unregister from the MAC subsystem.  This can fail, in
4951991Sheppo 	 * particular if there are DLPI style-2 streams still open -
4961991Sheppo 	 * in which case we just return failure.
4971991Sheppo 	 */
4982311Sseb 	if (mac_unregister(vnetp->mh) != 0)
4991991Sheppo 		goto vnet_detach_fail;
5001991Sheppo 
5011991Sheppo 	/* unlink from instance(vnet_t) list */
5021991Sheppo 	WRITE_ENTER(&vnet_rw);
5031991Sheppo 	for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) {
5041991Sheppo 		if (*vnetpp == vnetp) {
5051991Sheppo 			*vnetpp = vnetp->nextp;
5061991Sheppo 			break;
5071991Sheppo 		}
5081991Sheppo 	}
5091991Sheppo 	RW_EXIT(&vnet_rw);
5101991Sheppo 
5116495Sspeer 	ddi_taskq_destroy(vnetp->taskqp);
5126419Ssb155480 	/* destroy fdb */
5136419Ssb155480 	vnet_fdb_destroy(vnetp);
5143297Ssb155480 
5156495Sspeer 	rw_destroy(&vnetp->vrwlock);
5166495Sspeer 	rw_destroy(&vnetp->vsw_fp_rw);
5171991Sheppo 	KMEM_FREE(vnetp);
5181991Sheppo 
5191991Sheppo 	return (DDI_SUCCESS);
5201991Sheppo 
5211991Sheppo vnet_detach_fail:
5221991Sheppo 	return (DDI_FAILURE);
5231991Sheppo }
5241991Sheppo 
5251991Sheppo /* enable the device for transmit/receive */
5261991Sheppo static int
5271991Sheppo vnet_m_start(void *arg)
5281991Sheppo {
5291991Sheppo 	vnet_t		*vnetp = arg;
5301991Sheppo 
5314647Sraghuram 	DBG1(vnetp, "enter\n");
5321991Sheppo 
5336495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
5346495Sspeer 	vnetp->flags |= VNET_STARTED;
5356495Sspeer 	vnet_start_resources(vnetp);
5366495Sspeer 	RW_EXIT(&vnetp->vrwlock);
5371991Sheppo 
5384647Sraghuram 	DBG1(vnetp, "exit\n");
5391991Sheppo 	return (VNET_SUCCESS);
5401991Sheppo 
5411991Sheppo }
5421991Sheppo 
5431991Sheppo /* stop transmit/receive for the device */
5441991Sheppo static void
5451991Sheppo vnet_m_stop(void *arg)
5461991Sheppo {
5471991Sheppo 	vnet_t		*vnetp = arg;
5481991Sheppo 
5494647Sraghuram 	DBG1(vnetp, "enter\n");
5501991Sheppo 
5516495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
5526495Sspeer 	if (vnetp->flags & VNET_STARTED) {
5536495Sspeer 		vnet_stop_resources(vnetp);
5546495Sspeer 		vnetp->flags &= ~VNET_STARTED;
5551991Sheppo 	}
5566495Sspeer 	RW_EXIT(&vnetp->vrwlock);
5571991Sheppo 
5584647Sraghuram 	DBG1(vnetp, "exit\n");
5591991Sheppo }
5601991Sheppo 
5611991Sheppo /* set the unicast mac address of the device */
5621991Sheppo static int
5631991Sheppo vnet_m_unicst(void *arg, const uint8_t *macaddr)
5641991Sheppo {
5651991Sheppo 	_NOTE(ARGUNUSED(macaddr))
5661991Sheppo 
5671991Sheppo 	vnet_t *vnetp = arg;
5681991Sheppo 
5694647Sraghuram 	DBG1(vnetp, "enter\n");
5701991Sheppo 	/*
5712793Slm66018 	 * NOTE: setting mac address dynamically is not supported.
5721991Sheppo 	 */
5734647Sraghuram 	DBG1(vnetp, "exit\n");
5741991Sheppo 
5752109Slm66018 	return (VNET_FAILURE);
5761991Sheppo }
5771991Sheppo 
5781991Sheppo /* enable/disable a multicast address */
5791991Sheppo static int
5801991Sheppo vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
5811991Sheppo {
5821991Sheppo 	_NOTE(ARGUNUSED(add, mca))
5831991Sheppo 
5841991Sheppo 	vnet_t *vnetp = arg;
5856495Sspeer 	vnet_res_t	*vresp;
5866495Sspeer 	mac_register_t	*macp;
5872311Sseb 	mac_callbacks_t	*cbp;
5881991Sheppo 	int rv = VNET_SUCCESS;
5891991Sheppo 
5904647Sraghuram 	DBG1(vnetp, "enter\n");
5916495Sspeer 
5926495Sspeer 	READ_ENTER(&vnetp->vrwlock);
5936495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
5946495Sspeer 		if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
5956495Sspeer 			macp = &vresp->macreg;
5966495Sspeer 			cbp = macp->m_callbacks;
5976495Sspeer 			rv = cbp->mc_multicst(macp->m_driver, add, mca);
5981991Sheppo 		}
5991991Sheppo 	}
6006495Sspeer 	RW_EXIT(&vnetp->vrwlock);
6016495Sspeer 
6024647Sraghuram 	DBG1(vnetp, "exit(%d)\n", rv);
6031991Sheppo 	return (rv);
6041991Sheppo }
6051991Sheppo 
6061991Sheppo /* set or clear promiscuous mode on the device */
6071991Sheppo static int
6081991Sheppo vnet_m_promisc(void *arg, boolean_t on)
6091991Sheppo {
6101991Sheppo 	_NOTE(ARGUNUSED(on))
6111991Sheppo 
6121991Sheppo 	vnet_t *vnetp = arg;
6134647Sraghuram 	DBG1(vnetp, "enter\n");
6141991Sheppo 	/*
6152793Slm66018 	 * NOTE: setting promiscuous mode is not supported, just return success.
6161991Sheppo 	 */
6174647Sraghuram 	DBG1(vnetp, "exit\n");
6181991Sheppo 	return (VNET_SUCCESS);
6191991Sheppo }
6201991Sheppo 
6211991Sheppo /*
6221991Sheppo  * Transmit a chain of packets. This function provides switching functionality
6231991Sheppo  * based on the destination mac address to reach other guests (within ldoms) or
6241991Sheppo  * external hosts.
6251991Sheppo  */
6261991Sheppo mblk_t *
6271991Sheppo vnet_m_tx(void *arg, mblk_t *mp)
6281991Sheppo {
6296419Ssb155480 	vnet_t			*vnetp;
6306495Sspeer 	vnet_res_t		*vresp;
6316419Ssb155480 	mblk_t			*next;
6326495Sspeer 	mblk_t			*resid_mp;
6336495Sspeer 	mac_register_t		*macp;
6346495Sspeer 	struct ether_header	*ehp;
6356495Sspeer 	boolean_t		is_unicast;
6367896SSriharsha.Basavapatna@Sun.COM 	boolean_t		is_pvid;	/* non-default pvid ? */
6377896SSriharsha.Basavapatna@Sun.COM 	boolean_t		hres;		/* Hybrid resource ? */
6381991Sheppo 
6391991Sheppo 	vnetp = (vnet_t *)arg;
6404647Sraghuram 	DBG1(vnetp, "enter\n");
6411991Sheppo 	ASSERT(mp != NULL);
6421991Sheppo 
6437896SSriharsha.Basavapatna@Sun.COM 	is_pvid = (vnetp->pvid != vnetp->default_vlan_id) ? B_TRUE : B_FALSE;
6447896SSriharsha.Basavapatna@Sun.COM 
6451991Sheppo 	while (mp != NULL) {
6466419Ssb155480 
6471991Sheppo 		next = mp->b_next;
6481991Sheppo 		mp->b_next = NULL;
6491991Sheppo 
6506419Ssb155480 		/*
6516419Ssb155480 		 * Find fdb entry for the destination
6526419Ssb155480 		 * and hold a reference to it.
6536419Ssb155480 		 */
6541991Sheppo 		ehp = (struct ether_header *)mp->b_rptr;
6556495Sspeer 		vresp = vnet_fdbe_find(vnetp, &ehp->ether_dhost);
6566495Sspeer 		if (vresp != NULL) {
6571991Sheppo 
6581991Sheppo 			/*
6596419Ssb155480 			 * Destination found in FDB.
6606419Ssb155480 			 * The destination is a vnet device within ldoms
6616419Ssb155480 			 * and directly reachable, invoke the tx function
6626419Ssb155480 			 * in the fdb entry.
6631991Sheppo 			 */
6646495Sspeer 			macp = &vresp->macreg;
6656495Sspeer 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
6666419Ssb155480 
6676419Ssb155480 			/* tx done; now release ref on fdb entry */
6686495Sspeer 			VNET_FDBE_REFRELE(vresp);
6696419Ssb155480 
6701991Sheppo 			if (resid_mp != NULL) {
6711991Sheppo 				/* m_tx failed */
6721991Sheppo 				mp->b_next = next;
6731991Sheppo 				break;
6741991Sheppo 			}
6751991Sheppo 		} else {
6766495Sspeer 			is_unicast = !(IS_BROADCAST(ehp) ||
6776495Sspeer 			    (IS_MULTICAST(ehp)));
6781991Sheppo 			/*
6796419Ssb155480 			 * Destination is not in FDB.
6806495Sspeer 			 * If the destination is broadcast or multicast,
6816495Sspeer 			 * then forward the packet to vswitch.
6826495Sspeer 			 * If a Hybrid resource avilable, then send the
6836495Sspeer 			 * unicast packet via hybrid resource, otherwise
6846495Sspeer 			 * forward it to vswitch.
6851991Sheppo 			 */
6866419Ssb155480 			READ_ENTER(&vnetp->vsw_fp_rw);
6876419Ssb155480 
6886495Sspeer 			if ((is_unicast) && (vnetp->hio_fp != NULL)) {
6896495Sspeer 				vresp = vnetp->hio_fp;
6907896SSriharsha.Basavapatna@Sun.COM 				hres = B_TRUE;
6916495Sspeer 			} else {
6926495Sspeer 				vresp = vnetp->vsw_fp;
6937896SSriharsha.Basavapatna@Sun.COM 				hres = B_FALSE;
6946495Sspeer 			}
6956495Sspeer 			if (vresp == NULL) {
6966419Ssb155480 				/*
6976419Ssb155480 				 * no fdb entry to vsw? drop the packet.
6986419Ssb155480 				 */
6996419Ssb155480 				RW_EXIT(&vnetp->vsw_fp_rw);
7001991Sheppo 				freemsg(mp);
7016419Ssb155480 				mp = next;
7026419Ssb155480 				continue;
7031991Sheppo 			}
7046419Ssb155480 
7056419Ssb155480 			/* ref hold the fdb entry to vsw */
7066495Sspeer 			VNET_FDBE_REFHOLD(vresp);
7076419Ssb155480 
7086419Ssb155480 			RW_EXIT(&vnetp->vsw_fp_rw);
7096419Ssb155480 
7107896SSriharsha.Basavapatna@Sun.COM 			/*
7117896SSriharsha.Basavapatna@Sun.COM 			 * In the case of a hybrid resource we need to insert
7127896SSriharsha.Basavapatna@Sun.COM 			 * the tag for the pvid case here; unlike packets that
7137896SSriharsha.Basavapatna@Sun.COM 			 * are destined to a vnet/vsw in which case the vgen
7147896SSriharsha.Basavapatna@Sun.COM 			 * layer does the tagging before sending it over ldc.
7157896SSriharsha.Basavapatna@Sun.COM 			 */
7167896SSriharsha.Basavapatna@Sun.COM 			if (hres == B_TRUE) {
7177896SSriharsha.Basavapatna@Sun.COM 				/*
7187896SSriharsha.Basavapatna@Sun.COM 				 * Determine if the frame being transmitted
7197896SSriharsha.Basavapatna@Sun.COM 				 * over the hybrid resource is untagged. If so,
7207896SSriharsha.Basavapatna@Sun.COM 				 * insert the tag before transmitting.
7217896SSriharsha.Basavapatna@Sun.COM 				 */
7227896SSriharsha.Basavapatna@Sun.COM 				if (is_pvid == B_TRUE &&
7237896SSriharsha.Basavapatna@Sun.COM 				    ehp->ether_type != htons(ETHERTYPE_VLAN)) {
7247896SSriharsha.Basavapatna@Sun.COM 
7257896SSriharsha.Basavapatna@Sun.COM 					mp = vnet_vlan_insert_tag(mp,
7267896SSriharsha.Basavapatna@Sun.COM 					    vnetp->pvid);
7277896SSriharsha.Basavapatna@Sun.COM 					if (mp == NULL) {
7287896SSriharsha.Basavapatna@Sun.COM 						VNET_FDBE_REFRELE(vresp);
7297896SSriharsha.Basavapatna@Sun.COM 						mp = next;
7307896SSriharsha.Basavapatna@Sun.COM 						continue;
7317896SSriharsha.Basavapatna@Sun.COM 					}
7327896SSriharsha.Basavapatna@Sun.COM 
7337896SSriharsha.Basavapatna@Sun.COM 				}
7347896SSriharsha.Basavapatna@Sun.COM 			}
7357896SSriharsha.Basavapatna@Sun.COM 
7366495Sspeer 			macp = &vresp->macreg;
7376495Sspeer 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
7386419Ssb155480 
7396419Ssb155480 			/* tx done; now release ref on fdb entry */
7406495Sspeer 			VNET_FDBE_REFRELE(vresp);
7416419Ssb155480 
7426419Ssb155480 			if (resid_mp != NULL) {
7436419Ssb155480 				/* m_tx failed */
7446419Ssb155480 				mp->b_next = next;
7456419Ssb155480 				break;
7466419Ssb155480 			}
7471991Sheppo 		}
7481991Sheppo 
7491991Sheppo 		mp = next;
7501991Sheppo 	}
7511991Sheppo 
7524647Sraghuram 	DBG1(vnetp, "exit\n");
7531991Sheppo 	return (mp);
7541991Sheppo }
7551991Sheppo 
7562311Sseb /* get statistics from the device */
7572311Sseb int
7582311Sseb vnet_m_stat(void *arg, uint_t stat, uint64_t *val)
7591991Sheppo {
7601991Sheppo 	vnet_t *vnetp = arg;
7616495Sspeer 	vnet_res_t	*vresp;
7626495Sspeer 	mac_register_t	*macp;
7632311Sseb 	mac_callbacks_t	*cbp;
7642311Sseb 	uint64_t val_total = 0;
7651991Sheppo 
7664647Sraghuram 	DBG1(vnetp, "enter\n");
7671991Sheppo 
7681991Sheppo 	/*
7692311Sseb 	 * get the specified statistic from each transport and return the
7702311Sseb 	 * aggregate val.  This obviously only works for counters.
7711991Sheppo 	 */
7722311Sseb 	if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) ||
7732311Sseb 	    (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) {
7742311Sseb 		return (ENOTSUP);
7752311Sseb 	}
7766495Sspeer 
7776495Sspeer 	READ_ENTER(&vnetp->vrwlock);
7786495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
7796495Sspeer 		macp = &vresp->macreg;
7806495Sspeer 		cbp = macp->m_callbacks;
7816495Sspeer 		if (cbp->mc_getstat(macp->m_driver, stat, val) == 0)
7822311Sseb 			val_total += *val;
7831991Sheppo 	}
7846495Sspeer 	RW_EXIT(&vnetp->vrwlock);
7851991Sheppo 
7862311Sseb 	*val = val_total;
7872311Sseb 
7884647Sraghuram 	DBG1(vnetp, "exit\n");
7892311Sseb 	return (0);
7901991Sheppo }
7911991Sheppo 
7921991Sheppo /* wrapper function for mac_register() */
7931991Sheppo static int
7941991Sheppo vnet_mac_register(vnet_t *vnetp)
7951991Sheppo {
7962311Sseb 	mac_register_t	*macp;
7972311Sseb 	int		err;
7981991Sheppo 
7992311Sseb 	if ((macp = mac_alloc(MAC_VERSION)) == NULL)
8002311Sseb 		return (DDI_FAILURE);
8012311Sseb 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
8022311Sseb 	macp->m_driver = vnetp;
8031991Sheppo 	macp->m_dip = vnetp->dip;
8042311Sseb 	macp->m_src_addr = vnetp->curr_macaddr;
8052311Sseb 	macp->m_callbacks = &vnet_m_callbacks;
8062311Sseb 	macp->m_min_sdu = 0;
8077529SSriharsha.Basavapatna@Sun.COM 	macp->m_max_sdu = vnetp->mtu;
8086419Ssb155480 	macp->m_margin = VLAN_TAGSZ;
8091991Sheppo 
8101991Sheppo 	/*
8111991Sheppo 	 * Finally, we're ready to register ourselves with the MAC layer
8121991Sheppo 	 * interface; if this succeeds, we're all ready to start()
8131991Sheppo 	 */
8142311Sseb 	err = mac_register(macp, &vnetp->mh);
8152311Sseb 	mac_free(macp);
8162311Sseb 	return (err == 0 ? DDI_SUCCESS : DDI_FAILURE);
8171991Sheppo }
8181991Sheppo 
8191991Sheppo /* read the mac address of the device */
8201991Sheppo static int
8211991Sheppo vnet_read_mac_address(vnet_t *vnetp)
8221991Sheppo {
8231991Sheppo 	uchar_t 	*macaddr;
8241991Sheppo 	uint32_t 	size;
8251991Sheppo 	int 		rv;
8261991Sheppo 
8271991Sheppo 	rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip,
8284650Sraghuram 	    DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size);
8291991Sheppo 	if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) {
8304647Sraghuram 		DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n",
8314647Sraghuram 		    macaddr_propname, rv);
8321991Sheppo 		return (DDI_FAILURE);
8331991Sheppo 	}
8341991Sheppo 	bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL);
8351991Sheppo 	bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL);
8361991Sheppo 	ddi_prop_free(macaddr);
8371991Sheppo 
8381991Sheppo 	return (DDI_SUCCESS);
8391991Sheppo }
8401991Sheppo 
8416419Ssb155480 static void
8426419Ssb155480 vnet_fdb_create(vnet_t *vnetp)
8431991Sheppo {
8446419Ssb155480 	char		hashname[MAXNAMELEN];
8451991Sheppo 
8466419Ssb155480 	(void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash",
8476419Ssb155480 	    vnetp->instance);
8486419Ssb155480 	vnetp->fdb_nchains = vnet_fdb_nchains;
8496419Ssb155480 	vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains,
8506419Ssb155480 	    mod_hash_null_valdtor, sizeof (void *));
8516419Ssb155480 }
8521991Sheppo 
8536419Ssb155480 static void
8546419Ssb155480 vnet_fdb_destroy(vnet_t *vnetp)
8556419Ssb155480 {
8566419Ssb155480 	/* destroy fdb-hash-table */
8576419Ssb155480 	if (vnetp->fdb_hashp != NULL) {
8586419Ssb155480 		mod_hash_destroy_hash(vnetp->fdb_hashp);
8596419Ssb155480 		vnetp->fdb_hashp = NULL;
8606419Ssb155480 		vnetp->fdb_nchains = 0;
8611991Sheppo 	}
8621991Sheppo }
8631991Sheppo 
8646419Ssb155480 /*
8656419Ssb155480  * Add an entry into the fdb.
8666419Ssb155480  */
8671991Sheppo void
8686495Sspeer vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp)
8691991Sheppo {
8706419Ssb155480 	uint64_t	addr = 0;
8716419Ssb155480 	int		rv;
8726419Ssb155480 
8736495Sspeer 	KEY_HASH(addr, vresp->rem_macaddr);
8741991Sheppo 
8756419Ssb155480 	/*
8766495Sspeer 	 * If the entry being added corresponds to LDC_SERVICE resource,
8776495Sspeer 	 * that is, vswitch connection, it is added to the hash and also
8786495Sspeer 	 * the entry is cached, an additional reference count reflects
8796495Sspeer 	 * this. The HYBRID resource is not added to the hash, but only
8806495Sspeer 	 * cached, as it is only used for sending out packets for unknown
8816495Sspeer 	 * unicast destinations.
8826419Ssb155480 	 */
8836495Sspeer 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
8846495Sspeer 	    (vresp->refcnt = 1) : (vresp->refcnt = 0);
8851991Sheppo 
8866419Ssb155480 	/*
8876419Ssb155480 	 * Note: duplicate keys will be rejected by mod_hash.
8886419Ssb155480 	 */
8896495Sspeer 	if (vresp->type != VIO_NET_RES_HYBRID) {
8906495Sspeer 		rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr,
8916495Sspeer 		    (mod_hash_val_t)vresp);
8926495Sspeer 		if (rv != 0) {
8936495Sspeer 			DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr);
8946495Sspeer 			return;
8956495Sspeer 		}
8961991Sheppo 	}
8971991Sheppo 
8986495Sspeer 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
8996419Ssb155480 		/* Cache the fdb entry to vsw-port */
9006419Ssb155480 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9016419Ssb155480 		if (vnetp->vsw_fp == NULL)
9026495Sspeer 			vnetp->vsw_fp = vresp;
9036495Sspeer 		RW_EXIT(&vnetp->vsw_fp_rw);
9046495Sspeer 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
9056495Sspeer 		/* Cache the fdb entry to hybrid resource */
9066495Sspeer 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9076495Sspeer 		if (vnetp->hio_fp == NULL)
9086495Sspeer 			vnetp->hio_fp = vresp;
9096419Ssb155480 		RW_EXIT(&vnetp->vsw_fp_rw);
9102793Slm66018 	}
9111991Sheppo }
9121991Sheppo 
9136419Ssb155480 /*
9146419Ssb155480  * Remove an entry from fdb.
9156419Ssb155480  */
9166495Sspeer static void
9176495Sspeer vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp)
9185641Swentaoy {
9196419Ssb155480 	uint64_t	addr = 0;
9206419Ssb155480 	int		rv;
9216419Ssb155480 	uint32_t	refcnt;
9226495Sspeer 	vnet_res_t	*tmp;
9235641Swentaoy 
9246495Sspeer 	KEY_HASH(addr, vresp->rem_macaddr);
9255641Swentaoy 
9266419Ssb155480 	/*
9276419Ssb155480 	 * Remove the entry from fdb hash table.
9286419Ssb155480 	 * This prevents further references to this fdb entry.
9296419Ssb155480 	 */
9306495Sspeer 	if (vresp->type != VIO_NET_RES_HYBRID) {
9316495Sspeer 		rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr,
9326495Sspeer 		    (mod_hash_val_t *)&tmp);
9336495Sspeer 		if (rv != 0) {
9346495Sspeer 			/*
9356495Sspeer 			 * As the resources are added to the hash only
9366495Sspeer 			 * after they are started, this can occur if
9376495Sspeer 			 * a resource unregisters before it is ever started.
9386495Sspeer 			 */
9396495Sspeer 			return;
9406495Sspeer 		}
9416495Sspeer 	}
9425641Swentaoy 
9436495Sspeer 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
9446419Ssb155480 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9455641Swentaoy 
9466495Sspeer 		ASSERT(tmp == vnetp->vsw_fp);
9476419Ssb155480 		vnetp->vsw_fp = NULL;
9486419Ssb155480 
9496419Ssb155480 		RW_EXIT(&vnetp->vsw_fp_rw);
9506495Sspeer 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
9516495Sspeer 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9526495Sspeer 
9536495Sspeer 		vnetp->hio_fp = NULL;
9546495Sspeer 
9556495Sspeer 		RW_EXIT(&vnetp->vsw_fp_rw);
9565641Swentaoy 	}
9575641Swentaoy 
9585641Swentaoy 	/*
9596419Ssb155480 	 * If there are threads already ref holding before the entry was
9606419Ssb155480 	 * removed from hash table, then wait for ref count to drop to zero.
9615641Swentaoy 	 */
9626495Sspeer 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
9636495Sspeer 	    (refcnt = 1) : (refcnt = 0);
9646495Sspeer 	while (vresp->refcnt > refcnt) {
9656419Ssb155480 		delay(drv_usectohz(vnet_fdbe_refcnt_delay));
9666419Ssb155480 	}
9671991Sheppo }
9681991Sheppo 
9696419Ssb155480 /*
9706419Ssb155480  * Search fdb for a given mac address. If an entry is found, hold
9716419Ssb155480  * a reference to it and return the entry; else returns NULL.
9726419Ssb155480  */
9736495Sspeer static vnet_res_t *
9746419Ssb155480 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp)
9751991Sheppo {
9766419Ssb155480 	uint64_t	key = 0;
9776495Sspeer 	vnet_res_t	*vresp;
9786419Ssb155480 	int		rv;
9796419Ssb155480 
9806495Sspeer 	KEY_HASH(key, addrp->ether_addr_octet);
9816419Ssb155480 
9826419Ssb155480 	rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key,
9836495Sspeer 	    (mod_hash_val_t *)&vresp, vnet_fdbe_find_cb);
9841991Sheppo 
9856419Ssb155480 	if (rv != 0)
9866419Ssb155480 		return (NULL);
9871991Sheppo 
9886495Sspeer 	return (vresp);
9896419Ssb155480 }
9901991Sheppo 
9916419Ssb155480 /*
9926419Ssb155480  * Callback function provided to mod_hash_find_cb(). After finding the fdb
9936419Ssb155480  * entry corresponding to the key (macaddr), this callback will be invoked by
9946419Ssb155480  * mod_hash_find_cb() to atomically increment the reference count on the fdb
9956419Ssb155480  * entry before returning the found entry.
9966419Ssb155480  */
9976419Ssb155480 static void
9986419Ssb155480 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
9996419Ssb155480 {
10006419Ssb155480 	_NOTE(ARGUNUSED(key))
10016495Sspeer 	VNET_FDBE_REFHOLD((vnet_res_t *)val);
10026495Sspeer }
10036495Sspeer 
10047896SSriharsha.Basavapatna@Sun.COM /*
10057896SSriharsha.Basavapatna@Sun.COM  * Frames received that are tagged with the pvid of the vnet device must be
10067896SSriharsha.Basavapatna@Sun.COM  * untagged before sending up the stack. This function walks the chain of rx
10077896SSriharsha.Basavapatna@Sun.COM  * frames, untags any such frames and returns the updated chain.
10087896SSriharsha.Basavapatna@Sun.COM  *
10097896SSriharsha.Basavapatna@Sun.COM  * Arguments:
10107896SSriharsha.Basavapatna@Sun.COM  *    pvid:  pvid of the vnet device for which packets are being received
10117896SSriharsha.Basavapatna@Sun.COM  *    mp:    head of pkt chain to be validated and untagged
10127896SSriharsha.Basavapatna@Sun.COM  *
10137896SSriharsha.Basavapatna@Sun.COM  * Returns:
10147896SSriharsha.Basavapatna@Sun.COM  *    mp:    head of updated chain of packets
10157896SSriharsha.Basavapatna@Sun.COM  */
10167896SSriharsha.Basavapatna@Sun.COM static void
10177896SSriharsha.Basavapatna@Sun.COM vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp)
10187896SSriharsha.Basavapatna@Sun.COM {
10197896SSriharsha.Basavapatna@Sun.COM 	struct ether_vlan_header	*evhp;
10207896SSriharsha.Basavapatna@Sun.COM 	mblk_t				*bp;
10217896SSriharsha.Basavapatna@Sun.COM 	mblk_t				*bpt;
10227896SSriharsha.Basavapatna@Sun.COM 	mblk_t				*bph;
10237896SSriharsha.Basavapatna@Sun.COM 	mblk_t				*bpn;
10247896SSriharsha.Basavapatna@Sun.COM 
10257896SSriharsha.Basavapatna@Sun.COM 	bpn = bph = bpt = NULL;
10267896SSriharsha.Basavapatna@Sun.COM 
10277896SSriharsha.Basavapatna@Sun.COM 	for (bp = *mp; bp != NULL; bp = bpn) {
10287896SSriharsha.Basavapatna@Sun.COM 
10297896SSriharsha.Basavapatna@Sun.COM 		bpn = bp->b_next;
10307896SSriharsha.Basavapatna@Sun.COM 		bp->b_next = bp->b_prev = NULL;
10317896SSriharsha.Basavapatna@Sun.COM 
10327896SSriharsha.Basavapatna@Sun.COM 		evhp = (struct ether_vlan_header *)bp->b_rptr;
10337896SSriharsha.Basavapatna@Sun.COM 
10347896SSriharsha.Basavapatna@Sun.COM 		if (ntohs(evhp->ether_tpid) == ETHERTYPE_VLAN &&
10357896SSriharsha.Basavapatna@Sun.COM 		    VLAN_ID(ntohs(evhp->ether_tci)) == pvid) {
10367896SSriharsha.Basavapatna@Sun.COM 
10377896SSriharsha.Basavapatna@Sun.COM 			bp = vnet_vlan_remove_tag(bp);
10387896SSriharsha.Basavapatna@Sun.COM 			if (bp == NULL) {
10397896SSriharsha.Basavapatna@Sun.COM 				continue;
10407896SSriharsha.Basavapatna@Sun.COM 			}
10417896SSriharsha.Basavapatna@Sun.COM 
10427896SSriharsha.Basavapatna@Sun.COM 		}
10437896SSriharsha.Basavapatna@Sun.COM 
10447896SSriharsha.Basavapatna@Sun.COM 		/* build a chain of processed packets */
10457896SSriharsha.Basavapatna@Sun.COM 		if (bph == NULL) {
10467896SSriharsha.Basavapatna@Sun.COM 			bph = bpt = bp;
10477896SSriharsha.Basavapatna@Sun.COM 		} else {
10487896SSriharsha.Basavapatna@Sun.COM 			bpt->b_next = bp;
10497896SSriharsha.Basavapatna@Sun.COM 			bpt = bp;
10507896SSriharsha.Basavapatna@Sun.COM 		}
10517896SSriharsha.Basavapatna@Sun.COM 
10527896SSriharsha.Basavapatna@Sun.COM 	}
10537896SSriharsha.Basavapatna@Sun.COM 
10547896SSriharsha.Basavapatna@Sun.COM 	*mp = bph;
10557896SSriharsha.Basavapatna@Sun.COM }
10567896SSriharsha.Basavapatna@Sun.COM 
10576495Sspeer static void
10586495Sspeer vnet_rx(vio_net_handle_t vrh, mblk_t *mp)
10596495Sspeer {
10607896SSriharsha.Basavapatna@Sun.COM 	vnet_res_t	*vresp = (vnet_res_t *)vrh;
10617896SSriharsha.Basavapatna@Sun.COM 	vnet_t		*vnetp = vresp->vnetp;
10627896SSriharsha.Basavapatna@Sun.COM 
10637896SSriharsha.Basavapatna@Sun.COM 	if ((vnetp == NULL) || (vnetp->mh == 0)) {
10647896SSriharsha.Basavapatna@Sun.COM 		freemsgchain(mp);
10657896SSriharsha.Basavapatna@Sun.COM 		return;
10667896SSriharsha.Basavapatna@Sun.COM 	}
10676495Sspeer 
10687896SSriharsha.Basavapatna@Sun.COM 	/*
10697896SSriharsha.Basavapatna@Sun.COM 	 * Packets received over a hybrid resource need additional processing
10707896SSriharsha.Basavapatna@Sun.COM 	 * to remove the tag, for the pvid case. The underlying resource is
10717896SSriharsha.Basavapatna@Sun.COM 	 * not aware of the vnet's pvid and thus packets are received with the
10727896SSriharsha.Basavapatna@Sun.COM 	 * vlan tag in the header; unlike packets that are received over a ldc
10737896SSriharsha.Basavapatna@Sun.COM 	 * channel in which case the peer vnet/vsw would have already removed
10747896SSriharsha.Basavapatna@Sun.COM 	 * the tag.
10757896SSriharsha.Basavapatna@Sun.COM 	 */
10767896SSriharsha.Basavapatna@Sun.COM 	if (vresp->type == VIO_NET_RES_HYBRID &&
10777896SSriharsha.Basavapatna@Sun.COM 	    vnetp->pvid != vnetp->default_vlan_id) {
10787896SSriharsha.Basavapatna@Sun.COM 
10797896SSriharsha.Basavapatna@Sun.COM 		vnet_rx_frames_untag(vnetp->pvid, &mp);
10807896SSriharsha.Basavapatna@Sun.COM 		if (mp == NULL) {
10817896SSriharsha.Basavapatna@Sun.COM 			return;
10827896SSriharsha.Basavapatna@Sun.COM 		}
10836495Sspeer 	}
10847896SSriharsha.Basavapatna@Sun.COM 
10857896SSriharsha.Basavapatna@Sun.COM 	mac_rx(vnetp->mh, NULL, mp);
10861991Sheppo }
10872311Sseb 
10882311Sseb void
10896495Sspeer vnet_tx_update(vio_net_handle_t vrh)
10906495Sspeer {
10916495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vrh;
10926495Sspeer 	vnet_t *vnetp = vresp->vnetp;
10936495Sspeer 
10946495Sspeer 	if ((vnetp != NULL) && (vnetp->mh != NULL)) {
10956495Sspeer 		mac_tx_update(vnetp->mh);
10966495Sspeer 	}
10976495Sspeer }
10986495Sspeer 
10996495Sspeer /*
11007529SSriharsha.Basavapatna@Sun.COM  * Update the new mtu of vnet into the mac layer. First check if the device has
11017529SSriharsha.Basavapatna@Sun.COM  * been plumbed and if so fail the mtu update. Returns 0 on success.
11027529SSriharsha.Basavapatna@Sun.COM  */
11037529SSriharsha.Basavapatna@Sun.COM int
11047529SSriharsha.Basavapatna@Sun.COM vnet_mtu_update(vnet_t *vnetp, uint32_t mtu)
11057529SSriharsha.Basavapatna@Sun.COM {
11067529SSriharsha.Basavapatna@Sun.COM 	int	rv;
11077529SSriharsha.Basavapatna@Sun.COM 
11087529SSriharsha.Basavapatna@Sun.COM 	if (vnetp == NULL || vnetp->mh == NULL) {
11097529SSriharsha.Basavapatna@Sun.COM 		return (EINVAL);
11107529SSriharsha.Basavapatna@Sun.COM 	}
11117529SSriharsha.Basavapatna@Sun.COM 
11127529SSriharsha.Basavapatna@Sun.COM 	WRITE_ENTER(&vnetp->vrwlock);
11137529SSriharsha.Basavapatna@Sun.COM 
11147529SSriharsha.Basavapatna@Sun.COM 	if (vnetp->flags & VNET_STARTED) {
11157529SSriharsha.Basavapatna@Sun.COM 		RW_EXIT(&vnetp->vrwlock);
11167529SSriharsha.Basavapatna@Sun.COM 		cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu "
11177529SSriharsha.Basavapatna@Sun.COM 		    "update as the device is plumbed\n",
11187529SSriharsha.Basavapatna@Sun.COM 		    vnetp->instance);
11197529SSriharsha.Basavapatna@Sun.COM 		return (EBUSY);
11207529SSriharsha.Basavapatna@Sun.COM 	}
11217529SSriharsha.Basavapatna@Sun.COM 
11227529SSriharsha.Basavapatna@Sun.COM 	/* update mtu in the mac layer */
11237529SSriharsha.Basavapatna@Sun.COM 	rv = mac_maxsdu_update(vnetp->mh, mtu);
11247529SSriharsha.Basavapatna@Sun.COM 	if (rv != 0) {
11257529SSriharsha.Basavapatna@Sun.COM 		RW_EXIT(&vnetp->vrwlock);
11267529SSriharsha.Basavapatna@Sun.COM 		cmn_err(CE_NOTE,
11277529SSriharsha.Basavapatna@Sun.COM 		    "!vnet%d: Unable to update mtu with mac layer\n",
11287529SSriharsha.Basavapatna@Sun.COM 		    vnetp->instance);
11297529SSriharsha.Basavapatna@Sun.COM 		return (EIO);
11307529SSriharsha.Basavapatna@Sun.COM 	}
11317529SSriharsha.Basavapatna@Sun.COM 
11327529SSriharsha.Basavapatna@Sun.COM 	vnetp->mtu = mtu;
11337529SSriharsha.Basavapatna@Sun.COM 
11347529SSriharsha.Basavapatna@Sun.COM 	RW_EXIT(&vnetp->vrwlock);
11357529SSriharsha.Basavapatna@Sun.COM 
11367529SSriharsha.Basavapatna@Sun.COM 	return (0);
11377529SSriharsha.Basavapatna@Sun.COM }
11387529SSriharsha.Basavapatna@Sun.COM 
11397529SSriharsha.Basavapatna@Sun.COM /*
11406495Sspeer  * vio_net_resource_reg -- An interface called to register a resource
11416495Sspeer  *	with vnet.
11426495Sspeer  *	macp -- a GLDv3 mac_register that has all the details of
11436495Sspeer  *		a resource and its callbacks etc.
11446495Sspeer  *	type -- resource type.
11456495Sspeer  *	local_macaddr -- resource's MAC address. This is used to
11466495Sspeer  *			 associate a resource with a corresponding vnet.
11476495Sspeer  *	remote_macaddr -- remote side MAC address. This is ignored for
11486495Sspeer  *			  the Hybrid resources.
11496495Sspeer  *	vhp -- A handle returned to the caller.
11506495Sspeer  *	vcb -- A set of callbacks provided to the callers.
11516495Sspeer  */
11526495Sspeer int vio_net_resource_reg(mac_register_t *macp, vio_net_res_type_t type,
11536495Sspeer     ether_addr_t local_macaddr, ether_addr_t rem_macaddr, vio_net_handle_t *vhp,
11546495Sspeer     vio_net_callbacks_t *vcb)
11556495Sspeer {
11566495Sspeer 	vnet_t	*vnetp;
11576495Sspeer 	vnet_res_t *vresp;
11586495Sspeer 
11596495Sspeer 	vresp = kmem_zalloc(sizeof (vnet_res_t), KM_SLEEP);
11606495Sspeer 	ether_copy(local_macaddr, vresp->local_macaddr);
11616495Sspeer 	ether_copy(rem_macaddr, vresp->rem_macaddr);
11626495Sspeer 	vresp->type = type;
11636495Sspeer 	bcopy(macp, &vresp->macreg, sizeof (mac_register_t));
11646495Sspeer 
11656495Sspeer 	DBG1(NULL, "Resource Registerig type=0%X\n", type);
11666495Sspeer 
11676495Sspeer 	READ_ENTER(&vnet_rw);
11686495Sspeer 	vnetp = vnet_headp;
11696495Sspeer 	while (vnetp != NULL) {
11706495Sspeer 		if (VNET_MATCH_RES(vresp, vnetp)) {
11716495Sspeer 			WRITE_ENTER(&vnetp->vrwlock);
11726495Sspeer 			vresp->vnetp = vnetp;
11736495Sspeer 			vresp->nextp = vnetp->vres_list;
11746495Sspeer 			vnetp->vres_list = vresp;
11756495Sspeer 			RW_EXIT(&vnetp->vrwlock);
11766495Sspeer 			break;
11776495Sspeer 		}
11786495Sspeer 		vnetp = vnetp->nextp;
11796495Sspeer 	}
11806495Sspeer 	RW_EXIT(&vnet_rw);
11816495Sspeer 	if (vresp->vnetp == NULL) {
11826495Sspeer 		DWARN(NULL, "No vnet instance");
11836495Sspeer 		kmem_free(vresp, sizeof (vnet_res_t));
11846495Sspeer 		return (ENXIO);
11856495Sspeer 	}
11866495Sspeer 
1187*8160SWentao.Yang@Sun.COM 	/* Setup kstats for hio resource */
1188*8160SWentao.Yang@Sun.COM 	if (vresp->type == VIO_NET_RES_HYBRID) {
1189*8160SWentao.Yang@Sun.COM 		vresp->ksp = vnet_hio_setup_kstats(DRV_NAME, "hio", vresp);
1190*8160SWentao.Yang@Sun.COM 		if (vresp->ksp == NULL) {
1191*8160SWentao.Yang@Sun.COM 			DWARN(NULL, "Cannot create kstats for hio resource");
1192*8160SWentao.Yang@Sun.COM 			kmem_free(vresp, sizeof (vnet_res_t));
1193*8160SWentao.Yang@Sun.COM 			return (ENXIO);
1194*8160SWentao.Yang@Sun.COM 		}
1195*8160SWentao.Yang@Sun.COM 	}
1196*8160SWentao.Yang@Sun.COM 
11976495Sspeer 	*vhp = vresp;
11986495Sspeer 	vcb->vio_net_rx_cb = vnet_rx;
11996495Sspeer 	vcb->vio_net_tx_update = vnet_tx_update;
12006495Sspeer 	vcb->vio_net_report_err = vnet_handle_res_err;
12016495Sspeer 
12026495Sspeer 	/* Dispatch a task to start resources */
12036495Sspeer 	vnet_dispatch_res_task(vnetp);
12046495Sspeer 	return (0);
12056495Sspeer }
12066495Sspeer 
12076495Sspeer /*
12086495Sspeer  * vio_net_resource_unreg -- An interface to unregister a resource.
12096495Sspeer  */
12106495Sspeer void
12116495Sspeer vio_net_resource_unreg(vio_net_handle_t vhp)
12126495Sspeer {
12136495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vhp;
12146495Sspeer 	vnet_t *vnetp = vresp->vnetp;
12156495Sspeer 	vnet_res_t *vrp;
1216*8160SWentao.Yang@Sun.COM 	kstat_t *ksp = NULL;
12176495Sspeer 
12186495Sspeer 	DBG1(NULL, "Resource Registerig hdl=0x%p", vhp);
12196495Sspeer 
12206495Sspeer 	ASSERT(vnetp != NULL);
12216495Sspeer 	vnet_fdbe_del(vnetp, vresp);
12226495Sspeer 
12236495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
12246495Sspeer 	if (vresp == vnetp->vres_list) {
12256495Sspeer 		vnetp->vres_list = vresp->nextp;
12266495Sspeer 	} else {
12276495Sspeer 		vrp = vnetp->vres_list;
12286495Sspeer 		while (vrp->nextp != NULL) {
12296495Sspeer 			if (vrp->nextp == vresp) {
12306495Sspeer 				vrp->nextp = vresp->nextp;
12316495Sspeer 				break;
12326495Sspeer 			}
12336495Sspeer 			vrp = vrp->nextp;
12346495Sspeer 		}
12356495Sspeer 	}
1236*8160SWentao.Yang@Sun.COM 
1237*8160SWentao.Yang@Sun.COM 	ksp = vresp->ksp;
1238*8160SWentao.Yang@Sun.COM 	vresp->ksp = NULL;
1239*8160SWentao.Yang@Sun.COM 
12406495Sspeer 	vresp->vnetp = NULL;
12416495Sspeer 	vresp->nextp = NULL;
12426495Sspeer 	RW_EXIT(&vnetp->vrwlock);
1243*8160SWentao.Yang@Sun.COM 	vnet_hio_destroy_kstats(ksp);
12446495Sspeer 	KMEM_FREE(vresp);
12456495Sspeer }
12466495Sspeer 
12476495Sspeer /*
12486495Sspeer  * vnet_dds_rx -- an interface called by vgen to DDS messages.
12496495Sspeer  */
12506495Sspeer void
12516495Sspeer vnet_dds_rx(void *arg, void *dmsg)
12522311Sseb {
12532311Sseb 	vnet_t *vnetp = arg;
12546495Sspeer 	vdds_process_dds_msg(vnetp, dmsg);
12552311Sseb }
12562311Sseb 
12576495Sspeer /*
12586495Sspeer  * vnet_send_dds_msg -- An interface provided to DDS to send
12596495Sspeer  *	DDS messages. This simply sends meessages via vgen.
12606495Sspeer  */
12616495Sspeer int
12626495Sspeer vnet_send_dds_msg(vnet_t *vnetp, void *dmsg)
12636495Sspeer {
12646495Sspeer 	int rv;
12656495Sspeer 
12666495Sspeer 	if (vnetp->vgenhdl != NULL) {
12676495Sspeer 		rv = vgen_dds_tx(vnetp->vgenhdl, dmsg);
12686495Sspeer 	}
12696495Sspeer 	return (rv);
12706495Sspeer }
12716495Sspeer 
12726495Sspeer /*
12736495Sspeer  * vnet_handle_res_err -- A callback function called by a resource
12746495Sspeer  *	to report an error. For example, vgen can call to report
12756495Sspeer  *	an LDC down/reset event. This will trigger cleanup of associated
12766495Sspeer  *	Hybrid resource.
12776495Sspeer  */
12786495Sspeer /* ARGSUSED */
12796495Sspeer static void
12806495Sspeer vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err)
12816495Sspeer {
12826495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vrh;
12836495Sspeer 	vnet_t *vnetp = vresp->vnetp;
12847819SRaghuram.Kothakota@Sun.COM 	int rv;
12856495Sspeer 
12866495Sspeer 	if (vnetp == NULL) {
12876495Sspeer 		return;
12886495Sspeer 	}
12896495Sspeer 	if ((vresp->type != VIO_NET_RES_LDC_SERVICE) &&
12906495Sspeer 	    (vresp->type != VIO_NET_RES_HYBRID)) {
12916495Sspeer 		return;
12926495Sspeer 	}
12937819SRaghuram.Kothakota@Sun.COM 	rv = ddi_taskq_dispatch(vnetp->taskqp, vdds_cleanup_hybrid_res,
12947819SRaghuram.Kothakota@Sun.COM 	    vnetp, DDI_NOSLEEP);
12957819SRaghuram.Kothakota@Sun.COM 	if (rv != DDI_SUCCESS) {
12967819SRaghuram.Kothakota@Sun.COM 		cmn_err(CE_WARN,
12977819SRaghuram.Kothakota@Sun.COM 		    "vnet%d:Failed to dispatch task to cleanup hybrid resource",
12987819SRaghuram.Kothakota@Sun.COM 		    vnetp->instance);
12997819SRaghuram.Kothakota@Sun.COM 	}
13006495Sspeer }
13016495Sspeer 
13026495Sspeer /*
13036495Sspeer  * vnet_dispatch_res_task -- A function to dispatch tasks start resources.
13046495Sspeer  */
13056495Sspeer static void
13066495Sspeer vnet_dispatch_res_task(vnet_t *vnetp)
13076495Sspeer {
13086495Sspeer 	int rv;
13096495Sspeer 
13106495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
13116495Sspeer 	if (vnetp->flags & VNET_STARTED) {
13126495Sspeer 		rv = ddi_taskq_dispatch(vnetp->taskqp, vnet_res_start_task,
13136495Sspeer 		    vnetp, DDI_NOSLEEP);
13146495Sspeer 		if (rv != DDI_SUCCESS) {
13156495Sspeer 			cmn_err(CE_WARN,
13166495Sspeer 			    "vnet%d:Can't dispatch start resource task",
13176495Sspeer 			    vnetp->instance);
13186495Sspeer 		}
13196495Sspeer 	}
13206495Sspeer 	RW_EXIT(&vnetp->vrwlock);
13216495Sspeer }
13226495Sspeer 
13236495Sspeer /*
13246495Sspeer  * vnet_res_start_task -- A taskq callback function that starts a resource.
13256495Sspeer  */
13266495Sspeer static void
13276495Sspeer vnet_res_start_task(void *arg)
13282311Sseb {
13292311Sseb 	vnet_t *vnetp = arg;
13306495Sspeer 
13316495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
13326495Sspeer 	if (vnetp->flags & VNET_STARTED) {
13336495Sspeer 		vnet_start_resources(vnetp);
13346495Sspeer 	}
13356495Sspeer 	RW_EXIT(&vnetp->vrwlock);
13362311Sseb }
13376495Sspeer 
13386495Sspeer /*
13396495Sspeer  * vnet_start_resources -- starts all resources associated with
13406495Sspeer  *	a vnet.
13416495Sspeer  */
13426495Sspeer static void
13436495Sspeer vnet_start_resources(vnet_t *vnetp)
13446495Sspeer {
13456495Sspeer 	mac_register_t	*macp;
13466495Sspeer 	mac_callbacks_t	*cbp;
13476495Sspeer 	vnet_res_t	*vresp;
13486495Sspeer 	int rv;
13496495Sspeer 
13506495Sspeer 	DBG1(vnetp, "enter\n");
13516495Sspeer 
13526495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
13536495Sspeer 		/* skip if it is already started */
13546495Sspeer 		if (vresp->flags & VNET_STARTED) {
13556495Sspeer 			continue;
13566495Sspeer 		}
13576495Sspeer 		macp = &vresp->macreg;
13586495Sspeer 		cbp = macp->m_callbacks;
13596495Sspeer 		rv = cbp->mc_start(macp->m_driver);
13606495Sspeer 		if (rv == 0) {
13616495Sspeer 			/*
13626495Sspeer 			 * Successfully started the resource, so now
13636495Sspeer 			 * add it to the fdb.
13646495Sspeer 			 */
13656495Sspeer 			vresp->flags |= VNET_STARTED;
13666495Sspeer 			vnet_fdbe_add(vnetp, vresp);
13676495Sspeer 		}
13686495Sspeer 	}
13696495Sspeer 
13706495Sspeer 	DBG1(vnetp, "exit\n");
13716495Sspeer 
13726495Sspeer }
13736495Sspeer 
13746495Sspeer /*
13756495Sspeer  * vnet_stop_resources -- stop all resources associated with a vnet.
13766495Sspeer  */
13776495Sspeer static void
13786495Sspeer vnet_stop_resources(vnet_t *vnetp)
13796495Sspeer {
13806495Sspeer 	vnet_res_t	*vresp;
13816495Sspeer 	vnet_res_t	*nvresp;
13826495Sspeer 	mac_register_t	*macp;
13836495Sspeer 	mac_callbacks_t	*cbp;
13846495Sspeer 
13856495Sspeer 	DBG1(vnetp, "enter\n");
13866495Sspeer 
13876495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; ) {
13886495Sspeer 		nvresp = vresp->nextp;
13896495Sspeer 		if (vresp->flags & VNET_STARTED) {
13906495Sspeer 			macp = &vresp->macreg;
13916495Sspeer 			cbp = macp->m_callbacks;
13926495Sspeer 			cbp->mc_stop(macp->m_driver);
13936495Sspeer 			vresp->flags &= ~VNET_STARTED;
13946495Sspeer 		}
13956495Sspeer 		vresp = nvresp;
13966495Sspeer 	}
13976495Sspeer 	DBG1(vnetp, "exit\n");
13986495Sspeer }
1399*8160SWentao.Yang@Sun.COM 
1400*8160SWentao.Yang@Sun.COM /*
1401*8160SWentao.Yang@Sun.COM  * Setup kstats for the HIO statistics.
1402*8160SWentao.Yang@Sun.COM  * NOTE: the synchronization for the statistics is the
1403*8160SWentao.Yang@Sun.COM  * responsibility of the caller.
1404*8160SWentao.Yang@Sun.COM  */
1405*8160SWentao.Yang@Sun.COM kstat_t *
1406*8160SWentao.Yang@Sun.COM vnet_hio_setup_kstats(char *ks_mod, char *ks_name, vnet_res_t *vresp)
1407*8160SWentao.Yang@Sun.COM {
1408*8160SWentao.Yang@Sun.COM 	kstat_t *ksp;
1409*8160SWentao.Yang@Sun.COM 	vnet_t *vnetp = vresp->vnetp;
1410*8160SWentao.Yang@Sun.COM 	vnet_hio_kstats_t *hiokp;
1411*8160SWentao.Yang@Sun.COM 	size_t size;
1412*8160SWentao.Yang@Sun.COM 
1413*8160SWentao.Yang@Sun.COM 	ASSERT(vnetp != NULL);
1414*8160SWentao.Yang@Sun.COM 	size = sizeof (vnet_hio_kstats_t) / sizeof (kstat_named_t);
1415*8160SWentao.Yang@Sun.COM 	ksp = kstat_create(ks_mod, vnetp->instance, ks_name, "net",
1416*8160SWentao.Yang@Sun.COM 	    KSTAT_TYPE_NAMED, size, 0);
1417*8160SWentao.Yang@Sun.COM 	if (ksp == NULL) {
1418*8160SWentao.Yang@Sun.COM 		return (NULL);
1419*8160SWentao.Yang@Sun.COM 	}
1420*8160SWentao.Yang@Sun.COM 
1421*8160SWentao.Yang@Sun.COM 	hiokp = (vnet_hio_kstats_t *)ksp->ks_data;
1422*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->ipackets,		"ipackets",
1423*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1424*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->ierrors,		"ierrors",
1425*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1426*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->opackets,		"opackets",
1427*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1428*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->oerrors,		"oerrors",
1429*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1430*8160SWentao.Yang@Sun.COM 
1431*8160SWentao.Yang@Sun.COM 
1432*8160SWentao.Yang@Sun.COM 	/* MIB II kstat variables */
1433*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->rbytes,		"rbytes",
1434*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1435*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->obytes,		"obytes",
1436*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1437*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->multircv,		"multircv",
1438*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1439*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->multixmt,		"multixmt",
1440*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1441*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->brdcstrcv,		"brdcstrcv",
1442*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1443*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->brdcstxmt,		"brdcstxmt",
1444*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1445*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->norcvbuf,		"norcvbuf",
1446*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1447*8160SWentao.Yang@Sun.COM 	kstat_named_init(&hiokp->noxmtbuf,		"noxmtbuf",
1448*8160SWentao.Yang@Sun.COM 	    KSTAT_DATA_ULONG);
1449*8160SWentao.Yang@Sun.COM 
1450*8160SWentao.Yang@Sun.COM 	ksp->ks_update = vnet_hio_update_kstats;
1451*8160SWentao.Yang@Sun.COM 	ksp->ks_private = (void *)vresp;
1452*8160SWentao.Yang@Sun.COM 	kstat_install(ksp);
1453*8160SWentao.Yang@Sun.COM 	return (ksp);
1454*8160SWentao.Yang@Sun.COM }
1455*8160SWentao.Yang@Sun.COM 
1456*8160SWentao.Yang@Sun.COM /*
1457*8160SWentao.Yang@Sun.COM  * Destroy kstats.
1458*8160SWentao.Yang@Sun.COM  */
1459*8160SWentao.Yang@Sun.COM static void
1460*8160SWentao.Yang@Sun.COM vnet_hio_destroy_kstats(kstat_t *ksp)
1461*8160SWentao.Yang@Sun.COM {
1462*8160SWentao.Yang@Sun.COM 	if (ksp != NULL)
1463*8160SWentao.Yang@Sun.COM 		kstat_delete(ksp);
1464*8160SWentao.Yang@Sun.COM }
1465*8160SWentao.Yang@Sun.COM 
1466*8160SWentao.Yang@Sun.COM /*
1467*8160SWentao.Yang@Sun.COM  * Update the kstats.
1468*8160SWentao.Yang@Sun.COM  */
1469*8160SWentao.Yang@Sun.COM static int
1470*8160SWentao.Yang@Sun.COM vnet_hio_update_kstats(kstat_t *ksp, int rw)
1471*8160SWentao.Yang@Sun.COM {
1472*8160SWentao.Yang@Sun.COM 	vnet_t *vnetp;
1473*8160SWentao.Yang@Sun.COM 	vnet_res_t *vresp;
1474*8160SWentao.Yang@Sun.COM 	vnet_hio_stats_t statsp;
1475*8160SWentao.Yang@Sun.COM 	vnet_hio_kstats_t *hiokp;
1476*8160SWentao.Yang@Sun.COM 
1477*8160SWentao.Yang@Sun.COM 	vresp = (vnet_res_t *)ksp->ks_private;
1478*8160SWentao.Yang@Sun.COM 	vnetp = vresp->vnetp;
1479*8160SWentao.Yang@Sun.COM 
1480*8160SWentao.Yang@Sun.COM 	bzero(&statsp, sizeof (vnet_hio_stats_t));
1481*8160SWentao.Yang@Sun.COM 
1482*8160SWentao.Yang@Sun.COM 	READ_ENTER(&vnetp->vsw_fp_rw);
1483*8160SWentao.Yang@Sun.COM 	if (vnetp->hio_fp == NULL) {
1484*8160SWentao.Yang@Sun.COM 		/* not using hio resources, just return */
1485*8160SWentao.Yang@Sun.COM 		RW_EXIT(&vnetp->vsw_fp_rw);
1486*8160SWentao.Yang@Sun.COM 		return (0);
1487*8160SWentao.Yang@Sun.COM 	}
1488*8160SWentao.Yang@Sun.COM 	VNET_FDBE_REFHOLD(vnetp->hio_fp);
1489*8160SWentao.Yang@Sun.COM 	RW_EXIT(&vnetp->vsw_fp_rw);
1490*8160SWentao.Yang@Sun.COM 	vnet_hio_get_stats(vnetp->hio_fp, &statsp);
1491*8160SWentao.Yang@Sun.COM 	VNET_FDBE_REFRELE(vnetp->hio_fp);
1492*8160SWentao.Yang@Sun.COM 
1493*8160SWentao.Yang@Sun.COM 	hiokp = (vnet_hio_kstats_t *)ksp->ks_data;
1494*8160SWentao.Yang@Sun.COM 
1495*8160SWentao.Yang@Sun.COM 	if (rw == KSTAT_READ) {
1496*8160SWentao.Yang@Sun.COM 		/* Link Input/Output stats */
1497*8160SWentao.Yang@Sun.COM 		hiokp->ipackets.value.ul	= (uint32_t)statsp.ipackets;
1498*8160SWentao.Yang@Sun.COM 		hiokp->ipackets64.value.ull	= statsp.ipackets;
1499*8160SWentao.Yang@Sun.COM 		hiokp->ierrors.value.ul		= statsp.ierrors;
1500*8160SWentao.Yang@Sun.COM 		hiokp->opackets.value.ul	= (uint32_t)statsp.opackets;
1501*8160SWentao.Yang@Sun.COM 		hiokp->opackets64.value.ull	= statsp.opackets;
1502*8160SWentao.Yang@Sun.COM 		hiokp->oerrors.value.ul		= statsp.oerrors;
1503*8160SWentao.Yang@Sun.COM 
1504*8160SWentao.Yang@Sun.COM 		/* MIB II kstat variables */
1505*8160SWentao.Yang@Sun.COM 		hiokp->rbytes.value.ul		= (uint32_t)statsp.rbytes;
1506*8160SWentao.Yang@Sun.COM 		hiokp->rbytes64.value.ull	= statsp.rbytes;
1507*8160SWentao.Yang@Sun.COM 		hiokp->obytes.value.ul		= (uint32_t)statsp.obytes;
1508*8160SWentao.Yang@Sun.COM 		hiokp->obytes64.value.ull	= statsp.obytes;
1509*8160SWentao.Yang@Sun.COM 		hiokp->multircv.value.ul	= statsp.multircv;
1510*8160SWentao.Yang@Sun.COM 		hiokp->multixmt.value.ul	= statsp.multixmt;
1511*8160SWentao.Yang@Sun.COM 		hiokp->brdcstrcv.value.ul	= statsp.brdcstrcv;
1512*8160SWentao.Yang@Sun.COM 		hiokp->brdcstxmt.value.ul	= statsp.brdcstxmt;
1513*8160SWentao.Yang@Sun.COM 		hiokp->norcvbuf.value.ul	= statsp.norcvbuf;
1514*8160SWentao.Yang@Sun.COM 		hiokp->noxmtbuf.value.ul	= statsp.noxmtbuf;
1515*8160SWentao.Yang@Sun.COM 	} else {
1516*8160SWentao.Yang@Sun.COM 		return (EACCES);
1517*8160SWentao.Yang@Sun.COM 	}
1518*8160SWentao.Yang@Sun.COM 
1519*8160SWentao.Yang@Sun.COM 	return (0);
1520*8160SWentao.Yang@Sun.COM }
1521*8160SWentao.Yang@Sun.COM 
1522*8160SWentao.Yang@Sun.COM static void
1523*8160SWentao.Yang@Sun.COM vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp)
1524*8160SWentao.Yang@Sun.COM {
1525*8160SWentao.Yang@Sun.COM 	mac_register_t		*macp;
1526*8160SWentao.Yang@Sun.COM 	mac_callbacks_t		*cbp;
1527*8160SWentao.Yang@Sun.COM 	uint64_t		val;
1528*8160SWentao.Yang@Sun.COM 	int			stat;
1529*8160SWentao.Yang@Sun.COM 
1530*8160SWentao.Yang@Sun.COM 	/*
1531*8160SWentao.Yang@Sun.COM 	 * get the specified statistics from the underlying nxge.
1532*8160SWentao.Yang@Sun.COM 	 */
1533*8160SWentao.Yang@Sun.COM 	macp = &vresp->macreg;
1534*8160SWentao.Yang@Sun.COM 	cbp = macp->m_callbacks;
1535*8160SWentao.Yang@Sun.COM 	for (stat = MAC_STAT_MIN; stat < MAC_STAT_OVERFLOWS; stat++) {
1536*8160SWentao.Yang@Sun.COM 		if (cbp->mc_getstat(macp->m_driver, stat, &val) == 0) {
1537*8160SWentao.Yang@Sun.COM 			switch (stat) {
1538*8160SWentao.Yang@Sun.COM 			case MAC_STAT_IPACKETS:
1539*8160SWentao.Yang@Sun.COM 				statsp->ipackets = val;
1540*8160SWentao.Yang@Sun.COM 				break;
1541*8160SWentao.Yang@Sun.COM 
1542*8160SWentao.Yang@Sun.COM 			case MAC_STAT_IERRORS:
1543*8160SWentao.Yang@Sun.COM 				statsp->ierrors = val;
1544*8160SWentao.Yang@Sun.COM 				break;
1545*8160SWentao.Yang@Sun.COM 
1546*8160SWentao.Yang@Sun.COM 			case MAC_STAT_OPACKETS:
1547*8160SWentao.Yang@Sun.COM 				statsp->opackets = val;
1548*8160SWentao.Yang@Sun.COM 				break;
1549*8160SWentao.Yang@Sun.COM 
1550*8160SWentao.Yang@Sun.COM 			case MAC_STAT_OERRORS:
1551*8160SWentao.Yang@Sun.COM 				statsp->oerrors = val;
1552*8160SWentao.Yang@Sun.COM 				break;
1553*8160SWentao.Yang@Sun.COM 
1554*8160SWentao.Yang@Sun.COM 			case MAC_STAT_RBYTES:
1555*8160SWentao.Yang@Sun.COM 				statsp->rbytes = val;
1556*8160SWentao.Yang@Sun.COM 				break;
1557*8160SWentao.Yang@Sun.COM 
1558*8160SWentao.Yang@Sun.COM 			case MAC_STAT_OBYTES:
1559*8160SWentao.Yang@Sun.COM 				statsp->obytes = val;
1560*8160SWentao.Yang@Sun.COM 				break;
1561*8160SWentao.Yang@Sun.COM 
1562*8160SWentao.Yang@Sun.COM 			case MAC_STAT_MULTIRCV:
1563*8160SWentao.Yang@Sun.COM 				statsp->multircv = val;
1564*8160SWentao.Yang@Sun.COM 				break;
1565*8160SWentao.Yang@Sun.COM 
1566*8160SWentao.Yang@Sun.COM 			case MAC_STAT_MULTIXMT:
1567*8160SWentao.Yang@Sun.COM 				statsp->multixmt = val;
1568*8160SWentao.Yang@Sun.COM 				break;
1569*8160SWentao.Yang@Sun.COM 
1570*8160SWentao.Yang@Sun.COM 			case MAC_STAT_BRDCSTRCV:
1571*8160SWentao.Yang@Sun.COM 				statsp->brdcstrcv = val;
1572*8160SWentao.Yang@Sun.COM 				break;
1573*8160SWentao.Yang@Sun.COM 
1574*8160SWentao.Yang@Sun.COM 			case MAC_STAT_BRDCSTXMT:
1575*8160SWentao.Yang@Sun.COM 				statsp->brdcstxmt = val;
1576*8160SWentao.Yang@Sun.COM 				break;
1577*8160SWentao.Yang@Sun.COM 
1578*8160SWentao.Yang@Sun.COM 			case MAC_STAT_NOXMTBUF:
1579*8160SWentao.Yang@Sun.COM 				statsp->noxmtbuf = val;
1580*8160SWentao.Yang@Sun.COM 				break;
1581*8160SWentao.Yang@Sun.COM 
1582*8160SWentao.Yang@Sun.COM 			case MAC_STAT_NORCVBUF:
1583*8160SWentao.Yang@Sun.COM 				statsp->norcvbuf = val;
1584*8160SWentao.Yang@Sun.COM 				break;
1585*8160SWentao.Yang@Sun.COM 
1586*8160SWentao.Yang@Sun.COM 			default:
1587*8160SWentao.Yang@Sun.COM 				/*
1588*8160SWentao.Yang@Sun.COM 				 * parameters not interested.
1589*8160SWentao.Yang@Sun.COM 				 */
1590*8160SWentao.Yang@Sun.COM 				break;
1591*8160SWentao.Yang@Sun.COM 			}
1592*8160SWentao.Yang@Sun.COM 		}
1593*8160SWentao.Yang@Sun.COM 	}
1594*8160SWentao.Yang@Sun.COM }
1595