kqueue: drain kqueue taskqueue if syscall tickled it

Otherwise return from the syscall and next syscall, which could be
kevent(2) on the kqueue that should be notified, races with the kqueue
taskqueue thread, and potentially misses the wakeup.  This is reliably
visible when kevent(2) only peeks into events using zeroed timeout.

PR:	258310
Reported by:	arichardson, Jan Kokemüller <jan.kokemueller@gmail.com>
Reviewed by:	arichardson, markj
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D31858
This commit is contained in:
Konstantin Belousov 2021-09-06 14:43:06 +03:00
parent 936f4a42fa
commit 98168a6e6c
4 changed files with 19 additions and 2 deletions

View File

@ -1768,9 +1768,16 @@ kqueue_release(struct kqueue *kq, int locked)
KQ_UNLOCK(kq);
}
void
kqueue_drain_schedtask(void)
{
taskqueue_quiesce(taskqueue_kqueue_ctx);
}
static void
kqueue_schedtask(struct kqueue *kq)
{
struct thread *td;
KQ_OWNED(kq);
KASSERT(((kq->kq_state & KQ_TASKDRAIN) != KQ_TASKDRAIN),
@ -1779,6 +1786,10 @@ kqueue_schedtask(struct kqueue *kq)
if ((kq->kq_state & KQ_TASKSCHED) != KQ_TASKSCHED) {
taskqueue_enqueue(taskqueue_kqueue_ctx, &kq->kq_task);
kq->kq_state |= KQ_TASKSCHED;
td = curthread;
thread_lock(td);
td->td_flags |= TDF_ASTPENDING | TDF_KQTICKLED;
thread_unlock(td);
}
}

View File

@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/capsicum.h>
#include <sys/event.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/msan.h>
@ -241,7 +242,8 @@ ast(struct trapframe *framep)
thread_lock(td);
flags = td->td_flags;
td->td_flags &= ~(TDF_ASTPENDING | TDF_NEEDSIGCHK | TDF_NEEDSUSPCHK |
TDF_NEEDRESCHED | TDF_ALRMPEND | TDF_PROFPEND | TDF_MACPEND);
TDF_NEEDRESCHED | TDF_ALRMPEND | TDF_PROFPEND | TDF_MACPEND |
TDF_KQTICKLED);
thread_unlock(td);
VM_CNT_INC(v_trap);
@ -343,6 +345,9 @@ ast(struct trapframe *framep)
resched_sigs = false;
}
if ((flags & TDF_KQTICKLED) != 0)
kqueue_drain_schedtask();
/*
* Handle deferred update of the fast sigblock value, after
* the postsig() loop was performed.

View File

@ -349,6 +349,7 @@ int kqfd_register(int fd, struct kevent *kev, struct thread *p,
int mflag);
int kqueue_add_filteropts(int filt, struct filterops *filtops);
int kqueue_del_filteropts(int filt);
void kqueue_drain_schedtask(void);
#else /* !_KERNEL */

View File

@ -451,7 +451,7 @@ do { \
#define TDF_ALLPROCSUSP 0x00000200 /* suspended by SINGLE_ALLPROC */
#define TDF_BOUNDARY 0x00000400 /* Thread suspended at user boundary */
#define TDF_ASTPENDING 0x00000800 /* Thread has some asynchronous events. */
#define TDF_UNUSED12 0x00001000 /* --available-- */
#define TDF_KQTICKLED 0x00001000 /* AST drain kqueue taskqueue */
#define TDF_SBDRY 0x00002000 /* Stop only on usermode boundary. */
#define TDF_UPIBLOCKED 0x00004000 /* Thread blocked on user PI mutex. */
#define TDF_NEEDSUSPCHK 0x00008000 /* Thread may need to suspend. */