xref: /illumos-gate/usr/src/uts/common/io/dls/dls.c (revision 2264ca7f5db194583c672cb5779a67f52bcd92a9)
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 
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 	 * In the case of Ethernet, we need to tell mac_header() if we need
405 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
406 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
407 	 * (because such streams will be handling VLAN headers on their own)
408 	 * and one of the following conditions is satisfied:
409 	 *
410 	 * - This is a VLAN stream
411 	 * - This is a physical stream, the priority is not 0, and user
412 	 *   priority tagging is allowed.
413 	 */
414 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
415 	    (vid != VLAN_ID_NONE ||
416 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
417 		extra_len = sizeof (struct ether_vlan_header) -
418 		    sizeof (struct ether_header);
419 		mac_sap = ETHERTYPE_VLAN;
420 	} else {
421 		extra_len = 0;
422 		mac_sap = sap;
423 	}
424 
425 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
426 	if (mp == NULL)
427 		return (NULL);
428 
429 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
430 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
431 		return (mp);
432 
433 	/*
434 	 * Fill in the tag information.
435 	 */
436 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
437 	if (extra_len != 0) {
438 		mp->b_wptr += extra_len;
439 		evhp = (struct ether_vlan_header *)mp->b_rptr;
440 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
441 		evhp->ether_type = htons(sap);
442 	} else {
443 		/*
444 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
445 		 * in the payload. Update the priority.
446 		 */
447 		struct ether_vlan_extinfo *extinfo;
448 		size_t len = sizeof (struct ether_vlan_extinfo);
449 
450 		ASSERT(sap == ETHERTYPE_VLAN);
451 		ASSERT(payload != NULL);
452 
453 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
454 			mblk_t *newmp;
455 
456 			/*
457 			 * Because some DLS consumers only check the db_ref
458 			 * count of the first mblk, we pullup 'payload' into
459 			 * a single mblk.
460 			 */
461 			newmp = msgpullup(payload, -1);
462 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
463 				freemsg(newmp);
464 				freemsg(mp);
465 				return (NULL);
466 			} else {
467 				freemsg(payload);
468 				*payloadp = payload = newmp;
469 			}
470 		}
471 
472 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
473 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
474 		    VLAN_ID(ntohs(extinfo->ether_tci))));
475 	}
476 	return (mp);
477 }
478 
479 void
480 dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
481 {
482 	mutex_enter(&dsp->ds_lock);
483 	dsp->ds_rx = rx;
484 	dsp->ds_rx_arg = arg;
485 	mutex_exit(&dsp->ds_lock);
486 }
487 
488 static boolean_t
489 dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
490     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
491 {
492 	dls_multicst_addr_t	*dmap;
493 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
494 
495 	/*
496 	 * We must not accept packets if the dld_str_t is not marked as bound
497 	 * or is being removed.
498 	 */
499 	if (dsp->ds_dlstate != DL_IDLE)
500 		goto refuse;
501 
502 	if (dsp->ds_promisc != 0) {
503 		/*
504 		 * Filter out packets that arrived from the data path
505 		 * (i_dls_link_rx) when promisc mode is on.
506 		 */
507 		if (!promisc)
508 			goto refuse;
509 		/*
510 		 * If the dls_impl_t is in 'all physical' mode then
511 		 * always accept.
512 		 */
513 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
514 			goto accept;
515 
516 		/*
517 		 * Loopback packets i.e. packets sent out by DLS on a given
518 		 * mac end point, will be accepted back by DLS on loopback
519 		 * from the mac, only in the 'all physical' mode which has been
520 		 * covered by the previous check above
521 		 */
522 		if (promisc_loopback)
523 			goto refuse;
524 	}
525 
526 	switch (mhip->mhi_dsttype) {
527 	case MAC_ADDRTYPE_UNICAST:
528 	case MAC_ADDRTYPE_BROADCAST:
529 		/*
530 		 * We can accept unicast and broadcast packets because
531 		 * filtering is already done by the mac layer.
532 		 */
533 		goto accept;
534 	case MAC_ADDRTYPE_MULTICAST:
535 		/*
536 		 * Additional filtering is needed for multicast addresses
537 		 * because different streams may be interested in different
538 		 * addresses.
539 		 */
540 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
541 			goto accept;
542 
543 		rw_enter(&dsp->ds_rw_lock, RW_READER);
544 		for (dmap = dsp->ds_dmap; dmap != NULL;
545 		    dmap = dmap->dma_nextp) {
546 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
547 			    addr_length) == 0) {
548 				rw_exit(&dsp->ds_rw_lock);
549 				goto accept;
550 			}
551 		}
552 		rw_exit(&dsp->ds_rw_lock);
553 		break;
554 	}
555 
556 refuse:
557 	return (B_FALSE);
558 
559 accept:
560 	/*
561 	 * the returned ds_rx and ds_rx_arg will always be in sync.
562 	 */
563 	mutex_enter(&dsp->ds_lock);
564 	*ds_rx = dsp->ds_rx;
565 	*ds_rx_arg = dsp->ds_rx_arg;
566 	mutex_exit(&dsp->ds_lock);
567 
568 	return (B_TRUE);
569 }
570 
571 /* ARGSUSED */
572 boolean_t
573 dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
574     void **ds_rx_arg)
575 {
576 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
577 	    B_FALSE));
578 }
579 
580 boolean_t
581 dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
582     void **ds_rx_arg, boolean_t loopback)
583 {
584 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
585 	    loopback));
586 }
587 
588 int
589 dls_mac_active_set(dls_link_t *dlp)
590 {
591 	int err = 0;
592 
593 	/*
594 	 * First client; add the primary unicast address.
595 	 */
596 	if (dlp->dl_nactive == 0) {
597 		/*
598 		 * First client; add the primary unicast address.
599 		 */
600 		mac_diag_t diag;
601 
602 		/* request the primary MAC address */
603 		if ((err = mac_unicast_primary_add(dlp->dl_mch, &dlp->dl_mah,
604 		    &diag)) != 0) {
605 			return (err);
606 		}
607 
608 		/*
609 		 * Set the function to start receiving packets.
610 		 */
611 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
612 	}
613 	dlp->dl_nactive++;
614 	return (0);
615 }
616 
617 void
618 dls_mac_active_clear(dls_link_t *dlp)
619 {
620 	if (--dlp->dl_nactive == 0) {
621 		ASSERT(dlp->dl_mah != NULL);
622 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
623 		dlp->dl_mah = NULL;
624 		mac_rx_clear(dlp->dl_mch);
625 	}
626 }
627 
628 int
629 dls_active_set(dld_str_t *dsp)
630 {
631 	int err = 0;
632 
633 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
634 
635 	/* If we're already active, then there's nothing more to do. */
636 	if (dsp->ds_active)
637 		return (0);
638 
639 	if ((err = dls_mac_active_set(dsp->ds_dlp)) != 0) {
640 		/* except for ENXIO all other errors are mapped to EBUSY */
641 		if (err != ENXIO)
642 			return (EBUSY);
643 		return (err);
644 	}
645 
646 	dsp->ds_active = B_TRUE;
647 	return (0);
648 }
649 
650 void
651 dls_active_clear(dld_str_t *dsp)
652 {
653 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
654 
655 	if (!dsp->ds_active)
656 		return;
657 
658 	dls_mac_active_clear(dsp->ds_dlp);
659 	dsp->ds_active = B_FALSE;
660 }
661