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