0.1. summary
softirq 真正干活的函数是__do_softirq。
linuxv3.11内核里能够执行__do_softirq,有如下调用,
这里指真正执行softirq的地方,不是触发(设置)softirq标志 !!!
- 每个硬中断退出的时。
- 当bh使能的时。
- 发送回环报文时。
0.1.1. case1 每个硬中断退出的时候。
0.1.1.1. call trace
1 2 3
| > irq_exit > > invoke_softirq > > > __do_softirq
|
0.1.1.2. irq_exit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 350
353 void irq_exit(void) 354 { 355 #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED 356 local_irq_disable(); 357 #else 358 WARN_ON_ONCE(!irqs_disabled()); 359 #endif 360 361 account_irq_exit_time(current); 362 trace_hardirq_exit(); 363 sub_preempt_count(HARDIRQ_OFFSET); 364 if (!in_interrupt() && local_softirq_pending()) 365 invoke_softirq(); 366 367 tick_irq_exit(); 368 rcu_irq_exit(); 369 }
|
0.1.1.3. invoke_softirq
1 2 3 4 5 6 7
| 329 static inline void invoke_softirq(void) 330 { 331 if (!force_irqthreads) 332 __do_softirq(); 333 else 334 wakeup_softirqd(); 335 }
|
0.1.2. case2 使能bh的时候
0.1.2.1. call trace
1 2 3 4 5
| > local_bh_enable > > _local_bh_enable_ip > > > do_softirq > > > > (x86_64特有的)call_softirq > > > > __do_softirq
|
0.1.2.2. local_bh_enable
1 2 3 4 5
| 184 void local_bh_enable(void) 185 { 186 _local_bh_enable_ip(_RET_IP_); 187 } 188 EXPORT_SYMBOL(local_bh_enable);
|
0.1.2.3. _local_bh_enable_ip
NOTE: 使能bh的时候,刚开始抢占还是禁止的。只有软中断处理完了后,抢占才使能的。
顺便说一下,使能bh时,有可能发生进程切换(见preempt_check_resched).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 157 static inline void _local_bh_enable_ip(unsigned long ip) 158 { 159 WARN_ON_ONCE(in_irq() || irqs_disabled()); 160 #ifdef CONFIG_TRACE_IRQFLAGS 161 local_irq_disable(); 162 #endif 163
166 if (softirq_count() == SOFTIRQ_DISABLE_OFFSET) 167 trace_softirqs_on(ip); 168
172 sub_preempt_count(SOFTIRQ_DISABLE_OFFSET - 1); 173 174 if (unlikely(!in_interrupt() && local_softirq_pending())) 175 do_softirq(); 176 177 dec_preempt_count(); 178 #ifdef CONFIG_TRACE_IRQFLAGS 179 local_irq_enable(); 180 #endif 181 preempt_check_resched(); 182 }
|
0.1.2.4. do_softirq根据不同的体系机构有不同的版本。
当对应的硬件体系里没有定义do_softirq时,使用
kernel/softirq.c中通用的do_softirq`.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 286 #ifndef __ARCH_HAS_DO_SOFTIRQ 287 288 asmlinkage void do_softirq(void) 289 { 290 __u32 pending; 291 unsigned long flags; 292 293 if (in_interrupt()) 294 return; 295 296 local_irq_save(flags); 297 298 pending = local_softirq_pending(); 299 300 if (pending) 301 __do_softirq(); 302 303 local_irq_restore(flags); 304 } 305 306 #endif
|
而对于X86_64,有其自己的定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 92 extern void call_softirq(void); 93 94 asmlinkage void do_softirq(void) 95 { 96 __u32 pending; 97 unsigned long flags; 98 99 if (in_interrupt()) 100 return; 101 102 local_irq_save(flags); 103 pending = local_softirq_pending(); 104 105 if (pending) { 106 call_softirq(); 107 WARN_ON_ONCE(softirq_count()); 108 } 109 local_irq_restore(flags); 110 }
|
其中call_softirq是个用汇编语言写的一个函数定义在 arch/x86/kernel/entry_64.S
0.1.3. netif_rx_ni
这一块的理解不是很透,仅以一个具体的例子来说明。发送回环报文.
0.1.3.1. 发送回环报文
0.1.3.2. call trace
1 2 3 4
| > dev_loopback_xmit > > netif_rx_ni > > do_softirq > > > __do_softirq
|
loopback 报文会通过netif_rx函数,被重新放回到
per_cpu变量softnet_data下的队列input_pkt_queue里,
同时激发napi调用,进而激发软中断调用(只是设置标志位,不是真正调用)
另外,注意netif_rx_ni里没有禁止bh调度,而是只是禁止了抢占(preempt_disable).
0.1.3.2.1. dev_loopback_xmit
1 2 3 4 5 6 7 8 9 10 11
| 2765 int dev_loopback_xmit(struct sk_buff *skb) 2766 { 2767 skb_reset_mac_header(skb); 2768 __skb_pull(skb, skb_network_offset(skb)); 2769 skb->pkt_type = PACKET_LOOPBACK; 2770 skb->ip_summed = CHECKSUM_UNNECESSARY; 2771 WARN_ON(!skb_dst(skb)); 2772 skb_dst_force(skb); 2773 netif_rx_ni(skb); 2774 return 0; 2775 }
|
0.1.3.2.2. netif_rx_ni
1 2 3 4 5 6 7 8 9 10 11 12 13
| 3267 int netif_rx_ni(struct sk_buff *skb) 3268 { 3269 int err; 3270 3271 preempt_disable(); 3272 err = netif_rx(skb); 3273 if (local_softirq_pending()) 3274 do_softirq(); 3275 preempt_enable(); 3276 3277 return err; 3278 } 3279 EXPORT_SYMBOL(netif_rx_ni);
|