xref: /onnv-gate/usr/src/uts/sun4v/io/vnet.c (revision 7819:8dc765eacc7e)
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 
876495Sspeer static void vnet_rx(vio_net_handle_t vrh, mblk_t *mp);
886495Sspeer static void vnet_tx_update(vio_net_handle_t vrh);
896495Sspeer static void vnet_res_start_task(void *arg);
906495Sspeer static void vnet_start_resources(vnet_t *vnetp);
916495Sspeer static void vnet_stop_resources(vnet_t *vnetp);
926495Sspeer static void vnet_dispatch_res_task(vnet_t *vnetp);
936495Sspeer static void vnet_res_start_task(void *arg);
946495Sspeer static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err);
957529SSriharsha.Basavapatna@Sun.COM int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu);
966419Ssb155480 
976495Sspeer /* Exported to to vnet_dds */
986495Sspeer int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg);
991991Sheppo 
1006495Sspeer /* Externs that are imported from vnet_gen */
1016495Sspeer extern int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
1026495Sspeer     const uint8_t *macaddr, void **vgenhdl);
1032336Snarayan extern int vgen_uninit(void *arg);
1046495Sspeer extern int vgen_dds_tx(void *arg, void *dmsg);
1056495Sspeer 
1066495Sspeer /* Externs that are imported from vnet_dds */
1076495Sspeer extern void vdds_mod_init(void);
1086495Sspeer extern void vdds_mod_fini(void);
1096495Sspeer extern int vdds_init(vnet_t *vnetp);
1106495Sspeer extern void vdds_cleanup(vnet_t *vnetp);
1116495Sspeer extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg);
112*7819SRaghuram.Kothakota@Sun.COM extern void vdds_cleanup_hybrid_res(void *arg);
1131991Sheppo 
1146419Ssb155480 #define	VNET_FDBE_REFHOLD(p)						\
1156419Ssb155480 {									\
1166419Ssb155480 	atomic_inc_32(&(p)->refcnt);					\
1176419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1186419Ssb155480 }
1196419Ssb155480 
1206419Ssb155480 #define	VNET_FDBE_REFRELE(p)						\
1216419Ssb155480 {									\
1226419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1236419Ssb155480 	atomic_dec_32(&(p)->refcnt);					\
1246419Ssb155480 }
1256419Ssb155480 
1262311Sseb static mac_callbacks_t vnet_m_callbacks = {
1272311Sseb 	0,
1282311Sseb 	vnet_m_stat,
1292311Sseb 	vnet_m_start,
1302311Sseb 	vnet_m_stop,
1312311Sseb 	vnet_m_promisc,
1322311Sseb 	vnet_m_multicst,
1332311Sseb 	vnet_m_unicst,
1342311Sseb 	vnet_m_tx,
1352311Sseb 	NULL,
1362311Sseb 	NULL,
1372311Sseb 	NULL
1382311Sseb };
1392311Sseb 
1401991Sheppo /*
1411991Sheppo  * Linked list of "vnet_t" structures - one per instance.
1421991Sheppo  */
1431991Sheppo static vnet_t	*vnet_headp = NULL;
1441991Sheppo static krwlock_t vnet_rw;
1451991Sheppo 
1461991Sheppo /* Tunables */
1471991Sheppo uint32_t vnet_ntxds = VNET_NTXDS;	/* power of 2 transmit descriptors */
1481991Sheppo uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */
1491991Sheppo uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT;  /* tx timeout in msec */
1502410Slm66018 uint32_t vnet_ldc_mtu = VNET_LDC_MTU;		/* ldc mtu */
1516419Ssb155480 
1527529SSriharsha.Basavapatna@Sun.COM /*
1537529SSriharsha.Basavapatna@Sun.COM  * Set this to non-zero to enable additional internal receive buffer pools
1547529SSriharsha.Basavapatna@Sun.COM  * based on the MTU of the device for better performance at the cost of more
1557529SSriharsha.Basavapatna@Sun.COM  * memory consumption. This is turned off by default, to use allocb(9F) for
1567529SSriharsha.Basavapatna@Sun.COM  * receive buffer allocations of sizes > 2K.
1577529SSriharsha.Basavapatna@Sun.COM  */
1587529SSriharsha.Basavapatna@Sun.COM boolean_t vnet_jumbo_rxpools = B_FALSE;
1597529SSriharsha.Basavapatna@Sun.COM 
1606419Ssb155480 /* # of chains in fdb hash table */
1616419Ssb155480 uint32_t	vnet_fdb_nchains = VNET_NFDB_HASH;
1626419Ssb155480 
1636419Ssb155480 /* Internal tunables */
1646419Ssb155480 uint32_t	vnet_ethermtu = 1500;	/* mtu of the device */
1656419Ssb155480 
1666419Ssb155480 /*
1676419Ssb155480  * Default vlan id. This is only used internally when the "default-vlan-id"
1686419Ssb155480  * property is not present in the MD device node. Therefore, this should not be
1696419Ssb155480  * used as a tunable; if this value is changed, the corresponding variable
1706419Ssb155480  * should be updated to the same value in vsw and also other vnets connected to
1716419Ssb155480  * the same vsw.
1726419Ssb155480  */
1736419Ssb155480 uint16_t	vnet_default_vlan_id = 1;
1746419Ssb155480 
1756419Ssb155480 /* delay in usec to wait for all references on a fdb entry to be dropped */
1766419Ssb155480 uint32_t vnet_fdbe_refcnt_delay = 10;
1771991Sheppo 
1786495Sspeer static struct ether_addr etherbroadcastaddr = {
1796495Sspeer 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1806495Sspeer };
1816495Sspeer 
1826495Sspeer 
1831991Sheppo /*
1841991Sheppo  * Property names
1851991Sheppo  */
1861991Sheppo static char macaddr_propname[] = "local-mac-address";
1871991Sheppo 
1881991Sheppo /*
1891991Sheppo  * This is the string displayed by modinfo(1m).
1901991Sheppo  */
1917529SSriharsha.Basavapatna@Sun.COM static char vnet_ident[] = "vnet driver";
1921991Sheppo extern struct mod_ops mod_driverops;
1931991Sheppo static struct cb_ops cb_vnetops = {
1941991Sheppo 	nulldev,		/* cb_open */
1951991Sheppo 	nulldev,		/* cb_close */
1961991Sheppo 	nodev,			/* cb_strategy */
1971991Sheppo 	nodev,			/* cb_print */
1981991Sheppo 	nodev,			/* cb_dump */
1991991Sheppo 	nodev,			/* cb_read */
2001991Sheppo 	nodev,			/* cb_write */
2011991Sheppo 	nodev,			/* cb_ioctl */
2021991Sheppo 	nodev,			/* cb_devmap */
2031991Sheppo 	nodev,			/* cb_mmap */
2041991Sheppo 	nodev,			/* cb_segmap */
2051991Sheppo 	nochpoll,		/* cb_chpoll */
2061991Sheppo 	ddi_prop_op,		/* cb_prop_op */
2071991Sheppo 	NULL,			/* cb_stream */
2081991Sheppo 	(int)(D_MP)		/* cb_flag */
2091991Sheppo };
2101991Sheppo 
2111991Sheppo static struct dev_ops vnetops = {
2121991Sheppo 	DEVO_REV,		/* devo_rev */
2131991Sheppo 	0,			/* devo_refcnt */
2141991Sheppo 	NULL,			/* devo_getinfo */
2151991Sheppo 	nulldev,		/* devo_identify */
2161991Sheppo 	nulldev,		/* devo_probe */
2171991Sheppo 	vnetattach,		/* devo_attach */
2181991Sheppo 	vnetdetach,		/* devo_detach */
2191991Sheppo 	nodev,			/* devo_reset */
2201991Sheppo 	&cb_vnetops,		/* devo_cb_ops */
2217656SSherry.Moore@Sun.COM 	(struct bus_ops *)NULL,	/* devo_bus_ops */
2227656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
2237656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
2241991Sheppo };
2251991Sheppo 
2261991Sheppo static struct modldrv modldrv = {
2271991Sheppo 	&mod_driverops,		/* Type of module.  This one is a driver */
2281991Sheppo 	vnet_ident,		/* ID string */
2291991Sheppo 	&vnetops		/* driver specific ops */
2301991Sheppo };
2311991Sheppo 
2321991Sheppo static struct modlinkage modlinkage = {
2331991Sheppo 	MODREV_1, (void *)&modldrv, NULL
2341991Sheppo };
2351991Sheppo 
2364647Sraghuram #ifdef DEBUG
2371991Sheppo 
2381991Sheppo /*
2391991Sheppo  * Print debug messages - set to 0xf to enable all msgs
2401991Sheppo  */
2414647Sraghuram int vnet_dbglevel = 0x8;
2421991Sheppo 
2434647Sraghuram static void
2444647Sraghuram debug_printf(const char *fname, void *arg, const char *fmt, ...)
2451991Sheppo {
2461991Sheppo 	char    buf[512];
2471991Sheppo 	va_list ap;
2481991Sheppo 	vnet_t *vnetp = (vnet_t *)arg;
2494647Sraghuram 	char    *bufp = buf;
2501991Sheppo 
2514647Sraghuram 	if (vnetp == NULL) {
2524647Sraghuram 		(void) sprintf(bufp, "%s: ", fname);
2534647Sraghuram 		bufp += strlen(bufp);
2544647Sraghuram 	} else {
2554647Sraghuram 		(void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname);
2564647Sraghuram 		bufp += strlen(bufp);
2574647Sraghuram 	}
2584647Sraghuram 	va_start(ap, fmt);
2594647Sraghuram 	(void) vsprintf(bufp, fmt, ap);
2604647Sraghuram 	va_end(ap);
2614647Sraghuram 	cmn_err(CE_CONT, "%s\n", buf);
2624647Sraghuram }
2631991Sheppo 
2641991Sheppo #endif
2651991Sheppo 
2661991Sheppo /* _init(9E): initialize the loadable module */
2671991Sheppo int
2681991Sheppo _init(void)
2691991Sheppo {
2701991Sheppo 	int status;
2711991Sheppo 
2724647Sraghuram 	DBG1(NULL, "enter\n");
2731991Sheppo 
2741991Sheppo 	mac_init_ops(&vnetops, "vnet");
2751991Sheppo 	status = mod_install(&modlinkage);
2761991Sheppo 	if (status != 0) {
2771991Sheppo 		mac_fini_ops(&vnetops);
2781991Sheppo 	}
2796495Sspeer 	vdds_mod_init();
2804647Sraghuram 	DBG1(NULL, "exit(%d)\n", status);
2811991Sheppo 	return (status);
2821991Sheppo }
2831991Sheppo 
2841991Sheppo /* _fini(9E): prepare the module for unloading. */
2851991Sheppo int
2861991Sheppo _fini(void)
2871991Sheppo {
2881991Sheppo 	int status;
2891991Sheppo 
2904647Sraghuram 	DBG1(NULL, "enter\n");
2911991Sheppo 
2921991Sheppo 	status = mod_remove(&modlinkage);
2931991Sheppo 	if (status != 0)
2941991Sheppo 		return (status);
2951991Sheppo 	mac_fini_ops(&vnetops);
2966495Sspeer 	vdds_mod_fini();
2971991Sheppo 
2984647Sraghuram 	DBG1(NULL, "exit(%d)\n", status);
2991991Sheppo 	return (status);
3001991Sheppo }
3011991Sheppo 
3021991Sheppo /* _info(9E): return information about the loadable module */
3031991Sheppo int
3041991Sheppo _info(struct modinfo *modinfop)
3051991Sheppo {
3061991Sheppo 	return (mod_info(&modlinkage, modinfop));
3071991Sheppo }
3081991Sheppo 
3091991Sheppo /*
3101991Sheppo  * attach(9E): attach a device to the system.
3111991Sheppo  * called once for each instance of the device on the system.
3121991Sheppo  */
3131991Sheppo static int
3141991Sheppo vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3151991Sheppo {
3161991Sheppo 	vnet_t		*vnetp;
3176495Sspeer 	int		status;
3181991Sheppo 	int		instance;
3196495Sspeer 	uint64_t	reg;
3206495Sspeer 	char		qname[TASKQ_NAMELEN];
3214647Sraghuram 	enum	{ AST_init = 0x0, AST_vnet_alloc = 0x1,
3224647Sraghuram 		AST_mac_alloc = 0x2, AST_read_macaddr = 0x4,
3236495Sspeer 		AST_vgen_init = 0x8, AST_fdbh_alloc = 0x10,
3246495Sspeer 		AST_vdds_init = 0x20, AST_taskq_create = 0x40,
3256495Sspeer 		AST_vnet_list = 0x80 } attach_state;
3261991Sheppo 
3271991Sheppo 	attach_state = AST_init;
3281991Sheppo 
3291991Sheppo 	switch (cmd) {
3301991Sheppo 	case DDI_ATTACH:
3311991Sheppo 		break;
3321991Sheppo 	case DDI_RESUME:
3331991Sheppo 	case DDI_PM_RESUME:
3341991Sheppo 	default:
3351991Sheppo 		goto vnet_attach_fail;
3361991Sheppo 	}
3371991Sheppo 
3381991Sheppo 	instance = ddi_get_instance(dip);
3394647Sraghuram 	DBG1(NULL, "instance(%d) enter\n", instance);
3401991Sheppo 
3411991Sheppo 	/* allocate vnet_t and mac_t structures */
3421991Sheppo 	vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP);
3436495Sspeer 	vnetp->dip = dip;
3446495Sspeer 	vnetp->instance = instance;
3456495Sspeer 	rw_init(&vnetp->vrwlock, NULL, RW_DRIVER, NULL);
3466495Sspeer 	rw_init(&vnetp->vsw_fp_rw, NULL, RW_DRIVER, NULL);
3471991Sheppo 	attach_state |= AST_vnet_alloc;
3481991Sheppo 
3496495Sspeer 	status = vdds_init(vnetp);
3506495Sspeer 	if (status != 0) {
3516495Sspeer 		goto vnet_attach_fail;
3526495Sspeer 	}
3536495Sspeer 	attach_state |= AST_vdds_init;
3546495Sspeer 
3551991Sheppo 	/* setup links to vnet_t from both devinfo and mac_t */
3561991Sheppo 	ddi_set_driver_private(dip, (caddr_t)vnetp);
3571991Sheppo 
3581991Sheppo 	/* read the mac address */
3591991Sheppo 	status = vnet_read_mac_address(vnetp);
3601991Sheppo 	if (status != DDI_SUCCESS) {
3611991Sheppo 		goto vnet_attach_fail;
3621991Sheppo 	}
3631991Sheppo 	attach_state |= AST_read_macaddr;
3641991Sheppo 
3656495Sspeer 	reg = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
3666495Sspeer 	    DDI_PROP_DONTPASS, "reg", -1);
3676495Sspeer 	if (reg == -1) {
3686495Sspeer 		goto vnet_attach_fail;
3696495Sspeer 	}
3706495Sspeer 	vnetp->reg = reg;
3716495Sspeer 
3726495Sspeer 	vnet_fdb_create(vnetp);
3736495Sspeer 	attach_state |= AST_fdbh_alloc;
3746495Sspeer 
3756495Sspeer 	(void) snprintf(qname, TASKQ_NAMELEN, "vnet_taskq%d", instance);
3766495Sspeer 	if ((vnetp->taskqp = ddi_taskq_create(dip, qname, 1,
3776495Sspeer 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
3786495Sspeer 		cmn_err(CE_WARN, "!vnet%d: Unable to create task queue",
3796495Sspeer 		    instance);
3806495Sspeer 		goto vnet_attach_fail;
3816495Sspeer 	}
3826495Sspeer 	attach_state |= AST_taskq_create;
3836495Sspeer 
3846495Sspeer 	/* add to the list of vnet devices */
3856495Sspeer 	WRITE_ENTER(&vnet_rw);
3866495Sspeer 	vnetp->nextp = vnet_headp;
3876495Sspeer 	vnet_headp = vnetp;
3886495Sspeer 	RW_EXIT(&vnet_rw);
3896495Sspeer 
3906495Sspeer 	attach_state |= AST_vnet_list;
3916495Sspeer 
3921991Sheppo 	/*
3936495Sspeer 	 * Initialize the generic vnet plugin which provides
3946495Sspeer 	 * communication via sun4v LDC (logical domain channel) based
3956495Sspeer 	 * resources. It will register the LDC resources as and when
3966495Sspeer 	 * they become available.
3971991Sheppo 	 */
3986495Sspeer 	status = vgen_init(vnetp, reg, vnetp->dip,
3996495Sspeer 	    (uint8_t *)vnetp->curr_macaddr, &vnetp->vgenhdl);
4001991Sheppo 	if (status != DDI_SUCCESS) {
4014647Sraghuram 		DERR(vnetp, "vgen_init() failed\n");
4021991Sheppo 		goto vnet_attach_fail;
4031991Sheppo 	}
4041991Sheppo 	attach_state |= AST_vgen_init;
4051991Sheppo 
4061991Sheppo 	/* register with MAC layer */
4071991Sheppo 	status = vnet_mac_register(vnetp);
4081991Sheppo 	if (status != DDI_SUCCESS) {
4091991Sheppo 		goto vnet_attach_fail;
4101991Sheppo 	}
4111991Sheppo 
4124647Sraghuram 	DBG1(NULL, "instance(%d) exit\n", instance);
4131991Sheppo 	return (DDI_SUCCESS);
4141991Sheppo 
4151991Sheppo vnet_attach_fail:
4166495Sspeer 
4176495Sspeer 	if (attach_state & AST_vnet_list) {
4186495Sspeer 		vnet_t		**vnetpp;
4196495Sspeer 		/* unlink from instance(vnet_t) list */
4206495Sspeer 		WRITE_ENTER(&vnet_rw);
4216495Sspeer 		for (vnetpp = &vnet_headp; *vnetpp;
4226495Sspeer 		    vnetpp = &(*vnetpp)->nextp) {
4236495Sspeer 			if (*vnetpp == vnetp) {
4246495Sspeer 				*vnetpp = vnetp->nextp;
4256495Sspeer 				break;
4266495Sspeer 			}
4276495Sspeer 		}
4286495Sspeer 		RW_EXIT(&vnet_rw);
4296495Sspeer 	}
4306495Sspeer 
4316495Sspeer 	if (attach_state & AST_vdds_init) {
4326495Sspeer 		vdds_cleanup(vnetp);
4336495Sspeer 	}
4346495Sspeer 	if (attach_state & AST_taskq_create) {
4356495Sspeer 		ddi_taskq_destroy(vnetp->taskqp);
4366495Sspeer 	}
4371991Sheppo 	if (attach_state & AST_fdbh_alloc) {
4386419Ssb155480 		vnet_fdb_destroy(vnetp);
4391991Sheppo 	}
4401991Sheppo 	if (attach_state & AST_vgen_init) {
4416495Sspeer 		(void) vgen_uninit(vnetp->vgenhdl);
4421991Sheppo 	}
4431991Sheppo 	if (attach_state & AST_vnet_alloc) {
4446495Sspeer 		rw_destroy(&vnetp->vrwlock);
4456495Sspeer 		rw_destroy(&vnetp->vsw_fp_rw);
4461991Sheppo 		KMEM_FREE(vnetp);
4471991Sheppo 	}
4481991Sheppo 	return (DDI_FAILURE);
4491991Sheppo }
4501991Sheppo 
4511991Sheppo /*
4521991Sheppo  * detach(9E): detach a device from the system.
4531991Sheppo  */
4541991Sheppo static int
4551991Sheppo vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4561991Sheppo {
4571991Sheppo 	vnet_t		*vnetp;
4581991Sheppo 	vnet_t		**vnetpp;
4591991Sheppo 	int		instance;
4602336Snarayan 	int		rv;
4611991Sheppo 
4621991Sheppo 	instance = ddi_get_instance(dip);
4634647Sraghuram 	DBG1(NULL, "instance(%d) enter\n", instance);
4641991Sheppo 
4651991Sheppo 	vnetp = ddi_get_driver_private(dip);
4661991Sheppo 	if (vnetp == NULL) {
4671991Sheppo 		goto vnet_detach_fail;
4681991Sheppo 	}
4691991Sheppo 
4701991Sheppo 	switch (cmd) {
4711991Sheppo 	case DDI_DETACH:
4721991Sheppo 		break;
4731991Sheppo 	case DDI_SUSPEND:
4741991Sheppo 	case DDI_PM_SUSPEND:
4751991Sheppo 	default:
4761991Sheppo 		goto vnet_detach_fail;
4771991Sheppo 	}
4781991Sheppo 
4796495Sspeer 	(void) vdds_cleanup(vnetp);
4806495Sspeer 	rv = vgen_uninit(vnetp->vgenhdl);
4816495Sspeer 	if (rv != DDI_SUCCESS) {
4826495Sspeer 		goto vnet_detach_fail;
4832336Snarayan 	}
4842336Snarayan 
4851991Sheppo 	/*
4861991Sheppo 	 * Unregister from the MAC subsystem.  This can fail, in
4871991Sheppo 	 * particular if there are DLPI style-2 streams still open -
4881991Sheppo 	 * in which case we just return failure.
4891991Sheppo 	 */
4902311Sseb 	if (mac_unregister(vnetp->mh) != 0)
4911991Sheppo 		goto vnet_detach_fail;
4921991Sheppo 
4931991Sheppo 	/* unlink from instance(vnet_t) list */
4941991Sheppo 	WRITE_ENTER(&vnet_rw);
4951991Sheppo 	for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) {
4961991Sheppo 		if (*vnetpp == vnetp) {
4971991Sheppo 			*vnetpp = vnetp->nextp;
4981991Sheppo 			break;
4991991Sheppo 		}
5001991Sheppo 	}
5011991Sheppo 	RW_EXIT(&vnet_rw);
5021991Sheppo 
5036495Sspeer 	ddi_taskq_destroy(vnetp->taskqp);
5046419Ssb155480 	/* destroy fdb */
5056419Ssb155480 	vnet_fdb_destroy(vnetp);
5063297Ssb155480 
5076495Sspeer 	rw_destroy(&vnetp->vrwlock);
5086495Sspeer 	rw_destroy(&vnetp->vsw_fp_rw);
5091991Sheppo 	KMEM_FREE(vnetp);
5101991Sheppo 
5111991Sheppo 	return (DDI_SUCCESS);
5121991Sheppo 
5131991Sheppo vnet_detach_fail:
5141991Sheppo 	return (DDI_FAILURE);
5151991Sheppo }
5161991Sheppo 
5171991Sheppo /* enable the device for transmit/receive */
5181991Sheppo static int
5191991Sheppo vnet_m_start(void *arg)
5201991Sheppo {
5211991Sheppo 	vnet_t		*vnetp = arg;
5221991Sheppo 
5234647Sraghuram 	DBG1(vnetp, "enter\n");
5241991Sheppo 
5256495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
5266495Sspeer 	vnetp->flags |= VNET_STARTED;
5276495Sspeer 	vnet_start_resources(vnetp);
5286495Sspeer 	RW_EXIT(&vnetp->vrwlock);
5291991Sheppo 
5304647Sraghuram 	DBG1(vnetp, "exit\n");
5311991Sheppo 	return (VNET_SUCCESS);
5321991Sheppo 
5331991Sheppo }
5341991Sheppo 
5351991Sheppo /* stop transmit/receive for the device */
5361991Sheppo static void
5371991Sheppo vnet_m_stop(void *arg)
5381991Sheppo {
5391991Sheppo 	vnet_t		*vnetp = arg;
5401991Sheppo 
5414647Sraghuram 	DBG1(vnetp, "enter\n");
5421991Sheppo 
5436495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
5446495Sspeer 	if (vnetp->flags & VNET_STARTED) {
5456495Sspeer 		vnet_stop_resources(vnetp);
5466495Sspeer 		vnetp->flags &= ~VNET_STARTED;
5471991Sheppo 	}
5486495Sspeer 	RW_EXIT(&vnetp->vrwlock);
5491991Sheppo 
5504647Sraghuram 	DBG1(vnetp, "exit\n");
5511991Sheppo }
5521991Sheppo 
5531991Sheppo /* set the unicast mac address of the device */
5541991Sheppo static int
5551991Sheppo vnet_m_unicst(void *arg, const uint8_t *macaddr)
5561991Sheppo {
5571991Sheppo 	_NOTE(ARGUNUSED(macaddr))
5581991Sheppo 
5591991Sheppo 	vnet_t *vnetp = arg;
5601991Sheppo 
5614647Sraghuram 	DBG1(vnetp, "enter\n");
5621991Sheppo 	/*
5632793Slm66018 	 * NOTE: setting mac address dynamically is not supported.
5641991Sheppo 	 */
5654647Sraghuram 	DBG1(vnetp, "exit\n");
5661991Sheppo 
5672109Slm66018 	return (VNET_FAILURE);
5681991Sheppo }
5691991Sheppo 
5701991Sheppo /* enable/disable a multicast address */
5711991Sheppo static int
5721991Sheppo vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
5731991Sheppo {
5741991Sheppo 	_NOTE(ARGUNUSED(add, mca))
5751991Sheppo 
5761991Sheppo 	vnet_t *vnetp = arg;
5776495Sspeer 	vnet_res_t	*vresp;
5786495Sspeer 	mac_register_t	*macp;
5792311Sseb 	mac_callbacks_t	*cbp;
5801991Sheppo 	int rv = VNET_SUCCESS;
5811991Sheppo 
5824647Sraghuram 	DBG1(vnetp, "enter\n");
5836495Sspeer 
5846495Sspeer 	READ_ENTER(&vnetp->vrwlock);
5856495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
5866495Sspeer 		if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
5876495Sspeer 			macp = &vresp->macreg;
5886495Sspeer 			cbp = macp->m_callbacks;
5896495Sspeer 			rv = cbp->mc_multicst(macp->m_driver, add, mca);
5901991Sheppo 		}
5911991Sheppo 	}
5926495Sspeer 	RW_EXIT(&vnetp->vrwlock);
5936495Sspeer 
5944647Sraghuram 	DBG1(vnetp, "exit(%d)\n", rv);
5951991Sheppo 	return (rv);
5961991Sheppo }
5971991Sheppo 
5981991Sheppo /* set or clear promiscuous mode on the device */
5991991Sheppo static int
6001991Sheppo vnet_m_promisc(void *arg, boolean_t on)
6011991Sheppo {
6021991Sheppo 	_NOTE(ARGUNUSED(on))
6031991Sheppo 
6041991Sheppo 	vnet_t *vnetp = arg;
6054647Sraghuram 	DBG1(vnetp, "enter\n");
6061991Sheppo 	/*
6072793Slm66018 	 * NOTE: setting promiscuous mode is not supported, just return success.
6081991Sheppo 	 */
6094647Sraghuram 	DBG1(vnetp, "exit\n");
6101991Sheppo 	return (VNET_SUCCESS);
6111991Sheppo }
6121991Sheppo 
6131991Sheppo /*
6141991Sheppo  * Transmit a chain of packets. This function provides switching functionality
6151991Sheppo  * based on the destination mac address to reach other guests (within ldoms) or
6161991Sheppo  * external hosts.
6171991Sheppo  */
6181991Sheppo mblk_t *
6191991Sheppo vnet_m_tx(void *arg, mblk_t *mp)
6201991Sheppo {
6216419Ssb155480 	vnet_t			*vnetp;
6226495Sspeer 	vnet_res_t		*vresp;
6236419Ssb155480 	mblk_t			*next;
6246495Sspeer 	mblk_t			*resid_mp;
6256495Sspeer 	mac_register_t		*macp;
6266495Sspeer 	struct ether_header	*ehp;
6276495Sspeer 	boolean_t		is_unicast;
6281991Sheppo 
6291991Sheppo 	vnetp = (vnet_t *)arg;
6304647Sraghuram 	DBG1(vnetp, "enter\n");
6311991Sheppo 	ASSERT(mp != NULL);
6321991Sheppo 
6331991Sheppo 	while (mp != NULL) {
6346419Ssb155480 
6351991Sheppo 		next = mp->b_next;
6361991Sheppo 		mp->b_next = NULL;
6371991Sheppo 
6386419Ssb155480 		/*
6396419Ssb155480 		 * Find fdb entry for the destination
6406419Ssb155480 		 * and hold a reference to it.
6416419Ssb155480 		 */
6421991Sheppo 		ehp = (struct ether_header *)mp->b_rptr;
6436495Sspeer 		vresp = vnet_fdbe_find(vnetp, &ehp->ether_dhost);
6446495Sspeer 		if (vresp != NULL) {
6451991Sheppo 
6461991Sheppo 			/*
6476419Ssb155480 			 * Destination found in FDB.
6486419Ssb155480 			 * The destination is a vnet device within ldoms
6496419Ssb155480 			 * and directly reachable, invoke the tx function
6506419Ssb155480 			 * in the fdb entry.
6511991Sheppo 			 */
6526495Sspeer 			macp = &vresp->macreg;
6536495Sspeer 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
6546419Ssb155480 
6556419Ssb155480 			/* tx done; now release ref on fdb entry */
6566495Sspeer 			VNET_FDBE_REFRELE(vresp);
6576419Ssb155480 
6581991Sheppo 			if (resid_mp != NULL) {
6591991Sheppo 				/* m_tx failed */
6601991Sheppo 				mp->b_next = next;
6611991Sheppo 				break;
6621991Sheppo 			}
6631991Sheppo 		} else {
6646495Sspeer 			is_unicast = !(IS_BROADCAST(ehp) ||
6656495Sspeer 			    (IS_MULTICAST(ehp)));
6661991Sheppo 			/*
6676419Ssb155480 			 * Destination is not in FDB.
6686495Sspeer 			 * If the destination is broadcast or multicast,
6696495Sspeer 			 * then forward the packet to vswitch.
6706495Sspeer 			 * If a Hybrid resource avilable, then send the
6716495Sspeer 			 * unicast packet via hybrid resource, otherwise
6726495Sspeer 			 * forward it to vswitch.
6731991Sheppo 			 */
6746419Ssb155480 			READ_ENTER(&vnetp->vsw_fp_rw);
6756419Ssb155480 
6766495Sspeer 			if ((is_unicast) && (vnetp->hio_fp != NULL)) {
6776495Sspeer 				vresp = vnetp->hio_fp;
6786495Sspeer 			} else {
6796495Sspeer 				vresp = vnetp->vsw_fp;
6806495Sspeer 			}
6816495Sspeer 			if (vresp == NULL) {
6826419Ssb155480 				/*
6836419Ssb155480 				 * no fdb entry to vsw? drop the packet.
6846419Ssb155480 				 */
6856419Ssb155480 				RW_EXIT(&vnetp->vsw_fp_rw);
6861991Sheppo 				freemsg(mp);
6876419Ssb155480 				mp = next;
6886419Ssb155480 				continue;
6891991Sheppo 			}
6906419Ssb155480 
6916419Ssb155480 			/* ref hold the fdb entry to vsw */
6926495Sspeer 			VNET_FDBE_REFHOLD(vresp);
6936419Ssb155480 
6946419Ssb155480 			RW_EXIT(&vnetp->vsw_fp_rw);
6956419Ssb155480 
6966495Sspeer 			macp = &vresp->macreg;
6976495Sspeer 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
6986419Ssb155480 
6996419Ssb155480 			/* tx done; now release ref on fdb entry */
7006495Sspeer 			VNET_FDBE_REFRELE(vresp);
7016419Ssb155480 
7026419Ssb155480 			if (resid_mp != NULL) {
7036419Ssb155480 				/* m_tx failed */
7046419Ssb155480 				mp->b_next = next;
7056419Ssb155480 				break;
7066419Ssb155480 			}
7071991Sheppo 		}
7081991Sheppo 
7091991Sheppo 		mp = next;
7101991Sheppo 	}
7111991Sheppo 
7124647Sraghuram 	DBG1(vnetp, "exit\n");
7131991Sheppo 	return (mp);
7141991Sheppo }
7151991Sheppo 
7162311Sseb /* get statistics from the device */
7172311Sseb int
7182311Sseb vnet_m_stat(void *arg, uint_t stat, uint64_t *val)
7191991Sheppo {
7201991Sheppo 	vnet_t *vnetp = arg;
7216495Sspeer 	vnet_res_t	*vresp;
7226495Sspeer 	mac_register_t	*macp;
7232311Sseb 	mac_callbacks_t	*cbp;
7242311Sseb 	uint64_t val_total = 0;
7251991Sheppo 
7264647Sraghuram 	DBG1(vnetp, "enter\n");
7271991Sheppo 
7281991Sheppo 	/*
7292311Sseb 	 * get the specified statistic from each transport and return the
7302311Sseb 	 * aggregate val.  This obviously only works for counters.
7311991Sheppo 	 */
7322311Sseb 	if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) ||
7332311Sseb 	    (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) {
7342311Sseb 		return (ENOTSUP);
7352311Sseb 	}
7366495Sspeer 
7376495Sspeer 	READ_ENTER(&vnetp->vrwlock);
7386495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
7396495Sspeer 		macp = &vresp->macreg;
7406495Sspeer 		cbp = macp->m_callbacks;
7416495Sspeer 		if (cbp->mc_getstat(macp->m_driver, stat, val) == 0)
7422311Sseb 			val_total += *val;
7431991Sheppo 	}
7446495Sspeer 	RW_EXIT(&vnetp->vrwlock);
7451991Sheppo 
7462311Sseb 	*val = val_total;
7472311Sseb 
7484647Sraghuram 	DBG1(vnetp, "exit\n");
7492311Sseb 	return (0);
7501991Sheppo }
7511991Sheppo 
7521991Sheppo /* wrapper function for mac_register() */
7531991Sheppo static int
7541991Sheppo vnet_mac_register(vnet_t *vnetp)
7551991Sheppo {
7562311Sseb 	mac_register_t	*macp;
7572311Sseb 	int		err;
7581991Sheppo 
7592311Sseb 	if ((macp = mac_alloc(MAC_VERSION)) == NULL)
7602311Sseb 		return (DDI_FAILURE);
7612311Sseb 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
7622311Sseb 	macp->m_driver = vnetp;
7631991Sheppo 	macp->m_dip = vnetp->dip;
7642311Sseb 	macp->m_src_addr = vnetp->curr_macaddr;
7652311Sseb 	macp->m_callbacks = &vnet_m_callbacks;
7662311Sseb 	macp->m_min_sdu = 0;
7677529SSriharsha.Basavapatna@Sun.COM 	macp->m_max_sdu = vnetp->mtu;
7686419Ssb155480 	macp->m_margin = VLAN_TAGSZ;
7691991Sheppo 
7701991Sheppo 	/*
7711991Sheppo 	 * Finally, we're ready to register ourselves with the MAC layer
7721991Sheppo 	 * interface; if this succeeds, we're all ready to start()
7731991Sheppo 	 */
7742311Sseb 	err = mac_register(macp, &vnetp->mh);
7752311Sseb 	mac_free(macp);
7762311Sseb 	return (err == 0 ? DDI_SUCCESS : DDI_FAILURE);
7771991Sheppo }
7781991Sheppo 
7791991Sheppo /* read the mac address of the device */
7801991Sheppo static int
7811991Sheppo vnet_read_mac_address(vnet_t *vnetp)
7821991Sheppo {
7831991Sheppo 	uchar_t 	*macaddr;
7841991Sheppo 	uint32_t 	size;
7851991Sheppo 	int 		rv;
7861991Sheppo 
7871991Sheppo 	rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip,
7884650Sraghuram 	    DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size);
7891991Sheppo 	if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) {
7904647Sraghuram 		DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n",
7914647Sraghuram 		    macaddr_propname, rv);
7921991Sheppo 		return (DDI_FAILURE);
7931991Sheppo 	}
7941991Sheppo 	bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL);
7951991Sheppo 	bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL);
7961991Sheppo 	ddi_prop_free(macaddr);
7971991Sheppo 
7981991Sheppo 	return (DDI_SUCCESS);
7991991Sheppo }
8001991Sheppo 
8016419Ssb155480 static void
8026419Ssb155480 vnet_fdb_create(vnet_t *vnetp)
8031991Sheppo {
8046419Ssb155480 	char		hashname[MAXNAMELEN];
8051991Sheppo 
8066419Ssb155480 	(void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash",
8076419Ssb155480 	    vnetp->instance);
8086419Ssb155480 	vnetp->fdb_nchains = vnet_fdb_nchains;
8096419Ssb155480 	vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains,
8106419Ssb155480 	    mod_hash_null_valdtor, sizeof (void *));
8116419Ssb155480 }
8121991Sheppo 
8136419Ssb155480 static void
8146419Ssb155480 vnet_fdb_destroy(vnet_t *vnetp)
8156419Ssb155480 {
8166419Ssb155480 	/* destroy fdb-hash-table */
8176419Ssb155480 	if (vnetp->fdb_hashp != NULL) {
8186419Ssb155480 		mod_hash_destroy_hash(vnetp->fdb_hashp);
8196419Ssb155480 		vnetp->fdb_hashp = NULL;
8206419Ssb155480 		vnetp->fdb_nchains = 0;
8211991Sheppo 	}
8221991Sheppo }
8231991Sheppo 
8246419Ssb155480 /*
8256419Ssb155480  * Add an entry into the fdb.
8266419Ssb155480  */
8271991Sheppo void
8286495Sspeer vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp)
8291991Sheppo {
8306419Ssb155480 	uint64_t	addr = 0;
8316419Ssb155480 	int		rv;
8326419Ssb155480 
8336495Sspeer 	KEY_HASH(addr, vresp->rem_macaddr);
8341991Sheppo 
8356419Ssb155480 	/*
8366495Sspeer 	 * If the entry being added corresponds to LDC_SERVICE resource,
8376495Sspeer 	 * that is, vswitch connection, it is added to the hash and also
8386495Sspeer 	 * the entry is cached, an additional reference count reflects
8396495Sspeer 	 * this. The HYBRID resource is not added to the hash, but only
8406495Sspeer 	 * cached, as it is only used for sending out packets for unknown
8416495Sspeer 	 * unicast destinations.
8426419Ssb155480 	 */
8436495Sspeer 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
8446495Sspeer 	    (vresp->refcnt = 1) : (vresp->refcnt = 0);
8451991Sheppo 
8466419Ssb155480 	/*
8476419Ssb155480 	 * Note: duplicate keys will be rejected by mod_hash.
8486419Ssb155480 	 */
8496495Sspeer 	if (vresp->type != VIO_NET_RES_HYBRID) {
8506495Sspeer 		rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr,
8516495Sspeer 		    (mod_hash_val_t)vresp);
8526495Sspeer 		if (rv != 0) {
8536495Sspeer 			DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr);
8546495Sspeer 			return;
8556495Sspeer 		}
8561991Sheppo 	}
8571991Sheppo 
8586495Sspeer 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
8596419Ssb155480 		/* Cache the fdb entry to vsw-port */
8606419Ssb155480 		WRITE_ENTER(&vnetp->vsw_fp_rw);
8616419Ssb155480 		if (vnetp->vsw_fp == NULL)
8626495Sspeer 			vnetp->vsw_fp = vresp;
8636495Sspeer 		RW_EXIT(&vnetp->vsw_fp_rw);
8646495Sspeer 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
8656495Sspeer 		/* Cache the fdb entry to hybrid resource */
8666495Sspeer 		WRITE_ENTER(&vnetp->vsw_fp_rw);
8676495Sspeer 		if (vnetp->hio_fp == NULL)
8686495Sspeer 			vnetp->hio_fp = vresp;
8696419Ssb155480 		RW_EXIT(&vnetp->vsw_fp_rw);
8702793Slm66018 	}
8711991Sheppo }
8721991Sheppo 
8736419Ssb155480 /*
8746419Ssb155480  * Remove an entry from fdb.
8756419Ssb155480  */
8766495Sspeer static void
8776495Sspeer vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp)
8785641Swentaoy {
8796419Ssb155480 	uint64_t	addr = 0;
8806419Ssb155480 	int		rv;
8816419Ssb155480 	uint32_t	refcnt;
8826495Sspeer 	vnet_res_t	*tmp;
8835641Swentaoy 
8846495Sspeer 	KEY_HASH(addr, vresp->rem_macaddr);
8855641Swentaoy 
8866419Ssb155480 	/*
8876419Ssb155480 	 * Remove the entry from fdb hash table.
8886419Ssb155480 	 * This prevents further references to this fdb entry.
8896419Ssb155480 	 */
8906495Sspeer 	if (vresp->type != VIO_NET_RES_HYBRID) {
8916495Sspeer 		rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr,
8926495Sspeer 		    (mod_hash_val_t *)&tmp);
8936495Sspeer 		if (rv != 0) {
8946495Sspeer 			/*
8956495Sspeer 			 * As the resources are added to the hash only
8966495Sspeer 			 * after they are started, this can occur if
8976495Sspeer 			 * a resource unregisters before it is ever started.
8986495Sspeer 			 */
8996495Sspeer 			return;
9006495Sspeer 		}
9016495Sspeer 	}
9025641Swentaoy 
9036495Sspeer 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
9046419Ssb155480 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9055641Swentaoy 
9066495Sspeer 		ASSERT(tmp == vnetp->vsw_fp);
9076419Ssb155480 		vnetp->vsw_fp = NULL;
9086419Ssb155480 
9096419Ssb155480 		RW_EXIT(&vnetp->vsw_fp_rw);
9106495Sspeer 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
9116495Sspeer 		WRITE_ENTER(&vnetp->vsw_fp_rw);
9126495Sspeer 
9136495Sspeer 		vnetp->hio_fp = NULL;
9146495Sspeer 
9156495Sspeer 		RW_EXIT(&vnetp->vsw_fp_rw);
9165641Swentaoy 	}
9175641Swentaoy 
9185641Swentaoy 	/*
9196419Ssb155480 	 * If there are threads already ref holding before the entry was
9206419Ssb155480 	 * removed from hash table, then wait for ref count to drop to zero.
9215641Swentaoy 	 */
9226495Sspeer 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
9236495Sspeer 	    (refcnt = 1) : (refcnt = 0);
9246495Sspeer 	while (vresp->refcnt > refcnt) {
9256419Ssb155480 		delay(drv_usectohz(vnet_fdbe_refcnt_delay));
9266419Ssb155480 	}
9271991Sheppo }
9281991Sheppo 
9296419Ssb155480 /*
9306419Ssb155480  * Search fdb for a given mac address. If an entry is found, hold
9316419Ssb155480  * a reference to it and return the entry; else returns NULL.
9326419Ssb155480  */
9336495Sspeer static vnet_res_t *
9346419Ssb155480 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp)
9351991Sheppo {
9366419Ssb155480 	uint64_t	key = 0;
9376495Sspeer 	vnet_res_t	*vresp;
9386419Ssb155480 	int		rv;
9396419Ssb155480 
9406495Sspeer 	KEY_HASH(key, addrp->ether_addr_octet);
9416419Ssb155480 
9426419Ssb155480 	rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key,
9436495Sspeer 	    (mod_hash_val_t *)&vresp, vnet_fdbe_find_cb);
9441991Sheppo 
9456419Ssb155480 	if (rv != 0)
9466419Ssb155480 		return (NULL);
9471991Sheppo 
9486495Sspeer 	return (vresp);
9496419Ssb155480 }
9501991Sheppo 
9516419Ssb155480 /*
9526419Ssb155480  * Callback function provided to mod_hash_find_cb(). After finding the fdb
9536419Ssb155480  * entry corresponding to the key (macaddr), this callback will be invoked by
9546419Ssb155480  * mod_hash_find_cb() to atomically increment the reference count on the fdb
9556419Ssb155480  * entry before returning the found entry.
9566419Ssb155480  */
9576419Ssb155480 static void
9586419Ssb155480 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
9596419Ssb155480 {
9606419Ssb155480 	_NOTE(ARGUNUSED(key))
9616495Sspeer 	VNET_FDBE_REFHOLD((vnet_res_t *)val);
9626495Sspeer }
9636495Sspeer 
9646495Sspeer static void
9656495Sspeer vnet_rx(vio_net_handle_t vrh, mblk_t *mp)
9666495Sspeer {
9676495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vrh;
9686495Sspeer 	vnet_t *vnetp = vresp->vnetp;
9696495Sspeer 
9706495Sspeer 	if ((vnetp != NULL) && (vnetp->mh)) {
9716495Sspeer 		mac_rx(vnetp->mh, NULL, mp);
9726495Sspeer 	} else {
9736495Sspeer 		freemsgchain(mp);
9746495Sspeer 	}
9751991Sheppo }
9762311Sseb 
9772311Sseb void
9786495Sspeer vnet_tx_update(vio_net_handle_t vrh)
9796495Sspeer {
9806495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vrh;
9816495Sspeer 	vnet_t *vnetp = vresp->vnetp;
9826495Sspeer 
9836495Sspeer 	if ((vnetp != NULL) && (vnetp->mh != NULL)) {
9846495Sspeer 		mac_tx_update(vnetp->mh);
9856495Sspeer 	}
9866495Sspeer }
9876495Sspeer 
9886495Sspeer /*
9897529SSriharsha.Basavapatna@Sun.COM  * Update the new mtu of vnet into the mac layer. First check if the device has
9907529SSriharsha.Basavapatna@Sun.COM  * been plumbed and if so fail the mtu update. Returns 0 on success.
9917529SSriharsha.Basavapatna@Sun.COM  */
9927529SSriharsha.Basavapatna@Sun.COM int
9937529SSriharsha.Basavapatna@Sun.COM vnet_mtu_update(vnet_t *vnetp, uint32_t mtu)
9947529SSriharsha.Basavapatna@Sun.COM {
9957529SSriharsha.Basavapatna@Sun.COM 	int	rv;
9967529SSriharsha.Basavapatna@Sun.COM 
9977529SSriharsha.Basavapatna@Sun.COM 	if (vnetp == NULL || vnetp->mh == NULL) {
9987529SSriharsha.Basavapatna@Sun.COM 		return (EINVAL);
9997529SSriharsha.Basavapatna@Sun.COM 	}
10007529SSriharsha.Basavapatna@Sun.COM 
10017529SSriharsha.Basavapatna@Sun.COM 	WRITE_ENTER(&vnetp->vrwlock);
10027529SSriharsha.Basavapatna@Sun.COM 
10037529SSriharsha.Basavapatna@Sun.COM 	if (vnetp->flags & VNET_STARTED) {
10047529SSriharsha.Basavapatna@Sun.COM 		RW_EXIT(&vnetp->vrwlock);
10057529SSriharsha.Basavapatna@Sun.COM 		cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu "
10067529SSriharsha.Basavapatna@Sun.COM 		    "update as the device is plumbed\n",
10077529SSriharsha.Basavapatna@Sun.COM 		    vnetp->instance);
10087529SSriharsha.Basavapatna@Sun.COM 		return (EBUSY);
10097529SSriharsha.Basavapatna@Sun.COM 	}
10107529SSriharsha.Basavapatna@Sun.COM 
10117529SSriharsha.Basavapatna@Sun.COM 	/* update mtu in the mac layer */
10127529SSriharsha.Basavapatna@Sun.COM 	rv = mac_maxsdu_update(vnetp->mh, mtu);
10137529SSriharsha.Basavapatna@Sun.COM 	if (rv != 0) {
10147529SSriharsha.Basavapatna@Sun.COM 		RW_EXIT(&vnetp->vrwlock);
10157529SSriharsha.Basavapatna@Sun.COM 		cmn_err(CE_NOTE,
10167529SSriharsha.Basavapatna@Sun.COM 		    "!vnet%d: Unable to update mtu with mac layer\n",
10177529SSriharsha.Basavapatna@Sun.COM 		    vnetp->instance);
10187529SSriharsha.Basavapatna@Sun.COM 		return (EIO);
10197529SSriharsha.Basavapatna@Sun.COM 	}
10207529SSriharsha.Basavapatna@Sun.COM 
10217529SSriharsha.Basavapatna@Sun.COM 	vnetp->mtu = mtu;
10227529SSriharsha.Basavapatna@Sun.COM 
10237529SSriharsha.Basavapatna@Sun.COM 	RW_EXIT(&vnetp->vrwlock);
10247529SSriharsha.Basavapatna@Sun.COM 
10257529SSriharsha.Basavapatna@Sun.COM 	return (0);
10267529SSriharsha.Basavapatna@Sun.COM }
10277529SSriharsha.Basavapatna@Sun.COM 
10287529SSriharsha.Basavapatna@Sun.COM /*
10296495Sspeer  * vio_net_resource_reg -- An interface called to register a resource
10306495Sspeer  *	with vnet.
10316495Sspeer  *	macp -- a GLDv3 mac_register that has all the details of
10326495Sspeer  *		a resource and its callbacks etc.
10336495Sspeer  *	type -- resource type.
10346495Sspeer  *	local_macaddr -- resource's MAC address. This is used to
10356495Sspeer  *			 associate a resource with a corresponding vnet.
10366495Sspeer  *	remote_macaddr -- remote side MAC address. This is ignored for
10376495Sspeer  *			  the Hybrid resources.
10386495Sspeer  *	vhp -- A handle returned to the caller.
10396495Sspeer  *	vcb -- A set of callbacks provided to the callers.
10406495Sspeer  */
10416495Sspeer int vio_net_resource_reg(mac_register_t *macp, vio_net_res_type_t type,
10426495Sspeer     ether_addr_t local_macaddr, ether_addr_t rem_macaddr, vio_net_handle_t *vhp,
10436495Sspeer     vio_net_callbacks_t *vcb)
10446495Sspeer {
10456495Sspeer 	vnet_t	*vnetp;
10466495Sspeer 	vnet_res_t *vresp;
10476495Sspeer 
10486495Sspeer 	vresp = kmem_zalloc(sizeof (vnet_res_t), KM_SLEEP);
10496495Sspeer 	ether_copy(local_macaddr, vresp->local_macaddr);
10506495Sspeer 	ether_copy(rem_macaddr, vresp->rem_macaddr);
10516495Sspeer 	vresp->type = type;
10526495Sspeer 	bcopy(macp, &vresp->macreg, sizeof (mac_register_t));
10536495Sspeer 
10546495Sspeer 	DBG1(NULL, "Resource Registerig type=0%X\n", type);
10556495Sspeer 
10566495Sspeer 	READ_ENTER(&vnet_rw);
10576495Sspeer 	vnetp = vnet_headp;
10586495Sspeer 	while (vnetp != NULL) {
10596495Sspeer 		if (VNET_MATCH_RES(vresp, vnetp)) {
10606495Sspeer 			WRITE_ENTER(&vnetp->vrwlock);
10616495Sspeer 			vresp->vnetp = vnetp;
10626495Sspeer 			vresp->nextp = vnetp->vres_list;
10636495Sspeer 			vnetp->vres_list = vresp;
10646495Sspeer 			RW_EXIT(&vnetp->vrwlock);
10656495Sspeer 			break;
10666495Sspeer 		}
10676495Sspeer 		vnetp = vnetp->nextp;
10686495Sspeer 	}
10696495Sspeer 	RW_EXIT(&vnet_rw);
10706495Sspeer 	if (vresp->vnetp == NULL) {
10716495Sspeer 		DWARN(NULL, "No vnet instance");
10726495Sspeer 		kmem_free(vresp, sizeof (vnet_res_t));
10736495Sspeer 		return (ENXIO);
10746495Sspeer 	}
10756495Sspeer 
10766495Sspeer 	*vhp = vresp;
10776495Sspeer 	vcb->vio_net_rx_cb = vnet_rx;
10786495Sspeer 	vcb->vio_net_tx_update = vnet_tx_update;
10796495Sspeer 	vcb->vio_net_report_err = vnet_handle_res_err;
10806495Sspeer 
10816495Sspeer 	/* Dispatch a task to start resources */
10826495Sspeer 	vnet_dispatch_res_task(vnetp);
10836495Sspeer 	return (0);
10846495Sspeer }
10856495Sspeer 
10866495Sspeer /*
10876495Sspeer  * vio_net_resource_unreg -- An interface to unregister a resource.
10886495Sspeer  */
10896495Sspeer void
10906495Sspeer vio_net_resource_unreg(vio_net_handle_t vhp)
10916495Sspeer {
10926495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vhp;
10936495Sspeer 	vnet_t *vnetp = vresp->vnetp;
10946495Sspeer 	vnet_res_t *vrp;
10956495Sspeer 
10966495Sspeer 	DBG1(NULL, "Resource Registerig hdl=0x%p", vhp);
10976495Sspeer 
10986495Sspeer 	ASSERT(vnetp != NULL);
10996495Sspeer 	vnet_fdbe_del(vnetp, vresp);
11006495Sspeer 
11016495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
11026495Sspeer 	if (vresp == vnetp->vres_list) {
11036495Sspeer 		vnetp->vres_list = vresp->nextp;
11046495Sspeer 	} else {
11056495Sspeer 		vrp = vnetp->vres_list;
11066495Sspeer 		while (vrp->nextp != NULL) {
11076495Sspeer 			if (vrp->nextp == vresp) {
11086495Sspeer 				vrp->nextp = vresp->nextp;
11096495Sspeer 				break;
11106495Sspeer 			}
11116495Sspeer 			vrp = vrp->nextp;
11126495Sspeer 		}
11136495Sspeer 	}
11146495Sspeer 	vresp->vnetp = NULL;
11156495Sspeer 	vresp->nextp = NULL;
11166495Sspeer 	RW_EXIT(&vnetp->vrwlock);
11176495Sspeer 	KMEM_FREE(vresp);
11186495Sspeer }
11196495Sspeer 
11206495Sspeer /*
11216495Sspeer  * vnet_dds_rx -- an interface called by vgen to DDS messages.
11226495Sspeer  */
11236495Sspeer void
11246495Sspeer vnet_dds_rx(void *arg, void *dmsg)
11252311Sseb {
11262311Sseb 	vnet_t *vnetp = arg;
11276495Sspeer 	vdds_process_dds_msg(vnetp, dmsg);
11282311Sseb }
11292311Sseb 
11306495Sspeer /*
11316495Sspeer  * vnet_send_dds_msg -- An interface provided to DDS to send
11326495Sspeer  *	DDS messages. This simply sends meessages via vgen.
11336495Sspeer  */
11346495Sspeer int
11356495Sspeer vnet_send_dds_msg(vnet_t *vnetp, void *dmsg)
11366495Sspeer {
11376495Sspeer 	int rv;
11386495Sspeer 
11396495Sspeer 	if (vnetp->vgenhdl != NULL) {
11406495Sspeer 		rv = vgen_dds_tx(vnetp->vgenhdl, dmsg);
11416495Sspeer 	}
11426495Sspeer 	return (rv);
11436495Sspeer }
11446495Sspeer 
11456495Sspeer /*
11466495Sspeer  * vnet_handle_res_err -- A callback function called by a resource
11476495Sspeer  *	to report an error. For example, vgen can call to report
11486495Sspeer  *	an LDC down/reset event. This will trigger cleanup of associated
11496495Sspeer  *	Hybrid resource.
11506495Sspeer  */
11516495Sspeer /* ARGSUSED */
11526495Sspeer static void
11536495Sspeer vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err)
11546495Sspeer {
11556495Sspeer 	vnet_res_t *vresp = (vnet_res_t *)vrh;
11566495Sspeer 	vnet_t *vnetp = vresp->vnetp;
1157*7819SRaghuram.Kothakota@Sun.COM 	int rv;
11586495Sspeer 
11596495Sspeer 	if (vnetp == NULL) {
11606495Sspeer 		return;
11616495Sspeer 	}
11626495Sspeer 	if ((vresp->type != VIO_NET_RES_LDC_SERVICE) &&
11636495Sspeer 	    (vresp->type != VIO_NET_RES_HYBRID)) {
11646495Sspeer 		return;
11656495Sspeer 	}
1166*7819SRaghuram.Kothakota@Sun.COM 	rv = ddi_taskq_dispatch(vnetp->taskqp, vdds_cleanup_hybrid_res,
1167*7819SRaghuram.Kothakota@Sun.COM 	    vnetp, DDI_NOSLEEP);
1168*7819SRaghuram.Kothakota@Sun.COM 	if (rv != DDI_SUCCESS) {
1169*7819SRaghuram.Kothakota@Sun.COM 		cmn_err(CE_WARN,
1170*7819SRaghuram.Kothakota@Sun.COM 		    "vnet%d:Failed to dispatch task to cleanup hybrid resource",
1171*7819SRaghuram.Kothakota@Sun.COM 		    vnetp->instance);
1172*7819SRaghuram.Kothakota@Sun.COM 	}
11736495Sspeer }
11746495Sspeer 
11756495Sspeer /*
11766495Sspeer  * vnet_dispatch_res_task -- A function to dispatch tasks start resources.
11776495Sspeer  */
11786495Sspeer static void
11796495Sspeer vnet_dispatch_res_task(vnet_t *vnetp)
11806495Sspeer {
11816495Sspeer 	int rv;
11826495Sspeer 
11836495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
11846495Sspeer 	if (vnetp->flags & VNET_STARTED) {
11856495Sspeer 		rv = ddi_taskq_dispatch(vnetp->taskqp, vnet_res_start_task,
11866495Sspeer 		    vnetp, DDI_NOSLEEP);
11876495Sspeer 		if (rv != DDI_SUCCESS) {
11886495Sspeer 			cmn_err(CE_WARN,
11896495Sspeer 			    "vnet%d:Can't dispatch start resource task",
11906495Sspeer 			    vnetp->instance);
11916495Sspeer 		}
11926495Sspeer 	}
11936495Sspeer 	RW_EXIT(&vnetp->vrwlock);
11946495Sspeer }
11956495Sspeer 
11966495Sspeer /*
11976495Sspeer  * vnet_res_start_task -- A taskq callback function that starts a resource.
11986495Sspeer  */
11996495Sspeer static void
12006495Sspeer vnet_res_start_task(void *arg)
12012311Sseb {
12022311Sseb 	vnet_t *vnetp = arg;
12036495Sspeer 
12046495Sspeer 	WRITE_ENTER(&vnetp->vrwlock);
12056495Sspeer 	if (vnetp->flags & VNET_STARTED) {
12066495Sspeer 		vnet_start_resources(vnetp);
12076495Sspeer 	}
12086495Sspeer 	RW_EXIT(&vnetp->vrwlock);
12092311Sseb }
12106495Sspeer 
12116495Sspeer /*
12126495Sspeer  * vnet_start_resources -- starts all resources associated with
12136495Sspeer  *	a vnet.
12146495Sspeer  */
12156495Sspeer static void
12166495Sspeer vnet_start_resources(vnet_t *vnetp)
12176495Sspeer {
12186495Sspeer 	mac_register_t	*macp;
12196495Sspeer 	mac_callbacks_t	*cbp;
12206495Sspeer 	vnet_res_t	*vresp;
12216495Sspeer 	int rv;
12226495Sspeer 
12236495Sspeer 	DBG1(vnetp, "enter\n");
12246495Sspeer 
12256495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
12266495Sspeer 		/* skip if it is already started */
12276495Sspeer 		if (vresp->flags & VNET_STARTED) {
12286495Sspeer 			continue;
12296495Sspeer 		}
12306495Sspeer 		macp = &vresp->macreg;
12316495Sspeer 		cbp = macp->m_callbacks;
12326495Sspeer 		rv = cbp->mc_start(macp->m_driver);
12336495Sspeer 		if (rv == 0) {
12346495Sspeer 			/*
12356495Sspeer 			 * Successfully started the resource, so now
12366495Sspeer 			 * add it to the fdb.
12376495Sspeer 			 */
12386495Sspeer 			vresp->flags |= VNET_STARTED;
12396495Sspeer 			vnet_fdbe_add(vnetp, vresp);
12406495Sspeer 		}
12416495Sspeer 	}
12426495Sspeer 
12436495Sspeer 	DBG1(vnetp, "exit\n");
12446495Sspeer 
12456495Sspeer }
12466495Sspeer 
12476495Sspeer /*
12486495Sspeer  * vnet_stop_resources -- stop all resources associated with a vnet.
12496495Sspeer  */
12506495Sspeer static void
12516495Sspeer vnet_stop_resources(vnet_t *vnetp)
12526495Sspeer {
12536495Sspeer 	vnet_res_t	*vresp;
12546495Sspeer 	vnet_res_t	*nvresp;
12556495Sspeer 	mac_register_t	*macp;
12566495Sspeer 	mac_callbacks_t	*cbp;
12576495Sspeer 
12586495Sspeer 	DBG1(vnetp, "enter\n");
12596495Sspeer 
12606495Sspeer 	for (vresp = vnetp->vres_list; vresp != NULL; ) {
12616495Sspeer 		nvresp = vresp->nextp;
12626495Sspeer 		if (vresp->flags & VNET_STARTED) {
12636495Sspeer 			macp = &vresp->macreg;
12646495Sspeer 			cbp = macp->m_callbacks;
12656495Sspeer 			cbp->mc_stop(macp->m_driver);
12666495Sspeer 			vresp->flags &= ~VNET_STARTED;
12676495Sspeer 		}
12686495Sspeer 		vresp = nvresp;
12696495Sspeer 	}
12706495Sspeer 	DBG1(vnetp, "exit\n");
12716495Sspeer }
1272