вторник, 7 декабря 2010 г.

7.7. Quick Reference

7.7. Quick Reference

This chapter introduced the following symbols.

7.7.1. Timekeeping

#include
HZ

The HZ symbol specifies the number of clock ticks generated per second.

#include
volatile unsigned long jiffies
u64 jiffies_64

The jiffies_64 variable is incremented once for each clock tick; thus, it's incremented HZ times per second. Kernel code most often refers to jiffies, which is the same as jiffies_64 on 64-bit platforms and the least significant half of it on 32-bit platforms.

int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_before_eq(unsigned long a, unsigned long b);

These Boolean expressions compare jiffies in a safe way, without problems in case of counter overflow and without the need to access jiffies_64.

u64 get_jiffies_64(void);

Retrieves jiffies_64 without race conditions.

#include
unsigned long timespec_to_jiffies(struct timespec *value);
void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
unsigned long timeval_to_jiffies(struct timeval *value);
void jiffies_to_timeval(unsigned long jiffies, struct timeval *value);

Converts time representations between jiffies and other representations.

#include
rdtsc(low32,high32);
rdtscl(low32);
rdtscll(var32);

x86-specific macros to read the timestamp counter. They read it as two 32-bit halves, read only the lower half, or read all of it into a long long variable.

#include
cycles_t get_cycles(void);

Returns the timestamp counter in a platform-independent way. If the CPU offers no timestamp feature, 0 is returned.

#include
unsigned long mktime(year, mon, day, h, m, s);

Returns the number of seconds since the Epoch, based on the six unsigned int arguments.

void do_gettimeofday(struct timeval *tv);

Returns the current time, as seconds and microseconds since the Epoch, with the best resolution the hardware can offer. On most platforms the resolution is one microsecond or better, although some platforms offer only jiffies resolution.
struct timespec current_kernel_time(void);

Returns the current time with the resolution of one jiffy.

7.7.2. Delays

#include
long wait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed
long timeout);

Puts the current process to sleep on the wait queue, installing a timeout value expressed in jiffies. Use schedule_timeout (below) for noninterruptible sleeps.

#include
signed long schedule_timeout(signed long timeout);

Calls the scheduler after ensuring that the current process is awakened at timeout expiration. The caller must invoke set_current_state first to put itself in an interruptible or noninterruptible sleep state.

#include
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);

Introduces delays of an integer number of nanoseconds, microseconds, and milliseconds. The delay achieved is at least the requested value, but it can be more. The argument to each function must not exceed a platform-specific limit (usually a few thousands).

void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
Puts the process to sleep for the given number of milliseconds (or seconds, in the case of ssleep).

7.7.3. Kernel Timers

#include
int in_interrupt(void);
int in_atomic(void);

Returns a Boolean value telling whether the calling code is executing in interrupt context or atomic context. Interrupt context is outside of a process context, either during hardware or software interrupt processing. Atomic context is when you can't schedule either an interrupt context or a process's context with a spinlock held.

#include
void init_timer(struct timer_list * timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

This function and the static declaration of the timer structure are the two ways to initialize a timer_list data structure.

void add_timer(struct timer_list * timer);

Registers the timer structure to run on the current CPU.

int mod_timer(struct timer_list *timer, unsigned long expires);

Changes the expiration time of an already scheduled timer structure. It can also act as an alternative to add_timer.

int timer_pending(struct timer_list * timer);

Macro that returns a Boolean value stating whether the timer structure is already registered to run.

void del_timer(struct timer_list * timer);
void del_timer_sync(struct timer_list * timer);

Removes a timer from the list of active timers. The latter function ensures that the timer is not currently running on another CPU.

7.7.4. Tasklets

#include
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long),
unsigned long data);

The first two macros declare a tasklet structure, while the tasklet_init function initializes a tasklet structure that has been obtained by allocation or other means.

The second DECLARE macro marks the tasklet as disabled.

void tasklet_disable(struct tasklet_struct *t);
void tasklet_disable_nosync(struct tasklet_struct *t);
void tasklet_enable(struct tasklet_struct *t);

Disables and reenables a tasklet. Each disable must be matched with an enable (you can disable the tasklet even if it's already disabled). The function tasklet_disable waits for the tasklet to terminate if it is running on another CPU. The nosync version doesn't take this extra step.

void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);

Schedules a tasklet to run, either as a "normal" tasklet or a high-priority one.

When soft interrupts are executed, high-priority tasklets are dealt with first, while normal tasklets run last.

void tasklet_kill(struct tasklet_struct *t);

Removes the tasklet from the list of active ones, if it's scheduled to run. Like tasklet_disable, the function may block on SMP systems waiting for the tasklet to terminate if it's currently running on another CPU.

7.7.5. Workqueues

#include
struct workqueue_struct;
struct work_struct;

The structures representing a workqueue and a work entry, respectively.

struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
void destroy_workqueue(struct workqueue_struct *queue);

Functions for creating and destroying workqueues. A call to create_workqueue creates a queue with a worker thread on each processor in the system; instead, create_singlethread_workqueue creates a workqueue with a single worker process.

DECLARE_WORK(name, void (*function)(void *), void *data);
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);

Macros that declare and initialize workqueue entries.

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct
*work, unsigned long delay);

Functions that queue work for execution from a workqueue.

int cancel_delayed_work(struct work_struct *work);
void flush_workqueue(struct workqueue_struct *queue);

Use cancel_delayed_work to remove an entry from a workqueue; flush_workqueue ensures that no workqueue entries are running anywhere in the system.

int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void);

Functions for working with the shared workqueue.

7.6. Очереди задач

7.6. Очереди задач


Очереди задач (workqueue) поверхностно похожи на тасклеты; они позволяют коду ядра запросить, какая функция будет вызвана в будущем. Есть, однако, некоторые существенные различия между ними, в том числе:

Тасклеты работают в контексте программного прерывания, в результате чего весь код тасклета должен быть атомарным. Вместо этого, функции очереди задач выполняются в контексте специального процесса ядра; в результате чего они имеют больше гибкости. В частности, функции очереди задач могут спать.
Тасклеты всегда работают на процессоре, который их изначально запланировал. Очереди задач по умолчанию работают таким же образом.
Код ядра может дать запрос, чтобы отложить выполнение функций очереди задач на заданный интервал.

Ключевым различием между ними двумя является то, что тасклеты выполняются быстро, в течение короткого периода времени и в атомарном режиме, а функции очереди задач могут иметь более высокие задержки, но не должны быть атомарными. Каждый механизм имеет ситуации, когда он уместен.

Очереди задач имеют тип struct workqueue_struct, которая определена в . Очередь задач должна быть явно создана перед использованием, используя одну из двух следующих функций:

struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);

Каждая очередь задач имеет один или более специализированных процессов ("потоки ядра"), которые запускают функции, помещённые в очередь. Если вы используете create_workqueue, вы получите очередь задач, которая имеет специальный поток для каждого процессора в системе. Во многих случаях все эти потоки являются просто излишними; если одного рабочего потока будет достаточно, вместо этого создайте очередь задач с помощью create_singlethread_workqueue.

Чтобы поместить задачу в очередь задач, необходимо заполнить структуру work_struct. Этот можно сделать во время компиляции следующим образом:

DECLARE_WORK(name, void (*function)(void *), void *data);

Где name является именем структуры, которая должна быть объявлена, function является функцией, которая будет вызываться из очереди задач, и data является значением для передачи в эту функцию. Если необходимо создать структуру work_struct во время выполнения, используйте следующие два макроса:

INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);

INIT_WORK делает более серьёзную работу по инициализации структуры; вы должны использовать его в первый раз при создании структуры. PREPARE_WORK делает почти такую же работу, но он не инициализирует указатели, используемые для подключения в очередь задач структуры work_struct. Если есть любая возможность, что в настоящее время структура может быть помещена в очередь задач и вы должны изменить эту структуру, используйте PREPARE_WORK вместо INIT_WORK.

Для помещения работы в очередь задач cуществуют две функции:

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);

Любая из них добавляет work к данной очереди. Однако, если используется queue_delayed_work, фактическая работа не выполняется, пока по крайней мере не пройдёт delay тиков. Возвращаемым значением этих функций является 0, если work была успешно добавлена в очередь; ненулевой результат означает, что эта структура work_struct уже ожидает в очереди и не была добавлена во второй раз.

В некоторое время в будущем функция work будет вызвана с заданным значением data. Эта функция будет работать в контексте рабочего потока, поэтому он может заснуть в случае необходимости, хотя вы должны знать, как этот сон может повлиять на любые другие задачи, находящиеся в той же очереди задач. Однако, такая функция не может получить доступ в пользовательское пространство. Так как она работает внутри в потоке ядра, просто нет доступа в пользовательское пространства.

Если вам необходимо отменить ожидающую запись в очереди задач, можно вызвать:

int cancel_delayed_work(struct work_struct *work);

Возвращаемое значение отлично от нуля, если запись была отменена ещё до начала исполнения. Ядро гарантирует, что выполнение данной записи не будет начато после вызова cancel_delayed_work. Однако, если cancel_delayed_work возвращает 0, запись уже может работать на другом процессоре и может всё ещё быть запущена после вызова cancel_delayed_work. Чтобы иметь абсолютную уверенность, что функция work не работает нигде в системе после того, как cancel_delayed_work вернула 0, вы должны затем сделать вызов:

void flush_workqueue(struct workqueue_struct *queue);

После возвращения flush_workqueue, никакая из функций, помещённых перед этим для вызова в очередь, больше нигде в системе не работает.

Когда вы закончите с очередью задач, можно избавиться от неё:

void destroy_workqueue(struct workqueue_struct *queue);

7.6.1. Общая очередь

Драйвер устройства во многих случаях не нуждается в своей собственной очереди задач. Если вы только изредка помещаете задачи в очередь, может оказаться более эффективным просто использовать общую очередь задач по умолчанию, предоставляемую ядром. Однако, при использовании этой очереди, вы должны знать, что будете делить её с другими. Среди прочего, это означает, что вы не должны монополизировать очередь на длительные периоды времени (не использовать длительные засыпания) и вашим задачам может потребоваться больше времени для получения ими процессора.

Модуль jiq ("только в очередь") экспортирует два файла, которые демонстрируют использование общей очереди задач. Они используют одну структуру work_struct, которая создана таким образом:

static struct work_struct jiq_work;

    /* это строка в jiq_init( ) */
    INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);

Когда процесс читает /proc/jiqwq, модуль начинает серию перемещений по общей очереди задач без каких-либо задержек. Функция использует это:

int schedule_work(struct work_struct *work);

Обратите внимание, что при работе с общей очередью используется другая функция; в качестве аргумента требуется только структура work_struct. Фактический код в jiq выглядит следующим образом:

prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
schedule_work(&jiq_work);
schedule( );
finish_wait(&jiq_wait, &wait);

Реальная рабочая функция выводит строку так же, как делает модуль jit, затем, в случае необходимости, повторно помещает  структуру work_struct в очередь задач. Вот jiq_print_wq полностью:

static void jiq_print_wq(void *ptr)
{
    struct clientdata *data = (struct clientdata *) ptr;

    if (! jiq_print (ptr))
        return;

    if (data->delay)
        schedule_delayed_work(&jiq_work, data->delay);
    else
        schedule_work(&jiq_work);
}

Если пользователь читает устройство с задержкой (/proc/jiqwqdelay), рабочая функция повторно помещает себя в режиме задержки с помощью schedule_delayed_work:

int schedule_delayed_work(struct work_struct *work, unsigned long delay);

Если вы посмотрите на вывод из этих двух устройств, он выглядит примерно так:

cat /proc/jiqwq
    time   delta preempt  pid cpu command
  1113043      0       0    7   1 events/1
  1113043      0       0    7   1 events/1
  1113043      0       0    7   1 events/1
  1113043      0       0    7   1 events/1
  1113043      0       0    7   1 events/1
cat /proc/jiqwqdelay
    time   delta preempt  pid cpu command
  1122066      1       0    6   0 events/0
  1122067      1       0    6   0 events/0
  1122068      1       0    6   0 events/0
  1122069      1       0    6   0 events/0
  1122070      1       0    6   0 events/0

Когда /proc/jiqwq читается, между печатью каждой строки нет очевидной задержки. Когда вместо этого читается /proc/jiqwqdelay, есть задержка ровно на один тик между каждой строкой. В любом случае мы видим одинаковое печатаемое имя процесса; это имя потока ядра, который выполняет общую очередь задач. Номер процессора напечатан после косой черты; никогда не известно, какой процессор будет работать при чтении /proc файла, но рабочая функция в последующий период всегда будет работать на том же процессоре.

Если необходимо отменить работающую запись, помещённую в общую очередь, можно использовать cancel_delayed_work, как описано выше. Однако, очистка (flushing) общей очереди задач требует отдельной функции:

void flush_scheduled_work(void);

Поскольку не известно, кто ещё может использовать эту очередь, никогда не известно, сколько времени потребуется для возвращения flush_scheduled_work.

7.6. Workqueues

7.6. Workqueues

Workqueues are, superficially, similar to tasklets; they allow kernel code to request that a function be called at some future time. There are, however, some significant differences between the two, including:

Tasklets run in software interrupt context with the result that all tasklet code must be atomic. Instead, workqueue functions run in the context of a special kernel process; as a result, they have more flexibility. In particular, workqueue functions can sleep.

Tasklets always run on the processor from which they were originally submitted. Workqueues work in the same way, by default.

Kernel code can request that the execution of workqueue functions be delayed for an explicit interval.

The key difference between the two is that tasklets execute quickly, for a short period of time, and in atomic mode, while workqueue functions may have higher latency but need not be atomic. Each mechanism has situations where it is appropriate.

Workqueues have a type of struct workqueue_struct, which is defined in . A workqueue must be explicitly created before use, using one of the following two functions:

struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);

Each workqueue has one or more dedicated processes ("kernel threads"), which run functions submitted to the queue. If you use create_workqueue, you get a workqueue that has a dedicated thread for each processor on the system. In many cases, all those threads are simply overkill; if a single worker thread will suffice, create the workqueue with create_singlethread_workqueue instead.
To submit a task to a workqueue, you need to fill in a work_struct structure. This can be done at compile time as follows:

