xref: /titanic_41/usr/src/uts/common/io/dls/dls_link.c (revision 0d6bb4c6728fd20087fe25f4028a3838250e6e9c)
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