xref: /onnv-gate/usr/src/uts/sun4v/io/vsw_switching.c (revision 6537:e9d193f61614)
15373Sraghuram /*
25373Sraghuram  * CDDL HEADER START
35373Sraghuram  *
45373Sraghuram  * The contents of this file are subject to the terms of the
55373Sraghuram  * Common Development and Distribution License (the "License").
65373Sraghuram  * You may not use this file except in compliance with the License.
75373Sraghuram  *
85373Sraghuram  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95373Sraghuram  * or http://www.opensolaris.org/os/licensing.
105373Sraghuram  * See the License for the specific language governing permissions
115373Sraghuram  * and limitations under the License.
125373Sraghuram  *
135373Sraghuram  * When distributing Covered Code, include this CDDL HEADER in each
145373Sraghuram  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155373Sraghuram  * If applicable, add the following below this CDDL HEADER, with the
165373Sraghuram  * fields enclosed by brackets "[]" replaced with your own identifying
175373Sraghuram  * information: Portions Copyright [yyyy] [name of copyright owner]
185373Sraghuram  *
195373Sraghuram  * CDDL HEADER END
205373Sraghuram  */
215373Sraghuram 
225373Sraghuram /*
235935Ssb155480  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245373Sraghuram  * Use is subject to license terms.
255373Sraghuram  */
265373Sraghuram 
275373Sraghuram #pragma ident	"%Z%%M%	%I%	%E% SMI"
285373Sraghuram 
295373Sraghuram #include <sys/types.h>
305373Sraghuram #include <sys/errno.h>
315373Sraghuram #include <sys/debug.h>
325373Sraghuram #include <sys/time.h>
335373Sraghuram #include <sys/sysmacros.h>
345373Sraghuram #include <sys/systm.h>
355373Sraghuram #include <sys/user.h>
365373Sraghuram #include <sys/stropts.h>
375373Sraghuram #include <sys/stream.h>
385373Sraghuram #include <sys/strlog.h>
395373Sraghuram #include <sys/strsubr.h>
405373Sraghuram #include <sys/cmn_err.h>
415373Sraghuram #include <sys/cpu.h>
425373Sraghuram #include <sys/kmem.h>
435373Sraghuram #include <sys/conf.h>
445373Sraghuram #include <sys/ddi.h>
455373Sraghuram #include <sys/sunddi.h>
465373Sraghuram #include <sys/ksynch.h>
475373Sraghuram #include <sys/stat.h>
485373Sraghuram #include <sys/kstat.h>
495373Sraghuram #include <sys/vtrace.h>
505373Sraghuram #include <sys/strsun.h>
515373Sraghuram #include <sys/dlpi.h>
525373Sraghuram #include <sys/ethernet.h>
535373Sraghuram #include <net/if.h>
545373Sraghuram #include <sys/varargs.h>
555373Sraghuram #include <sys/machsystm.h>
565373Sraghuram #include <sys/modctl.h>
575373Sraghuram #include <sys/modhash.h>
585373Sraghuram #include <sys/mac.h>
595373Sraghuram #include <sys/mac_ether.h>
605373Sraghuram #include <sys/taskq.h>
615373Sraghuram #include <sys/note.h>
625373Sraghuram #include <sys/mach_descrip.h>
635373Sraghuram #include <sys/mac.h>
645373Sraghuram #include <sys/mdeg.h>
655373Sraghuram #include <sys/ldc.h>
665373Sraghuram #include <sys/vsw_fdb.h>
675373Sraghuram #include <sys/vsw.h>
685373Sraghuram #include <sys/vio_mailbox.h>
695373Sraghuram #include <sys/vnet_mailbox.h>
705373Sraghuram #include <sys/vnet_common.h>
715373Sraghuram #include <sys/vio_util.h>
725373Sraghuram #include <sys/sdt.h>
735373Sraghuram #include <sys/atomic.h>
746419Ssb155480 #include <sys/vlan.h>
755373Sraghuram 
765373Sraghuram /* Switching setup routines */
775373Sraghuram void vsw_setup_switching_timeout(void *arg);
785373Sraghuram void vsw_stop_switching_timeout(vsw_t *vswp);
795373Sraghuram int vsw_setup_switching(vsw_t *);
80*6537Swentaoy void vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller,
81*6537Swentaoy     vsw_port_t *port, mac_resource_handle_t mrh);
825373Sraghuram static	int vsw_setup_layer2(vsw_t *);
835373Sraghuram static	int vsw_setup_layer3(vsw_t *);
845373Sraghuram 
855373Sraghuram /* Switching/data transmit routines */
865373Sraghuram static	void vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller,
876419Ssb155480 	vsw_port_t *port, mac_resource_handle_t);
885373Sraghuram static	void vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller,
896419Ssb155480 	vsw_port_t *port, mac_resource_handle_t);
905935Ssb155480 static	int vsw_forward_all(vsw_t *vswp, mblk_t *mp,
915935Ssb155480 	int caller, vsw_port_t *port);
925935Ssb155480 static	int vsw_forward_grp(vsw_t *vswp, mblk_t *mp,
935373Sraghuram     int caller, vsw_port_t *port);
945373Sraghuram 
956419Ssb155480 /* VLAN routines */
966419Ssb155480 void vsw_create_vlans(void *arg, int type);
976419Ssb155480 void vsw_destroy_vlans(void *arg, int type);
986419Ssb155480 void vsw_vlan_add_ids(void *arg, int type);
996419Ssb155480 void vsw_vlan_remove_ids(void *arg, int type);
1006419Ssb155480 static	void vsw_vlan_create_hash(void *arg, int type);
1016419Ssb155480 static	void vsw_vlan_destroy_hash(void *arg, int type);
1026419Ssb155480 boolean_t vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp,
1036419Ssb155480 	uint16_t *vidp);
1046419Ssb155480 mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp);
1056419Ssb155480 uint32_t vsw_vlan_frames_untag(void *arg, int type, mblk_t **np, mblk_t **npt);
1066419Ssb155480 boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
1076419Ssb155480 
1085373Sraghuram /* Forwarding database (FDB) routines */
1096419Ssb155480 void vsw_fdbe_add(vsw_t *vswp, void *port);
1106419Ssb155480 void vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr);
1116419Ssb155480 static	vsw_fdbe_t *vsw_fdbe_find(vsw_t *vswp, struct ether_addr *);
1126419Ssb155480 static void vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val);
1136419Ssb155480 
1145373Sraghuram int vsw_add_rem_mcst(vnet_mcast_msg_t *, vsw_port_t *);
1155373Sraghuram int vsw_add_mcst(vsw_t *, uint8_t, uint64_t, void *);
1165373Sraghuram int vsw_del_mcst(vsw_t *, uint8_t, uint64_t, void *);
1175373Sraghuram void vsw_del_mcst_vsw(vsw_t *);
1185373Sraghuram 
1195373Sraghuram /* Support functions */
1205373Sraghuram static mblk_t *vsw_dupmsgchain(mblk_t *mp);
1215935Ssb155480 static uint32_t vsw_get_same_dest_list(struct ether_header *ehp,
1225373Sraghuram     mblk_t **rhead, mblk_t **rtail, mblk_t **mpp);
1235373Sraghuram 
1245373Sraghuram 
1255373Sraghuram /*
1265373Sraghuram  * Functions imported from other files.
1275373Sraghuram  */
1285373Sraghuram extern mblk_t *vsw_tx_msg(vsw_t *, mblk_t *);
1295373Sraghuram extern mcst_addr_t *vsw_del_addr(uint8_t, void *, uint64_t);
1305373Sraghuram extern int vsw_mac_open(vsw_t *vswp);
1315373Sraghuram extern void vsw_mac_close(vsw_t *vswp);
1325935Ssb155480 extern void vsw_mac_rx(vsw_t *vswp, mac_resource_handle_t mrh,
1335935Ssb155480     mblk_t *mp, vsw_macrx_flags_t flags);
1345373Sraghuram extern void vsw_set_addrs(vsw_t *vswp);
1355373Sraghuram extern int vsw_get_hw_maddr(vsw_t *);
1365373Sraghuram extern int vsw_mac_attach(vsw_t *vswp);
1375935Ssb155480 extern int vsw_portsend(vsw_port_t *port, mblk_t *mp, mblk_t *mpt,
1385935Ssb155480 	uint32_t count);
1396495Sspeer extern void vsw_hio_init(vsw_t *vswp);
1406495Sspeer extern void vsw_hio_start_ports(vsw_t *vswp);
1415373Sraghuram 
1425373Sraghuram /*
1435373Sraghuram  * Tunables used in this file.
1445373Sraghuram  */
1456419Ssb155480 extern	int vsw_setup_switching_delay;
1466419Ssb155480 extern	uint32_t vsw_vlan_nchains;
1476419Ssb155480 extern	uint32_t vsw_fdbe_refcnt_delay;
1485373Sraghuram 
1496419Ssb155480 #define	VSW_FDBE_REFHOLD(p)						\
1506419Ssb155480 {									\
1516419Ssb155480 	atomic_inc_32(&(p)->refcnt);					\
1526419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1536419Ssb155480 }
1546419Ssb155480 
1556419Ssb155480 #define	VSW_FDBE_REFRELE(p)						\
1566419Ssb155480 {									\
1576419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1586419Ssb155480 	atomic_dec_32(&(p)->refcnt);					\
1596419Ssb155480 }
1605373Sraghuram 
1615373Sraghuram /*
1625373Sraghuram  * Timeout routine to setup switching mode:
1635373Sraghuram  * vsw_setup_switching() is invoked from vsw_attach() or vsw_update_md_prop()
1645373Sraghuram  * initially. If it fails and the error is EAGAIN, then this timeout handler
1655373Sraghuram  * is started to retry vsw_setup_switching(). vsw_setup_switching() is retried
1665373Sraghuram  * until we successfully finish it; or the returned error is not EAGAIN.
1675373Sraghuram  */
1685373Sraghuram void
1695373Sraghuram vsw_setup_switching_timeout(void *arg)
1705373Sraghuram {
1715373Sraghuram 	vsw_t		*vswp = (vsw_t *)arg;
1725373Sraghuram 	int		rv;
1735373Sraghuram 
1745373Sraghuram 	if (vswp->swtmout_enabled == B_FALSE)
1755373Sraghuram 		return;
1765373Sraghuram 
1775373Sraghuram 	rv = vsw_setup_switching(vswp);
1785373Sraghuram 
1795373Sraghuram 	if (rv == 0) {
1805373Sraghuram 		/*
1815373Sraghuram 		 * Successfully setup switching mode.
1825373Sraghuram 		 * Program unicst, mcst addrs of vsw
1835373Sraghuram 		 * interface and ports in the physdev.
1845373Sraghuram 		 */
1855373Sraghuram 		vsw_set_addrs(vswp);
1866495Sspeer 
1876495Sspeer 		/* Start HIO for ports that have already connected */
1886495Sspeer 		vsw_hio_start_ports(vswp);
1895373Sraghuram 	}
1905373Sraghuram 
1915373Sraghuram 	mutex_enter(&vswp->swtmout_lock);
1925373Sraghuram 
1935373Sraghuram 	if (rv == EAGAIN && vswp->swtmout_enabled == B_TRUE) {
1945373Sraghuram 		/*
1955373Sraghuram 		 * Reschedule timeout() if the error is EAGAIN and the
1965373Sraghuram 		 * timeout is still enabled. For errors other than EAGAIN,
1975373Sraghuram 		 * we simply return without rescheduling timeout().
1985373Sraghuram 		 */
1995373Sraghuram 		vswp->swtmout_id =
2005373Sraghuram 		    timeout(vsw_setup_switching_timeout, vswp,
2015373Sraghuram 		    (vsw_setup_switching_delay * drv_usectohz(MICROSEC)));
2025373Sraghuram 		goto exit;
2035373Sraghuram 	}
2045373Sraghuram 
2055373Sraghuram 	/* timeout handler completed */
2065373Sraghuram 	vswp->swtmout_enabled = B_FALSE;
2075373Sraghuram 	vswp->swtmout_id = 0;
2085373Sraghuram 
2095373Sraghuram exit:
2105373Sraghuram 	mutex_exit(&vswp->swtmout_lock);
2115373Sraghuram }
2125373Sraghuram 
2135373Sraghuram /*
2145373Sraghuram  * Cancel the timeout handler to setup switching mode.
2155373Sraghuram  */
2165373Sraghuram void
2175373Sraghuram vsw_stop_switching_timeout(vsw_t *vswp)
2185373Sraghuram {
2195373Sraghuram 	timeout_id_t tid;
2205373Sraghuram 
2215373Sraghuram 	mutex_enter(&vswp->swtmout_lock);
2225373Sraghuram 
2235373Sraghuram 	tid = vswp->swtmout_id;
2245373Sraghuram 
2255373Sraghuram 	if (tid != 0) {
2265373Sraghuram 		/* signal timeout handler to stop */
2275373Sraghuram 		vswp->swtmout_enabled = B_FALSE;
2285373Sraghuram 		vswp->swtmout_id = 0;
2295373Sraghuram 		mutex_exit(&vswp->swtmout_lock);
2305373Sraghuram 
2315373Sraghuram 		(void) untimeout(tid);
2325373Sraghuram 	} else {
2335373Sraghuram 		mutex_exit(&vswp->swtmout_lock);
2345373Sraghuram 	}
2355373Sraghuram 
2365373Sraghuram 	(void) atomic_swap_32(&vswp->switching_setup_done, B_FALSE);
2375373Sraghuram 
2385373Sraghuram 	mutex_enter(&vswp->mac_lock);
2395373Sraghuram 	vswp->mac_open_retries = 0;
2405373Sraghuram 	mutex_exit(&vswp->mac_lock);
2415373Sraghuram }
2425373Sraghuram 
2435373Sraghuram /*
2445373Sraghuram  * Setup the required switching mode.
2455373Sraghuram  * This routine is invoked from vsw_attach() or vsw_update_md_prop()
2465373Sraghuram  * initially. If it fails and the error is EAGAIN, then a timeout handler
2475373Sraghuram  * is started to retry vsw_setup_switching(), until it successfully finishes;
2485373Sraghuram  * or the returned error is not EAGAIN.
2495373Sraghuram  *
2505373Sraghuram  * Returns:
2515373Sraghuram  *  0 on success.
2525373Sraghuram  *  EAGAIN if retry is needed.
2535373Sraghuram  *  1 on all other failures.
2545373Sraghuram  */
2555373Sraghuram int
2565373Sraghuram vsw_setup_switching(vsw_t *vswp)
2575373Sraghuram {
2585373Sraghuram 	int	i, rv = 1;
2595373Sraghuram 
2605373Sraghuram 	D1(vswp, "%s: enter", __func__);
2615373Sraghuram 
2625373Sraghuram 	/*
2635373Sraghuram 	 * Select best switching mode.
2645373Sraghuram 	 * Note that we start from the saved smode_idx. This is done as
2655373Sraghuram 	 * this routine can be called from the timeout handler to retry
2665373Sraghuram 	 * setting up a specific mode. Currently only the function which
2675373Sraghuram 	 * sets up layer2/promisc mode returns EAGAIN if the underlying
2685373Sraghuram 	 * physical device is not available yet, causing retries.
2695373Sraghuram 	 */
2705373Sraghuram 	for (i = vswp->smode_idx; i < vswp->smode_num; i++) {
2715373Sraghuram 		vswp->smode_idx = i;
2725373Sraghuram 		switch (vswp->smode[i]) {
2735373Sraghuram 		case VSW_LAYER2:
2745373Sraghuram 		case VSW_LAYER2_PROMISC:
2755373Sraghuram 			rv = vsw_setup_layer2(vswp);
2765373Sraghuram 			break;
2775373Sraghuram 
2785373Sraghuram 		case VSW_LAYER3:
2795373Sraghuram 			rv = vsw_setup_layer3(vswp);
2805373Sraghuram 			break;
2815373Sraghuram 
2825373Sraghuram 		default:
2835373Sraghuram 			DERR(vswp, "unknown switch mode");
2845373Sraghuram 			break;
2855373Sraghuram 		}
2865373Sraghuram 
2875373Sraghuram 		if ((rv == 0) || (rv == EAGAIN))
2885373Sraghuram 			break;
2895373Sraghuram 
2905373Sraghuram 		/* all other errors(rv != 0): continue & select the next mode */
2915373Sraghuram 		rv = 1;
2925373Sraghuram 	}
2935373Sraghuram 
2945373Sraghuram 	if (rv && (rv != EAGAIN)) {
2955373Sraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to setup specified "
2965373Sraghuram 		    "switching mode", vswp->instance);
2975373Sraghuram 	} else if (rv == 0) {
2985373Sraghuram 		(void) atomic_swap_32(&vswp->switching_setup_done, B_TRUE);
2995373Sraghuram 	}
3005373Sraghuram 
3015373Sraghuram 	D2(vswp, "%s: Operating in mode %d", __func__,
3025373Sraghuram 	    vswp->smode[vswp->smode_idx]);
3035373Sraghuram 
3045373Sraghuram 	D1(vswp, "%s: exit", __func__);
3055373Sraghuram 
3065373Sraghuram 	return (rv);
3075373Sraghuram }
3085373Sraghuram 
3095373Sraghuram /*
3105373Sraghuram  * Setup for layer 2 switching.
3115373Sraghuram  *
3125373Sraghuram  * Returns:
3135373Sraghuram  *  0 on success.
3145373Sraghuram  *  EAGAIN if retry is needed.
3155373Sraghuram  *  EIO on all other failures.
3165373Sraghuram  */
3175373Sraghuram static int
3185373Sraghuram vsw_setup_layer2(vsw_t *vswp)
3195373Sraghuram {
3205373Sraghuram 	int	rv;
3215373Sraghuram 
3225373Sraghuram 	D1(vswp, "%s: enter", __func__);
3235373Sraghuram 
3245373Sraghuram 	vswp->vsw_switch_frame = vsw_switch_l2_frame;
3255373Sraghuram 
3265373Sraghuram 	rv = strlen(vswp->physname);
3275373Sraghuram 	if (rv == 0) {
3285373Sraghuram 		/*
3295373Sraghuram 		 * Physical device name is NULL, which is
3305373Sraghuram 		 * required for layer 2.
3315373Sraghuram 		 */
3325373Sraghuram 		cmn_err(CE_WARN, "!vsw%d: no physical device name specified",
3335373Sraghuram 		    vswp->instance);
3345373Sraghuram 		return (EIO);
3355373Sraghuram 	}
3365373Sraghuram 
3375373Sraghuram 	mutex_enter(&vswp->mac_lock);
3385373Sraghuram 
3395373Sraghuram 	rv = vsw_mac_open(vswp);
3405373Sraghuram 	if (rv != 0) {
3415373Sraghuram 		if (rv != EAGAIN) {
3425373Sraghuram 			cmn_err(CE_WARN, "!vsw%d: Unable to open physical "
3435373Sraghuram 			    "device: %s\n", vswp->instance, vswp->physname);
3445373Sraghuram 		}
3455373Sraghuram 		mutex_exit(&vswp->mac_lock);
3465373Sraghuram 		return (rv);
3475373Sraghuram 	}
3485373Sraghuram 
3495373Sraghuram 	if (vswp->smode[vswp->smode_idx] == VSW_LAYER2) {
3505373Sraghuram 		/*
3515373Sraghuram 		 * Verify that underlying device can support multiple
3525373Sraghuram 		 * unicast mac addresses.
3535373Sraghuram 		 */
3545373Sraghuram 		rv = vsw_get_hw_maddr(vswp);
3555373Sraghuram 		if (rv != 0) {
3565373Sraghuram 			goto exit_error;
3575373Sraghuram 		}
3585373Sraghuram 	}
3595373Sraghuram 
3605373Sraghuram 	/*
3615373Sraghuram 	 * Attempt to link into the MAC layer so we can get
3625373Sraghuram 	 * and send packets out over the physical adapter.
3635373Sraghuram 	 */
3645373Sraghuram 	rv = vsw_mac_attach(vswp);
3655373Sraghuram 	if (rv != 0) {
3665373Sraghuram 		/*
3675373Sraghuram 		 * Registration with the MAC layer has failed,
3685373Sraghuram 		 * so return error so that can fall back to next
3695373Sraghuram 		 * prefered switching method.
3705373Sraghuram 		 */
3715373Sraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to setup physical device: "
3725373Sraghuram 		    "%s\n", vswp->instance, vswp->physname);
3735373Sraghuram 		goto exit_error;
3745373Sraghuram 	}
3755373Sraghuram 
3765373Sraghuram 	D1(vswp, "%s: exit", __func__);
3775373Sraghuram 
3785373Sraghuram 	mutex_exit(&vswp->mac_lock);
3796495Sspeer 
3806495Sspeer 	/* Initialize HybridIO related stuff */
3816495Sspeer 	vsw_hio_init(vswp);
3825373Sraghuram 	return (0);
3835373Sraghuram 
3845373Sraghuram exit_error:
3855373Sraghuram 	vsw_mac_close(vswp);
3865373Sraghuram 	mutex_exit(&vswp->mac_lock);
3875373Sraghuram 	return (EIO);
3885373Sraghuram }
3895373Sraghuram 
3905373Sraghuram static int
3915373Sraghuram vsw_setup_layer3(vsw_t *vswp)
3925373Sraghuram {
3935373Sraghuram 	D1(vswp, "%s: enter", __func__);
3945373Sraghuram 
3955373Sraghuram 	D2(vswp, "%s: operating in layer 3 mode", __func__);
3965373Sraghuram 	vswp->vsw_switch_frame = vsw_switch_l3_frame;
3975373Sraghuram 
3985373Sraghuram 	D1(vswp, "%s: exit", __func__);
3995373Sraghuram 
4005373Sraghuram 	return (0);
4015373Sraghuram }
4025373Sraghuram 
403*6537Swentaoy /* ARGSUSED */
404*6537Swentaoy void
405*6537Swentaoy vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port,
406*6537Swentaoy 			mac_resource_handle_t mrh)
407*6537Swentaoy {
408*6537Swentaoy 	freemsgchain(mp);
409*6537Swentaoy }
410*6537Swentaoy 
4115373Sraghuram /*
4125373Sraghuram  * Switch the given ethernet frame when operating in layer 2 mode.
4135373Sraghuram  *
4145373Sraghuram  * vswp: pointer to the vsw instance
4155373Sraghuram  * mp: pointer to chain of ethernet frame(s) to be switched
4165373Sraghuram  * caller: identifies the source of this frame as:
4175373Sraghuram  * 		1. VSW_VNETPORT - a vsw port (connected to a vnet).
4185373Sraghuram  *		2. VSW_PHYSDEV - the physical ethernet device
4195373Sraghuram  *		3. VSW_LOCALDEV - vsw configured as a virtual interface
4205373Sraghuram  * arg: argument provided by the caller.
4215373Sraghuram  *		1. for VNETPORT - pointer to the corresponding vsw_port_t.
4225373Sraghuram  *		2. for PHYSDEV - NULL
4235373Sraghuram  *		3. for LOCALDEV - pointer to to this vsw_t(self)
4245373Sraghuram  */
4255373Sraghuram void
4265373Sraghuram vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller,
4275373Sraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
4285373Sraghuram {
4295373Sraghuram 	struct ether_header	*ehp;
4305373Sraghuram 	mblk_t			*bp, *ret_m;
4315373Sraghuram 	mblk_t			*mpt = NULL;
4325935Ssb155480 	uint32_t		count;
4336419Ssb155480 	vsw_fdbe_t		*fp;
4345373Sraghuram 
4355373Sraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
4365373Sraghuram 
4375373Sraghuram 	/*
4385373Sraghuram 	 * PERF: rather than breaking up the chain here, scan it
4395373Sraghuram 	 * to find all mblks heading to same destination and then
4405373Sraghuram 	 * pass that sub-chain to the lower transmit functions.
4415373Sraghuram 	 */
4425373Sraghuram 
4435373Sraghuram 	/* process the chain of packets */
4445373Sraghuram 	bp = mp;
4455373Sraghuram 	while (bp) {
4465373Sraghuram 		ehp = (struct ether_header *)bp->b_rptr;
4475935Ssb155480 		count = vsw_get_same_dest_list(ehp, &mp, &mpt, &bp);
4485935Ssb155480 		ASSERT(count != 0);
4495373Sraghuram 
4505373Sraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
4515373Sraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
4525373Sraghuram 
4535373Sraghuram 		if (ether_cmp(&ehp->ether_dhost, &vswp->if_addr) == 0) {
4545373Sraghuram 			/*
4555373Sraghuram 			 * If destination is VSW_LOCALDEV (vsw as an eth
4565373Sraghuram 			 * interface) and if the device is up & running,
4575373Sraghuram 			 * send the packet up the stack on this host.
4585373Sraghuram 			 * If the virtual interface is down, drop the packet.
4595373Sraghuram 			 */
4605373Sraghuram 			if (caller != VSW_LOCALDEV) {
4615935Ssb155480 				vsw_mac_rx(vswp, mrh, mp, VSW_MACRX_FREEMSG);
4625373Sraghuram 			} else {
4635373Sraghuram 				freemsgchain(mp);
4645373Sraghuram 			}
4655373Sraghuram 			continue;
4665373Sraghuram 		}
4675373Sraghuram 
4686419Ssb155480 		/*
4696419Ssb155480 		 * Find fdb entry for the destination
4706419Ssb155480 		 * and hold a reference to it.
4716419Ssb155480 		 */
4726419Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
4736419Ssb155480 		if (fp != NULL) {
4745373Sraghuram 
4755373Sraghuram 			/*
4765373Sraghuram 			 * If plumbed and in promisc mode then copy msg
4775373Sraghuram 			 * and send up the stack.
4785373Sraghuram 			 */
4795935Ssb155480 			vsw_mac_rx(vswp, mrh, mp,
4805935Ssb155480 			    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
4815373Sraghuram 
4825373Sraghuram 			/*
4835373Sraghuram 			 * If the destination is in FDB, the packet
4845373Sraghuram 			 * should be forwarded to the correponding
4855373Sraghuram 			 * vsw_port (connected to a vnet device -
4865373Sraghuram 			 * VSW_VNETPORT)
4875373Sraghuram 			 */
4886419Ssb155480 			(void) vsw_portsend(fp->portp, mp, mpt, count);
4895373Sraghuram 
4906419Ssb155480 			/* Release the reference on the fdb entry */
4916419Ssb155480 			VSW_FDBE_REFRELE(fp);
4925373Sraghuram 		} else {
4935373Sraghuram 			/*
4945373Sraghuram 			 * Destination not in FDB.
4955373Sraghuram 			 *
4965373Sraghuram 			 * If the destination is broadcast or
4975373Sraghuram 			 * multicast forward the packet to all
4985373Sraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
4995373Sraghuram 			 * except the caller.
5005373Sraghuram 			 */
5015373Sraghuram 			if (IS_BROADCAST(ehp)) {
5025935Ssb155480 				D2(vswp, "%s: BROADCAST pkt", __func__);
5035935Ssb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
5045373Sraghuram 			} else if (IS_MULTICAST(ehp)) {
5055935Ssb155480 				D2(vswp, "%s: MULTICAST pkt", __func__);
5065935Ssb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
5075373Sraghuram 			} else {
5085373Sraghuram 				/*
5095373Sraghuram 				 * If the destination is unicast, and came
5105373Sraghuram 				 * from either a logical network device or
5115373Sraghuram 				 * the switch itself when it is plumbed, then
5125373Sraghuram 				 * send it out on the physical device and also
5135373Sraghuram 				 * up the stack if the logical interface is
5145373Sraghuram 				 * in promiscious mode.
5155373Sraghuram 				 *
5165373Sraghuram 				 * NOTE:  The assumption here is that if we
5175373Sraghuram 				 * cannot find the destination in our fdb, its
5185373Sraghuram 				 * a unicast address, and came from either a
5195373Sraghuram 				 * vnet or down the stack (when plumbed) it
5205373Sraghuram 				 * must be destinded for an ethernet device
5215373Sraghuram 				 * outside our ldoms.
5225373Sraghuram 				 */
5235373Sraghuram 				if (caller == VSW_VNETPORT) {
5245373Sraghuram 					/* promisc check copy etc */
5255935Ssb155480 					vsw_mac_rx(vswp, mrh, mp,
5265373Sraghuram 					    VSW_MACRX_PROMISC |
5275373Sraghuram 					    VSW_MACRX_COPYMSG);
5285373Sraghuram 
5295373Sraghuram 					if ((ret_m = vsw_tx_msg(vswp, mp))
5305373Sraghuram 					    != NULL) {
5315373Sraghuram 						DERR(vswp, "%s: drop mblks to "
5325373Sraghuram 						    "phys dev", __func__);
5335373Sraghuram 						freemsgchain(ret_m);
5345373Sraghuram 					}
5355373Sraghuram 
5365373Sraghuram 				} else if (caller == VSW_PHYSDEV) {
5375373Sraghuram 					/*
5385373Sraghuram 					 * Pkt seen because card in promisc
5395373Sraghuram 					 * mode. Send up stack if plumbed in
5405373Sraghuram 					 * promisc mode, else drop it.
5415373Sraghuram 					 */
5425935Ssb155480 					vsw_mac_rx(vswp, mrh, mp,
5435373Sraghuram 					    VSW_MACRX_PROMISC |
5445373Sraghuram 					    VSW_MACRX_FREEMSG);
5455373Sraghuram 
5465373Sraghuram 				} else if (caller == VSW_LOCALDEV) {
5475373Sraghuram 					/*
5485373Sraghuram 					 * Pkt came down the stack, send out
5495373Sraghuram 					 * over physical device.
5505373Sraghuram 					 */
5515373Sraghuram 					if ((ret_m = vsw_tx_msg(vswp, mp))
5525373Sraghuram 					    != NULL) {
5535373Sraghuram 						DERR(vswp, "%s: drop mblks to "
5545373Sraghuram 						    "phys dev", __func__);
5555373Sraghuram 						freemsgchain(ret_m);
5565373Sraghuram 					}
5575373Sraghuram 				}
5585373Sraghuram 			}
5595373Sraghuram 		}
5605373Sraghuram 	}
5615373Sraghuram 	D1(vswp, "%s: exit\n", __func__);
5625373Sraghuram }
5635373Sraghuram 
5645373Sraghuram /*
5655373Sraghuram  * Switch ethernet frame when in layer 3 mode (i.e. using IP
5665373Sraghuram  * layer to do the routing).
5675373Sraghuram  *
5685373Sraghuram  * There is a large amount of overlap between this function and
5695373Sraghuram  * vsw_switch_l2_frame. At some stage we need to revisit and refactor
5705373Sraghuram  * both these functions.
5715373Sraghuram  */
5725373Sraghuram void
5735373Sraghuram vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller,
5745373Sraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
5755373Sraghuram {
5765373Sraghuram 	struct ether_header	*ehp;
5775373Sraghuram 	mblk_t			*bp = NULL;
5785373Sraghuram 	mblk_t			*mpt;
5795935Ssb155480 	uint32_t		count;
5806419Ssb155480 	vsw_fdbe_t		*fp;
5815373Sraghuram 
5825373Sraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
5835373Sraghuram 
5845373Sraghuram 	/*
5855373Sraghuram 	 * In layer 3 mode should only ever be switching packets
5865373Sraghuram 	 * between IP layer and vnet devices. So make sure thats
5875373Sraghuram 	 * who is invoking us.
5885373Sraghuram 	 */
5895373Sraghuram 	if ((caller != VSW_LOCALDEV) && (caller != VSW_VNETPORT)) {
5905373Sraghuram 		DERR(vswp, "%s: unexpected caller (%d)", __func__, caller);
5915373Sraghuram 		freemsgchain(mp);
5925373Sraghuram 		return;
5935373Sraghuram 	}
5945373Sraghuram 
5955373Sraghuram 	/* process the chain of packets */
5965373Sraghuram 	bp = mp;
5975373Sraghuram 	while (bp) {
5985373Sraghuram 		ehp = (struct ether_header *)bp->b_rptr;
5995935Ssb155480 		count = vsw_get_same_dest_list(ehp, &mp, &mpt, &bp);
6005935Ssb155480 		ASSERT(count != 0);
6015373Sraghuram 
6025373Sraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
6035373Sraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
6045373Sraghuram 
6056419Ssb155480 		/*
6066419Ssb155480 		 * Find fdb entry for the destination
6076419Ssb155480 		 * and hold a reference to it.
6086419Ssb155480 		 */
6096419Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
6106419Ssb155480 		if (fp != NULL) {
6115373Sraghuram 
6125373Sraghuram 			D2(vswp, "%s: sending to target port", __func__);
6136419Ssb155480 			(void) vsw_portsend(fp->portp, mp, mpt, count);
6145373Sraghuram 
6156419Ssb155480 			/* Release the reference on the fdb entry */
6166419Ssb155480 			VSW_FDBE_REFRELE(fp);
6175373Sraghuram 		} else {
6185373Sraghuram 			/*
6195373Sraghuram 			 * Destination not in FDB
6205373Sraghuram 			 *
6215373Sraghuram 			 * If the destination is broadcast or
6225373Sraghuram 			 * multicast forward the packet to all
6235373Sraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
6245373Sraghuram 			 * except the caller.
6255373Sraghuram 			 */
6265373Sraghuram 			if (IS_BROADCAST(ehp)) {
6275373Sraghuram 				D2(vswp, "%s: BROADCAST pkt", __func__);
6285935Ssb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
6295373Sraghuram 			} else if (IS_MULTICAST(ehp)) {
6305373Sraghuram 				D2(vswp, "%s: MULTICAST pkt", __func__);
6315935Ssb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
6325373Sraghuram 			} else {
6335373Sraghuram 				/*
6345373Sraghuram 				 * Unicast pkt from vnet that we don't have
6355373Sraghuram 				 * an FDB entry for, so must be destinded for
6365373Sraghuram 				 * the outside world. Attempt to send up to the
6375373Sraghuram 				 * IP layer to allow it to deal with it.
6385373Sraghuram 				 */
6395373Sraghuram 				if (caller == VSW_VNETPORT) {
6405935Ssb155480 					vsw_mac_rx(vswp, mrh,
6415935Ssb155480 					    mp, VSW_MACRX_FREEMSG);
6425373Sraghuram 				}
6435373Sraghuram 			}
6445373Sraghuram 		}
6455373Sraghuram 	}
6465373Sraghuram 
6475373Sraghuram 	D1(vswp, "%s: exit", __func__);
6485373Sraghuram }
6495373Sraghuram 
6505373Sraghuram /*
6515373Sraghuram  * Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV),
6525373Sraghuram  * except the caller (port on which frame arrived).
6535373Sraghuram  */
6545373Sraghuram static int
6555935Ssb155480 vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
6565373Sraghuram {
6575373Sraghuram 	vsw_port_list_t	*plist = &vswp->plist;
6585373Sraghuram 	vsw_port_t	*portp;
6595373Sraghuram 	mblk_t		*nmp = NULL;
6605373Sraghuram 	mblk_t		*ret_m = NULL;
6615373Sraghuram 	int		skip_port = 0;
6625373Sraghuram 
6635373Sraghuram 	D1(vswp, "vsw_forward_all: enter\n");
6645373Sraghuram 
6655373Sraghuram 	/*
6665373Sraghuram 	 * Broadcast message from inside ldoms so send to outside
6675373Sraghuram 	 * world if in either of layer 2 modes.
6685373Sraghuram 	 */
6695373Sraghuram 	if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) ||
6705373Sraghuram 	    (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) &&
6715373Sraghuram 	    ((caller == VSW_LOCALDEV) || (caller == VSW_VNETPORT))) {
6725373Sraghuram 
6735373Sraghuram 		nmp = vsw_dupmsgchain(mp);
6745373Sraghuram 		if (nmp) {
6755373Sraghuram 			if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) {
6765373Sraghuram 				DERR(vswp, "%s: dropping pkt(s) "
6775373Sraghuram 				    "consisting of %ld bytes of data for"
6785373Sraghuram 				    " physical device", __func__, MBLKL(ret_m));
6795373Sraghuram 				freemsgchain(ret_m);
6805373Sraghuram 			}
6815373Sraghuram 		}
6825373Sraghuram 	}
6835373Sraghuram 
6845373Sraghuram 	if (caller == VSW_VNETPORT)
6855373Sraghuram 		skip_port = 1;
6865373Sraghuram 
6875373Sraghuram 	/*
6885373Sraghuram 	 * Broadcast message from other vnet (layer 2 or 3) or outside
6895373Sraghuram 	 * world (layer 2 only), send up stack if plumbed.
6905373Sraghuram 	 */
6915373Sraghuram 	if ((caller == VSW_PHYSDEV) || (caller == VSW_VNETPORT)) {
6925935Ssb155480 		vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_COPYMSG);
6935373Sraghuram 	}
6945373Sraghuram 
6955373Sraghuram 	/* send it to all VNETPORTs */
6965373Sraghuram 	READ_ENTER(&plist->lockrw);
6975373Sraghuram 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
6985373Sraghuram 		D2(vswp, "vsw_forward_all: port %d", portp->p_instance);
6995373Sraghuram 		/*
7005373Sraghuram 		 * Caution ! - don't reorder these two checks as arg
7015373Sraghuram 		 * will be NULL if the caller is PHYSDEV. skip_port is
7025373Sraghuram 		 * only set if caller is VNETPORT.
7035373Sraghuram 		 */
7045373Sraghuram 		if ((skip_port) && (portp == arg)) {
7055373Sraghuram 			continue;
7065373Sraghuram 		} else {
7075373Sraghuram 			nmp = vsw_dupmsgchain(mp);
7085373Sraghuram 			if (nmp) {
7095935Ssb155480 				mblk_t	*mpt = nmp;
7105935Ssb155480 				uint32_t count = 1;
7115373Sraghuram 
7125373Sraghuram 				/* Find tail */
7135373Sraghuram 				while (mpt->b_next != NULL) {
7145373Sraghuram 					mpt = mpt->b_next;
7155935Ssb155480 					count++;
7165373Sraghuram 				}
7175373Sraghuram 				/*
7185373Sraghuram 				 * The plist->lockrw is protecting the
7195373Sraghuram 				 * portp from getting destroyed here.
7205373Sraghuram 				 * So, no ref_cnt is incremented here.
7215373Sraghuram 				 */
7225935Ssb155480 				(void) vsw_portsend(portp, nmp, mpt, count);
7235373Sraghuram 			} else {
7245373Sraghuram 				DERR(vswp, "vsw_forward_all: nmp NULL");
7255373Sraghuram 			}
7265373Sraghuram 		}
7275373Sraghuram 	}
7285373Sraghuram 	RW_EXIT(&plist->lockrw);
7295373Sraghuram 
7305373Sraghuram 	freemsgchain(mp);
7315373Sraghuram 
7325373Sraghuram 	D1(vswp, "vsw_forward_all: exit\n");
7335373Sraghuram 	return (0);
7345373Sraghuram }
7355373Sraghuram 
7365373Sraghuram /*
7375373Sraghuram  * Forward pkts to any devices or interfaces which have registered
7385373Sraghuram  * an interest in them (i.e. multicast groups).
7395373Sraghuram  */
7405373Sraghuram static int
7415935Ssb155480 vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
7425373Sraghuram {
7435373Sraghuram 	struct ether_header	*ehp = (struct ether_header *)mp->b_rptr;
7445373Sraghuram 	mfdb_ent_t		*entp = NULL;
7455373Sraghuram 	mfdb_ent_t		*tpp = NULL;
7465373Sraghuram 	vsw_port_t 		*port;
7475373Sraghuram 	uint64_t		key = 0;
7485373Sraghuram 	mblk_t			*nmp = NULL;
7495373Sraghuram 	mblk_t			*ret_m = NULL;
7505373Sraghuram 	boolean_t		check_if = B_TRUE;
7515373Sraghuram 
7525373Sraghuram 	/*
7535373Sraghuram 	 * Convert address to hash table key
7545373Sraghuram 	 */
7556419Ssb155480 	KEY_HASH(key, &ehp->ether_dhost);
7565373Sraghuram 
7575373Sraghuram 	D1(vswp, "%s: key 0x%llx", __func__, key);
7585373Sraghuram 
7595373Sraghuram 	/*
7605373Sraghuram 	 * If pkt came from either a vnet or down the stack (if we are
7615373Sraghuram 	 * plumbed) and we are in layer 2 mode, then we send the pkt out
7625373Sraghuram 	 * over the physical adapter, and then check to see if any other
7635373Sraghuram 	 * vnets are interested in it.
7645373Sraghuram 	 */
7655373Sraghuram 	if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) ||
7665373Sraghuram 	    (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) &&
7675373Sraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) {
7685373Sraghuram 		nmp = vsw_dupmsgchain(mp);
7695373Sraghuram 		if (nmp) {
7705373Sraghuram 			if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) {
7715373Sraghuram 				DERR(vswp, "%s: dropping pkt(s) consisting of "
7725373Sraghuram 				    "%ld bytes of data for physical device",
7735373Sraghuram 				    __func__, MBLKL(ret_m));
7745373Sraghuram 				freemsgchain(ret_m);
7755373Sraghuram 			}
7765373Sraghuram 		}
7775373Sraghuram 	}
7785373Sraghuram 
7795373Sraghuram 	READ_ENTER(&vswp->mfdbrw);
7805373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key,
7815373Sraghuram 	    (mod_hash_val_t *)&entp) != 0) {
7825373Sraghuram 		D3(vswp, "%s: no table entry found for addr 0x%llx",
7835373Sraghuram 		    __func__, key);
7845373Sraghuram 	} else {
7855373Sraghuram 		/*
7865373Sraghuram 		 * Send to list of devices associated with this address...
7875373Sraghuram 		 */
7885373Sraghuram 		for (tpp = entp; tpp != NULL; tpp = tpp->nextp) {
7895373Sraghuram 
7905373Sraghuram 			/* dont send to ourselves */
7915373Sraghuram 			if ((caller == VSW_VNETPORT) &&
7925373Sraghuram 			    (tpp->d_addr == (void *)arg)) {
7935373Sraghuram 				port = (vsw_port_t *)tpp->d_addr;
7945373Sraghuram 				D3(vswp, "%s: not sending to ourselves"
7955373Sraghuram 				    " : port %d", __func__, port->p_instance);
7965373Sraghuram 				continue;
7975373Sraghuram 
7985373Sraghuram 			} else if ((caller == VSW_LOCALDEV) &&
7995373Sraghuram 			    (tpp->d_type == VSW_LOCALDEV)) {
8005935Ssb155480 				D2(vswp, "%s: not sending back up stack",
8015373Sraghuram 				    __func__);
8025373Sraghuram 				continue;
8035373Sraghuram 			}
8045373Sraghuram 
8055373Sraghuram 			if (tpp->d_type == VSW_VNETPORT) {
8065373Sraghuram 				port = (vsw_port_t *)tpp->d_addr;
8075373Sraghuram 				D3(vswp, "%s: sending to port %ld for addr "
8085373Sraghuram 				    "0x%llx", __func__, port->p_instance, key);
8095373Sraghuram 
8105373Sraghuram 				nmp = vsw_dupmsgchain(mp);
8115373Sraghuram 				if (nmp) {
8125935Ssb155480 					mblk_t	*mpt = nmp;
8135935Ssb155480 					uint32_t count = 1;
8145373Sraghuram 
8155373Sraghuram 					/* Find tail */
8165373Sraghuram 					while (mpt->b_next != NULL) {
8175373Sraghuram 						mpt = mpt->b_next;
8185935Ssb155480 						count++;
8195373Sraghuram 					}
8205373Sraghuram 					/*
8215373Sraghuram 					 * The vswp->mfdbrw is protecting the
8225373Sraghuram 					 * portp from getting destroyed here.
8235373Sraghuram 					 * So, no ref_cnt is incremented here.
8245373Sraghuram 					 */
8255935Ssb155480 					(void) vsw_portsend(port, nmp, mpt,
8265935Ssb155480 					    count);
8275373Sraghuram 				}
8285373Sraghuram 			} else {
8295935Ssb155480 				vsw_mac_rx(vswp, NULL,
8305935Ssb155480 				    mp, VSW_MACRX_COPYMSG);
8315935Ssb155480 				D2(vswp, "%s: sending up stack"
8325373Sraghuram 				    " for addr 0x%llx", __func__, key);
8335373Sraghuram 				check_if = B_FALSE;
8345373Sraghuram 			}
8355373Sraghuram 		}
8365373Sraghuram 	}
8375373Sraghuram 
8385373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
8395373Sraghuram 
8405373Sraghuram 	/*
8415373Sraghuram 	 * If the pkt came from either a vnet or from physical device,
8425373Sraghuram 	 * and if we havent already sent the pkt up the stack then we
8435373Sraghuram 	 * check now if we can/should (i.e. the interface is plumbed
8445373Sraghuram 	 * and in promisc mode).
8455373Sraghuram 	 */
8465373Sraghuram 	if ((check_if) &&
8475373Sraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) {
8485935Ssb155480 		vsw_mac_rx(vswp, NULL, mp,
8495373Sraghuram 		    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
8505373Sraghuram 	}
8515373Sraghuram 
8525373Sraghuram 	freemsgchain(mp);
8535373Sraghuram 
8545373Sraghuram 	D1(vswp, "%s: exit", __func__);
8555373Sraghuram 
8565373Sraghuram 	return (0);
8575373Sraghuram }
8585373Sraghuram 
8595373Sraghuram /*
8606419Ssb155480  * This function creates the vlan id hash table for the given vsw device or
8616419Ssb155480  * port. It then adds each vlan that the device or port has been assigned,
8626419Ssb155480  * into this hash table.
8636419Ssb155480  * Arguments:
8646419Ssb155480  *   arg:  vsw device or port.
8656419Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
8666419Ssb155480  */
8676419Ssb155480 void
8686419Ssb155480 vsw_create_vlans(void *arg, int type)
8696419Ssb155480 {
8706419Ssb155480 	/* create vlan hash table */
8716419Ssb155480 	vsw_vlan_create_hash(arg, type);
8726419Ssb155480 
8736419Ssb155480 	/* add vlan ids of the vsw device into its hash table */
8746419Ssb155480 	vsw_vlan_add_ids(arg, type);
8756419Ssb155480 }
8766419Ssb155480 
8776419Ssb155480 /*
8786419Ssb155480  * This function removes the vlan ids of the vsw device or port from its hash
8796419Ssb155480  * table. It then destroys the vlan hash table.
8806419Ssb155480  * Arguments:
8816419Ssb155480  *   arg:  vsw device or port.
8826419Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
8836419Ssb155480  */
8846419Ssb155480 void
8856419Ssb155480 vsw_destroy_vlans(void *arg, int type)
8866419Ssb155480 {
8876419Ssb155480 	/* remove vlan ids from the hash table */
8886419Ssb155480 	vsw_vlan_remove_ids(arg, type);
8896419Ssb155480 
8906419Ssb155480 	/* destroy vlan-hash-table */
8916419Ssb155480 	vsw_vlan_destroy_hash(arg, type);
8926419Ssb155480 }
8936419Ssb155480 
8946419Ssb155480 /*
8956419Ssb155480  * Create a vlan-id hash table for the given vsw device or port.
8966419Ssb155480  */
8976419Ssb155480 static void
8986419Ssb155480 vsw_vlan_create_hash(void *arg, int type)
8996419Ssb155480 {
9006419Ssb155480 	char		hashname[MAXNAMELEN];
9016419Ssb155480 
9026419Ssb155480 	if (type == VSW_LOCALDEV) {
9036419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9046419Ssb155480 
9056419Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "vsw%d-vlan-hash",
9066419Ssb155480 		    vswp->instance);
9076419Ssb155480 
9086419Ssb155480 		vswp->vlan_nchains = vsw_vlan_nchains;
9096419Ssb155480 		vswp->vlan_hashp = mod_hash_create_idhash(hashname,
9106419Ssb155480 		    vswp->vlan_nchains, mod_hash_null_valdtor);
9116419Ssb155480 
9126419Ssb155480 	} else if (type == VSW_VNETPORT) {
9136419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
9146419Ssb155480 
9156419Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash",
9166419Ssb155480 		    portp->p_instance);
9176419Ssb155480 
9186419Ssb155480 		portp->vlan_nchains = vsw_vlan_nchains;
9196419Ssb155480 		portp->vlan_hashp = mod_hash_create_idhash(hashname,
9206419Ssb155480 		    portp->vlan_nchains, mod_hash_null_valdtor);
9216419Ssb155480 
9226419Ssb155480 	} else {
9236419Ssb155480 		return;
9246419Ssb155480 	}
9256419Ssb155480 }
9266419Ssb155480 
9276419Ssb155480 /*
9286419Ssb155480  * Destroy the vlan-id hash table for the given vsw device or port.
9296419Ssb155480  */
9306419Ssb155480 static void
9316419Ssb155480 vsw_vlan_destroy_hash(void *arg, int type)
9326419Ssb155480 {
9336419Ssb155480 	if (type == VSW_LOCALDEV) {
9346419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9356419Ssb155480 
9366419Ssb155480 		mod_hash_destroy_hash(vswp->vlan_hashp);
9376419Ssb155480 		vswp->vlan_nchains = 0;
9386419Ssb155480 	} else if (type == VSW_VNETPORT) {
9396419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
9406419Ssb155480 
9416419Ssb155480 		mod_hash_destroy_hash(portp->vlan_hashp);
9426419Ssb155480 		portp->vlan_nchains = 0;
9436419Ssb155480 	} else {
9446419Ssb155480 		return;
9456419Ssb155480 	}
9466419Ssb155480 }
9476419Ssb155480 
9486419Ssb155480 /*
9496419Ssb155480  * Add vlan ids of the given vsw device or port into its hash table.
9505373Sraghuram  */
9516419Ssb155480 void
9526419Ssb155480 vsw_vlan_add_ids(void *arg, int type)
9536419Ssb155480 {
9546419Ssb155480 	int	rv;
9556419Ssb155480 	int	i;
9566419Ssb155480 
9576419Ssb155480 	if (type == VSW_LOCALDEV) {
9586419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9596419Ssb155480 
9606419Ssb155480 		rv = mod_hash_insert(vswp->vlan_hashp,
9616419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
9626419Ssb155480 		    (mod_hash_val_t)B_TRUE);
9636419Ssb155480 		ASSERT(rv == 0);
9646419Ssb155480 
9656419Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
9666419Ssb155480 			rv = mod_hash_insert(vswp->vlan_hashp,
9676419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i]),
9686419Ssb155480 			    (mod_hash_val_t)B_TRUE);
9696419Ssb155480 			ASSERT(rv == 0);
9706419Ssb155480 		}
9716419Ssb155480 
9726419Ssb155480 	} else if (type == VSW_VNETPORT) {
9736419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
9746419Ssb155480 
9756419Ssb155480 		rv = mod_hash_insert(portp->vlan_hashp,
9766419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
9776419Ssb155480 		    (mod_hash_val_t)B_TRUE);
9786419Ssb155480 		ASSERT(rv == 0);
9796419Ssb155480 
9806419Ssb155480 		for (i = 0; i < portp->nvids; i++) {
9816419Ssb155480 			rv = mod_hash_insert(portp->vlan_hashp,
9826419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
9836419Ssb155480 			    (mod_hash_val_t)B_TRUE);
9846419Ssb155480 			ASSERT(rv == 0);
9856419Ssb155480 		}
9866419Ssb155480 
9876419Ssb155480 	} else {
9886419Ssb155480 		return;
9896419Ssb155480 	}
9906419Ssb155480 }
9916419Ssb155480 
9926419Ssb155480 /*
9936419Ssb155480  * Remove vlan ids of the given vsw device or port from its hash table.
9946419Ssb155480  */
9956419Ssb155480 void
9966419Ssb155480 vsw_vlan_remove_ids(void *arg, int type)
9976419Ssb155480 {
9986419Ssb155480 	mod_hash_val_t	vp;
9996419Ssb155480 	int		rv;
10006419Ssb155480 	int		i;
10016419Ssb155480 
10026419Ssb155480 	if (type == VSW_LOCALDEV) {
10036419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
10046419Ssb155480 
10056419Ssb155480 		rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->pvid);
10066419Ssb155480 		if (rv == B_TRUE) {
10076419Ssb155480 			rv = mod_hash_remove(vswp->vlan_hashp,
10086419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
10096419Ssb155480 			    (mod_hash_val_t *)&vp);
10106419Ssb155480 			ASSERT(rv == 0);
10116419Ssb155480 		}
10126419Ssb155480 
10136419Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
10146419Ssb155480 			rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->vids[i]);
10156419Ssb155480 			if (rv == B_TRUE) {
10166419Ssb155480 				rv = mod_hash_remove(vswp->vlan_hashp,
10176419Ssb155480 				    (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i]),
10186419Ssb155480 				    (mod_hash_val_t *)&vp);
10196419Ssb155480 				ASSERT(rv == 0);
10206419Ssb155480 			}
10216419Ssb155480 		}
10226419Ssb155480 
10236419Ssb155480 	} else if (type == VSW_VNETPORT) {
10246419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
10256419Ssb155480 
10266419Ssb155480 		portp = (vsw_port_t *)arg;
10276419Ssb155480 		rv = vsw_vlan_lookup(portp->vlan_hashp, portp->pvid);
10286419Ssb155480 		if (rv == B_TRUE) {
10296419Ssb155480 			rv = mod_hash_remove(portp->vlan_hashp,
10306419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
10316419Ssb155480 			    (mod_hash_val_t *)&vp);
10326419Ssb155480 			ASSERT(rv == 0);
10336419Ssb155480 		}
10346419Ssb155480 
10356419Ssb155480 		for (i = 0; i < portp->nvids; i++) {
10366419Ssb155480 			rv = vsw_vlan_lookup(portp->vlan_hashp, portp->vids[i]);
10376419Ssb155480 			if (rv == B_TRUE) {
10386419Ssb155480 				rv = mod_hash_remove(portp->vlan_hashp,
10396419Ssb155480 				    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
10406419Ssb155480 				    (mod_hash_val_t *)&vp);
10416419Ssb155480 				ASSERT(rv == 0);
10426419Ssb155480 			}
10436419Ssb155480 		}
10446419Ssb155480 
10456419Ssb155480 	} else {
10466419Ssb155480 		return;
10476419Ssb155480 	}
10486419Ssb155480 }
10496419Ssb155480 
10506419Ssb155480 /*
10516419Ssb155480  * Find the given vlan id in the hash table.
10526419Ssb155480  * Return: B_TRUE if the id is found; B_FALSE if not found.
10536419Ssb155480  */
10546419Ssb155480 boolean_t
10556419Ssb155480 vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid)
10566419Ssb155480 {
10576419Ssb155480 	int		rv;
10586419Ssb155480 	mod_hash_val_t	vp;
10596419Ssb155480 
10606419Ssb155480 	rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp);
10616419Ssb155480 
10626419Ssb155480 	if (rv != 0)
10636419Ssb155480 		return (B_FALSE);
10646419Ssb155480 
10656419Ssb155480 	return (B_TRUE);
10666419Ssb155480 }
10676419Ssb155480 
10686419Ssb155480 /*
10696419Ssb155480  * Add an entry into FDB for the given vsw.
10706419Ssb155480  */
10716419Ssb155480 void
10726419Ssb155480 vsw_fdbe_add(vsw_t *vswp, void *port)
10735373Sraghuram {
10745373Sraghuram 	uint64_t	addr = 0;
10756419Ssb155480 	vsw_port_t	*portp;
10766419Ssb155480 	vsw_fdbe_t	*fp;
10776419Ssb155480 	int		rv;
10785373Sraghuram 
10796419Ssb155480 	portp = (vsw_port_t *)port;
10806419Ssb155480 	KEY_HASH(addr, &portp->p_macaddr);
10815373Sraghuram 
10826419Ssb155480 	fp = kmem_zalloc(sizeof (vsw_fdbe_t), KM_SLEEP);
10836419Ssb155480 	fp->portp = port;
10845373Sraghuram 
10855373Sraghuram 	/*
10865373Sraghuram 	 * Note: duplicate keys will be rejected by mod_hash.
10875373Sraghuram 	 */
10886419Ssb155480 	rv = mod_hash_insert(vswp->fdb_hashp, (mod_hash_key_t)addr,
10896419Ssb155480 	    (mod_hash_val_t)fp);
10906419Ssb155480 	ASSERT(rv == 0);
10915373Sraghuram }
10925373Sraghuram 
10935373Sraghuram /*
10945373Sraghuram  * Remove an entry from FDB.
10955373Sraghuram  */
10966419Ssb155480 void
10976419Ssb155480 vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr)
10985373Sraghuram {
10995373Sraghuram 	uint64_t	addr = 0;
11006419Ssb155480 	vsw_fdbe_t	*fp;
11016419Ssb155480 	int		rv;
11025373Sraghuram 
11036419Ssb155480 	KEY_HASH(addr, eaddr);
11046419Ssb155480 
11056419Ssb155480 	/*
11066419Ssb155480 	 * Remove the entry from fdb hash table.
11076419Ssb155480 	 * This prevents further references to this fdb entry.
11086419Ssb155480 	 */
11096419Ssb155480 	rv = mod_hash_remove(vswp->fdb_hashp, (mod_hash_key_t)addr,
11106419Ssb155480 	    (mod_hash_val_t *)&fp);
11116419Ssb155480 	if (rv != 0) {
11126419Ssb155480 		/* invalid key? */
11136419Ssb155480 		return;
11146419Ssb155480 	}
11156419Ssb155480 
11166419Ssb155480 	/*
11176419Ssb155480 	 * If there are threads already ref holding before the entry was
11186419Ssb155480 	 * removed from hash table, then wait for ref count to drop to zero.
11196419Ssb155480 	 */
11206419Ssb155480 	while (fp->refcnt != 0) {
11216419Ssb155480 		delay(drv_usectohz(vsw_fdbe_refcnt_delay));
11226419Ssb155480 	}
11236419Ssb155480 
11246419Ssb155480 	kmem_free(fp, sizeof (*fp));
11256419Ssb155480 }
11265373Sraghuram 
11276419Ssb155480 /*
11286419Ssb155480  * Search fdb for a given mac address. If an entry is found, hold
11296419Ssb155480  * a reference to it and return the entry, else returns NULL.
11306419Ssb155480  */
11316419Ssb155480 static vsw_fdbe_t *
11326419Ssb155480 vsw_fdbe_find(vsw_t *vswp, struct ether_addr *addrp)
11336419Ssb155480 {
11346419Ssb155480 	uint64_t	key = 0;
11356419Ssb155480 	vsw_fdbe_t	*fp;
11366419Ssb155480 	int		rv;
11376419Ssb155480 
11386419Ssb155480 	KEY_HASH(key, addrp);
11396419Ssb155480 
11406419Ssb155480 	rv = mod_hash_find_cb(vswp->fdb_hashp, (mod_hash_key_t)key,
11416419Ssb155480 	    (mod_hash_val_t *)&fp, vsw_fdbe_find_cb);
11426419Ssb155480 
11436419Ssb155480 	if (rv != 0)
11446419Ssb155480 		return (NULL);
11456419Ssb155480 
11466419Ssb155480 	return (fp);
11476419Ssb155480 }
11486419Ssb155480 
11496419Ssb155480 /*
11506419Ssb155480  * Callback function provided to mod_hash_find_cb(). After finding the fdb
11516419Ssb155480  * entry corresponding to the key (macaddr), this callback will be invoked by
11526419Ssb155480  * mod_hash_find_cb() to atomically increment the reference count on the fdb
11536419Ssb155480  * entry before returning the found entry.
11546419Ssb155480  */
11556419Ssb155480 static void
11566419Ssb155480 vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
11576419Ssb155480 {
11586419Ssb155480 	_NOTE(ARGUNUSED(key))
11596419Ssb155480 	VSW_FDBE_REFHOLD((vsw_fdbe_t *)val);
11606419Ssb155480 }
11615373Sraghuram 
11626419Ssb155480 /*
11636419Ssb155480  * A given frame must be always tagged with the appropriate vlan id (unless it
11646419Ssb155480  * is in the default-vlan) before the mac address switching function is called.
11656419Ssb155480  * Otherwise, after switching function determines the destination, we cannot
11666419Ssb155480  * figure out if the destination belongs to the the same vlan that the frame
11676419Ssb155480  * originated from and if it needs tag/untag. Frames which are inbound from
11686419Ssb155480  * the external(physical) network over a vlan trunk link are always tagged.
11696419Ssb155480  * However frames which are received from a vnet-port over ldc or frames which
11706419Ssb155480  * are coming down the stack on the service domain over vsw interface may be
11716419Ssb155480  * untagged. These frames must be tagged with the appropriate pvid of the
11726419Ssb155480  * sender (vnet-port or vsw device), before invoking the switching function.
11736419Ssb155480  *
11746419Ssb155480  * Arguments:
11756419Ssb155480  *   arg:    caller of the function.
11766419Ssb155480  *   type:   type of arg(caller): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
11776419Ssb155480  *   mp:     frame(s) to be tagged.
11786419Ssb155480  */
11796419Ssb155480 mblk_t *
11806419Ssb155480 vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp)
11816419Ssb155480 {
11826419Ssb155480 	vsw_t			*vswp;
11836419Ssb155480 	vsw_port_t		*portp;
11846419Ssb155480 	struct ether_header	*ehp;
11856419Ssb155480 	mblk_t			*bp;
11866419Ssb155480 	mblk_t			*bpt;
11876419Ssb155480 	mblk_t			*bph;
11886419Ssb155480 	mblk_t			*bpn;
11896419Ssb155480 	uint16_t		pvid;
11905373Sraghuram 
11916419Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
11925373Sraghuram 
11936419Ssb155480 	if (type == VSW_LOCALDEV) {
11946419Ssb155480 		vswp = (vsw_t *)arg;
11956419Ssb155480 		pvid = vswp->pvid;
11966419Ssb155480 		portp = NULL;
11976419Ssb155480 	} else {
11986419Ssb155480 		/* VSW_VNETPORT */
11996419Ssb155480 		portp = (vsw_port_t *)arg;
12006419Ssb155480 		pvid = portp->pvid;
12016419Ssb155480 		vswp = portp->p_vswp;
12026419Ssb155480 	}
12036419Ssb155480 
12046419Ssb155480 	bpn = bph = bpt = NULL;
12056419Ssb155480 
12066419Ssb155480 	for (bp = mp; bp != NULL; bp = bpn) {
12076419Ssb155480 
12086419Ssb155480 		bpn = bp->b_next;
12096419Ssb155480 		bp->b_next = bp->b_prev = NULL;
12106419Ssb155480 
12116419Ssb155480 		/* Determine if it is an untagged frame */
12126419Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
12135373Sraghuram 
12146419Ssb155480 		if (ehp->ether_type != ETHERTYPE_VLAN) {	/* untagged */
12156419Ssb155480 
12166419Ssb155480 			/* no need to tag if the frame is in default vlan */
12176419Ssb155480 			if (pvid != vswp->default_vlan_id) {
12186419Ssb155480 				bp = vnet_vlan_insert_tag(bp, pvid);
12196419Ssb155480 				if (bp == NULL) {
12206419Ssb155480 					continue;
12216419Ssb155480 				}
12226419Ssb155480 			}
12236419Ssb155480 		}
12246419Ssb155480 
12256419Ssb155480 		/* build a chain of processed packets */
12266419Ssb155480 		if (bph == NULL) {
12276419Ssb155480 			bph = bpt = bp;
12286419Ssb155480 		} else {
12296419Ssb155480 			bpt->b_next = bp;
12306419Ssb155480 			bpt = bp;
12316419Ssb155480 		}
12326419Ssb155480 
12336419Ssb155480 	}
12346419Ssb155480 
12356419Ssb155480 	return (bph);
12365373Sraghuram }
12375373Sraghuram 
12385373Sraghuram /*
12396419Ssb155480  * Frames destined to a vnet-port or to the local vsw interface, must be
12406419Ssb155480  * untagged if necessary before sending. This function first checks that the
12416419Ssb155480  * frame can be sent to the destination in the vlan identified by the frame
12426419Ssb155480  * tag. Note that when this function is invoked the frame must have been
12436419Ssb155480  * already tagged (unless it is in the default-vlan). Because, this function is
12446419Ssb155480  * called when the switching function determines the destination and invokes
12456419Ssb155480  * its send function (vnet-port or vsw interface) and all frames would have
12466419Ssb155480  * been tagged by this time (see comments in vsw_vlan_frame_pretag()).
12476419Ssb155480  *
12486419Ssb155480  * Arguments:
12496419Ssb155480  *   arg:    destination device.
12506419Ssb155480  *   type:   type of arg(destination): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
12516419Ssb155480  *   np:     head of pkt chain to be validated and untagged.
12526419Ssb155480  *   npt:    tail of pkt chain to be validated and untagged.
12536419Ssb155480  *
12546419Ssb155480  * Returns:
12556419Ssb155480  *   np:     head of updated chain of packets
12566419Ssb155480  *   npt:    tail of updated chain of packets
12576419Ssb155480  *   rv:     count of any packets dropped
12585373Sraghuram  */
12596419Ssb155480 uint32_t
12606419Ssb155480 vsw_vlan_frame_untag(void *arg, int type, mblk_t **np, mblk_t **npt)
12615373Sraghuram {
12626419Ssb155480 	mblk_t			*bp;
12636419Ssb155480 	mblk_t			*bpt;
12646419Ssb155480 	mblk_t			*bph;
12656419Ssb155480 	mblk_t			*bpn;
12666419Ssb155480 	vsw_port_t		*portp;
12676419Ssb155480 	vsw_t			*vswp;
12686419Ssb155480 	uint32_t		count;
12696419Ssb155480 	struct ether_header	*ehp;
12706419Ssb155480 	boolean_t		is_tagged;
12716419Ssb155480 	boolean_t		rv;
12726419Ssb155480 	uint16_t		vlan_id;
12736419Ssb155480 	uint16_t		pvid;
12746419Ssb155480 	mod_hash_t		*vlan_hashp;
12755373Sraghuram 
12766419Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
12775373Sraghuram 
12786419Ssb155480 	if (type == VSW_LOCALDEV) {
12796419Ssb155480 		vswp = (vsw_t *)arg;
12806419Ssb155480 		pvid = vswp->pvid;
12816419Ssb155480 		vlan_hashp = vswp->vlan_hashp;
12826419Ssb155480 		portp = NULL;
12836419Ssb155480 	} else {
12846419Ssb155480 		/* type == VSW_VNETPORT */
12856419Ssb155480 		portp = (vsw_port_t *)arg;
12866419Ssb155480 		vswp = portp->p_vswp;
12876419Ssb155480 		vlan_hashp = portp->vlan_hashp;
12886419Ssb155480 		pvid = portp->pvid;
12895373Sraghuram 	}
12905373Sraghuram 
12916419Ssb155480 	bpn = bph = bpt = NULL;
12926419Ssb155480 	count = 0;
12936419Ssb155480 
12946419Ssb155480 	for (bp = *np; bp != NULL; bp = bpn) {
12956419Ssb155480 
12966419Ssb155480 		bpn = bp->b_next;
12976419Ssb155480 		bp->b_next = bp->b_prev = NULL;
12986419Ssb155480 
12996419Ssb155480 		/*
13006419Ssb155480 		 * Determine the vlan id that the frame belongs to.
13016419Ssb155480 		 */
13026419Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
13036419Ssb155480 		is_tagged = vsw_frame_lookup_vid(arg, type, ehp, &vlan_id);
13046419Ssb155480 
13056419Ssb155480 		/*
13066419Ssb155480 		 * Check if the destination is in the same vlan.
13076419Ssb155480 		 */
13086419Ssb155480 		rv = vsw_vlan_lookup(vlan_hashp, vlan_id);
13096419Ssb155480 		if (rv == B_FALSE) {
13106419Ssb155480 			/* drop the packet */
13116419Ssb155480 			freemsg(bp);
13126419Ssb155480 			count++;
13136419Ssb155480 			continue;
13146419Ssb155480 		}
13156419Ssb155480 
13166419Ssb155480 		/*
13176419Ssb155480 		 * Check the frame header if tag/untag is  needed.
13186419Ssb155480 		 */
13196419Ssb155480 		if (is_tagged == B_FALSE) {
13206419Ssb155480 			/*
13216419Ssb155480 			 * Untagged frame. We shouldn't have an untagged
13226419Ssb155480 			 * packet at this point, unless the destination's
13236419Ssb155480 			 * vlan id is default-vlan-id; if it is not the
13246419Ssb155480 			 * default-vlan-id, we drop the packet.
13256419Ssb155480 			 */
13266419Ssb155480 			if (vlan_id != vswp->default_vlan_id) {
13276419Ssb155480 				/* drop the packet */
13286419Ssb155480 				freemsg(bp);
13296419Ssb155480 				count++;
13306419Ssb155480 				continue;
13316419Ssb155480 			}
13326419Ssb155480 		} else {
13336419Ssb155480 			/*
13346419Ssb155480 			 * Tagged frame, untag if it's the destination's pvid.
13356419Ssb155480 			 */
13366419Ssb155480 			if (vlan_id == pvid) {
13376419Ssb155480 
13386419Ssb155480 				bp = vnet_vlan_remove_tag(bp);
13396419Ssb155480 				if (bp == NULL) {
13406419Ssb155480 					/* packet dropped */
13416419Ssb155480 					count++;
13426419Ssb155480 					continue;
13436419Ssb155480 				}
13446419Ssb155480 			}
13456419Ssb155480 		}
13465373Sraghuram 
13476419Ssb155480 		/* build a chain of processed packets */
13486419Ssb155480 		if (bph == NULL) {
13496419Ssb155480 			bph = bpt = bp;
13506419Ssb155480 		} else {
13516419Ssb155480 			bpt->b_next = bp;
13526419Ssb155480 			bpt = bp;
13536419Ssb155480 		}
13546419Ssb155480 
13556419Ssb155480 	}
13566419Ssb155480 
13576419Ssb155480 	*np = bph;
13586419Ssb155480 	*npt = bpt;
13596419Ssb155480 
13606419Ssb155480 	return (count);
13616419Ssb155480 }
13626419Ssb155480 
13636419Ssb155480 /*
13646419Ssb155480  * Lookup the vlan id of the given frame. If it is a vlan-tagged frame,
13656419Ssb155480  * then the vlan-id is available in the tag; otherwise, its vlan id is
13666419Ssb155480  * implicitly obtained based on the caller (destination of the frame:
13676419Ssb155480  * VSW_VNETPORT or VSW_LOCALDEV).
13686419Ssb155480  * The vlan id determined is returned in vidp.
13696419Ssb155480  * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
13706419Ssb155480  */
13716419Ssb155480 boolean_t
13726419Ssb155480 vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp,
13736419Ssb155480 	uint16_t *vidp)
13746419Ssb155480 {
13756419Ssb155480 	struct ether_vlan_header	*evhp;
13766419Ssb155480 	vsw_t				*vswp;
13776419Ssb155480 	vsw_port_t			*portp;
13786419Ssb155480 
13796419Ssb155480 	/* If it's a tagged frame, get the vid from vlan header */
13806419Ssb155480 	if (ehp->ether_type == ETHERTYPE_VLAN) {
13816419Ssb155480 
13826419Ssb155480 		evhp = (struct ether_vlan_header *)ehp;
13836419Ssb155480 		*vidp = VLAN_ID(ntohs(evhp->ether_tci));
13846419Ssb155480 		return (B_TRUE);
13856419Ssb155480 	}
13866419Ssb155480 
13876419Ssb155480 	/* Untagged frame; determine vlan id based on caller */
13886419Ssb155480 	switch (caller) {
13896419Ssb155480 
13906419Ssb155480 	case VSW_VNETPORT:
13916419Ssb155480 		/*
13926419Ssb155480 		 * packet destined to a vnet; vlan-id is pvid of vnet-port.
13936419Ssb155480 		 */
13946419Ssb155480 		portp = (vsw_port_t *)arg;
13956419Ssb155480 		*vidp = portp->pvid;
13966419Ssb155480 		break;
13976419Ssb155480 
13986419Ssb155480 	case VSW_LOCALDEV:
13996419Ssb155480 
14006419Ssb155480 		/*
14016419Ssb155480 		 * packet destined to vsw interface;
14026419Ssb155480 		 * vlan-id is port-vlan-id of vsw device.
14036419Ssb155480 		 */
14046419Ssb155480 		vswp = (vsw_t *)arg;
14056419Ssb155480 		*vidp = vswp->pvid;
14066419Ssb155480 		break;
14076419Ssb155480 	}
14086419Ssb155480 
14096419Ssb155480 	return (B_FALSE);
14105373Sraghuram }
14115373Sraghuram 
14125373Sraghuram /*
14135373Sraghuram  * Add or remove multicast address(es).
14145373Sraghuram  *
14155373Sraghuram  * Returns 0 on success, 1 on failure.
14165373Sraghuram  */
14175373Sraghuram int
14185373Sraghuram vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port)
14195373Sraghuram {
14205373Sraghuram 	mcst_addr_t		*mcst_p = NULL;
14215373Sraghuram 	vsw_t			*vswp = port->p_vswp;
14225373Sraghuram 	uint64_t		addr = 0x0;
14235373Sraghuram 	int			i;
14245373Sraghuram 
14255373Sraghuram 	D1(vswp, "%s: enter", __func__);
14265373Sraghuram 
14275373Sraghuram 	D2(vswp, "%s: %d addresses", __func__, mcst_pkt->count);
14285373Sraghuram 
14295373Sraghuram 	for (i = 0; i < mcst_pkt->count; i++) {
14305373Sraghuram 		/*
14315373Sraghuram 		 * Convert address into form that can be used
14325373Sraghuram 		 * as hash table key.
14335373Sraghuram 		 */
14346419Ssb155480 		KEY_HASH(addr, &(mcst_pkt->mca[i]));
14355373Sraghuram 
14365373Sraghuram 		/*
14375373Sraghuram 		 * Add or delete the specified address/port combination.
14385373Sraghuram 		 */
14395373Sraghuram 		if (mcst_pkt->set == 0x1) {
14405373Sraghuram 			D3(vswp, "%s: adding multicast address 0x%llx for "
14415373Sraghuram 			    "port %ld", __func__, addr, port->p_instance);
14425373Sraghuram 			if (vsw_add_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
14435373Sraghuram 				/*
14445373Sraghuram 				 * Update the list of multicast
14455373Sraghuram 				 * addresses contained within the
14465373Sraghuram 				 * port structure to include this new
14475373Sraghuram 				 * one.
14485373Sraghuram 				 */
14495373Sraghuram 				mcst_p = kmem_zalloc(sizeof (mcst_addr_t),
14505373Sraghuram 				    KM_NOSLEEP);
14515373Sraghuram 				if (mcst_p == NULL) {
14525373Sraghuram 					DERR(vswp, "%s: unable to alloc mem",
14535373Sraghuram 					    __func__);
14545373Sraghuram 					(void) vsw_del_mcst(vswp,
14555373Sraghuram 					    VSW_VNETPORT, addr, port);
14565373Sraghuram 					return (1);
14575373Sraghuram 				}
14585373Sraghuram 
14595373Sraghuram 				mcst_p->nextp = NULL;
14605373Sraghuram 				mcst_p->addr = addr;
14615373Sraghuram 				ether_copy(&mcst_pkt->mca[i], &mcst_p->mca);
14625373Sraghuram 
14635373Sraghuram 				/*
14645373Sraghuram 				 * Program the address into HW. If the addr
14655373Sraghuram 				 * has already been programmed then the MAC
14665373Sraghuram 				 * just increments a ref counter (which is
14675373Sraghuram 				 * used when the address is being deleted)
14685373Sraghuram 				 */
14695373Sraghuram 				mutex_enter(&vswp->mac_lock);
14705373Sraghuram 				if (vswp->mh != NULL) {
14715373Sraghuram 					if (mac_multicst_add(vswp->mh,
14725373Sraghuram 					    (uchar_t *)&mcst_pkt->mca[i])) {
14735373Sraghuram 						mutex_exit(&vswp->mac_lock);
14745373Sraghuram 						cmn_err(CE_WARN, "!vsw%d: "
14755373Sraghuram 						    "unable to add multicast "
14765373Sraghuram 						    "address: %s\n",
14775373Sraghuram 						    vswp->instance,
14785373Sraghuram 						    ether_sprintf((void *)
14795373Sraghuram 						    &mcst_p->mca));
14805373Sraghuram 						(void) vsw_del_mcst(vswp,
14815373Sraghuram 						    VSW_VNETPORT, addr, port);
14825373Sraghuram 						kmem_free(mcst_p,
14835373Sraghuram 						    sizeof (*mcst_p));
14845373Sraghuram 						return (1);
14855373Sraghuram 					}
14865373Sraghuram 					mcst_p->mac_added = B_TRUE;
14875373Sraghuram 				}
14885373Sraghuram 				mutex_exit(&vswp->mac_lock);
14895373Sraghuram 
14905373Sraghuram 				mutex_enter(&port->mca_lock);
14915373Sraghuram 				mcst_p->nextp = port->mcap;
14925373Sraghuram 				port->mcap = mcst_p;
14935373Sraghuram 				mutex_exit(&port->mca_lock);
14945373Sraghuram 
14955373Sraghuram 			} else {
14965373Sraghuram 				DERR(vswp, "%s: error adding multicast "
14975373Sraghuram 				    "address 0x%llx for port %ld",
14985373Sraghuram 				    __func__, addr, port->p_instance);
14995373Sraghuram 				return (1);
15005373Sraghuram 			}
15015373Sraghuram 		} else {
15025373Sraghuram 			/*
15035373Sraghuram 			 * Delete an entry from the multicast hash
15045373Sraghuram 			 * table and update the address list
15055373Sraghuram 			 * appropriately.
15065373Sraghuram 			 */
15075373Sraghuram 			if (vsw_del_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
15085373Sraghuram 				D3(vswp, "%s: deleting multicast address "
15095373Sraghuram 				    "0x%llx for port %ld", __func__, addr,
15105373Sraghuram 				    port->p_instance);
15115373Sraghuram 
15125373Sraghuram 				mcst_p = vsw_del_addr(VSW_VNETPORT, port, addr);
15135373Sraghuram 				ASSERT(mcst_p != NULL);
15145373Sraghuram 
15155373Sraghuram 				/*
15165373Sraghuram 				 * Remove the address from HW. The address
15175373Sraghuram 				 * will actually only be removed once the ref
15185373Sraghuram 				 * count within the MAC layer has dropped to
15195373Sraghuram 				 * zero. I.e. we can safely call this fn even
15205373Sraghuram 				 * if other ports are interested in this
15215373Sraghuram 				 * address.
15225373Sraghuram 				 */
15235373Sraghuram 				mutex_enter(&vswp->mac_lock);
15245373Sraghuram 				if (vswp->mh != NULL && mcst_p->mac_added) {
15255373Sraghuram 					if (mac_multicst_remove(vswp->mh,
15265373Sraghuram 					    (uchar_t *)&mcst_pkt->mca[i])) {
15275373Sraghuram 						mutex_exit(&vswp->mac_lock);
15285373Sraghuram 						cmn_err(CE_WARN, "!vsw%d: "
15295373Sraghuram 						    "unable to remove mcast "
15305373Sraghuram 						    "address: %s\n",
15315373Sraghuram 						    vswp->instance,
15325373Sraghuram 						    ether_sprintf((void *)
15335373Sraghuram 						    &mcst_p->mca));
15345373Sraghuram 						kmem_free(mcst_p,
15355373Sraghuram 						    sizeof (*mcst_p));
15365373Sraghuram 						return (1);
15375373Sraghuram 					}
15385373Sraghuram 					mcst_p->mac_added = B_FALSE;
15395373Sraghuram 				}
15405373Sraghuram 				mutex_exit(&vswp->mac_lock);
15415373Sraghuram 				kmem_free(mcst_p, sizeof (*mcst_p));
15425373Sraghuram 
15435373Sraghuram 			} else {
15445373Sraghuram 				DERR(vswp, "%s: error deleting multicast "
15455373Sraghuram 				    "addr 0x%llx for port %ld",
15465373Sraghuram 				    __func__, addr, port->p_instance);
15475373Sraghuram 				return (1);
15485373Sraghuram 			}
15495373Sraghuram 		}
15505373Sraghuram 	}
15515373Sraghuram 	D1(vswp, "%s: exit", __func__);
15525373Sraghuram 	return (0);
15535373Sraghuram }
15545373Sraghuram 
15555373Sraghuram /*
15565373Sraghuram  * Add a new multicast entry.
15575373Sraghuram  *
15585373Sraghuram  * Search hash table based on address. If match found then
15595373Sraghuram  * update associated val (which is chain of ports), otherwise
15605373Sraghuram  * create new key/val (addr/port) pair and insert into table.
15615373Sraghuram  */
15625373Sraghuram int
15635373Sraghuram vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
15645373Sraghuram {
15655373Sraghuram 	int		dup = 0;
15665373Sraghuram 	int		rv = 0;
15675373Sraghuram 	mfdb_ent_t	*ment = NULL;
15685373Sraghuram 	mfdb_ent_t	*tmp_ent = NULL;
15695373Sraghuram 	mfdb_ent_t	*new_ent = NULL;
15705373Sraghuram 	void		*tgt = NULL;
15715373Sraghuram 
15725373Sraghuram 	if (devtype == VSW_VNETPORT) {
15735373Sraghuram 		/*
15745373Sraghuram 		 * Being invoked from a vnet.
15755373Sraghuram 		 */
15765373Sraghuram 		ASSERT(arg != NULL);
15775373Sraghuram 		tgt = arg;
15785373Sraghuram 		D2(NULL, "%s: port %d : address 0x%llx", __func__,
15795373Sraghuram 		    ((vsw_port_t *)arg)->p_instance, addr);
15805373Sraghuram 	} else {
15815373Sraghuram 		/*
15825373Sraghuram 		 * We are being invoked via the m_multicst mac entry
15835373Sraghuram 		 * point.
15845373Sraghuram 		 */
15855373Sraghuram 		D2(NULL, "%s: address 0x%llx", __func__, addr);
15865373Sraghuram 		tgt = (void *)vswp;
15875373Sraghuram 	}
15885373Sraghuram 
15895373Sraghuram 	WRITE_ENTER(&vswp->mfdbrw);
15905373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
15915373Sraghuram 	    (mod_hash_val_t *)&ment) != 0) {
15925373Sraghuram 
15935373Sraghuram 		/* address not currently in table */
15945373Sraghuram 		ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
15955373Sraghuram 		ment->d_addr = (void *)tgt;
15965373Sraghuram 		ment->d_type = devtype;
15975373Sraghuram 		ment->nextp = NULL;
15985373Sraghuram 
15995373Sraghuram 		if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr,
16005373Sraghuram 		    (mod_hash_val_t)ment) != 0) {
16015373Sraghuram 			DERR(vswp, "%s: hash table insertion failed", __func__);
16025373Sraghuram 			kmem_free(ment, sizeof (mfdb_ent_t));
16035373Sraghuram 			rv = 1;
16045373Sraghuram 		} else {
16055373Sraghuram 			D2(vswp, "%s: added initial entry for 0x%llx to "
16065373Sraghuram 			    "table", __func__, addr);
16075373Sraghuram 		}
16085373Sraghuram 	} else {
16095373Sraghuram 		/*
16105373Sraghuram 		 * Address in table. Check to see if specified port
16115373Sraghuram 		 * is already associated with the address. If not add
16125373Sraghuram 		 * it now.
16135373Sraghuram 		 */
16145373Sraghuram 		tmp_ent = ment;
16155373Sraghuram 		while (tmp_ent != NULL) {
16165373Sraghuram 			if (tmp_ent->d_addr == (void *)tgt) {
16175373Sraghuram 				if (devtype == VSW_VNETPORT) {
16185373Sraghuram 					DERR(vswp, "%s: duplicate port entry "
16195373Sraghuram 					    "found for portid %ld and key "
16205373Sraghuram 					    "0x%llx", __func__,
16215373Sraghuram 					    ((vsw_port_t *)arg)->p_instance,
16225373Sraghuram 					    addr);
16235373Sraghuram 				} else {
16245373Sraghuram 					DERR(vswp, "%s: duplicate entry found"
16255373Sraghuram 					    "for key 0x%llx", __func__, addr);
16265373Sraghuram 				}
16275373Sraghuram 				rv = 1;
16285373Sraghuram 				dup = 1;
16295373Sraghuram 				break;
16305373Sraghuram 			}
16315373Sraghuram 			tmp_ent = tmp_ent->nextp;
16325373Sraghuram 		}
16335373Sraghuram 
16345373Sraghuram 		/*
16355373Sraghuram 		 * Port not on list so add it to end now.
16365373Sraghuram 		 */
16375373Sraghuram 		if (0 == dup) {
16385373Sraghuram 			D2(vswp, "%s: added entry for 0x%llx to table",
16395373Sraghuram 			    __func__, addr);
16405373Sraghuram 			new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
16415373Sraghuram 			new_ent->d_addr = (void *)tgt;
16425373Sraghuram 			new_ent->d_type = devtype;
16435373Sraghuram 			new_ent->nextp = NULL;
16445373Sraghuram 
16455373Sraghuram 			tmp_ent = ment;
16465373Sraghuram 			while (tmp_ent->nextp != NULL)
16475373Sraghuram 				tmp_ent = tmp_ent->nextp;
16485373Sraghuram 
16495373Sraghuram 			tmp_ent->nextp = new_ent;
16505373Sraghuram 		}
16515373Sraghuram 	}
16525373Sraghuram 
16535373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
16545373Sraghuram 	return (rv);
16555373Sraghuram }
16565373Sraghuram 
16575373Sraghuram /*
16585373Sraghuram  * Remove a multicast entry from the hashtable.
16595373Sraghuram  *
16605373Sraghuram  * Search hash table based on address. If match found, scan
16615373Sraghuram  * list of ports associated with address. If specified port
16625373Sraghuram  * found remove it from list.
16635373Sraghuram  */
16645373Sraghuram int
16655373Sraghuram vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
16665373Sraghuram {
16675373Sraghuram 	mfdb_ent_t	*ment = NULL;
16685373Sraghuram 	mfdb_ent_t	*curr_p, *prev_p;
16695373Sraghuram 	void		*tgt = NULL;
16705373Sraghuram 
16715373Sraghuram 	D1(vswp, "%s: enter", __func__);
16725373Sraghuram 
16735373Sraghuram 	if (devtype == VSW_VNETPORT) {
16745373Sraghuram 		tgt = (vsw_port_t *)arg;
16755373Sraghuram 		D2(vswp, "%s: removing port %d from mFDB for address"
16765373Sraghuram 		    " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, addr);
16775373Sraghuram 	} else {
16785373Sraghuram 		D2(vswp, "%s: removing entry", __func__);
16795373Sraghuram 		tgt = (void *)vswp;
16805373Sraghuram 	}
16815373Sraghuram 
16825373Sraghuram 	WRITE_ENTER(&vswp->mfdbrw);
16835373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
16845373Sraghuram 	    (mod_hash_val_t *)&ment) != 0) {
16855373Sraghuram 		D2(vswp, "%s: address 0x%llx not in table", __func__, addr);
16865373Sraghuram 		RW_EXIT(&vswp->mfdbrw);
16875373Sraghuram 		return (1);
16885373Sraghuram 	}
16895373Sraghuram 
16905373Sraghuram 	prev_p = curr_p = ment;
16915373Sraghuram 
16925373Sraghuram 	while (curr_p != NULL) {
16935373Sraghuram 		if (curr_p->d_addr == (void *)tgt) {
16945373Sraghuram 			if (devtype == VSW_VNETPORT) {
16955373Sraghuram 				D2(vswp, "%s: port %d found", __func__,
16965373Sraghuram 				    ((vsw_port_t *)tgt)->p_instance);
16975373Sraghuram 			} else {
16985373Sraghuram 				D2(vswp, "%s: instance found", __func__);
16995373Sraghuram 			}
17005373Sraghuram 
17015373Sraghuram 			if (prev_p == curr_p) {
17025373Sraghuram 				/*
17035373Sraghuram 				 * head of list, if no other element is in
17045373Sraghuram 				 * list then destroy this entry, otherwise
17055373Sraghuram 				 * just replace it with updated value.
17065373Sraghuram 				 */
17075373Sraghuram 				ment = curr_p->nextp;
17085373Sraghuram 				if (ment == NULL) {
17095373Sraghuram 					(void) mod_hash_destroy(vswp->mfdb,
17105373Sraghuram 					    (mod_hash_val_t)addr);
17115373Sraghuram 				} else {
17125373Sraghuram 					(void) mod_hash_replace(vswp->mfdb,
17135373Sraghuram 					    (mod_hash_key_t)addr,
17145373Sraghuram 					    (mod_hash_val_t)ment);
17155373Sraghuram 				}
17165373Sraghuram 			} else {
17175373Sraghuram 				/*
17185373Sraghuram 				 * Not head of list, no need to do
17195373Sraghuram 				 * replacement, just adjust list pointers.
17205373Sraghuram 				 */
17215373Sraghuram 				prev_p->nextp = curr_p->nextp;
17225373Sraghuram 			}
17235373Sraghuram 			break;
17245373Sraghuram 		}
17255373Sraghuram 
17265373Sraghuram 		prev_p = curr_p;
17275373Sraghuram 		curr_p = curr_p->nextp;
17285373Sraghuram 	}
17295373Sraghuram 
17305373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
17315373Sraghuram 
17325373Sraghuram 	D1(vswp, "%s: exit", __func__);
17335373Sraghuram 
17345373Sraghuram 	if (curr_p == NULL)
17355373Sraghuram 		return (1);
17365373Sraghuram 	kmem_free(curr_p, sizeof (mfdb_ent_t));
17375373Sraghuram 	return (0);
17385373Sraghuram }
17395373Sraghuram 
17405373Sraghuram /*
17415373Sraghuram  * Port is being deleted, but has registered an interest in one
17425373Sraghuram  * or more multicast groups. Using the list of addresses maintained
17435373Sraghuram  * within the port structure find the appropriate entry in the hash
17445373Sraghuram  * table and remove this port from the list of interested ports.
17455373Sraghuram  */
17465373Sraghuram void
17475373Sraghuram vsw_del_mcst_port(vsw_port_t *port)
17485373Sraghuram {
17495373Sraghuram 	mcst_addr_t	*mcap = NULL;
17505373Sraghuram 	vsw_t		*vswp = port->p_vswp;
17515373Sraghuram 
17525373Sraghuram 	D1(vswp, "%s: enter", __func__);
17535373Sraghuram 
17545373Sraghuram 	mutex_enter(&port->mca_lock);
17555373Sraghuram 
17565373Sraghuram 	while ((mcap = port->mcap) != NULL) {
17575373Sraghuram 
17585373Sraghuram 		port->mcap = mcap->nextp;
17595373Sraghuram 
17605373Sraghuram 		mutex_exit(&port->mca_lock);
17615373Sraghuram 
17625373Sraghuram 		(void) vsw_del_mcst(vswp, VSW_VNETPORT,
17635373Sraghuram 		    mcap->addr, port);
17645373Sraghuram 
17655373Sraghuram 		/*
17665373Sraghuram 		 * Remove the address from HW. The address
17675373Sraghuram 		 * will actually only be removed once the ref
17685373Sraghuram 		 * count within the MAC layer has dropped to
17695373Sraghuram 		 * zero. I.e. we can safely call this fn even
17705373Sraghuram 		 * if other ports are interested in this
17715373Sraghuram 		 * address.
17725373Sraghuram 		 */
17735373Sraghuram 		mutex_enter(&vswp->mac_lock);
17745373Sraghuram 		if (vswp->mh != NULL && mcap->mac_added) {
17755373Sraghuram 			(void) mac_multicst_remove(vswp->mh,
17765373Sraghuram 			    (uchar_t *)&mcap->mca);
17775373Sraghuram 		}
17785373Sraghuram 		mutex_exit(&vswp->mac_lock);
17795373Sraghuram 
17805373Sraghuram 		kmem_free(mcap, sizeof (*mcap));
17815373Sraghuram 
17825373Sraghuram 		mutex_enter(&port->mca_lock);
17835373Sraghuram 
17845373Sraghuram 	}
17855373Sraghuram 
17865373Sraghuram 	mutex_exit(&port->mca_lock);
17875373Sraghuram 
17885373Sraghuram 	D1(vswp, "%s: exit", __func__);
17895373Sraghuram }
17905373Sraghuram 
17915373Sraghuram /*
17925373Sraghuram  * This vsw instance is detaching, but has registered an interest in one
17935373Sraghuram  * or more multicast groups. Using the list of addresses maintained
17945373Sraghuram  * within the vsw structure find the appropriate entry in the hash
17955373Sraghuram  * table and remove this instance from the list of interested ports.
17965373Sraghuram  */
17975373Sraghuram void
17985373Sraghuram vsw_del_mcst_vsw(vsw_t *vswp)
17995373Sraghuram {
18005373Sraghuram 	mcst_addr_t	*next_p = NULL;
18015373Sraghuram 
18025373Sraghuram 	D1(vswp, "%s: enter", __func__);
18035373Sraghuram 
18045373Sraghuram 	mutex_enter(&vswp->mca_lock);
18055373Sraghuram 
18065373Sraghuram 	while (vswp->mcap != NULL) {
18075373Sraghuram 		DERR(vswp, "%s: deleting addr 0x%llx",
18085373Sraghuram 		    __func__, vswp->mcap->addr);
18095373Sraghuram 		(void) vsw_del_mcst(vswp, VSW_LOCALDEV, vswp->mcap->addr, NULL);
18105373Sraghuram 
18115373Sraghuram 		next_p = vswp->mcap->nextp;
18125373Sraghuram 		kmem_free(vswp->mcap, sizeof (mcst_addr_t));
18135373Sraghuram 		vswp->mcap = next_p;
18145373Sraghuram 	}
18155373Sraghuram 
18165373Sraghuram 	vswp->mcap = NULL;
18175373Sraghuram 	mutex_exit(&vswp->mca_lock);
18185373Sraghuram 
18195373Sraghuram 	D1(vswp, "%s: exit", __func__);
18205373Sraghuram }
18215373Sraghuram 
18225935Ssb155480 static uint32_t
18235373Sraghuram vsw_get_same_dest_list(struct ether_header *ehp,
18245373Sraghuram     mblk_t **rhead, mblk_t **rtail, mblk_t **mpp)
18255373Sraghuram {
18265935Ssb155480 	uint32_t		count = 0;
18275935Ssb155480 	mblk_t			*bp;
18285935Ssb155480 	mblk_t			*nbp;
18295935Ssb155480 	mblk_t			*head = NULL;
18305935Ssb155480 	mblk_t			*tail = NULL;
18315935Ssb155480 	mblk_t			*prev = NULL;
18325935Ssb155480 	struct ether_header	*behp;
18335373Sraghuram 
18345373Sraghuram 	/* process the chain of packets */
18355373Sraghuram 	bp = *mpp;
18365373Sraghuram 	while (bp) {
18375373Sraghuram 		nbp = bp->b_next;
18385373Sraghuram 		behp = (struct ether_header *)bp->b_rptr;
18395373Sraghuram 		bp->b_prev = NULL;
18405373Sraghuram 		if (ether_cmp(&ehp->ether_dhost, &behp->ether_dhost) == 0) {
18415373Sraghuram 			if (prev == NULL) {
18425373Sraghuram 				*mpp = nbp;
18435373Sraghuram 			} else {
18445373Sraghuram 				prev->b_next = nbp;
18455373Sraghuram 			}
18465373Sraghuram 			bp->b_next =  NULL;
18475373Sraghuram 			if (head == NULL) {
18485373Sraghuram 				head = tail = bp;
18495373Sraghuram 			} else {
18505373Sraghuram 				tail->b_next = bp;
18515373Sraghuram 				tail = bp;
18525373Sraghuram 			}
18535373Sraghuram 			count++;
18545373Sraghuram 		} else {
18555373Sraghuram 			prev = bp;
18565373Sraghuram 		}
18575373Sraghuram 		bp = nbp;
18585373Sraghuram 	}
18595373Sraghuram 	*rhead = head;
18605373Sraghuram 	*rtail = tail;
18615373Sraghuram 	DTRACE_PROBE1(vsw_same_dest, int, count);
18625373Sraghuram 	return (count);
18635373Sraghuram }
18645373Sraghuram 
18655373Sraghuram static mblk_t *
18665373Sraghuram vsw_dupmsgchain(mblk_t *mp)
18675373Sraghuram {
18685373Sraghuram 	mblk_t	*nmp = NULL;
18695373Sraghuram 	mblk_t	**nmpp = &nmp;
18705373Sraghuram 
18715373Sraghuram 	for (; mp != NULL; mp = mp->b_next) {
18725373Sraghuram 		if ((*nmpp = dupmsg(mp)) == NULL) {
18735373Sraghuram 			freemsgchain(nmp);
18745373Sraghuram 			return (NULL);
18755373Sraghuram 		}
18765373Sraghuram 
18775373Sraghuram 		nmpp = &((*nmpp)->b_next);
18785373Sraghuram 	}
18795373Sraghuram 
18805373Sraghuram 	return (nmp);
18815373Sraghuram }
1882