Commit f8ed289f authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller

bridge: vlan: use br_vlan_(get|put)_master to deal with refcounts

Introduce br_vlan_(get|put)_master which take a reference (or create the
master vlan first if it didn't exist) and drop a reference respectively.
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 586c2b57
...@@ -146,6 +146,40 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, ...@@ -146,6 +146,40 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
return err; return err;
} }
/* Returns a master vlan, if it didn't exist it gets created. In all cases a
* a reference is taken to the master vlan before returning.
*/
static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid)
{
struct net_bridge_vlan *masterv;
masterv = br_vlan_find(br->vlgrp, vid);
if (!masterv) {
/* missing global ctx, create it now */
if (br_vlan_add(br, vid, 0))
return NULL;
masterv = br_vlan_find(br->vlgrp, vid);
if (WARN_ON(!masterv))
return NULL;
}
atomic_inc(&masterv->refcnt);
return masterv;
}
static void br_vlan_put_master(struct net_bridge_vlan *masterv)
{
if (!br_vlan_is_master(masterv))
return;
if (atomic_dec_and_test(&masterv->refcnt)) {
rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash,
&masterv->vnode, br_vlan_rht_params);
__vlan_del_list(masterv);
kfree_rcu(masterv, rcu);
}
}
/* This is the shared VLAN add function which works for both ports and bridge /* This is the shared VLAN add function which works for both ports and bridge
* devices. There are four possible calls to this function in terms of the * devices. There are four possible calls to this function in terms of the
* vlan entry type: * vlan entry type:
...@@ -196,16 +230,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) ...@@ -196,16 +230,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
goto out_filt; goto out_filt;
} }
masterv = br_vlan_find(br->vlgrp, v->vid); masterv = br_vlan_get_master(br, v->vid);
if (!masterv) { if (!masterv)
/* missing global ctx, create it now */ goto out_filt;
err = br_vlan_add(br, v->vid, 0);
if (err)
goto out_filt;
masterv = br_vlan_find(br->vlgrp, v->vid);
WARN_ON(!masterv);
}
atomic_inc(&masterv->refcnt);
v->brvlan = masterv; v->brvlan = masterv;
} }
...@@ -240,7 +267,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) ...@@ -240,7 +267,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
if (p) { if (p) {
__vlan_vid_del(dev, br, v->vid); __vlan_vid_del(dev, br, v->vid);
if (masterv) { if (masterv) {
atomic_dec(&masterv->refcnt); br_vlan_put_master(masterv);
v->brvlan = NULL; v->brvlan = NULL;
} }
} }
...@@ -289,12 +316,7 @@ static int __vlan_del(struct net_bridge_vlan *v) ...@@ -289,12 +316,7 @@ static int __vlan_del(struct net_bridge_vlan *v)
kfree_rcu(v, rcu); kfree_rcu(v, rcu);
} }
if (atomic_dec_and_test(&masterv->refcnt)) { br_vlan_put_master(masterv);
rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash,
&masterv->vnode, br_vlan_rht_params);
__vlan_del_list(masterv);
kfree_rcu(masterv, rcu);
}
out: out:
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment