Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/mce/amd: Cleanup threshold device remove path

Pass in the bank pointer directly to the cleaning up functions,
obviating the need for per-CPU accesses. Make the clean up path
interrupt-safe by cleaning the bank pointer first so that the rest of
the teardown happens safe from the thresholding interrupt.

No functional changes.

[ bp: Write commit message and reverse bank->shared test to save an
indentation level in threshold_remove_bank(). ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20200403161943.1458-7-bp@alien8.de

authored by

Thomas Gleixner and committed by
Borislav Petkov
f26d2580 6458de97

+39 -43
+1
arch/x86/include/asm/amd_nb.h
··· 57 57 58 58 /* initialized to the number of CPUs on the node sharing this bank */ 59 59 refcount_t cpus; 60 + unsigned int shared; 60 61 }; 61 62 62 63 struct amd_northbridge {
+38 -43
arch/x86/kernel/cpu/mce/amd.c
··· 1362 1362 } 1363 1363 1364 1364 if (is_shared_bank(bank)) { 1365 + b->shared = 1; 1365 1366 refcount_set(&b->cpus, 1); 1366 1367 1367 1368 /* nb is already initialized, see above */ ··· 1392 1391 kfree(to_block(kobj)); 1393 1392 } 1394 1393 1395 - static void deallocate_threshold_block(unsigned int cpu, unsigned int bank) 1394 + static void deallocate_threshold_blocks(struct threshold_bank *bank) 1396 1395 { 1397 - struct threshold_block *pos = NULL; 1398 - struct threshold_block *tmp = NULL; 1399 - struct threshold_bank *head = per_cpu(threshold_banks, cpu)[bank]; 1396 + struct threshold_block *pos, *tmp; 1400 1397 1401 - if (!head) 1402 - return; 1403 - 1404 - list_for_each_entry_safe(pos, tmp, &head->blocks->miscj, miscj) { 1398 + list_for_each_entry_safe(pos, tmp, &bank->blocks->miscj, miscj) { 1405 1399 list_del(&pos->miscj); 1406 1400 kobject_put(&pos->kobj); 1407 1401 } 1408 1402 1409 - kobject_put(&head->blocks->kobj); 1403 + kobject_put(&bank->blocks->kobj); 1410 1404 } 1411 1405 1412 1406 static void __threshold_remove_blocks(struct threshold_bank *b) ··· 1415 1419 kobject_del(&pos->kobj); 1416 1420 } 1417 1421 1418 - static void threshold_remove_bank(unsigned int cpu, int bank) 1422 + static void threshold_remove_bank(struct threshold_bank *bank) 1419 1423 { 1420 1424 struct amd_northbridge *nb; 1421 - struct threshold_bank *b; 1422 1425 1423 - b = per_cpu(threshold_banks, cpu)[bank]; 1424 - if (!b) 1426 + if (!bank->blocks) 1427 + goto out_free; 1428 + 1429 + if (!bank->shared) 1430 + goto out_dealloc; 1431 + 1432 + if (!refcount_dec_and_test(&bank->cpus)) { 1433 + __threshold_remove_blocks(bank); 1425 1434 return; 1426 - 1427 - if (!b->blocks) 1428 - goto free_out; 1429 - 1430 - if (is_shared_bank(bank)) { 1431 - if (!refcount_dec_and_test(&b->cpus)) { 1432 - __threshold_remove_blocks(b); 1433 - per_cpu(threshold_banks, cpu)[bank] = NULL; 1434 - return; 1435 - } else { 1436 - /* 1437 - * the last CPU on this node using the shared bank is 1438 - * going away, remove that bank now. 1439 - */ 1440 - nb = node_to_amd_nb(amd_get_nb_id(cpu)); 1441 - nb->bank4 = NULL; 1442 - } 1435 + } else { 1436 + /* 1437 + * The last CPU on this node using the shared bank is going 1438 + * away, remove that bank now. 1439 + */ 1440 + nb = node_to_amd_nb(amd_get_nb_id(smp_processor_id())); 1441 + nb->bank4 = NULL; 1443 1442 } 1444 1443 1445 - deallocate_threshold_block(cpu, bank); 1444 + out_dealloc: 1445 + deallocate_threshold_blocks(bank); 1446 1446 1447 - free_out: 1448 - kobject_del(b->kobj); 1449 - kobject_put(b->kobj); 1450 - kfree(b); 1451 - per_cpu(threshold_banks, cpu)[bank] = NULL; 1447 + out_free: 1448 + kobject_put(bank->kobj); 1449 + kfree(bank); 1452 1450 } 1453 1451 1454 1452 int mce_threshold_remove_device(unsigned int cpu) 1455 1453 { 1456 1454 struct threshold_bank **bp = this_cpu_read(threshold_banks); 1457 - unsigned int bank; 1455 + unsigned int bank, numbanks = this_cpu_read(mce_num_banks); 1458 1456 1459 1457 if (!bp) 1460 1458 return 0; 1461 1459 1462 - for (bank = 0; bank < per_cpu(mce_num_banks, cpu); ++bank) { 1463 - if (!(per_cpu(bank_map, cpu) & (1 << bank))) 1464 - continue; 1465 - threshold_remove_bank(cpu, bank); 1466 - } 1467 - /* Clear the pointer before freeing the memory */ 1460 + /* 1461 + * Clear the pointer before cleaning up, so that the interrupt won't 1462 + * touch anything of this. 1463 + */ 1468 1464 this_cpu_write(threshold_banks, NULL); 1465 + 1466 + for (bank = 0; bank < numbanks; bank++) { 1467 + if (bp[bank]) { 1468 + threshold_remove_bank(bp[bank]); 1469 + bp[bank] = NULL; 1470 + } 1471 + } 1469 1472 kfree(bp); 1470 1473 return 0; 1471 1474 }