xref: /onnv-gate/usr/src/uts/sun4v/io/vsw_switching.c (revision 12300:abf8b7f3c0ad)
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 /*
23*12300SSriharsha.Basavapatna@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
245373Sraghuram  */
255373Sraghuram 
265373Sraghuram #include <sys/types.h>
275373Sraghuram #include <sys/errno.h>
285373Sraghuram #include <sys/debug.h>
295373Sraghuram #include <sys/time.h>
305373Sraghuram #include <sys/sysmacros.h>
315373Sraghuram #include <sys/systm.h>
325373Sraghuram #include <sys/user.h>
335373Sraghuram #include <sys/stropts.h>
345373Sraghuram #include <sys/stream.h>
355373Sraghuram #include <sys/strlog.h>
365373Sraghuram #include <sys/strsubr.h>
375373Sraghuram #include <sys/cmn_err.h>
385373Sraghuram #include <sys/cpu.h>
395373Sraghuram #include <sys/kmem.h>
405373Sraghuram #include <sys/conf.h>
415373Sraghuram #include <sys/ddi.h>
425373Sraghuram #include <sys/sunddi.h>
435373Sraghuram #include <sys/ksynch.h>
445373Sraghuram #include <sys/stat.h>
455373Sraghuram #include <sys/kstat.h>
465373Sraghuram #include <sys/vtrace.h>
475373Sraghuram #include <sys/strsun.h>
485373Sraghuram #include <sys/dlpi.h>
495373Sraghuram #include <sys/ethernet.h>
505373Sraghuram #include <net/if.h>
515373Sraghuram #include <sys/varargs.h>
525373Sraghuram #include <sys/machsystm.h>
535373Sraghuram #include <sys/modctl.h>
545373Sraghuram #include <sys/modhash.h>
555373Sraghuram #include <sys/mac.h>
565373Sraghuram #include <sys/mac_ether.h>
575373Sraghuram #include <sys/taskq.h>
585373Sraghuram #include <sys/note.h>
595373Sraghuram #include <sys/mach_descrip.h>
605373Sraghuram #include <sys/mdeg.h>
615373Sraghuram #include <sys/ldc.h>
625373Sraghuram #include <sys/vsw_fdb.h>
635373Sraghuram #include <sys/vsw.h>
645373Sraghuram #include <sys/vio_mailbox.h>
655373Sraghuram #include <sys/vnet_mailbox.h>
665373Sraghuram #include <sys/vnet_common.h>
675373Sraghuram #include <sys/vio_util.h>
685373Sraghuram #include <sys/sdt.h>
695373Sraghuram #include <sys/atomic.h>
706419Ssb155480 #include <sys/vlan.h>
715373Sraghuram 
725373Sraghuram /* Switching setup routines */
738370SSriharsha.Basavapatna@Sun.COM void vsw_setup_switching_thread(void *arg);
748370SSriharsha.Basavapatna@Sun.COM int vsw_setup_switching_start(vsw_t *vswp);
758370SSriharsha.Basavapatna@Sun.COM void vsw_setup_switching_stop(vsw_t *vswp);
765373Sraghuram int vsw_setup_switching(vsw_t *);
779819SSriharsha.Basavapatna@Sun.COM void vsw_setup_switching_post_process(vsw_t *vswp);
786537Swentaoy void vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller,
796537Swentaoy     vsw_port_t *port, mac_resource_handle_t mrh);
805373Sraghuram static	int vsw_setup_layer2(vsw_t *);
815373Sraghuram static	int vsw_setup_layer3(vsw_t *);
825373Sraghuram 
835373Sraghuram /* Switching/data transmit routines */
848275SEric Cheng static	void vsw_switch_l2_frame_mac_client(vsw_t *vswp, mblk_t *mp, int caller,
858275SEric Cheng     vsw_port_t *port, mac_resource_handle_t);
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);
1218275SEric Cheng static mblk_t *vsw_get_same_dest_list(struct ether_header *ehp, mblk_t **mpp);
1225373Sraghuram 
1235373Sraghuram 
1245373Sraghuram /*
1255373Sraghuram  * Functions imported from other files.
1265373Sraghuram  */
1278275SEric Cheng extern mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
1285373Sraghuram extern mcst_addr_t *vsw_del_addr(uint8_t, void *, uint64_t);
1295373Sraghuram extern int vsw_mac_open(vsw_t *vswp);
1305373Sraghuram extern void vsw_mac_close(vsw_t *vswp);
1315935Ssb155480 extern void vsw_mac_rx(vsw_t *vswp, mac_resource_handle_t mrh,
1325935Ssb155480     mblk_t *mp, vsw_macrx_flags_t flags);
1335373Sraghuram extern void vsw_set_addrs(vsw_t *vswp);
1348275SEric Cheng extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
1356495Sspeer extern void vsw_hio_init(vsw_t *vswp);
1366495Sspeer extern void vsw_hio_start_ports(vsw_t *vswp);
1378275SEric Cheng extern int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port,
1388275SEric Cheng     mcst_addr_t *mcst_p, int type);
1398275SEric Cheng extern void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
1408275SEric Cheng     mcst_addr_t *mcst_p, int type);
1419819SSriharsha.Basavapatna@Sun.COM extern void vsw_mac_link_update(vsw_t *vswp, link_state_t link_state);
1429819SSriharsha.Basavapatna@Sun.COM extern void vsw_physlink_update_ports(vsw_t *vswp);
1435373Sraghuram 
1445373Sraghuram /*
1455373Sraghuram  * Tunables used in this file.
1465373Sraghuram  */
1476419Ssb155480 extern	int vsw_setup_switching_delay;
1486419Ssb155480 extern	uint32_t vsw_vlan_nchains;
1496419Ssb155480 extern	uint32_t vsw_fdbe_refcnt_delay;
1505373Sraghuram 
1516419Ssb155480 #define	VSW_FDBE_REFHOLD(p)						\
1526419Ssb155480 {									\
1536419Ssb155480 	atomic_inc_32(&(p)->refcnt);					\
1546419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1556419Ssb155480 }
1566419Ssb155480 
1576419Ssb155480 #define	VSW_FDBE_REFRELE(p)						\
1586419Ssb155480 {									\
1596419Ssb155480 	ASSERT((p)->refcnt != 0);					\
1606419Ssb155480 	atomic_dec_32(&(p)->refcnt);					\
1616419Ssb155480 }
1625373Sraghuram 
1635373Sraghuram /*
1648370SSriharsha.Basavapatna@Sun.COM  * Thread to setup switching mode. This thread is created during vsw_attach()
1658370SSriharsha.Basavapatna@Sun.COM  * initially. It invokes vsw_setup_switching() and keeps retrying while the
1668370SSriharsha.Basavapatna@Sun.COM  * returned value is EAGAIN. The thread exits when the switching mode setup is
1678370SSriharsha.Basavapatna@Sun.COM  * done successfully or when the error returned is not EAGAIN. This thread may
1688370SSriharsha.Basavapatna@Sun.COM  * also get created from vsw_update_md_prop() if the switching mode needs to be
1698370SSriharsha.Basavapatna@Sun.COM  * updated.
1705373Sraghuram  */
1715373Sraghuram void
vsw_setup_switching_thread(void * arg)1728370SSriharsha.Basavapatna@Sun.COM vsw_setup_switching_thread(void *arg)
1735373Sraghuram {
1748370SSriharsha.Basavapatna@Sun.COM 	callb_cpr_t	cprinfo;
1758370SSriharsha.Basavapatna@Sun.COM 	vsw_t		*vswp =  (vsw_t *)arg;
1768370SSriharsha.Basavapatna@Sun.COM 	clock_t		wait_time;
1778370SSriharsha.Basavapatna@Sun.COM 	clock_t		xwait;
1788370SSriharsha.Basavapatna@Sun.COM 	clock_t		wait_rv;
1795373Sraghuram 	int		rv;
1805373Sraghuram 
1818370SSriharsha.Basavapatna@Sun.COM 	/* wait time used on successive retries */
1828370SSriharsha.Basavapatna@Sun.COM 	xwait = drv_usectohz(vsw_setup_switching_delay * MICROSEC);
1838370SSriharsha.Basavapatna@Sun.COM 
1848370SSriharsha.Basavapatna@Sun.COM 	CALLB_CPR_INIT(&cprinfo, &vswp->sw_thr_lock, callb_generic_cpr,
1858370SSriharsha.Basavapatna@Sun.COM 	    "vsw_setup_sw_thread");
1868370SSriharsha.Basavapatna@Sun.COM 
1878370SSriharsha.Basavapatna@Sun.COM 	mutex_enter(&vswp->sw_thr_lock);
1888370SSriharsha.Basavapatna@Sun.COM 
1898370SSriharsha.Basavapatna@Sun.COM 	while ((vswp->sw_thr_flags & VSW_SWTHR_STOP) == 0) {
1908370SSriharsha.Basavapatna@Sun.COM 
1918370SSriharsha.Basavapatna@Sun.COM 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
1928370SSriharsha.Basavapatna@Sun.COM 
1938370SSriharsha.Basavapatna@Sun.COM 		/* Wait for sometime before (re)trying setup_switching() */
1948370SSriharsha.Basavapatna@Sun.COM 		wait_time = ddi_get_lbolt() + xwait;
1958370SSriharsha.Basavapatna@Sun.COM 		while ((vswp->sw_thr_flags & VSW_SWTHR_STOP) == 0) {
1968370SSriharsha.Basavapatna@Sun.COM 			wait_rv = cv_timedwait(&vswp->sw_thr_cv,
1978370SSriharsha.Basavapatna@Sun.COM 			    &vswp->sw_thr_lock, wait_time);
1988370SSriharsha.Basavapatna@Sun.COM 			if (wait_rv == -1) {	/* timed out */
1998370SSriharsha.Basavapatna@Sun.COM 				break;
2008370SSriharsha.Basavapatna@Sun.COM 			}
2018370SSriharsha.Basavapatna@Sun.COM 		}
2025373Sraghuram 
2038370SSriharsha.Basavapatna@Sun.COM 		CALLB_CPR_SAFE_END(&cprinfo, &vswp->sw_thr_lock)
2048370SSriharsha.Basavapatna@Sun.COM 
2058370SSriharsha.Basavapatna@Sun.COM 		if ((vswp->sw_thr_flags & VSW_SWTHR_STOP) != 0) {
2068370SSriharsha.Basavapatna@Sun.COM 			/*
2078370SSriharsha.Basavapatna@Sun.COM 			 * If there is a stop request, process that first and
2088370SSriharsha.Basavapatna@Sun.COM 			 * exit the loop. Continue to hold the mutex which gets
2098370SSriharsha.Basavapatna@Sun.COM 			 * released in CALLB_CPR_EXIT().
2108370SSriharsha.Basavapatna@Sun.COM 			 */
2118370SSriharsha.Basavapatna@Sun.COM 			break;
2128370SSriharsha.Basavapatna@Sun.COM 		}
2135373Sraghuram 
2148370SSriharsha.Basavapatna@Sun.COM 		mutex_exit(&vswp->sw_thr_lock);
2158370SSriharsha.Basavapatna@Sun.COM 		rv = vsw_setup_switching(vswp);
2168370SSriharsha.Basavapatna@Sun.COM 		if (rv == 0) {
2179819SSriharsha.Basavapatna@Sun.COM 			vsw_setup_switching_post_process(vswp);
2188370SSriharsha.Basavapatna@Sun.COM 		}
2198370SSriharsha.Basavapatna@Sun.COM 		mutex_enter(&vswp->sw_thr_lock);
2208370SSriharsha.Basavapatna@Sun.COM 		if (rv != EAGAIN) {
2218370SSriharsha.Basavapatna@Sun.COM 			break;
2228370SSriharsha.Basavapatna@Sun.COM 		}
2238370SSriharsha.Basavapatna@Sun.COM 
2245373Sraghuram 	}
2255373Sraghuram 
2268370SSriharsha.Basavapatna@Sun.COM 	vswp->sw_thr_flags &= ~VSW_SWTHR_STOP;
2278370SSriharsha.Basavapatna@Sun.COM 	vswp->sw_thread = NULL;
2288370SSriharsha.Basavapatna@Sun.COM 	CALLB_CPR_EXIT(&cprinfo);
2298370SSriharsha.Basavapatna@Sun.COM 	thread_exit();
2305373Sraghuram }
2315373Sraghuram 
2325373Sraghuram /*
2338370SSriharsha.Basavapatna@Sun.COM  * Create a thread to setup the switching mode.
2348370SSriharsha.Basavapatna@Sun.COM  * Returns 0 on success; 1 on failure.
2358370SSriharsha.Basavapatna@Sun.COM  */
2368370SSriharsha.Basavapatna@Sun.COM int
vsw_setup_switching_start(vsw_t * vswp)2378370SSriharsha.Basavapatna@Sun.COM vsw_setup_switching_start(vsw_t *vswp)
2388370SSriharsha.Basavapatna@Sun.COM {
2398370SSriharsha.Basavapatna@Sun.COM 	mutex_enter(&vswp->sw_thr_lock);
2408370SSriharsha.Basavapatna@Sun.COM 
2418370SSriharsha.Basavapatna@Sun.COM 	vswp->sw_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
2428370SSriharsha.Basavapatna@Sun.COM 	    vsw_setup_switching_thread, vswp, 0, &p0, TS_RUN, minclsyspri);
2438370SSriharsha.Basavapatna@Sun.COM 
2448370SSriharsha.Basavapatna@Sun.COM 	if (vswp->sw_thread == NULL) {
2458370SSriharsha.Basavapatna@Sun.COM 		mutex_exit(&vswp->sw_thr_lock);
2468370SSriharsha.Basavapatna@Sun.COM 		return (1);
2478370SSriharsha.Basavapatna@Sun.COM 	}
2488370SSriharsha.Basavapatna@Sun.COM 
2498370SSriharsha.Basavapatna@Sun.COM 	mutex_exit(&vswp->sw_thr_lock);
2508370SSriharsha.Basavapatna@Sun.COM 	return (0);
2518370SSriharsha.Basavapatna@Sun.COM }
2528370SSriharsha.Basavapatna@Sun.COM 
2538370SSriharsha.Basavapatna@Sun.COM /*
2548370SSriharsha.Basavapatna@Sun.COM  * Stop the thread to setup switching mode.
2555373Sraghuram  */
2565373Sraghuram void
vsw_setup_switching_stop(vsw_t * vswp)2578370SSriharsha.Basavapatna@Sun.COM vsw_setup_switching_stop(vsw_t *vswp)
2585373Sraghuram {
2598370SSriharsha.Basavapatna@Sun.COM 	kt_did_t	tid = 0;
2605373Sraghuram 
2618370SSriharsha.Basavapatna@Sun.COM 	/*
2628370SSriharsha.Basavapatna@Sun.COM 	 * Signal the setup_switching thread to stop and wait until it stops.
2638370SSriharsha.Basavapatna@Sun.COM 	 */
2648370SSriharsha.Basavapatna@Sun.COM 	mutex_enter(&vswp->sw_thr_lock);
2655373Sraghuram 
2668370SSriharsha.Basavapatna@Sun.COM 	if (vswp->sw_thread != NULL) {
2678370SSriharsha.Basavapatna@Sun.COM 		tid = vswp->sw_thread->t_did;
2688370SSriharsha.Basavapatna@Sun.COM 		vswp->sw_thr_flags |= VSW_SWTHR_STOP;
2698370SSriharsha.Basavapatna@Sun.COM 		cv_signal(&vswp->sw_thr_cv);
2708370SSriharsha.Basavapatna@Sun.COM 	}
2715373Sraghuram 
2728370SSriharsha.Basavapatna@Sun.COM 	mutex_exit(&vswp->sw_thr_lock);
2738370SSriharsha.Basavapatna@Sun.COM 
2748370SSriharsha.Basavapatna@Sun.COM 	if (tid != 0)
2758370SSriharsha.Basavapatna@Sun.COM 		thread_join(tid);
2765373Sraghuram 
2775373Sraghuram 	(void) atomic_swap_32(&vswp->switching_setup_done, B_FALSE);
2785373Sraghuram 
2795373Sraghuram 	vswp->mac_open_retries = 0;
2805373Sraghuram }
2815373Sraghuram 
2825373Sraghuram /*
2835373Sraghuram  * Setup the required switching mode.
2845373Sraghuram  * Returns:
2855373Sraghuram  *  0 on success.
2865373Sraghuram  *  EAGAIN if retry is needed.
2875373Sraghuram  *  1 on all other failures.
2885373Sraghuram  */
2895373Sraghuram int
vsw_setup_switching(vsw_t * vswp)2905373Sraghuram vsw_setup_switching(vsw_t *vswp)
2915373Sraghuram {
2928275SEric Cheng 	int	rv = 1;
2935373Sraghuram 
2945373Sraghuram 	D1(vswp, "%s: enter", __func__);
2955373Sraghuram 
2965373Sraghuram 	/*
2975373Sraghuram 	 * Select best switching mode.
2988275SEric Cheng 	 * This is done as this routine can be called from the timeout
2998275SEric Cheng 	 * handler to retry setting up a specific mode. Currently only
3008275SEric Cheng 	 * the function which sets up layer2/promisc mode returns EAGAIN
3018275SEric Cheng 	 * if the underlying network device is not available yet, causing
3028275SEric Cheng 	 * retries.
3035373Sraghuram 	 */
3048275SEric Cheng 	if (vswp->smode & VSW_LAYER2) {
3058275SEric Cheng 		rv = vsw_setup_layer2(vswp);
3068275SEric Cheng 	} else if (vswp->smode & VSW_LAYER3) {
3078275SEric Cheng 		rv = vsw_setup_layer3(vswp);
3088275SEric Cheng 	} else {
3098275SEric Cheng 		DERR(vswp, "unknown switch mode");
3105373Sraghuram 		rv = 1;
3115373Sraghuram 	}
3125373Sraghuram 
3135373Sraghuram 	if (rv && (rv != EAGAIN)) {
3145373Sraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to setup specified "
3155373Sraghuram 		    "switching mode", vswp->instance);
3165373Sraghuram 	} else if (rv == 0) {
3175373Sraghuram 		(void) atomic_swap_32(&vswp->switching_setup_done, B_TRUE);
3185373Sraghuram 	}
3195373Sraghuram 
3205373Sraghuram 	D2(vswp, "%s: Operating in mode %d", __func__,
3218275SEric Cheng 	    vswp->smode);
3225373Sraghuram 
3235373Sraghuram 	D1(vswp, "%s: exit", __func__);
3245373Sraghuram 
3255373Sraghuram 	return (rv);
3265373Sraghuram }
3275373Sraghuram 
3285373Sraghuram /*
3295373Sraghuram  * Setup for layer 2 switching.
3305373Sraghuram  *
3315373Sraghuram  * Returns:
3325373Sraghuram  *  0 on success.
3335373Sraghuram  *  EAGAIN if retry is needed.
3345373Sraghuram  *  EIO on all other failures.
3355373Sraghuram  */
3365373Sraghuram static int
vsw_setup_layer2(vsw_t * vswp)3375373Sraghuram vsw_setup_layer2(vsw_t *vswp)
3385373Sraghuram {
3395373Sraghuram 	int	rv;
3405373Sraghuram 
3415373Sraghuram 	D1(vswp, "%s: enter", __func__);
3425373Sraghuram 
3438275SEric Cheng 	/*
3448275SEric Cheng 	 * Until the network device is successfully opened,
3458275SEric Cheng 	 * set the switching to use vsw_switch_l2_frame.
3468275SEric Cheng 	 */
3475373Sraghuram 	vswp->vsw_switch_frame = vsw_switch_l2_frame;
3488275SEric Cheng 	vswp->mac_cl_switching = B_FALSE;
3495373Sraghuram 
3505373Sraghuram 	rv = strlen(vswp->physname);
3515373Sraghuram 	if (rv == 0) {
3525373Sraghuram 		/*
3535373Sraghuram 		 * Physical device name is NULL, which is
3545373Sraghuram 		 * required for layer 2.
3555373Sraghuram 		 */
3568275SEric Cheng 		cmn_err(CE_WARN, "!vsw%d: no network device name specified",
3575373Sraghuram 		    vswp->instance);
3585373Sraghuram 		return (EIO);
3595373Sraghuram 	}
3605373Sraghuram 
3618275SEric Cheng 	mutex_enter(&vswp->mac_lock);
3625373Sraghuram 
3635373Sraghuram 	rv = vsw_mac_open(vswp);
3645373Sraghuram 	if (rv != 0) {
3655373Sraghuram 		if (rv != EAGAIN) {
3668275SEric Cheng 			cmn_err(CE_WARN, "!vsw%d: Unable to open network "
3675373Sraghuram 			    "device: %s\n", vswp->instance, vswp->physname);
3685373Sraghuram 		}
3698275SEric Cheng 		mutex_exit(&vswp->mac_lock);
3705373Sraghuram 		return (rv);
3715373Sraghuram 	}
3725373Sraghuram 
3735373Sraghuram 	/*
3748275SEric Cheng 	 * Now we can use the mac client switching, so set the switching
3758275SEric Cheng 	 * function to use vsw_switch_l2_frame_mac_client(), which simply
3768275SEric Cheng 	 * sends the packets to MAC layer for switching.
3775373Sraghuram 	 */
3788275SEric Cheng 	vswp->vsw_switch_frame = vsw_switch_l2_frame_mac_client;
3798275SEric Cheng 	vswp->mac_cl_switching = B_TRUE;
3805373Sraghuram 
3815373Sraghuram 	D1(vswp, "%s: exit", __func__);
3825373Sraghuram 
3836495Sspeer 	/* Initialize HybridIO related stuff */
3846495Sspeer 	vsw_hio_init(vswp);
3858275SEric Cheng 
3868275SEric Cheng 	mutex_exit(&vswp->mac_lock);
3875373Sraghuram 	return (0);
3885373Sraghuram 
3895373Sraghuram exit_error:
3905373Sraghuram 	vsw_mac_close(vswp);
3918275SEric Cheng 	mutex_exit(&vswp->mac_lock);
3925373Sraghuram 	return (EIO);
3935373Sraghuram }
3945373Sraghuram 
3955373Sraghuram static int
vsw_setup_layer3(vsw_t * vswp)3965373Sraghuram vsw_setup_layer3(vsw_t *vswp)
3975373Sraghuram {
3985373Sraghuram 	D1(vswp, "%s: enter", __func__);
3995373Sraghuram 
4005373Sraghuram 	D2(vswp, "%s: operating in layer 3 mode", __func__);
4015373Sraghuram 	vswp->vsw_switch_frame = vsw_switch_l3_frame;
4025373Sraghuram 
4035373Sraghuram 	D1(vswp, "%s: exit", __func__);
4045373Sraghuram 
4055373Sraghuram 	return (0);
4065373Sraghuram }
4075373Sraghuram 
4086537Swentaoy /* ARGSUSED */
4096537Swentaoy void
vsw_switch_frame_nop(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * port,mac_resource_handle_t mrh)4106537Swentaoy vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port,
4116537Swentaoy 			mac_resource_handle_t mrh)
4126537Swentaoy {
4136537Swentaoy 	freemsgchain(mp);
4146537Swentaoy }
4156537Swentaoy 
4165373Sraghuram /*
4178275SEric Cheng  * Use mac client for layer 2 switching .
4188275SEric Cheng  */
4198275SEric Cheng static void
vsw_switch_l2_frame_mac_client(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * port,mac_resource_handle_t mrh)4208275SEric Cheng vsw_switch_l2_frame_mac_client(vsw_t *vswp, mblk_t *mp, int caller,
4218275SEric Cheng     vsw_port_t *port, mac_resource_handle_t mrh)
4228275SEric Cheng {
4238275SEric Cheng 	_NOTE(ARGUNUSED(mrh))
4248275SEric Cheng 
4258275SEric Cheng 	mblk_t		*ret_m;
4268275SEric Cheng 
4278275SEric Cheng 	/*
4288275SEric Cheng 	 * This switching function is expected to be called by
4298275SEric Cheng 	 * the ports or the interface only. The packets from
4308275SEric Cheng 	 * physical interface already switched.
4318275SEric Cheng 	 */
4328275SEric Cheng 	ASSERT((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV));
4338275SEric Cheng 
4348275SEric Cheng 	if ((ret_m = vsw_tx_msg(vswp, mp, caller, port)) != NULL) {
4358275SEric Cheng 		DERR(vswp, "%s: drop mblks to "
4368275SEric Cheng 		    "phys dev", __func__);
4378275SEric Cheng 		freemsgchain(ret_m);
4388275SEric Cheng 	}
4398275SEric Cheng }
4408275SEric Cheng 
4418275SEric Cheng /*
4425373Sraghuram  * Switch the given ethernet frame when operating in layer 2 mode.
4435373Sraghuram  *
4445373Sraghuram  * vswp: pointer to the vsw instance
4455373Sraghuram  * mp: pointer to chain of ethernet frame(s) to be switched
4465373Sraghuram  * caller: identifies the source of this frame as:
4475373Sraghuram  * 		1. VSW_VNETPORT - a vsw port (connected to a vnet).
4485373Sraghuram  *		2. VSW_PHYSDEV - the physical ethernet device
4495373Sraghuram  *		3. VSW_LOCALDEV - vsw configured as a virtual interface
4505373Sraghuram  * arg: argument provided by the caller.
4515373Sraghuram  *		1. for VNETPORT - pointer to the corresponding vsw_port_t.
4525373Sraghuram  *		2. for PHYSDEV - NULL
4535373Sraghuram  *		3. for LOCALDEV - pointer to to this vsw_t(self)
4545373Sraghuram  */
4555373Sraghuram void
vsw_switch_l2_frame(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg,mac_resource_handle_t mrh)4565373Sraghuram vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller,
4575373Sraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
4585373Sraghuram {
4595373Sraghuram 	struct ether_header	*ehp;
4605373Sraghuram 	mblk_t			*bp, *ret_m;
4616419Ssb155480 	vsw_fdbe_t		*fp;
4625373Sraghuram 
4635373Sraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
4645373Sraghuram 
4655373Sraghuram 	/*
4665373Sraghuram 	 * PERF: rather than breaking up the chain here, scan it
4675373Sraghuram 	 * to find all mblks heading to same destination and then
4685373Sraghuram 	 * pass that sub-chain to the lower transmit functions.
4695373Sraghuram 	 */
4705373Sraghuram 
4715373Sraghuram 	/* process the chain of packets */
4725373Sraghuram 	bp = mp;
4735373Sraghuram 	while (bp) {
4745373Sraghuram 		ehp = (struct ether_header *)bp->b_rptr;
4758275SEric Cheng 		mp = vsw_get_same_dest_list(ehp, &bp);
4768275SEric Cheng 		ASSERT(mp != NULL);
4775373Sraghuram 
4785373Sraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
4795373Sraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
4805373Sraghuram 
4815373Sraghuram 		if (ether_cmp(&ehp->ether_dhost, &vswp->if_addr) == 0) {
4825373Sraghuram 			/*
4835373Sraghuram 			 * If destination is VSW_LOCALDEV (vsw as an eth
4845373Sraghuram 			 * interface) and if the device is up & running,
4855373Sraghuram 			 * send the packet up the stack on this host.
4865373Sraghuram 			 * If the virtual interface is down, drop the packet.
4875373Sraghuram 			 */
4885373Sraghuram 			if (caller != VSW_LOCALDEV) {
4895935Ssb155480 				vsw_mac_rx(vswp, mrh, mp, VSW_MACRX_FREEMSG);
4905373Sraghuram 			} else {
4915373Sraghuram 				freemsgchain(mp);
4925373Sraghuram 			}
4935373Sraghuram 			continue;
4945373Sraghuram 		}
4955373Sraghuram 
4966419Ssb155480 		/*
4976419Ssb155480 		 * Find fdb entry for the destination
4986419Ssb155480 		 * and hold a reference to it.
4996419Ssb155480 		 */
5006419Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
5016419Ssb155480 		if (fp != NULL) {
5025373Sraghuram 
5035373Sraghuram 			/*
5045373Sraghuram 			 * If plumbed and in promisc mode then copy msg
5055373Sraghuram 			 * and send up the stack.
5065373Sraghuram 			 */
5075935Ssb155480 			vsw_mac_rx(vswp, mrh, mp,
5085935Ssb155480 			    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
5095373Sraghuram 
5105373Sraghuram 			/*
5115373Sraghuram 			 * If the destination is in FDB, the packet
5125373Sraghuram 			 * should be forwarded to the correponding
5135373Sraghuram 			 * vsw_port (connected to a vnet device -
5145373Sraghuram 			 * VSW_VNETPORT)
5155373Sraghuram 			 */
5168275SEric Cheng 			(void) vsw_portsend(fp->portp, mp);
5175373Sraghuram 
5186419Ssb155480 			/* Release the reference on the fdb entry */
5196419Ssb155480 			VSW_FDBE_REFRELE(fp);
5205373Sraghuram 		} else {
5215373Sraghuram 			/*
5225373Sraghuram 			 * Destination not in FDB.
5235373Sraghuram 			 *
5245373Sraghuram 			 * If the destination is broadcast or
5255373Sraghuram 			 * multicast forward the packet to all
5265373Sraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
5275373Sraghuram 			 * except the caller.
5285373Sraghuram 			 */
5295373Sraghuram 			if (IS_BROADCAST(ehp)) {
5305935Ssb155480 				D2(vswp, "%s: BROADCAST pkt", __func__);
5315935Ssb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
5325373Sraghuram 			} else if (IS_MULTICAST(ehp)) {
5335935Ssb155480 				D2(vswp, "%s: MULTICAST pkt", __func__);
5345935Ssb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
5355373Sraghuram 			} else {
5365373Sraghuram 				/*
5375373Sraghuram 				 * If the destination is unicast, and came
5385373Sraghuram 				 * from either a logical network device or
5395373Sraghuram 				 * the switch itself when it is plumbed, then
5405373Sraghuram 				 * send it out on the physical device and also
5415373Sraghuram 				 * up the stack if the logical interface is
5425373Sraghuram 				 * in promiscious mode.
5435373Sraghuram 				 *
5445373Sraghuram 				 * NOTE:  The assumption here is that if we
5455373Sraghuram 				 * cannot find the destination in our fdb, its
5465373Sraghuram 				 * a unicast address, and came from either a
5475373Sraghuram 				 * vnet or down the stack (when plumbed) it
5485373Sraghuram 				 * must be destinded for an ethernet device
5495373Sraghuram 				 * outside our ldoms.
5505373Sraghuram 				 */
5515373Sraghuram 				if (caller == VSW_VNETPORT) {
5525373Sraghuram 					/* promisc check copy etc */
5535935Ssb155480 					vsw_mac_rx(vswp, mrh, mp,
5545373Sraghuram 					    VSW_MACRX_PROMISC |
5555373Sraghuram 					    VSW_MACRX_COPYMSG);
5565373Sraghuram 
5578275SEric Cheng 					if ((ret_m = vsw_tx_msg(vswp, mp,
5588275SEric Cheng 					    caller, arg)) != NULL) {
5595373Sraghuram 						DERR(vswp, "%s: drop mblks to "
5605373Sraghuram 						    "phys dev", __func__);
5615373Sraghuram 						freemsgchain(ret_m);
5625373Sraghuram 					}
5635373Sraghuram 
5645373Sraghuram 				} else if (caller == VSW_PHYSDEV) {
5655373Sraghuram 					/*
5665373Sraghuram 					 * Pkt seen because card in promisc
5675373Sraghuram 					 * mode. Send up stack if plumbed in
5685373Sraghuram 					 * promisc mode, else drop it.
5695373Sraghuram 					 */
5705935Ssb155480 					vsw_mac_rx(vswp, mrh, mp,
5715373Sraghuram 					    VSW_MACRX_PROMISC |
5725373Sraghuram 					    VSW_MACRX_FREEMSG);
5735373Sraghuram 
5745373Sraghuram 				} else if (caller == VSW_LOCALDEV) {
5755373Sraghuram 					/*
5765373Sraghuram 					 * Pkt came down the stack, send out
5775373Sraghuram 					 * over physical device.
5785373Sraghuram 					 */
5798275SEric Cheng 					if ((ret_m = vsw_tx_msg(vswp, mp,
5808275SEric Cheng 					    caller, NULL)) != NULL) {
5815373Sraghuram 						DERR(vswp, "%s: drop mblks to "
5825373Sraghuram 						    "phys dev", __func__);
5835373Sraghuram 						freemsgchain(ret_m);
5845373Sraghuram 					}
5855373Sraghuram 				}
5865373Sraghuram 			}
5875373Sraghuram 		}
5885373Sraghuram 	}
5895373Sraghuram 	D1(vswp, "%s: exit\n", __func__);
5905373Sraghuram }
5915373Sraghuram 
5925373Sraghuram /*
5935373Sraghuram  * Switch ethernet frame when in layer 3 mode (i.e. using IP
5945373Sraghuram  * layer to do the routing).
5955373Sraghuram  *
5965373Sraghuram  * There is a large amount of overlap between this function and
5975373Sraghuram  * vsw_switch_l2_frame. At some stage we need to revisit and refactor
5985373Sraghuram  * both these functions.
5995373Sraghuram  */
6005373Sraghuram void
vsw_switch_l3_frame(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg,mac_resource_handle_t mrh)6015373Sraghuram vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller,
6025373Sraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
6035373Sraghuram {
6045373Sraghuram 	struct ether_header	*ehp;
6055373Sraghuram 	mblk_t			*bp = NULL;
6066419Ssb155480 	vsw_fdbe_t		*fp;
6075373Sraghuram 
6085373Sraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
6095373Sraghuram 
6105373Sraghuram 	/*
6115373Sraghuram 	 * In layer 3 mode should only ever be switching packets
6125373Sraghuram 	 * between IP layer and vnet devices. So make sure thats
6135373Sraghuram 	 * who is invoking us.
6145373Sraghuram 	 */
6155373Sraghuram 	if ((caller != VSW_LOCALDEV) && (caller != VSW_VNETPORT)) {
6165373Sraghuram 		DERR(vswp, "%s: unexpected caller (%d)", __func__, caller);
6175373Sraghuram 		freemsgchain(mp);
6185373Sraghuram 		return;
6195373Sraghuram 	}
6205373Sraghuram 
6215373Sraghuram 	/* process the chain of packets */
6225373Sraghuram 	bp = mp;
6235373Sraghuram 	while (bp) {
6245373Sraghuram 		ehp = (struct ether_header *)bp->b_rptr;
6258275SEric Cheng 		mp = vsw_get_same_dest_list(ehp, &bp);
6268275SEric Cheng 		ASSERT(mp != NULL);
6275373Sraghuram 
6285373Sraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
6295373Sraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
6305373Sraghuram 
6316419Ssb155480 		/*
6326419Ssb155480 		 * Find fdb entry for the destination
6336419Ssb155480 		 * and hold a reference to it.
6346419Ssb155480 		 */
6356419Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
6366419Ssb155480 		if (fp != NULL) {
6375373Sraghuram 
6385373Sraghuram 			D2(vswp, "%s: sending to target port", __func__);
6398275SEric Cheng 			(void) vsw_portsend(fp->portp, mp);
6405373Sraghuram 
6416419Ssb155480 			/* Release the reference on the fdb entry */
6426419Ssb155480 			VSW_FDBE_REFRELE(fp);
6435373Sraghuram 		} else {
6445373Sraghuram 			/*
6455373Sraghuram 			 * Destination not in FDB
6465373Sraghuram 			 *
6475373Sraghuram 			 * If the destination is broadcast or
6485373Sraghuram 			 * multicast forward the packet to all
6495373Sraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
6505373Sraghuram 			 * except the caller.
6515373Sraghuram 			 */
6525373Sraghuram 			if (IS_BROADCAST(ehp)) {
6535373Sraghuram 				D2(vswp, "%s: BROADCAST pkt", __func__);
6545935Ssb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
6555373Sraghuram 			} else if (IS_MULTICAST(ehp)) {
6565373Sraghuram 				D2(vswp, "%s: MULTICAST pkt", __func__);
6575935Ssb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
6585373Sraghuram 			} else {
6595373Sraghuram 				/*
6605373Sraghuram 				 * Unicast pkt from vnet that we don't have
6615373Sraghuram 				 * an FDB entry for, so must be destinded for
6625373Sraghuram 				 * the outside world. Attempt to send up to the
6635373Sraghuram 				 * IP layer to allow it to deal with it.
6645373Sraghuram 				 */
6655373Sraghuram 				if (caller == VSW_VNETPORT) {
6665935Ssb155480 					vsw_mac_rx(vswp, mrh,
6675935Ssb155480 					    mp, VSW_MACRX_FREEMSG);
6685373Sraghuram 				}
6695373Sraghuram 			}
6705373Sraghuram 		}
6715373Sraghuram 	}
6725373Sraghuram 
6735373Sraghuram 	D1(vswp, "%s: exit", __func__);
6745373Sraghuram }
6755373Sraghuram 
6765373Sraghuram /*
6779819SSriharsha.Basavapatna@Sun.COM  * Additional initializations that are needed for the specific switching mode.
6787816SWentao.Yang@Sun.COM  */
6797816SWentao.Yang@Sun.COM void
vsw_setup_switching_post_process(vsw_t * vswp)6809819SSriharsha.Basavapatna@Sun.COM vsw_setup_switching_post_process(vsw_t *vswp)
6817816SWentao.Yang@Sun.COM {
6829819SSriharsha.Basavapatna@Sun.COM 	link_state_t	link_state = LINK_STATE_UP;
6839819SSriharsha.Basavapatna@Sun.COM 
6848275SEric Cheng 	if (vswp->smode & VSW_LAYER2) {
6857816SWentao.Yang@Sun.COM 		/*
6867816SWentao.Yang@Sun.COM 		 * Program unicst, mcst addrs of vsw
6877816SWentao.Yang@Sun.COM 		 * interface and ports in the physdev.
6887816SWentao.Yang@Sun.COM 		 */
6897816SWentao.Yang@Sun.COM 		vsw_set_addrs(vswp);
6907816SWentao.Yang@Sun.COM 
6917816SWentao.Yang@Sun.COM 		/* Start HIO for ports that have already connected */
6927816SWentao.Yang@Sun.COM 		vsw_hio_start_ports(vswp);
6939336SSriharsha.Basavapatna@Sun.COM 
6949819SSriharsha.Basavapatna@Sun.COM 		if (vswp->pls_update == B_TRUE) {
6959819SSriharsha.Basavapatna@Sun.COM 			link_state = vswp->phys_link_state;
6969819SSriharsha.Basavapatna@Sun.COM 		}
6979819SSriharsha.Basavapatna@Sun.COM 
6989336SSriharsha.Basavapatna@Sun.COM 		/* Update physical link info to any ports already connected */
6999819SSriharsha.Basavapatna@Sun.COM 		vsw_physlink_update_ports(vswp);
7007816SWentao.Yang@Sun.COM 	}
7019819SSriharsha.Basavapatna@Sun.COM 
7029819SSriharsha.Basavapatna@Sun.COM 	vsw_mac_link_update(vswp, link_state);
7037816SWentao.Yang@Sun.COM }
7047816SWentao.Yang@Sun.COM 
7057816SWentao.Yang@Sun.COM /*
7065373Sraghuram  * Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV),
7075373Sraghuram  * except the caller (port on which frame arrived).
7085373Sraghuram  */
7095373Sraghuram static int
vsw_forward_all(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg)7105935Ssb155480 vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
7115373Sraghuram {
7125373Sraghuram 	vsw_port_list_t	*plist = &vswp->plist;
7135373Sraghuram 	vsw_port_t	*portp;
7145373Sraghuram 	mblk_t		*nmp = NULL;
7155373Sraghuram 	mblk_t		*ret_m = NULL;
7165373Sraghuram 	int		skip_port = 0;
7175373Sraghuram 
7185373Sraghuram 	D1(vswp, "vsw_forward_all: enter\n");
7195373Sraghuram 
7205373Sraghuram 	/*
7215373Sraghuram 	 * Broadcast message from inside ldoms so send to outside
7225373Sraghuram 	 * world if in either of layer 2 modes.
7235373Sraghuram 	 */
7248275SEric Cheng 	if ((vswp->smode & VSW_LAYER2) &&
7255373Sraghuram 	    ((caller == VSW_LOCALDEV) || (caller == VSW_VNETPORT))) {
7265373Sraghuram 
7275373Sraghuram 		nmp = vsw_dupmsgchain(mp);
7285373Sraghuram 		if (nmp) {
7298275SEric Cheng 			if ((ret_m = vsw_tx_msg(vswp, nmp, caller, arg))
7308275SEric Cheng 			    != NULL) {
7315373Sraghuram 				DERR(vswp, "%s: dropping pkt(s) "
7325373Sraghuram 				    "consisting of %ld bytes of data for"
7335373Sraghuram 				    " physical device", __func__, MBLKL(ret_m));
7345373Sraghuram 				freemsgchain(ret_m);
7355373Sraghuram 			}
7365373Sraghuram 		}
7375373Sraghuram 	}
7385373Sraghuram 
7395373Sraghuram 	if (caller == VSW_VNETPORT)
7405373Sraghuram 		skip_port = 1;
7415373Sraghuram 
7425373Sraghuram 	/*
7435373Sraghuram 	 * Broadcast message from other vnet (layer 2 or 3) or outside
7445373Sraghuram 	 * world (layer 2 only), send up stack if plumbed.
7455373Sraghuram 	 */
7465373Sraghuram 	if ((caller == VSW_PHYSDEV) || (caller == VSW_VNETPORT)) {
7475935Ssb155480 		vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_COPYMSG);
7485373Sraghuram 	}
7495373Sraghuram 
7505373Sraghuram 	/* send it to all VNETPORTs */
7515373Sraghuram 	READ_ENTER(&plist->lockrw);
7525373Sraghuram 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
7535373Sraghuram 		D2(vswp, "vsw_forward_all: port %d", portp->p_instance);
7545373Sraghuram 		/*
7555373Sraghuram 		 * Caution ! - don't reorder these two checks as arg
7565373Sraghuram 		 * will be NULL if the caller is PHYSDEV. skip_port is
7575373Sraghuram 		 * only set if caller is VNETPORT.
7585373Sraghuram 		 */
7595373Sraghuram 		if ((skip_port) && (portp == arg)) {
7605373Sraghuram 			continue;
7615373Sraghuram 		} else {
7625373Sraghuram 			nmp = vsw_dupmsgchain(mp);
7635373Sraghuram 			if (nmp) {
7645373Sraghuram 				/*
7655373Sraghuram 				 * The plist->lockrw is protecting the
7665373Sraghuram 				 * portp from getting destroyed here.
7675373Sraghuram 				 * So, no ref_cnt is incremented here.
7685373Sraghuram 				 */
7698275SEric Cheng 				(void) vsw_portsend(portp, nmp);
7705373Sraghuram 			} else {
7715373Sraghuram 				DERR(vswp, "vsw_forward_all: nmp NULL");
7725373Sraghuram 			}
7735373Sraghuram 		}
7745373Sraghuram 	}
7755373Sraghuram 	RW_EXIT(&plist->lockrw);
7765373Sraghuram 
7775373Sraghuram 	freemsgchain(mp);
7785373Sraghuram 
7795373Sraghuram 	D1(vswp, "vsw_forward_all: exit\n");
7805373Sraghuram 	return (0);
7815373Sraghuram }
7825373Sraghuram 
7835373Sraghuram /*
7845373Sraghuram  * Forward pkts to any devices or interfaces which have registered
7855373Sraghuram  * an interest in them (i.e. multicast groups).
7865373Sraghuram  */
7875373Sraghuram static int
vsw_forward_grp(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg)7885935Ssb155480 vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
7895373Sraghuram {
7905373Sraghuram 	struct ether_header	*ehp = (struct ether_header *)mp->b_rptr;
7915373Sraghuram 	mfdb_ent_t		*entp = NULL;
7925373Sraghuram 	mfdb_ent_t		*tpp = NULL;
7935373Sraghuram 	vsw_port_t 		*port;
7945373Sraghuram 	uint64_t		key = 0;
7955373Sraghuram 	mblk_t			*nmp = NULL;
7965373Sraghuram 	mblk_t			*ret_m = NULL;
7975373Sraghuram 	boolean_t		check_if = B_TRUE;
7985373Sraghuram 
7995373Sraghuram 	/*
8005373Sraghuram 	 * Convert address to hash table key
8015373Sraghuram 	 */
8026419Ssb155480 	KEY_HASH(key, &ehp->ether_dhost);
8035373Sraghuram 
8045373Sraghuram 	D1(vswp, "%s: key 0x%llx", __func__, key);
8055373Sraghuram 
8065373Sraghuram 	/*
8075373Sraghuram 	 * If pkt came from either a vnet or down the stack (if we are
8085373Sraghuram 	 * plumbed) and we are in layer 2 mode, then we send the pkt out
8095373Sraghuram 	 * over the physical adapter, and then check to see if any other
8105373Sraghuram 	 * vnets are interested in it.
8115373Sraghuram 	 */
8128275SEric Cheng 	if ((vswp->smode & VSW_LAYER2) &&
8135373Sraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) {
8145373Sraghuram 		nmp = vsw_dupmsgchain(mp);
8155373Sraghuram 		if (nmp) {
8168275SEric Cheng 			if ((ret_m = vsw_tx_msg(vswp, nmp, caller, arg))
8178275SEric Cheng 			    != NULL) {
8185373Sraghuram 				DERR(vswp, "%s: dropping pkt(s) consisting of "
8195373Sraghuram 				    "%ld bytes of data for physical device",
8205373Sraghuram 				    __func__, MBLKL(ret_m));
8215373Sraghuram 				freemsgchain(ret_m);
8225373Sraghuram 			}
8235373Sraghuram 		}
8245373Sraghuram 	}
8255373Sraghuram 
8265373Sraghuram 	READ_ENTER(&vswp->mfdbrw);
8275373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key,
8285373Sraghuram 	    (mod_hash_val_t *)&entp) != 0) {
8295373Sraghuram 		D3(vswp, "%s: no table entry found for addr 0x%llx",
8305373Sraghuram 		    __func__, key);
8315373Sraghuram 	} else {
8325373Sraghuram 		/*
8335373Sraghuram 		 * Send to list of devices associated with this address...
8345373Sraghuram 		 */
8355373Sraghuram 		for (tpp = entp; tpp != NULL; tpp = tpp->nextp) {
8365373Sraghuram 
8375373Sraghuram 			/* dont send to ourselves */
8385373Sraghuram 			if ((caller == VSW_VNETPORT) &&
8395373Sraghuram 			    (tpp->d_addr == (void *)arg)) {
8405373Sraghuram 				port = (vsw_port_t *)tpp->d_addr;
8415373Sraghuram 				D3(vswp, "%s: not sending to ourselves"
8425373Sraghuram 				    " : port %d", __func__, port->p_instance);
8435373Sraghuram 				continue;
8445373Sraghuram 
8455373Sraghuram 			} else if ((caller == VSW_LOCALDEV) &&
8465373Sraghuram 			    (tpp->d_type == VSW_LOCALDEV)) {
8475935Ssb155480 				D2(vswp, "%s: not sending back up stack",
8485373Sraghuram 				    __func__);
8495373Sraghuram 				continue;
8505373Sraghuram 			}
8515373Sraghuram 
8525373Sraghuram 			if (tpp->d_type == VSW_VNETPORT) {
8535373Sraghuram 				port = (vsw_port_t *)tpp->d_addr;
8545373Sraghuram 				D3(vswp, "%s: sending to port %ld for addr "
8555373Sraghuram 				    "0x%llx", __func__, port->p_instance, key);
8565373Sraghuram 
8575373Sraghuram 				nmp = vsw_dupmsgchain(mp);
8585373Sraghuram 				if (nmp) {
8595373Sraghuram 					/*
8605373Sraghuram 					 * The vswp->mfdbrw is protecting the
8615373Sraghuram 					 * portp from getting destroyed here.
8625373Sraghuram 					 * So, no ref_cnt is incremented here.
8635373Sraghuram 					 */
8648275SEric Cheng 					(void) vsw_portsend(port, nmp);
8655373Sraghuram 				}
8665373Sraghuram 			} else {
8675935Ssb155480 				vsw_mac_rx(vswp, NULL,
8685935Ssb155480 				    mp, VSW_MACRX_COPYMSG);
8695935Ssb155480 				D2(vswp, "%s: sending up stack"
8705373Sraghuram 				    " for addr 0x%llx", __func__, key);
8715373Sraghuram 				check_if = B_FALSE;
8725373Sraghuram 			}
8735373Sraghuram 		}
8745373Sraghuram 	}
8755373Sraghuram 
8765373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
8775373Sraghuram 
8785373Sraghuram 	/*
8795373Sraghuram 	 * If the pkt came from either a vnet or from physical device,
8805373Sraghuram 	 * and if we havent already sent the pkt up the stack then we
8815373Sraghuram 	 * check now if we can/should (i.e. the interface is plumbed
8825373Sraghuram 	 * and in promisc mode).
8835373Sraghuram 	 */
8845373Sraghuram 	if ((check_if) &&
8855373Sraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) {
8865935Ssb155480 		vsw_mac_rx(vswp, NULL, mp,
8875373Sraghuram 		    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
8885373Sraghuram 	}
8895373Sraghuram 
8905373Sraghuram 	freemsgchain(mp);
8915373Sraghuram 
8925373Sraghuram 	D1(vswp, "%s: exit", __func__);
8935373Sraghuram 
8945373Sraghuram 	return (0);
8955373Sraghuram }
8965373Sraghuram 
8975373Sraghuram /*
8986419Ssb155480  * This function creates the vlan id hash table for the given vsw device or
8996419Ssb155480  * port. It then adds each vlan that the device or port has been assigned,
9006419Ssb155480  * into this hash table.
9016419Ssb155480  * Arguments:
9026419Ssb155480  *   arg:  vsw device or port.
9036419Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
9046419Ssb155480  */
9056419Ssb155480 void
vsw_create_vlans(void * arg,int type)9066419Ssb155480 vsw_create_vlans(void *arg, int type)
9076419Ssb155480 {
9086419Ssb155480 	/* create vlan hash table */
9096419Ssb155480 	vsw_vlan_create_hash(arg, type);
9106419Ssb155480 
9116419Ssb155480 	/* add vlan ids of the vsw device into its hash table */
9126419Ssb155480 	vsw_vlan_add_ids(arg, type);
9136419Ssb155480 }
9146419Ssb155480 
9156419Ssb155480 /*
9166419Ssb155480  * This function removes the vlan ids of the vsw device or port from its hash
9176419Ssb155480  * table. It then destroys the vlan hash table.
9186419Ssb155480  * Arguments:
9196419Ssb155480  *   arg:  vsw device or port.
9206419Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
9216419Ssb155480  */
9226419Ssb155480 void
vsw_destroy_vlans(void * arg,int type)9236419Ssb155480 vsw_destroy_vlans(void *arg, int type)
9246419Ssb155480 {
9256419Ssb155480 	/* remove vlan ids from the hash table */
9266419Ssb155480 	vsw_vlan_remove_ids(arg, type);
9276419Ssb155480 
9286419Ssb155480 	/* destroy vlan-hash-table */
9296419Ssb155480 	vsw_vlan_destroy_hash(arg, type);
9306419Ssb155480 }
9316419Ssb155480 
9326419Ssb155480 /*
9336419Ssb155480  * Create a vlan-id hash table for the given vsw device or port.
9346419Ssb155480  */
9356419Ssb155480 static void
vsw_vlan_create_hash(void * arg,int type)9366419Ssb155480 vsw_vlan_create_hash(void *arg, int type)
9376419Ssb155480 {
9386419Ssb155480 	char		hashname[MAXNAMELEN];
9396419Ssb155480 
9406419Ssb155480 	if (type == VSW_LOCALDEV) {
9416419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9426419Ssb155480 
9436419Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "vsw%d-vlan-hash",
9446419Ssb155480 		    vswp->instance);
9456419Ssb155480 
9466419Ssb155480 		vswp->vlan_nchains = vsw_vlan_nchains;
9476419Ssb155480 		vswp->vlan_hashp = mod_hash_create_idhash(hashname,
9486419Ssb155480 		    vswp->vlan_nchains, mod_hash_null_valdtor);
9496419Ssb155480 
9506419Ssb155480 	} else if (type == VSW_VNETPORT) {
9516419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
9526419Ssb155480 
9536419Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash",
9546419Ssb155480 		    portp->p_instance);
9556419Ssb155480 
9566419Ssb155480 		portp->vlan_nchains = vsw_vlan_nchains;
9576419Ssb155480 		portp->vlan_hashp = mod_hash_create_idhash(hashname,
9586419Ssb155480 		    portp->vlan_nchains, mod_hash_null_valdtor);
9596419Ssb155480 
9606419Ssb155480 	} else {
9616419Ssb155480 		return;
9626419Ssb155480 	}
9636419Ssb155480 }
9646419Ssb155480 
9656419Ssb155480 /*
9666419Ssb155480  * Destroy the vlan-id hash table for the given vsw device or port.
9676419Ssb155480  */
9686419Ssb155480 static void
vsw_vlan_destroy_hash(void * arg,int type)9696419Ssb155480 vsw_vlan_destroy_hash(void *arg, int type)
9706419Ssb155480 {
9716419Ssb155480 	if (type == VSW_LOCALDEV) {
9726419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9736419Ssb155480 
9746419Ssb155480 		mod_hash_destroy_hash(vswp->vlan_hashp);
9756419Ssb155480 		vswp->vlan_nchains = 0;
9766419Ssb155480 	} else if (type == VSW_VNETPORT) {
9776419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
9786419Ssb155480 
9796419Ssb155480 		mod_hash_destroy_hash(portp->vlan_hashp);
9806419Ssb155480 		portp->vlan_nchains = 0;
9816419Ssb155480 	} else {
9826419Ssb155480 		return;
9836419Ssb155480 	}
9846419Ssb155480 }
9856419Ssb155480 
9866419Ssb155480 /*
9876419Ssb155480  * Add vlan ids of the given vsw device or port into its hash table.
9885373Sraghuram  */
9896419Ssb155480 void
vsw_vlan_add_ids(void * arg,int type)9906419Ssb155480 vsw_vlan_add_ids(void *arg, int type)
9916419Ssb155480 {
9926419Ssb155480 	int	rv;
9936419Ssb155480 	int	i;
9946419Ssb155480 
9956419Ssb155480 	if (type == VSW_LOCALDEV) {
9966419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
9976419Ssb155480 
9986419Ssb155480 		rv = mod_hash_insert(vswp->vlan_hashp,
9996419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
10006419Ssb155480 		    (mod_hash_val_t)B_TRUE);
10018275SEric Cheng 		if (rv != 0) {
10028275SEric Cheng 			cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d) for "
10038275SEric Cheng 			    "the interface", vswp->instance, vswp->pvid);
10048275SEric Cheng 		}
10056419Ssb155480 
10066419Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
10076419Ssb155480 			rv = mod_hash_insert(vswp->vlan_hashp,
10088275SEric Cheng 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i].vl_vid),
10096419Ssb155480 			    (mod_hash_val_t)B_TRUE);
10108275SEric Cheng 			if (rv != 0) {
10118275SEric Cheng 				cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d)"
10128275SEric Cheng 				    " for the interface", vswp->instance,
10138275SEric Cheng 				    vswp->pvid);
10148275SEric Cheng 			}
10156419Ssb155480 		}
10166419Ssb155480 
10176419Ssb155480 	} else if (type == VSW_VNETPORT) {
10186419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
10198275SEric Cheng 		vsw_t		*vswp = portp->p_vswp;
10206419Ssb155480 
10216419Ssb155480 		rv = mod_hash_insert(portp->vlan_hashp,
10226419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
10236419Ssb155480 		    (mod_hash_val_t)B_TRUE);
10248275SEric Cheng 		if (rv != 0) {
10258275SEric Cheng 			cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d) for "
10268275SEric Cheng 			    "the port(%d)", vswp->instance, vswp->pvid,
10278275SEric Cheng 			    portp->p_instance);
10288275SEric Cheng 		}
10296419Ssb155480 
10306419Ssb155480 		for (i = 0; i < portp->nvids; i++) {
10316419Ssb155480 			rv = mod_hash_insert(portp->vlan_hashp,
10328275SEric Cheng 			    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i].vl_vid),
10336419Ssb155480 			    (mod_hash_val_t)B_TRUE);
10348275SEric Cheng 			if (rv != 0) {
10358275SEric Cheng 				cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d)"
10368275SEric Cheng 				    " for the port(%d)", vswp->instance,
10378275SEric Cheng 				    vswp->pvid, portp->p_instance);
10388275SEric Cheng 			}
10396419Ssb155480 		}
10406419Ssb155480 
10416419Ssb155480 	}
10426419Ssb155480 }
10436419Ssb155480 
10446419Ssb155480 /*
10456419Ssb155480  * Remove vlan ids of the given vsw device or port from its hash table.
10466419Ssb155480  */
10476419Ssb155480 void
vsw_vlan_remove_ids(void * arg,int type)10486419Ssb155480 vsw_vlan_remove_ids(void *arg, int type)
10496419Ssb155480 {
10506419Ssb155480 	mod_hash_val_t	vp;
10516419Ssb155480 	int		rv;
10526419Ssb155480 	int		i;
10536419Ssb155480 
10546419Ssb155480 	if (type == VSW_LOCALDEV) {
10556419Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
10566419Ssb155480 
10576419Ssb155480 		rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->pvid);
10586419Ssb155480 		if (rv == B_TRUE) {
10596419Ssb155480 			rv = mod_hash_remove(vswp->vlan_hashp,
10606419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
10616419Ssb155480 			    (mod_hash_val_t *)&vp);
10626419Ssb155480 			ASSERT(rv == 0);
10636419Ssb155480 		}
10646419Ssb155480 
10656419Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
10668275SEric Cheng 			rv = vsw_vlan_lookup(vswp->vlan_hashp,
10678275SEric Cheng 			    vswp->vids[i].vl_vid);
10686419Ssb155480 			if (rv == B_TRUE) {
10696419Ssb155480 				rv = mod_hash_remove(vswp->vlan_hashp,
10708275SEric Cheng 				    (mod_hash_key_t)VLAN_ID_KEY(
10718275SEric Cheng 				    vswp->vids[i].vl_vid),
10726419Ssb155480 				    (mod_hash_val_t *)&vp);
10736419Ssb155480 				ASSERT(rv == 0);
10746419Ssb155480 			}
10756419Ssb155480 		}
10766419Ssb155480 
10776419Ssb155480 	} else if (type == VSW_VNETPORT) {
10786419Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
10796419Ssb155480 
10806419Ssb155480 		portp = (vsw_port_t *)arg;
10816419Ssb155480 		rv = vsw_vlan_lookup(portp->vlan_hashp, portp->pvid);
10826419Ssb155480 		if (rv == B_TRUE) {
10836419Ssb155480 			rv = mod_hash_remove(portp->vlan_hashp,
10846419Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
10856419Ssb155480 			    (mod_hash_val_t *)&vp);
10866419Ssb155480 			ASSERT(rv == 0);
10876419Ssb155480 		}
10886419Ssb155480 
10896419Ssb155480 		for (i = 0; i < portp->nvids; i++) {
10908275SEric Cheng 			rv = vsw_vlan_lookup(portp->vlan_hashp,
10918275SEric Cheng 			    portp->vids[i].vl_vid);
10926419Ssb155480 			if (rv == B_TRUE) {
10936419Ssb155480 				rv = mod_hash_remove(portp->vlan_hashp,
10948275SEric Cheng 				    (mod_hash_key_t)VLAN_ID_KEY(
10958275SEric Cheng 				    portp->vids[i].vl_vid),
10966419Ssb155480 				    (mod_hash_val_t *)&vp);
10976419Ssb155480 				ASSERT(rv == 0);
10986419Ssb155480 			}
10996419Ssb155480 		}
11006419Ssb155480 
11016419Ssb155480 	} else {
11026419Ssb155480 		return;
11036419Ssb155480 	}
11046419Ssb155480 }
11056419Ssb155480 
11066419Ssb155480 /*
11076419Ssb155480  * Find the given vlan id in the hash table.
11086419Ssb155480  * Return: B_TRUE if the id is found; B_FALSE if not found.
11096419Ssb155480  */
11106419Ssb155480 boolean_t
vsw_vlan_lookup(mod_hash_t * vlan_hashp,uint16_t vid)11116419Ssb155480 vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid)
11126419Ssb155480 {
11136419Ssb155480 	int		rv;
11146419Ssb155480 	mod_hash_val_t	vp;
11156419Ssb155480 
11166419Ssb155480 	rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp);
11176419Ssb155480 
11186419Ssb155480 	if (rv != 0)
11196419Ssb155480 		return (B_FALSE);
11206419Ssb155480 
11216419Ssb155480 	return (B_TRUE);
11226419Ssb155480 }
11236419Ssb155480 
11246419Ssb155480 /*
11256419Ssb155480  * Add an entry into FDB for the given vsw.
11266419Ssb155480  */
11276419Ssb155480 void
vsw_fdbe_add(vsw_t * vswp,void * port)11286419Ssb155480 vsw_fdbe_add(vsw_t *vswp, void *port)
11295373Sraghuram {
11305373Sraghuram 	uint64_t	addr = 0;
11316419Ssb155480 	vsw_port_t	*portp;
11326419Ssb155480 	vsw_fdbe_t	*fp;
11336419Ssb155480 	int		rv;
11345373Sraghuram 
11356419Ssb155480 	portp = (vsw_port_t *)port;
11366419Ssb155480 	KEY_HASH(addr, &portp->p_macaddr);
11375373Sraghuram 
11386419Ssb155480 	fp = kmem_zalloc(sizeof (vsw_fdbe_t), KM_SLEEP);
11396419Ssb155480 	fp->portp = port;
11405373Sraghuram 
11415373Sraghuram 	/*
11425373Sraghuram 	 * Note: duplicate keys will be rejected by mod_hash.
11435373Sraghuram 	 */
11446419Ssb155480 	rv = mod_hash_insert(vswp->fdb_hashp, (mod_hash_key_t)addr,
11456419Ssb155480 	    (mod_hash_val_t)fp);
11468275SEric Cheng 	if (rv != 0) {
11478275SEric Cheng 		cmn_err(CE_WARN, "vsw%d: Duplicate mac-address(%s) for "
11488275SEric Cheng 		    "the port(%d)", vswp->instance,
11498275SEric Cheng 		    ether_sprintf(&portp->p_macaddr), portp->p_instance);
1150*12300SSriharsha.Basavapatna@Sun.COM 		kmem_free(fp, sizeof (*fp));
11518275SEric Cheng 	}
11525373Sraghuram }
11535373Sraghuram 
11545373Sraghuram /*
11555373Sraghuram  * Remove an entry from FDB.
11565373Sraghuram  */
11576419Ssb155480 void
vsw_fdbe_del(vsw_t * vswp,struct ether_addr * eaddr)11586419Ssb155480 vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr)
11595373Sraghuram {
11605373Sraghuram 	uint64_t	addr = 0;
11616419Ssb155480 	vsw_fdbe_t	*fp;
11626419Ssb155480 	int		rv;
11635373Sraghuram 
11646419Ssb155480 	KEY_HASH(addr, eaddr);
11656419Ssb155480 
11666419Ssb155480 	/*
11676419Ssb155480 	 * Remove the entry from fdb hash table.
11686419Ssb155480 	 * This prevents further references to this fdb entry.
11696419Ssb155480 	 */
11706419Ssb155480 	rv = mod_hash_remove(vswp->fdb_hashp, (mod_hash_key_t)addr,
11716419Ssb155480 	    (mod_hash_val_t *)&fp);
11726419Ssb155480 	if (rv != 0) {
11736419Ssb155480 		/* invalid key? */
11746419Ssb155480 		return;
11756419Ssb155480 	}
11766419Ssb155480 
11776419Ssb155480 	/*
11786419Ssb155480 	 * If there are threads already ref holding before the entry was
11796419Ssb155480 	 * removed from hash table, then wait for ref count to drop to zero.
11806419Ssb155480 	 */
11816419Ssb155480 	while (fp->refcnt != 0) {
11826419Ssb155480 		delay(drv_usectohz(vsw_fdbe_refcnt_delay));
11836419Ssb155480 	}
11846419Ssb155480 
11856419Ssb155480 	kmem_free(fp, sizeof (*fp));
11866419Ssb155480 }
11875373Sraghuram 
11886419Ssb155480 /*
11896419Ssb155480  * Search fdb for a given mac address. If an entry is found, hold
11906419Ssb155480  * a reference to it and return the entry, else returns NULL.
11916419Ssb155480  */
11926419Ssb155480 static vsw_fdbe_t *
vsw_fdbe_find(vsw_t * vswp,struct ether_addr * addrp)11936419Ssb155480 vsw_fdbe_find(vsw_t *vswp, struct ether_addr *addrp)
11946419Ssb155480 {
11956419Ssb155480 	uint64_t	key = 0;
11966419Ssb155480 	vsw_fdbe_t	*fp;
11976419Ssb155480 	int		rv;
11986419Ssb155480 
11996419Ssb155480 	KEY_HASH(key, addrp);
12006419Ssb155480 
12016419Ssb155480 	rv = mod_hash_find_cb(vswp->fdb_hashp, (mod_hash_key_t)key,
12026419Ssb155480 	    (mod_hash_val_t *)&fp, vsw_fdbe_find_cb);
12036419Ssb155480 
12046419Ssb155480 	if (rv != 0)
12056419Ssb155480 		return (NULL);
12066419Ssb155480 
12076419Ssb155480 	return (fp);
12086419Ssb155480 }
12096419Ssb155480 
12106419Ssb155480 /*
12116419Ssb155480  * Callback function provided to mod_hash_find_cb(). After finding the fdb
12126419Ssb155480  * entry corresponding to the key (macaddr), this callback will be invoked by
12136419Ssb155480  * mod_hash_find_cb() to atomically increment the reference count on the fdb
12146419Ssb155480  * entry before returning the found entry.
12156419Ssb155480  */
12166419Ssb155480 static void
vsw_fdbe_find_cb(mod_hash_key_t key,mod_hash_val_t val)12176419Ssb155480 vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
12186419Ssb155480 {
12196419Ssb155480 	_NOTE(ARGUNUSED(key))
12206419Ssb155480 	VSW_FDBE_REFHOLD((vsw_fdbe_t *)val);
12216419Ssb155480 }
12225373Sraghuram 
12236419Ssb155480 /*
12246419Ssb155480  * A given frame must be always tagged with the appropriate vlan id (unless it
12256419Ssb155480  * is in the default-vlan) before the mac address switching function is called.
12266419Ssb155480  * Otherwise, after switching function determines the destination, we cannot
12276419Ssb155480  * figure out if the destination belongs to the the same vlan that the frame
12286419Ssb155480  * originated from and if it needs tag/untag. Frames which are inbound from
12296419Ssb155480  * the external(physical) network over a vlan trunk link are always tagged.
12306419Ssb155480  * However frames which are received from a vnet-port over ldc or frames which
12316419Ssb155480  * are coming down the stack on the service domain over vsw interface may be
12326419Ssb155480  * untagged. These frames must be tagged with the appropriate pvid of the
12336419Ssb155480  * sender (vnet-port or vsw device), before invoking the switching function.
12346419Ssb155480  *
12356419Ssb155480  * Arguments:
12366419Ssb155480  *   arg:    caller of the function.
12376419Ssb155480  *   type:   type of arg(caller): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
12386419Ssb155480  *   mp:     frame(s) to be tagged.
12396419Ssb155480  */
12406419Ssb155480 mblk_t *
vsw_vlan_frame_pretag(void * arg,int type,mblk_t * mp)12416419Ssb155480 vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp)
12426419Ssb155480 {
12436419Ssb155480 	vsw_t			*vswp;
12446419Ssb155480 	vsw_port_t		*portp;
12456419Ssb155480 	struct ether_header	*ehp;
12466419Ssb155480 	mblk_t			*bp;
12476419Ssb155480 	mblk_t			*bpt;
12486419Ssb155480 	mblk_t			*bph;
12496419Ssb155480 	mblk_t			*bpn;
12506419Ssb155480 	uint16_t		pvid;
12515373Sraghuram 
12526419Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
12535373Sraghuram 
12546419Ssb155480 	if (type == VSW_LOCALDEV) {
12556419Ssb155480 		vswp = (vsw_t *)arg;
12566419Ssb155480 		pvid = vswp->pvid;
12576419Ssb155480 		portp = NULL;
12586419Ssb155480 	} else {
12596419Ssb155480 		/* VSW_VNETPORT */
12606419Ssb155480 		portp = (vsw_port_t *)arg;
12616419Ssb155480 		pvid = portp->pvid;
12626419Ssb155480 		vswp = portp->p_vswp;
12636419Ssb155480 	}
12646419Ssb155480 
12656419Ssb155480 	bpn = bph = bpt = NULL;
12666419Ssb155480 
12676419Ssb155480 	for (bp = mp; bp != NULL; bp = bpn) {
12686419Ssb155480 
12696419Ssb155480 		bpn = bp->b_next;
12706419Ssb155480 		bp->b_next = bp->b_prev = NULL;
12716419Ssb155480 
12726419Ssb155480 		/* Determine if it is an untagged frame */
12736419Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
12745373Sraghuram 
12756419Ssb155480 		if (ehp->ether_type != ETHERTYPE_VLAN) {	/* untagged */
12766419Ssb155480 
12776419Ssb155480 			/* no need to tag if the frame is in default vlan */
12786419Ssb155480 			if (pvid != vswp->default_vlan_id) {
12796419Ssb155480 				bp = vnet_vlan_insert_tag(bp, pvid);
12806419Ssb155480 				if (bp == NULL) {
12816419Ssb155480 					continue;
12826419Ssb155480 				}
12836419Ssb155480 			}
12846419Ssb155480 		}
12856419Ssb155480 
12866419Ssb155480 		/* build a chain of processed packets */
12876419Ssb155480 		if (bph == NULL) {
12886419Ssb155480 			bph = bpt = bp;
12896419Ssb155480 		} else {
12906419Ssb155480 			bpt->b_next = bp;
12916419Ssb155480 			bpt = bp;
12926419Ssb155480 		}
12936419Ssb155480 
12946419Ssb155480 	}
12956419Ssb155480 
12966419Ssb155480 	return (bph);
12975373Sraghuram }
12985373Sraghuram 
12995373Sraghuram /*
13006419Ssb155480  * Frames destined to a vnet-port or to the local vsw interface, must be
13016419Ssb155480  * untagged if necessary before sending. This function first checks that the
13026419Ssb155480  * frame can be sent to the destination in the vlan identified by the frame
13036419Ssb155480  * tag. Note that when this function is invoked the frame must have been
13046419Ssb155480  * already tagged (unless it is in the default-vlan). Because, this function is
13056419Ssb155480  * called when the switching function determines the destination and invokes
13066419Ssb155480  * its send function (vnet-port or vsw interface) and all frames would have
13076419Ssb155480  * been tagged by this time (see comments in vsw_vlan_frame_pretag()).
13086419Ssb155480  *
13096419Ssb155480  * Arguments:
13106419Ssb155480  *   arg:    destination device.
13116419Ssb155480  *   type:   type of arg(destination): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
13126419Ssb155480  *   np:     head of pkt chain to be validated and untagged.
13136419Ssb155480  *   npt:    tail of pkt chain to be validated and untagged.
13146419Ssb155480  *
13156419Ssb155480  * Returns:
13166419Ssb155480  *   np:     head of updated chain of packets
13176419Ssb155480  *   npt:    tail of updated chain of packets
13188275SEric Cheng  *   rv:     count of the packets in the returned list
13195373Sraghuram  */
13206419Ssb155480 uint32_t
vsw_vlan_frame_untag(void * arg,int type,mblk_t ** np,mblk_t ** npt)13216419Ssb155480 vsw_vlan_frame_untag(void *arg, int type, mblk_t **np, mblk_t **npt)
13225373Sraghuram {
13236419Ssb155480 	mblk_t			*bp;
13246419Ssb155480 	mblk_t			*bpt;
13256419Ssb155480 	mblk_t			*bph;
13266419Ssb155480 	mblk_t			*bpn;
13276419Ssb155480 	vsw_port_t		*portp;
13286419Ssb155480 	vsw_t			*vswp;
13296419Ssb155480 	uint32_t		count;
13306419Ssb155480 	struct ether_header	*ehp;
13316419Ssb155480 	boolean_t		is_tagged;
13326419Ssb155480 	boolean_t		rv;
13336419Ssb155480 	uint16_t		vlan_id;
13346419Ssb155480 	uint16_t		pvid;
13356419Ssb155480 	mod_hash_t		*vlan_hashp;
13365373Sraghuram 
13376419Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
13385373Sraghuram 
13398275SEric Cheng 
13406419Ssb155480 	if (type == VSW_LOCALDEV) {
13416419Ssb155480 		vswp = (vsw_t *)arg;
13426419Ssb155480 		pvid = vswp->pvid;
13436419Ssb155480 		vlan_hashp = vswp->vlan_hashp;
13446419Ssb155480 		portp = NULL;
13456419Ssb155480 	} else {
13466419Ssb155480 		/* type == VSW_VNETPORT */
13476419Ssb155480 		portp = (vsw_port_t *)arg;
13486419Ssb155480 		vswp = portp->p_vswp;
13496419Ssb155480 		vlan_hashp = portp->vlan_hashp;
13506419Ssb155480 		pvid = portp->pvid;
13515373Sraghuram 	}
13525373Sraghuram 
13538275SEric Cheng 	/*
13548275SEric Cheng 	 * If the MAC layer switching in place, then
13558275SEric Cheng 	 * untagging required only if the pvid is not
13568275SEric Cheng 	 * the same as default_vlan_id. This is because,
13578275SEric Cheng 	 * the MAC layer will send packets for the
13588275SEric Cheng 	 * registered vlans only.
13598275SEric Cheng 	 */
13608275SEric Cheng 	if ((vswp->mac_cl_switching == B_TRUE) &&
13618275SEric Cheng 	    (pvid == vswp->default_vlan_id)) {
13628275SEric Cheng 		/* simply count and set the tail */
13638275SEric Cheng 		count = 1;
13648275SEric Cheng 		bp = *np;
13658275SEric Cheng 		ASSERT(bp != NULL);
13668275SEric Cheng 		while (bp->b_next != NULL) {
13678275SEric Cheng 			bp = bp->b_next;
13688275SEric Cheng 			count++;
13698275SEric Cheng 		}
13708275SEric Cheng 		*npt = bp;
13718275SEric Cheng 		return (count);
13728275SEric Cheng 	}
13738275SEric Cheng 
13746419Ssb155480 	bpn = bph = bpt = NULL;
13756419Ssb155480 	count = 0;
13766419Ssb155480 
13776419Ssb155480 	for (bp = *np; bp != NULL; bp = bpn) {
13786419Ssb155480 
13796419Ssb155480 		bpn = bp->b_next;
13806419Ssb155480 		bp->b_next = bp->b_prev = NULL;
13816419Ssb155480 
13826419Ssb155480 		/*
13836419Ssb155480 		 * Determine the vlan id that the frame belongs to.
13846419Ssb155480 		 */
13856419Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
13866419Ssb155480 		is_tagged = vsw_frame_lookup_vid(arg, type, ehp, &vlan_id);
13876419Ssb155480 
13886419Ssb155480 		/*
13898275SEric Cheng 		 * If MAC layer switching in place, then we
13908275SEric Cheng 		 * need to untag only if the tagged packet has
13918275SEric Cheng 		 * vlan-id same as the pvid.
13926419Ssb155480 		 */
13938275SEric Cheng 		if (vswp->mac_cl_switching == B_TRUE) {
13948275SEric Cheng 
13958275SEric Cheng 			/* only tagged packets expected here */
13968275SEric Cheng 			ASSERT(is_tagged == B_TRUE);
13976419Ssb155480 			if (vlan_id == pvid) {
13986419Ssb155480 				bp = vnet_vlan_remove_tag(bp);
13996419Ssb155480 				if (bp == NULL) {
14006419Ssb155480 					/* packet dropped */
14018275SEric Cheng 					continue;
14028275SEric Cheng 				}
14038275SEric Cheng 			}
14048275SEric Cheng 		} else { /* No MAC layer switching */
14058275SEric Cheng 
14068275SEric Cheng 			/*
14078275SEric Cheng 			 * Check the frame header if tag/untag is  needed.
14088275SEric Cheng 			 */
14098275SEric Cheng 			if (is_tagged == B_FALSE) {
14108275SEric Cheng 				/*
14118275SEric Cheng 				 * Untagged frame. We shouldn't have an
14128275SEric Cheng 				 * untagged packet at this point, unless
14138275SEric Cheng 				 * the destination's  vlan id is
14148275SEric Cheng 				 * default-vlan-id; if it is not the
14158275SEric Cheng 				 * default-vlan-id, we drop the packet.
14168275SEric Cheng 				 */
14178275SEric Cheng 				if (vlan_id != vswp->default_vlan_id) {
14188275SEric Cheng 					/* drop the packet */
14198275SEric Cheng 					freemsg(bp);
14206419Ssb155480 					continue;
14216419Ssb155480 				}
14228275SEric Cheng 			} else {	/* Tagged */
14238275SEric Cheng 				/*
14248275SEric Cheng 				 * Tagged frame, untag if it's the
14258275SEric Cheng 				 * destination's pvid.
14268275SEric Cheng 				 */
14278275SEric Cheng 				if (vlan_id == pvid) {
14288275SEric Cheng 
14298275SEric Cheng 					bp = vnet_vlan_remove_tag(bp);
14308275SEric Cheng 					if (bp == NULL) {
14318275SEric Cheng 						/* packet dropped */
14328275SEric Cheng 						continue;
14338275SEric Cheng 					}
14348275SEric Cheng 				} else {
14358275SEric Cheng 
14368275SEric Cheng 					/*
14378275SEric Cheng 					 * Check if the destination is in the
14388275SEric Cheng 					 * same vlan.
14398275SEric Cheng 					 */
14408275SEric Cheng 					rv = vsw_vlan_lookup(vlan_hashp,
14418275SEric Cheng 					    vlan_id);
14428275SEric Cheng 					if (rv == B_FALSE) {
14438275SEric Cheng 						/* drop the packet */
14448275SEric Cheng 						freemsg(bp);
14458275SEric Cheng 						continue;
14468275SEric Cheng 					}
14478275SEric Cheng 				}
14488275SEric Cheng 
14496419Ssb155480 			}
14506419Ssb155480 		}
14515373Sraghuram 
14526419Ssb155480 		/* build a chain of processed packets */
14536419Ssb155480 		if (bph == NULL) {
14546419Ssb155480 			bph = bpt = bp;
14556419Ssb155480 		} else {
14566419Ssb155480 			bpt->b_next = bp;
14576419Ssb155480 			bpt = bp;
14586419Ssb155480 		}
14598275SEric Cheng 		count++;
14606419Ssb155480 	}
14616419Ssb155480 
14626419Ssb155480 	*np = bph;
14636419Ssb155480 	*npt = bpt;
14646419Ssb155480 	return (count);
14656419Ssb155480 }
14666419Ssb155480 
14676419Ssb155480 /*
14686419Ssb155480  * Lookup the vlan id of the given frame. If it is a vlan-tagged frame,
14696419Ssb155480  * then the vlan-id is available in the tag; otherwise, its vlan id is
14706419Ssb155480  * implicitly obtained based on the caller (destination of the frame:
14716419Ssb155480  * VSW_VNETPORT or VSW_LOCALDEV).
14726419Ssb155480  * The vlan id determined is returned in vidp.
14736419Ssb155480  * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
14746419Ssb155480  */
14756419Ssb155480 boolean_t
vsw_frame_lookup_vid(void * arg,int caller,struct ether_header * ehp,uint16_t * vidp)14766419Ssb155480 vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp,
14776419Ssb155480 	uint16_t *vidp)
14786419Ssb155480 {
14796419Ssb155480 	struct ether_vlan_header	*evhp;
14806419Ssb155480 	vsw_t				*vswp;
14816419Ssb155480 	vsw_port_t			*portp;
14826419Ssb155480 
14836419Ssb155480 	/* If it's a tagged frame, get the vid from vlan header */
14846419Ssb155480 	if (ehp->ether_type == ETHERTYPE_VLAN) {
14856419Ssb155480 
14866419Ssb155480 		evhp = (struct ether_vlan_header *)ehp;
14876419Ssb155480 		*vidp = VLAN_ID(ntohs(evhp->ether_tci));
14886419Ssb155480 		return (B_TRUE);
14896419Ssb155480 	}
14906419Ssb155480 
14916419Ssb155480 	/* Untagged frame; determine vlan id based on caller */
14926419Ssb155480 	switch (caller) {
14936419Ssb155480 
14946419Ssb155480 	case VSW_VNETPORT:
14956419Ssb155480 		/*
14966419Ssb155480 		 * packet destined to a vnet; vlan-id is pvid of vnet-port.
14976419Ssb155480 		 */
14986419Ssb155480 		portp = (vsw_port_t *)arg;
14996419Ssb155480 		*vidp = portp->pvid;
15006419Ssb155480 		break;
15016419Ssb155480 
15026419Ssb155480 	case VSW_LOCALDEV:
15036419Ssb155480 
15046419Ssb155480 		/*
15056419Ssb155480 		 * packet destined to vsw interface;
15066419Ssb155480 		 * vlan-id is port-vlan-id of vsw device.
15076419Ssb155480 		 */
15086419Ssb155480 		vswp = (vsw_t *)arg;
15096419Ssb155480 		*vidp = vswp->pvid;
15106419Ssb155480 		break;
15116419Ssb155480 	}
15126419Ssb155480 
15136419Ssb155480 	return (B_FALSE);
15145373Sraghuram }
15155373Sraghuram 
15165373Sraghuram /*
15175373Sraghuram  * Add or remove multicast address(es).
15185373Sraghuram  *
15195373Sraghuram  * Returns 0 on success, 1 on failure.
15205373Sraghuram  */
15215373Sraghuram int
vsw_add_rem_mcst(vnet_mcast_msg_t * mcst_pkt,vsw_port_t * port)15225373Sraghuram vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port)
15235373Sraghuram {
15245373Sraghuram 	mcst_addr_t		*mcst_p = NULL;
15255373Sraghuram 	vsw_t			*vswp = port->p_vswp;
15265373Sraghuram 	uint64_t		addr = 0x0;
15275373Sraghuram 	int			i;
15285373Sraghuram 
15295373Sraghuram 	D1(vswp, "%s: enter", __func__);
15305373Sraghuram 
15315373Sraghuram 	D2(vswp, "%s: %d addresses", __func__, mcst_pkt->count);
15325373Sraghuram 
15335373Sraghuram 	for (i = 0; i < mcst_pkt->count; i++) {
15345373Sraghuram 		/*
15355373Sraghuram 		 * Convert address into form that can be used
15365373Sraghuram 		 * as hash table key.
15375373Sraghuram 		 */
15386419Ssb155480 		KEY_HASH(addr, &(mcst_pkt->mca[i]));
15395373Sraghuram 
15405373Sraghuram 		/*
15415373Sraghuram 		 * Add or delete the specified address/port combination.
15425373Sraghuram 		 */
15435373Sraghuram 		if (mcst_pkt->set == 0x1) {
15445373Sraghuram 			D3(vswp, "%s: adding multicast address 0x%llx for "
15455373Sraghuram 			    "port %ld", __func__, addr, port->p_instance);
15465373Sraghuram 			if (vsw_add_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
15475373Sraghuram 				/*
15485373Sraghuram 				 * Update the list of multicast
15495373Sraghuram 				 * addresses contained within the
15505373Sraghuram 				 * port structure to include this new
15515373Sraghuram 				 * one.
15525373Sraghuram 				 */
15535373Sraghuram 				mcst_p = kmem_zalloc(sizeof (mcst_addr_t),
15545373Sraghuram 				    KM_NOSLEEP);
15555373Sraghuram 				if (mcst_p == NULL) {
15565373Sraghuram 					DERR(vswp, "%s: unable to alloc mem",
15575373Sraghuram 					    __func__);
15585373Sraghuram 					(void) vsw_del_mcst(vswp,
15595373Sraghuram 					    VSW_VNETPORT, addr, port);
15605373Sraghuram 					return (1);
15615373Sraghuram 				}
15625373Sraghuram 
15635373Sraghuram 				mcst_p->nextp = NULL;
15645373Sraghuram 				mcst_p->addr = addr;
15655373Sraghuram 				ether_copy(&mcst_pkt->mca[i], &mcst_p->mca);
15665373Sraghuram 
15675373Sraghuram 				/*
15685373Sraghuram 				 * Program the address into HW. If the addr
15695373Sraghuram 				 * has already been programmed then the MAC
15705373Sraghuram 				 * just increments a ref counter (which is
15715373Sraghuram 				 * used when the address is being deleted)
15725373Sraghuram 				 */
15738275SEric Cheng 				if (vsw_mac_multicast_add(vswp, port, mcst_p,
15748275SEric Cheng 				    VSW_VNETPORT)) {
15758275SEric Cheng 					(void) vsw_del_mcst(vswp,
15768275SEric Cheng 					    VSW_VNETPORT, addr, port);
15778275SEric Cheng 					kmem_free(mcst_p, sizeof (*mcst_p));
15788275SEric Cheng 					return (1);
15795373Sraghuram 				}
15805373Sraghuram 
15815373Sraghuram 				mutex_enter(&port->mca_lock);
15825373Sraghuram 				mcst_p->nextp = port->mcap;
15835373Sraghuram 				port->mcap = mcst_p;
15845373Sraghuram 				mutex_exit(&port->mca_lock);
15855373Sraghuram 
15865373Sraghuram 			} else {
15875373Sraghuram 				DERR(vswp, "%s: error adding multicast "
15885373Sraghuram 				    "address 0x%llx for port %ld",
15895373Sraghuram 				    __func__, addr, port->p_instance);
15905373Sraghuram 				return (1);
15915373Sraghuram 			}
15925373Sraghuram 		} else {
15935373Sraghuram 			/*
15945373Sraghuram 			 * Delete an entry from the multicast hash
15955373Sraghuram 			 * table and update the address list
15965373Sraghuram 			 * appropriately.
15975373Sraghuram 			 */
15985373Sraghuram 			if (vsw_del_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
15995373Sraghuram 				D3(vswp, "%s: deleting multicast address "
16005373Sraghuram 				    "0x%llx for port %ld", __func__, addr,
16015373Sraghuram 				    port->p_instance);
16025373Sraghuram 
16035373Sraghuram 				mcst_p = vsw_del_addr(VSW_VNETPORT, port, addr);
16045373Sraghuram 				ASSERT(mcst_p != NULL);
16055373Sraghuram 
16065373Sraghuram 				/*
16075373Sraghuram 				 * Remove the address from HW. The address
16085373Sraghuram 				 * will actually only be removed once the ref
16095373Sraghuram 				 * count within the MAC layer has dropped to
16105373Sraghuram 				 * zero. I.e. we can safely call this fn even
16115373Sraghuram 				 * if other ports are interested in this
16125373Sraghuram 				 * address.
16135373Sraghuram 				 */
16148275SEric Cheng 				vsw_mac_multicast_remove(vswp, port, mcst_p,
16158275SEric Cheng 				    VSW_VNETPORT);
16165373Sraghuram 				kmem_free(mcst_p, sizeof (*mcst_p));
16175373Sraghuram 
16185373Sraghuram 			} else {
16195373Sraghuram 				DERR(vswp, "%s: error deleting multicast "
16205373Sraghuram 				    "addr 0x%llx for port %ld",
16215373Sraghuram 				    __func__, addr, port->p_instance);
16225373Sraghuram 				return (1);
16235373Sraghuram 			}
16245373Sraghuram 		}
16255373Sraghuram 	}
16265373Sraghuram 	D1(vswp, "%s: exit", __func__);
16275373Sraghuram 	return (0);
16285373Sraghuram }
16295373Sraghuram 
16305373Sraghuram /*
16315373Sraghuram  * Add a new multicast entry.
16325373Sraghuram  *
16335373Sraghuram  * Search hash table based on address. If match found then
16345373Sraghuram  * update associated val (which is chain of ports), otherwise
16355373Sraghuram  * create new key/val (addr/port) pair and insert into table.
16365373Sraghuram  */
16375373Sraghuram int
vsw_add_mcst(vsw_t * vswp,uint8_t devtype,uint64_t addr,void * arg)16385373Sraghuram vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
16395373Sraghuram {
16405373Sraghuram 	int		dup = 0;
16415373Sraghuram 	int		rv = 0;
16425373Sraghuram 	mfdb_ent_t	*ment = NULL;
16435373Sraghuram 	mfdb_ent_t	*tmp_ent = NULL;
16445373Sraghuram 	mfdb_ent_t	*new_ent = NULL;
16455373Sraghuram 	void		*tgt = NULL;
16465373Sraghuram 
16475373Sraghuram 	if (devtype == VSW_VNETPORT) {
16485373Sraghuram 		/*
16495373Sraghuram 		 * Being invoked from a vnet.
16505373Sraghuram 		 */
16515373Sraghuram 		ASSERT(arg != NULL);
16525373Sraghuram 		tgt = arg;
16535373Sraghuram 		D2(NULL, "%s: port %d : address 0x%llx", __func__,
16545373Sraghuram 		    ((vsw_port_t *)arg)->p_instance, addr);
16555373Sraghuram 	} else {
16565373Sraghuram 		/*
16575373Sraghuram 		 * We are being invoked via the m_multicst mac entry
16585373Sraghuram 		 * point.
16595373Sraghuram 		 */
16605373Sraghuram 		D2(NULL, "%s: address 0x%llx", __func__, addr);
16615373Sraghuram 		tgt = (void *)vswp;
16625373Sraghuram 	}
16635373Sraghuram 
16645373Sraghuram 	WRITE_ENTER(&vswp->mfdbrw);
16655373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
16665373Sraghuram 	    (mod_hash_val_t *)&ment) != 0) {
16675373Sraghuram 
16685373Sraghuram 		/* address not currently in table */
16695373Sraghuram 		ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
16705373Sraghuram 		ment->d_addr = (void *)tgt;
16715373Sraghuram 		ment->d_type = devtype;
16725373Sraghuram 		ment->nextp = NULL;
16735373Sraghuram 
16745373Sraghuram 		if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr,
16755373Sraghuram 		    (mod_hash_val_t)ment) != 0) {
16765373Sraghuram 			DERR(vswp, "%s: hash table insertion failed", __func__);
16775373Sraghuram 			kmem_free(ment, sizeof (mfdb_ent_t));
16785373Sraghuram 			rv = 1;
16795373Sraghuram 		} else {
16805373Sraghuram 			D2(vswp, "%s: added initial entry for 0x%llx to "
16815373Sraghuram 			    "table", __func__, addr);
16825373Sraghuram 		}
16835373Sraghuram 	} else {
16845373Sraghuram 		/*
16855373Sraghuram 		 * Address in table. Check to see if specified port
16865373Sraghuram 		 * is already associated with the address. If not add
16875373Sraghuram 		 * it now.
16885373Sraghuram 		 */
16895373Sraghuram 		tmp_ent = ment;
16905373Sraghuram 		while (tmp_ent != NULL) {
16915373Sraghuram 			if (tmp_ent->d_addr == (void *)tgt) {
16925373Sraghuram 				if (devtype == VSW_VNETPORT) {
16935373Sraghuram 					DERR(vswp, "%s: duplicate port entry "
16945373Sraghuram 					    "found for portid %ld and key "
16955373Sraghuram 					    "0x%llx", __func__,
16965373Sraghuram 					    ((vsw_port_t *)arg)->p_instance,
16975373Sraghuram 					    addr);
16985373Sraghuram 				} else {
16995373Sraghuram 					DERR(vswp, "%s: duplicate entry found"
17005373Sraghuram 					    "for key 0x%llx", __func__, addr);
17015373Sraghuram 				}
17025373Sraghuram 				rv = 1;
17035373Sraghuram 				dup = 1;
17045373Sraghuram 				break;
17055373Sraghuram 			}
17065373Sraghuram 			tmp_ent = tmp_ent->nextp;
17075373Sraghuram 		}
17085373Sraghuram 
17095373Sraghuram 		/*
17105373Sraghuram 		 * Port not on list so add it to end now.
17115373Sraghuram 		 */
17125373Sraghuram 		if (0 == dup) {
17135373Sraghuram 			D2(vswp, "%s: added entry for 0x%llx to table",
17145373Sraghuram 			    __func__, addr);
17155373Sraghuram 			new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
17165373Sraghuram 			new_ent->d_addr = (void *)tgt;
17175373Sraghuram 			new_ent->d_type = devtype;
17185373Sraghuram 			new_ent->nextp = NULL;
17195373Sraghuram 
17205373Sraghuram 			tmp_ent = ment;
17215373Sraghuram 			while (tmp_ent->nextp != NULL)
17225373Sraghuram 				tmp_ent = tmp_ent->nextp;
17235373Sraghuram 
17245373Sraghuram 			tmp_ent->nextp = new_ent;
17255373Sraghuram 		}
17265373Sraghuram 	}
17275373Sraghuram 
17285373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
17295373Sraghuram 	return (rv);
17305373Sraghuram }
17315373Sraghuram 
17325373Sraghuram /*
17335373Sraghuram  * Remove a multicast entry from the hashtable.
17345373Sraghuram  *
17355373Sraghuram  * Search hash table based on address. If match found, scan
17365373Sraghuram  * list of ports associated with address. If specified port
17375373Sraghuram  * found remove it from list.
17385373Sraghuram  */
17395373Sraghuram int
vsw_del_mcst(vsw_t * vswp,uint8_t devtype,uint64_t addr,void * arg)17405373Sraghuram vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
17415373Sraghuram {
17425373Sraghuram 	mfdb_ent_t	*ment = NULL;
17435373Sraghuram 	mfdb_ent_t	*curr_p, *prev_p;
17445373Sraghuram 	void		*tgt = NULL;
17455373Sraghuram 
17465373Sraghuram 	D1(vswp, "%s: enter", __func__);
17475373Sraghuram 
17485373Sraghuram 	if (devtype == VSW_VNETPORT) {
17495373Sraghuram 		tgt = (vsw_port_t *)arg;
17505373Sraghuram 		D2(vswp, "%s: removing port %d from mFDB for address"
17515373Sraghuram 		    " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, addr);
17525373Sraghuram 	} else {
17535373Sraghuram 		D2(vswp, "%s: removing entry", __func__);
17545373Sraghuram 		tgt = (void *)vswp;
17555373Sraghuram 	}
17565373Sraghuram 
17575373Sraghuram 	WRITE_ENTER(&vswp->mfdbrw);
17585373Sraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
17595373Sraghuram 	    (mod_hash_val_t *)&ment) != 0) {
17605373Sraghuram 		D2(vswp, "%s: address 0x%llx not in table", __func__, addr);
17615373Sraghuram 		RW_EXIT(&vswp->mfdbrw);
17625373Sraghuram 		return (1);
17635373Sraghuram 	}
17645373Sraghuram 
17655373Sraghuram 	prev_p = curr_p = ment;
17665373Sraghuram 
17675373Sraghuram 	while (curr_p != NULL) {
17685373Sraghuram 		if (curr_p->d_addr == (void *)tgt) {
17695373Sraghuram 			if (devtype == VSW_VNETPORT) {
17705373Sraghuram 				D2(vswp, "%s: port %d found", __func__,
17715373Sraghuram 				    ((vsw_port_t *)tgt)->p_instance);
17725373Sraghuram 			} else {
17735373Sraghuram 				D2(vswp, "%s: instance found", __func__);
17745373Sraghuram 			}
17755373Sraghuram 
17765373Sraghuram 			if (prev_p == curr_p) {
17775373Sraghuram 				/*
17785373Sraghuram 				 * head of list, if no other element is in
17795373Sraghuram 				 * list then destroy this entry, otherwise
17805373Sraghuram 				 * just replace it with updated value.
17815373Sraghuram 				 */
17825373Sraghuram 				ment = curr_p->nextp;
17835373Sraghuram 				if (ment == NULL) {
17845373Sraghuram 					(void) mod_hash_destroy(vswp->mfdb,
17855373Sraghuram 					    (mod_hash_val_t)addr);
17865373Sraghuram 				} else {
17875373Sraghuram 					(void) mod_hash_replace(vswp->mfdb,
17885373Sraghuram 					    (mod_hash_key_t)addr,
17895373Sraghuram 					    (mod_hash_val_t)ment);
17905373Sraghuram 				}
17915373Sraghuram 			} else {
17925373Sraghuram 				/*
17935373Sraghuram 				 * Not head of list, no need to do
17945373Sraghuram 				 * replacement, just adjust list pointers.
17955373Sraghuram 				 */
17965373Sraghuram 				prev_p->nextp = curr_p->nextp;
17975373Sraghuram 			}
17985373Sraghuram 			break;
17995373Sraghuram 		}
18005373Sraghuram 
18015373Sraghuram 		prev_p = curr_p;
18025373Sraghuram 		curr_p = curr_p->nextp;
18035373Sraghuram 	}
18045373Sraghuram 
18055373Sraghuram 	RW_EXIT(&vswp->mfdbrw);
18065373Sraghuram 
18075373Sraghuram 	D1(vswp, "%s: exit", __func__);
18085373Sraghuram 
18095373Sraghuram 	if (curr_p == NULL)
18105373Sraghuram 		return (1);
18115373Sraghuram 	kmem_free(curr_p, sizeof (mfdb_ent_t));
18125373Sraghuram 	return (0);
18135373Sraghuram }
18145373Sraghuram 
18155373Sraghuram /*
18165373Sraghuram  * Port is being deleted, but has registered an interest in one
18175373Sraghuram  * or more multicast groups. Using the list of addresses maintained
18185373Sraghuram  * within the port structure find the appropriate entry in the hash
18195373Sraghuram  * table and remove this port from the list of interested ports.
18205373Sraghuram  */
18215373Sraghuram void
vsw_del_mcst_port(vsw_port_t * port)18225373Sraghuram vsw_del_mcst_port(vsw_port_t *port)
18235373Sraghuram {
18245373Sraghuram 	mcst_addr_t	*mcap = NULL;
18255373Sraghuram 	vsw_t		*vswp = port->p_vswp;
18265373Sraghuram 
18275373Sraghuram 	D1(vswp, "%s: enter", __func__);
18285373Sraghuram 
18295373Sraghuram 	mutex_enter(&port->mca_lock);
18305373Sraghuram 
18315373Sraghuram 	while ((mcap = port->mcap) != NULL) {
18325373Sraghuram 
18335373Sraghuram 		port->mcap = mcap->nextp;
18345373Sraghuram 
18355373Sraghuram 		mutex_exit(&port->mca_lock);
18365373Sraghuram 
18375373Sraghuram 		(void) vsw_del_mcst(vswp, VSW_VNETPORT,
18385373Sraghuram 		    mcap->addr, port);
18395373Sraghuram 
18405373Sraghuram 		/*
18415373Sraghuram 		 * Remove the address from HW. The address
18425373Sraghuram 		 * will actually only be removed once the ref
18435373Sraghuram 		 * count within the MAC layer has dropped to
18445373Sraghuram 		 * zero. I.e. we can safely call this fn even
18455373Sraghuram 		 * if other ports are interested in this
18465373Sraghuram 		 * address.
18475373Sraghuram 		 */
18488275SEric Cheng 		vsw_mac_multicast_remove(vswp, port, mcap, VSW_VNETPORT);
18495373Sraghuram 		kmem_free(mcap, sizeof (*mcap));
18505373Sraghuram 
18515373Sraghuram 		mutex_enter(&port->mca_lock);
18525373Sraghuram 
18535373Sraghuram 	}
18545373Sraghuram 
18555373Sraghuram 	mutex_exit(&port->mca_lock);
18565373Sraghuram 
18575373Sraghuram 	D1(vswp, "%s: exit", __func__);
18585373Sraghuram }
18595373Sraghuram 
18605373Sraghuram /*
18615373Sraghuram  * This vsw instance is detaching, but has registered an interest in one
18625373Sraghuram  * or more multicast groups. Using the list of addresses maintained
18635373Sraghuram  * within the vsw structure find the appropriate entry in the hash
18645373Sraghuram  * table and remove this instance from the list of interested ports.
18655373Sraghuram  */
18665373Sraghuram void
vsw_del_mcst_vsw(vsw_t * vswp)18675373Sraghuram vsw_del_mcst_vsw(vsw_t *vswp)
18685373Sraghuram {
18695373Sraghuram 	mcst_addr_t	*next_p = NULL;
18705373Sraghuram 
18715373Sraghuram 	D1(vswp, "%s: enter", __func__);
18725373Sraghuram 
18735373Sraghuram 	mutex_enter(&vswp->mca_lock);
18745373Sraghuram 
18755373Sraghuram 	while (vswp->mcap != NULL) {
18765373Sraghuram 		DERR(vswp, "%s: deleting addr 0x%llx",
18775373Sraghuram 		    __func__, vswp->mcap->addr);
18785373Sraghuram 		(void) vsw_del_mcst(vswp, VSW_LOCALDEV, vswp->mcap->addr, NULL);
18795373Sraghuram 
18805373Sraghuram 		next_p = vswp->mcap->nextp;
18815373Sraghuram 		kmem_free(vswp->mcap, sizeof (mcst_addr_t));
18825373Sraghuram 		vswp->mcap = next_p;
18835373Sraghuram 	}
18845373Sraghuram 
18855373Sraghuram 	vswp->mcap = NULL;
18865373Sraghuram 	mutex_exit(&vswp->mca_lock);
18875373Sraghuram 
18885373Sraghuram 	D1(vswp, "%s: exit", __func__);
18895373Sraghuram }
18905373Sraghuram 
18918275SEric Cheng mblk_t *
vsw_get_same_dest_list(struct ether_header * ehp,mblk_t ** mpp)18928275SEric Cheng vsw_get_same_dest_list(struct ether_header *ehp, mblk_t **mpp)
18935373Sraghuram {
18945935Ssb155480 	mblk_t			*bp;
18955935Ssb155480 	mblk_t			*nbp;
18965935Ssb155480 	mblk_t			*head = NULL;
18975935Ssb155480 	mblk_t			*tail = NULL;
18985935Ssb155480 	mblk_t			*prev = NULL;
18995935Ssb155480 	struct ether_header	*behp;
19005373Sraghuram 
19015373Sraghuram 	/* process the chain of packets */
19025373Sraghuram 	bp = *mpp;
19035373Sraghuram 	while (bp) {
19045373Sraghuram 		nbp = bp->b_next;
19055373Sraghuram 		behp = (struct ether_header *)bp->b_rptr;
19065373Sraghuram 		bp->b_prev = NULL;
19075373Sraghuram 		if (ether_cmp(&ehp->ether_dhost, &behp->ether_dhost) == 0) {
19085373Sraghuram 			if (prev == NULL) {
19095373Sraghuram 				*mpp = nbp;
19105373Sraghuram 			} else {
19115373Sraghuram 				prev->b_next = nbp;
19125373Sraghuram 			}
19135373Sraghuram 			bp->b_next =  NULL;
19145373Sraghuram 			if (head == NULL) {
19155373Sraghuram 				head = tail = bp;
19165373Sraghuram 			} else {
19175373Sraghuram 				tail->b_next = bp;
19185373Sraghuram 				tail = bp;
19195373Sraghuram 			}
19205373Sraghuram 		} else {
19215373Sraghuram 			prev = bp;
19225373Sraghuram 		}
19235373Sraghuram 		bp = nbp;
19245373Sraghuram 	}
19258275SEric Cheng 	return (head);
19265373Sraghuram }
19275373Sraghuram 
19285373Sraghuram static mblk_t *
vsw_dupmsgchain(mblk_t * mp)19295373Sraghuram vsw_dupmsgchain(mblk_t *mp)
19305373Sraghuram {
19315373Sraghuram 	mblk_t	*nmp = NULL;
19325373Sraghuram 	mblk_t	**nmpp = &nmp;
19335373Sraghuram 
19345373Sraghuram 	for (; mp != NULL; mp = mp->b_next) {
19355373Sraghuram 		if ((*nmpp = dupmsg(mp)) == NULL) {
19365373Sraghuram 			freemsgchain(nmp);
19375373Sraghuram 			return (NULL);
19385373Sraghuram 		}
19395373Sraghuram 
19405373Sraghuram 		nmpp = &((*nmpp)->b_next);
19415373Sraghuram 	}
19425373Sraghuram 
19435373Sraghuram 	return (nmp);
19445373Sraghuram }
1945