Module Name: src Committed By: riastradh Date: Sun Dec 22 23:25:15 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_time_arith.c Log Message: t_time_arith: New test for timer calculations. This will facilitate fixing various problems in the arithmetic to determine, given an itimer's scheduled time (it_value) and periodic interval (it_interval) along with the current time of actual expiry (now), what time the itimer should be rescheduled for (next, new value of it_timer) on the same clock. Later we can also put tests for other functions like tvtohz here, and make them test many different values of hz/tick. XXX Not 100% sure about all of these cases -- there is some room for reasonable discussion about what the right answer is. But we have unquestionably implemented the wrong thing for many of these cases, even if the answers this test checks for may not be quite right yet. PR kern/58922: itimer(9): arithmetic overflow PR kern/58925: itimer(9) responds erratically to clock wound back PR kern/58926: itimer(9) integer overflow in overrun counting PR kern/58927: itimer(9): overrun accounting is broken To generate a diff of this commit: cvs rdiff -u -r1.456 -r1.457 src/distrib/sets/lists/debug/mi cvs rdiff -u -r1.1351 -r1.1352 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.83 -r1.84 src/tests/kernel/Makefile cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_time_arith.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.456 src/distrib/sets/lists/debug/mi:1.457 --- src/distrib/sets/lists/debug/mi:1.456 Tue Dec 17 17:56:45 2024 +++ src/distrib/sets/lists/debug/mi Sun Dec 22 23:25:14 2024 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.456 2024/12/17 17:56:45 christos Exp $ +# $NetBSD: mi,v 1.457 2024/12/22 23:25:14 riastradh Exp $ # ./etc/mtree/set.debug comp-sys-root ./usr/lib comp-sys-usr compatdir @@ -1838,6 +1838,7 @@ ./usr/libdata/debug/usr/tests/kernel/t_sysctl.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/kernel/t_sysv.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/kernel/t_time.debug tests-obsolete obsolete,compattestfile +./usr/libdata/debug/usr/tests/kernel/t_time_arith.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/kernel/t_timeleft.debug tests-kernel-tests debug,atf,compattestfile ./usr/libdata/debug/usr/tests/kernel/t_ucontext.debug tests-obsolete obsolete,compattestfile ./usr/libdata/debug/usr/tests/kernel/t_unmount.debug tests-kernel-tests debug,atf,rump Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.1351 src/distrib/sets/lists/tests/mi:1.1352 --- src/distrib/sets/lists/tests/mi:1.1351 Wed Dec 18 02:47:00 2024 +++ src/distrib/sets/lists/tests/mi Sun Dec 22 23:25:14 2024 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1351 2024/12/18 02:47:00 riastradh Exp $ +# $NetBSD: mi,v 1.1352 2024/12/22 23:25:14 riastradh Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2362,6 +2362,7 @@ ./usr/tests/kernel/t_sysctl tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_sysv tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_time tests-obsolete obsolete +./usr/tests/kernel/t_time_arith tests-obsolete obsolete ./usr/tests/kernel/t_timeleft tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_trapsignal tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_ucontext tests-obsolete obsolete Index: src/tests/kernel/Makefile diff -u src/tests/kernel/Makefile:1.83 src/tests/kernel/Makefile:1.84 --- src/tests/kernel/Makefile:1.83 Sun Nov 10 15:57:32 2024 +++ src/tests/kernel/Makefile Sun Dec 22 23:25:15 2024 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.83 2024/11/10 15:57:32 riastradh Exp $ +# $NetBSD: Makefile,v 1.84 2024/12/22 23:25:15 riastradh Exp $ NOMAN= # defined @@ -102,6 +102,12 @@ SRCS.t_extent= t_extent.c subr_extent.c CPPFLAGS.t_extent.c= -D_EXTENT_TESTING -D__POOL_EXPOSE -D_KERNTYPES CPPFLAGS.subr_extent.c= -D_EXTENT_TESTING -D__POOL_EXPOSE -D_KERNTYPES +TESTS_C+= t_time_arith +SRCS.t_time_arith= t_time_arith.c subr_time_arith.c +CPPFLAGS.t_time_arith.c+= -I${NETBSDSRCDIR}/sys -D_TIME_TESTING +CPPFLAGS.subr_time_arith.c+= -I${NETBSDSRCDIR}/sys -D_TIME_TESTING +LDADD.t_time_arith+= -lutil + t_subr_prf.c: gen_t_subr_prf ${NETBSDSRCDIR}/sys/kern/subr_prf.c ${HOST_SH} ${.ALLSRC} ${.TARGET} .if ${MKSANITIZER:Uno} == "yes" Added files: Index: src/tests/kernel/t_time_arith.c diff -u /dev/null src/tests/kernel/t_time_arith.c:1.1 --- /dev/null Sun Dec 22 23:25:15 2024 +++ src/tests/kernel/t_time_arith.c Sun Dec 22 23:25:15 2024 @@ -0,0 +1,413 @@ +/* $NetBSD: t_time_arith.c,v 1.1 2024/12/22 23:25:15 riastradh 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> +__RCSID("$NetBSD: t_time_arith.c,v 1.1 2024/12/22 23:25:15 riastradh Exp $"); + +#include <sys/timearith.h> + +#include <atf-c.h> +#include <errno.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <util.h> + +#include "h_macros.h" + +enum { HZ = 100 }; + +int hz = HZ; +int tick = 1000000/HZ; + +static sig_atomic_t jmp_en; +static int jmp_sig; +static jmp_buf jmp; + +static void +handle_signal(int signo) +{ + const int errno_save = errno; + char buf[32]; + + snprintf_ss(buf, sizeof(buf), "signal %d\n", signo); + (void)write(STDERR_FILENO, buf, strlen(buf)); + + errno = errno_save; + + if (jmp_en) { + jmp_sig = signo; + jmp_en = 0; + longjmp(jmp, 1); + } else { + raise_default_signal(signo); + } +} + +const struct itimer_transition { + struct itimerspec it_time; + struct timespec it_now; + struct timespec it_next; + int it_overruns; + const char *it_xfail; +} itimer_transitions[] = { + /* + * Fired more than one interval early -- treat clock as wound + * backwards, not counting overruns. Advance by somewhere + * between one and two intervals from now. + */ + [0] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {0,1}, {2,0}, 0, + /* 1.709551617 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [1] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {0,500000000}, {2,0}, 0, + /* 1.709551615 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [2] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {0,999999999}, {2,0}, 0, + /* 2.709551613 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [3] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {1,0}, {2,0}, 0, + /* 2.709551615 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [4] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {1,1}, {3,0}, 0, + /* 2.709551617 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [5] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {1,500000000}, {3,0}, 0, + /* 2.709551615 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [6] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {1,999999999}, {3,0}, 0, + /* 3.709551613 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + + /* + * Fired exactly one interval early. Treat this too as clock + * wound backwards. + */ + [7] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {2,0}, {3,0}, 0, + /* 3.709551615 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + + /* + * Fired less than one interval early -- callouts and real-time + * clock might not be perfectly synced, counted as zero + * overruns. Advance by one interval from the scheduled time. + */ + [8] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {2,1}, {4,0}, 0, + /* 3.000000001 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [9] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {2,500000000}, {4,0}, 0, + /* 3.999999999 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + [10] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {2,999999999}, {4,0}, 0, + /* 4.999999997 */ + "PR kern/58925: itimer(9) responds erratically to clock wound back"}, + + /* + * Fired exactly on time. Advance by one interval. + */ + [11] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {3,0}, {4,0}, 0, NULL}, + + /* + * Fired late by less than one interval -- callouts and + * real-time clock might not be prefectly synced, counted as + * zero overruns. Advance by one interval from the scheduled + * time (even if it's very close to a full interval). + */ + [12] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {3,1}, {4,0}, 0, NULL}, + [14] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {3,500000000}, {4,0}, 0, NULL}, + [15] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {3,999999999}, {4,0}, 0, NULL}, + + /* + * Fired late by exactly one interval -- treat it as overrun. + * + * XXX ...or treat it as not overrun? wat + */ + [16] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {4,0}, {4,0}, 0, NULL}, + + /* + * Fired late by more than one interval but less than two -- + * overrun. + */ + [17] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {4,1}, {5,0}, 1, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + [18] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {4,500000000}, {5,0}, 1, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + [19] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {4,999999999}, {5,0}, 1, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + + /* + * Fired late by exactly two intervals -- two overruns. + */ + [20] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {5,0}, {6,0}, 2, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + + /* + * Fired late by more intervals plus slop, up to 32. + * + * XXX Define DELAYTIMER_MAX so we can write it in terms of + * that. + */ + [21] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {13,123456789}, {14,0}, 10, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + [22] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {34,999999999}, {32,0}, 32, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + + /* + * Fired late by roughly INT_MAX intervals. + */ + [23] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {(time_t)3 + INT_MAX - 1, 0}, + {(time_t)3 + INT_MAX, 0}, + INT_MAX, + /* 4.000000000, overruns=0 */ + "PR kern/58927: itimer(9): overrun accounting is broken"}, + [24] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {(time_t)3 + INT_MAX, 0}, + {(time_t)3 + INT_MAX + 1, 0}, + INT_MAX, + /* 4.000000000, overruns=0 */ + "PR kern/58926: itimer(9) integer overflow in overrun counting"}, + [25] = {{.it_value = {3,0}, .it_interval = {1,0}}, + {(time_t)3 + INT_MAX + 1, 0}, + {(time_t)3 + INT_MAX + 2, 0}, + INT_MAX, + /* 4.000000000, overruns=0 */ + "PR kern/58926: itimer(9) integer overflow in overrun counting"}, + + /* (2^63 - 1) ns */ + [26] = {{.it_value = {3,0}, .it_interval = {9223372036,854775807}}, + {3,1}, {9223372039,854775807}, 0, NULL}, + /* 2^63 ns */ + [27] = {{.it_value = {3,0}, .it_interval = {9223372036,854775808}}, + {3,1}, {9223372039,854775808}, 0, NULL}, + /* (2^63 + 1) ns */ + [28] = {{.it_value = {3,0}, .it_interval = {9223372036,854775809}}, + {3,1}, {9223372039,854775809}, 0, NULL}, + + /* + * Overflows -- we should (XXX but currently don't) reject + * intervals of at least 2^64 nanoseconds up front, since this + * is more time than it is reasonable to wait (more than 584 + * years). + */ + + /* (2^64 - 1) ns */ + [29] = {{.it_value = {3,0}, .it_interval = {18446744073,709551615}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* 2^64 ns */ + [30] = {{.it_value = {3,0}, .it_interval = {18446744073,709551616}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* (2^64 + 1) ns */ + [31] = {{.it_value = {3,0}, .it_interval = {18446744073,709551617}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + + /* (2^63 - 1) us */ + [32] = {{.it_value = {3,0}, .it_interval = {9223372036854,775807}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* 2^63 us */ + [33] = {{.it_value = {3,0}, .it_interval = {9223372036854,775808}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* (2^63 + 1) us */ + [34] = {{.it_value = {3,0}, .it_interval = {9223372036854,775809}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + + /* (2^64 - 1) us */ + [35] = {{.it_value = {3,0}, .it_interval = {18446744073709,551615}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* 2^64 us */ + [36] = {{.it_value = {3,0}, .it_interval = {18446744073709,551616}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* (2^64 + 1) us */ + [37] = {{.it_value = {3,0}, .it_interval = {18446744073709,551617}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + + /* (2^63 - 1) ms */ + [38] = {{.it_value = {3,0}, .it_interval = {9223372036854775,807}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* 2^63 ms */ + [39] = {{.it_value = {3,0}, .it_interval = {9223372036854775,808}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* (2^63 + 1) ms */ + [40] = {{.it_value = {3,0}, .it_interval = {9223372036854775,809}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + + /* (2^64 - 1) ms */ + [41] = {{.it_value = {3,0}, .it_interval = {18446744073709551,615}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* 2^64 ms */ + [42] = {{.it_value = {3,0}, .it_interval = {18446744073709551,616}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + /* (2^64 + 1) ms */ + [43] = {{.it_value = {3,0}, .it_interval = {18446744073709551,617}}, + {2,999999999}, {0,0}, 0, + "PR kern/58922: itimer(9): arithmetic overflow"}, + + /* invalid intervals */ + [44] = {{.it_value = {3,0}, .it_interval = {-1,0}}, + {3,1}, {0,0}, 0, NULL}, + [45] = {{.it_value = {3,0}, .it_interval = {0,-1}}, + {3,1}, {0,0}, 0, NULL}, + [46] = {{.it_value = {3,0}, .it_interval = {0,1000000000}}, + {3,1}, {0,0}, 0, NULL}, +}; + +ATF_TC(itimer_transitions); +ATF_TC_HEAD(itimer_transitions, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Tests interval timer transitions"); +} +ATF_TC_BODY(itimer_transitions, tc) +{ + volatile unsigned i; + + REQUIRE_LIBC(signal(SIGFPE, handle_signal), SIG_ERR); + REQUIRE_LIBC(signal(SIGABRT, handle_signal), SIG_ERR); + + for (i = 0; i < __arraycount(itimer_transitions); i++) { + struct itimer_transition it = itimer_transitions[i]; + struct timespec next; + int overruns; + volatile bool aborted = true; + volatile bool expect_abort = false; + + fprintf(stderr, "case %u\n", i); + + if (it.it_xfail) + atf_tc_expect_fail("%s", it.it_xfail); + + if (itimespecfix(&it.it_time.it_value) != 0 || + itimespecfix(&it.it_time.it_interval) != 0) { + fprintf(stderr, "rejected by itimerspecfix\n"); + expect_abort = true; + } + + if (setjmp(jmp) == 0) { + jmp_en = 1; + itimer_transition(&it.it_time, &it.it_now, + &next, &overruns); + jmp_en = 0; + aborted = false; + } + ATF_CHECK(!jmp_en); + jmp_en = 0; /* paranoia */ + if (expect_abort) { + fprintf(stderr, "expected abort\n"); + ATF_CHECK_MSG(aborted, + "[%u] missing invariant assertion", i); + ATF_CHECK_MSG(jmp_sig == SIGABRT, + "[%u] missing invariant assertion", i); + } else { + ATF_CHECK_MSG(!aborted, "[%u] raised signal %d: %s", i, + jmp_sig, strsignal(jmp_sig)); + } + + ATF_CHECK_MSG((next.tv_sec == it.it_next.tv_sec && + next.tv_nsec == it.it_next.tv_nsec), + "[%u] periodic intervals of %lld.%09d from %lld.%09d" + " last expired at %lld.%09d:" + " next expiry at %lld.%09d, expected %lld.%09d", i, + (long long)it.it_time.it_interval.tv_sec, + (int)it.it_time.it_interval.tv_nsec, + (long long)it.it_time.it_value.tv_sec, + (int)it.it_time.it_value.tv_nsec, + (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec, + (long long)next.tv_sec, (int)next.tv_nsec, + (long long)it.it_next.tv_sec, (int)it.it_next.tv_nsec); + ATF_CHECK_EQ_MSG(overruns, it.it_overruns, + "[%u] periodic intervals of %lld.%09d from %lld.%09d" + " last expired at %lld.%09d:" + " overruns %d, expected %d", i, + (long long)it.it_time.it_interval.tv_sec, + (int)it.it_time.it_interval.tv_nsec, + (long long)it.it_time.it_value.tv_sec, + (int)it.it_time.it_value.tv_nsec, + (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec, + overruns, it.it_overruns); + + if (it.it_xfail) + atf_tc_expect_pass(); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, itimer_transitions); + + return atf_no_error(); +} +