brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 5f2cd9a Raw
369 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include <linux/if_bridge.h>4 5#include "lan966x_main.h"6 7static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x)8{9	u32 visited = GENMASK(lan966x->num_phys_ports - 1, 0);10	int p, lag, i;11 12	/* Reset destination and aggregation PGIDS */13	for (p = 0; p < lan966x->num_phys_ports; ++p)14		lan_wr(ANA_PGID_PGID_SET(BIT(p)),15		       lan966x, ANA_PGID(p));16 17	for (p = PGID_AGGR; p < PGID_SRC; ++p)18		lan_wr(ANA_PGID_PGID_SET(visited),19		       lan966x, ANA_PGID(p));20 21	/* The visited ports bitmask holds the list of ports offloading any22	 * bonding interface. Initially we mark all these ports as unvisited,23	 * then every time we visit a port in this bitmask, we know that it is24	 * the lowest numbered port, i.e. the one whose logical ID == physical25	 * port ID == LAG ID. So we mark as visited all further ports in the26	 * bitmask that are offloading the same bonding interface. This way,27	 * we set up the aggregation PGIDs only once per bonding interface.28	 */29	for (p = 0; p < lan966x->num_phys_ports; ++p) {30		struct lan966x_port *port = lan966x->ports[p];31 32		if (!port || !port->bond)33			continue;34 35		visited &= ~BIT(p);36	}37 38	/* Now, set PGIDs for each active LAG */39	for (lag = 0; lag < lan966x->num_phys_ports; ++lag) {40		struct lan966x_port *port = lan966x->ports[lag];41		int num_active_ports = 0;42		struct net_device *bond;43		unsigned long bond_mask;44		u8 aggr_idx[16];45 46		if (!port || !port->bond || (visited & BIT(lag)))47			continue;48 49		bond = port->bond;50		bond_mask = lan966x_lag_get_mask(lan966x, bond);51 52		for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) {53			struct lan966x_port *port = lan966x->ports[p];54 55			if (!port)56				continue;57 58			lan_wr(ANA_PGID_PGID_SET(bond_mask),59			       lan966x, ANA_PGID(p));60			if (port->lag_tx_active)61				aggr_idx[num_active_ports++] = p;62		}63 64		for (i = PGID_AGGR; i < PGID_SRC; ++i) {65			u32 ac;66 67			ac = lan_rd(lan966x, ANA_PGID(i));68			ac &= ~bond_mask;69			/* Don't do division by zero if there was no active70			 * port. Just make all aggregation codes zero.71			 */72			if (num_active_ports)73				ac |= BIT(aggr_idx[i % num_active_ports]);74			lan_wr(ANA_PGID_PGID_SET(ac),75			       lan966x, ANA_PGID(i));76		}77 78		/* Mark all ports in the same LAG as visited to avoid applying79		 * the same config again.80		 */81		for (p = lag; p < lan966x->num_phys_ports; p++) {82			struct lan966x_port *port = lan966x->ports[p];83 84			if (!port)85				continue;86 87			if (port->bond == bond)88				visited |= BIT(p);89		}90	}91}92 93static void lan966x_lag_set_port_ids(struct lan966x *lan966x)94{95	struct lan966x_port *port;96	u32 bond_mask;97	u32 lag_id;98	int p;99 100	for (p = 0; p < lan966x->num_phys_ports; ++p) {101		port = lan966x->ports[p];102		if (!port)103			continue;104 105		lag_id = port->chip_port;106 107		bond_mask = lan966x_lag_get_mask(lan966x, port->bond);108		if (bond_mask)109			lag_id = __ffs(bond_mask);110 111		lan_rmw(ANA_PORT_CFG_PORTID_VAL_SET(lag_id),112			ANA_PORT_CFG_PORTID_VAL,113			lan966x, ANA_PORT_CFG(port->chip_port));114	}115}116 117static void lan966x_lag_update_ids(struct lan966x *lan966x)118{119	lan966x_lag_set_port_ids(lan966x);120	lan966x_update_fwd_mask(lan966x);121	lan966x_lag_set_aggr_pgids(lan966x);122}123 124int lan966x_lag_port_join(struct lan966x_port *port,125			  struct net_device *brport_dev,126			  struct net_device *bond,127			  struct netlink_ext_ack *extack)128{129	struct lan966x *lan966x = port->lan966x;130	struct net_device *dev = port->dev;131	u32 lag_id = -1;132	u32 bond_mask;133	int err;134 135	bond_mask = lan966x_lag_get_mask(lan966x, bond);136	if (bond_mask)137		lag_id = __ffs(bond_mask);138 139	port->bond = bond;140	lan966x_lag_update_ids(lan966x);141 142	err = switchdev_bridge_port_offload(brport_dev, dev, port,143					    &lan966x_switchdev_nb,144					    &lan966x_switchdev_blocking_nb,145					    false, extack);146	if (err)147		goto out;148 149	lan966x_port_stp_state_set(port, br_port_get_stp_state(brport_dev));150 151	if (lan966x_lag_first_port(port->bond, port->dev) &&152	    lag_id != -1)153		lan966x_mac_lag_replace_port_entry(lan966x,154						   lan966x->ports[lag_id],155						   port);156 157	return 0;158 159out:160	port->bond = NULL;161	lan966x_lag_update_ids(lan966x);162 163	return err;164}165 166void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond)167{168	struct lan966x *lan966x = port->lan966x;169	u32 bond_mask;170	u32 lag_id;171 172	if (lan966x_lag_first_port(port->bond, port->dev)) {173		bond_mask = lan966x_lag_get_mask(lan966x, port->bond);174		bond_mask &= ~BIT(port->chip_port);175		if (bond_mask) {176			lag_id = __ffs(bond_mask);177			lan966x_mac_lag_replace_port_entry(lan966x, port,178							   lan966x->ports[lag_id]);179		} else {180			lan966x_mac_lag_remove_port_entry(lan966x, port);181		}182	}183 184	port->bond = NULL;185	lan966x_lag_update_ids(lan966x);186	lan966x_port_stp_state_set(port, BR_STATE_FORWARDING);187}188 189static bool lan966x_lag_port_check_hash_types(struct lan966x *lan966x,190					      enum netdev_lag_hash hash_type)191{192	int p;193 194	for (p = 0; p < lan966x->num_phys_ports; ++p) {195		struct lan966x_port *port = lan966x->ports[p];196 197		if (!port || !port->bond)198			continue;199 200		if (port->hash_type != hash_type)201			return false;202	}203 204	return true;205}206 207int lan966x_lag_port_prechangeupper(struct net_device *dev,208				    struct netdev_notifier_changeupper_info *info)209{210	struct lan966x_port *port = netdev_priv(dev);211	struct lan966x *lan966x = port->lan966x;212	struct netdev_lag_upper_info *lui;213	struct netlink_ext_ack *extack;214 215	extack = netdev_notifier_info_to_extack(&info->info);216	lui = info->upper_info;217	if (!lui) {218		port->hash_type = NETDEV_LAG_HASH_NONE;219		return NOTIFY_DONE;220	}221 222	if (lui->tx_type != NETDEV_LAG_TX_TYPE_HASH) {223		NL_SET_ERR_MSG_MOD(extack,224				   "LAG device using unsupported Tx type");225		return -EINVAL;226	}227 228	if (!lan966x_lag_port_check_hash_types(lan966x, lui->hash_type)) {229		NL_SET_ERR_MSG_MOD(extack,230				   "LAG devices can have only the same hash_type");231		return -EINVAL;232	}233 234	switch (lui->hash_type) {235	case NETDEV_LAG_HASH_L2:236		lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) |237		       ANA_AGGR_CFG_AC_SMAC_ENA_SET(1),238		       lan966x, ANA_AGGR_CFG);239		break;240	case NETDEV_LAG_HASH_L34:241		lan_wr(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) |242		       ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1) |243		       ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(1),244		       lan966x, ANA_AGGR_CFG);245		break;246	case NETDEV_LAG_HASH_L23:247		lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) |248		       ANA_AGGR_CFG_AC_SMAC_ENA_SET(1) |249		       ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) |250		       ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1),251		       lan966x, ANA_AGGR_CFG);252		break;253	default:254		NL_SET_ERR_MSG_MOD(extack,255				   "LAG device using unsupported hash type");256		return -EINVAL;257	}258 259	port->hash_type = lui->hash_type;260 261	return NOTIFY_OK;262}263 264int lan966x_lag_port_changelowerstate(struct net_device *dev,265				      struct netdev_notifier_changelowerstate_info *info)266{267	struct netdev_lag_lower_state_info *lag = info->lower_state_info;268	struct lan966x_port *port = netdev_priv(dev);269	struct lan966x *lan966x = port->lan966x;270	bool is_active;271 272	if (!port->bond)273		return NOTIFY_DONE;274 275	is_active = lag->link_up && lag->tx_enabled;276	if (port->lag_tx_active == is_active)277		return NOTIFY_DONE;278 279	port->lag_tx_active = is_active;280	lan966x_lag_set_aggr_pgids(lan966x);281 282	return NOTIFY_OK;283}284 285int lan966x_lag_netdev_prechangeupper(struct net_device *dev,286				      struct netdev_notifier_changeupper_info *info)287{288	struct lan966x_port *port;289	struct net_device *lower;290	struct list_head *iter;291	int err;292 293	netdev_for_each_lower_dev(dev, lower, iter) {294		if (!lan966x_netdevice_check(lower))295			continue;296 297		port = netdev_priv(lower);298		if (port->bond != dev)299			continue;300 301		err = lan966x_port_prechangeupper(lower, dev, info);302		if (err)303			return err;304	}305 306	return NOTIFY_DONE;307}308 309int lan966x_lag_netdev_changeupper(struct net_device *dev,310				   struct netdev_notifier_changeupper_info *info)311{312	struct lan966x_port *port;313	struct net_device *lower;314	struct list_head *iter;315	int err;316 317	netdev_for_each_lower_dev(dev, lower, iter) {318		if (!lan966x_netdevice_check(lower))319			continue;320 321		port = netdev_priv(lower);322		if (port->bond != dev)323			continue;324 325		err = lan966x_port_changeupper(lower, dev, info);326		if (err)327			return err;328	}329 330	return NOTIFY_DONE;331}332 333bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev)334{335	struct lan966x_port *port = netdev_priv(dev);336	struct lan966x *lan966x = port->lan966x;337	unsigned long bond_mask;338 339	if (port->bond != lag)340		return false;341 342	bond_mask = lan966x_lag_get_mask(lan966x, lag);343	if (bond_mask && port->chip_port == __ffs(bond_mask))344		return true;345 346	return false;347}348 349u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond)350{351	struct lan966x_port *port;352	u32 mask = 0;353	int p;354 355	if (!bond)356		return mask;357 358	for (p = 0; p < lan966x->num_phys_ports; p++) {359		port = lan966x->ports[p];360		if (!port)361			continue;362 363		if (port->bond == bond)364			mask |= BIT(p);365	}366 367	return mask;368}369