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