pm: Make pm_power_state_force multicore aware

Change pm_power_state_force to receive which cpu the state should be
forced. Also, it changed the API behavior to force the given state only
when the idle thread for that core is executed.

In a multicore environment force arbitrarily a core to suspend is not
safe because the kernel cannot infer what that cpu is running and how it
impacts the overall system, for example, if it is holding a lock that is
required by a thread that is running in another cpu.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
This commit is contained in:
Flavio Ceolin 2021-11-14 11:04:56 -08:00 committed by Carles Cufí
commit 4998c52ba8
5 changed files with 23 additions and 33 deletions

View file

@ -69,14 +69,15 @@ struct pm_notifier {
* @brief Force usage of given power state.
*
* This function overrides decision made by PM policy forcing
* usage of given power state immediately.
* usage of given power state upon next entry of the idle thread.
*
* @note This function can only run in thread context
*
* @param cpu CPU index.
* @param info Power state which should be used in the ongoing
* suspend operation.
*/
void pm_power_state_force(struct pm_state_info info);
bool pm_power_state_force(uint8_t cpu, struct pm_state_info info);
/**
* @brief Register a power management notifier

View file

@ -90,7 +90,7 @@ void main(void)
* controlled delay. Here we need to override that, then
* force entry to deep sleep on any delay.
*/
pm_power_state_force((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
printk("ERROR: System off failed\n");
while (true) {

View file

@ -118,5 +118,5 @@ void main(void)
printk("Device shutdown\n");
pm_power_state_force((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
}

View file

@ -67,7 +67,7 @@ void main(void)
/*
* Force the SOFT_OFF state.
*/
pm_power_state_force((struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
printk("ERROR: System off failed\n");
while (true) {

View file

@ -24,6 +24,8 @@ LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL);
static bool post_ops_done = true;
static sys_slist_t pm_notifiers = SYS_SLIST_STATIC_INIT(&pm_notifiers);
static struct pm_state_info z_power_states[CONFIG_MP_NUM_CPUS];
/* bitmask to check if a power state was forced. */
static ATOMIC_DEFINE(z_power_states_forced, CONFIG_MP_NUM_CPUS);
#ifdef CONFIG_PM_DEVICE
static atomic_t z_cpus_active = ATOMIC_INIT(CONFIG_MP_NUM_CPUS);
#endif
@ -239,43 +241,24 @@ void pm_system_resume(void)
pm_state_notify(false);
z_power_states[id] = (struct pm_state_info){PM_STATE_ACTIVE,
0, 0};
atomic_clear_bit(z_power_states_forced, id);
}
}
void pm_power_state_force(struct pm_state_info info)
bool pm_power_state_force(uint8_t cpu, struct pm_state_info info)
{
uint8_t id = _current_cpu->id;
bool ret = false;
__ASSERT(info.state < PM_STATES_LEN,
"Invalid power state %d!", info.state);
if (info.state == PM_STATE_ACTIVE) {
return;
if (!atomic_test_and_set_bit(z_power_states_forced, cpu)) {
z_power_states[cpu] = info;
ret = true;
}
(void)arch_irq_lock();
z_power_states[id] = info;
#ifdef CONFIG_PM_DEVICE
if (z_power_states[id].state != PM_STATE_RUNTIME_IDLE) {
(void)atomic_sub(&z_cpus_active, 1);
}
#endif
post_ops_done = false;
pm_state_notify(true);
k_sched_lock();
pm_start_timer();
/* Enter power state */
pm_state_set(info);
pm_stop_timer();
pm_system_resume();
#ifdef CONFIG_PM_DEVICE
(void)atomic_add(&z_cpus_active, 1);
#endif
k_sched_unlock();
return ret;
}
bool pm_system_suspend(int32_t ticks)
@ -283,11 +266,16 @@ bool pm_system_suspend(int32_t ticks)
uint8_t id = _current_cpu->id;
SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks);
z_power_states[id] = pm_policy_next_state(id, ticks);
if (!atomic_test_and_set_bit(z_power_states_forced, id)) {
z_power_states[id] = pm_policy_next_state(id, ticks);
}
if (z_power_states[id].state == PM_STATE_ACTIVE) {
LOG_DBG("No PM operations done.");
SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
z_power_states[id].state);
atomic_clear_bit(z_power_states_forced, id);
return false;
}
post_ops_done = false;
@ -320,6 +308,7 @@ bool pm_system_suspend(int32_t ticks)
(void)atomic_add(&z_cpus_active, 1);
SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
_handle_device_abort(z_power_states[id]));
atomic_clear_bit(z_power_states_forced, id);
return false;
}
}