Chinaunix首页 | 论坛 | 博客
  • 博客访问: 407329
  • 博文数量: 61
  • 博客积分: 1991
  • 博客等级: 上尉
  • 技术积分: 492
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-08 12:28
文章分类

全部博文(61)

文章存档

2011年(5)

2010年(21)

2009年(3)

2008年(4)

2007年(28)

我的朋友

分类: BSD

2007-05-15 15:52:50

代码:

/*
* Macros for interrupt interrupt entry, call to handler, and exit.
*/
#define INTR(irq_num, vec_name) \
  .text ; \
  SUPERALIGN_TEXT ; \
IDTVEC(vec_name) ; \
  pushl $0 ; /* dummy error code */ \
  pushl $0 ; /* dummy trap type */ \
  pushal ; /* 8 ints */ \
  pushl %ds ; /* save data and extra segments ... */ \
  pushl %es ; \
  pushl %fs ; \
  mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \
  mov %ax,%ds ; \
  mov %ax,%es ; \
  mov $KPSEL,%ax ; \
  mov %ax,%fs ; \
; \
  FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \
  pushl $irq_num; /* pass the IRQ */ \
  call atpic_handle_intr ; \
  addl $4, %esp ; /* discard the parameter */ \
; \
  MEXITCOUNT ; \
  jmp doreti

IRQ产生时,系统根据产生中断的IRQ号找到相应的中断向量入口,即此处的IDT_VEC(vec_name), 再这里,构造好函数atpic_handle_intr()的调用栈后,将转到atpic_handle_intr()进行处理。 同系统调用一样,这里的调用栈struct intrframe既是atpic_handle_intr()的参数,也是中断返回时用以恢复现场的寄存器状态。

代码:

/* Interrupt stack frame */
struct intrframe {
  int if_vec;
  int if_fs;
  int if_es;
  int if_ds;
  int if_edi;
  int if_esi;
  int if_ebp;
  int :32;
  int if_ebx;
  int if_edx;
  int if_ecx;
  int if_eax;
  int :32; /* for compat with trap frame - trapno */
  int :32; /* for compat with trap frame - err */
  /* below portion defined in 386 hardware */
  int if_eip;
  int if_cs;
  int if_eflags;
  /* below only when crossing rings (e.g. user to kernel) */
  int if_esp;
  int if_ss;
};

void
atpic_handle_intr(struct intrframe iframe)
{
  struct intsrc *isrc;

  KASSERT((uint)iframe.if_vec < ICU_LEN,
    ("unknown int %d\n", iframe.if_vec));
  isrc = &atintrs[iframe.if_vec].at_intsrc;

  /*
  * If we don't have an ithread, see if this is a spurious
  * interrupt.
  */

  if (isrc->is_ithread == NULL &&
    (iframe.if_vec == 7 || iframe.if_vec == 15)) {
    int port, isr;

    /*
    * Read the ISR register to see if IRQ 7/15 is really
    * pending. Reset read register back to IRR when done.
    */

    port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
    mtx_lock_spin(&icu_lock);
    outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
    isr = inb(port);
    outb(port, OCW3_SEL | OCW3_RR);
    mtx_unlock_spin(&icu_lock);
    if ((isr & IRQ7) == 0)
      return;
  }
  intr_execute_handlers(isrc, &iframe);
}

经过简单的有关8259A特有的检查,atpic_handle_intr()就转到intr_execute_handlers() 继续处理。

intr_execute_handlers()是一个重要的函数,它先得到IRQ号,然后判断是否是快速中断, 如果是,则直接在当前线程的上下文中运行,如果不是,则调度对应的中断线程来运行。 这个处理是被critical_enter()/critical_exit()保护起来的,以保证不会嵌套调度中断线程。

代码:

