Skip to content

Commit 2b8c612

Browse files
digetxrafaeljw
authored andcommitted
kernel/reboot: Fix powering off using a non-syscall code paths
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]>
1 parent 587b9bf commit 2b8c612

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

kernel/reboot.c

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ static struct sys_off_handler platform_sys_off_handler;
320320
static struct sys_off_handler *alloc_sys_off_handler(int priority)
321321
{
322322
struct sys_off_handler *handler;
323+
gfp_t flags;
323324

324325
/*
325326
* Platforms like m68k can't allocate sys_off handler dynamically
@@ -330,7 +331,12 @@ static struct sys_off_handler *alloc_sys_off_handler(int priority)
330331
if (handler->cb_data)
331332
return ERR_PTR(-EBUSY);
332333
} else {
333-
handler = kzalloc(sizeof(*handler), GFP_KERNEL);
334+
if (system_state > SYSTEM_RUNNING)
335+
flags = GFP_ATOMIC;
336+
else
337+
flags = GFP_KERNEL;
338+
339+
handler = kzalloc(sizeof(*handler), flags);
334340
if (!handler)
335341
return ERR_PTR(-ENOMEM);
336342
}
@@ -440,7 +446,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
440446
{
441447
int err;
442448

443-
if (!handler)
449+
if (IS_ERR_OR_NULL(handler))
444450
return;
445451

446452
if (handler->blocking)
@@ -615,7 +621,23 @@ static void do_kernel_power_off_prepare(void)
615621
*/
616622
void do_kernel_power_off(void)
617623
{
624+
struct sys_off_handler *sys_off = NULL;
625+
626+
/*
627+
* Register sys-off handlers for legacy PM callback. This allows
628+
* legacy PM callbacks temporary co-exist with the new sys-off API.
629+
*
630+
* TODO: Remove legacy handlers once all legacy PM users will be
631+
* switched to the sys-off based APIs.
632+
*/
633+
if (pm_power_off)
634+
sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
635+
SYS_OFF_PRIO_DEFAULT,
636+
legacy_pm_power_off, NULL);
637+
618638
atomic_notifier_call_chain(&power_off_handler_list, 0, NULL);
639+
640+
unregister_sys_off_handler(sys_off);
619641
}
620642

621643
/**
@@ -626,7 +648,8 @@ void do_kernel_power_off(void)
626648
*/
627649
bool kernel_can_power_off(void)
628650
{
629-
return !atomic_notifier_call_chain_is_empty(&power_off_handler_list);
651+
return !atomic_notifier_call_chain_is_empty(&power_off_handler_list) ||
652+
pm_power_off;
630653
}
631654
EXPORT_SYMBOL_GPL(kernel_can_power_off);
632655

@@ -661,7 +684,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
661684
void __user *, arg)
662685
{
663686
struct pid_namespace *pid_ns = task_active_pid_ns(current);
664-
struct sys_off_handler *sys_off = NULL;
665687
char buffer[256];
666688
int ret = 0;
667689

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

689-
/*
690-
* Register sys-off handlers for legacy PM callback. This allows
691-
* legacy PM callbacks temporary co-exist with the new sys-off API.
692-
*
693-
* TODO: Remove legacy handlers once all legacy PM users will be
694-
* switched to the sys-off based APIs.
695-
*/
696-
if (pm_power_off) {
697-
sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
698-
SYS_OFF_PRIO_DEFAULT,
699-
legacy_pm_power_off, NULL);
700-
if (IS_ERR(sys_off))
701-
return PTR_ERR(sys_off);
702-
}
703-
704711
/* Instead of trying to make the power_off code look like
705712
* halt when pm_power_off is not set do it the easy way.
706713
*/
@@ -758,7 +765,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
758765
break;
759766
}
760767
mutex_unlock(&system_transition_mutex);
761-
unregister_sys_off_handler(sys_off);
762768
return ret;
763769
}
764770

0 commit comments

Comments
 (0)