Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / core / cpu_hotplug.c
1 #include <linux/init.h>
2 #include <linux/kernel.h>
3 #include <linux/sched.h>
4 #include <linux/kobject.h>
5 #include <linux/notifier.h>
6 #include <linux/cpu.h>
7 #include <xen/cpu_hotplug.h>
8 #include <xen/xenbus.h>
9
10 /*
11  * Set of CPUs that remote admin software will allow us to bring online.
12  * Notified to us via xenbus.
13  */
14 static cpumask_var_t xenbus_allowed_cpumask;
15
16 /* Set of CPUs that local admin will allow us to bring online. */
17 static cpumask_var_t local_allowed_cpumask;
18
19 static int local_cpu_hotplug_request(void)
20 {
21         /*
22          * We assume a CPU hotplug request comes from local admin if it is made
23          * via a userspace process (i.e., one with a real mm_struct).
24          */
25         return (current->mm != NULL);
26 }
27
28 static void __cpuinit vcpu_hotplug(unsigned int cpu, struct device *dev)
29 {
30         int err;
31         char dir[32], state[32];
32
33         if ((cpu >= NR_CPUS) || !cpu_possible(cpu))
34                 return;
35
36         sprintf(dir, "cpu/%u", cpu);
37         err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state);
38         if (err != 1) {
39                 pr_err("XENBUS: Unable to read cpu state\n");
40                 return;
41         }
42
43         if (strcmp(state, "online") == 0) {
44                 cpumask_set_cpu(cpu, xenbus_allowed_cpumask);
45                 if (!cpu_up(cpu) && dev)
46                         kobject_uevent(&dev->kobj, KOBJ_ONLINE);
47         } else if (strcmp(state, "offline") == 0) {
48                 cpumask_clear_cpu(cpu, xenbus_allowed_cpumask);
49                 if (!cpu_down(cpu) && dev)
50                         kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
51         } else {
52                 pr_err("XENBUS: unknown state(%s) on CPU%d\n",
53                        state, cpu);
54         }
55 }
56
57 static void __cpuinit handle_vcpu_hotplug_event(
58         struct xenbus_watch *watch, const char **vec, unsigned int len)
59 {
60         unsigned int cpu;
61         char *cpustr;
62         const char *node = vec[XS_WATCH_PATH];
63
64         if ((cpustr = strstr(node, "cpu/")) != NULL) {
65                 sscanf(cpustr, "cpu/%u", &cpu);
66                 vcpu_hotplug(cpu, get_cpu_device(cpu));
67         }
68 }
69
70 static int smpboot_cpu_notify(struct notifier_block *notifier,
71                               unsigned long action, void *hcpu)
72 {
73         unsigned int cpu = (long)hcpu;
74
75         /*
76          * We do this in a callback notifier rather than __cpu_disable()
77          * because local_cpu_hotplug_request() does not work in the latter
78          * as it's always executed from within a stopmachine kthread.
79          */
80         if ((action == CPU_DOWN_PREPARE) && local_cpu_hotplug_request())
81                 cpumask_clear_cpu(cpu, local_allowed_cpumask);
82
83         return NOTIFY_OK;
84 }
85
86 static int __cpuinit setup_cpu_watcher(struct notifier_block *notifier,
87                                        unsigned long event, void *data)
88 {
89         unsigned int i;
90
91         static struct xenbus_watch __cpuinitdata cpu_watch = {
92                 .node = "cpu",
93                 .callback = handle_vcpu_hotplug_event,
94                 .flags = XBWF_new_thread };
95         (void)register_xenbus_watch(&cpu_watch);
96
97         if (!is_initial_xendomain()) {
98                 for_each_possible_cpu(i)
99                         vcpu_hotplug(i, get_cpu_device(i));
100                 pr_info("Brought up %ld CPUs\n", (long)num_online_cpus());
101         }
102
103         return NOTIFY_DONE;
104 }
105
106 static int __init setup_vcpu_hotplug_event(void)
107 {
108         static struct notifier_block hotplug_cpu = {
109                 .notifier_call = smpboot_cpu_notify };
110         static struct notifier_block __cpuinitdata xsn_cpu = {
111                 .notifier_call = setup_cpu_watcher };
112
113         if (!is_running_on_xen())
114                 return -ENODEV;
115
116         register_cpu_notifier(&hotplug_cpu);
117         register_xenstore_notifier(&xsn_cpu);
118
119         return 0;
120 }
121
122 arch_initcall(setup_vcpu_hotplug_event);
123
124 int __ref smp_suspend(void)
125 {
126         unsigned int cpu;
127         int err;
128
129         for_each_online_cpu(cpu) {
130                 if (cpu == 0)
131                         continue;
132                 err = cpu_down(cpu);
133                 if (err) {
134                         pr_crit("Failed to take all CPUs down: %d\n", err);
135                         for_each_possible_cpu(cpu)
136                                 vcpu_hotplug(cpu, NULL);
137                         return err;
138                 }
139         }
140
141         return 0;
142 }
143
144 void __ref smp_resume(void)
145 {
146         unsigned int cpu;
147
148         for_each_possible_cpu(cpu) {
149                 if (cpu == 0)
150                         continue;
151                 vcpu_hotplug(cpu, NULL);
152         }
153 }
154
155 int cpu_up_check(unsigned int cpu)
156 {
157         int rc = 0;
158
159         if (local_cpu_hotplug_request()) {
160                 cpumask_set_cpu(cpu, local_allowed_cpumask);
161                 if (!cpumask_test_cpu(cpu, xenbus_allowed_cpumask)) {
162                         pr_warning("%s: attempt to bring up CPU %u disallowed "
163                                    "by remote admin.\n", __FUNCTION__, cpu);
164                         rc = -EBUSY;
165                 }
166         } else if (!cpumask_test_cpu(cpu, local_allowed_cpumask) ||
167                    !cpumask_test_cpu(cpu, xenbus_allowed_cpumask)) {
168                 rc = -EBUSY;
169         }
170
171         return rc;
172 }
173
174 void __init init_xenbus_allowed_cpumask(void)
175 {
176         if (!alloc_cpumask_var(&xenbus_allowed_cpumask, GFP_KERNEL))
177                 BUG();
178         cpumask_copy(xenbus_allowed_cpumask, cpu_present_mask);
179         if (!alloc_cpumask_var(&local_allowed_cpumask, GFP_KERNEL))
180                 BUG();
181         cpumask_setall(local_allowed_cpumask);
182 }