Download raw body.
EVFILT_USER and kevent(2)
On Tue, May 06, 2025 at 03:38:57AM +0000, Visa Hankala wrote:
> On Sat, Apr 30, 2022 at 01:51:16PM +0000, Visa Hankala wrote:
> > It has been asked in the past if OpenBSD's kevent(2) should implement
> > user event filters, also known as EVFILT_USER. This filter type
> > originates from FreeBSD but is now available also on DragonFly BSD,
> > NetBSD, and macOS.
> >
> > Below is an implementation of EVFILT_USER. The logic should be fairly
> > straightforward. However, the filter type needs a special case in
> > kqueue_register() to allow triggering a previously registered user
> > event without using EV_ADD.
> >
> > The code limits the number of user events. Otherwise the user could
> > allocate copious amounts of kernel memory. The limit is per process
> > so that programs will not interfere with each other. The current limit
> > is arbitrary and might need adjusting later. Hopefully a sysctl knob
> > will not be necessary.
> >
> > I am in two minds about EVFILT_USER. On the one hand, having it on
> > OpenBSD might help with ports. On the other hand, it makes the kernel
> > perform a task that userspace can already handle using existing
> > interfaces.
>
> I wonder if there is more interest in EVFILT_USER now.
>
ok mvs@
> Here is a refreshed version of the patch:
>
> Index: lib/libc/sys/kqueue.2
> ===================================================================
> RCS file: src/lib/libc/sys/kqueue.2,v
> retrieving revision 1.51
> diff -u -p -r1.51 kqueue.2
> --- lib/libc/sys/kqueue.2 20 Aug 2023 19:52:40 -0000 1.51
> +++ lib/libc/sys/kqueue.2 6 May 2025 03:20:39 -0000
> @@ -561,6 +561,44 @@ e.g. an HDMI cable has been plugged in t
> On return,
> .Fa fflags
> contains the events which triggered the filter.
> +.It Dv EVFILT_USER
> +Establishes a user event identified by
> +.Va ident
> +which is not associated with any kernel mechanism but is triggered by
> +user level code.
> +The lower 24 bits of the
> +.Va fflags
> +may be used for user defined flags and manipulated using the following:
> +.Bl -tag -width XXNOTE_FFLAGSMASK
> +.It Dv NOTE_FFNOP
> +Ignore the input
> +.Va fflags .
> +.It Dv NOTE_FFAND
> +Bitwise AND
> +.Va fflags .
> +.It Dv NOTE_FFOR
> +Bitwise OR
> +.Va fflags .
> +.It Dv NOTE_FFCOPY
> +Copy
> +.Va fflags .
> +.It Dv NOTE_FFCTRLMASK
> +Control mask for
> +.Va fflags .
> +.It Dv NOTE_FFLAGSMASK
> +User defined flag mask for
> +.Va fflags .
> +.El
> +.Pp
> +A user event is triggered for output with the following:
> +.Bl -tag -width XXNOTE_FFLAGSMASK
> +.It Dv NOTE_TRIGGER
> +Cause the event to be triggered.
> +.El
> +.Pp
> +On return,
> +.Va fflags
> +contains the users defined flags in the lower 24 bits.
> .El
> .Sh RETURN VALUES
> .Fn kqueue
> Index: regress/sys/kern/kqueue/Makefile
> ===================================================================
> RCS file: src/regress/sys/kern/kqueue/Makefile,v
> retrieving revision 1.32
> diff -u -p -r1.32 Makefile
> --- regress/sys/kern/kqueue/Makefile 20 Aug 2023 15:19:34 -0000 1.32
> +++ regress/sys/kern/kqueue/Makefile 6 May 2025 03:20:40 -0000
> @@ -4,7 +4,8 @@ PROG= kqueue-test
> CFLAGS+=-Wall
> SRCS= kqueue-pipe.c kqueue-fork.c main.c kqueue-process.c kqueue-random.c \
> kqueue-pty.c kqueue-tun.c kqueue-signal.c kqueue-fdpass.c \
> - kqueue-exec.c kqueue-flock.c kqueue-timer.c kqueue-regress.c
> + kqueue-exec.c kqueue-flock.c kqueue-timer.c kqueue-regress.c \
> + kqueue-user.c
> LDADD= -levent -lutil
> DPADD= ${LIBEVENT} ${LIBUTIL}
>
> @@ -52,6 +53,8 @@ kq-regress-5: ${PROG}
> ./${PROG} -R5
> kq-regress-6: ${PROG}
> ./${PROG} -R6
> +kq-user: ${PROG}
> + ./${PROG} -u
>
> TESTS+= kq-exec
> TESTS+= kq-fdpass
> @@ -73,6 +76,7 @@ TESTS+= kq-reset-timer
> TESTS+= kq-signal
> TESTS+= kq-timer
> TESTS+= kq-tun
> +TESTS+= kq-user
>
> REGRESS_TARGETS=${TESTS}
> REGRESS_ROOT_TARGETS=kq-pty-1
> Index: regress/sys/kern/kqueue/kqueue-user.c
> ===================================================================
> RCS file: regress/sys/kern/kqueue/kqueue-user.c
> diff -N regress/sys/kern/kqueue/kqueue-user.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ regress/sys/kern/kqueue/kqueue-user.c 6 May 2025 03:20:40 -0000
> @@ -0,0 +1,189 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2022 Visa Hankala
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/types.h>
> +#include <sys/time.h>
> +#include <sys/event.h>
> +
> +#include <err.h>
> +#include <errno.h>
> +#include <unistd.h>
> +
> +#include "main.h"
> +
> +int
> +do_user(void)
> +{
> + const struct timespec ts = { 0, 10000 };
> + struct kevent kev[2];
> + int dummy, dummy2, i, kq, n;
> +
> + ASS((kq = kqueue()) >= 0,
> + warn("kqueue"));
> +
> + /* Set up an event. */
> + EV_SET(&kev[0], 1, EVFILT_USER, EV_ADD, ~0U & ~NOTE_TRIGGER, 0, NULL);
> + ASS(kevent(kq, kev, 1, NULL, 0, NULL) == 0,
> + warn("kevent"));
> +
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 0);
> +
> + /*
> + * Activate the event.
> + * Fields `data' and `udata' do not get updated without EV_ADD.
> + */
> + EV_SET(&kev[0], 1, EVFILT_USER, 0, NOTE_TRIGGER | NOTE_FFNOP,
> + 123, &dummy);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + /* Check active events. */
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 1);
> + ASSX(kev[0].fflags == NOTE_FFLAGSMASK);
> + ASSX(kev[0].data == 0);
> + ASSX(kev[0].udata == NULL);
> +
> + /* Activate the event. Update `data' and `udata'. */
> + EV_SET(&kev[0], 1, EVFILT_USER, EV_ADD, NOTE_TRIGGER | NOTE_FFNOP,
> + 123, &dummy);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + /* Check active events. */
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 1);
> + ASSX(kev[0].fflags == NOTE_FFLAGSMASK);
> + ASSX(kev[0].data == 123);
> + ASSX(kev[0].udata == &dummy);
> +
> + /* Set up another event. */
> + EV_SET(&kev[0], 2, EVFILT_USER, EV_ADD, NOTE_TRIGGER, 654, &dummy2);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + /* Check active events. This assumes a specific output order. */
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 2);
> + ASSX(kev[0].ident == 1);
> + ASSX(kev[0].fflags == NOTE_FFLAGSMASK);
> + ASSX(kev[0].data == 123);
> + ASSX(kev[0].udata == &dummy);
> + ASSX(kev[1].ident == 2);
> + ASSX(kev[1].fflags == 0);
> + ASSX(kev[1].data == 654);
> + ASSX(kev[1].udata == &dummy2);
> +
> + /* Clear the first event. */
> + EV_SET(&kev[0], 1, EVFILT_USER, EV_CLEAR, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 2);
> + ASSX(kev[0].fflags == 0);
> + ASSX(kev[0].data == 654);
> + ASSX(kev[0].udata == &dummy2);
> +
> + /* Delete the second event. */
> + EV_SET(&kev[0], 2, EVFILT_USER, EV_DELETE, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 0);
> +
> + /* Test self-clearing event. */
> + EV_SET(&kev[0], 2, EVFILT_USER, EV_ADD | EV_CLEAR, 0x11, 42, &dummy);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 0);
> +
> + EV_SET(&kev[0], 2, EVFILT_USER, 0, NOTE_TRIGGER | 0x3, 24, &dummy2);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 2);
> + ASSX(kev[0].fflags == 0x11);
> + ASSX(kev[0].data == 42);
> + ASSX(kev[0].udata == &dummy);
> +
> + n = kevent(kq, NULL, 0, kev, 2, &ts);
> + ASSX(n == 0);
> +
> + EV_SET(&kev[0], 2, EVFILT_USER, 0, NOTE_TRIGGER | 0x3, 9, &dummy2);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 2);
> + ASSX(kev[0].fflags == 0);
> + ASSX(kev[0].data == 0);
> + ASSX(kev[0].udata == &dummy);
> +
> + EV_SET(&kev[0], 2, EVFILT_USER, EV_DELETE, 0, 0, NULL);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 0);
> +
> + /* Change fflags. */
> + EV_SET(&kev[0], 1, EVFILT_USER, 0, NOTE_FFCOPY | 0x00aa00, 0, NULL);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 0);
> + EV_SET(&kev[0], 1, EVFILT_USER, 0, NOTE_FFOR | 0xff00ff, 0, NULL);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 0);
> + EV_SET(&kev[0], 1, EVFILT_USER, 0, NOTE_TRIGGER | NOTE_FFAND | 0x0ffff0,
> + 0, NULL);
> + n = kevent(kq, kev, 1, kev, 2, &ts);
> + ASSX(n == 1);
> + ASSX(kev[0].ident == 1);
> + ASSX(kev[0].fflags == 0x0faaf0);
> + ASSX(kev[0].data == 0);
> + ASSX(kev[0].udata == &dummy);
> +
> + /* Test event limit. */
> + for (i = 0;; i++) {
> + EV_SET(&kev[0], i, EVFILT_USER, EV_ADD, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + if (n == -1) {
> + ASSX(errno == ENOMEM);
> + break;
> + }
> + ASSX(n == 0);
> + }
> + ASSX(i < 1000000);
> +
> + /* Delete one event, ... */
> + EV_SET(&kev[0], 0, EVFILT_USER, EV_DELETE, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + /* ... after which adding should succeed. */
> + EV_SET(&kev[0], 0, EVFILT_USER, EV_ADD, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == 0);
> +
> + EV_SET(&kev[0], i, EVFILT_USER, EV_ADD, 0, 0, NULL);
> + n = kevent(kq, kev, 1, NULL, 0, NULL);
> + ASSX(n == -1);
> + ASSX(errno == ENOMEM);
> +
> + close(kq);
> +
> + return (0);
> +}
> Index: regress/sys/kern/kqueue/main.c
> ===================================================================
> RCS file: src/regress/sys/kern/kqueue/main.c,v
> retrieving revision 1.16
> diff -u -p -r1.16 main.c
> --- regress/sys/kern/kqueue/main.c 20 Aug 2023 15:19:34 -0000 1.16
> +++ regress/sys/kern/kqueue/main.c 6 May 2025 03:20:40 -0000
> @@ -17,7 +17,7 @@ main(int argc, char **argv)
> int n, ret, c;
>
> ret = 0;
> - while ((c = getopt(argc, argv, "efFiIjlpPrR:stT:")) != -1) {
> + while ((c = getopt(argc, argv, "efFiIjlpPrR:stT:u")) != -1) {
> switch (c) {
> case 'e':
> ret |= do_exec(argv[0]);
> @@ -63,8 +63,11 @@ main(int argc, char **argv)
> n = strtonum(optarg, 1, INT_MAX, NULL);
> ret |= do_pty(n);
> break;
> + case 'u':
> + ret |= do_user();
> + break;
> default:
> - fprintf(stderr, "usage: %s -[fFiIlpPrstT] [-R n]\n",
> + fprintf(stderr, "usage: %s -[fFiIlpPrstTu] [-R n]\n",
> __progname);
> exit(1);
> }
> Index: regress/sys/kern/kqueue/main.h
> ===================================================================
> RCS file: src/regress/sys/kern/kqueue/main.h,v
> retrieving revision 1.7
> diff -u -p -r1.7 main.h
> --- regress/sys/kern/kqueue/main.h 20 Aug 2023 15:19:34 -0000 1.7
> +++ regress/sys/kern/kqueue/main.h 6 May 2025 03:20:40 -0000
> @@ -29,3 +29,4 @@ int do_reset_timer(void);
> int do_signal(void);
> int do_timer(void);
> int do_tun(void);
> +int do_user(void);
> Index: sys/kern/kern_descrip.c
> ===================================================================
> RCS file: src/sys/kern/kern_descrip.c,v
> retrieving revision 1.210
> diff -u -p -r1.210 kern_descrip.c
> --- sys/kern/kern_descrip.c 30 Dec 2024 02:46:00 -0000 1.210
> +++ sys/kern/kern_descrip.c 6 May 2025 03:20:41 -0000
> @@ -39,6 +39,7 @@
>
> #include <sys/param.h>
> #include <sys/systm.h>
> +#include <sys/atomic.h>
> #include <sys/filedesc.h>
> #include <sys/vnode.h>
> #include <sys/proc.h>
> @@ -1201,6 +1202,7 @@ fdfree(struct proc *p)
> vrele(fdp->fd_cdir);
> if (fdp->fd_rdir)
> vrele(fdp->fd_rdir);
> + KASSERT(atomic_load_int(&fdp->fd_nuserevents) == 0);
> pool_put(&fdesc_pool, fdp);
> }
>
> Index: sys/kern/kern_event.c
> ===================================================================
> RCS file: src/sys/kern/kern_event.c,v
> retrieving revision 1.201
> diff -u -p -r1.201 kern_event.c
> --- sys/kern/kern_event.c 10 Feb 2025 16:45:46 -0000 1.201
> +++ sys/kern/kern_event.c 6 May 2025 03:20:41 -0000
> @@ -30,6 +30,7 @@
>
> #include <sys/param.h>
> #include <sys/systm.h>
> +#include <sys/atomic.h>
> #include <sys/proc.h>
> #include <sys/pledge.h>
> #include <sys/malloc.h>
> @@ -135,6 +136,10 @@ int filt_timerattach(struct knote *kn);
> void filt_timerdetach(struct knote *kn);
> int filt_timermodify(struct kevent *kev, struct knote *kn);
> int filt_timerprocess(struct knote *kn, struct kevent *kev);
> +int filt_userattach(struct knote *kn);
> +void filt_userdetach(struct knote *kn);
> +int filt_usermodify(struct kevent *kev, struct knote *kn);
> +int filt_userprocess(struct knote *kn, struct kevent *kev);
> void filt_seltruedetach(struct knote *kn);
>
> const struct filterops kqread_filtops = {
> @@ -180,12 +185,22 @@ const struct filterops timer_filtops = {
> .f_process = filt_timerprocess,
> };
>
> +const struct filterops user_filtops = {
> + .f_flags = FILTEROP_MPSAFE,
> + .f_attach = filt_userattach,
> + .f_detach = filt_userdetach,
> + .f_event = NULL,
> + .f_modify = filt_usermodify,
> + .f_process = filt_userprocess,
> +};
> +
> struct pool knote_pool;
> struct pool kqueue_pool;
> struct mutex kqueue_klist_lock = MUTEX_INITIALIZER(IPL_MPFLOOR);
> struct rwlock kqueue_ps_list_lock = RWLOCK_INITIALIZER("kqpsl");
> int kq_ntimeouts = 0;
> int kq_timeoutmax = (4 * 1024);
> +unsigned int kq_usereventsmax = 1024; /* per process */
>
> #define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask))
>
> @@ -202,6 +217,7 @@ const struct filterops *const sysfilt_op
> &timer_filtops, /* EVFILT_TIMER */
> &file_filtops, /* EVFILT_DEVICE */
> &file_filtops, /* EVFILT_EXCEPT */
> + &user_filtops, /* EVFILT_USER */
> };
>
> void
> @@ -731,6 +747,91 @@ filt_timerprocess(struct knote *kn, stru
> return (active);
> }
>
> +int
> +filt_userattach(struct knote *kn)
> +{
> + struct filedesc *fdp = kn->kn_kq->kq_fdp;
> + u_int nuserevents;
> +
> + nuserevents = atomic_inc_int_nv(&fdp->fd_nuserevents);
> + if (nuserevents > atomic_load_int(&kq_usereventsmax)) {
> + atomic_dec_int(&fdp->fd_nuserevents);
> + return (ENOMEM);
> + }
> +
> + kn->kn_ptr.p_useract = ((kn->kn_sfflags & NOTE_TRIGGER) != 0);
> + kn->kn_fflags = kn->kn_sfflags & NOTE_FFLAGSMASK;
> + kn->kn_data = kn->kn_sdata;
> +
> + return (0);
> +}
> +
> +void
> +filt_userdetach(struct knote *kn)
> +{
> + struct filedesc *fdp = kn->kn_kq->kq_fdp;
> +
> + atomic_dec_int(&fdp->fd_nuserevents);
> +}
> +
> +int
> +filt_usermodify(struct kevent *kev, struct knote *kn)
> +{
> + unsigned int ffctrl, fflags;
> +
> + if (kev->fflags & NOTE_TRIGGER)
> + kn->kn_ptr.p_useract = 1;
> +
> + ffctrl = kev->fflags & NOTE_FFCTRLMASK;
> + fflags = kev->fflags & NOTE_FFLAGSMASK;
> + switch (ffctrl) {
> + case NOTE_FFNOP:
> + break;
> + case NOTE_FFAND:
> + kn->kn_fflags &= fflags;
> + break;
> + case NOTE_FFOR:
> + kn->kn_fflags |= fflags;
> + break;
> + case NOTE_FFCOPY:
> + kn->kn_fflags = fflags;
> + break;
> + default:
> + /* ignored, should not happen */
> + break;
> + }
> +
> + if (kev->flags & EV_ADD) {
> + kn->kn_data = kev->data;
> + kn->kn_udata = kev->udata;
> + }
> +
> + /* Allow clearing of an activated event. */
> + if (kev->flags & EV_CLEAR) {
> + kn->kn_ptr.p_useract = 0;
> + kn->kn_data = 0;
> + }
> +
> + return (kn->kn_ptr.p_useract);
> +}
> +
> +int
> +filt_userprocess(struct knote *kn, struct kevent *kev)
> +{
> + int active;
> +
> + active = kn->kn_ptr.p_useract;
> + if (active && kev != NULL) {
> + *kev = kn->kn_kevent;
> + if (kn->kn_flags & EV_CLEAR) {
> + kn->kn_ptr.p_useract = 0;
> + kn->kn_fflags = 0;
> + kn->kn_data = 0;
> + }
> + }
> +
> + return (active);
> +}
>
> /*
> * filt_seltrue:
> @@ -1411,6 +1512,17 @@ again:
> filter_detach(kn);
> knote_drop(kn, p);
> goto done;
> + } else if (kn->kn_fop == &user_filtops) {
> + /* Call f_modify to allow NOTE_TRIGGER without EV_ADD. */
> + mtx_leave(&kq->kq_lock);
> + active = filter_modify(kev, kn);
> + mtx_enter(&kq->kq_lock);
> + if (active)
> + knote_activate(kn);
> + if (kev->flags & EV_ERROR) {
> + error = kev->data;
> + goto release;
> + }
> }
>
> if ((kev->flags & EV_DISABLE) && ((kn->kn_status & KN_DISABLED) == 0))
> Index: sys/sys/event.h
> ===================================================================
> RCS file: src/sys/sys/event.h,v
> retrieving revision 1.73
> diff -u -p -r1.73 event.h
> --- sys/sys/event.h 6 Aug 2024 08:44:54 -0000 1.73
> +++ sys/sys/event.h 6 May 2025 03:20:41 -0000
> @@ -40,8 +40,9 @@
> #define EVFILT_TIMER (-7) /* timers */
> #define EVFILT_DEVICE (-8) /* devices */
> #define EVFILT_EXCEPT (-9) /* exceptional conditions */
> +#define EVFILT_USER (-10) /* user event */
>
> -#define EVFILT_SYSCOUNT 9
> +#define EVFILT_SYSCOUNT 10
>
> #define EV_SET(kevp, a, b, c, d, e, f) do { \
> struct kevent *__kevp = (kevp); \
> @@ -130,6 +131,19 @@ struct kevent {
> #define NOTE_ABSTIME 0x00000010 /* timeout is absolute */
>
> /*
> + * data/hint flags for EVFILT_USER, shared with userspace
> + */
> +#define NOTE_FFNOP 0x00000000 /* ignore input fflags */
> +#define NOTE_FFAND 0x40000000 /* AND fflags */
> +#define NOTE_FFOR 0x80000000 /* OR fflags */
> +#define NOTE_FFCOPY 0xc0000000 /* copy fflags */
> +
> +#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */
> +#define NOTE_FFLAGSMASK 0x00ffffff
> +
> +#define NOTE_TRIGGER 0x01000000 /* trigger the event */
> +
> +/*
> * This is currently visible to userland to work around broken
> * programs which pull in <sys/proc.h> or <sys/selinfo.h>.
> */
> @@ -244,6 +258,7 @@ struct knote {
> union {
> struct file *p_fp; /* file data pointer */
> struct process *p_process; /* process pointer */
> + int p_useract; /* user event active */
> } kn_ptr;
> const struct filterops *kn_fop;
> void *kn_hook; /* [o] */
> Index: sys/sys/filedesc.h
> ===================================================================
> RCS file: src/sys/sys/filedesc.h,v
> retrieving revision 1.46
> diff -u -p -r1.46 filedesc.h
> --- sys/sys/filedesc.h 12 May 2022 13:33:09 -0000 1.46
> +++ sys/sys/filedesc.h 6 May 2025 03:20:41 -0000
> @@ -87,6 +87,7 @@ struct filedesc {
> LIST_HEAD(, kqueue) fd_kqlist; /* [f] kqueues attached to this
> * filedesc */
> int fd_flags; /* [a] flags on this filedesc */
> + u_int fd_nuserevents; /* [a] number of kqueue user events */
> };
>
> /*
> Index: usr.bin/kdump/mksubr
> ===================================================================
> RCS file: src/usr.bin/kdump/mksubr,v
> retrieving revision 1.40
> diff -u -p -r1.40 mksubr
> --- usr.bin/kdump/mksubr 13 Aug 2023 08:29:28 -0000 1.40
> +++ usr.bin/kdump/mksubr 6 May 2025 03:20:41 -0000
> @@ -583,6 +583,27 @@ cat <<_EOF_
> or = 1;
> if_print_or(fflags, NOTE_ABSTIME, or);
> break;
> + case EVFILT_USER:
> + if (fflags & NOTE_FFCTRLMASK) {
> + switch (fflags & NOTE_FFCTRLMASK) {
> + case NOTE_FFAND:
> + printf("NOTE_FFAND");
> + break;
> + case NOTE_FFOR:
> + printf("NOTE_FFOR");
> + break;
> + case NOTE_FFCOPY:
> + printf("NOTE_FFCOPY");
> + break;
> + }
> + or = 1;
> + }
> + if_print_or(fflags, NOTE_TRIGGER, or);
> + if (fflags & NOTE_FFLAGSMASK) {
> + printf("%s%#x", or ? "|" : "",
> + fflags & NOTE_FFLAGSMASK);
> + }
> + break;
> }
> printf(">");
> }
>
EVFILT_USER and kevent(2)