Bottom half mechanism analysis: soft interrupt, Tasklet, Work queue

Source: Internet
Author: User
Tags bitmask goto

In an interrupted system, the interrupt ISR should be designed to be as small as possible and, when processing interrupts, it is not allowed to interrupt the ISR and be interrupted by other subsequent interrupts, that is, to avoid interruption of nesting. Now most systems do not support interrupt nesting, Linux implementation is a typical. The way to prevent an interrupt nesting is to handle an interrupt when CPU execution is interrupted and no other interrupts are received. But this kind of interruption state can not last too long, close the interruption time is too long, it can also cause subsequent interrupts to be lost, so Linux divides the interrupt handler into two parts, the upper and lower halves, the upper part usually takes a short time, and most of it is closely related to hardware, so it needs to run in a disconnected environment. The lower half handles some of the more time-consuming operations, which are performed in the Open interrupt section. Generally called the upper half of the hard interrupt, the lower half for the soft interrupt, Linux design, soft interrupts can only be interrupted by a hard interrupt. Soft Interrupt

In the 2.6 kernel, the design of soft interrupts has always been a thought: "Who triggers, who executes (who marks, who runs)", so can effectively use the performance of SMP system, improve processing efficiency. 2.4 and previous versions used a mechanism called bottom half to implement the lower half, its fatal disadvantage is that the system can have a CPU at a time to execute BH code, so in a single core CPU is not a problem, but in the SMP system, the heavy loss of hardware performance.

2.6.34 Soft Interrupt Implementation

Soft Interrupt Request description in Include/linux/interrupt.h:

