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