xref: /onnv-gate/usr/src/uts/common/io/aggr/aggr_port.c (revision 11878:ac93462db6d7)
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
51804Sericheng  * Common Development and Distribution License (the "License").
61804Sericheng  * 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*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * IEEE 802.3ad Link Aggregation - Link Aggregation MAC ports.
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * Implements the functions needed to manage the MAC ports that are
300Sstevel@tonic-gate  * part of Link Aggregation groups.
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/conf.h>
360Sstevel@tonic-gate #include <sys/cmn_err.h>
375102Syz147064 #include <sys/id_space.h>
380Sstevel@tonic-gate #include <sys/list.h>
390Sstevel@tonic-gate #include <sys/ksynch.h>
400Sstevel@tonic-gate #include <sys/kmem.h>
410Sstevel@tonic-gate #include <sys/stream.h>
420Sstevel@tonic-gate #include <sys/modctl.h>
430Sstevel@tonic-gate #include <sys/ddi.h>
440Sstevel@tonic-gate #include <sys/sunddi.h>
450Sstevel@tonic-gate #include <sys/atomic.h>
460Sstevel@tonic-gate #include <sys/stat.h>
470Sstevel@tonic-gate #include <sys/sdt.h>
482311Sseb #include <sys/dlpi.h>
498275SEric Cheng #include <sys/dls.h>
500Sstevel@tonic-gate #include <sys/aggr.h>
510Sstevel@tonic-gate #include <sys/aggr_impl.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static kmem_cache_t *aggr_port_cache;
545102Syz147064 static id_space_t *aggr_portids;
555102Syz147064 
560Sstevel@tonic-gate static void aggr_port_notify_cb(void *, mac_notify_type_t);
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*ARGSUSED*/
590Sstevel@tonic-gate static int
aggr_port_constructor(void * buf,void * arg,int kmflag)600Sstevel@tonic-gate aggr_port_constructor(void *buf, void *arg, int kmflag)
610Sstevel@tonic-gate {
620Sstevel@tonic-gate 	bzero(buf, sizeof (aggr_port_t));
630Sstevel@tonic-gate 	return (0);
640Sstevel@tonic-gate }
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /*ARGSUSED*/
670Sstevel@tonic-gate static void
aggr_port_destructor(void * buf,void * arg)680Sstevel@tonic-gate aggr_port_destructor(void *buf, void *arg)
690Sstevel@tonic-gate {
700Sstevel@tonic-gate 	aggr_port_t *port = buf;
710Sstevel@tonic-gate 
728275SEric Cheng 	ASSERT(port->lp_mnh == NULL);
738275SEric Cheng 	ASSERT(port->lp_mphp == NULL);
74*11878SVenu.Iyer@Sun.COM 	ASSERT(!port->lp_rx_grp_added && !port->lp_tx_grp_added);
758275SEric Cheng 	ASSERT(port->lp_hwgh == NULL);
760Sstevel@tonic-gate }
770Sstevel@tonic-gate 
780Sstevel@tonic-gate void
aggr_port_init(void)790Sstevel@tonic-gate aggr_port_init(void)
800Sstevel@tonic-gate {
810Sstevel@tonic-gate 	aggr_port_cache = kmem_cache_create("aggr_port_cache",
820Sstevel@tonic-gate 	    sizeof (aggr_port_t), 0, aggr_port_constructor,
830Sstevel@tonic-gate 	    aggr_port_destructor, NULL, NULL, NULL, 0);
845102Syz147064 
855102Syz147064 	/*
865102Syz147064 	 * Allocate a id space to manage port identification. The range of
875102Syz147064 	 * the arena will be from 1 to UINT16_MAX, because the LACP protocol
885895Syz147064 	 * specifies 16-bit unique identification.
895102Syz147064 	 */
905102Syz147064 	aggr_portids = id_space_create("aggr_portids", 1, UINT16_MAX);
915102Syz147064 	ASSERT(aggr_portids != NULL);
920Sstevel@tonic-gate }
930Sstevel@tonic-gate 
941804Sericheng void
aggr_port_fini(void)950Sstevel@tonic-gate aggr_port_fini(void)
960Sstevel@tonic-gate {
970Sstevel@tonic-gate 	/*
980Sstevel@tonic-gate 	 * This function is called only after all groups have been
990Sstevel@tonic-gate 	 * freed. This ensures that there are no remaining allocated
1000Sstevel@tonic-gate 	 * ports when this function is invoked.
1010Sstevel@tonic-gate 	 */
1020Sstevel@tonic-gate 	kmem_cache_destroy(aggr_port_cache);
1035102Syz147064 	id_space_destroy(aggr_portids);
1040Sstevel@tonic-gate }
1050Sstevel@tonic-gate 
1068275SEric Cheng /* ARGSUSED */
1072163Syz147064 void
aggr_port_init_callbacks(aggr_port_t * port)1082163Syz147064 aggr_port_init_callbacks(aggr_port_t *port)
1092163Syz147064 {
1102163Syz147064 	/* add the port's receive callback */
1118275SEric Cheng 	port->lp_mnh = mac_notify_add(port->lp_mh, aggr_port_notify_cb, port);
1128275SEric Cheng 	/*
1138275SEric Cheng 	 * Hold a reference of the grp and the port and this reference will
114*11878SVenu.Iyer@Sun.COM 	 * be released when the thread exits.
1158275SEric Cheng 	 *
1168275SEric Cheng 	 * The reference on the port is used for aggr_port_delete() to
1178275SEric Cheng 	 * continue without waiting for the thread to exit; the reference
1188275SEric Cheng 	 * on the grp is used for aggr_grp_delete() to wait for the thread
1198275SEric Cheng 	 * to exit before calling mac_unregister().
1208275SEric Cheng 	 *
1218275SEric Cheng 	 * Note that these references will be released either in
1228275SEric Cheng 	 * aggr_port_delete() when mac_notify_remove() succeeds, or in
1238275SEric Cheng 	 * the aggr_port_notify_cb() callback when the port is deleted
1248275SEric Cheng 	 * (lp_closing is set).
1258275SEric Cheng 	 */
1268275SEric Cheng 	aggr_grp_port_hold(port);
1272163Syz147064 }
1282163Syz147064 
1298275SEric Cheng /* ARGSUSED */
1300Sstevel@tonic-gate int
aggr_port_create(aggr_grp_t * grp,const datalink_id_t linkid,boolean_t force,aggr_port_t ** pp)1318275SEric Cheng aggr_port_create(aggr_grp_t *grp, const datalink_id_t linkid, boolean_t force,
1328275SEric Cheng     aggr_port_t **pp)
1330Sstevel@tonic-gate {
1340Sstevel@tonic-gate 	int err;
1350Sstevel@tonic-gate 	mac_handle_t mh;
1368275SEric Cheng 	mac_client_handle_t mch = NULL;
1370Sstevel@tonic-gate 	aggr_port_t *port;
1385102Syz147064 	uint16_t portid;
1390Sstevel@tonic-gate 	uint_t i;
1405895Syz147064 	boolean_t no_link_update = B_FALSE;
1412311Sseb 	const mac_info_t *mip;
1425895Syz147064 	uint32_t note;
1435895Syz147064 	uint32_t margin;
1448275SEric Cheng 	char client_name[MAXNAMELEN];
1458275SEric Cheng 	char aggr_name[MAXNAMELEN];
1468275SEric Cheng 	char port_name[MAXNAMELEN];
1478275SEric Cheng 	mac_diag_t diag;
1488275SEric Cheng 	mac_unicast_handle_t mah;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	*pp = NULL;
1510Sstevel@tonic-gate 
1525895Syz147064 	if ((err = mac_open_by_linkid(linkid, &mh)) != 0)
1530Sstevel@tonic-gate 		return (err);
1540Sstevel@tonic-gate 
1552311Sseb 	mip = mac_info(mh);
1563147Sxc151355 	if (mip->mi_media != DL_ETHER || mip->mi_nativemedia != DL_ETHER) {
1575895Syz147064 		err = EINVAL;
1585895Syz147064 		goto fail;
1595895Syz147064 	}
1605895Syz147064 
1615895Syz147064 	/*
1625895Syz147064 	 * If the underlying MAC does not support link update notification, it
1635895Syz147064 	 * can only be aggregated if `force' is set.  This is because aggr
1645895Syz147064 	 * depends on link notifications to attach ports whose link is up.
1655895Syz147064 	 */
1665895Syz147064 	note = mac_no_notification(mh);
1675895Syz147064 	if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
1685895Syz147064 		no_link_update = B_TRUE;
1695895Syz147064 		if (!force) {
1705895Syz147064 			/*
1715895Syz147064 			 * We borrow this error code to indicate that link
1725895Syz147064 			 * notification is not supported.
1735895Syz147064 			 */
1745895Syz147064 			err = ENETDOWN;
1755895Syz147064 			goto fail;
1765895Syz147064 		}
1772311Sseb 	}
1782311Sseb 
1798275SEric Cheng 	if (((err = dls_mgmt_get_linkinfo(grp->lg_linkid,
1808275SEric Cheng 	    aggr_name, NULL, NULL, NULL)) != 0) ||
1818275SEric Cheng 	    ((err = dls_mgmt_get_linkinfo(linkid, port_name,
1828275SEric Cheng 	    NULL, NULL, NULL)) != 0)) {
1838275SEric Cheng 		goto fail;
1848275SEric Cheng 	}
1858275SEric Cheng 
1868275SEric Cheng 	(void) snprintf(client_name, MAXNAMELEN, "%s-%s", aggr_name, port_name);
1878275SEric Cheng 	if ((err = mac_client_open(mh, &mch, client_name,
1889024SVenu.Iyer@Sun.COM 	    MAC_OPEN_FLAGS_IS_AGGR_PORT | MAC_OPEN_FLAGS_EXCLUSIVE)) != 0) {
1898275SEric Cheng 		goto fail;
1908275SEric Cheng 	}
1918275SEric Cheng 
1925102Syz147064 	if ((portid = (uint16_t)id_alloc(aggr_portids)) == 0) {
1935895Syz147064 		err = ENOMEM;
1945895Syz147064 		goto fail;
1955895Syz147064 	}
1965895Syz147064 
1975895Syz147064 	/*
1985895Syz147064 	 * As the underlying mac's current margin size is used to determine
1995895Syz147064 	 * the margin size of the aggregation itself, request the underlying
2005895Syz147064 	 * mac not to change to a smaller size.
2015895Syz147064 	 */
2025895Syz147064 	if ((err = mac_margin_add(mh, &margin, B_TRUE)) != 0) {
2035895Syz147064 		id_free(aggr_portids, portid);
2045895Syz147064 		goto fail;
2055102Syz147064 	}
2065102Syz147064 
2079024SVenu.Iyer@Sun.COM 	if ((err = mac_unicast_add(mch, NULL, MAC_UNICAST_PRIMARY |
2089024SVenu.Iyer@Sun.COM 	    MAC_UNICAST_DISABLE_TX_VID_CHECK, &mah, 0, &diag)) != 0) {
2095895Syz147064 		VERIFY(mac_margin_remove(mh, margin) == 0);
2105102Syz147064 		id_free(aggr_portids, portid);
2115895Syz147064 		goto fail;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	port = kmem_cache_alloc(aggr_port_cache, KM_SLEEP);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	port->lp_refs = 1;
2170Sstevel@tonic-gate 	port->lp_next = NULL;
2180Sstevel@tonic-gate 	port->lp_mh = mh;
2198275SEric Cheng 	port->lp_mch = mch;
2202311Sseb 	port->lp_mip = mip;
2215895Syz147064 	port->lp_linkid = linkid;
2228275SEric Cheng 	port->lp_closing = B_FALSE;
2238275SEric Cheng 	port->lp_mah = mah;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/* get the port's original MAC address */
2268275SEric Cheng 	mac_unicast_primary_get(port->lp_mh, port->lp_addr);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/* initialize state */
2290Sstevel@tonic-gate 	port->lp_state = AGGR_PORT_STATE_STANDBY;
2300Sstevel@tonic-gate 	port->lp_link_state = LINK_STATE_UNKNOWN;
2310Sstevel@tonic-gate 	port->lp_ifspeed = 0;
2320Sstevel@tonic-gate 	port->lp_link_duplex = LINK_DUPLEX_UNKNOWN;
2330Sstevel@tonic-gate 	port->lp_started = B_FALSE;
2340Sstevel@tonic-gate 	port->lp_tx_enabled = B_FALSE;
2350Sstevel@tonic-gate 	port->lp_promisc_on = B_FALSE;
2365895Syz147064 	port->lp_no_link_update = no_link_update;
2375102Syz147064 	port->lp_portid = portid;
2385895Syz147064 	port->lp_margin = margin;
2398275SEric Cheng 	port->lp_prom_addr = NULL;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	/*
2420Sstevel@tonic-gate 	 * Save the current statistics of the port. They will be used
2435895Syz147064 	 * later by aggr_m_stats() when aggregating the statistics of
2445895Syz147064 	 * the constituent ports.
2450Sstevel@tonic-gate 	 */
2460Sstevel@tonic-gate 	for (i = 0; i < MAC_NSTAT; i++) {
2472311Sseb 		port->lp_stat[i] =
2482311Sseb 		    aggr_port_stat(port, i + MAC_STAT_MIN);
2492311Sseb 	}
2502311Sseb 	for (i = 0; i < ETHER_NSTAT; i++) {
2512311Sseb 		port->lp_ether_stat[i] =
2522311Sseb 		    aggr_port_stat(port, i + MACTYPE_STAT_MIN);
2530Sstevel@tonic-gate 	}
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/* LACP related state */
2560Sstevel@tonic-gate 	port->lp_collector_enabled = B_FALSE;
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	*pp = port;
2590Sstevel@tonic-gate 	return (0);
2605895Syz147064 
2615895Syz147064 fail:
2628275SEric Cheng 	if (mch != NULL)
2638275SEric Cheng 		mac_client_close(mch, MAC_CLOSE_FLAGS_EXCLUSIVE);
2645895Syz147064 	mac_close(mh);
2655895Syz147064 	return (err);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate void
aggr_port_delete(aggr_port_t * port)2690Sstevel@tonic-gate aggr_port_delete(aggr_port_t *port)
2700Sstevel@tonic-gate {
2718275SEric Cheng 	aggr_lacp_port_t *pl = &port->lp_lacp;
2728275SEric Cheng 
2738275SEric Cheng 	ASSERT(port->lp_mphp == NULL);
2748275SEric Cheng 	ASSERT(!port->lp_promisc_on);
2758275SEric Cheng 
2768275SEric Cheng 	port->lp_closing = B_TRUE;
2778275SEric Cheng 
2785895Syz147064 	VERIFY(mac_margin_remove(port->lp_mh, port->lp_margin) == 0);
2798275SEric Cheng 	mac_rx_clear(port->lp_mch);
2808275SEric Cheng 	/*
2818275SEric Cheng 	 * If the notification callback is already in process and waiting for
2828275SEric Cheng 	 * the aggr grp's mac perimeter, don't wait (otherwise there would be
2838275SEric Cheng 	 * deadlock). Otherwise, if mac_notify_remove() succeeds, we can
2848275SEric Cheng 	 * release the reference held when mac_notify_add() is called.
2858275SEric Cheng 	 */
2868275SEric Cheng 	if ((port->lp_mnh != NULL) &&
2878275SEric Cheng 	    (mac_notify_remove(port->lp_mnh, B_FALSE) == 0)) {
2888275SEric Cheng 		aggr_grp_port_rele(port);
2898275SEric Cheng 	}
2908275SEric Cheng 	port->lp_mnh = NULL;
2918275SEric Cheng 
2928275SEric Cheng 	/*
2938275SEric Cheng 	 * Inform the the port lacp timer thread to exit. Note that waiting
2948275SEric Cheng 	 * for the thread to exit may cause deadlock since that thread may
2958275SEric Cheng 	 * need to enter into the mac perimeter which we are currently in.
2968275SEric Cheng 	 * It is fine to continue without waiting though since that thread
2978275SEric Cheng 	 * is holding a reference of the port.
2988275SEric Cheng 	 */
2998275SEric Cheng 	mutex_enter(&pl->lacp_timer_lock);
3008275SEric Cheng 	pl->lacp_timer_bits |= LACP_THREAD_EXIT;
3018275SEric Cheng 	cv_broadcast(&pl->lacp_timer_cv);
3028275SEric Cheng 	mutex_exit(&pl->lacp_timer_lock);
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	/*
3050Sstevel@tonic-gate 	 * Restore the port MAC address. Note it is called after the
3060Sstevel@tonic-gate 	 * port's notification callback being removed. This prevent
3070Sstevel@tonic-gate 	 * port's MAC_NOTE_UNICST notify callback function being called.
3080Sstevel@tonic-gate 	 */
3098275SEric Cheng 	(void) mac_unicast_primary_set(port->lp_mh, port->lp_addr);
3108603SGirish.Moodalbail@Sun.COM 	if (port->lp_mah != NULL)
3118603SGirish.Moodalbail@Sun.COM 		(void) mac_unicast_remove(port->lp_mch, port->lp_mah);
3128275SEric Cheng 	mac_client_close(port->lp_mch, MAC_CLOSE_FLAGS_EXCLUSIVE);
3130Sstevel@tonic-gate 	mac_close(port->lp_mh);
3140Sstevel@tonic-gate 	AGGR_PORT_REFRELE(port);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate void
aggr_port_free(aggr_port_t * port)3180Sstevel@tonic-gate aggr_port_free(aggr_port_t *port)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	ASSERT(port->lp_refs == 0);
3210Sstevel@tonic-gate 	if (port->lp_grp != NULL)
3220Sstevel@tonic-gate 		AGGR_GRP_REFRELE(port->lp_grp);
3230Sstevel@tonic-gate 	port->lp_grp = NULL;
3245102Syz147064 	id_free(aggr_portids, port->lp_portid);
3255102Syz147064 	port->lp_portid = 0;
3268275SEric Cheng 	mutex_destroy(&port->lp_lacp.lacp_timer_lock);
3278275SEric Cheng 	cv_destroy(&port->lp_lacp.lacp_timer_cv);
3280Sstevel@tonic-gate 	kmem_cache_free(aggr_port_cache, port);
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate  * Invoked upon receiving a MAC_NOTE_LINK notification for
3335895Syz147064  * one of the constituent ports.
3340Sstevel@tonic-gate  */
3352163Syz147064 boolean_t
aggr_port_notify_link(aggr_grp_t * grp,aggr_port_t * port)3368275SEric Cheng aggr_port_notify_link(aggr_grp_t *grp, aggr_port_t *port)
3370Sstevel@tonic-gate {
3380Sstevel@tonic-gate 	boolean_t do_attach = B_FALSE;
3390Sstevel@tonic-gate 	boolean_t do_detach = B_FALSE;
3402047Syz147064 	boolean_t link_state_changed = B_TRUE;
3410Sstevel@tonic-gate 	uint64_t ifspeed;
3420Sstevel@tonic-gate 	link_state_t link_state;
3430Sstevel@tonic-gate 	link_duplex_t link_duplex;
3448275SEric Cheng 	mac_perim_handle_t mph;
3450Sstevel@tonic-gate 
3468275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
3478275SEric Cheng 	mac_perim_enter_by_mh(port->lp_mh, &mph);
3480Sstevel@tonic-gate 
3495895Syz147064 	/*
3505895Syz147064 	 * link state change?  For links that do not support link state
3515895Syz147064 	 * notification, always assume the link is up.
3525895Syz147064 	 */
3535895Syz147064 	link_state = port->lp_no_link_update ? LINK_STATE_UP :
3545895Syz147064 	    mac_link_get(port->lp_mh);
3550Sstevel@tonic-gate 	if (port->lp_link_state != link_state) {
3560Sstevel@tonic-gate 		if (link_state == LINK_STATE_UP)
3570Sstevel@tonic-gate 			do_attach = (port->lp_link_state != LINK_STATE_UP);
3580Sstevel@tonic-gate 		else
3590Sstevel@tonic-gate 			do_detach = (port->lp_link_state == LINK_STATE_UP);
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate 	port->lp_link_state = link_state;
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	/* link duplex change? */
3642311Sseb 	link_duplex = aggr_port_stat(port, ETHER_STAT_LINK_DUPLEX);
3650Sstevel@tonic-gate 	if (port->lp_link_duplex != link_duplex) {
3660Sstevel@tonic-gate 		if (link_duplex == LINK_DUPLEX_FULL)
3670Sstevel@tonic-gate 			do_attach |= (port->lp_link_duplex != LINK_DUPLEX_FULL);
3680Sstevel@tonic-gate 		else
3690Sstevel@tonic-gate 			do_detach |= (port->lp_link_duplex == LINK_DUPLEX_FULL);
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 	port->lp_link_duplex = link_duplex;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	/* link speed changes? */
3740Sstevel@tonic-gate 	ifspeed = aggr_port_stat(port, MAC_STAT_IFSPEED);
3750Sstevel@tonic-gate 	if (port->lp_ifspeed != ifspeed) {
3760Sstevel@tonic-gate 		if (port->lp_state == AGGR_PORT_STATE_ATTACHED)
3770Sstevel@tonic-gate 			do_detach |= (ifspeed != grp->lg_ifspeed);
3780Sstevel@tonic-gate 		else
3790Sstevel@tonic-gate 			do_attach |= (ifspeed == grp->lg_ifspeed);
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 	port->lp_ifspeed = ifspeed;
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	if (do_attach) {
3840Sstevel@tonic-gate 		/* attempt to attach the port to the aggregation */
3852047Syz147064 		link_state_changed = aggr_grp_attach_port(grp, port);
3860Sstevel@tonic-gate 	} else if (do_detach) {
3870Sstevel@tonic-gate 		/* detach the port from the aggregation */
3888275SEric Cheng 		link_state_changed = aggr_grp_detach_port(grp, port);
3890Sstevel@tonic-gate 	}
3900Sstevel@tonic-gate 
3918275SEric Cheng 	mac_perim_exit(mph);
3922047Syz147064 	return (link_state_changed);
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate /*
3960Sstevel@tonic-gate  * Invoked upon receiving a MAC_NOTE_UNICST for one of the constituent
3970Sstevel@tonic-gate  * ports of a group.
3980Sstevel@tonic-gate  */
3992047Syz147064 static void
aggr_port_notify_unicst(aggr_grp_t * grp,aggr_port_t * port,boolean_t * mac_addr_changedp,boolean_t * link_state_changedp)4002047Syz147064 aggr_port_notify_unicst(aggr_grp_t *grp, aggr_port_t *port,
4012047Syz147064     boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
4020Sstevel@tonic-gate {
4032047Syz147064 	boolean_t mac_addr_changed = B_FALSE;
4042047Syz147064 	boolean_t link_state_changed = B_FALSE;
4052047Syz147064 	uint8_t mac_addr[ETHERADDRL];
4068275SEric Cheng 	mac_perim_handle_t mph;
4072047Syz147064 
4088275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
4092047Syz147064 	ASSERT(mac_addr_changedp != NULL);
4102047Syz147064 	ASSERT(link_state_changedp != NULL);
4118275SEric Cheng 	mac_perim_enter_by_mh(port->lp_mh, &mph);
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	/*
4140Sstevel@tonic-gate 	 * If it is called when setting the MAC address to the
4150Sstevel@tonic-gate 	 * aggregation group MAC address, do nothing.
4160Sstevel@tonic-gate 	 */
4178275SEric Cheng 	mac_unicast_primary_get(port->lp_mh, mac_addr);
4182047Syz147064 	if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) {
4198275SEric Cheng 		mac_perim_exit(mph);
4202047Syz147064 		goto done;
4212047Syz147064 	}
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	/* save the new port MAC address */
4242047Syz147064 	bcopy(mac_addr, port->lp_addr, ETHERADDRL);
4250Sstevel@tonic-gate 
4262047Syz147064 	aggr_grp_port_mac_changed(grp, port, &mac_addr_changed,
4272047Syz147064 	    &link_state_changed);
4280Sstevel@tonic-gate 
4298275SEric Cheng 	mac_perim_exit(mph);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	/*
4320Sstevel@tonic-gate 	 * If this port was used to determine the MAC address of
4330Sstevel@tonic-gate 	 * the group, update the MAC address of the constituent
4340Sstevel@tonic-gate 	 * ports.
4350Sstevel@tonic-gate 	 */
4365102Syz147064 	if (mac_addr_changed && aggr_grp_update_ports_mac(grp))
4375102Syz147064 		link_state_changed = B_TRUE;
4380Sstevel@tonic-gate 
4392047Syz147064 done:
4402047Syz147064 	*mac_addr_changedp = mac_addr_changed;
4412047Syz147064 	*link_state_changedp = link_state_changed;
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate /*
4450Sstevel@tonic-gate  * Notification callback invoked by the MAC service module for
4460Sstevel@tonic-gate  * a particular MAC port.
4470Sstevel@tonic-gate  */
4480Sstevel@tonic-gate static void
aggr_port_notify_cb(void * arg,mac_notify_type_t type)4490Sstevel@tonic-gate aggr_port_notify_cb(void *arg, mac_notify_type_t type)
4500Sstevel@tonic-gate {
4510Sstevel@tonic-gate 	aggr_port_t *port = arg;
4520Sstevel@tonic-gate 	aggr_grp_t *grp = port->lp_grp;
4532047Syz147064 	boolean_t mac_addr_changed, link_state_changed;
4548275SEric Cheng 	mac_perim_handle_t mph;
4550Sstevel@tonic-gate 
4568275SEric Cheng 	mac_perim_enter_by_mh(grp->lg_mh, &mph);
4578275SEric Cheng 	if (port->lp_closing) {
4588275SEric Cheng 		mac_perim_exit(mph);
4598275SEric Cheng 
4608275SEric Cheng 		/*
4618275SEric Cheng 		 * Release the reference so it is safe for aggr to call
4628275SEric Cheng 		 * mac_unregister() now.
4638275SEric Cheng 		 */
4648275SEric Cheng 		aggr_grp_port_rele(port);
4651852Syz147064 		return;
4668275SEric Cheng 	}
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	switch (type) {
4690Sstevel@tonic-gate 	case MAC_NOTE_TX:
4702311Sseb 		mac_tx_update(grp->lg_mh);
4710Sstevel@tonic-gate 		break;
4720Sstevel@tonic-gate 	case MAC_NOTE_LINK:
4738275SEric Cheng 		if (aggr_port_notify_link(grp, port))
4742311Sseb 			mac_link_update(grp->lg_mh, grp->lg_link_state);
4750Sstevel@tonic-gate 		break;
4760Sstevel@tonic-gate 	case MAC_NOTE_UNICST:
4772047Syz147064 		aggr_port_notify_unicst(grp, port, &mac_addr_changed,
4782047Syz147064 		    &link_state_changed);
4792047Syz147064 		if (mac_addr_changed)
4802311Sseb 			mac_unicst_update(grp->lg_mh, grp->lg_addr);
4812047Syz147064 		if (link_state_changed)
4822311Sseb 			mac_link_update(grp->lg_mh, grp->lg_link_state);
4830Sstevel@tonic-gate 		break;
4840Sstevel@tonic-gate 	default:
4850Sstevel@tonic-gate 		break;
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 
4888275SEric Cheng 	mac_perim_exit(mph);
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate int
aggr_port_start(aggr_port_t * port)4920Sstevel@tonic-gate aggr_port_start(aggr_port_t *port)
4930Sstevel@tonic-gate {
4948275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
4950Sstevel@tonic-gate 
4968833SVenu.Iyer@Sun.COM 	if (port->lp_started)
4978833SVenu.Iyer@Sun.COM 		return (0);
4980Sstevel@tonic-gate 
4998833SVenu.Iyer@Sun.COM 	port->lp_started = B_TRUE;
5008833SVenu.Iyer@Sun.COM 	aggr_grp_multicst_port(port, B_TRUE);
5018275SEric Cheng 	return (0);
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate void
aggr_port_stop(aggr_port_t * port)5050Sstevel@tonic-gate aggr_port_stop(aggr_port_t *port)
5060Sstevel@tonic-gate {
5078275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if (!port->lp_started)
5100Sstevel@tonic-gate 		return;
5110Sstevel@tonic-gate 
5128833SVenu.Iyer@Sun.COM 	aggr_grp_multicst_port(port, B_FALSE);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	/* update the port state */
5150Sstevel@tonic-gate 	port->lp_started = B_FALSE;
5160Sstevel@tonic-gate }
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate int
aggr_port_promisc(aggr_port_t * port,boolean_t on)5190Sstevel@tonic-gate aggr_port_promisc(aggr_port_t *port, boolean_t on)
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate 	int rc;
5220Sstevel@tonic-gate 
5238275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	if (on == port->lp_promisc_on)
5260Sstevel@tonic-gate 		/* already in desired promiscous mode */
5270Sstevel@tonic-gate 		return (0);
5280Sstevel@tonic-gate 
5298275SEric Cheng 	if (on) {
5308275SEric Cheng 		mac_rx_clear(port->lp_mch);
5318275SEric Cheng 		rc = mac_promisc_add(port->lp_mch, MAC_CLIENT_PROMISC_ALL,
5328275SEric Cheng 		    aggr_recv_cb, port, &port->lp_mphp,
5338275SEric Cheng 		    MAC_PROMISC_FLAGS_NO_TX_LOOP);
5348275SEric Cheng 		if (rc != 0) {
5358275SEric Cheng 			mac_rx_set(port->lp_mch, aggr_recv_cb, port);
5368275SEric Cheng 			return (rc);
5378275SEric Cheng 		}
5388275SEric Cheng 	} else {
5399044SGirish.Moodalbail@Sun.COM 		mac_promisc_remove(port->lp_mphp);
5408275SEric Cheng 		port->lp_mphp = NULL;
5418275SEric Cheng 		mac_rx_set(port->lp_mch, aggr_recv_cb, port);
5428275SEric Cheng 	}
5430Sstevel@tonic-gate 
5448275SEric Cheng 	port->lp_promisc_on = on;
5450Sstevel@tonic-gate 
5468275SEric Cheng 	return (0);
5470Sstevel@tonic-gate }
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate /*
5500Sstevel@tonic-gate  * Set the MAC address of a port.
5510Sstevel@tonic-gate  */
5520Sstevel@tonic-gate int
aggr_port_unicst(aggr_port_t * port)5538275SEric Cheng aggr_port_unicst(aggr_port_t *port)
5540Sstevel@tonic-gate {
5558275SEric Cheng 	aggr_grp_t		*grp = port->lp_grp;
5560Sstevel@tonic-gate 
5578275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
5588275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_mh));
5590Sstevel@tonic-gate 
5608275SEric Cheng 	return (mac_unicast_primary_set(port->lp_mh, grp->lg_addr));
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate /*
5640Sstevel@tonic-gate  * Add or remove a multicast address to/from a port.
5650Sstevel@tonic-gate  */
5660Sstevel@tonic-gate int
aggr_port_multicst(void * arg,boolean_t add,const uint8_t * addrp)5670Sstevel@tonic-gate aggr_port_multicst(void *arg, boolean_t add, const uint8_t *addrp)
5680Sstevel@tonic-gate {
5690Sstevel@tonic-gate 	aggr_port_t *port = arg;
5700Sstevel@tonic-gate 
5718275SEric Cheng 	if (add) {
5728275SEric Cheng 		return (mac_multicast_add(port->lp_mch, addrp));
5738275SEric Cheng 	} else {
5748275SEric Cheng 		mac_multicast_remove(port->lp_mch, addrp);
5758275SEric Cheng 		return (0);
5768275SEric Cheng 	}
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate uint64_t
aggr_port_stat(aggr_port_t * port,uint_t stat)5802311Sseb aggr_port_stat(aggr_port_t *port, uint_t stat)
5810Sstevel@tonic-gate {
5820Sstevel@tonic-gate 	return (mac_stat_get(port->lp_mh, stat));
5830Sstevel@tonic-gate }
5848275SEric Cheng 
5858275SEric Cheng /*
5868275SEric Cheng  * Add a non-primary unicast address to the underlying port. If the port
5878275SEric Cheng  * supports HW Rx group, try to add the address into the HW Rx group of
5888275SEric Cheng  * the port first. If that fails, or if the port does not support HW Rx
5898275SEric Cheng  * group, enable the port's promiscous mode.
5908275SEric Cheng  */
5918275SEric Cheng int
aggr_port_addmac(aggr_port_t * port,const uint8_t * mac_addr)5928275SEric Cheng aggr_port_addmac(aggr_port_t *port, const uint8_t *mac_addr)
5938275SEric Cheng {
5948275SEric Cheng 	aggr_unicst_addr_t	*addr, **pprev;
5958275SEric Cheng 	mac_perim_handle_t	pmph;
5968275SEric Cheng 	int			err;
5978275SEric Cheng 
5988275SEric Cheng 	ASSERT(MAC_PERIM_HELD(port->lp_grp->lg_mh));
5998275SEric Cheng 	mac_perim_enter_by_mh(port->lp_mh, &pmph);
6008275SEric Cheng 
6018275SEric Cheng 	/*
6028275SEric Cheng 	 * If the underlying port support HW Rx group, add the mac to its
6038275SEric Cheng 	 * RX group directly.
6048275SEric Cheng 	 */
6058275SEric Cheng 	if ((port->lp_hwgh != NULL) &&
6068275SEric Cheng 	    ((mac_hwgroup_addmac(port->lp_hwgh, mac_addr)) == 0)) {
6078275SEric Cheng 		mac_perim_exit(pmph);
6088275SEric Cheng 		return (0);
6098275SEric Cheng 	}
6108275SEric Cheng 
6118275SEric Cheng 	/*
6128275SEric Cheng 	 * If that fails, or if the port does not support HW Rx group, enable
6138275SEric Cheng 	 * the port's promiscous mode. (Note that we turn on the promiscous
6148275SEric Cheng 	 * mode only if the port is already started.
6158275SEric Cheng 	 */
6168275SEric Cheng 	if (port->lp_started &&
6178275SEric Cheng 	    ((err = aggr_port_promisc(port, B_TRUE)) != 0)) {
6188275SEric Cheng 		mac_perim_exit(pmph);
6198275SEric Cheng 		return (err);
6208275SEric Cheng 	}
6218275SEric Cheng 
6228275SEric Cheng 	/*
6238275SEric Cheng 	 * Walk through the unicast addresses that requires promiscous mode
6248275SEric Cheng 	 * enabled on this port, and add this address to the end of the list.
6258275SEric Cheng 	 */
6268275SEric Cheng 	pprev = &port->lp_prom_addr;
6278275SEric Cheng 	while ((addr = *pprev) != NULL) {
6288275SEric Cheng 		ASSERT(bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0);
6298275SEric Cheng 		pprev = &addr->aua_next;
6308275SEric Cheng 	}
6318275SEric Cheng 	addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP);
6328275SEric Cheng 	bcopy(mac_addr, addr->aua_addr, ETHERADDRL);
6338275SEric Cheng 	addr->aua_next = NULL;
6348275SEric Cheng 	*pprev = addr;
6358275SEric Cheng 	mac_perim_exit(pmph);
6368275SEric Cheng 	return (0);
6378275SEric Cheng }
6388275SEric Cheng 
6398275SEric Cheng /*
6408275SEric Cheng  * Remove a non-primary unicast address from the underlying port. This address
6418275SEric Cheng  * must has been added by aggr_port_addmac(). As a result, we probably need to
6428275SEric Cheng  * remove the address from the port's HW Rx group, or to disable the port's
6438275SEric Cheng  * promiscous mode.
6448275SEric Cheng  */
6458275SEric Cheng void
aggr_port_remmac(aggr_port_t * port,const uint8_t * mac_addr)6468275SEric Cheng aggr_port_remmac(aggr_port_t *port, const uint8_t *mac_addr)
6478275SEric Cheng {
6488275SEric Cheng 	aggr_grp_t		*grp = port->lp_grp;
6498275SEric Cheng 	aggr_unicst_addr_t	*addr, **pprev;
6508275SEric Cheng 	mac_perim_handle_t	pmph;
6518275SEric Cheng 
6528275SEric Cheng 	ASSERT(MAC_PERIM_HELD(grp->lg_mh));
6538275SEric Cheng 	mac_perim_enter_by_mh(port->lp_mh, &pmph);
6548275SEric Cheng 
6558275SEric Cheng 	/*
6568275SEric Cheng 	 * See whether this address is in the list of addresses that requires
6578275SEric Cheng 	 * the port being promiscous mode.
6588275SEric Cheng 	 */
6598275SEric Cheng 	pprev = &port->lp_prom_addr;
6608275SEric Cheng 	while ((addr = *pprev) != NULL) {
6618275SEric Cheng 		if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0)
6628275SEric Cheng 			break;
6638275SEric Cheng 		pprev = &addr->aua_next;
6648275SEric Cheng 	}
6658275SEric Cheng 	if (addr != NULL) {
6668275SEric Cheng 		/*
6678275SEric Cheng 		 * This unicast address put the port into the promiscous mode,
6688275SEric Cheng 		 * delete this address from the lp_prom_addr list. If this is
6698275SEric Cheng 		 * the last address in that list, disable the promiscous mode
6708275SEric Cheng 		 * if the aggregation is not in promiscous mode.
6718275SEric Cheng 		 */
6728275SEric Cheng 		*pprev = addr->aua_next;
6738275SEric Cheng 		kmem_free(addr, sizeof (aggr_unicst_addr_t));
6748275SEric Cheng 		if (port->lp_prom_addr == NULL && !grp->lg_promisc)
6758275SEric Cheng 			(void) aggr_port_promisc(port, B_FALSE);
6768275SEric Cheng 	} else {
6778275SEric Cheng 		ASSERT(port->lp_hwgh != NULL);
6788275SEric Cheng 		(void) mac_hwgroup_remmac(port->lp_hwgh, mac_addr);
6798275SEric Cheng 	}
6808275SEric Cheng 	mac_perim_exit(pmph);
6818275SEric Cheng }
682