In-depth analysis of soft interrupts in the Linux kernel

  
 

Introduction to Soft Interrupts

Insert processing that can be deferred from the hard interrupt handler, so that this processing can be run with an interrupt, which is a soft interrupt. It can be seen that this detachment of soft interrupts can greatly reduce the response time of hard interrupts, which is important for many real-time applications.

We only talk about soft interrupts in this article. As for tasklet and workqueue, we will talk about it later. When we talk about the soft interrupt process (see linux kernel 4.0), we will try to understand the details and share our own understanding (if not, please also point out, thank you).





Soft interrupt data structure definition

Soft interrupt currently has 10 (defined by NR_SOFTIRQS), via softirq_vec[NR_SOFTIRQS] Arrays are used to manage these soft interrupts, all of which are shared by the CPU.

Registration of Soft Interrupts

Bind specific soft interrupt handlers and soft interrupt numbers by open_softirq(). For example, the network system registers the soft interrupt processing function of the transceiver packet:

open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action);

soft interrupt activation

each cpu There is a 32-bit bitmap (ie __softirq_pending) to maintain whether the soft interrupt on this cpu is active.

 typedef struct {unsigned int __softirq_pending; #ifdef CONFIG_SMP unsigned int ipi_irqs [NR_IPI]; #endif} ____cacheline_aligned irq_cpustat_irq_cpustat_t irq_stat [NR_CPUS] ____ cacheline_aligned; 

activating one soft interrupt time: irq_exit
< The soft interrupt may be activated in the p>irq_exit function. The activation condition is:

is not in the hard interrupt and is not in the soft interrupt and is set in the __softirq_pending of this cpu.

 if (in_interrupt () &! & local_softirq_pending ()) invoke_softirq (); 

From this condition, we can know, soft interrupts and hardware interrupts here is equal treatment (in in_interrupt Lane) The embodiment is the essence of interrupt handling. Conditions that cannot be in a hard interrupt indicate that priority must be given. Hard interrupts must be completely processed before soft interrupts are considered; conditions that cannot be in soft interrupts indicate that nesting of soft interrupts is masked.

The processing of the invoke_softirq function is either (wake up ksoftirqd first) to hand over the soft interrupt to the special thread of ksoftirqd, or directly call __do_softirq for immediate processing (of course, the instant processing is to distinguish which stack is on: the current The stack is still on a separate soft interrupt stack).

Let's take a look at this process in real time. Local_softirq_pending will definitely clear the hard interrupt bit in preempt_count. If there is no soft interrupt bit in preempt_count at this time, it can be preempted (immediately turn off the hard interrupt). During the process of entering __do_softirq to handle each soft interrupt, it is forbidden to preempt. Preemption in a hard (soft) interrupt context is well known and not allowed: it is unfair to make the execution time of the interrupted process undefined (that is, not to have a schedule to leave in the processing of hard and soft interrupts). Intention).

Software Interrupt Activation Timing 2: Raise_softirq

The NIC packet collection method has evolved from non-NAPI to NAPI mode, which fully demonstrates the advantages of soft interrupt: Maximize the reporting task to the maximum extent Soft interrupt handling minimizes hard interrupt handling. This evolution, we will talk about it later.

The raise_softirq function calls the __raise_softirq_irqoff function to set the corresponding soft interrupt on the __softirq_pending bitmap of the specified cpu. The difference between the raise_softirq_irqoff function and the raise_softirq function is whether the interrupt operation has been completed. The set bitmap is a contention operation that can be done in all hard interrupts, so it is guaranteed to be done in the event of an interrupt.

The activation of soft interrupts: ksoftirqd

Each cpu has a ksoftirqd thread that handles soft interrupts when the amount of soft interrupts is large:

DEFINE_PER_CPU(struct task_struct * , ksoftirqd); 

ksoftirqd thread core function run_ksoftirqd (loop) processing is: off interrupt to see the cpu __softirq_pending set, if any, execute __do_softirqd (), execute the open interrupt). This implementation is very smooth, because it is on the thread's own stack, there will be no problems affecting the user process.

There is a question here. Here used to be the preemption protection. Now it is the protection of the interrupt (refer to patch 3e339b, softirq: Use hotplugthread infrastructure in 2012)? Our understanding is: the protection of the preemption will make more subsequent soft interruptions handled by ksoftirqd, which does not meet the auxiliary status of ksoftirqd. In terms of dealing with the status of soft interrupts, it should be dominated by irq_exit, supplemented by ksoftirqd. )

Ksoftirqd can also be seen, can be preempted before executing soft interrupts, but can not be preempted once it starts executing (and one of the above schedules: the idea in irq_exit is consistent) of). That is to say, the processing idea of ​​soft interrupt and hard interrupt is consistent: scheduling is not allowed during execution!

The above reason for not being able to preempt is actually a principle similar to transactionality: once you start, you can't stop. Another reason is that the user-defined hard (soft) interrupt program is executed, and the operation is uncertain. If you have scheduling possibilities during these operations, it will be out of the control of the kernel.

Soft interrupt activation 4: Other places

For example, netif_rx_ni(), perform do_softirq pre-emption preemption, and cannot be scheduled during soft interrupt execution.

soft interrupt activation Five: local_bh_enable

 if (unlikely (in_interrupt () & & local_softirq_pending ())!) do_softirq (); 

