cpu_hotplug.c 2.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3
#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt

4 5
#include <linux/notifier.h>

6
#include <xen/xen.h>
7 8
#include <xen/xenbus.h>

Al Viro's avatar
Al Viro committed
9
#include <asm/xen/hypervisor.h>
10 11 12 13 14
#include <asm/cpu.h>

static void enable_hotplug_cpu(int cpu)
{
	if (!cpu_present(cpu))
15
		xen_arch_register_cpu(cpu);
16

17
	set_cpu_present(cpu, true);
18 19 20 21
}

static void disable_hotplug_cpu(int cpu)
{
22 23 24 25
	if (!cpu_is_hotpluggable(cpu))
		return;
	lock_device_hotplug();
	if (cpu_online(cpu))
26
		device_offline(get_cpu_device(cpu));
27
	if (!cpu_online(cpu) && cpu_present(cpu)) {
28
		xen_arch_unregister_cpu(cpu);
29 30 31
		set_cpu_present(cpu, false);
	}
	unlock_device_hotplug();
32 33
}

34
static int vcpu_online(unsigned int cpu)
35 36
{
	int err;
37
	char dir[16], state[16];
38 39

	sprintf(dir, "cpu/%u", cpu);
40
	err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state);
41
	if (err != 1) {
42
		if (!xen_initial_domain())
43
			pr_err("Unable to read cpu state\n");
44
		return err;
45 46
	}

47 48 49 50 51
	if (strcmp(state, "online") == 0)
		return 1;
	else if (strcmp(state, "offline") == 0)
		return 0;

52
	pr_err("unknown state(%s) on CPU%d\n", state, cpu);
53 54 55 56 57 58 59 60 61
	return -EINVAL;
}
static void vcpu_hotplug(unsigned int cpu)
{
	if (!cpu_possible(cpu))
		return;

	switch (vcpu_online(cpu)) {
	case 1:
62
		enable_hotplug_cpu(cpu);
63 64
		break;
	case 0:
65
		disable_hotplug_cpu(cpu);
66 67 68
		break;
	default:
		break;
69 70 71 72
	}
}

static void handle_vcpu_hotplug_event(struct xenbus_watch *watch,
73
				      const char *path, const char *token)
74 75 76 77
{
	unsigned int cpu;
	char *cpustr;

78
	cpustr = strstr(path, "cpu/");
79 80 81 82 83 84 85 86 87
	if (cpustr != NULL) {
		sscanf(cpustr, "cpu/%u", &cpu);
		vcpu_hotplug(cpu);
	}
}

static int setup_cpu_watcher(struct notifier_block *notifier,
			      unsigned long event, void *data)
{
88
	int cpu;
89 90 91 92 93 94
	static struct xenbus_watch cpu_watch = {
		.node = "cpu",
		.callback = handle_vcpu_hotplug_event};

	(void)register_xenbus_watch(&cpu_watch);

95 96 97
	for_each_possible_cpu(cpu) {
		if (vcpu_online(cpu) == 0) {
			(void)cpu_down(cpu);
98
			set_cpu_present(cpu, false);
99 100 101
		}
	}

102 103 104 105 106 107 108 109
	return NOTIFY_DONE;
}

static int __init setup_vcpu_hotplug_event(void)
{
	static struct notifier_block xsn_cpu = {
		.notifier_call = setup_cpu_watcher };

110
#ifdef CONFIG_X86
111
	if (!xen_pv_domain() && !xen_pvh_domain())
112 113 114
#else
	if (!xen_domain())
#endif
115 116 117 118 119 120 121 122 123
		return -ENODEV;

	register_xenstore_notifier(&xsn_cpu);

	return 0;
}

arch_initcall(setup_vcpu_hotplug_event);