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>
This commit is contained in:
Родитель
586c2b573e
Коммит
f8ed289fab
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче