Linux 2.4.x Kernel soft interrupt mechanism

Source: Internet
Author: User

http://www.ibm.com/developerworks/cn/linux/kernel/interrupt/

Soft Interrupt Overview

Soft interrupt is the concept of hardware interruption, which is simulated by software to realize the effect of asynchronous execution on macro. In many cases, soft interrupts and "signals" are somewhat similar, while soft interrupts are also corresponding to hard interrupts, " hard interrupts are external device-to-CPU interrupts", "soft interrupts are usually hard interrupt service program to the core of the interrupt", "signal is the kernel (or other processes) to a process interrupt " ( "Linux kernel source code Scenario analysis" chapter III). A typical application of soft interrupts is the so-called "bottom half" (bottom half), which derives its name from the mechanism of separating the hardware interrupt processing into the "top half" and "lower half" phases: The top half runs in the context of the shielding interrupt, and is used to complete the critical processing action; The lower half is relatively less urgent and usually time-consuming, so the system runs its own schedule and does not execute in the context of the interrupt service . the application of bottom half is also the reason that the excitation kernel develops the current soft interrupt mechanism, so we start with the implementation of the bottom half first.

Back to top of page

Bottom half

In the Linux kernel, bottom half is typically denoted by "BH", which was originally used to complete the non-critical time-consuming action of interrupt service in a lower privileged context and is now used for all asynchronous actions that can be performed in a low-priority context. The earliest bottom half implementation is the way to borrow the interrupt vector table, which can still be seen in the current 2.4.x kernel:

static void (*bh_base[32]) (void);         /* KERNEL/SOFTIRQ.C */

The system defines an array of function pointers, with a total of 32 function pointers, accessed using an array index, which corresponds to a set of functions:

void Init_bh (int nr,void (*routine) (void));

Assign a value of routine to the NR function pointer.

void Remove_bh (int nr);

Action with INIT_BH () instead, remove the NR function pointer.

void Mark_bh (int nr);

Mark nr of bottom half executable.

For historical reasons, most of the Bh_base function pointer positions have predefined meanings, and there is an enumeration in the v2.4.2 kernel:

enum {         timer_bh = 0,         tqueue_bh,         digi_bh,         serial_bh,         riscom8_bh,         specialix_bh,         Aurora_ BH,         esp_bh,         scsi_bh,         immediate_bh,         cyclades_bh,         cm206_bh         , JS_BH, MACSERIAL_BH,         ISICOM_BH};

and agreed to a driver using a bottom half location, such as serial port interruption agreed to use SERIAL_BH, now we use much of the main is TIMER_BH, TQUEUE_BH and IMMEDIATE_BH, but the semantics are very different, because the whole bottom The use of half has been very different, these three functions are only on the interface to maintain backward compatibility, in the implementation has been in the kernel with the soft interrupt mechanism is changing. Now, in the 2.4.x kernel, it uses the tasklet mechanism.

Back to top of page

Task Queue

Before introducing Tasklet, it is necessary to first look at the earlier task queue mechanism. Obviously, the original bottom half mechanism has a few great limitations, the most important one is that the number is limited to 32, as the system hardware more and more, the application of soft interrupts more and more large, this number is obviously not enough, and, each bottom half can only hook up a function, is not enough to use. Therefore, in the 2.0.x kernel, it has been expanded with Task queue (task queues), which is used in 2.4.2 implementations.

The task queue is built on the structure of the system queue, and the following is the data structure of the task queue, defined in Include/linux/tqueue.h:

struct Tq_struct {        struct list_head list;          /* Linked list structure */        unsigned long sync;             /* First known as 0, the time of the queue Atom 1, in order to avoid repeating the queue        * * void (*routine) (void *);        /* function called when activated */        void *data;                     /* Routine (data) */};typedef struct list_head task_queue;

When in use, follow these steps:

    1. Declare_task_queue (My_tqueue); /* Define a my_tqueue, which is actually a list_head queue with tq_struct as an element */
    2. Describe and define a tq_struct variable my_task;
    3. Queue_task (&my_task,&my_tqueue); /* Register My_task in My_tqueue */
    4. Run_task_queue (&my_tqueue); /* Manually start my_tqueue at the appropriate time */

