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

全部博文(61)

文章存档

2011年(5)

2010年(21)

2009年(3)

2008年(4)

2007年(28)

我的朋友

分类: BSD

2007-05-15 15:32:25

中断向量表有多个vector,0-31为CPU用,32~32+15对应IRQ0~IRQ15一个vector对应一个source,数据类型是struct intsrc

代码:

 

/*
* An interrupt source. The upper-layer code uses the PIC methods to
* control a given source. The lower-layer PIC drivers can store additional
* private data in a given interrupt source such as an interrupt pin number
* or an I/O APIC pointer.
*/

struct intsrc {
  struct pic *is_pic;
  struct ithd *is_ithread;
  u_long *is_count;
  u_long *is_straycount;
  u_int is_index;
};

其实在vector后面的是中断控制器,如8259A,I/O APIC等,事实上,对中断源的控制实际上就是对中断控制器的操作,因此,在struct intsrc中有成员struct pic *is_pic,
即中断控制器的操作函数表,通过这个表,可以为不同的中断控制器定义不同的操作,达到demultiplex的作用。这里pic是programmable interrupt controller的意思。

代码:

/*
* Methods that a PIC provides to mask/unmask a given interrupt source,
* "turn on" the interrupt on the CPU side by setting up an IDT entry, and
* return the vector associated with this source.
*/

struct pic {
  void (*pic_enable_source)(struct intsrc *);
  void (*pic_disable_source)(struct intsrc *);
  void (*pic_eoi_source)(struct intsrc *);
  void (*pic_enable_intr)(struct intsrc *);
  int (*pic_vector)(struct intsrc *);
  int (*pic_source_pending)(struct intsrc *);
  void (*pic_suspend)(struct intsrc *);
  void (*pic_resume)(struct intsrc *);
};

系统中所有的中断源组成一个数组,由于当采用I/O APIC作为中断控制器时,可以有191个中断号(IRQ),因此该数组大小定义为191。

代码:

static struct intsrc *interrupt_sources[NUM_IO_INTS];

/* With I/O APIC's we can have up to 191 interrupts. */
#define NUM_IO_INTS 191

所谓登记中断源,就是将实际的中断控制器的对应struct intsrc数据结构添加到该数组中去。同时,系统为每个登记的中断源创建一个中断线程,中断处理程序就在该线程的上下文中运行,该线程的入口函数为ithread_loop(),struct intsrc结构成员is_ithread指向描述中断线程的数据结构struct ithd,而struct ithd结构成员it_td指向真正的线程结构struct thread,从而将中断与系统的调度单元线程联系起来。

代码:

/*
* Describe an interrupt thread. There is one of these per interrupt vector.
* Note that this actually describes an interrupt source. There may or may
* not be an actual kernel thread attached to a given source.
*/

struct ithd {
  struct mtx it_lock;
  struct thread *it_td; /* Interrupt process. */
  LIST_ENTRY(ithd) it_list; /* All interrupt threads. */
  TAILQ_HEAD(, intrhand) it_handlers; /* Interrupt handlers. */
  struct ithd *it_interrupted; /* Who we interrupted. */
  void (*it_disable)(uintptr_t); /* Enable interrupt source. */
  void (*it_enable)(uintptr_t); /* Disable interrupt source. */
  void *it_md; /* Hook for MD interrupt code. */
  int it_flags; /* Interrupt-specific flags. */
  int it_need; /* Needs service. */
  uintptr_t it_vector;
  char it_name[MAXCOMLEN + 1];
};

/*
* Register a new interrupt source with the global interrupt system.
* The global interrupts need to be disabled when this function is
* called.
*/

int
intr_register_source(struct intsrc *isrc)
{
  int error, vector;

  vector = isrc->is_pic->pic_vector(isrc);
  if (interrupt_sources[vector] != NULL)
    return (EEXIST);
  error = ithread_create(&isrc->is_ithread, (uintptr_t)isrc, 0,
    (mask_fn)isrc->is_pic->pic_disable_source,
    (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector);
  if (error)
    return (error);
  mtx_lock_spin(&intr_table_lock);
  if (interrupt_sources[vector] != NULL) {
    mtx_unlock_spin(&intr_table_lock);
    ithread_destroy(isrc->is_ithread);
    return (EEXIST);
  }
  intrcnt_register(isrc);
  interrupt_sources[vector] = isrc;
  mtx_unlock_spin(&intr_table_lock);
  return (0);
}

int
ithread_create(struct ithd **ithread, uintptr_t vector, int flags,
  void (*disable)(uintptr_t), void (*enable)(uintptr_t), const char *fmt, ...)
{
  struct ithd *ithd;
  struct thread *td;
  struct proc *p;
  int error;
  va_list ap;

  /* The only valid flag during creation is IT_SOFT. */
  if ((flags & ~IT_SOFT) != 0)
    return (EINVAL);

  ithd = malloc(sizeof(struct ithd), M_ITHREAD, M_WAITOK | M_ZERO);
  ithd->it_vector = vector;
  ithd->it_disable = disable;
  ithd->it_enable = enable;
  ithd->it_flags = flags;
  TAILQ_INIT(&ithd->it_handlers);
  mtx_init(&ithd->it_lock, "ithread", NULL, MTX_DEF);

  va_start(ap, fmt);
  vsnprintf(ithd->it_name, sizeof(ithd->it_name), fmt, ap);
  va_end(ap);

  error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
    0, "%s", ithd->it_name);
  if (error) {
    mtx_destroy(&ithd->it_lock);
    free(ithd, M_ITHREAD);
    return (error);
  }
  td = FIRST_THREAD_IN_PROC(p); /* XXXKSE */
  mtx_lock_spin(&sched_lock);
  td->td_ksegrp->kg_pri_class = PRI_ITHD;
  td->td_priority = PRI_MAX_ITHD;
  TD_SET_IWAIT(td);
  mtx_unlock_spin(&sched_lock);
  ithd->it_td = td;
  td->td_ithd = ithd;
  if (ithread != NULL)
    *ithread = ithd;
  CTR2(KTR_INTR, "%s: created %s", __func__, ithd->it_name);
  return (0);
}

