mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: bridge: vlan: fix bugs caused by switchdev deletion errors
Allowing switchdev to prevent vlan deletion and error out in __vlan_del could cause multiple different issues - inconsistent state, memory leaks when flushing, NULL pointer dereference on bridge error when flushing. It doesn't make sense to allow it to stop __vlan_del, so log the error and continue with software vlan deletion. This is also consistent with 8021q behaviour. Suggested-by: Ido Schimmel <idosch@nvidia.com> Fixes:bf361ad381("net: bridge: check __vlan_vid_del for error") Fixes:5454f5c28e("net: bridge: vlan: check for errors from __vlan_del in __vlan_flush") Fixes:2594e9064a("bridge: vlan: add per-vlan struct and move to rhashtables") Fixes:9c86ce2c1a("net: bridge: Notify about bridge VLANs") Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260914105258.3436918-1-razor@blackwall.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
f0ef4b1eae
commit
2842ce397d
|
|
@ -387,12 +387,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __vlan_del(struct net_bridge_vlan *v)
|
static void __vlan_del(struct net_bridge_vlan *v)
|
||||||
{
|
{
|
||||||
struct net_bridge_vlan *masterv = v;
|
struct net_bridge_vlan *masterv = v;
|
||||||
struct net_bridge_vlan_group *vg;
|
struct net_bridge_vlan_group *vg;
|
||||||
struct net_bridge_port *p = NULL;
|
struct net_bridge_port *p = NULL;
|
||||||
int err = 0;
|
int err;
|
||||||
|
|
||||||
if (br_vlan_is_master(v)) {
|
if (br_vlan_is_master(v)) {
|
||||||
vg = br_vlan_group(v->br);
|
vg = br_vlan_group(v->br);
|
||||||
|
|
@ -406,12 +406,16 @@ static int __vlan_del(struct net_bridge_vlan *v)
|
||||||
if (p) {
|
if (p) {
|
||||||
err = __vlan_vid_del(p->dev, p->br, v);
|
err = __vlan_vid_del(p->dev, p->br, v);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
br_warn(p->br,
|
||||||
|
"port %u(%s) failed to delete vlan %u from switchdev: %pe\n",
|
||||||
|
(unsigned int)p->port_no, p->dev->name,
|
||||||
|
v->vid, ERR_PTR(err));
|
||||||
} else {
|
} else {
|
||||||
err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
|
err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
|
||||||
if (err && err != -EOPNOTSUPP)
|
if (err && err != -EOPNOTSUPP)
|
||||||
goto out;
|
br_warn(v->br,
|
||||||
err = 0;
|
"failed to delete bridge vlan %u from switchdev: %pe\n",
|
||||||
|
v->vid, ERR_PTR(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (br_vlan_should_use(v)) {
|
if (br_vlan_should_use(v)) {
|
||||||
|
|
@ -431,8 +435,6 @@ static int __vlan_del(struct net_bridge_vlan *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
br_vlan_put_master(masterv);
|
br_vlan_put_master(masterv);
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __vlan_group_free(struct net_bridge_vlan_group *vg)
|
static void __vlan_group_free(struct net_bridge_vlan_group *vg)
|
||||||
|
|
@ -449,7 +451,6 @@ static void __vlan_flush(const struct net_bridge *br,
|
||||||
{
|
{
|
||||||
struct net_bridge_vlan *vlan, *tmp;
|
struct net_bridge_vlan *vlan, *tmp;
|
||||||
u16 v_start = 0, v_end = 0;
|
u16 v_start = 0, v_end = 0;
|
||||||
int err;
|
|
||||||
|
|
||||||
__vlan_delete_pvid(vg, vg->pvid);
|
__vlan_delete_pvid(vg, vg->pvid);
|
||||||
list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
|
list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
|
||||||
|
|
@ -463,13 +464,7 @@ static void __vlan_flush(const struct net_bridge *br,
|
||||||
}
|
}
|
||||||
v_end = vlan->vid;
|
v_end = vlan->vid;
|
||||||
|
|
||||||
err = __vlan_del(vlan);
|
__vlan_del(vlan);
|
||||||
if (err) {
|
|
||||||
br_err(br,
|
|
||||||
"port %u(%s) failed to delete vlan %d: %pe\n",
|
|
||||||
(unsigned int) p->port_no, p->dev->name,
|
|
||||||
vlan->vid, ERR_PTR(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* notify about the last/whole vlan range */
|
/* notify about the last/whole vlan range */
|
||||||
|
|
@ -837,8 +832,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
|
||||||
br_fdb_delete_by_port(br, NULL, vid, 0);
|
br_fdb_delete_by_port(br, NULL, vid, 0);
|
||||||
|
|
||||||
vlan_tunnel_info_del(vg, v);
|
vlan_tunnel_info_del(vg, v);
|
||||||
|
__vlan_del(v);
|
||||||
|
|
||||||
return __vlan_del(v);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void br_vlan_flush(struct net_bridge *br)
|
void br_vlan_flush(struct net_bridge *br)
|
||||||
|
|
@ -1368,8 +1364,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
|
br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
|
||||||
br_fdb_delete_by_port(port->br, port, vid, 0);
|
br_fdb_delete_by_port(port->br, port, vid, 0);
|
||||||
|
__vlan_del(v);
|
||||||
|
|
||||||
return __vlan_del(v);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbp_vlan_flush(struct net_bridge_port *port)
|
void nbp_vlan_flush(struct net_bridge_port *port)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user