Mastering the Linux kernel Design (iv): The tasklet of the lower half mechanism

Source: Internet
Author: User

" copyright notice: respect for the original, reproduced please retain the source: Blog.csdn.net/shallnet, the article is only for learning exchange, do not use for commercial path "
         tasklet is a lower-half mechanism implemented with soft interrupts. compared to soft interrupts, the Tasklet interface is more simple and convenient, and the lock protection requirements are low. Tasklet is represented by the TASKLET_STRUCT structure:
struct tasklet_struct{    struct tasklet_struct *next;    The next Tasklet unsigned long state in the list    ;        Tasklet status    atomic_t count;        Reference count    Void (*func) (unsigned long);    Tasklet processing function    unsigned long data;    Arguments to the Tasklet processing function};
Tasklet is also divided into high-priority tasklet and general Tasklet, the previous analysis of soft interrupts softirq_init () registered two Tasklet soft interrupts.
void __init softirq_init (void) {    ...    Register here for two soft interrupt    Open_softirq (TASKLET_SOFTIRQ, tasklet_action);    OPEN_SOFTIRQ (HI_SOFTIRQ, tasklet_hi_action);    ......}
its processing functions are tasklet_action () and tasklet_hi_action().

the Tasklet_action () function is implemented as:
static void Tasklet_action (struct softirq_action *a) {struct tasklet_struct *list;    Local_irq_disable ();    List = __get_cpu_var (Tasklet_vec). Head;    __get_cpu_var (Tasklet_vec). Head = NULL;    __get_cpu_var (Tasklet_vec). Tail = &__get_cpu_var (Tasklet_vec). Head;    Local_irq_enable ();        while (list) {struct tasklet_struct *t = list;        List = list->next;                if (Tasklet_trylock (t)) {if (!atomic_read (&t->count)) {//t->count is zero, the function in Task_struct is called                 if (!test_and_clear_bit (tasklet_state_sched, &t->state)) BUG ();    T->func (T->data);                The tasklet_state_sched flag is set to be traversed to the corresponding function Tasklet_unlock (t) on the linked list;            Continue        } tasklet_unlock (t);        } local_irq_disable ();        T->next = NULL;        *__get_cpu_var (Tasklet_vec). Tail = t;        __get_cpu_var (Tasklet_vec). Tail = & (T->next); __raise_softirq_irqoff (TASKLET_SOFTIRQ);    Local_irq_enable ();    The}}//tasklet_hi_action function implements a similar static void Tasklet_hi_action (struct softirq_action *a) {struct tasklet_struct *list;    Local_irq_disable ();    List = __get_cpu_var (Tasklet_hi_vec). Head;    __get_cpu_var (Tasklet_hi_vec). Head = NULL;    __get_cpu_var (Tasklet_hi_vec). Tail = &__get_cpu_var (Tasklet_hi_vec). Head;    Local_irq_enable ();        while (list) {struct tasklet_struct *t = list;        List = list->next; if (Tasklet_trylock (t)) {if (!atomic_read (&t->count)) {if (!test_and_clear_bit (tasklet_                State_sched, &t->state)) BUG ();                T->func (T->data);                Tasklet_unlock (t);            Continue        } tasklet_unlock (t);        } local_irq_disable ();        T->next = NULL;        *__get_cpu_var (Tasklet_hi_vec). Tail = t;        __get_cpu_var (Tasklet_hi_vec). Tail = & (T->next); __raise_softirq_irqoff(HI_SOFTIRQ);    Local_irq_enable (); }}
The Tasklet_hi_action function implements similar:
static void Tasklet_hi_action (struct softirq_action *a) {struct tasklet_struct *list;    Local_irq_disable ();    List = __get_cpu_var (Tasklet_hi_vec). Head;    __get_cpu_var (Tasklet_hi_vec). Head = NULL;    __get_cpu_var (Tasklet_hi_vec). Tail = &__get_cpu_var (Tasklet_hi_vec). Head;    Local_irq_enable ();        while (list) {struct tasklet_struct *t = list;        List = list->next; if (Tasklet_trylock (t)) {if (!atomic_read (&t->count)) {if (!test_and_clear_bit (tasklet_                State_sched, &t->state)) BUG ();                T->func (T->data);                Tasklet_unlock (t);            Continue        } tasklet_unlock (t);        } local_irq_disable ();        T->next = NULL;        *__get_cpu_var (Tasklet_hi_vec). Tail = t;        __get_cpu_var (Tasklet_hi_vec). Tail = & (T->next);        __raise_softirq_irqoff (HI_SOFTIRQ);    Local_irq_enable (); }}

These two functions mainly do the following actions:
1. Disable the interrupt and retrieve the Tasklet_vec or Tasklet_hi_vec linked list for the current processor.
2. Set the linked list on the current processor to NULL to achieve a clear effect.
3. Run the appropriate interrupt.
4. Loop through the list to get each tasklet to be processed.
5. If it is a multiprocessor system, check the Tasklet_state_run to see if the Tasklet is running on another processor. If it's running, then don't do it now, jump
go to the next tasklet to be processed.
6. If the current tasklet is not executing, set its state to Taskletlet_state_run so that no other processor can execute it.
7. Check if the count value is 0, and make sure that Tasklet is not banned. If Tasklet is forbidden, skip to the next pending Tasklet.
8. It is now possible to make sure that the tasklet is not executed elsewhere, and that we are set to execute state so that it will not be executed in other parts and the reference counter is 0, and now it is possible to execute the Tasklet handler.
9. Repeat the next tasklet until there are no remaining tasklets waiting to be processed.

Under normal circumstances, are used tasklet to achieve the lower half, Tasklet can be dynamically created, easy to use, fast execution speed. Let's take a look at how to create your own tasklet.
The first step is to declare your own tasklet. It can be created either statically or dynamically, depending on whether you want to have a direct or indirect reference to Tasklet. Static Creation method (direct reference), you can use one of the following two macros (defined in linux/interrupt.h):
Declare_tasklet (Name,func,data) declare_tasklet_disabled (name,func,data)
The implementations of these two macros are:
#define Declare_tasklet (Name, func, data) struct tasklet_struct name = {NULL, 0, atomic_init (0), func, data} #define DECL Are_tasklet_disabled (Name, func, data) struct tasklet_struct name = {NULL, 0, Atomic_init (1), Func, data}
The difference between the two macros is that the initial value of the reference counter is different, and the previous reference counter for the created Tasklet is set to 0 so that it is active, and the other one sets it to 1, which is in a suppressed state. Dynamic creation (indirect referencing) is as follows:
tasklet_init(t,tasklet_handler,dev);
Its implementation code is:
void Tasklet_init (struct tasklet_struct *t,          Void (*func) (unsigned long), unsigned Long data) {    t->next = NULL;    t->state = 0;    Atomic_set (&t->count, 0);    T->func = func;    T->data = data;} <br style= "Background-color:inherit;"/>
The second step is to write the Tasklet handler.
Tasklet Processing the function type is void Tasklet_handler (unsigned long data). because it is implemented by soft interrupts, Tasklet cannot hibernate, that is, it cannot use semaphores or any other blocking functions in Tasklet. Because the Tasklet runtime allows a response to be interrupted, prevention must be done if some amount of data is shared between the newly added tasklet and interrupt handlers. Two identical tasklet must not be executed at the same time, and if the newly added tasklet and other tasklet or soft interrupts share the data, proper lock protection is required.
The Third step is to dispatch your own tasklet.
        calledTasklet_schedule ()(orTasklet_hi_schedule ()) function, the Tasklet enters the pending state for execution. If there is an identical tasklet that has been dispatched before the opportunity has been run, it will still run only once. If this is already running, the new tasklet will be re-dispatched and run again. An optimization strategy is that a tasklet is always executed on the processor that dispatches it.
Call tasklet_disable () to suppress a specified tasklet, and if the Tasklet is currently executing, the function waits until it finishes executing and returns. Calling Tasklet_disable_nosync () is also forbidden, but it is not safe to wait for Tasklet to complete before returning, as it is impossible to estimate whether the Tasklet is still executing. Tasklet_enable () activates a tasklet. You can use the Tasklet_kill () function to remove a tasklet from a pending pair of columns. This function waits for the tasklet to complete before it is removed. Of course, there is nothing to stop the code from dispatching the Tasklet elsewhere. Because the function may cause hibernation, It is not used in the context of the interrupt.

Take a look at the function tasklet_schedule
static inline void tasklet_schedule (struct tasklet_struct *t) {//Check Tasklet status is Tasklet    _state_sched. If yes, the description Tasklet has been passed and the function is returned. if (!test_and_set_bit (tasklet_state_sched, &t->state)) <span style= "font-family: Microsoft Ya hei;" >          </span> __tasklet_schedule (t);} void __tasklet_schedule (struct tasklet_struct *t) {unsigned long flags;//saves the interrupt state and then disables local interrupts.    This ensures that the data on the processor is not messed up when executing the Tasklet code.    Local_irq_save (flags);//Add the tasklet that need to be dispatched to the Tasklet_vec list of each processor or the table header of the Task_hi_vec list.    T->next = NULL;    *__get_cpu_var (Tasklet_vec). Tail = t; __get_cpu_var (Tasklet_vec). Tail = & (T->next);//evoke TASKLET_SOFTIRQ or HI_SOFTIRQ soft interrupts, so that the next call to DO_SOFTIRQ ()    The Tasklet is executed when the    Raise_softirq_irqoff (TASKLET_SOFTIRQ);//recovery is interrupted to the original state and returned. Local_irq_restore (flags);} 

Tasklet_hi_schedule () the implementation details of the function are similar.

for soft interrupts, the kernel chooses a few special actual processing (usually when the interrupt handler returns ). The frequency at which the soft interrupt is triggered is sometimes good, and it may also repeat itself, which results in the process of user space not getting enough processor time because of starvation. At the same time, it is unacceptable to take a policy that does not immediately deal with repetitive triggering of soft interrupts.
         Kernel selected scenario is not immediately handle the re-triggered soft interrupt, as an improvement, when a large number of soft interrupts occur, the kernel will wake up a set of kernel threads to handle these loads. These threads run at the lowest priority level (Nice value is 19). This approach ensures that the user program does not deal with starvation when the soft break is heavily burdened. The corresponding, can also ensure that "excessive" soft interrupt will eventually be processed. Finally, on idle systems, this scheme also behaves well and soft interrupts are handled very quickly (because the memory threads that are stored are bound to be dispatched immediately). To ensure that as long as there are idle processors, they handle soft interrupts, so each processor is assigned one such thread. The names of all the threads are called ksoftirad/n, the difference being N, which corresponds to the number of the processor. Once the thread is initialized, it executes a dead loop like this:
for (;;) {    if (!softirq_pending (CPU))//softirq_pending () is responsible for discovering whether the soft interrupt        schedule () is pending;    No pending soft interrupts invoke the scheduler to select other executable processes to be put into operation    Set_current_state (task_running);    while (softirq_pending (CPU)) {        DO_SOFTIRQ ();//soft interrupt to be processed, KSOFTIRQ calls DO_SOFTIRQ () to handle him.        if (need_resched ())    ///If necessary, call the schedule function after each soft interrupt is completed to allow other important processes to get processing opportunity            Schedule ();    } When all the required actions are completed, the kernel thread sets itself to the task_interruptible state    set_current_state (task_interruptible);}

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

Mastering the Linux kernel Design (iv): The tasklet of the lower half mechanism

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.