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