Power management fixes for 5.19-rc2

- Fix CPUIDLE_FLAG_IRQ_ENABLE handling in intel_idle (Peter Zijlstra).
 
  - Allow all platforms to use the global poweroff handler and make
    non-syscall poweroff code paths work again (Dmitry Osipenko).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmKjkJUSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxugkQAKjDzcrVBffv4zOjijJS+ojnctRZShIw
 tWw/CUjm+VVAASSQ9XsaWFP/Eu3sA2tLOEaZddoAMCxzlwCgCVVZUgwChGmytiqr
 s4hJHyLkVAP8TszQoXvoyy5AIJI7Un33moXveGH9ZIbIAjtYckfInQJbyIsmVDF6
 VmOryqDxt+LS7hiXaAhEkExiy6HdfbmTt5f+tBO76p+eXtyj1hvkbUYrk7yX+Rv4
 6V+wDxZxy4LbhQDZQRzGYL3OBG33t9ThmVKi0Km2Aq6DXd5XIUMXZ9tYJ6+TYfe2
 scn6ZpTT87HLb8j96ZQUHU+MESvlBlpGdPGHwA2WORRqBL1sR0tsGqcfvHt5c57u
 mTrDFXaVs1vG4Zaa9JpxbrKwrNyi3xspG7EVKKtPfFHjREKhcLEh5k5F/x4ZQMY/
 U45hbEoLEWrP4Z68th7vL0T7E85tCW3+wp3FfzVkJ36H5cCy7CWAG5qpto0d3uo7
 6M3Xe+ms547R2zWWlGcdxynlhIfWK3lzYc1YZb6eoeb67m7Iyav2aehnOmko9wIO
 iNONbaB4SrwPdBi7BqaAX30G+IX/MDSu6EotI6FTJEWlJmBZPXryPBXaIgluhZWL
 8CurDLxNPFyXR3tj7BiLAYbIxGJwIqFHODS6cm6kioM8VA+WskcvLfE9+QbEXCEh
 tH+l1dzk+NYt
 =2OMR
 -----END PGP SIGNATURE-----

Merge tag 'pm-5.19-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These fix an intel_idle issue introduced during the 5.16 development
  cycle and two recent regressions in the system reboot/poweroff code.

  Specifics:

   - Fix CPUIDLE_FLAG_IRQ_ENABLE handling in intel_idle (Peter Zijlstra)

   - Allow all platforms to use the global poweroff handler and make
     non-syscall poweroff code paths work again (Dmitry Osipenko)"

* tag 'pm-5.19-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  cpuidle,intel_idle: Fix CPUIDLE_FLAG_IRQ_ENABLE
  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:
Linus Torvalds 2022-06-10 11:49:27 -07:00
commit 1bc27dec7e
2 changed files with 87 additions and 32 deletions

View File

@ -115,6 +115,18 @@ static unsigned int mwait_substates __initdata;
#define flg2MWAIT(flags) (((flags) >> 24) & 0xFF)
#define MWAIT2flg(eax) ((eax & 0xFF) << 24)
static __always_inline int __intel_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned long ecx = 1; /* break on interrupt flag */
mwait_idle_with_hints(eax, ecx);
return index;
}
/**
* intel_idle - Ask the processor to enter the given idle state.
* @dev: cpuidle device of the target CPU.
@ -132,16 +144,19 @@ static unsigned int mwait_substates __initdata;
static __cpuidle int intel_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned long ecx = 1; /* break on interrupt flag */
return __intel_idle(dev, drv, index);
}
if (state->flags & CPUIDLE_FLAG_IRQ_ENABLE)
local_irq_enable();
static __cpuidle int intel_idle_irq(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
int ret;
mwait_idle_with_hints(eax, ecx);
raw_local_irq_enable();
ret = __intel_idle(dev, drv, index);
raw_local_irq_disable();
return index;
return ret;
}
/**
@ -1801,6 +1816,9 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv)
/* Structure copy. */
drv->states[drv->state_count] = cpuidle_state_table[cstate];
if (cpuidle_state_table[cstate].flags & CPUIDLE_FLAG_IRQ_ENABLE)
drv->states[drv->state_count].enter = intel_idle_irq;
if ((disabled_states_mask & BIT(drv->state_count)) ||
((icpu->use_acpi || force_use_acpi) &&
intel_idle_off_by_default(mwait_hint) &&

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;
}