The soft interrupts defined by the Linux kernel are as follows:
static struct Softirq_actionsoftirq_vec[nr_softirqs] __CACHELINE_ALIGNED_IN_SMP;
Char *softirq_to_name[nr_softirqs] = {
"HI", "TIMER", "Net_tx", "Net_rx", "block",
"Tasklet", "SCHED", "Hrtimer", "RCU"
};
The soft interrupts registered during kernel startup are as follows.
Its registration function is:
void Open_softirq (int nr, void (*action) (struct softirq_action *))
{
softirq_vec[nr].action= Action;
}
is actually initializing the globally defined array variable Softirq_actionsoftirq_vec[nr_softirqs].
Enum
{
Hi_softirq=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,/* Preferable RCU should always be the LASTSOFTIRQ * *
Nr_softirqs
}; The registration function is as follows:
OPEN_SOFTIRQ (hi_softirq,tasklet_hi_action);
OPEN_SOFTIRQ (TIMER_SOFTIRQ,RUN_TIMER_SOFTIRQ);
OPEN_SOFTIRQ (net_tx_softirq,net_tx_action);
OPEN_SOFTIRQ (net_rx_softirq,net_rx_action);
OPEN_SOFTIRQ (BLOCK_SOFTIRQ,BLK_DONE_SOFTIRQ);
OPEN_SOFTIRQ (tasklet_softirq,tasklet_action);
OPEN_SOFTIRQ (Sched_softirq,run_rebalance_domains);
OPEN_SOFTIRQ (HRTIMER_SOFTIRQ, RUN_HRTIMER_SOFTIRQ);
Each CPU in an IRQ has a structure
typedef struct {
Unsignedlong __softirq_pending;
} ____cacheline_aligned irq_cpustat_t; The interrupt handler function used to hold the current CPU for waiting.
As you can see in previous articles, function irq_exit (void) is invoked after each execution of the DO_IRQ () end of the system. In this function, the processing software is interrupted,
/*
*exit an interrupt. Process Softirqs if needed and possible:
*/
void Irq_exit (void)
{
Account_system_vtime (current);
Trace_hardirq_exit ();
Sub_preempt_count (Irq_exit_offset);
if (!in_interrupt () && local_softirq_pending ())//indicates that the processing software is interrupted without the interrupt environment.
INVOKE_SOFTIRQ ();//There is a software interrupt exists, then execute.
#ifdef CONFIG_NO_HZ
/*make sure that timer wheel updates are propagated * *
Rcu_irq_exit ();
if (Idle_cpu (smp_processor_id ()) &&!in_interrupt () &&!need_resched ())
Tick_nohz_stop_sched_tick (0);
#endif
Preempt_enable_no_resched ();
}
#ifdef __arch_irq_exit_irqs_disabled
# define INVOKE_SOFTIRQ () __DO_SOFTIRQ ()
#else
# define INVOKE_SOFTIRQ () DO_SOFTIRQ ()
#endif
its DO_SOFTIRQ () function is defined as follows: There is a similar logic in the function.
asmlinkage void Do_softirq (void)
{
__u32pending;
Unsignedlong flags;
if (In_interrupt ())
Return
Local_irq_save (flags);
pending= local_softirq_pending ()//There is a software interrupt pending and execution of the pending break.
if (pending)
__DO_SOFTIRQ ();
Local_irq_restore (flags);
}
#endif
In the processing software, the kernel defines a kernel thread to handle soft interrupt requests that cannot be processed in a timely manner. The function is:
static int ksoftirqd (void * __bind_cpu);
#define Max_softirq_restart 10
asmlinkage void __do_softirq (void)
{
---------------
h = softirq_vec;
do{
if (pending & 1) {
Intprev_count = Preempt_count ();
KSTAT_INCR_SOFTIRQS_THIS_CPU (H-softirq_vec);
Trace_softirq_entry (H,softirq_vec);
h->action (h);
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_qsctr_inc (CPU);
}
h++;
pending >>= 1;
}while (pending);
Local_irq_disable ();
pending =local_softirq_pending ();
if (pending &&--max_restart)
goto Restart;
if (pending)
wakeup_softirqd ();
Lockdep_softirq_exit ();
Account_system_vtime (current);
_local_bh_enable ();
}
In the __DO_SOFTIRQ () function, handle a pending soft interrupt, with the pending bit to determine if there is an interrupt suspend.
When it reaches (max_restart==0), the pending is not 0. Called the function wakeup_softirqd ();
the creation of kernel threads can refer to other articles.
Switch (action) {
Casecpu_up_prepare:
Casecpu_up_prepare_frozen:
p = kthread_create (KSOFTIRQD, Hcpu, "ksoftirqd/%d", hotcpu);
Tasklet
struct TASKLET_STRUCT
{
Structtasklet_struct *next;
Unsignedlong State;
Atomic_tcount;
void (*func) (unsigned long);
Unsignedlong data;
};
There are two types of tasklet in the Linux kernel,
Its tasklet initialization is as follows:
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;
}
After the Tasklet is initialized, it is added to the Tasklet queue.
the lower priority Tasklet scheduling functions are as follows:
static inline void Tasklet_schedule (structtasklet_struct *t)
{
if (!test_and_set_bit (tasklet_state_sched, &t->state))
__tasklet_schedule (t);
}
void __tasklet_schedule (Structtasklet_struct *t)
{
Unsignedlong flags;
Local_irq_save (flags);
T->next= NULL;
*__get_cpu_var (Tasklet_vec). tail= T;
__get_cpu_var (Tasklet_vec). Tail= & (T->next);
Raise_softirq_irqoff (TASKLET_SOFTIRQ);/Request soft interrupt.
Local_irq_restore (flags);
}
the scheduling functions for high-priority Tasklet are as follows:
Static inline Voidtasklet_hi_schedule (struct tasklet_struct *t)
{
if (!test_and_set_bit (tasklet_state_sched, &t->state))
__tasklet_hi_schedule (t);
}
void __tasklet_hi_schedule (Structtasklet_struct *t)
{
Unsignedlong flags;
Local_irq_save (flags);
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);/Request soft interrupt.
Local_irq_restore (flags);
}
With the above two-group functions, the Tasklet are placed in different Tasklet_vec and Tasklet_hi_vec queues.