Loading drivers/net/ethernet/microchip/lan966x/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ config LAN966X_SWITCH depends on HAS_IOMEM depends on OF depends on NET_SWITCHDEV depends on BRIDGE || BRIDGE=n select PHYLINK select PACKING help Loading drivers/net/ethernet/microchip/lan966x/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -8,4 +8,4 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \ lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o \ lan966x_ptp.o lan966x_fdma.o lan966x_ptp.o lan966x_fdma.o lan966x_lag.o drivers/net/ethernet/microchip/lan966x/lan966x_lag.c 0 → 100644 +337 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0+ #include <linux/if_bridge.h> #include "lan966x_main.h" static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x) { u32 visited = GENMASK(lan966x->num_phys_ports - 1, 0); int p, lag, i; /* Reset destination and aggregation PGIDS */ for (p = 0; p < lan966x->num_phys_ports; ++p) lan_wr(ANA_PGID_PGID_SET(BIT(p)), lan966x, ANA_PGID(p)); for (p = PGID_AGGR; p < PGID_SRC; ++p) lan_wr(ANA_PGID_PGID_SET(visited), lan966x, ANA_PGID(p)); /* The visited ports bitmask holds the list of ports offloading any * bonding interface. Initially we mark all these ports as unvisited, * then every time we visit a port in this bitmask, we know that it is * the lowest numbered port, i.e. the one whose logical ID == physical * port ID == LAG ID. So we mark as visited all further ports in the * bitmask that are offloading the same bonding interface. This way, * we set up the aggregation PGIDs only once per bonding interface. */ for (p = 0; p < lan966x->num_phys_ports; ++p) { struct lan966x_port *port = lan966x->ports[p]; if (!port || !port->bond) continue; visited &= ~BIT(p); } /* Now, set PGIDs for each active LAG */ for (lag = 0; lag < lan966x->num_phys_ports; ++lag) { struct net_device *bond = lan966x->ports[lag]->bond; int num_active_ports = 0; unsigned long bond_mask; u8 aggr_idx[16]; if (!bond || (visited & BIT(lag))) continue; bond_mask = lan966x_lag_get_mask(lan966x, bond); for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) { struct lan966x_port *port = lan966x->ports[p]; lan_wr(ANA_PGID_PGID_SET(bond_mask), lan966x, ANA_PGID(p)); if (port->lag_tx_active) aggr_idx[num_active_ports++] = p; } for (i = PGID_AGGR; i < PGID_SRC; ++i) { u32 ac; ac = lan_rd(lan966x, ANA_PGID(i)); ac &= ~bond_mask; /* Don't do division by zero if there was no active * port. Just make all aggregation codes zero. */ if (num_active_ports) ac |= BIT(aggr_idx[i % num_active_ports]); lan_wr(ANA_PGID_PGID_SET(ac), lan966x, ANA_PGID(i)); } /* Mark all ports in the same LAG as visited to avoid applying * the same config again. */ for (p = lag; p < lan966x->num_phys_ports; p++) { struct lan966x_port *port = lan966x->ports[p]; if (!port) continue; if (port->bond == bond) visited |= BIT(p); } } } static void lan966x_lag_set_port_ids(struct lan966x *lan966x) { struct lan966x_port *port; u32 bond_mask; u32 lag_id; int p; for (p = 0; p < lan966x->num_phys_ports; ++p) { port = lan966x->ports[p]; if (!port) continue; lag_id = port->chip_port; bond_mask = lan966x_lag_get_mask(lan966x, port->bond); if (bond_mask) lag_id = __ffs(bond_mask); lan_rmw(ANA_PORT_CFG_PORTID_VAL_SET(lag_id), ANA_PORT_CFG_PORTID_VAL, lan966x, ANA_PORT_CFG(port->chip_port)); } } static void lan966x_lag_update_ids(struct lan966x *lan966x) { lan966x_lag_set_port_ids(lan966x); lan966x_update_fwd_mask(lan966x); lan966x_lag_set_aggr_pgids(lan966x); } int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bond, struct netlink_ext_ack *extack) { struct lan966x *lan966x = port->lan966x; struct net_device *dev = port->dev; int err; port->bond = bond; lan966x_lag_update_ids(lan966x); err = switchdev_bridge_port_offload(brport_dev, dev, port, &lan966x_switchdev_nb, &lan966x_switchdev_blocking_nb, false, extack); if (err) goto out; lan966x_port_stp_state_set(port, br_port_get_stp_state(brport_dev)); return 0; out: port->bond = NULL; lan966x_lag_update_ids(lan966x); return err; } void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond) { struct lan966x *lan966x = port->lan966x; port->bond = NULL; lan966x_lag_update_ids(lan966x); lan966x_port_stp_state_set(port, BR_STATE_FORWARDING); } static bool lan966x_lag_port_check_hash_types(struct lan966x *lan966x, enum netdev_lag_hash hash_type) { int p; for (p = 0; p < lan966x->num_phys_ports; ++p) { struct lan966x_port *port = lan966x->ports[p]; if (!port || !port->bond) continue; if (port->hash_type != hash_type) return false; } return true; } int lan966x_lag_port_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; struct netdev_lag_upper_info *lui; struct netlink_ext_ack *extack; extack = netdev_notifier_info_to_extack(&info->info); lui = info->upper_info; if (!lui) { port->hash_type = NETDEV_LAG_HASH_NONE; return NOTIFY_DONE; } if (lui->tx_type != NETDEV_LAG_TX_TYPE_HASH) { NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); return -EINVAL; } if (!lan966x_lag_port_check_hash_types(lan966x, lui->hash_type)) { NL_SET_ERR_MSG_MOD(extack, "LAG devices can have only the same hash_type"); return -EINVAL; } switch (lui->hash_type) { case NETDEV_LAG_HASH_L2: lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; case NETDEV_LAG_HASH_L34: lan_wr(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; case NETDEV_LAG_HASH_L23: lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; default: NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported hash type"); return -EINVAL; } port->hash_type = lui->hash_type; return NOTIFY_OK; } int lan966x_lag_port_changelowerstate(struct net_device *dev, struct netdev_notifier_changelowerstate_info *info) { struct netdev_lag_lower_state_info *lag = info->lower_state_info; struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; bool is_active; if (!port->bond) return NOTIFY_DONE; is_active = lag->link_up && lag->tx_enabled; if (port->lag_tx_active == is_active) return NOTIFY_DONE; port->lag_tx_active = is_active; lan966x_lag_set_aggr_pgids(lan966x); return NOTIFY_OK; } int lan966x_lag_netdev_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port; struct net_device *lower; struct list_head *iter; int err; netdev_for_each_lower_dev(dev, lower, iter) { if (!lan966x_netdevice_check(lower)) continue; port = netdev_priv(lower); if (port->bond != dev) continue; err = lan966x_port_prechangeupper(lower, dev, info); if (err) return err; } return NOTIFY_DONE; } int lan966x_lag_netdev_changeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port; struct net_device *lower; struct list_head *iter; int err; netdev_for_each_lower_dev(dev, lower, iter) { if (!lan966x_netdevice_check(lower)) continue; port = netdev_priv(lower); if (port->bond != dev) continue; err = lan966x_port_changeupper(lower, dev, info); if (err) return err; } return NOTIFY_DONE; } bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; unsigned long bond_mask; if (port->bond != lag) return false; bond_mask = lan966x_lag_get_mask(lan966x, lag); if (bond_mask && port->chip_port == __ffs(bond_mask)) return true; return false; } u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond) { struct lan966x_port *port; u32 mask = 0; int p; if (!bond) return mask; for (p = 0; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; if (!port) continue; if (port->bond == bond) mask |= BIT(p); } return mask; } drivers/net/ethernet/microchip/lan966x/lan966x_main.h +31 −0 Original line number Diff line number Diff line Loading @@ -292,6 +292,10 @@ struct lan966x_port { u8 ptp_cmd; u16 ts_id; struct sk_buff_head tx_skbs; struct net_device *bond; bool lag_tx_active; enum netdev_lag_hash hash_type; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; Loading Loading @@ -409,6 +413,33 @@ int lan966x_fdma_init(struct lan966x *lan966x); void lan966x_fdma_deinit(struct lan966x *lan966x); irqreturn_t lan966x_fdma_irq_handler(int irq, void *args); int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bond, struct netlink_ext_ack *extack); void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond); int lan966x_lag_port_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); int lan966x_lag_port_changelowerstate(struct net_device *dev, struct netdev_notifier_changelowerstate_info *info); int lan966x_lag_netdev_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); int lan966x_lag_netdev_changeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev); u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond); int lan966x_port_changeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info); int lan966x_port_prechangeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info); void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state); void lan966x_port_ageing_set(struct lan966x_port *port, unsigned long ageing_clock_t); void lan966x_update_fwd_mask(struct lan966x *lan966x); static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, Loading drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c +73 −19 Original line number Diff line number Diff line Loading @@ -130,7 +130,7 @@ static int lan966x_port_pre_bridge_flags(struct lan966x_port *port, return 0; } static void lan966x_update_fwd_mask(struct lan966x *lan966x) void lan966x_update_fwd_mask(struct lan966x *lan966x) { int i; Loading @@ -138,9 +138,14 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) struct lan966x_port *port = lan966x->ports[i]; unsigned long mask = 0; if (port && lan966x->bridge_fwd_mask & BIT(i)) if (port && lan966x->bridge_fwd_mask & BIT(i)) { mask = lan966x->bridge_fwd_mask & ~BIT(i); if (port->bond) mask &= ~lan966x_lag_get_mask(lan966x, port->bond); } mask |= BIT(CPU_PORT); lan_wr(ANA_PGID_PGID_SET(mask), Loading @@ -148,7 +153,7 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) } } static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) { struct lan966x *lan966x = port->lan966x; bool learn_ena = false; Loading @@ -169,7 +174,7 @@ static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) lan966x_update_fwd_mask(lan966x); } static void lan966x_port_ageing_set(struct lan966x_port *port, void lan966x_port_ageing_set(struct lan966x_port *port, unsigned long ageing_clock_t) { unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); Loading Loading @@ -239,6 +244,7 @@ static int lan966x_port_attr_set(struct net_device *dev, const void *ctx, } static int lan966x_port_bridge_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bridge, struct netlink_ext_ack *extack) { Loading @@ -256,7 +262,7 @@ static int lan966x_port_bridge_join(struct lan966x_port *port, } } err = switchdev_bridge_port_offload(dev, dev, port, err = switchdev_bridge_port_offload(brport_dev, dev, port, &lan966x_switchdev_nb, &lan966x_switchdev_blocking_nb, false, extack); Loading Loading @@ -293,7 +299,8 @@ static void lan966x_port_bridge_leave(struct lan966x_port *port, lan966x_vlan_port_apply(port); } static int lan966x_port_changeupper(struct net_device *dev, int lan966x_port_changeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); Loading @@ -304,26 +311,46 @@ static int lan966x_port_changeupper(struct net_device *dev, if (netif_is_bridge_master(info->upper_dev)) { if (info->linking) err = lan966x_port_bridge_join(port, info->upper_dev, err = lan966x_port_bridge_join(port, brport_dev, info->upper_dev, extack); else lan966x_port_bridge_leave(port, info->upper_dev); } if (netif_is_lag_master(info->upper_dev)) { if (info->linking) err = lan966x_lag_port_join(port, info->upper_dev, info->upper_dev, extack); else lan966x_lag_port_leave(port, info->upper_dev); } return err; } static int lan966x_port_prechangeupper(struct net_device *dev, int lan966x_port_prechangeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); int err = NOTIFY_DONE; if (netif_is_bridge_master(info->upper_dev) && !info->linking) { switchdev_bridge_port_unoffload(port->dev, port, NULL, NULL); lan966x_fdb_flush_workqueue(port->lan966x); } return NOTIFY_DONE; if (netif_is_lag_master(info->upper_dev)) { err = lan966x_lag_port_prechangeupper(dev, info); if (err || info->linking) return err; switchdev_bridge_port_unoffload(brport_dev, port, NULL, NULL); } return err; } static int lan966x_foreign_bridging_check(struct net_device *upper, Loading Loading @@ -401,21 +428,44 @@ static int lan966x_netdevice_port_event(struct net_device *dev, int err = 0; if (!lan966x_netdevice_check(dev)) { switch (event) { case NETDEV_CHANGEUPPER: case NETDEV_PRECHANGEUPPER: err = lan966x_bridge_check(dev, ptr); if (err) return err; if (netif_is_lag_master(dev)) { if (event == NETDEV_CHANGEUPPER) return lan966x_bridge_check(dev, ptr); err = lan966x_lag_netdev_changeupper(dev, ptr); else err = lan966x_lag_netdev_prechangeupper(dev, ptr); return err; } break; default: return 0; } return 0; } switch (event) { case NETDEV_PRECHANGEUPPER: err = lan966x_port_prechangeupper(dev, ptr); err = lan966x_port_prechangeupper(dev, dev, ptr); break; case NETDEV_CHANGEUPPER: err = lan966x_bridge_check(dev, ptr); if (err) return err; err = lan966x_port_changeupper(dev, ptr); err = lan966x_port_changeupper(dev, dev, ptr); break; case NETDEV_CHANGELOWERSTATE: err = lan966x_lag_port_changelowerstate(dev, ptr); break; } Loading @@ -433,19 +483,23 @@ static int lan966x_netdevice_event(struct notifier_block *nb, return notifier_from_errno(ret); } /* We don't offload uppers such as LAG as bridge ports, so every device except * the bridge itself is foreign. */ static bool lan966x_foreign_dev_check(const struct net_device *dev, const struct net_device *foreign_dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; int i; if (netif_is_bridge_master(foreign_dev)) if (lan966x->bridge == foreign_dev) return false; if (netif_is_lag_master(foreign_dev)) for (i = 0; i < lan966x->num_phys_ports; ++i) if (lan966x->ports[i] && lan966x->ports[i]->bond == foreign_dev) return false; return true; } Loading Loading
drivers/net/ethernet/microchip/lan966x/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ config LAN966X_SWITCH depends on HAS_IOMEM depends on OF depends on NET_SWITCHDEV depends on BRIDGE || BRIDGE=n select PHYLINK select PACKING help Loading
drivers/net/ethernet/microchip/lan966x/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -8,4 +8,4 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \ lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o \ lan966x_ptp.o lan966x_fdma.o lan966x_ptp.o lan966x_fdma.o lan966x_lag.o
drivers/net/ethernet/microchip/lan966x/lan966x_lag.c 0 → 100644 +337 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0+ #include <linux/if_bridge.h> #include "lan966x_main.h" static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x) { u32 visited = GENMASK(lan966x->num_phys_ports - 1, 0); int p, lag, i; /* Reset destination and aggregation PGIDS */ for (p = 0; p < lan966x->num_phys_ports; ++p) lan_wr(ANA_PGID_PGID_SET(BIT(p)), lan966x, ANA_PGID(p)); for (p = PGID_AGGR; p < PGID_SRC; ++p) lan_wr(ANA_PGID_PGID_SET(visited), lan966x, ANA_PGID(p)); /* The visited ports bitmask holds the list of ports offloading any * bonding interface. Initially we mark all these ports as unvisited, * then every time we visit a port in this bitmask, we know that it is * the lowest numbered port, i.e. the one whose logical ID == physical * port ID == LAG ID. So we mark as visited all further ports in the * bitmask that are offloading the same bonding interface. This way, * we set up the aggregation PGIDs only once per bonding interface. */ for (p = 0; p < lan966x->num_phys_ports; ++p) { struct lan966x_port *port = lan966x->ports[p]; if (!port || !port->bond) continue; visited &= ~BIT(p); } /* Now, set PGIDs for each active LAG */ for (lag = 0; lag < lan966x->num_phys_ports; ++lag) { struct net_device *bond = lan966x->ports[lag]->bond; int num_active_ports = 0; unsigned long bond_mask; u8 aggr_idx[16]; if (!bond || (visited & BIT(lag))) continue; bond_mask = lan966x_lag_get_mask(lan966x, bond); for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) { struct lan966x_port *port = lan966x->ports[p]; lan_wr(ANA_PGID_PGID_SET(bond_mask), lan966x, ANA_PGID(p)); if (port->lag_tx_active) aggr_idx[num_active_ports++] = p; } for (i = PGID_AGGR; i < PGID_SRC; ++i) { u32 ac; ac = lan_rd(lan966x, ANA_PGID(i)); ac &= ~bond_mask; /* Don't do division by zero if there was no active * port. Just make all aggregation codes zero. */ if (num_active_ports) ac |= BIT(aggr_idx[i % num_active_ports]); lan_wr(ANA_PGID_PGID_SET(ac), lan966x, ANA_PGID(i)); } /* Mark all ports in the same LAG as visited to avoid applying * the same config again. */ for (p = lag; p < lan966x->num_phys_ports; p++) { struct lan966x_port *port = lan966x->ports[p]; if (!port) continue; if (port->bond == bond) visited |= BIT(p); } } } static void lan966x_lag_set_port_ids(struct lan966x *lan966x) { struct lan966x_port *port; u32 bond_mask; u32 lag_id; int p; for (p = 0; p < lan966x->num_phys_ports; ++p) { port = lan966x->ports[p]; if (!port) continue; lag_id = port->chip_port; bond_mask = lan966x_lag_get_mask(lan966x, port->bond); if (bond_mask) lag_id = __ffs(bond_mask); lan_rmw(ANA_PORT_CFG_PORTID_VAL_SET(lag_id), ANA_PORT_CFG_PORTID_VAL, lan966x, ANA_PORT_CFG(port->chip_port)); } } static void lan966x_lag_update_ids(struct lan966x *lan966x) { lan966x_lag_set_port_ids(lan966x); lan966x_update_fwd_mask(lan966x); lan966x_lag_set_aggr_pgids(lan966x); } int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bond, struct netlink_ext_ack *extack) { struct lan966x *lan966x = port->lan966x; struct net_device *dev = port->dev; int err; port->bond = bond; lan966x_lag_update_ids(lan966x); err = switchdev_bridge_port_offload(brport_dev, dev, port, &lan966x_switchdev_nb, &lan966x_switchdev_blocking_nb, false, extack); if (err) goto out; lan966x_port_stp_state_set(port, br_port_get_stp_state(brport_dev)); return 0; out: port->bond = NULL; lan966x_lag_update_ids(lan966x); return err; } void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond) { struct lan966x *lan966x = port->lan966x; port->bond = NULL; lan966x_lag_update_ids(lan966x); lan966x_port_stp_state_set(port, BR_STATE_FORWARDING); } static bool lan966x_lag_port_check_hash_types(struct lan966x *lan966x, enum netdev_lag_hash hash_type) { int p; for (p = 0; p < lan966x->num_phys_ports; ++p) { struct lan966x_port *port = lan966x->ports[p]; if (!port || !port->bond) continue; if (port->hash_type != hash_type) return false; } return true; } int lan966x_lag_port_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; struct netdev_lag_upper_info *lui; struct netlink_ext_ack *extack; extack = netdev_notifier_info_to_extack(&info->info); lui = info->upper_info; if (!lui) { port->hash_type = NETDEV_LAG_HASH_NONE; return NOTIFY_DONE; } if (lui->tx_type != NETDEV_LAG_TX_TYPE_HASH) { NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); return -EINVAL; } if (!lan966x_lag_port_check_hash_types(lan966x, lui->hash_type)) { NL_SET_ERR_MSG_MOD(extack, "LAG devices can have only the same hash_type"); return -EINVAL; } switch (lui->hash_type) { case NETDEV_LAG_HASH_L2: lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; case NETDEV_LAG_HASH_L34: lan_wr(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; case NETDEV_LAG_HASH_L23: lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1) | ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1), lan966x, ANA_AGGR_CFG); break; default: NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported hash type"); return -EINVAL; } port->hash_type = lui->hash_type; return NOTIFY_OK; } int lan966x_lag_port_changelowerstate(struct net_device *dev, struct netdev_notifier_changelowerstate_info *info) { struct netdev_lag_lower_state_info *lag = info->lower_state_info; struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; bool is_active; if (!port->bond) return NOTIFY_DONE; is_active = lag->link_up && lag->tx_enabled; if (port->lag_tx_active == is_active) return NOTIFY_DONE; port->lag_tx_active = is_active; lan966x_lag_set_aggr_pgids(lan966x); return NOTIFY_OK; } int lan966x_lag_netdev_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port; struct net_device *lower; struct list_head *iter; int err; netdev_for_each_lower_dev(dev, lower, iter) { if (!lan966x_netdevice_check(lower)) continue; port = netdev_priv(lower); if (port->bond != dev) continue; err = lan966x_port_prechangeupper(lower, dev, info); if (err) return err; } return NOTIFY_DONE; } int lan966x_lag_netdev_changeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port; struct net_device *lower; struct list_head *iter; int err; netdev_for_each_lower_dev(dev, lower, iter) { if (!lan966x_netdevice_check(lower)) continue; port = netdev_priv(lower); if (port->bond != dev) continue; err = lan966x_port_changeupper(lower, dev, info); if (err) return err; } return NOTIFY_DONE; } bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; unsigned long bond_mask; if (port->bond != lag) return false; bond_mask = lan966x_lag_get_mask(lan966x, lag); if (bond_mask && port->chip_port == __ffs(bond_mask)) return true; return false; } u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond) { struct lan966x_port *port; u32 mask = 0; int p; if (!bond) return mask; for (p = 0; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; if (!port) continue; if (port->bond == bond) mask |= BIT(p); } return mask; }
drivers/net/ethernet/microchip/lan966x/lan966x_main.h +31 −0 Original line number Diff line number Diff line Loading @@ -292,6 +292,10 @@ struct lan966x_port { u8 ptp_cmd; u16 ts_id; struct sk_buff_head tx_skbs; struct net_device *bond; bool lag_tx_active; enum netdev_lag_hash hash_type; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; Loading Loading @@ -409,6 +413,33 @@ int lan966x_fdma_init(struct lan966x *lan966x); void lan966x_fdma_deinit(struct lan966x *lan966x); irqreturn_t lan966x_fdma_irq_handler(int irq, void *args); int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bond, struct netlink_ext_ack *extack); void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond); int lan966x_lag_port_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); int lan966x_lag_port_changelowerstate(struct net_device *dev, struct netdev_notifier_changelowerstate_info *info); int lan966x_lag_netdev_prechangeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); int lan966x_lag_netdev_changeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info); bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev); u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond); int lan966x_port_changeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info); int lan966x_port_prechangeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info); void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state); void lan966x_port_ageing_set(struct lan966x_port *port, unsigned long ageing_clock_t); void lan966x_update_fwd_mask(struct lan966x *lan966x); static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, Loading
drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c +73 −19 Original line number Diff line number Diff line Loading @@ -130,7 +130,7 @@ static int lan966x_port_pre_bridge_flags(struct lan966x_port *port, return 0; } static void lan966x_update_fwd_mask(struct lan966x *lan966x) void lan966x_update_fwd_mask(struct lan966x *lan966x) { int i; Loading @@ -138,9 +138,14 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) struct lan966x_port *port = lan966x->ports[i]; unsigned long mask = 0; if (port && lan966x->bridge_fwd_mask & BIT(i)) if (port && lan966x->bridge_fwd_mask & BIT(i)) { mask = lan966x->bridge_fwd_mask & ~BIT(i); if (port->bond) mask &= ~lan966x_lag_get_mask(lan966x, port->bond); } mask |= BIT(CPU_PORT); lan_wr(ANA_PGID_PGID_SET(mask), Loading @@ -148,7 +153,7 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) } } static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) { struct lan966x *lan966x = port->lan966x; bool learn_ena = false; Loading @@ -169,7 +174,7 @@ static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) lan966x_update_fwd_mask(lan966x); } static void lan966x_port_ageing_set(struct lan966x_port *port, void lan966x_port_ageing_set(struct lan966x_port *port, unsigned long ageing_clock_t) { unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); Loading Loading @@ -239,6 +244,7 @@ static int lan966x_port_attr_set(struct net_device *dev, const void *ctx, } static int lan966x_port_bridge_join(struct lan966x_port *port, struct net_device *brport_dev, struct net_device *bridge, struct netlink_ext_ack *extack) { Loading @@ -256,7 +262,7 @@ static int lan966x_port_bridge_join(struct lan966x_port *port, } } err = switchdev_bridge_port_offload(dev, dev, port, err = switchdev_bridge_port_offload(brport_dev, dev, port, &lan966x_switchdev_nb, &lan966x_switchdev_blocking_nb, false, extack); Loading Loading @@ -293,7 +299,8 @@ static void lan966x_port_bridge_leave(struct lan966x_port *port, lan966x_vlan_port_apply(port); } static int lan966x_port_changeupper(struct net_device *dev, int lan966x_port_changeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); Loading @@ -304,26 +311,46 @@ static int lan966x_port_changeupper(struct net_device *dev, if (netif_is_bridge_master(info->upper_dev)) { if (info->linking) err = lan966x_port_bridge_join(port, info->upper_dev, err = lan966x_port_bridge_join(port, brport_dev, info->upper_dev, extack); else lan966x_port_bridge_leave(port, info->upper_dev); } if (netif_is_lag_master(info->upper_dev)) { if (info->linking) err = lan966x_lag_port_join(port, info->upper_dev, info->upper_dev, extack); else lan966x_lag_port_leave(port, info->upper_dev); } return err; } static int lan966x_port_prechangeupper(struct net_device *dev, int lan966x_port_prechangeupper(struct net_device *dev, struct net_device *brport_dev, struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); int err = NOTIFY_DONE; if (netif_is_bridge_master(info->upper_dev) && !info->linking) { switchdev_bridge_port_unoffload(port->dev, port, NULL, NULL); lan966x_fdb_flush_workqueue(port->lan966x); } return NOTIFY_DONE; if (netif_is_lag_master(info->upper_dev)) { err = lan966x_lag_port_prechangeupper(dev, info); if (err || info->linking) return err; switchdev_bridge_port_unoffload(brport_dev, port, NULL, NULL); } return err; } static int lan966x_foreign_bridging_check(struct net_device *upper, Loading Loading @@ -401,21 +428,44 @@ static int lan966x_netdevice_port_event(struct net_device *dev, int err = 0; if (!lan966x_netdevice_check(dev)) { switch (event) { case NETDEV_CHANGEUPPER: case NETDEV_PRECHANGEUPPER: err = lan966x_bridge_check(dev, ptr); if (err) return err; if (netif_is_lag_master(dev)) { if (event == NETDEV_CHANGEUPPER) return lan966x_bridge_check(dev, ptr); err = lan966x_lag_netdev_changeupper(dev, ptr); else err = lan966x_lag_netdev_prechangeupper(dev, ptr); return err; } break; default: return 0; } return 0; } switch (event) { case NETDEV_PRECHANGEUPPER: err = lan966x_port_prechangeupper(dev, ptr); err = lan966x_port_prechangeupper(dev, dev, ptr); break; case NETDEV_CHANGEUPPER: err = lan966x_bridge_check(dev, ptr); if (err) return err; err = lan966x_port_changeupper(dev, ptr); err = lan966x_port_changeupper(dev, dev, ptr); break; case NETDEV_CHANGELOWERSTATE: err = lan966x_lag_port_changelowerstate(dev, ptr); break; } Loading @@ -433,19 +483,23 @@ static int lan966x_netdevice_event(struct notifier_block *nb, return notifier_from_errno(ret); } /* We don't offload uppers such as LAG as bridge ports, so every device except * the bridge itself is foreign. */ static bool lan966x_foreign_dev_check(const struct net_device *dev, const struct net_device *foreign_dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; int i; if (netif_is_bridge_master(foreign_dev)) if (lan966x->bridge == foreign_dev) return false; if (netif_is_lag_master(foreign_dev)) for (i = 0; i < lan966x->num_phys_ports; ++i) if (lan966x->ports[i] && lan966x->ports[i]->bond == foreign_dev) return false; return true; } Loading