17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5f12af565Snd99603 * Common Development and Distribution License (the "License"). 6f12af565Snd99603 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*2ac91f16Smeem * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 267c478bd9Sstevel@tonic-gate * IEEE 802.3ad Link Aggregation - LACP & Marker Protocol processing. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 31da14cebeSEric Cheng #include <sys/callb.h> 327c478bd9Sstevel@tonic-gate #include <sys/conf.h> 337c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 34da14cebeSEric Cheng #include <sys/disp.h> 357c478bd9Sstevel@tonic-gate #include <sys/list.h> 367c478bd9Sstevel@tonic-gate #include <sys/ksynch.h> 377c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 387c478bd9Sstevel@tonic-gate #include <sys/stream.h> 397c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 407c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 417c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 427c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 437c478bd9Sstevel@tonic-gate #include <sys/stat.h> 447c478bd9Sstevel@tonic-gate #include <sys/byteorder.h> 457c478bd9Sstevel@tonic-gate #include <sys/strsun.h> 467c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h> 47f562e45bSRamesh Kumar Katla #include <sys/sdt.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include <sys/aggr.h> 507c478bd9Sstevel@tonic-gate #include <sys/aggr_impl.h> 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate static struct ether_addr etherzeroaddr = { 537c478bd9Sstevel@tonic-gate 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 547c478bd9Sstevel@tonic-gate }; 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate /* 577c478bd9Sstevel@tonic-gate * Slow_Protocol_Multicast address, as per IEEE 802.3ad spec. 587c478bd9Sstevel@tonic-gate */ 597c478bd9Sstevel@tonic-gate static struct ether_addr slow_multicast_addr = { 607c478bd9Sstevel@tonic-gate 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 617c478bd9Sstevel@tonic-gate }; 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate #ifdef DEBUG 647c478bd9Sstevel@tonic-gate /* LACP state machine debugging support */ 657c478bd9Sstevel@tonic-gate static uint32_t aggr_lacp_debug = 0; 667c478bd9Sstevel@tonic-gate #define AGGR_LACP_DBG(x) if (aggr_lacp_debug) { (void) printf x; } 677c478bd9Sstevel@tonic-gate #else 687c478bd9Sstevel@tonic-gate #define AGGR_LACP_DBG(x) {} 697c478bd9Sstevel@tonic-gate #endif /* DEBUG */ 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate #define NSECS_PER_SEC 1000000000ll 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate /* used by lacp_misconfig_walker() */ 747c478bd9Sstevel@tonic-gate typedef struct lacp_misconfig_check_state_s { 757c478bd9Sstevel@tonic-gate aggr_port_t *cs_portp; 767c478bd9Sstevel@tonic-gate boolean_t cs_found; 777c478bd9Sstevel@tonic-gate } lacp_misconfig_check_state_t; 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate static const char *lacp_receive_str[] = LACP_RECEIVE_STATE_STRINGS; 807c478bd9Sstevel@tonic-gate static const char *lacp_periodic_str[] = LACP_PERIODIC_STRINGS; 817c478bd9Sstevel@tonic-gate static const char *lacp_mux_str[] = LACP_MUX_STRINGS; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate static uint16_t lacp_port_priority = 0x1000; 847c478bd9Sstevel@tonic-gate static uint16_t lacp_system_priority = 0x1000; 857c478bd9Sstevel@tonic-gate 86f12af565Snd99603 /* 87f12af565Snd99603 * Maintains a list of all ports in ATTACHED state. This information 88f12af565Snd99603 * is used to detect misconfiguration. 89f12af565Snd99603 */ 90f12af565Snd99603 typedef struct lacp_sel_ports { 91d62bc4baSyz147064 datalink_id_t sp_grp_linkid; 92d62bc4baSyz147064 datalink_id_t sp_linkid; 9389842603Sseb /* Note: sp_partner_system must be 2-byte aligned */ 94f12af565Snd99603 struct ether_addr sp_partner_system; 95f12af565Snd99603 uint32_t sp_partner_key; 96f12af565Snd99603 struct lacp_sel_ports *sp_next; 97f12af565Snd99603 } lacp_sel_ports_t; 98f12af565Snd99603 99f12af565Snd99603 static lacp_sel_ports_t *sel_ports = NULL; 100f12af565Snd99603 static kmutex_t lacp_sel_lock; 101f12af565Snd99603 1027c478bd9Sstevel@tonic-gate static void periodic_timer_pop(void *); 103da14cebeSEric Cheng static void periodic_timer_pop_handler(aggr_port_t *); 1047c478bd9Sstevel@tonic-gate static void lacp_xmit_sm(aggr_port_t *); 1057c478bd9Sstevel@tonic-gate static void lacp_periodic_sm(aggr_port_t *); 1067c478bd9Sstevel@tonic-gate static void fill_lacp_pdu(aggr_port_t *, lacp_t *); 1077c478bd9Sstevel@tonic-gate static void fill_lacp_ether(aggr_port_t *, struct ether_header *); 1087c478bd9Sstevel@tonic-gate static void lacp_on(aggr_port_t *); 1097c478bd9Sstevel@tonic-gate static void lacp_off(aggr_port_t *); 1107c478bd9Sstevel@tonic-gate static boolean_t valid_lacp_pdu(aggr_port_t *, lacp_t *); 1117c478bd9Sstevel@tonic-gate static void lacp_receive_sm(aggr_port_t *, lacp_t *); 1127c478bd9Sstevel@tonic-gate static void aggr_set_coll_dist(aggr_port_t *, boolean_t); 1137c478bd9Sstevel@tonic-gate static void start_wait_while_timer(aggr_port_t *); 1147c478bd9Sstevel@tonic-gate static void stop_wait_while_timer(aggr_port_t *); 1157c478bd9Sstevel@tonic-gate static void lacp_reset_port(aggr_port_t *); 1167c478bd9Sstevel@tonic-gate static void stop_current_while_timer(aggr_port_t *); 1177c478bd9Sstevel@tonic-gate static void current_while_timer_pop(void *); 118da14cebeSEric Cheng static void current_while_timer_pop_handler(aggr_port_t *); 1197c478bd9Sstevel@tonic-gate static void update_default_selected(aggr_port_t *); 1207c478bd9Sstevel@tonic-gate static boolean_t update_selected(aggr_port_t *, lacp_t *); 121f12af565Snd99603 static boolean_t lacp_sel_ports_add(aggr_port_t *); 122f12af565Snd99603 static void lacp_sel_ports_del(aggr_port_t *); 123da14cebeSEric Cheng static void wait_while_timer_pop(void *); 124da14cebeSEric Cheng static void wait_while_timer_pop_handler(aggr_port_t *); 125f12af565Snd99603 126f12af565Snd99603 void 127f12af565Snd99603 aggr_lacp_init(void) 128f12af565Snd99603 { 129f12af565Snd99603 mutex_init(&lacp_sel_lock, NULL, MUTEX_DEFAULT, NULL); 130f12af565Snd99603 } 131f12af565Snd99603 132f12af565Snd99603 void 133f12af565Snd99603 aggr_lacp_fini(void) 134f12af565Snd99603 { 135f12af565Snd99603 mutex_destroy(&lacp_sel_lock); 136f12af565Snd99603 } 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* 139da14cebeSEric Cheng * The following functions are used for handling LACP timers. 140da14cebeSEric Cheng * 141da14cebeSEric Cheng * Note that we cannot fully rely on the aggr's mac perimeter in the timeout 142da14cebeSEric Cheng * handler routine, otherwise it may cause deadlock with the untimeout() call 143da14cebeSEric Cheng * which is usually called with the mac perimeter held. Instead, a 144da14cebeSEric Cheng * lacp_timer_lock mutex is introduced, which protects a bitwise flag 145da14cebeSEric Cheng * (lacp_timer_bits). This flag is set/cleared by timeout()/stop_timer() 146da14cebeSEric Cheng * routines and is checked by a dedicated thread, that executes the real 147da14cebeSEric Cheng * timeout operation. 148da14cebeSEric Cheng */ 149da14cebeSEric Cheng static void 150da14cebeSEric Cheng aggr_port_timer_thread(void *arg) 151da14cebeSEric Cheng { 152da14cebeSEric Cheng aggr_port_t *port = arg; 153da14cebeSEric Cheng aggr_lacp_port_t *pl = &port->lp_lacp; 154da14cebeSEric Cheng aggr_grp_t *grp = port->lp_grp; 155da14cebeSEric Cheng uint32_t lacp_timer_bits; 156da14cebeSEric Cheng mac_perim_handle_t mph; 157da14cebeSEric Cheng callb_cpr_t cprinfo; 158da14cebeSEric Cheng 159da14cebeSEric Cheng CALLB_CPR_INIT(&cprinfo, &pl->lacp_timer_lock, callb_generic_cpr, 160da14cebeSEric Cheng "aggr_port_timer_thread"); 161da14cebeSEric Cheng 162da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 163da14cebeSEric Cheng 164da14cebeSEric Cheng for (;;) { 165da14cebeSEric Cheng 166da14cebeSEric Cheng if ((lacp_timer_bits = pl->lacp_timer_bits) == 0) { 167da14cebeSEric Cheng CALLB_CPR_SAFE_BEGIN(&cprinfo); 168da14cebeSEric Cheng cv_wait(&pl->lacp_timer_cv, &pl->lacp_timer_lock); 169da14cebeSEric Cheng CALLB_CPR_SAFE_END(&cprinfo, &pl->lacp_timer_lock); 170da14cebeSEric Cheng continue; 171da14cebeSEric Cheng } 172da14cebeSEric Cheng pl->lacp_timer_bits = 0; 173da14cebeSEric Cheng 174da14cebeSEric Cheng if (lacp_timer_bits & LACP_THREAD_EXIT) 175da14cebeSEric Cheng break; 176da14cebeSEric Cheng 177da14cebeSEric Cheng if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT) 178da14cebeSEric Cheng pl->periodic_timer.id = 0; 179da14cebeSEric Cheng if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT) 180da14cebeSEric Cheng pl->wait_while_timer.id = 0; 181da14cebeSEric Cheng if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT) 182da14cebeSEric Cheng pl->current_while_timer.id = 0; 183da14cebeSEric Cheng 184da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 185da14cebeSEric Cheng 186da14cebeSEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph); 187da14cebeSEric Cheng if (port->lp_closing) { 188da14cebeSEric Cheng mac_perim_exit(mph); 189da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 190da14cebeSEric Cheng break; 191da14cebeSEric Cheng } 192da14cebeSEric Cheng 193da14cebeSEric Cheng if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT) 194da14cebeSEric Cheng periodic_timer_pop_handler(port); 195da14cebeSEric Cheng if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT) 196da14cebeSEric Cheng wait_while_timer_pop_handler(port); 197da14cebeSEric Cheng if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT) 198da14cebeSEric Cheng current_while_timer_pop_handler(port); 199da14cebeSEric Cheng mac_perim_exit(mph); 200da14cebeSEric Cheng 201da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 202da14cebeSEric Cheng if (pl->lacp_timer_bits & LACP_THREAD_EXIT) 203da14cebeSEric Cheng break; 204da14cebeSEric Cheng } 205da14cebeSEric Cheng 206da14cebeSEric Cheng pl->lacp_timer_bits = 0; 207da14cebeSEric Cheng pl->lacp_timer_thread = NULL; 208da14cebeSEric Cheng cv_broadcast(&pl->lacp_timer_cv); 209da14cebeSEric Cheng 210da14cebeSEric Cheng /* CALLB_CPR_EXIT drops the lock */ 211da14cebeSEric Cheng CALLB_CPR_EXIT(&cprinfo); 212da14cebeSEric Cheng 213da14cebeSEric Cheng /* 214da14cebeSEric Cheng * Release the reference of the grp so aggr_grp_delete() can call 215da14cebeSEric Cheng * mac_unregister() safely. 216da14cebeSEric Cheng */ 217da14cebeSEric Cheng aggr_grp_port_rele(port); 218da14cebeSEric Cheng thread_exit(); 219da14cebeSEric Cheng } 220da14cebeSEric Cheng 221da14cebeSEric Cheng /* 222f12af565Snd99603 * Set the port LACP state to SELECTED. Returns B_FALSE if the operation 223f12af565Snd99603 * could not be performed due to a memory allocation error, B_TRUE otherwise. 224f12af565Snd99603 */ 225f12af565Snd99603 static boolean_t 226f12af565Snd99603 lacp_port_select(aggr_port_t *portp) 227f12af565Snd99603 { 228da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 229f12af565Snd99603 230f12af565Snd99603 if (!lacp_sel_ports_add(portp)) 231f12af565Snd99603 return (B_FALSE); 232f12af565Snd99603 portp->lp_lacp.sm.selected = AGGR_SELECTED; 233f12af565Snd99603 return (B_TRUE); 234f12af565Snd99603 } 235f12af565Snd99603 236f12af565Snd99603 /* 237f12af565Snd99603 * Set the port LACP state to UNSELECTED. 238f12af565Snd99603 */ 239f12af565Snd99603 static void 240f12af565Snd99603 lacp_port_unselect(aggr_port_t *portp) 241f12af565Snd99603 { 242da14cebeSEric Cheng aggr_grp_t *grp = portp->lp_grp; 243da14cebeSEric Cheng 244da14cebeSEric Cheng ASSERT((grp->lg_mh == NULL) || MAC_PERIM_HELD(grp->lg_mh)); 245f12af565Snd99603 246f12af565Snd99603 lacp_sel_ports_del(portp); 247f12af565Snd99603 portp->lp_lacp.sm.selected = AGGR_UNSELECTED; 248f12af565Snd99603 } 249f12af565Snd99603 250f12af565Snd99603 /* 2517c478bd9Sstevel@tonic-gate * Initialize group specific LACP state and parameters. 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate void 2547c478bd9Sstevel@tonic-gate aggr_lacp_init_grp(aggr_grp_t *aggrp) 2557c478bd9Sstevel@tonic-gate { 2567c478bd9Sstevel@tonic-gate aggrp->aggr.PeriodicTimer = AGGR_LACP_TIMER_SHORT; 2577c478bd9Sstevel@tonic-gate aggrp->aggr.ActorSystemPriority = (uint16_t)lacp_system_priority; 2587c478bd9Sstevel@tonic-gate aggrp->aggr.CollectorMaxDelay = 10; 2597c478bd9Sstevel@tonic-gate aggrp->lg_lacp_mode = AGGR_LACP_OFF; 2607c478bd9Sstevel@tonic-gate aggrp->aggr.ready = B_FALSE; 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate /* 2647c478bd9Sstevel@tonic-gate * Complete LACP info initialization at port creation time. 2657c478bd9Sstevel@tonic-gate */ 2667c478bd9Sstevel@tonic-gate void 2677c478bd9Sstevel@tonic-gate aggr_lacp_init_port(aggr_port_t *portp) 2687c478bd9Sstevel@tonic-gate { 2697c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 2707c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 2717c478bd9Sstevel@tonic-gate 272da14cebeSEric Cheng ASSERT(aggrp->lg_mh == NULL || MAC_PERIM_HELD(aggrp->lg_mh)); 273da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_mh)); 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* actor port # */ 276392b1d6eSyz147064 pl->ActorPortNumber = portp->lp_portid; 277d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_init_port(%d): " 278d62bc4baSyz147064 "ActorPortNumber = 0x%x\n", portp->lp_linkid, 279ba2e4443Sseb pl->ActorPortNumber)); 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate pl->ActorPortPriority = (uint16_t)lacp_port_priority; 2827c478bd9Sstevel@tonic-gate pl->ActorPortAggrId = 0; /* aggregator id - not used */ 2837c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; /* need to transmit */ 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate pl->ActorAdminPortKey = aggrp->lg_key; 2867c478bd9Sstevel@tonic-gate pl->ActorOperPortKey = pl->ActorAdminPortKey; 287d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_init_port(%d) " 2887c478bd9Sstevel@tonic-gate "ActorAdminPortKey = 0x%x, ActorAdminPortKey = 0x%x\n", 289d62bc4baSyz147064 portp->lp_linkid, pl->ActorAdminPortKey, pl->ActorOperPortKey)); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate /* Actor admin. port state */ 2927c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.activity = B_FALSE; 2937c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.timeout = B_TRUE; 2947c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.aggregation = B_TRUE; 2957c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.sync = B_FALSE; 2967c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.collecting = B_FALSE; 2977c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.distributing = B_FALSE; 2987c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.defaulted = B_FALSE; 2997c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.expired = B_FALSE; 3007c478bd9Sstevel@tonic-gate pl->ActorOperPortState = pl->ActorAdminPortState; 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate /* 3037c478bd9Sstevel@tonic-gate * Partner Administrative Information 3047c478bd9Sstevel@tonic-gate * (All initialized to zero except for the following) 3057c478bd9Sstevel@tonic-gate * Fast Timeouts. 3067c478bd9Sstevel@tonic-gate */ 3077c478bd9Sstevel@tonic-gate pl->PartnerAdminPortState.bit.timeout = 3087c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.timeout = B_TRUE; 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */ 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate /* 3137c478bd9Sstevel@tonic-gate * State machine information. 3147c478bd9Sstevel@tonic-gate */ 3157c478bd9Sstevel@tonic-gate pl->sm.lacp_on = B_FALSE; /* LACP Off default */ 3167c478bd9Sstevel@tonic-gate pl->sm.begin = B_TRUE; /* Prevents transmissions */ 3177c478bd9Sstevel@tonic-gate pl->sm.lacp_enabled = B_FALSE; 3187c478bd9Sstevel@tonic-gate pl->sm.port_enabled = B_FALSE; /* Link Down */ 3197c478bd9Sstevel@tonic-gate pl->sm.actor_churn = B_FALSE; 3207c478bd9Sstevel@tonic-gate pl->sm.partner_churn = B_FALSE; 3217c478bd9Sstevel@tonic-gate pl->sm.ready_n = B_FALSE; 3227c478bd9Sstevel@tonic-gate pl->sm.port_moved = B_FALSE; 3237c478bd9Sstevel@tonic-gate 324f12af565Snd99603 lacp_port_unselect(portp); 325f12af565Snd99603 3267c478bd9Sstevel@tonic-gate pl->sm.periodic_state = LACP_NO_PERIODIC; 3277c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_INITIALIZE; 3287c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 3297c478bd9Sstevel@tonic-gate pl->sm.churn_state = LACP_NO_ACTOR_CHURN; 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate /* 3327c478bd9Sstevel@tonic-gate * Timer information. 3337c478bd9Sstevel@tonic-gate */ 3347c478bd9Sstevel@tonic-gate pl->current_while_timer.id = 0; 3357c478bd9Sstevel@tonic-gate pl->current_while_timer.val = SHORT_TIMEOUT_TIME; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate pl->periodic_timer.id = 0; 3387c478bd9Sstevel@tonic-gate pl->periodic_timer.val = FAST_PERIODIC_TIME; 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate pl->wait_while_timer.id = 0; 3417c478bd9Sstevel@tonic-gate pl->wait_while_timer.val = AGGREGATE_WAIT_TIME; 342da14cebeSEric Cheng 343da14cebeSEric Cheng pl->lacp_timer_bits = 0; 344da14cebeSEric Cheng 345da14cebeSEric Cheng mutex_init(&pl->lacp_timer_lock, NULL, MUTEX_DRIVER, NULL); 346da14cebeSEric Cheng cv_init(&pl->lacp_timer_cv, NULL, CV_DRIVER, NULL); 347da14cebeSEric Cheng 348da14cebeSEric Cheng pl->lacp_timer_thread = thread_create(NULL, 0, aggr_port_timer_thread, 349da14cebeSEric Cheng portp, 0, &p0, TS_RUN, minclsyspri); 350da14cebeSEric Cheng 351da14cebeSEric Cheng /* 352da14cebeSEric Cheng * Hold a reference of the grp and the port and this reference will 353da14cebeSEric Cheng * be release when the thread exits. 354da14cebeSEric Cheng * 355da14cebeSEric Cheng * The reference on the port is used for aggr_port_delete() to 356da14cebeSEric Cheng * continue without waiting for the thread to exit; the reference 357da14cebeSEric Cheng * on the grp is used for aggr_grp_delete() to wait for the thread 358da14cebeSEric Cheng * to exit before calling mac_unregister(). 359da14cebeSEric Cheng */ 360da14cebeSEric Cheng aggr_grp_port_hold(portp); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate * Port initialization when we need to 3657c478bd9Sstevel@tonic-gate * turn LACP on/off, etc. Not everything is 3667c478bd9Sstevel@tonic-gate * reset like in the above routine. 3677c478bd9Sstevel@tonic-gate * Do NOT modify things like link status. 3687c478bd9Sstevel@tonic-gate */ 3697c478bd9Sstevel@tonic-gate static void 3707c478bd9Sstevel@tonic-gate lacp_reset_port(aggr_port_t *portp) 3717c478bd9Sstevel@tonic-gate { 3727c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 3737c478bd9Sstevel@tonic-gate 374da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; /* need to transmit */ 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* reset operational port state */ 3797c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.timeout = 3807c478bd9Sstevel@tonic-gate pl->ActorAdminPortState.bit.timeout; 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.sync = B_FALSE; 3837c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.collecting = B_FALSE; 3847c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing = B_FALSE; 3857c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.defaulted = B_TRUE; 3867c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_FALSE; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.timeout = B_TRUE; /* fast t/o */ 3897c478bd9Sstevel@tonic-gate pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */ 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate /* 3927c478bd9Sstevel@tonic-gate * State machine information. 3937c478bd9Sstevel@tonic-gate */ 3947c478bd9Sstevel@tonic-gate pl->sm.begin = B_TRUE; /* Prevents transmissions */ 3957c478bd9Sstevel@tonic-gate pl->sm.actor_churn = B_FALSE; 3967c478bd9Sstevel@tonic-gate pl->sm.partner_churn = B_FALSE; 3977c478bd9Sstevel@tonic-gate pl->sm.ready_n = B_FALSE; 398f12af565Snd99603 399f12af565Snd99603 lacp_port_unselect(portp); 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate pl->sm.periodic_state = LACP_NO_PERIODIC; 4027c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_INITIALIZE; 4037c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 4047c478bd9Sstevel@tonic-gate pl->sm.churn_state = LACP_NO_ACTOR_CHURN; 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate /* 4077c478bd9Sstevel@tonic-gate * Timer information. 4087c478bd9Sstevel@tonic-gate */ 4097c478bd9Sstevel@tonic-gate pl->current_while_timer.val = SHORT_TIMEOUT_TIME; 4107c478bd9Sstevel@tonic-gate pl->periodic_timer.val = FAST_PERIODIC_TIME; 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate static void 4147c478bd9Sstevel@tonic-gate aggr_lacp_mcast_on(aggr_port_t *port) 4157c478bd9Sstevel@tonic-gate { 416da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh)); 417da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh)); 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 4207c478bd9Sstevel@tonic-gate return; 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate (void) aggr_port_multicst(port, B_TRUE, 4237c478bd9Sstevel@tonic-gate (uchar_t *)&slow_multicast_addr); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate static void 4277c478bd9Sstevel@tonic-gate aggr_lacp_mcast_off(aggr_port_t *port) 4287c478bd9Sstevel@tonic-gate { 429da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh)); 430da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh)); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate if (port->lp_state != AGGR_PORT_STATE_ATTACHED) 4337c478bd9Sstevel@tonic-gate return; 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate (void) aggr_port_multicst(port, B_FALSE, 4367c478bd9Sstevel@tonic-gate (uchar_t *)&slow_multicast_addr); 4377c478bd9Sstevel@tonic-gate } 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate static void 4407c478bd9Sstevel@tonic-gate start_periodic_timer(aggr_port_t *portp) 4417c478bd9Sstevel@tonic-gate { 442da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 4437c478bd9Sstevel@tonic-gate 444da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 445da14cebeSEric Cheng 446da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 447da14cebeSEric Cheng if (pl->periodic_timer.id == 0) { 448da14cebeSEric Cheng pl->periodic_timer.id = timeout(periodic_timer_pop, portp, 4497c478bd9Sstevel@tonic-gate drv_usectohz(1000000 * portp->lp_lacp.periodic_timer.val)); 4507c478bd9Sstevel@tonic-gate } 451da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate static void 4557c478bd9Sstevel@tonic-gate stop_periodic_timer(aggr_port_t *portp) 4567c478bd9Sstevel@tonic-gate { 457da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 458da14cebeSEric Cheng timeout_id_t id; 4597c478bd9Sstevel@tonic-gate 460da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 461da14cebeSEric Cheng 462da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 463da14cebeSEric Cheng if ((id = pl->periodic_timer.id) != 0) { 464da14cebeSEric Cheng pl->lacp_timer_bits &= ~LACP_PERIODIC_TIMEOUT; 465da14cebeSEric Cheng pl->periodic_timer.id = 0; 4667c478bd9Sstevel@tonic-gate } 467da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 468da14cebeSEric Cheng 469da14cebeSEric Cheng if (id != 0) 470da14cebeSEric Cheng (void) untimeout(id); 4717c478bd9Sstevel@tonic-gate } 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate /* 4747c478bd9Sstevel@tonic-gate * When the timer pops, we arrive here to 4757c478bd9Sstevel@tonic-gate * clear out LACPDU count as well as transmit an 4767c478bd9Sstevel@tonic-gate * LACPDU. We then set the periodic state and let 4777c478bd9Sstevel@tonic-gate * the periodic state machine restart the timer. 4787c478bd9Sstevel@tonic-gate */ 4797c478bd9Sstevel@tonic-gate static void 480da14cebeSEric Cheng periodic_timer_pop(void *data) 4817c478bd9Sstevel@tonic-gate { 482da14cebeSEric Cheng aggr_port_t *portp = data; 483da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 4847c478bd9Sstevel@tonic-gate 485da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 486da14cebeSEric Cheng pl->lacp_timer_bits |= LACP_PERIODIC_TIMEOUT; 487da14cebeSEric Cheng cv_broadcast(&pl->lacp_timer_cv); 488da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 489da14cebeSEric Cheng } 490da14cebeSEric Cheng 491da14cebeSEric Cheng /* 492da14cebeSEric Cheng * When the timer pops, we arrive here to 493da14cebeSEric Cheng * clear out LACPDU count as well as transmit an 494da14cebeSEric Cheng * LACPDU. We then set the periodic state and let 495da14cebeSEric Cheng * the periodic state machine restart the timer. 496da14cebeSEric Cheng */ 497da14cebeSEric Cheng static void 498da14cebeSEric Cheng periodic_timer_pop_handler(aggr_port_t *portp) 499da14cebeSEric Cheng { 500da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 501da14cebeSEric Cheng 5027c478bd9Sstevel@tonic-gate portp->lp_lacp_stats.LACPDUsTx = 0; 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate /* current timestamp */ 5057c478bd9Sstevel@tonic-gate portp->lp_lacp.time = gethrtime(); 5067c478bd9Sstevel@tonic-gate portp->lp_lacp.NTT = B_TRUE; 5077c478bd9Sstevel@tonic-gate lacp_xmit_sm(portp); 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate /* 5107c478bd9Sstevel@tonic-gate * Set Periodic State machine state based on the 5117c478bd9Sstevel@tonic-gate * value of the Partner Operation Port State timeout 5127c478bd9Sstevel@tonic-gate * bit. 5137c478bd9Sstevel@tonic-gate */ 5147c478bd9Sstevel@tonic-gate if (portp->lp_lacp.PartnerOperPortState.bit.timeout) { 5157c478bd9Sstevel@tonic-gate portp->lp_lacp.periodic_timer.val = FAST_PERIODIC_TIME; 5167c478bd9Sstevel@tonic-gate portp->lp_lacp.sm.periodic_state = LACP_FAST_PERIODIC; 5177c478bd9Sstevel@tonic-gate } else { 5187c478bd9Sstevel@tonic-gate portp->lp_lacp.periodic_timer.val = SLOW_PERIODIC_TIME; 5197c478bd9Sstevel@tonic-gate portp->lp_lacp.sm.periodic_state = LACP_SLOW_PERIODIC; 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate lacp_periodic_sm(portp); 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate /* 5267c478bd9Sstevel@tonic-gate * Invoked from: 5277c478bd9Sstevel@tonic-gate * - startup upon aggregation 5287c478bd9Sstevel@tonic-gate * - when the periodic timer pops 5297c478bd9Sstevel@tonic-gate * - when the periodic timer value is changed 5307c478bd9Sstevel@tonic-gate * - when the port is attached or detached 5317c478bd9Sstevel@tonic-gate * - when LACP mode is changed. 5327c478bd9Sstevel@tonic-gate */ 5337c478bd9Sstevel@tonic-gate static void 5347c478bd9Sstevel@tonic-gate lacp_periodic_sm(aggr_port_t *portp) 5357c478bd9Sstevel@tonic-gate { 5367c478bd9Sstevel@tonic-gate lacp_periodic_state_t oldstate = portp->lp_lacp.sm.periodic_state; 5377c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 5387c478bd9Sstevel@tonic-gate 539da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 5427c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_on) { 5437c478bd9Sstevel@tonic-gate /* Stop timer whether it is running or not */ 5447c478bd9Sstevel@tonic-gate stop_periodic_timer(portp); 5457c478bd9Sstevel@tonic-gate pl->sm.periodic_state = LACP_NO_PERIODIC; 5467c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; 547d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_periodic_sm(%d):NO LACP " 548d62bc4baSyz147064 "%s--->%s\n", portp->lp_linkid, 5497c478bd9Sstevel@tonic-gate lacp_periodic_str[oldstate], 5507c478bd9Sstevel@tonic-gate lacp_periodic_str[pl->sm.periodic_state])); 5517c478bd9Sstevel@tonic-gate return; 5527c478bd9Sstevel@tonic-gate } 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate if (pl->sm.begin || !pl->sm.lacp_enabled || 5557c478bd9Sstevel@tonic-gate !pl->sm.port_enabled || 5567c478bd9Sstevel@tonic-gate !pl->ActorOperPortState.bit.activity && 5577c478bd9Sstevel@tonic-gate !pl->PartnerOperPortState.bit.activity) { 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* Stop timer whether it is running or not */ 5607c478bd9Sstevel@tonic-gate stop_periodic_timer(portp); 5617c478bd9Sstevel@tonic-gate pl->sm.periodic_state = LACP_NO_PERIODIC; 5627c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; 563d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_periodic_sm(%d):STOP %s--->%s\n", 564d62bc4baSyz147064 portp->lp_linkid, lacp_periodic_str[oldstate], 5657c478bd9Sstevel@tonic-gate lacp_periodic_str[pl->sm.periodic_state])); 5667c478bd9Sstevel@tonic-gate return; 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate /* 5707c478bd9Sstevel@tonic-gate * Startup with FAST_PERIODIC_TIME if no previous LACPDU 5717c478bd9Sstevel@tonic-gate * has been received. Then after we timeout, then it is 5727c478bd9Sstevel@tonic-gate * possible to go to SLOW_PERIODIC_TIME. 5737c478bd9Sstevel@tonic-gate */ 5747c478bd9Sstevel@tonic-gate if (pl->sm.periodic_state == LACP_NO_PERIODIC) { 5757c478bd9Sstevel@tonic-gate pl->periodic_timer.val = FAST_PERIODIC_TIME; 5767c478bd9Sstevel@tonic-gate pl->sm.periodic_state = LACP_FAST_PERIODIC; 5777c478bd9Sstevel@tonic-gate } else if ((pl->sm.periodic_state == LACP_SLOW_PERIODIC) && 5787c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.timeout) { 5797c478bd9Sstevel@tonic-gate /* 5807c478bd9Sstevel@tonic-gate * If we receive a bit indicating we are going to 5817c478bd9Sstevel@tonic-gate * fast periodic from slow periodic, stop the timer 5827c478bd9Sstevel@tonic-gate * and let the periodic_timer_pop routine deal 5837c478bd9Sstevel@tonic-gate * with reseting the periodic state and transmitting 5847c478bd9Sstevel@tonic-gate * a LACPDU. 5857c478bd9Sstevel@tonic-gate */ 5867c478bd9Sstevel@tonic-gate stop_periodic_timer(portp); 587da14cebeSEric Cheng periodic_timer_pop_handler(portp); 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* Rearm timer with value provided by partner */ 5917c478bd9Sstevel@tonic-gate start_periodic_timer(portp); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate /* 5957c478bd9Sstevel@tonic-gate * This routine transmits an LACPDU if lacp_enabled 5967c478bd9Sstevel@tonic-gate * is TRUE and if NTT is set. 5977c478bd9Sstevel@tonic-gate */ 5987c478bd9Sstevel@tonic-gate static void 5997c478bd9Sstevel@tonic-gate lacp_xmit_sm(aggr_port_t *portp) 6007c478bd9Sstevel@tonic-gate { 6017c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 6027c478bd9Sstevel@tonic-gate size_t len; 6037c478bd9Sstevel@tonic-gate mblk_t *mp; 6047c478bd9Sstevel@tonic-gate hrtime_t now, elapsed; 6057c478bd9Sstevel@tonic-gate 606da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 6097c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_on || !pl->NTT || !portp->lp_started) 6107c478bd9Sstevel@tonic-gate return; 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate /* 6137c478bd9Sstevel@tonic-gate * Do nothing if LACP has been turned off or if the 6147c478bd9Sstevel@tonic-gate * periodic state machine is not enabled. 6157c478bd9Sstevel@tonic-gate */ 6167c478bd9Sstevel@tonic-gate if ((pl->sm.periodic_state == LACP_NO_PERIODIC) || 6177c478bd9Sstevel@tonic-gate !pl->sm.lacp_enabled || pl->sm.begin) { 6187c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; 6197c478bd9Sstevel@tonic-gate return; 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate /* 6237c478bd9Sstevel@tonic-gate * If we have sent 5 Slow packets in the last second, avoid 6247c478bd9Sstevel@tonic-gate * sending any more here. No more than three LACPDUs may be transmitted 6257c478bd9Sstevel@tonic-gate * in any Fast_Periodic_Time interval. 6267c478bd9Sstevel@tonic-gate */ 6277c478bd9Sstevel@tonic-gate if (portp->lp_lacp_stats.LACPDUsTx >= 3) { 6287c478bd9Sstevel@tonic-gate /* 6297c478bd9Sstevel@tonic-gate * Grab the current time value and see if 6307c478bd9Sstevel@tonic-gate * more than 1 second has passed. If so, 6317c478bd9Sstevel@tonic-gate * reset the timestamp and clear the count. 6327c478bd9Sstevel@tonic-gate */ 6337c478bd9Sstevel@tonic-gate now = gethrtime(); 6347c478bd9Sstevel@tonic-gate elapsed = now - pl->time; 6357c478bd9Sstevel@tonic-gate if (elapsed > NSECS_PER_SEC) { 6367c478bd9Sstevel@tonic-gate portp->lp_lacp_stats.LACPDUsTx = 0; 6377c478bd9Sstevel@tonic-gate pl->time = now; 6387c478bd9Sstevel@tonic-gate } else { 6397c478bd9Sstevel@tonic-gate return; 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate } 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate len = sizeof (lacp_t) + sizeof (struct ether_header); 6447c478bd9Sstevel@tonic-gate mp = allocb(len, BPRI_MED); 6457c478bd9Sstevel@tonic-gate if (mp == NULL) 6467c478bd9Sstevel@tonic-gate return; 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + len; 6497c478bd9Sstevel@tonic-gate bzero(mp->b_rptr, len); 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr); 6527c478bd9Sstevel@tonic-gate fill_lacp_pdu(portp, 6537c478bd9Sstevel@tonic-gate (lacp_t *)(mp->b_rptr + sizeof (struct ether_header))); 6547c478bd9Sstevel@tonic-gate 6550dc2366fSVenugopal Iyer /* Send the packet over the first TX ring */ 6560dc2366fSVenugopal Iyer mp = mac_hwring_send_priv(portp->lp_mch, portp->lp_tx_rings[0], mp); 6570dc2366fSVenugopal Iyer if (mp != NULL) 6580dc2366fSVenugopal Iyer freemsg(mp); 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate pl->NTT = B_FALSE; 6617c478bd9Sstevel@tonic-gate portp->lp_lacp_stats.LACPDUsTx++; 6627c478bd9Sstevel@tonic-gate } 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate /* 6657c478bd9Sstevel@tonic-gate * Initialize the ethernet header of a LACP packet sent from the specified 6667c478bd9Sstevel@tonic-gate * port. 6677c478bd9Sstevel@tonic-gate */ 6687c478bd9Sstevel@tonic-gate static void 6697c478bd9Sstevel@tonic-gate fill_lacp_ether(aggr_port_t *port, struct ether_header *ether) 6707c478bd9Sstevel@tonic-gate { 6717c478bd9Sstevel@tonic-gate bcopy(port->lp_addr, (uint8_t *)&(ether->ether_shost), ETHERADDRL); 6727c478bd9Sstevel@tonic-gate bcopy(&slow_multicast_addr, (uint8_t *)&(ether->ether_dhost), 6737c478bd9Sstevel@tonic-gate ETHERADDRL); 6747c478bd9Sstevel@tonic-gate ether->ether_type = htons(ETHERTYPE_SLOW); 6757c478bd9Sstevel@tonic-gate } 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate static void 6787c478bd9Sstevel@tonic-gate fill_lacp_pdu(aggr_port_t *portp, lacp_t *lacp) 6797c478bd9Sstevel@tonic-gate { 6807c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 6817c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 682da14cebeSEric Cheng mac_perim_handle_t pmph; 6837c478bd9Sstevel@tonic-gate 684da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(aggrp->lg_mh)); 685da14cebeSEric Cheng mac_perim_enter_by_mh(portp->lp_mh, &pmph); 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate lacp->subtype = LACP_SUBTYPE; 6887c478bd9Sstevel@tonic-gate lacp->version = LACP_VERSION; 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate /* 6917c478bd9Sstevel@tonic-gate * Actor Information 6927c478bd9Sstevel@tonic-gate */ 6937c478bd9Sstevel@tonic-gate lacp->actor_info.tlv_type = ACTOR_TLV; 6947c478bd9Sstevel@tonic-gate lacp->actor_info.information_len = sizeof (link_info_t); 6957c478bd9Sstevel@tonic-gate lacp->actor_info.system_priority = 6967c478bd9Sstevel@tonic-gate htons(aggrp->aggr.ActorSystemPriority); 6977c478bd9Sstevel@tonic-gate bcopy(aggrp->lg_addr, (uchar_t *)&lacp->actor_info.system_id, 6987c478bd9Sstevel@tonic-gate ETHERADDRL); 6997c478bd9Sstevel@tonic-gate lacp->actor_info.key = htons(pl->ActorOperPortKey); 7007c478bd9Sstevel@tonic-gate lacp->actor_info.port_priority = htons(pl->ActorPortPriority); 7017c478bd9Sstevel@tonic-gate lacp->actor_info.port = htons(pl->ActorPortNumber); 7027c478bd9Sstevel@tonic-gate lacp->actor_info.state.state = pl->ActorOperPortState.state; 7037c478bd9Sstevel@tonic-gate 7047c478bd9Sstevel@tonic-gate /* 7057c478bd9Sstevel@tonic-gate * Partner Information 7067c478bd9Sstevel@tonic-gate */ 7077c478bd9Sstevel@tonic-gate lacp->partner_info.tlv_type = PARTNER_TLV; 7087c478bd9Sstevel@tonic-gate lacp->partner_info.information_len = sizeof (link_info_t); 7097c478bd9Sstevel@tonic-gate lacp->partner_info.system_priority = 7107c478bd9Sstevel@tonic-gate htons(pl->PartnerOperSysPriority); 7117c478bd9Sstevel@tonic-gate lacp->partner_info.system_id = pl->PartnerOperSystem; 7127c478bd9Sstevel@tonic-gate lacp->partner_info.key = htons(pl->PartnerOperKey); 7137c478bd9Sstevel@tonic-gate lacp->partner_info.port_priority = 7147c478bd9Sstevel@tonic-gate htons(pl->PartnerOperPortPriority); 7157c478bd9Sstevel@tonic-gate lacp->partner_info.port = htons(pl->PartnerOperPortNum); 7167c478bd9Sstevel@tonic-gate lacp->partner_info.state.state = pl->PartnerOperPortState.state; 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate /* Collector Information */ 7197c478bd9Sstevel@tonic-gate lacp->tlv_collector = COLLECTOR_TLV; 7207c478bd9Sstevel@tonic-gate lacp->collector_len = 0x10; 7217c478bd9Sstevel@tonic-gate lacp->collector_max_delay = htons(aggrp->aggr.CollectorMaxDelay); 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate /* Termination Information */ 7247c478bd9Sstevel@tonic-gate lacp->tlv_terminator = TERMINATOR_TLV; 7257c478bd9Sstevel@tonic-gate lacp->terminator_len = 0x0; 7267c478bd9Sstevel@tonic-gate 727da14cebeSEric Cheng mac_perim_exit(pmph); 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate /* 7317c478bd9Sstevel@tonic-gate * lacp_mux_sm - LACP mux state machine 7327c478bd9Sstevel@tonic-gate * This state machine is invoked from: 7337c478bd9Sstevel@tonic-gate * - startup upon aggregation 7347c478bd9Sstevel@tonic-gate * - from the Selection logic 7357c478bd9Sstevel@tonic-gate * - when the wait_while_timer pops 7367c478bd9Sstevel@tonic-gate * - when the aggregation MAC address is changed 7377c478bd9Sstevel@tonic-gate * - when receiving DL_NOTE_LINK_UP/DOWN 7387c478bd9Sstevel@tonic-gate * - when receiving DL_NOTE_AGGR_AVAIL/UNAVAIL 7397c478bd9Sstevel@tonic-gate * - when LACP mode is changed. 7407c478bd9Sstevel@tonic-gate * - when a DL_NOTE_SPEED is received 7417c478bd9Sstevel@tonic-gate */ 7427c478bd9Sstevel@tonic-gate static void 7437c478bd9Sstevel@tonic-gate lacp_mux_sm(aggr_port_t *portp) 7447c478bd9Sstevel@tonic-gate { 7457c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 7467c478bd9Sstevel@tonic-gate boolean_t NTT_updated = B_FALSE; 7477c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 7487c478bd9Sstevel@tonic-gate lacp_mux_state_t oldstate = pl->sm.mux_state; 7497c478bd9Sstevel@tonic-gate 750da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(aggrp->lg_mh)); 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 7537c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_on) { 7547c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 7557c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.sync = B_FALSE; 7567c478bd9Sstevel@tonic-gate 7577c478bd9Sstevel@tonic-gate if (pl->ActorOperPortState.bit.collecting || 7587c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing) { 759d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link: (%d): " 7607c478bd9Sstevel@tonic-gate "Collector_Distributor Disabled.\n", 761d62bc4baSyz147064 portp->lp_linkid)); 7627c478bd9Sstevel@tonic-gate } 7637c478bd9Sstevel@tonic-gate 7647c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.collecting = 7657c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing = B_FALSE; 7667c478bd9Sstevel@tonic-gate return; 7677c478bd9Sstevel@tonic-gate } 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate if (pl->sm.begin || !pl->sm.lacp_enabled) 7707c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 7717c478bd9Sstevel@tonic-gate 772f12af565Snd99603 again: 7737c478bd9Sstevel@tonic-gate /* determine next state, or return if state unchanged */ 7747c478bd9Sstevel@tonic-gate switch (pl->sm.mux_state) { 7757c478bd9Sstevel@tonic-gate case LACP_DETACHED: 7767c478bd9Sstevel@tonic-gate if (pl->sm.begin) { 7777c478bd9Sstevel@tonic-gate break; 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate if ((pl->sm.selected == AGGR_SELECTED) || 7817c478bd9Sstevel@tonic-gate (pl->sm.selected == AGGR_STANDBY)) { 7827c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_WAITING; 7837c478bd9Sstevel@tonic-gate break; 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate return; 7867c478bd9Sstevel@tonic-gate 7877c478bd9Sstevel@tonic-gate case LACP_WAITING: 7887c478bd9Sstevel@tonic-gate if (pl->sm.selected == AGGR_UNSELECTED) { 7897c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 7907c478bd9Sstevel@tonic-gate break; 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate if ((pl->sm.selected == AGGR_SELECTED) && aggrp->aggr.ready) { 7947c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_ATTACHED; 7957c478bd9Sstevel@tonic-gate break; 7967c478bd9Sstevel@tonic-gate } 7977c478bd9Sstevel@tonic-gate return; 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate case LACP_ATTACHED: 8007c478bd9Sstevel@tonic-gate if ((pl->sm.selected == AGGR_UNSELECTED) || 8017c478bd9Sstevel@tonic-gate (pl->sm.selected == AGGR_STANDBY)) { 8027c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_DETACHED; 8037c478bd9Sstevel@tonic-gate break; 8047c478bd9Sstevel@tonic-gate } 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate if ((pl->sm.selected == AGGR_SELECTED) && 8077c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync) { 8087c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_COLLECTING_DISTRIBUTING; 8097c478bd9Sstevel@tonic-gate break; 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate return; 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate case LACP_COLLECTING_DISTRIBUTING: 8147c478bd9Sstevel@tonic-gate if ((pl->sm.selected == AGGR_UNSELECTED) || 8157c478bd9Sstevel@tonic-gate (pl->sm.selected == AGGR_STANDBY) || 8167c478bd9Sstevel@tonic-gate !pl->PartnerOperPortState.bit.sync) { 8177c478bd9Sstevel@tonic-gate pl->sm.mux_state = LACP_ATTACHED; 8187c478bd9Sstevel@tonic-gate break; 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate return; 8217c478bd9Sstevel@tonic-gate } 8227c478bd9Sstevel@tonic-gate 823d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_mux_sm(%d):%s--->%s\n", 824d62bc4baSyz147064 portp->lp_linkid, lacp_mux_str[oldstate], 8257c478bd9Sstevel@tonic-gate lacp_mux_str[pl->sm.mux_state])); 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate /* perform actions on entering a new state */ 8287c478bd9Sstevel@tonic-gate switch (pl->sm.mux_state) { 8297c478bd9Sstevel@tonic-gate case LACP_DETACHED: 8307c478bd9Sstevel@tonic-gate if (pl->ActorOperPortState.bit.collecting || 8317c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing) { 832d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link: (%d): " 8337c478bd9Sstevel@tonic-gate "Collector_Distributor Disabled.\n", 834d62bc4baSyz147064 portp->lp_linkid)); 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate 8377c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.sync = 8387c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.collecting = B_FALSE; 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate /* Turn OFF Collector_Distributor */ 8417c478bd9Sstevel@tonic-gate aggr_set_coll_dist(portp, B_FALSE); 8427c478bd9Sstevel@tonic-gate 8437c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing = B_FALSE; 8447c478bd9Sstevel@tonic-gate NTT_updated = B_TRUE; 8457c478bd9Sstevel@tonic-gate break; 8467c478bd9Sstevel@tonic-gate 8477c478bd9Sstevel@tonic-gate case LACP_WAITING: 8487c478bd9Sstevel@tonic-gate start_wait_while_timer(portp); 8497c478bd9Sstevel@tonic-gate break; 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate case LACP_ATTACHED: 8527c478bd9Sstevel@tonic-gate if (pl->ActorOperPortState.bit.collecting || 8537c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing) { 854d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link: (%d): " 8557c478bd9Sstevel@tonic-gate "Collector_Distributor Disabled.\n", 856d62bc4baSyz147064 portp->lp_linkid)); 8577c478bd9Sstevel@tonic-gate } 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.sync = B_TRUE; 8607c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.collecting = B_FALSE; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate /* Turn OFF Collector_Distributor */ 8637c478bd9Sstevel@tonic-gate aggr_set_coll_dist(portp, B_FALSE); 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing = B_FALSE; 8667c478bd9Sstevel@tonic-gate NTT_updated = B_TRUE; 867f12af565Snd99603 if (pl->PartnerOperPortState.bit.sync) { 868f12af565Snd99603 /* 869f12af565Snd99603 * We had already received an updated sync from 870f12af565Snd99603 * the partner. Attempt to transition to 871f12af565Snd99603 * collecting/distributing now. 872f12af565Snd99603 */ 873f12af565Snd99603 goto again; 874f12af565Snd99603 } 8757c478bd9Sstevel@tonic-gate break; 8767c478bd9Sstevel@tonic-gate 8777c478bd9Sstevel@tonic-gate case LACP_COLLECTING_DISTRIBUTING: 8787c478bd9Sstevel@tonic-gate if (!pl->ActorOperPortState.bit.collecting && 8797c478bd9Sstevel@tonic-gate !pl->ActorOperPortState.bit.distributing) { 880d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link: (%d): " 8817c478bd9Sstevel@tonic-gate "Collector_Distributor Enabled.\n", 882d62bc4baSyz147064 portp->lp_linkid)); 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.distributing = B_TRUE; 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate /* Turn Collector_Distributor back ON */ 8877c478bd9Sstevel@tonic-gate aggr_set_coll_dist(portp, B_TRUE); 8887c478bd9Sstevel@tonic-gate 8897c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.collecting = B_TRUE; 8907c478bd9Sstevel@tonic-gate NTT_updated = B_TRUE; 8917c478bd9Sstevel@tonic-gate break; 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate /* 8957c478bd9Sstevel@tonic-gate * If we updated the state of the NTT variable, then 8967c478bd9Sstevel@tonic-gate * initiate a LACPDU transmission. 8977c478bd9Sstevel@tonic-gate */ 8987c478bd9Sstevel@tonic-gate if (NTT_updated) { 8997c478bd9Sstevel@tonic-gate pl->NTT = B_TRUE; 9007c478bd9Sstevel@tonic-gate lacp_xmit_sm(portp); 9017c478bd9Sstevel@tonic-gate } 9027c478bd9Sstevel@tonic-gate } /* lacp_mux_sm */ 9037c478bd9Sstevel@tonic-gate 9047c478bd9Sstevel@tonic-gate 905da14cebeSEric Cheng static int 9067c478bd9Sstevel@tonic-gate receive_marker_pdu(aggr_port_t *portp, mblk_t *mp) 9077c478bd9Sstevel@tonic-gate { 9087c478bd9Sstevel@tonic-gate marker_pdu_t *markerp = (marker_pdu_t *)mp->b_rptr; 9097c478bd9Sstevel@tonic-gate 910da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 9117c478bd9Sstevel@tonic-gate 912d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link: (%d): MARKER PDU received:\n", 913d62bc4baSyz147064 portp->lp_linkid)); 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 9167c478bd9Sstevel@tonic-gate if (!portp->lp_lacp.sm.lacp_on) 917da14cebeSEric Cheng return (-1); 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate if (MBLKL(mp) < sizeof (marker_pdu_t)) 920da14cebeSEric Cheng return (-1); 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate if (markerp->version != MARKER_VERSION) { 923d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: " 9247c478bd9Sstevel@tonic-gate "version = %d does not match s/w version %d\n", 925d62bc4baSyz147064 portp->lp_linkid, markerp->version, MARKER_VERSION)); 926da14cebeSEric Cheng return (-1); 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate if (markerp->tlv_marker == MARKER_RESPONSE_TLV) { 9307c478bd9Sstevel@tonic-gate /* We do not yet send out MARKER info PDUs */ 931d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): MARKER RESPONSE PDU: " 9327c478bd9Sstevel@tonic-gate " MARKER TLV = %d - We don't send out info type!\n", 933d62bc4baSyz147064 portp->lp_linkid, markerp->tlv_marker)); 934da14cebeSEric Cheng return (-1); 9357c478bd9Sstevel@tonic-gate } 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate if (markerp->tlv_marker != MARKER_INFO_TLV) { 938d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: " 939d62bc4baSyz147064 " MARKER TLV = %d \n", portp->lp_linkid, 9407c478bd9Sstevel@tonic-gate markerp->tlv_marker)); 941da14cebeSEric Cheng return (-1); 9427c478bd9Sstevel@tonic-gate } 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate if (markerp->marker_len != MARKER_INFO_RESPONSE_LENGTH) { 945d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: " 946d62bc4baSyz147064 " MARKER length = %d \n", portp->lp_linkid, 9477c478bd9Sstevel@tonic-gate markerp->marker_len)); 948da14cebeSEric Cheng return (-1); 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate 9517c478bd9Sstevel@tonic-gate if (markerp->requestor_port != portp->lp_lacp.PartnerOperPortNum) { 952d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: " 9537c478bd9Sstevel@tonic-gate " MARKER Port %d not equal to Partner port %d\n", 954d62bc4baSyz147064 portp->lp_linkid, markerp->requestor_port, 9557c478bd9Sstevel@tonic-gate portp->lp_lacp.PartnerOperPortNum)); 956da14cebeSEric Cheng return (-1); 9577c478bd9Sstevel@tonic-gate } 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate if (ether_cmp(&markerp->system_id, 9607c478bd9Sstevel@tonic-gate &portp->lp_lacp.PartnerOperSystem) != 0) { 961d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: " 9627c478bd9Sstevel@tonic-gate " MARKER MAC not equal to Partner MAC\n", 963d62bc4baSyz147064 portp->lp_linkid)); 964da14cebeSEric Cheng return (-1); 9657c478bd9Sstevel@tonic-gate } 9667c478bd9Sstevel@tonic-gate 9677c478bd9Sstevel@tonic-gate /* 9687c478bd9Sstevel@tonic-gate * Turn into Marker Response PDU 9697c478bd9Sstevel@tonic-gate * and return mblk to sending system 9707c478bd9Sstevel@tonic-gate */ 9717c478bd9Sstevel@tonic-gate markerp->tlv_marker = MARKER_RESPONSE_TLV; 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate /* reuse the space that was used by received ethernet header */ 9747c478bd9Sstevel@tonic-gate ASSERT(MBLKHEAD(mp) >= sizeof (struct ether_header)); 9757c478bd9Sstevel@tonic-gate mp->b_rptr -= sizeof (struct ether_header); 9767c478bd9Sstevel@tonic-gate fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr); 977da14cebeSEric Cheng return (0); 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate /* 9817c478bd9Sstevel@tonic-gate * Update the LACP mode (off, active, or passive) of the specified group. 9827c478bd9Sstevel@tonic-gate */ 9837c478bd9Sstevel@tonic-gate void 9847c478bd9Sstevel@tonic-gate aggr_lacp_update_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode) 9857c478bd9Sstevel@tonic-gate { 9867c478bd9Sstevel@tonic-gate aggr_lacp_mode_t old_mode = grp->lg_lacp_mode; 9877c478bd9Sstevel@tonic-gate aggr_port_t *port; 9887c478bd9Sstevel@tonic-gate 989da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 990da14cebeSEric Cheng ASSERT(!grp->lg_closing); 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate if (mode == old_mode) 9937c478bd9Sstevel@tonic-gate return; 9947c478bd9Sstevel@tonic-gate 9957c478bd9Sstevel@tonic-gate grp->lg_lacp_mode = mode; 9967c478bd9Sstevel@tonic-gate 9977c478bd9Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 9987c478bd9Sstevel@tonic-gate port->lp_lacp.ActorAdminPortState.bit.activity = 9997c478bd9Sstevel@tonic-gate port->lp_lacp.ActorOperPortState.bit.activity = 10007c478bd9Sstevel@tonic-gate (mode == AGGR_LACP_ACTIVE); 10017c478bd9Sstevel@tonic-gate 10027c478bd9Sstevel@tonic-gate if (old_mode == AGGR_LACP_OFF) { 10037c478bd9Sstevel@tonic-gate /* OFF -> {PASSIVE,ACTIVE} */ 10047c478bd9Sstevel@tonic-gate /* turn OFF Collector_Distributor */ 10057c478bd9Sstevel@tonic-gate aggr_set_coll_dist(port, B_FALSE); 10067c478bd9Sstevel@tonic-gate lacp_on(port); 10077c478bd9Sstevel@tonic-gate } else if (mode == AGGR_LACP_OFF) { 10087c478bd9Sstevel@tonic-gate /* {PASSIVE,ACTIVE} -> OFF */ 10097c478bd9Sstevel@tonic-gate lacp_off(port); 10107c478bd9Sstevel@tonic-gate /* Turn ON Collector_Distributor */ 10117c478bd9Sstevel@tonic-gate aggr_set_coll_dist(port, B_TRUE); 10127c478bd9Sstevel@tonic-gate } else { 10137c478bd9Sstevel@tonic-gate /* PASSIVE->ACTIVE or ACTIVE->PASSIVE */ 10147c478bd9Sstevel@tonic-gate port->lp_lacp.sm.begin = B_TRUE; 10157c478bd9Sstevel@tonic-gate lacp_mux_sm(port); 10167c478bd9Sstevel@tonic-gate lacp_periodic_sm(port); 10177c478bd9Sstevel@tonic-gate 10187c478bd9Sstevel@tonic-gate /* kick off state machines */ 10197c478bd9Sstevel@tonic-gate lacp_receive_sm(port, NULL); 10207c478bd9Sstevel@tonic-gate lacp_mux_sm(port); 10217c478bd9Sstevel@tonic-gate } 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate } 10247c478bd9Sstevel@tonic-gate 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate /* 10277c478bd9Sstevel@tonic-gate * Update the LACP timer (short or long) of the specified group. 10287c478bd9Sstevel@tonic-gate */ 10297c478bd9Sstevel@tonic-gate void 10307c478bd9Sstevel@tonic-gate aggr_lacp_update_timer(aggr_grp_t *grp, aggr_lacp_timer_t timer) 10317c478bd9Sstevel@tonic-gate { 10327c478bd9Sstevel@tonic-gate aggr_port_t *port; 10337c478bd9Sstevel@tonic-gate 1034da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 10357c478bd9Sstevel@tonic-gate 10367c478bd9Sstevel@tonic-gate if (timer == grp->aggr.PeriodicTimer) 10377c478bd9Sstevel@tonic-gate return; 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate grp->aggr.PeriodicTimer = timer; 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) { 10427c478bd9Sstevel@tonic-gate port->lp_lacp.ActorAdminPortState.bit.timeout = 10437c478bd9Sstevel@tonic-gate port->lp_lacp.ActorOperPortState.bit.timeout = 10447c478bd9Sstevel@tonic-gate (timer == AGGR_LACP_TIMER_SHORT); 10457c478bd9Sstevel@tonic-gate } 10467c478bd9Sstevel@tonic-gate } 10477c478bd9Sstevel@tonic-gate 1048da14cebeSEric Cheng void 1049da14cebeSEric Cheng aggr_port_lacp_set_mode(aggr_grp_t *grp, aggr_port_t *port) 1050da14cebeSEric Cheng { 1051da14cebeSEric Cheng aggr_lacp_mode_t mode; 1052da14cebeSEric Cheng aggr_lacp_timer_t timer; 1053da14cebeSEric Cheng 1054da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1055da14cebeSEric Cheng 1056da14cebeSEric Cheng mode = grp->lg_lacp_mode; 1057da14cebeSEric Cheng timer = grp->aggr.PeriodicTimer; 1058da14cebeSEric Cheng 1059da14cebeSEric Cheng port->lp_lacp.ActorAdminPortState.bit.activity = 1060da14cebeSEric Cheng port->lp_lacp.ActorOperPortState.bit.activity = 1061da14cebeSEric Cheng (mode == AGGR_LACP_ACTIVE); 1062da14cebeSEric Cheng 1063da14cebeSEric Cheng port->lp_lacp.ActorAdminPortState.bit.timeout = 1064da14cebeSEric Cheng port->lp_lacp.ActorOperPortState.bit.timeout = 1065da14cebeSEric Cheng (timer == AGGR_LACP_TIMER_SHORT); 1066da14cebeSEric Cheng 1067da14cebeSEric Cheng if (mode == AGGR_LACP_OFF) { 1068da14cebeSEric Cheng /* Turn ON Collector_Distributor */ 1069da14cebeSEric Cheng aggr_set_coll_dist(port, B_TRUE); 1070da14cebeSEric Cheng } else { /* LACP_ACTIVE/PASSIVE */ 1071da14cebeSEric Cheng lacp_on(port); 1072da14cebeSEric Cheng } 1073da14cebeSEric Cheng } 10747c478bd9Sstevel@tonic-gate 10757c478bd9Sstevel@tonic-gate /* 10767c478bd9Sstevel@tonic-gate * Sets the initial LACP mode (off, active, passive) and LACP timer 10777c478bd9Sstevel@tonic-gate * (short, long) of the specified group. 10787c478bd9Sstevel@tonic-gate */ 10797c478bd9Sstevel@tonic-gate void 10807c478bd9Sstevel@tonic-gate aggr_lacp_set_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode, 10817c478bd9Sstevel@tonic-gate aggr_lacp_timer_t timer) 10827c478bd9Sstevel@tonic-gate { 10837c478bd9Sstevel@tonic-gate aggr_port_t *port; 10847c478bd9Sstevel@tonic-gate 1085da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 10867c478bd9Sstevel@tonic-gate 10877c478bd9Sstevel@tonic-gate grp->lg_lacp_mode = mode; 10887c478bd9Sstevel@tonic-gate grp->aggr.PeriodicTimer = timer; 10897c478bd9Sstevel@tonic-gate 1090da14cebeSEric Cheng for (port = grp->lg_ports; port != NULL; port = port->lp_next) 1091da14cebeSEric Cheng aggr_port_lacp_set_mode(grp, port); 10927c478bd9Sstevel@tonic-gate } 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate /* 10957c478bd9Sstevel@tonic-gate * Verify that the Partner MAC and Key recorded by the specified 10967c478bd9Sstevel@tonic-gate * port are not found in other ports that are not part of our 10977c478bd9Sstevel@tonic-gate * aggregation. Returns B_TRUE if such a port is found, B_FALSE 10987c478bd9Sstevel@tonic-gate * otherwise. 10997c478bd9Sstevel@tonic-gate */ 11007c478bd9Sstevel@tonic-gate static boolean_t 11017c478bd9Sstevel@tonic-gate lacp_misconfig_check(aggr_port_t *portp) 11027c478bd9Sstevel@tonic-gate { 1103f12af565Snd99603 aggr_grp_t *grp = portp->lp_grp; 1104f12af565Snd99603 lacp_sel_ports_t *cport; 11057c478bd9Sstevel@tonic-gate 1106f12af565Snd99603 mutex_enter(&lacp_sel_lock); 11077c478bd9Sstevel@tonic-gate 1108f12af565Snd99603 for (cport = sel_ports; cport != NULL; cport = cport->sp_next) { 11097c478bd9Sstevel@tonic-gate 1110f12af565Snd99603 /* skip entries of the group of the port being checked */ 1111d62bc4baSyz147064 if (cport->sp_grp_linkid == grp->lg_linkid) 1112f12af565Snd99603 continue; 11137c478bd9Sstevel@tonic-gate 1114f12af565Snd99603 if ((ether_cmp(&cport->sp_partner_system, 1115f12af565Snd99603 &grp->aggr.PartnerSystem) == 0) && 1116f12af565Snd99603 (cport->sp_partner_key == grp->aggr.PartnerOperAggrKey)) { 1117f12af565Snd99603 char mac_str[ETHERADDRL*3]; 1118f12af565Snd99603 struct ether_addr *mac = &cport->sp_partner_system; 1119f12af565Snd99603 1120f12af565Snd99603 /* 1121f12af565Snd99603 * The Partner port information is already in use 1122f12af565Snd99603 * by ports in another aggregation so disable this 1123f12af565Snd99603 * port. 1124f12af565Snd99603 */ 1125f12af565Snd99603 1126f12af565Snd99603 (void) snprintf(mac_str, sizeof (mac_str), 1127f12af565Snd99603 "%x:%x:%x:%x:%x:%x", 1128f12af565Snd99603 mac->ether_addr_octet[0], mac->ether_addr_octet[1], 1129f12af565Snd99603 mac->ether_addr_octet[2], mac->ether_addr_octet[3], 1130f12af565Snd99603 mac->ether_addr_octet[4], mac->ether_addr_octet[5]); 1131f12af565Snd99603 1132f12af565Snd99603 portp->lp_lacp.sm.selected = AGGR_UNSELECTED; 1133d62bc4baSyz147064 1134d62bc4baSyz147064 cmn_err(CE_NOTE, "aggr %d port %d: Port Partner " 1135d62bc4baSyz147064 "MAC %s and key %d in use on aggregation %d " 1136d62bc4baSyz147064 "port %d\n", grp->lg_linkid, portp->lp_linkid, 1137d62bc4baSyz147064 mac_str, portp->lp_lacp.PartnerOperKey, 1138d62bc4baSyz147064 cport->sp_grp_linkid, cport->sp_linkid); 1139f12af565Snd99603 break; 1140f12af565Snd99603 } 11417c478bd9Sstevel@tonic-gate } 11427c478bd9Sstevel@tonic-gate 1143f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1144f12af565Snd99603 return (cport != NULL); 1145f12af565Snd99603 } 1146f12af565Snd99603 1147f12af565Snd99603 /* 1148f12af565Snd99603 * Remove the specified port from the list of selected ports. 1149f12af565Snd99603 */ 1150f12af565Snd99603 static void 1151f12af565Snd99603 lacp_sel_ports_del(aggr_port_t *portp) 1152f12af565Snd99603 { 1153f12af565Snd99603 lacp_sel_ports_t *cport, **prev = NULL; 1154f12af565Snd99603 1155f12af565Snd99603 mutex_enter(&lacp_sel_lock); 1156f12af565Snd99603 1157f12af565Snd99603 prev = &sel_ports; 1158f12af565Snd99603 for (cport = sel_ports; cport != NULL; prev = &cport->sp_next, 1159f12af565Snd99603 cport = cport->sp_next) { 1160d62bc4baSyz147064 if (portp->lp_linkid == cport->sp_linkid) 1161f12af565Snd99603 break; 1162f12af565Snd99603 } 1163f12af565Snd99603 1164f12af565Snd99603 if (cport == NULL) { 1165f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1166f12af565Snd99603 return; 1167f12af565Snd99603 } 1168f12af565Snd99603 1169f12af565Snd99603 *prev = cport->sp_next; 1170f12af565Snd99603 kmem_free(cport, sizeof (*cport)); 1171f12af565Snd99603 1172f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1173f12af565Snd99603 } 1174f12af565Snd99603 1175f12af565Snd99603 /* 1176f12af565Snd99603 * Add the specified port to the list of selected ports. Returns B_FALSE 1177f12af565Snd99603 * if the operation could not be performed due to an memory allocation 1178f12af565Snd99603 * error. 1179f12af565Snd99603 */ 1180f12af565Snd99603 static boolean_t 1181f12af565Snd99603 lacp_sel_ports_add(aggr_port_t *portp) 1182f12af565Snd99603 { 1183f12af565Snd99603 lacp_sel_ports_t *new_port; 1184f12af565Snd99603 lacp_sel_ports_t *cport, **last; 1185f12af565Snd99603 1186f12af565Snd99603 mutex_enter(&lacp_sel_lock); 1187f12af565Snd99603 1188f12af565Snd99603 /* check if port is already in the list */ 1189f12af565Snd99603 last = &sel_ports; 1190f12af565Snd99603 for (cport = sel_ports; cport != NULL; 1191f12af565Snd99603 last = &cport->sp_next, cport = cport->sp_next) { 1192d62bc4baSyz147064 if (portp->lp_linkid == cport->sp_linkid) { 1193f12af565Snd99603 ASSERT(cport->sp_partner_key == 1194f12af565Snd99603 portp->lp_lacp.PartnerOperKey); 1195f12af565Snd99603 ASSERT(ether_cmp(&cport->sp_partner_system, 1196f12af565Snd99603 &portp->lp_lacp.PartnerOperSystem) == 0); 1197f12af565Snd99603 1198f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1199f12af565Snd99603 return (B_TRUE); 1200f12af565Snd99603 } 1201f12af565Snd99603 } 1202f12af565Snd99603 1203f12af565Snd99603 /* create and initialize new entry */ 1204f12af565Snd99603 new_port = kmem_zalloc(sizeof (lacp_sel_ports_t), KM_NOSLEEP); 1205f12af565Snd99603 if (new_port == NULL) { 1206f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1207f12af565Snd99603 return (B_FALSE); 1208f12af565Snd99603 } 1209f12af565Snd99603 1210d62bc4baSyz147064 new_port->sp_grp_linkid = portp->lp_grp->lg_linkid; 1211f12af565Snd99603 bcopy(&portp->lp_lacp.PartnerOperSystem, 1212f12af565Snd99603 &new_port->sp_partner_system, sizeof (new_port->sp_partner_system)); 1213f12af565Snd99603 new_port->sp_partner_key = portp->lp_lacp.PartnerOperKey; 1214d62bc4baSyz147064 new_port->sp_linkid = portp->lp_linkid; 1215f12af565Snd99603 1216f12af565Snd99603 *last = new_port; 1217f12af565Snd99603 1218f12af565Snd99603 mutex_exit(&lacp_sel_lock); 1219f12af565Snd99603 return (B_TRUE); 1220f12af565Snd99603 } 12217c478bd9Sstevel@tonic-gate 12227c478bd9Sstevel@tonic-gate /* 12237c478bd9Sstevel@tonic-gate * lacp_selection_logic - LACP selection logic 12247c478bd9Sstevel@tonic-gate * Sets the selected variable on a per port basis 12257c478bd9Sstevel@tonic-gate * and sets Ready when all waiting ports are ready 12267c478bd9Sstevel@tonic-gate * to go online. 12277c478bd9Sstevel@tonic-gate * 12287c478bd9Sstevel@tonic-gate * parameters: 12297c478bd9Sstevel@tonic-gate * - portp - instance this applies to. 12307c478bd9Sstevel@tonic-gate * 12317c478bd9Sstevel@tonic-gate * invoked: 12327c478bd9Sstevel@tonic-gate * - when initialization is needed 12337c478bd9Sstevel@tonic-gate * - when UNSELECTED is set from the lacp_receive_sm() in LACP_CURRENT state 12347c478bd9Sstevel@tonic-gate * - When the lacp_receive_sm goes to the LACP_DEFAULTED state 12357c478bd9Sstevel@tonic-gate * - every time the wait_while_timer pops 12367c478bd9Sstevel@tonic-gate * - everytime we turn LACP on/off 12377c478bd9Sstevel@tonic-gate */ 12387c478bd9Sstevel@tonic-gate static void 12397c478bd9Sstevel@tonic-gate lacp_selection_logic(aggr_port_t *portp) 12407c478bd9Sstevel@tonic-gate { 12417c478bd9Sstevel@tonic-gate aggr_port_t *tpp; 12427c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 12437c478bd9Sstevel@tonic-gate int ports_waiting; 12447c478bd9Sstevel@tonic-gate boolean_t reset_mac = B_FALSE; 12457c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 12467c478bd9Sstevel@tonic-gate 1247da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(aggrp->lg_mh)); 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 12507c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_on) { 1251f12af565Snd99603 lacp_port_unselect(portp); 12527c478bd9Sstevel@tonic-gate aggrp->aggr.ready = B_FALSE; 12537c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 12547c478bd9Sstevel@tonic-gate return; 12557c478bd9Sstevel@tonic-gate } 12567c478bd9Sstevel@tonic-gate 12577c478bd9Sstevel@tonic-gate if (pl->sm.begin || !pl->sm.lacp_enabled || 12587c478bd9Sstevel@tonic-gate (portp->lp_state != AGGR_PORT_STATE_ATTACHED)) { 12597c478bd9Sstevel@tonic-gate 1260d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_selection_logic:(%d): " 12617c478bd9Sstevel@tonic-gate "selected %d-->%d (begin=%d, lacp_enabled = %d, " 1262d62bc4baSyz147064 "lp_state=%d)\n", portp->lp_linkid, pl->sm.selected, 1263ba2e4443Sseb AGGR_UNSELECTED, pl->sm.begin, pl->sm.lacp_enabled, 12647c478bd9Sstevel@tonic-gate portp->lp_state)); 12657c478bd9Sstevel@tonic-gate 1266f12af565Snd99603 lacp_port_unselect(portp); 12677c478bd9Sstevel@tonic-gate aggrp->aggr.ready = B_FALSE; 12687c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 12697c478bd9Sstevel@tonic-gate return; 12707c478bd9Sstevel@tonic-gate } 12717c478bd9Sstevel@tonic-gate 12727c478bd9Sstevel@tonic-gate /* 12737c478bd9Sstevel@tonic-gate * If LACP is not enabled then selected is never set. 12747c478bd9Sstevel@tonic-gate */ 12757c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_enabled) { 1276d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_selection_logic:(%d): selected %d-->%d\n", 1277d62bc4baSyz147064 portp->lp_linkid, pl->sm.selected, AGGR_UNSELECTED)); 12787c478bd9Sstevel@tonic-gate 1279f12af565Snd99603 lacp_port_unselect(portp); 12807c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 12817c478bd9Sstevel@tonic-gate return; 12827c478bd9Sstevel@tonic-gate } 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate /* 12857c478bd9Sstevel@tonic-gate * Check if the Partner MAC or Key are zero. If so, we have 12867c478bd9Sstevel@tonic-gate * not received any LACP info or it has expired and the 12877c478bd9Sstevel@tonic-gate * receive machine is in the LACP_DEFAULTED state. 12887c478bd9Sstevel@tonic-gate */ 12897c478bd9Sstevel@tonic-gate if (ether_cmp(&pl->PartnerOperSystem, ðerzeroaddr) == 0 || 12907c478bd9Sstevel@tonic-gate (pl->PartnerOperKey == 0)) { 12917c478bd9Sstevel@tonic-gate 12927c478bd9Sstevel@tonic-gate for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) { 12937c478bd9Sstevel@tonic-gate if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem, 12947c478bd9Sstevel@tonic-gate ðerzeroaddr) != 0 && 12957c478bd9Sstevel@tonic-gate (tpp->lp_lacp.PartnerOperKey != 0)) 12967c478bd9Sstevel@tonic-gate break; 12977c478bd9Sstevel@tonic-gate } 12987c478bd9Sstevel@tonic-gate 12997c478bd9Sstevel@tonic-gate /* 13007c478bd9Sstevel@tonic-gate * If all ports have no key or aggregation address, 13017c478bd9Sstevel@tonic-gate * then clear the negotiated Partner MAC and key. 13027c478bd9Sstevel@tonic-gate */ 13037c478bd9Sstevel@tonic-gate if (tpp == NULL) { 13047c478bd9Sstevel@tonic-gate /* Clear the aggregation Partner MAC and key */ 13057c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerSystem = etherzeroaddr; 13067c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerOperAggrKey = 0; 13077c478bd9Sstevel@tonic-gate } 13087c478bd9Sstevel@tonic-gate 13097c478bd9Sstevel@tonic-gate return; 13107c478bd9Sstevel@tonic-gate } 13117c478bd9Sstevel@tonic-gate 13127c478bd9Sstevel@tonic-gate /* 13137c478bd9Sstevel@tonic-gate * Insure that at least one port in the aggregation 13147c478bd9Sstevel@tonic-gate * matches the Partner aggregation MAC and key. If not, 13157c478bd9Sstevel@tonic-gate * then clear the aggregation MAC and key. Later we will 13167c478bd9Sstevel@tonic-gate * set the Partner aggregation MAC and key to that of the 13177c478bd9Sstevel@tonic-gate * current port's Partner MAC and key. 13187c478bd9Sstevel@tonic-gate */ 13197c478bd9Sstevel@tonic-gate if (ether_cmp(&pl->PartnerOperSystem, 13207c478bd9Sstevel@tonic-gate &aggrp->aggr.PartnerSystem) != 0 || 13217c478bd9Sstevel@tonic-gate (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) { 13227c478bd9Sstevel@tonic-gate 13237c478bd9Sstevel@tonic-gate for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) { 13247c478bd9Sstevel@tonic-gate if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem, 13257c478bd9Sstevel@tonic-gate &aggrp->aggr.PartnerSystem) == 0 && 13267c478bd9Sstevel@tonic-gate (tpp->lp_lacp.PartnerOperKey == 13270dc2366fSVenugopal Iyer aggrp->aggr.PartnerOperAggrKey)) { 13280dc2366fSVenugopal Iyer /* Set aggregation Partner MAC and key */ 13290dc2366fSVenugopal Iyer aggrp->aggr.PartnerSystem = 13300dc2366fSVenugopal Iyer pl->PartnerOperSystem; 13310dc2366fSVenugopal Iyer aggrp->aggr.PartnerOperAggrKey = 13320dc2366fSVenugopal Iyer pl->PartnerOperKey; 13337c478bd9Sstevel@tonic-gate break; 13347c478bd9Sstevel@tonic-gate } 13350dc2366fSVenugopal Iyer } 13367c478bd9Sstevel@tonic-gate 13377c478bd9Sstevel@tonic-gate if (tpp == NULL) { 13387c478bd9Sstevel@tonic-gate /* Clear the aggregation Partner MAC and key */ 13397c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerSystem = etherzeroaddr; 13407c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerOperAggrKey = 0; 13417c478bd9Sstevel@tonic-gate reset_mac = B_TRUE; 13427c478bd9Sstevel@tonic-gate } 13437c478bd9Sstevel@tonic-gate } 13447c478bd9Sstevel@tonic-gate 13457c478bd9Sstevel@tonic-gate /* 13467c478bd9Sstevel@tonic-gate * If our Actor MAC is found in the Partner MAC 13477c478bd9Sstevel@tonic-gate * on this port then we have a loopback misconfiguration. 13487c478bd9Sstevel@tonic-gate */ 13497c478bd9Sstevel@tonic-gate if (ether_cmp(&pl->PartnerOperSystem, 13507c478bd9Sstevel@tonic-gate (struct ether_addr *)&aggrp->lg_addr) == 0) { 1351d62bc4baSyz147064 cmn_err(CE_NOTE, "trunk link: (%d): Loopback condition.\n", 1352d62bc4baSyz147064 portp->lp_linkid); 13537c478bd9Sstevel@tonic-gate 1354f12af565Snd99603 lacp_port_unselect(portp); 13557c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 13567c478bd9Sstevel@tonic-gate return; 13577c478bd9Sstevel@tonic-gate } 13587c478bd9Sstevel@tonic-gate 13597c478bd9Sstevel@tonic-gate /* 13607c478bd9Sstevel@tonic-gate * If our Partner MAC and Key are found on any other 13617c478bd9Sstevel@tonic-gate * ports that are not in our aggregation, we have 13627c478bd9Sstevel@tonic-gate * a misconfiguration. 13637c478bd9Sstevel@tonic-gate */ 13647c478bd9Sstevel@tonic-gate if (lacp_misconfig_check(portp)) { 13657c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 13667c478bd9Sstevel@tonic-gate return; 13677c478bd9Sstevel@tonic-gate } 13687c478bd9Sstevel@tonic-gate 13697c478bd9Sstevel@tonic-gate /* 13707c478bd9Sstevel@tonic-gate * If the Aggregation Partner MAC and Key have not been 13717c478bd9Sstevel@tonic-gate * set, then this is either the first port or the aggregation 13727c478bd9Sstevel@tonic-gate * MAC and key have been reset. In either case we must set 13737c478bd9Sstevel@tonic-gate * the values of the Partner MAC and key. 13747c478bd9Sstevel@tonic-gate */ 13757c478bd9Sstevel@tonic-gate if (ether_cmp(&aggrp->aggr.PartnerSystem, ðerzeroaddr) == 0 && 13767c478bd9Sstevel@tonic-gate (aggrp->aggr.PartnerOperAggrKey == 0)) { 13777c478bd9Sstevel@tonic-gate /* Set aggregation Partner MAC and key */ 13787c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerSystem = pl->PartnerOperSystem; 13797c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerOperAggrKey = pl->PartnerOperKey; 13807c478bd9Sstevel@tonic-gate 13817c478bd9Sstevel@tonic-gate /* 13827c478bd9Sstevel@tonic-gate * If we reset Partner aggregation MAC, then restart 13837c478bd9Sstevel@tonic-gate * selection_logic on ports that match new MAC address. 13847c478bd9Sstevel@tonic-gate */ 13857c478bd9Sstevel@tonic-gate if (reset_mac) { 13867c478bd9Sstevel@tonic-gate for (tpp = aggrp->lg_ports; tpp; tpp = 13877c478bd9Sstevel@tonic-gate tpp->lp_next) { 13887c478bd9Sstevel@tonic-gate if (tpp == portp) 13897c478bd9Sstevel@tonic-gate continue; 13907c478bd9Sstevel@tonic-gate if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem, 13917c478bd9Sstevel@tonic-gate &aggrp->aggr.PartnerSystem) == 0 && 13927c478bd9Sstevel@tonic-gate (tpp->lp_lacp.PartnerOperKey == 13937c478bd9Sstevel@tonic-gate aggrp->aggr.PartnerOperAggrKey)) 13947c478bd9Sstevel@tonic-gate lacp_selection_logic(tpp); 13957c478bd9Sstevel@tonic-gate } 13967c478bd9Sstevel@tonic-gate } 13977c478bd9Sstevel@tonic-gate } else if (ether_cmp(&pl->PartnerOperSystem, 13987c478bd9Sstevel@tonic-gate &aggrp->aggr.PartnerSystem) != 0 || 13997c478bd9Sstevel@tonic-gate (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) { 14007c478bd9Sstevel@tonic-gate /* 14017c478bd9Sstevel@tonic-gate * The Partner port information does not match 14027c478bd9Sstevel@tonic-gate * that of the other ports in the aggregation 14037c478bd9Sstevel@tonic-gate * so disable this port. 14047c478bd9Sstevel@tonic-gate */ 1405f12af565Snd99603 lacp_port_unselect(portp); 1406f12af565Snd99603 1407d62bc4baSyz147064 cmn_err(CE_NOTE, "trunk link: (%d): Port Partner MAC " 1408d62bc4baSyz147064 "or key (%d) incompatible with Aggregation Partner " 1409d62bc4baSyz147064 "MAC or key (%d)\n", portp->lp_linkid, pl->PartnerOperKey, 1410ba2e4443Sseb aggrp->aggr.PartnerOperAggrKey); 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 14137c478bd9Sstevel@tonic-gate return; 14147c478bd9Sstevel@tonic-gate } 14157c478bd9Sstevel@tonic-gate 14167c478bd9Sstevel@tonic-gate /* If we get to here, automatically set selected */ 14177c478bd9Sstevel@tonic-gate if (pl->sm.selected != AGGR_SELECTED) { 1418d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_selection_logic:(%d): " 1419d62bc4baSyz147064 "selected %d-->%d\n", portp->lp_linkid, 14207c478bd9Sstevel@tonic-gate pl->sm.selected, AGGR_SELECTED)); 1421f12af565Snd99603 if (!lacp_port_select(portp)) 1422f12af565Snd99603 return; 14237c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 14247c478bd9Sstevel@tonic-gate } 14257c478bd9Sstevel@tonic-gate 14267c478bd9Sstevel@tonic-gate /* 14277c478bd9Sstevel@tonic-gate * From this point onward we have selected the port 14287c478bd9Sstevel@tonic-gate * and are simply checking if the Ready flag should 14297c478bd9Sstevel@tonic-gate * be set. 14307c478bd9Sstevel@tonic-gate */ 14317c478bd9Sstevel@tonic-gate 14327c478bd9Sstevel@tonic-gate /* 14337c478bd9Sstevel@tonic-gate * If at least two ports are waiting to aggregate 14347c478bd9Sstevel@tonic-gate * and ready_n is set on all ports waiting to aggregate 14357c478bd9Sstevel@tonic-gate * then set READY for the aggregation. 14367c478bd9Sstevel@tonic-gate */ 14377c478bd9Sstevel@tonic-gate 14387c478bd9Sstevel@tonic-gate ports_waiting = 0; 14397c478bd9Sstevel@tonic-gate 14407c478bd9Sstevel@tonic-gate if (!aggrp->aggr.ready) { 14417c478bd9Sstevel@tonic-gate /* 14427c478bd9Sstevel@tonic-gate * If all ports in the aggregation have received compatible 14437c478bd9Sstevel@tonic-gate * partner information and they match up correctly with the 14447c478bd9Sstevel@tonic-gate * switch, there is no need to wait for all the 14457c478bd9Sstevel@tonic-gate * wait_while_timers to pop. 14467c478bd9Sstevel@tonic-gate */ 14477c478bd9Sstevel@tonic-gate for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) { 14487c478bd9Sstevel@tonic-gate if (((tpp->lp_lacp.sm.mux_state == LACP_WAITING) || 14497c478bd9Sstevel@tonic-gate tpp->lp_lacp.sm.begin) && 1450f562e45bSRamesh Kumar Katla !tpp->lp_lacp.PartnerOperPortState.bit.sync) { 14517c478bd9Sstevel@tonic-gate /* Add up ports uninitialized or waiting */ 14527c478bd9Sstevel@tonic-gate ports_waiting++; 1453f562e45bSRamesh Kumar Katla if (!tpp->lp_lacp.sm.ready_n) { 1454f562e45bSRamesh Kumar Katla DTRACE_PROBE1(port___not__ready, 1455f562e45bSRamesh Kumar Katla aggr_port_t *, tpp); 14567c478bd9Sstevel@tonic-gate return; 14577c478bd9Sstevel@tonic-gate } 14587c478bd9Sstevel@tonic-gate } 14597c478bd9Sstevel@tonic-gate } 1460f562e45bSRamesh Kumar Katla } 14617c478bd9Sstevel@tonic-gate 14627c478bd9Sstevel@tonic-gate if (aggrp->aggr.ready) { 1463d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_selection_logic:(%d): " 1464d62bc4baSyz147064 "aggr.ready already set\n", portp->lp_linkid)); 14657c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 14667c478bd9Sstevel@tonic-gate } else { 1467d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_selection_logic:(%d): Ready %d-->%d\n", 1468d62bc4baSyz147064 portp->lp_linkid, aggrp->aggr.ready, B_TRUE)); 14697c478bd9Sstevel@tonic-gate aggrp->aggr.ready = B_TRUE; 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) 14727c478bd9Sstevel@tonic-gate lacp_mux_sm(tpp); 14737c478bd9Sstevel@tonic-gate } 14747c478bd9Sstevel@tonic-gate 14757c478bd9Sstevel@tonic-gate } 14767c478bd9Sstevel@tonic-gate 14777c478bd9Sstevel@tonic-gate /* 14787c478bd9Sstevel@tonic-gate * wait_while_timer_pop - When the timer pops, we arrive here to 14797c478bd9Sstevel@tonic-gate * set ready_n and trigger the selection logic. 14807c478bd9Sstevel@tonic-gate */ 14817c478bd9Sstevel@tonic-gate static void 14827c478bd9Sstevel@tonic-gate wait_while_timer_pop(void *data) 14837c478bd9Sstevel@tonic-gate { 14847c478bd9Sstevel@tonic-gate aggr_port_t *portp = data; 1485da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 14867c478bd9Sstevel@tonic-gate 1487da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1488da14cebeSEric Cheng pl->lacp_timer_bits |= LACP_WAIT_WHILE_TIMEOUT; 1489da14cebeSEric Cheng cv_broadcast(&pl->lacp_timer_cv); 1490da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 1491da14cebeSEric Cheng } 14927c478bd9Sstevel@tonic-gate 1493da14cebeSEric Cheng /* 1494da14cebeSEric Cheng * wait_while_timer_pop_handler - When the timer pops, we arrive here to 1495da14cebeSEric Cheng * set ready_n and trigger the selection logic. 1496da14cebeSEric Cheng */ 1497da14cebeSEric Cheng static void 1498da14cebeSEric Cheng wait_while_timer_pop_handler(aggr_port_t *portp) 1499da14cebeSEric Cheng { 1500da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 15017c478bd9Sstevel@tonic-gate 1502d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link:(%d): wait_while_timer pop \n", 1503d62bc4baSyz147064 portp->lp_linkid)); 15047c478bd9Sstevel@tonic-gate portp->lp_lacp.sm.ready_n = B_TRUE; 15057c478bd9Sstevel@tonic-gate 15067c478bd9Sstevel@tonic-gate lacp_selection_logic(portp); 15077c478bd9Sstevel@tonic-gate } 15087c478bd9Sstevel@tonic-gate 15097c478bd9Sstevel@tonic-gate static void 15107c478bd9Sstevel@tonic-gate start_wait_while_timer(aggr_port_t *portp) 15117c478bd9Sstevel@tonic-gate { 1512da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 15137c478bd9Sstevel@tonic-gate 1514da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 1515da14cebeSEric Cheng 1516da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1517da14cebeSEric Cheng if (pl->wait_while_timer.id == 0) { 1518da14cebeSEric Cheng pl->wait_while_timer.id = 15197c478bd9Sstevel@tonic-gate timeout(wait_while_timer_pop, portp, 15207c478bd9Sstevel@tonic-gate drv_usectohz(1000000 * 15217c478bd9Sstevel@tonic-gate portp->lp_lacp.wait_while_timer.val)); 15227c478bd9Sstevel@tonic-gate } 1523da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 15247c478bd9Sstevel@tonic-gate } 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate 15277c478bd9Sstevel@tonic-gate static void 1528da14cebeSEric Cheng stop_wait_while_timer(aggr_port_t *portp) 15297c478bd9Sstevel@tonic-gate { 1530da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 1531da14cebeSEric Cheng timeout_id_t id; 15327c478bd9Sstevel@tonic-gate 1533da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 1534da14cebeSEric Cheng 1535da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1536da14cebeSEric Cheng if ((id = pl->wait_while_timer.id) != 0) { 1537da14cebeSEric Cheng pl->lacp_timer_bits &= ~LACP_WAIT_WHILE_TIMEOUT; 1538da14cebeSEric Cheng pl->wait_while_timer.id = 0; 15397c478bd9Sstevel@tonic-gate } 1540da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 1541da14cebeSEric Cheng 1542da14cebeSEric Cheng if (id != 0) 1543da14cebeSEric Cheng (void) untimeout(id); 15447c478bd9Sstevel@tonic-gate } 15457c478bd9Sstevel@tonic-gate 15467c478bd9Sstevel@tonic-gate /* 15477c478bd9Sstevel@tonic-gate * Invoked when a port has been attached to a group. 15487c478bd9Sstevel@tonic-gate * Complete the processing that couldn't be finished from lacp_on() 15497c478bd9Sstevel@tonic-gate * because the port was not started. We know that the link is full 15507c478bd9Sstevel@tonic-gate * duplex and ON, otherwise it wouldn't be attached. 15517c478bd9Sstevel@tonic-gate */ 15527c478bd9Sstevel@tonic-gate void 15537c478bd9Sstevel@tonic-gate aggr_lacp_port_attached(aggr_port_t *portp) 15547c478bd9Sstevel@tonic-gate { 15557c478bd9Sstevel@tonic-gate aggr_grp_t *grp = portp->lp_grp; 15567c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 15577c478bd9Sstevel@tonic-gate 1558da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1559da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_mh)); 15607c478bd9Sstevel@tonic-gate ASSERT(portp->lp_state == AGGR_PORT_STATE_ATTACHED); 15617c478bd9Sstevel@tonic-gate 1562d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_port_attached: port %d\n", 1563d62bc4baSyz147064 portp->lp_linkid)); 15647c478bd9Sstevel@tonic-gate 15657c478bd9Sstevel@tonic-gate portp->lp_lacp.sm.port_enabled = B_TRUE; /* link on */ 15667c478bd9Sstevel@tonic-gate 1567da14cebeSEric Cheng if (grp->lg_lacp_mode == AGGR_LACP_OFF) 15687c478bd9Sstevel@tonic-gate return; 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate pl->sm.lacp_enabled = B_TRUE; 15717c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.aggregation = B_TRUE; 15727c478bd9Sstevel@tonic-gate pl->sm.begin = B_TRUE; 15737c478bd9Sstevel@tonic-gate 15747c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, NULL); 15757c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 15767c478bd9Sstevel@tonic-gate 15777c478bd9Sstevel@tonic-gate /* Enable Multicast Slow Protocol address */ 15787c478bd9Sstevel@tonic-gate aggr_lacp_mcast_on(portp); 15797c478bd9Sstevel@tonic-gate 15807c478bd9Sstevel@tonic-gate /* periodic_sm is started up from the receive machine */ 15817c478bd9Sstevel@tonic-gate lacp_selection_logic(portp); 15827c478bd9Sstevel@tonic-gate } 15837c478bd9Sstevel@tonic-gate 15847c478bd9Sstevel@tonic-gate /* 15857c478bd9Sstevel@tonic-gate * Invoked when a port has been detached from a group. Turn off 15867c478bd9Sstevel@tonic-gate * LACP processing if it was enabled. 15877c478bd9Sstevel@tonic-gate */ 15887c478bd9Sstevel@tonic-gate void 15897c478bd9Sstevel@tonic-gate aggr_lacp_port_detached(aggr_port_t *portp) 15907c478bd9Sstevel@tonic-gate { 15917c478bd9Sstevel@tonic-gate aggr_grp_t *grp = portp->lp_grp; 15927c478bd9Sstevel@tonic-gate 1593da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh)); 1594da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_mh)); 15957c478bd9Sstevel@tonic-gate 1596d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_port_detached: port %d\n", 1597d62bc4baSyz147064 portp->lp_linkid)); 15987c478bd9Sstevel@tonic-gate 15997c478bd9Sstevel@tonic-gate portp->lp_lacp.sm.port_enabled = B_FALSE; 16007c478bd9Sstevel@tonic-gate 16017c478bd9Sstevel@tonic-gate if (grp->lg_lacp_mode == AGGR_LACP_OFF) 16027c478bd9Sstevel@tonic-gate return; 16037c478bd9Sstevel@tonic-gate 1604da14cebeSEric Cheng portp->lp_lacp.sm.lacp_enabled = B_FALSE; 1605da14cebeSEric Cheng lacp_selection_logic(portp); 1606da14cebeSEric Cheng lacp_mux_sm(portp); 1607da14cebeSEric Cheng lacp_periodic_sm(portp); 16087c478bd9Sstevel@tonic-gate 16097c478bd9Sstevel@tonic-gate /* 1610da14cebeSEric Cheng * Disable Slow Protocol Timers. 16117c478bd9Sstevel@tonic-gate */ 1612da14cebeSEric Cheng stop_periodic_timer(portp); 1613da14cebeSEric Cheng stop_current_while_timer(portp); 1614da14cebeSEric Cheng stop_wait_while_timer(portp); 16157c478bd9Sstevel@tonic-gate 1616da14cebeSEric Cheng /* Disable Multicast Slow Protocol address */ 1617da14cebeSEric Cheng aggr_lacp_mcast_off(portp); 1618da14cebeSEric Cheng aggr_set_coll_dist(portp, B_FALSE); 16197c478bd9Sstevel@tonic-gate } 16207c478bd9Sstevel@tonic-gate 16217c478bd9Sstevel@tonic-gate /* 16227c478bd9Sstevel@tonic-gate * Enable Slow Protocol LACP and Marker PDUs. 16237c478bd9Sstevel@tonic-gate */ 16247c478bd9Sstevel@tonic-gate static void 16257c478bd9Sstevel@tonic-gate lacp_on(aggr_port_t *portp) 16267c478bd9Sstevel@tonic-gate { 1627da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 1628da14cebeSEric Cheng mac_perim_handle_t mph; 1629da14cebeSEric Cheng 1630da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 1631da14cebeSEric Cheng 1632da14cebeSEric Cheng mac_perim_enter_by_mh(portp->lp_mh, &mph); 16337c478bd9Sstevel@tonic-gate 16347c478bd9Sstevel@tonic-gate /* 16357c478bd9Sstevel@tonic-gate * Reset the state machines and Partner operational 16367c478bd9Sstevel@tonic-gate * information. Careful to not reset things like 16377c478bd9Sstevel@tonic-gate * our link state. 16387c478bd9Sstevel@tonic-gate */ 16397c478bd9Sstevel@tonic-gate lacp_reset_port(portp); 1640da14cebeSEric Cheng pl->sm.lacp_on = B_TRUE; 16417c478bd9Sstevel@tonic-gate 1642d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_on:(%d): \n", portp->lp_linkid)); 16437c478bd9Sstevel@tonic-gate 1644da14cebeSEric Cheng if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) { 1645da14cebeSEric Cheng pl->sm.port_enabled = B_TRUE; 1646da14cebeSEric Cheng pl->sm.lacp_enabled = B_TRUE; 1647da14cebeSEric Cheng pl->ActorOperPortState.bit.aggregation = B_TRUE; 1648da14cebeSEric Cheng } 1649da14cebeSEric Cheng 16507c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, NULL); 16517c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 16527c478bd9Sstevel@tonic-gate 1653da14cebeSEric Cheng if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) { 16547c478bd9Sstevel@tonic-gate /* Enable Multicast Slow Protocol address */ 16557c478bd9Sstevel@tonic-gate aggr_lacp_mcast_on(portp); 16567c478bd9Sstevel@tonic-gate 16577c478bd9Sstevel@tonic-gate /* periodic_sm is started up from the receive machine */ 16587c478bd9Sstevel@tonic-gate lacp_selection_logic(portp); 1659da14cebeSEric Cheng } 1660da14cebeSEric Cheng done: 1661da14cebeSEric Cheng mac_perim_exit(mph); 16627c478bd9Sstevel@tonic-gate } /* lacp_on */ 16637c478bd9Sstevel@tonic-gate 16647c478bd9Sstevel@tonic-gate /* Disable Slow Protocol LACP and Marker PDUs */ 16657c478bd9Sstevel@tonic-gate static void 16667c478bd9Sstevel@tonic-gate lacp_off(aggr_port_t *portp) 16677c478bd9Sstevel@tonic-gate { 1668da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 1669da14cebeSEric Cheng mac_perim_handle_t mph; 16707c478bd9Sstevel@tonic-gate 1671da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 1672da14cebeSEric Cheng mac_perim_enter_by_mh(portp->lp_mh, &mph); 16737c478bd9Sstevel@tonic-gate 1674da14cebeSEric Cheng pl->sm.lacp_on = B_FALSE; 16757c478bd9Sstevel@tonic-gate 1676d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_off:(%d): \n", portp->lp_linkid)); 16777c478bd9Sstevel@tonic-gate 1678da14cebeSEric Cheng if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) { 16797c478bd9Sstevel@tonic-gate /* 1680da14cebeSEric Cheng * Disable Slow Protocol Timers. 16817c478bd9Sstevel@tonic-gate */ 16827c478bd9Sstevel@tonic-gate stop_periodic_timer(portp); 16837c478bd9Sstevel@tonic-gate stop_current_while_timer(portp); 16847c478bd9Sstevel@tonic-gate stop_wait_while_timer(portp); 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate /* Disable Multicast Slow Protocol address */ 16877c478bd9Sstevel@tonic-gate aggr_lacp_mcast_off(portp); 16887c478bd9Sstevel@tonic-gate 1689da14cebeSEric Cheng pl->sm.port_enabled = B_FALSE; 1690da14cebeSEric Cheng pl->sm.lacp_enabled = B_FALSE; 1691da14cebeSEric Cheng pl->ActorOperPortState.bit.aggregation = B_FALSE; 1692da14cebeSEric Cheng } 1693da14cebeSEric Cheng 1694da14cebeSEric Cheng lacp_mux_sm(portp); 1695da14cebeSEric Cheng lacp_periodic_sm(portp); 1696da14cebeSEric Cheng lacp_selection_logic(portp); 1697da14cebeSEric Cheng 1698da14cebeSEric Cheng /* Turn OFF Collector_Distributor */ 1699da14cebeSEric Cheng aggr_set_coll_dist(portp, B_FALSE); 1700da14cebeSEric Cheng 17017c478bd9Sstevel@tonic-gate lacp_reset_port(portp); 1702da14cebeSEric Cheng mac_perim_exit(mph); 17037c478bd9Sstevel@tonic-gate } 17047c478bd9Sstevel@tonic-gate 17057c478bd9Sstevel@tonic-gate 17067c478bd9Sstevel@tonic-gate static boolean_t 17077c478bd9Sstevel@tonic-gate valid_lacp_pdu(aggr_port_t *portp, lacp_t *lacp) 17087c478bd9Sstevel@tonic-gate { 17097c478bd9Sstevel@tonic-gate /* 17107c478bd9Sstevel@tonic-gate * 43.4.12 - "a Receive machine shall not validate 17117c478bd9Sstevel@tonic-gate * the Version Number, TLV_type, or Reserved fields in received 17127c478bd9Sstevel@tonic-gate * LACPDUs." 17137c478bd9Sstevel@tonic-gate * ... "a Receive machine may validate the Actor_Information_Length, 17147c478bd9Sstevel@tonic-gate * Partner_Information_Length, Collector_Information_Length, 17157c478bd9Sstevel@tonic-gate * or Terminator_Length fields." 17167c478bd9Sstevel@tonic-gate */ 17177c478bd9Sstevel@tonic-gate if ((lacp->actor_info.information_len != sizeof (link_info_t)) || 17187c478bd9Sstevel@tonic-gate (lacp->partner_info.information_len != sizeof (link_info_t)) || 17197c478bd9Sstevel@tonic-gate (lacp->collector_len != LACP_COLLECTOR_INFO_LEN) || 17207c478bd9Sstevel@tonic-gate (lacp->terminator_len != LACP_TERMINATOR_INFO_LEN)) { 1721d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link (%d): Malformed LACPDU: " 1722d62bc4baSyz147064 " Terminator Length = %d \n", portp->lp_linkid, 1723ba2e4443Sseb lacp->terminator_len)); 17247c478bd9Sstevel@tonic-gate return (B_FALSE); 17257c478bd9Sstevel@tonic-gate } 17267c478bd9Sstevel@tonic-gate 17277c478bd9Sstevel@tonic-gate return (B_TRUE); 17287c478bd9Sstevel@tonic-gate } 17297c478bd9Sstevel@tonic-gate 17307c478bd9Sstevel@tonic-gate 17317c478bd9Sstevel@tonic-gate static void 17327c478bd9Sstevel@tonic-gate start_current_while_timer(aggr_port_t *portp, uint_t time) 17337c478bd9Sstevel@tonic-gate { 1734da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 17357c478bd9Sstevel@tonic-gate 1736da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 17377c478bd9Sstevel@tonic-gate 1738da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1739da14cebeSEric Cheng if (pl->current_while_timer.id == 0) { 1740da14cebeSEric Cheng if (time > 0) 1741da14cebeSEric Cheng pl->current_while_timer.val = time; 1742da14cebeSEric Cheng else if (pl->ActorOperPortState.bit.timeout) 1743da14cebeSEric Cheng pl->current_while_timer.val = SHORT_TIMEOUT_TIME; 1744da14cebeSEric Cheng else 1745da14cebeSEric Cheng pl->current_while_timer.val = LONG_TIMEOUT_TIME; 1746da14cebeSEric Cheng 1747da14cebeSEric Cheng pl->current_while_timer.id = 17487c478bd9Sstevel@tonic-gate timeout(current_while_timer_pop, portp, 17497c478bd9Sstevel@tonic-gate drv_usectohz((clock_t)1000000 * 17507c478bd9Sstevel@tonic-gate (clock_t)portp->lp_lacp.current_while_timer.val)); 17517c478bd9Sstevel@tonic-gate } 1752da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 17537c478bd9Sstevel@tonic-gate } 17547c478bd9Sstevel@tonic-gate 17557c478bd9Sstevel@tonic-gate 17567c478bd9Sstevel@tonic-gate static void 17577c478bd9Sstevel@tonic-gate stop_current_while_timer(aggr_port_t *portp) 17587c478bd9Sstevel@tonic-gate { 1759da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 1760da14cebeSEric Cheng timeout_id_t id; 17617c478bd9Sstevel@tonic-gate 1762da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 17637c478bd9Sstevel@tonic-gate 1764da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1765da14cebeSEric Cheng if ((id = pl->current_while_timer.id) != 0) { 1766da14cebeSEric Cheng pl->lacp_timer_bits &= ~LACP_CURRENT_WHILE_TIMEOUT; 1767da14cebeSEric Cheng pl->current_while_timer.id = 0; 1768da14cebeSEric Cheng } 1769da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 1770da14cebeSEric Cheng 1771da14cebeSEric Cheng if (id != 0) 1772da14cebeSEric Cheng (void) untimeout(id); 1773da14cebeSEric Cheng } 17747c478bd9Sstevel@tonic-gate 17757c478bd9Sstevel@tonic-gate static void 17767c478bd9Sstevel@tonic-gate current_while_timer_pop(void *data) 17777c478bd9Sstevel@tonic-gate { 17787c478bd9Sstevel@tonic-gate aggr_port_t *portp = (aggr_port_t *)data; 1779da14cebeSEric Cheng aggr_lacp_port_t *pl = &portp->lp_lacp; 17807c478bd9Sstevel@tonic-gate 1781da14cebeSEric Cheng mutex_enter(&pl->lacp_timer_lock); 1782da14cebeSEric Cheng pl->lacp_timer_bits |= LACP_CURRENT_WHILE_TIMEOUT; 1783da14cebeSEric Cheng cv_broadcast(&pl->lacp_timer_cv); 1784da14cebeSEric Cheng mutex_exit(&pl->lacp_timer_lock); 1785da14cebeSEric Cheng } 17867c478bd9Sstevel@tonic-gate 1787da14cebeSEric Cheng static void 1788da14cebeSEric Cheng current_while_timer_pop_handler(aggr_port_t *portp) 1789da14cebeSEric Cheng { 1790da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 17917c478bd9Sstevel@tonic-gate 1792d62bc4baSyz147064 AGGR_LACP_DBG(("trunk link:(%d): current_while_timer " 1793d62bc4baSyz147064 "pop id=%p\n", portp->lp_linkid, 17947c478bd9Sstevel@tonic-gate portp->lp_lacp.current_while_timer.id)); 17957c478bd9Sstevel@tonic-gate 17967c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, NULL); 17977c478bd9Sstevel@tonic-gate } 17987c478bd9Sstevel@tonic-gate 17997c478bd9Sstevel@tonic-gate /* 18007c478bd9Sstevel@tonic-gate * record_Default - Simply copies over administrative values 18017c478bd9Sstevel@tonic-gate * to the partner operational values, and sets our state to indicate we 18027c478bd9Sstevel@tonic-gate * are using defaulted values. 18037c478bd9Sstevel@tonic-gate */ 18047c478bd9Sstevel@tonic-gate static void 18057c478bd9Sstevel@tonic-gate record_Default(aggr_port_t *portp) 18067c478bd9Sstevel@tonic-gate { 18077c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 18087c478bd9Sstevel@tonic-gate 1809da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 18107c478bd9Sstevel@tonic-gate 18117c478bd9Sstevel@tonic-gate pl->PartnerOperPortNum = pl->PartnerAdminPortNum; 18127c478bd9Sstevel@tonic-gate pl->PartnerOperPortPriority = pl->PartnerAdminPortPriority; 18137c478bd9Sstevel@tonic-gate pl->PartnerOperSystem = pl->PartnerAdminSystem; 18147c478bd9Sstevel@tonic-gate pl->PartnerOperSysPriority = pl->PartnerAdminSysPriority; 18157c478bd9Sstevel@tonic-gate pl->PartnerOperKey = pl->PartnerAdminKey; 18167c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.state = pl->PartnerAdminPortState.state; 18177c478bd9Sstevel@tonic-gate 18187c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.defaulted = B_TRUE; 18197c478bd9Sstevel@tonic-gate } 18207c478bd9Sstevel@tonic-gate 18217c478bd9Sstevel@tonic-gate 18227c478bd9Sstevel@tonic-gate /* Returns B_TRUE on sync value changing */ 18237c478bd9Sstevel@tonic-gate static boolean_t 18247c478bd9Sstevel@tonic-gate record_PDU(aggr_port_t *portp, lacp_t *lacp) 18257c478bd9Sstevel@tonic-gate { 18267c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 18277c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 18287c478bd9Sstevel@tonic-gate uint8_t save_sync; 18297c478bd9Sstevel@tonic-gate 1830da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(aggrp->lg_mh)); 18317c478bd9Sstevel@tonic-gate 18327c478bd9Sstevel@tonic-gate /* 18337c478bd9Sstevel@tonic-gate * Partner Information 18347c478bd9Sstevel@tonic-gate */ 18357c478bd9Sstevel@tonic-gate pl->PartnerOperPortNum = ntohs(lacp->actor_info.port); 18367c478bd9Sstevel@tonic-gate pl->PartnerOperPortPriority = 18377c478bd9Sstevel@tonic-gate ntohs(lacp->actor_info.port_priority); 18387c478bd9Sstevel@tonic-gate pl->PartnerOperSystem = lacp->actor_info.system_id; 18397c478bd9Sstevel@tonic-gate pl->PartnerOperSysPriority = 18407c478bd9Sstevel@tonic-gate htons(lacp->actor_info.system_priority); 18417c478bd9Sstevel@tonic-gate pl->PartnerOperKey = ntohs(lacp->actor_info.key); 18427c478bd9Sstevel@tonic-gate 18437c478bd9Sstevel@tonic-gate /* All state info except for Synchronization */ 18447c478bd9Sstevel@tonic-gate save_sync = pl->PartnerOperPortState.bit.sync; 18457c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.state = lacp->actor_info.state.state; 18467c478bd9Sstevel@tonic-gate 18477c478bd9Sstevel@tonic-gate /* Defaulted set to FALSE */ 18487c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.defaulted = B_FALSE; 18497c478bd9Sstevel@tonic-gate 18507c478bd9Sstevel@tonic-gate /* 18517c478bd9Sstevel@tonic-gate * 43.4.9 - (Partner_Port, Partner_Port_Priority, Partner_system, 18527c478bd9Sstevel@tonic-gate * Partner_System_Priority, Partner_Key, and 18537c478bd9Sstevel@tonic-gate * Partner_State.Aggregation) are compared to the 18547c478bd9Sstevel@tonic-gate * corresponding operations paramters values for 18557c478bd9Sstevel@tonic-gate * the Actor. If these are equal, or if this is 18567c478bd9Sstevel@tonic-gate * an individual link, we are synchronized. 18577c478bd9Sstevel@tonic-gate */ 18587c478bd9Sstevel@tonic-gate if (((ntohs(lacp->partner_info.port) == pl->ActorPortNumber) && 18597c478bd9Sstevel@tonic-gate (ntohs(lacp->partner_info.port_priority) == 18607c478bd9Sstevel@tonic-gate pl->ActorPortPriority) && 18617c478bd9Sstevel@tonic-gate (ether_cmp(&lacp->partner_info.system_id, 18627c478bd9Sstevel@tonic-gate (struct ether_addr *)&aggrp->lg_addr) == 0) && 18637c478bd9Sstevel@tonic-gate (ntohs(lacp->partner_info.system_priority) == 18647c478bd9Sstevel@tonic-gate aggrp->aggr.ActorSystemPriority) && 18657c478bd9Sstevel@tonic-gate (ntohs(lacp->partner_info.key) == pl->ActorOperPortKey) && 18667c478bd9Sstevel@tonic-gate (lacp->partner_info.state.bit.aggregation == 18677c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.aggregation)) || 18687c478bd9Sstevel@tonic-gate (!lacp->actor_info.state.bit.aggregation)) { 18697c478bd9Sstevel@tonic-gate 18707c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync = 18717c478bd9Sstevel@tonic-gate lacp->actor_info.state.bit.sync; 18727c478bd9Sstevel@tonic-gate } else { 18737c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync = B_FALSE; 18747c478bd9Sstevel@tonic-gate } 18757c478bd9Sstevel@tonic-gate 18767c478bd9Sstevel@tonic-gate if (save_sync != pl->PartnerOperPortState.bit.sync) { 1877d62bc4baSyz147064 AGGR_LACP_DBG(("record_PDU:(%d): partner sync " 1878d62bc4baSyz147064 "%d -->%d\n", portp->lp_linkid, save_sync, 1879ba2e4443Sseb pl->PartnerOperPortState.bit.sync)); 18807c478bd9Sstevel@tonic-gate return (B_TRUE); 18817c478bd9Sstevel@tonic-gate } else { 18827c478bd9Sstevel@tonic-gate return (B_FALSE); 18837c478bd9Sstevel@tonic-gate } 18847c478bd9Sstevel@tonic-gate } 18857c478bd9Sstevel@tonic-gate 18867c478bd9Sstevel@tonic-gate 18877c478bd9Sstevel@tonic-gate /* 18887c478bd9Sstevel@tonic-gate * update_selected - If any of the Partner parameters has 18897c478bd9Sstevel@tonic-gate * changed from a previous value, then 18907c478bd9Sstevel@tonic-gate * unselect the link from the aggregator. 18917c478bd9Sstevel@tonic-gate */ 18927c478bd9Sstevel@tonic-gate static boolean_t 18937c478bd9Sstevel@tonic-gate update_selected(aggr_port_t *portp, lacp_t *lacp) 18947c478bd9Sstevel@tonic-gate { 18957c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 18967c478bd9Sstevel@tonic-gate 1897da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 18987c478bd9Sstevel@tonic-gate 18997c478bd9Sstevel@tonic-gate if ((pl->PartnerOperPortNum != ntohs(lacp->actor_info.port)) || 19007c478bd9Sstevel@tonic-gate (pl->PartnerOperPortPriority != 19017c478bd9Sstevel@tonic-gate ntohs(lacp->actor_info.port_priority)) || 19027c478bd9Sstevel@tonic-gate (ether_cmp(&pl->PartnerOperSystem, 19037c478bd9Sstevel@tonic-gate &lacp->actor_info.system_id) != 0) || 19047c478bd9Sstevel@tonic-gate (pl->PartnerOperSysPriority != 19057c478bd9Sstevel@tonic-gate ntohs(lacp->actor_info.system_priority)) || 19067c478bd9Sstevel@tonic-gate (pl->PartnerOperKey != ntohs(lacp->actor_info.key)) || 19077c478bd9Sstevel@tonic-gate (pl->PartnerOperPortState.bit.aggregation != 19087c478bd9Sstevel@tonic-gate lacp->actor_info.state.bit.aggregation)) { 1909d62bc4baSyz147064 AGGR_LACP_DBG(("update_selected:(%d): " 1910d62bc4baSyz147064 "selected %d-->%d\n", portp->lp_linkid, pl->sm.selected, 1911ba2e4443Sseb AGGR_UNSELECTED)); 19127c478bd9Sstevel@tonic-gate 1913f12af565Snd99603 lacp_port_unselect(portp); 19147c478bd9Sstevel@tonic-gate return (B_TRUE); 19157c478bd9Sstevel@tonic-gate } else { 19167c478bd9Sstevel@tonic-gate return (B_FALSE); 19177c478bd9Sstevel@tonic-gate } 19187c478bd9Sstevel@tonic-gate } 19197c478bd9Sstevel@tonic-gate 19207c478bd9Sstevel@tonic-gate 19217c478bd9Sstevel@tonic-gate /* 19227c478bd9Sstevel@tonic-gate * update_default_selected - If any of the operational Partner parameters 19237c478bd9Sstevel@tonic-gate * is different than that of the administrative values 19247c478bd9Sstevel@tonic-gate * then unselect the link from the aggregator. 19257c478bd9Sstevel@tonic-gate */ 19267c478bd9Sstevel@tonic-gate static void 19277c478bd9Sstevel@tonic-gate update_default_selected(aggr_port_t *portp) 19287c478bd9Sstevel@tonic-gate { 19297c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 19307c478bd9Sstevel@tonic-gate 1931da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 19327c478bd9Sstevel@tonic-gate 19337c478bd9Sstevel@tonic-gate if ((pl->PartnerAdminPortNum != pl->PartnerOperPortNum) || 19347c478bd9Sstevel@tonic-gate (pl->PartnerOperPortPriority != pl->PartnerAdminPortPriority) || 19357c478bd9Sstevel@tonic-gate (ether_cmp(&pl->PartnerOperSystem, &pl->PartnerAdminSystem) != 0) || 19367c478bd9Sstevel@tonic-gate (pl->PartnerOperSysPriority != pl->PartnerAdminSysPriority) || 19377c478bd9Sstevel@tonic-gate (pl->PartnerOperKey != pl->PartnerAdminKey) || 19387c478bd9Sstevel@tonic-gate (pl->PartnerOperPortState.bit.aggregation != 19397c478bd9Sstevel@tonic-gate pl->PartnerAdminPortState.bit.aggregation)) { 19407c478bd9Sstevel@tonic-gate 1941d62bc4baSyz147064 AGGR_LACP_DBG(("update_default_selected:(%d): " 1942d62bc4baSyz147064 "selected %d-->%d\n", portp->lp_linkid, 19437c478bd9Sstevel@tonic-gate pl->sm.selected, AGGR_UNSELECTED)); 1944f12af565Snd99603 1945f12af565Snd99603 lacp_port_unselect(portp); 19467c478bd9Sstevel@tonic-gate } 19477c478bd9Sstevel@tonic-gate } 19487c478bd9Sstevel@tonic-gate 19497c478bd9Sstevel@tonic-gate 19507c478bd9Sstevel@tonic-gate /* 19517c478bd9Sstevel@tonic-gate * update_NTT - If any of the Partner values in the received LACPDU 19527c478bd9Sstevel@tonic-gate * are different than that of the Actor operational 19537c478bd9Sstevel@tonic-gate * values then set NTT to true. 19547c478bd9Sstevel@tonic-gate */ 19557c478bd9Sstevel@tonic-gate static void 19567c478bd9Sstevel@tonic-gate update_NTT(aggr_port_t *portp, lacp_t *lacp) 19577c478bd9Sstevel@tonic-gate { 19587c478bd9Sstevel@tonic-gate aggr_grp_t *aggrp = portp->lp_grp; 19597c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 19607c478bd9Sstevel@tonic-gate 1961da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(aggrp->lg_mh)); 19627c478bd9Sstevel@tonic-gate 19637c478bd9Sstevel@tonic-gate if ((pl->ActorPortNumber != ntohs(lacp->partner_info.port)) || 19647c478bd9Sstevel@tonic-gate (pl->ActorPortPriority != 19657c478bd9Sstevel@tonic-gate ntohs(lacp->partner_info.port_priority)) || 19667c478bd9Sstevel@tonic-gate (ether_cmp(&aggrp->lg_addr, 19677c478bd9Sstevel@tonic-gate &lacp->partner_info.system_id) != 0) || 19687c478bd9Sstevel@tonic-gate (aggrp->aggr.ActorSystemPriority != 19697c478bd9Sstevel@tonic-gate ntohs(lacp->partner_info.system_priority)) || 19707c478bd9Sstevel@tonic-gate (pl->ActorOperPortKey != ntohs(lacp->partner_info.key)) || 19717c478bd9Sstevel@tonic-gate (pl->ActorOperPortState.bit.activity != 19727c478bd9Sstevel@tonic-gate lacp->partner_info.state.bit.activity) || 19737c478bd9Sstevel@tonic-gate (pl->ActorOperPortState.bit.timeout != 19747c478bd9Sstevel@tonic-gate lacp->partner_info.state.bit.timeout) || 19757c478bd9Sstevel@tonic-gate (pl->ActorOperPortState.bit.sync != 19767c478bd9Sstevel@tonic-gate lacp->partner_info.state.bit.sync) || 19777c478bd9Sstevel@tonic-gate (pl->ActorOperPortState.bit.aggregation != 19787c478bd9Sstevel@tonic-gate lacp->partner_info.state.bit.aggregation)) { 19797c478bd9Sstevel@tonic-gate 1980d62bc4baSyz147064 AGGR_LACP_DBG(("update_NTT:(%d): NTT %d-->%d\n", 1981d62bc4baSyz147064 portp->lp_linkid, pl->NTT, B_TRUE)); 19827c478bd9Sstevel@tonic-gate 19837c478bd9Sstevel@tonic-gate pl->NTT = B_TRUE; 19847c478bd9Sstevel@tonic-gate } 19857c478bd9Sstevel@tonic-gate } 19867c478bd9Sstevel@tonic-gate 19877c478bd9Sstevel@tonic-gate /* 19887c478bd9Sstevel@tonic-gate * lacp_receive_sm - LACP receive state machine 19897c478bd9Sstevel@tonic-gate * 19907c478bd9Sstevel@tonic-gate * parameters: 19917c478bd9Sstevel@tonic-gate * - portp - instance this applies to. 19927c478bd9Sstevel@tonic-gate * - lacp - pointer in the case of a received LACPDU. 19937c478bd9Sstevel@tonic-gate * This value is NULL if there is no LACPDU. 19947c478bd9Sstevel@tonic-gate * 19957c478bd9Sstevel@tonic-gate * invoked: 19967c478bd9Sstevel@tonic-gate * - when initialization is needed 19977c478bd9Sstevel@tonic-gate * - upon reception of an LACPDU. This is the common case. 19987c478bd9Sstevel@tonic-gate * - every time the current_while_timer pops 19997c478bd9Sstevel@tonic-gate */ 20007c478bd9Sstevel@tonic-gate static void 20017c478bd9Sstevel@tonic-gate lacp_receive_sm(aggr_port_t *portp, lacp_t *lacp) 20027c478bd9Sstevel@tonic-gate { 20037c478bd9Sstevel@tonic-gate boolean_t sync_updated, selected_updated, save_activity; 20047c478bd9Sstevel@tonic-gate aggr_lacp_port_t *pl = &portp->lp_lacp; 20057c478bd9Sstevel@tonic-gate lacp_receive_state_t oldstate = pl->sm.receive_state; 20067c478bd9Sstevel@tonic-gate 2007da14cebeSEric Cheng ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh)); 20087c478bd9Sstevel@tonic-gate 20097c478bd9Sstevel@tonic-gate /* LACP_OFF state not in specification so check here. */ 20107c478bd9Sstevel@tonic-gate if (!pl->sm.lacp_on) 20117c478bd9Sstevel@tonic-gate return; 20127c478bd9Sstevel@tonic-gate 20137c478bd9Sstevel@tonic-gate /* figure next state */ 20147c478bd9Sstevel@tonic-gate if (pl->sm.begin || pl->sm.port_moved) { 20157c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_INITIALIZE; 20167c478bd9Sstevel@tonic-gate } else if (!pl->sm.port_enabled) { /* DL_NOTE_LINK_DOWN */ 20177c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_PORT_DISABLED; 20187c478bd9Sstevel@tonic-gate } else if (!pl->sm.lacp_enabled) { /* DL_NOTE_AGGR_UNAVAIL */ 20197c478bd9Sstevel@tonic-gate pl->sm.receive_state = 20207c478bd9Sstevel@tonic-gate (pl->sm.receive_state == LACP_PORT_DISABLED) ? 20217c478bd9Sstevel@tonic-gate LACP_DISABLED : LACP_PORT_DISABLED; 20227c478bd9Sstevel@tonic-gate } else if (lacp != NULL) { 20237c478bd9Sstevel@tonic-gate if ((pl->sm.receive_state == LACP_EXPIRED) || 20247c478bd9Sstevel@tonic-gate (pl->sm.receive_state == LACP_DEFAULTED)) { 20257c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_CURRENT; 20267c478bd9Sstevel@tonic-gate } 20277c478bd9Sstevel@tonic-gate } else if ((pl->sm.receive_state == LACP_CURRENT) && 20287c478bd9Sstevel@tonic-gate (pl->current_while_timer.id == 0)) { 20297c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_EXPIRED; 20307c478bd9Sstevel@tonic-gate } else if ((pl->sm.receive_state == LACP_EXPIRED) && 20317c478bd9Sstevel@tonic-gate (pl->current_while_timer.id == 0)) { 20327c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_DEFAULTED; 20337c478bd9Sstevel@tonic-gate } 20347c478bd9Sstevel@tonic-gate 20357c478bd9Sstevel@tonic-gate if (!((lacp && (oldstate == LACP_CURRENT) && 20367c478bd9Sstevel@tonic-gate (pl->sm.receive_state == LACP_CURRENT)))) { 2037d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_receive_sm(%d):%s--->%s\n", 2038d62bc4baSyz147064 portp->lp_linkid, lacp_receive_str[oldstate], 20397c478bd9Sstevel@tonic-gate lacp_receive_str[pl->sm.receive_state])); 20407c478bd9Sstevel@tonic-gate } 20417c478bd9Sstevel@tonic-gate 20427c478bd9Sstevel@tonic-gate switch (pl->sm.receive_state) { 20437c478bd9Sstevel@tonic-gate case LACP_INITIALIZE: 2044f12af565Snd99603 lacp_port_unselect(portp); 20457c478bd9Sstevel@tonic-gate record_Default(portp); 20467c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_FALSE; 20477c478bd9Sstevel@tonic-gate pl->sm.port_moved = B_FALSE; 20487c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_PORT_DISABLED; 20497c478bd9Sstevel@tonic-gate pl->sm.begin = B_FALSE; 20507c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, NULL); 20517c478bd9Sstevel@tonic-gate break; 20527c478bd9Sstevel@tonic-gate 20537c478bd9Sstevel@tonic-gate case LACP_PORT_DISABLED: 20547c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync = B_FALSE; 20557c478bd9Sstevel@tonic-gate /* 20567c478bd9Sstevel@tonic-gate * Stop current_while_timer in case 20577c478bd9Sstevel@tonic-gate * we got here from link down 20587c478bd9Sstevel@tonic-gate */ 20597c478bd9Sstevel@tonic-gate stop_current_while_timer(portp); 20607c478bd9Sstevel@tonic-gate 20617c478bd9Sstevel@tonic-gate if (pl->sm.port_enabled && !pl->sm.lacp_enabled) { 20627c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_DISABLED; 20637c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, lacp); 20647c478bd9Sstevel@tonic-gate /* We goto LACP_DISABLED state */ 20657c478bd9Sstevel@tonic-gate break; 20667c478bd9Sstevel@tonic-gate } else if (pl->sm.port_enabled && pl->sm.lacp_enabled) { 20677c478bd9Sstevel@tonic-gate pl->sm.receive_state = LACP_EXPIRED; 20687c478bd9Sstevel@tonic-gate /* 20697c478bd9Sstevel@tonic-gate * FALL THROUGH TO LACP_EXPIRED CASE: 20707c478bd9Sstevel@tonic-gate * We have no way of knowing if we get into 20717c478bd9Sstevel@tonic-gate * lacp_receive_sm() from a current_while_timer 20727c478bd9Sstevel@tonic-gate * expiring as it has never been kicked off yet! 20737c478bd9Sstevel@tonic-gate */ 20747c478bd9Sstevel@tonic-gate } else { 20757c478bd9Sstevel@tonic-gate /* We stay in LACP_PORT_DISABLED state */ 20767c478bd9Sstevel@tonic-gate break; 20777c478bd9Sstevel@tonic-gate } 20787c478bd9Sstevel@tonic-gate /* LACP_PORT_DISABLED -> LACP_EXPIRED */ 20797c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 20807c478bd9Sstevel@tonic-gate 20817c478bd9Sstevel@tonic-gate case LACP_EXPIRED: 20827c478bd9Sstevel@tonic-gate /* 20837c478bd9Sstevel@tonic-gate * Arrives here from LACP_PORT_DISABLED state as well as 20847c478bd9Sstevel@tonic-gate * as well as current_while_timer expiring. 20857c478bd9Sstevel@tonic-gate */ 20867c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync = B_FALSE; 20877c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.timeout = B_TRUE; 20887c478bd9Sstevel@tonic-gate 20897c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_TRUE; 20907c478bd9Sstevel@tonic-gate start_current_while_timer(portp, SHORT_TIMEOUT_TIME); 20917c478bd9Sstevel@tonic-gate lacp_periodic_sm(portp); 20927c478bd9Sstevel@tonic-gate break; 20937c478bd9Sstevel@tonic-gate 20947c478bd9Sstevel@tonic-gate case LACP_DISABLED: 20957c478bd9Sstevel@tonic-gate /* 20967c478bd9Sstevel@tonic-gate * This is the normal state for recv_sm when LACP_OFF 20977c478bd9Sstevel@tonic-gate * is set or the NIC is in half duplex mode. 20987c478bd9Sstevel@tonic-gate */ 2099f12af565Snd99603 lacp_port_unselect(portp); 21007c478bd9Sstevel@tonic-gate record_Default(portp); 21017c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.aggregation = B_FALSE; 21027c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_FALSE; 21037c478bd9Sstevel@tonic-gate break; 21047c478bd9Sstevel@tonic-gate 21057c478bd9Sstevel@tonic-gate case LACP_DEFAULTED: 21067c478bd9Sstevel@tonic-gate /* 21077c478bd9Sstevel@tonic-gate * Current_while_timer expired a second time. 21087c478bd9Sstevel@tonic-gate */ 21097c478bd9Sstevel@tonic-gate update_default_selected(portp); 21107c478bd9Sstevel@tonic-gate record_Default(portp); /* overwrite Partner Oper val */ 21117c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_FALSE; 21127c478bd9Sstevel@tonic-gate pl->PartnerOperPortState.bit.sync = B_TRUE; 21137c478bd9Sstevel@tonic-gate 21147c478bd9Sstevel@tonic-gate lacp_selection_logic(portp); 21157c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 21167c478bd9Sstevel@tonic-gate break; 21177c478bd9Sstevel@tonic-gate 21187c478bd9Sstevel@tonic-gate case LACP_CURRENT: 21197c478bd9Sstevel@tonic-gate /* 21207c478bd9Sstevel@tonic-gate * Reception of LACPDU 21217c478bd9Sstevel@tonic-gate */ 21227c478bd9Sstevel@tonic-gate 21237c478bd9Sstevel@tonic-gate if (!lacp) /* no LACPDU so current_while_timer popped */ 21247c478bd9Sstevel@tonic-gate break; 21257c478bd9Sstevel@tonic-gate 2126d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_receive_sm: (%d): LACPDU received:\n", 2127d62bc4baSyz147064 portp->lp_linkid)); 21287c478bd9Sstevel@tonic-gate 21297c478bd9Sstevel@tonic-gate /* 21307c478bd9Sstevel@tonic-gate * Validate Actor_Information_Length, 21317c478bd9Sstevel@tonic-gate * Partner_Information_Length, Collector_Information_Length, 21327c478bd9Sstevel@tonic-gate * and Terminator_Length fields. 21337c478bd9Sstevel@tonic-gate */ 21347c478bd9Sstevel@tonic-gate if (!valid_lacp_pdu(portp, lacp)) { 2135d62bc4baSyz147064 AGGR_LACP_DBG(("lacp_receive_sm (%d): " 21367c478bd9Sstevel@tonic-gate "Invalid LACPDU received\n", 2137d62bc4baSyz147064 portp->lp_linkid)); 21387c478bd9Sstevel@tonic-gate break; 21397c478bd9Sstevel@tonic-gate } 21407c478bd9Sstevel@tonic-gate 21417c478bd9Sstevel@tonic-gate save_activity = pl->PartnerOperPortState.bit.activity; 21427c478bd9Sstevel@tonic-gate selected_updated = update_selected(portp, lacp); 21437c478bd9Sstevel@tonic-gate update_NTT(portp, lacp); 21447c478bd9Sstevel@tonic-gate sync_updated = record_PDU(portp, lacp); 21457c478bd9Sstevel@tonic-gate 21467c478bd9Sstevel@tonic-gate pl->ActorOperPortState.bit.expired = B_FALSE; 21477c478bd9Sstevel@tonic-gate 21487c478bd9Sstevel@tonic-gate if (selected_updated) { 21497c478bd9Sstevel@tonic-gate lacp_selection_logic(portp); 21507c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 21517c478bd9Sstevel@tonic-gate } else if (sync_updated) { 21527c478bd9Sstevel@tonic-gate lacp_mux_sm(portp); 21537c478bd9Sstevel@tonic-gate } 21547c478bd9Sstevel@tonic-gate 21557c478bd9Sstevel@tonic-gate /* 21567c478bd9Sstevel@tonic-gate * If the periodic timer value bit has been modified 21577c478bd9Sstevel@tonic-gate * or the partner activity bit has been changed then 21587c478bd9Sstevel@tonic-gate * we need to respectively: 21597c478bd9Sstevel@tonic-gate * - restart the timer with the proper timeout value. 21607c478bd9Sstevel@tonic-gate * - possibly enable/disable transmission of LACPDUs. 21617c478bd9Sstevel@tonic-gate */ 21627c478bd9Sstevel@tonic-gate if ((pl->PartnerOperPortState.bit.timeout && 21637c478bd9Sstevel@tonic-gate (pl->periodic_timer.val != FAST_PERIODIC_TIME)) || 21647c478bd9Sstevel@tonic-gate (!pl->PartnerOperPortState.bit.timeout && 21657c478bd9Sstevel@tonic-gate (pl->periodic_timer.val != SLOW_PERIODIC_TIME)) || 21667c478bd9Sstevel@tonic-gate (pl->PartnerOperPortState.bit.activity != 21677c478bd9Sstevel@tonic-gate save_activity)) { 21687c478bd9Sstevel@tonic-gate lacp_periodic_sm(portp); 21697c478bd9Sstevel@tonic-gate } 21707c478bd9Sstevel@tonic-gate 21717c478bd9Sstevel@tonic-gate stop_current_while_timer(portp); 21727c478bd9Sstevel@tonic-gate /* Check if we need to transmit an LACPDU */ 21737c478bd9Sstevel@tonic-gate if (pl->NTT) 21747c478bd9Sstevel@tonic-gate lacp_xmit_sm(portp); 21757c478bd9Sstevel@tonic-gate start_current_while_timer(portp, 0); 21767c478bd9Sstevel@tonic-gate 21777c478bd9Sstevel@tonic-gate break; 21787c478bd9Sstevel@tonic-gate } 21797c478bd9Sstevel@tonic-gate } 21807c478bd9Sstevel@tonic-gate 21817c478bd9Sstevel@tonic-gate static void 21827c478bd9Sstevel@tonic-gate aggr_set_coll_dist(aggr_port_t *portp, boolean_t enable) 21837c478bd9Sstevel@tonic-gate { 2184da14cebeSEric Cheng mac_perim_handle_t mph; 21857c478bd9Sstevel@tonic-gate 2186d62bc4baSyz147064 AGGR_LACP_DBG(("AGGR_SET_COLL_DIST_TYPE: (%d) %s\n", 2187d62bc4baSyz147064 portp->lp_linkid, enable ? "ENABLED" : "DISABLED")); 21887c478bd9Sstevel@tonic-gate 2189da14cebeSEric Cheng mac_perim_enter_by_mh(portp->lp_mh, &mph); 21907c478bd9Sstevel@tonic-gate if (!enable) { 21917c478bd9Sstevel@tonic-gate /* 21927c478bd9Sstevel@tonic-gate * Turn OFF Collector_Distributor. 21937c478bd9Sstevel@tonic-gate */ 21947c478bd9Sstevel@tonic-gate portp->lp_collector_enabled = B_FALSE; 21957c478bd9Sstevel@tonic-gate aggr_send_port_disable(portp); 2196da14cebeSEric Cheng goto done; 21977c478bd9Sstevel@tonic-gate } 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate /* 22007c478bd9Sstevel@tonic-gate * Turn ON Collector_Distributor. 22017c478bd9Sstevel@tonic-gate */ 22027c478bd9Sstevel@tonic-gate 22037c478bd9Sstevel@tonic-gate if (!portp->lp_lacp.sm.lacp_on || (portp->lp_lacp.sm.lacp_on && 22047c478bd9Sstevel@tonic-gate (portp->lp_lacp.sm.mux_state == LACP_COLLECTING_DISTRIBUTING))) { 22057c478bd9Sstevel@tonic-gate /* Port is compatible and can be aggregated */ 22067c478bd9Sstevel@tonic-gate portp->lp_collector_enabled = B_TRUE; 22077c478bd9Sstevel@tonic-gate aggr_send_port_enable(portp); 22087c478bd9Sstevel@tonic-gate } 2209da14cebeSEric Cheng 2210da14cebeSEric Cheng done: 2211da14cebeSEric Cheng mac_perim_exit(mph); 22127c478bd9Sstevel@tonic-gate } 22137c478bd9Sstevel@tonic-gate 22147c478bd9Sstevel@tonic-gate /* 2215da14cebeSEric Cheng * Because the LACP packet processing needs to enter the aggr's mac perimeter 2216da14cebeSEric Cheng * and that would potentially cause a deadlock with the thread in which the 2217da14cebeSEric Cheng * grp/port is deleted, we defer the packet process to a worker thread. Here 2218da14cebeSEric Cheng * we only enqueue the received Marker or LACPDU for later processing. 22197c478bd9Sstevel@tonic-gate */ 22207c478bd9Sstevel@tonic-gate void 2221da14cebeSEric Cheng aggr_lacp_rx_enqueue(aggr_port_t *portp, mblk_t *dmp) 22227c478bd9Sstevel@tonic-gate { 2223da14cebeSEric Cheng aggr_grp_t *grp = portp->lp_grp; 22247c478bd9Sstevel@tonic-gate lacp_t *lacp; 22257c478bd9Sstevel@tonic-gate 22267c478bd9Sstevel@tonic-gate dmp->b_rptr += sizeof (struct ether_header); 22277c478bd9Sstevel@tonic-gate 22287c478bd9Sstevel@tonic-gate if (MBLKL(dmp) < sizeof (lacp_t)) { 22297c478bd9Sstevel@tonic-gate freemsg(dmp); 22307c478bd9Sstevel@tonic-gate return; 22317c478bd9Sstevel@tonic-gate } 22327c478bd9Sstevel@tonic-gate 22337c478bd9Sstevel@tonic-gate lacp = (lacp_t *)dmp->b_rptr; 2234da14cebeSEric Cheng if (lacp->subtype != LACP_SUBTYPE && lacp->subtype != MARKER_SUBTYPE) { 2235da14cebeSEric Cheng AGGR_LACP_DBG(("aggr_lacp_rx_enqueue: (%d): " 2236da14cebeSEric Cheng "Unknown Slow Protocol type %d\n", 2237da14cebeSEric Cheng portp->lp_linkid, lacp->subtype)); 2238da14cebeSEric Cheng freemsg(dmp); 2239da14cebeSEric Cheng return; 2240da14cebeSEric Cheng } 22417c478bd9Sstevel@tonic-gate 2242da14cebeSEric Cheng mutex_enter(&grp->lg_lacp_lock); 2243da14cebeSEric Cheng 2244da14cebeSEric Cheng /* 2245da14cebeSEric Cheng * If the lg_lacp_done is set, this aggregation is in the process of 2246da14cebeSEric Cheng * being deleted, return directly. 2247da14cebeSEric Cheng */ 2248da14cebeSEric Cheng if (grp->lg_lacp_done) { 2249da14cebeSEric Cheng mutex_exit(&grp->lg_lacp_lock); 2250da14cebeSEric Cheng freemsg(dmp); 2251da14cebeSEric Cheng return; 2252da14cebeSEric Cheng } 2253da14cebeSEric Cheng 2254da14cebeSEric Cheng if (grp->lg_lacp_tail == NULL) { 2255da14cebeSEric Cheng grp->lg_lacp_head = grp->lg_lacp_tail = dmp; 2256da14cebeSEric Cheng } else { 2257da14cebeSEric Cheng grp->lg_lacp_tail->b_next = dmp; 2258da14cebeSEric Cheng grp->lg_lacp_tail = dmp; 2259da14cebeSEric Cheng } 2260da14cebeSEric Cheng 2261da14cebeSEric Cheng /* 2262da14cebeSEric Cheng * Hold a reference of the port so that the port won't be freed when it 2263da14cebeSEric Cheng * is removed from the aggr. The b_prev field is borrowed to save the 2264da14cebeSEric Cheng * port information. 2265da14cebeSEric Cheng */ 2266da14cebeSEric Cheng AGGR_PORT_REFHOLD(portp); 2267da14cebeSEric Cheng dmp->b_prev = (mblk_t *)portp; 2268da14cebeSEric Cheng cv_broadcast(&grp->lg_lacp_cv); 2269da14cebeSEric Cheng mutex_exit(&grp->lg_lacp_lock); 2270da14cebeSEric Cheng } 2271da14cebeSEric Cheng 2272da14cebeSEric Cheng static void 2273da14cebeSEric Cheng aggr_lacp_rx(mblk_t *dmp) 2274da14cebeSEric Cheng { 2275da14cebeSEric Cheng aggr_port_t *portp = (aggr_port_t *)dmp->b_prev; 2276da14cebeSEric Cheng mac_perim_handle_t mph; 2277da14cebeSEric Cheng lacp_t *lacp; 2278da14cebeSEric Cheng 2279da14cebeSEric Cheng dmp->b_prev = NULL; 2280da14cebeSEric Cheng 2281da14cebeSEric Cheng mac_perim_enter_by_mh(portp->lp_grp->lg_mh, &mph); 2282da14cebeSEric Cheng if (portp->lp_closing) 2283da14cebeSEric Cheng goto done; 2284da14cebeSEric Cheng 2285da14cebeSEric Cheng lacp = (lacp_t *)dmp->b_rptr; 22867c478bd9Sstevel@tonic-gate switch (lacp->subtype) { 22877c478bd9Sstevel@tonic-gate case LACP_SUBTYPE: 2288d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_rx:(%d): LACPDU received.\n", 2289d62bc4baSyz147064 portp->lp_linkid)); 22907c478bd9Sstevel@tonic-gate 22917c478bd9Sstevel@tonic-gate if (!portp->lp_lacp.sm.lacp_on) { 22927c478bd9Sstevel@tonic-gate break; 22937c478bd9Sstevel@tonic-gate } 22947c478bd9Sstevel@tonic-gate lacp_receive_sm(portp, lacp); 22957c478bd9Sstevel@tonic-gate break; 22967c478bd9Sstevel@tonic-gate 22977c478bd9Sstevel@tonic-gate case MARKER_SUBTYPE: 2298d62bc4baSyz147064 AGGR_LACP_DBG(("aggr_lacp_rx:(%d): Marker Packet received.\n", 2299d62bc4baSyz147064 portp->lp_linkid)); 23007c478bd9Sstevel@tonic-gate 2301da14cebeSEric Cheng if (receive_marker_pdu(portp, dmp) != 0) 23027c478bd9Sstevel@tonic-gate break; 23037c478bd9Sstevel@tonic-gate 23040dc2366fSVenugopal Iyer /* Send the packet over the first TX ring */ 23050dc2366fSVenugopal Iyer dmp = mac_hwring_send_priv(portp->lp_mch, 23060dc2366fSVenugopal Iyer portp->lp_tx_rings[0], dmp); 23070dc2366fSVenugopal Iyer if (dmp != NULL) 23080dc2366fSVenugopal Iyer freemsg(dmp); 2309da14cebeSEric Cheng mac_perim_exit(mph); 2310da14cebeSEric Cheng AGGR_PORT_REFRELE(portp); 2311da14cebeSEric Cheng return; 23127c478bd9Sstevel@tonic-gate } 23137c478bd9Sstevel@tonic-gate 2314da14cebeSEric Cheng done: 2315da14cebeSEric Cheng mac_perim_exit(mph); 2316da14cebeSEric Cheng AGGR_PORT_REFRELE(portp); 23177c478bd9Sstevel@tonic-gate freemsg(dmp); 23187c478bd9Sstevel@tonic-gate } 2319da14cebeSEric Cheng 2320da14cebeSEric Cheng void 2321da14cebeSEric Cheng aggr_lacp_rx_thread(void *arg) 2322da14cebeSEric Cheng { 2323da14cebeSEric Cheng callb_cpr_t cprinfo; 2324da14cebeSEric Cheng aggr_grp_t *grp = (aggr_grp_t *)arg; 2325da14cebeSEric Cheng aggr_port_t *port; 2326da14cebeSEric Cheng mblk_t *mp, *nextmp; 2327da14cebeSEric Cheng 2328da14cebeSEric Cheng CALLB_CPR_INIT(&cprinfo, &grp->lg_lacp_lock, callb_generic_cpr, 2329da14cebeSEric Cheng "aggr_lacp_rx_thread"); 2330da14cebeSEric Cheng 2331da14cebeSEric Cheng mutex_enter(&grp->lg_lacp_lock); 2332da14cebeSEric Cheng 2333da14cebeSEric Cheng /* 2334da14cebeSEric Cheng * Quit the thread if the grp is deleted. 2335da14cebeSEric Cheng */ 2336da14cebeSEric Cheng while (!grp->lg_lacp_done) { 2337da14cebeSEric Cheng if ((mp = grp->lg_lacp_head) == NULL) { 2338da14cebeSEric Cheng CALLB_CPR_SAFE_BEGIN(&cprinfo); 2339da14cebeSEric Cheng cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock); 2340da14cebeSEric Cheng CALLB_CPR_SAFE_END(&cprinfo, &grp->lg_lacp_lock); 2341da14cebeSEric Cheng continue; 2342da14cebeSEric Cheng } 2343da14cebeSEric Cheng 2344da14cebeSEric Cheng grp->lg_lacp_head = grp->lg_lacp_tail = NULL; 2345da14cebeSEric Cheng mutex_exit(&grp->lg_lacp_lock); 2346da14cebeSEric Cheng 2347da14cebeSEric Cheng while (mp != NULL) { 2348da14cebeSEric Cheng nextmp = mp->b_next; 2349da14cebeSEric Cheng mp->b_next = NULL; 2350da14cebeSEric Cheng aggr_lacp_rx(mp); 2351da14cebeSEric Cheng mp = nextmp; 2352da14cebeSEric Cheng } 2353da14cebeSEric Cheng mutex_enter(&grp->lg_lacp_lock); 2354da14cebeSEric Cheng } 2355da14cebeSEric Cheng 2356da14cebeSEric Cheng /* 2357da14cebeSEric Cheng * The grp is being destroyed, simply free all of the LACP messages 2358da14cebeSEric Cheng * left in the queue which did not have the chance to be processed. 2359da14cebeSEric Cheng * We cannot use freemsgchain() here since we need to clear the 2360da14cebeSEric Cheng * b_prev field. 2361da14cebeSEric Cheng */ 2362*2ac91f16Smeem for (mp = grp->lg_lacp_head; mp != NULL; mp = nextmp) { 2363da14cebeSEric Cheng port = (aggr_port_t *)mp->b_prev; 2364da14cebeSEric Cheng AGGR_PORT_REFRELE(port); 2365da14cebeSEric Cheng nextmp = mp->b_next; 2366da14cebeSEric Cheng mp->b_next = NULL; 2367da14cebeSEric Cheng mp->b_prev = NULL; 2368da14cebeSEric Cheng freemsg(mp); 2369da14cebeSEric Cheng } 2370da14cebeSEric Cheng 2371da14cebeSEric Cheng grp->lg_lacp_head = grp->lg_lacp_tail = NULL; 2372da14cebeSEric Cheng grp->lg_lacp_rx_thread = NULL; 2373da14cebeSEric Cheng cv_broadcast(&grp->lg_lacp_cv); 2374da14cebeSEric Cheng CALLB_CPR_EXIT(&cprinfo); 2375da14cebeSEric Cheng thread_exit(); 2376da14cebeSEric Cheng } 2377