struct softirq_action
{
    void (*action) (struct softirq_action *);
It can be seen that in 2.6.34, 10 types of soft interrupts are defined

static struct softirq_action Softirq_vec[nr_softirqs] __cacheline_aligned_in_smp;
Enum
{
    hi_softirq=0,/* for high-priority tasklet*/
    TIMER_SOFTIRQ,/* for the lower half of the timer * * *
    NET_TX_SOFTIRQ,/* For the network layer contract/
    NET_RX_SOFTIRQ,/* For the network layer of the closed * *
    BLOCK_SOFTIRQ,
    Block_iopoll_softirq,
    TASKLET_SOFTIRQ,/* For low priority tasklet*/
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,/* Preferable RCU should always is the last SOFTIRQ * * Nr_softirqs
};

For soft interrupts, Linux is executed in an interrupt handler, with the following specific path:

DO_IRQ ()->irq_exit ()->invoke_softirq ()->do_softirq ()->__do_softirq ()

/* Read the soft interrupt mask for the local CPU and perform the delay function associated with each set bit, __DO_SOFTIRQ only do a fixed number of loops and then return.
    If there are other pending soft interrupts, then kernel thread KSOFIRQD will process them within the expected time/asmlinkage void __do_softirq (void) {struct softirq_action *h;
    __U32 pending;
    /* Initializes the value of the loop counter to 10*/int max_restart = Max_softirq_restart;
 
    int CPU;
    * * Copy the local CPU (local_softirq_pending selected) software interrupt bitmask to local variable pending/pending = Local_softirq_pending ();
 
    Account_system_vtime (current);
    /* Increase the value of the soft interrupt counter/__local_bh_disable ((unsigned long) __builtin_return_address (0));
    Lockdep_softirq_enter ();
 
CPU = smp_processor_id (); Restart: * Reset the pending bitmask before enabling IRQs/set_softirq_pending (0);
    /* Clear the soft interrupt bitmap of the local CPU so that it can activate the new soft interrupt/* Activate the local interrupt/* local_irq_enable ();
            h = softirq_vec;//h The global soft interrupt vector table do {/* performs the corresponding soft interrupt processing function according to the pending per bit setting/if (pending & 1) {
            int prev_count = Preempt_count ();
            KSTAT_INCR_SOFTIRQS_THIS_CPU (H-softirq_vec);
   Trace_softirq_entry (H, Softirq_vec);         H->action (h);
            /* The specific soft interrupt function to perform registration/trace_softirq_exit (h, Softirq_vec);
                if (Unlikely (Prev_count!= Preempt_count ())) {PRINTK (kern_err "Huh, entered Softirq%td%s%p" "With Preempt_count%08x," "exited with%08x?\n", H-softirq_vec, softirq_to_name[
                H-softirq_vec], h->action, Prev_count, Preempt_count ());
            Preempt_count () = Prev_count;
         } rcu_bh_qs (CPU);
        } h++;
 
    Pending >>= 1;//handles a variety of soft interrupts by priority while (pending);
 
    Local_irq_disable ();
    /* up to 10 times/* pending = Local_softirq_pending ();
 
    if (pending &&--max_restart) goto restart;
 
    if (pending)/* If there is a soft interrupt pending, wake the kernel thread to handle the soft interrupt of the local CPU/WAKEUP_SOFTIRQD ();
 
    Lockdep_softirq_exit ();
 
    Account_system_vtime (current); _local_bh_enable ()/* Soft Interrupt counter-1, thus reactivating the delay function/}

Soft Interrupt use:

Open_softirq (): interrupt registration. Opens a specified soft interrupt vector nr, initializing the corresponding descriptor for NR SOFTIRQ_VEC[NR].

Raise_softirq (): interrupt trigger.

The soft interrupt mechanism also has a kernel thread ksoftirqd, which is why this thread is used, and the annotation at one end of the kernel/softirq.c is clearly stated. To balance system load only

 
/
 * We cannot loop indefinitely avoid userspace starvation,
 * But we also don ' t want to introduce a worst Case 1/hz Latency
 * to the pending events, so lets the scheduler to balance
 * The SOFTIRQ load for us.
 */

If the system keeps triggering a soft interrupt request, the CPU will always handle the soft interrupt, because at least every time the clock interrupts (TIMER_SOFTIRQ) performs a DO_SOFTIRQ (), so that other important tasks in the system are not CPU-hungry, So add a small thread and put too many soft interrupt requests into the system when the time period executes.

The general process of interrupting triggers to wake kernel thread ksoftirqd to handle interrupts is this:

void Raise_softirq (unsigned int nr)
{
    unsigned long flags;
    Local_irq_save (flags);
    Raise_softirq_irqoff (NR);
    Local_irq_restore (flags);
}

inline void Raise_softirq_irqoff (unsigned int nr)
{
    __raise_softirq_irqoff (NR);
    if (!in_interrupt ())
        wakeup_softirqd ();//wake kernel Interrupt processing thread
}

#define __raise_softirq_irqoff (NR) do {or_softirq_pending (1UL << (NR)), while (0)
#define Or_softirq_ Pending (x) (Local_softirq_pending () |= (x))
#define local_softirq_pending () \
        __irq_stat (smp_processor_id (), __softirq_pending)
#define __IRQ_STAT (CPU, member) (Irq_stat[cpu].member)

The data structure to manipulate is here irq_cpustat_t:/*arch/xxx/include/asm/hardirq.h*/

typedef struct {

    unsigned int __softirq_pending;
    unsigned int __nmi_count; /* Arch dependent *
    /unsigned int irq0_irqs;
#ifdef config_x86_local_apic
    unsigned int apic_timer_irqs;/* Arch dependent * *
    unsigned int irq_spurious_count ;
#endif
    unsigned int x86_platform_ipis/* Arch dependent *
    unsigned int apic_perf_irqs;
    unsigned int apic_pending_irqs;
#ifdef CONFIG_SMP
    unsigned int irq_resched_count;
    unsigned int irq_call_count;
    unsigned int irq_tlb_count;
#endif
#ifdef config_x86_thermal_vector
    unsigned int irq_thermal_count;
#endif
#ifdef config_x86_mce_threshold
    unsigned int irq_threshold_count;
#endif
} ____cacheline_aligned irq_cpustat_t;

All that is done above is in the irq_cpustat_t structure that belongs to this processor, the Operation __softirq_pending Flag, RAISE_SOFTIRQ () is the corresponding position in the __softirq_pending sign, Then determine if the system is currently in an interrupt context, and if not, immediately wake the kernel soft interrupt processing thread KSOFTIRQD to handle the soft interrupt that was just triggered.

The implementation of the KSOFTIRQD thread in the ksoftirqd thread 2.6.34 is this:

static int run_ksoftirqd (void * __bind_cpu) {/* Set process status to Interruptible/Set_current_state (task_interruptible);
        while (!kthread_should_stop ()) {/* should not immediately return/preempt_disable (); /* A key data structure in the implementation of soft interrupts is a 32-bit mask for each CPU (describing a suspended soft interrupt), which he holds in the __softirq_pending field of the IRQ_CPUSTAT_T data structure. In order to get or set the value of the bitmask, the kernel uses the macro local_softirq_pending, and he chooses the soft interrupt of the CPU as the Mask/if (!local_softirq_pending ()) {* * bit mask is 0, indicating no soft interrupt
            * * preempt_enable_no_resched ();
            Schedule ();
        Preempt_disable ();
        } __set_current_state (task_running);
             while (Local_softirq_pending ()) {/* Preempt disable stops CPU going offline. If already offline, we ' ll be on wrong Cpu:don ' t process/if (Cpu_is_offline (long) __bind_cpu
 
            ) goto Wait_to_die;
            DO_SOFTIRQ ()/* Call soft interrupt processing function/preempt_enable_no_resched ();
            Cond_resched ();
            Preempt_disable (); Rcu_sched_qs (LONG) __bind_cpu);
        } preempt_enable ();
    Set_current_state (task_interruptible);
 
    } __set_current_state (task_running);
 
return 0;
    Wait_to_die:preempt_enable ();
    /* Wait for Kthread_stop * * Set_current_state (task_interruptible);
        while (!kthread_should_stop ()) {schedule ();
    Set_current_state (task_interruptible);
    } __set_current_state (task_running);
return 0; }

Tasklet

The essence of Tasklet is also soft interrupt, soft interrupt vector HI_SOFTIRQ and TASKLET_SOFTIRQ are all realized by tasklet mechanism. Its characteristics are: 1, different Tasklet code at the same time can be executed in parallel on multiple CPUs; 2, compared with soft interrupts, the same section of Tasklet code can only run on one CPU at a time, while the interrupt service function registered in soft interrupts can run on multiple CPUs at the same time.

Tasklet Description tasklet_struct:/*include/linux/interrupt.h*/

struct tasklet_struct
{
    struct tasklet_struct *next;
    unsigned long state;
    atomic_t count;
    void (*func) (unsigned long);
    unsigned long data;

The next pointer points to the next tasklet, which is used to connect multiple tasklet into a one-way circular list.

State defines the current status of the Tasklet, which is a 32-bit unsigned integer, but currently uses only bit 0 and bit 1,bit 0 1 to indicate that Tasklet has been scheduled to execute, while bit 1 is specifically set for the SMP system. 1 indicates that Tasklet is currently executing on a CPU in order to prevent multiple CPUs from executing a tasklet at the same time.

Enum
{
    tasklet_state_sched,/* Tasklet is scheduled for execution * * * Tasklet_state_run is Tasklet
    (SMP only) */
};

Count is an atomic count of Tasklet references, Count 0 o'clock, Tasklet code snippet to execute, if not 0, the tasklet is prohibited, so you must first check to see if the count is 0 before executing the Tasklet code.

Soft interrupt Hi_softirq and TASKLET_SOFTIRQ of the implementation of similar, but is not the same priority, the following to pick the high priority of the Tasklet implementation to walk the process:

static void Tasklet_hi_action (struct softirq_action *a) {struct tasklet_struct;
    /* Critical area to get the current CPU high priority Tasklet Task 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; /* lock, which is directly passed in a non-SMP system, in an SMP system, if the other CPUs are running this Tasklet code, the CPU will skip the */if (Tasklet_trylock (t)) {/* Only the reference count is 0, indicating This tasklet is enabled in order to continue to execute/if (!atomic_read (&t->count)) {//If to this extent, this tasklet state is still being dispatched, then ask
                * * IF (!test_and_clear_bit (tasklet_state_sched, &t->state)) BUG ();
                /* Execute the specific processing function/T->func (t->data);
                Tasklet_unlock (t);
            Continue
        } tasklet_unlock (t); }
        /*Aftermath * * local_irq_disable ();
        T->next = NULL;
        *__get_cpu_var (Tasklet_hi_vec). Tail = t;
        __get_cpu_var (Tasklet_hi_vec). Tail = & (T->next);
        * This is very interesting, and then trigger the HI_SOFTIRQ soft interrupt, which will be handled in the kernel soft interrupt processing thread/__raise_softirq_irqoff (HI_SOFTIRQ);
    Local_irq_enable (); }
}

There's nothing more to say. workqueue (2.6.34) Basic term: workqueue: All Work items (work that needs to be performed) are arranged in the queue. worker thread: is a kernel thread that is used to perform individual work items in Workqueue, which becomes a idle state when there are no work items in Workqueue. Single threaded (ST): One manifestation of worker thread, in system-wide, only one worker thread for workqueue service. Multi Threaded (MT): One of the manifestations of worker thread, a worker thread on each CPU on a multiple-CPU system is a workqueue service.

The workqueue mechanism is defined and implemented in Include/linux/workqueue.h and kernel/workqueue.c.

The Task Force columns are maintained by the WORKQUEUE_STRUCT structure and are defined as follows:

struct Workqueue_struct {
    struct cpu_workqueue_struct *cpu_wq;
    struct List_head list;
    const char *name;
    int singlethread;
    int freezeable; /* Freeze Threads during suspend *
    /int rt;
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

The CPU_WORKQUEUE_STRUCT structure is defined for each CPU. For each CPU, the kernel hooks up a work queue for it, so that the new work can be dynamically placed into the working queues under different CPUs to support load balancing.
struct Cpu_workqueue_struct {
    spinlock_t lock;/* Structure lock/*
    struct List_head worklist;/* Work list/*
    Wait_queue_ head_t more_work; /* Waiting queue to be processed
    /struct work_struct *current_work//* Processed waiting queue/
    struct workqueue_struct *wq;/* Task Force Column node
    /* struct Task_struct *thread; /* Worker thread * *
____cacheline_aligned;

Work Item Definition
struct Work_struct {
    atomic_long_t data;
    struct List_head entry;
    work_func_t func; /* Task Force column function pointers, pointing to the specific work to be handled/* *
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

Creation of work Items

Static Declare_work (n, f) declare_delayed_work (n, F)

Dynamic init_work (struct work_struct WORK, work_func_t func); Prepare_work (struct work_struct WORK, work_func_t func); Init_delayed_work (struct delayed_work WORK, work_func_t func); Prepare_delayed_work (struct delayed_work WORK, work_func_t func);

Kernel default global Work Queue Keventd_wq, located in kernel/workqueue.c

720 static struct workqueue_struct *keventd_wq __read_mostly;
1177 Keventd_wq = Create_workqueue ("events");

The work item joins the KEVENTD_WQ interface:
int schedule_work (struct work_struct *work)
{return
    queue_work (KEVENTD_WQ, work);
}
 
int schedule_delayed_work (struct delayed_work *dwork, unsigned long delay)
{return
    queue_delayed_work ( KEVENTD_WQ, dwork, delay);

user Custom work Queues Create:Create_singlethread_workqueue (name)//corresponds to only one kernel thread create_workqueue (name)//corresponding to multiple kernel threads, as above. Submit:int Queue_work (workqueue_t *queue, work_t *work); int Queue_delayed_work (workqueue_t *queue, work_t *work, unsigned long delay);

An example WQ.C:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct workqueue_struct *queue = NULL;
static struct work_struct work;
 
static void Work_handler (struct work_struct *data) {PRINTK Kern_alert work handler for
    Work_item in queue Test_ WQ \ n ");
    Each work in/*workqueue is removed workqueue.*/
}
 
static int __init wq_init (void)
{
    * * Create a Work queue */
    queue = Create_singlethread_workqueue ("Test_wq");
    if (!queue)
    {
        goto err;
    }
 
    /* Create Work Item * *
    init_work (&work, work_handler);
    /* Mount work items to the work queue
    /queue_work (queue, &work);
    return 0;
 
ERR:
    return-1;
}
 
static void __exit wq_exit (void)
{
    destroy_workqueue (queue);
}
Module_license ("GPL");
Module_init (wq_init);
Module_exit (Wq_exit);

Makefile
Obj-m: = wq.o
kernelbuild: =/lib/modules/$ (Shell uname-r)/build
default:
    make-c $ (kernelbuild) m=$ (shell PWD) Modules Clean
:
    rm-rf *.o *.ko *.mod.c. *.cmd *.markers. *.order *.symvers

Let's summarize it briefly:

Soft interrupts:

1. Soft interrupts are statically allocated during compilation.

2, can have a maximum of 32 soft interrupts, 2.6.34 used 10.

3, soft interrupt will not preempt another soft interrupt, the only way to preempt the soft interrupt is interrupt handler (ISR).

4, can run concurrently on multiple CPUs, must be designed as a reentrant function, need to use spin lock to protect its data structure.

6. Execution time: From the time the hardware interrupt code is returned, in the KSOFTIRQD kernel thread, and in some of the code that shows the check and execution of the soft interrupt. Tasklet:

1, Tasklet is implemented using two kinds of soft interrupts: Hi_softirq and TASKLET_SOFTIRQ.

2, can increase the dynamic decrease, there is no quantity limit.

3, the same class of tasklet can not be executed concurrently.

4, different types can be executed concurrently, the same type cannot be concurrent.

5, most of the use of Tasklet. Task Force columns:

1, in the process context, by the kernel thread to execute.

2, can sleep, obstruction.

Through the lower half of the mechanism of three implementation analysis (Softirq,tasklet,workqueue), in the specific need to use when it is no longer puzzled, need sleep, there are blocking, can only work queue, and then select Tasklet, direct use of soft interruption of the opportunity is relatively low bar, Generally, when you need to improve performance, the focus of using soft interrupts is how to take effective measures to ensure the security of shared data. Because soft interrupts are executed concurrently on multiple CPUs.

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.