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