xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_lacp.c (revision 666e8af9)
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
5f12af565Snd  * Common Development and Distribution License (the "License").
6f12af565Snd  * 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 /*
222ac91f16Smeem  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23*666e8af9SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * IEEE 802.3ad Link Aggregation - LACP & Marker Protocol processing.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
32da14cebeSEric Cheng #include <sys/callb.h>
337c478bd9Sstevel@tonic-gate #include <sys/conf.h>
347c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
35da14cebeSEric Cheng #include <sys/disp.h>
367c478bd9Sstevel@tonic-gate #include <sys/list.h>
377c478bd9Sstevel@tonic-gate #include <sys/ksynch.h>
387c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
397c478bd9Sstevel@tonic-gate #include <sys/stream.h>
407c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
417c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
427c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
437c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
447c478bd9Sstevel@tonic-gate #include <sys/stat.h>
457c478bd9Sstevel@tonic-gate #include <sys/byteorder.h>
467c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
477c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
48f562e45bSRamesh Kumar Katla #include <sys/sdt.h>
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include <sys/aggr.h>
517c478bd9Sstevel@tonic-gate #include <sys/aggr_impl.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static struct ether_addr	etherzeroaddr = {
547c478bd9Sstevel@tonic-gate 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
557c478bd9Sstevel@tonic-gate };
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Slow_Protocol_Multicast address, as per IEEE 802.3ad spec.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate static struct ether_addr   slow_multicast_addr = {
617c478bd9Sstevel@tonic-gate 	0x01, 0x80, 0xc2, 0x00, 0x00, 0x02
627c478bd9Sstevel@tonic-gate };
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #ifdef DEBUG
657c478bd9Sstevel@tonic-gate /* LACP state machine debugging support */
667c478bd9Sstevel@tonic-gate static uint32_t aggr_lacp_debug = 0;
677c478bd9Sstevel@tonic-gate #define	AGGR_LACP_DBG(x)	if (aggr_lacp_debug) { (void) printf x; }
687c478bd9Sstevel@tonic-gate #else
697c478bd9Sstevel@tonic-gate #define	AGGR_LACP_DBG(x)	{}
707c478bd9Sstevel@tonic-gate #endif /* DEBUG */
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #define	NSECS_PER_SEC   1000000000ll
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /* used by lacp_misconfig_walker() */
757c478bd9Sstevel@tonic-gate typedef struct lacp_misconfig_check_state_s {
767c478bd9Sstevel@tonic-gate 	aggr_port_t *cs_portp;
777c478bd9Sstevel@tonic-gate 	boolean_t cs_found;
787c478bd9Sstevel@tonic-gate } lacp_misconfig_check_state_t;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate static const char *lacp_receive_str[] = LACP_RECEIVE_STATE_STRINGS;
817c478bd9Sstevel@tonic-gate static const char *lacp_periodic_str[] = LACP_PERIODIC_STRINGS;
827c478bd9Sstevel@tonic-gate static const char *lacp_mux_str[] = LACP_MUX_STRINGS;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate static uint16_t lacp_port_priority = 0x1000;
857c478bd9Sstevel@tonic-gate static uint16_t lacp_system_priority = 0x1000;
867c478bd9Sstevel@tonic-gate 
87f12af565Snd /*
88f12af565Snd  * Maintains a list of all ports in ATTACHED state. This information
89f12af565Snd  * is used to detect misconfiguration.
90f12af565Snd  */
91f12af565Snd typedef struct lacp_sel_ports {
92d62bc4baSyz 	datalink_id_t sp_grp_linkid;
93d62bc4baSyz 	datalink_id_t sp_linkid;
9489842603Sseb 	/* Note: sp_partner_system must be 2-byte aligned */
95f12af565Snd 	struct ether_addr sp_partner_system;
96f12af565Snd 	uint32_t sp_partner_key;
97f12af565Snd 	struct lacp_sel_ports *sp_next;
98f12af565Snd } lacp_sel_ports_t;
99f12af565Snd 
100f12af565Snd static lacp_sel_ports_t *sel_ports = NULL;
101f12af565Snd static kmutex_t lacp_sel_lock;
102f12af565Snd 
1037c478bd9Sstevel@tonic-gate static void periodic_timer_pop(void *);
104da14cebeSEric Cheng static void periodic_timer_pop_handler(aggr_port_t *);
1057c478bd9Sstevel@tonic-gate static void lacp_xmit_sm(aggr_port_t *);
1067c478bd9Sstevel@tonic-gate static void lacp_periodic_sm(aggr_port_t *);
1077c478bd9Sstevel@tonic-gate static void fill_lacp_pdu(aggr_port_t *, lacp_t *);
1087c478bd9Sstevel@tonic-gate static void fill_lacp_ether(aggr_port_t *, struct ether_header *);
1097c478bd9Sstevel@tonic-gate static void lacp_on(aggr_port_t *);
1107c478bd9Sstevel@tonic-gate static void lacp_off(aggr_port_t *);
1117c478bd9Sstevel@tonic-gate static boolean_t valid_lacp_pdu(aggr_port_t *, lacp_t *);
1127c478bd9Sstevel@tonic-gate static void lacp_receive_sm(aggr_port_t *, lacp_t *);
1137c478bd9Sstevel@tonic-gate static void aggr_set_coll_dist(aggr_port_t *, boolean_t);
1147c478bd9Sstevel@tonic-gate static void start_wait_while_timer(aggr_port_t *);
1157c478bd9Sstevel@tonic-gate static void stop_wait_while_timer(aggr_port_t *);
1167c478bd9Sstevel@tonic-gate static void lacp_reset_port(aggr_port_t *);
1177c478bd9Sstevel@tonic-gate static void stop_current_while_timer(aggr_port_t *);
1187c478bd9Sstevel@tonic-gate static void current_while_timer_pop(void *);
119da14cebeSEric Cheng static void current_while_timer_pop_handler(aggr_port_t *);
1207c478bd9Sstevel@tonic-gate static void update_default_selected(aggr_port_t *);
1217c478bd9Sstevel@tonic-gate static boolean_t update_selected(aggr_port_t *, lacp_t *);
122f12af565Snd static boolean_t lacp_sel_ports_add(aggr_port_t *);
123f12af565Snd static void lacp_sel_ports_del(aggr_port_t *);
124da14cebeSEric Cheng static void wait_while_timer_pop(void *);
125da14cebeSEric Cheng static void wait_while_timer_pop_handler(aggr_port_t *);
126f12af565Snd 
127f12af565Snd void
aggr_lacp_init(void)128f12af565Snd aggr_lacp_init(void)
129f12af565Snd {
130f12af565Snd 	mutex_init(&lacp_sel_lock, NULL, MUTEX_DEFAULT, NULL);
131f12af565Snd }
132f12af565Snd 
133f12af565Snd void
aggr_lacp_fini(void)134f12af565Snd aggr_lacp_fini(void)
135f12af565Snd {
136f12af565Snd 	mutex_destroy(&lacp_sel_lock);
137f12af565Snd }
1387c478bd9Sstevel@tonic-gate 
139da14cebeSEric Cheng /*
140da14cebeSEric Cheng  * The following functions are used for handling LACP timers.
141da14cebeSEric Cheng  *
142da14cebeSEric Cheng  * Note that we cannot fully rely on the aggr's mac perimeter in the timeout
143da14cebeSEric Cheng  * handler routine, otherwise it may cause deadlock with the untimeout() call
144da14cebeSEric Cheng  * which is usually called with the mac perimeter held. Instead, a
145da14cebeSEric Cheng  * lacp_timer_lock mutex is introduced, which protects a bitwise flag
146da14cebeSEric Cheng  * (lacp_timer_bits). This flag is set/cleared by timeout()/stop_timer()
147da14cebeSEric Cheng  * routines and is checked by a dedicated thread, that executes the real
148da14cebeSEric Cheng  * timeout operation.
149da14cebeSEric Cheng  */
150da14cebeSEric Cheng static void
aggr_port_timer_thread(void * arg)151da14cebeSEric Cheng aggr_port_timer_thread(void *arg)
152da14cebeSEric Cheng {
153da14cebeSEric Cheng 	aggr_port_t		*port = arg;
154da14cebeSEric Cheng 	aggr_lacp_port_t	*pl = &port->lp_lacp;
155da14cebeSEric Cheng 	aggr_grp_t		*grp = port->lp_grp;
156da14cebeSEric Cheng 	uint32_t		lacp_timer_bits;
157da14cebeSEric Cheng 	mac_perim_handle_t	mph;
158da14cebeSEric Cheng 	callb_cpr_t		cprinfo;
159da14cebeSEric Cheng 
160da14cebeSEric Cheng 	CALLB_CPR_INIT(&cprinfo, &pl->lacp_timer_lock, callb_generic_cpr,
161da14cebeSEric Cheng 	    "aggr_port_timer_thread");
162da14cebeSEric Cheng 
163da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
164da14cebeSEric Cheng 
165da14cebeSEric Cheng 	for (;;) {
166da14cebeSEric Cheng 
167da14cebeSEric Cheng 		if ((lacp_timer_bits = pl->lacp_timer_bits) == 0) {
168da14cebeSEric Cheng 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
169da14cebeSEric Cheng 			cv_wait(&pl->lacp_timer_cv, &pl->lacp_timer_lock);
170da14cebeSEric Cheng 			CALLB_CPR_SAFE_END(&cprinfo, &pl->lacp_timer_lock);
171da14cebeSEric Cheng 			continue;
172da14cebeSEric Cheng 		}
173da14cebeSEric Cheng 		pl->lacp_timer_bits = 0;
174da14cebeSEric Cheng 
175da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_THREAD_EXIT)
176da14cebeSEric Cheng 			break;
177da14cebeSEric Cheng 
178da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT)
179da14cebeSEric Cheng 			pl->periodic_timer.id = 0;
180da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT)
181da14cebeSEric Cheng 			pl->wait_while_timer.id = 0;
182da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT)
183da14cebeSEric Cheng 			pl->current_while_timer.id = 0;
184da14cebeSEric Cheng 
185da14cebeSEric Cheng 		mutex_exit(&pl->lacp_timer_lock);
186da14cebeSEric Cheng 
187da14cebeSEric Cheng 		mac_perim_enter_by_mh(grp->lg_mh, &mph);
188da14cebeSEric Cheng 		if (port->lp_closing) {
189da14cebeSEric Cheng 			mac_perim_exit(mph);
190da14cebeSEric Cheng 			mutex_enter(&pl->lacp_timer_lock);
191da14cebeSEric Cheng 			break;
192da14cebeSEric Cheng 		}
193da14cebeSEric Cheng 
194da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT)
195da14cebeSEric Cheng 			periodic_timer_pop_handler(port);
196da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT)
197da14cebeSEric Cheng 			wait_while_timer_pop_handler(port);
198da14cebeSEric Cheng 		if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT)
199da14cebeSEric Cheng 			current_while_timer_pop_handler(port);
200da14cebeSEric Cheng 		mac_perim_exit(mph);
201da14cebeSEric Cheng 
202da14cebeSEric Cheng 		mutex_enter(&pl->lacp_timer_lock);
203da14cebeSEric Cheng 		if (pl->lacp_timer_bits & LACP_THREAD_EXIT)
204da14cebeSEric Cheng 			break;
205da14cebeSEric Cheng 	}
206da14cebeSEric Cheng 
207da14cebeSEric Cheng 	pl->lacp_timer_bits = 0;
208da14cebeSEric Cheng 	pl->lacp_timer_thread = NULL;
209da14cebeSEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
210da14cebeSEric Cheng 
211da14cebeSEric Cheng 	/* CALLB_CPR_EXIT drops the lock */
212da14cebeSEric Cheng 	CALLB_CPR_EXIT(&cprinfo);
213da14cebeSEric Cheng 
214da14cebeSEric Cheng 	/*
215da14cebeSEric Cheng 	 * Release the reference of the grp so aggr_grp_delete() can call
216da14cebeSEric Cheng 	 * mac_unregister() safely.
217da14cebeSEric Cheng 	 */
218da14cebeSEric Cheng 	aggr_grp_port_rele(port);
219da14cebeSEric Cheng 	thread_exit();
220da14cebeSEric Cheng }
221da14cebeSEric Cheng 
222f12af565Snd /*
223f12af565Snd  * Set the port LACP state to SELECTED. Returns B_FALSE if the operation
224f12af565Snd  * could not be performed due to a memory allocation error, B_TRUE otherwise.
225f12af565Snd  */
226f12af565Snd static boolean_t
lacp_port_select(aggr_port_t * portp)227f12af565Snd lacp_port_select(aggr_port_t *portp)
228f12af565Snd {
229da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
230f12af565Snd 
231f12af565Snd 	if (!lacp_sel_ports_add(portp))
232f12af565Snd 		return (B_FALSE);
233f12af565Snd 	portp->lp_lacp.sm.selected = AGGR_SELECTED;
234f12af565Snd 	return (B_TRUE);
235f12af565Snd }
236f12af565Snd 
237f12af565Snd /*
238f12af565Snd  * Set the port LACP state to UNSELECTED.
239f12af565Snd  */
240f12af565Snd static void
lacp_port_unselect(aggr_port_t * portp)241f12af565Snd lacp_port_unselect(aggr_port_t *portp)
242f12af565Snd {
243da14cebeSEric Cheng 	aggr_grp_t	*grp = portp->lp_grp;
244da14cebeSEric Cheng 
245da14cebeSEric Cheng 	ASSERT((grp->lg_mh == NULL) || MAC_PERIM_HELD(grp->lg_mh));
246f12af565Snd 
247f12af565Snd 	lacp_sel_ports_del(portp);
248f12af565Snd 	portp->lp_lacp.sm.selected = AGGR_UNSELECTED;
249f12af565Snd }
250f12af565Snd 
2517c478bd9Sstevel@tonic-gate /*
2527c478bd9Sstevel@tonic-gate  * Initialize group specific LACP state and parameters.
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate void
aggr_lacp_init_grp(aggr_grp_t * aggrp)2557c478bd9Sstevel@tonic-gate aggr_lacp_init_grp(aggr_grp_t *aggrp)
2567c478bd9Sstevel@tonic-gate {
2577c478bd9Sstevel@tonic-gate 	aggrp->aggr.PeriodicTimer = AGGR_LACP_TIMER_SHORT;
2587c478bd9Sstevel@tonic-gate 	aggrp->aggr.ActorSystemPriority = (uint16_t)lacp_system_priority;
2597c478bd9Sstevel@tonic-gate 	aggrp->aggr.CollectorMaxDelay = 10;
2607c478bd9Sstevel@tonic-gate 	aggrp->lg_lacp_mode = AGGR_LACP_OFF;
2617c478bd9Sstevel@tonic-gate 	aggrp->aggr.ready = B_FALSE;
2627c478bd9Sstevel@tonic-gate }
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate  * Complete LACP info initialization at port creation time.
2667c478bd9Sstevel@tonic-gate  */
2677c478bd9Sstevel@tonic-gate void
aggr_lacp_init_port(aggr_port_t * portp)2687c478bd9Sstevel@tonic-gate aggr_lacp_init_port(aggr_port_t *portp)
2697c478bd9Sstevel@tonic-gate {
2707c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
2717c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
2727c478bd9Sstevel@tonic-gate 
273da14cebeSEric Cheng 	ASSERT(aggrp->lg_mh == NULL || MAC_PERIM_HELD(aggrp->lg_mh));
274da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	/* actor port # */
277392b1d6eSyz 	pl->ActorPortNumber = portp->lp_portid;
278d62bc4baSyz 	AGGR_LACP_DBG(("aggr_lacp_init_port(%d): "
279d62bc4baSyz 	    "ActorPortNumber = 0x%x\n", portp->lp_linkid,
280ba2e4443Sseb 	    pl->ActorPortNumber));
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	pl->ActorPortPriority = (uint16_t)lacp_port_priority;
2837c478bd9Sstevel@tonic-gate 	pl->ActorPortAggrId = 0;	/* aggregator id - not used */
2847c478bd9Sstevel@tonic-gate 	pl->NTT = B_FALSE;			/* need to transmit */
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortKey = aggrp->lg_key;
2877c478bd9Sstevel@tonic-gate 	pl->ActorOperPortKey = pl->ActorAdminPortKey;
288d62bc4baSyz 	AGGR_LACP_DBG(("aggr_lacp_init_port(%d) "
2897c478bd9Sstevel@tonic-gate 	    "ActorAdminPortKey = 0x%x, ActorAdminPortKey = 0x%x\n",
290d62bc4baSyz 	    portp->lp_linkid, pl->ActorAdminPortKey, pl->ActorOperPortKey));
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	/* Actor admin. port state */
2937c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.activity = B_FALSE;
2947c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.timeout = B_TRUE;
2957c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.aggregation = B_TRUE;
2967c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.sync = B_FALSE;
2977c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.collecting = B_FALSE;
2987c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.distributing = B_FALSE;
2997c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.defaulted = B_FALSE;
3007c478bd9Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.expired = B_FALSE;
3017c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState = pl->ActorAdminPortState;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * Partner Administrative Information
3057c478bd9Sstevel@tonic-gate 	 * (All initialized to zero except for the following)
3067c478bd9Sstevel@tonic-gate 	 * Fast Timeouts.
3077c478bd9Sstevel@tonic-gate 	 */
3087c478bd9Sstevel@tonic-gate 	pl->PartnerAdminPortState.bit.timeout =
3097c478bd9Sstevel@tonic-gate 	    pl->PartnerOperPortState.bit.timeout = B_TRUE;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	/*
3147c478bd9Sstevel@tonic-gate 	 * State machine information.
3157c478bd9Sstevel@tonic-gate 	 */
3167c478bd9Sstevel@tonic-gate 	pl->sm.lacp_on = B_FALSE;		/* LACP Off default */
3177c478bd9Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;		/* Prevents transmissions */
3187c478bd9Sstevel@tonic-gate 	pl->sm.lacp_enabled = B_FALSE;
3197c478bd9Sstevel@tonic-gate 	pl->sm.port_enabled = B_FALSE;		/* Link Down */
3207c478bd9Sstevel@tonic-gate 	pl->sm.actor_churn = B_FALSE;
3217c478bd9Sstevel@tonic-gate 	pl->sm.partner_churn = B_FALSE;
3227c478bd9Sstevel@tonic-gate 	pl->sm.ready_n = B_FALSE;
3237c478bd9Sstevel@tonic-gate 	pl->sm.port_moved = B_FALSE;
3247c478bd9Sstevel@tonic-gate 
325f12af565Snd 	lacp_port_unselect(portp);
326f12af565Snd 
3277c478bd9Sstevel@tonic-gate 	pl->sm.periodic_state = LACP_NO_PERIODIC;
3287c478bd9Sstevel@tonic-gate 	pl->sm.receive_state = LACP_INITIALIZE;
3297c478bd9Sstevel@tonic-gate 	pl->sm.mux_state = LACP_DETACHED;
3307c478bd9Sstevel@tonic-gate 	pl->sm.churn_state = LACP_NO_ACTOR_CHURN;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/*
3337c478bd9Sstevel@tonic-gate 	 * Timer information.
3347c478bd9Sstevel@tonic-gate 	 */
3357c478bd9Sstevel@tonic-gate 	pl->current_while_timer.id = 0;
3367c478bd9Sstevel@tonic-gate 	pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	pl->periodic_timer.id = 0;
3397c478bd9Sstevel@tonic-gate 	pl->periodic_timer.val = FAST_PERIODIC_TIME;
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	pl->wait_while_timer.id = 0;
3427c478bd9Sstevel@tonic-gate 	pl->wait_while_timer.val = AGGREGATE_WAIT_TIME;
343da14cebeSEric Cheng 
344da14cebeSEric Cheng 	pl->lacp_timer_bits = 0;
345da14cebeSEric Cheng 
346da14cebeSEric Cheng 	mutex_init(&pl->lacp_timer_lock, NULL, MUTEX_DRIVER, NULL);
347da14cebeSEric Cheng 	cv_init(&pl->lacp_timer_cv, NULL, CV_DRIVER, NULL);
348da14cebeSEric Cheng 
349da14cebeSEric Cheng 	pl->lacp_timer_thread = thread_create(NULL, 0, aggr_port_timer_thread,
350da14cebeSEric Cheng 	    portp, 0, &p0, TS_RUN, minclsyspri);
351da14cebeSEric Cheng 
352da14cebeSEric Cheng 	/*
353da14cebeSEric Cheng 	 * Hold a reference of the grp and the port and this reference will
354da14cebeSEric Cheng 	 * be release when the thread exits.
355da14cebeSEric Cheng 	 *
356da14cebeSEric Cheng 	 * The reference on the port is used for aggr_port_delete() to
357da14cebeSEric Cheng 	 * continue without waiting for the thread to exit; the reference
358da14cebeSEric Cheng 	 * on the grp is used for aggr_grp_delete() to wait for the thread
359da14cebeSEric Cheng 	 * to exit before calling mac_unregister().
360da14cebeSEric Cheng 	 */
361da14cebeSEric Cheng 	aggr_grp_port_hold(portp);
3627c478bd9Sstevel@tonic-gate }
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate  * Port initialization when we need to
3667c478bd9Sstevel@tonic-gate  * turn LACP on/off, etc. Not everything is
3677c478bd9Sstevel@tonic-gate  * reset like in the above routine.
3687c478bd9Sstevel@tonic-gate  *		Do NOT modify things like link status.
3697c478bd9Sstevel@tonic-gate  */
3707c478bd9Sstevel@tonic-gate static void
lacp_reset_port(aggr_port_t * portp)3717c478bd9Sstevel@tonic-gate lacp_reset_port(aggr_port_t *portp)
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
3747c478bd9Sstevel@tonic-gate 
375da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	pl->NTT = B_FALSE;			/* need to transmit */
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	/* reset operational port state */
3807c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.timeout =
381392b1d6eSyz 	    pl->ActorAdminPortState.bit.timeout;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.sync = B_FALSE;
3847c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.collecting = B_FALSE;
3857c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.distributing = B_FALSE;
3867c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_TRUE;
3877c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.expired = B_FALSE;
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortState.bit.timeout = B_TRUE;	/* fast t/o */
3907c478bd9Sstevel@tonic-gate 	pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	/*
3937c478bd9Sstevel@tonic-gate 	 * State machine information.
3947c478bd9Sstevel@tonic-gate 	 */
3957c478bd9Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;		/* Prevents transmissions */
3967c478bd9Sstevel@tonic-gate 	pl->sm.actor_churn = B_FALSE;
3977c478bd9Sstevel@tonic-gate 	pl->sm.partner_churn = B_FALSE;
3987c478bd9Sstevel@tonic-gate 	pl->sm.ready_n = B_FALSE;
399f12af565Snd 
400f12af565Snd 	lacp_port_unselect(portp);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	pl->sm.periodic_state = LACP_NO_PERIODIC;
4037c478bd9Sstevel@tonic-gate 	pl->sm.receive_state = LACP_INITIALIZE;
4047c478bd9Sstevel@tonic-gate 	pl->sm.mux_state = LACP_DETACHED;
4057c478bd9Sstevel@tonic-gate 	pl->sm.churn_state = LACP_NO_ACTOR_CHURN;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	/*
4087c478bd9Sstevel@tonic-gate 	 * Timer information.
4097c478bd9Sstevel@tonic-gate 	 */
4107c478bd9Sstevel@tonic-gate 	pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
4117c478bd9Sstevel@tonic-gate 	pl->periodic_timer.val = FAST_PERIODIC_TIME;
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate static void
aggr_lacp_mcast_on(aggr_port_t * port)4157c478bd9Sstevel@tonic-gate aggr_lacp_mcast_on(aggr_port_t *port)
4167c478bd9Sstevel@tonic-gate {
417da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
418da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
4217c478bd9Sstevel@tonic-gate 		return;
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	(void) aggr_port_multicst(port, B_TRUE,
4247c478bd9Sstevel@tonic-gate 	    (uchar_t *)&slow_multicast_addr);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate static void
aggr_lacp_mcast_off(aggr_port_t * port)4287c478bd9Sstevel@tonic-gate aggr_lacp_mcast_off(aggr_port_t *port)
4297c478bd9Sstevel@tonic-gate {
430da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
431da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
4347c478bd9Sstevel@tonic-gate 		return;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	(void) aggr_port_multicst(port, B_FALSE,
4377c478bd9Sstevel@tonic-gate 	    (uchar_t *)&slow_multicast_addr);
4387c478bd9Sstevel@tonic-gate }
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate static void
start_periodic_timer(aggr_port_t * portp)4417c478bd9Sstevel@tonic-gate start_periodic_timer(aggr_port_t *portp)
4427c478bd9Sstevel@tonic-gate {
443da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
444da14cebeSEric Cheng 
445da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
4467c478bd9Sstevel@tonic-gate 
447da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
448da14cebeSEric Cheng 	if (pl->periodic_timer.id == 0) {
449da14cebeSEric Cheng 		pl->periodic_timer.id = timeout(periodic_timer_pop, portp,
4507c478bd9Sstevel@tonic-gate 		    drv_usectohz(1000000 * portp->lp_lacp.periodic_timer.val));
4517c478bd9Sstevel@tonic-gate 	}
452da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
4537c478bd9Sstevel@tonic-gate }
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate static void
stop_periodic_timer(aggr_port_t * portp)4567c478bd9Sstevel@tonic-gate stop_periodic_timer(aggr_port_t *portp)
4577c478bd9Sstevel@tonic-gate {
458da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
459da14cebeSEric Cheng 	timeout_id_t id;
460da14cebeSEric Cheng 
461da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
4627c478bd9Sstevel@tonic-gate 
463da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
464da14cebeSEric Cheng 	if ((id = pl->periodic_timer.id) != 0) {
465da14cebeSEric Cheng 		pl->lacp_timer_bits &= ~LACP_PERIODIC_TIMEOUT;
466da14cebeSEric Cheng 		pl->periodic_timer.id = 0;
4677c478bd9Sstevel@tonic-gate 	}
468da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
469da14cebeSEric Cheng 
470da14cebeSEric Cheng 	if (id != 0)
471da14cebeSEric Cheng 		(void) untimeout(id);
4727c478bd9Sstevel@tonic-gate }
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * When the timer pops, we arrive here to
4767c478bd9Sstevel@tonic-gate  * clear out LACPDU count as well as transmit an
4777c478bd9Sstevel@tonic-gate  * LACPDU. We then set the periodic state and let
4787c478bd9Sstevel@tonic-gate  * the periodic state machine restart the timer.
4797c478bd9Sstevel@tonic-gate  */
480da14cebeSEric Cheng static void
periodic_timer_pop(void * data)481da14cebeSEric Cheng periodic_timer_pop(void *data)
482da14cebeSEric Cheng {
483da14cebeSEric Cheng 	aggr_port_t *portp = data;
484da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
485da14cebeSEric Cheng 
486da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
487da14cebeSEric Cheng 	pl->lacp_timer_bits |= LACP_PERIODIC_TIMEOUT;
488da14cebeSEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
489da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
490da14cebeSEric Cheng }
4917c478bd9Sstevel@tonic-gate 
492da14cebeSEric Cheng /*
493da14cebeSEric Cheng  * When the timer pops, we arrive here to
494da14cebeSEric Cheng  * clear out LACPDU count as well as transmit an
495da14cebeSEric Cheng  * LACPDU. We then set the periodic state and let
496da14cebeSEric Cheng  * the periodic state machine restart the timer.
497da14cebeSEric Cheng  */
4987c478bd9Sstevel@tonic-gate static void
periodic_timer_pop_handler(aggr_port_t * portp)499da14cebeSEric Cheng periodic_timer_pop_handler(aggr_port_t *portp)
5007c478bd9Sstevel@tonic-gate {
501da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	portp->lp_lacp_stats.LACPDUsTx = 0;
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/* current timestamp */
5067c478bd9Sstevel@tonic-gate 	portp->lp_lacp.time = gethrtime();
5077c478bd9Sstevel@tonic-gate 	portp->lp_lacp.NTT = B_TRUE;
5087c478bd9Sstevel@tonic-gate 	lacp_xmit_sm(portp);
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	/*
5117c478bd9Sstevel@tonic-gate 	 * Set Periodic State machine state based on the
5127c478bd9Sstevel@tonic-gate 	 * value of the Partner Operation Port State timeout
5137c478bd9Sstevel@tonic-gate 	 * bit.
5147c478bd9Sstevel@tonic-gate 	 */
5157c478bd9Sstevel@tonic-gate 	if (portp->lp_lacp.PartnerOperPortState.bit.timeout) {
5167c478bd9Sstevel@tonic-gate 		portp->lp_lacp.periodic_timer.val = FAST_PERIODIC_TIME;
5177c478bd9Sstevel@tonic-gate 		portp->lp_lacp.sm.periodic_state = LACP_FAST_PERIODIC;
5187c478bd9Sstevel@tonic-gate 	} else {
5197c478bd9Sstevel@tonic-gate 		portp->lp_lacp.periodic_timer.val = SLOW_PERIODIC_TIME;
5207c478bd9Sstevel@tonic-gate 		portp->lp_lacp.sm.periodic_state = LACP_SLOW_PERIODIC;
5217c478bd9Sstevel@tonic-gate 	}
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	lacp_periodic_sm(portp);
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate /*
5277c478bd9Sstevel@tonic-gate  * Invoked from:
5287c478bd9Sstevel@tonic-gate  *	- startup upon aggregation
5297c478bd9Sstevel@tonic-gate  *	- when the periodic timer pops
5307c478bd9Sstevel@tonic-gate  *	- when the periodic timer value is changed
5317c478bd9Sstevel@tonic-gate  *	- when the port is attached or detached
5327c478bd9Sstevel@tonic-gate  *	- when LACP mode is changed.
5337c478bd9Sstevel@tonic-gate  */
5347c478bd9Sstevel@tonic-gate static void
lacp_periodic_sm(aggr_port_t * portp)5357c478bd9Sstevel@tonic-gate lacp_periodic_sm(aggr_port_t *portp)
5367c478bd9Sstevel@tonic-gate {
5377c478bd9Sstevel@tonic-gate 	lacp_periodic_state_t oldstate = portp->lp_lacp.sm.periodic_state;
5387c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
5397c478bd9Sstevel@tonic-gate 
540da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
5437c478bd9Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
5447c478bd9Sstevel@tonic-gate 		/* Stop timer whether it is running or not */
5457c478bd9Sstevel@tonic-gate 		stop_periodic_timer(portp);
5467c478bd9Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_NO_PERIODIC;
5477c478bd9Sstevel@tonic-gate 		pl->NTT = B_FALSE;
548d62bc4baSyz 		AGGR_LACP_DBG(("lacp_periodic_sm(%d):NO LACP "
549d62bc4baSyz 		    "%s--->%s\n", portp->lp_linkid,
5507c478bd9Sstevel@tonic-gate 		    lacp_periodic_str[oldstate],
5517c478bd9Sstevel@tonic-gate 		    lacp_periodic_str[pl->sm.periodic_state]));
5527c478bd9Sstevel@tonic-gate 		return;
5537c478bd9Sstevel@tonic-gate 	}
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled ||
5567c478bd9Sstevel@tonic-gate 	    !pl->sm.port_enabled ||
5577c478bd9Sstevel@tonic-gate 	    !pl->ActorOperPortState.bit.activity &&
5587c478bd9Sstevel@tonic-gate 	    !pl->PartnerOperPortState.bit.activity) {
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 		/* Stop timer whether it is running or not */
5617c478bd9Sstevel@tonic-gate 		stop_periodic_timer(portp);
5627c478bd9Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_NO_PERIODIC;
5637c478bd9Sstevel@tonic-gate 		pl->NTT = B_FALSE;
564d62bc4baSyz 		AGGR_LACP_DBG(("lacp_periodic_sm(%d):STOP %s--->%s\n",
565d62bc4baSyz 		    portp->lp_linkid, lacp_periodic_str[oldstate],
5667c478bd9Sstevel@tonic-gate 		    lacp_periodic_str[pl->sm.periodic_state]));
5677c478bd9Sstevel@tonic-gate 		return;
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	/*
5717c478bd9Sstevel@tonic-gate 	 * Startup with FAST_PERIODIC_TIME if no previous LACPDU
5727c478bd9Sstevel@tonic-gate 	 * has been received. Then after we timeout, then it is
5737c478bd9Sstevel@tonic-gate 	 * possible to go to SLOW_PERIODIC_TIME.
5747c478bd9Sstevel@tonic-gate 	 */
5757c478bd9Sstevel@tonic-gate 	if (pl->sm.periodic_state == LACP_NO_PERIODIC) {
5767c478bd9Sstevel@tonic-gate 		pl->periodic_timer.val = FAST_PERIODIC_TIME;
5777c478bd9Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_FAST_PERIODIC;
5787c478bd9Sstevel@tonic-gate 	} else if ((pl->sm.periodic_state == LACP_SLOW_PERIODIC) &&
5797c478bd9Sstevel@tonic-gate 	    pl->PartnerOperPortState.bit.timeout) {
5807c478bd9Sstevel@tonic-gate 		/*
5817c478bd9Sstevel@tonic-gate 		 * If we receive a bit indicating we are going to
5827c478bd9Sstevel@tonic-gate 		 * fast periodic from slow periodic, stop the timer
5837c478bd9Sstevel@tonic-gate 		 * and let the periodic_timer_pop routine deal
5847c478bd9Sstevel@tonic-gate 		 * with reseting the periodic state and transmitting
5857c478bd9Sstevel@tonic-gate 		 * a LACPDU.
5867c478bd9Sstevel@tonic-gate 		 */
5877c478bd9Sstevel@tonic-gate 		stop_periodic_timer(portp);
588da14cebeSEric Cheng 		periodic_timer_pop_handler(portp);
5897c478bd9Sstevel@tonic-gate 	}
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	/* Rearm timer with value provided by partner */
5927c478bd9Sstevel@tonic-gate 	start_periodic_timer(portp);
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate /*
5967c478bd9Sstevel@tonic-gate  * This routine transmits an LACPDU if lacp_enabled
5977c478bd9Sstevel@tonic-gate  * is TRUE and if NTT is set.
5987c478bd9Sstevel@tonic-gate  */
5997c478bd9Sstevel@tonic-gate static void
lacp_xmit_sm(aggr_port_t * portp)6007c478bd9Sstevel@tonic-gate lacp_xmit_sm(aggr_port_t *portp)
6017c478bd9Sstevel@tonic-gate {
6027c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
6037c478bd9Sstevel@tonic-gate 	size_t	len;
6047c478bd9Sstevel@tonic-gate 	mblk_t  *mp;
6057c478bd9Sstevel@tonic-gate 	hrtime_t now, elapsed;
6067c478bd9Sstevel@tonic-gate 
607da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
610*666e8af9SRobert Mustacchi 	if (!pl->sm.lacp_on || !pl->NTT)
6117c478bd9Sstevel@tonic-gate 		return;
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	/*
6147c478bd9Sstevel@tonic-gate 	 * Do nothing if LACP has been turned off or if the
6157c478bd9Sstevel@tonic-gate 	 * periodic state machine is not enabled.
6167c478bd9Sstevel@tonic-gate 	 */
6177c478bd9Sstevel@tonic-gate 	if ((pl->sm.periodic_state == LACP_NO_PERIODIC) ||
6187c478bd9Sstevel@tonic-gate 	    !pl->sm.lacp_enabled || pl->sm.begin) {
6197c478bd9Sstevel@tonic-gate 		pl->NTT = B_FALSE;
6207c478bd9Sstevel@tonic-gate 		return;
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/*
6247c478bd9Sstevel@tonic-gate 	 * If we have sent 5 Slow packets in the last second, avoid
6257c478bd9Sstevel@tonic-gate 	 * sending any more here. No more than three LACPDUs may be transmitted
6267c478bd9Sstevel@tonic-gate 	 * in any Fast_Periodic_Time interval.
6277c478bd9Sstevel@tonic-gate 	 */
6287c478bd9Sstevel@tonic-gate 	if (portp->lp_lacp_stats.LACPDUsTx >= 3) {
6297c478bd9Sstevel@tonic-gate 		/*
6307c478bd9Sstevel@tonic-gate 		 * Grab the current time value and see if
6317c478bd9Sstevel@tonic-gate 		 * more than 1 second has passed. If so,
6327c478bd9Sstevel@tonic-gate 		 * reset the timestamp and clear the count.
6337c478bd9Sstevel@tonic-gate 		 */
6347c478bd9Sstevel@tonic-gate 		now = gethrtime();
6357c478bd9Sstevel@tonic-gate 		elapsed = now - pl->time;
6367c478bd9Sstevel@tonic-gate 		if (elapsed > NSECS_PER_SEC) {
6377c478bd9Sstevel@tonic-gate 			portp->lp_lacp_stats.LACPDUsTx = 0;
6387c478bd9Sstevel@tonic-gate 			pl->time = now;
6397c478bd9Sstevel@tonic-gate 		} else {
6407c478bd9Sstevel@tonic-gate 			return;
6417c478bd9Sstevel@tonic-gate 		}
6427c478bd9Sstevel@tonic-gate 	}
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	len = sizeof (lacp_t) + sizeof (struct ether_header);
6457c478bd9Sstevel@tonic-gate 	mp = allocb(len, BPRI_MED);
6467c478bd9Sstevel@tonic-gate 	if (mp == NULL)
6477c478bd9Sstevel@tonic-gate 		return;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + len;
6507c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, len);
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr);
6537c478bd9Sstevel@tonic-gate 	fill_lacp_pdu(portp,
6547c478bd9Sstevel@tonic-gate 	    (lacp_t *)(mp->b_rptr + sizeof (struct ether_header)));
6557c478bd9Sstevel@tonic-gate 
6560dc2366fSVenugopal Iyer 	/* Send the packet over the first TX ring */
6570dc2366fSVenugopal Iyer 	mp = mac_hwring_send_priv(portp->lp_mch, portp->lp_tx_rings[0], mp);
6580dc2366fSVenugopal Iyer 	if (mp != NULL)
6590dc2366fSVenugopal Iyer 		freemsg(mp);
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	pl->NTT = B_FALSE;
6627c478bd9Sstevel@tonic-gate 	portp->lp_lacp_stats.LACPDUsTx++;
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate /*
6667c478bd9Sstevel@tonic-gate  * Initialize the ethernet header of a LACP packet sent from the specified
6677c478bd9Sstevel@tonic-gate  * port.
6687c478bd9Sstevel@tonic-gate  */
6697c478bd9Sstevel@tonic-gate static void
fill_lacp_ether(aggr_port_t * port,struct ether_header * ether)6707c478bd9Sstevel@tonic-gate fill_lacp_ether(aggr_port_t *port, struct ether_header *ether)
6717c478bd9Sstevel@tonic-gate {
6727c478bd9Sstevel@tonic-gate 	bcopy(port->lp_addr, (uint8_t *)&(ether->ether_shost), ETHERADDRL);
6737c478bd9Sstevel@tonic-gate 	bcopy(&slow_multicast_addr, (uint8_t *)&(ether->ether_dhost),
6747c478bd9Sstevel@tonic-gate 	    ETHERADDRL);
6757c478bd9Sstevel@tonic-gate 	ether->ether_type = htons(ETHERTYPE_SLOW);
6767c478bd9Sstevel@tonic-gate }
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate static void
fill_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)6797c478bd9Sstevel@tonic-gate fill_lacp_pdu(aggr_port_t *portp, lacp_t *lacp)
6807c478bd9Sstevel@tonic-gate {
6817c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
6827c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
683da14cebeSEric Cheng 	mac_perim_handle_t pmph;
6847c478bd9Sstevel@tonic-gate 
685da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
686da14cebeSEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &pmph);
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	lacp->subtype = LACP_SUBTYPE;
6897c478bd9Sstevel@tonic-gate 	lacp->version = LACP_VERSION;
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	/*
6927c478bd9Sstevel@tonic-gate 	 * Actor Information
6937c478bd9Sstevel@tonic-gate 	 */
6947c478bd9Sstevel@tonic-gate 	lacp->actor_info.tlv_type = ACTOR_TLV;
6957c478bd9Sstevel@tonic-gate 	lacp->actor_info.information_len = sizeof (link_info_t);
6967c478bd9Sstevel@tonic-gate 	lacp->actor_info.system_priority =
6977c478bd9Sstevel@tonic-gate 	    htons(aggrp->aggr.ActorSystemPriority);
6987c478bd9Sstevel@tonic-gate 	bcopy(aggrp->lg_addr, (uchar_t *)&lacp->actor_info.system_id,
6997c478bd9Sstevel@tonic-gate 	    ETHERADDRL);
7007c478bd9Sstevel@tonic-gate 	lacp->actor_info.key = htons(pl->ActorOperPortKey);
7017c478bd9Sstevel@tonic-gate 	lacp->actor_info.port_priority = htons(pl->ActorPortPriority);
7027c478bd9Sstevel@tonic-gate 	lacp->actor_info.port = htons(pl->ActorPortNumber);
7037c478bd9Sstevel@tonic-gate 	lacp->actor_info.state.state = pl->ActorOperPortState.state;
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	/*
7067c478bd9Sstevel@tonic-gate 	 * Partner Information
7077c478bd9Sstevel@tonic-gate 	 */
7087c478bd9Sstevel@tonic-gate 	lacp->partner_info.tlv_type = PARTNER_TLV;
7097c478bd9Sstevel@tonic-gate 	lacp->partner_info.information_len = sizeof (link_info_t);
7107c478bd9Sstevel@tonic-gate 	lacp->partner_info.system_priority =
7117c478bd9Sstevel@tonic-gate 	    htons(pl->PartnerOperSysPriority);
7127c478bd9Sstevel@tonic-gate 	lacp->partner_info.system_id = pl->PartnerOperSystem;
7137c478bd9Sstevel@tonic-gate 	lacp->partner_info.key = htons(pl->PartnerOperKey);
7147c478bd9Sstevel@tonic-gate 	lacp->partner_info.port_priority =
7157c478bd9Sstevel@tonic-gate 	    htons(pl->PartnerOperPortPriority);
7167c478bd9Sstevel@tonic-gate 	lacp->partner_info.port = htons(pl->PartnerOperPortNum);
7177c478bd9Sstevel@tonic-gate 	lacp->partner_info.state.state = pl->PartnerOperPortState.state;
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 	/* Collector Information */
7207c478bd9Sstevel@tonic-gate 	lacp->tlv_collector = COLLECTOR_TLV;
7217c478bd9Sstevel@tonic-gate 	lacp->collector_len = 0x10;
7227c478bd9Sstevel@tonic-gate 	lacp->collector_max_delay = htons(aggrp->aggr.CollectorMaxDelay);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	/* Termination Information */
7257c478bd9Sstevel@tonic-gate 	lacp->tlv_terminator = TERMINATOR_TLV;
7267c478bd9Sstevel@tonic-gate 	lacp->terminator_len = 0x0;
7277c478bd9Sstevel@tonic-gate 
728da14cebeSEric Cheng 	mac_perim_exit(pmph);
7297c478bd9Sstevel@tonic-gate }
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate /*
7327c478bd9Sstevel@tonic-gate  * lacp_mux_sm - LACP mux state machine
7337c478bd9Sstevel@tonic-gate  *		This state machine is invoked from:
7347c478bd9Sstevel@tonic-gate  *			- startup upon aggregation
7357c478bd9Sstevel@tonic-gate  *			- from the Selection logic
7367c478bd9Sstevel@tonic-gate  *			- when the wait_while_timer pops
7377c478bd9Sstevel@tonic-gate  *			- when the aggregation MAC address is changed
7387c478bd9Sstevel@tonic-gate  *			- when receiving DL_NOTE_LINK_UP/DOWN
7397c478bd9Sstevel@tonic-gate  *			- when receiving DL_NOTE_AGGR_AVAIL/UNAVAIL
7407c478bd9Sstevel@tonic-gate  *			- when LACP mode is changed.
7417c478bd9Sstevel@tonic-gate  *			- when a DL_NOTE_SPEED is received
7427c478bd9Sstevel@tonic-gate  */
7437c478bd9Sstevel@tonic-gate static void
lacp_mux_sm(aggr_port_t * portp)7447c478bd9Sstevel@tonic-gate lacp_mux_sm(aggr_port_t *portp)
7457c478bd9Sstevel@tonic-gate {
7467c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
7477c478bd9Sstevel@tonic-gate 	boolean_t NTT_updated = B_FALSE;
7487c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
7497c478bd9Sstevel@tonic-gate 	lacp_mux_state_t oldstate = pl->sm.mux_state;
7507c478bd9Sstevel@tonic-gate 
751da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
7547c478bd9Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
7557c478bd9Sstevel@tonic-gate 		pl->sm.mux_state = LACP_DETACHED;
7567c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync = B_FALSE;
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
7597c478bd9Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
760d62bc4baSyz 			AGGR_LACP_DBG(("trunk link: (%d): "
7617c478bd9Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
762d62bc4baSyz 			    portp->lp_linkid));
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting =
7667c478bd9Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing = B_FALSE;
7677c478bd9Sstevel@tonic-gate 		return;
7687c478bd9Sstevel@tonic-gate 	}
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled)
7717c478bd9Sstevel@tonic-gate 		pl->sm.mux_state = LACP_DETACHED;
7727c478bd9Sstevel@tonic-gate 
773f12af565Snd again:
7747c478bd9Sstevel@tonic-gate 	/* determine next state, or return if state unchanged */
7757c478bd9Sstevel@tonic-gate 	switch (pl->sm.mux_state) {
7767c478bd9Sstevel@tonic-gate 	case LACP_DETACHED:
7777c478bd9Sstevel@tonic-gate 		if (pl->sm.begin) {
7787c478bd9Sstevel@tonic-gate 			break;
7797c478bd9Sstevel@tonic-gate 		}
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) ||
7827c478bd9Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY)) {
7837c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_WAITING;
7847c478bd9Sstevel@tonic-gate 			break;
7857c478bd9Sstevel@tonic-gate 		}
7867c478bd9Sstevel@tonic-gate 		return;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	case LACP_WAITING:
7897c478bd9Sstevel@tonic-gate 		if (pl->sm.selected == AGGR_UNSELECTED) {
7907c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_DETACHED;
7917c478bd9Sstevel@tonic-gate 			break;
7927c478bd9Sstevel@tonic-gate 		}
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) && aggrp->aggr.ready) {
7957c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_ATTACHED;
7967c478bd9Sstevel@tonic-gate 			break;
7977c478bd9Sstevel@tonic-gate 		}
7987c478bd9Sstevel@tonic-gate 		return;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	case LACP_ATTACHED:
8017c478bd9Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_UNSELECTED) ||
8027c478bd9Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY)) {
8037c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_DETACHED;
8047c478bd9Sstevel@tonic-gate 			break;
8057c478bd9Sstevel@tonic-gate 		}
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) &&
8087c478bd9Sstevel@tonic-gate 		    pl->PartnerOperPortState.bit.sync) {
8097c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_COLLECTING_DISTRIBUTING;
8107c478bd9Sstevel@tonic-gate 			break;
8117c478bd9Sstevel@tonic-gate 		}
8127c478bd9Sstevel@tonic-gate 		return;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	case LACP_COLLECTING_DISTRIBUTING:
8157c478bd9Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_UNSELECTED) ||
8167c478bd9Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY) ||
8177c478bd9Sstevel@tonic-gate 		    !pl->PartnerOperPortState.bit.sync) {
8187c478bd9Sstevel@tonic-gate 			pl->sm.mux_state = LACP_ATTACHED;
8197c478bd9Sstevel@tonic-gate 			break;
8207c478bd9Sstevel@tonic-gate 		}
8217c478bd9Sstevel@tonic-gate 		return;
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate 
824d62bc4baSyz 	AGGR_LACP_DBG(("lacp_mux_sm(%d):%s--->%s\n",
825d62bc4baSyz 	    portp->lp_linkid, lacp_mux_str[oldstate],
8267c478bd9Sstevel@tonic-gate 	    lacp_mux_str[pl->sm.mux_state]));
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	/* perform actions on entering a new state */
8297c478bd9Sstevel@tonic-gate 	switch (pl->sm.mux_state) {
8307c478bd9Sstevel@tonic-gate 	case LACP_DETACHED:
8317c478bd9Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
8327c478bd9Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
833d62bc4baSyz 			AGGR_LACP_DBG(("trunk link: (%d): "
8347c478bd9Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
835d62bc4baSyz 			    portp->lp_linkid));
8367c478bd9Sstevel@tonic-gate 		}
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync =
8397c478bd9Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.collecting = B_FALSE;
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 		/* Turn OFF Collector_Distributor */
8427c478bd9Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_FALSE);
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_FALSE;
8457c478bd9Sstevel@tonic-gate 		NTT_updated = B_TRUE;
8467c478bd9Sstevel@tonic-gate 		break;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	case LACP_WAITING:
8497c478bd9Sstevel@tonic-gate 		start_wait_while_timer(portp);
8507c478bd9Sstevel@tonic-gate 		break;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	case LACP_ATTACHED:
8537c478bd9Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
8547c478bd9Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
855d62bc4baSyz 			AGGR_LACP_DBG(("trunk link: (%d): "
8567c478bd9Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
857d62bc4baSyz 			    portp->lp_linkid));
8587c478bd9Sstevel@tonic-gate 		}
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync = B_TRUE;
8617c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting = B_FALSE;
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 		/* Turn OFF Collector_Distributor */
8647c478bd9Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_FALSE);
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_FALSE;
8677c478bd9Sstevel@tonic-gate 		NTT_updated = B_TRUE;
868f12af565Snd 		if (pl->PartnerOperPortState.bit.sync) {
869f12af565Snd 			/*
870f12af565Snd 			 * We had already received an updated sync from
871f12af565Snd 			 * the partner. Attempt to transition to
872f12af565Snd 			 * collecting/distributing now.
873f12af565Snd 			 */
874f12af565Snd 			goto again;
875f12af565Snd 		}
8767c478bd9Sstevel@tonic-gate 		break;
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	case LACP_COLLECTING_DISTRIBUTING:
8797c478bd9Sstevel@tonic-gate 		if (!pl->ActorOperPortState.bit.collecting &&
8807c478bd9Sstevel@tonic-gate 		    !pl->ActorOperPortState.bit.distributing) {
881d62bc4baSyz 			AGGR_LACP_DBG(("trunk link: (%d): "
8827c478bd9Sstevel@tonic-gate 			    "Collector_Distributor Enabled.\n",
883d62bc4baSyz 			    portp->lp_linkid));
8847c478bd9Sstevel@tonic-gate 		}
8857c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_TRUE;
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 		/* Turn Collector_Distributor back ON */
8887c478bd9Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_TRUE);
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting = B_TRUE;
8917c478bd9Sstevel@tonic-gate 		NTT_updated = B_TRUE;
8927c478bd9Sstevel@tonic-gate 		break;
8937c478bd9Sstevel@tonic-gate 	}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	/*
8967c478bd9Sstevel@tonic-gate 	 * If we updated the state of the NTT variable, then
8977c478bd9Sstevel@tonic-gate 	 * initiate a LACPDU transmission.
8987c478bd9Sstevel@tonic-gate 	 */
8997c478bd9Sstevel@tonic-gate 	if (NTT_updated) {
9007c478bd9Sstevel@tonic-gate 		pl->NTT = B_TRUE;
9017c478bd9Sstevel@tonic-gate 		lacp_xmit_sm(portp);
9027c478bd9Sstevel@tonic-gate 	}
9037c478bd9Sstevel@tonic-gate } /* lacp_mux_sm */
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 
906da14cebeSEric Cheng static int
receive_marker_pdu(aggr_port_t * portp,mblk_t * mp)9077c478bd9Sstevel@tonic-gate receive_marker_pdu(aggr_port_t *portp, mblk_t *mp)
9087c478bd9Sstevel@tonic-gate {
90998b1442aSmeem 	marker_pdu_t		*markerp = (marker_pdu_t *)mp->b_rptr;
9107c478bd9Sstevel@tonic-gate 
911da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
9127c478bd9Sstevel@tonic-gate 
913d62bc4baSyz 	AGGR_LACP_DBG(("trunk link: (%d): MARKER PDU received:\n",
914d62bc4baSyz 	    portp->lp_linkid));
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
9177c478bd9Sstevel@tonic-gate 	if (!portp->lp_lacp.sm.lacp_on)
918da14cebeSEric Cheng 		return (-1);
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (marker_pdu_t))
921da14cebeSEric Cheng 		return (-1);
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	if (markerp->version != MARKER_VERSION) {
924d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
9257c478bd9Sstevel@tonic-gate 		    "version = %d does not match s/w version %d\n",
926d62bc4baSyz 		    portp->lp_linkid, markerp->version, MARKER_VERSION));
927da14cebeSEric Cheng 		return (-1);
9287c478bd9Sstevel@tonic-gate 	}
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	if (markerp->tlv_marker == MARKER_RESPONSE_TLV) {
9317c478bd9Sstevel@tonic-gate 		/* We do not yet send out MARKER info PDUs */
932d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): MARKER RESPONSE PDU: "
9337c478bd9Sstevel@tonic-gate 		    " MARKER TLV = %d - We don't send out info type!\n",
934d62bc4baSyz 		    portp->lp_linkid, markerp->tlv_marker));
935da14cebeSEric Cheng 		return (-1);
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	if (markerp->tlv_marker != MARKER_INFO_TLV) {
939d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
940d62bc4baSyz 		    " MARKER TLV = %d \n", portp->lp_linkid,
9417c478bd9Sstevel@tonic-gate 		    markerp->tlv_marker));
942da14cebeSEric Cheng 		return (-1);
9437c478bd9Sstevel@tonic-gate 	}
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	if (markerp->marker_len != MARKER_INFO_RESPONSE_LENGTH) {
946d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
947d62bc4baSyz 		    " MARKER length = %d \n", portp->lp_linkid,
9487c478bd9Sstevel@tonic-gate 		    markerp->marker_len));
949da14cebeSEric Cheng 		return (-1);
9507c478bd9Sstevel@tonic-gate 	}
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	if (markerp->requestor_port != portp->lp_lacp.PartnerOperPortNum) {
953d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: "
9547c478bd9Sstevel@tonic-gate 		    " MARKER Port %d not equal to Partner port %d\n",
955d62bc4baSyz 		    portp->lp_linkid, markerp->requestor_port,
9567c478bd9Sstevel@tonic-gate 		    portp->lp_lacp.PartnerOperPortNum));
957da14cebeSEric Cheng 		return (-1);
9587c478bd9Sstevel@tonic-gate 	}
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	if (ether_cmp(&markerp->system_id,
9617c478bd9Sstevel@tonic-gate 	    &portp->lp_lacp.PartnerOperSystem) != 0) {
962d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: "
9637c478bd9Sstevel@tonic-gate 		    " MARKER MAC not equal to Partner MAC\n",
964d62bc4baSyz 		    portp->lp_linkid));
965da14cebeSEric Cheng 		return (-1);
9667c478bd9Sstevel@tonic-gate 	}
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	/*
9697c478bd9Sstevel@tonic-gate 	 * Turn into Marker Response PDU
9707c478bd9Sstevel@tonic-gate 	 * and return mblk to sending system
9717c478bd9Sstevel@tonic-gate 	 */
9727c478bd9Sstevel@tonic-gate 	markerp->tlv_marker = MARKER_RESPONSE_TLV;
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	/* reuse the space that was used by received ethernet header */
9757c478bd9Sstevel@tonic-gate 	ASSERT(MBLKHEAD(mp) >= sizeof (struct ether_header));
9767c478bd9Sstevel@tonic-gate 	mp->b_rptr -= sizeof (struct ether_header);
9777c478bd9Sstevel@tonic-gate 	fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr);
978da14cebeSEric Cheng 	return (0);
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate /*
9827c478bd9Sstevel@tonic-gate  * Update the LACP mode (off, active, or passive) of the specified group.
9837c478bd9Sstevel@tonic-gate  */
9847c478bd9Sstevel@tonic-gate void
aggr_lacp_update_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode)9857c478bd9Sstevel@tonic-gate aggr_lacp_update_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode)
9867c478bd9Sstevel@tonic-gate {
9877c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t old_mode = grp->lg_lacp_mode;
9887c478bd9Sstevel@tonic-gate 	aggr_port_t *port;
9897c478bd9Sstevel@tonic-gate 
990da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
991da14cebeSEric Cheng 	ASSERT(!grp->lg_closing);
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 	if (mode == old_mode)
9947c478bd9Sstevel@tonic-gate 		return;
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 	grp->lg_lacp_mode = mode;
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
9997c478bd9Sstevel@tonic-gate 		port->lp_lacp.ActorAdminPortState.bit.activity =
10007c478bd9Sstevel@tonic-gate 		    port->lp_lacp.ActorOperPortState.bit.activity =
10017c478bd9Sstevel@tonic-gate 		    (mode == AGGR_LACP_ACTIVE);
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 		if (old_mode == AGGR_LACP_OFF) {
10047c478bd9Sstevel@tonic-gate 			/* OFF -> {PASSIVE,ACTIVE} */
10057c478bd9Sstevel@tonic-gate 			/* turn OFF Collector_Distributor */
10067c478bd9Sstevel@tonic-gate 			aggr_set_coll_dist(port, B_FALSE);
10077c478bd9Sstevel@tonic-gate 			lacp_on(port);
10087c478bd9Sstevel@tonic-gate 		} else if (mode == AGGR_LACP_OFF) {
10097c478bd9Sstevel@tonic-gate 			/* {PASSIVE,ACTIVE} -> OFF */
10107c478bd9Sstevel@tonic-gate 			lacp_off(port);
1011da14cebeSEric Cheng 			/* Turn ON Collector_Distributor */
1012da14cebeSEric Cheng 			aggr_set_coll_dist(port, B_TRUE);
10137c478bd9Sstevel@tonic-gate 		} else {
10147c478bd9Sstevel@tonic-gate 			/* PASSIVE->ACTIVE or ACTIVE->PASSIVE */
10157c478bd9Sstevel@tonic-gate 			port->lp_lacp.sm.begin = B_TRUE;
10167c478bd9Sstevel@tonic-gate 			lacp_mux_sm(port);
10177c478bd9Sstevel@tonic-gate 			lacp_periodic_sm(port);
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 			/* kick off state machines */
10207c478bd9Sstevel@tonic-gate 			lacp_receive_sm(port, NULL);
10217c478bd9Sstevel@tonic-gate 			lacp_mux_sm(port);
10227c478bd9Sstevel@tonic-gate 		}
10237c478bd9Sstevel@tonic-gate 	}
10247c478bd9Sstevel@tonic-gate }
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate /*
10287c478bd9Sstevel@tonic-gate  * Update the LACP timer (short or long) of the specified group.
10297c478bd9Sstevel@tonic-gate  */
10307c478bd9Sstevel@tonic-gate void
aggr_lacp_update_timer(aggr_grp_t * grp,aggr_lacp_timer_t timer)10317c478bd9Sstevel@tonic-gate aggr_lacp_update_timer(aggr_grp_t *grp, aggr_lacp_timer_t timer)
10327c478bd9Sstevel@tonic-gate {
10337c478bd9Sstevel@tonic-gate 	aggr_port_t *port;
10347c478bd9Sstevel@tonic-gate 
1035da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	if (timer == grp->aggr.PeriodicTimer)
10387c478bd9Sstevel@tonic-gate 		return;
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	grp->aggr.PeriodicTimer = timer;
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
10437c478bd9Sstevel@tonic-gate 		port->lp_lacp.ActorAdminPortState.bit.timeout =
10447c478bd9Sstevel@tonic-gate 		    port->lp_lacp.ActorOperPortState.bit.timeout =
10457c478bd9Sstevel@tonic-gate 		    (timer == AGGR_LACP_TIMER_SHORT);
10467c478bd9Sstevel@tonic-gate 	}
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate 
1049da14cebeSEric Cheng void
aggr_port_lacp_set_mode(aggr_grp_t * grp,aggr_port_t * port)1050da14cebeSEric Cheng aggr_port_lacp_set_mode(aggr_grp_t *grp, aggr_port_t *port)
1051da14cebeSEric Cheng {
1052da14cebeSEric Cheng 	aggr_lacp_mode_t	mode;
1053da14cebeSEric Cheng 	aggr_lacp_timer_t	timer;
1054da14cebeSEric Cheng 
1055da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
1056da14cebeSEric Cheng 
1057da14cebeSEric Cheng 	mode = grp->lg_lacp_mode;
1058da14cebeSEric Cheng 	timer = grp->aggr.PeriodicTimer;
1059da14cebeSEric Cheng 
1060da14cebeSEric Cheng 	port->lp_lacp.ActorAdminPortState.bit.activity =
1061da14cebeSEric Cheng 	    port->lp_lacp.ActorOperPortState.bit.activity =
1062da14cebeSEric Cheng 	    (mode == AGGR_LACP_ACTIVE);
1063da14cebeSEric Cheng 
1064da14cebeSEric Cheng 	port->lp_lacp.ActorAdminPortState.bit.timeout =
1065da14cebeSEric Cheng 	    port->lp_lacp.ActorOperPortState.bit.timeout =
1066da14cebeSEric Cheng 	    (timer == AGGR_LACP_TIMER_SHORT);
1067da14cebeSEric Cheng 
1068da14cebeSEric Cheng 	if (mode == AGGR_LACP_OFF) {
1069da14cebeSEric Cheng 		/* Turn ON Collector_Distributor */
1070da14cebeSEric Cheng 		aggr_set_coll_dist(port, B_TRUE);
1071da14cebeSEric Cheng 	} else { /* LACP_ACTIVE/PASSIVE */
1072da14cebeSEric Cheng 		lacp_on(port);
1073da14cebeSEric Cheng 	}
1074da14cebeSEric Cheng }
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate /*
10777c478bd9Sstevel@tonic-gate  * Sets the initial LACP mode (off, active, passive) and LACP timer
10787c478bd9Sstevel@tonic-gate  * (short, long) of the specified group.
10797c478bd9Sstevel@tonic-gate  */
10807c478bd9Sstevel@tonic-gate void
aggr_lacp_set_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode,aggr_lacp_timer_t timer)10817c478bd9Sstevel@tonic-gate aggr_lacp_set_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode,
10827c478bd9Sstevel@tonic-gate     aggr_lacp_timer_t timer)
10837c478bd9Sstevel@tonic-gate {
10847c478bd9Sstevel@tonic-gate 	aggr_port_t *port;
10857c478bd9Sstevel@tonic-gate 
1086da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	grp->lg_lacp_mode = mode;
10897c478bd9Sstevel@tonic-gate 	grp->aggr.PeriodicTimer = timer;
10907c478bd9Sstevel@tonic-gate 
1091da14cebeSEric Cheng 	for (port = grp->lg_ports; port != NULL; port = port->lp_next)
1092da14cebeSEric Cheng 		aggr_port_lacp_set_mode(grp, port);
10937c478bd9Sstevel@tonic-gate }
10947c478bd9Sstevel@tonic-gate 
1095f12af565Snd /*
1096f12af565Snd  * Verify that the Partner MAC and Key recorded by the specified
1097f12af565Snd  * port are not found in other ports that are not part of our
1098f12af565Snd  * aggregation. Returns B_TRUE if such a port is found, B_FALSE
1099f12af565Snd  * otherwise.
1100f12af565Snd  */
1101f12af565Snd static boolean_t
lacp_misconfig_check(aggr_port_t * portp)1102f12af565Snd lacp_misconfig_check(aggr_port_t *portp)
11037c478bd9Sstevel@tonic-gate {
1104f12af565Snd 	aggr_grp_t *grp = portp->lp_grp;
1105f12af565Snd 	lacp_sel_ports_t *cport;
11067c478bd9Sstevel@tonic-gate 
1107f12af565Snd 	mutex_enter(&lacp_sel_lock);
11087c478bd9Sstevel@tonic-gate 
1109f12af565Snd 	for (cport = sel_ports; cport != NULL; cport = cport->sp_next) {
11107c478bd9Sstevel@tonic-gate 
1111f12af565Snd 		/* skip entries of the group of the port being checked */
1112d62bc4baSyz 		if (cport->sp_grp_linkid == grp->lg_linkid)
1113f12af565Snd 			continue;
11147c478bd9Sstevel@tonic-gate 
1115f12af565Snd 		if ((ether_cmp(&cport->sp_partner_system,
11167c478bd9Sstevel@tonic-gate 		    &grp->aggr.PartnerSystem) == 0) &&
1117f12af565Snd 		    (cport->sp_partner_key == grp->aggr.PartnerOperAggrKey)) {
1118f12af565Snd 			char mac_str[ETHERADDRL*3];
1119f12af565Snd 			struct ether_addr *mac = &cport->sp_partner_system;
1120f12af565Snd 
11217c478bd9Sstevel@tonic-gate 			/*
11227c478bd9Sstevel@tonic-gate 			 * The Partner port information is already in use
11237c478bd9Sstevel@tonic-gate 			 * by ports in another aggregation so disable this
11247c478bd9Sstevel@tonic-gate 			 * port.
11257c478bd9Sstevel@tonic-gate 			 */
1126f12af565Snd 
1127f12af565Snd 			(void) snprintf(mac_str, sizeof (mac_str),
1128f12af565Snd 			    "%x:%x:%x:%x:%x:%x",
1129f12af565Snd 			    mac->ether_addr_octet[0], mac->ether_addr_octet[1],
1130f12af565Snd 			    mac->ether_addr_octet[2], mac->ether_addr_octet[3],
1131f12af565Snd 			    mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
1132f12af565Snd 
1133f12af565Snd 			portp->lp_lacp.sm.selected = AGGR_UNSELECTED;
1134d62bc4baSyz 
1135d62bc4baSyz 			cmn_err(CE_NOTE, "aggr %d port %d: Port Partner "
1136d62bc4baSyz 			    "MAC %s and key %d in use on aggregation %d "
1137d62bc4baSyz 			    "port %d\n", grp->lg_linkid, portp->lp_linkid,
1138d62bc4baSyz 			    mac_str, portp->lp_lacp.PartnerOperKey,
1139d62bc4baSyz 			    cport->sp_grp_linkid, cport->sp_linkid);
11407c478bd9Sstevel@tonic-gate 			break;
11417c478bd9Sstevel@tonic-gate 		}
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 
1144f12af565Snd 	mutex_exit(&lacp_sel_lock);
1145f12af565Snd 	return (cport != NULL);
11467c478bd9Sstevel@tonic-gate }
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate /*
1149f12af565Snd  * Remove the specified port from the list of selected ports.
11507c478bd9Sstevel@tonic-gate  */
1151f12af565Snd static void
lacp_sel_ports_del(aggr_port_t * portp)1152f12af565Snd lacp_sel_ports_del(aggr_port_t *portp)
11537c478bd9Sstevel@tonic-gate {
1154f12af565Snd 	lacp_sel_ports_t *cport, **prev = NULL;
11557c478bd9Sstevel@tonic-gate 
1156f12af565Snd 	mutex_enter(&lacp_sel_lock);
1157f12af565Snd 
1158f12af565Snd 	prev = &sel_ports;
1159f12af565Snd 	for (cport = sel_ports; cport != NULL; prev = &cport->sp_next,
1160f12af565Snd 	    cport = cport->sp_next) {
1161d62bc4baSyz 		if (portp->lp_linkid == cport->sp_linkid)
1162f12af565Snd 			break;
1163f12af565Snd 	}
11647c478bd9Sstevel@tonic-gate 
1165f12af565Snd 	if (cport == NULL) {
1166f12af565Snd 		mutex_exit(&lacp_sel_lock);
1167f12af565Snd 		return;
1168f12af565Snd 	}
11697c478bd9Sstevel@tonic-gate 
1170f12af565Snd 	*prev = cport->sp_next;
1171f12af565Snd 	kmem_free(cport, sizeof (*cport));
11727c478bd9Sstevel@tonic-gate 
1173f12af565Snd 	mutex_exit(&lacp_sel_lock);
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate 
1176f12af565Snd /*
1177f12af565Snd  * Add the specified port to the list of selected ports. Returns B_FALSE
1178f12af565Snd  * if the operation could not be performed due to an memory allocation
1179f12af565Snd  * error.
1180f12af565Snd  */
1181f12af565Snd static boolean_t
lacp_sel_ports_add(aggr_port_t * portp)1182f12af565Snd lacp_sel_ports_add(aggr_port_t *portp)
1183f12af565Snd {
1184f12af565Snd 	lacp_sel_ports_t *new_port;
1185f12af565Snd 	lacp_sel_ports_t *cport, **last;
1186f12af565Snd 
1187f12af565Snd 	mutex_enter(&lacp_sel_lock);
1188f12af565Snd 
1189f12af565Snd 	/* check if port is already in the list */
1190f12af565Snd 	last = &sel_ports;
1191f12af565Snd 	for (cport = sel_ports; cport != NULL;
1192f12af565Snd 	    last = &cport->sp_next, cport = cport->sp_next) {
1193d62bc4baSyz 		if (portp->lp_linkid == cport->sp_linkid) {
1194f12af565Snd 			ASSERT(cport->sp_partner_key ==
1195f12af565Snd 			    portp->lp_lacp.PartnerOperKey);
1196f12af565Snd 			ASSERT(ether_cmp(&cport->sp_partner_system,
1197f12af565Snd 			    &portp->lp_lacp.PartnerOperSystem) == 0);
1198f12af565Snd 
1199f12af565Snd 			mutex_exit(&lacp_sel_lock);
1200f12af565Snd 			return (B_TRUE);
1201f12af565Snd 		}
1202f12af565Snd 	}
1203f12af565Snd 
1204f12af565Snd 	/* create and initialize new entry */
1205f12af565Snd 	new_port = kmem_zalloc(sizeof (lacp_sel_ports_t), KM_NOSLEEP);
1206f12af565Snd 	if (new_port == NULL) {
1207f12af565Snd 		mutex_exit(&lacp_sel_lock);
1208f12af565Snd 		return (B_FALSE);
1209f12af565Snd 	}
1210f12af565Snd 
1211d62bc4baSyz 	new_port->sp_grp_linkid = portp->lp_grp->lg_linkid;
1212f12af565Snd 	bcopy(&portp->lp_lacp.PartnerOperSystem,
1213f12af565Snd 	    &new_port->sp_partner_system, sizeof (new_port->sp_partner_system));
1214f12af565Snd 	new_port->sp_partner_key = portp->lp_lacp.PartnerOperKey;
1215d62bc4baSyz 	new_port->sp_linkid = portp->lp_linkid;
1216f12af565Snd 
1217f12af565Snd 	*last = new_port;
1218f12af565Snd 
1219f12af565Snd 	mutex_exit(&lacp_sel_lock);
1220f12af565Snd 	return (B_TRUE);
1221f12af565Snd }
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate /*
12247c478bd9Sstevel@tonic-gate  * lacp_selection_logic - LACP selection logic
12257c478bd9Sstevel@tonic-gate  *		Sets the selected variable on a per port basis
12267c478bd9Sstevel@tonic-gate  *		and sets Ready when all waiting ports are ready
12277c478bd9Sstevel@tonic-gate  *		to go online.
12287c478bd9Sstevel@tonic-gate  *
12297c478bd9Sstevel@tonic-gate  * parameters:
12307c478bd9Sstevel@tonic-gate  *      - portp - instance this applies to.
12317c478bd9Sstevel@tonic-gate  *
12327c478bd9Sstevel@tonic-gate  * invoked:
12337c478bd9Sstevel@tonic-gate  *    - when initialization is needed
12347c478bd9Sstevel@tonic-gate  *    - when UNSELECTED is set from the lacp_receive_sm() in LACP_CURRENT state
12357c478bd9Sstevel@tonic-gate  *    - When the lacp_receive_sm goes to the LACP_DEFAULTED state
12367c478bd9Sstevel@tonic-gate  *    - every time the wait_while_timer pops
12377c478bd9Sstevel@tonic-gate  *    - everytime we turn LACP on/off
12387c478bd9Sstevel@tonic-gate  */
12397c478bd9Sstevel@tonic-gate static void
lacp_selection_logic(aggr_port_t * portp)12407c478bd9Sstevel@tonic-gate lacp_selection_logic(aggr_port_t *portp)
12417c478bd9Sstevel@tonic-gate {
12427c478bd9Sstevel@tonic-gate 	aggr_port_t *tpp;
12437c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
12447c478bd9Sstevel@tonic-gate 	int ports_waiting;
12457c478bd9Sstevel@tonic-gate 	boolean_t reset_mac = B_FALSE;
12467c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
12477c478bd9Sstevel@tonic-gate 
1248da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
12517c478bd9Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
1252f12af565Snd 		lacp_port_unselect(portp);
12537c478bd9Sstevel@tonic-gate 		aggrp->aggr.ready = B_FALSE;
12547c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
12557c478bd9Sstevel@tonic-gate 		return;
12567c478bd9Sstevel@tonic-gate 	}
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled ||
12597c478bd9Sstevel@tonic-gate 	    (portp->lp_state != AGGR_PORT_STATE_ATTACHED)) {
12607c478bd9Sstevel@tonic-gate 
1261d62bc4baSyz 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
12627c478bd9Sstevel@tonic-gate 		    "selected %d-->%d (begin=%d, lacp_enabled = %d, "
1263d62bc4baSyz 		    "lp_state=%d)\n", portp->lp_linkid, pl->sm.selected,
1264ba2e4443Sseb 		    AGGR_UNSELECTED, pl->sm.begin, pl->sm.lacp_enabled,
12657c478bd9Sstevel@tonic-gate 		    portp->lp_state));
12667c478bd9Sstevel@tonic-gate 
1267f12af565Snd 		lacp_port_unselect(portp);
12687c478bd9Sstevel@tonic-gate 		aggrp->aggr.ready = B_FALSE;
12697c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
12707c478bd9Sstevel@tonic-gate 		return;
12717c478bd9Sstevel@tonic-gate 	}
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	/*
12747c478bd9Sstevel@tonic-gate 	 * If LACP is not enabled then selected is never set.
12757c478bd9Sstevel@tonic-gate 	 */
12767c478bd9Sstevel@tonic-gate 	if (!pl->sm.lacp_enabled) {
1277d62bc4baSyz 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): selected %d-->%d\n",
1278d62bc4baSyz 		    portp->lp_linkid, pl->sm.selected, AGGR_UNSELECTED));
12797c478bd9Sstevel@tonic-gate 
1280f12af565Snd 		lacp_port_unselect(portp);
12817c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
12827c478bd9Sstevel@tonic-gate 		return;
12837c478bd9Sstevel@tonic-gate 	}
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 	/*
12867c478bd9Sstevel@tonic-gate 	 * Check if the Partner MAC or Key are zero. If so, we have
12877c478bd9Sstevel@tonic-gate 	 * not received any LACP info or it has expired and the
12887c478bd9Sstevel@tonic-gate 	 * receive machine is in the LACP_DEFAULTED state.
12897c478bd9Sstevel@tonic-gate 	 */
12907c478bd9Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem, &etherzeroaddr) == 0 ||
12917c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperKey == 0)) {
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
12947c478bd9Sstevel@tonic-gate 			if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
12957c478bd9Sstevel@tonic-gate 			    &etherzeroaddr) != 0 &&
12967c478bd9Sstevel@tonic-gate 			    (tpp->lp_lacp.PartnerOperKey != 0))
12977c478bd9Sstevel@tonic-gate 				break;
12987c478bd9Sstevel@tonic-gate 		}
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 		/*
13017c478bd9Sstevel@tonic-gate 		 * If all ports have no key or aggregation address,
13027c478bd9Sstevel@tonic-gate 		 * then clear the negotiated Partner MAC and key.
13037c478bd9Sstevel@tonic-gate 		 */
13047c478bd9Sstevel@tonic-gate 		if (tpp == NULL) {
13057c478bd9Sstevel@tonic-gate 			/* Clear the aggregation Partner MAC and key */
13067c478bd9Sstevel@tonic-gate 			aggrp->aggr.PartnerSystem = etherzeroaddr;
13077c478bd9Sstevel@tonic-gate 			aggrp->aggr.PartnerOperAggrKey = 0;
13087c478bd9Sstevel@tonic-gate 		}
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 		return;
13117c478bd9Sstevel@tonic-gate 	}
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	/*
13147c478bd9Sstevel@tonic-gate 	 * Insure that at least one port in the aggregation
13157c478bd9Sstevel@tonic-gate 	 * matches the Partner aggregation MAC and key. If not,
13167c478bd9Sstevel@tonic-gate 	 * then clear the aggregation MAC and key. Later we will
13177c478bd9Sstevel@tonic-gate 	 * set the Partner aggregation MAC and key to that of the
13187c478bd9Sstevel@tonic-gate 	 * current port's Partner MAC and key.
13197c478bd9Sstevel@tonic-gate 	 */
13207c478bd9Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem,
13217c478bd9Sstevel@tonic-gate 	    &aggrp->aggr.PartnerSystem) != 0 ||
13227c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) {
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
13257c478bd9Sstevel@tonic-gate 			if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
13267c478bd9Sstevel@tonic-gate 			    &aggrp->aggr.PartnerSystem) == 0 &&
13277c478bd9Sstevel@tonic-gate 			    (tpp->lp_lacp.PartnerOperKey ==
13280dc2366fSVenugopal Iyer 			    aggrp->aggr.PartnerOperAggrKey)) {
13290dc2366fSVenugopal Iyer 				/* Set aggregation Partner MAC and key */
13300dc2366fSVenugopal Iyer 				aggrp->aggr.PartnerSystem =
13310dc2366fSVenugopal Iyer 				    pl->PartnerOperSystem;
13320dc2366fSVenugopal Iyer 				aggrp->aggr.PartnerOperAggrKey =
13330dc2366fSVenugopal Iyer 				    pl->PartnerOperKey;
13347c478bd9Sstevel@tonic-gate 				break;
13350dc2366fSVenugopal Iyer 			}
13367c478bd9Sstevel@tonic-gate 		}
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 		if (tpp == NULL) {
13397c478bd9Sstevel@tonic-gate 			/* Clear the aggregation Partner MAC and key */
13407c478bd9Sstevel@tonic-gate 			aggrp->aggr.PartnerSystem = etherzeroaddr;
13417c478bd9Sstevel@tonic-gate 			aggrp->aggr.PartnerOperAggrKey = 0;
13427c478bd9Sstevel@tonic-gate 			reset_mac = B_TRUE;
13437c478bd9Sstevel@tonic-gate 		}
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	/*
13477c478bd9Sstevel@tonic-gate 	 * If our Actor MAC is found in the Partner MAC
13487c478bd9Sstevel@tonic-gate 	 * on this port then we have a loopback misconfiguration.
13497c478bd9Sstevel@tonic-gate 	 */
13507c478bd9Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem,
13517c478bd9Sstevel@tonic-gate 	    (struct ether_addr *)&aggrp->lg_addr) == 0) {
1352d62bc4baSyz 		cmn_err(CE_NOTE, "trunk link: (%d): Loopback condition.\n",
1353d62bc4baSyz 		    portp->lp_linkid);
13547c478bd9Sstevel@tonic-gate 
1355f12af565Snd 		lacp_port_unselect(portp);
13567c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
13577c478bd9Sstevel@tonic-gate 		return;
13587c478bd9Sstevel@tonic-gate 	}
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	/*
13617c478bd9Sstevel@tonic-gate 	 * If our Partner MAC and Key are found on any other
13627c478bd9Sstevel@tonic-gate 	 * ports that are not in our aggregation, we have
13637c478bd9Sstevel@tonic-gate 	 * a misconfiguration.
13647c478bd9Sstevel@tonic-gate 	 */
13657c478bd9Sstevel@tonic-gate 	if (lacp_misconfig_check(portp)) {
13667c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
13677c478bd9Sstevel@tonic-gate 		return;
13687c478bd9Sstevel@tonic-gate 	}
13697c478bd9Sstevel@tonic-gate 
13707c478bd9Sstevel@tonic-gate 	/*
13717c478bd9Sstevel@tonic-gate 	 * If the Aggregation Partner MAC and Key have not been
13727c478bd9Sstevel@tonic-gate 	 * set, then this is either the first port or the aggregation
13737c478bd9Sstevel@tonic-gate 	 * MAC and key have been reset. In either case we must set
13747c478bd9Sstevel@tonic-gate 	 * the values of the Partner MAC and key.
13757c478bd9Sstevel@tonic-gate 	 */
13767c478bd9Sstevel@tonic-gate 	if (ether_cmp(&aggrp->aggr.PartnerSystem, &etherzeroaddr) == 0 &&
13777c478bd9Sstevel@tonic-gate 	    (aggrp->aggr.PartnerOperAggrKey == 0)) {
13787c478bd9Sstevel@tonic-gate 		/* Set aggregation Partner MAC and key */
13797c478bd9Sstevel@tonic-gate 		aggrp->aggr.PartnerSystem = pl->PartnerOperSystem;
13807c478bd9Sstevel@tonic-gate 		aggrp->aggr.PartnerOperAggrKey = pl->PartnerOperKey;
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate 		/*
13837c478bd9Sstevel@tonic-gate 		 * If we reset Partner aggregation MAC, then restart
13847c478bd9Sstevel@tonic-gate 		 * selection_logic on ports that match new MAC address.
13857c478bd9Sstevel@tonic-gate 		 */
13867c478bd9Sstevel@tonic-gate 		if (reset_mac) {
13877c478bd9Sstevel@tonic-gate 			for (tpp = aggrp->lg_ports; tpp; tpp =
13887c478bd9Sstevel@tonic-gate 			    tpp->lp_next) {
13897c478bd9Sstevel@tonic-gate 				if (tpp == portp)
13907c478bd9Sstevel@tonic-gate 					continue;
13917c478bd9Sstevel@tonic-gate 				if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
13927c478bd9Sstevel@tonic-gate 				    &aggrp->aggr.PartnerSystem) == 0 &&
13937c478bd9Sstevel@tonic-gate 				    (tpp->lp_lacp.PartnerOperKey ==
13947c478bd9Sstevel@tonic-gate 				    aggrp->aggr.PartnerOperAggrKey))
13957c478bd9Sstevel@tonic-gate 					lacp_selection_logic(tpp);
13967c478bd9Sstevel@tonic-gate 			}
13977c478bd9Sstevel@tonic-gate 		}
13987c478bd9Sstevel@tonic-gate 	} else if (ether_cmp(&pl->PartnerOperSystem,
13997c478bd9Sstevel@tonic-gate 	    &aggrp->aggr.PartnerSystem) != 0 ||
14007c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) {
14017c478bd9Sstevel@tonic-gate 		/*
14027c478bd9Sstevel@tonic-gate 		 * The Partner port information does not match
14037c478bd9Sstevel@tonic-gate 		 * that of the other ports in the aggregation
14047c478bd9Sstevel@tonic-gate 		 * so disable this port.
14057c478bd9Sstevel@tonic-gate 		 */
1406f12af565Snd 		lacp_port_unselect(portp);
1407f12af565Snd 
1408d62bc4baSyz 		cmn_err(CE_NOTE, "trunk link: (%d): Port Partner MAC "
1409d62bc4baSyz 		    "or key (%d) incompatible with Aggregation Partner "
1410d62bc4baSyz 		    "MAC or key (%d)\n", portp->lp_linkid, pl->PartnerOperKey,
1411ba2e4443Sseb 		    aggrp->aggr.PartnerOperAggrKey);
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
14147c478bd9Sstevel@tonic-gate 		return;
14157c478bd9Sstevel@tonic-gate 	}
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 	/* If we get to here, automatically set selected */
14187c478bd9Sstevel@tonic-gate 	if (pl->sm.selected != AGGR_SELECTED) {
1419d62bc4baSyz 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
1420d62bc4baSyz 		    "selected %d-->%d\n", portp->lp_linkid,
14217c478bd9Sstevel@tonic-gate 		    pl->sm.selected, AGGR_SELECTED));
1422f12af565Snd 		if (!lacp_port_select(portp))
1423f12af565Snd 			return;
14247c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
14257c478bd9Sstevel@tonic-gate 	}
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 	/*
14287c478bd9Sstevel@tonic-gate 	 * From this point onward we have selected the port
14297c478bd9Sstevel@tonic-gate 	 * and are simply checking if the Ready flag should
14307c478bd9Sstevel@tonic-gate 	 * be set.
14317c478bd9Sstevel@tonic-gate 	 */
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 	/*
14347c478bd9Sstevel@tonic-gate 	 * If at least two ports are waiting to aggregate
14357c478bd9Sstevel@tonic-gate 	 * and ready_n is set on all ports waiting to aggregate
14367c478bd9Sstevel@tonic-gate 	 * then set READY for the aggregation.
14377c478bd9Sstevel@tonic-gate 	 */
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	ports_waiting = 0;
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 	if (!aggrp->aggr.ready) {
14427c478bd9Sstevel@tonic-gate 		/*
14437c478bd9Sstevel@tonic-gate 		 * If all ports in the aggregation have received compatible
14447c478bd9Sstevel@tonic-gate 		 * partner information and they match up correctly with the
14457c478bd9Sstevel@tonic-gate 		 * switch, there is no need to wait for all the
14467c478bd9Sstevel@tonic-gate 		 * wait_while_timers to pop.
14477c478bd9Sstevel@tonic-gate 		 */
14487c478bd9Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
14497c478bd9Sstevel@tonic-gate 			if (((tpp->lp_lacp.sm.mux_state == LACP_WAITING) ||
14507c478bd9Sstevel@tonic-gate 			    tpp->lp_lacp.sm.begin) &&
1451f562e45bSRamesh Kumar Katla 			    !tpp->lp_lacp.PartnerOperPortState.bit.sync) {
14527c478bd9Sstevel@tonic-gate 				/* Add up ports uninitialized or waiting */
14537c478bd9Sstevel@tonic-gate 				ports_waiting++;
1454f562e45bSRamesh Kumar Katla 				if (!tpp->lp_lacp.sm.ready_n) {
1455f562e45bSRamesh Kumar Katla 					DTRACE_PROBE1(port___not__ready,
1456f562e45bSRamesh Kumar Katla 					    aggr_port_t *, tpp);
14577c478bd9Sstevel@tonic-gate 					return;
1458f562e45bSRamesh Kumar Katla 				}
14597c478bd9Sstevel@tonic-gate 			}
14607c478bd9Sstevel@tonic-gate 		}
14617c478bd9Sstevel@tonic-gate 	}
14627c478bd9Sstevel@tonic-gate 
14637c478bd9Sstevel@tonic-gate 	if (aggrp->aggr.ready) {
1464d62bc4baSyz 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
1465d62bc4baSyz 		    "aggr.ready already set\n", portp->lp_linkid));
14667c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
14677c478bd9Sstevel@tonic-gate 	} else {
1468d62bc4baSyz 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): Ready %d-->%d\n",
1469d62bc4baSyz 		    portp->lp_linkid, aggrp->aggr.ready, B_TRUE));
14707c478bd9Sstevel@tonic-gate 		aggrp->aggr.ready = B_TRUE;
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next)
14737c478bd9Sstevel@tonic-gate 			lacp_mux_sm(tpp);
14747c478bd9Sstevel@tonic-gate 	}
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate }
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate /*
14797c478bd9Sstevel@tonic-gate  * wait_while_timer_pop - When the timer pops, we arrive here to
14807c478bd9Sstevel@tonic-gate  *			set ready_n and trigger the selection logic.
14817c478bd9Sstevel@tonic-gate  */
14827c478bd9Sstevel@tonic-gate static void
wait_while_timer_pop(void * data)14837c478bd9Sstevel@tonic-gate wait_while_timer_pop(void *data)
14847c478bd9Sstevel@tonic-gate {
14857c478bd9Sstevel@tonic-gate 	aggr_port_t *portp = data;
1486da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
14877c478bd9Sstevel@tonic-gate 
1488da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1489da14cebeSEric Cheng 	pl->lacp_timer_bits |= LACP_WAIT_WHILE_TIMEOUT;
1490da14cebeSEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
1491da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
1492da14cebeSEric Cheng }
14937c478bd9Sstevel@tonic-gate 
1494da14cebeSEric Cheng /*
1495da14cebeSEric Cheng  * wait_while_timer_pop_handler - When the timer pops, we arrive here to
1496da14cebeSEric Cheng  *			set ready_n and trigger the selection logic.
1497da14cebeSEric Cheng  */
1498da14cebeSEric Cheng static void
wait_while_timer_pop_handler(aggr_port_t * portp)1499da14cebeSEric Cheng wait_while_timer_pop_handler(aggr_port_t *portp)
1500da14cebeSEric Cheng {
1501da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15027c478bd9Sstevel@tonic-gate 
1503d62bc4baSyz 	AGGR_LACP_DBG(("trunk link:(%d): wait_while_timer pop \n",
1504d62bc4baSyz 	    portp->lp_linkid));
15057c478bd9Sstevel@tonic-gate 	portp->lp_lacp.sm.ready_n = B_TRUE;
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	lacp_selection_logic(portp);
15087c478bd9Sstevel@tonic-gate }
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate static void
start_wait_while_timer(aggr_port_t * portp)15117c478bd9Sstevel@tonic-gate start_wait_while_timer(aggr_port_t *portp)
15127c478bd9Sstevel@tonic-gate {
1513da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1514da14cebeSEric Cheng 
1515da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15167c478bd9Sstevel@tonic-gate 
1517da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1518da14cebeSEric Cheng 	if (pl->wait_while_timer.id == 0) {
1519da14cebeSEric Cheng 		pl->wait_while_timer.id =
15207c478bd9Sstevel@tonic-gate 		    timeout(wait_while_timer_pop, portp,
15217c478bd9Sstevel@tonic-gate 		    drv_usectohz(1000000 *
15227c478bd9Sstevel@tonic-gate 		    portp->lp_lacp.wait_while_timer.val));
15237c478bd9Sstevel@tonic-gate 	}
1524da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
15257c478bd9Sstevel@tonic-gate }
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate static void
stop_wait_while_timer(aggr_port_t * portp)1529da14cebeSEric Cheng stop_wait_while_timer(aggr_port_t *portp)
15307c478bd9Sstevel@tonic-gate {
1531da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1532da14cebeSEric Cheng 	timeout_id_t id;
1533da14cebeSEric Cheng 
1534da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15357c478bd9Sstevel@tonic-gate 
1536da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1537da14cebeSEric Cheng 	if ((id = pl->wait_while_timer.id) != 0) {
1538da14cebeSEric Cheng 		pl->lacp_timer_bits &= ~LACP_WAIT_WHILE_TIMEOUT;
1539da14cebeSEric Cheng 		pl->wait_while_timer.id = 0;
15407c478bd9Sstevel@tonic-gate 	}
1541da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
1542da14cebeSEric Cheng 
1543da14cebeSEric Cheng 	if (id != 0)
1544da14cebeSEric Cheng 		(void) untimeout(id);
15457c478bd9Sstevel@tonic-gate }
15467c478bd9Sstevel@tonic-gate 
15477c478bd9Sstevel@tonic-gate /*
15487c478bd9Sstevel@tonic-gate  * Invoked when a port has been attached to a group.
15497c478bd9Sstevel@tonic-gate  * Complete the processing that couldn't be finished from lacp_on()
15507c478bd9Sstevel@tonic-gate  * because the port was not started. We know that the link is full
15517c478bd9Sstevel@tonic-gate  * duplex and ON, otherwise it wouldn't be attached.
15527c478bd9Sstevel@tonic-gate  */
15537c478bd9Sstevel@tonic-gate void
aggr_lacp_port_attached(aggr_port_t * portp)15547c478bd9Sstevel@tonic-gate aggr_lacp_port_attached(aggr_port_t *portp)
15557c478bd9Sstevel@tonic-gate {
15567c478bd9Sstevel@tonic-gate 	aggr_grp_t *grp = portp->lp_grp;
15577c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
15587c478bd9Sstevel@tonic-gate 
1559da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
1560da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
15617c478bd9Sstevel@tonic-gate 	ASSERT(portp->lp_state == AGGR_PORT_STATE_ATTACHED);
15627c478bd9Sstevel@tonic-gate 
1563d62bc4baSyz 	AGGR_LACP_DBG(("aggr_lacp_port_attached: port %d\n",
1564d62bc4baSyz 	    portp->lp_linkid));
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	portp->lp_lacp.sm.port_enabled = B_TRUE;	/* link on */
15677c478bd9Sstevel@tonic-gate 
1568da14cebeSEric Cheng 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
15697c478bd9Sstevel@tonic-gate 		return;
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	pl->sm.lacp_enabled = B_TRUE;
15727c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.aggregation = B_TRUE;
15737c478bd9Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;
15747c478bd9Sstevel@tonic-gate 
1575da14cebeSEric Cheng 	lacp_receive_sm(portp, NULL);
1576da14cebeSEric Cheng 	lacp_mux_sm(portp);
15777c478bd9Sstevel@tonic-gate 
1578da14cebeSEric Cheng 	/* Enable Multicast Slow Protocol address */
1579da14cebeSEric Cheng 	aggr_lacp_mcast_on(portp);
15807c478bd9Sstevel@tonic-gate 
1581da14cebeSEric Cheng 	/* periodic_sm is started up from the receive machine */
1582da14cebeSEric Cheng 	lacp_selection_logic(portp);
15837c478bd9Sstevel@tonic-gate }
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate /*
15867c478bd9Sstevel@tonic-gate  * Invoked when a port has been detached from a group. Turn off
15877c478bd9Sstevel@tonic-gate  * LACP processing if it was enabled.
15887c478bd9Sstevel@tonic-gate  */
15897c478bd9Sstevel@tonic-gate void
aggr_lacp_port_detached(aggr_port_t * portp)15907c478bd9Sstevel@tonic-gate aggr_lacp_port_detached(aggr_port_t *portp)
15917c478bd9Sstevel@tonic-gate {
15927c478bd9Sstevel@tonic-gate 	aggr_grp_t *grp = portp->lp_grp;
15937c478bd9Sstevel@tonic-gate 
1594da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
1595da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
15967c478bd9Sstevel@tonic-gate 
1597d62bc4baSyz 	AGGR_LACP_DBG(("aggr_lacp_port_detached: port %d\n",
1598d62bc4baSyz 	    portp->lp_linkid));
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate 	portp->lp_lacp.sm.port_enabled = B_FALSE;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
16037c478bd9Sstevel@tonic-gate 		return;
16047c478bd9Sstevel@tonic-gate 
1605da14cebeSEric Cheng 	portp->lp_lacp.sm.lacp_enabled = B_FALSE;
1606da14cebeSEric Cheng 	lacp_selection_logic(portp);
1607da14cebeSEric Cheng 	lacp_mux_sm(portp);
1608da14cebeSEric Cheng 	lacp_periodic_sm(portp);
16097c478bd9Sstevel@tonic-gate 
1610da14cebeSEric Cheng 	/*
1611da14cebeSEric Cheng 	 * Disable Slow Protocol Timers.
1612da14cebeSEric Cheng 	 */
1613da14cebeSEric Cheng 	stop_periodic_timer(portp);
1614da14cebeSEric Cheng 	stop_current_while_timer(portp);
1615da14cebeSEric Cheng 	stop_wait_while_timer(portp);
16167c478bd9Sstevel@tonic-gate 
1617da14cebeSEric Cheng 	/* Disable Multicast Slow Protocol address */
1618da14cebeSEric Cheng 	aggr_lacp_mcast_off(portp);
1619da14cebeSEric Cheng 	aggr_set_coll_dist(portp, B_FALSE);
16207c478bd9Sstevel@tonic-gate }
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate /*
16237c478bd9Sstevel@tonic-gate  * Enable Slow Protocol LACP and Marker PDUs.
16247c478bd9Sstevel@tonic-gate  */
16257c478bd9Sstevel@tonic-gate static void
lacp_on(aggr_port_t * portp)16267c478bd9Sstevel@tonic-gate lacp_on(aggr_port_t *portp)
16277c478bd9Sstevel@tonic-gate {
1628da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1629da14cebeSEric Cheng 	mac_perim_handle_t mph;
1630da14cebeSEric Cheng 
1631da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
1632da14cebeSEric Cheng 
1633da14cebeSEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	/*
16367c478bd9Sstevel@tonic-gate 	 * Reset the state machines and Partner operational
16377c478bd9Sstevel@tonic-gate 	 * information. Careful to not reset things like
16387c478bd9Sstevel@tonic-gate 	 * our link state.
16397c478bd9Sstevel@tonic-gate 	 */
16407c478bd9Sstevel@tonic-gate 	lacp_reset_port(portp);
1641da14cebeSEric Cheng 	pl->sm.lacp_on = B_TRUE;
16427c478bd9Sstevel@tonic-gate 
1643d62bc4baSyz 	AGGR_LACP_DBG(("lacp_on:(%d): \n", portp->lp_linkid));
16447c478bd9Sstevel@tonic-gate 
1645da14cebeSEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
1646da14cebeSEric Cheng 		pl->sm.port_enabled = B_TRUE;
1647da14cebeSEric Cheng 		pl->sm.lacp_enabled = B_TRUE;
1648da14cebeSEric Cheng 		pl->ActorOperPortState.bit.aggregation = B_TRUE;
1649da14cebeSEric Cheng 	}
1650da14cebeSEric Cheng 
16517c478bd9Sstevel@tonic-gate 	lacp_receive_sm(portp, NULL);
16527c478bd9Sstevel@tonic-gate 	lacp_mux_sm(portp);
16537c478bd9Sstevel@tonic-gate 
1654da14cebeSEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
1655da14cebeSEric Cheng 		/* Enable Multicast Slow Protocol address */
1656da14cebeSEric Cheng 		aggr_lacp_mcast_on(portp);
16577c478bd9Sstevel@tonic-gate 
1658da14cebeSEric Cheng 		/* periodic_sm is started up from the receive machine */
1659da14cebeSEric Cheng 		lacp_selection_logic(portp);
1660da14cebeSEric Cheng 	}
1661da14cebeSEric Cheng done:
1662da14cebeSEric Cheng 	mac_perim_exit(mph);
16637c478bd9Sstevel@tonic-gate } /* lacp_on */
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate /* Disable Slow Protocol LACP and Marker PDUs */
16667c478bd9Sstevel@tonic-gate static void
lacp_off(aggr_port_t * portp)16677c478bd9Sstevel@tonic-gate lacp_off(aggr_port_t *portp)
16687c478bd9Sstevel@tonic-gate {
1669da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1670da14cebeSEric Cheng 	mac_perim_handle_t mph;
16717c478bd9Sstevel@tonic-gate 
1672da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
1673da14cebeSEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
16747c478bd9Sstevel@tonic-gate 
1675da14cebeSEric Cheng 	pl->sm.lacp_on = B_FALSE;
16767c478bd9Sstevel@tonic-gate 
1677d62bc4baSyz 	AGGR_LACP_DBG(("lacp_off:(%d): \n", portp->lp_linkid));
16787c478bd9Sstevel@tonic-gate 
1679da14cebeSEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
1680da14cebeSEric Cheng 		/*
1681da14cebeSEric Cheng 		 * Disable Slow Protocol Timers.
1682da14cebeSEric Cheng 		 */
1683da14cebeSEric Cheng 		stop_periodic_timer(portp);
1684da14cebeSEric Cheng 		stop_current_while_timer(portp);
1685da14cebeSEric Cheng 		stop_wait_while_timer(portp);
16867c478bd9Sstevel@tonic-gate 
1687da14cebeSEric Cheng 		/* Disable Multicast Slow Protocol address */
1688da14cebeSEric Cheng 		aggr_lacp_mcast_off(portp);
16897c478bd9Sstevel@tonic-gate 
1690da14cebeSEric Cheng 		pl->sm.port_enabled = B_FALSE;
1691da14cebeSEric Cheng 		pl->sm.lacp_enabled = B_FALSE;
1692da14cebeSEric Cheng 		pl->ActorOperPortState.bit.aggregation = B_FALSE;
16937c478bd9Sstevel@tonic-gate 	}
16947c478bd9Sstevel@tonic-gate 
1695da14cebeSEric Cheng 	lacp_mux_sm(portp);
1696da14cebeSEric Cheng 	lacp_periodic_sm(portp);
1697da14cebeSEric Cheng 	lacp_selection_logic(portp);
16987c478bd9Sstevel@tonic-gate 
1699da14cebeSEric Cheng 	/* Turn OFF Collector_Distributor */
1700da14cebeSEric Cheng 	aggr_set_coll_dist(portp, B_FALSE);
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 	lacp_reset_port(portp);
1703da14cebeSEric Cheng 	mac_perim_exit(mph);
17047c478bd9Sstevel@tonic-gate }
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate static boolean_t
valid_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)17087c478bd9Sstevel@tonic-gate valid_lacp_pdu(aggr_port_t *portp, lacp_t *lacp)
17097c478bd9Sstevel@tonic-gate {
17107c478bd9Sstevel@tonic-gate 	/*
17117c478bd9Sstevel@tonic-gate 	 * 43.4.12 - "a Receive machine shall not validate
17127c478bd9Sstevel@tonic-gate 	 * the Version Number, TLV_type, or Reserved fields in received
17137c478bd9Sstevel@tonic-gate 	 * LACPDUs."
17147c478bd9Sstevel@tonic-gate 	 * ... "a Receive machine may validate the Actor_Information_Length,
17157c478bd9Sstevel@tonic-gate 	 * Partner_Information_Length, Collector_Information_Length,
17167c478bd9Sstevel@tonic-gate 	 * or Terminator_Length fields."
17177c478bd9Sstevel@tonic-gate 	 */
17187c478bd9Sstevel@tonic-gate 	if ((lacp->actor_info.information_len != sizeof (link_info_t)) ||
17197c478bd9Sstevel@tonic-gate 	    (lacp->partner_info.information_len != sizeof (link_info_t)) ||
17207c478bd9Sstevel@tonic-gate 	    (lacp->collector_len != LACP_COLLECTOR_INFO_LEN) ||
17217c478bd9Sstevel@tonic-gate 	    (lacp->terminator_len != LACP_TERMINATOR_INFO_LEN)) {
1722d62bc4baSyz 		AGGR_LACP_DBG(("trunk link (%d): Malformed LACPDU: "
1723d62bc4baSyz 		    " Terminator Length = %d \n", portp->lp_linkid,
1724ba2e4443Sseb 		    lacp->terminator_len));
17257c478bd9Sstevel@tonic-gate 		return (B_FALSE);
17267c478bd9Sstevel@tonic-gate 	}
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 	return (B_TRUE);
17297c478bd9Sstevel@tonic-gate }
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate static void
start_current_while_timer(aggr_port_t * portp,uint_t time)17337c478bd9Sstevel@tonic-gate start_current_while_timer(aggr_port_t *portp, uint_t time)
17347c478bd9Sstevel@tonic-gate {
1735da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1736da14cebeSEric Cheng 
1737da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
1738da14cebeSEric Cheng 
1739da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1740da14cebeSEric Cheng 	if (pl->current_while_timer.id == 0) {
1741da14cebeSEric Cheng 		if (time > 0)
1742da14cebeSEric Cheng 			pl->current_while_timer.val = time;
1743da14cebeSEric Cheng 		else if (pl->ActorOperPortState.bit.timeout)
1744da14cebeSEric Cheng 			pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
1745da14cebeSEric Cheng 		else
1746da14cebeSEric Cheng 			pl->current_while_timer.val = LONG_TIMEOUT_TIME;
17477c478bd9Sstevel@tonic-gate 
1748da14cebeSEric Cheng 		pl->current_while_timer.id =
17497c478bd9Sstevel@tonic-gate 		    timeout(current_while_timer_pop, portp,
17507c478bd9Sstevel@tonic-gate 		    drv_usectohz((clock_t)1000000 *
17517c478bd9Sstevel@tonic-gate 		    (clock_t)portp->lp_lacp.current_while_timer.val));
17527c478bd9Sstevel@tonic-gate 	}
1753da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
17547c478bd9Sstevel@tonic-gate }
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate static void
stop_current_while_timer(aggr_port_t * portp)17587c478bd9Sstevel@tonic-gate stop_current_while_timer(aggr_port_t *portp)
17597c478bd9Sstevel@tonic-gate {
1760da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
1761da14cebeSEric Cheng 	timeout_id_t id;
1762da14cebeSEric Cheng 
1763da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
17647c478bd9Sstevel@tonic-gate 
1765da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1766da14cebeSEric Cheng 	if ((id = pl->current_while_timer.id) != 0) {
1767da14cebeSEric Cheng 		pl->lacp_timer_bits &= ~LACP_CURRENT_WHILE_TIMEOUT;
1768da14cebeSEric Cheng 		pl->current_while_timer.id = 0;
17697c478bd9Sstevel@tonic-gate 	}
1770da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
17717c478bd9Sstevel@tonic-gate 
1772da14cebeSEric Cheng 	if (id != 0)
1773da14cebeSEric Cheng 		(void) untimeout(id);
1774da14cebeSEric Cheng }
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate static void
current_while_timer_pop(void * data)17777c478bd9Sstevel@tonic-gate current_while_timer_pop(void *data)
17787c478bd9Sstevel@tonic-gate {
17797c478bd9Sstevel@tonic-gate 	aggr_port_t *portp = (aggr_port_t *)data;
1780da14cebeSEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
17817c478bd9Sstevel@tonic-gate 
1782da14cebeSEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1783da14cebeSEric Cheng 	pl->lacp_timer_bits |= LACP_CURRENT_WHILE_TIMEOUT;
1784da14cebeSEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
1785da14cebeSEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
1786da14cebeSEric Cheng }
17877c478bd9Sstevel@tonic-gate 
1788da14cebeSEric Cheng static void
current_while_timer_pop_handler(aggr_port_t * portp)1789da14cebeSEric Cheng current_while_timer_pop_handler(aggr_port_t *portp)
1790da14cebeSEric Cheng {
1791da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
17927c478bd9Sstevel@tonic-gate 
1793d62bc4baSyz 	AGGR_LACP_DBG(("trunk link:(%d): current_while_timer "
1794d62bc4baSyz 	    "pop id=%p\n", portp->lp_linkid,
17957c478bd9Sstevel@tonic-gate 	    portp->lp_lacp.current_while_timer.id));
17967c478bd9Sstevel@tonic-gate 
17977c478bd9Sstevel@tonic-gate 	lacp_receive_sm(portp, NULL);
17987c478bd9Sstevel@tonic-gate }
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate /*
18017c478bd9Sstevel@tonic-gate  * record_Default - Simply copies over administrative values
18027c478bd9Sstevel@tonic-gate  * to the partner operational values, and sets our state to indicate we
18037c478bd9Sstevel@tonic-gate  * are using defaulted values.
18047c478bd9Sstevel@tonic-gate  */
18057c478bd9Sstevel@tonic-gate static void
record_Default(aggr_port_t * portp)18067c478bd9Sstevel@tonic-gate record_Default(aggr_port_t *portp)
18077c478bd9Sstevel@tonic-gate {
18087c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18097c478bd9Sstevel@tonic-gate 
1810da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortNum = pl->PartnerAdminPortNum;
18137c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortPriority = pl->PartnerAdminPortPriority;
18147c478bd9Sstevel@tonic-gate 	pl->PartnerOperSystem = pl->PartnerAdminSystem;
18157c478bd9Sstevel@tonic-gate 	pl->PartnerOperSysPriority = pl->PartnerAdminSysPriority;
18167c478bd9Sstevel@tonic-gate 	pl->PartnerOperKey = pl->PartnerAdminKey;
18177c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortState.state = pl->PartnerAdminPortState.state;
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_TRUE;
18207c478bd9Sstevel@tonic-gate }
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate /* Returns B_TRUE on sync value changing */
18247c478bd9Sstevel@tonic-gate static boolean_t
record_PDU(aggr_port_t * portp,lacp_t * lacp)18257c478bd9Sstevel@tonic-gate record_PDU(aggr_port_t *portp, lacp_t *lacp)
18267c478bd9Sstevel@tonic-gate {
18277c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
18287c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18297c478bd9Sstevel@tonic-gate 	uint8_t save_sync;
18307c478bd9Sstevel@tonic-gate 
1831da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 	/*
18347c478bd9Sstevel@tonic-gate 	 * Partner Information
18357c478bd9Sstevel@tonic-gate 	 */
18367c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortNum = ntohs(lacp->actor_info.port);
18377c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortPriority =
18387c478bd9Sstevel@tonic-gate 	    ntohs(lacp->actor_info.port_priority);
18397c478bd9Sstevel@tonic-gate 	pl->PartnerOperSystem = lacp->actor_info.system_id;
18407c478bd9Sstevel@tonic-gate 	pl->PartnerOperSysPriority =
18417c478bd9Sstevel@tonic-gate 	    htons(lacp->actor_info.system_priority);
18427c478bd9Sstevel@tonic-gate 	pl->PartnerOperKey = ntohs(lacp->actor_info.key);
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate 	/* All state info except for Synchronization */
18457c478bd9Sstevel@tonic-gate 	save_sync = pl->PartnerOperPortState.bit.sync;
18467c478bd9Sstevel@tonic-gate 	pl->PartnerOperPortState.state = lacp->actor_info.state.state;
18477c478bd9Sstevel@tonic-gate 
18487c478bd9Sstevel@tonic-gate 	/* Defaulted set to FALSE */
18497c478bd9Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_FALSE;
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 	/*
18527c478bd9Sstevel@tonic-gate 	 * 43.4.9 - (Partner_Port, Partner_Port_Priority, Partner_system,
18537c478bd9Sstevel@tonic-gate 	 *		Partner_System_Priority, Partner_Key, and
18547c478bd9Sstevel@tonic-gate 	 *		Partner_State.Aggregation) are compared to the
18557c478bd9Sstevel@tonic-gate 	 *		corresponding operations paramters values for
18567c478bd9Sstevel@tonic-gate 	 *		the Actor. If these are equal, or if this is
18577c478bd9Sstevel@tonic-gate 	 *		an individual link, we are synchronized.
18587c478bd9Sstevel@tonic-gate 	 */
18597c478bd9Sstevel@tonic-gate 	if (((ntohs(lacp->partner_info.port) == pl->ActorPortNumber) &&
18607c478bd9Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.port_priority) ==
18617c478bd9Sstevel@tonic-gate 	    pl->ActorPortPriority) &&
18627c478bd9Sstevel@tonic-gate 	    (ether_cmp(&lacp->partner_info.system_id,
1863392b1d6eSyz 	    (struct ether_addr *)&aggrp->lg_addr) == 0) &&
18647c478bd9Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.system_priority) ==
18657c478bd9Sstevel@tonic-gate 	    aggrp->aggr.ActorSystemPriority) &&
18667c478bd9Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.key) == pl->ActorOperPortKey) &&
18677c478bd9Sstevel@tonic-gate 	    (lacp->partner_info.state.bit.aggregation ==
18687c478bd9Sstevel@tonic-gate 	    pl->ActorOperPortState.bit.aggregation)) ||
18697c478bd9Sstevel@tonic-gate 	    (!lacp->actor_info.state.bit.aggregation)) {
18707c478bd9Sstevel@tonic-gate 
18717c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync =
18727c478bd9Sstevel@tonic-gate 		    lacp->actor_info.state.bit.sync;
18737c478bd9Sstevel@tonic-gate 	} else {
18747c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
18757c478bd9Sstevel@tonic-gate 	}
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 	if (save_sync != pl->PartnerOperPortState.bit.sync) {
1878d62bc4baSyz 		AGGR_LACP_DBG(("record_PDU:(%d): partner sync "
1879d62bc4baSyz 		    "%d -->%d\n", portp->lp_linkid, save_sync,
1880ba2e4443Sseb 		    pl->PartnerOperPortState.bit.sync));
18817c478bd9Sstevel@tonic-gate 		return (B_TRUE);
18827c478bd9Sstevel@tonic-gate 	} else {
18837c478bd9Sstevel@tonic-gate 		return (B_FALSE);
18847c478bd9Sstevel@tonic-gate 	}
18857c478bd9Sstevel@tonic-gate }
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate /*
18897c478bd9Sstevel@tonic-gate  * update_selected - If any of the Partner parameters has
18907c478bd9Sstevel@tonic-gate  *			changed from a previous value, then
18917c478bd9Sstevel@tonic-gate  *			unselect the link from the aggregator.
18927c478bd9Sstevel@tonic-gate  */
18937c478bd9Sstevel@tonic-gate static boolean_t
update_selected(aggr_port_t * portp,lacp_t * lacp)18947c478bd9Sstevel@tonic-gate update_selected(aggr_port_t *portp, lacp_t *lacp)
18957c478bd9Sstevel@tonic-gate {
18967c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18977c478bd9Sstevel@tonic-gate 
1898da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	if ((pl->PartnerOperPortNum != ntohs(lacp->actor_info.port)) ||
19017c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperPortPriority !=
19027c478bd9Sstevel@tonic-gate 	    ntohs(lacp->actor_info.port_priority)) ||
19037c478bd9Sstevel@tonic-gate 	    (ether_cmp(&pl->PartnerOperSystem,
19047c478bd9Sstevel@tonic-gate 	    &lacp->actor_info.system_id) != 0) ||
19057c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperSysPriority !=
19067c478bd9Sstevel@tonic-gate 	    ntohs(lacp->actor_info.system_priority)) ||
19077c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperKey != ntohs(lacp->actor_info.key)) ||
19087c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperPortState.bit.aggregation !=
19097c478bd9Sstevel@tonic-gate 	    lacp->actor_info.state.bit.aggregation)) {
1910d62bc4baSyz 		AGGR_LACP_DBG(("update_selected:(%d): "
1911d62bc4baSyz 		    "selected  %d-->%d\n", portp->lp_linkid, pl->sm.selected,
1912ba2e4443Sseb 		    AGGR_UNSELECTED));
19137c478bd9Sstevel@tonic-gate 
1914f12af565Snd 		lacp_port_unselect(portp);
19157c478bd9Sstevel@tonic-gate 		return (B_TRUE);
19167c478bd9Sstevel@tonic-gate 	} else {
19177c478bd9Sstevel@tonic-gate 		return (B_FALSE);
19187c478bd9Sstevel@tonic-gate 	}
19197c478bd9Sstevel@tonic-gate }
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate /*
19237c478bd9Sstevel@tonic-gate  * update_default_selected - If any of the operational Partner parameters
19247c478bd9Sstevel@tonic-gate  *			is different than that of the administrative values
19257c478bd9Sstevel@tonic-gate  *			then unselect the link from the aggregator.
19267c478bd9Sstevel@tonic-gate  */
19277c478bd9Sstevel@tonic-gate static void
update_default_selected(aggr_port_t * portp)19287c478bd9Sstevel@tonic-gate update_default_selected(aggr_port_t *portp)
19297c478bd9Sstevel@tonic-gate {
19307c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
19317c478bd9Sstevel@tonic-gate 
1932da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 	if ((pl->PartnerAdminPortNum != pl->PartnerOperPortNum) ||
19357c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperPortPriority != pl->PartnerAdminPortPriority) ||
19367c478bd9Sstevel@tonic-gate 	    (ether_cmp(&pl->PartnerOperSystem, &pl->PartnerAdminSystem) != 0) ||
19377c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperSysPriority != pl->PartnerAdminSysPriority) ||
19387c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperKey != pl->PartnerAdminKey) ||
19397c478bd9Sstevel@tonic-gate 	    (pl->PartnerOperPortState.bit.aggregation !=
19407c478bd9Sstevel@tonic-gate 	    pl->PartnerAdminPortState.bit.aggregation)) {
19417c478bd9Sstevel@tonic-gate 
1942d62bc4baSyz 		AGGR_LACP_DBG(("update_default_selected:(%d): "
1943d62bc4baSyz 		    "selected  %d-->%d\n", portp->lp_linkid,
19447c478bd9Sstevel@tonic-gate 		    pl->sm.selected, AGGR_UNSELECTED));
1945f12af565Snd 
1946f12af565Snd 		lacp_port_unselect(portp);
19477c478bd9Sstevel@tonic-gate 	}
19487c478bd9Sstevel@tonic-gate }
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 
19517c478bd9Sstevel@tonic-gate /*
19527c478bd9Sstevel@tonic-gate  * update_NTT - If any of the Partner values in the received LACPDU
19537c478bd9Sstevel@tonic-gate  *			are different than that of the Actor operational
19547c478bd9Sstevel@tonic-gate  *			values then set NTT to true.
19557c478bd9Sstevel@tonic-gate  */
19567c478bd9Sstevel@tonic-gate static void
update_NTT(aggr_port_t * portp,lacp_t * lacp)19577c478bd9Sstevel@tonic-gate update_NTT(aggr_port_t *portp, lacp_t *lacp)
19587c478bd9Sstevel@tonic-gate {
19597c478bd9Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
19607c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
19617c478bd9Sstevel@tonic-gate 
1962da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 	if ((pl->ActorPortNumber != ntohs(lacp->partner_info.port)) ||
19657c478bd9Sstevel@tonic-gate 	    (pl->ActorPortPriority !=
19667c478bd9Sstevel@tonic-gate 	    ntohs(lacp->partner_info.port_priority)) ||
19677c478bd9Sstevel@tonic-gate 	    (ether_cmp(&aggrp->lg_addr,
19687c478bd9Sstevel@tonic-gate 	    &lacp->partner_info.system_id) != 0) ||
19697c478bd9Sstevel@tonic-gate 	    (aggrp->aggr.ActorSystemPriority !=
19707c478bd9Sstevel@tonic-gate 	    ntohs(lacp->partner_info.system_priority)) ||
19717c478bd9Sstevel@tonic-gate 	    (pl->ActorOperPortKey != ntohs(lacp->partner_info.key)) ||
19727c478bd9Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.activity !=
19737c478bd9Sstevel@tonic-gate 	    lacp->partner_info.state.bit.activity) ||
19747c478bd9Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.timeout !=
19757c478bd9Sstevel@tonic-gate 	    lacp->partner_info.state.bit.timeout) ||
19767c478bd9Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.sync !=
19777c478bd9Sstevel@tonic-gate 	    lacp->partner_info.state.bit.sync) ||
19787c478bd9Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.aggregation !=
19797c478bd9Sstevel@tonic-gate 	    lacp->partner_info.state.bit.aggregation)) {
19807c478bd9Sstevel@tonic-gate 
1981d62bc4baSyz 		AGGR_LACP_DBG(("update_NTT:(%d): NTT  %d-->%d\n",
1982d62bc4baSyz 		    portp->lp_linkid, pl->NTT, B_TRUE));
19837c478bd9Sstevel@tonic-gate 
19847c478bd9Sstevel@tonic-gate 		pl->NTT = B_TRUE;
19857c478bd9Sstevel@tonic-gate 	}
19867c478bd9Sstevel@tonic-gate }
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate /*
19897c478bd9Sstevel@tonic-gate  * lacp_receive_sm - LACP receive state machine
19907c478bd9Sstevel@tonic-gate  *
19917c478bd9Sstevel@tonic-gate  * parameters:
19927c478bd9Sstevel@tonic-gate  *      - portp - instance this applies to.
19937c478bd9Sstevel@tonic-gate  *      - lacp - pointer in the case of a received LACPDU.
19947c478bd9Sstevel@tonic-gate  *                This value is NULL if there is no LACPDU.
19957c478bd9Sstevel@tonic-gate  *
19967c478bd9Sstevel@tonic-gate  * invoked:
19977c478bd9Sstevel@tonic-gate  *    - when initialization is needed
19987c478bd9Sstevel@tonic-gate  *    - upon reception of an LACPDU. This is the common case.
19997c478bd9Sstevel@tonic-gate  *    - every time the current_while_timer pops
20007c478bd9Sstevel@tonic-gate  */
20017c478bd9Sstevel@tonic-gate static void
lacp_receive_sm(aggr_port_t * portp,lacp_t * lacp)20027c478bd9Sstevel@tonic-gate lacp_receive_sm(aggr_port_t *portp, lacp_t *lacp)
20037c478bd9Sstevel@tonic-gate {
20047c478bd9Sstevel@tonic-gate 	boolean_t sync_updated, selected_updated, save_activity;
20057c478bd9Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
20067c478bd9Sstevel@tonic-gate 	lacp_receive_state_t oldstate = pl->sm.receive_state;
20077c478bd9Sstevel@tonic-gate 
2008da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
20117c478bd9Sstevel@tonic-gate 	if (!pl->sm.lacp_on)
20127c478bd9Sstevel@tonic-gate 		return;
20137c478bd9Sstevel@tonic-gate 
20147c478bd9Sstevel@tonic-gate 	/* figure next state */
20157c478bd9Sstevel@tonic-gate 	if (pl->sm.begin || pl->sm.port_moved) {
20167c478bd9Sstevel@tonic-gate 		pl->sm.receive_state = LACP_INITIALIZE;
20177c478bd9Sstevel@tonic-gate 	} else if (!pl->sm.port_enabled) {	/* DL_NOTE_LINK_DOWN */
20187c478bd9Sstevel@tonic-gate 		pl->sm.receive_state = LACP_PORT_DISABLED;
20197c478bd9Sstevel@tonic-gate 	} else if (!pl->sm.lacp_enabled) { /* DL_NOTE_AGGR_UNAVAIL */
20207c478bd9Sstevel@tonic-gate 		pl->sm.receive_state =
20217c478bd9Sstevel@tonic-gate 		    (pl->sm.receive_state == LACP_PORT_DISABLED) ?
20227c478bd9Sstevel@tonic-gate 		    LACP_DISABLED : LACP_PORT_DISABLED;
20237c478bd9Sstevel@tonic-gate 	} else if (lacp != NULL) {
20247c478bd9Sstevel@tonic-gate 		if ((pl->sm.receive_state == LACP_EXPIRED) ||
20257c478bd9Sstevel@tonic-gate 		    (pl->sm.receive_state == LACP_DEFAULTED)) {
20267c478bd9Sstevel@tonic-gate 			pl->sm.receive_state = LACP_CURRENT;
20277c478bd9Sstevel@tonic-gate 		}
20287c478bd9Sstevel@tonic-gate 	} else if ((pl->sm.receive_state == LACP_CURRENT) &&
20297c478bd9Sstevel@tonic-gate 	    (pl->current_while_timer.id == 0)) {
20307c478bd9Sstevel@tonic-gate 		pl->sm.receive_state = LACP_EXPIRED;
20317c478bd9Sstevel@tonic-gate 	} else if ((pl->sm.receive_state == LACP_EXPIRED) &&
20327c478bd9Sstevel@tonic-gate 	    (pl->current_while_timer.id == 0)) {
20337c478bd9Sstevel@tonic-gate 		pl->sm.receive_state = LACP_DEFAULTED;
20347c478bd9Sstevel@tonic-gate 	}
20357c478bd9Sstevel@tonic-gate 
20367c478bd9Sstevel@tonic-gate 	if (!((lacp && (oldstate == LACP_CURRENT) &&
20377c478bd9Sstevel@tonic-gate 	    (pl->sm.receive_state == LACP_CURRENT)))) {
2038d62bc4baSyz 		AGGR_LACP_DBG(("lacp_receive_sm(%d):%s--->%s\n",
2039d62bc4baSyz 		    portp->lp_linkid, lacp_receive_str[oldstate],
20407c478bd9Sstevel@tonic-gate 		    lacp_receive_str[pl->sm.receive_state]));
20417c478bd9Sstevel@tonic-gate 	}
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 	switch (pl->sm.receive_state) {
20447c478bd9Sstevel@tonic-gate 	case LACP_INITIALIZE:
2045f12af565Snd 		lacp_port_unselect(portp);
20467c478bd9Sstevel@tonic-gate 		record_Default(portp);
20477c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
20487c478bd9Sstevel@tonic-gate 		pl->sm.port_moved = B_FALSE;
20497c478bd9Sstevel@tonic-gate 		pl->sm.receive_state = LACP_PORT_DISABLED;
20507c478bd9Sstevel@tonic-gate 		pl->sm.begin = B_FALSE;
20517c478bd9Sstevel@tonic-gate 		lacp_receive_sm(portp, NULL);
20527c478bd9Sstevel@tonic-gate 		break;
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate 	case LACP_PORT_DISABLED:
20557c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
20567c478bd9Sstevel@tonic-gate 		/*
20577c478bd9Sstevel@tonic-gate 		 * Stop current_while_timer in case
20587c478bd9Sstevel@tonic-gate 		 * we got here from link down
20597c478bd9Sstevel@tonic-gate 		 */
20607c478bd9Sstevel@tonic-gate 		stop_current_while_timer(portp);
20617c478bd9Sstevel@tonic-gate 
20627c478bd9Sstevel@tonic-gate 		if (pl->sm.port_enabled && !pl->sm.lacp_enabled) {
20637c478bd9Sstevel@tonic-gate 			pl->sm.receive_state = LACP_DISABLED;
20647c478bd9Sstevel@tonic-gate 			lacp_receive_sm(portp, lacp);
20657c478bd9Sstevel@tonic-gate 			/* We goto LACP_DISABLED state */
20667c478bd9Sstevel@tonic-gate 			break;
20677c478bd9Sstevel@tonic-gate 		} else if (pl->sm.port_enabled && pl->sm.lacp_enabled) {
20687c478bd9Sstevel@tonic-gate 			pl->sm.receive_state = LACP_EXPIRED;
20697c478bd9Sstevel@tonic-gate 			/*
20707c478bd9Sstevel@tonic-gate 			 * FALL THROUGH TO LACP_EXPIRED CASE:
20717c478bd9Sstevel@tonic-gate 			 * We have no way of knowing if we get into
20727c478bd9Sstevel@tonic-gate 			 * lacp_receive_sm() from a  current_while_timer
20737c478bd9Sstevel@tonic-gate 			 * expiring as it has never been kicked off yet!
20747c478bd9Sstevel@tonic-gate 			 */
20757c478bd9Sstevel@tonic-gate 		} else {
20767c478bd9Sstevel@tonic-gate 			/* We stay in LACP_PORT_DISABLED state */
20777c478bd9Sstevel@tonic-gate 			break;
20787c478bd9Sstevel@tonic-gate 		}
20797c478bd9Sstevel@tonic-gate 		/* LACP_PORT_DISABLED -> LACP_EXPIRED */
20807c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate 	case LACP_EXPIRED:
20837c478bd9Sstevel@tonic-gate 		/*
20847c478bd9Sstevel@tonic-gate 		 * Arrives here from LACP_PORT_DISABLED state as well as
20857c478bd9Sstevel@tonic-gate 		 * as well as current_while_timer expiring.
20867c478bd9Sstevel@tonic-gate 		 */
20877c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
20887c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.timeout = B_TRUE;
20897c478bd9Sstevel@tonic-gate 
20907c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_TRUE;
20917c478bd9Sstevel@tonic-gate 		start_current_while_timer(portp, SHORT_TIMEOUT_TIME);
20927c478bd9Sstevel@tonic-gate 		lacp_periodic_sm(portp);
20937c478bd9Sstevel@tonic-gate 		break;
20947c478bd9Sstevel@tonic-gate 
20957c478bd9Sstevel@tonic-gate 	case LACP_DISABLED:
20967c478bd9Sstevel@tonic-gate 		/*
20977c478bd9Sstevel@tonic-gate 		 * This is the normal state for recv_sm when LACP_OFF
20987c478bd9Sstevel@tonic-gate 		 * is set or the NIC is in half duplex mode.
20997c478bd9Sstevel@tonic-gate 		 */
2100f12af565Snd 		lacp_port_unselect(portp);
21017c478bd9Sstevel@tonic-gate 		record_Default(portp);
21027c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.aggregation = B_FALSE;
21037c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21047c478bd9Sstevel@tonic-gate 		break;
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate 	case LACP_DEFAULTED:
21077c478bd9Sstevel@tonic-gate 		/*
21087c478bd9Sstevel@tonic-gate 		 * Current_while_timer expired a second time.
21097c478bd9Sstevel@tonic-gate 		 */
21107c478bd9Sstevel@tonic-gate 		update_default_selected(portp);
21117c478bd9Sstevel@tonic-gate 		record_Default(portp);	/* overwrite Partner Oper val */
21127c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21137c478bd9Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_TRUE;
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 		lacp_selection_logic(portp);
21167c478bd9Sstevel@tonic-gate 		lacp_mux_sm(portp);
21177c478bd9Sstevel@tonic-gate 		break;
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 	case LACP_CURRENT:
21207c478bd9Sstevel@tonic-gate 		/*
21217c478bd9Sstevel@tonic-gate 		 * Reception of LACPDU
21227c478bd9Sstevel@tonic-gate 		 */
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate 		if (!lacp) /* no LACPDU so current_while_timer popped */
21257c478bd9Sstevel@tonic-gate 			break;
21267c478bd9Sstevel@tonic-gate 
2127d62bc4baSyz 		AGGR_LACP_DBG(("lacp_receive_sm: (%d): LACPDU received:\n",
2128d62bc4baSyz 		    portp->lp_linkid));
21297c478bd9Sstevel@tonic-gate 
21307c478bd9Sstevel@tonic-gate 		/*
21317c478bd9Sstevel@tonic-gate 		 * Validate Actor_Information_Length,
21327c478bd9Sstevel@tonic-gate 		 * Partner_Information_Length, Collector_Information_Length,
21337c478bd9Sstevel@tonic-gate 		 * and Terminator_Length fields.
21347c478bd9Sstevel@tonic-gate 		 */
21357c478bd9Sstevel@tonic-gate 		if (!valid_lacp_pdu(portp, lacp)) {
2136d62bc4baSyz 			AGGR_LACP_DBG(("lacp_receive_sm (%d): "
21377c478bd9Sstevel@tonic-gate 			    "Invalid LACPDU received\n",
2138d62bc4baSyz 			    portp->lp_linkid));
21397c478bd9Sstevel@tonic-gate 			break;
21407c478bd9Sstevel@tonic-gate 		}
21417c478bd9Sstevel@tonic-gate 
21427c478bd9Sstevel@tonic-gate 		save_activity = pl->PartnerOperPortState.bit.activity;
21437c478bd9Sstevel@tonic-gate 		selected_updated = update_selected(portp, lacp);
21447c478bd9Sstevel@tonic-gate 		update_NTT(portp, lacp);
21457c478bd9Sstevel@tonic-gate 		sync_updated = record_PDU(portp, lacp);
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 		if (selected_updated) {
21507c478bd9Sstevel@tonic-gate 			lacp_selection_logic(portp);
21517c478bd9Sstevel@tonic-gate 			lacp_mux_sm(portp);
21527c478bd9Sstevel@tonic-gate 		} else if (sync_updated) {
21537c478bd9Sstevel@tonic-gate 			lacp_mux_sm(portp);
21547c478bd9Sstevel@tonic-gate 		}
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 		/*
21577c478bd9Sstevel@tonic-gate 		 * If the periodic timer value bit has been modified
21587c478bd9Sstevel@tonic-gate 		 * or the partner activity bit has been changed then
21597c478bd9Sstevel@tonic-gate 		 * we need to respectively:
21607c478bd9Sstevel@tonic-gate 		 *  - restart the timer with the proper timeout value.
21617c478bd9Sstevel@tonic-gate 		 *  - possibly enable/disable transmission of LACPDUs.
21627c478bd9Sstevel@tonic-gate 		 */
21637c478bd9Sstevel@tonic-gate 		if ((pl->PartnerOperPortState.bit.timeout &&
21647c478bd9Sstevel@tonic-gate 		    (pl->periodic_timer.val != FAST_PERIODIC_TIME)) ||
21657c478bd9Sstevel@tonic-gate 		    (!pl->PartnerOperPortState.bit.timeout &&
21667c478bd9Sstevel@tonic-gate 		    (pl->periodic_timer.val != SLOW_PERIODIC_TIME)) ||
21677c478bd9Sstevel@tonic-gate 		    (pl->PartnerOperPortState.bit.activity !=
21687c478bd9Sstevel@tonic-gate 		    save_activity)) {
21697c478bd9Sstevel@tonic-gate 			lacp_periodic_sm(portp);
21707c478bd9Sstevel@tonic-gate 		}
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 		stop_current_while_timer(portp);
21737c478bd9Sstevel@tonic-gate 		/* Check if we need to transmit an LACPDU */
21747c478bd9Sstevel@tonic-gate 		if (pl->NTT)
21757c478bd9Sstevel@tonic-gate 			lacp_xmit_sm(portp);
21767c478bd9Sstevel@tonic-gate 		start_current_while_timer(portp, 0);
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 		break;
21797c478bd9Sstevel@tonic-gate 	}
21807c478bd9Sstevel@tonic-gate }
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate static void
aggr_set_coll_dist(aggr_port_t * portp,boolean_t enable)21837c478bd9Sstevel@tonic-gate aggr_set_coll_dist(aggr_port_t *portp, boolean_t enable)
21847c478bd9Sstevel@tonic-gate {
2185da14cebeSEric Cheng 	mac_perim_handle_t mph;
21867c478bd9Sstevel@tonic-gate 
2187d62bc4baSyz 	AGGR_LACP_DBG(("AGGR_SET_COLL_DIST_TYPE: (%d) %s\n",
2188d62bc4baSyz 	    portp->lp_linkid, enable ? "ENABLED" : "DISABLED"));
21897c478bd9Sstevel@tonic-gate 
2190da14cebeSEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
21917c478bd9Sstevel@tonic-gate 	if (!enable) {
21927c478bd9Sstevel@tonic-gate 		/*
21937c478bd9Sstevel@tonic-gate 		 * Turn OFF Collector_Distributor.
21947c478bd9Sstevel@tonic-gate 		 */
21957c478bd9Sstevel@tonic-gate 		portp->lp_collector_enabled = B_FALSE;
21967c478bd9Sstevel@tonic-gate 		aggr_send_port_disable(portp);
2197da14cebeSEric Cheng 		goto done;
21987c478bd9Sstevel@tonic-gate 	}
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 	/*
22017c478bd9Sstevel@tonic-gate 	 * Turn ON Collector_Distributor.
22027c478bd9Sstevel@tonic-gate 	 */
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 	if (!portp->lp_lacp.sm.lacp_on || (portp->lp_lacp.sm.lacp_on &&
22057c478bd9Sstevel@tonic-gate 	    (portp->lp_lacp.sm.mux_state == LACP_COLLECTING_DISTRIBUTING))) {
22067c478bd9Sstevel@tonic-gate 		/* Port is compatible and can be aggregated */
22077c478bd9Sstevel@tonic-gate 		portp->lp_collector_enabled = B_TRUE;
22087c478bd9Sstevel@tonic-gate 		aggr_send_port_enable(portp);
22097c478bd9Sstevel@tonic-gate 	}
2210da14cebeSEric Cheng 
2211da14cebeSEric Cheng done:
2212da14cebeSEric Cheng 	mac_perim_exit(mph);
22137c478bd9Sstevel@tonic-gate }
22147c478bd9Sstevel@tonic-gate 
22157c478bd9Sstevel@tonic-gate /*
2216da14cebeSEric Cheng  * Because the LACP packet processing needs to enter the aggr's mac perimeter
2217da14cebeSEric Cheng  * and that would potentially cause a deadlock with the thread in which the
2218da14cebeSEric Cheng  * grp/port is deleted, we defer the packet process to a worker thread. Here
2219da14cebeSEric Cheng  * we only enqueue the received Marker or LACPDU for later processing.
22207c478bd9Sstevel@tonic-gate  */
22217c478bd9Sstevel@tonic-gate void
aggr_lacp_rx_enqueue(aggr_port_t * portp,mblk_t * dmp)2222da14cebeSEric Cheng aggr_lacp_rx_enqueue(aggr_port_t *portp, mblk_t *dmp)
22237c478bd9Sstevel@tonic-gate {
2224da14cebeSEric Cheng 	aggr_grp_t *grp = portp->lp_grp;
22257c478bd9Sstevel@tonic-gate 	lacp_t	*lacp;
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 	dmp->b_rptr += sizeof (struct ether_header);
22287c478bd9Sstevel@tonic-gate 
22297c478bd9Sstevel@tonic-gate 	if (MBLKL(dmp) < sizeof (lacp_t)) {
22307c478bd9Sstevel@tonic-gate 		freemsg(dmp);
22317c478bd9Sstevel@tonic-gate 		return;
22327c478bd9Sstevel@tonic-gate 	}
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	lacp = (lacp_t *)dmp->b_rptr;
2235da14cebeSEric Cheng 	if (lacp->subtype != LACP_SUBTYPE && lacp->subtype != MARKER_SUBTYPE) {
2236da14cebeSEric Cheng 		AGGR_LACP_DBG(("aggr_lacp_rx_enqueue: (%d): "
2237da14cebeSEric Cheng 		    "Unknown Slow Protocol type %d\n",
2238da14cebeSEric Cheng 		    portp->lp_linkid, lacp->subtype));
2239da14cebeSEric Cheng 		freemsg(dmp);
2240da14cebeSEric Cheng 		return;
2241da14cebeSEric Cheng 	}
2242da14cebeSEric Cheng 
2243da14cebeSEric Cheng 	mutex_enter(&grp->lg_lacp_lock);
2244da14cebeSEric Cheng 
2245da14cebeSEric Cheng 	/*
2246da14cebeSEric Cheng 	 * If the lg_lacp_done is set, this aggregation is in the process of
2247da14cebeSEric Cheng 	 * being deleted, return directly.
2248da14cebeSEric Cheng 	 */
2249da14cebeSEric Cheng 	if (grp->lg_lacp_done) {
2250da14cebeSEric Cheng 		mutex_exit(&grp->lg_lacp_lock);
2251da14cebeSEric Cheng 		freemsg(dmp);
2252da14cebeSEric Cheng 		return;
2253da14cebeSEric Cheng 	}
2254da14cebeSEric Cheng 
2255da14cebeSEric Cheng 	if (grp->lg_lacp_tail == NULL) {
2256da14cebeSEric Cheng 		grp->lg_lacp_head = grp->lg_lacp_tail = dmp;
2257da14cebeSEric Cheng 	} else {
2258da14cebeSEric Cheng 		grp->lg_lacp_tail->b_next = dmp;
2259da14cebeSEric Cheng 		grp->lg_lacp_tail = dmp;
2260da14cebeSEric Cheng 	}
2261da14cebeSEric Cheng 
2262da14cebeSEric Cheng 	/*
2263da14cebeSEric Cheng 	 * Hold a reference of the port so that the port won't be freed when it
2264da14cebeSEric Cheng 	 * is removed from the aggr. The b_prev field is borrowed to save the
2265da14cebeSEric Cheng 	 * port information.
2266da14cebeSEric Cheng 	 */
2267da14cebeSEric Cheng 	AGGR_PORT_REFHOLD(portp);
2268da14cebeSEric Cheng 	dmp->b_prev = (mblk_t *)portp;
2269da14cebeSEric Cheng 	cv_broadcast(&grp->lg_lacp_cv);
2270da14cebeSEric Cheng 	mutex_exit(&grp->lg_lacp_lock);
2271da14cebeSEric Cheng }
22727c478bd9Sstevel@tonic-gate 
2273da14cebeSEric Cheng static void
aggr_lacp_rx(mblk_t * dmp)2274da14cebeSEric Cheng aggr_lacp_rx(mblk_t *dmp)
2275da14cebeSEric Cheng {
2276da14cebeSEric Cheng 	aggr_port_t *portp = (aggr_port_t *)dmp->b_prev;
2277da14cebeSEric Cheng 	mac_perim_handle_t mph;
2278da14cebeSEric Cheng 	lacp_t	*lacp;
2279da14cebeSEric Cheng 
2280da14cebeSEric Cheng 	dmp->b_prev = NULL;
2281da14cebeSEric Cheng 
2282da14cebeSEric Cheng 	mac_perim_enter_by_mh(portp->lp_grp->lg_mh, &mph);
2283da14cebeSEric Cheng 	if (portp->lp_closing)
2284da14cebeSEric Cheng 		goto done;
2285da14cebeSEric Cheng 
2286da14cebeSEric Cheng 	lacp = (lacp_t *)dmp->b_rptr;
22877c478bd9Sstevel@tonic-gate 	switch (lacp->subtype) {
22887c478bd9Sstevel@tonic-gate 	case LACP_SUBTYPE:
2289d62bc4baSyz 		AGGR_LACP_DBG(("aggr_lacp_rx:(%d): LACPDU received.\n",
2290d62bc4baSyz 		    portp->lp_linkid));
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 		if (!portp->lp_lacp.sm.lacp_on) {
22937c478bd9Sstevel@tonic-gate 			break;
22947c478bd9Sstevel@tonic-gate 		}
22957c478bd9Sstevel@tonic-gate 		lacp_receive_sm(portp, lacp);
22967c478bd9Sstevel@tonic-gate 		break;
22977c478bd9Sstevel@tonic-gate 
22987c478bd9Sstevel@tonic-gate 	case MARKER_SUBTYPE:
2299d62bc4baSyz 		AGGR_LACP_DBG(("aggr_lacp_rx:(%d): Marker Packet received.\n",
2300d62bc4baSyz 		    portp->lp_linkid));
23017c478bd9Sstevel@tonic-gate 
2302da14cebeSEric Cheng 		if (receive_marker_pdu(portp, dmp) != 0)
2303da14cebeSEric Cheng 			break;
23047c478bd9Sstevel@tonic-gate 
23050dc2366fSVenugopal Iyer 		/* Send the packet over the first TX ring */
23060dc2366fSVenugopal Iyer 		dmp = mac_hwring_send_priv(portp->lp_mch,
23070dc2366fSVenugopal Iyer 		    portp->lp_tx_rings[0], dmp);
23080dc2366fSVenugopal Iyer 		if (dmp != NULL)
23090dc2366fSVenugopal Iyer 			freemsg(dmp);
2310da14cebeSEric Cheng 		mac_perim_exit(mph);
2311da14cebeSEric Cheng 		AGGR_PORT_REFRELE(portp);
2312da14cebeSEric Cheng 		return;
23137c478bd9Sstevel@tonic-gate 	}
23147c478bd9Sstevel@tonic-gate 
2315da14cebeSEric Cheng done:
2316da14cebeSEric Cheng 	mac_perim_exit(mph);
2317da14cebeSEric Cheng 	AGGR_PORT_REFRELE(portp);
23187c478bd9Sstevel@tonic-gate 	freemsg(dmp);
23197c478bd9Sstevel@tonic-gate }
2320da14cebeSEric Cheng 
2321da14cebeSEric Cheng void
aggr_lacp_rx_thread(void * arg)2322da14cebeSEric Cheng aggr_lacp_rx_thread(void *arg)
2323da14cebeSEric Cheng {
2324da14cebeSEric Cheng 	callb_cpr_t	cprinfo;
2325da14cebeSEric Cheng 	aggr_grp_t	*grp = (aggr_grp_t *)arg;
2326da14cebeSEric Cheng 	aggr_port_t	*port;
2327da14cebeSEric Cheng 	mblk_t		*mp, *nextmp;
2328da14cebeSEric Cheng 
2329da14cebeSEric Cheng 	CALLB_CPR_INIT(&cprinfo, &grp->lg_lacp_lock, callb_generic_cpr,
2330da14cebeSEric Cheng 	    "aggr_lacp_rx_thread");
2331da14cebeSEric Cheng 
2332da14cebeSEric Cheng 	mutex_enter(&grp->lg_lacp_lock);
2333da14cebeSEric Cheng 
2334da14cebeSEric Cheng 	/*
2335da14cebeSEric Cheng 	 * Quit the thread if the grp is deleted.
2336da14cebeSEric Cheng 	 */
2337da14cebeSEric Cheng 	while (!grp->lg_lacp_done) {
2338da14cebeSEric Cheng 		if ((mp = grp->lg_lacp_head) == NULL) {
2339da14cebeSEric Cheng 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
2340da14cebeSEric Cheng 			cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock);
2341da14cebeSEric Cheng 			CALLB_CPR_SAFE_END(&cprinfo, &grp->lg_lacp_lock);
2342da14cebeSEric Cheng 			continue;
2343da14cebeSEric Cheng 		}
2344da14cebeSEric Cheng 
2345da14cebeSEric Cheng 		grp->lg_lacp_head = grp->lg_lacp_tail = NULL;
2346da14cebeSEric Cheng 		mutex_exit(&grp->lg_lacp_lock);
2347da14cebeSEric Cheng 
2348da14cebeSEric Cheng 		while (mp != NULL) {
2349da14cebeSEric Cheng 			nextmp = mp->b_next;
2350da14cebeSEric Cheng 			mp->b_next = NULL;
2351da14cebeSEric Cheng 			aggr_lacp_rx(mp);
2352da14cebeSEric Cheng 			mp = nextmp;
2353da14cebeSEric Cheng 		}
2354da14cebeSEric Cheng 		mutex_enter(&grp->lg_lacp_lock);
2355da14cebeSEric Cheng 	}
2356da14cebeSEric Cheng 
2357da14cebeSEric Cheng 	/*
2358da14cebeSEric Cheng 	 * The grp is being destroyed, simply free all of the LACP messages
2359da14cebeSEric Cheng 	 * left in the queue which did not have the chance to be processed.
2360da14cebeSEric Cheng 	 * We cannot use freemsgchain() here since we need to clear the
2361da14cebeSEric Cheng 	 * b_prev field.
2362da14cebeSEric Cheng 	 */
23632ac91f16Smeem 	for (mp = grp->lg_lacp_head; mp != NULL; mp = nextmp) {
2364da14cebeSEric Cheng 		port = (aggr_port_t *)mp->b_prev;
2365da14cebeSEric Cheng 		AGGR_PORT_REFRELE(port);
2366da14cebeSEric Cheng 		nextmp = mp->b_next;
2367da14cebeSEric Cheng 		mp->b_next = NULL;
2368da14cebeSEric Cheng 		mp->b_prev = NULL;
2369da14cebeSEric Cheng 		freemsg(mp);
2370da14cebeSEric Cheng 	}
2371da14cebeSEric Cheng 
2372da14cebeSEric Cheng 	grp->lg_lacp_head = grp->lg_lacp_tail = NULL;
2373da14cebeSEric Cheng 	grp->lg_lacp_rx_thread = NULL;
2374da14cebeSEric Cheng 	cv_broadcast(&grp->lg_lacp_cv);
2375da14cebeSEric Cheng 	CALLB_CPR_EXIT(&cprinfo);
2376da14cebeSEric Cheng 	thread_exit();
2377da14cebeSEric Cheng }
2378