DECLARE_WORK(name, void (*function)(void *), void *data);

Where name is the name of the structure to be declared, function is the function that is to be called from the workqueue, and data is a value to pass to that function. If you need to set up the work_struct structure at runtime, use the following two macros:

INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);

INIT_WORK does a more thorough job of initializing the structure; you should use it the first time that structure is set up. PREPARE_WORK does almost the same job, but it does not initialize the pointers used to link the work_struct structure into the workqueue. If there is any possibility that the structure may currently be submitted to a workqueue, and you need to change that structure, use PREPARE_WORK rather than INIT_WORK.

There are two functions for submitting work to a workqueue:

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, 
                       struct work_struct *work, unsigned long delay);

Either one adds work to the given queue. If queue_delayed_work is used, however, the actual work is not performed until at least delay jiffies have passed.

The return value from these functions is 0 if the work was successfully added to the queue; a nonzero result means that this work_struct structure was already waiting in the queue, and was not added a second time.

At some time in the future, the work function will be called with the given data value. The function will be running in the context of the worker thread, so it can sleep if need be—although you should be aware of how that sleep might affect any other tasks submitted to the same workqueue. What the function cannot do, however, is access user space. Since it is running inside a kernel thread, there simply is no user space to access.

Should you need to cancel a pending workqueue entry, you may call:

int cancel_delayed_work(struct work_struct *work);

The return value is nonzero if the entry was canceled before it began execution.

The kernel guarantees that execution of the given entry will not be initiated after a call to cancel_delayed_work. If cancel_delayed_work returns 0, however, the entry may have already been running on a different processor, and might still be running after a call to cancel_delayed_work. To be absolutely sure that the work function is not running anywhere in the system after cancel_delayed_work returns 0, you must follow that call with a call to:

void flush_workqueue(struct workqueue_struct *queue);

After flush_workqueue returns, no work function submitted prior to the call is running anywhere in the system.

When you are done with a workqueue, you can get rid of it with:

void destroy_workqueue(struct workqueue_struct *queue);


7.6.1. The Shared Queue

A device driver, in many cases, does not need its own workqueue. If you only submit tasks to the queue occasionally, it may be more efficient to simply use the shared, default workqueue that is provided by the kernel. If you use this queue, however, you must be aware that you will be sharing it with others. Among other things, that means that you should not monopolize the queue for long periods of time (no long sleeps), and it may take longer for your tasks to get their turn in the processor.

The jiq ("just in queue") module exports two files that demonstrate the use of the shared workqueue. They use a single work_struct structure, which is set up this way:

static struct work_struct jiq_work;
    /* this line is in jiq_init(  ) */
    INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);

When a process reads /proc/jiqwq, the module initiates a series of trips through the shared workqueue with no delay. The function it uses is:

int schedule_work(struct work_struct *work);

Note that a different function is used when working with the shared queue; it requires only the work_struct structure for an argument. The actual code in jiq looks like this:

prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
schedule_work(&jiq_work);
schedule(  );
finish_wait(&jiq_wait, &wait);

The actual work function prints out a line just like the jit module does, then, if need be, resubmits the work_struct structure into the workqueue. Here is jiq_print_wq in its entirety:

static void jiq_print_wq(void *ptr)
{
    struct clientdata *data = (struct clientdata *) ptr;
    
    if (! jiq_print (ptr))
        return;
    
    if (data->delay)
        schedule_delayed_work(&jiq_work, data->delay);
    else
        schedule_work(&jiq_work);
}

If the user is reading the delayed device (/proc/jiqwqdelay), the work function resubmits itself in the delayed mode with schedule_delayed_work:

int schedule_delayed_work(struct work_struct *work, unsigned long delay);

If you look at the output from these two devices, it looks something like:
% cat /proc/jiqwq
    time  delta preempt   pid cpu command
  1113043     0       0     7   1 events/1
  1113043     0       0     7   1 events/1
  1113043     0       0     7   1 events/1
  1113043     0       0     7   1 events/1
  1113043     0       0     7   1 events/1
% cat /proc/jiqwqdelay
    time  delta preempt   pid cpu command
  1122066     1       0     6   0 events/0
  1122067     1       0     6   0 events/0
  1122068     1       0     6   0 events/0
  1122069     1       0     6   0 events/0
  1122070     1       0     6   0 events/0

When /proc/jiqwq is read, there is no obvious delay between the printing of each line. When, instead, /proc/jiqwqdelay is read, there is a delay of exactly one jiffy between each line. In either case, we see the same process name printed; it is the name of the kernel thread that implements the shared workqueue. The CPU number is printed after the slash; we never know which CPU will be running when the /proc file is read, but the work function will always run on the same processor thereafter.

If you need to cancel a work entry submitted to the shared queue, you may use cancel_delayed_work, as described above. Flushing the shared workqueue requires a separate function, however:

void flush_scheduled_work(void);

Since you do not know who else might be using this queue, you never really know how long it might take for flush_scheduled_work to return.

7.5. Тасклеты

7.5. Тасклеты


Другим объектом ядра, связанным с вопросом времени является механизм тасклета (tasklet). Он в основном используется в управлении прерываниями (мы увидим его снова в Главе 10).

Тасклеты в некотором смысле напоминают таймеры ядра. Они всегда выполняются во время прерывания, они всегда работают на том же процессоре, который их запланировал и они получают unsigned long аргумент. Однако, в отличие от таймеров ядра, невозможно запросить выполнения функции в заданное время. Запланировав тасклет вы просто попросите, чтобы он выполнился позже во время, выбранное ядром. Такое поведение особенно полезно для обработчиков прерываний, когда аппаратное прерывание должно обслуживаться как можно быстрее, но большинство управлений данными можно смело отложить до более позднее время. На самом деле тасклет, как и таймер ядра выполняется (в атомарном режиме) в контексте "программного прерывания", механизма ядра, который выполняет асинхронные задачи при разрешённых аппаратных прерываниях.

Тасклет существует как структура данных, которая должна быть проинициализирована перед использованием. Инициализация может быть выполнена путём вызова специальной функции или объявлением структуры с помощью определённых макросов:

#include 

struct tasklet_struct {
    /* ... */
    void (*func)(unsigned long);
    unsigned long data;
};

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);

Тасклеты предлагают ряд интересных особенностей:

Тасклет можно отключить и повторно включить позже; он не будет выполнен, пока он не включён столько раз, сколько был отключён.
Так же как таймеры, тасклет может перерегистрировать себя.
Тасклет может быть запланирован для выполнения в нормальном или высоком приоритете. Последняя группа всегда выполняется первой.
Тасклеты могут быть запущены сразу же, если система не находится под большой нагрузкой, но не позднее, чем при следующем тике таймера.
Тасклеты могут конкурировать с другими тасклетами, но строго последовательны в отношении самих себя - тот же тасклет никогда не работает одновременно более чем на одном процессоре. Кроме того, как уже отмечалось, тасклет всегда работает на том же процессоре, который его запланировал.

Модуль jit включает два файла, /proc/jitasklet и /proc/jitasklethi, которые возвращают те же данные, что и /proc/jitimer, описанный в разделе "Таймеры ядра". Когда вы читаете один из этих файлов, вы получаете обратно заголовок и шесть строк данных. Первая строка данных описывает контекст вызывающего процесса, а другие строки описывают контекст последовательных запусков процедуры тасклета. Это пример работы во время компиляции ядра:

phon% cat /proc/jitasklet
   time   delta  inirq    pid   cpu command
 6076139     0     0      4370   0   cat
 6076140     1     1      4368   0   cc1
 6076141     1     1      4368   0   cc1
 6076141     0     1         2   0   ksoftirqd/0
 6076141     0     1         2   0   ksoftirqd/0
 6076141     0     1         2   0   ksoftirqd/0

Как подтверждается приведенными выше данными, тасклет выполняется при следующем тике таймера до тех пор, пока процессор занят выполнением процесса, но он запускается сразу, когда процессор простаивает. Ядро предоставляет набор потоков ядра ksoftirqd, один на процессор, чтобы просто запускать обработчики "программных прерываний", такие, как функция tasklet_action. Таким образом, последние три запуска тасклета проходили в контексте потока ядра ksoftirqd, связанного с CPU 0. Реализация jitasklethi использует высокоприоритетный тасклет, объясняемый в последующем списке функций.

Фактический код в jit, который реализует /proc/jitasklet и /proc/jitasklethi почти идентичен коду, который реализует /proc/jitimer, но он использует вызовы тасклета вместо таймера. Ниже приводится список деталей интерфейса ядра для тасклетов после того, как структура тасклета была проинициализирована:

void tasklet_disable(struct tasklet_struct *t);
  • Эта функция отключает данный тасклет. Тасклет по-прежнему может быть запланирован с помощью tasklet_schedule, но его выполнение отложено, пока тасклет снова не будет включен. Если тасклет в настоящее время работает, эта функция активно ждёт завершения тасклета; таким образом, после вызова tasklet_disable, вы можете быть уверены, что тасклет не работает нигде в системе.

void tasklet_disable_nosync(struct tasklet_struct *t);
  • Отключает тасклет, но не дожидается завершения никакой запущенной функции. Когда возвращается, тасклет является отключенным и не будет планироваться в будущем, пока снова не будет включён, но всё ещё может работать на другом процессоре, когда функция возвращается.

void tasklet_enable(struct tasklet_struct *t);
  • Включает тасклет, который был ранее отключён. Если тасклет уже запланирован, он будет запущен в ближайшее время. Вызов tasklet_enable должен соответствовать каждому вызову tasklet_disable, так как ядро отслеживает "счётчик отключений" для каждого тасклета.

void tasklet_schedule(struct tasklet_struct *t);
  • Планирует тасклет на исполнение. Если тасклет запланирован снова прежде, чем у него появится шанс заработать, он запустится только один раз. Однако, если он запланирован во время работы, он запустится снова после его завершения; это гарантирует, что события, происходящие во время обработки других событий, получат должное внимание. Такое поведение также позволяет тасклету перепланировать себя.

void tasklet_hi_schedule(struct tasklet_struct *t);
  • Планирует тасклет для выполнения с более высоким приоритетом. Когда обработчик программного прерывания работает, он выполняет высокоприоритетные тасклеты перед другими задачами программных прерываний, в том числе "нормальных" тасклетов. В идеале только задания с требованиями малой задержки (такие, как заполнение звукового буфера) должны использовать эту функцию, чтобы избежать дополнительных задержек, вводимых другими обработчиками программных прерываний. На самом деле /proc/jitasklethi не показывает видимое человеку отличие от /proc/jitasklet.

void tasklet_kill(struct tasklet_struct *t);
  • Эта функция гарантирует, что тасклет не планируется запустить снова; её обычно вызывают, когда устройство закрывается или модуль удаляется. Если тасклет запланирован на запуск, функция ждёт его выполнения. Если тасклет перепланирует сам себя, вы должны запретить ему перепланировать себя перед вызовом tasklet_kill, как и в случае del_timer_sync.

Тасклеты реализованы в kernel/softirq.c. Два списка тасклетов (нормальный и высоко-приоритетный) объявлены как структуры данных, имеющие копии для каждого процессора, используя такой же механизм родства процессоров, как и для таймеров ядра. Структура данных, используемая для управления тасклетами, является простым связным списком, потому что тасклеты не имеют ни одного из требований сортировки таймеров ядра.

7.5. Tasklets

7.5. Tasklets

Another kernel facility related to timing issues is the tasklet mechanism. It is mostly used in interrupt management (we'll see it again in Chapter 10.)

Tasklets resemble kernel timers in some ways. They are always run at interrupt time, they always run on the same CPU that schedules them, and they receive an unsigned long argument. Unlike kernel timers, however, you can't ask to execute the function at a specific time. By scheduling a tasklet, you simply ask for it to be executed at a later time chosen by the kernel. This behavior is especially useful with interrupt handlers, where the hardware interrupt must be managed as quickly as possible, but most of the data management can be safely delayed to a later time. Actually, a tasklet, just like a kernel timer, is executed (in atomic mode) in the context of a "soft interrupt," a kernel mechanism that executes asynchronous tasks with hardware interrupts enabled.

A tasklet exists as a data structure that must be initialized before use. Initialization can be performed by calling a specific function or by declaring the structure using certain macros:

#include
struct tasklet_struct {
      /* ... */
      void (*func)(unsigned long);
      unsigned long data;
};
void tasklet_init(struct tasklet_struct *t,
      void (*func)(unsigned long), unsigned long data);
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);

Tasklets offer a number of interesting features:

A tasklet can be disabled and re-enabled later; it won't be executed until it is enabled as many times as it has been disabled.

Just like timers, a tasklet can reregister itself.

A tasklet can be scheduled to execute at normal priority or high priority. The latter group is always executed first.

Tasklets may be run immediately if the system is not under heavy load but never later than the next timer tick.

A tasklets can be concurrent with other tasklets but is strictly serialized with respect to itself—the same tasklet never runs simultaneously on more than one processor. Also, as already noted, a tasklet always runs on the same CPU that schedules it.

The jit module includes two files, /proc/jitasklet and /proc/jitasklethi, that return the same data as /proc/jitimer, introduced in Section 7.4 When you read one of the files, you get back a header and six data lines. The first data line describes the context of the calling process, and the other lines describe the context of successive runs of a tasklet procedure. This is a sample run while compiling a kernel:

phon% cat /proc/jitasklet
   time   delta  inirq    pid   cpu command
  6076139    0     0      4370   0   cat
  6076140    1     1      4368   0   cc1
  6076141    1     1      4368   0   cc1
  6076141    0     1         2   0   ksoftirqd/0
  6076141    0     1         2   0   ksoftirqd/0
  6076141    0     1         2   0   ksoftirqd/0

