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
i_dls_link_constructor(void * buf,void * arg,int kmflag)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
i_dls_link_destructor(void * buf,void * arg)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 *
i_dls_link_subchain(dls_link_t * dlp,mblk_t * mp,const mac_header_info_t * mhip,uint_t * countp)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
i_dls_head_hold(mod_hash_key_t key,mod_hash_val_t val)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
i_dls_head_rele(dls_head_t * dhp)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 *
i_dls_head_alloc(mod_hash_key_t key)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
i_dls_head_free(dls_head_t * dhp)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
i_dls_link_rx_func(dls_link_t * dlp,mac_resource_handle_t mrh,mac_header_info_t * mhip,mblk_t * mp,uint32_t sap,boolean_t (* acceptfunc)())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
i_dls_link_rx(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)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*0d6bb4c6SJosef '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
dls_rx_vlan_promisc(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)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*0d6bb4c6SJosef 'Jeff' Sipek atomic_inc_32(&dlp->dl_unknowns);
539605445d5Sdg199075 freemsg(mp);
5407c478bd9Sstevel@tonic-gate }
5417c478bd9Sstevel@tonic-gate
542210db224Sericheng /* ARGSUSED */
543da14cebeSEric Cheng void
dls_rx_promisc(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)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*0d6bb4c6SJosef 'Jeff' Sipek atomic_inc_32(&dlp->dl_unknowns);
579da14cebeSEric Cheng freemsg(mp);
580da14cebeSEric Cheng }
581da14cebeSEric Cheng
582da14cebeSEric Cheng static void
i_dls_link_destroy(dls_link_t * dlp)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
i_dls_link_create(const char * name,dls_link_t ** dlpp)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
dls_link_init(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
dls_link_fini(void)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
dls_link_hold_common(const char * name,dls_link_t ** dlpp,boolean_t create)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*0d6bb4c6SJosef '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
dls_link_hold_create(const char * name,dls_link_t ** dlpp)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
dls_link_hold(const char * name,dls_link_t ** dlpp)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 *
dls_link_devinfo(dev_t dev)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
dls_link_dev(dls_link_t * dlp)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
dls_link_rele(dls_link_t * dlp)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*0d6bb4c6SJosef 'Jeff' Sipek atomic_dec_32(&i_dls_link_count);
823da14cebeSEric Cheng }
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate
8267c478bd9Sstevel@tonic-gate int
dls_link_rele_by_name(const char * name)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
dls_link_setzid(const char * name,zoneid_t zid)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
dls_link_getzid(const char * name,zoneid_t * zidp)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
dls_link_add(dls_link_t * dlp,uint32_t sap,dld_str_t * dsp)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
dls_link_remove(dls_link_t * dlp,dld_str_t * dsp)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