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