commit
82c13852e7
@ -0,0 +1,444 @@
|
||||
Timers in the Linux kernel. Part 3.
|
||||
================================================================================
|
||||
|
||||
The tick broadcast framework and dyntick
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This is third part of the [chapter](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html) which describes timers and time management related stuff in the Linux kernel and we stopped on the `clocksource` framework in the previous [part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-2.html). We have started to consider this framework because it is closely related to the special counters which are provided by the Linux kernel. One of these counters which we already saw in the first [part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-1.html) of this chapter is - `jiffies`. As I already wrote in the first part of this chapter, we will consider time management related stuff step by step during the Linux kernel initialization. Previous step was call of the:
|
||||
|
||||
```C
|
||||
register_refined_jiffies(CLOCK_TICK_RATE);
|
||||
```
|
||||
|
||||
function which defined in the [kernel/time/jiffies.c](https://github.com/torvalds/linux/blob/master/kernel/time/jiffies.c) source code file and executes initialization of the `refined_jiffies` clock source for us. Recall that this function is called from the `setup_arch` function that defined in the [https://github.com/torvalds/linux/blob/master/arch/x86/kernel/setup.c](arch/x86/kernel/setup.c) source code and executes architecture-specific ([x86_64](https://en.wikipedia.org/wiki/X86-64) in our case) initialization. Look on the implementation of the `setup_arch` and you will note that the call of the `register_refined_jiffies` is the last step before the `setup_arch` function will finish its work.
|
||||
|
||||
There are many different `x86_64` specific things already configured after the end of the `setup_arch` execution. For example some early [interrupt](https://en.wikipedia.org/wiki/Interrupt) handlers already able to handle interrupts, memory space reserved for the [initrd](https://en.wikipedia.org/wiki/Initrd), [DMI](https://en.wikipedia.org/wiki/Desktop_Management_Interface) scanned, the Linux kernel log buffer is already set and this means that the [printk](https://en.wikipedia.org/wiki/Printk) function is able to work, [e820](https://en.wikipedia.org/wiki/E820) parsed and the Linux kernel already knows about available memory and and many many other architecture specific things (if you are interesting, you can read more about the `setup_arch` function and Linux kernel initialization process in the second [chapter](https://0xax.gitbooks.io/linux-insides/content/Initialization/index.html) of this book).
|
||||
|
||||
Now, the `setup_arch` finished its work and we can back to the generic Linux kernel code. Recall that the `setup_arch` function was called from the `start_kernel` function which is defined in the [init/main.c](https://github.com/torvalds/linux/blob/master/init/main.c) source code file. So, we shall return to this function. You can see that there are many different function are called right after `setup_arch` function inside of the `start_kernel` function, but since our chapter is devoted to timers and time management related stuff, we will skip all code which is not related to this topic. The first function which is related to the time management in the Linux kernel is:
|
||||
|
||||
```C
|
||||
tick_init();
|
||||
```
|
||||
|
||||
in the `start_kernel`. The `tick_init` function defined in the [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-common.c) source code file and does two things:
|
||||
|
||||
* Initialization of `tick broadcast` framework related data structures;
|
||||
* Initialization of `full` tickless mode related data structures.
|
||||
|
||||
We didn't see anything related to the `tick broadcast` framework in this book and didn't know anything about tickless mode in the Linux kernel. So, the main point of this part is to look on these concepts and to know what are they.
|
||||
|
||||
The idle process
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
First of all, let's look on the implementation of the `tick_init` function. As I already wrote, this function defined in the [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-common.c) source code file and consists from the two calls of following functions:
|
||||
|
||||
```C
|
||||
void __init tick_init(void)
|
||||
{
|
||||
tick_broadcast_init();
|
||||
tick_nohz_init();
|
||||
}
|
||||
```
|
||||
|
||||
As you can understand from the paragraph's title, we are interesting only in the `tick_broadcast_init` function for now. This function defined in the [kernel/time/tick-broadcast.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-broadcast.c) source code file and executes initialization of the `tick broadcast` framework related data structures. Before we will look on the implementation of the `tick_broadcast_init` function and will try to understand what does this function do, we need to know about `tick broadcast` framework.
|
||||
|
||||
Main point of a central processor is to execute programs. But somtimes a processor may be in a special state when it is not being used by any program. This special state is called - [idle](https://en.wikipedia.org/wiki/Idle_%28CPU%29). When the processor has no anything to execute, the Linux kernel launches `idle` task. We already saw a little about this in the last part of the [Linux kernel initialization process](https://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-10.html). When the Linux kernel will finish all initialization processes in the `start_kernel` function from the [init/main.c](https://github.com/torvalds/linux/blob/master/init/main.c) source code file, it will call the `rest_init` function from the same source code file. Main point of this function is to launch kernel `init` thread and the `kthreadd` thread, to call the `schedule` function to start task scheduling and to go to sleep by calling the `cpu_idle_loop` function that defined in the [kernel/sched/idle.c](https://github.com/torvalds/linux/blob/master/kernel/sched/idle.c) source code file.
|
||||
|
||||
The `cpu_idle_loop` function represents infinite loop which checks the need for rescheduling on each iteration. After the scheduller will fins something to execute, the `idle` process will finish its work and the control will be moved to a new runnable task with the call of the `schedule_preempt_disabled` function:
|
||||
|
||||
```C
|
||||
static void cpu_idle_loop(void)
|
||||
{
|
||||
while (1) {
|
||||
while (!need_resched()) {
|
||||
...
|
||||
...
|
||||
...
|
||||
/* the main idle function */
|
||||
cpuidle_idle_call();
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
schedule_preempt_disabled();
|
||||
}
|
||||
```
|
||||
|
||||
Of course, we will not consider full implementation of the `cpu_idle_loop` function and details of the `idle` state in this part, because it is not related to our topic. But there is one interesting moment for us. We know that the processor can execute only one task in one time. How does the Linux kernel decide to reschedule and stop `idle` process if the processor executes infinite loop in the `cpu_idle_loop`? The answer is system timer interrupts. When an interrupt occurs, the processor stops the `idle` thread and transfers control to an interrupt handler. After the system timer interrupt handler will be handled, the `need_resched` will return true and the Linux kernel will stop `idle` process and will transfer control to the current runnable task. But handling of the system timer interrupts is not effective for [power management](https://en.wikipedia.org/wiki/Power_management), because if a processor is in `idle` state, there is little point in sending it a system timer interrupt.
|
||||
|
||||
By default, there is the `CONFIG_HZ_PERIODIC` kernel configuration option which is enabled in the Linux kernel and tells to handle each interrupt of the system timer. To solve this problem, the Linux kernel provides two additional ways of managing scheduling-clock interrupts:
|
||||
|
||||
The first is to omit scheduling-clock ticks on idle processors. To enable this behaviour in the Linux kernel, we need to enable the `CONFIG_NO_HZ_IDLE` kernel configuration option. This option allows Linux kernel to avoid sending timer interrupts to idle processors. In this case periodic timer interrupts will be replaced with on-demand interrupts. This mode is called - `dyntick-idle` mode. But if the kernel does not handle interrupts of a system timer, how can the kernel decide if the system has nothing to do?
|
||||
|
||||
Whenever the idle task is selected to run, the periodic tick is disabled with the call of the `tick_nohz_idle_enter` function that defined in the [kernel/time/tick-sched.c](https://github.com/torvalds/linux/blob/master/kernel/time/tich-sched.c) source code file and enabled with the call of the `tick_nohz_idle_exit` function. There is special concept in the Linux kernel which is called - `clock event devices` that are used to schedule the next interrupt. This concept provides API for devices which can deliver interrupts at a specific time in the future and represented by the `clock_event_device` structure in the Linux kernel. We will not dive into implementation of the `clock_event_device` structure now. We will see it in the next prat of this chapter. But there is one interesting moment for us right now.
|
||||
|
||||
The second way is to omit scheduling-clock ticks on processors that are either in `idle` state or that have only one runnable task or in other words busy processor. We can enable this feature with the `CONFIG_NO_HZ_FULL` kernel configuration option and it allows to reduce the number of timer interrupts significantly.
|
||||
|
||||
Besides the `cpu_idle_loop`, idle processor can be in a sleeping state. The Linux kernel provides special `cpuidle` framework. Main point of this framework is to put an idle processor to sleeping states. The name of the set of these states is - `C-states`. But how does a processor will be woken if local timer is disabled? The linux kernel provides `tick broadcast` framework for this. The main point of this framework is assign a timer which is not affected by the `C-states`. This timer will wake a sleeping processor.
|
||||
|
||||
Now, after some theory we can return to the implementation of our function. Let's recall that the `tick_init` function just calls two following functions:
|
||||
|
||||
```C
|
||||
void __init tick_init(void)
|
||||
{
|
||||
tick_broadcast_init();
|
||||
tick_nohz_init();
|
||||
}
|
||||
```
|
||||
|
||||
Let's consider the first function. The first `tick_broadcast_init` function defined in the [kernel/time/tick-broadcast.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-broadcast.c) source code file and executes initialization of the `tick broadcast` framework related data structures. Let's look on the implementation of the `tick_broadcast_init` function:
|
||||
|
||||
```C
|
||||
void __init tick_broadcast_init(void)
|
||||
{
|
||||
zalloc_cpumask_var(&tick_broadcast_mask, GFP_NOWAIT);
|
||||
zalloc_cpumask_var(&tick_broadcast_on, GFP_NOWAIT);
|
||||
zalloc_cpumask_var(&tmpmask, GFP_NOWAIT);
|
||||
#ifdef CONFIG_TICK_ONESHOT
|
||||
zalloc_cpumask_var(&tick_broadcast_oneshot_mask, GFP_NOWAIT);
|
||||
zalloc_cpumask_var(&tick_broadcast_pending_mask, GFP_NOWAIT);
|
||||
zalloc_cpumask_var(&tick_broadcast_force_mask, GFP_NOWAIT);
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
As we can see, the `tick_broadcast_init` function allocates different [cpumasks](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html) with the help of the `zalloc_cpumask_var` function. The `zalloc_cpumask_var` function defined in the [lib/cpumask.c](https://github.com/torvalds/linux/blob/master/lib/cpumask.c) source code file and expands to the call of the following function:
|
||||
|
||||
```C
|
||||
bool zalloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
|
||||
{
|
||||
return alloc_cpumask_var(mask, flags | __GFP_ZERO);
|
||||
}
|
||||
```
|
||||
|
||||
Ultimately, the memory space will be allocated for the given `cpumask` with the certain flags with the help of the `kmalloc_node` function:
|
||||
|
||||
```C
|
||||
*mask = kmalloc_node(cpumask_size(), flags, node);
|
||||
```
|
||||
|
||||
Now let's look on the `cpumasks` that will be initialized in the `tick_broadcast_init` function. As we can see, the `tick_broadcast_init` function will initialize six `cpumasks`, and moreover, initialization of the last three `cpumasks` will be dependet on the `CONFIG_TICK_ONESHOT` kernel configuration option.
|
||||
|
||||
The first three `cpumasks` are:
|
||||
|
||||
* `tick_broadcast_mask` - the bitmap which represents list of processors that are in a sleeping mode;
|
||||
* `tick_broadcast_on` - the bitmap that stores numbers of processors which are in a periodic broadcast state;
|
||||
* `tmpmask` - this bitmap for temporary usage.
|
||||
|
||||
As we already know, the next three `cpumasks` depends on the `CONFIG_TICK_ONESHOT` kernel configuration option. Actually each clock event devices can be in one of two modes:
|
||||
|
||||
* `periodic` - clock events devices that support periodic events;
|
||||
* `oneshot` - clock events devices that capable of issuing events that happen only once.
|
||||
|
||||
The linux kernel defines two mask for such clock events devices in the [include/linux/clockchips.h](https://github.com/torvalds/linux/blob/master/include/linux/clockchips.h) header file:
|
||||
|
||||
```C
|
||||
#define CLOCK_EVT_FEAT_PERIODIC 0x000001
|
||||
#define CLOCK_EVT_FEAT_ONESHOT 0x000002
|
||||
```
|
||||
|
||||
So, the last three `cpumasks` are:
|
||||
|
||||
* `tick_broadcast_oneshot_mask` - stores numbers of processors that must be notified;
|
||||
* `tick_broadcast_pending_mask` - stores numbers of processors that pending broadcast;
|
||||
* `tick_broadcast_force_mask` - stores numbers of processors with enforced broadcast.
|
||||
|
||||
We have initialized six `cpumasks` in the `tick broadcast` framework, and now we can proceed to implementation of this framework.
|
||||
|
||||
The `tick boradcast` framework
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Hardware may provide some clock source devices. When a processor sleeps and its local timer stopped, there must be additional clock source device that will handle awakening of a processor. The Linux kernel uses these `special` clock source devices which can raise an interrupt at a specified time. We already know that such timers called `clock events` devices in the Linux kernel. Besides `clock events` devices. Actually, each processor in the system has its own local timer which is programmed to issue interrupt at the time of the next deferred task. Also these timers can be programmed to do a periodical job, like updating `jiffies` and etc. These timers represented by the `tick_device` structure in the Linux kernel. This structure defined in the [kernel/time/tick-sched.h](https://github.com/torvalds/linux/blob/master/kernel/time/tick-sched.h) header file and looks:
|
||||
|
||||
```C
|
||||
struct tick_device {
|
||||
struct clock_event_device *evtdev;
|
||||
enum tick_device_mode mode;
|
||||
};
|
||||
```
|
||||
|
||||
Note, that the `tick_device` structure contains two fields. The first field - `evtdev` represents pointer to the `clock_event_device` structure that defined in the [include/linux/clockchips.h](https://github.com/torvalds/linux/blob/master/include/linux/clockchips.h) header file and represents descriptor of a clock event device. A `clock event` device allows to register an event that will happen in the future. As I already wrote, we will not consider `clock_event_device` structure and related API in this part, but will see it in the next part.
|
||||
|
||||
The second field of the `tick_device` structure represents mode of the `tick_device`. As we already know, the mode can be one of the:
|
||||
|
||||
```C
|
||||
num tick_device_mode {
|
||||
TICKDEV_MODE_PERIODIC,
|
||||
TICKDEV_MODE_ONESHOT,
|
||||
};
|
||||
```
|
||||
|
||||
Each `clock events` device in the system registers itself by the call of the `clockevents_register_device` function or `clockevents_config_and_register` function during initialization process of the Linux kernel. During the registration of a new `clock events` device, the Linux kernel calls the `tick_check_new_device` function that defined in the [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/tick-common.c) source code file and checks the given `clock events` device should be used by the Linux kernel. After all checks, the `tick_check_new_device` function executes a call of the:
|
||||
|
||||
```C
|
||||
tick_install_broadcast_device(newdev);
|
||||
```
|
||||
|
||||
function that cheks that the given `clock event` device can be broadcast device and install it, if the given device can be broadcast device. Let's look on the implementation of the `tick_install_broadcast_device` function:
|
||||
|
||||
```C
|
||||
void tick_install_broadcast_device(struct clock_event_device *dev)
|
||||
{
|
||||
struct clock_event_device *cur = tick_broadcast_device.evtdev;
|
||||
|
||||
if (!tick_check_broadcast_device(cur, dev))
|
||||
return;
|
||||
|
||||
if (!try_module_get(dev->owner))
|
||||
return;
|
||||
|
||||
clockevents_exchange_device(cur, dev);
|
||||
|
||||
if (cur)
|
||||
cur->event_handler = clockevents_handle_noop;
|
||||
|
||||
tick_broadcast_device.evtdev = dev;
|
||||
|
||||
if (!cpumask_empty(tick_broadcast_mask))
|
||||
tick_broadcast_start_periodic(dev);
|
||||
|
||||
if (dev->features & CLOCK_EVT_FEAT_ONESHOT)
|
||||
tick_clock_notify();
|
||||
}
|
||||
```
|
||||
|
||||
First of all we get the current `clock event` device from the `tick_broadcast_device`. The `tick_broadcast_device` defined in the [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/tick-common.c) source code file:
|
||||
|
||||
```C
|
||||
static struct tick_device tick_broadcast_device;
|
||||
```
|
||||
|
||||
and represents external clock device that keeps track of events for a processor. The first step after we got the current clock device is the call of the `tick_check_broadcast_device` function which checks that a given clock events device can be utilized as broadcast device. The main point of the `tick_check_broadcast_device` function is to check value of the `features` field of the given `clock events` device. As we can understand from the name of this field, the `features` field contains a clock event device features. Avaliable values defined in the [include/linux/clockchips.h](https://github.com/torvalds/linux/blob/master/include/linux/clockchips.h) header file and can be one of the `CLOCK_EVT_FEAT_PERIODIC` - which represents a clock events device which supports periodic events and etc. So, the `tick_check_broadcast_device` function check `features` flags for `CLOCK_EVT_FEAT_ONESHOT`, `CLOCK_EVT_FEAT_DUMMY` and other flags and returns `false` if the given clock events device has one of these features. In other way the `tick_check_broadcast_device` function compares `ratings` of the given clock event device and current clock event device and returns the best.
|
||||
|
||||
After the `tick_check_broadcast_device` function, we can see the call of the `try_module_get` function that checks module owner of the clock events. We need to do it to be sure that the given `clock events` device was correctly initialized. The next step is the call of the `clockevents_exchange_device` function that defined in the [kernel/time/clockevents.c](https://github.com/torvalds/linux/blob/master/kernel/time/clockevents.c) source code file and will release old clock events device and replace the previous functional handler with a dummy handler.
|
||||
|
||||
In the last step of the `tick_install_broadcast_device` function we check that the `tick_broadcast_mask` is not empty and start the given `clock events` device in periodic mode with the call of the `tick_broadcast_start_periodic` function:
|
||||
|
||||
```C
|
||||
if (!cpumask_empty(tick_broadcast_mask))
|
||||
tick_broadcast_start_periodic(dev);
|
||||
|
||||
if (dev->features & CLOCK_EVT_FEAT_ONESHOT)
|
||||
tick_clock_notify();
|
||||
```
|
||||
|
||||
The `tick_broadcast_mask` filled in the `tick_device_uses_broadcast` function that checks a `clock events` device during registration of this `clock events` device:
|
||||
|
||||
```C
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
||||
{
|
||||
...
|
||||
...
|
||||
...
|
||||
if (!tick_device_is_functional(dev)) {
|
||||
...
|
||||
cpumask_set_cpu(cpu, tick_broadcast_mask);
|
||||
...
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
More about the `smp_processor_id` macro you can read in the fourth [part](https://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-4.html) of the Linux kernel initialization process chapter.
|
||||
|
||||
The `tick_broadcast_start_periodic` function check the given `clock event` device and call the `tick_setup_periodic` function:
|
||||
|
||||
```
|
||||
static void tick_broadcast_start_periodic(struct clock_event_device *bc)
|
||||
{
|
||||
if (bc)
|
||||
tick_setup_periodic(bc, 1);
|
||||
}
|
||||
```
|
||||
|
||||
that defined in the [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-common.c) source code file and sets broadcast handler for the given `clock event` device by the call of the following function:
|
||||
|
||||
```C
|
||||
tick_set_periodic_handler(dev, broadcast);
|
||||
```
|
||||
|
||||
This function checks the second parameter which represents broadcast state (`on` or `off`) and sets the broadcast handler depends on its value:
|
||||
|
||||
```C
|
||||
void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast)
|
||||
{
|
||||
if (!broadcast)
|
||||
dev->event_handler = tick_handle_periodic;
|
||||
else
|
||||
dev->event_handler = tick_handle_periodic_broadcast;
|
||||
}
|
||||
```
|
||||
|
||||
When an `clock event` device will issue an interrupt, the `dev->event_handler` will be called. For example, let's look on the interrupt handler of the [high precision event timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer) which is located in the [arch/x86/kernel/hpet.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/hpet.c) source code file:
|
||||
|
||||
```C
|
||||
static irqreturn_t hpet_interrupt_handler(int irq, void *data)
|
||||
{
|
||||
struct hpet_dev *dev = (struct hpet_dev *)data;
|
||||
struct clock_event_device *hevt = &dev->evt;
|
||||
|
||||
if (!hevt->event_handler) {
|
||||
printk(KERN_INFO "Spurious HPET timer interrupt on HPET timer %d\n",
|
||||
dev->num);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
hevt->event_handler(hevt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
```
|
||||
|
||||
The `hpet_interrupt_handler` gets the [irq](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29) specific data and check the event handler of the `clock event` device. Recall that we just set in the `tick_set_periodic_handler` function. So the `tick_handler_periodic_broadcast` function will be called in the end of the high precision event timer interrupt handler.
|
||||
|
||||
The `tick_handler_periodic_broadcast` function calls the
|
||||
|
||||
```C
|
||||
bc_local = tick_do_periodic_broadcast();
|
||||
```
|
||||
|
||||
function which stores numbers of processors which have asked to be woken up in the temporary `cpumask` and call the `tick_do_broadcast` function:
|
||||
|
||||
```
|
||||
cpumask_and(tmpmask, cpu_online_mask, tick_broadcast_mask);
|
||||
return tick_do_broadcast(tmpmask);
|
||||
```
|
||||
|
||||
The `tick_do_broadcast` calls the `broadcast` function of the given clock events which sends [IPI](https://en.wikipedia.org/wiki/Inter-processor_interrupt) interrupt to the set of the processors. In the end we can call the event handler of the given `tick_device`:
|
||||
|
||||
```C
|
||||
if (bc_local)
|
||||
td->evtdev->event_handler(td->evtdev);
|
||||
```
|
||||
|
||||
which actually represents interrupt handler of the local timer of a processor. After this a processor will wake up. That is all about `tick broadcast` framework in the Linux kernel. We have missed some aspects of this framework, for example reprogramming of a `clock event` device and broadcast with the oneshot timer and etc. But the Linux kernel is very big, it is not real to cover all aspects of it. I think it will be interesting to dive into with yourself.
|
||||
|
||||
If you remember, we have started this part with the call of the `tick_init` function. We just consider the `tick_broadcast_init` function and releated theory, but the `tick_init` function contains another call of a function and this function is - `tick_nohz_init`. Let's look on the implementation of this function.
|
||||
|
||||
Initialization of dyntick related data structures
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
We already saw some information about `dyntick` concept in this part and we know that this concept allows kernel to disable system timer interrupts in the `idle` state. The `tick_nohz_init` function makes initialization of the different data structures which are related to this concept. This function defined in the [kernel/time/tick-sched.c](https://github.com/torvalds/linux/blob/master/kernel/time/tich-sched.c) source code file and starts from the check of the value of the `tick_nohz_full_running` variable which represents state of the tick-less mode for the `idle` state and the state when system timer interrups are disabled durung a processor has only one runnable task:
|
||||
|
||||
```C
|
||||
if (!tick_nohz_full_running) {
|
||||
if (tick_nohz_init_all() < 0)
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
If this mode is not running we cann the `tick_nohz_init_all` function that defined in the same source code file and check its result. The `tick_nohz_init_all` function tries to allocate the `tick_nohz_full_mask` with the call of the `alloc_cpumask_var` that will allocate space for a `tick_nohz_full_mask`. The `tck_nohz_full_mask` will store numbers of processors that have enabled full `NO_HZ`. After successful allocation of the `tick_nohz_full_mask` we set all bits in the `tick_nogz_full_mask`, set the `tick_nohz_full_running` and return result to the `tick_nohz_init` function:
|
||||
|
||||
```C
|
||||
static int tick_nohz_init_all(void)
|
||||
{
|
||||
int err = -1;
|
||||
#ifdef CONFIG_NO_HZ_FULL_ALL
|
||||
if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
|
||||
WARN(1, "NO_HZ: Can't allocate full dynticks cpumask\n");
|
||||
return err;
|
||||
}
|
||||
err = 0;
|
||||
cpumask_setall(tick_nohz_full_mask);
|
||||
tick_nohz_full_running = true;
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
```
|
||||
|
||||
In the next step we try to allocate a memory space for the `housekeeping_mask`:
|
||||
|
||||
```C
|
||||
if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
|
||||
WARN(1, "NO_HZ: Can't allocate not-full dynticks cpumask\n");
|
||||
cpumask_clear(tick_nohz_full_mask);
|
||||
tick_nohz_full_running = false;
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
This `cpumask` will store number of processor for `housekeeping` or in other words we need at least in one processor that will not me in `NO_HZ` mode, because it will do timekeeping and etc. After this we check the result of the architecture-specific `arch_irq_work_has_interrupt` function. This function checks ability to send inter-processor interrupt for the certain architecture. We need to check this, because system timer of a processor will be disabled during `NO_HZ` mode, so there must be at least one online processor which can send inter-processor interrupt to awake offline processor. This function defined in the [arch/x86/include/asm/irq_work.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/irq_work.h) header file for the [x86_64](https://en.wikipedia.org/wiki/X86-64) and just checks that a processor has [APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller) from the [CPUID](https://en.wikipedia.org/wiki/CPUID):
|
||||
|
||||
```C
|
||||
static inline bool arch_irq_work_has_interrupt(void)
|
||||
{
|
||||
return cpu_has_apic;
|
||||
}
|
||||
```
|
||||
|
||||
If a processor has not `APIC`, the Linux kernel prints warning message, clears the `tick_nohz_full_mask` cpumask, copies numbers of all posible processors in the system to the `housekeeping_mask` and resets the value of the `tick_nogz_full_running` variable:
|
||||
|
||||
```C
|
||||
if (!arch_irq_work_has_interrupt()) {
|
||||
pr_warning("NO_HZ: Can't run full dynticks because arch doesn't "
|
||||
"support irq work self-IPIs\n");
|
||||
cpumask_clear(tick_nohz_full_mask);
|
||||
cpumask_copy(housekeeping_mask, cpu_possible_mask);
|
||||
tick_nohz_full_running = false;
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
After this step, we get the number of the current processor by the call of the `smp_processor_id` and check this processor in the `tick_nogh_full_mask`. If the `tick_nohz_full_mask` contains a given processor we clear appropriate bit in the `tick_nohz_full_mask`:
|
||||
|
||||
```C
|
||||
cpu = smp_processor_id();
|
||||
|
||||
if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
|
||||
pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
|
||||
cpumask_clear_cpu(cpu, tick_nohz_full_mask);
|
||||
}
|
||||
```
|
||||
|
||||
Because this processor will be used for timekeeping. After this step we put all numbers of processors that are in the `cpu_posssible_mask` and not in the `tick_nogz_full_mask`:
|
||||
|
||||
```C
|
||||
cpumask_andnot(housekeeping_mask,
|
||||
cpu_possible_mask, tick_nohz_full_mask);
|
||||
```
|
||||
|
||||
After this operation, the `housekeeping_mask` will contain all processors of the system except a processor for timekeeping. In the last step of the `tick_nohz_init_all` function, we are going through all processors that are defined in the `tick_nohz_full_mask` and call the following function for an each processor:
|
||||
|
||||
```C
|
||||
for_each_cpu(cpu, tick_nohz_full_mask)
|
||||
context_tracking_cpu_set(cpu);
|
||||
```
|
||||
|
||||
The `context_tracking_cpu_set` function defined in the [kernel/context_tracking.c](https://github.com/torvalds/linux/blob/master/kernel/context_tracking.c) source code file and main point of this function is to set the `context_tracking.active` [percpu](https://0xax.gitbooks.io/linux-insides/content/Concepts/per-cpu.html) variable to `true`. When the `active` field will be set to `true` for the certain processor, all [context switches](https://en.wikipedia.org/wiki/Context_switch) will be ignored by the Linux kernel context tracking subsystem for this processor.
|
||||
|
||||
That's all. This is the end of the `tick_nohz_init` function. After this `NO_HZ` related data structures will be initialzed. We didn't see API of the `NO_HZ` mode, but will see it soon.
|
||||
|
||||
Conclusion
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This is the end of the third part of the chapter that describes timers and timer management related stuff in the Linux kernel. In the previous part got acquainted with the `clocksource` concept in the Linux kernel which represents framework for managing different clock source in a interrupt and hardware characteristics independent way. We continued to look on the Linux kernel initialization process in a time management context in this part and got acquainted with two new concepts for us: the `tick broadcast` framework and `tick-less` mode. The first concept helps the Linux kernel to deal with processors which which are in deep sleep and the second concept represents the mode in which kernel may work to improve power management of `idle` processors.
|
||||
|
||||
In the next part we will continue to dive into timer management related things in the Linux kernel and will see new concept for us - `timers`.
|
||||
|
||||
If you have questions or suggestions, feel free to ping me in twitter [0xAX](https://twitter.com/0xAX), drop me [email](anotherworldofworld@gmail.com) or just create [issue](https://github.com/0xAX/linux-internals/issues/new).
|
||||
|
||||
**Please note that English is not my first language and I am really sorry for any inconvenience. If you found any mistakes please send me PR to [linux-insides](https://github.com/0xAX/linux-insides).**
|
||||
|
||||
Links
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
* [x86_64](https://en.wikipedia.org/wiki/X86-64)
|
||||
* [initrd](https://en.wikipedia.org/wiki/Initrd)
|
||||
* [interrupt](https://en.wikipedia.org/wiki/Interrupt)
|
||||
* [DMI](https://en.wikipedia.org/wiki/Desktop_Management_Interface)
|
||||
* [printk](https://en.wikipedia.org/wiki/Printk)
|
||||
* [CPU idle](https://en.wikipedia.org/wiki/Idle_%28CPU%29)
|
||||
* [power management](https://en.wikipedia.org/wiki/Power_management)
|
||||
* [NO_HZ documentation](https://github.com/torvalds/linux/blob/master/Documentation/timers/NO_HZ.txt)
|
||||
* [cpumasks](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html)
|
||||
* [high precision event timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)
|
||||
* [irq](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29)
|
||||
* [IPI](https://en.wikipedia.org/wiki/Inter-processor_interrupt)
|
||||
* [CPUID](https://en.wikipedia.org/wiki/CPUID)
|
||||
* [APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)
|
||||
* [percpu](https://0xax.gitbooks.io/linux-insides/content/Concepts/per-cpu.html)
|
||||
* [context switches](https://en.wikipedia.org/wiki/Context_switch)
|
||||
* [Previous part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-2.html)
|
@ -0,0 +1,427 @@
|
||||
Timers in the Linux kernel. Part 4.
|
||||
================================================================================
|
||||
|
||||
Timers
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This is fourth part of the [chapter](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html) which describes timers and time management related stuff in the Linux kernel and in the previous [part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-3.html) we knew about the `tick broadcast` framework and `NO_HZ` mode in the Linux kernel. We will continue to dive into the time managemented related stuff in the Linux kernel in this part and will and will 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.
|
||||
|
||||
The Linux kernel provides a `software timer` concept 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](https://github.com/torvalds/linux/blob/master/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](https://en.wikipedia.org/wiki/Internet_Protocol) addresses.
|
||||
|
||||
We can find the `list_set` structure that contains `gc` filed in this source code file:
|
||||
|
||||
```C
|
||||
struct list_set {
|
||||
...
|
||||
struct timer_list gc;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Not that the `gc` filed has `timer_list` type. This structure defined in the [include/linux/timer.h](https://github.com/torvalds/linux/blob/master/include/linux/timer.h) header file and main point of this structure is to store `dynamic` timers 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_list` structure contains actual `dynanic` timers. The `list_set` contains `gc` timer in our example represents timer for garbage collection. This timer will be initialized in the `list_set_gc_init` function:
|
||||
|
||||
```C
|
||||
static void
|
||||
list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
|
||||
{
|
||||
struct list_set *map = set->data;
|
||||
...
|
||||
...
|
||||
...
|
||||
map->gc.function = gc;
|
||||
map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
A function that is pointed by the `gc` pointer, will be called after timeout which is equal to the `map->gc.expires`.
|
||||
|
||||
Ok, we will not dive into this example with the [netfilter](https://en.wikipedia.org/wiki/Netfilter), because this chapter is not about [network](https://en.wikipedia.org/wiki/Computer_network) related stuff. But we saw that timers are widely used in the Linux kernel and learned that they represent concept which allows to functions to be called in future.
|
||||
|
||||
Now let's continue to research source code of Linux kernel which is related to the timers and time management stuff as we did it in all previous chapters.
|
||||
|
||||
Introduction to dynamic timers in the Linux kernel
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
As I already wrote, we knew about the `tick broadcast` framework and `NO_HZ` mode in the previous [part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-3.html). They will be initialized in the [init/main.c](https://github.com/torvalds/linux/blob/master/init/main.c) source code file by the call of the `tick_init` function. If we will look at this source code file, we will see that the next time management related function is:
|
||||
|
||||
```C
|
||||
init_timers();
|
||||
```
|
||||
|
||||
This function defined in the [kernel/time/timer.c](https://github.com/torvalds/linux/blob/master/kernel/time/timer.c) source code file and contains calls of four functions:
|
||||
|
||||
```C
|
||||
void __init init_timers(void)
|
||||
{
|
||||
init_timer_cpus();
|
||||
init_timer_stats();
|
||||
timer_register_cpu_notifier();
|
||||
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
|
||||
}
|
||||
```
|
||||
|
||||
Let's look on implementation of each function. The first function is `init_timer_cpus` defined in the [same](https://github.com/torvalds/linux/blob/master/kernel/time/timer.c) source code file and just calls the `init_timer_cpu` function for each possible processor in the system:
|
||||
|
||||
```C
|
||||
static void __init init_timer_cpus(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
init_timer_cpu(cpu);
|
||||
}
|
||||
```
|
||||
|
||||
If you do not know or do not remember what is it a `possible` cpu, you can read the special [part](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html) of this book which describes `cpumask` concept in the Linux kernel. In shot words, a `possible` processor is a processor which can be plugged in anytime during the life of the system.
|
||||
|
||||
The `init_timer_cpu` function does main work for us, namely it executes initialization of the `tvec_base` structure for each processor. This structure defined in the [kernel/time/timer.c](https://github.com/torvalds/linux/blob/master/kernel/time/timer.c) source code file and stores data related to a `dynamic` timer for a certain processor. Let's look on the definition of this structure:
|
||||
|
||||
```C
|
||||
struct tvec_base {
|
||||
spinlock_t lock;
|
||||
struct timer_list *running_timer;
|
||||
unsigned long timer_jiffies;
|
||||
unsigned long next_timer;
|
||||
unsigned long active_timers;
|
||||
unsigned long all_timers;
|
||||
int cpu;
|
||||
bool migration_enabled;
|
||||
bool nohz_active;
|
||||
struct tvec_root tv1;
|
||||
struct tvec tv2;
|
||||
struct tvec tv3;
|
||||
struct tvec tv4;
|
||||
struct tvec tv5;
|
||||
} ____cacheline_aligned;
|
||||
```
|
||||
|
||||
The `thec_base` structure contains following fields: The `lock` for `tvec_base` protection, the next `running_timer` field points to the currently running timer for the certain processor, the `timer_jiffies` fields represents the earilest expiration time (it will be used by the Linux kernel to find already expired timers). The next field - `next_timer` contains the next pending timer for a next timer [interrupt](https://en.wikipedia.org/wiki/Interrupt) in a case when a processor goes to sleep and the `NO_HZ` mode is enabled in the Linux kernel. The `active_timers` field provides accounting of non-deferrable timers timers or in other words all timers that will not be stopped during a processor will go to sleep. The `all_timers` field tracks total number of timers or `active_timers` + deferrable timers. The `cpu` field represents number of a processor which owns timers. The `migration_enabled` and `nohz_active` fields are represent opportunity of timers migration to another processor and status of the `NO_HZ` mode respectively.
|
||||
|
||||
The last five fields of the `tvec_base` structure represent lists of dynamic timers. The first `tv1` field has:
|
||||
|
||||
```C
|
||||
#define TVR_SIZE (1 << TVR_BITS)
|
||||
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
struct tvec_root {
|
||||
struct hlist_head vec[TVR_SIZE];
|
||||
};
|
||||
```
|
||||
|
||||
type. Note that the value of the `TVR_SIZE` depends on the `CONFIG_BASE_SMALL` kernel configuration option:
|
||||
|
||||
![base small](http://s17.postimg.org/db3towlu7/base_small.png)
|
||||
|
||||
that reduces size of the kernel data structures if disabled. The `v1` is array that may contain `64` or `256` elements where an each element represents a dynamic timer that will decay within the next `255` system timer interrupts. Next three fields: `tv2`, `tv3` and `tv4` are lists with dynamic timers too, but they store dynamic timers which will decay the next `2^14 - 1`, `2^20 - 1` and `2^26` respectively. The last `tv5` field represents list which stores dynamic timers with a large expiring period.
|
||||
|
||||
So, now we saw the `tvec_base` structure and description of its fields and we can look on the implementation of the `init_timer_cpu` function. As I already wrote, this function defined in the [kernel/time/timer.c](https://github.com/torvalds/linux/blob/master/kernel/time/timer.c) source code file and executes initialization of the `tvec_bases`:
|
||||
|
||||
```C
|
||||
static void __init init_timer_cpu(int cpu)
|
||||
{
|
||||
struct tvec_base *base = per_cpu_ptr(&tvec_bases, cpu);
|
||||
|
||||
base->cpu = cpu;
|
||||
spin_lock_init(&base->lock);
|
||||
|
||||
base->timer_jiffies = jiffies;
|
||||
base->next_timer = base->timer_jiffies;
|
||||
}
|
||||
```
|
||||
|
||||
The `tvec_bases` represents [per-cpu](https://0xax.gitbooks.io/linux-insides/content/Concepts/per-cpu.html) variable which represents main data structure for a dynamic timer for a given processor. This `per-cpu` variable defined in the same source code file:
|
||||
|
||||
```C
|
||||
static DEFINE_PER_CPU(struct tvec_base, tvec_bases);
|
||||
```
|
||||
|
||||
First of all we're getting the address of the `tvec_bases` for the given processor to `base` variable and as we got it, we are starting to initialize some of the `tvec_base` fields in the `init_timer_cpu` function. After initialization of the `per-cpu` dynamic timers with the [jiffies](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-1.html) and the number of a possible processor, we need to initialize a `tstats_lookup_lock` [spinlock](https://en.wikipedia.org/wiki/Spinlock) in the `init_timer_stats` function:
|
||||
|
||||
```C
|
||||
void __init init_timer_stats(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
raw_spin_lock_init(&per_cpu(tstats_lookup_lock, cpu));
|
||||
}
|
||||
```
|
||||
|
||||
The `tstats_lookcup_lock` variable represents `per-cpu` raw spinlock:
|
||||
|
||||
```C
|
||||
static DEFINE_PER_CPU(raw_spinlock_t, tstats_lookup_lock);
|
||||
```
|
||||
|
||||
which will be used for protection of operation with statistics of timers that can be accessed through the [procfs](https://en.wikipedia.org/wiki/Procfs):
|
||||
|
||||
```C
|
||||
static int __init init_tstats_procfs(void)
|
||||
{
|
||||
struct proc_dir_entry *pe;
|
||||
|
||||
pe = proc_create("timer_stats", 0644, NULL, &tstats_fops);
|
||||
if (!pe)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ cat /proc/timer_stats
|
||||
Timerstats sample period: 3.888770 s
|
||||
12, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
|
||||
15, 1 swapper hcd_submit_urb (rh_timer_func)
|
||||
4, 959 kedac schedule_timeout (process_timeout)
|
||||
1, 0 swapper page_writeback_init (wb_timer_fn)
|
||||
28, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
|
||||
22, 2948 IRQ 4 tty_flip_buffer_push (delayed_work_timer_fn)
|
||||
...
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
The next step after initialization of the `tstats_lookup_lock` spinlock is the call of the `timer_register_cpu_notifier` function. This function depends on the `CONFIG_HOTPLUG_CPU` kernel configuration option which enables support for [hotplug](https://en.wikipedia.org/wiki/Hot_swapping) processors in the Linux kernel.
|
||||
|
||||
When a processor will be logically offlined, a notification will be sent to the Linux kernel with the `CPU_DEAD` or the `CPU_DEAD_FROZEN` event by the call of the `cpu_notifier` macro:
|
||||
|
||||
```C
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
...
|
||||
...
|
||||
static inline void timer_register_cpu_notifier(void)
|
||||
{
|
||||
cpu_notifier(timer_cpu_notify, 0);
|
||||
}
|
||||
...
|
||||
...
|
||||
#else
|
||||
...
|
||||
...
|
||||
static inline void timer_register_cpu_notifier(void) { }
|
||||
...
|
||||
...
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
```
|
||||
|
||||
In this case the `timer_cpu_notify` will be called which checks an event type and will call the `migrate_timers` function:
|
||||
|
||||
```C
|
||||
static int timer_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
switch (action) {
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
migrate_timers((long)hcpu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
```
|
||||
|
||||
This chapter will not describe `hotplug` related events in the Linux kernel source code, but if you are interesting in such things, you can find implementation iof the `migrate_timers` function in the [kernel/time/timer.c](https://github.com/torvalds/linux/blob/master/kernel/time/timer.c) source code file.
|
||||
|
||||
The last step in the is the `init_timers` function is the call of the:
|
||||
|
||||
```C
|
||||
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
|
||||
```
|
||||
|
||||
function. The `open_softirq` function may be already familar to you if you have read the ninth [part](https://0xax.gitbooks.io/linux-insides/content/interrupts/interrupts-9.html) about the interrupts and interrupt handling in the Linux kernel. In short words, the `open_softirq` function defined in the [kernel/softirq.c](https://github.com/torvalds/linux/blob/master/kernel/softirq.c) source code file and executes initialization of the deferred interrupt handler.
|
||||
|
||||
In our case the deferred function is the `run_timer_softirq` function that is will be called after a hardware interrupt in the `do_IRQ` function which defined in the [arch/x86/kernel/irq.c](https://github.com/torvalds/linux/blob/master/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.
|
||||
|
||||
Let's look on the implementation of the `run_timer_softirq` function:
|
||||
|
||||
```C
|
||||
static void run_timer_softirq(struct softirq_action *h)
|
||||
{
|
||||
struct tvec_base *base = this_cpu_ptr(&tvec_bases);
|
||||
|
||||
if (time_after_eq(jiffies, base->timer_jiffies))
|
||||
__run_timers(base);
|
||||
}
|
||||
```
|
||||
|
||||
At the beginning of the `run_timer_softirq` function we get a `dynamic` timer for a current processor and compares the current value of the [jiffies](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-1.html) with the value of the `timer_jiffies` for the current structure by the call of the `time_after_eq` macro which is defined in the [include/linux/jiffies.h](https://github.com/torvalds/linux/blob/master/include/linux/jiffies.h) header file:
|
||||
|
||||
```C
|
||||
#define time_after_eq(a,b) \
|
||||
(typecheck(unsigned long, a) && \
|
||||
typecheck(unsigned long, b) && \
|
||||
((long)((a) - (b)) >= 0))
|
||||
```
|
||||
|
||||
Reclaim that the `timer_jiffies` field of the `tvec_base` structure 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 `jiffies` is greater than `base->timer_jiffies`, we call the `__run_timers` function that defined in the same source code file. Let's look on the implementation of this function.
|
||||
|
||||
As I just wrote, the `__run_timers` function runs all expired timers for a given processor. This function starts from the acquireing of the `tvec_base's` lock to protect the `tvec_base` structure
|
||||
|
||||
```C
|
||||
static inline void __run_timers(struct tvec_base *base)
|
||||
{
|
||||
struct timer_list *timer;
|
||||
|
||||
spin_lock_irq(&base->lock);
|
||||
...
|
||||
...
|
||||
...
|
||||
spin_unlock_irq(&base->lock);
|
||||
}
|
||||
```
|
||||
|
||||
After this it starts the loop while the `timer_jiffies` will not be greater than the `jiffies`:
|
||||
|
||||
```C
|
||||
while (time_after_eq(jiffies, base->timer_jiffies)) {
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
We can find many different manipulations in the our loop, but the main point is to find expired timers and call delayed functions. First of all we need to calculate the `index` of the `base->tv1` list that stores the next timer to be handled with the following expression:
|
||||
|
||||
```C
|
||||
index = base->timer_jiffies & TVR_MASK;
|
||||
```
|
||||
|
||||
where the `TVR_MASK` is a mask for the getting of the `tvec_root->vec` elements. 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 `tv2`, `tv3` and etc., and rehashing it with the call of the `cascade` function:
|
||||
|
||||
```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));
|
||||
```
|
||||
|
||||
After this we increase the value of the `base->timer_jiffies`:
|
||||
|
||||
```C
|
||||
++base->timer_jiffies;
|
||||
```
|
||||
|
||||
In the last step we are executing a corresponding function for each timer from the list in a following loop:
|
||||
|
||||
```C
|
||||
hlist_move_list(base->tv1.vec + index, head);
|
||||
|
||||
while (!hlist_empty(head)) {
|
||||
...
|
||||
...
|
||||
...
|
||||
timer = hlist_entry(head->first, struct timer_list, entry);
|
||||
fn = timer->function;
|
||||
data = timer->data;
|
||||
|
||||
spin_unlock(&base->lock);
|
||||
call_timer_fn(timer, fn, data);
|
||||
spin_lock(&base->lock);
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where the `call_timer_fn` just call the given function:
|
||||
|
||||
```C
|
||||
static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
|
||||
unsigned long data)
|
||||
{
|
||||
...
|
||||
...
|
||||
...
|
||||
fn(data);
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
That's all. The Linux kernel has infrastructure for `dynamic timers` from this moment. We will not dive into this interesting theme. As I already wrote the `timers` is a [widely](http://lxr.free-electrons.com/ident?i=timer_list) 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.
|
||||
|
||||
Now let's look usage of `dynamic timers` in the Linux kernel.
|
||||
|
||||
Usage of dynamic timers
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
As you already can noted, if the Linux kernel provides a concept, it also provides API for managing of this concept and the `dynamic timers` concept is not exception here. To use a timer in the Linux kernel code, we must define a variable with a `timer_list` type. We can initialize our `timer_list` structure in two ways. The first is to use the `init_timer` macro that defined in the [include/linux/timer.h](https://github.com/torvalds/linux/blob/master/include/linux/timer.h) header file:
|
||||
|
||||
```C
|
||||
#define init_timer(timer) \
|
||||
__init_timer((timer), 0)
|
||||
|
||||
#define __init_timer(_timer, _flags) \
|
||||
init_timer_key((_timer), (_flags), NULL, NULL)
|
||||
```
|
||||
|
||||
where the `init_timer_key` function just calls the:
|
||||
|
||||
```C
|
||||
do_init_timer(timer, flags, name, key);
|
||||
```
|
||||
|
||||
function which fields the given `timer` with default values. The second way is to use the:
|
||||
|
||||
```C
|
||||
#define TIMER_INITIALIZER(_function, _expires, _data) \
|
||||
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
|
||||
```
|
||||
|
||||
macro which will initilize the given `timer_list` structure too.
|
||||
|
||||
After a `dynamic timer` is initialzed we can start this `timer` with the call of the:
|
||||
|
||||
```C
|
||||
void add_timer(struct timer_list * timer);
|
||||
```
|
||||
|
||||
function and stop it with the:
|
||||
|
||||
```C
|
||||
int del_timer(struct timer_list * timer);
|
||||
```
|
||||
|
||||
function.
|
||||
|
||||
That's all.
|
||||
|
||||
Conclusion
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This is the end of the fourth part of the chapter that describes timers and timer management related stuff in the Linux kernel. In the previous part we got acquainted with the two new concepts: the `tick broadcast` framework and the `NO_HZ` mode. In this part we continued to dive into time managemented related stuff and got acquainted with the new concept - `dynamic timer` or software timer. We didn't saw implementation of a `dynamic timers` management code in details in this part but saw data structures and API around this concept.
|
||||
|
||||
In the next part we will continue to dive into timer management related things in the Linux kernel and will see new concept for us - `timers`.
|
||||
|
||||
If you have questions or suggestions, feel free to ping me in twitter [0xAX](https://twitter.com/0xAX), drop me [email](anotherworldofworld@gmail.com) or just create [issue](https://github.com/0xAX/linux-internals/issues/new).
|
||||
|
||||
**Please note that English is not my first language and I am really sorry for any inconvenience. If you found any mistakes please send me PR to [linux-insides](https://github.com/0xAX/linux-insides).**
|
||||
|
||||
Links
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
* [IP](https://en.wikipedia.org/wiki/Internet_Protocol)
|
||||
* [netfilter](https://en.wikipedia.org/wiki/Netfilter)
|
||||
* [network](https://en.wikipedia.org/wiki/Computer_network)
|
||||
* [cpumask](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html)
|
||||
* [interrupt](https://en.wikipedia.org/wiki/Interrupt)
|
||||
* [jiffies](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-1.html)
|
||||
* [per-cpu](https://0xax.gitbooks.io/linux-insides/content/Concepts/per-cpu.html)
|
||||
* [spinlock](https://en.wikipedia.org/wiki/Spinlock)
|
||||
* [procfs](https://en.wikipedia.org/wiki/Procfs)
|
||||
* [previous part](https://0xax.gitbooks.io/linux-insides/content/Timers/timers-3.html)
|
Loading…
Reference in new issue