bridge: vlan: use rcu for vlan_list traversal in br_fill_ifinfo
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Mon, 12 Oct 2015 19:47:03 +0000 (21:47 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 13 Oct 2015 11:57:54 +0000 (04:57 -0700)
br_fill_ifinfo is called by br_ifinfo_notify which can be called from
many contexts with different locks held, sometimes it relies upon
bridge's spinlock only which is a problem for the vlan code, so use
explicitly rcu for that to avoid problems.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_netlink.c

index 2ee8fd63c7269246b1663f71f0011daad4dca828..94b4de8c46464b95bd28603c12a289eb822c9fb6 100644 (file)
@@ -253,7 +253,7 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
         * if vlaninfo represents a range
         */
        pvid = br_get_pvid(vg);
-       list_for_each_entry(v, &vg->vlan_list, vlist) {
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                flags = 0;
                if (!br_vlan_should_use(v))
                        continue;
@@ -303,7 +303,7 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
        u16 pvid;
 
        pvid = br_get_pvid(vg);
-       list_for_each_entry(v, &vg->vlan_list, vlist) {
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                if (!br_vlan_should_use(v))
                        continue;
 
@@ -386,22 +386,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                struct nlattr *af;
                int err;
 
+               /* RCU needed because of the VLAN locking rules (rcu || rtnl) */
+               rcu_read_lock();
                if (port)
-                       vg = nbp_vlan_group(port);
+                       vg = nbp_vlan_group_rcu(port);
                else
-                       vg = br_vlan_group(br);
+                       vg = br_vlan_group_rcu(br);
 
-               if (!vg || !vg->num_vlans)
+               if (!vg || !vg->num_vlans) {
+                       rcu_read_unlock();
                        goto done;
-
+               }
                af = nla_nest_start(skb, IFLA_AF_SPEC);
-               if (!af)
+               if (!af) {
+                       rcu_read_unlock();
                        goto nla_put_failure;
-
+               }
                if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
                        err = br_fill_ifvlaninfo_compressed(skb, vg);
                else
                        err = br_fill_ifvlaninfo(skb, vg);
+               rcu_read_unlock();
                if (err)
                        goto nla_put_failure;
                nla_nest_end(skb, af);