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(); +}