Re: git: 1f4b0ea4f3eb - main - kqueue: Add a helper macro for sleeping on in-flux knotes
Date: Wed, 29 Jul 2026 11:05:43 UTC
Hi Mark,
This is resulting in relatively easy to trigger panic with 'syncthing'
running under INVARIANTS:
panic: kqueue_scan: knote 0xfffffe018aeab930 not on kqueue 0xfffff80010aa2400
The panic occurs at the new KASSERT in KQ_FLUX_SLEEP_WMESG(), called
from kqueue_scan() line 2197. Two Syncthing threads were concurrently
scanning the same kqueue.
kgdb showed that the knote named in the panic was exactly the other
scanning thread’s local marker:
&marker = 0xfffffe018aeab930
marker.kn_status = KN_MARKER
marker.kn_kq = NULL
kq = 0xfffff80010aa2400
I have the full backtraces if useful.
Regards,
Kevin
On Mon, Jul 27, 2026 at 4:15 PM Mark Johnston <markj@freebsd.org> wrote:
>
> The branch main has been updated by markj:
>
> URL: https://cgit.FreeBSD.org/src/commit/?id=1f4b0ea4f3eb1b8a885eff8bd0d332156f0c3e1f
>
> commit 1f4b0ea4f3eb1b8a885eff8bd0d332156f0c3e1f
> Author: Mark Johnston <markj@FreeBSD.org>
> AuthorDate: 2026-07-27 23:12:16 +0000
> Commit: Mark Johnston <markj@FreeBSD.org>
> CommitDate: 2026-07-27 23:12:16 +0000
>
> kqueue: Add a helper macro for sleeping on in-flux knotes
>
> Other in-flux operations are implemented by this set of macros, so we
> should do the same for sleeping.
>
> No functional change intended.
>
> Reviewed by: kib
> MFC after: 1 week
> Sponsored by: The FreeBSD Foundation
> Differential Revision: https://reviews.freebsd.org/D58443
> ---
> sys/kern/kern_event.c | 32 ++++++++++++++++----------------
> 1 file changed, 16 insertions(+), 16 deletions(-)
>
> diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
> index 23e9d309f74c..60c3b18a07eb 100644
> --- a/sys/kern/kern_event.c
> +++ b/sys/kern/kern_event.c
> @@ -257,6 +257,14 @@ SYSCTL_UINT(_kern, OID_AUTO, kq_calloutmax, CTLFLAG_RW,
> wakeup((kq)); \
> } \
> } while (0)
> +#define KQ_FLUX_SLEEP_WMESG(kq, kn, flags, wmesg) do { \
> + KASSERT((kn)->kn_kq == (kq), \
> + ("%s: knote %p not on kqueue %p", __func__, kn, kq)); \
> + (kq)->kq_state |= KQ_FLUXWAIT; \
> + msleep((kq), &(kq)->kq_lock, PSOCK | (flags), (wmesg), 0); \
> +} while (0)
> +#define KQ_FLUX_SLEEP(kq, kn, flags) \
> + KQ_FLUX_SLEEP_WMESG(kq, kn, flags, "kqfluxwt")
> #define KQ_UNLOCK_FLUX(kq) do { \
> KQ_FLUX_WAKEUP(kq); \
> mtx_unlock(&(kq)->kq_lock); \
> @@ -1789,8 +1797,7 @@ findkn:
> FILEDESC_XUNLOCK(td->td_proc->p_fd);
> filedesc_unlock = 0;
> }
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK | PDROP, "kqflxwt", 0);
> + KQ_FLUX_SLEEP(kq, kn, PDROP);
> if (fp != NULL) {
> fdrop(fp, td);
> fp = NULL;
> @@ -2187,9 +2194,7 @@ retry:
> influx = 0;
> KQ_FLUX_WAKEUP(kq);
> }
> - kq->kq_state |= KQ_FLUXWAIT;
> - error = msleep(kq, &kq->kq_lock, PSOCK,
> - "kqflxwt", 0);
> + KQ_FLUX_SLEEP(kq, kn, 0);
> continue;
> }
>
> @@ -2423,8 +2428,7 @@ kqueue_drain(struct kqueue *kq, struct thread *td)
> for (i = 0; i < kq->kq_knlistsize; i++) {
> while ((kn = SLIST_FIRST(&kq->kq_knlist[i])) != NULL) {
> if (kn_in_flux(kn)) {
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK, "kqclo1", 0);
> + KQ_FLUX_SLEEP_WMESG(kq, kn, 0, "kqclo1");
> continue;
> }
> kn_enter_flux(kn);
> @@ -2437,9 +2441,8 @@ kqueue_drain(struct kqueue *kq, struct thread *td)
> for (i = 0; i <= kq->kq_knhashmask; i++) {
> while ((kn = SLIST_FIRST(&kq->kq_knhash[i])) != NULL) {
> if (kn_in_flux(kn)) {
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK,
> - "kqclo2", 0);
> + KQ_FLUX_SLEEP_WMESG(kq, kn, 0,
> + "kqclo2");
> continue;
> }
> kn_enter_flux(kn);
> @@ -2834,8 +2837,7 @@ knlist_cleardel(struct knlist *knl, struct thread *td, int islocked, int killkn)
> KQ_LOCK(kq);
> KASSERT(kn_in_flux(kn), ("knote removed w/o list lock"));
> knl->kl_unlock(knl->kl_lockarg);
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK | PDROP, "kqkclr", 0);
> + KQ_FLUX_SLEEP_WMESG(kq, kn, PDROP, "kqkclr");
> kq = NULL;
> knl->kl_lock(knl->kl_lockarg);
> }
> @@ -2881,8 +2883,7 @@ knote_fdclose(struct thread *td, int fd)
> * the case that it's in the process of being
> * dropped anyways.
> */
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK, "kqflxwt", 0);
> + KQ_FLUX_SLEEP(kq, kn, 0);
> continue;
> }
> kn_enter_flux(kn);
> @@ -2946,8 +2947,7 @@ knote_drop_detached(struct knote *kn, struct thread *td)
> kn, kn->kn_influx));
> if (kn->kn_influx == 1)
> break;
> - kq->kq_state |= KQ_FLUXWAIT;
> - msleep(kq, &kq->kq_lock, PSOCK, "kqflxwt", 0);
> + KQ_FLUX_SLEEP(kq, kn, 0);
> }
>
> MPASS(kn->kn_kq == kq);
>