As confirmed by the above data, the tasklet is run at the next timer tick as long as the CPU is busy running a process, but it is run immediately when the CPU is otherwise idle. The kernel provides a set of ksoftirqd kernel threads, one per CPU, just to run "soft interrupt" handlers, such as the tasklet_action function. Thus, the final three runs of the tasklet take place in the context of the ksoftirqd kernel thread associated to CPU 0. The jitasklethi implementation uses a high-priority tasklet, explained in an upcoming list of functions.

The actual code in jit that implements /proc/jitasklet and /proc/jitasklethi is almost identical to the code that implements /proc/jitimer, but it uses the tasklet calls instead of the timer ones. The following list lays out in detail the kernel interface to tasklets after the tasklet structure has been initialized:

void tasklet_disable(struct tasklet_struct *t);

This function disables the given tasklet. The tasklet may still be scheduled with tasklet_schedule, but its execution is deferred until the tasklet has been enabled again. If the tasklet is currently running, this function busy-waits until the tasklet exits; thus, after calling tasklet_disable, you can be sure that the tasklet is not running anywhere in the system.

void tasklet_disable_nosync(struct tasklet_struct *t);

Disable the tasklet, but without waiting for any currently-running function to exit. When it returns, the tasklet is disabled and won't be scheduled in the future until re-enabled, but it may be still running on another CPU when the function returns.

void tasklet_enable(struct tasklet_struct *t);

Enables a tasklet that had been previously disabled. If the tasklet has already been scheduled, it will run soon. A call to tasklet_enable must match each call to tasklet_disable, as the kernel keeps track of the "disable count" for each tasklet.

void tasklet_schedule(struct tasklet_struct *t);

Schedule the tasklet for execution. If a tasklet is scheduled again before it has a chance to run, it runs only once. However, if it is scheduled while it runs, it runs again after it completes; this ensures that events occurring while other events are being processed receive due attention. This behavior also allows a tasklet to reschedule itself.

void tasklet_hi_schedule(struct tasklet_struct *t);

Schedule the tasklet for execution with higher priority. When the soft interrupt handler runs, it deals with high-priority tasklets before other soft interrupt tasks, including "normal" tasklets. Ideally, only tasks with low-latency requirements (such as filling the audio buffer) should use this function, to avoid the additional latencies introduced by other soft interrupt handlers. Actually, /proc/jitasklethi shows no human-visible difference from /proc/jitasklet.

void tasklet_kill(struct tasklet_struct *t);

This function ensures that the tasklet is not scheduled to run again; it is usually called when a device is being closed or the module removed. If the tasklet is scheduled to run, the function waits until it has executed. If the tasklet reschedules itself, you must prevent it from rescheduling itself before calling tasklet_kill, as with del_timer_sync.

Tasklets are implemented in kernel/softirq.c. The two tasklet lists (normal and high-priority) are declared as per-CPU data structures, using the same CPU-affinity mechanism used by kernel timers. The data structure used in tasklet management is a simple linked list, because tasklets have none of the sorting requirements of kernel timers.

7.4. Таймеры ядра

7.4. Таймеры ядра


Если необходимо запланировать действия на позднее время без блокирования текущего процесса до наступления момента времени, инструментом для вас являются таймеры ядра. Такие таймеры используются для планирования выполнения функции в определённое время в будущем, основываясь на тактовых тиках, и могут использоваться для различных задач; например, опрос устройства путём проверки его состояния через регулярные промежутки времени, когда оборудование не может генерировать прерывания. Другим типичным использованием таймеров ядра является отключение двигателя дисковода или завершение другой длительной операции выключения. В таких случаях задержка возвращения из close создала бы ненужные (и неожиданные) трудности для прикладной программы. Наконец, само ядро использует таймеры в ряде ситуаций, включая реализацию schedule_timeout.

Таймер ядра является структурой данных, которая инструктирует ядро для выполнения заданных пользователем функции с заданным пользователем аргументом в заданное пользователем время. Реализация находится в и kernel/timer.c и подробно описана в разделе "Реализация таймеров ядра".

Функции, запланированные для запуска, почти наверняка не работают, пока выполняется процесс, который их регистрирует. Вместо этого, они запускаются асинхронно. До сих пор всё, что мы сделали в наших примерах драйверов - это работа в контексте процесса, выполняющего системные вызовы. Однако, когда запущен таймер, процесс, который запланировал его, может спать, выполняться на другом процессоре, или, вполне возможно, вообще завершиться.

Это асинхронное выполнение напоминает то, что происходит, когда происходит аппаратное прерывание (которое подробно рассматривается в Главе 10). В самом деле, таймеры ядра работают как результат "программного прерывания". При запуске в атомарном контексте этого рода на ваш код налагается ряд ограничений. Функции таймера должны быть атомарными всеми способами, которые мы обсуждали в разделе "Спин-блокировки и контекст атомарности" в Главе 5, но есть некоторые дополнительные вопросы, вызванные отсутствием контекста процесса. Теперь вы введём эти ограничения; они будут рассматриваться снова в нескольких местах в последующих главах. Повторение делается потому, что правила для атомарных контекстов должны усердно соблюдаться, или система окажется в тяжёлом положении. Некоторые действия требуют для выполнения контекст процесса. Когда вы находитесь за пределами контекста процесса (то есть в контексте прерывания), вы должны соблюдать следующие правила:

Не разрешён доступ к пользовательскому пространству. Из-за отсутствия контекста процесса, нет пути к пользовательскому пространству, связанному с любым определённым процессом.
Указатель current не имеет смысла в атомарном режиме и не может быть использован, так как соответствующий код не имеет связи с процессом, который был прерван.
Не может быть выполнено засыпание или переключение. Атомарный код не может вызвать schedule или какую-то из форм wait_event и не может вызвать любые другие функции, которые могли бы заснуть. Например, вызов kmalloc(..., GFP_KERNEL) идёт против правил. Семафоры также не должны быть использованы, поскольку они могут спать.

Код ядра может понять, работает ли он в контексте прерывания, вызовом функции in_interrupt( ), которая не имеет параметров и возвращает ненулевое значение, если процессор в настоящее время работает в контексте прерывания, аппаратного или программного. Функцией, связанной с in_interrupt() является in_atomic(). Она возвращает ненулевое значение, когда переключение не допускается; это включает в себя аппаратный и программный контексты прерывания, а также любое время, когда удерживается спин-блокировка. В последнем случае, current может быть действительным, но доступ к пользовательскому пространству запрещён, поскольку это может привести к переключению. Всякий раз, когда вы используете in_interrupt(), следует всерьёз рассмотреть вопрос, не является ли in_atomic() тем, что вы действительно имеете в виду. Обе функции объявлены в .

Ещё одной важной особенностью таймеров ядра является то, что задача может перерегистрировать себя для запуска снова в более позднее время. Это возможно, потому что каждая структура timer_list не связана со списком активных таймеров перед запуском и, следовательно, может быть немедленно перекомпонована где угодно. Хотя переключение на одну и ту же задачу снова и снова может показаться бессмысленной операцией, иногда это бывает полезно. Например, это может быть использовано для реализации опроса устройств.

Кроме того, стоит знать, что в многопроцессорных системах, таймерная функция (функция, запускаемая таймером) выполняется тем же процессором, который её зарегистрировал для достижения лучшего расположения кэша, когда это возможно. Поэтому таймер, который перерегистрирует себя, всегда запускается на том же процессоре.

