|
|
eb8b6e |
From 5f2ee8632f15a8978c522de6789777171e898671 Mon Sep 17 00:00:00 2001
|
|
|
eb8b6e |
From: Michal Sekletar <msekleta@redhat.com>
|
|
|
eb8b6e |
Date: Mon, 4 Oct 2021 19:44:06 +0200
|
|
|
eb8b6e |
Subject: [PATCH] sd-event: introduce callback invoked when event source
|
|
|
eb8b6e |
ratelimit expires
|
|
|
eb8b6e |
|
|
|
eb8b6e |
(cherry picked from commit fd69f2247520b0be3190ded96d646a415edc97b7)
|
|
|
eb8b6e |
|
|
|
eb8b6e |
Related: #2037395
|
|
|
eb8b6e |
---
|
|
|
eb8b6e |
src/libsystemd/libsystemd.sym | 5 +++
|
|
|
eb8b6e |
src/libsystemd/sd-event/sd-event.c | 61 +++++++++++++++++++++++-----
|
|
|
eb8b6e |
src/libsystemd/sd-event/test-event.c | 12 ++++++
|
|
|
eb8b6e |
src/systemd/sd-event.h | 1 +
|
|
|
eb8b6e |
4 files changed, 68 insertions(+), 11 deletions(-)
|
|
|
eb8b6e |
|
|
|
eb8b6e |
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
|
|
|
eb8b6e |
index 149d2e7b82..f4a1426248 100644
|
|
|
eb8b6e |
--- a/src/libsystemd/libsystemd.sym
|
|
|
eb8b6e |
+++ b/src/libsystemd/libsystemd.sym
|
|
|
eb8b6e |
@@ -579,3 +579,8 @@ global:
|
|
|
eb8b6e |
sd_event_source_get_ratelimit;
|
|
|
eb8b6e |
sd_event_source_is_ratelimited;
|
|
|
eb8b6e |
} LIBSYSTEMD_239;
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+LIBSYSTEMD_250 {
|
|
|
eb8b6e |
+global:
|
|
|
eb8b6e |
+ sd_event_source_set_ratelimit_expire_callback;
|
|
|
eb8b6e |
+} LIBSYSTEMD_248;
|
|
|
eb8b6e |
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
|
|
|
eb8b6e |
index 47cf93b3f4..0adfdd9e1a 100644
|
|
|
eb8b6e |
--- a/src/libsystemd/sd-event/sd-event.c
|
|
|
eb8b6e |
+++ b/src/libsystemd/sd-event/sd-event.c
|
|
|
eb8b6e |
@@ -125,6 +125,7 @@ struct sd_event_source {
|
|
|
eb8b6e |
uint64_t prepare_iteration;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
sd_event_destroy_t destroy_callback;
|
|
|
eb8b6e |
+ sd_event_handler_t ratelimit_expire_callback;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
LIST_FIELDS(sd_event_source, sources);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
@@ -2734,7 +2735,7 @@ fail:
|
|
|
eb8b6e |
return r;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
-static int event_source_leave_ratelimit(sd_event_source *s) {
|
|
|
eb8b6e |
+static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) {
|
|
|
eb8b6e |
int r;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
assert(s);
|
|
|
eb8b6e |
@@ -2766,6 +2767,23 @@ static int event_source_leave_ratelimit(sd_event_source *s) {
|
|
|
eb8b6e |
ratelimit_reset(&s->rate_limit);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ if (run_callback && s->ratelimit_expire_callback) {
|
|
|
eb8b6e |
+ s->dispatching = true;
|
|
|
eb8b6e |
+ r = s->ratelimit_expire_callback(s, s->userdata);
|
|
|
eb8b6e |
+ s->dispatching = false;
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ if (r < 0) {
|
|
|
eb8b6e |
+ log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, disabling: %m",
|
|
|
eb8b6e |
+ strna(s->description),
|
|
|
eb8b6e |
+ event_source_type_to_string(s->type));
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
|
|
|
eb8b6e |
+ }
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ return 1;
|
|
|
eb8b6e |
+ }
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
return 0;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
fail:
|
|
|
eb8b6e |
@@ -2966,6 +2984,7 @@ static int process_timer(
|
|
|
eb8b6e |
struct clock_data *d) {
|
|
|
eb8b6e |
|
|
|
eb8b6e |
sd_event_source *s;
|
|
|
eb8b6e |
+ bool callback_invoked = false;
|
|
|
eb8b6e |
int r;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
assert(e);
|
|
|
eb8b6e |
@@ -2981,9 +3000,11 @@ static int process_timer(
|
|
|
eb8b6e |
* again. */
|
|
|
eb8b6e |
assert(s->ratelimited);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- r = event_source_leave_ratelimit(s);
|
|
|
eb8b6e |
+ r = event_source_leave_ratelimit(s, /* run_callback */ true);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
return r;
|
|
|
eb8b6e |
+ else if (r == 1)
|
|
|
eb8b6e |
+ callback_invoked = true;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
continue;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
@@ -2998,7 +3019,7 @@ static int process_timer(
|
|
|
eb8b6e |
event_source_time_prioq_reshuffle(s);
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- return 0;
|
|
|
eb8b6e |
+ return callback_invoked;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
static int process_child(sd_event *e) {
|
|
|
eb8b6e |
@@ -3698,15 +3719,15 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- r = process_timer(e, e->timestamp.realtime, &e->realtime);
|
|
|
eb8b6e |
+ r = process_inotify(e);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- r = process_timer(e, e->timestamp.boottime, &e->boottime);
|
|
|
eb8b6e |
+ r = process_timer(e, e->timestamp.realtime, &e->realtime);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
|
|
|
eb8b6e |
+ r = process_timer(e, e->timestamp.boottime, &e->boottime);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
@@ -3718,16 +3739,27 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
+ r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
|
|
|
eb8b6e |
+ if (r < 0)
|
|
|
eb8b6e |
+ goto finish;
|
|
|
eb8b6e |
+ else if (r == 1) {
|
|
|
eb8b6e |
+ /* Ratelimit expiry callback was called. Let's postpone processing pending sources and
|
|
|
eb8b6e |
+ * put loop in the initial state in order to evaluate (in the next iteration) also sources
|
|
|
eb8b6e |
+ * there were potentially re-enabled by the callback.
|
|
|
eb8b6e |
+ *
|
|
|
eb8b6e |
+ * Wondering why we treat only this invocation of process_timer() differently? Once event
|
|
|
eb8b6e |
+ * source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence
|
|
|
eb8b6e |
+ * ratelimit expiry callback is never called for any other timer type. */
|
|
|
eb8b6e |
+ r = 0;
|
|
|
eb8b6e |
+ goto finish;
|
|
|
eb8b6e |
+ }
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
if (e->need_process_child) {
|
|
|
eb8b6e |
r = process_child(e);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
goto finish;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
- r = process_inotify(e);
|
|
|
eb8b6e |
- if (r < 0)
|
|
|
eb8b6e |
- goto finish;
|
|
|
eb8b6e |
-
|
|
|
eb8b6e |
if (event_next_pending(e)) {
|
|
|
eb8b6e |
e->state = SD_EVENT_PENDING;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
@@ -4054,7 +4086,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
|
|
|
eb8b6e |
|
|
|
eb8b6e |
/* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
|
|
|
eb8b6e |
* non-ratelimited. */
|
|
|
eb8b6e |
- r = event_source_leave_ratelimit(s);
|
|
|
eb8b6e |
+ r = event_source_leave_ratelimit(s, /* run_callback */ false);
|
|
|
eb8b6e |
if (r < 0)
|
|
|
eb8b6e |
return r;
|
|
|
eb8b6e |
|
|
|
eb8b6e |
@@ -4062,6 +4094,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
|
|
|
eb8b6e |
return 0;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
+_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) {
|
|
|
eb8b6e |
+ assert_return(s, -EINVAL);
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ s->ratelimit_expire_callback = callback;
|
|
|
eb8b6e |
+ return 0;
|
|
|
eb8b6e |
+}
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
|
|
|
eb8b6e |
assert_return(s, -EINVAL);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
|
|
|
eb8b6e |
index e3ee4cd5c3..9135b22839 100644
|
|
|
eb8b6e |
--- a/src/libsystemd/sd-event/test-event.c
|
|
|
eb8b6e |
+++ b/src/libsystemd/sd-event/test-event.c
|
|
|
eb8b6e |
@@ -506,6 +506,11 @@ static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userd
|
|
|
eb8b6e |
return 0;
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
+static int expired = -1;
|
|
|
eb8b6e |
+static int ratelimit_expired(sd_event_source *s, void *userdata) {
|
|
|
eb8b6e |
+ return ++expired;
|
|
|
eb8b6e |
+}
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
static void test_ratelimit(void) {
|
|
|
eb8b6e |
_cleanup_close_pair_ int p[2] = {-1, -1};
|
|
|
eb8b6e |
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
|
|
eb8b6e |
@@ -568,12 +573,19 @@ static void test_ratelimit(void) {
|
|
|
eb8b6e |
|
|
|
eb8b6e |
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
+ /* Set callback that will be invoked when we leave rate limited state. */
|
|
|
eb8b6e |
+ assert_se(sd_event_source_set_ratelimit_expire_callback(s, ratelimit_expired) >= 0);
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
do {
|
|
|
eb8b6e |
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
|
|
|
eb8b6e |
} while (!sd_event_source_is_ratelimited(s));
|
|
|
eb8b6e |
|
|
|
eb8b6e |
log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited");
|
|
|
eb8b6e |
assert_se(count == 20);
|
|
|
eb8b6e |
+
|
|
|
eb8b6e |
+ /* Dispatch the event loop once more and check that ratelimit expiration callback got called */
|
|
|
eb8b6e |
+ assert_se(sd_event_run(e, UINT64_MAX) >= 0);
|
|
|
eb8b6e |
+ assert_se(expired == 0);
|
|
|
eb8b6e |
}
|
|
|
eb8b6e |
|
|
|
eb8b6e |
int main(int argc, char *argv[]) {
|
|
|
eb8b6e |
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
|
|
|
eb8b6e |
index a17a9b3488..c2e9c9614d 100644
|
|
|
eb8b6e |
--- a/src/systemd/sd-event.h
|
|
|
eb8b6e |
+++ b/src/systemd/sd-event.h
|
|
|
eb8b6e |
@@ -147,6 +147,7 @@ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t
|
|
|
eb8b6e |
int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
|
|
|
eb8b6e |
int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
|
|
|
eb8b6e |
int sd_event_source_is_ratelimited(sd_event_source *s);
|
|
|
eb8b6e |
+int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback);
|
|
|
eb8b6e |
|
|
|
eb8b6e |
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
|
|
|
eb8b6e |
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
|