diff options
author | jhb <jhb@FreeBSD.org> | 2001-02-22 02:18:32 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2001-02-22 02:18:32 +0000 |
commit | bfd047a3c936c2ea237d09f71e53f3b02aae1497 (patch) | |
tree | 4b98153f7f2c6e9e7c78153cbc3f699eb673ed87 /sys/kern | |
parent | 0d396b87d337a6f2a1fea3ae75887fe04277c9ab (diff) | |
download | FreeBSD-src-bfd047a3c936c2ea237d09f71e53f3b02aae1497.zip FreeBSD-src-bfd047a3c936c2ea237d09f71e53f3b02aae1497.tar.gz |
Work around a race condition where an interrupt handler can be removed from
an interrupt thread while the interrupt thread is blocked on Giant waiting
to execute the interrupt handler being removed. The result was that the
intrhand structure would be free'd, and we would call 0xdeadc0de. The work
around is to check to see if the interrupt thread is idle when removing a
handler. If not, then we mark the interrupt handler as being dead using
the new IH_DEAD flag and don't remove it from the interrupt threads' list
of handlers. When the interrupt thread resumes, it will see a dead handler
while traversing the list of handlers and will remove the handler then.
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_intr.c | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index 1cd558e..1c37284 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -301,11 +301,29 @@ ithread_remove_handler(void *cookie) ih->ih_name, ithread->it_name); ok: #endif - TAILQ_REMOVE(&ithread->it_handlers, handler, ih_next); - ithread_update(ithread); + /* + * If the interrupt thread is already running, then just mark this + * handler as being dead and let the ithread do the actual removal. + */ + mtx_lock_spin(&sched_lock); + if (ithread->it_proc->p_stat != SWAIT) { + 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. + */ + ithread->it_need = 1; + } else { + TAILQ_REMOVE(&ithread->it_handlers, handler, ih_next); + ithread_update(ithread); + } + mtx_unlock_spin(&sched_lock); mtx_unlock_spin(&ithread_list_lock); - free(handler, M_ITHREAD); + if ((handler->ih_flags & IH_DEAD) == 0) + free(handler, M_ITHREAD); return (0); } @@ -468,6 +486,7 @@ ithread_loop(void *arg) * another pass. */ atomic_store_rel_int(&ithd->it_need, 0); +restart: TAILQ_FOREACH(ih, &ithd->it_handlers, ih_next) { if (ithd->it_flags & IT_SOFT && !ih->ih_need) continue; @@ -480,6 +499,18 @@ ithread_loop(void *arg) if ((ih->ih_flags & IH_MPSAFE) == 0) mtx_lock(&Giant); + if ((ih->ih_flags & IH_DEAD) != 0) { + mtx_lock_spin(&ithread_list_lock); + TAILQ_REMOVE(&ithd->it_handlers, ih, + ih_next); + ithread_update(ithd); + mtx_unlock_spin(&ithread_list_lock); + if (!mtx_owned(&Giant)) + mtx_lock(&Giant); + free(ih, M_ITHREAD); + mtx_unlock(&Giant); + goto restart; + } ih->ih_handler(ih->ih_argument); if ((ih->ih_flags & IH_MPSAFE) == 0) mtx_unlock(&Giant); |