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