kernel/idle: Clean up and refactoring / remove TICKLESS_IDLE_THRESH
While I'm in the idle code, let's clean this loop up. It was a really bad #ifdef hell: * Remove the CONFIG_TICKLESS_IDLE_THRESH logic (and the kconfig), which never did anything but needlessly increase latency. * Move the needed timeout logic from the main loop into pm_save_idle(), which eliminates the special case for !SYS_CLOCK_EXISTS. Behavior (modulo that one kconfig) should be completely unchanged, and now the inner part of the idle loop looks like: while (true) { (void) arch_irq_lock(); if (IS_ENABLED(CONFIG_PM)) { pm_save_idle(); } else { k_cpu_idle(); } } Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
d72b5d16a6
commit
6400bb54d6
2 changed files with 44 additions and 81 deletions
|
@ -852,16 +852,6 @@ config TICKLESS_IDLE
|
||||||
saving state for extended periods without having to wake up to
|
saving state for extended periods without having to wake up to
|
||||||
service each tick as it occurs.
|
service each tick as it occurs.
|
||||||
|
|
||||||
config TICKLESS_IDLE_THRESH
|
|
||||||
int "Tickless idle threshold"
|
|
||||||
default 3
|
|
||||||
depends on TICKLESS_IDLE
|
|
||||||
help
|
|
||||||
This option enables clock interrupt suppression when the kernel idles
|
|
||||||
for only a short period of time. It specifies the minimum number of
|
|
||||||
ticks that must occur before the next kernel timer expires in order
|
|
||||||
for suppression to happen.
|
|
||||||
|
|
||||||
config TICKLESS_KERNEL
|
config TICKLESS_KERNEL
|
||||||
bool "Tickless kernel"
|
bool "Tickless kernel"
|
||||||
default y if TICKLESS_CAPABLE
|
default y if TICKLESS_CAPABLE
|
||||||
|
|
115
kernel/idle.c
115
kernel/idle.c
|
@ -14,21 +14,10 @@
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
#include <ksched.h>
|
#include <ksched.h>
|
||||||
|
|
||||||
|
extern uint32_t z_timestamp_idle;
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
||||||
|
|
||||||
#ifdef CONFIG_TICKLESS_IDLE_THRESH
|
|
||||||
#define IDLE_THRESH CONFIG_TICKLESS_IDLE_THRESH
|
|
||||||
#else
|
|
||||||
#define IDLE_THRESH 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Fallback idle spin loop for SMP platforms without a working IPI */
|
|
||||||
#if (defined(CONFIG_SMP) && !defined(CONFIG_SCHED_IPI_SUPPORTED))
|
|
||||||
#define SMP_FALLBACK 1
|
|
||||||
#else
|
|
||||||
#define SMP_FALLBACK 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
/*
|
/*
|
||||||
* Used to allow pm_system_suspend() implementation to control notification
|
* Used to allow pm_system_suspend() implementation to control notification
|
||||||
|
@ -36,7 +25,6 @@ LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
||||||
*/
|
*/
|
||||||
unsigned char pm_idle_exit_notify;
|
unsigned char pm_idle_exit_notify;
|
||||||
|
|
||||||
|
|
||||||
/* LCOV_EXCL_START
|
/* LCOV_EXCL_START
|
||||||
* These are almost certainly overidden and in any event do nothing
|
* These are almost certainly overidden and in any event do nothing
|
||||||
*/
|
*/
|
||||||
|
@ -52,20 +40,16 @@ void __attribute__((weak)) pm_system_resume_from_deep_sleep(void)
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @brief Indicate that kernel is idling in tickless mode
|
* @brief Indicate that kernel is idling in tickless mode
|
||||||
*
|
*
|
||||||
* Sets the kernel data structure idle field to either a positive value or
|
* Sets the kernel data structure idle field to either a positive value or
|
||||||
* K_FOREVER.
|
* K_FOREVER.
|
||||||
*
|
|
||||||
* @param ticks the number of ticks to idle
|
|
||||||
*
|
|
||||||
* @return N/A
|
|
||||||
*/
|
*/
|
||||||
#if !SMP_FALLBACK && CONFIG_PM
|
static void pm_save_idle(void)
|
||||||
static enum pm_state pm_save_idle(int32_t ticks)
|
|
||||||
{
|
{
|
||||||
static enum pm_state idle_state = PM_STATE_ACTIVE;
|
#ifdef CONFIG_PM
|
||||||
|
int32_t ticks = z_get_next_timeout_expiry();
|
||||||
|
_kernel.idle = ticks;
|
||||||
|
|
||||||
pm_idle_exit_notify = 1U;
|
pm_idle_exit_notify = 1U;
|
||||||
|
|
||||||
|
@ -82,16 +66,12 @@ static enum pm_state pm_save_idle(int32_t ticks)
|
||||||
* idle processing re-enables interrupts which is essential for
|
* idle processing re-enables interrupts which is essential for
|
||||||
* the kernel's scheduling logic.
|
* the kernel's scheduling logic.
|
||||||
*/
|
*/
|
||||||
idle_state = pm_system_suspend(ticks);
|
if (pm_system_suspend(ticks) == PM_STATE_ACTIVE) {
|
||||||
if (idle_state == PM_STATE_ACTIVE) {
|
|
||||||
pm_idle_exit_notify = 0U;
|
pm_idle_exit_notify = 0U;
|
||||||
|
k_cpu_idle();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return idle_state;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif /* !SMP_FALLBACK */
|
|
||||||
|
|
||||||
|
|
||||||
void z_pm_save_idle_exit(int32_t ticks)
|
void z_pm_save_idle_exit(int32_t ticks)
|
||||||
{
|
{
|
||||||
|
@ -105,64 +85,57 @@ void z_pm_save_idle_exit(int32_t ticks)
|
||||||
if (pm_idle_exit_notify) {
|
if (pm_idle_exit_notify) {
|
||||||
pm_system_resume();
|
pm_system_resume();
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM */
|
#endif
|
||||||
z_clock_idle_exit();
|
z_clock_idle_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void idle(void *unused1, void *unused2, void *unused3)
|
||||||
#if K_IDLE_PRIO < 0
|
|
||||||
#define IDLE_YIELD_IF_COOP() k_yield()
|
|
||||||
#else
|
|
||||||
#define IDLE_YIELD_IF_COOP() do { } while (false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void idle(void *p1, void *unused2, void *unused3)
|
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(unused1);
|
||||||
ARG_UNUSED(unused2);
|
ARG_UNUSED(unused2);
|
||||||
ARG_UNUSED(unused3);
|
ARG_UNUSED(unused3);
|
||||||
|
|
||||||
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
|
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
|
||||||
/* record timestamp when idling begins */
|
|
||||||
|
|
||||||
extern uint32_t z_timestamp_idle;
|
|
||||||
|
|
||||||
z_timestamp_idle = k_cycle_get_32();
|
z_timestamp_idle = k_cycle_get_32();
|
||||||
#endif /* CONFIG_BOOT_TIME_MEASUREMENT */
|
#endif
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
#if SMP_FALLBACK
|
/* SMP systems without a working IPI can't
|
||||||
k_busy_wait(100);
|
* actual enter an idle state, because they
|
||||||
k_yield();
|
* can't be notified of scheduler changes
|
||||||
#else
|
* (i.e. threads they should run). They just
|
||||||
|
* spin in a yield loop. This is intended as
|
||||||
|
* a fallback configuration for new platform
|
||||||
|
* bringup.
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_SMP) &&
|
||||||
|
!IS_ENABLED(CONFIG_SCHED_IPI_SUPPORTED)) {
|
||||||
|
k_busy_wait(100);
|
||||||
|
k_yield();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note weird API: k_cpu_idle() is called with local
|
||||||
|
* CPU interrupts masked, and returns with them
|
||||||
|
* unmasked. It does not take a spinlock or other
|
||||||
|
* higher level construct.
|
||||||
|
*/
|
||||||
(void) arch_irq_lock();
|
(void) arch_irq_lock();
|
||||||
|
|
||||||
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
if (IS_ENABLED(CONFIG_PM)) {
|
||||||
int32_t ticks = z_get_next_timeout_expiry();
|
pm_save_idle();
|
||||||
|
} else {
|
||||||
/* The documented behavior of CONFIG_TICKLESS_IDLE_THRESH is
|
|
||||||
* that the system should not enter a tickless idle for
|
|
||||||
* periods less than that. This seems... silly, given that it
|
|
||||||
* saves no power and does not improve latency. But it's an
|
|
||||||
* API we need to honor...
|
|
||||||
*/
|
|
||||||
z_set_timeout_expiry((ticks < IDLE_THRESH) ? 1 : ticks, true);
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
_kernel.idle = ticks;
|
|
||||||
/* Check power policy and decide if we are going to sleep or
|
|
||||||
* just idle.
|
|
||||||
*/
|
|
||||||
if (pm_save_idle(ticks) == PM_STATE_ACTIVE) {
|
|
||||||
k_cpu_idle();
|
k_cpu_idle();
|
||||||
}
|
}
|
||||||
#else /* CONFIG_PM */
|
|
||||||
k_cpu_idle();
|
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
#else /* CONFIG_SYS_CLOCK_EXISTS */
|
|
||||||
k_cpu_idle();
|
|
||||||
#endif /* CONFIG_SYS_CLOCK_EXISTS */
|
|
||||||
|
|
||||||
IDLE_YIELD_IF_COOP();
|
/* It is possible to (pathologically) configure the
|
||||||
#endif /* SMP_FALLBACK */
|
* idle thread to have a non-preemptible priority.
|
||||||
|
* You might think this is an API bug, but we actually
|
||||||
|
* have a test that exercises this. Handle the edge
|
||||||
|
* case when that happens.
|
||||||
|
*/
|
||||||
|
if (K_IDLE_PRIO < 0) {
|
||||||
|
k_yield();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue