linux/drivers/xen/manage.c
<<
>>
Prefs
   1/*
   2 * Handle extern requests for shutdown, reboot and sysrq
   3 */
   4#include <linux/kernel.h>
   5#include <linux/err.h>
   6#include <linux/slab.h>
   7#include <linux/reboot.h>
   8#include <linux/sysrq.h>
   9#include <linux/stop_machine.h>
  10#include <linux/freezer.h>
  11#include <linux/syscore_ops.h>
  12#include <linux/export.h>
  13
  14#include <xen/xen.h>
  15#include <xen/xenbus.h>
  16#include <xen/grant_table.h>
  17#include <xen/events.h>
  18#include <xen/hvc-console.h>
  19#include <xen/xen-ops.h>
  20
  21#include <asm/xen/hypercall.h>
  22#include <asm/xen/page.h>
  23#include <asm/xen/hypervisor.h>
  24
  25enum shutdown_state {
  26        SHUTDOWN_INVALID = -1,
  27        SHUTDOWN_POWEROFF = 0,
  28        SHUTDOWN_SUSPEND = 2,
  29        /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
  30           report a crash, not be instructed to crash!
  31           HALT is the same as POWEROFF, as far as we're concerned.  The tools use
  32           the distinction when we return the reason code to them.  */
  33         SHUTDOWN_HALT = 4,
  34};
  35
  36/* Ignore multiple shutdown requests. */
  37static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
  38
  39struct suspend_info {
  40        int cancelled;
  41        unsigned long arg; /* extra hypercall argument */
  42        void (*pre)(void);
  43        void (*post)(int cancelled);
  44};
  45
  46static void xen_hvm_post_suspend(int cancelled)
  47{
  48        xen_arch_hvm_post_suspend(cancelled);
  49        gnttab_resume();
  50}
  51
  52static void xen_pre_suspend(void)
  53{
  54        xen_mm_pin_all();
  55        gnttab_suspend();
  56        xen_arch_pre_suspend();
  57}
  58
  59static void xen_post_suspend(int cancelled)
  60{
  61        xen_arch_post_suspend(cancelled);
  62        gnttab_resume();
  63        xen_mm_unpin_all();
  64}
  65
  66#ifdef CONFIG_HIBERNATE_CALLBACKS
  67static int xen_suspend(void *data)
  68{
  69        struct suspend_info *si = data;
  70        int err;
  71
  72        BUG_ON(!irqs_disabled());
  73
  74        err = syscore_suspend();
  75        if (err) {
  76                printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
  77                        err);
  78                return err;
  79        }
  80
  81        if (si->pre)
  82                si->pre();
  83
  84        /*
  85         * This hypercall returns 1 if suspend was cancelled
  86         * or the domain was merely checkpointed, and 0 if it
  87         * is resuming in a new domain.
  88         */
  89        si->cancelled = HYPERVISOR_suspend(si->arg);
  90
  91        if (si->post)
  92                si->post(si->cancelled);
  93
  94        if (!si->cancelled) {
  95                xen_irq_resume();
  96                xen_console_resume();
  97                xen_timer_resume();
  98        }
  99
 100        syscore_resume();
 101
 102        return 0;
 103}
 104
 105static void do_suspend(void)
 106{
 107        int err;
 108        struct suspend_info si;
 109
 110        shutting_down = SHUTDOWN_SUSPEND;
 111
 112#ifdef CONFIG_PREEMPT
 113        /* If the kernel is preemptible, we need to freeze all the processes
 114           to prevent them from being in the middle of a pagetable update
 115           during suspend. */
 116        err = freeze_processes();
 117        if (err) {
 118                printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
 119                goto out;
 120        }
 121#endif
 122
 123        err = dpm_suspend_start(PMSG_FREEZE);
 124        if (err) {
 125                printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
 126                goto out_thaw;
 127        }
 128
 129        printk(KERN_DEBUG "suspending xenstore...\n");
 130        xs_suspend();
 131
 132        err = dpm_suspend_end(PMSG_FREEZE);
 133        if (err) {
 134                printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
 135                si.cancelled = 0;
 136                goto out_resume;
 137        }
 138
 139        si.cancelled = 1;
 140
 141        if (xen_hvm_domain()) {
 142                si.arg = 0UL;
 143                si.pre = NULL;
 144                si.post = &xen_hvm_post_suspend;
 145        } else {
 146                si.arg = virt_to_mfn(xen_start_info);
 147                si.pre = &xen_pre_suspend;
 148                si.post = &xen_post_suspend;
 149        }
 150
 151        err = stop_machine(xen_suspend, &si, cpumask_of(0));
 152
 153        dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
 154
 155        if (err) {
 156                printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
 157                si.cancelled = 1;
 158        }
 159
 160out_resume:
 161        if (!si.cancelled) {
 162                xen_arch_resume();
 163                xs_resume();
 164        } else
 165                xs_suspend_cancel();
 166
 167        dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
 168
 169        /* Make sure timer events get retriggered on all CPUs */
 170        clock_was_set();
 171
 172out_thaw:
 173#ifdef CONFIG_PREEMPT
 174        thaw_processes();
 175out:
 176#endif
 177        shutting_down = SHUTDOWN_INVALID;
 178}
 179#endif  /* CONFIG_HIBERNATE_CALLBACKS */
 180
 181struct shutdown_handler {
 182        const char *command;
 183        void (*cb)(void);
 184};
 185
 186static void do_poweroff(void)
 187{
 188        shutting_down = SHUTDOWN_POWEROFF;
 189        orderly_poweroff(false);
 190}
 191
 192static void do_reboot(void)
 193{
 194        shutting_down = SHUTDOWN_POWEROFF; /* ? */
 195        ctrl_alt_del();
 196}
 197
 198static void shutdown_handler(struct xenbus_watch *watch,
 199                             const char **vec, unsigned int len)
 200{
 201        char *str;
 202        struct xenbus_transaction xbt;
 203        int err;
 204        static struct shutdown_handler handlers[] = {
 205                { "poweroff",   do_poweroff },
 206                { "halt",       do_poweroff },
 207                { "reboot",     do_reboot   },
 208#ifdef CONFIG_HIBERNATE_CALLBACKS
 209                { "suspend",    do_suspend  },
 210#endif
 211                {NULL, NULL},
 212        };
 213        static struct shutdown_handler *handler;
 214
 215        if (shutting_down != SHUTDOWN_INVALID)
 216                return;
 217
 218 again:
 219        err = xenbus_transaction_start(&xbt);
 220        if (err)
 221                return;
 222
 223        str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
 224        /* Ignore read errors and empty reads. */
 225        if (XENBUS_IS_ERR_READ(str)) {
 226                xenbus_transaction_end(xbt, 1);
 227                return;
 228        }
 229
 230        for (handler = &handlers[0]; handler->command; handler++) {
 231                if (strcmp(str, handler->command) == 0)
 232                        break;
 233        }
 234
 235        /* Only acknowledge commands which we are prepared to handle. */
 236        if (handler->cb)
 237                xenbus_write(xbt, "control", "shutdown", "");
 238
 239        err = xenbus_transaction_end(xbt, 0);
 240        if (err == -EAGAIN) {
 241                kfree(str);
 242                goto again;
 243        }
 244
 245        if (handler->cb) {
 246                handler->cb();
 247        } else {
 248                printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
 249                shutting_down = SHUTDOWN_INVALID;
 250        }
 251
 252        kfree(str);
 253}
 254
 255#ifdef CONFIG_MAGIC_SYSRQ
 256static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
 257                          unsigned int len)
 258{
 259        char sysrq_key = '\0';
 260        struct xenbus_transaction xbt;
 261        int err;
 262
 263 again:
 264        err = xenbus_transaction_start(&xbt);
 265        if (err)
 266                return;
 267        if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
 268                printk(KERN_ERR "Unable to read sysrq code in "
 269                       "control/sysrq\n");
 270                xenbus_transaction_end(xbt, 1);
 271                return;
 272        }
 273
 274        if (sysrq_key != '\0')
 275                xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
 276
 277        err = xenbus_transaction_end(xbt, 0);
 278        if (err == -EAGAIN)
 279                goto again;
 280
 281        if (sysrq_key != '\0')
 282                handle_sysrq(sysrq_key);
 283}
 284
 285static struct xenbus_watch sysrq_watch = {
 286        .node = "control/sysrq",
 287        .callback = sysrq_handler
 288};
 289#endif
 290
 291static struct xenbus_watch shutdown_watch = {
 292        .node = "control/shutdown",
 293        .callback = shutdown_handler
 294};
 295
 296static int setup_shutdown_watcher(void)
 297{
 298        int err;
 299
 300        err = register_xenbus_watch(&shutdown_watch);
 301        if (err) {
 302                printk(KERN_ERR "Failed to set shutdown watcher\n");
 303                return err;
 304        }
 305
 306#ifdef CONFIG_MAGIC_SYSRQ
 307        err = register_xenbus_watch(&sysrq_watch);
 308        if (err) {
 309                printk(KERN_ERR "Failed to set sysrq watcher\n");
 310                return err;
 311        }
 312#endif
 313
 314        return 0;
 315}
 316
 317static int shutdown_event(struct notifier_block *notifier,
 318                          unsigned long event,
 319                          void *data)
 320{
 321        setup_shutdown_watcher();
 322        return NOTIFY_DONE;
 323}
 324
 325int xen_setup_shutdown_event(void)
 326{
 327        static struct notifier_block xenstore_notifier = {
 328                .notifier_call = shutdown_event
 329        };
 330
 331        if (!xen_domain())
 332                return -ENODEV;
 333        register_xenstore_notifier(&xenstore_notifier);
 334
 335        return 0;
 336}
 337EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
 338
 339subsys_initcall(xen_setup_shutdown_event);
 340
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.