Linux interrupt lower half

Source: Internet
Author: User

6.1 lower half

The lower half of the task is to execute the work closely related to the interrupt processing but the interrupt processing program itself does not execute. Although there are no strict rules for division between the upper half and the lower half, there are still some tips for reference: (1) If a task is very time-sensitive, place it in the Interrupt Processing Program for execution. (2) If a task is related to hardware, place it in the Interrupt Processing Program for execution. (3) If a task is to be interrupted without being interrupted by others, place it in the Interrupt Processing Program for execution. (4) All other tasks should be executed in the lower half. When we start to write our own drivers, reading other interrupt handlers and the lower half will benefit you a lot. Now the question is: when will the lower half be implemented in the future? The lower half does not need to specify a specific time. You just need to postpone these tasks and let them execute after the system is not busy and the recovery is interrupted. Generally, the lower half will be executed immediately when the interrupt handler returns. The key to the lower half of execution is to allow responses to all interruptions when they are running.

 

6.1.1 why should I use the lower half?

The reason is that when the interrupt handler is running, the current Disconnection will be blocked. If a handler is of the sa_interrupt type, during execution, it will prohibit all local interruptions (and globally block local disconnections). In addition, the interrupt handler will be executed asynchronously with other programs-or even other interrupt handlers.

When will it be implemented?

Here, "later" is only used to emphasize that it is not "immediate". The lower half does not need to specify an exact time, but simply delays these tasks, make them run after the system is not busy and the interruption is restored. Generally, the lower half will be executed immediately as soon as the interrupt handler returns, and the lower half will be executed as soon as they run, allow response to all interruptions.

 

6.2 Soft Interrupt

Soft Interrupt simulates the concept of hardware interrupt in software mode to achieve macro asynchronous execution. tasklet is also implemented Based on Soft Interrupt.

The signal based on asynchronous notifications is similar to interrupt.

Hard interrupt refers to the CPU interruption caused by external devices.

Soft Interrupt is usually the interruption of the kernel by the hard interrupt service program.

The signal is the interruption of a process by the kernel (or other processes.

 

6.2.1 Implementation of Soft Interrupt

Soft interruptions are statically allocated during compilation. Unlike tasklet, it can be dynamically registered or removed. Soft Interrupt is represented by the softirq_action structure, which is defined in <Linux/interrupt. h>:

Struct softirq_action {

Void (* Action) (struct softirq_action *);

/* Functions to be executed */

Void * date;/the parameter passed to the function */

};

In kernel/softirq. C, an array containing 32 such struct is defined.

Static strcut softirq_action softirq_vec [32]; each registered Soft Interrupt occupies one of the arrays.

(1) soft interrupt handling program:

The function prototype of the soft interrupt handler action is as follows:

Void softirq_handler (struct softirq_action *)

When the kernel runs a soft interrupt handler, it executes this action function. Its unique parameter is the pointer to the corresponding softirq_action struct.

A Soft Interrupt does not preempt another Soft Interrupt. In fact, the only thing that can preempt a Soft Interrupt is the interrupt processing program. However, other soft interrupts-or even the same type-can be executed simultaneously on other processors.

(2) soft interruption:

A Soft Interrupt of registration can only be executed after it is marked. This is called raising the softirq ). Generally, the interrupt handler will mark its soft interrupt before returning so that it will be executed later. After a Soft Interrupt is marked, you can use softirq_pending () to check the corresponding position 1 of the flag and return the softirq_pending () value according to the index number.

At the right time, the Soft Interrupt will run, and the soft interrupt to be processed will be checked and executed in the following places:

After processing a hard interrupt

In the ksoftirqd kernel thread

In the code that explicitly checks and executes the Soft Interrupt to be processed, such as in the network subsystem

No matter how it is invoked, soft interruptions must be executed in do_softirq (). This function is very simple. If soft interruptions are to be processed, do_softirq () traverses each of them and calls their processing programs.

The Soft Interrupt is executed in do_softirq. The simplified core section of do_softirq:

U32 pending = sofeirq_pending (CPU );

If (pending ){

Struct softirq_action * H = softirq_vec;

Softirq_pending (CPU) = 0;

Do {

If (pending & 1) h-> action (h); // call the Action Function

H ++;

Pending> = 1;

} While (pending );

}

 

6.2.2 Soft Interrupt

The Soft Interrupt is retained to the lower half of the system with the strictest and most important time requirements. The kernel timer and tasklets are both built on Soft Interrupt. If you want to add a new soft interrupt, you must first think about why tasklet cannot be used, and tasklet can be dynamically generated, because they do not have high requirements for locking, it is also very convenient to use. Of course, soft interruption is the right choice for applications that require time and can efficiently complete locking.

1. Allocate indexes: during compilation, you can use an enumeration type defined in <Linux/interrupt. h> to statically declare soft interruptions.

2. register the handler: Then, call open_softirq () at runtime to register the software interrupt handler. This function has three parameters: index number, processing function, and data field value. For example, the network subsystem registers its own soft interrupt in the following ways:

Open_softirq (net_tx_softirq, net_tx_action, null );

Open_softirq (net_tx_softirq, net_rx_action, null );

When the soft interrupt handler is executed, the response is interrupted, but you cannot sleep.

3. trigger your Soft Interrupt:

After adding a new entry to the list of enumeration types and calling open_softirq () for registration, the new soft interrupt handler can run. The raise_softirq () function can set a soft interrupt to a pending state, so that it will be put into operation when the next call to the do_softirq () function. Example:

Raise_softirq (net_tx_softirq );

This will trigger the net_tx_softirq Soft Interrupt. Its handler net_tx_action () will be put into operation during the next Soft Interrupt of the kernel. This function must disable the interrupt before triggering a Soft Interrupt, and then return to the original state after triggering. Triggering Soft Interrupt In the interrupt handler is the most common form. In this way, the kernel will call do_softirq immediately after the interrupt processing program is executed. As a result, the Soft Interrupt begins to execute the interrupt processing program and leaves it to complete the remaining tasks.

 

6.3 tasklet

Tasklet is a lower half Mechanism Implemented by Soft Interrupt. It has nothing to do with the process. It is essentially similar to Soft Interrupt, and its behavior is similar. However, its interface is simpler and its lock protection is also less demanding.

How can I choose Soft Interrupt and tasklet?

Generally, you should use tasklet. Soft Interrupt is rarely used. It is only required when the execution frequency is high and the continuity requirement is high, but tasklet is more widely used.

 

6.3.1 Implementation of tasklet

Tasklet is implemented through Soft Interrupt, so it is also Soft Interrupt.

(1) tasklet structure: tasklet is represented by the tasklet_struct structure. Each struct represents a tasklet, which is defined in <Linux/interrupt. h>:

Struct tasklet_struct {

Struct task_struct * Next;/* points to the next tasklet in the linked list */

Unsigned long state;/* tasklet state */

Atomic_t count;/* Reference Counter */

Void (* func) (unsigned long);/* tasklet processing function */

Unsigned long data;/* parameters for the tasklet processing function */

};

The func member in the struct is the processing program of tasklet, and data is its unique parameter. The State member can only be set between 0, tasklet_state_sched, and tasklet_state_run. Tasklet_state_sched indicates that the tasklet has been scheduled and is ready for running. tasklet_state_run indicates that the tasklet is running. Tasklet is activated only when count is 0. Otherwise, it is allowed and cannot be executed.

 

Schedule tasklet

The scheduled tasklet is stored in two single-processor data structures: tasklet_vec and task_hi_vec. They are all linked lists composed of tasklet_struct struct. Each tasklet_struct in the linked list represents a different tasklet.

Tasklet is scheduled by the tasklet_schedule () and tasklet_hi_schedule () functions. They accept a pointer to the tasklet_struct structure as a parameter.

Tasklet implementation is implemented through Soft Interrupt. The tasklet_schedule () scheduling function executes some initial work, and then invokes the tasklet_softirq or hi_softirq Soft Interrupt, so that the tasklet will be executed the next time do_softirq () is called.

So when will the do_softirq () function be executed?

Do_softirq () will be executed at the next appropriate time as early as possible, because most tasklets and soft interrupts are set to the pending status in the interrupt handler, therefore, the return of the last interrupt seems to be the best time to execute do_softirq. Because tasklet_softirq and hi_softirq have been triggered, do_softirq executes the corresponding Soft Interrupt Processing Program.

Tasklet_action () and tasklet_hi_action () are the core of tasklet processing.

Conclusion: All tasklets are implemented through repeated use of tasklet_softirq or hi_softirq. When a tasklet is scheduled, the kernel will invoke one of the two soft interrupts, this Soft Interrupt will be processed by specific functions, and all the scheduled tasklets will be executed, this function ensures that only one tasklet of a given category is executed at a time (but other tasklets of different types can be executed at the same time). All these complexities are hidden by a simple interface.

 

6.3.2 use tasklet

Declare your own tasklet

It can be created either statically or dynamically, corresponding to direct reference and indirect reference respectively. The method you choose depends on whether you have (or want) a direct or indirect reference to tasklet, and create a tasklet (that is, a direct reference) statically ), use the following <Linux/interrupt. h> one of the two macros defined in:

 

Declare_tasklet (name, func, data)

Implement a tasklet with the definition name "name" and bind it to the func function. The parameter passed in to this function is data.

Declare_tasklet_disabled (name, func, data );

Declare_tasklet (my_tasklet, my_tasklet_handler, Dev); The Running code is actually equivalent:

Struct tasklet_struct my_tasklet = {null, 0, atomic_init (0), my_tasklet_handler, Dev}; A tasklet named my_tasklet is created, and the handler is tasklet_handler and activated.

You can also assign an indirect reference (a pointer) to a dynamically created tasklet_struct structure to initialize a tasklet:

Tasklet_init (T, tasklet_handler, Dev);/* dynamic instead of static creation */

 

Compile your own tasklet Handler

Required function types:

Void tasklet_handler (unsigned long data)

Tasklet cannot sleep because it is implemented by software interruption, which means you cannot use semaphores or other blocking functions in tasklet.

 

Schedule Your Own tasklet

By calling the task_schedule () function and passing it to its corresponding tasklet_struct pointer, The tasklet will be scheduled for execution.

Tasklet_schedule (& my_tasklet);/* mark my_tasklet as suspended */

 

Let's take a look at the similarities and differences between Soft Interrupt and tasklet:

In preparation, you must first assign an index to the soft interrupt, while tasklet should use a macro to declare the handler. After assigning an index to a soft interrupt, you must use the open_softirq () function to register the handler. In this case, tasklet is a one-step processing function, while Soft Interrupt requires more work. Next, the Soft Interrupt will wait for triggering (raise_softirq () or raise_softirq_irqoff), while the tasklet will wait for tasklet_schedule () and tasklet_hi_schedule () to schedule it. Although the two are named differently, the final result is to wait for do_softirq () to execute the processing function. The lower half is set to the pending status for execution later. In addition, in tasklet's tasklet_schedule (), one of the actions that need to be done is to call (trigger) the Soft Interrupt of tasklet_softirq or hi_softirq, which means that the tasklet is still based on Soft Interrupt. After entering do_softirq (), the work is still different and will not be discussed.

Both Soft Interrupt and work queue occur asynchronously (that is, when the Interrupt Processing returns)

 

6.4 working queue

 

The work queue is another form of pushing jobs back and executing them. It is totally different from other forms we have discussed earlier. The work queue can push the work to a kernel thread for execution-the lower half is always executed in the process context. In this way, the Code executed through the work queue can take all the advantages of the process context. The most important thing is that the work queue allows rescheduling or even sleep.

If you need an entity that can be rescheduled to execute your lower half of the processing, you should use the Working queue, which is the only mechanism that can be implemented in the lower half of the process context, this means that when you need to obtain a large amount of memory, when you need to obtain a semaphore, when you need to perform a blocking Io operation, it will be very useful. If you don't need to use a kernel thread to push and execute the job, consider using tasklet!

 

6.4.1 implementation of work queue

The working queue subsystem is an interface used to create a kernel thread. The process created through it is responsible for executing other tasks in the queue from other parts of the kernel. The kernel threads it creates are called worker threads. The working queue allows the driver to create a dedicated worker thread to process jobs that need to be pushed back. However, the work queue subsystem provides a default worker thread to handle these jobs. Therefore, the most basic form of expression in the work queue is to give the task to a specific universal thread.

 

Indicates the data structure of the thread.

The worker thread is represented in the workqueue_struct structure:

Struct workqueue_struct {

Struct cpu_workqueue_struct cpu_wq [nr_cpus];

Const char * Name;

Struct list_head list;

};

This structure is an array consisting of the cpu_workqueue_struct structure, defined in kernel/workqueue. C, each item of the array corresponds to a processor in the system. Each worker thread corresponds to such a cpu_workqueue_struct struct. Cpu_workqueue_struct is the core data structure in kernel/workqueue. C:

Struct cpu_workqueue_struct {

Spinlock_t lock;/* Lock to protect the struct */

Long romove_sequeue;/* The Last One added (the next one to be run )*/

Long insert_sequeue;/* next to add */

Wait_queue_head_t more_work;

Wait_queue_head_t work_done;

Struct workqueue_struct * WQ;/* associated workqueue_struct structure */

Task_t * thread;/* associated thread */

Int run_depth;/* run_workqueue () loop depth */

};

It can be seen that each worker thread type is associated with a workqueue_struct of its own. In this struct, assign a cpu_workqueue_struct to each thread, that is, assign a cpu_workqueue_struct to each processor because each processor has a thread of this type.

Indicates the working Data Structure

