manage.c 7.4 KB
Newer Older
1 2 3 4 5
/*
 * Handle extern requests for shutdown, reboot and sysrq
 */
#include <linux/kernel.h>
#include <linux/err.h>
6
#include <linux/slab.h>
7 8
#include <linux/reboot.h>
#include <linux/sysrq.h>
9 10
#include <linux/stop_machine.h>
#include <linux/freezer.h>
11

12
#include <xen/xen.h>
13
#include <xen/xenbus.h>
14 15 16 17
#include <xen/grant_table.h>
#include <xen/events.h>
#include <xen/hvc-console.h>
#include <xen/xen-ops.h>
18

19 20
#include <asm/xen/hypercall.h>
#include <asm/xen/page.h>
21
#include <asm/xen/hypervisor.h>
22 23 24 25 26 27 28 29 30 31 32

enum shutdown_state {
	SHUTDOWN_INVALID = -1,
	SHUTDOWN_POWEROFF = 0,
	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.  */
	 SHUTDOWN_HALT = 4,
};
33 34

/* Ignore multiple shutdown requests. */
35 36
static enum shutdown_state shutting_down = SHUTDOWN_INVALID;

37 38
struct suspend_info {
	int cancelled;
39
	unsigned long arg; /* extra hypercall argument */
40 41
	void (*pre)(void);
	void (*post)(int cancelled);
42 43
};

44
static void xen_hvm_post_suspend(int cancelled)
45
{
46
	xen_arch_hvm_post_suspend(cancelled);
47 48 49 50 51 52 53
	gnttab_resume();
}

static void xen_pre_suspend(void)
{
	xen_mm_pin_all();
	gnttab_suspend();
54
	xen_arch_pre_suspend();
55 56
}

57
static void xen_post_suspend(int cancelled)
58
{
59
	xen_arch_post_suspend(cancelled);
60 61 62 63
	gnttab_resume();
	xen_mm_unpin_all();
}

64
#ifdef CONFIG_PM_SLEEP
65
static int xen_hvm_suspend(void *data)
66
{
67
	struct suspend_info *si = data;
68
	int err;
69 70 71

	BUG_ON(!irqs_disabled());

72 73 74 75 76 77 78
	err = sysdev_suspend(PMSG_SUSPEND);
	if (err) {
		printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
		       err);
		return err;
	}

79 80 81
	if (si->pre)
		si->pre();

82 83 84 85 86
	/*
	 * This hypercall returns 1 if suspend was cancelled
	 * or the domain was merely checkpointed, and 0 if it
	 * is resuming in a new domain.
	 */
87
	si->cancelled = HYPERVISOR_suspend(si->arg);
88

89 90
	if (si->post)
		si->post(si->cancelled);
91

92
	if (!si->cancelled) {
93
		xen_irq_resume();
94
		xen_console_resume();
95 96 97
		xen_timer_resume();
	}

98 99
	sysdev_resume();

100 101 102 103 104
	return 0;
}

static int xen_suspend(void *data)
{
105
	struct suspend_info *si = data;
106
	int err;
107 108 109

	BUG_ON(!irqs_disabled());

110 111 112 113 114 115
	err = sysdev_suspend(PMSG_SUSPEND);
	if (err) {
		printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
			err);
		return err;
	}
116

117 118
	if (si->pre)
		si->pre();
119 120 121 122 123 124

	/*
	 * This hypercall returns 1 if suspend was cancelled
	 * or the domain was merely checkpointed, and 0 if it
	 * is resuming in a new domain.
	 */
125
	si->cancelled = HYPERVISOR_suspend(si->arg);
126

127 128
	if (si->post)
		si->post(si->cancelled);
129

130
	if (!si->cancelled) {
131 132
		xen_irq_resume();
		xen_console_resume();
133
		xen_timer_resume();
134 135
	}

136 137
	sysdev_resume();

138 139 140 141 142 143
	return 0;
}

