Skip to content

Commit

Permalink
kernel/reboot: Fix powering off using a non-syscall code paths
Browse files Browse the repository at this point in the history
There are other methods of powering off machine than the reboot syscall.
Previously we missed to cover those methods and it created power-off
regression for some machines, like the PowerPC e500.

Fix this problem by moving the legacy sys-off handler registration to
the latest phase of power-off process and making the kernel_can_power_off()
check the legacy pm_power_off presence.

Tested-by: Michael Ellerman <[email protected]> # ppce500
Reported-by: Michael Ellerman <[email protected]> # ppce500
Fixes: da007f1 ("kernel/reboot: Change registration order of legacy power-off handler")
Signed-off-by: Dmitry Osipenko <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
digetx authored and rafaeljw committed Jun 7, 2022
1 parent 587b9bf commit 2b8c612
Showing 1 changed file with 26 additions and 20 deletions.
46 changes: 26 additions & 20 deletions kernel/reboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ static struct sys_off_handler platform_sys_off_handler;
static struct sys_off_handler *alloc_sys_off_handler(int priority)
{
struct sys_off_handler *handler;
gfp_t flags;

/*
* Platforms like m68k can't allocate sys_off handler dynamically
Expand All @@ -330,7 +331,12 @@ static struct sys_off_handler *alloc_sys_off_handler(int priority)
if (handler->cb_data)
return ERR_PTR(-EBUSY);
} else {
handler = kzalloc(sizeof(*handler), GFP_KERNEL);
if (system_state > SYSTEM_RUNNING)
flags = GFP_ATOMIC;
else
flags = GFP_KERNEL;

handler = kzalloc(sizeof(*handler), flags);
if (!handler)
return ERR_PTR(-ENOMEM);
}
Expand Down Expand Up @@ -440,7 +446,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
{
int err;

if (!handler)
if (IS_ERR_OR_NULL(handler))
return;

if (handler->blocking)
Expand Down Expand Up @@ -615,7 +621,23 @@ static void do_kernel_power_off_prepare(void)
*/
void do_kernel_power_off(void)
{
struct sys_off_handler *sys_off = NULL;

/*
* Register sys-off handlers for legacy PM callback. This allows
* legacy PM callbacks temporary co-exist with the new sys-off API.
*
* TODO: Remove legacy handlers once all legacy PM users will be
* switched to the sys-off based APIs.
*/
if (pm_power_off)
sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_DEFAULT,
legacy_pm_power_off, NULL);

atomic_notifier_call_chain(&power_off_handler_list, 0, NULL);

unregister_sys_off_handler(sys_off);
}

/**
Expand All @@ -626,7 +648,8 @@ void do_kernel_power_off(void)
*/
bool kernel_can_power_off(void)
{
return !atomic_notifier_call_chain_is_empty(&power_off_handler_list);
return !atomic_notifier_call_chain_is_empty(&power_off_handler_list) ||
pm_power_off;
}
EXPORT_SYMBOL_GPL(kernel_can_power_off);

Expand Down Expand Up @@ -661,7 +684,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
struct pid_namespace *pid_ns = task_active_pid_ns(current);
struct sys_off_handler *sys_off = NULL;
char buffer[256];
int ret = 0;

Expand All @@ -686,21 +708,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
if (ret)
return ret;

/*
* Register sys-off handlers for legacy PM callback. This allows
* legacy PM callbacks temporary co-exist with the new sys-off API.
*
* TODO: Remove legacy handlers once all legacy PM users will be
* switched to the sys-off based APIs.
*/
if (pm_power_off) {
sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_DEFAULT,
legacy_pm_power_off, NULL);
if (IS_ERR(sys_off))
return PTR_ERR(sys_off);
}

/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
Expand Down Expand Up @@ -758,7 +765,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
break;
}
mutex_unlock(&system_transition_mutex);
unregister_sys_off_handler(sys_off);
return ret;
}

Expand Down

0 comments on commit 2b8c612

Please sign in to comment.