All worker threads are implemented using common kernel threads, and they all need to execute the worker_thread () function. After initialization, this function (worker_thread) starts to sleep. When an operation is inserted into the queue, the thread is awakened to execute these operations. When there are no remaining operations, it will continue to sleep.

Work is represented by the work_struct struct defined in <Linux/workqueue. h>:

Struct work_struct {

Unsigned long pending;/* whether the job is waiting for processing */

Struct list_head entry;/* l connects all working linked lists */

Void (* func) (void *);/* processing function */

Void * wq_data;/* internal use */

Struct timer_list timer;/* timer used by the delayed working queue */

};

These struct are connected to a linked list, and each type of queue of each processor corresponds to such a linked list. When a worker thread is awakened, it executes all the work on its linked list. When the work is completed, the corresponding work_struct object will be removed from the linked list. When there is no object on the linked list, it will continue to sleep.

 

6.4.2 use a work queue

(1) Creation and push

The first thing to do is to create some work that needs to be pushed back for execution. You can use declare_work to statically create the struct during compilation:

Declare_work (name, void (* func) (void *), void * data );

In this way, a work_struct struct named "name" with the processing function func and parameter "data" will be created statically. You can also create a job by using a pointer at runtime:

Init_work (struct work_struct * Work, void (* func) (void *), void * data );

In this way, a job directed by work is initialized dynamically.

(2) processing functions of the work queue

Prototype: void work_handler (void * Data)

This function will be executed by a worker thread. Therefore, the function will run in the process context. By default, the function can respond to interruptions without holding any lock. If needed, the function can sleep, note that although the operation handler function runs in the process context, it cannot access the user space, because the kernel thread does not have relevant memory ing in the user space, usually when a system call occurs, the kernel will run on behalf of the user space process. At this time, it can access the user space, and only at this time it will map the user space memory.

(3) Schedule jobs

Now that the job has been created, we can schedule it. To submit the processing function of a given job to the default events working thread, we only need to call schedule_work (& work ); work will be scheduled immediately. Once the worker thread on its processor is awakened, It will be executed.

(4) Refresh operation

The function used to refresh the work queue is to ensure that some operations have been completed before the module is detached. This function is as follows:

Void flush_scheduled_work (void );

This function will wait until all the objects in the queue are executed before returning. When waiting for the execution of the work to be processed, the function will enter the sleep state, therefore, it can only be used in process context.

 

(5) create a new work queue

When the default queue cannot meet your needs, you should create a new work queue and the corresponding worker thread.

 

 

 

Select between the lower half of 6.5

1. From the design perspective

The Soft Interrupt provides the least guarantee for the execution of serialization, which requires the Soft Interrupt to take some steps to ensure the security of shared data. If the Code itself is tested to be multi-threaded, the work will be very good, and soft interruptions will be very good. If the time requirement is strict and the execution frequency is very high, it will be executed quickly. If the multi-clue work of the Code itself is not fully done, it is better to select tasklet. Because two tasklet of the same type cannot be executed at the same time, it is easier to implement it.

2. If you need to push the task to the process context, you can only select a work queue.

If you do not need to sleep, soft interruptions and work queues are more suitable. The maximum overhead of the work queue is because it involves kernel threads or even context switches.

3 when it comes to ease of use, it is best to use the default events queue for working queues. Next, let's talk about tasklet. Its interface is very simple. The last is Soft Interrupt. It must be created statically.

 

6.6 lock between lower half

One advantage of tasklet is that it is responsible for ensuring the serialization of the execution. Two identical tasklets cannot be executed simultaneously, even on different processors, this means that you do not need to consider the internal synchronization of tasklets of the same type. Of course, the lock mechanism must be correctly used for synchronization between tasklets (when two different types of tasklets share the same data.

Because soft interruptions do not guarantee the execution of serialization at all (even two instances may execute the same type of soft interruptions at the same time), all shared data requires a proper lock.

If the process context shares data with the lower half, you must disable the lower half of the process and obtain the right to use the lock before accessing the data, this is to protect local and SMP resources and prevent deadlocks.

If the interrupt context shares data with the lower half, you need to disable the interruption and get the right to use the lock before accessing the data. This is done to protect local and SMP resources and prevent deadlocks.

Any data shared in the work queue also requires a lock mechanism. The key points of the lock are no different from those in general kernel code, because the work queue is originally executed in the context of the process.

 

Disable lower half

Generally, it is not enough to simply disable the lower half. To ensure the security of shared data, a more common practice is to get a lock first and then process the lower half, this method is usually used in drivers.

Http://blog.163.com/stm32f103vct6@126/blog/static/166743479201142135637772/

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.