xref: /titanic_52/usr/src/uts/common/io/dls/dls.c (revision 23a1ccea6aac035f084a7a4cdc968687d1b02daf)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Data-Link Services Module
28  */
29 
30 #include	<sys/strsun.h>
31 #include	<sys/vlan.h>
32 #include	<sys/dld_impl.h>
33 #include	<sys/mac_client_priv.h>
34 
35 int
36 dls_open(dls_link_t *dlp, dls_dl_handle_t ddh, dld_str_t *dsp)
37 {
38 	zoneid_t	zid = getzoneid();
39 	boolean_t	local;
40 	int		err;
41 
42 	/*
43 	 * Check whether this client belongs to the zone of this dlp. Note that
44 	 * a global zone client is allowed to open a local zone dlp.
45 	 */
46 	if (zid != GLOBAL_ZONEID && dlp->dl_zid != zid)
47 		return (ENOENT);
48 
49 	/*
50 	 * mac_start() is required for non-legacy MACs to show accurate
51 	 * kstats even before the interface is brought up. For legacy
52 	 * drivers, this is not needed. Further, calling mac_start() for
53 	 * legacy drivers would make the shared-lower-stream to stay in
54 	 * the DL_IDLE state, which in turn causes performance regression.
55 	 */
56 	if (!mac_capab_get(dlp->dl_mh, MAC_CAPAB_LEGACY, NULL) &&
57 	    ((err = mac_start(dlp->dl_mh)) != 0)) {
58 		return (err);
59 	}
60 
61 	local = (zid == dlp->dl_zid);
62 	dlp->dl_zone_ref += (local ? 1 : 0);
63 
64 	/*
65 	 * Cache a copy of the MAC interface handle, a pointer to the
66 	 * immutable MAC info.
67 	 */
68 	dsp->ds_dlp = dlp;
69 	dsp->ds_mh = dlp->dl_mh;
70 	dsp->ds_mch = dlp->dl_mch;
71 	dsp->ds_mip = dlp->dl_mip;
72 	dsp->ds_ddh = ddh;
73 	dsp->ds_local = local;
74 
75 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
76 	return (0);
77 }
78 
79 void
80 dls_close(dld_str_t *dsp)
81 {
82 	dls_link_t		*dlp = dsp->ds_dlp;
83 	dls_multicst_addr_t	*p;
84 	dls_multicst_addr_t	*nextp;
85 	uint32_t		old_flags;
86 
87 	ASSERT(dsp->ds_datathr_cnt == 0);
88 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
89 
90 	if (dsp->ds_local)
91 		dlp->dl_zone_ref--;
92 	dsp->ds_local = B_FALSE;
93 
94 	/*
95 	 * Walk the list of multicast addresses, disabling each at the MAC.
96 	 * Note that we must remove multicast address before
97 	 * mac_unicast_remove() (called by dls_active_clear()) because
98 	 * mac_multicast_remove() relies on the unicast flows on the mac
99 	 * client.
100 	 */
101 	for (p = dsp->ds_dmap; p != NULL; p = nextp) {
102 		(void) mac_multicast_remove(dsp->ds_mch, p->dma_addr);
103 		nextp = p->dma_nextp;
104 		kmem_free(p, sizeof (dls_multicst_addr_t));
105 	}
106 	dsp->ds_dmap = NULL;
107 
108 	dls_active_clear(dsp, B_TRUE);
109 
110 	/*
111 	 * If the dld_str_t is bound then unbind it.
112 	 */
113 	if (dsp->ds_dlstate == DL_IDLE) {
114 		dls_unbind(dsp);
115 		dsp->ds_dlstate = DL_UNBOUND;
116 	}
117 
118 	/*
119 	 * If the MAC has been set in promiscuous mode then disable it.
120 	 * This needs to be done before resetting ds_rx.
121 	 */
122 	old_flags = dsp->ds_promisc;
123 	dsp->ds_promisc = 0;
124 	(void) dls_promisc(dsp, old_flags);
125 
126 	/*
127 	 * At this point we have cutoff inbound packet flow from the mac
128 	 * for this 'dsp'. The dls_link_remove above cut off packets meant
129 	 * for us and waited for upcalls to finish. Similarly the dls_promisc
130 	 * reset above waited for promisc callbacks to finish. Now we can
131 	 * safely reset ds_rx to NULL
132 	 */
133 	dsp->ds_rx = NULL;
134 	dsp->ds_rx_arg = NULL;
135 
136 	dsp->ds_dlp = NULL;
137 
138 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_LEGACY, NULL))
139 		mac_stop(dsp->ds_mh);
140 
141 	/*
142 	 * Release our reference to the dls_link_t allowing that to be
143 	 * destroyed if there are no more dls_impl_t.
144 	 */
145 	dls_link_rele(dlp);
146 }
147 
148 int
149 dls_bind(dld_str_t *dsp, uint32_t sap)
150 {
151 	uint32_t	dls_sap;
152 
153 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
154 
155 	/*
156 	 * Check to see the value is legal for the media type.
157 	 */
158 	if (!mac_sap_verify(dsp->ds_mh, sap, &dls_sap))
159 		return (EINVAL);
160 
161 	if (dsp->ds_promisc & DLS_PROMISC_SAP)
162 		dls_sap = DLS_SAP_PROMISC;
163 
164 	/*
165 	 * Set up the dld_str_t to mark it as able to receive packets.
166 	 */
167 	dsp->ds_sap = sap;
168 
169 	/*
170 	 * The MAC layer does the VLAN demultiplexing and will only pass up
171 	 * untagged packets to non-promiscuous primary MAC clients. In order to
172 	 * support the binding to the VLAN SAP which is required by DLPI, dls
173 	 * needs to get a copy of all tagged packets when the client binds to
174 	 * the VLAN SAP. We do this by registering a separate promiscuous
175 	 * callback for each dls client binding to that SAP.
176 	 *
177 	 * Note: even though there are two promiscuous handles in dld_str_t,
178 	 * ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
179 	 * to receive VLAN pkt when promiscuous mode is not on. Only one of
180 	 * them can be non-NULL at the same time, to avoid receiving dup copies
181 	 * of pkts.
182 	 */
183 	if (sap == ETHERTYPE_VLAN && dsp->ds_promisc == 0) {
184 		int err;
185 
186 		if (dsp->ds_vlan_mph != NULL)
187 			return (EINVAL);
188 		err = mac_promisc_add(dsp->ds_mch,
189 		    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
190 		    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
191 
192 		if (err == 0 && dsp->ds_nonip &&
193 		    dsp->ds_dlp->dl_nonip_cnt++ == 0)
194 			mac_rx_bypass_disable(dsp->ds_mch);
195 
196 		return (err);
197 	}
198 
199 	/*
200 	 * Now bind the dld_str_t by adding it into the hash table in the
201 	 * dls_link_t.
202 	 */
203 	dls_link_add(dsp->ds_dlp, dls_sap, dsp);
204 	if (dsp->ds_nonip && dsp->ds_dlp->dl_nonip_cnt++ == 0)
205 		mac_rx_bypass_disable(dsp->ds_mch);
206 
207 	return (0);
208 }
209 
210 void
211 dls_unbind(dld_str_t *dsp)
212 {
213 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
214 
215 	if (dsp->ds_nonip && --dsp->ds_dlp->dl_nonip_cnt == 0)
216 		mac_rx_bypass_enable(dsp->ds_mch);
217 
218 	/*
219 	 * For VLAN SAP, there was a promisc handle registered when dls_bind.
220 	 * When unbind this dls link, we need to remove the promisc handle.
221 	 * See comments in dls_bind().
222 	 */
223 	if (dsp->ds_vlan_mph != NULL) {
224 		mac_promisc_remove(dsp->ds_vlan_mph);
225 		dsp->ds_vlan_mph = NULL;
226 		return;
227 	}
228 
229 	/*
230 	 * Unbind the dld_str_t by removing it from the hash table in the
231 	 * dls_link_t.
232 	 */
233 	dls_link_remove(dsp->ds_dlp, dsp);
234 	dsp->ds_sap = 0;
235 }
236 
237 int
238 dls_promisc(dld_str_t *dsp, uint32_t old_flags)
239 {
240 	int		err = 0;
241 
242 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
243 	ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
244 	    DLS_PROMISC_PHYS)));
245 
246 	if (old_flags == 0 && dsp->ds_promisc != 0) {
247 		/*
248 		 * If only DLS_PROMISC_SAP, we don't turn on the
249 		 * physical promisc mode
250 		 */
251 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
252 		    dls_rx_promisc, dsp, &dsp->ds_mph,
253 		    (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
254 		    MAC_PROMISC_FLAGS_NO_PHYS);
255 		if (err != 0)
256 			return (err);
257 
258 		/* Remove vlan promisc handle to avoid sending dup copy up */
259 		if (dsp->ds_vlan_mph != NULL) {
260 			mac_promisc_remove(dsp->ds_vlan_mph);
261 			dsp->ds_vlan_mph = NULL;
262 		}
263 	} else if (old_flags != 0 && dsp->ds_promisc == 0) {
264 		ASSERT(dsp->ds_mph != NULL);
265 
266 		mac_promisc_remove(dsp->ds_mph);
267 		dsp->ds_mph = NULL;
268 
269 		if (dsp->ds_sap == ETHERTYPE_VLAN &&
270 		    dsp->ds_dlstate != DL_UNBOUND) {
271 			int err;
272 
273 			if (dsp->ds_vlan_mph != NULL)
274 				return (EINVAL);
275 			err = mac_promisc_add(dsp->ds_mch,
276 			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
277 			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
278 			return (err);
279 		}
280 	} else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
281 	    dsp->ds_promisc != old_flags) {
282 		/*
283 		 * If the old flag is PROMISC_SAP, but the current flag has
284 		 * changed to some new non-zero value, we need to turn the
285 		 * physical promiscuous mode.
286 		 */
287 		ASSERT(dsp->ds_mph != NULL);
288 		mac_promisc_remove(dsp->ds_mph);
289 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
290 		    dls_rx_promisc, dsp, &dsp->ds_mph, 0);
291 	}
292 
293 	return (err);
294 }
295 
296 int
297 dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
298 {
299 	int			err;
300 	dls_multicst_addr_t	**pp;
301 	dls_multicst_addr_t	*p;
302 	uint_t			addr_length;
303 
304 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
305 
306 	/*
307 	 * Check whether the address is in the list of enabled addresses for
308 	 * this dld_str_t.
309 	 */
310 	addr_length = dsp->ds_mip->mi_addr_length;
311 
312 	/*
313 	 * Protect against concurrent access of ds_dmap by data threads using
314 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
315 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
316 	 * ok and is also required by the locking protocol.
317 	 */
318 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
319 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
320 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
321 			/*
322 			 * It is there so there's nothing to do.
323 			 */
324 			err = 0;
325 			goto done;
326 		}
327 	}
328 
329 	/*
330 	 * Allocate a new list item and add it to the list.
331 	 */
332 	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
333 	bcopy(addr, p->dma_addr, addr_length);
334 	*pp = p;
335 	rw_exit(&dsp->ds_rw_lock);
336 
337 	/*
338 	 * Enable the address at the MAC.
339 	 */
340 	err = mac_multicast_add(dsp->ds_mch, addr);
341 	if (err == 0)
342 		return (0);
343 
344 	/* Undo the operation as it has failed */
345 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
346 	ASSERT(*pp == p && p->dma_nextp == NULL);
347 	*pp = NULL;
348 	kmem_free(p, sizeof (dls_multicst_addr_t));
349 done:
350 	rw_exit(&dsp->ds_rw_lock);
351 	return (err);
352 }
353 
354 int
355 dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
356 {
357 	dls_multicst_addr_t	**pp;
358 	dls_multicst_addr_t	*p;
359 	uint_t			addr_length;
360 
361 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
362 
363 	/*
364 	 * Find the address in the list of enabled addresses for this
365 	 * dld_str_t.
366 	 */
367 	addr_length = dsp->ds_mip->mi_addr_length;
368 
369 	/*
370 	 * Protect against concurrent access to ds_dmap by data threads using
371 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
372 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
373 	 * ok and is also required by the locking protocol.
374 	 */
375 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
376 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
377 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
378 			break;
379 	}
380 
381 	/*
382 	 * If we walked to the end of the list then the given address is
383 	 * not currently enabled for this dld_str_t.
384 	 */
385 	if (p == NULL) {
386 		rw_exit(&dsp->ds_rw_lock);
387 		return (ENOENT);
388 	}
389 
390 	/*
391 	 * Remove the address from the list.
392 	 */
393 	*pp = p->dma_nextp;
394 	rw_exit(&dsp->ds_rw_lock);
395 
396 	/*
397 	 * Disable the address at the MAC.
398 	 */
399 	mac_multicast_remove(dsp->ds_mch, addr);
400 	kmem_free(p, sizeof (dls_multicst_addr_t));
401 	return (0);
402 }
403 
404 mblk_t *
405 dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
406     mblk_t **payloadp)
407 {
408 	uint16_t vid;
409 	size_t extra_len;
410 	uint16_t mac_sap;
411 	mblk_t *mp, *payload;
412 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
413 	struct ether_vlan_header *evhp;
414 
415 	vid = mac_client_vid(dsp->ds_mch);
416 	payload = (payloadp == NULL) ? NULL : (*payloadp);
417 
418 	/*
419 	 * In the case of Ethernet, we need to tell mac_header() if we need
420 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
421 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
422 	 * (because such streams will be handling VLAN headers on their own)
423 	 * and one of the following conditions is satisfied:
424 	 *
425 	 * - This is a VLAN stream
426 	 * - This is a physical stream, the priority is not 0, and user
427 	 *   priority tagging is allowed.
428 	 */
429 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
430 	    (vid != VLAN_ID_NONE ||
431 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
432 		extra_len = sizeof (struct ether_vlan_header) -
433 		    sizeof (struct ether_header);
434 		mac_sap = ETHERTYPE_VLAN;
435 	} else {
436 		extra_len = 0;
437 		mac_sap = sap;
438 	}
439 
440 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
441 	if (mp == NULL)
442 		return (NULL);
443 
444 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
445 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
446 		return (mp);
447 
448 	/*
449 	 * Fill in the tag information.
450 	 */
451 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
452 	if (extra_len != 0) {
453 		mp->b_wptr += extra_len;
454 		evhp = (struct ether_vlan_header *)mp->b_rptr;
455 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
456 		evhp->ether_type = htons(sap);
457 	} else {
458 		/*
459 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
460 		 * in the payload. Update the priority.
461 		 */
462 		struct ether_vlan_extinfo *extinfo;
463 		size_t len = sizeof (struct ether_vlan_extinfo);
464 
465 		ASSERT(sap == ETHERTYPE_VLAN);
466 		ASSERT(payload != NULL);
467 
468 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
469 			mblk_t *newmp;
470 
471 			/*
472 			 * Because some DLS consumers only check the db_ref
473 			 * count of the first mblk, we pullup 'payload' into
474 			 * a single mblk.
475 			 */
476 			newmp = msgpullup(payload, -1);
477 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
478 				freemsg(newmp);
479 				freemsg(mp);
480 				return (NULL);
481 			} else {
482 				freemsg(payload);
483 				*payloadp = payload = newmp;
484 			}
485 		}
486 
487 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
488 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
489 		    VLAN_ID(ntohs(extinfo->ether_tci))));
490 	}
491 	return (mp);
492 }
493 
494 void
495 dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
496 {
497 	mutex_enter(&dsp->ds_lock);
498 	dsp->ds_rx = rx;
499 	dsp->ds_rx_arg = arg;
500 	mutex_exit(&dsp->ds_lock);
501 }
502 
503 static boolean_t
504 dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
505     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
506 {
507 	dls_multicst_addr_t	*dmap;
508 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
509 
510 	/*
511 	 * We must not accept packets if the dld_str_t is not marked as bound
512 	 * or is being removed.
513 	 */
514 	if (dsp->ds_dlstate != DL_IDLE)
515 		goto refuse;
516 
517 	if (dsp->ds_promisc != 0) {
518 		/*
519 		 * Filter out packets that arrived from the data path
520 		 * (i_dls_link_rx) when promisc mode is on.
521 		 */
522 		if (!promisc)
523 			goto refuse;
524 		/*
525 		 * If the dls_impl_t is in 'all physical' mode then
526 		 * always accept.
527 		 */
528 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
529 			goto accept;
530 
531 		/*
532 		 * Loopback packets i.e. packets sent out by DLS on a given
533 		 * mac end point, will be accepted back by DLS on loopback
534 		 * from the mac, only in the 'all physical' mode which has been
535 		 * covered by the previous check above
536 		 */
537 		if (promisc_loopback)
538 			goto refuse;
539 	}
540 
541 	switch (mhip->mhi_dsttype) {
542 	case MAC_ADDRTYPE_UNICAST:
543 	case MAC_ADDRTYPE_BROADCAST:
544 		/*
545 		 * We can accept unicast and broadcast packets because
546 		 * filtering is already done by the mac layer.
547 		 */
548 		goto accept;
549 	case MAC_ADDRTYPE_MULTICAST:
550 		/*
551 		 * Additional filtering is needed for multicast addresses
552 		 * because different streams may be interested in different
553 		 * addresses.
554 		 */
555 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
556 			goto accept;
557 
558 		rw_enter(&dsp->ds_rw_lock, RW_READER);
559 		for (dmap = dsp->ds_dmap; dmap != NULL;
560 		    dmap = dmap->dma_nextp) {
561 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
562 			    addr_length) == 0) {
563 				rw_exit(&dsp->ds_rw_lock);
564 				goto accept;
565 			}
566 		}
567 		rw_exit(&dsp->ds_rw_lock);
568 		break;
569 	}
570 
571 refuse:
572 	return (B_FALSE);
573 
574 accept:
575 	/*
576 	 * the returned ds_rx and ds_rx_arg will always be in sync.
577 	 */
578 	mutex_enter(&dsp->ds_lock);
579 	*ds_rx = dsp->ds_rx;
580 	*ds_rx_arg = dsp->ds_rx_arg;
581 	mutex_exit(&dsp->ds_lock);
582 
583 	return (B_TRUE);
584 }
585 
586 /* ARGSUSED */
587 boolean_t
588 dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
589     void **ds_rx_arg)
590 {
591 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
592 	    B_FALSE));
593 }
594 
595 boolean_t
596 dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
597     void **ds_rx_arg, boolean_t loopback)
598 {
599 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
600 	    loopback));
601 }
602 
603 int
604 dls_mac_active_set(dls_link_t *dlp)
605 {
606 	int err = 0;
607 
608 	/*
609 	 * First client; add the primary unicast address.
610 	 */
611 	if (dlp->dl_nactive == 0) {
612 		/*
613 		 * First client; add the primary unicast address.
614 		 */
615 		mac_diag_t diag;
616 
617 		/* request the primary MAC address */
618 		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
619 		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
620 		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah, 0,
621 		    &diag)) != 0) {
622 			return (err);
623 		}
624 
625 		/*
626 		 * Set the function to start receiving packets.
627 		 */
628 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
629 	}
630 	dlp->dl_nactive++;
631 	return (0);
632 }
633 
634 void
635 dls_mac_active_clear(dls_link_t *dlp)
636 {
637 	if (--dlp->dl_nactive == 0) {
638 		ASSERT(dlp->dl_mah != NULL);
639 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
640 		dlp->dl_mah = NULL;
641 		mac_rx_clear(dlp->dl_mch);
642 	}
643 }
644 
645 int
646 dls_active_set(dld_str_t *dsp)
647 {
648 	int err = 0;
649 
650 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
651 
652 	if (dsp->ds_passivestate == DLD_PASSIVE)
653 		return (0);
654 
655 	/* If we're already active, then there's nothing more to do. */
656 	if ((dsp->ds_nactive == 0) &&
657 	    ((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
658 		/* except for ENXIO all other errors are mapped to EBUSY */
659 		if (err != ENXIO)
660 			return (EBUSY);
661 		return (err);
662 	}
663 
664 	dsp->ds_passivestate = DLD_ACTIVE;
665 	dsp->ds_nactive++;
666 	return (0);
667 }
668 
669 /*
670  * Note that dls_active_set() is called whenever an active operation
671  * (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
672  * dls_active_clear(dsp, B_FALSE) is called whenever the active operation
673  * is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
674  * a stream is closed without every active operation being undone and we
675  * need to clear all the "active" states by calling
676  * dls_active_clear(dsp, B_TRUE).
677  */
678 void
679 dls_active_clear(dld_str_t *dsp, boolean_t all)
680 {
681 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
682 
683 	if (dsp->ds_passivestate == DLD_PASSIVE)
684 		return;
685 
686 	if (all && dsp->ds_nactive == 0)
687 		return;
688 
689 	ASSERT(dsp->ds_nactive > 0);
690 
691 	dsp->ds_nactive -= (all ? dsp->ds_nactive : 1);
692 	if (dsp->ds_nactive != 0)
693 		return;
694 
695 	ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
696 	dls_mac_active_clear(dsp->ds_dlp);
697 	dsp->ds_passivestate = DLD_UNINITIALIZED;
698 }
699