17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 56b6515e2Sericheng * Common Development and Distribution License (the "License"). 66b6515e2Sericheng * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22ae6aa22aSVenugopal Iyer * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * Data-Link Services Module 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 31da14cebeSEric Cheng #include <sys/strsubr.h> 32da14cebeSEric Cheng #include <sys/strsun.h> 337c478bd9Sstevel@tonic-gate #include <sys/vlan.h> 347c478bd9Sstevel@tonic-gate #include <sys/dld_impl.h> 35da14cebeSEric Cheng #include <sys/sdt.h> 36da14cebeSEric Cheng #include <sys/atomic.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate static kmem_cache_t *i_dls_link_cachep; 39ae6aa22aSVenugopal Iyer mod_hash_t *i_dls_link_hash; 40210db224Sericheng static uint_t i_dls_link_count; 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate #define LINK_HASHSZ 67 /* prime */ 437c478bd9Sstevel@tonic-gate #define IMPL_HASHSZ 67 /* prime */ 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate /* 467c478bd9Sstevel@tonic-gate * Construct a hash key encompassing both DLSAP value and VLAN idenitifier. 477c478bd9Sstevel@tonic-gate */ 48da14cebeSEric Cheng #define MAKE_KEY(_sap) \ 49da14cebeSEric Cheng ((mod_hash_key_t)(uintptr_t)((_sap) << VLAN_ID_SIZE)) 507c478bd9Sstevel@tonic-gate 51ba2e4443Sseb #define DLS_STRIP_PADDING(pktsize, p) { \ 52ba2e4443Sseb if (pktsize != 0) { \ 53ba2e4443Sseb ssize_t delta = pktsize - msgdsize(p); \ 54ba2e4443Sseb \ 55ba2e4443Sseb if (delta < 0) \ 56ba2e4443Sseb (void) adjmsg(p, delta); \ 57ba2e4443Sseb } \ 58ba2e4443Sseb } 59ba2e4443Sseb 607c478bd9Sstevel@tonic-gate /* 617c478bd9Sstevel@tonic-gate * Private functions. 627c478bd9Sstevel@tonic-gate */ 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 657c478bd9Sstevel@tonic-gate static int 667c478bd9Sstevel@tonic-gate i_dls_link_constructor(void *buf, void *arg, int kmflag) 677c478bd9Sstevel@tonic-gate { 687c478bd9Sstevel@tonic-gate dls_link_t *dlp = buf; 697c478bd9Sstevel@tonic-gate char name[MAXNAMELEN]; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate bzero(buf, sizeof (dls_link_t)); 727c478bd9Sstevel@tonic-gate 73d62bc4baSyz147064 (void) snprintf(name, MAXNAMELEN, "dls_link_t_%p_hash", buf); 74da14cebeSEric Cheng dlp->dl_str_hash = mod_hash_create_idhash(name, IMPL_HASHSZ, 75210db224Sericheng mod_hash_null_valdtor); 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate return (0); 787c478bd9Sstevel@tonic-gate } 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 817c478bd9Sstevel@tonic-gate static void 827c478bd9Sstevel@tonic-gate i_dls_link_destructor(void *buf, void *arg) 837c478bd9Sstevel@tonic-gate { 847c478bd9Sstevel@tonic-gate dls_link_t *dlp = buf; 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate ASSERT(dlp->dl_ref == 0); 877c478bd9Sstevel@tonic-gate ASSERT(dlp->dl_mh == NULL); 88da14cebeSEric Cheng ASSERT(dlp->dl_mah == NULL); 897c478bd9Sstevel@tonic-gate ASSERT(dlp->dl_unknowns == 0); 907c478bd9Sstevel@tonic-gate 91da14cebeSEric Cheng mod_hash_destroy_idhash(dlp->dl_str_hash); 92da14cebeSEric Cheng dlp->dl_str_hash = NULL; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate } 957c478bd9Sstevel@tonic-gate 96ba2e4443Sseb /* 97605445d5Sdg199075 * - Parse the mac header information of the given packet. 98605445d5Sdg199075 * - Strip the padding and skip over the header. Note that because some 99605445d5Sdg199075 * DLS consumers only check the db_ref count of the first mblk, we 1006f45d2aeSyz147064 * pullup the message into a single mblk. Because the original message 10125ec3e3dSEric Cheng * is freed as the result of message pulling up, mac_vlan_header_info() 1026f45d2aeSyz147064 * is called again to update the mhi_saddr and mhi_daddr pointers in the 10325ec3e3dSEric Cheng * mhip. Further, the mac_vlan_header_info() function ensures that the 1046f45d2aeSyz147064 * size of the pulled message is greater than the MAC header size, 1056f45d2aeSyz147064 * therefore we can directly advance b_rptr to point at the payload. 106605445d5Sdg199075 * 107605445d5Sdg199075 * We choose to use a macro for performance reasons. 108605445d5Sdg199075 */ 10925ec3e3dSEric Cheng #define DLS_PREPARE_PKT(mh, mp, mhip, err) { \ 110605445d5Sdg199075 mblk_t *nextp = (mp)->b_next; \ 11125ec3e3dSEric Cheng if (((err) = mac_vlan_header_info((mh), (mp), (mhip))) == 0) { \ 112605445d5Sdg199075 DLS_STRIP_PADDING((mhip)->mhi_pktsize, (mp)); \ 113605445d5Sdg199075 if (MBLKL((mp)) < (mhip)->mhi_hdrsize) { \ 114605445d5Sdg199075 mblk_t *newmp; \ 115605445d5Sdg199075 if ((newmp = msgpullup((mp), -1)) == NULL) { \ 116605445d5Sdg199075 (err) = EINVAL; \ 117605445d5Sdg199075 } else { \ 1186f45d2aeSyz147064 (mp)->b_next = NULL; \ 119605445d5Sdg199075 freemsg((mp)); \ 120605445d5Sdg199075 (mp) = newmp; \ 12125ec3e3dSEric Cheng VERIFY(mac_vlan_header_info((mh), \ 1226f45d2aeSyz147064 (mp), (mhip)) == 0); \ 123605445d5Sdg199075 (mp)->b_next = nextp; \ 124605445d5Sdg199075 (mp)->b_rptr += (mhip)->mhi_hdrsize; \ 125605445d5Sdg199075 } \ 126605445d5Sdg199075 } else { \ 127605445d5Sdg199075 (mp)->b_rptr += (mhip)->mhi_hdrsize; \ 128605445d5Sdg199075 } \ 129605445d5Sdg199075 } \ 130605445d5Sdg199075 } 131605445d5Sdg199075 132605445d5Sdg199075 /* 133ba2e4443Sseb * Truncate the chain starting at mp such that all packets in the chain 134605445d5Sdg199075 * have identical source and destination addresses, saps, and tag types 135605445d5Sdg199075 * (see below). It returns a pointer to the mblk following the chain, 136605445d5Sdg199075 * NULL if there is no further packet following the processed chain. 137605445d5Sdg199075 * The countp argument is set to the number of valid packets in the chain. 138605445d5Sdg199075 * Note that the whole MAC header (including the VLAN tag if any) in each 139605445d5Sdg199075 * packet will be stripped. 140ba2e4443Sseb */ 1417c478bd9Sstevel@tonic-gate static mblk_t * 142605445d5Sdg199075 i_dls_link_subchain(dls_link_t *dlp, mblk_t *mp, const mac_header_info_t *mhip, 143605445d5Sdg199075 uint_t *countp) 1447c478bd9Sstevel@tonic-gate { 145605445d5Sdg199075 mblk_t *prevp; 146605445d5Sdg199075 uint_t npacket = 1; 147ba2e4443Sseb size_t addr_size = dlp->dl_mip->mi_addr_length; 148605445d5Sdg199075 uint16_t vid = VLAN_ID(mhip->mhi_tci); 149605445d5Sdg199075 uint16_t pri = VLAN_PRI(mhip->mhi_tci); 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate /* 1527c478bd9Sstevel@tonic-gate * Compare with subsequent headers until we find one that has 1536b6515e2Sericheng * differing header information. After checking each packet 1546b6515e2Sericheng * strip padding and skip over the header. 1557c478bd9Sstevel@tonic-gate */ 156605445d5Sdg199075 for (prevp = mp; (mp = mp->b_next) != NULL; prevp = mp) { 157ba2e4443Sseb mac_header_info_t cmhi; 158605445d5Sdg199075 uint16_t cvid, cpri; 159605445d5Sdg199075 int err; 160ba2e4443Sseb 16125ec3e3dSEric Cheng DLS_PREPARE_PKT(dlp->dl_mh, mp, &cmhi, err); 162605445d5Sdg199075 if (err != 0) 1637c478bd9Sstevel@tonic-gate break; 164ba2e4443Sseb 165605445d5Sdg199075 prevp->b_next = mp; 166605445d5Sdg199075 167ba2e4443Sseb /* 168f97419bcSEric Cheng * The source, destination, sap, vlan tag must all match in 169f97419bcSEric Cheng * a given subchain. 170ba2e4443Sseb */ 171f97419bcSEric Cheng if (mhip->mhi_saddr == NULL || cmhi.mhi_saddr == NULL || 172f97419bcSEric Cheng memcmp(mhip->mhi_daddr, cmhi.mhi_daddr, addr_size) != 0 || 173ba2e4443Sseb memcmp(mhip->mhi_saddr, cmhi.mhi_saddr, addr_size) != 0 || 174da14cebeSEric Cheng mhip->mhi_bindsap != cmhi.mhi_bindsap) { 175605445d5Sdg199075 /* 176605445d5Sdg199075 * Note that we don't need to restore the padding. 177605445d5Sdg199075 */ 178605445d5Sdg199075 mp->b_rptr -= cmhi.mhi_hdrsize; 179ba2e4443Sseb break; 180ba2e4443Sseb } 181ba2e4443Sseb 182605445d5Sdg199075 cvid = VLAN_ID(cmhi.mhi_tci); 183605445d5Sdg199075 cpri = VLAN_PRI(cmhi.mhi_tci); 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate /* 186605445d5Sdg199075 * There are several types of packets. Packets don't match 187605445d5Sdg199075 * if they are classified to different type or if they are 188605445d5Sdg199075 * VLAN packets but belong to different VLANs: 189605445d5Sdg199075 * 190605445d5Sdg199075 * packet type tagged vid pri 191605445d5Sdg199075 * --------------------------------------------------------- 192605445d5Sdg199075 * untagged No zero zero 193605445d5Sdg199075 * VLAN packets Yes non-zero - 194605445d5Sdg199075 * priority tagged Yes zero non-zero 195605445d5Sdg199075 * 0 tagged Yes zero zero 1967c478bd9Sstevel@tonic-gate */ 197605445d5Sdg199075 if ((mhip->mhi_istagged != cmhi.mhi_istagged) || 198605445d5Sdg199075 (vid != cvid) || ((vid == VLAN_ID_NONE) && 199605445d5Sdg199075 (((pri == 0) && (cpri != 0)) || 200605445d5Sdg199075 ((pri != 0) && (cpri == 0))))) { 201605445d5Sdg199075 mp->b_rptr -= cmhi.mhi_hdrsize; 202605445d5Sdg199075 break; 203605445d5Sdg199075 } 204605445d5Sdg199075 205605445d5Sdg199075 npacket++; 206605445d5Sdg199075 } 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate /* 2097c478bd9Sstevel@tonic-gate * Break the chain at this point and return a pointer to the next 2107c478bd9Sstevel@tonic-gate * sub-chain. 2117c478bd9Sstevel@tonic-gate */ 212605445d5Sdg199075 prevp->b_next = NULL; 2137c478bd9Sstevel@tonic-gate *countp = npacket; 214605445d5Sdg199075 return (mp); 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate 217da14cebeSEric Cheng /* ARGSUSED */ 218da14cebeSEric Cheng static int 219da14cebeSEric Cheng i_dls_head_hold(mod_hash_key_t key, mod_hash_val_t val) 220210db224Sericheng { 221da14cebeSEric Cheng dls_head_t *dhp = (dls_head_t *)val; 222da14cebeSEric Cheng 223da14cebeSEric Cheng /* 224da14cebeSEric Cheng * The lock order is mod_hash's internal lock -> dh_lock as in the 225da14cebeSEric Cheng * call to i_dls_link_rx -> mod_hash_find_cb_rval -> i_dls_head_hold 226da14cebeSEric Cheng */ 227da14cebeSEric Cheng mutex_enter(&dhp->dh_lock); 228da14cebeSEric Cheng if (dhp->dh_removing) { 229da14cebeSEric Cheng mutex_exit(&dhp->dh_lock); 230da14cebeSEric Cheng return (-1); 231da14cebeSEric Cheng } 232da14cebeSEric Cheng dhp->dh_ref++; 233da14cebeSEric Cheng mutex_exit(&dhp->dh_lock); 234da14cebeSEric Cheng return (0); 235210db224Sericheng } 236210db224Sericheng 237da14cebeSEric Cheng void 238210db224Sericheng i_dls_head_rele(dls_head_t *dhp) 239210db224Sericheng { 240da14cebeSEric Cheng mutex_enter(&dhp->dh_lock); 241da14cebeSEric Cheng dhp->dh_ref--; 242da14cebeSEric Cheng if (dhp->dh_ref == 0 && dhp->dh_removing != 0) 243da14cebeSEric Cheng cv_broadcast(&dhp->dh_cv); 244da14cebeSEric Cheng mutex_exit(&dhp->dh_lock); 245210db224Sericheng } 246210db224Sericheng 247210db224Sericheng static dls_head_t * 248210db224Sericheng i_dls_head_alloc(mod_hash_key_t key) 249210db224Sericheng { 250210db224Sericheng dls_head_t *dhp; 251210db224Sericheng 252210db224Sericheng dhp = kmem_zalloc(sizeof (dls_head_t), KM_SLEEP); 253210db224Sericheng dhp->dh_key = key; 254210db224Sericheng return (dhp); 255210db224Sericheng } 256210db224Sericheng 257210db224Sericheng static void 258210db224Sericheng i_dls_head_free(dls_head_t *dhp) 259210db224Sericheng { 260210db224Sericheng ASSERT(dhp->dh_ref == 0); 261210db224Sericheng kmem_free(dhp, sizeof (dls_head_t)); 262210db224Sericheng } 263210db224Sericheng 264605445d5Sdg199075 /* 265605445d5Sdg199075 * Try to send mp up to the streams of the given sap and vid. Return B_TRUE 266605445d5Sdg199075 * if this message is sent to any streams. 267605445d5Sdg199075 * Note that this function will copy the message chain and the original 268605445d5Sdg199075 * mp will remain valid after this function 269605445d5Sdg199075 */ 270605445d5Sdg199075 static uint_t 271605445d5Sdg199075 i_dls_link_rx_func(dls_link_t *dlp, mac_resource_handle_t mrh, 272da14cebeSEric Cheng mac_header_info_t *mhip, mblk_t *mp, uint32_t sap, 273605445d5Sdg199075 boolean_t (*acceptfunc)()) 2747c478bd9Sstevel@tonic-gate { 275da14cebeSEric Cheng mod_hash_t *hash = dlp->dl_str_hash; 276605445d5Sdg199075 mod_hash_key_t key; 277210db224Sericheng dls_head_t *dhp; 278da14cebeSEric Cheng dld_str_t *dsp; 2797c478bd9Sstevel@tonic-gate mblk_t *nmp; 280da14cebeSEric Cheng dls_rx_t ds_rx; 281da14cebeSEric Cheng void *ds_rx_arg; 282605445d5Sdg199075 uint_t naccepted = 0; 283da14cebeSEric Cheng int rval; 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate /* 2867c478bd9Sstevel@tonic-gate * Construct a hash key from the VLAN identifier and the 287da14cebeSEric Cheng * DLSAP that represents dld_str_t in promiscuous mode. 2887c478bd9Sstevel@tonic-gate */ 289da14cebeSEric Cheng key = MAKE_KEY(sap); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate /* 292da14cebeSEric Cheng * Search the hash table for dld_str_t eligible to receive 293da14cebeSEric Cheng * a packet chain for this DLSAP/VLAN combination. The mod hash's 294da14cebeSEric Cheng * internal lock serializes find/insert/remove from the mod hash list. 295da14cebeSEric Cheng * Incrementing the dh_ref (while holding the mod hash lock) ensures 296da14cebeSEric Cheng * dls_link_remove will wait for the upcall to finish. 2977c478bd9Sstevel@tonic-gate */ 298da14cebeSEric Cheng if (mod_hash_find_cb_rval(hash, key, (mod_hash_val_t *)&dhp, 299da14cebeSEric Cheng i_dls_head_hold, &rval) != 0 || (rval != 0)) { 300605445d5Sdg199075 return (B_FALSE); 3017c478bd9Sstevel@tonic-gate } 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 304da14cebeSEric Cheng * Find dld_str_t that will accept the sub-chain. 3057c478bd9Sstevel@tonic-gate */ 306da14cebeSEric Cheng for (dsp = dhp->dh_list; dsp != NULL; dsp = dsp->ds_next) { 307da14cebeSEric Cheng if (!acceptfunc(dsp, mhip, &ds_rx, &ds_rx_arg)) 3087c478bd9Sstevel@tonic-gate continue; 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate /* 3117c478bd9Sstevel@tonic-gate * We have at least one acceptor. 3127c478bd9Sstevel@tonic-gate */ 313605445d5Sdg199075 naccepted++; 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate /* 316da14cebeSEric Cheng * There will normally be at least more dld_str_t 3177c478bd9Sstevel@tonic-gate * (since we've yet to check for non-promiscuous 318da14cebeSEric Cheng * dld_str_t) so dup the sub-chain. 3197c478bd9Sstevel@tonic-gate */ 3207c478bd9Sstevel@tonic-gate if ((nmp = copymsgchain(mp)) != NULL) 321da14cebeSEric Cheng ds_rx(ds_rx_arg, mrh, nmp, mhip); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate /* 325da14cebeSEric Cheng * Release the hold on the dld_str_t chain now that we have 3267c478bd9Sstevel@tonic-gate * finished walking it. 3277c478bd9Sstevel@tonic-gate */ 328210db224Sericheng i_dls_head_rele(dhp); 329605445d5Sdg199075 return (naccepted); 330605445d5Sdg199075 } 3317c478bd9Sstevel@tonic-gate 332da14cebeSEric Cheng /* ARGSUSED */ 333da14cebeSEric Cheng void 334da14cebeSEric Cheng i_dls_link_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 335da14cebeSEric Cheng boolean_t loopback) 336605445d5Sdg199075 { 337605445d5Sdg199075 dls_link_t *dlp = arg; 338da14cebeSEric Cheng mod_hash_t *hash = dlp->dl_str_hash; 339605445d5Sdg199075 mblk_t *nextp; 340605445d5Sdg199075 mac_header_info_t mhi; 341605445d5Sdg199075 dls_head_t *dhp; 342da14cebeSEric Cheng dld_str_t *dsp; 343da14cebeSEric Cheng dld_str_t *ndsp; 344605445d5Sdg199075 mblk_t *nmp; 345605445d5Sdg199075 mod_hash_key_t key; 346605445d5Sdg199075 uint_t npacket; 347605445d5Sdg199075 boolean_t accepted; 348da14cebeSEric Cheng dls_rx_t ds_rx, nds_rx; 349da14cebeSEric Cheng void *ds_rx_arg, *nds_rx_arg; 350605445d5Sdg199075 uint16_t vid; 351da14cebeSEric Cheng int err, rval; 352605445d5Sdg199075 353605445d5Sdg199075 /* 354605445d5Sdg199075 * Walk the packet chain. 355605445d5Sdg199075 */ 356605445d5Sdg199075 for (; mp != NULL; mp = nextp) { 357605445d5Sdg199075 /* 358605445d5Sdg199075 * Wipe the accepted state. 359605445d5Sdg199075 */ 360605445d5Sdg199075 accepted = B_FALSE; 361605445d5Sdg199075 36225ec3e3dSEric Cheng DLS_PREPARE_PKT(dlp->dl_mh, mp, &mhi, err); 363605445d5Sdg199075 if (err != 0) { 364*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&(dlp->dl_unknowns)); 365605445d5Sdg199075 nextp = mp->b_next; 3666f45d2aeSyz147064 mp->b_next = NULL; 367605445d5Sdg199075 freemsg(mp); 368605445d5Sdg199075 continue; 369605445d5Sdg199075 } 370605445d5Sdg199075 371605445d5Sdg199075 /* 372605445d5Sdg199075 * Grab the longest sub-chain we can process as a single 373605445d5Sdg199075 * unit. 374605445d5Sdg199075 */ 375605445d5Sdg199075 nextp = i_dls_link_subchain(dlp, mp, &mhi, &npacket); 376605445d5Sdg199075 ASSERT(npacket != 0); 377605445d5Sdg199075 378605445d5Sdg199075 vid = VLAN_ID(mhi.mhi_tci); 379605445d5Sdg199075 380605445d5Sdg199075 if (mhi.mhi_istagged) { 381605445d5Sdg199075 /* 382605445d5Sdg199075 * If it is tagged traffic, send it upstream to 383da14cebeSEric Cheng * all dld_str_t which are attached to the physical 384605445d5Sdg199075 * link and bound to SAP 0x8100. 385605445d5Sdg199075 */ 386605445d5Sdg199075 if (i_dls_link_rx_func(dlp, mrh, &mhi, mp, 387da14cebeSEric Cheng ETHERTYPE_VLAN, dls_accept) > 0) { 388605445d5Sdg199075 accepted = B_TRUE; 389605445d5Sdg199075 } 390605445d5Sdg199075 391605445d5Sdg199075 /* 392605445d5Sdg199075 * Don't pass the packets up if they are tagged 393605445d5Sdg199075 * packets and: 3944eaa4710SRishi Srivatsavai * - their VID and priority are both zero and the 3954eaa4710SRishi Srivatsavai * original packet isn't using the PVID (invalid 396605445d5Sdg199075 * packets). 397605445d5Sdg199075 * - their sap is ETHERTYPE_VLAN and their VID is 398605445d5Sdg199075 * zero as they have already been sent upstreams. 399605445d5Sdg199075 */ 4004eaa4710SRishi Srivatsavai if ((vid == VLAN_ID_NONE && !mhi.mhi_ispvid && 401605445d5Sdg199075 VLAN_PRI(mhi.mhi_tci) == 0) || 402605445d5Sdg199075 (mhi.mhi_bindsap == ETHERTYPE_VLAN && 403605445d5Sdg199075 vid == VLAN_ID_NONE)) { 404605445d5Sdg199075 freemsgchain(mp); 405605445d5Sdg199075 goto loop; 406605445d5Sdg199075 } 407605445d5Sdg199075 } 408605445d5Sdg199075 4097c478bd9Sstevel@tonic-gate /* 4107c478bd9Sstevel@tonic-gate * Construct a hash key from the VLAN identifier and the 4117c478bd9Sstevel@tonic-gate * DLSAP. 4127c478bd9Sstevel@tonic-gate */ 413da14cebeSEric Cheng key = MAKE_KEY(mhi.mhi_bindsap); 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* 416da14cebeSEric Cheng * Search the has table for dld_str_t eligible to receive 4177c478bd9Sstevel@tonic-gate * a packet chain for this DLSAP/VLAN combination. 4187c478bd9Sstevel@tonic-gate */ 419da14cebeSEric Cheng if (mod_hash_find_cb_rval(hash, key, (mod_hash_val_t *)&dhp, 420da14cebeSEric Cheng i_dls_head_hold, &rval) != 0 || (rval != 0)) { 4217c478bd9Sstevel@tonic-gate freemsgchain(mp); 4227c478bd9Sstevel@tonic-gate goto loop; 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate /* 426da14cebeSEric Cheng * Find the first dld_str_t that will accept the sub-chain. 4277c478bd9Sstevel@tonic-gate */ 428da14cebeSEric Cheng for (dsp = dhp->dh_list; dsp != NULL; dsp = dsp->ds_next) 429da14cebeSEric Cheng if (dls_accept(dsp, &mhi, &ds_rx, &ds_rx_arg)) 4307c478bd9Sstevel@tonic-gate break; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate /* 433da14cebeSEric Cheng * If we did not find any dld_str_t willing to accept the 4347c478bd9Sstevel@tonic-gate * sub-chain then throw it away. 4357c478bd9Sstevel@tonic-gate */ 436da14cebeSEric Cheng if (dsp == NULL) { 437210db224Sericheng i_dls_head_rele(dhp); 4387c478bd9Sstevel@tonic-gate freemsgchain(mp); 4397c478bd9Sstevel@tonic-gate goto loop; 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate /* 4437c478bd9Sstevel@tonic-gate * We have at least one acceptor. 4447c478bd9Sstevel@tonic-gate */ 4457c478bd9Sstevel@tonic-gate accepted = B_TRUE; 4467c478bd9Sstevel@tonic-gate for (;;) { 4477c478bd9Sstevel@tonic-gate /* 448da14cebeSEric Cheng * Find the next dld_str_t that will accept the 4497c478bd9Sstevel@tonic-gate * sub-chain. 4507c478bd9Sstevel@tonic-gate */ 451da14cebeSEric Cheng for (ndsp = dsp->ds_next; ndsp != NULL; 452da14cebeSEric Cheng ndsp = ndsp->ds_next) 453da14cebeSEric Cheng if (dls_accept(ndsp, &mhi, &nds_rx, 454da14cebeSEric Cheng &nds_rx_arg)) 4557c478bd9Sstevel@tonic-gate break; 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate /* 458da14cebeSEric Cheng * If there are no more dld_str_t that are willing 4597c478bd9Sstevel@tonic-gate * to accept the sub-chain then we don't need to dup 4607c478bd9Sstevel@tonic-gate * it before handing it to the current one. 4617c478bd9Sstevel@tonic-gate */ 462da14cebeSEric Cheng if (ndsp == NULL) { 463da14cebeSEric Cheng ds_rx(ds_rx_arg, mrh, mp, &mhi); 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate /* 466da14cebeSEric Cheng * Since there are no more dld_str_t, we're 4677c478bd9Sstevel@tonic-gate * done. 4687c478bd9Sstevel@tonic-gate */ 4697c478bd9Sstevel@tonic-gate break; 4707c478bd9Sstevel@tonic-gate } 4717c478bd9Sstevel@tonic-gate 4727c478bd9Sstevel@tonic-gate /* 473da14cebeSEric Cheng * There are more dld_str_t so dup the sub-chain. 4747c478bd9Sstevel@tonic-gate */ 4757c478bd9Sstevel@tonic-gate if ((nmp = copymsgchain(mp)) != NULL) 476da14cebeSEric Cheng ds_rx(ds_rx_arg, mrh, nmp, &mhi); 4777c478bd9Sstevel@tonic-gate 478da14cebeSEric Cheng dsp = ndsp; 479da14cebeSEric Cheng ds_rx = nds_rx; 480da14cebeSEric Cheng ds_rx_arg = nds_rx_arg; 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate /* 484da14cebeSEric Cheng * Release the hold on the dld_str_t chain now that we have 4857c478bd9Sstevel@tonic-gate * finished walking it. 4867c478bd9Sstevel@tonic-gate */ 487210db224Sericheng i_dls_head_rele(dhp); 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate loop: 4907c478bd9Sstevel@tonic-gate /* 4917c478bd9Sstevel@tonic-gate * If there were no acceptors then add the packet count to the 4927c478bd9Sstevel@tonic-gate * 'unknown' count. 4937c478bd9Sstevel@tonic-gate */ 4947c478bd9Sstevel@tonic-gate if (!accepted) 4957c478bd9Sstevel@tonic-gate atomic_add_32(&(dlp->dl_unknowns), npacket); 496605445d5Sdg199075 } 497605445d5Sdg199075 } 4987c478bd9Sstevel@tonic-gate 499da14cebeSEric Cheng /* ARGSUSED */ 500da14cebeSEric Cheng void 501da14cebeSEric Cheng dls_rx_vlan_promisc(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 502da14cebeSEric Cheng boolean_t loopback) 503605445d5Sdg199075 { 504da14cebeSEric Cheng dld_str_t *dsp = arg; 505da14cebeSEric Cheng dls_link_t *dlp = dsp->ds_dlp; 506ba2e4443Sseb mac_header_info_t mhi; 507da14cebeSEric Cheng dls_rx_t ds_rx; 508da14cebeSEric Cheng void *ds_rx_arg; 509605445d5Sdg199075 int err; 5107c478bd9Sstevel@tonic-gate 51125ec3e3dSEric Cheng DLS_PREPARE_PKT(dlp->dl_mh, mp, &mhi, err); 512da14cebeSEric Cheng if (err != 0) 513da14cebeSEric Cheng goto drop; 514da14cebeSEric Cheng 515da14cebeSEric Cheng /* 516da14cebeSEric Cheng * If there is promiscuous handle for vlan, we filter out the untagged 517da14cebeSEric Cheng * pkts and pkts that are not for the primary unicast address. 518da14cebeSEric Cheng */ 519da14cebeSEric Cheng if (dsp->ds_vlan_mph != NULL) { 520da14cebeSEric Cheng uint8_t prim_addr[MAXMACADDRLEN]; 521da14cebeSEric Cheng size_t addr_length = dsp->ds_mip->mi_addr_length; 522da14cebeSEric Cheng 523da14cebeSEric Cheng if (!(mhi.mhi_istagged)) 524da14cebeSEric Cheng goto drop; 525da14cebeSEric Cheng ASSERT(dsp->ds_mh != NULL); 526da14cebeSEric Cheng mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)prim_addr); 527da14cebeSEric Cheng if (memcmp(mhi.mhi_daddr, prim_addr, addr_length) != 0) 528da14cebeSEric Cheng goto drop; 529da14cebeSEric Cheng 530da14cebeSEric Cheng if (!dls_accept(dsp, &mhi, &ds_rx, &ds_rx_arg)) 531da14cebeSEric Cheng goto drop; 532da14cebeSEric Cheng 533da14cebeSEric Cheng ds_rx(ds_rx_arg, NULL, mp, &mhi); 534da14cebeSEric Cheng return; 535da14cebeSEric Cheng } 536da14cebeSEric Cheng 537da14cebeSEric Cheng drop: 538*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&dlp->dl_unknowns); 539605445d5Sdg199075 freemsg(mp); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 542210db224Sericheng /* ARGSUSED */ 543da14cebeSEric Cheng void 544da14cebeSEric Cheng dls_rx_promisc(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 545da14cebeSEric Cheng boolean_t loopback) 5467c478bd9Sstevel@tonic-gate { 547da14cebeSEric Cheng dld_str_t *dsp = arg; 548da14cebeSEric Cheng dls_link_t *dlp = dsp->ds_dlp; 549da14cebeSEric Cheng mac_header_info_t mhi; 550da14cebeSEric Cheng dls_rx_t ds_rx; 551da14cebeSEric Cheng void *ds_rx_arg; 552da14cebeSEric Cheng int err; 553da14cebeSEric Cheng dls_head_t *dhp; 554da14cebeSEric Cheng mod_hash_key_t key; 5557c478bd9Sstevel@tonic-gate 55625ec3e3dSEric Cheng DLS_PREPARE_PKT(dlp->dl_mh, mp, &mhi, err); 557da14cebeSEric Cheng if (err != 0) 558da14cebeSEric Cheng goto drop; 559da14cebeSEric Cheng 560da14cebeSEric Cheng /* 561da14cebeSEric Cheng * In order to filter out sap pkt that no dls channel listens, search 562da14cebeSEric Cheng * the hash table trying to find a dld_str_t eligible to receive the pkt 563da14cebeSEric Cheng */ 564da14cebeSEric Cheng if ((dsp->ds_promisc & DLS_PROMISC_SAP) == 0) { 565da14cebeSEric Cheng key = MAKE_KEY(mhi.mhi_bindsap); 566da14cebeSEric Cheng if (mod_hash_find(dsp->ds_dlp->dl_str_hash, key, 567da14cebeSEric Cheng (mod_hash_val_t *)&dhp) != 0) 568da14cebeSEric Cheng goto drop; 5697c478bd9Sstevel@tonic-gate } 5707c478bd9Sstevel@tonic-gate 571da14cebeSEric Cheng if (!dls_accept_promisc(dsp, &mhi, &ds_rx, &ds_rx_arg, loopback)) 572da14cebeSEric Cheng goto drop; 573da14cebeSEric Cheng 574da14cebeSEric Cheng ds_rx(ds_rx_arg, NULL, mp, &mhi); 575da14cebeSEric Cheng return; 576da14cebeSEric Cheng 577da14cebeSEric Cheng drop: 578*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&dlp->dl_unknowns); 579da14cebeSEric Cheng freemsg(mp); 580da14cebeSEric Cheng } 581da14cebeSEric Cheng 582da14cebeSEric Cheng static void 583da14cebeSEric Cheng i_dls_link_destroy(dls_link_t *dlp) 584da14cebeSEric Cheng { 585da14cebeSEric Cheng ASSERT(dlp->dl_nactive == 0); 586da14cebeSEric Cheng ASSERT(dlp->dl_impl_count == 0); 587da14cebeSEric Cheng ASSERT(dlp->dl_zone_ref == 0); 588da14cebeSEric Cheng 589da14cebeSEric Cheng /* 590da14cebeSEric Cheng * Free the structure back to the cache. 591da14cebeSEric Cheng */ 592da14cebeSEric Cheng if (dlp->dl_mch != NULL) 593da14cebeSEric Cheng mac_client_close(dlp->dl_mch, 0); 594da14cebeSEric Cheng 595da14cebeSEric Cheng if (dlp->dl_mh != NULL) { 596da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 597da14cebeSEric Cheng mac_close(dlp->dl_mh); 598da14cebeSEric Cheng } 599da14cebeSEric Cheng 600da14cebeSEric Cheng dlp->dl_mh = NULL; 601da14cebeSEric Cheng dlp->dl_mch = NULL; 602da14cebeSEric Cheng dlp->dl_mip = NULL; 603da14cebeSEric Cheng dlp->dl_unknowns = 0; 6048d4cf8d8S dlp->dl_nonip_cnt = 0; 605da14cebeSEric Cheng kmem_cache_free(i_dls_link_cachep, dlp); 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate static int 609a08fa175Syz147064 i_dls_link_create(const char *name, dls_link_t **dlpp) 6107c478bd9Sstevel@tonic-gate { 6117c478bd9Sstevel@tonic-gate dls_link_t *dlp; 612da14cebeSEric Cheng int err; 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate /* 6157c478bd9Sstevel@tonic-gate * Allocate a new dls_link_t structure. 6167c478bd9Sstevel@tonic-gate */ 6177c478bd9Sstevel@tonic-gate dlp = kmem_cache_alloc(i_dls_link_cachep, KM_SLEEP); 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate /* 6207c478bd9Sstevel@tonic-gate * Name the dls_link_t after the MAC interface it represents. 6217c478bd9Sstevel@tonic-gate */ 622ba2e4443Sseb (void) strlcpy(dlp->dl_name, name, sizeof (dlp->dl_name)); 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate /* 625da14cebeSEric Cheng * First reference; hold open the MAC interface. 6267c478bd9Sstevel@tonic-gate */ 627da14cebeSEric Cheng ASSERT(dlp->dl_mh == NULL); 628da14cebeSEric Cheng err = mac_open(dlp->dl_name, &dlp->dl_mh); 629da14cebeSEric Cheng if (err != 0) 630da14cebeSEric Cheng goto bail; 631da14cebeSEric Cheng 632da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 633da14cebeSEric Cheng dlp->dl_mip = mac_info(dlp->dl_mh); 634da14cebeSEric Cheng 635da14cebeSEric Cheng /* DLS is the "primary" MAC client */ 636da14cebeSEric Cheng ASSERT(dlp->dl_mch == NULL); 637da14cebeSEric Cheng 638da14cebeSEric Cheng err = mac_client_open(dlp->dl_mh, &dlp->dl_mch, NULL, 639da14cebeSEric Cheng MAC_OPEN_FLAGS_USE_DATALINK_NAME); 640da14cebeSEric Cheng if (err != 0) 641da14cebeSEric Cheng goto bail; 642da14cebeSEric Cheng 643da14cebeSEric Cheng DTRACE_PROBE2(dls__primary__client, char *, dlp->dl_name, void *, 644da14cebeSEric Cheng dlp->dl_mch); 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate *dlpp = dlp; 6477c478bd9Sstevel@tonic-gate return (0); 6487c478bd9Sstevel@tonic-gate 649da14cebeSEric Cheng bail: 650da14cebeSEric Cheng i_dls_link_destroy(dlp); 651da14cebeSEric Cheng return (err); 6527c478bd9Sstevel@tonic-gate } 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate /* 6557c478bd9Sstevel@tonic-gate * Module initialization functions. 6567c478bd9Sstevel@tonic-gate */ 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate void 6597c478bd9Sstevel@tonic-gate dls_link_init(void) 6607c478bd9Sstevel@tonic-gate { 6617c478bd9Sstevel@tonic-gate /* 6627c478bd9Sstevel@tonic-gate * Create a kmem_cache of dls_link_t structures. 6637c478bd9Sstevel@tonic-gate */ 6647c478bd9Sstevel@tonic-gate i_dls_link_cachep = kmem_cache_create("dls_link_cache", 6657c478bd9Sstevel@tonic-gate sizeof (dls_link_t), 0, i_dls_link_constructor, 6667c478bd9Sstevel@tonic-gate i_dls_link_destructor, NULL, NULL, NULL, 0); 6677c478bd9Sstevel@tonic-gate ASSERT(i_dls_link_cachep != NULL); 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate /* 670210db224Sericheng * Create a dls_link_t hash table and associated lock. 6717c478bd9Sstevel@tonic-gate */ 672210db224Sericheng i_dls_link_hash = mod_hash_create_extended("dls_link_hash", 673210db224Sericheng IMPL_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 674210db224Sericheng mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 675210db224Sericheng i_dls_link_count = 0; 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate int 6797c478bd9Sstevel@tonic-gate dls_link_fini(void) 6807c478bd9Sstevel@tonic-gate { 681210db224Sericheng if (i_dls_link_count > 0) 682210db224Sericheng return (EBUSY); 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate /* 6857c478bd9Sstevel@tonic-gate * Destroy the kmem_cache. 6867c478bd9Sstevel@tonic-gate */ 6877c478bd9Sstevel@tonic-gate kmem_cache_destroy(i_dls_link_cachep); 688210db224Sericheng 689210db224Sericheng /* 690210db224Sericheng * Destroy the hash table and associated lock. 691210db224Sericheng */ 692210db224Sericheng mod_hash_destroy_hash(i_dls_link_hash); 6937c478bd9Sstevel@tonic-gate return (0); 6947c478bd9Sstevel@tonic-gate } 6957c478bd9Sstevel@tonic-gate 6967c478bd9Sstevel@tonic-gate /* 6977c478bd9Sstevel@tonic-gate * Exported functions. 6987c478bd9Sstevel@tonic-gate */ 6997c478bd9Sstevel@tonic-gate 700da14cebeSEric Cheng static int 701da14cebeSEric Cheng dls_link_hold_common(const char *name, dls_link_t **dlpp, boolean_t create) 7027c478bd9Sstevel@tonic-gate { 7037c478bd9Sstevel@tonic-gate dls_link_t *dlp; 7047c478bd9Sstevel@tonic-gate int err; 7057c478bd9Sstevel@tonic-gate 7067c478bd9Sstevel@tonic-gate /* 707da14cebeSEric Cheng * Look up a dls_link_t corresponding to the given macname in the 708da14cebeSEric Cheng * global hash table. The i_dls_link_hash itself is protected by the 709da14cebeSEric Cheng * mod_hash package's internal lock which synchronizes 710da14cebeSEric Cheng * find/insert/remove into the global mod_hash list. Assumes that 711da14cebeSEric Cheng * inserts and removes are single threaded on a per mac end point 712da14cebeSEric Cheng * by the mac perimeter. 7137c478bd9Sstevel@tonic-gate */ 714210db224Sericheng if ((err = mod_hash_find(i_dls_link_hash, (mod_hash_key_t)name, 715210db224Sericheng (mod_hash_val_t *)&dlp)) == 0) 7167c478bd9Sstevel@tonic-gate goto done; 717210db224Sericheng 718210db224Sericheng ASSERT(err == MH_ERR_NOTFOUND); 719da14cebeSEric Cheng if (!create) 720da14cebeSEric Cheng return (ENOENT); 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate /* 7237c478bd9Sstevel@tonic-gate * We didn't find anything so we need to create one. 7247c478bd9Sstevel@tonic-gate */ 725da14cebeSEric Cheng if ((err = i_dls_link_create(name, &dlp)) != 0) 7267c478bd9Sstevel@tonic-gate return (err); 7277c478bd9Sstevel@tonic-gate 7287c478bd9Sstevel@tonic-gate /* 729210db224Sericheng * Insert the dls_link_t. 7307c478bd9Sstevel@tonic-gate */ 731d62bc4baSyz147064 err = mod_hash_insert(i_dls_link_hash, (mod_hash_key_t)dlp->dl_name, 732210db224Sericheng (mod_hash_val_t)dlp); 7337c478bd9Sstevel@tonic-gate ASSERT(err == 0); 7347c478bd9Sstevel@tonic-gate 735*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&i_dls_link_count); 736210db224Sericheng ASSERT(i_dls_link_count != 0); 737210db224Sericheng 7387c478bd9Sstevel@tonic-gate done: 739da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 7407c478bd9Sstevel@tonic-gate /* 7417c478bd9Sstevel@tonic-gate * Bump the reference count and hand back the reference. 7427c478bd9Sstevel@tonic-gate */ 7437c478bd9Sstevel@tonic-gate dlp->dl_ref++; 7447c478bd9Sstevel@tonic-gate *dlpp = dlp; 745210db224Sericheng return (0); 7467c478bd9Sstevel@tonic-gate } 7477c478bd9Sstevel@tonic-gate 748da14cebeSEric Cheng int 749da14cebeSEric Cheng dls_link_hold_create(const char *name, dls_link_t **dlpp) 750da14cebeSEric Cheng { 751da14cebeSEric Cheng return (dls_link_hold_common(name, dlpp, B_TRUE)); 752da14cebeSEric Cheng } 753da14cebeSEric Cheng 754da14cebeSEric Cheng int 755da14cebeSEric Cheng dls_link_hold(const char *name, dls_link_t **dlpp) 756da14cebeSEric Cheng { 757da14cebeSEric Cheng return (dls_link_hold_common(name, dlpp, B_FALSE)); 758da14cebeSEric Cheng } 759da14cebeSEric Cheng 760da14cebeSEric Cheng dev_info_t * 761da14cebeSEric Cheng dls_link_devinfo(dev_t dev) 762da14cebeSEric Cheng { 763da14cebeSEric Cheng dls_link_t *dlp; 764da14cebeSEric Cheng dev_info_t *dip; 765da14cebeSEric Cheng char macname[MAXNAMELEN]; 766da14cebeSEric Cheng char *drv; 767da14cebeSEric Cheng mac_perim_handle_t mph; 768da14cebeSEric Cheng 769da14cebeSEric Cheng if ((drv = ddi_major_to_name(getmajor(dev))) == NULL) 770da14cebeSEric Cheng return (NULL); 77161af1958SGarrett D'Amore (void) snprintf(macname, MAXNAMELEN, "%s%d", drv, 77261af1958SGarrett D'Amore DLS_MINOR2INST(getminor(dev))); 773da14cebeSEric Cheng 774da14cebeSEric Cheng /* 775da14cebeSEric Cheng * The code below assumes that the name constructed above is the 776da14cebeSEric Cheng * macname. This is not the case for legacy devices. Currently this 777da14cebeSEric Cheng * is ok because this function is only called in the getinfo(9e) path, 778da14cebeSEric Cheng * which for a legacy device would directly end up in the driver's 779da14cebeSEric Cheng * getinfo, rather than here 780da14cebeSEric Cheng */ 781da14cebeSEric Cheng if (mac_perim_enter_by_macname(macname, &mph) != 0) 782da14cebeSEric Cheng return (NULL); 783da14cebeSEric Cheng 784da14cebeSEric Cheng if (dls_link_hold(macname, &dlp) != 0) { 785da14cebeSEric Cheng mac_perim_exit(mph); 786da14cebeSEric Cheng return (NULL); 787da14cebeSEric Cheng } 788da14cebeSEric Cheng 789da14cebeSEric Cheng dip = mac_devinfo_get(dlp->dl_mh); 790da14cebeSEric Cheng dls_link_rele(dlp); 791da14cebeSEric Cheng mac_perim_exit(mph); 792da14cebeSEric Cheng 793da14cebeSEric Cheng return (dip); 794da14cebeSEric Cheng } 795da14cebeSEric Cheng 796da14cebeSEric Cheng dev_t 797da14cebeSEric Cheng dls_link_dev(dls_link_t *dlp) 798da14cebeSEric Cheng { 799da14cebeSEric Cheng return (makedevice(ddi_driver_major(mac_devinfo_get(dlp->dl_mh)), 800da14cebeSEric Cheng mac_minor(dlp->dl_mh))); 801da14cebeSEric Cheng } 802da14cebeSEric Cheng 8037c478bd9Sstevel@tonic-gate void 8047c478bd9Sstevel@tonic-gate dls_link_rele(dls_link_t *dlp) 8057c478bd9Sstevel@tonic-gate { 806210db224Sericheng mod_hash_val_t val; 8077c478bd9Sstevel@tonic-gate 808da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 8097c478bd9Sstevel@tonic-gate /* 8107c478bd9Sstevel@tonic-gate * Check if there are any more references. 8117c478bd9Sstevel@tonic-gate */ 812da14cebeSEric Cheng if (--dlp->dl_ref == 0) { 813210db224Sericheng (void) mod_hash_remove(i_dls_link_hash, 814210db224Sericheng (mod_hash_key_t)dlp->dl_name, &val); 815210db224Sericheng ASSERT(dlp == (dls_link_t *)val); 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate /* 8187c478bd9Sstevel@tonic-gate * Destroy the dls_link_t. 8197c478bd9Sstevel@tonic-gate */ 8207c478bd9Sstevel@tonic-gate i_dls_link_destroy(dlp); 821210db224Sericheng ASSERT(i_dls_link_count > 0); 822*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&i_dls_link_count); 823da14cebeSEric Cheng } 8247c478bd9Sstevel@tonic-gate } 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate int 827da14cebeSEric Cheng dls_link_rele_by_name(const char *name) 8287c478bd9Sstevel@tonic-gate { 829da14cebeSEric Cheng dls_link_t *dlp; 830da14cebeSEric Cheng 831da14cebeSEric Cheng if (mod_hash_find(i_dls_link_hash, (mod_hash_key_t)name, 832da14cebeSEric Cheng (mod_hash_val_t *)&dlp) != 0) 833da14cebeSEric Cheng return (ENOENT); 834da14cebeSEric Cheng 835da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 836da14cebeSEric Cheng 837da14cebeSEric Cheng /* 838da14cebeSEric Cheng * Must fail detach if mac client is busy. 839da14cebeSEric Cheng */ 840da14cebeSEric Cheng ASSERT(dlp->dl_ref > 0 && dlp->dl_mch != NULL); 841da14cebeSEric Cheng if (mac_link_has_flows(dlp->dl_mch)) 842da14cebeSEric Cheng return (ENOTEMPTY); 843da14cebeSEric Cheng 844da14cebeSEric Cheng dls_link_rele(dlp); 845da14cebeSEric Cheng return (0); 846da14cebeSEric Cheng } 847da14cebeSEric Cheng 848da14cebeSEric Cheng int 849da14cebeSEric Cheng dls_link_setzid(const char *name, zoneid_t zid) 850da14cebeSEric Cheng { 851da14cebeSEric Cheng dls_link_t *dlp; 8527c478bd9Sstevel@tonic-gate int err = 0; 853da14cebeSEric Cheng zoneid_t old_zid; 8547c478bd9Sstevel@tonic-gate 855da14cebeSEric Cheng if ((err = dls_link_hold_create(name, &dlp)) != 0) 856da14cebeSEric Cheng return (err); 857d62bc4baSyz147064 858da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 8597c478bd9Sstevel@tonic-gate 860da14cebeSEric Cheng if ((old_zid = dlp->dl_zid) == zid) 861da14cebeSEric Cheng goto done; 862da14cebeSEric Cheng 863da14cebeSEric Cheng /* 8642b24ab6bSSebastien Roy * Check whether this dlp is used by its own zone. If yes, we cannot 8652b24ab6bSSebastien Roy * change its zoneid. 866da14cebeSEric Cheng */ 867da14cebeSEric Cheng if (dlp->dl_zone_ref != 0) { 868da14cebeSEric Cheng err = EBUSY; 869da14cebeSEric Cheng goto done; 8707c478bd9Sstevel@tonic-gate } 8717c478bd9Sstevel@tonic-gate 8722b24ab6bSSebastien Roy dlp->dl_zid = zid; 8732b24ab6bSSebastien Roy 874da14cebeSEric Cheng if (zid == GLOBAL_ZONEID) { 875da14cebeSEric Cheng /* 8762b24ab6bSSebastien Roy * The link is moving from a non-global zone to the global 8772b24ab6bSSebastien Roy * zone, so we need to release the reference that was held 8782b24ab6bSSebastien Roy * when the link was originally assigned to the non-global 8792b24ab6bSSebastien Roy * zone. 880da14cebeSEric Cheng */ 881da14cebeSEric Cheng dls_link_rele(dlp); 882da14cebeSEric Cheng } 883da14cebeSEric Cheng 884da14cebeSEric Cheng done: 8852b24ab6bSSebastien Roy /* 8862b24ab6bSSebastien Roy * We only keep the reference to this link open if the link has 8872b24ab6bSSebastien Roy * successfully moved from the global zone to a non-global zone. 8882b24ab6bSSebastien Roy */ 8892b24ab6bSSebastien Roy if (err != 0 || old_zid != GLOBAL_ZONEID) 890da14cebeSEric Cheng dls_link_rele(dlp); 8917c478bd9Sstevel@tonic-gate return (err); 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate 8940a0e9771SDarren Reed int 8950a0e9771SDarren Reed dls_link_getzid(const char *name, zoneid_t *zidp) 8960a0e9771SDarren Reed { 8970a0e9771SDarren Reed dls_link_t *dlp; 8980a0e9771SDarren Reed int err = 0; 8990a0e9771SDarren Reed 9000a0e9771SDarren Reed if ((err = dls_link_hold(name, &dlp)) != 0) 9010a0e9771SDarren Reed return (err); 9020a0e9771SDarren Reed 9030a0e9771SDarren Reed ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 9040a0e9771SDarren Reed 9050a0e9771SDarren Reed *zidp = dlp->dl_zid; 9060a0e9771SDarren Reed 9070a0e9771SDarren Reed dls_link_rele(dlp); 9080a0e9771SDarren Reed return (0); 9090a0e9771SDarren Reed } 9100a0e9771SDarren Reed 9117c478bd9Sstevel@tonic-gate void 912da14cebeSEric Cheng dls_link_add(dls_link_t *dlp, uint32_t sap, dld_str_t *dsp) 9137c478bd9Sstevel@tonic-gate { 914da14cebeSEric Cheng mod_hash_t *hash = dlp->dl_str_hash; 915210db224Sericheng mod_hash_key_t key; 916210db224Sericheng dls_head_t *dhp; 917da14cebeSEric Cheng dld_str_t *p; 9187c478bd9Sstevel@tonic-gate int err; 919da14cebeSEric Cheng 920da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate /* 923da14cebeSEric Cheng * Generate a hash key based on the sap. 9247c478bd9Sstevel@tonic-gate */ 925da14cebeSEric Cheng key = MAKE_KEY(sap); 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate /* 928210db224Sericheng * Search the table for a list head with this key. 9297c478bd9Sstevel@tonic-gate */ 930210db224Sericheng if ((err = mod_hash_find(hash, key, (mod_hash_val_t *)&dhp)) != 0) { 931210db224Sericheng ASSERT(err == MH_ERR_NOTFOUND); 9327c478bd9Sstevel@tonic-gate 933210db224Sericheng dhp = i_dls_head_alloc(key); 934210db224Sericheng err = mod_hash_insert(hash, key, (mod_hash_val_t)dhp); 9357c478bd9Sstevel@tonic-gate ASSERT(err == 0); 9367c478bd9Sstevel@tonic-gate } 9377c478bd9Sstevel@tonic-gate 9387c478bd9Sstevel@tonic-gate /* 939da14cebeSEric Cheng * Add the dld_str_t to the head of the list. List walkers in 940da14cebeSEric Cheng * i_dls_link_rx_* bump up dh_ref to ensure the list does not change 941da14cebeSEric Cheng * while they walk the list. The membar below ensures that list walkers 942da14cebeSEric Cheng * see exactly the old list or the new list. 9437c478bd9Sstevel@tonic-gate */ 944da14cebeSEric Cheng ASSERT(dsp->ds_next == NULL); 945210db224Sericheng p = dhp->dh_list; 946da14cebeSEric Cheng dsp->ds_next = p; 947da14cebeSEric Cheng 948da14cebeSEric Cheng membar_producer(); 949da14cebeSEric Cheng 950da14cebeSEric Cheng dhp->dh_list = dsp; 9517c478bd9Sstevel@tonic-gate 9527c478bd9Sstevel@tonic-gate /* 953210db224Sericheng * Save a pointer to the list head. 9547c478bd9Sstevel@tonic-gate */ 955da14cebeSEric Cheng dsp->ds_head = dhp; 956210db224Sericheng dlp->dl_impl_count++; 9577c478bd9Sstevel@tonic-gate } 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate void 960da14cebeSEric Cheng dls_link_remove(dls_link_t *dlp, dld_str_t *dsp) 9617c478bd9Sstevel@tonic-gate { 962da14cebeSEric Cheng mod_hash_t *hash = dlp->dl_str_hash; 963da14cebeSEric Cheng dld_str_t **pp; 964da14cebeSEric Cheng dld_str_t *p; 965210db224Sericheng dls_head_t *dhp; 966da14cebeSEric Cheng 967da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(dlp->dl_mh)); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate /* 970da14cebeSEric Cheng * We set dh_removing here to tell the receive callbacks not to pass 971da14cebeSEric Cheng * up packets anymore. Then wait till the current callbacks are done. 972da14cebeSEric Cheng * This happens either in the close path or in processing the 973da14cebeSEric Cheng * DL_UNBIND_REQ via a taskq thread, and it is ok to cv_wait in either. 974da14cebeSEric Cheng * The dh_ref ensures there aren't and there won't be any upcalls 975da14cebeSEric Cheng * walking or using the dh_list. The mod hash internal lock ensures 976da14cebeSEric Cheng * that the insert/remove of the dls_head_t itself synchronizes with 977da14cebeSEric Cheng * any i_dls_link_rx trying to locate it. The perimeter ensures that 978da14cebeSEric Cheng * there isn't another simultaneous dls_link_add/remove. 9797c478bd9Sstevel@tonic-gate */ 980da14cebeSEric Cheng dhp = dsp->ds_head; 981da14cebeSEric Cheng 982da14cebeSEric Cheng mutex_enter(&dhp->dh_lock); 983da14cebeSEric Cheng dhp->dh_removing = B_TRUE; 984da14cebeSEric Cheng while (dhp->dh_ref != 0) 985da14cebeSEric Cheng cv_wait(&dhp->dh_cv, &dhp->dh_lock); 986da14cebeSEric Cheng mutex_exit(&dhp->dh_lock); 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate /* 989da14cebeSEric Cheng * Walk the list and remove the dld_str_t. 9907c478bd9Sstevel@tonic-gate */ 991da14cebeSEric Cheng for (pp = &dhp->dh_list; (p = *pp) != NULL; pp = &(p->ds_next)) { 992da14cebeSEric Cheng if (p == dsp) 9937c478bd9Sstevel@tonic-gate break; 9947c478bd9Sstevel@tonic-gate } 9957c478bd9Sstevel@tonic-gate ASSERT(p != NULL); 996da14cebeSEric Cheng *pp = p->ds_next; 997da14cebeSEric Cheng p->ds_next = NULL; 998da14cebeSEric Cheng p->ds_head = NULL; 9997c478bd9Sstevel@tonic-gate 1000da14cebeSEric Cheng ASSERT(dlp->dl_impl_count != 0); 1001210db224Sericheng dlp->dl_impl_count--; 1002210db224Sericheng 1003210db224Sericheng if (dhp->dh_list == NULL) { 1004210db224Sericheng mod_hash_val_t val = NULL; 1005210db224Sericheng 10067c478bd9Sstevel@tonic-gate /* 10077c478bd9Sstevel@tonic-gate * The list is empty so remove the hash table entry. 10087c478bd9Sstevel@tonic-gate */ 1009210db224Sericheng (void) mod_hash_remove(hash, dhp->dh_key, &val); 1010210db224Sericheng ASSERT(dhp == (dls_head_t *)val); 1011210db224Sericheng i_dls_head_free(dhp); 10127c478bd9Sstevel@tonic-gate } else { 1013da14cebeSEric Cheng mutex_enter(&dhp->dh_lock); 1014da14cebeSEric Cheng dhp->dh_removing = B_FALSE; 1015da14cebeSEric Cheng mutex_exit(&dhp->dh_lock); 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate } 1018