Current version


 Module timers

 HOWTO: Module usage





Author's homepage

APIC timer Module for Linux

Timers of the APIC timer module.

Author: Vincent Oberle


This document describes how timers are managed in the APIC timer module in order to get the less latency possible.

It also gives some information on how the kernel was patched to redirect the APIC timer IRQ.


The timers linked list
The APIC timer patch
How the timers are issued
From the user side

The timers linked list

The timers are managed in the form of a double-linked list of timer struct, similar to the generic timer_list in timer.h

The declaration of the timer apic_timer_list struct is the following:

struct apic_timer_list {
	struct apic_timer_list *next, *prev;
	unsigned long long expires;
	unsigned long data;
	void (*function)(unsigned long long, unsigned long);
expires is the value of the TSC register when the timer expires,
data is a value that will be passed to the timer function,
function is the function to execute; Its parameters are the expires value and data.

The linked list is ordered according to the expires value. It allows to issue the timers as fast as possible, without needing to search through the complete list.

The list itself, without any APIC stuff, is managed by 3 three functions:

  • void detach_apic_timer (struct apic_timer_list *timer)
    Removes the timer from the list.
  • inline void detach_first_apic_timer (void)
    Removes the first timer of the list.
  • void insert_apic_timer (struct apic_timer_list *timer)
    Adds a timer to the list, starting from the end and ordering it with the expires field.

The APIC timer patch

With the Pentium processor family, Intel introduced the Advanced Programmable Interrupt Controller (APIC) to replace the old PIC. This APIC is refered as the local APIC to distinguish it from the I/O APIC, which is an external controller to manage interrupts on SMP systems.

To summarize, there is no I/O APIC on UP boards but since P54C, all CPUs contain an on-chip local APIC. Nevertheless, the local APIC is generally disabled on P5 chips and cannot be enabled by software. Only P6 chips (ie starting with Pentium Pro) allow to enable the local APIC by software means. The disable pin only exists on P5.

But the kernel must be patched to use APIC interrupts.

If you want to use the interrupts generated by the APIC, like the timer interrupt in the module, you need to patch the kernel to redirect the corresponding Interrupt Service Routine (ISR) to your own ISR.

It is currently not possible to use a method like request_irq with APIC interrupts. I may try to implement something for the kernel, so that it would be possible to use the interrupts generated by the APIC like other interrupts (the keyboard IRQ for example).

To keep the patch as simple as possible, we preserve the original APIC timer ISR, smp_apic_timer_interrupt from the arch/i386/kernel/apic.c file.

A pointer on a supplementary ISR is added:

	void (*apic_timer_up_handler)(unsigned long);
and a function to modify this pointer:
	void set_apic_timer_up_handler (void (*f)(unsigned long))

In the smp_apic_timer_interrupt function, the supplementary handler is called if it is set.

There are a few other minor modifications to export the set_apic_timer_up_handler function. Everything is described in the README.patch file of the distribution.

How the timers are issued

The timers are issued by the run_apic_timer function. This function has two versions which can be chosen at the compilation of the module. Both checks if there are timers to be issued, but one calls first all corresponding timer functions and then detachs the timers that have been issued; and the other detach the timer and call the function for each timer to issue. The first is faster, but only the second allows the modifying the timer list in a timer function.

run_apic_timer is called by the APIC timer ISR and when a new timer is added. This second check needs to be done, because the APIC interrupt is masked (in software) while adding a new timer, so that the timers list must be checked.

The APIC timer is programmed on the first timer of the list. The start_apic_timer is used to convert the expires value (in "processor cycles" unit) into the correct value (in "bus cycles" unit) for the APIC timer. This function also starts the timer.

Both functions also contain some code to try to correct the latency. It allows to have the microsecond precision.

From the user side...

The user of the module only sees the apic_timer_list struct already described, and the three following functions:

  • int add_apic_timer (struct apic_timer_list *timer)
    Adds an APIC timer
  • int del_apic_timer (struct apic_timer_list *timer)
    Removes an APIC timer.
  • int mod_apic_timer (struct apic_timer_list *timer, unsigned long long expires)
    More efficient way to update the expire field of an active timer

Moreover, when creating a new apic_timer_list struct, the following function should always be used to properly initialize the struct:

	static inline void init_apic_timer (struct apic_timer_list *timer)

vincent at oberle dot org