Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / xen / core / cpu_hotplug.c
1
2 #include <linux/init.h>
3 #include <linux/kernel.h>
4 #include <linux/sched.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_t xenbus_allowed_cpumask;
15
16 /* Set of CPUs that local admin will allow us to bring online. */
17 static cpumask_t local_allowed_cpumask = CPU_MASK_ALL;
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 vcpu_hotplug(unsigned int cpu)
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/%d", cpu);
37         err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state);
38         if (err != 1) {
39                 printk(KERN_ERR "XENBUS: Unable to read cpu state\n");
40                 return;
41         }
42
43         if (strcmp(state, "online") == 0) {
44                 cpu_set(cpu, xenbus_allowed_cpumask);
45                 (void)cpu_up(cpu);
46         } else if (strcmp(state, "offline") == 0) {
47                 cpu_clear(cpu, xenbus_allowed_cpumask);
48                 (void)cpu_down(cpu);
49         } else {
50                 printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n",
51                        state, cpu);
52         }
53 }
54
55 static void handle_vcpu_hotplug_event(
56         struct xenbus_watch *watch, const char **vec, unsigned int len)
57 {
58         int cpu;
59         char *cpustr;
60         const char *node = vec[XS_WATCH_PATH];
61
62         if ((cpustr = strstr(node, "cpu/")) != NULL) {
63                 sscanf(cpustr, "cpu/%d", &cpu);
64                 vcpu_hotplug(cpu);
65         }
66 }
67
68 static int smpboot_cpu_notify(struct notifier_block *notifier,
69                               unsigned long action, void *hcpu)
70 {
71         int cpu = (long)hcpu;
72
73         /*
74          * We do this in a callback notifier rather than __cpu_disable()
75          * because local_cpu_hotplug_request() does not work in the latter
76          * as it's always executed from within a stopmachine kthread.
77          */
78         if ((action == CPU_DOWN_PREPARE) && local_cpu_hotplug_request())
79                 cpu_clear(cpu, local_allowed_cpumask);
80
81         return NOTIFY_OK;
82 }
83
84 static int setup_cpu_watcher(struct notifier_block *notifier,
85                               unsigned long event, void *data)
86 {
87         int i;
88
89         static struct xenbus_watch cpu_watch = {
90                 .node = "cpu",
91                 .callback = handle_vcpu_hotplug_event,
92                 .flags = XBWF_new_thread };
93         (void)register_xenbus_watch(&cpu_watch);
94
95         if (!is_initial_xendomain()) {
96                 for_each_possible_cpu(i)
97                         vcpu_hotplug(i);
98                 printk(KERN_INFO "Brought up %ld CPUs\n",
99                        (long)num_online_cpus());
100         }
101
102         return NOTIFY_DONE;
103 }
104
105 static int __init setup_vcpu_hotplug_event(void)
106 {
107         static struct notifier_block hotplug_cpu = {
108                 .notifier_call = smpboot_cpu_notify };
109         static struct notifier_block xsn_cpu = {
110                 .notifier_call = setup_cpu_watcher };
111
112         if (!is_running_on_xen())
113                 return -ENODEV;
114
115         register_cpu_notifier(&hotplug_cpu);
116         register_xenstore_notifier(&xsn_cpu);
117
118         return 0;
119 }
120
121 arch_initcall(setup_vcpu_hotplug_event);
122
123 int smp_suspend(void)
124 {
125         int i, err;
126
127         lock_cpu_hotplug();
128
129         /*
130          * Take all other CPUs offline. We hold the hotplug mutex to
131          * avoid other processes bringing up CPUs under our feet.
132          */
133         while (num_online_cpus() > 1) {
134                 unlock_cpu_hotplug();
135                 for_each_online_cpu(i) {
136                         if (i == 0)
137                                 continue;
138                         err = cpu_down(i);
139                         if (err) {
140                                 printk(KERN_CRIT "Failed to take all CPUs "
141                                        "down: %d.\n", err);
142                                 for_each_possible_cpu(i)
143                                         vcpu_hotplug(i);
144                                 return err;
145                         }
146                 }
147                 lock_cpu_hotplug();
148         }
149
150         return 0;
151 }
152
153 void smp_resume(void)
154 {
155         int cpu;
156
157         for_each_possible_cpu(cpu)
158                 cpu_initialize_context(cpu);
159
160         unlock_cpu_hotplug();
161
162         for_each_possible_cpu(cpu)
163                 vcpu_hotplug(cpu);
164 }
165
166 int cpu_up_check(unsigned int cpu)
167 {
168         int rc = 0;
169
170         if (local_cpu_hotplug_request()) {
171                 cpu_set(cpu, local_allowed_cpumask);
172                 if (!cpu_isset(cpu, xenbus_allowed_cpumask)) {
173                         printk("%s: attempt to bring up CPU %u disallowed by "
174                                "remote admin.\n", __FUNCTION__, cpu);
175                         rc = -EBUSY;
176                 }
177         } else if (!cpu_isset(cpu, local_allowed_cpumask) ||
178                    !cpu_isset(cpu, xenbus_allowed_cpumask)) {
179                 rc = -EBUSY;
180         }
181
182         return rc;
183 }
184
185 void init_xenbus_allowed_cpumask(void)
186 {
187         xenbus_allowed_cpumask = cpu_present_map;
188 }