162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CPU subsystem support 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/cpu.h> 1162306a36Sopenharmony_ci#include <linux/topology.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/node.h> 1462306a36Sopenharmony_ci#include <linux/gfp.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/percpu.h> 1762306a36Sopenharmony_ci#include <linux/acpi.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/cpufeature.h> 2062306a36Sopenharmony_ci#include <linux/tick.h> 2162306a36Sopenharmony_ci#include <linux/pm_qos.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/sched/isolation.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "base.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct device *, cpu_sys_devices); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int cpu_subsys_match(struct device *dev, struct device_driver *drv) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci /* ACPI style match is the only one that may succeed. */ 3262306a36Sopenharmony_ci if (acpi_driver_match_device(dev, drv)) 3362306a36Sopenharmony_ci return 1; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3962306a36Sopenharmony_cistatic void change_cpu_under_node(struct cpu *cpu, 4062306a36Sopenharmony_ci unsigned int from_nid, unsigned int to_nid) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int cpuid = cpu->dev.id; 4362306a36Sopenharmony_ci unregister_cpu_under_node(cpuid, from_nid); 4462306a36Sopenharmony_ci register_cpu_under_node(cpuid, to_nid); 4562306a36Sopenharmony_ci cpu->node_id = to_nid; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int cpu_subsys_online(struct device *dev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct cpu *cpu = container_of(dev, struct cpu, dev); 5162306a36Sopenharmony_ci int cpuid = dev->id; 5262306a36Sopenharmony_ci int from_nid, to_nid; 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci int retries = 0; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci from_nid = cpu_to_node(cpuid); 5762306a36Sopenharmony_ci if (from_nid == NUMA_NO_NODE) 5862306a36Sopenharmony_ci return -ENODEV; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciretry: 6162306a36Sopenharmony_ci ret = cpu_device_up(dev); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * If -EBUSY is returned, it is likely that hotplug is temporarily 6562306a36Sopenharmony_ci * disabled when cpu_hotplug_disable() was called. This condition is 6662306a36Sopenharmony_ci * transient. So we retry after waiting for an exponentially 6762306a36Sopenharmony_ci * increasing delay up to a total of at least 620ms as some PCI 6862306a36Sopenharmony_ci * device initialization can take quite a while. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci if (ret == -EBUSY) { 7162306a36Sopenharmony_ci retries++; 7262306a36Sopenharmony_ci if (retries > 5) 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci msleep(10 * (1 << retries)); 7562306a36Sopenharmony_ci goto retry; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * When hot adding memory to memoryless node and enabling a cpu 8062306a36Sopenharmony_ci * on the node, node number of the cpu may internally change. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci to_nid = cpu_to_node(cpuid); 8362306a36Sopenharmony_ci if (from_nid != to_nid) 8462306a36Sopenharmony_ci change_cpu_under_node(cpu, from_nid, to_nid); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int cpu_subsys_offline(struct device *dev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return cpu_device_down(dev); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_civoid unregister_cpu(struct cpu *cpu) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int logical_cpu = cpu->dev.id; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci device_unregister(&cpu->dev); 10162306a36Sopenharmony_ci per_cpu(cpu_sys_devices, logical_cpu) = NULL; 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 10662306a36Sopenharmony_cistatic ssize_t cpu_probe_store(struct device *dev, 10762306a36Sopenharmony_ci struct device_attribute *attr, 10862306a36Sopenharmony_ci const char *buf, 10962306a36Sopenharmony_ci size_t count) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci ssize_t cnt; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = lock_device_hotplug_sysfs(); 11562306a36Sopenharmony_ci if (ret) 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cnt = arch_cpu_probe(buf, count); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci unlock_device_hotplug(); 12162306a36Sopenharmony_ci return cnt; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic ssize_t cpu_release_store(struct device *dev, 12562306a36Sopenharmony_ci struct device_attribute *attr, 12662306a36Sopenharmony_ci const char *buf, 12762306a36Sopenharmony_ci size_t count) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci ssize_t cnt; 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = lock_device_hotplug_sysfs(); 13362306a36Sopenharmony_ci if (ret) 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci cnt = arch_cpu_release(buf, count); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci unlock_device_hotplug(); 13962306a36Sopenharmony_ci return cnt; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); 14362306a36Sopenharmony_cistatic DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); 14462306a36Sopenharmony_ci#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 14562306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 14862306a36Sopenharmony_ci#include <linux/kexec.h> 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic ssize_t crash_notes_show(struct device *dev, 15162306a36Sopenharmony_ci struct device_attribute *attr, 15262306a36Sopenharmony_ci char *buf) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct cpu *cpu = container_of(dev, struct cpu, dev); 15562306a36Sopenharmony_ci unsigned long long addr; 15662306a36Sopenharmony_ci int cpunum; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci cpunum = cpu->dev.id; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Might be reading other cpu's data based on which cpu read thread 16262306a36Sopenharmony_ci * has been scheduled. But cpu data (memory) is allocated once during 16362306a36Sopenharmony_ci * boot up and this data does not change there after. Hence this 16462306a36Sopenharmony_ci * operation should be safe. No locking required. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return sysfs_emit(buf, "%llx\n", addr); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_notes); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic ssize_t crash_notes_size_show(struct device *dev, 17362306a36Sopenharmony_ci struct device_attribute *attr, 17462306a36Sopenharmony_ci char *buf) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci return sysfs_emit(buf, "%zu\n", sizeof(note_buf_t)); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_notes_size); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct attribute *crash_note_cpu_attrs[] = { 18162306a36Sopenharmony_ci &dev_attr_crash_notes.attr, 18262306a36Sopenharmony_ci &dev_attr_crash_notes_size.attr, 18362306a36Sopenharmony_ci NULL 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic const struct attribute_group crash_note_cpu_attr_group = { 18762306a36Sopenharmony_ci .attrs = crash_note_cpu_attrs, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT 19262306a36Sopenharmony_cistatic ssize_t isolate_show(struct device *dev, 19362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct cpu *cpu = container_of(dev, struct cpu, dev); 19662306a36Sopenharmony_ci ssize_t rc; 19762306a36Sopenharmony_ci int cpuid = cpu->dev.id; 19862306a36Sopenharmony_ci unsigned int isolated = cpu_isolated(cpuid); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rc = sysfs_emit(buf, "%d\n", isolated); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return rc; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(isolate); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic struct attribute *cpu_isolated_attrs[] = { 20862306a36Sopenharmony_ci &dev_attr_isolate.attr, 20962306a36Sopenharmony_ci NULL 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic struct attribute_group cpu_isolated_attr_group = { 21362306a36Sopenharmony_ci .attrs = cpu_isolated_attrs, 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci#endif 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct attribute_group *common_cpu_attr_groups[] = { 21862306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 21962306a36Sopenharmony_ci &crash_note_cpu_attr_group, 22062306a36Sopenharmony_ci#endif 22162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT 22262306a36Sopenharmony_ci &cpu_isolated_attr_group, 22362306a36Sopenharmony_ci#endif 22462306a36Sopenharmony_ci NULL 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const struct attribute_group *hotplugable_cpu_attr_groups[] = { 22862306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 22962306a36Sopenharmony_ci &crash_note_cpu_attr_group, 23062306a36Sopenharmony_ci#endif 23162306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT 23262306a36Sopenharmony_ci &cpu_isolated_attr_group, 23362306a36Sopenharmony_ci#endif 23462306a36Sopenharmony_ci NULL 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * Print cpu online, possible, present, and system maps 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistruct cpu_attr { 24262306a36Sopenharmony_ci struct device_attribute attr; 24362306a36Sopenharmony_ci const struct cpumask *const map; 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic ssize_t show_cpus_attr(struct device *dev, 24762306a36Sopenharmony_ci struct device_attribute *attr, 24862306a36Sopenharmony_ci char *buf) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, ca->map); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#define _CPU_ATTR(name, map) \ 25662306a36Sopenharmony_ci { __ATTR(name, 0444, show_cpus_attr, NULL), map } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Keep in sync with cpu_subsys_attrs */ 25962306a36Sopenharmony_cistatic struct cpu_attr cpu_attrs[] = { 26062306a36Sopenharmony_ci _CPU_ATTR(online, &__cpu_online_mask), 26162306a36Sopenharmony_ci _CPU_ATTR(possible, &__cpu_possible_mask), 26262306a36Sopenharmony_ci _CPU_ATTR(present, &__cpu_present_mask), 26362306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT 26462306a36Sopenharmony_ci _CPU_ATTR(core_ctl_isolated, &__cpu_isolated_mask), 26562306a36Sopenharmony_ci#endif 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * Print values for NR_CPUS and offlined cpus 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic ssize_t print_cpus_kernel_max(struct device *dev, 27262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", NR_CPUS - 1); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_cistatic DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ 27962306a36Sopenharmony_ciunsigned int total_cpus; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic ssize_t print_cpus_offline(struct device *dev, 28262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int len = 0; 28562306a36Sopenharmony_ci cpumask_var_t offline; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* display offline cpus < nr_cpu_ids */ 28862306a36Sopenharmony_ci if (!alloc_cpumask_var(&offline, GFP_KERNEL)) 28962306a36Sopenharmony_ci return -ENOMEM; 29062306a36Sopenharmony_ci cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask); 29162306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%*pbl", cpumask_pr_args(offline)); 29262306a36Sopenharmony_ci free_cpumask_var(offline); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* display offline cpus >= nr_cpu_ids */ 29562306a36Sopenharmony_ci if (total_cpus && nr_cpu_ids < total_cpus) { 29662306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, ","); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (nr_cpu_ids == total_cpus-1) 29962306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%u", nr_cpu_ids); 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%u-%d", 30262306a36Sopenharmony_ci nr_cpu_ids, total_cpus - 1); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "\n"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return len; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_cistatic DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic ssize_t print_cpus_isolated(struct device *dev, 31262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int len; 31562306a36Sopenharmony_ci cpumask_var_t isolated; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!alloc_cpumask_var(&isolated, GFP_KERNEL)) 31862306a36Sopenharmony_ci return -ENOMEM; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci cpumask_andnot(isolated, cpu_possible_mask, 32162306a36Sopenharmony_ci housekeeping_cpumask(HK_TYPE_DOMAIN)); 32262306a36Sopenharmony_ci len = sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(isolated)); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci free_cpumask_var(isolated); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return len; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_cistatic DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci#ifdef CONFIG_NO_HZ_FULL 33162306a36Sopenharmony_cistatic ssize_t print_cpus_nohz_full(struct device *dev, 33262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_cistatic DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL); 33762306a36Sopenharmony_ci#endif 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG 34062306a36Sopenharmony_cistatic ssize_t crash_hotplug_show(struct device *dev, 34162306a36Sopenharmony_ci struct device_attribute *attr, 34262306a36Sopenharmony_ci char *buf) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", crash_hotplug_cpu_support()); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(crash_hotplug); 34762306a36Sopenharmony_ci#endif 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void cpu_device_release(struct device *dev) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * This is an empty function to prevent the driver core from spitting a 35362306a36Sopenharmony_ci * warning at us. Yes, I know this is directly opposite of what the 35462306a36Sopenharmony_ci * documentation for the driver core and kobjects say, and the author 35562306a36Sopenharmony_ci * of this code has already been publically ridiculed for doing 35662306a36Sopenharmony_ci * something as foolish as this. However, at this point in time, it is 35762306a36Sopenharmony_ci * the only way to handle the issue of statically allocated cpu 35862306a36Sopenharmony_ci * devices. The different architectures will have their cpu device 35962306a36Sopenharmony_ci * code reworked to properly handle this in the near future, so this 36062306a36Sopenharmony_ci * function will then be changed to correctly free up the memory held 36162306a36Sopenharmony_ci * by the cpu device. 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * Never copy this way of doing things, or you too will be made fun of 36462306a36Sopenharmony_ci * on the linux-kernel list, you have been warned. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE 36962306a36Sopenharmony_cistatic ssize_t print_cpu_modalias(struct device *dev, 37062306a36Sopenharmony_ci struct device_attribute *attr, 37162306a36Sopenharmony_ci char *buf) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci int len = 0; 37462306a36Sopenharmony_ci u32 i; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, 37762306a36Sopenharmony_ci "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:", 37862306a36Sopenharmony_ci CPU_FEATURE_TYPEVAL); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < MAX_CPU_FEATURES; i++) 38162306a36Sopenharmony_ci if (cpu_have_feature(i)) { 38262306a36Sopenharmony_ci if (len + sizeof(",XXXX\n") >= PAGE_SIZE) { 38362306a36Sopenharmony_ci WARN(1, "CPU features overflow page\n"); 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, ",%04X", i); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "\n"); 38962306a36Sopenharmony_ci return len; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 39562306a36Sopenharmony_ci if (buf) { 39662306a36Sopenharmony_ci print_cpu_modalias(NULL, NULL, buf); 39762306a36Sopenharmony_ci add_uevent_var(env, "MODALIAS=%s", buf); 39862306a36Sopenharmony_ci kfree(buf); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci#endif 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistruct bus_type cpu_subsys = { 40562306a36Sopenharmony_ci .name = "cpu", 40662306a36Sopenharmony_ci .dev_name = "cpu", 40762306a36Sopenharmony_ci .match = cpu_subsys_match, 40862306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 40962306a36Sopenharmony_ci .online = cpu_subsys_online, 41062306a36Sopenharmony_ci .offline = cpu_subsys_offline, 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE 41362306a36Sopenharmony_ci .uevent = cpu_uevent, 41462306a36Sopenharmony_ci#endif 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_subsys); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * register_cpu - Setup a sysfs device for a CPU. 42062306a36Sopenharmony_ci * @cpu - cpu->hotpluggable field set to 1 will generate a control file in 42162306a36Sopenharmony_ci * sysfs for this CPU. 42262306a36Sopenharmony_ci * @num - CPU number to use when creating the device. 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * Initialize and register the CPU device. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ciint register_cpu(struct cpu *cpu, int num) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci int error; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cpu->node_id = cpu_to_node(num); 43162306a36Sopenharmony_ci memset(&cpu->dev, 0x00, sizeof(struct device)); 43262306a36Sopenharmony_ci cpu->dev.id = num; 43362306a36Sopenharmony_ci cpu->dev.bus = &cpu_subsys; 43462306a36Sopenharmony_ci cpu->dev.release = cpu_device_release; 43562306a36Sopenharmony_ci cpu->dev.offline_disabled = !cpu->hotpluggable; 43662306a36Sopenharmony_ci cpu->dev.offline = !cpu_online(num); 43762306a36Sopenharmony_ci cpu->dev.of_node = of_get_cpu_node(num, NULL); 43862306a36Sopenharmony_ci cpu->dev.groups = common_cpu_attr_groups; 43962306a36Sopenharmony_ci if (cpu->hotpluggable) 44062306a36Sopenharmony_ci cpu->dev.groups = hotplugable_cpu_attr_groups; 44162306a36Sopenharmony_ci error = device_register(&cpu->dev); 44262306a36Sopenharmony_ci if (error) { 44362306a36Sopenharmony_ci put_device(&cpu->dev); 44462306a36Sopenharmony_ci return error; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci per_cpu(cpu_sys_devices, num) = &cpu->dev; 44862306a36Sopenharmony_ci register_cpu_under_node(num, cpu_to_node(num)); 44962306a36Sopenharmony_ci dev_pm_qos_expose_latency_limit(&cpu->dev, 45062306a36Sopenharmony_ci PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistruct device *get_cpu_device(unsigned int cpu) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci if (cpu < nr_cpu_ids && cpu_possible(cpu)) 45862306a36Sopenharmony_ci return per_cpu(cpu_sys_devices, cpu); 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci return NULL; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_cpu_device); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void device_create_release(struct device *dev) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci kfree(dev); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci__printf(4, 0) 47062306a36Sopenharmony_cistatic struct device * 47162306a36Sopenharmony_ci__cpu_device_create(struct device *parent, void *drvdata, 47262306a36Sopenharmony_ci const struct attribute_group **groups, 47362306a36Sopenharmony_ci const char *fmt, va_list args) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct device *dev = NULL; 47662306a36Sopenharmony_ci int retval = -ENOMEM; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 47962306a36Sopenharmony_ci if (!dev) 48062306a36Sopenharmony_ci goto error; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci device_initialize(dev); 48362306a36Sopenharmony_ci dev->parent = parent; 48462306a36Sopenharmony_ci dev->groups = groups; 48562306a36Sopenharmony_ci dev->release = device_create_release; 48662306a36Sopenharmony_ci device_set_pm_not_required(dev); 48762306a36Sopenharmony_ci dev_set_drvdata(dev, drvdata); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci retval = kobject_set_name_vargs(&dev->kobj, fmt, args); 49062306a36Sopenharmony_ci if (retval) 49162306a36Sopenharmony_ci goto error; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci retval = device_add(dev); 49462306a36Sopenharmony_ci if (retval) 49562306a36Sopenharmony_ci goto error; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return dev; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cierror: 50062306a36Sopenharmony_ci put_device(dev); 50162306a36Sopenharmony_ci return ERR_PTR(retval); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistruct device *cpu_device_create(struct device *parent, void *drvdata, 50562306a36Sopenharmony_ci const struct attribute_group **groups, 50662306a36Sopenharmony_ci const char *fmt, ...) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci va_list vargs; 50962306a36Sopenharmony_ci struct device *dev; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci va_start(vargs, fmt); 51262306a36Sopenharmony_ci dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs); 51362306a36Sopenharmony_ci va_end(vargs); 51462306a36Sopenharmony_ci return dev; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_device_create); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE 51962306a36Sopenharmony_cistatic DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); 52062306a36Sopenharmony_ci#endif 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct attribute *cpu_root_attrs[] = { 52362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 52462306a36Sopenharmony_ci &dev_attr_probe.attr, 52562306a36Sopenharmony_ci &dev_attr_release.attr, 52662306a36Sopenharmony_ci#endif 52762306a36Sopenharmony_ci &cpu_attrs[0].attr.attr, 52862306a36Sopenharmony_ci &cpu_attrs[1].attr.attr, 52962306a36Sopenharmony_ci &cpu_attrs[2].attr.attr, 53062306a36Sopenharmony_ci#ifdef CONFIG_CPU_ISOLATION_OPT 53162306a36Sopenharmony_ci &cpu_attrs[3].attr.attr, 53262306a36Sopenharmony_ci#endif 53362306a36Sopenharmony_ci &dev_attr_kernel_max.attr, 53462306a36Sopenharmony_ci &dev_attr_offline.attr, 53562306a36Sopenharmony_ci &dev_attr_isolated.attr, 53662306a36Sopenharmony_ci#ifdef CONFIG_NO_HZ_FULL 53762306a36Sopenharmony_ci &dev_attr_nohz_full.attr, 53862306a36Sopenharmony_ci#endif 53962306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG 54062306a36Sopenharmony_ci &dev_attr_crash_hotplug.attr, 54162306a36Sopenharmony_ci#endif 54262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_AUTOPROBE 54362306a36Sopenharmony_ci &dev_attr_modalias.attr, 54462306a36Sopenharmony_ci#endif 54562306a36Sopenharmony_ci NULL 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic const struct attribute_group cpu_root_attr_group = { 54962306a36Sopenharmony_ci .attrs = cpu_root_attrs, 55062306a36Sopenharmony_ci}; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic const struct attribute_group *cpu_root_attr_groups[] = { 55362306a36Sopenharmony_ci &cpu_root_attr_group, 55462306a36Sopenharmony_ci NULL, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cibool cpu_is_hotpluggable(unsigned int cpu) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct device *dev = get_cpu_device(cpu); 56062306a36Sopenharmony_ci return dev && container_of(dev, struct cpu, dev)->hotpluggable 56162306a36Sopenharmony_ci && tick_nohz_cpu_hotpluggable(cpu); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpu_is_hotpluggable); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_DEVICES 56662306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu, cpu_devices); 56762306a36Sopenharmony_ci#endif 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void __init cpu_dev_register_generic(void) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_DEVICES 57262306a36Sopenharmony_ci int i; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci for_each_possible_cpu(i) { 57562306a36Sopenharmony_ci if (register_cpu(&per_cpu(cpu_devices, i), i)) 57662306a36Sopenharmony_ci panic("Failed to register CPU device"); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci#endif 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES 58262306a36Sopenharmony_cistatic ssize_t cpu_show_not_affected(struct device *dev, 58362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci return sysfs_emit(buf, "Not affected\n"); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci#define CPU_SHOW_VULN_FALLBACK(func) \ 58962306a36Sopenharmony_ci ssize_t cpu_show_##func(struct device *, \ 59062306a36Sopenharmony_ci struct device_attribute *, char *) \ 59162306a36Sopenharmony_ci __attribute__((weak, alias("cpu_show_not_affected"))) 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(meltdown); 59462306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spectre_v1); 59562306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spectre_v2); 59662306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spec_store_bypass); 59762306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(l1tf); 59862306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(mds); 59962306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(tsx_async_abort); 60062306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(itlb_multihit); 60162306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(srbds); 60262306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(mmio_stale_data); 60362306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(retbleed); 60462306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(spec_rstack_overflow); 60562306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(gds); 60662306a36Sopenharmony_ciCPU_SHOW_VULN_FALLBACK(reg_file_data_sampling); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL); 60962306a36Sopenharmony_cistatic DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL); 61062306a36Sopenharmony_cistatic DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL); 61162306a36Sopenharmony_cistatic DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL); 61262306a36Sopenharmony_cistatic DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL); 61362306a36Sopenharmony_cistatic DEVICE_ATTR(mds, 0444, cpu_show_mds, NULL); 61462306a36Sopenharmony_cistatic DEVICE_ATTR(tsx_async_abort, 0444, cpu_show_tsx_async_abort, NULL); 61562306a36Sopenharmony_cistatic DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL); 61662306a36Sopenharmony_cistatic DEVICE_ATTR(srbds, 0444, cpu_show_srbds, NULL); 61762306a36Sopenharmony_cistatic DEVICE_ATTR(mmio_stale_data, 0444, cpu_show_mmio_stale_data, NULL); 61862306a36Sopenharmony_cistatic DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL); 61962306a36Sopenharmony_cistatic DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL); 62062306a36Sopenharmony_cistatic DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL); 62162306a36Sopenharmony_cistatic DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic struct attribute *cpu_root_vulnerabilities_attrs[] = { 62462306a36Sopenharmony_ci &dev_attr_meltdown.attr, 62562306a36Sopenharmony_ci &dev_attr_spectre_v1.attr, 62662306a36Sopenharmony_ci &dev_attr_spectre_v2.attr, 62762306a36Sopenharmony_ci &dev_attr_spec_store_bypass.attr, 62862306a36Sopenharmony_ci &dev_attr_l1tf.attr, 62962306a36Sopenharmony_ci &dev_attr_mds.attr, 63062306a36Sopenharmony_ci &dev_attr_tsx_async_abort.attr, 63162306a36Sopenharmony_ci &dev_attr_itlb_multihit.attr, 63262306a36Sopenharmony_ci &dev_attr_srbds.attr, 63362306a36Sopenharmony_ci &dev_attr_mmio_stale_data.attr, 63462306a36Sopenharmony_ci &dev_attr_retbleed.attr, 63562306a36Sopenharmony_ci &dev_attr_spec_rstack_overflow.attr, 63662306a36Sopenharmony_ci &dev_attr_gather_data_sampling.attr, 63762306a36Sopenharmony_ci &dev_attr_reg_file_data_sampling.attr, 63862306a36Sopenharmony_ci NULL 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic const struct attribute_group cpu_root_vulnerabilities_group = { 64262306a36Sopenharmony_ci .name = "vulnerabilities", 64362306a36Sopenharmony_ci .attrs = cpu_root_vulnerabilities_attrs, 64462306a36Sopenharmony_ci}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void __init cpu_register_vulnerabilities(void) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct device *dev = bus_get_dev_root(&cpu_subsys); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (dev) { 65162306a36Sopenharmony_ci if (sysfs_create_group(&dev->kobj, &cpu_root_vulnerabilities_group)) 65262306a36Sopenharmony_ci pr_err("Unable to register CPU vulnerabilities\n"); 65362306a36Sopenharmony_ci put_device(dev); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci#else 65862306a36Sopenharmony_cistatic inline void cpu_register_vulnerabilities(void) { } 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid __init cpu_dev_init(void) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups)) 66462306a36Sopenharmony_ci panic("Failed to register CPU subsystem"); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci cpu_dev_register_generic(); 66762306a36Sopenharmony_ci cpu_register_vulnerabilities(); 66862306a36Sopenharmony_ci} 669