poweroff: change orderly_poweroff() to use schedule_work()
David said:
Commit 6c0c0d4d10
("poweroff: fix bug in orderly_poweroff()")
apparently fixes one bug in orderly_poweroff(), but introduces
another. The comments on orderly_poweroff() claim it can be called
from any context - and indeed we call it from interrupt context in
arch/powerpc/platforms/pseries/ras.c for example. But since that
commit this is no longer safe, since call_usermodehelper_fns() is not
safe in interrupt context without the UMH_NO_WAIT option.
orderly_poweroff() can be used from any context but UMH_WAIT_EXEC is
sleepable. Move the "force" logic into __orderly_poweroff() and change
orderly_poweroff() to use the global poweroff_work which simply calls
__orderly_poweroff().
While at it, remove the unneeded "int argc" and change argv_split() to
use GFP_KERNEL.
We use the global "bool poweroff_force" to pass the argument, this can
obviously affect the previous request if it is pending/running. So we
only allow the "false => true" transition assuming that the pending
"true" should succeed anyway. If schedule_work() fails after that we
know that work->func() was not called yet, it must see the new value.
This means that orderly_poweroff() becomes async even if we do not run
the command and always succeeds, schedule_work() can only fail if the
work is already pending. We can export __orderly_poweroff() and change
the non-atomic callers which want the old semantics.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Reported-by: David Gibson <david@gibson.dropbear.id.au>
Cc: Lucas De Marchi <lucas.demarchi@profusion.mobi>
Cc: Feng Hong <hongfeng@marvell.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge Hallyn <serge.hallyn@canonical.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
d00285884c
commit
2ca067efd8
59
kernel/sys.c
59
kernel/sys.c
|
@ -2185,9 +2185,8 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
|
||||||
|
|
||||||
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
|
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
|
||||||
|
|
||||||
static int __orderly_poweroff(void)
|
static int __orderly_poweroff(bool force)
|
||||||
{
|
{
|
||||||
int argc;
|
|
||||||
char **argv;
|
char **argv;
|
||||||
static char *envp[] = {
|
static char *envp[] = {
|
||||||
"HOME=/",
|
"HOME=/",
|
||||||
|
@ -2196,35 +2195,19 @@ static int __orderly_poweroff(void)
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc);
|
argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL);
|
||||||
if (argv == NULL) {
|
if (argv) {
|
||||||
|
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
|
||||||
|
argv_free(argv);
|
||||||
|
} else {
|
||||||
printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
|
printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
|
||||||
__func__, poweroff_cmd);
|
__func__, poweroff_cmd);
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = call_usermodehelper_fns(argv[0], argv, envp, UMH_WAIT_EXEC,
|
|
||||||
NULL, NULL, NULL);
|
|
||||||
argv_free(argv);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* orderly_poweroff - Trigger an orderly system poweroff
|
|
||||||
* @force: force poweroff if command execution fails
|
|
||||||
*
|
|
||||||
* This may be called from any context to trigger a system shutdown.
|
|
||||||
* If the orderly shutdown fails, it will force an immediate shutdown.
|
|
||||||
*/
|
|
||||||
int orderly_poweroff(bool force)
|
|
||||||
{
|
|
||||||
int ret = __orderly_poweroff();
|
|
||||||
|
|
||||||
if (ret && force) {
|
if (ret && force) {
|
||||||
printk(KERN_WARNING "Failed to start orderly shutdown: "
|
printk(KERN_WARNING "Failed to start orderly shutdown: "
|
||||||
"forcing the issue\n");
|
"forcing the issue\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I guess this should try to kick off some daemon to sync and
|
* I guess this should try to kick off some daemon to sync and
|
||||||
* poweroff asap. Or not even bother syncing if we're doing an
|
* poweroff asap. Or not even bother syncing if we're doing an
|
||||||
|
@ -2236,4 +2219,28 @@ int orderly_poweroff(bool force)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool poweroff_force;
|
||||||
|
|
||||||
|
static void poweroff_work_func(struct work_struct *work)
|
||||||
|
{
|
||||||
|
__orderly_poweroff(poweroff_force);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_WORK(poweroff_work, poweroff_work_func);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* orderly_poweroff - Trigger an orderly system poweroff
|
||||||
|
* @force: force poweroff if command execution fails
|
||||||
|
*
|
||||||
|
* This may be called from any context to trigger a system shutdown.
|
||||||
|
* If the orderly shutdown fails, it will force an immediate shutdown.
|
||||||
|
*/
|
||||||
|
int orderly_poweroff(bool force)
|
||||||
|
{
|
||||||
|
if (force) /* do not override the pending "true" */
|
||||||
|
poweroff_force = true;
|
||||||
|
schedule_work(&poweroff_work);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(orderly_poweroff);
|
EXPORT_SYMBOL_GPL(orderly_poweroff);
|
||||||
|
|
Loading…
Reference in a new issue