Важной особенностью таймеров о которой, однако, не следует забывать является то, что они являются потенциальным источником состояний гонок даже на однопроцессорных системах. Это прямой результате их асинхронности с другим кодом. Таким образом, любые структуры данных, которые используются таймерной функцией, должны быть защищены от одновременного доступа либо использованием атомарных типов (обсуждаемых в разделе "Атомарные переменные" в Главе 5) или использованием спин-блокировок (обсуждаемых в Главе 5).

7.4.1. API таймера

Ядро предоставляет драйверам ряд функций для декларации, регистрации и удаления таймеров ядра. Ниже приводится выдержка, показывающая основные стандартные блоки:

#include 
struct timer_list {
    /* ... */
    unsigned long expires;
    void (*function)(unsigned long);
    unsigned long data;
};

void init_timer(struct timer_list *timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

void add_timer(struct timer_list * timer);
int del_timer(struct timer_list * timer);

Структура данных включает в себя больше полей, чем показано, но эти три предназначены для доступа снаружи кодом таймера. Поле expires представляет значение jiffies, которое таймер ожидает для запуска; в это время функция function вызывается с data в качестве аргумента. Если необходимо передать много объектов в аргументе, можно собрать их в единую структуру данных и передать указатель, приведя к unsigned long, это безопасная практика на всех поддерживаемых архитектурах и довольно распространена в управлении памятью (как описывается в Главе 15). 

Значение expires не является типом jiffies_64, поскольку не ожидается, что таймер сработает очень далеко в будущем и на 32-х разрядных платформах 64-х разрядные операции медленны.

Структуры должны быть проинициализированы перед использованием. Этот шаг гарантирует, что все поля правильно настроены, в том числе те, которые не видимы для вызывающего. Инициализация может быть осуществлена вызовом init_timer или присвоением TIMER_INITIALIZER статической структуре, в соответствии с вашими потребностями. После инициализации, перед вызовом add_timer, можно изменить три открытых поля. Чтобы отключить зарегистрированный таймер до его истечения, вызовите del_timer. Модуль jit включает файл примера, /proc/jitimer (для "только по таймеру"), который возвращает строку заголовка и шесть строк данных. Строки данных показывают текущее окружение, где выполняется код; первая создаётся файловой операцией read, остальные - по таймеру. Следующий вывод был записан во время компиляции ядра:

phon% cat /proc/jitimer
  time    delta inirq  pid   cpu command
 33565837   0     0    1269   0   cat
 33565847  10     1    1271   0   sh
 33565857  10     1    1273   0   cpp0
 33565867  10     1    1273   0   cpp0
 33565877  10     1    1274   0   cc1
 33565887  10     1    1274   0   cc1

В этом выводе, поле time является значением jiffies, когда код запускается, delta является изменением jiffies относительно предыдущей строки, inirq - это логическое значение, возвращаемое in_interrupt, pid и command относятся к текущему процессу и cpu является номером используемого процессора (всегда 0 на однопроцессорных системах).

Если вы прочитаете /proc/jitimer при выгрузке системы, вы обнаружите, что контекстом таймера является процесс 0, задача простоя (idle), которая называется “swapper” ("планировщик своппинга") в основном по историческим причинам.

Таймер используется для генерации данных /proc/jitimer по умолчанию каждые 10 тиков, но при загрузке модуля можно изменить значение, установив параметр tdelay (timer delay, задержка таймера).

Следующий отрывок кода показывает часть jit, связанную с таймером jitimer. Когда процесс пытается прочитать наш файл, мы устанавливаем таймер следующим образом:

unsigned long j = jiffies;

/* заполняем данные для нашей таймерной функции */
data->prevjiffies = j;
data->buf = buf2;
data->loops = JIT_ASYNC_LOOPS;

/* регистрируем таймер */
data->timer.data = (unsigned long)data;
data->timer.function = jit_timer_fn;
data->timer.expires = j + tdelay; /* параметр */
add_timer(&data->timer);

/* ждём заполнения буфера */
wait_event_interruptible(data->wait, !data->loops);

Фактическая функция, вызываемая по таймеру, выглядит следующим образом:

void jit_timer_fn(unsigned long arg)
{
    struct jit_data *data = (struct jit_data *)arg;
    unsigned long j = jiffies;
    data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
                            j, j - data->prevjiffies, in_interrupt( ) ? 1 : 0,
                            current->pid, smp_processor_id( ), current->comm);

    if (--data->loops) {
        data->timer.expires += tdelay;
        data->prevjiffies = j;
        add_timer(&data->timer);
    } else {
        wake_up_interruptible(&data->wait);
    }
}

API таймера включает в себя несколько больше функций, чем те, которые введены выше. Следующий набор завершает список предложения ядра:

int mod_timer(struct timer_list *timer, unsigned long expires);
  • Обновление времени истечения срока таймера, общая задача, для которой используется таймер ожидания (опять же, типичный пример - таймер выключения мотора дисковода). mod_timer может быть вызвана при неактивных таймерах, также, как обычно используется add_timer.

int del_timer_sync(struct timer_list *timer);
  • Работает как del_timer, но также гарантирует, что когда она вернётся, таймерная функция не запущена на каком-то процессоре. del_timer_sync используется, чтобы избежать состояний состязания на многопроцессорных системах, и аналогична del_timer на однопроцессорных ядрах. Этой функции в большинстве ситуаций следует отдавать предпочтение перед del_timer. Эта функция может заснуть, если вызывается из неатомарного контекста, но находится в активном ожидании в других ситуациях. Будьте очень осторожны вызывая del_timer_sync при удержании блокировок; если таймерная функция попытается получить ту же блокировку, система может заблокироваться. Если эта таймерная функция перерегистрирует себя, вызывающий должен сначала убедиться, что эта перерегистрация не произошла; обычно это достигается установкой флага “shutting down” ("выключить"), который проверяется таймерной функцией.

int timer_pending(const struct timer_list * timer);
  • Возвращает истину или ложь, чтобы показать, будет ли таймер в настоящее время запланирован для запуска, чтением одной из скрытых полей структуры.
7.4.2. Реализация таймеров ядра

Хотя вам не требуется знать, как реализованы таймеры ядра для их использования, реализация их интересна и стоит посмотреть на их внутренности.

Реализация счётчиков была разработана, чтобы соответствовать следующим требованиям и предположениям:

Управление таймером должно быть как можно более лёгким.
Конструкция должна позволять увеличивать количество активных таймеров.
Большинство таймеров истекают в течение нескольких секунд или, самое большее, минут, в то время как таймеры с длительными задержками довольно редки.
Таймер должен работать на том же процессоре, который его зарегистрировал.

Решение, разработанное разработчиками ядра, основано на копии структуры данных для каждого процессора. Структура timer_list включает в себя указатель на такую структуру данных в своём поле base. Если base является NULL, таймер не запланирован для запуска; в противном случае, указатель говорит, какая структура данных (и, следовательно, какой процессор) запускает его. Копии объектов данных для каждого процессора описываются в разделе "Копии переменных для процессора" в Главе 8.