Think about it, if exceptions and If the soft interrupt has shared data, the exception handling needs to turn off the soft interrupt when going to the critical area of ​​the shared data, but it is not necessary to turn off the hard interrupt. Then when the critical section is finished, it needs to open a soft interrupt. At this time, it is an activation opportunity (see preempt_count, in fact, it may also be a preemption opportunity).

The reason for using “activation” instead of "call" is that the peripheral processing only modifies the __softirq_pending bitmap of this cpu, and finally the core mechanism (such as ksoftirqd, soft interrupt processing that can be checked by in_interrupt) The real deal, and this is the idea of ​​a soft interrupt: let the hard interrupt (or other) execute faster, so the direct call is not used.

“The principle of activating” is who activates, who handles which soft interrupt caused by the hard interrupt on the cpu is handled by which CPU (or, the attribution cpu is a soft interrupt followed by a hard interrupt) . In this way, give full play to the advantages of smp and balance to each cpu. As for the relationship between hard interrupts and CPUs, we will discuss them later when we talk about hard interrupts. Each cpu maintains its own soft interrupt mechanism, and each CPU is irrelevant. Note that there is still correlation: when each CPU handles the same type of soft interrupt in parallel, this type of soft interrupt processing needs to protect the shared data, which is the cost of soft interrupt reentrancy.

Soft interrupt core function processing do_softirq

do_softirq first check the soft interrupt re-entry condition: must not be in the hard interrupt and not in the soft interrupt, after the conditions are met, you can start the following soft interrupt dealt with:

 pending = local_softirq_pending (); if (pending) __do_softirq (); 

this process is done under the protection of customs interrupted, after all, hard and soft interrupt interrupt essentially the same, It's all interrupted (of course, it's a different matter to go inside the hard/soft interrupt). It can also be seen that the local variable pending is not passed inside __do_softirq, so here is only the judgment, not the use, the judgment value and the internal use value may be different here, and the number of digits in the bitmap will be less.

Let's take a closer look at this check condition. Our understanding is:

This condition achieves two effects: soft interrupts on the same cpu are not nested; soft interrupts are not handled in nested hard interrupts. For the same cpu, the execution of the __do_softirq function is serial, non-reentrant (do_softirq function can be said to be reentrant); for multiple cpu, the __do_softirq function is reentrant, even if it is the same A type of soft interrupt. That is to say, the soft interrupt achieves the serialization of the soft interrupt processing on the CPU through this check condition. Of course, the multiple CPUs are still parallel, so the same type of soft interrupt processing still needs to protect its own shared data structure. .

Soft interrupt core function processing __do_softirq

__do_softirq function processing is to try to execute all activated soft interrupts (although it may still be executed) (identified by __softirq_pending bitmap on this cpu) )deal with. We analyze in three stages.

Preparation processing phase: Turn off the soft interrupt (the effect is to make the above mentioned check condition true, thus achieving the purpose of prohibiting soft interrupt nesting on this CPU).

Core processing stage: Turn off the hard interrupt, get the __softirq_pending bitmap of this cpu and store it, clear the bitmap, open the hard interrupt (only need to turn off the hard interrupt when reading and writing the bitmap, prevent other hard interrupts) Simultaneous operation). Execute all soft interrupts of this cpu (obtained from the stored bitmap). This core processing is a loop, up to 10 times (MAX_SOFTIRQ_RESTART), after all, the stack of the user process is used at this time, and can not be borrowed for too long. The condition for exiting the loop is that the total time is exceeded or preempted (the interrupt will be preempted) or up to 10 times.

End processing stage: turn off hard interrupt, open soft interrupt.

In addition, if 10 cycles complete the soft interrupt, it means that there are many hard interrupts during the period, and there are many additional soft interrupts. Then it will not continue to affect the borrowed user process stack, and directly hand it to the special ksoftirqd kernel thread for processing. This also explains the meaning of the loop: when processing the soft interrupt period, it will also enter a new hard interrupt, thus bringing in a new soft interrupt (of course, just set on the __softirq_pending of this cpu, there will be no actual processing) , so you need to deal with it repeatedly (the goal of processing is very clear, is to empty the __softirq_pending bitmap on this cpu).

Look again at the process of preventing soft interrupt nesting. In the soft interrupt, there must be a key statement that adds 1 to the atom. If the current kernel path A is interrupted by another kernel path B before the atomic operation, then B performs a hard interrupt and a soft interrupt, and then returns to A. At that point, A then performs the atomic operation, and the subsequent soft interrupt processing should be idling because it must have been processed by B. If B is interrupted after the atomic operation, B executes a hard interrupt, does not execute its own soft interrupt but instead exits directly (because the soft interrupt is nested), returns to A here, and A executes. This time, in addition to handling its own soft interrupt, A will additionally handle B's soft interrupt.

For the soft interrupt bit in preempt_count, as can be seen from the above, it has two functions: to prevent soft interrupts from being nested on a single cpu; to ensure that they are not preempted during the execution of a soft interrupt.

Finally, I have to repeat: The __do_softirq function mentioned here is handled on a cpu, and the parallelism on multiple cpus is not subject to any control.

Summary

The timing of interrupts seems complicated, but in fact they can't escape two principles: hard interrupts will interrupt hard interrupts (of course different types); hard interrupts will interrupt Soft interrupts (again: soft interrupts do not interrupt hard interrupts, soft interrupts do not interrupt soft interrupts). All seemingly complicated timings are just superpositions of these two.

Copyright © Windows knowledge All Rights Reserved