Skip to content

Commit

Permalink
Merge branch 'bridge-vlan'
Browse files Browse the repository at this point in the history
Nikolay Aleksandrov says:

====================
bridge: vlan: cleanups & fixes (part 2)

This is the second follow-up set with one fix (patch 01) and more cleanups
(patches 02,03 and 04). These are minor compared to the previous ones and
should be the last before taking on the optimization changes on the
fast-path.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 4, 2015
2 parents e96f78a + 6be144f commit 68d4e52
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 48 deletions.
10 changes: 8 additions & 2 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,

pvid = br_get_pvid(vg);
/* Count number of vlan infos */
list_for_each_entry(v, &vg->vlan_list, vlist) {
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
flags = 0;
/* only a context, bridge vlan not activated */
if (!br_vlan_should_use(v))
Expand Down Expand Up @@ -76,13 +76,19 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
u32 filter_mask)
{
int num_vlans;

if (!vg)
return 0;

if (filter_mask & RTEXT_FILTER_BRVLAN)
return vg->num_vlans;

return __get_num_vlan_infos(vg, filter_mask);
rcu_read_lock();
num_vlans = __get_num_vlan_infos(vg, filter_mask);
rcu_read_unlock();

return num_vlans;
}

static size_t br_get_link_af_size_filtered(const struct net_device *dev,
Expand Down
2 changes: 1 addition & 1 deletion net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ static inline bool br_vlan_is_brentry(const struct net_bridge_vlan *v)
return v->flags & BRIDGE_VLAN_INFO_BRENTRY;
}

/* check if we should use the vlan entry is usable */
/* check if we should use the vlan entry, returns false if it's only context */
static inline bool br_vlan_should_use(const struct net_bridge_vlan *v)
{
if (br_vlan_is_master(v)) {
Expand Down
102 changes: 57 additions & 45 deletions net/bridge/br_vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ static void __vlan_add_list(struct net_bridge_vlan *v)
else
break;
}
list_add(&v->vlist, hpos);
list_add_rcu(&v->vlist, hpos);
}

static void __vlan_del_list(struct net_bridge_vlan *v)
{
list_del(&v->vlist);
list_del_rcu(&v->vlist);
}

static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
Expand Down Expand Up @@ -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:
Expand All @@ -161,25 +195,23 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
{
struct net_bridge_vlan *masterv = NULL;
struct net_bridge_port *p = NULL;
struct rhashtable *tbl;
struct net_bridge_vlan_group *vg;
struct net_device *dev;
struct net_bridge *br;
int err;

if (br_vlan_is_master(v)) {
br = v->br;
dev = br->dev;
tbl = &br->vlgrp->vlan_hash;
vg = br->vlgrp;
} else {
p = v->port;
br = p->br;
dev = p->dev;
tbl = &p->vlgrp->vlan_hash;
vg = p->vlgrp;
}

if (p) {
u16 master_flags = flags;

/* Add VLAN to the device filter if it is supported.
* This ensures tagged traffic enters the bridge when
* promiscuous mode is disabled by br_manage_promisc().
Expand All @@ -190,57 +222,49 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)

/* need to work on the master vlan too */
if (flags & BRIDGE_VLAN_INFO_MASTER) {
master_flags |= BRIDGE_VLAN_INFO_BRENTRY;
err = br_vlan_add(br, v->vid, master_flags);
err = br_vlan_add(br, v->vid, flags |
BRIDGE_VLAN_INFO_BRENTRY);
if (err)
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;
}

/* Add the dev mac only if it's a usable vlan */
/* Add the dev mac and count the vlan only if it's usable */
if (br_vlan_should_use(v)) {
err = br_fdb_insert(br, p, dev->dev_addr, v->vid);
if (err) {
br_err(br, "failed insert local address into bridge forwarding table\n");
goto out_filt;
}
vg->num_vlans++;
}

err = rhashtable_lookup_insert_fast(tbl, &v->vnode, br_vlan_rht_params);
err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode,
br_vlan_rht_params);
if (err)
goto out_fdb_insert;

__vlan_add_list(v);
__vlan_add_flags(v, flags);
if (br_vlan_is_master(v)) {
if (br_vlan_is_brentry(v))
br->vlgrp->num_vlans++;
} else {
p->vlgrp->num_vlans++;
}
out:
return err;

out_fdb_insert:
br_fdb_find_delete_local(br, p, br->dev->dev_addr, v->vid);
if (br_vlan_should_use(v)) {
br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid);
vg->num_vlans--;
}

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;
}
}
Expand All @@ -253,15 +277,12 @@ static int __vlan_del(struct net_bridge_vlan *v)
struct net_bridge_vlan *masterv = v;
struct net_bridge_vlan_group *vg;
struct net_bridge_port *p = NULL;
struct net_bridge *br;
int err = 0;

if (br_vlan_is_master(v)) {
br = v->br;
vg = v->br->vlgrp;
} else {
p = v->port;
br = p->br;
vg = v->port->vlgrp;
masterv = v->brvlan;
}
Expand All @@ -273,13 +294,9 @@ static int __vlan_del(struct net_bridge_vlan *v)
goto out;
}

if (br_vlan_is_master(v)) {
if (br_vlan_is_brentry(v)) {
v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY;
br->vlgrp->num_vlans--;
}
} else {
p->vlgrp->num_vlans--;
if (br_vlan_should_use(v)) {
v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY;
vg->num_vlans--;
}

if (masterv != v) {
Expand All @@ -289,12 +306,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;
}
Expand Down

0 comments on commit 68d4e52

Please sign in to comment.