From owner-p4-projects@FreeBSD.ORG Fri Mar 9 16:11:45 2007 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 7BF6916A474; Fri, 9 Mar 2007 16:11:45 +0000 (UTC) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 5612216A420 for ; Fri, 9 Mar 2007 16:11:45 +0000 (UTC) (envelope-from piso@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id 435D613C4A7 for ; Fri, 9 Mar 2007 16:11:45 +0000 (UTC) (envelope-from piso@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id l29GBjqJ075487 for ; Fri, 9 Mar 2007 16:11:45 GMT (envelope-from piso@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id l29GBipK075479 for perforce@freebsd.org; Fri, 9 Mar 2007 16:11:44 GMT (envelope-from piso@freebsd.org) Date: Fri, 9 Mar 2007 16:11:44 GMT Message-Id: <200703091611.l29GBipK075479@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to piso@freebsd.org using -f From: Paolo Pisati To: Perforce Change Reviews Cc: Subject: PERFORCE change 115609 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 09 Mar 2007 16:11:45 -0000 http://perforce.freebsd.org/chv.cgi?CH=115609 Change 115609 by piso@piso_newluxor on 2007/03/09 16:11:39 Bring back the old MI interrupt handling code, merge it with the new interrupt filtering code, and appropriately #ifdef ... #endif both worlds. While here, axe a stale reference to stray_detection() that is no more. Affected files ... .. //depot/projects/soc2006/intr_filter/kern/kern_intr.c#38 edit .. //depot/projects/soc2006/intr_filter/sys/interrupt.h#19 edit Differences ... ==== //depot/projects/soc2006/intr_filter/kern/kern_intr.c#38 (text+ko) ==== @@ -92,13 +92,19 @@ TAILQ_HEAD_INITIALIZER(event_list); static void intr_event_update(struct intr_event *ie); -static struct intr_thread *ithread_create(const char *name, +#ifdef INTR_FILTER +static struct intr_thread *ithread_create(const char *name, struct intr_handler *ih); +#else +static struct intr_thread *ithread_create(const char *name); +#endif static void ithread_destroy(struct intr_thread *ithread); static void ithread_execute_handlers(struct proc *p, struct intr_event *ie); +#ifdef INTR_FILTER static void priv_ithread_execute_handler(struct proc *p, struct intr_handler *ih); +#endif static void ithread_loop(void *); static void ithread_update(struct intr_thread *ithd); static void start_softintr(void *); @@ -231,6 +237,37 @@ CTR2(KTR_INTR, "%s: updated %s", __func__, ie->ie_fullname); } +#ifndef INTR_FILTER +int +intr_event_create(struct intr_event **event, void *source, int flags, + void (*enable)(void *), const char *fmt, ...) +{ + struct intr_event *ie; + va_list ap; + + /* The only valid flag during creation is IE_SOFT. */ + if ((flags & ~IE_SOFT) != 0) + return (EINVAL); + ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO); + ie->ie_source = source; + ie->ie_enable = enable; + ie->ie_flags = flags; + TAILQ_INIT(&ie->ie_handlers); + mtx_init(&ie->ie_lock, "intr event", NULL, MTX_DEF); + + va_start(ap, fmt); + vsnprintf(ie->ie_name, sizeof(ie->ie_name), fmt, ap); + va_end(ap); + strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname)); + mtx_pool_lock(mtxpool_sleep, &event_list); + TAILQ_INSERT_TAIL(&event_list, ie, ie_list); + mtx_pool_unlock(mtxpool_sleep, &event_list); + if (event != NULL) + *event = ie; + CTR2(KTR_INTR, "%s: created %s", __func__, ie->ie_name); + return (0); +} +#else int intr_event_create(struct intr_event **event, void *source, int flags, void (*enable)(void *), void (*eoi)(void *), void (*disab)(void *), @@ -263,6 +300,7 @@ CTR2(KTR_INTR, "%s: created %s", __func__, ie->ie_name); return (0); } +#endif int intr_event_destroy(struct intr_event *ie) @@ -288,7 +326,33 @@ return (0); } +#ifndef INTR_FILTER static struct intr_thread * +ithread_create(const char *name) +{ + struct intr_thread *ithd; + struct thread *td; + struct proc *p; + int error; + + ithd = malloc(sizeof(struct intr_thread), M_ITHREAD, M_WAITOK | M_ZERO); + + error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID, + 0, "%s", name); + if (error) + panic("kthread_create() failed with %d", error); + td = FIRST_THREAD_IN_PROC(p); /* XXXKSE */ + mtx_lock_spin(&sched_lock); + sched_class(td, PRI_ITHD); + TD_SET_IWAIT(td); + mtx_unlock_spin(&sched_lock); + td->td_pflags |= TDP_ITHREAD; + ithd->it_thread = td; + CTR2(KTR_INTR, "%s: created %s", __func__, name); + return (ithd); +} +#else +static struct intr_thread * ithread_create(const char *name, struct intr_handler *ih) { struct intr_thread *ithd; @@ -312,6 +376,7 @@ CTR2(KTR_INTR, "%s: created %s", __func__, name); return (ithd); } +#endif static void ithread_destroy(struct intr_thread *ithread) @@ -329,6 +394,7 @@ mtx_unlock_spin(&sched_lock); } +#ifndef INTR_FILTER int intr_event_add_handler(struct intr_event *ie, const char *name, driver_filter_t filter, driver_intr_t handler, void *arg, u_char pri, @@ -377,6 +443,79 @@ TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next); intr_event_update(ie); + /* Create a thread if we need one. */ + while (ie->ie_thread == NULL && handler != NULL) { + if (ie->ie_flags & IE_ADDING_THREAD) + msleep(ie, &ie->ie_lock, 0, "ithread", 0); + else { + ie->ie_flags |= IE_ADDING_THREAD; + mtx_unlock(&ie->ie_lock); + it = ithread_create("intr: newborn"); + mtx_lock(&ie->ie_lock); + ie->ie_flags &= ~IE_ADDING_THREAD; + ie->ie_thread = it; + it->it_event = ie; + ithread_update(it); + wakeup(ie); + } + } + CTR3(KTR_INTR, "%s: added %s to %s", __func__, ih->ih_name, + ie->ie_name); + mtx_unlock(&ie->ie_lock); + + if (cookiep != NULL) + *cookiep = ih; + return (0); +} +#else +int +intr_event_add_handler(struct intr_event *ie, const char *name, + driver_filter_t filter, driver_intr_t handler, void *arg, u_char pri, + enum intr_type flags, void **cookiep) +{ + struct intr_handler *ih, *temp_ih; + struct intr_thread *it; + + if (ie == NULL || name == NULL || (handler == NULL && filter == NULL)) + return (EINVAL); + + /* Allocate and populate an interrupt handler structure. */ + ih = malloc(sizeof(struct intr_handler), M_ITHREAD, M_WAITOK | M_ZERO); + ih->ih_filter = filter; + ih->ih_handler = handler; + ih->ih_argument = arg; + ih->ih_name = name; + ih->ih_event = ie; + ih->ih_pri = pri; + 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; + + /* We can only have one exclusive handler in a event. */ + mtx_lock(&ie->ie_lock); + if (!TAILQ_EMPTY(&ie->ie_handlers)) { + if ((flags & INTR_EXCL) || + (TAILQ_FIRST(&ie->ie_handlers)->ih_flags & IH_EXCLUSIVE)) { + mtx_unlock(&ie->ie_lock); + free(ih, M_ITHREAD); + return (EINVAL); + } + } + + /* Add the new handler to the event in priority order. */ + TAILQ_FOREACH(temp_ih, &ie->ie_handlers, ih_next) { + if (temp_ih->ih_pri > ih->ih_pri) + break; + } + if (temp_ih == NULL) + TAILQ_INSERT_TAIL(&ie->ie_handlers, ih, ih_next); + else + TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next); + intr_event_update(ie); + /* For filtered handlers, create a private ithread to run on. */ if (filter != NULL && handler != NULL) { mtx_unlock(&ie->ie_lock); @@ -410,6 +549,7 @@ *cookiep = ih; return (0); } +#endif /* * Return the ie_source field from the intr_event an intr_handler is @@ -431,11 +571,158 @@ return (ie->ie_source); } +#ifndef INTR_FILTER int intr_event_remove_handler(void *cookie) { struct intr_handler *handler = (struct intr_handler *)cookie; struct intr_event *ie; +#ifdef INVARIANTS + struct intr_handler *ih; +#endif +#ifdef notyet + int dead; +#endif + + if (handler == NULL) + return (EINVAL); + ie = handler->ih_event; + KASSERT(ie != NULL, + ("interrupt handler \"%s\" has a NULL interrupt event", + handler->ih_name)); + mtx_lock(&ie->ie_lock); + CTR3(KTR_INTR, "%s: removing %s from %s", __func__, handler->ih_name, + ie->ie_name); +#ifdef INVARIANTS + TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) + if (ih == handler) + goto ok; + mtx_unlock(&ie->ie_lock); + panic("interrupt handler \"%s\" not found in interrupt event \"%s\"", + ih->ih_name, ie->ie_name); +ok: +#endif + /* + * If there is no ithread, then just remove the handler and return. + * XXX: Note that an INTR_FAST handler might be running on another + * CPU! + */ + if (ie->ie_thread == NULL) { + TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next); + mtx_unlock(&ie->ie_lock); + free(handler, M_ITHREAD); + return (0); + } + + /* + * If the interrupt thread is already running, then just mark this + * handler as being dead and let the ithread do the actual removal. + * + * During a cold boot while cold is set, msleep() does not sleep, + * so we have to remove the handler here rather than letting the + * thread do it. + */ + mtx_lock_spin(&sched_lock); + if (!TD_AWAITING_INTR(ie->ie_thread->it_thread) && !cold) { + handler->ih_flags |= IH_DEAD; + + /* + * Ensure that the thread will process the handler list + * again and remove this handler if it has already passed + * it on the list. + */ + ie->ie_thread->it_need = 1; + } else + TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next); + mtx_unlock_spin(&sched_lock); + while (handler->ih_flags & IH_DEAD) + msleep(handler, &ie->ie_lock, 0, "iev_rmh", 0); + intr_event_update(ie); +#ifdef notyet + /* + * XXX: This could be bad in the case of ppbus(8). Also, I think + * this could lead to races of stale data when servicing an + * interrupt. + */ + dead = 1; + TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) { + if (!(ih->ih_flags & IH_FAST)) { + dead = 0; + break; + } + } + if (dead) { + ithread_destroy(ie->ie_thread); + ie->ie_thread = NULL; + } +#endif + mtx_unlock(&ie->ie_lock); + free(handler, M_ITHREAD); + return (0); +} + +int +intr_event_schedule_thread(struct intr_event *ie) +{ + struct intr_entropy entropy; + struct intr_thread *it; + struct thread *td; + struct thread *ctd; + struct proc *p; + + /* + * If no ithread or no handlers, then we have a stray interrupt. + */ + if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers) || + ie->ie_thread == NULL) + return (EINVAL); + + ctd = curthread; + it = ie->ie_thread; + td = it->it_thread; + p = td->td_proc; + + /* + * If any of the handlers for this ithread claim to be good + * sources of entropy, then gather some. + */ + if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) { + CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__, + p->p_pid, p->p_comm); + entropy.event = (uintptr_t)ie; + entropy.td = ctd; + random_harvest(&entropy, sizeof(entropy), 2, 0, + RANDOM_INTERRUPT); + } + + KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name)); + + /* + * 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. + */ + it->it_need = 1; + mtx_lock_spin(&sched_lock); + if (TD_AWAITING_INTR(td)) { + CTR3(KTR_INTR, "%s: schedule pid %d (%s)", __func__, p->p_pid, + p->p_comm); + TD_CLR_IWAIT(td); + sched_add(td, SRQ_INTR); + } else { + CTR5(KTR_INTR, "%s: pid %d (%s): it_need %d, state %d", + __func__, p->p_pid, p->p_comm, it->it_need, td->td_state); + } + mtx_unlock_spin(&sched_lock); + + return (0); +} +#else +int +intr_event_remove_handler(void *cookie) +{ + struct intr_handler *handler = (struct intr_handler *)cookie; + struct intr_event *ie; struct intr_thread *it; #ifdef INVARIANTS struct intr_handler *ih; @@ -584,6 +871,7 @@ return (0); } +#endif /* * Add a software interrupt handler to a specified event. If a given event @@ -657,6 +945,7 @@ return (intr_event_remove_handler(cookie)); } +#ifdef INTR_FILTER static void priv_ithread_execute_handler(struct proc *p, struct intr_handler *ih) { @@ -687,6 +976,7 @@ if (!(ih->ih_flags & IH_MPSAFE)) mtx_unlock(&Giant); } +#endif static void ithread_execute_handlers(struct proc *p, struct intr_event *ie) @@ -771,6 +1061,74 @@ ie->ie_enable(ie->ie_source); } +#ifndef INTR_FILTER +/* + * This is the main code for interrupt threads. + */ +static void +ithread_loop(void *arg) +{ + struct intr_thread *ithd; + struct intr_event *ie; + struct thread *td; + struct proc *p; + + td = curthread; + p = td->td_proc; + ithd = (struct intr_thread *)arg; + KASSERT(ithd->it_thread == td, + ("%s: ithread and proc linkage out of sync", __func__)); + ie = ithd->it_event; + ie->ie_count = 0; + + /* + * 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); + free(ithd, M_ITHREAD); + kthread_exit(0); + } + + /* + * Service interrupts. If another interrupt arrives while + * we are running, it will set it_need to note that we + * should make another pass. + */ + while (ithd->it_need) { + /* + * This might need a full read and write barrier + * to make sure that this write posts before any + * of the memory or device accesses in the + * handlers. + */ + atomic_store_rel_int(&ithd->it_need, 0); + ithread_execute_handlers(p, ie); + } + WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread"); + mtx_assert(&Giant, MA_NOTOWNED); + + /* + * 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. + */ + mtx_lock_spin(&sched_lock); + if (!ithd->it_need && !(ithd->it_flags & IT_DEAD)) { + TD_SET_IWAIT(td); + ie->ie_count = 0; + mi_switch(SW_VOL, NULL); + } + mtx_unlock_spin(&sched_lock); + } +} +#else /* * This is the main code for interrupt threads. */ @@ -978,6 +1336,7 @@ td->td_intr_nesting_level--; return (0); } +#endif #ifdef DDB /* ==== //depot/projects/soc2006/intr_filter/sys/interrupt.h#19 (text+ko) ==== @@ -73,8 +73,10 @@ void *ie_source; /* Cookie used by MD code. */ struct intr_thread *ie_thread; /* Thread we are connected to. */ void (*ie_enable)(void *); +#ifdef INTR_FILTER void (*ie_eoi)(void *); void (*ie_disab)(void *); +#endif int ie_flags; int ie_count; /* Loop counter. */ int ie_warned; /* Warned about interrupt storm. */ @@ -115,21 +117,33 @@ #ifdef DDB void db_dump_intr_event(struct intr_event *ie, int handlers); #endif +#ifdef INTR_FILTER int intr_filter_loop(struct intr_event *ie, struct trapframe *frame, struct intr_thread **ithd); -void stray_detection(void *_arg); int intr_event_handle(struct intr_event *ie, struct trapframe *frame); +#endif u_char intr_priority(enum intr_type flags); int intr_event_add_handler(struct intr_event *ie, const char *name, driver_filter_t filter, driver_intr_t handler, void *arg, u_char pri, enum intr_type flags, void **cookiep); +#ifndef INTR_FILTER +int intr_event_create(struct intr_event **event, void *source, + int flags, void (*enable)(void *), const char *fmt, ...) + __printflike(5, 6); +#else int intr_event_create(struct intr_event **event, void *source, int flags, void (*enable)(void *), void (*eoi)(void *), void (*disab)(void *), const char *fmt, ...) __printflike(7, 8); +#endif int intr_event_destroy(struct intr_event *ie); int intr_event_remove_handler(void *cookie); -int intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *ithd); +#ifndef INTR_FILTER +int intr_event_schedule_thread(struct intr_event *ie); +#else +int intr_event_schedule_thread(struct intr_event *ie, + struct intr_thread *ithd); +#endif void *intr_handler_source(void *cookie); int swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler, void *arg, int pri, enum intr_type flags,