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
aggr_lacp_init(void)127f12af565Snd99603 aggr_lacp_init(void)
128f12af565Snd99603 {
129f12af565Snd99603 mutex_init(&lacp_sel_lock, NULL, MUTEX_DEFAULT, NULL);
130f12af565Snd99603 }
131f12af565Snd99603
132f12af565Snd99603 void
aggr_lacp_fini(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
aggr_port_timer_thread(void * arg)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
lacp_port_select(aggr_port_t * portp)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
lacp_port_unselect(aggr_port_t * portp)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
aggr_lacp_init_grp(aggr_grp_t * aggrp)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
aggr_lacp_init_port(aggr_port_t * portp)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
lacp_reset_port(aggr_port_t * portp)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
aggr_lacp_mcast_on(aggr_port_t * port)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
aggr_lacp_mcast_off(aggr_port_t * port)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
start_periodic_timer(aggr_port_t * portp)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
stop_periodic_timer(aggr_port_t * portp)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
periodic_timer_pop(void * data)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
periodic_timer_pop_handler(aggr_port_t * portp)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
lacp_periodic_sm(aggr_port_t * portp)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
lacp_xmit_sm(aggr_port_t * portp)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
fill_lacp_ether(aggr_port_t * port,struct ether_header * ether)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
fill_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)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
lacp_mux_sm(aggr_port_t * portp)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
receive_marker_pdu(aggr_port_t * portp,mblk_t * mp)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
aggr_lacp_update_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode)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
aggr_lacp_update_timer(aggr_grp_t * grp,aggr_lacp_timer_t timer)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
aggr_port_lacp_set_mode(aggr_grp_t * grp,aggr_port_t * port)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
aggr_lacp_set_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode,aggr_lacp_timer_t timer)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
lacp_misconfig_check(aggr_port_t * portp)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
lacp_sel_ports_del(aggr_port_t * portp)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
lacp_sel_ports_add(aggr_port_t * portp)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
lacp_selection_logic(aggr_port_t * portp)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
wait_while_timer_pop(void * data)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
wait_while_timer_pop_handler(aggr_port_t * portp)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
start_wait_while_timer(aggr_port_t * portp)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
stop_wait_while_timer(aggr_port_t * portp)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
aggr_lacp_port_attached(aggr_port_t * portp)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
aggr_lacp_port_detached(aggr_port_t * portp)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
lacp_on(aggr_port_t * portp)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
lacp_off(aggr_port_t * portp)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
valid_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)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
start_current_while_timer(aggr_port_t * portp,uint_t time)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
stop_current_while_timer(aggr_port_t * portp)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
current_while_timer_pop(void * data)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
current_while_timer_pop_handler(aggr_port_t * portp)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
record_Default(aggr_port_t * portp)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
record_PDU(aggr_port_t * portp,lacp_t * lacp)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
update_selected(aggr_port_t * portp,lacp_t * lacp)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
update_default_selected(aggr_port_t * portp)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
update_NTT(aggr_port_t * portp,lacp_t * lacp)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
lacp_receive_sm(aggr_port_t * portp,lacp_t * lacp)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
aggr_set_coll_dist(aggr_port_t * portp,boolean_t enable)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
aggr_lacp_rx_enqueue(aggr_port_t * portp,mblk_t * dmp)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
aggr_lacp_rx(mblk_t * dmp)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
aggr_lacp_rx_thread(void * arg)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