Add remote reboot capability, so that a virtual machine can be
rebooted, halted or 'powered off' by external management tools.
Signed-off-by: Ian Pratt <[email protected]>
Signed-off-by: Christian Limpach <[email protected]>
Signed-off-by: Chris Wright <[email protected]>
---
TODO:
- move poweroff and halt to generic similar to c_a_d
arch/i386/kernel/Makefile | 1
drivers/xen/core/reboot.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+)
arch/i386/kernel/Makefile | 1
drivers/xen/core/reboot.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 233 insertions(+)
--- linus-2.6.orig/arch/i386/kernel/Makefile
+++ linus-2.6/arch/i386/kernel/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_SCx200) += scx200.o
hw_irq-y := i8259.o
hw_irq-$(CONFIG_XEN) := ../../../drivers/xen/core/evtchn.o
+reboot-$(CONFIG_XEN) := ../../../drivers/xen/core/reboot.o
time-$(CONFIG_XEN) := ../../../drivers/xen/core/time.o
# vsyscall.o contains the vsyscall DSO images as __initdata.
--- /dev/null
+++ linus-2.6/drivers/xen/core/reboot.c
@@ -0,0 +1,232 @@
+#define __KERNEL_SYSCALLS__
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/sysrq.h>
+#include <linux/stringify.h>
+#include <linux/syscalls.h>
+#include <linux/cpu.h>
+#include <linux/kthread.h>
+
+#include <xen/evtchn.h>
+#include <xen/xenbus.h>
+#include <xen/xencons.h>
+
+#include <asm/irq.h>
+#include <asm/mmu_context.h>
+#include <asm/hypervisor.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * Power off function, if any
+ */
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+#endif
+
+extern void ctrl_alt_del(void);
+
+#define SHUTDOWN_INVALID -1
+#define SHUTDOWN_POWEROFF 0
+#define SHUTDOWN_SUSPEND 2
+/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
+ * report a crash, not be instructed to crash!
+ * HALT is the same as POWEROFF, as far as we're concerned. The tools use
+ * the distinction when we return the reason code to them.
+ */
+#define SHUTDOWN_HALT 4
+
+void machine_emergency_restart(void)
+{
+ /* We really want to get pending console data out before we die. */
+ xencons_force_flush();
+ HYPERVISOR_sched_op(SCHEDOP_shutdown, SHUTDOWN_reboot);
+}
+
+void machine_restart(char * __unused)
+{
+ machine_emergency_restart();
+}
+
+void machine_halt(void)
+{
+ machine_power_off();
+}
+
+void machine_power_off(void)
+{
+ /* We really want to get pending console data out before we die. */
+ xencons_force_flush();
+ HYPERVISOR_sched_op(SCHEDOP_shutdown, SHUTDOWN_poweroff);
+}
+
+int reboot_thru_bios = 0; /* for dmi_scan.c */
+EXPORT_SYMBOL(machine_restart);
+EXPORT_SYMBOL(machine_halt);
+EXPORT_SYMBOL(machine_power_off);
+
+
+/******************************************************************************
+ * Stop/pickle callback handling.
+ */
+
+/* Ignore multiple shutdown requests. */
+static int shutting_down = SHUTDOWN_INVALID;
+static void __shutdown_handler(void *unused);
+static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
+
+static int shutdown_process(void *__unused)
+{
+ static char *envp[] = { "HOME=/", "TERM=linux",
+ "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
+
+ if ((shutting_down == SHUTDOWN_POWEROFF) ||
+ (shutting_down == SHUTDOWN_HALT)) {
+ if (execve(poweroff_argv[0], poweroff_argv, envp) < 0) {
+ sys_reboot(LINUX_REBOOT_MAGIC1,
+ LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_POWER_OFF,
+ NULL);
+ }
+ }
+
+ shutting_down = SHUTDOWN_INVALID; /* could try again */
+
+ return 0;
+}
+
+static void __shutdown_handler(void *unused)
+{
+ int err = 0;
+
+ if (shutting_down != SHUTDOWN_SUSPEND)
+ err = kernel_thread(shutdown_process, NULL,
+ CLONE_FS | CLONE_FILES);
+
+ if (err < 0) {
+ printk(KERN_WARNING "Error creating shutdown process (%d): "
+ "retrying...\n", -err);
+ schedule_delayed_work(&shutdown_work, HZ/2);
+ }
+}
+
+static void shutdown_handler(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ char *str;
+ xenbus_transaction_t xbt;
+ int err;
+
+ if (shutting_down != SHUTDOWN_INVALID)
+ return;
+
+ again:
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ return;
+ str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
+ /* Ignore read errors and empty reads. */
+ if (XENBUS_IS_ERR_READ(str)) {
+ xenbus_transaction_end(xbt, 1);
+ return;
+ }
+
+ xenbus_write(xbt, "control", "shutdown", "");
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN) {
+ kfree(str);
+ goto again;
+ }
+
+ if (strcmp(str, "poweroff") == 0)
+ shutting_down = SHUTDOWN_POWEROFF;
+ else if (strcmp(str, "reboot") == 0)
+ ctrl_alt_del();
+ else if (strcmp(str, "suspend") == 0)
+ shutting_down = SHUTDOWN_SUSPEND;
+ else if (strcmp(str, "halt") == 0)
+ shutting_down = SHUTDOWN_HALT;
+ else {
+ printk("Ignoring shutdown request: %s\n", str);
+ shutting_down = SHUTDOWN_INVALID;
+ }
+
+ if (shutting_down != SHUTDOWN_INVALID)
+ schedule_work(&shutdown_work);
+
+ kfree(str);
+}
+
+static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
+ unsigned int len)
+{
+ char sysrq_key = '\0';
+ xenbus_transaction_t xbt;
+ int err;
+
+ again:
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ return;
+ if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
+ printk(KERN_ERR "Unable to read sysrq code in "
+ "control/sysrq\n");
+ xenbus_transaction_end(xbt, 1);
+ return;
+ }
+
+ if (sysrq_key != '\0')
+ xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN)
+ goto again;
+
+#ifdef CONFIG_MAGIC_SYSRQ
+ if (sysrq_key != '\0')
+ handle_sysrq(sysrq_key, NULL, NULL);
+#endif
+}
+
+static struct xenbus_watch shutdown_watch = {
+ .node = "control/shutdown",
+ .callback = shutdown_handler
+};
+
+static struct xenbus_watch sysrq_watch = {
+ .node ="control/sysrq",
+ .callback = sysrq_handler
+};
+
+static int setup_shutdown_watcher(struct notifier_block *notifier,
+ unsigned long event,
+ void *data)
+{
+ int err;
+
+ err = register_xenbus_watch(&shutdown_watch);
+ if (err)
+ printk(KERN_ERR "Failed to set shutdown watcher\n");
+
+ err = register_xenbus_watch(&sysrq_watch);
+ if (err)
+ printk(KERN_ERR "Failed to set sysrq watcher\n");
+
+ return NOTIFY_DONE;
+}
+
+static int __init setup_shutdown_event(void)
+{
+ static struct notifier_block xenstore_notifier = {
+ .notifier_call = setup_shutdown_watcher
+ };
+ register_xenstore_notifier(&xenstore_notifier);
+ return 0;
+}
+
+subsys_initcall(setup_shutdown_event);
--
> +++ linus-2.6/drivers/xen/core/reboot.c
> @@ -0,0 +1,232 @@
> +#define __KERNEL_SYSCALLS__
> +#include <linux/version.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/unistd.h>
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/sysrq.h>
> +#include <linux/stringify.h>
> +#include <linux/syscalls.h>
> +#include <linux/cpu.h>
> +#include <linux/kthread.h>
Do you really need all these includes?
> +#if defined(__i386__) || defined(__x86_64__)
aka CONFIG_X86
> +/*
> + * Power off function, if any
> + */
> +void (*pm_power_off)(void);
> +EXPORT_SYMBOL(pm_power_off);
> +#endif
> +
> +extern void ctrl_alt_del(void);
That should be in some header
> +
> +/* Ignore multiple shutdown requests. */
> +static int shutting_down = SHUTDOWN_INVALID;
> +static void __shutdown_handler(void *unused);
> +static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
> +
> +static int shutdown_process(void *__unused)
> +{
> + static char *envp[] = { "HOME=/", "TERM=linux",
> + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
> + static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
This should be configurable, probably in a sysctl
> +
> + if ((shutting_down == SHUTDOWN_POWEROFF) ||
> + (shutting_down == SHUTDOWN_HALT)) {
> + if (execve(poweroff_argv[0], poweroff_argv, envp) < 0) {
> + sys_reboot(LINUX_REBOOT_MAGIC1,
> + LINUX_REBOOT_MAGIC2,
> + LINUX_REBOOT_CMD_POWER_OFF,
> + NULL);
> + }
> + }
> +
> + shutting_down = SHUTDOWN_INVALID; /* could try again */
> +
> + return 0;
> +}
> +
> +static void __shutdown_handler(void *unused)
> +{
> + int err = 0;
> +
> + if (shutting_down != SHUTDOWN_SUSPEND)
The whole shutting_down handling looks racy. Probably needs some locking?
-Andi
Hi!
> > +
> > +/* Ignore multiple shutdown requests. */
> > +static int shutting_down = SHUTDOWN_INVALID;
> > +static void __shutdown_handler(void *unused);
> > +static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
> > +
> > +static int shutdown_process(void *__unused)
> > +{
> > + static char *envp[] = { "HOME=/", "TERM=linux",
> > + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
> > + static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
>
> This should be configurable, probably in a sysctl
Actually we have similar code in sparc and acpi parts, IIRC. We
probably want to have one, common, shut-me-off routine.
--
Thanks for all the (sleeping) penguins.
* Pavel Machek ([email protected]) wrote:
> Hi!
>
> > > +
> > > +/* Ignore multiple shutdown requests. */
> > > +static int shutting_down = SHUTDOWN_INVALID;
> > > +static void __shutdown_handler(void *unused);
> > > +static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
> > > +
> > > +static int shutdown_process(void *__unused)
> > > +{
> > > + static char *envp[] = { "HOME=/", "TERM=linux",
> > > + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
> > > + static char *poweroff_argv[] = { "/sbin/poweroff", NULL };
> >
> > This should be configurable, probably in a sysctl
>
> Actually we have similar code in sparc and acpi parts, IIRC. We
> probably want to have one, common, shut-me-off routine.
Yep, I had that cleanup in mind, the patch said:
TODO:
- move poweroff and halt to generic similar to c_a_d
thanks,
-chris