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 1011 rw_enter(&aggr_grp_lock, RW_WRITER); 1012 1013 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 1014 (mod_hash_val_t *)&grp) != 0) { 1015 rw_exit(&aggr_grp_lock); 1016 return (ENOENT); 1017 } 1018 1019 AGGR_LACP_LOCK(grp); 1020 rw_enter(&grp->lg_lock, RW_WRITER); 1021 1022 grp->lg_closing = B_TRUE; 1023 1024 /* 1025 * Unregister from the MAC service module. Since this can 1026 * fail if a client hasn't closed the MAC port, we gracefully 1027 * fail the operation. 1028 */ 1029 if (mac_unregister(grp->lg_mh)) { 1030 grp->lg_closing = B_FALSE; 1031 rw_exit(&grp->lg_lock); 1032 AGGR_LACP_UNLOCK(grp); 1033 rw_exit(&aggr_grp_lock); 1034 return (EBUSY); 1035 } 1036 1037 /* detach and free MAC ports associated with group */ 1038 port = grp->lg_ports; 1039 while (port != NULL) { 1040 cport = port->lp_next; 1041 rw_enter(&port->lp_lock, RW_WRITER); 1042 if (grp->lg_started) 1043 aggr_port_stop(port); 1044 (void) aggr_grp_detach_port(grp, port); 1045 rw_exit(&port->lp_lock); 1046 aggr_port_delete(port); 1047 port = cport; 1048 } 1049 1050 rw_exit(&grp->lg_lock); 1051 AGGR_LACP_UNLOCK(grp); 1052 1053 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(key), &val); 1054 ASSERT(grp == (aggr_grp_t *)val); 1055 1056 ASSERT(aggr_grp_cnt > 0); 1057 aggr_grp_cnt--; 1058 1059 rw_exit(&aggr_grp_lock); 1060 AGGR_GRP_REFRELE(grp); 1061 1062 return (0); 1063 } 1064 1065 void 1066 aggr_grp_free(aggr_grp_t *grp) 1067 { 1068 ASSERT(grp->lg_refs == 0); 1069 kmem_cache_free(aggr_grp_cache, grp); 1070 } 1071 1072 /* 1073 * Walker invoked when building the list of configured groups and 1074 * their ports that must be passed up to user-space. 1075 */ 1076 1077 /*ARGSUSED*/ 1078 static uint_t 1079 aggr_grp_info_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1080 { 1081 aggr_grp_t *grp; 1082 aggr_port_t *port; 1083 aggr_grp_info_state_t *state = arg; 1084 1085 if (state->ls_rc != 0) 1086 return (MH_WALK_TERMINATE); /* terminate walk */ 1087 1088 grp = (aggr_grp_t *)val; 1089 1090 rw_enter(&grp->lg_lock, RW_READER); 1091 1092 if (state->ls_group_key != 0 && grp->lg_key != state->ls_group_key) 1093 goto bail; 1094 1095 state->ls_group_found = B_TRUE; 1096 1097 state->ls_rc = state->ls_new_grp_fn(state->ls_fn_arg, grp->lg_key, 1098 grp->lg_addr, grp->lg_addr_fixed, grp->lg_tx_policy, 1099 grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer); 1100 1101 if (state->ls_rc != 0) 1102 goto bail; 1103 1104 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1105 1106 rw_enter(&port->lp_lock, RW_READER); 1107 1108 state->ls_rc = state->ls_new_port_fn(state->ls_fn_arg, 1109 port->lp_devname, port->lp_addr, port->lp_state, 1110 &port->lp_lacp.ActorOperPortState); 1111 1112 rw_exit(&port->lp_lock); 1113 1114 if (state->ls_rc != 0) 1115 goto bail; 1116 } 1117 1118 bail: 1119 rw_exit(&grp->lg_lock); 1120 return ((state->ls_rc == 0) ? MH_WALK_CONTINUE : MH_WALK_TERMINATE); 1121 } 1122 1123 int 1124 aggr_grp_info(uint_t *ngroups, uint32_t group_key, void *fn_arg, 1125 aggr_grp_info_new_grp_fn_t new_grp_fn, 1126 aggr_grp_info_new_port_fn_t new_port_fn) 1127 { 1128 aggr_grp_info_state_t state; 1129 int rc = 0; 1130 1131 rw_enter(&aggr_grp_lock, RW_READER); 1132 1133 *ngroups = aggr_grp_cnt; 1134 1135 bzero(&state, sizeof (state)); 1136 state.ls_group_key = group_key; 1137 state.ls_new_grp_fn = new_grp_fn; 1138 state.ls_new_port_fn = new_port_fn; 1139 state.ls_fn_arg = fn_arg; 1140 1141 mod_hash_walk(aggr_grp_hash, aggr_grp_info_walker, &state); 1142 1143 if ((rc = state.ls_rc) == 0 && group_key != 0 && 1144 !state.ls_group_found) 1145 rc = ENOENT; 1146 1147 rw_exit(&aggr_grp_lock); 1148 return (rc); 1149 } 1150 1151 static void 1152 aggr_m_resources(void *arg) 1153 { 1154 aggr_grp_t *grp = arg; 1155 aggr_port_t *port; 1156 1157 /* Call each port's m_resources function */ 1158 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 1159 mac_resources(port->lp_mh); 1160 } 1161 1162 /*ARGSUSED*/ 1163 static void 1164 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1165 { 1166 miocnak(q, mp, 0, ENOTSUP); 1167 } 1168 1169 static int 1170 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val) 1171 { 1172 aggr_port_t *port; 1173 uint_t stat_index; 1174 1175 /* We only aggregate counter statistics. */ 1176 if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) || 1177 IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) { 1178 return (ENOTSUP); 1179 } 1180 1181 /* 1182 * Counter statistics for a group are computed by aggregating the 1183 * counters of the members MACs while they were aggregated, plus 1184 * the residual counter of the group itself, which is updated each 1185 * time a MAC is removed from the group. 1186 */ 1187 *val = 0; 1188 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1189 /* actual port statistic */ 1190 *val += aggr_port_stat(port, stat); 1191 /* 1192 * minus the port stat when it was added, plus any residual 1193 * ammount for the group. 1194 */ 1195 if (IS_MAC_STAT(stat)) { 1196 stat_index = stat - MAC_STAT_MIN; 1197 *val -= port->lp_stat[stat_index]; 1198 *val += grp->lg_stat[stat_index]; 1199 } else if (IS_MACTYPE_STAT(stat)) { 1200 stat_index = stat - MACTYPE_STAT_MIN; 1201 *val -= port->lp_ether_stat[stat_index]; 1202 *val += grp->lg_ether_stat[stat_index]; 1203 } 1204 } 1205 return (0); 1206 } 1207 1208 static int 1209 aggr_m_stat(void *arg, uint_t stat, uint64_t *val) 1210 { 1211 aggr_grp_t *grp = arg; 1212 int rval = 0; 1213 1214 rw_enter(&grp->lg_lock, RW_READER); 1215 1216 switch (stat) { 1217 case MAC_STAT_IFSPEED: 1218 *val = grp->lg_ifspeed; 1219 break; 1220 1221 case ETHER_STAT_LINK_DUPLEX: 1222 *val = grp->lg_link_duplex; 1223 break; 1224 1225 default: 1226 /* 1227 * For all other statistics, we return the aggregated stat 1228 * from the underlying ports. aggr_grp_stat() will set 1229 * rval appropriately if the statistic isn't a counter. 1230 */ 1231 rval = aggr_grp_stat(grp, stat, val); 1232 } 1233 1234 rw_exit(&grp->lg_lock); 1235 return (rval); 1236 } 1237 1238 static int 1239 aggr_m_start(void *arg) 1240 { 1241 aggr_grp_t *grp = arg; 1242 aggr_port_t *port; 1243 1244 AGGR_LACP_LOCK(grp); 1245 rw_enter(&grp->lg_lock, RW_WRITER); 1246 1247 /* 1248 * Attempts to start all configured members of the group. 1249 * Group members will be attached when their link-up notification 1250 * is received. 1251 */ 1252 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1253 rw_enter(&port->lp_lock, RW_WRITER); 1254 if (aggr_port_start(port) != 0) { 1255 rw_exit(&port->lp_lock); 1256 continue; 1257 } 1258 1259 /* set port promiscuous mode */ 1260 if (aggr_port_promisc(port, grp->lg_promisc) != 0) 1261 aggr_port_stop(port); 1262 rw_exit(&port->lp_lock); 1263 } 1264 1265 grp->lg_started = B_TRUE; 1266 1267 rw_exit(&grp->lg_lock); 1268 AGGR_LACP_UNLOCK(grp); 1269 1270 return (0); 1271 } 1272 1273 static void 1274 aggr_m_stop(void *arg) 1275 { 1276 aggr_grp_t *grp = arg; 1277 aggr_port_t *port; 1278 1279 rw_enter(&grp->lg_lock, RW_WRITER); 1280 1281 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1282 rw_enter(&port->lp_lock, RW_WRITER); 1283 aggr_port_stop(port); 1284 rw_exit(&port->lp_lock); 1285 } 1286 1287 grp->lg_started = B_FALSE; 1288 1289 rw_exit(&grp->lg_lock); 1290 } 1291 1292 static int 1293 aggr_m_promisc(void *arg, boolean_t on) 1294 { 1295 aggr_grp_t *grp = arg; 1296 aggr_port_t *port; 1297 boolean_t link_state_changed = B_FALSE; 1298 1299 AGGR_LACP_LOCK(grp); 1300 rw_enter(&grp->lg_lock, RW_WRITER); 1301 AGGR_GRP_REFHOLD(grp); 1302 1303 ASSERT(!grp->lg_closing); 1304 1305 if (on == grp->lg_promisc) 1306 goto bail; 1307 1308 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1309 rw_enter(&port->lp_lock, RW_WRITER); 1310 AGGR_PORT_REFHOLD(port); 1311 if (port->lp_started) { 1312 if (aggr_port_promisc(port, on) != 0) { 1313 if (aggr_grp_detach_port(grp, port)) 1314 link_state_changed = B_TRUE; 1315 } else { 1316 /* 1317 * If a port was detached because of a previous 1318 * failure changing the promiscuity, the port 1319 * is reattached when it successfully changes 1320 * the promiscuity now, and this might cause 1321 * the link state of the aggregation to change. 1322 */ 1323 if (aggr_grp_attach_port(grp, port)) 1324 link_state_changed = B_TRUE; 1325 } 1326 } 1327 rw_exit(&port->lp_lock); 1328 AGGR_PORT_REFRELE(port); 1329 } 1330 1331 grp->lg_promisc = on; 1332 1333 if (link_state_changed) 1334 mac_link_update(grp->lg_mh, grp->lg_link_state); 1335 1336 bail: 1337 rw_exit(&grp->lg_lock); 1338 AGGR_LACP_UNLOCK(grp); 1339 AGGR_GRP_REFRELE(grp); 1340 1341 return (0); 1342 } 1343 1344 /* 1345 * Initialize the capabilities that are advertised for the group 1346 * according to the capabilities of the constituent ports. 1347 */ 1348 static boolean_t 1349 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 1350 { 1351 aggr_grp_t *grp = arg; 1352 1353 switch (cap) { 1354 case MAC_CAPAB_HCKSUM: { 1355 uint32_t *hcksum_txflags = cap_data; 1356 *hcksum_txflags = grp->lg_hcksum_txflags; 1357 break; 1358 } 1359 case MAC_CAPAB_POLL: 1360 /* 1361 * There's nothing for us to fill in, we simply return 1362 * B_TRUE or B_FALSE to represent the group's support 1363 * status for this capability. 1364 */ 1365 return (grp->lg_gldv3_polling); 1366 default: 1367 return (B_FALSE); 1368 } 1369 return (B_TRUE); 1370 } 1371 1372 /* 1373 * Add or remove the multicast addresses that are defined for the group 1374 * to or from the specified port. 1375 * This function is called before stopping a port, before a port 1376 * is detached from a group, and when attaching a port to a group. 1377 */ 1378 void 1379 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add) 1380 { 1381 aggr_grp_t *grp = port->lp_grp; 1382 1383 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 1384 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 1385 1386 if (!port->lp_started) 1387 return; 1388 1389 mac_multicst_refresh(grp->lg_mh, aggr_port_multicst, port, add); 1390 } 1391 1392 static int 1393 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 1394 { 1395 aggr_grp_t *grp = arg; 1396 aggr_port_t *port = NULL; 1397 int err = 0, cerr; 1398 1399 rw_enter(&grp->lg_lock, RW_WRITER); 1400 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1401 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 1402 continue; 1403 cerr = aggr_port_multicst(port, add, addrp); 1404 if (cerr != 0 && err == 0) 1405 err = cerr; 1406 } 1407 rw_exit(&grp->lg_lock); 1408 return (err); 1409 } 1410 1411 static int 1412 aggr_m_unicst(void *arg, const uint8_t *macaddr) 1413 { 1414 aggr_grp_t *grp = arg; 1415 int rc; 1416 1417 AGGR_LACP_LOCK(grp); 1418 rw_enter(&grp->lg_lock, RW_WRITER); 1419 rc = aggr_grp_modify(0, grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr, 1420 0, 0); 1421 rw_exit(&grp->lg_lock); 1422 AGGR_LACP_UNLOCK(grp); 1423 1424 return (rc); 1425 } 1426 1427 /* 1428 * Initialize the capabilities that are advertised for the group 1429 * according to the capabilities of the constituent ports. 1430 */ 1431 static void 1432 aggr_grp_capab_set(aggr_grp_t *grp) 1433 { 1434 uint32_t cksum; 1435 aggr_port_t *port; 1436 1437 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 1438 ASSERT(grp->lg_ports != NULL); 1439 1440 grp->lg_hcksum_txflags = (uint32_t)-1; 1441 grp->lg_gldv3_polling = B_TRUE; 1442 1443 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1444 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum)) 1445 cksum = 0; 1446 grp->lg_hcksum_txflags &= cksum; 1447 1448 grp->lg_gldv3_polling &= 1449 mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL); 1450 } 1451 } 1452 1453 1454 /* 1455 * Checks whether the capabilities of the port being added are compatible 1456 * with the current capabilities of the aggregation. 1457 */ 1458 static boolean_t 1459 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port) 1460 { 1461 uint32_t hcksum_txflags; 1462 1463 ASSERT(grp->lg_ports != NULL); 1464 1465 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) { 1466 if (grp->lg_hcksum_txflags != 0) 1467 return (B_FALSE); 1468 } else if ((hcksum_txflags & grp->lg_hcksum_txflags) != 1469 grp->lg_hcksum_txflags) { 1470 return (B_FALSE); 1471 } 1472 1473 if (mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL) != 1474 grp->lg_gldv3_polling) { 1475 return (B_FALSE); 1476 } 1477 1478 return (B_TRUE); 1479 } 1480 1481 /* 1482 * Returns the maximum SDU according to the SDU of the constituent ports. 1483 */ 1484 static uint_t 1485 aggr_grp_max_sdu(aggr_grp_t *grp) 1486 { 1487 uint_t max_sdu = (uint_t)-1; 1488 aggr_port_t *port; 1489 1490 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 1491 ASSERT(grp->lg_ports != NULL); 1492 1493 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1494 const mac_info_t *port_mi = mac_info(port->lp_mh); 1495 if (max_sdu > port_mi->mi_sdu_max) 1496 max_sdu = port_mi->mi_sdu_max; 1497 } 1498 1499 return (max_sdu); 1500 } 1501 1502 /* 1503 * Checks if the maximum SDU of the specified port is compatible 1504 * with the maximum SDU of the specified aggregation group, returns 1505 * B_TRUE if it is, B_FALSE otherwise. 1506 */ 1507 static boolean_t 1508 aggr_grp_sdu_check(aggr_grp_t *grp, aggr_port_t *port) 1509 { 1510 const mac_info_t *port_mi = mac_info(port->lp_mh); 1511 1512 return (port_mi->mi_sdu_max >= grp->lg_max_sdu); 1513 } 1514