xref: /openbmc/linux/drivers/base/cpu.c (revision db4d30fb)
1989d42e8SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
38a25a2fdSKay Sievers  * CPU subsystem support
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
6024f7846SBen Hutchings #include <linux/kernel.h>
71da177e4SLinus Torvalds #include <linux/module.h>
81da177e4SLinus Torvalds #include <linux/init.h>
9f6a57033SAl Viro #include <linux/sched.h>
101da177e4SLinus Torvalds #include <linux/cpu.h>
111da177e4SLinus Torvalds #include <linux/topology.h>
121da177e4SLinus Torvalds #include <linux/device.h>
1376b67ed9SKAMEZAWA Hiroyuki #include <linux/node.h>
145a0e3ad6STejun Heo #include <linux/gfp.h>
15fad12ac8SThomas Renninger #include <linux/slab.h>
169f13a1fdSBen Hutchings #include <linux/percpu.h>
17ac212b69SRafael J. Wysocki #include <linux/acpi.h>
18f86e4718SSudeep KarkadaNagesha #include <linux/of.h>
1967bad2fdSArd Biesheuvel #include <linux/cpufeature.h>
206570a9a1SRik van Riel #include <linux/tick.h>
2137efa4b4SAlex Shi #include <linux/pm_qos.h>
22edb93821SFrederic Weisbecker #include <linux/sched/isolation.h>
231da177e4SLinus Torvalds 
24a1bdc7aaSBen Dooks #include "base.h"
251da177e4SLinus Torvalds 
268a25a2fdSKay Sievers static DEFINE_PER_CPU(struct device *, cpu_sys_devices);
27ad74557aSAshok Raj 
28ac212b69SRafael J. Wysocki static int cpu_subsys_match(struct device *dev, struct device_driver *drv)
29ac212b69SRafael J. Wysocki {
30ac212b69SRafael J. Wysocki 	/* ACPI style match is the only one that may succeed. */
31ac212b69SRafael J. Wysocki 	if (acpi_driver_match_device(dev, drv))
32ac212b69SRafael J. Wysocki 		return 1;
33ac212b69SRafael J. Wysocki 
34ac212b69SRafael J. Wysocki 	return 0;
35ac212b69SRafael J. Wysocki }
36ac212b69SRafael J. Wysocki 
371da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU
3834640468SYasuaki Ishimatsu static void change_cpu_under_node(struct cpu *cpu,
3934640468SYasuaki Ishimatsu 			unsigned int from_nid, unsigned int to_nid)
4034640468SYasuaki Ishimatsu {
4134640468SYasuaki Ishimatsu 	int cpuid = cpu->dev.id;
4234640468SYasuaki Ishimatsu 	unregister_cpu_under_node(cpuid, from_nid);
4334640468SYasuaki Ishimatsu 	register_cpu_under_node(cpuid, to_nid);
4434640468SYasuaki Ishimatsu 	cpu->node_id = to_nid;
4534640468SYasuaki Ishimatsu }
4634640468SYasuaki Ishimatsu 
47eda5867bSMathias Krause static int cpu_subsys_online(struct device *dev)
481da177e4SLinus Torvalds {
498a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
500902a904SRafael J. Wysocki 	int cpuid = dev->id;
5134640468SYasuaki Ishimatsu 	int from_nid, to_nid;
526dedcca6SToshi Kani 	int ret;
530902a904SRafael J. Wysocki 
5434640468SYasuaki Ishimatsu 	from_nid = cpu_to_node(cpuid);
55c7991b0bSRafael J. Wysocki 	if (from_nid == NUMA_NO_NODE)
566dedcca6SToshi Kani 		return -ENODEV;
57c7991b0bSRafael J. Wysocki 
5834640468SYasuaki Ishimatsu 	ret = cpu_up(cpuid);
5934640468SYasuaki Ishimatsu 	/*
6034640468SYasuaki Ishimatsu 	 * When hot adding memory to memoryless node and enabling a cpu
6134640468SYasuaki Ishimatsu 	 * on the node, node number of the cpu may internally change.
6234640468SYasuaki Ishimatsu 	 */
6334640468SYasuaki Ishimatsu 	to_nid = cpu_to_node(cpuid);
6434640468SYasuaki Ishimatsu 	if (from_nid != to_nid)
6534640468SYasuaki Ishimatsu 		change_cpu_under_node(cpu, from_nid, to_nid);
6634640468SYasuaki Ishimatsu 
671da177e4SLinus Torvalds 	return ret;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
700902a904SRafael J. Wysocki static int cpu_subsys_offline(struct device *dev)
711da177e4SLinus Torvalds {
726dedcca6SToshi Kani 	return cpu_down(dev->id);
731da177e4SLinus Torvalds }
741c4e2d70SIgor Mammedov 
7576b67ed9SKAMEZAWA Hiroyuki void unregister_cpu(struct cpu *cpu)
761da177e4SLinus Torvalds {
778a25a2fdSKay Sievers 	int logical_cpu = cpu->dev.id;
781da177e4SLinus Torvalds 
7976b67ed9SKAMEZAWA Hiroyuki 	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
8076b67ed9SKAMEZAWA Hiroyuki 
818a25a2fdSKay Sievers 	device_unregister(&cpu->dev);
82e37d05daSMike Travis 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
831da177e4SLinus Torvalds 	return;
841da177e4SLinus Torvalds }
8512633e80SNathan Fontenot 
8612633e80SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
878a25a2fdSKay Sievers static ssize_t cpu_probe_store(struct device *dev,
888a25a2fdSKay Sievers 			       struct device_attribute *attr,
8928812fe1SAndi Kleen 			       const char *buf,
9012633e80SNathan Fontenot 			       size_t count)
9112633e80SNathan Fontenot {
92574b851eSToshi Kani 	ssize_t cnt;
93574b851eSToshi Kani 	int ret;
94574b851eSToshi Kani 
95574b851eSToshi Kani 	ret = lock_device_hotplug_sysfs();
96574b851eSToshi Kani 	if (ret)
97574b851eSToshi Kani 		return ret;
98574b851eSToshi Kani 
99574b851eSToshi Kani 	cnt = arch_cpu_probe(buf, count);
100574b851eSToshi Kani 
101574b851eSToshi Kani 	unlock_device_hotplug();
102574b851eSToshi Kani 	return cnt;
10312633e80SNathan Fontenot }
10412633e80SNathan Fontenot 
1058a25a2fdSKay Sievers static ssize_t cpu_release_store(struct device *dev,
1068a25a2fdSKay Sievers 				 struct device_attribute *attr,
10728812fe1SAndi Kleen 				 const char *buf,
10812633e80SNathan Fontenot 				 size_t count)
10912633e80SNathan Fontenot {
110574b851eSToshi Kani 	ssize_t cnt;
111574b851eSToshi Kani 	int ret;
112574b851eSToshi Kani 
113574b851eSToshi Kani 	ret = lock_device_hotplug_sysfs();
114574b851eSToshi Kani 	if (ret)
115574b851eSToshi Kani 		return ret;
116574b851eSToshi Kani 
117574b851eSToshi Kani 	cnt = arch_cpu_release(buf, count);
118574b851eSToshi Kani 
119574b851eSToshi Kani 	unlock_device_hotplug();
120574b851eSToshi Kani 	return cnt;
12112633e80SNathan Fontenot }
12212633e80SNathan Fontenot 
1238a25a2fdSKay Sievers static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
1248a25a2fdSKay Sievers static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
12512633e80SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
1261da177e4SLinus Torvalds #endif /* CONFIG_HOTPLUG_CPU */
1271da177e4SLinus Torvalds 
1280902a904SRafael J. Wysocki struct bus_type cpu_subsys = {
1290902a904SRafael J. Wysocki 	.name = "cpu",
1300902a904SRafael J. Wysocki 	.dev_name = "cpu",
131ac212b69SRafael J. Wysocki 	.match = cpu_subsys_match,
1320902a904SRafael J. Wysocki #ifdef CONFIG_HOTPLUG_CPU
1330902a904SRafael J. Wysocki 	.online = cpu_subsys_online,
1340902a904SRafael J. Wysocki 	.offline = cpu_subsys_offline,
1350902a904SRafael J. Wysocki #endif
1360902a904SRafael J. Wysocki };
1370902a904SRafael J. Wysocki EXPORT_SYMBOL_GPL(cpu_subsys);
1380902a904SRafael J. Wysocki 
13951be5606SVivek Goyal #ifdef CONFIG_KEXEC
14051be5606SVivek Goyal #include <linux/kexec.h>
14151be5606SVivek Goyal 
1428a25a2fdSKay Sievers static ssize_t show_crash_notes(struct device *dev, struct device_attribute *attr,
1434a0b2b4dSAndi Kleen 				char *buf)
14451be5606SVivek Goyal {
1458a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
14651be5606SVivek Goyal 	ssize_t rc;
14751be5606SVivek Goyal 	unsigned long long addr;
14851be5606SVivek Goyal 	int cpunum;
14951be5606SVivek Goyal 
1508a25a2fdSKay Sievers 	cpunum = cpu->dev.id;
15151be5606SVivek Goyal 
15251be5606SVivek Goyal 	/*
15351be5606SVivek Goyal 	 * Might be reading other cpu's data based on which cpu read thread
15451be5606SVivek Goyal 	 * has been scheduled. But cpu data (memory) is allocated once during
15551be5606SVivek Goyal 	 * boot up and this data does not change there after. Hence this
15651be5606SVivek Goyal 	 * operation should be safe. No locking required.
15751be5606SVivek Goyal 	 */
1583b034b0dSVivek Goyal 	addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
15951be5606SVivek Goyal 	rc = sprintf(buf, "%Lx\n", addr);
16051be5606SVivek Goyal 	return rc;
16151be5606SVivek Goyal }
1628a25a2fdSKay Sievers static DEVICE_ATTR(crash_notes, 0400, show_crash_notes, NULL);
163eca4549fSZhang Yanfei 
164eca4549fSZhang Yanfei static ssize_t show_crash_notes_size(struct device *dev,
165eca4549fSZhang Yanfei 				     struct device_attribute *attr,
166eca4549fSZhang Yanfei 				     char *buf)
167eca4549fSZhang Yanfei {
168eca4549fSZhang Yanfei 	ssize_t rc;
169eca4549fSZhang Yanfei 
170bcfb87fbSArnd Bergmann 	rc = sprintf(buf, "%zu\n", sizeof(note_buf_t));
171eca4549fSZhang Yanfei 	return rc;
172eca4549fSZhang Yanfei }
173eca4549fSZhang Yanfei static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL);
174c055da9fSIgor Mammedov 
175c055da9fSIgor Mammedov static struct attribute *crash_note_cpu_attrs[] = {
176c055da9fSIgor Mammedov 	&dev_attr_crash_notes.attr,
177c055da9fSIgor Mammedov 	&dev_attr_crash_notes_size.attr,
178c055da9fSIgor Mammedov 	NULL
179c055da9fSIgor Mammedov };
180c055da9fSIgor Mammedov 
181c055da9fSIgor Mammedov static struct attribute_group crash_note_cpu_attr_group = {
182c055da9fSIgor Mammedov 	.attrs = crash_note_cpu_attrs,
183c055da9fSIgor Mammedov };
18451be5606SVivek Goyal #endif
18551be5606SVivek Goyal 
186c055da9fSIgor Mammedov static const struct attribute_group *common_cpu_attr_groups[] = {
187c055da9fSIgor Mammedov #ifdef CONFIG_KEXEC
188c055da9fSIgor Mammedov 	&crash_note_cpu_attr_group,
189c055da9fSIgor Mammedov #endif
190c055da9fSIgor Mammedov 	NULL
191c055da9fSIgor Mammedov };
192c055da9fSIgor Mammedov 
1931c4e2d70SIgor Mammedov static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
1941c4e2d70SIgor Mammedov #ifdef CONFIG_KEXEC
1951c4e2d70SIgor Mammedov 	&crash_note_cpu_attr_group,
1961c4e2d70SIgor Mammedov #endif
1971c4e2d70SIgor Mammedov 	NULL
1981c4e2d70SIgor Mammedov };
1991c4e2d70SIgor Mammedov 
2001da177e4SLinus Torvalds /*
2019d1fe323SMike Travis  * Print cpu online, possible, present, and system maps
2029d1fe323SMike Travis  */
203265d2e2eSAndi Kleen 
204265d2e2eSAndi Kleen struct cpu_attr {
2058a25a2fdSKay Sievers 	struct device_attribute attr;
206848e2391SRasmus Villemoes 	const struct cpumask *const map;
207265d2e2eSAndi Kleen };
208265d2e2eSAndi Kleen 
2098a25a2fdSKay Sievers static ssize_t show_cpus_attr(struct device *dev,
2108a25a2fdSKay Sievers 			      struct device_attribute *attr,
211265d2e2eSAndi Kleen 			      char *buf)
2129d1fe323SMike Travis {
213265d2e2eSAndi Kleen 	struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
2149d1fe323SMike Travis 
215848e2391SRasmus Villemoes 	return cpumap_print_to_pagebuf(true, buf, ca->map);
2169d1fe323SMike Travis }
2179d1fe323SMike Travis 
218265d2e2eSAndi Kleen #define _CPU_ATTR(name, map) \
2198a25a2fdSKay Sievers 	{ __ATTR(name, 0444, show_cpus_attr, NULL), map }
2209d1fe323SMike Travis 
2218a25a2fdSKay Sievers /* Keep in sync with cpu_subsys_attrs */
222265d2e2eSAndi Kleen static struct cpu_attr cpu_attrs[] = {
223848e2391SRasmus Villemoes 	_CPU_ATTR(online, &__cpu_online_mask),
224848e2391SRasmus Villemoes 	_CPU_ATTR(possible, &__cpu_possible_mask),
225848e2391SRasmus Villemoes 	_CPU_ATTR(present, &__cpu_present_mask),
226265d2e2eSAndi Kleen };
2279d1fe323SMike Travis 
228e057d7aeSMike Travis /*
229e057d7aeSMike Travis  * Print values for NR_CPUS and offlined cpus
230e057d7aeSMike Travis  */
2318a25a2fdSKay Sievers static ssize_t print_cpus_kernel_max(struct device *dev,
2328a25a2fdSKay Sievers 				     struct device_attribute *attr, char *buf)
233e057d7aeSMike Travis {
2348fd2d2d5SMike Travis 	int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
235e057d7aeSMike Travis 	return n;
236e057d7aeSMike Travis }
2378a25a2fdSKay Sievers static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
238e057d7aeSMike Travis 
239e057d7aeSMike Travis /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
240e057d7aeSMike Travis unsigned int total_cpus;
241e057d7aeSMike Travis 
2428a25a2fdSKay Sievers static ssize_t print_cpus_offline(struct device *dev,
2438a25a2fdSKay Sievers 				  struct device_attribute *attr, char *buf)
244e057d7aeSMike Travis {
245e057d7aeSMike Travis 	int n = 0, len = PAGE_SIZE-2;
246e057d7aeSMike Travis 	cpumask_var_t offline;
247e057d7aeSMike Travis 
248e057d7aeSMike Travis 	/* display offline cpus < nr_cpu_ids */
249e057d7aeSMike Travis 	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
250e057d7aeSMike Travis 		return -ENOMEM;
251cdc6e3d3SJan Beulich 	cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask);
252f799b1a7STejun Heo 	n = scnprintf(buf, len, "%*pbl", cpumask_pr_args(offline));
253e057d7aeSMike Travis 	free_cpumask_var(offline);
254e057d7aeSMike Travis 
255e057d7aeSMike Travis 	/* display offline cpus >= nr_cpu_ids */
256e057d7aeSMike Travis 	if (total_cpus && nr_cpu_ids < total_cpus) {
257e057d7aeSMike Travis 		if (n && n < len)
258e057d7aeSMike Travis 			buf[n++] = ',';
259e057d7aeSMike Travis 
260e057d7aeSMike Travis 		if (nr_cpu_ids == total_cpus-1)
2619b130ad5SAlexey Dobriyan 			n += snprintf(&buf[n], len - n, "%u", nr_cpu_ids);
262e057d7aeSMike Travis 		else
2639b130ad5SAlexey Dobriyan 			n += snprintf(&buf[n], len - n, "%u-%d",
264e057d7aeSMike Travis 						      nr_cpu_ids, total_cpus-1);
265e057d7aeSMike Travis 	}
266e057d7aeSMike Travis 
267e057d7aeSMike Travis 	n += snprintf(&buf[n], len - n, "\n");
268e057d7aeSMike Travis 	return n;
269e057d7aeSMike Travis }
2708a25a2fdSKay Sievers static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
271e057d7aeSMike Travis 
27259f30abeSRik van Riel static ssize_t print_cpus_isolated(struct device *dev,
27359f30abeSRik van Riel 				  struct device_attribute *attr, char *buf)
27459f30abeSRik van Riel {
27559f30abeSRik van Riel 	int n = 0, len = PAGE_SIZE-2;
276edb93821SFrederic Weisbecker 	cpumask_var_t isolated;
27759f30abeSRik van Riel 
278edb93821SFrederic Weisbecker 	if (!alloc_cpumask_var(&isolated, GFP_KERNEL))
279edb93821SFrederic Weisbecker 		return -ENOMEM;
280edb93821SFrederic Weisbecker 
281edb93821SFrederic Weisbecker 	cpumask_andnot(isolated, cpu_possible_mask,
282edb93821SFrederic Weisbecker 		       housekeeping_cpumask(HK_FLAG_DOMAIN));
283edb93821SFrederic Weisbecker 	n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(isolated));
284edb93821SFrederic Weisbecker 
285edb93821SFrederic Weisbecker 	free_cpumask_var(isolated);
28659f30abeSRik van Riel 
28759f30abeSRik van Riel 	return n;
28859f30abeSRik van Riel }
28959f30abeSRik van Riel static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
29059f30abeSRik van Riel 
2916570a9a1SRik van Riel #ifdef CONFIG_NO_HZ_FULL
2926570a9a1SRik van Riel static ssize_t print_cpus_nohz_full(struct device *dev,
2936570a9a1SRik van Riel 				  struct device_attribute *attr, char *buf)
2946570a9a1SRik van Riel {
2956570a9a1SRik van Riel 	int n = 0, len = PAGE_SIZE-2;
2966570a9a1SRik van Riel 
2976570a9a1SRik van Riel 	n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
2986570a9a1SRik van Riel 
2996570a9a1SRik van Riel 	return n;
3006570a9a1SRik van Riel }
3016570a9a1SRik van Riel static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
3026570a9a1SRik van Riel #endif
3036570a9a1SRik van Riel 
3042885e25cSGreg Kroah-Hartman static void cpu_device_release(struct device *dev)
3052885e25cSGreg Kroah-Hartman {
3062885e25cSGreg Kroah-Hartman 	/*
3072885e25cSGreg Kroah-Hartman 	 * This is an empty function to prevent the driver core from spitting a
3082885e25cSGreg Kroah-Hartman 	 * warning at us.  Yes, I know this is directly opposite of what the
3092885e25cSGreg Kroah-Hartman 	 * documentation for the driver core and kobjects say, and the author
3102885e25cSGreg Kroah-Hartman 	 * of this code has already been publically ridiculed for doing
3112885e25cSGreg Kroah-Hartman 	 * something as foolish as this.  However, at this point in time, it is
3122885e25cSGreg Kroah-Hartman 	 * the only way to handle the issue of statically allocated cpu
3132885e25cSGreg Kroah-Hartman 	 * devices.  The different architectures will have their cpu device
3142885e25cSGreg Kroah-Hartman 	 * code reworked to properly handle this in the near future, so this
3152885e25cSGreg Kroah-Hartman 	 * function will then be changed to correctly free up the memory held
3162885e25cSGreg Kroah-Hartman 	 * by the cpu device.
3172885e25cSGreg Kroah-Hartman 	 *
3182885e25cSGreg Kroah-Hartman 	 * Never copy this way of doing things, or you too will be made fun of
31930a4840aSRalf Baechle 	 * on the linux-kernel list, you have been warned.
3202885e25cSGreg Kroah-Hartman 	 */
3212885e25cSGreg Kroah-Hartman }
3222885e25cSGreg Kroah-Hartman 
32367bad2fdSArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
32467bad2fdSArd Biesheuvel static ssize_t print_cpu_modalias(struct device *dev,
32567bad2fdSArd Biesheuvel 				  struct device_attribute *attr,
32667bad2fdSArd Biesheuvel 				  char *buf)
32767bad2fdSArd Biesheuvel {
32867bad2fdSArd Biesheuvel 	ssize_t n;
32967bad2fdSArd Biesheuvel 	u32 i;
33067bad2fdSArd Biesheuvel 
33167bad2fdSArd Biesheuvel 	n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:",
33267bad2fdSArd Biesheuvel 		    CPU_FEATURE_TYPEVAL);
33367bad2fdSArd Biesheuvel 
33467bad2fdSArd Biesheuvel 	for (i = 0; i < MAX_CPU_FEATURES; i++)
33567bad2fdSArd Biesheuvel 		if (cpu_have_feature(i)) {
33667bad2fdSArd Biesheuvel 			if (PAGE_SIZE < n + sizeof(",XXXX\n")) {
33767bad2fdSArd Biesheuvel 				WARN(1, "CPU features overflow page\n");
33867bad2fdSArd Biesheuvel 				break;
33967bad2fdSArd Biesheuvel 			}
34067bad2fdSArd Biesheuvel 			n += sprintf(&buf[n], ",%04X", i);
34167bad2fdSArd Biesheuvel 		}
34267bad2fdSArd Biesheuvel 	buf[n++] = '\n';
34367bad2fdSArd Biesheuvel 	return n;
34467bad2fdSArd Biesheuvel }
34567bad2fdSArd Biesheuvel 
34667bad2fdSArd Biesheuvel static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
34767bad2fdSArd Biesheuvel {
34867bad2fdSArd Biesheuvel 	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
34967bad2fdSArd Biesheuvel 	if (buf) {
35067bad2fdSArd Biesheuvel 		print_cpu_modalias(NULL, NULL, buf);
35167bad2fdSArd Biesheuvel 		add_uevent_var(env, "MODALIAS=%s", buf);
35267bad2fdSArd Biesheuvel 		kfree(buf);
35367bad2fdSArd Biesheuvel 	}
35467bad2fdSArd Biesheuvel 	return 0;
35567bad2fdSArd Biesheuvel }
35667bad2fdSArd Biesheuvel #endif
35767bad2fdSArd Biesheuvel 
3589d1fe323SMike Travis /*
359405ae7d3SRobert P. J. Day  * register_cpu - Setup a sysfs device for a CPU.
36072486f1fSSiddha, Suresh B  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
36172486f1fSSiddha, Suresh B  *	  sysfs for this CPU.
3621da177e4SLinus Torvalds  * @num - CPU number to use when creating the device.
3631da177e4SLinus Torvalds  *
3641da177e4SLinus Torvalds  * Initialize and register the CPU device.
3651da177e4SLinus Torvalds  */
366a83048ebSPaul Gortmaker int register_cpu(struct cpu *cpu, int num)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	int error;
3698a25a2fdSKay Sievers 
3701da177e4SLinus Torvalds 	cpu->node_id = cpu_to_node(num);
37129bb5d4fSGreg Kroah-Hartman 	memset(&cpu->dev, 0x00, sizeof(struct device));
3728a25a2fdSKay Sievers 	cpu->dev.id = num;
3738a25a2fdSKay Sievers 	cpu->dev.bus = &cpu_subsys;
3742885e25cSGreg Kroah-Hartman 	cpu->dev.release = cpu_device_release;
3750902a904SRafael J. Wysocki 	cpu->dev.offline_disabled = !cpu->hotpluggable;
3761001b4d4SToshi Kani 	cpu->dev.offline = !cpu_online(num);
377f86e4718SSudeep KarkadaNagesha 	cpu->dev.of_node = of_get_cpu_node(num, NULL);
3782b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
37967bad2fdSArd Biesheuvel 	cpu->dev.bus->uevent = cpu_uevent;
380fad12ac8SThomas Renninger #endif
381c055da9fSIgor Mammedov 	cpu->dev.groups = common_cpu_attr_groups;
3821c4e2d70SIgor Mammedov 	if (cpu->hotpluggable)
3831c4e2d70SIgor Mammedov 		cpu->dev.groups = hotplugable_cpu_attr_groups;
3848a25a2fdSKay Sievers 	error = device_register(&cpu->dev);
3853aaba245SArvind Yadav 	if (error) {
3863aaba245SArvind Yadav 		put_device(&cpu->dev);
38759fffa34SAlex Shi 		return error;
3883aaba245SArvind Yadav 	}
38959fffa34SAlex Shi 
3908a25a2fdSKay Sievers 	per_cpu(cpu_sys_devices, num) = &cpu->dev;
39176b67ed9SKAMEZAWA Hiroyuki 	register_cpu_under_node(num, cpu_to_node(num));
3920759e80bSRafael J. Wysocki 	dev_pm_qos_expose_latency_limit(&cpu->dev,
3930759e80bSRafael J. Wysocki 					PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
39451be5606SVivek Goyal 
39559fffa34SAlex Shi 	return 0;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
3988a25a2fdSKay Sievers struct device *get_cpu_device(unsigned cpu)
399ad74557aSAshok Raj {
400e37d05daSMike Travis 	if (cpu < nr_cpu_ids && cpu_possible(cpu))
401e37d05daSMike Travis 		return per_cpu(cpu_sys_devices, cpu);
402ad74557aSAshok Raj 	else
403ad74557aSAshok Raj 		return NULL;
404ad74557aSAshok Raj }
4058a25a2fdSKay Sievers EXPORT_SYMBOL_GPL(get_cpu_device);
4068a25a2fdSKay Sievers 
4073d52943bSSudeep Holla static void device_create_release(struct device *dev)
4083d52943bSSudeep Holla {
4093d52943bSSudeep Holla 	kfree(dev);
4103d52943bSSudeep Holla }
4113d52943bSSudeep Holla 
412fa548d79SMathieu Malaterre __printf(4, 0)
4133d52943bSSudeep Holla static struct device *
4143d52943bSSudeep Holla __cpu_device_create(struct device *parent, void *drvdata,
4153d52943bSSudeep Holla 		    const struct attribute_group **groups,
4163d52943bSSudeep Holla 		    const char *fmt, va_list args)
4173d52943bSSudeep Holla {
4183d52943bSSudeep Holla 	struct device *dev = NULL;
4193d52943bSSudeep Holla 	int retval = -ENODEV;
4203d52943bSSudeep Holla 
4213d52943bSSudeep Holla 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
4223d52943bSSudeep Holla 	if (!dev) {
4233d52943bSSudeep Holla 		retval = -ENOMEM;
4243d52943bSSudeep Holla 		goto error;
4253d52943bSSudeep Holla 	}
4263d52943bSSudeep Holla 
4273d52943bSSudeep Holla 	device_initialize(dev);
4283d52943bSSudeep Holla 	dev->parent = parent;
4293d52943bSSudeep Holla 	dev->groups = groups;
4303d52943bSSudeep Holla 	dev->release = device_create_release;
43185945c28SSudeep Holla 	device_set_pm_not_required(dev);
4323d52943bSSudeep Holla 	dev_set_drvdata(dev, drvdata);
4333d52943bSSudeep Holla 
4343d52943bSSudeep Holla 	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
4353d52943bSSudeep Holla 	if (retval)
4363d52943bSSudeep Holla 		goto error;
4373d52943bSSudeep Holla 
4383d52943bSSudeep Holla 	retval = device_add(dev);
4393d52943bSSudeep Holla 	if (retval)
4403d52943bSSudeep Holla 		goto error;
4413d52943bSSudeep Holla 
4423d52943bSSudeep Holla 	return dev;
4433d52943bSSudeep Holla 
4443d52943bSSudeep Holla error:
4453d52943bSSudeep Holla 	put_device(dev);
4463d52943bSSudeep Holla 	return ERR_PTR(retval);
4473d52943bSSudeep Holla }
4483d52943bSSudeep Holla 
4493d52943bSSudeep Holla struct device *cpu_device_create(struct device *parent, void *drvdata,
4503d52943bSSudeep Holla 				 const struct attribute_group **groups,
4513d52943bSSudeep Holla 				 const char *fmt, ...)
4523d52943bSSudeep Holla {
4533d52943bSSudeep Holla 	va_list vargs;
4543d52943bSSudeep Holla 	struct device *dev;
4553d52943bSSudeep Holla 
4563d52943bSSudeep Holla 	va_start(vargs, fmt);
4573d52943bSSudeep Holla 	dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs);
4583d52943bSSudeep Holla 	va_end(vargs);
4593d52943bSSudeep Holla 	return dev;
4603d52943bSSudeep Holla }
4613d52943bSSudeep Holla EXPORT_SYMBOL_GPL(cpu_device_create);
4623d52943bSSudeep Holla 
4632b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
46467bad2fdSArd Biesheuvel static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
465fad12ac8SThomas Renninger #endif
466fad12ac8SThomas Renninger 
4678a25a2fdSKay Sievers static struct attribute *cpu_root_attrs[] = {
4688a25a2fdSKay Sievers #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
4698a25a2fdSKay Sievers 	&dev_attr_probe.attr,
4708a25a2fdSKay Sievers 	&dev_attr_release.attr,
4718a25a2fdSKay Sievers #endif
4728a25a2fdSKay Sievers 	&cpu_attrs[0].attr.attr,
4738a25a2fdSKay Sievers 	&cpu_attrs[1].attr.attr,
4748a25a2fdSKay Sievers 	&cpu_attrs[2].attr.attr,
4758a25a2fdSKay Sievers 	&dev_attr_kernel_max.attr,
4768a25a2fdSKay Sievers 	&dev_attr_offline.attr,
47759f30abeSRik van Riel 	&dev_attr_isolated.attr,
4786570a9a1SRik van Riel #ifdef CONFIG_NO_HZ_FULL
4796570a9a1SRik van Riel 	&dev_attr_nohz_full.attr,
4806570a9a1SRik van Riel #endif
4812b9c1f03SArd Biesheuvel #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
482fad12ac8SThomas Renninger 	&dev_attr_modalias.attr,
483fad12ac8SThomas Renninger #endif
4848a25a2fdSKay Sievers 	NULL
4858a25a2fdSKay Sievers };
4868a25a2fdSKay Sievers 
4878a25a2fdSKay Sievers static struct attribute_group cpu_root_attr_group = {
4888a25a2fdSKay Sievers 	.attrs = cpu_root_attrs,
4898a25a2fdSKay Sievers };
4908a25a2fdSKay Sievers 
4918a25a2fdSKay Sievers static const struct attribute_group *cpu_root_attr_groups[] = {
4928a25a2fdSKay Sievers 	&cpu_root_attr_group,
4938a25a2fdSKay Sievers 	NULL,
4948a25a2fdSKay Sievers };
4951da177e4SLinus Torvalds 
4962987557fSJosh Triplett bool cpu_is_hotpluggable(unsigned cpu)
4972987557fSJosh Triplett {
4987affca35SLinus Torvalds 	struct device *dev = get_cpu_device(cpu);
4997affca35SLinus Torvalds 	return dev && container_of(dev, struct cpu, dev)->hotpluggable;
5002987557fSJosh Triplett }
5012987557fSJosh Triplett EXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
5022987557fSJosh Triplett 
5039f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
5049f13a1fdSBen Hutchings static DEFINE_PER_CPU(struct cpu, cpu_devices);
5059f13a1fdSBen Hutchings #endif
5069f13a1fdSBen Hutchings 
5079f13a1fdSBen Hutchings static void __init cpu_dev_register_generic(void)
5089f13a1fdSBen Hutchings {
5099f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
5109f13a1fdSBen Hutchings 	int i;
5119f13a1fdSBen Hutchings 
5129f13a1fdSBen Hutchings 	for_each_possible_cpu(i) {
5139f13a1fdSBen Hutchings 		if (register_cpu(&per_cpu(cpu_devices, i), i))
5149f13a1fdSBen Hutchings 			panic("Failed to register CPU device");
5159f13a1fdSBen Hutchings 	}
5169f13a1fdSBen Hutchings #endif
5179f13a1fdSBen Hutchings }
5189f13a1fdSBen Hutchings 
51987590ce6SThomas Gleixner #ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
52087590ce6SThomas Gleixner 
52187590ce6SThomas Gleixner ssize_t __weak cpu_show_meltdown(struct device *dev,
52287590ce6SThomas Gleixner 				 struct device_attribute *attr, char *buf)
52387590ce6SThomas Gleixner {
52487590ce6SThomas Gleixner 	return sprintf(buf, "Not affected\n");
52587590ce6SThomas Gleixner }
52687590ce6SThomas Gleixner 
52787590ce6SThomas Gleixner ssize_t __weak cpu_show_spectre_v1(struct device *dev,
52887590ce6SThomas Gleixner 				   struct device_attribute *attr, char *buf)
52987590ce6SThomas Gleixner {
53087590ce6SThomas Gleixner 	return sprintf(buf, "Not affected\n");
53187590ce6SThomas Gleixner }
53287590ce6SThomas Gleixner 
53387590ce6SThomas Gleixner ssize_t __weak cpu_show_spectre_v2(struct device *dev,
53487590ce6SThomas Gleixner 				   struct device_attribute *attr, char *buf)
53587590ce6SThomas Gleixner {
53687590ce6SThomas Gleixner 	return sprintf(buf, "Not affected\n");
53787590ce6SThomas Gleixner }
53887590ce6SThomas Gleixner 
539c456442cSKonrad Rzeszutek Wilk ssize_t __weak cpu_show_spec_store_bypass(struct device *dev,
540c456442cSKonrad Rzeszutek Wilk 					  struct device_attribute *attr, char *buf)
541c456442cSKonrad Rzeszutek Wilk {
542c456442cSKonrad Rzeszutek Wilk 	return sprintf(buf, "Not affected\n");
543c456442cSKonrad Rzeszutek Wilk }
544c456442cSKonrad Rzeszutek Wilk 
54517dbca11SAndi Kleen ssize_t __weak cpu_show_l1tf(struct device *dev,
54617dbca11SAndi Kleen 			     struct device_attribute *attr, char *buf)
54717dbca11SAndi Kleen {
54817dbca11SAndi Kleen 	return sprintf(buf, "Not affected\n");
54917dbca11SAndi Kleen }
55017dbca11SAndi Kleen 
5518a4b06d3SThomas Gleixner ssize_t __weak cpu_show_mds(struct device *dev,
5528a4b06d3SThomas Gleixner 			    struct device_attribute *attr, char *buf)
5538a4b06d3SThomas Gleixner {
5548a4b06d3SThomas Gleixner 	return sprintf(buf, "Not affected\n");
5558a4b06d3SThomas Gleixner }
5568a4b06d3SThomas Gleixner 
5576608b45aSPawan Gupta ssize_t __weak cpu_show_tsx_async_abort(struct device *dev,
5586608b45aSPawan Gupta 					struct device_attribute *attr,
5596608b45aSPawan Gupta 					char *buf)
5606608b45aSPawan Gupta {
5616608b45aSPawan Gupta 	return sprintf(buf, "Not affected\n");
5626608b45aSPawan Gupta }
5636608b45aSPawan Gupta 
564db4d30fbSVineela Tummalapalli ssize_t __weak cpu_show_itlb_multihit(struct device *dev,
565db4d30fbSVineela Tummalapalli 			    struct device_attribute *attr, char *buf)
566db4d30fbSVineela Tummalapalli {
567db4d30fbSVineela Tummalapalli 	return sprintf(buf, "Not affected\n");
568db4d30fbSVineela Tummalapalli }
569db4d30fbSVineela Tummalapalli 
57087590ce6SThomas Gleixner static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
57187590ce6SThomas Gleixner static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
57287590ce6SThomas Gleixner static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
573c456442cSKonrad Rzeszutek Wilk static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);
57417dbca11SAndi Kleen static DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL);
5758a4b06d3SThomas Gleixner static DEVICE_ATTR(mds, 0444, cpu_show_mds, NULL);
5766608b45aSPawan Gupta static DEVICE_ATTR(tsx_async_abort, 0444, cpu_show_tsx_async_abort, NULL);
577db4d30fbSVineela Tummalapalli static DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL);
57887590ce6SThomas Gleixner 
57987590ce6SThomas Gleixner static struct attribute *cpu_root_vulnerabilities_attrs[] = {
58087590ce6SThomas Gleixner 	&dev_attr_meltdown.attr,
58187590ce6SThomas Gleixner 	&dev_attr_spectre_v1.attr,
58287590ce6SThomas Gleixner 	&dev_attr_spectre_v2.attr,
583c456442cSKonrad Rzeszutek Wilk 	&dev_attr_spec_store_bypass.attr,
58417dbca11SAndi Kleen 	&dev_attr_l1tf.attr,
5858a4b06d3SThomas Gleixner 	&dev_attr_mds.attr,
5866608b45aSPawan Gupta 	&dev_attr_tsx_async_abort.attr,
587db4d30fbSVineela Tummalapalli 	&dev_attr_itlb_multihit.attr,
58887590ce6SThomas Gleixner 	NULL
58987590ce6SThomas Gleixner };
59087590ce6SThomas Gleixner 
59187590ce6SThomas Gleixner static const struct attribute_group cpu_root_vulnerabilities_group = {
59287590ce6SThomas Gleixner 	.name  = "vulnerabilities",
59387590ce6SThomas Gleixner 	.attrs = cpu_root_vulnerabilities_attrs,
59487590ce6SThomas Gleixner };
59587590ce6SThomas Gleixner 
59687590ce6SThomas Gleixner static void __init cpu_register_vulnerabilities(void)
59787590ce6SThomas Gleixner {
59887590ce6SThomas Gleixner 	if (sysfs_create_group(&cpu_subsys.dev_root->kobj,
59987590ce6SThomas Gleixner 			       &cpu_root_vulnerabilities_group))
60087590ce6SThomas Gleixner 		pr_err("Unable to register CPU vulnerabilities\n");
60187590ce6SThomas Gleixner }
60287590ce6SThomas Gleixner 
60387590ce6SThomas Gleixner #else
60487590ce6SThomas Gleixner static inline void cpu_register_vulnerabilities(void) { }
60587590ce6SThomas Gleixner #endif
60687590ce6SThomas Gleixner 
607024f7846SBen Hutchings void __init cpu_dev_init(void)
6081da177e4SLinus Torvalds {
609024f7846SBen Hutchings 	if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
610024f7846SBen Hutchings 		panic("Failed to register CPU subsystem");
6115c45bf27SSiddha, Suresh B 
6129f13a1fdSBen Hutchings 	cpu_dev_register_generic();
61387590ce6SThomas Gleixner 	cpu_register_vulnerabilities();
6141da177e4SLinus Torvalds }
615