aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-10-04 16:43:56 -0700
committerDavid S. Miller <davem@davemloft.net>2015-10-04 16:43:56 -0700
commit68d4e5208291ac8bd7d47d992f353da31a2e1484 (patch)
tree017c1d62972036289cbb2111e1321471e0930353
parenttcp/dccp: add SLAB_DESTROY_BY_RCU flag for request sockets (diff)
parentbridge: vlan: use br_vlan_should_use to simplify __vlan_add/del (diff)
downloadlinux-dev-68d4e5208291ac8bd7d47d992f353da31a2e1484.tar.xz
linux-dev-68d4e5208291ac8bd7d47d992f353da31a2e1484.zip
Merge branch 'bridge-vlan'
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>
-rw-r--r--net/bridge/br_netlink.c10
-rw-r--r--net/bridge/br_private.h2
-rw-r--r--net/bridge/br_vlan.c102
3 files changed, 66 insertions, 48 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index c64dcad11662..c3186198d46d 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -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))
@@ -76,13 +76,19 @@ initvars:
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,
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4ed8308db66e..1ff6a0faef3f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -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)) {
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 1a79e199ca3b..b879111582f0 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -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,
@@ -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:
@@ -161,7 +195,7 @@ 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;
@@ -169,17 +203,15 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
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().
@@ -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;
}
}
@@ -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;
}
@@ -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) {
@@ -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;
}