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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2012 OmniTI Computer Consulting, Inc All rights reserved. 25 * Copyright 2018 Joyent, Inc. 26 */ 27 28 /* 29 * IEEE 802.3ad Link Aggregation - Link Aggregation MAC ports. 30 * 31 * Implements the functions needed to manage the MAC ports that are 32 * part of Link Aggregation groups. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/sysmacros.h> 37 #include <sys/conf.h> 38 #include <sys/cmn_err.h> 39 #include <sys/id_space.h> 40 #include <sys/list.h> 41 #include <sys/ksynch.h> 42 #include <sys/kmem.h> 43 #include <sys/stream.h> 44 #include <sys/modctl.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/atomic.h> 48 #include <sys/stat.h> 49 #include <sys/sdt.h> 50 #include <sys/dlpi.h> 51 #include <sys/dls.h> 52 #include <sys/aggr.h> 53 #include <sys/aggr_impl.h> 54 55 static kmem_cache_t *aggr_port_cache; 56 static id_space_t *aggr_portids; 57 58 static void aggr_port_notify_cb(void *, mac_notify_type_t); 59 60 /*ARGSUSED*/ 61 static int 62 aggr_port_constructor(void *buf, void *arg, int kmflag) 63 { 64 bzero(buf, sizeof (aggr_port_t)); 65 return (0); 66 } 67 68 /*ARGSUSED*/ 69 static void 70 aggr_port_destructor(void *buf, void *arg) 71 { 72 aggr_port_t *port = buf; 73 74 ASSERT(port->lp_mnh == NULL); 75 ASSERT(port->lp_mphp == NULL); 76 ASSERT(!port->lp_rx_grp_added && !port->lp_tx_grp_added); 77 ASSERT(port->lp_hwgh == NULL); 78 } 79 80 void 81 aggr_port_init(void) 82 { 83 aggr_port_cache = kmem_cache_create("aggr_port_cache", 84 sizeof (aggr_port_t), 0, aggr_port_constructor, 85 aggr_port_destructor, NULL, NULL, NULL, 0); 86 87 /* 88 * Allocate a id space to manage port identification. The range of 89 * the arena will be from 1 to UINT16_MAX, because the LACP protocol 90 * specifies 16-bit unique identification. 91 */ 92 aggr_portids = id_space_create("aggr_portids", 1, UINT16_MAX); 93 ASSERT(aggr_portids != NULL); 94 } 95 96 void 97 aggr_port_fini(void) 98 { 99 /* 100 * This function is called only after all groups have been 101 * freed. This ensures that there are no remaining allocated 102 * ports when this function is invoked. 103 */ 104 kmem_cache_destroy(aggr_port_cache); 105 id_space_destroy(aggr_portids); 106 } 107 108 /* ARGSUSED */ 109 void 110 aggr_port_init_callbacks(aggr_port_t *port) 111 { 112 /* add the port's receive callback */ 113 port->lp_mnh = mac_notify_add(port->lp_mh, aggr_port_notify_cb, port); 114 /* 115 * Hold a reference of the grp and the port and this reference will 116 * be released when the thread exits. 117 * 118 * The reference on the port is used for aggr_port_delete() to 119 * continue without waiting for the thread to exit; the reference 120 * on the grp is used for aggr_grp_delete() to wait for the thread 121 * to exit before calling mac_unregister(). 122 * 123 * Note that these references will be released either in 124 * aggr_port_delete() when mac_notify_remove() succeeds, or in 125 * the aggr_port_notify_cb() callback when the port is deleted 126 * (lp_closing is set). 127 */ 128 aggr_grp_port_hold(port); 129 } 130 131 /* ARGSUSED */ 132 int 133 aggr_port_create(aggr_grp_t *grp, const datalink_id_t linkid, boolean_t force, 134 aggr_port_t **pp) 135 { 136 int err; 137 mac_handle_t mh; 138 mac_client_handle_t mch = NULL; 139 aggr_port_t *port; 140 uint16_t portid; 141 uint_t i; 142 boolean_t no_link_update = B_FALSE; 143 const mac_info_t *mip; 144 uint32_t note; 145 uint32_t margin; 146 char client_name[MAXNAMELEN]; 147 char aggr_name[MAXNAMELEN]; 148 char port_name[MAXNAMELEN]; 149 mac_diag_t diag; 150 mac_unicast_handle_t mah; 151 152 *pp = NULL; 153 154 if ((err = mac_open_by_linkid(linkid, &mh)) != 0) 155 return (err); 156 157 mip = mac_info(mh); 158 if (mip->mi_media != DL_ETHER || mip->mi_nativemedia != DL_ETHER) { 159 err = EINVAL; 160 goto fail; 161 } 162 163 /* 164 * If the underlying MAC does not support link update notification, it 165 * can only be aggregated if `force' is set. This is because aggr 166 * depends on link notifications to attach ports whose link is up. 167 */ 168 note = mac_no_notification(mh); 169 if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) { 170 no_link_update = B_TRUE; 171 if (!force) { 172 /* 173 * We borrow this error code to indicate that link 174 * notification is not supported. 175 */ 176 err = ENETDOWN; 177 goto fail; 178 } 179 } 180 181 if (((err = dls_mgmt_get_linkinfo(grp->lg_linkid, 182 aggr_name, NULL, NULL, NULL)) != 0) || 183 ((err = dls_mgmt_get_linkinfo(linkid, port_name, 184 NULL, NULL, NULL)) != 0)) { 185 goto fail; 186 } 187 188 (void) snprintf(client_name, MAXNAMELEN, "%s-%s", aggr_name, port_name); 189 if ((err = mac_client_open(mh, &mch, client_name, 190 MAC_OPEN_FLAGS_IS_AGGR_PORT | MAC_OPEN_FLAGS_EXCLUSIVE)) != 0) { 191 goto fail; 192 } 193 194 if ((portid = (uint16_t)id_alloc(aggr_portids)) == 0) { 195 err = ENOMEM; 196 goto fail; 197 } 198 199 /* 200 * As the underlying mac's current margin size is used to determine 201 * the margin size of the aggregation itself, request the underlying 202 * mac not to change to a smaller size. 203 */ 204 if ((err = mac_margin_add(mh, &margin, B_TRUE)) != 0) { 205 id_free(aggr_portids, portid); 206 goto fail; 207 } 208 209 if ((err = mac_unicast_add(mch, NULL, MAC_UNICAST_PRIMARY | 210 MAC_UNICAST_DISABLE_TX_VID_CHECK, &mah, 0, &diag)) != 0) { 211 VERIFY(mac_margin_remove(mh, margin) == 0); 212 id_free(aggr_portids, portid); 213 goto fail; 214 } 215 216 port = kmem_cache_alloc(aggr_port_cache, KM_SLEEP); 217 218 port->lp_refs = 1; 219 port->lp_next = NULL; 220 port->lp_mh = mh; 221 port->lp_mch = mch; 222 port->lp_mip = mip; 223 port->lp_linkid = linkid; 224 port->lp_closing = B_FALSE; 225 port->lp_mah = mah; 226 227 /* get the port's original MAC address */ 228 mac_unicast_primary_get(port->lp_mh, port->lp_addr); 229 230 /* initialize state */ 231 port->lp_state = AGGR_PORT_STATE_STANDBY; 232 port->lp_link_state = LINK_STATE_UNKNOWN; 233 port->lp_ifspeed = 0; 234 port->lp_link_duplex = LINK_DUPLEX_UNKNOWN; 235 port->lp_started = B_FALSE; 236 port->lp_tx_enabled = B_FALSE; 237 port->lp_promisc_on = B_FALSE; 238 port->lp_no_link_update = no_link_update; 239 port->lp_portid = portid; 240 port->lp_margin = margin; 241 port->lp_prom_addr = NULL; 242 243 /* 244 * Save the current statistics of the port. They will be used 245 * later by aggr_m_stats() when aggregating the statistics of 246 * the constituent ports. 247 */ 248 for (i = 0; i < MAC_NSTAT; i++) { 249 port->lp_stat[i] = 250 aggr_port_stat(port, i + MAC_STAT_MIN); 251 } 252 for (i = 0; i < ETHER_NSTAT; i++) { 253 port->lp_ether_stat[i] = 254 aggr_port_stat(port, i + MACTYPE_STAT_MIN); 255 } 256 257 /* LACP related state */ 258 port->lp_collector_enabled = B_FALSE; 259 260 *pp = port; 261 return (0); 262 263 fail: 264 if (mch != NULL) 265 mac_client_close(mch, MAC_CLOSE_FLAGS_EXCLUSIVE); 266 mac_close(mh); 267 return (err); 268 } 269 270 void 271 aggr_port_delete(aggr_port_t *port) 272 { 273 aggr_lacp_port_t *pl = &port->lp_lacp; 274 275 ASSERT(port->lp_mphp == NULL); 276 ASSERT(!port->lp_promisc_on); 277 278 port->lp_closing = B_TRUE; 279 280 VERIFY(mac_margin_remove(port->lp_mh, port->lp_margin) == 0); 281 mac_rx_clear(port->lp_mch); 282 /* 283 * If the notification callback is already in process and waiting for 284 * the aggr grp's mac perimeter, don't wait (otherwise there would be 285 * deadlock). Otherwise, if mac_notify_remove() succeeds, we can 286 * release the reference held when mac_notify_add() is called. 287 */ 288 if ((port->lp_mnh != NULL) && 289 (mac_notify_remove(port->lp_mnh, B_FALSE) == 0)) { 290 aggr_grp_port_rele(port); 291 } 292 port->lp_mnh = NULL; 293 294 /* 295 * Inform the the port lacp timer thread to exit. Note that waiting 296 * for the thread to exit may cause deadlock since that thread may 297 * need to enter into the mac perimeter which we are currently in. 298 * It is fine to continue without waiting though since that thread 299 * is holding a reference of the port. 300 */ 301 mutex_enter(&pl->lacp_timer_lock); 302 pl->lacp_timer_bits |= LACP_THREAD_EXIT; 303 cv_broadcast(&pl->lacp_timer_cv); 304 mutex_exit(&pl->lacp_timer_lock); 305 306 /* 307 * Restore the port MAC address. Note it is called after the 308 * port's notification callback being removed. This prevent 309 * port's MAC_NOTE_UNICST notify callback function being called. 310 */ 311 (void) mac_unicast_primary_set(port->lp_mh, port->lp_addr); 312 if (port->lp_mah != NULL) 313 (void) mac_unicast_remove(port->lp_mch, port->lp_mah); 314 mac_client_close(port->lp_mch, MAC_CLOSE_FLAGS_EXCLUSIVE); 315 mac_close(port->lp_mh); 316 AGGR_PORT_REFRELE(port); 317 } 318 319 void 320 aggr_port_free(aggr_port_t *port) 321 { 322 ASSERT(port->lp_refs == 0); 323 if (port->lp_grp != NULL) 324 AGGR_GRP_REFRELE(port->lp_grp); 325 port->lp_grp = NULL; 326 id_free(aggr_portids, port->lp_portid); 327 port->lp_portid = 0; 328 mutex_destroy(&port->lp_lacp.lacp_timer_lock); 329 cv_destroy(&port->lp_lacp.lacp_timer_cv); 330 kmem_cache_free(aggr_port_cache, port); 331 } 332 333 /* 334 * Invoked upon receiving a MAC_NOTE_LINK notification for 335 * one of the constituent ports. 336 */ 337 boolean_t 338 aggr_port_notify_link(aggr_grp_t *grp, aggr_port_t *port) 339 { 340 boolean_t do_attach = B_FALSE; 341 boolean_t do_detach = B_FALSE; 342 boolean_t link_state_changed = B_TRUE; 343 uint64_t ifspeed; 344 link_state_t link_state; 345 link_duplex_t link_duplex; 346 mac_perim_handle_t mph; 347 348 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 349 mac_perim_enter_by_mh(port->lp_mh, &mph); 350 351 /* 352 * link state change? For links that do not support link state 353 * notification, always assume the link is up. 354 */ 355 link_state = port->lp_no_link_update ? LINK_STATE_UP : 356 mac_link_get(port->lp_mh); 357 if (port->lp_link_state != link_state) { 358 if (link_state == LINK_STATE_UP) 359 do_attach = (port->lp_link_state != LINK_STATE_UP); 360 else 361 do_detach = (port->lp_link_state == LINK_STATE_UP); 362 } 363 port->lp_link_state = link_state; 364 365 /* link duplex change? */ 366 link_duplex = aggr_port_stat(port, ETHER_STAT_LINK_DUPLEX); 367 if (port->lp_link_duplex != link_duplex) { 368 if (link_duplex == LINK_DUPLEX_FULL) 369 do_attach |= (port->lp_link_duplex != LINK_DUPLEX_FULL); 370 else 371 do_detach |= (port->lp_link_duplex == LINK_DUPLEX_FULL); 372 } 373 port->lp_link_duplex = link_duplex; 374 375 /* link speed changes? */ 376 ifspeed = aggr_port_stat(port, MAC_STAT_IFSPEED); 377 if (port->lp_ifspeed != ifspeed) { 378 mutex_enter(&grp->lg_stat_lock); 379 380 if (port->lp_state == AGGR_PORT_STATE_ATTACHED) 381 do_detach |= (ifspeed != grp->lg_ifspeed); 382 else 383 do_attach |= (ifspeed == grp->lg_ifspeed); 384 385 mutex_exit(&grp->lg_stat_lock); 386 } 387 port->lp_ifspeed = ifspeed; 388 389 if (do_attach) { 390 /* attempt to attach the port to the aggregation */ 391 link_state_changed = aggr_grp_attach_port(grp, port); 392 } else if (do_detach) { 393 /* detach the port from the aggregation */ 394 link_state_changed = aggr_grp_detach_port(grp, port); 395 } 396 397 mac_perim_exit(mph); 398 return (link_state_changed); 399 } 400 401 /* 402 * Invoked upon receiving a MAC_NOTE_UNICST for one of the constituent 403 * ports of a group. 404 */ 405 static void 406 aggr_port_notify_unicst(aggr_grp_t *grp, aggr_port_t *port, 407 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 408 { 409 boolean_t mac_addr_changed = B_FALSE; 410 boolean_t link_state_changed = B_FALSE; 411 uint8_t mac_addr[ETHERADDRL]; 412 mac_perim_handle_t mph; 413 414 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 415 ASSERT(mac_addr_changedp != NULL); 416 ASSERT(link_state_changedp != NULL); 417 mac_perim_enter_by_mh(port->lp_mh, &mph); 418 419 /* 420 * If it is called when setting the MAC address to the 421 * aggregation group MAC address, do nothing. 422 */ 423 mac_unicast_primary_get(port->lp_mh, mac_addr); 424 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 425 mac_perim_exit(mph); 426 goto done; 427 } 428 429 /* save the new port MAC address */ 430 bcopy(mac_addr, port->lp_addr, ETHERADDRL); 431 432 aggr_grp_port_mac_changed(grp, port, &mac_addr_changed, 433 &link_state_changed); 434 435 mac_perim_exit(mph); 436 437 /* 438 * If this port was used to determine the MAC address of 439 * the group, update the MAC address of the constituent 440 * ports. 441 */ 442 if (mac_addr_changed && aggr_grp_update_ports_mac(grp)) 443 link_state_changed = B_TRUE; 444 445 done: 446 *mac_addr_changedp = mac_addr_changed; 447 *link_state_changedp = link_state_changed; 448 } 449 450 /* 451 * Notification callback invoked by the MAC service module for 452 * a particular MAC port. 453 */ 454 static void 455 aggr_port_notify_cb(void *arg, mac_notify_type_t type) 456 { 457 aggr_port_t *port = arg; 458 aggr_grp_t *grp = port->lp_grp; 459 boolean_t mac_addr_changed, link_state_changed; 460 mac_perim_handle_t mph; 461 462 mac_perim_enter_by_mh(grp->lg_mh, &mph); 463 if (port->lp_closing) { 464 mac_perim_exit(mph); 465 466 /* 467 * Release the reference so it is safe for aggr to call 468 * mac_unregister() now. 469 */ 470 aggr_grp_port_rele(port); 471 return; 472 } 473 474 switch (type) { 475 case MAC_NOTE_TX: 476 mac_tx_update(grp->lg_mh); 477 break; 478 case MAC_NOTE_LINK: 479 if (aggr_port_notify_link(grp, port)) 480 mac_link_update(grp->lg_mh, grp->lg_link_state); 481 break; 482 case MAC_NOTE_UNICST: 483 aggr_port_notify_unicst(grp, port, &mac_addr_changed, 484 &link_state_changed); 485 if (mac_addr_changed) 486 mac_unicst_update(grp->lg_mh, grp->lg_addr); 487 if (link_state_changed) 488 mac_link_update(grp->lg_mh, grp->lg_link_state); 489 break; 490 default: 491 break; 492 } 493 494 mac_perim_exit(mph); 495 } 496 497 int 498 aggr_port_start(aggr_port_t *port) 499 { 500 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 501 502 if (port->lp_started) 503 return (0); 504 505 port->lp_started = B_TRUE; 506 aggr_grp_multicst_port(port, B_TRUE); 507 return (0); 508 } 509 510 void 511 aggr_port_stop(aggr_port_t *port) 512 { 513 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 514 515 if (!port->lp_started) 516 return; 517 518 aggr_grp_multicst_port(port, B_FALSE); 519 520 /* update the port state */ 521 port->lp_started = B_FALSE; 522 } 523 524 int 525 aggr_port_promisc(aggr_port_t *port, boolean_t on) 526 { 527 int rc; 528 529 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 530 531 if (on == port->lp_promisc_on) 532 /* already in desired promiscous mode */ 533 return (0); 534 535 if (on) { 536 mac_rx_clear(port->lp_mch); 537 538 /* 539 * We use the promisc callback because without hardware 540 * rings, we deliver through flows that will cause duplicate 541 * delivery of packets when we've flipped into this mode 542 * to compensate for the lack of hardware MAC matching 543 */ 544 rc = mac_promisc_add(port->lp_mch, MAC_CLIENT_PROMISC_ALL, 545 aggr_recv_promisc_cb, port, &port->lp_mphp, 546 MAC_PROMISC_FLAGS_NO_TX_LOOP); 547 if (rc != 0) { 548 mac_rx_set(port->lp_mch, aggr_recv_cb, port); 549 return (rc); 550 } 551 } else { 552 mac_promisc_remove(port->lp_mphp); 553 port->lp_mphp = NULL; 554 mac_rx_set(port->lp_mch, aggr_recv_cb, port); 555 } 556 557 port->lp_promisc_on = on; 558 559 return (0); 560 } 561 562 /* 563 * Set the MAC address of a port. 564 */ 565 int 566 aggr_port_unicst(aggr_port_t *port) 567 { 568 aggr_grp_t *grp = port->lp_grp; 569 570 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 571 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 572 573 return (mac_unicast_primary_set(port->lp_mh, grp->lg_addr)); 574 } 575 576 /* 577 * Add or remove a multicast address to/from a port. 578 */ 579 int 580 aggr_port_multicst(void *arg, boolean_t add, const uint8_t *addrp) 581 { 582 aggr_port_t *port = arg; 583 584 if (add) { 585 return (mac_multicast_add(port->lp_mch, addrp)); 586 } else { 587 mac_multicast_remove(port->lp_mch, addrp); 588 return (0); 589 } 590 } 591 592 uint64_t 593 aggr_port_stat(aggr_port_t *port, uint_t stat) 594 { 595 return (mac_stat_get(port->lp_mh, stat)); 596 } 597 598 /* 599 * Add a non-primary unicast address to the underlying port. If the port 600 * supports HW Rx group, try to add the address into the HW Rx group of 601 * the port first. If that fails, or if the port does not support HW Rx 602 * group, enable the port's promiscous mode. 603 */ 604 int 605 aggr_port_addmac(aggr_port_t *port, const uint8_t *mac_addr) 606 { 607 aggr_unicst_addr_t *addr, **pprev; 608 mac_perim_handle_t pmph; 609 int err; 610 611 ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh)); 612 mac_perim_enter_by_mh(port->lp_mh, &pmph); 613 614 /* 615 * If the underlying port support HW Rx group, add the mac to its 616 * RX group directly. 617 */ 618 if ((port->lp_hwgh != NULL) && 619 ((mac_hwgroup_addmac(port->lp_hwgh, mac_addr)) == 0)) { 620 mac_perim_exit(pmph); 621 return (0); 622 } 623 624 /* 625 * If that fails, or if the port does not support HW Rx group, enable 626 * the port's promiscous mode. (Note that we turn on the promiscous 627 * mode only if the port is already started. 628 */ 629 if (port->lp_started && 630 ((err = aggr_port_promisc(port, B_TRUE)) != 0)) { 631 mac_perim_exit(pmph); 632 return (err); 633 } 634 635 /* 636 * Walk through the unicast addresses that requires promiscous mode 637 * enabled on this port, and add this address to the end of the list. 638 */ 639 pprev = &port->lp_prom_addr; 640 while ((addr = *pprev) != NULL) { 641 ASSERT(bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0); 642 pprev = &addr->aua_next; 643 } 644 addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP); 645 bcopy(mac_addr, addr->aua_addr, ETHERADDRL); 646 addr->aua_next = NULL; 647 *pprev = addr; 648 mac_perim_exit(pmph); 649 return (0); 650 } 651 652 /* 653 * Remove a non-primary unicast address from the underlying port. This address 654 * must has been added by aggr_port_addmac(). As a result, we probably need to 655 * remove the address from the port's HW Rx group, or to disable the port's 656 * promiscous mode. 657 */ 658 void 659 aggr_port_remmac(aggr_port_t *port, const uint8_t *mac_addr) 660 { 661 aggr_grp_t *grp = port->lp_grp; 662 aggr_unicst_addr_t *addr, **pprev; 663 mac_perim_handle_t pmph; 664 665 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 666 mac_perim_enter_by_mh(port->lp_mh, &pmph); 667 668 /* 669 * See whether this address is in the list of addresses that requires 670 * the port being promiscous mode. 671 */ 672 pprev = &port->lp_prom_addr; 673 while ((addr = *pprev) != NULL) { 674 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0) 675 break; 676 pprev = &addr->aua_next; 677 } 678 if (addr != NULL) { 679 /* 680 * This unicast address put the port into the promiscous mode, 681 * delete this address from the lp_prom_addr list. If this is 682 * the last address in that list, disable the promiscous mode 683 * if the aggregation is not in promiscous mode. 684 */ 685 *pprev = addr->aua_next; 686 kmem_free(addr, sizeof (aggr_unicst_addr_t)); 687 if (port->lp_prom_addr == NULL && !grp->lg_promisc) 688 (void) aggr_port_promisc(port, B_FALSE); 689 } else { 690 ASSERT(port->lp_hwgh != NULL); 691 (void) mac_hwgroup_remmac(port->lp_hwgh, mac_addr); 692 } 693 mac_perim_exit(pmph); 694 } 695 696 int 697 aggr_port_addvlan(aggr_port_t *port, uint16_t vid) 698 { 699 mac_perim_handle_t pmph; 700 int err; 701 702 ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh)); 703 mac_perim_enter_by_mh(port->lp_mh, &pmph); 704 705 /* 706 * Add the VLAN filter to the HW group if the port has a HW 707 * group. If the port doesn't have a HW group, then it will 708 * implicitly allow tagged traffic to pass and there is 709 * nothing to do. 710 */ 711 if (port->lp_hwgh == NULL) 712 return (0); 713 714 err = mac_hwgroup_addvlan(port->lp_hwgh, vid); 715 mac_perim_exit(pmph); 716 return (err); 717 } 718 719 int 720 aggr_port_remvlan(aggr_port_t *port, uint16_t vid) 721 { 722 mac_perim_handle_t pmph; 723 int err; 724 725 ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh)); 726 mac_perim_enter_by_mh(port->lp_mh, &pmph); 727 728 if (port->lp_hwgh == NULL) 729 return (0); 730 731 err = mac_hwgroup_remvlan(port->lp_hwgh, vid); 732 mac_perim_exit(pmph); 733 return (err); 734 }