Module Name:    src
Committed By:   kre
Date:           Wed Oct  9 13:02:54 UTC 2024

Modified Files:
        src/distrib/sets/lists/debug: mi
        src/distrib/sets/lists/tests: mi
        src/tests/kernel: Makefile
Added Files:
        src/tests/kernel: t_nanosleep.c

Log Message:
Add a test for clock_nanotime() [ PR kern/58733 ]

Add a t_nanosleep test to the kernel tests, to (to a limited extent)
validate its functionality.

Initially this concentrates on the issue from PR kern/58733
where if a process paused in nanosleep() is stopped, and then
continued, and if it is using TIMER_ABSTIME, the call will
return prematurely, but indicating success.

There are (currently) 4 test cases, to test all 4 possibilities
using CLOCK_MONOTONIC and CLOCK_REALTIME (if someone wants to
add any other clocks that make sense, that should be easy) and
TIMER_ABSTIME and TIMER_RELTIME.

Currently both TIMER_ABSTIME tests fail (the TIMER_RELTIME
tests pass).   When the kernel bug is fixed, the ABSTIME
tests should be fixed along with it.

These tests are currently somewhat crude, and I'm not sure
how well they will work on a qemu test system (they work as
expected on bare metal).


To generate a diff of this commit:
cvs rdiff -u -r1.450 -r1.451 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1340 -r1.1341 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.81 -r1.82 src/tests/kernel/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_nanosleep.c

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

Modified files:

Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.450 src/distrib/sets/lists/debug/mi:1.451
--- src/distrib/sets/lists/debug/mi:1.450	Thu Oct  3 17:14:12 2024
+++ src/distrib/sets/lists/debug/mi	Wed Oct  9 13:02:53 2024
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.450 2024/10/03 17:14:12 christos Exp $
+# $NetBSD: mi,v 1.451 2024/10/09 13:02:53 kre Exp $
 #
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib					comp-sys-usr		compatdir
@@ -1810,6 +1810,7 @@
 ./usr/libdata/debug/usr/tests/kernel/t_memfd_create.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_mkdir.debug			tests-obsolete		obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_mqueue.debug			tests-kernel-tests	debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_nanosleep.debug			tests-kernel-tests	debug,atf
 ./usr/libdata/debug/usr/tests/kernel/t_open_pr_57260.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_pipe.debug			tests-obsolete		obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_poll3w.debug			tests-obsolete		obsolete,compattestfile

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1340 src/distrib/sets/lists/tests/mi:1.1341
--- src/distrib/sets/lists/tests/mi:1.1340	Thu Oct  3 17:14:12 2024
+++ src/distrib/sets/lists/tests/mi	Wed Oct  9 13:02:53 2024
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1340 2024/10/03 17:14:12 christos Exp $
+# $NetBSD: mi,v 1.1341 2024/10/09 13:02:53 kre Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2331,6 +2331,7 @@
 ./usr/tests/kernel/t_memfd_create			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_mkdir				tests-obsolete		obsolete
 ./usr/tests/kernel/t_mqueue				tests-kernel-tests	compattestfile,atf
+./usr/tests/kernel/t_nanosleep				tests-kernel-tests	atf
 ./usr/tests/kernel/t_nointerpreter			tests-kernel-tests	atf
 ./usr/tests/kernel/t_open_pr_57260			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_origin				tests-kernel-tests	compattestfile,atf

Index: src/tests/kernel/Makefile
diff -u src/tests/kernel/Makefile:1.81 src/tests/kernel/Makefile:1.82
--- src/tests/kernel/Makefile:1.81	Fri Oct  4 05:56:03 2024
+++ src/tests/kernel/Makefile	Wed Oct  9 13:02:53 2024
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.81 2024/10/04 05:56:03 msaitoh Exp $
+# $NetBSD: Makefile,v 1.82 2024/10/09 13:02:53 kre Exp $
 
 NOMAN=		# defined
 
@@ -18,6 +18,7 @@ TESTS_C+=	t_lock
 TESTS_C+=	t_lockf
 TESTS_C+=	t_memfd_create
 TESTS_C+=	t_mqueue
+TESTS_C+=	t_nanosleep
 TESTS_C+=	t_open_pr_57260
 TESTS_C+=	t_proccwd
 TESTS_C+=	t_pty

Added files:

