NetBSD Problem Report #55663

From www@netbsd.org  Wed Sep 16 19:10:14 2020
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 4223B1A9239
	for <gnats-bugs@gnats.NetBSD.org>; Wed, 16 Sep 2020 19:10:14 +0000 (UTC)
Message-Id: <20200916191011.242C61A923A@mollari.NetBSD.org>
Date: Wed, 16 Sep 2020 19:10:11 +0000 (UTC)
From: nruslan_devel@yahoo.com
Reply-To: nruslan_devel@yahoo.com
To: gnats-bugs@NetBSD.org
Subject: Support for EVFILT_USER in kqueue(2)
X-Send-Pr-Version: www-1.0

>Number:         55663
>Category:       kern
>Synopsis:       Support for EVFILT_USER in kqueue(2)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    jnemeth
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Sep 16 19:15:00 +0000 2020
>Closed-Date:    Mon Dec 07 17:52:09 +0000 2020
>Last-Modified:  Mon Dec 07 17:52:09 +0000 2020
>Originator:     Ruslan Nikolaev
>Release:        master
>Organization:
Virginia Tech
>Environment:
>Description:
NetBSD lacks support for EVTFILT_USER (e.g., see the bug report for pkg/54627). This option was introduced in FreeBSD long time ago, but NetBSD still lacks it.

While working on our project which uses the NetBSD code (https://github.com/ssrg-vt/rumprun-smp), we encountered that problem and ported a patch. We partially based our patch on that of OpenBSD (https://www.mail-archive.com/tech@openbsd.org/msg42433.html). OpenBSD's patch itself is backported from FreeBSD.

We relocated the patch to the most recent version (master branch). Please let us know if that can be upstreamed into NetBSD and who can review our patch.
>How-To-Repeat:

>Fix:
diff --git a/share/man/man9/kfilter_register.9 b/share/man/man9/kfilter_register.9
index 43ba379b8c40..f405819140aa 100644
--- a/share/man/man9/kfilter_register.9
+++ b/share/man/man9/kfilter_register.9
@@ -97,6 +97,7 @@ struct filterops {
 				/* called when knote is DELETEd */
 	int	(*f_event)(struct knote *kn, long hint);
 				/* called when event is triggered */
+	void	(*f_touch)(struct knote *kn, struct kevent *kev, long hint);
 };
 .Ed
 .Pp
diff --git a/sys/arch/amiga/dev/event.c b/sys/arch/amiga/dev/event.c
index 153176189f3d..6eb76425a5b9 100644
--- a/sys/arch/amiga/dev/event.c
+++ b/sys/arch/amiga/dev/event.c
@@ -195,6 +195,7 @@ static const struct filterops ev_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_evrdetach,
 	.f_event = filt_evread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/arc/dev/opms.c b/sys/arch/arc/dev/opms.c
index 222905f3cee3..aa20ab9f43c9 100644
--- a/sys/arch/arc/dev/opms.c
+++ b/sys/arch/arc/dev/opms.c
@@ -477,6 +477,7 @@ static const struct filterops opmsread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_opmsrdetach,
 	.f_event = filt_opmsread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/arm/xscale/pxa2x0_apm.c b/sys/arch/arm/xscale/pxa2x0_apm.c
index f27b56901f82..7a9d987d3e1f 100644
--- a/sys/arch/arm/xscale/pxa2x0_apm.c
+++ b/sys/arch/arm/xscale/pxa2x0_apm.c
@@ -105,7 +105,7 @@ int	filt_apmread(struct knote *kn, long hint);
 int	apmkqfilter(dev_t dev, struct knote *kn);

 struct filterops apmread_filtops =
-	{ 1, NULL, filt_apmrdetach, filt_apmread};
+	{ 1, NULL, filt_apmrdetach, filt_apmread, NULL };
 #endif

 /*
diff --git a/sys/arch/atari/dev/event.c b/sys/arch/atari/dev/event.c
index ca325c187319..4b2077a02ee8 100644
--- a/sys/arch/atari/dev/event.c
+++ b/sys/arch/atari/dev/event.c
@@ -194,6 +194,7 @@ static const struct filterops ev_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_evrdetach,
 	.f_event = filt_evread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/landisk/dev/button.c b/sys/arch/landisk/dev/button.c
index 1dc52c0d3af9..98bda5264e42 100644
--- a/sys/arch/landisk/dev/button.c
+++ b/sys/arch/landisk/dev/button.c
@@ -317,6 +317,7 @@ static const struct filterops btn_read_filtops = {
     .f_attach = NULL,
     .f_detach = filt_btn_rdetach,
     .f_event = filt_btn_read,
+	.f_touch = NULL,
 };

 static const struct filterops btn_write_filtops = {
@@ -324,6 +325,7 @@ static const struct filterops btn_write_filtops = {
     .f_attach = NULL,
     .f_detach = filt_btn_rdetach,
     .f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/mac68k/dev/aed.c b/sys/arch/mac68k/dev/aed.c
index a0e3b0eacfb7..45352e4c30a8 100644
--- a/sys/arch/mac68k/dev/aed.c
+++ b/sys/arch/mac68k/dev/aed.c
@@ -602,6 +602,7 @@ static const struct filterops aedread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_aedrdetach,
 	.f_event = filt_aedread,
+	.f_touch = NULL,
 };

 static const struct filterops aed_seltrue_filtops = {
@@ -609,6 +610,7 @@ static const struct filterops aed_seltrue_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_aedrdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/macppc/dev/aed.c b/sys/arch/macppc/dev/aed.c
index eb1551d68c55..21f6256ccf38 100644
--- a/sys/arch/macppc/dev/aed.c
+++ b/sys/arch/macppc/dev/aed.c
@@ -606,14 +606,16 @@ static const struct filterops aedread_filtops = {
 	.f_isfd = 1,
 	.f_attach = NULL,
 	.f_detach = filt_aedrdetach,
-	.f_event = filt_aedread
+	.f_event = filt_aedread,
+	.f_touch = NULL,
 };

 static const struct filterops aed_seltrue_filtops = {
 	.f_isfd = 1,
 	.f_attach = NULL,
 	.f_detach = filt_aedrdetach,
-	.f_event = filt_seltrue
+	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/macppc/dev/apm.c b/sys/arch/macppc/dev/apm.c
index c96f2aac2b2d..db515f309690 100644
--- a/sys/arch/macppc/dev/apm.c
+++ b/sys/arch/macppc/dev/apm.c
@@ -432,7 +432,7 @@ filt_apmread(struct knote *kn, long hint)
 }

 static struct filterops apmread_filtops =
-	{ 1, NULL, filt_apmrdetach, filt_apmread};
+	{ 1, NULL, filt_apmrdetach, filt_apmread, NULL };

 int
 apmkqfilter(dev_t dev, struct knote *kn)
diff --git a/sys/arch/mips/ralink/ralink_gpio.c b/sys/arch/mips/ralink/ralink_gpio.c
index 127acd6b5991..3ccec0a74dd3 100644
--- a/sys/arch/mips/ralink/ralink_gpio.c
+++ b/sys/arch/mips/ralink/ralink_gpio.c
@@ -498,7 +498,8 @@ static struct filterops app_fops = {
 	0,
 	gpio_event_app_user_attach,
 	gpio_event_app_user_detach,
-	gpio_event_app_user_event
+	gpio_event_app_user_event,
+	NULL
 };
 static struct callout led_tick_callout;
 static int gpio_driver_blink_leds = 1;
diff --git a/sys/arch/sandpoint/sandpoint/satmgr.c b/sys/arch/sandpoint/sandpoint/satmgr.c
index c3b049d6a075..f22c85989d36 100644
--- a/sys/arch/sandpoint/sandpoint/satmgr.c
+++ b/sys/arch/sandpoint/sandpoint/satmgr.c
@@ -607,6 +607,7 @@ static const struct filterops read_filtops ={
 	.f_attach = NULL,
 	.f_detach = filt_rdetach,
 	.f_event = filt_read,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/arch/shark/shark/opms.c b/sys/arch/shark/shark/opms.c
index 3dd08d4acc41..9b3c8fe9cb05 100644
--- a/sys/arch/shark/shark/opms.c
+++ b/sys/arch/shark/shark/opms.c
@@ -984,6 +984,7 @@ static const struct filterops opmsread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_opmsrdetach,
 	.f_event = filt_opmsread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/sparc/dev/tctrl.c b/sys/arch/sparc/dev/tctrl.c
index 7fd9d74acf0b..ceb5b06ab65c 100644
--- a/sys/arch/sparc/dev/tctrl.c
+++ b/sys/arch/sparc/dev/tctrl.c
@@ -1236,6 +1236,7 @@ static const struct filterops tctrlread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_tctrlrdetach,
 	.f_event = filt_tctrlread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/arch/x68k/dev/event.c b/sys/arch/x68k/dev/event.c
index 7fb4a41bdfae..102e905d7644 100644
--- a/sys/arch/x68k/dev/event.c
+++ b/sys/arch/x68k/dev/event.c
@@ -221,6 +221,7 @@ static const struct filterops ev_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_evrdetach,
 	.f_event = filt_evread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/coda/coda_psdev.c b/sys/coda/coda_psdev.c
index c42d1db98bd0..745d2985b7d2 100644
--- a/sys/coda/coda_psdev.c
+++ b/sys/coda/coda_psdev.c
@@ -491,6 +491,7 @@ static const struct filterops vc_nb_read_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_vc_nb_detach,
 	.f_event = filt_vc_nb_read,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/apm/apm.c b/sys/dev/apm/apm.c
index 04e5c541308e..794ae0eee7f0 100644
--- a/sys/dev/apm/apm.c
+++ b/sys/dev/apm/apm.c
@@ -888,6 +888,7 @@ static const struct filterops apmread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_apmrdetach,
 	.f_event = filt_apmread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/audio/audio.c b/sys/dev/audio/audio.c
index 0d04894795b6..89ed11183b39 100644
--- a/sys/dev/audio/audio.c
+++ b/sys/dev/audio/audio.c
@@ -3099,6 +3099,7 @@ static const struct filterops audioread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_audioread_detach,
 	.f_event = filt_audioread_event,
+	.f_touch = NULL,
 };

 static void
@@ -3146,6 +3147,7 @@ static const struct filterops audiowrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_audiowrite_detach,
 	.f_event = filt_audiowrite_event,
+	.f_touch = NULL,
 };

 static void
diff --git a/sys/dev/hpc/apm/apmdev.c b/sys/dev/hpc/apm/apmdev.c
index 6b182349ef53..917cd8a10a36 100644
--- a/sys/dev/hpc/apm/apmdev.c
+++ b/sys/dev/hpc/apm/apmdev.c
@@ -929,6 +929,7 @@ static const struct filterops apmread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_apmrdetach,
 	.f_event = filt_apmread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/ir/irframe_tty.c b/sys/dev/ir/irframe_tty.c
index e3c70dd752fa..6bf37dfe81af 100644
--- a/sys/dev/ir/irframe_tty.c
+++ b/sys/dev/ir/irframe_tty.c
@@ -834,6 +834,7 @@ static const struct filterops irframetread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_irframetrdetach,
 	.f_event = filt_irframetread,
+	.f_touch = NULL,
 };

 static const struct filterops irframetwrite_filtops = {
@@ -841,6 +842,7 @@ static const struct filterops irframetwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_irframetwdetach,
 	.f_event = filt_irframetwrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/midi.c b/sys/dev/midi.c
index 4949cc1feca9..d2a4f7b70571 100644
--- a/sys/dev/midi.c
+++ b/sys/dev/midi.c
@@ -1768,6 +1768,7 @@ static const struct filterops midiread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_midirdetach,
 	.f_event = filt_midiread,
+	.f_touch = NULL,
 };

 static void
@@ -1816,6 +1817,7 @@ static const struct filterops midiwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_midiwdetach,
 	.f_event = filt_midiwrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/pci/oboe.c b/sys/dev/pci/oboe.c
index db77bcc30c58..90fab594ed6e 100644
--- a/sys/dev/pci/oboe.c
+++ b/sys/dev/pci/oboe.c
@@ -499,6 +499,7 @@ static const struct filterops oboeread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_oboerdetach,
 	.f_event = filt_oboeread,
+	.f_touch = NULL,
 };

 static const struct filterops oboewrite_filtops = {
@@ -506,6 +507,7 @@ static const struct filterops oboewrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_oboewdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/pci/vio9p.c b/sys/dev/pci/vio9p.c
index cfc8449b25e0..2c0432ee8368 100644
--- a/sys/dev/pci/vio9p.c
+++ b/sys/dev/pci/vio9p.c
@@ -412,6 +412,7 @@ static const struct filterops vio9p_read_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_vio9p_detach,
 	.f_event = filt_vio9p_read,
+	.f_touch = NULL,
 };

 static int
@@ -428,6 +429,7 @@ static const struct filterops vio9p_write_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_vio9p_detach,
 	.f_event = filt_vio9p_write,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/pci/xmm7360.c b/sys/dev/pci/xmm7360.c
index 17a28aeb9aad..34586b99a5a5 100644
--- a/sys/dev/pci/xmm7360.c
+++ b/sys/dev/pci/xmm7360.c
@@ -2800,6 +2800,7 @@ static const struct filterops wwancread_filtops = {
 	.f_attach	= NULL,
 	.f_detach	= filt_wwancrdetach,
 	.f_event	= filt_wwancread,
+	.f_touch	= NULL,
 };

 static const struct filterops wwancwrite_filtops = {
@@ -2807,6 +2808,7 @@ static const struct filterops wwancwrite_filtops = {
 	.f_attach	= NULL,
 	.f_detach	= filt_wwancwdetach,
 	.f_event	= filt_wwancwrite,
+	.f_touch	= NULL,
 };

 int
diff --git a/sys/dev/putter/putter.c b/sys/dev/putter/putter.c
index 7d4f57d30f6a..9997643ae03e 100644
--- a/sys/dev/putter/putter.c
+++ b/sys/dev/putter/putter.c
@@ -496,6 +496,7 @@ static const struct filterops putter_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_putterdetach,
 	.f_event = filt_putter,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/qbus/qd.c b/sys/dev/qbus/qd.c
index 9a36533adf92..431949a601ed 100644
--- a/sys/dev/qbus/qd.c
+++ b/sys/dev/qbus/qd.c
@@ -1585,6 +1585,7 @@ static const struct filterops qdread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_qdrdetach,
 	.f_event = filt_qdread,
+	.f_touch = NULL,
 };

 static const struct filterops qdwrite_filtops = {
@@ -1592,6 +1593,7 @@ static const struct filterops qdwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_qdrdetach,
 	.f_event = filt_qdwrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/sbus/bpp.c b/sys/dev/sbus/bpp.c
index 76cf6fabb718..dc9d328372c0 100644
--- a/sys/dev/sbus/bpp.c
+++ b/sys/dev/sbus/bpp.c
@@ -525,6 +525,7 @@ static const struct filterops bppread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_bpprdetach,
 	.f_event = filt_bppread,
+	.f_touch = NULL,
 };

 static void
@@ -555,6 +556,7 @@ static const struct filterops bppwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_bppwdetach,
 	.f_event = filt_bpfwrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/scsipi/ch.c b/sys/dev/scsipi/ch.c
index 208aa77e2886..df758d73b200 100644
--- a/sys/dev/scsipi/ch.c
+++ b/sys/dev/scsipi/ch.c
@@ -492,6 +492,7 @@ static const struct filterops chread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_chdetach,
 	.f_event = filt_chread,
+	.f_touch = NULL,
 };

 static const struct filterops chwrite_filtops = {
@@ -499,6 +500,7 @@ static const struct filterops chwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_chdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/sequencer.c b/sys/dev/sequencer.c
index 3cbeb6ca9d02..b2add01e6c40 100644
--- a/sys/dev/sequencer.c
+++ b/sys/dev/sequencer.c
@@ -931,6 +931,7 @@ static const struct filterops sequencerread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_sequencerrdetach,
 	.f_event = filt_sequencerread,
+	.f_touch = NULL,
 };

 static void
@@ -969,6 +970,7 @@ static const struct filterops sequencerwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_sequencerwdetach,
 	.f_event = filt_sequencerwrite,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/sun/event.c b/sys/dev/sun/event.c
index 60504f9cc92f..4a2ba12116ff 100644
--- a/sys/dev/sun/event.c
+++ b/sys/dev/sun/event.c
@@ -201,6 +201,7 @@ static const struct filterops ev_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_evrdetach,
 	.f_event = filt_evread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/sysmon/sysmon_power.c b/sys/dev/sysmon/sysmon_power.c
index e06f0830fd1e..a72c54db5313 100644
--- a/sys/dev/sysmon/sysmon_power.c
+++ b/sys/dev/sysmon/sysmon_power.c
@@ -559,6 +559,7 @@ static const struct filterops sysmon_power_read_filtops = {
     .f_attach = NULL,
     .f_detach = filt_sysmon_power_rdetach,
     .f_event = filt_sysmon_power_read,
+	.f_touch = NULL,
 };

 static const struct filterops sysmon_power_write_filtops = {
@@ -566,6 +567,7 @@ static const struct filterops sysmon_power_write_filtops = {
     .f_attach = NULL,
     .f_detach = filt_sysmon_power_rdetach,
     .f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 /*
diff --git a/sys/dev/usb/udsir.c b/sys/dev/usb/udsir.c
index ee2ae7153cab..73dc0d968c87 100644
--- a/sys/dev/usb/udsir.c
+++ b/sys/dev/usb/udsir.c
@@ -667,6 +667,7 @@ static const struct filterops udsirread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_udsirrdetach,
 	.f_event = filt_udsirread,
+	.f_touch = NULL,
 };

 static const struct filterops udsirwrite_filtops = {
@@ -674,6 +675,7 @@ static const struct filterops udsirwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_udsirwdetach,
 	.f_event = filt_udsirwrite,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
index d887f1e80dcc..a1f4e0f70146 100644
--- a/sys/dev/usb/ugen.c
+++ b/sys/dev/usb/ugen.c
@@ -2140,6 +2140,7 @@ static const struct filterops ugenread_intr_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ugenrdetach,
 	.f_event = filt_ugenread_intr,
+	.f_touch = NULL,
 };

 static const struct filterops ugenread_isoc_filtops = {
@@ -2147,6 +2148,7 @@ static const struct filterops ugenread_isoc_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ugenrdetach,
 	.f_event = filt_ugenread_isoc,
+	.f_touch = NULL,
 };

 static const struct filterops ugenread_bulk_filtops = {
@@ -2154,6 +2156,7 @@ static const struct filterops ugenread_bulk_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ugenrdetach,
 	.f_event = filt_ugenread_bulk,
+	.f_touch = NULL,
 };

 static const struct filterops ugenwrite_bulk_filtops = {
@@ -2161,6 +2164,7 @@ static const struct filterops ugenwrite_bulk_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ugenrdetach,
 	.f_event = filt_ugenwrite_bulk,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c
index 072e201fcfb4..dfdbeb507d5f 100644
--- a/sys/dev/usb/uhid.c
+++ b/sys/dev/usb/uhid.c
@@ -800,6 +800,7 @@ static const struct filterops uhidread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_uhidrdetach,
 	.f_event = filt_uhidread,
+	.f_touch = NULL,
 };

 static const struct filterops uhid_seltrue_filtops = {
@@ -807,6 +808,7 @@ static const struct filterops uhid_seltrue_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_uhidrdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/dev/usb/uirda.c b/sys/dev/usb/uirda.c
index 06697ad74af1..48be32e242fc 100644
--- a/sys/dev/usb/uirda.c
+++ b/sys/dev/usb/uirda.c
@@ -645,6 +645,7 @@ static const struct filterops uirdaread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_uirdardetach,
 	.f_event = filt_uirdaread,
+	.f_touch = NULL,
 };

 static const struct filterops uirdawrite_filtops = {
@@ -652,6 +653,7 @@ static const struct filterops uirdawrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_uirdawdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c
index 0876ae6189ec..63fad3594131 100644
--- a/sys/dev/usb/usb.c
+++ b/sys/dev/usb/usb.c
@@ -1042,6 +1042,7 @@ static const struct filterops usbread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_usbrdetach,
 	.f_event = filt_usbread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/usb/uscanner.c b/sys/dev/usb/uscanner.c
index 299c34ea7ae3..8cf40cf85068 100644
--- a/sys/dev/usb/uscanner.c
+++ b/sys/dev/usb/uscanner.c
@@ -683,6 +683,7 @@ static const struct filterops uscanner_seltrue_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_uscannerdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/dev/usb/ustir.c b/sys/dev/usb/ustir.c
index df3776d525ba..bb1ba4bb69e9 100644
--- a/sys/dev/usb/ustir.c
+++ b/sys/dev/usb/ustir.c
@@ -1099,6 +1099,7 @@ static const struct filterops ustirread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ustirrdetach,
 	.f_event = filt_ustirread,
+	.f_touch = NULL,
 };

 static const struct filterops ustirwrite_filtops = {
@@ -1106,6 +1107,7 @@ static const struct filterops ustirwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ustirwdetach,
 	.f_event = filt_ustirwrite,
+	.f_touch = NULL,
 };

 Static int
diff --git a/sys/dev/wscons/wsevent.c b/sys/dev/wscons/wsevent.c
index a3c34b1e3630..c37acf69e831 100644
--- a/sys/dev/wscons/wsevent.c
+++ b/sys/dev/wscons/wsevent.c
@@ -330,6 +330,7 @@ static const struct filterops wsevent_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_wseventrdetach,
 	.f_event = filt_wseventread,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/external/bsd/drm2/drm/drm_cdevsw.c b/sys/external/bsd/drm2/drm/drm_cdevsw.c
index 20073f5a5dc8..081846d903dd 100644
--- a/sys/external/bsd/drm2/drm/drm_cdevsw.c
+++ b/sys/external/bsd/drm2/drm/drm_cdevsw.c
@@ -390,6 +390,7 @@ static const struct filterops drm_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_drm_detach,
 	.f_event = filt_drm_event,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/kern/kern_entropy.c b/sys/kern/kern_entropy.c
index 1bb1350c68e6..e3d637147718 100644
--- a/sys/kern/kern_entropy.c
+++ b/sys/kern/kern_entropy.c
@@ -1458,6 +1458,7 @@ static const struct filterops entropy_read_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_entropy_read_detach,
 	.f_event = filt_entropy_read_event,
+	.f_touch = NULL,
 };

 /*
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 6ed5bec32e52..ccab72d51e92 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -31,6 +31,7 @@

 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
+ * Copyright (c) 2009 Apple, Inc
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -109,6 +110,10 @@ static int	filt_timer(struct knote *, long hint);
 static int	filt_fsattach(struct knote *kn);
 static void	filt_fsdetach(struct knote *kn);
 static int	filt_fs(struct knote *kn, long hint);
+static int	filt_userattach(struct knote *);
+static void	filt_userdetach(struct knote *);
+static int	filt_user(struct knote *, long hint);
+static void	filt_usertouch(struct knote *, struct kevent *, long type);

 static const struct fileops kqueueops = {
 	.fo_name = "kqueue",
@@ -128,6 +133,7 @@ static const struct filterops kqread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_kqdetach,
 	.f_event = filt_kqueue,
+	.f_touch = NULL,
 };

 static const struct filterops proc_filtops = {
@@ -135,6 +141,7 @@ static const struct filterops proc_filtops = {
 	.f_attach = filt_procattach,
 	.f_detach = filt_procdetach,
 	.f_event = filt_proc,
+	.f_touch = NULL,
 };

 static const struct filterops file_filtops = {
@@ -142,6 +149,7 @@ static const struct filterops file_filtops = {
 	.f_attach = filt_fileattach,
 	.f_detach = NULL,
 	.f_event = NULL,
+	.f_touch = NULL,
 };

 static const struct filterops timer_filtops = {
@@ -149,6 +157,7 @@ static const struct filterops timer_filtops = {
 	.f_attach = filt_timerattach,
 	.f_detach = filt_timerdetach,
 	.f_event = filt_timer,
+	.f_touch = NULL,
 };

 static const struct filterops fs_filtops = {
@@ -156,6 +165,15 @@ static const struct filterops fs_filtops = {
 	.f_attach = filt_fsattach,
 	.f_detach = filt_fsdetach,
 	.f_event = filt_fs,
+	.f_touch = NULL,
+};
+
+static const struct filterops user_filtops = {
+	.f_isfd = 0,
+	.f_attach = filt_userattach,
+	.f_detach = filt_userdetach,
+	.f_event = filt_user,
+	.f_touch = filt_usertouch,
 };

 static u_int	kq_ncallouts = 0;
@@ -192,6 +210,7 @@ static struct kfilter sys_kfilters[] = {
 	{ "EVFILT_SIGNAL",	EVFILT_SIGNAL,	0, &sig_filtops, 0 },
 	{ "EVFILT_TIMER",	EVFILT_TIMER,	0, &timer_filtops, 0 },
 	{ "EVFILT_FS",		EVFILT_FS,	0, &fs_filtops, 0 },
+	{ "EVFILT_USER",	EVFILT_USER,	0, &user_filtops, 0 },
 	{ NULL,			0,		0, NULL, 0 },
 };

@@ -776,6 +795,106 @@ filt_fs(struct knote *kn, long hint)
 	return rv;
 }

+static int
+filt_userattach(struct knote *kn)
+{
+	struct kqueue *kq = kn->kn_kq;
+
+	/*
+	 * EVFILT_USER knotes are not attached to anything in the kernel.
+	 */
+	mutex_spin_enter(&kq->kq_lock);
+	kn->kn_hook = NULL;
+	if (kn->kn_fflags & NOTE_TRIGGER)
+		kn->kn_hookid = 1;
+	else
+		kn->kn_hookid = 0;
+	mutex_spin_exit(&kq->kq_lock);
+	return (0);
+}
+
+static void
+filt_userdetach(struct knote *kn)
+{
+
+	/*
+	 * EVFILT_USER knotes are not attached to anything in the kernel.
+	 */
+}
+
+static int
+filt_user(struct knote *kn, long hint)
+{
+	struct kqueue *kq = kn->kn_kq;
+	int hookid;
+
+	mutex_spin_enter(&kq->kq_lock);
+	hookid = kn->kn_hookid;
+	mutex_spin_exit(&kq->kq_lock);
+
+	return hookid;
+}
+
+static void
+filt_usertouch(struct knote *kn, struct kevent *kev, long type)
+{
+	struct kqueue *kq = kn->kn_kq;
+	int ffctrl;
+
+	mutex_spin_enter(&kq->kq_lock);
+	switch (type) {
+	case EVENT_REGISTER:
+		if (kev->fflags & NOTE_TRIGGER)
+			kn->kn_hookid = 1;
+
+		ffctrl = kev->fflags & NOTE_FFCTRLMASK;
+		kev->fflags &= NOTE_FFLAGSMASK;
+		switch (ffctrl) {
+		case NOTE_FFNOP:
+			break;
+
+		case NOTE_FFAND:
+			kn->kn_sfflags &= kev->fflags;
+			break;
+
+		case NOTE_FFOR:
+			kn->kn_sfflags |= kev->fflags;
+			break;
+
+		case NOTE_FFCOPY:
+			kn->kn_sfflags = kev->fflags;
+			break;
+
+		default:
+			/* XXX Return error? */
+			break;
+		}
+		kn->kn_sdata = kev->data;
+		if (kev->flags & EV_CLEAR) {
+			kn->kn_hookid = 0;
+			kn->kn_data = 0;
+			kn->kn_fflags = 0;
+		}
+		break;
+
+	case EVENT_PROCESS:
+		*kev = kn->kn_kevent;
+		kev->fflags = kn->kn_sfflags;
+		kev->data = kn->kn_sdata;
+		if (kn->kn_flags & EV_CLEAR) {
+			kn->kn_hookid = 0;
+			kn->kn_data = 0;
+			kn->kn_fflags = 0;
+		}
+		break;
+
+	default:
+		panic("filt_usertouch() - invalid type (%ld)", type);
+		break;
+	}
+	mutex_spin_exit(&kq->kq_lock);
+}
+
 /*
  * filt_seltrue:
  *
@@ -809,6 +928,7 @@ const struct filterops seltrue_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_seltruedetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
@@ -1072,8 +1192,8 @@ kqueue_register(struct kqueue *kq, struct kevent *kev)
 	/*
 	 * kn now contains the matching knote, or NULL if no match
 	 */
-	if (kev->flags & EV_ADD) {
-		if (kn == NULL) {
+	if (kn == NULL) {
+		if (kev->flags & EV_ADD) {
 			/* create new knote */
 			kn = newkn;
 			newkn = NULL;
@@ -1137,41 +1257,51 @@ kqueue_register(struct kqueue *kq, struct kevent *kev)
 				goto done;
 			}
 			atomic_inc_uint(&kfilter->refcnt);
+			goto done_ev_add;
 		} else {
-			/*
-			 * The user may change some filter values after the
-			 * initial EV_ADD, but doing so will not reset any
-			 * filter which have already been triggered.
-			 */
-			kn->kn_sfflags = kev->fflags;
-			kn->kn_sdata = kev->data;
-			kn->kn_kevent.udata = kev->udata;
+			/* No matching knote and the EV_ADD flag is not set. */
+			error = ENOENT;
+			goto doneunlock;
 		}
-		/*
-		 * We can get here if we are trying to attach
-		 * an event to a file descriptor that does not
-		 * support events, and the attach routine is
-		 * broken and does not return an error.
-		 */
-		KASSERT(kn->kn_fop != NULL);
-		KASSERT(kn->kn_fop->f_event != NULL);
+	}
+
+	if (kev->flags & EV_DELETE) {
+		/* knote_detach() drops fdp->fd_lock */
+		knote_detach(kn, fdp, true);
+		goto done;
+	}
+
+	/*
+	 * The user may change some filter values after the
+	 * initial EV_ADD, but doing so will not reset any
+	 * filter which have already been triggered.
+	 */
+	kn->kn_kevent.udata = kev->udata;
+	KASSERT(kn->kn_fop != NULL);
+	if (!kn->kn_fop->f_isfd && kn->kn_fop->f_touch != NULL) {
 		KERNEL_LOCK(1, NULL);			/* XXXSMP */
-		rv = (*kn->kn_fop->f_event)(kn, 0);
+		(*kn->kn_fop->f_touch)(kn, kev, EVENT_REGISTER);
 		KERNEL_UNLOCK_ONE(NULL);		/* XXXSMP */
-		if (rv)
-			knote_activate(kn);
 	} else {
-		if (kn == NULL) {
-			error = ENOENT;
-			goto doneunlock;
-		}
-		if (kev->flags & EV_DELETE) {
-			/* knote_detach() drops fdp->fd_lock */
-			knote_detach(kn, fdp, true);
-			goto done;
-		}
+		kn->kn_sfflags = kev->fflags;
+		kn->kn_sdata = kev->data;
 	}

+	/*
+	 * We can get here if we are trying to attach
+	 * an event to a file descriptor that does not
+	 * support events, and the attach routine is
+	 * broken and does not return an error.
+	 */
+done_ev_add:
+	KASSERT(kn->kn_fop != NULL);
+	KASSERT(kn->kn_fop->f_event != NULL);
+	KERNEL_LOCK(1, NULL);			/* XXXSMP */
+	rv = (*kn->kn_fop->f_event)(kn, 0);
+	KERNEL_UNLOCK_ONE(NULL);		/* XXXSMP */
+	if (rv)
+		knote_activate(kn);
+
 	/* disable knote */
 	if ((kev->flags & EV_DISABLE)) {
 		mutex_spin_enter(&kq->kq_lock);
@@ -1271,7 +1401,7 @@ kqueue_scan(file_t *fp, size_t maxevents, struct kevent *ulistp,
 	struct timespec	ats, sleepts;
 	struct knote	*kn, *marker, morker;
 	size_t		count, nkev, nevents;
-	int		timeout, error, rv;
+	int		timeout, error, touch, rv;
 	filedesc_t	*fdp;

 	fdp = curlwp->l_fd;
@@ -1382,8 +1512,20 @@ kqueue_scan(file_t *fp, size_t maxevents, struct kevent *ulistp,
 					continue;
 				}
 			}
+			KASSERT(kn->kn_fop != NULL);
+			touch = (!kn->kn_fop->f_isfd &&
+					kn->kn_fop->f_touch != NULL);
 			/* XXXAD should be got from f_event if !oneshot. */
-			*kevp++ = kn->kn_kevent;
+			if (touch) {
+				mutex_spin_exit(&kq->kq_lock);
+				KERNEL_LOCK(1, NULL);		/* XXXSMP */
+				(*kn->kn_fop->f_touch)(kn, kevp, EVENT_PROCESS);
+				KERNEL_UNLOCK_ONE(NULL);	/* XXXSMP */
+				mutex_spin_enter(&kq->kq_lock);
+			} else {
+				*kevp = kn->kn_kevent;
+			}
+			kevp++;
 			nkev++;
 			if (kn->kn_flags & EV_ONESHOT) {
 				/* delete ONESHOT events after retrieval */
@@ -1396,6 +1538,14 @@ kqueue_scan(file_t *fp, size_t maxevents, struct kevent *ulistp,
 				/* clear state after retrieval */
 				kn->kn_data = 0;
 				kn->kn_fflags = 0;
+				/*
+				 * Manually clear knotes who weren't
+				 * 'touch'ed.
+				 */
+				if (touch == 0) {
+					kn->kn_data = 0;
+					kn->kn_fflags = 0;
+				}
 				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE|KN_BUSY);
 			} else if (kn->kn_flags & EV_DISPATCH) {
 				kn->kn_status |= KN_DISABLED;
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 1e4c4449bb49..5af9504eaf0a 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2665,4 +2665,5 @@ const struct filterops sig_filtops = {
 		.f_attach = filt_sigattach,
 		.f_detach = filt_sigdetach,
 		.f_event = filt_signal,
+		.f_touch = NULL,
 };
diff --git a/sys/kern/subr_log.c b/sys/kern/subr_log.c
index ad57b693b343..90ecea9808bf 100644
--- a/sys/kern/subr_log.c
+++ b/sys/kern/subr_log.c
@@ -294,6 +294,7 @@ static const struct filterops logread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_logrdetach,
 	.f_event = filt_logread,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
index a5e2fb11b818..a147d3192b8a 100644
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -1092,6 +1092,7 @@ static const struct filterops pipe_rfiltops = {
 	.f_attach = NULL,
 	.f_detach = filt_pipedetach,
 	.f_event = filt_piperead,
+	.f_touch = NULL,
 };

 static const struct filterops pipe_wfiltops = {
@@ -1099,6 +1100,7 @@ static const struct filterops pipe_wfiltops = {
 	.f_attach = NULL,
 	.f_detach = filt_pipedetach,
 	.f_event = filt_pipewrite,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index e5d4bd96618c..96229c08b8ce 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -1515,6 +1515,7 @@ static const struct filterops ttyread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ttyrdetach,
 	.f_event = filt_ttyread,
+	.f_touch = NULL,
 };

 static const struct filterops ttywrite_filtops = {
@@ -1522,6 +1523,7 @@ static const struct filterops ttywrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ttywdetach,
 	.f_event = filt_ttywrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c
index 68d267c8852c..61051c45b044 100644
--- a/sys/kern/tty_pty.c
+++ b/sys/kern/tty_pty.c
@@ -1006,6 +1006,7 @@ static const struct filterops ptcread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ptcrdetach,
 	.f_event = filt_ptcread,
+	.f_touch = NULL,
 };

 static const struct filterops ptcwrite_filtops = {
@@ -1013,6 +1014,7 @@ static const struct filterops ptcwrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_ptcwdetach,
 	.f_event = filt_ptcwrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 702868f1cf5d..10e2783746f7 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -2319,6 +2319,7 @@ static const struct filterops solisten_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_sordetach,
 	.f_event = filt_solisten,
+	.f_touch = NULL,
 };

 static const struct filterops soread_filtops = {
@@ -2326,6 +2327,7 @@ static const struct filterops soread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_sordetach,
 	.f_event = filt_soread,
+	.f_touch = NULL,
 };

 static const struct filterops sowrite_filtops = {
@@ -2333,6 +2335,7 @@ static const struct filterops sowrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_sowdetach,
 	.f_event = filt_sowrite,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/miscfs/fifofs/fifo_vnops.c b/sys/miscfs/fifofs/fifo_vnops.c
index 5cb407b22bc3..fdfb113ee898 100644
--- a/sys/miscfs/fifofs/fifo_vnops.c
+++ b/sys/miscfs/fifofs/fifo_vnops.c
@@ -584,6 +584,7 @@ static const struct filterops fiforead_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_fifordetach,
 	.f_event = filt_fiforead,
+	.f_touch = NULL,
 };

 static const struct filterops fifowrite_filtops = {
@@ -591,6 +592,7 @@ static const struct filterops fifowrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_fifowdetach,
 	.f_event = filt_fifowrite,
+	.f_touch = NULL,
 };

 /* ARGSUSED */
diff --git a/sys/miscfs/genfs/genfs_vnops.c b/sys/miscfs/genfs/genfs_vnops.c
index 8f586e41e22c..baa152de50ab 100644
--- a/sys/miscfs/genfs/genfs_vnops.c
+++ b/sys/miscfs/genfs/genfs_vnops.c
@@ -575,6 +575,7 @@ static const struct filterops genfsread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_genfsdetach,
 	.f_event = filt_genfsread,
+	.f_touch = NULL,
 };

 static const struct filterops genfswrite_filtops = {
@@ -582,6 +583,7 @@ static const struct filterops genfswrite_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_genfsdetach,
 	.f_event = filt_genfswrite,
+	.f_touch = NULL,
 };

 static const struct filterops genfsvnode_filtops = {
@@ -589,6 +591,7 @@ static const struct filterops genfsvnode_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_genfsdetach,
 	.f_event = filt_genfsvnode,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/net/bpf.c b/sys/net/bpf.c
index 331758630392..b9f237713ba4 100644
--- a/sys/net/bpf.c
+++ b/sys/net/bpf.c
@@ -1552,6 +1552,7 @@ static const struct filterops bpfread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_bpfrdetach,
 	.f_event = filt_bpfread,
+	.f_touch = NULL,
 };

 static int
diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c
index d6505938e6c3..d553e6b1e2f9 100644
--- a/sys/net/if_tap.c
+++ b/sys/net/if_tap.c
@@ -1233,9 +1233,9 @@ tap_dev_poll(int unit, int events, struct lwp *l)
 }

 static struct filterops tap_read_filterops = { 1, NULL, tap_kqdetach,
-	tap_kqread };
+	tap_kqread, NULL };
 static struct filterops tap_seltrue_filterops = { 1, NULL, tap_kqdetach,
-	filt_seltrue };
+	filt_seltrue, NULL };

 static int
 tap_cdev_kqfilter(dev_t dev, struct knote *kn)
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c
index 4aedddec131f..b21bff9ec3c1 100644
--- a/sys/net/if_tun.c
+++ b/sys/net/if_tun.c
@@ -1082,6 +1082,7 @@ static const struct filterops tunread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_tunrdetach,
 	.f_event = filt_tunread,
+	.f_touch = NULL,
 };

 static const struct filterops tun_seltrue_filtops = {
@@ -1089,6 +1090,7 @@ static const struct filterops tun_seltrue_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_tunrdetach,
 	.f_event = filt_seltrue,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/nfs/nfs_kq.c b/sys/nfs/nfs_kq.c
index 8ceecc5d6f38..4c918d20717b 100644
--- a/sys/nfs/nfs_kq.c
+++ b/sys/nfs/nfs_kq.c
@@ -281,6 +281,7 @@ static const struct filterops nfsread_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_nfsdetach,
 	.f_event = filt_nfsread,
+	.f_touch = NULL,
 };

 static const struct filterops nfsvnode_filtops = {
@@ -288,6 +289,7 @@ static const struct filterops nfsvnode_filtops = {
 	.f_attach = NULL,
 	.f_detach = filt_nfsdetach,
 	.f_event = filt_nfsvnode,
+	.f_touch = NULL,
 };

 int
diff --git a/sys/sys/event.h b/sys/sys/event.h
index dd18e51bb4ce..2473d7b220e3 100644
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -44,7 +44,8 @@
 #define	EVFILT_SIGNAL		5U	/* attached to struct proc */
 #define	EVFILT_TIMER		6U	/* arbitrary timer (in ms) */
 #define	EVFILT_FS		7U	/* filesystem events */
-#define	EVFILT_SYSCOUNT		8U	/* number of filters */
+#define	EVFILT_USER		8U	/* user events */
+#define	EVFILT_SYSCOUNT		9U	/* number of filters */

 struct kevent {
 	uintptr_t	ident;		/* identifier for this event */
@@ -90,6 +91,26 @@ _EV_SET(struct kevent *_kevp, uintptr_t _ident, uint32_t _filter,
 #define	EV_EOF		0x8000U		/* EOF detected */
 #define	EV_ERROR	0x4000U		/* error, data contains errno */

+/*
+ * data/hint flags/masks for EVFILT_USER, shared with userspace
+ *
+ * On input, the top two bits of fflags specifies how the lower twenty four
+ * bits should be applied to the stored value of fflags.
+ *
+ * On output, the top two bits will always be set to NOTE_FFNOP and the
+ * remaining twenty four bits will contain the stored fflags value.
+ */
+#define	NOTE_FFNOP	0x00000000U		/* ignore input fflags */
+#define	NOTE_FFAND	0x40000000U		/* AND fflags */
+#define	NOTE_FFOR	0x80000000U		/* OR fflags */
+#define	NOTE_FFCOPY	0xc0000000U		/* copy fflags */
+
+#define	NOTE_FFCTRLMASK	0xc0000000U		/* masks for operations */
+#define	NOTE_FFLAGSMASK	0x00ffffffU
+
+#define	NOTE_TRIGGER	0x01000000U		/* Cause the event to be
+						   triggered for output. */
+
 /*
  * hint flag for in-kernel use - must not equal any existing note
  */
@@ -161,6 +182,16 @@ struct kfilter_mapping {
  */
 #define	NOTE_SIGNAL	0x08000000U

+/*
+ * Hint values for the optional f_touch event filter.  If f_touch is not set
+ * to NULL and f_isfd is zero the f_touch filter will be called with the type
+ * argument set to EVENT_REGISTER during a kevent() system call.  It is also
+ * called under the same conditions with the type argument set to EVENT_PROCESS
+ * when the event has been triggered.
+ */
+#define	EVENT_REGISTER	1
+#define	EVENT_PROCESS	2
+
 /*
  * Callback methods for each filter type.
  */
@@ -172,6 +203,7 @@ struct filterops {
 					/* called when knote is DELETEd */
 	int	(*f_event)	(struct knote *, long);
 					/* called when event is triggered */
+	void	(*f_touch)	(struct knote *, struct kevent *, long);
 };

 /*
@@ -196,6 +228,7 @@ struct knote {
 	const struct filterops	*kn_fop;
 	struct kfilter		*kn_kfilter;
 	void 			*kn_hook;
+	int			kn_hookid;

 #define	KN_ACTIVE	0x01U			/* event has been triggered */
 #define	KN_QUEUED	0x02U			/* event is on queue */

>Release-Note:

>Audit-Trail:
From: Martin Husemann <martin@duskware.de>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Wed, 16 Sep 2020 21:20:59 +0200

 Style nit:

 @@ -1092,6 +1092,7 @@ static const struct filterops pipe_rfiltops = {
         .f_attach = NULL, 
         .f_detach = filt_pipedetach,
         .f_event = filt_piperead,
 +       .f_touch = NULL,        
  };


 I would suggest to not initialize NULL in such C99 struct initializers
 and convert all non-C99 initializers to C99 style instead. That should reduce
 the diff dramatically and leave only the important parts.

 Martin

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org,
 gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Wed, 16 Sep 2020 16:09:36 -0400

 Martin,

 Thanks for your response! Just to confirm what you are saying:

 1. Skip changes like you have shown, where f_touch is initialized to 
 NULL, assuming the default value for unspecified fields (= 0).

 2. Change structs such as { 1, NULL, filt_apmrdetach, filt_apmread}
 to {
 	.f_isfd = 1,
 	.f_detach = filt_apmrdetach,
 	.f_event = filt_apmread,
 }

 Is it correct?

 Ruslan

 On 9/16/20 3:25 PM, Martin Husemann wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: Martin Husemann <martin@duskware.de>
 > To: gnats-bugs@netbsd.org
 > Cc:
 > Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
 > Date: Wed, 16 Sep 2020 21:20:59 +0200
 > 
 >   Style nit:
 >   
 >   @@ -1092,6 +1092,7 @@ static const struct filterops pipe_rfiltops = {
 >           .f_attach = NULL,
 >           .f_detach = filt_pipedetach,
 >           .f_event = filt_piperead,
 >   +       .f_touch = NULL,
 >    };
 >   
 >   
 >   I would suggest to not initialize NULL in such C99 struct initializers
 >   and convert all non-C99 initializers to C99 style instead. That should reduce
 >   the diff dramatically and leave only the important parts.
 >   
 >   Martin
 >   
 > 

From: Martin Husemann <martin@duskware.de>
To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Thu, 17 Sep 2020 07:25:10 +0200

 On Wed, Sep 16, 2020 at 04:09:36PM -0400, Ruslan Nikolaev wrote:
 > 1. Skip changes like you have shown, where f_touch is initialized to NULL,
 > assuming the default value for unspecified fields (= 0).
 > 
 > 2. Change structs such as { 1, NULL, filt_apmrdetach, filt_apmread}
 > to {
 > 	.f_isfd = 1,
 > 	.f_detach = filt_apmrdetach,
 > 	.f_event = filt_apmread,
 > }

 Yes, exactly!

 Martin

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org,
 gnats-admin@netbsd.org, netbsd-bugs@netbsd.org, martin@duskware.de
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Thu, 17 Sep 2020 12:29:15 -0400

 I attached a new patch below with changes per Martin's recommendations. 
 Please let me know if someone can review that.


 diff --git a/share/man/man9/kfilter_register.9 
 b/share/man/man9/kfilter_register.9
 index 43ba379b8c40..f405819140aa 100644
 --- a/share/man/man9/kfilter_register.9
 +++ b/share/man/man9/kfilter_register.9
 @@ -97,6 +97,7 @@ struct filterops {
   				/* called when knote is DELETEd */
   	int	(*f_event)(struct knote *kn, long hint);
   				/* called when event is triggered */
 +	void	(*f_touch)(struct knote *kn, struct kevent *kev, long hint);
   };
   .Ed
   .Pp
 diff --git a/sys/arch/arm/xscale/pxa2x0_apm.c 
 b/sys/arch/arm/xscale/pxa2x0_apm.c
 index f27b56901f82..869e392fb9f1 100644
 --- a/sys/arch/arm/xscale/pxa2x0_apm.c
 +++ b/sys/arch/arm/xscale/pxa2x0_apm.c
 @@ -104,8 +104,12 @@ void	filt_apmrdetach(struct knote *kn);
   int	filt_apmread(struct knote *kn, long hint);
   int	apmkqfilter(dev_t dev, struct knote *kn);

 -struct filterops apmread_filtops =
 -	{ 1, NULL, filt_apmrdetach, filt_apmread};
 +struct filterops apmread_filtops = {
 +	.f_isfd = 1,
 +	.f_attach = NULL,
 +	.f_detach = filt_apmrdetach,
 +	.f_event = filt_apmread,
 +};
   #endif

   /*
 diff --git a/sys/arch/macppc/dev/apm.c b/sys/arch/macppc/dev/apm.c
 index c96f2aac2b2d..ae8b15cffe5b 100644
 --- a/sys/arch/macppc/dev/apm.c
 +++ b/sys/arch/macppc/dev/apm.c
 @@ -431,8 +431,12 @@ filt_apmread(struct knote *kn, long hint)
   	return (kn->kn_data > 0);
   }

 -static struct filterops apmread_filtops =
 -	{ 1, NULL, filt_apmrdetach, filt_apmread};
 +static struct filterops apmread_filtops = {
 +	.f_isfd = 1,
 +	.f_attach = NULL,
 +	.f_detach = filt_apmrdetach,
 +	.f_event = filt_apmread,
 +};

   int
   apmkqfilter(dev_t dev, struct knote *kn)
 diff --git a/sys/arch/mips/ralink/ralink_gpio.c 
 b/sys/arch/mips/ralink/ralink_gpio.c
 index 127acd6b5991..a073029ad698 100644
 --- a/sys/arch/mips/ralink/ralink_gpio.c
 +++ b/sys/arch/mips/ralink/ralink_gpio.c
 @@ -495,10 +495,10 @@ static int  gpio_event_app_user_event(struct knote 
 *, long);
   static struct klist knotes;
   static int app_filter_id;
   static struct filterops app_fops = {
 -	0,
 -	gpio_event_app_user_attach,
 -	gpio_event_app_user_detach,
 -	gpio_event_app_user_event
 +	.f_isfd = 0,
 +	.f_attach = gpio_event_app_user_attach,
 +	.f_detach = gpio_event_app_user_detach,
 +	.f_event = gpio_event_app_user_event,
   };
   static struct callout led_tick_callout;
   static int gpio_driver_blink_leds = 1;
 diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
 index 6ed5bec32e52..f8bb3968e84a 100644
 --- a/sys/kern/kern_event.c
 +++ b/sys/kern/kern_event.c
 @@ -31,6 +31,7 @@

   /*-
    * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
 + * Copyright (c) 2009 Apple, Inc
    * All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
 @@ -109,6 +110,10 @@ static int	filt_timer(struct knote *, long hint);
   static int	filt_fsattach(struct knote *kn);
   static void	filt_fsdetach(struct knote *kn);
   static int	filt_fs(struct knote *kn, long hint);
 +static int	filt_userattach(struct knote *);
 +static void	filt_userdetach(struct knote *);
 +static int	filt_user(struct knote *, long hint);
 +static void	filt_usertouch(struct knote *, struct kevent *, long type);

   static const struct fileops kqueueops = {
   	.fo_name = "kqueue",
 @@ -128,6 +133,7 @@ static const struct filterops kqread_filtops = {
   	.f_attach = NULL,
   	.f_detach = filt_kqdetach,
   	.f_event = filt_kqueue,
 +	.f_touch = NULL,
   };

   static const struct filterops proc_filtops = {
 @@ -135,6 +141,7 @@ static const struct filterops proc_filtops = {
   	.f_attach = filt_procattach,
   	.f_detach = filt_procdetach,
   	.f_event = filt_proc,
 +	.f_touch = NULL,
   };

   static const struct filterops file_filtops = {
 @@ -142,6 +149,7 @@ static const struct filterops file_filtops = {
   	.f_attach = filt_fileattach,
   	.f_detach = NULL,
   	.f_event = NULL,
 +	.f_touch = NULL,
   };

   static const struct filterops timer_filtops = {
 @@ -149,6 +157,7 @@ static const struct filterops timer_filtops = {
   	.f_attach = filt_timerattach,
   	.f_detach = filt_timerdetach,
   	.f_event = filt_timer,
 +	.f_touch = NULL,
   };

   static const struct filterops fs_filtops = {
 @@ -156,6 +165,15 @@ static const struct filterops fs_filtops = {
   	.f_attach = filt_fsattach,
   	.f_detach = filt_fsdetach,
   	.f_event = filt_fs,
 +	.f_touch = NULL,
 +};
 +
 +static const struct filterops user_filtops = {
 +	.f_isfd = 0,
 +	.f_attach = filt_userattach,
 +	.f_detach = filt_userdetach,
 +	.f_event = filt_user,
 +	.f_touch = filt_usertouch,
   };

   static u_int	kq_ncallouts = 0;
 @@ -192,6 +210,7 @@ static struct kfilter sys_kfilters[] = {
   	{ "EVFILT_SIGNAL",	EVFILT_SIGNAL,	0, &sig_filtops, 0 },
   	{ "EVFILT_TIMER",	EVFILT_TIMER,	0, &timer_filtops, 0 },
   	{ "EVFILT_FS",		EVFILT_FS,	0, &fs_filtops, 0 },
 +	{ "EVFILT_USER",	EVFILT_USER,	0, &user_filtops, 0 },
   	{ NULL,			0,		0, NULL, 0 },
   };

 @@ -776,6 +795,106 @@ filt_fs(struct knote *kn, long hint)
   	return rv;
   }

 +static int
 +filt_userattach(struct knote *kn)
 +{
 +	struct kqueue *kq = kn->kn_kq;
 +
 +	/*
 +	 * EVFILT_USER knotes are not attached to anything in the kernel.
 +	 */
 +	mutex_spin_enter(&kq->kq_lock);
 +	kn->kn_hook = NULL;
 +	if (kn->kn_fflags & NOTE_TRIGGER)
 +		kn->kn_hookid = 1;
 +	else
 +		kn->kn_hookid = 0;
 +	mutex_spin_exit(&kq->kq_lock);
 +	return (0);
 +}
 +
 +static void
 +filt_userdetach(struct knote *kn)
 +{
 +
 +	/*
 +	 * EVFILT_USER knotes are not attached to anything in the kernel.
 +	 */
 +}
 +
 +static int
 +filt_user(struct knote *kn, long hint)
 +{
 +	struct kqueue *kq = kn->kn_kq;
 +	int hookid;
 +
 +	mutex_spin_enter(&kq->kq_lock);
 +	hookid = kn->kn_hookid;
 +	mutex_spin_exit(&kq->kq_lock);
 +
 +	return hookid;
 +}
 +
 +static void
 +filt_usertouch(struct knote *kn, struct kevent *kev, long type)
 +{
 +	struct kqueue *kq = kn->kn_kq;
 +	int ffctrl;
 +
 +	mutex_spin_enter(&kq->kq_lock);
 +	switch (type) {
 +	case EVENT_REGISTER:
 +		if (kev->fflags & NOTE_TRIGGER)
 +			kn->kn_hookid = 1;
 +
 +		ffctrl = kev->fflags & NOTE_FFCTRLMASK;
 +		kev->fflags &= NOTE_FFLAGSMASK;
 +		switch (ffctrl) {
 +		case NOTE_FFNOP:
 +			break;
 +
 +		case NOTE_FFAND:
 +			kn->kn_sfflags &= kev->fflags;
 +			break;
 +
 +		case NOTE_FFOR:
 +			kn->kn_sfflags |= kev->fflags;
 +			break;
 +
 +		case NOTE_FFCOPY:
 +			kn->kn_sfflags = kev->fflags;
 +			break;
 +
 +		default:
 +			/* XXX Return error? */
 +			break;
 +		}
 +		kn->kn_sdata = kev->data;
 +		if (kev->flags & EV_CLEAR) {
 +			kn->kn_hookid = 0;
 +			kn->kn_data = 0;
 +			kn->kn_fflags = 0;
 +		}
 +		break;
 +
 +	case EVENT_PROCESS:
 +		*kev = kn->kn_kevent;
 +		kev->fflags = kn->kn_sfflags;
 +		kev->data = kn->kn_sdata;
 +		if (kn->kn_flags & EV_CLEAR) {
 +			kn->kn_hookid = 0;
 +			kn->kn_data = 0;
 +			kn->kn_fflags = 0;
 +		}
 +		break;
 +
 +	default:
 +		panic("filt_usertouch() - invalid type (%ld)", type);
 +		break;
 +	}
 +	mutex_spin_exit(&kq->kq_lock);
 +}
 +
   /*
    * filt_seltrue:
    *
 @@ -809,6 +928,7 @@ const struct filterops seltrue_filtops = {
   	.f_attach = NULL,
   	.f_detach = filt_seltruedetach,
   	.f_event = filt_seltrue,
 +	.f_touch = NULL,
   };

   int
 @@ -1072,8 +1192,8 @@ kqueue_register(struct kqueue *kq, struct kevent *kev)
   	/*
   	 * kn now contains the matching knote, or NULL if no match
   	 */
 -	if (kev->flags & EV_ADD) {
 -		if (kn == NULL) {
 +	if (kn == NULL) {
 +		if (kev->flags & EV_ADD) {
   			/* create new knote */
   			kn = newkn;
   			newkn = NULL;
 @@ -1137,41 +1257,51 @@ kqueue_register(struct kqueue *kq, struct kevent 
 *kev)
   				goto done;
   			}
   			atomic_inc_uint(&kfilter->refcnt);
 +			goto done_ev_add;
   		} else {
 -			/*
 -			 * The user may change some filter values after the
 -			 * initial EV_ADD, but doing so will not reset any
 -			 * filter which have already been triggered.
 -			 */
 -			kn->kn_sfflags = kev->fflags;
 -			kn->kn_sdata = kev->data;
 -			kn->kn_kevent.udata = kev->udata;
 -		}
 -		/*
 -		 * We can get here if we are trying to attach
 -		 * an event to a file descriptor that does not
 -		 * support events, and the attach routine is
 -		 * broken and does not return an error.
 -		 */
 -		KASSERT(kn->kn_fop != NULL);
 -		KASSERT(kn->kn_fop->f_event != NULL);
 -		KERNEL_LOCK(1, NULL);			/* XXXSMP */
 -		rv = (*kn->kn_fop->f_event)(kn, 0);
 -		KERNEL_UNLOCK_ONE(NULL);		/* XXXSMP */
 -		if (rv)
 -			knote_activate(kn);
 -	} else {
 -		if (kn == NULL) {
 +			/* No matching knote and the EV_ADD flag is not set. */
   			error = ENOENT;
   			goto doneunlock;
   		}
 -		if (kev->flags & EV_DELETE) {
 -			/* knote_detach() drops fdp->fd_lock */
 -			knote_detach(kn, fdp, true);
 -			goto done;
 -		}
   	}

 +	if (kev->flags & EV_DELETE) {
 +		/* knote_detach() drops fdp->fd_lock */
 +		knote_detach(kn, fdp, true);
 +		goto done;
 +	}
 +
 +	/*
 +	 * The user may change some filter values after the
 +	 * initial EV_ADD, but doing so will not reset any
 +	 * filter which have already been triggered.
 +	 */
 +	kn->kn_kevent.udata = kev->udata;
 +	KASSERT(kn->kn_fop != NULL);
 +	if (!kn->kn_fop->f_isfd && kn->kn_fop->f_touch != NULL) {
 +		KERNEL_LOCK(1, NULL);		/* XXXSMP */
 +		(*kn->kn_fop->f_touch)(kn, kev, EVENT_REGISTER);
 +		KERNEL_UNLOCK_ONE(NULL);	/* XXXSMP */
 +	} else {
 +		kn->kn_sfflags = kev->fflags;
 +		kn->kn_sdata = kev->data;
 +	}
 +
 +	/*
 +	 * We can get here if we are trying to attach
 +	 * an event to a file descriptor that does not
 +	 * support events, and the attach routine is
 +	 * broken and does not return an error.
 +	 */
 +done_ev_add:
 +	KASSERT(kn->kn_fop != NULL);
 +	KASSERT(kn->kn_fop->f_event != NULL);
 +	KERNEL_LOCK(1, NULL);			/* XXXSMP */
 +	rv = (*kn->kn_fop->f_event)(kn, 0);
 +	KERNEL_UNLOCK_ONE(NULL);		/* XXXSMP */
 +	if (rv)
 +		knote_activate(kn);
 +
   	/* disable knote */
   	if ((kev->flags & EV_DISABLE)) {
   		mutex_spin_enter(&kq->kq_lock);
 @@ -1271,7 +1401,7 @@ kqueue_scan(file_t *fp, size_t maxevents, struct 
 kevent *ulistp,
   	struct timespec	ats, sleepts;
   	struct knote	*kn, *marker, morker;
   	size_t		count, nkev, nevents;
 -	int		timeout, error, rv;
 +	int		timeout, error, touch, rv;
   	filedesc_t	*fdp;

   	fdp = curlwp->l_fd;
 @@ -1382,8 +1512,20 @@ kqueue_scan(file_t *fp, size_t maxevents, struct 
 kevent *ulistp,
   					continue;
   				}
   			}
 +			KASSERT(kn->kn_fop != NULL);
 +			touch = (!kn->kn_fop->f_isfd &&
 +					kn->kn_fop->f_touch != NULL);
   			/* XXXAD should be got from f_event if !oneshot. */
 -			*kevp++ = kn->kn_kevent;
 +			if (touch) {
 +				mutex_spin_exit(&kq->kq_lock);
 +				KERNEL_LOCK(1, NULL);		/* XXXSMP */
 +				(*kn->kn_fop->f_touch)(kn, kevp, EVENT_PROCESS);
 +				KERNEL_UNLOCK_ONE(NULL);	/* XXXSMP */
 +				mutex_spin_enter(&kq->kq_lock);
 +			} else {
 +				*kevp = kn->kn_kevent;
 +			}
 +			kevp++;
   			nkev++;
   			if (kn->kn_flags & EV_ONESHOT) {
   				/* delete ONESHOT events after retrieval */
 @@ -1396,6 +1538,14 @@ kqueue_scan(file_t *fp, size_t maxevents, struct 
 kevent *ulistp,
   				/* clear state after retrieval */
   				kn->kn_data = 0;
   				kn->kn_fflags = 0;
 +				/*
 +				 * Manually clear knotes who weren't
 +				 * 'touch'ed.
 +				 */
 +				if (touch == 0) {
 +					kn->kn_data = 0;
 +					kn->kn_fflags = 0;
 +				}
   				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE|KN_BUSY);
   			} else if (kn->kn_flags & EV_DISPATCH) {
   				kn->kn_status |= KN_DISABLED;
 diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c
 index d6505938e6c3..4176a57402c6 100644
 --- a/sys/net/if_tap.c
 +++ b/sys/net/if_tap.c
 @@ -1232,10 +1232,19 @@ tap_dev_poll(int unit, int events, struct lwp *l)
   	return revents;
   }

 -static struct filterops tap_read_filterops = { 1, NULL, tap_kqdetach,
 -	tap_kqread };
 -static struct filterops tap_seltrue_filterops = { 1, NULL, tap_kqdetach,
 -	filt_seltrue };
 +static struct filterops tap_read_filterops = {
 +	.f_isfd = 1,
 +	.f_attach = NULL,
 +	.f_detach = tap_kqdetach,
 +	.f_event = tap_kqread,
 +};
 +
 +static struct filterops tap_seltrue_filterops = {
 +	.f_isfd = 1,
 +	.f_attach = NULL,
 +	.f_detach = tap_kqdetach,
 +	.f_event = filt_seltrue,
 +};

   static int
   tap_cdev_kqfilter(dev_t dev, struct knote *kn)
 diff --git a/sys/sys/event.h b/sys/sys/event.h
 index dd18e51bb4ce..2473d7b220e3 100644
 --- a/sys/sys/event.h
 +++ b/sys/sys/event.h
 @@ -44,7 +44,8 @@
   #define	EVFILT_SIGNAL		5U	/* attached to struct proc */
   #define	EVFILT_TIMER		6U	/* arbitrary timer (in ms) */
   #define	EVFILT_FS		7U	/* filesystem events */
 -#define	EVFILT_SYSCOUNT		8U	/* number of filters */
 +#define	EVFILT_USER		8U	/* user events */
 +#define	EVFILT_SYSCOUNT		9U	/* number of filters */

   struct kevent {
   	uintptr_t	ident;		/* identifier for this event */
 @@ -90,6 +91,26 @@ _EV_SET(struct kevent *_kevp, uintptr_t _ident, 
 uint32_t _filter,
   #define	EV_EOF		0x8000U		/* EOF detected */
   #define	EV_ERROR	0x4000U		/* error, data contains errno */

 +/*
 + * data/hint flags/masks for EVFILT_USER, shared with userspace
 + *
 + * On input, the top two bits of fflags specifies how the lower twenty four
 + * bits should be applied to the stored value of fflags.
 + *
 + * On output, the top two bits will always be set to NOTE_FFNOP and the
 + * remaining twenty four bits will contain the stored fflags value.
 + */
 +#define	NOTE_FFNOP	0x00000000U		/* ignore input fflags */
 +#define	NOTE_FFAND	0x40000000U		/* AND fflags */
 +#define	NOTE_FFOR	0x80000000U		/* OR fflags */
 +#define	NOTE_FFCOPY	0xc0000000U		/* copy fflags */
 +
 +#define	NOTE_FFCTRLMASK	0xc0000000U		/* masks for operations */
 +#define	NOTE_FFLAGSMASK	0x00ffffffU
 +
 +#define	NOTE_TRIGGER	0x01000000U		/* Cause the event to be
 +						   triggered for output. */
 +
   /*
    * hint flag for in-kernel use - must not equal any existing note
    */
 @@ -161,6 +182,16 @@ struct kfilter_mapping {
    */
   #define	NOTE_SIGNAL	0x08000000U

 +/*
 + * Hint values for the optional f_touch event filter.  If f_touch is 
 not set
 + * to NULL and f_isfd is zero the f_touch filter will be called with 
 the type
 + * argument set to EVENT_REGISTER during a kevent() system call.  It is 
 also
 + * called under the same conditions with the type argument set to 
 EVENT_PROCESS
 + * when the event has been triggered.
 + */
 +#define	EVENT_REGISTER	1
 +#define	EVENT_PROCESS	2
 +
   /*
    * Callback methods for each filter type.
    */
 @@ -172,6 +203,7 @@ struct filterops {
   					/* called when knote is DELETEd */
   	int	(*f_event)	(struct knote *, long);
   					/* called when event is triggered */
 +	void	(*f_touch)	(struct knote *, struct kevent *, long);
   };

   /*
 @@ -196,6 +228,7 @@ struct knote {
   	const struct filterops	*kn_fop;
   	struct kfilter		*kn_kfilter;
   	void 			*kn_hook;
 +	int			kn_hookid;

   #define	KN_ACTIVE	0x01U			/* event has been triggered */
   #define	KN_QUEUED	0x02U			/* event is on queue */

 ------------------------------------------------------------------------------------------




 On 9/17/20 1:30 AM, Martin Husemann wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: Martin Husemann <martin@duskware.de>
 > To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
 > Cc: gnats-bugs@netbsd.org
 > Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
 > Date: Thu, 17 Sep 2020 07:25:10 +0200
 > 
 >   On Wed, Sep 16, 2020 at 04:09:36PM -0400, Ruslan Nikolaev wrote:
 >   > 1. Skip changes like you have shown, where f_touch is initialized to NULL,
 >   > assuming the default value for unspecified fields (= 0).
 >   >
 >   > 2. Change structs such as { 1, NULL, filt_apmrdetach, filt_apmread}
 >   > to {
 >   > 	.f_isfd = 1,
 >   > 	.f_detach = filt_apmrdetach,
 >   > 	.f_event = filt_apmread,
 >   > }
 >   
 >   Yes, exactly!
 >   
 >   Martin
 >   
 > 

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org,
 gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Wed, 23 Sep 2020 15:29:49 -0400

 I was wondering if there is any update on that.

From: Martin Husemann <martin@duskware.de>
To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Thu, 24 Sep 2020 07:55:55 +0200

 Can you explain the changes?

 It is not obvious (at least to me) how it works and why this intrusive
 changes to the inner workings of kevent are needed.

 Martin

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org,
 gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Fri, 25 Sep 2020 01:52:43 -0400

 Martin, I will try to get back with a summary of changes ASAP.

 On 9/24/20 2:00 AM, Martin Husemann wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: Martin Husemann <martin@duskware.de>
 > To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
 > Cc: gnats-bugs@netbsd.org
 > Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
 > Date: Thu, 24 Sep 2020 07:55:55 +0200
 > 
 >   Can you explain the changes?
 >   
 >   It is not obvious (at least to me) how it works and why this intrusive
 >   changes to the inner workings of kevent are needed.
 >   
 >   Martin
 >   
 > 

Responsible-Changed-From-To: kern-bug-people->thorpej
Responsible-Changed-By: maya@NetBSD.org
Responsible-Changed-When: Sun, 27 Sep 2020 21:16:12 +0000
Responsible-Changed-Why:
(by request)


Responsible-Changed-From-To: thorpej->kern-bug-people
Responsible-Changed-By: maya@NetBSD.org
Responsible-Changed-When: Sun, 27 Sep 2020 21:16:44 +0000
Responsible-Changed-Why:
Ooops, wrong bug.


From: John Nemeth <jnemeth@cue.bc.ca>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org, gnats-admin@netbsd.org,
        netbsd-bugs@netbsd.org, nruslan_devel@yahoo.com
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Mon, 12 Oct 2020 01:38:11 -0700

 Hi Ruslan,

      On Sept. 25th, you stated that you would try to get back with
 a summary of changes ASAP.  That was over two weeks ago.  Are there
 any updates? In internal discussions, we were wondering about the
 f_touch stuff.  Is that really necessary?

      I was looking at this awhile ago, but got sidetracked.  I
 would like to see this, and would probably tackle it.

From: Joerg Sonnenberger <joerg@bec.de>
To: gnats-bugs@netbsd.org
Cc: kern-bug-people@netbsd.org, gnats-admin@netbsd.org,
	netbsd-bugs@netbsd.org, nruslan_devel@yahoo.com
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Mon, 12 Oct 2020 12:51:38 +0200

 On Mon, Oct 12, 2020 at 08:40:02AM +0000, John Nemeth wrote:
 >       I was looking at this awhile ago, but got sidetracked.  I
 >  would like to see this, and would probably tackle it.

 I don't really see the point of EVFILT_USER, especially with the diff
 posted the first time. It adds very little over what can be done with a
 simple pipe in userland already and seems actually more limited than the
 pipe solution too.

 Joerg

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, kern-bug-people@netbsd.org,
 gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
Date: Mon, 12 Oct 2020 17:08:01 -0400

 Sorry, I did not have time to write a summary yet. Hopefully, I will get 
 more time this week and will give some update.

 I guess, the larger point was that some applications (e.g., nginx and 
 others) already use this feature because FreeBSD implements it. 
 Otherwise, they would compile a much more restricted version -- that was 
 the reason why it could be really beneficial.

 On 10/12/20 6:55 AM, Joerg Sonnenberger wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: Joerg Sonnenberger <joerg@bec.de>
 > To: gnats-bugs@netbsd.org
 > Cc: kern-bug-people@netbsd.org, gnats-admin@netbsd.org,
 > 	netbsd-bugs@netbsd.org, nruslan_devel@yahoo.com
 > Subject: Re: kern/55663: Support for EVFILT_USER in kqueue(2)
 > Date: Mon, 12 Oct 2020 12:51:38 +0200
 > 
 >   On Mon, Oct 12, 2020 at 08:40:02AM +0000, John Nemeth wrote:
 >   >       I was looking at this awhile ago, but got sidetracked.  I
 >   >  would like to see this, and would probably tackle it.
 >   
 >   I don't really see the point of EVFILT_USER, especially with the diff
 >   posted the first time. It adds very little over what can be done with a
 >   simple pipe in userland already and seems actually more limited than the
 >   pipe solution too.
 >   
 >   Joerg
 >   
 > 

Responsible-Changed-From-To: kern-bug-people->jnemeth
Responsible-Changed-By: jnemeth@NetBSD.org
Responsible-Changed-When: Tue, 13 Oct 2020 04:47:46 +0000
Responsible-Changed-Why:
Take.


From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: "jnemeth@netbsd.org" <jnemeth@netbsd.org>, 
	"kern-bug-people@netbsd.org" <kern-bug-people@netbsd.org>, 
	"netbsd-bugs@netbsd.org" <netbsd-bugs@netbsd.org>, 
	"gnats-admin@netbsd.org" <gnats-admin@netbsd.org>, 
	"gnats-bugs@netbsd.org" <gnats-bugs@netbsd.org>
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Tue, 20 Oct 2020 20:31:34 +0000 (UTC)

 I greatly apologize for the delay. Recently, I had so many things to do...
 Please keep in mind that my understanding may be incomplete since I have no=
 t really introduced this feature, I just ported it from FreeBSD/OpenBSD. In=
  fact, I have far more experience with epoll() rather than kqueue(). So, le=
 t me start the discussion with the motivation first, and then we can go to =
 specific technical details (in a separate email which I will try to prepare=
  next as needed).

 1. Why is it needed?

 Quite a few applications use it. To be specific, please consider Nginx. Alt=
 hough, it *does* compile under NetBSD, several critical features are missin=
 g without EVFILT_USER.

 Take a look at https://nginx.googlesource.com/nginx/+/refs/heads/master/src=
 /core/ngx_thread_pool.c:

 static ngx_int_t
 ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
 {
 ...
 =C2=A0=C2=A0=C2=A0 if (ngx_notify_init =3D=3D NULL || ngx_notify =3D=3D NUL=
 L) {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ngx_log_error(NGX_LOG_ALERT, cyc=
 le->log, 0,
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0 "the configured event method cannot be used with thread pools");
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return NGX_ERROR;
 =C2=A0=C2=A0=C2=A0 }

 ...


 Note that if ngx_notify is *NOT* initialized to some function, Nginx will n=
 ot support the thread pool mode, which is arguably a very important Nginx f=
 eature that boosts performance greatly!

 So, where is this function initialized? For kqueue, Nginx provides https://=
 nginx.googlesource.com/nginx/+/refs/heads/master/src/event/modules/ngx_kque=
 ue_module.c

 Note these lines:
 static ngx_event_module_t=C2=A0 ngx_kqueue_module_ctx =3D {
 ...
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NULL,=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /*=
  delete an connection */
 #ifdef EVFILT_USER
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ngx_kqueue_notify_init,=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /* init a notify =
 */
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ngx_kqueue_notify,=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0 /* trigger a notify */
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NULL,=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /*=
  close a notify */
 #else
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NULL,=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /*=
  init a notify */
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NULL,=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /*=
  trigger a notify */
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NULL,=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /*=
  close a notify */
 #endif
 ...

 So, in other words, Nginx compiles and runs but lacks the thread pool suppo=
 rt, which is an important feature. I am not saying that they could not have=
  implemented it differently, but quite a few applications just assume you h=
 ave EVFILT_USER and do not provide any good fallback.
 If we can just implement EVFILT_USER as in FreeBSD (regardless of its merit=
 s), that would take care of those cases completely.

 As another example, see the bug report for mySQL: http://gnats.netbsd.org/5=
 4627
 Without EVFILT_USER, it will even have uninitialized data

 2. Regarding the EVFILT_USER itself, the FreeBSD man page says the followin=
 g:

 EVFILT_USER=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Establishes a user event identifi=
 ed by ident which is
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 not associated with any kernel mechanism but is trig-
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 gered by user level code.=C2=A0 The lower 24 bits of =
 the
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 fflags may be used for user defined flags and manipu-
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 lated using the following:

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFNOP=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0 Ignore the input fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFAND=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0 Bitwise AND fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFOR=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0 Bitwise OR fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFCOPY=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0 Copy fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFCTRLMASK=C2=A0=C2=A0=C2=A0=C2=A0 Control mask =
 for fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_FFLAGSMASK=C2=A0=C2=A0=C2=A0=C2=A0 User defined =
 flag mask for
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fflags.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 A user event is triggered for output with the follow-
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 ing:

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 NOTE_TRIGGER=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0 Cause the event to be triggered.

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 On return, fflags contains the users defined flags in
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0=C2=A0 the lower 24 bits.
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
 =A0=C2=A0=C2=A0 =C2=A0
 Please let me know if EVFILT_USER may sound interesting for NetBSD. If so, =
 I can try to recollect technical details (it has been really a while). But =
 for the most part it was based on OpenBSD's patch (with some adaptations fo=
 r NetBSD): https://www.mail-archive.com/tech@openbsd.org/msg42433.html


 Thanks,
 Ruslan

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, kern-bug-people@netbsd.org,
 netbsd-bugs@netbsd.org, gnats-admin@netbsd.org
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Tue, 20 Oct 2020 17:21:29 -0400

 [I am re-sending the email, the previous message was corrupted by the 
 email client ]

 I greatly apologize for the delay. Recently, I had so many things to do...
 Please keep in mind that my understanding may be incomplete since I have 
 not really introduced this feature, I just ported it from 
 FreeBSD/OpenBSD. In fact, I have far more experience with epoll() rather 
 than kqueue(). So, let me start the discussion with the motivation 
 first, and then we can go to specific technical details (in a separate 
 email which I will try to prepare next as needed).

 1. Why is it needed?

 Quite a few applications use it. To be specific, please consider Nginx. 
 Although, it *does* compile under NetBSD, several critical features are 
 missing without EVFILT_USER.

 Take a look at 
 https://nginx.googlesource.com/nginx/+/refs/heads/master/src/core/ngx_thread_pool.c:

 static ngx_int_t
 ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
 {
 ...
      if (ngx_notify_init == NULL || ngx_notify == NULL) {
          ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                 "the configured event method cannot be used with thread 
 pools");
          return NGX_ERROR;
      }

 ...


 Note that if ngx_notify is *NOT* initialized to some function, Nginx 
 will not support the thread pool mode, which is arguably a very 
 important Nginx feature that boosts performance greatly!

 So, where is this function initialized? For kqueue, Nginx provides 
 https://nginx.googlesource.com/nginx/+/refs/heads/master/src/event/modules/ngx_kqueue_module.c

 Note these lines:
 static ngx_event_module_t  ngx_kqueue_module_ctx = {
 ...
          NULL,                              /* delete an connection */
 #ifdef EVFILT_USER
          ngx_kqueue_notify_init,            /* init a notify */
          ngx_kqueue_notify,                 /* trigger a notify */
          NULL,                              /* close a notify */
 #else
          NULL,                              /* init a notify */
          NULL,                              /* trigger a notify */
          NULL,                              /* close a notify */
 #endif
 ...

 So, in other words, Nginx compiles and runs but lacks the thread pool 
 support, which is an important feature. I am not saying that they could 
 not have implemented it differently, but quite a few applications just 
 assume you have EVFILT_USER and do not provide any good fallback.
 If we can just implement EVFILT_USER as in FreeBSD (regardless of its 
 merits), that would take care of those cases completely.

 As another example, see the bug report for mySQL: 
 http://gnats.netbsd.org/54627
 Without EVFILT_USER, it will even have uninitialized data

 2. Regarding the EVFILT_USER itself, the FreeBSD man page says the 
 following:

 EVFILT_USER      Establishes a user event identified by ident which is
                   not associated with any kernel mechanism but is trig-
                   gered by user level code.  The lower 24 bits of the
                   fflags may be used for user defined flags and manipu-
                   lated using the following:

                   NOTE_FFNOP          Ignore the input fflags.

                   NOTE_FFAND          Bitwise AND fflags.

                   NOTE_FFOR           Bitwise OR fflags.

                   NOTE_FFCOPY         Copy fflags.

                   NOTE_FFCTRLMASK     Control mask for fflags.

                   NOTE_FFLAGSMASK     User defined flag mask for
                                       fflags.

                   A user event is triggered for output with the follow-
                   ing:

                   NOTE_TRIGGER        Cause the event to be triggered.

                   On return, fflags contains the users defined flags in
                   the lower 24 bits.

 Please let me know if EVFILT_USER may sound interesting for NetBSD. If 
 so, I can try to recollect technical details (it has been really a 
 while). But for the most part it was based on OpenBSD's patch (with some 
 adaptations for NetBSD): 
 https://www.mail-archive.com/tech@openbsd.org/msg42433.html


 Thanks,
 Ruslan

From: Christos Zoulas <christos@zoulas.com>
To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
Cc: "jnemeth@netbsd.org" <jnemeth@netbsd.org>,
 "kern-bug-people@netbsd.org" <kern-bug-people@netbsd.org>,
 "netbsd-bugs@netbsd.org" <netbsd-bugs@netbsd.org>,
 "gnats-admin@netbsd.org" <gnats-admin@netbsd.org>,
 "gnats-bugs@netbsd.org" <gnats-bugs@netbsd.org>
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Tue, 20 Oct 2020 17:45:32 -0400

 --Apple-Mail=_766A3B26-EA7C-479C-82B3-A20CF787FB75
 Content-Transfer-Encoding: 7bit
 Content-Type: text/plain;
 	charset=us-ascii

 I think that although the functionality can be implemented in userland,
 having a consistent feature-set in the BSD API's is desirable
 so that 3rd party software can rely on functionality to be present.
 I support adding it.

 christos

 --Apple-Mail=_766A3B26-EA7C-479C-82B3-A20CF787FB75
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCX49afAAKCRBxESqxbLM7
 OghaAJkBpy1/egbOCg7nKMdhDyZCV2HLmACgz6zqTIht1qJKi7pHsPbpEAt4WbI=
 =0u8X
 -----END PGP SIGNATURE-----

 --Apple-Mail=_766A3B26-EA7C-479C-82B3-A20CF787FB75--

From: Joerg Sonnenberger <joerg@bec.de>
To: gnats-bugs@netbsd.org
Cc: jnemeth@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
	nruslan_devel@yahoo.com
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Wed, 21 Oct 2020 02:05:26 +0200

 On Tue, Oct 20, 2020 at 09:25:01PM +0000, Ruslan Nikolaev wrote:
 >  1. Why is it needed?
 >  
 >  Quite a few applications use it. To be specific, please consider Nginx. 
 >  Although, it *does* compile under NetBSD, several critical features are 
 >  missing without EVFILT_USER.

 That's a strawman. By that reasoning, we should add epoll(2). There is
 nothing in Nginx that needs it, just that the code currently requires
 it. That's a big difference.

 Joerg

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Tue, 20 Oct 2020 22:16:31 -0400

 Yes, I know that features should be added more sparingly... However, 
 this particular one is implemented more than 10 years ago in OS X and 
 FreeBSD. And some well-known applications such as nginx and mysql just 
 blindly assume it is present (and they are also probably tested more 
 when this option enabled as the mysql bug report suggests). It would be 
 good to maintain better NetBSD and FreeBSD compatibility if possible. 
 And it still does not fundamentally change the interface, unlike epoll 
 which would be a totally different story :)

 On 10/20/20 8:10 PM, Joerg Sonnenberger wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 >
 > From: Joerg Sonnenberger <joerg@bec.de>
 > To: gnats-bugs@netbsd.org
 > Cc: jnemeth@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
 > 	nruslan_devel@yahoo.com
 > Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
 > Date: Wed, 21 Oct 2020 02:05:26 +0200
 >
 >   On Tue, Oct 20, 2020 at 09:25:01PM +0000, Ruslan Nikolaev wrote:
 >   >  1. Why is it needed?
 >   >
 >   >  Quite a few applications use it. To be specific, please consider Nginx.
 >   >  Although, it *does* compile under NetBSD, several critical features are
 >   >  missing without EVFILT_USER.
 >   
 >   That's a strawman. By that reasoning, we should add epoll(2). There is
 >   nothing in Nginx that needs it, just that the code currently requires
 >   it. That's a big difference.
 >   
 >   Joerg
 >   

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Tue, 20 Oct 2020 23:33:49 -0400

 Also just to add regarding pipe-based solutions, some applications 
 decided to use EVFILT_USER instead. Looking on the Internet show the 
 following results (since 2010): e.g. libevent:

 Date: Fri, 17 Sep 2010 00:34:13 -0400
 Subject: Replace pipe-based notification with EVFILT_USER where possible
 Commit: 53a07fe2f95c360d060b9fd58dfcd929c2c1aac2

 Sufficiently recent kqueue implementations have an EVFILT_USER filter
 that we can use to wake up an event base from another thread.  When
 it's supported, we now use this mechanism rather than our old
 (pipe-based) mechanism.  This should save some time and complications
 on newer OSX and freebsds.
 ---

 https://sourceforge.net/p/levent/mailman/levent-commits/?viewmonth=201204

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Wed, 28 Oct 2020 04:37:20 -0400

 So, just wanted to check -- is there any interest in this option? Is 
 there any other information needed from me?

From: Kamil Rytarowski <kamil@netbsd.org>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org, nruslan_devel@yahoo.com
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Wed, 28 Oct 2020 13:43:28 +0100

 On 28.10.2020 09:40, Ruslan Nikolaev wrote:
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
 > To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 >  netbsd-bugs@netbsd.org
 > Cc: 
 > Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
 > Date: Wed, 28 Oct 2020 04:37:20 -0400
 > 
 >  So, just wanted to check -- is there any interest in this option? Is 
 >  there any other information needed from me?
 >  
 > 

 There is rather an agreement that we would want to see it implemented
 inside libc, similar to CTL_USER for sysctl(3).

From: jnemeth@CornerstoneService.ca (John Nemeth)
To: Kamil Rytarowski <kamil@netbsd.org>, gnats-bugs@netbsd.org,
        jnemeth@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
        nruslan_devel@yahoo.com
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Wed, 28 Oct 2020 20:01:18 -0700

 On Oct 28, 13:43, Kamil Rytarowski wrote:
 } On 28.10.2020 09:40, Ruslan Nikolaev wrote:
 } > From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
 } > Date: Wed, 28 Oct 2020 04:37:20 -0400
 } > 
 } >  So, just wanted to check -- is there any interest in this option? Is 
 } >  there any other information needed from me?

      Multiple people have expressed interest.  On Sept. 24th, Martin
 asked you to explain the changes.  On Sept. 25th and Oct. 12th you
 said you would do this.  On Oct. 20th you talked about why you
 wanted the change and posted a snippet from the FreeBSD manpage.
 I think everybody is in agreement about it being needed by a number
 of major packages.

      I'm guessing that English isn't your first language, so there
 may be a misunderstanding.  What was being requested is an explanation
 of the code.  In a previous message, I had asked about the f_touch
 part.  In particular, what we are wanting to know, is if this can
 be implemented without having to modify so many parts of the code?
 The flip side is that maybe we should just update our code to look
 like FreeBSD's as much as possible.

 } There is rather an agreement that we would want to see it implemented
 } inside libc, similar to CTL_USER for sysctl(3).

      I do not believe there is any such agreement (I certainly am
 not in agreement).  Also, to this point, only person has publically
 commented that they don't like the idea of this change.

 }-- End of excerpt from Kamil Rytarowski

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org
Cc: 
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Fri, 30 Oct 2020 00:33:53 -0400

 Thanks for the clarification, John! I just wanted to make sure that 
 there is some interest before we proceed. (This is why I also clarified 
 motivation for that option.)

 Just to be clear, I am not super familiar with this code, and as I 
 previously mentioned, I just ported those changes from OpenBSD/FreeBSD 
 so that I can use EVFILT_USER when compiling programs. That particular 
 piece of code in kqueue looked very similar to that of NetBSD, i.e., 
 that was not super hard to port it. So my knowledge may not necessarily 
 be complete. (For example, I cannot really say why f_touch is used since 
 it was in OpenBSD/FreeBSD code.) However, there are some specific 
 changes for NetBSD which I made due some differences with 
 OpenBSD/FreeBSD, I will recollect and post it here.

 Does it sound good?

