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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 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 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 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 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 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 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 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 * 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 * 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