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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * IEEE 802.3ad Link Aggregation -- Link Aggregation Groups. 30 * 31 * An instance of the structure aggr_grp_t is allocated for each 32 * link aggregation group. When created, aggr_grp_t objects are 33 * entered into the aggr_grp_hash hash table maintained by the modhash 34 * module. The hash key is the port number associated with the link 35 * aggregation group. The port number associated with a group corresponds 36 * the key associated with the group. 37 * 38 * A set of MAC ports are associated with each association group. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/sysmacros.h> 43 #include <sys/conf.h> 44 #include <sys/cmn_err.h> 45 #include <sys/list.h> 46 #include <sys/ksynch.h> 47 #include <sys/kmem.h> 48 #include <sys/stream.h> 49 #include <sys/modctl.h> 50 #include <sys/ddi.h> 51 #include <sys/sunddi.h> 52 #include <sys/atomic.h> 53 #include <sys/stat.h> 54 #include <sys/modhash.h> 55 #include <sys/strsun.h> 56 #include <sys/dlpi.h> 57 58 #include <sys/aggr.h> 59 #include <sys/aggr_impl.h> 60 61 static int aggr_m_start(void *); 62 static void aggr_m_stop(void *); 63 static int aggr_m_promisc(void *, boolean_t); 64 static int aggr_m_multicst(void *, boolean_t, const uint8_t *); 65 static int aggr_m_unicst(void *, const uint8_t *); 66 static int aggr_m_stat(void *, uint_t, uint64_t *); 67 static void aggr_m_resources(void *); 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 71 static aggr_port_t *aggr_grp_port_lookup(aggr_grp_t *, const char *); 72 static int aggr_grp_rem_port(aggr_grp_t *, aggr_port_t *, boolean_t *, 73 boolean_t *); 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 77 static kmem_cache_t *aggr_grp_cache; 78 static mod_hash_t *aggr_grp_hash; 79 static krwlock_t aggr_grp_lock; 80 static uint_t aggr_grp_cnt; 81 82 #define GRP_HASHSZ 64 83 #define GRP_HASH_KEY(key) ((mod_hash_key_t)(uintptr_t)key) 84 85 static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0}; 86 87 /* used by grp_info_walker */ 88 typedef struct aggr_grp_info_state { 89 uint32_t ls_group_key; 90 boolean_t ls_group_found; 91 aggr_grp_info_new_grp_fn_t ls_new_grp_fn; 92 aggr_grp_info_new_port_fn_t ls_new_port_fn; 93 void *ls_fn_arg; 94 int ls_rc; 95 } aggr_grp_info_state_t; 96 97 #define AGGR_M_CALLBACK_FLAGS (MC_RESOURCES | MC_IOCTL | MC_GETCAPAB) 98 99 static mac_callbacks_t aggr_m_callbacks = { 100 AGGR_M_CALLBACK_FLAGS, 101 aggr_m_stat, 102 aggr_m_start, 103 aggr_m_stop, 104 aggr_m_promisc, 105 aggr_m_multicst, 106 aggr_m_unicst, 107 aggr_m_tx, 108 aggr_m_resources, 109 aggr_m_ioctl, 110 aggr_m_capab_get 111 }; 112 113 /*ARGSUSED*/ 114 static int 115 aggr_grp_constructor(void *buf, void *arg, int kmflag) 116 { 117 aggr_grp_t *grp = buf; 118 119 bzero(grp, sizeof (*grp)); 120 rw_init(&grp->lg_lock, NULL, RW_DRIVER, NULL); 121 mutex_init(&grp->aggr.gl_lock, NULL, MUTEX_DEFAULT, NULL); 122 123 grp->lg_link_state = LINK_STATE_UNKNOWN; 124 125 return (0); 126 } 127 128 /*ARGSUSED*/ 129 static void 130 aggr_grp_destructor(void *buf, void *arg) 131 { 132 aggr_grp_t *grp = buf; 133 134 if (grp->lg_tx_ports != NULL) { 135 kmem_free(grp->lg_tx_ports, 136 grp->lg_tx_ports_size * sizeof (aggr_port_t *)); 137 } 138 139 mutex_destroy(&grp->aggr.gl_lock); 140 rw_destroy(&grp->lg_lock); 141 } 142 143 void 144 aggr_grp_init(void) 145 { 146 aggr_grp_cache = kmem_cache_create("aggr_grp_cache", 147 sizeof (aggr_grp_t), 0, aggr_grp_constructor, 148 aggr_grp_destructor, NULL, NULL, NULL, 0); 149 150 aggr_grp_hash = mod_hash_create_idhash("aggr_grp_hash", 151 GRP_HASHSZ, mod_hash_null_valdtor); 152 rw_init(&aggr_grp_lock, NULL, RW_DEFAULT, NULL); 153 aggr_grp_cnt = 0; 154 } 155 156 void 157 aggr_grp_fini(void) 158 { 159 rw_destroy(&aggr_grp_lock); 160 mod_hash_destroy_idhash(aggr_grp_hash); 161 kmem_cache_destroy(aggr_grp_cache); 162 } 163 164 uint_t 165 aggr_grp_count(void) 166 { 167 uint_t count; 168 169 rw_enter(&aggr_grp_lock, RW_READER); 170 count = aggr_grp_cnt; 171 rw_exit(&aggr_grp_lock); 172 return (count); 173 } 174 175 /* 176 * Attach a port to a link aggregation group. 177 * 178 * A port is attached to a link aggregation group once its speed 179 * and link state have been verified. 180 * 181 * Returns B_TRUE if the group link state or speed has changed. If 182 * it's the case, the caller must notify the MAC layer via a call 183 * to mac_link(). 184 */ 185 boolean_t 186 aggr_grp_attach_port(aggr_grp_t *grp, aggr_port_t *port) 187 { 188 boolean_t link_state_changed = B_FALSE; 189 190 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 191 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 192 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 193 194 if (port->lp_state == AGGR_PORT_STATE_ATTACHED) 195 return (B_FALSE); 196 197 /* 198 * Validate the MAC port link speed and update the group 199 * link speed if needed. 200 */ 201 if (port->lp_ifspeed == 0 || 202 port->lp_link_state != LINK_STATE_UP || 203 port->lp_link_duplex != LINK_DUPLEX_FULL) { 204 /* 205 * Can't attach a MAC port with unknown link speed, 206 * down link, or not in full duplex mode. 207 */ 208 return (B_FALSE); 209 } 210 211 if (grp->lg_ifspeed == 0) { 212 /* 213 * The group inherits the speed of the first link being 214 * attached. 215 */ 216 grp->lg_ifspeed = port->lp_ifspeed; 217 link_state_changed = B_TRUE; 218 } else if (grp->lg_ifspeed != port->lp_ifspeed) { 219 /* 220 * The link speed of the MAC port must be the same as 221 * the group link speed, as per 802.3ad. Since it is 222 * not, the attach is cancelled. 223 */ 224 return (B_FALSE); 225 } 226 227 grp->lg_nattached_ports++; 228 229 /* 230 * Update the group link state. 231 */ 232 if (grp->lg_link_state != LINK_STATE_UP) { 233 grp->lg_link_state = LINK_STATE_UP; 234 grp->lg_link_duplex = LINK_DUPLEX_FULL; 235 link_state_changed = B_TRUE; 236 } 237 238 aggr_grp_multicst_port(port, B_TRUE); 239 240 /* 241 * Update port's state. 242 */ 243 port->lp_state = AGGR_PORT_STATE_ATTACHED; 244 245 /* 246 * Set port's receive callback 247 */ 248 port->lp_mrh = mac_rx_add(port->lp_mh, aggr_recv_cb, (void *)port); 249 250 /* 251 * If LACP is OFF, the port can be used to send data as soon 252 * as its link is up and verified to be compatible with the 253 * aggregation. 254 * 255 * If LACP is active or passive, notify the LACP subsystem, which 256 * will enable sending on the port following the LACP protocol. 257 */ 258 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 259 aggr_send_port_enable(port); 260 else 261 aggr_lacp_port_attached(port); 262 263 return (link_state_changed); 264 } 265 266 boolean_t 267 aggr_grp_detach_port(aggr_grp_t *grp, aggr_port_t *port) 268 { 269 boolean_t link_state_changed = B_FALSE; 270 271 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 272 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 273 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 274 275 /* update state */ 276 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 277 return (B_FALSE); 278 279 mac_rx_remove(port->lp_mh, port->lp_mrh); 280 port->lp_state = AGGR_PORT_STATE_STANDBY; 281 282 aggr_grp_multicst_port(port, B_FALSE); 283 284 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 285 aggr_send_port_disable(port); 286 else 287 aggr_lacp_port_detached(port); 288 289 grp->lg_nattached_ports--; 290 if (grp->lg_nattached_ports == 0) { 291 /* the last attached MAC port of the group is being detached */ 292 grp->lg_ifspeed = 0; 293 grp->lg_link_state = LINK_STATE_DOWN; 294 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 295 link_state_changed = B_TRUE; 296 } 297 298 return (link_state_changed); 299 } 300 301 /* 302 * Update the MAC addresses of the constituent ports of the specified 303 * group. This function is invoked: 304 * - after creating a new aggregation group. 305 * - after adding new ports to an aggregation group. 306 * - after removing a port from a group when the MAC address of 307 * that port was used for the MAC address of the group. 308 * - after the MAC address of a port changed when the MAC address 309 * of that port was used for the MAC address of the group. 310 * 311 * Return true if the link state of the aggregation changed, for example 312 * as a result of a failure changing the MAC address of one of the 313 * constituent ports. 314 */ 315 boolean_t 316 aggr_grp_update_ports_mac(aggr_grp_t *grp) 317 { 318 aggr_port_t *cport; 319 boolean_t link_state_changed = B_FALSE; 320 321 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 322 323 if (grp->lg_closing) 324 return (link_state_changed); 325 326 for (cport = grp->lg_ports; cport != NULL; 327 cport = cport->lp_next) { 328 rw_enter(&cport->lp_lock, RW_WRITER); 329 if (aggr_port_unicst(cport, grp->lg_addr) != 0) { 330 link_state_changed = link_state_changed || 331 aggr_grp_detach_port(grp, cport); 332 } else { 333 /* 334 * If a port was detached because of a previous 335 * failure changing the MAC address, the port is 336 * reattached when it successfully changes the MAC 337 * address now, and this might cause the link state 338 * of the aggregation to change. 339 */ 340 link_state_changed = link_state_changed || 341 aggr_grp_attach_port(grp, cport); 342 } 343 rw_exit(&cport->lp_lock); 344 } 345 return (link_state_changed); 346 } 347 348 /* 349 * Invoked when the MAC address of a port has changed. If the port's 350 * MAC address was used for the group MAC address, set mac_addr_changedp 351 * to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST 352 * notification. If the link state changes due to detach/attach of 353 * the constituent port, set link_state_changedp to B_TRUE to indicate 354 * to the caller that it should send a MAC_NOTE_LINK notification. In both 355 * cases, it is the responsibility of the caller to invoke notification 356 * functions after releasing the the port lock. 357 */ 358 void 359 aggr_grp_port_mac_changed(aggr_grp_t *grp, aggr_port_t *port, 360 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 361 { 362 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 363 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 364 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 365 ASSERT(mac_addr_changedp != NULL); 366 ASSERT(link_state_changedp != NULL); 367 368 *mac_addr_changedp = B_FALSE; 369 *link_state_changedp = B_FALSE; 370 371 if (grp->lg_addr_fixed) { 372 /* 373 * The group is using a fixed MAC address or an automatic 374 * MAC address has not been set. 375 */ 376 return; 377 } 378 379 if (grp->lg_mac_addr_port == port) { 380 /* 381 * The MAC address of the port was assigned to the group 382 * MAC address. Update the group MAC address. 383 */ 384 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 385 *mac_addr_changedp = B_TRUE; 386 } else { 387 /* 388 * Update the actual port MAC address to the MAC address 389 * of the group. 390 */ 391 if (aggr_port_unicst(port, grp->lg_addr) != 0) { 392 *link_state_changedp = aggr_grp_detach_port(grp, port); 393 } else { 394 /* 395 * If a port was detached because of a previous 396 * failure changing the MAC address, the port is 397 * reattached when it successfully changes the MAC 398 * address now, and this might cause the link state 399 * of the aggregation to change. 400 */ 401 *link_state_changedp = aggr_grp_attach_port(grp, port); 402 } 403 } 404 } 405 406 /* 407 * Add a port to a link aggregation group. 408 */ 409 static int 410 aggr_grp_add_port(aggr_grp_t *grp, const char *name, aggr_port_t **pp) 411 { 412 aggr_port_t *port, **cport; 413 int err; 414 415 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 416 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 417 418 /* create new port */ 419 err = aggr_port_create(name, &port); 420 if (err != 0) 421 return (err); 422 423 rw_enter(&port->lp_lock, RW_WRITER); 424 425 /* add port to list of group constituent ports */ 426 cport = &grp->lg_ports; 427 while (*cport != NULL) 428 cport = &((*cport)->lp_next); 429 *cport = port; 430 431 /* 432 * Back reference to the group it is member of. A port always 433 * holds a reference to its group to ensure that the back 434 * reference is always valid. 435 */ 436 port->lp_grp = grp; 437 AGGR_GRP_REFHOLD(grp); 438 grp->lg_nports++; 439 440 aggr_lacp_init_port(port); 441 442 /* 443 * Initialize the callback functions for this port. Note that this 444 * can only be done after the lp_grp field is set. 445 */ 446 aggr_port_init_callbacks(port); 447 448 rw_exit(&port->lp_lock); 449 450 if (pp != NULL) 451 *pp = port; 452 453 return (0); 454 } 455 456 /* 457 * Add one or more ports to an existing link aggregation group. 458 */ 459 int 460 aggr_grp_add_ports(uint32_t key, uint_t nports, laioc_port_t *ports) 461 { 462 int rc, i, nadded = 0; 463 aggr_grp_t *grp = NULL; 464 aggr_port_t *port; 465 boolean_t link_state_changed = B_FALSE; 466 467 /* get group corresponding to key */ 468 rw_enter(&aggr_grp_lock, RW_READER); 469 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 470 (mod_hash_val_t *)&grp) != 0) { 471 rw_exit(&aggr_grp_lock); 472 return (ENOENT); 473 } 474 AGGR_GRP_REFHOLD(grp); 475 rw_exit(&aggr_grp_lock); 476 477 AGGR_LACP_LOCK(grp); 478 rw_enter(&grp->lg_lock, RW_WRITER); 479 480 /* add the specified ports to group */ 481 for (i = 0; i < nports; i++) { 482 /* add port to group */ 483 if ((rc = aggr_grp_add_port(grp, ports[i].lp_devname, &port)) != 484 0) { 485 goto bail; 486 } 487 ASSERT(port != NULL); 488 nadded++; 489 490 /* check capabilities */ 491 if (!aggr_grp_capab_check(grp, port)) { 492 rc = ENOTSUP; 493 goto bail; 494 } 495 496 /* start port if group has already been started */ 497 if (grp->lg_started) { 498 rw_enter(&port->lp_lock, RW_WRITER); 499 rc = aggr_port_start(port); 500 if (rc != 0) { 501 rw_exit(&port->lp_lock); 502 goto bail; 503 } 504 505 /* set port promiscuous mode */ 506 rc = aggr_port_promisc(port, grp->lg_promisc); 507 if (rc != 0) { 508 rw_exit(&port->lp_lock); 509 goto bail; 510 } 511 rw_exit(&port->lp_lock); 512 } 513 514 /* 515 * Attach each port if necessary. 516 */ 517 link_state_changed = link_state_changed || 518 aggr_port_notify_link(grp, port, B_FALSE); 519 } 520 521 /* update the MAC address of the constituent ports */ 522 link_state_changed = link_state_changed || 523 aggr_grp_update_ports_mac(grp); 524 525 if (link_state_changed) 526 mac_link_update(grp->lg_mh, grp->lg_link_state); 527 528 bail: 529 if (rc != 0) { 530 /* stop and remove ports that have been added */ 531 for (i = 0; i < nadded && !grp->lg_closing; i++) { 532 port = aggr_grp_port_lookup(grp, ports[i].lp_devname); 533 ASSERT(port != NULL); 534 if (grp->lg_started) { 535 rw_enter(&port->lp_lock, RW_WRITER); 536 aggr_port_stop(port); 537 rw_exit(&port->lp_lock); 538 } 539 (void) aggr_grp_rem_port(grp, port, NULL, NULL); 540 } 541 } 542 543 rw_exit(&grp->lg_lock); 544 AGGR_LACP_UNLOCK(grp); 545 if (rc == 0 && !grp->lg_closing) 546 mac_resource_update(grp->lg_mh); 547 AGGR_GRP_REFRELE(grp); 548 return (rc); 549 } 550 551 /* 552 * Update properties of an existing link aggregation group. 553 */ 554 int 555 aggr_grp_modify(uint32_t key, aggr_grp_t *grp_arg, uint8_t update_mask, 556 uint32_t policy, boolean_t mac_fixed, const uchar_t *mac_addr, 557 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 558 { 559 int rc = 0; 560 aggr_grp_t *grp = NULL; 561 boolean_t mac_addr_changed = B_FALSE; 562 boolean_t link_state_changed = B_FALSE; 563 564 if (grp_arg == NULL) { 565 /* get group corresponding to key */ 566 rw_enter(&aggr_grp_lock, RW_READER); 567 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 568 (mod_hash_val_t *)&grp) != 0) { 569 rc = ENOENT; 570 goto bail; 571 } 572 AGGR_LACP_LOCK(grp); 573 rw_enter(&grp->lg_lock, RW_WRITER); 574 } else { 575 grp = grp_arg; 576 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 577 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 578 } 579 580 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 581 AGGR_GRP_REFHOLD(grp); 582 583 /* validate fixed address if specified */ 584 if ((update_mask & AGGR_MODIFY_MAC) && mac_fixed && 585 ((bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) || 586 (mac_addr[0] & 0x01))) { 587 rc = EINVAL; 588 goto bail; 589 } 590 591 /* update policy if requested */ 592 if (update_mask & AGGR_MODIFY_POLICY) 593 aggr_send_update_policy(grp, policy); 594 595 /* update unicast MAC address if requested */ 596 if (update_mask & AGGR_MODIFY_MAC) { 597 if (mac_fixed) { 598 /* user-supplied MAC address */ 599 grp->lg_mac_addr_port = NULL; 600 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) != 0) { 601 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 602 mac_addr_changed = B_TRUE; 603 } 604 } else if (grp->lg_addr_fixed) { 605 /* switch from user-supplied to automatic */ 606 aggr_port_t *port = grp->lg_ports; 607 608 rw_enter(&port->lp_lock, RW_WRITER); 609 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 610 grp->lg_mac_addr_port = port; 611 mac_addr_changed = B_TRUE; 612 rw_exit(&port->lp_lock); 613 } 614 grp->lg_addr_fixed = mac_fixed; 615 } 616 617 if (mac_addr_changed) 618 link_state_changed = aggr_grp_update_ports_mac(grp); 619 620 if (update_mask & AGGR_MODIFY_LACP_MODE) 621 aggr_lacp_update_mode(grp, lacp_mode); 622 623 if ((update_mask & AGGR_MODIFY_LACP_TIMER) && !grp->lg_closing) 624 aggr_lacp_update_timer(grp, lacp_timer); 625 626 bail: 627 if (grp != NULL && !grp->lg_closing) { 628 /* 629 * If grp_arg is non-NULL, this function is called from 630 * mac_unicst_set(), and the MAC_NOTE_UNICST notification 631 * will be sent there. 632 */ 633 if ((grp_arg == NULL) && mac_addr_changed) 634 mac_unicst_update(grp->lg_mh, grp->lg_addr); 635 636 if (link_state_changed) 637 mac_link_update(grp->lg_mh, grp->lg_link_state); 638 639 } 640 641 if (grp_arg == NULL) { 642 if (grp != NULL) { 643 rw_exit(&grp->lg_lock); 644 AGGR_LACP_UNLOCK(grp); 645 } 646 rw_exit(&aggr_grp_lock); 647 } 648 649 if (grp != NULL) 650 AGGR_GRP_REFRELE(grp); 651 652 return (rc); 653 } 654 655 /* 656 * Create a new link aggregation group upon request from administrator. 657 * Returns 0 on success, an errno on failure. 658 */ 659 int 660 aggr_grp_create(uint32_t key, uint_t nports, laioc_port_t *ports, 661 uint32_t policy, boolean_t mac_fixed, uchar_t *mac_addr, 662 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer) 663 { 664 aggr_grp_t *grp = NULL; 665 aggr_port_t *port; 666 mac_register_t *mac; 667 boolean_t link_state_changed; 668 int err; 669 int i; 670 671 /* need at least one port */ 672 if (nports == 0) 673 return (EINVAL); 674 675 rw_enter(&aggr_grp_lock, RW_WRITER); 676 677 /* does a group with the same key already exist? */ 678 err = mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 679 (mod_hash_val_t *)&grp); 680 if (err == 0) { 681 rw_exit(&aggr_grp_lock); 682 return (EEXIST); 683 } 684 685 grp = kmem_cache_alloc(aggr_grp_cache, KM_SLEEP); 686 687 AGGR_LACP_LOCK(grp); 688 rw_enter(&grp->lg_lock, RW_WRITER); 689 690 grp->lg_refs = 1; 691 grp->lg_closing = B_FALSE; 692 grp->lg_key = key; 693 694 grp->lg_ifspeed = 0; 695 grp->lg_link_state = LINK_STATE_UNKNOWN; 696 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 697 grp->lg_started = B_FALSE; 698 grp->lg_promisc = B_FALSE; 699 aggr_lacp_init_grp(grp); 700 701 /* add MAC ports to group */ 702 grp->lg_ports = NULL; 703 grp->lg_nports = 0; 704 grp->lg_nattached_ports = 0; 705 grp->lg_ntx_ports = 0; 706 707 for (i = 0; i < nports; i++) { 708 err = aggr_grp_add_port(grp, ports[i].lp_devname, NULL); 709 if (err != 0) 710 goto bail; 711 } 712 713 /* 714 * If no explicit MAC address was specified by the administrator, 715 * set it to the MAC address of the first port. 716 */ 717 grp->lg_addr_fixed = mac_fixed; 718 if (grp->lg_addr_fixed) { 719 /* validate specified address */ 720 if (bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) { 721 err = EINVAL; 722 goto bail; 723 } 724 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 725 } else { 726 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 727 grp->lg_mac_addr_port = grp->lg_ports; 728 } 729 730 /* 731 * Update the MAC address of the constituent ports. 732 * None of the port is attached at this time, the link state of the 733 * aggregation will not change. 734 */ 735 link_state_changed = aggr_grp_update_ports_mac(grp); 736 ASSERT(!link_state_changed); 737 738 /* update outbound load balancing policy */ 739 aggr_send_update_policy(grp, policy); 740 741 /* set the initial group capabilities */ 742 aggr_grp_capab_set(grp); 743 744 if ((mac = mac_alloc(MAC_VERSION)) == NULL) 745 goto bail; 746 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 747 mac->m_driver = grp; 748 mac->m_dip = aggr_dip; 749 mac->m_instance = key; 750 mac->m_src_addr = grp->lg_addr; 751 mac->m_callbacks = &aggr_m_callbacks; 752 mac->m_min_sdu = 0; 753 mac->m_max_sdu = ETHERMTU; 754 err = mac_register(mac, &grp->lg_mh); 755 mac_free(mac); 756 if (err != 0) 757 goto bail; 758 759 /* set LACP mode */ 760 aggr_lacp_set_mode(grp, lacp_mode, lacp_timer); 761 762 /* 763 * Attach each port if necessary. 764 */ 765 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 766 (void) aggr_port_notify_link(grp, port, B_FALSE); 767 768 /* add new group to hash table */ 769 err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(key), 770 (mod_hash_val_t)grp); 771 ASSERT(err == 0); 772 aggr_grp_cnt++; 773 774 rw_exit(&grp->lg_lock); 775 AGGR_LACP_UNLOCK(grp); 776 rw_exit(&aggr_grp_lock); 777 return (0); 778 779 bail: 780 if (grp != NULL) { 781 aggr_port_t *cport; 782 783 grp->lg_closing = B_TRUE; 784 785 port = grp->lg_ports; 786 while (port != NULL) { 787 cport = port->lp_next; 788 aggr_port_delete(port); 789 port = cport; 790 } 791 792 rw_exit(&grp->lg_lock); 793 AGGR_LACP_UNLOCK(grp); 794 795 kmem_cache_free(aggr_grp_cache, grp); 796 } 797 798 rw_exit(&aggr_grp_lock); 799 return (err); 800 } 801 802 /* 803 * Return a pointer to the member of a group with specified device name 804 * and port number. 805 */ 806 static aggr_port_t * 807 aggr_grp_port_lookup(aggr_grp_t *grp, const char *devname) 808 { 809 aggr_port_t *port; 810 811 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 812 813 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 814 if (strcmp(port->lp_devname, devname) == 0) 815 break; 816 } 817 818 return (port); 819 } 820 821 /* 822 * Stop, detach and remove a port from a link aggregation group. 823 */ 824 static int 825 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port, 826 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 827 { 828 int rc = 0; 829 aggr_port_t **pport; 830 boolean_t mac_addr_changed = B_FALSE; 831 boolean_t link_state_changed = B_FALSE; 832 uint64_t val; 833 uint_t i; 834 uint_t stat; 835 836 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 837 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 838 ASSERT(grp->lg_nports > 1); 839 ASSERT(!grp->lg_closing); 840 841 /* unlink port */ 842 for (pport = &grp->lg_ports; *pport != port; 843 pport = &(*pport)->lp_next) { 844 if (*pport == NULL) { 845 rc = ENOENT; 846 goto done; 847 } 848 } 849 *pport = port->lp_next; 850 851 atomic_add_32(&port->lp_closing, 1); 852 853 rw_enter(&port->lp_lock, RW_WRITER); 854 855 /* 856 * If the MAC address of the port being removed was assigned 857 * to the group, update the group MAC address 858 * using the MAC address of a different port. 859 */ 860 if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) { 861 /* 862 * Set the MAC address of the group to the 863 * MAC address of its first port. 864 */ 865 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 866 grp->lg_mac_addr_port = grp->lg_ports; 867 mac_addr_changed = B_TRUE; 868 } 869 870 link_state_changed = aggr_grp_detach_port(grp, port); 871 872 /* 873 * Add the counter statistics of the ports while it was aggregated 874 * to the group's residual statistics. This is done by obtaining 875 * the current counter from the underlying MAC then subtracting the 876 * value of the counter at the moment it was added to the 877 * aggregation. 878 */ 879 for (i = 0; i < MAC_NSTAT && !grp->lg_closing; i++) { 880 stat = i + MAC_STAT_MIN; 881 if (!MAC_STAT_ISACOUNTER(stat)) 882 continue; 883 val = aggr_port_stat(port, stat); 884 val -= port->lp_stat[i]; 885 grp->lg_stat[i] += val; 886 } 887 for (i = 0; i < ETHER_NSTAT && !grp->lg_closing; i++) { 888 stat = i + MACTYPE_STAT_MIN; 889 if (!ETHER_STAT_ISACOUNTER(stat)) 890 continue; 891 val = aggr_port_stat(port, stat); 892 val -= port->lp_ether_stat[i]; 893 grp->lg_ether_stat[i] += val; 894 } 895 896 grp->lg_nports--; 897 898 rw_exit(&port->lp_lock); 899 900 aggr_port_delete(port); 901 902 /* 903 * If the group MAC address has changed, update the MAC address of 904 * the remaining consistuent ports according to the new MAC 905 * address of the group. 906 */ 907 if (mac_addr_changed) 908 link_state_changed = link_state_changed || 909 aggr_grp_update_ports_mac(grp); 910 911 done: 912 if (mac_addr_changedp != NULL) 913 *mac_addr_changedp = mac_addr_changed; 914 if (link_state_changedp != NULL) 915 *link_state_changedp = link_state_changed; 916 917 return (rc); 918 } 919 920 /* 921 * Remove one or more ports from an existing link aggregation group. 922 */ 923 int 924 aggr_grp_rem_ports(uint32_t key, uint_t nports, laioc_port_t *ports) 925 { 926 int rc = 0, i; 927 aggr_grp_t *grp = NULL; 928 aggr_port_t *port; 929 boolean_t mac_addr_update = B_FALSE, mac_addr_changed; 930 boolean_t link_state_update = B_FALSE, link_state_changed; 931 932 /* get group corresponding to key */ 933 rw_enter(&aggr_grp_lock, RW_READER); 934 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 935 (mod_hash_val_t *)&grp) != 0) { 936 rw_exit(&aggr_grp_lock); 937 return (ENOENT); 938 } 939 AGGR_GRP_REFHOLD(grp); 940 rw_exit(&aggr_grp_lock); 941 942 AGGR_LACP_LOCK(grp); 943 rw_enter(&grp->lg_lock, RW_WRITER); 944 945 /* we need to keep at least one port per group */ 946 if (nports >= grp->lg_nports) { 947 rc = EINVAL; 948 goto bail; 949 } 950 951 /* first verify that all the groups are valid */ 952 for (i = 0; i < nports; i++) { 953 if (aggr_grp_port_lookup(grp, ports[i].lp_devname) == NULL) { 954 /* port not found */ 955 rc = ENOENT; 956 goto bail; 957 } 958 } 959 960 /* remove the specified ports from group */ 961 for (i = 0; i < nports && !grp->lg_closing; i++) { 962 /* lookup port */ 963 port = aggr_grp_port_lookup(grp, ports[i].lp_devname); 964 ASSERT(port != NULL); 965 966 /* stop port if group has already been started */ 967 if (grp->lg_started) { 968 rw_enter(&port->lp_lock, RW_WRITER); 969 aggr_port_stop(port); 970 rw_exit(&port->lp_lock); 971 } 972 973 /* remove port from group */ 974 rc = aggr_grp_rem_port(grp, port, &mac_addr_changed, 975 &link_state_changed); 976 ASSERT(rc == 0); 977 mac_addr_update = mac_addr_update || mac_addr_changed; 978 link_state_update = link_state_update || link_state_changed; 979 } 980 981 bail: 982 rw_exit(&grp->lg_lock); 983 AGGR_LACP_UNLOCK(grp); 984 if (!grp->lg_closing) { 985 if (mac_addr_update) 986 mac_unicst_update(grp->lg_mh, grp->lg_addr); 987 if (link_state_update) 988 mac_link_update(grp->lg_mh, grp->lg_link_state); 989 if (rc == 0) 990 mac_resource_update(grp->lg_mh); 991 } 992 AGGR_GRP_REFRELE(grp); 993 994 return (rc); 995 } 996 997 int 998 aggr_grp_delete(uint32_t key) 999 { 1000 aggr_grp_t *grp = NULL; 1001 aggr_port_t *port, *cport; 1002 mod_hash_val_t val; 1003 1004 rw_enter(&aggr_grp_lock, RW_WRITER); 1005 1006 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 1007 (mod_hash_val_t *)&grp) != 0) { 1008 rw_exit(&aggr_grp_lock); 1009 return (ENOENT); 1010 } 1011 1012 AGGR_LACP_LOCK(grp); 1013 rw_enter(&grp->lg_lock, RW_WRITER); 1014 1015 grp->lg_closing = B_TRUE; 1016 1017 /* 1018 * Unregister from the MAC service module. Since this can 1019 * fail if a client hasn't closed the MAC port, we gracefully 1020 * fail the operation. 1021 */ 1022 if (mac_unregister(grp->lg_mh)) { 1023 grp->lg_closing = B_FALSE; 1024 rw_exit(&grp->lg_lock); 1025 AGGR_LACP_UNLOCK(grp); 1026 rw_exit(&aggr_grp_lock); 1027 return (EBUSY); 1028 } 1029 1030 /* detach and free MAC ports associated with group */ 1031 port = grp->lg_ports; 1032 while (port != NULL) { 1033 cport = port->lp_next; 1034 rw_enter(&port->lp_lock, RW_WRITER); 1035 if (grp->lg_started) 1036 aggr_port_stop(port); 1037 (void) aggr_grp_detach_port(grp, port); 1038 rw_exit(&port->lp_lock); 1039 aggr_port_delete(port); 1040 port = cport; 1041 } 1042 1043 rw_exit(&grp->lg_lock); 1044 AGGR_LACP_UNLOCK(grp); 1045 1046 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(key), &val); 1047 ASSERT(grp == (aggr_grp_t *)val); 1048 1049 ASSERT(aggr_grp_cnt > 0); 1050 aggr_grp_cnt--; 1051 1052 rw_exit(&aggr_grp_lock); 1053 AGGR_GRP_REFRELE(grp); 1054 1055 return (0); 1056 } 1057 1058 void 1059 aggr_grp_free(aggr_grp_t *grp) 1060 { 1061 ASSERT(grp->lg_refs == 0); 1062 kmem_cache_free(aggr_grp_cache, grp); 1063 } 1064 1065 /* 1066 * Walker invoked when building the list of configured groups and 1067 * their ports that must be passed up to user-space. 1068 */ 1069 1070 /*ARGSUSED*/ 1071 static uint_t 1072 aggr_grp_info_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1073 { 1074 aggr_grp_t *grp; 1075 aggr_port_t *port; 1076 aggr_grp_info_state_t *state = arg; 1077 1078 if (state->ls_rc != 0) 1079 return (MH_WALK_TERMINATE); /* terminate walk */ 1080 1081 grp = (aggr_grp_t *)val; 1082 1083 rw_enter(&grp->lg_lock, RW_READER); 1084 1085 if (state->ls_group_key != 0 && grp->lg_key != state->ls_group_key) 1086 goto bail; 1087 1088 state->ls_group_found = B_TRUE; 1089 1090 state->ls_rc = state->ls_new_grp_fn(state->ls_fn_arg, grp->lg_key, 1091 grp->lg_addr, grp->lg_addr_fixed, grp->lg_tx_policy, 1092 grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer); 1093 1094 if (state->ls_rc != 0) 1095 goto bail; 1096 1097 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1098 1099 rw_enter(&port->lp_lock, RW_READER); 1100 1101 state->ls_rc = state->ls_new_port_fn(state->ls_fn_arg, 1102 port->lp_devname, port->lp_addr, port->lp_state, 1103 &port->lp_lacp.ActorOperPortState); 1104 1105 rw_exit(&port->lp_lock); 1106 1107 if (state->ls_rc != 0) 1108 goto bail; 1109 } 1110 1111 bail: 1112 rw_exit(&grp->lg_lock); 1113 return ((state->ls_rc == 0) ? MH_WALK_CONTINUE : MH_WALK_TERMINATE); 1114 } 1115 1116 int 1117 aggr_grp_info(uint_t *ngroups, uint32_t group_key, void *fn_arg, 1118 aggr_grp_info_new_grp_fn_t new_grp_fn, 1119 aggr_grp_info_new_port_fn_t new_port_fn) 1120 { 1121 aggr_grp_info_state_t state; 1122 int rc = 0; 1123 1124 rw_enter(&aggr_grp_lock, RW_READER); 1125 1126 *ngroups = aggr_grp_cnt; 1127 1128 bzero(&state, sizeof (state)); 1129 state.ls_group_key = group_key; 1130 state.ls_new_grp_fn = new_grp_fn; 1131 state.ls_new_port_fn = new_port_fn; 1132 state.ls_fn_arg = fn_arg; 1133 1134 mod_hash_walk(aggr_grp_hash, aggr_grp_info_walker, &state); 1135 1136 if ((rc = state.ls_rc) == 0 && group_key != 0 && 1137 !state.ls_group_found) 1138 rc = ENOENT; 1139 1140 rw_exit(&aggr_grp_lock); 1141 return (rc); 1142 } 1143 1144 static void 1145 aggr_m_resources(void *arg) 1146 { 1147 aggr_grp_t *grp = arg; 1148 aggr_port_t *port; 1149 1150 /* Call each port's m_resources function */ 1151 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 1152 mac_resources(port->lp_mh); 1153 } 1154 1155 /*ARGSUSED*/ 1156 static void 1157 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1158 { 1159 miocnak(q, mp, 0, ENOTSUP); 1160 } 1161 1162 static int 1163 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val) 1164 { 1165 aggr_port_t *port; 1166 uint_t stat_index; 1167 1168 /* We only aggregate counter statistics. */ 1169 if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) || 1170 IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) { 1171 return (ENOTSUP); 1172 } 1173 1174 /* 1175 * Counter statistics for a group are computed by aggregating the 1176 * counters of the members MACs while they were aggregated, plus 1177 * the residual counter of the group itself, which is updated each 1178 * time a MAC is removed from the group. 1179 */ 1180 *val = 0; 1181 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1182 /* actual port statistic */ 1183 *val += aggr_port_stat(port, stat); 1184 /* 1185 * minus the port stat when it was added, plus any residual 1186 * ammount for the group. 1187 */ 1188 if (IS_MAC_STAT(stat)) { 1189 stat_index = stat - MAC_STAT_MIN; 1190 *val -= port->lp_stat[stat_index]; 1191 *val += grp->lg_stat[stat_index]; 1192 } else if (IS_MACTYPE_STAT(stat)) { 1193 stat_index = stat - MACTYPE_STAT_MIN; 1194 *val -= port->lp_ether_stat[stat_index]; 1195 *val += grp->lg_ether_stat[stat_index]; 1196 } 1197 } 1198 return (0); 1199 } 1200 1201 static int 1202 aggr_m_stat(void *arg, uint_t stat, uint64_t *val) 1203 { 1204 aggr_grp_t *grp = arg; 1205 int rval = 0; 1206 1207 rw_enter(&grp->lg_lock, RW_READER); 1208 1209 switch (stat) { 1210 case MAC_STAT_IFSPEED: 1211 *val = grp->lg_ifspeed; 1212 break; 1213 1214 case ETHER_STAT_LINK_DUPLEX: 1215 *val = grp->lg_link_duplex; 1216 break; 1217 1218 default: 1219 /* 1220 * For all other statistics, we return the aggregated stat 1221 * from the underlying ports. aggr_grp_stat() will set 1222 * rval appropriately if the statistic isn't a counter. 1223 */ 1224 rval = aggr_grp_stat(grp, stat, val); 1225 } 1226 1227 rw_exit(&grp->lg_lock); 1228 return (rval); 1229 } 1230 1231 static int 1232 aggr_m_start(void *arg) 1233 { 1234 aggr_grp_t *grp = arg; 1235 aggr_port_t *port; 1236 1237 AGGR_LACP_LOCK(grp); 1238 rw_enter(&grp->lg_lock, RW_WRITER); 1239 1240 /* 1241 * Attempts to start all configured members of the group. 1242 * Group members will be attached when their link-up notification 1243 * is received. 1244 */ 1245 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1246 rw_enter(&port->lp_lock, RW_WRITER); 1247 if (aggr_port_start(port) != 0) { 1248 rw_exit(&port->lp_lock); 1249 continue; 1250 } 1251 1252 /* set port promiscuous mode */ 1253 if (aggr_port_promisc(port, grp->lg_promisc) != 0) 1254 aggr_port_stop(port); 1255 rw_exit(&port->lp_lock); 1256 } 1257 1258 grp->lg_started = B_TRUE; 1259 1260 rw_exit(&grp->lg_lock); 1261 AGGR_LACP_UNLOCK(grp); 1262 1263 return (0); 1264 } 1265 1266 static void 1267 aggr_m_stop(void *arg) 1268 { 1269 aggr_grp_t *grp = arg; 1270 aggr_port_t *port; 1271 1272 rw_enter(&grp->lg_lock, RW_WRITER); 1273 1274 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1275 rw_enter(&port->lp_lock, RW_WRITER); 1276 aggr_port_stop(port); 1277 rw_exit(&port->lp_lock); 1278 } 1279 1280 grp->lg_started = B_FALSE; 1281 1282 rw_exit(&grp->lg_lock); 1283 } 1284 1285 static int 1286 aggr_m_promisc(void *arg, boolean_t on) 1287 { 1288 aggr_grp_t *grp = arg; 1289 aggr_port_t *port; 1290 boolean_t link_state_changed = B_FALSE; 1291 1292 AGGR_LACP_LOCK(grp); 1293 rw_enter(&grp->lg_lock, RW_WRITER); 1294 AGGR_GRP_REFHOLD(grp); 1295 1296 ASSERT(!grp->lg_closing); 1297 1298 if (on == grp->lg_promisc) 1299 goto bail; 1300 1301 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1302 rw_enter(&port->lp_lock, RW_WRITER); 1303 AGGR_PORT_REFHOLD(port); 1304 if (port->lp_started) { 1305 if (aggr_port_promisc(port, on) != 0) { 1306 link_state_changed = link_state_changed || 1307 aggr_grp_detach_port(grp, port); 1308 } else { 1309 /* 1310 * If a port was detached because of a previous 1311 * failure changing the promiscuity, the port 1312 * is reattached when it successfully changes 1313 * the promiscuity now, and this might cause 1314 * the link state of the aggregation to change. 1315 */ 1316 link_state_changed = link_state_changed || 1317 aggr_grp_attach_port(grp, port); 1318 } 1319 } 1320 rw_exit(&port->lp_lock); 1321 AGGR_PORT_REFRELE(port); 1322 } 1323 1324 grp->lg_promisc = on; 1325 1326 if (link_state_changed) 1327 mac_link_update(grp->lg_mh, grp->lg_link_state); 1328 1329 bail: 1330 rw_exit(&grp->lg_lock); 1331 AGGR_LACP_UNLOCK(grp); 1332 AGGR_GRP_REFRELE(grp); 1333 1334 return (0); 1335 } 1336 1337 /* 1338 * Initialize the capabilities that are advertised for the group 1339 * according to the capabilities of the constituent ports. 1340 */ 1341 static boolean_t 1342 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 1343 { 1344 aggr_grp_t *grp = arg; 1345 1346 switch (cap) { 1347 case MAC_CAPAB_HCKSUM: { 1348 uint32_t *hcksum_txflags = cap_data; 1349 *hcksum_txflags = grp->lg_hcksum_txflags; 1350 break; 1351 } 1352 case MAC_CAPAB_POLL: 1353 /* 1354 * There's nothing for us to fill in, we simply return 1355 * B_TRUE or B_FALSE to represent the group's support 1356 * status for this capability. 1357 */ 1358 return (grp->lg_gldv3_polling); 1359 default: 1360 return (B_FALSE); 1361 } 1362 return (B_TRUE); 1363 } 1364 1365 /* 1366 * Add or remove the multicast addresses that are defined for the group 1367 * to or from the specified port. 1368 * This function is called before stopping a port, before a port 1369 * is detached from a group, and when attaching a port to a group. 1370 */ 1371 void 1372 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add) 1373 { 1374 aggr_grp_t *grp = port->lp_grp; 1375 1376 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 1377 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 1378 1379 if (!port->lp_started) 1380 return; 1381 1382 mac_multicst_refresh(grp->lg_mh, aggr_port_multicst, port, add); 1383 } 1384 1385 static int 1386 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 1387 { 1388 aggr_grp_t *grp = arg; 1389 aggr_port_t *port = NULL; 1390 int err = 0, cerr; 1391 1392 rw_enter(&grp->lg_lock, RW_WRITER); 1393 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1394 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 1395 continue; 1396 cerr = aggr_port_multicst(port, add, addrp); 1397 if (cerr != 0 && err == 0) 1398 err = cerr; 1399 } 1400 rw_exit(&grp->lg_lock); 1401 return (err); 1402 } 1403 1404 static int 1405 aggr_m_unicst(void *arg, const uint8_t *macaddr) 1406 { 1407 aggr_grp_t *grp = arg; 1408 int rc; 1409 1410 AGGR_LACP_LOCK(grp); 1411 rw_enter(&grp->lg_lock, RW_WRITER); 1412 rc = aggr_grp_modify(0, grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr, 1413 0, 0); 1414 rw_exit(&grp->lg_lock); 1415 AGGR_LACP_UNLOCK(grp); 1416 1417 return (rc); 1418 } 1419 1420 /* 1421 * Initialize the capabilities that are advertised for the group 1422 * according to the capabilities of the constituent ports. 1423 */ 1424 static void 1425 aggr_grp_capab_set(aggr_grp_t *grp) 1426 { 1427 uint32_t cksum; 1428 aggr_port_t *port; 1429 1430 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 1431 ASSERT(grp->lg_ports != NULL); 1432 1433 grp->lg_hcksum_txflags = (uint32_t)-1; 1434 grp->lg_gldv3_polling = B_TRUE; 1435 1436 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1437 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum)) 1438 cksum = 0; 1439 grp->lg_hcksum_txflags &= cksum; 1440 1441 grp->lg_gldv3_polling &= 1442 mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL); 1443 } 1444 } 1445 1446 1447 /* 1448 * Checks whether the capabilities of the port being added are compatible 1449 * with the current capabilities of the aggregation. 1450 */ 1451 static boolean_t 1452 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port) 1453 { 1454 uint32_t hcksum_txflags; 1455 1456 ASSERT(grp->lg_ports != NULL); 1457 1458 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) { 1459 if (grp->lg_hcksum_txflags != 0) 1460 return (B_FALSE); 1461 } else if ((hcksum_txflags & grp->lg_hcksum_txflags) != 1462 grp->lg_hcksum_txflags) { 1463 return (B_FALSE); 1464 } 1465 1466 if (mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL) != 1467 grp->lg_gldv3_polling) { 1468 return (B_FALSE); 1469 } 1470 1471 return (B_TRUE); 1472 } 1473