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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * IEEE 802.3ad Link Aggregation -- Link Aggregation Groups. 27 * 28 * An instance of the structure aggr_grp_t is allocated for each 29 * link aggregation group. When created, aggr_grp_t objects are 30 * entered into the aggr_grp_hash hash table maintained by the modhash 31 * module. The hash key is the linkid associated with the link 32 * aggregation group. 33 * 34 * A set of MAC ports are associated with each association group. 35 * 36 * Aggr pseudo TX rings 37 * -------------------- 38 * The underlying ports (NICs) in an aggregation can have TX rings. To 39 * enhance aggr's performance, these TX rings are made available to the 40 * aggr layer as pseudo TX rings. The concept of pseudo rings are not new. 41 * They are already present and implemented on the RX side. It is called 42 * as pseudo RX rings. The same concept is extended to the TX side where 43 * each TX ring of an underlying port is reflected in aggr as a pseudo 44 * TX ring. Thus each pseudo TX ring will map to a specific hardware TX 45 * ring. Even in the case of a NIC that does not have a TX ring, a pseudo 46 * TX ring is given to the aggregation layer. 47 * 48 * With this change, the outgoing stack depth looks much better: 49 * 50 * mac_tx() -> mac_tx_aggr_mode() -> mac_tx_soft_ring_process() -> 51 * mac_tx_send() -> aggr_ring_rx() -> <driver>_ring_tx() 52 * 53 * Two new modes are introduced to mac_tx() to handle aggr pseudo TX rings: 54 * SRS_TX_AGGR and SRS_TX_BW_AGGR. 55 * 56 * In SRS_TX_AGGR mode, mac_tx_aggr_mode() routine is called. This routine 57 * invokes an aggr function, aggr_find_tx_ring(), to find a (pseudo) TX 58 * ring belonging to a port on which the packet has to be sent. 59 * aggr_find_tx_ring() first finds the outgoing port based on L2/L3/L4 60 * policy and then uses the fanout_hint passed to it to pick a TX ring from 61 * the selected port. 62 * 63 * In SRS_TX_BW_AGGR mode, mac_tx_bw_mode() function is called where 64 * bandwidth limit is applied first on the outgoing packet and the packets 65 * allowed to go out would call mac_tx_aggr_mode() to send the packet on a 66 * particular TX ring. 67 */ 68 69 #include <sys/types.h> 70 #include <sys/sysmacros.h> 71 #include <sys/conf.h> 72 #include <sys/cmn_err.h> 73 #include <sys/disp.h> 74 #include <sys/list.h> 75 #include <sys/ksynch.h> 76 #include <sys/kmem.h> 77 #include <sys/stream.h> 78 #include <sys/modctl.h> 79 #include <sys/ddi.h> 80 #include <sys/sunddi.h> 81 #include <sys/atomic.h> 82 #include <sys/stat.h> 83 #include <sys/modhash.h> 84 #include <sys/id_space.h> 85 #include <sys/strsun.h> 86 #include <sys/cred.h> 87 #include <sys/dlpi.h> 88 #include <sys/zone.h> 89 #include <sys/mac_provider.h> 90 #include <sys/dls.h> 91 #include <sys/vlan.h> 92 #include <sys/aggr.h> 93 #include <sys/aggr_impl.h> 94 95 static int aggr_m_start(void *); 96 static void aggr_m_stop(void *); 97 static int aggr_m_promisc(void *, boolean_t); 98 static int aggr_m_multicst(void *, boolean_t, const uint8_t *); 99 static int aggr_m_unicst(void *, const uint8_t *); 100 static int aggr_m_stat(void *, uint_t, uint64_t *); 101 static void aggr_m_ioctl(void *, queue_t *, mblk_t *); 102 static boolean_t aggr_m_capab_get(void *, mac_capab_t, void *); 103 static int aggr_m_setprop(void *, const char *, mac_prop_id_t, uint_t, 104 const void *); 105 static void aggr_m_propinfo(void *, const char *, mac_prop_id_t, 106 mac_prop_info_handle_t); 107 108 static aggr_port_t *aggr_grp_port_lookup(aggr_grp_t *, datalink_id_t); 109 static int aggr_grp_rem_port(aggr_grp_t *, aggr_port_t *, boolean_t *, 110 boolean_t *); 111 112 static void aggr_grp_capab_set(aggr_grp_t *); 113 static boolean_t aggr_grp_capab_check(aggr_grp_t *, aggr_port_t *); 114 static uint_t aggr_grp_max_sdu(aggr_grp_t *); 115 static uint32_t aggr_grp_max_margin(aggr_grp_t *); 116 static boolean_t aggr_grp_sdu_check(aggr_grp_t *, aggr_port_t *); 117 static boolean_t aggr_grp_margin_check(aggr_grp_t *, aggr_port_t *); 118 119 static int aggr_add_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *); 120 static void aggr_rem_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *); 121 static int aggr_pseudo_disable_intr(mac_intr_handle_t); 122 static int aggr_pseudo_enable_intr(mac_intr_handle_t); 123 static int aggr_pseudo_start_ring(mac_ring_driver_t, uint64_t); 124 static void aggr_pseudo_stop_ring(mac_ring_driver_t); 125 static int aggr_addmac(void *, const uint8_t *); 126 static int aggr_remmac(void *, const uint8_t *); 127 static mblk_t *aggr_rx_poll(void *, int); 128 static void aggr_fill_ring(void *, mac_ring_type_t, const int, 129 const int, mac_ring_info_t *, mac_ring_handle_t); 130 static void aggr_fill_group(void *, mac_ring_type_t, const int, 131 mac_group_info_t *, mac_group_handle_t); 132 133 static kmem_cache_t *aggr_grp_cache; 134 static mod_hash_t *aggr_grp_hash; 135 static krwlock_t aggr_grp_lock; 136 static uint_t aggr_grp_cnt; 137 static id_space_t *key_ids; 138 139 #define GRP_HASHSZ 64 140 #define GRP_HASH_KEY(linkid) ((mod_hash_key_t)(uintptr_t)linkid) 141 #define AGGR_PORT_NAME_DELIMIT '-' 142 143 static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0}; 144 145 #define AGGR_M_CALLBACK_FLAGS \ 146 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_PROPINFO) 147 148 static mac_callbacks_t aggr_m_callbacks = { 149 AGGR_M_CALLBACK_FLAGS, 150 aggr_m_stat, 151 aggr_m_start, 152 aggr_m_stop, 153 aggr_m_promisc, 154 aggr_m_multicst, 155 NULL, 156 NULL, 157 NULL, 158 aggr_m_ioctl, 159 aggr_m_capab_get, 160 NULL, 161 NULL, 162 aggr_m_setprop, 163 NULL, 164 aggr_m_propinfo 165 }; 166 167 /*ARGSUSED*/ 168 static int 169 aggr_grp_constructor(void *buf, void *arg, int kmflag) 170 { 171 aggr_grp_t *grp = buf; 172 173 bzero(grp, sizeof (*grp)); 174 mutex_init(&grp->lg_lacp_lock, NULL, MUTEX_DEFAULT, NULL); 175 cv_init(&grp->lg_lacp_cv, NULL, CV_DEFAULT, NULL); 176 rw_init(&grp->lg_tx_lock, NULL, RW_DRIVER, NULL); 177 mutex_init(&grp->lg_port_lock, NULL, MUTEX_DEFAULT, NULL); 178 cv_init(&grp->lg_port_cv, NULL, CV_DEFAULT, NULL); 179 mutex_init(&grp->lg_tx_flowctl_lock, NULL, MUTEX_DEFAULT, NULL); 180 cv_init(&grp->lg_tx_flowctl_cv, NULL, CV_DEFAULT, NULL); 181 grp->lg_link_state = LINK_STATE_UNKNOWN; 182 return (0); 183 } 184 185 /*ARGSUSED*/ 186 static void 187 aggr_grp_destructor(void *buf, void *arg) 188 { 189 aggr_grp_t *grp = buf; 190 191 if (grp->lg_tx_ports != NULL) { 192 kmem_free(grp->lg_tx_ports, 193 grp->lg_tx_ports_size * sizeof (aggr_port_t *)); 194 } 195 196 mutex_destroy(&grp->lg_lacp_lock); 197 cv_destroy(&grp->lg_lacp_cv); 198 mutex_destroy(&grp->lg_port_lock); 199 cv_destroy(&grp->lg_port_cv); 200 rw_destroy(&grp->lg_tx_lock); 201 mutex_destroy(&grp->lg_tx_flowctl_lock); 202 cv_destroy(&grp->lg_tx_flowctl_cv); 203 } 204 205 void 206 aggr_grp_init(void) 207 { 208 aggr_grp_cache = kmem_cache_create("aggr_grp_cache", 209 sizeof (aggr_grp_t), 0, aggr_grp_constructor, 210 aggr_grp_destructor, NULL, NULL, NULL, 0); 211 212 aggr_grp_hash = mod_hash_create_idhash("aggr_grp_hash", 213 GRP_HASHSZ, mod_hash_null_valdtor); 214 rw_init(&aggr_grp_lock, NULL, RW_DEFAULT, NULL); 215 aggr_grp_cnt = 0; 216 217 /* 218 * Allocate an id space to manage key values (when key is not 219 * specified). The range of the id space will be from 220 * (AGGR_MAX_KEY + 1) to UINT16_MAX, because the LACP protocol 221 * uses a 16-bit key. 222 */ 223 key_ids = id_space_create("aggr_key_ids", AGGR_MAX_KEY + 1, UINT16_MAX); 224 ASSERT(key_ids != NULL); 225 } 226 227 void 228 aggr_grp_fini(void) 229 { 230 id_space_destroy(key_ids); 231 rw_destroy(&aggr_grp_lock); 232 mod_hash_destroy_idhash(aggr_grp_hash); 233 kmem_cache_destroy(aggr_grp_cache); 234 } 235 236 uint_t 237 aggr_grp_count(void) 238 { 239 uint_t count; 240 241 rw_enter(&aggr_grp_lock, RW_READER); 242 count = aggr_grp_cnt; 243 rw_exit(&aggr_grp_lock); 244 return (count); 245 } 246 247 /* 248 * Since both aggr_port_notify_cb() and aggr_port_timer_thread() functions 249 * requires the mac perimeter, this function holds a reference of the aggr 250 * and aggr won't call mac_unregister() until this reference drops to 0. 251 */ 252 void 253 aggr_grp_port_hold(aggr_port_t *port) 254 { 255 aggr_grp_t *grp = port->lp_grp; 256 257 AGGR_PORT_REFHOLD(port); 258 mutex_enter(&grp->lg_port_lock); 259 grp->lg_port_ref++; 260 mutex_exit(&grp->lg_port_lock); 261 } 262 263 /* 264 * Release the reference of the grp and inform aggr_grp_delete() calling 265 * mac_unregister() is now safe. 266 */ 267 void 268 aggr_grp_port_rele(aggr_port_t *port) 269 { 270 aggr_grp_t *grp = port->lp_grp; 271 272 mutex_enter(&grp->lg_port_lock); 273 if (--grp->lg_port_ref == 0) 274 cv_signal(&grp->lg_port_cv); 275 mutex_exit(&grp->lg_port_lock); 276 AGGR_PORT_REFRELE(port); 277 } 278 279 /* 280 * Wait for the port's lacp timer thread and the port's notification callback 281 * to exit. 282 */ 283 void 284 aggr_grp_port_wait(aggr_grp_t *grp) 285 { 286 mutex_enter(&grp->lg_port_lock); 287 if (grp->lg_port_ref != 0) 288 cv_wait(&grp->lg_port_cv, &grp->lg_port_lock); 289 mutex_exit(&grp->lg_port_lock); 290 } 291 292 /* 293 * Attach a port to a link aggregation group. 294 * 295 * A port is attached to a link aggregation group once its speed 296 * and link state have been verified. 297 * 298 * Returns B_TRUE if the group link state or speed has changed. If 299 * it's the case, the caller must notify the MAC layer via a call 300 * to mac_link(). 301 */ 302 boolean_t 303 aggr_grp_attach_port(aggr_grp_t *grp, aggr_port_t *port) 304 { 305 boolean_t link_state_changed = B_FALSE; 306 307 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 308 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 309 310 if (port->lp_state == AGGR_PORT_STATE_ATTACHED) 311 return (B_FALSE); 312 313 /* 314 * Validate the MAC port link speed and update the group 315 * link speed if needed. 316 */ 317 if (port->lp_ifspeed == 0 || 318 port->lp_link_state != LINK_STATE_UP || 319 port->lp_link_duplex != LINK_DUPLEX_FULL) { 320 /* 321 * Can't attach a MAC port with unknown link speed, 322 * down link, or not in full duplex mode. 323 */ 324 return (B_FALSE); 325 } 326 327 if (grp->lg_ifspeed == 0) { 328 /* 329 * The group inherits the speed of the first link being 330 * attached. 331 */ 332 grp->lg_ifspeed = port->lp_ifspeed; 333 link_state_changed = B_TRUE; 334 } else if (grp->lg_ifspeed != port->lp_ifspeed) { 335 /* 336 * The link speed of the MAC port must be the same as 337 * the group link speed, as per 802.3ad. Since it is 338 * not, the attach is cancelled. 339 */ 340 return (B_FALSE); 341 } 342 343 grp->lg_nattached_ports++; 344 345 /* 346 * Update the group link state. 347 */ 348 if (grp->lg_link_state != LINK_STATE_UP) { 349 grp->lg_link_state = LINK_STATE_UP; 350 grp->lg_link_duplex = LINK_DUPLEX_FULL; 351 link_state_changed = B_TRUE; 352 } 353 354 /* 355 * Update port's state. 356 */ 357 port->lp_state = AGGR_PORT_STATE_ATTACHED; 358 359 aggr_grp_multicst_port(port, B_TRUE); 360 361 /* 362 * Set port's receive callback 363 */ 364 mac_rx_set(port->lp_mch, aggr_recv_cb, port); 365 366 /* 367 * If LACP is OFF, the port can be used to send data as soon 368 * as its link is up and verified to be compatible with the 369 * aggregation. 370 * 371 * If LACP is active or passive, notify the LACP subsystem, which 372 * will enable sending on the port following the LACP protocol. 373 */ 374 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 375 aggr_send_port_enable(port); 376 else 377 aggr_lacp_port_attached(port); 378 379 return (link_state_changed); 380 } 381 382 boolean_t 383 aggr_grp_detach_port(aggr_grp_t *grp, aggr_port_t *port) 384 { 385 boolean_t link_state_changed = B_FALSE; 386 387 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 388 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 389 390 /* update state */ 391 if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 392 return (B_FALSE); 393 394 mac_rx_clear(port->lp_mch); 395 396 aggr_grp_multicst_port(port, B_FALSE); 397 398 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 399 aggr_send_port_disable(port); 400 else 401 aggr_lacp_port_detached(port); 402 403 port->lp_state = AGGR_PORT_STATE_STANDBY; 404 405 grp->lg_nattached_ports--; 406 if (grp->lg_nattached_ports == 0) { 407 /* the last attached MAC port of the group is being detached */ 408 grp->lg_ifspeed = 0; 409 grp->lg_link_state = LINK_STATE_DOWN; 410 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 411 link_state_changed = B_TRUE; 412 } 413 414 return (link_state_changed); 415 } 416 417 /* 418 * Update the MAC addresses of the constituent ports of the specified 419 * group. This function is invoked: 420 * - after creating a new aggregation group. 421 * - after adding new ports to an aggregation group. 422 * - after removing a port from a group when the MAC address of 423 * that port was used for the MAC address of the group. 424 * - after the MAC address of a port changed when the MAC address 425 * of that port was used for the MAC address of the group. 426 * 427 * Return true if the link state of the aggregation changed, for example 428 * as a result of a failure changing the MAC address of one of the 429 * constituent ports. 430 */ 431 boolean_t 432 aggr_grp_update_ports_mac(aggr_grp_t *grp) 433 { 434 aggr_port_t *cport; 435 boolean_t link_state_changed = B_FALSE; 436 mac_perim_handle_t mph; 437 438 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 439 440 for (cport = grp->lg_ports; cport != NULL; 441 cport = cport->lp_next) { 442 mac_perim_enter_by_mh(cport->lp_mh, &mph); 443 if (aggr_port_unicst(cport) != 0) { 444 if (aggr_grp_detach_port(grp, cport)) 445 link_state_changed = B_TRUE; 446 } else { 447 /* 448 * If a port was detached because of a previous 449 * failure changing the MAC address, the port is 450 * reattached when it successfully changes the MAC 451 * address now, and this might cause the link state 452 * of the aggregation to change. 453 */ 454 if (aggr_grp_attach_port(grp, cport)) 455 link_state_changed = B_TRUE; 456 } 457 mac_perim_exit(mph); 458 } 459 return (link_state_changed); 460 } 461 462 /* 463 * Invoked when the MAC address of a port has changed. If the port's 464 * MAC address was used for the group MAC address, set mac_addr_changedp 465 * to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST 466 * notification. If the link state changes due to detach/attach of 467 * the constituent port, set link_state_changedp to B_TRUE to indicate 468 * to the caller that it should send a MAC_NOTE_LINK notification. In both 469 * cases, it is the responsibility of the caller to invoke notification 470 * functions after releasing the the port lock. 471 */ 472 void 473 aggr_grp_port_mac_changed(aggr_grp_t *grp, aggr_port_t *port, 474 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 475 { 476 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 477 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 478 ASSERT(mac_addr_changedp != NULL); 479 ASSERT(link_state_changedp != NULL); 480 481 *mac_addr_changedp = B_FALSE; 482 *link_state_changedp = B_FALSE; 483 484 if (grp->lg_addr_fixed) { 485 /* 486 * The group is using a fixed MAC address or an automatic 487 * MAC address has not been set. 488 */ 489 return; 490 } 491 492 if (grp->lg_mac_addr_port == port) { 493 /* 494 * The MAC address of the port was assigned to the group 495 * MAC address. Update the group MAC address. 496 */ 497 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 498 *mac_addr_changedp = B_TRUE; 499 } else { 500 /* 501 * Update the actual port MAC address to the MAC address 502 * of the group. 503 */ 504 if (aggr_port_unicst(port) != 0) { 505 *link_state_changedp = aggr_grp_detach_port(grp, port); 506 } else { 507 /* 508 * If a port was detached because of a previous 509 * failure changing the MAC address, the port is 510 * reattached when it successfully changes the MAC 511 * address now, and this might cause the link state 512 * of the aggregation to change. 513 */ 514 *link_state_changedp = aggr_grp_attach_port(grp, port); 515 } 516 } 517 } 518 519 /* 520 * Add a port to a link aggregation group. 521 */ 522 static int 523 aggr_grp_add_port(aggr_grp_t *grp, datalink_id_t port_linkid, boolean_t force, 524 aggr_port_t **pp) 525 { 526 aggr_port_t *port, **cport; 527 mac_perim_handle_t mph; 528 zoneid_t port_zoneid = ALL_ZONES; 529 int err; 530 531 /* The port must be int the same zone as the aggregation. */ 532 if (zone_check_datalink(&port_zoneid, port_linkid) != 0) 533 port_zoneid = GLOBAL_ZONEID; 534 if (grp->lg_zoneid != port_zoneid) 535 return (EBUSY); 536 537 /* 538 * lg_mh could be NULL when the function is called during the creation 539 * of the aggregation. 540 */ 541 ASSERT(grp->lg_mh == NULL || MAC_PERIM_HELD(grp->lg_mh)); 542 543 /* create new port */ 544 err = aggr_port_create(grp, port_linkid, force, &port); 545 if (err != 0) 546 return (err); 547 548 mac_perim_enter_by_mh(port->lp_mh, &mph); 549 550 /* add port to list of group constituent ports */ 551 cport = &grp->lg_ports; 552 while (*cport != NULL) 553 cport = &((*cport)->lp_next); 554 *cport = port; 555 556 /* 557 * Back reference to the group it is member of. A port always 558 * holds a reference to its group to ensure that the back 559 * reference is always valid. 560 */ 561 port->lp_grp = grp; 562 AGGR_GRP_REFHOLD(grp); 563 grp->lg_nports++; 564 565 aggr_lacp_init_port(port); 566 mac_perim_exit(mph); 567 568 if (pp != NULL) 569 *pp = port; 570 571 return (0); 572 } 573 574 /* 575 * Add a pseudo RX ring for the given HW ring handle. 576 */ 577 static int 578 aggr_add_pseudo_rx_ring(aggr_port_t *port, 579 aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh) 580 { 581 aggr_pseudo_rx_ring_t *ring; 582 int err; 583 int j; 584 585 for (j = 0; j < MAX_RINGS_PER_GROUP; j++) { 586 ring = rx_grp->arg_rings + j; 587 if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE)) 588 break; 589 } 590 591 /* 592 * No slot for this new RX ring. 593 */ 594 if (j == MAX_RINGS_PER_GROUP) 595 return (EIO); 596 597 ring->arr_flags |= MAC_PSEUDO_RING_INUSE; 598 ring->arr_hw_rh = hw_rh; 599 ring->arr_port = port; 600 rx_grp->arg_ring_cnt++; 601 602 /* 603 * The group is already registered, dynamically add a new ring to the 604 * mac group. 605 */ 606 if ((err = mac_group_add_ring(rx_grp->arg_gh, j)) != 0) { 607 ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE; 608 ring->arr_hw_rh = NULL; 609 ring->arr_port = NULL; 610 rx_grp->arg_ring_cnt--; 611 } else { 612 mac_hwring_setup(hw_rh, (mac_resource_handle_t)ring, 613 mac_find_ring(rx_grp->arg_gh, j)); 614 } 615 return (err); 616 } 617 618 /* 619 * Remove the pseudo RX ring of the given HW ring handle. 620 */ 621 static void 622 aggr_rem_pseudo_rx_ring(aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh) 623 { 624 aggr_pseudo_rx_ring_t *ring; 625 int j; 626 627 for (j = 0; j < MAX_RINGS_PER_GROUP; j++) { 628 ring = rx_grp->arg_rings + j; 629 if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE) || 630 ring->arr_hw_rh != hw_rh) { 631 continue; 632 } 633 634 mac_group_rem_ring(rx_grp->arg_gh, ring->arr_rh); 635 636 ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE; 637 ring->arr_hw_rh = NULL; 638 ring->arr_port = NULL; 639 rx_grp->arg_ring_cnt--; 640 mac_hwring_teardown(hw_rh); 641 break; 642 } 643 } 644 645 /* 646 * This function is called to create pseudo rings over the hardware rings of 647 * the underlying device. Note that there is a 1:1 mapping between the pseudo 648 * RX rings of the aggr and the hardware rings of the underlying port. 649 */ 650 static int 651 aggr_add_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp) 652 { 653 aggr_grp_t *grp = port->lp_grp; 654 mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP]; 655 aggr_unicst_addr_t *addr, *a; 656 mac_perim_handle_t pmph; 657 int hw_rh_cnt, i = 0, j; 658 int err = 0; 659 660 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 661 mac_perim_enter_by_mh(port->lp_mh, &pmph); 662 663 /* 664 * This function must be called after the aggr registers its mac 665 * and its RX group has been initialized. 666 */ 667 ASSERT(rx_grp->arg_gh != NULL); 668 669 /* 670 * Get the list the the underlying HW rings. 671 */ 672 hw_rh_cnt = mac_hwrings_get(port->lp_mch, 673 &port->lp_hwgh, hw_rh, MAC_RING_TYPE_RX); 674 675 if (port->lp_hwgh != NULL) { 676 /* 677 * Quiesce the HW ring and the mac srs on the ring. Note 678 * that the HW ring will be restarted when the pseudo ring 679 * is started. At that time all the packets will be 680 * directly passed up to the pseudo RX ring and handled 681 * by mac srs created over the pseudo RX ring. 682 */ 683 mac_rx_client_quiesce(port->lp_mch); 684 mac_srs_perm_quiesce(port->lp_mch, B_TRUE); 685 } 686 687 /* 688 * Add all the unicast addresses to the newly added port. 689 */ 690 for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) { 691 if ((err = aggr_port_addmac(port, addr->aua_addr)) != 0) 692 break; 693 } 694 695 for (i = 0; err == 0 && i < hw_rh_cnt; i++) 696 err = aggr_add_pseudo_rx_ring(port, rx_grp, hw_rh[i]); 697 698 if (err != 0) { 699 for (j = 0; j < i; j++) 700 aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[j]); 701 702 for (a = rx_grp->arg_macaddr; a != addr; a = a->aua_next) 703 aggr_port_remmac(port, a->aua_addr); 704 705 if (port->lp_hwgh != NULL) { 706 mac_srs_perm_quiesce(port->lp_mch, B_FALSE); 707 mac_rx_client_restart(port->lp_mch); 708 port->lp_hwgh = NULL; 709 } 710 } else { 711 port->lp_rx_grp_added = B_TRUE; 712 } 713 done: 714 mac_perim_exit(pmph); 715 return (err); 716 } 717 718 /* 719 * This function is called by aggr to remove pseudo RX rings over the 720 * HW rings of the underlying port. 721 */ 722 static void 723 aggr_rem_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp) 724 { 725 aggr_grp_t *grp = port->lp_grp; 726 mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP]; 727 aggr_unicst_addr_t *addr; 728 mac_group_handle_t hwgh; 729 mac_perim_handle_t pmph; 730 int hw_rh_cnt, i; 731 732 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 733 mac_perim_enter_by_mh(port->lp_mh, &pmph); 734 735 if (!port->lp_rx_grp_added) 736 goto done; 737 738 ASSERT(rx_grp->arg_gh != NULL); 739 hw_rh_cnt = mac_hwrings_get(port->lp_mch, 740 &hwgh, hw_rh, MAC_RING_TYPE_RX); 741 742 /* 743 * If hw_rh_cnt is 0, it means that the underlying port does not 744 * support RX rings. Directly return in this case. 745 */ 746 for (i = 0; i < hw_rh_cnt; i++) 747 aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[i]); 748 749 for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) 750 aggr_port_remmac(port, addr->aua_addr); 751 752 if (port->lp_hwgh != NULL) { 753 port->lp_hwgh = NULL; 754 755 /* 756 * First clear the permanent-quiesced flag of the RX srs then 757 * restart the HW ring and the mac srs on the ring. Note that 758 * the HW ring and associated SRS will soon been removed when 759 * the port is removed from the aggr. 760 */ 761 mac_srs_perm_quiesce(port->lp_mch, B_FALSE); 762 mac_rx_client_restart(port->lp_mch); 763 } 764 765 port->lp_rx_grp_added = B_FALSE; 766 done: 767 mac_perim_exit(pmph); 768 } 769 770 /* 771 * Add a pseudo TX ring for the given HW ring handle. 772 */ 773 static int 774 aggr_add_pseudo_tx_ring(aggr_port_t *port, 775 aggr_pseudo_tx_group_t *tx_grp, mac_ring_handle_t hw_rh, 776 mac_ring_handle_t *pseudo_rh) 777 { 778 aggr_pseudo_tx_ring_t *ring; 779 int err; 780 int i; 781 782 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 783 for (i = 0; i < MAX_RINGS_PER_GROUP; i++) { 784 ring = tx_grp->atg_rings + i; 785 if (!(ring->atr_flags & MAC_PSEUDO_RING_INUSE)) 786 break; 787 } 788 /* 789 * No slot for this new TX ring. 790 */ 791 if (i == MAX_RINGS_PER_GROUP) 792 return (EIO); 793 /* 794 * The following 4 statements needs to be done before 795 * calling mac_group_add_ring(). Otherwise it will 796 * result in an assertion failure in mac_init_ring(). 797 */ 798 ring->atr_flags |= MAC_PSEUDO_RING_INUSE; 799 ring->atr_hw_rh = hw_rh; 800 ring->atr_port = port; 801 tx_grp->atg_ring_cnt++; 802 803 /* 804 * The TX side has no concept of ring groups unlike RX groups. 805 * There is just a single group which stores all the TX rings. 806 * This group will be used to store aggr's pseudo TX rings. 807 */ 808 if ((err = mac_group_add_ring(tx_grp->atg_gh, i)) != 0) { 809 ring->atr_flags &= ~MAC_PSEUDO_RING_INUSE; 810 ring->atr_hw_rh = NULL; 811 ring->atr_port = NULL; 812 tx_grp->atg_ring_cnt--; 813 } else { 814 *pseudo_rh = mac_find_ring(tx_grp->atg_gh, i); 815 if (hw_rh != NULL) { 816 mac_hwring_setup(hw_rh, (mac_resource_handle_t)ring, 817 mac_find_ring(tx_grp->atg_gh, i)); 818 } 819 } 820 return (err); 821 } 822 823 /* 824 * Remove the pseudo TX ring of the given HW ring handle. 825 */ 826 static void 827 aggr_rem_pseudo_tx_ring(aggr_pseudo_tx_group_t *tx_grp, 828 mac_ring_handle_t pseudo_hw_rh) 829 { 830 aggr_pseudo_tx_ring_t *ring; 831 int i; 832 833 for (i = 0; i < MAX_RINGS_PER_GROUP; i++) { 834 ring = tx_grp->atg_rings + i; 835 if (ring->atr_rh != pseudo_hw_rh) 836 continue; 837 838 ASSERT(ring->atr_flags & MAC_PSEUDO_RING_INUSE); 839 mac_group_rem_ring(tx_grp->atg_gh, pseudo_hw_rh); 840 ring->atr_flags &= ~MAC_PSEUDO_RING_INUSE; 841 mac_hwring_teardown(ring->atr_hw_rh); 842 ring->atr_hw_rh = NULL; 843 ring->atr_port = NULL; 844 tx_grp->atg_ring_cnt--; 845 break; 846 } 847 } 848 849 /* 850 * This function is called to create pseudo rings over hardware rings of 851 * the underlying device. There is a 1:1 mapping between the pseudo TX 852 * rings of the aggr and the hardware rings of the underlying port. 853 */ 854 static int 855 aggr_add_pseudo_tx_group(aggr_port_t *port, aggr_pseudo_tx_group_t *tx_grp) 856 { 857 aggr_grp_t *grp = port->lp_grp; 858 mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP], pseudo_rh; 859 mac_perim_handle_t pmph; 860 int hw_rh_cnt, i = 0, j; 861 int err = 0; 862 863 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 864 mac_perim_enter_by_mh(port->lp_mh, &pmph); 865 866 /* 867 * Get the list the the underlying HW rings. 868 */ 869 hw_rh_cnt = mac_hwrings_get(port->lp_mch, 870 NULL, hw_rh, MAC_RING_TYPE_TX); 871 872 /* 873 * Even if the underlying NIC does not have TX rings, we 874 * still make a psuedo TX ring for that NIC with NULL as 875 * the ring handle. 876 */ 877 if (hw_rh_cnt == 0) 878 port->lp_tx_ring_cnt = 1; 879 else 880 port->lp_tx_ring_cnt = hw_rh_cnt; 881 882 port->lp_tx_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) * 883 port->lp_tx_ring_cnt), KM_SLEEP); 884 port->lp_pseudo_tx_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) * 885 port->lp_tx_ring_cnt), KM_SLEEP); 886 887 if (hw_rh_cnt == 0) { 888 if ((err = aggr_add_pseudo_tx_ring(port, tx_grp, 889 NULL, &pseudo_rh)) == 0) { 890 port->lp_tx_rings[0] = NULL; 891 port->lp_pseudo_tx_rings[0] = pseudo_rh; 892 } 893 } else { 894 for (i = 0; err == 0 && i < hw_rh_cnt; i++) { 895 err = aggr_add_pseudo_tx_ring(port, 896 tx_grp, hw_rh[i], &pseudo_rh); 897 if (err != 0) 898 break; 899 port->lp_tx_rings[i] = hw_rh[i]; 900 port->lp_pseudo_tx_rings[i] = pseudo_rh; 901 } 902 } 903 904 if (err != 0) { 905 if (hw_rh_cnt != 0) { 906 for (j = 0; j < i; j++) { 907 aggr_rem_pseudo_tx_ring(tx_grp, 908 port->lp_pseudo_tx_rings[j]); 909 } 910 } 911 kmem_free(port->lp_tx_rings, 912 (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt)); 913 kmem_free(port->lp_pseudo_tx_rings, 914 (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt)); 915 port->lp_tx_ring_cnt = 0; 916 } else { 917 port->lp_tx_grp_added = B_TRUE; 918 port->lp_tx_notify_mh = mac_client_tx_notify(port->lp_mch, 919 aggr_tx_ring_update, port); 920 } 921 mac_perim_exit(pmph); 922 return (err); 923 } 924 925 /* 926 * This function is called by aggr to remove pseudo TX rings over the 927 * HW rings of the underlying port. 928 */ 929 static void 930 aggr_rem_pseudo_tx_group(aggr_port_t *port, aggr_pseudo_tx_group_t *tx_grp) 931 { 932 aggr_grp_t *grp = port->lp_grp; 933 mac_perim_handle_t pmph; 934 int i; 935 936 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 937 mac_perim_enter_by_mh(port->lp_mh, &pmph); 938 939 if (!port->lp_tx_grp_added) 940 goto done; 941 942 ASSERT(tx_grp->atg_gh != NULL); 943 944 for (i = 0; i < port->lp_tx_ring_cnt; i++) 945 aggr_rem_pseudo_tx_ring(tx_grp, port->lp_pseudo_tx_rings[i]); 946 947 kmem_free(port->lp_tx_rings, 948 (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt)); 949 kmem_free(port->lp_pseudo_tx_rings, 950 (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt)); 951 952 port->lp_tx_ring_cnt = 0; 953 (void) mac_client_tx_notify(port->lp_mch, NULL, port->lp_tx_notify_mh); 954 port->lp_tx_grp_added = B_FALSE; 955 done: 956 mac_perim_exit(pmph); 957 } 958 959 static int 960 aggr_pseudo_disable_intr(mac_intr_handle_t ih) 961 { 962 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih; 963 return (mac_hwring_disable_intr(rr_ring->arr_hw_rh)); 964 } 965 966 static int 967 aggr_pseudo_enable_intr(mac_intr_handle_t ih) 968 { 969 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih; 970 return (mac_hwring_enable_intr(rr_ring->arr_hw_rh)); 971 } 972 973 static int 974 aggr_pseudo_start_ring(mac_ring_driver_t arg, uint64_t mr_gen) 975 { 976 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg; 977 int err; 978 979 err = mac_hwring_start(rr_ring->arr_hw_rh); 980 if (err == 0) 981 rr_ring->arr_gen = mr_gen; 982 return (err); 983 } 984 985 static void 986 aggr_pseudo_stop_ring(mac_ring_driver_t arg) 987 { 988 aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg; 989 mac_hwring_stop(rr_ring->arr_hw_rh); 990 } 991 992 /* 993 * Add one or more ports to an existing link aggregation group. 994 */ 995 int 996 aggr_grp_add_ports(datalink_id_t linkid, uint_t nports, boolean_t force, 997 laioc_port_t *ports) 998 { 999 int rc, i, nadded = 0; 1000 aggr_grp_t *grp = NULL; 1001 aggr_port_t *port; 1002 boolean_t link_state_changed = B_FALSE; 1003 mac_perim_handle_t mph, pmph; 1004 1005 /* get group corresponding to linkid */ 1006 rw_enter(&aggr_grp_lock, RW_READER); 1007 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1008 (mod_hash_val_t *)&grp) != 0) { 1009 rw_exit(&aggr_grp_lock); 1010 return (ENOENT); 1011 } 1012 AGGR_GRP_REFHOLD(grp); 1013 1014 /* 1015 * Hold the perimeter so that the aggregation won't be destroyed. 1016 */ 1017 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1018 rw_exit(&aggr_grp_lock); 1019 1020 /* add the specified ports to group */ 1021 for (i = 0; i < nports; i++) { 1022 /* add port to group */ 1023 if ((rc = aggr_grp_add_port(grp, ports[i].lp_linkid, 1024 force, &port)) != 0) { 1025 goto bail; 1026 } 1027 ASSERT(port != NULL); 1028 nadded++; 1029 1030 /* check capabilities */ 1031 if (!aggr_grp_capab_check(grp, port) || 1032 !aggr_grp_sdu_check(grp, port) || 1033 !aggr_grp_margin_check(grp, port)) { 1034 rc = ENOTSUP; 1035 goto bail; 1036 } 1037 1038 /* 1039 * Create the pseudo ring for each HW ring of the underlying 1040 * port. 1041 */ 1042 rc = aggr_add_pseudo_tx_group(port, &grp->lg_tx_group); 1043 if (rc != 0) 1044 goto bail; 1045 rc = aggr_add_pseudo_rx_group(port, &grp->lg_rx_group); 1046 if (rc != 0) 1047 goto bail; 1048 1049 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1050 1051 /* set LACP mode */ 1052 aggr_port_lacp_set_mode(grp, port); 1053 1054 /* start port if group has already been started */ 1055 if (grp->lg_started) { 1056 rc = aggr_port_start(port); 1057 if (rc != 0) { 1058 mac_perim_exit(pmph); 1059 goto bail; 1060 } 1061 1062 /* 1063 * Turn on the promiscuous mode over the port when it 1064 * is requested to be turned on to receive the 1065 * non-primary address over a port, or the promiscous 1066 * mode is enabled over the aggr. 1067 */ 1068 if (grp->lg_promisc || port->lp_prom_addr != NULL) { 1069 rc = aggr_port_promisc(port, B_TRUE); 1070 if (rc != 0) { 1071 mac_perim_exit(pmph); 1072 goto bail; 1073 } 1074 } 1075 } 1076 mac_perim_exit(pmph); 1077 1078 /* 1079 * Attach each port if necessary. 1080 */ 1081 if (aggr_port_notify_link(grp, port)) 1082 link_state_changed = B_TRUE; 1083 1084 /* 1085 * Initialize the callback functions for this port. 1086 */ 1087 aggr_port_init_callbacks(port); 1088 } 1089 1090 /* update the MAC address of the constituent ports */ 1091 if (aggr_grp_update_ports_mac(grp)) 1092 link_state_changed = B_TRUE; 1093 1094 if (link_state_changed) 1095 mac_link_update(grp->lg_mh, grp->lg_link_state); 1096 1097 bail: 1098 if (rc != 0) { 1099 /* stop and remove ports that have been added */ 1100 for (i = 0; i < nadded; i++) { 1101 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 1102 ASSERT(port != NULL); 1103 if (grp->lg_started) { 1104 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1105 (void) aggr_port_promisc(port, B_FALSE); 1106 aggr_port_stop(port); 1107 mac_perim_exit(pmph); 1108 } 1109 aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group); 1110 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 1111 (void) aggr_grp_rem_port(grp, port, NULL, NULL); 1112 } 1113 } 1114 1115 mac_perim_exit(mph); 1116 AGGR_GRP_REFRELE(grp); 1117 return (rc); 1118 } 1119 1120 static int 1121 aggr_grp_modify_common(aggr_grp_t *grp, uint8_t update_mask, uint32_t policy, 1122 boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1123 aggr_lacp_timer_t lacp_timer) 1124 { 1125 boolean_t mac_addr_changed = B_FALSE; 1126 boolean_t link_state_changed = B_FALSE; 1127 mac_perim_handle_t pmph; 1128 1129 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1130 1131 /* validate fixed address if specified */ 1132 if ((update_mask & AGGR_MODIFY_MAC) && mac_fixed && 1133 ((bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) || 1134 (mac_addr[0] & 0x01))) { 1135 return (EINVAL); 1136 } 1137 1138 /* update policy if requested */ 1139 if (update_mask & AGGR_MODIFY_POLICY) 1140 aggr_send_update_policy(grp, policy); 1141 1142 /* update unicast MAC address if requested */ 1143 if (update_mask & AGGR_MODIFY_MAC) { 1144 if (mac_fixed) { 1145 /* user-supplied MAC address */ 1146 grp->lg_mac_addr_port = NULL; 1147 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) != 0) { 1148 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 1149 mac_addr_changed = B_TRUE; 1150 } 1151 } else if (grp->lg_addr_fixed) { 1152 /* switch from user-supplied to automatic */ 1153 aggr_port_t *port = grp->lg_ports; 1154 1155 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1156 bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL); 1157 grp->lg_mac_addr_port = port; 1158 mac_addr_changed = B_TRUE; 1159 mac_perim_exit(pmph); 1160 } 1161 grp->lg_addr_fixed = mac_fixed; 1162 } 1163 1164 if (mac_addr_changed) 1165 link_state_changed = aggr_grp_update_ports_mac(grp); 1166 1167 if (update_mask & AGGR_MODIFY_LACP_MODE) 1168 aggr_lacp_update_mode(grp, lacp_mode); 1169 1170 if (update_mask & AGGR_MODIFY_LACP_TIMER) 1171 aggr_lacp_update_timer(grp, lacp_timer); 1172 1173 if (link_state_changed) 1174 mac_link_update(grp->lg_mh, grp->lg_link_state); 1175 1176 if (mac_addr_changed) 1177 mac_unicst_update(grp->lg_mh, grp->lg_addr); 1178 1179 return (0); 1180 } 1181 1182 /* 1183 * Update properties of an existing link aggregation group. 1184 */ 1185 int 1186 aggr_grp_modify(datalink_id_t linkid, uint8_t update_mask, uint32_t policy, 1187 boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1188 aggr_lacp_timer_t lacp_timer) 1189 { 1190 aggr_grp_t *grp = NULL; 1191 mac_perim_handle_t mph; 1192 int err; 1193 1194 /* get group corresponding to linkid */ 1195 rw_enter(&aggr_grp_lock, RW_READER); 1196 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1197 (mod_hash_val_t *)&grp) != 0) { 1198 rw_exit(&aggr_grp_lock); 1199 return (ENOENT); 1200 } 1201 AGGR_GRP_REFHOLD(grp); 1202 1203 /* 1204 * Hold the perimeter so that the aggregation won't be destroyed. 1205 */ 1206 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1207 rw_exit(&aggr_grp_lock); 1208 1209 err = aggr_grp_modify_common(grp, update_mask, policy, mac_fixed, 1210 mac_addr, lacp_mode, lacp_timer); 1211 1212 mac_perim_exit(mph); 1213 AGGR_GRP_REFRELE(grp); 1214 return (err); 1215 } 1216 1217 /* 1218 * Create a new link aggregation group upon request from administrator. 1219 * Returns 0 on success, an errno on failure. 1220 */ 1221 int 1222 aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports, 1223 laioc_port_t *ports, uint32_t policy, boolean_t mac_fixed, boolean_t force, 1224 uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, 1225 cred_t *credp) 1226 { 1227 aggr_grp_t *grp = NULL; 1228 aggr_port_t *port; 1229 mac_register_t *mac; 1230 boolean_t link_state_changed; 1231 mac_perim_handle_t mph; 1232 int err; 1233 int i; 1234 kt_did_t tid = 0; 1235 1236 /* need at least one port */ 1237 if (nports == 0) 1238 return (EINVAL); 1239 1240 rw_enter(&aggr_grp_lock, RW_WRITER); 1241 1242 /* does a group with the same linkid already exist? */ 1243 err = mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1244 (mod_hash_val_t *)&grp); 1245 if (err == 0) { 1246 rw_exit(&aggr_grp_lock); 1247 return (EEXIST); 1248 } 1249 1250 grp = kmem_cache_alloc(aggr_grp_cache, KM_SLEEP); 1251 1252 grp->lg_refs = 1; 1253 grp->lg_closing = B_FALSE; 1254 grp->lg_force = force; 1255 grp->lg_linkid = linkid; 1256 grp->lg_zoneid = crgetzoneid(credp); 1257 grp->lg_ifspeed = 0; 1258 grp->lg_link_state = LINK_STATE_UNKNOWN; 1259 grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN; 1260 grp->lg_started = B_FALSE; 1261 grp->lg_promisc = B_FALSE; 1262 grp->lg_lacp_done = B_FALSE; 1263 grp->lg_tx_notify_done = B_FALSE; 1264 grp->lg_lacp_head = grp->lg_lacp_tail = NULL; 1265 grp->lg_lacp_rx_thread = thread_create(NULL, 0, 1266 aggr_lacp_rx_thread, grp, 0, &p0, TS_RUN, minclsyspri); 1267 grp->lg_tx_notify_thread = thread_create(NULL, 0, 1268 aggr_tx_notify_thread, grp, 0, &p0, TS_RUN, minclsyspri); 1269 grp->lg_tx_blocked_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) * 1270 MAX_RINGS_PER_GROUP), KM_SLEEP); 1271 grp->lg_tx_blocked_cnt = 0; 1272 bzero(&grp->lg_rx_group, sizeof (aggr_pseudo_rx_group_t)); 1273 bzero(&grp->lg_tx_group, sizeof (aggr_pseudo_tx_group_t)); 1274 aggr_lacp_init_grp(grp); 1275 1276 /* add MAC ports to group */ 1277 grp->lg_ports = NULL; 1278 grp->lg_nports = 0; 1279 grp->lg_nattached_ports = 0; 1280 grp->lg_ntx_ports = 0; 1281 1282 /* 1283 * If key is not specified by the user, allocate the key. 1284 */ 1285 if ((key == 0) && ((key = (uint32_t)id_alloc(key_ids)) == 0)) { 1286 err = ENOMEM; 1287 goto bail; 1288 } 1289 grp->lg_key = key; 1290 1291 for (i = 0; i < nports; i++) { 1292 err = aggr_grp_add_port(grp, ports[i].lp_linkid, force, NULL); 1293 if (err != 0) 1294 goto bail; 1295 } 1296 1297 /* 1298 * If no explicit MAC address was specified by the administrator, 1299 * set it to the MAC address of the first port. 1300 */ 1301 grp->lg_addr_fixed = mac_fixed; 1302 if (grp->lg_addr_fixed) { 1303 /* validate specified address */ 1304 if (bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) { 1305 err = EINVAL; 1306 goto bail; 1307 } 1308 bcopy(mac_addr, grp->lg_addr, ETHERADDRL); 1309 } else { 1310 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 1311 grp->lg_mac_addr_port = grp->lg_ports; 1312 } 1313 1314 /* set the initial group capabilities */ 1315 aggr_grp_capab_set(grp); 1316 1317 if ((mac = mac_alloc(MAC_VERSION)) == NULL) { 1318 err = ENOMEM; 1319 goto bail; 1320 } 1321 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 1322 mac->m_driver = grp; 1323 mac->m_dip = aggr_dip; 1324 mac->m_instance = grp->lg_key > AGGR_MAX_KEY ? (uint_t)-1 : grp->lg_key; 1325 mac->m_src_addr = grp->lg_addr; 1326 mac->m_callbacks = &aggr_m_callbacks; 1327 mac->m_min_sdu = 0; 1328 mac->m_max_sdu = grp->lg_max_sdu = aggr_grp_max_sdu(grp); 1329 mac->m_margin = aggr_grp_max_margin(grp); 1330 mac->m_v12n = MAC_VIRT_LEVEL1; 1331 err = mac_register(mac, &grp->lg_mh); 1332 mac_free(mac); 1333 if (err != 0) 1334 goto bail; 1335 1336 err = dls_devnet_create(grp->lg_mh, grp->lg_linkid, crgetzoneid(credp)); 1337 if (err != 0) { 1338 (void) mac_unregister(grp->lg_mh); 1339 grp->lg_mh = NULL; 1340 goto bail; 1341 } 1342 1343 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1344 1345 /* 1346 * Update the MAC address of the constituent ports. 1347 * None of the port is attached at this time, the link state of the 1348 * aggregation will not change. 1349 */ 1350 link_state_changed = aggr_grp_update_ports_mac(grp); 1351 ASSERT(!link_state_changed); 1352 1353 /* update outbound load balancing policy */ 1354 aggr_send_update_policy(grp, policy); 1355 1356 /* set LACP mode */ 1357 aggr_lacp_set_mode(grp, lacp_mode, lacp_timer); 1358 1359 /* 1360 * Attach each port if necessary. 1361 */ 1362 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1363 /* 1364 * Create the pseudo ring for each HW ring of the underlying 1365 * port. Note that this is done after the aggr registers the 1366 * mac. 1367 */ 1368 VERIFY(aggr_add_pseudo_tx_group(port, &grp->lg_tx_group) == 0); 1369 VERIFY(aggr_add_pseudo_rx_group(port, &grp->lg_rx_group) == 0); 1370 if (aggr_port_notify_link(grp, port)) 1371 link_state_changed = B_TRUE; 1372 1373 /* 1374 * Initialize the callback functions for this port. 1375 */ 1376 aggr_port_init_callbacks(port); 1377 } 1378 1379 if (link_state_changed) 1380 mac_link_update(grp->lg_mh, grp->lg_link_state); 1381 1382 /* add new group to hash table */ 1383 err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(linkid), 1384 (mod_hash_val_t)grp); 1385 ASSERT(err == 0); 1386 aggr_grp_cnt++; 1387 1388 mac_perim_exit(mph); 1389 rw_exit(&aggr_grp_lock); 1390 return (0); 1391 1392 bail: 1393 1394 grp->lg_closing = B_TRUE; 1395 1396 port = grp->lg_ports; 1397 while (port != NULL) { 1398 aggr_port_t *cport; 1399 1400 cport = port->lp_next; 1401 aggr_port_delete(port); 1402 port = cport; 1403 } 1404 1405 /* 1406 * Inform the lacp_rx thread to exit. 1407 */ 1408 mutex_enter(&grp->lg_lacp_lock); 1409 grp->lg_lacp_done = B_TRUE; 1410 cv_signal(&grp->lg_lacp_cv); 1411 while (grp->lg_lacp_rx_thread != NULL) 1412 cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock); 1413 mutex_exit(&grp->lg_lacp_lock); 1414 /* 1415 * Inform the tx_notify thread to exit. 1416 */ 1417 mutex_enter(&grp->lg_tx_flowctl_lock); 1418 if (grp->lg_tx_notify_thread != NULL) { 1419 tid = grp->lg_tx_notify_thread->t_did; 1420 grp->lg_tx_notify_done = B_TRUE; 1421 cv_signal(&grp->lg_tx_flowctl_cv); 1422 } 1423 mutex_exit(&grp->lg_tx_flowctl_lock); 1424 if (tid != 0) 1425 thread_join(tid); 1426 1427 kmem_free(grp->lg_tx_blocked_rings, 1428 (sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP)); 1429 rw_exit(&aggr_grp_lock); 1430 AGGR_GRP_REFRELE(grp); 1431 return (err); 1432 } 1433 1434 /* 1435 * Return a pointer to the member of a group with specified linkid. 1436 */ 1437 static aggr_port_t * 1438 aggr_grp_port_lookup(aggr_grp_t *grp, datalink_id_t linkid) 1439 { 1440 aggr_port_t *port; 1441 1442 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1443 1444 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1445 if (port->lp_linkid == linkid) 1446 break; 1447 } 1448 1449 return (port); 1450 } 1451 1452 /* 1453 * Stop, detach and remove a port from a link aggregation group. 1454 */ 1455 static int 1456 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port, 1457 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 1458 { 1459 int rc = 0; 1460 aggr_port_t **pport; 1461 boolean_t mac_addr_changed = B_FALSE; 1462 boolean_t link_state_changed = B_FALSE; 1463 mac_perim_handle_t mph; 1464 uint64_t val; 1465 uint_t i; 1466 uint_t stat; 1467 1468 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1469 ASSERT(grp->lg_nports > 1); 1470 ASSERT(!grp->lg_closing); 1471 1472 /* unlink port */ 1473 for (pport = &grp->lg_ports; *pport != port; 1474 pport = &(*pport)->lp_next) { 1475 if (*pport == NULL) { 1476 rc = ENOENT; 1477 goto done; 1478 } 1479 } 1480 *pport = port->lp_next; 1481 1482 mac_perim_enter_by_mh(port->lp_mh, &mph); 1483 1484 /* 1485 * If the MAC address of the port being removed was assigned 1486 * to the group, update the group MAC address 1487 * using the MAC address of a different port. 1488 */ 1489 if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) { 1490 /* 1491 * Set the MAC address of the group to the 1492 * MAC address of its first port. 1493 */ 1494 bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL); 1495 grp->lg_mac_addr_port = grp->lg_ports; 1496 mac_addr_changed = B_TRUE; 1497 } 1498 1499 link_state_changed = aggr_grp_detach_port(grp, port); 1500 1501 /* 1502 * Add the counter statistics of the ports while it was aggregated 1503 * to the group's residual statistics. This is done by obtaining 1504 * the current counter from the underlying MAC then subtracting the 1505 * value of the counter at the moment it was added to the 1506 * aggregation. 1507 */ 1508 for (i = 0; i < MAC_NSTAT; i++) { 1509 stat = i + MAC_STAT_MIN; 1510 if (!MAC_STAT_ISACOUNTER(stat)) 1511 continue; 1512 val = aggr_port_stat(port, stat); 1513 val -= port->lp_stat[i]; 1514 grp->lg_stat[i] += val; 1515 } 1516 for (i = 0; i < ETHER_NSTAT; i++) { 1517 stat = i + MACTYPE_STAT_MIN; 1518 if (!ETHER_STAT_ISACOUNTER(stat)) 1519 continue; 1520 val = aggr_port_stat(port, stat); 1521 val -= port->lp_ether_stat[i]; 1522 grp->lg_ether_stat[i] += val; 1523 } 1524 1525 grp->lg_nports--; 1526 mac_perim_exit(mph); 1527 1528 aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group); 1529 aggr_port_delete(port); 1530 1531 /* 1532 * If the group MAC address has changed, update the MAC address of 1533 * the remaining constituent ports according to the new MAC 1534 * address of the group. 1535 */ 1536 if (mac_addr_changed && aggr_grp_update_ports_mac(grp)) 1537 link_state_changed = B_TRUE; 1538 1539 done: 1540 if (mac_addr_changedp != NULL) 1541 *mac_addr_changedp = mac_addr_changed; 1542 if (link_state_changedp != NULL) 1543 *link_state_changedp = link_state_changed; 1544 1545 return (rc); 1546 } 1547 1548 /* 1549 * Remove one or more ports from an existing link aggregation group. 1550 */ 1551 int 1552 aggr_grp_rem_ports(datalink_id_t linkid, uint_t nports, laioc_port_t *ports) 1553 { 1554 int rc = 0, i; 1555 aggr_grp_t *grp = NULL; 1556 aggr_port_t *port; 1557 boolean_t mac_addr_update = B_FALSE, mac_addr_changed; 1558 boolean_t link_state_update = B_FALSE, link_state_changed; 1559 mac_perim_handle_t mph, pmph; 1560 1561 /* get group corresponding to linkid */ 1562 rw_enter(&aggr_grp_lock, RW_READER); 1563 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1564 (mod_hash_val_t *)&grp) != 0) { 1565 rw_exit(&aggr_grp_lock); 1566 return (ENOENT); 1567 } 1568 AGGR_GRP_REFHOLD(grp); 1569 1570 /* 1571 * Hold the perimeter so that the aggregation won't be destroyed. 1572 */ 1573 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1574 rw_exit(&aggr_grp_lock); 1575 1576 /* we need to keep at least one port per group */ 1577 if (nports >= grp->lg_nports) { 1578 rc = EINVAL; 1579 goto bail; 1580 } 1581 1582 /* first verify that all the groups are valid */ 1583 for (i = 0; i < nports; i++) { 1584 if (aggr_grp_port_lookup(grp, ports[i].lp_linkid) == NULL) { 1585 /* port not found */ 1586 rc = ENOENT; 1587 goto bail; 1588 } 1589 } 1590 1591 /* clear the promiscous mode for the specified ports */ 1592 for (i = 0; i < nports && rc == 0; i++) { 1593 /* lookup port */ 1594 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 1595 ASSERT(port != NULL); 1596 1597 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1598 rc = aggr_port_promisc(port, B_FALSE); 1599 mac_perim_exit(pmph); 1600 } 1601 if (rc != 0) { 1602 for (i = 0; i < nports; i++) { 1603 port = aggr_grp_port_lookup(grp, 1604 ports[i].lp_linkid); 1605 ASSERT(port != NULL); 1606 1607 /* 1608 * Turn the promiscuous mode back on if it is required 1609 * to receive the non-primary address over a port, or 1610 * the promiscous mode is enabled over the aggr. 1611 */ 1612 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1613 if (port->lp_started && (grp->lg_promisc || 1614 port->lp_prom_addr != NULL)) { 1615 (void) aggr_port_promisc(port, B_TRUE); 1616 } 1617 mac_perim_exit(pmph); 1618 } 1619 goto bail; 1620 } 1621 1622 /* remove the specified ports from group */ 1623 for (i = 0; i < nports; i++) { 1624 /* lookup port */ 1625 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid); 1626 ASSERT(port != NULL); 1627 1628 /* stop port if group has already been started */ 1629 if (grp->lg_started) { 1630 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1631 aggr_port_stop(port); 1632 mac_perim_exit(pmph); 1633 } 1634 1635 /* 1636 * aggr_rem_pseudo_tx_group() is not called here. Instead 1637 * it is called from inside aggr_grp_rem_port() after the 1638 * port has been detached. The reason is that 1639 * aggr_rem_pseudo_tx_group() removes one ring at a time 1640 * and if there is still traffic going on, then there 1641 * is the possibility of aggr_find_tx_ring() returning a 1642 * removed ring for transmission. Once the port has been 1643 * detached, that port will not be used and 1644 * aggr_find_tx_ring() will not return any rings 1645 * belonging to it. 1646 */ 1647 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 1648 1649 /* remove port from group */ 1650 rc = aggr_grp_rem_port(grp, port, &mac_addr_changed, 1651 &link_state_changed); 1652 ASSERT(rc == 0); 1653 mac_addr_update = mac_addr_update || mac_addr_changed; 1654 link_state_update = link_state_update || link_state_changed; 1655 } 1656 1657 bail: 1658 if (mac_addr_update) 1659 mac_unicst_update(grp->lg_mh, grp->lg_addr); 1660 if (link_state_update) 1661 mac_link_update(grp->lg_mh, grp->lg_link_state); 1662 1663 mac_perim_exit(mph); 1664 AGGR_GRP_REFRELE(grp); 1665 1666 return (rc); 1667 } 1668 1669 int 1670 aggr_grp_delete(datalink_id_t linkid, cred_t *cred) 1671 { 1672 aggr_grp_t *grp = NULL; 1673 aggr_port_t *port, *cport; 1674 datalink_id_t tmpid; 1675 mod_hash_val_t val; 1676 mac_perim_handle_t mph, pmph; 1677 int err; 1678 kt_did_t tid = 0; 1679 1680 rw_enter(&aggr_grp_lock, RW_WRITER); 1681 1682 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1683 (mod_hash_val_t *)&grp) != 0) { 1684 rw_exit(&aggr_grp_lock); 1685 return (ENOENT); 1686 } 1687 1688 /* 1689 * Note that dls_devnet_destroy() must be called before lg_lock is 1690 * held. Otherwise, it will deadlock if another thread is in 1691 * aggr_m_stat() and thus has a kstat_hold() on the kstats that 1692 * dls_devnet_destroy() needs to delete. 1693 */ 1694 if ((err = dls_devnet_destroy(grp->lg_mh, &tmpid, B_TRUE)) != 0) { 1695 rw_exit(&aggr_grp_lock); 1696 return (err); 1697 } 1698 ASSERT(linkid == tmpid); 1699 1700 /* 1701 * Unregister from the MAC service module. Since this can 1702 * fail if a client hasn't closed the MAC port, we gracefully 1703 * fail the operation. 1704 */ 1705 if ((err = mac_disable(grp->lg_mh)) != 0) { 1706 (void) dls_devnet_create(grp->lg_mh, linkid, crgetzoneid(cred)); 1707 rw_exit(&aggr_grp_lock); 1708 return (err); 1709 } 1710 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(linkid), &val); 1711 ASSERT(grp == (aggr_grp_t *)val); 1712 1713 ASSERT(aggr_grp_cnt > 0); 1714 aggr_grp_cnt--; 1715 rw_exit(&aggr_grp_lock); 1716 1717 /* 1718 * Inform the lacp_rx thread to exit. 1719 */ 1720 mutex_enter(&grp->lg_lacp_lock); 1721 grp->lg_lacp_done = B_TRUE; 1722 cv_signal(&grp->lg_lacp_cv); 1723 while (grp->lg_lacp_rx_thread != NULL) 1724 cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock); 1725 mutex_exit(&grp->lg_lacp_lock); 1726 /* 1727 * Inform the tx_notify_thread to exit. 1728 */ 1729 mutex_enter(&grp->lg_tx_flowctl_lock); 1730 if (grp->lg_tx_notify_thread != NULL) { 1731 tid = grp->lg_tx_notify_thread->t_did; 1732 grp->lg_tx_notify_done = B_TRUE; 1733 cv_signal(&grp->lg_tx_flowctl_cv); 1734 } 1735 mutex_exit(&grp->lg_tx_flowctl_lock); 1736 if (tid != 0) 1737 thread_join(tid); 1738 1739 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1740 1741 grp->lg_closing = B_TRUE; 1742 /* detach and free MAC ports associated with group */ 1743 port = grp->lg_ports; 1744 while (port != NULL) { 1745 cport = port->lp_next; 1746 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1747 if (grp->lg_started) 1748 aggr_port_stop(port); 1749 (void) aggr_grp_detach_port(grp, port); 1750 mac_perim_exit(pmph); 1751 aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group); 1752 aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group); 1753 aggr_port_delete(port); 1754 port = cport; 1755 } 1756 1757 mac_perim_exit(mph); 1758 1759 kmem_free(grp->lg_tx_blocked_rings, 1760 (sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP)); 1761 /* 1762 * Wait for the port's lacp timer thread and its notification callback 1763 * to exit before calling mac_unregister() since both needs to access 1764 * the mac perimeter of the grp. 1765 */ 1766 aggr_grp_port_wait(grp); 1767 1768 VERIFY(mac_unregister(grp->lg_mh) == 0); 1769 grp->lg_mh = NULL; 1770 1771 AGGR_GRP_REFRELE(grp); 1772 return (0); 1773 } 1774 1775 void 1776 aggr_grp_free(aggr_grp_t *grp) 1777 { 1778 ASSERT(grp->lg_refs == 0); 1779 ASSERT(grp->lg_port_ref == 0); 1780 if (grp->lg_key > AGGR_MAX_KEY) { 1781 id_free(key_ids, grp->lg_key); 1782 grp->lg_key = 0; 1783 } 1784 kmem_cache_free(aggr_grp_cache, grp); 1785 } 1786 1787 int 1788 aggr_grp_info(datalink_id_t linkid, void *fn_arg, 1789 aggr_grp_info_new_grp_fn_t new_grp_fn, 1790 aggr_grp_info_new_port_fn_t new_port_fn, cred_t *cred) 1791 { 1792 aggr_grp_t *grp; 1793 aggr_port_t *port; 1794 mac_perim_handle_t mph, pmph; 1795 int rc = 0; 1796 1797 /* 1798 * Make sure that the aggregation link is visible from the caller's 1799 * zone. 1800 */ 1801 if (!dls_devnet_islinkvisible(linkid, crgetzoneid(cred))) 1802 return (ENOENT); 1803 1804 rw_enter(&aggr_grp_lock, RW_READER); 1805 1806 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid), 1807 (mod_hash_val_t *)&grp) != 0) { 1808 rw_exit(&aggr_grp_lock); 1809 return (ENOENT); 1810 } 1811 AGGR_GRP_REFHOLD(grp); 1812 1813 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1814 rw_exit(&aggr_grp_lock); 1815 1816 rc = new_grp_fn(fn_arg, grp->lg_linkid, 1817 (grp->lg_key > AGGR_MAX_KEY) ? 0 : grp->lg_key, grp->lg_addr, 1818 grp->lg_addr_fixed, grp->lg_force, grp->lg_tx_policy, 1819 grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer); 1820 1821 if (rc != 0) 1822 goto bail; 1823 1824 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1825 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1826 rc = new_port_fn(fn_arg, port->lp_linkid, port->lp_addr, 1827 port->lp_state, &port->lp_lacp.ActorOperPortState); 1828 mac_perim_exit(pmph); 1829 1830 if (rc != 0) 1831 goto bail; 1832 } 1833 1834 bail: 1835 mac_perim_exit(mph); 1836 AGGR_GRP_REFRELE(grp); 1837 return (rc); 1838 } 1839 1840 /*ARGSUSED*/ 1841 static void 1842 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp) 1843 { 1844 miocnak(q, mp, 0, ENOTSUP); 1845 } 1846 1847 static int 1848 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val) 1849 { 1850 aggr_port_t *port; 1851 uint_t stat_index; 1852 1853 /* We only aggregate counter statistics. */ 1854 if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) || 1855 IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) { 1856 return (ENOTSUP); 1857 } 1858 1859 /* 1860 * Counter statistics for a group are computed by aggregating the 1861 * counters of the members MACs while they were aggregated, plus 1862 * the residual counter of the group itself, which is updated each 1863 * time a MAC is removed from the group. 1864 */ 1865 *val = 0; 1866 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1867 /* actual port statistic */ 1868 *val += aggr_port_stat(port, stat); 1869 /* 1870 * minus the port stat when it was added, plus any residual 1871 * amount for the group. 1872 */ 1873 if (IS_MAC_STAT(stat)) { 1874 stat_index = stat - MAC_STAT_MIN; 1875 *val -= port->lp_stat[stat_index]; 1876 *val += grp->lg_stat[stat_index]; 1877 } else if (IS_MACTYPE_STAT(stat)) { 1878 stat_index = stat - MACTYPE_STAT_MIN; 1879 *val -= port->lp_ether_stat[stat_index]; 1880 *val += grp->lg_ether_stat[stat_index]; 1881 } 1882 } 1883 return (0); 1884 } 1885 1886 int 1887 aggr_rx_ring_stat(mac_ring_driver_t rdriver, uint_t stat, uint64_t *val) 1888 { 1889 aggr_pseudo_rx_ring_t *rx_ring = (aggr_pseudo_rx_ring_t *)rdriver; 1890 1891 if (rx_ring->arr_hw_rh != NULL) { 1892 *val = mac_pseudo_rx_ring_stat_get(rx_ring->arr_hw_rh, stat); 1893 } else { 1894 aggr_port_t *port = rx_ring->arr_port; 1895 1896 *val = mac_stat_get(port->lp_mh, stat); 1897 1898 } 1899 return (0); 1900 } 1901 1902 int 1903 aggr_tx_ring_stat(mac_ring_driver_t rdriver, uint_t stat, uint64_t *val) 1904 { 1905 aggr_pseudo_tx_ring_t *tx_ring = (aggr_pseudo_tx_ring_t *)rdriver; 1906 1907 if (tx_ring->atr_hw_rh != NULL) { 1908 *val = mac_pseudo_tx_ring_stat_get(tx_ring->atr_hw_rh, stat); 1909 } else { 1910 aggr_port_t *port = tx_ring->atr_port; 1911 1912 *val = mac_stat_get(port->lp_mh, stat); 1913 } 1914 return (0); 1915 } 1916 1917 static int 1918 aggr_m_stat(void *arg, uint_t stat, uint64_t *val) 1919 { 1920 aggr_grp_t *grp = arg; 1921 mac_perim_handle_t mph; 1922 int rval = 0; 1923 1924 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1925 1926 switch (stat) { 1927 case MAC_STAT_IFSPEED: 1928 *val = grp->lg_ifspeed; 1929 break; 1930 1931 case ETHER_STAT_LINK_DUPLEX: 1932 *val = grp->lg_link_duplex; 1933 break; 1934 1935 default: 1936 /* 1937 * For all other statistics, we return the aggregated stat 1938 * from the underlying ports. aggr_grp_stat() will set 1939 * rval appropriately if the statistic isn't a counter. 1940 */ 1941 rval = aggr_grp_stat(grp, stat, val); 1942 } 1943 1944 mac_perim_exit(mph); 1945 return (rval); 1946 } 1947 1948 static int 1949 aggr_m_start(void *arg) 1950 { 1951 aggr_grp_t *grp = arg; 1952 aggr_port_t *port; 1953 mac_perim_handle_t mph, pmph; 1954 1955 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1956 1957 /* 1958 * Attempts to start all configured members of the group. 1959 * Group members will be attached when their link-up notification 1960 * is received. 1961 */ 1962 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1963 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1964 if (aggr_port_start(port) != 0) { 1965 mac_perim_exit(pmph); 1966 continue; 1967 } 1968 1969 /* 1970 * Turn on the promiscuous mode if it is required to receive 1971 * the non-primary address over a port, or the promiscous 1972 * mode is enabled over the aggr. 1973 */ 1974 if (grp->lg_promisc || port->lp_prom_addr != NULL) { 1975 if (aggr_port_promisc(port, B_TRUE) != 0) 1976 aggr_port_stop(port); 1977 } 1978 mac_perim_exit(pmph); 1979 } 1980 1981 grp->lg_started = B_TRUE; 1982 1983 mac_perim_exit(mph); 1984 return (0); 1985 } 1986 1987 static void 1988 aggr_m_stop(void *arg) 1989 { 1990 aggr_grp_t *grp = arg; 1991 aggr_port_t *port; 1992 mac_perim_handle_t mph, pmph; 1993 1994 mac_perim_enter_by_mh(grp->lg_mh, &mph); 1995 1996 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 1997 mac_perim_enter_by_mh(port->lp_mh, &pmph); 1998 1999 /* reset port promiscuous mode */ 2000 (void) aggr_port_promisc(port, B_FALSE); 2001 2002 aggr_port_stop(port); 2003 mac_perim_exit(pmph); 2004 } 2005 2006 grp->lg_started = B_FALSE; 2007 mac_perim_exit(mph); 2008 } 2009 2010 static int 2011 aggr_m_promisc(void *arg, boolean_t on) 2012 { 2013 aggr_grp_t *grp = arg; 2014 aggr_port_t *port; 2015 boolean_t link_state_changed = B_FALSE; 2016 mac_perim_handle_t mph, pmph; 2017 2018 AGGR_GRP_REFHOLD(grp); 2019 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2020 2021 ASSERT(!grp->lg_closing); 2022 2023 if (on == grp->lg_promisc) 2024 goto bail; 2025 2026 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2027 int err = 0; 2028 2029 mac_perim_enter_by_mh(port->lp_mh, &pmph); 2030 AGGR_PORT_REFHOLD(port); 2031 if (!on && (port->lp_prom_addr == NULL)) 2032 err = aggr_port_promisc(port, B_FALSE); 2033 else if (on && port->lp_started) 2034 err = aggr_port_promisc(port, B_TRUE); 2035 2036 if (err != 0) { 2037 if (aggr_grp_detach_port(grp, port)) 2038 link_state_changed = B_TRUE; 2039 } else { 2040 /* 2041 * If a port was detached because of a previous 2042 * failure changing the promiscuity, the port 2043 * is reattached when it successfully changes 2044 * the promiscuity now, and this might cause 2045 * the link state of the aggregation to change. 2046 */ 2047 if (aggr_grp_attach_port(grp, port)) 2048 link_state_changed = B_TRUE; 2049 } 2050 mac_perim_exit(pmph); 2051 AGGR_PORT_REFRELE(port); 2052 } 2053 2054 grp->lg_promisc = on; 2055 2056 if (link_state_changed) 2057 mac_link_update(grp->lg_mh, grp->lg_link_state); 2058 2059 bail: 2060 mac_perim_exit(mph); 2061 AGGR_GRP_REFRELE(grp); 2062 2063 return (0); 2064 } 2065 2066 static void 2067 aggr_grp_port_rename(const char *new_name, void *arg) 2068 { 2069 /* 2070 * aggr port's mac client name is the format of "aggr link name" plus 2071 * AGGR_PORT_NAME_DELIMIT plus "underneath link name". 2072 */ 2073 int aggr_len, link_len, clnt_name_len, i; 2074 char *str_end, *str_st, *str_del; 2075 char aggr_name[MAXNAMELEN]; 2076 char link_name[MAXNAMELEN]; 2077 char *clnt_name; 2078 aggr_grp_t *aggr_grp = arg; 2079 aggr_port_t *aggr_port = aggr_grp->lg_ports; 2080 2081 for (i = 0; i < aggr_grp->lg_nports; i++) { 2082 clnt_name = mac_client_name(aggr_port->lp_mch); 2083 clnt_name_len = strlen(clnt_name); 2084 str_st = clnt_name; 2085 str_end = &(clnt_name[clnt_name_len]); 2086 str_del = strchr(str_st, AGGR_PORT_NAME_DELIMIT); 2087 ASSERT(str_del != NULL); 2088 aggr_len = (intptr_t)((uintptr_t)str_del - (uintptr_t)str_st); 2089 link_len = (intptr_t)((uintptr_t)str_end - (uintptr_t)str_del); 2090 bzero(aggr_name, MAXNAMELEN); 2091 bzero(link_name, MAXNAMELEN); 2092 bcopy(clnt_name, aggr_name, aggr_len); 2093 bcopy(str_del, link_name, link_len + 1); 2094 bzero(clnt_name, MAXNAMELEN); 2095 (void) snprintf(clnt_name, MAXNAMELEN, "%s%s", new_name, 2096 link_name); 2097 2098 (void) mac_rename_primary(aggr_port->lp_mh, NULL); 2099 aggr_port = aggr_port->lp_next; 2100 } 2101 } 2102 2103 /* 2104 * Initialize the capabilities that are advertised for the group 2105 * according to the capabilities of the constituent ports. 2106 */ 2107 static boolean_t 2108 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data) 2109 { 2110 aggr_grp_t *grp = arg; 2111 2112 switch (cap) { 2113 case MAC_CAPAB_HCKSUM: { 2114 uint32_t *hcksum_txflags = cap_data; 2115 *hcksum_txflags = grp->lg_hcksum_txflags; 2116 break; 2117 } 2118 case MAC_CAPAB_LSO: { 2119 mac_capab_lso_t *cap_lso = cap_data; 2120 2121 if (grp->lg_lso) { 2122 *cap_lso = grp->lg_cap_lso; 2123 break; 2124 } else { 2125 return (B_FALSE); 2126 } 2127 } 2128 case MAC_CAPAB_NO_NATIVEVLAN: 2129 return (!grp->lg_vlan); 2130 case MAC_CAPAB_NO_ZCOPY: 2131 return (!grp->lg_zcopy); 2132 case MAC_CAPAB_RINGS: { 2133 mac_capab_rings_t *cap_rings = cap_data; 2134 2135 if (cap_rings->mr_type == MAC_RING_TYPE_RX) { 2136 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC; 2137 cap_rings->mr_rnum = grp->lg_rx_group.arg_ring_cnt; 2138 2139 /* 2140 * An aggregation advertises only one (pseudo) RX 2141 * group, which virtualizes the main/primary group of 2142 * the underlying devices. 2143 */ 2144 cap_rings->mr_gnum = 1; 2145 cap_rings->mr_gaddring = NULL; 2146 cap_rings->mr_gremring = NULL; 2147 } else { 2148 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC; 2149 cap_rings->mr_rnum = grp->lg_tx_group.atg_ring_cnt; 2150 cap_rings->mr_gnum = 0; 2151 } 2152 cap_rings->mr_rget = aggr_fill_ring; 2153 cap_rings->mr_gget = aggr_fill_group; 2154 break; 2155 } 2156 case MAC_CAPAB_AGGR: 2157 { 2158 mac_capab_aggr_t *aggr_cap; 2159 2160 if (cap_data != NULL) { 2161 aggr_cap = cap_data; 2162 aggr_cap->mca_rename_fn = aggr_grp_port_rename; 2163 aggr_cap->mca_unicst = aggr_m_unicst; 2164 aggr_cap->mca_find_tx_ring_fn = aggr_find_tx_ring; 2165 aggr_cap->mca_arg = arg; 2166 } 2167 return (B_TRUE); 2168 } 2169 default: 2170 return (B_FALSE); 2171 } 2172 return (B_TRUE); 2173 } 2174 2175 /* 2176 * Callback funtion for MAC layer to register groups. 2177 */ 2178 static void 2179 aggr_fill_group(void *arg, mac_ring_type_t rtype, const int index, 2180 mac_group_info_t *infop, mac_group_handle_t gh) 2181 { 2182 aggr_grp_t *grp = arg; 2183 aggr_pseudo_rx_group_t *rx_group; 2184 aggr_pseudo_tx_group_t *tx_group; 2185 2186 ASSERT(index == 0); 2187 if (rtype == MAC_RING_TYPE_RX) { 2188 rx_group = &grp->lg_rx_group; 2189 rx_group->arg_gh = gh; 2190 rx_group->arg_grp = grp; 2191 2192 infop->mgi_driver = (mac_group_driver_t)rx_group; 2193 infop->mgi_start = NULL; 2194 infop->mgi_stop = NULL; 2195 infop->mgi_addmac = aggr_addmac; 2196 infop->mgi_remmac = aggr_remmac; 2197 infop->mgi_count = rx_group->arg_ring_cnt; 2198 } else { 2199 tx_group = &grp->lg_tx_group; 2200 tx_group->atg_gh = gh; 2201 } 2202 } 2203 2204 /* 2205 * Callback funtion for MAC layer to register all rings. 2206 */ 2207 static void 2208 aggr_fill_ring(void *arg, mac_ring_type_t rtype, const int rg_index, 2209 const int index, mac_ring_info_t *infop, mac_ring_handle_t rh) 2210 { 2211 aggr_grp_t *grp = arg; 2212 2213 switch (rtype) { 2214 case MAC_RING_TYPE_RX: { 2215 aggr_pseudo_rx_group_t *rx_group = &grp->lg_rx_group; 2216 aggr_pseudo_rx_ring_t *rx_ring; 2217 mac_intr_t aggr_mac_intr; 2218 2219 ASSERT(rg_index == 0); 2220 2221 ASSERT((index >= 0) && (index < rx_group->arg_ring_cnt)); 2222 rx_ring = rx_group->arg_rings + index; 2223 rx_ring->arr_rh = rh; 2224 2225 /* 2226 * Entrypoint to enable interrupt (disable poll) and 2227 * disable interrupt (enable poll). 2228 */ 2229 aggr_mac_intr.mi_handle = (mac_intr_handle_t)rx_ring; 2230 aggr_mac_intr.mi_enable = aggr_pseudo_enable_intr; 2231 aggr_mac_intr.mi_disable = aggr_pseudo_disable_intr; 2232 aggr_mac_intr.mi_ddi_handle = NULL; 2233 2234 infop->mri_driver = (mac_ring_driver_t)rx_ring; 2235 infop->mri_start = aggr_pseudo_start_ring; 2236 infop->mri_stop = aggr_pseudo_stop_ring; 2237 2238 infop->mri_intr = aggr_mac_intr; 2239 infop->mri_poll = aggr_rx_poll; 2240 2241 infop->mri_stat = aggr_rx_ring_stat; 2242 break; 2243 } 2244 case MAC_RING_TYPE_TX: { 2245 aggr_pseudo_tx_group_t *tx_group = &grp->lg_tx_group; 2246 aggr_pseudo_tx_ring_t *tx_ring; 2247 2248 ASSERT(rg_index == -1); 2249 ASSERT(index < tx_group->atg_ring_cnt); 2250 2251 tx_ring = &tx_group->atg_rings[index]; 2252 tx_ring->atr_rh = rh; 2253 2254 infop->mri_driver = (mac_ring_driver_t)tx_ring; 2255 infop->mri_start = NULL; 2256 infop->mri_stop = NULL; 2257 infop->mri_tx = aggr_ring_tx; 2258 infop->mri_stat = aggr_tx_ring_stat; 2259 /* 2260 * Use the hw TX ring handle to find if the ring needs 2261 * serialization or not. For NICs that do not expose 2262 * Tx rings, atr_hw_rh will be NULL. 2263 */ 2264 if (tx_ring->atr_hw_rh != NULL) { 2265 infop->mri_flags = 2266 mac_hwring_getinfo(tx_ring->atr_hw_rh); 2267 } 2268 break; 2269 } 2270 default: 2271 break; 2272 } 2273 } 2274 2275 static mblk_t * 2276 aggr_rx_poll(void *arg, int bytes_to_pickup) 2277 { 2278 aggr_pseudo_rx_ring_t *rr_ring = arg; 2279 aggr_port_t *port = rr_ring->arr_port; 2280 aggr_grp_t *grp = port->lp_grp; 2281 mblk_t *mp_chain, *mp, **mpp; 2282 2283 mp_chain = mac_hwring_poll(rr_ring->arr_hw_rh, bytes_to_pickup); 2284 2285 if (grp->lg_lacp_mode == AGGR_LACP_OFF) 2286 return (mp_chain); 2287 2288 mpp = &mp_chain; 2289 while ((mp = *mpp) != NULL) { 2290 if (MBLKL(mp) >= sizeof (struct ether_header)) { 2291 struct ether_header *ehp; 2292 2293 ehp = (struct ether_header *)mp->b_rptr; 2294 if (ntohs(ehp->ether_type) == ETHERTYPE_SLOW) { 2295 *mpp = mp->b_next; 2296 mp->b_next = NULL; 2297 aggr_recv_lacp(port, 2298 (mac_resource_handle_t)rr_ring, mp); 2299 continue; 2300 } 2301 } 2302 2303 if (!port->lp_collector_enabled) { 2304 *mpp = mp->b_next; 2305 mp->b_next = NULL; 2306 freemsg(mp); 2307 continue; 2308 } 2309 mpp = &mp->b_next; 2310 } 2311 return (mp_chain); 2312 } 2313 2314 static int 2315 aggr_addmac(void *arg, const uint8_t *mac_addr) 2316 { 2317 aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg; 2318 aggr_unicst_addr_t *addr, **pprev; 2319 aggr_grp_t *grp = rx_group->arg_grp; 2320 aggr_port_t *port, *p; 2321 mac_perim_handle_t mph; 2322 int err = 0; 2323 2324 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2325 2326 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 2327 mac_perim_exit(mph); 2328 return (0); 2329 } 2330 2331 /* 2332 * Insert this mac address into the list of mac addresses owned by 2333 * the aggregation pseudo group. 2334 */ 2335 pprev = &rx_group->arg_macaddr; 2336 while ((addr = *pprev) != NULL) { 2337 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0) { 2338 mac_perim_exit(mph); 2339 return (EEXIST); 2340 } 2341 pprev = &addr->aua_next; 2342 } 2343 addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP); 2344 bcopy(mac_addr, addr->aua_addr, ETHERADDRL); 2345 addr->aua_next = NULL; 2346 *pprev = addr; 2347 2348 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 2349 if ((err = aggr_port_addmac(port, mac_addr)) != 0) 2350 break; 2351 2352 if (err != 0) { 2353 for (p = grp->lg_ports; p != port; p = p->lp_next) 2354 aggr_port_remmac(p, mac_addr); 2355 2356 *pprev = NULL; 2357 kmem_free(addr, sizeof (aggr_unicst_addr_t)); 2358 } 2359 2360 mac_perim_exit(mph); 2361 return (err); 2362 } 2363 2364 static int 2365 aggr_remmac(void *arg, const uint8_t *mac_addr) 2366 { 2367 aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg; 2368 aggr_unicst_addr_t *addr, **pprev; 2369 aggr_grp_t *grp = rx_group->arg_grp; 2370 aggr_port_t *port; 2371 mac_perim_handle_t mph; 2372 int err = 0; 2373 2374 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2375 2376 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 2377 mac_perim_exit(mph); 2378 return (0); 2379 } 2380 2381 /* 2382 * Insert this mac address into the list of mac addresses owned by 2383 * the aggregation pseudo group. 2384 */ 2385 pprev = &rx_group->arg_macaddr; 2386 while ((addr = *pprev) != NULL) { 2387 if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0) { 2388 pprev = &addr->aua_next; 2389 continue; 2390 } 2391 break; 2392 } 2393 if (addr == NULL) { 2394 mac_perim_exit(mph); 2395 return (EINVAL); 2396 } 2397 2398 for (port = grp->lg_ports; port != NULL; port = port->lp_next) 2399 aggr_port_remmac(port, mac_addr); 2400 2401 *pprev = addr->aua_next; 2402 kmem_free(addr, sizeof (aggr_unicst_addr_t)); 2403 2404 mac_perim_exit(mph); 2405 return (err); 2406 } 2407 2408 /* 2409 * Add or remove the multicast addresses that are defined for the group 2410 * to or from the specified port. 2411 * 2412 * Note that aggr_grp_multicst_port(..., B_TRUE) is called when the port 2413 * is started and attached, and aggr_grp_multicst_port(..., B_FALSE) is 2414 * called when the port is either stopped or detached. 2415 */ 2416 void 2417 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add) 2418 { 2419 aggr_grp_t *grp = port->lp_grp; 2420 2421 ASSERT(MAC_PERIM_HELD(port->lp_mh)); 2422 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 2423 2424 if (!port->lp_started || port->lp_state != AGGR_PORT_STATE_ATTACHED) 2425 return; 2426 2427 mac_multicast_refresh(grp->lg_mh, aggr_port_multicst, port, add); 2428 } 2429 2430 static int 2431 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 2432 { 2433 aggr_grp_t *grp = arg; 2434 aggr_port_t *port = NULL, *errport = NULL; 2435 mac_perim_handle_t mph; 2436 int err = 0; 2437 2438 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2439 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2440 if (port->lp_state != AGGR_PORT_STATE_ATTACHED || 2441 !port->lp_started) { 2442 continue; 2443 } 2444 err = aggr_port_multicst(port, add, addrp); 2445 if (err != 0) { 2446 errport = port; 2447 break; 2448 } 2449 } 2450 2451 /* 2452 * At least one port caused error return and this error is returned to 2453 * mac, eventually a NAK would be sent upwards. 2454 * Some ports have this multicast address listed now, and some don't. 2455 * Treat this error as a whole aggr failure not individual port failure. 2456 * Therefore remove this multicast address from other ports. 2457 */ 2458 if ((err != 0) && add) { 2459 for (port = grp->lg_ports; port != errport; 2460 port = port->lp_next) { 2461 if (port->lp_state != AGGR_PORT_STATE_ATTACHED || 2462 !port->lp_started) { 2463 continue; 2464 } 2465 (void) aggr_port_multicst(port, B_FALSE, addrp); 2466 } 2467 } 2468 mac_perim_exit(mph); 2469 return (err); 2470 } 2471 2472 static int 2473 aggr_m_unicst(void *arg, const uint8_t *macaddr) 2474 { 2475 aggr_grp_t *grp = arg; 2476 mac_perim_handle_t mph; 2477 int err; 2478 2479 mac_perim_enter_by_mh(grp->lg_mh, &mph); 2480 err = aggr_grp_modify_common(grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr, 2481 0, 0); 2482 mac_perim_exit(mph); 2483 return (err); 2484 } 2485 2486 /* 2487 * Initialize the capabilities that are advertised for the group 2488 * according to the capabilities of the constituent ports. 2489 */ 2490 static void 2491 aggr_grp_capab_set(aggr_grp_t *grp) 2492 { 2493 uint32_t cksum; 2494 aggr_port_t *port; 2495 mac_capab_lso_t cap_lso; 2496 2497 ASSERT(grp->lg_mh == NULL); 2498 ASSERT(grp->lg_ports != NULL); 2499 2500 grp->lg_hcksum_txflags = (uint32_t)-1; 2501 grp->lg_zcopy = B_TRUE; 2502 grp->lg_vlan = B_TRUE; 2503 2504 grp->lg_lso = B_TRUE; 2505 grp->lg_cap_lso.lso_flags = (t_uscalar_t)-1; 2506 grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max = (t_uscalar_t)-1; 2507 2508 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2509 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum)) 2510 cksum = 0; 2511 grp->lg_hcksum_txflags &= cksum; 2512 2513 grp->lg_vlan &= 2514 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL); 2515 2516 grp->lg_zcopy &= 2517 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL); 2518 2519 grp->lg_lso &= 2520 mac_capab_get(port->lp_mh, MAC_CAPAB_LSO, &cap_lso); 2521 if (grp->lg_lso) { 2522 grp->lg_cap_lso.lso_flags &= cap_lso.lso_flags; 2523 if (grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max > 2524 cap_lso.lso_basic_tcp_ipv4.lso_max) 2525 grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max = 2526 cap_lso.lso_basic_tcp_ipv4.lso_max; 2527 } 2528 } 2529 } 2530 2531 /* 2532 * Checks whether the capabilities of the port being added are compatible 2533 * with the current capabilities of the aggregation. 2534 */ 2535 static boolean_t 2536 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port) 2537 { 2538 uint32_t hcksum_txflags; 2539 2540 ASSERT(grp->lg_ports != NULL); 2541 2542 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) & 2543 grp->lg_vlan) != grp->lg_vlan) { 2544 return (B_FALSE); 2545 } 2546 2547 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL)) & 2548 grp->lg_zcopy) != grp->lg_zcopy) { 2549 return (B_FALSE); 2550 } 2551 2552 if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) { 2553 if (grp->lg_hcksum_txflags != 0) 2554 return (B_FALSE); 2555 } else if ((hcksum_txflags & grp->lg_hcksum_txflags) != 2556 grp->lg_hcksum_txflags) { 2557 return (B_FALSE); 2558 } 2559 2560 if (grp->lg_lso) { 2561 mac_capab_lso_t cap_lso; 2562 2563 if (mac_capab_get(port->lp_mh, MAC_CAPAB_LSO, &cap_lso)) { 2564 if ((grp->lg_cap_lso.lso_flags & cap_lso.lso_flags) != 2565 grp->lg_cap_lso.lso_flags) 2566 return (B_FALSE); 2567 if (grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max > 2568 cap_lso.lso_basic_tcp_ipv4.lso_max) 2569 return (B_FALSE); 2570 } else { 2571 return (B_FALSE); 2572 } 2573 } 2574 2575 return (B_TRUE); 2576 } 2577 2578 /* 2579 * Returns the maximum SDU according to the SDU of the constituent ports. 2580 */ 2581 static uint_t 2582 aggr_grp_max_sdu(aggr_grp_t *grp) 2583 { 2584 uint_t max_sdu = (uint_t)-1; 2585 aggr_port_t *port; 2586 2587 ASSERT(grp->lg_ports != NULL); 2588 2589 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2590 uint_t port_sdu_max; 2591 2592 mac_sdu_get(port->lp_mh, NULL, &port_sdu_max); 2593 if (max_sdu > port_sdu_max) 2594 max_sdu = port_sdu_max; 2595 } 2596 2597 return (max_sdu); 2598 } 2599 2600 /* 2601 * Checks if the maximum SDU of the specified port is compatible 2602 * with the maximum SDU of the specified aggregation group, returns 2603 * B_TRUE if it is, B_FALSE otherwise. 2604 */ 2605 static boolean_t 2606 aggr_grp_sdu_check(aggr_grp_t *grp, aggr_port_t *port) 2607 { 2608 uint_t port_sdu_max; 2609 2610 mac_sdu_get(port->lp_mh, NULL, &port_sdu_max); 2611 return (port_sdu_max >= grp->lg_max_sdu); 2612 } 2613 2614 /* 2615 * Returns the maximum margin according to the margin of the constituent ports. 2616 */ 2617 static uint32_t 2618 aggr_grp_max_margin(aggr_grp_t *grp) 2619 { 2620 uint32_t margin = UINT32_MAX; 2621 aggr_port_t *port; 2622 2623 ASSERT(grp->lg_mh == NULL); 2624 ASSERT(grp->lg_ports != NULL); 2625 2626 for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 2627 if (margin > port->lp_margin) 2628 margin = port->lp_margin; 2629 } 2630 2631 grp->lg_margin = margin; 2632 return (margin); 2633 } 2634 2635 /* 2636 * Checks if the maximum margin of the specified port is compatible 2637 * with the maximum margin of the specified aggregation group, returns 2638 * B_TRUE if it is, B_FALSE otherwise. 2639 */ 2640 static boolean_t 2641 aggr_grp_margin_check(aggr_grp_t *grp, aggr_port_t *port) 2642 { 2643 if (port->lp_margin >= grp->lg_margin) 2644 return (B_TRUE); 2645 2646 /* 2647 * See whether the current margin value is allowed to be changed to 2648 * the new value. 2649 */ 2650 if (!mac_margin_update(grp->lg_mh, port->lp_margin)) 2651 return (B_FALSE); 2652 2653 grp->lg_margin = port->lp_margin; 2654 return (B_TRUE); 2655 } 2656 2657 /* 2658 * Set MTU on individual ports of an aggregation group 2659 */ 2660 static int 2661 aggr_set_port_sdu(aggr_grp_t *grp, aggr_port_t *port, uint32_t sdu, 2662 uint32_t *old_mtu) 2663 { 2664 boolean_t removed = B_FALSE; 2665 mac_perim_handle_t mph; 2666 mac_diag_t diag; 2667 int err, rv, retry = 0; 2668 2669 if (port->lp_mah != NULL) { 2670 (void) mac_unicast_remove(port->lp_mch, port->lp_mah); 2671 port->lp_mah = NULL; 2672 removed = B_TRUE; 2673 } 2674 err = mac_set_mtu(port->lp_mh, sdu, old_mtu); 2675 try_again: 2676 if (removed && (rv = mac_unicast_add(port->lp_mch, NULL, 2677 MAC_UNICAST_PRIMARY | MAC_UNICAST_DISABLE_TX_VID_CHECK, 2678 &port->lp_mah, 0, &diag)) != 0) { 2679 /* 2680 * following is a workaround for a bug in 'bge' driver. 2681 * See CR 6794654 for more information and this work around 2682 * will be removed once the CR is fixed. 2683 */ 2684 if (rv == EIO && retry++ < 3) { 2685 delay(2 * hz); 2686 goto try_again; 2687 } 2688 /* 2689 * if mac_unicast_add() failed while setting the MTU, 2690 * detach the port from the group. 2691 */ 2692 mac_perim_enter_by_mh(port->lp_mh, &mph); 2693 (void) aggr_grp_detach_port(grp, port); 2694 mac_perim_exit(mph); 2695 cmn_err(CE_WARN, "Unable to restart the port %s while " 2696 "setting MTU. Detaching the port from the aggregation.", 2697 mac_client_name(port->lp_mch)); 2698 } 2699 return (err); 2700 } 2701 2702 static int 2703 aggr_sdu_update(aggr_grp_t *grp, uint32_t sdu) 2704 { 2705 int err = 0, i, rv; 2706 aggr_port_t *port; 2707 uint32_t *mtu; 2708 2709 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 2710 2711 /* 2712 * If the MTU being set is equal to aggr group's maximum 2713 * allowable value, then there is nothing to change 2714 */ 2715 if (sdu == grp->lg_max_sdu) 2716 return (0); 2717 2718 /* 0 is aggr group's min sdu */ 2719 if (sdu == 0) 2720 return (EINVAL); 2721 2722 mtu = kmem_alloc(sizeof (uint32_t) * grp->lg_nports, KM_SLEEP); 2723 for (port = grp->lg_ports, i = 0; port != NULL && err == 0; 2724 port = port->lp_next, i++) { 2725 err = aggr_set_port_sdu(grp, port, sdu, mtu + i); 2726 } 2727 if (err != 0) { 2728 /* recover from error: reset the mtus of the ports */ 2729 aggr_port_t *tmp; 2730 2731 for (tmp = grp->lg_ports, i = 0; tmp != port; 2732 tmp = tmp->lp_next, i++) { 2733 (void) aggr_set_port_sdu(grp, tmp, *(mtu + i), NULL); 2734 } 2735 goto bail; 2736 } 2737 grp->lg_max_sdu = aggr_grp_max_sdu(grp); 2738 rv = mac_maxsdu_update(grp->lg_mh, grp->lg_max_sdu); 2739 ASSERT(rv == 0); 2740 bail: 2741 kmem_free(mtu, sizeof (uint32_t) * grp->lg_nports); 2742 return (err); 2743 } 2744 2745 /* 2746 * Callback functions for set/get of properties 2747 */ 2748 /*ARGSUSED*/ 2749 static int 2750 aggr_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, 2751 uint_t pr_valsize, const void *pr_val) 2752 { 2753 int err = ENOTSUP; 2754 aggr_grp_t *grp = m_driver; 2755 2756 switch (pr_num) { 2757 case MAC_PROP_MTU: { 2758 uint32_t mtu; 2759 2760 if (pr_valsize < sizeof (mtu)) { 2761 err = EINVAL; 2762 break; 2763 } 2764 bcopy(pr_val, &mtu, sizeof (mtu)); 2765 err = aggr_sdu_update(grp, mtu); 2766 break; 2767 } 2768 default: 2769 break; 2770 } 2771 return (err); 2772 } 2773 2774 typedef struct rboundary { 2775 uint32_t bval; 2776 int btype; 2777 } rboundary_t; 2778 2779 /* 2780 * This function finds the intersection of mtu ranges stored in arrays - 2781 * mrange[0] ... mrange[mcount -1]. It returns the intersection in rval. 2782 * Individual arrays are assumed to contain non-overlapping ranges. 2783 * Algorithm: 2784 * A range has two boundaries - min and max. We scan all arrays and store 2785 * each boundary as a separate element in a temporary array. We also store 2786 * the boundary types, min or max, as +1 or -1 respectively in the temporary 2787 * array. Then we sort the temporary array in ascending order. We scan the 2788 * sorted array from lower to higher values and keep a cumulative sum of 2789 * boundary types. Element in the temporary array for which the sum reaches 2790 * mcount is a min boundary of a range in the result and next element will be 2791 * max boundary. 2792 * 2793 * Example for mcount = 3, 2794 * 2795 * ----|_________|-------|_______|----|__|------ mrange[0] 2796 * 2797 * -------|________|--|____________|-----|___|-- mrange[1] 2798 * 2799 * --------|________________|-------|____|------ mrange[2] 2800 * 2801 * 3 2 1 2802 * \|/ 2803 * 1 23 2 1 2 3 2 1 01 2 V 0 <- the sum 2804 * ----|--||-----|-|--|--|--|----|-||-|--|---|-- sorted array 2805 * 2806 * same min and max 2807 * V 2808 * --------|_____|-------|__|------------|------ intersecting ranges 2809 */ 2810 void 2811 aggr_mtu_range_intersection(mac_propval_range_t **mrange, int mcount, 2812 mac_propval_uint32_range_t **prval, int *prmaxcnt, int *prcount) 2813 { 2814 mac_propval_uint32_range_t *rval, *ur; 2815 int rmaxcnt, rcount; 2816 size_t sz_range32; 2817 rboundary_t *ta; /* temporary array */ 2818 rboundary_t temp; 2819 boolean_t range_started = B_FALSE; 2820 int i, j, m, sum; 2821 2822 sz_range32 = sizeof (mac_propval_uint32_range_t); 2823 2824 for (i = 0, rmaxcnt = 0; i < mcount; i++) 2825 rmaxcnt += mrange[i]->mpr_count; 2826 2827 /* Allocate enough space to store the results */ 2828 rval = kmem_alloc(rmaxcnt * sz_range32, KM_SLEEP); 2829 2830 /* Number of boundaries are twice as many as ranges */ 2831 ta = kmem_alloc(2 * rmaxcnt * sizeof (rboundary_t), KM_SLEEP); 2832 2833 for (i = 0, m = 0; i < mcount; i++) { 2834 ur = &(mrange[i]->mpr_range_uint32[0]); 2835 for (j = 0; j < mrange[i]->mpr_count; j++) { 2836 ta[m].bval = ur[j].mpur_min; 2837 ta[m++].btype = 1; 2838 ta[m].bval = ur[j].mpur_max; 2839 ta[m++].btype = -1; 2840 } 2841 } 2842 2843 /* 2844 * Sort the temporary array in ascending order of bval; 2845 * if boundary values are same then sort on btype. 2846 */ 2847 for (i = 0; i < m-1; i++) { 2848 for (j = i+1; j < m; j++) { 2849 if ((ta[i].bval > ta[j].bval) || 2850 ((ta[i].bval == ta[j].bval) && 2851 (ta[i].btype < ta[j].btype))) { 2852 temp = ta[i]; 2853 ta[i] = ta[j]; 2854 ta[j] = temp; 2855 } 2856 } 2857 } 2858 2859 /* Walk through temporary array to find all ranges in the results */ 2860 for (i = 0, sum = 0, rcount = 0; i < m; i++) { 2861 sum += ta[i].btype; 2862 if (sum == mcount) { 2863 rval[rcount].mpur_min = ta[i].bval; 2864 range_started = B_TRUE; 2865 } else if (sum < mcount && range_started) { 2866 rval[rcount++].mpur_max = ta[i].bval; 2867 range_started = B_FALSE; 2868 } 2869 } 2870 2871 *prval = rval; 2872 *prmaxcnt = rmaxcnt; 2873 *prcount = rcount; 2874 } 2875 2876 /* 2877 * Returns the mtu ranges which could be supported by aggr group. 2878 * prmaxcnt returns the size of the buffer prval, prcount returns 2879 * the number of valid entries in prval. Caller is responsible 2880 * for freeing up prval. 2881 */ 2882 int 2883 aggr_grp_possible_mtu_range(aggr_grp_t *grp, mac_propval_uint32_range_t **prval, 2884 int *prmaxcnt, int *prcount) 2885 { 2886 mac_propval_range_t **vals; 2887 aggr_port_t *port; 2888 mac_perim_handle_t mph; 2889 uint_t i, numr; 2890 int err = 0; 2891 size_t sz_propval, sz_range32; 2892 size_t size; 2893 2894 sz_propval = sizeof (mac_propval_range_t); 2895 sz_range32 = sizeof (mac_propval_uint32_range_t); 2896 2897 ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 2898 2899 vals = kmem_zalloc(sizeof (mac_propval_range_t *) * grp->lg_nports, 2900 KM_SLEEP); 2901 2902 for (port = grp->lg_ports, i = 0; port != NULL; 2903 port = port->lp_next, i++) { 2904 2905 size = sz_propval; 2906 vals[i] = kmem_alloc(size, KM_SLEEP); 2907 vals[i]->mpr_count = 1; 2908 2909 mac_perim_enter_by_mh(port->lp_mh, &mph); 2910 2911 err = mac_prop_info(port->lp_mh, MAC_PROP_MTU, NULL, 2912 NULL, 0, vals[i], NULL); 2913 if (err == ENOSPC) { 2914 /* 2915 * Not enough space to hold all ranges. 2916 * Allocate extra space as indicated and retry. 2917 */ 2918 numr = vals[i]->mpr_count; 2919 kmem_free(vals[i], sz_propval); 2920 size = sz_propval + (numr - 1) * sz_range32; 2921 vals[i] = kmem_alloc(size, KM_SLEEP); 2922 vals[i]->mpr_count = numr; 2923 err = mac_prop_info(port->lp_mh, MAC_PROP_MTU, NULL, 2924 NULL, 0, vals[i], NULL); 2925 ASSERT(err != ENOSPC); 2926 } 2927 mac_perim_exit(mph); 2928 if (err != 0) { 2929 kmem_free(vals[i], size); 2930 vals[i] = NULL; 2931 break; 2932 } 2933 } 2934 2935 /* 2936 * if any of the underlying ports does not support changing MTU then 2937 * just return ENOTSUP 2938 */ 2939 if (port != NULL) { 2940 ASSERT(err != 0); 2941 goto done; 2942 } 2943 2944 aggr_mtu_range_intersection(vals, grp->lg_nports, prval, prmaxcnt, 2945 prcount); 2946 2947 done: 2948 for (i = 0; i < grp->lg_nports; i++) { 2949 if (vals[i] != NULL) { 2950 numr = vals[i]->mpr_count; 2951 size = sz_propval + (numr - 1) * sz_range32; 2952 kmem_free(vals[i], size); 2953 } 2954 } 2955 2956 kmem_free(vals, sizeof (mac_propval_range_t *) * grp->lg_nports); 2957 return (err); 2958 } 2959 2960 static void 2961 aggr_m_propinfo(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, 2962 mac_prop_info_handle_t prh) 2963 { 2964 aggr_grp_t *grp = m_driver; 2965 mac_propval_uint32_range_t *rval = NULL; 2966 int i, rcount, rmaxcnt; 2967 int err = 0; 2968 2969 _NOTE(ARGUNUSED(pr_name)); 2970 2971 switch (pr_num) { 2972 case MAC_PROP_MTU: 2973 2974 err = aggr_grp_possible_mtu_range(grp, &rval, &rmaxcnt, 2975 &rcount); 2976 if (err != 0) { 2977 ASSERT(rval == NULL); 2978 return; 2979 } 2980 for (i = 0; i < rcount; i++) { 2981 mac_prop_info_set_range_uint32(prh, 2982 rval[i].mpur_min, rval[i].mpur_max); 2983 } 2984 kmem_free(rval, sizeof (mac_propval_uint32_range_t) * rmaxcnt); 2985 break; 2986 } 2987 } 2988