Commit 7e8c1858 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: bridge: allow the switchdev replay functions to be called for deletion

When a switchdev port leaves a LAG that is a bridge port, the switchdev
objects and port attributes offloaded to that port are not removed:

ip link add br0 type bridge
ip link add bond0 type bond mode 802.3ad
ip link set swp0 master bond0
ip link set bond0 master br0
bridge vlan add dev bond0 vid 100
ip link set swp0 nomaster

VLAN 100 will remain installed on swp0 despite it going into standalone
mode, because as far as the bridge is concerned, nothing ever happened
to its bridge port.

Let's extend the bridge vlan, fdb and mdb replay functions to take a
'bool adding' argument, and make DSA and ocelot call the replay
functions with 'adding' as false from the switchdev unsync path, for the
switch port that leaves the bridge.

Note that this patch in itself does not salvage anything, because in the
current pull mode of operation, DSA still needs to call the replay
helpers with adding=false. This will be done in another patch.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bdf123b4
...@@ -1175,12 +1175,12 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port, ...@@ -1175,12 +1175,12 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port,
ageing_time = br_get_ageing_time(bridge_dev); ageing_time = br_get_ageing_time(bridge_dev);
ocelot_port_attr_ageing_set(ocelot, port, ageing_time); ocelot_port_attr_ageing_set(ocelot, port, ageing_time);
err = br_mdb_replay(bridge_dev, brport_dev, priv, err = br_mdb_replay(bridge_dev, brport_dev, priv, true,
&ocelot_switchdev_blocking_nb, extack); &ocelot_switchdev_blocking_nb, extack);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
err = br_vlan_replay(bridge_dev, brport_dev, priv, err = br_vlan_replay(bridge_dev, brport_dev, priv, true,
&ocelot_switchdev_blocking_nb, extack); &ocelot_switchdev_blocking_nb, extack);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
......
...@@ -71,7 +71,7 @@ bool br_multicast_has_router_adjacent(struct net_device *dev, int proto); ...@@ -71,7 +71,7 @@ bool br_multicast_has_router_adjacent(struct net_device *dev, int proto);
bool br_multicast_enabled(const struct net_device *dev); bool br_multicast_enabled(const struct net_device *dev);
bool br_multicast_router(const struct net_device *dev); bool br_multicast_router(const struct net_device *dev);
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev, int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
const void *ctx, struct notifier_block *nb, const void *ctx, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
#else #else
static inline int br_multicast_list_adjacent(struct net_device *dev, static inline int br_multicast_list_adjacent(struct net_device *dev,
...@@ -106,7 +106,7 @@ static inline bool br_multicast_router(const struct net_device *dev) ...@@ -106,7 +106,7 @@ static inline bool br_multicast_router(const struct net_device *dev)
} }
static inline int br_mdb_replay(const struct net_device *br_dev, static inline int br_mdb_replay(const struct net_device *br_dev,
const struct net_device *dev, const void *ctx, const struct net_device *dev, const void *ctx,
struct notifier_block *nb, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -121,7 +121,7 @@ int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto); ...@@ -121,7 +121,7 @@ int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto);
int br_vlan_get_info(const struct net_device *dev, u16 vid, int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo); struct bridge_vlan_info *p_vinfo);
int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
const void *ctx, struct notifier_block *nb, const void *ctx, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
#else #else
static inline bool br_vlan_enabled(const struct net_device *dev) static inline bool br_vlan_enabled(const struct net_device *dev)
...@@ -152,7 +152,7 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid, ...@@ -152,7 +152,7 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
static inline int br_vlan_replay(struct net_device *br_dev, static inline int br_vlan_replay(struct net_device *br_dev,
struct net_device *dev, const void *ctx, struct net_device *dev, const void *ctx,
struct notifier_block *nb, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -168,7 +168,7 @@ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag); ...@@ -168,7 +168,7 @@ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
u8 br_port_get_stp_state(const struct net_device *dev); u8 br_port_get_stp_state(const struct net_device *dev);
clock_t br_get_ageing_time(const struct net_device *br_dev); clock_t br_get_ageing_time(const struct net_device *br_dev);
int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev, int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
const void *ctx, struct notifier_block *nb); const void *ctx, bool adding, struct notifier_block *nb);
#else #else
static inline struct net_device * static inline struct net_device *
br_fdb_find_port(const struct net_device *br_dev, br_fdb_find_port(const struct net_device *br_dev,
...@@ -200,7 +200,7 @@ static inline clock_t br_get_ageing_time(const struct net_device *br_dev) ...@@ -200,7 +200,7 @@ static inline clock_t br_get_ageing_time(const struct net_device *br_dev)
static inline int br_fdb_replay(const struct net_device *br_dev, static inline int br_fdb_replay(const struct net_device *br_dev,
const struct net_device *dev, const void *ctx, const struct net_device *dev, const void *ctx,
struct notifier_block *nb) bool adding, struct notifier_block *nb)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -728,7 +728,8 @@ static inline size_t fdb_nlmsg_size(void) ...@@ -728,7 +728,8 @@ static inline size_t fdb_nlmsg_size(void)
static int br_fdb_replay_one(struct notifier_block *nb, static int br_fdb_replay_one(struct notifier_block *nb,
const struct net_bridge_fdb_entry *fdb, const struct net_bridge_fdb_entry *fdb,
struct net_device *dev, const void *ctx) struct net_device *dev, unsigned long action,
const void *ctx)
{ {
struct switchdev_notifier_fdb_info item; struct switchdev_notifier_fdb_info item;
int err; int err;
...@@ -741,15 +742,16 @@ static int br_fdb_replay_one(struct notifier_block *nb, ...@@ -741,15 +742,16 @@ static int br_fdb_replay_one(struct notifier_block *nb,
item.info.dev = dev; item.info.dev = dev;
item.info.ctx = ctx; item.info.ctx = ctx;
err = nb->notifier_call(nb, SWITCHDEV_FDB_ADD_TO_DEVICE, &item); err = nb->notifier_call(nb, action, &item);
return notifier_to_errno(err); return notifier_to_errno(err);
} }
int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev, int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
const void *ctx, struct notifier_block *nb) const void *ctx, bool adding, struct notifier_block *nb)
{ {
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
struct net_bridge *br; struct net_bridge *br;
unsigned long action;
int err = 0; int err = 0;
if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev)) if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev))
...@@ -757,6 +759,11 @@ int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev, ...@@ -757,6 +759,11 @@ int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
br = netdev_priv(br_dev); br = netdev_priv(br_dev);
if (adding)
action = SWITCHDEV_FDB_ADD_TO_DEVICE;
else
action = SWITCHDEV_FDB_DEL_TO_DEVICE;
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) {
...@@ -767,7 +774,7 @@ int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev, ...@@ -767,7 +774,7 @@ int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
if (dst_dev != br_dev && dst_dev != dev) if (dst_dev != br_dev && dst_dev != dev)
continue; continue;
err = br_fdb_replay_one(nb, fdb, dst_dev, ctx); err = br_fdb_replay_one(nb, fdb, dst_dev, action, ctx);
if (err) if (err)
break; break;
} }
......
...@@ -568,7 +568,8 @@ static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb, ...@@ -568,7 +568,8 @@ static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
static int br_mdb_replay_one(struct notifier_block *nb, struct net_device *dev, static int br_mdb_replay_one(struct notifier_block *nb, struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
const void *ctx, struct netlink_ext_ack *extack) unsigned long action, const void *ctx,
struct netlink_ext_ack *extack)
{ {
struct switchdev_notifier_port_obj_info obj_info = { struct switchdev_notifier_port_obj_info obj_info = {
.info = { .info = {
...@@ -580,7 +581,7 @@ static int br_mdb_replay_one(struct notifier_block *nb, struct net_device *dev, ...@@ -580,7 +581,7 @@ static int br_mdb_replay_one(struct notifier_block *nb, struct net_device *dev,
}; };
int err; int err;
err = nb->notifier_call(nb, SWITCHDEV_PORT_OBJ_ADD, &obj_info); err = nb->notifier_call(nb, action, &obj_info);
return notifier_to_errno(err); return notifier_to_errno(err);
} }
...@@ -604,12 +605,13 @@ static int br_mdb_queue_one(struct list_head *mdb_list, ...@@ -604,12 +605,13 @@ static int br_mdb_queue_one(struct list_head *mdb_list,
} }
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev, int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
const void *ctx, struct notifier_block *nb, const void *ctx, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
const struct net_bridge_mdb_entry *mp; const struct net_bridge_mdb_entry *mp;
struct switchdev_obj *obj, *tmp; struct switchdev_obj *obj, *tmp;
struct net_bridge *br; struct net_bridge *br;
unsigned long action;
LIST_HEAD(mdb_list); LIST_HEAD(mdb_list);
int err = 0; int err = 0;
...@@ -664,9 +666,14 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev, ...@@ -664,9 +666,14 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
rcu_read_unlock(); rcu_read_unlock();
if (adding)
action = SWITCHDEV_PORT_OBJ_ADD;
else
action = SWITCHDEV_PORT_OBJ_DEL;
list_for_each_entry(obj, &mdb_list, list) { list_for_each_entry(obj, &mdb_list, list) {
err = br_mdb_replay_one(nb, dev, SWITCHDEV_OBJ_PORT_MDB(obj), err = br_mdb_replay_one(nb, dev, SWITCHDEV_OBJ_PORT_MDB(obj),
ctx, extack); action, ctx, extack);
if (err) if (err)
goto out_free_mdb; goto out_free_mdb;
} }
......
...@@ -1807,7 +1807,8 @@ void br_vlan_notify(const struct net_bridge *br, ...@@ -1807,7 +1807,8 @@ void br_vlan_notify(const struct net_bridge *br,
static int br_vlan_replay_one(struct notifier_block *nb, static int br_vlan_replay_one(struct notifier_block *nb,
struct net_device *dev, struct net_device *dev,
struct switchdev_obj_port_vlan *vlan, struct switchdev_obj_port_vlan *vlan,
const void *ctx, struct netlink_ext_ack *extack) const void *ctx, unsigned long action,
struct netlink_ext_ack *extack)
{ {
struct switchdev_notifier_port_obj_info obj_info = { struct switchdev_notifier_port_obj_info obj_info = {
.info = { .info = {
...@@ -1819,18 +1820,19 @@ static int br_vlan_replay_one(struct notifier_block *nb, ...@@ -1819,18 +1820,19 @@ static int br_vlan_replay_one(struct notifier_block *nb,
}; };
int err; int err;
err = nb->notifier_call(nb, SWITCHDEV_PORT_OBJ_ADD, &obj_info); err = nb->notifier_call(nb, action, &obj_info);
return notifier_to_errno(err); return notifier_to_errno(err);
} }
int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
const void *ctx, struct notifier_block *nb, const void *ctx, bool adding, struct notifier_block *nb,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;
struct net_bridge_port *p; struct net_bridge_port *p;
struct net_bridge *br; struct net_bridge *br;
unsigned long action;
int err = 0; int err = 0;
u16 pvid; u16 pvid;
...@@ -1857,6 +1859,11 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, ...@@ -1857,6 +1859,11 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
if (!vg) if (!vg)
return 0; return 0;
if (adding)
action = SWITCHDEV_PORT_OBJ_ADD;
else
action = SWITCHDEV_PORT_OBJ_DEL;
pvid = br_get_pvid(vg); pvid = br_get_pvid(vg);
list_for_each_entry(v, &vg->vlan_list, vlist) { list_for_each_entry(v, &vg->vlan_list, vlist) {
...@@ -1870,7 +1877,7 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, ...@@ -1870,7 +1877,7 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
if (!br_vlan_should_use(v)) if (!br_vlan_should_use(v))
continue; continue;
err = br_vlan_replay_one(nb, dev, &vlan, ctx, extack); err = br_vlan_replay_one(nb, dev, &vlan, ctx, action, extack);
if (err) if (err)
return err; return err;
} }
......
...@@ -194,19 +194,18 @@ static int dsa_port_switchdev_sync(struct dsa_port *dp, ...@@ -194,19 +194,18 @@ static int dsa_port_switchdev_sync(struct dsa_port *dp,
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
err = br_mdb_replay(br, brport_dev, dp, err = br_mdb_replay(br, brport_dev, dp, true,
&dsa_slave_switchdev_blocking_notifier, &dsa_slave_switchdev_blocking_notifier, extack);
extack);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
err = br_fdb_replay(br, brport_dev, dp, &dsa_slave_switchdev_notifier); err = br_fdb_replay(br, brport_dev, dp, true,
&dsa_slave_switchdev_notifier);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
err = br_vlan_replay(br, brport_dev, dp, err = br_vlan_replay(br, brport_dev, dp, true,
&dsa_slave_switchdev_blocking_notifier, &dsa_slave_switchdev_blocking_notifier, extack);
extack);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
return err; return err;
......
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