Всякий раз, когда код ядра регистрирует таймер (через add_timer или mod_timer), операции в конечном итоге выполняются internal_add_timerkernel/timer.c), которая, в свою очередь, добавляет новый таймер в двусвязный список таймеров в рамках "каскадной таблицы", связанной с текущим процессором.

Каскадные таблицы работают так: если таймер истекает в следующих тиках от 0 до 255, он добавляется в один из 256 списков, посвященных таймерам малого диапазона, используя самые младшие биты поля expires. Если он истекает дальше в будущем (но до 16384 тиков), он добавляется в один из 64 списков на основе битов 9-14 поля expires. Для таймеров истекающих ещё позже, тот же приём используется для битов 15-20, 21-26 и 27-31. Таймеры с полем времени окончания ещё дальше в будущем (что может случиться только на 64-х разрядных платформах) делятся на задержки со значением 0xffffffff и таймеры с expires в прошлом планируются для запуска в следующем тике таймера. (Таймер, который уже истёк, иногда может быть зарегистрирован в ситуациях высокой нагрузки, особенно если вы работаете с вытесняющим ядром.)

После запуска __run_timers, он запускает все отложенные таймеры на текущий тик таймера. Если jiffies в настоящее время является кратной 256, функция также заново делит один из списков таймеров следующего уровня на 256 списков короткого диапазона, возможно каскадируя один или нескольких других уровней также в соответствии с битовым представлением jiffies.

Это подход, хотя и чрезвычайно сложный на первый взгляд, выполняется очень хорошо как с несколькими таймерами, так и с большим их числом. Время, необходимое для управления каждым активным таймером, не зависит от количества уже зарегистрированных таймеров и ограничено несколькими логическими операциями над двоичным представлением поля expires. Накладным расходом, связанным с этой реализацией, является память для 512 заголовков листов (256 краткосрочных списков и 4 группы из 64 списков более длительных диапазонов), то есть 4 Кб памяти.

Функция __run_timers, как показано /proc/jitimer, будет запущена в атомарном контексте. В добавок к уже описанным ограничениям, это приносит интересную особенность: таймер истекает только в заданное время, даже если вы не работаете на вытесняющем ядре и процессор занят в пространстве ядра. Вы можете видеть, что происходит, когда вы читаете /proc/jitbusy в фоновом режиме и /proc/jitimer с высоким приоритетом. Хотя система кажется прочно заблокированной системным вызовом с активным ожиданием, таймеры ядра всё же работают нормально.

Однако, имейте в виду, что таймер ядра далёк от совершенства, он страдает от дрожания и других артефактов, вносимых аппаратными прерываниями, а также другими таймерами и другими асинхронными задачами. Хотя таймер, связанный с простым цифровым вводом/выводом, может быть достаточен для таких простых задач, как запуск шагового двигателя или другой любительской электроники, это обычно не подходит для производственных систем в промышленных условиях. Для выполнения таких задач вам скорее всего придётся прибегнуть к расширению ядра для реального времени.

7.4. Kernel Timers

7.4. Kernel Timers

Whenever you need to schedule an action to happen later, without blocking the current process until that time arrives, kernel timers are the tool for you. These timers are used to schedule execution of a function at a particular time in the future, based on the clock tick, and can be used for a variety of tasks; for example, polling a device by checking its state at regular intervals when the hardware can't fire interrupts. Other typical uses of kernel timers are turning off the floppy motor or finishing another lengthy shut down operation. In such cases, delaying the return from close would impose an unnecessary (and surprising) cost on the application program. Finally, the kernel itself uses the timers in several situations, including the implementation of schedule_timeout.

A kernel timer is a data structure that instructs the kernel to execute a user-defined function with a user-defined argument at a user-defined time. The implementation resides in and kernel/timer.c and is described in
detail in the Section 7.4.2

The functions scheduled to run almost certainly do not run while the process that registered them is executing. They are, instead, run asynchronously. Until now, everything we have done in our sample drivers has run in the context of a process executing system calls. When a timer runs, however, the process that scheduled it could be asleep, executing on a different processor, or quite possibly has exited altogether.

This asynchronous execution resembles what happens when a hardware interrupt happens (which is discussed in detail in Chapter 10). In fact, kernel timers are run as the result of a "software interrupt." When running in this sort of atomic context, your code is subject to a number of constraints. Timer functions must be atomic in all the ways we discussed in Chapter 5, but there are some additional issues brought about by the lack of a process context. We will introduce these constraints now; they will be seen again in several places in later chapters.

Repetition is called for because the rules for atomic contexts must be followed assiduously, or the system will find itself in deep trouble.

A number of actions require the context of a process in order to be executed.

When you are outside of process context (i.e., in interrupt context), you must observe the following rules:

No access to user space is allowed. Because there is no process context, there is no path to the user space associated with any particular process.

The current pointer is not meaningful in atomic mode and cannot be used since the relevant code has no connection with the process that has been interrupted.

No sleeping or scheduling may be performed. Atomic code may not call schedule or a form of wait_event, nor may it call any other function that could sleep. For example, calling kmalloc(..., GFP_KERNEL) is against the rules. Semaphores also must not be used since they can sleep.

Kernel code can tell if it is running in interrupt context by calling the function in_interrupt( ), which takes no parameters and returns nonzero if the processor is currently running in interrupt context, either hardware interrupt or software interrupt.

A function related to in_interrupt( ) is in_atomic( ). Its return value is nonzero whenever scheduling is not allowed; this includes hardware and software interrupt contexts as well as any time when a spinlock is held. In the latter case, current may be valid, but access to user space is forbidden, since it can cause scheduling to happen. Whenever you are using in_interrupt( ), you should really consider whether in_atomic( ) is what you actually mean. Both functions are declared in

One other important feature of kernel timers is that a task can reregister itself to run again at a later time. This is possible because each timer_list structure is unlinked from the list of active timers before being run and can, therefore, be immediately re-linked elsewhere. Although rescheduling the same task over and over might appear to be a pointless operation, it is sometimes useful. For example, it can be used to implement the polling of devices.

It's also worth knowing that in an SMP system, the timer function is executed by the same CPU that registered it, to achieve better cache locality whenever possible. Therefore, a timer that reregisters itself always runs on the same CPU.
An important feature of timers that should not be forgotten, though, is that they are a potential source of race conditions, even on uniprocessor systems. This is a direct result of their being asynchronous with other code. Therefore, any data structures accessed by the timer function should be protected from concurrent access, either by being atomic types or by using spinlocks.

7.4.1. The Timer API

The kernel provides drivers with a number of functions to declare, register, and remove kernel timers. The following excerpt shows the basic building blocks:

