svn commit: r357744 - head/sys/kern
Hans Petter Selasky
hselasky at FreeBSD.org
Mon Feb 10 20:23:09 UTC 2020
Author: hselasky
Date: Mon Feb 10 20:23:08 2020
New Revision: 357744
URL: https://svnweb.freebsd.org/changeset/base/357744
Log:
Fix for unbalanced EPOCH(9) usage in the generic kernel interrupt
handler.
Interrupt handlers are removed via intr_event_execute_handlers() when
IH_DEAD is set. The thread removing the interrupt is woken up, and
calls intr_event_update(). When this happens, the ie_hflags are
cleared and re-built from all the remaining handlers sharing the
event. When the last IH_NET handler is removed, the IH_NET flag will
be cleared from ih_hflags (or ie_hflags may still be being rebuilt in
a different context), and the ithread_execute_handlers() may return
with ie_hflags missing IH_NET. This can lead to a scenario where
IH_NET was present before calling ithread_execute_handlers, and is not
present at its return, meaning the need for epoch must be cached
locally.
This can happen when loading and unloading network drivers. Also make
sure the ie_hflags is not cleared before being updated.
This is a regression issue after r357004.
Backtrace:
panic()
# trying to access epoch tracker on stack of dead thread
_epoch_enter_preempt()
ifunit_ref()
ifioctl()
fo_ioctl()
kern_ioctl()
sys_ioctl()
syscallenter()
amd64_syscall()
Differential Revision: https://reviews.freebsd.org/D23483
Reviewed by: glebius@, gallatin@, mav@, jeff@ and kib@
Sponsored by: Mellanox Technologies
Modified:
head/sys/kern/kern_intr.c
Modified: head/sys/kern/kern_intr.c
==============================================================================
--- head/sys/kern/kern_intr.c Mon Feb 10 18:28:02 2020 (r357743)
+++ head/sys/kern/kern_intr.c Mon Feb 10 20:23:08 2020 (r357744)
@@ -189,12 +189,12 @@ intr_event_update(struct intr_event *ie)
{
struct intr_handler *ih;
char *last;
- int missed, space;
+ int missed, space, flags;
/* Start off with no entropy and just the name of the event. */
mtx_assert(&ie->ie_lock, MA_OWNED);
strlcpy(ie->ie_fullname, ie->ie_name, sizeof(ie->ie_fullname));
- ie->ie_hflags = 0;
+ flags = 0;
missed = 0;
space = 1;
@@ -207,8 +207,9 @@ intr_event_update(struct intr_event *ie)
space = 0;
} else
missed++;
- ie->ie_hflags |= ih->ih_flags;
+ flags |= ih->ih_flags;
}
+ ie->ie_hflags = flags;
/*
* If there is only one handler and its name is too long, just copy in
@@ -1208,6 +1209,7 @@ ithread_loop(void *arg)
struct thread *td;
struct proc *p;
int wake, epoch_count;
+ bool needs_epoch;
td = curthread;
p = td->td_proc;
@@ -1242,20 +1244,22 @@ ithread_loop(void *arg)
* that the load of ih_need in ithread_execute_handlers()
* is ordered after the load of it_need here.
*/
- if (ie->ie_hflags & IH_NET) {
+ needs_epoch =
+ (atomic_load_int(&ie->ie_hflags) & IH_NET) != 0;
+ if (needs_epoch) {
epoch_count = 0;
NET_EPOCH_ENTER(et);
}
while (atomic_cmpset_acq_int(&ithd->it_need, 1, 0) != 0) {
ithread_execute_handlers(p, ie);
- if ((ie->ie_hflags & IH_NET) &&
+ if (needs_epoch &&
++epoch_count >= intr_epoch_batch) {
NET_EPOCH_EXIT(et);
epoch_count = 0;
NET_EPOCH_ENTER(et);
}
}
- if (ie->ie_hflags & IH_NET)
+ if (needs_epoch)
NET_EPOCH_EXIT(et);
WITNESS_WARN(WARN_PANIC, NULL, "suspending ithread");
mtx_assert(&Giant, MA_NOTOWNED);
More information about the svn-src-all
mailing list