From: matthew green <mrg@eterna.com.au>
To: Ruslan Nikolaev <nruslan_devel@yahoo.com>
Cc: gnats-bugs@netbsd.org, jnemeth@netbsd.org, gnats-admin@netbsd.org,
    netbsd-bugs@netbsd.org
Subject: re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Fri, 30 Oct 2020 20:24:12 +1100

 just to throw my hat in here as well -


 i am interested in a solution for EVFILT_USER support.
 there are many apps that would benefit from it.  (there
 are a few more events like directory updates, close,
 and write in freebsd i'd also like to see added.)

 i believe it could live in userspace, and that would be
 strongly preferably, but unless it's actually compatible
 with existing implementations (ie, they just start to
 work without patching beyond "use this freebsd path"),
 it seems not nearly as useful as it should be.


 thanks for working on this.


 .mrg.

From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: jnemeth@netbsd.org,
 gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org,
 nruslan_devel@yahoo.com
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Fri, 30 Oct 2020 08:58:26 -0400

 --Apple-Mail=_7162EED5-70FE-49B8-AF66-17D398974B7E
 Content-Transfer-Encoding: quoted-printable
 Content-Type: text/plain;
 	charset=us-ascii

 I would like to see it too for compatibility, and it is not a big deal =
 to me if it is
 in userland or the kernel. In fact perhaps it is better to be in the =
 kernel so everything
 is in one place, and the code does not add much complexity.

 christos


 --Apple-Mail=_7162EED5-70FE-49B8-AF66-17D398974B7E
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCX5wN8gAKCRBxESqxbLM7
 OhcJAJ40PLy0GD41xoTLTdZKaj2zHSztxwCfYNAjn9u8IbZw/Rg8knpxw1r2JFk=
 =Y/qK
 -----END PGP SIGNATURE-----

 --Apple-Mail=_7162EED5-70FE-49B8-AF66-17D398974B7E--

From: John Nemeth <jnemeth@cue.bc.ca>
To: matthew green <mrg@eterna.com.au>,
        Ruslan Nikolaev <nruslan_devel@yahoo.com>
Cc: gnats-bugs@netbsd.org
Subject: re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Fri, 30 Oct 2020 13:31:24 -0700

 On Oct 30, 20:24, matthew green wrote:
 }
 } just to throw my hat in here as well -
 } 
 } i am interested in a solution for EVFILT_USER support.
 } there are many apps that would benefit from it.  (there
 } are a few more events like directory updates, close,
 } and write in freebsd i'd also like to see added.)
 } 
 } i believe it could live in userspace, and that would be
 } strongly preferably, but unless it's actually compatible
 } with existing implementations (ie, they just start to
 } work without patching beyond "use this freebsd path"),
 } it seems not nearly as useful as it should be.

      We've seen cases where libc is bypassed by major things such
 as Rust or Go (I recall one of those having problems due to using
 syscalls directly, but I don't recall which one).  If we're going
 to be compatible with other BSDs then I strongly believe it has to
 be at the syscall level as much as possible and not at the API
 level.  For this reason, I believe that it must go in the kernel.

 }-- End of excerpt from matthew green

From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: jnemeth@netbsd.org,
 gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org,
 nruslan_devel@yahoo.com
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Fri, 30 Oct 2020 17:42:59 -0400

 --Apple-Mail=_B61E97F7-BB02-4F1E-8E2D-51877CB6CFDB
 Content-Transfer-Encoding: 7bit
 Content-Type: text/plain;
 	charset=us-ascii

 I agree!

 christos

 > On Oct 30, 2020, at 4:35 PM, John Nemeth <jnemeth@cue.bc.ca> wrote:
 > 
 > The following reply was made to PR kern/55663; it has been noted by GNATS.
 > 
 > From: John Nemeth <jnemeth@cue.bc.ca>
 > To: matthew green <mrg@eterna.com.au>,
 >        Ruslan Nikolaev <nruslan_devel@yahoo.com>
 > Cc: gnats-bugs@netbsd.org
 > Subject: re: kern/55663 (Support for EVFILT_USER in kqueue(2))
 > Date: Fri, 30 Oct 2020 13:31:24 -0700
 > 
 > On Oct 30, 20:24, matthew green wrote:
 > }
 > } just to throw my hat in here as well -
 > }
 > } i am interested in a solution for EVFILT_USER support.
 > } there are many apps that would benefit from it.  (there
 > } are a few more events like directory updates, close,
 > } and write in freebsd i'd also like to see added.)
 > }
 > } i believe it could live in userspace, and that would be
 > } strongly preferably, but unless it's actually compatible
 > } with existing implementations (ie, they just start to
 > } work without patching beyond "use this freebsd path"),
 > } it seems not nearly as useful as it should be.
 > 
 >      We've seen cases where libc is bypassed by major things such
 > as Rust or Go (I recall one of those having problems due to using
 > syscalls directly, but I don't recall which one).  If we're going
 > to be compatible with other BSDs then I strongly believe it has to
 > be at the syscall level as much as possible and not at the API
 > level.  For this reason, I believe that it must go in the kernel.
 > 
 > }-- End of excerpt from matthew green
 > 


 --Apple-Mail=_B61E97F7-BB02-4F1E-8E2D-51877CB6CFDB
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCX5yI4wAKCRBxESqxbLM7
 OsczAKCIc70EhZktROhGGRjkqe64zqTvawCfXC4v346k4oDEDAvN4E0KCOjW6VU=
 =ro4C
 -----END PGP SIGNATURE-----

 --Apple-Mail=_B61E97F7-BB02-4F1E-8E2D-51877CB6CFDB--

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/55663 CVS commit: src
Date: Fri, 30 Oct 2020 21:08:32 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sat Oct 31 01:08:32 UTC 2020

 Modified Files:
 	src/lib/libc/sys: kqueue.2
 	src/sys/kern: kern_event.c
 	src/sys/sys: event.h
 	src/tests/lib/libc/sys: t_kevent.c

 Log Message:
 PR/55663: Ruslan Nikolaev: Add support for EVFILT_USER in kqueue(2)


 To generate a diff of this commit:
 cvs rdiff -u -r1.50 -r1.51 src/lib/libc/sys/kqueue.2
 cvs rdiff -u -r1.107 -r1.108 src/sys/kern/kern_event.c
 cvs rdiff -u -r1.38 -r1.39 src/sys/sys/event.h
 cvs rdiff -u -r1.8 -r1.9 src/tests/lib/libc/sys/t_kevent.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Ruslan Nikolaev <nruslan_devel@yahoo.com>
To: Christos Zoulas <christos@zoulas.com>, gnats-bugs@netbsd.org
Cc: jnemeth@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: kern/55663 (Support for EVFILT_USER in kqueue(2))
Date: Sat, 31 Oct 2020 23:12:40 -0400

 Thanks Christos and other contributors for taking care of this patch! I 
 assume nothing else is needed from my side.

 Btw, a bit off topic, did anyone look at kern/55664 (the next bug 
 report)? There I have a fix for the SMP version of rump kernels, and I 
 can fully explain/justify those changes. There is a race condition there 
 which we encountered while working on our project. In addition, I have 
 some other (small) fixes for rump kernels which I have not posted yet.

 On 10/30/20 5:42 PM, Christos Zoulas wrote:
 > I agree!
 >
 > christos
 >
 >> On Oct 30, 2020, at 4:35 PM, John Nemeth <jnemeth@cue.bc.ca> wrote:
 >>
 >> The following reply was made to PR kern/55663; it has been noted by GNATS.
 >>
 >> From: John Nemeth <jnemeth@cue.bc.ca>
 >> To: matthew green <mrg@eterna.com.au>,
 >>         Ruslan Nikolaev <nruslan_devel@yahoo.com>
 >> Cc: gnats-bugs@netbsd.org
 >> Subject: re: kern/55663 (Support for EVFILT_USER in kqueue(2))
 >> Date: Fri, 30 Oct 2020 13:31:24 -0700
 >>
 >> On Oct 30, 20:24, matthew green wrote:
 >> }
 >> } just to throw my hat in here as well -
 >> }
 >> } i am interested in a solution for EVFILT_USER support.
 >> } there are many apps that would benefit from it.  (there
 >> } are a few more events like directory updates, close,
 >> } and write in freebsd i'd also like to see added.)
 >> }
 >> } i believe it could live in userspace, and that would be
 >> } strongly preferably, but unless it's actually compatible
 >> } with existing implementations (ie, they just start to
 >> } work without patching beyond "use this freebsd path"),
 >> } it seems not nearly as useful as it should be.
 >>
 >>       We've seen cases where libc is bypassed by major things such
 >> as Rust or Go (I recall one of those having problems due to using
 >> syscalls directly, but I don't recall which one).  If we're going
 >> to be compatible with other BSDs then I strongly believe it has to
 >> be at the syscall level as much as possible and not at the API
 >> level.  For this reason, I believe that it must go in the kernel.
 >>
 >> }-- End of excerpt from matthew green
 >>

State-Changed-From-To: open->closed
State-Changed-By: maya@NetBSD.org
State-Changed-When: Mon, 07 Dec 2020 17:52:09 +0000
State-Changed-Why:
Sounds like nothing is left to do. Let me know if I understood incorrectly.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.46 2020/01/03 16:35:01 leot Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2020 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.