#include
struct timer_list {
        /* ... */
        unsigned long expires;
        void (*function)(unsigned long);
        unsigned long data;
};
void init_timer(struct timer_list *timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
void add_timer(struct timer_list * timer);
int del_timer(struct timer_list * timer);

The data structure includes more fields than the ones shown, but those three are the ones that are meant to be accessed from outside the timer code iteslf. The expires field represents the jiffies value when the timer is expected to run; at that time, the function function is called with data as an argument. If you need to pass multiple items in the argument, you can bundle them as a single data structure and pass a pointer cast to unsigned long, a safe practice on all supported architectures and pretty common in memory management (as discussed in Chapter 15). The expires value is not a jiffies_64 item because timers are not expected to expire very far in the future, and 64-bit operations are slow on 32-bit platforms.

The structure must be initialized before use. This step ensures that all the fields are properly set up, including the ones that are opaque to the caller. Initialization can be performed by calling init_timer or assigning TIMER_INITIALIZER to a static structure, according to your needs. After initialization, you can change the three public fields before calling add_timer. To disable a registered timer before it expires, call del_timer.

The jit module includes a sample file, /proc/jitimer (for "just in timer"), that returns one header line and six data lines. The data lines represent the current environment where the code is running; the first one is generated by the read file operation and the others by a timer. The following output was recorded while compiling a kernel:

phon% cat /proc/jitimer
   time   delta  inirq    pid   cpu command
 33565837    0     0      1269   0   cat
 33565847   10     1      1271   0   sh
 33565857   10     1      1273   0   cpp0
 33565867   10     1      1273   0   cpp0
 33565877   10     1      1274   0   cc1
 33565887   10     1      1274   0   cc1

In this output, the time field is the value of jiffies when the code runs, delta is the change in jiffies since the previous line, inirq is the Boolean value returned by in_interrupt, pid and command refer to the current process, and cpu is the number of the CPU being used (always 0 on uniprocessor systems).

If you read /proc/jitimer while the system is unloaded, you'll find that the context of the timer is process 0, the idle task, which is called "swapper" mainly for historical reasons.

The timer used to generate /proc/jitimer data is run every 10 jiffies by default, but you can change the value by setting the tdelay (timer delay) parameter when loading the module.

The following code excerpt shows the part of jit related to the jitimer timer. When a process attempts to read our file, we set up the timer as follows:

unsigned long j = jiffies;
/* fill the data for our timer function */
data->prevjiffies = j;
data->buf = buf2;
data->loops = JIT_ASYNC_LOOPS;
    
/* register the timer */
data->timer.data = (unsigned long)data;
data->timer.function = jit_timer_fn;
data->timer.expires = j + tdelay; /* parameter */
add_timer(&data->timer);
/* wait for the buffer to fill */
wait_event_interruptible(data->wait, !data->loops);

The actual timer function looks like this:

void jit_timer_fn(unsigned long arg)
{
    struct jit_data *data = (struct jit_data *)arg;
    unsigned long j = jiffies;
    data->buf += sprintf(data->buf, "%9li  %3li     %i    %6i   %i   %s\n",
                 j, j - data->prevjiffies, in_interrupt(  ) ? 1 : 0,
                 current->pid, smp_processor_id(  ), current->comm);
    if (--data->loops) {
        data->timer.expires += tdelay;
        data->prevjiffies = j;
        add_timer(&data->timer);
    } else {
        wake_up_interruptible(&data->wait);
    }
}

The timer API includes a few more functions than the ones introduced above. The following set completes the list of kernel offerings:

int mod_timer(struct timer_list *timer, unsigned long expires);

Updates the expiration time of a timer, a common task for which a timeout timer is used (again, the motor-off floppy timer is a typical example). mod_timer can be called on inactive timers as well, where you normally use add_timer.

int del_timer_sync(struct timer_list *timer);

Works like del_timer, but also guarantees that when it returns, the timer function is not running on any CPU. del_timer_sync is used to avoid race conditions on SMP systems and is the same as del_timer in UP kernels. This function should be preferred over del_timer in most situations. This function can sleep if it is called from a nonatomic context but busy waits in other situations. Be very careful about calling del_timer_sync while holding locks; if the timer function attempts to obtain the same lock, the system can deadlock. If the timer function reregisters itself, the caller must first ensure that this reregistration will not happen; this is usually accomplished by setting a "shutting down" flag, which is checked by the timer function.

int timer_pending(const struct timer_list * timer);

Returns true or false to indicate whether the timer is currently scheduled to run by reading one of the opaque fields of the structure.

7.4.2. The Implementation of Kernel Timers

Although you won't need to know how kernel timers are implemented in order to use them, the implementation is interesting, and a look at its internals is worthwhile.

The implementation of the timers has been designed to meet the following requirements and assumptions:

Timer management must be as lightweight as possible.

The design should scale well as the number of active timers increases.

Most timers expire within a few seconds or minutes at most, while timers with long delays are pretty rare.

A timer should run on the same CPU that registered it.

The solution devised by kernel developers is based on a per-CPU data structure. The timer_list structure includes a pointer to that data structure in its base field. If base is NULL, the timer is not scheduled to run; otherwise, the pointer tells which data structure (and, therefore, which CPU) runs it. Per-CPU data items are described in Section 8.5 in Section 7.1.1.

Whenever kernel code registers a timer (via add_timer or mod_timer), the operation is eventually performed by internal_add_timer (in kernel/timer.c) which, in turn, adds the new timer to a double-linked list of timers within a "cascading table" associated to the current CPU.

The cascading table works like this: if the timer expires in the next to 255 jiffies, it is added to one of the 256 lists devoted to short-range timers using the least significant bits of the expires field. If it expires farther in the future (but before 16,384 jiffies), it is added to one of 64 lists based on bits 9-14 of the expires field. For timers expiring even farther, the same trick is used for bits 15-20, 21-26, and 27-31. Timers with an expire field pointing still farther in the future (something that can happen only on 64-bit platforms) are hashed with a delay value of 0xffffffff, and timers with expires in the past are scheduled to run at the next timer tick. (A timer that is already expired may sometimes be registered in high-load situations, especially if you run a preemptible kernel.)

When _ _run_timers is fired, it executes all pending timers for the current timer tick. If jiffies is currently a multiple of 256, the function also rehashes one of the next-level lists of timers into the 256 short-term lists, possibly cascading one or more of the other levels as well, according to the bit representation of jiffies.
 

This approach, while exceedingly complex at first sight, performs very well both with few timers and with a large number of them. The time required to manage each active timer is independent of the number of timers already registered and is limited to a few logic operations on the binary representation of its expires field. The only cost associated with this implementation is the memory for the 512 list heads (256 short-term lists and 4 groups of 64 more lists)—i.e., 4 KB of storage.

The function _ _run_timers, as shown by /proc/jitimer, is run in atomic context. In addition to the limitations we already described, this brings in an interesting feature: the timer expires at just the right time, even if you are not running a preemptible kernel, and the CPU is busy in kernel space. You can see what happens when you read /proc/jitbusy in the background and /proc/jitimer in the foreground. Although the system appears to be locked solid by the busy-waiting system call, the kernel timers still work fine.

Keep in mind, however, that a kernel timer is far from perfect, as it suffers from jitter and other artifacts induced by hardware interrupts, as well as other timers and other asynchronous tasks. While a timer associated with simple digital I/O can be enough for simple tasks like running a stepper motor or other amateur electronics, it is usually not suitable for production systems in industrial environments. For such tasks, you'll most likely need to resort to a real-time kernel extension.