History

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. ====================

Jakub Kicinski committed 4 years ago

Commit b6224a32 parents c32349f 675c8071 file · +80 −32Browse files

Unified · Split

drivers/net/ethernet/microchip/lan966x/lan966x_mac.c+80−32
@@ -75,6 +75,9 @@ static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, · 74 unmodified lines
75 unsigned int vid,75 unsigned int vid,
76 enum macaccess_entry_type type)76 enum macaccess_entry_type type)
77{77{
78 int ret;
79
80 spin_lock(&lan966x->mac_lock);
78 lan966x_mac_select(lan966x, mac, vid);81 lan966x_mac_select(lan966x, mac, vid);
79 82
80 /* Issue a write command */83 /* Issue a write command */
@@ -86,7 +89,10 @@ static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, · 5 unmodified lines
86 ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),89 ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
87 lan966x, ANA_MACACCESS);90 lan966x, ANA_MACACCESS);
88 91
89 return lan966x_mac_wait_for_completion(lan966x);92 ret = lan966x_mac_wait_for_completion(lan966x);
93 spin_unlock(&lan966x->mac_lock);
94
95 return ret;
90}96}
91 97
92/* The mask of the front ports is encoded inside the mac parameter via a call98/* 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, · 20 unmodified lines
113 return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);119 return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
114}120}
115 121
116int lan966x_mac_forget(struct lan966x *lan966x,122static int lan966x_mac_forget_locked(struct lan966x *lan966x,
117 const unsigned char mac[ETH_ALEN],123 const unsigned char mac[ETH_ALEN],
118 unsigned int vid,124 unsigned int vid,
119 enum macaccess_entry_type type)125 enum macaccess_entry_type type)
120{126{
127 lockdep_assert_held(&lan966x->mac_lock);
128
121 lan966x_mac_select(lan966x, mac, vid);129 lan966x_mac_select(lan966x, mac, vid);
122 130
123 /* Issue a forget command */131 /* Issue a forget command */
@@ -128,6 +136,20 @@ int lan966x_mac_forget(struct lan966x *lan966x, · 4 unmodified lines
128 return lan966x_mac_wait_for_completion(lan966x);136 return lan966x_mac_wait_for_completion(lan966x);
129}137}
130 138
139int lan966x_mac_forget(struct lan966x *lan966x,
140 const unsigned char mac[ETH_ALEN],
141 unsigned int vid,
142 enum macaccess_entry_type type)
143{
144 int ret;
145
146 spin_lock(&lan966x->mac_lock);
147 ret = lan966x_mac_forget_locked(lan966x, mac, vid, type);
148 spin_unlock(&lan966x->mac_lock);
149
150 return ret;
151}
152
131int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)153int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)
132{154{
133 return lan966x_mac_learn(lan966x, PGID_CPU, addr, vid, ENTRYTYPE_LOCKED);155 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 · 27 unmodified lines
161{183{
162 struct lan966x_mac_entry *mac_entry;184 struct lan966x_mac_entry *mac_entry;
163 185
164 mac_entry = kzalloc(sizeof(*mac_entry), GFP_KERNEL);186 mac_entry = kzalloc(sizeof(*mac_entry), GFP_ATOMIC);
165 if (!mac_entry)187 if (!mac_entry)
166 return NULL;188 return NULL;
167 189
@@ -179,7 +201,6 @@ static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x, · 11 unmodified lines
179 struct lan966x_mac_entry *res = NULL;201 struct lan966x_mac_entry *res = NULL;
180 struct lan966x_mac_entry *mac_entry;202 struct lan966x_mac_entry *mac_entry;
181 203
182 spin_lock(&lan966x->mac_lock);
183 list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {204 list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
184 if (mac_entry->vid == vid &&205 if (mac_entry->vid == vid &&
185 ether_addr_equal(mac, mac_entry->mac) &&206 ether_addr_equal(mac, mac_entry->mac) &&
@@ -188,7 +209,6 @@ static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x, · 2 unmodified lines
188 break;209 break;
189 }210 }
190 }211 }
191 spin_unlock(&lan966x->mac_lock);
192 212
193 return res;213 return res;
194}214}
@@ -231,8 +251,11 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port, · 36 unmodified lines
231{251{
232 struct lan966x_mac_entry *mac_entry;252 struct lan966x_mac_entry *mac_entry;
233 253
234 if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL))254 spin_lock(&lan966x->mac_lock);
255 if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL)) {
256 spin_unlock(&lan966x->mac_lock);
235 return 0;257 return 0;
258 }
236 259
237 /* In case the entry already exists, don't add it again to SW,260 /* In case the entry already exists, don't add it again to SW,
238 * just update HW, but we need to look in the actual HW because261 * 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, · 2 unmodified lines
241 * add the entry but without the extern_learn flag.264 * add the entry but without the extern_learn flag.
242 */265 */
243 mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);266 mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
244 if (mac_entry)267 if (mac_entry) {
245 return lan966x_mac_learn(lan966x, port->chip_port,268 spin_unlock(&lan966x->mac_lock);
246 addr, vid, ENTRYTYPE_LOCKED);269 goto mac_learn;
270 }
247 271
248 mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);272 mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
249 if (!mac_entry)273 if (!mac_entry) {
274 spin_unlock(&lan966x->mac_lock);
250 return -ENOMEM;275 return -ENOMEM;
276 }
251 277
252 spin_lock(&lan966x->mac_lock);
253 list_add_tail(&mac_entry->list, &lan966x->mac_entries);278 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
254 spin_unlock(&lan966x->mac_lock);279 spin_unlock(&lan966x->mac_lock);
255 280
256 lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
257 lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);281 lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
258 282
283mac_learn:
284 lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
285
259 return 0;286 return 0;
260}287}
261 288
@@ -269,8 +296,9 @@ int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr, · 7 unmodified lines
269 list) {296 list) {
270 if (mac_entry->vid == vid &&297 if (mac_entry->vid == vid &&
271 ether_addr_equal(addr, mac_entry->mac)) {298 ether_addr_equal(addr, mac_entry->mac)) {
272 lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,299 lan966x_mac_forget_locked(lan966x, mac_entry->mac,
273 ENTRYTYPE_LOCKED);300 mac_entry->vid,
301 ENTRYTYPE_LOCKED);
274 302
275 list_del(&mac_entry->list);303 list_del(&mac_entry->list);
276 kfree(mac_entry);304 kfree(mac_entry);
@@ -288,8 +316,8 @@ void lan966x_mac_purge_entries(struct lan966x *lan966x) · 11 unmodified lines
288 spin_lock(&lan966x->mac_lock);316 spin_lock(&lan966x->mac_lock);
289 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,317 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
290 list) {318 list) {
291 lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,319 lan966x_mac_forget_locked(lan966x, mac_entry->mac,
292 ENTRYTYPE_LOCKED);320 mac_entry->vid, ENTRYTYPE_LOCKED);
293 321
294 list_del(&mac_entry->list);322 list_del(&mac_entry->list);
295 kfree(mac_entry);323 kfree(mac_entry);
@@ -325,10 +353,13 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, · 29 unmodified lines
325{353{
326 struct lan966x_mac_entry *mac_entry, *tmp;354 struct lan966x_mac_entry *mac_entry, *tmp;
327 unsigned char mac[ETH_ALEN] __aligned(2);355 unsigned char mac[ETH_ALEN] __aligned(2);
356 struct list_head mac_deleted_entries;
328 u32 dest_idx;357 u32 dest_idx;
329 u32 column;358 u32 column;
330 u16 vid;359 u16 vid;
331 360
361 INIT_LIST_HEAD(&mac_deleted_entries);
362
332 spin_lock(&lan966x->mac_lock);363 spin_lock(&lan966x->mac_lock);
333 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {364 list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
334 bool found = false;365 bool found = false;
@@ -362,20 +393,26 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, · 27 unmodified lines
362 }393 }
363 394
364 if (!found) {395 if (!found) {
365 /* Notify the bridge that the entry doesn't exist
366 * anymore in the HW and remove the entry from the SW
367 * list
368 */
369 lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
370 mac_entry->mac, mac_entry->vid,
371 lan966x->ports[mac_entry->port_index]->dev);
372
373 list_del(&mac_entry->list);396 list_del(&mac_entry->list);
374 kfree(mac_entry);397 /* Move the entry from SW list to a tmp list such that
398 * it would be deleted later
399 */
400 list_add_tail(&mac_entry->list, &mac_deleted_entries);
375 }401 }
376 }402 }
377 spin_unlock(&lan966x->mac_lock);403 spin_unlock(&lan966x->mac_lock);
378 404
405 list_for_each_entry_safe(mac_entry, tmp, &mac_deleted_entries, list) {
406 /* Notify the bridge that the entry doesn't exist
407 * anymore in the HW
408 */
409 lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
410 mac_entry->mac, mac_entry->vid,
411 lan966x->ports[mac_entry->port_index]->dev);
412 list_del(&mac_entry->list);
413 kfree(mac_entry);
414 }
415
379 /* Now go to the list of columns and see if any entry was not in the SW416 /* Now go to the list of columns and see if any entry was not in the SW
380 * list, then that means that the entry is new so it needs to notify the417 * list, then that means that the entry is new so it needs to notify the
381 * bridge.418 * bridge.
@@ -396,13 +433,20 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, · 14 unmodified lines
396 if (WARN_ON(dest_idx >= lan966x->num_phys_ports))433 if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
397 continue;434 continue;
398 435
436 spin_lock(&lan966x->mac_lock);
437 mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, dest_idx);
438 if (mac_entry) {
439 spin_unlock(&lan966x->mac_lock);
440 continue;
441 }
442
399 mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);443 mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);
400 if (!mac_entry)444 if (!mac_entry) {
445 spin_unlock(&lan966x->mac_lock);
401 return;446 return;
447 }
402 448
403 mac_entry->row = row;449 mac_entry->row = row;
404
405 spin_lock(&lan966x->mac_lock);
406 list_add_tail(&mac_entry->list, &lan966x->mac_entries);450 list_add_tail(&mac_entry->list, &lan966x->mac_entries);
407 spin_unlock(&lan966x->mac_lock);451 spin_unlock(&lan966x->mac_lock);
408 452
@@ -424,6 +468,7 @@ irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x) · 15 unmodified lines
424 lan966x, ANA_MACTINDX);468 lan966x, ANA_MACTINDX);
425 469
426 while (1) {470 while (1) {
471 spin_lock(&lan966x->mac_lock);
427 lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),472 lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),
428 ANA_MACACCESS_MAC_TABLE_CMD,473 ANA_MACACCESS_MAC_TABLE_CMD,
429 lan966x, ANA_MACACCESS);474 lan966x, ANA_MACACCESS);
@@ -447,12 +492,15 @@ irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x) · 17 unmodified lines
447 stop = false;492 stop = false;
448 493
449 if (column == LAN966X_MAC_COLUMNS - 1 &&494 if (column == LAN966X_MAC_COLUMNS - 1 &&
450 index == 0 && stop)495 index == 0 && stop) {
496 spin_unlock(&lan966x->mac_lock);
451 break;497 break;
498 }
452 499
453 entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);500 entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);
454 entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);501 entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);
455 entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);502 entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);
503 spin_unlock(&lan966x->mac_lock);
456 504
457 /* Once all the columns are read process them */505 /* Once all the columns are read process them */
458 if (column == LAN966X_MAC_COLUMNS - 1) {506 if (column == LAN966X_MAC_COLUMNS - 1) {