dryang / rpms / systemd

Forked from rpms/systemd a year ago
Clone
9adc5d
From 0c81900965a72b29eb76e0737ed899b925ee75b6 Mon Sep 17 00:00:00 2001
9adc5d
From: Anita Zhang <the.anitazha@gmail.com>
9adc5d
Date: Tue, 8 Jun 2021 00:04:35 -0700
9adc5d
Subject: [PATCH 1/2] test: add extended test for triggering mount rate limit
9adc5d
9adc5d
It's hard to trigger the failure to exit the rate limit state in
9adc5d
isolation as it needs multiple event sources in order to show that it
9adc5d
gets stuck in the queue. Hence why this is an extended test.
9adc5d
---
9adc5d
 test/TEST-60-MOUNT-RATELIMIT/Makefile |  1 +
9adc5d
 test/TEST-60-MOUNT-RATELIMIT/test.sh  |  7 +++
9adc5d
 test/units/testsuite-60.service       |  6 +++
9adc5d
 test/units/testsuite-60.sh            | 73 +++++++++++++++++++++++++++
9adc5d
 4 files changed, 87 insertions(+)
9adc5d
 create mode 120000 test/TEST-60-MOUNT-RATELIMIT/Makefile
9adc5d
 create mode 100755 test/TEST-60-MOUNT-RATELIMIT/test.sh
9adc5d
 create mode 100644 test/units/testsuite-60.service
9adc5d
 create mode 100755 test/units/testsuite-60.sh
9adc5d
9adc5d
diff --git a/test/TEST-60-MOUNT-RATELIMIT/Makefile b/test/TEST-60-MOUNT-RATELIMIT/Makefile
9adc5d
new file mode 120000
9adc5d
index 000000000000..e9f93b1104cd
9adc5d
--- /dev/null
9adc5d
+++ b/test/TEST-60-MOUNT-RATELIMIT/Makefile
9adc5d
@@ -0,0 +1 @@
9adc5d
+../TEST-01-BASIC/Makefile
9adc5d
\ No newline at end of file
9adc5d
diff --git a/test/TEST-60-MOUNT-RATELIMIT/test.sh b/test/TEST-60-MOUNT-RATELIMIT/test.sh
9adc5d
new file mode 100755
9adc5d
index 000000000000..f9eb11ccb441
9adc5d
--- /dev/null
9adc5d
+++ b/test/TEST-60-MOUNT-RATELIMIT/test.sh
9adc5d
@@ -0,0 +1,7 @@
9adc5d
+#!/usr/bin/env bash
9adc5d
+set -e
9adc5d
+TEST_DESCRIPTION="Test that mount/unmount storms can enter/exit rate limit state and will not leak units"
9adc5d
+
9adc5d
+. $TEST_BASE_DIR/test-functions
9adc5d
+
9adc5d
+do_test "$@"
9adc5d
diff --git a/test/units/testsuite-60.service b/test/units/testsuite-60.service
9adc5d
new file mode 100644
9adc5d
index 000000000000..3715c4f341c8
9adc5d
--- /dev/null
9adc5d
+++ b/test/units/testsuite-60.service
9adc5d
@@ -0,0 +1,6 @@
9adc5d
+[Unit]
9adc5d
+Description=TEST-60-MOUNT-RATELIMIT
9adc5d
+
9adc5d
+[Service]
9adc5d
+Type=oneshot
9adc5d
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
9adc5d
diff --git a/test/units/testsuite-60.sh b/test/units/testsuite-60.sh
9adc5d
new file mode 100755
9adc5d
index 000000000000..815875466762
9adc5d
--- /dev/null
9adc5d
+++ b/test/units/testsuite-60.sh
9adc5d
@@ -0,0 +1,73 @@
9adc5d
+#!/usr/bin/env bash
9adc5d
+set -eux
9adc5d
+set -o pipefail
9adc5d
+
9adc5d
+systemd-analyze log-level debug
9adc5d
+systemd-analyze log-target journal
9adc5d
+
9adc5d
+NUM_DIRS=20
9adc5d
+
9adc5d
+# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
9adc5d
+
9adc5d
+for ((i = 0; i < NUM_DIRS; i++)); do
9adc5d
+    mkdir "/tmp/meow${i}"
9adc5d
+done
9adc5d
+
9adc5d
+for ((i = 0; i < NUM_DIRS; i++)); do
9adc5d
+    mount -t tmpfs tmpfs "/tmp/meow${i}"
9adc5d
+done
9adc5d
+
9adc5d
+systemctl daemon-reload
9adc5d
+systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
9adc5d
+
9adc5d
+for ((i = 0; i < NUM_DIRS; i++)); do
9adc5d
+    umount "/tmp/meow${i}"
9adc5d
+done
9adc5d
+
9adc5d
+# figure out if we have entered the rate limit state
9adc5d
+
9adc5d
+exited_rl=0
9adc5d
+timeout="$(date -ud "2 minutes" +%s)"
9adc5d
+while [[ $(date -u +%s) -le ${timeout} ]]; do
9adc5d
+    if journalctl -u init.scope | grep -q "(mount-monitor-dispatch) entered rate limit"; then
9adc5d
+        entered_rl=1
9adc5d
+        break
9adc5d
+    fi
9adc5d
+    sleep 5
9adc5d
+done
9adc5d
+
9adc5d
+# if the infra is slow we might not enter the rate limit state; in that case skip the exit check
9adc5d
+
9adc5d
+if [ "${entered_rl}" = "1" ]; then
9adc5d
+    exited_rl=0
9adc5d
+    timeout="$(date -ud "2 minutes" +%s)"
9adc5d
+    while [[ $(date -u +%s) -le ${timeout} ]]; do
9adc5d
+        if journalctl -u init.scope | grep -q "(mount-monitor-dispatch) left rate limit"; then
9adc5d
+            exited_rl=1
9adc5d
+            break
9adc5d
+        fi
9adc5d
+        sleep 5
9adc5d
+    done
9adc5d
+
9adc5d
+    if [ "${exited_rl}" = "0" ]; then
9adc5d
+        exit 24
9adc5d
+    fi
9adc5d
+fi
9adc5d
+
9adc5d
+# give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units
9adc5d
+
9adc5d
+sleep 60
9adc5d
+systemctl daemon-reload
9adc5d
+sleep 60
9adc5d
+
9adc5d
+# verify that the mount units are always cleaned up at the end
9adc5d
+
9adc5d
+if systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; then
9adc5d
+    exit 42
9adc5d
+fi
9adc5d
+
9adc5d
+systemd-analyze log-level info
9adc5d
+
9adc5d
+echo OK >/testok
9adc5d
+
9adc5d
+exit 0
9adc5d
9adc5d
From 81107b8419c39f726fd2805517a5b9faab204e59 Mon Sep 17 00:00:00 2001
9adc5d
From: Lennart Poettering <lennart@poettering.net>
9adc5d
Date: Tue, 8 Jun 2021 00:07:51 -0700
9adc5d
Subject: [PATCH 2/2] sd-event: change ordering of pending/ratelimited events
9adc5d
9adc5d
Instead of ordering non-pending before pending we should order
9adc5d
"non-pending OR ratelimited" before "pending AND not-ratelimited".
9adc5d
This fixes a bug where ratelimited events were ordered at the end of the
9adc5d
priority queue and could be stuck there for an indeterminate amount of
9adc5d
time.
9adc5d
---
9adc5d
 src/libsystemd/sd-event/sd-event.c | 45 ++++++++++++++----------------
