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(cpu); 16 17 set_cpu_present(cpu, true); 18 } 19 20 static void disable_hotplug_cpu(int cpu) 21 { 22 if (cpu_online(cpu)) { 23 lock_device_hotplug(); 24 device_offline(get_cpu_device(cpu)); 25 unlock_device_hotplug(); 26 } 27 if (cpu_present(cpu)) 28 xen_arch_unregister_cpu(cpu); 29 30 set_cpu_present(cpu, false); 31 } 32 33 static int vcpu_online(unsigned int cpu) 34 { 35 int err; 36 char dir[16], state[16]; 37 38 sprintf(dir, "cpu/%u", cpu); 39 err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state); 40 if (err != 1) { 41 if (!xen_initial_domain()) 42 pr_err("Unable to read cpu state\n"); 43 return err; 44 } 45 46 if (strcmp(state, "online") == 0) 47 return 1; 48 else if (strcmp(state, "offline") == 0) 49 return 0; 50 51 pr_err("unknown state(%s) on CPU%d\n", state, cpu); 52 return -EINVAL; 53 } 54 static void vcpu_hotplug(unsigned int cpu) 55 { 56 if (!cpu_possible(cpu)) 57 return; 58 59 switch (vcpu_online(cpu)) { 60 case 1: 61 enable_hotplug_cpu(cpu); 62 break; 63 case 0: 64 disable_hotplug_cpu(cpu); 65 break; 66 default: 67 break; 68 } 69 } 70 71 static void handle_vcpu_hotplug_event(struct xenbus_watch *watch, 72 const char *path, const char *token) 73 { 74 unsigned int cpu; 75 char *cpustr; 76 77 cpustr = strstr(path, "cpu/"); 78 if (cpustr != NULL) { 79 sscanf(cpustr, "cpu/%u", &cpu); 80 vcpu_hotplug(cpu); 81 } 82 } 83 84 static int setup_cpu_watcher(struct notifier_block *notifier, 85 unsigned long event, void *data) 86 { 87 int cpu; 88 static struct xenbus_watch cpu_watch = { 89 .node = "cpu", 90 .callback = handle_vcpu_hotplug_event}; 91 92 (void)register_xenbus_watch(&cpu_watch); 93 94 for_each_possible_cpu(cpu) { 95 if (vcpu_online(cpu) == 0) { 96 (void)cpu_down(cpu); 97 set_cpu_present(cpu, false); 98 } 99 } 100 101 return NOTIFY_DONE; 102 } 103 104 static int __init setup_vcpu_hotplug_event(void) 105 { 106 static struct notifier_block xsn_cpu = { 107 .notifier_call = setup_cpu_watcher }; 108 109 #ifdef CONFIG_X86 110 if (!xen_pv_domain() && !xen_pvh_domain()) 111 #else 112 if (!xen_domain()) 113 #endif 114 return -ENODEV; 115 116 register_xenstore_notifier(&xsn_cpu); 117 118 return 0; 119 } 120 121 arch_initcall(setup_vcpu_hotplug_event); 122 123