Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src Support modifying an existing timer without having to delete...
details: https://anonhg.NetBSD.org/src/rev/b7d89010cd9c
branches: trunk
changeset: 1024399:b7d89010cd9c
user: thorpej <thorpej%NetBSD.org@localhost>
date: Fri Oct 22 04:49:24 2021 +0000
description:
Support modifying an existing timer without having to delete it first.
Semantics match FreeBSD.
diffstat:
sys/kern/kern_event.c | 101 ++++++++++++++++++++++++++++++++++++++---
tests/kernel/kqueue/t_timer.c | 81 ++++++++++++++++++++++++++++++++-
2 files changed, 172 insertions(+), 10 deletions(-)
diffs (273 lines):
diff -r 44a3328ade2b -r b7d89010cd9c sys/kern/kern_event.c
--- a/sys/kern/kern_event.c Fri Oct 22 02:57:23 2021 +0000
+++ b/sys/kern/kern_event.c Fri Oct 22 04:49:24 2021 +0000
@@ -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_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 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 @@
.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 @@
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 @@
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 @@
}
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 @@
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).
diff -r 44a3328ade2b -r b7d89010cd9c tests/kernel/kqueue/t_timer.c
--- a/tests/kernel/kqueue/t_timer.c Fri Oct 22 02:57:23 2021 +0000
+++ b/tests/kernel/kqueue/t_timer.c Fri Oct 22 04:49:24 2021 +0000
@@ -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 @@
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_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();
}
Home |
Main Index |
Thread Index |
Old Index