Merge branch 'pm-sysoff'

Merge fixes for regressions introduced by the recent rework of the
system reboot/poweroff code.

* pm-sysoff:
  kernel/reboot: Fix powering off using a non-syscall code paths
  kernel/reboot: Use static handler for register_platform_power_off()
This commit is contained in:
Rafael J. Wysocki 2022-06-10 20:24:10 +02:00
commit 67e59f8d01

View File

@ -315,6 +315,43 @@ static int sys_off_notify(struct notifier_block *nb,
return handler->sys_off_cb(&data);
}
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
* at the early boot time because memory allocator isn't available yet.
*/
if (priority == SYS_OFF_PRIO_PLATFORM) {
handler = &platform_sys_off_handler;
if (handler->cb_data)
return ERR_PTR(-EBUSY);
} else {
if (system_state > SYSTEM_RUNNING)
flags = GFP_ATOMIC;
else
flags = GFP_KERNEL;
handler = kzalloc(sizeof(*handler), flags);
if (!handler)
return ERR_PTR(-ENOMEM);
}
return handler;
}
static void free_sys_off_handler(struct sys_off_handler *handler)
{
if (handler == &platform_sys_off_handler)
memset(handler, 0, sizeof(*handler));
else
kfree(handler);
}
/**
* register_sys_off_handler - Register sys-off handler
* @mode: Sys-off mode
@ -345,9 +382,9 @@ register_sys_off_handler(enum sys_off_mode mode,
struct sys_off_handler *handler;
int err;
handler = kzalloc(sizeof(*handler), GFP_KERNEL);
if (!handler)
return ERR_PTR(-ENOMEM);
handler = alloc_sys_off_handler(priority);
if (IS_ERR(handler))
return handler;
switch (mode) {
case SYS_OFF_MODE_POWER_OFF_PREPARE:
@ -364,7 +401,7 @@ register_sys_off_handler(enum sys_off_mode mode,
break;
default:
kfree(handler);
free_sys_off_handler(handler);
return ERR_PTR(-EINVAL);
}
@ -391,7 +428,7 @@ register_sys_off_handler(enum sys_off_mode mode,
}
if (err) {
kfree(handler);
free_sys_off_handler(handler);
return ERR_PTR(err);
}
@ -409,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)
@ -422,7 +459,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
/* sanity check, shall never happen */
WARN_ON(err);
kfree(handler);
free_sys_off_handler(handler);
}
EXPORT_SYMBOL_GPL(unregister_sys_off_handler);
@ -584,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);
}
/**
@ -595,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);
@ -630,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;
@ -655,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.
*/
@ -727,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;
}