void
intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
{
  struct thread *td;
  struct ithd *it;
  struct intrhand *ih;
  int error, vector;

  td = curthread;
  td->td_intr_nesting_level++;

  /*
  * We count software interrupts when we process them. The
  * code here follows previous practice, but there's an
  * argument for counting hardware interrupts when they're
  * processed too.
  */

  atomic_add_long(isrc->is_count, 1);
  atomic_add_int(&cnt.v_intr, 1);

  it = isrc->is_ithread;
  if (it == NULL)
    ih = NULL;
  else
    ih = TAILQ_FIRST(&it->it_handlers);

  /*
  * XXX: We assume that IRQ 0 is only used for the ISA timer
  * device (clk).
  */

  vector = isrc->is_pic->pic_vector(isrc);
  if (vector == 0)
    clkintr_pending = 1;

  critical_enter();
  if (ih != NULL && ih->ih_flags & IH_FAST) {
    /*
    * Execute fast interrupt handlers directly.
    * To support clock handlers, if a handler registers
    * with a NULL argument, then we pass it a pointer to
    * a trapframe as its argument.
    */

    TAILQ_FOREACH(ih, &it->it_handlers, ih_next) {
      MPASS(ih->ih_flags & IH_FAST);
      CTR3(KTR_INTR, "%s: executing handler %p(%p)",
        __func__, ih->ih_handler,
        ih->ih_argument == NULL ? iframe :
        ih->ih_argument);
      if (ih->ih_argument == NULL)
        ih->ih_handler(iframe);
      else
        ih->ih_handler(ih->ih_argument);
    }
    isrc->is_pic->pic_eoi_source(isrc);
    error = 0;

凡是总是有例外,fast中断不在中断线程的上下文中运行,而是直接在用户进程的上下文中运行

代码:

} else {
    /*
    * For stray and threaded interrupts, we mask and EOI the
    * source.
    */

    isrc->is_pic->pic_disable_source(isrc);
    isrc->is_pic->pic_eoi_source(isrc);
    if (ih == NULL)
      error = EINVAL;
    else
      error = ithread_schedule(it, !cold);
  }

其他的非快速中断则需要调度。这里先应答中断控制器,然后调度。

代码:

critical_exit();
  if (error == EINVAL) {
    atomic_add_long(isrc->is_straycount, 1);
    if (*isrc->is_straycount < MAX_STRAY_LOG)
      log(LOG_ERR, "stray irq%d\n", vector);
    else if (*isrc->is_straycount == MAX_STRAY_LOG)
      log(LOG_CRIT,
        "too many stray irq %d's: not logging anymore\n",
        vector);
  }
  td->td_intr_nesting_level--;
}

中断线程调度函数ithread_schedule()处理有关中断线程调度的工作。

代码:

int
ithread_schedule(struct ithd *ithread, int do_switch)
{
  struct int_entropy entropy;
  struct thread *td;
  struct thread *ctd;
  struct proc *p;

  /*
  * If no ithread or no handlers, then we have a stray interrupt.
  */

  if ((ithread == NULL) || TAILQ_EMPTY(&ithread->it_handlers))
    return (EINVAL);

  ctd = curthread;
  /*
  * If any of the handlers for this ithread claim to be good
  * sources of entropy, then gather some.
  */

  if (harvest.interrupt && ithread->it_flags & IT_ENTROPY) {
    entropy.vector = ithread->it_vector;
    entropy.proc = ctd->td_proc;
    random_harvest(&entropy, sizeof(entropy), 2, 0,
      RANDOM_INTERRUPT);
  }

如果该中断线程有IT_ENTROPY标志,说明可以当作随机数的来源。

代码:

td = ithread->it_td;
  p = td->td_proc;
  KASSERT(p != NULL, ("ithread %s has no process", ithread->it_name));
  CTR4(KTR_INTR, "%s: pid %d: (%s) need = %d",
    __func__, p->p_pid, p->p_comm, ithread->it_need);

  /*
  * Set it_need to tell the thread to keep running if it is already
  * running. Then, grab sched_lock and see if we actually need to
  * put this thread on the runqueue. If so and the do_switch flag is
  * true and it is safe to switch, then switch to the ithread
  * immediately. Otherwise, set the needresched flag to guarantee
  * that this ithread will run before any userland processes.
  */

  ithread->it_need = 1;

设置it_need,可以保证中断线程不会在还有中断的情况下,错过中断而去睡眠,见ithread_loop()。

代码:

mtx_lock_spin(&sched_lock);
  if (TD_AWAITING_INTR(td)) {
    CTR2(KTR_INTR, "%s: setrunqueue %d", __func__, p->p_pid);
    TD_CLR_IWAIT(td);
    setrunqueue(td);
    if (do_switch &&
      (ctd->td_critnest == 1) ) {
      KASSERT((TD_IS_RUNNING(ctd)),
        ("ithread_schedule: Bad state for curthread."));
      ctd->td_proc->p_stats->p_ru.ru_nivcsw++;
      if (ctd->td_flags & TDF_IDLETD)
        ctd->td_state = TDS_CAN_RUN; /* XXXKSE */
      mi_switch();
    } else {
      curthread->td_flags |= TDF_NEEDRESCHED;
    }

如果中断线程正在睡眠,也就是说中断线程正在等待中断的到来,则将它放入runqueue,马上运行。 如果参数指示可以调度,并且当前线程的嵌套调度深度为1,即第一次试图调度中断线程,则进行上下文切换,否则,将不立即调度运行中断线程,而要等到正常调度时再运行。

这里需要指出的是,如果决定mi_switch(),由于中断线程优先级很高,中断线程将会立即执行,中断处理函数完成后也许将回到这里,也可能有变数,不会马上回到这里(FIXME),因此前面intr_execute_handlers()中先应答中断控制器,将中断处理必须做的先做完。

调度回来后,继续运行,完成整个中断的处理。

代码:

} else {
    CTR4(KTR_INTR, "%s: pid %d: it_need %d, state %d",
      __func__, p->p_pid, ithread->it_need, td->td_state);
  }

否则,由于已经设置了it_need=1,已经在运行的中断线程将负责处理之。

代码:

mtx_unlock_spin(&sched_lock);

  return (0);
}

