The branch main has been updated by kevans:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9c999a259f00b35f0467acd351fea9157ed7e1e4

commit 9c999a259f00b35f0467acd351fea9157ed7e1e4
Author:     Kyle Evans <kev...@freebsd.org>
AuthorDate: 2021-09-29 19:55:59 +0000
Commit:     Kyle Evans <kev...@freebsd.org>
CommitDate: 2021-10-01 02:31:24 +0000

    kqueue: don't arbitrarily restrict long-past values for NOTE_ABSTIME
    
    NOTE_ABSTIME values are converted to values relative to boottime in
    filt_timervalidate(), and negative values are currently rejected.  We
    don't reject times in the past in general, so clamp this up to 0 as
    needed such that the timer fires immediately rather than imposing what
    looks like an arbitrary restriction.
    
    Another possible scenario is that the system clock had to be adjusted
    by ~minutes or ~hours and we have less than that in terms of uptime,
    making a reasonable short-timeout suddenly invalid. Firing it is still
    a valid choice in this scenario so that applications can at least
    expect a consistent behavior.
    
    Reviewed by:    kib, markj
    Discussed with: allanjude
    Differential Revision:  https://reviews.freebsd.org/D32230
---
 sys/kern/kern_event.c              | 11 +++--
 tests/sys/kqueue/libkqueue/timer.c | 84 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 92 insertions(+), 3 deletions(-)

diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 5fa5bf9cad06..3cd7753d4f6d 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -798,13 +798,13 @@ filt_timervalidate(struct knote *kn, sbintime_t *to)
                return (EINVAL);
 
        *to = timer2sbintime(kn->kn_sdata, kn->kn_sfflags);
+       if (*to < 0)
+               return (EINVAL);
        if ((kn->kn_sfflags & NOTE_ABSTIME) != 0) {
                getboottimebin(&bt);
                sbt = bttosbt(bt);
-               *to -= sbt;
+               *to = MAX(0, *to - sbt);
        }
-       if (*to < 0)
-               return (EINVAL);
        return (0);
 }
 
@@ -815,9 +815,14 @@ filt_timerattach(struct knote *kn)
        sbintime_t to;
        int error;
 
+       to = -1;
        error = filt_timervalidate(kn, &to);
        if (error != 0)
                return (error);
+       KASSERT((kn->kn_flags & EV_ONESHOT) != 0 || to > 0,
+           ("%s: periodic timer has a calculated zero timeout", __func__));
+       KASSERT(to >= 0,
+           ("%s: timer has a calculated negative timeout", __func__));
 
        if (atomic_fetchadd_int(&kq_ncallouts, 1) + 1 > kq_calloutmax) {
                atomic_subtract_int(&kq_ncallouts, 1);
diff --git a/tests/sys/kqueue/libkqueue/timer.c 
b/tests/sys/kqueue/libkqueue/timer.c
index cb22887be276..76dfc99e11f0 100644
--- a/tests/sys/kqueue/libkqueue/timer.c
+++ b/tests/sys/kqueue/libkqueue/timer.c
@@ -247,6 +247,88 @@ test_abstime(void)
     success();
 }
 
+static void
+test_abstime_preboot(void)
+{
+    const char *test_id = "kevent(EVFILT_TIMER (PREBOOT), EV_ONESHOT, 
NOTE_ABSTIME)";
+    struct kevent kev;
+    struct timespec btp;
+    uint64_t end, start, stop;
+
+    test_begin(test_id);
+
+    test_no_kevents();
+
+    /*
+     * We'll expire it at just before system boot (roughly) with the hope that
+     * we'll get an ~immediate expiration, just as we do for any value 
specified
+     * between system boot and now.
+     */
+    start = now();
+    if (clock_gettime(CLOCK_BOOTTIME, &btp) != 0)
+      err(1, "%s", test_id);
+
+    end = start - SEC_TO_US(btp.tv_sec + 1);
+    EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+      NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
+    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+        err(1, "%s", test_id);
+
+    /* Retrieve the event */
+    kev.flags = EV_ADD | EV_ONESHOT;
+    kev.data = 1;
+    kev.fflags = 0;
+    kevent_cmp(&kev, kevent_get(kqfd));
+
+    stop = now();
+    if (stop < end)
+        err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
+    /* Check if the event occurs again */
+    sleep(3);
+    test_no_kevents();
+
+    success();
+}
+
+static void
+test_abstime_postboot(void)
+{
+    const char *test_id = "kevent(EVFILT_TIMER (POSTBOOT), EV_ONESHOT, 
NOTE_ABSTIME)";
+    struct kevent kev;
+    uint64_t end, start, stop;
+    const int timeout_sec = 1;
+
+    test_begin(test_id);
+
+    test_no_kevents();
+
+    /*
+     * Set a timer for 1 second ago, it should fire immediately rather than
+     * being rejected.
+     */
+    start = now();
+    end = start - SEC_TO_US(timeout_sec);
+    EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+      NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
+    if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+        err(1, "%s", test_id);
+
+    /* Retrieve the event */
+    kev.flags = EV_ADD | EV_ONESHOT;
+    kev.data = 1;
+    kev.fflags = 0;
+    kevent_cmp(&kev, kevent_get(kqfd));
+
+    stop = now();
+    if (stop < end)
+        err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
+    /* Check if the event occurs again */
+    sleep(3);
+    test_no_kevents();
+
+    success();
+}
+
 static void
 test_update(void)
 {
@@ -517,6 +599,8 @@ test_evfilt_timer(void)
     test_oneshot();
     test_periodic();
     test_abstime();
+    test_abstime_preboot();
+    test_abstime_postboot();
     test_update();
     test_update_equal();
     test_update_expired();
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to