Linux softirq(软中断)

1. 软中断简介

在linux内核中,硬件中断主要来自于CPU外部。softirq则是内核定义的需要CPU快速处理的程序。软中断在中断服务处理之后处理,如果在规定的时间内无法完成软中断的处理,则在softirqd内核进程处理。


2. 软中断模块的初始化


kernel/softirq.c


751 void __init softirq_init(void)
752 {
753         int cpu;
754
755         for_each_possible_cpu(cpu) {
756                 int i;
757
758                 per_cpu(tasklet_vec, cpu).tail =
759                         &per_cpu(tasklet_vec, cpu).head;
760                 per_cpu(tasklet_hi_vec, cpu).tail =
761                         &per_cpu(tasklet_hi_vec, cpu).head;
762                 for (i = 0; i < NR_SOFTIRQS; i++)
763                         INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
764         }
765
766         register_hotcpu_notifier(&remote_softirq_cpu_notifier);
767
768         open_softirq(TASKLET_SOFTIRQ, tasklet_action);
769         open_softirq(HI_SOFTIRQ, tasklet_hi_action);
770 }

755-764 初始化tasklet_vec, tasklet_hi_vec数据结构,初始化softirq_work_list。

768-769 打开TASKLET_SOFTIRQ,HI_SOFTIRQ。


void open_softirq(int nr, void (*action)(struct softirq_action *))
{
        softirq_vec[nr].action = action;
}

设置 软中断向量。


3. 触发软中断

385 /*
386  * This function must run with irqs disabled!
387  */
388 inline void raise_softirq_irqoff(unsigned int nr)
389 {
390         __raise_softirq_irqoff(nr);
391
392         /*
393          * If we're in an interrupt or softirq, we're done
394          * (this also catches softirq-disabled code). We will
395          * actually run the softirq once we return from
396          * the irq or softirq.
397          *
398          * Otherwise we wake up ksoftirqd to make sure we
399          * schedule the softirq soon.
400          */
401         if (!in_interrupt())
402                 wakeup_softirqd();
403 }

405 void raise_softirq(unsigned int nr)
406 {
407         unsigned long flags;
408
409         local_irq_save(flags);
410         raise_softirq_irqoff(nr);
411         local_irq_restore(flags);
412 }
413
414 void __raise_softirq_irqoff(unsigned int nr)
415 {
416         trace_softirq_raise(nr);
417         or_softirq_pending(1UL << nr);
418 }

include/linux/interrupt.h

#define or_softirq_pending(x)  (local_softirq_pending() |= (x))

raise_softirq主要是,设置pending标志,如果可能唤醒softirqd内核进程。


4. 软中断的处理

kernel/softirq.c


367 void irq_exit(void)
368 {
369 #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
370         local_irq_disable();
371 #else
372         WARN_ON_ONCE(!irqs_disabled());
373 #endif
374
375         account_irq_exit_time(current);
376         trace_hardirq_exit();
377         sub_preempt_count(HARDIRQ_OFFSET);
378         if (!in_interrupt() && local_softirq_pending())
379                 invoke_softirq();
380
381         tick_irq_exit();
382         rcu_irq_exit();
383 }

378行,如果不在中断中且有等待处理的软中断,调用invoke_softirq。


334 static inline void invoke_softirq(void)
335 {
336         if (!force_irqthreads) {
337                 /*
338                  * We can safely execute softirq on the current stack if
339                  * it is the irq stack, because it should be near empty
340                  * at this stage. But we have no way to know if the arch
341                  * calls irq_exit() on the irq stack. So call softirq
342                  * in its own stack to prevent from any overrun on top
343                  * of a potentially deep task stack.
344                  */
345                 do_softirq();
346         } else {
347                 wakeup_softirqd();
348         }
349 }

在内核的配置中,force_irqthreads是0,调用do_softirq。

293 asmlinkage void do_softirq(void)
294 {
295         __u32 pending;
296         unsigned long flags;
297
298         if (in_interrupt())
299                 return;
300
301         local_irq_save(flags);
302
303         pending = local_softirq_pending();
304
305         if (pending)
306                 __do_softirq();
307
308         local_irq_restore(flags);
309 }


214 asmlinkage void __do_softirq(void)
215 {
216         struct softirq_action *h;
217         __u32 pending;
218         unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
219         int cpu;
220         unsigned long old_flags = current->flags;
221         int max_restart = MAX_SOFTIRQ_RESTART;
222
223         /*
224          * Mask out PF_MEMALLOC s current task context is borrowed for the
225          * softirq. A softirq handled such as network RX might set PF_MEMALLOC
226          * again if the socket is related to swap
227          */
228         current->flags &= ~PF_MEMALLOC;
229
230         pending = local_softirq_pending();
231         account_irq_enter_time(current);
232
233         __local_bh_disable((unsigned long)__builtin_return_address(0),
234                                 SOFTIRQ_OFFSET);
235         lockdep_softirq_enter();
236
237         cpu = smp_processor_id();
238 restart:
239         /* Reset the pending bitmask before enabling irqs */
240         set_softirq_pending(0);
241
242         local_irq_enable();
243
244         h = softirq_vec;
245
246         do {
247                 if (pending & 1) {
248                         unsigned int vec_nr = h - softirq_vec;
249                         int prev_count = preempt_count();
250
251                         kstat_incr_softirqs_this_cpu(vec_nr);
252
253                         trace_softirq_entry(vec_nr);
254             mt_trace_SoftIRQ_start(vec_nr);
255             h->action(h);
256             mt_trace_SoftIRQ_end(vec_nr);

257                         trace_softirq_exit(vec_nr);
258                         if (unlikely(prev_count != preempt_count())) {
259                                 printk(KERN_ERR "huh, entered softirq %u %s %p"
260                                        "with preempt_count %08x,"
261                                        " exited with %08x?\n", vec_nr,
262                                        softirq_to_name[vec_nr], h->action,
263                                        prev_count, preempt_count());
264                                 preempt_count() = prev_count;
265                         }
266
267                         rcu_bh_qs(cpu);
268                 }
269                 h++;
270                 pending >>= 1;
271         } while (pending);
272
273         local_irq_disable();
274
275         pending = local_softirq_pending();
276         if (pending) {
277                 if (time_before(jiffies, end) && !need_resched() &&
278                     --max_restart)
279                         goto restart;
280
281                 wakeup_softirqd();
282         }
283
284         lockdep_softirq_exit();
285
286         account_irq_exit_time(current);
287         __local_bh_enable(SOFTIRQ_OFFSET);
288         tsk_restore_flags(current, old_flags, PF_MEMALLOC);
289 }

255调用open_softirq注册的处理函数,如TASKLET_SOFTIRQ的处理函数tasklet_action。

如果277满足,继续处理软中断。否则唤醒内核软中断处理进程。



以上讲述了软中断处理的整个流程,注册/触发/处理。更详细的内容可以读kernel/softirq.c,代码是最好的老师。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值