From nobody Mon Aug 3 17:29:46 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 4hDNwD1Q1Gz6mbVW for ; Mon, 03 Aug 2026 17:29:48 +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 "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hDNwC0qCSz3lhf for ; Mon, 03 Aug 2026 17:29:47 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785778187; 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=SupZjt8PgqfcG7X7nnFqSIk0vsZBX+UnhjEEKFggCeA=; b=SqEED7dWrO5LSRK50tMbVYMw0iXzU48jZDH/nKYxWkeM4QMfCI06dtINiUy71tvYRb8VJg T1iP0c7vN7cNyHagmphi5L6WP35ojPY/WOpl7R95tIHH5FbBLFMyevIWL5BaD0pcO5O2jv eR6rHbfCNNLgKQIZ28wbYQfHgNQZ3U/yXWBcijEDCcEtSjyuwEvfnr0k9ZyExnrOiBPc8P IRlS8hs3a4pf7S0Y9Pvs24K3hACe1sN6aqZikvdJknfEKXXunQ7RaOtDo72fFopLLpin3Q 8zNG6qnht/knT5jw3FBsJRgwHbS4QG33NCidjFEyuYAE9tzwarFFyZr2DKMdvA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1785778187; a=rsa-sha256; cv=none; b=G6V2gjT1VWmHLp754YBbgHvaYa9jK0WmxJrOVAAgauCMyRlVncl8x0ibx1r+j5G1+cyLd/ N2yYptF8TeRIV9k+q6E4+tg5LcYMVmT3gk6dDO5fztAzn2DHFy8qJX1+DBGn2Qai5yfB8p d7WH2oUusqiK+0v32pu0fSyE+NR5nfhfq9iSdqoKgc+dN/HpRoa22rfwWzJlmUFGdPrNhc jfOKPa7p4v4xNTeXFZhM9dVQFAOhVbNN98xp2/PEZc1Jb9Fk6iHFuTNt3pp8o59hcEW+0K FIv7jM44DNy5Lxy0292sfLbCTkclWLVS+KLdYuPg4ea9gKPDHace3Xo2E2an1A== 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=1785778187; 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=SupZjt8PgqfcG7X7nnFqSIk0vsZBX+UnhjEEKFggCeA=; b=KiJKd0BHEWunh3SX1q6dZ/EFc5YyZI7PIg1s/cBmdEK9DpDN9AmJmLAs6iJHHLvyKghtM6 aZw9ibqXopGtXC/uoOoLvA/dKpxdmz5GA5BNs+vRyLQC3zchyNJc2uOwiQdiX+GCK+8eSP Fzx7G6+R9engT29PI8ZapX3gCtG56/BFS0IGkACghkPVmTvAspFAnlx1ow1YO8Dp2cIEmT VeqCc2H8E/wFnkpvKY5tlf0HUiiE+qO3xa0n8ayteoIhd4FOcdBw1vKWKCBNEuA8DJi799 CoISZTMA5z+rzFWBqp2ys3YDy+A0qsHdoL9HWcauCnZsMFSThqKCAaoghDUeBA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hDNwB5j24z19KG for ; Mon, 03 Aug 2026 17:29:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 46165 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 03 Aug 2026 17:29:46 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Alexander Leidinger Subject: git: d5e51b56c248 - stable/15 - hwpmc: add regression tests for detaching a live process-mode PMC 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 List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: netchild X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: d5e51b56c248e179dca588089ab349c72160d6bd Auto-Submitted: auto-generated Date: Mon, 03 Aug 2026 17:29:46 +0000 Message-Id: <6a70d00a.46165.5332cba2@gitrepo.freebsd.org> The branch stable/15 has been updated by netchild: URL: https://cgit.FreeBSD.org/src/commit/?id=d5e51b56c248e179dca588089ab349c72160d6bd commit d5e51b56c248e179dca588089ab349c72160d6bd Author: Alexander Leidinger AuthorDate: 2026-07-19 12:15:20 +0000 Commit: Alexander Leidinger CommitDate: 2026-08-03 17:29:26 +0000 hwpmc: add regression tests for detaching a live process-mode PMC Attach a process-mode counting PMC to the current process, start it, then detach and release it while it is still loaded on the hardware - the case that previously leaked the PMC's runcount reference and wedged pmc_wait_for_pmc_idle() at release. A second case does the same from a multi-threaded process so the sibling threads' references have to be drained too. The tests need an allocatable process-mode counting event and skip where none is available (hwpmc(4) not loaded, or a VM without a vPMU). Reviewed by: adrian MFC after: 2 weeks Assisted-by: Claude Code (Fable 5) Differential Revision: https://reviews.freebsd.org/D58343 (cherry picked from commit 3c3f886e4bc7619f7847ad0d0f996088ddf5915a) --- tests/sys/pmc/Makefile | 2 + tests/sys/pmc/pmc_detach_test.c | 178 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/tests/sys/pmc/Makefile b/tests/sys/pmc/Makefile index 403acd0311ce..305a3e65c8b3 100644 --- a/tests/sys/pmc/Makefile +++ b/tests/sys/pmc/Makefile @@ -1,7 +1,9 @@ TESTSDIR= ${TESTSBASE}/sys/pmc ATF_TESTS_C= pmc_wrap_test +ATF_TESTS_C+= pmc_detach_test LIBADD+= pmc +LIBADD.pmc_detach_test+= pthread .include diff --git a/tests/sys/pmc/pmc_detach_test.c b/tests/sys/pmc/pmc_detach_test.c new file mode 100644 index 000000000000..05fda61b2836 --- /dev/null +++ b/tests/sys/pmc/pmc_detach_test.c @@ -0,0 +1,178 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Alexander Leidinger + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other advertising materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Regression tests for detaching a process-mode PMC from a target that + * still has it loaded on hardware. The runcount reference for a live + * PMC is dropped only by the context-switch-out reclaim, which the + * scheduler gates on P_HWPMC; detaching used to clear P_HWPMC without + * draining the reference, so the following release spun forever in + * pmc_wait_for_pmc_idle() - a panic ("waiting too long for pmc to be + * free") on an INVARIANTS kernel, an unkillable loop holding the hwpmc + * lock otherwise. + * + * The failure is deterministic: attaching a counting PMC to the current + * (running) thread and detaching it before release always left the + * reference live. On a kernel that carries the fix these tests complete + * immediately; run against a kernel that lacks it they would wedge, so + * (like any hwpmc regression test) they ship alongside the fix. + * + * They need an allocatable process-mode counting event, and skip where + * none is available (hwpmc(4) not loaded, or a VM without a vPMU). + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +static const char *counting_events[] = { + "instructions", + "cycles", + "branches", + "unhalted-core-cycles", + "inst_retired.any", + "cpu_clk_unhalted.thread", + "ls_not_halted_cyc", + NULL +}; + +static volatile int spin_stop; + +static void * +spinner(void *arg __unused) +{ + volatile unsigned long s = 0; + + while (spin_stop == 0) + s += 1; + return (NULL); +} + +static void +burn_cpu(void) +{ + volatile unsigned long s = 0; + int i; + + for (i = 0; i < 1000000; i++) + s += i; +} + +/* Allocate the first available process-mode counting PMC, or skip. */ +static pmc_id_t +alloc_counting_pmc(void) +{ + pmc_id_t id; + int i; + + if (pmc_init() != 0) + atf_tc_skip("hwpmc(4) is not available: %s", strerror(errno)); + + for (i = 0; counting_events[i] != NULL; i++) { + id = PMC_ID_INVALID; + if (pmc_allocate(counting_events[i], PMC_MODE_TC, 0, + PMC_CPU_ANY, &id, 0) == 0) + return (id); + } + atf_tc_skip("no allocatable process-mode counting event " + "(no vPMU?)"); + return (PMC_ID_INVALID); /* not reached */ +} + +ATF_TC(detach_live_self); +ATF_TC_HEAD(detach_live_self, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Detaching a running counting PMC from the current thread and " + "then releasing it does not leak its runcount reference"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(detach_live_self, tc) +{ + pmc_id_t id; + + id = alloc_counting_pmc(); + + ATF_REQUIRE(pmc_attach(id, 0) == 0); /* 0 == current process */ + ATF_REQUIRE(pmc_start(id) == 0); + burn_cpu(); /* make the counter live */ + + /* Detach while still loaded on this CPU, then release. */ + ATF_REQUIRE(pmc_detach(id, 0) == 0); + ATF_REQUIRE(pmc_release(id) == 0); /* wedged before the fix */ +} + +ATF_TC(detach_live_multithread); +ATF_TC_HEAD(detach_live_multithread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Detaching a running process-scope counting PMC drains the " + "references held by the process' other threads"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(detach_live_multithread, tc) +{ + pthread_t th[3]; + pmc_id_t id; + int i, n; + + id = alloc_counting_pmc(); + + /* Sibling threads keep the process-scope PMC live on other CPUs. */ + spin_stop = 0; + for (n = 0; n < 3; n++) + ATF_REQUIRE(pthread_create(&th[n], NULL, spinner, NULL) == 0); + + ATF_REQUIRE(pmc_attach(id, 0) == 0); + ATF_REQUIRE(pmc_start(id) == 0); + burn_cpu(); + /* Let the siblings be scheduled and pick up the PMC. */ + usleep(200 * 1000); + + ATF_REQUIRE(pmc_detach(id, 0) == 0); + ATF_REQUIRE(pmc_release(id) == 0); /* must drain the siblings */ + + spin_stop = 1; + for (i = 0; i < n; i++) + pthread_join(th[i], NULL); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, detach_live_self); + ATF_TP_ADD_TC(tp, detach_live_multithread); + + return (atf_no_error()); +}