• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/xen/
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
12#include <xen/xen.h>
13#include <xen/xenbus.h>
14#include <xen/grant_table.h>
15#include <xen/events.h>
16#include <xen/hvc-console.h>
17#include <xen/xen-ops.h>
18
19#include <asm/xen/hypercall.h>
20#include <asm/xen/page.h>
21#include <asm/xen/hypervisor.h>
22
23enum shutdown_state {
24	SHUTDOWN_INVALID = -1,
25	SHUTDOWN_POWEROFF = 0,
26	SHUTDOWN_SUSPEND = 2,
27	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
28	   report a crash, not be instructed to crash!
29	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
30	   the distinction when we return the reason code to them.  */
31	 SHUTDOWN_HALT = 4,
32};
33
34/* Ignore multiple shutdown requests. */
35static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
36
37#ifdef CONFIG_PM_SLEEP
38static int xen_hvm_suspend(void *data)
39{
40	struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
41	int *cancelled = data;
42
43	BUG_ON(!irqs_disabled());
44
45	*cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
46
47	xen_hvm_post_suspend(*cancelled);
48	gnttab_resume();
49
50	if (!*cancelled) {
51		xen_irq_resume();
52		xen_timer_resume();
53	}
54
55	return 0;
56}
57
58static int xen_suspend(void *data)
59{
60	int err;
61	int *cancelled = data;
62
63	BUG_ON(!irqs_disabled());
64
65	err = sysdev_suspend(PMSG_SUSPEND);
66	if (err) {
67		printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
68			err);
69		return err;
70	}
71
72	xen_mm_pin_all();
73	gnttab_suspend();
74	xen_pre_suspend();
75
76	/*
77	 * This hypercall returns 1 if suspend was cancelled
78	 * or the domain was merely checkpointed, and 0 if it
79	 * is resuming in a new domain.
80	 */
81	*cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
82
83	xen_post_suspend(*cancelled);
84	gnttab_resume();
85	xen_mm_unpin_all();
86
87	if (!*cancelled) {
88		xen_irq_resume();
89		xen_console_resume();
90		xen_timer_resume();
91	}
92
93	sysdev_resume();
94
95	return 0;
96}
97
98static void do_suspend(void)
99{
100	int err;
101	int cancelled = 1;
102
103	shutting_down = SHUTDOWN_SUSPEND;
104
105#ifdef CONFIG_PREEMPT
106	/* If the kernel is preemptible, we need to freeze all the processes
107	   to prevent them from being in the middle of a pagetable update
108	   during suspend. */
109	err = freeze_processes();
110	if (err) {
111		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
112		goto out;
113	}
114#endif
115
116	err = dpm_suspend_start(PMSG_SUSPEND);
117	if (err) {
118		printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
119		goto out_thaw;
120	}
121
122	printk(KERN_DEBUG "suspending xenstore...\n");
123	xs_suspend();
124
125	err = dpm_suspend_noirq(PMSG_SUSPEND);
126	if (err) {
127		printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
128		goto out_resume;
129	}
130
131	if (xen_hvm_domain())
132		err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
133	else
134		err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
135
136	dpm_resume_noirq(PMSG_RESUME);
137
138	if (err) {
139		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
140		cancelled = 1;
141	}
142
143out_resume:
144	if (!cancelled) {
145		xen_arch_resume();
146		xs_resume();
147	} else
148		xs_suspend_cancel();
149
150	dpm_resume_end(PMSG_RESUME);
151
152	/* Make sure timer events get retriggered on all CPUs */
153	clock_was_set();
154
155out_thaw:
156#ifdef CONFIG_PREEMPT
157	thaw_processes();
158out:
159#endif
160	shutting_down = SHUTDOWN_INVALID;
161}
162#endif	/* CONFIG_PM_SLEEP */
163
164static void shutdown_handler(struct xenbus_watch *watch,
165			     const char **vec, unsigned int len)
166{
167	char *str;
168	struct xenbus_transaction xbt;
169	int err;
170
171	if (shutting_down != SHUTDOWN_INVALID)
172		return;
173
174 again:
175	err = xenbus_transaction_start(&xbt);
176	if (err)
177		return;
178
179	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
180	/* Ignore read errors and empty reads. */
181	if (XENBUS_IS_ERR_READ(str)) {
182		xenbus_transaction_end(xbt, 1);
183		return;
184	}
185
186	xenbus_write(xbt, "control", "shutdown", "");
187
188	err = xenbus_transaction_end(xbt, 0);
189	if (err == -EAGAIN) {
190		kfree(str);
191		goto again;
192	}
193
194	if (strcmp(str, "poweroff") == 0 ||
195	    strcmp(str, "halt") == 0) {
196		shutting_down = SHUTDOWN_POWEROFF;
197		orderly_poweroff(false);
198	} else if (strcmp(str, "reboot") == 0) {
199		shutting_down = SHUTDOWN_POWEROFF; /* ? */
200		ctrl_alt_del();
201#ifdef CONFIG_PM_SLEEP
202	} else if (strcmp(str, "suspend") == 0) {
203		do_suspend();
204#endif
205	} else {
206		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
207		shutting_down = SHUTDOWN_INVALID;
208	}
209
210	kfree(str);
211}
212
213#ifdef CONFIG_MAGIC_SYSRQ
214static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
215			  unsigned int len)
216{
217	char sysrq_key = '\0';
218	struct xenbus_transaction xbt;
219	int err;
220
221 again:
222	err = xenbus_transaction_start(&xbt);
223	if (err)
224		return;
225	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
226		printk(KERN_ERR "Unable to read sysrq code in "
227		       "control/sysrq\n");
228		xenbus_transaction_end(xbt, 1);
229		return;
230	}
231
232	if (sysrq_key != '\0')
233		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
234
235	err = xenbus_transaction_end(xbt, 0);
236	if (err == -EAGAIN)
237		goto again;
238
239	if (sysrq_key != '\0')
240		handle_sysrq(sysrq_key);
241}
242
243static struct xenbus_watch sysrq_watch = {
244	.node = "control/sysrq",
245	.callback = sysrq_handler
246};
247#endif
248
249static struct xenbus_watch shutdown_watch = {
250	.node = "control/shutdown",
251	.callback = shutdown_handler
252};
253
254static int setup_shutdown_watcher(void)
255{
256	int err;
257
258	err = register_xenbus_watch(&shutdown_watch);
259	if (err) {
260		printk(KERN_ERR "Failed to set shutdown watcher\n");
261		return err;
262	}
263
264#ifdef CONFIG_MAGIC_SYSRQ
265	err = register_xenbus_watch(&sysrq_watch);
266	if (err) {
267		printk(KERN_ERR "Failed to set sysrq watcher\n");
268		return err;
269	}
270#endif
271
272	return 0;
273}
274
275static int shutdown_event(struct notifier_block *notifier,
276			  unsigned long event,
277			  void *data)
278{
279	setup_shutdown_watcher();
280	return NOTIFY_DONE;
281}
282
283static int __init __setup_shutdown_event(void)
284{
285	/* Delay initialization in the PV on HVM case */
286	if (xen_hvm_domain())
287		return 0;
288
289	if (!xen_pv_domain())
290		return -ENODEV;
291
292	return xen_setup_shutdown_event();
293}
294
295int xen_setup_shutdown_event(void)
296{
297	static struct notifier_block xenstore_notifier = {
298		.notifier_call = shutdown_event
299	};
300	register_xenstore_notifier(&xenstore_notifier);
301
302	return 0;
303}
304EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
305
306subsys_initcall(__setup_shutdown_event);
307