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 <mpe@ellerman.id.au> # ppce500 Reported-by: Michael Ellerman <mpe@ellerman.id.au> # ppce500 Fixes: da007f171fc9 ("kernel/reboot: Change registration order of legacy power-off handler") Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
587b9bfe06
commit
2b8c612c61
@ -320,6 +320,7 @@ static struct sys_off_handler platform_sys_off_handler;
|
|||||||
static struct sys_off_handler *alloc_sys_off_handler(int priority)
|
static struct sys_off_handler *alloc_sys_off_handler(int priority)
|
||||||
{
|
{
|
||||||
struct sys_off_handler *handler;
|
struct sys_off_handler *handler;
|
||||||
|
gfp_t flags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Platforms like m68k can't allocate sys_off handler dynamically
|
* 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)
|
|||||||
if (handler->cb_data)
|
if (handler->cb_data)
|
||||||
return ERR_PTR(-EBUSY);
|
return ERR_PTR(-EBUSY);
|
||||||
} else {
|
} 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)
|
if (!handler)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
@ -440,7 +446,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
|
|||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!handler)
|
if (IS_ERR_OR_NULL(handler))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (handler->blocking)
|
if (handler->blocking)
|
||||||
@ -615,7 +621,23 @@ static void do_kernel_power_off_prepare(void)
|
|||||||
*/
|
*/
|
||||||
void do_kernel_power_off(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);
|
atomic_notifier_call_chain(&power_off_handler_list, 0, NULL);
|
||||||
|
|
||||||
|
unregister_sys_off_handler(sys_off);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -626,7 +648,8 @@ void do_kernel_power_off(void)
|
|||||||
*/
|
*/
|
||||||
bool kernel_can_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);
|
EXPORT_SYMBOL_GPL(kernel_can_power_off);
|
||||||
|
|
||||||
@ -661,7 +684,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
|
|||||||
void __user *, arg)
|
void __user *, arg)
|
||||||
{
|
{
|
||||||
struct pid_namespace *pid_ns = task_active_pid_ns(current);
|
struct pid_namespace *pid_ns = task_active_pid_ns(current);
|
||||||
struct sys_off_handler *sys_off = NULL;
|
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -686,21 +708,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return 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
|
/* Instead of trying to make the power_off code look like
|
||||||
* halt when pm_power_off is not set do it the easy way.
|
* halt when pm_power_off is not set do it the easy way.
|
||||||
*/
|
*/
|
||||||
@ -758,7 +765,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&system_transition_mutex);
|
mutex_unlock(&system_transition_mutex);
|
||||||
unregister_sys_off_handler(sys_off);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user