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

bridge: vlan: signal if anything changed on vlan add

Before this patch there was no way to tell if the vlan add operation
actually changed anything, thus we would always generate a notification
on adds. Let's make the notifications more precise and generate them
only if anything changed, so use the new bool parameter to signal that the
vlan was updated. We cannot return an error because there are valid use
cases that will be broken (e.g. overlapping range add) and also we can't
risk masking errors due to calls into drivers for vlan add which can
potentially return anything.
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Reviewed-by: default avatarToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e19b42a1
...@@ -508,6 +508,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, ...@@ -508,6 +508,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
int cmd, struct bridge_vlan_info *vinfo, bool *changed) int cmd, struct bridge_vlan_info *vinfo, bool *changed)
{ {
bool curr_change;
int err = 0; int err = 0;
switch (cmd) { switch (cmd) {
...@@ -516,12 +517,14 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, ...@@ -516,12 +517,14 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
/* if the MASTER flag is set this will act on the global /* if the MASTER flag is set this will act on the global
* per-VLAN entry as well * per-VLAN entry as well
*/ */
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); err = nbp_vlan_add(p, vinfo->vid, vinfo->flags,
&curr_change);
} else { } else {
vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY; vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
err = br_vlan_add(br, vinfo->vid, vinfo->flags); err = br_vlan_add(br, vinfo->vid, vinfo->flags,
&curr_change);
} }
if (!err) if (curr_change)
*changed = true; *changed = true;
break; break;
......
...@@ -803,7 +803,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, ...@@ -803,7 +803,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_bridge_port *port, const struct net_bridge_port *port,
struct net_bridge_vlan_group *vg, struct net_bridge_vlan_group *vg,
struct sk_buff *skb); struct sk_buff *skb);
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
bool *changed);
int br_vlan_delete(struct net_bridge *br, u16 vid); int br_vlan_delete(struct net_bridge *br, u16 vid);
void br_vlan_flush(struct net_bridge *br); void br_vlan_flush(struct net_bridge *br);
struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid); struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid);
...@@ -816,7 +817,8 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val); ...@@ -816,7 +817,8 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val);
int br_vlan_init(struct net_bridge *br); int br_vlan_init(struct net_bridge *br);
int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val); int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid); int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed);
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
void nbp_vlan_flush(struct net_bridge_port *port); void nbp_vlan_flush(struct net_bridge_port *port);
int nbp_vlan_init(struct net_bridge_port *port); int nbp_vlan_init(struct net_bridge_port *port);
...@@ -903,8 +905,10 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, ...@@ -903,8 +905,10 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
return skb; return skb;
} }
static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
bool *changed)
{ {
*changed = false;
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -926,8 +930,10 @@ static inline int br_vlan_init(struct net_bridge *br) ...@@ -926,8 +930,10 @@ static inline int br_vlan_init(struct net_bridge *br)
return 0; return 0;
} }
static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed)
{ {
*changed = false;
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -32,27 +32,34 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid) ...@@ -32,27 +32,34 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params); return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
} }
static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid) static bool __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid)
{ {
if (vg->pvid == vid) if (vg->pvid == vid)
return; return false;
smp_wmb(); smp_wmb();
vg->pvid = vid; vg->pvid = vid;
return true;
} }
static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) static bool __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid)
{ {
if (vg->pvid != vid) if (vg->pvid != vid)
return; return false;
smp_wmb(); smp_wmb();
vg->pvid = 0; vg->pvid = 0;
return true;
} }
static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) /* return true if anything changed, false otherwise */
static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
u16 old_flags = v->flags;
bool ret;
if (br_vlan_is_master(v)) if (br_vlan_is_master(v))
vg = br_vlan_group(v->br); vg = br_vlan_group(v->br);
...@@ -60,14 +67,16 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) ...@@ -60,14 +67,16 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
vg = nbp_vlan_group(v->port); vg = nbp_vlan_group(v->port);
if (flags & BRIDGE_VLAN_INFO_PVID) if (flags & BRIDGE_VLAN_INFO_PVID)
__vlan_add_pvid(vg, v->vid); ret = __vlan_add_pvid(vg, v->vid);
else else
__vlan_delete_pvid(vg, v->vid); ret = __vlan_delete_pvid(vg, v->vid);
if (flags & BRIDGE_VLAN_INFO_UNTAGGED) if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
v->flags |= BRIDGE_VLAN_INFO_UNTAGGED; v->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
else else
v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED; v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
return ret || !!(old_flags ^ v->flags);
} }
static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
...@@ -151,8 +160,10 @@ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid ...@@ -151,8 +160,10 @@ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid
vg = br_vlan_group(br); vg = br_vlan_group(br);
masterv = br_vlan_find(vg, vid); masterv = br_vlan_find(vg, vid);
if (!masterv) { if (!masterv) {
bool changed;
/* missing global ctx, create it now */ /* missing global ctx, create it now */
if (br_vlan_add(br, vid, 0)) if (br_vlan_add(br, vid, 0, &changed))
return NULL; return NULL;
masterv = br_vlan_find(vg, vid); masterv = br_vlan_find(vg, vid);
if (WARN_ON(!masterv)) if (WARN_ON(!masterv))
...@@ -232,8 +243,11 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) ...@@ -232,8 +243,11 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
/* need to work on the master vlan too */ /* need to work on the master vlan too */
if (flags & BRIDGE_VLAN_INFO_MASTER) { if (flags & BRIDGE_VLAN_INFO_MASTER) {
err = br_vlan_add(br, v->vid, flags | bool changed;
BRIDGE_VLAN_INFO_BRENTRY);
err = br_vlan_add(br, v->vid,
flags | BRIDGE_VLAN_INFO_BRENTRY,
&changed);
if (err) if (err)
goto out_filt; goto out_filt;
} }
...@@ -550,8 +564,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) ...@@ -550,8 +564,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
/* Must be protected by RTNL. /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive. * Must be called with vid in range from 1 to 4094 inclusive.
* changed must be true only if the vlan was created or updated
*/ */
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *vlan; struct net_bridge_vlan *vlan;
...@@ -559,6 +574,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) ...@@ -559,6 +574,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
ASSERT_RTNL(); ASSERT_RTNL();
*changed = false;
vg = br_vlan_group(br); vg = br_vlan_group(br);
vlan = br_vlan_find(vg, vid); vlan = br_vlan_find(vg, vid);
if (vlan) { if (vlan) {
...@@ -576,8 +592,11 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) ...@@ -576,8 +592,11 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
refcount_inc(&vlan->refcnt); refcount_inc(&vlan->refcnt);
vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY; vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
vg->num_vlans++; vg->num_vlans++;
*changed = true;
} }
__vlan_add_flags(vlan, flags); if (__vlan_add_flags(vlan, flags))
*changed = true;
return 0; return 0;
} }
...@@ -600,6 +619,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) ...@@ -600,6 +619,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
if (ret) { if (ret) {
free_percpu(vlan->stats); free_percpu(vlan->stats);
kfree(vlan); kfree(vlan);
} else {
*changed = true;
} }
return ret; return ret;
...@@ -824,9 +845,10 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) ...@@ -824,9 +845,10 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
const struct net_bridge_vlan *pvent; const struct net_bridge_vlan *pvent;
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_port *p; struct net_bridge_port *p;
unsigned long *changed;
bool vlchange;
u16 old_pvid; u16 old_pvid;
int err = 0; int err = 0;
unsigned long *changed;
if (!pvid) { if (!pvid) {
br_vlan_disable_default_pvid(br); br_vlan_disable_default_pvid(br);
...@@ -850,7 +872,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) ...@@ -850,7 +872,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
err = br_vlan_add(br, pvid, err = br_vlan_add(br, pvid,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY); BRIDGE_VLAN_INFO_BRENTRY,
&vlchange);
if (err) if (err)
goto out; goto out;
br_vlan_delete(br, old_pvid); br_vlan_delete(br, old_pvid);
...@@ -869,7 +892,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) ...@@ -869,7 +892,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
err = nbp_vlan_add(p, pvid, err = nbp_vlan_add(p, pvid,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED); BRIDGE_VLAN_INFO_UNTAGGED,
&vlchange);
if (err) if (err)
goto err_port; goto err_port;
nbp_vlan_delete(p, old_pvid); nbp_vlan_delete(p, old_pvid);
...@@ -890,7 +914,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) ...@@ -890,7 +914,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
if (old_pvid) if (old_pvid)
nbp_vlan_add(p, old_pvid, nbp_vlan_add(p, old_pvid,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED); BRIDGE_VLAN_INFO_UNTAGGED,
&vlchange);
nbp_vlan_delete(p, pvid); nbp_vlan_delete(p, pvid);
} }
...@@ -899,7 +924,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) ...@@ -899,7 +924,8 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
br_vlan_add(br, old_pvid, br_vlan_add(br, old_pvid,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY); BRIDGE_VLAN_INFO_BRENTRY,
&vlchange);
br_vlan_delete(br, pvid); br_vlan_delete(br, pvid);
} }
goto out; goto out;
...@@ -931,6 +957,7 @@ int br_vlan_init(struct net_bridge *br) ...@@ -931,6 +957,7 @@ int br_vlan_init(struct net_bridge *br)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
int ret = -ENOMEM; int ret = -ENOMEM;
bool changed;
vg = kzalloc(sizeof(*vg), GFP_KERNEL); vg = kzalloc(sizeof(*vg), GFP_KERNEL);
if (!vg) if (!vg)
...@@ -947,7 +974,7 @@ int br_vlan_init(struct net_bridge *br) ...@@ -947,7 +974,7 @@ int br_vlan_init(struct net_bridge *br)
rcu_assign_pointer(br->vlgrp, vg); rcu_assign_pointer(br->vlgrp, vg);
ret = br_vlan_add(br, 1, ret = br_vlan_add(br, 1,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY); BRIDGE_VLAN_INFO_BRENTRY, &changed);
if (ret) if (ret)
goto err_vlan_add; goto err_vlan_add;
...@@ -992,9 +1019,12 @@ int nbp_vlan_init(struct net_bridge_port *p) ...@@ -992,9 +1019,12 @@ int nbp_vlan_init(struct net_bridge_port *p)
INIT_LIST_HEAD(&vg->vlan_list); INIT_LIST_HEAD(&vg->vlan_list);
rcu_assign_pointer(p->vlgrp, vg); rcu_assign_pointer(p->vlgrp, vg);
if (p->br->default_pvid) { if (p->br->default_pvid) {
bool changed;
ret = nbp_vlan_add(p, p->br->default_pvid, ret = nbp_vlan_add(p, p->br->default_pvid,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED); BRIDGE_VLAN_INFO_UNTAGGED,
&changed);
if (ret) if (ret)
goto err_vlan_add; goto err_vlan_add;
} }
...@@ -1016,8 +1046,10 @@ int nbp_vlan_init(struct net_bridge_port *p) ...@@ -1016,8 +1046,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
/* Must be protected by RTNL. /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive. * Must be called with vid in range from 1 to 4094 inclusive.
* changed must be true only if the vlan was created or updated
*/ */
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed)
{ {
struct switchdev_obj_port_vlan v = { struct switchdev_obj_port_vlan v = {
.obj.orig_dev = port->dev, .obj.orig_dev = port->dev,
...@@ -1031,13 +1063,15 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) ...@@ -1031,13 +1063,15 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
ASSERT_RTNL(); ASSERT_RTNL();
*changed = false;
vlan = br_vlan_find(nbp_vlan_group(port), vid); vlan = br_vlan_find(nbp_vlan_group(port), vid);
if (vlan) { if (vlan) {
/* Pass the flags to the hardware bridge */ /* Pass the flags to the hardware bridge */
ret = switchdev_port_obj_add(port->dev, &v.obj); ret = switchdev_port_obj_add(port->dev, &v.obj);
if (ret && ret != -EOPNOTSUPP) if (ret && ret != -EOPNOTSUPP)
return ret; return ret;
__vlan_add_flags(vlan, flags); *changed = __vlan_add_flags(vlan, flags);
return 0; return 0;
} }
...@@ -1050,6 +1084,8 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) ...@@ -1050,6 +1084,8 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
ret = __vlan_add(vlan, flags); ret = __vlan_add(vlan, flags);
if (ret) if (ret)
kfree(vlan); kfree(vlan);
else
*changed = true;
return ret; return ret;
} }
......
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