Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 May 2026 20:57:41 +0000
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 4a875b186e8d - stable/14 - eventhandler: Fix a race when pruning eventhandlers
Message-ID:  <6a19fdc5.3460f.f44f774@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=4a875b186e8d53e38f79a9e4c0e9ed8949e38ed7

commit 4a875b186e8d53e38f79a9e4c0e9ed8949e38ed7
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-05-06 11:48:05 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-05-29 19:44:43 +0000

    eventhandler: Fix a race when pruning eventhandlers
    
    By default, eventhandler_deregister() blocks until it reaches some point
    where no threads are invoking the event.  At this point, it knows that
    1) no threads are currently executing the handler,
    2) some thread has freed the eventhandler structure by virtue of having
       called eventhandler_prune_list(),
    so it is safe to return.
    
    Suppose a thread is trying to deregister an event handler.  A different
    thread prunes it, and wakes up the first thread.  Before the first
    thread runs, a third thread grabs the event handler lock, and starts
    executing handlers.  The first thread observes el_runcount > 0, and goes
    back to sleep.  The third thread sees no event handlers to prune, and
    doesn't wake up the first thread, which sleeps forever.
    
    This change fixes the race and tries to make eventhandler_invoke() more
    efficient: keep a count of the number of dead list entries and only
    prune the list if there is at least one dead entry.  Also, in
    eventhandler_deregister(), we only need to sleep if some dead entries
    are present, rather than sleeping whenever some thread is running
    handlers.
    
    Reviewed by:    kib
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D56767
    
    (cherry picked from commit 735b16d490aee158beb54c415b716475a0d19cda)
---
 sys/kern/subr_eventhandler.c | 18 ++++++++++++++----
 sys/sys/eventhandler.h       |  4 ++--
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/sys/kern/subr_eventhandler.c b/sys/kern/subr_eventhandler.c
index 5a5365d0be6e..ccbb07b826b8 100644
--- a/sys/kern/subr_eventhandler.c
+++ b/sys/kern/subr_eventhandler.c
@@ -199,7 +199,10 @@ _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag,
 	} else {
 	    CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__,
 		ep, list->el_name);
+	    KASSERT(ep->ee_priority != EHE_DEAD_PRIORITY,
+		("%s: handler for %s is dead", __func__, list->el_name));
 	    ep->ee_priority = EHE_DEAD_PRIORITY;
+	    list->el_deadcount++;
 	}
     } else {
 	/* remove entire list */
@@ -214,11 +217,15 @@ _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag,
 	} else {
 	    CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead",
 		__func__, list->el_name);
-	    TAILQ_FOREACH(ep, &list->el_entries, ee_link)
+	    TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
+		KASSERT(ep->ee_priority != EHE_DEAD_PRIORITY,
+		    ("%s: handler for %s is dead", __func__, list->el_name));
 		ep->ee_priority = EHE_DEAD_PRIORITY;
+		list->el_deadcount++;
+	    }
 	}
     }
-    while (wait && list->el_runcount > 0)
+    while (wait && list->el_deadcount > 0)
 	    mtx_sleep(list, &list->el_lock, 0, "evhrm", 0);
     EHL_UNLOCK(list);
 }
@@ -293,8 +300,11 @@ eventhandler_prune_list(struct eventhandler_list *list)
 	    pruned++;
 	}
     }
-    if (pruned > 0)
-	    wakeup(list);
+    KASSERT(pruned == list->el_deadcount,
+	("%s: pruned %u entries from \"%s\" but expected %u",
+	__func__, pruned, list->el_name, list->el_deadcount));
+    list->el_deadcount = 0;
+    wakeup(list);
 }
 
 /*
diff --git a/sys/sys/eventhandler.h b/sys/sys/eventhandler.h
index 47024ecf87a9..be1b04606320 100644
--- a/sys/sys/eventhandler.h
+++ b/sys/sys/eventhandler.h
@@ -45,7 +45,7 @@ struct eventhandler_entry_vimage {
 
 struct eventhandler_list {
 	char				*el_name;
-	int				el_flags;	/* Unused. */
+	u_int				el_deadcount;
 	u_int				el_runcount;
 	struct mtx			el_lock;
 	TAILQ_ENTRY(eventhandler_list)	el_link;
@@ -81,7 +81,7 @@ struct eventhandler_list {
 	KASSERT((list)->el_runcount > 0,				\
 	    ("eventhandler_invoke: runcount underflow"));		\
 	(list)->el_runcount--;						\
-	if ((list)->el_runcount == 0)					\
+	if ((list)->el_runcount == 0 && (list)->el_deadcount != 0)	\
 		eventhandler_prune_list(list);				\
 	EHL_UNLOCK((list));						\
 } while (0)


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a19fdc5.3460f.f44f774>