PERFORCE change 111336 for review
Paolo Pisati
piso at FreeBSD.org
Sat Dec 9 09:26:03 PST 2006
http://perforce.freebsd.org/chv.cgi?CH=111336
Change 111336 by piso at piso_newluxor on 2006/12/09 17:25:17
Add a private per handler ithread for every filtered driver.
Affected files ...
.. //depot/projects/soc2006/intr_filter/i386/i386/intr_machdep.c#16 edit
.. //depot/projects/soc2006/intr_filter/kern/kern_intr.c#18 edit
.. //depot/projects/soc2006/intr_filter/sys/interrupt.h#7 edit
Differences ...
==== //depot/projects/soc2006/intr_filter/i386/i386/intr_machdep.c#16 (text+ko) ====
@@ -235,6 +235,7 @@
{
struct thread *td;
struct intr_event *ie;
+ struct intr_thread *ithd = NULL;
int error, vector, thread;
td = curthread;
@@ -275,9 +276,9 @@
}
td->td_intr_nesting_level++;
- thread = 0;
+ thread = 0;
critical_enter();
- thread = intr_filter_loop(ie, frame);
+ thread = intr_filter_loop(ie, frame, &ithd);
/*
* If the interrupt was fully served, send it an EOI but leave it
@@ -299,9 +300,9 @@
mtx_unlock_spin(&intr_table_lock);
}
- /* Schedule the ithread if needed. */
+ /* Schedule an ithread if needed. */
if (thread & FILTER_SCHEDULE_THREAD) {
- error = intr_event_schedule_thread(ie);
+ error = intr_event_schedule_thread(ie, ithd);
KASSERT(error == 0, ("bad stray interrupt"));
}
td->td_intr_nesting_level--;
==== //depot/projects/soc2006/intr_filter/kern/kern_intr.c#18 (text+ko) ====
@@ -96,9 +96,12 @@
TAILQ_HEAD_INITIALIZER(event_list);
static void intr_event_update(struct intr_event *ie);
-static struct intr_thread *ithread_create(const char *name);
+static struct intr_thread *ithread_create(const char *name,
+ struct intr_handler *ih);
static void ithread_destroy(struct intr_thread *ithread);
static void ithread_execute_handlers(struct proc *p, struct intr_event *ie);
+static void priv_ithread_execute_handler(struct proc *p,
+ struct intr_handler *ih);
static void ithread_loop(void *);
static void ithread_update(struct intr_thread *ithd);
static void start_softintr(void *);
@@ -286,7 +289,7 @@
}
static struct intr_thread *
-ithread_create(const char *name)
+ithread_create(const char *name, struct intr_handler *ih)
{
struct intr_thread *ithd;
struct thread *td;
@@ -295,7 +298,7 @@
ithd = malloc(sizeof(struct intr_thread), M_ITHREAD, M_WAITOK | M_ZERO);
- error = kthread_create(ithread_loop, ithd, &p, RFSTOPPED | RFHIGHPID,
+ error = kthread_create(ithread_loop, ih, &p, RFSTOPPED | RFHIGHPID,
0, "%s", name);
if (error)
panic("kthread_create() failed with %d", error);
@@ -378,20 +381,29 @@
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);
+ /* For filtered handlers, create a private ithread to run on. */
+ if (filter != NULL && handler != NULL) {
+ mtx_unlock(&ie->ie_lock);
+ it = ithread_create("intr: newborn", ih);
+ mtx_lock(&ie->ie_lock);
+ it->it_event = ie;
+ ih->ih_thread = it;
+ ithread_update(it); // XXX - do we really need this?!?!?
+ } else { /* Create the global per-event 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", ih);
+ 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,
@@ -408,6 +420,7 @@
{
struct intr_handler *handler = (struct intr_handler *)cookie;
struct intr_event *ie;
+ struct intr_thread *it;
#ifdef INVARIANTS
struct intr_handler *ih;
#endif
@@ -434,17 +447,19 @@
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 there are no ithreads (per event and per handler), 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) {
+ if (ie->ie_thread == NULL && handler->ih_thread == NULL) {
TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
mtx_unlock(&ie->ie_lock);
free(handler, M_ITHREAD);
return (0);
}
+ /* Private or global ithread? */
+ it = (handler->ih_thread) ? handler->ih_thread : ie->ie_thread;
/*
* If the interrupt thread is already running, then just mark this
* handler as being dead and let the ithread do the actual removal.
@@ -454,7 +469,7 @@
* thread do it.
*/
mtx_lock_spin(&sched_lock);
- if (!TD_AWAITING_INTR(ie->ie_thread->it_thread) && !cold) {
+ if (!TD_AWAITING_INTR(it->it_thread) && !cold) {
handler->ih_flags |= IH_DEAD;
/*
@@ -462,12 +477,20 @@
* again and remove this handler if it has already passed
* it on the list.
*/
- ie->ie_thread->it_need = 1;
+ it->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);
+ /*
+ * At this point, the handler has been disconnected from the event,
+ * so we can kill the private ithread if any.
+ */
+ if (handler->ih_thread) {
+ ithread_destroy(handler->ih_thread);
+ handler->ih_thread = NULL;
+ }
intr_event_update(ie);
#ifdef notyet
/*
@@ -493,10 +516,9 @@
}
int
-intr_event_schedule_thread(struct intr_event *ie)
+intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it)
{
struct intr_entropy entropy;
- struct intr_thread *it;
struct thread *td;
struct thread *ctd;
struct proc *p;
@@ -504,12 +526,10 @@
/*
* If no ithread or no handlers, then we have a stray interrupt.
*/
- if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers) ||
- ie->ie_thread == NULL)
+ if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers) || it == NULL)
return (EINVAL);
ctd = curthread;
- it = ie->ie_thread;
td = it->it_thread;
p = td->td_proc;
@@ -603,7 +623,7 @@
if (!(flags & SWI_DELAY)) {
PCPU_LAZY_INC(cnt.v_soft);
- error = intr_event_schedule_thread(ie);
+ error = intr_event_schedule_thread(ie, ie->ie_thread);
KASSERT(error == 0, ("stray software interrupt"));
}
}
@@ -622,6 +642,37 @@
}
static void
+priv_ithread_execute_handler(struct proc *p, struct intr_handler *ih)
+{
+ struct intr_event *ie;
+
+ ie = ih->ih_event;
+ /*
+ * If this handler is marked for death, remove it from
+ * the list of handlers and wake up the sleeper.
+ */
+ if (ih->ih_flags & IH_DEAD) {
+ mtx_lock(&ie->ie_lock);
+ TAILQ_REMOVE(&ie->ie_handlers, ih, ih_next);
+ ih->ih_flags &= ~IH_DEAD;
+ wakeup(ih);
+ mtx_unlock(&ie->ie_lock);
+ return;
+ }
+
+ /* Execute this handler. */
+ CTR6(KTR_INTR, "%s: pid %d exec %p(%p) for %s flg=%x",
+ __func__, p->p_pid, (void *)ih->ih_handler, ih->ih_argument,
+ ih->ih_name, ih->ih_flags);
+
+ if (!(ih->ih_flags & IH_MPSAFE))
+ mtx_lock(&Giant);
+ ih->ih_handler(ih->ih_argument);
+ if (!(ih->ih_flags & IH_MPSAFE))
+ mtx_unlock(&Giant);
+}
+
+static void
ithread_execute_handlers(struct proc *p, struct intr_event *ie)
{
struct intr_handler *ih, *ihn;
@@ -645,12 +696,16 @@
}
/*
- * Execute handlers that have their need flag set.
- */
- if (!ih->ih_need)
- continue;
- else
- atomic_store_rel_int(&ih->ih_need, 0);
+ * For software interrupt threads, we only execute
+ * handlers that have their need flag set. Hardware
+ * interrupt threads always invoke all of their handlers.
+ */
+ if (ie->ie_flags & IE_SOFT) {
+ if (!ih->ih_need)
+ continue;
+ else
+ atomic_store_rel_int(&ih->ih_need, 0);
+ }
/* Execute this handler. */
CTR6(KTR_INTR, "%s: pid %d exec %p(%p) for %s flg=%x",
@@ -702,13 +757,17 @@
ithread_loop(void *arg)
{
struct intr_thread *ithd;
+ struct intr_handler *ih;
struct intr_event *ie;
struct thread *td;
struct proc *p;
+ int priv;
td = curthread;
p = td->td_proc;
- ithd = (struct intr_thread *)arg;
+ ih = (struct intr_handler *)arg;
+ priv = (ih->ih_thread != NULL) ? 1 : 0;
+ ithd = (priv) ? ih->ih_thread : ih->ih_event->ie_thread;
KASSERT(ithd->it_thread == td,
("%s: ithread and proc linkage out of sync", __func__));
ie = ithd->it_event;
@@ -742,7 +801,10 @@
* handlers.
*/
atomic_store_rel_int(&ithd->it_need, 0);
- ithread_execute_handlers(p, ie);
+ if (priv)
+ priv_ithread_execute_handler(p, ih);
+ else
+ ithread_execute_handlers(p, ie);
}
WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
mtx_assert(&Giant, MA_NOTOWNED);
@@ -768,13 +830,30 @@
* Some architectures (i386, amd64 and arm) require the optional frame
* parameter, and use it as the main argument for fast handler execution
* when ih_argument == NULL.
+ *
+ * Return value:
+ * o FILTER_STRAY: No filter recognized the event, and no
+ * filter-less handler is registered on this
+ * line.
+ * o FILTER_HANDLED: A filter claimed the event and served it.
+ * o FILTER_SCHEDULE_THREAD: No filter claimed the event, but there's at
+ * least one filter-less handler on this line.
+ * o FILTER_HANDLED |
+ * FILTER_SCHEDULE_THREAD: A filter claimed the event, and asked for
+ * scheduling the per-handler ithread.
+ *
+ * In case an ithread has to be scheduled, in *ithd there will be a
+ * pointer to a struct intr_thread containing the thread to be
+ * scheduled.
*/
int
-intr_filter_loop(struct intr_event *ie, struct trapframe *frame) {
+intr_filter_loop(struct intr_event *ie, struct trapframe *frame,
+ struct intr_thread **ithd)
+{
struct intr_handler *ih;
void *arg;
- int ret, ret2, thread_only;
+ int ret, thread_only;
ret = 0;
thread_only = 0;
@@ -791,45 +870,30 @@
ih->ih_filter, ih->ih_handler, arg, ih->ih_name);
if (ih->ih_filter != NULL)
- ret2 = ih->ih_filter(arg);
- else {
- /* Legacy ithread only handler. */
+ ret = ih->ih_filter(arg);
+ else {
thread_only = 1;
continue;
}
- /* Mark handler for later execution in ithread. */
- if (ret2 & FILTER_SCHEDULE_THREAD) {
- ih->ih_need = 1;
- ret |= FILTER_SCHEDULE_THREAD;
+ KASSERT(ret != FILTER_SCHEDULE_THREAD,
+ "intr_filter_loop: FILTER_SCHEDULE_THREAD from filter");
+
+ if (ret & FILTER_STRAY)
continue;
- }
-
- /* Interrupt served in filter. */
- if (ret2 & FILTER_HANDLED) {
- ret |= FILTER_HANDLED;
+ else {
+ *ithd = ih->ih_thread;
return (ret);
}
}
/*
- * A filter did claim the interrupt but didn't shut it up
- * fully, so schedule the ithread.
- */
- if (ret != 0)
- return (ret);
-
- /*
* No filters handled the interrupt and we have at least
* one handler without a filter. In this case, we schedule
* all of the filter-less handlers to run in the ithread.
*/
if (thread_only) {
- TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
- if (ih->ih_filter != NULL)
- continue;
- ih->ih_need = 1;
- }
+ *ithd = ie->ie_thread;
return (FILTER_SCHEDULE_THREAD);
}
return (FILTER_STRAY);
@@ -839,6 +903,7 @@
void
stray_detection(void *_arg)
{
+ struct intr_thread *ithd = NULL;
struct intr_event *ie;
void *(*walk_src)(void) = _arg;
int thread;
@@ -852,7 +917,7 @@
/*
* yes, it's still pending: call filters...
*/
- thread = intr_filter_loop(ie, NULL /* XXX frame */ );
+ thread = intr_filter_loop(ie, NULL /* XXX frame */, &ithd);
if (thread & FILTER_STRAY) {
/*
* no filter claimed the intr,
==== //depot/projects/soc2006/intr_filter/sys/interrupt.h#7 (text+ko) ====
@@ -52,6 +52,7 @@
int ih_need; /* Needs service. */
TAILQ_ENTRY(intr_handler) ih_next; /* Next handler for this event. */
u_char ih_pri; /* Priority of this handler. */
+ struct intr_thread *ih_thread; /* Ithread for filtered handler. */
};
#define IS_FAST(filter, handler) (filter != NULL && handler == NULL)
@@ -115,7 +116,8 @@
#ifdef DDB
void db_dump_intr_event(struct intr_event *ie, int handlers);
#endif
-int intr_filter_loop(struct intr_event *ie, struct trapframe *frame);
+int intr_filter_loop(struct intr_event *ie, struct trapframe *frame,
+ struct intr_thread **ithd);
void stray_detection(void *_arg);
u_char intr_priority(enum intr_type flags);
int intr_event_add_handler(struct intr_event *ie, const char *name,
@@ -127,7 +129,7 @@
__printflike(6, 7);
int intr_event_destroy(struct intr_event *ie);
int intr_event_remove_handler(void *cookie);
-int intr_event_schedule_thread(struct intr_event *ie);
+int intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *ithd);
int swi_add(struct intr_event **eventp, const char *name,
driver_intr_t handler, void *arg, int pri, enum intr_type flags,
void **cookiep);
More information about the p4-projects
mailing list