x86/mce/amd: Cleanup threshold device remove path
authorThomas Gleixner <tglx@linutronix.de>
Tue, 31 Mar 2020 08:53:18 +0000 (10:53 +0200)
committerBorislav Petkov <bp@suse.de>
Tue, 14 Apr 2020 13:49:51 +0000 (15:49 +0200)
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
arch/x86/include/asm/amd_nb.h
arch/x86/kernel/cpu/mce/amd.c

index c7df20e..455066a 100644 (file)
@@ -57,6 +57,7 @@ struct threshold_bank {
 
        /* initialized to the number of CPUs on the node sharing this bank */
        refcount_t              cpus;
+       unsigned int            shared;
 };
 
 struct amd_northbridge {
index a33d9a1..16e7aea 100644 (file)
@@ -1362,6 +1362,7 @@ static int threshold_create_bank(struct threshold_bank **bp, unsigned int cpu,
        }
 
        if (is_shared_bank(bank)) {
+               b->shared = 1;
                refcount_set(&b->cpus, 1);
 
                /* nb is already initialized, see above */
@@ -1391,21 +1392,16 @@ static void threshold_block_release(struct kobject *kobj)
        kfree(to_block(kobj));
 }
 
-static void deallocate_threshold_block(unsigned int cpu, unsigned int bank)
+static void deallocate_threshold_blocks(struct threshold_bank *bank)
 {
-       struct threshold_block *pos = NULL;
-       struct threshold_block *tmp = NULL;
-       struct threshold_bank *head = per_cpu(threshold_banks, cpu)[bank];
-
-       if (!head)
-               return;
+       struct threshold_block *pos, *tmp;
 
-       list_for_each_entry_safe(pos, tmp, &head->blocks->miscj, miscj) {
+       list_for_each_entry_safe(pos, tmp, &bank->blocks->miscj, miscj) {
                list_del(&pos->miscj);
                kobject_put(&pos->kobj);
        }
 
-       kobject_put(&head->blocks->kobj);
+       kobject_put(&bank->blocks->kobj);
 }
 
 static void __threshold_remove_blocks(struct threshold_bank *b)
@@ -1419,57 +1415,56 @@ static void __threshold_remove_blocks(struct threshold_bank *b)
                kobject_del(&pos->kobj);
 }
 
-static void threshold_remove_bank(unsigned int cpu, int bank)
+static void threshold_remove_bank(struct threshold_bank *bank)
 {
        struct amd_northbridge *nb;
-       struct threshold_bank *b;
 
-       b = per_cpu(threshold_banks, cpu)[bank];
-       if (!b)
-               return;
+       if (!bank->blocks)
+               goto out_free;
 
-       if (!b->blocks)
-               goto free_out;
+       if (!bank->shared)
+               goto out_dealloc;
 
-       if (is_shared_bank(bank)) {
-               if (!refcount_dec_and_test(&b->cpus)) {
-                       __threshold_remove_blocks(b);
-                       per_cpu(threshold_banks, cpu)[bank] = NULL;
-                       return;
-               } else {
-                       /*
-                        * the last CPU on this node using the shared bank is
-                        * going away, remove that bank now.
-                        */
-                       nb = node_to_amd_nb(amd_get_nb_id(cpu));
-                       nb->bank4 = NULL;
-               }
+       if (!refcount_dec_and_test(&bank->cpus)) {
+               __threshold_remove_blocks(bank);
+               return;
+       } else {
+               /*
+                * The last CPU on this node using the shared bank is going
+                * away, remove that bank now.
+                */
+               nb = node_to_amd_nb(amd_get_nb_id(smp_processor_id()));
+               nb->bank4 = NULL;
        }
 
-       deallocate_threshold_block(cpu, bank);
+out_dealloc:
+       deallocate_threshold_blocks(bank);
 
-free_out:
-       kobject_del(b->kobj);
-       kobject_put(b->kobj);
-       kfree(b);
-       per_cpu(threshold_banks, cpu)[bank] = NULL;
+out_free:
+       kobject_put(bank->kobj);
+       kfree(bank);
 }
 
 int mce_threshold_remove_device(unsigned int cpu)
 {
        struct threshold_bank **bp = this_cpu_read(threshold_banks);
-       unsigned int bank;
+       unsigned int bank, numbanks = this_cpu_read(mce_num_banks);
 
        if (!bp)
                return 0;
 
-       for (bank = 0; bank < per_cpu(mce_num_banks, cpu); ++bank) {
-               if (!(per_cpu(bank_map, cpu) & (1 << bank)))
-                       continue;
-               threshold_remove_bank(cpu, bank);
-       }
-       /* Clear the pointer before freeing the memory */
+       /*
+        * Clear the pointer before cleaning up, so that the interrupt won't
+        * touch anything of this.
+        */
        this_cpu_write(threshold_banks, NULL);
+
+       for (bank = 0; bank < numbanks; bank++) {
+               if (bp[bank]) {
+                       threshold_remove_bank(bp[bank]);
+                       bp[bank] = NULL;
+               }
+       }
        kfree(bp);
        return 0;
 }