static void do_suspend(void)
{
	int err;
144
	struct suspend_info si;
145 146 147 148 149 150 151 152 153 154

	shutting_down = SHUTDOWN_SUSPEND;

#ifdef CONFIG_PREEMPT
	/* If the kernel is preemptible, we need to freeze all the processes
	   to prevent them from being in the middle of a pagetable update
	   during suspend. */
	err = freeze_processes();
	if (err) {
		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
155
		goto out;
156 157 158
	}
#endif

159
	err = dpm_suspend_start(PMSG_SUSPEND);
160
	if (err) {
161
		printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
162
		goto out_thaw;
163 164
	}

I
Ian Campbell 已提交
165 166 167
	printk(KERN_DEBUG "suspending xenstore...\n");
	xs_suspend();

168
	err = dpm_suspend_noirq(PMSG_SUSPEND);
169
	if (err) {
170
		printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
171
		goto out_resume;
172 173
	}

174 175
	si.cancelled = 1;

176
	if (xen_hvm_domain()) {
177
		si.arg = 0UL;
178 179 180
		si.pre = NULL;
		si.post = &xen_hvm_post_suspend;
	} else {
181
		si.arg = virt_to_mfn(xen_start_info);
182 183 184
		si.pre = &xen_pre_suspend;
		si.post = &xen_post_suspend;
	}
185

186
	if (xen_hvm_domain())
187
		err = stop_machine(xen_hvm_suspend, &si, cpumask_of(0));
188
	else
189
		err = stop_machine(xen_suspend, &si, cpumask_of(0));
190 191 192

	dpm_resume_noirq(PMSG_RESUME);

193 194
	if (err) {
		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
195
		si.cancelled = 1;
196 197
	}

I
Ian Campbell 已提交
198
out_resume:
199
	if (!si.cancelled) {
200
		xen_arch_resume();
201
		xs_resume();
202
	} else
203
		xs_suspend_cancel();
204

205
	dpm_resume_end(PMSG_RESUME);
206

207 208
	/* Make sure timer events get retriggered on all CPUs */
	clock_was_set();
209 210

out_thaw:
211 212
#ifdef CONFIG_PREEMPT
	thaw_processes();
213
out:
214
#endif
215 216
	shutting_down = SHUTDOWN_INVALID;
}
217
#endif	/* CONFIG_PM_SLEEP */
218

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
struct shutdown_handler {
	const char *command;
	void (*cb)(void);
};

static void do_poweroff(void)
{
	shutting_down = SHUTDOWN_POWEROFF;
	orderly_poweroff(false);
}

static void do_reboot(void)
{
	shutting_down = SHUTDOWN_POWEROFF; /* ? */
	ctrl_alt_del();
}

236 237 238 239 240 241
static void shutdown_handler(struct xenbus_watch *watch,
			     const char **vec, unsigned int len)
{
	char *str;
	struct xenbus_transaction xbt;
	int err;
242 243 244 245 246 247 248 249 250 251
	static struct shutdown_handler handlers[] = {
		{ "poweroff",	do_poweroff },
		{ "halt",	do_poweroff },
		{ "reboot",	do_reboot   },
#ifdef CONFIG_PM_SLEEP
		{ "suspend",	do_suspend  },
#endif
		{NULL, NULL},
	};
	static struct shutdown_handler *handler;
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

	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;
	}

268 269 270 271 272 273 274 275
	for (handler = &handlers[0]; handler->command; handler++) {
		if (strcmp(str, handler->command) == 0)
			break;
	}

	/* Only acknowledge commands which we are prepared to handle. */
	if (handler->cb)
		xenbus_write(xbt, "control", "shutdown", "");
276 277 278 279 280 281 282

	err = xenbus_transaction_end(xbt, 0);
	if (err == -EAGAIN) {
		kfree(str);
		goto again;
	}

283 284
	if (handler->cb) {
		handler->cb();
285
	} else {
286 287 288 289 290 291 292
		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
		shutting_down = SHUTDOWN_INVALID;
	}

	kfree(str);
}

293
#ifdef CONFIG_MAGIC_SYSRQ
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
			  unsigned int len)
{
	char sysrq_key = '\0';
	struct xenbus_transaction 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;

	if (sysrq_key != '\0')
320
		handle_sysrq(sysrq_key);
321 322 323 324 325 326
}

static struct xenbus_watch sysrq_watch = {
	.node = "control/sysrq",
	.callback = sysrq_handler
};
327 328 329 330 331 332
#endif

static struct xenbus_watch shutdown_watch = {
	.node = "control/shutdown",
	.callback = shutdown_handler
};
333 334 335 336 337 338 339 340 341 342 343

static int setup_shutdown_watcher(void)
{
	int err;

	err = register_xenbus_watch(&shutdown_watch);
	if (err) {
		printk(KERN_ERR "Failed to set shutdown watcher\n");
		return err;
	}

344
#ifdef CONFIG_MAGIC_SYSRQ
345 346 347 348 349
	err = register_xenbus_watch(&sysrq_watch);
	if (err) {
		printk(KERN_ERR "Failed to set sysrq watcher\n");
		return err;
	}
350
#endif
351 352 353 354 355 356 357 358 359 360 361 362

	return 0;
}

static int shutdown_event(struct notifier_block *notifier,
			  unsigned long event,
			  void *data)
{
	setup_shutdown_watcher();
	return NOTIFY_DONE;
}

363
int xen_setup_shutdown_event(void)
364 365 366 367
{
	static struct notifier_block xenstore_notifier = {
		.notifier_call = shutdown_event
	};
368 369 370

	if (!xen_domain())
		return -ENODEV;
371 372 373 374
	register_xenstore_notifier(&xenstore_notifier);

	return 0;
}
375
EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
376

377
subsys_initcall(xen_setup_shutdown_event);