中断源登记完成后,便可以登记中断处理程序了。struct ithd有一个成员it_handlers,指向一个链表,这个链表是中断处理程序的链表。为什么多个中断处理程序会连接在一个链表中呢?这是因为多个设备可以共享同一个IRQ号,即同一个vertor可以登记多个设备的中断处理函数。当中断来临时,系统分别调用各个设备的中断处理函数,由他们自己判断是否是自己的中断。

intr_add_handler()函数就是用来登记中断处理程序的,它从系统中分配一个描述中断处理程序的数据结构struct intrhand,并将传入的参数,即中断处理函数driver_intr_t handler保存在结构struct intrhand的成员时ih_handler中。中断发生时真正处理中断事务的就是该函数。

代码:

/*
* Describe a hardware interrupt handler.
*
* Multiple interrupt handlers for a specific vector can be chained
* together.
*/

struct intrhand {
  driver_intr_t *ih_handler; /* Handler function. */
  void *ih_argument; /* Argument to pass to handler. */
  int ih_flags;
  const char *ih_name; /* Name of handler. */
  struct ithd *ih_ithread; /* Ithread we are connected to. */
  int ih_need; /* Needs service. */
  TAILQ_ENTRY(intrhand) ih_next; /* Next handler for this vector. */
  u_char ih_pri; /* Priority of this handler. */
};

/* Interrupt handle flags kept in ih_flags */
#define IH_FAST 0x00000001 /* Fast interrupt. */
#define IH_EXCLUSIVE 0x00000002 /* Exclusive interrupt. */
#define IH_ENTROPY 0x00000004 /* Device is a good entropy source. */
#define IH_DEAD 0x00000008 /* Handler should be removed. */
#define IH_MPSAFE 0x80000000 /* Handler does not need Giant. */

这里有几个flag值值得一提。IH_FAST指示该中断是快速中断,系统将尽快执行该处理函数,
并不将它调度到中断线程的上下文中运行,也就是说这种函数的运行是在中断环境下运行,没有线程的上下文,是为历史遗留的还未迁移到新中断模式下的驱动程序提供的。H_EXCLUSIVE指示该中断是独占IRQ的,即不能和其他设备共享IRQ。IH_MPSAFE表明该中断处理函数是SMP安全的。

代码:

int
intr_add_handler(const char *name, int vector, driver_intr_t handler,
  void *arg, enum intr_type flags, void **cookiep)
{
  struct intsrc *isrc;
  int error;

  isrc = intr_lookup_source(vector);
  if (isrc == NULL)
    return (EINVAL);
  error = ithread_add_handler(isrc->is_ithread, name, handler, arg,
    ithread_priority(flags), flags, cookiep);
  if (error == 0) {
    intrcnt_updatename(isrc);
    isrc->is_pic->pic_enable_intr(isrc);
    isrc->is_pic->pic_enable_source(isrc);
  }
  return (error);
}

int
ithread_add_handler(struct ithd* ithread, const char *name,
  driver_intr_t handler, void *arg, u_char pri, enum intr_type flags,
  void **cookiep)
{
  struct intrhand *ih, *temp_ih;

  if (ithread == NULL || name == NULL || handler == NULL)
    return (EINVAL);

  ih = malloc(sizeof(struct intrhand), M_ITHREAD, M_WAITOK | M_ZERO);
  ih->ih_handler = handler;
  ih->ih_argument = arg;
  ih->ih_name = name;
  ih->ih_ithread = ithread;
  ih->ih_pri = pri;
  if (flags & INTR_FAST)
    ih->ih_flags = IH_FAST;
  else if (flags & INTR_EXCL)
    ih->ih_flags = IH_EXCLUSIVE;
  if (flags & INTR_MPSAFE)
    ih->ih_flags |= IH_MPSAFE;
  if (flags & INTR_ENTROPY)
    ih->ih_flags |= IH_ENTROPY;

  mtx_lock(&ithread->it_lock);
  if ((flags & INTR_EXCL) != 0 && !TAILQ_EMPTY(&ithread->it_handlers))
    goto fail;
  if (!TAILQ_EMPTY(&ithread->it_handlers)) {
    temp_ih = TAILQ_FIRST(&ithread->it_handlers);
    if (temp_ih->ih_flags & IH_EXCLUSIVE)
      goto fail;
    if ((ih->ih_flags & IH_FAST) && !(temp_ih->ih_flags & IH_FAST))
      goto fail;
    if (!(ih->ih_flags & IH_FAST) && (temp_ih->ih_flags & IH_FAST))
      goto fail;
  }

  TAILQ_FOREACH(temp_ih, &ithread->it_handlers, ih_next)
    if (temp_ih->ih_pri > ih->ih_pri)
      break;
  if (temp_ih == NULL)
    TAILQ_INSERT_TAIL(&ithread->it_handlers, ih, ih_next);
  else
    TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next);
  ithread_update(ithread);
  mtx_unlock(&ithread->it_lock);

  if (cookiep != NULL)
    *cookiep = ih;
  CTR3(KTR_INTR, "%s: added %s to %s", __func__, ih->ih_name,
    ithread->it_name);
  return (0);

fail:
  mtx_unlock(&ithread->it_lock);
  free(ih, M_ITHREAD);
  return (EINVAL);
}

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