Commit 10a0a8d4e3f6bf2d077f94344441909abe670f5a
Committed by
Jeremy Fitzhardinge
1 parent
0ab4dc9227
Exists in
master
and in
4 other branches
Add common orderly_poweroff()
Various pieces of code around the kernel want to be able to trigger an orderly poweroff. This pulls them together into a single implementation. By default the poweroff command is /sbin/poweroff, but it can be set via sysctl: kernel/poweroff_cmd. This is split at whitespace, so it can include command-line arguments. This patch replaces four other instances of invoking either "poweroff" or "shutdown -h now": two sbus drivers, and acpi thermal management. sparc64 has its own "powerd"; still need to determine whether it should be replaced by orderly_poweroff(). Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Acked-by: Len Brown <lenb@kernel.org> Signed-off-by: Chris Wright <chrisw@sous-sol.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Randy Dunlap <randy.dunlap@oracle.com> Cc: Andi Kleen <ak@suse.de> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Arnd Bergmann <arnd@arndb.de> Cc: David S. Miller <davem@davemloft.net>
Showing 6 changed files with 79 additions and 30 deletions Side-by-side Diff
drivers/acpi/thermal.c
... | ... | @@ -40,6 +40,7 @@ |
40 | 40 | #include <linux/jiffies.h> |
41 | 41 | #include <linux/kmod.h> |
42 | 42 | #include <linux/seq_file.h> |
43 | +#include <linux/reboot.h> | |
43 | 44 | #include <asm/uaccess.h> |
44 | 45 | |
45 | 46 | #include <acpi/acpi_bus.h> |
... | ... | @@ -59,7 +60,6 @@ |
59 | 60 | #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 |
60 | 61 | #define ACPI_THERMAL_NOTIFY_HOT 0xF1 |
61 | 62 | #define ACPI_THERMAL_MODE_ACTIVE 0x00 |
62 | -#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" | |
63 | 63 | |
64 | 64 | #define ACPI_THERMAL_MAX_ACTIVE 10 |
65 | 65 | #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 |
... | ... | @@ -419,26 +419,6 @@ |
419 | 419 | return 0; |
420 | 420 | } |
421 | 421 | |
422 | -static int acpi_thermal_call_usermode(char *path) | |
423 | -{ | |
424 | - char *argv[2] = { NULL, NULL }; | |
425 | - char *envp[3] = { NULL, NULL, NULL }; | |
426 | - | |
427 | - | |
428 | - if (!path) | |
429 | - return -EINVAL; | |
430 | - | |
431 | - argv[0] = path; | |
432 | - | |
433 | - /* minimal command environment */ | |
434 | - envp[0] = "HOME=/"; | |
435 | - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
436 | - | |
437 | - call_usermodehelper(argv[0], argv, envp, 0); | |
438 | - | |
439 | - return 0; | |
440 | -} | |
441 | - | |
442 | 422 | static int acpi_thermal_critical(struct acpi_thermal *tz) |
443 | 423 | { |
444 | 424 | if (!tz || !tz->trips.critical.flags.valid) |
... | ... | @@ -456,7 +436,7 @@ |
456 | 436 | acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, |
457 | 437 | tz->trips.critical.flags.enabled); |
458 | 438 | |
459 | - acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF); | |
439 | + orderly_poweroff(true); | |
460 | 440 | |
461 | 441 | return 0; |
462 | 442 | } |
drivers/sbus/char/bbc_envctrl.c
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 | #include <linux/kthread.h> |
8 | 8 | #include <linux/delay.h> |
9 | 9 | #include <linux/kmod.h> |
10 | +#include <linux/reboot.h> | |
10 | 11 | #include <asm/oplib.h> |
11 | 12 | #include <asm/ebus.h> |
12 | 13 | |
... | ... | @@ -170,8 +171,6 @@ |
170 | 171 | static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp) |
171 | 172 | { |
172 | 173 | static int shutting_down = 0; |
173 | - static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; | |
174 | - char *argv[] = { "/sbin/shutdown", "-h", "now", NULL }; | |
175 | 174 | char *type = "???"; |
176 | 175 | s8 val = -1; |
177 | 176 | |
... | ... | @@ -195,7 +194,7 @@ |
195 | 194 | printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n"); |
196 | 195 | |
197 | 196 | shutting_down = 1; |
198 | - if (call_usermodehelper("/sbin/shutdown", argv, envp, 0) < 0) | |
197 | + if (orderly_poweroff(true) < 0) | |
199 | 198 | printk(KERN_CRIT "envctrl: shutdown execution failed\n"); |
200 | 199 | } |
201 | 200 |
drivers/sbus/char/envctrl.c
... | ... | @@ -26,6 +26,7 @@ |
26 | 26 | #include <linux/ioport.h> |
27 | 27 | #include <linux/miscdevice.h> |
28 | 28 | #include <linux/kmod.h> |
29 | +#include <linux/reboot.h> | |
29 | 30 | |
30 | 31 | #include <asm/ebus.h> |
31 | 32 | #include <asm/uaccess.h> |
... | ... | @@ -966,10 +967,6 @@ |
966 | 967 | static void envctrl_do_shutdown(void) |
967 | 968 | { |
968 | 969 | static int inprog = 0; |
969 | - static char *envp[] = { | |
970 | - "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; | |
971 | - char *argv[] = { | |
972 | - "/sbin/shutdown", "-h", "now", NULL }; | |
973 | 970 | int ret; |
974 | 971 | |
975 | 972 | if (inprog != 0) |
... | ... | @@ -977,7 +974,7 @@ |
977 | 974 | |
978 | 975 | inprog = 1; |
979 | 976 | printk(KERN_CRIT "kenvctrld: WARNING: Shutting down the system now.\n"); |
980 | - ret = call_usermodehelper("/sbin/shutdown", argv, envp, 0); | |
977 | + ret = orderly_poweroff(true); | |
981 | 978 | if (ret < 0) { |
982 | 979 | printk(KERN_CRIT "kenvctrld: WARNING: system shutdown failed!\n"); |
983 | 980 | inprog = 0; /* unlikely to succeed, but we could try again */ |
include/linux/reboot.h
kernel/sys.c
... | ... | @@ -2286,4 +2286,62 @@ |
2286 | 2286 | } |
2287 | 2287 | return err ? -EFAULT : 0; |
2288 | 2288 | } |
2289 | + | |
2290 | +char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; | |
2291 | + | |
2292 | +static void argv_cleanup(char **argv, char **envp) | |
2293 | +{ | |
2294 | + argv_free(argv); | |
2295 | +} | |
2296 | + | |
2297 | +/** | |
2298 | + * orderly_poweroff - Trigger an orderly system poweroff | |
2299 | + * @force: force poweroff if command execution fails | |
2300 | + * | |
2301 | + * This may be called from any context to trigger a system shutdown. | |
2302 | + * If the orderly shutdown fails, it will force an immediate shutdown. | |
2303 | + */ | |
2304 | +int orderly_poweroff(bool force) | |
2305 | +{ | |
2306 | + int argc; | |
2307 | + char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc); | |
2308 | + static char *envp[] = { | |
2309 | + "HOME=/", | |
2310 | + "PATH=/sbin:/bin:/usr/sbin:/usr/bin", | |
2311 | + NULL | |
2312 | + }; | |
2313 | + int ret = -ENOMEM; | |
2314 | + struct subprocess_info *info; | |
2315 | + | |
2316 | + if (argv == NULL) { | |
2317 | + printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n", | |
2318 | + __func__, poweroff_cmd); | |
2319 | + goto out; | |
2320 | + } | |
2321 | + | |
2322 | + info = call_usermodehelper_setup(argv[0], argv, envp); | |
2323 | + if (info == NULL) { | |
2324 | + argv_free(argv); | |
2325 | + goto out; | |
2326 | + } | |
2327 | + | |
2328 | + call_usermodehelper_setcleanup(info, argv_cleanup); | |
2329 | + | |
2330 | + ret = call_usermodehelper_exec(info, -1); | |
2331 | + | |
2332 | + out: | |
2333 | + if (ret && force) { | |
2334 | + printk(KERN_WARNING "Failed to start orderly shutdown: " | |
2335 | + "forcing the issue\n"); | |
2336 | + | |
2337 | + /* I guess this should try to kick off some daemon to | |
2338 | + sync and poweroff asap. Or not even bother syncing | |
2339 | + if we're doing an emergency shutdown? */ | |
2340 | + emergency_sync(); | |
2341 | + kernel_power_off(); | |
2342 | + } | |
2343 | + | |
2344 | + return ret; | |
2345 | +} | |
2346 | +EXPORT_SYMBOL_GPL(orderly_poweroff); |
kernel/sysctl.c
... | ... | @@ -46,6 +46,7 @@ |
46 | 46 | #include <linux/syscalls.h> |
47 | 47 | #include <linux/nfs_fs.h> |
48 | 48 | #include <linux/acpi.h> |
49 | +#include <linux/reboot.h> | |
49 | 50 | |
50 | 51 | #include <asm/uaccess.h> |
51 | 52 | #include <asm/processor.h> |
... | ... | @@ -705,6 +706,15 @@ |
705 | 706 | .proc_handler = &proc_dointvec, |
706 | 707 | }, |
707 | 708 | #endif |
709 | + { | |
710 | + .ctl_name = CTL_UNNUMBERED, | |
711 | + .procname = "poweroff_cmd", | |
712 | + .data = &poweroff_cmd, | |
713 | + .maxlen = POWEROFF_CMD_PATH_LEN, | |
714 | + .mode = 0644, | |
715 | + .proc_handler = &proc_dostring, | |
716 | + .strategy = &sysctl_string, | |
717 | + }, | |
708 | 718 | |
709 | 719 | { .ctl_name = 0 } |
710 | 720 | }; |