代码:
/* * 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); } }
|
阅读(2109) | 评论(0) | 转发(0) |