aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Aleksandrov2015-10-12 21:47:03 +0200
committerDavid S. Miller2015-10-13 04:57:54 -0700
commite9c953eff7f0ec69a52cfa87b912ab48902a0314 (patch)
tree3a7b29b6618b2d13ba9be59027ed209dc58dd6d1
parent907b1e6e83ed25d9dece1e55b704581b6c127051 (diff)
bridge: vlan: use rcu for vlan_list traversal in br_fill_ifinfo
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>
-rw-r--r--net/bridge/br_netlink.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 2ee8fd63c726..94b4de8c4646 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -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);