diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index dd0096cc3221..d0d63da7f01f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -2253,40 +2253,10 @@ static int dpaa2_switch_port_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
- struct ethsw_port_priv *port_priv = netdev_priv(dev);
- struct ethsw_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
- struct ethsw_core *ethsw = port_priv->ethsw_data;
if (event == SWITCHDEV_PORT_ATTR_SET)
return dpaa2_switch_port_attr_set_event(dev, ptr);
- if (!dpaa2_switch_port_dev_check(dev))
- return NOTIFY_DONE;
-
- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
- if (!switchdev_work)
- return NOTIFY_BAD;
-
- INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
- switchdev_work->dev = dev;
- switchdev_work->event = event;
-
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, fdb_info,
- sizeof(switchdev_work->fdb_info));
- /* Take a reference on the device to avoid being freed. */
- dev_hold(dev);
- break;
- default:
- kfree(switchdev_work);
- return NOTIFY_DONE;
- }
-
- queue_work(ethsw->workqueue, &switchdev_work->work);
-
return NOTIFY_DONE;
}
@@ -2312,6 +2282,34 @@ static int dpaa2_switch_port_obj_event(unsigned long event,
return notifier_from_errno(err);
}
+static int dpaa2_switch_fdb_event(unsigned long event,
+ struct net_device *dev,
+ struct switchdev_notifier_fdb_info *fdb_info)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(dev);
+ struct ethsw_switchdev_event_work *switchdev_work;
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+
+ if (!dpaa2_switch_port_dev_check(dev))
+ return NOTIFY_DONE;
+
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return NOTIFY_BAD;
+
+ INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
+ switchdev_work->dev = dev;
+ switchdev_work->event = event;
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+
+ /* Take a reference on the device to avoid being freed. */
+ dev_hold(dev);
+
+ queue_work(ethsw->workqueue, &switchdev_work->work);
+
+ return NOTIFY_DONE;
+}
+
static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{@@ -2323,6 +2321,9 @@ static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
return dpaa2_switch_port_obj_event(event, dev, ptr);
case SWITCHDEV_PORT_ATTR_SET:
return dpaa2_switch_port_attr_set_event(dev, ptr);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ return dpaa2_switch_fdb_event(event, dev, ptr);
}
return NOTIFY_DONE;
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
index 236b07c42df0..a89cda394685 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
@@ -844,10 +844,6 @@ static int prestera_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
- struct switchdev_notifier_fdb_info *fdb_info;
- struct switchdev_notifier_info *info = ptr;
- struct prestera_fdb_event_work *swdev_work;
- struct net_device *upper;
int err;
if (event == SWITCHDEV_PORT_ATTR_SET) {@@ -857,42 +853,6 @@ static int prestera_switchdev_event(struct notifier_block *unused,
return notifier_from_errno(err);
}
- if (!prestera_netdev_check(dev))
- return NOTIFY_DONE;
-
- upper = netdev_master_upper_dev_get_rcu(dev);
- if (!upper)
- return NOTIFY_DONE;
-
- if (!netif_is_bridge_master(upper))
- return NOTIFY_DONE;
-
- swdev_work = kzalloc(sizeof(*swdev_work), GFP_ATOMIC);
- if (!swdev_work)
- return NOTIFY_BAD;
-
- swdev_work->event = event;
- swdev_work->dev = dev;
-
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- fdb_info = container_of(info,
- struct switchdev_notifier_fdb_info,
- info);
-
- INIT_WORK(&swdev_work->work, prestera_fdb_event_work);
- memcpy(&swdev_work->fdb_info, fdb_info,
- sizeof(swdev_work->fdb_info));
- dev_hold(dev);
- break;
-
- default:
- kfree(swdev_work);
- return NOTIFY_DONE;
- }
-
- queue_work(swdev_wq, &swdev_work->work);
return NOTIFY_DONE;
}
@@ -1089,6 +1049,42 @@ static int prestera_port_obj_del(struct net_device *dev, const void *ctx,
}
}
+static int prestera_switchdev_fdb_event(struct net_device *dev,
+ unsigned long event,
+ struct switchdev_notifier_info *info)
+{
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct prestera_fdb_event_work *swdev_work;
+ struct net_device *upper;
+
+ if (!prestera_netdev_check(dev))
+ return 0;
+
+ upper = netdev_master_upper_dev_get_rcu(dev);
+ if (!upper)
+ return 0;
+
+ if (!netif_is_bridge_master(upper))
+ return 0;
+
+ swdev_work = kzalloc(sizeof(*swdev_work), GFP_ATOMIC);
+ if (!swdev_work)
+ return -ENOMEM;
+
+ swdev_work->event = event;
+ swdev_work->dev = dev;
+
+ fdb_info = container_of(info, struct switchdev_notifier_fdb_info,
+ info);
+
+ INIT_WORK(&swdev_work->work, prestera_fdb_event_work);
+ memcpy(&swdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ dev_hold(dev);
+
+ queue_work(swdev_wq, &swdev_work->work);
+ return 0;
+}
+
static int prestera_switchdev_blk_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{@@ -1111,6 +1107,12 @@ static int prestera_switchdev_blk_event(struct notifier_block *unused,
prestera_netdev_check,
prestera_port_obj_attr_set);
break;
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ rcu_read_lock();
+ err = prestera_switchdev_fdb_event(dev, event, ptr);
+ rcu_read_unlock();
+ break;
default:
err = -EOPNOTSUPP;
}
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 3e11420d8057..06bb8bb8e39e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
@@ -276,6 +276,51 @@ mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
return err;
}
+static struct mlx5_bridge_switchdev_fdb_work *
+mlx5_esw_bridge_init_switchdev_fdb_work(struct net_device *dev, bool add,
+ struct switchdev_notifier_fdb_info *fdb_info,
+ struct mlx5_esw_bridge_offloads *br_offloads);
+
+static int
+mlx5_esw_bridge_fdb_event(struct net_device *dev, unsigned long event,
+ struct switchdev_notifier_info *info,
+ struct mlx5_esw_bridge_offloads *br_offloads)
+{
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct mlx5_bridge_switchdev_fdb_work *work;
+ struct mlx5_eswitch *esw = br_offloads->esw;
+ u16 vport_num, esw_owner_vhca_id;
+ struct net_device *upper, *rep;
+
+ upper = netdev_master_upper_dev_get_rcu(dev);
+ if (!upper)
+ return 0;
+ if (!netif_is_bridge_master(upper))
+ return 0;
+
+ rep = mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, esw,
+ &vport_num,
+ &esw_owner_vhca_id);
+ if (!rep)
+ return 0;
+
+ fdb_info = container_of(info, struct switchdev_notifier_fdb_info, info);
+
+ work = mlx5_esw_bridge_init_switchdev_fdb_work(dev,
+ event == SWITCHDEV_FDB_ADD_TO_DEVICE,
+ fdb_info,
+ br_offloads);
+ if (IS_ERR(work)) {
+ WARN_ONCE(1, "Failed to init switchdev work, err=%ld",
+ PTR_ERR(work));
+ return PTR_ERR(work);
+ }
+
+ queue_work(br_offloads->wq, &work->work);
+
+ return 0;
+}
+
static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
unsigned long event, void *ptr)
{@@ -295,6 +340,12 @@ static int mlx5_esw_bridge_event_blocking(struct notifier_block *nb,
case SWITCHDEV_PORT_ATTR_SET:
err = mlx5_esw_bridge_port_obj_attr_set(dev, ptr, br_offloads);
break;
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ rcu_read_lock();
+ err = mlx5_esw_bridge_fdb_event(dev, event, ptr, br_offloads);
+ rcu_read_unlock();
+ break;
default:
err = 0;
}
@@ -405,9 +456,7 @@ static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
/* only handle the event on peers */
if (mlx5_esw_bridge_is_local(dev, rep, esw))
break;
- fallthrough;
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
+
fdb_info = container_of(info,
struct switchdev_notifier_fdb_info,
info);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 78e5059beafa..fbaed9de3929 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -3246,8 +3246,6 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
switchdev_work->event = event;
switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
case SWITCHDEV_FDB_ADD_TO_BRIDGE:
case SWITCHDEV_FDB_DEL_TO_BRIDGE:
fdb_info = container_of(info,@@ -3506,6 +3504,45 @@ mlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev,
}
}
+static int mlxsw_sp_switchdev_fdb_event(struct net_device *dev, unsigned long event,
+ struct switchdev_notifier_info *info)
+{
+ struct mlxsw_sp_switchdev_event_work *switchdev_work;
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct net_device *br_dev;
+
+ /* Tunnel devices are not our uppers, so check their master instead */
+ br_dev = netdev_master_upper_dev_get_rcu(dev);
+ if (!br_dev)
+ return 0;
+ if (!netif_is_bridge_master(br_dev))
+ return 0;
+ if (!mlxsw_sp_port_dev_lower_find_rcu(br_dev))
+ return 0;
+
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return -ENOMEM;
+
+ switchdev_work->dev = dev;
+ switchdev_work->event = event;
+
+ fdb_info = container_of(info, struct switchdev_notifier_fdb_info,
+ info);
+ INIT_WORK(&switchdev_work->work,
+ mlxsw_sp_switchdev_bridge_fdb_event_work);
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ /* Take a reference on the device. This can be either
+ * upper device containig mlxsw_sp_port or just a
+ * mlxsw_sp_port
+ */
+ dev_hold(dev);
+
+ mlxsw_core_schedule_work(&switchdev_work->work);
+
+ return 0;
+}
+
static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{@@ -3534,6 +3571,12 @@ static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
mlxsw_sp_port_dev_check,
mlxsw_sp_port_attr_set);
return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ rcu_read_lock();
+ err = mlxsw_sp_switchdev_fdb_event(dev, event, ptr);
+ rcu_read_unlock();
+ return notifier_from_errno(err);
}
return NOTIFY_DONE;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
index 5c5eb557a19c..0d19f2be0895 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
@@ -267,9 +267,6 @@ static int sparx5_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
- struct sparx5_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info;
- struct switchdev_notifier_info *info = ptr;
int err;
switch (event) {@@ -278,27 +275,6 @@ static int sparx5_switchdev_event(struct notifier_block *unused,
sparx5_netdevice_check,
sparx5_port_attr_set);
return notifier_from_errno(err);
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- fallthrough;
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
- if (!switchdev_work)
- return NOTIFY_BAD;
-
- switchdev_work->dev = dev;
- switchdev_work->event = event;
-
- fdb_info = container_of(info,
- struct switchdev_notifier_fdb_info,
- info);
- INIT_WORK(&switchdev_work->work,
- sparx5_switchdev_bridge_fdb_event_work);
- memcpy(&switchdev_work->fdb_info, fdb_info,
- sizeof(switchdev_work->fdb_info));
- dev_hold(dev);
-
- sparx5_schedule_work(&switchdev_work->work);
- break;
}
return NOTIFY_DONE;
@@ -449,6 +425,36 @@ static int sparx5_handle_port_obj_del(struct net_device *dev,
return err;
}
+static int sparx5_switchdev_fdb_event(struct net_device *dev, unsigned long event,
+ struct switchdev_notifier_info *info)
+{
+ struct sparx5_switchdev_event_work *switchdev_work;
+ struct switchdev_notifier_fdb_info *fdb_info;
+
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return -ENOMEM;
+
+ switchdev_work->dev = dev;
+ switchdev_work->event = event;
+
+ fdb_info = container_of(info,
+ struct switchdev_notifier_fdb_info,
+ info);
+ INIT_WORK(&switchdev_work->work,
+ sparx5_switchdev_bridge_fdb_event_work);
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ dev_hold(dev);
+
+ sparx5_schedule_work(&switchdev_work->work);
+
+ return 0;
+
+err_addr_alloc:
+ kfree(switchdev_work);
+ return -ENOMEM;
+}
+
static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
unsigned long event,
void *ptr)@@ -468,6 +474,10 @@ static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
sparx5_netdevice_check,
sparx5_port_attr_set);
return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ err = sparx5_switchdev_fdb_event(dev, event, ptr);
+ return notifier_from_errno(err);
}
return NOTIFY_DONE;
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index d490d006cc98..f6facbf4dc2c 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -2766,9 +2766,6 @@ static int rocker_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
- struct rocker_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
- struct rocker_port *rocker_port;
if (!rocker_port_dev_check(dev))
return NOTIFY_DONE;@@ -2776,30 +2773,6 @@ static int rocker_switchdev_event(struct notifier_block *unused,
if (event == SWITCHDEV_PORT_ATTR_SET)
return rocker_switchdev_port_attr_set_event(dev, ptr);
- rocker_port = netdev_priv(dev);
- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
- if (WARN_ON(!switchdev_work))
- return NOTIFY_BAD;
-
- INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work);
- switchdev_work->rocker_port = rocker_port;
- switchdev_work->event = event;
-
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, fdb_info,
- sizeof(switchdev_work->fdb_info));
- /* Take a reference on the rocker device */
- dev_hold(dev);
- break;
- default:
- kfree(switchdev_work);
- return NOTIFY_DONE;
- }
-
- queue_work(rocker_port->rocker->rocker_owq,
- &switchdev_work->work);
return NOTIFY_DONE;
}
@@ -2822,6 +2795,32 @@ rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
return notifier_from_errno(err);
}
+static int
+rocker_switchdev_fdb_event(unsigned long event, struct net_device *dev,
+ struct switchdev_notifier_fdb_info *fdb_info)
+{
+ struct rocker_switchdev_event_work *switchdev_work;
+ struct rocker_port *rocker_port;
+
+ rocker_port = netdev_priv(dev);
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (WARN_ON(!switchdev_work))
+ return NOTIFY_BAD;
+
+ INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work);
+ switchdev_work->rocker_port = rocker_port;
+ switchdev_work->event = event;
+
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ /* Take a reference on the rocker device */
+ dev_hold(dev);
+
+ queue_work(rocker_port->rocker->rocker_owq,
+ &switchdev_work->work);
+
+ return NOTIFY_DONE;
+}
+
static int rocker_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{@@ -2836,6 +2835,9 @@ static int rocker_switchdev_blocking_event(struct notifier_block *unused,
return rocker_switchdev_port_obj_event(event, dev, ptr);
case SWITCHDEV_PORT_ATTR_SET:
return rocker_switchdev_port_attr_set_event(dev, ptr);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ return rocker_switchdev_fdb_event(event, dev, ptr);
}
return NOTIFY_DONE;
diff --git a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
index 860214e1a8ca..5087d529c93e 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
@@ -423,9 +423,6 @@ static int am65_cpsw_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
- struct am65_cpsw_switchdev_event_work *switchdev_work;
- struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
int err;
if (event == SWITCHDEV_PORT_ATTR_SET) {@@ -435,38 +432,39 @@ static int am65_cpsw_switchdev_event(struct notifier_block *unused,
return notifier_from_errno(err);
}
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block cpsw_switchdev_notifier = {
+ .notifier_call = am65_cpsw_switchdev_event,
+};
+
+static int am65_cpsw_switchdev_fdb_event(struct net_device *ndev,
+ unsigned long event,
+ struct switchdev_notifier_fdb_info *fdb_info)
+{
+ struct am65_cpsw_switchdev_event_work *switchdev_work;
+ struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
+
if (!am65_cpsw_port_dev_check(ndev))
- return NOTIFY_DONE;
+ return 0;
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (WARN_ON(!switchdev_work))
- return NOTIFY_BAD;
+ return -ENOMEM;
INIT_WORK(&switchdev_work->work, am65_cpsw_switchdev_event_work);
switchdev_work->port = port;
switchdev_work->event = event;
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, fdb_info,
- sizeof(switchdev_work->fdb_info));
- dev_hold(ndev);
- break;
- default:
- kfree(switchdev_work);
- return NOTIFY_DONE;
- }
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ dev_hold(ndev);
queue_work(system_long_wq, &switchdev_work->work);
return NOTIFY_DONE;
}
-static struct notifier_block cpsw_switchdev_notifier = {
- .notifier_call = am65_cpsw_switchdev_event,
-};
-
static int am65_cpsw_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{@@ -489,6 +487,10 @@ static int am65_cpsw_switchdev_blocking_event(struct notifier_block *unused,
am65_cpsw_port_dev_check,
am65_cpsw_port_attr_set);
return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ err = am65_cpsw_switchdev_fdb_event(dev, event, ptr);
+ return notifier_from_errno(err);
default:
break;
}
diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c
index 786bb848ddeb..8d463d1283f9 100644
--- a/drivers/net/ethernet/ti/cpsw_switchdev.c
+++ b/drivers/net/ethernet/ti/cpsw_switchdev.c
@@ -433,9 +433,6 @@ static int cpsw_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
- struct cpsw_switchdev_event_work *switchdev_work;
- struct cpsw_priv *priv = netdev_priv(ndev);
int err;
if (event == SWITCHDEV_PORT_ATTR_SET) {@@ -445,38 +442,40 @@ static int cpsw_switchdev_event(struct notifier_block *unused,
return notifier_from_errno(err);
}
- if (!cpsw_port_dev_check(ndev))
- return NOTIFY_DONE;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block cpsw_switchdev_notifier = {
+ .notifier_call = cpsw_switchdev_event,
+};
+
+static int cpsw_switchdev_fdb_event(struct net_device *dev, unsigned long event,
+ struct switchdev_notifier_fdb_info *fdb_info)
+{
+ struct cpsw_switchdev_event_work *switchdev_work;
+ struct cpsw_priv *priv;
+
+ if (!cpsw_port_dev_check(dev))
+ return 0;
+
+ priv = netdev_priv(dev);
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (WARN_ON(!switchdev_work))
- return NOTIFY_BAD;
+ return -ENOMEM;
INIT_WORK(&switchdev_work->work, cpsw_switchdev_event_work);
switchdev_work->priv = priv;
switchdev_work->event = event;
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, fdb_info,
- sizeof(switchdev_work->fdb_info));
- dev_hold(ndev);
- break;
- default:
- kfree(switchdev_work);
- return NOTIFY_DONE;
- }
+ memcpy(&switchdev_work->fdb_info, fdb_info, sizeof(*fdb_info));
+ dev_hold(dev);
queue_work(system_long_wq, &switchdev_work->work);
return NOTIFY_DONE;
}
-static struct notifier_block cpsw_switchdev_notifier = {
- .notifier_call = cpsw_switchdev_event,
-};
-
static int cpsw_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{@@ -499,6 +498,10 @@ static int cpsw_switchdev_blocking_event(struct notifier_block *unused,
cpsw_port_dev_check,
cpsw_port_attr_set);
return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ err = cpsw_switchdev_fdb_event(dev, event, ptr);
+ return notifier_from_errno(err);
default:
break;
}
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index de98f79c11ab..98678dc9d054 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -863,10 +863,15 @@ static int qeth_l2_switchdev_event(struct notifier_block *unused,
event == SWITCHDEV_FDB_DEL_TO_DEVICE))
return NOTIFY_DONE;
+ rcu_read_lock();
+
dstdev = switchdev_notifier_info_to_dev(info);
brdev = netdev_master_upper_dev_get_rcu(dstdev);
- if (!brdev || !netif_is_bridge_master(brdev))
+ if (!brdev || !netif_is_bridge_master(brdev)) {
+ rcu_read_unlock();
return NOTIFY_DONE;
+ }
+
fdb_info = container_of(info,
struct switchdev_notifier_fdb_info,
info);@@ -881,11 +886,15 @@ static int qeth_l2_switchdev_event(struct notifier_block *unused,
fdb_info->addr);
if (rc) {
QETH_CARD_TEXT(card, 2, "b2dqwerr");
+ rcu_read_unlock();
return NOTIFY_BAD;
}
}
lowerdev = netdev_next_lower_dev_rcu(brdev, &iter);
}
+
+ rcu_read_unlock();
+
return NOTIFY_DONE;
}
@@ -901,7 +910,7 @@ static void qeth_l2_br2dev_get(void)
int rc;
if (!refcount_inc_not_zero(&qeth_l2_switchdev_notify_refcnt)) {
- rc = register_switchdev_notifier(&qeth_l2_sw_notifier);
+ rc = register_switchdev_blocking_notifier(&qeth_l2_sw_notifier);
if (rc) {
QETH_DBF_MESSAGE(2,
"failed to register qeth_l2_sw_notifier: %d\n",@@ -921,7 +930,7 @@ static void qeth_l2_br2dev_put(void)
int rc;
if (refcount_dec_and_test(&qeth_l2_switchdev_notify_refcnt)) {
- rc = unregister_switchdev_notifier(&qeth_l2_sw_notifier);
+ rc = unregister_switchdev_blocking_notifier(&qeth_l2_sw_notifier);
if (rc) {
QETH_DBF_MESSAGE(2,
"failed to unregister qeth_l2_sw_notifier: %d\n",diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 6764fb7692e2..e27da5bd665f 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -191,8 +191,8 @@ struct switchdev_brport {
enum switchdev_notifier_type {
SWITCHDEV_FDB_ADD_TO_BRIDGE = 1,
SWITCHDEV_FDB_DEL_TO_BRIDGE,
- SWITCHDEV_FDB_ADD_TO_DEVICE,
- SWITCHDEV_FDB_DEL_TO_DEVICE,
+ SWITCHDEV_FDB_ADD_TO_DEVICE, /* Blocking. */
+ SWITCHDEV_FDB_DEL_TO_DEVICE, /* Blocking. */
SWITCHDEV_FDB_OFFLOADED,
SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
@@ -283,6 +283,13 @@ int switchdev_port_obj_add(struct net_device *dev,
int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj);
+int
+switchdev_fdb_add_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info);
+int
+switchdev_fdb_del_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info);
+
int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb);
int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
@@ -386,6 +393,20 @@ static inline int switchdev_port_obj_del(struct net_device *dev,
return -EOPNOTSUPP;
}
+static inline int
+switchdev_fdb_add_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+switchdev_fdb_del_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int register_switchdev_notifier(struct notifier_block *nb)
{
return 0;diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 7e62904089c8..c7c8e23c2147 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -140,12 +140,10 @@ br_switchdev_fdb_notify(struct net_bridge *br,
switch (type) {
case RTM_DELNEIGH:
- call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
- dev, &info.info, NULL);
+ switchdev_fdb_del_to_device(dev, &info);
break;
case RTM_NEWNEIGH:
- call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
- dev, &info.info, NULL);
+ switchdev_fdb_add_to_device(dev, &info);
break;
}
}@@ -303,6 +301,8 @@ static int br_fdb_replay(const struct net_device *br_dev, const void *ctx,
if (!nb)
return 0;
+ ASSERT_RTNL();
+
if (!netif_is_bridge_master(br_dev))
return -EINVAL;
@@ -343,7 +343,7 @@ static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx,
if (err && err != -EOPNOTSUPP)
return err;
- err = br_fdb_replay(br_dev, ctx, true, atomic_nb);
+ err = br_fdb_replay(br_dev, ctx, true, blocking_nb);
if (err && err != -EOPNOTSUPP)
return err;
@@ -362,7 +362,7 @@ static void nbp_switchdev_unsync_objs(struct net_bridge_port *p,
br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL);
- br_fdb_replay(br_dev, ctx, false, atomic_nb);
+ br_fdb_replay(br_dev, ctx, false, blocking_nb);
}
/* Let the bridge know that this port is offloaded, so that it can assign a
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7bc88767db9d..6601224f6a5a 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -2454,20 +2454,6 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
dsa_slave_dev_check,
dsa_slave_port_attr_set);
return notifier_from_errno(err);
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- err = switchdev_handle_fdb_add_to_device(dev, ptr,
- dsa_slave_dev_check,
- dsa_foreign_dev_check,
- dsa_slave_fdb_add_to_device,
- NULL);
- return notifier_from_errno(err);
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- err = switchdev_handle_fdb_del_to_device(dev, ptr,
- dsa_slave_dev_check,
- dsa_foreign_dev_check,
- dsa_slave_fdb_del_to_device,
- NULL);
- return notifier_from_errno(err);
default:
return NOTIFY_DONE;
}
@@ -2497,6 +2483,24 @@ static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
dsa_slave_dev_check,
dsa_slave_port_attr_set);
return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ rcu_read_lock();
+ err = switchdev_handle_fdb_add_to_device(dev, ptr,
+ dsa_slave_dev_check,
+ dsa_foreign_dev_check,
+ dsa_slave_fdb_add_to_device,
+ NULL);
+ rcu_read_unlock();
+ return notifier_from_errno(err);
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ rcu_read_lock();
+ err = switchdev_handle_fdb_del_to_device(dev, ptr,
+ dsa_slave_dev_check,
+ dsa_foreign_dev_check,
+ dsa_slave_fdb_del_to_device,
+ NULL);
+ rcu_read_unlock();
+ return notifier_from_errno(err);
}
return NOTIFY_DONE;
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 0b2c18efc079..c34c6abceec6 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -378,6 +378,53 @@ int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
}
EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
+static void switchdev_fdb_add_deferred(struct net_device *dev, const void *data)
+{
+ const struct switchdev_notifier_fdb_info *fdb_info = data;
+ struct switchdev_notifier_fdb_info tmp = *fdb_info;
+ int err;
+
+ ASSERT_RTNL();
+ err = call_switchdev_blocking_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
+ dev, &tmp.info, NULL);
+ err = notifier_to_errno(err);
+ if (err && err != -EOPNOTSUPP)
+ netdev_err(dev, "failed to add FDB entry: %pe\n", ERR_PTR(err));
+}
+
+static void switchdev_fdb_del_deferred(struct net_device *dev, const void *data)
+{
+ const struct switchdev_notifier_fdb_info *fdb_info = data;
+ struct switchdev_notifier_fdb_info tmp = *fdb_info;
+ int err;
+
+ ASSERT_RTNL();
+ err = call_switchdev_blocking_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
+ dev, &tmp.info, NULL);
+ err = notifier_to_errno(err);
+ if (err && err != -EOPNOTSUPP)
+ netdev_err(dev, "failed to delete FDB entry: %pe\n",
+ ERR_PTR(err));
+}
+
+int
+switchdev_fdb_add_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info)
+{
+ return switchdev_deferred_enqueue(dev, fdb_info, sizeof(*fdb_info),
+ switchdev_fdb_add_deferred);
+}
+EXPORT_SYMBOL_GPL(switchdev_fdb_add_to_device);
+
+int
+switchdev_fdb_del_to_device(struct net_device *dev,
+ const struct switchdev_notifier_fdb_info *fdb_info)
+{
+ return switchdev_deferred_enqueue(dev, fdb_info, sizeof(*fdb_info),
+ switchdev_fdb_del_deferred);
+}
+EXPORT_SYMBOL_GPL(switchdev_fdb_del_to_device);
+
struct switchdev_nested_priv {
bool (*check_cb)(const struct net_device *dev);
bool (*foreign_dev_check_cb)(const struct net_device *dev,--
2.25.1