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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * IEEE 802.3ad Link Aggregation -- Link Aggregation Groups. 28 * 29 * An instance of the structure aggr_grp_t is allocated for each 30 * link aggregation group. When created, aggr_grp_t objects are 31 * entered into the aggr_grp_hash hash table maintained by the modhash 32 * module. The hash key is the linkid associated with the link 33 * aggregation group. 34 * 35 * A set of MAC ports are associated with each association group. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/sysmacros.h> 40 #include <sys/conf.h> 41 #include <sys/cmn_err.h> 42 #include <sys/disp.h> 43 #include <sys/list.h> 44 #include <sys/ksynch.h> 45 #include <sys/kmem.h> 46 #include <sys/stream.h> 47 #include <sys/modctl.h> 48 #include <sys/ddi.h> 49 #include <sys/sunddi.h> 50 #include <sys/atomic.h> 51 #include <sys/stat.h> 52 #include <sys/modhash.h> 53 #include <sys/id_space.h> 54 #include <sys/strsun.h> 55 #include <sys/dlpi.h> 56 #include <sys/mac_provider.h> 57 #include <sys/dls.h> 58 #include <sys/vlan.h> 59 #include <sys/aggr.h> 60 #include <sys/aggr_impl.h> 61 62 static int aggr_m_start(void *); 63 static void aggr_m_stop(void *); 64 static int aggr_m_promisc(void *, boolean_t); 65 static int aggr_m_multicst(void *, boolean_t, const uint8_t *); 66 static int aggr_m_unicst(void *, const uint8_t *); 67 static int aggr_m_stat(void *, uint_t, uint64_t *); 68 static void aggr_m_ioctl(void *, queue_t *, mblk_t *); 69 static boolean_t aggr_m_capab_get(void *, mac_capab_t, void *); 70 static aggr_port_t *aggr_grp_port_lookup(aggr_grp_t *, datalink_id_t); 71 static int aggr_grp_rem_port(aggr_grp_t *, aggr_port_t *, boolean_t *, 72 boolean_t *); 73 74 static void aggr_grp_capab_set(aggr_grp_t *); 75 static boolean_t aggr_grp_capab_check(aggr_grp_t *, aggr_port_t *); 76 static uint_t aggr_grp_max_sdu(aggr_grp_t *); 77 static uint32_t aggr_grp_max_margin(aggr_grp_t *); 78 static boolean_t aggr_grp_sdu_check(aggr_grp_t *, aggr_port_t *); 79 static boolean_t aggr_grp_margin_check(aggr_grp_t *, aggr_port_t *); 80 81 static int aggr_add_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *); 82 static void aggr_rem_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *); 83 static int aggr_pseudo_disable_intr(mac_intr_handle_t); 84 static int aggr_pseudo_enable_intr(mac_intr_handle_t); 85 static int aggr_pseudo_start_ring(mac_ring_driver_t, uint64_t); 86 static void aggr_pseudo_stop_ring(mac_ring_driver_t); 87 static int aggr_addmac(void *, const uint8_t *); 88 static int aggr_remmac(void *, const uint8_t *); 89 static mblk_t *aggr_rx_poll(void *, int); 90 static void aggr_fill_ring(void *, mac_ring_type_t, const int, 91 const int, mac_ring_info_t *, mac_ring_handle_t); 92 static void aggr_fill_group(void *, mac_ring_type_t, const int, 93 mac_group_info_t *, mac_group_handle_t); 94 95 static kmem_cache_t *aggr_grp_cache; 96 static mod_hash_t *aggr_grp_hash; 97 static krwlock_t aggr_grp_lock; 98 static uint_t aggr_grp_cnt; 99 static id_space_t *key_ids; 100 101 #define GRP_HASHSZ 64 102 #define GRP_HASH_KEY(linkid) ((mod_hash_key_t)(uintptr_t)linkid) 103 #define AGGR_PORT_NAME_DELIMIT '-' 104 105 static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0}; 106 107 #define AGGR_M_CALLBACK_FLAGS (MC_IOCTL | MC_GETCAPAB) 108 109 static mac_callbacks_t aggr_m_callbacks = { 110 AGGR_M_CALLBACK_FLAGS, 111 aggr_m_stat, 112 aggr_m_start, 113 aggr_m_stop, 114 aggr_m_promisc, 115 aggr_m_multicst, 116 NULL, 117 aggr_m_tx, 118 aggr_m_ioctl, 119 aggr_m_capab_get 120 }; 121 122 /*ARGSUSED*/ 123 static int 124 aggr_grp_constructor(void *buf, void *arg, int kmflag) 125 { 126 aggr_grp_t *grp = buf; 127 128 bzero(grp, sizeof (*grp)); 129 mutex_init(&grp->lg_lacp_lock, NULL, MUTEX_DEFAULT, NULL); 130 cv_init(&grp->lg_lacp_cv, NULL, CV_DEFAULT, NULL); 131 rw_init(&grp->lg_tx_lock, NULL, RW_DRIVER, NULL); 132 mutex_init(&grp->lg_port_lock, NULL, MUTEX_DEFAULT, NULL); 133 cv_init(&grp->lg_port_cv, NULL, CV_DEFAULT, NULL); 134 grp->lg_link_state = LINK_STATE_UNKNOWN; 135 return (0); 136 } 137 138 /*ARGSUSED*/ 139 static void 140 aggr_grp_destructor(void *buf, void *arg) 141 { 142 aggr_grp_t *grp = buf; 143 144 if (grp->lg_tx_ports != NULL) { 145 kmem_free(grp->lg_tx_ports, 146 grp->lg_tx_ports_size * sizeof (aggr_port_t *)); 147 } 148 149 mutex_destroy(&grp->lg_lacp_lock); 150 cv_destroy(&grp->lg_lacp_cv); 151 mutex_destroy(&grp->lg_port_lock); 152 cv_destroy(&grp->lg_port_cv); 153 rw_destroy(&grp->lg_tx_lock); 154 } 155 156 void 157 aggr_grp_init(void) 158 { 159 aggr_grp_cache = kmem_cache_create("aggr_grp_cache", 160 sizeof (aggr_grp_t), 0, aggr_grp_constructor, 161 aggr_grp_destructor, NULL, NULL, NULL, 0); 162 163 aggr_grp_hash = mod_hash_create_idhash("aggr_grp_hash", 164 GRP_HASHSZ, mod_hash_null_valdtor); 165 rw_init(&aggr_grp_lock, NULL, RW_DEFAULT, NULL); 166 aggr_grp_cnt = 0; 167 168 /* 169 * Allocate an id space to manage key values (when key is not 170 * specified). The range of the id space will be from 171 * (AGGR_MAX_KEY + 1) to UINT16_MAX, because the LACP protocol 172 * uses a 16-bit key. 173 */ 174 key_ids = id_space_create("aggr_key_ids", AGGR_MAX_KEY + 1, UINT16_MAX); 175 ASSERT(key_ids != NULL); 176 } 177 178 void 179 aggr_grp_fini(void) 180 { 181 id_space_destroy(key_ids); 182 rw_destroy(&aggr_grp_lock); 183 mod_hash_destroy_idhash(aggr_grp_hash); 184 kmem_cache_destroy(aggr_grp_cache); 185 } 186 187 uint_t 188 aggr_grp_count(void) 189 { 190 uint_t count; 191 192 rw_enter(&aggr_grp_lock, RW_READER); 193 count = aggr_grp_cnt; 194 rw_exit(&aggr_grp_lock); 195 return (count); 196 } 197 198 /* 199 * Since both aggr_port_notify_cb() and aggr_port_timer_thread() functions 200 * requires the mac perimeter, this function holds a reference of the aggr 201 * and aggr won't call mac_unregister() until this reference drops to 0. 202 */ 203 void 204 aggr_grp_port_hold(aggr_port_t *port) 205 { 206 aggr_grp_t *grp = port->lp_grp; 207 208 AGGR_PORT_REFHOLD(port); 209 mutex_enter(&grp->lg_port_lock); 210 grp->lg_port_ref++; 211 mutex_exit(&grp->lg_port_lock); 212 } 213 214 /* 215 * Release the reference of the grp and inform aggr_grp_delete() calling 216 * mac_unregister() is now safe. 217 */ 218 void 219 aggr_grp_port_rele(aggr_port_t *port) 220 { 221 aggr_grp_t *grp = port->lp_grp; 222 223 mutex_enter(&grp->lg_port_lock); 224 if (--grp->lg_port_ref == 0) 225 cv_signal(&grp->lg_port_cv); 226 mutex_exit(&grp->lg_port_lock); 227 AGGR_PORT_REFRELE(port); 228 } 229 230 /* 231 * Wait for the port's lacp timer thread and the port's notification callback 232 * to exit. 233 */ 234 void 235 aggr_grp_port_wait(aggr_grp_t *grp) 236 { 237 mutex_enter(&grp->lg_port_lock); 238 if (grp->lg_port_ref != 0) 239 cv_wait(&grp->lg_port_cv, &grp->lg_port_lock); 240 mutex_exit(&grp->lg_port_lock); 241 } 242 243 /* 244 * Attach a port to a link aggregation group. 245 * 246 * A port is attached to a link aggregation group once its speed 247 * and link state have been verified. 248 * 249 * Returns B_TRUE if the group link state or speed has changed. If 250 * it's the case, the caller must notify the MAC layer via a call 251 * to mac_link(). 252 */ 253 boolean_t 254 aggr_grp_attach_port(aggr_grp_t *grp, aggr_port_t *port) 255 { 256 boolean_t link_state_changed = B_FALSE; 257 258 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 259 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 260 261 if (port->lp_state == AGGR_PORT_STATE_ATTACHED) 262 return (B_FALSE); 263 264 /* 265 * Validate the MAC port link speed and update the group 266 * link speed if needed. 267 */ 268 if (port->lp_ifspeed == 0 || 269 port->lp_link_state != LINK_STATE_UP || 270 port->lp_link_duplex != LINK_DUPLEX_FULL) { 271 /* 272 * Can't attach a MAC port with unknown link speed, 273 * down link, or not in full duplex mode. 274 */ 275 return (B_FALSE); 276 } 277 278 if (grp->lg_ifspeed == 0) { 279 /* 280 * The group inherits the speed of the first link being 281 * attached. 282 */ 283 grp->lg_ifspeed = port->lp_ifspeed; 284 link_state_changed = B_TRUE; 285 } else if (grp->lg_ifspeed != port->lp_ifspeed) { 286 /* 287 * The link speed of the MAC port must be the same as 288 * the group link speed, as per 802.3ad. Since it is 289 * not, the attach is cancelled. 290 */ 291 return (B_FALSE); 292 } 293 294 grp->lg_nattached_ports++; 295 296 /* 297 * Update the group link state. 298 */ 299 if (grp->lg_link_state != LINK_STATE_UP) { 300 grp->lg_link_state = LINK_STATE_UP; 301 grp->lg_link_duplex = LINK_DUPLEX_FULL; 302 link_state_changed = B_TRUE; 303 } 304 305 aggr_grp_multicst_port(port, B_TRUE); 306 307 /* 308 * Update port's state. 309 */ 310 port->lp_state = AGGR_PORT_STATE_ATTACHED; 311 312 /* 313 * Set port's receive callback 314 */ 315 mac_rx_set(port->lp_mch, aggr_recv_cb, port); 316 317 /* 318 * If LACP is OFF, the port can be used to send data as soon 319 * as its link is up and verified to be compatible with the 320 * aggregation. 321 * 322 * If LACP is active or passive, notify the LACP subsystem, which 323 * will enable sending on the port following the LACP protocol. 324 */ 325 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 326 aggr_send_port_enable(port); 327 else 328 aggr_lacp_port_attached(port); 329 330 return (link_state_changed); 331 } 332 333 boolean_t 334 aggr_grp_detach_port(aggr_grp_t *grp, aggr_port_t *port) 335 { 336 boolean_t link_state_changed = B_FALSE; 337 338 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 339 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 340 341 /* update state */ 342 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 343 return (B_FALSE); 344 345 mac_rx_clear(port->lp_mch); 346 347 aggr_grp_multicst_port(port, B_FALSE); 348 349 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 350 aggr_send_port_disable(port); 351 else 352 aggr_lacp_port_detached(port); 353 354 port->lp_state = AGGR_PORT_STATE_STANDBY; 355 356 grp->lg_nattached_ports--; 357 if (grp->lg_nattached_ports == 0) { 358 /* the last attached MAC port of the group is being detached */ 359 grp->lg_ifspeed = 0; 360 grp->lg_link_state = LINK_STATE_DOWN; 361 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 362 link_state_changed = B_TRUE; 363 } 364 365 return (link_state_changed); 366 } 367 368 /* 369 * Update the MAC addresses of the constituent ports of the specified 370 * group. This function is invoked: 371 * - after creating a new aggregation group. 372 * - after adding new ports to an aggregation group. 373 * - after removing a port from a group when the MAC address of 374 * that port was used for the MAC address of the group. 375 * - after the MAC address of a port changed when the MAC address 376 * of that port was used for the MAC address of the group. 377 * 378 * Return true if the link state of the aggregation changed, for example 379 * as a result of a failure changing the MAC address of one of the 380 * constituent ports. 381 */ 382 boolean_t 383 aggr_grp_update_ports_mac(aggr_grp_t *grp) 384 { 385 aggr_port_t *cport; 386 boolean_t link_state_changed = B_FALSE; 387 mac_perim_handle_t mph; 388 389 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 390 391 for (cport = grp->lg_ports; cport != NULL; 392 cport = cport->lp_next) { 393 mac_perim_enter_by_mh(cport->lp_mh, &mph); 394 if (aggr_port_unicst(cport) != 0) { 395 if (aggr_grp_detach_port(grp, cport)) 396 link_state_changed = B_TRUE; 397 } else { 398 /* 399 * If a port was detached because of a previous 400 * failure changing the MAC address, the port is 401 * reattached when it successfully changes the MAC 402 * address now, and this might cause the link state 403 * of the aggregation to change. 404 */ 405 if (aggr_grp_attach_port(grp, cport)) 406 link_state_changed = B_TRUE; 407 } 408 mac_perim_exit(mph); 409 } 410 return (link_state_changed); 411 } 412 413 /* 414 * Invoked when the MAC address of a port has changed. If the port's 415 * MAC address was used for the group MAC address, set mac_addr_changedp 416 * to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST 417 * notification. If the link state changes due to detach/attach of 418 * the constituent port, set link_state_changedp to B_TRUE to indicate 419 * to the caller that it should send a MAC_NOTE_LINK notification. In both 420 * cases, it is the responsibility of the caller to invoke notification 421 * functions after releasing the the port lock. 422 */ 423 void 424 aggr_grp_port_mac_changed(aggr_grp_t *grp, aggr_port_t *port, 425 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 426 { 427 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 428 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 429 ASSERT(mac_addr_changedp != NULL); 430 ASSERT(link_state_changedp != NULL); 431 432 *mac_addr_changedp = B_FALSE; 433 *link_state_changedp = B_FALSE; 434 435 if (grp->lg_addr_fixed) { 436 /* 437 * The group is using a fixed MAC address or an automatic 438 * MAC address has not been set. 439 */ 440 return; 441 } 442 443 if (grp->lg_mac_addr_port == port) { 444 /* 445 * The MAC address of the port was assigned to the group 446 * MAC address. Update the group MAC address. 447 */ 448 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 449 *mac_addr_changedp = B_TRUE; 450 } else { 451 /* 452 * Update the actual port MAC address to the MAC address 453 * of the group. 454 */ 455 if (aggr_port_unicst(port) != 0) { 456 *link_state_changedp = aggr_grp_detach_port(grp, port); 457 } else { 458 /* 459 * If a port was detached because of a previous 460 * failure changing the MAC address, the port is 461 * reattached when it successfully changes the MAC 462 * address now, and this might cause the link state 463 * of the aggregation to change. 464 */ 465 *link_state_changedp = aggr_grp_attach_port(grp, port); 466 } 467 } 468 } 469 470 /* 471 * Add a port to a link aggregation group. 472 */ 473 static int 474 aggr_grp_add_port(aggr_grp_t *grp, datalink_id_t port_linkid, boolean_t force, 475 aggr_port_t **pp) 476 { 477 aggr_port_t *port, **cport; 478 mac_perim_handle_t mph; 479 int err; 480 481 /* 482 * lg_mh could be NULL when the function is called during the creation 483 * of the aggregation. 484 */ 485 ASSERT(grp->lg_mh == NULL || MAC_PERIM_HELD(grp->lg_mh)); 486 487 /* create new port */ 488 err = aggr_port_create(grp, port_linkid, force, &port); 489 if (err != 0) 490 return (err); 491 492 mac_perim_enter_by_mh(port->lp_mh, &mph); 493 494 /* add port to list of group constituent ports */ 495 cport = &grp->lg_ports; 496 while (*cport != NULL) 497 cport = &((*cport)->lp_next); 498 *cport = port; 499 500 /* 501 * Back reference to the group it is member of. A port always 502 * holds a reference to its group to ensure that the back 503 * reference is always valid. 504 */ 505 port->lp_grp = grp; 506 AGGR_GRP_REFHOLD(grp); 507 grp->lg_nports++; 508 509 aggr_lacp_init_port(port); 510 mac_perim_exit(mph); 511 512 if (pp != NULL) 513 *pp = port; 514 515 return (0); 516 } 517 518 /* 519 * Add a pseudo Rx ring for the given HW ring handle. 520 */ 521 static int 522 aggr_add_pseudo_rx_ring(aggr_port_t *port, 523 aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh) 524 { 525 aggr_pseudo_rx_ring_t *ring; 526 int err; 527 int j; 528 529 for (j = 0; j < MAX_RINGS_PER_GROUP; j++) { 530 ring = rx_grp->arg_rings + j; 531 if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE)) 532 break; 533 } 534 535 /* 536 * No slot for this new Rx ring. 537 */ 538 if (j == MAX_RINGS_PER_GROUP) 539 return (EIO); 540 541 ring->arr_flags |= MAC_PSEUDO_RING_INUSE; 542 ring->arr_hw_rh = hw_rh; 543 ring->arr_port = port; 544 rx_grp->arg_ring_cnt++; 545 546 /* 547 * The group is already registered, dynamically add a new ring to the 548 * mac group. 549 */ 550 mac_hwring_setup(hw_rh, (mac_resource_handle_t)ring); 551 if ((err = mac_group_add_ring(rx_grp->arg_gh, j)) != 0) { 552 ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE; 553 ring->arr_hw_rh = NULL; 554 ring->arr_port = NULL; 555 rx_grp->arg_ring_cnt--; 556 mac_hwring_teardown(hw_rh); 557 } 558 return (err); 559 } 560 561 /* 562 * Remove the pseudo Rx ring of the given HW ring handle. 563 */ 564 static void 565 aggr_rem_pseudo_rx_ring(aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh) 566 { 567 aggr_pseudo_rx_ring_t *ring; 568 int j; 569 570 for (j = 0; j < MAX_RINGS_PER_GROUP; j++) { 571 ring = rx_grp->arg_rings + j; 572 if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE) || 573 ring->arr_hw_rh != hw_rh) { 574 continue; 575 } 576 577 mac_group_rem_ring(rx_grp->arg_gh, ring->arr_rh); 578 579 ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE; 580 ring->arr_hw_rh = NULL; 581 ring->arr_port = NULL; 582 rx_grp->arg_ring_cnt--; 583 mac_hwring_teardown(hw_rh); 584 break; 585 } 586 } 587 588 /* 589 * This function is called to create pseudo rings over the hardware rings of 590 * the underlying device. Note that there is a 1:1 mapping between the pseudo 591 * RX rings of the aggr and the hardware rings of the underlying port. 592 */ 593 static int 594 aggr_add_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp) 595 { 596 aggr_grp_t *grp = port->lp_grp; 597 mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP]; 598 aggr_unicst_addr_t *addr, *a; 599 mac_perim_handle_t pmph; 600 int hw_rh_cnt, i = 0, j; 601 int err = 0; 602 603 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 604 mac_perim_enter_by_mh(port->lp_mh, &pmph); 605 606 /* 607 * This function must be called after the aggr registers its mac 608 * and its RX group has been initialized. 609 */ 610 ASSERT(rx_grp->arg_gh != NULL); 611 612 /* 613 * Get the list the the underlying HW rings. 614 */ 615 hw_rh_cnt = mac_hwrings_get(port->lp_mch, &port->lp_hwgh, hw_rh); 616 617 if (port->lp_hwgh != NULL) { 618 /* 619 * Quiesce the HW ring and the mac srs on the ring. Note 620 * that the HW ring will be restarted when the pseudo ring 621 * is started. At that time all the packets will be 622 * directly passed up to the pseudo RX ring and handled 623 * by mac srs created over the pseudo RX ring. 624 */ 625 mac_rx_client_quiesce(port->lp_mch); 626 mac_srs_perm_quiesce(port->lp_mch, B_TRUE); 627 } 628 629 /* 630 * Add all the unicast addresses to the newly added port. 631 */ 632 for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) { 633 if ((err = aggr_port_addmac(port, addr->aua_addr)) != 0) 634 break; 635 } 636 637 for (i = 0; err == 0 && i < hw_rh_cnt; i++) 638 err = aggr_add_pseudo_rx_ring(port, rx_grp, hw_rh[i]); 639 640 if (err != 0) { 641 for (j = 0; j < i; j++) 642 aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[j]); 643 644 for (a = rx_grp->arg_macaddr; a != addr; a = a->aua_next) 645 aggr_port_remmac(port, a->aua_addr); 646 647 if (port->lp_hwgh != NULL) { 648 mac_srs_perm_quiesce(port->lp_mch, B_FALSE); 649 mac_rx_client_restart(port->lp_mch); 650 port->lp_hwgh = NULL; 651 } 652 } else { 653 port->lp_grp_added = B_TRUE; 654 } 655 done: 656 mac_perim_exit(pmph); 657 return (err); 658 } 659 660 /* 661 * This function is called by aggr to remove pseudo RX rings over the 662 * HW rings of the underlying port. 663 */ 664 static void 665 aggr_rem_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp) 666 { 667 aggr_grp_t *grp = port->lp_grp; 668 mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP]; 669 aggr_unicst_addr_t *addr; 670 mac_group_handle_t hwgh; 671 mac_perim_handle_t pmph; 672 int hw_rh_cnt, i; 673 674 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 675 mac_perim_enter_by_mh(port->lp_mh, &pmph); 676 677 if (!port->lp_grp_added) 678 goto done; 679 680 ASSERT(rx_grp->arg_gh != NULL); 681 hw_rh_cnt = mac_hwrings_get(port->lp_mch, &hwgh, hw_rh); 682 683 /* 684 * If hw_rh_cnt is 0, it means that the underlying port does not 685 * support RX rings. Directly return in this case. 686 */ 687 for (i = 0; i < hw_rh_cnt; i++) 688 aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[i]); 689 690 for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) 691 aggr_port_remmac(port, addr->aua_addr); 692 693 if (port->lp_hwgh != NULL) { 694 port->lp_hwgh = NULL; 695 696 /* 697 * First clear the permanent-quiesced flag of the RX srs then 698 * restart the HW ring and the mac srs on the ring. Note that 699 * the HW ring and associated SRS will soon been removed when 700 * the port is removed from the aggr. 701 */ 702 mac_srs_perm_quiesce(port->lp_mch, B_FALSE); 703 mac_rx_client_restart(port->lp_mch); 704 } 705 706 port->lp_grp_added = B_FALSE; 707 done: 708 mac_perim_exit(pmph); 709 } 710 711 static int 712 aggr_pseudo_disable_intr(mac_intr_handle_t ih) 713 { 714 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih; 715 return (mac_hwring_disable_intr(rr_ring->arr_hw_rh)); 716 } 717 718 static int 719 aggr_pseudo_enable_intr(mac_intr_handle_t ih) 720 { 721 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih; 722 return (mac_hwring_enable_intr(rr_ring->arr_hw_rh)); 723 } 724 725 static int 726 aggr_pseudo_start_ring(mac_ring_driver_t arg, uint64_t mr_gen) 727 { 728 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg; 729 int err; 730 731 err = mac_hwring_start(rr_ring->arr_hw_rh); 732 if (err == 0) 733 rr_ring->arr_gen = mr_gen; 734 return (err); 735 } 736 737 static void 738 aggr_pseudo_stop_ring(mac_ring_driver_t arg) 739 { 740 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg; 741 mac_hwring_stop(rr_ring->arr_hw_rh); 742 } 743 744 /* 745 * Add one or more ports to an existing link aggregation group. 746 */ 747 int 748 aggr_grp_add_ports(datalink_id_t linkid, uint_t nports, boolean_t force, 749 laioc_port_t *ports) 750 { 751 int rc, i, nadded = 0; 752 aggr_grp_t *grp = NULL; 753 aggr_port_t *port; 754 boolean_t link_state_changed = B_FALSE; 755 mac_perim_handle_t mph, pmph; 756 757 /* get group corresponding to linkid */ 758 rw_enter(&aggr_grp_lock, RW_READER); 759 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 760 (mod_hash_val_t *)&grp) != 0) { 761 rw_exit(&aggr_grp_lock); 762 return (ENOENT); 763 } 764 AGGR_GRP_REFHOLD(grp); 765 766 /* 767 * Hold the perimeter so that the aggregation won't be destroyed. 768 */ 769 mac_perim_enter_by_mh(grp->lg_mh, &mph); 770 rw_exit(&aggr_grp_lock); 771 772 /* add the specified ports to group */ 773 for (i = 0; i < nports; i++) { 774 /* add port to group */ 775 if ((rc = aggr_grp_add_port(grp, ports[i].lp_linkid, 776 force, &port)) != 0) { 777 goto bail; 778 } 779 ASSERT(port != NULL); 780 nadded++; 781 782 /* check capabilities */ 783 if (!aggr_grp_capab_check(grp, port) || 784 !aggr_grp_sdu_check(grp, port) || 785 !aggr_grp_margin_check(grp, port)) { 786 rc = ENOTSUP; 787 goto bail; 788 } 789 790 /* 791 * Create the pseudo ring for each HW ring of the underlying 792 * port. 793 */ 794 rc = aggr_add_pseudo_rx_group(port, &grp->lg_rx_group); 795 if (rc != 0) 796 goto bail; 797 798 mac_perim_enter_by_mh(port->lp_mh, &pmph); 799 800 /* set LACP mode */ 801 aggr_port_lacp_set_mode(grp, port); 802 803 /* start port if group has already been started */ 804 if (grp->lg_started) { 805 rc = aggr_port_start(port); 806 if (rc != 0) { 807 mac_perim_exit(pmph); 808 goto bail; 809 } 810 811 /* 812 * Turn on the promiscuous mode over the port when it 813 * is requested to be turned on to receive the 814 * non-primary address over a port, or the promiscous 815 * mode is enabled over the aggr. 816 */ 817 if (grp->lg_promisc || port->lp_prom_addr != NULL) { 818 rc = aggr_port_promisc(port, B_TRUE); 819 if (rc != 0) { 820 mac_perim_exit(pmph); 821 goto bail; 822 } 823 } 824 } 825 mac_perim_exit(pmph); 826 827 /* 828 * Attach each port if necessary. 829 */ 830 if (aggr_port_notify_link(grp, port)) 831 link_state_changed = B_TRUE; 832 833 /* 834 * Initialize the callback functions for this port. 835 */ 836 aggr_port_init_callbacks(port); 837 } 838 839 /* update the MAC address of the constituent ports */ 840 if (aggr_grp_update_ports_mac(grp)) 841 link_state_changed = B_TRUE; 842 843 if (link_state_changed) 844 mac_link_update(grp->lg_mh, grp->lg_link_state); 845 846 bail: 847 if (rc != 0) { 848 /* stop and remove ports that have been added */ 849 for (i = 0; i < nadded; i++) { 850 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 851 ASSERT(port != NULL); 852 if (grp->lg_started) { 853 mac_perim_enter_by_mh(port->lp_mh, &pmph); 854 (void) aggr_port_promisc(port, B_FALSE); 855 aggr_port_stop(port); 856 mac_perim_exit(pmph); 857 } 858 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 859 (void) aggr_grp_rem_port(grp, port, NULL, NULL); 860 } 861 } 862 863 if (rc == 0) 864 mac_resource_update(grp->lg_mh); 865 mac_perim_exit(mph); 866 AGGR_GRP_REFRELE(grp); 867 return (rc); 868 } 869 870 static int 871 aggr_grp_modify_common(aggr_grp_t *grp, uint8_t update_mask, uint32_t policy, 872 boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 873 aggr_lacp_timer_t lacp_timer) 874 { 875 boolean_t mac_addr_changed = B_FALSE; 876 boolean_t link_state_changed = B_FALSE; 877 mac_perim_handle_t pmph; 878 879 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 880 881 /* validate fixed address if specified */ 882 if ((update_mask & AGGR_MODIFY_MAC) && mac_fixed && 883 ((bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) || 884 (mac_addr[0] & 0x01))) { 885 return (EINVAL); 886 } 887 888 /* update policy if requested */ 889 if (update_mask & AGGR_MODIFY_POLICY) 890 aggr_send_update_policy(grp, policy); 891 892 /* update unicast MAC address if requested */ 893 if (update_mask & AGGR_MODIFY_MAC) { 894 if (mac_fixed) { 895 /* user-supplied MAC address */ 896 grp->lg_mac_addr_port = NULL; 897 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) != 0) { 898 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 899 mac_addr_changed = B_TRUE; 900 } 901 } else if (grp->lg_addr_fixed) { 902 /* switch from user-supplied to automatic */ 903 aggr_port_t *port = grp->lg_ports; 904 905 mac_perim_enter_by_mh(port->lp_mh, &pmph); 906 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 907 grp->lg_mac_addr_port = port; 908 mac_addr_changed = B_TRUE; 909 mac_perim_exit(pmph); 910 } 911 grp->lg_addr_fixed = mac_fixed; 912 } 913 914 if (mac_addr_changed) 915 link_state_changed = aggr_grp_update_ports_mac(grp); 916 917 if (update_mask & AGGR_MODIFY_LACP_MODE) 918 aggr_lacp_update_mode(grp, lacp_mode); 919 920 if (update_mask & AGGR_MODIFY_LACP_TIMER) 921 aggr_lacp_update_timer(grp, lacp_timer); 922 923 if (link_state_changed) 924 mac_link_update(grp->lg_mh, grp->lg_link_state); 925 926 if (mac_addr_changed) 927 mac_unicst_update(grp->lg_mh, grp->lg_addr); 928 929 return (0); 930 } 931 932 /* 933 * Update properties of an existing link aggregation group. 934 */ 935 int 936 aggr_grp_modify(datalink_id_t linkid, uint8_t update_mask, uint32_t policy, 937 boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 938 aggr_lacp_timer_t lacp_timer) 939 { 940 aggr_grp_t *grp = NULL; 941 mac_perim_handle_t mph; 942 int err; 943 944 /* get group corresponding to linkid */ 945 rw_enter(&aggr_grp_lock, RW_READER); 946 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 947 (mod_hash_val_t *)&grp) != 0) { 948 rw_exit(&aggr_grp_lock); 949 return (ENOENT); 950 } 951 AGGR_GRP_REFHOLD(grp); 952 953 /* 954 * Hold the perimeter so that the aggregation won't be destroyed. 955 */ 956 mac_perim_enter_by_mh(grp->lg_mh, &mph); 957 rw_exit(&aggr_grp_lock); 958 959 err = aggr_grp_modify_common(grp, update_mask, policy, mac_fixed, 960 mac_addr, lacp_mode, lacp_timer); 961 962 mac_perim_exit(mph); 963 AGGR_GRP_REFRELE(grp); 964 return (err); 965 } 966 967 /* 968 * Create a new link aggregation group upon request from administrator. 969 * Returns 0 on success, an errno on failure. 970 */ 971 int 972 aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, 973 laioc_port_t *ports, uint32_t policy, boolean_t mac_fixed, boolean_t force, 974 uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 975 { 976 aggr_grp_t *grp = NULL; 977 aggr_port_t *port; 978 mac_register_t *mac; 979 boolean_t link_state_changed; 980 mac_perim_handle_t mph; 981 int err; 982 int i; 983 984 /* need at least one port */ 985 if (nports == 0) 986 return (EINVAL); 987 988 rw_enter(&aggr_grp_lock, RW_WRITER); 989 990 /* does a group with the same linkid already exist? */ 991 err = mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 992 (mod_hash_val_t *)&grp); 993 if (err == 0) { 994 rw_exit(&aggr_grp_lock); 995 return (EEXIST); 996 } 997 998 grp = kmem_cache_alloc(aggr_grp_cache, KM_SLEEP); 999 1000 grp->lg_refs = 1; 1001 grp->lg_closing = B_FALSE; 1002 grp->lg_force = force; 1003 grp->lg_linkid = linkid; 1004 grp->lg_ifspeed = 0; 1005 grp->lg_link_state = LINK_STATE_UNKNOWN; 1006 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 1007 grp->lg_started = B_FALSE; 1008 grp->lg_promisc = B_FALSE; 1009 grp->lg_lacp_done = B_FALSE; 1010 grp->lg_lacp_head = grp->lg_lacp_tail = NULL; 1011 grp->lg_lacp_rx_thread = thread_create(NULL, 0, 1012 aggr_lacp_rx_thread, grp, 0, &p0, TS_RUN, minclsyspri); 1013 bzero(&grp->lg_rx_group, sizeof (aggr_pseudo_rx_group_t)); 1014 aggr_lacp_init_grp(grp); 1015 1016 /* add MAC ports to group */ 1017 grp->lg_ports = NULL; 1018 grp->lg_nports = 0; 1019 grp->lg_nattached_ports = 0; 1020 grp->lg_ntx_ports = 0; 1021 1022 /* 1023 * If key is not specified by the user, allocate the key. 1024 */ 1025 if ((key == 0) && ((key = (uint32_t)id_alloc(key_ids)) == 0)) { 1026 err = ENOMEM; 1027 goto bail; 1028 } 1029 grp->lg_key = key; 1030 1031 for (i = 0; i < nports; i++) { 1032 err = aggr_grp_add_port(grp, ports[i].lp_linkid, force, NULL); 1033 if (err != 0) 1034 goto bail; 1035 } 1036 1037 /* 1038 * If no explicit MAC address was specified by the administrator, 1039 * set it to the MAC address of the first port. 1040 */ 1041 grp->lg_addr_fixed = mac_fixed; 1042 if (grp->lg_addr_fixed) { 1043 /* validate specified address */ 1044 if (bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) { 1045 err = EINVAL; 1046 goto bail; 1047 } 1048 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 1049 } else { 1050 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 1051 grp->lg_mac_addr_port = grp->lg_ports; 1052 } 1053 1054 /* set the initial group capabilities */ 1055 aggr_grp_capab_set(grp); 1056 1057 if ((mac = mac_alloc(MAC_VERSION)) == NULL) { 1058 err = ENOMEM; 1059 goto bail; 1060 } 1061 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 1062 mac->m_driver = grp; 1063 mac->m_dip = aggr_dip; 1064 mac->m_instance = grp->lg_key > AGGR_MAX_KEY ? (uint_t)-1 : grp->lg_key; 1065 mac->m_src_addr = grp->lg_addr; 1066 mac->m_callbacks = &aggr_m_callbacks; 1067 mac->m_min_sdu = 0; 1068 mac->m_max_sdu = grp->lg_max_sdu = aggr_grp_max_sdu(grp); 1069 mac->m_margin = aggr_grp_max_margin(grp); 1070 mac->m_v12n = MAC_VIRT_LEVEL1; 1071 err = mac_register(mac, &grp->lg_mh); 1072 mac_free(mac); 1073 if (err != 0) 1074 goto bail; 1075 1076 if ((err = dls_devnet_create(grp->lg_mh, grp->lg_linkid)) != 0) { 1077 (void) mac_unregister(grp->lg_mh); 1078 grp->lg_mh = NULL; 1079 goto bail; 1080 } 1081 1082 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1083 1084 /* 1085 * Update the MAC address of the constituent ports. 1086 * None of the port is attached at this time, the link state of the 1087 * aggregation will not change. 1088 */ 1089 link_state_changed = aggr_grp_update_ports_mac(grp); 1090 ASSERT(!link_state_changed); 1091 1092 /* update outbound load balancing policy */ 1093 aggr_send_update_policy(grp, policy); 1094 1095 /* set LACP mode */ 1096 aggr_lacp_set_mode(grp, lacp_mode, lacp_timer); 1097 1098 /* 1099 * Attach each port if necessary. 1100 */ 1101 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1102 /* 1103 * Create the pseudo ring for each HW ring of the underlying 1104 * port. Note that this is done after the aggr registers the 1105 * mac. 1106 */ 1107 VERIFY(aggr_add_pseudo_rx_group(port, &grp->lg_rx_group) == 0); 1108 if (aggr_port_notify_link(grp, port)) 1109 link_state_changed = B_TRUE; 1110 1111 /* 1112 * Initialize the callback functions for this port. 1113 */ 1114 aggr_port_init_callbacks(port); 1115 } 1116 1117 if (link_state_changed) 1118 mac_link_update(grp->lg_mh, grp->lg_link_state); 1119 1120 /* add new group to hash table */ 1121 err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(linkid), 1122 (mod_hash_val_t)grp); 1123 ASSERT(err == 0); 1124 aggr_grp_cnt++; 1125 1126 mac_perim_exit(mph); 1127 rw_exit(&aggr_grp_lock); 1128 return (0); 1129 1130 bail: 1131 1132 grp->lg_closing = B_TRUE; 1133 1134 port = grp->lg_ports; 1135 while (port != NULL) { 1136 aggr_port_t *cport; 1137 1138 cport = port->lp_next; 1139 aggr_port_delete(port); 1140 port = cport; 1141 } 1142 1143 /* 1144 * Inform the lacp_rx thread to exit. 1145 */ 1146 mutex_enter(&grp->lg_lacp_lock); 1147 grp->lg_lacp_done = B_TRUE; 1148 cv_signal(&grp->lg_lacp_cv); 1149 while (grp->lg_lacp_rx_thread != NULL) 1150 cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock); 1151 mutex_exit(&grp->lg_lacp_lock); 1152 1153 rw_exit(&aggr_grp_lock); 1154 AGGR_GRP_REFRELE(grp); 1155 return (err); 1156 } 1157 1158 /* 1159 * Return a pointer to the member of a group with specified linkid. 1160 */ 1161 static aggr_port_t * 1162 aggr_grp_port_lookup(aggr_grp_t *grp, datalink_id_t linkid) 1163 { 1164 aggr_port_t *port; 1165 1166 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1167 1168 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1169 if (port->lp_linkid == linkid) 1170 break; 1171 } 1172 1173 return (port); 1174 } 1175 1176 /* 1177 * Stop, detach and remove a port from a link aggregation group. 1178 */ 1179 static int 1180 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port, 1181 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 1182 { 1183 int rc = 0; 1184 aggr_port_t **pport; 1185 boolean_t mac_addr_changed = B_FALSE; 1186 boolean_t link_state_changed = B_FALSE; 1187 mac_perim_handle_t mph; 1188 uint64_t val; 1189 uint_t i; 1190 uint_t stat; 1191 1192 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1193 ASSERT(grp->lg_nports > 1); 1194 ASSERT(!grp->lg_closing); 1195 1196 /* unlink port */ 1197 for (pport = &grp->lg_ports; *pport != port; 1198 pport = &(*pport)->lp_next) { 1199 if (*pport == NULL) { 1200 rc = ENOENT; 1201 goto done; 1202 } 1203 } 1204 *pport = port->lp_next; 1205 1206 mac_perim_enter_by_mh(port->lp_mh, &mph); 1207 1208 /* 1209 * If the MAC address of the port being removed was assigned 1210 * to the group, update the group MAC address 1211 * using the MAC address of a different port. 1212 */ 1213 if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) { 1214 /* 1215 * Set the MAC address of the group to the 1216 * MAC address of its first port. 1217 */ 1218 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 1219 grp->lg_mac_addr_port = grp->lg_ports; 1220 mac_addr_changed = B_TRUE; 1221 } 1222 1223 link_state_changed = aggr_grp_detach_port(grp, port); 1224 1225 /* 1226 * Add the counter statistics of the ports while it was aggregated 1227 * to the group's residual statistics. This is done by obtaining 1228 * the current counter from the underlying MAC then subtracting the 1229 * value of the counter at the moment it was added to the 1230 * aggregation. 1231 */ 1232 for (i = 0; i < MAC_NSTAT; i++) { 1233 stat = i + MAC_STAT_MIN; 1234 if (!MAC_STAT_ISACOUNTER(stat)) 1235 continue; 1236 val = aggr_port_stat(port, stat); 1237 val -= port->lp_stat[i]; 1238 grp->lg_stat[i] += val; 1239 } 1240 for (i = 0; i < ETHER_NSTAT; i++) { 1241 stat = i + MACTYPE_STAT_MIN; 1242 if (!ETHER_STAT_ISACOUNTER(stat)) 1243 continue; 1244 val = aggr_port_stat(port, stat); 1245 val -= port->lp_ether_stat[i]; 1246 grp->lg_ether_stat[i] += val; 1247 } 1248 1249 grp->lg_nports--; 1250 mac_perim_exit(mph); 1251 1252 aggr_port_delete(port); 1253 1254 /* 1255 * If the group MAC address has changed, update the MAC address of 1256 * the remaining constituent ports according to the new MAC 1257 * address of the group. 1258 */ 1259 if (mac_addr_changed && aggr_grp_update_ports_mac(grp)) 1260 link_state_changed = B_TRUE; 1261 1262 done: 1263 if (mac_addr_changedp != NULL) 1264 *mac_addr_changedp = mac_addr_changed; 1265 if (link_state_changedp != NULL) 1266 *link_state_changedp = link_state_changed; 1267 1268 return (rc); 1269 } 1270 1271 /* 1272 * Remove one or more ports from an existing link aggregation group. 1273 */ 1274 int 1275 aggr_grp_rem_ports(datalink_id_t linkid, uint_t nports, laioc_port_t *ports) 1276 { 1277 int rc = 0, i; 1278 aggr_grp_t *grp = NULL; 1279 aggr_port_t *port; 1280 boolean_t mac_addr_update = B_FALSE, mac_addr_changed; 1281 boolean_t link_state_update = B_FALSE, link_state_changed; 1282 mac_perim_handle_t mph, pmph; 1283 1284 /* get group corresponding to linkid */ 1285 rw_enter(&aggr_grp_lock, RW_READER); 1286 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1287 (mod_hash_val_t *)&grp) != 0) { 1288 rw_exit(&aggr_grp_lock); 1289 return (ENOENT); 1290 } 1291 AGGR_GRP_REFHOLD(grp); 1292 1293 /* 1294 * Hold the perimeter so that the aggregation won't be destroyed. 1295 */ 1296 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1297 rw_exit(&aggr_grp_lock); 1298 1299 /* we need to keep at least one port per group */ 1300 if (nports >= grp->lg_nports) { 1301 rc = EINVAL; 1302 goto bail; 1303 } 1304 1305 /* first verify that all the groups are valid */ 1306 for (i = 0; i < nports; i++) { 1307 if (aggr_grp_port_lookup(grp, ports[i].lp_linkid) == NULL) { 1308 /* port not found */ 1309 rc = ENOENT; 1310 goto bail; 1311 } 1312 } 1313 1314 /* clear the promiscous mode for the specified ports */ 1315 for (i = 0; i < nports && rc == 0; i++) { 1316 /* lookup port */ 1317 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 1318 ASSERT(port != NULL); 1319 1320 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1321 rc = aggr_port_promisc(port, B_FALSE); 1322 mac_perim_exit(pmph); 1323 } 1324 if (rc != 0) { 1325 for (i = 0; i < nports; i++) { 1326 port = aggr_grp_port_lookup(grp, 1327 ports[i].lp_linkid); 1328 ASSERT(port != NULL); 1329 1330 /* 1331 * Turn the promiscuous mode back on if it is required 1332 * to receive the non-primary address over a port, or 1333 * the promiscous mode is enabled over the aggr. 1334 */ 1335 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1336 if (port->lp_started && (grp->lg_promisc || 1337 port->lp_prom_addr != NULL)) { 1338 (void) aggr_port_promisc(port, B_TRUE); 1339 } 1340 mac_perim_exit(pmph); 1341 } 1342 goto bail; 1343 } 1344 1345 /* remove the specified ports from group */ 1346 for (i = 0; i < nports; i++) { 1347 /* lookup port */ 1348 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 1349 ASSERT(port != NULL); 1350 1351 /* stop port if group has already been started */ 1352 if (grp->lg_started) { 1353 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1354 aggr_port_stop(port); 1355 mac_perim_exit(pmph); 1356 } 1357 1358 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 1359 /* remove port from group */ 1360 rc = aggr_grp_rem_port(grp, port, &mac_addr_changed, 1361 &link_state_changed); 1362 ASSERT(rc == 0); 1363 mac_addr_update = mac_addr_update || mac_addr_changed; 1364 link_state_update = link_state_update || link_state_changed; 1365 } 1366 1367 bail: 1368 if (mac_addr_update) 1369 mac_unicst_update(grp->lg_mh, grp->lg_addr); 1370 if (link_state_update) 1371 mac_link_update(grp->lg_mh, grp->lg_link_state); 1372 if (rc == 0) 1373 mac_resource_update(grp->lg_mh); 1374 1375 mac_perim_exit(mph); 1376 AGGR_GRP_REFRELE(grp); 1377 1378 return (rc); 1379 } 1380 1381 int 1382 aggr_grp_delete(datalink_id_t linkid) 1383 { 1384 aggr_grp_t *grp = NULL; 1385 aggr_port_t *port, *cport; 1386 datalink_id_t tmpid; 1387 mod_hash_val_t val; 1388 mac_perim_handle_t mph, pmph; 1389 int err; 1390 1391 rw_enter(&aggr_grp_lock, RW_WRITER); 1392 1393 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1394 (mod_hash_val_t *)&grp) != 0) { 1395 rw_exit(&aggr_grp_lock); 1396 return (ENOENT); 1397 } 1398 1399 /* 1400 * Note that dls_devnet_destroy() must be called before lg_lock is 1401 * held. Otherwise, it will deadlock if another thread is in 1402 * aggr_m_stat() and thus has a kstat_hold() on the kstats that 1403 * dls_devnet_destroy() needs to delete. 1404 */ 1405 if ((err = dls_devnet_destroy(grp->lg_mh, &tmpid, B_TRUE)) != 0) { 1406 rw_exit(&aggr_grp_lock); 1407 return (err); 1408 } 1409 ASSERT(linkid == tmpid); 1410 1411 /* 1412 * Unregister from the MAC service module. Since this can 1413 * fail if a client hasn't closed the MAC port, we gracefully 1414 * fail the operation. 1415 */ 1416 if ((err = mac_disable(grp->lg_mh)) != 0) { 1417 (void) dls_devnet_create(grp->lg_mh, linkid); 1418 rw_exit(&aggr_grp_lock); 1419 return (err); 1420 } 1421 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(linkid), &val); 1422 ASSERT(grp == (aggr_grp_t *)val); 1423 1424 ASSERT(aggr_grp_cnt > 0); 1425 aggr_grp_cnt--; 1426 rw_exit(&aggr_grp_lock); 1427 1428 /* 1429 * Inform the lacp_rx thread to exit. 1430 */ 1431 mutex_enter(&grp->lg_lacp_lock); 1432 grp->lg_lacp_done = B_TRUE; 1433 cv_signal(&grp->lg_lacp_cv); 1434 while (grp->lg_lacp_rx_thread != NULL) 1435 cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock); 1436 mutex_exit(&grp->lg_lacp_lock); 1437 1438 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1439 1440 grp->lg_closing = B_TRUE; 1441 /* detach and free MAC ports associated with group */ 1442 port = grp->lg_ports; 1443 while (port != NULL) { 1444 cport = port->lp_next; 1445 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1446 if (grp->lg_started) 1447 aggr_port_stop(port); 1448 (void) aggr_grp_detach_port(grp, port); 1449 mac_perim_exit(pmph); 1450 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 1451 aggr_port_delete(port); 1452 port = cport; 1453 } 1454 1455 mac_perim_exit(mph); 1456 1457 /* 1458 * Wait for the port's lacp timer thread and its notification callback 1459 * to exit before calling mac_unregister() since both needs to access 1460 * the mac perimeter of the grp. 1461 */ 1462 aggr_grp_port_wait(grp); 1463 1464 VERIFY(mac_unregister(grp->lg_mh) == 0); 1465 grp->lg_mh = NULL; 1466 1467 AGGR_GRP_REFRELE(grp); 1468 return (0); 1469 } 1470 1471 void 1472 aggr_grp_free(aggr_grp_t *grp) 1473 { 1474 ASSERT(grp->lg_refs == 0); 1475 ASSERT(grp->lg_port_ref == 0); 1476 if (grp->lg_key > AGGR_MAX_KEY) { 1477 id_free(key_ids, grp->lg_key); 1478 grp->lg_key = 0; 1479 } 1480 kmem_cache_free(aggr_grp_cache, grp); 1481 } 1482 1483 int 1484 aggr_grp_info(datalink_id_t linkid, void *fn_arg, 1485 aggr_grp_info_new_grp_fn_t new_grp_fn, 1486 aggr_grp_info_new_port_fn_t new_port_fn) 1487 { 1488 aggr_grp_t *grp; 1489 aggr_port_t *port; 1490 mac_perim_handle_t mph, pmph; 1491 int rc = 0; 1492 1493 rw_enter(&aggr_grp_lock, RW_READER); 1494 1495 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1496 (mod_hash_val_t *)&grp) != 0) { 1497 rw_exit(&aggr_grp_lock); 1498 return (ENOENT); 1499 } 1500 AGGR_GRP_REFHOLD(grp); 1501 1502 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1503 rw_exit(&aggr_grp_lock); 1504 1505 rc = new_grp_fn(fn_arg, grp->lg_linkid, 1506 (grp->lg_key > AGGR_MAX_KEY) ? 0 : grp->lg_key, grp->lg_addr, 1507 grp->lg_addr_fixed, grp->lg_force, grp->lg_tx_policy, 1508 grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer); 1509 1510 if (rc != 0) 1511 goto bail; 1512 1513 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1514 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1515 rc = new_port_fn(fn_arg, port->lp_linkid, port->lp_addr, 1516 port->lp_state, &port->lp_lacp.ActorOperPortState); 1517 mac_perim_exit(pmph); 1518 1519 if (rc != 0) 1520 goto bail; 1521 } 1522 1523 bail: 1524 mac_perim_exit(mph); 1525 AGGR_GRP_REFRELE(grp); 1526 return (rc); 1527 } 1528 1529 /*ARGSUSED*/ 1530 static void 1531 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1532 { 1533 miocnak(q, mp, 0, ENOTSUP); 1534 } 1535 1536 static int 1537 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val) 1538 { 1539 aggr_port_t *port; 1540 uint_t stat_index; 1541 1542 /* We only aggregate counter statistics. */ 1543 if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) || 1544 IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) { 1545 return (ENOTSUP); 1546 } 1547 1548 /* 1549 * Counter statistics for a group are computed by aggregating the 1550 * counters of the members MACs while they were aggregated, plus 1551 * the residual counter of the group itself, which is updated each 1552 * time a MAC is removed from the group. 1553 */ 1554 *val = 0; 1555 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1556 /* actual port statistic */ 1557 *val += aggr_port_stat(port, stat); 1558 /* 1559 * minus the port stat when it was added, plus any residual 1560 * amount for the group. 1561 */ 1562 if (IS_MAC_STAT(stat)) { 1563 stat_index = stat - MAC_STAT_MIN; 1564 *val -= port->lp_stat[stat_index]; 1565 *val += grp->lg_stat[stat_index]; 1566 } else if (IS_MACTYPE_STAT(stat)) { 1567 stat_index = stat - MACTYPE_STAT_MIN; 1568 *val -= port->lp_ether_stat[stat_index]; 1569 *val += grp->lg_ether_stat[stat_index]; 1570 } 1571 } 1572 return (0); 1573 } 1574 1575 static int 1576 aggr_m_stat(void *arg, uint_t stat, uint64_t *val) 1577 { 1578 aggr_grp_t *grp = arg; 1579 mac_perim_handle_t mph; 1580 int rval = 0; 1581 1582 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1583 1584 switch (stat) { 1585 case MAC_STAT_IFSPEED: 1586 *val = grp->lg_ifspeed; 1587 break; 1588 1589 case ETHER_STAT_LINK_DUPLEX: 1590 *val = grp->lg_link_duplex; 1591 break; 1592 1593 default: 1594 /* 1595 * For all other statistics, we return the aggregated stat 1596 * from the underlying ports. aggr_grp_stat() will set 1597 * rval appropriately if the statistic isn't a counter. 1598 */ 1599 rval = aggr_grp_stat(grp, stat, val); 1600 } 1601 1602 mac_perim_exit(mph); 1603 return (rval); 1604 } 1605 1606 static int 1607 aggr_m_start(void *arg) 1608 { 1609 aggr_grp_t *grp = arg; 1610 aggr_port_t *port; 1611 mac_perim_handle_t mph, pmph; 1612 1613 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1614 1615 /* 1616 * Attempts to start all configured members of the group. 1617 * Group members will be attached when their link-up notification 1618 * is received. 1619 */ 1620 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1621 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1622 if (aggr_port_start(port) != 0) { 1623 mac_perim_exit(pmph); 1624 continue; 1625 } 1626 1627 /* 1628 * Turn on the promiscuous mode if it is required to receive 1629 * the non-primary address over a port, or the promiscous 1630 * mode is enabled over the aggr. 1631 */ 1632 if (grp->lg_promisc || port->lp_prom_addr != NULL) { 1633 if (aggr_port_promisc(port, B_TRUE) != 0) 1634 aggr_port_stop(port); 1635 } 1636 mac_perim_exit(pmph); 1637 } 1638 1639 grp->lg_started = B_TRUE; 1640 1641 mac_perim_exit(mph); 1642 return (0); 1643 } 1644 1645 static void 1646 aggr_m_stop(void *arg) 1647 { 1648 aggr_grp_t *grp = arg; 1649 aggr_port_t *port; 1650 mac_perim_handle_t mph, pmph; 1651 1652 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1653 1654 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1655 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1656 1657 /* reset port promiscuous mode */ 1658 (void) aggr_port_promisc(port, B_FALSE); 1659 1660 aggr_port_stop(port); 1661 mac_perim_exit(pmph); 1662 } 1663 1664 grp->lg_started = B_FALSE; 1665 mac_perim_exit(mph); 1666 } 1667 1668 static int 1669 aggr_m_promisc(void *arg, boolean_t on) 1670 { 1671 aggr_grp_t *grp = arg; 1672 aggr_port_t *port; 1673 boolean_t link_state_changed = B_FALSE; 1674 mac_perim_handle_t mph, pmph; 1675 1676 AGGR_GRP_REFHOLD(grp); 1677 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1678 1679 ASSERT(!grp->lg_closing); 1680 1681 if (on == grp->lg_promisc) 1682 goto bail; 1683 1684 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1685 int err = 0; 1686 1687 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1688 AGGR_PORT_REFHOLD(port); 1689 if (!on && (port->lp_prom_addr == NULL)) 1690 err = aggr_port_promisc(port, B_FALSE); 1691 else if (on && port->lp_started) 1692 err = aggr_port_promisc(port, B_TRUE); 1693 1694 if (err != 0) { 1695 if (aggr_grp_detach_port(grp, port)) 1696 link_state_changed = B_TRUE; 1697 } else { 1698 /* 1699 * If a port was detached because of a previous 1700 * failure changing the promiscuity, the port 1701 * is reattached when it successfully changes 1702 * the promiscuity now, and this might cause 1703 * the link state of the aggregation to change. 1704 */ 1705 if (aggr_grp_attach_port(grp, port)) 1706 link_state_changed = B_TRUE; 1707 } 1708 mac_perim_exit(pmph); 1709 AGGR_PORT_REFRELE(port); 1710 } 1711 1712 grp->lg_promisc = on; 1713 1714 if (link_state_changed) 1715 mac_link_update(grp->lg_mh, grp->lg_link_state); 1716 1717 bail: 1718 mac_perim_exit(mph); 1719 AGGR_GRP_REFRELE(grp); 1720 1721 return (0); 1722 } 1723 1724 static void 1725 aggr_grp_port_rename(const char *new_name, void *arg) 1726 { 1727 /* 1728 * aggr port's mac client name is the format of "aggr link name" plus 1729 * AGGR_PORT_NAME_DELIMIT plus "underneath link name". 1730 */ 1731 int aggr_len, link_len, clnt_name_len, i; 1732 char *str_end, *str_st, *str_del; 1733 char aggr_name[MAXNAMELEN]; 1734 char link_name[MAXNAMELEN]; 1735 char *clnt_name; 1736 aggr_grp_t *aggr_grp = arg; 1737 aggr_port_t *aggr_port = aggr_grp->lg_ports; 1738 1739 for (i = 0; i < aggr_grp->lg_nports; i++) { 1740 clnt_name = mac_client_name(aggr_port->lp_mch); 1741 clnt_name_len = strlen(clnt_name); 1742 str_st = clnt_name; 1743 str_end = &(clnt_name[clnt_name_len]); 1744 str_del = strchr(str_st, AGGR_PORT_NAME_DELIMIT); 1745 ASSERT(str_del != NULL); 1746 aggr_len = (intptr_t)((uintptr_t)str_del - (uintptr_t)str_st); 1747 link_len = (intptr_t)((uintptr_t)str_end - (uintptr_t)str_del); 1748 bzero(aggr_name, MAXNAMELEN); 1749 bzero(link_name, MAXNAMELEN); 1750 bcopy(clnt_name, aggr_name, aggr_len); 1751 bcopy(str_del, link_name, link_len + 1); 1752 bzero(clnt_name, MAXNAMELEN); 1753 (void) snprintf(clnt_name, MAXNAMELEN, "%s%s", new_name, 1754 link_name); 1755 1756 (void) mac_rename_primary(aggr_port->lp_mh, NULL); 1757 aggr_port = aggr_port->lp_next; 1758 } 1759 } 1760 1761 /* 1762 * Initialize the capabilities that are advertised for the group 1763 * according to the capabilities of the constituent ports. 1764 */ 1765 static boolean_t 1766 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 1767 { 1768 aggr_grp_t *grp = arg; 1769 1770 switch (cap) { 1771 case MAC_CAPAB_HCKSUM: { 1772 uint32_t *hcksum_txflags = cap_data; 1773 *hcksum_txflags = grp->lg_hcksum_txflags; 1774 break; 1775 } 1776 case MAC_CAPAB_NO_NATIVEVLAN: 1777 return (!grp->lg_vlan); 1778 case MAC_CAPAB_NO_ZCOPY: 1779 return (!grp->lg_zcopy); 1780 case MAC_CAPAB_RINGS: { 1781 mac_capab_rings_t *cap_rings = cap_data; 1782 1783 if (cap_rings->mr_type == MAC_RING_TYPE_RX) { 1784 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC; 1785 cap_rings->mr_rnum = grp->lg_rx_group.arg_ring_cnt; 1786 cap_rings->mr_rget = aggr_fill_ring; 1787 1788 /* 1789 * An aggregation advertises only one (pseudo) RX 1790 * group, which virtualizes the main/primary group of 1791 * the underlying devices. 1792 */ 1793 cap_rings->mr_gnum = 1; 1794 cap_rings->mr_gget = aggr_fill_group; 1795 cap_rings->mr_gaddring = NULL; 1796 cap_rings->mr_gremring = NULL; 1797 } else { 1798 return (B_FALSE); 1799 } 1800 break; 1801 } 1802 case MAC_CAPAB_AGGR: 1803 { 1804 mac_capab_aggr_t *aggr_cap; 1805 1806 if (cap_data != NULL) { 1807 aggr_cap = cap_data; 1808 aggr_cap->mca_rename_fn = aggr_grp_port_rename; 1809 aggr_cap->mca_unicst = aggr_m_unicst; 1810 } 1811 return (B_TRUE); 1812 } 1813 default: 1814 return (B_FALSE); 1815 } 1816 return (B_TRUE); 1817 } 1818 1819 /* 1820 * Callback funtion for MAC layer to register groups. 1821 */ 1822 static void 1823 aggr_fill_group(void *arg, mac_ring_type_t rtype, const int index, 1824 mac_group_info_t *infop, mac_group_handle_t gh) 1825 { 1826 aggr_grp_t *grp = arg; 1827 aggr_pseudo_rx_group_t *rx_group; 1828 1829 ASSERT(rtype == MAC_RING_TYPE_RX && index == 0); 1830 rx_group = &grp->lg_rx_group; 1831 rx_group->arg_gh = gh; 1832 rx_group->arg_grp = grp; 1833 1834 infop->mgi_driver = (mac_group_driver_t)rx_group; 1835 infop->mgi_start = NULL; 1836 infop->mgi_stop = NULL; 1837 infop->mgi_addmac = aggr_addmac; 1838 infop->mgi_remmac = aggr_remmac; 1839 infop->mgi_count = rx_group->arg_ring_cnt; 1840 } 1841 1842 /* 1843 * Callback funtion for MAC layer to register all rings. 1844 */ 1845 static void 1846 aggr_fill_ring(void *arg, mac_ring_type_t rtype, const int rg_index, 1847 const int index, mac_ring_info_t *infop, mac_ring_handle_t rh) 1848 { 1849 aggr_grp_t *grp = arg; 1850 1851 switch (rtype) { 1852 case MAC_RING_TYPE_RX: { 1853 aggr_pseudo_rx_group_t *rx_group = &grp->lg_rx_group; 1854 aggr_pseudo_rx_ring_t *rx_ring; 1855 mac_intr_t aggr_mac_intr; 1856 1857 ASSERT(rg_index == 0); 1858 1859 ASSERT((index >= 0) && (index < rx_group->arg_ring_cnt)); 1860 rx_ring = rx_group->arg_rings + index; 1861 rx_ring->arr_rh = rh; 1862 1863 /* 1864 * Entrypoint to enable interrupt (disable poll) and 1865 * disable interrupt (enable poll). 1866 */ 1867 aggr_mac_intr.mi_handle = (mac_intr_handle_t)rx_ring; 1868 aggr_mac_intr.mi_enable = aggr_pseudo_enable_intr; 1869 aggr_mac_intr.mi_disable = aggr_pseudo_disable_intr; 1870 1871 infop->mri_driver = (mac_ring_driver_t)rx_ring; 1872 infop->mri_start = aggr_pseudo_start_ring; 1873 infop->mri_stop = aggr_pseudo_stop_ring; 1874 1875 infop->mri_intr = aggr_mac_intr; 1876 infop->mri_poll = aggr_rx_poll; 1877 break; 1878 } 1879 default: 1880 break; 1881 } 1882 } 1883 1884 static mblk_t * 1885 aggr_rx_poll(void *arg, int bytes_to_pickup) 1886 { 1887 aggr_pseudo_rx_ring_t *rr_ring = arg; 1888 aggr_port_t *port = rr_ring->arr_port; 1889 aggr_grp_t *grp = port->lp_grp; 1890 mblk_t *mp_chain, *mp, **mpp; 1891 1892 mp_chain = mac_hwring_poll(rr_ring->arr_hw_rh, bytes_to_pickup); 1893 1894 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 1895 return (mp_chain); 1896 1897 mpp = &mp_chain; 1898 while ((mp = *mpp) != NULL) { 1899 if (MBLKL(mp) >= sizeof (struct ether_header)) { 1900 struct ether_header *ehp; 1901 1902 ehp = (struct ether_header *)mp->b_rptr; 1903 if (ntohs(ehp->ether_type) == ETHERTYPE_SLOW) { 1904 *mpp = mp->b_next; 1905 mp->b_next = NULL; 1906 aggr_recv_lacp(port, 1907 (mac_resource_handle_t)rr_ring, mp); 1908 continue; 1909 } 1910 } 1911 1912 if (!port->lp_collector_enabled) { 1913 *mpp = mp->b_next; 1914 mp->b_next = NULL; 1915 freemsg(mp); 1916 continue; 1917 } 1918 mpp = &mp->b_next; 1919 } 1920 return (mp_chain); 1921 } 1922 1923 static int 1924 aggr_addmac(void *arg, const uint8_t *mac_addr) 1925 { 1926 aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg; 1927 aggr_unicst_addr_t *addr, **pprev; 1928 aggr_grp_t *grp = rx_group->arg_grp; 1929 aggr_port_t *port, *p; 1930 mac_perim_handle_t mph; 1931 int err = 0; 1932 1933 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1934 1935 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 1936 mac_perim_exit(mph); 1937 return (0); 1938 } 1939 1940 /* 1941 * Insert this mac address into the list of mac addresses owned by 1942 * the aggregation pseudo group. 1943 */ 1944 pprev = &rx_group->arg_macaddr; 1945 while ((addr = *pprev) != NULL) { 1946 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0) { 1947 mac_perim_exit(mph); 1948 return (EEXIST); 1949 } 1950 pprev = &addr->aua_next; 1951 } 1952 addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP); 1953 bcopy(mac_addr, addr->aua_addr, ETHERADDRL); 1954 addr->aua_next = NULL; 1955 *pprev = addr; 1956 1957 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 1958 if ((err = aggr_port_addmac(port, mac_addr)) != 0) 1959 break; 1960 1961 if (err != 0) { 1962 for (p = grp->lg_ports; p != port; p = p->lp_next) 1963 aggr_port_remmac(p, mac_addr); 1964 1965 *pprev = NULL; 1966 kmem_free(addr, sizeof (aggr_unicst_addr_t)); 1967 } 1968 1969 mac_perim_exit(mph); 1970 return (err); 1971 } 1972 1973 static int 1974 aggr_remmac(void *arg, const uint8_t *mac_addr) 1975 { 1976 aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg; 1977 aggr_unicst_addr_t *addr, **pprev; 1978 aggr_grp_t *grp = rx_group->arg_grp; 1979 aggr_port_t *port; 1980 mac_perim_handle_t mph; 1981 int err = 0; 1982 1983 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1984 1985 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 1986 mac_perim_exit(mph); 1987 return (0); 1988 } 1989 1990 /* 1991 * Insert this mac address into the list of mac addresses owned by 1992 * the aggregation pseudo group. 1993 */ 1994 pprev = &rx_group->arg_macaddr; 1995 while ((addr = *pprev) != NULL) { 1996 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0) { 1997 pprev = &addr->aua_next; 1998 continue; 1999 } 2000 break; 2001 } 2002 if (addr == NULL) { 2003 mac_perim_exit(mph); 2004 return (EINVAL); 2005 } 2006 2007 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 2008 aggr_port_remmac(port, mac_addr); 2009 2010 *pprev = addr->aua_next; 2011 kmem_free(addr, sizeof (aggr_unicst_addr_t)); 2012 2013 mac_perim_exit(mph); 2014 return (err); 2015 } 2016 2017 /* 2018 * Add or remove the multicast addresses that are defined for the group 2019 * to or from the specified port. 2020 * This function is called before stopping a port, before a port 2021 * is detached from a group, and when attaching a port to a group. 2022 */ 2023 void 2024 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add) 2025 { 2026 aggr_grp_t *grp = port->lp_grp; 2027 2028 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 2029 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 2030 2031 if (!port->lp_started) 2032 return; 2033 2034 mac_multicast_refresh(grp->lg_mh, aggr_port_multicst, port, add); 2035 } 2036 2037 static int 2038 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 2039 { 2040 aggr_grp_t *grp = arg; 2041 aggr_port_t *port = NULL; 2042 mac_perim_handle_t mph; 2043 int err = 0, cerr; 2044 2045 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2046 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2047 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 2048 continue; 2049 cerr = aggr_port_multicst(port, add, addrp); 2050 if (cerr != 0 && err == 0) 2051 err = cerr; 2052 } 2053 mac_perim_exit(mph); 2054 return (err); 2055 } 2056 2057 static int 2058 aggr_m_unicst(void *arg, const uint8_t *macaddr) 2059 { 2060 aggr_grp_t *grp = arg; 2061 mac_perim_handle_t mph; 2062 int err; 2063 2064 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2065 err = aggr_grp_modify_common(grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr, 2066 0, 0); 2067 mac_perim_exit(mph); 2068 return (err); 2069 } 2070 2071 /* 2072 * Initialize the capabilities that are advertised for the group 2073 * according to the capabilities of the constituent ports. 2074 */ 2075 static void 2076 aggr_grp_capab_set(aggr_grp_t *grp) 2077 { 2078 uint32_t cksum; 2079 aggr_port_t *port; 2080 2081 ASSERT(grp->lg_mh == NULL); 2082 ASSERT(grp->lg_ports != NULL); 2083 2084 grp->lg_hcksum_txflags = (uint32_t)-1; 2085 grp->lg_zcopy = B_TRUE; 2086 grp->lg_vlan = B_TRUE; 2087 2088 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2089 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum)) 2090 cksum = 0; 2091 grp->lg_hcksum_txflags &= cksum; 2092 2093 grp->lg_vlan &= 2094 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL); 2095 2096 grp->lg_zcopy &= 2097 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL); 2098 } 2099 } 2100 2101 /* 2102 * Checks whether the capabilities of the port being added are compatible 2103 * with the current capabilities of the aggregation. 2104 */ 2105 static boolean_t 2106 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port) 2107 { 2108 uint32_t hcksum_txflags; 2109 2110 ASSERT(grp->lg_ports != NULL); 2111 2112 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) & 2113 grp->lg_vlan) != grp->lg_vlan) { 2114 return (B_FALSE); 2115 } 2116 2117 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL)) & 2118 grp->lg_zcopy) != grp->lg_zcopy) { 2119 return (B_FALSE); 2120 } 2121 2122 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) { 2123 if (grp->lg_hcksum_txflags != 0) 2124 return (B_FALSE); 2125 } else if ((hcksum_txflags & grp->lg_hcksum_txflags) != 2126 grp->lg_hcksum_txflags) { 2127 return (B_FALSE); 2128 } 2129 2130 return (B_TRUE); 2131 } 2132 2133 /* 2134 * Returns the maximum SDU according to the SDU of the constituent ports. 2135 */ 2136 static uint_t 2137 aggr_grp_max_sdu(aggr_grp_t *grp) 2138 { 2139 uint_t max_sdu = (uint_t)-1; 2140 aggr_port_t *port; 2141 2142 ASSERT(grp->lg_mh == NULL); 2143 ASSERT(grp->lg_ports != NULL); 2144 2145 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2146 uint_t port_sdu_max; 2147 2148 mac_sdu_get(port->lp_mh, NULL, &port_sdu_max); 2149 if (max_sdu > port_sdu_max) 2150 max_sdu = port_sdu_max; 2151 } 2152 2153 return (max_sdu); 2154 } 2155 2156 /* 2157 * Checks if the maximum SDU of the specified port is compatible 2158 * with the maximum SDU of the specified aggregation group, returns 2159 * B_TRUE if it is, B_FALSE otherwise. 2160 */ 2161 static boolean_t 2162 aggr_grp_sdu_check(aggr_grp_t *grp, aggr_port_t *port) 2163 { 2164 uint_t port_sdu_max; 2165 2166 mac_sdu_get(port->lp_mh, NULL, &port_sdu_max); 2167 return (port_sdu_max >= grp->lg_max_sdu); 2168 } 2169 2170 /* 2171 * Returns the maximum margin according to the margin of the constituent ports. 2172 */ 2173 static uint32_t 2174 aggr_grp_max_margin(aggr_grp_t *grp) 2175 { 2176 uint32_t margin = UINT32_MAX; 2177 aggr_port_t *port; 2178 2179 ASSERT(grp->lg_mh == NULL); 2180 ASSERT(grp->lg_ports != NULL); 2181 2182 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2183 if (margin > port->lp_margin) 2184 margin = port->lp_margin; 2185 } 2186 2187 grp->lg_margin = margin; 2188 return (margin); 2189 } 2190 2191 /* 2192 * Checks if the maximum margin of the specified port is compatible 2193 * with the maximum margin of the specified aggregation group, returns 2194 * B_TRUE if it is, B_FALSE otherwise. 2195 */ 2196 static boolean_t 2197 aggr_grp_margin_check(aggr_grp_t *grp, aggr_port_t *port) 2198 { 2199 if (port->lp_margin >= grp->lg_margin) 2200 return (B_TRUE); 2201 2202 /* 2203 * See whether the current margin value is allowed to be changed to 2204 * the new value. 2205 */ 2206 if (!mac_margin_update(grp->lg_mh, port->lp_margin)) 2207 return (B_FALSE); 2208 2209 grp->lg_margin = port->lp_margin; 2210 return (B_TRUE); 2211 } 2212