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 
 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
 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
 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
 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
 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 *
 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
 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
 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
 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
 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
 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
 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
 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
 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 }