summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Osipenko <dmitry.osipenko@collabora.com>2022-06-06 18:56:40 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-06-07 19:42:31 +0200
commit2b8c612c6102f751e6e3e1bd425f64e9d3d3f638 (patch)
tree4aec7da25b81670ec25d70e1b102932b0348de07
parentkernel/reboot: Use static handler for register_platform_power_off() (diff)
downloadlinux-2b8c612c6102f751e6e3e1bd425f64e9d3d3f638.tar.xz
linux-2b8c612c6102f751e6e3e1bd425f64e9d3d3f638.zip
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>
-rw-r--r--kernel/reboot.c46
1 files changed, 26 insertions, 20 deletions
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 3b19b123efec..b5a71d1ff603 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -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
@@ -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);
}
@@ -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)
@@ -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);
}
/**
@@ -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);
@@ -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;
@@ -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.
*/
@@ -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;
}