tick broadcastframework and
NO_HZmode in the Linux kernel. We will continue to dive into the time management related stuff in the Linux kernel in this part and will be acquainted with yet another concept in the Linux kernel -
timers. Before we will look at timers in the Linux kernel, we have to learn some theory about this concept. Note that we will consider software timers in this part.
software timerconcept to allow to kernel functions could be invoked at future moment. Timers are widely used in the Linux kernel. For example, look in the net/netfilter/ipset/ip_set_list_set.c source code file. This source code file provides implementation of the framework for the managing of groups of IP addresses.
list_setstructure that contains
gcfiled in this source code file:
timer_listtype. This structure defined in the include/linux/timer.h header file and main point of this structure is to store
dynamictimers in the Linux kernel. Actually, the Linux kernel provides two types of timers called dynamic timers and interval timers. First type of timers is used by the kernel, and the second can be used by user mode. The
timer_liststructure contains actual
gctimer in our example represents timer for garbage collection. This timer will be initialized in the
gcpointer, will be called after timeout which is equal to the
tick broadcastframework and
NO_HZmode in the previous part. They will be initialized in the init/main.c source code file by the call of the
tick_initfunction. If we will look at this source code file, we will see that the next time management related function is:
init_timer_cpusdefined in the same source code file and just calls the
init_timer_cpufunction for each possible processor in the system:
possiblecpu, you can read the special part of this book which describes
cpumaskconcept in the Linux kernel. In short words, a
possibleprocessor is a processor which can be plugged in anytime during the life of the system.
init_timer_cpufunction does main work for us, namely it executes initialization of the
tvec_basestructure for each processor. This structure defined in the kernel/time/timer.c source code file and stores data related to a
dynamictimer for a certain processor. Let's look on the definition of this structure:
thec_basestructure contains following fields: The
tvec_baseprotection, the next
running_timerfield points to the currently running timer for the certain processor, the
timer_jiffiesfields represents the earliest expiration time (it will be used by the Linux kernel to find already expired timers). The next field -
next_timercontains the next pending timer for a next timer interrupt in a case when a processor goes to sleep and the
NO_HZmode is enabled in the Linux kernel. The
active_timersfield provides accounting of non-deferrable timers or in other words all timers that will not be stopped during a processor will go to sleep. The
all_timersfield tracks total number of timers or
active_timers+ deferrable timers. The
cpufield represents number of a processor which owns timers. The
nohz_activefields are represent opportunity of timers migration to another processor and status of the
tvec_basestructure represent lists of dynamic timers. The first
TVR_SIZEdepends on the
CONFIG_BASE_SMALLkernel configuration option:
v1is array that may contain
256elements where an each element represents a dynamic timer that will decay within the next
255system timer interrupts. Next three fields:
tv4are lists with dynamic timers too, but they store dynamic timers which will decay the next
2^14 - 1,
2^20 - 1and
2^26respectively. The last
tv5field represents list which stores dynamic timers with a large expiring period.
tvec_basestructure and description of its fields and we can look on the implementation of the
init_timer_cpufunction. As I already wrote, this function defined in the kernel/time/timer.c source code file and executes initialization of the
tvec_basesfor the given processor to
basevariable and as we got it, we are starting to initialize some of the
tvec_basefields in the
init_timer_cpufunction. After initialization of the
per-cpudynamic timers with the jiffies and the number of a possible processor, we need to initialize a
tstats_lookup_lockspinlock in the
tstats_lookup_lockspinlock is the call of the
timer_register_cpu_notifierfunction. This function depends on the
CONFIG_HOTPLUG_CPUkernel configuration option which enables support for hotplug processors in the Linux kernel.
CPU_DEAD_FROZENevent by the call of the
timer_cpu_notifywill be called which checks an event type and will call the
hotplugrelated events in the Linux kernel source code, but if you are interesting in such things, you can find implementation of the
migrate_timersfunction in the kernel/time/timer.c source code file.
init_timersfunction is the call of the:
open_softirqfunction may be already familiar to you if you have read the ninth part about the interrupts and interrupt handling in the Linux kernel. In short words, the
open_softirqfunction defined in the kernel/softirq.c source code file and executes initialization of the deferred interrupt handler.
run_timer_softirqfunction that is will be called after a hardware interrupt in the
do_IRQfunction which defined in the arch/x86/kernel/irq.c source code file. The main point of this function is to handle a software dynamic timer. The Linux kernel does not do this thing during the hardware timer interrupt handling because this is time consuming operation.
run_timer_softirqfunction we get a
dynamictimer for a current processor and compares the current value of the jiffies with the value of the
timer_jiffiesfor the current structure by the call of the
time_after_eqmacro which is defined in the include/linux/jiffies.h header file:
timer_jiffiesfield of the
tvec_basestructure represents the relative time when functions delayed by the given timer will be executed. So we compare these two values and if the current time represented by the
jiffiesis greater than
base->timer_jiffies, we call the
__run_timersfunction that defined in the same source code file. Let's look on the implementation of this function.
__run_timersfunction runs all expired timers for a given processor. This function starts from the acquiring of the
tvec_base'slock to protect the
timer_jiffieswill not be greater than the
base->tv1list that stores the next timer to be handled with the following expression:
TVR_MASKis a mask for the getting of the
tvec_root->vecelements. As we got the index with the next timer which must be handled we check its value. If the index is zero, we go through all lists in our cascade table
tv3and etc., and rehashing it with the call of the
call_timer_fnjust call the given function:
dynamic timersfrom this moment. We will not dive into this interesting theme. As I already wrote the
timersis a widely used concept in the Linux kernel and nor one part, nor two parts will not cover understanding of such things how it implemented and how it works. But now we know about this concept, why does the Linux kernel needs in it and some data structures around it.
dynamic timersin the Linux kernel.
dynamic timersconcept is not exception here. To use a timer in the Linux kernel code, we must define a variable with a
timer_listtype. We can initialize our
timer_liststructure in two ways. The first is to use the
init_timermacro that defined in the include/linux/timer.h header file:
init_timer_keyfunction just calls the:
timerwith default values. The second way is to use the:
dynamic timeris initialized we can start this
timerwith the call of the:
tick broadcastframework and the
NO_HZmode. In this part we continued to dive into time management related stuff and got acquainted with the new concept -
dynamic timeror software timer. We didn't saw implementation of a
dynamic timersmanagement code in details in this part but saw data structures and API around this concept.