xref: /onnv-gate/usr/src/uts/common/io/aggr/aggr_lacp.c (revision 12634:09fce1ed6a60)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51537Snd99603  * Common Development and Distribution License (the "License").
61537Snd99603  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12634SPeter.Memishian@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * IEEE 802.3ad Link Aggregation - LACP & Marker Protocol processing.
270Sstevel@tonic-gate  */
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/sysmacros.h>
318275SEric Cheng #include <sys/callb.h>
320Sstevel@tonic-gate #include <sys/conf.h>
330Sstevel@tonic-gate #include <sys/cmn_err.h>
348275SEric Cheng #include <sys/disp.h>
350Sstevel@tonic-gate #include <sys/list.h>
360Sstevel@tonic-gate #include <sys/ksynch.h>
370Sstevel@tonic-gate #include <sys/kmem.h>
380Sstevel@tonic-gate #include <sys/stream.h>
390Sstevel@tonic-gate #include <sys/modctl.h>
400Sstevel@tonic-gate #include <sys/ddi.h>
410Sstevel@tonic-gate #include <sys/sunddi.h>
420Sstevel@tonic-gate #include <sys/atomic.h>
430Sstevel@tonic-gate #include <sys/stat.h>
440Sstevel@tonic-gate #include <sys/byteorder.h>
450Sstevel@tonic-gate #include <sys/strsun.h>
460Sstevel@tonic-gate #include <sys/isa_defs.h>
4711197SRamesh.K@Sun.COM #include <sys/sdt.h>
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <sys/aggr.h>
500Sstevel@tonic-gate #include <sys/aggr_impl.h>
510Sstevel@tonic-gate 
520Sstevel@tonic-gate static struct ether_addr	etherzeroaddr = {
530Sstevel@tonic-gate 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
540Sstevel@tonic-gate };
550Sstevel@tonic-gate 
560Sstevel@tonic-gate /*
570Sstevel@tonic-gate  * Slow_Protocol_Multicast address, as per IEEE 802.3ad spec.
580Sstevel@tonic-gate  */
590Sstevel@tonic-gate static struct ether_addr   slow_multicast_addr = {
600Sstevel@tonic-gate 	0x01, 0x80, 0xc2, 0x00, 0x00, 0x02
610Sstevel@tonic-gate };
620Sstevel@tonic-gate 
630Sstevel@tonic-gate #ifdef DEBUG
640Sstevel@tonic-gate /* LACP state machine debugging support */
650Sstevel@tonic-gate static uint32_t aggr_lacp_debug = 0;
660Sstevel@tonic-gate #define	AGGR_LACP_DBG(x)	if (aggr_lacp_debug) { (void) printf x; }
670Sstevel@tonic-gate #else
680Sstevel@tonic-gate #define	AGGR_LACP_DBG(x)	{}
690Sstevel@tonic-gate #endif /* DEBUG */
700Sstevel@tonic-gate 
710Sstevel@tonic-gate #define	NSECS_PER_SEC   1000000000ll
720Sstevel@tonic-gate 
730Sstevel@tonic-gate /* used by lacp_misconfig_walker() */
740Sstevel@tonic-gate typedef struct lacp_misconfig_check_state_s {
750Sstevel@tonic-gate 	aggr_port_t *cs_portp;
760Sstevel@tonic-gate 	boolean_t cs_found;
770Sstevel@tonic-gate } lacp_misconfig_check_state_t;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate static const char *lacp_receive_str[] = LACP_RECEIVE_STATE_STRINGS;
800Sstevel@tonic-gate static const char *lacp_periodic_str[] = LACP_PERIODIC_STRINGS;
810Sstevel@tonic-gate static const char *lacp_mux_str[] = LACP_MUX_STRINGS;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate static uint16_t lacp_port_priority = 0x1000;
840Sstevel@tonic-gate static uint16_t lacp_system_priority = 0x1000;
850Sstevel@tonic-gate 
861537Snd99603 /*
871537Snd99603  * Maintains a list of all ports in ATTACHED state. This information
881537Snd99603  * is used to detect misconfiguration.
891537Snd99603  */
901537Snd99603 typedef struct lacp_sel_ports {
915895Syz147064 	datalink_id_t sp_grp_linkid;
925895Syz147064 	datalink_id_t sp_linkid;
935508Sseb 	/* Note: sp_partner_system must be 2-byte aligned */
945508Sseb 	struct ether_addr sp_partner_system;
951537Snd99603 	uint32_t sp_partner_key;
961537Snd99603 	struct lacp_sel_ports *sp_next;
971537Snd99603 } lacp_sel_ports_t;
981537Snd99603 
991537Snd99603 static lacp_sel_ports_t *sel_ports = NULL;
1001537Snd99603 static kmutex_t lacp_sel_lock;
1011537Snd99603 
1020Sstevel@tonic-gate static void periodic_timer_pop(void *);
1038275SEric Cheng static void periodic_timer_pop_handler(aggr_port_t *);
1040Sstevel@tonic-gate static void lacp_xmit_sm(aggr_port_t *);
1050Sstevel@tonic-gate static void lacp_periodic_sm(aggr_port_t *);
1060Sstevel@tonic-gate static void fill_lacp_pdu(aggr_port_t *, lacp_t *);
1070Sstevel@tonic-gate static void fill_lacp_ether(aggr_port_t *, struct ether_header *);
1080Sstevel@tonic-gate static void lacp_on(aggr_port_t *);
1090Sstevel@tonic-gate static void lacp_off(aggr_port_t *);
1100Sstevel@tonic-gate static boolean_t valid_lacp_pdu(aggr_port_t *, lacp_t *);
1110Sstevel@tonic-gate static void lacp_receive_sm(aggr_port_t *, lacp_t *);
1120Sstevel@tonic-gate static void aggr_set_coll_dist(aggr_port_t *, boolean_t);
1130Sstevel@tonic-gate static void start_wait_while_timer(aggr_port_t *);
1140Sstevel@tonic-gate static void stop_wait_while_timer(aggr_port_t *);
1150Sstevel@tonic-gate static void lacp_reset_port(aggr_port_t *);
1160Sstevel@tonic-gate static void stop_current_while_timer(aggr_port_t *);
1170Sstevel@tonic-gate static void current_while_timer_pop(void *);
1188275SEric Cheng static void current_while_timer_pop_handler(aggr_port_t *);
1190Sstevel@tonic-gate static void update_default_selected(aggr_port_t *);
1200Sstevel@tonic-gate static boolean_t update_selected(aggr_port_t *, lacp_t *);
1211537Snd99603 static boolean_t lacp_sel_ports_add(aggr_port_t *);
1221537Snd99603 static void lacp_sel_ports_del(aggr_port_t *);
1238275SEric Cheng static void wait_while_timer_pop(void *);
1248275SEric Cheng static void wait_while_timer_pop_handler(aggr_port_t *);
1251537Snd99603 
1261537Snd99603 void
aggr_lacp_init(void)1271537Snd99603 aggr_lacp_init(void)
1281537Snd99603 {
1291537Snd99603 	mutex_init(&lacp_sel_lock, NULL, MUTEX_DEFAULT, NULL);
1301537Snd99603 }
1311537Snd99603 
1321537Snd99603 void
aggr_lacp_fini(void)1331537Snd99603 aggr_lacp_fini(void)
1341537Snd99603 {
1351537Snd99603 	mutex_destroy(&lacp_sel_lock);
1361537Snd99603 }
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /*
1398275SEric Cheng  * The following functions are used for handling LACP timers.
1408275SEric Cheng  *
1418275SEric Cheng  * Note that we cannot fully rely on the aggr's mac perimeter in the timeout
1428275SEric Cheng  * handler routine, otherwise it may cause deadlock with the untimeout() call
1438275SEric Cheng  * which is usually called with the mac perimeter held. Instead, a
1448275SEric Cheng  * lacp_timer_lock mutex is introduced, which protects a bitwise flag
1458275SEric Cheng  * (lacp_timer_bits). This flag is set/cleared by timeout()/stop_timer()
1468275SEric Cheng  * routines and is checked by a dedicated thread, that executes the real
1478275SEric Cheng  * timeout operation.
1488275SEric Cheng  */
1498275SEric Cheng static void
aggr_port_timer_thread(void * arg)1508275SEric Cheng aggr_port_timer_thread(void *arg)
1518275SEric Cheng {
1528275SEric Cheng 	aggr_port_t		*port = arg;
1538275SEric Cheng 	aggr_lacp_port_t	*pl = &port->lp_lacp;
1548275SEric Cheng 	aggr_grp_t		*grp = port->lp_grp;
1558275SEric Cheng 	uint32_t		lacp_timer_bits;
1568275SEric Cheng 	mac_perim_handle_t	mph;
1578275SEric Cheng 	callb_cpr_t		cprinfo;
1588275SEric Cheng 
1598275SEric Cheng 	CALLB_CPR_INIT(&cprinfo, &pl->lacp_timer_lock, callb_generic_cpr,
1608275SEric Cheng 	    "aggr_port_timer_thread");
1618275SEric Cheng 
1628275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
1638275SEric Cheng 
1648275SEric Cheng 	for (;;) {
1658275SEric Cheng 
1668275SEric Cheng 		if ((lacp_timer_bits = pl->lacp_timer_bits) == 0) {
1678275SEric Cheng 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
1688275SEric Cheng 			cv_wait(&pl->lacp_timer_cv, &pl->lacp_timer_lock);
1698275SEric Cheng 			CALLB_CPR_SAFE_END(&cprinfo, &pl->lacp_timer_lock);
1708275SEric Cheng 			continue;
1718275SEric Cheng 		}
1728275SEric Cheng 		pl->lacp_timer_bits = 0;
1738275SEric Cheng 
1748275SEric Cheng 		if (lacp_timer_bits & LACP_THREAD_EXIT)
1758275SEric Cheng 			break;
1768275SEric Cheng 
1778275SEric Cheng 		if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT)
1788275SEric Cheng 			pl->periodic_timer.id = 0;
1798275SEric Cheng 		if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT)
1808275SEric Cheng 			pl->wait_while_timer.id = 0;
1818275SEric Cheng 		if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT)
1828275SEric Cheng 			pl->current_while_timer.id = 0;
1838275SEric Cheng 
1848275SEric Cheng 		mutex_exit(&pl->lacp_timer_lock);
1858275SEric Cheng 
1868275SEric Cheng 		mac_perim_enter_by_mh(grp->lg_mh, &mph);
1878275SEric Cheng 		if (port->lp_closing) {
1888275SEric Cheng 			mac_perim_exit(mph);
1898275SEric Cheng 			mutex_enter(&pl->lacp_timer_lock);
1908275SEric Cheng 			break;
1918275SEric Cheng 		}
1928275SEric Cheng 
1938275SEric Cheng 		if (lacp_timer_bits & LACP_PERIODIC_TIMEOUT)
1948275SEric Cheng 			periodic_timer_pop_handler(port);
1958275SEric Cheng 		if (lacp_timer_bits & LACP_WAIT_WHILE_TIMEOUT)
1968275SEric Cheng 			wait_while_timer_pop_handler(port);
1978275SEric Cheng 		if (lacp_timer_bits & LACP_CURRENT_WHILE_TIMEOUT)
1988275SEric Cheng 			current_while_timer_pop_handler(port);
1998275SEric Cheng 		mac_perim_exit(mph);
2008275SEric Cheng 
2018275SEric Cheng 		mutex_enter(&pl->lacp_timer_lock);
2028275SEric Cheng 		if (pl->lacp_timer_bits & LACP_THREAD_EXIT)
2038275SEric Cheng 			break;
2048275SEric Cheng 	}
2058275SEric Cheng 
2068275SEric Cheng 	pl->lacp_timer_bits = 0;
2078275SEric Cheng 	pl->lacp_timer_thread = NULL;
2088275SEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
2098275SEric Cheng 
2108275SEric Cheng 	/* CALLB_CPR_EXIT drops the lock */
2118275SEric Cheng 	CALLB_CPR_EXIT(&cprinfo);
2128275SEric Cheng 
2138275SEric Cheng 	/*
2148275SEric Cheng 	 * Release the reference of the grp so aggr_grp_delete() can call
2158275SEric Cheng 	 * mac_unregister() safely.
2168275SEric Cheng 	 */
2178275SEric Cheng 	aggr_grp_port_rele(port);
2188275SEric Cheng 	thread_exit();
2198275SEric Cheng }
2208275SEric Cheng 
2218275SEric Cheng /*
2221537Snd99603  * Set the port LACP state to SELECTED. Returns B_FALSE if the operation
2231537Snd99603  * could not be performed due to a memory allocation error, B_TRUE otherwise.
2241537Snd99603  */
2251537Snd99603 static boolean_t
lacp_port_select(aggr_port_t * portp)2261537Snd99603 lacp_port_select(aggr_port_t *portp)
2271537Snd99603 {
2288275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
2291537Snd99603 
2301537Snd99603 	if (!lacp_sel_ports_add(portp))
2311537Snd99603 		return (B_FALSE);
2321537Snd99603 	portp->lp_lacp.sm.selected = AGGR_SELECTED;
2331537Snd99603 	return (B_TRUE);
2341537Snd99603 }
2351537Snd99603 
2361537Snd99603 /*
2371537Snd99603  * Set the port LACP state to UNSELECTED.
2381537Snd99603  */
2391537Snd99603 static void
lacp_port_unselect(aggr_port_t * portp)2401537Snd99603 lacp_port_unselect(aggr_port_t *portp)
2411537Snd99603 {
2428275SEric Cheng 	aggr_grp_t	*grp = portp->lp_grp;
2438275SEric Cheng 
2448275SEric Cheng 	ASSERT((grp->lg_mh == NULL) || MAC_PERIM_HELD(grp->lg_mh));
2451537Snd99603 
2461537Snd99603 	lacp_sel_ports_del(portp);
2471537Snd99603 	portp->lp_lacp.sm.selected = AGGR_UNSELECTED;
2481537Snd99603 }
2491537Snd99603 
2501537Snd99603 /*
2510Sstevel@tonic-gate  * Initialize group specific LACP state and parameters.
2520Sstevel@tonic-gate  */
2530Sstevel@tonic-gate void
aggr_lacp_init_grp(aggr_grp_t * aggrp)2540Sstevel@tonic-gate aggr_lacp_init_grp(aggr_grp_t *aggrp)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate 	aggrp->aggr.PeriodicTimer = AGGR_LACP_TIMER_SHORT;
2570Sstevel@tonic-gate 	aggrp->aggr.ActorSystemPriority = (uint16_t)lacp_system_priority;
2580Sstevel@tonic-gate 	aggrp->aggr.CollectorMaxDelay = 10;
2590Sstevel@tonic-gate 	aggrp->lg_lacp_mode = AGGR_LACP_OFF;
2600Sstevel@tonic-gate 	aggrp->aggr.ready = B_FALSE;
2610Sstevel@tonic-gate }
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate /*
2640Sstevel@tonic-gate  * Complete LACP info initialization at port creation time.
2650Sstevel@tonic-gate  */
2660Sstevel@tonic-gate void
aggr_lacp_init_port(aggr_port_t * portp)2670Sstevel@tonic-gate aggr_lacp_init_port(aggr_port_t *portp)
2680Sstevel@tonic-gate {
2690Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
2700Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
2710Sstevel@tonic-gate 
2728275SEric Cheng 	ASSERT(aggrp->lg_mh == NULL || MAC_PERIM_HELD(aggrp->lg_mh));
2738275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	/* actor port # */
2765102Syz147064 	pl->ActorPortNumber = portp->lp_portid;
2775895Syz147064 	AGGR_LACP_DBG(("aggr_lacp_init_port(%d): "
2785895Syz147064 	    "ActorPortNumber = 0x%x\n", portp->lp_linkid,
2792311Sseb 	    pl->ActorPortNumber));
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	pl->ActorPortPriority = (uint16_t)lacp_port_priority;
2820Sstevel@tonic-gate 	pl->ActorPortAggrId = 0;	/* aggregator id - not used */
2830Sstevel@tonic-gate 	pl->NTT = B_FALSE;			/* need to transmit */
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	pl->ActorAdminPortKey = aggrp->lg_key;
2860Sstevel@tonic-gate 	pl->ActorOperPortKey = pl->ActorAdminPortKey;
2875895Syz147064 	AGGR_LACP_DBG(("aggr_lacp_init_port(%d) "
2880Sstevel@tonic-gate 	    "ActorAdminPortKey = 0x%x, ActorAdminPortKey = 0x%x\n",
2895895Syz147064 	    portp->lp_linkid, pl->ActorAdminPortKey, pl->ActorOperPortKey));
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/* Actor admin. port state */
2920Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.activity = B_FALSE;
2930Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.timeout = B_TRUE;
2940Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.aggregation = B_TRUE;
2950Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.sync = B_FALSE;
2960Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.collecting = B_FALSE;
2970Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.distributing = B_FALSE;
2980Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.defaulted = B_FALSE;
2990Sstevel@tonic-gate 	pl->ActorAdminPortState.bit.expired = B_FALSE;
3000Sstevel@tonic-gate 	pl->ActorOperPortState = pl->ActorAdminPortState;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/*
3030Sstevel@tonic-gate 	 * Partner Administrative Information
3040Sstevel@tonic-gate 	 * (All initialized to zero except for the following)
3050Sstevel@tonic-gate 	 * Fast Timeouts.
3060Sstevel@tonic-gate 	 */
3070Sstevel@tonic-gate 	pl->PartnerAdminPortState.bit.timeout =
3080Sstevel@tonic-gate 	    pl->PartnerOperPortState.bit.timeout = B_TRUE;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	/*
3130Sstevel@tonic-gate 	 * State machine information.
3140Sstevel@tonic-gate 	 */
3150Sstevel@tonic-gate 	pl->sm.lacp_on = B_FALSE;		/* LACP Off default */
3160Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;		/* Prevents transmissions */
3170Sstevel@tonic-gate 	pl->sm.lacp_enabled = B_FALSE;
3180Sstevel@tonic-gate 	pl->sm.port_enabled = B_FALSE;		/* Link Down */
3190Sstevel@tonic-gate 	pl->sm.actor_churn = B_FALSE;
3200Sstevel@tonic-gate 	pl->sm.partner_churn = B_FALSE;
3210Sstevel@tonic-gate 	pl->sm.ready_n = B_FALSE;
3220Sstevel@tonic-gate 	pl->sm.port_moved = B_FALSE;
3230Sstevel@tonic-gate 
3241537Snd99603 	lacp_port_unselect(portp);
3251537Snd99603 
3260Sstevel@tonic-gate 	pl->sm.periodic_state = LACP_NO_PERIODIC;
3270Sstevel@tonic-gate 	pl->sm.receive_state = LACP_INITIALIZE;
3280Sstevel@tonic-gate 	pl->sm.mux_state = LACP_DETACHED;
3290Sstevel@tonic-gate 	pl->sm.churn_state = LACP_NO_ACTOR_CHURN;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	/*
3320Sstevel@tonic-gate 	 * Timer information.
3330Sstevel@tonic-gate 	 */
3340Sstevel@tonic-gate 	pl->current_while_timer.id = 0;
3350Sstevel@tonic-gate 	pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	pl->periodic_timer.id = 0;
3380Sstevel@tonic-gate 	pl->periodic_timer.val = FAST_PERIODIC_TIME;
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	pl->wait_while_timer.id = 0;
3410Sstevel@tonic-gate 	pl->wait_while_timer.val = AGGREGATE_WAIT_TIME;
3428275SEric Cheng 
3438275SEric Cheng 	pl->lacp_timer_bits = 0;
3448275SEric Cheng 
3458275SEric Cheng 	mutex_init(&pl->lacp_timer_lock, NULL, MUTEX_DRIVER, NULL);
3468275SEric Cheng 	cv_init(&pl->lacp_timer_cv, NULL, CV_DRIVER, NULL);
3478275SEric Cheng 
3488275SEric Cheng 	pl->lacp_timer_thread = thread_create(NULL, 0, aggr_port_timer_thread,
3498275SEric Cheng 	    portp, 0, &p0, TS_RUN, minclsyspri);
3508275SEric Cheng 
3518275SEric Cheng 	/*
3528275SEric Cheng 	 * Hold a reference of the grp and the port and this reference will
3538275SEric Cheng 	 * be release when the thread exits.
3548275SEric Cheng 	 *
3558275SEric Cheng 	 * The reference on the port is used for aggr_port_delete() to
3568275SEric Cheng 	 * continue without waiting for the thread to exit; the reference
3578275SEric Cheng 	 * on the grp is used for aggr_grp_delete() to wait for the thread
3588275SEric Cheng 	 * to exit before calling mac_unregister().
3598275SEric Cheng 	 */
3608275SEric Cheng 	aggr_grp_port_hold(portp);
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate  * Port initialization when we need to
3650Sstevel@tonic-gate  * turn LACP on/off, etc. Not everything is
3660Sstevel@tonic-gate  * reset like in the above routine.
3670Sstevel@tonic-gate  *		Do NOT modify things like link status.
3680Sstevel@tonic-gate  */
3690Sstevel@tonic-gate static void
lacp_reset_port(aggr_port_t * portp)3700Sstevel@tonic-gate lacp_reset_port(aggr_port_t *portp)
3710Sstevel@tonic-gate {
3720Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
3730Sstevel@tonic-gate 
3748275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	pl->NTT = B_FALSE;			/* need to transmit */
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 	/* reset operational port state */
3790Sstevel@tonic-gate 	pl->ActorOperPortState.bit.timeout =
3805102Syz147064 	    pl->ActorAdminPortState.bit.timeout;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	pl->ActorOperPortState.bit.sync = B_FALSE;
3830Sstevel@tonic-gate 	pl->ActorOperPortState.bit.collecting = B_FALSE;
3840Sstevel@tonic-gate 	pl->ActorOperPortState.bit.distributing = B_FALSE;
3850Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_TRUE;
3860Sstevel@tonic-gate 	pl->ActorOperPortState.bit.expired = B_FALSE;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	pl->PartnerOperPortState.bit.timeout = B_TRUE;	/* fast t/o */
3890Sstevel@tonic-gate 	pl->PartnerCollectorMaxDelay = 0; /* tens of microseconds */
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	/*
3920Sstevel@tonic-gate 	 * State machine information.
3930Sstevel@tonic-gate 	 */
3940Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;		/* Prevents transmissions */
3950Sstevel@tonic-gate 	pl->sm.actor_churn = B_FALSE;
3960Sstevel@tonic-gate 	pl->sm.partner_churn = B_FALSE;
3970Sstevel@tonic-gate 	pl->sm.ready_n = B_FALSE;
3981537Snd99603 
3991537Snd99603 	lacp_port_unselect(portp);
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	pl->sm.periodic_state = LACP_NO_PERIODIC;
4020Sstevel@tonic-gate 	pl->sm.receive_state = LACP_INITIALIZE;
4030Sstevel@tonic-gate 	pl->sm.mux_state = LACP_DETACHED;
4040Sstevel@tonic-gate 	pl->sm.churn_state = LACP_NO_ACTOR_CHURN;
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	/*
4070Sstevel@tonic-gate 	 * Timer information.
4080Sstevel@tonic-gate 	 */
4090Sstevel@tonic-gate 	pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
4100Sstevel@tonic-gate 	pl->periodic_timer.val = FAST_PERIODIC_TIME;
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate static void
aggr_lacp_mcast_on(aggr_port_t * port)4140Sstevel@tonic-gate aggr_lacp_mcast_on(aggr_port_t *port)
4150Sstevel@tonic-gate {
4168275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
4178275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
4200Sstevel@tonic-gate 		return;
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	(void) aggr_port_multicst(port, B_TRUE,
4230Sstevel@tonic-gate 	    (uchar_t *)&slow_multicast_addr);
4240Sstevel@tonic-gate }
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate static void
aggr_lacp_mcast_off(aggr_port_t * port)4270Sstevel@tonic-gate aggr_lacp_mcast_off(aggr_port_t *port)
4280Sstevel@tonic-gate {
4298275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
4308275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
4330Sstevel@tonic-gate 		return;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	(void) aggr_port_multicst(port, B_FALSE,
4360Sstevel@tonic-gate 	    (uchar_t *)&slow_multicast_addr);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate static void
start_periodic_timer(aggr_port_t * portp)4400Sstevel@tonic-gate start_periodic_timer(aggr_port_t *portp)
4410Sstevel@tonic-gate {
4428275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
4438275SEric Cheng 
4448275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
4450Sstevel@tonic-gate 
4468275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
4478275SEric Cheng 	if (pl->periodic_timer.id == 0) {
4488275SEric Cheng 		pl->periodic_timer.id = timeout(periodic_timer_pop, portp,
4490Sstevel@tonic-gate 		    drv_usectohz(1000000 * portp->lp_lacp.periodic_timer.val));
4500Sstevel@tonic-gate 	}
4518275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
4520Sstevel@tonic-gate }
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate static void
stop_periodic_timer(aggr_port_t * portp)4550Sstevel@tonic-gate stop_periodic_timer(aggr_port_t *portp)
4560Sstevel@tonic-gate {
4578275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
4588275SEric Cheng 	timeout_id_t id;
4598275SEric Cheng 
4608275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
4610Sstevel@tonic-gate 
4628275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
4638275SEric Cheng 	if ((id = pl->periodic_timer.id) != 0) {
4648275SEric Cheng 		pl->lacp_timer_bits &= ~LACP_PERIODIC_TIMEOUT;
4658275SEric Cheng 		pl->periodic_timer.id = 0;
4660Sstevel@tonic-gate 	}
4678275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
4688275SEric Cheng 
4698275SEric Cheng 	if (id != 0)
4708275SEric Cheng 		(void) untimeout(id);
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate /*
4740Sstevel@tonic-gate  * When the timer pops, we arrive here to
4750Sstevel@tonic-gate  * clear out LACPDU count as well as transmit an
4760Sstevel@tonic-gate  * LACPDU. We then set the periodic state and let
4770Sstevel@tonic-gate  * the periodic state machine restart the timer.
4780Sstevel@tonic-gate  */
4790Sstevel@tonic-gate static void
periodic_timer_pop(void * data)4808275SEric Cheng periodic_timer_pop(void *data)
4810Sstevel@tonic-gate {
4828275SEric Cheng 	aggr_port_t *portp = data;
4838275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
4848275SEric Cheng 
4858275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
4868275SEric Cheng 	pl->lacp_timer_bits |= LACP_PERIODIC_TIMEOUT;
4878275SEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
4888275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
4898275SEric Cheng }
4900Sstevel@tonic-gate 
4918275SEric Cheng /*
4928275SEric Cheng  * When the timer pops, we arrive here to
4938275SEric Cheng  * clear out LACPDU count as well as transmit an
4948275SEric Cheng  * LACPDU. We then set the periodic state and let
4958275SEric Cheng  * the periodic state machine restart the timer.
4968275SEric Cheng  */
4978275SEric Cheng static void
periodic_timer_pop_handler(aggr_port_t * portp)4988275SEric Cheng periodic_timer_pop_handler(aggr_port_t *portp)
4998275SEric Cheng {
5008275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
5018275SEric Cheng 
5020Sstevel@tonic-gate 	portp->lp_lacp_stats.LACPDUsTx = 0;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	/* current timestamp */
5050Sstevel@tonic-gate 	portp->lp_lacp.time = gethrtime();
5060Sstevel@tonic-gate 	portp->lp_lacp.NTT = B_TRUE;
5070Sstevel@tonic-gate 	lacp_xmit_sm(portp);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	/*
5100Sstevel@tonic-gate 	 * Set Periodic State machine state based on the
5110Sstevel@tonic-gate 	 * value of the Partner Operation Port State timeout
5120Sstevel@tonic-gate 	 * bit.
5130Sstevel@tonic-gate 	 */
5140Sstevel@tonic-gate 	if (portp->lp_lacp.PartnerOperPortState.bit.timeout) {
5150Sstevel@tonic-gate 		portp->lp_lacp.periodic_timer.val = FAST_PERIODIC_TIME;
5160Sstevel@tonic-gate 		portp->lp_lacp.sm.periodic_state = LACP_FAST_PERIODIC;
5170Sstevel@tonic-gate 	} else {
5180Sstevel@tonic-gate 		portp->lp_lacp.periodic_timer.val = SLOW_PERIODIC_TIME;
5190Sstevel@tonic-gate 		portp->lp_lacp.sm.periodic_state = LACP_SLOW_PERIODIC;
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	lacp_periodic_sm(portp);
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate /*
5260Sstevel@tonic-gate  * Invoked from:
5270Sstevel@tonic-gate  *	- startup upon aggregation
5280Sstevel@tonic-gate  *	- when the periodic timer pops
5290Sstevel@tonic-gate  *	- when the periodic timer value is changed
5300Sstevel@tonic-gate  *	- when the port is attached or detached
5310Sstevel@tonic-gate  *	- when LACP mode is changed.
5320Sstevel@tonic-gate  */
5330Sstevel@tonic-gate static void
lacp_periodic_sm(aggr_port_t * portp)5340Sstevel@tonic-gate lacp_periodic_sm(aggr_port_t *portp)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate 	lacp_periodic_state_t oldstate = portp->lp_lacp.sm.periodic_state;
5370Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
5380Sstevel@tonic-gate 
5398275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
5420Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
5430Sstevel@tonic-gate 		/* Stop timer whether it is running or not */
5440Sstevel@tonic-gate 		stop_periodic_timer(portp);
5450Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_NO_PERIODIC;
5460Sstevel@tonic-gate 		pl->NTT = B_FALSE;
5475895Syz147064 		AGGR_LACP_DBG(("lacp_periodic_sm(%d):NO LACP "
5485895Syz147064 		    "%s--->%s\n", portp->lp_linkid,
5490Sstevel@tonic-gate 		    lacp_periodic_str[oldstate],
5500Sstevel@tonic-gate 		    lacp_periodic_str[pl->sm.periodic_state]));
5510Sstevel@tonic-gate 		return;
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled ||
5550Sstevel@tonic-gate 	    !pl->sm.port_enabled ||
5560Sstevel@tonic-gate 	    !pl->ActorOperPortState.bit.activity &&
5570Sstevel@tonic-gate 	    !pl->PartnerOperPortState.bit.activity) {
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 		/* Stop timer whether it is running or not */
5600Sstevel@tonic-gate 		stop_periodic_timer(portp);
5610Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_NO_PERIODIC;
5620Sstevel@tonic-gate 		pl->NTT = B_FALSE;
5635895Syz147064 		AGGR_LACP_DBG(("lacp_periodic_sm(%d):STOP %s--->%s\n",
5645895Syz147064 		    portp->lp_linkid, lacp_periodic_str[oldstate],
5650Sstevel@tonic-gate 		    lacp_periodic_str[pl->sm.periodic_state]));
5660Sstevel@tonic-gate 		return;
5670Sstevel@tonic-gate 	}
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	/*
5700Sstevel@tonic-gate 	 * Startup with FAST_PERIODIC_TIME if no previous LACPDU
5710Sstevel@tonic-gate 	 * has been received. Then after we timeout, then it is
5720Sstevel@tonic-gate 	 * possible to go to SLOW_PERIODIC_TIME.
5730Sstevel@tonic-gate 	 */
5740Sstevel@tonic-gate 	if (pl->sm.periodic_state == LACP_NO_PERIODIC) {
5750Sstevel@tonic-gate 		pl->periodic_timer.val = FAST_PERIODIC_TIME;
5760Sstevel@tonic-gate 		pl->sm.periodic_state = LACP_FAST_PERIODIC;
5770Sstevel@tonic-gate 	} else if ((pl->sm.periodic_state == LACP_SLOW_PERIODIC) &&
5780Sstevel@tonic-gate 	    pl->PartnerOperPortState.bit.timeout) {
5790Sstevel@tonic-gate 		/*
5800Sstevel@tonic-gate 		 * If we receive a bit indicating we are going to
5810Sstevel@tonic-gate 		 * fast periodic from slow periodic, stop the timer
5820Sstevel@tonic-gate 		 * and let the periodic_timer_pop routine deal
5830Sstevel@tonic-gate 		 * with reseting the periodic state and transmitting
5840Sstevel@tonic-gate 		 * a LACPDU.
5850Sstevel@tonic-gate 		 */
5860Sstevel@tonic-gate 		stop_periodic_timer(portp);
5878275SEric Cheng 		periodic_timer_pop_handler(portp);
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	/* Rearm timer with value provided by partner */
5910Sstevel@tonic-gate 	start_periodic_timer(portp);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate /*
5950Sstevel@tonic-gate  * This routine transmits an LACPDU if lacp_enabled
5960Sstevel@tonic-gate  * is TRUE and if NTT is set.
5970Sstevel@tonic-gate  */
5980Sstevel@tonic-gate static void
lacp_xmit_sm(aggr_port_t * portp)5990Sstevel@tonic-gate lacp_xmit_sm(aggr_port_t *portp)
6000Sstevel@tonic-gate {
6010Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
6020Sstevel@tonic-gate 	size_t	len;
6030Sstevel@tonic-gate 	mblk_t  *mp;
6040Sstevel@tonic-gate 	hrtime_t now, elapsed;
6050Sstevel@tonic-gate 
6068275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
6090Sstevel@tonic-gate 	if (!pl->sm.lacp_on || !pl->NTT || !portp->lp_started)
6100Sstevel@tonic-gate 		return;
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	/*
6130Sstevel@tonic-gate 	 * Do nothing if LACP has been turned off or if the
6140Sstevel@tonic-gate 	 * periodic state machine is not enabled.
6150Sstevel@tonic-gate 	 */
6160Sstevel@tonic-gate 	if ((pl->sm.periodic_state == LACP_NO_PERIODIC) ||
6170Sstevel@tonic-gate 	    !pl->sm.lacp_enabled || pl->sm.begin) {
6180Sstevel@tonic-gate 		pl->NTT = B_FALSE;
6190Sstevel@tonic-gate 		return;
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	/*
6230Sstevel@tonic-gate 	 * If we have sent 5 Slow packets in the last second, avoid
6240Sstevel@tonic-gate 	 * sending any more here. No more than three LACPDUs may be transmitted
6250Sstevel@tonic-gate 	 * in any Fast_Periodic_Time interval.
6260Sstevel@tonic-gate 	 */
6270Sstevel@tonic-gate 	if (portp->lp_lacp_stats.LACPDUsTx >= 3) {
6280Sstevel@tonic-gate 		/*
6290Sstevel@tonic-gate 		 * Grab the current time value and see if
6300Sstevel@tonic-gate 		 * more than 1 second has passed. If so,
6310Sstevel@tonic-gate 		 * reset the timestamp and clear the count.
6320Sstevel@tonic-gate 		 */
6330Sstevel@tonic-gate 		now = gethrtime();
6340Sstevel@tonic-gate 		elapsed = now - pl->time;
6350Sstevel@tonic-gate 		if (elapsed > NSECS_PER_SEC) {
6360Sstevel@tonic-gate 			portp->lp_lacp_stats.LACPDUsTx = 0;
6370Sstevel@tonic-gate 			pl->time = now;
6380Sstevel@tonic-gate 		} else {
6390Sstevel@tonic-gate 			return;
6400Sstevel@tonic-gate 		}
6410Sstevel@tonic-gate 	}
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	len = sizeof (lacp_t) + sizeof (struct ether_header);
6440Sstevel@tonic-gate 	mp = allocb(len, BPRI_MED);
6450Sstevel@tonic-gate 	if (mp == NULL)
6460Sstevel@tonic-gate 		return;
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + len;
6490Sstevel@tonic-gate 	bzero(mp->b_rptr, len);
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr);
6520Sstevel@tonic-gate 	fill_lacp_pdu(portp,
6530Sstevel@tonic-gate 	    (lacp_t *)(mp->b_rptr + sizeof (struct ether_header)));
6540Sstevel@tonic-gate 
65511878SVenu.Iyer@Sun.COM 	/* Send the packet over the first TX ring */
65611878SVenu.Iyer@Sun.COM 	mp = mac_hwring_send_priv(portp->lp_mch, portp->lp_tx_rings[0], mp);
65711878SVenu.Iyer@Sun.COM 	if (mp != NULL)
65811878SVenu.Iyer@Sun.COM 		freemsg(mp);
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	pl->NTT = B_FALSE;
6610Sstevel@tonic-gate 	portp->lp_lacp_stats.LACPDUsTx++;
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate /*
6650Sstevel@tonic-gate  * Initialize the ethernet header of a LACP packet sent from the specified
6660Sstevel@tonic-gate  * port.
6670Sstevel@tonic-gate  */
6680Sstevel@tonic-gate static void
fill_lacp_ether(aggr_port_t * port,struct ether_header * ether)6690Sstevel@tonic-gate fill_lacp_ether(aggr_port_t *port, struct ether_header *ether)
6700Sstevel@tonic-gate {
6710Sstevel@tonic-gate 	bcopy(port->lp_addr, (uint8_t *)&(ether->ether_shost), ETHERADDRL);
6720Sstevel@tonic-gate 	bcopy(&slow_multicast_addr, (uint8_t *)&(ether->ether_dhost),
6730Sstevel@tonic-gate 	    ETHERADDRL);
6740Sstevel@tonic-gate 	ether->ether_type = htons(ETHERTYPE_SLOW);
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate static void
fill_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)6780Sstevel@tonic-gate fill_lacp_pdu(aggr_port_t *portp, lacp_t *lacp)
6790Sstevel@tonic-gate {
6800Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
6810Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
6828275SEric Cheng 	mac_perim_handle_t pmph;
6830Sstevel@tonic-gate 
6848275SEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
6858275SEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &pmph);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	lacp->subtype = LACP_SUBTYPE;
6880Sstevel@tonic-gate 	lacp->version = LACP_VERSION;
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	/*
6910Sstevel@tonic-gate 	 * Actor Information
6920Sstevel@tonic-gate 	 */
6930Sstevel@tonic-gate 	lacp->actor_info.tlv_type = ACTOR_TLV;
6940Sstevel@tonic-gate 	lacp->actor_info.information_len = sizeof (link_info_t);
6950Sstevel@tonic-gate 	lacp->actor_info.system_priority =
6960Sstevel@tonic-gate 	    htons(aggrp->aggr.ActorSystemPriority);
6970Sstevel@tonic-gate 	bcopy(aggrp->lg_addr, (uchar_t *)&lacp->actor_info.system_id,
6980Sstevel@tonic-gate 	    ETHERADDRL);
6990Sstevel@tonic-gate 	lacp->actor_info.key = htons(pl->ActorOperPortKey);
7000Sstevel@tonic-gate 	lacp->actor_info.port_priority = htons(pl->ActorPortPriority);
7010Sstevel@tonic-gate 	lacp->actor_info.port = htons(pl->ActorPortNumber);
7020Sstevel@tonic-gate 	lacp->actor_info.state.state = pl->ActorOperPortState.state;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	/*
7050Sstevel@tonic-gate 	 * Partner Information
7060Sstevel@tonic-gate 	 */
7070Sstevel@tonic-gate 	lacp->partner_info.tlv_type = PARTNER_TLV;
7080Sstevel@tonic-gate 	lacp->partner_info.information_len = sizeof (link_info_t);
7090Sstevel@tonic-gate 	lacp->partner_info.system_priority =
7100Sstevel@tonic-gate 	    htons(pl->PartnerOperSysPriority);
7110Sstevel@tonic-gate 	lacp->partner_info.system_id = pl->PartnerOperSystem;
7120Sstevel@tonic-gate 	lacp->partner_info.key = htons(pl->PartnerOperKey);
7130Sstevel@tonic-gate 	lacp->partner_info.port_priority =
7140Sstevel@tonic-gate 	    htons(pl->PartnerOperPortPriority);
7150Sstevel@tonic-gate 	lacp->partner_info.port = htons(pl->PartnerOperPortNum);
7160Sstevel@tonic-gate 	lacp->partner_info.state.state = pl->PartnerOperPortState.state;
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	/* Collector Information */
7190Sstevel@tonic-gate 	lacp->tlv_collector = COLLECTOR_TLV;
7200Sstevel@tonic-gate 	lacp->collector_len = 0x10;
7210Sstevel@tonic-gate 	lacp->collector_max_delay = htons(aggrp->aggr.CollectorMaxDelay);
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	/* Termination Information */
7240Sstevel@tonic-gate 	lacp->tlv_terminator = TERMINATOR_TLV;
7250Sstevel@tonic-gate 	lacp->terminator_len = 0x0;
7260Sstevel@tonic-gate 
7278275SEric Cheng 	mac_perim_exit(pmph);
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate /*
7310Sstevel@tonic-gate  * lacp_mux_sm - LACP mux state machine
7320Sstevel@tonic-gate  *		This state machine is invoked from:
7330Sstevel@tonic-gate  *			- startup upon aggregation
7340Sstevel@tonic-gate  *			- from the Selection logic
7350Sstevel@tonic-gate  *			- when the wait_while_timer pops
7360Sstevel@tonic-gate  *			- when the aggregation MAC address is changed
7370Sstevel@tonic-gate  *			- when receiving DL_NOTE_LINK_UP/DOWN
7380Sstevel@tonic-gate  *			- when receiving DL_NOTE_AGGR_AVAIL/UNAVAIL
7390Sstevel@tonic-gate  *			- when LACP mode is changed.
7400Sstevel@tonic-gate  *			- when a DL_NOTE_SPEED is received
7410Sstevel@tonic-gate  */
7420Sstevel@tonic-gate static void
lacp_mux_sm(aggr_port_t * portp)7430Sstevel@tonic-gate lacp_mux_sm(aggr_port_t *portp)
7440Sstevel@tonic-gate {
7450Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
7460Sstevel@tonic-gate 	boolean_t NTT_updated = B_FALSE;
7470Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
7480Sstevel@tonic-gate 	lacp_mux_state_t oldstate = pl->sm.mux_state;
7490Sstevel@tonic-gate 
7508275SEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
7530Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
7540Sstevel@tonic-gate 		pl->sm.mux_state = LACP_DETACHED;
7550Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync = B_FALSE;
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
7580Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
7595895Syz147064 			AGGR_LACP_DBG(("trunk link: (%d): "
7600Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
7615895Syz147064 			    portp->lp_linkid));
7620Sstevel@tonic-gate 		}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting =
7650Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing = B_FALSE;
7660Sstevel@tonic-gate 		return;
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled)
7700Sstevel@tonic-gate 		pl->sm.mux_state = LACP_DETACHED;
7710Sstevel@tonic-gate 
7721537Snd99603 again:
7730Sstevel@tonic-gate 	/* determine next state, or return if state unchanged */
7740Sstevel@tonic-gate 	switch (pl->sm.mux_state) {
7750Sstevel@tonic-gate 	case LACP_DETACHED:
7760Sstevel@tonic-gate 		if (pl->sm.begin) {
7770Sstevel@tonic-gate 			break;
7780Sstevel@tonic-gate 		}
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) ||
7810Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY)) {
7820Sstevel@tonic-gate 			pl->sm.mux_state = LACP_WAITING;
7830Sstevel@tonic-gate 			break;
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 		return;
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 	case LACP_WAITING:
7880Sstevel@tonic-gate 		if (pl->sm.selected == AGGR_UNSELECTED) {
7890Sstevel@tonic-gate 			pl->sm.mux_state = LACP_DETACHED;
7900Sstevel@tonic-gate 			break;
7910Sstevel@tonic-gate 		}
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) && aggrp->aggr.ready) {
7940Sstevel@tonic-gate 			pl->sm.mux_state = LACP_ATTACHED;
7950Sstevel@tonic-gate 			break;
7960Sstevel@tonic-gate 		}
7970Sstevel@tonic-gate 		return;
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	case LACP_ATTACHED:
8000Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_UNSELECTED) ||
8010Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY)) {
8020Sstevel@tonic-gate 			pl->sm.mux_state = LACP_DETACHED;
8030Sstevel@tonic-gate 			break;
8040Sstevel@tonic-gate 		}
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_SELECTED) &&
8070Sstevel@tonic-gate 		    pl->PartnerOperPortState.bit.sync) {
8080Sstevel@tonic-gate 			pl->sm.mux_state = LACP_COLLECTING_DISTRIBUTING;
8090Sstevel@tonic-gate 			break;
8100Sstevel@tonic-gate 		}
8110Sstevel@tonic-gate 		return;
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	case LACP_COLLECTING_DISTRIBUTING:
8140Sstevel@tonic-gate 		if ((pl->sm.selected == AGGR_UNSELECTED) ||
8150Sstevel@tonic-gate 		    (pl->sm.selected == AGGR_STANDBY) ||
8160Sstevel@tonic-gate 		    !pl->PartnerOperPortState.bit.sync) {
8170Sstevel@tonic-gate 			pl->sm.mux_state = LACP_ATTACHED;
8180Sstevel@tonic-gate 			break;
8190Sstevel@tonic-gate 		}
8200Sstevel@tonic-gate 		return;
8210Sstevel@tonic-gate 	}
8220Sstevel@tonic-gate 
8235895Syz147064 	AGGR_LACP_DBG(("lacp_mux_sm(%d):%s--->%s\n",
8245895Syz147064 	    portp->lp_linkid, lacp_mux_str[oldstate],
8250Sstevel@tonic-gate 	    lacp_mux_str[pl->sm.mux_state]));
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	/* perform actions on entering a new state */
8280Sstevel@tonic-gate 	switch (pl->sm.mux_state) {
8290Sstevel@tonic-gate 	case LACP_DETACHED:
8300Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
8310Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
8325895Syz147064 			AGGR_LACP_DBG(("trunk link: (%d): "
8330Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
8345895Syz147064 			    portp->lp_linkid));
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync =
8380Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.collecting = B_FALSE;
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 		/* Turn OFF Collector_Distributor */
8410Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_FALSE);
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_FALSE;
8440Sstevel@tonic-gate 		NTT_updated = B_TRUE;
8450Sstevel@tonic-gate 		break;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	case LACP_WAITING:
8480Sstevel@tonic-gate 		start_wait_while_timer(portp);
8490Sstevel@tonic-gate 		break;
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	case LACP_ATTACHED:
8520Sstevel@tonic-gate 		if (pl->ActorOperPortState.bit.collecting ||
8530Sstevel@tonic-gate 		    pl->ActorOperPortState.bit.distributing) {
8545895Syz147064 			AGGR_LACP_DBG(("trunk link: (%d): "
8550Sstevel@tonic-gate 			    "Collector_Distributor Disabled.\n",
8565895Syz147064 			    portp->lp_linkid));
8570Sstevel@tonic-gate 		}
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 		pl->ActorOperPortState.bit.sync = B_TRUE;
8600Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting = B_FALSE;
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 		/* Turn OFF Collector_Distributor */
8630Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_FALSE);
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_FALSE;
8660Sstevel@tonic-gate 		NTT_updated = B_TRUE;
8671537Snd99603 		if (pl->PartnerOperPortState.bit.sync) {
8681537Snd99603 			/*
8691537Snd99603 			 * We had already received an updated sync from
8701537Snd99603 			 * the partner. Attempt to transition to
8711537Snd99603 			 * collecting/distributing now.
8721537Snd99603 			 */
8731537Snd99603 			goto again;
8741537Snd99603 		}
8750Sstevel@tonic-gate 		break;
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	case LACP_COLLECTING_DISTRIBUTING:
8780Sstevel@tonic-gate 		if (!pl->ActorOperPortState.bit.collecting &&
8790Sstevel@tonic-gate 		    !pl->ActorOperPortState.bit.distributing) {
8805895Syz147064 			AGGR_LACP_DBG(("trunk link: (%d): "
8810Sstevel@tonic-gate 			    "Collector_Distributor Enabled.\n",
8825895Syz147064 			    portp->lp_linkid));
8830Sstevel@tonic-gate 		}
8840Sstevel@tonic-gate 		pl->ActorOperPortState.bit.distributing = B_TRUE;
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 		/* Turn Collector_Distributor back ON */
8870Sstevel@tonic-gate 		aggr_set_coll_dist(portp, B_TRUE);
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 		pl->ActorOperPortState.bit.collecting = B_TRUE;
8900Sstevel@tonic-gate 		NTT_updated = B_TRUE;
8910Sstevel@tonic-gate 		break;
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	/*
8950Sstevel@tonic-gate 	 * If we updated the state of the NTT variable, then
8960Sstevel@tonic-gate 	 * initiate a LACPDU transmission.
8970Sstevel@tonic-gate 	 */
8980Sstevel@tonic-gate 	if (NTT_updated) {
8990Sstevel@tonic-gate 		pl->NTT = B_TRUE;
9000Sstevel@tonic-gate 		lacp_xmit_sm(portp);
9010Sstevel@tonic-gate 	}
9020Sstevel@tonic-gate } /* lacp_mux_sm */
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 
9058275SEric Cheng static int
receive_marker_pdu(aggr_port_t * portp,mblk_t * mp)9060Sstevel@tonic-gate receive_marker_pdu(aggr_port_t *portp, mblk_t *mp)
9070Sstevel@tonic-gate {
90856Smeem 	marker_pdu_t		*markerp = (marker_pdu_t *)mp->b_rptr;
9090Sstevel@tonic-gate 
9108275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
9110Sstevel@tonic-gate 
9125895Syz147064 	AGGR_LACP_DBG(("trunk link: (%d): MARKER PDU received:\n",
9135895Syz147064 	    portp->lp_linkid));
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
9160Sstevel@tonic-gate 	if (!portp->lp_lacp.sm.lacp_on)
9178275SEric Cheng 		return (-1);
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (marker_pdu_t))
9208275SEric Cheng 		return (-1);
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	if (markerp->version != MARKER_VERSION) {
9235895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
9240Sstevel@tonic-gate 		    "version = %d does not match s/w version %d\n",
9255895Syz147064 		    portp->lp_linkid, markerp->version, MARKER_VERSION));
9268275SEric Cheng 		return (-1);
9270Sstevel@tonic-gate 	}
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	if (markerp->tlv_marker == MARKER_RESPONSE_TLV) {
9300Sstevel@tonic-gate 		/* We do not yet send out MARKER info PDUs */
9315895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): MARKER RESPONSE PDU: "
9320Sstevel@tonic-gate 		    " MARKER TLV = %d - We don't send out info type!\n",
9335895Syz147064 		    portp->lp_linkid, markerp->tlv_marker));
9348275SEric Cheng 		return (-1);
9350Sstevel@tonic-gate 	}
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	if (markerp->tlv_marker != MARKER_INFO_TLV) {
9385895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
9395895Syz147064 		    " MARKER TLV = %d \n", portp->lp_linkid,
9400Sstevel@tonic-gate 		    markerp->tlv_marker));
9418275SEric Cheng 		return (-1);
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	if (markerp->marker_len != MARKER_INFO_RESPONSE_LENGTH) {
9455895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): Malformed MARKER PDU: "
9465895Syz147064 		    " MARKER length = %d \n", portp->lp_linkid,
9470Sstevel@tonic-gate 		    markerp->marker_len));
9488275SEric Cheng 		return (-1);
9490Sstevel@tonic-gate 	}
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	if (markerp->requestor_port != portp->lp_lacp.PartnerOperPortNum) {
9525895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: "
9530Sstevel@tonic-gate 		    " MARKER Port %d not equal to Partner port %d\n",
9545895Syz147064 		    portp->lp_linkid, markerp->requestor_port,
9550Sstevel@tonic-gate 		    portp->lp_lacp.PartnerOperPortNum));
9568275SEric Cheng 		return (-1);
9570Sstevel@tonic-gate 	}
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	if (ether_cmp(&markerp->system_id,
9600Sstevel@tonic-gate 	    &portp->lp_lacp.PartnerOperSystem) != 0) {
9615895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): MARKER PDU: "
9620Sstevel@tonic-gate 		    " MARKER MAC not equal to Partner MAC\n",
9635895Syz147064 		    portp->lp_linkid));
9648275SEric Cheng 		return (-1);
9650Sstevel@tonic-gate 	}
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	/*
9680Sstevel@tonic-gate 	 * Turn into Marker Response PDU
9690Sstevel@tonic-gate 	 * and return mblk to sending system
9700Sstevel@tonic-gate 	 */
9710Sstevel@tonic-gate 	markerp->tlv_marker = MARKER_RESPONSE_TLV;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	/* reuse the space that was used by received ethernet header */
9740Sstevel@tonic-gate 	ASSERT(MBLKHEAD(mp) >= sizeof (struct ether_header));
9750Sstevel@tonic-gate 	mp->b_rptr -= sizeof (struct ether_header);
9760Sstevel@tonic-gate 	fill_lacp_ether(portp, (struct ether_header *)mp->b_rptr);
9778275SEric Cheng 	return (0);
9780Sstevel@tonic-gate }
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate /*
9810Sstevel@tonic-gate  * Update the LACP mode (off, active, or passive) of the specified group.
9820Sstevel@tonic-gate  */
9830Sstevel@tonic-gate void
aggr_lacp_update_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode)9840Sstevel@tonic-gate aggr_lacp_update_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode)
9850Sstevel@tonic-gate {
9860Sstevel@tonic-gate 	aggr_lacp_mode_t old_mode = grp->lg_lacp_mode;
9870Sstevel@tonic-gate 	aggr_port_t *port;
9880Sstevel@tonic-gate 
9898275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
9908275SEric Cheng 	ASSERT(!grp->lg_closing);
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 	if (mode == old_mode)
9930Sstevel@tonic-gate 		return;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	grp->lg_lacp_mode = mode;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
9980Sstevel@tonic-gate 		port->lp_lacp.ActorAdminPortState.bit.activity =
9990Sstevel@tonic-gate 		    port->lp_lacp.ActorOperPortState.bit.activity =
10000Sstevel@tonic-gate 		    (mode == AGGR_LACP_ACTIVE);
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 		if (old_mode == AGGR_LACP_OFF) {
10030Sstevel@tonic-gate 			/* OFF -> {PASSIVE,ACTIVE} */
10040Sstevel@tonic-gate 			/* turn OFF Collector_Distributor */
10050Sstevel@tonic-gate 			aggr_set_coll_dist(port, B_FALSE);
10060Sstevel@tonic-gate 			lacp_on(port);
10070Sstevel@tonic-gate 		} else if (mode == AGGR_LACP_OFF) {
10080Sstevel@tonic-gate 			/* {PASSIVE,ACTIVE} -> OFF */
10090Sstevel@tonic-gate 			lacp_off(port);
10108275SEric Cheng 			/* Turn ON Collector_Distributor */
10118275SEric Cheng 			aggr_set_coll_dist(port, B_TRUE);
10120Sstevel@tonic-gate 		} else {
10130Sstevel@tonic-gate 			/* PASSIVE->ACTIVE or ACTIVE->PASSIVE */
10140Sstevel@tonic-gate 			port->lp_lacp.sm.begin = B_TRUE;
10150Sstevel@tonic-gate 			lacp_mux_sm(port);
10160Sstevel@tonic-gate 			lacp_periodic_sm(port);
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 			/* kick off state machines */
10190Sstevel@tonic-gate 			lacp_receive_sm(port, NULL);
10200Sstevel@tonic-gate 			lacp_mux_sm(port);
10210Sstevel@tonic-gate 		}
10220Sstevel@tonic-gate 	}
10230Sstevel@tonic-gate }
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate /*
10270Sstevel@tonic-gate  * Update the LACP timer (short or long) of the specified group.
10280Sstevel@tonic-gate  */
10290Sstevel@tonic-gate void
aggr_lacp_update_timer(aggr_grp_t * grp,aggr_lacp_timer_t timer)10300Sstevel@tonic-gate aggr_lacp_update_timer(aggr_grp_t *grp, aggr_lacp_timer_t timer)
10310Sstevel@tonic-gate {
10320Sstevel@tonic-gate 	aggr_port_t *port;
10330Sstevel@tonic-gate 
10348275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if (timer == grp->aggr.PeriodicTimer)
10370Sstevel@tonic-gate 		return;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	grp->aggr.PeriodicTimer = timer;
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
10420Sstevel@tonic-gate 		port->lp_lacp.ActorAdminPortState.bit.timeout =
10430Sstevel@tonic-gate 		    port->lp_lacp.ActorOperPortState.bit.timeout =
10440Sstevel@tonic-gate 		    (timer == AGGR_LACP_TIMER_SHORT);
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate 
10488275SEric Cheng void
aggr_port_lacp_set_mode(aggr_grp_t * grp,aggr_port_t * port)10498275SEric Cheng aggr_port_lacp_set_mode(aggr_grp_t *grp, aggr_port_t *port)
10508275SEric Cheng {
10518275SEric Cheng 	aggr_lacp_mode_t	mode;
10528275SEric Cheng 	aggr_lacp_timer_t	timer;
10538275SEric Cheng 
10548275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
10558275SEric Cheng 
10568275SEric Cheng 	mode = grp->lg_lacp_mode;
10578275SEric Cheng 	timer = grp->aggr.PeriodicTimer;
10588275SEric Cheng 
10598275SEric Cheng 	port->lp_lacp.ActorAdminPortState.bit.activity =
10608275SEric Cheng 	    port->lp_lacp.ActorOperPortState.bit.activity =
10618275SEric Cheng 	    (mode == AGGR_LACP_ACTIVE);
10628275SEric Cheng 
10638275SEric Cheng 	port->lp_lacp.ActorAdminPortState.bit.timeout =
10648275SEric Cheng 	    port->lp_lacp.ActorOperPortState.bit.timeout =
10658275SEric Cheng 	    (timer == AGGR_LACP_TIMER_SHORT);
10668275SEric Cheng 
10678275SEric Cheng 	if (mode == AGGR_LACP_OFF) {
10688275SEric Cheng 		/* Turn ON Collector_Distributor */
10698275SEric Cheng 		aggr_set_coll_dist(port, B_TRUE);
10708275SEric Cheng 	} else { /* LACP_ACTIVE/PASSIVE */
10718275SEric Cheng 		lacp_on(port);
10728275SEric Cheng 	}
10738275SEric Cheng }
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate /*
10760Sstevel@tonic-gate  * Sets the initial LACP mode (off, active, passive) and LACP timer
10770Sstevel@tonic-gate  * (short, long) of the specified group.
10780Sstevel@tonic-gate  */
10790Sstevel@tonic-gate void
aggr_lacp_set_mode(aggr_grp_t * grp,aggr_lacp_mode_t mode,aggr_lacp_timer_t timer)10800Sstevel@tonic-gate aggr_lacp_set_mode(aggr_grp_t *grp, aggr_lacp_mode_t mode,
10810Sstevel@tonic-gate     aggr_lacp_timer_t timer)
10820Sstevel@tonic-gate {
10830Sstevel@tonic-gate 	aggr_port_t *port;
10840Sstevel@tonic-gate 
10858275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 	grp->lg_lacp_mode = mode;
10880Sstevel@tonic-gate 	grp->aggr.PeriodicTimer = timer;
10890Sstevel@tonic-gate 
10908275SEric Cheng 	for (port = grp->lg_ports; port != NULL; port = port->lp_next)
10918275SEric Cheng 		aggr_port_lacp_set_mode(grp, port);
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate /*
10950Sstevel@tonic-gate  * Verify that the Partner MAC and Key recorded by the specified
10960Sstevel@tonic-gate  * port are not found in other ports that are not part of our
10970Sstevel@tonic-gate  * aggregation. Returns B_TRUE if such a port is found, B_FALSE
10980Sstevel@tonic-gate  * otherwise.
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate static boolean_t
lacp_misconfig_check(aggr_port_t * portp)11010Sstevel@tonic-gate lacp_misconfig_check(aggr_port_t *portp)
11020Sstevel@tonic-gate {
11031537Snd99603 	aggr_grp_t *grp = portp->lp_grp;
11041537Snd99603 	lacp_sel_ports_t *cport;
11051537Snd99603 
11061537Snd99603 	mutex_enter(&lacp_sel_lock);
11071537Snd99603 
11081537Snd99603 	for (cport = sel_ports; cport != NULL; cport = cport->sp_next) {
11090Sstevel@tonic-gate 
11101537Snd99603 		/* skip entries of the group of the port being checked */
11115895Syz147064 		if (cport->sp_grp_linkid == grp->lg_linkid)
11121537Snd99603 			continue;
11131537Snd99603 
11141537Snd99603 		if ((ether_cmp(&cport->sp_partner_system,
11151537Snd99603 		    &grp->aggr.PartnerSystem) == 0) &&
11161537Snd99603 		    (cport->sp_partner_key == grp->aggr.PartnerOperAggrKey)) {
11171537Snd99603 			char mac_str[ETHERADDRL*3];
11181537Snd99603 			struct ether_addr *mac = &cport->sp_partner_system;
11190Sstevel@tonic-gate 
11201537Snd99603 			/*
11211537Snd99603 			 * The Partner port information is already in use
11221537Snd99603 			 * by ports in another aggregation so disable this
11231537Snd99603 			 * port.
11241537Snd99603 			 */
11251537Snd99603 
11261537Snd99603 			(void) snprintf(mac_str, sizeof (mac_str),
11271537Snd99603 			    "%x:%x:%x:%x:%x:%x",
11281537Snd99603 			    mac->ether_addr_octet[0], mac->ether_addr_octet[1],
11291537Snd99603 			    mac->ether_addr_octet[2], mac->ether_addr_octet[3],
11301537Snd99603 			    mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
11310Sstevel@tonic-gate 
11321537Snd99603 			portp->lp_lacp.sm.selected = AGGR_UNSELECTED;
11335895Syz147064 
11345895Syz147064 			cmn_err(CE_NOTE, "aggr %d port %d: Port Partner "
11355895Syz147064 			    "MAC %s and key %d in use on aggregation %d "
11365895Syz147064 			    "port %d\n", grp->lg_linkid, portp->lp_linkid,
11375895Syz147064 			    mac_str, portp->lp_lacp.PartnerOperKey,
11385895Syz147064 			    cport->sp_grp_linkid, cport->sp_linkid);
11391537Snd99603 			break;
11401537Snd99603 		}
11411537Snd99603 	}
11420Sstevel@tonic-gate 
11431537Snd99603 	mutex_exit(&lacp_sel_lock);
11441537Snd99603 	return (cport != NULL);
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate 
11471537Snd99603 /*
11481537Snd99603  * Remove the specified port from the list of selected ports.
11491537Snd99603  */
11501537Snd99603 static void
lacp_sel_ports_del(aggr_port_t * portp)11511537Snd99603 lacp_sel_ports_del(aggr_port_t *portp)
11521537Snd99603 {
11531537Snd99603 	lacp_sel_ports_t *cport, **prev = NULL;
11541537Snd99603 
11551537Snd99603 	mutex_enter(&lacp_sel_lock);
11561537Snd99603 
11571537Snd99603 	prev = &sel_ports;
11581537Snd99603 	for (cport = sel_ports; cport != NULL; prev = &cport->sp_next,
11591537Snd99603 	    cport = cport->sp_next) {
11605895Syz147064 		if (portp->lp_linkid == cport->sp_linkid)
11611537Snd99603 			break;
11621537Snd99603 	}
11631537Snd99603 
11641537Snd99603 	if (cport == NULL) {
11651537Snd99603 		mutex_exit(&lacp_sel_lock);
11661537Snd99603 		return;
11671537Snd99603 	}
11681537Snd99603 
11691537Snd99603 	*prev = cport->sp_next;
11701537Snd99603 	kmem_free(cport, sizeof (*cport));
11711537Snd99603 
11721537Snd99603 	mutex_exit(&lacp_sel_lock);
11731537Snd99603 }
11741537Snd99603 
11751537Snd99603 /*
11761537Snd99603  * Add the specified port to the list of selected ports. Returns B_FALSE
11771537Snd99603  * if the operation could not be performed due to an memory allocation
11781537Snd99603  * error.
11791537Snd99603  */
11801537Snd99603 static boolean_t
lacp_sel_ports_add(aggr_port_t * portp)11811537Snd99603 lacp_sel_ports_add(aggr_port_t *portp)
11821537Snd99603 {
11831537Snd99603 	lacp_sel_ports_t *new_port;
11841537Snd99603 	lacp_sel_ports_t *cport, **last;
11851537Snd99603 
11861537Snd99603 	mutex_enter(&lacp_sel_lock);
11871537Snd99603 
11881537Snd99603 	/* check if port is already in the list */
11891537Snd99603 	last = &sel_ports;
11901537Snd99603 	for (cport = sel_ports; cport != NULL;
11911537Snd99603 	    last = &cport->sp_next, cport = cport->sp_next) {
11925895Syz147064 		if (portp->lp_linkid == cport->sp_linkid) {
11931537Snd99603 			ASSERT(cport->sp_partner_key ==
11941537Snd99603 			    portp->lp_lacp.PartnerOperKey);
11951537Snd99603 			ASSERT(ether_cmp(&cport->sp_partner_system,
11961537Snd99603 			    &portp->lp_lacp.PartnerOperSystem) == 0);
11971537Snd99603 
11981537Snd99603 			mutex_exit(&lacp_sel_lock);
11991537Snd99603 			return (B_TRUE);
12001537Snd99603 		}
12011537Snd99603 	}
12021537Snd99603 
12031537Snd99603 	/* create and initialize new entry */
12041537Snd99603 	new_port = kmem_zalloc(sizeof (lacp_sel_ports_t), KM_NOSLEEP);
12051537Snd99603 	if (new_port == NULL) {
12061537Snd99603 		mutex_exit(&lacp_sel_lock);
12071537Snd99603 		return (B_FALSE);
12081537Snd99603 	}
12091537Snd99603 
12105895Syz147064 	new_port->sp_grp_linkid = portp->lp_grp->lg_linkid;
12111537Snd99603 	bcopy(&portp->lp_lacp.PartnerOperSystem,
12121537Snd99603 	    &new_port->sp_partner_system, sizeof (new_port->sp_partner_system));
12131537Snd99603 	new_port->sp_partner_key = portp->lp_lacp.PartnerOperKey;
12145895Syz147064 	new_port->sp_linkid = portp->lp_linkid;
12151537Snd99603 
12161537Snd99603 	*last = new_port;
12171537Snd99603 
12181537Snd99603 	mutex_exit(&lacp_sel_lock);
12191537Snd99603 	return (B_TRUE);
12201537Snd99603 }
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate /*
12230Sstevel@tonic-gate  * lacp_selection_logic - LACP selection logic
12240Sstevel@tonic-gate  *		Sets the selected variable on a per port basis
12250Sstevel@tonic-gate  *		and sets Ready when all waiting ports are ready
12260Sstevel@tonic-gate  *		to go online.
12270Sstevel@tonic-gate  *
12280Sstevel@tonic-gate  * parameters:
12290Sstevel@tonic-gate  *      - portp - instance this applies to.
12300Sstevel@tonic-gate  *
12310Sstevel@tonic-gate  * invoked:
12320Sstevel@tonic-gate  *    - when initialization is needed
12330Sstevel@tonic-gate  *    - when UNSELECTED is set from the lacp_receive_sm() in LACP_CURRENT state
12340Sstevel@tonic-gate  *    - When the lacp_receive_sm goes to the LACP_DEFAULTED state
12350Sstevel@tonic-gate  *    - every time the wait_while_timer pops
12360Sstevel@tonic-gate  *    - everytime we turn LACP on/off
12370Sstevel@tonic-gate  */
12380Sstevel@tonic-gate static void
lacp_selection_logic(aggr_port_t * portp)12390Sstevel@tonic-gate lacp_selection_logic(aggr_port_t *portp)
12400Sstevel@tonic-gate {
12410Sstevel@tonic-gate 	aggr_port_t *tpp;
12420Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
12430Sstevel@tonic-gate 	int ports_waiting;
12440Sstevel@tonic-gate 	boolean_t reset_mac = B_FALSE;
12450Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
12460Sstevel@tonic-gate 
12478275SEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
12500Sstevel@tonic-gate 	if (!pl->sm.lacp_on) {
12511537Snd99603 		lacp_port_unselect(portp);
12520Sstevel@tonic-gate 		aggrp->aggr.ready = B_FALSE;
12530Sstevel@tonic-gate 		lacp_mux_sm(portp);
12540Sstevel@tonic-gate 		return;
12550Sstevel@tonic-gate 	}
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	if (pl->sm.begin || !pl->sm.lacp_enabled ||
12580Sstevel@tonic-gate 	    (portp->lp_state != AGGR_PORT_STATE_ATTACHED)) {
12590Sstevel@tonic-gate 
12605895Syz147064 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
12610Sstevel@tonic-gate 		    "selected %d-->%d (begin=%d, lacp_enabled = %d, "
12625895Syz147064 		    "lp_state=%d)\n", portp->lp_linkid, pl->sm.selected,
12632311Sseb 		    AGGR_UNSELECTED, pl->sm.begin, pl->sm.lacp_enabled,
12640Sstevel@tonic-gate 		    portp->lp_state));
12650Sstevel@tonic-gate 
12661537Snd99603 		lacp_port_unselect(portp);
12670Sstevel@tonic-gate 		aggrp->aggr.ready = B_FALSE;
12680Sstevel@tonic-gate 		lacp_mux_sm(portp);
12690Sstevel@tonic-gate 		return;
12700Sstevel@tonic-gate 	}
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	/*
12730Sstevel@tonic-gate 	 * If LACP is not enabled then selected is never set.
12740Sstevel@tonic-gate 	 */
12750Sstevel@tonic-gate 	if (!pl->sm.lacp_enabled) {
12765895Syz147064 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): selected %d-->%d\n",
12775895Syz147064 		    portp->lp_linkid, pl->sm.selected, AGGR_UNSELECTED));
12780Sstevel@tonic-gate 
12791537Snd99603 		lacp_port_unselect(portp);
12800Sstevel@tonic-gate 		lacp_mux_sm(portp);
12810Sstevel@tonic-gate 		return;
12820Sstevel@tonic-gate 	}
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 	/*
12850Sstevel@tonic-gate 	 * Check if the Partner MAC or Key are zero. If so, we have
12860Sstevel@tonic-gate 	 * not received any LACP info or it has expired and the
12870Sstevel@tonic-gate 	 * receive machine is in the LACP_DEFAULTED state.
12880Sstevel@tonic-gate 	 */
12890Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem, &etherzeroaddr) == 0 ||
12900Sstevel@tonic-gate 	    (pl->PartnerOperKey == 0)) {
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
12930Sstevel@tonic-gate 			if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
12940Sstevel@tonic-gate 			    &etherzeroaddr) != 0 &&
12950Sstevel@tonic-gate 			    (tpp->lp_lacp.PartnerOperKey != 0))
12960Sstevel@tonic-gate 				break;
12970Sstevel@tonic-gate 		}
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 		/*
13000Sstevel@tonic-gate 		 * If all ports have no key or aggregation address,
13010Sstevel@tonic-gate 		 * then clear the negotiated Partner MAC and key.
13020Sstevel@tonic-gate 		 */
13030Sstevel@tonic-gate 		if (tpp == NULL) {
13040Sstevel@tonic-gate 			/* Clear the aggregation Partner MAC and key */
13050Sstevel@tonic-gate 			aggrp->aggr.PartnerSystem = etherzeroaddr;
13060Sstevel@tonic-gate 			aggrp->aggr.PartnerOperAggrKey = 0;
13070Sstevel@tonic-gate 		}
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 		return;
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	/*
13130Sstevel@tonic-gate 	 * Insure that at least one port in the aggregation
13140Sstevel@tonic-gate 	 * matches the Partner aggregation MAC and key. If not,
13150Sstevel@tonic-gate 	 * then clear the aggregation MAC and key. Later we will
13160Sstevel@tonic-gate 	 * set the Partner aggregation MAC and key to that of the
13170Sstevel@tonic-gate 	 * current port's Partner MAC and key.
13180Sstevel@tonic-gate 	 */
13190Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem,
13200Sstevel@tonic-gate 	    &aggrp->aggr.PartnerSystem) != 0 ||
13210Sstevel@tonic-gate 	    (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) {
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
13240Sstevel@tonic-gate 			if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
13250Sstevel@tonic-gate 			    &aggrp->aggr.PartnerSystem) == 0 &&
13260Sstevel@tonic-gate 			    (tpp->lp_lacp.PartnerOperKey ==
132711878SVenu.Iyer@Sun.COM 			    aggrp->aggr.PartnerOperAggrKey)) {
132811878SVenu.Iyer@Sun.COM 				/* Set aggregation Partner MAC and key */
132911878SVenu.Iyer@Sun.COM 				aggrp->aggr.PartnerSystem =
133011878SVenu.Iyer@Sun.COM 				    pl->PartnerOperSystem;
133111878SVenu.Iyer@Sun.COM 				aggrp->aggr.PartnerOperAggrKey =
133211878SVenu.Iyer@Sun.COM 				    pl->PartnerOperKey;
13330Sstevel@tonic-gate 				break;
133411878SVenu.Iyer@Sun.COM 			}
13350Sstevel@tonic-gate 		}
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate 		if (tpp == NULL) {
13380Sstevel@tonic-gate 			/* Clear the aggregation Partner MAC and key */
13390Sstevel@tonic-gate 			aggrp->aggr.PartnerSystem = etherzeroaddr;
13400Sstevel@tonic-gate 			aggrp->aggr.PartnerOperAggrKey = 0;
13410Sstevel@tonic-gate 			reset_mac = B_TRUE;
13420Sstevel@tonic-gate 		}
13430Sstevel@tonic-gate 	}
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 	/*
13460Sstevel@tonic-gate 	 * If our Actor MAC is found in the Partner MAC
13470Sstevel@tonic-gate 	 * on this port then we have a loopback misconfiguration.
13480Sstevel@tonic-gate 	 */
13490Sstevel@tonic-gate 	if (ether_cmp(&pl->PartnerOperSystem,
13500Sstevel@tonic-gate 	    (struct ether_addr *)&aggrp->lg_addr) == 0) {
13515895Syz147064 		cmn_err(CE_NOTE, "trunk link: (%d): Loopback condition.\n",
13525895Syz147064 		    portp->lp_linkid);
13530Sstevel@tonic-gate 
13541537Snd99603 		lacp_port_unselect(portp);
13550Sstevel@tonic-gate 		lacp_mux_sm(portp);
13560Sstevel@tonic-gate 		return;
13570Sstevel@tonic-gate 	}
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	/*
13600Sstevel@tonic-gate 	 * If our Partner MAC and Key are found on any other
13610Sstevel@tonic-gate 	 * ports that are not in our aggregation, we have
13620Sstevel@tonic-gate 	 * a misconfiguration.
13630Sstevel@tonic-gate 	 */
13640Sstevel@tonic-gate 	if (lacp_misconfig_check(portp)) {
13650Sstevel@tonic-gate 		lacp_mux_sm(portp);
13660Sstevel@tonic-gate 		return;
13670Sstevel@tonic-gate 	}
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	/*
13700Sstevel@tonic-gate 	 * If the Aggregation Partner MAC and Key have not been
13710Sstevel@tonic-gate 	 * set, then this is either the first port or the aggregation
13720Sstevel@tonic-gate 	 * MAC and key have been reset. In either case we must set
13730Sstevel@tonic-gate 	 * the values of the Partner MAC and key.
13740Sstevel@tonic-gate 	 */
13750Sstevel@tonic-gate 	if (ether_cmp(&aggrp->aggr.PartnerSystem, &etherzeroaddr) == 0 &&
13760Sstevel@tonic-gate 	    (aggrp->aggr.PartnerOperAggrKey == 0)) {
13770Sstevel@tonic-gate 		/* Set aggregation Partner MAC and key */
13780Sstevel@tonic-gate 		aggrp->aggr.PartnerSystem = pl->PartnerOperSystem;
13790Sstevel@tonic-gate 		aggrp->aggr.PartnerOperAggrKey = pl->PartnerOperKey;
13800Sstevel@tonic-gate 
13810Sstevel@tonic-gate 		/*
13820Sstevel@tonic-gate 		 * If we reset Partner aggregation MAC, then restart
13830Sstevel@tonic-gate 		 * selection_logic on ports that match new MAC address.
13840Sstevel@tonic-gate 		 */
13850Sstevel@tonic-gate 		if (reset_mac) {
13860Sstevel@tonic-gate 			for (tpp = aggrp->lg_ports; tpp; tpp =
13870Sstevel@tonic-gate 			    tpp->lp_next) {
13880Sstevel@tonic-gate 				if (tpp == portp)
13890Sstevel@tonic-gate 					continue;
13900Sstevel@tonic-gate 				if (ether_cmp(&tpp->lp_lacp.PartnerOperSystem,
13910Sstevel@tonic-gate 				    &aggrp->aggr.PartnerSystem) == 0 &&
13920Sstevel@tonic-gate 				    (tpp->lp_lacp.PartnerOperKey ==
13930Sstevel@tonic-gate 				    aggrp->aggr.PartnerOperAggrKey))
13940Sstevel@tonic-gate 					lacp_selection_logic(tpp);
13950Sstevel@tonic-gate 			}
13960Sstevel@tonic-gate 		}
13970Sstevel@tonic-gate 	} else if (ether_cmp(&pl->PartnerOperSystem,
13980Sstevel@tonic-gate 	    &aggrp->aggr.PartnerSystem) != 0 ||
13990Sstevel@tonic-gate 	    (pl->PartnerOperKey != aggrp->aggr.PartnerOperAggrKey)) {
14000Sstevel@tonic-gate 		/*
14010Sstevel@tonic-gate 		 * The Partner port information does not match
14020Sstevel@tonic-gate 		 * that of the other ports in the aggregation
14030Sstevel@tonic-gate 		 * so disable this port.
14040Sstevel@tonic-gate 		 */
14051537Snd99603 		lacp_port_unselect(portp);
14061537Snd99603 
14075895Syz147064 		cmn_err(CE_NOTE, "trunk link: (%d): Port Partner MAC "
14085895Syz147064 		    "or key (%d) incompatible with Aggregation Partner "
14095895Syz147064 		    "MAC or key (%d)\n", portp->lp_linkid, pl->PartnerOperKey,
14102311Sseb 		    aggrp->aggr.PartnerOperAggrKey);
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate 		lacp_mux_sm(portp);
14130Sstevel@tonic-gate 		return;
14140Sstevel@tonic-gate 	}
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	/* If we get to here, automatically set selected */
14170Sstevel@tonic-gate 	if (pl->sm.selected != AGGR_SELECTED) {
14185895Syz147064 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
14195895Syz147064 		    "selected %d-->%d\n", portp->lp_linkid,
14200Sstevel@tonic-gate 		    pl->sm.selected, AGGR_SELECTED));
14211537Snd99603 		if (!lacp_port_select(portp))
14221537Snd99603 			return;
14230Sstevel@tonic-gate 		lacp_mux_sm(portp);
14240Sstevel@tonic-gate 	}
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 	/*
14270Sstevel@tonic-gate 	 * From this point onward we have selected the port
14280Sstevel@tonic-gate 	 * and are simply checking if the Ready flag should
14290Sstevel@tonic-gate 	 * be set.
14300Sstevel@tonic-gate 	 */
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	/*
14330Sstevel@tonic-gate 	 * If at least two ports are waiting to aggregate
14340Sstevel@tonic-gate 	 * and ready_n is set on all ports waiting to aggregate
14350Sstevel@tonic-gate 	 * then set READY for the aggregation.
14360Sstevel@tonic-gate 	 */
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	ports_waiting = 0;
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 	if (!aggrp->aggr.ready) {
14410Sstevel@tonic-gate 		/*
14420Sstevel@tonic-gate 		 * If all ports in the aggregation have received compatible
14430Sstevel@tonic-gate 		 * partner information and they match up correctly with the
14440Sstevel@tonic-gate 		 * switch, there is no need to wait for all the
14450Sstevel@tonic-gate 		 * wait_while_timers to pop.
14460Sstevel@tonic-gate 		 */
14470Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next) {
14480Sstevel@tonic-gate 			if (((tpp->lp_lacp.sm.mux_state == LACP_WAITING) ||
14490Sstevel@tonic-gate 			    tpp->lp_lacp.sm.begin) &&
145011197SRamesh.K@Sun.COM 			    !tpp->lp_lacp.PartnerOperPortState.bit.sync) {
14510Sstevel@tonic-gate 				/* Add up ports uninitialized or waiting */
14520Sstevel@tonic-gate 				ports_waiting++;
145311197SRamesh.K@Sun.COM 				if (!tpp->lp_lacp.sm.ready_n) {
145411197SRamesh.K@Sun.COM 					DTRACE_PROBE1(port___not__ready,
145511197SRamesh.K@Sun.COM 					    aggr_port_t *, tpp);
14560Sstevel@tonic-gate 					return;
145711197SRamesh.K@Sun.COM 				}
14580Sstevel@tonic-gate 			}
14590Sstevel@tonic-gate 		}
14600Sstevel@tonic-gate 	}
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	if (aggrp->aggr.ready) {
14635895Syz147064 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): "
14645895Syz147064 		    "aggr.ready already set\n", portp->lp_linkid));
14650Sstevel@tonic-gate 		lacp_mux_sm(portp);
14660Sstevel@tonic-gate 	} else {
14675895Syz147064 		AGGR_LACP_DBG(("lacp_selection_logic:(%d): Ready %d-->%d\n",
14685895Syz147064 		    portp->lp_linkid, aggrp->aggr.ready, B_TRUE));
14690Sstevel@tonic-gate 		aggrp->aggr.ready = B_TRUE;
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 		for (tpp = aggrp->lg_ports; tpp; tpp = tpp->lp_next)
14720Sstevel@tonic-gate 			lacp_mux_sm(tpp);
14730Sstevel@tonic-gate 	}
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate }
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate /*
14780Sstevel@tonic-gate  * wait_while_timer_pop - When the timer pops, we arrive here to
14790Sstevel@tonic-gate  *			set ready_n and trigger the selection logic.
14800Sstevel@tonic-gate  */
14810Sstevel@tonic-gate static void
wait_while_timer_pop(void * data)14820Sstevel@tonic-gate wait_while_timer_pop(void *data)
14830Sstevel@tonic-gate {
14840Sstevel@tonic-gate 	aggr_port_t *portp = data;
14858275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
14860Sstevel@tonic-gate 
14878275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
14888275SEric Cheng 	pl->lacp_timer_bits |= LACP_WAIT_WHILE_TIMEOUT;
14898275SEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
14908275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
14918275SEric Cheng }
14920Sstevel@tonic-gate 
14938275SEric Cheng /*
14948275SEric Cheng  * wait_while_timer_pop_handler - When the timer pops, we arrive here to
14958275SEric Cheng  *			set ready_n and trigger the selection logic.
14968275SEric Cheng  */
14978275SEric Cheng static void
wait_while_timer_pop_handler(aggr_port_t * portp)14988275SEric Cheng wait_while_timer_pop_handler(aggr_port_t *portp)
14998275SEric Cheng {
15008275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15010Sstevel@tonic-gate 
15025895Syz147064 	AGGR_LACP_DBG(("trunk link:(%d): wait_while_timer pop \n",
15035895Syz147064 	    portp->lp_linkid));
15040Sstevel@tonic-gate 	portp->lp_lacp.sm.ready_n = B_TRUE;
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	lacp_selection_logic(portp);
15070Sstevel@tonic-gate }
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate static void
start_wait_while_timer(aggr_port_t * portp)15100Sstevel@tonic-gate start_wait_while_timer(aggr_port_t *portp)
15110Sstevel@tonic-gate {
15128275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
15138275SEric Cheng 
15148275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15150Sstevel@tonic-gate 
15168275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
15178275SEric Cheng 	if (pl->wait_while_timer.id == 0) {
15188275SEric Cheng 		pl->wait_while_timer.id =
15190Sstevel@tonic-gate 		    timeout(wait_while_timer_pop, portp,
15200Sstevel@tonic-gate 		    drv_usectohz(1000000 *
15210Sstevel@tonic-gate 		    portp->lp_lacp.wait_while_timer.val));
15220Sstevel@tonic-gate 	}
15238275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
15240Sstevel@tonic-gate }
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate static void
stop_wait_while_timer(aggr_port_t * portp)15288275SEric Cheng stop_wait_while_timer(aggr_port_t *portp)
15290Sstevel@tonic-gate {
15308275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
15318275SEric Cheng 	timeout_id_t id;
15328275SEric Cheng 
15338275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
15340Sstevel@tonic-gate 
15358275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
15368275SEric Cheng 	if ((id = pl->wait_while_timer.id) != 0) {
15378275SEric Cheng 		pl->lacp_timer_bits &= ~LACP_WAIT_WHILE_TIMEOUT;
15388275SEric Cheng 		pl->wait_while_timer.id = 0;
15390Sstevel@tonic-gate 	}
15408275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
15418275SEric Cheng 
15428275SEric Cheng 	if (id != 0)
15438275SEric Cheng 		(void) untimeout(id);
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate /*
15470Sstevel@tonic-gate  * Invoked when a port has been attached to a group.
15480Sstevel@tonic-gate  * Complete the processing that couldn't be finished from lacp_on()
15490Sstevel@tonic-gate  * because the port was not started. We know that the link is full
15500Sstevel@tonic-gate  * duplex and ON, otherwise it wouldn't be attached.
15510Sstevel@tonic-gate  */
15520Sstevel@tonic-gate void
aggr_lacp_port_attached(aggr_port_t * portp)15530Sstevel@tonic-gate aggr_lacp_port_attached(aggr_port_t *portp)
15540Sstevel@tonic-gate {
15550Sstevel@tonic-gate 	aggr_grp_t *grp = portp->lp_grp;
15560Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
15570Sstevel@tonic-gate 
15588275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
15598275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
15600Sstevel@tonic-gate 	ASSERT(portp->lp_state == AGGR_PORT_STATE_ATTACHED);
15610Sstevel@tonic-gate 
15625895Syz147064 	AGGR_LACP_DBG(("aggr_lacp_port_attached: port %d\n",
15635895Syz147064 	    portp->lp_linkid));
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate 	portp->lp_lacp.sm.port_enabled = B_TRUE;	/* link on */
15660Sstevel@tonic-gate 
15678275SEric Cheng 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
15680Sstevel@tonic-gate 		return;
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 	pl->sm.lacp_enabled = B_TRUE;
15710Sstevel@tonic-gate 	pl->ActorOperPortState.bit.aggregation = B_TRUE;
15720Sstevel@tonic-gate 	pl->sm.begin = B_TRUE;
15730Sstevel@tonic-gate 
15748275SEric Cheng 	lacp_receive_sm(portp, NULL);
15758275SEric Cheng 	lacp_mux_sm(portp);
15760Sstevel@tonic-gate 
15778275SEric Cheng 	/* Enable Multicast Slow Protocol address */
15788275SEric Cheng 	aggr_lacp_mcast_on(portp);
15790Sstevel@tonic-gate 
15808275SEric Cheng 	/* periodic_sm is started up from the receive machine */
15818275SEric Cheng 	lacp_selection_logic(portp);
15820Sstevel@tonic-gate }
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate /*
15850Sstevel@tonic-gate  * Invoked when a port has been detached from a group. Turn off
15860Sstevel@tonic-gate  * LACP processing if it was enabled.
15870Sstevel@tonic-gate  */
15880Sstevel@tonic-gate void
aggr_lacp_port_detached(aggr_port_t * portp)15890Sstevel@tonic-gate aggr_lacp_port_detached(aggr_port_t *portp)
15900Sstevel@tonic-gate {
15910Sstevel@tonic-gate 	aggr_grp_t *grp = portp->lp_grp;
15920Sstevel@tonic-gate 
15938275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
15948275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_mh));
15950Sstevel@tonic-gate 
15965895Syz147064 	AGGR_LACP_DBG(("aggr_lacp_port_detached: port %d\n",
15975895Syz147064 	    portp->lp_linkid));
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	portp->lp_lacp.sm.port_enabled = B_FALSE;
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
16020Sstevel@tonic-gate 		return;
16030Sstevel@tonic-gate 
16048275SEric Cheng 	portp->lp_lacp.sm.lacp_enabled = B_FALSE;
16058275SEric Cheng 	lacp_selection_logic(portp);
16068275SEric Cheng 	lacp_mux_sm(portp);
16078275SEric Cheng 	lacp_periodic_sm(portp);
16080Sstevel@tonic-gate 
16098275SEric Cheng 	/*
16108275SEric Cheng 	 * Disable Slow Protocol Timers.
16118275SEric Cheng 	 */
16128275SEric Cheng 	stop_periodic_timer(portp);
16138275SEric Cheng 	stop_current_while_timer(portp);
16148275SEric Cheng 	stop_wait_while_timer(portp);
16150Sstevel@tonic-gate 
16168275SEric Cheng 	/* Disable Multicast Slow Protocol address */
16178275SEric Cheng 	aggr_lacp_mcast_off(portp);
16188275SEric Cheng 	aggr_set_coll_dist(portp, B_FALSE);
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate /*
16220Sstevel@tonic-gate  * Enable Slow Protocol LACP and Marker PDUs.
16230Sstevel@tonic-gate  */
16240Sstevel@tonic-gate static void
lacp_on(aggr_port_t * portp)16250Sstevel@tonic-gate lacp_on(aggr_port_t *portp)
16260Sstevel@tonic-gate {
16278275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
16288275SEric Cheng 	mac_perim_handle_t mph;
16298275SEric Cheng 
16308275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
16318275SEric Cheng 
16328275SEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	/*
16350Sstevel@tonic-gate 	 * Reset the state machines and Partner operational
16360Sstevel@tonic-gate 	 * information. Careful to not reset things like
16370Sstevel@tonic-gate 	 * our link state.
16380Sstevel@tonic-gate 	 */
16390Sstevel@tonic-gate 	lacp_reset_port(portp);
16408275SEric Cheng 	pl->sm.lacp_on = B_TRUE;
16410Sstevel@tonic-gate 
16425895Syz147064 	AGGR_LACP_DBG(("lacp_on:(%d): \n", portp->lp_linkid));
16430Sstevel@tonic-gate 
16448275SEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
16458275SEric Cheng 		pl->sm.port_enabled = B_TRUE;
16468275SEric Cheng 		pl->sm.lacp_enabled = B_TRUE;
16478275SEric Cheng 		pl->ActorOperPortState.bit.aggregation = B_TRUE;
16488275SEric Cheng 	}
16498275SEric Cheng 
16500Sstevel@tonic-gate 	lacp_receive_sm(portp, NULL);
16510Sstevel@tonic-gate 	lacp_mux_sm(portp);
16520Sstevel@tonic-gate 
16538275SEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
16548275SEric Cheng 		/* Enable Multicast Slow Protocol address */
16558275SEric Cheng 		aggr_lacp_mcast_on(portp);
16560Sstevel@tonic-gate 
16578275SEric Cheng 		/* periodic_sm is started up from the receive machine */
16588275SEric Cheng 		lacp_selection_logic(portp);
16598275SEric Cheng 	}
16608275SEric Cheng done:
16618275SEric Cheng 	mac_perim_exit(mph);
16620Sstevel@tonic-gate } /* lacp_on */
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate /* Disable Slow Protocol LACP and Marker PDUs */
16650Sstevel@tonic-gate static void
lacp_off(aggr_port_t * portp)16660Sstevel@tonic-gate lacp_off(aggr_port_t *portp)
16670Sstevel@tonic-gate {
16688275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
16698275SEric Cheng 	mac_perim_handle_t mph;
16700Sstevel@tonic-gate 
16718275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
16728275SEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
16730Sstevel@tonic-gate 
16748275SEric Cheng 	pl->sm.lacp_on = B_FALSE;
16750Sstevel@tonic-gate 
16765895Syz147064 	AGGR_LACP_DBG(("lacp_off:(%d): \n", portp->lp_linkid));
16770Sstevel@tonic-gate 
16788275SEric Cheng 	if (portp->lp_state == AGGR_PORT_STATE_ATTACHED) {
16798275SEric Cheng 		/*
16808275SEric Cheng 		 * Disable Slow Protocol Timers.
16818275SEric Cheng 		 */
16828275SEric Cheng 		stop_periodic_timer(portp);
16838275SEric Cheng 		stop_current_while_timer(portp);
16848275SEric Cheng 		stop_wait_while_timer(portp);
16850Sstevel@tonic-gate 
16868275SEric Cheng 		/* Disable Multicast Slow Protocol address */
16878275SEric Cheng 		aggr_lacp_mcast_off(portp);
16880Sstevel@tonic-gate 
16898275SEric Cheng 		pl->sm.port_enabled = B_FALSE;
16908275SEric Cheng 		pl->sm.lacp_enabled = B_FALSE;
16918275SEric Cheng 		pl->ActorOperPortState.bit.aggregation = B_FALSE;
16920Sstevel@tonic-gate 	}
16930Sstevel@tonic-gate 
16948275SEric Cheng 	lacp_mux_sm(portp);
16958275SEric Cheng 	lacp_periodic_sm(portp);
16968275SEric Cheng 	lacp_selection_logic(portp);
16970Sstevel@tonic-gate 
16988275SEric Cheng 	/* Turn OFF Collector_Distributor */
16998275SEric Cheng 	aggr_set_coll_dist(portp, B_FALSE);
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate 	lacp_reset_port(portp);
17028275SEric Cheng 	mac_perim_exit(mph);
17030Sstevel@tonic-gate }
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate static boolean_t
valid_lacp_pdu(aggr_port_t * portp,lacp_t * lacp)17070Sstevel@tonic-gate valid_lacp_pdu(aggr_port_t *portp, lacp_t *lacp)
17080Sstevel@tonic-gate {
17090Sstevel@tonic-gate 	/*
17100Sstevel@tonic-gate 	 * 43.4.12 - "a Receive machine shall not validate
17110Sstevel@tonic-gate 	 * the Version Number, TLV_type, or Reserved fields in received
17120Sstevel@tonic-gate 	 * LACPDUs."
17130Sstevel@tonic-gate 	 * ... "a Receive machine may validate the Actor_Information_Length,
17140Sstevel@tonic-gate 	 * Partner_Information_Length, Collector_Information_Length,
17150Sstevel@tonic-gate 	 * or Terminator_Length fields."
17160Sstevel@tonic-gate 	 */
17170Sstevel@tonic-gate 	if ((lacp->actor_info.information_len != sizeof (link_info_t)) ||
17180Sstevel@tonic-gate 	    (lacp->partner_info.information_len != sizeof (link_info_t)) ||
17190Sstevel@tonic-gate 	    (lacp->collector_len != LACP_COLLECTOR_INFO_LEN) ||
17200Sstevel@tonic-gate 	    (lacp->terminator_len != LACP_TERMINATOR_INFO_LEN)) {
17215895Syz147064 		AGGR_LACP_DBG(("trunk link (%d): Malformed LACPDU: "
17225895Syz147064 		    " Terminator Length = %d \n", portp->lp_linkid,
17232311Sseb 		    lacp->terminator_len));
17240Sstevel@tonic-gate 		return (B_FALSE);
17250Sstevel@tonic-gate 	}
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	return (B_TRUE);
17280Sstevel@tonic-gate }
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate static void
start_current_while_timer(aggr_port_t * portp,uint_t time)17320Sstevel@tonic-gate start_current_while_timer(aggr_port_t *portp, uint_t time)
17330Sstevel@tonic-gate {
17348275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
17358275SEric Cheng 
17368275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
17370Sstevel@tonic-gate 
17388275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
17398275SEric Cheng 	if (pl->current_while_timer.id == 0) {
17408275SEric Cheng 		if (time > 0)
17418275SEric Cheng 			pl->current_while_timer.val = time;
17428275SEric Cheng 		else if (pl->ActorOperPortState.bit.timeout)
17438275SEric Cheng 			pl->current_while_timer.val = SHORT_TIMEOUT_TIME;
17448275SEric Cheng 		else
17458275SEric Cheng 			pl->current_while_timer.val = LONG_TIMEOUT_TIME;
17460Sstevel@tonic-gate 
17478275SEric Cheng 		pl->current_while_timer.id =
17480Sstevel@tonic-gate 		    timeout(current_while_timer_pop, portp,
17490Sstevel@tonic-gate 		    drv_usectohz((clock_t)1000000 *
17500Sstevel@tonic-gate 		    (clock_t)portp->lp_lacp.current_while_timer.val));
17510Sstevel@tonic-gate 	}
17528275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
17530Sstevel@tonic-gate }
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate static void
stop_current_while_timer(aggr_port_t * portp)17570Sstevel@tonic-gate stop_current_while_timer(aggr_port_t *portp)
17580Sstevel@tonic-gate {
17598275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
17608275SEric Cheng 	timeout_id_t id;
17618275SEric Cheng 
17628275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
17630Sstevel@tonic-gate 
17648275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
17658275SEric Cheng 	if ((id = pl->current_while_timer.id) != 0) {
17668275SEric Cheng 		pl->lacp_timer_bits &= ~LACP_CURRENT_WHILE_TIMEOUT;
17678275SEric Cheng 		pl->current_while_timer.id = 0;
17680Sstevel@tonic-gate 	}
17698275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
17708275SEric Cheng 
17718275SEric Cheng 	if (id != 0)
17728275SEric Cheng 		(void) untimeout(id);
17730Sstevel@tonic-gate }
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate static void
current_while_timer_pop(void * data)17760Sstevel@tonic-gate current_while_timer_pop(void *data)
17770Sstevel@tonic-gate {
17780Sstevel@tonic-gate 	aggr_port_t *portp = (aggr_port_t *)data;
17798275SEric Cheng 	aggr_lacp_port_t *pl = &portp->lp_lacp;
17800Sstevel@tonic-gate 
17818275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
17828275SEric Cheng 	pl->lacp_timer_bits |= LACP_CURRENT_WHILE_TIMEOUT;
17838275SEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
17848275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
17858275SEric Cheng }
17860Sstevel@tonic-gate 
17878275SEric Cheng static void
current_while_timer_pop_handler(aggr_port_t * portp)17888275SEric Cheng current_while_timer_pop_handler(aggr_port_t *portp)
17898275SEric Cheng {
17908275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
17910Sstevel@tonic-gate 
17925895Syz147064 	AGGR_LACP_DBG(("trunk link:(%d): current_while_timer "
17935895Syz147064 	    "pop id=%p\n", portp->lp_linkid,
17940Sstevel@tonic-gate 	    portp->lp_lacp.current_while_timer.id));
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	lacp_receive_sm(portp, NULL);
17970Sstevel@tonic-gate }
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate /*
18000Sstevel@tonic-gate  * record_Default - Simply copies over administrative values
18010Sstevel@tonic-gate  * to the partner operational values, and sets our state to indicate we
18020Sstevel@tonic-gate  * are using defaulted values.
18030Sstevel@tonic-gate  */
18040Sstevel@tonic-gate static void
record_Default(aggr_port_t * portp)18050Sstevel@tonic-gate record_Default(aggr_port_t *portp)
18060Sstevel@tonic-gate {
18070Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18080Sstevel@tonic-gate 
18098275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 	pl->PartnerOperPortNum = pl->PartnerAdminPortNum;
18120Sstevel@tonic-gate 	pl->PartnerOperPortPriority = pl->PartnerAdminPortPriority;
18130Sstevel@tonic-gate 	pl->PartnerOperSystem = pl->PartnerAdminSystem;
18140Sstevel@tonic-gate 	pl->PartnerOperSysPriority = pl->PartnerAdminSysPriority;
18150Sstevel@tonic-gate 	pl->PartnerOperKey = pl->PartnerAdminKey;
18160Sstevel@tonic-gate 	pl->PartnerOperPortState.state = pl->PartnerAdminPortState.state;
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_TRUE;
18190Sstevel@tonic-gate }
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate /* Returns B_TRUE on sync value changing */
18230Sstevel@tonic-gate static boolean_t
record_PDU(aggr_port_t * portp,lacp_t * lacp)18240Sstevel@tonic-gate record_PDU(aggr_port_t *portp, lacp_t *lacp)
18250Sstevel@tonic-gate {
18260Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
18270Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18280Sstevel@tonic-gate 	uint8_t save_sync;
18290Sstevel@tonic-gate 
18308275SEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	/*
18330Sstevel@tonic-gate 	 * Partner Information
18340Sstevel@tonic-gate 	 */
18350Sstevel@tonic-gate 	pl->PartnerOperPortNum = ntohs(lacp->actor_info.port);
18360Sstevel@tonic-gate 	pl->PartnerOperPortPriority =
18370Sstevel@tonic-gate 	    ntohs(lacp->actor_info.port_priority);
18380Sstevel@tonic-gate 	pl->PartnerOperSystem = lacp->actor_info.system_id;
18390Sstevel@tonic-gate 	pl->PartnerOperSysPriority =
18400Sstevel@tonic-gate 	    htons(lacp->actor_info.system_priority);
18410Sstevel@tonic-gate 	pl->PartnerOperKey = ntohs(lacp->actor_info.key);
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 	/* All state info except for Synchronization */
18440Sstevel@tonic-gate 	save_sync = pl->PartnerOperPortState.bit.sync;
18450Sstevel@tonic-gate 	pl->PartnerOperPortState.state = lacp->actor_info.state.state;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	/* Defaulted set to FALSE */
18480Sstevel@tonic-gate 	pl->ActorOperPortState.bit.defaulted = B_FALSE;
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate 	/*
18510Sstevel@tonic-gate 	 * 43.4.9 - (Partner_Port, Partner_Port_Priority, Partner_system,
18520Sstevel@tonic-gate 	 *		Partner_System_Priority, Partner_Key, and
18530Sstevel@tonic-gate 	 *		Partner_State.Aggregation) are compared to the
18540Sstevel@tonic-gate 	 *		corresponding operations paramters values for
18550Sstevel@tonic-gate 	 *		the Actor. If these are equal, or if this is
18560Sstevel@tonic-gate 	 *		an individual link, we are synchronized.
18570Sstevel@tonic-gate 	 */
18580Sstevel@tonic-gate 	if (((ntohs(lacp->partner_info.port) == pl->ActorPortNumber) &&
18590Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.port_priority) ==
18600Sstevel@tonic-gate 	    pl->ActorPortPriority) &&
18610Sstevel@tonic-gate 	    (ether_cmp(&lacp->partner_info.system_id,
18625102Syz147064 	    (struct ether_addr *)&aggrp->lg_addr) == 0) &&
18630Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.system_priority) ==
18640Sstevel@tonic-gate 	    aggrp->aggr.ActorSystemPriority) &&
18650Sstevel@tonic-gate 	    (ntohs(lacp->partner_info.key) == pl->ActorOperPortKey) &&
18660Sstevel@tonic-gate 	    (lacp->partner_info.state.bit.aggregation ==
18670Sstevel@tonic-gate 	    pl->ActorOperPortState.bit.aggregation)) ||
18680Sstevel@tonic-gate 	    (!lacp->actor_info.state.bit.aggregation)) {
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync =
18710Sstevel@tonic-gate 		    lacp->actor_info.state.bit.sync;
18720Sstevel@tonic-gate 	} else {
18730Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
18740Sstevel@tonic-gate 	}
18750Sstevel@tonic-gate 
18760Sstevel@tonic-gate 	if (save_sync != pl->PartnerOperPortState.bit.sync) {
18775895Syz147064 		AGGR_LACP_DBG(("record_PDU:(%d): partner sync "
18785895Syz147064 		    "%d -->%d\n", portp->lp_linkid, save_sync,
18792311Sseb 		    pl->PartnerOperPortState.bit.sync));
18800Sstevel@tonic-gate 		return (B_TRUE);
18810Sstevel@tonic-gate 	} else {
18820Sstevel@tonic-gate 		return (B_FALSE);
18830Sstevel@tonic-gate 	}
18840Sstevel@tonic-gate }
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate /*
18880Sstevel@tonic-gate  * update_selected - If any of the Partner parameters has
18890Sstevel@tonic-gate  *			changed from a previous value, then
18900Sstevel@tonic-gate  *			unselect the link from the aggregator.
18910Sstevel@tonic-gate  */
18920Sstevel@tonic-gate static boolean_t
update_selected(aggr_port_t * portp,lacp_t * lacp)18930Sstevel@tonic-gate update_selected(aggr_port_t *portp, lacp_t *lacp)
18940Sstevel@tonic-gate {
18950Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
18960Sstevel@tonic-gate 
18978275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
18980Sstevel@tonic-gate 
18990Sstevel@tonic-gate 	if ((pl->PartnerOperPortNum != ntohs(lacp->actor_info.port)) ||
19000Sstevel@tonic-gate 	    (pl->PartnerOperPortPriority !=
19010Sstevel@tonic-gate 	    ntohs(lacp->actor_info.port_priority)) ||
19020Sstevel@tonic-gate 	    (ether_cmp(&pl->PartnerOperSystem,
19030Sstevel@tonic-gate 	    &lacp->actor_info.system_id) != 0) ||
19040Sstevel@tonic-gate 	    (pl->PartnerOperSysPriority !=
19050Sstevel@tonic-gate 	    ntohs(lacp->actor_info.system_priority)) ||
19060Sstevel@tonic-gate 	    (pl->PartnerOperKey != ntohs(lacp->actor_info.key)) ||
19070Sstevel@tonic-gate 	    (pl->PartnerOperPortState.bit.aggregation !=
19080Sstevel@tonic-gate 	    lacp->actor_info.state.bit.aggregation)) {
19095895Syz147064 		AGGR_LACP_DBG(("update_selected:(%d): "
19105895Syz147064 		    "selected  %d-->%d\n", portp->lp_linkid, pl->sm.selected,
19112311Sseb 		    AGGR_UNSELECTED));
19120Sstevel@tonic-gate 
19131537Snd99603 		lacp_port_unselect(portp);
19140Sstevel@tonic-gate 		return (B_TRUE);
19150Sstevel@tonic-gate 	} else {
19160Sstevel@tonic-gate 		return (B_FALSE);
19170Sstevel@tonic-gate 	}
19180Sstevel@tonic-gate }
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate /*
19220Sstevel@tonic-gate  * update_default_selected - If any of the operational Partner parameters
19230Sstevel@tonic-gate  *			is different than that of the administrative values
19240Sstevel@tonic-gate  *			then unselect the link from the aggregator.
19250Sstevel@tonic-gate  */
19260Sstevel@tonic-gate static void
update_default_selected(aggr_port_t * portp)19270Sstevel@tonic-gate update_default_selected(aggr_port_t *portp)
19280Sstevel@tonic-gate {
19290Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
19300Sstevel@tonic-gate 
19318275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 	if ((pl->PartnerAdminPortNum != pl->PartnerOperPortNum) ||
19340Sstevel@tonic-gate 	    (pl->PartnerOperPortPriority != pl->PartnerAdminPortPriority) ||
19350Sstevel@tonic-gate 	    (ether_cmp(&pl->PartnerOperSystem, &pl->PartnerAdminSystem) != 0) ||
19360Sstevel@tonic-gate 	    (pl->PartnerOperSysPriority != pl->PartnerAdminSysPriority) ||
19370Sstevel@tonic-gate 	    (pl->PartnerOperKey != pl->PartnerAdminKey) ||
19380Sstevel@tonic-gate 	    (pl->PartnerOperPortState.bit.aggregation !=
19390Sstevel@tonic-gate 	    pl->PartnerAdminPortState.bit.aggregation)) {
19400Sstevel@tonic-gate 
19415895Syz147064 		AGGR_LACP_DBG(("update_default_selected:(%d): "
19425895Syz147064 		    "selected  %d-->%d\n", portp->lp_linkid,
19430Sstevel@tonic-gate 		    pl->sm.selected, AGGR_UNSELECTED));
19441537Snd99603 
19451537Snd99603 		lacp_port_unselect(portp);
19460Sstevel@tonic-gate 	}
19470Sstevel@tonic-gate }
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate /*
19510Sstevel@tonic-gate  * update_NTT - If any of the Partner values in the received LACPDU
19520Sstevel@tonic-gate  *			are different than that of the Actor operational
19530Sstevel@tonic-gate  *			values then set NTT to true.
19540Sstevel@tonic-gate  */
19550Sstevel@tonic-gate static void
update_NTT(aggr_port_t * portp,lacp_t * lacp)19560Sstevel@tonic-gate update_NTT(aggr_port_t *portp, lacp_t *lacp)
19570Sstevel@tonic-gate {
19580Sstevel@tonic-gate 	aggr_grp_t *aggrp = portp->lp_grp;
19590Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
19600Sstevel@tonic-gate 
19618275SEric Cheng 	ASSERT(MAC_PERIM_HELD(aggrp->lg_mh));
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate 	if ((pl->ActorPortNumber != ntohs(lacp->partner_info.port)) ||
19640Sstevel@tonic-gate 	    (pl->ActorPortPriority !=
19650Sstevel@tonic-gate 	    ntohs(lacp->partner_info.port_priority)) ||
19660Sstevel@tonic-gate 	    (ether_cmp(&aggrp->lg_addr,
19670Sstevel@tonic-gate 	    &lacp->partner_info.system_id) != 0) ||
19680Sstevel@tonic-gate 	    (aggrp->aggr.ActorSystemPriority !=
19690Sstevel@tonic-gate 	    ntohs(lacp->partner_info.system_priority)) ||
19700Sstevel@tonic-gate 	    (pl->ActorOperPortKey != ntohs(lacp->partner_info.key)) ||
19710Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.activity !=
19720Sstevel@tonic-gate 	    lacp->partner_info.state.bit.activity) ||
19730Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.timeout !=
19740Sstevel@tonic-gate 	    lacp->partner_info.state.bit.timeout) ||
19750Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.sync !=
19760Sstevel@tonic-gate 	    lacp->partner_info.state.bit.sync) ||
19770Sstevel@tonic-gate 	    (pl->ActorOperPortState.bit.aggregation !=
19780Sstevel@tonic-gate 	    lacp->partner_info.state.bit.aggregation)) {
19790Sstevel@tonic-gate 
19805895Syz147064 		AGGR_LACP_DBG(("update_NTT:(%d): NTT  %d-->%d\n",
19815895Syz147064 		    portp->lp_linkid, pl->NTT, B_TRUE));
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 		pl->NTT = B_TRUE;
19840Sstevel@tonic-gate 	}
19850Sstevel@tonic-gate }
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate /*
19880Sstevel@tonic-gate  * lacp_receive_sm - LACP receive state machine
19890Sstevel@tonic-gate  *
19900Sstevel@tonic-gate  * parameters:
19910Sstevel@tonic-gate  *      - portp - instance this applies to.
19920Sstevel@tonic-gate  *      - lacp - pointer in the case of a received LACPDU.
19930Sstevel@tonic-gate  *                This value is NULL if there is no LACPDU.
19940Sstevel@tonic-gate  *
19950Sstevel@tonic-gate  * invoked:
19960Sstevel@tonic-gate  *    - when initialization is needed
19970Sstevel@tonic-gate  *    - upon reception of an LACPDU. This is the common case.
19980Sstevel@tonic-gate  *    - every time the current_while_timer pops
19990Sstevel@tonic-gate  */
20000Sstevel@tonic-gate static void
lacp_receive_sm(aggr_port_t * portp,lacp_t * lacp)20010Sstevel@tonic-gate lacp_receive_sm(aggr_port_t *portp, lacp_t *lacp)
20020Sstevel@tonic-gate {
20030Sstevel@tonic-gate 	boolean_t sync_updated, selected_updated, save_activity;
20040Sstevel@tonic-gate 	aggr_lacp_port_t *pl = &portp->lp_lacp;
20050Sstevel@tonic-gate 	lacp_receive_state_t oldstate = pl->sm.receive_state;
20060Sstevel@tonic-gate 
20078275SEric Cheng 	ASSERT(MAC_PERIM_HELD(portp->lp_grp->lg_mh));
20080Sstevel@tonic-gate 
20090Sstevel@tonic-gate 	/* LACP_OFF state not in specification so check here.  */
20100Sstevel@tonic-gate 	if (!pl->sm.lacp_on)
20110Sstevel@tonic-gate 		return;
20120Sstevel@tonic-gate 
20130Sstevel@tonic-gate 	/* figure next state */
20140Sstevel@tonic-gate 	if (pl->sm.begin || pl->sm.port_moved) {
20150Sstevel@tonic-gate 		pl->sm.receive_state = LACP_INITIALIZE;
20160Sstevel@tonic-gate 	} else if (!pl->sm.port_enabled) {	/* DL_NOTE_LINK_DOWN */
20170Sstevel@tonic-gate 		pl->sm.receive_state = LACP_PORT_DISABLED;
20180Sstevel@tonic-gate 	} else if (!pl->sm.lacp_enabled) { /* DL_NOTE_AGGR_UNAVAIL */
20190Sstevel@tonic-gate 		pl->sm.receive_state =
20200Sstevel@tonic-gate 		    (pl->sm.receive_state == LACP_PORT_DISABLED) ?
20210Sstevel@tonic-gate 		    LACP_DISABLED : LACP_PORT_DISABLED;
20220Sstevel@tonic-gate 	} else if (lacp != NULL) {
20230Sstevel@tonic-gate 		if ((pl->sm.receive_state == LACP_EXPIRED) ||
20240Sstevel@tonic-gate 		    (pl->sm.receive_state == LACP_DEFAULTED)) {
20250Sstevel@tonic-gate 			pl->sm.receive_state = LACP_CURRENT;
20260Sstevel@tonic-gate 		}
20270Sstevel@tonic-gate 	} else if ((pl->sm.receive_state == LACP_CURRENT) &&
20280Sstevel@tonic-gate 	    (pl->current_while_timer.id == 0)) {
20290Sstevel@tonic-gate 		pl->sm.receive_state = LACP_EXPIRED;
20300Sstevel@tonic-gate 	} else if ((pl->sm.receive_state == LACP_EXPIRED) &&
20310Sstevel@tonic-gate 	    (pl->current_while_timer.id == 0)) {
20320Sstevel@tonic-gate 		pl->sm.receive_state = LACP_DEFAULTED;
20330Sstevel@tonic-gate 	}
20340Sstevel@tonic-gate 
20350Sstevel@tonic-gate 	if (!((lacp && (oldstate == LACP_CURRENT) &&
20360Sstevel@tonic-gate 	    (pl->sm.receive_state == LACP_CURRENT)))) {
20375895Syz147064 		AGGR_LACP_DBG(("lacp_receive_sm(%d):%s--->%s\n",
20385895Syz147064 		    portp->lp_linkid, lacp_receive_str[oldstate],
20390Sstevel@tonic-gate 		    lacp_receive_str[pl->sm.receive_state]));
20400Sstevel@tonic-gate 	}
20410Sstevel@tonic-gate 
20420Sstevel@tonic-gate 	switch (pl->sm.receive_state) {
20430Sstevel@tonic-gate 	case LACP_INITIALIZE:
20441537Snd99603 		lacp_port_unselect(portp);
20450Sstevel@tonic-gate 		record_Default(portp);
20460Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
20470Sstevel@tonic-gate 		pl->sm.port_moved = B_FALSE;
20480Sstevel@tonic-gate 		pl->sm.receive_state = LACP_PORT_DISABLED;
20490Sstevel@tonic-gate 		pl->sm.begin = B_FALSE;
20500Sstevel@tonic-gate 		lacp_receive_sm(portp, NULL);
20510Sstevel@tonic-gate 		break;
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 	case LACP_PORT_DISABLED:
20540Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
20550Sstevel@tonic-gate 		/*
20560Sstevel@tonic-gate 		 * Stop current_while_timer in case
20570Sstevel@tonic-gate 		 * we got here from link down
20580Sstevel@tonic-gate 		 */
20590Sstevel@tonic-gate 		stop_current_while_timer(portp);
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 		if (pl->sm.port_enabled && !pl->sm.lacp_enabled) {
20620Sstevel@tonic-gate 			pl->sm.receive_state = LACP_DISABLED;
20630Sstevel@tonic-gate 			lacp_receive_sm(portp, lacp);
20640Sstevel@tonic-gate 			/* We goto LACP_DISABLED state */
20650Sstevel@tonic-gate 			break;
20660Sstevel@tonic-gate 		} else if (pl->sm.port_enabled && pl->sm.lacp_enabled) {
20670Sstevel@tonic-gate 			pl->sm.receive_state = LACP_EXPIRED;
20680Sstevel@tonic-gate 			/*
20690Sstevel@tonic-gate 			 * FALL THROUGH TO LACP_EXPIRED CASE:
20700Sstevel@tonic-gate 			 * We have no way of knowing if we get into
20710Sstevel@tonic-gate 			 * lacp_receive_sm() from a  current_while_timer
20720Sstevel@tonic-gate 			 * expiring as it has never been kicked off yet!
20730Sstevel@tonic-gate 			 */
20740Sstevel@tonic-gate 		} else {
20750Sstevel@tonic-gate 			/* We stay in LACP_PORT_DISABLED state */
20760Sstevel@tonic-gate 			break;
20770Sstevel@tonic-gate 		}
20780Sstevel@tonic-gate 		/* LACP_PORT_DISABLED -> LACP_EXPIRED */
20790Sstevel@tonic-gate 		/* FALLTHROUGH */
20800Sstevel@tonic-gate 
20810Sstevel@tonic-gate 	case LACP_EXPIRED:
20820Sstevel@tonic-gate 		/*
20830Sstevel@tonic-gate 		 * Arrives here from LACP_PORT_DISABLED state as well as
20840Sstevel@tonic-gate 		 * as well as current_while_timer expiring.
20850Sstevel@tonic-gate 		 */
20860Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_FALSE;
20870Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.timeout = B_TRUE;
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_TRUE;
20900Sstevel@tonic-gate 		start_current_while_timer(portp, SHORT_TIMEOUT_TIME);
20910Sstevel@tonic-gate 		lacp_periodic_sm(portp);
20920Sstevel@tonic-gate 		break;
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	case LACP_DISABLED:
20950Sstevel@tonic-gate 		/*
20960Sstevel@tonic-gate 		 * This is the normal state for recv_sm when LACP_OFF
20970Sstevel@tonic-gate 		 * is set or the NIC is in half duplex mode.
20980Sstevel@tonic-gate 		 */
20991537Snd99603 		lacp_port_unselect(portp);
21000Sstevel@tonic-gate 		record_Default(portp);
21010Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.aggregation = B_FALSE;
21020Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21030Sstevel@tonic-gate 		break;
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	case LACP_DEFAULTED:
21060Sstevel@tonic-gate 		/*
21070Sstevel@tonic-gate 		 * Current_while_timer expired a second time.
21080Sstevel@tonic-gate 		 */
21090Sstevel@tonic-gate 		update_default_selected(portp);
21100Sstevel@tonic-gate 		record_Default(portp);	/* overwrite Partner Oper val */
21110Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21120Sstevel@tonic-gate 		pl->PartnerOperPortState.bit.sync = B_TRUE;
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 		lacp_selection_logic(portp);
21150Sstevel@tonic-gate 		lacp_mux_sm(portp);
21160Sstevel@tonic-gate 		break;
21170Sstevel@tonic-gate 
21180Sstevel@tonic-gate 	case LACP_CURRENT:
21190Sstevel@tonic-gate 		/*
21200Sstevel@tonic-gate 		 * Reception of LACPDU
21210Sstevel@tonic-gate 		 */
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate 		if (!lacp) /* no LACPDU so current_while_timer popped */
21240Sstevel@tonic-gate 			break;
21250Sstevel@tonic-gate 
21265895Syz147064 		AGGR_LACP_DBG(("lacp_receive_sm: (%d): LACPDU received:\n",
21275895Syz147064 		    portp->lp_linkid));
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 		/*
21300Sstevel@tonic-gate 		 * Validate Actor_Information_Length,
21310Sstevel@tonic-gate 		 * Partner_Information_Length, Collector_Information_Length,
21320Sstevel@tonic-gate 		 * and Terminator_Length fields.
21330Sstevel@tonic-gate 		 */
21340Sstevel@tonic-gate 		if (!valid_lacp_pdu(portp, lacp)) {
21355895Syz147064 			AGGR_LACP_DBG(("lacp_receive_sm (%d): "
21360Sstevel@tonic-gate 			    "Invalid LACPDU received\n",
21375895Syz147064 			    portp->lp_linkid));
21380Sstevel@tonic-gate 			break;
21390Sstevel@tonic-gate 		}
21400Sstevel@tonic-gate 
21410Sstevel@tonic-gate 		save_activity = pl->PartnerOperPortState.bit.activity;
21420Sstevel@tonic-gate 		selected_updated = update_selected(portp, lacp);
21430Sstevel@tonic-gate 		update_NTT(portp, lacp);
21440Sstevel@tonic-gate 		sync_updated = record_PDU(portp, lacp);
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate 		pl->ActorOperPortState.bit.expired = B_FALSE;
21470Sstevel@tonic-gate 
21480Sstevel@tonic-gate 		if (selected_updated) {
21490Sstevel@tonic-gate 			lacp_selection_logic(portp);
21500Sstevel@tonic-gate 			lacp_mux_sm(portp);
21510Sstevel@tonic-gate 		} else if (sync_updated) {
21520Sstevel@tonic-gate 			lacp_mux_sm(portp);
21530Sstevel@tonic-gate 		}
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 		/*
21560Sstevel@tonic-gate 		 * If the periodic timer value bit has been modified
21570Sstevel@tonic-gate 		 * or the partner activity bit has been changed then
21580Sstevel@tonic-gate 		 * we need to respectively:
21590Sstevel@tonic-gate 		 *  - restart the timer with the proper timeout value.
21600Sstevel@tonic-gate 		 *  - possibly enable/disable transmission of LACPDUs.
21610Sstevel@tonic-gate 		 */
21620Sstevel@tonic-gate 		if ((pl->PartnerOperPortState.bit.timeout &&
21630Sstevel@tonic-gate 		    (pl->periodic_timer.val != FAST_PERIODIC_TIME)) ||
21640Sstevel@tonic-gate 		    (!pl->PartnerOperPortState.bit.timeout &&
21650Sstevel@tonic-gate 		    (pl->periodic_timer.val != SLOW_PERIODIC_TIME)) ||
21660Sstevel@tonic-gate 		    (pl->PartnerOperPortState.bit.activity !=
21670Sstevel@tonic-gate 		    save_activity)) {
21680Sstevel@tonic-gate 			lacp_periodic_sm(portp);
21690Sstevel@tonic-gate 		}
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 		stop_current_while_timer(portp);
21720Sstevel@tonic-gate 		/* Check if we need to transmit an LACPDU */
21730Sstevel@tonic-gate 		if (pl->NTT)
21740Sstevel@tonic-gate 			lacp_xmit_sm(portp);
21750Sstevel@tonic-gate 		start_current_while_timer(portp, 0);
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 		break;
21780Sstevel@tonic-gate 	}
21790Sstevel@tonic-gate }
21800Sstevel@tonic-gate 
21810Sstevel@tonic-gate static void
aggr_set_coll_dist(aggr_port_t * portp,boolean_t enable)21820Sstevel@tonic-gate aggr_set_coll_dist(aggr_port_t *portp, boolean_t enable)
21830Sstevel@tonic-gate {
21848275SEric Cheng 	mac_perim_handle_t mph;
21850Sstevel@tonic-gate 
21865895Syz147064 	AGGR_LACP_DBG(("AGGR_SET_COLL_DIST_TYPE: (%d) %s\n",
21875895Syz147064 	    portp->lp_linkid, enable ? "ENABLED" : "DISABLED"));
21880Sstevel@tonic-gate 
21898275SEric Cheng 	mac_perim_enter_by_mh(portp->lp_mh, &mph);
21900Sstevel@tonic-gate 	if (!enable) {
21910Sstevel@tonic-gate 		/*
21920Sstevel@tonic-gate 		 * Turn OFF Collector_Distributor.
21930Sstevel@tonic-gate 		 */
21940Sstevel@tonic-gate 		portp->lp_collector_enabled = B_FALSE;
21950Sstevel@tonic-gate 		aggr_send_port_disable(portp);
21968275SEric Cheng 		goto done;
21970Sstevel@tonic-gate 	}
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	/*
22000Sstevel@tonic-gate 	 * Turn ON Collector_Distributor.
22010Sstevel@tonic-gate 	 */
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 	if (!portp->lp_lacp.sm.lacp_on || (portp->lp_lacp.sm.lacp_on &&
22040Sstevel@tonic-gate 	    (portp->lp_lacp.sm.mux_state == LACP_COLLECTING_DISTRIBUTING))) {
22050Sstevel@tonic-gate 		/* Port is compatible and can be aggregated */
22060Sstevel@tonic-gate 		portp->lp_collector_enabled = B_TRUE;
22070Sstevel@tonic-gate 		aggr_send_port_enable(portp);
22080Sstevel@tonic-gate 	}
22098275SEric Cheng 
22108275SEric Cheng done:
22118275SEric Cheng 	mac_perim_exit(mph);
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate /*
22158275SEric Cheng  * Because the LACP packet processing needs to enter the aggr's mac perimeter
22168275SEric Cheng  * and that would potentially cause a deadlock with the thread in which the
22178275SEric Cheng  * grp/port is deleted, we defer the packet process to a worker thread. Here
22188275SEric Cheng  * we only enqueue the received Marker or LACPDU for later processing.
22190Sstevel@tonic-gate  */
22200Sstevel@tonic-gate void
aggr_lacp_rx_enqueue(aggr_port_t * portp,mblk_t * dmp)22218275SEric Cheng aggr_lacp_rx_enqueue(aggr_port_t *portp, mblk_t *dmp)
22220Sstevel@tonic-gate {
22238275SEric Cheng 	aggr_grp_t *grp = portp->lp_grp;
22240Sstevel@tonic-gate 	lacp_t	*lacp;
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 	dmp->b_rptr += sizeof (struct ether_header);
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	if (MBLKL(dmp) < sizeof (lacp_t)) {
22290Sstevel@tonic-gate 		freemsg(dmp);
22300Sstevel@tonic-gate 		return;
22310Sstevel@tonic-gate 	}
22320Sstevel@tonic-gate 
22330Sstevel@tonic-gate 	lacp = (lacp_t *)dmp->b_rptr;
22348275SEric Cheng 	if (lacp->subtype != LACP_SUBTYPE && lacp->subtype != MARKER_SUBTYPE) {
22358275SEric Cheng 		AGGR_LACP_DBG(("aggr_lacp_rx_enqueue: (%d): "
22368275SEric Cheng 		    "Unknown Slow Protocol type %d\n",
22378275SEric Cheng 		    portp->lp_linkid, lacp->subtype));
22388275SEric Cheng 		freemsg(dmp);
22398275SEric Cheng 		return;
22408275SEric Cheng 	}
22410Sstevel@tonic-gate 
22428275SEric Cheng 	mutex_enter(&grp->lg_lacp_lock);
22438275SEric Cheng 
22448275SEric Cheng 	/*
22458275SEric Cheng 	 * If the lg_lacp_done is set, this aggregation is in the process of
22468275SEric Cheng 	 * being deleted, return directly.
22478275SEric Cheng 	 */
22488275SEric Cheng 	if (grp->lg_lacp_done) {
22498275SEric Cheng 		mutex_exit(&grp->lg_lacp_lock);
22508275SEric Cheng 		freemsg(dmp);
22518275SEric Cheng 		return;
22528275SEric Cheng 	}
22538275SEric Cheng 
22548275SEric Cheng 	if (grp->lg_lacp_tail == NULL) {
22558275SEric Cheng 		grp->lg_lacp_head = grp->lg_lacp_tail = dmp;
22568275SEric Cheng 	} else {
22578275SEric Cheng 		grp->lg_lacp_tail->b_next = dmp;
22588275SEric Cheng 		grp->lg_lacp_tail = dmp;
22598275SEric Cheng 	}
22608275SEric Cheng 
22618275SEric Cheng 	/*
22628275SEric Cheng 	 * Hold a reference of the port so that the port won't be freed when it
22638275SEric Cheng 	 * is removed from the aggr. The b_prev field is borrowed to save the
22648275SEric Cheng 	 * port information.
22658275SEric Cheng 	 */
22668275SEric Cheng 	AGGR_PORT_REFHOLD(portp);
22678275SEric Cheng 	dmp->b_prev = (mblk_t *)portp;
22688275SEric Cheng 	cv_broadcast(&grp->lg_lacp_cv);
22698275SEric Cheng 	mutex_exit(&grp->lg_lacp_lock);
22708275SEric Cheng }
22718275SEric Cheng 
22728275SEric Cheng static void
aggr_lacp_rx(mblk_t * dmp)22738275SEric Cheng aggr_lacp_rx(mblk_t *dmp)
22748275SEric Cheng {
22758275SEric Cheng 	aggr_port_t *portp = (aggr_port_t *)dmp->b_prev;
22768275SEric Cheng 	mac_perim_handle_t mph;
22778275SEric Cheng 	lacp_t	*lacp;
22788275SEric Cheng 
22798275SEric Cheng 	dmp->b_prev = NULL;
22808275SEric Cheng 
22818275SEric Cheng 	mac_perim_enter_by_mh(portp->lp_grp->lg_mh, &mph);
22828275SEric Cheng 	if (portp->lp_closing)
22838275SEric Cheng 		goto done;
22848275SEric Cheng 
22858275SEric Cheng 	lacp = (lacp_t *)dmp->b_rptr;
22860Sstevel@tonic-gate 	switch (lacp->subtype) {
22870Sstevel@tonic-gate 	case LACP_SUBTYPE:
22885895Syz147064 		AGGR_LACP_DBG(("aggr_lacp_rx:(%d): LACPDU received.\n",
22895895Syz147064 		    portp->lp_linkid));
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate 		if (!portp->lp_lacp.sm.lacp_on) {
22920Sstevel@tonic-gate 			break;
22930Sstevel@tonic-gate 		}
22940Sstevel@tonic-gate 		lacp_receive_sm(portp, lacp);
22950Sstevel@tonic-gate 		break;
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	case MARKER_SUBTYPE:
22985895Syz147064 		AGGR_LACP_DBG(("aggr_lacp_rx:(%d): Marker Packet received.\n",
22995895Syz147064 		    portp->lp_linkid));
23000Sstevel@tonic-gate 
23018275SEric Cheng 		if (receive_marker_pdu(portp, dmp) != 0)
23028275SEric Cheng 			break;
23030Sstevel@tonic-gate 
230411878SVenu.Iyer@Sun.COM 		/* Send the packet over the first TX ring */
230511878SVenu.Iyer@Sun.COM 		dmp = mac_hwring_send_priv(portp->lp_mch,
230611878SVenu.Iyer@Sun.COM 		    portp->lp_tx_rings[0], dmp);
230711878SVenu.Iyer@Sun.COM 		if (dmp != NULL)
230811878SVenu.Iyer@Sun.COM 			freemsg(dmp);
23098275SEric Cheng 		mac_perim_exit(mph);
23108275SEric Cheng 		AGGR_PORT_REFRELE(portp);
23118275SEric Cheng 		return;
23120Sstevel@tonic-gate 	}
23130Sstevel@tonic-gate 
23148275SEric Cheng done:
23158275SEric Cheng 	mac_perim_exit(mph);
23168275SEric Cheng 	AGGR_PORT_REFRELE(portp);
23170Sstevel@tonic-gate 	freemsg(dmp);
23180Sstevel@tonic-gate }
23198275SEric Cheng 
23208275SEric Cheng void
aggr_lacp_rx_thread(void * arg)23218275SEric Cheng aggr_lacp_rx_thread(void *arg)
23228275SEric Cheng {
23238275SEric Cheng 	callb_cpr_t	cprinfo;
23248275SEric Cheng 	aggr_grp_t	*grp = (aggr_grp_t *)arg;
23258275SEric Cheng 	aggr_port_t	*port;
23268275SEric Cheng 	mblk_t		*mp, *nextmp;
23278275SEric Cheng 
23288275SEric Cheng 	CALLB_CPR_INIT(&cprinfo, &grp->lg_lacp_lock, callb_generic_cpr,
23298275SEric Cheng 	    "aggr_lacp_rx_thread");
23308275SEric Cheng 
23318275SEric Cheng 	mutex_enter(&grp->lg_lacp_lock);
23328275SEric Cheng 
23338275SEric Cheng 	/*
23348275SEric Cheng 	 * Quit the thread if the grp is deleted.
23358275SEric Cheng 	 */
23368275SEric Cheng 	while (!grp->lg_lacp_done) {
23378275SEric Cheng 		if ((mp = grp->lg_lacp_head) == NULL) {
23388275SEric Cheng 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
23398275SEric Cheng 			cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock);
23408275SEric Cheng 			CALLB_CPR_SAFE_END(&cprinfo, &grp->lg_lacp_lock);
23418275SEric Cheng 			continue;
23428275SEric Cheng 		}
23438275SEric Cheng 
23448275SEric Cheng 		grp->lg_lacp_head = grp->lg_lacp_tail = NULL;
23458275SEric Cheng 		mutex_exit(&grp->lg_lacp_lock);
23468275SEric Cheng 
23478275SEric Cheng 		while (mp != NULL) {
23488275SEric Cheng 			nextmp = mp->b_next;
23498275SEric Cheng 			mp->b_next = NULL;
23508275SEric Cheng 			aggr_lacp_rx(mp);
23518275SEric Cheng 			mp = nextmp;
23528275SEric Cheng 		}
23538275SEric Cheng 		mutex_enter(&grp->lg_lacp_lock);
23548275SEric Cheng 	}
23558275SEric Cheng 
23568275SEric Cheng 	/*
23578275SEric Cheng 	 * The grp is being destroyed, simply free all of the LACP messages
23588275SEric Cheng 	 * left in the queue which did not have the chance to be processed.
23598275SEric Cheng 	 * We cannot use freemsgchain() here since we need to clear the
23608275SEric Cheng 	 * b_prev field.
23618275SEric Cheng 	 */
2362*12634SPeter.Memishian@Sun.COM 	for (mp = grp->lg_lacp_head; mp != NULL; mp = nextmp) {
23638275SEric Cheng 		port = (aggr_port_t *)mp->b_prev;
23648275SEric Cheng 		AGGR_PORT_REFRELE(port);
23658275SEric Cheng 		nextmp = mp->b_next;
23668275SEric Cheng 		mp->b_next = NULL;
23678275SEric Cheng 		mp->b_prev = NULL;
23688275SEric Cheng 		freemsg(mp);
23698275SEric Cheng 	}
23708275SEric Cheng 
23718275SEric Cheng 	grp->lg_lacp_head = grp->lg_lacp_tail = NULL;
23728275SEric Cheng 	grp->lg_lacp_rx_thread = NULL;
23738275SEric Cheng 	cv_broadcast(&grp->lg_lacp_cv);
23748275SEric Cheng 	CALLB_CPR_EXIT(&cprinfo);
23758275SEric Cheng 	thread_exit();
23768275SEric Cheng }
2377