中断代码阅读(1)_共享中断和短时间两次同一中断

来源:互联网 发布:淘宝第三方公司 编辑:程序博客网 时间:2024/05/21 08:58

 1. 共享中断的注册和处理

注册中断时,在setup_irq中,

 struct irq_desc *desc = irq_desc + irq;
...

 p = &desc->action;
 old = *p;
 if (old) {
如果已注册中断或者本次注册中断不是共享中断,则

  if (!((old->flags & new->flags) & IRQF_SHARED) ||
      ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
   old_name = old->name;
   goto mismatch;
  }

如果是共享中断,则

  do {
   p = &old->next;
   old = *p;
  } while (old);
  shared = 1;

然后将本次注册中断添加到desc->action的链表上。
 *p = new;

2. 执行中断时共享中断相关代码

执行handle_IRQ_event时,会依次执行所有注册到当前中断上的action->handler,所以当多个中断共享同一中断号时,每次中断都会依次执行所有注册的ISR。另外在执行action->handler之前,如果不是独占中断(IRQF_DISABLED),会重新使能中断,action->handler执行完毕再关闭中断。

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
 irqreturn_t ret, retval = IRQ_NONE;
 unsigned int status = 0;

 handle_dynamic_tick(action);

 if (!(action->flags & IRQF_DISABLED))
  local_irq_enable_in_hardirq();

 do {
  ret = action->handler(irq, action->dev_id);
  if (ret == IRQ_HANDLED)
   status |= action->flags;
  retval |= ret;
  action = action->next;
 } while (action);

 if (status & IRQF_SAMPLE_RANDOM)
  add_interrupt_randomness(irq);
 local_irq_disable();

 return retval;
}

 

 3. 中断处理期间又来了一次当前正处理的中断

在执行驱动中断处理函数过程中,如果又来了一次与当前正执行中断类型相同的中断:

A. 离开当前中断处理,重新进入中断处理,如果正在处理(IRQ_INPROGRESS),则action为0直接返回,而status被设置了IRQ_PENDING。参考代码:

 status |= IRQ_PENDING; /* we _want_ to handle it */
 action = NULL;
 if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
  action = desc->action;
  status &= ~IRQ_PENDING; /* we commit to handling */
  status |= IRQ_INPROGRESS; /* we are handling it */
 }
 desc->status = status;
 if (unlikely(!action))
  goto out;

 B. 从新中断返回后,会回到前面正在执行的中断处理,此时,如果发现有IRQ_PENDING,会再次执行一遍中断处理流程,知道没有IRQ_PENDING被设置为止。

可参考在函数unsigned int __do_IRQ(unsigned int irq)中的代码:

 /*
  * Edge triggered interrupts need to remember
  * pending events.
  * This applies to any hw interrupts that allow a second
  * instance of the same irq to arrive while we are in do_IRQ
  * or in the handler. But the code here only handles the _second_
  * instance of the irq, not the third or fourth. So it is mostly
  * useful for irq hardware that does not mask cleanly in an
  * SMP environment.
  */
 for (;;) {
  irqreturn_t action_ret;

  spin_unlock(&desc->lock);

  action_ret = handle_IRQ_event(irq, action);
  if (!noirqdebug)
   note_interrupt(irq, desc, action_ret);

  spin_lock(&desc->lock);
  if (likely(!(desc->status & IRQ_PENDING)))
   break;
  desc->status &= ~IRQ_PENDING;
 }

 

注意:如果在一次中断执行过程中来了多次(n>1)同一种中断,则只会执行一次,有(n-1)个该类中断被“无视”了。

 

原创粉丝点击