]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - arch/x86/kernel/cpu/mcheck/mce_amd.c
x86, MCE, AMD: Print decimal thresholding values
[mirror_ubuntu-jammy-kernel.git] / arch / x86 / kernel / cpu / mcheck / mce_amd.c
index f4873a64f46dcb2dac8db0c3585afecc642e70ee..0b1bb0e158810c0d8f0aec6376410daf2433542f 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/cpu.h>
 #include <linux/smp.h>
 
+#include <asm/amd_nb.h>
 #include <asm/apic.h>
 #include <asm/idle.h>
 #include <asm/mce.h>
 #define MASK_BLKPTR_LO    0xFF000000
 #define MCG_XBLK_ADDR     0xC0000400
 
-struct threshold_block {
-       unsigned int            block;
-       unsigned int            bank;
-       unsigned int            cpu;
-       u32                     address;
-       u16                     interrupt_enable;
-       bool                    interrupt_capable;
-       u16                     threshold_limit;
-       struct kobject          kobj;
-       struct list_head        miscj;
-};
-
-struct threshold_bank {
-       struct kobject          *kobj;
-       struct threshold_block  *blocks;
-       cpumask_var_t           cpus;
-};
 static DEFINE_PER_CPU(struct threshold_bank * [NR_BANKS], threshold_banks);
 
 static unsigned char shared_bank[NR_BANKS] = {
@@ -224,8 +208,6 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
 
                        if (!block)
                                per_cpu(bank_map, cpu) |= (1 << bank);
-                       if (shared_bank[bank] && c->cpu_core_id)
-                               break;
 
                        memset(&b, 0, sizeof(b));
                        b.cpu                   = cpu;
@@ -326,7 +308,7 @@ struct threshold_attr {
 #define SHOW_FIELDS(name)                                              \
 static ssize_t show_ ## name(struct threshold_block *b, char *buf)     \
 {                                                                      \
-       return sprintf(buf, "%lx\n", (unsigned long) b->name);          \
+       return sprintf(buf, "%lu\n", (unsigned long) b->name);          \
 }
 SHOW_FIELDS(interrupt_enable)
 SHOW_FIELDS(threshold_limit)
@@ -397,7 +379,7 @@ static ssize_t show_error_count(struct threshold_block *b, char *buf)
        struct threshold_block_cross_cpu tbcc = { .tb = b, };
 
        smp_call_function_single(b->cpu, local_error_count_handler, &tbcc, 1);
-       return sprintf(buf, "%lx\n", tbcc.retval);
+       return sprintf(buf, "%lu\n", tbcc.retval);
 }
 
 static ssize_t store_error_count(struct threshold_block *b,
@@ -548,98 +530,93 @@ out_free:
        return err;
 }
 
-static __cpuinit long
-local_allocate_threshold_blocks(int cpu, unsigned int bank)
+static __cpuinit int __threshold_add_blocks(struct threshold_bank *b)
 {
-       return allocate_threshold_blocks(cpu, bank, 0,
-                                        MSR_IA32_MC0_MISC + bank * 4);
+       struct list_head *head = &b->blocks->miscj;
+       struct threshold_block *pos = NULL;
+       struct threshold_block *tmp = NULL;
+       int err = 0;
+
+       err = kobject_add(&b->blocks->kobj, b->kobj, b->blocks->kobj.name);
+       if (err)
+               return err;
+
+       list_for_each_entry_safe(pos, tmp, head, miscj) {
+
+               err = kobject_add(&pos->kobj, b->kobj, pos->kobj.name);
+               if (err) {
+                       list_for_each_entry_safe_reverse(pos, tmp, head, miscj)
+                               kobject_del(&pos->kobj);
+
+                       return err;
+               }
+       }
+       return err;
 }
 
-/* symlinks sibling shared banks to first core.  first core owns dir/files. */
 static __cpuinit int threshold_create_bank(unsigned int cpu, unsigned int bank)
 {
-       int i, err = 0;
-       struct threshold_bank *b = NULL;
        struct device *dev = per_cpu(mce_device, cpu);
+       struct amd_northbridge *nb = NULL;
+       struct threshold_bank *b = NULL;
        char name[32];
+       int err = 0;
 
        sprintf(name, "threshold_bank%i", bank);
 
-#ifdef CONFIG_SMP
-       if (cpu_data(cpu).cpu_core_id && shared_bank[bank]) {   /* symlink */
-               i = cpumask_first(cpu_llc_shared_mask(cpu));
+       if (shared_bank[bank]) {
 
-               /* first core not up yet */
-               if (cpu_data(i).cpu_core_id)
-                       goto out;
+               nb = node_to_amd_nb(amd_get_nb_id(cpu));
+               WARN_ON(!nb);
 
-               /* already linked */
-               if (per_cpu(threshold_banks, cpu)[bank])
-                       goto out;
+               /* threshold descriptor already initialized on this node? */
+               if (nb->bank4) {
+                       /* yes, use it */
+                       b = nb->bank4;
+                       err = kobject_add(b->kobj, &dev->kobj, name);
+                       if (err)
+                               goto out;
 
-               b = per_cpu(threshold_banks, i)[bank];
+                       per_cpu(threshold_banks, cpu)[bank] = b;
+                       atomic_inc(&b->cpus);
 
-               if (!b)
-                       goto out;
+                       err = __threshold_add_blocks(b);
 
-               err = sysfs_create_link(&dev->kobj, b->kobj, name);
-               if (err)
                        goto out;
-
-               cpumask_copy(b->cpus, cpu_llc_shared_mask(cpu));
-               per_cpu(threshold_banks, cpu)[bank] = b;
-
-               goto out;
+               }
        }
-#endif
 
        b = kzalloc(sizeof(struct threshold_bank), GFP_KERNEL);
        if (!b) {
                err = -ENOMEM;
                goto out;
        }
-       if (!zalloc_cpumask_var(&b->cpus, GFP_KERNEL)) {
-               kfree(b);
-               err = -ENOMEM;
-               goto out;
-       }
 
        b->kobj = kobject_create_and_add(name, &dev->kobj);
-       if (!b->kobj)
+       if (!b->kobj) {
+               err = -EINVAL;
                goto out_free;
-
-#ifndef CONFIG_SMP
-       cpumask_setall(b->cpus);
-#else
-       cpumask_set_cpu(cpu, b->cpus);
-#endif
+       }
 
        per_cpu(threshold_banks, cpu)[bank] = b;
 
-       err = local_allocate_threshold_blocks(cpu, bank);
-       if (err)
-               goto out_free;
-
-       for_each_cpu(i, b->cpus) {
-               if (i == cpu)
-                       continue;
+       if (shared_bank[bank]) {
+               atomic_set(&b->cpus, 1);
 
-               dev = per_cpu(mce_device, i);
-               if (dev)
-                       err = sysfs_create_link(&dev->kobj,b->kobj, name);
-               if (err)
-                       goto out;
-
-               per_cpu(threshold_banks, i)[bank] = b;
+               /* nb is already initialized, see above */
+               WARN_ON(nb->bank4);
+               nb->bank4 = b;
        }
 
-       goto out;
+       err = allocate_threshold_blocks(cpu, bank, 0,
+                                       MSR_IA32_MC0_MISC + bank * 4);
+       if (!err)
+               goto out;
 
-out_free:
-       per_cpu(threshold_banks, cpu)[bank] = NULL;
-       free_cpumask_var(b->cpus);
+ out_free:
        kfree(b);
-out:
+
+ out:
        return err;
 }
 
@@ -660,12 +637,6 @@ static __cpuinit int threshold_create_device(unsigned int cpu)
        return err;
 }
 
-/*
- * let's be hotplug friendly.
- * in case of multiple core processors, the first core always takes ownership
- *   of shared sysfs dir/files, and rest of the cores will be symlinked to it.
- */
-
 static void deallocate_threshold_block(unsigned int cpu,
                                                 unsigned int bank)
 {
@@ -686,41 +657,42 @@ static void deallocate_threshold_block(unsigned int cpu,
        per_cpu(threshold_banks, cpu)[bank]->blocks = NULL;
 }
 
+static void __threshold_remove_blocks(struct threshold_bank *b)
+{
+       struct threshold_block *pos = NULL;
+       struct threshold_block *tmp = NULL;
+
+       kobject_del(b->kobj);
+
+       list_for_each_entry_safe(pos, tmp, &b->blocks->miscj, miscj)
+               kobject_del(&pos->kobj);
+}
+
 static void threshold_remove_bank(unsigned int cpu, int bank)
 {
+       struct amd_northbridge *nb;
        struct threshold_bank *b;
-       struct device *dev;
-       char name[32];
-       int i = 0;
 
        b = per_cpu(threshold_banks, cpu)[bank];
        if (!b)
                return;
+
        if (!b->blocks)
                goto free_out;
 
-       sprintf(name, "threshold_bank%i", bank);
-
-#ifdef CONFIG_SMP
-       /* sibling symlink */
-       if (shared_bank[bank] && b->blocks->cpu != cpu) {
-               dev = per_cpu(mce_device, cpu);
-               sysfs_remove_link(&dev->kobj, name);
-               per_cpu(threshold_banks, cpu)[bank] = NULL;
-
-               return;
-       }
-#endif
-
-       /* remove all sibling symlinks before unregistering */
-       for_each_cpu(i, b->cpus) {
-               if (i == cpu)
-                       continue;
-
-               dev = per_cpu(mce_device, i);
-               if (dev)
-                       sysfs_remove_link(&dev->kobj, name);
-               per_cpu(threshold_banks, i)[bank] = NULL;
+       if (shared_bank[bank]) {
+               if (!atomic_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;
+               }
        }
 
        deallocate_threshold_block(cpu, bank);
@@ -728,7 +700,6 @@ static void threshold_remove_bank(unsigned int cpu, int bank)
 free_out:
        kobject_del(b->kobj);
        kobject_put(b->kobj);
-       free_cpumask_var(b->cpus);
        kfree(b);
        per_cpu(threshold_banks, cpu)[bank] = NULL;
 }