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 /*
2212850SPrakash.Jalan@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 -- Link Aggregation Groups.
270Sstevel@tonic-gate *
280Sstevel@tonic-gate * An instance of the structure aggr_grp_t is allocated for each
290Sstevel@tonic-gate * link aggregation group. When created, aggr_grp_t objects are
30269Sericheng * entered into the aggr_grp_hash hash table maintained by the modhash
315895Syz147064 * module. The hash key is the linkid associated with the link
325895Syz147064 * aggregation group.
330Sstevel@tonic-gate *
340Sstevel@tonic-gate * A set of MAC ports are associated with each association group.
3511878SVenu.Iyer@Sun.COM *
3611878SVenu.Iyer@Sun.COM * Aggr pseudo TX rings
3711878SVenu.Iyer@Sun.COM * --------------------
3811878SVenu.Iyer@Sun.COM * The underlying ports (NICs) in an aggregation can have TX rings. To
3911878SVenu.Iyer@Sun.COM * enhance aggr's performance, these TX rings are made available to the
4011878SVenu.Iyer@Sun.COM * aggr layer as pseudo TX rings. The concept of pseudo rings are not new.
4111878SVenu.Iyer@Sun.COM * They are already present and implemented on the RX side. It is called
4211878SVenu.Iyer@Sun.COM * as pseudo RX rings. The same concept is extended to the TX side where
4311878SVenu.Iyer@Sun.COM * each TX ring of an underlying port is reflected in aggr as a pseudo
4411878SVenu.Iyer@Sun.COM * TX ring. Thus each pseudo TX ring will map to a specific hardware TX
4511878SVenu.Iyer@Sun.COM * ring. Even in the case of a NIC that does not have a TX ring, a pseudo
4611878SVenu.Iyer@Sun.COM * TX ring is given to the aggregation layer.
4711878SVenu.Iyer@Sun.COM *
4811878SVenu.Iyer@Sun.COM * With this change, the outgoing stack depth looks much better:
4911878SVenu.Iyer@Sun.COM *
5011878SVenu.Iyer@Sun.COM * mac_tx() -> mac_tx_aggr_mode() -> mac_tx_soft_ring_process() ->
5111878SVenu.Iyer@Sun.COM * mac_tx_send() -> aggr_ring_rx() -> <driver>_ring_tx()
5211878SVenu.Iyer@Sun.COM *
5311878SVenu.Iyer@Sun.COM * Two new modes are introduced to mac_tx() to handle aggr pseudo TX rings:
5411878SVenu.Iyer@Sun.COM * SRS_TX_AGGR and SRS_TX_BW_AGGR.
5511878SVenu.Iyer@Sun.COM *
5611878SVenu.Iyer@Sun.COM * In SRS_TX_AGGR mode, mac_tx_aggr_mode() routine is called. This routine
5711878SVenu.Iyer@Sun.COM * invokes an aggr function, aggr_find_tx_ring(), to find a (pseudo) TX
5811878SVenu.Iyer@Sun.COM * ring belonging to a port on which the packet has to be sent.
5911878SVenu.Iyer@Sun.COM * aggr_find_tx_ring() first finds the outgoing port based on L2/L3/L4
6011878SVenu.Iyer@Sun.COM * policy and then uses the fanout_hint passed to it to pick a TX ring from
6111878SVenu.Iyer@Sun.COM * the selected port.
6211878SVenu.Iyer@Sun.COM *
6311878SVenu.Iyer@Sun.COM * In SRS_TX_BW_AGGR mode, mac_tx_bw_mode() function is called where
6411878SVenu.Iyer@Sun.COM * bandwidth limit is applied first on the outgoing packet and the packets
6511878SVenu.Iyer@Sun.COM * allowed to go out would call mac_tx_aggr_mode() to send the packet on a
6611878SVenu.Iyer@Sun.COM * particular TX ring.
670Sstevel@tonic-gate */
680Sstevel@tonic-gate
690Sstevel@tonic-gate #include <sys/types.h>
700Sstevel@tonic-gate #include <sys/sysmacros.h>
710Sstevel@tonic-gate #include <sys/conf.h>
720Sstevel@tonic-gate #include <sys/cmn_err.h>
738275SEric Cheng #include <sys/disp.h>
740Sstevel@tonic-gate #include <sys/list.h>
750Sstevel@tonic-gate #include <sys/ksynch.h>
760Sstevel@tonic-gate #include <sys/kmem.h>
770Sstevel@tonic-gate #include <sys/stream.h>
780Sstevel@tonic-gate #include <sys/modctl.h>
790Sstevel@tonic-gate #include <sys/ddi.h>
800Sstevel@tonic-gate #include <sys/sunddi.h>
810Sstevel@tonic-gate #include <sys/atomic.h>
820Sstevel@tonic-gate #include <sys/stat.h>
83269Sericheng #include <sys/modhash.h>
845895Syz147064 #include <sys/id_space.h>
850Sstevel@tonic-gate #include <sys/strsun.h>
8610616SSebastien.Roy@Sun.COM #include <sys/cred.h>
870Sstevel@tonic-gate #include <sys/dlpi.h>
8810616SSebastien.Roy@Sun.COM #include <sys/zone.h>
898275SEric Cheng #include <sys/mac_provider.h>
905895Syz147064 #include <sys/dls.h>
915895Syz147064 #include <sys/vlan.h>
920Sstevel@tonic-gate #include <sys/aggr.h>
930Sstevel@tonic-gate #include <sys/aggr_impl.h>
940Sstevel@tonic-gate
950Sstevel@tonic-gate static int aggr_m_start(void *);
960Sstevel@tonic-gate static void aggr_m_stop(void *);
970Sstevel@tonic-gate static int aggr_m_promisc(void *, boolean_t);
980Sstevel@tonic-gate static int aggr_m_multicst(void *, boolean_t, const uint8_t *);
990Sstevel@tonic-gate static int aggr_m_unicst(void *, const uint8_t *);
1002311Sseb static int aggr_m_stat(void *, uint_t, uint64_t *);
1010Sstevel@tonic-gate static void aggr_m_ioctl(void *, queue_t *, mblk_t *);
1022311Sseb static boolean_t aggr_m_capab_get(void *, mac_capab_t, void *);
1038603SGirish.Moodalbail@Sun.COM static int aggr_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
1048603SGirish.Moodalbail@Sun.COM const void *);
10511878SVenu.Iyer@Sun.COM static void aggr_m_propinfo(void *, const char *, mac_prop_id_t,
10611878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
1078603SGirish.Moodalbail@Sun.COM
1085895Syz147064 static aggr_port_t *aggr_grp_port_lookup(aggr_grp_t *, datalink_id_t);
1092047Syz147064 static int aggr_grp_rem_port(aggr_grp_t *, aggr_port_t *, boolean_t *,
1102047Syz147064 boolean_t *);
1115895Syz147064
1120Sstevel@tonic-gate static void aggr_grp_capab_set(aggr_grp_t *);
1130Sstevel@tonic-gate static boolean_t aggr_grp_capab_check(aggr_grp_t *, aggr_port_t *);
1142803Snd99603 static uint_t aggr_grp_max_sdu(aggr_grp_t *);
1155895Syz147064 static uint32_t aggr_grp_max_margin(aggr_grp_t *);
1162803Snd99603 static boolean_t aggr_grp_sdu_check(aggr_grp_t *, aggr_port_t *);
1175895Syz147064 static boolean_t aggr_grp_margin_check(aggr_grp_t *, aggr_port_t *);
1188275SEric Cheng
1198275SEric Cheng static int aggr_add_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *);
1208275SEric Cheng static void aggr_rem_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *);
1218275SEric Cheng static int aggr_pseudo_disable_intr(mac_intr_handle_t);
1228275SEric Cheng static int aggr_pseudo_enable_intr(mac_intr_handle_t);
1238275SEric Cheng static int aggr_pseudo_start_ring(mac_ring_driver_t, uint64_t);
1248275SEric Cheng static void aggr_pseudo_stop_ring(mac_ring_driver_t);
1258275SEric Cheng static int aggr_addmac(void *, const uint8_t *);
1268275SEric Cheng static int aggr_remmac(void *, const uint8_t *);
1278275SEric Cheng static mblk_t *aggr_rx_poll(void *, int);
1288275SEric Cheng static void aggr_fill_ring(void *, mac_ring_type_t, const int,
1298275SEric Cheng const int, mac_ring_info_t *, mac_ring_handle_t);
1308275SEric Cheng static void aggr_fill_group(void *, mac_ring_type_t, const int,
1318275SEric Cheng mac_group_info_t *, mac_group_handle_t);
1320Sstevel@tonic-gate
133269Sericheng static kmem_cache_t *aggr_grp_cache;
134269Sericheng static mod_hash_t *aggr_grp_hash;
135269Sericheng static krwlock_t aggr_grp_lock;
136269Sericheng static uint_t aggr_grp_cnt;
1375895Syz147064 static id_space_t *key_ids;
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate #define GRP_HASHSZ 64
1405895Syz147064 #define GRP_HASH_KEY(linkid) ((mod_hash_key_t)(uintptr_t)linkid)
1418275SEric Cheng #define AGGR_PORT_NAME_DELIMIT '-'
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0};
1440Sstevel@tonic-gate
1458603SGirish.Moodalbail@Sun.COM #define AGGR_M_CALLBACK_FLAGS \
14611878SVenu.Iyer@Sun.COM (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_PROPINFO)
1472311Sseb
1482311Sseb static mac_callbacks_t aggr_m_callbacks = {
1492311Sseb AGGR_M_CALLBACK_FLAGS,
1502311Sseb aggr_m_stat,
1512311Sseb aggr_m_start,
1522311Sseb aggr_m_stop,
1532311Sseb aggr_m_promisc,
1542311Sseb aggr_m_multicst,
1558275SEric Cheng NULL,
15611878SVenu.Iyer@Sun.COM NULL,
15711878SVenu.Iyer@Sun.COM NULL,
1582311Sseb aggr_m_ioctl,
1598603SGirish.Moodalbail@Sun.COM aggr_m_capab_get,
1608603SGirish.Moodalbail@Sun.COM NULL,
1618603SGirish.Moodalbail@Sun.COM NULL,
1628603SGirish.Moodalbail@Sun.COM aggr_m_setprop,
16311878SVenu.Iyer@Sun.COM NULL,
16411878SVenu.Iyer@Sun.COM aggr_m_propinfo
1652311Sseb };
1662311Sseb
1670Sstevel@tonic-gate /*ARGSUSED*/
1680Sstevel@tonic-gate static int
aggr_grp_constructor(void * buf,void * arg,int kmflag)1690Sstevel@tonic-gate aggr_grp_constructor(void *buf, void *arg, int kmflag)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate aggr_grp_t *grp = buf;
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate bzero(grp, sizeof (*grp));
1748275SEric Cheng mutex_init(&grp->lg_lacp_lock, NULL, MUTEX_DEFAULT, NULL);
1758275SEric Cheng cv_init(&grp->lg_lacp_cv, NULL, CV_DEFAULT, NULL);
1768275SEric Cheng rw_init(&grp->lg_tx_lock, NULL, RW_DRIVER, NULL);
1778275SEric Cheng mutex_init(&grp->lg_port_lock, NULL, MUTEX_DEFAULT, NULL);
1788275SEric Cheng cv_init(&grp->lg_port_cv, NULL, CV_DEFAULT, NULL);
17911878SVenu.Iyer@Sun.COM mutex_init(&grp->lg_tx_flowctl_lock, NULL, MUTEX_DEFAULT, NULL);
18011878SVenu.Iyer@Sun.COM cv_init(&grp->lg_tx_flowctl_cv, NULL, CV_DEFAULT, NULL);
1810Sstevel@tonic-gate grp->lg_link_state = LINK_STATE_UNKNOWN;
1820Sstevel@tonic-gate return (0);
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate
1850Sstevel@tonic-gate /*ARGSUSED*/
1860Sstevel@tonic-gate static void
aggr_grp_destructor(void * buf,void * arg)1870Sstevel@tonic-gate aggr_grp_destructor(void *buf, void *arg)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate aggr_grp_t *grp = buf;
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate if (grp->lg_tx_ports != NULL) {
1920Sstevel@tonic-gate kmem_free(grp->lg_tx_ports,
1930Sstevel@tonic-gate grp->lg_tx_ports_size * sizeof (aggr_port_t *));
1940Sstevel@tonic-gate }
1950Sstevel@tonic-gate
1968275SEric Cheng mutex_destroy(&grp->lg_lacp_lock);
1978275SEric Cheng cv_destroy(&grp->lg_lacp_cv);
1988275SEric Cheng mutex_destroy(&grp->lg_port_lock);
1998275SEric Cheng cv_destroy(&grp->lg_port_cv);
2008275SEric Cheng rw_destroy(&grp->lg_tx_lock);
20111878SVenu.Iyer@Sun.COM mutex_destroy(&grp->lg_tx_flowctl_lock);
20211878SVenu.Iyer@Sun.COM cv_destroy(&grp->lg_tx_flowctl_cv);
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate
2050Sstevel@tonic-gate void
aggr_grp_init(void)2060Sstevel@tonic-gate aggr_grp_init(void)
2070Sstevel@tonic-gate {
2080Sstevel@tonic-gate aggr_grp_cache = kmem_cache_create("aggr_grp_cache",
2090Sstevel@tonic-gate sizeof (aggr_grp_t), 0, aggr_grp_constructor,
2100Sstevel@tonic-gate aggr_grp_destructor, NULL, NULL, NULL, 0);
2110Sstevel@tonic-gate
212269Sericheng aggr_grp_hash = mod_hash_create_idhash("aggr_grp_hash",
213269Sericheng GRP_HASHSZ, mod_hash_null_valdtor);
214269Sericheng rw_init(&aggr_grp_lock, NULL, RW_DEFAULT, NULL);
215269Sericheng aggr_grp_cnt = 0;
2165895Syz147064
2175895Syz147064 /*
2185895Syz147064 * Allocate an id space to manage key values (when key is not
2195895Syz147064 * specified). The range of the id space will be from
2205895Syz147064 * (AGGR_MAX_KEY + 1) to UINT16_MAX, because the LACP protocol
2215895Syz147064 * uses a 16-bit key.
2225895Syz147064 */
2235895Syz147064 key_ids = id_space_create("aggr_key_ids", AGGR_MAX_KEY + 1, UINT16_MAX);
2245895Syz147064 ASSERT(key_ids != NULL);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate
2271804Sericheng void
aggr_grp_fini(void)2280Sstevel@tonic-gate aggr_grp_fini(void)
2290Sstevel@tonic-gate {
2305895Syz147064 id_space_destroy(key_ids);
231269Sericheng rw_destroy(&aggr_grp_lock);
232269Sericheng mod_hash_destroy_idhash(aggr_grp_hash);
2330Sstevel@tonic-gate kmem_cache_destroy(aggr_grp_cache);
2340Sstevel@tonic-gate }
2350Sstevel@tonic-gate
236269Sericheng uint_t
aggr_grp_count(void)237269Sericheng aggr_grp_count(void)
238269Sericheng {
239269Sericheng uint_t count;
240269Sericheng
241269Sericheng rw_enter(&aggr_grp_lock, RW_READER);
242269Sericheng count = aggr_grp_cnt;
243269Sericheng rw_exit(&aggr_grp_lock);
244269Sericheng return (count);
245269Sericheng }
246269Sericheng
2470Sstevel@tonic-gate /*
2488275SEric Cheng * Since both aggr_port_notify_cb() and aggr_port_timer_thread() functions
2498275SEric Cheng * requires the mac perimeter, this function holds a reference of the aggr
2508275SEric Cheng * and aggr won't call mac_unregister() until this reference drops to 0.
2518275SEric Cheng */
2528275SEric Cheng void
aggr_grp_port_hold(aggr_port_t * port)2538275SEric Cheng aggr_grp_port_hold(aggr_port_t *port)
2548275SEric Cheng {
2558275SEric Cheng aggr_grp_t *grp = port->lp_grp;
2568275SEric Cheng
2578275SEric Cheng AGGR_PORT_REFHOLD(port);
2588275SEric Cheng mutex_enter(&grp->lg_port_lock);
2598275SEric Cheng grp->lg_port_ref++;
2608275SEric Cheng mutex_exit(&grp->lg_port_lock);
2618275SEric Cheng }
2628275SEric Cheng
2638275SEric Cheng /*
2648275SEric Cheng * Release the reference of the grp and inform aggr_grp_delete() calling
2658275SEric Cheng * mac_unregister() is now safe.
2668275SEric Cheng */
2678275SEric Cheng void
aggr_grp_port_rele(aggr_port_t * port)2688275SEric Cheng aggr_grp_port_rele(aggr_port_t *port)
2698275SEric Cheng {
2708275SEric Cheng aggr_grp_t *grp = port->lp_grp;
2718275SEric Cheng
2728275SEric Cheng mutex_enter(&grp->lg_port_lock);
2738275SEric Cheng if (--grp->lg_port_ref == 0)
2748275SEric Cheng cv_signal(&grp->lg_port_cv);
2758275SEric Cheng mutex_exit(&grp->lg_port_lock);
2768275SEric Cheng AGGR_PORT_REFRELE(port);
2778275SEric Cheng }
2788275SEric Cheng
2798275SEric Cheng /*
2808275SEric Cheng * Wait for the port's lacp timer thread and the port's notification callback
2818275SEric Cheng * to exit.
2828275SEric Cheng */
2838275SEric Cheng void
aggr_grp_port_wait(aggr_grp_t * grp)2848275SEric Cheng aggr_grp_port_wait(aggr_grp_t *grp)
2858275SEric Cheng {
2868275SEric Cheng mutex_enter(&grp->lg_port_lock);
2878275SEric Cheng if (grp->lg_port_ref != 0)
2888275SEric Cheng cv_wait(&grp->lg_port_cv, &grp->lg_port_lock);
2898275SEric Cheng mutex_exit(&grp->lg_port_lock);
2908275SEric Cheng }
2918275SEric Cheng
2928275SEric Cheng /*
2930Sstevel@tonic-gate * Attach a port to a link aggregation group.
2940Sstevel@tonic-gate *
2950Sstevel@tonic-gate * A port is attached to a link aggregation group once its speed
2960Sstevel@tonic-gate * and link state have been verified.
2970Sstevel@tonic-gate *
2980Sstevel@tonic-gate * Returns B_TRUE if the group link state or speed has changed. If
2990Sstevel@tonic-gate * it's the case, the caller must notify the MAC layer via a call
3000Sstevel@tonic-gate * to mac_link().
3010Sstevel@tonic-gate */
3020Sstevel@tonic-gate boolean_t
aggr_grp_attach_port(aggr_grp_t * grp,aggr_port_t * port)3030Sstevel@tonic-gate aggr_grp_attach_port(aggr_grp_t *grp, aggr_port_t *port)
3040Sstevel@tonic-gate {
3052047Syz147064 boolean_t link_state_changed = B_FALSE;
3060Sstevel@tonic-gate
3078275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
3088275SEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh));
3090Sstevel@tonic-gate
3100Sstevel@tonic-gate if (port->lp_state == AGGR_PORT_STATE_ATTACHED)
3110Sstevel@tonic-gate return (B_FALSE);
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate * Validate the MAC port link speed and update the group
3150Sstevel@tonic-gate * link speed if needed.
3160Sstevel@tonic-gate */
3170Sstevel@tonic-gate if (port->lp_ifspeed == 0 ||
3180Sstevel@tonic-gate port->lp_link_state != LINK_STATE_UP ||
3190Sstevel@tonic-gate port->lp_link_duplex != LINK_DUPLEX_FULL) {
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate * Can't attach a MAC port with unknown link speed,
3220Sstevel@tonic-gate * down link, or not in full duplex mode.
3230Sstevel@tonic-gate */
3240Sstevel@tonic-gate return (B_FALSE);
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate
3270Sstevel@tonic-gate if (grp->lg_ifspeed == 0) {
3280Sstevel@tonic-gate /*
3290Sstevel@tonic-gate * The group inherits the speed of the first link being
3300Sstevel@tonic-gate * attached.
3310Sstevel@tonic-gate */
3320Sstevel@tonic-gate grp->lg_ifspeed = port->lp_ifspeed;
3332047Syz147064 link_state_changed = B_TRUE;
3340Sstevel@tonic-gate } else if (grp->lg_ifspeed != port->lp_ifspeed) {
3350Sstevel@tonic-gate /*
3360Sstevel@tonic-gate * The link speed of the MAC port must be the same as
3370Sstevel@tonic-gate * the group link speed, as per 802.3ad. Since it is
3380Sstevel@tonic-gate * not, the attach is cancelled.
3390Sstevel@tonic-gate */
3400Sstevel@tonic-gate return (B_FALSE);
3410Sstevel@tonic-gate }
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate grp->lg_nattached_ports++;
3440Sstevel@tonic-gate
3450Sstevel@tonic-gate /*
3460Sstevel@tonic-gate * Update the group link state.
3470Sstevel@tonic-gate */
3480Sstevel@tonic-gate if (grp->lg_link_state != LINK_STATE_UP) {
3490Sstevel@tonic-gate grp->lg_link_state = LINK_STATE_UP;
3500Sstevel@tonic-gate grp->lg_link_duplex = LINK_DUPLEX_FULL;
3512047Syz147064 link_state_changed = B_TRUE;
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate /*
3550Sstevel@tonic-gate * Update port's state.
3560Sstevel@tonic-gate */
3570Sstevel@tonic-gate port->lp_state = AGGR_PORT_STATE_ATTACHED;
3580Sstevel@tonic-gate
3598833SVenu.Iyer@Sun.COM aggr_grp_multicst_port(port, B_TRUE);
3608833SVenu.Iyer@Sun.COM
3610Sstevel@tonic-gate /*
3621852Syz147064 * Set port's receive callback
3631852Syz147064 */
3648275SEric Cheng mac_rx_set(port->lp_mch, aggr_recv_cb, port);
3651852Syz147064
3661852Syz147064 /*
3670Sstevel@tonic-gate * If LACP is OFF, the port can be used to send data as soon
3680Sstevel@tonic-gate * as its link is up and verified to be compatible with the
3690Sstevel@tonic-gate * aggregation.
3700Sstevel@tonic-gate *
3710Sstevel@tonic-gate * If LACP is active or passive, notify the LACP subsystem, which
3720Sstevel@tonic-gate * will enable sending on the port following the LACP protocol.
3730Sstevel@tonic-gate */
3740Sstevel@tonic-gate if (grp->lg_lacp_mode == AGGR_LACP_OFF)
3750Sstevel@tonic-gate aggr_send_port_enable(port);
3760Sstevel@tonic-gate else
3770Sstevel@tonic-gate aggr_lacp_port_attached(port);
3780Sstevel@tonic-gate
3792047Syz147064 return (link_state_changed);
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate
3820Sstevel@tonic-gate boolean_t
aggr_grp_detach_port(aggr_grp_t * grp,aggr_port_t * port)3838275SEric Cheng aggr_grp_detach_port(aggr_grp_t *grp, aggr_port_t *port)
3840Sstevel@tonic-gate {
3852047Syz147064 boolean_t link_state_changed = B_FALSE;
3860Sstevel@tonic-gate
3878275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
3888275SEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh));
3890Sstevel@tonic-gate
3908275SEric Cheng /* update state */
3910Sstevel@tonic-gate if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
3920Sstevel@tonic-gate return (B_FALSE);
3931852Syz147064
3948275SEric Cheng mac_rx_clear(port->lp_mch);
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate aggr_grp_multicst_port(port, B_FALSE);
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate if (grp->lg_lacp_mode == AGGR_LACP_OFF)
3990Sstevel@tonic-gate aggr_send_port_disable(port);
4008275SEric Cheng else
4010Sstevel@tonic-gate aggr_lacp_port_detached(port);
4020Sstevel@tonic-gate
4037802SRamesh.K@Sun.COM port->lp_state = AGGR_PORT_STATE_STANDBY;
4048275SEric Cheng
4050Sstevel@tonic-gate grp->lg_nattached_ports--;
4060Sstevel@tonic-gate if (grp->lg_nattached_ports == 0) {
4070Sstevel@tonic-gate /* the last attached MAC port of the group is being detached */
4080Sstevel@tonic-gate grp->lg_ifspeed = 0;
4090Sstevel@tonic-gate grp->lg_link_state = LINK_STATE_DOWN;
4100Sstevel@tonic-gate grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN;
4112047Syz147064 link_state_changed = B_TRUE;
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate
4142047Syz147064 return (link_state_changed);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate /*
4180Sstevel@tonic-gate * Update the MAC addresses of the constituent ports of the specified
4190Sstevel@tonic-gate * group. This function is invoked:
4200Sstevel@tonic-gate * - after creating a new aggregation group.
4210Sstevel@tonic-gate * - after adding new ports to an aggregation group.
4220Sstevel@tonic-gate * - after removing a port from a group when the MAC address of
4230Sstevel@tonic-gate * that port was used for the MAC address of the group.
4240Sstevel@tonic-gate * - after the MAC address of a port changed when the MAC address
4250Sstevel@tonic-gate * of that port was used for the MAC address of the group.
4262047Syz147064 *
4272047Syz147064 * Return true if the link state of the aggregation changed, for example
4282047Syz147064 * as a result of a failure changing the MAC address of one of the
4292047Syz147064 * constituent ports.
4300Sstevel@tonic-gate */
4312047Syz147064 boolean_t
aggr_grp_update_ports_mac(aggr_grp_t * grp)4320Sstevel@tonic-gate aggr_grp_update_ports_mac(aggr_grp_t *grp)
4330Sstevel@tonic-gate {
4340Sstevel@tonic-gate aggr_port_t *cport;
4352047Syz147064 boolean_t link_state_changed = B_FALSE;
4368275SEric Cheng mac_perim_handle_t mph;
4370Sstevel@tonic-gate
4388275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
4392047Syz147064
4400Sstevel@tonic-gate for (cport = grp->lg_ports; cport != NULL;
4410Sstevel@tonic-gate cport = cport->lp_next) {
4428275SEric Cheng mac_perim_enter_by_mh(cport->lp_mh, &mph);
4438275SEric Cheng if (aggr_port_unicst(cport) != 0) {
4448275SEric Cheng if (aggr_grp_detach_port(grp, cport))
4455102Syz147064 link_state_changed = B_TRUE;
4462047Syz147064 } else {
4472047Syz147064 /*
4482047Syz147064 * If a port was detached because of a previous
4492047Syz147064 * failure changing the MAC address, the port is
4502047Syz147064 * reattached when it successfully changes the MAC
4512047Syz147064 * address now, and this might cause the link state
4522047Syz147064 * of the aggregation to change.
4532047Syz147064 */
4545102Syz147064 if (aggr_grp_attach_port(grp, cport))
4555102Syz147064 link_state_changed = B_TRUE;
4562047Syz147064 }
4578275SEric Cheng mac_perim_exit(mph);
4580Sstevel@tonic-gate }
4592047Syz147064 return (link_state_changed);
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate
4620Sstevel@tonic-gate /*
4630Sstevel@tonic-gate * Invoked when the MAC address of a port has changed. If the port's
4642047Syz147064 * MAC address was used for the group MAC address, set mac_addr_changedp
4652047Syz147064 * to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST
4662047Syz147064 * notification. If the link state changes due to detach/attach of
4672047Syz147064 * the constituent port, set link_state_changedp to B_TRUE to indicate
4682047Syz147064 * to the caller that it should send a MAC_NOTE_LINK notification. In both
4692047Syz147064 * cases, it is the responsibility of the caller to invoke notification
4702047Syz147064 * functions after releasing the the port lock.
4710Sstevel@tonic-gate */
4722047Syz147064 void
aggr_grp_port_mac_changed(aggr_grp_t * grp,aggr_port_t * port,boolean_t * mac_addr_changedp,boolean_t * link_state_changedp)4732047Syz147064 aggr_grp_port_mac_changed(aggr_grp_t *grp, aggr_port_t *port,
4742047Syz147064 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
4750Sstevel@tonic-gate {
4768275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
4778275SEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh));
4782047Syz147064 ASSERT(mac_addr_changedp != NULL);
4792047Syz147064 ASSERT(link_state_changedp != NULL);
4802047Syz147064
4812047Syz147064 *mac_addr_changedp = B_FALSE;
4822047Syz147064 *link_state_changedp = B_FALSE;
4830Sstevel@tonic-gate
4840Sstevel@tonic-gate if (grp->lg_addr_fixed) {
4850Sstevel@tonic-gate /*
4860Sstevel@tonic-gate * The group is using a fixed MAC address or an automatic
4870Sstevel@tonic-gate * MAC address has not been set.
4880Sstevel@tonic-gate */
4892047Syz147064 return;
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate if (grp->lg_mac_addr_port == port) {
4930Sstevel@tonic-gate /*
4940Sstevel@tonic-gate * The MAC address of the port was assigned to the group
4950Sstevel@tonic-gate * MAC address. Update the group MAC address.
4960Sstevel@tonic-gate */
4970Sstevel@tonic-gate bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL);
4982047Syz147064 *mac_addr_changedp = B_TRUE;
4990Sstevel@tonic-gate } else {
5000Sstevel@tonic-gate /*
5010Sstevel@tonic-gate * Update the actual port MAC address to the MAC address
5020Sstevel@tonic-gate * of the group.
5030Sstevel@tonic-gate */
5048275SEric Cheng if (aggr_port_unicst(port) != 0) {
5058275SEric Cheng *link_state_changedp = aggr_grp_detach_port(grp, port);
5062047Syz147064 } else {
5072047Syz147064 /*
5082047Syz147064 * If a port was detached because of a previous
5092047Syz147064 * failure changing the MAC address, the port is
5102047Syz147064 * reattached when it successfully changes the MAC
5112047Syz147064 * address now, and this might cause the link state
5122047Syz147064 * of the aggregation to change.
5132047Syz147064 */
5142047Syz147064 *link_state_changedp = aggr_grp_attach_port(grp, port);
5152047Syz147064 }
5160Sstevel@tonic-gate }
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate
5190Sstevel@tonic-gate /*
5200Sstevel@tonic-gate * Add a port to a link aggregation group.
5210Sstevel@tonic-gate */
5220Sstevel@tonic-gate static int
aggr_grp_add_port(aggr_grp_t * grp,datalink_id_t port_linkid,boolean_t force,aggr_port_t ** pp)5238275SEric Cheng aggr_grp_add_port(aggr_grp_t *grp, datalink_id_t port_linkid, boolean_t force,
5245895Syz147064 aggr_port_t **pp)
5250Sstevel@tonic-gate {
5260Sstevel@tonic-gate aggr_port_t *port, **cport;
5278275SEric Cheng mac_perim_handle_t mph;
52810616SSebastien.Roy@Sun.COM zoneid_t port_zoneid = ALL_ZONES;
5290Sstevel@tonic-gate int err;
5300Sstevel@tonic-gate
53110616SSebastien.Roy@Sun.COM /* The port must be int the same zone as the aggregation. */
53210616SSebastien.Roy@Sun.COM if (zone_check_datalink(&port_zoneid, port_linkid) != 0)
53310616SSebastien.Roy@Sun.COM port_zoneid = GLOBAL_ZONEID;
53410616SSebastien.Roy@Sun.COM if (grp->lg_zoneid != port_zoneid)
53510616SSebastien.Roy@Sun.COM return (EBUSY);
53610616SSebastien.Roy@Sun.COM
5378275SEric Cheng /*
5388275SEric Cheng * lg_mh could be NULL when the function is called during the creation
5398275SEric Cheng * of the aggregation.
5408275SEric Cheng */
5418275SEric Cheng ASSERT(grp->lg_mh == NULL || MAC_PERIM_HELD(grp->lg_mh));
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate /* create new port */
5448275SEric Cheng err = aggr_port_create(grp, port_linkid, force, &port);
5450Sstevel@tonic-gate if (err != 0)
5460Sstevel@tonic-gate return (err);
5470Sstevel@tonic-gate
5488275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &mph);
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate /* add port to list of group constituent ports */
5510Sstevel@tonic-gate cport = &grp->lg_ports;
5520Sstevel@tonic-gate while (*cport != NULL)
5530Sstevel@tonic-gate cport = &((*cport)->lp_next);
5540Sstevel@tonic-gate *cport = port;
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate /*
5570Sstevel@tonic-gate * Back reference to the group it is member of. A port always
5580Sstevel@tonic-gate * holds a reference to its group to ensure that the back
5590Sstevel@tonic-gate * reference is always valid.
5600Sstevel@tonic-gate */
5610Sstevel@tonic-gate port->lp_grp = grp;
5620Sstevel@tonic-gate AGGR_GRP_REFHOLD(grp);
5630Sstevel@tonic-gate grp->lg_nports++;
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate aggr_lacp_init_port(port);
5668275SEric Cheng mac_perim_exit(mph);
5670Sstevel@tonic-gate
5680Sstevel@tonic-gate if (pp != NULL)
5690Sstevel@tonic-gate *pp = port;
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate return (0);
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate
5740Sstevel@tonic-gate /*
57511878SVenu.Iyer@Sun.COM * Add a pseudo RX ring for the given HW ring handle.
5768275SEric Cheng */
5778275SEric Cheng static int
aggr_add_pseudo_rx_ring(aggr_port_t * port,aggr_pseudo_rx_group_t * rx_grp,mac_ring_handle_t hw_rh)5788275SEric Cheng aggr_add_pseudo_rx_ring(aggr_port_t *port,
5798275SEric Cheng aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh)
5808275SEric Cheng {
5818275SEric Cheng aggr_pseudo_rx_ring_t *ring;
5828275SEric Cheng int err;
5838275SEric Cheng int j;
5848275SEric Cheng
5858275SEric Cheng for (j = 0; j < MAX_RINGS_PER_GROUP; j++) {
5868275SEric Cheng ring = rx_grp->arg_rings + j;
5878275SEric Cheng if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE))
5888275SEric Cheng break;
5898275SEric Cheng }
5908275SEric Cheng
5918275SEric Cheng /*
59211878SVenu.Iyer@Sun.COM * No slot for this new RX ring.
5938275SEric Cheng */
5948275SEric Cheng if (j == MAX_RINGS_PER_GROUP)
5958275SEric Cheng return (EIO);
5968275SEric Cheng
5978275SEric Cheng ring->arr_flags |= MAC_PSEUDO_RING_INUSE;
5988275SEric Cheng ring->arr_hw_rh = hw_rh;
5998275SEric Cheng ring->arr_port = port;
6008275SEric Cheng rx_grp->arg_ring_cnt++;
6018275SEric Cheng
6028275SEric Cheng /*
6038275SEric Cheng * The group is already registered, dynamically add a new ring to the
6048275SEric Cheng * mac group.
6058275SEric Cheng */
6068275SEric Cheng if ((err = mac_group_add_ring(rx_grp->arg_gh, j)) != 0) {
6078275SEric Cheng ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE;
6088275SEric Cheng ring->arr_hw_rh = NULL;
6098275SEric Cheng ring->arr_port = NULL;
6108275SEric Cheng rx_grp->arg_ring_cnt--;
61111878SVenu.Iyer@Sun.COM } else {
61211878SVenu.Iyer@Sun.COM mac_hwring_setup(hw_rh, (mac_resource_handle_t)ring,
61311878SVenu.Iyer@Sun.COM mac_find_ring(rx_grp->arg_gh, j));
6148275SEric Cheng }
6158275SEric Cheng return (err);
6168275SEric Cheng }
6178275SEric Cheng
6188275SEric Cheng /*
61911878SVenu.Iyer@Sun.COM * Remove the pseudo RX ring of the given HW ring handle.
6208275SEric Cheng */
6218275SEric Cheng static void
aggr_rem_pseudo_rx_ring(aggr_pseudo_rx_group_t * rx_grp,mac_ring_handle_t hw_rh)6228275SEric Cheng aggr_rem_pseudo_rx_ring(aggr_pseudo_rx_group_t *rx_grp, mac_ring_handle_t hw_rh)
6238275SEric Cheng {
6248275SEric Cheng aggr_pseudo_rx_ring_t *ring;
6258275SEric Cheng int j;
6268275SEric Cheng
6278275SEric Cheng for (j = 0; j < MAX_RINGS_PER_GROUP; j++) {
6288275SEric Cheng ring = rx_grp->arg_rings + j;
6298275SEric Cheng if (!(ring->arr_flags & MAC_PSEUDO_RING_INUSE) ||
6308275SEric Cheng ring->arr_hw_rh != hw_rh) {
6318275SEric Cheng continue;
6328275SEric Cheng }
6338275SEric Cheng
6348275SEric Cheng mac_group_rem_ring(rx_grp->arg_gh, ring->arr_rh);
6358275SEric Cheng
6368275SEric Cheng ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE;
6378275SEric Cheng ring->arr_hw_rh = NULL;
6388275SEric Cheng ring->arr_port = NULL;
6398275SEric Cheng rx_grp->arg_ring_cnt--;
6408275SEric Cheng mac_hwring_teardown(hw_rh);
6418275SEric Cheng break;
6428275SEric Cheng }
6438275SEric Cheng }
6448275SEric Cheng
6458275SEric Cheng /*
6468275SEric Cheng * This function is called to create pseudo rings over the hardware rings of
6478275SEric Cheng * the underlying device. Note that there is a 1:1 mapping between the pseudo
6488275SEric Cheng * RX rings of the aggr and the hardware rings of the underlying port.
6498275SEric Cheng */
6508275SEric Cheng static int
aggr_add_pseudo_rx_group(aggr_port_t * port,aggr_pseudo_rx_group_t * rx_grp)6518275SEric Cheng aggr_add_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp)
6528275SEric Cheng {
6538275SEric Cheng aggr_grp_t *grp = port->lp_grp;
6548275SEric Cheng mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP];
6558275SEric Cheng aggr_unicst_addr_t *addr, *a;
6568275SEric Cheng mac_perim_handle_t pmph;
6578275SEric Cheng int hw_rh_cnt, i = 0, j;
6588275SEric Cheng int err = 0;
6598275SEric Cheng
6608275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
6618275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
6628275SEric Cheng
6638275SEric Cheng /*
6648275SEric Cheng * This function must be called after the aggr registers its mac
6658275SEric Cheng * and its RX group has been initialized.
6668275SEric Cheng */
6678275SEric Cheng ASSERT(rx_grp->arg_gh != NULL);
6688275SEric Cheng
6698275SEric Cheng /*
6708275SEric Cheng * Get the list the the underlying HW rings.
6718275SEric Cheng */
67211878SVenu.Iyer@Sun.COM hw_rh_cnt = mac_hwrings_get(port->lp_mch,
67311878SVenu.Iyer@Sun.COM &port->lp_hwgh, hw_rh, MAC_RING_TYPE_RX);
6748275SEric Cheng
6758275SEric Cheng if (port->lp_hwgh != NULL) {
6768275SEric Cheng /*
6778275SEric Cheng * Quiesce the HW ring and the mac srs on the ring. Note
6788275SEric Cheng * that the HW ring will be restarted when the pseudo ring
6798275SEric Cheng * is started. At that time all the packets will be
6808275SEric Cheng * directly passed up to the pseudo RX ring and handled
6818275SEric Cheng * by mac srs created over the pseudo RX ring.
6828275SEric Cheng */
6838275SEric Cheng mac_rx_client_quiesce(port->lp_mch);
6848275SEric Cheng mac_srs_perm_quiesce(port->lp_mch, B_TRUE);
6858275SEric Cheng }
6868275SEric Cheng
6878275SEric Cheng /*
6888275SEric Cheng * Add all the unicast addresses to the newly added port.
6898275SEric Cheng */
6908275SEric Cheng for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) {
6918275SEric Cheng if ((err = aggr_port_addmac(port, addr->aua_addr)) != 0)
6928275SEric Cheng break;
6938275SEric Cheng }
6948275SEric Cheng
6958275SEric Cheng for (i = 0; err == 0 && i < hw_rh_cnt; i++)
6968275SEric Cheng err = aggr_add_pseudo_rx_ring(port, rx_grp, hw_rh[i]);
6978275SEric Cheng
6988275SEric Cheng if (err != 0) {
6998275SEric Cheng for (j = 0; j < i; j++)
7008275SEric Cheng aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[j]);
7018275SEric Cheng
7028275SEric Cheng for (a = rx_grp->arg_macaddr; a != addr; a = a->aua_next)
7038275SEric Cheng aggr_port_remmac(port, a->aua_addr);
7048275SEric Cheng
7058275SEric Cheng if (port->lp_hwgh != NULL) {
7068275SEric Cheng mac_srs_perm_quiesce(port->lp_mch, B_FALSE);
7078275SEric Cheng mac_rx_client_restart(port->lp_mch);
7088275SEric Cheng port->lp_hwgh = NULL;
7098275SEric Cheng }
7108275SEric Cheng } else {
71111878SVenu.Iyer@Sun.COM port->lp_rx_grp_added = B_TRUE;
7128275SEric Cheng }
7138275SEric Cheng done:
7148275SEric Cheng mac_perim_exit(pmph);
7158275SEric Cheng return (err);
7168275SEric Cheng }
7178275SEric Cheng
7188275SEric Cheng /*
7198275SEric Cheng * This function is called by aggr to remove pseudo RX rings over the
7208275SEric Cheng * HW rings of the underlying port.
7218275SEric Cheng */
7228275SEric Cheng static void
aggr_rem_pseudo_rx_group(aggr_port_t * port,aggr_pseudo_rx_group_t * rx_grp)7238275SEric Cheng aggr_rem_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp)
7248275SEric Cheng {
7258275SEric Cheng aggr_grp_t *grp = port->lp_grp;
7268275SEric Cheng mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP];
7278275SEric Cheng aggr_unicst_addr_t *addr;
7288275SEric Cheng mac_group_handle_t hwgh;
7298275SEric Cheng mac_perim_handle_t pmph;
7308275SEric Cheng int hw_rh_cnt, i;
7318275SEric Cheng
7328275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
7338275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
7348275SEric Cheng
73511878SVenu.Iyer@Sun.COM if (!port->lp_rx_grp_added)
7368275SEric Cheng goto done;
7378275SEric Cheng
7388275SEric Cheng ASSERT(rx_grp->arg_gh != NULL);
73911878SVenu.Iyer@Sun.COM hw_rh_cnt = mac_hwrings_get(port->lp_mch,
74011878SVenu.Iyer@Sun.COM &hwgh, hw_rh, MAC_RING_TYPE_RX);
7418275SEric Cheng
7428275SEric Cheng /*
7438275SEric Cheng * If hw_rh_cnt is 0, it means that the underlying port does not
7448275SEric Cheng * support RX rings. Directly return in this case.
7458275SEric Cheng */
7468275SEric Cheng for (i = 0; i < hw_rh_cnt; i++)
7478275SEric Cheng aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[i]);
7488275SEric Cheng
7498275SEric Cheng for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next)
7508275SEric Cheng aggr_port_remmac(port, addr->aua_addr);
7518275SEric Cheng
7528275SEric Cheng if (port->lp_hwgh != NULL) {
7538275SEric Cheng port->lp_hwgh = NULL;
7548275SEric Cheng
7558275SEric Cheng /*
7568275SEric Cheng * First clear the permanent-quiesced flag of the RX srs then
7578275SEric Cheng * restart the HW ring and the mac srs on the ring. Note that
7588275SEric Cheng * the HW ring and associated SRS will soon been removed when
7598275SEric Cheng * the port is removed from the aggr.
7608275SEric Cheng */
7618275SEric Cheng mac_srs_perm_quiesce(port->lp_mch, B_FALSE);
7628275SEric Cheng mac_rx_client_restart(port->lp_mch);
7638275SEric Cheng }
7648275SEric Cheng
76511878SVenu.Iyer@Sun.COM port->lp_rx_grp_added = B_FALSE;
76611878SVenu.Iyer@Sun.COM done:
76711878SVenu.Iyer@Sun.COM mac_perim_exit(pmph);
76811878SVenu.Iyer@Sun.COM }
76911878SVenu.Iyer@Sun.COM
77011878SVenu.Iyer@Sun.COM /*
77111878SVenu.Iyer@Sun.COM * Add a pseudo TX ring for the given HW ring handle.
77211878SVenu.Iyer@Sun.COM */
77311878SVenu.Iyer@Sun.COM static int
aggr_add_pseudo_tx_ring(aggr_port_t * port,aggr_pseudo_tx_group_t * tx_grp,mac_ring_handle_t hw_rh,mac_ring_handle_t * pseudo_rh)77411878SVenu.Iyer@Sun.COM aggr_add_pseudo_tx_ring(aggr_port_t *port,
77511878SVenu.Iyer@Sun.COM aggr_pseudo_tx_group_t *tx_grp, mac_ring_handle_t hw_rh,
77611878SVenu.Iyer@Sun.COM mac_ring_handle_t *pseudo_rh)
77711878SVenu.Iyer@Sun.COM {
77811878SVenu.Iyer@Sun.COM aggr_pseudo_tx_ring_t *ring;
77911878SVenu.Iyer@Sun.COM int err;
78011878SVenu.Iyer@Sun.COM int i;
78111878SVenu.Iyer@Sun.COM
78211878SVenu.Iyer@Sun.COM ASSERT(MAC_PERIM_HELD(port->lp_mh));
78311878SVenu.Iyer@Sun.COM for (i = 0; i < MAX_RINGS_PER_GROUP; i++) {
78411878SVenu.Iyer@Sun.COM ring = tx_grp->atg_rings + i;
78511878SVenu.Iyer@Sun.COM if (!(ring->atr_flags & MAC_PSEUDO_RING_INUSE))
78611878SVenu.Iyer@Sun.COM break;
78711878SVenu.Iyer@Sun.COM }
78811878SVenu.Iyer@Sun.COM /*
78911878SVenu.Iyer@Sun.COM * No slot for this new TX ring.
79011878SVenu.Iyer@Sun.COM */
79111878SVenu.Iyer@Sun.COM if (i == MAX_RINGS_PER_GROUP)
79211878SVenu.Iyer@Sun.COM return (EIO);
79311878SVenu.Iyer@Sun.COM /*
79411878SVenu.Iyer@Sun.COM * The following 4 statements needs to be done before
79511878SVenu.Iyer@Sun.COM * calling mac_group_add_ring(). Otherwise it will
79611878SVenu.Iyer@Sun.COM * result in an assertion failure in mac_init_ring().
79711878SVenu.Iyer@Sun.COM */
79811878SVenu.Iyer@Sun.COM ring->atr_flags |= MAC_PSEUDO_RING_INUSE;
79911878SVenu.Iyer@Sun.COM ring->atr_hw_rh = hw_rh;
80011878SVenu.Iyer@Sun.COM ring->atr_port = port;
80111878SVenu.Iyer@Sun.COM tx_grp->atg_ring_cnt++;
80211878SVenu.Iyer@Sun.COM
80311878SVenu.Iyer@Sun.COM /*
80411878SVenu.Iyer@Sun.COM * The TX side has no concept of ring groups unlike RX groups.
80511878SVenu.Iyer@Sun.COM * There is just a single group which stores all the TX rings.
80611878SVenu.Iyer@Sun.COM * This group will be used to store aggr's pseudo TX rings.
80711878SVenu.Iyer@Sun.COM */
80811878SVenu.Iyer@Sun.COM if ((err = mac_group_add_ring(tx_grp->atg_gh, i)) != 0) {
80911878SVenu.Iyer@Sun.COM ring->atr_flags &= ~MAC_PSEUDO_RING_INUSE;
81011878SVenu.Iyer@Sun.COM ring->atr_hw_rh = NULL;
81111878SVenu.Iyer@Sun.COM ring->atr_port = NULL;
81211878SVenu.Iyer@Sun.COM tx_grp->atg_ring_cnt--;
81311878SVenu.Iyer@Sun.COM } else {
81411878SVenu.Iyer@Sun.COM *pseudo_rh = mac_find_ring(tx_grp->atg_gh, i);
81511878SVenu.Iyer@Sun.COM if (hw_rh != NULL) {
81611878SVenu.Iyer@Sun.COM mac_hwring_setup(hw_rh, (mac_resource_handle_t)ring,
81711878SVenu.Iyer@Sun.COM mac_find_ring(tx_grp->atg_gh, i));
81811878SVenu.Iyer@Sun.COM }
81911878SVenu.Iyer@Sun.COM }
82011878SVenu.Iyer@Sun.COM return (err);
82111878SVenu.Iyer@Sun.COM }
82211878SVenu.Iyer@Sun.COM
82311878SVenu.Iyer@Sun.COM /*
82411878SVenu.Iyer@Sun.COM * Remove the pseudo TX ring of the given HW ring handle.
82511878SVenu.Iyer@Sun.COM */
82611878SVenu.Iyer@Sun.COM static void
aggr_rem_pseudo_tx_ring(aggr_pseudo_tx_group_t * tx_grp,mac_ring_handle_t pseudo_hw_rh)82711878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_ring(aggr_pseudo_tx_group_t *tx_grp,
82811878SVenu.Iyer@Sun.COM mac_ring_handle_t pseudo_hw_rh)
82911878SVenu.Iyer@Sun.COM {
83011878SVenu.Iyer@Sun.COM aggr_pseudo_tx_ring_t *ring;
83111878SVenu.Iyer@Sun.COM int i;
83211878SVenu.Iyer@Sun.COM
83311878SVenu.Iyer@Sun.COM for (i = 0; i < MAX_RINGS_PER_GROUP; i++) {
83411878SVenu.Iyer@Sun.COM ring = tx_grp->atg_rings + i;
83511878SVenu.Iyer@Sun.COM if (ring->atr_rh != pseudo_hw_rh)
83611878SVenu.Iyer@Sun.COM continue;
83711878SVenu.Iyer@Sun.COM
83811878SVenu.Iyer@Sun.COM ASSERT(ring->atr_flags & MAC_PSEUDO_RING_INUSE);
83911878SVenu.Iyer@Sun.COM mac_group_rem_ring(tx_grp->atg_gh, pseudo_hw_rh);
84011878SVenu.Iyer@Sun.COM ring->atr_flags &= ~MAC_PSEUDO_RING_INUSE;
84111878SVenu.Iyer@Sun.COM mac_hwring_teardown(ring->atr_hw_rh);
84211878SVenu.Iyer@Sun.COM ring->atr_hw_rh = NULL;
84311878SVenu.Iyer@Sun.COM ring->atr_port = NULL;
84411878SVenu.Iyer@Sun.COM tx_grp->atg_ring_cnt--;
84511878SVenu.Iyer@Sun.COM break;
84611878SVenu.Iyer@Sun.COM }
84711878SVenu.Iyer@Sun.COM }
84811878SVenu.Iyer@Sun.COM
84911878SVenu.Iyer@Sun.COM /*
85011878SVenu.Iyer@Sun.COM * This function is called to create pseudo rings over hardware rings of
85111878SVenu.Iyer@Sun.COM * the underlying device. There is a 1:1 mapping between the pseudo TX
85211878SVenu.Iyer@Sun.COM * rings of the aggr and the hardware rings of the underlying port.
85311878SVenu.Iyer@Sun.COM */
85411878SVenu.Iyer@Sun.COM static int
aggr_add_pseudo_tx_group(aggr_port_t * port,aggr_pseudo_tx_group_t * tx_grp)85511878SVenu.Iyer@Sun.COM aggr_add_pseudo_tx_group(aggr_port_t *port, aggr_pseudo_tx_group_t *tx_grp)
85611878SVenu.Iyer@Sun.COM {
85711878SVenu.Iyer@Sun.COM aggr_grp_t *grp = port->lp_grp;
85811878SVenu.Iyer@Sun.COM mac_ring_handle_t hw_rh[MAX_RINGS_PER_GROUP], pseudo_rh;
85911878SVenu.Iyer@Sun.COM mac_perim_handle_t pmph;
86011878SVenu.Iyer@Sun.COM int hw_rh_cnt, i = 0, j;
86111878SVenu.Iyer@Sun.COM int err = 0;
86211878SVenu.Iyer@Sun.COM
86311878SVenu.Iyer@Sun.COM ASSERT(MAC_PERIM_HELD(grp->lg_mh));
86411878SVenu.Iyer@Sun.COM mac_perim_enter_by_mh(port->lp_mh, &pmph);
86511878SVenu.Iyer@Sun.COM
86611878SVenu.Iyer@Sun.COM /*
86711878SVenu.Iyer@Sun.COM * Get the list the the underlying HW rings.
86811878SVenu.Iyer@Sun.COM */
86911878SVenu.Iyer@Sun.COM hw_rh_cnt = mac_hwrings_get(port->lp_mch,
87011878SVenu.Iyer@Sun.COM NULL, hw_rh, MAC_RING_TYPE_TX);
87111878SVenu.Iyer@Sun.COM
87211878SVenu.Iyer@Sun.COM /*
87311878SVenu.Iyer@Sun.COM * Even if the underlying NIC does not have TX rings, we
87411878SVenu.Iyer@Sun.COM * still make a psuedo TX ring for that NIC with NULL as
87511878SVenu.Iyer@Sun.COM * the ring handle.
87611878SVenu.Iyer@Sun.COM */
87711878SVenu.Iyer@Sun.COM if (hw_rh_cnt == 0)
87811878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt = 1;
87911878SVenu.Iyer@Sun.COM else
88011878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt = hw_rh_cnt;
88111878SVenu.Iyer@Sun.COM
88211878SVenu.Iyer@Sun.COM port->lp_tx_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) *
88311878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt), KM_SLEEP);
88411878SVenu.Iyer@Sun.COM port->lp_pseudo_tx_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) *
88511878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt), KM_SLEEP);
88611878SVenu.Iyer@Sun.COM
88711878SVenu.Iyer@Sun.COM if (hw_rh_cnt == 0) {
88811878SVenu.Iyer@Sun.COM if ((err = aggr_add_pseudo_tx_ring(port, tx_grp,
88911878SVenu.Iyer@Sun.COM NULL, &pseudo_rh)) == 0) {
89011878SVenu.Iyer@Sun.COM port->lp_tx_rings[0] = NULL;
89111878SVenu.Iyer@Sun.COM port->lp_pseudo_tx_rings[0] = pseudo_rh;
89211878SVenu.Iyer@Sun.COM }
89311878SVenu.Iyer@Sun.COM } else {
89411878SVenu.Iyer@Sun.COM for (i = 0; err == 0 && i < hw_rh_cnt; i++) {
89511878SVenu.Iyer@Sun.COM err = aggr_add_pseudo_tx_ring(port,
89611878SVenu.Iyer@Sun.COM tx_grp, hw_rh[i], &pseudo_rh);
89711878SVenu.Iyer@Sun.COM if (err != 0)
89811878SVenu.Iyer@Sun.COM break;
89911878SVenu.Iyer@Sun.COM port->lp_tx_rings[i] = hw_rh[i];
90011878SVenu.Iyer@Sun.COM port->lp_pseudo_tx_rings[i] = pseudo_rh;
90111878SVenu.Iyer@Sun.COM }
90211878SVenu.Iyer@Sun.COM }
90311878SVenu.Iyer@Sun.COM
90411878SVenu.Iyer@Sun.COM if (err != 0) {
90511878SVenu.Iyer@Sun.COM if (hw_rh_cnt != 0) {
90611878SVenu.Iyer@Sun.COM for (j = 0; j < i; j++) {
90711878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_ring(tx_grp,
90811878SVenu.Iyer@Sun.COM port->lp_pseudo_tx_rings[j]);
90911878SVenu.Iyer@Sun.COM }
91011878SVenu.Iyer@Sun.COM }
91111878SVenu.Iyer@Sun.COM kmem_free(port->lp_tx_rings,
91211878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt));
91311878SVenu.Iyer@Sun.COM kmem_free(port->lp_pseudo_tx_rings,
91411878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt));
91511878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt = 0;
91611878SVenu.Iyer@Sun.COM } else {
91711878SVenu.Iyer@Sun.COM port->lp_tx_grp_added = B_TRUE;
91811878SVenu.Iyer@Sun.COM port->lp_tx_notify_mh = mac_client_tx_notify(port->lp_mch,
91911878SVenu.Iyer@Sun.COM aggr_tx_ring_update, port);
92011878SVenu.Iyer@Sun.COM }
92111878SVenu.Iyer@Sun.COM mac_perim_exit(pmph);
92211878SVenu.Iyer@Sun.COM return (err);
92311878SVenu.Iyer@Sun.COM }
92411878SVenu.Iyer@Sun.COM
92511878SVenu.Iyer@Sun.COM /*
92611878SVenu.Iyer@Sun.COM * This function is called by aggr to remove pseudo TX rings over the
92711878SVenu.Iyer@Sun.COM * HW rings of the underlying port.
92811878SVenu.Iyer@Sun.COM */
92911878SVenu.Iyer@Sun.COM static void
aggr_rem_pseudo_tx_group(aggr_port_t * port,aggr_pseudo_tx_group_t * tx_grp)93011878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_group(aggr_port_t *port, aggr_pseudo_tx_group_t *tx_grp)
93111878SVenu.Iyer@Sun.COM {
93211878SVenu.Iyer@Sun.COM aggr_grp_t *grp = port->lp_grp;
93311878SVenu.Iyer@Sun.COM mac_perim_handle_t pmph;
93411878SVenu.Iyer@Sun.COM int i;
93511878SVenu.Iyer@Sun.COM
93611878SVenu.Iyer@Sun.COM ASSERT(MAC_PERIM_HELD(grp->lg_mh));
93711878SVenu.Iyer@Sun.COM mac_perim_enter_by_mh(port->lp_mh, &pmph);
93811878SVenu.Iyer@Sun.COM
93911878SVenu.Iyer@Sun.COM if (!port->lp_tx_grp_added)
94011878SVenu.Iyer@Sun.COM goto done;
94111878SVenu.Iyer@Sun.COM
94211878SVenu.Iyer@Sun.COM ASSERT(tx_grp->atg_gh != NULL);
94311878SVenu.Iyer@Sun.COM
94411878SVenu.Iyer@Sun.COM for (i = 0; i < port->lp_tx_ring_cnt; i++)
94511878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_ring(tx_grp, port->lp_pseudo_tx_rings[i]);
94611878SVenu.Iyer@Sun.COM
94711878SVenu.Iyer@Sun.COM kmem_free(port->lp_tx_rings,
94811878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt));
94911878SVenu.Iyer@Sun.COM kmem_free(port->lp_pseudo_tx_rings,
95011878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * port->lp_tx_ring_cnt));
95111878SVenu.Iyer@Sun.COM
95211878SVenu.Iyer@Sun.COM port->lp_tx_ring_cnt = 0;
95311878SVenu.Iyer@Sun.COM (void) mac_client_tx_notify(port->lp_mch, NULL, port->lp_tx_notify_mh);
95411878SVenu.Iyer@Sun.COM port->lp_tx_grp_added = B_FALSE;
9558275SEric Cheng done:
9568275SEric Cheng mac_perim_exit(pmph);
9578275SEric Cheng }
9588275SEric Cheng
9598275SEric Cheng static int
aggr_pseudo_disable_intr(mac_intr_handle_t ih)9608275SEric Cheng aggr_pseudo_disable_intr(mac_intr_handle_t ih)
9618275SEric Cheng {
9628275SEric Cheng aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih;
9638275SEric Cheng return (mac_hwring_disable_intr(rr_ring->arr_hw_rh));
9648275SEric Cheng }
9658275SEric Cheng
9668275SEric Cheng static int
aggr_pseudo_enable_intr(mac_intr_handle_t ih)9678275SEric Cheng aggr_pseudo_enable_intr(mac_intr_handle_t ih)
9688275SEric Cheng {
9698275SEric Cheng aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)ih;
9708275SEric Cheng return (mac_hwring_enable_intr(rr_ring->arr_hw_rh));
9718275SEric Cheng }
9728275SEric Cheng
9738275SEric Cheng static int
aggr_pseudo_start_ring(mac_ring_driver_t arg,uint64_t mr_gen)9748275SEric Cheng aggr_pseudo_start_ring(mac_ring_driver_t arg, uint64_t mr_gen)
9758275SEric Cheng {
9768275SEric Cheng aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg;
9778275SEric Cheng int err;
9788275SEric Cheng
9798275SEric Cheng err = mac_hwring_start(rr_ring->arr_hw_rh);
9808275SEric Cheng if (err == 0)
9818275SEric Cheng rr_ring->arr_gen = mr_gen;
9828275SEric Cheng return (err);
9838275SEric Cheng }
9848275SEric Cheng
9858275SEric Cheng static void
aggr_pseudo_stop_ring(mac_ring_driver_t arg)9868275SEric Cheng aggr_pseudo_stop_ring(mac_ring_driver_t arg)
9878275SEric Cheng {
9888275SEric Cheng aggr_pseudo_rx_ring_t *rr_ring = (aggr_pseudo_rx_ring_t *)arg;
9898275SEric Cheng mac_hwring_stop(rr_ring->arr_hw_rh);
9908275SEric Cheng }
9918275SEric Cheng
9928275SEric Cheng /*
9930Sstevel@tonic-gate * Add one or more ports to an existing link aggregation group.
9940Sstevel@tonic-gate */
9950Sstevel@tonic-gate int
aggr_grp_add_ports(datalink_id_t linkid,uint_t nports,boolean_t force,laioc_port_t * ports)9965895Syz147064 aggr_grp_add_ports(datalink_id_t linkid, uint_t nports, boolean_t force,
9975895Syz147064 laioc_port_t *ports)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate int rc, i, nadded = 0;
10000Sstevel@tonic-gate aggr_grp_t *grp = NULL;
10010Sstevel@tonic-gate aggr_port_t *port;
10022163Syz147064 boolean_t link_state_changed = B_FALSE;
10038275SEric Cheng mac_perim_handle_t mph, pmph;
10040Sstevel@tonic-gate
10055895Syz147064 /* get group corresponding to linkid */
1006269Sericheng rw_enter(&aggr_grp_lock, RW_READER);
10075895Syz147064 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
1008269Sericheng (mod_hash_val_t *)&grp) != 0) {
1009269Sericheng rw_exit(&aggr_grp_lock);
1010269Sericheng return (ENOENT);
10110Sstevel@tonic-gate }
10120Sstevel@tonic-gate AGGR_GRP_REFHOLD(grp);
10138275SEric Cheng
10148275SEric Cheng /*
10158275SEric Cheng * Hold the perimeter so that the aggregation won't be destroyed.
10168275SEric Cheng */
10178275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
1018269Sericheng rw_exit(&aggr_grp_lock);
10190Sstevel@tonic-gate
10200Sstevel@tonic-gate /* add the specified ports to group */
10210Sstevel@tonic-gate for (i = 0; i < nports; i++) {
10220Sstevel@tonic-gate /* add port to group */
10235895Syz147064 if ((rc = aggr_grp_add_port(grp, ports[i].lp_linkid,
10245895Syz147064 force, &port)) != 0) {
10250Sstevel@tonic-gate goto bail;
10262311Sseb }
10270Sstevel@tonic-gate ASSERT(port != NULL);
10280Sstevel@tonic-gate nadded++;
10290Sstevel@tonic-gate
10300Sstevel@tonic-gate /* check capabilities */
10312803Snd99603 if (!aggr_grp_capab_check(grp, port) ||
10325895Syz147064 !aggr_grp_sdu_check(grp, port) ||
10335895Syz147064 !aggr_grp_margin_check(grp, port)) {
10340Sstevel@tonic-gate rc = ENOTSUP;
10350Sstevel@tonic-gate goto bail;
10360Sstevel@tonic-gate }
10370Sstevel@tonic-gate
10388275SEric Cheng /*
10398275SEric Cheng * Create the pseudo ring for each HW ring of the underlying
10408275SEric Cheng * port.
10418275SEric Cheng */
104211878SVenu.Iyer@Sun.COM rc = aggr_add_pseudo_tx_group(port, &grp->lg_tx_group);
104311878SVenu.Iyer@Sun.COM if (rc != 0)
104411878SVenu.Iyer@Sun.COM goto bail;
10458275SEric Cheng rc = aggr_add_pseudo_rx_group(port, &grp->lg_rx_group);
10468275SEric Cheng if (rc != 0)
10478275SEric Cheng goto bail;
10488275SEric Cheng
10498275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
10508275SEric Cheng
10518275SEric Cheng /* set LACP mode */
10528275SEric Cheng aggr_port_lacp_set_mode(grp, port);
10538275SEric Cheng
10540Sstevel@tonic-gate /* start port if group has already been started */
10550Sstevel@tonic-gate if (grp->lg_started) {
10560Sstevel@tonic-gate rc = aggr_port_start(port);
10570Sstevel@tonic-gate if (rc != 0) {
10588275SEric Cheng mac_perim_exit(pmph);
10590Sstevel@tonic-gate goto bail;
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate
10628275SEric Cheng /*
10638275SEric Cheng * Turn on the promiscuous mode over the port when it
10648275SEric Cheng * is requested to be turned on to receive the
10658275SEric Cheng * non-primary address over a port, or the promiscous
10668275SEric Cheng * mode is enabled over the aggr.
10678275SEric Cheng */
10688275SEric Cheng if (grp->lg_promisc || port->lp_prom_addr != NULL) {
10698275SEric Cheng rc = aggr_port_promisc(port, B_TRUE);
10708275SEric Cheng if (rc != 0) {
10718275SEric Cheng mac_perim_exit(pmph);
10728275SEric Cheng goto bail;
10738275SEric Cheng }
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate }
10768275SEric Cheng mac_perim_exit(pmph);
10772163Syz147064
10782163Syz147064 /*
10792163Syz147064 * Attach each port if necessary.
10802163Syz147064 */
10818275SEric Cheng if (aggr_port_notify_link(grp, port))
10825102Syz147064 link_state_changed = B_TRUE;
10838275SEric Cheng
10848275SEric Cheng /*
10858275SEric Cheng * Initialize the callback functions for this port.
10868275SEric Cheng */
10878275SEric Cheng aggr_port_init_callbacks(port);
10880Sstevel@tonic-gate }
10890Sstevel@tonic-gate
10900Sstevel@tonic-gate /* update the MAC address of the constituent ports */
10915102Syz147064 if (aggr_grp_update_ports_mac(grp))
10925102Syz147064 link_state_changed = B_TRUE;
10932163Syz147064
10942163Syz147064 if (link_state_changed)
10952311Sseb mac_link_update(grp->lg_mh, grp->lg_link_state);
10960Sstevel@tonic-gate
10970Sstevel@tonic-gate bail:
10980Sstevel@tonic-gate if (rc != 0) {
10990Sstevel@tonic-gate /* stop and remove ports that have been added */
11008275SEric Cheng for (i = 0; i < nadded; i++) {
11015895Syz147064 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid);
11020Sstevel@tonic-gate ASSERT(port != NULL);
11030Sstevel@tonic-gate if (grp->lg_started) {
11048275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
11058275SEric Cheng (void) aggr_port_promisc(port, B_FALSE);
11060Sstevel@tonic-gate aggr_port_stop(port);
11078275SEric Cheng mac_perim_exit(pmph);
11080Sstevel@tonic-gate }
110911878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group);
11108275SEric Cheng aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group);
11112047Syz147064 (void) aggr_grp_rem_port(grp, port, NULL, NULL);
11120Sstevel@tonic-gate }
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate
11158275SEric Cheng mac_perim_exit(mph);
11160Sstevel@tonic-gate AGGR_GRP_REFRELE(grp);
11170Sstevel@tonic-gate return (rc);
11180Sstevel@tonic-gate }
11190Sstevel@tonic-gate
11208275SEric Cheng static int
aggr_grp_modify_common(aggr_grp_t * grp,uint8_t update_mask,uint32_t policy,boolean_t mac_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer)11218275SEric Cheng aggr_grp_modify_common(aggr_grp_t *grp, uint8_t update_mask, uint32_t policy,
11228275SEric Cheng boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
11238275SEric Cheng aggr_lacp_timer_t lacp_timer)
11240Sstevel@tonic-gate {
11250Sstevel@tonic-gate boolean_t mac_addr_changed = B_FALSE;
11262047Syz147064 boolean_t link_state_changed = B_FALSE;
11278275SEric Cheng mac_perim_handle_t pmph;
11280Sstevel@tonic-gate
11298275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
11300Sstevel@tonic-gate
11310Sstevel@tonic-gate /* validate fixed address if specified */
11320Sstevel@tonic-gate if ((update_mask & AGGR_MODIFY_MAC) && mac_fixed &&
11330Sstevel@tonic-gate ((bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) ||
11340Sstevel@tonic-gate (mac_addr[0] & 0x01))) {
11358275SEric Cheng return (EINVAL);
11360Sstevel@tonic-gate }
11370Sstevel@tonic-gate
11380Sstevel@tonic-gate /* update policy if requested */
11390Sstevel@tonic-gate if (update_mask & AGGR_MODIFY_POLICY)
11400Sstevel@tonic-gate aggr_send_update_policy(grp, policy);
11410Sstevel@tonic-gate
11420Sstevel@tonic-gate /* update unicast MAC address if requested */
11430Sstevel@tonic-gate if (update_mask & AGGR_MODIFY_MAC) {
11440Sstevel@tonic-gate if (mac_fixed) {
11450Sstevel@tonic-gate /* user-supplied MAC address */
11460Sstevel@tonic-gate grp->lg_mac_addr_port = NULL;
11470Sstevel@tonic-gate if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) != 0) {
11480Sstevel@tonic-gate bcopy(mac_addr, grp->lg_addr, ETHERADDRL);
11490Sstevel@tonic-gate mac_addr_changed = B_TRUE;
11500Sstevel@tonic-gate }
11510Sstevel@tonic-gate } else if (grp->lg_addr_fixed) {
11520Sstevel@tonic-gate /* switch from user-supplied to automatic */
11530Sstevel@tonic-gate aggr_port_t *port = grp->lg_ports;
11540Sstevel@tonic-gate
11558275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
11560Sstevel@tonic-gate bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL);
11570Sstevel@tonic-gate grp->lg_mac_addr_port = port;
11580Sstevel@tonic-gate mac_addr_changed = B_TRUE;
11598275SEric Cheng mac_perim_exit(pmph);
11600Sstevel@tonic-gate }
11610Sstevel@tonic-gate grp->lg_addr_fixed = mac_fixed;
11620Sstevel@tonic-gate }
11630Sstevel@tonic-gate
11640Sstevel@tonic-gate if (mac_addr_changed)
11652047Syz147064 link_state_changed = aggr_grp_update_ports_mac(grp);
11660Sstevel@tonic-gate
11670Sstevel@tonic-gate if (update_mask & AGGR_MODIFY_LACP_MODE)
11680Sstevel@tonic-gate aggr_lacp_update_mode(grp, lacp_mode);
11690Sstevel@tonic-gate
11708275SEric Cheng if (update_mask & AGGR_MODIFY_LACP_TIMER)
11710Sstevel@tonic-gate aggr_lacp_update_timer(grp, lacp_timer);
11720Sstevel@tonic-gate
11738275SEric Cheng if (link_state_changed)
11748275SEric Cheng mac_link_update(grp->lg_mh, grp->lg_link_state);
11758275SEric Cheng
11768275SEric Cheng if (mac_addr_changed)
11778275SEric Cheng mac_unicst_update(grp->lg_mh, grp->lg_addr);
11788275SEric Cheng
11798275SEric Cheng return (0);
11808275SEric Cheng }
11812047Syz147064
11828275SEric Cheng /*
11838275SEric Cheng * Update properties of an existing link aggregation group.
11848275SEric Cheng */
11858275SEric Cheng int
aggr_grp_modify(datalink_id_t linkid,uint8_t update_mask,uint32_t policy,boolean_t mac_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer)11868275SEric Cheng aggr_grp_modify(datalink_id_t linkid, uint8_t update_mask, uint32_t policy,
11878275SEric Cheng boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
11888275SEric Cheng aggr_lacp_timer_t lacp_timer)
11898275SEric Cheng {
11908275SEric Cheng aggr_grp_t *grp = NULL;
11918275SEric Cheng mac_perim_handle_t mph;
11928275SEric Cheng int err;
11932047Syz147064
11948275SEric Cheng /* get group corresponding to linkid */
11958275SEric Cheng rw_enter(&aggr_grp_lock, RW_READER);
11968275SEric Cheng if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
11978275SEric Cheng (mod_hash_val_t *)&grp) != 0) {
11988275SEric Cheng rw_exit(&aggr_grp_lock);
11998275SEric Cheng return (ENOENT);
12002047Syz147064 }
12018275SEric Cheng AGGR_GRP_REFHOLD(grp);
12022047Syz147064
12038275SEric Cheng /*
12048275SEric Cheng * Hold the perimeter so that the aggregation won't be destroyed.
12058275SEric Cheng */
12068275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
12078275SEric Cheng rw_exit(&aggr_grp_lock);
12080Sstevel@tonic-gate
12098275SEric Cheng err = aggr_grp_modify_common(grp, update_mask, policy, mac_fixed,
12108275SEric Cheng mac_addr, lacp_mode, lacp_timer);
12110Sstevel@tonic-gate
12128275SEric Cheng mac_perim_exit(mph);
12138275SEric Cheng AGGR_GRP_REFRELE(grp);
12148275SEric Cheng return (err);
12150Sstevel@tonic-gate }
12160Sstevel@tonic-gate
12170Sstevel@tonic-gate /*
12180Sstevel@tonic-gate * Create a new link aggregation group upon request from administrator.
12190Sstevel@tonic-gate * Returns 0 on success, an errno on failure.
12200Sstevel@tonic-gate */
12210Sstevel@tonic-gate int
aggr_grp_create(datalink_id_t linkid,uint32_t key,uint_t nports,laioc_port_t * ports,uint32_t policy,boolean_t mac_fixed,boolean_t force,uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer,cred_t * credp)12225895Syz147064 aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
12235895Syz147064 laioc_port_t *ports, uint32_t policy, boolean_t mac_fixed, boolean_t force,
122410616SSebastien.Roy@Sun.COM uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer,
122510616SSebastien.Roy@Sun.COM cred_t *credp)
12260Sstevel@tonic-gate {
12270Sstevel@tonic-gate aggr_grp_t *grp = NULL;
12280Sstevel@tonic-gate aggr_port_t *port;
12292311Sseb mac_register_t *mac;
12302047Syz147064 boolean_t link_state_changed;
12318275SEric Cheng mac_perim_handle_t mph;
12320Sstevel@tonic-gate int err;
12330Sstevel@tonic-gate int i;
123411878SVenu.Iyer@Sun.COM kt_did_t tid = 0;
12350Sstevel@tonic-gate
12360Sstevel@tonic-gate /* need at least one port */
12370Sstevel@tonic-gate if (nports == 0)
12380Sstevel@tonic-gate return (EINVAL);
12390Sstevel@tonic-gate
1240269Sericheng rw_enter(&aggr_grp_lock, RW_WRITER);
12410Sstevel@tonic-gate
12425895Syz147064 /* does a group with the same linkid already exist? */
12435895Syz147064 err = mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
1244269Sericheng (mod_hash_val_t *)&grp);
1245269Sericheng if (err == 0) {
1246269Sericheng rw_exit(&aggr_grp_lock);
12470Sstevel@tonic-gate return (EEXIST);
12480Sstevel@tonic-gate }
12490Sstevel@tonic-gate
12500Sstevel@tonic-gate grp = kmem_cache_alloc(aggr_grp_cache, KM_SLEEP);
12510Sstevel@tonic-gate
12520Sstevel@tonic-gate grp->lg_refs = 1;
12532047Syz147064 grp->lg_closing = B_FALSE;
12545895Syz147064 grp->lg_force = force;
12555895Syz147064 grp->lg_linkid = linkid;
125610616SSebastien.Roy@Sun.COM grp->lg_zoneid = crgetzoneid(credp);
12570Sstevel@tonic-gate grp->lg_ifspeed = 0;
12580Sstevel@tonic-gate grp->lg_link_state = LINK_STATE_UNKNOWN;
12590Sstevel@tonic-gate grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN;
12600Sstevel@tonic-gate grp->lg_started = B_FALSE;
12610Sstevel@tonic-gate grp->lg_promisc = B_FALSE;
12628275SEric Cheng grp->lg_lacp_done = B_FALSE;
126311878SVenu.Iyer@Sun.COM grp->lg_tx_notify_done = B_FALSE;
12648275SEric Cheng grp->lg_lacp_head = grp->lg_lacp_tail = NULL;
12658275SEric Cheng grp->lg_lacp_rx_thread = thread_create(NULL, 0,
12668275SEric Cheng aggr_lacp_rx_thread, grp, 0, &p0, TS_RUN, minclsyspri);
126711878SVenu.Iyer@Sun.COM grp->lg_tx_notify_thread = thread_create(NULL, 0,
126811878SVenu.Iyer@Sun.COM aggr_tx_notify_thread, grp, 0, &p0, TS_RUN, minclsyspri);
126911878SVenu.Iyer@Sun.COM grp->lg_tx_blocked_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) *
127011878SVenu.Iyer@Sun.COM MAX_RINGS_PER_GROUP), KM_SLEEP);
127111878SVenu.Iyer@Sun.COM grp->lg_tx_blocked_cnt = 0;
12728275SEric Cheng bzero(&grp->lg_rx_group, sizeof (aggr_pseudo_rx_group_t));
127311878SVenu.Iyer@Sun.COM bzero(&grp->lg_tx_group, sizeof (aggr_pseudo_tx_group_t));
12740Sstevel@tonic-gate aggr_lacp_init_grp(grp);
12750Sstevel@tonic-gate
12760Sstevel@tonic-gate /* add MAC ports to group */
12770Sstevel@tonic-gate grp->lg_ports = NULL;
12780Sstevel@tonic-gate grp->lg_nports = 0;
12790Sstevel@tonic-gate grp->lg_nattached_ports = 0;
12800Sstevel@tonic-gate grp->lg_ntx_ports = 0;
12810Sstevel@tonic-gate
12825895Syz147064 /*
12835895Syz147064 * If key is not specified by the user, allocate the key.
12845895Syz147064 */
12855895Syz147064 if ((key == 0) && ((key = (uint32_t)id_alloc(key_ids)) == 0)) {
12865895Syz147064 err = ENOMEM;
12875895Syz147064 goto bail;
12885895Syz147064 }
12895895Syz147064 grp->lg_key = key;
12905895Syz147064
12910Sstevel@tonic-gate for (i = 0; i < nports; i++) {
12925895Syz147064 err = aggr_grp_add_port(grp, ports[i].lp_linkid, force, NULL);
12930Sstevel@tonic-gate if (err != 0)
12940Sstevel@tonic-gate goto bail;
12950Sstevel@tonic-gate }
12960Sstevel@tonic-gate
12970Sstevel@tonic-gate /*
12980Sstevel@tonic-gate * If no explicit MAC address was specified by the administrator,
12990Sstevel@tonic-gate * set it to the MAC address of the first port.
13000Sstevel@tonic-gate */
13010Sstevel@tonic-gate grp->lg_addr_fixed = mac_fixed;
13020Sstevel@tonic-gate if (grp->lg_addr_fixed) {
13030Sstevel@tonic-gate /* validate specified address */
13040Sstevel@tonic-gate if (bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) {
13050Sstevel@tonic-gate err = EINVAL;
13060Sstevel@tonic-gate goto bail;
13070Sstevel@tonic-gate }
13080Sstevel@tonic-gate bcopy(mac_addr, grp->lg_addr, ETHERADDRL);
13090Sstevel@tonic-gate } else {
13100Sstevel@tonic-gate bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL);
13110Sstevel@tonic-gate grp->lg_mac_addr_port = grp->lg_ports;
13120Sstevel@tonic-gate }
13130Sstevel@tonic-gate
13140Sstevel@tonic-gate /* set the initial group capabilities */
13150Sstevel@tonic-gate aggr_grp_capab_set(grp);
13160Sstevel@tonic-gate
13175895Syz147064 if ((mac = mac_alloc(MAC_VERSION)) == NULL) {
13185895Syz147064 err = ENOMEM;
13192311Sseb goto bail;
13205895Syz147064 }
13212311Sseb mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
13222311Sseb mac->m_driver = grp;
13232311Sseb mac->m_dip = aggr_dip;
13245895Syz147064 mac->m_instance = grp->lg_key > AGGR_MAX_KEY ? (uint_t)-1 : grp->lg_key;
13252311Sseb mac->m_src_addr = grp->lg_addr;
13262311Sseb mac->m_callbacks = &aggr_m_callbacks;
13272311Sseb mac->m_min_sdu = 0;
13282803Snd99603 mac->m_max_sdu = grp->lg_max_sdu = aggr_grp_max_sdu(grp);
13295895Syz147064 mac->m_margin = aggr_grp_max_margin(grp);
13308275SEric Cheng mac->m_v12n = MAC_VIRT_LEVEL1;
13312311Sseb err = mac_register(mac, &grp->lg_mh);
13322311Sseb mac_free(mac);
13332311Sseb if (err != 0)
13340Sstevel@tonic-gate goto bail;
13350Sstevel@tonic-gate
133610616SSebastien.Roy@Sun.COM err = dls_devnet_create(grp->lg_mh, grp->lg_linkid, crgetzoneid(credp));
133710616SSebastien.Roy@Sun.COM if (err != 0) {
13385895Syz147064 (void) mac_unregister(grp->lg_mh);
13398275SEric Cheng grp->lg_mh = NULL;
13405895Syz147064 goto bail;
13415895Syz147064 }
13425895Syz147064
13438275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
13448275SEric Cheng
13458275SEric Cheng /*
13468275SEric Cheng * Update the MAC address of the constituent ports.
13478275SEric Cheng * None of the port is attached at this time, the link state of the
13488275SEric Cheng * aggregation will not change.
13498275SEric Cheng */
13508275SEric Cheng link_state_changed = aggr_grp_update_ports_mac(grp);
13518275SEric Cheng ASSERT(!link_state_changed);
13528275SEric Cheng
13538275SEric Cheng /* update outbound load balancing policy */
13548275SEric Cheng aggr_send_update_policy(grp, policy);
13558275SEric Cheng
13560Sstevel@tonic-gate /* set LACP mode */
13570Sstevel@tonic-gate aggr_lacp_set_mode(grp, lacp_mode, lacp_timer);
13580Sstevel@tonic-gate
13592163Syz147064 /*
13602163Syz147064 * Attach each port if necessary.
13612163Syz147064 */
13625102Syz147064 for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
13638275SEric Cheng /*
13648275SEric Cheng * Create the pseudo ring for each HW ring of the underlying
13658275SEric Cheng * port. Note that this is done after the aggr registers the
13668275SEric Cheng * mac.
13678275SEric Cheng */
136811878SVenu.Iyer@Sun.COM VERIFY(aggr_add_pseudo_tx_group(port, &grp->lg_tx_group) == 0);
13698275SEric Cheng VERIFY(aggr_add_pseudo_rx_group(port, &grp->lg_rx_group) == 0);
13708275SEric Cheng if (aggr_port_notify_link(grp, port))
13715102Syz147064 link_state_changed = B_TRUE;
13728275SEric Cheng
13738275SEric Cheng /*
13748275SEric Cheng * Initialize the callback functions for this port.
13758275SEric Cheng */
13768275SEric Cheng aggr_port_init_callbacks(port);
13775102Syz147064 }
13785102Syz147064
13795102Syz147064 if (link_state_changed)
13805102Syz147064 mac_link_update(grp->lg_mh, grp->lg_link_state);
13812163Syz147064
13820Sstevel@tonic-gate /* add new group to hash table */
13835895Syz147064 err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(linkid),
1384269Sericheng (mod_hash_val_t)grp);
13850Sstevel@tonic-gate ASSERT(err == 0);
1386269Sericheng aggr_grp_cnt++;
13870Sstevel@tonic-gate
13888275SEric Cheng mac_perim_exit(mph);
1389269Sericheng rw_exit(&aggr_grp_lock);
13900Sstevel@tonic-gate return (0);
13910Sstevel@tonic-gate
13920Sstevel@tonic-gate bail:
13938275SEric Cheng
13948275SEric Cheng grp->lg_closing = B_TRUE;
13958275SEric Cheng
13968275SEric Cheng port = grp->lg_ports;
13978275SEric Cheng while (port != NULL) {
13980Sstevel@tonic-gate aggr_port_t *cport;
13990Sstevel@tonic-gate
14008275SEric Cheng cport = port->lp_next;
14018275SEric Cheng aggr_port_delete(port);
14028275SEric Cheng port = cport;
14030Sstevel@tonic-gate }
14040Sstevel@tonic-gate
14058275SEric Cheng /*
14068275SEric Cheng * Inform the lacp_rx thread to exit.
14078275SEric Cheng */
14088275SEric Cheng mutex_enter(&grp->lg_lacp_lock);
14098275SEric Cheng grp->lg_lacp_done = B_TRUE;
14108275SEric Cheng cv_signal(&grp->lg_lacp_cv);
14118275SEric Cheng while (grp->lg_lacp_rx_thread != NULL)
14128275SEric Cheng cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock);
14138275SEric Cheng mutex_exit(&grp->lg_lacp_lock);
141411878SVenu.Iyer@Sun.COM /*
141511878SVenu.Iyer@Sun.COM * Inform the tx_notify thread to exit.
141611878SVenu.Iyer@Sun.COM */
141711878SVenu.Iyer@Sun.COM mutex_enter(&grp->lg_tx_flowctl_lock);
141811878SVenu.Iyer@Sun.COM if (grp->lg_tx_notify_thread != NULL) {
141911878SVenu.Iyer@Sun.COM tid = grp->lg_tx_notify_thread->t_did;
142011878SVenu.Iyer@Sun.COM grp->lg_tx_notify_done = B_TRUE;
142111878SVenu.Iyer@Sun.COM cv_signal(&grp->lg_tx_flowctl_cv);
142211878SVenu.Iyer@Sun.COM }
142311878SVenu.Iyer@Sun.COM mutex_exit(&grp->lg_tx_flowctl_lock);
142411878SVenu.Iyer@Sun.COM if (tid != 0)
142511878SVenu.Iyer@Sun.COM thread_join(tid);
142611878SVenu.Iyer@Sun.COM
142711878SVenu.Iyer@Sun.COM kmem_free(grp->lg_tx_blocked_rings,
142811878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP));
1429269Sericheng rw_exit(&aggr_grp_lock);
14308275SEric Cheng AGGR_GRP_REFRELE(grp);
14310Sstevel@tonic-gate return (err);
14320Sstevel@tonic-gate }
14330Sstevel@tonic-gate
14340Sstevel@tonic-gate /*
14355895Syz147064 * Return a pointer to the member of a group with specified linkid.
14360Sstevel@tonic-gate */
14370Sstevel@tonic-gate static aggr_port_t *
aggr_grp_port_lookup(aggr_grp_t * grp,datalink_id_t linkid)14385895Syz147064 aggr_grp_port_lookup(aggr_grp_t *grp, datalink_id_t linkid)
14390Sstevel@tonic-gate {
14400Sstevel@tonic-gate aggr_port_t *port;
14410Sstevel@tonic-gate
14428275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
14430Sstevel@tonic-gate
14440Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
14455895Syz147064 if (port->lp_linkid == linkid)
14460Sstevel@tonic-gate break;
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate
14490Sstevel@tonic-gate return (port);
14500Sstevel@tonic-gate }
14510Sstevel@tonic-gate
14520Sstevel@tonic-gate /*
14530Sstevel@tonic-gate * Stop, detach and remove a port from a link aggregation group.
14540Sstevel@tonic-gate */
14550Sstevel@tonic-gate static int
aggr_grp_rem_port(aggr_grp_t * grp,aggr_port_t * port,boolean_t * mac_addr_changedp,boolean_t * link_state_changedp)14562047Syz147064 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port,
14572047Syz147064 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
14580Sstevel@tonic-gate {
14592047Syz147064 int rc = 0;
14600Sstevel@tonic-gate aggr_port_t **pport;
14612047Syz147064 boolean_t mac_addr_changed = B_FALSE;
14622047Syz147064 boolean_t link_state_changed = B_FALSE;
14638275SEric Cheng mac_perim_handle_t mph;
14640Sstevel@tonic-gate uint64_t val;
14650Sstevel@tonic-gate uint_t i;
14662311Sseb uint_t stat;
14670Sstevel@tonic-gate
14688275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
14690Sstevel@tonic-gate ASSERT(grp->lg_nports > 1);
14702047Syz147064 ASSERT(!grp->lg_closing);
14710Sstevel@tonic-gate
14720Sstevel@tonic-gate /* unlink port */
14730Sstevel@tonic-gate for (pport = &grp->lg_ports; *pport != port;
14740Sstevel@tonic-gate pport = &(*pport)->lp_next) {
14752047Syz147064 if (*pport == NULL) {
14762047Syz147064 rc = ENOENT;
14772047Syz147064 goto done;
14782047Syz147064 }
14790Sstevel@tonic-gate }
14800Sstevel@tonic-gate *pport = port->lp_next;
14810Sstevel@tonic-gate
14828275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &mph);
14830Sstevel@tonic-gate
14840Sstevel@tonic-gate /*
14850Sstevel@tonic-gate * If the MAC address of the port being removed was assigned
14860Sstevel@tonic-gate * to the group, update the group MAC address
14870Sstevel@tonic-gate * using the MAC address of a different port.
14880Sstevel@tonic-gate */
14890Sstevel@tonic-gate if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) {
14900Sstevel@tonic-gate /*
14910Sstevel@tonic-gate * Set the MAC address of the group to the
14920Sstevel@tonic-gate * MAC address of its first port.
14930Sstevel@tonic-gate */
14940Sstevel@tonic-gate bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL);
14950Sstevel@tonic-gate grp->lg_mac_addr_port = grp->lg_ports;
14962047Syz147064 mac_addr_changed = B_TRUE;
14970Sstevel@tonic-gate }
14980Sstevel@tonic-gate
14998275SEric Cheng link_state_changed = aggr_grp_detach_port(grp, port);
15000Sstevel@tonic-gate
15010Sstevel@tonic-gate /*
15022311Sseb * Add the counter statistics of the ports while it was aggregated
15032311Sseb * to the group's residual statistics. This is done by obtaining
15042311Sseb * the current counter from the underlying MAC then subtracting the
15052311Sseb * value of the counter at the moment it was added to the
15062311Sseb * aggregation.
15070Sstevel@tonic-gate */
15088275SEric Cheng for (i = 0; i < MAC_NSTAT; i++) {
15092311Sseb stat = i + MAC_STAT_MIN;
15102311Sseb if (!MAC_STAT_ISACOUNTER(stat))
15110Sstevel@tonic-gate continue;
15122311Sseb val = aggr_port_stat(port, stat);
15130Sstevel@tonic-gate val -= port->lp_stat[i];
15140Sstevel@tonic-gate grp->lg_stat[i] += val;
15150Sstevel@tonic-gate }
15168275SEric Cheng for (i = 0; i < ETHER_NSTAT; i++) {
15172311Sseb stat = i + MACTYPE_STAT_MIN;
15182311Sseb if (!ETHER_STAT_ISACOUNTER(stat))
15194913Sethindra continue;
15202311Sseb val = aggr_port_stat(port, stat);
15212311Sseb val -= port->lp_ether_stat[i];
15222311Sseb grp->lg_ether_stat[i] += val;
15232311Sseb }
15240Sstevel@tonic-gate
15250Sstevel@tonic-gate grp->lg_nports--;
15268275SEric Cheng mac_perim_exit(mph);
15270Sstevel@tonic-gate
152811878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group);
15290Sstevel@tonic-gate aggr_port_delete(port);
15300Sstevel@tonic-gate
15310Sstevel@tonic-gate /*
15320Sstevel@tonic-gate * If the group MAC address has changed, update the MAC address of
15335895Syz147064 * the remaining constituent ports according to the new MAC
15340Sstevel@tonic-gate * address of the group.
15350Sstevel@tonic-gate */
15365102Syz147064 if (mac_addr_changed && aggr_grp_update_ports_mac(grp))
15375102Syz147064 link_state_changed = B_TRUE;
15380Sstevel@tonic-gate
15392047Syz147064 done:
15402047Syz147064 if (mac_addr_changedp != NULL)
15412047Syz147064 *mac_addr_changedp = mac_addr_changed;
15422047Syz147064 if (link_state_changedp != NULL)
15432047Syz147064 *link_state_changedp = link_state_changed;
15440Sstevel@tonic-gate
15452047Syz147064 return (rc);
15460Sstevel@tonic-gate }
15470Sstevel@tonic-gate
15480Sstevel@tonic-gate /*
15490Sstevel@tonic-gate * Remove one or more ports from an existing link aggregation group.
15500Sstevel@tonic-gate */
15510Sstevel@tonic-gate int
aggr_grp_rem_ports(datalink_id_t linkid,uint_t nports,laioc_port_t * ports)15525895Syz147064 aggr_grp_rem_ports(datalink_id_t linkid, uint_t nports, laioc_port_t *ports)
15530Sstevel@tonic-gate {
15540Sstevel@tonic-gate int rc = 0, i;
15550Sstevel@tonic-gate aggr_grp_t *grp = NULL;
15560Sstevel@tonic-gate aggr_port_t *port;
15572047Syz147064 boolean_t mac_addr_update = B_FALSE, mac_addr_changed;
15582047Syz147064 boolean_t link_state_update = B_FALSE, link_state_changed;
15598275SEric Cheng mac_perim_handle_t mph, pmph;
15600Sstevel@tonic-gate
15615895Syz147064 /* get group corresponding to linkid */
1562269Sericheng rw_enter(&aggr_grp_lock, RW_READER);
15635895Syz147064 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
1564269Sericheng (mod_hash_val_t *)&grp) != 0) {
1565269Sericheng rw_exit(&aggr_grp_lock);
1566269Sericheng return (ENOENT);
15670Sstevel@tonic-gate }
15680Sstevel@tonic-gate AGGR_GRP_REFHOLD(grp);
15698275SEric Cheng
15708275SEric Cheng /*
15718275SEric Cheng * Hold the perimeter so that the aggregation won't be destroyed.
15728275SEric Cheng */
15738275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
1574269Sericheng rw_exit(&aggr_grp_lock);
1575269Sericheng
15760Sstevel@tonic-gate /* we need to keep at least one port per group */
15770Sstevel@tonic-gate if (nports >= grp->lg_nports) {
15780Sstevel@tonic-gate rc = EINVAL;
15790Sstevel@tonic-gate goto bail;
15800Sstevel@tonic-gate }
15810Sstevel@tonic-gate
15820Sstevel@tonic-gate /* first verify that all the groups are valid */
15830Sstevel@tonic-gate for (i = 0; i < nports; i++) {
15845895Syz147064 if (aggr_grp_port_lookup(grp, ports[i].lp_linkid) == NULL) {
15850Sstevel@tonic-gate /* port not found */
15860Sstevel@tonic-gate rc = ENOENT;
15870Sstevel@tonic-gate goto bail;
15880Sstevel@tonic-gate }
15890Sstevel@tonic-gate }
15900Sstevel@tonic-gate
15918275SEric Cheng /* clear the promiscous mode for the specified ports */
15928275SEric Cheng for (i = 0; i < nports && rc == 0; i++) {
15938275SEric Cheng /* lookup port */
15948275SEric Cheng port = aggr_grp_port_lookup(grp, ports[i].lp_linkid);
15958275SEric Cheng ASSERT(port != NULL);
15968275SEric Cheng
15978275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
15988275SEric Cheng rc = aggr_port_promisc(port, B_FALSE);
15998275SEric Cheng mac_perim_exit(pmph);
16008275SEric Cheng }
16018275SEric Cheng if (rc != 0) {
16028275SEric Cheng for (i = 0; i < nports; i++) {
16038275SEric Cheng port = aggr_grp_port_lookup(grp,
16048275SEric Cheng ports[i].lp_linkid);
16058275SEric Cheng ASSERT(port != NULL);
16068275SEric Cheng
16078275SEric Cheng /*
16088275SEric Cheng * Turn the promiscuous mode back on if it is required
16098275SEric Cheng * to receive the non-primary address over a port, or
16108275SEric Cheng * the promiscous mode is enabled over the aggr.
16118275SEric Cheng */
16128275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
16138275SEric Cheng if (port->lp_started && (grp->lg_promisc ||
16148275SEric Cheng port->lp_prom_addr != NULL)) {
16158275SEric Cheng (void) aggr_port_promisc(port, B_TRUE);
16168275SEric Cheng }
16178275SEric Cheng mac_perim_exit(pmph);
16188275SEric Cheng }
16198275SEric Cheng goto bail;
16208275SEric Cheng }
16218275SEric Cheng
16220Sstevel@tonic-gate /* remove the specified ports from group */
16238275SEric Cheng for (i = 0; i < nports; i++) {
16240Sstevel@tonic-gate /* lookup port */
16255895Syz147064 port = aggr_grp_port_lookup(grp, ports[i].lp_linkid);
16260Sstevel@tonic-gate ASSERT(port != NULL);
16270Sstevel@tonic-gate
16280Sstevel@tonic-gate /* stop port if group has already been started */
16290Sstevel@tonic-gate if (grp->lg_started) {
16308275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
16310Sstevel@tonic-gate aggr_port_stop(port);
16328275SEric Cheng mac_perim_exit(pmph);
16330Sstevel@tonic-gate }
16340Sstevel@tonic-gate
163511878SVenu.Iyer@Sun.COM /*
163611878SVenu.Iyer@Sun.COM * aggr_rem_pseudo_tx_group() is not called here. Instead
163711878SVenu.Iyer@Sun.COM * it is called from inside aggr_grp_rem_port() after the
163811878SVenu.Iyer@Sun.COM * port has been detached. The reason is that
163911878SVenu.Iyer@Sun.COM * aggr_rem_pseudo_tx_group() removes one ring at a time
164011878SVenu.Iyer@Sun.COM * and if there is still traffic going on, then there
164111878SVenu.Iyer@Sun.COM * is the possibility of aggr_find_tx_ring() returning a
164211878SVenu.Iyer@Sun.COM * removed ring for transmission. Once the port has been
164311878SVenu.Iyer@Sun.COM * detached, that port will not be used and
164411878SVenu.Iyer@Sun.COM * aggr_find_tx_ring() will not return any rings
164511878SVenu.Iyer@Sun.COM * belonging to it.
164611878SVenu.Iyer@Sun.COM */
16478275SEric Cheng aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group);
164811878SVenu.Iyer@Sun.COM
16490Sstevel@tonic-gate /* remove port from group */
16502047Syz147064 rc = aggr_grp_rem_port(grp, port, &mac_addr_changed,
16512047Syz147064 &link_state_changed);
16520Sstevel@tonic-gate ASSERT(rc == 0);
16532047Syz147064 mac_addr_update = mac_addr_update || mac_addr_changed;
16542047Syz147064 link_state_update = link_state_update || link_state_changed;
16550Sstevel@tonic-gate }
16560Sstevel@tonic-gate
16570Sstevel@tonic-gate bail:
16588275SEric Cheng if (mac_addr_update)
16598275SEric Cheng mac_unicst_update(grp->lg_mh, grp->lg_addr);
16608275SEric Cheng if (link_state_update)
16618275SEric Cheng mac_link_update(grp->lg_mh, grp->lg_link_state);
16628275SEric Cheng
16638275SEric Cheng mac_perim_exit(mph);
16640Sstevel@tonic-gate AGGR_GRP_REFRELE(grp);
16650Sstevel@tonic-gate
16660Sstevel@tonic-gate return (rc);
16670Sstevel@tonic-gate }
16680Sstevel@tonic-gate
16690Sstevel@tonic-gate int
aggr_grp_delete(datalink_id_t linkid,cred_t * cred)167010616SSebastien.Roy@Sun.COM aggr_grp_delete(datalink_id_t linkid, cred_t *cred)
16710Sstevel@tonic-gate {
1672269Sericheng aggr_grp_t *grp = NULL;
16730Sstevel@tonic-gate aggr_port_t *port, *cport;
16745895Syz147064 datalink_id_t tmpid;
1675269Sericheng mod_hash_val_t val;
16768275SEric Cheng mac_perim_handle_t mph, pmph;
16775397Syz147064 int err;
167811878SVenu.Iyer@Sun.COM kt_did_t tid = 0;
16790Sstevel@tonic-gate
1680269Sericheng rw_enter(&aggr_grp_lock, RW_WRITER);
16810Sstevel@tonic-gate
16825895Syz147064 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
1683269Sericheng (mod_hash_val_t *)&grp) != 0) {
1684269Sericheng rw_exit(&aggr_grp_lock);
1685269Sericheng return (ENOENT);
16860Sstevel@tonic-gate }
16871852Syz147064
16885895Syz147064 /*
16895895Syz147064 * Note that dls_devnet_destroy() must be called before lg_lock is
16905895Syz147064 * held. Otherwise, it will deadlock if another thread is in
16915895Syz147064 * aggr_m_stat() and thus has a kstat_hold() on the kstats that
16925895Syz147064 * dls_devnet_destroy() needs to delete.
16935895Syz147064 */
16948275SEric Cheng if ((err = dls_devnet_destroy(grp->lg_mh, &tmpid, B_TRUE)) != 0) {
16955895Syz147064 rw_exit(&aggr_grp_lock);
16965895Syz147064 return (err);
16975895Syz147064 }
16985895Syz147064 ASSERT(linkid == tmpid);
16995895Syz147064
17000Sstevel@tonic-gate /*
17010Sstevel@tonic-gate * Unregister from the MAC service module. Since this can
17020Sstevel@tonic-gate * fail if a client hasn't closed the MAC port, we gracefully
17030Sstevel@tonic-gate * fail the operation.
17040Sstevel@tonic-gate */
17055397Syz147064 if ((err = mac_disable(grp->lg_mh)) != 0) {
170610616SSebastien.Roy@Sun.COM (void) dls_devnet_create(grp->lg_mh, linkid, crgetzoneid(cred));
1707269Sericheng rw_exit(&aggr_grp_lock);
17085397Syz147064 return (err);
17090Sstevel@tonic-gate }
17105895Syz147064 (void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(linkid), &val);
1711269Sericheng ASSERT(grp == (aggr_grp_t *)val);
17120Sstevel@tonic-gate
1713269Sericheng ASSERT(aggr_grp_cnt > 0);
1714269Sericheng aggr_grp_cnt--;
17158275SEric Cheng rw_exit(&aggr_grp_lock);
1716269Sericheng
17178275SEric Cheng /*
17188275SEric Cheng * Inform the lacp_rx thread to exit.
17198275SEric Cheng */
17208275SEric Cheng mutex_enter(&grp->lg_lacp_lock);
17218275SEric Cheng grp->lg_lacp_done = B_TRUE;
17228275SEric Cheng cv_signal(&grp->lg_lacp_cv);
17238275SEric Cheng while (grp->lg_lacp_rx_thread != NULL)
17248275SEric Cheng cv_wait(&grp->lg_lacp_cv, &grp->lg_lacp_lock);
17258275SEric Cheng mutex_exit(&grp->lg_lacp_lock);
172611878SVenu.Iyer@Sun.COM /*
172711878SVenu.Iyer@Sun.COM * Inform the tx_notify_thread to exit.
172811878SVenu.Iyer@Sun.COM */
172911878SVenu.Iyer@Sun.COM mutex_enter(&grp->lg_tx_flowctl_lock);
173011878SVenu.Iyer@Sun.COM if (grp->lg_tx_notify_thread != NULL) {
173111878SVenu.Iyer@Sun.COM tid = grp->lg_tx_notify_thread->t_did;
173211878SVenu.Iyer@Sun.COM grp->lg_tx_notify_done = B_TRUE;
173311878SVenu.Iyer@Sun.COM cv_signal(&grp->lg_tx_flowctl_cv);
173411878SVenu.Iyer@Sun.COM }
173511878SVenu.Iyer@Sun.COM mutex_exit(&grp->lg_tx_flowctl_lock);
173611878SVenu.Iyer@Sun.COM if (tid != 0)
173711878SVenu.Iyer@Sun.COM thread_join(tid);
17388275SEric Cheng
17398275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
17408275SEric Cheng
17418275SEric Cheng grp->lg_closing = B_TRUE;
17428275SEric Cheng /* detach and free MAC ports associated with group */
17438275SEric Cheng port = grp->lg_ports;
17448275SEric Cheng while (port != NULL) {
17458275SEric Cheng cport = port->lp_next;
17468275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
17478275SEric Cheng if (grp->lg_started)
17488275SEric Cheng aggr_port_stop(port);
17498275SEric Cheng (void) aggr_grp_detach_port(grp, port);
17508275SEric Cheng mac_perim_exit(pmph);
175111878SVenu.Iyer@Sun.COM aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group);
17528275SEric Cheng aggr_rem_pseudo_rx_group(port, &grp->lg_rx_group);
17538275SEric Cheng aggr_port_delete(port);
17548275SEric Cheng port = cport;
17558275SEric Cheng }
17568275SEric Cheng
17578275SEric Cheng mac_perim_exit(mph);
17588275SEric Cheng
175911878SVenu.Iyer@Sun.COM kmem_free(grp->lg_tx_blocked_rings,
176011878SVenu.Iyer@Sun.COM (sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP));
17618275SEric Cheng /*
17628275SEric Cheng * Wait for the port's lacp timer thread and its notification callback
17638275SEric Cheng * to exit before calling mac_unregister() since both needs to access
17648275SEric Cheng * the mac perimeter of the grp.
17658275SEric Cheng */
17668275SEric Cheng aggr_grp_port_wait(grp);
17678275SEric Cheng
17688275SEric Cheng VERIFY(mac_unregister(grp->lg_mh) == 0);
17698275SEric Cheng grp->lg_mh = NULL;
17708275SEric Cheng
17710Sstevel@tonic-gate AGGR_GRP_REFRELE(grp);
17720Sstevel@tonic-gate return (0);
17730Sstevel@tonic-gate }
17740Sstevel@tonic-gate
17750Sstevel@tonic-gate void
aggr_grp_free(aggr_grp_t * grp)17760Sstevel@tonic-gate aggr_grp_free(aggr_grp_t *grp)
17770Sstevel@tonic-gate {
17780Sstevel@tonic-gate ASSERT(grp->lg_refs == 0);
17798275SEric Cheng ASSERT(grp->lg_port_ref == 0);
17805895Syz147064 if (grp->lg_key > AGGR_MAX_KEY) {
17815895Syz147064 id_free(key_ids, grp->lg_key);
17825895Syz147064 grp->lg_key = 0;
17835895Syz147064 }
17840Sstevel@tonic-gate kmem_cache_free(aggr_grp_cache, grp);
17850Sstevel@tonic-gate }
17860Sstevel@tonic-gate
17875895Syz147064 int
aggr_grp_info(datalink_id_t linkid,void * fn_arg,aggr_grp_info_new_grp_fn_t new_grp_fn,aggr_grp_info_new_port_fn_t new_port_fn,cred_t * cred)17885895Syz147064 aggr_grp_info(datalink_id_t linkid, void *fn_arg,
17895895Syz147064 aggr_grp_info_new_grp_fn_t new_grp_fn,
179010616SSebastien.Roy@Sun.COM aggr_grp_info_new_port_fn_t new_port_fn, cred_t *cred)
17910Sstevel@tonic-gate {
17925895Syz147064 aggr_grp_t *grp;
17935895Syz147064 aggr_port_t *port;
17948275SEric Cheng mac_perim_handle_t mph, pmph;
17955895Syz147064 int rc = 0;
17965895Syz147064
179710616SSebastien.Roy@Sun.COM /*
179810616SSebastien.Roy@Sun.COM * Make sure that the aggregation link is visible from the caller's
179910616SSebastien.Roy@Sun.COM * zone.
180010616SSebastien.Roy@Sun.COM */
180110616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(linkid, crgetzoneid(cred)))
180210616SSebastien.Roy@Sun.COM return (ENOENT);
180310616SSebastien.Roy@Sun.COM
18045895Syz147064 rw_enter(&aggr_grp_lock, RW_READER);
18050Sstevel@tonic-gate
18065895Syz147064 if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(linkid),
18075895Syz147064 (mod_hash_val_t *)&grp) != 0) {
18085895Syz147064 rw_exit(&aggr_grp_lock);
18095895Syz147064 return (ENOENT);
18105895Syz147064 }
18118275SEric Cheng AGGR_GRP_REFHOLD(grp);
18120Sstevel@tonic-gate
18138275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
18148275SEric Cheng rw_exit(&aggr_grp_lock);
18150Sstevel@tonic-gate
18165895Syz147064 rc = new_grp_fn(fn_arg, grp->lg_linkid,
18175895Syz147064 (grp->lg_key > AGGR_MAX_KEY) ? 0 : grp->lg_key, grp->lg_addr,
18185895Syz147064 grp->lg_addr_fixed, grp->lg_force, grp->lg_tx_policy,
18190Sstevel@tonic-gate grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer);
18200Sstevel@tonic-gate
18215895Syz147064 if (rc != 0)
18220Sstevel@tonic-gate goto bail;
18230Sstevel@tonic-gate
18240Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
18258275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
18265895Syz147064 rc = new_port_fn(fn_arg, port->lp_linkid, port->lp_addr,
18275895Syz147064 port->lp_state, &port->lp_lacp.ActorOperPortState);
18288275SEric Cheng mac_perim_exit(pmph);
18290Sstevel@tonic-gate
18305895Syz147064 if (rc != 0)
18310Sstevel@tonic-gate goto bail;
18320Sstevel@tonic-gate }
18330Sstevel@tonic-gate
18340Sstevel@tonic-gate bail:
18358275SEric Cheng mac_perim_exit(mph);
18368275SEric Cheng AGGR_GRP_REFRELE(grp);
18370Sstevel@tonic-gate return (rc);
18380Sstevel@tonic-gate }
18390Sstevel@tonic-gate
18400Sstevel@tonic-gate /*ARGSUSED*/
18410Sstevel@tonic-gate static void
aggr_m_ioctl(void * arg,queue_t * q,mblk_t * mp)18420Sstevel@tonic-gate aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
18430Sstevel@tonic-gate {
18440Sstevel@tonic-gate miocnak(q, mp, 0, ENOTSUP);
18450Sstevel@tonic-gate }
18460Sstevel@tonic-gate
18472311Sseb static int
aggr_grp_stat(aggr_grp_t * grp,uint_t stat,uint64_t * val)18482311Sseb aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val)
18490Sstevel@tonic-gate {
18502311Sseb aggr_port_t *port;
18512311Sseb uint_t stat_index;
18522311Sseb
18532311Sseb /* We only aggregate counter statistics. */
18542311Sseb if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) ||
18552311Sseb IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) {
18562311Sseb return (ENOTSUP);
18572311Sseb }
18582311Sseb
18592311Sseb /*
18602311Sseb * Counter statistics for a group are computed by aggregating the
18612311Sseb * counters of the members MACs while they were aggregated, plus
18622311Sseb * the residual counter of the group itself, which is updated each
18632311Sseb * time a MAC is removed from the group.
18642311Sseb */
18652311Sseb *val = 0;
18662311Sseb for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
18672311Sseb /* actual port statistic */
18682311Sseb *val += aggr_port_stat(port, stat);
18692311Sseb /*
18702311Sseb * minus the port stat when it was added, plus any residual
18715895Syz147064 * amount for the group.
18722311Sseb */
18732311Sseb if (IS_MAC_STAT(stat)) {
18742311Sseb stat_index = stat - MAC_STAT_MIN;
18752311Sseb *val -= port->lp_stat[stat_index];
18762311Sseb *val += grp->lg_stat[stat_index];
18772311Sseb } else if (IS_MACTYPE_STAT(stat)) {
18782311Sseb stat_index = stat - MACTYPE_STAT_MIN;
18792311Sseb *val -= port->lp_ether_stat[stat_index];
18802311Sseb *val += grp->lg_ether_stat[stat_index];
18812311Sseb }
18822311Sseb }
18832311Sseb return (0);
18842311Sseb }
18852311Sseb
188611878SVenu.Iyer@Sun.COM int
aggr_rx_ring_stat(mac_ring_driver_t rdriver,uint_t stat,uint64_t * val)188711878SVenu.Iyer@Sun.COM aggr_rx_ring_stat(mac_ring_driver_t rdriver, uint_t stat, uint64_t *val)
188811878SVenu.Iyer@Sun.COM {
188911878SVenu.Iyer@Sun.COM aggr_pseudo_rx_ring_t *rx_ring = (aggr_pseudo_rx_ring_t *)rdriver;
189011878SVenu.Iyer@Sun.COM
189111878SVenu.Iyer@Sun.COM if (rx_ring->arr_hw_rh != NULL) {
189211878SVenu.Iyer@Sun.COM *val = mac_pseudo_rx_ring_stat_get(rx_ring->arr_hw_rh, stat);
189311878SVenu.Iyer@Sun.COM } else {
189411878SVenu.Iyer@Sun.COM aggr_port_t *port = rx_ring->arr_port;
189511878SVenu.Iyer@Sun.COM
189611878SVenu.Iyer@Sun.COM *val = mac_stat_get(port->lp_mh, stat);
189711878SVenu.Iyer@Sun.COM
189811878SVenu.Iyer@Sun.COM }
189911878SVenu.Iyer@Sun.COM return (0);
190011878SVenu.Iyer@Sun.COM }
190111878SVenu.Iyer@Sun.COM
190211878SVenu.Iyer@Sun.COM int
aggr_tx_ring_stat(mac_ring_driver_t rdriver,uint_t stat,uint64_t * val)190311878SVenu.Iyer@Sun.COM aggr_tx_ring_stat(mac_ring_driver_t rdriver, uint_t stat, uint64_t *val)
190411878SVenu.Iyer@Sun.COM {
190511878SVenu.Iyer@Sun.COM aggr_pseudo_tx_ring_t *tx_ring = (aggr_pseudo_tx_ring_t *)rdriver;
190611878SVenu.Iyer@Sun.COM
190711878SVenu.Iyer@Sun.COM if (tx_ring->atr_hw_rh != NULL) {
190811878SVenu.Iyer@Sun.COM *val = mac_pseudo_tx_ring_stat_get(tx_ring->atr_hw_rh, stat);
190911878SVenu.Iyer@Sun.COM } else {
191011878SVenu.Iyer@Sun.COM aggr_port_t *port = tx_ring->atr_port;
191111878SVenu.Iyer@Sun.COM
191211878SVenu.Iyer@Sun.COM *val = mac_stat_get(port->lp_mh, stat);
191311878SVenu.Iyer@Sun.COM }
191411878SVenu.Iyer@Sun.COM return (0);
191511878SVenu.Iyer@Sun.COM }
191611878SVenu.Iyer@Sun.COM
19172311Sseb static int
aggr_m_stat(void * arg,uint_t stat,uint64_t * val)19182311Sseb aggr_m_stat(void *arg, uint_t stat, uint64_t *val)
19192311Sseb {
19208275SEric Cheng aggr_grp_t *grp = arg;
19218275SEric Cheng mac_perim_handle_t mph;
19228275SEric Cheng int rval = 0;
19230Sstevel@tonic-gate
19248275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
19250Sstevel@tonic-gate
19260Sstevel@tonic-gate switch (stat) {
19270Sstevel@tonic-gate case MAC_STAT_IFSPEED:
19282311Sseb *val = grp->lg_ifspeed;
19290Sstevel@tonic-gate break;
19302311Sseb
19312311Sseb case ETHER_STAT_LINK_DUPLEX:
19322311Sseb *val = grp->lg_link_duplex;
19330Sstevel@tonic-gate break;
19342311Sseb
19350Sstevel@tonic-gate default:
19360Sstevel@tonic-gate /*
19372311Sseb * For all other statistics, we return the aggregated stat
19382311Sseb * from the underlying ports. aggr_grp_stat() will set
19392311Sseb * rval appropriately if the statistic isn't a counter.
19400Sstevel@tonic-gate */
19412311Sseb rval = aggr_grp_stat(grp, stat, val);
19420Sstevel@tonic-gate }
19430Sstevel@tonic-gate
19448275SEric Cheng mac_perim_exit(mph);
19452311Sseb return (rval);
19460Sstevel@tonic-gate }
19470Sstevel@tonic-gate
19480Sstevel@tonic-gate static int
aggr_m_start(void * arg)19490Sstevel@tonic-gate aggr_m_start(void *arg)
19500Sstevel@tonic-gate {
19510Sstevel@tonic-gate aggr_grp_t *grp = arg;
19520Sstevel@tonic-gate aggr_port_t *port;
19538275SEric Cheng mac_perim_handle_t mph, pmph;
19540Sstevel@tonic-gate
19558275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
19560Sstevel@tonic-gate
19570Sstevel@tonic-gate /*
19580Sstevel@tonic-gate * Attempts to start all configured members of the group.
19590Sstevel@tonic-gate * Group members will be attached when their link-up notification
19600Sstevel@tonic-gate * is received.
19610Sstevel@tonic-gate */
19620Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
19638275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
19640Sstevel@tonic-gate if (aggr_port_start(port) != 0) {
19658275SEric Cheng mac_perim_exit(pmph);
19660Sstevel@tonic-gate continue;
19670Sstevel@tonic-gate }
19680Sstevel@tonic-gate
19698275SEric Cheng /*
19708275SEric Cheng * Turn on the promiscuous mode if it is required to receive
19718275SEric Cheng * the non-primary address over a port, or the promiscous
19728275SEric Cheng * mode is enabled over the aggr.
19738275SEric Cheng */
19748275SEric Cheng if (grp->lg_promisc || port->lp_prom_addr != NULL) {
19758275SEric Cheng if (aggr_port_promisc(port, B_TRUE) != 0)
19768275SEric Cheng aggr_port_stop(port);
19778275SEric Cheng }
19788275SEric Cheng mac_perim_exit(pmph);
19790Sstevel@tonic-gate }
19800Sstevel@tonic-gate
19810Sstevel@tonic-gate grp->lg_started = B_TRUE;
19820Sstevel@tonic-gate
19838275SEric Cheng mac_perim_exit(mph);
19840Sstevel@tonic-gate return (0);
19850Sstevel@tonic-gate }
19860Sstevel@tonic-gate
19870Sstevel@tonic-gate static void
aggr_m_stop(void * arg)19880Sstevel@tonic-gate aggr_m_stop(void *arg)
19890Sstevel@tonic-gate {
19900Sstevel@tonic-gate aggr_grp_t *grp = arg;
19910Sstevel@tonic-gate aggr_port_t *port;
19928275SEric Cheng mac_perim_handle_t mph, pmph;
19930Sstevel@tonic-gate
19948275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
19950Sstevel@tonic-gate
19960Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
19978275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
19988275SEric Cheng
19998275SEric Cheng /* reset port promiscuous mode */
20008275SEric Cheng (void) aggr_port_promisc(port, B_FALSE);
20018275SEric Cheng
20020Sstevel@tonic-gate aggr_port_stop(port);
20038275SEric Cheng mac_perim_exit(pmph);
20040Sstevel@tonic-gate }
20050Sstevel@tonic-gate
20060Sstevel@tonic-gate grp->lg_started = B_FALSE;
20078275SEric Cheng mac_perim_exit(mph);
20080Sstevel@tonic-gate }
20090Sstevel@tonic-gate
20100Sstevel@tonic-gate static int
aggr_m_promisc(void * arg,boolean_t on)20110Sstevel@tonic-gate aggr_m_promisc(void *arg, boolean_t on)
20120Sstevel@tonic-gate {
20130Sstevel@tonic-gate aggr_grp_t *grp = arg;
20140Sstevel@tonic-gate aggr_port_t *port;
20152047Syz147064 boolean_t link_state_changed = B_FALSE;
20168275SEric Cheng mac_perim_handle_t mph, pmph;
20170Sstevel@tonic-gate
20180Sstevel@tonic-gate AGGR_GRP_REFHOLD(grp);
20198275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
20200Sstevel@tonic-gate
20212047Syz147064 ASSERT(!grp->lg_closing);
20222047Syz147064
20230Sstevel@tonic-gate if (on == grp->lg_promisc)
20240Sstevel@tonic-gate goto bail;
20250Sstevel@tonic-gate
20260Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
20278275SEric Cheng int err = 0;
20288275SEric Cheng
20298275SEric Cheng mac_perim_enter_by_mh(port->lp_mh, &pmph);
20300Sstevel@tonic-gate AGGR_PORT_REFHOLD(port);
20318275SEric Cheng if (!on && (port->lp_prom_addr == NULL))
20328275SEric Cheng err = aggr_port_promisc(port, B_FALSE);
20338275SEric Cheng else if (on && port->lp_started)
20348275SEric Cheng err = aggr_port_promisc(port, B_TRUE);
20358275SEric Cheng
20368275SEric Cheng if (err != 0) {
20378275SEric Cheng if (aggr_grp_detach_port(grp, port))
20388275SEric Cheng link_state_changed = B_TRUE;
20398275SEric Cheng } else {
20408275SEric Cheng /*
20418275SEric Cheng * If a port was detached because of a previous
20428275SEric Cheng * failure changing the promiscuity, the port
20438275SEric Cheng * is reattached when it successfully changes
20448275SEric Cheng * the promiscuity now, and this might cause
20458275SEric Cheng * the link state of the aggregation to change.
20468275SEric Cheng */
20478275SEric Cheng if (aggr_grp_attach_port(grp, port))
20488275SEric Cheng link_state_changed = B_TRUE;
20490Sstevel@tonic-gate }
20508275SEric Cheng mac_perim_exit(pmph);
20510Sstevel@tonic-gate AGGR_PORT_REFRELE(port);
20520Sstevel@tonic-gate }
20530Sstevel@tonic-gate
20540Sstevel@tonic-gate grp->lg_promisc = on;
20550Sstevel@tonic-gate
20562047Syz147064 if (link_state_changed)
20572311Sseb mac_link_update(grp->lg_mh, grp->lg_link_state);
20582047Syz147064
20590Sstevel@tonic-gate bail:
20608275SEric Cheng mac_perim_exit(mph);
20610Sstevel@tonic-gate AGGR_GRP_REFRELE(grp);
20620Sstevel@tonic-gate
20630Sstevel@tonic-gate return (0);
20640Sstevel@tonic-gate }
20650Sstevel@tonic-gate
20668275SEric Cheng static void
aggr_grp_port_rename(const char * new_name,void * arg)20678275SEric Cheng aggr_grp_port_rename(const char *new_name, void *arg)
20688275SEric Cheng {
20698275SEric Cheng /*
20708275SEric Cheng * aggr port's mac client name is the format of "aggr link name" plus
20718275SEric Cheng * AGGR_PORT_NAME_DELIMIT plus "underneath link name".
20728275SEric Cheng */
20738275SEric Cheng int aggr_len, link_len, clnt_name_len, i;
20748275SEric Cheng char *str_end, *str_st, *str_del;
20758275SEric Cheng char aggr_name[MAXNAMELEN];
20768275SEric Cheng char link_name[MAXNAMELEN];
20778275SEric Cheng char *clnt_name;
20788275SEric Cheng aggr_grp_t *aggr_grp = arg;
20798275SEric Cheng aggr_port_t *aggr_port = aggr_grp->lg_ports;
20808275SEric Cheng
20818275SEric Cheng for (i = 0; i < aggr_grp->lg_nports; i++) {
20828275SEric Cheng clnt_name = mac_client_name(aggr_port->lp_mch);
20838275SEric Cheng clnt_name_len = strlen(clnt_name);
20848275SEric Cheng str_st = clnt_name;
20858275SEric Cheng str_end = &(clnt_name[clnt_name_len]);
20868275SEric Cheng str_del = strchr(str_st, AGGR_PORT_NAME_DELIMIT);
20878275SEric Cheng ASSERT(str_del != NULL);
20888275SEric Cheng aggr_len = (intptr_t)((uintptr_t)str_del - (uintptr_t)str_st);
20898275SEric Cheng link_len = (intptr_t)((uintptr_t)str_end - (uintptr_t)str_del);
20908275SEric Cheng bzero(aggr_name, MAXNAMELEN);
20918275SEric Cheng bzero(link_name, MAXNAMELEN);
20928275SEric Cheng bcopy(clnt_name, aggr_name, aggr_len);
20938275SEric Cheng bcopy(str_del, link_name, link_len + 1);
20948275SEric Cheng bzero(clnt_name, MAXNAMELEN);
20958275SEric Cheng (void) snprintf(clnt_name, MAXNAMELEN, "%s%s", new_name,
20968275SEric Cheng link_name);
20978275SEric Cheng
20988275SEric Cheng (void) mac_rename_primary(aggr_port->lp_mh, NULL);
20998275SEric Cheng aggr_port = aggr_port->lp_next;
21008275SEric Cheng }
21018275SEric Cheng }
21028275SEric Cheng
21030Sstevel@tonic-gate /*
21042311Sseb * Initialize the capabilities that are advertised for the group
21052311Sseb * according to the capabilities of the constituent ports.
21062311Sseb */
21072311Sseb static boolean_t
aggr_m_capab_get(void * arg,mac_capab_t cap,void * cap_data)21082311Sseb aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
21092311Sseb {
21102311Sseb aggr_grp_t *grp = arg;
21112311Sseb
21122311Sseb switch (cap) {
21132311Sseb case MAC_CAPAB_HCKSUM: {
21142311Sseb uint32_t *hcksum_txflags = cap_data;
21152311Sseb *hcksum_txflags = grp->lg_hcksum_txflags;
21162311Sseb break;
21172311Sseb }
21188970SRoamer@Sun.COM case MAC_CAPAB_LSO: {
21198970SRoamer@Sun.COM mac_capab_lso_t *cap_lso = cap_data;
21208970SRoamer@Sun.COM
21218970SRoamer@Sun.COM if (grp->lg_lso) {
21228970SRoamer@Sun.COM *cap_lso = grp->lg_cap_lso;
21238970SRoamer@Sun.COM break;
21248970SRoamer@Sun.COM } else {
21258970SRoamer@Sun.COM return (B_FALSE);
21268970SRoamer@Sun.COM }
21278970SRoamer@Sun.COM }
21285895Syz147064 case MAC_CAPAB_NO_NATIVEVLAN:
21295895Syz147064 return (!grp->lg_vlan);
21305895Syz147064 case MAC_CAPAB_NO_ZCOPY:
21315895Syz147064 return (!grp->lg_zcopy);
21328275SEric Cheng case MAC_CAPAB_RINGS: {
21338275SEric Cheng mac_capab_rings_t *cap_rings = cap_data;
21348275SEric Cheng
21358275SEric Cheng if (cap_rings->mr_type == MAC_RING_TYPE_RX) {
21368275SEric Cheng cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
21378275SEric Cheng cap_rings->mr_rnum = grp->lg_rx_group.arg_ring_cnt;
21388275SEric Cheng
21398275SEric Cheng /*
21408275SEric Cheng * An aggregation advertises only one (pseudo) RX
21418275SEric Cheng * group, which virtualizes the main/primary group of
21428275SEric Cheng * the underlying devices.
21438275SEric Cheng */
21448275SEric Cheng cap_rings->mr_gnum = 1;
21458275SEric Cheng cap_rings->mr_gaddring = NULL;
21468275SEric Cheng cap_rings->mr_gremring = NULL;
21478275SEric Cheng } else {
214811878SVenu.Iyer@Sun.COM cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
214911878SVenu.Iyer@Sun.COM cap_rings->mr_rnum = grp->lg_tx_group.atg_ring_cnt;
215011878SVenu.Iyer@Sun.COM cap_rings->mr_gnum = 0;
21518275SEric Cheng }
215211878SVenu.Iyer@Sun.COM cap_rings->mr_rget = aggr_fill_ring;
215311878SVenu.Iyer@Sun.COM cap_rings->mr_gget = aggr_fill_group;
21548275SEric Cheng break;
21558275SEric Cheng }
21568275SEric Cheng case MAC_CAPAB_AGGR:
21578275SEric Cheng {
21588275SEric Cheng mac_capab_aggr_t *aggr_cap;
21598275SEric Cheng
21608275SEric Cheng if (cap_data != NULL) {
21618275SEric Cheng aggr_cap = cap_data;
21628275SEric Cheng aggr_cap->mca_rename_fn = aggr_grp_port_rename;
21638275SEric Cheng aggr_cap->mca_unicst = aggr_m_unicst;
216411878SVenu.Iyer@Sun.COM aggr_cap->mca_find_tx_ring_fn = aggr_find_tx_ring;
216511878SVenu.Iyer@Sun.COM aggr_cap->mca_arg = arg;
21668275SEric Cheng }
21678275SEric Cheng return (B_TRUE);
21688275SEric Cheng }
21692311Sseb default:
21702311Sseb return (B_FALSE);
21712311Sseb }
21722311Sseb return (B_TRUE);
21732311Sseb }
21742311Sseb
21758275SEric Cheng /*
21768275SEric Cheng * Callback funtion for MAC layer to register groups.
21778275SEric Cheng */
21788275SEric Cheng static void
aggr_fill_group(void * arg,mac_ring_type_t rtype,const int index,mac_group_info_t * infop,mac_group_handle_t gh)21798275SEric Cheng aggr_fill_group(void *arg, mac_ring_type_t rtype, const int index,
21808275SEric Cheng mac_group_info_t *infop, mac_group_handle_t gh)
21817085Sudpa {
21828275SEric Cheng aggr_grp_t *grp = arg;
21838275SEric Cheng aggr_pseudo_rx_group_t *rx_group;
218411878SVenu.Iyer@Sun.COM aggr_pseudo_tx_group_t *tx_group;
218511878SVenu.Iyer@Sun.COM
218611878SVenu.Iyer@Sun.COM ASSERT(index == 0);
218711878SVenu.Iyer@Sun.COM if (rtype == MAC_RING_TYPE_RX) {
218811878SVenu.Iyer@Sun.COM rx_group = &grp->lg_rx_group;
218911878SVenu.Iyer@Sun.COM rx_group->arg_gh = gh;
219011878SVenu.Iyer@Sun.COM rx_group->arg_grp = grp;
219111878SVenu.Iyer@Sun.COM
219211878SVenu.Iyer@Sun.COM infop->mgi_driver = (mac_group_driver_t)rx_group;
219311878SVenu.Iyer@Sun.COM infop->mgi_start = NULL;
219411878SVenu.Iyer@Sun.COM infop->mgi_stop = NULL;
219511878SVenu.Iyer@Sun.COM infop->mgi_addmac = aggr_addmac;
219611878SVenu.Iyer@Sun.COM infop->mgi_remmac = aggr_remmac;
219711878SVenu.Iyer@Sun.COM infop->mgi_count = rx_group->arg_ring_cnt;
219811878SVenu.Iyer@Sun.COM } else {
219911878SVenu.Iyer@Sun.COM tx_group = &grp->lg_tx_group;
220011878SVenu.Iyer@Sun.COM tx_group->atg_gh = gh;
220111878SVenu.Iyer@Sun.COM }
22028275SEric Cheng }
22038275SEric Cheng
22048275SEric Cheng /*
22058275SEric Cheng * Callback funtion for MAC layer to register all rings.
22068275SEric Cheng */
22078275SEric Cheng static void
aggr_fill_ring(void * arg,mac_ring_type_t rtype,const int rg_index,const int index,mac_ring_info_t * infop,mac_ring_handle_t rh)22088275SEric Cheng aggr_fill_ring(void *arg, mac_ring_type_t rtype, const int rg_index,
22098275SEric Cheng const int index, mac_ring_info_t *infop, mac_ring_handle_t rh)
22108275SEric Cheng {
22118275SEric Cheng aggr_grp_t *grp = arg;
22128275SEric Cheng
22138275SEric Cheng switch (rtype) {
22148275SEric Cheng case MAC_RING_TYPE_RX: {
22158275SEric Cheng aggr_pseudo_rx_group_t *rx_group = &grp->lg_rx_group;
22168275SEric Cheng aggr_pseudo_rx_ring_t *rx_ring;
22178275SEric Cheng mac_intr_t aggr_mac_intr;
22188275SEric Cheng
22198275SEric Cheng ASSERT(rg_index == 0);
22208275SEric Cheng
22218275SEric Cheng ASSERT((index >= 0) && (index < rx_group->arg_ring_cnt));
22228275SEric Cheng rx_ring = rx_group->arg_rings + index;
22238275SEric Cheng rx_ring->arr_rh = rh;
22248275SEric Cheng
22258275SEric Cheng /*
22268275SEric Cheng * Entrypoint to enable interrupt (disable poll) and
22278275SEric Cheng * disable interrupt (enable poll).
22288275SEric Cheng */
22298275SEric Cheng aggr_mac_intr.mi_handle = (mac_intr_handle_t)rx_ring;
22308275SEric Cheng aggr_mac_intr.mi_enable = aggr_pseudo_enable_intr;
22318275SEric Cheng aggr_mac_intr.mi_disable = aggr_pseudo_disable_intr;
223211878SVenu.Iyer@Sun.COM aggr_mac_intr.mi_ddi_handle = NULL;
22338275SEric Cheng
22348275SEric Cheng infop->mri_driver = (mac_ring_driver_t)rx_ring;
22358275SEric Cheng infop->mri_start = aggr_pseudo_start_ring;
22368275SEric Cheng infop->mri_stop = aggr_pseudo_stop_ring;
22377085Sudpa
22388275SEric Cheng infop->mri_intr = aggr_mac_intr;
22398275SEric Cheng infop->mri_poll = aggr_rx_poll;
224011878SVenu.Iyer@Sun.COM
224111878SVenu.Iyer@Sun.COM infop->mri_stat = aggr_rx_ring_stat;
224211878SVenu.Iyer@Sun.COM break;
224311878SVenu.Iyer@Sun.COM }
224411878SVenu.Iyer@Sun.COM case MAC_RING_TYPE_TX: {
224511878SVenu.Iyer@Sun.COM aggr_pseudo_tx_group_t *tx_group = &grp->lg_tx_group;
224611878SVenu.Iyer@Sun.COM aggr_pseudo_tx_ring_t *tx_ring;
224711878SVenu.Iyer@Sun.COM
224811878SVenu.Iyer@Sun.COM ASSERT(rg_index == -1);
224911878SVenu.Iyer@Sun.COM ASSERT(index < tx_group->atg_ring_cnt);
225011878SVenu.Iyer@Sun.COM
225111878SVenu.Iyer@Sun.COM tx_ring = &tx_group->atg_rings[index];
225211878SVenu.Iyer@Sun.COM tx_ring->atr_rh = rh;
225311878SVenu.Iyer@Sun.COM
225411878SVenu.Iyer@Sun.COM infop->mri_driver = (mac_ring_driver_t)tx_ring;
225511878SVenu.Iyer@Sun.COM infop->mri_start = NULL;
225611878SVenu.Iyer@Sun.COM infop->mri_stop = NULL;
225711878SVenu.Iyer@Sun.COM infop->mri_tx = aggr_ring_tx;
225811878SVenu.Iyer@Sun.COM infop->mri_stat = aggr_tx_ring_stat;
225911878SVenu.Iyer@Sun.COM /*
226011878SVenu.Iyer@Sun.COM * Use the hw TX ring handle to find if the ring needs
226111878SVenu.Iyer@Sun.COM * serialization or not. For NICs that do not expose
226211878SVenu.Iyer@Sun.COM * Tx rings, atr_hw_rh will be NULL.
226311878SVenu.Iyer@Sun.COM */
226411878SVenu.Iyer@Sun.COM if (tx_ring->atr_hw_rh != NULL) {
226511878SVenu.Iyer@Sun.COM infop->mri_flags =
226611878SVenu.Iyer@Sun.COM mac_hwring_getinfo(tx_ring->atr_hw_rh);
226711878SVenu.Iyer@Sun.COM }
22688275SEric Cheng break;
22698275SEric Cheng }
22708275SEric Cheng default:
22718275SEric Cheng break;
22728275SEric Cheng }
22738275SEric Cheng }
22748275SEric Cheng
22758275SEric Cheng static mblk_t *
aggr_rx_poll(void * arg,int bytes_to_pickup)22768275SEric Cheng aggr_rx_poll(void *arg, int bytes_to_pickup)
22778275SEric Cheng {
22788275SEric Cheng aggr_pseudo_rx_ring_t *rr_ring = arg;
22798275SEric Cheng aggr_port_t *port = rr_ring->arr_port;
22808275SEric Cheng aggr_grp_t *grp = port->lp_grp;
22818275SEric Cheng mblk_t *mp_chain, *mp, **mpp;
22828275SEric Cheng
22838275SEric Cheng mp_chain = mac_hwring_poll(rr_ring->arr_hw_rh, bytes_to_pickup);
22848275SEric Cheng
22858275SEric Cheng if (grp->lg_lacp_mode == AGGR_LACP_OFF)
22868275SEric Cheng return (mp_chain);
22878275SEric Cheng
22888275SEric Cheng mpp = &mp_chain;
22898275SEric Cheng while ((mp = *mpp) != NULL) {
22908275SEric Cheng if (MBLKL(mp) >= sizeof (struct ether_header)) {
22918275SEric Cheng struct ether_header *ehp;
22927085Sudpa
22938275SEric Cheng ehp = (struct ether_header *)mp->b_rptr;
22948275SEric Cheng if (ntohs(ehp->ether_type) == ETHERTYPE_SLOW) {
22958275SEric Cheng *mpp = mp->b_next;
22968275SEric Cheng mp->b_next = NULL;
22978275SEric Cheng aggr_recv_lacp(port,
22988275SEric Cheng (mac_resource_handle_t)rr_ring, mp);
22998275SEric Cheng continue;
23008275SEric Cheng }
23018275SEric Cheng }
23028275SEric Cheng
23038275SEric Cheng if (!port->lp_collector_enabled) {
23048275SEric Cheng *mpp = mp->b_next;
23058275SEric Cheng mp->b_next = NULL;
23068275SEric Cheng freemsg(mp);
23078275SEric Cheng continue;
23088275SEric Cheng }
23098275SEric Cheng mpp = &mp->b_next;
23108275SEric Cheng }
23118275SEric Cheng return (mp_chain);
23128275SEric Cheng }
23138275SEric Cheng
23148275SEric Cheng static int
aggr_addmac(void * arg,const uint8_t * mac_addr)23158275SEric Cheng aggr_addmac(void *arg, const uint8_t *mac_addr)
23168275SEric Cheng {
23178275SEric Cheng aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg;
23188275SEric Cheng aggr_unicst_addr_t *addr, **pprev;
23198275SEric Cheng aggr_grp_t *grp = rx_group->arg_grp;
23208275SEric Cheng aggr_port_t *port, *p;
23218275SEric Cheng mac_perim_handle_t mph;
23228275SEric Cheng int err = 0;
23238275SEric Cheng
23248275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
23258275SEric Cheng
23268275SEric Cheng if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) {
23278275SEric Cheng mac_perim_exit(mph);
23288275SEric Cheng return (0);
23297085Sudpa }
23307085Sudpa
23318275SEric Cheng /*
23328275SEric Cheng * Insert this mac address into the list of mac addresses owned by
23338275SEric Cheng * the aggregation pseudo group.
23348275SEric Cheng */
23358275SEric Cheng pprev = &rx_group->arg_macaddr;
23368275SEric Cheng while ((addr = *pprev) != NULL) {
23378275SEric Cheng if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) == 0) {
23388275SEric Cheng mac_perim_exit(mph);
23398275SEric Cheng return (EEXIST);
23408275SEric Cheng }
23418275SEric Cheng pprev = &addr->aua_next;
23428275SEric Cheng }
23438275SEric Cheng addr = kmem_alloc(sizeof (aggr_unicst_addr_t), KM_SLEEP);
23448275SEric Cheng bcopy(mac_addr, addr->aua_addr, ETHERADDRL);
23458275SEric Cheng addr->aua_next = NULL;
23468275SEric Cheng *pprev = addr;
23478275SEric Cheng
23488275SEric Cheng for (port = grp->lg_ports; port != NULL; port = port->lp_next)
23498275SEric Cheng if ((err = aggr_port_addmac(port, mac_addr)) != 0)
23508275SEric Cheng break;
23518275SEric Cheng
23528275SEric Cheng if (err != 0) {
23538275SEric Cheng for (p = grp->lg_ports; p != port; p = p->lp_next)
23548275SEric Cheng aggr_port_remmac(p, mac_addr);
23558275SEric Cheng
23568275SEric Cheng *pprev = NULL;
23578275SEric Cheng kmem_free(addr, sizeof (aggr_unicst_addr_t));
23587085Sudpa }
23598275SEric Cheng
23608275SEric Cheng mac_perim_exit(mph);
23618275SEric Cheng return (err);
23628275SEric Cheng }
23638275SEric Cheng
23648275SEric Cheng static int
aggr_remmac(void * arg,const uint8_t * mac_addr)23658275SEric Cheng aggr_remmac(void *arg, const uint8_t *mac_addr)
23668275SEric Cheng {
23678275SEric Cheng aggr_pseudo_rx_group_t *rx_group = (aggr_pseudo_rx_group_t *)arg;
23688275SEric Cheng aggr_unicst_addr_t *addr, **pprev;
23698275SEric Cheng aggr_grp_t *grp = rx_group->arg_grp;
23708275SEric Cheng aggr_port_t *port;
23718275SEric Cheng mac_perim_handle_t mph;
23728275SEric Cheng int err = 0;
23738275SEric Cheng
23748275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
23758275SEric Cheng
23768275SEric Cheng if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) {
23778275SEric Cheng mac_perim_exit(mph);
23788275SEric Cheng return (0);
23798275SEric Cheng }
23808275SEric Cheng
23818275SEric Cheng /*
23828275SEric Cheng * Insert this mac address into the list of mac addresses owned by
23838275SEric Cheng * the aggregation pseudo group.
23848275SEric Cheng */
23858275SEric Cheng pprev = &rx_group->arg_macaddr;
23868275SEric Cheng while ((addr = *pprev) != NULL) {
23878275SEric Cheng if (bcmp(mac_addr, addr->aua_addr, ETHERADDRL) != 0) {
23888275SEric Cheng pprev = &addr->aua_next;
23898275SEric Cheng continue;
23908275SEric Cheng }
23918275SEric Cheng break;
23928275SEric Cheng }
23938275SEric Cheng if (addr == NULL) {
23948275SEric Cheng mac_perim_exit(mph);
23958275SEric Cheng return (EINVAL);
23968275SEric Cheng }
23978275SEric Cheng
23988275SEric Cheng for (port = grp->lg_ports; port != NULL; port = port->lp_next)
23998275SEric Cheng aggr_port_remmac(port, mac_addr);
24008275SEric Cheng
24018275SEric Cheng *pprev = addr->aua_next;
24028275SEric Cheng kmem_free(addr, sizeof (aggr_unicst_addr_t));
24038275SEric Cheng
24048275SEric Cheng mac_perim_exit(mph);
24058275SEric Cheng return (err);
24067085Sudpa }
24077085Sudpa
24082311Sseb /*
24090Sstevel@tonic-gate * Add or remove the multicast addresses that are defined for the group
24100Sstevel@tonic-gate * to or from the specified port.
24118833SVenu.Iyer@Sun.COM *
24128833SVenu.Iyer@Sun.COM * Note that aggr_grp_multicst_port(..., B_TRUE) is called when the port
24138833SVenu.Iyer@Sun.COM * is started and attached, and aggr_grp_multicst_port(..., B_FALSE) is
24148833SVenu.Iyer@Sun.COM * called when the port is either stopped or detached.
24150Sstevel@tonic-gate */
24160Sstevel@tonic-gate void
aggr_grp_multicst_port(aggr_port_t * port,boolean_t add)24170Sstevel@tonic-gate aggr_grp_multicst_port(aggr_port_t *port, boolean_t add)
24180Sstevel@tonic-gate {
24190Sstevel@tonic-gate aggr_grp_t *grp = port->lp_grp;
24200Sstevel@tonic-gate
24218275SEric Cheng ASSERT(MAC_PERIM_HELD(port->lp_mh));
24228275SEric Cheng ASSERT(MAC_PERIM_HELD(grp->lg_mh));
24230Sstevel@tonic-gate
24248833SVenu.Iyer@Sun.COM if (!port->lp_started || port->lp_state != AGGR_PORT_STATE_ATTACHED)
24250Sstevel@tonic-gate return;
24260Sstevel@tonic-gate
24278275SEric Cheng mac_multicast_refresh(grp->lg_mh, aggr_port_multicst, port, add);
24280Sstevel@tonic-gate }
24290Sstevel@tonic-gate
24300Sstevel@tonic-gate static int
aggr_m_multicst(void * arg,boolean_t add,const uint8_t * addrp)24310Sstevel@tonic-gate aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
24320Sstevel@tonic-gate {
24330Sstevel@tonic-gate aggr_grp_t *grp = arg;
243412878Sanil.udupa@sun.com aggr_port_t *port = NULL, *errport = NULL;
24358275SEric Cheng mac_perim_handle_t mph;
243612878Sanil.udupa@sun.com int err = 0;
24370Sstevel@tonic-gate
24388275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
24390Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
24408833SVenu.Iyer@Sun.COM if (port->lp_state != AGGR_PORT_STATE_ATTACHED ||
24418833SVenu.Iyer@Sun.COM !port->lp_started) {
24420Sstevel@tonic-gate continue;
24438833SVenu.Iyer@Sun.COM }
244412878Sanil.udupa@sun.com err = aggr_port_multicst(port, add, addrp);
244512878Sanil.udupa@sun.com if (err != 0) {
244612878Sanil.udupa@sun.com errport = port;
244712878Sanil.udupa@sun.com break;
244812878Sanil.udupa@sun.com }
244912878Sanil.udupa@sun.com }
245012878Sanil.udupa@sun.com
245112878Sanil.udupa@sun.com /*
245212878Sanil.udupa@sun.com * At least one port caused error return and this error is returned to
245312878Sanil.udupa@sun.com * mac, eventually a NAK would be sent upwards.
245412878Sanil.udupa@sun.com * Some ports have this multicast address listed now, and some don't.
245512878Sanil.udupa@sun.com * Treat this error as a whole aggr failure not individual port failure.
245612878Sanil.udupa@sun.com * Therefore remove this multicast address from other ports.
245712878Sanil.udupa@sun.com */
245812878Sanil.udupa@sun.com if ((err != 0) && add) {
245912878Sanil.udupa@sun.com for (port = grp->lg_ports; port != errport;
246012878Sanil.udupa@sun.com port = port->lp_next) {
246112878Sanil.udupa@sun.com if (port->lp_state != AGGR_PORT_STATE_ATTACHED ||
246212878Sanil.udupa@sun.com !port->lp_started) {
246312878Sanil.udupa@sun.com continue;
246412878Sanil.udupa@sun.com }
246512878Sanil.udupa@sun.com (void) aggr_port_multicst(port, B_FALSE, addrp);
246612878Sanil.udupa@sun.com }
24670Sstevel@tonic-gate }
24688275SEric Cheng mac_perim_exit(mph);
24690Sstevel@tonic-gate return (err);
24700Sstevel@tonic-gate }
24710Sstevel@tonic-gate
24720Sstevel@tonic-gate static int
aggr_m_unicst(void * arg,const uint8_t * macaddr)24730Sstevel@tonic-gate aggr_m_unicst(void *arg, const uint8_t *macaddr)
24740Sstevel@tonic-gate {
24750Sstevel@tonic-gate aggr_grp_t *grp = arg;
24768275SEric Cheng mac_perim_handle_t mph;
24778275SEric Cheng int err;
24780Sstevel@tonic-gate
24798275SEric Cheng mac_perim_enter_by_mh(grp->lg_mh, &mph);
24808275SEric Cheng err = aggr_grp_modify_common(grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr,
24810Sstevel@tonic-gate 0, 0);
24828275SEric Cheng mac_perim_exit(mph);
24838275SEric Cheng return (err);
24840Sstevel@tonic-gate }
24850Sstevel@tonic-gate
24860Sstevel@tonic-gate /*
24870Sstevel@tonic-gate * Initialize the capabilities that are advertised for the group
24880Sstevel@tonic-gate * according to the capabilities of the constituent ports.
24890Sstevel@tonic-gate */
24900Sstevel@tonic-gate static void
aggr_grp_capab_set(aggr_grp_t * grp)24910Sstevel@tonic-gate aggr_grp_capab_set(aggr_grp_t *grp)
24920Sstevel@tonic-gate {
24932524Sseb uint32_t cksum;
24940Sstevel@tonic-gate aggr_port_t *port;
24958970SRoamer@Sun.COM mac_capab_lso_t cap_lso;
24960Sstevel@tonic-gate
24978275SEric Cheng ASSERT(grp->lg_mh == NULL);
24982311Sseb ASSERT(grp->lg_ports != NULL);
24990Sstevel@tonic-gate
25002311Sseb grp->lg_hcksum_txflags = (uint32_t)-1;
25015895Syz147064 grp->lg_zcopy = B_TRUE;
25025895Syz147064 grp->lg_vlan = B_TRUE;
25032311Sseb
25048970SRoamer@Sun.COM grp->lg_lso = B_TRUE;
25058970SRoamer@Sun.COM grp->lg_cap_lso.lso_flags = (t_uscalar_t)-1;
25068970SRoamer@Sun.COM grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max = (t_uscalar_t)-1;
25078970SRoamer@Sun.COM
25080Sstevel@tonic-gate for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
25092524Sseb if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum))
25102524Sseb cksum = 0;
25112524Sseb grp->lg_hcksum_txflags &= cksum;
25122311Sseb
25135895Syz147064 grp->lg_vlan &=
25145895Syz147064 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL);
25155895Syz147064
25165895Syz147064 grp->lg_zcopy &=
25175895Syz147064 !mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL);
25188970SRoamer@Sun.COM
25198970SRoamer@Sun.COM grp->lg_lso &=
25208970SRoamer@Sun.COM mac_capab_get(port->lp_mh, MAC_CAPAB_LSO, &cap_lso);
25218970SRoamer@Sun.COM if (grp->lg_lso) {
25228970SRoamer@Sun.COM grp->lg_cap_lso.lso_flags &= cap_lso.lso_flags;
25238970SRoamer@Sun.COM if (grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max >
25248970SRoamer@Sun.COM cap_lso.lso_basic_tcp_ipv4.lso_max)
25258970SRoamer@Sun.COM grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max =
25268970SRoamer@Sun.COM cap_lso.lso_basic_tcp_ipv4.lso_max;
25278970SRoamer@Sun.COM }
25280Sstevel@tonic-gate }
25290Sstevel@tonic-gate }
25300Sstevel@tonic-gate
25310Sstevel@tonic-gate /*
25322311Sseb * Checks whether the capabilities of the port being added are compatible
25330Sstevel@tonic-gate * with the current capabilities of the aggregation.
25340Sstevel@tonic-gate */
25350Sstevel@tonic-gate static boolean_t
aggr_grp_capab_check(aggr_grp_t * grp,aggr_port_t * port)25360Sstevel@tonic-gate aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port)
25370Sstevel@tonic-gate {
25385895Syz147064 uint32_t hcksum_txflags;
25390Sstevel@tonic-gate
25400Sstevel@tonic-gate ASSERT(grp->lg_ports != NULL);
25410Sstevel@tonic-gate
25425895Syz147064 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) &
25435895Syz147064 grp->lg_vlan) != grp->lg_vlan) {
25445895Syz147064 return (B_FALSE);
25455895Syz147064 }
25465895Syz147064
25475895Syz147064 if (((!mac_capab_get(port->lp_mh, MAC_CAPAB_NO_ZCOPY, NULL)) &
25485895Syz147064 grp->lg_zcopy) != grp->lg_zcopy) {
25495895Syz147064 return (B_FALSE);
25505895Syz147064 }
25515895Syz147064
25522311Sseb if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) {
25532311Sseb if (grp->lg_hcksum_txflags != 0)
25542311Sseb return (B_FALSE);
25552311Sseb } else if ((hcksum_txflags & grp->lg_hcksum_txflags) !=
25562311Sseb grp->lg_hcksum_txflags) {
25572311Sseb return (B_FALSE);
25582311Sseb }
25592311Sseb
25608970SRoamer@Sun.COM if (grp->lg_lso) {
25618970SRoamer@Sun.COM mac_capab_lso_t cap_lso;
25628970SRoamer@Sun.COM
25638970SRoamer@Sun.COM if (mac_capab_get(port->lp_mh, MAC_CAPAB_LSO, &cap_lso)) {
25648970SRoamer@Sun.COM if ((grp->lg_cap_lso.lso_flags & cap_lso.lso_flags) !=
25658970SRoamer@Sun.COM grp->lg_cap_lso.lso_flags)
25668970SRoamer@Sun.COM return (B_FALSE);
25678970SRoamer@Sun.COM if (grp->lg_cap_lso.lso_basic_tcp_ipv4.lso_max >
25688970SRoamer@Sun.COM cap_lso.lso_basic_tcp_ipv4.lso_max)
25698970SRoamer@Sun.COM return (B_FALSE);
25708970SRoamer@Sun.COM } else {
25718970SRoamer@Sun.COM return (B_FALSE);
25728970SRoamer@Sun.COM }
25738970SRoamer@Sun.COM }
25748970SRoamer@Sun.COM
25752311Sseb return (B_TRUE);
25760Sstevel@tonic-gate }
25772803Snd99603
25782803Snd99603 /*
25792803Snd99603 * Returns the maximum SDU according to the SDU of the constituent ports.
25802803Snd99603 */
25812803Snd99603 static uint_t
aggr_grp_max_sdu(aggr_grp_t * grp)25822803Snd99603 aggr_grp_max_sdu(aggr_grp_t *grp)
25832803Snd99603 {
25842803Snd99603 uint_t max_sdu = (uint_t)-1;
25852803Snd99603 aggr_port_t *port;
25862803Snd99603
25872803Snd99603 ASSERT(grp->lg_ports != NULL);
25882803Snd99603
25892803Snd99603 for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
25905903Ssowmini uint_t port_sdu_max;
25915903Ssowmini
25925903Ssowmini mac_sdu_get(port->lp_mh, NULL, &port_sdu_max);
25935903Ssowmini if (max_sdu > port_sdu_max)
25945903Ssowmini max_sdu = port_sdu_max;
25952803Snd99603 }
25962803Snd99603
25972803Snd99603 return (max_sdu);
25982803Snd99603 }
25992803Snd99603
26002803Snd99603 /*
26012803Snd99603 * Checks if the maximum SDU of the specified port is compatible
26022803Snd99603 * with the maximum SDU of the specified aggregation group, returns
26032803Snd99603 * B_TRUE if it is, B_FALSE otherwise.
26042803Snd99603 */
26052803Snd99603 static boolean_t
aggr_grp_sdu_check(aggr_grp_t * grp,aggr_port_t * port)26062803Snd99603 aggr_grp_sdu_check(aggr_grp_t *grp, aggr_port_t *port)
26072803Snd99603 {
26085903Ssowmini uint_t port_sdu_max;
26092803Snd99603
26105903Ssowmini mac_sdu_get(port->lp_mh, NULL, &port_sdu_max);
26115903Ssowmini return (port_sdu_max >= grp->lg_max_sdu);
26122803Snd99603 }
26135895Syz147064
26145895Syz147064 /*
26155895Syz147064 * Returns the maximum margin according to the margin of the constituent ports.
26165895Syz147064 */
26175895Syz147064 static uint32_t
aggr_grp_max_margin(aggr_grp_t * grp)26185895Syz147064 aggr_grp_max_margin(aggr_grp_t *grp)
26195895Syz147064 {
26205895Syz147064 uint32_t margin = UINT32_MAX;
26215895Syz147064 aggr_port_t *port;
26225895Syz147064
26238275SEric Cheng ASSERT(grp->lg_mh == NULL);
26245895Syz147064 ASSERT(grp->lg_ports != NULL);
26255895Syz147064
26265895Syz147064 for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
26275895Syz147064 if (margin > port->lp_margin)
26285895Syz147064 margin = port->lp_margin;
26295895Syz147064 }
26305895Syz147064
26315895Syz147064 grp->lg_margin = margin;
26325895Syz147064 return (margin);
26335895Syz147064 }
26345895Syz147064
26355895Syz147064 /*
26365895Syz147064 * Checks if the maximum margin of the specified port is compatible
26375895Syz147064 * with the maximum margin of the specified aggregation group, returns
26385895Syz147064 * B_TRUE if it is, B_FALSE otherwise.
26395895Syz147064 */
26405895Syz147064 static boolean_t
aggr_grp_margin_check(aggr_grp_t * grp,aggr_port_t * port)26415895Syz147064 aggr_grp_margin_check(aggr_grp_t *grp, aggr_port_t *port)
26425895Syz147064 {
26435895Syz147064 if (port->lp_margin >= grp->lg_margin)
26445895Syz147064 return (B_TRUE);
26455895Syz147064
26465895Syz147064 /*
26475895Syz147064 * See whether the current margin value is allowed to be changed to
26485895Syz147064 * the new value.
26495895Syz147064 */
26505895Syz147064 if (!mac_margin_update(grp->lg_mh, port->lp_margin))
26515895Syz147064 return (B_FALSE);
26525895Syz147064
26535895Syz147064 grp->lg_margin = port->lp_margin;
26545895Syz147064 return (B_TRUE);
26555895Syz147064 }
26568603SGirish.Moodalbail@Sun.COM
26578603SGirish.Moodalbail@Sun.COM /*
26588603SGirish.Moodalbail@Sun.COM * Set MTU on individual ports of an aggregation group
26598603SGirish.Moodalbail@Sun.COM */
26608603SGirish.Moodalbail@Sun.COM static int
aggr_set_port_sdu(aggr_grp_t * grp,aggr_port_t * port,uint32_t sdu,uint32_t * old_mtu)26618603SGirish.Moodalbail@Sun.COM aggr_set_port_sdu(aggr_grp_t *grp, aggr_port_t *port, uint32_t sdu,
26628603SGirish.Moodalbail@Sun.COM uint32_t *old_mtu)
26638603SGirish.Moodalbail@Sun.COM {
26648603SGirish.Moodalbail@Sun.COM boolean_t removed = B_FALSE;
26658603SGirish.Moodalbail@Sun.COM mac_perim_handle_t mph;
26668603SGirish.Moodalbail@Sun.COM mac_diag_t diag;
26678603SGirish.Moodalbail@Sun.COM int err, rv, retry = 0;
26688603SGirish.Moodalbail@Sun.COM
26698603SGirish.Moodalbail@Sun.COM if (port->lp_mah != NULL) {
26708603SGirish.Moodalbail@Sun.COM (void) mac_unicast_remove(port->lp_mch, port->lp_mah);
26718603SGirish.Moodalbail@Sun.COM port->lp_mah = NULL;
26728603SGirish.Moodalbail@Sun.COM removed = B_TRUE;
26738603SGirish.Moodalbail@Sun.COM }
26748603SGirish.Moodalbail@Sun.COM err = mac_set_mtu(port->lp_mh, sdu, old_mtu);
26758603SGirish.Moodalbail@Sun.COM try_again:
26769024SVenu.Iyer@Sun.COM if (removed && (rv = mac_unicast_add(port->lp_mch, NULL,
26779024SVenu.Iyer@Sun.COM MAC_UNICAST_PRIMARY | MAC_UNICAST_DISABLE_TX_VID_CHECK,
26789024SVenu.Iyer@Sun.COM &port->lp_mah, 0, &diag)) != 0) {
26798603SGirish.Moodalbail@Sun.COM /*
26808603SGirish.Moodalbail@Sun.COM * following is a workaround for a bug in 'bge' driver.
26818603SGirish.Moodalbail@Sun.COM * See CR 6794654 for more information and this work around
26828603SGirish.Moodalbail@Sun.COM * will be removed once the CR is fixed.
26838603SGirish.Moodalbail@Sun.COM */
26848603SGirish.Moodalbail@Sun.COM if (rv == EIO && retry++ < 3) {
26858603SGirish.Moodalbail@Sun.COM delay(2 * hz);
26868603SGirish.Moodalbail@Sun.COM goto try_again;
26878603SGirish.Moodalbail@Sun.COM }
26888603SGirish.Moodalbail@Sun.COM /*
26899024SVenu.Iyer@Sun.COM * if mac_unicast_add() failed while setting the MTU,
26908603SGirish.Moodalbail@Sun.COM * detach the port from the group.
26918603SGirish.Moodalbail@Sun.COM */
26928603SGirish.Moodalbail@Sun.COM mac_perim_enter_by_mh(port->lp_mh, &mph);
26938603SGirish.Moodalbail@Sun.COM (void) aggr_grp_detach_port(grp, port);
26948603SGirish.Moodalbail@Sun.COM mac_perim_exit(mph);
26958603SGirish.Moodalbail@Sun.COM cmn_err(CE_WARN, "Unable to restart the port %s while "
26968603SGirish.Moodalbail@Sun.COM "setting MTU. Detaching the port from the aggregation.",
26978603SGirish.Moodalbail@Sun.COM mac_client_name(port->lp_mch));
26988603SGirish.Moodalbail@Sun.COM }
26998603SGirish.Moodalbail@Sun.COM return (err);
27008603SGirish.Moodalbail@Sun.COM }
27018603SGirish.Moodalbail@Sun.COM
27028603SGirish.Moodalbail@Sun.COM static int
aggr_sdu_update(aggr_grp_t * grp,uint32_t sdu)27038603SGirish.Moodalbail@Sun.COM aggr_sdu_update(aggr_grp_t *grp, uint32_t sdu)
27048603SGirish.Moodalbail@Sun.COM {
27058603SGirish.Moodalbail@Sun.COM int err = 0, i, rv;
27068603SGirish.Moodalbail@Sun.COM aggr_port_t *port;
27078603SGirish.Moodalbail@Sun.COM uint32_t *mtu;
27088603SGirish.Moodalbail@Sun.COM
27098603SGirish.Moodalbail@Sun.COM ASSERT(MAC_PERIM_HELD(grp->lg_mh));
27108603SGirish.Moodalbail@Sun.COM
27118603SGirish.Moodalbail@Sun.COM /*
27128603SGirish.Moodalbail@Sun.COM * If the MTU being set is equal to aggr group's maximum
27138603SGirish.Moodalbail@Sun.COM * allowable value, then there is nothing to change
27148603SGirish.Moodalbail@Sun.COM */
27158603SGirish.Moodalbail@Sun.COM if (sdu == grp->lg_max_sdu)
27168603SGirish.Moodalbail@Sun.COM return (0);
27178603SGirish.Moodalbail@Sun.COM
27188603SGirish.Moodalbail@Sun.COM /* 0 is aggr group's min sdu */
27198603SGirish.Moodalbail@Sun.COM if (sdu == 0)
27208603SGirish.Moodalbail@Sun.COM return (EINVAL);
27218603SGirish.Moodalbail@Sun.COM
27228603SGirish.Moodalbail@Sun.COM mtu = kmem_alloc(sizeof (uint32_t) * grp->lg_nports, KM_SLEEP);
27238603SGirish.Moodalbail@Sun.COM for (port = grp->lg_ports, i = 0; port != NULL && err == 0;
27248603SGirish.Moodalbail@Sun.COM port = port->lp_next, i++) {
27258603SGirish.Moodalbail@Sun.COM err = aggr_set_port_sdu(grp, port, sdu, mtu + i);
27268603SGirish.Moodalbail@Sun.COM }
27278603SGirish.Moodalbail@Sun.COM if (err != 0) {
27288603SGirish.Moodalbail@Sun.COM /* recover from error: reset the mtus of the ports */
27298603SGirish.Moodalbail@Sun.COM aggr_port_t *tmp;
27308603SGirish.Moodalbail@Sun.COM
27318603SGirish.Moodalbail@Sun.COM for (tmp = grp->lg_ports, i = 0; tmp != port;
27328603SGirish.Moodalbail@Sun.COM tmp = tmp->lp_next, i++) {
27338603SGirish.Moodalbail@Sun.COM (void) aggr_set_port_sdu(grp, tmp, *(mtu + i), NULL);
27348603SGirish.Moodalbail@Sun.COM }
27358603SGirish.Moodalbail@Sun.COM goto bail;
27368603SGirish.Moodalbail@Sun.COM }
27378603SGirish.Moodalbail@Sun.COM grp->lg_max_sdu = aggr_grp_max_sdu(grp);
27388603SGirish.Moodalbail@Sun.COM rv = mac_maxsdu_update(grp->lg_mh, grp->lg_max_sdu);
27398603SGirish.Moodalbail@Sun.COM ASSERT(rv == 0);
27408603SGirish.Moodalbail@Sun.COM bail:
27418603SGirish.Moodalbail@Sun.COM kmem_free(mtu, sizeof (uint32_t) * grp->lg_nports);
27428603SGirish.Moodalbail@Sun.COM return (err);
27438603SGirish.Moodalbail@Sun.COM }
27448603SGirish.Moodalbail@Sun.COM
27458603SGirish.Moodalbail@Sun.COM /*
27468603SGirish.Moodalbail@Sun.COM * Callback functions for set/get of properties
27478603SGirish.Moodalbail@Sun.COM */
27488603SGirish.Moodalbail@Sun.COM /*ARGSUSED*/
27498603SGirish.Moodalbail@Sun.COM static int
aggr_m_setprop(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)27508603SGirish.Moodalbail@Sun.COM aggr_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
27518603SGirish.Moodalbail@Sun.COM uint_t pr_valsize, const void *pr_val)
27528603SGirish.Moodalbail@Sun.COM {
27538603SGirish.Moodalbail@Sun.COM int err = ENOTSUP;
27548603SGirish.Moodalbail@Sun.COM aggr_grp_t *grp = m_driver;
27558603SGirish.Moodalbail@Sun.COM
27568603SGirish.Moodalbail@Sun.COM switch (pr_num) {
27578603SGirish.Moodalbail@Sun.COM case MAC_PROP_MTU: {
27588603SGirish.Moodalbail@Sun.COM uint32_t mtu;
27598603SGirish.Moodalbail@Sun.COM
27608603SGirish.Moodalbail@Sun.COM if (pr_valsize < sizeof (mtu)) {
27618603SGirish.Moodalbail@Sun.COM err = EINVAL;
27628603SGirish.Moodalbail@Sun.COM break;
27638603SGirish.Moodalbail@Sun.COM }
27648603SGirish.Moodalbail@Sun.COM bcopy(pr_val, &mtu, sizeof (mtu));
27658603SGirish.Moodalbail@Sun.COM err = aggr_sdu_update(grp, mtu);
27668603SGirish.Moodalbail@Sun.COM break;
27678603SGirish.Moodalbail@Sun.COM }
27688603SGirish.Moodalbail@Sun.COM default:
27698603SGirish.Moodalbail@Sun.COM break;
27708603SGirish.Moodalbail@Sun.COM }
27718603SGirish.Moodalbail@Sun.COM return (err);
27728603SGirish.Moodalbail@Sun.COM }
27738603SGirish.Moodalbail@Sun.COM
277412850SPrakash.Jalan@Sun.COM typedef struct rboundary {
277512850SPrakash.Jalan@Sun.COM uint32_t bval;
277612850SPrakash.Jalan@Sun.COM int btype;
277712850SPrakash.Jalan@Sun.COM } rboundary_t;
277812850SPrakash.Jalan@Sun.COM
277912850SPrakash.Jalan@Sun.COM /*
278012850SPrakash.Jalan@Sun.COM * This function finds the intersection of mtu ranges stored in arrays -
278112850SPrakash.Jalan@Sun.COM * mrange[0] ... mrange[mcount -1]. It returns the intersection in rval.
278212850SPrakash.Jalan@Sun.COM * Individual arrays are assumed to contain non-overlapping ranges.
278312850SPrakash.Jalan@Sun.COM * Algorithm:
278412850SPrakash.Jalan@Sun.COM * A range has two boundaries - min and max. We scan all arrays and store
278512850SPrakash.Jalan@Sun.COM * each boundary as a separate element in a temporary array. We also store
278612850SPrakash.Jalan@Sun.COM * the boundary types, min or max, as +1 or -1 respectively in the temporary
278712850SPrakash.Jalan@Sun.COM * array. Then we sort the temporary array in ascending order. We scan the
278812850SPrakash.Jalan@Sun.COM * sorted array from lower to higher values and keep a cumulative sum of
278912850SPrakash.Jalan@Sun.COM * boundary types. Element in the temporary array for which the sum reaches
279012850SPrakash.Jalan@Sun.COM * mcount is a min boundary of a range in the result and next element will be
279112850SPrakash.Jalan@Sun.COM * max boundary.
279212850SPrakash.Jalan@Sun.COM *
279312850SPrakash.Jalan@Sun.COM * Example for mcount = 3,
279412850SPrakash.Jalan@Sun.COM *
279512850SPrakash.Jalan@Sun.COM * ----|_________|-------|_______|----|__|------ mrange[0]
279612850SPrakash.Jalan@Sun.COM *
279712850SPrakash.Jalan@Sun.COM * -------|________|--|____________|-----|___|-- mrange[1]
279812850SPrakash.Jalan@Sun.COM *
279912850SPrakash.Jalan@Sun.COM * --------|________________|-------|____|------ mrange[2]
280012850SPrakash.Jalan@Sun.COM *
280112850SPrakash.Jalan@Sun.COM * 3 2 1
280212850SPrakash.Jalan@Sun.COM * \|/
280312850SPrakash.Jalan@Sun.COM * 1 23 2 1 2 3 2 1 01 2 V 0 <- the sum
280412850SPrakash.Jalan@Sun.COM * ----|--||-----|-|--|--|--|----|-||-|--|---|-- sorted array
280512850SPrakash.Jalan@Sun.COM *
280612850SPrakash.Jalan@Sun.COM * same min and max
280712850SPrakash.Jalan@Sun.COM * V
280812850SPrakash.Jalan@Sun.COM * --------|_____|-------|__|------------|------ intersecting ranges
280912850SPrakash.Jalan@Sun.COM */
281012850SPrakash.Jalan@Sun.COM void
aggr_mtu_range_intersection(mac_propval_range_t ** mrange,int mcount,mac_propval_uint32_range_t ** prval,int * prmaxcnt,int * prcount)281112850SPrakash.Jalan@Sun.COM aggr_mtu_range_intersection(mac_propval_range_t **mrange, int mcount,
281212850SPrakash.Jalan@Sun.COM mac_propval_uint32_range_t **prval, int *prmaxcnt, int *prcount)
28139514SGirish.Moodalbail@Sun.COM {
281412850SPrakash.Jalan@Sun.COM mac_propval_uint32_range_t *rval, *ur;
281512850SPrakash.Jalan@Sun.COM int rmaxcnt, rcount;
281612850SPrakash.Jalan@Sun.COM size_t sz_range32;
281712850SPrakash.Jalan@Sun.COM rboundary_t *ta; /* temporary array */
281812850SPrakash.Jalan@Sun.COM rboundary_t temp;
281912850SPrakash.Jalan@Sun.COM boolean_t range_started = B_FALSE;
282012850SPrakash.Jalan@Sun.COM int i, j, m, sum;
282112850SPrakash.Jalan@Sun.COM
282212850SPrakash.Jalan@Sun.COM sz_range32 = sizeof (mac_propval_uint32_range_t);
282312850SPrakash.Jalan@Sun.COM
282412850SPrakash.Jalan@Sun.COM for (i = 0, rmaxcnt = 0; i < mcount; i++)
282512850SPrakash.Jalan@Sun.COM rmaxcnt += mrange[i]->mpr_count;
282612850SPrakash.Jalan@Sun.COM
282712850SPrakash.Jalan@Sun.COM /* Allocate enough space to store the results */
282812850SPrakash.Jalan@Sun.COM rval = kmem_alloc(rmaxcnt * sz_range32, KM_SLEEP);
282912850SPrakash.Jalan@Sun.COM
283012850SPrakash.Jalan@Sun.COM /* Number of boundaries are twice as many as ranges */
283112850SPrakash.Jalan@Sun.COM ta = kmem_alloc(2 * rmaxcnt * sizeof (rboundary_t), KM_SLEEP);
283212850SPrakash.Jalan@Sun.COM
283312850SPrakash.Jalan@Sun.COM for (i = 0, m = 0; i < mcount; i++) {
283412850SPrakash.Jalan@Sun.COM ur = &(mrange[i]->mpr_range_uint32[0]);
283512850SPrakash.Jalan@Sun.COM for (j = 0; j < mrange[i]->mpr_count; j++) {
283612850SPrakash.Jalan@Sun.COM ta[m].bval = ur[j].mpur_min;
283712850SPrakash.Jalan@Sun.COM ta[m++].btype = 1;
283812850SPrakash.Jalan@Sun.COM ta[m].bval = ur[j].mpur_max;
283912850SPrakash.Jalan@Sun.COM ta[m++].btype = -1;
284012850SPrakash.Jalan@Sun.COM }
284112850SPrakash.Jalan@Sun.COM }
284212850SPrakash.Jalan@Sun.COM
284312850SPrakash.Jalan@Sun.COM /*
284412850SPrakash.Jalan@Sun.COM * Sort the temporary array in ascending order of bval;
284512850SPrakash.Jalan@Sun.COM * if boundary values are same then sort on btype.
284612850SPrakash.Jalan@Sun.COM */
284712850SPrakash.Jalan@Sun.COM for (i = 0; i < m-1; i++) {
284812850SPrakash.Jalan@Sun.COM for (j = i+1; j < m; j++) {
284912850SPrakash.Jalan@Sun.COM if ((ta[i].bval > ta[j].bval) ||
285012850SPrakash.Jalan@Sun.COM ((ta[i].bval == ta[j].bval) &&
285112850SPrakash.Jalan@Sun.COM (ta[i].btype < ta[j].btype))) {
285212850SPrakash.Jalan@Sun.COM temp = ta[i];
285312850SPrakash.Jalan@Sun.COM ta[i] = ta[j];
285412850SPrakash.Jalan@Sun.COM ta[j] = temp;
285512850SPrakash.Jalan@Sun.COM }
285612850SPrakash.Jalan@Sun.COM }
285712850SPrakash.Jalan@Sun.COM }
285812850SPrakash.Jalan@Sun.COM
285912850SPrakash.Jalan@Sun.COM /* Walk through temporary array to find all ranges in the results */
286012850SPrakash.Jalan@Sun.COM for (i = 0, sum = 0, rcount = 0; i < m; i++) {
286112850SPrakash.Jalan@Sun.COM sum += ta[i].btype;
286212850SPrakash.Jalan@Sun.COM if (sum == mcount) {
286312850SPrakash.Jalan@Sun.COM rval[rcount].mpur_min = ta[i].bval;
286412850SPrakash.Jalan@Sun.COM range_started = B_TRUE;
286512850SPrakash.Jalan@Sun.COM } else if (sum < mcount && range_started) {
286612850SPrakash.Jalan@Sun.COM rval[rcount++].mpur_max = ta[i].bval;
286712850SPrakash.Jalan@Sun.COM range_started = B_FALSE;
286812850SPrakash.Jalan@Sun.COM }
286912850SPrakash.Jalan@Sun.COM }
287012850SPrakash.Jalan@Sun.COM
287112850SPrakash.Jalan@Sun.COM *prval = rval;
287212850SPrakash.Jalan@Sun.COM *prmaxcnt = rmaxcnt;
287312850SPrakash.Jalan@Sun.COM *prcount = rcount;
2874*13032SPrakash.Jalan@Sun.COM
2875*13032SPrakash.Jalan@Sun.COM kmem_free(ta, 2 * rmaxcnt * sizeof (rboundary_t));
287612850SPrakash.Jalan@Sun.COM }
287712850SPrakash.Jalan@Sun.COM
287812850SPrakash.Jalan@Sun.COM /*
287912850SPrakash.Jalan@Sun.COM * Returns the mtu ranges which could be supported by aggr group.
288012850SPrakash.Jalan@Sun.COM * prmaxcnt returns the size of the buffer prval, prcount returns
288112850SPrakash.Jalan@Sun.COM * the number of valid entries in prval. Caller is responsible
288212850SPrakash.Jalan@Sun.COM * for freeing up prval.
288312850SPrakash.Jalan@Sun.COM */
288412850SPrakash.Jalan@Sun.COM int
aggr_grp_possible_mtu_range(aggr_grp_t * grp,mac_propval_uint32_range_t ** prval,int * prmaxcnt,int * prcount)288512850SPrakash.Jalan@Sun.COM aggr_grp_possible_mtu_range(aggr_grp_t *grp, mac_propval_uint32_range_t **prval,
288612850SPrakash.Jalan@Sun.COM int *prmaxcnt, int *prcount)
288712850SPrakash.Jalan@Sun.COM {
288812850SPrakash.Jalan@Sun.COM mac_propval_range_t **vals;
28899514SGirish.Moodalbail@Sun.COM aggr_port_t *port;
28909514SGirish.Moodalbail@Sun.COM mac_perim_handle_t mph;
289112850SPrakash.Jalan@Sun.COM uint_t i, numr;
28929514SGirish.Moodalbail@Sun.COM int err = 0;
289312850SPrakash.Jalan@Sun.COM size_t sz_propval, sz_range32;
289412850SPrakash.Jalan@Sun.COM size_t size;
289512850SPrakash.Jalan@Sun.COM
289612850SPrakash.Jalan@Sun.COM sz_propval = sizeof (mac_propval_range_t);
289712850SPrakash.Jalan@Sun.COM sz_range32 = sizeof (mac_propval_uint32_range_t);
28989514SGirish.Moodalbail@Sun.COM
28999514SGirish.Moodalbail@Sun.COM ASSERT(MAC_PERIM_HELD(grp->lg_mh));
29009514SGirish.Moodalbail@Sun.COM
290112850SPrakash.Jalan@Sun.COM vals = kmem_zalloc(sizeof (mac_propval_range_t *) * grp->lg_nports,
29029514SGirish.Moodalbail@Sun.COM KM_SLEEP);
29039514SGirish.Moodalbail@Sun.COM
29049514SGirish.Moodalbail@Sun.COM for (port = grp->lg_ports, i = 0; port != NULL;
29059514SGirish.Moodalbail@Sun.COM port = port->lp_next, i++) {
290612850SPrakash.Jalan@Sun.COM
290712850SPrakash.Jalan@Sun.COM size = sz_propval;
290812850SPrakash.Jalan@Sun.COM vals[i] = kmem_alloc(size, KM_SLEEP);
290912850SPrakash.Jalan@Sun.COM vals[i]->mpr_count = 1;
291012850SPrakash.Jalan@Sun.COM
29119514SGirish.Moodalbail@Sun.COM mac_perim_enter_by_mh(port->lp_mh, &mph);
291212850SPrakash.Jalan@Sun.COM
291311878SVenu.Iyer@Sun.COM err = mac_prop_info(port->lp_mh, MAC_PROP_MTU, NULL,
291412850SPrakash.Jalan@Sun.COM NULL, 0, vals[i], NULL);
291512850SPrakash.Jalan@Sun.COM if (err == ENOSPC) {
291612850SPrakash.Jalan@Sun.COM /*
291712850SPrakash.Jalan@Sun.COM * Not enough space to hold all ranges.
291812850SPrakash.Jalan@Sun.COM * Allocate extra space as indicated and retry.
291912850SPrakash.Jalan@Sun.COM */
292012850SPrakash.Jalan@Sun.COM numr = vals[i]->mpr_count;
292112850SPrakash.Jalan@Sun.COM kmem_free(vals[i], sz_propval);
292212850SPrakash.Jalan@Sun.COM size = sz_propval + (numr - 1) * sz_range32;
292312850SPrakash.Jalan@Sun.COM vals[i] = kmem_alloc(size, KM_SLEEP);
292412850SPrakash.Jalan@Sun.COM vals[i]->mpr_count = numr;
292512850SPrakash.Jalan@Sun.COM err = mac_prop_info(port->lp_mh, MAC_PROP_MTU, NULL,
292612850SPrakash.Jalan@Sun.COM NULL, 0, vals[i], NULL);
292712850SPrakash.Jalan@Sun.COM ASSERT(err != ENOSPC);
292812850SPrakash.Jalan@Sun.COM }
29299514SGirish.Moodalbail@Sun.COM mac_perim_exit(mph);
293012850SPrakash.Jalan@Sun.COM if (err != 0) {
293112850SPrakash.Jalan@Sun.COM kmem_free(vals[i], size);
293212850SPrakash.Jalan@Sun.COM vals[i] = NULL;
29339514SGirish.Moodalbail@Sun.COM break;
293412850SPrakash.Jalan@Sun.COM }
29359514SGirish.Moodalbail@Sun.COM }
293611878SVenu.Iyer@Sun.COM
29379514SGirish.Moodalbail@Sun.COM /*
29389514SGirish.Moodalbail@Sun.COM * if any of the underlying ports does not support changing MTU then
29399514SGirish.Moodalbail@Sun.COM * just return ENOTSUP
29409514SGirish.Moodalbail@Sun.COM */
29419514SGirish.Moodalbail@Sun.COM if (port != NULL) {
29429514SGirish.Moodalbail@Sun.COM ASSERT(err != 0);
29439514SGirish.Moodalbail@Sun.COM goto done;
29449514SGirish.Moodalbail@Sun.COM }
294511878SVenu.Iyer@Sun.COM
294612850SPrakash.Jalan@Sun.COM aggr_mtu_range_intersection(vals, grp->lg_nports, prval, prmaxcnt,
294712850SPrakash.Jalan@Sun.COM prcount);
294812850SPrakash.Jalan@Sun.COM
294912850SPrakash.Jalan@Sun.COM done:
29509514SGirish.Moodalbail@Sun.COM for (i = 0; i < grp->lg_nports; i++) {
295112850SPrakash.Jalan@Sun.COM if (vals[i] != NULL) {
295212850SPrakash.Jalan@Sun.COM numr = vals[i]->mpr_count;
295312850SPrakash.Jalan@Sun.COM size = sz_propval + (numr - 1) * sz_range32;
295412850SPrakash.Jalan@Sun.COM kmem_free(vals[i], size);
295512850SPrakash.Jalan@Sun.COM }
29569514SGirish.Moodalbail@Sun.COM }
295712850SPrakash.Jalan@Sun.COM
295812850SPrakash.Jalan@Sun.COM kmem_free(vals, sizeof (mac_propval_range_t *) * grp->lg_nports);
29599514SGirish.Moodalbail@Sun.COM return (err);
29609514SGirish.Moodalbail@Sun.COM }
29619514SGirish.Moodalbail@Sun.COM
296211878SVenu.Iyer@Sun.COM static void
aggr_m_propinfo(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)296311878SVenu.Iyer@Sun.COM aggr_m_propinfo(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
296411878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh)
29658603SGirish.Moodalbail@Sun.COM {
296612850SPrakash.Jalan@Sun.COM aggr_grp_t *grp = m_driver;
296712850SPrakash.Jalan@Sun.COM mac_propval_uint32_range_t *rval = NULL;
296812850SPrakash.Jalan@Sun.COM int i, rcount, rmaxcnt;
296912850SPrakash.Jalan@Sun.COM int err = 0;
29709514SGirish.Moodalbail@Sun.COM
297111878SVenu.Iyer@Sun.COM _NOTE(ARGUNUSED(pr_name));
297211878SVenu.Iyer@Sun.COM
29739514SGirish.Moodalbail@Sun.COM switch (pr_num) {
297412850SPrakash.Jalan@Sun.COM case MAC_PROP_MTU:
297512850SPrakash.Jalan@Sun.COM
297612850SPrakash.Jalan@Sun.COM err = aggr_grp_possible_mtu_range(grp, &rval, &rmaxcnt,
297712850SPrakash.Jalan@Sun.COM &rcount);
297812850SPrakash.Jalan@Sun.COM if (err != 0) {
297912850SPrakash.Jalan@Sun.COM ASSERT(rval == NULL);
298011878SVenu.Iyer@Sun.COM return;
298112850SPrakash.Jalan@Sun.COM }
298212850SPrakash.Jalan@Sun.COM for (i = 0; i < rcount; i++) {
298312850SPrakash.Jalan@Sun.COM mac_prop_info_set_range_uint32(prh,
298412850SPrakash.Jalan@Sun.COM rval[i].mpur_min, rval[i].mpur_max);
298512850SPrakash.Jalan@Sun.COM }
298612850SPrakash.Jalan@Sun.COM kmem_free(rval, sizeof (mac_propval_uint32_range_t) * rmaxcnt);
298711878SVenu.Iyer@Sun.COM break;
29889514SGirish.Moodalbail@Sun.COM }
29898603SGirish.Moodalbail@Sun.COM }
2990