linux内核软中断实现

软中断

软中断是内核提供的一种延迟机制,完全由软件触发。虽然是延迟机制,实际上,在大多数情况下,它比普通进程能够得到更快的响应。软中断也是内核其他机制的基础,如tasklet、高分辨率timer等。

软中断资料有限,目前内核中实现了10中类型的软中断

1.   enum  

2.   {  

3.       HI_SOFTIRQ=0,  

4.       TIMER_SOFTIRQ,  

5.       NET_TX_SOFTIRQ,  

6.       NET_RX_SOFTIRQ,  

7.       BLOCK_SOFTIRQ,  

8.       BLOCK_IOPOLL_SOFTIRQ,  

9.       TASKLET_SOFTIRQ,  

10.     SCHED_SOFTIRQ,  

11.     HRTIMER_SOFTIRQ,  

12.     RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */  

13.   

14.     NR_SOFTIRQS  

15. }; 

内核开发者不建议擅自增加软中断数量,如果需要新的软中断,尽可能它实现为基于tasklet形式

1、          ksoftirqd

early_initcall(spawn_ksoftirqd);---初始化的时候调用spawn_ksoftirqd函数。

smpboot_register_percpu_thread(&softirq_threads)---热插拔阶段为每个percpu上创建一个ksoftirqd守护进程

1.  DEFINE_PER_CPU(struct task_struct *, ksoftirqd);

2.    

3.   static struct smp_hotplug_thread softirq_threads = {  

4.       .store          = &ksoftirqd,  

5.       .thread_should_run  = ksoftirqd_should_run, /*判断是否应该运行处理软中断*/  

6.       .thread_fn      = run_ksoftirqd,        /*运行处理软中断*/  

7.       .thread_comm        = "ksoftirqd/%u",  

8.   };  

9.    

10. static void run_ksoftirqd(unsigned int cpu)  

11. {  

12.     /*关闭本地cpu中断*/  

13.     local_irq_disable();  

14.     if (local_softirq_pending()) { /*检查本地cpu上是否有软中断挂起*/  

15.         /* 

16.          * We can safely run softirq on inline stack, as we are not deep 

17.          * in the task stack here. 

18.          */  

19.         __do_softirq();/*处理软中断*/  

20.         local_irq_enable();/*使能本地cpu中断*/  

21.         cond_resched(); /*有条件的重新调度*/  

22.   

23.         preempt_disable();  

24.         rcu_note_context_switch(cpu);  

25.         preempt_enable();  

26.   

27.         return;  

28.     }  

29.     local_irq_enable();  

30. }  

2、          结构

Irq_cpustat_t,多个软中断可以同时在多个cpu运行,就算是同一个软中断,也有可能同时在多个cpu上运行。内核为每个cpu都管理着一个待决软中断pedding,他就是Irq_cpustat_t

1.   typedef struct {  

2.       unsigned int __softirq_pending;  

3.   } ____cacheline_aligned irq_cpustat_t; 

4.    

5.  irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; 

1.   struct softirq_action  

2.   {  

3.       void    (*action)(struct softirq_action *);  

4.   };  

5.   static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  

__softirq_pending中每个bit,对应某一个软中断,某个Bit被置位,说明有相应的软总段等待处理。因此最多只能定义32个软中断类型。

3、          软中断触发

想触发软中断,只需要调用raise_softirq即可,它的实现简单先关闭本地cpu中断,然后调用raise_softirq_irqoff,再打开本地cpu中断。

1.   void raise_softirq(unsigned int nr)  

2.   {  

3.       unsigned long flags;  

4.     

5.       local_irq_save(flags);  

6.       raise_softirq_irqoff(nr);  

7.       local_irq_restore(flags);  

8.   }  

再来看raise_softirq_irqoff

1.   inline void raise_softirq_irqoff(unsigned int nr)  

