Chinaunix首页 | 论坛 | 博客
  • 博客访问: 434440
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2014-02-05 17:20:41

1. softirq执行的上下文
softirq执行的上下文是中断上下文,也就是中断服务程序即将结束时。  irq_exit 会调用invoke_softirq();
  /*
 * 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);
/* 这里判断in_interrupt,保证softirq不会嵌套。
假如在执行softirq时,硬中断发生,此softirq被中断执行,并且假如硬中断执行过程中raise了新的softirq,在硬中断执行完毕后,虽然发现有新的softirq pending,但在发现
in_interrupt() 中有softirq还有没有完成,本次就不会再次调用invoke_softirq。当硬件中断执行完毕后, softirq通过弾栈继续执行
 */
 if (!in_interrupt() && local_softirq_pending()) 
  invoke_softirq();

#ifdef CONFIG_NO_HZ
 /* Make sure that timer wheel updates are propagated */
 if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
  tick_nohz_irq_exit();
#endif
 rcu_irq_exit();
 sched_preempt_enable_no_resched();
}


static inline void invoke_softirq(void)
{
 if (!force_irqthreads) {
#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
  __do_softirq();
#else
  do_softirq();
#endif
 } else {
  __local_bh_disable((unsigned long)__builtin_return_address(0),
    SOFTIRQ_OFFSET);
  wakeup_softirqd();
  __local_bh_enable(SOFTIRQ_OFFSET);
 }
}

2. __do_softirq的分析

调用__do_softirq之前需要保证中断是关闭的
asmlinkage void __do_softirq(void)
{
 struct softirq_action *h;
 __u32 pending;
 int max_restart = MAX_SOFTIRQ_RESTART;
 int cpu;

 pending = local_softirq_pending(); /*  pending 的bit记入栈中,下面会清空全局变量里面的pending值  */
 account_system_vtime(current);

 __local_bh_disable((unsigned long)__builtin_return_address(0),
    SOFTIRQ_OFFSET);  /* 此处禁止softirq,防止softirq嵌套 */
 lockdep_softirq_enter();

 cpu = smp_processor_id();
restart:
 /* Reset the pending bitmask before enabling irqs */
 set_softirq_pending(0);   /* pending 的bit已经记入栈中,可以清空softirq全局变量里面的pending,
       这里注意irq是关闭的, 清空pending值是安全的,因为不会有中断打断这过程从而也不会有新的pengding产生 */

 local_irq_enable();  /* 开启本地中断,保证中断的最高优先级,即软中断可以被irq打断 */

 h = softirq_vec;

 do {
  if (pending & 1) {
   unsigned int vec_nr = h - softirq_vec;
   int prev_count = preempt_count();

   kstat_incr_softirqs_this_cpu(vec_nr);

   trace_softirq_entry(vec_nr);
   h->action(h);
   trace_softirq_exit(vec_nr);
/* callback函数不应破坏抢占计数 */
   if (unlikely(prev_count != preempt_count())) {
    printk(KERN_ERR "huh, entered softirq %u %s %p"
           "with preempt_count %08x,"
           " exited with %08x?\n", vec_nr,
           softirq_to_name[vec_nr], h->action,
           prev_count, preempt_count());
    preempt_count() = prev_count;
   }

   rcu_bh_qs(cpu);
  }
  h++;
  pending >>= 1;
 } while (pending);

 local_irq_disable();

 pending = local_softirq_pending();  /* 如果软中断被中断打断, 并且中断执行过程中raise了新的软中断,当中断退出后,此软中断重新执行,到这里需要重新读取softirqpending,到restart处重新开始处理softirq, 这样顺手就把新raise的softirq处理了, 但是restart的过程不能太多了,防止影响task的调度 */
 if (pending && --max_restart)
  goto restart;

 if (pending)
  wakeup_softirqd();

 lockdep_softirq_exit();

 account_system_vtime(current);
 __local_bh_enable(SOFTIRQ_OFFSET); /* 此处使能softirq */
}

3. do_softirq的分析
如果在irq没有关闭的环境下,比如在线程环境下执行softirq,需要要使用本函数。本函数只是__do_softirq的封装,只是会在调用__do_softirq()前关闭中断,结束后打开中断。


asmlinkage void do_softirq(void)
{
 __u32 pending;
 unsigned long flags;

 if (in_interrupt())
  return;

 local_irq_save(flags); /* 在调用__do_softirq()前关闭中断,结束后打开中断 */
/* 注意__do_softirq();中会有开中断,又关中断的操作 */

 pending = local_softirq_pending();

 if (pending)
  __do_softirq();

 local_irq_restore(flags);
}

阅读(1204) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~