1#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
2
3#include <linux/notifier.h>
4
5#include <xen/xen.h>
6#include <xen/xenbus.h>
7
8#include <asm/xen/hypervisor.h>
9#include <asm/cpu.h>
10
11static void enable_hotplug_cpu(int cpu)
12{
13	if (!cpu_present(cpu))
14		arch_register_cpu(cpu);
15
16	set_cpu_present(cpu, true);
17}
18
19static void disable_hotplug_cpu(int cpu)
20{
21	if (cpu_present(cpu))
22		arch_unregister_cpu(cpu);
23
24	set_cpu_present(cpu, false);
25}
26
27static int vcpu_online(unsigned int cpu)
28{
29	int err;
30	char dir[16], state[16];
31
32	sprintf(dir, "cpu/%u", cpu);
33	err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state);
34	if (err != 1) {
35		if (!xen_initial_domain())
36			pr_err("Unable to read cpu state\n");
37		return err;
38	}
39
40	if (strcmp(state, "online") == 0)
41		return 1;
42	else if (strcmp(state, "offline") == 0)
43		return 0;
44
45	pr_err("unknown state(%s) on CPU%d\n", state, cpu);
46	return -EINVAL;
47}
48static void vcpu_hotplug(unsigned int cpu)
49{
50	if (!cpu_possible(cpu))
51		return;
52
53	switch (vcpu_online(cpu)) {
54	case 1:
55		enable_hotplug_cpu(cpu);
56		break;
57	case 0:
58		(void)cpu_down(cpu);
59		disable_hotplug_cpu(cpu);
60		break;
61	default:
62		break;
63	}
64}
65
66static void handle_vcpu_hotplug_event(struct xenbus_watch *watch,
67					const char **vec, unsigned int len)
68{
69	unsigned int cpu;
70	char *cpustr;
71	const char *node = vec[XS_WATCH_PATH];
72
73	cpustr = strstr(node, "cpu/");
74	if (cpustr != NULL) {
75		sscanf(cpustr, "cpu/%u", &cpu);
76		vcpu_hotplug(cpu);
77	}
78}
79
80static int setup_cpu_watcher(struct notifier_block *notifier,
81			      unsigned long event, void *data)
82{
83	int cpu;
84	static struct xenbus_watch cpu_watch = {
85		.node = "cpu",
86		.callback = handle_vcpu_hotplug_event};
87
88	(void)register_xenbus_watch(&cpu_watch);
89
90	for_each_possible_cpu(cpu) {
91		if (vcpu_online(cpu) == 0) {
92			(void)cpu_down(cpu);
93			set_cpu_present(cpu, false);
94		}
95	}
96
97	return NOTIFY_DONE;
98}
99
100static int __init setup_vcpu_hotplug_event(void)
101{
102	static struct notifier_block xsn_cpu = {
103		.notifier_call = setup_cpu_watcher };
104
105	if (!xen_pv_domain())
106		return -ENODEV;
107
108	register_xenstore_notifier(&xsn_cpu);
109
110	return 0;
111}
112
113arch_initcall(setup_vcpu_hotplug_event);
114
115