Merge tag 'perf-tools-2020-12-24' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / xen / manage.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Handle extern requests for shutdown, reboot and sysrq
4  */
5
6 #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
7
8 #include <linux/kernel.h>
9 #include <linux/err.h>
10 #include <linux/slab.h>
11 #include <linux/reboot.h>
12 #include <linux/sysrq.h>
13 #include <linux/stop_machine.h>
14 #include <linux/freezer.h>
15 #include <linux/syscore_ops.h>
16 #include <linux/export.h>
17
18 #include <xen/xen.h>
19 #include <xen/xenbus.h>
20 #include <xen/grant_table.h>
21 #include <xen/events.h>
22 #include <xen/hvc-console.h>
23 #include <xen/page.h>
24 #include <xen/xen-ops.h>
25
26 #include <asm/xen/hypercall.h>
27 #include <asm/xen/hypervisor.h>
28
29 enum shutdown_state {
30         SHUTDOWN_INVALID = -1,
31         SHUTDOWN_POWEROFF = 0,
32         SHUTDOWN_SUSPEND = 2,
33         /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
34            report a crash, not be instructed to crash!
35            HALT is the same as POWEROFF, as far as we're concerned.  The tools use
36            the distinction when we return the reason code to them.  */
37          SHUTDOWN_HALT = 4,
38 };
39
40 /* Ignore multiple shutdown requests. */
41 static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
42
43 struct suspend_info {
44         int cancelled;
45 };
46
47 static RAW_NOTIFIER_HEAD(xen_resume_notifier);
48
49 void xen_resume_notifier_register(struct notifier_block *nb)
50 {
51         raw_notifier_chain_register(&xen_resume_notifier, nb);
52 }
53 EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
54
55 void xen_resume_notifier_unregister(struct notifier_block *nb)
56 {
57         raw_notifier_chain_unregister(&xen_resume_notifier, nb);
58 }
59 EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
60
61 #ifdef CONFIG_HIBERNATE_CALLBACKS
62 static int xen_suspend(void *data)
63 {
64         struct suspend_info *si = data;
65         int err;
66
67         BUG_ON(!irqs_disabled());
68
69         err = syscore_suspend();
70         if (err) {
71                 pr_err("%s: system core suspend failed: %d\n", __func__, err);
72                 return err;
73         }
74
75         gnttab_suspend();
76         xen_manage_runstate_time(-1);
77         xen_arch_pre_suspend();
78
79         si->cancelled = HYPERVISOR_suspend(xen_pv_domain()
80                                            ? virt_to_gfn(xen_start_info)
81                                            : 0);
82
83         xen_arch_post_suspend(si->cancelled);
84         xen_manage_runstate_time(si->cancelled ? 1 : 0);
85         gnttab_resume();
86
87         if (!si->cancelled) {
88                 xen_irq_resume();
89                 xen_timer_resume();
90         }
91
92         syscore_resume();
93
94         return 0;
95 }
96
97 static void do_suspend(void)
98 {
99         int err;
100         struct suspend_info si;
101
102         shutting_down = SHUTDOWN_SUSPEND;
103
104         err = freeze_processes();
105         if (err) {
106                 pr_err("%s: freeze processes failed %d\n", __func__, err);
107                 goto out;
108         }
109
110         err = freeze_kernel_threads();
111         if (err) {
112                 pr_err("%s: freeze kernel threads failed %d\n", __func__, err);
113                 goto out_thaw;
114         }
115
116         err = dpm_suspend_start(PMSG_FREEZE);
117         if (err) {
118                 pr_err("%s: dpm_suspend_start %d\n", __func__, err);
119                 goto out_thaw;
120         }
121
122         printk(KERN_DEBUG "suspending xenstore...\n");
123         xs_suspend();
124
125         err = dpm_suspend_end(PMSG_FREEZE);
126         if (err) {
127                 pr_err("dpm_suspend_end failed: %d\n", err);
128                 si.cancelled = 0;
129                 goto out_resume;
130         }
131
132         xen_arch_suspend();
133
134         si.cancelled = 1;
135
136         err = stop_machine(xen_suspend, &si, cpumask_of(0));
137
138         /* Resume console as early as possible. */
139         if (!si.cancelled)
140                 xen_console_resume();
141
142         raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
143
144         dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
145
146         if (err) {
147                 pr_err("failed to start xen_suspend: %d\n", err);
148                 si.cancelled = 1;
149         }
150
151         xen_arch_resume();
152
153 out_resume:
154         if (!si.cancelled)
155                 xs_resume();
156         else
157                 xs_suspend_cancel();
158
159         dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
160
161 out_thaw:
162         thaw_processes();
163 out:
164         shutting_down = SHUTDOWN_INVALID;
165 }
166 #endif  /* CONFIG_HIBERNATE_CALLBACKS */
167
168 struct shutdown_handler {
169 #define SHUTDOWN_CMD_SIZE 11
170         const char command[SHUTDOWN_CMD_SIZE];
171         bool flag;
172         void (*cb)(void);
173 };
174
175 static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused)
176 {
177         switch (code) {
178         case SYS_DOWN:
179         case SYS_HALT:
180         case SYS_POWER_OFF:
181                 shutting_down = SHUTDOWN_POWEROFF;
182                 break;
183         default:
184                 break;
185         }
186         return NOTIFY_DONE;
187 }
188 static void do_poweroff(void)
189 {
190         switch (system_state) {
191         case SYSTEM_BOOTING:
192         case SYSTEM_SCHEDULING:
193                 orderly_poweroff(true);
194                 break;
195         case SYSTEM_RUNNING:
196                 orderly_poweroff(false);
197                 break;
198         default:
199                 /* Don't do it when we are halting/rebooting. */
200                 pr_info("Ignoring Xen toolstack shutdown.\n");
201                 break;
202         }
203 }
204
205 static void do_reboot(void)
206 {
207         shutting_down = SHUTDOWN_POWEROFF; /* ? */
208         ctrl_alt_del();
209 }
210
211 static struct shutdown_handler shutdown_handlers[] = {
212         { "poweroff",   true,   do_poweroff },
213         { "halt",       false,  do_poweroff },
214         { "reboot",     true,   do_reboot   },
215 #ifdef CONFIG_HIBERNATE_CALLBACKS
216         { "suspend",    true,   do_suspend  },
217 #endif
218 };
219
220 static void shutdown_handler(struct xenbus_watch *watch,
221                              const char *path, const char *token)
222 {
223         char *str;
224         struct xenbus_transaction xbt;
225         int err;
226         int idx;
227
228         if (shutting_down != SHUTDOWN_INVALID)
229                 return;
230
231  again:
232         err = xenbus_transaction_start(&xbt);
233         if (err)
234                 return;
235
236         str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
237         /* Ignore read errors and empty reads. */
238         if (XENBUS_IS_ERR_READ(str)) {
239                 xenbus_transaction_end(xbt, 1);
240                 return;
241         }
242
243         for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) {
244                 if (strcmp(str, shutdown_handlers[idx].command) == 0)
245                         break;
246         }
247
248         /* Only acknowledge commands which we are prepared to handle. */
249         if (idx < ARRAY_SIZE(shutdown_handlers))
250                 xenbus_write(xbt, "control", "shutdown", "");
251
252         err = xenbus_transaction_end(xbt, 0);
253         if (err == -EAGAIN) {
254                 kfree(str);
255                 goto again;
256         }
257
258         if (idx < ARRAY_SIZE(shutdown_handlers)) {
259                 shutdown_handlers[idx].cb();
260         } else {
261                 pr_info("Ignoring shutdown request: %s\n", str);
262                 shutting_down = SHUTDOWN_INVALID;
263         }
264
265         kfree(str);
266 }
267
268 #ifdef CONFIG_MAGIC_SYSRQ
269 static void sysrq_handler(struct xenbus_watch *watch, const char *path,
270                           const char *token)
271 {
272         char sysrq_key = '\0';
273         struct xenbus_transaction xbt;
274         int err;
275
276  again:
277         err = xenbus_transaction_start(&xbt);
278         if (err)
279                 return;
280         err = xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key);
281         if (err < 0) {
282                 /*
283                  * The Xenstore watch fires directly after registering it and
284                  * after a suspend/resume cycle. So ENOENT is no error but
285                  * might happen in those cases. ERANGE is observed when we get
286                  * an empty value (''), this happens when we acknowledge the
287                  * request by writing '\0' below.
288                  */
289                 if (err != -ENOENT && err != -ERANGE)
290                         pr_err("Error %d reading sysrq code in control/sysrq\n",
291                                err);
292                 xenbus_transaction_end(xbt, 1);
293                 return;
294         }
295
296         if (sysrq_key != '\0') {
297                 err = xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
298                 if (err) {
299                         pr_err("%s: Error %d writing sysrq in control/sysrq\n",
300                                __func__, err);
301                         xenbus_transaction_end(xbt, 1);
302                         return;
303                 }
304         }
305
306         err = xenbus_transaction_end(xbt, 0);
307         if (err == -EAGAIN)
308                 goto again;
309
310         if (sysrq_key != '\0')
311                 handle_sysrq(sysrq_key);
312 }
313
314 static struct xenbus_watch sysrq_watch = {
315         .node = "control/sysrq",
316         .callback = sysrq_handler
317 };
318 #endif
319
320 static struct xenbus_watch shutdown_watch = {
321         .node = "control/shutdown",
322         .callback = shutdown_handler
323 };
324
325 static struct notifier_block xen_reboot_nb = {
326         .notifier_call = poweroff_nb,
327 };
328
329 static int setup_shutdown_watcher(void)
330 {
331         int err;
332         int idx;
333 #define FEATURE_PATH_SIZE (SHUTDOWN_CMD_SIZE + sizeof("feature-"))
334         char node[FEATURE_PATH_SIZE];
335
336         err = register_xenbus_watch(&shutdown_watch);
337         if (err) {
338                 pr_err("Failed to set shutdown watcher\n");
339                 return err;
340         }
341
342
343 #ifdef CONFIG_MAGIC_SYSRQ
344         err = register_xenbus_watch(&sysrq_watch);
345         if (err) {
346                 pr_err("Failed to set sysrq watcher\n");
347                 return err;
348         }
349 #endif
350
351         for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) {
352                 if (!shutdown_handlers[idx].flag)
353                         continue;
354                 snprintf(node, FEATURE_PATH_SIZE, "feature-%s",
355                          shutdown_handlers[idx].command);
356                 err = xenbus_printf(XBT_NIL, "control", node, "%u", 1);
357                 if (err) {
358                         pr_err("%s: Error %d writing %s\n", __func__,
359                                 err, node);
360                         return err;
361                 }
362         }
363
364         return 0;
365 }
366
367 static int shutdown_event(struct notifier_block *notifier,
368                           unsigned long event,
369                           void *data)
370 {
371         setup_shutdown_watcher();
372         return NOTIFY_DONE;
373 }
374
375 int xen_setup_shutdown_event(void)
376 {
377         static struct notifier_block xenstore_notifier = {
378                 .notifier_call = shutdown_event
379         };
380
381         if (!xen_domain())
382                 return -ENODEV;
383         register_xenstore_notifier(&xenstore_notifier);
384         register_reboot_notifier(&xen_reboot_nb);
385
386         return 0;
387 }
388 EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
389
390 subsys_initcall(xen_setup_shutdown_event);