]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: dsa: replay master state events in dsa_tree_{setup,teardown}_master
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 2 Feb 2022 00:03:21 +0000 (01:03 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Feb 2022 14:43:59 +0000 (14:43 +0000)
In order for switch driver to be able to make simple and reliable use of
the master tracking operations, they must also be notified of the
initial state of the DSA master, not just of the changes. This is
because they might enable certain features only during the time when
they know that the DSA master is up and running.

Therefore, this change explicitly checks the state of the DSA master
under the same rtnl_mutex as we were holding during the
dsa_master_setup() and dsa_master_teardown() call. The idea being that
if the DSA master became operational in between the moment in which it
became a DSA master (dsa_master_setup set dev->dsa_ptr) and the moment
when we checked for the master being up, there is a chance that we
would emit a ->master_state_change() call with no actual state change.
We need to avoid that by serializing the concurrent netdevice event with
us. If the netdevice event started before, we force it to finish before
we begin, because we take rtnl_lock before making netdev_uses_dsa()
return true. So we also handle that early event and do nothing on it.
Similarly, if the dev_open() attempt is concurrent with us, it will
attempt to take the rtnl_mutex, but we're holding it. We'll see that
the master flag IFF_UP isn't set, then when we release the rtnl_mutex
we'll process the NETDEV_UP notifier.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/dsa/dsa2.c

index ff998c0ede027a06f9ae9ae2ce3cd8753f97675c..909b045c9b118c27adb5f29b1e4ee2f1e39edb05 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/of_net.h>
 #include <net/devlink.h>
+#include <net/sch_generic.h>
 
 #include "dsa_priv.h"
 
@@ -1064,9 +1065,18 @@ static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
 
        list_for_each_entry(dp, &dst->ports, list) {
                if (dsa_port_is_cpu(dp)) {
-                       err = dsa_master_setup(dp->master, dp);
+                       struct net_device *master = dp->master;
+                       bool admin_up = (master->flags & IFF_UP) &&
+                                       !qdisc_tx_is_noop(master);
+
+                       err = dsa_master_setup(master, dp);
                        if (err)
                                return err;
+
+                       /* Replay master state event */
+                       dsa_tree_master_admin_state_change(dst, master, admin_up);
+                       dsa_tree_master_oper_state_change(dst, master,
+                                                         netif_oper_up(master));
                }
        }
 
@@ -1081,9 +1091,19 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
 
        rtnl_lock();
 
-       list_for_each_entry(dp, &dst->ports, list)
-               if (dsa_port_is_cpu(dp))
-                       dsa_master_teardown(dp->master);
+       list_for_each_entry(dp, &dst->ports, list) {
+               if (dsa_port_is_cpu(dp)) {
+                       struct net_device *master = dp->master;
+
+                       /* Synthesizing an "admin down" state is sufficient for
+                        * the switches to get a notification if the master is
+                        * currently up and running.
+                        */
+                       dsa_tree_master_admin_state_change(dst, master, false);
+
+                       dsa_master_teardown(master);
+               }
+       }
 
        rtnl_unlock();
 }