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