Skip to content

Commit

Permalink
x86/mce/amd: Cleanup threshold device remove path
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Thomas Gleixner authored and Borislav Petkov committed Apr 14, 2020
1 parent 6458de9 commit f26d258
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 42 deletions.
1 change: 1 addition & 0 deletions arch/x86/include/asm/amd_nb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
79 changes: 37 additions & 42 deletions arch/x86/kernel/cpu/mce/amd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down

0 comments on commit f26d258

Please sign in to comment.