我们再来看看中断线程本身,该函数较为简单,两个嵌套的循环保证不会遗漏中断,如果中断服务完成,则睡眠,调用mi_switch()

代码:

/*
* This is the main code for interrupt threads.
*/

static void
ithread_loop(void *arg)
{
  struct ithd *ithd; /* our thread context */
  struct intrhand *ih; /* and our interrupt handler chain */
  struct thread *td;
  struct proc *p;
  
  td = curthread;
  p = td->td_proc;
  ithd = (struct ithd *)arg; /* point to myself */
  KASSERT(ithd->it_td == td && td->td_ithd == ithd,
    ("%s: ithread and proc linkage out of sync", __func__));

  /*
  * As long as we have interrupts outstanding, go through the
  * list of handlers, giving each one a go at it.
  */

  for (;;) {
    /*
    * If we are an orphaned thread, then just die.
    */

    if (ithd->it_flags & IT_DEAD) {
      CTR3(KTR_INTR, "%s: pid %d: (%s) exiting", __func__,
        p->p_pid, p->p_comm);
      td->td_ithd = NULL;
      mtx_destroy(&ithd->it_lock);
      mtx_lock(&Giant);
      free(ithd, M_ITHREAD);
      kthread_exit(0);
    }

如果已经删除当前IRQ的中断处理程序,则需要退出中断线程。

代码:

CTR4(KTR_INTR, "%s: pid %d: (%s) need=%d", __func__,
      p->p_pid, p->p_comm, ithd->it_need);
    while (ithd->it_need) {
      /*
      * Service interrupts. If another interrupt
      * arrives while we are running, they will set
      * it_need to denote that we should make
      * another pass.
      */

      atomic_store_rel_int(&ithd->it_need, 0);

清除it_need标志,当清除后又有中断发生时,it_need将变成1,从而循环继续。

代码:

restart:
      TAILQ_FOREACH(ih, &ithd->it_handlers, ih_next) {
        if (ithd->it_flags & IT_SOFT && !ih->ih_need)
          continue;
        atomic_store_rel_int(&ih->ih_need, 0);
        CTR6(KTR_INTR,
          "%s: pid %d ih=%p: %p(%p) flg=%x", __func__,
          p->p_pid, (void *)ih,
          (void *)ih->ih_handler, ih->ih_argument,
          ih->ih_flags);

        if ((ih->ih_flags & IH_DEAD) != 0) {
          mtx_lock(&ithd->it_lock);
          TAILQ_REMOVE(&ithd->it_handlers, ih,
            ih_next);
          wakeup(ih);
          mtx_unlock(&ithd->it_lock);
          goto restart;
        }
        if ((ih->ih_flags & IH_MPSAFE) == 0)
          mtx_lock(&Giant);
        ih->ih_handler(ih->ih_argument);

调用设备驱动的中断服务函数。所有注册到该IRQ的函数都将被调用,各个设备的函数将检查
自己设备的状态以确定是否是自己的设备产生的中断。

代码:

if ((ih->ih_flags & IH_MPSAFE) == 0)
          mtx_unlock(&Giant);
      }
    }

    /*
    * Processed all our interrupts. Now get the sched
    * lock. This may take a while and it_need may get
    * set again, so we have to check it again.
    */

    WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
    mtx_assert(&Giant, MA_NOTOWNED);
    mtx_lock_spin(&sched_lock);
    if (!ithd->it_need) {
      /*
      * Should we call this earlier in the loop above?
      */

      if (ithd->it_enable != NULL)
        ithd->it_enable(ithd->it_vector);
      TD_SET_IWAIT(td); /* we're idle */
      p->p_stats->p_ru.ru_nvcsw++;
      CTR2(KTR_INTR, "%s: pid %d: done", __func__, p->p_pid);
      mi_switch();
      CTR2(KTR_INTR, "%s: pid %d: resumed", __func__, p->p_pid);
    }

如果此时it_need==1,则说明新来了中断,继续for循环为该中断服务,否则挂起调度。

代码:

mtx_unlock_spin(&sched_lock);
  }
}

阅读(2043) | 评论(0) | 转发(0) |
0

上一篇:1.2 8259A的登记过程

下一篇:3 软件中断swi

给主人留下些什么吧!~~