1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt |
3 | |
4 | #include <linux/notifier.h> |
5 | |
6 | #include <xen/xen.h> |
7 | #include <xen/xenbus.h> |
8 | |
9 | #include <asm/xen/hypervisor.h> |
10 | #include <asm/cpu.h> |
11 | |
12 | static void enable_hotplug_cpu(int cpu) |
13 | { |
14 | if (!cpu_present(cpu)) |
15 | xen_arch_register_cpu(num: cpu); |
16 | |
17 | set_cpu_present(cpu, present: true); |
18 | } |
19 | |
20 | static void disable_hotplug_cpu(int cpu) |
21 | { |
22 | if (!cpu_is_hotpluggable(cpu)) |
23 | return; |
24 | lock_device_hotplug(); |
25 | if (cpu_online(cpu)) |
26 | device_offline(dev: get_cpu_device(cpu)); |
27 | if (!cpu_online(cpu) && cpu_present(cpu)) { |
28 | xen_arch_unregister_cpu(num: cpu); |
29 | set_cpu_present(cpu, present: false); |
30 | } |
31 | 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(buf: dir, fmt: "cpu/%u" , cpu); |
40 | err = xenbus_scanf(XBT_NIL, dir, node: "availability" , fmt: "%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 | if (strcmp(state, "online" ) == 0) |
48 | return 1; |
49 | else if (strcmp(state, "offline" ) == 0) |
50 | return 0; |
51 | |
52 | pr_err("unknown state(%s) on CPU%d\n" , state, cpu); |
53 | return -EINVAL; |
54 | } |
55 | static void vcpu_hotplug(unsigned int cpu) |
56 | { |
57 | if (cpu >= nr_cpu_ids || !cpu_possible(cpu)) |
58 | return; |
59 | |
60 | switch (vcpu_online(cpu)) { |
61 | case 1: |
62 | enable_hotplug_cpu(cpu); |
63 | break; |
64 | case 0: |
65 | disable_hotplug_cpu(cpu); |
66 | break; |
67 | default: |
68 | break; |
69 | } |
70 | } |
71 | |
72 | static void handle_vcpu_hotplug_event(struct xenbus_watch *watch, |
73 | const char *path, const char *token) |
74 | { |
75 | unsigned int cpu; |
76 | char *cpustr; |
77 | |
78 | cpustr = strstr(path, "cpu/" ); |
79 | if (cpustr != NULL) { |
80 | sscanf(cpustr, "cpu/%u" , &cpu); |
81 | vcpu_hotplug(cpu); |
82 | } |
83 | } |
84 | |
85 | static int setup_cpu_watcher(struct notifier_block *notifier, |
86 | unsigned long event, void *data) |
87 | { |
88 | int cpu; |
89 | static struct xenbus_watch cpu_watch = { |
90 | .node = "cpu" , |
91 | .callback = handle_vcpu_hotplug_event}; |
92 | |
93 | (void)register_xenbus_watch(watch: &cpu_watch); |
94 | |
95 | for_each_possible_cpu(cpu) { |
96 | if (vcpu_online(cpu) == 0) |
97 | disable_hotplug_cpu(cpu); |
98 | } |
99 | |
100 | return NOTIFY_DONE; |
101 | } |
102 | |
103 | static int __init setup_vcpu_hotplug_event(void) |
104 | { |
105 | static struct notifier_block xsn_cpu = { |
106 | .notifier_call = setup_cpu_watcher }; |
107 | |
108 | #ifdef CONFIG_X86 |
109 | if (!xen_pv_domain() && !xen_pvh_domain()) |
110 | #else |
111 | if (!xen_domain()) |
112 | #endif |
113 | return -ENODEV; |
114 | |
115 | register_xenstore_notifier(nb: &xsn_cpu); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | late_initcall(setup_vcpu_hotplug_event); |
121 | |
122 | |