xref: /illumos-gate/usr/src/uts/common/io/dls/dls_link.c (revision fea9cb91bd8e12d84069b4dab1268363668b4bff)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Data-Link Services Module
31  */
32 
33 #include	<sys/types.h>
34 #include	<sys/stream.h>
35 #include	<sys/strsun.h>
36 #include	<sys/strsubr.h>
37 #include	<sys/sysmacros.h>
38 #include	<sys/atomic.h>
39 #include	<sys/modhash.h>
40 #include	<sys/dlpi.h>
41 #include	<sys/ethernet.h>
42 #include	<sys/byteorder.h>
43 #include	<sys/vlan.h>
44 #include	<sys/mac.h>
45 #include	<sys/sdt.h>
46 
47 #include	<sys/dls.h>
48 #include	<sys/dld_impl.h>
49 #include	<sys/dls_impl.h>
50 
51 static kmem_cache_t	*i_dls_link_cachep;
52 static mod_hash_t	*i_dls_link_hash;
53 static uint_t		i_dls_link_count;
54 static krwlock_t	i_dls_link_lock;
55 
56 #define		LINK_HASHSZ	67	/* prime */
57 #define		IMPL_HASHSZ	67	/* prime */
58 
59 /*
60  * Construct a hash key encompassing both DLSAP value and VLAN idenitifier.
61  */
62 #define	MAKE_KEY(_sap, _vid)						\
63 	((mod_hash_key_t)(uintptr_t)					\
64 	(((_sap) << VLAN_ID_SIZE) | (_vid) & VLAN_ID_MASK))
65 
66 /*
67  * Extract the DLSAP value from the hash key.
68  */
69 #define	KEY_SAP(_key)							\
70 	(((uint32_t)(uintptr_t)(_key)) >> VLAN_ID_SIZE)
71 
72 /*
73  * Private functions.
74  */
75 
76 /*ARGSUSED*/
77 static int
78 i_dls_link_constructor(void *buf, void *arg, int kmflag)
79 {
80 	dls_link_t	*dlp = buf;
81 	char		name[MAXNAMELEN];
82 
83 	bzero(buf, sizeof (dls_link_t));
84 
85 	(void) sprintf(name, "dls_link_t_%p_hash", buf);
86 	dlp->dl_impl_hash = mod_hash_create_idhash(name, IMPL_HASHSZ,
87 	    mod_hash_null_valdtor);
88 
89 	mutex_init(&dlp->dl_lock, NULL, MUTEX_DEFAULT, NULL);
90 	mutex_init(&dlp->dl_promisc_lock, NULL, MUTEX_DEFAULT, NULL);
91 	rw_init(&dlp->dl_impl_lock, NULL, RW_DEFAULT, NULL);
92 	return (0);
93 }
94 
95 /*ARGSUSED*/
96 static void
97 i_dls_link_destructor(void *buf, void *arg)
98 {
99 	dls_link_t	*dlp = buf;
100 
101 	ASSERT(dlp->dl_ref == 0);
102 	ASSERT(dlp->dl_mh == NULL);
103 	ASSERT(dlp->dl_unknowns == 0);
104 
105 	mod_hash_destroy_idhash(dlp->dl_impl_hash);
106 	dlp->dl_impl_hash = NULL;
107 
108 	mutex_destroy(&dlp->dl_lock);
109 	mutex_destroy(&dlp->dl_promisc_lock);
110 	rw_destroy(&dlp->dl_impl_lock);
111 }
112 
113 #define	ETHER_MATCH(_pkt_a, _pkt_b)					\
114 	((((uint16_t *)(_pkt_a))[0] == ((uint16_t *)(_pkt_b))[0]) &&	\
115 	(((uint16_t *)(_pkt_a))[1] == ((uint16_t *)(_pkt_b))[1]) &&	\
116 	(((uint16_t *)(_pkt_a))[2] == ((uint16_t *)(_pkt_b))[2]) &&	\
117 	(((uint16_t *)(_pkt_a))[6] == ((uint16_t *)(_pkt_b))[6]))
118 
119 #define	ETHER_VLAN_MATCH(_pkt_a, _pkt_b)				\
120 	((((uint16_t *)(_pkt_a))[0] == ((uint16_t *)(_pkt_b))[0]) &&	\
121 	(((uint16_t *)(_pkt_a))[1] == ((uint16_t *)(_pkt_b))[1]) &&	\
122 	(((uint16_t *)(_pkt_a))[2] == ((uint16_t *)(_pkt_b))[2]) &&	\
123 	(((uint16_t *)(_pkt_a))[6] == ((uint16_t *)(_pkt_b))[6]) &&	\
124 	(((uint16_t *)(_pkt_a))[7] == ((uint16_t *)(_pkt_b))[7]) &&	\
125 	(((uint16_t *)(_pkt_a))[8] == ((uint16_t *)(_pkt_b))[8]))
126 
127 static mblk_t *
128 i_dls_link_ether_subchain(mblk_t *mp, uint_t *header_lengthp,
129     uint8_t **daddrp, uint16_t *type_lengthp, uint16_t *vidp,
130     uint_t *countp)
131 {
132 	struct ether_header		*ehp;
133 	struct ether_vlan_header	*evhp;
134 	mblk_t				**pp;
135 	mblk_t				*p;
136 	uint_t				npacket;
137 
138 	/*
139 	 * Packets should always be at least 16 bit aligned.
140 	 */
141 	ASSERT(IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)));
142 
143 	/*
144 	 * Determine whether this is a VLAN or non-VLAN packet.
145 	 */
146 	ASSERT(MBLKL(mp) >= sizeof (struct ether_header));
147 	ehp = (struct ether_header *)mp->b_rptr;
148 	if ((*type_lengthp = ntohs(ehp->ether_type)) == VLAN_TPID)
149 		goto vlan;
150 
151 	/*
152 	 * It is a non-VLAN header.
153 	 */
154 	*header_lengthp = sizeof (struct ether_header);
155 
156 	/*
157 	 * Parse the rest of the header information that we need.
158 	 */
159 	*daddrp = (uint8_t *)&(ehp->ether_dhost);
160 	*vidp = VLAN_ID_NONE;
161 
162 	/*
163 	 * Compare with subsequent headers until we find one that has
164 	 * differing header information. After checking each packet skip over
165 	 * the header.
166 	 */
167 	npacket = 1;
168 	for (pp = &(mp->b_next); (p = *pp) != NULL; pp = &(p->b_next)) {
169 		if (!ETHER_MATCH(p->b_rptr, mp->b_rptr) != 0)
170 			break;
171 		p->b_rptr += sizeof (struct ether_header);
172 		npacket++;
173 	}
174 
175 	/*
176 	 * Skip over the initial packet's header.
177 	 */
178 	mp->b_rptr += sizeof (struct ether_header);
179 	goto done;
180 
181 vlan:
182 	/*
183 	 * It is a VLAN header.
184 	 */
185 	evhp = (struct ether_vlan_header *)mp->b_rptr;
186 	*header_lengthp = sizeof (struct ether_vlan_header);
187 
188 	/*
189 	 * Parse the header information.
190 	 */
191 	*daddrp = (uint8_t *)&(evhp->ether_dhost);
192 	*vidp = VLAN_ID(ntohs(evhp->ether_tci));
193 	*type_lengthp = ntohs(evhp->ether_type);
194 
195 	/*
196 	 * Compare with subsequent headers until we find one that has
197 	 * differing header information. After checking each packet skip over
198 	 * the header.
199 	 */
200 	npacket = 1;
201 	for (pp = &(mp->b_next); (p = *pp) != NULL; pp = &(p->b_next)) {
202 		if (!ETHER_VLAN_MATCH(p->b_rptr, mp->b_rptr) != 0)
203 			break;
204 		p->b_rptr += sizeof (struct ether_vlan_header);
205 		npacket++;
206 	}
207 
208 	/*
209 	 * Skip over the initial packet's header.
210 	 */
211 	mp->b_rptr += sizeof (struct ether_vlan_header);
212 
213 done:
214 	/*
215 	 * Break the chain at this point and return a pointer to the next
216 	 * sub-chain.
217 	 */
218 	*pp = NULL;
219 	*countp = npacket;
220 	return (p);
221 }
222 
223 static void
224 i_dls_head_hold(dls_head_t *dhp)
225 {
226 	atomic_inc_32(&dhp->dh_ref);
227 }
228 
229 static void
230 i_dls_head_rele(dls_head_t *dhp)
231 {
232 	atomic_dec_32(&dhp->dh_ref);
233 }
234 
235 static dls_head_t *
236 i_dls_head_alloc(mod_hash_key_t key)
237 {
238 	dls_head_t	*dhp;
239 
240 	dhp = kmem_zalloc(sizeof (dls_head_t), KM_SLEEP);
241 	dhp->dh_key = key;
242 	return (dhp);
243 }
244 
245 static void
246 i_dls_head_free(dls_head_t *dhp)
247 {
248 	ASSERT(dhp->dh_ref == 0);
249 	kmem_free(dhp, sizeof (dls_head_t));
250 }
251 
252 static void
253 i_dls_link_ether_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp)
254 {
255 	dls_link_t			*dlp = arg;
256 	mod_hash_t			*hash = dlp->dl_impl_hash;
257 	mblk_t				*nextp;
258 	uint_t				header_length;
259 	uint8_t				*daddr;
260 	uint16_t			type_length;
261 	uint16_t			vid;
262 	uint16_t			sap;
263 	dls_head_t			*dhp;
264 	dls_impl_t			*dip;
265 	dls_impl_t			*ndip;
266 	mblk_t				*nmp;
267 	mod_hash_key_t			key;
268 	uint_t				npacket;
269 	boolean_t			accepted;
270 	dls_rx_t			di_rx, ndi_rx;
271 	void				*di_rx_arg, *ndi_rx_arg;
272 
273 	/*
274 	 * Walk the packet chain.
275 	 */
276 	while (mp != NULL) {
277 		/*
278 		 * Wipe the accepted state.
279 		 */
280 		accepted = B_FALSE;
281 
282 		/*
283 		 * Grab the longest sub-chain we can process as a single
284 		 * unit.
285 		 */
286 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
287 		    &type_length, &vid, &npacket);
288 
289 		/*
290 		 * Calculate the DLSAP: LLC (0) if the type/length field is
291 		 * interpreted as a length, otherwise it is the value of the
292 		 * type/length field.
293 		 */
294 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
295 
296 		/*
297 		 * Construct a hash key from the VLAN identifier and the
298 		 * DLSAP.
299 		 */
300 		key = MAKE_KEY(sap, vid);
301 
302 		/*
303 		 * Search the has table for dls_impl_t eligible to receive
304 		 * a packet chain for this DLSAP/VLAN combination.
305 		 */
306 		rw_enter(&dlp->dl_impl_lock, RW_READER);
307 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
308 			rw_exit(&dlp->dl_impl_lock);
309 			freemsgchain(mp);
310 			goto loop;
311 		}
312 		i_dls_head_hold(dhp);
313 		rw_exit(&dlp->dl_impl_lock);
314 
315 		/*
316 		 * Find the first dls_impl_t that will accept the sub-chain.
317 		 */
318 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
319 			if (dls_accept(dip, daddr, &di_rx, &di_rx_arg))
320 				break;
321 
322 		/*
323 		 * If we did not find any dls_impl_t willing to accept the
324 		 * sub-chain then throw it away.
325 		 */
326 		if (dip == NULL) {
327 			i_dls_head_rele(dhp);
328 			freemsgchain(mp);
329 			goto loop;
330 		}
331 
332 		/*
333 		 * We have at least one acceptor.
334 		 */
335 		accepted = B_TRUE;
336 		for (;;) {
337 			/*
338 			 * Find the next dls_impl_t that will accept the
339 			 * sub-chain.
340 			 */
341 			for (ndip = dip->di_nextp; ndip != NULL;
342 			    ndip = ndip->di_nextp)
343 				if (dls_accept(ndip, daddr, &ndi_rx,
344 				    &ndi_rx_arg))
345 					break;
346 
347 			/*
348 			 * If there are no more dls_impl_t that are willing
349 			 * to accept the sub-chain then we don't need to dup
350 			 * it before handing it to the current one.
351 			 */
352 			if (ndip == NULL) {
353 				di_rx(di_rx_arg, mrh, mp, header_length);
354 
355 				/*
356 				 * Since there are no more dls_impl_t, we're
357 				 * done.
358 				 */
359 				break;
360 			}
361 
362 			/*
363 			 * There are more dls_impl_t so dup the sub-chain.
364 			 */
365 			if ((nmp = copymsgchain(mp)) != NULL)
366 				di_rx(di_rx_arg, mrh, nmp, header_length);
367 
368 			dip = ndip;
369 			di_rx = ndi_rx;
370 			di_rx_arg = ndi_rx_arg;
371 		}
372 
373 		/*
374 		 * Release the hold on the dls_impl_t chain now that we have
375 		 * finished walking it.
376 		 */
377 		i_dls_head_rele(dhp);
378 
379 loop:
380 		/*
381 		 * If there were no acceptors then add the packet count to the
382 		 * 'unknown' count.
383 		 */
384 		if (!accepted)
385 			atomic_add_32(&(dlp->dl_unknowns), npacket);
386 
387 		/*
388 		 * Move onto the next sub-chain.
389 		 */
390 		mp = nextp;
391 	}
392 }
393 
394 static void
395 i_dls_link_ether_rx_promisc(void *arg, mac_resource_handle_t mrh,
396     mblk_t *mp)
397 {
398 	dls_link_t			*dlp = arg;
399 	mod_hash_t			*hash = dlp->dl_impl_hash;
400 	mblk_t				*nextp;
401 	uint_t				header_length;
402 	uint8_t				*daddr;
403 	uint16_t			type_length;
404 	uint16_t			vid;
405 	uint16_t			sap;
406 	dls_head_t			*dhp;
407 	dls_impl_t			*dip;
408 	dls_impl_t			*ndip;
409 	mblk_t				*nmp;
410 	mod_hash_key_t			key;
411 	uint_t				npacket;
412 	boolean_t			accepted;
413 	dls_rx_t			di_rx, ndi_rx;
414 	void				*di_rx_arg, *ndi_rx_arg;
415 
416 	/*
417 	 * Walk the packet chain.
418 	 */
419 	while (mp != NULL) {
420 		/*
421 		 * Wipe the accepted state.
422 		 */
423 		accepted = B_FALSE;
424 
425 		/*
426 		 * Grab the longest sub-chain we can process as a single
427 		 * unit.
428 		 */
429 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
430 		    &type_length, &vid, &npacket);
431 
432 		/*
433 		 * Construct a hash key from the VLAN identifier and the
434 		 * DLSAP that represents dls_impl_t in promiscuous mode.
435 		 */
436 		key = MAKE_KEY(DLS_SAP_PROMISC, vid);
437 
438 		/*
439 		 * Search the has table for dls_impl_t eligible to receive
440 		 * a packet chain for this DLSAP/VLAN combination.
441 		 */
442 		rw_enter(&dlp->dl_impl_lock, RW_READER);
443 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
444 			rw_exit(&dlp->dl_impl_lock);
445 			goto non_promisc;
446 		}
447 		i_dls_head_hold(dhp);
448 		rw_exit(&dlp->dl_impl_lock);
449 
450 		/*
451 		 * Find dls_impl_t that will accept the sub-chain.
452 		 */
453 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp) {
454 			if (!dls_accept(dip, daddr, &di_rx, &di_rx_arg))
455 				continue;
456 
457 			/*
458 			 * We have at least one acceptor.
459 			 */
460 			accepted = B_TRUE;
461 
462 			/*
463 			 * There will normally be at least more dls_impl_t
464 			 * (since we've yet to check for non-promiscuous
465 			 * dls_impl_t) so dup the sub-chain.
466 			 */
467 			if ((nmp = copymsgchain(mp)) != NULL)
468 				di_rx(di_rx_arg, mrh, nmp, header_length);
469 		}
470 
471 		/*
472 		 * Release the hold on the dls_impl_t chain now that we have
473 		 * finished walking it.
474 		 */
475 		i_dls_head_rele(dhp);
476 
477 non_promisc:
478 		/*
479 		 * Calculate the DLSAP: LLC (0) if the type/length field is
480 		 * interpreted as a length, otherwise it is the value of the
481 		 * type/length field.
482 		 */
483 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
484 
485 		/*
486 		 * Construct a hash key from the VLAN identifier and the
487 		 * DLSAP.
488 		 */
489 		key = MAKE_KEY(sap, vid);
490 
491 		/*
492 		 * Search the has table for dls_impl_t eligible to receive
493 		 * a packet chain for this DLSAP/VLAN combination.
494 		 */
495 		rw_enter(&dlp->dl_impl_lock, RW_READER);
496 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
497 			rw_exit(&dlp->dl_impl_lock);
498 			freemsgchain(mp);
499 			goto loop;
500 		}
501 		i_dls_head_hold(dhp);
502 		rw_exit(&dlp->dl_impl_lock);
503 
504 		/*
505 		 * Find the first dls_impl_t that will accept the sub-chain.
506 		 */
507 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
508 			if (dls_accept(dip, daddr, &di_rx, &di_rx_arg))
509 				break;
510 
511 		/*
512 		 * If we did not find any dls_impl_t willing to accept the
513 		 * sub-chain then throw it away.
514 		 */
515 		if (dip == NULL) {
516 			i_dls_head_rele(dhp);
517 			freemsgchain(mp);
518 			goto loop;
519 		}
520 
521 		/*
522 		 * We have at least one acceptor.
523 		 */
524 		accepted = B_TRUE;
525 		for (;;) {
526 			/*
527 			 * Find the next dls_impl_t that will accept the
528 			 * sub-chain.
529 			 */
530 			for (ndip = dip->di_nextp; ndip != NULL;
531 			    ndip = ndip->di_nextp)
532 				if (dls_accept(ndip, daddr, &ndi_rx,
533 				    &ndi_rx_arg))
534 					break;
535 
536 			/*
537 			 * If there are no more dls_impl_t that are willing
538 			 * to accept the sub-chain then we don't need to dup
539 			 * it before handing it to the current one.
540 			 */
541 			if (ndip == NULL) {
542 				di_rx(di_rx_arg, mrh, mp, header_length);
543 
544 				/*
545 				 * Since there are no more dls_impl_t, we're
546 				 * done.
547 				 */
548 				break;
549 			}
550 
551 			/*
552 			 * There are more dls_impl_t so dup the sub-chain.
553 			 */
554 			if ((nmp = copymsgchain(mp)) != NULL)
555 				di_rx(di_rx_arg, mrh, nmp, header_length);
556 
557 			dip = ndip;
558 			di_rx = ndi_rx;
559 			di_rx_arg = ndi_rx_arg;
560 		}
561 
562 		/*
563 		 * Release the hold on the dls_impl_t chain now that we have
564 		 * finished walking it.
565 		 */
566 		i_dls_head_rele(dhp);
567 
568 loop:
569 		/*
570 		 * If there were no acceptors then add the packet count to the
571 		 * 'unknown' count.
572 		 */
573 		if (!accepted)
574 			atomic_add_32(&(dlp->dl_unknowns), npacket);
575 
576 		/*
577 		 * Move onto the next sub-chain.
578 		 */
579 		mp = nextp;
580 	}
581 }
582 
583 static void
584 i_dls_link_ether_loopback(void *arg, mblk_t *mp)
585 {
586 	dls_link_t			*dlp = arg;
587 	mod_hash_t			*hash = dlp->dl_impl_hash;
588 	mblk_t				*nextp;
589 	uint_t				header_length;
590 	uint8_t				*daddr;
591 	uint16_t			type_length;
592 	uint16_t			vid;
593 	uint16_t			sap;
594 	dls_head_t			*dhp;
595 	dls_impl_t			*dip;
596 	dls_impl_t			*ndip;
597 	mblk_t				*nmp;
598 	mod_hash_key_t			key;
599 	uint_t				npacket;
600 	dls_rx_t			di_rx, ndi_rx;
601 	void				*di_rx_arg, *ndi_rx_arg;
602 
603 	/*
604 	 * Walk the packet chain.
605 	 */
606 	while (mp != NULL) {
607 		/*
608 		 * Grab the longest sub-chain we can process as a single
609 		 * unit.
610 		 */
611 		nextp = i_dls_link_ether_subchain(mp, &header_length, &daddr,
612 		    &type_length, &vid, &npacket);
613 
614 		/*
615 		 * Calculate the DLSAP: LLC (0) if the type/length field is
616 		 * interpreted as a length, otherwise it is the value of the
617 		 * type/length field.
618 		 */
619 		sap = (type_length <= ETHERMTU) ? DLS_SAP_LLC : type_length;
620 
621 		/*
622 		 * Construct a hash key from the VLAN identifier and the
623 		 * DLSAP.
624 		 */
625 		key = MAKE_KEY(sap, vid);
626 
627 		/*
628 		 * Search the has table for dls_impl_t eligible to receive
629 		 * a packet chain for this DLSAP/VLAN combination.
630 		 */
631 		rw_enter(&dlp->dl_impl_lock, RW_READER);
632 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
633 			rw_exit(&dlp->dl_impl_lock);
634 			goto promisc;
635 		}
636 		i_dls_head_hold(dhp);
637 		rw_exit(&dlp->dl_impl_lock);
638 
639 		/*
640 		 * Find dls_impl_t that will accept the sub-chain.
641 		 */
642 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp) {
643 			if (!dls_accept_loopback(dip, daddr, &di_rx,
644 			    &di_rx_arg))
645 				continue;
646 
647 			/*
648 			 * There should be at least more dls_impl_t (since
649 			 * we've yet to check for dls_impl_t in promiscuous
650 			 * mode) so dup the sub-chain.
651 			 */
652 			if ((nmp = copymsgchain(mp)) != NULL)
653 				di_rx(di_rx_arg, NULL, nmp, header_length);
654 		}
655 
656 		/*
657 		 * Release the hold on the dls_impl_t chain now that we have
658 		 * finished walking it.
659 		 */
660 		i_dls_head_rele(dhp);
661 
662 promisc:
663 		/*
664 		 * Construct a hash key from the VLAN identifier and the
665 		 * DLSAP that represents dls_impl_t in promiscuous mode.
666 		 */
667 		key = MAKE_KEY(DLS_SAP_PROMISC, vid);
668 
669 		/*
670 		 * Search the has table for dls_impl_t eligible to receive
671 		 * a packet chain for this DLSAP/VLAN combination.
672 		 */
673 		rw_enter(&dlp->dl_impl_lock, RW_READER);
674 		if (mod_hash_find(hash, key, (mod_hash_val_t *)&dhp) != 0) {
675 			rw_exit(&dlp->dl_impl_lock);
676 			freemsgchain(mp);
677 			goto loop;
678 		}
679 		i_dls_head_hold(dhp);
680 		rw_exit(&dlp->dl_impl_lock);
681 
682 		/*
683 		 * Find the first dls_impl_t that will accept the sub-chain.
684 		 */
685 		for (dip = dhp->dh_list; dip != NULL; dip = dip->di_nextp)
686 			if (dls_accept_loopback(dip, daddr, &di_rx, &di_rx_arg))
687 				break;
688 
689 		/*
690 		 * If we did not find any dls_impl_t willing to accept the
691 		 * sub-chain then throw it away.
692 		 */
693 		if (dip == NULL) {
694 			i_dls_head_rele(dhp);
695 			freemsgchain(mp);
696 			goto loop;
697 		}
698 
699 		for (;;) {
700 			/*
701 			 * Find the next dls_impl_t that will accept the
702 			 * sub-chain.
703 			 */
704 			for (ndip = dip->di_nextp; ndip != NULL;
705 			    ndip = ndip->di_nextp)
706 				if (dls_accept_loopback(ndip, daddr,
707 				    &ndi_rx, &ndi_rx_arg))
708 					break;
709 
710 			/*
711 			 * If there are no more dls_impl_t that are willing
712 			 * to accept the sub-chain then we don't need to dup
713 			 * it before handing it to the current one.
714 			 */
715 			if (ndip == NULL) {
716 				di_rx(di_rx_arg, NULL, mp, header_length);
717 
718 				/*
719 				 * Since there are no more dls_impl_t, we're
720 				 * done.
721 				 */
722 				break;
723 			}
724 
725 			/*
726 			 * There are more dls_impl_t so dup the sub-chain.
727 			 */
728 			if ((nmp = copymsgchain(mp)) != NULL)
729 				di_rx(di_rx_arg, NULL, nmp, header_length);
730 
731 			dip = ndip;
732 			di_rx = ndi_rx;
733 			di_rx_arg = ndi_rx_arg;
734 		}
735 
736 		/*
737 		 * Release the hold on the dls_impl_t chain now that we have
738 		 * finished walking it.
739 		 */
740 		i_dls_head_rele(dhp);
741 
742 loop:
743 		/*
744 		 * Move onto the next sub-chain.
745 		 */
746 		mp = nextp;
747 	}
748 }
749 
750 /*ARGSUSED*/
751 static uint_t
752 i_dls_link_walk(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
753 {
754 	boolean_t	*promiscp = arg;
755 	uint32_t	sap = KEY_SAP(key);
756 
757 	if (sap == DLS_SAP_PROMISC) {
758 		*promiscp = B_TRUE;
759 		return (MH_WALK_TERMINATE);
760 	}
761 
762 	return (MH_WALK_CONTINUE);
763 }
764 
765 static int
766 i_dls_link_create(const char *dev, uint_t port, dls_link_t **dlpp)
767 {
768 	dls_link_t		*dlp;
769 
770 	/*
771 	 * Allocate a new dls_link_t structure.
772 	 */
773 	dlp = kmem_cache_alloc(i_dls_link_cachep, KM_SLEEP);
774 
775 	/*
776 	 * Name the dls_link_t after the MAC interface it represents.
777 	 */
778 	MAC_NAME(dlp->dl_name, dev, port);
779 	(void) strlcpy(dlp->dl_dev, dev, MAXNAMELEN);
780 	dlp->dl_port = port;
781 
782 	/*
783 	 * Set the packet loopback function for use when the MAC is in
784 	 * promiscuous mode, and initialize promiscuous bookeeping fields.
785 	 */
786 	dlp->dl_loopback = i_dls_link_ether_loopback;
787 	dlp->dl_npromisc = 0;
788 	dlp->dl_mth = NULL;
789 
790 	*dlpp = dlp;
791 	return (0);
792 }
793 
794 static void
795 i_dls_link_destroy(dls_link_t *dlp)
796 {
797 	ASSERT(dlp->dl_npromisc == 0);
798 	ASSERT(dlp->dl_nactive == 0);
799 	ASSERT(dlp->dl_mth == NULL);
800 	ASSERT(dlp->dl_macref == 0);
801 	ASSERT(dlp->dl_mh == NULL);
802 	ASSERT(dlp->dl_mip == NULL);
803 	ASSERT(dlp->dl_impl_count == 0);
804 	ASSERT(dlp->dl_mrh == NULL);
805 
806 	/*
807 	 * Free the structure back to the cache.
808 	 */
809 	dlp->dl_unknowns = 0;
810 	kmem_cache_free(i_dls_link_cachep, dlp);
811 }
812 
813 /*
814  * Module initialization functions.
815  */
816 
817 void
818 dls_link_init(void)
819 {
820 	/*
821 	 * Create a kmem_cache of dls_link_t structures.
822 	 */
823 	i_dls_link_cachep = kmem_cache_create("dls_link_cache",
824 	    sizeof (dls_link_t), 0, i_dls_link_constructor,
825 	    i_dls_link_destructor, NULL, NULL, NULL, 0);
826 	ASSERT(i_dls_link_cachep != NULL);
827 
828 	/*
829 	 * Create a dls_link_t hash table and associated lock.
830 	 */
831 	i_dls_link_hash = mod_hash_create_extended("dls_link_hash",
832 	    IMPL_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
833 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
834 	rw_init(&i_dls_link_lock, NULL, RW_DEFAULT, NULL);
835 	i_dls_link_count = 0;
836 }
837 
838 int
839 dls_link_fini(void)
840 {
841 	if (i_dls_link_count > 0)
842 		return (EBUSY);
843 
844 	/*
845 	 * Destroy the kmem_cache.
846 	 */
847 	kmem_cache_destroy(i_dls_link_cachep);
848 
849 	/*
850 	 * Destroy the hash table and associated lock.
851 	 */
852 	mod_hash_destroy_hash(i_dls_link_hash);
853 	rw_destroy(&i_dls_link_lock);
854 	return (0);
855 }
856 
857 /*
858  * Exported functions.
859  */
860 
861 int
862 dls_link_hold(const char *dev, uint_t port, dls_link_t **dlpp)
863 {
864 	char			name[MAXNAMELEN];
865 	dls_link_t		*dlp;
866 	int			err;
867 
868 	/*
869 	 * Construct a copy of the name used to identify any existing
870 	 * dls_link_t.
871 	 */
872 	MAC_NAME(name, dev, port);
873 
874 	/*
875 	 * Look up a dls_link_t corresponding to the given mac_handle_t
876 	 * in the global hash table. We need to hold i_dls_link_lock in
877 	 * order to atomically find and insert a dls_link_t into the
878 	 * hash table.
879 	 */
880 	rw_enter(&i_dls_link_lock, RW_WRITER);
881 	if ((err = mod_hash_find(i_dls_link_hash, (mod_hash_key_t)name,
882 	    (mod_hash_val_t *)&dlp)) == 0)
883 		goto done;
884 
885 	ASSERT(err == MH_ERR_NOTFOUND);
886 
887 	/*
888 	 * We didn't find anything so we need to create one.
889 	 */
890 	if ((err = i_dls_link_create(dev, port, &dlp)) != 0) {
891 		rw_exit(&i_dls_link_lock);
892 		return (err);
893 	}
894 
895 	/*
896 	 * Insert the dls_link_t.
897 	 */
898 	err = mod_hash_insert(i_dls_link_hash, (mod_hash_key_t)dlp->dl_name,
899 	    (mod_hash_val_t)dlp);
900 	ASSERT(err == 0);
901 
902 	i_dls_link_count++;
903 	ASSERT(i_dls_link_count != 0);
904 
905 done:
906 	/*
907 	 * Bump the reference count and hand back the reference.
908 	 */
909 	dlp->dl_ref++;
910 	*dlpp = dlp;
911 	rw_exit(&i_dls_link_lock);
912 	return (0);
913 }
914 
915 void
916 dls_link_rele(dls_link_t *dlp)
917 {
918 	mod_hash_val_t	val;
919 
920 	rw_enter(&i_dls_link_lock, RW_WRITER);
921 
922 	/*
923 	 * Check if there are any more references.
924 	 */
925 	if (--dlp->dl_ref != 0) {
926 		/*
927 		 * There are more references so there's nothing more to do.
928 		 */
929 		goto done;
930 	}
931 
932 	(void) mod_hash_remove(i_dls_link_hash,
933 	    (mod_hash_key_t)dlp->dl_name, &val);
934 	ASSERT(dlp == (dls_link_t *)val);
935 
936 	/*
937 	 * Destroy the dls_link_t.
938 	 */
939 	i_dls_link_destroy(dlp);
940 	ASSERT(i_dls_link_count > 0);
941 	i_dls_link_count--;
942 done:
943 	rw_exit(&i_dls_link_lock);
944 }
945 
946 int
947 dls_mac_hold(dls_link_t *dlp)
948 {
949 	int err = 0;
950 
951 	mutex_enter(&dlp->dl_lock);
952 
953 	ASSERT(IMPLY(dlp->dl_macref != 0, dlp->dl_mh != NULL));
954 	ASSERT(IMPLY(dlp->dl_macref == 0, dlp->dl_mh == NULL));
955 
956 	if (dlp->dl_macref == 0) {
957 		/*
958 		 * First reference; hold open the MAC interface.
959 		 */
960 		err = mac_open(dlp->dl_dev, dlp->dl_port, &dlp->dl_mh);
961 		if (err != 0)
962 			goto done;
963 
964 		dlp->dl_mip = mac_info(dlp->dl_mh);
965 	}
966 
967 	dlp->dl_macref++;
968 done:
969 	mutex_exit(&dlp->dl_lock);
970 	return (err);
971 }
972 
973 void
974 dls_mac_rele(dls_link_t *dlp)
975 {
976 	mutex_enter(&dlp->dl_lock);
977 	ASSERT(dlp->dl_mh != NULL);
978 
979 	if (--dlp->dl_macref == 0) {
980 		mac_close(dlp->dl_mh);
981 		dlp->dl_mh = NULL;
982 		dlp->dl_mip = NULL;
983 	}
984 	mutex_exit(&dlp->dl_lock);
985 }
986 
987 void
988 dls_link_add(dls_link_t *dlp, uint32_t sap, dls_impl_t *dip)
989 {
990 	dls_vlan_t	*dvp = dip->di_dvp;
991 	mod_hash_t	*hash = dlp->dl_impl_hash;
992 	mod_hash_key_t	key;
993 	dls_head_t	*dhp;
994 	dls_impl_t	*p;
995 	mac_rx_t	rx;
996 	int		err;
997 	boolean_t	promisc = B_FALSE;
998 
999 	/*
1000 	 * For ethernet media, sap values less than or equal to
1001 	 * ETHERMTU (1500) represent LLC channels. (See PSARC 2003/150).
1002 	 * We strictly use 0 to represent LLC channels.
1003 	 */
1004 	sap = (sap <= ETHERMTU) ? 0 : sap;
1005 
1006 	/*
1007 	 * Make the appropriate key value depending on whether the
1008 	 * dls_impl_t is in promiscuous mode or not.
1009 	 */
1010 	key = MAKE_KEY(sap, dvp->dv_id);
1011 
1012 	/*
1013 	 * We need dl_lock here because we want to be able to walk
1014 	 * the hash table *and* set the mac rx func atomically. if
1015 	 * these two operations are separate, someone else could
1016 	 * insert/remove dls_impl_t from the hash table after we
1017 	 * drop the hash lock and this could cause our chosen rx
1018 	 * func to be incorrect. note that we cannot call mac_rx_add
1019 	 * when holding the hash lock because this can cause deadlock.
1020 	 */
1021 	mutex_enter(&dlp->dl_lock);
1022 
1023 	/*
1024 	 * Search the table for a list head with this key.
1025 	 */
1026 	rw_enter(&dlp->dl_impl_lock, RW_WRITER);
1027 
1028 	if ((err = mod_hash_find(hash, key, (mod_hash_val_t *)&dhp)) != 0) {
1029 		ASSERT(err == MH_ERR_NOTFOUND);
1030 
1031 		dhp = i_dls_head_alloc(key);
1032 		err = mod_hash_insert(hash, key, (mod_hash_val_t)dhp);
1033 		ASSERT(err == 0);
1034 	}
1035 
1036 	/*
1037 	 * Add the dls_impl_t to the head of the list.
1038 	 */
1039 	ASSERT(dip->di_nextp == NULL);
1040 	p = dhp->dh_list;
1041 	dip->di_nextp = p;
1042 	dhp->dh_list = dip;
1043 
1044 	/*
1045 	 * Save a pointer to the list head.
1046 	 */
1047 	dip->di_headp = dhp;
1048 	dlp->dl_impl_count++;
1049 
1050 	/*
1051 	 * Walk the bound dls_impl_t to see if there are any
1052 	 * in promiscuous 'all sap' mode.
1053 	 */
1054 	mod_hash_walk(hash, i_dls_link_walk, (void *)&promisc);
1055 	rw_exit(&dlp->dl_impl_lock);
1056 
1057 	/*
1058 	 * If there are then we need to use a receive routine
1059 	 * which will route packets to those dls_impl_t as well
1060 	 * as ones bound to the  DLSAP of the packet.
1061 	 */
1062 	if (promisc)
1063 		rx = i_dls_link_ether_rx_promisc;
1064 	else
1065 		rx = i_dls_link_ether_rx;
1066 
1067 	/* Replace the existing receive function if there is one. */
1068 	if (dlp->dl_mrh != NULL)
1069 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
1070 	dlp->dl_mrh = mac_rx_add(dlp->dl_mh, rx, (void *)dlp);
1071 	mutex_exit(&dlp->dl_lock);
1072 }
1073 
1074 void
1075 dls_link_remove(dls_link_t *dlp, dls_impl_t *dip)
1076 {
1077 	mod_hash_t	*hash = dlp->dl_impl_hash;
1078 	dls_impl_t	**pp;
1079 	dls_impl_t	*p;
1080 	dls_head_t	*dhp;
1081 	mac_rx_t	rx;
1082 
1083 	/*
1084 	 * We need dl_lock here because we want to be able to walk
1085 	 * the hash table *and* set the mac rx func atomically. if
1086 	 * these two operations are separate, someone else could
1087 	 * insert/remove dls_impl_t from the hash table after we
1088 	 * drop the hash lock and this could cause our chosen rx
1089 	 * func to be incorrect. note that we cannot call mac_rx_add
1090 	 * when holding the hash lock because this can cause deadlock.
1091 	 */
1092 	mutex_enter(&dlp->dl_lock);
1093 	rw_enter(&dlp->dl_impl_lock, RW_WRITER);
1094 
1095 	/*
1096 	 * Poll the hash table entry until all references have been dropped.
1097 	 * We need to drop all locks before sleeping because we don't want
1098 	 * the interrupt handler to block. We set di_removing here to
1099 	 * tell the receive callbacks not to pass up packets anymore.
1100 	 * This is only a hint to quicken the decrease of the refcnt so
1101 	 * the assignment need not be protected by any lock.
1102 	 */
1103 	dhp = dip->di_headp;
1104 	dip->di_removing = B_TRUE;
1105 	while (dhp->dh_ref != 0) {
1106 		rw_exit(&dlp->dl_impl_lock);
1107 		mutex_exit(&dlp->dl_lock);
1108 		delay(drv_usectohz(1000));	/* 1ms delay */
1109 		mutex_enter(&dlp->dl_lock);
1110 		rw_enter(&dlp->dl_impl_lock, RW_WRITER);
1111 	}
1112 
1113 	/*
1114 	 * Walk the list and remove the dls_impl_t.
1115 	 */
1116 	for (pp = &dhp->dh_list; (p = *pp) != NULL; pp = &(p->di_nextp)) {
1117 		if (p == dip)
1118 			break;
1119 	}
1120 	ASSERT(p != NULL);
1121 	*pp = p->di_nextp;
1122 	p->di_nextp = NULL;
1123 
1124 	ASSERT(dlp->dl_impl_count > 0);
1125 	dlp->dl_impl_count--;
1126 
1127 	if (dhp->dh_list == NULL) {
1128 		mod_hash_val_t	val = NULL;
1129 
1130 		/*
1131 		 * The list is empty so remove the hash table entry.
1132 		 */
1133 		(void) mod_hash_remove(hash, dhp->dh_key, &val);
1134 		ASSERT(dhp == (dls_head_t *)val);
1135 		i_dls_head_free(dhp);
1136 	}
1137 	dip->di_removing = B_FALSE;
1138 
1139 	/*
1140 	 * If there are no dls_impl_t then there's no need to register a
1141 	 * receive function with the mac.
1142 	 */
1143 	if (dlp->dl_impl_count == 0) {
1144 		rw_exit(&dlp->dl_impl_lock);
1145 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
1146 		dlp->dl_mrh = NULL;
1147 	} else {
1148 		boolean_t promisc = B_FALSE;
1149 
1150 		/*
1151 		 * Walk the bound dls_impl_t to see if there are any
1152 		 * in promiscuous 'all sap' mode.
1153 		 */
1154 		mod_hash_walk(hash, i_dls_link_walk, (void *)&promisc);
1155 		rw_exit(&dlp->dl_impl_lock);
1156 
1157 		/*
1158 		 * If there are then we need to use a receive routine
1159 		 * which will route packets to those dls_impl_t as well
1160 		 * as ones bound to the  DLSAP of the packet.
1161 		 */
1162 		if (promisc)
1163 			rx = i_dls_link_ether_rx_promisc;
1164 		else
1165 			rx = i_dls_link_ether_rx;
1166 
1167 		mac_rx_remove(dlp->dl_mh, dlp->dl_mrh);
1168 		dlp->dl_mrh = mac_rx_add(dlp->dl_mh, rx, (void *)dlp);
1169 	}
1170 	mutex_exit(&dlp->dl_lock);
1171 }
1172