From 79918c45a6cb0f1be2a1f4a98b4db650b3e3ec66 Mon Sep 17 00:00:00 2001 From: jhb Date: Sat, 5 Apr 2008 19:58:30 +0000 Subject: Add a MI intr_event_handle() routine for the non-INTR_FILTER case. This allows all the INTR_FILTER #ifdef's to be removed from the MD interrupt code. - Rename the intr_event 'eoi', 'disable', and 'enable' hooks to 'post_filter', 'pre_ithread', and 'post_ithread' to be less x86-centric. Also, add a comment describe what the MI code expects them to do. - On amd64, i386, and powerpc this is effectively a NOP. - On arm, don't bother masking the interrupt unless the ithread is scheduled in the non-INTR_FILTER case to match what INTR_FILTER did. Also, don't bother unmasking the interrupt in the post_filter case if we never masked it. The INTR_FILTER case had been doing this by having arm_unmask_irq for the post_filter (formerly 'eoi') hook. - On ia64, stray interrupts are now masked for the non-INTR_FILTER case. They were already masked in the INTR_FILTER case. - On sparc64, use the a NULL pre_ithread hook and use intr_enable_eoi() for both the 'post_filter' and 'post_ithread' hooks to match what the non-INTR_FILTER code did. - On sun4v, retire the ithread wrapper hack by using an appropriate 'post_ithread' hook instead (it's what 'post_ithread'/'enable' was designed to do even in 5.x). Glanced at by: piso Reviewed by: marius Requested by: marius [1], [5] Tested on: amd64, i386, arm, sparc64 --- sys/kern/kern_intr.c | 126 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 21 deletions(-) (limited to 'sys/kern/kern_intr.c') diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index 75234b6..6bd434d 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -94,9 +94,14 @@ static TAILQ_HEAD(, intr_event) event_list = static void intr_event_update(struct intr_event *ie); #ifdef INTR_FILTER +static int intr_event_schedule_thread(struct intr_event *ie, + struct intr_thread *ithd); +static int intr_filter_loop(struct intr_event *ie, + struct trapframe *frame, struct intr_thread **ithd); static struct intr_thread *ithread_create(const char *name, struct intr_handler *ih); #else +static int intr_event_schedule_thread(struct intr_event *ie); static struct intr_thread *ithread_create(const char *name); #endif static void ithread_destroy(struct intr_thread *ithread); @@ -239,8 +244,9 @@ intr_event_update(struct intr_event *ie) int intr_event_create(struct intr_event **event, void *source,int flags, - void (*disable)(void *), void (*enable)(void *), void (*eoi)(void *), - int (*assign_cpu)(void *, u_char), const char *fmt, ...) + void (*pre_ithread)(void *), void (*post_ithread)(void *), + void (*post_filter)(void *), int (*assign_cpu)(void *, u_char), + const char *fmt, ...) { struct intr_event *ie; va_list ap; @@ -250,9 +256,9 @@ intr_event_create(struct intr_event **event, void *source,int flags, return (EINVAL); ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO); ie->ie_source = source; - ie->ie_disable = disable; - ie->ie_enable = enable; - ie->ie_eoi = eoi; + ie->ie_pre_ithread = pre_ithread; + ie->ie_post_ithread = post_ithread; + ie->ie_post_filter = post_filter; ie->ie_assign_cpu = assign_cpu; ie->ie_flags = flags; ie->ie_cpu = NOCPU; @@ -675,7 +681,7 @@ ok: return (0); } -int +static int intr_event_schedule_thread(struct intr_event *ie) { struct intr_entropy entropy; @@ -832,7 +838,7 @@ ok: return (0); } -int +static int intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it) { struct intr_entropy entropy; @@ -1084,8 +1090,8 @@ ithread_execute_handlers(struct proc *p, struct intr_event *ie) * Now that all the handlers have had a chance to run, reenable * the interrupt source. */ - if (ie->ie_enable != NULL) - ie->ie_enable(ie->ie_source); + if (ie->ie_post_ithread != NULL) + ie->ie_post_ithread(ie->ie_source); } #ifndef INTR_FILTER @@ -1172,6 +1178,90 @@ ithread_loop(void *arg) thread_unlock(td); } } + +/* + * Main interrupt handling body. + * + * Input: + * o ie: the event connected to this interrupt. + * o frame: some archs (i.e. i386) pass a frame to some. + * handlers as their main argument. + * Return value: + * o 0: everything ok. + * o EINVAL: stray interrupt. + */ +int +intr_event_handle(struct intr_event *ie, struct trapframe *frame) +{ + struct intr_handler *ih; + struct thread *td; + int error, ret, thread; + + td = curthread; + + /* An interrupt with no event or handlers is a stray interrupt. */ + if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers)) + return (EINVAL); + + /* + * 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. + */ + td->td_intr_nesting_level++; + thread = 0; + ret = 0; + critical_enter(); + TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) { + if (ih->ih_filter == NULL) { + thread = 1; + continue; + } + CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__, + ih->ih_filter, ih->ih_argument == NULL ? frame : + ih->ih_argument, ih->ih_name); + if (ih->ih_argument == NULL) + ret = ih->ih_filter(frame); + else + ret = ih->ih_filter(ih->ih_argument); + /* + * Wrapper handler special handling: + * + * in some particular cases (like pccard and pccbb), + * the _real_ device handler is wrapped in a couple of + * functions - a filter wrapper and an ithread wrapper. + * In this case (and just in this case), the filter wrapper + * could ask the system to schedule the ithread and mask + * the interrupt source if the wrapped handler is composed + * of just an ithread handler. + * + * TODO: write a generic wrapper to avoid people rolling + * their own + */ + if (!thread) { + if (ret == FILTER_SCHEDULE_THREAD) + thread = 1; + } + } + + if (thread) { + if (ie->ie_pre_ithread != NULL) + ie->ie_pre_ithread(ie->ie_source); + } else { + if (ie->ie_post_filter != NULL) + ie->ie_post_filter(ie->ie_source); + } + + /* Schedule the ithread if needed. */ + if (thread) { + error = intr_event_schedule_thread(ie); + KASSERT(error == 0, ("bad stray interrupt")); + } + critical_exit(); + td->td_intr_nesting_level--; + return (0); +} #else /* * This is the main code for interrupt threads. @@ -1287,7 +1377,7 @@ ithread_loop(void *arg) * scheduled. */ -int +static int intr_filter_loop(struct intr_event *ie, struct trapframe *frame, struct intr_thread **ithd) { @@ -1363,19 +1453,13 @@ intr_event_handle(struct intr_event *ie, struct trapframe *frame) td->td_intr_nesting_level++; thread = 0; critical_enter(); - thread = intr_filter_loop(ie, frame, &ithd); - - /* - * If the interrupt was fully served, send it an EOI but leave - * it unmasked. Otherwise, mask the source as well as sending - * it an EOI. - */ + thread = intr_filter_loop(ie, frame, &ithd); if (thread & FILTER_HANDLED) { - if (ie->ie_eoi != NULL) - ie->ie_eoi(ie->ie_source); + if (ie->ie_post_filter != NULL) + ie->ie_post_filter(ie->ie_source); } else { - if (ie->ie_disable != NULL) - ie->ie_disable(ie->ie_source); + if (ie->ie_pre_ithread != NULL) + ie->ie_pre_ithread(ie->ie_source); } critical_exit(); -- cgit v1.1