2.   {  

3.       __raise_softirq_irqoff(nr);  

4.     

5.           ......  

6.       if (!in_interrupt())  

7.           wakeup_softirqd();  

8.  

先通过__raise_softirq_irqoff设置cpu的软中断pending标志位(irq_stat(NR_CPUS)),然后通过in_interrupt判断是否在中断上下文中,如果不成立,则唤醒软中断守护进程,在守护进程中执行软中断的回调函数。

4、          软中断的执行

软中断有两种执行方式,一种是在中断调用结束时,一种是在ksoftirqd守护进程中

1.   /* 

2.    * Exit an interrupt context. Process softirqs if needed and possible: 

3.    */  

4.   void irq_exit(void)  

5.   {  

6.   #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED  

7.       local_irq_disable();  

8.   #else  

9.       WARN_ON_ONCE(!irqs_disabled());  

10. #endif  

11.   

12.     account_irq_exit_time(current);  

13.     preempt_count_sub(HARDIRQ_OFFSET);  

14.     /* 

15.     在中断发生嵌套时,通过in_interrupt能确保在最外层的中断Irq_exit阶段 

16.     invoke_softirq才会被调用 

17.     */  

18.     if (!in_interrupt() && local_softirq_pending())  

19.         invoke_softirq();  

20.   

21.     tick_irq_exit();  

22.     rcu_irq_exit();  

23.     trace_hardirq_exit(); /* must be last! */  

24. }  

代码最终都会进入到__do_softirq中,执行软中断的重点都在该函数中。

1.   asmlinkage void __do_softirq(void)  

2.   {  

3.       unsigned long end = jiffies + MAX_SOFTIRQ_TIME;  

4.       unsigned long old_flags = current->flags;  

5.       int max_restart = MAX_SOFTIRQ_RESTART;  

6.       struct softirq_action *h;  

7.       bool in_hardirq;  

8.       __u32 pending;  

9.       int softirq_bit;  

10.     int cpu;  

11.   

12.     /* 

13.      * Mask out PF_MEMALLOC s current task context is borrowed for the 

14.      * softirq. A softirq handled such as network RX might set PF_MEMALLOC 

15.      * again if the socket is related to swap 

16.      */  

17.     current->flags &= ~PF_MEMALLOC;  

18.     /* 

19.     复制软中断掩码到局部变量,这是必要的 

20.     因为local_softirq_pending中的值在开中断后将不再可靠,必须先保存 

21.     */  

22.     pending = local_softirq_pending();  

23.     account_irq_enter_time(current);  

24.   

25.     /* 

26.     标志下面的代码正在处理softirq 

27.     */  

28.     __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);  

29.     in_hardirq = lockdep_softirq_start();  

30.   

31.     cpu = smp_processor_id();  

32. restart:  

33.     /* Reset the pending bitmask before enabling irqs */  

34.     set_softirq_pending(0);/*清空pending*/  

35.   

36.     local_irq_enable(); /*打开本地中断*/  

37.   

38.     /* 

39.     到这里已经打开了本地中断,下面在软中断处理执行过程中可能会被硬件中断抢占 

40.     */  

41.     /* 

42.     根据软中断标志位处理软中断 

43.     */  

44.     /* softirq_vec 存放action的结构体*/

45.     h = softirq_vec;  

46.   

47.     while ((softirq_bit = ffs(pending))) {  

48.         unsigned int vec_nr;  

49.         int prev_count;  

50.   

51.         h += softirq_bit - 1;  

52.   

53.         vec_nr = h - softirq_vec;  

54.         prev_count = preempt_count();  

55.   

56.         kstat_incr_softirqs_this_cpu(vec_nr);  

57.   

58.         trace_softirq_entry(vec_nr);  

59.         h->action(h);  

60.         trace_softirq_exit(vec_nr);  

61.         if (unlikely(prev_count != preempt_count())) {  

62.             pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",  

63.                    vec_nr, softirq_to_name[vec_nr], h->action,  

64.                    prev_count, preempt_count());  

65.             preempt_count_set(prev_count);  

66.         }  

67.         rcu_bh_qs(cpu);  

68.         h++;  

69.         pending >>= softirq_bit;  

70.     }  

71.   

72.     /*关掉本地中断*/  

73.     local_irq_disable();  

74.     /* 

75.     由于前面有打开过本地中断,因此这次可能会有新的软中断未处理,再检查处理, 

76.     */  

77.     pending = local_softirq_pending();  

78.     if (pending) {  

79.         if (time_before(jiffies, end) && !need_resched() &&  

80.             --max_restart)  

81.             goto restart;  

82.   

83.         wakeup_softirqd();  

84.     }  

85.   

86.       

87.     lockdep_softirq_end(in_hardirq);  

88.     account_irq_exit_time(current);  

89.     __local_bh_enable(SOFTIRQ_OFFSET);  

90.     WARN_ON_ONCE(in_interrupt());  

91.     tsk_restore_flags(current, old_flags, PF_MEMALLOC);  

92. }  

5、          软中断注册

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


猜你喜欢

转载自blog.csdn.net/you5522393/article/details/80702352