18275SEric Cheng /* 28275SEric Cheng * CDDL HEADER START 38275SEric Cheng * 48275SEric Cheng * The contents of this file are subject to the terms of the 58275SEric Cheng * Common Development and Distribution License (the "License"). 68275SEric Cheng * You may not use this file except in compliance with the License. 78275SEric Cheng * 88275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98275SEric Cheng * or http://www.opensolaris.org/os/licensing. 108275SEric Cheng * See the License for the specific language governing permissions 118275SEric Cheng * and limitations under the License. 128275SEric Cheng * 138275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 148275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the 168275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 178275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 188275SEric Cheng * 198275SEric Cheng * CDDL HEADER END 208275SEric Cheng */ 218275SEric Cheng /* 228558SGirish.Moodalbail@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238275SEric Cheng * Use is subject to license terms. 248275SEric Cheng */ 258275SEric Cheng 268275SEric Cheng #include <sys/types.h> 278275SEric Cheng #include <sys/sysmacros.h> 288275SEric Cheng #include <sys/conf.h> 298275SEric Cheng #include <sys/cmn_err.h> 308275SEric Cheng #include <sys/list.h> 318275SEric Cheng #include <sys/kmem.h> 328275SEric Cheng #include <sys/stream.h> 338275SEric Cheng #include <sys/modctl.h> 348275SEric Cheng #include <sys/ddi.h> 358275SEric Cheng #include <sys/sunddi.h> 368275SEric Cheng #include <sys/atomic.h> 378275SEric Cheng #include <sys/stat.h> 388275SEric Cheng #include <sys/modhash.h> 398275SEric Cheng #include <sys/strsubr.h> 408275SEric Cheng #include <sys/strsun.h> 418275SEric Cheng #include <sys/sdt.h> 428275SEric Cheng #include <sys/mac.h> 438275SEric Cheng #include <sys/mac_impl.h> 448275SEric Cheng #include <sys/mac_client_impl.h> 458275SEric Cheng #include <sys/mac_client_priv.h> 468275SEric Cheng #include <sys/mac_flow_impl.h> 478275SEric Cheng 488275SEric Cheng /* 498275SEric Cheng * Broadcast and multicast traffic must be distributed to the MAC clients 508275SEric Cheng * that are defined on top of the same MAC. The set of 518275SEric Cheng * destinations to which a multicast packet must be sent is a subset 528275SEric Cheng * of all MAC clients defined on top of the MAC. A MAC client can be member 538275SEric Cheng * of more than one such subset. 548275SEric Cheng * 558275SEric Cheng * To accomodate these requirements, we introduce broadcast groups. 568275SEric Cheng * A broadcast group is associated with a broadcast or multicast 578275SEric Cheng * address. The members of a broadcast group consist of the MAC clients 588275SEric Cheng * that should received copies of packets sent to the address 598275SEric Cheng * associated with the group, and are defined on top of the 608275SEric Cheng * same MAC. 618275SEric Cheng * 628275SEric Cheng * The broadcast groups defined on top of a MAC are chained, 638275SEric Cheng * hanging off the mac_impl_t. The broadcast group id's are 648275SEric Cheng * unique globally (tracked by mac_bcast_id). 658275SEric Cheng */ 668275SEric Cheng 678275SEric Cheng /* 688275SEric Cheng * The same MAC client may be added for different <addr,vid> tuple, 698275SEric Cheng * we maintain a ref count for the number of times it has been added 708275SEric Cheng * to account for deleting the MAC client from the group. 718275SEric Cheng */ 728275SEric Cheng typedef struct mac_bcast_grp_mcip_s { 738275SEric Cheng mac_client_impl_t *mgb_client; 748275SEric Cheng int mgb_client_ref; 758275SEric Cheng } mac_bcast_grp_mcip_t; 768275SEric Cheng 778275SEric Cheng typedef struct mac_bcast_grp_s { /* Protected by */ 788275SEric Cheng struct mac_bcast_grp_s *mbg_next; /* SL */ 798275SEric Cheng void *mbg_addr; /* SL */ 808275SEric Cheng uint16_t mbg_vid; /* SL */ 818275SEric Cheng mac_impl_t *mbg_mac_impl; /* WO */ 828275SEric Cheng mac_addrtype_t mbg_addrtype; /* WO */ 838275SEric Cheng flow_entry_t *mbg_flow_ent; /* WO */ 848275SEric Cheng mac_bcast_grp_mcip_t *mbg_clients; /* mi_rw_lock */ 858275SEric Cheng uint_t mbg_nclients; /* mi_rw_lock */ 868275SEric Cheng uint_t mbg_nclients_alloc; /* SL */ 878275SEric Cheng uint64_t mbg_clients_gen; /* mi_rw_lock */ 888275SEric Cheng uint32_t mbg_id; /* atomic */ 898275SEric Cheng } mac_bcast_grp_t; 908275SEric Cheng 918275SEric Cheng static kmem_cache_t *mac_bcast_grp_cache; 928275SEric Cheng static uint32_t mac_bcast_id = 0; 938275SEric Cheng 948275SEric Cheng void 958275SEric Cheng mac_bcast_init(void) 968275SEric Cheng { 978275SEric Cheng mac_bcast_grp_cache = kmem_cache_create("mac_bcast_grp_cache", 988275SEric Cheng sizeof (mac_bcast_grp_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 998275SEric Cheng } 1008275SEric Cheng 1018275SEric Cheng void 1028275SEric Cheng mac_bcast_fini(void) 1038275SEric Cheng { 1048275SEric Cheng kmem_cache_destroy(mac_bcast_grp_cache); 1058275SEric Cheng } 1068275SEric Cheng 1078275SEric Cheng mac_impl_t * 1088275SEric Cheng mac_bcast_grp_mip(void *grp) 1098275SEric Cheng { 1108275SEric Cheng mac_bcast_grp_t *bcast_grp = grp; 1118275SEric Cheng 1128275SEric Cheng return (bcast_grp->mbg_mac_impl); 1138275SEric Cheng } 1148275SEric Cheng 1158275SEric Cheng /* 1168275SEric Cheng * Free the specific broadcast group. Invoked when the last reference 1178275SEric Cheng * to the group is released. 1188275SEric Cheng */ 1198275SEric Cheng void 1208275SEric Cheng mac_bcast_grp_free(void *bcast_grp) 1218275SEric Cheng { 1228275SEric Cheng mac_bcast_grp_t *grp = bcast_grp; 1238275SEric Cheng mac_impl_t *mip = grp->mbg_mac_impl; 1248275SEric Cheng 1258275SEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 1268275SEric Cheng 1278275SEric Cheng ASSERT(grp->mbg_addr != NULL); 1288275SEric Cheng kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length); 1298275SEric Cheng kmem_free(grp->mbg_clients, 1308275SEric Cheng grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t)); 1318275SEric Cheng mip->mi_bcast_ngrps--; 1328275SEric Cheng kmem_cache_free(mac_bcast_grp_cache, grp); 1338275SEric Cheng } 1348275SEric Cheng 1358275SEric Cheng /* 1368275SEric Cheng * arg1: broadcast group 1378275SEric Cheng * arg2: sender MAC client if it is being sent by a MAC client, 1388275SEric Cheng * NULL if it was received from the wire. 1398275SEric Cheng */ 1408275SEric Cheng void 1418275SEric Cheng mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback) 1428275SEric Cheng { 1438275SEric Cheng mac_bcast_grp_t *grp = arg1; 1448275SEric Cheng mac_client_impl_t *src_mcip = arg2, *dst_mcip; 1458275SEric Cheng mac_impl_t *mip = grp->mbg_mac_impl; 1468275SEric Cheng uint64_t gen; 1478275SEric Cheng uint_t i; 1488275SEric Cheng mblk_t *mp_chain1; 1498275SEric Cheng flow_entry_t *flent; 1508275SEric Cheng int err; 1518275SEric Cheng 1528275SEric Cheng rw_enter(&mip->mi_rw_lock, RW_READER); 1538275SEric Cheng 1548275SEric Cheng /* 1558275SEric Cheng * Pass a copy of the mp chain to every MAC client except the sender 1568275SEric Cheng * MAC client, if the packet was not received from the underlying NIC. 1578275SEric Cheng * 1588275SEric Cheng * The broadcast group lock should not be held across calls to 1598275SEric Cheng * the flow's callback function, since the same group could 1608275SEric Cheng * potentially be accessed from the same context. When the lock 1618275SEric Cheng * is reacquired, changes to the broadcast group while the lock 1628275SEric Cheng * was released are caught using a generation counter incremented 1638275SEric Cheng * each time the list of MAC clients associated with the broadcast 1648275SEric Cheng * group is changed. 1658275SEric Cheng */ 1668275SEric Cheng for (i = 0; i < grp->mbg_nclients_alloc; i++) { 1678275SEric Cheng dst_mcip = grp->mbg_clients[i].mgb_client; 1688275SEric Cheng if (dst_mcip == NULL) 1698275SEric Cheng continue; 1708275SEric Cheng flent = dst_mcip->mci_flent; 1718275SEric Cheng if (flent == NULL || dst_mcip == src_mcip) { 1728275SEric Cheng /* 1738275SEric Cheng * Don't send a copy of the packet back to 1748275SEric Cheng * its sender. 1758275SEric Cheng */ 1768275SEric Cheng continue; 1778275SEric Cheng } 1788275SEric Cheng 1798275SEric Cheng /* 1808275SEric Cheng * It is important to hold a reference on the 1818275SEric Cheng * flow_ent here. 1828275SEric Cheng */ 1838275SEric Cheng if ((mp_chain1 = mac_copymsgchain_cksum(mp_chain)) == NULL) 1848275SEric Cheng break; 1858275SEric Cheng /* 1868275SEric Cheng * Fix the checksum for packets originating 1878275SEric Cheng * from the local machine. 1888275SEric Cheng */ 1898275SEric Cheng if ((src_mcip != NULL) && 1908275SEric Cheng (mp_chain1 = mac_fix_cksum(mp_chain1)) == NULL) 1918275SEric Cheng break; 1928275SEric Cheng 1938275SEric Cheng FLOW_TRY_REFHOLD(flent, err); 1948275SEric Cheng if (err != 0) { 1958275SEric Cheng freemsgchain(mp_chain1); 1968275SEric Cheng continue; 1978275SEric Cheng } 1988275SEric Cheng 1998275SEric Cheng gen = grp->mbg_clients_gen; 2008275SEric Cheng 2018275SEric Cheng rw_exit(&mip->mi_rw_lock); 2028275SEric Cheng 2038275SEric Cheng DTRACE_PROBE4(mac__bcast__send__to, mac_client_impl_t *, 2048275SEric Cheng src_mcip, flow_fn_t, dst_mcip->mci_flent->fe_cb_fn, 2058275SEric Cheng void *, dst_mcip->mci_flent->fe_cb_arg1, 2068275SEric Cheng void *, dst_mcip->mci_flent->fe_cb_arg2); 2078275SEric Cheng 2088275SEric Cheng (dst_mcip->mci_flent->fe_cb_fn)(dst_mcip->mci_flent->fe_cb_arg1, 2098275SEric Cheng dst_mcip->mci_flent->fe_cb_arg2, mp_chain1, is_loopback); 2108275SEric Cheng FLOW_REFRELE(flent); 2118275SEric Cheng 2128275SEric Cheng rw_enter(&mip->mi_rw_lock, RW_READER); 2138275SEric Cheng 2148275SEric Cheng /* update stats */ 2158275SEric Cheng if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) 2168275SEric Cheng dst_mcip->mci_stat_multircv++; 2178275SEric Cheng else 2188275SEric Cheng dst_mcip->mci_stat_brdcstrcv++; 2198275SEric Cheng 2208275SEric Cheng if (grp->mbg_clients_gen != gen) { 2218275SEric Cheng /* 2228275SEric Cheng * The list of MAC clients associated with the group 2238275SEric Cheng * was changed while the lock was released. 2248275SEric Cheng * Give up on the current packet. 2258275SEric Cheng */ 2268275SEric Cheng rw_exit(&mip->mi_rw_lock); 2278275SEric Cheng freemsgchain(mp_chain); 2288275SEric Cheng return; 2298275SEric Cheng } 2308275SEric Cheng } 2318275SEric Cheng rw_exit(&mip->mi_rw_lock); 2328275SEric Cheng 2338275SEric Cheng if (src_mcip != NULL) { 2348275SEric Cheng /* 2358275SEric Cheng * The packet was sent from one of the MAC clients, 2368275SEric Cheng * so we need to send a copy of the packet to the 2378275SEric Cheng * underlying NIC so that it can be sent on the wire. 2388275SEric Cheng */ 2398275SEric Cheng mblk_t *rest; 2408275SEric Cheng 2418275SEric Cheng src_mcip->mci_stat_multixmt++; 2428275SEric Cheng src_mcip->mci_stat_brdcstxmt++; 2438275SEric Cheng 2448275SEric Cheng rest = MAC_RING_TX_DEFAULT(mip, mp_chain); 2458275SEric Cheng if (rest != NULL) 2468275SEric Cheng freemsgchain(rest); 2478275SEric Cheng } else { 2488275SEric Cheng freemsgchain(mp_chain); 2498275SEric Cheng } 2508275SEric Cheng } 2518275SEric Cheng 2528275SEric Cheng /* 2538275SEric Cheng * Add the specified MAC client to the group corresponding to the specified 2548275SEric Cheng * broadcast or multicast address. 2558275SEric Cheng * Return 0 on success, or an errno value on failure. 2568275SEric Cheng */ 2578275SEric Cheng int 2588275SEric Cheng mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid, 2598275SEric Cheng mac_addrtype_t addrtype) 2608275SEric Cheng { 2618275SEric Cheng mac_impl_t *mip = mcip->mci_mip; 2628275SEric Cheng mac_bcast_grp_t *grp = NULL, **last_grp; 2638275SEric Cheng size_t addr_len = mip->mi_type->mt_addr_length; 2648275SEric Cheng int rc = 0; 2658275SEric Cheng int i, index = -1; 266*8833SVenu.Iyer@Sun.COM mac_mcast_addrs_t **prev_mi_addr = NULL; 267*8833SVenu.Iyer@Sun.COM mac_mcast_addrs_t **prev_mci_addr = NULL; 2688275SEric Cheng 2698275SEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 2708275SEric Cheng 2718275SEric Cheng ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST || 2728275SEric Cheng addrtype == MAC_ADDRTYPE_BROADCAST); 2738275SEric Cheng 274*8833SVenu.Iyer@Sun.COM /* 275*8833SVenu.Iyer@Sun.COM * Add the MAC client to the list of MAC clients associated 276*8833SVenu.Iyer@Sun.COM * with the group. 277*8833SVenu.Iyer@Sun.COM */ 278*8833SVenu.Iyer@Sun.COM if (addrtype == MAC_ADDRTYPE_MULTICAST) { 279*8833SVenu.Iyer@Sun.COM mac_mcast_addrs_t *maddr; 280*8833SVenu.Iyer@Sun.COM 281*8833SVenu.Iyer@Sun.COM /* 282*8833SVenu.Iyer@Sun.COM * In case of a driver (say aggr), we need this information 283*8833SVenu.Iyer@Sun.COM * on a per MAC instance basis. 284*8833SVenu.Iyer@Sun.COM */ 285*8833SVenu.Iyer@Sun.COM prev_mi_addr = &mip->mi_mcast_addrs; 286*8833SVenu.Iyer@Sun.COM for (maddr = *prev_mi_addr; maddr != NULL; 287*8833SVenu.Iyer@Sun.COM prev_mi_addr = &maddr->mma_next, maddr = maddr->mma_next) { 288*8833SVenu.Iyer@Sun.COM if (bcmp(maddr->mma_addr, addr, addr_len) == 0) 289*8833SVenu.Iyer@Sun.COM break; 290*8833SVenu.Iyer@Sun.COM } 291*8833SVenu.Iyer@Sun.COM if (maddr == NULL) { 292*8833SVenu.Iyer@Sun.COM /* 293*8833SVenu.Iyer@Sun.COM * For multicast addresses, have the underlying MAC 294*8833SVenu.Iyer@Sun.COM * join the corresponding multicast group. 295*8833SVenu.Iyer@Sun.COM */ 296*8833SVenu.Iyer@Sun.COM rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr); 297*8833SVenu.Iyer@Sun.COM if (rc != 0) 298*8833SVenu.Iyer@Sun.COM return (rc); 299*8833SVenu.Iyer@Sun.COM maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 300*8833SVenu.Iyer@Sun.COM KM_SLEEP); 301*8833SVenu.Iyer@Sun.COM bcopy(addr, maddr->mma_addr, addr_len); 302*8833SVenu.Iyer@Sun.COM *prev_mi_addr = maddr; 303*8833SVenu.Iyer@Sun.COM } else { 304*8833SVenu.Iyer@Sun.COM prev_mi_addr = NULL; 305*8833SVenu.Iyer@Sun.COM } 306*8833SVenu.Iyer@Sun.COM maddr->mma_ref++; 307*8833SVenu.Iyer@Sun.COM 308*8833SVenu.Iyer@Sun.COM /* 309*8833SVenu.Iyer@Sun.COM * We maintain a separate list for each MAC client. Get 310*8833SVenu.Iyer@Sun.COM * the entry or add, if it is not present. 311*8833SVenu.Iyer@Sun.COM */ 312*8833SVenu.Iyer@Sun.COM prev_mci_addr = &mcip->mci_mcast_addrs; 313*8833SVenu.Iyer@Sun.COM for (maddr = *prev_mci_addr; maddr != NULL; 314*8833SVenu.Iyer@Sun.COM prev_mci_addr = &maddr->mma_next, maddr = maddr->mma_next) { 315*8833SVenu.Iyer@Sun.COM if (bcmp(maddr->mma_addr, addr, addr_len) == 0) 316*8833SVenu.Iyer@Sun.COM break; 317*8833SVenu.Iyer@Sun.COM } 318*8833SVenu.Iyer@Sun.COM if (maddr == NULL) { 319*8833SVenu.Iyer@Sun.COM maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), 320*8833SVenu.Iyer@Sun.COM KM_SLEEP); 321*8833SVenu.Iyer@Sun.COM bcopy(addr, maddr->mma_addr, addr_len); 322*8833SVenu.Iyer@Sun.COM *prev_mci_addr = maddr; 323*8833SVenu.Iyer@Sun.COM } else { 324*8833SVenu.Iyer@Sun.COM prev_mci_addr = NULL; 325*8833SVenu.Iyer@Sun.COM } 326*8833SVenu.Iyer@Sun.COM maddr->mma_ref++; 327*8833SVenu.Iyer@Sun.COM } 328*8833SVenu.Iyer@Sun.COM 3298275SEric Cheng /* The list is protected by the perimeter */ 3308275SEric Cheng last_grp = &mip->mi_bcast_grp; 3318275SEric Cheng for (grp = *last_grp; grp != NULL; 3328275SEric Cheng last_grp = &grp->mbg_next, grp = grp->mbg_next) { 3338275SEric Cheng if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && 3348275SEric Cheng grp->mbg_vid == vid) 3358275SEric Cheng break; 3368275SEric Cheng } 3378275SEric Cheng 3388275SEric Cheng if (grp == NULL) { 3398275SEric Cheng /* 3408275SEric Cheng * The group does not yet exist, create it. 3418275SEric Cheng */ 3428275SEric Cheng flow_desc_t flow_desc; 3438558SGirish.Moodalbail@Sun.COM char flow_name[MAXFLOWNAMELEN]; 3448275SEric Cheng 3458275SEric Cheng grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP); 3468275SEric Cheng bzero(grp, sizeof (mac_bcast_grp_t)); 3478275SEric Cheng grp->mbg_next = NULL; 3488275SEric Cheng grp->mbg_mac_impl = mip; 3498275SEric Cheng 3508275SEric Cheng DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *, 3518275SEric Cheng grp); 3528275SEric Cheng 3538275SEric Cheng grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP); 3548275SEric Cheng bcopy(addr, grp->mbg_addr, addr_len); 3558275SEric Cheng grp->mbg_addrtype = addrtype; 3568275SEric Cheng grp->mbg_vid = vid; 3578275SEric Cheng 3588275SEric Cheng /* 3598275SEric Cheng * Add a new flow to the underlying MAC. 3608275SEric Cheng */ 3618275SEric Cheng bzero(&flow_desc, sizeof (flow_desc)); 3628275SEric Cheng bcopy(addr, &flow_desc.fd_dst_mac, addr_len); 3638275SEric Cheng flow_desc.fd_mac_len = (uint32_t)addr_len; 3648275SEric Cheng 3658275SEric Cheng flow_desc.fd_mask = FLOW_LINK_DST; 3668275SEric Cheng if (vid != 0) { 3678275SEric Cheng flow_desc.fd_vid = vid; 3688275SEric Cheng flow_desc.fd_mask |= FLOW_LINK_VID; 3698275SEric Cheng } 3708275SEric Cheng 3718275SEric Cheng grp->mbg_id = atomic_add_32_nv(&mac_bcast_id, 1); 3728275SEric Cheng (void) sprintf(flow_name, 3738275SEric Cheng "mac/%s/mcast%d", mip->mi_name, grp->mbg_id); 3748275SEric Cheng 3758275SEric Cheng rc = mac_flow_create(&flow_desc, NULL, flow_name, 3768275SEric Cheng grp, FLOW_MCAST, &grp->mbg_flow_ent); 3778275SEric Cheng if (rc != 0) { 3788275SEric Cheng kmem_free(grp->mbg_addr, addr_len); 3798275SEric Cheng kmem_cache_free(mac_bcast_grp_cache, grp); 380*8833SVenu.Iyer@Sun.COM goto fail; 3818275SEric Cheng } 3828275SEric Cheng grp->mbg_flow_ent->fe_mbg = grp; 3838275SEric Cheng mip->mi_bcast_ngrps++; 3848275SEric Cheng 3858275SEric Cheng /* 3868275SEric Cheng * Initial creation reference on the flow. This is released 3878275SEric Cheng * in the corresponding delete action i_mac_bcast_delete() 3888275SEric Cheng */ 3898275SEric Cheng FLOW_REFHOLD(grp->mbg_flow_ent); 3908275SEric Cheng 3918275SEric Cheng /* 3928275SEric Cheng * When the multicast and broadcast packet is received 3938275SEric Cheng * by the underlying NIC, mac_rx_classify() will invoke 3948275SEric Cheng * mac_bcast_send() with arg2=NULL, which will cause 3958275SEric Cheng * mac_bcast_send() to send a copy of the packet(s) 3968275SEric Cheng * to every MAC client opened on top of the underlying MAC. 3978275SEric Cheng * 3988275SEric Cheng * When the mac_bcast_send() function is invoked from 3998275SEric Cheng * the transmit path of a MAC client, it will specify the 4008275SEric Cheng * transmitting MAC client as the arg2 value, which will 4018275SEric Cheng * allow mac_bcast_send() to skip that MAC client and not 4028275SEric Cheng * send it a copy of the packet. 4038275SEric Cheng * 4048275SEric Cheng * We program the classifier to dispatch matching broadcast 4058275SEric Cheng * packets to mac_bcast_send(). 4068275SEric Cheng */ 4078275SEric Cheng 4088275SEric Cheng grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send; 4098275SEric Cheng grp->mbg_flow_ent->fe_cb_arg1 = grp; 4108275SEric Cheng grp->mbg_flow_ent->fe_cb_arg2 = NULL; 4118275SEric Cheng 4128275SEric Cheng rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent); 4138275SEric Cheng if (rc != 0) { 4148275SEric Cheng FLOW_FINAL_REFRELE(grp->mbg_flow_ent); 415*8833SVenu.Iyer@Sun.COM goto fail; 4168275SEric Cheng } 4178275SEric Cheng 4188275SEric Cheng *last_grp = grp; 4198275SEric Cheng } 4208275SEric Cheng 4218275SEric Cheng ASSERT(grp->mbg_addrtype == addrtype); 4228275SEric Cheng 4238275SEric Cheng /* 4248275SEric Cheng * Add the MAC client to the list of MAC clients associated 4258275SEric Cheng * with the group. 4268275SEric Cheng */ 4278275SEric Cheng rw_enter(&mip->mi_rw_lock, RW_WRITER); 4288275SEric Cheng for (i = 0; i < grp->mbg_nclients_alloc; i++) { 4298275SEric Cheng /* 4308275SEric Cheng * The MAC client was already added, say when we have 4318275SEric Cheng * different unicast addresses with the same vid. 4328275SEric Cheng * Just increment the ref and we are done. 4338275SEric Cheng */ 4348275SEric Cheng if (grp->mbg_clients[i].mgb_client == mcip) { 4358275SEric Cheng grp->mbg_clients[i].mgb_client_ref++; 436*8833SVenu.Iyer@Sun.COM rw_exit(&mip->mi_rw_lock); 437*8833SVenu.Iyer@Sun.COM return (0); 4388275SEric Cheng } else if (grp->mbg_clients[i].mgb_client == NULL && 4398275SEric Cheng index == -1) { 4408275SEric Cheng index = i; 4418275SEric Cheng } 4428275SEric Cheng } 4438275SEric Cheng if (grp->mbg_nclients_alloc == grp->mbg_nclients) { 4448275SEric Cheng mac_bcast_grp_mcip_t *new_clients; 4458275SEric Cheng uint_t new_size = grp->mbg_nclients+1; 4468275SEric Cheng 4478275SEric Cheng new_clients = kmem_zalloc(new_size * 4488275SEric Cheng sizeof (mac_bcast_grp_mcip_t), KM_SLEEP); 4498275SEric Cheng 4508275SEric Cheng if (grp->mbg_nclients > 0) { 4518275SEric Cheng ASSERT(grp->mbg_clients != NULL); 4528275SEric Cheng bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients * 4538275SEric Cheng sizeof (mac_bcast_grp_mcip_t)); 4548275SEric Cheng kmem_free(grp->mbg_clients, grp->mbg_nclients * 4558275SEric Cheng sizeof (mac_bcast_grp_mcip_t)); 4568275SEric Cheng } 4578275SEric Cheng 4588275SEric Cheng grp->mbg_clients = new_clients; 4598275SEric Cheng grp->mbg_nclients_alloc = new_size; 4608275SEric Cheng index = new_size - 1; 4618275SEric Cheng } 4628275SEric Cheng 4638275SEric Cheng ASSERT(index != -1); 4648275SEric Cheng grp->mbg_clients[index].mgb_client = mcip; 4658275SEric Cheng grp->mbg_clients[index].mgb_client_ref = 1; 4668275SEric Cheng grp->mbg_nclients++; 4678275SEric Cheng /* 4688275SEric Cheng * Since we're adding to the list of MAC clients using that group, 4698275SEric Cheng * kick the generation count, which will allow mac_bcast_send() 4708275SEric Cheng * to detect that condition after re-acquiring the lock. 4718275SEric Cheng */ 4728275SEric Cheng grp->mbg_clients_gen++; 4738275SEric Cheng rw_exit(&mip->mi_rw_lock); 474*8833SVenu.Iyer@Sun.COM return (0); 4758275SEric Cheng 476*8833SVenu.Iyer@Sun.COM fail: 477*8833SVenu.Iyer@Sun.COM if (prev_mi_addr != NULL) { 478*8833SVenu.Iyer@Sun.COM kmem_free(*prev_mi_addr, sizeof (mac_mcast_addrs_t)); 479*8833SVenu.Iyer@Sun.COM *prev_mi_addr = NULL; 480*8833SVenu.Iyer@Sun.COM (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); 481*8833SVenu.Iyer@Sun.COM } 482*8833SVenu.Iyer@Sun.COM if (prev_mci_addr != NULL) { 483*8833SVenu.Iyer@Sun.COM kmem_free(*prev_mci_addr, sizeof (mac_mcast_addrs_t)); 484*8833SVenu.Iyer@Sun.COM *prev_mci_addr = NULL; 485*8833SVenu.Iyer@Sun.COM } 486*8833SVenu.Iyer@Sun.COM return (rc); 4878275SEric Cheng } 4888275SEric Cheng 4898275SEric Cheng /* 4908275SEric Cheng * Remove the specified MAC client from the group corresponding to 4918275SEric Cheng * the specific broadcast or multicast address. 4928275SEric Cheng * 4938275SEric Cheng * Note: mac_bcast_delete() calls mac_remove_flow() which 4948275SEric Cheng * will call cv_wait for fe_refcnt to drop to 0. So this function 4958275SEric Cheng * should not be called from interrupt or STREAMS context. 4968275SEric Cheng */ 4978275SEric Cheng void 4988275SEric Cheng mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid) 4998275SEric Cheng { 5008275SEric Cheng mac_impl_t *mip = mcip->mci_mip; 5018275SEric Cheng mac_bcast_grp_t *grp = NULL, **prev; 5028275SEric Cheng size_t addr_len = mip->mi_type->mt_addr_length; 5038275SEric Cheng flow_entry_t *flent; 5048275SEric Cheng uint_t i; 5058275SEric Cheng mac_mcast_addrs_t *maddr = NULL; 5068275SEric Cheng mac_mcast_addrs_t **mprev; 5078275SEric Cheng 5088275SEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 5098275SEric Cheng 5108275SEric Cheng /* find the broadcast group. The list is protected by the perimeter */ 5118275SEric Cheng prev = &mip->mi_bcast_grp; 5128275SEric Cheng for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next, 5138275SEric Cheng grp = grp->mbg_next) { 5148275SEric Cheng if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && 5158275SEric Cheng grp->mbg_vid == vid) 5168275SEric Cheng break; 5178275SEric Cheng } 5188275SEric Cheng ASSERT(grp != NULL); 5198275SEric Cheng 5208275SEric Cheng /* 5218275SEric Cheng * Remove the MAC client from the list of MAC clients associated 5228275SEric Cheng * with that broadcast group. 5238275SEric Cheng * 5248275SEric Cheng * We mark the mbg_clients[] location corresponding to the removed MAC 5258275SEric Cheng * client NULL and reuse that location when we add a new MAC client. 5268275SEric Cheng */ 5278275SEric Cheng 5288275SEric Cheng rw_enter(&mip->mi_rw_lock, RW_WRITER); 5298275SEric Cheng 5308275SEric Cheng for (i = 0; i < grp->mbg_nclients_alloc; i++) { 5318275SEric Cheng if (grp->mbg_clients[i].mgb_client == mcip) 5328275SEric Cheng break; 5338275SEric Cheng } 5348275SEric Cheng 5358275SEric Cheng ASSERT(i < grp->mbg_nclients_alloc); 5368275SEric Cheng /* 5378275SEric Cheng * If there are more references to this MAC client, then we let 5388275SEric Cheng * it remain till it goes to 0. 5398275SEric Cheng */ 5408275SEric Cheng if (--grp->mbg_clients[i].mgb_client_ref > 0) 5418275SEric Cheng goto update_maddr; 5428275SEric Cheng 5438275SEric Cheng grp->mbg_clients[i].mgb_client = NULL; 5448275SEric Cheng grp->mbg_clients[i].mgb_client_ref = 0; 5458275SEric Cheng 5468275SEric Cheng /* 5478275SEric Cheng * Since we're removing from the list of MAC clients using that group, 5488275SEric Cheng * kick the generation count, which will allow mac_bcast_send() 5498275SEric Cheng * to detect that condition. 5508275SEric Cheng */ 5518275SEric Cheng grp->mbg_clients_gen++; 5528275SEric Cheng 5538275SEric Cheng if (--grp->mbg_nclients == 0) { 5548275SEric Cheng /* 5558275SEric Cheng * The last MAC client of the group was just removed. 5568275SEric Cheng * Unlink the current group from the list of groups 5578275SEric Cheng * defined on top of the underlying NIC. The group 5588275SEric Cheng * structure will stay around until the last reference 5598275SEric Cheng * is dropped. 5608275SEric Cheng */ 5618275SEric Cheng *prev = grp->mbg_next; 5628275SEric Cheng } 5638275SEric Cheng update_maddr: 564*8833SVenu.Iyer@Sun.COM rw_exit(&mip->mi_rw_lock); 565*8833SVenu.Iyer@Sun.COM 5668275SEric Cheng if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) { 5678275SEric Cheng mprev = &mcip->mci_mcast_addrs; 5688275SEric Cheng for (maddr = mcip->mci_mcast_addrs; maddr != NULL; 5698275SEric Cheng mprev = &maddr->mma_next, maddr = maddr->mma_next) { 5708275SEric Cheng if (bcmp(grp->mbg_addr, maddr->mma_addr, 5718275SEric Cheng mip->mi_type->mt_addr_length) == 0) 5728275SEric Cheng break; 5738275SEric Cheng } 5748275SEric Cheng ASSERT(maddr != NULL); 5758275SEric Cheng if (--maddr->mma_ref == 0) { 5768275SEric Cheng *mprev = maddr->mma_next; 5778275SEric Cheng maddr->mma_next = NULL; 5788275SEric Cheng kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 5798275SEric Cheng } 5808275SEric Cheng 5818275SEric Cheng mprev = &mip->mi_mcast_addrs; 5828275SEric Cheng for (maddr = mip->mi_mcast_addrs; maddr != NULL; 5838275SEric Cheng mprev = &maddr->mma_next, maddr = maddr->mma_next) { 5848275SEric Cheng if (bcmp(grp->mbg_addr, maddr->mma_addr, 5858275SEric Cheng mip->mi_type->mt_addr_length) == 0) 5868275SEric Cheng break; 5878275SEric Cheng } 5888275SEric Cheng ASSERT(maddr != NULL); 5898275SEric Cheng if (--maddr->mma_ref == 0) { 590*8833SVenu.Iyer@Sun.COM (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); 5918275SEric Cheng *mprev = maddr->mma_next; 5928275SEric Cheng maddr->mma_next = NULL; 5938275SEric Cheng kmem_free(maddr, sizeof (mac_mcast_addrs_t)); 5948275SEric Cheng } 5958275SEric Cheng } 5968275SEric Cheng 5978275SEric Cheng /* 5988275SEric Cheng * If the group itself is being removed, remove the 5998275SEric Cheng * corresponding flow from the underlying NIC. 6008275SEric Cheng */ 6018275SEric Cheng flent = grp->mbg_flow_ent; 6028275SEric Cheng if (grp->mbg_nclients == 0) { 6038275SEric Cheng mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE); 6048275SEric Cheng mac_flow_wait(flent, FLOW_DRIVER_UPCALL); 6058275SEric Cheng FLOW_FINAL_REFRELE(flent); 6068275SEric Cheng } 6078275SEric Cheng } 6088275SEric Cheng 6098275SEric Cheng /* 6108275SEric Cheng * This will be called by a driver, such as aggr, when a port is added/removed 6118275SEric Cheng * to add/remove the port to/from all the multcast addresses for that aggr. 6128275SEric Cheng */ 6138275SEric Cheng void 6148275SEric Cheng mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg, 6158275SEric Cheng boolean_t add) 6168275SEric Cheng { 6178275SEric Cheng mac_mcast_addrs_t *grp, *next; 6188275SEric Cheng 6198275SEric Cheng ASSERT(refresh_fn != NULL); 6208275SEric Cheng 6218275SEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 6228275SEric Cheng 6238275SEric Cheng /* 6248275SEric Cheng * Walk the multicast address list and call the refresh function for 6258275SEric Cheng * each address. 6268275SEric Cheng */ 6278275SEric Cheng 6288275SEric Cheng for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) { 6298275SEric Cheng /* 6308275SEric Cheng * Save the next pointer just in case the refresh 6318275SEric Cheng * function's action causes the group entry to be 6328275SEric Cheng * freed. 6338275SEric Cheng * We won't be adding to this list as part of the 6348275SEric Cheng * refresh. 6358275SEric Cheng */ 6368275SEric Cheng next = grp->mma_next; 6378275SEric Cheng refresh_fn(arg, add, grp->mma_addr); 6388275SEric Cheng } 6398275SEric Cheng } 6408275SEric Cheng 6418275SEric Cheng /* 6428275SEric Cheng * Walk the MAC client's multicast address list and add/remove the addr/vid 6438275SEric Cheng * ('arg' is 'flent') to all the addresses. 6448275SEric Cheng */ 6458275SEric Cheng void 6468275SEric Cheng mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn, 6478275SEric Cheng void *arg, boolean_t add) 6488275SEric Cheng { 6498275SEric Cheng mac_mcast_addrs_t *grp, *next; 6508275SEric Cheng mac_impl_t *mip = mcip->mci_mip; 6518275SEric Cheng 6528275SEric Cheng ASSERT(refresh_fn != NULL); 6538275SEric Cheng 6548275SEric Cheng ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 6558275SEric Cheng /* 6568275SEric Cheng * Walk the multicast address list and call the refresh function for 6578275SEric Cheng * each address. 6588275SEric Cheng * Broadcast addresses are not added or removed through the multicast 6598275SEric Cheng * entry points, so don't include them as part of the refresh. 6608275SEric Cheng */ 6618275SEric Cheng for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) { 6628275SEric Cheng /* 6638275SEric Cheng * Save the next pointer just in case the refresh 6648275SEric Cheng * function's action causes the group entry to be 6658275SEric Cheng * freed. 6668275SEric Cheng * We won't be adding to this list as part of the 6678275SEric Cheng * refresh. 6688275SEric Cheng */ 6698275SEric Cheng next = grp->mma_next; 6708275SEric Cheng refresh_fn(arg, add, grp->mma_addr); 6718275SEric Cheng } 6728275SEric Cheng } 673