pm: Fix possible race condition in multicore

There is a race condition in a multicore system that happens when the
idle thread in a CPU checks if the state was forced, if not it will
call the policy manager. If a secondary core forces a state after that
this point the value returned by the policy will be rewritten.

Another case is, if a state is forced while a CPU is sleeping,
when this CPU resumes, the forced bit is cleared and the forced state
is never be used.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
This commit is contained in:
Flavio Ceolin 2022-05-05 20:58:43 -07:00 committed by Marti Bolivar
commit bbb8c91afa

View file

@ -35,15 +35,18 @@ static struct pm_state_info z_cpus_pm_state[] = {
LISTIFY(CONFIG_MP_NUM_CPUS, CPU_PM_STATE_INIT, (,))
};
/* bitmask to check if a power state was forced. */
static ATOMIC_DEFINE(z_cpus_pm_state_forced, CONFIG_MP_NUM_CPUS);
static struct pm_state_info z_cpus_pm_forced_state[] = {
LISTIFY(CONFIG_MP_NUM_CPUS, CPU_PM_STATE_INIT, (,))
};
static struct k_spinlock pm_forced_state_lock;
#ifdef CONFIG_PM_DEVICE
static atomic_t z_cpus_active = ATOMIC_INIT(CONFIG_MP_NUM_CPUS);
#endif
static struct k_spinlock pm_notifier_lock;
#ifdef CONFIG_PM_DEVICE
extern const struct device *__pm_device_slots_start[];
@ -182,27 +185,30 @@ void pm_system_resume(void)
bool pm_state_force(uint8_t cpu, const struct pm_state_info *info)
{
bool ret = false;
k_spinlock_key_t key;
__ASSERT(info->state < PM_STATE_COUNT,
"Invalid power state %d!", info->state);
key = k_spin_lock(&pm_forced_state_lock);
z_cpus_pm_forced_state[cpu] = *info;
k_spin_unlock(&pm_forced_state_lock, key);
if (!atomic_test_and_set_bit(z_cpus_pm_state_forced, cpu)) {
z_cpus_pm_state[cpu] = *info;
ret = true;
}
return ret;
return true;
}
bool pm_system_suspend(int32_t ticks)
{
uint8_t id = _current_cpu->id;
k_spinlock_key_t key;
SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks);
if (!atomic_test_bit(z_cpus_pm_state_forced, id)) {
key = k_spin_lock(&pm_forced_state_lock);
if (z_cpus_pm_forced_state[id].state != PM_STATE_ACTIVE) {
z_cpus_pm_state[id] = z_cpus_pm_forced_state[id];
z_cpus_pm_forced_state[id].state = PM_STATE_ACTIVE;
} else {
const struct pm_state_info *info;
info = pm_policy_next_state(id, ticks);
@ -210,12 +216,12 @@ bool pm_system_suspend(int32_t ticks)
z_cpus_pm_state[id] = *info;
}
}
k_spin_unlock(&pm_forced_state_lock, key);
if (z_cpus_pm_state[id].state == PM_STATE_ACTIVE) {
LOG_DBG("No PM operations done.");
SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
z_cpus_pm_state[id].state);
atomic_clear_bit(z_cpus_pm_state_forced, id);
return false;
}
@ -239,7 +245,6 @@ bool pm_system_suspend(int32_t ticks)
(void)atomic_add(&z_cpus_active, 1);
SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
z_cpus_pm_state[id].state);
atomic_clear_bit(z_cpus_pm_state_forced, id);
return false;
}
}
@ -269,7 +274,6 @@ bool pm_system_suspend(int32_t ticks)
#endif
pm_stats_update(z_cpus_pm_state[id].state);
pm_system_resume();
atomic_clear_bit(z_cpus_pm_state_forced, id);
k_sched_unlock();
SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
z_cpus_pm_state[id].state);