1
0
Fork 0

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: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
hifive-unleashed-5.1
Nikolay Aleksandrov 2015-10-02 15:05:11 +02:00 committed by David S. Miller
parent 586c2b573e
commit f8ed289fab
1 changed files with 39 additions and 17 deletions

View File

@ -146,6 +146,40 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
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
* devices. There are four possible calls to this function in terms of the
* vlan entry type:
@ -196,16 +230,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
goto out_filt;
}
masterv = br_vlan_find(br->vlgrp, v->vid);
if (!masterv) {
/* missing global ctx, create it now */
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);
masterv = br_vlan_get_master(br, v->vid);
if (!masterv)
goto out_filt;
v->brvlan = masterv;
}
@ -240,7 +267,7 @@ out_filt:
if (p) {
__vlan_vid_del(dev, br, v->vid);
if (masterv) {
atomic_dec(&masterv->refcnt);
br_vlan_put_master(masterv);
v->brvlan = NULL;
}
}
@ -289,12 +316,7 @@ static int __vlan_del(struct net_bridge_vlan *v)
kfree_rcu(v, rcu);
}
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);
}
br_vlan_put_master(masterv);
out:
return err;
}