Index: src/tests/kernel/t_nanosleep.c
diff -u /dev/null src/tests/kernel/t_nanosleep.c:1.1
--- /dev/null	Wed Oct  9 13:02:54 2024
+++ src/tests/kernel/t_nanosleep.c	Wed Oct  9 13:02:53 2024
@@ -0,0 +1,246 @@
+/* $NetBSD: t_nanosleep.c,v 1.1 2024/10/09 13:02:53 kre Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2024\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_nanosleep.c,v 1.1 2024/10/09 13:02:53 kre Exp $");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+static void
+sacrifice(void)
+{
+	pause();
+}
+
+static void
+tester(pid_t victim, clockid_t clock, int flags)
+{
+	/*
+	 * we need this sleep to be long enough that we
+	 * can accurately detect when the sleep finishes
+	 * early, but not so long that when there's no
+	 * bug and things actually sleep this long, that
+	 * the execution of a sleep this long, several
+	 * times, won't slow down the overall testing
+	 * process too much.    Trial and error...
+	 */
+	struct timespec to_sleep = { 4, 0 };
+
+	struct timespec before, after;
+	struct timespec *ts;
+	int e;
+
+	if (clock_gettime(clock, &before) != 0)
+		exit(1);
+
+	if (flags & TIMER_ABSTIME) {
+		timespecadd(&to_sleep, &before, &after);
+		ts = &after;
+	} else
+		ts = &to_sleep;
+
+	printf("Test: Clock=%d Flags=%x, starting at %jd.%.9ld\n",
+		(int)clock, flags, (intmax_t)before.tv_sec, before.tv_nsec);
+	if (flags & TIMER_ABSTIME)
+		printf("Sleeping until %jd.%.9ld\n",
+		    (intmax_t)ts->tv_sec, ts->tv_nsec);
+	else
+		printf("Sleeping for %jd.%.9ld\n",
+		    (intmax_t)ts->tv_sec, ts->tv_nsec);
+
+	/* OK, we're ready */
+
+	/* these next two steps need to be as close together as possible */
+	if (kill(victim, SIGKILL) == -1)
+		exit(2);
+	if ((e = clock_nanosleep(clock, flags, ts, &after)) != 0)
+		exit(20 + e);
+
+	if (!(flags & TIMER_ABSTIME)) {
+		printf("Remaining to sleep: %jd.%.9ld\n",
+		    (intmax_t)after.tv_sec, after.tv_nsec);
+
+		if (after.tv_sec != 0 || after.tv_nsec != 0)
+			exit(3);
+	}
+
+	if (clock_gettime(clock, &after) != 0)
+		exit(4);
+
+	printf("Sleep ended at: %jd.%.9ld\n",
+		(intmax_t)after.tv_sec, after.tv_nsec);
+
+	timespecadd(&before, &to_sleep, &before);
+	if (timespeccmp(&before, &after, >))
+		exit(5);
+
+	exit(0);
+}
+
+/*
+ * The parent of the masochist/victim above, controls everything.
+ */
+static void
+runit(clockid_t clock, int flags)
+{
+	pid_t v, m, x;
+	int status;
+	struct timespec brief = { 0, 3 * 100 * 1000 * 1000 };  /* 300 ms */
+
+	ATF_REQUIRE((v = fork()) != -1);
+	if (v == 0)
+		sacrifice();
+
+	ATF_REQUIRE((m = fork()) != -1);
+	if (m == 0)
+		tester(v, clock, flags);
+
+	ATF_REQUIRE((x = wait(&status)) != -1);
+
+	if (x == m) {
+		/*
+		 * This is bad, the murderer shouldn't die first
+		 */
+		fprintf(stderr, "M exited first, status %#x\n", status);
+		(void)kill(v, SIGKILL);	/* just in case */
+		atf_tc_fail("2nd child predeceased first");
+	}
+	if (x != v) {
+		fprintf(stderr, "Unknown exit from %d (status: %#x)"
+		    "(M=%d V=%d)\n", x, status, m, v);
+		(void)kill(m, SIGKILL);
+		(void)kill(v, SIGKILL);
+		atf_tc_fail("Strange child died");
+	}
+
+	/*
+	 * OK, the victim died, we don't really care why,
+	 * (it should have been because of a SIGKILL, maybe
+	 * test for that someday).
+	 *
+	 * Now we get to proceed to the real test.
+	 *
+	 * But we want to wait a short whle to try and be sure
+	 * that m (the child still running) has a chance to
+	 * fall asleep.
+	 */
+	(void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+
+	/*
+	 * This is the test, for PR kern/58733
+	 *   -  stop a process while in clock_nanosleep()
+	 *   -  resume it again
+	 *   -  see if it still sleeps as long as was requested (or longer)
+	 */
+	ATF_REQUIRE(kill(m, SIGSTOP) == 0);
+	(void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+	ATF_REQUIRE(kill(m, SIGCONT) == 0);
+
+	ATF_REQUIRE((x = wait(&status)) != -1);
+
+	if (x != m) {
+		fprintf(stderr, "Unknown exit from %d (status: %#x)"
+		    "(M=%d V=%d)\n", x, status, m, v);
+		(void) kill(m, SIGKILL);
+		atf_tc_fail("Strange child died");
+	}
+
+	if (status == 0)
+		atf_tc_pass();
+
+	/*
+	 * Here we should decode the status, and give a better
+	 * clue what really went wrong.   Later...
+	 */
+	fprintf(stderr, "Test failed: status from M: %#x\n", status);
+	atf_tc_fail("M exited with non-zero status.  PR kern/58733");
+}
+
+
+ATF_TC(nanosleep_monotonic_absolute);
+ATF_TC_HEAD(nanosleep_monotonic_absolute, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, ABS)");
+}
+ATF_TC_BODY(nanosleep_monotonic_absolute, tc)
+{
+	runit(CLOCK_MONOTONIC, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_monotonic_relative);
+ATF_TC_HEAD(nanosleep_monotonic_relative, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, REL)");
+}
+ATF_TC_BODY(nanosleep_monotonic_relative, tc)
+{
+	runit(CLOCK_MONOTONIC, TIMER_RELTIME);
+}
+
+ATF_TC(nanosleep_realtime_absolute);
+ATF_TC_HEAD(nanosleep_realtime_absolute, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, ABS)");
+}
+ATF_TC_BODY(nanosleep_realtime_absolute, tc)
+{
+	runit(CLOCK_REALTIME, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_realtime_relative);
+ATF_TC_HEAD(nanosleep_realtime_relative, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, REL)");
+}
+ATF_TC_BODY(nanosleep_realtime_relative, tc)
+{
+	runit(CLOCK_REALTIME, TIMER_RELTIME);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+	ATF_TP_ADD_TC(tp, nanosleep_monotonic_absolute);
+	ATF_TP_ADD_TC(tp, nanosleep_monotonic_relative);
+	ATF_TP_ADD_TC(tp, nanosleep_realtime_absolute);
+	ATF_TP_ADD_TC(tp, nanosleep_realtime_relative);
+
+	return atf_no_error();
+}

Reply via email to