linux 内核中的软中断处理

来源:互联网 发布:网络直放站 编辑:程序博客网 时间:2024/05/21 12:41

Linux内核定义的软中断如下:

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"

};

内核启动时注册的软中断有以下。


其注册函数为:

void open_softirq(int nr, void(*action)(struct softirq_action *))

{

       softirq_vec[nr].action= action;

}

其实就是对全局定义的数组变量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

}; 注册函数如下:

       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);

在irq中每一个CPU都有个结构体

typedef struct {

       unsignedlong __softirq_pending;

} ____cacheline_aligned irq_cpustat_t; 用来保存当前CPU是否有等待的中断处理函数。

在之前的文章可以知道,系统每一次执行do_irq()结束之后,都会调用函数irq_exit(void)。在这个函数中将处理软件中断,

/*

 *Exit an interrupt context. 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())//表示不在中断环境下,处理软件中断,

              invoke_softirq();//同时有软件中断存在,则执行。

 

#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

其do_softirq()函数定义如下:函数中有类似的逻辑。

asmlinkage void do_softirq(void)

{

       __u32pending;

       unsignedlong flags;

       if(in_interrupt())

              return;

       local_irq_save(flags);

       pending= local_softirq_pending();//有软件中断挂起,对挂起的断进行执行。

       if(pending)

              __do_softirq();

       local_irq_restore(flags);

}

#endif

 

在处理软件中,内核定义了一个内核线程来处理不能及时处理的软中断请求。其函数为:

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();

}

在__do_softirq()函数中,处理挂起的软中断,同来pending位来判断的是否有中断挂起。

入中达到了(max_restart==0)的时候,pending不为0。调用了函数wakeup_softirqd();

内核线程的创建可参考其他文章。

       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;

};

在linux内核中有俩种类型的tasklet,

其tasklet初始化如下:

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;

}

初始化完tasklet之后,将其加入到tasklet队列中。

低优先级tasklet的调度函数如下:

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);//请求软中断。

       local_irq_restore(flags);

}

高优先级tasklet的调度函数如下:

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);//请求软中断。

       local_irq_restore(flags);

}

有上面的俩组函数可知,将tasklet放入到不同的tasklet_vec和tasklet_hi_vec队列中。

 

原创粉丝点击