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