9adc5d
 1 file changed, 21 insertions(+), 24 deletions(-)
9adc5d
9adc5d
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
9adc5d
index 611af45293ac..489878a3e967 100644
9adc5d
--- a/src/libsystemd/sd-event/sd-event.c
9adc5d
+++ b/src/libsystemd/sd-event/sd-event.c
9adc5d
@@ -237,25 +237,6 @@ static usec_t time_event_source_next(const sd_event_source *s) {
9adc5d
         return USEC_INFINITY;
9adc5d
 }
9adc5d
 
9adc5d
-static int earliest_time_prioq_compare(const void *a, const void *b) {
9adc5d
-        const sd_event_source *x = a, *y = b;
9adc5d
-
9adc5d
-        /* Enabled ones first */
9adc5d
-        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
9adc5d
-                return -1;
9adc5d
-        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
9adc5d
-                return 1;
9adc5d
-
9adc5d
-        /* Move the pending ones to the end */
9adc5d
-        if (!x->pending && y->pending)
9adc5d
-                return -1;
9adc5d
-        if (x->pending && !y->pending)
9adc5d
-                return 1;
9adc5d
-
9adc5d
-        /* Order by time */
9adc5d
-        return CMP(time_event_source_next(x), time_event_source_next(y));
9adc5d
-}
9adc5d
-
9adc5d
 static usec_t time_event_source_latest(const sd_event_source *s) {
9adc5d
         assert(s);
9adc5d
 
9adc5d
@@ -274,7 +255,15 @@ static usec_t time_event_source_latest(const sd_event_source *s) {
9adc5d
         return USEC_INFINITY;
9adc5d
 }
9adc5d
 
9adc5d
-static int latest_time_prioq_compare(const void *a, const void *b) {
9adc5d
+static bool event_source_timer_candidate(const sd_event_source *s) {
9adc5d
+        assert(s);
9adc5d
+
9adc5d
+        /* Returns true for event sources that either are not pending yet (i.e. where it's worth to mark them pending)
9adc5d
+         * or which are currently ratelimited (i.e. where it's worth leaving the ratelimited state) */
9adc5d
+        return !s->pending || s->ratelimited;
9adc5d
+}
9adc5d
+
9adc5d
+static int time_prioq_compare(const void *a, const void *b, usec_t (*time_func)(const sd_event_source *s)) {
9adc5d
         const sd_event_source *x = a, *y = b;
9adc5d
 
9adc5d
         /* Enabled ones first */
9adc5d
@@ -283,14 +272,22 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
9adc5d
         if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
9adc5d
                 return 1;
9adc5d
 
9adc5d
-        /* Move the pending ones to the end */
9adc5d
-        if (!x->pending && y->pending)
9adc5d
+        /* Order "non-pending OR ratelimited" before "pending AND not-ratelimited" */
9adc5d
+        if (event_source_timer_candidate(x) && !event_source_timer_candidate(y))
9adc5d
                 return -1;
9adc5d
-        if (x->pending && !y->pending)
9adc5d
+        if (!event_source_timer_candidate(x) && event_source_timer_candidate(y))
9adc5d
                 return 1;
9adc5d
 
9adc5d
         /* Order by time */
9adc5d
-        return CMP(time_event_source_latest(x), time_event_source_latest(y));
9adc5d
+        return CMP(time_func(x), time_func(y));
9adc5d
+}
9adc5d
+
9adc5d
+static int earliest_time_prioq_compare(const void *a, const void *b) {
9adc5d
+        return time_prioq_compare(a, b, time_event_source_next);
9adc5d
+}
9adc5d
+
9adc5d
+static int latest_time_prioq_compare(const void *a, const void *b) {
9adc5d
+        return time_prioq_compare(a, b, time_event_source_latest);
9adc5d
 }
9adc5d
 
9adc5d
 static int exit_prioq_compare(const void *a, const void *b) {