diff --git a/backports/0008-realtek-update-to-latest-owrt-HEAD.patch b/backports/0008-realtek-update-to-latest-owrt-HEAD.patch index ad72a911a..26564557c 100644 --- a/backports/0008-realtek-update-to-latest-owrt-HEAD.patch +++ b/backports/0008-realtek-update-to-latest-owrt-HEAD.patch @@ -1,13 +1,11 @@ -From cb6bb0f9e8f398f33b60f7dde1733e6e188f6e50 Mon Sep 17 00:00:00 2001 +From c6ab9a9f4858c2f6b2a77ab7559bd422ed2eaee8 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 16 Mar 2021 10:46:51 +0100 -Subject: [PATCH 01/53] realtek: update to latest owrt HEAD +Subject: [PATCH 01/54] realtek: update to latest owrt HEAD Signed-off-by: John Crispin --- package/boot/uboot-envtools/files/realtek | 7 + - .../generic/backport-5.4/870-dsa-lag.patch | 3123 +++++++++++++++++ - .../backport-5.4/871-brflood-api.patch | 817 +++++ .../realtek/base-files/etc/board.d/01_leds | 1 - .../realtek/base-files/etc/board.d/02_network | 12 +- target/linux/realtek/config-5.4 | 33 +- @@ -42,9 +40,9 @@ Signed-off-by: John Crispin .../realtek/patches-5.4/710-adt7470.patch | 20 + .../realtek/patches-5.4/711-ec4100.patch | 150 + .../linux/realtek/patches-5.4/712-fixes.patch | 23 + + .../realtek/patches-5.4/870-dsa-lag.patch | 3123 +++++++++++++++++ + .../realtek/patches-5.4/871-brflood-api.patch | 817 +++++ 37 files changed, 14558 insertions(+), 1413 deletions(-) - create mode 100644 target/linux/generic/backport-5.4/870-dsa-lag.patch - create mode 100644 target/linux/generic/backport-5.4/871-brflood-api.patch create mode 100644 target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts create mode 100644 target/linux/realtek/dts/rtl839x.dtsi delete mode 100644 target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h @@ -59,6 +57,8 @@ Signed-off-by: John Crispin create mode 100644 target/linux/realtek/patches-5.4/710-adt7470.patch create mode 100644 target/linux/realtek/patches-5.4/711-ec4100.patch create mode 100644 target/linux/realtek/patches-5.4/712-fixes.patch + create mode 100644 target/linux/realtek/patches-5.4/870-dsa-lag.patch + create mode 100644 target/linux/realtek/patches-5.4/871-brflood-api.patch diff --git a/package/boot/uboot-envtools/files/realtek b/package/boot/uboot-envtools/files/realtek index cce0628ffc..a4b7089d62 100644 @@ -83,3958 +83,6 @@ index cce0628ffc..a4b7089d62 100644 *) idx="$(find_mtd_index u-boot-env)" [ -n "$idx" ] && \ -diff --git a/target/linux/generic/backport-5.4/870-dsa-lag.patch b/target/linux/generic/backport-5.4/870-dsa-lag.patch -new file mode 100644 -index 0000000000..3d1992e4cb ---- /dev/null -+++ b/target/linux/generic/backport-5.4/870-dsa-lag.patch -@@ -0,0 +1,3123 @@ -+diff -urpN linux-5.4.137.old/drivers/net/bonding/bond_main.c linux-5.4.137/drivers/net/bonding/bond_main.c -+--- linux-5.4.137.old/drivers/net/bonding/bond_main.c 2021-08-04 14:05:38.055697349 +0700 -++++ linux-5.4.137/drivers/net/bonding/bond_main.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -1753,6 +1753,8 @@ int bond_enslave(struct net_device *bond -+ goto err_unregister; -+ } -+ -++ bond_lower_state_changed(new_slave); -++ -+ res = bond_sysfs_slave_add(new_slave); -+ if (res) { -+ slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res); -+diff -urpN linux-5.4.137.old/drivers/net/dsa/b53/b53_common.c linux-5.4.137/drivers/net/dsa/b53/b53_common.c -+--- linux-5.4.137.old/drivers/net/dsa/b53/b53_common.c 2021-08-04 14:05:38.055697349 +0700 -++++ linux-5.4.137/drivers/net/dsa/b53/b53_common.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -537,7 +537,7 @@ int b53_enable_port(struct dsa_switch *d -+ if (!dsa_is_user_port(ds, port)) -+ return 0; -+ -+- cpu_port = ds->ports[port].cpu_dp->index; -++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ -+ b53_br_egress_floods(ds, port, true, true); -+ b53_port_set_learning(dev, port, false); -+@@ -1674,7 +1674,7 @@ EXPORT_SYMBOL(b53_fdb_dump); -+ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) -+ { -+ struct b53_device *dev = ds->priv; -+- s8 cpu_port = ds->ports[port].cpu_dp->index; -++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ u16 pvlan, reg; -+ unsigned int i; -+ -+@@ -1722,7 +1722,7 @@ void b53_br_leave(struct dsa_switch *ds, -+ { -+ struct b53_device *dev = ds->priv; -+ struct b53_vlan *vl = &dev->vlans[0]; -+- s8 cpu_port = ds->ports[port].cpu_dp->index; -++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ unsigned int i; -+ u16 pvlan, reg, pvid; -+ -+@@ -2396,10 +2396,13 @@ struct b53_device *b53_switch_alloc(stru -+ struct dsa_switch *ds; -+ struct b53_device *dev; -+ -+- ds = dsa_switch_alloc(base, DSA_MAX_PORTS); -++ ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return NULL; -+ -++ ds->dev = base; -++ ds->num_ports = DSA_MAX_PORTS; -++ -+ dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); -+ if (!dev) -+ return NULL; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/bcm_sf2.c linux-5.4.137/drivers/net/dsa/bcm_sf2.c -+--- linux-5.4.137.old/drivers/net/dsa/bcm_sf2.c 2021-08-04 14:05:38.055697349 +0700 -++++ linux-5.4.137/drivers/net/dsa/bcm_sf2.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -670,7 +670,7 @@ static void bcm_sf2_sw_fixed_state(struc -+ * state machine and make it go in PHY_FORCING state instead. -+ */ -+ if (!status->link) -+- netif_carrier_off(ds->ports[port].slave); -++ netif_carrier_off(dsa_to_port(ds, port)->slave); -+ status->duplex = DUPLEX_FULL; -+ } else { -+ status->link = true; -+@@ -736,7 +736,7 @@ static int bcm_sf2_sw_resume(struct dsa_ -+ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, -+ struct ethtool_wolinfo *wol) -+ { -+- struct net_device *p = ds->ports[port].cpu_dp->master; -++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; -+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -+ struct ethtool_wolinfo pwol = { }; -+ -+@@ -760,9 +760,9 @@ static void bcm_sf2_sw_get_wol(struct ds -+ static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, -+ struct ethtool_wolinfo *wol) -+ { -+- struct net_device *p = ds->ports[port].cpu_dp->master; -++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; -+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -+- s8 cpu_port = ds->ports[port].cpu_dp->index; -++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ struct ethtool_wolinfo pwol = { }; -+ -+ if (p->ethtool_ops->get_wol) -+diff -urpN linux-5.4.137.old/drivers/net/dsa/bcm_sf2_cfp.c linux-5.4.137/drivers/net/dsa/bcm_sf2_cfp.c -+--- linux-5.4.137.old/drivers/net/dsa/bcm_sf2_cfp.c 2021-08-04 14:05:38.055697349 +0700 -++++ linux-5.4.137/drivers/net/dsa/bcm_sf2_cfp.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -821,7 +821,7 @@ static int bcm_sf2_cfp_rule_insert(struc -+ struct ethtool_rx_flow_spec *fs) -+ { -+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -+- s8 cpu_port = ds->ports[port].cpu_dp->index; -++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ __u64 ring_cookie = fs->ring_cookie; -+ unsigned int queue_num, port_num; -+ int ret; -+@@ -1046,7 +1046,7 @@ static int bcm_sf2_cfp_rule_get_all(stru -+ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, -+ struct ethtool_rxnfc *nfc, u32 *rule_locs) -+ { -+- struct net_device *p = ds->ports[port].cpu_dp->master; -++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; -+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -+ int ret = 0; -+ -+@@ -1089,7 +1089,7 @@ int bcm_sf2_get_rxnfc(struct dsa_switch -+ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, -+ struct ethtool_rxnfc *nfc) -+ { -+- struct net_device *p = ds->ports[port].cpu_dp->master; -++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; -+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -+ int ret = 0; -+ -+diff -urpN linux-5.4.137.old/drivers/net/dsa/dsa_loop.c linux-5.4.137/drivers/net/dsa/dsa_loop.c -+--- linux-5.4.137.old/drivers/net/dsa/dsa_loop.c 2021-08-04 14:05:38.055697349 +0700 -++++ linux-5.4.137/drivers/net/dsa/dsa_loop.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -286,10 +286,13 @@ static int dsa_loop_drv_probe(struct mdi -+ dev_info(&mdiodev->dev, "%s: 0x%0x\n", -+ pdata->name, pdata->enabled_ports); -+ -+- ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); -++ ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return -ENOMEM; -+ -++ ds->dev = &mdiodev->dev; -++ ds->num_ports = DSA_MAX_PORTS; -++ -+ ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); -+ if (!ps) -+ return -ENOMEM; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/lan9303-core.c linux-5.4.137/drivers/net/dsa/lan9303-core.c -+--- linux-5.4.137.old/drivers/net/dsa/lan9303-core.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/lan9303-core.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -1283,10 +1283,12 @@ static int lan9303_register_switch(struc -+ { -+ int base; -+ -+- chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS); -++ chip->ds = devm_kzalloc(chip->dev, sizeof(*chip->ds), GFP_KERNEL); -+ if (!chip->ds) -+ return -ENOMEM; -+ -++ chip->ds->dev = chip->dev; -++ chip->ds->num_ports = LAN9303_NUM_PORTS; -+ chip->ds->priv = chip; -+ chip->ds->ops = &lan9303_switch_ops; -+ base = chip->phy_addr_base; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/lantiq_gswip.c linux-5.4.137/drivers/net/dsa/lantiq_gswip.c -+--- linux-5.4.137.old/drivers/net/dsa/lantiq_gswip.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/lantiq_gswip.c 2021-08-04 14:05:53.887713713 +0700 -+@@ -2006,10 +2006,12 @@ static int gswip_probe(struct platform_d -+ if (!priv->hw_info) -+ return -EINVAL; -+ -+- priv->ds = dsa_switch_alloc(dev, priv->hw_info->max_ports); -++ priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); -+ if (!priv->ds) -+ return -ENOMEM; -+ -++ priv->ds->dev = dev; -++ priv->ds->num_ports = priv->hw_info->max_ports; -+ priv->ds->priv = priv; -+ priv->ds->ops = &gswip_switch_ops; -+ priv->dev = dev; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/microchip/ksz_common.c linux-5.4.137/drivers/net/dsa/microchip/ksz_common.c -+--- linux-5.4.137.old/drivers/net/dsa/microchip/ksz_common.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/microchip/ksz_common.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -396,10 +396,13 @@ struct ksz_device *ksz_switch_alloc(stru -+ struct dsa_switch *ds; -+ struct ksz_device *swdev; -+ -+- ds = dsa_switch_alloc(base, DSA_MAX_PORTS); -++ ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return NULL; -+ -++ ds->dev = base; -++ ds->num_ports = DSA_MAX_PORTS; -++ -+ swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); -+ if (!swdev) -+ return NULL; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mt7530.c linux-5.4.137/drivers/net/dsa/mt7530.c -+--- linux-5.4.137.old/drivers/net/dsa/mt7530.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mt7530.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -785,7 +785,7 @@ mt7530_port_set_vlan_unaware(struct dsa_ -+ -+ for (i = 0; i < MT7530_NUM_PORTS; i++) { -+ if (dsa_is_user_port(ds, i) && -+- dsa_port_is_vlan_filtering(&ds->ports[i])) { -++ dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { -+ all_user_ports_removed = false; -+ break; -+ } -+@@ -843,7 +843,7 @@ mt7530_port_bridge_leave(struct dsa_swit -+ * other port is still a VLAN-aware port. -+ */ -+ if (dsa_is_user_port(ds, i) && i != port && -+- !dsa_port_is_vlan_filtering(&ds->ports[i])) { -++ !dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { -+ if (dsa_to_port(ds, i)->bridge_dev != bridge) -+ continue; -+ if (priv->ports[i].enable) -+@@ -1219,7 +1219,7 @@ mt7530_setup(struct dsa_switch *ds) -+ * controller also is the container for two GMACs nodes representing -+ * as two netdev instances. -+ */ -+- dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; -++ dn = dsa_to_port(ds, port)->master->dev.of_node->parent; -+ ds->configure_vlan_while_not_filtering = true; -+ -+ if (priv->id == ID_MT7530) { -+@@ -1306,7 +1306,7 @@ mt7530_setup(struct dsa_switch *ds) -+ -+ if (!dsa_is_unused_port(ds, 5)) { -+ priv->p5_intf_sel = P5_INTF_SEL_GMAC5; -+- interface = of_get_phy_mode(ds->ports[5].dn); -++ interface = of_get_phy_mode(dsa_to_port(ds, 5)->dn); -+ } else { -+ /* Scan the ethernet nodes. look for GMAC1, lookup used phy */ -+ for_each_child_of_node(dn, mac_np) { -+@@ -1649,10 +1649,13 @@ mt7530_probe(struct mdio_device *mdiodev -+ if (!priv) -+ return -ENOMEM; -+ -+- priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); -++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); -+ if (!priv->ds) -+ return -ENOMEM; -+ -++ priv->ds->dev = &mdiodev->dev; -++ priv->ds->num_ports = DSA_MAX_PORTS; -++ -+ /* Use medatek,mcm property to distinguish hardware type that would -+ * casues a little bit differences on power-on sequence. -+ */ -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6060.c linux-5.4.137/drivers/net/dsa/mv88e6060.c -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6060.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6060.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -270,10 +270,12 @@ static int mv88e6060_probe(struct mdio_d -+ -+ dev_info(dev, "switch %s detected\n", name); -+ -+- ds = dsa_switch_alloc(dev, MV88E6060_PORTS); -++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return -ENOMEM; -+ -++ ds->dev = dev; -++ ds->num_ports = MV88E6060_PORTS; -+ ds->priv = priv; -+ ds->dev = dev; -+ ds->ops = &mv88e6060_switch_ops; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/chip.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/chip.c -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/chip.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/chip.c 2021-08-04 14:05:57.643717592 +0700 -+@@ -1075,7 +1075,7 @@ static u16 mv88e6xxx_port_vlan(struct mv -+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) -+ return mv88e6xxx_port_mask(chip); -+ -+- br = ds->ports[port].bridge_dev; -++ br = dsa_to_port(ds, port)->bridge_dev; -+ pvlan = 0; -+ -+ /* Frames from user ports can egress any local DSA links and CPU ports, -+@@ -1135,6 +1135,7 @@ static int mv88e6xxx_pri_setup(struct mv -+ -+ static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip) -+ { -++ struct dsa_switch *ds = chip->ds; -+ int target, port; -+ int err; -+ -+@@ -1143,10 +1144,9 @@ static int mv88e6xxx_devmap_setup(struct -+ -+ /* Initialize the routing port to the 32 possible target devices */ -+ for (target = 0; target < 32; target++) { -+- port = 0x1f; -+- if (target < DSA_MAX_SWITCHES) -+- if (chip->ds->rtable[target] != DSA_RTABLE_NONE) -+- port = chip->ds->rtable[target]; -++ port = dsa_routing_port(ds, target); -++ if (port == ds->num_ports) -++ port = 0x1f; -+ -+ err = mv88e6xxx_g2_device_mapping_write(chip, target, port); -+ if (err) -+@@ -1250,14 +1250,30 @@ static int mv88e6xxx_mac_setup(struct mv -+ -+ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) -+ { -++ struct dsa_switch_tree *dst = chip->ds->dst; -++ struct dsa_switch *ds; -++ struct dsa_port *dp; -+ u16 pvlan = 0; -+ -+ if (!mv88e6xxx_has_pvt(chip)) -+ return -EOPNOTSUPP; -+ -+ /* Skip the local source device, which uses in-chip port VLAN */ -+- if (dev != chip->ds->index) -++ if (dev != chip->ds->index) { -+ pvlan = mv88e6xxx_port_vlan(chip, dev, port); -++ ds = dsa_switch_find(dst->index, dev); -++ dp = ds ? dsa_to_port(ds, port) : NULL; -++ if (dp && dp->lag_dev) { -++ /* As the PVT is used to limit flooding of -++ * FORWARD frames, which use the LAG ID as the -++ * source port, we must translate dev/port to -++ * the special "LAG device" in the PVT, using -++ * the LAG ID as the port number. -++ */ -++ dev = MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK; -++ port = dsa_lag_id(dst, dp->lag_dev); -++ } -++ } -+ -+ return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); -+ } -+@@ -1402,7 +1418,7 @@ static int mv88e6xxx_port_check_hw_vlan( -+ if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) -+ continue; -+ -+- if (!ds->ports[i].slave) -++ if (!dsa_to_port(ds, i)->slave) -+ continue; -+ -+ if (vlan.member[i] == -+@@ -1410,7 +1426,7 @@ static int mv88e6xxx_port_check_hw_vlan( -+ continue; -+ -+ if (dsa_to_port(ds, i)->bridge_dev == -+- ds->ports[port].bridge_dev) -++ dsa_to_port(ds, port)->bridge_dev) -+ break; /* same bridge, check next VLAN */ -+ -+ if (!dsa_to_port(ds, i)->bridge_dev) -+@@ -2048,7 +2064,7 @@ static int mv88e6xxx_bridge_map(struct m -+ -+ /* Remap the Port VLAN of each local bridge group member */ -+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { -+- if (chip->ds->ports[port].bridge_dev == br) { -++ if (dsa_to_port(chip->ds, port)->bridge_dev == br) { -+ err = mv88e6xxx_port_vlan_map(chip, port); -+ if (err) -+ return err; -+@@ -2065,7 +2081,7 @@ static int mv88e6xxx_bridge_map(struct m -+ break; -+ -+ for (port = 0; port < ds->num_ports; ++port) { -+- if (ds->ports[port].bridge_dev == br) { -++ if (dsa_to_port(ds, port)->bridge_dev == br) { -+ err = mv88e6xxx_pvt_map(chip, dev, port); -+ if (err) -+ return err; -+@@ -5022,6 +5038,271 @@ static int mv88e6xxx_port_egress_floods( -+ return err; -+ } -+ -++static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, -++ struct net_device *lag, -++ struct netdev_lag_upper_info *info) -++{ -++ struct dsa_port *dp; -++ int id, members = 0; -++ -++ id = dsa_lag_id(ds->dst, lag); -++ if (id < 0 || id >= ds->num_lag_ids) -++ return false; -++ -++ dsa_lag_foreach_port(dp, ds->dst, lag) -++ /* Includes the port joining the LAG */ -++ members++; -++ -++ if (members > 8) -++ return false; -++ -++ /* We could potentially relax this to include active -++ * backup in the future. -++ */ -++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) -++ return false; -++ -++ /* Ideally we would also validate that the hash type matches -++ * the hardware. Alas, this is always set to unknown on team -++ * interfaces. -++ */ -++ return true; -++} -++ -++static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ struct dsa_port *dp; -++ u16 map = 0; -++ int id; -++ -++ id = dsa_lag_id(ds->dst, lag); -++ -++ /* Build the map of all ports to distribute flows destined for -++ * this LAG. This can be either a local user port, or a DSA -++ * port if the LAG port is on a remote chip. -++ */ -++ dsa_lag_foreach_port(dp, ds->dst, lag) -++ map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index)); -++ -++ return mv88e6xxx_g2_trunk_mapping_write(chip, id, map); -++} -++ -++static const u8 mv88e6xxx_lag_mask_table[8][8] = { -++ /* Row number corresponds to the number of active members in a -++ * LAG. Each column states which of the eight hash buckets are -++ * mapped to the column:th port in the LAG. -++ * -++ * Example: In a LAG with three active ports, the second port -++ * ([2][1]) would be selected for traffic mapped to buckets -++ * 3,4,5 (0x38). -++ */ -++ { 0xff, 0, 0, 0, 0, 0, 0, 0 }, -++ { 0x0f, 0xf0, 0, 0, 0, 0, 0, 0 }, -++ { 0x07, 0x38, 0xc0, 0, 0, 0, 0, 0 }, -++ { 0x03, 0x0c, 0x30, 0xc0, 0, 0, 0, 0 }, -++ { 0x03, 0x0c, 0x30, 0x40, 0x80, 0, 0, 0 }, -++ { 0x03, 0x0c, 0x10, 0x20, 0x40, 0x80, 0, 0 }, -++ { 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0 }, -++ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, -++}; -++ -++static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port, -++ int num_tx, int nth) -++{ -++ u8 active = 0; -++ int i; -++ -++ num_tx = num_tx <= 8 ? num_tx : 8; -++ if (nth < num_tx) -++ active = mv88e6xxx_lag_mask_table[num_tx - 1][nth]; -++ -++ for (i = 0; i < 8; i++) { -++ if (BIT(i) & active) -++ mask[i] |= BIT(port); -++ } -++} -++ -++static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ unsigned int id, num_tx; -++ struct net_device *lag; -++ struct dsa_port *dp; -++ int i, err, nth; -++ u16 mask[8]; -++ u16 ivec; -++ -++ /* Assume no port is a member of any LAG. */ -++ ivec = BIT(mv88e6xxx_num_ports(chip)) - 1; -++ -++ /* Disable all masks for ports that _are_ members of a LAG. */ -++ list_for_each_entry(dp, &ds->dst->ports, list) { -++ if (!dp->lag_dev || dp->ds != ds) -++ continue; -++ -++ ivec &= ~BIT(dp->index); -++ } -++ -++ for (i = 0; i < 8; i++) -++ mask[i] = ivec; -++ -++ /* Enable the correct subset of masks for all LAG ports that -++ * are in the Tx set. -++ */ -++ dsa_lags_foreach_id(id, ds->dst) { -++ lag = dsa_lag_dev(ds->dst, id); -++ if (!lag) -++ continue; -++ -++ num_tx = 0; -++ dsa_lag_foreach_port(dp, ds->dst, lag) { -++ if (dp->lag_tx_enabled) -++ num_tx++; -++ } -++ -++ if (!num_tx) -++ continue; -++ -++ nth = 0; -++ dsa_lag_foreach_port(dp, ds->dst, lag) { -++ if (!dp->lag_tx_enabled) -++ continue; -++ -++ if (dp->ds == ds) -++ mv88e6xxx_lag_set_port_mask(mask, dp->index, -++ num_tx, nth); -++ -++ nth++; -++ } -++ } -++ -++ for (i = 0; i < 8; i++) { -++ err = mv88e6xxx_g2_trunk_mask_write(chip, i, true, mask[i]); -++ if (err) -++ return err; -++ } -++ -++ return 0; -++} -++ -++static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds, -++ struct net_device *lag) -++{ -++ int err; -++ -++ err = mv88e6xxx_lag_sync_masks(ds); -++ -++ if (!err) -++ err = mv88e6xxx_lag_sync_map(ds, lag); -++ -++ return err; -++} -++ -++static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err; -++ -++ mv88e6xxx_reg_lock(chip); -++ err = mv88e6xxx_lag_sync_masks(ds); -++ mv88e6xxx_reg_unlock(chip); -++ return err; -++} -++ -++static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, -++ struct net_device *lag, -++ struct netdev_lag_upper_info *info) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err, id; -++ -++ if (!mv88e6xxx_lag_can_offload(ds, lag, info)) -++ return -EOPNOTSUPP; -++ -++ id = dsa_lag_id(ds->dst, lag); -++ -++ mv88e6xxx_reg_lock(chip); -++ -++ err = mv88e6xxx_port_set_trunk(chip, port, true, id); -++ if (err) -++ goto err_unlock; -++ -++ err = mv88e6xxx_lag_sync_masks_map(ds, lag); -++ if (err) -++ goto err_clear_trunk; -++ -++ mv88e6xxx_reg_unlock(chip); -++ return 0; -++ -++err_clear_trunk: -++ mv88e6xxx_port_set_trunk(chip, port, false, 0); -++err_unlock: -++ mv88e6xxx_reg_unlock(chip); -++ return err; -++} -++ -++static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port, -++ struct net_device *lag) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err_sync, err_trunk; -++ -++ mv88e6xxx_reg_lock(chip); -++ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); -++ err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0); -++ mv88e6xxx_reg_unlock(chip); -++ return err_sync ? : err_trunk; -++} -++ -++static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index, -++ int port) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err; -++ -++ mv88e6xxx_reg_lock(chip); -++ err = mv88e6xxx_lag_sync_masks(ds); -++ mv88e6xxx_reg_unlock(chip); -++ return err; -++} -++ -++static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, -++ int port, struct net_device *lag, -++ struct netdev_lag_upper_info *info) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err; -++ -++ if (!mv88e6xxx_lag_can_offload(ds, lag, info)) -++ return -EOPNOTSUPP; -++ -++ mv88e6xxx_reg_lock(chip); -++ -++ err = mv88e6xxx_lag_sync_masks_map(ds, lag); -++ if (err) -++ goto unlock; -++ -++ err = mv88e6xxx_pvt_map(chip, sw_index, port); -++ -++unlock: -++ mv88e6xxx_reg_unlock(chip); -++ return err; -++} -++ -++static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index, -++ int port, struct net_device *lag) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err_sync, err_pvt; -++ -++ mv88e6xxx_reg_lock(chip); -++ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); -++ err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port); -++ mv88e6xxx_reg_unlock(chip); -++ return err_sync ? : err_pvt; -++} -++ -+ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { -+ .get_tag_protocol = mv88e6xxx_get_tag_protocol, -+ .setup = mv88e6xxx_setup, -+@@ -5069,6 +5350,12 @@ static const struct dsa_switch_ops mv88e -+ .port_txtstamp = mv88e6xxx_port_txtstamp, -+ .port_rxtstamp = mv88e6xxx_port_rxtstamp, -+ .get_ts_info = mv88e6xxx_get_ts_info, -++ .port_lag_change = mv88e6xxx_port_lag_change, -++ .port_lag_join = mv88e6xxx_port_lag_join, -++ .port_lag_leave = mv88e6xxx_port_lag_leave, -++ .crosschip_lag_change = mv88e6xxx_crosschip_lag_change, -++ .crosschip_lag_join = mv88e6xxx_crosschip_lag_join, -++ .crosschip_lag_leave = mv88e6xxx_crosschip_lag_leave, -+ }; -+ -+ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) -+@@ -5076,10 +5363,12 @@ static int mv88e6xxx_register_switch(str -+ struct device *dev = chip->dev; -+ struct dsa_switch *ds; -+ -+- ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip)); -++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return -ENOMEM; -+ -++ ds->dev = dev; -++ ds->num_ports = mv88e6xxx_num_ports(chip); -+ ds->priv = chip; -+ ds->dev = dev; -+ ds->ops = &mv88e6xxx_switch_ops; -+@@ -5087,6 +5376,12 @@ static int mv88e6xxx_register_switch(str -+ ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; -+ ds->assisted_learning_on_cpu_port = true; -+ -++ /* Some chips support up to 32, but that requires enabling the -++ * 5-bit port mode, which we do not support. 640k^W16 ought to -++ * be enough for anyone. -++ */ -++ ds->num_lag_ids = 16; -++ -+ dev_set_drvdata(dev, ds); -+ -+ return dsa_register_switch(ds); -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.c -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -126,8 +126,8 @@ int mv88e6xxx_g2_device_mapping_write(st -+ -+ /* Offset 0x07: Trunk Mask Table register */ -+ -+-static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, -+- bool hash, u16 mask) -++int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, -++ bool hash, u16 mask) -+ { -+ u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip)); -+ -+@@ -140,8 +140,8 @@ static int mv88e6xxx_g2_trunk_mask_write -+ -+ /* Offset 0x08: Trunk Mapping Table register */ -+ -+-static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, -+- u16 map) -++int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, -++ u16 map) -+ { -+ const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; -+ u16 val = (id << 11) | (map & port_mask); -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.h linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.h -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.h 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.h 2021-08-04 14:05:53.891713717 +0700 -+@@ -101,6 +101,7 @@ -+ #define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN 0x3000 -+ #define MV88E6XXX_G2_PVT_ADDR_OP_READ 0x4000 -+ #define MV88E6XXX_G2_PVT_ADDR_PTR_MASK 0x01ff -++#define MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK 0x1f -+ -+ /* Offset 0x0C: Cross-chip Port VLAN Data Register */ -+ #define MV88E6XXX_G2_PVT_DATA 0x0c -+@@ -336,6 +337,10 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv -+ -+ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); -+ -++int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, -++ bool hash, u16 mask); -++int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, -++ u16 map); -+ int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip); -+ -+ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.c -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -994,6 +994,27 @@ int mv88e6xxx_port_set_message_port(stru -+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); -+ } -+ -++int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, -++ bool trunk, u8 id) -++{ -++ u16 val; -++ int err; -++ -++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val); -++ if (err) -++ return err; -++ -++ val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK; -++ -++ if (trunk) -++ val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT | -++ (id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT); -++ else -++ val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT; -++ -++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); -++} -++ -+ /* Offset 0x06: Port Based VLAN Map */ -+ -+ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) -+diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.h linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.h -+--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.h 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.h 2021-08-04 14:05:53.891713717 +0700 -+@@ -168,6 +168,9 @@ -+ /* Offset 0x05: Port Control 1 */ -+ #define MV88E6XXX_PORT_CTL1 0x05 -+ #define MV88E6XXX_PORT_CTL1_MESSAGE_PORT 0x8000 -++#define MV88E6XXX_PORT_CTL1_TRUNK_PORT 0x4000 -++#define MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK 0x0f00 -++#define MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT 8 -+ #define MV88E6XXX_PORT_CTL1_FID_11_4_MASK 0x00ff -+ -+ /* Offset 0x06: Port Based VLAN Map */ -+@@ -343,6 +346,8 @@ int mv88e6351_port_set_ether_type(struct -+ u16 etype); -+ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, -+ bool message_port); -++int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, -++ bool trunk, u8 id); -+ int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port, -+ size_t size); -+ int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); -+diff -urpN linux-5.4.137.old/drivers/net/dsa/qca8k.c linux-5.4.137/drivers/net/dsa/qca8k.c -+--- linux-5.4.137.old/drivers/net/dsa/qca8k.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/qca8k.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -661,7 +661,7 @@ qca8k_setup(struct dsa_switch *ds) -+ return ret; -+ -+ /* Initialize CPU port pad mode (xMII type, delays...) */ -+- phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn); -++ phy_mode = of_get_phy_mode(dsa_to_port(ds, QCA8K_CPU_PORT)->dn); -+ if (phy_mode < 0) { -+ pr_err("Can't find phy-mode for master device\n"); -+ return phy_mode; -+@@ -1077,10 +1077,13 @@ qca8k_sw_probe(struct mdio_device *mdiod -+ if (id != QCA8K_ID_QCA8337) -+ return -ENODEV; -+ -+- priv->ds = dsa_switch_alloc(&mdiodev->dev, QCA8K_NUM_PORTS); -++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), -++ QCA8K_NUM_PORTS); -+ if (!priv->ds) -+ return -ENOMEM; -+ -++ priv->ds->dev = &mdiodev->dev; -++ priv->ds->num_ports = DSA_MAX_PORTS; -+ priv->ds->priv = priv; -+ priv->ops = qca8k_switch_ops; -+ priv->ds->ops = &priv->ops; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/realtek-smi-core.c linux-5.4.137/drivers/net/dsa/realtek-smi-core.c -+--- linux-5.4.137.old/drivers/net/dsa/realtek-smi-core.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/realtek-smi-core.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -444,9 +444,12 @@ static int realtek_smi_probe(struct plat -+ return ret; -+ } -+ -+- smi->ds = dsa_switch_alloc(dev, smi->num_ports); -++ smi->ds = devm_kzalloc(dev, sizeof(*smi->ds), GFP_KERNEL); -+ if (!smi->ds) -+ return -ENOMEM; -++ -++ smi->ds->dev = dev; -++ smi->ds->num_ports = smi->num_ports; -+ smi->ds->priv = smi; -+ -+ smi->ds->ops = var->ds_ops; -+diff -urpN linux-5.4.137.old/drivers/net/dsa/sja1105/sja1105_main.c linux-5.4.137/drivers/net/dsa/sja1105/sja1105_main.c -+--- linux-5.4.137.old/drivers/net/dsa/sja1105/sja1105_main.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/sja1105/sja1105_main.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -1096,7 +1096,7 @@ int sja1105pqrs_fdb_add(struct dsa_switc -+ l2_lookup.vlanid = vid; -+ l2_lookup.iotag = SJA1105_S_TAG; -+ l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); -+- if (dsa_port_is_vlan_filtering(&ds->ports[port])) { -++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { -+ l2_lookup.mask_vlanid = VLAN_VID_MASK; -+ l2_lookup.mask_iotag = BIT(0); -+ } else { -+@@ -1159,7 +1159,7 @@ int sja1105pqrs_fdb_del(struct dsa_switc -+ l2_lookup.vlanid = vid; -+ l2_lookup.iotag = SJA1105_S_TAG; -+ l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); -+- if (dsa_port_is_vlan_filtering(&ds->ports[port])) { -++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { -+ l2_lookup.mask_vlanid = VLAN_VID_MASK; -+ l2_lookup.mask_iotag = BIT(0); -+ } else { -+@@ -1205,7 +1205,7 @@ static int sja1105_fdb_add(struct dsa_sw -+ * for what gets printed in 'bridge fdb show'. In the case of zero, -+ * no VID gets printed at all. -+ */ -+- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) -++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) -+ vid = 0; -+ -+ return priv->info->fdb_add_cmd(ds, port, addr, vid); -+@@ -1216,7 +1216,7 @@ static int sja1105_fdb_del(struct dsa_sw -+ { -+ struct sja1105_private *priv = ds->priv; -+ -+- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) -++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) -+ vid = 0; -+ -+ return priv->info->fdb_del_cmd(ds, port, addr, vid); -+@@ -1255,7 +1255,7 @@ static int sja1105_fdb_dump(struct dsa_s -+ u64_to_ether_addr(l2_lookup.macaddr, macaddr); -+ -+ /* We need to hide the dsa_8021q VLANs from the user. */ -+- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) -++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) -+ l2_lookup.vlanid = 0; -+ cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); -+ } -+@@ -1748,7 +1748,7 @@ static int sja1105_port_enable(struct ds -+ if (!dsa_is_user_port(ds, port)) -+ return 0; -+ -+- slave = ds->ports[port].slave; -++ slave = dsa_to_port(ds, port)->slave; -+ -+ slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; -+ -+@@ -1780,7 +1780,7 @@ static int sja1105_mgmt_xmit(struct dsa_ -+ } -+ -+ /* Transfer skb to the host port. */ -+- dsa_enqueue_skb(skb, ds->ports[port].slave); -++ dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); -+ -+ /* Wait until the switch has processed the frame */ -+ do { -+@@ -2198,10 +2198,12 @@ static int sja1105_probe(struct spi_devi -+ -+ dev_info(dev, "Probed switch chip: %s\n", priv->info->name); -+ -+- ds = dsa_switch_alloc(dev, SJA1105_NUM_PORTS); -++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); -+ if (!ds) -+ return -ENOMEM; -+ -++ ds->dev = dev; -++ ds->num_ports = SJA1105_NUM_PORTS; -+ ds->ops = &sja1105_switch_ops; -+ ds->priv = priv; -+ priv->ds = ds; -+@@ -2215,8 +2217,8 @@ static int sja1105_probe(struct spi_devi -+ for (i = 0; i < SJA1105_NUM_PORTS; i++) { -+ struct sja1105_port *sp = &priv->ports[i]; -+ -+- ds->ports[i].priv = sp; -+- sp->dp = &ds->ports[i]; -++ dsa_to_port(ds, i)->priv = sp; -++ sp->dp = dsa_to_port(ds, i); -+ sp->data = tagger_data; -+ } -+ mutex_init(&priv->mgmt_lock); -+diff -urpN linux-5.4.137.old/drivers/net/dsa/vitesse-vsc73xx-core.c linux-5.4.137/drivers/net/dsa/vitesse-vsc73xx-core.c -+--- linux-5.4.137.old/drivers/net/dsa/vitesse-vsc73xx-core.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/drivers/net/dsa/vitesse-vsc73xx-core.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -1178,9 +1178,12 @@ int vsc73xx_probe(struct vsc73xx *vsc) -+ * We allocate 8 ports and avoid access to the nonexistant -+ * ports. -+ */ -+- vsc->ds = dsa_switch_alloc(dev, 8); -++ vsc->ds = devm_kzalloc(dev, sizeof(*vsc->ds), GFP_KERNEL); -+ if (!vsc->ds) -+ return -ENOMEM; -++ -++ vsc->ds->dev = dev; -++ vsc->ds->num_ports = 8; -+ vsc->ds->priv = vsc; -+ -+ vsc->ds->ops = &vsc73xx_ds_ops; -+diff -urpN linux-5.4.137.old/include/net/dsa.h linux-5.4.137/include/net/dsa.h -+--- linux-5.4.137.old/include/net/dsa.h 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/include/net/dsa.h 2021-08-04 14:05:57.643717592 +0700 -+@@ -124,17 +124,46 @@ struct dsa_switch_tree { -+ */ -+ struct dsa_platform_data *pd; -+ -+- /* -+- * The switch port to which the CPU is attached. -+- */ -+- struct dsa_port *cpu_dp; -++ /* List of switch ports */ -++ struct list_head ports; -+ -+- /* -+- * Data for the individual switch chips. -++ /* List of DSA links composing the routing table */ -++ struct list_head rtable; -++ -++ /* Maps offloaded LAG netdevs to a zero-based linear ID for -++ * drivers that need it. -+ */ -+- struct dsa_switch *ds[DSA_MAX_SWITCHES]; -++ struct net_device **lags; -++ unsigned int lags_len; -+ }; -+ -++#define dsa_lags_foreach_id(_id, _dst) \ -++ for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \ -++ if ((_dst)->lags[(_id)]) -++ -++#define dsa_lag_foreach_port(_dp, _dst, _lag) \ -++ list_for_each_entry((_dp), &(_dst)->ports, list) \ -++ if ((_dp)->lag_dev == (_lag)) -++ -++static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst, -++ unsigned int id) -++{ -++ return dst->lags[id]; -++} -++ -++static inline int dsa_lag_id(struct dsa_switch_tree *dst, -++ struct net_device *lag) -++{ -++ unsigned int id; -++ -++ dsa_lags_foreach_id(id, dst) { -++ if (dsa_lag_dev(dst, id) == lag) -++ return id; -++ } -++ -++ return -ENODEV; -++} -++ -+ /* TC matchall action types, only mirroring for now */ -+ enum dsa_port_mall_action_type { -+ DSA_PORT_MALL_MIRROR, -+@@ -195,10 +224,14 @@ struct dsa_port { -+ struct devlink_port devlink_port; -+ struct phylink *pl; -+ struct phylink_config pl_config; -++ struct net_device *lag_dev; -++ bool lag_tx_enabled; -+ -+ struct work_struct xmit_work; -+ struct sk_buff_head xmit_queue; -+ -++ struct list_head list; -++ -+ /* -+ * Give the switch driver somewhere to hang its per-port private data -+ * structures (accessible from the tagger). -+@@ -214,9 +247,24 @@ struct dsa_port { -+ * Original copy of the master netdev net_device_ops -+ */ -+ const struct net_device_ops *orig_ndo_ops; -++ -++ bool setup; -++}; -++ -++/* TODO: ideally DSA ports would have a single dp->link_dp member, -++ * and no dst->rtable nor this struct dsa_link would be needed, -++ * but this would require some more complex tree walking, -++ * so keep it stupid at the moment and list them all. -++ */ -++struct dsa_link { -++ struct dsa_port *dp; -++ struct dsa_port *link_dp; -++ struct list_head list; -+ }; -+ -+ struct dsa_switch { -++ bool setup; -++ -+ struct device *dev; -+ -+ /* -+@@ -245,13 +293,6 @@ struct dsa_switch { -+ const struct dsa_switch_ops *ops; -+ -+ /* -+- * An array of which element [a] indicates which port on this -+- * switch should be used to send packets to that are destined -+- * for switch a. Can be NULL if there is only one switch chip. -+- */ -+- s8 rtable[DSA_MAX_SWITCHES]; -+- -+- /* -+ * Slave mii_bus and devices for the individual ports. -+ */ -+ u32 phys_mii_mask; -+@@ -289,14 +330,27 @@ struct dsa_switch { -+ */ -+ bool vlan_filtering; -+ -+- /* Dynamically allocated ports, keep last */ -+ size_t num_ports; -+- struct dsa_port ports[]; -++ -++ /* Drivers that benefit from having an ID associated with each -++ * offloaded LAG should set this to the maximum number of -++ * supported IDs. DSA will then maintain a mapping of _at -++ * least_ these many IDs, accessible to drivers via -++ * dsa_lag_id(). -++ */ -++ unsigned int num_lag_ids; -+ }; -+ -+-static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) -++static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) -+ { -+- return &ds->ports[p]; -++ struct dsa_switch_tree *dst = ds->dst; -++ struct dsa_port *dp; -++ -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dp->ds == ds && dp->index == p) -++ return dp; -++ -++ return NULL; -+ } -+ -+ static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) -+@@ -331,6 +385,19 @@ static inline u32 dsa_user_ports(struct -+ return mask; -+ } -+ -++/* Return the local port used to reach an arbitrary switch device */ -++static inline unsigned int dsa_routing_port(struct dsa_switch *ds, int device) -++{ -++ struct dsa_switch_tree *dst = ds->dst; -++ struct dsa_link *dl; -++ -++ list_for_each_entry(dl, &dst->rtable, list) -++ if (dl->dp->ds == ds && dl->link_dp->ds->index == device) -++ return dl->dp->index; -++ -++ return ds->num_ports; -++} -++ -+ /* Return the local port used to reach an arbitrary switch port */ -+ static inline unsigned int dsa_towards_port(struct dsa_switch *ds, int device, -+ int port) -+@@ -338,7 +405,7 @@ static inline unsigned int dsa_towards_p -+ if (device == ds->index) -+ return port; -+ else -+- return ds->rtable[device]; -++ return dsa_routing_port(ds, device); -+ } -+ -+ /* Return the local port used to reach the dedicated CPU port */ -+@@ -539,6 +606,13 @@ struct dsa_switch_ops { -+ int port, struct net_device *br); -+ void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, -+ int port, struct net_device *br); -++ int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index, -++ int port); -++ int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index, -++ int port, struct net_device *lag, -++ struct netdev_lag_upper_info *info); -++ int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index, -++ int port, struct net_device *lag); -+ -+ /* -+ * PTP functionality -+@@ -557,6 +631,16 @@ struct dsa_switch_ops { -+ */ -+ netdev_tx_t (*port_deferred_xmit)(struct dsa_switch *ds, int port, -+ struct sk_buff *skb); -++ -++ /* -++ * LAG integration -++ */ -++ int (*port_lag_change)(struct dsa_switch *ds, int port); -++ int (*port_lag_join)(struct dsa_switch *ds, int port, -++ struct net_device *lag, -++ struct netdev_lag_upper_info *info); -++ int (*port_lag_leave)(struct dsa_switch *ds, int port, -++ struct net_device *lag); -+ }; -+ -+ struct dsa_switch_driver { -+@@ -584,7 +668,6 @@ static inline bool dsa_can_decode(const -+ return false; -+ } -+ -+-struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n); -+ void dsa_unregister_switch(struct dsa_switch *ds); -+ int dsa_register_switch(struct dsa_switch *ds); -+ #ifdef CONFIG_PM_SLEEP -+@@ -628,6 +711,7 @@ int register_dsa_notifier(struct notifie -+ int unregister_dsa_notifier(struct notifier_block *nb); -+ int call_dsa_notifiers(unsigned long val, struct net_device *dev, -+ struct dsa_notifier_info *info); -++bool dsa_slave_dev_check(const struct net_device *dev); -+ #else -+ static inline int register_dsa_notifier(struct notifier_block *nb) -+ { -+@@ -644,6 +728,11 @@ static inline int call_dsa_notifiers(uns -+ { -+ return NOTIFY_DONE; -+ } -++ -++static inline bool dsa_slave_dev_check(const struct net_device *dev) -++{ -++ return false; -++} -+ #endif -+ -+ /* Broadcom tag specific helpers to insert and extract queue/port number */ -+diff -urpN linux-5.4.137.old/net/dsa/Kconfig linux-5.4.137/net/dsa/Kconfig -+--- linux-5.4.137.old/net/dsa/Kconfig 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/Kconfig 2021-08-04 14:05:53.891713717 +0700 -+@@ -56,14 +56,19 @@ config NET_DSA_TAG_GSWIP -+ Say Y or M if you want to enable support for tagging frames for the -+ Lantiq / Intel GSWIP switches. -+ -++config NET_DSA_TAG_DSA_COMMON -++ tristate -++ -+ config NET_DSA_TAG_DSA -+ tristate "Tag driver for Marvell switches using DSA headers" -++ select NET_DSA_TAG_DSA_COMMON -+ help -+ Say Y or M if you want to enable support for tagging frames for the -+ Marvell switches which use DSA headers. -+ -+ config NET_DSA_TAG_EDSA -+ tristate "Tag driver for Marvell switches using EtherType DSA headers" -++ select NET_DSA_TAG_DSA_COMMON -+ help -+ Say Y or M if you want to enable support for tagging frames for the -+ Marvell switches which use EtherType DSA headers. -+diff -urpN linux-5.4.137.old/net/dsa/Makefile linux-5.4.137/net/dsa/Makefile -+--- linux-5.4.137.old/net/dsa/Makefile 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/Makefile 2021-08-04 14:05:53.891713717 +0700 -+@@ -6,8 +6,7 @@ dsa_core-y += dsa.o dsa2.o master.o port -+ # tagging formats -+ obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o -+ obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o -+-obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o -+-obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o -++obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o -+ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o -+ obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o -+ obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o -+diff -urpN linux-5.4.137.old/net/dsa/dsa.c linux-5.4.137/net/dsa/dsa.c -+--- linux-5.4.137.old/net/dsa/dsa.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/dsa.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -224,11 +224,21 @@ static int dsa_switch_rcv(struct sk_buff -+ } -+ -+ skb = nskb; -+- p = netdev_priv(skb->dev); -+ skb_push(skb, ETH_HLEN); -+ skb->pkt_type = PACKET_HOST; -+ skb->protocol = eth_type_trans(skb, skb->dev); -+ -++ if (unlikely(!dsa_slave_dev_check(skb->dev))) { -++ /* Packet is to be injected directly on an upper -++ * device, e.g. a team/bond, so skip all DSA-port -++ * specific actions. -++ */ -++ netif_rx(skb); -++ return 0; -++ } -++ -++ p = netdev_priv(skb->dev); -++ -+ s = this_cpu_ptr(p->stats64); -+ u64_stats_update_begin(&s->syncp); -+ s->rx_packets++; -+@@ -246,7 +256,9 @@ static int dsa_switch_rcv(struct sk_buff -+ #ifdef CONFIG_PM_SLEEP -+ static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) -+ { -+- return dsa_is_user_port(ds, p) && ds->ports[p].slave; -++ const struct dsa_port *dp = dsa_to_port(ds, p); -++ -++ return dp->type == DSA_PORT_TYPE_USER && dp->slave; -+ } -+ -+ int dsa_switch_suspend(struct dsa_switch *ds) -+@@ -258,7 +270,7 @@ int dsa_switch_suspend(struct dsa_switch -+ if (!dsa_is_port_initialized(ds, i)) -+ continue; -+ -+- ret = dsa_slave_suspend(ds->ports[i].slave); -++ ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave); -+ if (ret) -+ return ret; -+ } -+@@ -285,7 +297,7 @@ int dsa_switch_resume(struct dsa_switch -+ if (!dsa_is_port_initialized(ds, i)) -+ continue; -+ -+- ret = dsa_slave_resume(ds->ports[i].slave); -++ ret = dsa_slave_resume(dsa_to_port(ds, i)->slave); -+ if (ret) -+ return ret; -+ } -+diff -urpN linux-5.4.137.old/net/dsa/dsa2.c linux-5.4.137/net/dsa/dsa2.c -+--- linux-5.4.137.old/net/dsa/dsa2.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/dsa2.c 2021-08-04 14:05:57.643717592 +0700 -+@@ -25,6 +25,65 @@ static DEFINE_MUTEX(dsa2_mutex); -+ static const struct devlink_ops dsa_devlink_ops = { -+ }; -+ -++/** -++ * dsa_lag_map() - Map LAG netdev to a linear LAG ID -++ * @dst: Tree in which to record the mapping. -++ * @lag: Netdev that is to be mapped to an ID. -++ * -++ * dsa_lag_id/dsa_lag_dev can then be used to translate between the -++ * two spaces. The size of the mapping space is determined by the -++ * driver by setting ds->num_lag_ids. It is perfectly legal to leave -++ * it unset if it is not needed, in which case these functions become -++ * no-ops. -++ */ -++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) -++{ -++ unsigned int id; -++ -++ if (dsa_lag_id(dst, lag) >= 0) -++ /* Already mapped */ -++ return; -++ -++ for (id = 0; id < dst->lags_len; id++) { -++ if (!dsa_lag_dev(dst, id)) { -++ dst->lags[id] = lag; -++ return; -++ } -++ } -++ -++ /* No IDs left, which is OK. Some drivers do not need it. The -++ * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id -++ * returns an error for this device when joining the LAG. The -++ * driver can then return -EOPNOTSUPP back to DSA, which will -++ * fall back to a software LAG. -++ */ -++} -++ -++/** -++ * dsa_lag_unmap() - Remove a LAG ID mapping -++ * @dst: Tree in which the mapping is recorded. -++ * @lag: Netdev that was mapped. -++ * -++ * As there may be multiple users of the mapping, it is only removed -++ * if there are no other references to it. -++ */ -++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag) -++{ -++ struct dsa_port *dp; -++ unsigned int id; -++ -++ dsa_lag_foreach_port(dp, dst, lag) -++ /* There are remaining users of this mapping */ -++ return; -++ -++ dsa_lags_foreach_id(id, dst) { -++ if (dsa_lag_dev(dst, id) == lag) { -++ dst->lags[id] = NULL; -++ break; -++ } -++ } -++} -++ -+ static struct dsa_switch_tree *dsa_tree_find(int index) -+ { -+ struct dsa_switch_tree *dst; -+@@ -46,6 +105,10 @@ static struct dsa_switch_tree *dsa_tree_ -+ -+ dst->index = index; -+ -++ INIT_LIST_HEAD(&dst->rtable); -++ -++ INIT_LIST_HEAD(&dst->ports); -++ -+ INIT_LIST_HEAD(&dst->list); -+ list_add_tail(&dst->list, &dsa_tree_list); -+ -+@@ -112,24 +175,38 @@ static bool dsa_port_is_user(struct dsa_ -+ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, -+ struct device_node *dn) -+ { -+- struct dsa_switch *ds; -+ struct dsa_port *dp; -+- int device, port; -+ -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dp->dn == dn) -++ return dp; -+ -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -++ return NULL; -++} -+ -+- if (dp->dn == dn) -+- return dp; -+- } -+- } -++struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp) -++{ -++ struct dsa_switch *ds = dp->ds; -++ struct dsa_switch_tree *dst; -++ struct dsa_link *dl; -+ -+- return NULL; -++ dst = ds->dst; -++ -++ list_for_each_entry(dl, &dst->rtable, list) -++ if (dl->dp == dp && dl->link_dp == link_dp) -++ return dl; -++ -++ dl = kzalloc(sizeof(*dl), GFP_KERNEL); -++ if (!dl) -++ return NULL; -++ -++ dl->dp = dp; -++ dl->link_dp = link_dp; -++ -++ INIT_LIST_HEAD(&dl->list); -++ list_add_tail(&dl->list, &dst->rtable); -++ -++ return dl; -+ } -+ -+ static bool dsa_port_setup_routing_table(struct dsa_port *dp) -+@@ -139,6 +216,7 @@ static bool dsa_port_setup_routing_table -+ struct device_node *dn = dp->dn; -+ struct of_phandle_iterator it; -+ struct dsa_port *link_dp; -++ struct dsa_link *dl; -+ int err; -+ -+ of_for_each_phandle(&it, err, dn, "link", NULL, 0) { -+@@ -148,24 +226,22 @@ static bool dsa_port_setup_routing_table -+ return false; -+ } -+ -+- ds->rtable[link_dp->ds->index] = dp->index; -++ dl = dsa_link_touch(dp, link_dp); -++ if (!dl) { -++ of_node_put(it.node); -++ return false; -++ } -+ } -+ -+ return true; -+ } -+ -+-static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) -++static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) -+ { -+ bool complete = true; -+ struct dsa_port *dp; -+- int i; -+- -+- for (i = 0; i < DSA_MAX_SWITCHES; i++) -+- ds->rtable[i] = DSA_RTABLE_NONE; -+- -+- for (i = 0; i < ds->num_ports; i++) { -+- dp = &ds->ports[i]; -+ -++ list_for_each_entry(dp, &dst->ports, list) { -+ if (dsa_port_is_dsa(dp)) { -+ complete = dsa_port_setup_routing_table(dp); -+ if (!complete) -+@@ -176,81 +252,42 @@ static bool dsa_switch_setup_routing_tab -+ return complete; -+ } -+ -+-static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) -+-{ -+- struct dsa_switch *ds; -+- bool complete = true; -+- int device; -+- -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -+- -+- complete = dsa_switch_setup_routing_table(ds); -+- if (!complete) -+- break; -+- } -+- -+- return complete; -+-} -+- -+ static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) -+ { -+- struct dsa_switch *ds; -+ struct dsa_port *dp; -+- int device, port; -+ -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -+- -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -+- -+- if (dsa_port_is_cpu(dp)) -+- return dp; -+- } -+- } -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dsa_port_is_cpu(dp)) -++ return dp; -+ -+ return NULL; -+ } -+ -+ static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) -+ { -+- struct dsa_switch *ds; -+- struct dsa_port *dp; -+- int device, port; -++ struct dsa_port *cpu_dp, *dp; -+ -+- /* DSA currently only supports a single CPU port */ -+- dst->cpu_dp = dsa_tree_find_first_cpu(dst); -+- if (!dst->cpu_dp) { -+- pr_warn("Tree has no master device\n"); -++ cpu_dp = dsa_tree_find_first_cpu(dst); -++ if (!cpu_dp) { -++ pr_err("DSA: tree %d has no CPU port\n", dst->index); -+ return -EINVAL; -+ } -+ -+ /* Assign the default CPU port to all ports of the fabric */ -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -+- -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -+- -+- if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) -+- dp->cpu_dp = dst->cpu_dp; -+- } -+- } -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) -++ dp->cpu_dp = cpu_dp; -+ -+ return 0; -+ } -+ -+ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) -+ { -+- /* DSA currently only supports a single CPU port */ -+- dst->cpu_dp = NULL; -++ struct dsa_port *dp; -++ -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) -++ dp->cpu_dp = NULL; -+ } -+ -+ static int dsa_port_setup(struct dsa_port *dp) -+@@ -266,6 +303,9 @@ static int dsa_port_setup(struct dsa_por -+ bool dsa_port_enabled = false; -+ int err = 0; -+ -++ if (dp->setup) -++ return 0; -++ -+ switch (dp->type) { -+ case DSA_PORT_TYPE_UNUSED: -+ dsa_port_disable(dp); -+@@ -335,14 +375,21 @@ static int dsa_port_setup(struct dsa_por -+ dsa_port_link_unregister_of(dp); -+ if (err && devlink_port_registered) -+ devlink_port_unregister(dlp); -++ if (err) -++ return err; -+ -+- return err; -++ dp->setup = true; -++ -++ return 0; -+ } -+ -+ static void dsa_port_teardown(struct dsa_port *dp) -+ { -+ struct devlink_port *dlp = &dp->devlink_port; -+ -++ if (!dp->setup) -++ return; -++ -+ switch (dp->type) { -+ case DSA_PORT_TYPE_UNUSED: -+ break; -+@@ -365,11 +412,16 @@ static void dsa_port_teardown(struct dsa -+ } -+ break; -+ } -++ -++ dp->setup = false; -+ } -+ -+ static int dsa_switch_setup(struct dsa_switch *ds) -+ { -+- int err = 0; -++ int err; -++ -++ if (ds->setup) -++ return 0; -+ -+ /* Initialize ds->phys_mii_mask before registering the slave MDIO bus -+ * driver and before ops->setup() has run, since the switch drivers and -+@@ -411,6 +463,8 @@ static int dsa_switch_setup(struct dsa_s -+ goto teardown; -+ } -+ -++ ds->setup = true; -++ -+ return 0; -+ -+ teardown: -+@@ -429,6 +483,9 @@ free_devlink: -+ -+ static void dsa_switch_teardown(struct dsa_switch *ds) -+ { -++ if (!ds->setup) -++ return; -++ -+ if (ds->slave_mii_bus && ds->ops->phy_read) -+ mdiobus_unregister(ds->slave_mii_bus); -+ -+@@ -443,89 +500,98 @@ static void dsa_switch_teardown(struct d -+ ds->devlink = NULL; -+ } -+ -++ ds->setup = false; -+ } -+ -+ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) -+ { -+- struct dsa_switch *ds; -+ struct dsa_port *dp; -+- int device, port, i; -+- int err = 0; -+- -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -++ int err; -+ -+- err = dsa_switch_setup(ds); -++ list_for_each_entry(dp, &dst->ports, list) { -++ err = dsa_switch_setup(dp->ds); -+ if (err) -+- goto switch_teardown; -+- -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -+- -+- err = dsa_port_setup(dp); -+- if (err) -+- continue; -+- } -++ goto teardown; -+ } -+ -+- return 0; -+- -+-switch_teardown: -+- for (i = 0; i < device; i++) { -+- ds = dst->ds[i]; -+- if (!ds) -+- continue; -+- -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -++ list_for_each_entry(dp, &dst->ports, list) { -++ err = dsa_port_setup(dp); -++ if (err) -++ goto teardown; -++ } -+ -+- dsa_port_teardown(dp); -+- } -++ return 0; -+ -+- dsa_switch_teardown(ds); -+- } -++teardown: -++ list_for_each_entry(dp, &dst->ports, list) -++ dsa_port_teardown(dp); -++ -++ list_for_each_entry(dp, &dst->ports, list) -++ dsa_switch_teardown(dp->ds); -+ -+ return err; -+ } -+ -+ static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) -+ { -+- struct dsa_switch *ds; -+ struct dsa_port *dp; -+- int device, port; -+ -+- for (device = 0; device < DSA_MAX_SWITCHES; device++) { -+- ds = dst->ds[device]; -+- if (!ds) -+- continue; -++ list_for_each_entry(dp, &dst->ports, list) -++ dsa_port_teardown(dp); -+ -+- for (port = 0; port < ds->num_ports; port++) { -+- dp = &ds->ports[port]; -++ list_for_each_entry(dp, &dst->ports, list) -++ dsa_switch_teardown(dp->ds); -++} -+ -+- dsa_port_teardown(dp); -+- } -++static int dsa_tree_setup_master(struct dsa_switch_tree *dst) -++{ -++ struct dsa_port *dp; -++ int err; -+ -+- dsa_switch_teardown(ds); -++ list_for_each_entry(dp, &dst->ports, list) { -++ if (dsa_port_is_cpu(dp)) { -++ err = dsa_master_setup(dp->master, dp); -++ if (err) -++ return err; -++ } -+ } -++ -++ return 0; -+ } -+ -+-static int dsa_tree_setup_master(struct dsa_switch_tree *dst) -++static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) -+ { -+- struct dsa_port *cpu_dp = dst->cpu_dp; -+- struct net_device *master = cpu_dp->master; -++ struct dsa_port *dp; -+ -+- /* DSA currently supports a single pair of CPU port and master device */ -+- return dsa_master_setup(master, cpu_dp); -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dsa_port_is_cpu(dp)) -++ dsa_master_teardown(dp->master); -+ } -+ -+-static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) -++static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) -+ { -+- struct dsa_port *cpu_dp = dst->cpu_dp; -+- struct net_device *master = cpu_dp->master; -++ unsigned int len = 0; -++ struct dsa_port *dp; -++ -++ list_for_each_entry(dp, &dst->ports, list) { -++ if (dp->ds->num_lag_ids > len) -++ len = dp->ds->num_lag_ids; -++ } -+ -+- return dsa_master_teardown(master); -++ if (!len) -++ return 0; -++ -++ dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL); -++ if (!dst->lags) -++ return -ENOMEM; -++ -++ dst->lags_len = len; -++ return 0; -++} -++ -++static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst) -++{ -++ kfree(dst->lags); -+ } -+ -+ static int dsa_tree_setup(struct dsa_switch_tree *dst) -+@@ -555,12 +621,18 @@ static int dsa_tree_setup(struct dsa_swi -+ if (err) -+ goto teardown_switches; -+ -++ err = dsa_tree_setup_lags(dst); -++ if (err) -++ goto teardown_master; -++ -+ dst->setup = true; -+ -+ pr_info("DSA: tree %d setup\n", dst->index); -+ -+ return 0; -+ -++teardown_master: -++ dsa_tree_teardown_master(dst); -+ teardown_switches: -+ dsa_tree_teardown_switches(dst); -+ teardown_default_cpu: -+@@ -571,48 +643,49 @@ teardown_default_cpu: -+ -+ static void dsa_tree_teardown(struct dsa_switch_tree *dst) -+ { -++ struct dsa_link *dl, *next; -++ -+ if (!dst->setup) -+ return; -+ -++ dsa_tree_teardown_lags(dst); -++ -+ dsa_tree_teardown_master(dst); -+ -+ dsa_tree_teardown_switches(dst); -+ -+ dsa_tree_teardown_default_cpu(dst); -+ -++ list_for_each_entry_safe(dl, next, &dst->rtable, list) { -++ list_del(&dl->list); -++ kfree(dl); -++ } -++ -+ pr_info("DSA: tree %d torn down\n", dst->index); -+ -+ dst->setup = false; -+ } -+ -+-static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, -+- unsigned int index) -++static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) -+ { -+- dsa_tree_teardown(dst); -++ struct dsa_switch_tree *dst = ds->dst; -++ struct dsa_port *dp; -+ -+- dst->ds[index] = NULL; -+- dsa_tree_put(dst); -+-} -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dp->ds == ds && dp->index == index) -++ return dp; -+ -+-static int dsa_tree_add_switch(struct dsa_switch_tree *dst, -+- struct dsa_switch *ds) -+-{ -+- unsigned int index = ds->index; -+- int err; -++ dp = kzalloc(sizeof(*dp), GFP_KERNEL); -++ if (!dp) -++ return NULL; -+ -+- if (dst->ds[index]) -+- return -EBUSY; -++ dp->ds = ds; -++ dp->index = index; -+ -+- dsa_tree_get(dst); -+- dst->ds[index] = ds; -++ INIT_LIST_HEAD(&dp->list); -++ list_add_tail(&dp->list, &dst->ports); -+ -+- err = dsa_tree_setup(dst); -+- if (err) { -+- dst->ds[index] = NULL; -+- dsa_tree_put(dst); -+- } -+- -+- return err; -++ return dp; -+ } -+ -+ static int dsa_port_parse_user(struct dsa_port *dp, const char *name) -+@@ -707,7 +780,7 @@ static int dsa_switch_parse_ports_of(str -+ goto out_put_node; -+ } -+ -+- dp = &ds->ports[reg]; -++ dp = dsa_to_port(ds, reg); -+ -+ err = dsa_port_parse_of(dp, port); -+ if (err) -+@@ -731,8 +804,6 @@ static int dsa_switch_parse_member_of(st -+ return sz; -+ -+ ds->index = m[1]; -+- if (ds->index >= DSA_MAX_SWITCHES) -+- return -EINVAL; -+ -+ ds->dst = dsa_tree_touch(m[0]); -+ if (!ds->dst) -+@@ -741,6 +812,20 @@ static int dsa_switch_parse_member_of(st -+ return 0; -+ } -+ -++static int dsa_switch_touch_ports(struct dsa_switch *ds) -++{ -++ struct dsa_port *dp; -++ int port; -++ -++ for (port = 0; port < ds->num_ports; port++) { -++ dp = dsa_port_touch(ds, port); -++ if (!dp) -++ return -ENOMEM; -++ } -++ -++ return 0; -++} -++ -+ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) -+ { -+ int err; -+@@ -749,6 +834,10 @@ static int dsa_switch_parse_of(struct ds -+ if (err) -+ return err; -+ -++ err = dsa_switch_touch_ports(ds); -++ if (err) -++ return err; -++ -+ return dsa_switch_parse_ports_of(ds, dn); -+ } -+ -+@@ -786,7 +875,7 @@ static int dsa_switch_parse_ports(struct -+ for (i = 0; i < DSA_MAX_PORTS; i++) { -+ name = cd->port_names[i]; -+ dev = cd->netdev[i]; -+- dp = &ds->ports[i]; -++ dp = dsa_to_port(ds, i); -+ -+ if (!name) -+ continue; -+@@ -806,6 +895,8 @@ static int dsa_switch_parse_ports(struct -+ -+ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) -+ { -++ int err; -++ -+ ds->cd = cd; -+ -+ /* We don't support interconnected switches nor multiple trees via -+@@ -816,22 +907,26 @@ static int dsa_switch_parse(struct dsa_s -+ if (!ds->dst) -+ return -ENOMEM; -+ -+- return dsa_switch_parse_ports(ds, cd); -+-} -+- -+-static int dsa_switch_add(struct dsa_switch *ds) -+-{ -+- struct dsa_switch_tree *dst = ds->dst; -++ err = dsa_switch_touch_ports(ds); -++ if (err) -++ return err; -+ -+- return dsa_tree_add_switch(dst, ds); -++ return dsa_switch_parse_ports(ds, cd); -+ } -+ -+ static int dsa_switch_probe(struct dsa_switch *ds) -+ { -++ struct dsa_switch_tree *dst; -+ struct dsa_chip_data *pdata = ds->dev->platform_data; -+ struct device_node *np = ds->dev->of_node; -+ int err; -+ -++ if (!ds->dev) -++ return -ENODEV; -++ -++ if (!ds->num_ports) -++ return -EINVAL; -++ -+ if (np) -+ err = dsa_switch_parse_of(ds, np); -+ else if (pdata) -+@@ -842,29 +937,14 @@ static int dsa_switch_probe(struct dsa_s -+ if (err) -+ return err; -+ -+- return dsa_switch_add(ds); -+-} -+- -+-struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) -+-{ -+- struct dsa_switch *ds; -+- int i; -+- -+- ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL); -+- if (!ds) -+- return NULL; -+- -+- ds->dev = dev; -+- ds->num_ports = n; -+- -+- for (i = 0; i < ds->num_ports; ++i) { -+- ds->ports[i].index = i; -+- ds->ports[i].ds = ds; -+- } -++ dst = ds->dst; -++ dsa_tree_get(dst); -++ err = dsa_tree_setup(dst); -++ if (err) -++ dsa_tree_put(dst); -+ -+- return ds; -++ return err; -+ } -+-EXPORT_SYMBOL_GPL(dsa_switch_alloc); -+ -+ int dsa_register_switch(struct dsa_switch *ds) -+ { -+@@ -882,9 +962,15 @@ EXPORT_SYMBOL_GPL(dsa_register_switch); -+ static void dsa_switch_remove(struct dsa_switch *ds) -+ { -+ struct dsa_switch_tree *dst = ds->dst; -+- unsigned int index = ds->index; -++ struct dsa_port *dp, *next; -++ -++ list_for_each_entry_safe(dp, next, &dst->ports, list) { -++ list_del(&dp->list); -++ kfree(dp); -++ } -+ -+- dsa_tree_remove_switch(dst, index); -++ dsa_tree_teardown(dst); -++ dsa_tree_put(dst); -+ } -+ -+ void dsa_unregister_switch(struct dsa_switch *ds) -+diff -urpN linux-5.4.137.old/net/dsa/dsa_priv.h linux-5.4.137/net/dsa/dsa_priv.h -+--- linux-5.4.137.old/net/dsa/dsa_priv.h 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/dsa_priv.h 2021-08-04 14:05:53.891713717 +0700 -+@@ -19,6 +19,9 @@ enum { -+ DSA_NOTIFIER_BRIDGE_LEAVE, -+ DSA_NOTIFIER_FDB_ADD, -+ DSA_NOTIFIER_FDB_DEL, -++ DSA_NOTIFIER_LAG_CHANGE, -++ DSA_NOTIFIER_LAG_JOIN, -++ DSA_NOTIFIER_LAG_LEAVE, -+ DSA_NOTIFIER_MDB_ADD, -+ DSA_NOTIFIER_MDB_DEL, -+ DSA_NOTIFIER_VLAN_ADD, -+@@ -54,6 +57,15 @@ struct dsa_notifier_mdb_info { -+ int port; -+ }; -+ -++/* DSA_NOTIFIER_LAG_* */ -++struct dsa_notifier_lag_info { -++ struct net_device *lag; -++ int sw_index; -++ int port; -++ -++ struct netdev_lag_upper_info *info; -++}; -++ -+ /* DSA_NOTIFIER_VLAN_* */ -+ struct dsa_notifier_vlan_info { -+ const struct switchdev_obj_port_vlan *vlan; -+@@ -119,25 +131,14 @@ static inline struct net_device *dsa_mas -+ { -+ struct dsa_port *cpu_dp = dev->dsa_ptr; -+ struct dsa_switch_tree *dst = cpu_dp->dst; -+- struct dsa_switch *ds; -+- struct dsa_port *slave_port; -+- -+- if (device < 0 || device >= DSA_MAX_SWITCHES) -+- return NULL; -+- -+- ds = dst->ds[device]; -+- if (!ds) -+- return NULL; -+- -+- if (port < 0 || port >= ds->num_ports) -+- return NULL; -++ struct dsa_port *dp; -+ -+- slave_port = &ds->ports[port]; -++ list_for_each_entry(dp, &dst->ports, list) -++ if (dp->ds->index == device && dp->index == port && -++ dp->type == DSA_PORT_TYPE_USER) -++ return dp->slave; -+ -+- if (unlikely(slave_port->type != DSA_PORT_TYPE_USER)) -+- return NULL; -+- -+- return slave_port->slave; -++ return NULL; -+ } -+ -+ /* port.c */ -+@@ -149,6 +150,11 @@ void dsa_port_disable_rt(struct dsa_port -+ void dsa_port_disable(struct dsa_port *dp); -+ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br); -+ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); -++int dsa_port_lag_change(struct dsa_port *dp, -++ struct netdev_lag_lower_state_info *linfo); -++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, -++ struct netdev_lag_upper_info *uinfo); -++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); -+ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, -+ struct switchdev_trans *trans); -+ bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); -+@@ -197,6 +203,22 @@ void dsa_port_phylink_mac_link_up(struct -+ struct phy_device *phydev); -+ extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; -+ -++static inline bool dsa_port_offloads_netdev(struct dsa_port *dp, -++ struct net_device *dev) -++{ -++ /* Switchdev offloading can be configured on: */ -++ -++ if (dev == dp->slave) -++ /* DSA ports directly connected to a bridge. */ -++ return true; -++ -++ if (dp->lag_dev == dev) -++ /* DSA ports connected to a bridge via a LAG */ -++ return true; -++ -++ return false; -++} -++ -+ /* slave.c */ -+ extern const struct dsa_device_ops notag_netdev_ops; -+ void dsa_slave_mii_bus_init(struct dsa_switch *ds); -+@@ -227,4 +249,9 @@ dsa_slave_to_master(const struct net_dev -+ /* switch.c */ -+ int dsa_switch_register_notifier(struct dsa_switch *ds); -+ void dsa_switch_unregister_notifier(struct dsa_switch *ds); -++ -++/* dsa2.c */ -++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag); -++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag); -++ -+ #endif -+diff -urpN linux-5.4.137.old/net/dsa/port.c linux-5.4.137/net/dsa/port.c -+--- linux-5.4.137.old/net/dsa/port.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/port.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -174,6 +174,85 @@ void dsa_port_bridge_leave(struct dsa_po -+ dsa_port_set_state_now(dp, BR_STATE_FORWARDING); -+ } -+ -++int dsa_port_lag_change(struct dsa_port *dp, -++ struct netdev_lag_lower_state_info *linfo) -++{ -++ struct dsa_notifier_lag_info info = { -++ .sw_index = dp->ds->index, -++ .port = dp->index, -++ }; -++ bool tx_enabled; -++ -++ if (!dp->lag_dev) -++ return 0; -++ -++ /* On statically configured aggregates (e.g. loadbalance -++ * without LACP) ports will always be tx_enabled, even if the -++ * link is down. Thus we require both link_up and tx_enabled -++ * in order to include it in the tx set. -++ */ -++ tx_enabled = linfo->link_up && linfo->tx_enabled; -++ -++ if (tx_enabled == dp->lag_tx_enabled) -++ return 0; -++ -++ dp->lag_tx_enabled = tx_enabled; -++ -++ return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); -++} -++ -++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, -++ struct netdev_lag_upper_info *uinfo) -++{ -++ struct dsa_notifier_lag_info info = { -++ .sw_index = dp->ds->index, -++ .port = dp->index, -++ .lag = lag, -++ .info = uinfo, -++ }; -++ int err; -++ -++ dsa_lag_map(dp->ds->dst, lag); -++ dp->lag_dev = lag; -++ -++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); -++ if (err) { -++ dp->lag_dev = NULL; -++ dsa_lag_unmap(dp->ds->dst, lag); -++ } -++ -++ return err; -++} -++ -++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) -++{ -++ struct dsa_notifier_lag_info info = { -++ .sw_index = dp->ds->index, -++ .port = dp->index, -++ .lag = lag, -++ }; -++ int err; -++ -++ if (!dp->lag_dev) -++ return; -++ -++ /* Port might have been part of a LAG that in turn was -++ * attached to a bridge. -++ */ -++ if (dp->bridge_dev) -++ dsa_port_bridge_leave(dp, dp->bridge_dev); -++ -++ dp->lag_tx_enabled = false; -++ dp->lag_dev = NULL; -++ -++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); -++ if (err) -++ pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", -++ err); -++ -++ dsa_lag_unmap(dp->ds->dst, lag); -++} -++ -+ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, -+ bool vlan_filtering) -+ { -+diff -urpN linux-5.4.137.old/net/dsa/slave.c linux-5.4.137/net/dsa/slave.c -+--- linux-5.4.137.old/net/dsa/slave.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/slave.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -26,8 +26,6 @@ -+ -+ #include "dsa_priv.h" -+ -+-static bool dsa_slave_dev_check(const struct net_device *dev); -+- -+ /* slave mii_bus handling ***************************************************/ -+ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) -+ { -+@@ -286,6 +284,9 @@ static int dsa_slave_port_attr_set(struc -+ struct dsa_port *dp = dsa_slave_to_port(dev); -+ int ret; -+ -++ if (!dsa_port_offloads_netdev(dp, attr->orig_dev)) -++ return -EOPNOTSUPP; -++ -+ switch (attr->id) { -+ case SWITCHDEV_ATTR_ID_PORT_STP_STATE: -+ ret = dsa_port_set_state(dp, attr->u.stp_state, trans); -+@@ -323,7 +324,7 @@ static int dsa_slave_vlan_add(struct net -+ struct switchdev_obj_port_vlan vlan; -+ int err; -+ -+- if (obj->orig_dev != dev) -++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ -+ if (dsa_port_skip_vlan_configuration(dp)) -+@@ -363,7 +364,7 @@ static int dsa_slave_port_obj_add(struct -+ -+ switch (obj->id) { -+ case SWITCHDEV_OBJ_ID_PORT_MDB: -+- if (obj->orig_dev != dev) -++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); -+ break; -+@@ -390,7 +391,7 @@ static int dsa_slave_vlan_del(struct net -+ { -+ struct dsa_port *dp = dsa_slave_to_port(dev); -+ -+- if (obj->orig_dev != dev) -++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ -+ if (dsa_port_skip_vlan_configuration(dp)) -+@@ -410,7 +411,7 @@ static int dsa_slave_port_obj_del(struct -+ -+ switch (obj->id) { -+ case SWITCHDEV_OBJ_ID_PORT_MDB: -+- if (obj->orig_dev != dev) -++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); -+ break; -+@@ -1527,10 +1528,11 @@ void dsa_slave_destroy(struct net_device -+ free_netdev(slave_dev); -+ } -+ -+-static bool dsa_slave_dev_check(const struct net_device *dev) -++bool dsa_slave_dev_check(const struct net_device *dev) -+ { -+ return dev->netdev_ops == &dsa_slave_netdev_ops; -+ } -++EXPORT_SYMBOL_GPL(dsa_slave_dev_check); -+ -+ static int dsa_slave_changeupper(struct net_device *dev, -+ struct netdev_notifier_changeupper_info *info) -+@@ -1546,6 +1548,46 @@ static int dsa_slave_changeupper(struct -+ dsa_port_bridge_leave(dp, info->upper_dev); -+ err = NOTIFY_OK; -+ } -++ } else if (netif_is_lag_master(info->upper_dev)) { -++ if (info->linking) { -++ err = dsa_port_lag_join(dp, info->upper_dev, -++ info->upper_info); -++ if (err == -EOPNOTSUPP) { -++ NL_SET_ERR_MSG_MOD(info->info.extack, -++ "Offloading not supported"); -++ err = 0; -++ } -++ err = notifier_from_errno(err); -++ } else { -++ dsa_port_lag_leave(dp, info->upper_dev); -++ err = NOTIFY_OK; -++ } -++ } -++ -++ return err; -++} -++ -++static int -++dsa_slave_lag_changeupper(struct net_device *dev, -++ struct netdev_notifier_changeupper_info *info) -++{ -++ struct net_device *lower; -++ struct list_head *iter; -++ int err = NOTIFY_DONE; -++ struct dsa_port *dp; -++ -++ netdev_for_each_lower_dev(dev, lower, iter) { -++ if (!dsa_slave_dev_check(lower)) -++ continue; -++ -++ dp = dsa_slave_to_port(lower); -++ if (!dp->lag_dev) -++ /* Software LAG */ -++ continue; -++ -++ err = dsa_slave_changeupper(lower, info); -++ if (notifier_to_errno(err)) -++ break; -+ } -+ -+ return err; -+@@ -1588,11 +1630,33 @@ static int dsa_slave_netdevice_event(str -+ { -+ struct net_device *dev = netdev_notifier_info_to_dev(ptr); -+ -+- if (event == NETDEV_CHANGEUPPER) { -++ switch(event) { -++ case NETDEV_PRECHANGEUPPER: -+ if (!dsa_slave_dev_check(dev)) -+ return dsa_slave_upper_vlan_check(dev, ptr); -+ -+- return dsa_slave_changeupper(dev, ptr); -++ break; -++ case NETDEV_CHANGEUPPER: -++ if (dsa_slave_dev_check(dev)) -++ return dsa_slave_changeupper(dev, ptr); -++ -++ if (netif_is_lag_master(dev)) -++ return dsa_slave_lag_changeupper(dev, ptr); -++ -++ break; -++ case NETDEV_CHANGELOWERSTATE: { -++ struct netdev_notifier_changelowerstate_info *info = ptr; -++ struct dsa_port *dp; -++ int err; -++ -++ if (!dsa_slave_dev_check(dev)) -++ break; -++ -++ dp = dsa_slave_to_port(dev); -++ -++ err = dsa_port_lag_change(dp, info->lower_state_info); -++ return notifier_from_errno(err); -++ } -+ } -+ -+ return NOTIFY_DONE; -+diff -urpN linux-5.4.137.old/net/dsa/switch.c linux-5.4.137/net/dsa/switch.c -+--- linux-5.4.137.old/net/dsa/switch.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/switch.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -20,7 +20,7 @@ static unsigned int dsa_switch_fastest_a -+ int i; -+ -+ for (i = 0; i < ds->num_ports; ++i) { -+- struct dsa_port *dp = &ds->ports[i]; -++ struct dsa_port *dp = dsa_to_port(ds, i); -+ -+ if (dp->ageing_time && dp->ageing_time < ageing_time) -+ ageing_time = dp->ageing_time; -+@@ -98,7 +98,7 @@ static int dsa_switch_bridge_leave(struc -+ if (unset_vlan_filtering) { -+ struct switchdev_trans trans = {0}; -+ -+- err = dsa_port_vlan_filtering(&ds->ports[info->port], -++ err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), -+ false, &trans); -+ if (err && err != EOPNOTSUPP) -+ return err; -+@@ -128,6 +128,47 @@ static int dsa_switch_fdb_del(struct dsa -+ return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); -+ } -+ -++static int dsa_switch_lag_change(struct dsa_switch *ds, -++ struct dsa_notifier_lag_info *info) -++{ -++ if (ds->index == info->sw_index && ds->ops->port_lag_change) -++ return ds->ops->port_lag_change(ds, info->port); -++ -++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) -++ return ds->ops->crosschip_lag_change(ds, info->sw_index, -++ info->port); -++ -++ return 0; -++} -++ -++static int dsa_switch_lag_join(struct dsa_switch *ds, -++ struct dsa_notifier_lag_info *info) -++{ -++ if (ds->index == info->sw_index && ds->ops->port_lag_join) -++ return ds->ops->port_lag_join(ds, info->port, info->lag, -++ info->info); -++ -++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) -++ return ds->ops->crosschip_lag_join(ds, info->sw_index, -++ info->port, info->lag, -++ info->info); -++ -++ return 0; -++} -++ -++static int dsa_switch_lag_leave(struct dsa_switch *ds, -++ struct dsa_notifier_lag_info *info) -++{ -++ if (ds->index == info->sw_index && ds->ops->port_lag_leave) -++ return ds->ops->port_lag_leave(ds, info->port, info->lag); -++ -++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) -++ return ds->ops->crosschip_lag_leave(ds, info->sw_index, -++ info->port, info->lag); -++ -++ return 0; -++} -++ -+ static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, -+ struct dsa_notifier_mdb_info *info) -+ { -+@@ -316,6 +357,15 @@ static int dsa_switch_event(struct notif -+ case DSA_NOTIFIER_FDB_DEL: -+ err = dsa_switch_fdb_del(ds, info); -+ break; -++ case DSA_NOTIFIER_LAG_CHANGE: -++ err = dsa_switch_lag_change(ds, info); -++ break; -++ case DSA_NOTIFIER_LAG_JOIN: -++ err = dsa_switch_lag_join(ds, info); -++ break; -++ case DSA_NOTIFIER_LAG_LEAVE: -++ err = dsa_switch_lag_leave(ds, info); -++ break; -+ case DSA_NOTIFIER_MDB_ADD: -+ err = dsa_switch_mdb_add(ds, info); -+ break; -+diff -urpN linux-5.4.137.old/net/dsa/tag_8021q.c linux-5.4.137/net/dsa/tag_8021q.c -+--- linux-5.4.137.old/net/dsa/tag_8021q.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/tag_8021q.c 2021-08-04 14:05:57.643717592 +0700 -+@@ -31,15 +31,14 @@ -+ * Must be transmitted as zero and ignored on receive. -+ * -+ * SWITCH_ID - VID[8:6]: -+- * Index of switch within DSA tree. Must be between 0 and -+- * DSA_MAX_SWITCHES - 1. -++ * Index of switch within DSA tree. Must be between 0 and 7. -+ * -+ * RSV - VID[5:4]: -+ * To be used for further expansion of PORT or for other purposes. -+ * Must be transmitted as zero and ignored on receive. -+ * -+ * PORT - VID[3:0]: -+- * Index of switch port. Must be between 0 and DSA_MAX_PORTS - 1. -++ * Index of switch port. Must be between 0 and 15. -+ */ -+ -+ #define DSA_8021Q_DIR_SHIFT 10 -+@@ -103,7 +102,7 @@ static int dsa_8021q_restore_pvid(struct -+ if (!dsa_is_user_port(ds, port)) -+ return 0; -+ -+- slave = ds->ports[port].slave; -++ slave = dsa_to_port(ds, port)->slave; -+ -+ err = br_vlan_get_pvid(slave, &pvid); -+ if (!pvid || err < 0) -+@@ -118,7 +117,7 @@ static int dsa_8021q_restore_pvid(struct -+ return err; -+ } -+ -+- return dsa_port_vid_add(&ds->ports[port], pvid, vinfo.flags); -++ return dsa_port_vid_add(dsa_to_port(ds, port), pvid, vinfo.flags); -+ } -+ -+ /* If @enabled is true, installs @vid with @flags into the switch port's HW -+@@ -130,7 +129,7 @@ static int dsa_8021q_restore_pvid(struct -+ static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, -+ u16 flags, bool enabled) -+ { -+- struct dsa_port *dp = &ds->ports[port]; -++ struct dsa_port *dp = dsa_to_port(ds, port); -+ struct bridge_vlan_info vinfo; -+ int err; -+ -+diff -urpN linux-5.4.137.old/net/dsa/tag_dsa.c linux-5.4.137/net/dsa/tag_dsa.c -+--- linux-5.4.137.old/net/dsa/tag_dsa.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/tag_dsa.c 2021-08-04 14:05:53.891713717 +0700 -+@@ -1,7 +1,48 @@ -+ // SPDX-License-Identifier: GPL-2.0+ -+ /* -+- * net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging -++ * Regular and Ethertype DSA tagging -+ * Copyright (c) 2008-2009 Marvell Semiconductor -++ * -++ * Regular DSA -++ * ----------- -++ -++ * For untagged (in 802.1Q terms) packets, the switch will splice in -++ * the tag between the SA and the ethertype of the original -++ * packet. Tagged frames will instead have their outermost .1Q tag -++ * converted to a DSA tag. It expects the same layout when receiving -++ * packets from the CPU. -++ * -++ * Example: -++ * -++ * .----.----.----.--------- -++ * Pu: | DA | SA | ET | Payload ... -++ * '----'----'----'--------- -++ * 6 6 2 N -++ * .----.----.--------.-----.----.--------- -++ * Pt: | DA | SA | 0x8100 | TCI | ET | Payload ... -++ * '----'----'--------'-----'----'--------- -++ * 6 6 2 2 2 N -++ * .----.----.-----.----.--------- -++ * Pd: | DA | SA | DSA | ET | Payload ... -++ * '----'----'-----'----'--------- -++ * 6 6 4 2 N -++ * -++ * No matter if a packet is received untagged (Pu) or tagged (Pt), -++ * they will both have the same layout (Pd) when they are sent to the -++ * CPU. This is done by ignoring 802.3, replacing the ethertype field -++ * with more metadata, among which is a bit to signal if the original -++ * packet was tagged or not. -++ * -++ * Ethertype DSA -++ * ------------- -++ * Uses the exact same tag format as regular DSA, but also includes a -++ * proper ethertype field (which the mv88e6xxx driver sets to -++ * ETH_P_EDSA/0xdada) followed by two zero bytes: -++ * -++ * .----.----.--------.--------.-----.----.--------- -++ * | DA | SA | 0xdada | 0x0000 | DSA | ET | Payload ... -++ * '----'----'--------'--------'-----'----'--------- -++ * 6 6 2 2 4 2 N -+ */ -+ -+ #include -+@@ -12,16 +53,81 @@ -+ -+ #define DSA_HLEN 4 -+ -+-static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) -++/** -++ * enum dsa_cmd - DSA Command -++ * @DSA_CMD_TO_CPU: Set on packets that were trapped or mirrored to -++ * the CPU port. This is needed to implement control protocols, -++ * e.g. STP and LLDP, that must not allow those control packets to -++ * be switched according to the normal rules. -++ * @DSA_CMD_FROM_CPU: Used by the CPU to send a packet to a specific -++ * port, ignoring all the barriers that the switch normally -++ * enforces (VLANs, STP port states etc.). No source address -++ * learning takes place. "sudo send packet" -++ * @DSA_CMD_TO_SNIFFER: Set on the copies of packets that matched some -++ * user configured ingress or egress monitor criteria. These are -++ * forwarded by the switch tree to the user configured ingress or -++ * egress monitor port, which can be set to the CPU port or a -++ * regular port. If the destination is a regular port, the tag -++ * will be removed before egressing the port. If the destination -++ * is the CPU port, the tag will not be removed. -++ * @DSA_CMD_FORWARD: This tag is used on all bulk traffic passing -++ * through the switch tree, including the flows that are directed -++ * towards the CPU. Its device/port tuple encodes the original -++ * source port on which the packet ingressed. It can also be used -++ * on transmit by the CPU to defer the forwarding decision to the -++ * hardware, based on the current config of PVT/VTU/ATU -++ * etc. Source address learning takes places if enabled on the -++ * receiving DSA/CPU port. -++ */ -++enum dsa_cmd { -++ DSA_CMD_TO_CPU = 0, -++ DSA_CMD_FROM_CPU = 1, -++ DSA_CMD_TO_SNIFFER = 2, -++ DSA_CMD_FORWARD = 3 -++}; -++ -++/** -++ * enum dsa_code - TO_CPU Code -++ * -++ * @DSA_CODE_MGMT_TRAP: DA was classified as a management -++ * address. Typical examples include STP BPDUs and LLDP. -++ * @DSA_CODE_FRAME2REG: Response to a "remote management" request. -++ * @DSA_CODE_IGMP_MLD_TRAP: IGMP/MLD signaling. -++ * @DSA_CODE_POLICY_TRAP: Frame matched some policy configuration on -++ * the device. Typical examples are matching on DA/SA/VID and DHCP -++ * snooping. -++ * @DSA_CODE_ARP_MIRROR: The name says it all really. -++ * @DSA_CODE_POLICY_MIRROR: Same as @DSA_CODE_POLICY_TRAP, but the -++ * particular policy was set to trigger a mirror instead of a -++ * trap. -++ * @DSA_CODE_RESERVED_6: Unused on all devices up to at least 6393X. -++ * @DSA_CODE_RESERVED_7: Unused on all devices up to at least 6393X. -++ * -++ * A 3-bit code is used to relay why a particular frame was sent to -++ * the CPU. We only use this to determine if the packet was mirrored -++ * or trapped, i.e. whether the packet has been forwarded by hardware -++ * or not. -++ * -++ * This is the superset of all possible codes. Any particular device -++ * may only implement a subset. -++ */ -++enum dsa_code { -++ DSA_CODE_MGMT_TRAP = 0, -++ DSA_CODE_FRAME2REG = 1, -++ DSA_CODE_IGMP_MLD_TRAP = 2, -++ DSA_CODE_POLICY_TRAP = 3, -++ DSA_CODE_ARP_MIRROR = 4, -++ DSA_CODE_POLICY_MIRROR = 5, -++ DSA_CODE_RESERVED_6 = 6, -++ DSA_CODE_RESERVED_7 = 7 -++}; -++ -++static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, -++ u8 extra) -+ { -+ struct dsa_port *dp = dsa_slave_to_port(dev); -+ u8 *dsa_header; -+ -+- /* -+- * Convert the outermost 802.1q tag to a DSA tag for tagged -+- * packets, or insert a DSA tag between the addresses and -+- * the ethertype field for untagged packets. -+- */ -+ if (skb->protocol == htons(ETH_P_8021Q)) { -+ if (skb_cow_head(skb, 0) < 0) -+ return NULL; -+@@ -29,8 +135,13 @@ static struct sk_buff *dsa_xmit(struct s -+ /* -+ * Construct tagged FROM_CPU DSA tag from 802.1q tag. -+ */ -+- dsa_header = skb->data + 2 * ETH_ALEN; -+- dsa_header[0] = 0x60 | dp->ds->index; -++ if (extra) { -++ skb_push(skb, extra); -++ memmove(skb->data, skb->data + extra, 2 * ETH_ALEN); -++ } -++ -++ dsa_header = skb->data + 2 * ETH_ALEN + extra; -++ dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | 0x20 | dp->ds->index; -+ dsa_header[1] = dp->index << 3; -+ -+ /* -+@@ -43,15 +154,12 @@ static struct sk_buff *dsa_xmit(struct s -+ } else { -+ if (skb_cow_head(skb, DSA_HLEN) < 0) -+ return NULL; -+- skb_push(skb, DSA_HLEN); -++ skb_push(skb, DSA_HLEN + extra); -++ memmove(skb->data, skb->data + DSA_HLEN + extra, 2 * ETH_ALEN); -+ -+- memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); -+- -+- /* -+- * Construct untagged FROM_CPU DSA tag. -+- */ -+- dsa_header = skb->data + 2 * ETH_ALEN; -+- dsa_header[0] = 0x40 | dp->ds->index; -++ /* Construct untagged FROM_CPU DSA tag. */ -++ dsa_header = skb->data + 2 * ETH_ALEN + extra; -++ dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | dp->ds->index; -+ dsa_header[1] = dp->index << 3; -+ dsa_header[2] = 0x00; -+ dsa_header[3] = 0x00; -+@@ -60,47 +168,91 @@ static struct sk_buff *dsa_xmit(struct s -+ return skb; -+ } -+ -+-static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, -+- struct packet_type *pt) -++static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, -++ u8 extra) -+ { -++ int source_device, source_port; -++ bool trunk = false; -++ enum dsa_code code; -++ enum dsa_cmd cmd; -+ u8 *dsa_header; -+- int source_device; -+- int source_port; -+ -+- if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) -+- return NULL; -+- -+- /* -+- * The ethertype field is part of the DSA header. -+- */ -++ /* The ethertype field is part of the DSA header. */ -+ dsa_header = skb->data - 2; -+ -+- /* -+- * Check that frame type is either TO_CPU or FORWARD. -+- */ -+- if ((dsa_header[0] & 0xc0) != 0x00 && (dsa_header[0] & 0xc0) != 0xc0) -++ cmd = dsa_header[0] >> 6; -++ switch (cmd) { -++ case DSA_CMD_FORWARD: -++ skb->offload_fwd_mark = 1; -++ -++ trunk = !!(dsa_header[1] & 7); -++ break; -++ -++ case DSA_CMD_TO_CPU: -++ code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1); -++ -++ switch (code) { -++ case DSA_CODE_FRAME2REG: -++ /* Remote management is not implemented yet, -++ * drop. -++ */ -++ return NULL; -++ case DSA_CODE_ARP_MIRROR: -++ case DSA_CODE_POLICY_MIRROR: -++ /* Mark mirrored packets to notify any upper -++ * device (like a bridge) that forwarding has -++ * already been done by hardware. -++ */ -++ skb->offload_fwd_mark = 1; -++ break; -++ case DSA_CODE_MGMT_TRAP: -++ case DSA_CODE_IGMP_MLD_TRAP: -++ case DSA_CODE_POLICY_TRAP: -++ /* Traps have, by definition, not been -++ * forwarded by hardware, so don't mark them. -++ */ -++ break; -++ default: -++ /* Reserved code, this could be anything. Drop -++ * seems like the safest option. -++ */ -++ return NULL; -++ } -++ -++ break; -++ -++ default: -+ return NULL; -++ } -+ -+- /* -+- * Determine source device and port. -+- */ -+ source_device = dsa_header[0] & 0x1f; -+ source_port = (dsa_header[1] >> 3) & 0x1f; -+ -+- skb->dev = dsa_master_find_slave(dev, source_device, source_port); -++ if (trunk) { -++ struct dsa_port *cpu_dp = dev->dsa_ptr; -++ -++ /* The exact source port is not available in the tag, -++ * so we inject the frame directly on the upper -++ * team/bond. -++ */ -++ skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); -++ } else { -++ skb->dev = dsa_master_find_slave(dev, source_device, -++ source_port); -++ } -++ -+ if (!skb->dev) -+ return NULL; -+ -+- /* -+- * Convert the DSA header to an 802.1q header if the 'tagged' -+- * bit in the DSA header is set. If the 'tagged' bit is clear, -+- * delete the DSA header entirely. -++ /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q -++ * tag, and delete the ethertype (extra) if applicable. If the -++ * 'tagged' bit is cleared; delete the DSA tag, and ethertype -++ * if applicable. -+ */ -+ if (dsa_header[0] & 0x20) { -+ u8 new_header[4]; -+ -+- /* -+- * Insert 802.1q ethertype and copy the VLAN-related -++ /* Insert 802.1Q ethertype and copy the VLAN-related -+ * fields, but clear the bit that will hold CFI (since -+ * DSA uses that bit location for another purpose). -+ */ -+@@ -109,16 +261,13 @@ static struct sk_buff *dsa_rcv(struct sk -+ new_header[2] = dsa_header[2] & ~0x10; -+ new_header[3] = dsa_header[3]; -+ -+- /* -+- * Move CFI bit from its place in the DSA header to -+- * its 802.1q-designated place. -++ /* Move CFI bit from its place in the DSA header to -++ * its 802.1Q-designated place. -+ */ -+ if (dsa_header[1] & 0x01) -+ new_header[2] |= 0x10; -+ -+- /* -+- * Update packet checksum if skb is CHECKSUM_COMPLETE. -+- */ -++ /* Update packet checksum if skb is CHECKSUM_COMPLETE. */ -+ if (skb->ip_summed == CHECKSUM_COMPLETE) { -+ __wsum c = skb->csum; -+ c = csum_add(c, csum_partial(new_header + 2, 2, 0)); -+@@ -126,19 +275,20 @@ static struct sk_buff *dsa_rcv(struct sk -+ skb->csum = c; -+ } -+ -++ -+ memcpy(dsa_header, new_header, DSA_HLEN); -++ -++ if (extra) -++ memmove(skb->data - ETH_HLEN, -++ skb->data - ETH_HLEN - extra, -++ 2 * ETH_ALEN); -+ } else { -+- /* -+- * Remove DSA tag and update checksum. -+- */ -+ skb_pull_rcsum(skb, DSA_HLEN); -+ memmove(skb->data - ETH_HLEN, -+- skb->data - ETH_HLEN - DSA_HLEN, -++ skb->data - ETH_HLEN - DSA_HLEN - extra, -+ 2 * ETH_ALEN); -+ } -+ -+- skb->offload_fwd_mark = 1; -+- -+ return skb; -+ } -+ -+@@ -150,16 +300,88 @@ static int dsa_tag_flow_dissect(const st -+ return 0; -+ } -+ -++#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) -++ -++static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) -++{ -++ return dsa_xmit_ll(skb, dev, 0); -++} -++ -++static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, -++ struct packet_type *pt) -++{ -++ if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) -++ return NULL; -++ -++ return dsa_rcv_ll(skb, dev, 0); -++} -++ -+ static const struct dsa_device_ops dsa_netdev_ops = { -+- .name = "dsa", -+- .proto = DSA_TAG_PROTO_DSA, -+- .xmit = dsa_xmit, -+- .rcv = dsa_rcv, -++ .name = "dsa", -++ .proto = DSA_TAG_PROTO_DSA, -++ .xmit = dsa_xmit, -++ .rcv = dsa_rcv, -+ .flow_dissect = dsa_tag_flow_dissect, -+ .overhead = DSA_HLEN, -+ }; -+ -+-MODULE_LICENSE("GPL"); -++DSA_TAG_DRIVER(dsa_netdev_ops); -+ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA); -+ -+-module_dsa_tag_driver(dsa_netdev_ops); -++#endif /* CONFIG_NET_DSA_TAG_DSA */ -++ -++#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) -++ -++#define EDSA_HLEN 8 -++ -++static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) -++{ -++ u8 *edsa_header; -++ -++ skb = dsa_xmit_ll(skb, dev, EDSA_HLEN - DSA_HLEN); -++ if (!skb) -++ return NULL; -++ -++ edsa_header = skb->data + 2 * ETH_ALEN; -++ edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; -++ edsa_header[1] = ETH_P_EDSA & 0xff; -++ edsa_header[2] = 0x00; -++ edsa_header[3] = 0x00; -++ return skb; -++} -++ -++static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, -++ struct packet_type *pt) -++{ -++ if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) -++ return NULL; -++ -++ skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); -++ -++ return dsa_rcv_ll(skb, dev, EDSA_HLEN - DSA_HLEN); -++} -++ -++static const struct dsa_device_ops edsa_netdev_ops = { -++ .name = "edsa", -++ .proto = DSA_TAG_PROTO_EDSA, -++ .xmit = edsa_xmit, -++ .rcv = edsa_rcv, -++ .overhead = EDSA_HLEN, -++}; -++ -++DSA_TAG_DRIVER(edsa_netdev_ops); -++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); -++#endif /* CONFIG_NET_DSA_TAG_EDSA */ -++ -++static struct dsa_tag_driver *dsa_tag_drivers[] = { -++#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) -++ &DSA_TAG_DRIVER_NAME(dsa_netdev_ops), -++#endif -++#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) -++ &DSA_TAG_DRIVER_NAME(edsa_netdev_ops), -++#endif -++}; -++ -++module_dsa_tag_drivers(dsa_tag_drivers); -++ -++MODULE_LICENSE("GPL"); -+diff -urpN linux-5.4.137.old/net/dsa/tag_edsa.c linux-5.4.137/net/dsa/tag_edsa.c -+--- linux-5.4.137.old/net/dsa/tag_edsa.c 2021-08-04 14:05:38.059697353 +0700 -++++ linux-5.4.137/net/dsa/tag_edsa.c 1970-01-01 07:00:00.000000000 +0700 -+@@ -1,215 +0,0 @@ -+-// SPDX-License-Identifier: GPL-2.0+ -+-/* -+- * net/dsa/tag_edsa.c - Ethertype DSA tagging -+- * Copyright (c) 2008-2009 Marvell Semiconductor -+- */ -+- -+-#include -+-#include -+-#include -+- -+-#include "dsa_priv.h" -+- -+-#define DSA_HLEN 4 -+-#define EDSA_HLEN 8 -+- -+-#define FRAME_TYPE_TO_CPU 0x00 -+-#define FRAME_TYPE_FORWARD 0x03 -+- -+-#define TO_CPU_CODE_MGMT_TRAP 0x00 -+-#define TO_CPU_CODE_FRAME2REG 0x01 -+-#define TO_CPU_CODE_IGMP_MLD_TRAP 0x02 -+-#define TO_CPU_CODE_POLICY_TRAP 0x03 -+-#define TO_CPU_CODE_ARP_MIRROR 0x04 -+-#define TO_CPU_CODE_POLICY_MIRROR 0x05 -+- -+-static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) -+-{ -+- struct dsa_port *dp = dsa_slave_to_port(dev); -+- u8 *edsa_header; -+- -+- /* -+- * Convert the outermost 802.1q tag to a DSA tag and prepend -+- * a DSA ethertype field is the packet is tagged, or insert -+- * a DSA ethertype plus DSA tag between the addresses and the -+- * current ethertype field if the packet is untagged. -+- */ -+- if (skb->protocol == htons(ETH_P_8021Q)) { -+- if (skb_cow_head(skb, DSA_HLEN) < 0) -+- return NULL; -+- skb_push(skb, DSA_HLEN); -+- -+- memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); -+- -+- /* -+- * Construct tagged FROM_CPU DSA tag from 802.1q tag. -+- */ -+- edsa_header = skb->data + 2 * ETH_ALEN; -+- edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; -+- edsa_header[1] = ETH_P_EDSA & 0xff; -+- edsa_header[2] = 0x00; -+- edsa_header[3] = 0x00; -+- edsa_header[4] = 0x60 | dp->ds->index; -+- edsa_header[5] = dp->index << 3; -+- -+- /* -+- * Move CFI field from byte 6 to byte 5. -+- */ -+- if (edsa_header[6] & 0x10) { -+- edsa_header[5] |= 0x01; -+- edsa_header[6] &= ~0x10; -+- } -+- } else { -+- if (skb_cow_head(skb, EDSA_HLEN) < 0) -+- return NULL; -+- skb_push(skb, EDSA_HLEN); -+- -+- memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN); -+- -+- /* -+- * Construct untagged FROM_CPU DSA tag. -+- */ -+- edsa_header = skb->data + 2 * ETH_ALEN; -+- edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; -+- edsa_header[1] = ETH_P_EDSA & 0xff; -+- edsa_header[2] = 0x00; -+- edsa_header[3] = 0x00; -+- edsa_header[4] = 0x40 | dp->ds->index; -+- edsa_header[5] = dp->index << 3; -+- edsa_header[6] = 0x00; -+- edsa_header[7] = 0x00; -+- } -+- -+- return skb; -+-} -+- -+-static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, -+- struct packet_type *pt) -+-{ -+- u8 *edsa_header; -+- int frame_type; -+- int code; -+- int source_device; -+- int source_port; -+- -+- if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) -+- return NULL; -+- -+- /* -+- * Skip the two null bytes after the ethertype. -+- */ -+- edsa_header = skb->data + 2; -+- -+- /* -+- * Check that frame type is either TO_CPU or FORWARD. -+- */ -+- frame_type = edsa_header[0] >> 6; -+- -+- switch (frame_type) { -+- case FRAME_TYPE_TO_CPU: -+- code = (edsa_header[1] & 0x6) | ((edsa_header[2] >> 4) & 1); -+- -+- /* -+- * Mark the frame to never egress on any port of the same switch -+- * unless it's a trapped IGMP/MLD packet, in which case the -+- * bridge might want to forward it. -+- */ -+- if (code != TO_CPU_CODE_IGMP_MLD_TRAP) -+- skb->offload_fwd_mark = 1; -+- -+- break; -+- -+- case FRAME_TYPE_FORWARD: -+- skb->offload_fwd_mark = 1; -+- break; -+- -+- default: -+- return NULL; -+- } -+- -+- /* -+- * Determine source device and port. -+- */ -+- source_device = edsa_header[0] & 0x1f; -+- source_port = (edsa_header[1] >> 3) & 0x1f; -+- -+- skb->dev = dsa_master_find_slave(dev, source_device, source_port); -+- if (!skb->dev) -+- return NULL; -+- -+- /* -+- * If the 'tagged' bit is set, convert the DSA tag to a 802.1q -+- * tag and delete the ethertype part. If the 'tagged' bit is -+- * clear, delete the ethertype and the DSA tag parts. -+- */ -+- if (edsa_header[0] & 0x20) { -+- u8 new_header[4]; -+- -+- /* -+- * Insert 802.1q ethertype and copy the VLAN-related -+- * fields, but clear the bit that will hold CFI (since -+- * DSA uses that bit location for another purpose). -+- */ -+- new_header[0] = (ETH_P_8021Q >> 8) & 0xff; -+- new_header[1] = ETH_P_8021Q & 0xff; -+- new_header[2] = edsa_header[2] & ~0x10; -+- new_header[3] = edsa_header[3]; -+- -+- /* -+- * Move CFI bit from its place in the DSA header to -+- * its 802.1q-designated place. -+- */ -+- if (edsa_header[1] & 0x01) -+- new_header[2] |= 0x10; -+- -+- skb_pull_rcsum(skb, DSA_HLEN); -+- -+- /* -+- * Update packet checksum if skb is CHECKSUM_COMPLETE. -+- */ -+- if (skb->ip_summed == CHECKSUM_COMPLETE) { -+- __wsum c = skb->csum; -+- c = csum_add(c, csum_partial(new_header + 2, 2, 0)); -+- c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0)); -+- skb->csum = c; -+- } -+- -+- memcpy(edsa_header, new_header, DSA_HLEN); -+- -+- memmove(skb->data - ETH_HLEN, -+- skb->data - ETH_HLEN - DSA_HLEN, -+- 2 * ETH_ALEN); -+- } else { -+- /* -+- * Remove DSA tag and update checksum. -+- */ -+- skb_pull_rcsum(skb, EDSA_HLEN); -+- memmove(skb->data - ETH_HLEN, -+- skb->data - ETH_HLEN - EDSA_HLEN, -+- 2 * ETH_ALEN); -+- } -+- -+- return skb; -+-} -+- -+-static int edsa_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto, -+- int *offset) -+-{ -+- *offset = 8; -+- *proto = ((__be16 *)skb->data)[3]; -+- return 0; -+-} -+- -+-static const struct dsa_device_ops edsa_netdev_ops = { -+- .name = "edsa", -+- .proto = DSA_TAG_PROTO_EDSA, -+- .xmit = edsa_xmit, -+- .rcv = edsa_rcv, -+- .flow_dissect = edsa_tag_flow_dissect, -+- .overhead = EDSA_HLEN, -+-}; -+- -+-MODULE_LICENSE("GPL"); -+-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); -+- -+-module_dsa_tag_driver(edsa_netdev_ops); -diff --git a/target/linux/generic/backport-5.4/871-brflood-api.patch b/target/linux/generic/backport-5.4/871-brflood-api.patch -new file mode 100644 -index 0000000000..0a7f1986ea ---- /dev/null -+++ b/target/linux/generic/backport-5.4/871-brflood-api.patch -@@ -0,0 +1,817 @@ -+Index: linux-5.4.111/drivers/net/dsa/b53/b53_common.c -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/b53/b53_common.c -++++ linux-5.4.111/drivers/net/dsa/b53/b53_common.c -+@@ -527,6 +527,39 @@ static void b53_port_set_learning(struct -+ b53_write16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, reg); -+ } -+ -++static void b53_port_set_ucast_flood(struct b53_device *dev, int port, -++ bool unicast) -++{ -++ u16 uc; -++ -++ b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); -++ if (unicast) -++ uc |= BIT(port); -++ else -++ uc &= ~BIT(port); -++ b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); -++} -++ -++static void b53_port_set_mcast_flood(struct b53_device *dev, int port, -++ bool multicast) -++{ -++ u16 mc; -++ -++ b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); -++ if (multicast) -++ mc |= BIT(port); -++ else -++ mc &= ~BIT(port); -++ b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); -++ -++ b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); -++ if (multicast) -++ mc |= BIT(port); -++ else -++ mc &= ~BIT(port); -++ b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); -++} -++ -+ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) -+ { -+ struct b53_device *dev = ds->priv; -+@@ -539,7 +572,8 @@ int b53_enable_port(struct dsa_switch *d -+ -+ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -+ -+- b53_br_egress_floods(ds, port, true, true); -++ b53_port_set_ucast_flood(dev, port, true); -++ b53_port_set_mcast_flood(dev, port, true); -+ b53_port_set_learning(dev, port, false); -+ -+ if (dev->ops->irq_enable) -+@@ -658,7 +692,8 @@ static void b53_enable_cpu_port(struct b -+ -+ b53_brcm_hdr_setup(dev->ds, port); -+ -+- b53_br_egress_floods(dev->ds, port, true, true); -++ b53_port_set_ucast_flood(dev, port, true); -++ b53_port_set_mcast_flood(dev, port, true); -+ b53_port_set_learning(dev, port, false); -+ } -+ -+@@ -1808,37 +1843,37 @@ void b53_br_fast_age(struct dsa_switch * -+ } -+ EXPORT_SYMBOL(b53_br_fast_age); -+ -+-int b53_br_egress_floods(struct dsa_switch *ds, int port, -+- bool unicast, bool multicast) -++static int b53_br_flags_pre(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack) -+ { -+- struct b53_device *dev = ds->priv; -+- u16 uc, mc; -+- -+- b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); -+- if (unicast) -+- uc |= BIT(port); -+- else -+- uc &= ~BIT(port); -+- b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); -++ if (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)) -++ return -EINVAL; -+ -+- b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); -+- if (multicast) -+- mc |= BIT(port); -+- else -+- mc &= ~BIT(port); -+- b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); -++ return 0; -++} -+ -+- b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); -+- if (multicast) -+- mc |= BIT(port); -+- else -+- mc &= ~BIT(port); -+- b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); -++static int b53_br_flags(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack) -++{ -++ if (flags & BR_FLOOD) -++ b53_port_set_ucast_flood(ds->priv, port, -++ !!(flags.val & BR_FLOOD)); -++ if (flags & BR_MCAST_FLOOD) -++ b53_port_set_mcast_flood(ds->priv, port, -++ !!(flags.val & BR_MCAST_FLOOD)); -+ -+ return 0; -++} -+ -++static int b53_set_mrouter(struct dsa_switch *ds, int port, bool mrouter, -++ struct netlink_ext_ack *extack) -++{ -++ b53_port_set_mcast_flood(ds->priv, port, mrouter); -++ -++ return 0; -+ } -+-EXPORT_SYMBOL(b53_br_egress_floods); -+ -+ static bool b53_possible_cpu_port(struct dsa_switch *ds, int port) -+ { -+@@ -2037,9 +2072,11 @@ static const struct dsa_switch_ops b53_s -+ .set_mac_eee = b53_set_mac_eee, -+ .port_bridge_join = b53_br_join, -+ .port_bridge_leave = b53_br_leave, -++ .port_pre_bridge_flags = b53_br_flags_pre, -++ .port_bridge_flags = b53_br_flags, -++ .port_set_mrouter = b53_set_mrouter, -+ .port_stp_state_set = b53_br_set_stp_state, -+ .port_fast_age = b53_br_fast_age, -+- .port_egress_floods = b53_br_egress_floods, -+ .port_vlan_filtering = b53_vlan_filtering, -+ .port_vlan_prepare = b53_vlan_prepare, -+ .port_vlan_add = b53_vlan_add, -+Index: linux-5.4.111/drivers/net/dsa/b53/b53_priv.h -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/b53/b53_priv.h -++++ linux-5.4.111/drivers/net/dsa/b53/b53_priv.h -+@@ -319,8 +319,6 @@ int b53_br_join(struct dsa_switch *ds, i -+ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge); -+ void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state); -+ void b53_br_fast_age(struct dsa_switch *ds, int port); -+-int b53_br_egress_floods(struct dsa_switch *ds, int port, -+- bool unicast, bool multicast); -+ void b53_port_event(struct dsa_switch *ds, int port); -+ void b53_phylink_validate(struct dsa_switch *ds, int port, -+ unsigned long *supported, -+Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.c -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/chip.c -++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.c -+@@ -2274,12 +2274,20 @@ static int mv88e6xxx_setup_egress_floods -+ { -+ struct dsa_switch *ds = chip->ds; -+ bool flood; -++ int err; -+ -+ /* Upstream ports flood frames with unknown unicast or multicast DA */ -+ flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port); -+- if (chip->info->ops->port_set_egress_floods) -+- return chip->info->ops->port_set_egress_floods(chip, port, -+- flood, flood); -++ if (chip->info->ops->port_set_ucast_flood) { -++ err = chip->info->ops->port_set_ucast_flood(chip, port, flood); -++ if (err) -++ return err; -++ } -++ if (chip->info->ops->port_set_mcast_flood) { -++ err = chip->info->ops->port_set_mcast_flood(chip, port, flood); -++ if (err) -++ return err; -++ } -+ -+ return 0; -+ } -+@@ -3019,7 +3027,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+ .port_pause_limit = mv88e6097_port_pause_limit, -+@@ -3058,7 +3067,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_duplex = mv88e6xxx_port_set_duplex, -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_set_frame_mode = mv88e6085_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6185_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, -++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, -+ .port_set_upstream_port = mv88e6095_port_set_upstream_port, -+ .port_link_state = mv88e6185_port_link_state, -+ .port_get_cmode = mv88e6185_port_get_cmode, -+@@ -3090,7 +3100,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, -+ .port_pause_limit = mv88e6097_port_pause_limit, -+@@ -3128,7 +3139,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_duplex = mv88e6xxx_port_set_duplex, -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_set_frame_mode = mv88e6085_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, -+ .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -+ .port_link_state = mv88e6352_port_link_state, -+@@ -3162,7 +3174,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6185_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, -++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_upstream_port = mv88e6095_port_set_upstream_port, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+@@ -3207,7 +3220,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_max_speed_mode = mv88e6341_port_max_speed_mode, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3253,7 +3267,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3329,7 +3344,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3372,7 +3388,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3416,7 +3433,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3459,7 +3477,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3503,7 +3522,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_duplex = mv88e6xxx_port_set_duplex, -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_set_frame_mode = mv88e6085_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6185_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, -++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, -+ .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, -+ .port_set_upstream_port = mv88e6095_port_set_upstream_port, -+ .port_set_pause = mv88e6185_port_set_pause, -+@@ -3545,7 +3565,6 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_pause_limit = mv88e6390_port_pause_limit, -+ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, -+@@ -3594,7 +3613,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_pause_limit = mv88e6390_port_pause_limit, -+ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, -+@@ -3642,7 +3662,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_max_speed_mode = mv88e6390_port_max_speed_mode, -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_pause_limit = mv88e6390_port_pause_limit, -+ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, -+@@ -3692,7 +3713,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3743,7 +3765,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6250_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+ .port_pause_limit = mv88e6097_port_pause_limit, -+@@ -3784,7 +3807,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_pause_limit = mv88e6390_port_pause_limit, -+ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, -+@@ -3833,7 +3857,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3877,7 +3902,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3921,7 +3947,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_max_speed_mode = mv88e6341_port_max_speed_mode, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -3970,7 +3997,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_set_speed = mv88e6185_port_set_speed, -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -4055,7 +4083,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6095_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -4110,7 +4139,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -4163,7 +4193,8 @@ static const struct mv88e6xxx_ops mv88e6 -+ .port_tag_remap = mv88e6390_port_tag_remap, -+ .port_set_policy = mv88e6352_port_set_policy, -+ .port_set_frame_mode = mv88e6351_port_set_frame_mode, -+- .port_set_egress_floods = mv88e6352_port_set_egress_floods, -++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, -++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, -+ .port_set_ether_type = mv88e6351_port_set_ether_type, -+ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, -+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, -+@@ -5016,17 +5047,72 @@ static void mv88e6xxx_port_mirror_del(st -+ mutex_unlock(&chip->reg_lock); -+ } -+ -+-static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port, -+- bool unicast, bool multicast) -++static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ const struct mv88e6xxx_ops *ops; -++ -++ if (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)) -++ return -EINVAL; -++ -++ ops = chip->info->ops; -++ -++ if ((flags & BR_FLOOD) && !ops->port_set_ucast_flood) -++ return -EINVAL; -++ -++ if ((flags & BR_MCAST_FLOOD) && !ops->port_set_mcast_flood) -++ return -EINVAL; -++ -++ return 0; -++} -++ -++static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack) -+ { -+ struct mv88e6xxx_chip *chip = ds->priv; -+ int err = -EOPNOTSUPP; -+ -+ mv88e6xxx_reg_lock(chip); -+- if (chip->info->ops->port_set_egress_floods) -+- err = chip->info->ops->port_set_egress_floods(chip, port, -+- unicast, -+- multicast); -++ -++ if (flags & BR_FLOOD) { -++ bool unicast = !!(flags.val & BR_FLOOD); -++ -++ err = chip->info->ops->port_set_ucast_flood(chip, port, -++ unicast); -++ if (err) -++ goto out; -++ } -++ -++ if (flags & BR_MCAST_FLOOD) { -++ bool multicast = !!(flags.val & BR_MCAST_FLOOD); -++ -++ err = chip->info->ops->port_set_mcast_flood(chip, port, -++ multicast); -++ if (err) -++ goto out; -++ } -++ -++out: -++ mv88e6xxx_reg_unlock(chip); -++ -++ return err; -++} -++ -++static int mv88e6xxx_port_set_mrouter(struct dsa_switch *ds, int port, -++ bool mrouter, -++ struct netlink_ext_ack *extack) -++{ -++ struct mv88e6xxx_chip *chip = ds->priv; -++ int err; -++ -++ if (!chip->info->ops->port_set_mcast_flood) -++ return -EOPNOTSUPP; -++ -++ mv88e6xxx_reg_lock(chip); -++ err = chip->info->ops->port_set_mcast_flood(chip, port, mrouter); -+ mv88e6xxx_reg_unlock(chip); -+ -+ return err; -+@@ -5322,7 +5408,9 @@ static const struct dsa_switch_ops mv88e -+ .set_ageing_time = mv88e6xxx_set_ageing_time, -+ .port_bridge_join = mv88e6xxx_port_bridge_join, -+ .port_bridge_leave = mv88e6xxx_port_bridge_leave, -+- .port_egress_floods = mv88e6xxx_port_egress_floods, -++ .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags, -++ .port_bridge_flags = mv88e6xxx_port_bridge_flags, -++ .port_set_mrouter = mv88e6xxx_port_set_mrouter, -+ .port_stp_state_set = mv88e6xxx_port_stp_state_set, -+ .port_fast_age = mv88e6xxx_port_fast_age, -+ .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, -+Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.h -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/chip.h -++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.h -+@@ -429,8 +429,10 @@ struct mv88e6xxx_ops { -+ -+ int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, -+ enum mv88e6xxx_frame_mode mode); -+- int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port, -+- bool unicast, bool multicast); -++ int (*port_set_ucast_flood)(struct mv88e6xxx_chip *chip, int port, -++ bool unicast); -++ int (*port_set_mcast_flood)(struct mv88e6xxx_chip *chip, int port, -++ bool multicast); -+ int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, -+ u16 etype); -+ int (*port_set_jumbo_size)(struct mv88e6xxx_chip *chip, int port, -+Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.c -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/port.c -++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.c -+@@ -932,8 +932,8 @@ int mv88e6351_port_set_frame_mode(struct -+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); -+ } -+ -+-static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, -+- int port, bool unicast) -++int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, -++ int port, bool unicast) -+ { -+ int err; -+ u16 reg; -+@@ -950,8 +950,8 @@ static int mv88e6185_port_set_forward_un -+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); -+ } -+ -+-int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, -+- bool unicast, bool multicast) -++int mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port, -++ bool unicast) -+ { -+ int err; -+ u16 reg; -+@@ -960,16 +960,28 @@ int mv88e6352_port_set_egress_floods(str -+ if (err) -+ return err; -+ -+- reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK; -++ if (unicast) -++ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC; -++ else -++ reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC; -++ -++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); -++} -++ -++int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port, -++ bool multicast) -++{ -++ int err; -++ u16 reg; -++ -++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®); -++ if (err) -++ return err; -+ -+- if (unicast && multicast) -+- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA; -+- else if (unicast) -+- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA; -+- else if (multicast) -+- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA; -++ if (multicast) -++ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC; -+ else -+- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA; -++ reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC; -+ -+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); -+ } -+@@ -1156,8 +1168,8 @@ static const char * const mv88e6xxx_port -+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure", -+ }; -+ -+-static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, -+- int port, bool multicast) -++int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, -++ int port, bool multicast) -+ { -+ int err; -+ u16 reg; -+@@ -1174,18 +1186,6 @@ static int mv88e6185_port_set_default_fo -+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); -+ } -+ -+-int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, -+- bool unicast, bool multicast) -+-{ -+- int err; -+- -+- err = mv88e6185_port_set_forward_unknown(chip, port, unicast); -+- if (err) -+- return err; -+- -+- return mv88e6185_port_set_default_forward(chip, port, multicast); -+-} -+- -+ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, -+ int upstream_port) -+ { -+Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.h -+=================================================================== -+--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/port.h -++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.h -+@@ -154,11 +154,8 @@ -+ #define MV88E6185_PORT_CTL0_USE_IP 0x0020 -+ #define MV88E6185_PORT_CTL0_USE_TAG 0x0010 -+ #define MV88E6185_PORT_CTL0_FORWARD_UNKNOWN 0x0004 -+-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK 0x000c -+-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA 0x0000 -+-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA 0x0004 -+-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA 0x0008 -+-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA 0x000c -++#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC 0x0004 -++#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC 0x0008 -+ #define MV88E6XXX_PORT_CTL0_STATE_MASK 0x0003 -+ #define MV88E6XXX_PORT_CTL0_STATE_DISABLED 0x0000 -+ #define MV88E6XXX_PORT_CTL0_STATE_BLOCKING 0x0001 -+@@ -335,10 +332,14 @@ int mv88e6085_port_set_frame_mode(struct -+ enum mv88e6xxx_frame_mode mode); -+ int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, -+ enum mv88e6xxx_frame_mode mode); -+-int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, -+- bool unicast, bool multicast); -+-int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, -+- bool unicast, bool multicast); -++int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, -++ int port, bool unicast); -++int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, -++ int port, bool multicast); -++int mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port, -++ bool unicast); -++int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port, -++ bool multicast); -+ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, -+ enum mv88e6xxx_policy_mapping mapping, -+ enum mv88e6xxx_policy_action action); -+Index: linux-5.4.111/include/net/dsa.h -+=================================================================== -+--- linux-5.4.111.orig/include/net/dsa.h -++++ linux-5.4.111/include/net/dsa.h -+@@ -547,8 +547,14 @@ struct dsa_switch_ops { -+ void (*port_stp_state_set)(struct dsa_switch *ds, int port, -+ u8 state); -+ void (*port_fast_age)(struct dsa_switch *ds, int port); -+- int (*port_egress_floods)(struct dsa_switch *ds, int port, -+- bool unicast, bool multicast); -++ int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack); -++ int (*port_bridge_flags)(struct dsa_switch *ds, int port, -++ unsigned long flags, -++ struct netlink_ext_ack *extack); -++ int (*port_set_mrouter)(struct dsa_switch *ds, int port, bool mrouter, -++ struct netlink_ext_ack *extack); -+ -+ /* -+ * VLAN support -+Index: linux-5.4.111/net/dsa/dsa_priv.h -+=================================================================== -+--- linux-5.4.111.orig/net/dsa/dsa_priv.h -++++ linux-5.4.111/net/dsa/dsa_priv.h -+@@ -171,11 +171,11 @@ int dsa_port_mdb_add(const struct dsa_po -+ int dsa_port_mdb_del(const struct dsa_port *dp, -+ const struct switchdev_obj_port_mdb *mdb); -+ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, -+- struct switchdev_trans *trans); -++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); -+ int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, -+- struct switchdev_trans *trans); -++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); -+ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, -+- struct switchdev_trans *trans); -++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); -+ int dsa_port_vlan_add(struct dsa_port *dp, -+ const struct switchdev_obj_port_vlan *vlan, -+ struct switchdev_trans *trans); -+Index: linux-5.4.111/net/dsa/port.c -+=================================================================== -+--- linux-5.4.111.orig/net/dsa/port.c -++++ linux-5.4.111/net/dsa/port.c -+@@ -127,7 +127,7 @@ int dsa_port_bridge_join(struct dsa_port -+ int err; -+ -+ /* Set the flooding mode before joining the port in the switch */ -+- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); -++ err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); -+ if (err) -+ return err; -+ -+@@ -140,7 +140,7 @@ int dsa_port_bridge_join(struct dsa_port -+ -+ /* The bridging is rolled back on error */ -+ if (err) { -+- dsa_port_bridge_flags(dp, 0, NULL); -++ dsa_port_bridge_flags(dp, 0, NULL, NULL); -+ dp->bridge_dev = NULL; -+ } -+ -+@@ -166,7 +166,7 @@ void dsa_port_bridge_leave(struct dsa_po -+ pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); -+ -+ /* Port is leaving the bridge, disable flooding */ -+- dsa_port_bridge_flags(dp, 0, NULL); -++ dsa_port_bridge_flags(dp, 0, NULL, NULL); -+ -+ /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, -+ * so allow it to be in BR_STATE_FORWARDING to be kept functional -+@@ -350,44 +350,44 @@ int dsa_port_ageing_time(struct dsa_port -+ } -+ -+ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, -+- struct switchdev_trans *trans) -++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) -+ { -+ struct dsa_switch *ds = dp->ds; -+ -+- if (!ds->ops->port_egress_floods || -+- (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) -++ if (!ds->ops->port_pre_bridge_flags) -+ return -EINVAL; -+ -+- return 0; -++ return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); -+ } -+ -+ int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, -+- struct switchdev_trans *trans) -++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) -+ { -+ struct dsa_switch *ds = dp->ds; -+- int port = dp->index; -+- int err = 0; -+ -+ if (switchdev_trans_ph_prepare(trans)) -+ return 0; -+ -+- if (ds->ops->port_egress_floods) -+- err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, -+- flags & BR_MCAST_FLOOD); -++ if (!ds->ops->port_bridge_flags) -++ return -EINVAL; -++ -++ return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); -+ -+- return err; -+ } -+ -+ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, -+- struct switchdev_trans *trans) -++ struct switchdev_trans *trans, -++ struct netlink_ext_ack *extack) -+ { -+ struct dsa_switch *ds = dp->ds; -+- int port = dp->index; -+ -+ if (switchdev_trans_ph_prepare(trans)) -+- return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; -++ return ds->ops->port_set_mrouter ? 0 : -EOPNOTSUPP; -++ -++ if (!ds->ops->port_set_mrouter) -++ return -EOPNOTSUPP; -+ -+- return ds->ops->port_egress_floods(ds, port, true, mrouter); -++ return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack); -+ } -+ -+ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, -+Index: linux-5.4.111/net/dsa/slave.c -+=================================================================== -+--- linux-5.4.111.orig/net/dsa/slave.c -++++ linux-5.4.111/net/dsa/slave.c -+@@ -293,13 +293,13 @@ static int dsa_slave_port_attr_set(struc -+ break; -+ case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: -+ ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, -+- trans); -++ trans, NULL); -+ break; -+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: -+- ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans); -++ ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans, NULL); -+ break; -+ case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: -+- ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans); -++ ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans, NULL); -+ break; -+ default: -+ ret = -EOPNOTSUPP; diff --git a/target/linux/realtek/base-files/etc/board.d/01_leds b/target/linux/realtek/base-files/etc/board.d/01_leds index 699ab817dd..36ca01a696 100755 --- a/target/linux/realtek/base-files/etc/board.d/01_leds @@ -18162,6 +14210,3958 @@ index 0000000000..68494b0ecc + break; + } + if (l >= 100) { +diff --git a/target/linux/realtek/patches-5.4/870-dsa-lag.patch b/target/linux/realtek/patches-5.4/870-dsa-lag.patch +new file mode 100644 +index 0000000000..3d1992e4cb +--- /dev/null ++++ b/target/linux/realtek/patches-5.4/870-dsa-lag.patch +@@ -0,0 +1,3123 @@ ++diff -urpN linux-5.4.137.old/drivers/net/bonding/bond_main.c linux-5.4.137/drivers/net/bonding/bond_main.c ++--- linux-5.4.137.old/drivers/net/bonding/bond_main.c 2021-08-04 14:05:38.055697349 +0700 +++++ linux-5.4.137/drivers/net/bonding/bond_main.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -1753,6 +1753,8 @@ int bond_enslave(struct net_device *bond ++ goto err_unregister; ++ } ++ +++ bond_lower_state_changed(new_slave); +++ ++ res = bond_sysfs_slave_add(new_slave); ++ if (res) { ++ slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res); ++diff -urpN linux-5.4.137.old/drivers/net/dsa/b53/b53_common.c linux-5.4.137/drivers/net/dsa/b53/b53_common.c ++--- linux-5.4.137.old/drivers/net/dsa/b53/b53_common.c 2021-08-04 14:05:38.055697349 +0700 +++++ linux-5.4.137/drivers/net/dsa/b53/b53_common.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -537,7 +537,7 @@ int b53_enable_port(struct dsa_switch *d ++ if (!dsa_is_user_port(ds, port)) ++ return 0; ++ ++- cpu_port = ds->ports[port].cpu_dp->index; +++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ b53_br_egress_floods(ds, port, true, true); ++ b53_port_set_learning(dev, port, false); ++@@ -1674,7 +1674,7 @@ EXPORT_SYMBOL(b53_fdb_dump); ++ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) ++ { ++ struct b53_device *dev = ds->priv; ++- s8 cpu_port = ds->ports[port].cpu_dp->index; +++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ u16 pvlan, reg; ++ unsigned int i; ++ ++@@ -1722,7 +1722,7 @@ void b53_br_leave(struct dsa_switch *ds, ++ { ++ struct b53_device *dev = ds->priv; ++ struct b53_vlan *vl = &dev->vlans[0]; ++- s8 cpu_port = ds->ports[port].cpu_dp->index; +++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ unsigned int i; ++ u16 pvlan, reg, pvid; ++ ++@@ -2396,10 +2396,13 @@ struct b53_device *b53_switch_alloc(stru ++ struct dsa_switch *ds; ++ struct b53_device *dev; ++ ++- ds = dsa_switch_alloc(base, DSA_MAX_PORTS); +++ ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return NULL; ++ +++ ds->dev = base; +++ ds->num_ports = DSA_MAX_PORTS; +++ ++ dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return NULL; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/bcm_sf2.c linux-5.4.137/drivers/net/dsa/bcm_sf2.c ++--- linux-5.4.137.old/drivers/net/dsa/bcm_sf2.c 2021-08-04 14:05:38.055697349 +0700 +++++ linux-5.4.137/drivers/net/dsa/bcm_sf2.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -670,7 +670,7 @@ static void bcm_sf2_sw_fixed_state(struc ++ * state machine and make it go in PHY_FORCING state instead. ++ */ ++ if (!status->link) ++- netif_carrier_off(ds->ports[port].slave); +++ netif_carrier_off(dsa_to_port(ds, port)->slave); ++ status->duplex = DUPLEX_FULL; ++ } else { ++ status->link = true; ++@@ -736,7 +736,7 @@ static int bcm_sf2_sw_resume(struct dsa_ ++ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, ++ struct ethtool_wolinfo *wol) ++ { ++- struct net_device *p = ds->ports[port].cpu_dp->master; +++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; ++ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); ++ struct ethtool_wolinfo pwol = { }; ++ ++@@ -760,9 +760,9 @@ static void bcm_sf2_sw_get_wol(struct ds ++ static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, ++ struct ethtool_wolinfo *wol) ++ { ++- struct net_device *p = ds->ports[port].cpu_dp->master; +++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; ++ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); ++- s8 cpu_port = ds->ports[port].cpu_dp->index; +++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ struct ethtool_wolinfo pwol = { }; ++ ++ if (p->ethtool_ops->get_wol) ++diff -urpN linux-5.4.137.old/drivers/net/dsa/bcm_sf2_cfp.c linux-5.4.137/drivers/net/dsa/bcm_sf2_cfp.c ++--- linux-5.4.137.old/drivers/net/dsa/bcm_sf2_cfp.c 2021-08-04 14:05:38.055697349 +0700 +++++ linux-5.4.137/drivers/net/dsa/bcm_sf2_cfp.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -821,7 +821,7 @@ static int bcm_sf2_cfp_rule_insert(struc ++ struct ethtool_rx_flow_spec *fs) ++ { ++ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); ++- s8 cpu_port = ds->ports[port].cpu_dp->index; +++ s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ __u64 ring_cookie = fs->ring_cookie; ++ unsigned int queue_num, port_num; ++ int ret; ++@@ -1046,7 +1046,7 @@ static int bcm_sf2_cfp_rule_get_all(stru ++ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, ++ struct ethtool_rxnfc *nfc, u32 *rule_locs) ++ { ++- struct net_device *p = ds->ports[port].cpu_dp->master; +++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; ++ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); ++ int ret = 0; ++ ++@@ -1089,7 +1089,7 @@ int bcm_sf2_get_rxnfc(struct dsa_switch ++ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, ++ struct ethtool_rxnfc *nfc) ++ { ++- struct net_device *p = ds->ports[port].cpu_dp->master; +++ struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; ++ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); ++ int ret = 0; ++ ++diff -urpN linux-5.4.137.old/drivers/net/dsa/dsa_loop.c linux-5.4.137/drivers/net/dsa/dsa_loop.c ++--- linux-5.4.137.old/drivers/net/dsa/dsa_loop.c 2021-08-04 14:05:38.055697349 +0700 +++++ linux-5.4.137/drivers/net/dsa/dsa_loop.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -286,10 +286,13 @@ static int dsa_loop_drv_probe(struct mdi ++ dev_info(&mdiodev->dev, "%s: 0x%0x\n", ++ pdata->name, pdata->enabled_ports); ++ ++- ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); +++ ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ +++ ds->dev = &mdiodev->dev; +++ ds->num_ports = DSA_MAX_PORTS; +++ ++ ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); ++ if (!ps) ++ return -ENOMEM; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/lan9303-core.c linux-5.4.137/drivers/net/dsa/lan9303-core.c ++--- linux-5.4.137.old/drivers/net/dsa/lan9303-core.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/lan9303-core.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -1283,10 +1283,12 @@ static int lan9303_register_switch(struc ++ { ++ int base; ++ ++- chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS); +++ chip->ds = devm_kzalloc(chip->dev, sizeof(*chip->ds), GFP_KERNEL); ++ if (!chip->ds) ++ return -ENOMEM; ++ +++ chip->ds->dev = chip->dev; +++ chip->ds->num_ports = LAN9303_NUM_PORTS; ++ chip->ds->priv = chip; ++ chip->ds->ops = &lan9303_switch_ops; ++ base = chip->phy_addr_base; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/lantiq_gswip.c linux-5.4.137/drivers/net/dsa/lantiq_gswip.c ++--- linux-5.4.137.old/drivers/net/dsa/lantiq_gswip.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/lantiq_gswip.c 2021-08-04 14:05:53.887713713 +0700 ++@@ -2006,10 +2006,12 @@ static int gswip_probe(struct platform_d ++ if (!priv->hw_info) ++ return -EINVAL; ++ ++- priv->ds = dsa_switch_alloc(dev, priv->hw_info->max_ports); +++ priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ +++ priv->ds->dev = dev; +++ priv->ds->num_ports = priv->hw_info->max_ports; ++ priv->ds->priv = priv; ++ priv->ds->ops = &gswip_switch_ops; ++ priv->dev = dev; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/microchip/ksz_common.c linux-5.4.137/drivers/net/dsa/microchip/ksz_common.c ++--- linux-5.4.137.old/drivers/net/dsa/microchip/ksz_common.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/microchip/ksz_common.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -396,10 +396,13 @@ struct ksz_device *ksz_switch_alloc(stru ++ struct dsa_switch *ds; ++ struct ksz_device *swdev; ++ ++- ds = dsa_switch_alloc(base, DSA_MAX_PORTS); +++ ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return NULL; ++ +++ ds->dev = base; +++ ds->num_ports = DSA_MAX_PORTS; +++ ++ swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); ++ if (!swdev) ++ return NULL; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mt7530.c linux-5.4.137/drivers/net/dsa/mt7530.c ++--- linux-5.4.137.old/drivers/net/dsa/mt7530.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mt7530.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -785,7 +785,7 @@ mt7530_port_set_vlan_unaware(struct dsa_ ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ if (dsa_is_user_port(ds, i) && ++- dsa_port_is_vlan_filtering(&ds->ports[i])) { +++ dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { ++ all_user_ports_removed = false; ++ break; ++ } ++@@ -843,7 +843,7 @@ mt7530_port_bridge_leave(struct dsa_swit ++ * other port is still a VLAN-aware port. ++ */ ++ if (dsa_is_user_port(ds, i) && i != port && ++- !dsa_port_is_vlan_filtering(&ds->ports[i])) { +++ !dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { ++ if (dsa_to_port(ds, i)->bridge_dev != bridge) ++ continue; ++ if (priv->ports[i].enable) ++@@ -1219,7 +1219,7 @@ mt7530_setup(struct dsa_switch *ds) ++ * controller also is the container for two GMACs nodes representing ++ * as two netdev instances. ++ */ ++- dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; +++ dn = dsa_to_port(ds, port)->master->dev.of_node->parent; ++ ds->configure_vlan_while_not_filtering = true; ++ ++ if (priv->id == ID_MT7530) { ++@@ -1306,7 +1306,7 @@ mt7530_setup(struct dsa_switch *ds) ++ ++ if (!dsa_is_unused_port(ds, 5)) { ++ priv->p5_intf_sel = P5_INTF_SEL_GMAC5; ++- interface = of_get_phy_mode(ds->ports[5].dn); +++ interface = of_get_phy_mode(dsa_to_port(ds, 5)->dn); ++ } else { ++ /* Scan the ethernet nodes. look for GMAC1, lookup used phy */ ++ for_each_child_of_node(dn, mac_np) { ++@@ -1649,10 +1649,13 @@ mt7530_probe(struct mdio_device *mdiodev ++ if (!priv) ++ return -ENOMEM; ++ ++- priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); +++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ +++ priv->ds->dev = &mdiodev->dev; +++ priv->ds->num_ports = DSA_MAX_PORTS; +++ ++ /* Use medatek,mcm property to distinguish hardware type that would ++ * casues a little bit differences on power-on sequence. ++ */ ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6060.c linux-5.4.137/drivers/net/dsa/mv88e6060.c ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6060.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6060.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -270,10 +270,12 @@ static int mv88e6060_probe(struct mdio_d ++ ++ dev_info(dev, "switch %s detected\n", name); ++ ++- ds = dsa_switch_alloc(dev, MV88E6060_PORTS); +++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ +++ ds->dev = dev; +++ ds->num_ports = MV88E6060_PORTS; ++ ds->priv = priv; ++ ds->dev = dev; ++ ds->ops = &mv88e6060_switch_ops; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/chip.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/chip.c ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/chip.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/chip.c 2021-08-04 14:05:57.643717592 +0700 ++@@ -1075,7 +1075,7 @@ static u16 mv88e6xxx_port_vlan(struct mv ++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) ++ return mv88e6xxx_port_mask(chip); ++ ++- br = ds->ports[port].bridge_dev; +++ br = dsa_to_port(ds, port)->bridge_dev; ++ pvlan = 0; ++ ++ /* Frames from user ports can egress any local DSA links and CPU ports, ++@@ -1135,6 +1135,7 @@ static int mv88e6xxx_pri_setup(struct mv ++ ++ static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip) ++ { +++ struct dsa_switch *ds = chip->ds; ++ int target, port; ++ int err; ++ ++@@ -1143,10 +1144,9 @@ static int mv88e6xxx_devmap_setup(struct ++ ++ /* Initialize the routing port to the 32 possible target devices */ ++ for (target = 0; target < 32; target++) { ++- port = 0x1f; ++- if (target < DSA_MAX_SWITCHES) ++- if (chip->ds->rtable[target] != DSA_RTABLE_NONE) ++- port = chip->ds->rtable[target]; +++ port = dsa_routing_port(ds, target); +++ if (port == ds->num_ports) +++ port = 0x1f; ++ ++ err = mv88e6xxx_g2_device_mapping_write(chip, target, port); ++ if (err) ++@@ -1250,14 +1250,30 @@ static int mv88e6xxx_mac_setup(struct mv ++ ++ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) ++ { +++ struct dsa_switch_tree *dst = chip->ds->dst; +++ struct dsa_switch *ds; +++ struct dsa_port *dp; ++ u16 pvlan = 0; ++ ++ if (!mv88e6xxx_has_pvt(chip)) ++ return -EOPNOTSUPP; ++ ++ /* Skip the local source device, which uses in-chip port VLAN */ ++- if (dev != chip->ds->index) +++ if (dev != chip->ds->index) { ++ pvlan = mv88e6xxx_port_vlan(chip, dev, port); +++ ds = dsa_switch_find(dst->index, dev); +++ dp = ds ? dsa_to_port(ds, port) : NULL; +++ if (dp && dp->lag_dev) { +++ /* As the PVT is used to limit flooding of +++ * FORWARD frames, which use the LAG ID as the +++ * source port, we must translate dev/port to +++ * the special "LAG device" in the PVT, using +++ * the LAG ID as the port number. +++ */ +++ dev = MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK; +++ port = dsa_lag_id(dst, dp->lag_dev); +++ } +++ } ++ ++ return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); ++ } ++@@ -1402,7 +1418,7 @@ static int mv88e6xxx_port_check_hw_vlan( ++ if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) ++ continue; ++ ++- if (!ds->ports[i].slave) +++ if (!dsa_to_port(ds, i)->slave) ++ continue; ++ ++ if (vlan.member[i] == ++@@ -1410,7 +1426,7 @@ static int mv88e6xxx_port_check_hw_vlan( ++ continue; ++ ++ if (dsa_to_port(ds, i)->bridge_dev == ++- ds->ports[port].bridge_dev) +++ dsa_to_port(ds, port)->bridge_dev) ++ break; /* same bridge, check next VLAN */ ++ ++ if (!dsa_to_port(ds, i)->bridge_dev) ++@@ -2048,7 +2064,7 @@ static int mv88e6xxx_bridge_map(struct m ++ ++ /* Remap the Port VLAN of each local bridge group member */ ++ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { ++- if (chip->ds->ports[port].bridge_dev == br) { +++ if (dsa_to_port(chip->ds, port)->bridge_dev == br) { ++ err = mv88e6xxx_port_vlan_map(chip, port); ++ if (err) ++ return err; ++@@ -2065,7 +2081,7 @@ static int mv88e6xxx_bridge_map(struct m ++ break; ++ ++ for (port = 0; port < ds->num_ports; ++port) { ++- if (ds->ports[port].bridge_dev == br) { +++ if (dsa_to_port(ds, port)->bridge_dev == br) { ++ err = mv88e6xxx_pvt_map(chip, dev, port); ++ if (err) ++ return err; ++@@ -5022,6 +5038,271 @@ static int mv88e6xxx_port_egress_floods( ++ return err; ++ } ++ +++static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, +++ struct net_device *lag, +++ struct netdev_lag_upper_info *info) +++{ +++ struct dsa_port *dp; +++ int id, members = 0; +++ +++ id = dsa_lag_id(ds->dst, lag); +++ if (id < 0 || id >= ds->num_lag_ids) +++ return false; +++ +++ dsa_lag_foreach_port(dp, ds->dst, lag) +++ /* Includes the port joining the LAG */ +++ members++; +++ +++ if (members > 8) +++ return false; +++ +++ /* We could potentially relax this to include active +++ * backup in the future. +++ */ +++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) +++ return false; +++ +++ /* Ideally we would also validate that the hash type matches +++ * the hardware. Alas, this is always set to unknown on team +++ * interfaces. +++ */ +++ return true; +++} +++ +++static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ struct dsa_port *dp; +++ u16 map = 0; +++ int id; +++ +++ id = dsa_lag_id(ds->dst, lag); +++ +++ /* Build the map of all ports to distribute flows destined for +++ * this LAG. This can be either a local user port, or a DSA +++ * port if the LAG port is on a remote chip. +++ */ +++ dsa_lag_foreach_port(dp, ds->dst, lag) +++ map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index)); +++ +++ return mv88e6xxx_g2_trunk_mapping_write(chip, id, map); +++} +++ +++static const u8 mv88e6xxx_lag_mask_table[8][8] = { +++ /* Row number corresponds to the number of active members in a +++ * LAG. Each column states which of the eight hash buckets are +++ * mapped to the column:th port in the LAG. +++ * +++ * Example: In a LAG with three active ports, the second port +++ * ([2][1]) would be selected for traffic mapped to buckets +++ * 3,4,5 (0x38). +++ */ +++ { 0xff, 0, 0, 0, 0, 0, 0, 0 }, +++ { 0x0f, 0xf0, 0, 0, 0, 0, 0, 0 }, +++ { 0x07, 0x38, 0xc0, 0, 0, 0, 0, 0 }, +++ { 0x03, 0x0c, 0x30, 0xc0, 0, 0, 0, 0 }, +++ { 0x03, 0x0c, 0x30, 0x40, 0x80, 0, 0, 0 }, +++ { 0x03, 0x0c, 0x10, 0x20, 0x40, 0x80, 0, 0 }, +++ { 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0 }, +++ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, +++}; +++ +++static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port, +++ int num_tx, int nth) +++{ +++ u8 active = 0; +++ int i; +++ +++ num_tx = num_tx <= 8 ? num_tx : 8; +++ if (nth < num_tx) +++ active = mv88e6xxx_lag_mask_table[num_tx - 1][nth]; +++ +++ for (i = 0; i < 8; i++) { +++ if (BIT(i) & active) +++ mask[i] |= BIT(port); +++ } +++} +++ +++static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ unsigned int id, num_tx; +++ struct net_device *lag; +++ struct dsa_port *dp; +++ int i, err, nth; +++ u16 mask[8]; +++ u16 ivec; +++ +++ /* Assume no port is a member of any LAG. */ +++ ivec = BIT(mv88e6xxx_num_ports(chip)) - 1; +++ +++ /* Disable all masks for ports that _are_ members of a LAG. */ +++ list_for_each_entry(dp, &ds->dst->ports, list) { +++ if (!dp->lag_dev || dp->ds != ds) +++ continue; +++ +++ ivec &= ~BIT(dp->index); +++ } +++ +++ for (i = 0; i < 8; i++) +++ mask[i] = ivec; +++ +++ /* Enable the correct subset of masks for all LAG ports that +++ * are in the Tx set. +++ */ +++ dsa_lags_foreach_id(id, ds->dst) { +++ lag = dsa_lag_dev(ds->dst, id); +++ if (!lag) +++ continue; +++ +++ num_tx = 0; +++ dsa_lag_foreach_port(dp, ds->dst, lag) { +++ if (dp->lag_tx_enabled) +++ num_tx++; +++ } +++ +++ if (!num_tx) +++ continue; +++ +++ nth = 0; +++ dsa_lag_foreach_port(dp, ds->dst, lag) { +++ if (!dp->lag_tx_enabled) +++ continue; +++ +++ if (dp->ds == ds) +++ mv88e6xxx_lag_set_port_mask(mask, dp->index, +++ num_tx, nth); +++ +++ nth++; +++ } +++ } +++ +++ for (i = 0; i < 8; i++) { +++ err = mv88e6xxx_g2_trunk_mask_write(chip, i, true, mask[i]); +++ if (err) +++ return err; +++ } +++ +++ return 0; +++} +++ +++static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds, +++ struct net_device *lag) +++{ +++ int err; +++ +++ err = mv88e6xxx_lag_sync_masks(ds); +++ +++ if (!err) +++ err = mv88e6xxx_lag_sync_map(ds, lag); +++ +++ return err; +++} +++ +++static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err; +++ +++ mv88e6xxx_reg_lock(chip); +++ err = mv88e6xxx_lag_sync_masks(ds); +++ mv88e6xxx_reg_unlock(chip); +++ return err; +++} +++ +++static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, +++ struct net_device *lag, +++ struct netdev_lag_upper_info *info) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err, id; +++ +++ if (!mv88e6xxx_lag_can_offload(ds, lag, info)) +++ return -EOPNOTSUPP; +++ +++ id = dsa_lag_id(ds->dst, lag); +++ +++ mv88e6xxx_reg_lock(chip); +++ +++ err = mv88e6xxx_port_set_trunk(chip, port, true, id); +++ if (err) +++ goto err_unlock; +++ +++ err = mv88e6xxx_lag_sync_masks_map(ds, lag); +++ if (err) +++ goto err_clear_trunk; +++ +++ mv88e6xxx_reg_unlock(chip); +++ return 0; +++ +++err_clear_trunk: +++ mv88e6xxx_port_set_trunk(chip, port, false, 0); +++err_unlock: +++ mv88e6xxx_reg_unlock(chip); +++ return err; +++} +++ +++static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port, +++ struct net_device *lag) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err_sync, err_trunk; +++ +++ mv88e6xxx_reg_lock(chip); +++ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); +++ err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0); +++ mv88e6xxx_reg_unlock(chip); +++ return err_sync ? : err_trunk; +++} +++ +++static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index, +++ int port) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err; +++ +++ mv88e6xxx_reg_lock(chip); +++ err = mv88e6xxx_lag_sync_masks(ds); +++ mv88e6xxx_reg_unlock(chip); +++ return err; +++} +++ +++static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, +++ int port, struct net_device *lag, +++ struct netdev_lag_upper_info *info) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err; +++ +++ if (!mv88e6xxx_lag_can_offload(ds, lag, info)) +++ return -EOPNOTSUPP; +++ +++ mv88e6xxx_reg_lock(chip); +++ +++ err = mv88e6xxx_lag_sync_masks_map(ds, lag); +++ if (err) +++ goto unlock; +++ +++ err = mv88e6xxx_pvt_map(chip, sw_index, port); +++ +++unlock: +++ mv88e6xxx_reg_unlock(chip); +++ return err; +++} +++ +++static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index, +++ int port, struct net_device *lag) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err_sync, err_pvt; +++ +++ mv88e6xxx_reg_lock(chip); +++ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag); +++ err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port); +++ mv88e6xxx_reg_unlock(chip); +++ return err_sync ? : err_pvt; +++} +++ ++ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { ++ .get_tag_protocol = mv88e6xxx_get_tag_protocol, ++ .setup = mv88e6xxx_setup, ++@@ -5069,6 +5350,12 @@ static const struct dsa_switch_ops mv88e ++ .port_txtstamp = mv88e6xxx_port_txtstamp, ++ .port_rxtstamp = mv88e6xxx_port_rxtstamp, ++ .get_ts_info = mv88e6xxx_get_ts_info, +++ .port_lag_change = mv88e6xxx_port_lag_change, +++ .port_lag_join = mv88e6xxx_port_lag_join, +++ .port_lag_leave = mv88e6xxx_port_lag_leave, +++ .crosschip_lag_change = mv88e6xxx_crosschip_lag_change, +++ .crosschip_lag_join = mv88e6xxx_crosschip_lag_join, +++ .crosschip_lag_leave = mv88e6xxx_crosschip_lag_leave, ++ }; ++ ++ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) ++@@ -5076,10 +5363,12 @@ static int mv88e6xxx_register_switch(str ++ struct device *dev = chip->dev; ++ struct dsa_switch *ds; ++ ++- ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip)); +++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ +++ ds->dev = dev; +++ ds->num_ports = mv88e6xxx_num_ports(chip); ++ ds->priv = chip; ++ ds->dev = dev; ++ ds->ops = &mv88e6xxx_switch_ops; ++@@ -5087,6 +5376,12 @@ static int mv88e6xxx_register_switch(str ++ ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; ++ +++ /* Some chips support up to 32, but that requires enabling the +++ * 5-bit port mode, which we do not support. 640k^W16 ought to +++ * be enough for anyone. +++ */ +++ ds->num_lag_ids = 16; +++ ++ dev_set_drvdata(dev, ds); ++ ++ return dsa_register_switch(ds); ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.c ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -126,8 +126,8 @@ int mv88e6xxx_g2_device_mapping_write(st ++ ++ /* Offset 0x07: Trunk Mask Table register */ ++ ++-static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, ++- bool hash, u16 mask) +++int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, +++ bool hash, u16 mask) ++ { ++ u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip)); ++ ++@@ -140,8 +140,8 @@ static int mv88e6xxx_g2_trunk_mask_write ++ ++ /* Offset 0x08: Trunk Mapping Table register */ ++ ++-static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, ++- u16 map) +++int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, +++ u16 map) ++ { ++ const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; ++ u16 val = (id << 11) | (map & port_mask); ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.h linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.h ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/global2.h 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/global2.h 2021-08-04 14:05:53.891713717 +0700 ++@@ -101,6 +101,7 @@ ++ #define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN 0x3000 ++ #define MV88E6XXX_G2_PVT_ADDR_OP_READ 0x4000 ++ #define MV88E6XXX_G2_PVT_ADDR_PTR_MASK 0x01ff +++#define MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK 0x1f ++ ++ /* Offset 0x0C: Cross-chip Port VLAN Data Register */ ++ #define MV88E6XXX_G2_PVT_DATA 0x0c ++@@ -336,6 +337,10 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv ++ ++ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); ++ +++int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, +++ bool hash, u16 mask); +++int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, +++ u16 map); ++ int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip); ++ ++ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.c linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.c ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -994,6 +994,27 @@ int mv88e6xxx_port_set_message_port(stru ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); ++ } ++ +++int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, +++ bool trunk, u8 id) +++{ +++ u16 val; +++ int err; +++ +++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val); +++ if (err) +++ return err; +++ +++ val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK; +++ +++ if (trunk) +++ val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT | +++ (id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT); +++ else +++ val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT; +++ +++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val); +++} +++ ++ /* Offset 0x06: Port Based VLAN Map */ ++ ++ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) ++diff -urpN linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.h linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.h ++--- linux-5.4.137.old/drivers/net/dsa/mv88e6xxx/port.h 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/mv88e6xxx/port.h 2021-08-04 14:05:53.891713717 +0700 ++@@ -168,6 +168,9 @@ ++ /* Offset 0x05: Port Control 1 */ ++ #define MV88E6XXX_PORT_CTL1 0x05 ++ #define MV88E6XXX_PORT_CTL1_MESSAGE_PORT 0x8000 +++#define MV88E6XXX_PORT_CTL1_TRUNK_PORT 0x4000 +++#define MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK 0x0f00 +++#define MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT 8 ++ #define MV88E6XXX_PORT_CTL1_FID_11_4_MASK 0x00ff ++ ++ /* Offset 0x06: Port Based VLAN Map */ ++@@ -343,6 +346,8 @@ int mv88e6351_port_set_ether_type(struct ++ u16 etype); ++ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, ++ bool message_port); +++int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, +++ bool trunk, u8 id); ++ int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port, ++ size_t size); ++ int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); ++diff -urpN linux-5.4.137.old/drivers/net/dsa/qca8k.c linux-5.4.137/drivers/net/dsa/qca8k.c ++--- linux-5.4.137.old/drivers/net/dsa/qca8k.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/qca8k.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -661,7 +661,7 @@ qca8k_setup(struct dsa_switch *ds) ++ return ret; ++ ++ /* Initialize CPU port pad mode (xMII type, delays...) */ ++- phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn); +++ phy_mode = of_get_phy_mode(dsa_to_port(ds, QCA8K_CPU_PORT)->dn); ++ if (phy_mode < 0) { ++ pr_err("Can't find phy-mode for master device\n"); ++ return phy_mode; ++@@ -1077,10 +1077,13 @@ qca8k_sw_probe(struct mdio_device *mdiod ++ if (id != QCA8K_ID_QCA8337) ++ return -ENODEV; ++ ++- priv->ds = dsa_switch_alloc(&mdiodev->dev, QCA8K_NUM_PORTS); +++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), +++ QCA8K_NUM_PORTS); ++ if (!priv->ds) ++ return -ENOMEM; ++ +++ priv->ds->dev = &mdiodev->dev; +++ priv->ds->num_ports = DSA_MAX_PORTS; ++ priv->ds->priv = priv; ++ priv->ops = qca8k_switch_ops; ++ priv->ds->ops = &priv->ops; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/realtek-smi-core.c linux-5.4.137/drivers/net/dsa/realtek-smi-core.c ++--- linux-5.4.137.old/drivers/net/dsa/realtek-smi-core.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/realtek-smi-core.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -444,9 +444,12 @@ static int realtek_smi_probe(struct plat ++ return ret; ++ } ++ ++- smi->ds = dsa_switch_alloc(dev, smi->num_ports); +++ smi->ds = devm_kzalloc(dev, sizeof(*smi->ds), GFP_KERNEL); ++ if (!smi->ds) ++ return -ENOMEM; +++ +++ smi->ds->dev = dev; +++ smi->ds->num_ports = smi->num_ports; ++ smi->ds->priv = smi; ++ ++ smi->ds->ops = var->ds_ops; ++diff -urpN linux-5.4.137.old/drivers/net/dsa/sja1105/sja1105_main.c linux-5.4.137/drivers/net/dsa/sja1105/sja1105_main.c ++--- linux-5.4.137.old/drivers/net/dsa/sja1105/sja1105_main.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/sja1105/sja1105_main.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -1096,7 +1096,7 @@ int sja1105pqrs_fdb_add(struct dsa_switc ++ l2_lookup.vlanid = vid; ++ l2_lookup.iotag = SJA1105_S_TAG; ++ l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); ++- if (dsa_port_is_vlan_filtering(&ds->ports[port])) { +++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { ++ l2_lookup.mask_vlanid = VLAN_VID_MASK; ++ l2_lookup.mask_iotag = BIT(0); ++ } else { ++@@ -1159,7 +1159,7 @@ int sja1105pqrs_fdb_del(struct dsa_switc ++ l2_lookup.vlanid = vid; ++ l2_lookup.iotag = SJA1105_S_TAG; ++ l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); ++- if (dsa_port_is_vlan_filtering(&ds->ports[port])) { +++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { ++ l2_lookup.mask_vlanid = VLAN_VID_MASK; ++ l2_lookup.mask_iotag = BIT(0); ++ } else { ++@@ -1205,7 +1205,7 @@ static int sja1105_fdb_add(struct dsa_sw ++ * for what gets printed in 'bridge fdb show'. In the case of zero, ++ * no VID gets printed at all. ++ */ ++- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) +++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) ++ vid = 0; ++ ++ return priv->info->fdb_add_cmd(ds, port, addr, vid); ++@@ -1216,7 +1216,7 @@ static int sja1105_fdb_del(struct dsa_sw ++ { ++ struct sja1105_private *priv = ds->priv; ++ ++- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) +++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) ++ vid = 0; ++ ++ return priv->info->fdb_del_cmd(ds, port, addr, vid); ++@@ -1255,7 +1255,7 @@ static int sja1105_fdb_dump(struct dsa_s ++ u64_to_ether_addr(l2_lookup.macaddr, macaddr); ++ ++ /* We need to hide the dsa_8021q VLANs from the user. */ ++- if (!dsa_port_is_vlan_filtering(&ds->ports[port])) +++ if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) ++ l2_lookup.vlanid = 0; ++ cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); ++ } ++@@ -1748,7 +1748,7 @@ static int sja1105_port_enable(struct ds ++ if (!dsa_is_user_port(ds, port)) ++ return 0; ++ ++- slave = ds->ports[port].slave; +++ slave = dsa_to_port(ds, port)->slave; ++ ++ slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; ++ ++@@ -1780,7 +1780,7 @@ static int sja1105_mgmt_xmit(struct dsa_ ++ } ++ ++ /* Transfer skb to the host port. */ ++- dsa_enqueue_skb(skb, ds->ports[port].slave); +++ dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); ++ ++ /* Wait until the switch has processed the frame */ ++ do { ++@@ -2198,10 +2198,12 @@ static int sja1105_probe(struct spi_devi ++ ++ dev_info(dev, "Probed switch chip: %s\n", priv->info->name); ++ ++- ds = dsa_switch_alloc(dev, SJA1105_NUM_PORTS); +++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ +++ ds->dev = dev; +++ ds->num_ports = SJA1105_NUM_PORTS; ++ ds->ops = &sja1105_switch_ops; ++ ds->priv = priv; ++ priv->ds = ds; ++@@ -2215,8 +2217,8 @@ static int sja1105_probe(struct spi_devi ++ for (i = 0; i < SJA1105_NUM_PORTS; i++) { ++ struct sja1105_port *sp = &priv->ports[i]; ++ ++- ds->ports[i].priv = sp; ++- sp->dp = &ds->ports[i]; +++ dsa_to_port(ds, i)->priv = sp; +++ sp->dp = dsa_to_port(ds, i); ++ sp->data = tagger_data; ++ } ++ mutex_init(&priv->mgmt_lock); ++diff -urpN linux-5.4.137.old/drivers/net/dsa/vitesse-vsc73xx-core.c linux-5.4.137/drivers/net/dsa/vitesse-vsc73xx-core.c ++--- linux-5.4.137.old/drivers/net/dsa/vitesse-vsc73xx-core.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/drivers/net/dsa/vitesse-vsc73xx-core.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -1178,9 +1178,12 @@ int vsc73xx_probe(struct vsc73xx *vsc) ++ * We allocate 8 ports and avoid access to the nonexistant ++ * ports. ++ */ ++- vsc->ds = dsa_switch_alloc(dev, 8); +++ vsc->ds = devm_kzalloc(dev, sizeof(*vsc->ds), GFP_KERNEL); ++ if (!vsc->ds) ++ return -ENOMEM; +++ +++ vsc->ds->dev = dev; +++ vsc->ds->num_ports = 8; ++ vsc->ds->priv = vsc; ++ ++ vsc->ds->ops = &vsc73xx_ds_ops; ++diff -urpN linux-5.4.137.old/include/net/dsa.h linux-5.4.137/include/net/dsa.h ++--- linux-5.4.137.old/include/net/dsa.h 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/include/net/dsa.h 2021-08-04 14:05:57.643717592 +0700 ++@@ -124,17 +124,46 @@ struct dsa_switch_tree { ++ */ ++ struct dsa_platform_data *pd; ++ ++- /* ++- * The switch port to which the CPU is attached. ++- */ ++- struct dsa_port *cpu_dp; +++ /* List of switch ports */ +++ struct list_head ports; ++ ++- /* ++- * Data for the individual switch chips. +++ /* List of DSA links composing the routing table */ +++ struct list_head rtable; +++ +++ /* Maps offloaded LAG netdevs to a zero-based linear ID for +++ * drivers that need it. ++ */ ++- struct dsa_switch *ds[DSA_MAX_SWITCHES]; +++ struct net_device **lags; +++ unsigned int lags_len; ++ }; ++ +++#define dsa_lags_foreach_id(_id, _dst) \ +++ for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \ +++ if ((_dst)->lags[(_id)]) +++ +++#define dsa_lag_foreach_port(_dp, _dst, _lag) \ +++ list_for_each_entry((_dp), &(_dst)->ports, list) \ +++ if ((_dp)->lag_dev == (_lag)) +++ +++static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst, +++ unsigned int id) +++{ +++ return dst->lags[id]; +++} +++ +++static inline int dsa_lag_id(struct dsa_switch_tree *dst, +++ struct net_device *lag) +++{ +++ unsigned int id; +++ +++ dsa_lags_foreach_id(id, dst) { +++ if (dsa_lag_dev(dst, id) == lag) +++ return id; +++ } +++ +++ return -ENODEV; +++} +++ ++ /* TC matchall action types, only mirroring for now */ ++ enum dsa_port_mall_action_type { ++ DSA_PORT_MALL_MIRROR, ++@@ -195,10 +224,14 @@ struct dsa_port { ++ struct devlink_port devlink_port; ++ struct phylink *pl; ++ struct phylink_config pl_config; +++ struct net_device *lag_dev; +++ bool lag_tx_enabled; ++ ++ struct work_struct xmit_work; ++ struct sk_buff_head xmit_queue; ++ +++ struct list_head list; +++ ++ /* ++ * Give the switch driver somewhere to hang its per-port private data ++ * structures (accessible from the tagger). ++@@ -214,9 +247,24 @@ struct dsa_port { ++ * Original copy of the master netdev net_device_ops ++ */ ++ const struct net_device_ops *orig_ndo_ops; +++ +++ bool setup; +++}; +++ +++/* TODO: ideally DSA ports would have a single dp->link_dp member, +++ * and no dst->rtable nor this struct dsa_link would be needed, +++ * but this would require some more complex tree walking, +++ * so keep it stupid at the moment and list them all. +++ */ +++struct dsa_link { +++ struct dsa_port *dp; +++ struct dsa_port *link_dp; +++ struct list_head list; ++ }; ++ ++ struct dsa_switch { +++ bool setup; +++ ++ struct device *dev; ++ ++ /* ++@@ -245,13 +293,6 @@ struct dsa_switch { ++ const struct dsa_switch_ops *ops; ++ ++ /* ++- * An array of which element [a] indicates which port on this ++- * switch should be used to send packets to that are destined ++- * for switch a. Can be NULL if there is only one switch chip. ++- */ ++- s8 rtable[DSA_MAX_SWITCHES]; ++- ++- /* ++ * Slave mii_bus and devices for the individual ports. ++ */ ++ u32 phys_mii_mask; ++@@ -289,14 +330,27 @@ struct dsa_switch { ++ */ ++ bool vlan_filtering; ++ ++- /* Dynamically allocated ports, keep last */ ++ size_t num_ports; ++- struct dsa_port ports[]; +++ +++ /* Drivers that benefit from having an ID associated with each +++ * offloaded LAG should set this to the maximum number of +++ * supported IDs. DSA will then maintain a mapping of _at +++ * least_ these many IDs, accessible to drivers via +++ * dsa_lag_id(). +++ */ +++ unsigned int num_lag_ids; ++ }; ++ ++-static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) +++static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) ++ { ++- return &ds->ports[p]; +++ struct dsa_switch_tree *dst = ds->dst; +++ struct dsa_port *dp; +++ +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dp->ds == ds && dp->index == p) +++ return dp; +++ +++ return NULL; ++ } ++ ++ static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) ++@@ -331,6 +385,19 @@ static inline u32 dsa_user_ports(struct ++ return mask; ++ } ++ +++/* Return the local port used to reach an arbitrary switch device */ +++static inline unsigned int dsa_routing_port(struct dsa_switch *ds, int device) +++{ +++ struct dsa_switch_tree *dst = ds->dst; +++ struct dsa_link *dl; +++ +++ list_for_each_entry(dl, &dst->rtable, list) +++ if (dl->dp->ds == ds && dl->link_dp->ds->index == device) +++ return dl->dp->index; +++ +++ return ds->num_ports; +++} +++ ++ /* Return the local port used to reach an arbitrary switch port */ ++ static inline unsigned int dsa_towards_port(struct dsa_switch *ds, int device, ++ int port) ++@@ -338,7 +405,7 @@ static inline unsigned int dsa_towards_p ++ if (device == ds->index) ++ return port; ++ else ++- return ds->rtable[device]; +++ return dsa_routing_port(ds, device); ++ } ++ ++ /* Return the local port used to reach the dedicated CPU port */ ++@@ -539,6 +606,13 @@ struct dsa_switch_ops { ++ int port, struct net_device *br); ++ void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, ++ int port, struct net_device *br); +++ int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index, +++ int port); +++ int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index, +++ int port, struct net_device *lag, +++ struct netdev_lag_upper_info *info); +++ int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index, +++ int port, struct net_device *lag); ++ ++ /* ++ * PTP functionality ++@@ -557,6 +631,16 @@ struct dsa_switch_ops { ++ */ ++ netdev_tx_t (*port_deferred_xmit)(struct dsa_switch *ds, int port, ++ struct sk_buff *skb); +++ +++ /* +++ * LAG integration +++ */ +++ int (*port_lag_change)(struct dsa_switch *ds, int port); +++ int (*port_lag_join)(struct dsa_switch *ds, int port, +++ struct net_device *lag, +++ struct netdev_lag_upper_info *info); +++ int (*port_lag_leave)(struct dsa_switch *ds, int port, +++ struct net_device *lag); ++ }; ++ ++ struct dsa_switch_driver { ++@@ -584,7 +668,6 @@ static inline bool dsa_can_decode(const ++ return false; ++ } ++ ++-struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n); ++ void dsa_unregister_switch(struct dsa_switch *ds); ++ int dsa_register_switch(struct dsa_switch *ds); ++ #ifdef CONFIG_PM_SLEEP ++@@ -628,6 +711,7 @@ int register_dsa_notifier(struct notifie ++ int unregister_dsa_notifier(struct notifier_block *nb); ++ int call_dsa_notifiers(unsigned long val, struct net_device *dev, ++ struct dsa_notifier_info *info); +++bool dsa_slave_dev_check(const struct net_device *dev); ++ #else ++ static inline int register_dsa_notifier(struct notifier_block *nb) ++ { ++@@ -644,6 +728,11 @@ static inline int call_dsa_notifiers(uns ++ { ++ return NOTIFY_DONE; ++ } +++ +++static inline bool dsa_slave_dev_check(const struct net_device *dev) +++{ +++ return false; +++} ++ #endif ++ ++ /* Broadcom tag specific helpers to insert and extract queue/port number */ ++diff -urpN linux-5.4.137.old/net/dsa/Kconfig linux-5.4.137/net/dsa/Kconfig ++--- linux-5.4.137.old/net/dsa/Kconfig 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/Kconfig 2021-08-04 14:05:53.891713717 +0700 ++@@ -56,14 +56,19 @@ config NET_DSA_TAG_GSWIP ++ Say Y or M if you want to enable support for tagging frames for the ++ Lantiq / Intel GSWIP switches. ++ +++config NET_DSA_TAG_DSA_COMMON +++ tristate +++ ++ config NET_DSA_TAG_DSA ++ tristate "Tag driver for Marvell switches using DSA headers" +++ select NET_DSA_TAG_DSA_COMMON ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ Marvell switches which use DSA headers. ++ ++ config NET_DSA_TAG_EDSA ++ tristate "Tag driver for Marvell switches using EtherType DSA headers" +++ select NET_DSA_TAG_DSA_COMMON ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ Marvell switches which use EtherType DSA headers. ++diff -urpN linux-5.4.137.old/net/dsa/Makefile linux-5.4.137/net/dsa/Makefile ++--- linux-5.4.137.old/net/dsa/Makefile 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/Makefile 2021-08-04 14:05:53.891713717 +0700 ++@@ -6,8 +6,7 @@ dsa_core-y += dsa.o dsa2.o master.o port ++ # tagging formats ++ obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o ++ obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o ++-obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o ++-obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o +++obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o ++ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o ++ obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o ++ obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o ++diff -urpN linux-5.4.137.old/net/dsa/dsa.c linux-5.4.137/net/dsa/dsa.c ++--- linux-5.4.137.old/net/dsa/dsa.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/dsa.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -224,11 +224,21 @@ static int dsa_switch_rcv(struct sk_buff ++ } ++ ++ skb = nskb; ++- p = netdev_priv(skb->dev); ++ skb_push(skb, ETH_HLEN); ++ skb->pkt_type = PACKET_HOST; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ +++ if (unlikely(!dsa_slave_dev_check(skb->dev))) { +++ /* Packet is to be injected directly on an upper +++ * device, e.g. a team/bond, so skip all DSA-port +++ * specific actions. +++ */ +++ netif_rx(skb); +++ return 0; +++ } +++ +++ p = netdev_priv(skb->dev); +++ ++ s = this_cpu_ptr(p->stats64); ++ u64_stats_update_begin(&s->syncp); ++ s->rx_packets++; ++@@ -246,7 +256,9 @@ static int dsa_switch_rcv(struct sk_buff ++ #ifdef CONFIG_PM_SLEEP ++ static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) ++ { ++- return dsa_is_user_port(ds, p) && ds->ports[p].slave; +++ const struct dsa_port *dp = dsa_to_port(ds, p); +++ +++ return dp->type == DSA_PORT_TYPE_USER && dp->slave; ++ } ++ ++ int dsa_switch_suspend(struct dsa_switch *ds) ++@@ -258,7 +270,7 @@ int dsa_switch_suspend(struct dsa_switch ++ if (!dsa_is_port_initialized(ds, i)) ++ continue; ++ ++- ret = dsa_slave_suspend(ds->ports[i].slave); +++ ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave); ++ if (ret) ++ return ret; ++ } ++@@ -285,7 +297,7 @@ int dsa_switch_resume(struct dsa_switch ++ if (!dsa_is_port_initialized(ds, i)) ++ continue; ++ ++- ret = dsa_slave_resume(ds->ports[i].slave); +++ ret = dsa_slave_resume(dsa_to_port(ds, i)->slave); ++ if (ret) ++ return ret; ++ } ++diff -urpN linux-5.4.137.old/net/dsa/dsa2.c linux-5.4.137/net/dsa/dsa2.c ++--- linux-5.4.137.old/net/dsa/dsa2.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/dsa2.c 2021-08-04 14:05:57.643717592 +0700 ++@@ -25,6 +25,65 @@ static DEFINE_MUTEX(dsa2_mutex); ++ static const struct devlink_ops dsa_devlink_ops = { ++ }; ++ +++/** +++ * dsa_lag_map() - Map LAG netdev to a linear LAG ID +++ * @dst: Tree in which to record the mapping. +++ * @lag: Netdev that is to be mapped to an ID. +++ * +++ * dsa_lag_id/dsa_lag_dev can then be used to translate between the +++ * two spaces. The size of the mapping space is determined by the +++ * driver by setting ds->num_lag_ids. It is perfectly legal to leave +++ * it unset if it is not needed, in which case these functions become +++ * no-ops. +++ */ +++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) +++{ +++ unsigned int id; +++ +++ if (dsa_lag_id(dst, lag) >= 0) +++ /* Already mapped */ +++ return; +++ +++ for (id = 0; id < dst->lags_len; id++) { +++ if (!dsa_lag_dev(dst, id)) { +++ dst->lags[id] = lag; +++ return; +++ } +++ } +++ +++ /* No IDs left, which is OK. Some drivers do not need it. The +++ * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id +++ * returns an error for this device when joining the LAG. The +++ * driver can then return -EOPNOTSUPP back to DSA, which will +++ * fall back to a software LAG. +++ */ +++} +++ +++/** +++ * dsa_lag_unmap() - Remove a LAG ID mapping +++ * @dst: Tree in which the mapping is recorded. +++ * @lag: Netdev that was mapped. +++ * +++ * As there may be multiple users of the mapping, it is only removed +++ * if there are no other references to it. +++ */ +++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag) +++{ +++ struct dsa_port *dp; +++ unsigned int id; +++ +++ dsa_lag_foreach_port(dp, dst, lag) +++ /* There are remaining users of this mapping */ +++ return; +++ +++ dsa_lags_foreach_id(id, dst) { +++ if (dsa_lag_dev(dst, id) == lag) { +++ dst->lags[id] = NULL; +++ break; +++ } +++ } +++} +++ ++ static struct dsa_switch_tree *dsa_tree_find(int index) ++ { ++ struct dsa_switch_tree *dst; ++@@ -46,6 +105,10 @@ static struct dsa_switch_tree *dsa_tree_ ++ ++ dst->index = index; ++ +++ INIT_LIST_HEAD(&dst->rtable); +++ +++ INIT_LIST_HEAD(&dst->ports); +++ ++ INIT_LIST_HEAD(&dst->list); ++ list_add_tail(&dst->list, &dsa_tree_list); ++ ++@@ -112,24 +175,38 @@ static bool dsa_port_is_user(struct dsa_ ++ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, ++ struct device_node *dn) ++ { ++- struct dsa_switch *ds; ++ struct dsa_port *dp; ++- int device, port; ++ ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dp->dn == dn) +++ return dp; ++ ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; +++ return NULL; +++} ++ ++- if (dp->dn == dn) ++- return dp; ++- } ++- } +++struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp) +++{ +++ struct dsa_switch *ds = dp->ds; +++ struct dsa_switch_tree *dst; +++ struct dsa_link *dl; ++ ++- return NULL; +++ dst = ds->dst; +++ +++ list_for_each_entry(dl, &dst->rtable, list) +++ if (dl->dp == dp && dl->link_dp == link_dp) +++ return dl; +++ +++ dl = kzalloc(sizeof(*dl), GFP_KERNEL); +++ if (!dl) +++ return NULL; +++ +++ dl->dp = dp; +++ dl->link_dp = link_dp; +++ +++ INIT_LIST_HEAD(&dl->list); +++ list_add_tail(&dl->list, &dst->rtable); +++ +++ return dl; ++ } ++ ++ static bool dsa_port_setup_routing_table(struct dsa_port *dp) ++@@ -139,6 +216,7 @@ static bool dsa_port_setup_routing_table ++ struct device_node *dn = dp->dn; ++ struct of_phandle_iterator it; ++ struct dsa_port *link_dp; +++ struct dsa_link *dl; ++ int err; ++ ++ of_for_each_phandle(&it, err, dn, "link", NULL, 0) { ++@@ -148,24 +226,22 @@ static bool dsa_port_setup_routing_table ++ return false; ++ } ++ ++- ds->rtable[link_dp->ds->index] = dp->index; +++ dl = dsa_link_touch(dp, link_dp); +++ if (!dl) { +++ of_node_put(it.node); +++ return false; +++ } ++ } ++ ++ return true; ++ } ++ ++-static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) +++static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) ++ { ++ bool complete = true; ++ struct dsa_port *dp; ++- int i; ++- ++- for (i = 0; i < DSA_MAX_SWITCHES; i++) ++- ds->rtable[i] = DSA_RTABLE_NONE; ++- ++- for (i = 0; i < ds->num_ports; i++) { ++- dp = &ds->ports[i]; ++ +++ list_for_each_entry(dp, &dst->ports, list) { ++ if (dsa_port_is_dsa(dp)) { ++ complete = dsa_port_setup_routing_table(dp); ++ if (!complete) ++@@ -176,81 +252,42 @@ static bool dsa_switch_setup_routing_tab ++ return complete; ++ } ++ ++-static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) ++-{ ++- struct dsa_switch *ds; ++- bool complete = true; ++- int device; ++- ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; ++- ++- complete = dsa_switch_setup_routing_table(ds); ++- if (!complete) ++- break; ++- } ++- ++- return complete; ++-} ++- ++ static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) ++ { ++- struct dsa_switch *ds; ++ struct dsa_port *dp; ++- int device, port; ++ ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; ++- ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; ++- ++- if (dsa_port_is_cpu(dp)) ++- return dp; ++- } ++- } +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dsa_port_is_cpu(dp)) +++ return dp; ++ ++ return NULL; ++ } ++ ++ static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) ++ { ++- struct dsa_switch *ds; ++- struct dsa_port *dp; ++- int device, port; +++ struct dsa_port *cpu_dp, *dp; ++ ++- /* DSA currently only supports a single CPU port */ ++- dst->cpu_dp = dsa_tree_find_first_cpu(dst); ++- if (!dst->cpu_dp) { ++- pr_warn("Tree has no master device\n"); +++ cpu_dp = dsa_tree_find_first_cpu(dst); +++ if (!cpu_dp) { +++ pr_err("DSA: tree %d has no CPU port\n", dst->index); ++ return -EINVAL; ++ } ++ ++ /* Assign the default CPU port to all ports of the fabric */ ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; ++- ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; ++- ++- if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) ++- dp->cpu_dp = dst->cpu_dp; ++- } ++- } +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) +++ dp->cpu_dp = cpu_dp; ++ ++ return 0; ++ } ++ ++ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) ++ { ++- /* DSA currently only supports a single CPU port */ ++- dst->cpu_dp = NULL; +++ struct dsa_port *dp; +++ +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) +++ dp->cpu_dp = NULL; ++ } ++ ++ static int dsa_port_setup(struct dsa_port *dp) ++@@ -266,6 +303,9 @@ static int dsa_port_setup(struct dsa_por ++ bool dsa_port_enabled = false; ++ int err = 0; ++ +++ if (dp->setup) +++ return 0; +++ ++ switch (dp->type) { ++ case DSA_PORT_TYPE_UNUSED: ++ dsa_port_disable(dp); ++@@ -335,14 +375,21 @@ static int dsa_port_setup(struct dsa_por ++ dsa_port_link_unregister_of(dp); ++ if (err && devlink_port_registered) ++ devlink_port_unregister(dlp); +++ if (err) +++ return err; ++ ++- return err; +++ dp->setup = true; +++ +++ return 0; ++ } ++ ++ static void dsa_port_teardown(struct dsa_port *dp) ++ { ++ struct devlink_port *dlp = &dp->devlink_port; ++ +++ if (!dp->setup) +++ return; +++ ++ switch (dp->type) { ++ case DSA_PORT_TYPE_UNUSED: ++ break; ++@@ -365,11 +412,16 @@ static void dsa_port_teardown(struct dsa ++ } ++ break; ++ } +++ +++ dp->setup = false; ++ } ++ ++ static int dsa_switch_setup(struct dsa_switch *ds) ++ { ++- int err = 0; +++ int err; +++ +++ if (ds->setup) +++ return 0; ++ ++ /* Initialize ds->phys_mii_mask before registering the slave MDIO bus ++ * driver and before ops->setup() has run, since the switch drivers and ++@@ -411,6 +463,8 @@ static int dsa_switch_setup(struct dsa_s ++ goto teardown; ++ } ++ +++ ds->setup = true; +++ ++ return 0; ++ ++ teardown: ++@@ -429,6 +483,9 @@ free_devlink: ++ ++ static void dsa_switch_teardown(struct dsa_switch *ds) ++ { +++ if (!ds->setup) +++ return; +++ ++ if (ds->slave_mii_bus && ds->ops->phy_read) ++ mdiobus_unregister(ds->slave_mii_bus); ++ ++@@ -443,89 +500,98 @@ static void dsa_switch_teardown(struct d ++ ds->devlink = NULL; ++ } ++ +++ ds->setup = false; ++ } ++ ++ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) ++ { ++- struct dsa_switch *ds; ++ struct dsa_port *dp; ++- int device, port, i; ++- int err = 0; ++- ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; +++ int err; ++ ++- err = dsa_switch_setup(ds); +++ list_for_each_entry(dp, &dst->ports, list) { +++ err = dsa_switch_setup(dp->ds); ++ if (err) ++- goto switch_teardown; ++- ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; ++- ++- err = dsa_port_setup(dp); ++- if (err) ++- continue; ++- } +++ goto teardown; ++ } ++ ++- return 0; ++- ++-switch_teardown: ++- for (i = 0; i < device; i++) { ++- ds = dst->ds[i]; ++- if (!ds) ++- continue; ++- ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; +++ list_for_each_entry(dp, &dst->ports, list) { +++ err = dsa_port_setup(dp); +++ if (err) +++ goto teardown; +++ } ++ ++- dsa_port_teardown(dp); ++- } +++ return 0; ++ ++- dsa_switch_teardown(ds); ++- } +++teardown: +++ list_for_each_entry(dp, &dst->ports, list) +++ dsa_port_teardown(dp); +++ +++ list_for_each_entry(dp, &dst->ports, list) +++ dsa_switch_teardown(dp->ds); ++ ++ return err; ++ } ++ ++ static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) ++ { ++- struct dsa_switch *ds; ++ struct dsa_port *dp; ++- int device, port; ++ ++- for (device = 0; device < DSA_MAX_SWITCHES; device++) { ++- ds = dst->ds[device]; ++- if (!ds) ++- continue; +++ list_for_each_entry(dp, &dst->ports, list) +++ dsa_port_teardown(dp); ++ ++- for (port = 0; port < ds->num_ports; port++) { ++- dp = &ds->ports[port]; +++ list_for_each_entry(dp, &dst->ports, list) +++ dsa_switch_teardown(dp->ds); +++} ++ ++- dsa_port_teardown(dp); ++- } +++static int dsa_tree_setup_master(struct dsa_switch_tree *dst) +++{ +++ struct dsa_port *dp; +++ int err; ++ ++- dsa_switch_teardown(ds); +++ list_for_each_entry(dp, &dst->ports, list) { +++ if (dsa_port_is_cpu(dp)) { +++ err = dsa_master_setup(dp->master, dp); +++ if (err) +++ return err; +++ } ++ } +++ +++ return 0; ++ } ++ ++-static int dsa_tree_setup_master(struct dsa_switch_tree *dst) +++static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) ++ { ++- struct dsa_port *cpu_dp = dst->cpu_dp; ++- struct net_device *master = cpu_dp->master; +++ struct dsa_port *dp; ++ ++- /* DSA currently supports a single pair of CPU port and master device */ ++- return dsa_master_setup(master, cpu_dp); +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dsa_port_is_cpu(dp)) +++ dsa_master_teardown(dp->master); ++ } ++ ++-static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) +++static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) ++ { ++- struct dsa_port *cpu_dp = dst->cpu_dp; ++- struct net_device *master = cpu_dp->master; +++ unsigned int len = 0; +++ struct dsa_port *dp; +++ +++ list_for_each_entry(dp, &dst->ports, list) { +++ if (dp->ds->num_lag_ids > len) +++ len = dp->ds->num_lag_ids; +++ } ++ ++- return dsa_master_teardown(master); +++ if (!len) +++ return 0; +++ +++ dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL); +++ if (!dst->lags) +++ return -ENOMEM; +++ +++ dst->lags_len = len; +++ return 0; +++} +++ +++static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst) +++{ +++ kfree(dst->lags); ++ } ++ ++ static int dsa_tree_setup(struct dsa_switch_tree *dst) ++@@ -555,12 +621,18 @@ static int dsa_tree_setup(struct dsa_swi ++ if (err) ++ goto teardown_switches; ++ +++ err = dsa_tree_setup_lags(dst); +++ if (err) +++ goto teardown_master; +++ ++ dst->setup = true; ++ ++ pr_info("DSA: tree %d setup\n", dst->index); ++ ++ return 0; ++ +++teardown_master: +++ dsa_tree_teardown_master(dst); ++ teardown_switches: ++ dsa_tree_teardown_switches(dst); ++ teardown_default_cpu: ++@@ -571,48 +643,49 @@ teardown_default_cpu: ++ ++ static void dsa_tree_teardown(struct dsa_switch_tree *dst) ++ { +++ struct dsa_link *dl, *next; +++ ++ if (!dst->setup) ++ return; ++ +++ dsa_tree_teardown_lags(dst); +++ ++ dsa_tree_teardown_master(dst); ++ ++ dsa_tree_teardown_switches(dst); ++ ++ dsa_tree_teardown_default_cpu(dst); ++ +++ list_for_each_entry_safe(dl, next, &dst->rtable, list) { +++ list_del(&dl->list); +++ kfree(dl); +++ } +++ ++ pr_info("DSA: tree %d torn down\n", dst->index); ++ ++ dst->setup = false; ++ } ++ ++-static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, ++- unsigned int index) +++static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) ++ { ++- dsa_tree_teardown(dst); +++ struct dsa_switch_tree *dst = ds->dst; +++ struct dsa_port *dp; ++ ++- dst->ds[index] = NULL; ++- dsa_tree_put(dst); ++-} +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dp->ds == ds && dp->index == index) +++ return dp; ++ ++-static int dsa_tree_add_switch(struct dsa_switch_tree *dst, ++- struct dsa_switch *ds) ++-{ ++- unsigned int index = ds->index; ++- int err; +++ dp = kzalloc(sizeof(*dp), GFP_KERNEL); +++ if (!dp) +++ return NULL; ++ ++- if (dst->ds[index]) ++- return -EBUSY; +++ dp->ds = ds; +++ dp->index = index; ++ ++- dsa_tree_get(dst); ++- dst->ds[index] = ds; +++ INIT_LIST_HEAD(&dp->list); +++ list_add_tail(&dp->list, &dst->ports); ++ ++- err = dsa_tree_setup(dst); ++- if (err) { ++- dst->ds[index] = NULL; ++- dsa_tree_put(dst); ++- } ++- ++- return err; +++ return dp; ++ } ++ ++ static int dsa_port_parse_user(struct dsa_port *dp, const char *name) ++@@ -707,7 +780,7 @@ static int dsa_switch_parse_ports_of(str ++ goto out_put_node; ++ } ++ ++- dp = &ds->ports[reg]; +++ dp = dsa_to_port(ds, reg); ++ ++ err = dsa_port_parse_of(dp, port); ++ if (err) ++@@ -731,8 +804,6 @@ static int dsa_switch_parse_member_of(st ++ return sz; ++ ++ ds->index = m[1]; ++- if (ds->index >= DSA_MAX_SWITCHES) ++- return -EINVAL; ++ ++ ds->dst = dsa_tree_touch(m[0]); ++ if (!ds->dst) ++@@ -741,6 +812,20 @@ static int dsa_switch_parse_member_of(st ++ return 0; ++ } ++ +++static int dsa_switch_touch_ports(struct dsa_switch *ds) +++{ +++ struct dsa_port *dp; +++ int port; +++ +++ for (port = 0; port < ds->num_ports; port++) { +++ dp = dsa_port_touch(ds, port); +++ if (!dp) +++ return -ENOMEM; +++ } +++ +++ return 0; +++} +++ ++ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) ++ { ++ int err; ++@@ -749,6 +834,10 @@ static int dsa_switch_parse_of(struct ds ++ if (err) ++ return err; ++ +++ err = dsa_switch_touch_ports(ds); +++ if (err) +++ return err; +++ ++ return dsa_switch_parse_ports_of(ds, dn); ++ } ++ ++@@ -786,7 +875,7 @@ static int dsa_switch_parse_ports(struct ++ for (i = 0; i < DSA_MAX_PORTS; i++) { ++ name = cd->port_names[i]; ++ dev = cd->netdev[i]; ++- dp = &ds->ports[i]; +++ dp = dsa_to_port(ds, i); ++ ++ if (!name) ++ continue; ++@@ -806,6 +895,8 @@ static int dsa_switch_parse_ports(struct ++ ++ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) ++ { +++ int err; +++ ++ ds->cd = cd; ++ ++ /* We don't support interconnected switches nor multiple trees via ++@@ -816,22 +907,26 @@ static int dsa_switch_parse(struct dsa_s ++ if (!ds->dst) ++ return -ENOMEM; ++ ++- return dsa_switch_parse_ports(ds, cd); ++-} ++- ++-static int dsa_switch_add(struct dsa_switch *ds) ++-{ ++- struct dsa_switch_tree *dst = ds->dst; +++ err = dsa_switch_touch_ports(ds); +++ if (err) +++ return err; ++ ++- return dsa_tree_add_switch(dst, ds); +++ return dsa_switch_parse_ports(ds, cd); ++ } ++ ++ static int dsa_switch_probe(struct dsa_switch *ds) ++ { +++ struct dsa_switch_tree *dst; ++ struct dsa_chip_data *pdata = ds->dev->platform_data; ++ struct device_node *np = ds->dev->of_node; ++ int err; ++ +++ if (!ds->dev) +++ return -ENODEV; +++ +++ if (!ds->num_ports) +++ return -EINVAL; +++ ++ if (np) ++ err = dsa_switch_parse_of(ds, np); ++ else if (pdata) ++@@ -842,29 +937,14 @@ static int dsa_switch_probe(struct dsa_s ++ if (err) ++ return err; ++ ++- return dsa_switch_add(ds); ++-} ++- ++-struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) ++-{ ++- struct dsa_switch *ds; ++- int i; ++- ++- ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL); ++- if (!ds) ++- return NULL; ++- ++- ds->dev = dev; ++- ds->num_ports = n; ++- ++- for (i = 0; i < ds->num_ports; ++i) { ++- ds->ports[i].index = i; ++- ds->ports[i].ds = ds; ++- } +++ dst = ds->dst; +++ dsa_tree_get(dst); +++ err = dsa_tree_setup(dst); +++ if (err) +++ dsa_tree_put(dst); ++ ++- return ds; +++ return err; ++ } ++-EXPORT_SYMBOL_GPL(dsa_switch_alloc); ++ ++ int dsa_register_switch(struct dsa_switch *ds) ++ { ++@@ -882,9 +962,15 @@ EXPORT_SYMBOL_GPL(dsa_register_switch); ++ static void dsa_switch_remove(struct dsa_switch *ds) ++ { ++ struct dsa_switch_tree *dst = ds->dst; ++- unsigned int index = ds->index; +++ struct dsa_port *dp, *next; +++ +++ list_for_each_entry_safe(dp, next, &dst->ports, list) { +++ list_del(&dp->list); +++ kfree(dp); +++ } ++ ++- dsa_tree_remove_switch(dst, index); +++ dsa_tree_teardown(dst); +++ dsa_tree_put(dst); ++ } ++ ++ void dsa_unregister_switch(struct dsa_switch *ds) ++diff -urpN linux-5.4.137.old/net/dsa/dsa_priv.h linux-5.4.137/net/dsa/dsa_priv.h ++--- linux-5.4.137.old/net/dsa/dsa_priv.h 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/dsa_priv.h 2021-08-04 14:05:53.891713717 +0700 ++@@ -19,6 +19,9 @@ enum { ++ DSA_NOTIFIER_BRIDGE_LEAVE, ++ DSA_NOTIFIER_FDB_ADD, ++ DSA_NOTIFIER_FDB_DEL, +++ DSA_NOTIFIER_LAG_CHANGE, +++ DSA_NOTIFIER_LAG_JOIN, +++ DSA_NOTIFIER_LAG_LEAVE, ++ DSA_NOTIFIER_MDB_ADD, ++ DSA_NOTIFIER_MDB_DEL, ++ DSA_NOTIFIER_VLAN_ADD, ++@@ -54,6 +57,15 @@ struct dsa_notifier_mdb_info { ++ int port; ++ }; ++ +++/* DSA_NOTIFIER_LAG_* */ +++struct dsa_notifier_lag_info { +++ struct net_device *lag; +++ int sw_index; +++ int port; +++ +++ struct netdev_lag_upper_info *info; +++}; +++ ++ /* DSA_NOTIFIER_VLAN_* */ ++ struct dsa_notifier_vlan_info { ++ const struct switchdev_obj_port_vlan *vlan; ++@@ -119,25 +131,14 @@ static inline struct net_device *dsa_mas ++ { ++ struct dsa_port *cpu_dp = dev->dsa_ptr; ++ struct dsa_switch_tree *dst = cpu_dp->dst; ++- struct dsa_switch *ds; ++- struct dsa_port *slave_port; ++- ++- if (device < 0 || device >= DSA_MAX_SWITCHES) ++- return NULL; ++- ++- ds = dst->ds[device]; ++- if (!ds) ++- return NULL; ++- ++- if (port < 0 || port >= ds->num_ports) ++- return NULL; +++ struct dsa_port *dp; ++ ++- slave_port = &ds->ports[port]; +++ list_for_each_entry(dp, &dst->ports, list) +++ if (dp->ds->index == device && dp->index == port && +++ dp->type == DSA_PORT_TYPE_USER) +++ return dp->slave; ++ ++- if (unlikely(slave_port->type != DSA_PORT_TYPE_USER)) ++- return NULL; ++- ++- return slave_port->slave; +++ return NULL; ++ } ++ ++ /* port.c */ ++@@ -149,6 +150,11 @@ void dsa_port_disable_rt(struct dsa_port ++ void dsa_port_disable(struct dsa_port *dp); ++ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br); ++ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); +++int dsa_port_lag_change(struct dsa_port *dp, +++ struct netdev_lag_lower_state_info *linfo); +++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, +++ struct netdev_lag_upper_info *uinfo); +++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); ++ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, ++ struct switchdev_trans *trans); ++ bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); ++@@ -197,6 +203,22 @@ void dsa_port_phylink_mac_link_up(struct ++ struct phy_device *phydev); ++ extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; ++ +++static inline bool dsa_port_offloads_netdev(struct dsa_port *dp, +++ struct net_device *dev) +++{ +++ /* Switchdev offloading can be configured on: */ +++ +++ if (dev == dp->slave) +++ /* DSA ports directly connected to a bridge. */ +++ return true; +++ +++ if (dp->lag_dev == dev) +++ /* DSA ports connected to a bridge via a LAG */ +++ return true; +++ +++ return false; +++} +++ ++ /* slave.c */ ++ extern const struct dsa_device_ops notag_netdev_ops; ++ void dsa_slave_mii_bus_init(struct dsa_switch *ds); ++@@ -227,4 +249,9 @@ dsa_slave_to_master(const struct net_dev ++ /* switch.c */ ++ int dsa_switch_register_notifier(struct dsa_switch *ds); ++ void dsa_switch_unregister_notifier(struct dsa_switch *ds); +++ +++/* dsa2.c */ +++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag); +++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag); +++ ++ #endif ++diff -urpN linux-5.4.137.old/net/dsa/port.c linux-5.4.137/net/dsa/port.c ++--- linux-5.4.137.old/net/dsa/port.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/port.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -174,6 +174,85 @@ void dsa_port_bridge_leave(struct dsa_po ++ dsa_port_set_state_now(dp, BR_STATE_FORWARDING); ++ } ++ +++int dsa_port_lag_change(struct dsa_port *dp, +++ struct netdev_lag_lower_state_info *linfo) +++{ +++ struct dsa_notifier_lag_info info = { +++ .sw_index = dp->ds->index, +++ .port = dp->index, +++ }; +++ bool tx_enabled; +++ +++ if (!dp->lag_dev) +++ return 0; +++ +++ /* On statically configured aggregates (e.g. loadbalance +++ * without LACP) ports will always be tx_enabled, even if the +++ * link is down. Thus we require both link_up and tx_enabled +++ * in order to include it in the tx set. +++ */ +++ tx_enabled = linfo->link_up && linfo->tx_enabled; +++ +++ if (tx_enabled == dp->lag_tx_enabled) +++ return 0; +++ +++ dp->lag_tx_enabled = tx_enabled; +++ +++ return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); +++} +++ +++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, +++ struct netdev_lag_upper_info *uinfo) +++{ +++ struct dsa_notifier_lag_info info = { +++ .sw_index = dp->ds->index, +++ .port = dp->index, +++ .lag = lag, +++ .info = uinfo, +++ }; +++ int err; +++ +++ dsa_lag_map(dp->ds->dst, lag); +++ dp->lag_dev = lag; +++ +++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); +++ if (err) { +++ dp->lag_dev = NULL; +++ dsa_lag_unmap(dp->ds->dst, lag); +++ } +++ +++ return err; +++} +++ +++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) +++{ +++ struct dsa_notifier_lag_info info = { +++ .sw_index = dp->ds->index, +++ .port = dp->index, +++ .lag = lag, +++ }; +++ int err; +++ +++ if (!dp->lag_dev) +++ return; +++ +++ /* Port might have been part of a LAG that in turn was +++ * attached to a bridge. +++ */ +++ if (dp->bridge_dev) +++ dsa_port_bridge_leave(dp, dp->bridge_dev); +++ +++ dp->lag_tx_enabled = false; +++ dp->lag_dev = NULL; +++ +++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); +++ if (err) +++ pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", +++ err); +++ +++ dsa_lag_unmap(dp->ds->dst, lag); +++} +++ ++ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, ++ bool vlan_filtering) ++ { ++diff -urpN linux-5.4.137.old/net/dsa/slave.c linux-5.4.137/net/dsa/slave.c ++--- linux-5.4.137.old/net/dsa/slave.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/slave.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -26,8 +26,6 @@ ++ ++ #include "dsa_priv.h" ++ ++-static bool dsa_slave_dev_check(const struct net_device *dev); ++- ++ /* slave mii_bus handling ***************************************************/ ++ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) ++ { ++@@ -286,6 +284,9 @@ static int dsa_slave_port_attr_set(struc ++ struct dsa_port *dp = dsa_slave_to_port(dev); ++ int ret; ++ +++ if (!dsa_port_offloads_netdev(dp, attr->orig_dev)) +++ return -EOPNOTSUPP; +++ ++ switch (attr->id) { ++ case SWITCHDEV_ATTR_ID_PORT_STP_STATE: ++ ret = dsa_port_set_state(dp, attr->u.stp_state, trans); ++@@ -323,7 +324,7 @@ static int dsa_slave_vlan_add(struct net ++ struct switchdev_obj_port_vlan vlan; ++ int err; ++ ++- if (obj->orig_dev != dev) +++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ ++ if (dsa_port_skip_vlan_configuration(dp)) ++@@ -363,7 +364,7 @@ static int dsa_slave_port_obj_add(struct ++ ++ switch (obj->id) { ++ case SWITCHDEV_OBJ_ID_PORT_MDB: ++- if (obj->orig_dev != dev) +++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); ++ break; ++@@ -390,7 +391,7 @@ static int dsa_slave_vlan_del(struct net ++ { ++ struct dsa_port *dp = dsa_slave_to_port(dev); ++ ++- if (obj->orig_dev != dev) +++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ ++ if (dsa_port_skip_vlan_configuration(dp)) ++@@ -410,7 +411,7 @@ static int dsa_slave_port_obj_del(struct ++ ++ switch (obj->id) { ++ case SWITCHDEV_OBJ_ID_PORT_MDB: ++- if (obj->orig_dev != dev) +++ if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); ++ break; ++@@ -1527,10 +1528,11 @@ void dsa_slave_destroy(struct net_device ++ free_netdev(slave_dev); ++ } ++ ++-static bool dsa_slave_dev_check(const struct net_device *dev) +++bool dsa_slave_dev_check(const struct net_device *dev) ++ { ++ return dev->netdev_ops == &dsa_slave_netdev_ops; ++ } +++EXPORT_SYMBOL_GPL(dsa_slave_dev_check); ++ ++ static int dsa_slave_changeupper(struct net_device *dev, ++ struct netdev_notifier_changeupper_info *info) ++@@ -1546,6 +1548,46 @@ static int dsa_slave_changeupper(struct ++ dsa_port_bridge_leave(dp, info->upper_dev); ++ err = NOTIFY_OK; ++ } +++ } else if (netif_is_lag_master(info->upper_dev)) { +++ if (info->linking) { +++ err = dsa_port_lag_join(dp, info->upper_dev, +++ info->upper_info); +++ if (err == -EOPNOTSUPP) { +++ NL_SET_ERR_MSG_MOD(info->info.extack, +++ "Offloading not supported"); +++ err = 0; +++ } +++ err = notifier_from_errno(err); +++ } else { +++ dsa_port_lag_leave(dp, info->upper_dev); +++ err = NOTIFY_OK; +++ } +++ } +++ +++ return err; +++} +++ +++static int +++dsa_slave_lag_changeupper(struct net_device *dev, +++ struct netdev_notifier_changeupper_info *info) +++{ +++ struct net_device *lower; +++ struct list_head *iter; +++ int err = NOTIFY_DONE; +++ struct dsa_port *dp; +++ +++ netdev_for_each_lower_dev(dev, lower, iter) { +++ if (!dsa_slave_dev_check(lower)) +++ continue; +++ +++ dp = dsa_slave_to_port(lower); +++ if (!dp->lag_dev) +++ /* Software LAG */ +++ continue; +++ +++ err = dsa_slave_changeupper(lower, info); +++ if (notifier_to_errno(err)) +++ break; ++ } ++ ++ return err; ++@@ -1588,11 +1630,33 @@ static int dsa_slave_netdevice_event(str ++ { ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++- if (event == NETDEV_CHANGEUPPER) { +++ switch(event) { +++ case NETDEV_PRECHANGEUPPER: ++ if (!dsa_slave_dev_check(dev)) ++ return dsa_slave_upper_vlan_check(dev, ptr); ++ ++- return dsa_slave_changeupper(dev, ptr); +++ break; +++ case NETDEV_CHANGEUPPER: +++ if (dsa_slave_dev_check(dev)) +++ return dsa_slave_changeupper(dev, ptr); +++ +++ if (netif_is_lag_master(dev)) +++ return dsa_slave_lag_changeupper(dev, ptr); +++ +++ break; +++ case NETDEV_CHANGELOWERSTATE: { +++ struct netdev_notifier_changelowerstate_info *info = ptr; +++ struct dsa_port *dp; +++ int err; +++ +++ if (!dsa_slave_dev_check(dev)) +++ break; +++ +++ dp = dsa_slave_to_port(dev); +++ +++ err = dsa_port_lag_change(dp, info->lower_state_info); +++ return notifier_from_errno(err); +++ } ++ } ++ ++ return NOTIFY_DONE; ++diff -urpN linux-5.4.137.old/net/dsa/switch.c linux-5.4.137/net/dsa/switch.c ++--- linux-5.4.137.old/net/dsa/switch.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/switch.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -20,7 +20,7 @@ static unsigned int dsa_switch_fastest_a ++ int i; ++ ++ for (i = 0; i < ds->num_ports; ++i) { ++- struct dsa_port *dp = &ds->ports[i]; +++ struct dsa_port *dp = dsa_to_port(ds, i); ++ ++ if (dp->ageing_time && dp->ageing_time < ageing_time) ++ ageing_time = dp->ageing_time; ++@@ -98,7 +98,7 @@ static int dsa_switch_bridge_leave(struc ++ if (unset_vlan_filtering) { ++ struct switchdev_trans trans = {0}; ++ ++- err = dsa_port_vlan_filtering(&ds->ports[info->port], +++ err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), ++ false, &trans); ++ if (err && err != EOPNOTSUPP) ++ return err; ++@@ -128,6 +128,47 @@ static int dsa_switch_fdb_del(struct dsa ++ return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); ++ } ++ +++static int dsa_switch_lag_change(struct dsa_switch *ds, +++ struct dsa_notifier_lag_info *info) +++{ +++ if (ds->index == info->sw_index && ds->ops->port_lag_change) +++ return ds->ops->port_lag_change(ds, info->port); +++ +++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) +++ return ds->ops->crosschip_lag_change(ds, info->sw_index, +++ info->port); +++ +++ return 0; +++} +++ +++static int dsa_switch_lag_join(struct dsa_switch *ds, +++ struct dsa_notifier_lag_info *info) +++{ +++ if (ds->index == info->sw_index && ds->ops->port_lag_join) +++ return ds->ops->port_lag_join(ds, info->port, info->lag, +++ info->info); +++ +++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) +++ return ds->ops->crosschip_lag_join(ds, info->sw_index, +++ info->port, info->lag, +++ info->info); +++ +++ return 0; +++} +++ +++static int dsa_switch_lag_leave(struct dsa_switch *ds, +++ struct dsa_notifier_lag_info *info) +++{ +++ if (ds->index == info->sw_index && ds->ops->port_lag_leave) +++ return ds->ops->port_lag_leave(ds, info->port, info->lag); +++ +++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) +++ return ds->ops->crosschip_lag_leave(ds, info->sw_index, +++ info->port, info->lag); +++ +++ return 0; +++} +++ ++ static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, ++ struct dsa_notifier_mdb_info *info) ++ { ++@@ -316,6 +357,15 @@ static int dsa_switch_event(struct notif ++ case DSA_NOTIFIER_FDB_DEL: ++ err = dsa_switch_fdb_del(ds, info); ++ break; +++ case DSA_NOTIFIER_LAG_CHANGE: +++ err = dsa_switch_lag_change(ds, info); +++ break; +++ case DSA_NOTIFIER_LAG_JOIN: +++ err = dsa_switch_lag_join(ds, info); +++ break; +++ case DSA_NOTIFIER_LAG_LEAVE: +++ err = dsa_switch_lag_leave(ds, info); +++ break; ++ case DSA_NOTIFIER_MDB_ADD: ++ err = dsa_switch_mdb_add(ds, info); ++ break; ++diff -urpN linux-5.4.137.old/net/dsa/tag_8021q.c linux-5.4.137/net/dsa/tag_8021q.c ++--- linux-5.4.137.old/net/dsa/tag_8021q.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/tag_8021q.c 2021-08-04 14:05:57.643717592 +0700 ++@@ -31,15 +31,14 @@ ++ * Must be transmitted as zero and ignored on receive. ++ * ++ * SWITCH_ID - VID[8:6]: ++- * Index of switch within DSA tree. Must be between 0 and ++- * DSA_MAX_SWITCHES - 1. +++ * Index of switch within DSA tree. Must be between 0 and 7. ++ * ++ * RSV - VID[5:4]: ++ * To be used for further expansion of PORT or for other purposes. ++ * Must be transmitted as zero and ignored on receive. ++ * ++ * PORT - VID[3:0]: ++- * Index of switch port. Must be between 0 and DSA_MAX_PORTS - 1. +++ * Index of switch port. Must be between 0 and 15. ++ */ ++ ++ #define DSA_8021Q_DIR_SHIFT 10 ++@@ -103,7 +102,7 @@ static int dsa_8021q_restore_pvid(struct ++ if (!dsa_is_user_port(ds, port)) ++ return 0; ++ ++- slave = ds->ports[port].slave; +++ slave = dsa_to_port(ds, port)->slave; ++ ++ err = br_vlan_get_pvid(slave, &pvid); ++ if (!pvid || err < 0) ++@@ -118,7 +117,7 @@ static int dsa_8021q_restore_pvid(struct ++ return err; ++ } ++ ++- return dsa_port_vid_add(&ds->ports[port], pvid, vinfo.flags); +++ return dsa_port_vid_add(dsa_to_port(ds, port), pvid, vinfo.flags); ++ } ++ ++ /* If @enabled is true, installs @vid with @flags into the switch port's HW ++@@ -130,7 +129,7 @@ static int dsa_8021q_restore_pvid(struct ++ static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, ++ u16 flags, bool enabled) ++ { ++- struct dsa_port *dp = &ds->ports[port]; +++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct bridge_vlan_info vinfo; ++ int err; ++ ++diff -urpN linux-5.4.137.old/net/dsa/tag_dsa.c linux-5.4.137/net/dsa/tag_dsa.c ++--- linux-5.4.137.old/net/dsa/tag_dsa.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/tag_dsa.c 2021-08-04 14:05:53.891713717 +0700 ++@@ -1,7 +1,48 @@ ++ // SPDX-License-Identifier: GPL-2.0+ ++ /* ++- * net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging +++ * Regular and Ethertype DSA tagging ++ * Copyright (c) 2008-2009 Marvell Semiconductor +++ * +++ * Regular DSA +++ * ----------- +++ +++ * For untagged (in 802.1Q terms) packets, the switch will splice in +++ * the tag between the SA and the ethertype of the original +++ * packet. Tagged frames will instead have their outermost .1Q tag +++ * converted to a DSA tag. It expects the same layout when receiving +++ * packets from the CPU. +++ * +++ * Example: +++ * +++ * .----.----.----.--------- +++ * Pu: | DA | SA | ET | Payload ... +++ * '----'----'----'--------- +++ * 6 6 2 N +++ * .----.----.--------.-----.----.--------- +++ * Pt: | DA | SA | 0x8100 | TCI | ET | Payload ... +++ * '----'----'--------'-----'----'--------- +++ * 6 6 2 2 2 N +++ * .----.----.-----.----.--------- +++ * Pd: | DA | SA | DSA | ET | Payload ... +++ * '----'----'-----'----'--------- +++ * 6 6 4 2 N +++ * +++ * No matter if a packet is received untagged (Pu) or tagged (Pt), +++ * they will both have the same layout (Pd) when they are sent to the +++ * CPU. This is done by ignoring 802.3, replacing the ethertype field +++ * with more metadata, among which is a bit to signal if the original +++ * packet was tagged or not. +++ * +++ * Ethertype DSA +++ * ------------- +++ * Uses the exact same tag format as regular DSA, but also includes a +++ * proper ethertype field (which the mv88e6xxx driver sets to +++ * ETH_P_EDSA/0xdada) followed by two zero bytes: +++ * +++ * .----.----.--------.--------.-----.----.--------- +++ * | DA | SA | 0xdada | 0x0000 | DSA | ET | Payload ... +++ * '----'----'--------'--------'-----'----'--------- +++ * 6 6 2 2 4 2 N ++ */ ++ ++ #include ++@@ -12,16 +53,81 @@ ++ ++ #define DSA_HLEN 4 ++ ++-static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) +++/** +++ * enum dsa_cmd - DSA Command +++ * @DSA_CMD_TO_CPU: Set on packets that were trapped or mirrored to +++ * the CPU port. This is needed to implement control protocols, +++ * e.g. STP and LLDP, that must not allow those control packets to +++ * be switched according to the normal rules. +++ * @DSA_CMD_FROM_CPU: Used by the CPU to send a packet to a specific +++ * port, ignoring all the barriers that the switch normally +++ * enforces (VLANs, STP port states etc.). No source address +++ * learning takes place. "sudo send packet" +++ * @DSA_CMD_TO_SNIFFER: Set on the copies of packets that matched some +++ * user configured ingress or egress monitor criteria. These are +++ * forwarded by the switch tree to the user configured ingress or +++ * egress monitor port, which can be set to the CPU port or a +++ * regular port. If the destination is a regular port, the tag +++ * will be removed before egressing the port. If the destination +++ * is the CPU port, the tag will not be removed. +++ * @DSA_CMD_FORWARD: This tag is used on all bulk traffic passing +++ * through the switch tree, including the flows that are directed +++ * towards the CPU. Its device/port tuple encodes the original +++ * source port on which the packet ingressed. It can also be used +++ * on transmit by the CPU to defer the forwarding decision to the +++ * hardware, based on the current config of PVT/VTU/ATU +++ * etc. Source address learning takes places if enabled on the +++ * receiving DSA/CPU port. +++ */ +++enum dsa_cmd { +++ DSA_CMD_TO_CPU = 0, +++ DSA_CMD_FROM_CPU = 1, +++ DSA_CMD_TO_SNIFFER = 2, +++ DSA_CMD_FORWARD = 3 +++}; +++ +++/** +++ * enum dsa_code - TO_CPU Code +++ * +++ * @DSA_CODE_MGMT_TRAP: DA was classified as a management +++ * address. Typical examples include STP BPDUs and LLDP. +++ * @DSA_CODE_FRAME2REG: Response to a "remote management" request. +++ * @DSA_CODE_IGMP_MLD_TRAP: IGMP/MLD signaling. +++ * @DSA_CODE_POLICY_TRAP: Frame matched some policy configuration on +++ * the device. Typical examples are matching on DA/SA/VID and DHCP +++ * snooping. +++ * @DSA_CODE_ARP_MIRROR: The name says it all really. +++ * @DSA_CODE_POLICY_MIRROR: Same as @DSA_CODE_POLICY_TRAP, but the +++ * particular policy was set to trigger a mirror instead of a +++ * trap. +++ * @DSA_CODE_RESERVED_6: Unused on all devices up to at least 6393X. +++ * @DSA_CODE_RESERVED_7: Unused on all devices up to at least 6393X. +++ * +++ * A 3-bit code is used to relay why a particular frame was sent to +++ * the CPU. We only use this to determine if the packet was mirrored +++ * or trapped, i.e. whether the packet has been forwarded by hardware +++ * or not. +++ * +++ * This is the superset of all possible codes. Any particular device +++ * may only implement a subset. +++ */ +++enum dsa_code { +++ DSA_CODE_MGMT_TRAP = 0, +++ DSA_CODE_FRAME2REG = 1, +++ DSA_CODE_IGMP_MLD_TRAP = 2, +++ DSA_CODE_POLICY_TRAP = 3, +++ DSA_CODE_ARP_MIRROR = 4, +++ DSA_CODE_POLICY_MIRROR = 5, +++ DSA_CODE_RESERVED_6 = 6, +++ DSA_CODE_RESERVED_7 = 7 +++}; +++ +++static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, +++ u8 extra) ++ { ++ struct dsa_port *dp = dsa_slave_to_port(dev); ++ u8 *dsa_header; ++ ++- /* ++- * Convert the outermost 802.1q tag to a DSA tag for tagged ++- * packets, or insert a DSA tag between the addresses and ++- * the ethertype field for untagged packets. ++- */ ++ if (skb->protocol == htons(ETH_P_8021Q)) { ++ if (skb_cow_head(skb, 0) < 0) ++ return NULL; ++@@ -29,8 +135,13 @@ static struct sk_buff *dsa_xmit(struct s ++ /* ++ * Construct tagged FROM_CPU DSA tag from 802.1q tag. ++ */ ++- dsa_header = skb->data + 2 * ETH_ALEN; ++- dsa_header[0] = 0x60 | dp->ds->index; +++ if (extra) { +++ skb_push(skb, extra); +++ memmove(skb->data, skb->data + extra, 2 * ETH_ALEN); +++ } +++ +++ dsa_header = skb->data + 2 * ETH_ALEN + extra; +++ dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | 0x20 | dp->ds->index; ++ dsa_header[1] = dp->index << 3; ++ ++ /* ++@@ -43,15 +154,12 @@ static struct sk_buff *dsa_xmit(struct s ++ } else { ++ if (skb_cow_head(skb, DSA_HLEN) < 0) ++ return NULL; ++- skb_push(skb, DSA_HLEN); +++ skb_push(skb, DSA_HLEN + extra); +++ memmove(skb->data, skb->data + DSA_HLEN + extra, 2 * ETH_ALEN); ++ ++- memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); ++- ++- /* ++- * Construct untagged FROM_CPU DSA tag. ++- */ ++- dsa_header = skb->data + 2 * ETH_ALEN; ++- dsa_header[0] = 0x40 | dp->ds->index; +++ /* Construct untagged FROM_CPU DSA tag. */ +++ dsa_header = skb->data + 2 * ETH_ALEN + extra; +++ dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | dp->ds->index; ++ dsa_header[1] = dp->index << 3; ++ dsa_header[2] = 0x00; ++ dsa_header[3] = 0x00; ++@@ -60,47 +168,91 @@ static struct sk_buff *dsa_xmit(struct s ++ return skb; ++ } ++ ++-static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, ++- struct packet_type *pt) +++static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, +++ u8 extra) ++ { +++ int source_device, source_port; +++ bool trunk = false; +++ enum dsa_code code; +++ enum dsa_cmd cmd; ++ u8 *dsa_header; ++- int source_device; ++- int source_port; ++ ++- if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) ++- return NULL; ++- ++- /* ++- * The ethertype field is part of the DSA header. ++- */ +++ /* The ethertype field is part of the DSA header. */ ++ dsa_header = skb->data - 2; ++ ++- /* ++- * Check that frame type is either TO_CPU or FORWARD. ++- */ ++- if ((dsa_header[0] & 0xc0) != 0x00 && (dsa_header[0] & 0xc0) != 0xc0) +++ cmd = dsa_header[0] >> 6; +++ switch (cmd) { +++ case DSA_CMD_FORWARD: +++ skb->offload_fwd_mark = 1; +++ +++ trunk = !!(dsa_header[1] & 7); +++ break; +++ +++ case DSA_CMD_TO_CPU: +++ code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1); +++ +++ switch (code) { +++ case DSA_CODE_FRAME2REG: +++ /* Remote management is not implemented yet, +++ * drop. +++ */ +++ return NULL; +++ case DSA_CODE_ARP_MIRROR: +++ case DSA_CODE_POLICY_MIRROR: +++ /* Mark mirrored packets to notify any upper +++ * device (like a bridge) that forwarding has +++ * already been done by hardware. +++ */ +++ skb->offload_fwd_mark = 1; +++ break; +++ case DSA_CODE_MGMT_TRAP: +++ case DSA_CODE_IGMP_MLD_TRAP: +++ case DSA_CODE_POLICY_TRAP: +++ /* Traps have, by definition, not been +++ * forwarded by hardware, so don't mark them. +++ */ +++ break; +++ default: +++ /* Reserved code, this could be anything. Drop +++ * seems like the safest option. +++ */ +++ return NULL; +++ } +++ +++ break; +++ +++ default: ++ return NULL; +++ } ++ ++- /* ++- * Determine source device and port. ++- */ ++ source_device = dsa_header[0] & 0x1f; ++ source_port = (dsa_header[1] >> 3) & 0x1f; ++ ++- skb->dev = dsa_master_find_slave(dev, source_device, source_port); +++ if (trunk) { +++ struct dsa_port *cpu_dp = dev->dsa_ptr; +++ +++ /* The exact source port is not available in the tag, +++ * so we inject the frame directly on the upper +++ * team/bond. +++ */ +++ skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); +++ } else { +++ skb->dev = dsa_master_find_slave(dev, source_device, +++ source_port); +++ } +++ ++ if (!skb->dev) ++ return NULL; ++ ++- /* ++- * Convert the DSA header to an 802.1q header if the 'tagged' ++- * bit in the DSA header is set. If the 'tagged' bit is clear, ++- * delete the DSA header entirely. +++ /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q +++ * tag, and delete the ethertype (extra) if applicable. If the +++ * 'tagged' bit is cleared; delete the DSA tag, and ethertype +++ * if applicable. ++ */ ++ if (dsa_header[0] & 0x20) { ++ u8 new_header[4]; ++ ++- /* ++- * Insert 802.1q ethertype and copy the VLAN-related +++ /* Insert 802.1Q ethertype and copy the VLAN-related ++ * fields, but clear the bit that will hold CFI (since ++ * DSA uses that bit location for another purpose). ++ */ ++@@ -109,16 +261,13 @@ static struct sk_buff *dsa_rcv(struct sk ++ new_header[2] = dsa_header[2] & ~0x10; ++ new_header[3] = dsa_header[3]; ++ ++- /* ++- * Move CFI bit from its place in the DSA header to ++- * its 802.1q-designated place. +++ /* Move CFI bit from its place in the DSA header to +++ * its 802.1Q-designated place. ++ */ ++ if (dsa_header[1] & 0x01) ++ new_header[2] |= 0x10; ++ ++- /* ++- * Update packet checksum if skb is CHECKSUM_COMPLETE. ++- */ +++ /* Update packet checksum if skb is CHECKSUM_COMPLETE. */ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) { ++ __wsum c = skb->csum; ++ c = csum_add(c, csum_partial(new_header + 2, 2, 0)); ++@@ -126,19 +275,20 @@ static struct sk_buff *dsa_rcv(struct sk ++ skb->csum = c; ++ } ++ +++ ++ memcpy(dsa_header, new_header, DSA_HLEN); +++ +++ if (extra) +++ memmove(skb->data - ETH_HLEN, +++ skb->data - ETH_HLEN - extra, +++ 2 * ETH_ALEN); ++ } else { ++- /* ++- * Remove DSA tag and update checksum. ++- */ ++ skb_pull_rcsum(skb, DSA_HLEN); ++ memmove(skb->data - ETH_HLEN, ++- skb->data - ETH_HLEN - DSA_HLEN, +++ skb->data - ETH_HLEN - DSA_HLEN - extra, ++ 2 * ETH_ALEN); ++ } ++ ++- skb->offload_fwd_mark = 1; ++- ++ return skb; ++ } ++ ++@@ -150,16 +300,88 @@ static int dsa_tag_flow_dissect(const st ++ return 0; ++ } ++ +++#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) +++ +++static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) +++{ +++ return dsa_xmit_ll(skb, dev, 0); +++} +++ +++static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, +++ struct packet_type *pt) +++{ +++ if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) +++ return NULL; +++ +++ return dsa_rcv_ll(skb, dev, 0); +++} +++ ++ static const struct dsa_device_ops dsa_netdev_ops = { ++- .name = "dsa", ++- .proto = DSA_TAG_PROTO_DSA, ++- .xmit = dsa_xmit, ++- .rcv = dsa_rcv, +++ .name = "dsa", +++ .proto = DSA_TAG_PROTO_DSA, +++ .xmit = dsa_xmit, +++ .rcv = dsa_rcv, ++ .flow_dissect = dsa_tag_flow_dissect, ++ .overhead = DSA_HLEN, ++ }; ++ ++-MODULE_LICENSE("GPL"); +++DSA_TAG_DRIVER(dsa_netdev_ops); ++ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA); ++ ++-module_dsa_tag_driver(dsa_netdev_ops); +++#endif /* CONFIG_NET_DSA_TAG_DSA */ +++ +++#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) +++ +++#define EDSA_HLEN 8 +++ +++static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) +++{ +++ u8 *edsa_header; +++ +++ skb = dsa_xmit_ll(skb, dev, EDSA_HLEN - DSA_HLEN); +++ if (!skb) +++ return NULL; +++ +++ edsa_header = skb->data + 2 * ETH_ALEN; +++ edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; +++ edsa_header[1] = ETH_P_EDSA & 0xff; +++ edsa_header[2] = 0x00; +++ edsa_header[3] = 0x00; +++ return skb; +++} +++ +++static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, +++ struct packet_type *pt) +++{ +++ if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) +++ return NULL; +++ +++ skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); +++ +++ return dsa_rcv_ll(skb, dev, EDSA_HLEN - DSA_HLEN); +++} +++ +++static const struct dsa_device_ops edsa_netdev_ops = { +++ .name = "edsa", +++ .proto = DSA_TAG_PROTO_EDSA, +++ .xmit = edsa_xmit, +++ .rcv = edsa_rcv, +++ .overhead = EDSA_HLEN, +++}; +++ +++DSA_TAG_DRIVER(edsa_netdev_ops); +++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); +++#endif /* CONFIG_NET_DSA_TAG_EDSA */ +++ +++static struct dsa_tag_driver *dsa_tag_drivers[] = { +++#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) +++ &DSA_TAG_DRIVER_NAME(dsa_netdev_ops), +++#endif +++#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) +++ &DSA_TAG_DRIVER_NAME(edsa_netdev_ops), +++#endif +++}; +++ +++module_dsa_tag_drivers(dsa_tag_drivers); +++ +++MODULE_LICENSE("GPL"); ++diff -urpN linux-5.4.137.old/net/dsa/tag_edsa.c linux-5.4.137/net/dsa/tag_edsa.c ++--- linux-5.4.137.old/net/dsa/tag_edsa.c 2021-08-04 14:05:38.059697353 +0700 +++++ linux-5.4.137/net/dsa/tag_edsa.c 1970-01-01 07:00:00.000000000 +0700 ++@@ -1,215 +0,0 @@ ++-// SPDX-License-Identifier: GPL-2.0+ ++-/* ++- * net/dsa/tag_edsa.c - Ethertype DSA tagging ++- * Copyright (c) 2008-2009 Marvell Semiconductor ++- */ ++- ++-#include ++-#include ++-#include ++- ++-#include "dsa_priv.h" ++- ++-#define DSA_HLEN 4 ++-#define EDSA_HLEN 8 ++- ++-#define FRAME_TYPE_TO_CPU 0x00 ++-#define FRAME_TYPE_FORWARD 0x03 ++- ++-#define TO_CPU_CODE_MGMT_TRAP 0x00 ++-#define TO_CPU_CODE_FRAME2REG 0x01 ++-#define TO_CPU_CODE_IGMP_MLD_TRAP 0x02 ++-#define TO_CPU_CODE_POLICY_TRAP 0x03 ++-#define TO_CPU_CODE_ARP_MIRROR 0x04 ++-#define TO_CPU_CODE_POLICY_MIRROR 0x05 ++- ++-static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) ++-{ ++- struct dsa_port *dp = dsa_slave_to_port(dev); ++- u8 *edsa_header; ++- ++- /* ++- * Convert the outermost 802.1q tag to a DSA tag and prepend ++- * a DSA ethertype field is the packet is tagged, or insert ++- * a DSA ethertype plus DSA tag between the addresses and the ++- * current ethertype field if the packet is untagged. ++- */ ++- if (skb->protocol == htons(ETH_P_8021Q)) { ++- if (skb_cow_head(skb, DSA_HLEN) < 0) ++- return NULL; ++- skb_push(skb, DSA_HLEN); ++- ++- memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN); ++- ++- /* ++- * Construct tagged FROM_CPU DSA tag from 802.1q tag. ++- */ ++- edsa_header = skb->data + 2 * ETH_ALEN; ++- edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; ++- edsa_header[1] = ETH_P_EDSA & 0xff; ++- edsa_header[2] = 0x00; ++- edsa_header[3] = 0x00; ++- edsa_header[4] = 0x60 | dp->ds->index; ++- edsa_header[5] = dp->index << 3; ++- ++- /* ++- * Move CFI field from byte 6 to byte 5. ++- */ ++- if (edsa_header[6] & 0x10) { ++- edsa_header[5] |= 0x01; ++- edsa_header[6] &= ~0x10; ++- } ++- } else { ++- if (skb_cow_head(skb, EDSA_HLEN) < 0) ++- return NULL; ++- skb_push(skb, EDSA_HLEN); ++- ++- memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN); ++- ++- /* ++- * Construct untagged FROM_CPU DSA tag. ++- */ ++- edsa_header = skb->data + 2 * ETH_ALEN; ++- edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; ++- edsa_header[1] = ETH_P_EDSA & 0xff; ++- edsa_header[2] = 0x00; ++- edsa_header[3] = 0x00; ++- edsa_header[4] = 0x40 | dp->ds->index; ++- edsa_header[5] = dp->index << 3; ++- edsa_header[6] = 0x00; ++- edsa_header[7] = 0x00; ++- } ++- ++- return skb; ++-} ++- ++-static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, ++- struct packet_type *pt) ++-{ ++- u8 *edsa_header; ++- int frame_type; ++- int code; ++- int source_device; ++- int source_port; ++- ++- if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) ++- return NULL; ++- ++- /* ++- * Skip the two null bytes after the ethertype. ++- */ ++- edsa_header = skb->data + 2; ++- ++- /* ++- * Check that frame type is either TO_CPU or FORWARD. ++- */ ++- frame_type = edsa_header[0] >> 6; ++- ++- switch (frame_type) { ++- case FRAME_TYPE_TO_CPU: ++- code = (edsa_header[1] & 0x6) | ((edsa_header[2] >> 4) & 1); ++- ++- /* ++- * Mark the frame to never egress on any port of the same switch ++- * unless it's a trapped IGMP/MLD packet, in which case the ++- * bridge might want to forward it. ++- */ ++- if (code != TO_CPU_CODE_IGMP_MLD_TRAP) ++- skb->offload_fwd_mark = 1; ++- ++- break; ++- ++- case FRAME_TYPE_FORWARD: ++- skb->offload_fwd_mark = 1; ++- break; ++- ++- default: ++- return NULL; ++- } ++- ++- /* ++- * Determine source device and port. ++- */ ++- source_device = edsa_header[0] & 0x1f; ++- source_port = (edsa_header[1] >> 3) & 0x1f; ++- ++- skb->dev = dsa_master_find_slave(dev, source_device, source_port); ++- if (!skb->dev) ++- return NULL; ++- ++- /* ++- * If the 'tagged' bit is set, convert the DSA tag to a 802.1q ++- * tag and delete the ethertype part. If the 'tagged' bit is ++- * clear, delete the ethertype and the DSA tag parts. ++- */ ++- if (edsa_header[0] & 0x20) { ++- u8 new_header[4]; ++- ++- /* ++- * Insert 802.1q ethertype and copy the VLAN-related ++- * fields, but clear the bit that will hold CFI (since ++- * DSA uses that bit location for another purpose). ++- */ ++- new_header[0] = (ETH_P_8021Q >> 8) & 0xff; ++- new_header[1] = ETH_P_8021Q & 0xff; ++- new_header[2] = edsa_header[2] & ~0x10; ++- new_header[3] = edsa_header[3]; ++- ++- /* ++- * Move CFI bit from its place in the DSA header to ++- * its 802.1q-designated place. ++- */ ++- if (edsa_header[1] & 0x01) ++- new_header[2] |= 0x10; ++- ++- skb_pull_rcsum(skb, DSA_HLEN); ++- ++- /* ++- * Update packet checksum if skb is CHECKSUM_COMPLETE. ++- */ ++- if (skb->ip_summed == CHECKSUM_COMPLETE) { ++- __wsum c = skb->csum; ++- c = csum_add(c, csum_partial(new_header + 2, 2, 0)); ++- c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0)); ++- skb->csum = c; ++- } ++- ++- memcpy(edsa_header, new_header, DSA_HLEN); ++- ++- memmove(skb->data - ETH_HLEN, ++- skb->data - ETH_HLEN - DSA_HLEN, ++- 2 * ETH_ALEN); ++- } else { ++- /* ++- * Remove DSA tag and update checksum. ++- */ ++- skb_pull_rcsum(skb, EDSA_HLEN); ++- memmove(skb->data - ETH_HLEN, ++- skb->data - ETH_HLEN - EDSA_HLEN, ++- 2 * ETH_ALEN); ++- } ++- ++- return skb; ++-} ++- ++-static int edsa_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto, ++- int *offset) ++-{ ++- *offset = 8; ++- *proto = ((__be16 *)skb->data)[3]; ++- return 0; ++-} ++- ++-static const struct dsa_device_ops edsa_netdev_ops = { ++- .name = "edsa", ++- .proto = DSA_TAG_PROTO_EDSA, ++- .xmit = edsa_xmit, ++- .rcv = edsa_rcv, ++- .flow_dissect = edsa_tag_flow_dissect, ++- .overhead = EDSA_HLEN, ++-}; ++- ++-MODULE_LICENSE("GPL"); ++-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); ++- ++-module_dsa_tag_driver(edsa_netdev_ops); +diff --git a/target/linux/realtek/patches-5.4/871-brflood-api.patch b/target/linux/realtek/patches-5.4/871-brflood-api.patch +new file mode 100644 +index 0000000000..0a7f1986ea +--- /dev/null ++++ b/target/linux/realtek/patches-5.4/871-brflood-api.patch +@@ -0,0 +1,817 @@ ++Index: linux-5.4.111/drivers/net/dsa/b53/b53_common.c ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/b53/b53_common.c +++++ linux-5.4.111/drivers/net/dsa/b53/b53_common.c ++@@ -527,6 +527,39 @@ static void b53_port_set_learning(struct ++ b53_write16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, reg); ++ } ++ +++static void b53_port_set_ucast_flood(struct b53_device *dev, int port, +++ bool unicast) +++{ +++ u16 uc; +++ +++ b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); +++ if (unicast) +++ uc |= BIT(port); +++ else +++ uc &= ~BIT(port); +++ b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); +++} +++ +++static void b53_port_set_mcast_flood(struct b53_device *dev, int port, +++ bool multicast) +++{ +++ u16 mc; +++ +++ b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); +++ if (multicast) +++ mc |= BIT(port); +++ else +++ mc &= ~BIT(port); +++ b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); +++ +++ b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); +++ if (multicast) +++ mc |= BIT(port); +++ else +++ mc &= ~BIT(port); +++ b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); +++} +++ ++ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) ++ { ++ struct b53_device *dev = ds->priv; ++@@ -539,7 +572,8 @@ int b53_enable_port(struct dsa_switch *d ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++- b53_br_egress_floods(ds, port, true, true); +++ b53_port_set_ucast_flood(dev, port, true); +++ b53_port_set_mcast_flood(dev, port, true); ++ b53_port_set_learning(dev, port, false); ++ ++ if (dev->ops->irq_enable) ++@@ -658,7 +692,8 @@ static void b53_enable_cpu_port(struct b ++ ++ b53_brcm_hdr_setup(dev->ds, port); ++ ++- b53_br_egress_floods(dev->ds, port, true, true); +++ b53_port_set_ucast_flood(dev, port, true); +++ b53_port_set_mcast_flood(dev, port, true); ++ b53_port_set_learning(dev, port, false); ++ } ++ ++@@ -1808,37 +1843,37 @@ void b53_br_fast_age(struct dsa_switch * ++ } ++ EXPORT_SYMBOL(b53_br_fast_age); ++ ++-int b53_br_egress_floods(struct dsa_switch *ds, int port, ++- bool unicast, bool multicast) +++static int b53_br_flags_pre(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack) ++ { ++- struct b53_device *dev = ds->priv; ++- u16 uc, mc; ++- ++- b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); ++- if (unicast) ++- uc |= BIT(port); ++- else ++- uc &= ~BIT(port); ++- b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); +++ if (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)) +++ return -EINVAL; ++ ++- b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); ++- if (multicast) ++- mc |= BIT(port); ++- else ++- mc &= ~BIT(port); ++- b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); +++ return 0; +++} ++ ++- b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); ++- if (multicast) ++- mc |= BIT(port); ++- else ++- mc &= ~BIT(port); ++- b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); +++static int b53_br_flags(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack) +++{ +++ if (flags & BR_FLOOD) +++ b53_port_set_ucast_flood(ds->priv, port, +++ !!(flags.val & BR_FLOOD)); +++ if (flags & BR_MCAST_FLOOD) +++ b53_port_set_mcast_flood(ds->priv, port, +++ !!(flags.val & BR_MCAST_FLOOD)); ++ ++ return 0; +++} ++ +++static int b53_set_mrouter(struct dsa_switch *ds, int port, bool mrouter, +++ struct netlink_ext_ack *extack) +++{ +++ b53_port_set_mcast_flood(ds->priv, port, mrouter); +++ +++ return 0; ++ } ++-EXPORT_SYMBOL(b53_br_egress_floods); ++ ++ static bool b53_possible_cpu_port(struct dsa_switch *ds, int port) ++ { ++@@ -2037,9 +2072,11 @@ static const struct dsa_switch_ops b53_s ++ .set_mac_eee = b53_set_mac_eee, ++ .port_bridge_join = b53_br_join, ++ .port_bridge_leave = b53_br_leave, +++ .port_pre_bridge_flags = b53_br_flags_pre, +++ .port_bridge_flags = b53_br_flags, +++ .port_set_mrouter = b53_set_mrouter, ++ .port_stp_state_set = b53_br_set_stp_state, ++ .port_fast_age = b53_br_fast_age, ++- .port_egress_floods = b53_br_egress_floods, ++ .port_vlan_filtering = b53_vlan_filtering, ++ .port_vlan_prepare = b53_vlan_prepare, ++ .port_vlan_add = b53_vlan_add, ++Index: linux-5.4.111/drivers/net/dsa/b53/b53_priv.h ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/b53/b53_priv.h +++++ linux-5.4.111/drivers/net/dsa/b53/b53_priv.h ++@@ -319,8 +319,6 @@ int b53_br_join(struct dsa_switch *ds, i ++ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge); ++ void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state); ++ void b53_br_fast_age(struct dsa_switch *ds, int port); ++-int b53_br_egress_floods(struct dsa_switch *ds, int port, ++- bool unicast, bool multicast); ++ void b53_port_event(struct dsa_switch *ds, int port); ++ void b53_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.c ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/chip.c +++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.c ++@@ -2274,12 +2274,20 @@ static int mv88e6xxx_setup_egress_floods ++ { ++ struct dsa_switch *ds = chip->ds; ++ bool flood; +++ int err; ++ ++ /* Upstream ports flood frames with unknown unicast or multicast DA */ ++ flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port); ++- if (chip->info->ops->port_set_egress_floods) ++- return chip->info->ops->port_set_egress_floods(chip, port, ++- flood, flood); +++ if (chip->info->ops->port_set_ucast_flood) { +++ err = chip->info->ops->port_set_ucast_flood(chip, port, flood); +++ if (err) +++ return err; +++ } +++ if (chip->info->ops->port_set_mcast_flood) { +++ err = chip->info->ops->port_set_mcast_flood(chip, port, flood); +++ if (err) +++ return err; +++ } ++ ++ return 0; ++ } ++@@ -3019,7 +3027,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++ .port_pause_limit = mv88e6097_port_pause_limit, ++@@ -3058,7 +3067,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_duplex = mv88e6xxx_port_set_duplex, ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_set_frame_mode = mv88e6085_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6185_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, +++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, ++ .port_set_upstream_port = mv88e6095_port_set_upstream_port, ++ .port_link_state = mv88e6185_port_link_state, ++ .port_get_cmode = mv88e6185_port_get_cmode, ++@@ -3090,7 +3100,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, ++ .port_pause_limit = mv88e6097_port_pause_limit, ++@@ -3128,7 +3139,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_duplex = mv88e6xxx_port_set_duplex, ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_set_frame_mode = mv88e6085_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, ++ .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, ++ .port_link_state = mv88e6352_port_link_state, ++@@ -3162,7 +3174,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6185_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, +++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_upstream_port = mv88e6095_port_set_upstream_port, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++@@ -3207,7 +3220,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_max_speed_mode = mv88e6341_port_max_speed_mode, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3253,7 +3267,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3329,7 +3344,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3372,7 +3388,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3416,7 +3433,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3459,7 +3477,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3503,7 +3522,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_duplex = mv88e6xxx_port_set_duplex, ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_set_frame_mode = mv88e6085_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6185_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6185_port_set_forward_unknown, +++ .port_set_mcast_flood = mv88e6185_port_set_default_forward, ++ .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, ++ .port_set_upstream_port = mv88e6095_port_set_upstream_port, ++ .port_set_pause = mv88e6185_port_set_pause, ++@@ -3545,7 +3565,6 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_pause_limit = mv88e6390_port_pause_limit, ++ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, ++@@ -3594,7 +3613,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_pause_limit = mv88e6390_port_pause_limit, ++ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, ++@@ -3642,7 +3662,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_max_speed_mode = mv88e6390_port_max_speed_mode, ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_pause_limit = mv88e6390_port_pause_limit, ++ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, ++@@ -3692,7 +3713,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3743,7 +3765,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6250_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++ .port_pause_limit = mv88e6097_port_pause_limit, ++@@ -3784,7 +3807,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_pause_limit = mv88e6390_port_pause_limit, ++ .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, ++@@ -3833,7 +3857,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3877,7 +3902,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3921,7 +3947,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_max_speed_mode = mv88e6341_port_max_speed_mode, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -3970,7 +3997,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_set_speed = mv88e6185_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -4055,7 +4083,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -4110,7 +4139,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -4163,7 +4193,8 @@ static const struct mv88e6xxx_ops mv88e6 ++ .port_tag_remap = mv88e6390_port_tag_remap, ++ .port_set_policy = mv88e6352_port_set_policy, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++- .port_set_egress_floods = mv88e6352_port_set_egress_floods, +++ .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, +++ .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++@@ -5016,17 +5047,72 @@ static void mv88e6xxx_port_mirror_del(st ++ mutex_unlock(&chip->reg_lock); ++ } ++ ++-static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port, ++- bool unicast, bool multicast) +++static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ const struct mv88e6xxx_ops *ops; +++ +++ if (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)) +++ return -EINVAL; +++ +++ ops = chip->info->ops; +++ +++ if ((flags & BR_FLOOD) && !ops->port_set_ucast_flood) +++ return -EINVAL; +++ +++ if ((flags & BR_MCAST_FLOOD) && !ops->port_set_mcast_flood) +++ return -EINVAL; +++ +++ return 0; +++} +++ +++static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack) ++ { ++ struct mv88e6xxx_chip *chip = ds->priv; ++ int err = -EOPNOTSUPP; ++ ++ mv88e6xxx_reg_lock(chip); ++- if (chip->info->ops->port_set_egress_floods) ++- err = chip->info->ops->port_set_egress_floods(chip, port, ++- unicast, ++- multicast); +++ +++ if (flags & BR_FLOOD) { +++ bool unicast = !!(flags.val & BR_FLOOD); +++ +++ err = chip->info->ops->port_set_ucast_flood(chip, port, +++ unicast); +++ if (err) +++ goto out; +++ } +++ +++ if (flags & BR_MCAST_FLOOD) { +++ bool multicast = !!(flags.val & BR_MCAST_FLOOD); +++ +++ err = chip->info->ops->port_set_mcast_flood(chip, port, +++ multicast); +++ if (err) +++ goto out; +++ } +++ +++out: +++ mv88e6xxx_reg_unlock(chip); +++ +++ return err; +++} +++ +++static int mv88e6xxx_port_set_mrouter(struct dsa_switch *ds, int port, +++ bool mrouter, +++ struct netlink_ext_ack *extack) +++{ +++ struct mv88e6xxx_chip *chip = ds->priv; +++ int err; +++ +++ if (!chip->info->ops->port_set_mcast_flood) +++ return -EOPNOTSUPP; +++ +++ mv88e6xxx_reg_lock(chip); +++ err = chip->info->ops->port_set_mcast_flood(chip, port, mrouter); ++ mv88e6xxx_reg_unlock(chip); ++ ++ return err; ++@@ -5322,7 +5408,9 @@ static const struct dsa_switch_ops mv88e ++ .set_ageing_time = mv88e6xxx_set_ageing_time, ++ .port_bridge_join = mv88e6xxx_port_bridge_join, ++ .port_bridge_leave = mv88e6xxx_port_bridge_leave, ++- .port_egress_floods = mv88e6xxx_port_egress_floods, +++ .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags, +++ .port_bridge_flags = mv88e6xxx_port_bridge_flags, +++ .port_set_mrouter = mv88e6xxx_port_set_mrouter, ++ .port_stp_state_set = mv88e6xxx_port_stp_state_set, ++ .port_fast_age = mv88e6xxx_port_fast_age, ++ .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, ++Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.h ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/chip.h +++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/chip.h ++@@ -429,8 +429,10 @@ struct mv88e6xxx_ops { ++ ++ int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, ++ enum mv88e6xxx_frame_mode mode); ++- int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port, ++- bool unicast, bool multicast); +++ int (*port_set_ucast_flood)(struct mv88e6xxx_chip *chip, int port, +++ bool unicast); +++ int (*port_set_mcast_flood)(struct mv88e6xxx_chip *chip, int port, +++ bool multicast); ++ int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, ++ u16 etype); ++ int (*port_set_jumbo_size)(struct mv88e6xxx_chip *chip, int port, ++Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.c ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/port.c +++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.c ++@@ -932,8 +932,8 @@ int mv88e6351_port_set_frame_mode(struct ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); ++ } ++ ++-static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, ++- int port, bool unicast) +++int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, +++ int port, bool unicast) ++ { ++ int err; ++ u16 reg; ++@@ -950,8 +950,8 @@ static int mv88e6185_port_set_forward_un ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); ++ } ++ ++-int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, ++- bool unicast, bool multicast) +++int mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port, +++ bool unicast) ++ { ++ int err; ++ u16 reg; ++@@ -960,16 +960,28 @@ int mv88e6352_port_set_egress_floods(str ++ if (err) ++ return err; ++ ++- reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK; +++ if (unicast) +++ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC; +++ else +++ reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC; +++ +++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); +++} +++ +++int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port, +++ bool multicast) +++{ +++ int err; +++ u16 reg; +++ +++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®); +++ if (err) +++ return err; ++ ++- if (unicast && multicast) ++- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA; ++- else if (unicast) ++- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA; ++- else if (multicast) ++- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA; +++ if (multicast) +++ reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC; ++ else ++- reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA; +++ reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC; ++ ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); ++ } ++@@ -1156,8 +1168,8 @@ static const char * const mv88e6xxx_port ++ [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure", ++ }; ++ ++-static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, ++- int port, bool multicast) +++int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, +++ int port, bool multicast) ++ { ++ int err; ++ u16 reg; ++@@ -1174,18 +1186,6 @@ static int mv88e6185_port_set_default_fo ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); ++ } ++ ++-int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, ++- bool unicast, bool multicast) ++-{ ++- int err; ++- ++- err = mv88e6185_port_set_forward_unknown(chip, port, unicast); ++- if (err) ++- return err; ++- ++- return mv88e6185_port_set_default_forward(chip, port, multicast); ++-} ++- ++ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, ++ int upstream_port) ++ { ++Index: linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.h ++=================================================================== ++--- linux-5.4.111.orig/drivers/net/dsa/mv88e6xxx/port.h +++++ linux-5.4.111/drivers/net/dsa/mv88e6xxx/port.h ++@@ -154,11 +154,8 @@ ++ #define MV88E6185_PORT_CTL0_USE_IP 0x0020 ++ #define MV88E6185_PORT_CTL0_USE_TAG 0x0010 ++ #define MV88E6185_PORT_CTL0_FORWARD_UNKNOWN 0x0004 ++-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK 0x000c ++-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA 0x0000 ++-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA 0x0004 ++-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA 0x0008 ++-#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA 0x000c +++#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC 0x0004 +++#define MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC 0x0008 ++ #define MV88E6XXX_PORT_CTL0_STATE_MASK 0x0003 ++ #define MV88E6XXX_PORT_CTL0_STATE_DISABLED 0x0000 ++ #define MV88E6XXX_PORT_CTL0_STATE_BLOCKING 0x0001 ++@@ -335,10 +332,14 @@ int mv88e6085_port_set_frame_mode(struct ++ enum mv88e6xxx_frame_mode mode); ++ int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, ++ enum mv88e6xxx_frame_mode mode); ++-int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, ++- bool unicast, bool multicast); ++-int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, ++- bool unicast, bool multicast); +++int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, +++ int port, bool unicast); +++int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, +++ int port, bool multicast); +++int mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port, +++ bool unicast); +++int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port, +++ bool multicast); ++ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, ++ enum mv88e6xxx_policy_mapping mapping, ++ enum mv88e6xxx_policy_action action); ++Index: linux-5.4.111/include/net/dsa.h ++=================================================================== ++--- linux-5.4.111.orig/include/net/dsa.h +++++ linux-5.4.111/include/net/dsa.h ++@@ -547,8 +547,14 @@ struct dsa_switch_ops { ++ void (*port_stp_state_set)(struct dsa_switch *ds, int port, ++ u8 state); ++ void (*port_fast_age)(struct dsa_switch *ds, int port); ++- int (*port_egress_floods)(struct dsa_switch *ds, int port, ++- bool unicast, bool multicast); +++ int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack); +++ int (*port_bridge_flags)(struct dsa_switch *ds, int port, +++ unsigned long flags, +++ struct netlink_ext_ack *extack); +++ int (*port_set_mrouter)(struct dsa_switch *ds, int port, bool mrouter, +++ struct netlink_ext_ack *extack); ++ ++ /* ++ * VLAN support ++Index: linux-5.4.111/net/dsa/dsa_priv.h ++=================================================================== ++--- linux-5.4.111.orig/net/dsa/dsa_priv.h +++++ linux-5.4.111/net/dsa/dsa_priv.h ++@@ -171,11 +171,11 @@ int dsa_port_mdb_add(const struct dsa_po ++ int dsa_port_mdb_del(const struct dsa_port *dp, ++ const struct switchdev_obj_port_mdb *mdb); ++ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, ++- struct switchdev_trans *trans); +++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); ++ int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, ++- struct switchdev_trans *trans); +++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); ++ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, ++- struct switchdev_trans *trans); +++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); ++ int dsa_port_vlan_add(struct dsa_port *dp, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct switchdev_trans *trans); ++Index: linux-5.4.111/net/dsa/port.c ++=================================================================== ++--- linux-5.4.111.orig/net/dsa/port.c +++++ linux-5.4.111/net/dsa/port.c ++@@ -127,7 +127,7 @@ int dsa_port_bridge_join(struct dsa_port ++ int err; ++ ++ /* Set the flooding mode before joining the port in the switch */ ++- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); +++ err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); ++ if (err) ++ return err; ++ ++@@ -140,7 +140,7 @@ int dsa_port_bridge_join(struct dsa_port ++ ++ /* The bridging is rolled back on error */ ++ if (err) { ++- dsa_port_bridge_flags(dp, 0, NULL); +++ dsa_port_bridge_flags(dp, 0, NULL, NULL); ++ dp->bridge_dev = NULL; ++ } ++ ++@@ -166,7 +166,7 @@ void dsa_port_bridge_leave(struct dsa_po ++ pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); ++ ++ /* Port is leaving the bridge, disable flooding */ ++- dsa_port_bridge_flags(dp, 0, NULL); +++ dsa_port_bridge_flags(dp, 0, NULL, NULL); ++ ++ /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, ++ * so allow it to be in BR_STATE_FORWARDING to be kept functional ++@@ -350,44 +350,44 @@ int dsa_port_ageing_time(struct dsa_port ++ } ++ ++ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, ++- struct switchdev_trans *trans) +++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) ++ { ++ struct dsa_switch *ds = dp->ds; ++ ++- if (!ds->ops->port_egress_floods || ++- (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) +++ if (!ds->ops->port_pre_bridge_flags) ++ return -EINVAL; ++ ++- return 0; +++ return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); ++ } ++ ++ int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, ++- struct switchdev_trans *trans) +++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) ++ { ++ struct dsa_switch *ds = dp->ds; ++- int port = dp->index; ++- int err = 0; ++ ++ if (switchdev_trans_ph_prepare(trans)) ++ return 0; ++ ++- if (ds->ops->port_egress_floods) ++- err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, ++- flags & BR_MCAST_FLOOD); +++ if (!ds->ops->port_bridge_flags) +++ return -EINVAL; +++ +++ return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); ++ ++- return err; ++ } ++ ++ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, ++- struct switchdev_trans *trans) +++ struct switchdev_trans *trans, +++ struct netlink_ext_ack *extack) ++ { ++ struct dsa_switch *ds = dp->ds; ++- int port = dp->index; ++ ++ if (switchdev_trans_ph_prepare(trans)) ++- return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; +++ return ds->ops->port_set_mrouter ? 0 : -EOPNOTSUPP; +++ +++ if (!ds->ops->port_set_mrouter) +++ return -EOPNOTSUPP; ++ ++- return ds->ops->port_egress_floods(ds, port, true, mrouter); +++ return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack); ++ } ++ ++ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, ++Index: linux-5.4.111/net/dsa/slave.c ++=================================================================== ++--- linux-5.4.111.orig/net/dsa/slave.c +++++ linux-5.4.111/net/dsa/slave.c ++@@ -293,13 +293,13 @@ static int dsa_slave_port_attr_set(struc ++ break; ++ case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: ++ ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, ++- trans); +++ trans, NULL); ++ break; ++ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: ++- ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans); +++ ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans, NULL); ++ break; ++ case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: ++- ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans); +++ ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans, NULL); ++ break; ++ default: ++ ret = -EOPNOTSUPP; -- 2.25.1