Merge branch 'net-lan966x-fix-issues-with-mac-table'
Horatiu Vultur says: ==================== net: lan966x: Fix issues with MAC table The patch series fixes 2 issues: - when an entry was forgotten the irq thread was holding a spin lock and then was talking also rtnl_lock. - the access to the HW MAC table is indirect, so the access to the HW MAC table was not synchronized, which means that there could be race conditions. ==================== Link: https://lore.kernel.org/r/20220714194040.231651-1-horatiu.vultur@microchip.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
b6224a36de
@ -75,6 +75,9 @@ static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
|
||||
unsigned int vid,
|
||||
enum macaccess_entry_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
lan966x_mac_select(lan966x, mac, vid);
|
||||
|
||||
/* Issue a write command */
|
||||
@ -86,7 +89,10 @@ static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
|
||||
ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
|
||||
lan966x, ANA_MACACCESS);
|
||||
|
||||
return lan966x_mac_wait_for_completion(lan966x);
|
||||
ret = lan966x_mac_wait_for_completion(lan966x);
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The mask of the front ports is encoded inside the mac parameter via a call
|
||||
@ -113,11 +119,13 @@ int lan966x_mac_learn(struct lan966x *lan966x, int port,
|
||||
return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
|
||||
}
|
||||
|
||||
int lan966x_mac_forget(struct lan966x *lan966x,
|
||||
const unsigned char mac[ETH_ALEN],
|
||||
unsigned int vid,
|
||||
enum macaccess_entry_type type)
|
||||
static int lan966x_mac_forget_locked(struct lan966x *lan966x,
|
||||
const unsigned char mac[ETH_ALEN],
|
||||
unsigned int vid,
|
||||
enum macaccess_entry_type type)
|
||||
{
|
||||
lockdep_assert_held(&lan966x->mac_lock);
|
||||
|
||||
lan966x_mac_select(lan966x, mac, vid);
|
||||
|
||||
/* Issue a forget command */
|
||||
@ -128,6 +136,20 @@ int lan966x_mac_forget(struct lan966x *lan966x,
|
||||
return lan966x_mac_wait_for_completion(lan966x);
|
||||
}
|
||||
|
||||
int lan966x_mac_forget(struct lan966x *lan966x,
|
||||
const unsigned char mac[ETH_ALEN],
|
||||
unsigned int vid,
|
||||
enum macaccess_entry_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
ret = lan966x_mac_forget_locked(lan966x, mac, vid, type);
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)
|
||||
{
|
||||
return lan966x_mac_learn(lan966x, PGID_CPU, addr, vid, ENTRYTYPE_LOCKED);
|
||||
@ -161,7 +183,7 @@ static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *ma
|
||||
{
|
||||
struct lan966x_mac_entry *mac_entry;
|
||||
|
||||
mac_entry = kzalloc(sizeof(*mac_entry), GFP_KERNEL);
|
||||
mac_entry = kzalloc(sizeof(*mac_entry), GFP_ATOMIC);
|
||||
if (!mac_entry)
|
||||
return NULL;
|
||||
|
||||
@ -179,7 +201,6 @@ static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
|
||||
struct lan966x_mac_entry *res = NULL;
|
||||
struct lan966x_mac_entry *mac_entry;
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
|
||||
if (mac_entry->vid == vid &&
|
||||
ether_addr_equal(mac, mac_entry->mac) &&
|
||||
@ -188,7 +209,6 @@ static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -231,8 +251,11 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
|
||||
{
|
||||
struct lan966x_mac_entry *mac_entry;
|
||||
|
||||
if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL))
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL)) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In case the entry already exists, don't add it again to SW,
|
||||
* just update HW, but we need to look in the actual HW because
|
||||
@ -241,21 +264,25 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
|
||||
* add the entry but without the extern_learn flag.
|
||||
*/
|
||||
mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
|
||||
if (mac_entry)
|
||||
return lan966x_mac_learn(lan966x, port->chip_port,
|
||||
addr, vid, ENTRYTYPE_LOCKED);
|
||||
if (mac_entry) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
goto mac_learn;
|
||||
}
|
||||
|
||||
mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
|
||||
if (!mac_entry)
|
||||
if (!mac_entry) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
list_add_tail(&mac_entry->list, &lan966x->mac_entries);
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
|
||||
lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
|
||||
|
||||
mac_learn:
|
||||
lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -269,8 +296,9 @@ int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr,
|
||||
list) {
|
||||
if (mac_entry->vid == vid &&
|
||||
ether_addr_equal(addr, mac_entry->mac)) {
|
||||
lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,
|
||||
ENTRYTYPE_LOCKED);
|
||||
lan966x_mac_forget_locked(lan966x, mac_entry->mac,
|
||||
mac_entry->vid,
|
||||
ENTRYTYPE_LOCKED);
|
||||
|
||||
list_del(&mac_entry->list);
|
||||
kfree(mac_entry);
|
||||
@ -288,8 +316,8 @@ void lan966x_mac_purge_entries(struct lan966x *lan966x)
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
|
||||
list) {
|
||||
lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,
|
||||
ENTRYTYPE_LOCKED);
|
||||
lan966x_mac_forget_locked(lan966x, mac_entry->mac,
|
||||
mac_entry->vid, ENTRYTYPE_LOCKED);
|
||||
|
||||
list_del(&mac_entry->list);
|
||||
kfree(mac_entry);
|
||||
@ -325,10 +353,13 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
|
||||
{
|
||||
struct lan966x_mac_entry *mac_entry, *tmp;
|
||||
unsigned char mac[ETH_ALEN] __aligned(2);
|
||||
struct list_head mac_deleted_entries;
|
||||
u32 dest_idx;
|
||||
u32 column;
|
||||
u16 vid;
|
||||
|
||||
INIT_LIST_HEAD(&mac_deleted_entries);
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
|
||||
bool found = false;
|
||||
@ -362,20 +393,26 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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
|
||||
* list, then that means that the entry is new so it needs to notify the
|
||||
* bridge.
|
||||
@ -396,13 +433,20 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
|
||||
if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
|
||||
continue;
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, dest_idx);
|
||||
if (mac_entry) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);
|
||||
if (!mac_entry)
|
||||
if (!mac_entry) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
mac_entry->row = row;
|
||||
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
list_add_tail(&mac_entry->list, &lan966x->mac_entries);
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
@ -424,6 +468,7 @@ irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x)
|
||||
lan966x, ANA_MACTINDX);
|
||||
|
||||
while (1) {
|
||||
spin_lock(&lan966x->mac_lock);
|
||||
lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),
|
||||
ANA_MACACCESS_MAC_TABLE_CMD,
|
||||
lan966x, ANA_MACACCESS);
|
||||
@ -447,12 +492,15 @@ irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x)
|
||||
stop = false;
|
||||
|
||||
if (column == LAN966X_MAC_COLUMNS - 1 &&
|
||||
index == 0 && stop)
|
||||
index == 0 && stop) {
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);
|
||||
entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);
|
||||
entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);
|
||||
spin_unlock(&lan966x->mac_lock);
|
||||
|
||||
/* Once all the columns are read process them */
|
||||
if (column == LAN966X_MAC_COLUMNS - 1) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user