]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
net: dsa: targeted MTU notifiers should only match on one port
authorVladimir Oltean <vladimir.oltean@nxp.com>
Mon, 21 Jun 2021 16:42:18 +0000 (19:42 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 21 Jun 2021 19:50:20 +0000 (12:50 -0700)
dsa_slave_change_mtu() calls dsa_port_mtu_change() twice:
- it sends a cross-chip notifier with the MTU of the CPU port which is
  used to update the DSA links.
- it sends one targeted MTU notifier which is supposed to only match the
  user port on which we are changing the MTU. The "propagate_upstream"
  variable is used here to bypass the cross-chip notifier system from
  switch.c

But due to a mistake, the second, targeted notifier matches not only on
the user port, but also on the DSA link which is a member of the same
switch, if that exists.

And because the DSA links of the entire dst were programmed in a
previous round to the largest_mtu via a "propagate_upstream == true"
notification, then the dsa_port_mtu_change(propagate_upstream == false)
call that is immediately upcoming will break the MTU on the one DSA link
which is chip-wise local to the dp whose MTU is changing right now.

Example given this daisy chain topology:

   sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
[  cpu  ] [  user ] [  user ] [  dsa  ] [  user ]
[   x   ] [       ] [       ] [   x   ] [       ]
                                  |
                                  +---------+
                                            |
   sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
[  user ] [  user ] [  user ] [  dsa  ] [  dsa  ]
[       ] [       ] [       ] [       ] [   x   ]

ip link set sw0p1 mtu 9000
ip link set sw1p1 mtu 9000 # at this stage, sw0p1 and sw1p1 can talk
                           # to one another using jumbo frames
ip link set sw0p2 mtu 1500 # this programs the sw0p3 DSA link first to
                           # the largest_mtu of 9000, then reprograms it to
                           # 1500 with the "propagate_upstream == false"
                           # notifier, breaking communication between
                           # sw0p1 and sw1p1

To escape from this situation, make the targeted match really match on a
single port - the user port, and rename the "propagate_upstream"
variable to "targeted_match" to clarify the intention and avoid future
issues.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/dsa/dsa_priv.h
net/dsa/port.c
net/dsa/slave.c
net/dsa/switch.c

index b8b17474b72b18173afc34a3769f382251a08c3b..b0811253d10188028a8ac77012899b1133a8f34f 100644 (file)
@@ -84,7 +84,7 @@ struct dsa_notifier_vlan_info {
 
 /* DSA_NOTIFIER_MTU */
 struct dsa_notifier_mtu_info {
-       bool propagate_upstream;
+       bool targeted_match;
        int sw_index;
        int port;
        int mtu;
@@ -200,7 +200,7 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
 bool dsa_port_skip_vlan_configuration(struct dsa_port *dp);
 int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock);
 int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
-                       bool propagate_upstream);
+                       bool targeted_match);
 int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                     u16 vid);
 int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
index 6379d66a6bb3258846233c11bd7c60446828b870..5c93f1e1a03d62f25014edb086ba9a183e7c83c3 100644 (file)
@@ -567,11 +567,11 @@ int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
 }
 
 int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
-                       bool propagate_upstream)
+                       bool targeted_match)
 {
        struct dsa_notifier_mtu_info info = {
                .sw_index = dp->ds->index,
-               .propagate_upstream = propagate_upstream,
+               .targeted_match = targeted_match,
                .port = dp->index,
                .mtu = new_mtu,
        };
index ac2ca5f75af35a060002225f7e71cda34cbcfd0d..5e668e529575d07542c640e79a87867ff6e5c402 100644 (file)
@@ -1586,14 +1586,15 @@ int dsa_slave_change_mtu(struct net_device *dev, int new_mtu)
                        goto out_master_failed;
 
                /* We only need to propagate the MTU of the CPU port to
-                * upstream switches.
+                * upstream switches, so create a non-targeted notifier which
+                * updates all switches.
                 */
-               err = dsa_port_mtu_change(cpu_dp, cpu_mtu, true);
+               err = dsa_port_mtu_change(cpu_dp, cpu_mtu, false);
                if (err)
                        goto out_cpu_failed;
        }
 
-       err = dsa_port_mtu_change(dp, new_mtu, false);
+       err = dsa_port_mtu_change(dp, new_mtu, true);
        if (err)
                goto out_port_failed;
 
@@ -1607,7 +1608,7 @@ out_port_failed:
        if (new_master_mtu != old_master_mtu)
                dsa_port_mtu_change(cpu_dp, old_master_mtu -
                                    dsa_tag_protocol_overhead(cpu_dp->tag_ops),
-                                   true);
+                                   false);
 out_cpu_failed:
        if (new_master_mtu != old_master_mtu)
                dev_set_mtu(master, old_master_mtu);
index 8b601ced6b45fb56bd157772ebfa314467c48450..75f567390a6b60e1717586d4174d5556a2164d61 100644 (file)
@@ -52,10 +52,13 @@ static int dsa_switch_ageing_time(struct dsa_switch *ds,
 static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
                                 struct dsa_notifier_mtu_info *info)
 {
-       if (ds->index == info->sw_index)
-               return (port == info->port) || dsa_is_dsa_port(ds, port);
+       if (ds->index == info->sw_index && port == info->port)
+               return true;
 
-       if (!info->propagate_upstream)
+       /* Do not propagate to other switches in the tree if the notifier was
+        * targeted for a single switch.
+        */
+       if (info->targeted_match)
                return false;
 
        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))