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