xref: /illumos-gate/usr/src/uts/common/io/dls/dls.c (revision f83ffe1aa13dc057aa65c36bacc83297b35f9be2)
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 		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 void
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 		mac_promisc_remove(dsp->ds_vlan_mph);
204 		dsp->ds_vlan_mph = NULL;
205 		return;
206 	}
207 
208 	/*
209 	 * Unbind the dld_str_t by removing it from the hash table in the
210 	 * dls_link_t.
211 	 */
212 	dls_link_remove(dsp->ds_dlp, dsp);
213 	dsp->ds_sap = 0;
214 }
215 
216 int
217 dls_promisc(dld_str_t *dsp, uint32_t old_flags)
218 {
219 	int		err = 0;
220 
221 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
222 	ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
223 	    DLS_PROMISC_PHYS)));
224 
225 	if (old_flags == 0 && dsp->ds_promisc != 0) {
226 		/*
227 		 * If only DLS_PROMISC_SAP, we don't turn on the
228 		 * physical promisc mode
229 		 */
230 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
231 		    dls_rx_promisc, dsp, &dsp->ds_mph,
232 		    (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
233 		    MAC_PROMISC_FLAGS_NO_PHYS);
234 		if (err != 0)
235 			return (err);
236 
237 		/* Remove vlan promisc handle to avoid sending dup copy up */
238 		if (dsp->ds_vlan_mph != NULL) {
239 			mac_promisc_remove(dsp->ds_vlan_mph);
240 			dsp->ds_vlan_mph = NULL;
241 		}
242 	} else if (old_flags != 0 && dsp->ds_promisc == 0) {
243 		ASSERT(dsp->ds_mph != NULL);
244 
245 		mac_promisc_remove(dsp->ds_mph);
246 		dsp->ds_mph = NULL;
247 
248 		if (dsp->ds_sap == ETHERTYPE_VLAN &&
249 		    dsp->ds_dlstate != DL_UNBOUND) {
250 			int err;
251 
252 			if (dsp->ds_vlan_mph != NULL)
253 				return (EINVAL);
254 			err = mac_promisc_add(dsp->ds_mch,
255 			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
256 			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
257 			return (err);
258 		}
259 	} else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
260 	    dsp->ds_promisc != old_flags) {
261 		/*
262 		 * If the old flag is PROMISC_SAP, but the current flag has
263 		 * changed to some new non-zero value, we need to turn the
264 		 * physical promiscuous mode.
265 		 */
266 		ASSERT(dsp->ds_mph != NULL);
267 		mac_promisc_remove(dsp->ds_mph);
268 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
269 		    dls_rx_promisc, dsp, &dsp->ds_mph, 0);
270 	}
271 
272 	return (err);
273 }
274 
275 int
276 dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
277 {
278 	int			err;
279 	dls_multicst_addr_t	**pp;
280 	dls_multicst_addr_t	*p;
281 	uint_t			addr_length;
282 
283 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
284 
285 	/*
286 	 * Check whether the address is in the list of enabled addresses for
287 	 * this dld_str_t.
288 	 */
289 	addr_length = dsp->ds_mip->mi_addr_length;
290 
291 	/*
292 	 * Protect against concurrent access of ds_dmap by data threads using
293 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
294 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
295 	 * ok and is also required by the locking protocol.
296 	 */
297 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
298 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
299 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
300 			/*
301 			 * It is there so there's nothing to do.
302 			 */
303 			err = 0;
304 			goto done;
305 		}
306 	}
307 
308 	/*
309 	 * Allocate a new list item and add it to the list.
310 	 */
311 	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
312 	bcopy(addr, p->dma_addr, addr_length);
313 	*pp = p;
314 	rw_exit(&dsp->ds_rw_lock);
315 
316 	/*
317 	 * Enable the address at the MAC.
318 	 */
319 	err = mac_multicast_add(dsp->ds_mch, addr);
320 	if (err == 0)
321 		return (0);
322 
323 	/* Undo the operation as it has failed */
324 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
325 	ASSERT(*pp == p && p->dma_nextp == NULL);
326 	*pp = NULL;
327 	kmem_free(p, sizeof (dls_multicst_addr_t));
328 done:
329 	rw_exit(&dsp->ds_rw_lock);
330 	return (err);
331 }
332 
333 int
334 dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
335 {
336 	dls_multicst_addr_t	**pp;
337 	dls_multicst_addr_t	*p;
338 	uint_t			addr_length;
339 
340 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
341 
342 	/*
343 	 * Find the address in the list of enabled addresses for this
344 	 * dld_str_t.
345 	 */
346 	addr_length = dsp->ds_mip->mi_addr_length;
347 
348 	/*
349 	 * Protect against concurrent access to ds_dmap by data threads using
350 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
351 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
352 	 * ok and is also required by the locking protocol.
353 	 */
354 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
355 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
356 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
357 			break;
358 	}
359 
360 	/*
361 	 * If we walked to the end of the list then the given address is
362 	 * not currently enabled for this dld_str_t.
363 	 */
364 	if (p == NULL) {
365 		rw_exit(&dsp->ds_rw_lock);
366 		return (ENOENT);
367 	}
368 
369 	/*
370 	 * Remove the address from the list.
371 	 */
372 	*pp = p->dma_nextp;
373 	rw_exit(&dsp->ds_rw_lock);
374 
375 	/*
376 	 * Disable the address at the MAC.
377 	 */
378 	mac_multicast_remove(dsp->ds_mch, addr);
379 	kmem_free(p, sizeof (dls_multicst_addr_t));
380 	return (0);
381 }
382 
383 mblk_t *
384 dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
385     mblk_t **payloadp)
386 {
387 	uint16_t vid;
388 	size_t extra_len;
389 	uint16_t mac_sap;
390 	mblk_t *mp, *payload;
391 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
392 	struct ether_vlan_header *evhp;
393 
394 	vid = mac_client_vid(dsp->ds_mch);
395 	payload = (payloadp == NULL) ? NULL : (*payloadp);
396 
397 	/*
398 	 * In the case of Ethernet, we need to tell mac_header() if we need
399 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
400 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
401 	 * (because such streams will be handling VLAN headers on their own)
402 	 * and one of the following conditions is satisfied:
403 	 *
404 	 * - This is a VLAN stream
405 	 * - This is a physical stream, the priority is not 0, and user
406 	 *   priority tagging is allowed.
407 	 */
408 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
409 	    (vid != VLAN_ID_NONE ||
410 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
411 		extra_len = sizeof (struct ether_vlan_header) -
412 		    sizeof (struct ether_header);
413 		mac_sap = ETHERTYPE_VLAN;
414 	} else {
415 		extra_len = 0;
416 		mac_sap = sap;
417 	}
418 
419 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
420 	if (mp == NULL)
421 		return (NULL);
422 
423 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
424 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
425 		return (mp);
426 
427 	/*
428 	 * Fill in the tag information.
429 	 */
430 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
431 	if (extra_len != 0) {
432 		mp->b_wptr += extra_len;
433 		evhp = (struct ether_vlan_header *)mp->b_rptr;
434 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
435 		evhp->ether_type = htons(sap);
436 	} else {
437 		/*
438 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
439 		 * in the payload. Update the priority.
440 		 */
441 		struct ether_vlan_extinfo *extinfo;
442 		size_t len = sizeof (struct ether_vlan_extinfo);
443 
444 		ASSERT(sap == ETHERTYPE_VLAN);
445 		ASSERT(payload != NULL);
446 
447 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
448 			mblk_t *newmp;
449 
450 			/*
451 			 * Because some DLS consumers only check the db_ref
452 			 * count of the first mblk, we pullup 'payload' into
453 			 * a single mblk.
454 			 */
455 			newmp = msgpullup(payload, -1);
456 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
457 				freemsg(newmp);
458 				freemsg(mp);
459 				return (NULL);
460 			} else {
461 				freemsg(payload);
462 				*payloadp = payload = newmp;
463 			}
464 		}
465 
466 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
467 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
468 		    VLAN_ID(ntohs(extinfo->ether_tci))));
469 	}
470 	return (mp);
471 }
472 
473 void
474 dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
475 {
476 	mutex_enter(&dsp->ds_lock);
477 	dsp->ds_rx = rx;
478 	dsp->ds_rx_arg = arg;
479 	mutex_exit(&dsp->ds_lock);
480 }
481 
482 static boolean_t
483 dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
484     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
485 {
486 	dls_multicst_addr_t	*dmap;
487 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
488 
489 	/*
490 	 * We must not accept packets if the dld_str_t is not marked as bound
491 	 * or is being removed.
492 	 */
493 	if (dsp->ds_dlstate != DL_IDLE)
494 		goto refuse;
495 
496 	if (dsp->ds_promisc != 0) {
497 		/*
498 		 * Filter out packets that arrived from the data path
499 		 * (i_dls_link_rx) when promisc mode is on.
500 		 */
501 		if (!promisc)
502 			goto refuse;
503 		/*
504 		 * If the dls_impl_t is in 'all physical' mode then
505 		 * always accept.
506 		 */
507 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
508 			goto accept;
509 
510 		/*
511 		 * Loopback packets i.e. packets sent out by DLS on a given
512 		 * mac end point, will be accepted back by DLS on loopback
513 		 * from the mac, only in the 'all physical' mode which has been
514 		 * covered by the previous check above
515 		 */
516 		if (promisc_loopback)
517 			goto refuse;
518 	}
519 
520 	switch (mhip->mhi_dsttype) {
521 	case MAC_ADDRTYPE_UNICAST:
522 	case MAC_ADDRTYPE_BROADCAST:
523 		/*
524 		 * We can accept unicast and broadcast packets because
525 		 * filtering is already done by the mac layer.
526 		 */
527 		goto accept;
528 	case MAC_ADDRTYPE_MULTICAST:
529 		/*
530 		 * Additional filtering is needed for multicast addresses
531 		 * because different streams may be interested in different
532 		 * addresses.
533 		 */
534 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
535 			goto accept;
536 
537 		rw_enter(&dsp->ds_rw_lock, RW_READER);
538 		for (dmap = dsp->ds_dmap; dmap != NULL;
539 		    dmap = dmap->dma_nextp) {
540 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
541 			    addr_length) == 0) {
542 				rw_exit(&dsp->ds_rw_lock);
543 				goto accept;
544 			}
545 		}
546 		rw_exit(&dsp->ds_rw_lock);
547 		break;
548 	}
549 
550 refuse:
551 	return (B_FALSE);
552 
553 accept:
554 	/*
555 	 * the returned ds_rx and ds_rx_arg will always be in sync.
556 	 */
557 	mutex_enter(&dsp->ds_lock);
558 	*ds_rx = dsp->ds_rx;
559 	*ds_rx_arg = dsp->ds_rx_arg;
560 	mutex_exit(&dsp->ds_lock);
561 
562 	return (B_TRUE);
563 }
564 
565 /* ARGSUSED */
566 boolean_t
567 dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
568     void **ds_rx_arg)
569 {
570 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
571 	    B_FALSE));
572 }
573 
574 boolean_t
575 dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
576     void **ds_rx_arg, boolean_t loopback)
577 {
578 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
579 	    loopback));
580 }
581 
582 int
583 dls_mac_active_set(dls_link_t *dlp)
584 {
585 	int err = 0;
586 
587 	/*
588 	 * First client; add the primary unicast address.
589 	 */
590 	if (dlp->dl_nactive == 0) {
591 		/*
592 		 * First client; add the primary unicast address.
593 		 */
594 		mac_diag_t diag;
595 
596 		/* request the primary MAC address */
597 		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
598 		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
599 		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah, 0,
600 		    &diag)) != 0) {
601 			return (err);
602 		}
603 
604 		/*
605 		 * Set the function to start receiving packets.
606 		 */
607 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
608 	}
609 	dlp->dl_nactive++;
610 	return (0);
611 }
612 
613 void
614 dls_mac_active_clear(dls_link_t *dlp)
615 {
616 	if (--dlp->dl_nactive == 0) {
617 		ASSERT(dlp->dl_mah != NULL);
618 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
619 		dlp->dl_mah = NULL;
620 		mac_rx_clear(dlp->dl_mch);
621 	}
622 }
623 
624 int
625 dls_active_set(dld_str_t *dsp)
626 {
627 	int err = 0;
628 
629 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
630 
631 	/* If we're already active, then there's nothing more to do. */
632 	if (dsp->ds_active)
633 		return (0);
634 
635 	if ((err = dls_mac_active_set(dsp->ds_dlp)) != 0) {
636 		/* except for ENXIO all other errors are mapped to EBUSY */
637 		if (err != ENXIO)
638 			return (EBUSY);
639 		return (err);
640 	}
641 
642 	dsp->ds_active = B_TRUE;
643 	return (0);
644 }
645 
646 void
647 dls_active_clear(dld_str_t *dsp)
648 {
649 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
650 
651 	if (!dsp->ds_active)
652 		return;
653 
654 	dls_mac_active_clear(dsp->ds_dlp);
655 	dsp->ds_active = B_FALSE;
656 }
657