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