In most cases, it is not necessary to call Declare_task_queue () to define its own task queue because the system has predefined three task queues:

    1. Tq_timer, initiated by the clock Interrupt service program;
    2. Tq_immediate, starting before the interrupt return and the schedule () function;
    3. Tq_disk, internal use of the memory management module.

Most asynchronous tasks can be done with tq_immediate.

The Run_task_queue (task_queue *list) function can be used to start all the tasks that are hooked up in the list, either manually or by hooking up in the bottom half vector table mentioned above. The function pointer with Run_task_queue () as BH_BASE[NR] actually expands the number of function handles for each bottom half, while the Tq_timer and tq_immediate that are predefined for the system are respectively hooked up to Tqueue_ BH and IMMEDIATE_BH (note that TIMER_BH is not used in this way, but TQUEUE_BH is also initiated in Do_timer (), which can be used to expand the number of bottom half. At this point, you do not need to call Run_task_queue () manually (this is not appropriate), but simply call MARK_BH (IMMEDIATE_BH), let the bottom half mechanism dispatch it at the appropriate time.

Back to top of page

Tasklet

As seen from the above, task queue is based on bottom half, while bottom half in v2.4.x is based on the newly introduced tasklet.

The main consideration in introducing Tasklet is to better support SMP and improve the utilization of SMP CPUs: different tasklet can run on different CPUs at the same time . In its source code comments also explained a few features, boils down to a point, that is: the same tasklet will only run on one CPU .

struct tasklet_struct{        struct tasklet_struct *next;/* queue pointer */        unsigned long state;/* tasklet State, bitwise operation, Currently defines two bits meaning: tasklet_state_sched (No. 0 bit) or Tasklet_state_run (1th bit) */        atomic_t count;/* reference count, usually 1 for disabled *        / void (*func) (unsigned long);/* Function pointer */        unsigned long data;/* func (data) */};

Comparing the above structure with tq_struct, it can be seen that tasklet expands a bit of functionality, mainly the State property, for synchronization between CPUs.

The use of Tasklet is fairly straightforward:

    1. Defines a processing function void My_tasklet_func (unsigned long);
    2. Declare_tasklet (My_tasklet,my_tasklet_func,data); /* Define a Tasklet structure my_tasklet, associated with the My_tasklet_func (data) function, equivalent to Declare_task_queue () */
    3. Tasklet_schedule (&my_tasklet); /* Register My_tasklet to allow the system to be scheduled to run at the appropriate time, equivalent to Queue_task (&my_task,&tq_immediate) and MARK_BH (IMMEDIATE_BH) */

It can be seen that tasklet is simpler to use than task queue, and Tasklet is better at supporting SMP structures, so Tasklet is the recommended asynchronous task execution mechanism in the new 2.4.x kernel. In addition to the use steps mentioned above, the Tasklet mechanism provides additional invocation interfaces:

Declare_tasklet_disabled (Name,function,data); /* is similar to Declare_tasklet (), but even if it is scheduled to not run immediately, you must wait for enable */
tasklet_enable (struct tasklet_struct *);/* Tasklet Enable */
tasklet_disble (struct tasklet_struct *);/* Disable Tasklet, as long as Tasklet is not running, it will be deferred until it is enabled */
Tasklet_init (struct tasklet_struct *,void (*func) (unsigned long), unsigned long);/* similar to Declare_tasklet () */
Tasklet_kill (struct tasklet_struct *);/* Clears the scheduled bit of the specified Tasklet, that is, does not allow scheduling of the tasklet, but does not do the cleanup of the Tasklet itself */

As mentioned earlier, in the 2.4.x kernel, bottom half is implemented using the Tasklet mechanism, which behaves as if all bottom half actions are run in a class of tasklet, unlike the tasklet we generally use.

In 2.4.x, the system defines a vector table of two tasklet queues, each of which corresponds to a CPU (the size of the vector table is the maximum number of CPUs the system can support, and the SMP is currently 2.4.2 32) organized into a tasklet linked list:

struct Tasklet_head Tasklet_vec[nr_cpus] __cacheline_aligned;struct tasklet_head Tasklet_hi_vec[nr_cpus] __cacheline _aligned;

In addition, for 32 bottom half, the system also defines the corresponding 32 Tasklet structures:

struct tasklet_struct bh_task_vec[32];

When the soft interrupt subsystem is initialized, the action of this set of Tasklet is initialized to Bh_action (NR), and Bh_action (NR) invokes the function pointer of Bh_base[nr], which is linked to the semantics of bottom half. MARK_BH (NR) is implemented as a call to Tasklet_hi_schedule (BH_TASKLET_VEC+NR), in which BH_TASKLET_VEC[NR] will be hooked up to TASKLET_HI_VEC[CPU] On the chain (where the CPU is the current CPU number, which is the CPU that made the bottom half request, which CPU executes the request), and then fires the HI_SOFTIRQ soft interrupt signal to start the operation in the HI_SOFTIRQ interrupt response.

Tasklet_schedule (&my_tasklet) hooks My_tasklet to tasklet_vec[cpu], fires TASKLET_SOFTIRQ and executes in TASKLET_SOFTIRQ's interrupt response. HI_SOFTIRQ and TASKLET_SOFTIRQ are terms in the SOFTIRQ subsystem, which is described in the next section.

Back to top of page

Softirq

As you can see from the previous discussion, the task queue is based on thebottom Half,bottom half based on Tasklet, and Tasklet is based on SOFTIRQ.

As you can say, SOFTIRQ is the oldest bottom half thought, but on top of this "bottom half" mechanism, a larger and more complex soft interrupt subsystem has been implemented.

struct softirq_action{        void    (*action) (struct softirq_action *);        void    *data;}; static struct softirq_action softirq_vec[32] __cacheline_aligned;

This softirq_vec[] adds the action () function's parameters only to bh_base[], and on execution, SOFTIRQ is less restrictive than bottom half.

Similar to bottom half, the system also pre-defines the purpose of several softirq_vec[] structures, expressed by the following enumeration:

enum{        hi_softirq=0,        Net_tx_softirq,        Net_rx_softirq,        TASKLET_SOFTIRQ};

HI_SOFTIRQ is used to implement bottom HALF,TASKLET_SOFTIRQ for public tasklet use, NET_TX_SOFTIRQ and NET_RX_SOFTIRQ for network subsystem messaging. When the soft interrupt subsystem was initialized (Softirq_init ()), OPEN_SOFTIRQ () was called to initialize HI_SOFTIRQ and TASKLET_SOFTIRQ:

void Open_softirq (int nr, void (*action) (struct softirq_action*), void *data)

OPEN_SOFTIRQ () fills Softirq_vec[nr], and the action and data are set to the parameters passed in. The TASKLET_SOFTIRQ is populated with tasklet_action (null), HI_SOFTIRQ is populated with tasklet_hi_action (null), and in the DO_SOFTIRQ () function, the two functions are called, Start the tasklet operation on the tasklet_vec[cpu] and TASKLET_HI_VEC[CPU] chains, respectively.

static inline void __cpu_raise_softirq (int cpu, int nr)

This function is used to activate the soft interrupt, which is actually the CPU number CPU of NR number of soft interrupt active position 1. This active bit will be judged in DO_SOFTIRQ (). Tasklet_schedule () and Tasklet_hi_schedule () will call this function.

DO_SOFTIRQ () has 4 execution times, which are: returned from the system call (Arch/i386/kernel/entry. S::entry (Ret_from_sys_call)), returning from the exception (Arch/i386/kernel/entry. S::ret_from_exception), in the Scheduler (Kernel/sched.c::schedule ()), and after the hardware interrupt is processed (kernel/irq.c::d O_IRQ ()). It will traverse all the Softirq_vec and start the action () in turn. It is important to note that the soft interrupt service program is not allowed to execute in a hard Interrupt service program, nor is it allowed to be nested in a soft interrupt service program, but allows multiple soft interrupt service programs to concurrently concurrent on multiple CPUs.

Back to top of page

Using the example

SOFTIRQ, as a bottom-level mechanism, is rarely used directly by kernel programmers, so the use of the example here is only for the rest of the soft interrupt mechanism.

1.bottom Half

The original bottom half usage can also be seen in DRIVERS/CHAR/SERIAL.C, including three steps:

INIT_BH (SERIAL_BH,DO_SERIAL_BH);//In the initialization function rs_init () of the serial device, DO_SERIAL_BH () is the processing function mark_bh (SERIAL_BH);//In Rs_sched_ Event (), this function is called by the interrupt processing routine REMOVE_BH (SERIAL_BH);   Called in the End Function Rs_fini () of the serial device

Although the logic is still three steps, the action in the DO_SERIAL_BH () function is to start a task Queue:run_task_queue (&tq_serial), and in Rs_sched_event (), MARK_BH () The previous call is Queue_task (..., &tq_serial), which means that the serial bottom half has been combined with a task queue. The more general bottom half, such as IMMEDIATE_BH, must be used in conjunction with the task queue, and in general, task queue is rarely used independently, but with bottom half, which in the next section of the task The queue usage example can be seen clearly.

2.task queue

In general, programmers rarely define their own task queue, but combine bottom half, directly using the system's predefined tq_immediate, and so on, especially tq_immediate use most frequently. See the following code snippet, excerpt from DRIVERS/BLOCK/FLOPPY.C:

The static struct Tq_struct floppy_tq;//defines a tq_struct structure variable FLOPPY_TQ, which does not require other initialization actions, static void Schedule_bh (void (*handler) ( void*) {        floppy_tq.routine = (void *) (void *) handler;//The calling function of the specified FLOPPY_TQ is handler, and no other domains in FLOPPY_TQ need to be considered Queue_        Task (&FLOPPY_TQ, &tq_immediate);//Add FLOPPY_TQ to Tq_immediate        mark_bh (IMMEDIATE_BH);//Activate IMMEDIATE_BH , as described above, this actually throws a soft interrupt to perform the various functions that are hooked up in Tq_immediate}

Of course, we can still define and use our own task queue without tq_immediate, and the tq_serial mentioned in DRIVERS/CHAR/SERIAL.C is the serial port driver's own definition:

Static Declare_task_queue (tq_serial);

At this point, you need to call Run_task_queue (&tq_serial) to start the function, so it is not commonly used.

3.tasklet

This is a more powerful set of soft interrupt mechanisms than task queue and bottom half, and is relatively simple to use, see the following code snippet:

1:void foo_tasklet_action (unsigned long t); 2:unsigned long Stop_tasklet;3:declare_tasklet (Foo_tasklet, foo_tasklet_ Action, 0); 4:void foo_tasklet_action (unsigned long T) 5:{6://do Something7:8://reschedule9:if (!stop_tasklet) 10: Tasklet_schedule (&foo_tasklet); 11:}12:void foo_init (void) 13:{14:stop_tasklet=0;15:tasklet_schedule (&foo _tasklet); 16:}17:void foo_clean (void) 18:{19:stop_tasklet=1;20:tasklet_kill (&foo_tasklet); 21:}

This more complete piece of code uses a recurring tasklet to do some work, first defining Foo_tasklet in line 3rd, associated with the corresponding action function foo_tasklet_action, and specifying Foo_tasklet_action () The parameter is 0. Although this is a parameter of 0, it is also possible to specify other meaningful parameter values, but it is important to note that this parameter value must be defined as a variable or constant with a fixed value (as in the above example), which means that a global variable can be defined and passed its address as a parameter to Foo_tasklet_action ( ), for example:

int Flags;declare_tasklet (foo_tasklet,foo_tasklet_action,&flags); void foo_tasklet_action (unsigned long T) {    int flags=* (int *) T; ...}

This allows the information to be brought into the tasklet by changing the value of the flags. Directly in the Declare_tasklet filled FLAGS,GCC will be reported "initializer element is not constant" error.

Line 9th, 10 is a kind of reschedule technology. We know that after the execution of a tasklet, it is removed from the execution queue, and if you want to re-enable it to run, you must call Tasklet_schedule () again, the timing of the call can be either when an event occurs, or in a tasklet action like this. This reschedule technology will cause Tasklet to run forever, so there should be a way to stop the Tasklet when the subsystem exits. Stop_tasklet variables and Tasklet_kill () that's what this is all about.

Linux 2.4.x Kernel soft interrupt mechanism

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.