Module Name: src Committed By: thorpej Date: Fri Oct 22 04:49:24 UTC 2021
Modified Files: src/sys/kern: kern_event.c src/tests/kernel/kqueue: t_timer.c Log Message: Support modifying an existing timer without having to delete it first. Semantics match FreeBSD. To generate a diff of this commit: cvs rdiff -u -r1.135 -r1.136 src/sys/kern/kern_event.c cvs rdiff -u -r1.1 -r1.2 src/tests/kernel/kqueue/t_timer.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/kern_event.c diff -u src/sys/kern/kern_event.c:1.135 src/sys/kern/kern_event.c:1.136 --- src/sys/kern/kern_event.c:1.135 Thu Oct 21 02:34:03 2021 +++ src/sys/kern/kern_event.c Fri Oct 22 04:49:24 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_event.c,v 1.135 2021/10/21 02:34:03 thorpej Exp $ */ +/* $NetBSD: kern_event.c,v 1.136 2021/10/22 04:49:24 thorpej Exp $ */ /*- * Copyright (c) 2008, 2009, 2021 The NetBSD Foundation, Inc. @@ -63,7 +63,7 @@ #endif /* _KERNEL_OPT */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.135 2021/10/21 02:34:03 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.136 2021/10/22 04:49:24 thorpej Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -102,6 +102,7 @@ static void knote_detach(struct knote *, static void knote_enqueue(struct knote *); static void knote_activate(struct knote *); static void knote_activate_locked(struct knote *); +static void knote_deactivate_locked(struct knote *); static void filt_kqdetach(struct knote *); static int filt_kqueue(struct knote *, long hint); @@ -113,6 +114,7 @@ static void filt_timerexpire(void *x); static int filt_timerattach(struct knote *); static void filt_timerdetach(struct knote *); static int filt_timer(struct knote *, long hint); +static int filt_timertouch(struct knote *, struct kevent *, long type); static int filt_userattach(struct knote *); static void filt_userdetach(struct knote *); static int filt_user(struct knote *, long hint); @@ -163,6 +165,7 @@ static const struct filterops timer_filt .f_attach = filt_timerattach, .f_detach = filt_timerdetach, .f_event = filt_timer, + .f_touch = filt_timertouch, }; static const struct filterops user_filtops = { @@ -1261,6 +1264,22 @@ filt_timerexpire(void *knx) mutex_spin_exit(&kq->kq_lock); } +static inline void +filt_timerstart(struct knote *kn, uintptr_t tticks) +{ + callout_t *calloutp = kn->kn_hook; + + KASSERT(mutex_owned(&kn->kn_kq->kq_lock)); + KASSERT(!callout_pending(calloutp)); + + if (__predict_false(tticks == FILT_TIMER_NOSCHED)) { + kn->kn_data = 1; + } else { + KASSERT(tticks <= INT_MAX); + callout_reset(calloutp, (int)tticks, filt_timerexpire, kn); + } +} + static int filt_timerattach(struct knote *kn) { @@ -1295,12 +1314,7 @@ filt_timerattach(struct knote *kn) KASSERT(kn->kn_sfflags == kev.fflags); kn->kn_hook = calloutp; - if (__predict_false(tticks == FILT_TIMER_NOSCHED)) { - kn->kn_data = 1; - } else { - KASSERT(tticks <= INT_MAX); - callout_reset(calloutp, (int)tticks, filt_timerexpire, kn); - } + filt_timerstart(kn, tticks); mutex_spin_exit(&kq->kq_lock); @@ -1332,6 +1346,61 @@ filt_timerdetach(struct knote *kn) } static int +filt_timertouch(struct knote *kn, struct kevent *kev, long type) +{ + struct kqueue *kq = kn->kn_kq; + callout_t *calloutp; + uintptr_t tticks; + int error; + + KASSERT(mutex_owned(&kq->kq_lock)); + + switch (type) { + case EVENT_REGISTER: + /* Only relevant for EV_ADD. */ + if ((kev->flags & EV_ADD) == 0) { + return 0; + } + + /* + * Stop the timer, under the assumption that if + * an application is re-configuring the timer, + * they no longer care about the old one. We + * can safely drop the kq_lock while we wait + * because fdp->fd_lock will be held throughout, + * ensuring that no one can sneak in with an + * EV_DELETE or close the kq. + */ + KASSERT(mutex_owned(&kq->kq_fdp->fd_lock)); + + calloutp = kn->kn_hook; + callout_halt(calloutp, &kq->kq_lock); + KASSERT(mutex_owned(&kq->kq_lock)); + knote_deactivate_locked(kn); + kn->kn_data = 0; + + error = filt_timercompute(kev, &tticks); + if (error) { + return error; + } + kn->kn_sdata = kev->data; + kn->kn_flags = kev->flags; + kn->kn_sfflags = kev->fflags; + filt_timerstart(kn, tticks); + break; + + case EVENT_PROCESS: + *kev = kn->kn_kevent; + break; + + default: + panic("%s: invalid type (%ld)", __func__, type); + } + + return 0; +} + +static int filt_timer(struct knote *kn, long hint) { struct kqueue *kq = kn->kn_kq; @@ -2686,6 +2755,22 @@ knote_activate(struct knote *kn) mutex_spin_exit(&kq->kq_lock); } +static void +knote_deactivate_locked(struct knote *kn) +{ + struct kqueue *kq = kn->kn_kq; + + if (kn->kn_status & KN_QUEUED) { + kq_check(kq); + kn->kn_status &= ~KN_QUEUED; + TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe); + KASSERT(KQ_COUNT(kq) > 0); + kq->kq_count--; + kq_check(kq); + } + kn->kn_status &= ~KN_ACTIVE; +} + /* * Set EV_EOF on the specified knote. Also allows additional * EV_* flags to be set (e.g. EV_ONESHOT). Index: src/tests/kernel/kqueue/t_timer.c diff -u src/tests/kernel/kqueue/t_timer.c:1.1 src/tests/kernel/kqueue/t_timer.c:1.2 --- src/tests/kernel/kqueue/t_timer.c:1.1 Wed Oct 13 04:57:19 2021 +++ src/tests/kernel/kqueue/t_timer.c Fri Oct 22 04:49:24 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: t_timer.c,v 1.1 2021/10/13 04:57:19 thorpej Exp $ */ +/* $NetBSD: t_timer.c,v 1.2 2021/10/22 04:49:24 thorpej Exp $ */ /*- * Copyright (c) 2021 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: t_timer.c,v 1.1 2021/10/13 04:57:19 thorpej Exp $"); +__RCSID("$NetBSD: t_timer.c,v 1.2 2021/10/22 04:49:24 thorpej Exp $"); #include <sys/types.h> #include <sys/event.h> @@ -170,6 +170,82 @@ ATF_TC_BODY(count_expirations, tc) event[0].data == TIME1_COUNT + 1); } +ATF_TC(modify); +ATF_TC_HEAD(modify, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests modifying a timer"); +} + +ATF_TC_BODY(modify, tc) +{ + struct kevent event[1]; + struct timespec ts = { 0, 0 }; + struct timespec sleepts; + int kq; + + ATF_REQUIRE((kq = kqueue()) >= 0); + + /* + * Start a 500ms timer, sleep for 5 seconds, and check + * the total count. + */ + EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, 500, NULL); + ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0); + + sleepts.tv_sec = 5; + sleepts.tv_nsec = 0; + ATF_REQUIRE(nanosleep(&sleepts, NULL) == 0); + + ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); + ATF_REQUIRE(event[0].ident == 1); + ATF_REQUIRE(event[0].data >= 9 && event[0].data <= 11); + + /* + * Modify to a 4 second timer, sleep for 5 seconds, and check + * the total count. + */ + EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, 4000, NULL); + ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0); + + sleepts.tv_sec = 5; + sleepts.tv_nsec = 0; + ATF_REQUIRE(nanosleep(&sleepts, NULL) == 0); + + ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); + ATF_REQUIRE(event[0].ident == 1); + ATF_REQUIRE(event[0].data == 1); + + /* + * Start a 500ms timer, sleep for 2 seconds. + */ + EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, 500, NULL); + ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0); + + sleepts.tv_sec = 2; + sleepts.tv_nsec = 0; + ATF_REQUIRE(nanosleep(&sleepts, NULL) == 0); + + /* + * Set the SAME timer, sleep for 2 seconds. + */ + EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, 500, NULL); + ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0); + + sleepts.tv_sec = 2; + sleepts.tv_nsec = 0; + ATF_REQUIRE(nanosleep(&sleepts, NULL) == 0); + + /* + * The kernel should have reset the count when modifying the + * timer, so we should only expect to see the expiration count + * for the second sleep. + */ + ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); + ATF_REQUIRE(event[0].ident == 1); + ATF_REQUIRE(event[0].data >= 3 && event[0].data <= 5); +} + ATF_TC(abstime); ATF_TC_HEAD(abstime, tc) { @@ -269,6 +345,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, count_expirations); ATF_TP_ADD_TC(tp, abstime); ATF_TP_ADD_TC(tp, timer_units); + ATF_TP_ADD_TC(tp, modify); return atf_no_error(); }