diff options
Diffstat (limited to 'drivers/hwmon/coretemp.c')
-rw-r--r-- | drivers/hwmon/coretemp.c | 321 |
1 files changed, 113 insertions, 208 deletions
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 6a27eb2fed17..3ac4c03ba77b 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -51,6 +51,7 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); +#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */ #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ @@ -58,7 +59,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) -#define TO_PHYS_ID(cpu) (cpu_data(cpu).phys_proc_id) #define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) #define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) @@ -102,20 +102,17 @@ struct temp_data { /* Platform Data per Physical CPU */ struct platform_data { - struct device *hwmon_dev; - u16 phys_proc_id; - struct temp_data *core_data[MAX_CORE_DATA]; + struct device *hwmon_dev; + u16 pkg_id; + struct cpumask cpumask; + struct temp_data *core_data[MAX_CORE_DATA]; struct device_attribute name_attr; }; -struct pdev_entry { - struct list_head list; - struct platform_device *pdev; - u16 phys_proc_id; -}; - -static LIST_HEAD(pdev_list); -static DEFINE_MUTEX(pdev_list_mutex); +/* Keep track of how many package pointers we allocated in init() */ +static int max_packages __read_mostly; +/* Array of package pointers. Serialized by cpu hotplug lock */ +static struct platform_device **pkg_devices; static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) @@ -125,7 +122,7 @@ static ssize_t show_label(struct device *dev, struct temp_data *tdata = pdata->core_data[attr->index]; if (tdata->is_pkg_data) - return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id); + return sprintf(buf, "Package id %u\n", pdata->pkg_id); return sprintf(buf, "Core %u\n", tdata->cpu_core_id); } @@ -138,7 +135,9 @@ static ssize_t show_crit_alarm(struct device *dev, struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = pdata->core_data[attr->index]; + mutex_lock(&tdata->update_lock); rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + mutex_unlock(&tdata->update_lock); return sprintf(buf, "%d\n", (eax >> 5) & 1); } @@ -435,18 +434,10 @@ static int chk_ucode_version(unsigned int cpu) static struct platform_device *coretemp_get_pdev(unsigned int cpu) { - u16 phys_proc_id = TO_PHYS_ID(cpu); - struct pdev_entry *p; - - mutex_lock(&pdev_list_mutex); + int pkgid = topology_logical_package_id(cpu); - list_for_each_entry(p, &pdev_list, list) - if (p->phys_proc_id == phys_proc_id) { - mutex_unlock(&pdev_list_mutex); - return p->pdev; - } - - mutex_unlock(&pdev_list_mutex); + if (pkgid >= 0 && pkgid < max_packages) + return pkg_devices[pkgid]; return NULL; } @@ -483,21 +474,11 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, * The attr number is always core id + 2 * The Pkgtemp will always show up as temp1_*, if available */ - attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu); + attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); if (attr_no > MAX_CORE_DATA - 1) return -ERANGE; - /* - * Provide a single set of attributes for all HT siblings of a core - * to avoid duplicate sensors (the processor ID and core ID of all - * HT siblings of a core are the same). - * Skip if a HT sibling of this core is already registered. - * This is not an error. - */ - if (pdata->core_data[attr_no] != NULL) - return 0; - tdata = init_temp_data(cpu, pkg_flag); if (!tdata) return -ENOMEM; @@ -539,21 +520,14 @@ exit_free: return err; } -static void coretemp_add_core(unsigned int cpu, int pkg_flag) +static void +coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag) { - struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; - - if (!pdev) - return; - - err = create_core_data(pdev, cpu, pkg_flag); - if (err) + if (create_core_data(pdev, cpu, pkg_flag)) dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); } -static void coretemp_remove_core(struct platform_data *pdata, - int indx) +static void coretemp_remove_core(struct platform_data *pdata, int indx) { struct temp_data *tdata = pdata->core_data[indx]; @@ -574,7 +548,7 @@ static int coretemp_probe(struct platform_device *pdev) if (!pdata) return -ENOMEM; - pdata->phys_proc_id = pdev->id; + pdata->pkg_id = pdev->id; platform_set_drvdata(pdev, pdata); pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, @@ -602,85 +576,33 @@ static struct platform_driver coretemp_driver = { .remove = coretemp_remove, }; -static int coretemp_device_add(unsigned int cpu) +static struct platform_device *coretemp_device_add(unsigned int cpu) { - int err; + int err, pkgid = topology_logical_package_id(cpu); struct platform_device *pdev; - struct pdev_entry *pdev_entry; - mutex_lock(&pdev_list_mutex); + if (pkgid < 0) + return ERR_PTR(-ENOMEM); - pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit; - } - - pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); - if (!pdev_entry) { - err = -ENOMEM; - goto exit_device_put; - } + pdev = platform_device_alloc(DRVNAME, pkgid); + if (!pdev) + return ERR_PTR(-ENOMEM); err = platform_device_add(pdev); if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_free; - } - - pdev_entry->pdev = pdev; - pdev_entry->phys_proc_id = pdev->id; - - list_add_tail(&pdev_entry->list, &pdev_list); - mutex_unlock(&pdev_list_mutex); - - return 0; - -exit_device_free: - kfree(pdev_entry); -exit_device_put: - platform_device_put(pdev); -exit: - mutex_unlock(&pdev_list_mutex); - return err; -} - -static void coretemp_device_remove(unsigned int cpu) -{ - struct pdev_entry *p, *n; - u16 phys_proc_id = TO_PHYS_ID(cpu); - - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - if (p->phys_proc_id != phys_proc_id) - continue; - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); + platform_device_put(pdev); + return ERR_PTR(err); } - mutex_unlock(&pdev_list_mutex); -} - -static bool is_any_core_online(struct platform_data *pdata) -{ - int i; - /* Find online cores, except pkgtemp data */ - for (i = MAX_CORE_DATA - 1; i >= 0; --i) { - if (pdata->core_data[i] && - !pdata->core_data[i]->is_pkg_data) { - return true; - } - } - return false; + pkg_devices[pkgid] = pdev; + return pdev; } -static void get_core_online(unsigned int cpu) +static int coretemp_cpu_online(unsigned int cpu) { - struct cpuinfo_x86 *c = &cpu_data(cpu); struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; + struct cpuinfo_x86 *c = &cpu_data(cpu); + struct platform_data *pdata; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -688,12 +610,12 @@ static void get_core_online(unsigned int cpu) * without thermal sensors will be filtered out. */ if (!cpu_has(c, X86_FEATURE_DTHERM)) - return; + return -ENODEV; if (!pdev) { /* Check the microcode version of the CPU */ if (chk_ucode_version(cpu)) - return; + return -EINVAL; /* * Alright, we have DTS support. @@ -701,101 +623,100 @@ static void get_core_online(unsigned int cpu) * online. So, initialize per-pkg data structures and * then bring this core online. */ - err = coretemp_device_add(cpu); - if (err) - return; + pdev = coretemp_device_add(cpu); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + /* * Check whether pkgtemp support is available. * If so, add interfaces for pkgtemp. */ if (cpu_has(c, X86_FEATURE_PTS)) - coretemp_add_core(cpu, 1); + coretemp_add_core(pdev, cpu, 1); } + + pdata = platform_get_drvdata(pdev); /* - * Physical CPU device already exists. - * So, just add interfaces for this core. + * Check whether a thread sibling is already online. If not add the + * interface for this CPU core. */ - coretemp_add_core(cpu, 0); + if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu))) + coretemp_add_core(pdev, cpu, 0); + + cpumask_set_cpu(cpu, &pdata->cpumask); + return 0; } -static void put_core_offline(unsigned int cpu) +static int coretemp_cpu_offline(unsigned int cpu) { - int i, indx; - struct platform_data *pdata; struct platform_device *pdev = coretemp_get_pdev(cpu); + struct platform_data *pd; + struct temp_data *tdata; + int indx, target; /* If the physical CPU device does not exist, just return */ if (!pdev) - return; - - pdata = platform_get_drvdata(pdev); - - indx = TO_ATTR_NO(cpu); + return 0; /* The core id is too big, just return */ + indx = TO_ATTR_NO(cpu); if (indx > MAX_CORE_DATA - 1) - return; + return 0; - if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) - coretemp_remove_core(pdata, indx); + pd = platform_get_drvdata(pdev); + tdata = pd->core_data[indx]; + + cpumask_clear_cpu(cpu, &pd->cpumask); /* - * If a HT sibling of a core is taken offline, but another HT sibling - * of the same core is still online, register the alternate sibling. - * This ensures that exactly one set of attributes is provided as long - * as at least one HT sibling of a core is online. + * If this is the last thread sibling, remove the CPU core + * interface, If there is still a sibling online, transfer the + * target cpu of that core interface to it. */ - for_each_sibling(i, cpu) { - if (i != cpu) { - get_core_online(i); - /* - * Display temperature sensor data for one HT sibling - * per core only, so abort the loop after one such - * sibling has been found. - */ - break; - } + target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu)); + if (target >= nr_cpu_ids) { + coretemp_remove_core(pd, indx); + } else if (tdata && tdata->cpu == cpu) { + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } + /* - * If all cores in this pkg are offline, remove the device. - * coretemp_device_remove calls unregister_platform_device, - * which in turn calls coretemp_remove. This removes the - * pkgtemp entry and does other clean ups. + * If all cores in this pkg are offline, remove the device. This + * will invoke the platform driver remove function, which cleans up + * the rest. */ - if (!is_any_core_online(pdata)) - coretemp_device_remove(cpu); -} + if (cpumask_empty(&pd->cpumask)) { + pkg_devices[topology_logical_package_id(cpu)] = NULL; + platform_device_unregister(pdev); + return 0; + } -static int coretemp_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long) hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - get_core_online(cpu); - break; - case CPU_DOWN_PREPARE: - put_core_offline(cpu); - break; + /* + * Check whether this core is the target for the package + * interface. We need to assign it to some other cpu. + */ + tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; + if (tdata && tdata->cpu == cpu) { + target = cpumask_first(&pd->cpumask); + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } - return NOTIFY_OK; + return 0; } - -static struct notifier_block coretemp_cpu_notifier __refdata = { - .notifier_call = coretemp_cpu_callback, -}; - static const struct x86_cpu_id __initconst coretemp_ids[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, {} }; MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); +static enum cpuhp_state coretemp_hp_online; + static int __init coretemp_init(void) { - int i, err; + int err; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -805,54 +726,38 @@ static int __init coretemp_init(void) if (!x86_match_cpu(coretemp_ids)) return -ENODEV; + max_packages = topology_max_packages(); + pkg_devices = kzalloc(max_packages * sizeof(struct platform_device *), + GFP_KERNEL); + if (!pkg_devices) + return -ENOMEM; + err = platform_driver_register(&coretemp_driver); if (err) - goto exit; + return err; - cpu_notifier_register_begin(); - for_each_online_cpu(i) - get_core_online(i); - -#ifndef CONFIG_HOTPLUG_CPU - if (list_empty(&pdev_list)) { - cpu_notifier_register_done(); - err = -ENODEV; - goto exit_driver_unreg; - } -#endif - - __register_hotcpu_notifier(&coretemp_cpu_notifier); - cpu_notifier_register_done(); + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", + coretemp_cpu_online, coretemp_cpu_offline); + if (err < 0) + goto outdrv; + coretemp_hp_online = err; return 0; -#ifndef CONFIG_HOTPLUG_CPU -exit_driver_unreg: +outdrv: platform_driver_unregister(&coretemp_driver); -#endif -exit: + kfree(pkg_devices); return err; } +module_init(coretemp_init) static void __exit coretemp_exit(void) { - struct pdev_entry *p, *n; - - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&coretemp_cpu_notifier); - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); - cpu_notifier_register_done(); + cpuhp_remove_state(coretemp_hp_online); platform_driver_unregister(&coretemp_driver); + kfree(pkg_devices); } +module_exit(coretemp_exit) MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); MODULE_DESCRIPTION("Intel Core temperature monitor"); MODULE_LICENSE("GPL"); - -module_init(coretemp_init) -module_exit(coretemp_exit) |