xref: /titanic_44/usr/src/uts/sun4v/io/vsw_switching.c (revision a862df29af145cac620492c4ebe1f42c1906c66e)
106db247cSraghuram /*
206db247cSraghuram  * CDDL HEADER START
306db247cSraghuram  *
406db247cSraghuram  * The contents of this file are subject to the terms of the
506db247cSraghuram  * Common Development and Distribution License (the "License").
606db247cSraghuram  * You may not use this file except in compliance with the License.
706db247cSraghuram  *
806db247cSraghuram  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
906db247cSraghuram  * or http://www.opensolaris.org/os/licensing.
1006db247cSraghuram  * See the License for the specific language governing permissions
1106db247cSraghuram  * and limitations under the License.
1206db247cSraghuram  *
1306db247cSraghuram  * When distributing Covered Code, include this CDDL HEADER in each
1406db247cSraghuram  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1506db247cSraghuram  * If applicable, add the following below this CDDL HEADER, with the
1606db247cSraghuram  * fields enclosed by brackets "[]" replaced with your own identifying
1706db247cSraghuram  * information: Portions Copyright [yyyy] [name of copyright owner]
1806db247cSraghuram  *
1906db247cSraghuram  * CDDL HEADER END
2006db247cSraghuram  */
2106db247cSraghuram 
2206db247cSraghuram /*
23*a862df29SSriharsha Basavapatna  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2406db247cSraghuram  */
2506db247cSraghuram 
2606db247cSraghuram #include <sys/types.h>
2706db247cSraghuram #include <sys/errno.h>
2806db247cSraghuram #include <sys/debug.h>
2906db247cSraghuram #include <sys/time.h>
3006db247cSraghuram #include <sys/sysmacros.h>
3106db247cSraghuram #include <sys/systm.h>
3206db247cSraghuram #include <sys/user.h>
3306db247cSraghuram #include <sys/stropts.h>
3406db247cSraghuram #include <sys/stream.h>
3506db247cSraghuram #include <sys/strlog.h>
3606db247cSraghuram #include <sys/strsubr.h>
3706db247cSraghuram #include <sys/cmn_err.h>
3806db247cSraghuram #include <sys/cpu.h>
3906db247cSraghuram #include <sys/kmem.h>
4006db247cSraghuram #include <sys/conf.h>
4106db247cSraghuram #include <sys/ddi.h>
4206db247cSraghuram #include <sys/sunddi.h>
4306db247cSraghuram #include <sys/ksynch.h>
4406db247cSraghuram #include <sys/stat.h>
4506db247cSraghuram #include <sys/kstat.h>
4606db247cSraghuram #include <sys/vtrace.h>
4706db247cSraghuram #include <sys/strsun.h>
4806db247cSraghuram #include <sys/dlpi.h>
4906db247cSraghuram #include <sys/ethernet.h>
5006db247cSraghuram #include <net/if.h>
5106db247cSraghuram #include <sys/varargs.h>
5206db247cSraghuram #include <sys/machsystm.h>
5306db247cSraghuram #include <sys/modctl.h>
5406db247cSraghuram #include <sys/modhash.h>
5506db247cSraghuram #include <sys/mac.h>
5606db247cSraghuram #include <sys/mac_ether.h>
5706db247cSraghuram #include <sys/taskq.h>
5806db247cSraghuram #include <sys/note.h>
5906db247cSraghuram #include <sys/mach_descrip.h>
6006db247cSraghuram #include <sys/mdeg.h>
6106db247cSraghuram #include <sys/ldc.h>
6206db247cSraghuram #include <sys/vsw_fdb.h>
6306db247cSraghuram #include <sys/vsw.h>
6406db247cSraghuram #include <sys/vio_mailbox.h>
6506db247cSraghuram #include <sys/vnet_mailbox.h>
6606db247cSraghuram #include <sys/vnet_common.h>
6706db247cSraghuram #include <sys/vio_util.h>
6806db247cSraghuram #include <sys/sdt.h>
6906db247cSraghuram #include <sys/atomic.h>
70c1c61f44Ssb155480 #include <sys/vlan.h>
7106db247cSraghuram 
7206db247cSraghuram /* Switching setup routines */
73808f26a8SSriharsha Basavapatna void vsw_setup_switching_thread(void *arg);
74808f26a8SSriharsha Basavapatna int vsw_setup_switching_start(vsw_t *vswp);
75808f26a8SSriharsha Basavapatna void vsw_setup_switching_stop(vsw_t *vswp);
7606db247cSraghuram int vsw_setup_switching(vsw_t *);
77d8a518c8SSriharsha Basavapatna void vsw_setup_switching_post_process(vsw_t *vswp);
787a327842Swentaoy void vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller,
797a327842Swentaoy     vsw_port_t *port, mac_resource_handle_t mrh);
8006db247cSraghuram static	int vsw_setup_layer2(vsw_t *);
8106db247cSraghuram static	int vsw_setup_layer3(vsw_t *);
8206db247cSraghuram 
8306db247cSraghuram /* Switching/data transmit routines */
84da14cebeSEric Cheng static	void vsw_switch_l2_frame_mac_client(vsw_t *vswp, mblk_t *mp, int caller,
85da14cebeSEric Cheng     vsw_port_t *port, mac_resource_handle_t);
8606db247cSraghuram static	void vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller,
8706db247cSraghuram 	vsw_port_t *port, mac_resource_handle_t);
8806db247cSraghuram static	void vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller,
8906db247cSraghuram 	vsw_port_t *port, mac_resource_handle_t);
90f0ca1d9aSsb155480 static	int vsw_forward_all(vsw_t *vswp, mblk_t *mp,
9106db247cSraghuram 	int caller, vsw_port_t *port);
92f0ca1d9aSsb155480 static	int vsw_forward_grp(vsw_t *vswp, mblk_t *mp,
9306db247cSraghuram     int caller, vsw_port_t *port);
9406db247cSraghuram 
95c1c61f44Ssb155480 /* VLAN routines */
96c1c61f44Ssb155480 void vsw_create_vlans(void *arg, int type);
97c1c61f44Ssb155480 void vsw_destroy_vlans(void *arg, int type);
98c1c61f44Ssb155480 void vsw_vlan_add_ids(void *arg, int type);
99c1c61f44Ssb155480 void vsw_vlan_remove_ids(void *arg, int type);
100c1c61f44Ssb155480 static	void vsw_vlan_create_hash(void *arg, int type);
101c1c61f44Ssb155480 static	void vsw_vlan_destroy_hash(void *arg, int type);
102c1c61f44Ssb155480 boolean_t vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp,
103c1c61f44Ssb155480 	uint16_t *vidp);
104c1c61f44Ssb155480 mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp);
105c1c61f44Ssb155480 uint32_t vsw_vlan_frames_untag(void *arg, int type, mblk_t **np, mblk_t **npt);
106c1c61f44Ssb155480 boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
107c1c61f44Ssb155480 
10806db247cSraghuram /* Forwarding database (FDB) routines */
109c1c61f44Ssb155480 void vsw_fdbe_add(vsw_t *vswp, void *port);
110c1c61f44Ssb155480 void vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr);
111c1c61f44Ssb155480 static	vsw_fdbe_t *vsw_fdbe_find(vsw_t *vswp, struct ether_addr *);
112c1c61f44Ssb155480 static void vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val);
113c1c61f44Ssb155480 
11406db247cSraghuram int vsw_add_rem_mcst(vnet_mcast_msg_t *, vsw_port_t *);
11506db247cSraghuram int vsw_add_mcst(vsw_t *, uint8_t, uint64_t, void *);
11606db247cSraghuram int vsw_del_mcst(vsw_t *, uint8_t, uint64_t, void *);
11706db247cSraghuram void vsw_del_mcst_vsw(vsw_t *);
11806db247cSraghuram 
11906db247cSraghuram /* Support functions */
12006db247cSraghuram static mblk_t *vsw_dupmsgchain(mblk_t *mp);
121da14cebeSEric Cheng static mblk_t *vsw_get_same_dest_list(struct ether_header *ehp, mblk_t **mpp);
12206db247cSraghuram 
12306db247cSraghuram 
12406db247cSraghuram /*
12506db247cSraghuram  * Functions imported from other files.
12606db247cSraghuram  */
127da14cebeSEric Cheng extern mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
12806db247cSraghuram extern mcst_addr_t *vsw_del_addr(uint8_t, void *, uint64_t);
12906db247cSraghuram extern int vsw_mac_open(vsw_t *vswp);
13006db247cSraghuram extern void vsw_mac_close(vsw_t *vswp);
131f0ca1d9aSsb155480 extern void vsw_mac_rx(vsw_t *vswp, mac_resource_handle_t mrh,
132f0ca1d9aSsb155480     mblk_t *mp, vsw_macrx_flags_t flags);
13306db247cSraghuram extern void vsw_set_addrs(vsw_t *vswp);
134da14cebeSEric Cheng extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
135678453a8Sspeer extern void vsw_hio_init(vsw_t *vswp);
136678453a8Sspeer extern void vsw_hio_start_ports(vsw_t *vswp);
137da14cebeSEric Cheng extern int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port,
138da14cebeSEric Cheng     mcst_addr_t *mcst_p, int type);
139da14cebeSEric Cheng extern void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
140da14cebeSEric Cheng     mcst_addr_t *mcst_p, int type);
141d8a518c8SSriharsha Basavapatna extern void vsw_mac_link_update(vsw_t *vswp, link_state_t link_state);
142d8a518c8SSriharsha Basavapatna extern void vsw_physlink_update_ports(vsw_t *vswp);
14306db247cSraghuram 
14406db247cSraghuram /*
14506db247cSraghuram  * Tunables used in this file.
14606db247cSraghuram  */
14706db247cSraghuram extern	int vsw_setup_switching_delay;
148c1c61f44Ssb155480 extern	uint32_t vsw_vlan_nchains;
149c1c61f44Ssb155480 extern	uint32_t vsw_fdbe_refcnt_delay;
15006db247cSraghuram 
151c1c61f44Ssb155480 #define	VSW_FDBE_REFHOLD(p)						\
152c1c61f44Ssb155480 {									\
153c1c61f44Ssb155480 	atomic_inc_32(&(p)->refcnt);					\
154c1c61f44Ssb155480 	ASSERT((p)->refcnt != 0);					\
155c1c61f44Ssb155480 }
156c1c61f44Ssb155480 
157c1c61f44Ssb155480 #define	VSW_FDBE_REFRELE(p)						\
158c1c61f44Ssb155480 {									\
159c1c61f44Ssb155480 	ASSERT((p)->refcnt != 0);					\
160c1c61f44Ssb155480 	atomic_dec_32(&(p)->refcnt);					\
161c1c61f44Ssb155480 }
16206db247cSraghuram 
16306db247cSraghuram /*
164808f26a8SSriharsha Basavapatna  * Thread to setup switching mode. This thread is created during vsw_attach()
165808f26a8SSriharsha Basavapatna  * initially. It invokes vsw_setup_switching() and keeps retrying while the
166808f26a8SSriharsha Basavapatna  * returned value is EAGAIN. The thread exits when the switching mode setup is
167808f26a8SSriharsha Basavapatna  * done successfully or when the error returned is not EAGAIN. This thread may
168808f26a8SSriharsha Basavapatna  * also get created from vsw_update_md_prop() if the switching mode needs to be
169808f26a8SSriharsha Basavapatna  * updated.
17006db247cSraghuram  */
17106db247cSraghuram void
vsw_setup_switching_thread(void * arg)172808f26a8SSriharsha Basavapatna vsw_setup_switching_thread(void *arg)
17306db247cSraghuram {
174808f26a8SSriharsha Basavapatna 	callb_cpr_t	cprinfo;
17506db247cSraghuram 	vsw_t		*vswp =  (vsw_t *)arg;
176808f26a8SSriharsha Basavapatna 	clock_t		wait_time;
177808f26a8SSriharsha Basavapatna 	clock_t		xwait;
178808f26a8SSriharsha Basavapatna 	clock_t		wait_rv;
17906db247cSraghuram 	int		rv;
18006db247cSraghuram 
181808f26a8SSriharsha Basavapatna 	/* wait time used on successive retries */
182808f26a8SSriharsha Basavapatna 	xwait = drv_usectohz(vsw_setup_switching_delay * MICROSEC);
18306db247cSraghuram 
184808f26a8SSriharsha Basavapatna 	CALLB_CPR_INIT(&cprinfo, &vswp->sw_thr_lock, callb_generic_cpr,
185808f26a8SSriharsha Basavapatna 	    "vsw_setup_sw_thread");
186808f26a8SSriharsha Basavapatna 
187808f26a8SSriharsha Basavapatna 	mutex_enter(&vswp->sw_thr_lock);
188808f26a8SSriharsha Basavapatna 
189808f26a8SSriharsha Basavapatna 	while ((vswp->sw_thr_flags & VSW_SWTHR_STOP) == 0) {
190808f26a8SSriharsha Basavapatna 
191808f26a8SSriharsha Basavapatna 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
192808f26a8SSriharsha Basavapatna 
193808f26a8SSriharsha Basavapatna 		/* Wait for sometime before (re)trying setup_switching() */
194808f26a8SSriharsha Basavapatna 		wait_time = ddi_get_lbolt() + xwait;
195808f26a8SSriharsha Basavapatna 		while ((vswp->sw_thr_flags & VSW_SWTHR_STOP) == 0) {
196808f26a8SSriharsha Basavapatna 			wait_rv = cv_timedwait(&vswp->sw_thr_cv,
197808f26a8SSriharsha Basavapatna 			    &vswp->sw_thr_lock, wait_time);
198808f26a8SSriharsha Basavapatna 			if (wait_rv == -1) {	/* timed out */
199808f26a8SSriharsha Basavapatna 				break;
200808f26a8SSriharsha Basavapatna 			}
201808f26a8SSriharsha Basavapatna 		}
202808f26a8SSriharsha Basavapatna 
203808f26a8SSriharsha Basavapatna 		CALLB_CPR_SAFE_END(&cprinfo, &vswp->sw_thr_lock)
204808f26a8SSriharsha Basavapatna 
205808f26a8SSriharsha Basavapatna 		if ((vswp->sw_thr_flags & VSW_SWTHR_STOP) != 0) {
206808f26a8SSriharsha Basavapatna 			/*
207808f26a8SSriharsha Basavapatna 			 * If there is a stop request, process that first and
208808f26a8SSriharsha Basavapatna 			 * exit the loop. Continue to hold the mutex which gets
209808f26a8SSriharsha Basavapatna 			 * released in CALLB_CPR_EXIT().
210808f26a8SSriharsha Basavapatna 			 */
211808f26a8SSriharsha Basavapatna 			break;
212808f26a8SSriharsha Basavapatna 		}
213808f26a8SSriharsha Basavapatna 
214808f26a8SSriharsha Basavapatna 		mutex_exit(&vswp->sw_thr_lock);
21506db247cSraghuram 		rv = vsw_setup_switching(vswp);
21606db247cSraghuram 		if (rv == 0) {
217d8a518c8SSriharsha Basavapatna 			vsw_setup_switching_post_process(vswp);
21806db247cSraghuram 		}
219808f26a8SSriharsha Basavapatna 		mutex_enter(&vswp->sw_thr_lock);
220808f26a8SSriharsha Basavapatna 		if (rv != EAGAIN) {
221808f26a8SSriharsha Basavapatna 			break;
222808f26a8SSriharsha Basavapatna 		}
22306db247cSraghuram 
224808f26a8SSriharsha Basavapatna 	}
22506db247cSraghuram 
226808f26a8SSriharsha Basavapatna 	vswp->sw_thr_flags &= ~VSW_SWTHR_STOP;
227808f26a8SSriharsha Basavapatna 	vswp->sw_thread = NULL;
228808f26a8SSriharsha Basavapatna 	CALLB_CPR_EXIT(&cprinfo);
229808f26a8SSriharsha Basavapatna 	thread_exit();
230808f26a8SSriharsha Basavapatna }
231808f26a8SSriharsha Basavapatna 
23206db247cSraghuram /*
233808f26a8SSriharsha Basavapatna  * Create a thread to setup the switching mode.
234808f26a8SSriharsha Basavapatna  * Returns 0 on success; 1 on failure.
23506db247cSraghuram  */
236808f26a8SSriharsha Basavapatna int
vsw_setup_switching_start(vsw_t * vswp)237808f26a8SSriharsha Basavapatna vsw_setup_switching_start(vsw_t *vswp)
238808f26a8SSriharsha Basavapatna {
239808f26a8SSriharsha Basavapatna 	mutex_enter(&vswp->sw_thr_lock);
240808f26a8SSriharsha Basavapatna 
241808f26a8SSriharsha Basavapatna 	vswp->sw_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
242808f26a8SSriharsha Basavapatna 	    vsw_setup_switching_thread, vswp, 0, &p0, TS_RUN, minclsyspri);
243808f26a8SSriharsha Basavapatna 
244808f26a8SSriharsha Basavapatna 	if (vswp->sw_thread == NULL) {
245808f26a8SSriharsha Basavapatna 		mutex_exit(&vswp->sw_thr_lock);
246808f26a8SSriharsha Basavapatna 		return (1);
24706db247cSraghuram 	}
24806db247cSraghuram 
249808f26a8SSriharsha Basavapatna 	mutex_exit(&vswp->sw_thr_lock);
250808f26a8SSriharsha Basavapatna 	return (0);
25106db247cSraghuram }
25206db247cSraghuram 
25306db247cSraghuram /*
254808f26a8SSriharsha Basavapatna  * Stop the thread to setup switching mode.
25506db247cSraghuram  */
25606db247cSraghuram void
vsw_setup_switching_stop(vsw_t * vswp)257808f26a8SSriharsha Basavapatna vsw_setup_switching_stop(vsw_t *vswp)
25806db247cSraghuram {
259808f26a8SSriharsha Basavapatna 	kt_did_t	tid = 0;
26006db247cSraghuram 
261808f26a8SSriharsha Basavapatna 	/*
262808f26a8SSriharsha Basavapatna 	 * Signal the setup_switching thread to stop and wait until it stops.
263808f26a8SSriharsha Basavapatna 	 */
264808f26a8SSriharsha Basavapatna 	mutex_enter(&vswp->sw_thr_lock);
26506db247cSraghuram 
266808f26a8SSriharsha Basavapatna 	if (vswp->sw_thread != NULL) {
267808f26a8SSriharsha Basavapatna 		tid = vswp->sw_thread->t_did;
268808f26a8SSriharsha Basavapatna 		vswp->sw_thr_flags |= VSW_SWTHR_STOP;
269808f26a8SSriharsha Basavapatna 		cv_signal(&vswp->sw_thr_cv);
27006db247cSraghuram 	}
27106db247cSraghuram 
272808f26a8SSriharsha Basavapatna 	mutex_exit(&vswp->sw_thr_lock);
273808f26a8SSriharsha Basavapatna 
274808f26a8SSriharsha Basavapatna 	if (tid != 0)
275808f26a8SSriharsha Basavapatna 		thread_join(tid);
276808f26a8SSriharsha Basavapatna 
27706db247cSraghuram 	(void) atomic_swap_32(&vswp->switching_setup_done, B_FALSE);
27806db247cSraghuram 
27906db247cSraghuram 	vswp->mac_open_retries = 0;
28006db247cSraghuram }
28106db247cSraghuram 
28206db247cSraghuram /*
28306db247cSraghuram  * Setup the required switching mode.
28406db247cSraghuram  * Returns:
28506db247cSraghuram  *  0 on success.
28606db247cSraghuram  *  EAGAIN if retry is needed.
28706db247cSraghuram  *  1 on all other failures.
28806db247cSraghuram  */
28906db247cSraghuram int
vsw_setup_switching(vsw_t * vswp)29006db247cSraghuram vsw_setup_switching(vsw_t *vswp)
29106db247cSraghuram {
292da14cebeSEric Cheng 	int	rv = 1;
29306db247cSraghuram 
29406db247cSraghuram 	D1(vswp, "%s: enter", __func__);
29506db247cSraghuram 
29606db247cSraghuram 	/*
29706db247cSraghuram 	 * Select best switching mode.
298da14cebeSEric Cheng 	 * This is done as this routine can be called from the timeout
299da14cebeSEric Cheng 	 * handler to retry setting up a specific mode. Currently only
300da14cebeSEric Cheng 	 * the function which sets up layer2/promisc mode returns EAGAIN
301da14cebeSEric Cheng 	 * if the underlying network device is not available yet, causing
302da14cebeSEric Cheng 	 * retries.
30306db247cSraghuram 	 */
304da14cebeSEric Cheng 	if (vswp->smode & VSW_LAYER2) {
30506db247cSraghuram 		rv = vsw_setup_layer2(vswp);
306da14cebeSEric Cheng 	} else if (vswp->smode & VSW_LAYER3) {
30706db247cSraghuram 		rv = vsw_setup_layer3(vswp);
308da14cebeSEric Cheng 	} else {
30906db247cSraghuram 		DERR(vswp, "unknown switch mode");
31006db247cSraghuram 		rv = 1;
31106db247cSraghuram 	}
31206db247cSraghuram 
31306db247cSraghuram 	if (rv && (rv != EAGAIN)) {
31406db247cSraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to setup specified "
31506db247cSraghuram 		    "switching mode", vswp->instance);
31606db247cSraghuram 	} else if (rv == 0) {
31706db247cSraghuram 		(void) atomic_swap_32(&vswp->switching_setup_done, B_TRUE);
31806db247cSraghuram 	}
31906db247cSraghuram 
32006db247cSraghuram 	D2(vswp, "%s: Operating in mode %d", __func__,
321da14cebeSEric Cheng 	    vswp->smode);
32206db247cSraghuram 
32306db247cSraghuram 	D1(vswp, "%s: exit", __func__);
32406db247cSraghuram 
32506db247cSraghuram 	return (rv);
32606db247cSraghuram }
32706db247cSraghuram 
32806db247cSraghuram /*
32906db247cSraghuram  * Setup for layer 2 switching.
33006db247cSraghuram  *
33106db247cSraghuram  * Returns:
33206db247cSraghuram  *  0 on success.
33306db247cSraghuram  *  EAGAIN if retry is needed.
33406db247cSraghuram  *  EIO on all other failures.
33506db247cSraghuram  */
33606db247cSraghuram static int
vsw_setup_layer2(vsw_t * vswp)33706db247cSraghuram vsw_setup_layer2(vsw_t *vswp)
33806db247cSraghuram {
33906db247cSraghuram 	int	rv;
34006db247cSraghuram 
34106db247cSraghuram 	D1(vswp, "%s: enter", __func__);
34206db247cSraghuram 
343da14cebeSEric Cheng 	/*
344da14cebeSEric Cheng 	 * Until the network device is successfully opened,
345da14cebeSEric Cheng 	 * set the switching to use vsw_switch_l2_frame.
346da14cebeSEric Cheng 	 */
34706db247cSraghuram 	vswp->vsw_switch_frame = vsw_switch_l2_frame;
348da14cebeSEric Cheng 	vswp->mac_cl_switching = B_FALSE;
34906db247cSraghuram 
35006db247cSraghuram 	rv = strlen(vswp->physname);
35106db247cSraghuram 	if (rv == 0) {
35206db247cSraghuram 		/*
35306db247cSraghuram 		 * Physical device name is NULL, which is
35406db247cSraghuram 		 * required for layer 2.
35506db247cSraghuram 		 */
356da14cebeSEric Cheng 		cmn_err(CE_WARN, "!vsw%d: no network device name specified",
35706db247cSraghuram 		    vswp->instance);
35806db247cSraghuram 		return (EIO);
35906db247cSraghuram 	}
36006db247cSraghuram 
361da14cebeSEric Cheng 	mutex_enter(&vswp->mac_lock);
36206db247cSraghuram 
36306db247cSraghuram 	rv = vsw_mac_open(vswp);
36406db247cSraghuram 	if (rv != 0) {
36506db247cSraghuram 		if (rv != EAGAIN) {
366da14cebeSEric Cheng 			cmn_err(CE_WARN, "!vsw%d: Unable to open network "
36706db247cSraghuram 			    "device: %s\n", vswp->instance, vswp->physname);
36806db247cSraghuram 		}
369da14cebeSEric Cheng 		mutex_exit(&vswp->mac_lock);
37006db247cSraghuram 		return (rv);
37106db247cSraghuram 	}
37206db247cSraghuram 
37306db247cSraghuram 	/*
374da14cebeSEric Cheng 	 * Now we can use the mac client switching, so set the switching
375da14cebeSEric Cheng 	 * function to use vsw_switch_l2_frame_mac_client(), which simply
376da14cebeSEric Cheng 	 * sends the packets to MAC layer for switching.
37706db247cSraghuram 	 */
378da14cebeSEric Cheng 	vswp->vsw_switch_frame = vsw_switch_l2_frame_mac_client;
379da14cebeSEric Cheng 	vswp->mac_cl_switching = B_TRUE;
38006db247cSraghuram 
38106db247cSraghuram 	D1(vswp, "%s: exit", __func__);
38206db247cSraghuram 
383678453a8Sspeer 	/* Initialize HybridIO related stuff */
384678453a8Sspeer 	vsw_hio_init(vswp);
385da14cebeSEric Cheng 
386da14cebeSEric Cheng 	mutex_exit(&vswp->mac_lock);
38706db247cSraghuram 	return (0);
38806db247cSraghuram 
38906db247cSraghuram exit_error:
39006db247cSraghuram 	vsw_mac_close(vswp);
391da14cebeSEric Cheng 	mutex_exit(&vswp->mac_lock);
39206db247cSraghuram 	return (EIO);
39306db247cSraghuram }
39406db247cSraghuram 
39506db247cSraghuram static int
vsw_setup_layer3(vsw_t * vswp)39606db247cSraghuram vsw_setup_layer3(vsw_t *vswp)
39706db247cSraghuram {
39806db247cSraghuram 	D1(vswp, "%s: enter", __func__);
39906db247cSraghuram 
40006db247cSraghuram 	D2(vswp, "%s: operating in layer 3 mode", __func__);
40106db247cSraghuram 	vswp->vsw_switch_frame = vsw_switch_l3_frame;
40206db247cSraghuram 
40306db247cSraghuram 	D1(vswp, "%s: exit", __func__);
40406db247cSraghuram 
40506db247cSraghuram 	return (0);
40606db247cSraghuram }
40706db247cSraghuram 
4087a327842Swentaoy /* ARGSUSED */
4097a327842Swentaoy void
vsw_switch_frame_nop(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * port,mac_resource_handle_t mrh)4107a327842Swentaoy vsw_switch_frame_nop(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port,
4117a327842Swentaoy 			mac_resource_handle_t mrh)
4127a327842Swentaoy {
4137a327842Swentaoy 	freemsgchain(mp);
4147a327842Swentaoy }
4157a327842Swentaoy 
41606db247cSraghuram /*
417da14cebeSEric Cheng  * Use mac client for layer 2 switching .
418da14cebeSEric Cheng  */
419da14cebeSEric 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)420da14cebeSEric Cheng vsw_switch_l2_frame_mac_client(vsw_t *vswp, mblk_t *mp, int caller,
421da14cebeSEric Cheng     vsw_port_t *port, mac_resource_handle_t mrh)
422da14cebeSEric Cheng {
423da14cebeSEric Cheng 	_NOTE(ARGUNUSED(mrh))
424da14cebeSEric Cheng 
425da14cebeSEric Cheng 	mblk_t		*ret_m;
426da14cebeSEric Cheng 
427da14cebeSEric Cheng 	/*
428da14cebeSEric Cheng 	 * This switching function is expected to be called by
429da14cebeSEric Cheng 	 * the ports or the interface only. The packets from
430da14cebeSEric Cheng 	 * physical interface already switched.
431da14cebeSEric Cheng 	 */
432da14cebeSEric Cheng 	ASSERT((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV));
433da14cebeSEric Cheng 
434da14cebeSEric Cheng 	if ((ret_m = vsw_tx_msg(vswp, mp, caller, port)) != NULL) {
435da14cebeSEric Cheng 		DERR(vswp, "%s: drop mblks to "
436da14cebeSEric Cheng 		    "phys dev", __func__);
437da14cebeSEric Cheng 		freemsgchain(ret_m);
438da14cebeSEric Cheng 	}
439da14cebeSEric Cheng }
440da14cebeSEric Cheng 
441da14cebeSEric Cheng /*
44206db247cSraghuram  * Switch the given ethernet frame when operating in layer 2 mode.
44306db247cSraghuram  *
44406db247cSraghuram  * vswp: pointer to the vsw instance
44506db247cSraghuram  * mp: pointer to chain of ethernet frame(s) to be switched
44606db247cSraghuram  * caller: identifies the source of this frame as:
44706db247cSraghuram  * 		1. VSW_VNETPORT - a vsw port (connected to a vnet).
44806db247cSraghuram  *		2. VSW_PHYSDEV - the physical ethernet device
44906db247cSraghuram  *		3. VSW_LOCALDEV - vsw configured as a virtual interface
45006db247cSraghuram  * arg: argument provided by the caller.
45106db247cSraghuram  *		1. for VNETPORT - pointer to the corresponding vsw_port_t.
45206db247cSraghuram  *		2. for PHYSDEV - NULL
45306db247cSraghuram  *		3. for LOCALDEV - pointer to to this vsw_t(self)
45406db247cSraghuram  */
45506db247cSraghuram void
vsw_switch_l2_frame(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg,mac_resource_handle_t mrh)45606db247cSraghuram vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller,
45706db247cSraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
45806db247cSraghuram {
45906db247cSraghuram 	struct ether_header	*ehp;
46006db247cSraghuram 	mblk_t			*bp, *ret_m;
461c1c61f44Ssb155480 	vsw_fdbe_t		*fp;
46206db247cSraghuram 
46306db247cSraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
46406db247cSraghuram 
46506db247cSraghuram 	/*
46606db247cSraghuram 	 * PERF: rather than breaking up the chain here, scan it
46706db247cSraghuram 	 * to find all mblks heading to same destination and then
46806db247cSraghuram 	 * pass that sub-chain to the lower transmit functions.
46906db247cSraghuram 	 */
47006db247cSraghuram 
47106db247cSraghuram 	/* process the chain of packets */
47206db247cSraghuram 	bp = mp;
47306db247cSraghuram 	while (bp) {
47406db247cSraghuram 		ehp = (struct ether_header *)bp->b_rptr;
475da14cebeSEric Cheng 		mp = vsw_get_same_dest_list(ehp, &bp);
476da14cebeSEric Cheng 		ASSERT(mp != NULL);
47706db247cSraghuram 
47806db247cSraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
47906db247cSraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
48006db247cSraghuram 
48106db247cSraghuram 		if (ether_cmp(&ehp->ether_dhost, &vswp->if_addr) == 0) {
48206db247cSraghuram 			/*
48306db247cSraghuram 			 * If destination is VSW_LOCALDEV (vsw as an eth
48406db247cSraghuram 			 * interface) and if the device is up & running,
48506db247cSraghuram 			 * send the packet up the stack on this host.
48606db247cSraghuram 			 * If the virtual interface is down, drop the packet.
48706db247cSraghuram 			 */
48806db247cSraghuram 			if (caller != VSW_LOCALDEV) {
489f0ca1d9aSsb155480 				vsw_mac_rx(vswp, mrh, mp, VSW_MACRX_FREEMSG);
49006db247cSraghuram 			} else {
49106db247cSraghuram 				freemsgchain(mp);
49206db247cSraghuram 			}
49306db247cSraghuram 			continue;
49406db247cSraghuram 		}
49506db247cSraghuram 
49606db247cSraghuram 		/*
497c1c61f44Ssb155480 		 * Find fdb entry for the destination
498c1c61f44Ssb155480 		 * and hold a reference to it.
49906db247cSraghuram 		 */
500c1c61f44Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
501c1c61f44Ssb155480 		if (fp != NULL) {
50206db247cSraghuram 
50306db247cSraghuram 			/*
50406db247cSraghuram 			 * If plumbed and in promisc mode then copy msg
50506db247cSraghuram 			 * and send up the stack.
50606db247cSraghuram 			 */
507f0ca1d9aSsb155480 			vsw_mac_rx(vswp, mrh, mp,
508f0ca1d9aSsb155480 			    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
50906db247cSraghuram 
51006db247cSraghuram 			/*
51106db247cSraghuram 			 * If the destination is in FDB, the packet
51206db247cSraghuram 			 * should be forwarded to the correponding
51306db247cSraghuram 			 * vsw_port (connected to a vnet device -
51406db247cSraghuram 			 * VSW_VNETPORT)
51506db247cSraghuram 			 */
516da14cebeSEric Cheng 			(void) vsw_portsend(fp->portp, mp);
51706db247cSraghuram 
518c1c61f44Ssb155480 			/* Release the reference on the fdb entry */
519c1c61f44Ssb155480 			VSW_FDBE_REFRELE(fp);
52006db247cSraghuram 		} else {
52106db247cSraghuram 			/*
52206db247cSraghuram 			 * Destination not in FDB.
52306db247cSraghuram 			 *
52406db247cSraghuram 			 * If the destination is broadcast or
52506db247cSraghuram 			 * multicast forward the packet to all
52606db247cSraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
52706db247cSraghuram 			 * except the caller.
52806db247cSraghuram 			 */
52906db247cSraghuram 			if (IS_BROADCAST(ehp)) {
530f0ca1d9aSsb155480 				D2(vswp, "%s: BROADCAST pkt", __func__);
531f0ca1d9aSsb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
53206db247cSraghuram 			} else if (IS_MULTICAST(ehp)) {
533f0ca1d9aSsb155480 				D2(vswp, "%s: MULTICAST pkt", __func__);
534f0ca1d9aSsb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
53506db247cSraghuram 			} else {
53606db247cSraghuram 				/*
53706db247cSraghuram 				 * If the destination is unicast, and came
53806db247cSraghuram 				 * from either a logical network device or
53906db247cSraghuram 				 * the switch itself when it is plumbed, then
54006db247cSraghuram 				 * send it out on the physical device and also
54106db247cSraghuram 				 * up the stack if the logical interface is
54206db247cSraghuram 				 * in promiscious mode.
54306db247cSraghuram 				 *
54406db247cSraghuram 				 * NOTE:  The assumption here is that if we
54506db247cSraghuram 				 * cannot find the destination in our fdb, its
54606db247cSraghuram 				 * a unicast address, and came from either a
54706db247cSraghuram 				 * vnet or down the stack (when plumbed) it
54806db247cSraghuram 				 * must be destinded for an ethernet device
54906db247cSraghuram 				 * outside our ldoms.
55006db247cSraghuram 				 */
55106db247cSraghuram 				if (caller == VSW_VNETPORT) {
55206db247cSraghuram 					/* promisc check copy etc */
553f0ca1d9aSsb155480 					vsw_mac_rx(vswp, mrh, mp,
55406db247cSraghuram 					    VSW_MACRX_PROMISC |
55506db247cSraghuram 					    VSW_MACRX_COPYMSG);
55606db247cSraghuram 
557da14cebeSEric Cheng 					if ((ret_m = vsw_tx_msg(vswp, mp,
558da14cebeSEric Cheng 					    caller, arg)) != NULL) {
55906db247cSraghuram 						DERR(vswp, "%s: drop mblks to "
56006db247cSraghuram 						    "phys dev", __func__);
56106db247cSraghuram 						freemsgchain(ret_m);
56206db247cSraghuram 					}
56306db247cSraghuram 
56406db247cSraghuram 				} else if (caller == VSW_PHYSDEV) {
56506db247cSraghuram 					/*
56606db247cSraghuram 					 * Pkt seen because card in promisc
56706db247cSraghuram 					 * mode. Send up stack if plumbed in
56806db247cSraghuram 					 * promisc mode, else drop it.
56906db247cSraghuram 					 */
570f0ca1d9aSsb155480 					vsw_mac_rx(vswp, mrh, mp,
57106db247cSraghuram 					    VSW_MACRX_PROMISC |
57206db247cSraghuram 					    VSW_MACRX_FREEMSG);
57306db247cSraghuram 
57406db247cSraghuram 				} else if (caller == VSW_LOCALDEV) {
57506db247cSraghuram 					/*
57606db247cSraghuram 					 * Pkt came down the stack, send out
57706db247cSraghuram 					 * over physical device.
57806db247cSraghuram 					 */
579da14cebeSEric Cheng 					if ((ret_m = vsw_tx_msg(vswp, mp,
580da14cebeSEric Cheng 					    caller, NULL)) != NULL) {
58106db247cSraghuram 						DERR(vswp, "%s: drop mblks to "
58206db247cSraghuram 						    "phys dev", __func__);
58306db247cSraghuram 						freemsgchain(ret_m);
58406db247cSraghuram 					}
58506db247cSraghuram 				}
58606db247cSraghuram 			}
58706db247cSraghuram 		}
58806db247cSraghuram 	}
58906db247cSraghuram 	D1(vswp, "%s: exit\n", __func__);
59006db247cSraghuram }
59106db247cSraghuram 
59206db247cSraghuram /*
59306db247cSraghuram  * Switch ethernet frame when in layer 3 mode (i.e. using IP
59406db247cSraghuram  * layer to do the routing).
59506db247cSraghuram  *
59606db247cSraghuram  * There is a large amount of overlap between this function and
59706db247cSraghuram  * vsw_switch_l2_frame. At some stage we need to revisit and refactor
59806db247cSraghuram  * both these functions.
59906db247cSraghuram  */
60006db247cSraghuram void
vsw_switch_l3_frame(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg,mac_resource_handle_t mrh)60106db247cSraghuram vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller,
60206db247cSraghuram 			vsw_port_t *arg, mac_resource_handle_t mrh)
60306db247cSraghuram {
60406db247cSraghuram 	struct ether_header	*ehp;
60506db247cSraghuram 	mblk_t			*bp = NULL;
606c1c61f44Ssb155480 	vsw_fdbe_t		*fp;
60706db247cSraghuram 
60806db247cSraghuram 	D1(vswp, "%s: enter (caller %d)", __func__, caller);
60906db247cSraghuram 
61006db247cSraghuram 	/*
61106db247cSraghuram 	 * In layer 3 mode should only ever be switching packets
61206db247cSraghuram 	 * between IP layer and vnet devices. So make sure thats
61306db247cSraghuram 	 * who is invoking us.
61406db247cSraghuram 	 */
61506db247cSraghuram 	if ((caller != VSW_LOCALDEV) && (caller != VSW_VNETPORT)) {
61606db247cSraghuram 		DERR(vswp, "%s: unexpected caller (%d)", __func__, caller);
61706db247cSraghuram 		freemsgchain(mp);
61806db247cSraghuram 		return;
61906db247cSraghuram 	}
62006db247cSraghuram 
62106db247cSraghuram 	/* process the chain of packets */
62206db247cSraghuram 	bp = mp;
62306db247cSraghuram 	while (bp) {
62406db247cSraghuram 		ehp = (struct ether_header *)bp->b_rptr;
625da14cebeSEric Cheng 		mp = vsw_get_same_dest_list(ehp, &bp);
626da14cebeSEric Cheng 		ASSERT(mp != NULL);
62706db247cSraghuram 
62806db247cSraghuram 		D2(vswp, "%s: mblk data buffer %lld : actual data size %lld",
62906db247cSraghuram 		    __func__, MBLKSIZE(mp), MBLKL(mp));
63006db247cSraghuram 
63106db247cSraghuram 		/*
632c1c61f44Ssb155480 		 * Find fdb entry for the destination
633c1c61f44Ssb155480 		 * and hold a reference to it.
63406db247cSraghuram 		 */
635c1c61f44Ssb155480 		fp = vsw_fdbe_find(vswp, &ehp->ether_dhost);
636c1c61f44Ssb155480 		if (fp != NULL) {
63706db247cSraghuram 
63806db247cSraghuram 			D2(vswp, "%s: sending to target port", __func__);
639da14cebeSEric Cheng 			(void) vsw_portsend(fp->portp, mp);
64006db247cSraghuram 
641c1c61f44Ssb155480 			/* Release the reference on the fdb entry */
642c1c61f44Ssb155480 			VSW_FDBE_REFRELE(fp);
64306db247cSraghuram 		} else {
64406db247cSraghuram 			/*
64506db247cSraghuram 			 * Destination not in FDB
64606db247cSraghuram 			 *
64706db247cSraghuram 			 * If the destination is broadcast or
64806db247cSraghuram 			 * multicast forward the packet to all
64906db247cSraghuram 			 * (VNETPORTs, PHYSDEV, LOCALDEV),
65006db247cSraghuram 			 * except the caller.
65106db247cSraghuram 			 */
65206db247cSraghuram 			if (IS_BROADCAST(ehp)) {
65306db247cSraghuram 				D2(vswp, "%s: BROADCAST pkt", __func__);
654f0ca1d9aSsb155480 				(void) vsw_forward_all(vswp, mp, caller, arg);
65506db247cSraghuram 			} else if (IS_MULTICAST(ehp)) {
65606db247cSraghuram 				D2(vswp, "%s: MULTICAST pkt", __func__);
657f0ca1d9aSsb155480 				(void) vsw_forward_grp(vswp, mp, caller, arg);
65806db247cSraghuram 			} else {
65906db247cSraghuram 				/*
66006db247cSraghuram 				 * Unicast pkt from vnet that we don't have
66106db247cSraghuram 				 * an FDB entry for, so must be destinded for
66206db247cSraghuram 				 * the outside world. Attempt to send up to the
66306db247cSraghuram 				 * IP layer to allow it to deal with it.
66406db247cSraghuram 				 */
66506db247cSraghuram 				if (caller == VSW_VNETPORT) {
666f0ca1d9aSsb155480 					vsw_mac_rx(vswp, mrh,
667f0ca1d9aSsb155480 					    mp, VSW_MACRX_FREEMSG);
66806db247cSraghuram 				}
66906db247cSraghuram 			}
67006db247cSraghuram 		}
67106db247cSraghuram 	}
67206db247cSraghuram 
67306db247cSraghuram 	D1(vswp, "%s: exit", __func__);
67406db247cSraghuram }
67506db247cSraghuram 
67606db247cSraghuram /*
677d8a518c8SSriharsha Basavapatna  * Additional initializations that are needed for the specific switching mode.
67871bdf936SWENTAO YANG  */
67971bdf936SWENTAO YANG void
vsw_setup_switching_post_process(vsw_t * vswp)680d8a518c8SSriharsha Basavapatna vsw_setup_switching_post_process(vsw_t *vswp)
68171bdf936SWENTAO YANG {
682d8a518c8SSriharsha Basavapatna 	link_state_t	link_state = LINK_STATE_UP;
683d8a518c8SSriharsha Basavapatna 
684da14cebeSEric Cheng 	if (vswp->smode & VSW_LAYER2) {
68571bdf936SWENTAO YANG 		/*
68671bdf936SWENTAO YANG 		 * Program unicst, mcst addrs of vsw
68771bdf936SWENTAO YANG 		 * interface and ports in the physdev.
68871bdf936SWENTAO YANG 		 */
68971bdf936SWENTAO YANG 		vsw_set_addrs(vswp);
69071bdf936SWENTAO YANG 
69171bdf936SWENTAO YANG 		/* Start HIO for ports that have already connected */
69271bdf936SWENTAO YANG 		vsw_hio_start_ports(vswp);
6931107ea93SSriharsha Basavapatna 
694d8a518c8SSriharsha Basavapatna 		if (vswp->pls_update == B_TRUE) {
695d8a518c8SSriharsha Basavapatna 			link_state = vswp->phys_link_state;
69671bdf936SWENTAO YANG 		}
697d8a518c8SSriharsha Basavapatna 
698d8a518c8SSriharsha Basavapatna 		/* Update physical link info to any ports already connected */
699d8a518c8SSriharsha Basavapatna 		vsw_physlink_update_ports(vswp);
700d8a518c8SSriharsha Basavapatna 	}
701d8a518c8SSriharsha Basavapatna 
702d8a518c8SSriharsha Basavapatna 	vsw_mac_link_update(vswp, link_state);
70371bdf936SWENTAO YANG }
70471bdf936SWENTAO YANG 
70571bdf936SWENTAO YANG /*
70606db247cSraghuram  * Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV),
70706db247cSraghuram  * except the caller (port on which frame arrived).
70806db247cSraghuram  */
70906db247cSraghuram static int
vsw_forward_all(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg)710f0ca1d9aSsb155480 vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
71106db247cSraghuram {
71206db247cSraghuram 	vsw_port_list_t	*plist = &vswp->plist;
71306db247cSraghuram 	vsw_port_t	*portp;
71406db247cSraghuram 	mblk_t		*nmp = NULL;
71506db247cSraghuram 	mblk_t		*ret_m = NULL;
71606db247cSraghuram 	int		skip_port = 0;
71706db247cSraghuram 
71806db247cSraghuram 	D1(vswp, "vsw_forward_all: enter\n");
71906db247cSraghuram 
72006db247cSraghuram 	/*
72106db247cSraghuram 	 * Broadcast message from inside ldoms so send to outside
72206db247cSraghuram 	 * world if in either of layer 2 modes.
72306db247cSraghuram 	 */
724da14cebeSEric Cheng 	if ((vswp->smode & VSW_LAYER2) &&
72506db247cSraghuram 	    ((caller == VSW_LOCALDEV) || (caller == VSW_VNETPORT))) {
72606db247cSraghuram 
72706db247cSraghuram 		nmp = vsw_dupmsgchain(mp);
72806db247cSraghuram 		if (nmp) {
729da14cebeSEric Cheng 			if ((ret_m = vsw_tx_msg(vswp, nmp, caller, arg))
730da14cebeSEric Cheng 			    != NULL) {
73106db247cSraghuram 				DERR(vswp, "%s: dropping pkt(s) "
73206db247cSraghuram 				    "consisting of %ld bytes of data for"
73306db247cSraghuram 				    " physical device", __func__, MBLKL(ret_m));
73406db247cSraghuram 				freemsgchain(ret_m);
73506db247cSraghuram 			}
73606db247cSraghuram 		}
73706db247cSraghuram 	}
73806db247cSraghuram 
73906db247cSraghuram 	if (caller == VSW_VNETPORT)
74006db247cSraghuram 		skip_port = 1;
74106db247cSraghuram 
74206db247cSraghuram 	/*
74306db247cSraghuram 	 * Broadcast message from other vnet (layer 2 or 3) or outside
74406db247cSraghuram 	 * world (layer 2 only), send up stack if plumbed.
74506db247cSraghuram 	 */
74606db247cSraghuram 	if ((caller == VSW_PHYSDEV) || (caller == VSW_VNETPORT)) {
747f0ca1d9aSsb155480 		vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_COPYMSG);
74806db247cSraghuram 	}
74906db247cSraghuram 
75006db247cSraghuram 	/* send it to all VNETPORTs */
75106db247cSraghuram 	READ_ENTER(&plist->lockrw);
75206db247cSraghuram 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
75306db247cSraghuram 		D2(vswp, "vsw_forward_all: port %d", portp->p_instance);
75406db247cSraghuram 		/*
75506db247cSraghuram 		 * Caution ! - don't reorder these two checks as arg
75606db247cSraghuram 		 * will be NULL if the caller is PHYSDEV. skip_port is
75706db247cSraghuram 		 * only set if caller is VNETPORT.
75806db247cSraghuram 		 */
75906db247cSraghuram 		if ((skip_port) && (portp == arg)) {
76006db247cSraghuram 			continue;
76106db247cSraghuram 		} else {
76206db247cSraghuram 			nmp = vsw_dupmsgchain(mp);
76306db247cSraghuram 			if (nmp) {
76406db247cSraghuram 				/*
76506db247cSraghuram 				 * The plist->lockrw is protecting the
76606db247cSraghuram 				 * portp from getting destroyed here.
76706db247cSraghuram 				 * So, no ref_cnt is incremented here.
76806db247cSraghuram 				 */
769da14cebeSEric Cheng 				(void) vsw_portsend(portp, nmp);
77006db247cSraghuram 			} else {
77106db247cSraghuram 				DERR(vswp, "vsw_forward_all: nmp NULL");
77206db247cSraghuram 			}
77306db247cSraghuram 		}
77406db247cSraghuram 	}
77506db247cSraghuram 	RW_EXIT(&plist->lockrw);
77606db247cSraghuram 
77706db247cSraghuram 	freemsgchain(mp);
77806db247cSraghuram 
77906db247cSraghuram 	D1(vswp, "vsw_forward_all: exit\n");
78006db247cSraghuram 	return (0);
78106db247cSraghuram }
78206db247cSraghuram 
78306db247cSraghuram /*
78406db247cSraghuram  * Forward pkts to any devices or interfaces which have registered
78506db247cSraghuram  * an interest in them (i.e. multicast groups).
78606db247cSraghuram  */
78706db247cSraghuram static int
vsw_forward_grp(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * arg)788f0ca1d9aSsb155480 vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg)
78906db247cSraghuram {
79006db247cSraghuram 	struct ether_header	*ehp = (struct ether_header *)mp->b_rptr;
79106db247cSraghuram 	mfdb_ent_t		*entp = NULL;
79206db247cSraghuram 	mfdb_ent_t		*tpp = NULL;
79306db247cSraghuram 	vsw_port_t 		*port;
79406db247cSraghuram 	uint64_t		key = 0;
79506db247cSraghuram 	mblk_t			*nmp = NULL;
79606db247cSraghuram 	mblk_t			*ret_m = NULL;
79706db247cSraghuram 	boolean_t		check_if = B_TRUE;
79806db247cSraghuram 
79906db247cSraghuram 	/*
80006db247cSraghuram 	 * Convert address to hash table key
80106db247cSraghuram 	 */
802c1c61f44Ssb155480 	KEY_HASH(key, &ehp->ether_dhost);
80306db247cSraghuram 
80406db247cSraghuram 	D1(vswp, "%s: key 0x%llx", __func__, key);
80506db247cSraghuram 
80606db247cSraghuram 	/*
80706db247cSraghuram 	 * If pkt came from either a vnet or down the stack (if we are
80806db247cSraghuram 	 * plumbed) and we are in layer 2 mode, then we send the pkt out
80906db247cSraghuram 	 * over the physical adapter, and then check to see if any other
81006db247cSraghuram 	 * vnets are interested in it.
81106db247cSraghuram 	 */
812da14cebeSEric Cheng 	if ((vswp->smode & VSW_LAYER2) &&
81306db247cSraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) {
81406db247cSraghuram 		nmp = vsw_dupmsgchain(mp);
81506db247cSraghuram 		if (nmp) {
816da14cebeSEric Cheng 			if ((ret_m = vsw_tx_msg(vswp, nmp, caller, arg))
817da14cebeSEric Cheng 			    != NULL) {
81806db247cSraghuram 				DERR(vswp, "%s: dropping pkt(s) consisting of "
81906db247cSraghuram 				    "%ld bytes of data for physical device",
82006db247cSraghuram 				    __func__, MBLKL(ret_m));
82106db247cSraghuram 				freemsgchain(ret_m);
82206db247cSraghuram 			}
82306db247cSraghuram 		}
82406db247cSraghuram 	}
82506db247cSraghuram 
82606db247cSraghuram 	READ_ENTER(&vswp->mfdbrw);
82706db247cSraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key,
82806db247cSraghuram 	    (mod_hash_val_t *)&entp) != 0) {
82906db247cSraghuram 		D3(vswp, "%s: no table entry found for addr 0x%llx",
83006db247cSraghuram 		    __func__, key);
83106db247cSraghuram 	} else {
83206db247cSraghuram 		/*
83306db247cSraghuram 		 * Send to list of devices associated with this address...
83406db247cSraghuram 		 */
83506db247cSraghuram 		for (tpp = entp; tpp != NULL; tpp = tpp->nextp) {
83606db247cSraghuram 
83706db247cSraghuram 			/* dont send to ourselves */
83806db247cSraghuram 			if ((caller == VSW_VNETPORT) &&
83906db247cSraghuram 			    (tpp->d_addr == (void *)arg)) {
84006db247cSraghuram 				port = (vsw_port_t *)tpp->d_addr;
84106db247cSraghuram 				D3(vswp, "%s: not sending to ourselves"
84206db247cSraghuram 				    " : port %d", __func__, port->p_instance);
84306db247cSraghuram 				continue;
84406db247cSraghuram 
84506db247cSraghuram 			} else if ((caller == VSW_LOCALDEV) &&
84606db247cSraghuram 			    (tpp->d_type == VSW_LOCALDEV)) {
847f0ca1d9aSsb155480 				D2(vswp, "%s: not sending back up stack",
84806db247cSraghuram 				    __func__);
84906db247cSraghuram 				continue;
85006db247cSraghuram 			}
85106db247cSraghuram 
85206db247cSraghuram 			if (tpp->d_type == VSW_VNETPORT) {
85306db247cSraghuram 				port = (vsw_port_t *)tpp->d_addr;
85406db247cSraghuram 				D3(vswp, "%s: sending to port %ld for addr "
85506db247cSraghuram 				    "0x%llx", __func__, port->p_instance, key);
85606db247cSraghuram 
85706db247cSraghuram 				nmp = vsw_dupmsgchain(mp);
85806db247cSraghuram 				if (nmp) {
85906db247cSraghuram 					/*
86006db247cSraghuram 					 * The vswp->mfdbrw is protecting the
86106db247cSraghuram 					 * portp from getting destroyed here.
86206db247cSraghuram 					 * So, no ref_cnt is incremented here.
86306db247cSraghuram 					 */
864da14cebeSEric Cheng 					(void) vsw_portsend(port, nmp);
86506db247cSraghuram 				}
86606db247cSraghuram 			} else {
867f0ca1d9aSsb155480 				vsw_mac_rx(vswp, NULL,
868f0ca1d9aSsb155480 				    mp, VSW_MACRX_COPYMSG);
869f0ca1d9aSsb155480 				D2(vswp, "%s: sending up stack"
87006db247cSraghuram 				    " for addr 0x%llx", __func__, key);
87106db247cSraghuram 				check_if = B_FALSE;
87206db247cSraghuram 			}
87306db247cSraghuram 		}
87406db247cSraghuram 	}
87506db247cSraghuram 
87606db247cSraghuram 	RW_EXIT(&vswp->mfdbrw);
87706db247cSraghuram 
87806db247cSraghuram 	/*
87906db247cSraghuram 	 * If the pkt came from either a vnet or from physical device,
88006db247cSraghuram 	 * and if we havent already sent the pkt up the stack then we
88106db247cSraghuram 	 * check now if we can/should (i.e. the interface is plumbed
88206db247cSraghuram 	 * and in promisc mode).
88306db247cSraghuram 	 */
88406db247cSraghuram 	if ((check_if) &&
88506db247cSraghuram 	    ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) {
886f0ca1d9aSsb155480 		vsw_mac_rx(vswp, NULL, mp,
88706db247cSraghuram 		    VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG);
88806db247cSraghuram 	}
88906db247cSraghuram 
89006db247cSraghuram 	freemsgchain(mp);
89106db247cSraghuram 
89206db247cSraghuram 	D1(vswp, "%s: exit", __func__);
89306db247cSraghuram 
89406db247cSraghuram 	return (0);
89506db247cSraghuram }
89606db247cSraghuram 
89706db247cSraghuram /*
898c1c61f44Ssb155480  * This function creates the vlan id hash table for the given vsw device or
899c1c61f44Ssb155480  * port. It then adds each vlan that the device or port has been assigned,
900c1c61f44Ssb155480  * into this hash table.
901c1c61f44Ssb155480  * Arguments:
902c1c61f44Ssb155480  *   arg:  vsw device or port.
903c1c61f44Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
90406db247cSraghuram  */
905c1c61f44Ssb155480 void
vsw_create_vlans(void * arg,int type)906c1c61f44Ssb155480 vsw_create_vlans(void *arg, int type)
907c1c61f44Ssb155480 {
908c1c61f44Ssb155480 	/* create vlan hash table */
909c1c61f44Ssb155480 	vsw_vlan_create_hash(arg, type);
910c1c61f44Ssb155480 
911c1c61f44Ssb155480 	/* add vlan ids of the vsw device into its hash table */
912c1c61f44Ssb155480 	vsw_vlan_add_ids(arg, type);
913c1c61f44Ssb155480 }
914c1c61f44Ssb155480 
915c1c61f44Ssb155480 /*
916c1c61f44Ssb155480  * This function removes the vlan ids of the vsw device or port from its hash
917c1c61f44Ssb155480  * table. It then destroys the vlan hash table.
918c1c61f44Ssb155480  * Arguments:
919c1c61f44Ssb155480  *   arg:  vsw device or port.
920c1c61f44Ssb155480  *   type: type of arg; VSW_LOCALDEV(vsw device) or VSW_VNETPORT(port).
921c1c61f44Ssb155480  */
922c1c61f44Ssb155480 void
vsw_destroy_vlans(void * arg,int type)923c1c61f44Ssb155480 vsw_destroy_vlans(void *arg, int type)
924c1c61f44Ssb155480 {
925c1c61f44Ssb155480 	/* remove vlan ids from the hash table */
926c1c61f44Ssb155480 	vsw_vlan_remove_ids(arg, type);
927c1c61f44Ssb155480 
928c1c61f44Ssb155480 	/* destroy vlan-hash-table */
929c1c61f44Ssb155480 	vsw_vlan_destroy_hash(arg, type);
930c1c61f44Ssb155480 }
931c1c61f44Ssb155480 
932c1c61f44Ssb155480 /*
933c1c61f44Ssb155480  * Create a vlan-id hash table for the given vsw device or port.
934c1c61f44Ssb155480  */
935c1c61f44Ssb155480 static void
vsw_vlan_create_hash(void * arg,int type)936c1c61f44Ssb155480 vsw_vlan_create_hash(void *arg, int type)
937c1c61f44Ssb155480 {
938c1c61f44Ssb155480 	char		hashname[MAXNAMELEN];
939c1c61f44Ssb155480 
940c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
941c1c61f44Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
942c1c61f44Ssb155480 
943c1c61f44Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "vsw%d-vlan-hash",
944c1c61f44Ssb155480 		    vswp->instance);
945c1c61f44Ssb155480 
946c1c61f44Ssb155480 		vswp->vlan_nchains = vsw_vlan_nchains;
947c1c61f44Ssb155480 		vswp->vlan_hashp = mod_hash_create_idhash(hashname,
948c1c61f44Ssb155480 		    vswp->vlan_nchains, mod_hash_null_valdtor);
949c1c61f44Ssb155480 
950c1c61f44Ssb155480 	} else if (type == VSW_VNETPORT) {
951c1c61f44Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
952c1c61f44Ssb155480 
953c1c61f44Ssb155480 		(void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash",
954c1c61f44Ssb155480 		    portp->p_instance);
955c1c61f44Ssb155480 
956c1c61f44Ssb155480 		portp->vlan_nchains = vsw_vlan_nchains;
957c1c61f44Ssb155480 		portp->vlan_hashp = mod_hash_create_idhash(hashname,
958c1c61f44Ssb155480 		    portp->vlan_nchains, mod_hash_null_valdtor);
959c1c61f44Ssb155480 
960c1c61f44Ssb155480 	} else {
961c1c61f44Ssb155480 		return;
962c1c61f44Ssb155480 	}
963c1c61f44Ssb155480 }
964c1c61f44Ssb155480 
965c1c61f44Ssb155480 /*
966c1c61f44Ssb155480  * Destroy the vlan-id hash table for the given vsw device or port.
967c1c61f44Ssb155480  */
968c1c61f44Ssb155480 static void
vsw_vlan_destroy_hash(void * arg,int type)969c1c61f44Ssb155480 vsw_vlan_destroy_hash(void *arg, int type)
970c1c61f44Ssb155480 {
971c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
972c1c61f44Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
973c1c61f44Ssb155480 
974c1c61f44Ssb155480 		mod_hash_destroy_hash(vswp->vlan_hashp);
975c1c61f44Ssb155480 		vswp->vlan_nchains = 0;
976c1c61f44Ssb155480 	} else if (type == VSW_VNETPORT) {
977c1c61f44Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
978c1c61f44Ssb155480 
979c1c61f44Ssb155480 		mod_hash_destroy_hash(portp->vlan_hashp);
980c1c61f44Ssb155480 		portp->vlan_nchains = 0;
981c1c61f44Ssb155480 	} else {
982c1c61f44Ssb155480 		return;
983c1c61f44Ssb155480 	}
984c1c61f44Ssb155480 }
985c1c61f44Ssb155480 
986c1c61f44Ssb155480 /*
987c1c61f44Ssb155480  * Add vlan ids of the given vsw device or port into its hash table.
988c1c61f44Ssb155480  */
989c1c61f44Ssb155480 void
vsw_vlan_add_ids(void * arg,int type)990c1c61f44Ssb155480 vsw_vlan_add_ids(void *arg, int type)
991c1c61f44Ssb155480 {
992c1c61f44Ssb155480 	int	rv;
993c1c61f44Ssb155480 	int	i;
994c1c61f44Ssb155480 
995c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
996c1c61f44Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
997c1c61f44Ssb155480 
998c1c61f44Ssb155480 		rv = mod_hash_insert(vswp->vlan_hashp,
999c1c61f44Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
1000c1c61f44Ssb155480 		    (mod_hash_val_t)B_TRUE);
1001da14cebeSEric Cheng 		if (rv != 0) {
1002da14cebeSEric Cheng 			cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d) for "
1003da14cebeSEric Cheng 			    "the interface", vswp->instance, vswp->pvid);
1004da14cebeSEric Cheng 		}
1005c1c61f44Ssb155480 
1006c1c61f44Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
1007c1c61f44Ssb155480 			rv = mod_hash_insert(vswp->vlan_hashp,
1008da14cebeSEric Cheng 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->vids[i].vl_vid),
1009c1c61f44Ssb155480 			    (mod_hash_val_t)B_TRUE);
1010da14cebeSEric Cheng 			if (rv != 0) {
1011da14cebeSEric Cheng 				cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d)"
1012da14cebeSEric Cheng 				    " for the interface", vswp->instance,
1013da14cebeSEric Cheng 				    vswp->pvid);
1014da14cebeSEric Cheng 			}
1015c1c61f44Ssb155480 		}
1016c1c61f44Ssb155480 
1017c1c61f44Ssb155480 	} else if (type == VSW_VNETPORT) {
1018c1c61f44Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
1019da14cebeSEric Cheng 		vsw_t		*vswp = portp->p_vswp;
1020c1c61f44Ssb155480 
1021c1c61f44Ssb155480 		rv = mod_hash_insert(portp->vlan_hashp,
1022c1c61f44Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
1023c1c61f44Ssb155480 		    (mod_hash_val_t)B_TRUE);
1024da14cebeSEric Cheng 		if (rv != 0) {
1025da14cebeSEric Cheng 			cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d) for "
1026da14cebeSEric Cheng 			    "the port(%d)", vswp->instance, vswp->pvid,
1027da14cebeSEric Cheng 			    portp->p_instance);
1028da14cebeSEric Cheng 		}
1029c1c61f44Ssb155480 
1030c1c61f44Ssb155480 		for (i = 0; i < portp->nvids; i++) {
1031c1c61f44Ssb155480 			rv = mod_hash_insert(portp->vlan_hashp,
1032da14cebeSEric Cheng 			    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i].vl_vid),
1033c1c61f44Ssb155480 			    (mod_hash_val_t)B_TRUE);
1034da14cebeSEric Cheng 			if (rv != 0) {
1035da14cebeSEric Cheng 				cmn_err(CE_WARN, "vsw%d: Duplicate vlan-id(%d)"
1036da14cebeSEric Cheng 				    " for the port(%d)", vswp->instance,
1037da14cebeSEric Cheng 				    vswp->pvid, portp->p_instance);
1038da14cebeSEric Cheng 			}
1039c1c61f44Ssb155480 		}
1040c1c61f44Ssb155480 
1041c1c61f44Ssb155480 	}
1042c1c61f44Ssb155480 }
1043c1c61f44Ssb155480 
1044c1c61f44Ssb155480 /*
1045c1c61f44Ssb155480  * Remove vlan ids of the given vsw device or port from its hash table.
1046c1c61f44Ssb155480  */
1047c1c61f44Ssb155480 void
vsw_vlan_remove_ids(void * arg,int type)1048c1c61f44Ssb155480 vsw_vlan_remove_ids(void *arg, int type)
1049c1c61f44Ssb155480 {
1050c1c61f44Ssb155480 	mod_hash_val_t	vp;
1051c1c61f44Ssb155480 	int		rv;
1052c1c61f44Ssb155480 	int		i;
1053c1c61f44Ssb155480 
1054c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
1055c1c61f44Ssb155480 		vsw_t		*vswp = (vsw_t *)arg;
1056c1c61f44Ssb155480 
1057c1c61f44Ssb155480 		rv = vsw_vlan_lookup(vswp->vlan_hashp, vswp->pvid);
1058c1c61f44Ssb155480 		if (rv == B_TRUE) {
1059c1c61f44Ssb155480 			rv = mod_hash_remove(vswp->vlan_hashp,
1060c1c61f44Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(vswp->pvid),
1061c1c61f44Ssb155480 			    (mod_hash_val_t *)&vp);
1062c1c61f44Ssb155480 			ASSERT(rv == 0);
1063c1c61f44Ssb155480 		}
1064c1c61f44Ssb155480 
1065c1c61f44Ssb155480 		for (i = 0; i < vswp->nvids; i++) {
1066da14cebeSEric Cheng 			rv = vsw_vlan_lookup(vswp->vlan_hashp,
1067da14cebeSEric Cheng 			    vswp->vids[i].vl_vid);
1068c1c61f44Ssb155480 			if (rv == B_TRUE) {
1069c1c61f44Ssb155480 				rv = mod_hash_remove(vswp->vlan_hashp,
1070da14cebeSEric Cheng 				    (mod_hash_key_t)VLAN_ID_KEY(
1071da14cebeSEric Cheng 				    vswp->vids[i].vl_vid),
1072c1c61f44Ssb155480 				    (mod_hash_val_t *)&vp);
1073c1c61f44Ssb155480 				ASSERT(rv == 0);
1074c1c61f44Ssb155480 			}
1075c1c61f44Ssb155480 		}
1076c1c61f44Ssb155480 
1077c1c61f44Ssb155480 	} else if (type == VSW_VNETPORT) {
1078c1c61f44Ssb155480 		vsw_port_t	*portp = (vsw_port_t *)arg;
1079c1c61f44Ssb155480 
1080c1c61f44Ssb155480 		portp = (vsw_port_t *)arg;
1081c1c61f44Ssb155480 		rv = vsw_vlan_lookup(portp->vlan_hashp, portp->pvid);
1082c1c61f44Ssb155480 		if (rv == B_TRUE) {
1083c1c61f44Ssb155480 			rv = mod_hash_remove(portp->vlan_hashp,
1084c1c61f44Ssb155480 			    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
1085c1c61f44Ssb155480 			    (mod_hash_val_t *)&vp);
1086c1c61f44Ssb155480 			ASSERT(rv == 0);
1087c1c61f44Ssb155480 		}
1088c1c61f44Ssb155480 
1089c1c61f44Ssb155480 		for (i = 0; i < portp->nvids; i++) {
1090da14cebeSEric Cheng 			rv = vsw_vlan_lookup(portp->vlan_hashp,
1091da14cebeSEric Cheng 			    portp->vids[i].vl_vid);
1092c1c61f44Ssb155480 			if (rv == B_TRUE) {
1093c1c61f44Ssb155480 				rv = mod_hash_remove(portp->vlan_hashp,
1094da14cebeSEric Cheng 				    (mod_hash_key_t)VLAN_ID_KEY(
1095da14cebeSEric Cheng 				    portp->vids[i].vl_vid),
1096c1c61f44Ssb155480 				    (mod_hash_val_t *)&vp);
1097c1c61f44Ssb155480 				ASSERT(rv == 0);
1098c1c61f44Ssb155480 			}
1099c1c61f44Ssb155480 		}
1100c1c61f44Ssb155480 
1101c1c61f44Ssb155480 	} else {
1102c1c61f44Ssb155480 		return;
1103c1c61f44Ssb155480 	}
1104c1c61f44Ssb155480 }
1105c1c61f44Ssb155480 
1106c1c61f44Ssb155480 /*
1107c1c61f44Ssb155480  * Find the given vlan id in the hash table.
1108c1c61f44Ssb155480  * Return: B_TRUE if the id is found; B_FALSE if not found.
1109c1c61f44Ssb155480  */
1110c1c61f44Ssb155480 boolean_t
vsw_vlan_lookup(mod_hash_t * vlan_hashp,uint16_t vid)1111c1c61f44Ssb155480 vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid)
1112c1c61f44Ssb155480 {
1113c1c61f44Ssb155480 	int		rv;
1114c1c61f44Ssb155480 	mod_hash_val_t	vp;
1115c1c61f44Ssb155480 
1116c1c61f44Ssb155480 	rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp);
1117c1c61f44Ssb155480 
1118c1c61f44Ssb155480 	if (rv != 0)
1119c1c61f44Ssb155480 		return (B_FALSE);
1120c1c61f44Ssb155480 
1121c1c61f44Ssb155480 	return (B_TRUE);
1122c1c61f44Ssb155480 }
1123c1c61f44Ssb155480 
1124c1c61f44Ssb155480 /*
1125c1c61f44Ssb155480  * Add an entry into FDB for the given vsw.
1126c1c61f44Ssb155480  */
1127c1c61f44Ssb155480 void
vsw_fdbe_add(vsw_t * vswp,void * port)1128c1c61f44Ssb155480 vsw_fdbe_add(vsw_t *vswp, void *port)
112906db247cSraghuram {
113006db247cSraghuram 	uint64_t	addr = 0;
1131c1c61f44Ssb155480 	vsw_port_t	*portp;
1132c1c61f44Ssb155480 	vsw_fdbe_t	*fp;
1133c1c61f44Ssb155480 	int		rv;
113406db247cSraghuram 
1135c1c61f44Ssb155480 	portp = (vsw_port_t *)port;
1136c1c61f44Ssb155480 	KEY_HASH(addr, &portp->p_macaddr);
113706db247cSraghuram 
1138c1c61f44Ssb155480 	fp = kmem_zalloc(sizeof (vsw_fdbe_t), KM_SLEEP);
1139c1c61f44Ssb155480 	fp->portp = port;
114006db247cSraghuram 
114106db247cSraghuram 	/*
114206db247cSraghuram 	 * Note: duplicate keys will be rejected by mod_hash.
114306db247cSraghuram 	 */
1144c1c61f44Ssb155480 	rv = mod_hash_insert(vswp->fdb_hashp, (mod_hash_key_t)addr,
1145c1c61f44Ssb155480 	    (mod_hash_val_t)fp);
1146da14cebeSEric Cheng 	if (rv != 0) {
1147da14cebeSEric Cheng 		cmn_err(CE_WARN, "vsw%d: Duplicate mac-address(%s) for "
1148da14cebeSEric Cheng 		    "the port(%d)", vswp->instance,
1149da14cebeSEric Cheng 		    ether_sprintf(&portp->p_macaddr), portp->p_instance);
1150*a862df29SSriharsha Basavapatna 		kmem_free(fp, sizeof (*fp));
1151da14cebeSEric Cheng 	}
115206db247cSraghuram }
115306db247cSraghuram 
115406db247cSraghuram /*
115506db247cSraghuram  * Remove an entry from FDB.
115606db247cSraghuram  */
1157c1c61f44Ssb155480 void
vsw_fdbe_del(vsw_t * vswp,struct ether_addr * eaddr)1158c1c61f44Ssb155480 vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr)
115906db247cSraghuram {
116006db247cSraghuram 	uint64_t	addr = 0;
1161c1c61f44Ssb155480 	vsw_fdbe_t	*fp;
1162c1c61f44Ssb155480 	int		rv;
116306db247cSraghuram 
1164c1c61f44Ssb155480 	KEY_HASH(addr, eaddr);
116506db247cSraghuram 
1166c1c61f44Ssb155480 	/*
1167c1c61f44Ssb155480 	 * Remove the entry from fdb hash table.
1168c1c61f44Ssb155480 	 * This prevents further references to this fdb entry.
1169c1c61f44Ssb155480 	 */
1170c1c61f44Ssb155480 	rv = mod_hash_remove(vswp->fdb_hashp, (mod_hash_key_t)addr,
1171c1c61f44Ssb155480 	    (mod_hash_val_t *)&fp);
1172c1c61f44Ssb155480 	if (rv != 0) {
1173c1c61f44Ssb155480 		/* invalid key? */
1174c1c61f44Ssb155480 		return;
117506db247cSraghuram 	}
117606db247cSraghuram 
117706db247cSraghuram 	/*
1178c1c61f44Ssb155480 	 * If there are threads already ref holding before the entry was
1179c1c61f44Ssb155480 	 * removed from hash table, then wait for ref count to drop to zero.
118006db247cSraghuram 	 */
1181c1c61f44Ssb155480 	while (fp->refcnt != 0) {
1182c1c61f44Ssb155480 		delay(drv_usectohz(vsw_fdbe_refcnt_delay));
118306db247cSraghuram 	}
118406db247cSraghuram 
1185c1c61f44Ssb155480 	kmem_free(fp, sizeof (*fp));
1186c1c61f44Ssb155480 }
118706db247cSraghuram 
1188c1c61f44Ssb155480 /*
1189c1c61f44Ssb155480  * Search fdb for a given mac address. If an entry is found, hold
1190c1c61f44Ssb155480  * a reference to it and return the entry, else returns NULL.
1191c1c61f44Ssb155480  */
1192c1c61f44Ssb155480 static vsw_fdbe_t *
vsw_fdbe_find(vsw_t * vswp,struct ether_addr * addrp)1193c1c61f44Ssb155480 vsw_fdbe_find(vsw_t *vswp, struct ether_addr *addrp)
1194c1c61f44Ssb155480 {
1195c1c61f44Ssb155480 	uint64_t	key = 0;
1196c1c61f44Ssb155480 	vsw_fdbe_t	*fp;
1197c1c61f44Ssb155480 	int		rv;
1198c1c61f44Ssb155480 
1199c1c61f44Ssb155480 	KEY_HASH(key, addrp);
1200c1c61f44Ssb155480 
1201c1c61f44Ssb155480 	rv = mod_hash_find_cb(vswp->fdb_hashp, (mod_hash_key_t)key,
1202c1c61f44Ssb155480 	    (mod_hash_val_t *)&fp, vsw_fdbe_find_cb);
1203c1c61f44Ssb155480 
1204c1c61f44Ssb155480 	if (rv != 0)
1205c1c61f44Ssb155480 		return (NULL);
1206c1c61f44Ssb155480 
1207c1c61f44Ssb155480 	return (fp);
1208c1c61f44Ssb155480 }
1209c1c61f44Ssb155480 
1210c1c61f44Ssb155480 /*
1211c1c61f44Ssb155480  * Callback function provided to mod_hash_find_cb(). After finding the fdb
1212c1c61f44Ssb155480  * entry corresponding to the key (macaddr), this callback will be invoked by
1213c1c61f44Ssb155480  * mod_hash_find_cb() to atomically increment the reference count on the fdb
1214c1c61f44Ssb155480  * entry before returning the found entry.
1215c1c61f44Ssb155480  */
1216c1c61f44Ssb155480 static void
vsw_fdbe_find_cb(mod_hash_key_t key,mod_hash_val_t val)1217c1c61f44Ssb155480 vsw_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
1218c1c61f44Ssb155480 {
1219c1c61f44Ssb155480 	_NOTE(ARGUNUSED(key))
1220c1c61f44Ssb155480 	VSW_FDBE_REFHOLD((vsw_fdbe_t *)val);
1221c1c61f44Ssb155480 }
1222c1c61f44Ssb155480 
1223c1c61f44Ssb155480 /*
1224c1c61f44Ssb155480  * A given frame must be always tagged with the appropriate vlan id (unless it
1225c1c61f44Ssb155480  * is in the default-vlan) before the mac address switching function is called.
1226c1c61f44Ssb155480  * Otherwise, after switching function determines the destination, we cannot
1227c1c61f44Ssb155480  * figure out if the destination belongs to the the same vlan that the frame
1228c1c61f44Ssb155480  * originated from and if it needs tag/untag. Frames which are inbound from
1229c1c61f44Ssb155480  * the external(physical) network over a vlan trunk link are always tagged.
1230c1c61f44Ssb155480  * However frames which are received from a vnet-port over ldc or frames which
1231c1c61f44Ssb155480  * are coming down the stack on the service domain over vsw interface may be
1232c1c61f44Ssb155480  * untagged. These frames must be tagged with the appropriate pvid of the
1233c1c61f44Ssb155480  * sender (vnet-port or vsw device), before invoking the switching function.
1234c1c61f44Ssb155480  *
1235c1c61f44Ssb155480  * Arguments:
1236c1c61f44Ssb155480  *   arg:    caller of the function.
1237c1c61f44Ssb155480  *   type:   type of arg(caller): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
1238c1c61f44Ssb155480  *   mp:     frame(s) to be tagged.
1239c1c61f44Ssb155480  */
1240c1c61f44Ssb155480 mblk_t *
vsw_vlan_frame_pretag(void * arg,int type,mblk_t * mp)1241c1c61f44Ssb155480 vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp)
1242c1c61f44Ssb155480 {
1243c1c61f44Ssb155480 	vsw_t			*vswp;
1244c1c61f44Ssb155480 	vsw_port_t		*portp;
1245c1c61f44Ssb155480 	struct ether_header	*ehp;
1246c1c61f44Ssb155480 	mblk_t			*bp;
1247c1c61f44Ssb155480 	mblk_t			*bpt;
1248c1c61f44Ssb155480 	mblk_t			*bph;
1249c1c61f44Ssb155480 	mblk_t			*bpn;
1250c1c61f44Ssb155480 	uint16_t		pvid;
1251c1c61f44Ssb155480 
1252c1c61f44Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
1253c1c61f44Ssb155480 
1254c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
1255c1c61f44Ssb155480 		vswp = (vsw_t *)arg;
1256c1c61f44Ssb155480 		pvid = vswp->pvid;
1257c1c61f44Ssb155480 		portp = NULL;
1258c1c61f44Ssb155480 	} else {
1259c1c61f44Ssb155480 		/* VSW_VNETPORT */
1260c1c61f44Ssb155480 		portp = (vsw_port_t *)arg;
1261c1c61f44Ssb155480 		pvid = portp->pvid;
1262c1c61f44Ssb155480 		vswp = portp->p_vswp;
1263c1c61f44Ssb155480 	}
1264c1c61f44Ssb155480 
1265c1c61f44Ssb155480 	bpn = bph = bpt = NULL;
1266c1c61f44Ssb155480 
1267c1c61f44Ssb155480 	for (bp = mp; bp != NULL; bp = bpn) {
1268c1c61f44Ssb155480 
1269c1c61f44Ssb155480 		bpn = bp->b_next;
1270c1c61f44Ssb155480 		bp->b_next = bp->b_prev = NULL;
1271c1c61f44Ssb155480 
1272c1c61f44Ssb155480 		/* Determine if it is an untagged frame */
1273c1c61f44Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
1274c1c61f44Ssb155480 
1275c1c61f44Ssb155480 		if (ehp->ether_type != ETHERTYPE_VLAN) {	/* untagged */
1276c1c61f44Ssb155480 
1277c1c61f44Ssb155480 			/* no need to tag if the frame is in default vlan */
1278c1c61f44Ssb155480 			if (pvid != vswp->default_vlan_id) {
1279c1c61f44Ssb155480 				bp = vnet_vlan_insert_tag(bp, pvid);
1280c1c61f44Ssb155480 				if (bp == NULL) {
1281c1c61f44Ssb155480 					continue;
1282c1c61f44Ssb155480 				}
1283c1c61f44Ssb155480 			}
1284c1c61f44Ssb155480 		}
1285c1c61f44Ssb155480 
1286c1c61f44Ssb155480 		/* build a chain of processed packets */
1287c1c61f44Ssb155480 		if (bph == NULL) {
1288c1c61f44Ssb155480 			bph = bpt = bp;
1289c1c61f44Ssb155480 		} else {
1290c1c61f44Ssb155480 			bpt->b_next = bp;
1291c1c61f44Ssb155480 			bpt = bp;
1292c1c61f44Ssb155480 		}
1293c1c61f44Ssb155480 
1294c1c61f44Ssb155480 	}
1295c1c61f44Ssb155480 
1296c1c61f44Ssb155480 	return (bph);
1297c1c61f44Ssb155480 }
1298c1c61f44Ssb155480 
1299c1c61f44Ssb155480 /*
1300c1c61f44Ssb155480  * Frames destined to a vnet-port or to the local vsw interface, must be
1301c1c61f44Ssb155480  * untagged if necessary before sending. This function first checks that the
1302c1c61f44Ssb155480  * frame can be sent to the destination in the vlan identified by the frame
1303c1c61f44Ssb155480  * tag. Note that when this function is invoked the frame must have been
1304c1c61f44Ssb155480  * already tagged (unless it is in the default-vlan). Because, this function is
1305c1c61f44Ssb155480  * called when the switching function determines the destination and invokes
1306c1c61f44Ssb155480  * its send function (vnet-port or vsw interface) and all frames would have
1307c1c61f44Ssb155480  * been tagged by this time (see comments in vsw_vlan_frame_pretag()).
1308c1c61f44Ssb155480  *
1309c1c61f44Ssb155480  * Arguments:
1310c1c61f44Ssb155480  *   arg:    destination device.
1311c1c61f44Ssb155480  *   type:   type of arg(destination): VSW_LOCALDEV(vsw) or VSW_VNETPORT(port)
1312c1c61f44Ssb155480  *   np:     head of pkt chain to be validated and untagged.
1313c1c61f44Ssb155480  *   npt:    tail of pkt chain to be validated and untagged.
1314c1c61f44Ssb155480  *
1315c1c61f44Ssb155480  * Returns:
1316c1c61f44Ssb155480  *   np:     head of updated chain of packets
1317c1c61f44Ssb155480  *   npt:    tail of updated chain of packets
1318da14cebeSEric Cheng  *   rv:     count of the packets in the returned list
1319c1c61f44Ssb155480  */
1320c1c61f44Ssb155480 uint32_t
vsw_vlan_frame_untag(void * arg,int type,mblk_t ** np,mblk_t ** npt)1321c1c61f44Ssb155480 vsw_vlan_frame_untag(void *arg, int type, mblk_t **np, mblk_t **npt)
1322c1c61f44Ssb155480 {
1323c1c61f44Ssb155480 	mblk_t			*bp;
1324c1c61f44Ssb155480 	mblk_t			*bpt;
1325c1c61f44Ssb155480 	mblk_t			*bph;
1326c1c61f44Ssb155480 	mblk_t			*bpn;
1327c1c61f44Ssb155480 	vsw_port_t		*portp;
1328c1c61f44Ssb155480 	vsw_t			*vswp;
1329c1c61f44Ssb155480 	uint32_t		count;
1330c1c61f44Ssb155480 	struct ether_header	*ehp;
1331c1c61f44Ssb155480 	boolean_t		is_tagged;
1332c1c61f44Ssb155480 	boolean_t		rv;
1333c1c61f44Ssb155480 	uint16_t		vlan_id;
1334c1c61f44Ssb155480 	uint16_t		pvid;
1335c1c61f44Ssb155480 	mod_hash_t		*vlan_hashp;
1336c1c61f44Ssb155480 
1337c1c61f44Ssb155480 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
1338c1c61f44Ssb155480 
1339da14cebeSEric Cheng 
1340c1c61f44Ssb155480 	if (type == VSW_LOCALDEV) {
1341c1c61f44Ssb155480 		vswp = (vsw_t *)arg;
1342c1c61f44Ssb155480 		pvid = vswp->pvid;
1343c1c61f44Ssb155480 		vlan_hashp = vswp->vlan_hashp;
1344c1c61f44Ssb155480 		portp = NULL;
1345c1c61f44Ssb155480 	} else {
1346c1c61f44Ssb155480 		/* type == VSW_VNETPORT */
1347c1c61f44Ssb155480 		portp = (vsw_port_t *)arg;
1348c1c61f44Ssb155480 		vswp = portp->p_vswp;
1349c1c61f44Ssb155480 		vlan_hashp = portp->vlan_hashp;
1350c1c61f44Ssb155480 		pvid = portp->pvid;
1351c1c61f44Ssb155480 	}
1352c1c61f44Ssb155480 
1353da14cebeSEric Cheng 	/*
1354da14cebeSEric Cheng 	 * If the MAC layer switching in place, then
1355da14cebeSEric Cheng 	 * untagging required only if the pvid is not
1356da14cebeSEric Cheng 	 * the same as default_vlan_id. This is because,
1357da14cebeSEric Cheng 	 * the MAC layer will send packets for the
1358da14cebeSEric Cheng 	 * registered vlans only.
1359da14cebeSEric Cheng 	 */
1360da14cebeSEric Cheng 	if ((vswp->mac_cl_switching == B_TRUE) &&
1361da14cebeSEric Cheng 	    (pvid == vswp->default_vlan_id)) {
1362da14cebeSEric Cheng 		/* simply count and set the tail */
1363da14cebeSEric Cheng 		count = 1;
1364da14cebeSEric Cheng 		bp = *np;
1365da14cebeSEric Cheng 		ASSERT(bp != NULL);
1366da14cebeSEric Cheng 		while (bp->b_next != NULL) {
1367da14cebeSEric Cheng 			bp = bp->b_next;
1368da14cebeSEric Cheng 			count++;
1369da14cebeSEric Cheng 		}
1370da14cebeSEric Cheng 		*npt = bp;
1371da14cebeSEric Cheng 		return (count);
1372da14cebeSEric Cheng 	}
1373da14cebeSEric Cheng 
1374c1c61f44Ssb155480 	bpn = bph = bpt = NULL;
1375c1c61f44Ssb155480 	count = 0;
1376c1c61f44Ssb155480 
1377c1c61f44Ssb155480 	for (bp = *np; bp != NULL; bp = bpn) {
1378c1c61f44Ssb155480 
1379c1c61f44Ssb155480 		bpn = bp->b_next;
1380c1c61f44Ssb155480 		bp->b_next = bp->b_prev = NULL;
1381c1c61f44Ssb155480 
1382c1c61f44Ssb155480 		/*
1383c1c61f44Ssb155480 		 * Determine the vlan id that the frame belongs to.
1384c1c61f44Ssb155480 		 */
1385c1c61f44Ssb155480 		ehp = (struct ether_header *)bp->b_rptr;
1386c1c61f44Ssb155480 		is_tagged = vsw_frame_lookup_vid(arg, type, ehp, &vlan_id);
1387c1c61f44Ssb155480 
1388c1c61f44Ssb155480 		/*
1389da14cebeSEric Cheng 		 * If MAC layer switching in place, then we
1390da14cebeSEric Cheng 		 * need to untag only if the tagged packet has
1391da14cebeSEric Cheng 		 * vlan-id same as the pvid.
1392c1c61f44Ssb155480 		 */
1393da14cebeSEric Cheng 		if (vswp->mac_cl_switching == B_TRUE) {
1394da14cebeSEric Cheng 
1395da14cebeSEric Cheng 			/* only tagged packets expected here */
1396da14cebeSEric Cheng 			ASSERT(is_tagged == B_TRUE);
1397da14cebeSEric Cheng 			if (vlan_id == pvid) {
1398da14cebeSEric Cheng 				bp = vnet_vlan_remove_tag(bp);
1399da14cebeSEric Cheng 				if (bp == NULL) {
1400da14cebeSEric Cheng 					/* packet dropped */
1401c1c61f44Ssb155480 					continue;
1402c1c61f44Ssb155480 				}
1403da14cebeSEric Cheng 			}
1404da14cebeSEric Cheng 		} else { /* No MAC layer switching */
1405c1c61f44Ssb155480 
1406c1c61f44Ssb155480 			/*
1407c1c61f44Ssb155480 			 * Check the frame header if tag/untag is  needed.
1408c1c61f44Ssb155480 			 */
1409c1c61f44Ssb155480 			if (is_tagged == B_FALSE) {
1410c1c61f44Ssb155480 				/*
1411da14cebeSEric Cheng 				 * Untagged frame. We shouldn't have an
1412da14cebeSEric Cheng 				 * untagged packet at this point, unless
1413da14cebeSEric Cheng 				 * the destination's  vlan id is
1414da14cebeSEric Cheng 				 * default-vlan-id; if it is not the
1415c1c61f44Ssb155480 				 * default-vlan-id, we drop the packet.
1416c1c61f44Ssb155480 				 */
1417c1c61f44Ssb155480 				if (vlan_id != vswp->default_vlan_id) {
1418c1c61f44Ssb155480 					/* drop the packet */
1419c1c61f44Ssb155480 					freemsg(bp);
1420c1c61f44Ssb155480 					continue;
1421c1c61f44Ssb155480 				}
1422da14cebeSEric Cheng 			} else {	/* Tagged */
1423c1c61f44Ssb155480 				/*
1424da14cebeSEric Cheng 				 * Tagged frame, untag if it's the
1425da14cebeSEric Cheng 				 * destination's pvid.
1426c1c61f44Ssb155480 				 */
1427c1c61f44Ssb155480 				if (vlan_id == pvid) {
1428c1c61f44Ssb155480 
1429c1c61f44Ssb155480 					bp = vnet_vlan_remove_tag(bp);
1430c1c61f44Ssb155480 					if (bp == NULL) {
1431c1c61f44Ssb155480 						/* packet dropped */
1432c1c61f44Ssb155480 						continue;
1433c1c61f44Ssb155480 					}
1434da14cebeSEric Cheng 				} else {
1435da14cebeSEric Cheng 
1436da14cebeSEric Cheng 					/*
1437da14cebeSEric Cheng 					 * Check if the destination is in the
1438da14cebeSEric Cheng 					 * same vlan.
1439da14cebeSEric Cheng 					 */
1440da14cebeSEric Cheng 					rv = vsw_vlan_lookup(vlan_hashp,
1441da14cebeSEric Cheng 					    vlan_id);
1442da14cebeSEric Cheng 					if (rv == B_FALSE) {
1443da14cebeSEric Cheng 						/* drop the packet */
1444da14cebeSEric Cheng 						freemsg(bp);
1445da14cebeSEric Cheng 						continue;
1446da14cebeSEric Cheng 					}
1447da14cebeSEric Cheng 				}
1448da14cebeSEric Cheng 
1449c1c61f44Ssb155480 			}
1450c1c61f44Ssb155480 		}
1451c1c61f44Ssb155480 
1452c1c61f44Ssb155480 		/* build a chain of processed packets */
1453c1c61f44Ssb155480 		if (bph == NULL) {
1454c1c61f44Ssb155480 			bph = bpt = bp;
1455c1c61f44Ssb155480 		} else {
1456c1c61f44Ssb155480 			bpt->b_next = bp;
1457c1c61f44Ssb155480 			bpt = bp;
1458c1c61f44Ssb155480 		}
1459da14cebeSEric Cheng 		count++;
1460c1c61f44Ssb155480 	}
1461c1c61f44Ssb155480 
1462c1c61f44Ssb155480 	*np = bph;
1463c1c61f44Ssb155480 	*npt = bpt;
1464c1c61f44Ssb155480 	return (count);
1465c1c61f44Ssb155480 }
1466c1c61f44Ssb155480 
1467c1c61f44Ssb155480 /*
1468c1c61f44Ssb155480  * Lookup the vlan id of the given frame. If it is a vlan-tagged frame,
1469c1c61f44Ssb155480  * then the vlan-id is available in the tag; otherwise, its vlan id is
1470c1c61f44Ssb155480  * implicitly obtained based on the caller (destination of the frame:
1471c1c61f44Ssb155480  * VSW_VNETPORT or VSW_LOCALDEV).
1472c1c61f44Ssb155480  * The vlan id determined is returned in vidp.
1473c1c61f44Ssb155480  * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
1474c1c61f44Ssb155480  */
1475c1c61f44Ssb155480 boolean_t
vsw_frame_lookup_vid(void * arg,int caller,struct ether_header * ehp,uint16_t * vidp)1476c1c61f44Ssb155480 vsw_frame_lookup_vid(void *arg, int caller, struct ether_header *ehp,
1477c1c61f44Ssb155480 	uint16_t *vidp)
1478c1c61f44Ssb155480 {
1479c1c61f44Ssb155480 	struct ether_vlan_header	*evhp;
1480c1c61f44Ssb155480 	vsw_t				*vswp;
1481c1c61f44Ssb155480 	vsw_port_t			*portp;
1482c1c61f44Ssb155480 
1483c1c61f44Ssb155480 	/* If it's a tagged frame, get the vid from vlan header */
1484c1c61f44Ssb155480 	if (ehp->ether_type == ETHERTYPE_VLAN) {
1485c1c61f44Ssb155480 
1486c1c61f44Ssb155480 		evhp = (struct ether_vlan_header *)ehp;
1487c1c61f44Ssb155480 		*vidp = VLAN_ID(ntohs(evhp->ether_tci));
1488c1c61f44Ssb155480 		return (B_TRUE);
1489c1c61f44Ssb155480 	}
1490c1c61f44Ssb155480 
1491c1c61f44Ssb155480 	/* Untagged frame; determine vlan id based on caller */
1492c1c61f44Ssb155480 	switch (caller) {
1493c1c61f44Ssb155480 
1494c1c61f44Ssb155480 	case VSW_VNETPORT:
1495c1c61f44Ssb155480 		/*
1496c1c61f44Ssb155480 		 * packet destined to a vnet; vlan-id is pvid of vnet-port.
1497c1c61f44Ssb155480 		 */
1498c1c61f44Ssb155480 		portp = (vsw_port_t *)arg;
1499c1c61f44Ssb155480 		*vidp = portp->pvid;
1500c1c61f44Ssb155480 		break;
1501c1c61f44Ssb155480 
1502c1c61f44Ssb155480 	case VSW_LOCALDEV:
1503c1c61f44Ssb155480 
1504c1c61f44Ssb155480 		/*
1505c1c61f44Ssb155480 		 * packet destined to vsw interface;
1506c1c61f44Ssb155480 		 * vlan-id is port-vlan-id of vsw device.
1507c1c61f44Ssb155480 		 */
1508c1c61f44Ssb155480 		vswp = (vsw_t *)arg;
1509c1c61f44Ssb155480 		*vidp = vswp->pvid;
1510c1c61f44Ssb155480 		break;
1511c1c61f44Ssb155480 	}
1512c1c61f44Ssb155480 
1513c1c61f44Ssb155480 	return (B_FALSE);
151406db247cSraghuram }
151506db247cSraghuram 
151606db247cSraghuram /*
151706db247cSraghuram  * Add or remove multicast address(es).
151806db247cSraghuram  *
151906db247cSraghuram  * Returns 0 on success, 1 on failure.
152006db247cSraghuram  */
152106db247cSraghuram int
vsw_add_rem_mcst(vnet_mcast_msg_t * mcst_pkt,vsw_port_t * port)152206db247cSraghuram vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port)
152306db247cSraghuram {
152406db247cSraghuram 	mcst_addr_t		*mcst_p = NULL;
152506db247cSraghuram 	vsw_t			*vswp = port->p_vswp;
152606db247cSraghuram 	uint64_t		addr = 0x0;
152706db247cSraghuram 	int			i;
152806db247cSraghuram 
152906db247cSraghuram 	D1(vswp, "%s: enter", __func__);
153006db247cSraghuram 
153106db247cSraghuram 	D2(vswp, "%s: %d addresses", __func__, mcst_pkt->count);
153206db247cSraghuram 
153306db247cSraghuram 	for (i = 0; i < mcst_pkt->count; i++) {
153406db247cSraghuram 		/*
153506db247cSraghuram 		 * Convert address into form that can be used
153606db247cSraghuram 		 * as hash table key.
153706db247cSraghuram 		 */
1538c1c61f44Ssb155480 		KEY_HASH(addr, &(mcst_pkt->mca[i]));
153906db247cSraghuram 
154006db247cSraghuram 		/*
154106db247cSraghuram 		 * Add or delete the specified address/port combination.
154206db247cSraghuram 		 */
154306db247cSraghuram 		if (mcst_pkt->set == 0x1) {
154406db247cSraghuram 			D3(vswp, "%s: adding multicast address 0x%llx for "
154506db247cSraghuram 			    "port %ld", __func__, addr, port->p_instance);
154606db247cSraghuram 			if (vsw_add_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
154706db247cSraghuram 				/*
154806db247cSraghuram 				 * Update the list of multicast
154906db247cSraghuram 				 * addresses contained within the
155006db247cSraghuram 				 * port structure to include this new
155106db247cSraghuram 				 * one.
155206db247cSraghuram 				 */
155306db247cSraghuram 				mcst_p = kmem_zalloc(sizeof (mcst_addr_t),
155406db247cSraghuram 				    KM_NOSLEEP);
155506db247cSraghuram 				if (mcst_p == NULL) {
155606db247cSraghuram 					DERR(vswp, "%s: unable to alloc mem",
155706db247cSraghuram 					    __func__);
155806db247cSraghuram 					(void) vsw_del_mcst(vswp,
155906db247cSraghuram 					    VSW_VNETPORT, addr, port);
156006db247cSraghuram 					return (1);
156106db247cSraghuram 				}
156206db247cSraghuram 
156306db247cSraghuram 				mcst_p->nextp = NULL;
156406db247cSraghuram 				mcst_p->addr = addr;
156506db247cSraghuram 				ether_copy(&mcst_pkt->mca[i], &mcst_p->mca);
156606db247cSraghuram 
156706db247cSraghuram 				/*
156806db247cSraghuram 				 * Program the address into HW. If the addr
156906db247cSraghuram 				 * has already been programmed then the MAC
157006db247cSraghuram 				 * just increments a ref counter (which is
157106db247cSraghuram 				 * used when the address is being deleted)
157206db247cSraghuram 				 */
1573da14cebeSEric Cheng 				if (vsw_mac_multicast_add(vswp, port, mcst_p,
1574da14cebeSEric Cheng 				    VSW_VNETPORT)) {
157506db247cSraghuram 					(void) vsw_del_mcst(vswp,
157606db247cSraghuram 					    VSW_VNETPORT, addr, port);
1577da14cebeSEric Cheng 					kmem_free(mcst_p, sizeof (*mcst_p));
157806db247cSraghuram 					return (1);
157906db247cSraghuram 				}
158006db247cSraghuram 
158106db247cSraghuram 				mutex_enter(&port->mca_lock);
158206db247cSraghuram 				mcst_p->nextp = port->mcap;
158306db247cSraghuram 				port->mcap = mcst_p;
158406db247cSraghuram 				mutex_exit(&port->mca_lock);
158506db247cSraghuram 
158606db247cSraghuram 			} else {
158706db247cSraghuram 				DERR(vswp, "%s: error adding multicast "
158806db247cSraghuram 				    "address 0x%llx for port %ld",
158906db247cSraghuram 				    __func__, addr, port->p_instance);
159006db247cSraghuram 				return (1);
159106db247cSraghuram 			}
159206db247cSraghuram 		} else {
159306db247cSraghuram 			/*
159406db247cSraghuram 			 * Delete an entry from the multicast hash
159506db247cSraghuram 			 * table and update the address list
159606db247cSraghuram 			 * appropriately.
159706db247cSraghuram 			 */
159806db247cSraghuram 			if (vsw_del_mcst(vswp, VSW_VNETPORT, addr, port) == 0) {
159906db247cSraghuram 				D3(vswp, "%s: deleting multicast address "
160006db247cSraghuram 				    "0x%llx for port %ld", __func__, addr,
160106db247cSraghuram 				    port->p_instance);
160206db247cSraghuram 
160306db247cSraghuram 				mcst_p = vsw_del_addr(VSW_VNETPORT, port, addr);
160406db247cSraghuram 				ASSERT(mcst_p != NULL);
160506db247cSraghuram 
160606db247cSraghuram 				/*
160706db247cSraghuram 				 * Remove the address from HW. The address
160806db247cSraghuram 				 * will actually only be removed once the ref
160906db247cSraghuram 				 * count within the MAC layer has dropped to
161006db247cSraghuram 				 * zero. I.e. we can safely call this fn even
161106db247cSraghuram 				 * if other ports are interested in this
161206db247cSraghuram 				 * address.
161306db247cSraghuram 				 */
1614da14cebeSEric Cheng 				vsw_mac_multicast_remove(vswp, port, mcst_p,
1615da14cebeSEric Cheng 				    VSW_VNETPORT);
161606db247cSraghuram 				kmem_free(mcst_p, sizeof (*mcst_p));
161706db247cSraghuram 
161806db247cSraghuram 			} else {
161906db247cSraghuram 				DERR(vswp, "%s: error deleting multicast "
162006db247cSraghuram 				    "addr 0x%llx for port %ld",
162106db247cSraghuram 				    __func__, addr, port->p_instance);
162206db247cSraghuram 				return (1);
162306db247cSraghuram 			}
162406db247cSraghuram 		}
162506db247cSraghuram 	}
162606db247cSraghuram 	D1(vswp, "%s: exit", __func__);
162706db247cSraghuram 	return (0);
162806db247cSraghuram }
162906db247cSraghuram 
163006db247cSraghuram /*
163106db247cSraghuram  * Add a new multicast entry.
163206db247cSraghuram  *
163306db247cSraghuram  * Search hash table based on address. If match found then
163406db247cSraghuram  * update associated val (which is chain of ports), otherwise
163506db247cSraghuram  * create new key/val (addr/port) pair and insert into table.
163606db247cSraghuram  */
163706db247cSraghuram int
vsw_add_mcst(vsw_t * vswp,uint8_t devtype,uint64_t addr,void * arg)163806db247cSraghuram vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
163906db247cSraghuram {
164006db247cSraghuram 	int		dup = 0;
164106db247cSraghuram 	int		rv = 0;
164206db247cSraghuram 	mfdb_ent_t	*ment = NULL;
164306db247cSraghuram 	mfdb_ent_t	*tmp_ent = NULL;
164406db247cSraghuram 	mfdb_ent_t	*new_ent = NULL;
164506db247cSraghuram 	void		*tgt = NULL;
164606db247cSraghuram 
164706db247cSraghuram 	if (devtype == VSW_VNETPORT) {
164806db247cSraghuram 		/*
164906db247cSraghuram 		 * Being invoked from a vnet.
165006db247cSraghuram 		 */
165106db247cSraghuram 		ASSERT(arg != NULL);
165206db247cSraghuram 		tgt = arg;
165306db247cSraghuram 		D2(NULL, "%s: port %d : address 0x%llx", __func__,
165406db247cSraghuram 		    ((vsw_port_t *)arg)->p_instance, addr);
165506db247cSraghuram 	} else {
165606db247cSraghuram 		/*
165706db247cSraghuram 		 * We are being invoked via the m_multicst mac entry
165806db247cSraghuram 		 * point.
165906db247cSraghuram 		 */
166006db247cSraghuram 		D2(NULL, "%s: address 0x%llx", __func__, addr);
166106db247cSraghuram 		tgt = (void *)vswp;
166206db247cSraghuram 	}
166306db247cSraghuram 
166406db247cSraghuram 	WRITE_ENTER(&vswp->mfdbrw);
166506db247cSraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
166606db247cSraghuram 	    (mod_hash_val_t *)&ment) != 0) {
166706db247cSraghuram 
166806db247cSraghuram 		/* address not currently in table */
166906db247cSraghuram 		ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
167006db247cSraghuram 		ment->d_addr = (void *)tgt;
167106db247cSraghuram 		ment->d_type = devtype;
167206db247cSraghuram 		ment->nextp = NULL;
167306db247cSraghuram 
167406db247cSraghuram 		if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr,
167506db247cSraghuram 		    (mod_hash_val_t)ment) != 0) {
167606db247cSraghuram 			DERR(vswp, "%s: hash table insertion failed", __func__);
167706db247cSraghuram 			kmem_free(ment, sizeof (mfdb_ent_t));
167806db247cSraghuram 			rv = 1;
167906db247cSraghuram 		} else {
168006db247cSraghuram 			D2(vswp, "%s: added initial entry for 0x%llx to "
168106db247cSraghuram 			    "table", __func__, addr);
168206db247cSraghuram 		}
168306db247cSraghuram 	} else {
168406db247cSraghuram 		/*
168506db247cSraghuram 		 * Address in table. Check to see if specified port
168606db247cSraghuram 		 * is already associated with the address. If not add
168706db247cSraghuram 		 * it now.
168806db247cSraghuram 		 */
168906db247cSraghuram 		tmp_ent = ment;
169006db247cSraghuram 		while (tmp_ent != NULL) {
169106db247cSraghuram 			if (tmp_ent->d_addr == (void *)tgt) {
169206db247cSraghuram 				if (devtype == VSW_VNETPORT) {
169306db247cSraghuram 					DERR(vswp, "%s: duplicate port entry "
169406db247cSraghuram 					    "found for portid %ld and key "
169506db247cSraghuram 					    "0x%llx", __func__,
169606db247cSraghuram 					    ((vsw_port_t *)arg)->p_instance,
169706db247cSraghuram 					    addr);
169806db247cSraghuram 				} else {
169906db247cSraghuram 					DERR(vswp, "%s: duplicate entry found"
170006db247cSraghuram 					    "for key 0x%llx", __func__, addr);
170106db247cSraghuram 				}
170206db247cSraghuram 				rv = 1;
170306db247cSraghuram 				dup = 1;
170406db247cSraghuram 				break;
170506db247cSraghuram 			}
170606db247cSraghuram 			tmp_ent = tmp_ent->nextp;
170706db247cSraghuram 		}
170806db247cSraghuram 
170906db247cSraghuram 		/*
171006db247cSraghuram 		 * Port not on list so add it to end now.
171106db247cSraghuram 		 */
171206db247cSraghuram 		if (0 == dup) {
171306db247cSraghuram 			D2(vswp, "%s: added entry for 0x%llx to table",
171406db247cSraghuram 			    __func__, addr);
171506db247cSraghuram 			new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP);
171606db247cSraghuram 			new_ent->d_addr = (void *)tgt;
171706db247cSraghuram 			new_ent->d_type = devtype;
171806db247cSraghuram 			new_ent->nextp = NULL;
171906db247cSraghuram 
172006db247cSraghuram 			tmp_ent = ment;
172106db247cSraghuram 			while (tmp_ent->nextp != NULL)
172206db247cSraghuram 				tmp_ent = tmp_ent->nextp;
172306db247cSraghuram 
172406db247cSraghuram 			tmp_ent->nextp = new_ent;
172506db247cSraghuram 		}
172606db247cSraghuram 	}
172706db247cSraghuram 
172806db247cSraghuram 	RW_EXIT(&vswp->mfdbrw);
172906db247cSraghuram 	return (rv);
173006db247cSraghuram }
173106db247cSraghuram 
173206db247cSraghuram /*
173306db247cSraghuram  * Remove a multicast entry from the hashtable.
173406db247cSraghuram  *
173506db247cSraghuram  * Search hash table based on address. If match found, scan
173606db247cSraghuram  * list of ports associated with address. If specified port
173706db247cSraghuram  * found remove it from list.
173806db247cSraghuram  */
173906db247cSraghuram int
vsw_del_mcst(vsw_t * vswp,uint8_t devtype,uint64_t addr,void * arg)174006db247cSraghuram vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg)
174106db247cSraghuram {
174206db247cSraghuram 	mfdb_ent_t	*ment = NULL;
174306db247cSraghuram 	mfdb_ent_t	*curr_p, *prev_p;
174406db247cSraghuram 	void		*tgt = NULL;
174506db247cSraghuram 
174606db247cSraghuram 	D1(vswp, "%s: enter", __func__);
174706db247cSraghuram 
174806db247cSraghuram 	if (devtype == VSW_VNETPORT) {
174906db247cSraghuram 		tgt = (vsw_port_t *)arg;
175006db247cSraghuram 		D2(vswp, "%s: removing port %d from mFDB for address"
175106db247cSraghuram 		    " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, addr);
175206db247cSraghuram 	} else {
175306db247cSraghuram 		D2(vswp, "%s: removing entry", __func__);
175406db247cSraghuram 		tgt = (void *)vswp;
175506db247cSraghuram 	}
175606db247cSraghuram 
175706db247cSraghuram 	WRITE_ENTER(&vswp->mfdbrw);
175806db247cSraghuram 	if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr,
175906db247cSraghuram 	    (mod_hash_val_t *)&ment) != 0) {
176006db247cSraghuram 		D2(vswp, "%s: address 0x%llx not in table", __func__, addr);
176106db247cSraghuram 		RW_EXIT(&vswp->mfdbrw);
176206db247cSraghuram 		return (1);
176306db247cSraghuram 	}
176406db247cSraghuram 
176506db247cSraghuram 	prev_p = curr_p = ment;
176606db247cSraghuram 
176706db247cSraghuram 	while (curr_p != NULL) {
176806db247cSraghuram 		if (curr_p->d_addr == (void *)tgt) {
176906db247cSraghuram 			if (devtype == VSW_VNETPORT) {
177006db247cSraghuram 				D2(vswp, "%s: port %d found", __func__,
177106db247cSraghuram 				    ((vsw_port_t *)tgt)->p_instance);
177206db247cSraghuram 			} else {
177306db247cSraghuram 				D2(vswp, "%s: instance found", __func__);
177406db247cSraghuram 			}
177506db247cSraghuram 
177606db247cSraghuram 			if (prev_p == curr_p) {
177706db247cSraghuram 				/*
177806db247cSraghuram 				 * head of list, if no other element is in
177906db247cSraghuram 				 * list then destroy this entry, otherwise
178006db247cSraghuram 				 * just replace it with updated value.
178106db247cSraghuram 				 */
178206db247cSraghuram 				ment = curr_p->nextp;
178306db247cSraghuram 				if (ment == NULL) {
178406db247cSraghuram 					(void) mod_hash_destroy(vswp->mfdb,
178506db247cSraghuram 					    (mod_hash_val_t)addr);
178606db247cSraghuram 				} else {
178706db247cSraghuram 					(void) mod_hash_replace(vswp->mfdb,
178806db247cSraghuram 					    (mod_hash_key_t)addr,
178906db247cSraghuram 					    (mod_hash_val_t)ment);
179006db247cSraghuram 				}
179106db247cSraghuram 			} else {
179206db247cSraghuram 				/*
179306db247cSraghuram 				 * Not head of list, no need to do
179406db247cSraghuram 				 * replacement, just adjust list pointers.
179506db247cSraghuram 				 */
179606db247cSraghuram 				prev_p->nextp = curr_p->nextp;
179706db247cSraghuram 			}
179806db247cSraghuram 			break;
179906db247cSraghuram 		}
180006db247cSraghuram 
180106db247cSraghuram 		prev_p = curr_p;
180206db247cSraghuram 		curr_p = curr_p->nextp;
180306db247cSraghuram 	}
180406db247cSraghuram 
180506db247cSraghuram 	RW_EXIT(&vswp->mfdbrw);
180606db247cSraghuram 
180706db247cSraghuram 	D1(vswp, "%s: exit", __func__);
180806db247cSraghuram 
180906db247cSraghuram 	if (curr_p == NULL)
181006db247cSraghuram 		return (1);
181106db247cSraghuram 	kmem_free(curr_p, sizeof (mfdb_ent_t));
181206db247cSraghuram 	return (0);
181306db247cSraghuram }
181406db247cSraghuram 
181506db247cSraghuram /*
181606db247cSraghuram  * Port is being deleted, but has registered an interest in one
181706db247cSraghuram  * or more multicast groups. Using the list of addresses maintained
181806db247cSraghuram  * within the port structure find the appropriate entry in the hash
181906db247cSraghuram  * table and remove this port from the list of interested ports.
182006db247cSraghuram  */
182106db247cSraghuram void
vsw_del_mcst_port(vsw_port_t * port)182206db247cSraghuram vsw_del_mcst_port(vsw_port_t *port)
182306db247cSraghuram {
182406db247cSraghuram 	mcst_addr_t	*mcap = NULL;
182506db247cSraghuram 	vsw_t		*vswp = port->p_vswp;
182606db247cSraghuram 
182706db247cSraghuram 	D1(vswp, "%s: enter", __func__);
182806db247cSraghuram 
182906db247cSraghuram 	mutex_enter(&port->mca_lock);
183006db247cSraghuram 
183106db247cSraghuram 	while ((mcap = port->mcap) != NULL) {
183206db247cSraghuram 
183306db247cSraghuram 		port->mcap = mcap->nextp;
183406db247cSraghuram 
183506db247cSraghuram 		mutex_exit(&port->mca_lock);
183606db247cSraghuram 
183706db247cSraghuram 		(void) vsw_del_mcst(vswp, VSW_VNETPORT,
183806db247cSraghuram 		    mcap->addr, port);
183906db247cSraghuram 
184006db247cSraghuram 		/*
184106db247cSraghuram 		 * Remove the address from HW. The address
184206db247cSraghuram 		 * will actually only be removed once the ref
184306db247cSraghuram 		 * count within the MAC layer has dropped to
184406db247cSraghuram 		 * zero. I.e. we can safely call this fn even
184506db247cSraghuram 		 * if other ports are interested in this
184606db247cSraghuram 		 * address.
184706db247cSraghuram 		 */
1848da14cebeSEric Cheng 		vsw_mac_multicast_remove(vswp, port, mcap, VSW_VNETPORT);
184906db247cSraghuram 		kmem_free(mcap, sizeof (*mcap));
185006db247cSraghuram 
185106db247cSraghuram 		mutex_enter(&port->mca_lock);
185206db247cSraghuram 
185306db247cSraghuram 	}
185406db247cSraghuram 
185506db247cSraghuram 	mutex_exit(&port->mca_lock);
185606db247cSraghuram 
185706db247cSraghuram 	D1(vswp, "%s: exit", __func__);
185806db247cSraghuram }
185906db247cSraghuram 
186006db247cSraghuram /*
186106db247cSraghuram  * This vsw instance is detaching, but has registered an interest in one
186206db247cSraghuram  * or more multicast groups. Using the list of addresses maintained
186306db247cSraghuram  * within the vsw structure find the appropriate entry in the hash
186406db247cSraghuram  * table and remove this instance from the list of interested ports.
186506db247cSraghuram  */
186606db247cSraghuram void
vsw_del_mcst_vsw(vsw_t * vswp)186706db247cSraghuram vsw_del_mcst_vsw(vsw_t *vswp)
186806db247cSraghuram {
186906db247cSraghuram 	mcst_addr_t	*next_p = NULL;
187006db247cSraghuram 
187106db247cSraghuram 	D1(vswp, "%s: enter", __func__);
187206db247cSraghuram 
187306db247cSraghuram 	mutex_enter(&vswp->mca_lock);
187406db247cSraghuram 
187506db247cSraghuram 	while (vswp->mcap != NULL) {
187606db247cSraghuram 		DERR(vswp, "%s: deleting addr 0x%llx",
187706db247cSraghuram 		    __func__, vswp->mcap->addr);
187806db247cSraghuram 		(void) vsw_del_mcst(vswp, VSW_LOCALDEV, vswp->mcap->addr, NULL);
187906db247cSraghuram 
188006db247cSraghuram 		next_p = vswp->mcap->nextp;
188106db247cSraghuram 		kmem_free(vswp->mcap, sizeof (mcst_addr_t));
188206db247cSraghuram 		vswp->mcap = next_p;
188306db247cSraghuram 	}
188406db247cSraghuram 
188506db247cSraghuram 	vswp->mcap = NULL;
188606db247cSraghuram 	mutex_exit(&vswp->mca_lock);
188706db247cSraghuram 
188806db247cSraghuram 	D1(vswp, "%s: exit", __func__);
188906db247cSraghuram }
189006db247cSraghuram 
1891da14cebeSEric Cheng mblk_t *
vsw_get_same_dest_list(struct ether_header * ehp,mblk_t ** mpp)1892da14cebeSEric Cheng vsw_get_same_dest_list(struct ether_header *ehp, mblk_t **mpp)
189306db247cSraghuram {
189406db247cSraghuram 	mblk_t			*bp;
189506db247cSraghuram 	mblk_t			*nbp;
189606db247cSraghuram 	mblk_t			*head = NULL;
189706db247cSraghuram 	mblk_t			*tail = NULL;
189806db247cSraghuram 	mblk_t			*prev = NULL;
189906db247cSraghuram 	struct ether_header	*behp;
190006db247cSraghuram 
190106db247cSraghuram 	/* process the chain of packets */
190206db247cSraghuram 	bp = *mpp;
190306db247cSraghuram 	while (bp) {
190406db247cSraghuram 		nbp = bp->b_next;
190506db247cSraghuram 		behp = (struct ether_header *)bp->b_rptr;
190606db247cSraghuram 		bp->b_prev = NULL;
190706db247cSraghuram 		if (ether_cmp(&ehp->ether_dhost, &behp->ether_dhost) == 0) {
190806db247cSraghuram 			if (prev == NULL) {
190906db247cSraghuram 				*mpp = nbp;
191006db247cSraghuram 			} else {
191106db247cSraghuram 				prev->b_next = nbp;
191206db247cSraghuram 			}
191306db247cSraghuram 			bp->b_next =  NULL;
191406db247cSraghuram 			if (head == NULL) {
191506db247cSraghuram 				head = tail = bp;
191606db247cSraghuram 			} else {
191706db247cSraghuram 				tail->b_next = bp;
191806db247cSraghuram 				tail = bp;
191906db247cSraghuram 			}
192006db247cSraghuram 		} else {
192106db247cSraghuram 			prev = bp;
192206db247cSraghuram 		}
192306db247cSraghuram 		bp = nbp;
192406db247cSraghuram 	}
1925da14cebeSEric Cheng 	return (head);
192606db247cSraghuram }
192706db247cSraghuram 
192806db247cSraghuram static mblk_t *
vsw_dupmsgchain(mblk_t * mp)192906db247cSraghuram vsw_dupmsgchain(mblk_t *mp)
193006db247cSraghuram {
193106db247cSraghuram 	mblk_t	*nmp = NULL;
193206db247cSraghuram 	mblk_t	**nmpp = &nmp;
193306db247cSraghuram 
193406db247cSraghuram 	for (; mp != NULL; mp = mp->b_next) {
193506db247cSraghuram 		if ((*nmpp = dupmsg(mp)) == NULL) {
193606db247cSraghuram 			freemsgchain(nmp);
193706db247cSraghuram 			return (NULL);
193806db247cSraghuram 		}
193906db247cSraghuram 
194006db247cSraghuram 		nmpp = &((*nmpp)->b_next);
194106db247cSraghuram 	}
194206db247cSraghuram 
194306db247cSraghuram 	return (nmp);
194406db247cSraghuram }
1945