Commit 45533a53 authored by Horatiu Vultur's avatar Horatiu Vultur Committed by Jakub Kicinski

net: lan966x: Fix taking rtnl_lock while holding spin_lock

When the HW deletes an entry in MAC table then it generates an
interrupt. The SW will go through it's own list of MAC entries and if it
is not found then it would notify the listeners about this. The problem
is that when the SW will go through it's own list it would take a spin
lock(lan966x->mac_lock) and when it notifies that the entry is deleted.
But to notify the listeners it taking the rtnl_lock which is illegal.

This is fixed by instead of notifying right away that the entry is
deleted, move the entry on a temp list and once, it checks all the
entries then just notify that the entries from temp list are deleted.

Fixes: 5ccd66e0 ("net: lan966x: add support for interrupts from analyzer")
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Reviewed-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent c32349f3
...@@ -325,10 +325,13 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, ...@@ -325,10 +325,13 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
{ {
struct lan966x_mac_entry *mac_entry, *tmp; struct lan966x_mac_entry *mac_entry, *tmp;
unsigned char mac[ETH_ALEN] __aligned(2); unsigned char mac[ETH_ALEN] __aligned(2);
struct list_head mac_deleted_entries;
u32 dest_idx; u32 dest_idx;
u32 column; u32 column;
u16 vid; u16 vid;
INIT_LIST_HEAD(&mac_deleted_entries);
spin_lock(&lan966x->mac_lock); spin_lock(&lan966x->mac_lock);
list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) { list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
bool found = false; bool found = false;
...@@ -362,20 +365,26 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, ...@@ -362,20 +365,26 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
} }
if (!found) { if (!found) {
/* Notify the bridge that the entry doesn't exist
* anymore in the HW and remove the entry from the SW
* list
*/
lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
mac_entry->mac, mac_entry->vid,
lan966x->ports[mac_entry->port_index]->dev);
list_del(&mac_entry->list); list_del(&mac_entry->list);
kfree(mac_entry); /* Move the entry from SW list to a tmp list such that
* it would be deleted later
*/
list_add_tail(&mac_entry->list, &mac_deleted_entries);
} }
} }
spin_unlock(&lan966x->mac_lock); spin_unlock(&lan966x->mac_lock);
list_for_each_entry_safe(mac_entry, tmp, &mac_deleted_entries, list) {
/* Notify the bridge that the entry doesn't exist
* anymore in the HW
*/
lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
mac_entry->mac, mac_entry->vid,
lan966x->ports[mac_entry->port_index]->dev);
list_del(&mac_entry->list);
kfree(mac_entry);
}
/* Now go to the list of columns and see if any entry was not in the SW /* Now go to the list of columns and see if any entry was not in the SW
* list, then that means that the entry is new so it needs to notify the * list, then that means that the entry is new so it needs to notify the
* bridge. * bridge.
......
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