Linux Kernel Architecture

(Jacob Rumans) #1

Chapter 15: Time Management


The index position of the first group can be computed by masking the value ofbase->timer_jiffies
withTVR_MASK.

int index = base->timer_jiffies & TVR_MASK;

Generally, the following macro can be used to compute the current index position in groupN:

#define INDEX(N) (base->timer_jiffies >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK

Doubting Thomases can easily convince themselves of the correctness of the bit operations by means of a
short Perl script.

The implementation produces exactly the results described above using the following code
(__run_timersis called by the abovementionedrun_timer_softirq):

kernel/timer.c
static inline void __run_timers(tvec_base_t *base)
{

while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list;
struct list_head *head = &work_list;
int index = base->timer_jiffies & TVR_MASK;
...

If the kernel has missed a number of timers in the past, they are dealt with now by processing all pointers
that expired between the last execution point (base->timer_jiffies) and the current time (jiffies):

kernel/timer.c
if (!index &&
(!cascade(base, &base->tv2, INDEX(0))) &&
(!cascade(base, &base->tv3, INDEX(1))) &&
!cascade(base, &base->tv4, INDEX(2)))
cascade(base, &base->tv5, INDEX(3));
...

Thecascadefunction is used to replenish the timer lists with timers from higher groups (although its
implementation is not discussed here, suffice it to say that it uses the mechanism described above).

kernel/timer.c
++base->timer_jiffies;
list_replace_init(base->tv1.vec + index, &work_list);
...

All timers located in the first group at the corresponding position for thetimer_jiffiesvalue (which
is incremented by 1 for the next cycle) are copied into a temporary list and therefore removed from the
original data structures.

All that need then be done is to execute the individual handler routines:

kernel/timer.c
while (!list_empty(head)) {
void (*fn)(unsigned long);
Free download pdf