diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 05de37c3b64c7..f3d12d0714fb5 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1625,7 +1625,7 @@ static int dpaa2_switch_port_bridge_flags(struct net_device *netdev, return 0; } -static int dpaa2_switch_port_attr_set(struct net_device *netdev, +static int dpaa2_switch_port_attr_set(struct net_device *netdev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index 74b81b4fbb975..0b3e8f2db2949 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -708,7 +708,7 @@ static int prestera_port_attr_stp_state_set(struct prestera_port *port, return err; } -static int prestera_port_obj_attr_set(struct net_device *dev, +static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { @@ -1040,7 +1040,7 @@ static int prestera_port_vlans_add(struct prestera_port *port, flag_pvid, extack); } -static int prestera_port_obj_add(struct net_device *dev, +static int prestera_port_obj_add(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { @@ -1078,7 +1078,7 @@ static int prestera_port_vlans_del(struct prestera_port *port, return 0; } -static int prestera_port_obj_del(struct net_device *dev, +static int prestera_port_obj_del(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj) { struct prestera_port *port = netdev_priv(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c index 7f5efc1b43923..3c0032c9647c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c @@ -76,6 +76,7 @@ static int mlx5_esw_bridge_switchdev_port_event(struct notifier_block *nb, } static int mlx5_esw_bridge_port_obj_add(struct net_device *dev, + const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { @@ -107,6 +108,7 @@ static int mlx5_esw_bridge_port_obj_add(struct net_device *dev, } static int mlx5_esw_bridge_port_obj_del(struct net_device *dev, + const void *ctx, const struct switchdev_obj *obj) { const struct switchdev_obj_port_vlan *vlan; @@ -136,6 +138,7 @@ static int mlx5_esw_bridge_port_obj_del(struct net_device *dev, } static int mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev, + const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0cfba29868412..c5ef9aa64efe3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -898,7 +898,7 @@ mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, return 0; } -static int mlxsw_sp_port_attr_set(struct net_device *dev, +static int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { @@ -1766,7 +1766,7 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, } } -static int mlxsw_sp_port_obj_add(struct net_device *dev, +static int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { @@ -1916,7 +1916,7 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, } } -static int mlxsw_sp_port_obj_del(struct net_device *dev, +static int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index 19c7cb795b4b3..246eba711f150 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -65,7 +65,7 @@ static void sparx5_port_attr_ageing_set(struct sparx5_port *port, sparx5_set_ageing(port->sparx5, ageing_time); } -static int sparx5_port_attr_set(struct net_device *dev, +static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index aad33d22c33f6..3e89e34f86d59 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -939,7 +939,7 @@ static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) ANA_PORT_CPU_FWD_CFG, port); } -static int ocelot_port_attr_set(struct net_device *dev, +static int ocelot_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { @@ -948,6 +948,9 @@ static int ocelot_port_attr_set(struct net_device *dev, int port = priv->chip_port; int err = 0; + if (ctx && ctx != priv) + return 0; + switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: ocelot_port_attr_stp_state_set(ocelot, port, attr->u.stp_state); @@ -1058,12 +1061,16 @@ ocelot_port_obj_mrp_del_ring_role(struct net_device *dev, return ocelot_mrp_del_ring_role(ocelot, port, mrp); } -static int ocelot_port_obj_add(struct net_device *dev, +static int ocelot_port_obj_add(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { + struct ocelot_port_private *priv = netdev_priv(dev); int ret = 0; + if (ctx && ctx != priv) + return 0; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: ret = ocelot_port_obj_add_vlan(dev, @@ -1086,11 +1093,15 @@ static int ocelot_port_obj_add(struct net_device *dev, return ret; } -static int ocelot_port_obj_del(struct net_device *dev, +static int ocelot_port_obj_del(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj) { + struct ocelot_port_private *priv = netdev_priv(dev); int ret = 0; + if (ctx && ctx != priv) + return 0; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: ret = ocelot_vlan_vid_del(dev, @@ -1143,10 +1154,14 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port, struct net_device *bridge_dev, struct netlink_ext_ack *extack) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_port_private *priv; clock_t ageing_time; u8 stp_state; int err; + priv = container_of(ocelot_port, struct ocelot_port_private, port); + ocelot_inherit_brport_flags(ocelot, port, brport_dev); stp_state = br_port_get_stp_state(brport_dev); @@ -1160,16 +1175,12 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port, ageing_time = br_get_ageing_time(bridge_dev); ocelot_port_attr_ageing_set(ocelot, port, ageing_time); - err = br_mdb_replay(bridge_dev, brport_dev, + err = br_mdb_replay(bridge_dev, brport_dev, priv, true, &ocelot_switchdev_blocking_nb, extack); if (err && err != -EOPNOTSUPP) return err; - err = br_fdb_replay(bridge_dev, brport_dev, &ocelot_switchdev_nb); - if (err) - return err; - - err = br_vlan_replay(bridge_dev, brport_dev, + err = br_vlan_replay(bridge_dev, brport_dev, priv, true, &ocelot_switchdev_blocking_nb, extack); if (err && err != -EOPNOTSUPP) return err; diff --git a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c index 23cfb91e9c4d4..9c29b363e9aec 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c +++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c @@ -84,7 +84,7 @@ static int am65_cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, return 0; } -static int am65_cpsw_port_attr_set(struct net_device *ndev, +static int am65_cpsw_port_attr_set(struct net_device *ndev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { @@ -302,7 +302,7 @@ static int am65_cpsw_port_mdb_del(struct am65_cpsw_port *port, return 0; } -static int am65_cpsw_port_obj_add(struct net_device *ndev, +static int am65_cpsw_port_obj_add(struct net_device *ndev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { @@ -329,7 +329,7 @@ static int am65_cpsw_port_obj_add(struct net_device *ndev, return err; } -static int am65_cpsw_port_obj_del(struct net_device *ndev, +static int am65_cpsw_port_obj_del(struct net_device *ndev, const void *ctx, const struct switchdev_obj *obj) { struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c index 05a64fb7a04f2..f7fb6e17dadd5 100644 --- a/drivers/net/ethernet/ti/cpsw_switchdev.c +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c @@ -86,7 +86,7 @@ static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, return 0; } -static int cpsw_port_attr_set(struct net_device *ndev, +static int cpsw_port_attr_set(struct net_device *ndev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { @@ -310,7 +310,7 @@ static int cpsw_port_mdb_del(struct cpsw_priv *priv, return err; } -static int cpsw_port_obj_add(struct net_device *ndev, +static int cpsw_port_obj_add(struct net_device *ndev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { @@ -338,7 +338,7 @@ static int cpsw_port_obj_add(struct net_device *ndev, return err; } -static int cpsw_port_obj_del(struct net_device *ndev, +static int cpsw_port_obj_del(struct net_device *ndev, const void *ctx, const struct switchdev_obj *obj) { struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 12e9a32dbca07..b651c5e32a28a 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -71,7 +71,8 @@ bool br_multicast_has_router_adjacent(struct net_device *dev, int proto); bool br_multicast_enabled(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, - struct notifier_block *nb, struct netlink_ext_ack *extack); + const void *ctx, bool adding, struct notifier_block *nb, + struct netlink_ext_ack *extack); #else static inline int br_multicast_list_adjacent(struct net_device *dev, struct list_head *br_ip_list) @@ -103,9 +104,9 @@ static inline bool br_multicast_router(const struct net_device *dev) { return false; } -static inline int br_mdb_replay(struct net_device *br_dev, - struct net_device *dev, - struct notifier_block *nb, +static inline int br_mdb_replay(const struct net_device *br_dev, + const struct net_device *dev, const void *ctx, + bool adding, struct notifier_block *nb, struct netlink_ext_ack *extack) { return -EOPNOTSUPP; @@ -120,7 +121,8 @@ 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, struct bridge_vlan_info *p_vinfo); int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, - struct notifier_block *nb, struct netlink_ext_ack *extack); + const void *ctx, bool adding, struct notifier_block *nb, + struct netlink_ext_ack *extack); #else static inline bool br_vlan_enabled(const struct net_device *dev) { @@ -149,8 +151,8 @@ 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, - struct net_device *dev, - struct notifier_block *nb, + struct net_device *dev, const void *ctx, + bool adding, struct notifier_block *nb, struct netlink_ext_ack *extack) { return -EOPNOTSUPP; @@ -164,9 +166,9 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev, void br_fdb_clear_offload(const struct net_device *dev, u16 vid); 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); -clock_t br_get_ageing_time(struct net_device *br_dev); -int br_fdb_replay(struct net_device *br_dev, struct net_device *dev, - struct notifier_block *nb); +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, + const void *ctx, bool adding, struct notifier_block *nb); #else static inline struct net_device * br_fdb_find_port(const struct net_device *br_dev, @@ -191,14 +193,14 @@ static inline u8 br_port_get_stp_state(const struct net_device *dev) return BR_STATE_DISABLED; } -static inline clock_t br_get_ageing_time(struct net_device *br_dev) +static inline clock_t br_get_ageing_time(const struct net_device *br_dev) { return 0; } -static inline int br_fdb_replay(struct net_device *br_dev, - struct net_device *dev, - struct notifier_block *nb) +static inline int br_fdb_replay(const struct net_device *br_dev, + const struct net_device *dev, const void *ctx, + bool adding, struct notifier_block *nb) { return -EOPNOTSUPP; } diff --git a/include/net/switchdev.h b/include/net/switchdev.h index f1a5a9a3634d9..e4cac9218ce18 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -202,6 +202,7 @@ enum switchdev_notifier_type { struct switchdev_notifier_info { struct net_device *dev; struct netlink_ext_ack *extack; + const void *ctx; }; struct switchdev_notifier_fdb_info { @@ -268,19 +269,19 @@ void switchdev_port_fwd_mark_set(struct net_device *dev, int switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*add_cb)(struct net_device *dev, + int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)); int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*del_cb)(struct net_device *dev, + int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)); int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), - int (*set_cb)(struct net_device *dev, + int (*set_cb)(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack)); #else @@ -352,7 +353,7 @@ static inline int switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*add_cb)(struct net_device *dev, + int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)) { @@ -363,7 +364,7 @@ static inline int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*del_cb)(struct net_device *dev, + int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)) { return 0; @@ -373,7 +374,7 @@ static inline int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), - int (*set_cb)(struct net_device *dev, + int (*set_cb)(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack)) { diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 698b79747d32e..16f9434fdb5d3 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -727,8 +727,9 @@ static inline size_t fdb_nlmsg_size(void) } static int br_fdb_replay_one(struct notifier_block *nb, - struct net_bridge_fdb_entry *fdb, - struct net_device *dev) + const struct net_bridge_fdb_entry *fdb, + struct net_device *dev, unsigned long action, + const void *ctx) { struct switchdev_notifier_fdb_info item; int err; @@ -737,17 +738,20 @@ static int br_fdb_replay_one(struct notifier_block *nb, item.vid = fdb->key.vlan_id; item.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); item.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags); + item.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); item.info.dev = dev; + 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); } -int br_fdb_replay(struct net_device *br_dev, struct net_device *dev, - struct notifier_block *nb) +int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev, + const void *ctx, bool adding, struct notifier_block *nb) { struct net_bridge_fdb_entry *fdb; struct net_bridge *br; + unsigned long action; int err = 0; if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev)) @@ -755,17 +759,22 @@ int br_fdb_replay(struct net_device *br_dev, struct net_device *dev, br = netdev_priv(br_dev); + if (adding) + action = SWITCHDEV_FDB_ADD_TO_DEVICE; + else + action = SWITCHDEV_FDB_DEL_TO_DEVICE; + rcu_read_lock(); hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { - struct net_bridge_port *dst = READ_ONCE(fdb->dst); + const struct net_bridge_port *dst = READ_ONCE(fdb->dst); struct net_device *dst_dev; dst_dev = dst ? dst->dev : br->dev; if (dst_dev != br_dev && dst_dev != dev) continue; - err = br_fdb_replay_one(nb, fdb, dst_dev); + err = br_fdb_replay_one(nb, fdb, dst_dev, action, ctx); if (err) break; } diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 3f839a8cc9fb2..17a720b4473f0 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -567,19 +567,21 @@ 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, - struct switchdev_obj_port_mdb *mdb, + const struct switchdev_obj_port_mdb *mdb, + unsigned long action, const void *ctx, struct netlink_ext_ack *extack) { struct switchdev_notifier_port_obj_info obj_info = { .info = { .dev = dev, .extack = extack, + .ctx = ctx, }, .obj = &mdb->obj, }; 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); } @@ -603,11 +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, - struct notifier_block *nb, struct netlink_ext_ack *extack) + const void *ctx, bool adding, struct notifier_block *nb, + struct netlink_ext_ack *extack) { - struct net_bridge_mdb_entry *mp; + const struct net_bridge_mdb_entry *mp; struct switchdev_obj *obj, *tmp; struct net_bridge *br; + unsigned long action; LIST_HEAD(mdb_list); int err = 0; @@ -632,8 +636,8 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev, rcu_read_lock(); hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { - struct net_bridge_port_group __rcu **pp; - struct net_bridge_port_group *p; + struct net_bridge_port_group __rcu * const *pp; + const struct net_bridge_port_group *p; if (mp->host_joined) { err = br_mdb_queue_one(&mdb_list, @@ -662,9 +666,14 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev, rcu_read_unlock(); + if (adding) + action = SWITCHDEV_PORT_OBJ_ADD; + else + action = SWITCHDEV_PORT_OBJ_DEL; + list_for_each_entry(obj, &mdb_list, list) { err = br_mdb_replay_one(nb, dev, SWITCHDEV_OBJ_PORT_MDB(obj), - extack); + action, ctx, extack); if (err) goto out_free_mdb; } diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 3dafb6143cff3..1d80f34a139c3 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -639,9 +639,9 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) return 0; } -clock_t br_get_ageing_time(struct net_device *br_dev) +clock_t br_get_ageing_time(const struct net_device *br_dev) { - struct net_bridge *br; + const struct net_bridge *br; if (!netif_is_bridge_master(br_dev)) return 0; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 8789a57af543a..a08e9f1930093 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1807,28 +1807,32 @@ void br_vlan_notify(const struct net_bridge *br, static int br_vlan_replay_one(struct notifier_block *nb, struct net_device *dev, struct switchdev_obj_port_vlan *vlan, + const void *ctx, unsigned long action, struct netlink_ext_ack *extack) { struct switchdev_notifier_port_obj_info obj_info = { .info = { .dev = dev, .extack = extack, + .ctx = ctx, }, .obj = &vlan->obj, }; 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); } int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, - struct notifier_block *nb, struct netlink_ext_ack *extack) + const void *ctx, bool adding, struct notifier_block *nb, + struct netlink_ext_ack *extack) { struct net_bridge_vlan_group *vg; struct net_bridge_vlan *v; struct net_bridge_port *p; struct net_bridge *br; + unsigned long action; int err = 0; u16 pvid; @@ -1855,6 +1859,11 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, if (!vg) return 0; + if (adding) + action = SWITCHDEV_PORT_OBJ_ADD; + else + action = SWITCHDEV_PORT_OBJ_DEL; + pvid = br_get_pvid(vg); list_for_each_entry(v, &vg->vlan_list, vlist) { @@ -1868,7 +1877,7 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev, if (!br_vlan_should_use(v)) continue; - err = br_vlan_replay_one(nb, dev, &vlan, extack); + err = br_vlan_replay_one(nb, dev, &vlan, ctx, action, extack); if (err) return err; } diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index b0811253d1018..c8712942002fd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -188,12 +188,16 @@ void dsa_port_disable_rt(struct dsa_port *dp); void dsa_port_disable(struct dsa_port *dp); int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, struct netlink_ext_ack *extack); +int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br, + struct netlink_ext_ack *extack); void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); int dsa_port_lag_change(struct dsa_port *dp, struct netdev_lag_lower_state_info *linfo); int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, struct netdev_lag_upper_info *uinfo, struct netlink_ext_ack *extack); +int dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev, + struct netlink_ext_ack *extack); void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct netlink_ext_ack *extack); diff --git a/net/dsa/port.c b/net/dsa/port.c index 5c93f1e1a03d6..46089dd2b2ec3 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -194,26 +194,51 @@ static int dsa_port_switchdev_sync(struct dsa_port *dp, if (err && err != -EOPNOTSUPP) return err; - err = br_mdb_replay(br, brport_dev, - &dsa_slave_switchdev_blocking_notifier, - extack); + err = br_mdb_replay(br, brport_dev, dp, true, + &dsa_slave_switchdev_blocking_notifier, extack); if (err && err != -EOPNOTSUPP) return err; - err = br_fdb_replay(br, brport_dev, &dsa_slave_switchdev_notifier); + err = br_fdb_replay(br, brport_dev, dp, true, + &dsa_slave_switchdev_notifier); if (err && err != -EOPNOTSUPP) return err; - err = br_vlan_replay(br, brport_dev, - &dsa_slave_switchdev_blocking_notifier, - extack); + err = br_vlan_replay(br, brport_dev, dp, true, + &dsa_slave_switchdev_blocking_notifier, extack); if (err && err != -EOPNOTSUPP) return err; return 0; } -static void dsa_port_switchdev_unsync(struct dsa_port *dp) +static int dsa_port_switchdev_unsync_objs(struct dsa_port *dp, + struct net_device *br, + struct netlink_ext_ack *extack) +{ + struct net_device *brport_dev = dsa_port_to_bridge_port(dp); + int err; + + /* Delete the switchdev objects left on this port */ + err = br_mdb_replay(br, brport_dev, dp, false, + &dsa_slave_switchdev_blocking_notifier, extack); + if (err && err != -EOPNOTSUPP) + return err; + + err = br_fdb_replay(br, brport_dev, dp, false, + &dsa_slave_switchdev_notifier); + if (err && err != -EOPNOTSUPP) + return err; + + err = br_vlan_replay(br, brport_dev, dp, false, + &dsa_slave_switchdev_blocking_notifier, extack); + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + +static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp) { /* Configure the port for standalone mode (no address learning, * flood everything). @@ -279,6 +304,12 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, return err; } +int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br, + struct netlink_ext_ack *extack) +{ + return dsa_port_switchdev_unsync_objs(dp, br, extack); +} + void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) { struct dsa_notifier_bridge_info info = { @@ -298,7 +329,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) if (err) pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); - dsa_port_switchdev_unsync(dp); + dsa_port_switchdev_unsync_attrs(dp); } int dsa_port_lag_change(struct dsa_port *dp, @@ -366,6 +397,15 @@ int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, return err; } +int dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag, + struct netlink_ext_ack *extack) +{ + if (dp->bridge_dev) + return dsa_port_pre_bridge_leave(dp, dp->bridge_dev, extack); + + return 0; +} + void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) { struct dsa_notifier_lag_info info = { diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5e668e529575d..898ed9cf756fd 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -271,13 +271,16 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return phylink_mii_ioctl(p->dp->pl, ifr, cmd); } -static int dsa_slave_port_attr_set(struct net_device *dev, +static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) { struct dsa_port *dp = dsa_slave_to_port(dev); int ret; + if (ctx && ctx != dp) + return 0; + switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) @@ -394,13 +397,16 @@ static int dsa_slave_vlan_add(struct net_device *dev, return vlan_vid_add(master, htons(ETH_P_8021Q), vlan.vid); } -static int dsa_slave_port_obj_add(struct net_device *dev, +static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { struct dsa_port *dp = dsa_slave_to_port(dev); int err; + if (ctx && ctx != dp) + return 0; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_MDB: if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) @@ -469,12 +475,15 @@ static int dsa_slave_vlan_del(struct net_device *dev, return 0; } -static int dsa_slave_port_obj_del(struct net_device *dev, +static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj) { struct dsa_port *dp = dsa_slave_to_port(dev); int err; + if (ctx && ctx != dp) + return 0; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_MDB: if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) @@ -2068,6 +2077,26 @@ static int dsa_slave_changeupper(struct net_device *dev, return err; } +static int dsa_slave_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct netlink_ext_ack *extack; + int err = 0; + + extack = netdev_notifier_info_to_extack(&info->info); + + if (netif_is_bridge_master(info->upper_dev) && !info->linking) + err = dsa_port_pre_bridge_leave(dp, info->upper_dev, extack); + else if (netif_is_lag_master(info->upper_dev) && !info->linking) + err = dsa_port_pre_lag_leave(dp, info->upper_dev, extack); + /* dsa_port_pre_hsr_leave is not yet necessary since hsr cannot be + * meaningfully enslaved to a bridge yet + */ + + return notifier_from_errno(err); +} + static int dsa_slave_lag_changeupper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) @@ -2094,6 +2123,35 @@ dsa_slave_lag_changeupper(struct net_device *dev, return err; } +/* Same as dsa_slave_lag_changeupper() except that it calls + * dsa_slave_prechangeupper() + */ +static int +dsa_slave_lag_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct net_device *lower; + struct list_head *iter; + int err = NOTIFY_DONE; + struct dsa_port *dp; + + netdev_for_each_lower_dev(dev, lower, iter) { + if (!dsa_slave_dev_check(lower)) + continue; + + dp = dsa_slave_to_port(lower); + if (!dp->lag_dev) + /* Software LAG */ + continue; + + err = dsa_slave_prechangeupper(lower, info); + if (notifier_to_errno(err)) + break; + } + + return err; +} + static int dsa_prevent_bridging_8021q_upper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) @@ -2157,6 +2215,32 @@ dsa_slave_check_8021q_upper(struct net_device *dev, return NOTIFY_DONE; } +static int +dsa_slave_prechangeupper_sanity_check(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct dsa_switch *ds; + struct dsa_port *dp; + int err; + + if (!dsa_slave_dev_check(dev)) + return dsa_prevent_bridging_8021q_upper(dev, info); + + dp = dsa_slave_to_port(dev); + ds = dp->ds; + + if (ds->ops->port_prechangeupper) { + err = ds->ops->port_prechangeupper(ds, dp->index, info); + if (err) + return notifier_from_errno(err); + } + + if (is_vlan_dev(info->upper_dev)) + return dsa_slave_check_8021q_upper(dev, info); + + return NOTIFY_DONE; +} + static int dsa_slave_netdevice_event(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -2165,24 +2249,18 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, switch (event) { case NETDEV_PRECHANGEUPPER: { struct netdev_notifier_changeupper_info *info = ptr; - struct dsa_switch *ds; - struct dsa_port *dp; int err; - if (!dsa_slave_dev_check(dev)) - return dsa_prevent_bridging_8021q_upper(dev, ptr); + err = dsa_slave_prechangeupper_sanity_check(dev, info); + if (err != NOTIFY_DONE) + return err; - dp = dsa_slave_to_port(dev); - ds = dp->ds; + if (dsa_slave_dev_check(dev)) + return dsa_slave_prechangeupper(dev, ptr); - if (ds->ops->port_prechangeupper) { - err = ds->ops->port_prechangeupper(ds, dp->index, info); - if (err) - return notifier_from_errno(err); - } + if (netif_is_lag_master(dev)) + return dsa_slave_lag_prechangeupper(dev, ptr); - if (is_vlan_dev(info->upper_dev)) - return dsa_slave_check_8021q_upper(dev, ptr); break; } case NETDEV_CHANGEUPPER: diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 89a36db47ab46..070698dd19bcf 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -381,19 +381,20 @@ EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers); static int __switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*add_cb)(struct net_device *dev, + int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)) { + struct switchdev_notifier_info *info = &port_obj_info->info; struct netlink_ext_ack *extack; struct net_device *lower_dev; struct list_head *iter; int err = -EOPNOTSUPP; - extack = switchdev_notifier_info_to_extack(&port_obj_info->info); + extack = switchdev_notifier_info_to_extack(info); if (check_cb(dev)) { - err = add_cb(dev, port_obj_info->obj, extack); + err = add_cb(dev, info->ctx, port_obj_info->obj, extack); if (err != -EOPNOTSUPP) port_obj_info->handled = true; return err; @@ -422,7 +423,7 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev, int switchdev_handle_port_obj_add(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*add_cb)(struct net_device *dev, + int (*add_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack)) { @@ -439,15 +440,16 @@ EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add); static int __switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*del_cb)(struct net_device *dev, + int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)) { + struct switchdev_notifier_info *info = &port_obj_info->info; struct net_device *lower_dev; struct list_head *iter; int err = -EOPNOTSUPP; if (check_cb(dev)) { - err = del_cb(dev, port_obj_info->obj); + err = del_cb(dev, info->ctx, port_obj_info->obj); if (err != -EOPNOTSUPP) port_obj_info->handled = true; return err; @@ -476,7 +478,7 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev, int switchdev_handle_port_obj_del(struct net_device *dev, struct switchdev_notifier_port_obj_info *port_obj_info, bool (*check_cb)(const struct net_device *dev), - int (*del_cb)(struct net_device *dev, + int (*del_cb)(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj)) { int err; @@ -492,19 +494,20 @@ EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del); static int __switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), - int (*set_cb)(struct net_device *dev, + int (*set_cb)(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack)) { + struct switchdev_notifier_info *info = &port_attr_info->info; struct netlink_ext_ack *extack; struct net_device *lower_dev; struct list_head *iter; int err = -EOPNOTSUPP; - extack = switchdev_notifier_info_to_extack(&port_attr_info->info); + extack = switchdev_notifier_info_to_extack(info); if (check_cb(dev)) { - err = set_cb(dev, port_attr_info->attr, extack); + err = set_cb(dev, info->ctx, port_attr_info->attr, extack); if (err != -EOPNOTSUPP) port_attr_info->handled = true; return err; @@ -533,7 +536,7 @@ static int __switchdev_handle_port_attr_set(struct net_device *dev, int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), - int (*set_cb)(struct net_device *dev, + int (*set_cb)(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack)) {