xref: /kernel/linux/linux-5.10/drivers/xen/pcpu.c (revision 8c2ecf20)
1/******************************************************************************
2 * pcpu.c
3 * Management physical cpu in dom0, get pcpu info and provide sys interface
4 *
5 * Copyright (c) 2012 Intel Corporation
6 * Author: Liu, Jinsong <jinsong.liu@intel.com>
7 * Author: Jiang, Yunhong <yunhong.jiang@intel.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation; or, when distributed
12 * separately from the Linux kernel or incorporated into other
13 * software packages, subject to the following license:
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this source file (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use, copy, modify,
18 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
33
34#define pr_fmt(fmt) "xen_cpu: " fmt
35
36#include <linux/interrupt.h>
37#include <linux/spinlock.h>
38#include <linux/cpu.h>
39#include <linux/stat.h>
40#include <linux/capability.h>
41
42#include <xen/xen.h>
43#include <xen/acpi.h>
44#include <xen/xenbus.h>
45#include <xen/events.h>
46#include <xen/interface/platform.h>
47#include <asm/xen/hypervisor.h>
48#include <asm/xen/hypercall.h>
49
50
51/*
52 * @cpu_id: Xen physical cpu logic number
53 * @flags: Xen physical cpu status flag
54 * - XEN_PCPU_FLAGS_ONLINE: cpu is online
55 * - XEN_PCPU_FLAGS_INVALID: cpu is not present
56 */
57struct pcpu {
58	struct list_head list;
59	struct device dev;
60	uint32_t cpu_id;
61	uint32_t acpi_id;
62	uint32_t flags;
63};
64
65static struct bus_type xen_pcpu_subsys = {
66	.name = "xen_cpu",
67	.dev_name = "xen_cpu",
68};
69
70static DEFINE_MUTEX(xen_pcpu_lock);
71
72static LIST_HEAD(xen_pcpus);
73
74static int xen_pcpu_down(uint32_t cpu_id)
75{
76	struct xen_platform_op op = {
77		.cmd			= XENPF_cpu_offline,
78		.interface_version	= XENPF_INTERFACE_VERSION,
79		.u.cpu_ol.cpuid		= cpu_id,
80	};
81
82	return HYPERVISOR_platform_op(&op);
83}
84
85static int xen_pcpu_up(uint32_t cpu_id)
86{
87	struct xen_platform_op op = {
88		.cmd			= XENPF_cpu_online,
89		.interface_version	= XENPF_INTERFACE_VERSION,
90		.u.cpu_ol.cpuid		= cpu_id,
91	};
92
93	return HYPERVISOR_platform_op(&op);
94}
95
96static ssize_t show_online(struct device *dev,
97			   struct device_attribute *attr,
98			   char *buf)
99{
100	struct pcpu *cpu = container_of(dev, struct pcpu, dev);
101
102	return sprintf(buf, "%u\n", !!(cpu->flags & XEN_PCPU_FLAGS_ONLINE));
103}
104
105static ssize_t __ref store_online(struct device *dev,
106				  struct device_attribute *attr,
107				  const char *buf, size_t count)
108{
109	struct pcpu *pcpu = container_of(dev, struct pcpu, dev);
110	unsigned long long val;
111	ssize_t ret;
112
113	if (!capable(CAP_SYS_ADMIN))
114		return -EPERM;
115
116	if (kstrtoull(buf, 0, &val) < 0)
117		return -EINVAL;
118
119	switch (val) {
120	case 0:
121		ret = xen_pcpu_down(pcpu->cpu_id);
122		break;
123	case 1:
124		ret = xen_pcpu_up(pcpu->cpu_id);
125		break;
126	default:
127		ret = -EINVAL;
128	}
129
130	if (ret >= 0)
131		ret = count;
132	return ret;
133}
134static DEVICE_ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online);
135
136static struct attribute *pcpu_dev_attrs[] = {
137	&dev_attr_online.attr,
138	NULL
139};
140
141static umode_t pcpu_dev_is_visible(struct kobject *kobj,
142				   struct attribute *attr, int idx)
143{
144	struct device *dev = kobj_to_dev(kobj);
145	/*
146	 * Xen never offline cpu0 due to several restrictions
147	 * and assumptions. This basically doesn't add a sys control
148	 * to user, one cannot attempt to offline BSP.
149	 */
150	return dev->id ? attr->mode : 0;
151}
152
153static const struct attribute_group pcpu_dev_group = {
154	.attrs = pcpu_dev_attrs,
155	.is_visible = pcpu_dev_is_visible,
156};
157
158static const struct attribute_group *pcpu_dev_groups[] = {
159	&pcpu_dev_group,
160	NULL
161};
162
163static bool xen_pcpu_online(uint32_t flags)
164{
165	return !!(flags & XEN_PCPU_FLAGS_ONLINE);
166}
167
168static void pcpu_online_status(struct xenpf_pcpuinfo *info,
169			       struct pcpu *pcpu)
170{
171	if (xen_pcpu_online(info->flags) &&
172	   !xen_pcpu_online(pcpu->flags)) {
173		/* the pcpu is onlined */
174		pcpu->flags |= XEN_PCPU_FLAGS_ONLINE;
175		kobject_uevent(&pcpu->dev.kobj, KOBJ_ONLINE);
176	} else if (!xen_pcpu_online(info->flags) &&
177		    xen_pcpu_online(pcpu->flags)) {
178		/* The pcpu is offlined */
179		pcpu->flags &= ~XEN_PCPU_FLAGS_ONLINE;
180		kobject_uevent(&pcpu->dev.kobj, KOBJ_OFFLINE);
181	}
182}
183
184static struct pcpu *get_pcpu(uint32_t cpu_id)
185{
186	struct pcpu *pcpu;
187
188	list_for_each_entry(pcpu, &xen_pcpus, list) {
189		if (pcpu->cpu_id == cpu_id)
190			return pcpu;
191	}
192
193	return NULL;
194}
195
196static void pcpu_release(struct device *dev)
197{
198	struct pcpu *pcpu = container_of(dev, struct pcpu, dev);
199
200	list_del(&pcpu->list);
201	kfree(pcpu);
202}
203
204static void unregister_and_remove_pcpu(struct pcpu *pcpu)
205{
206	struct device *dev;
207
208	if (!pcpu)
209		return;
210
211	dev = &pcpu->dev;
212	/* pcpu remove would be implicitly done */
213	device_unregister(dev);
214}
215
216static int register_pcpu(struct pcpu *pcpu)
217{
218	struct device *dev;
219	int err = -EINVAL;
220
221	if (!pcpu)
222		return err;
223
224	dev = &pcpu->dev;
225	dev->bus = &xen_pcpu_subsys;
226	dev->id = pcpu->cpu_id;
227	dev->release = pcpu_release;
228	dev->groups = pcpu_dev_groups;
229
230	err = device_register(dev);
231	if (err) {
232		put_device(dev);
233		return err;
234	}
235
236	return 0;
237}
238
239static struct pcpu *create_and_register_pcpu(struct xenpf_pcpuinfo *info)
240{
241	struct pcpu *pcpu;
242	int err;
243
244	if (info->flags & XEN_PCPU_FLAGS_INVALID)
245		return ERR_PTR(-ENODEV);
246
247	pcpu = kzalloc(sizeof(struct pcpu), GFP_KERNEL);
248	if (!pcpu)
249		return ERR_PTR(-ENOMEM);
250
251	INIT_LIST_HEAD(&pcpu->list);
252	pcpu->cpu_id = info->xen_cpuid;
253	pcpu->acpi_id = info->acpi_id;
254	pcpu->flags = info->flags;
255
256	/* Need hold on xen_pcpu_lock before pcpu list manipulations */
257	list_add_tail(&pcpu->list, &xen_pcpus);
258
259	err = register_pcpu(pcpu);
260	if (err) {
261		pr_warn("Failed to register pcpu%u\n", info->xen_cpuid);
262		return ERR_PTR(-ENOENT);
263	}
264
265	return pcpu;
266}
267
268/*
269 * Caller should hold the xen_pcpu_lock
270 */
271static int sync_pcpu(uint32_t cpu, uint32_t *max_cpu)
272{
273	int ret;
274	struct pcpu *pcpu = NULL;
275	struct xenpf_pcpuinfo *info;
276	struct xen_platform_op op = {
277		.cmd                   = XENPF_get_cpuinfo,
278		.interface_version     = XENPF_INTERFACE_VERSION,
279		.u.pcpu_info.xen_cpuid = cpu,
280	};
281
282	ret = HYPERVISOR_platform_op(&op);
283	if (ret)
284		return ret;
285
286	info = &op.u.pcpu_info;
287	if (max_cpu)
288		*max_cpu = info->max_present;
289
290	pcpu = get_pcpu(cpu);
291
292	/*
293	 * Only those at cpu present map has its sys interface.
294	 */
295	if (info->flags & XEN_PCPU_FLAGS_INVALID) {
296		unregister_and_remove_pcpu(pcpu);
297		return 0;
298	}
299
300	if (!pcpu) {
301		pcpu = create_and_register_pcpu(info);
302		if (IS_ERR_OR_NULL(pcpu))
303			return -ENODEV;
304	} else
305		pcpu_online_status(info, pcpu);
306
307	return 0;
308}
309
310/*
311 * Sync dom0's pcpu information with xen hypervisor's
312 */
313static int xen_sync_pcpus(void)
314{
315	/*
316	 * Boot cpu always have cpu_id 0 in xen
317	 */
318	uint32_t cpu = 0, max_cpu = 0;
319	int err = 0;
320	struct pcpu *pcpu, *tmp;
321
322	mutex_lock(&xen_pcpu_lock);
323
324	while (!err && (cpu <= max_cpu)) {
325		err = sync_pcpu(cpu, &max_cpu);
326		cpu++;
327	}
328
329	if (err)
330		list_for_each_entry_safe(pcpu, tmp, &xen_pcpus, list)
331			unregister_and_remove_pcpu(pcpu);
332
333	mutex_unlock(&xen_pcpu_lock);
334
335	return err;
336}
337
338static void xen_pcpu_work_fn(struct work_struct *work)
339{
340	xen_sync_pcpus();
341}
342static DECLARE_WORK(xen_pcpu_work, xen_pcpu_work_fn);
343
344static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id)
345{
346	schedule_work(&xen_pcpu_work);
347	return IRQ_HANDLED;
348}
349
350/* Sync with Xen hypervisor after cpu hotadded */
351void xen_pcpu_hotplug_sync(void)
352{
353	schedule_work(&xen_pcpu_work);
354}
355EXPORT_SYMBOL_GPL(xen_pcpu_hotplug_sync);
356
357/*
358 * For hypervisor presented cpu, return logic cpu id;
359 * For hypervisor non-presented cpu, return -ENODEV.
360 */
361int xen_pcpu_id(uint32_t acpi_id)
362{
363	int cpu_id = 0, max_id = 0;
364	struct xen_platform_op op;
365
366	op.cmd = XENPF_get_cpuinfo;
367	while (cpu_id <= max_id) {
368		op.u.pcpu_info.xen_cpuid = cpu_id;
369		if (HYPERVISOR_platform_op(&op)) {
370			cpu_id++;
371			continue;
372		}
373
374		if (acpi_id == op.u.pcpu_info.acpi_id)
375			return cpu_id;
376		if (op.u.pcpu_info.max_present > max_id)
377			max_id = op.u.pcpu_info.max_present;
378		cpu_id++;
379	}
380
381	return -ENODEV;
382}
383EXPORT_SYMBOL_GPL(xen_pcpu_id);
384
385static int __init xen_pcpu_init(void)
386{
387	int irq, ret;
388
389	if (!xen_initial_domain())
390		return -ENODEV;
391
392	irq = bind_virq_to_irqhandler(VIRQ_PCPU_STATE, 0,
393				      xen_pcpu_interrupt, 0,
394				      "xen-pcpu", NULL);
395	if (irq < 0) {
396		pr_warn("Failed to bind pcpu virq\n");
397		return irq;
398	}
399
400	ret = subsys_system_register(&xen_pcpu_subsys, NULL);
401	if (ret) {
402		pr_warn("Failed to register pcpu subsys\n");
403		goto err1;
404	}
405
406	ret = xen_sync_pcpus();
407	if (ret) {
408		pr_warn("Failed to sync pcpu info\n");
409		goto err2;
410	}
411
412	return 0;
413
414err2:
415	bus_unregister(&xen_pcpu_subsys);
416err1:
417	unbind_from_irqhandler(irq, NULL);
418	return ret;
419}
420arch_initcall(xen_pcpu_init);
421
422#ifdef CONFIG_ACPI
423bool __init xen_processor_present(uint32_t acpi_id)
424{
425	const struct pcpu *pcpu;
426	bool online = false;
427
428	mutex_lock(&xen_pcpu_lock);
429	list_for_each_entry(pcpu, &xen_pcpus, list)
430		if (pcpu->acpi_id == acpi_id) {
431			online = pcpu->flags & XEN_PCPU_FLAGS_ONLINE;
432			break;
433		}
434	mutex_unlock(&xen_pcpu_lock);
435
436	return online;
437}
438#endif
439