6478d580c01701053fef96788e658d4ee726cba8
[librecmc/librecmc.git] /
1 From e83d56537859849f2223b90749e554831b1f3c27 Mon Sep 17 00:00:00 2001
2 From: Vladimir Oltean <vladimir.oltean@nxp.com>
3 Date: Wed, 2 Feb 2022 01:03:21 +0100
4 Subject: [PATCH 02/16] net: dsa: replay master state events in
5  dsa_tree_{setup,teardown}_master
6
7 In order for switch driver to be able to make simple and reliable use of
8 the master tracking operations, they must also be notified of the
9 initial state of the DSA master, not just of the changes. This is
10 because they might enable certain features only during the time when
11 they know that the DSA master is up and running.
12
13 Therefore, this change explicitly checks the state of the DSA master
14 under the same rtnl_mutex as we were holding during the
15 dsa_master_setup() and dsa_master_teardown() call. The idea being that
16 if the DSA master became operational in between the moment in which it
17 became a DSA master (dsa_master_setup set dev->dsa_ptr) and the moment
18 when we checked for the master being up, there is a chance that we
19 would emit a ->master_state_change() call with no actual state change.
20 We need to avoid that by serializing the concurrent netdevice event with
21 us. If the netdevice event started before, we force it to finish before
22 we begin, because we take rtnl_lock before making netdev_uses_dsa()
23 return true. So we also handle that early event and do nothing on it.
24 Similarly, if the dev_open() attempt is concurrent with us, it will
25 attempt to take the rtnl_mutex, but we're holding it. We'll see that
26 the master flag IFF_UP isn't set, then when we release the rtnl_mutex
27 we'll process the NETDEV_UP notifier.
28
29 Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
30 Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
31 Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
32 Signed-off-by: David S. Miller <davem@davemloft.net>
33 ---
34  net/dsa/dsa2.c | 28 ++++++++++++++++++++++++----
35  1 file changed, 24 insertions(+), 4 deletions(-)
36
37 --- a/net/dsa/dsa2.c
38 +++ b/net/dsa/dsa2.c
39 @@ -15,6 +15,7 @@
40  #include <linux/of.h>
41  #include <linux/of_net.h>
42  #include <net/devlink.h>
43 +#include <net/sch_generic.h>
44  
45  #include "dsa_priv.h"
46  
47 @@ -1060,9 +1061,18 @@ static int dsa_tree_setup_master(struct
48  
49         list_for_each_entry(dp, &dst->ports, list) {
50                 if (dsa_port_is_cpu(dp)) {
51 -                       err = dsa_master_setup(dp->master, dp);
52 +                       struct net_device *master = dp->master;
53 +                       bool admin_up = (master->flags & IFF_UP) &&
54 +                                       !qdisc_tx_is_noop(master);
55 +
56 +                       err = dsa_master_setup(master, dp);
57                         if (err)
58                                 return err;
59 +
60 +                       /* Replay master state event */
61 +                       dsa_tree_master_admin_state_change(dst, master, admin_up);
62 +                       dsa_tree_master_oper_state_change(dst, master,
63 +                                                         netif_oper_up(master));
64                 }
65         }
66  
67 @@ -1077,9 +1087,19 @@ static void dsa_tree_teardown_master(str
68  
69         rtnl_lock();
70  
71 -       list_for_each_entry(dp, &dst->ports, list)
72 -               if (dsa_port_is_cpu(dp))
73 -                       dsa_master_teardown(dp->master);
74 +       list_for_each_entry(dp, &dst->ports, list) {
75 +               if (dsa_port_is_cpu(dp)) {
76 +                       struct net_device *master = dp->master;
77 +
78 +                       /* Synthesizing an "admin down" state is sufficient for
79 +                        * the switches to get a notification if the master is
80 +                        * currently up and running.
81 +                        */
82 +                       dsa_tree_master_admin_state_change(dst, master, false);
83 +
84 +                       dsa_master_teardown(master);
85 +               }
86 +       }
87  
88         rtnl_unlock();
89  }