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 link_state_changed = link_state_changed || 333 aggr_grp_detach_port(grp, cport); 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 link_state_changed = link_state_changed || 343 aggr_grp_attach_port(grp, cport); 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 link_state_changed = link_state_changed || 521 aggr_port_notify_link(grp, port, B_FALSE); 522 } 523 524 /* update the MAC address of the constituent ports */ 525 link_state_changed = link_state_changed || 526 aggr_grp_update_ports_mac(grp); 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 (void) aggr_port_notify_link(grp, port, B_FALSE); 770 771 /* add new group to hash table */ 772 err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(key), 773 (mod_hash_val_t)grp); 774 ASSERT(err == 0); 775 aggr_grp_cnt++; 776 777 rw_exit(&grp->lg_lock); 778 AGGR_LACP_UNLOCK(grp); 779 rw_exit(&aggr_grp_lock); 780 return (0); 781 782 bail: 783 if (grp != NULL) { 784 aggr_port_t *cport; 785 786 grp->lg_closing = B_TRUE; 787 788 port = grp->lg_ports; 789 while (port != NULL) { 790 cport = port->lp_next; 791 aggr_port_delete(port); 792 port = cport; 793 } 794 795 rw_exit(&grp->lg_lock); 796 AGGR_LACP_UNLOCK(grp); 797 798 kmem_cache_free(aggr_grp_cache, grp); 799 } 800 801 rw_exit(&aggr_grp_lock); 802 return (err); 803 } 804 805 /* 806 * Return a pointer to the member of a group with specified device name 807 * and port number. 808 */ 809 static aggr_port_t * 810 aggr_grp_port_lookup(aggr_grp_t *grp, const char *devname) 811 { 812 aggr_port_t *port; 813 814 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 815 816 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 817 if (strcmp(port->lp_devname, devname) == 0) 818 break; 819 } 820 821 return (port); 822 } 823 824 /* 825 * Stop, detach and remove a port from a link aggregation group. 826 */ 827 static int 828 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port, 829 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 830 { 831 int rc = 0; 832 aggr_port_t **pport; 833 boolean_t mac_addr_changed = B_FALSE; 834 boolean_t link_state_changed = B_FALSE; 835 uint64_t val; 836 uint_t i; 837 uint_t stat; 838 839 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 840 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 841 ASSERT(grp->lg_nports > 1); 842 ASSERT(!grp->lg_closing); 843 844 /* unlink port */ 845 for (pport = &grp->lg_ports; *pport != port; 846 pport = &(*pport)->lp_next) { 847 if (*pport == NULL) { 848 rc = ENOENT; 849 goto done; 850 } 851 } 852 *pport = port->lp_next; 853 854 atomic_add_32(&port->lp_closing, 1); 855 856 rw_enter(&port->lp_lock, RW_WRITER); 857 858 /* 859 * If the MAC address of the port being removed was assigned 860 * to the group, update the group MAC address 861 * using the MAC address of a different port. 862 */ 863 if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) { 864 /* 865 * Set the MAC address of the group to the 866 * MAC address of its first port. 867 */ 868 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 869 grp->lg_mac_addr_port = grp->lg_ports; 870 mac_addr_changed = B_TRUE; 871 } 872 873 link_state_changed = aggr_grp_detach_port(grp, port); 874 875 /* 876 * Add the counter statistics of the ports while it was aggregated 877 * to the group's residual statistics. This is done by obtaining 878 * the current counter from the underlying MAC then subtracting the 879 * value of the counter at the moment it was added to the 880 * aggregation. 881 */ 882 for (i = 0; i < MAC_NSTAT && !grp->lg_closing; i++) { 883 stat = i + MAC_STAT_MIN; 884 if (!MAC_STAT_ISACOUNTER(stat)) 885 continue; 886 val = aggr_port_stat(port, stat); 887 val -= port->lp_stat[i]; 888 grp->lg_stat[i] += val; 889 } 890 for (i = 0; i < ETHER_NSTAT && !grp->lg_closing; i++) { 891 stat = i + MACTYPE_STAT_MIN; 892 if (!ETHER_STAT_ISACOUNTER(stat)) 893 continue; 894 val = aggr_port_stat(port, stat); 895 val -= port->lp_ether_stat[i]; 896 grp->lg_ether_stat[i] += val; 897 } 898 899 grp->lg_nports--; 900 901 rw_exit(&port->lp_lock); 902 903 aggr_port_delete(port); 904 905 /* 906 * If the group MAC address has changed, update the MAC address of 907 * the remaining consistuent ports according to the new MAC 908 * address of the group. 909 */ 910 if (mac_addr_changed) 911 link_state_changed = link_state_changed || 912 aggr_grp_update_ports_mac(grp); 913 914 done: 915 if (mac_addr_changedp != NULL) 916 *mac_addr_changedp = mac_addr_changed; 917 if (link_state_changedp != NULL) 918 *link_state_changedp = link_state_changed; 919 920 return (rc); 921 } 922 923 /* 924 * Remove one or more ports from an existing link aggregation group. 925 */ 926 int 927 aggr_grp_rem_ports(uint32_t key, uint_t nports, laioc_port_t *ports) 928 { 929 int rc = 0, i; 930 aggr_grp_t *grp = NULL; 931 aggr_port_t *port; 932 boolean_t mac_addr_update = B_FALSE, mac_addr_changed; 933 boolean_t link_state_update = B_FALSE, link_state_changed; 934 935 /* get group corresponding to key */ 936 rw_enter(&aggr_grp_lock, RW_READER); 937 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 938 (mod_hash_val_t *)&grp) != 0) { 939 rw_exit(&aggr_grp_lock); 940 return (ENOENT); 941 } 942 AGGR_GRP_REFHOLD(grp); 943 rw_exit(&aggr_grp_lock); 944 945 AGGR_LACP_LOCK(grp); 946 rw_enter(&grp->lg_lock, RW_WRITER); 947 948 /* we need to keep at least one port per group */ 949 if (nports >= grp->lg_nports) { 950 rc = EINVAL; 951 goto bail; 952 } 953 954 /* first verify that all the groups are valid */ 955 for (i = 0; i < nports; i++) { 956 if (aggr_grp_port_lookup(grp, ports[i].lp_devname) == NULL) { 957 /* port not found */ 958 rc = ENOENT; 959 goto bail; 960 } 961 } 962 963 /* remove the specified ports from group */ 964 for (i = 0; i < nports && !grp->lg_closing; i++) { 965 /* lookup port */ 966 port = aggr_grp_port_lookup(grp, ports[i].lp_devname); 967 ASSERT(port != NULL); 968 969 /* stop port if group has already been started */ 970 if (grp->lg_started) { 971 rw_enter(&port->lp_lock, RW_WRITER); 972 aggr_port_stop(port); 973 rw_exit(&port->lp_lock); 974 } 975 976 /* remove port from group */ 977 rc = aggr_grp_rem_port(grp, port, &mac_addr_changed, 978 &link_state_changed); 979 ASSERT(rc == 0); 980 mac_addr_update = mac_addr_update || mac_addr_changed; 981 link_state_update = link_state_update || link_state_changed; 982 } 983 984 bail: 985 rw_exit(&grp->lg_lock); 986 AGGR_LACP_UNLOCK(grp); 987 if (!grp->lg_closing) { 988 if (mac_addr_update) 989 mac_unicst_update(grp->lg_mh, grp->lg_addr); 990 if (link_state_update) 991 mac_link_update(grp->lg_mh, grp->lg_link_state); 992 if (rc == 0) 993 mac_resource_update(grp->lg_mh); 994 } 995 AGGR_GRP_REFRELE(grp); 996 997 return (rc); 998 } 999 1000 int 1001 aggr_grp_delete(uint32_t key) 1002 { 1003 aggr_grp_t *grp = NULL; 1004 aggr_port_t *port, *cport; 1005 mod_hash_val_t val; 1006 1007 rw_enter(&aggr_grp_lock, RW_WRITER); 1008 1009 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key), 1010 (mod_hash_val_t *)&grp) != 0) { 1011 rw_exit(&aggr_grp_lock); 1012 return (ENOENT); 1013 } 1014 1015 AGGR_LACP_LOCK(grp); 1016 rw_enter(&grp->lg_lock, RW_WRITER); 1017 1018 grp->lg_closing = B_TRUE; 1019 1020 /* 1021 * Unregister from the MAC service module. Since this can 1022 * fail if a client hasn't closed the MAC port, we gracefully 1023 * fail the operation. 1024 */ 1025 if (mac_unregister(grp->lg_mh)) { 1026 grp->lg_closing = B_FALSE; 1027 rw_exit(&grp->lg_lock); 1028 AGGR_LACP_UNLOCK(grp); 1029 rw_exit(&aggr_grp_lock); 1030 return (EBUSY); 1031 } 1032 1033 /* detach and free MAC ports associated with group */ 1034 port = grp->lg_ports; 1035 while (port != NULL) { 1036 cport = port->lp_next; 1037 rw_enter(&port->lp_lock, RW_WRITER); 1038 if (grp->lg_started) 1039 aggr_port_stop(port); 1040 (void) aggr_grp_detach_port(grp, port); 1041 rw_exit(&port->lp_lock); 1042 aggr_port_delete(port); 1043 port = cport; 1044 } 1045 1046 rw_exit(&grp->lg_lock); 1047 AGGR_LACP_UNLOCK(grp); 1048 1049 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(key), &val); 1050 ASSERT(grp == (aggr_grp_t *)val); 1051 1052 ASSERT(aggr_grp_cnt > 0); 1053 aggr_grp_cnt--; 1054 1055 rw_exit(&aggr_grp_lock); 1056 AGGR_GRP_REFRELE(grp); 1057 1058 return (0); 1059 } 1060 1061 void 1062 aggr_grp_free(aggr_grp_t *grp) 1063 { 1064 ASSERT(grp->lg_refs == 0); 1065 kmem_cache_free(aggr_grp_cache, grp); 1066 } 1067 1068 /* 1069 * Walker invoked when building the list of configured groups and 1070 * their ports that must be passed up to user-space. 1071 */ 1072 1073 /*ARGSUSED*/ 1074 static uint_t 1075 aggr_grp_info_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 1076 { 1077 aggr_grp_t *grp; 1078 aggr_port_t *port; 1079 aggr_grp_info_state_t *state = arg; 1080 1081 if (state->ls_rc != 0) 1082 return (MH_WALK_TERMINATE); /* terminate walk */ 1083 1084 grp = (aggr_grp_t *)val; 1085 1086 rw_enter(&grp->lg_lock, RW_READER); 1087 1088 if (state->ls_group_key != 0 && grp->lg_key != state->ls_group_key) 1089 goto bail; 1090 1091 state->ls_group_found = B_TRUE; 1092 1093 state->ls_rc = state->ls_new_grp_fn(state->ls_fn_arg, grp->lg_key, 1094 grp->lg_addr, grp->lg_addr_fixed, grp->lg_tx_policy, 1095 grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer); 1096 1097 if (state->ls_rc != 0) 1098 goto bail; 1099 1100 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1101 1102 rw_enter(&port->lp_lock, RW_READER); 1103 1104 state->ls_rc = state->ls_new_port_fn(state->ls_fn_arg, 1105 port->lp_devname, port->lp_addr, port->lp_state, 1106 &port->lp_lacp.ActorOperPortState); 1107 1108 rw_exit(&port->lp_lock); 1109 1110 if (state->ls_rc != 0) 1111 goto bail; 1112 } 1113 1114 bail: 1115 rw_exit(&grp->lg_lock); 1116 return ((state->ls_rc == 0) ? MH_WALK_CONTINUE : MH_WALK_TERMINATE); 1117 } 1118 1119 int 1120 aggr_grp_info(uint_t *ngroups, uint32_t group_key, void *fn_arg, 1121 aggr_grp_info_new_grp_fn_t new_grp_fn, 1122 aggr_grp_info_new_port_fn_t new_port_fn) 1123 { 1124 aggr_grp_info_state_t state; 1125 int rc = 0; 1126 1127 rw_enter(&aggr_grp_lock, RW_READER); 1128 1129 *ngroups = aggr_grp_cnt; 1130 1131 bzero(&state, sizeof (state)); 1132 state.ls_group_key = group_key; 1133 state.ls_new_grp_fn = new_grp_fn; 1134 state.ls_new_port_fn = new_port_fn; 1135 state.ls_fn_arg = fn_arg; 1136 1137 mod_hash_walk(aggr_grp_hash, aggr_grp_info_walker, &state); 1138 1139 if ((rc = state.ls_rc) == 0 && group_key != 0 && 1140 !state.ls_group_found) 1141 rc = ENOENT; 1142 1143 rw_exit(&aggr_grp_lock); 1144 return (rc); 1145 } 1146 1147 static void 1148 aggr_m_resources(void *arg) 1149 { 1150 aggr_grp_t *grp = arg; 1151 aggr_port_t *port; 1152 1153 /* Call each port's m_resources function */ 1154 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 1155 mac_resources(port->lp_mh); 1156 } 1157 1158 /*ARGSUSED*/ 1159 static void 1160 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1161 { 1162 miocnak(q, mp, 0, ENOTSUP); 1163 } 1164 1165 static int 1166 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val) 1167 { 1168 aggr_port_t *port; 1169 uint_t stat_index; 1170 1171 /* We only aggregate counter statistics. */ 1172 if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) || 1173 IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) { 1174 return (ENOTSUP); 1175 } 1176 1177 /* 1178 * Counter statistics for a group are computed by aggregating the 1179 * counters of the members MACs while they were aggregated, plus 1180 * the residual counter of the group itself, which is updated each 1181 * time a MAC is removed from the group. 1182 */ 1183 *val = 0; 1184 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1185 /* actual port statistic */ 1186 *val += aggr_port_stat(port, stat); 1187 /* 1188 * minus the port stat when it was added, plus any residual 1189 * ammount for the group. 1190 */ 1191 if (IS_MAC_STAT(stat)) { 1192 stat_index = stat - MAC_STAT_MIN; 1193 *val -= port->lp_stat[stat_index]; 1194 *val += grp->lg_stat[stat_index]; 1195 } else if (IS_MACTYPE_STAT(stat)) { 1196 stat_index = stat - MACTYPE_STAT_MIN; 1197 *val -= port->lp_ether_stat[stat_index]; 1198 *val += grp->lg_ether_stat[stat_index]; 1199 } 1200 } 1201 return (0); 1202 } 1203 1204 static int 1205 aggr_m_stat(void *arg, uint_t stat, uint64_t *val) 1206 { 1207 aggr_grp_t *grp = arg; 1208 int rval = 0; 1209 1210 rw_enter(&grp->lg_lock, RW_READER); 1211 1212 switch (stat) { 1213 case MAC_STAT_IFSPEED: 1214 *val = grp->lg_ifspeed; 1215 break; 1216 1217 case ETHER_STAT_LINK_DUPLEX: 1218 *val = grp->lg_link_duplex; 1219 break; 1220 1221 default: 1222 /* 1223 * For all other statistics, we return the aggregated stat 1224 * from the underlying ports. aggr_grp_stat() will set 1225 * rval appropriately if the statistic isn't a counter. 1226 */ 1227 rval = aggr_grp_stat(grp, stat, val); 1228 } 1229 1230 rw_exit(&grp->lg_lock); 1231 return (rval); 1232 } 1233 1234 static int 1235 aggr_m_start(void *arg) 1236 { 1237 aggr_grp_t *grp = arg; 1238 aggr_port_t *port; 1239 1240 AGGR_LACP_LOCK(grp); 1241 rw_enter(&grp->lg_lock, RW_WRITER); 1242 1243 /* 1244 * Attempts to start all configured members of the group. 1245 * Group members will be attached when their link-up notification 1246 * is received. 1247 */ 1248 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1249 rw_enter(&port->lp_lock, RW_WRITER); 1250 if (aggr_port_start(port) != 0) { 1251 rw_exit(&port->lp_lock); 1252 continue; 1253 } 1254 1255 /* set port promiscuous mode */ 1256 if (aggr_port_promisc(port, grp->lg_promisc) != 0) 1257 aggr_port_stop(port); 1258 rw_exit(&port->lp_lock); 1259 } 1260 1261 grp->lg_started = B_TRUE; 1262 1263 rw_exit(&grp->lg_lock); 1264 AGGR_LACP_UNLOCK(grp); 1265 1266 return (0); 1267 } 1268 1269 static void 1270 aggr_m_stop(void *arg) 1271 { 1272 aggr_grp_t *grp = arg; 1273 aggr_port_t *port; 1274 1275 rw_enter(&grp->lg_lock, RW_WRITER); 1276 1277 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1278 rw_enter(&port->lp_lock, RW_WRITER); 1279 aggr_port_stop(port); 1280 rw_exit(&port->lp_lock); 1281 } 1282 1283 grp->lg_started = B_FALSE; 1284 1285 rw_exit(&grp->lg_lock); 1286 } 1287 1288 static int 1289 aggr_m_promisc(void *arg, boolean_t on) 1290 { 1291 aggr_grp_t *grp = arg; 1292 aggr_port_t *port; 1293 boolean_t link_state_changed = B_FALSE; 1294 1295 AGGR_LACP_LOCK(grp); 1296 rw_enter(&grp->lg_lock, RW_WRITER); 1297 AGGR_GRP_REFHOLD(grp); 1298 1299 ASSERT(!grp->lg_closing); 1300 1301 if (on == grp->lg_promisc) 1302 goto bail; 1303 1304 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1305 rw_enter(&port->lp_lock, RW_WRITER); 1306 AGGR_PORT_REFHOLD(port); 1307 if (port->lp_started) { 1308 if (aggr_port_promisc(port, on) != 0) { 1309 link_state_changed = link_state_changed || 1310 aggr_grp_detach_port(grp, port); 1311 } else { 1312 /* 1313 * If a port was detached because of a previous 1314 * failure changing the promiscuity, the port 1315 * is reattached when it successfully changes 1316 * the promiscuity now, and this might cause 1317 * the link state of the aggregation to change. 1318 */ 1319 link_state_changed = link_state_changed || 1320 aggr_grp_attach_port(grp, port); 1321 } 1322 } 1323 rw_exit(&port->lp_lock); 1324 AGGR_PORT_REFRELE(port); 1325 } 1326 1327 grp->lg_promisc = on; 1328 1329 if (link_state_changed) 1330 mac_link_update(grp->lg_mh, grp->lg_link_state); 1331 1332 bail: 1333 rw_exit(&grp->lg_lock); 1334 AGGR_LACP_UNLOCK(grp); 1335 AGGR_GRP_REFRELE(grp); 1336 1337 return (0); 1338 } 1339 1340 /* 1341 * Initialize the capabilities that are advertised for the group 1342 * according to the capabilities of the constituent ports. 1343 */ 1344 static boolean_t 1345 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 1346 { 1347 aggr_grp_t *grp = arg; 1348 1349 switch (cap) { 1350 case MAC_CAPAB_HCKSUM: { 1351 uint32_t *hcksum_txflags = cap_data; 1352 *hcksum_txflags = grp->lg_hcksum_txflags; 1353 break; 1354 } 1355 case MAC_CAPAB_POLL: 1356 /* 1357 * There's nothing for us to fill in, we simply return 1358 * B_TRUE or B_FALSE to represent the group's support 1359 * status for this capability. 1360 */ 1361 return (grp->lg_gldv3_polling); 1362 default: 1363 return (B_FALSE); 1364 } 1365 return (B_TRUE); 1366 } 1367 1368 /* 1369 * Add or remove the multicast addresses that are defined for the group 1370 * to or from the specified port. 1371 * This function is called before stopping a port, before a port 1372 * is detached from a group, and when attaching a port to a group. 1373 */ 1374 void 1375 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add) 1376 { 1377 aggr_grp_t *grp = port->lp_grp; 1378 1379 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 1380 ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock)); 1381 1382 if (!port->lp_started) 1383 return; 1384 1385 mac_multicst_refresh(grp->lg_mh, aggr_port_multicst, port, add); 1386 } 1387 1388 static int 1389 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 1390 { 1391 aggr_grp_t *grp = arg; 1392 aggr_port_t *port = NULL; 1393 int err = 0, cerr; 1394 1395 rw_enter(&grp->lg_lock, RW_WRITER); 1396 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1397 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 1398 continue; 1399 cerr = aggr_port_multicst(port, add, addrp); 1400 if (cerr != 0 && err == 0) 1401 err = cerr; 1402 } 1403 rw_exit(&grp->lg_lock); 1404 return (err); 1405 } 1406 1407 static int 1408 aggr_m_unicst(void *arg, const uint8_t *macaddr) 1409 { 1410 aggr_grp_t *grp = arg; 1411 int rc; 1412 1413 AGGR_LACP_LOCK(grp); 1414 rw_enter(&grp->lg_lock, RW_WRITER); 1415 rc = aggr_grp_modify(0, grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr, 1416 0, 0); 1417 rw_exit(&grp->lg_lock); 1418 AGGR_LACP_UNLOCK(grp); 1419 1420 return (rc); 1421 } 1422 1423 /* 1424 * Initialize the capabilities that are advertised for the group 1425 * according to the capabilities of the constituent ports. 1426 */ 1427 static void 1428 aggr_grp_capab_set(aggr_grp_t *grp) 1429 { 1430 uint32_t cksum; 1431 aggr_port_t *port; 1432 1433 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 1434 ASSERT(grp->lg_ports != NULL); 1435 1436 grp->lg_hcksum_txflags = (uint32_t)-1; 1437 grp->lg_gldv3_polling = B_TRUE; 1438 1439 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1440 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum)) 1441 cksum = 0; 1442 grp->lg_hcksum_txflags &= cksum; 1443 1444 grp->lg_gldv3_polling &= 1445 mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL); 1446 } 1447 } 1448 1449 1450 /* 1451 * Checks whether the capabilities of the port being added are compatible 1452 * with the current capabilities of the aggregation. 1453 */ 1454 static boolean_t 1455 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port) 1456 { 1457 uint32_t hcksum_txflags; 1458 1459 ASSERT(grp->lg_ports != NULL); 1460 1461 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) { 1462 if (grp->lg_hcksum_txflags != 0) 1463 return (B_FALSE); 1464 } else if ((hcksum_txflags & grp->lg_hcksum_txflags) != 1465 grp->lg_hcksum_txflags) { 1466 return (B_FALSE); 1467 } 1468 1469 if (mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL) != 1470 grp->lg_gldv3_polling) { 1471 return (B_FALSE); 1472 } 1473 1474 return (B_TRUE); 1475 } 1476 1477 /* 1478 * Returns the maximum SDU according to the SDU of the constituent ports. 1479 */ 1480 static uint_t 1481 aggr_grp_max_sdu(aggr_grp_t *grp) 1482 { 1483 uint_t max_sdu = (uint_t)-1; 1484 aggr_port_t *port; 1485 1486 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 1487 ASSERT(grp->lg_ports != NULL); 1488 1489 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1490 const mac_info_t *port_mi = mac_info(port->lp_mh); 1491 if (max_sdu > port_mi->mi_sdu_max) 1492 max_sdu = port_mi->mi_sdu_max; 1493 } 1494 1495 return (max_sdu); 1496 } 1497 1498 /* 1499 * Checks if the maximum SDU of the specified port is compatible 1500 * with the maximum SDU of the specified aggregation group, returns 1501 * B_TRUE if it is, B_FALSE otherwise. 1502 */ 1503 static boolean_t 1504 aggr_grp_sdu_check(aggr_grp_t *grp, aggr_port_t *port) 1505 { 1506 const mac_info_t *port_mi = mac_info(port->lp_mh); 1507 1508 return (port_mi->mi_sdu_max >= grp->lg_max_sdu); 1509 } 1510