Clocks, timers, and power management

If your system needs to manage power consumption, you can set up your timers to help the system to sleep for longer intervals, saving power.

The kernel can reduce power consumption by running in tickless mode, although this is a bit of a misnomer. The system still has clock ticks, and everything runs as normal unless the system is idle. Only when the system goes completely idle does the kernel "turn off" clock ticks, and in reality what it does is slow down the clock so that the next tick interrupt occurs just after the next active timer is to fire.

In order for the kernel to enter tickless mode, the following must all be true:

If the kernel decides to enter tickless mode, it determines the number of nanoseconds until the next timer is supposed to expire. If that expiry is more than a certain time away, the kernel reprograms the timer hardware to generate its next interrupt shortly after that, and then sets some variables to indicate that it's gone tickless. When something other than the idle thread is made ready, the kernel stops tickless operation, checks the list of active timers, and fires off any were supposed to expire before it went to sleep.

So, what can you do to help?

If your timer doesn't have to be too precise, you can give it a tolerance value; the kernel then uses the expiry time plus the tolerance to decide if it should enter tickless operation or a low-power mode.

Note: This tolerance may lengthen, but never shortens, the amount of time before the timer expires.

To set the tolerance for a timer, call one of the following functions, specifying the TIMER_TOLERANCE flag:

For TimerSettime(), if the itime argument is non-NULL, the value it points to indicates the tolerance. If the otime argument is non-NULL, the previous tolerance value is stored in the memory it points to. You can call TimerSettime() with this flag at any point after calling TimerCreate(), without affecting the active/inactive status of the timer.

To determine the tolerance for a timer, call TimerInfo(), specifying the _NTO_TI_REPORT_TOLERANCE flag; the function puts the tolerance in the otime.interval_nsec field.

It's also possible to set a default timer tolerance for a process, to be used for timers that don't have a specific tolerance, by calling procmgr_timer_tolerance(). The default timer tolerance is inherited across a fork(), but not an exec*() or a spawn*().

Another way to reduce power consumption is to use "lazy" interrupts; for more information, see "Interrupts and power management" in the Writing an Interrupt Handler chapter in this guide.

Timer harmonization

Timer harmonization is the adjustment of a timer's expiry so as to increase the probability that it will occur at the same time as other periodic timers of the same interval.

Harmonization uses the CLOCK_HARMONIC clock ID. You can use it with the ClockPeriod() kernel call to set or report the harmonic timer boundary; the nsec field in the struct _clockperiod is used to specify the number of seconds.

Note: It's in seconds, not nanoseconds as usual, in spite of the field's name.

If the length of time before timer expiry exceeds the value set for CLOCK_HARMONIC, the timer is considered for harmonization processing.

Note: When the kernel is deciding if a timer's expiry time is long enough to be harmonized, it doesn't consider any tolerance specified for the timer, but it does consider any default timer tolerance specified for the process with procmgr_timer_tolerance().

You can exclude a timer from harmonization by specifying TIMER_PRECISE in the flags parameter of TimerSettime(), timer_settime(), TimerTimeout(), or timer_timeout(). If TIMER_PRECISE was specified for a timer, TimerInfo() reports _NTO_TI_PRECISE in the flags field of the result structure.

Coding with power management in mind

Here are some tips for helping the kernel reduce power consumption: