From nobody Mon Apr 13 04:01:58 2026 X-Original-To: dev-commits-src-branches@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 4fvDHq33x0z6YCq3 for ; Mon, 13 Apr 2026 04:01:59 +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 4fvDHq0229z3wWN for ; Mon, 13 Apr 2026 04:01:59 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1776052919; 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=rkZOt02LNflDve90R3D+S97JFdwMED5I4QXt90rfx0E=; b=eYX4ndyN1q2A2gKAZe+uWAJHYNEKku78O806WeccOKdZbFsibt7NqTU+UagM3u8Dfa4kkM ZaW/EM/2rFT0jm3OUWSx6rWJFiLwcaUc5Rc6a2xqLTwUhgCzoTx3CvhlODSNwUrEF5jmnp SKIFA79DDqy7veT8ftpdn2ae6o+UW3Q6M7QINphz6Iw10QMD9vQGvhV/xDvpOQcaw0KCoH BDX9JaUK89szTkZbC5ChmJcIow68RhWSftwYtvyIh0tpYdtQCGEX7yfu65TghAGy94keMz Er0Ip9IM98aEGEmXLM8Ej78ry4NIC7mkhrU3H6C9u7gBYfHF6giaw0naNlNfSg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1776052919; a=rsa-sha256; cv=none; b=ujtCYk/ulgbCyMe4OCMOYk/6yZDsa4f6nTNPl/7viITtf9pCjL7twrBIntSXwZmvVo0JgC 1lm+NidTpXV+1VxUX8sho7qHM1oHgFguQHrXEHhMnVIpsNafWgd/D68QcDCP6P0s/v6aNR 5uiCHuObwUs5Y1mKK0q1cOEK+KOLv1cuGiTUIS+KvHYRNYUOUSYh4uMZt6aQ0nMGSaFjdh ql7kDb1x3zPRxGrIz1jOBI6t3Lvu8+BGQaJR12fSDqvZWaQ3Kw4/++FoQbkCZ6EMO3dyRS D+X3QtCjvLj7ohT+1Nb16BE+lb9XfAPvT918A1KytMQ6K1Q5Tq5y0QFi4r0PdA== 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=1776052919; 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=rkZOt02LNflDve90R3D+S97JFdwMED5I4QXt90rfx0E=; b=rjwLTH6Om/BYBwevdLNjeEqQP3cMAGjsP67dbtQ9pzOLrNou2vVTGqvO9r4/btK+jEr7jN GV2n3htdK1rcEgRpecLgNnQcFg8q0Snn8el7lqwZVaOTdZap6OW+e43/8BoHN6jXMaiKbD f0r5RQm167cSciivmuZmDag5tXtIB9j+zl7NnyuMLYc6aqDqb0SJQaP52Y2zr7b7cZSSXk YErmolaZ+u4UlGWNYEw7HHT7RZa+UxZW+XNUYfBHEoP7uzUXz0evSml9glZoCt8TfvKZHt uKUGwTyybQlDwY7M6OCzFQgIjQh3Ns0bNX5LDKZBMLsuSLXtAdb6zor/QBuzaQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fvDHp6MSQznXK for ; Mon, 13 Apr 2026 04:01:58 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 19dfb by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 13 Apr 2026 04:01:58 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kyle Evans Subject: git: 84561bc27cb4 - stable/15 - tests: kqueue: add a basic test for CPONFORK List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@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/stable/15 X-Git-Reftype: branch X-Git-Commit: 84561bc27cb4830a9d4fa27f6ce32c8042f53185 Auto-Submitted: auto-generated Date: Mon, 13 Apr 2026 04:01:58 +0000 Message-Id: <69dc6ab6.19dfb.56963c4@gitrepo.freebsd.org> The branch stable/15 has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=84561bc27cb4830a9d4fa27f6ce32c8042f53185 commit 84561bc27cb4830a9d4fa27f6ce32c8042f53185 Author: Kyle Evans AuthorDate: 2026-04-09 02:37:00 +0000 Commit: Kyle Evans CommitDate: 2026-04-13 03:12:55 +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 (cherry picked from commit 0c9cec8b66e7033f50059329704515d5222b9ff4) --- 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()); }