From 365d0371a9ecf9f15fcf9e054e3bb7205603344d Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:26 +0200 Subject: [PATCH 1/8] dpaa2-switch: set interface MAC address only on endpoint change There is no point in updating the MAC address of a switch interface each time the link state changes, this only needs to happen in case the endpoint changes (the switch interface is [dis]connected from/to a MAC). Just move the call to dpaa2_switch_port_set_mac_addr() under DPSW_IRQ_EVENT_ENDPOINT_CHANGED. Reviewed-by: Simon Horman Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index e01a246124ac6..811e2cfe6c937 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1523,12 +1523,11 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) if_id = (status & 0xFFFF0000) >> 16; port_priv = ethsw->ports[if_id]; - if (status & DPSW_IRQ_EVENT_LINK_CHANGED) { + if (status & DPSW_IRQ_EVENT_LINK_CHANGED) dpaa2_switch_port_link_state_update(port_priv->netdev); - dpaa2_switch_port_set_mac_addr(port_priv); - } if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { + dpaa2_switch_port_set_mac_addr(port_priv); /* We can avoid locking because the "endpoint changed" IRQ * handler is the only one who changes priv->mac at runtime, * so we are not racing with anyone. From 7218e963196e5f85008a8f86e5b4dd613fae0a4c Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:27 +0200 Subject: [PATCH 2/8] dpaa2-switch: declare the netdev as IFF_LIVE_ADDR_CHANGE capable There is no restriction around the change of the MAC address on the switch ports, thus declare the interface netdevs IFF_LIVE_ADDR_CHANGE capable. Reviewed-by: Simon Horman Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 811e2cfe6c937..a41d5c7428ab8 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -3293,6 +3293,7 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, port_netdev->features = NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER | NETIF_F_HW_TC; + port_netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; err = dpaa2_switch_port_init(port_priv, port_idx); if (err) From d50b1a8c3033bfb1cf97b0a8d0ce58e355c706e2 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:28 +0200 Subject: [PATCH 3/8] dpaa2-switch: print an error when the vlan is already configured Print a netdev error when we hit a case in which a specific VLAN is already configured on the port. While at it, change the already existing netdev_warn into an _err for consistency purposes. Reviewed-by: Simon Horman Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index a41d5c7428ab8..0f9103b134387 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -289,7 +289,7 @@ static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv, int err; if (port_priv->vlans[vid]) { - netdev_warn(netdev, "VLAN %d already configured\n", vid); + netdev_err(netdev, "VLAN %d already configured\n", vid); return -EEXIST; } @@ -1774,8 +1774,10 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev, /* Make sure that the VLAN is not already configured * on the switch port */ - if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) + if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) { + netdev_err(netdev, "VLAN %d already configured\n", vlan->vid); return -EEXIST; + } /* Check if there is space for a new VLAN */ err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, From 77c42a3b0a3a68408976492d9e435a5adfeca5aa Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:29 +0200 Subject: [PATCH 4/8] dpaa2-switch: add ENDPOINT_CHANGED to the irq_mask Commit 84cba72956fd ("dpaa2-switch: integrate the MAC endpoint support") added support for MAC endpoints in the dpaa2-switch driver but omitted to add the ENDPOINT_CHANGED irq to the list of interrupt sources. Fix this by extending the list of events which can raise an interrupt by extending the mask passed to the dpsw_set_irq_mask() firmware API. There is no user visible impact even without this patch since whenever a switch interface is connected/disconnected from an endpoint both events are set (LINK_CHANGED and ENDPOINT_CHANGED) and, luckily, the LINK_CHANGED event could actually raise the interrupt and thus get the MAC/PHY SW configuration started. Even with this, it's better to just not rely on undocumented firmware behavior which can change. Signed-off-by: Ioana Ciornei Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 0f9103b134387..a355295468bdb 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1550,9 +1550,9 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) static int dpaa2_switch_setup_irqs(struct fsl_mc_device *sw_dev) { + u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED | DPSW_IRQ_EVENT_ENDPOINT_CHANGED; struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw = dev_get_drvdata(dev); - u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED; struct fsl_mc_device_irq *irq; int err; From f6da276479c63ca29774bc331a537b92f0550c45 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:30 +0200 Subject: [PATCH 5/8] dpaa2-switch: do not clear any interrupts automatically The DPSW object has multiple event sources multiplexed over the same IRQ. The driver has the capability to configure only some of these events to trigger the IRQ. The dpsw_get_irq_status() can clear events automatically based on the value stored in the 'status' variable passed to it. We don't want that to happen because we could get into a situation when we are clearing more events than we actually handled. Just resort to manually clearing the events that we handled. Also, since status is not used on the out path we remove its initialization to zero. This change does not have a user-visible effect because the dpaa2-switch driver enables and handles all the DPSW events which exist at the moment. Signed-off-by: Ioana Ciornei Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index a355295468bdb..1b8d233e08020 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1509,9 +1509,9 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) struct device *dev = (struct device *)arg; struct ethsw_core *ethsw = dev_get_drvdata(dev); struct ethsw_port_priv *port_priv; - u32 status = ~0; int err, if_id; bool had_mac; + u32 status; err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, DPSW_IRQ_INDEX_IF, &status); @@ -1539,12 +1539,12 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) dpaa2_switch_port_connect_mac(port_priv); } -out: err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, DPSW_IRQ_INDEX_IF, status); if (err) dev_err(dev, "Can't clear irq status (err %d)\n", err); +out: return IRQ_HANDLED; } From a8150c9fb1d5c7ea190842ed8e5612ece0017e23 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:31 +0200 Subject: [PATCH 6/8] dpaa2-switch: reorganize the [pre]changeupper events Create separate functions, dpaa2_switch_port_prechangeupper and dpaa2_switch_port_changeupper, to be called directly when a DPSW port changes its upper device. This way we are not open-coding everything in the main event callback and we can easily extent, for example, with bond offload. Signed-off-by: Ioana Ciornei Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/freescale/dpaa2/dpaa2-switch.c | 77 +++++++++++++------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 1b8d233e08020..a9a76d640bc8b 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2173,51 +2173,78 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, return 0; } -static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb, - unsigned long event, void *ptr) +static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, + struct netdev_notifier_changeupper_info *info) { - struct net_device *netdev = netdev_notifier_info_to_dev(ptr); - struct netdev_notifier_changeupper_info *info = ptr; struct netlink_ext_ack *extack; struct net_device *upper_dev; - int err = 0; + int err; if (!dpaa2_switch_port_dev_check(netdev)) - return NOTIFY_DONE; + return 0; extack = netdev_notifier_info_to_extack(&info->info); - - switch (event) { - case NETDEV_PRECHANGEUPPER: - upper_dev = info->upper_dev; - if (!netif_is_bridge_master(upper_dev)) - break; - + upper_dev = info->upper_dev; + if (netif_is_bridge_master(upper_dev)) { err = dpaa2_switch_prechangeupper_sanity_checks(netdev, upper_dev, extack); if (err) - goto out; + return err; if (!info->linking) dpaa2_switch_port_pre_bridge_leave(netdev); + } + + return 0; +} + +static int dpaa2_switch_port_changeupper(struct net_device *netdev, + struct netdev_notifier_changeupper_info *info) +{ + struct netlink_ext_ack *extack; + struct net_device *upper_dev; + + if (!dpaa2_switch_port_dev_check(netdev)) + return 0; + + extack = netdev_notifier_info_to_extack(&info->info); + + upper_dev = info->upper_dev; + if (netif_is_bridge_master(upper_dev)) { + if (info->linking) + return dpaa2_switch_port_bridge_join(netdev, + upper_dev, + extack); + else + return dpaa2_switch_port_bridge_leave(netdev); + } + + return 0; +} + +static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + int err = 0; + + switch (event) { + case NETDEV_PRECHANGEUPPER: + err = dpaa2_switch_port_prechangeupper(netdev, ptr); + if (err) + return notifier_from_errno(err); break; case NETDEV_CHANGEUPPER: - upper_dev = info->upper_dev; - if (netif_is_bridge_master(upper_dev)) { - if (info->linking) - err = dpaa2_switch_port_bridge_join(netdev, - upper_dev, - extack); - else - err = dpaa2_switch_port_bridge_leave(netdev); - } + err = dpaa2_switch_port_changeupper(netdev, ptr); + if (err) + return notifier_from_errno(err); + break; } -out: - return notifier_from_errno(err); + return NOTIFY_DONE; } struct ethsw_switchdev_event_work { From 6d46a4f10532ce59e88a58199b666cebf6625ec7 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:32 +0200 Subject: [PATCH 7/8] dpaa2-switch: move a check to the prechangeupper stage Two different DPAA2 switch ports from two different DPSW instances cannot be under the same bridge. Instead of checking for this unsupported configuration in the CHANGEUPPER event, check it as early as possible in the PRECHANGEUPPER one. Reviewed-by: Simon Horman Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- .../ethernet/freescale/dpaa2/dpaa2-switch.c | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index a9a76d640bc8b..bccdaaaf5ea16 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2005,24 +2005,9 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, { struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct ethsw_core *ethsw = port_priv->ethsw_data; - struct ethsw_port_priv *other_port_priv; - struct net_device *other_dev; - struct list_head *iter; bool learn_ena; int err; - netdev_for_each_lower_dev(upper_dev, other_dev, iter) { - if (!dpaa2_switch_port_dev_check(other_dev)) - continue; - - other_port_priv = netdev_priv(other_dev); - if (other_port_priv->ethsw_data != port_priv->ethsw_data) { - NL_SET_ERR_MSG_MOD(extack, - "Interface from a different DPSW is in the bridge already"); - return -EINVAL; - } - } - /* Delete the previously manually installed VLAN 1 */ err = dpaa2_switch_port_del_vlan(port_priv, 1); if (err) @@ -2156,6 +2141,10 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, struct net_device *upper_dev, struct netlink_ext_ack *extack) { + struct ethsw_port_priv *port_priv = netdev_priv(netdev); + struct ethsw_port_priv *other_port_priv; + struct net_device *other_dev; + struct list_head *iter; int err; if (!br_vlan_enabled(upper_dev)) { @@ -2170,6 +2159,18 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, return 0; } + netdev_for_each_lower_dev(upper_dev, other_dev, iter) { + if (!dpaa2_switch_port_dev_check(other_dev)) + continue; + + other_port_priv = netdev_priv(other_dev); + if (other_port_priv->ethsw_data != port_priv->ethsw_data) { + NL_SET_ERR_MSG_MOD(extack, + "Interface from a different DPSW is in the bridge already"); + return -EINVAL; + } + } + return 0; } From 71150d9447c0f4d46403dca7342d149b626f0bc4 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 19 Dec 2023 13:59:33 +0200 Subject: [PATCH 8/8] dpaa2-switch: cleanup the egress flood of an unused FDB In case a DPAA2 switch interface joins a bridge, the FDB used on the port will be changed to the one associated with the bridge. What this means exactly is that any VLAN installed on the port will need to be removed and then installed back so that it points to the new FDB. Once this is done, the previous FDB will become unused (no VLAN to point to it). Even though no traffic will reach this FDB, it's best to just cleanup the state of the FDB by zeroing its egress flood domain. Reviewed-by: Simon Horman Signed-off-by: Ioana Ciornei Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index bccdaaaf5ea16..f3543a2df68d5 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2004,6 +2004,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, struct netlink_ext_ack *extack) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); + struct dpaa2_switch_fdb *old_fdb = port_priv->fdb; struct ethsw_core *ethsw = port_priv->ethsw_data; bool learn_ena; int err; @@ -2025,6 +2026,11 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, if (err) goto err_egress_flood; + /* Recreate the egress flood domain of the FDB that we just left. */ + err = dpaa2_switch_fdb_set_egress_flood(ethsw, old_fdb->fdb_id); + if (err) + goto err_egress_flood; + err = switchdev_bridge_port_offload(netdev, netdev, NULL, NULL, NULL, false, extack); if (err)