Commit 9e7c5752 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge-vlan-notify'

Petr Machata says:

====================
net: bridge: Notify about bridge VLANs

In commit 946a11e7 ("mlxsw: spectrum_span: Allow bridge for gretap
mirror"), mlxsw got support for offloading mirror-to-gretap such that
the underlay packet path involves a bridge. In that case, the offload is
also influenced by PVID setting of said bridge. However, changes to VLAN
configuration of the bridge itself do not generate switchdev
notifications, so there's no mechanism to prod mlxsw to update the
offload when these settings change.

In this patchset, the problem is resolved by distributing the switchdev
notification SWITCHDEV_OBJ_ID_PORT_VLAN also for configuration changes
on bridge VLANs. Since stacked devices distribute the notification to
lower devices, such event eventually reaches the driver, which can
determine whether it's a bridge or port VLAN by inspecting orig_dev.

To keep things consistent, the newly-distributed notifications observe
the same protocol as the existing ones: dual prepare/commit, with
-EOPNOTSUPP indicating lack of support, even though there's currently
nothing to prepare for and nothing to support. Correspondingly, all
switchdev drivers have been updated to return -EOPNOTSUPP for bridge
VLAN notifications.

In patches #1 and #2, the code base is changed to support the following
additions: functions br_switchdev_port_vlan_add() and
br_switchdev_port_vlan_del() are introduced to simplify sending
notifications; and br_vlan_add_existing() is introduced to later make it
simpler to add error-handling code for the case of configuring a
preexisting VLAN on bridge CPU port.

In patches #3-#6, respectively for mlxsw, rocker, DSA and DPAA2 ethsw,
the new notifications (which are not enabled yet) are ignored to
maintain the current behavior.

In patch #7, the notification is actually enabled.

In patch #8, mlxsw is changed to update offloads of mirror-to-gre also
for bridge-related notifications.

Changes from v3 to v4:

- In patch #1, separate variable declarations from program logic.
- Add patch #2.
- In patch #7, add error handling around a newly-introduced call to
  br_switchdev_port_vlan_add().
- Rephrase commit messages of patches #3-#6 to explain motivation for
  the change.

Changes from v2 to v3:

- Add a fallback definition for br_switchdev_port_obj_add() and
  br_switchdev_port_obj_del() when !CONFIG_NET_SWITCHDEV.

Changes from v1 to v2:

- Rename br_switchdev_port_obj_add() and br_switchdev_port_obj_del() to
  br_switchdev_port_vlan_add() and br_switchdev_port_vlan_del(), and
  move from br_vlan.c to br_switchdev.c.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 32d26a68 7edcb8ec
...@@ -1144,6 +1144,9 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1144,6 +1144,9 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_bridge_port *bridge_port;
u16 vid; u16 vid;
if (netif_is_bridge_master(orig_dev))
return -EOPNOTSUPP;
if (switchdev_trans_ph_prepare(trans)) if (switchdev_trans_ph_prepare(trans))
return 0; return 0;
...@@ -1694,7 +1697,7 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev, ...@@ -1694,7 +1697,7 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans); err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans);
if (switchdev_trans_ph_commit(trans)) { if (switchdev_trans_ph_prepare(trans)) {
/* The event is emitted before the changes are actually /* The event is emitted before the changes are actually
* applied to the bridge. Therefore schedule the respin * applied to the bridge. Therefore schedule the respin
* call for later, so that the respin logic sees the * call for later, so that the respin logic sees the
...@@ -1741,6 +1744,9 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -1741,6 +1744,9 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_bridge_port *bridge_port;
u16 vid; u16 vid;
if (netif_is_bridge_master(orig_dev))
return -EOPNOTSUPP;
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
if (WARN_ON(!bridge_port)) if (WARN_ON(!bridge_port))
return -EINVAL; return -EINVAL;
......
...@@ -1632,6 +1632,9 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, ...@@ -1632,6 +1632,9 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
{ {
struct rocker_world_ops *wops = rocker_port->rocker->wops; struct rocker_world_ops *wops = rocker_port->rocker->wops;
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
if (!wops->port_obj_vlan_add) if (!wops->port_obj_vlan_add)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1647,6 +1650,9 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, ...@@ -1647,6 +1650,9 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port,
{ {
struct rocker_world_ops *wops = rocker_port->rocker->wops; struct rocker_world_ops *wops = rocker_port->rocker->wops;
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
if (!wops->port_obj_vlan_del) if (!wops->port_obj_vlan_del)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return wops->port_obj_vlan_del(rocker_port, vlan); return wops->port_obj_vlan_del(rocker_port, vlan);
......
...@@ -719,6 +719,9 @@ static int port_vlans_add(struct net_device *netdev, ...@@ -719,6 +719,9 @@ static int port_vlans_add(struct net_device *netdev,
struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int vid, err; int vid, err;
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
if (switchdev_trans_ph_prepare(trans)) if (switchdev_trans_ph_prepare(trans))
return 0; return 0;
...@@ -873,6 +876,9 @@ static int port_vlans_del(struct net_device *netdev, ...@@ -873,6 +876,9 @@ static int port_vlans_del(struct net_device *netdev,
struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int vid, err; int vid, err;
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
err = ethsw_port_del_vlan(port_priv, vid); err = ethsw_port_del_vlan(port_priv, vid);
if (err) if (err)
......
...@@ -1139,6 +1139,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, ...@@ -1139,6 +1139,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long mask); unsigned long mask);
void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
int type); int type);
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags);
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid);
static inline void br_switchdev_frame_unmark(struct sk_buff *skb) static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
{ {
...@@ -1168,6 +1170,17 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p, ...@@ -1168,6 +1170,17 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p,
return 0; return 0;
} }
static inline int br_switchdev_port_vlan_add(struct net_device *dev,
u16 vid, u16 flags)
{
return -EOPNOTSUPP;
}
static inline int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
{
return -EOPNOTSUPP;
}
static inline void static inline void
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
{ {
......
...@@ -136,3 +136,28 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ...@@ -136,3 +136,28 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
break; break;
} }
} }
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
return switchdev_port_obj_add(dev, &v.obj);
}
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
};
return switchdev_port_obj_del(dev, &v.obj);
}
...@@ -82,19 +82,12 @@ static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) ...@@ -82,19 +82,12 @@ static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 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,
u16 vid, u16 flags) u16 vid, u16 flags)
{ {
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
int err; int err;
/* Try switchdev op first. In case it is not supported, fallback to /* Try switchdev op first. In case it is not supported, fallback to
* 8021q add. * 8021q add.
*/ */
err = switchdev_port_obj_add(dev, &v.obj); err = br_switchdev_port_vlan_add(dev, vid, flags);
if (err == -EOPNOTSUPP) if (err == -EOPNOTSUPP)
return vlan_vid_add(dev, br->vlan_proto, vid); return vlan_vid_add(dev, br->vlan_proto, vid);
return err; return err;
...@@ -130,18 +123,12 @@ static void __vlan_del_list(struct net_bridge_vlan *v) ...@@ -130,18 +123,12 @@ static void __vlan_del_list(struct net_bridge_vlan *v)
static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br, static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
u16 vid) u16 vid)
{ {
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
};
int err; int err;
/* Try switchdev op first. In case it is not supported, fallback to /* Try switchdev op first. In case it is not supported, fallback to
* 8021q del. * 8021q del.
*/ */
err = switchdev_port_obj_del(dev, &v.obj); err = br_switchdev_port_vlan_del(dev, vid);
if (err == -EOPNOTSUPP) { if (err == -EOPNOTSUPP) {
vlan_vid_del(dev, br->vlan_proto, vid); vlan_vid_del(dev, br->vlan_proto, vid);
return 0; return 0;
...@@ -259,6 +246,10 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) ...@@ -259,6 +246,10 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
goto out_filt; goto out_filt;
v->brvlan = masterv; v->brvlan = masterv;
v->stats = masterv->stats; v->stats = masterv->stats;
} else {
err = br_switchdev_port_vlan_add(dev, v->vid, flags);
if (err && err != -EOPNOTSUPP)
goto out;
} }
/* Add the dev mac and count the vlan only if it's usable */ /* Add the dev mac and count the vlan only if it's usable */
...@@ -294,6 +285,8 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) ...@@ -294,6 +285,8 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
br_vlan_put_master(masterv); br_vlan_put_master(masterv);
v->brvlan = NULL; v->brvlan = NULL;
} }
} else {
br_switchdev_port_vlan_del(dev, v->vid);
} }
goto out; goto out;
...@@ -319,6 +312,11 @@ static int __vlan_del(struct net_bridge_vlan *v) ...@@ -319,6 +312,11 @@ static int __vlan_del(struct net_bridge_vlan *v)
err = __vlan_vid_del(p->dev, p->br, v->vid); err = __vlan_vid_del(p->dev, p->br, v->vid);
if (err) if (err)
goto out; goto out;
} else {
err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
if (err && err != -EOPNOTSUPP)
goto out;
err = 0;
} }
if (br_vlan_should_use(v)) { if (br_vlan_should_use(v)) {
...@@ -564,6 +562,48 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) ...@@ -564,6 +562,48 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
return false; return false;
} }
static int br_vlan_add_existing(struct net_bridge *br,
struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan,
u16 flags, bool *changed)
{
int err;
err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags);
if (err && err != -EOPNOTSUPP)
return err;
if (!br_vlan_is_brentry(vlan)) {
/* Trying to change flags of non-existent bridge vlan */
if (!(flags & BRIDGE_VLAN_INFO_BRENTRY)) {
err = -EINVAL;
goto err_flags;
}
/* It was only kept for port vlans, now make it real */
err = br_fdb_insert(br, NULL, br->dev->dev_addr,
vlan->vid);
if (err) {
br_err(br, "failed to insert local address into bridge forwarding table\n");
goto err_fdb_insert;
}
refcount_inc(&vlan->refcnt);
vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
vg->num_vlans++;
*changed = true;
}
if (__vlan_add_flags(vlan, flags))
*changed = true;
return 0;
err_fdb_insert:
err_flags:
br_switchdev_port_vlan_del(br->dev, vlan->vid);
return err;
}
/* 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 * changed must be true only if the vlan was created or updated
...@@ -579,28 +619,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed) ...@@ -579,28 +619,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
*changed = false; *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)
if (!br_vlan_is_brentry(vlan)) { return br_vlan_add_existing(br, vg, vlan, flags, changed);
/* Trying to change flags of non-existent bridge vlan */
if (!(flags & BRIDGE_VLAN_INFO_BRENTRY))
return -EINVAL;
/* It was only kept for port vlans, now make it real */
ret = br_fdb_insert(br, NULL, br->dev->dev_addr,
vlan->vid);
if (ret) {
br_err(br, "failed insert local address into bridge forwarding table\n");
return ret;
}
refcount_inc(&vlan->refcnt);
vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
vg->num_vlans++;
*changed = true;
}
if (__vlan_add_flags(vlan, flags))
*changed = true;
return 0;
}
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
if (!vlan) if (!vlan)
...@@ -1053,13 +1073,6 @@ int nbp_vlan_init(struct net_bridge_port *p) ...@@ -1053,13 +1073,6 @@ int nbp_vlan_init(struct net_bridge_port *p)
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) bool *changed)
{ {
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = port->dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
struct net_bridge_vlan *vlan; struct net_bridge_vlan *vlan;
int ret; int ret;
...@@ -1069,7 +1082,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags, ...@@ -1069,7 +1082,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
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 = br_switchdev_port_vlan_add(port->dev, vid, flags);
if (ret && ret != -EOPNOTSUPP) if (ret && ret != -EOPNOTSUPP)
return ret; return ret;
*changed = __vlan_add_flags(vlan, flags); *changed = __vlan_add_flags(vlan, flags);
......
...@@ -252,6 +252,9 @@ int dsa_port_vlan_add(struct dsa_port *dp, ...@@ -252,6 +252,9 @@ int dsa_port_vlan_add(struct dsa_port *dp,
.vlan = vlan, .vlan = vlan,
}; };
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
if (br_vlan_enabled(dp->bridge_dev)) if (br_vlan_enabled(dp->bridge_dev))
return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
...@@ -267,6 +270,9 @@ int dsa_port_vlan_del(struct dsa_port *dp, ...@@ -267,6 +270,9 @@ int dsa_port_vlan_del(struct dsa_port *dp,
.vlan = vlan, .vlan = vlan,
}; };
if (netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
if (br_vlan_enabled(dp->bridge_dev)) if (br_vlan_enabled(dp->bridge_dev))
return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
......
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