From nobody Thu Apr 9 02:37:33 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4frkcF3xY3z6YGPW for ; Thu, 09 Apr 2026 02:37:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4frkcF27zMz3VWJ for ; Thu, 09 Apr 2026 02:37:33 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1775702253; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=jlK8QtyHMG/juKPH77IquhsoCDfhky8ZGuW0HHUIcfg=; b=qAkfKvpF0/NS/5x7Or3Qy/BH9Bil4qcAVyOb8qUL67D36zA2oBtT0d/JldYrBD8ifzVlrK Wzu8s/VjByp5gw86y/BXfcAk30A/6LdKjg8MDtYNcE2PfxCOIHHbyHHnDIJgHBi5Q/+DNY r6iclJr1s7YV7SphXRk1K+sygpZY0saJuD1XGSCWenncIiD6FSOLu9+FIPl3gdtw+CP67E /K0OfYrZJNEVQyBpjyDLkC6O0XU2mDOrqVESfrnaqqsvF+syU1Go8rLH+lSBiB118HewRT 1GoE+oXe5rTol6Ffpzta9vxHPG8SaUWj436H6VEuNEsXKi11BxOtwZ8Ebv1k1Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1775702253; a=rsa-sha256; cv=none; b=QvDtctGHVybT6qv0FZtRDZF0QF/q4nToYFIAjh1SGki03goUzyQM/iwy9sUFkzKO0s4HiO rNlVGZOat9EnjrCI5GbX5n2hf3k4equTgNUSNhuzYP/d9o08OLSai8/D5KqQ4q8wC9atOo 0s5CQgJvPoHUY4iZWYoAiKLpjod9Z/mSaFaMM27hQezFZIFj5C8p3STRhK9qiCrB3uB0Bz c2AWaf5cj/OANxfhg92DJUak7NHaPMstrFab4iLpMHGGRvmKgdNLp9DshtjcjgfFYpArNl l3nZIYIxvcHsPEsQh9v6USYvDRDiKzjF6pfV4NxQ2wLEixUofTof6f/hd/K6AA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1775702253; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=jlK8QtyHMG/juKPH77IquhsoCDfhky8ZGuW0HHUIcfg=; b=mFRU8x7KkQxIabN10VOpqQ+wspVpztjCJ9gxLWdHijXTd2RwyIIADN/Ayun1y8om3bf7iv UJfevwXHVKGKmrmYuN32n7EKhMny3asBQS/iS19WVERv2hJoYV3aQUhAzkLhrLtxIBEy5+ F+Dpzns99aQMuk5ZP3cEssNsR7J64UvwoR1qYpFisRmVUo6mKjKJngZOFaVzqQs+sbYTTD +Bx6iHKR7CkYXyOEc7gG7Kd0+Ch19C3rHFOozMioB/PpbRCdjJEgRnGE6v64VKLSO86mO9 UThmU1ho0WZe0jtQIcLw4F1LyMa/Ft8bUKeki/Z4sTWL7RlGCNGrFafDMdNBKQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4frkcF1FB8ztRB for ; Thu, 09 Apr 2026 02:37:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 434e8 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 09 Apr 2026 02:37:33 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: 0c9cec8b66e7 - main - tests: kqueue: add a basic test for CPONFORK List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 0c9cec8b66e7033f50059329704515d5222b9ff4 Auto-Submitted: auto-generated Date: Thu, 09 Apr 2026 02:37:33 +0000 Message-Id: <69d710ed.434e8.7113bca8@gitrepo.freebsd.org> The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=0c9cec8b66e7033f50059329704515d5222b9ff4 commit 0c9cec8b66e7033f50059329704515d5222b9ff4 Author: Kyle Evans AuthorDate: 2026-04-09 02:37:00 +0000 Commit: Kyle Evans CommitDate: 2026-04-09 02:37:11 +0000 tests: kqueue: add a basic test for CPONFORK Just copy over a timer and a write-filter, be sure that we can observe both in the child. Maybe the timer should check for a minimum time passed, but I don't know that we'd be likely to get that wrong. This also adds a negative test with a kqueue that is *not* set for CPONFORK being added to the first one, made readable, and confirming that we don't see a knote for it in the child. Some other improvements to the test noted in the review are planned in the short term, but they're not particularly worth blocking adding this as a basic sanity check. Reviewed by: kib, markj Differential Revision: https://reviews.freebsd.org/D56223 --- tests/sys/kqueue/kqueue_fork.c | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/tests/sys/kqueue/kqueue_fork.c b/tests/sys/kqueue/kqueue_fork.c index e4c0412c1980..6f517a2e0e29 100644 --- a/tests/sys/kqueue/kqueue_fork.c +++ b/tests/sys/kqueue/kqueue_fork.c @@ -27,9 +27,13 @@ */ #include +#include +#include +#include #include #include +#include #include #include @@ -81,9 +85,145 @@ ATF_TC_BODY(shared_table_filt_sig, tc) ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); } +#define TIMER_FORKED 0 +#define TIMER_TIMEOUT 1 + +#define RECV_TIMER 0x01 +#define RECV_VNODE 0x02 +#define RECV_CLOREAD 0x04 +#define RECV_ERROR 0x80 +#define RECV_ALL (RECV_TIMER | RECV_VNODE) + +static int +cponfork_notes_check(int kq, int clofd) +{ + struct kevent ev; + int error, received = 0; + + EV_SET(&ev, TIMER_TIMEOUT, EVFILT_TIMER, + EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_SECONDS, 4, NULL); + error = kevent(kq, &ev, 1, NULL, 0, NULL); + if (error == -1) + return (RECV_ERROR); + + while ((received & RECV_ALL) != RECV_ALL) { + error = kevent(kq, NULL, 0, &ev, 1, NULL); + if (error < 0) + return (RECV_ERROR); + else if (error == 0) + break; + + switch (ev.filter) { + case EVFILT_TIMER: + if (ev.ident == TIMER_TIMEOUT) + return (received | RECV_ERROR); + + received |= RECV_TIMER; + break; + case EVFILT_VNODE: + received |= RECV_VNODE; + break; + case EVFILT_READ: + if ((int)ev.ident != clofd) + return (received | RECV_ERROR); + received |= RECV_CLOREAD; + break; + } + } + + return (received); +} + +ATF_TC_WITHOUT_HEAD(cponfork_notes); +ATF_TC_BODY(cponfork_notes, tc) +{ + struct kevent ev[3]; + int clofd, dfd, error, kq, pdfd, pmask, status; + pid_t pid; + + kq = kqueuex(KQUEUE_CPONFORK); + ATF_REQUIRE(kq >= 0); + + dfd = open(".", O_DIRECTORY); + ATF_REQUIRE(dfd >= 0); + + clofd = kqueue(); + ATF_REQUIRE(clofd >= 0); + + /* + * Setup an event on clofd that we can trigger to make it readable, + * as we'll want this ready to go when we fork to be sure that if we + * *were* going to receive an event from it, it would have occurred + * before the three-second timer that would normally close out the child + * fires. + */ + EV_SET(&ev[0], 0, EVFILT_USER, EV_ADD | EV_ENABLE, 0, 0, NULL); + error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); + ATF_REQUIRE(error != -1); + + /* + * Every event we setup here we should expect to observe in both the + * child and the parent, with exception to the EVFILT_READ of clofd. We + * except that one to be dropped in the child when the kqueue it's + * attached to goes away, thus its exclusion from the RECV_ALL mask. + */ + EV_SET(&ev[0], dfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_WRITE, 0, NULL); + EV_SET(&ev[1], TIMER_FORKED, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_SECONDS, 3, NULL); + EV_SET(&ev[2], clofd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, + 0, NULL); + error = kevent(kq, &ev[0], 3, NULL, 0, NULL); + ATF_REQUIRE(error != -1); + + /* Fire off an event to make clofd readable. */ + EV_SET(&ev[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); + + /* + * We're only using pdfork here for the kill-on-exit semantics, in case + * the parent fails to setup some context needed for one of our events + * to fire. + */ + pid = pdfork(&pdfd, 0); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + struct kinfo_file kf = { .kf_structsize = sizeof(kf) }; + + if (fcntl(kq, F_KINFO, &kf) != 0) + _exit(RECV_ERROR); + else if (kf.kf_type != KF_TYPE_KQUEUE) + _exit(RECV_ERROR); + + _exit(cponfork_notes_check(kq, clofd)); + } + + /* Setup anything we need to fire off any of our events above. */ + error = mkdir("canary", 0755); + ATF_REQUIRE(error == 0); + + /* + * We'll simultaneously do the same exercise of polling the kqueue in + * the parent, to demonstrate that forking doesn't "steal" any of the + * knotes from us -- all of the events we've added are one-shot and + * still fire twice (once in parent, once in child). + */ + pmask = cponfork_notes_check(kq, clofd); + ATF_REQUIRE_EQ(pmask, RECV_ALL | RECV_CLOREAD); + + /* Wait for the child to timeout or observe the timer. */ + _Static_assert(RECV_ALL <= UCHAR_MAX, + "Too many events to observe -- switch from waitpid -> waitid"); + error = waitpid(pid, &status, 0); + ATF_REQUIRE(error != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), RECV_ALL); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, shared_table_filt_sig); + ATF_TP_ADD_TC(tp, cponfork_notes); return (atf_no_error()); }