xref: /onnv-gate/usr/src/uts/common/io/dls/dls_link.c (revision 1502:dee1cf667d72)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*1502Sericheng  * Common Development and Distribution License (the "License").
6*1502Sericheng  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*1502Sericheng  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Data-Link Services Module
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include	<sys/types.h>
330Sstevel@tonic-gate #include	<sys/stream.h>
340Sstevel@tonic-gate #include	<sys/strsun.h>
350Sstevel@tonic-gate #include	<sys/strsubr.h>
360Sstevel@tonic-gate #include	<sys/sysmacros.h>
370Sstevel@tonic-gate #include	<sys/atomic.h>
38269Sericheng #include	<sys/modhash.h>
390Sstevel@tonic-gate #include	<sys/dlpi.h>
400Sstevel@tonic-gate #include	<sys/ethernet.h>
410Sstevel@tonic-gate #include	<sys/byteorder.h>
420Sstevel@tonic-gate #include	<sys/vlan.h>
430Sstevel@tonic-gate #include	<sys/mac.h>
440Sstevel@tonic-gate #include	<sys/sdt.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #include	<sys/dls.h>
470Sstevel@tonic-gate #include	<sys/dld_impl.h>
480Sstevel@tonic-gate #include	<sys/dls_impl.h>
490Sstevel@tonic-gate 
500Sstevel@tonic-gate static kmem_cache_t	*i_dls_link_cachep;
51269Sericheng static mod_hash_t	*i_dls_link_hash;
52269Sericheng static uint_t		i_dls_link_count;
53269Sericheng static krwlock_t	i_dls_link_lock;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate #define		LINK_HASHSZ	67	/* prime */
560Sstevel@tonic-gate #define		IMPL_HASHSZ	67	/* prime */
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  * Construct a hash key encompassing both DLSAP value and VLAN idenitifier.
600Sstevel@tonic-gate  */
610Sstevel@tonic-gate #define	MAKE_KEY(_sap, _vid)						\
62269Sericheng 	((mod_hash_key_t)(uintptr_t)					\
63269Sericheng 	(((_sap) << VLAN_ID_SIZE) | (_vid) & VLAN_ID_MASK))
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * Extract the DLSAP value from the hash key.
670Sstevel@tonic-gate  */
680Sstevel@tonic-gate #define	KEY_SAP(_key)							\
690Sstevel@tonic-gate 	(((uint32_t)(uintptr_t)(_key)) >> VLAN_ID_SIZE)
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate  * Private functions.
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*ARGSUSED*/
760Sstevel@tonic-gate static int
770Sstevel@tonic-gate i_dls_link_constructor(void *buf, void *arg, int kmflag)
780Sstevel@tonic-gate {
790Sstevel@tonic-gate 	dls_link_t	*dlp = buf;
800Sstevel@tonic-gate 	char		name[MAXNAMELEN];
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	bzero(buf, sizeof (dls_link_t));
830Sstevel@tonic-gate 
84269Sericheng 	(void) sprintf(name, "dls_link_t_%p_hash", buf);
85269Sericheng 	dlp->dl_impl_hash = mod_hash_create_idhash(name, IMPL_HASHSZ,
86269Sericheng 	    mod_hash_null_valdtor);
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	mutex_init(&dlp->dl_lock, NULL, MUTEX_DEFAULT, NULL);
8956Smeem 	mutex_init(&dlp->dl_promisc_lock, NULL, MUTEX_DEFAULT, NULL);
90269Sericheng 	rw_init(&dlp->dl_impl_lock, NULL, RW_DEFAULT, NULL);
910Sstevel@tonic-gate 	return (0);
920Sstevel@tonic-gate }
930Sstevel@tonic-gate 
940Sstevel@tonic-gate /*ARGSUSED*/
950Sstevel@tonic-gate static void
960Sstevel@tonic-gate i_dls_link_destructor(void *buf, void *arg)
970Sstevel@tonic-gate {
980Sstevel@tonic-gate 	dls_link_t	*dlp = buf;
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate 	ASSERT(dlp->dl_ref == 0);
1010Sstevel@tonic-gate 	ASSERT(dlp->dl_mh == NULL);
1020Sstevel@tonic-gate 	ASSERT(dlp->dl_unknowns == 0);
1030Sstevel@tonic-gate 
104269Sericheng 	mod_hash_destroy_idhash(dlp->dl_impl_hash);
105269Sericheng 	dlp->dl_impl_hash = NULL;
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	mutex_destroy(&dlp->dl_lock);
10856Smeem 	mutex_destroy(&dlp->dl_promisc_lock);
109269Sericheng 	rw_destroy(&dlp->dl_impl_lock);
1100Sstevel@tonic-gate }
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate #define	ETHER_MATCH(_pkt_a, _pkt_b)					\
1130Sstevel@tonic-gate 	((((uint16_t *)(_pkt_a))[0] == ((uint16_t *)(_pkt_b))[0]) &&	\
1140Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[1] == ((uint16_t *)(_pkt_b))[1]) &&	\
1150Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[2] == ((uint16_t *)(_pkt_b))[2]) &&	\
1160Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[6] == ((uint16_t *)(_pkt_b))[6]))
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate #define	ETHER_VLAN_MATCH(_pkt_a, _pkt_b)				\
1190Sstevel@tonic-gate 	((((uint16_t *)(_pkt_a))[0] == ((uint16_t *)(_pkt_b))[0]) &&	\
1200Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[1] == ((uint16_t *)(_pkt_b))[1]) &&	\
1210Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[2] == ((uint16_t *)(_pkt_b))[2]) &&	\
1220Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[6] == ((uint16_t *)(_pkt_b))[6]) &&	\
1230Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[7] == ((uint16_t *)(_pkt_b))[7]) &&	\
1240Sstevel@tonic-gate 	(((uint16_t *)(_pkt_a))[8] == ((uint16_t *)(_pkt_b))[8]))
1250Sstevel@tonic-gate 
126*1502Sericheng #define	ETHER_STRIP_PADDING(typelen, hdrlen, p) {		\
127*1502Sericheng 	if (typelen <= ETHERMTU) {				\
128*1502Sericheng 		ssize_t delta = typelen + hdrlen - msgdsize(p);	\
129*1502Sericheng 								\
130*1502Sericheng 		if (delta < 0)					\
131*1502Sericheng 			(void) adjmsg(p, delta);		\
132*1502Sericheng 	}							\
133*1502Sericheng }
134*1502Sericheng 
1350Sstevel@tonic-gate static mblk_t *
1360Sstevel@tonic-gate i_dls_link_ether_subchain(mblk_t *mp, uint_t *header_lengthp,
1370Sstevel@tonic-gate     uint8_t **daddrp, uint16_t *type_lengthp, uint16_t *vidp,
1380Sstevel@tonic-gate     uint_t *countp)
1390Sstevel@tonic-gate {
1400Sstevel@tonic-gate 	struct ether_header		*ehp;
1410Sstevel@tonic-gate 	struct ether_vlan_header	*evhp;
1420Sstevel@tonic-gate 	mblk_t				**pp;
1430Sstevel@tonic-gate 	mblk_t				*p;
1440Sstevel@tonic-gate 	uint_t				npacket;
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	/*
1470Sstevel@tonic-gate 	 * Packets should always be at least 16 bit aligned.
1480Sstevel@tonic-gate 	 */
1490Sstevel@tonic-gate 	ASSERT(IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)));
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	/*
1520Sstevel@tonic-gate 	 * Determine whether this is a VLAN or non-VLAN packet.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	ASSERT(MBLKL(mp) >= sizeof (struct ether_header));
1550Sstevel@tonic-gate 	ehp = (struct ether_header *)mp->b_rptr;
1560Sstevel@tonic-gate 	if ((*type_lengthp = ntohs(ehp->ether_type)) == VLAN_TPID)
1570Sstevel@tonic-gate 		goto vlan;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	/*
1600Sstevel@tonic-gate 	 * It is a non-VLAN header.
1610Sstevel@tonic-gate 	 */
1620Sstevel@tonic-gate 	*header_lengthp = sizeof (struct ether_header);
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	/*
1650Sstevel@tonic-gate 	 * Parse the rest of the header information that we need.
1660Sstevel@tonic-gate 	 */
1670Sstevel@tonic-gate 	*daddrp = (uint8_t *)&(ehp->ether_dhost);
1680Sstevel@tonic-gate 	*vidp = VLAN_ID_NONE;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	/*
1710Sstevel@tonic-gate 	 * Compare with subsequent headers until we find one that has
172*1502Sericheng 	 * differing header information. After checking each packet
173*1502Sericheng 	 * strip padding and skip over the header.
1740Sstevel@tonic-gate 	 */
1750Sstevel@tonic-gate 	npacket = 1;
1760Sstevel@tonic-gate 	for (pp = &(mp->b_next); (p = *pp) != NULL; pp = &(p->b_next)) {
1770Sstevel@tonic-gate 		if (!ETHER_MATCH(p->b_rptr, mp->b_rptr) != 0)
1780Sstevel@tonic-gate 			break;
179*1502Sericheng 		ETHER_STRIP_PADDING(*type_lengthp, *header_lengthp, p);
1800Sstevel@tonic-gate 		p->b_rptr += sizeof (struct ether_header);
1810Sstevel@tonic-gate 		npacket++;
1820Sstevel@tonic-gate 	}
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	/*
185*1502Sericheng 	 * Strip padding and skip over the initial packet's header.
1860Sstevel@tonic-gate 	 */
187*1502Sericheng 	ETHER_STRIP_PADDING(*type_lengthp, *header_lengthp, mp);
1880Sstevel@tonic-gate 	mp->b_rptr += sizeof (struct ether_header);
1890Sstevel@tonic-gate 	goto done;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate vlan:
1920Sstevel@tonic-gate 	/*
1930Sstevel@tonic-gate 	 * It is a VLAN header.
1940Sstevel@tonic-gate 	 */
1950Sstevel@tonic-gate 	evhp = (struct ether_vlan_header *)mp->b_rptr;
1960Sstevel@tonic-gate 	*header_lengthp = sizeof (struct ether_vlan_header);
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	/*
1990Sstevel@tonic-gate 	 * Parse the header information.
2000Sstevel@tonic-gate 	 */
2010Sstevel@tonic-gate 	*daddrp = (uint8_t *)&(evhp->ether_dhost);
2020Sstevel@tonic-gate 	*vidp = VLAN_ID(ntohs(evhp->ether_tci));
2030Sstevel@tonic-gate 	*type_lengthp = ntohs(evhp->ether_type);
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	/*
2060Sstevel@tonic-gate 	 * Compare with subsequent headers until we find one that has
207*1502Sericheng 	 * differing header information. After checking each packet
208*1502Sericheng 	 * strip padding and skip over the header.
2090Sstevel@tonic-gate 	 */
2100Sstevel@tonic-gate 	npacket = 1;
2110Sstevel@tonic-gate 	for (pp = &(mp->b_next); (p = *pp) != NULL; pp = &(p->b_next)) {
2120Sstevel@tonic-gate 		if (!ETHER_VLAN_MATCH(p->b_rptr, mp->b_rptr) != 0)
2130Sstevel@tonic-gate 			break;
214*1502Sericheng 		ETHER_STRIP_PADDING(*type_lengthp, *header_lengthp, p);
2150Sstevel@tonic-gate 		p->b_rptr += sizeof (struct ether_vlan_header);
2160Sstevel@tonic-gate 		npacket++;
2170Sstevel@tonic-gate 	}
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	/*
220*1502Sericheng 	 * Strip padding and skip over the initial packet's header.
2210Sstevel@tonic-gate 	 */
222*1502Sericheng 	ETHER_STRIP_PADDING(*type_lengthp, *header_lengthp, mp);
2230Sstevel@tonic-gate 	mp->b_rptr += sizeof (struct ether_vlan_header);
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate done:
2260Sstevel@tonic-gate 	/*
2270Sstevel@tonic-gate 	 * Break the chain at this point and return a pointer to the next
2280Sstevel@tonic-gate 	 * sub-chain.
2290Sstevel@tonic-gate 	 */
2300Sstevel@tonic-gate 	*pp = NULL;
2310Sstevel@tonic-gate 	*countp = npacket;
2320Sstevel@tonic-gate 	return (p);
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate static void
236269Sericheng i_dls_head_hold(dls_head_t *dhp)
237269Sericheng {
238269Sericheng 	atomic_inc_32(&dhp->dh_ref);
239269Sericheng }
240269Sericheng 
241269Sericheng static void
242269Sericheng i_dls_head_rele(dls_head_t *dhp)
243269Sericheng {
244269Sericheng 	atomic_dec_32(&dhp->dh_ref);
245269Sericheng }
246269Sericheng 
247269Sericheng static dls_head_t *
248269Sericheng i_dls_head_alloc(mod_hash_key_t key)
249269Sericheng {
250269Sericheng 	dls_head_t	*dhp;
251269Sericheng 
252269Sericheng 	dhp = kmem_zalloc(sizeof (dls_head_t), KM_SLEEP);
253269Sericheng 	dhp->dh_key = key;
254269Sericheng 	return (dhp);
255269Sericheng }
256269Sericheng 
257269Sericheng static void
258269Sericheng i_dls_head_free(dls_head_t *dhp)
259269Sericheng {
260269Sericheng 	ASSERT(dhp->dh_ref == 0);
261269Sericheng 	kmem_free(dhp, sizeof (dls_head_t));
262269Sericheng }
263269Sericheng 
264269Sericheng static void
2650Sstevel@tonic-gate i_dls_link_ether_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp)
2660Sstevel@tonic-gate {
2670Sstevel@tonic-gate 	dls_link_t			*dlp = arg;
268269Sericheng 	mod_hash_t			*hash = dlp->dl_impl_hash;
2690Sstevel@tonic-gate 	mblk_t				*nextp;
2700Sstevel@tonic-gate 	uint_t				header_length;
2710Sstevel@tonic-gate 	uint8_t				*daddr;
2720Sstevel@tonic-gate 	uint16_t			type_length;
2730Sstevel@tonic-gate 	uint16_t			vid;
2740Sstevel@tonic-gate 	uint16_t			sap;
275269Sericheng 	dls_head_t			*dhp;
2760Sstevel@tonic-gate 	dls_impl_t			*dip;
2770Sstevel@tonic-gate 	dls_impl_t			*ndip;
2780Sstevel@tonic-gate 	mblk_t				*nmp;
279269Sericheng 	mod_hash_key_t			key;
2800Sstevel@tonic-gate 	uint_t				npacket;
2810Sstevel@tonic-gate 	boolean_t			accepted;
282449Sericheng 	dls_rx_t			di_rx, ndi_rx;
283449Sericheng 	void				*di_rx_arg, *ndi_rx_arg;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	/*
2860Sstevel@tonic-gate 	 * Walk the packet chain.
2870Sstevel@tonic-gate 	 */
2880Sstevel@tonic-gate 	while (mp != NULL) {
2890Sstevel@tonic-gate 		/*
2900Sstevel@tonic-gate 		 * Wipe the accepted state.
2910Sstevel@tonic-gate 		 */
2920Sstevel@tonic-gate 		accepted = B_FALSE;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 		/*
2950Sstevel@tonic-gate 		 * Grab the longest sub-chain we can process as a single
2960Sstevel@tonic-gate 		 * unit.
2970Sstevel@tonic-gate 		 */
2980Sstevel@tonic-gate 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
2990Sstevel@tonic-gate 		    &type_length, &vid, &npacket);
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 		/*
3020Sstevel@tonic-gate 		 * Calculate the DLSAP: LLC (0) if the type/length field is
3030Sstevel@tonic-gate 		 * interpreted as a length, otherwise it is the value of the
3040Sstevel@tonic-gate 		 * type/length field.
3050Sstevel@tonic-gate 		 */
3060Sstevel@tonic-gate 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 		/*
3090Sstevel@tonic-gate 		 * Construct a hash key from the VLAN identifier and the
3100Sstevel@tonic-gate 		 * DLSAP.
3110Sstevel@tonic-gate 		 */
3120Sstevel@tonic-gate 		key = MAKE_KEY(sap, vid);
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 		/*
3150Sstevel@tonic-gate 		 * Search the has table for dls_impl_t eligible to receive
3160Sstevel@tonic-gate 		 * a packet chain for this DLSAP/VLAN combination.
3170Sstevel@tonic-gate 		 */
318269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_READER);
319269Sericheng 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
320269Sericheng 			rw_exit(&dlp->dl_impl_lock);
3210Sstevel@tonic-gate 			freemsgchain(mp);
3220Sstevel@tonic-gate 			goto loop;
3230Sstevel@tonic-gate 		}
324269Sericheng 		i_dls_head_hold(dhp);
325269Sericheng 		rw_exit(&dlp->dl_impl_lock);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 		/*
3280Sstevel@tonic-gate 		 * Find the first dls_impl_t that will accept the sub-chain.
3290Sstevel@tonic-gate 		 */
330269Sericheng 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
331449Sericheng 			if (dls_accept(dip, daddr, &di_rx, &di_rx_arg))
3320Sstevel@tonic-gate 				break;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 		/*
3350Sstevel@tonic-gate 		 * If we did not find any dls_impl_t willing to accept the
3360Sstevel@tonic-gate 		 * sub-chain then throw it away.
3370Sstevel@tonic-gate 		 */
3380Sstevel@tonic-gate 		if (dip == NULL) {
339269Sericheng 			i_dls_head_rele(dhp);
3400Sstevel@tonic-gate 			freemsgchain(mp);
3410Sstevel@tonic-gate 			goto loop;
3420Sstevel@tonic-gate 		}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		/*
3450Sstevel@tonic-gate 		 * We have at least one acceptor.
3460Sstevel@tonic-gate 		 */
3470Sstevel@tonic-gate 		accepted = B_TRUE;
3480Sstevel@tonic-gate 		for (;;) {
3490Sstevel@tonic-gate 			/*
3500Sstevel@tonic-gate 			 * Find the next dls_impl_t that will accept the
3510Sstevel@tonic-gate 			 * sub-chain.
3520Sstevel@tonic-gate 			 */
3530Sstevel@tonic-gate 			for (ndip = dip->di_nextp; ndip != NULL;
3540Sstevel@tonic-gate 			    ndip = ndip->di_nextp)
355449Sericheng 				if (dls_accept(ndip, daddr, &ndi_rx,
356449Sericheng 				    &ndi_rx_arg))
3570Sstevel@tonic-gate 					break;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 			/*
3600Sstevel@tonic-gate 			 * If there are no more dls_impl_t that are willing
3610Sstevel@tonic-gate 			 * to accept the sub-chain then we don't need to dup
3620Sstevel@tonic-gate 			 * it before handing it to the current one.
3630Sstevel@tonic-gate 			 */
3640Sstevel@tonic-gate 			if (ndip == NULL) {
365449Sericheng 				di_rx(di_rx_arg, mrh, mp, header_length);
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 				/*
3680Sstevel@tonic-gate 				 * Since there are no more dls_impl_t, we're
3690Sstevel@tonic-gate 				 * done.
3700Sstevel@tonic-gate 				 */
3710Sstevel@tonic-gate 				break;
3720Sstevel@tonic-gate 			}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 			/*
3750Sstevel@tonic-gate 			 * There are more dls_impl_t so dup the sub-chain.
3760Sstevel@tonic-gate 			 */
3770Sstevel@tonic-gate 			if ((nmp = copymsgchain(mp)) != NULL)
378449Sericheng 				di_rx(di_rx_arg, mrh, nmp, header_length);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 			dip = ndip;
381449Sericheng 			di_rx = ndi_rx;
382449Sericheng 			di_rx_arg = ndi_rx_arg;
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 		/*
3860Sstevel@tonic-gate 		 * Release the hold on the dls_impl_t chain now that we have
3870Sstevel@tonic-gate 		 * finished walking it.
3880Sstevel@tonic-gate 		 */
389269Sericheng 		i_dls_head_rele(dhp);
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate loop:
3920Sstevel@tonic-gate 		/*
3930Sstevel@tonic-gate 		 * If there were no acceptors then add the packet count to the
3940Sstevel@tonic-gate 		 * 'unknown' count.
3950Sstevel@tonic-gate 		 */
3960Sstevel@tonic-gate 		if (!accepted)
3970Sstevel@tonic-gate 			atomic_add_32(&(dlp->dl_unknowns), npacket);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 		/*
4000Sstevel@tonic-gate 		 * Move onto the next sub-chain.
4010Sstevel@tonic-gate 		 */
4020Sstevel@tonic-gate 		mp = nextp;
4030Sstevel@tonic-gate 	}
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate static void
4070Sstevel@tonic-gate i_dls_link_ether_rx_promisc(void *arg, mac_resource_handle_t mrh,
4080Sstevel@tonic-gate     mblk_t *mp)
4090Sstevel@tonic-gate {
4100Sstevel@tonic-gate 	dls_link_t			*dlp = arg;
411269Sericheng 	mod_hash_t			*hash = dlp->dl_impl_hash;
4120Sstevel@tonic-gate 	mblk_t				*nextp;
4130Sstevel@tonic-gate 	uint_t				header_length;
4140Sstevel@tonic-gate 	uint8_t				*daddr;
4150Sstevel@tonic-gate 	uint16_t			type_length;
4160Sstevel@tonic-gate 	uint16_t			vid;
4170Sstevel@tonic-gate 	uint16_t			sap;
418269Sericheng 	dls_head_t			*dhp;
4190Sstevel@tonic-gate 	dls_impl_t			*dip;
4200Sstevel@tonic-gate 	dls_impl_t			*ndip;
4210Sstevel@tonic-gate 	mblk_t				*nmp;
422269Sericheng 	mod_hash_key_t			key;
4230Sstevel@tonic-gate 	uint_t				npacket;
4240Sstevel@tonic-gate 	boolean_t			accepted;
425449Sericheng 	dls_rx_t			di_rx, ndi_rx;
426449Sericheng 	void				*di_rx_arg, *ndi_rx_arg;
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	/*
4290Sstevel@tonic-gate 	 * Walk the packet chain.
4300Sstevel@tonic-gate 	 */
4310Sstevel@tonic-gate 	while (mp != NULL) {
4320Sstevel@tonic-gate 		/*
4330Sstevel@tonic-gate 		 * Wipe the accepted state.
4340Sstevel@tonic-gate 		 */
4350Sstevel@tonic-gate 		accepted = B_FALSE;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 		/*
4380Sstevel@tonic-gate 		 * Grab the longest sub-chain we can process as a single
4390Sstevel@tonic-gate 		 * unit.
4400Sstevel@tonic-gate 		 */
4410Sstevel@tonic-gate 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
4420Sstevel@tonic-gate 		    &type_length, &vid, &npacket);
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 		/*
4450Sstevel@tonic-gate 		 * Construct a hash key from the VLAN identifier and the
4460Sstevel@tonic-gate 		 * DLSAP that represents dls_impl_t in promiscuous mode.
4470Sstevel@tonic-gate 		 */
4480Sstevel@tonic-gate 		key = MAKE_KEY(DLS_SAP_PROMISC, vid);
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 		/*
4510Sstevel@tonic-gate 		 * Search the has table for dls_impl_t eligible to receive
4520Sstevel@tonic-gate 		 * a packet chain for this DLSAP/VLAN combination.
4530Sstevel@tonic-gate 		 */
454269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_READER);
455269Sericheng 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
456269Sericheng 			rw_exit(&dlp->dl_impl_lock);
4570Sstevel@tonic-gate 			goto non_promisc;
4580Sstevel@tonic-gate 		}
459269Sericheng 		i_dls_head_hold(dhp);
460269Sericheng 		rw_exit(&dlp->dl_impl_lock);
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 		/*
4630Sstevel@tonic-gate 		 * Find dls_impl_t that will accept the sub-chain.
4640Sstevel@tonic-gate 		 */
465269Sericheng 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp) {
466449Sericheng 			if (!dls_accept(dip, daddr, &di_rx, &di_rx_arg))
4670Sstevel@tonic-gate 				continue;
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 			/*
4700Sstevel@tonic-gate 			 * We have at least one acceptor.
4710Sstevel@tonic-gate 			 */
4720Sstevel@tonic-gate 			accepted = B_TRUE;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 			/*
4750Sstevel@tonic-gate 			 * There will normally be at least more dls_impl_t
4760Sstevel@tonic-gate 			 * (since we've yet to check for non-promiscuous
4770Sstevel@tonic-gate 			 * dls_impl_t) so dup the sub-chain.
4780Sstevel@tonic-gate 			 */
4790Sstevel@tonic-gate 			if ((nmp = copymsgchain(mp)) != NULL)
480449Sericheng 				di_rx(di_rx_arg, mrh, nmp, header_length);
4810Sstevel@tonic-gate 		}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		/*
4840Sstevel@tonic-gate 		 * Release the hold on the dls_impl_t chain now that we have
4850Sstevel@tonic-gate 		 * finished walking it.
4860Sstevel@tonic-gate 		 */
487269Sericheng 		i_dls_head_rele(dhp);
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate non_promisc:
4900Sstevel@tonic-gate 		/*
4910Sstevel@tonic-gate 		 * Calculate the DLSAP: LLC (0) if the type/length field is
4920Sstevel@tonic-gate 		 * interpreted as a length, otherwise it is the value of the
4930Sstevel@tonic-gate 		 * type/length field.
4940Sstevel@tonic-gate 		 */
4950Sstevel@tonic-gate 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 		/*
4980Sstevel@tonic-gate 		 * Construct a hash key from the VLAN identifier and the
4990Sstevel@tonic-gate 		 * DLSAP.
5000Sstevel@tonic-gate 		 */
5010Sstevel@tonic-gate 		key = MAKE_KEY(sap, vid);
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		/*
5040Sstevel@tonic-gate 		 * Search the has table for dls_impl_t eligible to receive
5050Sstevel@tonic-gate 		 * a packet chain for this DLSAP/VLAN combination.
5060Sstevel@tonic-gate 		 */
507269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_READER);
508269Sericheng 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
509269Sericheng 			rw_exit(&dlp->dl_impl_lock);
5100Sstevel@tonic-gate 			freemsgchain(mp);
5110Sstevel@tonic-gate 			goto loop;
5120Sstevel@tonic-gate 		}
513269Sericheng 		i_dls_head_hold(dhp);
514269Sericheng 		rw_exit(&dlp->dl_impl_lock);
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 		/*
5170Sstevel@tonic-gate 		 * Find the first dls_impl_t that will accept the sub-chain.
5180Sstevel@tonic-gate 		 */
519269Sericheng 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
520449Sericheng 			if (dls_accept(dip, daddr, &di_rx, &di_rx_arg))
5210Sstevel@tonic-gate 				break;
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		/*
5240Sstevel@tonic-gate 		 * If we did not find any dls_impl_t willing to accept the
5250Sstevel@tonic-gate 		 * sub-chain then throw it away.
5260Sstevel@tonic-gate 		 */
5270Sstevel@tonic-gate 		if (dip == NULL) {
528269Sericheng 			i_dls_head_rele(dhp);
5290Sstevel@tonic-gate 			freemsgchain(mp);
5300Sstevel@tonic-gate 			goto loop;
5310Sstevel@tonic-gate 		}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 		/*
5340Sstevel@tonic-gate 		 * We have at least one acceptor.
5350Sstevel@tonic-gate 		 */
5360Sstevel@tonic-gate 		accepted = B_TRUE;
5370Sstevel@tonic-gate 		for (;;) {
5380Sstevel@tonic-gate 			/*
5390Sstevel@tonic-gate 			 * Find the next dls_impl_t that will accept the
5400Sstevel@tonic-gate 			 * sub-chain.
5410Sstevel@tonic-gate 			 */
5420Sstevel@tonic-gate 			for (ndip = dip->di_nextp; ndip != NULL;
5430Sstevel@tonic-gate 			    ndip = ndip->di_nextp)
544449Sericheng 				if (dls_accept(ndip, daddr, &ndi_rx,
545449Sericheng 				    &ndi_rx_arg))
5460Sstevel@tonic-gate 					break;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 			/*
5490Sstevel@tonic-gate 			 * If there are no more dls_impl_t that are willing
5500Sstevel@tonic-gate 			 * to accept the sub-chain then we don't need to dup
5510Sstevel@tonic-gate 			 * it before handing it to the current one.
5520Sstevel@tonic-gate 			 */
5530Sstevel@tonic-gate 			if (ndip == NULL) {
554449Sericheng 				di_rx(di_rx_arg, mrh, mp, header_length);
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 				/*
5570Sstevel@tonic-gate 				 * Since there are no more dls_impl_t, we're
5580Sstevel@tonic-gate 				 * done.
5590Sstevel@tonic-gate 				 */
5600Sstevel@tonic-gate 				break;
5610Sstevel@tonic-gate 			}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 			/*
5640Sstevel@tonic-gate 			 * There are more dls_impl_t so dup the sub-chain.
5650Sstevel@tonic-gate 			 */
5660Sstevel@tonic-gate 			if ((nmp = copymsgchain(mp)) != NULL)
567449Sericheng 				di_rx(di_rx_arg, mrh, nmp, header_length);
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 			dip = ndip;
570449Sericheng 			di_rx = ndi_rx;
571449Sericheng 			di_rx_arg = ndi_rx_arg;
5720Sstevel@tonic-gate 		}
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		/*
5750Sstevel@tonic-gate 		 * Release the hold on the dls_impl_t chain now that we have
5760Sstevel@tonic-gate 		 * finished walking it.
5770Sstevel@tonic-gate 		 */
578269Sericheng 		i_dls_head_rele(dhp);
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate loop:
5810Sstevel@tonic-gate 		/*
5820Sstevel@tonic-gate 		 * If there were no acceptors then add the packet count to the
5830Sstevel@tonic-gate 		 * 'unknown' count.
5840Sstevel@tonic-gate 		 */
5850Sstevel@tonic-gate 		if (!accepted)
5860Sstevel@tonic-gate 			atomic_add_32(&(dlp->dl_unknowns), npacket);
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 		/*
5890Sstevel@tonic-gate 		 * Move onto the next sub-chain.
5900Sstevel@tonic-gate 		 */
5910Sstevel@tonic-gate 		mp = nextp;
5920Sstevel@tonic-gate 	}
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate static void
5960Sstevel@tonic-gate i_dls_link_ether_loopback(void *arg, mblk_t *mp)
5970Sstevel@tonic-gate {
5980Sstevel@tonic-gate 	dls_link_t			*dlp = arg;
599269Sericheng 	mod_hash_t			*hash = dlp->dl_impl_hash;
6000Sstevel@tonic-gate 	mblk_t				*nextp;
6010Sstevel@tonic-gate 	uint_t				header_length;
6020Sstevel@tonic-gate 	uint8_t				*daddr;
6030Sstevel@tonic-gate 	uint16_t			type_length;
6040Sstevel@tonic-gate 	uint16_t			vid;
6050Sstevel@tonic-gate 	uint16_t			sap;
606269Sericheng 	dls_head_t			*dhp;
6070Sstevel@tonic-gate 	dls_impl_t			*dip;
6080Sstevel@tonic-gate 	dls_impl_t			*ndip;
6090Sstevel@tonic-gate 	mblk_t				*nmp;
610269Sericheng 	mod_hash_key_t			key;
6110Sstevel@tonic-gate 	uint_t				npacket;
612449Sericheng 	dls_rx_t			di_rx, ndi_rx;
613449Sericheng 	void				*di_rx_arg, *ndi_rx_arg;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	/*
6160Sstevel@tonic-gate 	 * Walk the packet chain.
6170Sstevel@tonic-gate 	 */
6180Sstevel@tonic-gate 	while (mp != NULL) {
6190Sstevel@tonic-gate 		/*
6200Sstevel@tonic-gate 		 * Grab the longest sub-chain we can process as a single
6210Sstevel@tonic-gate 		 * unit.
6220Sstevel@tonic-gate 		 */
6230Sstevel@tonic-gate 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
6240Sstevel@tonic-gate 		    &type_length, &vid, &npacket);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		/*
6270Sstevel@tonic-gate 		 * Calculate the DLSAP: LLC (0) if the type/length field is
6280Sstevel@tonic-gate 		 * interpreted as a length, otherwise it is the value of the
6290Sstevel@tonic-gate 		 * type/length field.
6300Sstevel@tonic-gate 		 */
6310Sstevel@tonic-gate 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		/*
6340Sstevel@tonic-gate 		 * Construct a hash key from the VLAN identifier and the
6350Sstevel@tonic-gate 		 * DLSAP.
6360Sstevel@tonic-gate 		 */
6370Sstevel@tonic-gate 		key = MAKE_KEY(sap, vid);
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 		/*
6400Sstevel@tonic-gate 		 * Search the has table for dls_impl_t eligible to receive
6410Sstevel@tonic-gate 		 * a packet chain for this DLSAP/VLAN combination.
6420Sstevel@tonic-gate 		 */
643269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_READER);
644269Sericheng 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
645269Sericheng 			rw_exit(&dlp->dl_impl_lock);
6460Sstevel@tonic-gate 			goto promisc;
6470Sstevel@tonic-gate 		}
648269Sericheng 		i_dls_head_hold(dhp);
649269Sericheng 		rw_exit(&dlp->dl_impl_lock);
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 		/*
6520Sstevel@tonic-gate 		 * Find dls_impl_t that will accept the sub-chain.
6530Sstevel@tonic-gate 		 */
654269Sericheng 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp) {
655449Sericheng 			if (!dls_accept_loopback(dip, daddr, &di_rx,
656449Sericheng 			    &di_rx_arg))
6570Sstevel@tonic-gate 				continue;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 			/*
6600Sstevel@tonic-gate 			 * There should be at least more dls_impl_t (since
6610Sstevel@tonic-gate 			 * we've yet to check for dls_impl_t in promiscuous
6620Sstevel@tonic-gate 			 * mode) so dup the sub-chain.
6630Sstevel@tonic-gate 			 */
6640Sstevel@tonic-gate 			if ((nmp = copymsgchain(mp)) != NULL)
665449Sericheng 				di_rx(di_rx_arg, NULL, nmp, header_length);
6660Sstevel@tonic-gate 		}
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 		/*
6690Sstevel@tonic-gate 		 * Release the hold on the dls_impl_t chain now that we have
6700Sstevel@tonic-gate 		 * finished walking it.
6710Sstevel@tonic-gate 		 */
672269Sericheng 		i_dls_head_rele(dhp);
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate promisc:
6750Sstevel@tonic-gate 		/*
6760Sstevel@tonic-gate 		 * Construct a hash key from the VLAN identifier and the
6770Sstevel@tonic-gate 		 * DLSAP that represents dls_impl_t in promiscuous mode.
6780Sstevel@tonic-gate 		 */
6790Sstevel@tonic-gate 		key = MAKE_KEY(DLS_SAP_PROMISC, vid);
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 		/*
6820Sstevel@tonic-gate 		 * Search the has table for dls_impl_t eligible to receive
6830Sstevel@tonic-gate 		 * a packet chain for this DLSAP/VLAN combination.
6840Sstevel@tonic-gate 		 */
685269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_READER);
686269Sericheng 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
687269Sericheng 			rw_exit(&dlp->dl_impl_lock);
6880Sstevel@tonic-gate 			freemsgchain(mp);
6890Sstevel@tonic-gate 			goto loop;
6900Sstevel@tonic-gate 		}
691269Sericheng 		i_dls_head_hold(dhp);
692269Sericheng 		rw_exit(&dlp->dl_impl_lock);
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 		/*
6950Sstevel@tonic-gate 		 * Find the first dls_impl_t that will accept the sub-chain.
6960Sstevel@tonic-gate 		 */
697269Sericheng 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
698449Sericheng 			if (dls_accept_loopback(dip, daddr, &di_rx, &di_rx_arg))
6990Sstevel@tonic-gate 				break;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 		/*
7020Sstevel@tonic-gate 		 * If we did not find any dls_impl_t willing to accept the
7030Sstevel@tonic-gate 		 * sub-chain then throw it away.
7040Sstevel@tonic-gate 		 */
7050Sstevel@tonic-gate 		if (dip == NULL) {
706269Sericheng 			i_dls_head_rele(dhp);
7070Sstevel@tonic-gate 			freemsgchain(mp);
7080Sstevel@tonic-gate 			goto loop;
7090Sstevel@tonic-gate 		}
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 		for (;;) {
7120Sstevel@tonic-gate 			/*
7130Sstevel@tonic-gate 			 * Find the next dls_impl_t that will accept the
7140Sstevel@tonic-gate 			 * sub-chain.
7150Sstevel@tonic-gate 			 */
7160Sstevel@tonic-gate 			for (ndip = dip->di_nextp; ndip != NULL;
7170Sstevel@tonic-gate 			    ndip = ndip->di_nextp)
718449Sericheng 				if (dls_accept_loopback(ndip, daddr,
719449Sericheng 				    &ndi_rx, &ndi_rx_arg))
7200Sstevel@tonic-gate 					break;
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 			/*
7230Sstevel@tonic-gate 			 * If there are no more dls_impl_t that are willing
7240Sstevel@tonic-gate 			 * to accept the sub-chain then we don't need to dup
7250Sstevel@tonic-gate 			 * it before handing it to the current one.
7260Sstevel@tonic-gate 			 */
7270Sstevel@tonic-gate 			if (ndip == NULL) {
728449Sericheng 				di_rx(di_rx_arg, NULL, mp, header_length);
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 				/*
7310Sstevel@tonic-gate 				 * Since there are no more dls_impl_t, we're
7320Sstevel@tonic-gate 				 * done.
7330Sstevel@tonic-gate 				 */
7340Sstevel@tonic-gate 				break;
7350Sstevel@tonic-gate 			}
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 			/*
7380Sstevel@tonic-gate 			 * There are more dls_impl_t so dup the sub-chain.
7390Sstevel@tonic-gate 			 */
7400Sstevel@tonic-gate 			if ((nmp = copymsgchain(mp)) != NULL)
741449Sericheng 				di_rx(di_rx_arg, NULL, nmp, header_length);
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 			dip = ndip;
744449Sericheng 			di_rx = ndi_rx;
745449Sericheng 			di_rx_arg = ndi_rx_arg;
7460Sstevel@tonic-gate 		}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 		/*
7490Sstevel@tonic-gate 		 * Release the hold on the dls_impl_t chain now that we have
7500Sstevel@tonic-gate 		 * finished walking it.
7510Sstevel@tonic-gate 		 */
752269Sericheng 		i_dls_head_rele(dhp);
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate loop:
7550Sstevel@tonic-gate 		/*
7560Sstevel@tonic-gate 		 * Move onto the next sub-chain.
7570Sstevel@tonic-gate 		 */
7580Sstevel@tonic-gate 		mp = nextp;
7590Sstevel@tonic-gate 	}
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate 
762269Sericheng /*ARGSUSED*/
763269Sericheng static uint_t
764269Sericheng i_dls_link_walk(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
7650Sstevel@tonic-gate {
7660Sstevel@tonic-gate 	boolean_t	*promiscp = arg;
7670Sstevel@tonic-gate 	uint32_t	sap = KEY_SAP(key);
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (sap == DLS_SAP_PROMISC) {
7700Sstevel@tonic-gate 		*promiscp = B_TRUE;
771269Sericheng 		return (MH_WALK_TERMINATE);
7720Sstevel@tonic-gate 	}
7730Sstevel@tonic-gate 
774269Sericheng 	return (MH_WALK_CONTINUE);
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate static int
7780Sstevel@tonic-gate i_dls_link_create(const char *dev, uint_t port, dls_link_t **dlpp)
7790Sstevel@tonic-gate {
7800Sstevel@tonic-gate 	dls_link_t		*dlp;
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	/*
7830Sstevel@tonic-gate 	 * Allocate a new dls_link_t structure.
7840Sstevel@tonic-gate 	 */
7850Sstevel@tonic-gate 	dlp = kmem_cache_alloc(i_dls_link_cachep, KM_SLEEP);
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 	/*
7880Sstevel@tonic-gate 	 * Name the dls_link_t after the MAC interface it represents.
7890Sstevel@tonic-gate 	 */
7900Sstevel@tonic-gate 	MAC_NAME(dlp->dl_name, dev, port);
7910Sstevel@tonic-gate 	(void) strlcpy(dlp->dl_dev, dev, MAXNAMELEN);
7920Sstevel@tonic-gate 	dlp->dl_port = port;
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	/*
7950Sstevel@tonic-gate 	 * Set the packet loopback function for use when the MAC is in
7960Sstevel@tonic-gate 	 * promiscuous mode, and initialize promiscuous bookeeping fields.
7970Sstevel@tonic-gate 	 */
7980Sstevel@tonic-gate 	dlp->dl_loopback = i_dls_link_ether_loopback;
7990Sstevel@tonic-gate 	dlp->dl_npromisc = 0;
8000Sstevel@tonic-gate 	dlp->dl_mth = NULL;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	*dlpp = dlp;
8030Sstevel@tonic-gate 	return (0);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate static void
8070Sstevel@tonic-gate i_dls_link_destroy(dls_link_t *dlp)
8080Sstevel@tonic-gate {
8090Sstevel@tonic-gate 	ASSERT(dlp->dl_npromisc == 0);
8100Sstevel@tonic-gate 	ASSERT(dlp->dl_nactive == 0);
8110Sstevel@tonic-gate 	ASSERT(dlp->dl_mth == NULL);
8120Sstevel@tonic-gate 	ASSERT(dlp->dl_macref == 0);
8130Sstevel@tonic-gate 	ASSERT(dlp->dl_mh == NULL);
8140Sstevel@tonic-gate 	ASSERT(dlp->dl_mip == NULL);
815269Sericheng 	ASSERT(dlp->dl_impl_count == 0);
816269Sericheng 	ASSERT(dlp->dl_mrh == NULL);
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	/*
8190Sstevel@tonic-gate 	 * Free the structure back to the cache.
8200Sstevel@tonic-gate 	 */
8210Sstevel@tonic-gate 	dlp->dl_unknowns = 0;
8220Sstevel@tonic-gate 	kmem_cache_free(i_dls_link_cachep, dlp);
8230Sstevel@tonic-gate }
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate /*
8260Sstevel@tonic-gate  * Module initialization functions.
8270Sstevel@tonic-gate  */
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate void
8300Sstevel@tonic-gate dls_link_init(void)
8310Sstevel@tonic-gate {
8320Sstevel@tonic-gate 	/*
8330Sstevel@tonic-gate 	 * Create a kmem_cache of dls_link_t structures.
8340Sstevel@tonic-gate 	 */
8350Sstevel@tonic-gate 	i_dls_link_cachep = kmem_cache_create("dls_link_cache",
8360Sstevel@tonic-gate 	    sizeof (dls_link_t), 0, i_dls_link_constructor,
8370Sstevel@tonic-gate 	    i_dls_link_destructor, NULL, NULL, NULL, 0);
8380Sstevel@tonic-gate 	ASSERT(i_dls_link_cachep != NULL);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	/*
841269Sericheng 	 * Create a dls_link_t hash table and associated lock.
8420Sstevel@tonic-gate 	 */
843269Sericheng 	i_dls_link_hash = mod_hash_create_extended("dls_link_hash",
844269Sericheng 	    IMPL_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
845269Sericheng 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
846269Sericheng 	rw_init(&i_dls_link_lock, NULL, RW_DEFAULT, NULL);
847269Sericheng 	i_dls_link_count = 0;
8480Sstevel@tonic-gate }
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate int
8510Sstevel@tonic-gate dls_link_fini(void)
8520Sstevel@tonic-gate {
853269Sericheng 	if (i_dls_link_count > 0)
854269Sericheng 		return (EBUSY);
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	/*
8570Sstevel@tonic-gate 	 * Destroy the kmem_cache.
8580Sstevel@tonic-gate 	 */
8590Sstevel@tonic-gate 	kmem_cache_destroy(i_dls_link_cachep);
860269Sericheng 
861269Sericheng 	/*
862269Sericheng 	 * Destroy the hash table and associated lock.
863269Sericheng 	 */
864269Sericheng 	mod_hash_destroy_hash(i_dls_link_hash);
865269Sericheng 	rw_destroy(&i_dls_link_lock);
8660Sstevel@tonic-gate 	return (0);
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate /*
8700Sstevel@tonic-gate  * Exported functions.
8710Sstevel@tonic-gate  */
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate int
8740Sstevel@tonic-gate dls_link_hold(const char *dev, uint_t port, dls_link_t **dlpp)
8750Sstevel@tonic-gate {
8760Sstevel@tonic-gate 	char			name[MAXNAMELEN];
8770Sstevel@tonic-gate 	dls_link_t		*dlp;
8780Sstevel@tonic-gate 	int			err;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	/*
8810Sstevel@tonic-gate 	 * Construct a copy of the name used to identify any existing
8820Sstevel@tonic-gate 	 * dls_link_t.
8830Sstevel@tonic-gate 	 */
8840Sstevel@tonic-gate 	MAC_NAME(name, dev, port);
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	/*
8870Sstevel@tonic-gate 	 * Look up a dls_link_t corresponding to the given mac_handle_t
888269Sericheng 	 * in the global hash table. We need to hold i_dls_link_lock in
889269Sericheng 	 * order to atomically find and insert a dls_link_t into the
890269Sericheng 	 * hash table.
8910Sstevel@tonic-gate 	 */
892269Sericheng 	rw_enter(&i_dls_link_lock, RW_WRITER);
893269Sericheng 	if ((err = mod_hash_find(i_dls_link_hash, (mod_hash_key_t)name,
894269Sericheng 	    (mod_hash_val_t *)&dlp)) == 0)
8950Sstevel@tonic-gate 		goto done;
896269Sericheng 
897269Sericheng 	ASSERT(err == MH_ERR_NOTFOUND);
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	/*
9000Sstevel@tonic-gate 	 * We didn't find anything so we need to create one.
9010Sstevel@tonic-gate 	 */
9020Sstevel@tonic-gate 	if ((err = i_dls_link_create(dev, port, &dlp)) != 0) {
903269Sericheng 		rw_exit(&i_dls_link_lock);
9040Sstevel@tonic-gate 		return (err);
9050Sstevel@tonic-gate 	}
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	/*
908269Sericheng 	 * Insert the dls_link_t.
9090Sstevel@tonic-gate 	 */
910269Sericheng 	err = mod_hash_insert(i_dls_link_hash, (mod_hash_key_t)dlp->dl_name,
911269Sericheng 	    (mod_hash_val_t)dlp);
9120Sstevel@tonic-gate 	ASSERT(err == 0);
9130Sstevel@tonic-gate 
914269Sericheng 	i_dls_link_count++;
915269Sericheng 	ASSERT(i_dls_link_count != 0);
916269Sericheng 
9170Sstevel@tonic-gate done:
9180Sstevel@tonic-gate 	/*
9190Sstevel@tonic-gate 	 * Bump the reference count and hand back the reference.
9200Sstevel@tonic-gate 	 */
9210Sstevel@tonic-gate 	dlp->dl_ref++;
9220Sstevel@tonic-gate 	*dlpp = dlp;
923269Sericheng 	rw_exit(&i_dls_link_lock);
924269Sericheng 	return (0);
9250Sstevel@tonic-gate }
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate void
9280Sstevel@tonic-gate dls_link_rele(dls_link_t *dlp)
9290Sstevel@tonic-gate {
930269Sericheng 	mod_hash_val_t	val;
9310Sstevel@tonic-gate 
932269Sericheng 	rw_enter(&i_dls_link_lock, RW_WRITER);
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	/*
9350Sstevel@tonic-gate 	 * Check if there are any more references.
9360Sstevel@tonic-gate 	 */
9370Sstevel@tonic-gate 	if (--dlp->dl_ref != 0) {
9380Sstevel@tonic-gate 		/*
9390Sstevel@tonic-gate 		 * There are more references so there's nothing more to do.
9400Sstevel@tonic-gate 		 */
9410Sstevel@tonic-gate 		goto done;
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 
944269Sericheng 	(void) mod_hash_remove(i_dls_link_hash,
945269Sericheng 	    (mod_hash_key_t)dlp->dl_name, &val);
946269Sericheng 	ASSERT(dlp == (dls_link_t *)val);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	/*
9490Sstevel@tonic-gate 	 * Destroy the dls_link_t.
9500Sstevel@tonic-gate 	 */
9510Sstevel@tonic-gate 	i_dls_link_destroy(dlp);
952269Sericheng 	ASSERT(i_dls_link_count > 0);
953269Sericheng 	i_dls_link_count--;
9540Sstevel@tonic-gate done:
955269Sericheng 	rw_exit(&i_dls_link_lock);
9560Sstevel@tonic-gate }
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate int
9590Sstevel@tonic-gate dls_mac_hold(dls_link_t *dlp)
9600Sstevel@tonic-gate {
9610Sstevel@tonic-gate 	int err = 0;
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	mutex_enter(&dlp->dl_lock);
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_macref != 0, dlp->dl_mh != NULL));
9660Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_macref == 0, dlp->dl_mh == NULL));
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	if (dlp->dl_macref == 0) {
9690Sstevel@tonic-gate 		/*
9700Sstevel@tonic-gate 		 * First reference; hold open the MAC interface.
9710Sstevel@tonic-gate 		 */
9720Sstevel@tonic-gate 		err = mac_open(dlp->dl_dev, dlp->dl_port, &dlp->dl_mh);
9730Sstevel@tonic-gate 		if (err != 0)
9740Sstevel@tonic-gate 			goto done;
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 		dlp->dl_mip = mac_info(dlp->dl_mh);
9770Sstevel@tonic-gate 	}
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	dlp->dl_macref++;
9800Sstevel@tonic-gate done:
9810Sstevel@tonic-gate 	mutex_exit(&dlp->dl_lock);
9820Sstevel@tonic-gate 	return (err);
9830Sstevel@tonic-gate }
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate void
9860Sstevel@tonic-gate dls_mac_rele(dls_link_t *dlp)
9870Sstevel@tonic-gate {
9880Sstevel@tonic-gate 	mutex_enter(&dlp->dl_lock);
9890Sstevel@tonic-gate 	ASSERT(dlp->dl_mh != NULL);
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	if (--dlp->dl_macref == 0) {
9920Sstevel@tonic-gate 		mac_close(dlp->dl_mh);
9930Sstevel@tonic-gate 		dlp->dl_mh = NULL;
9940Sstevel@tonic-gate 		dlp->dl_mip = NULL;
9950Sstevel@tonic-gate 	}
9960Sstevel@tonic-gate 	mutex_exit(&dlp->dl_lock);
9970Sstevel@tonic-gate }
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate void
10000Sstevel@tonic-gate dls_link_add(dls_link_t *dlp, uint32_t sap, dls_impl_t *dip)
10010Sstevel@tonic-gate {
10020Sstevel@tonic-gate 	dls_vlan_t	*dvp = dip->di_dvp;
1003269Sericheng 	mod_hash_t	*hash = dlp->dl_impl_hash;
1004269Sericheng 	mod_hash_key_t	key;
1005269Sericheng 	dls_head_t	*dhp;
10060Sstevel@tonic-gate 	dls_impl_t	*p;
10070Sstevel@tonic-gate 	mac_rx_t	rx;
10080Sstevel@tonic-gate 	int		err;
1009269Sericheng 	boolean_t	promisc = B_FALSE;
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	/*
10120Sstevel@tonic-gate 	 * For ethernet media, sap values less than or equal to
10130Sstevel@tonic-gate 	 * ETHERMTU (1500) represent LLC channels. (See PSARC 2003/150).
10140Sstevel@tonic-gate 	 * We strictly use 0 to represent LLC channels.
10150Sstevel@tonic-gate 	 */
10160Sstevel@tonic-gate 	sap = (sap <= ETHERMTU) ? 0 : sap;
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	/*
10190Sstevel@tonic-gate 	 * Make the appropriate key value depending on whether the
10200Sstevel@tonic-gate 	 * dls_impl_t is in promiscuous mode or not.
10210Sstevel@tonic-gate 	 */
10220Sstevel@tonic-gate 	key = MAKE_KEY(sap, dvp->dv_id);
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	/*
10250Sstevel@tonic-gate 	 * We need dl_lock here because we want to be able to walk
10260Sstevel@tonic-gate 	 * the hash table *and* set the mac rx func atomically. if
10270Sstevel@tonic-gate 	 * these two operations are separate, someone else could
1028269Sericheng 	 * insert/remove dls_impl_t from the hash table after we
1029269Sericheng 	 * drop the hash lock and this could cause our chosen rx
1030269Sericheng 	 * func to be incorrect. note that we cannot call mac_rx_add
1031269Sericheng 	 * when holding the hash lock because this can cause deadlock.
10320Sstevel@tonic-gate 	 */
10330Sstevel@tonic-gate 	mutex_enter(&dlp->dl_lock);
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	/*
1036269Sericheng 	 * Search the table for a list head with this key.
10370Sstevel@tonic-gate 	 */
1038269Sericheng 	rw_enter(&dlp->dl_impl_lock, RW_WRITER);
10390Sstevel@tonic-gate 
1040269Sericheng 	if ((err = mod_hash_find(hash, key, (mod_hash_val_t *)&dhp)) != 0) {
1041269Sericheng 		ASSERT(err == MH_ERR_NOTFOUND);
10420Sstevel@tonic-gate 
1043269Sericheng 		dhp = i_dls_head_alloc(key);
1044269Sericheng 		err = mod_hash_insert(hash, key, (mod_hash_val_t)dhp);
1045269Sericheng 		ASSERT(err == 0);
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 	/*
1049269Sericheng 	 * Add the dls_impl_t to the head of the list.
1050269Sericheng 	 */
1051269Sericheng 	ASSERT(dip->di_nextp == NULL);
1052269Sericheng 	p = dhp->dh_list;
1053269Sericheng 	dip->di_nextp = p;
1054269Sericheng 	dhp->dh_list = dip;
1055269Sericheng 
1056269Sericheng 	/*
1057269Sericheng 	 * Save a pointer to the list head.
1058269Sericheng 	 */
1059269Sericheng 	dip->di_headp = dhp;
1060269Sericheng 	dlp->dl_impl_count++;
1061269Sericheng 
1062269Sericheng 	/*
1063269Sericheng 	 * Walk the bound dls_impl_t to see if there are any
1064269Sericheng 	 * in promiscuous 'all sap' mode.
10650Sstevel@tonic-gate 	 */
1066269Sericheng 	mod_hash_walk(hash, i_dls_link_walk, (void *)&promisc);
1067269Sericheng 	rw_exit(&dlp->dl_impl_lock);
1068269Sericheng 
1069269Sericheng 	/*
1070269Sericheng 	 * If there are then we need to use a receive routine
1071269Sericheng 	 * which will route packets to those dls_impl_t as well
1072269Sericheng 	 * as ones bound to the  DLSAP of the packet.
1073269Sericheng 	 */
1074269Sericheng 	if (promisc)
1075269Sericheng 		rx = i_dls_link_ether_rx_promisc;
1076269Sericheng 	else
1077269Sericheng 		rx = i_dls_link_ether_rx;
1078269Sericheng 
1079269Sericheng 	/* Replace the existing receive function if there is one. */
1080269Sericheng 	if (dlp->dl_mrh != NULL)
1081269Sericheng 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
1082269Sericheng 	dlp->dl_mrh = mac_rx_add(dlp->dl_mh, rx, (void *)dlp);
1083269Sericheng 	mutex_exit(&dlp->dl_lock);
1084269Sericheng }
1085269Sericheng 
1086269Sericheng void
1087269Sericheng dls_link_remove(dls_link_t *dlp, dls_impl_t *dip)
1088269Sericheng {
1089269Sericheng 	mod_hash_t	*hash = dlp->dl_impl_hash;
1090269Sericheng 	dls_impl_t	**pp;
1091269Sericheng 	dls_impl_t	*p;
1092269Sericheng 	dls_head_t	*dhp;
1093269Sericheng 	mac_rx_t	rx;
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	/*
1096269Sericheng 	 * We need dl_lock here because we want to be able to walk
1097269Sericheng 	 * the hash table *and* set the mac rx func atomically. if
1098269Sericheng 	 * these two operations are separate, someone else could
1099269Sericheng 	 * insert/remove dls_impl_t from the hash table after we
1100269Sericheng 	 * drop the hash lock and this could cause our chosen rx
1101269Sericheng 	 * func to be incorrect. note that we cannot call mac_rx_add
1102269Sericheng 	 * when holding the hash lock because this can cause deadlock.
11030Sstevel@tonic-gate 	 */
1104269Sericheng 	mutex_enter(&dlp->dl_lock);
1105269Sericheng 	rw_enter(&dlp->dl_impl_lock, RW_WRITER);
11060Sstevel@tonic-gate 
1107269Sericheng 	/*
1108269Sericheng 	 * Poll the hash table entry until all references have been dropped.
1109269Sericheng 	 * We need to drop all locks before sleeping because we don't want
1110269Sericheng 	 * the interrupt handler to block. We set di_removing here to
1111269Sericheng 	 * tell the receive callbacks not to pass up packets anymore.
1112269Sericheng 	 * This is only a hint to quicken the decrease of the refcnt so
1113269Sericheng 	 * the assignment need not be protected by any lock.
1114269Sericheng 	 */
1115269Sericheng 	dhp = dip->di_headp;
1116269Sericheng 	dip->di_removing = B_TRUE;
1117269Sericheng 	while (dhp->dh_ref != 0) {
1118269Sericheng 		rw_exit(&dlp->dl_impl_lock);
1119269Sericheng 		mutex_exit(&dlp->dl_lock);
1120269Sericheng 		delay(drv_usectohz(1000));	/* 1ms delay */
1121269Sericheng 		mutex_enter(&dlp->dl_lock);
1122269Sericheng 		rw_enter(&dlp->dl_impl_lock, RW_WRITER);
1123269Sericheng 	}
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate 	/*
1126269Sericheng 	 * Walk the list and remove the dls_impl_t.
11270Sstevel@tonic-gate 	 */
1128269Sericheng 	for (pp = &dhp->dh_list; (p = *pp) != NULL; pp = &(p->di_nextp)) {
1129269Sericheng 		if (p == dip)
1130269Sericheng 			break;
1131269Sericheng 	}
1132269Sericheng 	ASSERT(p != NULL);
1133269Sericheng 	*pp = p->di_nextp;
1134269Sericheng 	p->di_nextp = NULL;
1135269Sericheng 
1136269Sericheng 	ASSERT(dlp->dl_impl_count > 0);
1137269Sericheng 	dlp->dl_impl_count--;
11380Sstevel@tonic-gate 
1139269Sericheng 	if (dhp->dh_list == NULL) {
1140269Sericheng 		mod_hash_val_t	val = NULL;
1141269Sericheng 
1142269Sericheng 		/*
1143269Sericheng 		 * The list is empty so remove the hash table entry.
1144269Sericheng 		 */
1145269Sericheng 		(void) mod_hash_remove(hash, dhp->dh_key, &val);
1146269Sericheng 		ASSERT(dhp == (dls_head_t *)val);
1147269Sericheng 		i_dls_head_free(dhp);
1148269Sericheng 	}
1149269Sericheng 	dip->di_removing = B_FALSE;
1150269Sericheng 
11510Sstevel@tonic-gate 	/*
1152269Sericheng 	 * If there are no dls_impl_t then there's no need to register a
1153269Sericheng 	 * receive function with the mac.
11540Sstevel@tonic-gate 	 */
1155269Sericheng 	if (dlp->dl_impl_count == 0) {
1156269Sericheng 		rw_exit(&dlp->dl_impl_lock);
1157269Sericheng 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
1158269Sericheng 		dlp->dl_mrh = NULL;
11590Sstevel@tonic-gate 	} else {
11600Sstevel@tonic-gate 		boolean_t promisc = B_FALSE;
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate 		/*
11630Sstevel@tonic-gate 		 * Walk the bound dls_impl_t to see if there are any
11640Sstevel@tonic-gate 		 * in promiscuous 'all sap' mode.
11650Sstevel@tonic-gate 		 */
1166269Sericheng 		mod_hash_walk(hash, i_dls_link_walk, (void *)&promisc);
1167269Sericheng 		rw_exit(&dlp->dl_impl_lock);
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 		/*
11700Sstevel@tonic-gate 		 * If there are then we need to use a receive routine
11710Sstevel@tonic-gate 		 * which will route packets to those dls_impl_t as well
11720Sstevel@tonic-gate 		 * as ones bound to the  DLSAP of the packet.
11730Sstevel@tonic-gate 		 */
11740Sstevel@tonic-gate 		if (promisc)
11750Sstevel@tonic-gate 			rx = i_dls_link_ether_rx_promisc;
11760Sstevel@tonic-gate 		else
11770Sstevel@tonic-gate 			rx = i_dls_link_ether_rx;
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
11800Sstevel@tonic-gate 		dlp->dl_mrh = mac_rx_add(dlp->dl_mh, rx, (void *)dlp);
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 	mutex_exit(&dlp->dl_lock);
11830Sstevel@tonic-gate }
1184