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