18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/notifier.h>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <xen/xen.h>
78c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h>
108c2ecf20Sopenharmony_ci#include <asm/cpu.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic void enable_hotplug_cpu(int cpu)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	if (!cpu_present(cpu))
158c2ecf20Sopenharmony_ci		xen_arch_register_cpu(cpu);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	set_cpu_present(cpu, true);
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic void disable_hotplug_cpu(int cpu)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	if (!cpu_is_hotpluggable(cpu))
238c2ecf20Sopenharmony_ci		return;
248c2ecf20Sopenharmony_ci	lock_device_hotplug();
258c2ecf20Sopenharmony_ci	if (cpu_online(cpu))
268c2ecf20Sopenharmony_ci		device_offline(get_cpu_device(cpu));
278c2ecf20Sopenharmony_ci	if (!cpu_online(cpu) && cpu_present(cpu)) {
288c2ecf20Sopenharmony_ci		xen_arch_unregister_cpu(cpu);
298c2ecf20Sopenharmony_ci		set_cpu_present(cpu, false);
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci	unlock_device_hotplug();
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int vcpu_online(unsigned int cpu)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	int err;
378c2ecf20Sopenharmony_ci	char dir[16], state[16];
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	sprintf(dir, "cpu/%u", cpu);
408c2ecf20Sopenharmony_ci	err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state);
418c2ecf20Sopenharmony_ci	if (err != 1) {
428c2ecf20Sopenharmony_ci		if (!xen_initial_domain())
438c2ecf20Sopenharmony_ci			pr_err("Unable to read cpu state\n");
448c2ecf20Sopenharmony_ci		return err;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (strcmp(state, "online") == 0)
488c2ecf20Sopenharmony_ci		return 1;
498c2ecf20Sopenharmony_ci	else if (strcmp(state, "offline") == 0)
508c2ecf20Sopenharmony_ci		return 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	pr_err("unknown state(%s) on CPU%d\n", state, cpu);
538c2ecf20Sopenharmony_ci	return -EINVAL;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_cistatic void vcpu_hotplug(unsigned int cpu)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (cpu >= nr_cpu_ids || !cpu_possible(cpu))
588c2ecf20Sopenharmony_ci		return;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	switch (vcpu_online(cpu)) {
618c2ecf20Sopenharmony_ci	case 1:
628c2ecf20Sopenharmony_ci		enable_hotplug_cpu(cpu);
638c2ecf20Sopenharmony_ci		break;
648c2ecf20Sopenharmony_ci	case 0:
658c2ecf20Sopenharmony_ci		disable_hotplug_cpu(cpu);
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	default:
688c2ecf20Sopenharmony_ci		break;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void handle_vcpu_hotplug_event(struct xenbus_watch *watch,
738c2ecf20Sopenharmony_ci				      const char *path, const char *token)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	unsigned int cpu;
768c2ecf20Sopenharmony_ci	char *cpustr;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	cpustr = strstr(path, "cpu/");
798c2ecf20Sopenharmony_ci	if (cpustr != NULL) {
808c2ecf20Sopenharmony_ci		sscanf(cpustr, "cpu/%u", &cpu);
818c2ecf20Sopenharmony_ci		vcpu_hotplug(cpu);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int setup_cpu_watcher(struct notifier_block *notifier,
868c2ecf20Sopenharmony_ci			      unsigned long event, void *data)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int cpu;
898c2ecf20Sopenharmony_ci	static struct xenbus_watch cpu_watch = {
908c2ecf20Sopenharmony_ci		.node = "cpu",
918c2ecf20Sopenharmony_ci		.callback = handle_vcpu_hotplug_event};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	(void)register_xenbus_watch(&cpu_watch);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
968c2ecf20Sopenharmony_ci		if (vcpu_online(cpu) == 0)
978c2ecf20Sopenharmony_ci			disable_hotplug_cpu(cpu);
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int __init setup_vcpu_hotplug_event(void)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	static struct notifier_block xsn_cpu = {
1068c2ecf20Sopenharmony_ci		.notifier_call = setup_cpu_watcher };
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#ifdef CONFIG_X86
1098c2ecf20Sopenharmony_ci	if (!xen_pv_domain() && !xen_pvh_domain())
1108c2ecf20Sopenharmony_ci#else
1118c2ecf20Sopenharmony_ci	if (!xen_domain())
1128c2ecf20Sopenharmony_ci#endif
1138c2ecf20Sopenharmony_ci		return -ENODEV;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	register_xenstore_notifier(&xsn_cpu);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cilate_initcall(setup_vcpu_hotplug_event);
1218c2ecf20Sopenharmony_ci
122