diff --git a/backport-core-Add-trigger-limit-for-path-units.patch b/backport-core-Add-trigger-limit-for-path-units.patch new file mode 100644 index 0000000..cb5588c --- /dev/null +++ b/backport-core-Add-trigger-limit-for-path-units.patch @@ -0,0 +1,128 @@ +From aaae822b37aa3ca39aebb516fdc6bef36d730c25 Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Fri, 17 Dec 2021 20:01:31 +0100 +Subject: [PATCH] core: Add trigger limit for path units + +When conditions fail on a service unit, a path unit can cause +PID 1 to busy loop as it keeps trying to activate the service unit. +To avoid this from happening, add a trigger limit to the path unit, +identical to the trigger limit we have for socket units. + +Initially, let's start with a high limit and not make it configurable. +If needed, we can add properties to configure the rate limit similar +to the ones we have for socket units. + +Conflict:code context adaptation +Reference:https://github.com/systemd/systemd-stable/commit/aaae822b37aa3ca39aebb516fdc6bef36d730c25 +--- + src/core/path.c | 10 ++++++++++ + src/core/path.h | 3 +++ + test/testsuite-63.units/test63.service | 2 +- + test/units/testsuite-63.service | 21 +++++++++++++++++---- + 4 files changed, 31 insertions(+), 5 deletions(-) + +diff --git a/src/core/path.c b/src/core/path.c +index 1b0f115dd8..f89e35a001 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -265,6 +265,9 @@ static void path_init(Unit *u) { + assert(u->load_state == UNIT_STUB); + + p->directory_mode = 0755; ++ ++ p->trigger_limit.interval = 2 * USEC_PER_SEC; ++ p->trigger_limit.burst = 200; + } + + void path_free_specs(Path *p) { +@@ -494,6 +497,12 @@ static void path_enter_running(Path *p) { + if (unit_stop_pending(UNIT(p))) + return; + ++ if (!ratelimit_below(&p->trigger_limit)) { ++ log_unit_warning(UNIT(p), "Trigger limit hit, refusing further activation."); ++ path_enter_dead(p, PATH_FAILURE_TRIGGER_LIMIT_HIT); ++ return; ++ } ++ + trigger = UNIT_TRIGGER(UNIT(p)); + if (!trigger) { + log_unit_error(UNIT(p), "Unit to trigger vanished."); +@@ -836,6 +845,7 @@ static const char* const path_result_table[_PATH_RESULT_MAX] = { + [PATH_FAILURE_RESOURCES] = "resources", + [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit", ++ [PATH_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", + }; + + DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); +diff --git a/src/core/path.h b/src/core/path.h +index 66ae857dc4..d835c24166 100644 +--- a/src/core/path.h ++++ b/src/core/path.h +@@ -46,6 +46,7 @@ typedef enum PathResult { + PATH_FAILURE_RESOURCES, + PATH_FAILURE_START_LIMIT_HIT, + PATH_FAILURE_UNIT_START_LIMIT_HIT, ++ PATH_FAILURE_TRIGGER_LIMIT_HIT, + _PATH_RESULT_MAX, + _PATH_RESULT_INVALID = -EINVAL, + } PathResult; +@@ -61,6 +62,8 @@ struct Path { + mode_t directory_mode; + + PathResult result; ++ ++ RateLimit trigger_limit; + }; + + void path_free_specs(Path *p); +diff --git a/test/testsuite-63.units/test63.service b/test/testsuite-63.units/test63.service +index c838018..6292434 100644 +--- a/test/testsuite-63.units/test63.service ++++ b/test/testsuite-63.units/test63.service +@@ -1,5 +1,5 @@ + [Unit] +-ConditionPathExists=!/tmp/nonexistent ++ConditionPathExists=/tmp/nonexistent + + [Service] + ExecStart=true +diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service +index 0a8d143be9..40422127ff 100644 +--- a/test/units/testsuite-63.service ++++ b/test/units/testsuite-63.service +@@ -5,13 +5,26 @@ Description=TEST-63-ISSUE-17433 + [Service] + ExecStartPre=rm -f /failed /testok + Type=oneshot ++ ++# Test that a path unit continuously triggering a service that fails condition checks eventually fails with ++# the trigger-limit-hit error. + ExecStart=rm -f /tmp/nonexistent + ExecStart=systemctl start test63.path + ExecStart=touch /tmp/test63 +-# Make sure systemd has sufficient time to hit the start limit for test63.service. ++# Make sure systemd has sufficient time to hit the trigger limit for test63.path. + ExecStart=sleep 2 +-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = failed' +-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = start-limit-hit' ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive' ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success' + ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed' +-ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-start-limit-hit' ++ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = trigger-limit-hit' ++ ++# Test that starting the service manually doesn't affect the path unit. ++ExecStart=rm -f /tmp/test63 ++ExecStart=systemctl reset-failed ++ExecStart=systemctl start test63.path ++ExecStart=systemctl start test63.service ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive' ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success' ++ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = active' ++ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = success' + ExecStart=sh -x -c 'echo OK >/testok' +-- +2.33.0 + diff --git a/backport-core-Check-unit-start-rate-limiting-earlier.patch b/backport-core-Check-unit-start-rate-limiting-earlier.patch new file mode 100644 index 0000000..f1fc862 --- /dev/null +++ b/backport-core-Check-unit-start-rate-limiting-earlier.patch @@ -0,0 +1,493 @@ +From 9727f2427ff6b2e1f4ab927cc57ad8e888f04e95 Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Tue, 24 Aug 2021 16:46:47 +0100 +Subject: [PATCH] core: Check unit start rate limiting earlier + +Fixes #17433. Currently, if any of the validations we do before we +check start rate limiting fail, we can still enter a busy loop as +no rate limiting gets applied. A common occurence of this scenario +is path units triggering a service that fails a condition check. + +To fix the issue, we simply move up start rate limiting checks to +be the first thing we do when starting a unit. To achieve this, +we add a new method to the unit vtable and implement it for the +relevant unit types so that we can do the start rate limit checks +earlier on. + +Conflict:code context adaptation +Reference:https://github.com/systemd/systemd-stable/commit/9727f2427ff6b2e1f4ab927cc57ad8e888f04e95 +--- + src/core/automount.c | 23 +++++++++++++++++------ + src/core/mount.c | 23 +++++++++++++++++------ + src/core/path.c | 23 +++++++++++++++++------ + src/core/service.c | 25 ++++++++++++++++++------- + src/core/socket.c | 23 +++++++++++++++++------ + src/core/swap.c | 23 +++++++++++++++++------ + src/core/timer.c | 23 +++++++++++++++++------ + src/core/unit.c | 7 +++++++ + src/core/unit.h | 4 ++++ + test/TEST-63-ISSUE-17433/Makefile | 1 + + test/TEST-63-ISSUE-17433/test.sh | 9 +++++++++ + test/meson.build | 2 ++ + test/testsuite-10.units/test10.service | 3 +++ + test/testsuite-63.units/test63.path | 2 ++ + test/testsuite-63.units/test63.service | 5 +++++ + test/units/testsuite-63.service | 16 ++++++++++++++++ + 16 files changed, 169 insertions(+), 43 deletions(-) + create mode 120000 test/TEST-63-ISSUE-17433/Makefile + create mode 100755 test/TEST-63-ISSUE-17433/test.sh + create mode 100644 test/testsuite-63.units/test63.path + create mode 100644 test/testsuite-63.units/test63.service + create mode 100644 test/units/testsuite-63.service + +diff --git a/src/core/automount.c b/src/core/automount.c +index 30226b9bde..11eb352b9b 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -813,12 +813,6 @@ static int automount_start(Unit *u) { + if (r < 0) + return r; + +- r = unit_test_start_limit(u); +- if (r < 0) { +- automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -1064,6 +1058,21 @@ static bool automount_supported(void) { + return supported; + } + ++static int automount_test_start_limit(Unit *u) { ++ Automount *a = AUTOMOUNT(u); ++ int r; ++ ++ assert(a); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { + [AUTOMOUNT_SUCCESS] = "success", + [AUTOMOUNT_FAILURE_RESOURCES] = "resources", +@@ -1126,4 +1135,6 @@ const UnitVTable automount_vtable = { + [JOB_FAILED] = "Failed to unset automount %s.", + }, + }, ++ ++ .test_start_limit = automount_test_start_limit, + }; +diff --git a/src/core/mount.c b/src/core/mount.c +index fb8f72e257..35b56426d4 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1167,12 +1167,6 @@ static int mount_start(Unit *u) { + + assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED)); + +- r = unit_test_start_limit(u); +- if (r < 0) { +- mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -2138,6 +2132,21 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&m->exec_context, ret); + } + ++static int mount_test_start_limit(Unit *u) { ++ Mount *m = MOUNT(u); ++ int r; ++ ++ assert(m); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { + [MOUNT_EXEC_MOUNT] = "ExecMount", + [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", +@@ -2235,4 +2244,6 @@ const UnitVTable mount_vtable = { + [JOB_TIMEOUT] = "Timed out unmounting %s.", + }, + }, ++ ++ .test_start_limit = mount_test_start_limit, + }; +diff --git a/src/core/path.c b/src/core/path.c +index 800524a308..693636b0ee 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -590,12 +590,6 @@ static int path_start(Unit *u) { + if (r < 0) + return r; + +- r = unit_test_start_limit(u); +- if (r < 0) { +- path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -812,6 +806,21 @@ static void path_reset_failed(Unit *u) { + p->result = PATH_SUCCESS; + } + ++static int path_test_start_limit(Unit *u) { ++ Path *p = PATH(u); ++ int r; ++ ++ assert(p); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const path_type_table[_PATH_TYPE_MAX] = { + [PATH_EXISTS] = "PathExists", + [PATH_EXISTS_GLOB] = "PathExistsGlob", +@@ -866,4 +875,6 @@ const UnitVTable path_vtable = { + .reset_failed = path_reset_failed, + + .bus_set_property = bus_path_set_property, ++ ++ .test_start_limit = path_test_start_limit, + }; +diff --git a/src/core/service.c b/src/core/service.c +index c55304d170..9d8eef1f74 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2436,13 +2436,6 @@ static int service_start(Unit *u) { + + assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); + +- /* Make sure we don't enter a busy loop of some kind. */ +- r = unit_test_start_limit(u); +- if (r < 0) { +- service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -4445,6 +4438,22 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) { + return NULL; + } + ++static int service_test_start_limit(Unit *u) { ++ Service *s = SERVICE(u); ++ int r; ++ ++ assert(s); ++ ++ /* Make sure we don't enter a busy loop of some kind. */ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { + [SERVICE_RESTART_NO] = "no", + [SERVICE_RESTART_ON_SUCCESS] = "on-success", +@@ -4608,4 +4617,6 @@ const UnitVTable service_vtable = { + }, + .finished_job = service_finished_job, + }, ++ ++ .test_start_limit = service_test_start_limit, + }; +diff --git a/src/core/socket.c b/src/core/socket.c +index ceaf39bdd3..177068eed4 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2513,12 +2513,6 @@ static int socket_start(Unit *u) { + + assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED)); + +- r = unit_test_start_limit(u); +- if (r < 0) { +- socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -3425,6 +3419,21 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + ++static int socket_test_start_limit(Unit *u) { ++ Socket *s = SOCKET(u); ++ int r; ++ ++ assert(s); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { + [SOCKET_EXEC_START_PRE] = "ExecStartPre", + [SOCKET_EXEC_START_CHOWN] = "ExecStartChown", +@@ -3551,4 +3560,6 @@ const UnitVTable socket_vtable = { + [JOB_TIMEOUT] = "Timed out stopping %s.", + }, + }, ++ ++ .test_start_limit = socket_test_start_limit, + }; +diff --git a/src/core/swap.c b/src/core/swap.c +index 48ba5c7664..29c63118ac 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -932,12 +932,6 @@ static int swap_start(Unit *u) { + if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) + return -EAGAIN; + +- r = unit_test_start_limit(u); +- if (r < 0) { +- swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -1588,6 +1582,21 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + ++static int swap_test_start_limit(Unit *u) { ++ Swap *s = SWAP(u); ++ int r; ++ ++ assert(s); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { + [SWAP_EXEC_ACTIVATE] = "ExecActivate", + [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", +@@ -1683,4 +1692,6 @@ const UnitVTable swap_vtable = { + [JOB_TIMEOUT] = "Timed out deactivating swap %s.", + }, + }, ++ ++ .test_start_limit = swap_test_start_limit, + }; +diff --git a/src/core/timer.c b/src/core/timer.c +index 12515a6a75..8853121c00 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -627,12 +627,6 @@ static int timer_start(Unit *u) { + if (r < 0) + return r; + +- r = unit_test_start_limit(u); +- if (r < 0) { +- timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); +- return r; +- } +- + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; +@@ -890,6 +884,21 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) { + return 0; + } + ++static int timer_test_start_limit(Unit *u) { ++ Timer *t = TIMER(u); ++ int r; ++ ++ assert(t); ++ ++ r = unit_test_start_limit(u); ++ if (r < 0) { ++ timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); ++ return r; ++ } ++ ++ return 0; ++} ++ + static const char* const timer_base_table[_TIMER_BASE_MAX] = { + [TIMER_ACTIVE] = "OnActiveSec", + [TIMER_BOOT] = "OnBootSec", +@@ -949,4 +958,6 @@ const UnitVTable timer_vtable = { + .timezone_change = timer_timezone_change, + + .bus_set_property = bus_timer_set_property, ++ ++ .test_start_limit = timer_test_start_limit, + }; +diff --git a/src/core/unit.c b/src/core/unit.c +index 7d72dfa864..48e7b95e56 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1857,6 +1857,13 @@ int unit_start(Unit *u) { + + assert(u); + ++ /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */ ++ if (UNIT_VTABLE(u)->test_start_limit) { ++ int r = UNIT_VTABLE(u)->test_start_limit(u); ++ if (r < 0) ++ return r; ++ } ++ + /* If this is already started, then this will succeed. Note that this will even succeed if this unit + * is not startable by the user. This is relied on to detect when we need to wait for units and when + * waiting is finished. */ +diff --git a/src/core/unit.h b/src/core/unit.h +index b3e9c2106f..b689f29f8f 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -658,6 +658,10 @@ typedef struct UnitVTable { + * of this type will immediately fail. */ + bool (*supported)(void); + ++ /* If this function is set, it's invoked first as part of starting a unit to allow start rate ++ * limiting checks to occur before we do anything else. */ ++ int (*test_start_limit)(Unit *u); ++ + /* The strings to print in status messages */ + UnitStatusMessageFormats status_message_formats; + +diff --git a/test/TEST-63-ISSUE-17433/Makefile b/test/TEST-63-ISSUE-17433/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-63-ISSUE-17433/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-63-ISSUE-17433/test.sh b/test/TEST-63-ISSUE-17433/test.sh +new file mode 100755 +index 0000000000..c595a9f2de +--- /dev/null ++++ b/test/TEST-63-ISSUE-17433/test.sh +@@ -0,0 +1,9 @@ ++#!/usr/bin/env bash ++set -e ++ ++TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/17433" ++ ++# shellcheck source=test/test-functions ++. "${TEST_BASE_DIR:?}/test-functions" ++ ++do_test "$@" +diff --git a/test/meson.build b/test/meson.build +index a21230a4a8..b8335fb50f 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -33,6 +33,8 @@ if install_tests + install_dir : testdata_dir) + install_subdir('testsuite-52.units', + install_dir : testdata_dir) ++ install_subdir('testsuite-63.units', ++ install_dir : testdata_dir) + + testsuite08_dir = testdata_dir + '/testsuite-08.units' + install_data('testsuite-08.units/-.mount', +diff --git a/test/testsuite-10.units/test10.service b/test/testsuite-10.units/test10.service +index d0be786b01..2fb476b986 100644 +--- a/test/testsuite-10.units/test10.service ++++ b/test/testsuite-10.units/test10.service +@@ -1,6 +1,9 @@ + [Unit] + Requires=test10.socket + ConditionPathExistsGlob=/tmp/nonexistent ++# Make sure we hit the socket trigger limit in the test and not the service start limit. ++StartLimitInterval=1000 ++StartLimitBurst=1000 + + [Service] + ExecStart=true +diff --git a/test/testsuite-63.units/test63.path b/test/testsuite-63.units/test63.path +new file mode 100644 +index 0000000000..a6573bda0a +--- /dev/null ++++ b/test/testsuite-63.units/test63.path +@@ -0,0 +1,2 @@ ++[Path] ++PathExists=/tmp/test63 +diff --git a/test/testsuite-63.units/test63.service b/test/testsuite-63.units/test63.service +new file mode 100644 +index 0000000000..c83801874d +--- /dev/null ++++ b/test/testsuite-63.units/test63.service +@@ -0,0 +1,5 @@ ++[Unit] ++ConditionPathExists=!/tmp/nonexistent ++ ++[Service] ++ExecStart=true +diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service +new file mode 100644 +index 0000000000..04122723d4 +--- /dev/null ++++ b/test/units/testsuite-63.service +@@ -0,0 +1,16 @@ ++[Unit] ++Description=TEST-63-ISSUE-17433 ++ ++[Service] ++ExecStartPre=rm -f /failed /testok ++Type=oneshot ++ExecStart=rm -f /tmp/nonexistent ++ExecStart=systemctl start test63.path ++ExecStart=touch /tmp/test63 ++# Make sure systemd has sufficient time to hit the start limit for test63.service. ++ExecStart=sleep 2 ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = failed' ++ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = start-limit-hit' ++ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed' ++ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-start-limit-hit' ++ExecStart=sh -x -c 'echo OK >/testok' +-- +2.33.0 + diff --git a/backport-core-Delay-start-rate-limit-check-when-starting-a-un.patch b/backport-core-Delay-start-rate-limit-check-when-starting-a-un.patch new file mode 100644 index 0000000..9de1c3f --- /dev/null +++ b/backport-core-Delay-start-rate-limit-check-when-starting-a-un.patch @@ -0,0 +1,51 @@ +From ce2146f5256659c7fb53a7d5b9dc551252e27e7e Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Tue, 19 Oct 2021 10:45:48 +0100 +Subject: [PATCH] core: Delay start rate limit check when starting a unit + +Doing start rate limit checks before doing condition checks made +condition check failures count towards the start rate limit which +broke existing assumptions (see #21025). Run the rate limit checks +after the condition checks again to restore the previous behaviour. + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/ce2146f5256659c7fb53a7d5b9dc551252e27e7e +--- + src/core/unit.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index a2944c1917..a6403002a6 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1855,13 +1855,6 @@ int unit_start(Unit *u) { + + assert(u); + +- /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */ +- if (UNIT_VTABLE(u)->test_start_limit) { +- r = UNIT_VTABLE(u)->test_start_limit(u); +- if (r < 0) +- return r; +- } +- + /* If this is already started, then this will succeed. Note that this will even succeed if this unit + * is not startable by the user. This is relied on to detect when we need to wait for units and when + * waiting is finished. */ +@@ -1911,6 +1904,13 @@ int unit_start(Unit *u) { + return unit_start(following); + } + ++ /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */ ++ if (UNIT_VTABLE(u)->test_start_limit) { ++ r = UNIT_VTABLE(u)->test_start_limit(u); ++ if (r < 0) ++ return r; ++ } ++ + /* If it is stopped, but we cannot start it, then fail */ + if (!UNIT_VTABLE(u)->start) + return -EBADR; +-- +2.33.0 + diff --git a/backport-core-Move-r-variable-declaration-to-start-of-unit_st.patch b/backport-core-Move-r-variable-declaration-to-start-of-unit_st.patch new file mode 100644 index 0000000..e0599b3 --- /dev/null +++ b/backport-core-Move-r-variable-declaration-to-start-of-unit_st.patch @@ -0,0 +1,33 @@ +From 5f37c1a955e399756c4137d22f7f0f45a619f425 Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Sat, 30 Oct 2021 22:12:06 +0100 +Subject: [PATCH] core: Move 'r' variable declaration to start of unit_start() + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/5f37c1a955e399756c4137d22f7f0f45a619f425 +--- + src/core/unit.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 4c55827a65..a2944c1917 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1851,12 +1851,13 @@ static bool unit_verify_deps(Unit *u) { + int unit_start(Unit *u) { + UnitActiveState state; + Unit *following; ++ int r; + + assert(u); + + /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */ + if (UNIT_VTABLE(u)->test_start_limit) { +- int r = UNIT_VTABLE(u)->test_start_limit(u); ++ r = UNIT_VTABLE(u)->test_start_limit(u); + if (r < 0) + return r; + } +-- +2.33.0 + diff --git a/backport-core-rename-generalize-UNIT-u-test_start_limit-hook.patch b/backport-core-rename-generalize-UNIT-u-test_start_limit-hook.patch new file mode 100644 index 0000000..5a4198b --- /dev/null +++ b/backport-core-rename-generalize-UNIT-u-test_start_limit-hook.patch @@ -0,0 +1,265 @@ +From 705578c3b9d794097233aa66010cf67b2a444716 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 4 Oct 2021 17:51:52 +0200 +Subject: [PATCH] core: rename/generalize UNIT(u)->test_start_limit() hook + +Up until now the main reason why we didn't proceed with starting the +unit was exceed start limit burst. However, for unit types like mounts +the other reason could be effective ratelimit on /proc/self/mountinfo +event source. That means our mount unit state may not reflect current +kernel state. Hence, we need to attempt to re-run the start job again +after ratelimit on event source expires. + +As we will be introducing another reason than start limit let's rename +the virtual function that implements the check. + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/705578c3b9d794097233aa66010cf67b2a444716 +--- + src/core/automount.c | 6 +++--- + src/core/mount.c | 6 +++--- + src/core/path.c | 6 +++--- + src/core/service.c | 6 +++--- + src/core/socket.c | 6 +++--- + src/core/swap.c | 6 +++--- + src/core/timer.c | 6 +++--- + src/core/unit.c | 6 +++--- + src/core/unit.h | 2 +- + 9 files changed, 25 insertions(+), 25 deletions(-) + +diff --git a/src/core/automount.c b/src/core/automount.c +index de470935c7..1fc3fc0f82 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -1063,7 +1063,7 @@ static bool automount_supported(void) { + return supported; + } + +-static int automount_test_start_limit(Unit *u) { ++static int automount_can_start(Unit *u) { + Automount *a = AUTOMOUNT(u); + int r; + +@@ -1075,7 +1075,7 @@ static int automount_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { +@@ -1142,5 +1142,5 @@ const UnitVTable automount_vtable = { + }, + }, + +- .test_start_limit = automount_test_start_limit, ++ .can_start = automount_can_start, + }; +diff --git a/src/core/mount.c b/src/core/mount.c +index 321c7986b3..2ebae752b6 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -2135,7 +2135,7 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&m->exec_context, ret); + } + +-static int mount_test_start_limit(Unit *u) { ++static int mount_can_start(Unit *u) { + Mount *m = MOUNT(u); + int r; + +@@ -2147,7 +2147,7 @@ static int mount_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { +@@ -2248,5 +2248,5 @@ const UnitVTable mount_vtable = { + }, + }, + +- .test_start_limit = mount_test_start_limit, ++ .can_start = mount_can_start, + }; +diff --git a/src/core/path.c b/src/core/path.c +index 0a3d86e9db..cdab9dcf8c 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -811,7 +811,7 @@ static void path_reset_failed(Unit *u) { + p->result = PATH_SUCCESS; + } + +-static int path_test_start_limit(Unit *u) { ++static int path_can_start(Unit *u) { + Path *p = PATH(u); + int r; + +@@ -823,7 +823,7 @@ static int path_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const path_type_table[_PATH_TYPE_MAX] = { +@@ -882,5 +882,5 @@ const UnitVTable path_vtable = { + + .bus_set_property = bus_path_set_property, + +- .test_start_limit = path_test_start_limit, ++ .can_start = path_can_start, + }; +diff --git a/src/core/service.c b/src/core/service.c +index 83cbc9f489..17c19a2c4a 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -4482,7 +4482,7 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) { + return NULL; + } + +-static int service_test_start_limit(Unit *u) { ++static int service_can_start(Unit *u) { + Service *s = SERVICE(u); + int r; + +@@ -4495,7 +4495,7 @@ static int service_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { +@@ -4669,5 +4669,5 @@ const UnitVTable service_vtable = { + .finished_job = service_finished_job, + }, + +- .test_start_limit = service_test_start_limit, ++ .can_start = service_can_start, + }; +diff --git a/src/core/socket.c b/src/core/socket.c +index 6534311bef..f265aab594 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -3427,7 +3427,7 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + +-static int socket_test_start_limit(Unit *u) { ++static int socket_can_start(Unit *u) { + Socket *s = SOCKET(u); + int r; + +@@ -3439,7 +3439,7 @@ static int socket_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { +@@ -3570,5 +3570,5 @@ const UnitVTable socket_vtable = { + }, + }, + +- .test_start_limit = socket_test_start_limit, ++ .can_start = socket_can_start, + }; +diff --git a/src/core/swap.c b/src/core/swap.c +index de72ac9232..3b28235194 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -1581,7 +1581,7 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + +-static int swap_test_start_limit(Unit *u) { ++static int swap_can_start(Unit *u) { + Swap *s = SWAP(u); + int r; + +@@ -1593,7 +1593,7 @@ static int swap_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { +@@ -1692,5 +1692,5 @@ const UnitVTable swap_vtable = { + }, + }, + +- .test_start_limit = swap_test_start_limit, ++ .can_start = swap_can_start, + }; +diff --git a/src/core/timer.c b/src/core/timer.c +index 240a2f473b..b22168fad5 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -889,7 +889,7 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) { + return 0; + } + +-static int timer_test_start_limit(Unit *u) { ++static int timer_can_start(Unit *u) { + Timer *t = TIMER(u); + int r; + +@@ -901,7 +901,7 @@ static int timer_test_start_limit(Unit *u) { + return r; + } + +- return 0; ++ return 1; + } + + static const char* const timer_base_table[_TIMER_BASE_MAX] = { +@@ -965,5 +965,5 @@ const UnitVTable timer_vtable = { + + .bus_set_property = bus_timer_set_property, + +- .test_start_limit = timer_test_start_limit, ++ .can_start = timer_can_start, + }; +diff --git a/src/core/unit.c b/src/core/unit.c +index 77d4ceaf24..929cc85e13 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1904,9 +1904,9 @@ int unit_start(Unit *u) { + return unit_start(following); + } + +- /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */ +- if (UNIT_VTABLE(u)->test_start_limit) { +- r = UNIT_VTABLE(u)->test_start_limit(u); ++ /* Check our ability to start early so that failure conditions don't cause us to enter a busy loop. */ ++ if (UNIT_VTABLE(u)->can_start) { ++ r = UNIT_VTABLE(u)->can_start(u); + if (r < 0) + return r; + } +diff --git a/src/core/unit.h b/src/core/unit.h +index 3f3a75d33b..76701519c2 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -665,7 +665,7 @@ typedef struct UnitVTable { + + /* If this function is set, it's invoked first as part of starting a unit to allow start rate + * limiting checks to occur before we do anything else. */ +- int (*test_start_limit)(Unit *u); ++ int (*can_start)(Unit *u); + + /* The strings to print in status messages */ + UnitStatusMessageFormats status_message_formats; +-- +2.33.0 + diff --git a/backport-mount-make-mount-units-start-jobs-not-runnable-if-p-.patch b/backport-mount-make-mount-units-start-jobs-not-runnable-if-p-.patch new file mode 100644 index 0000000..a7da1ca --- /dev/null +++ b/backport-mount-make-mount-units-start-jobs-not-runnable-if-p-.patch @@ -0,0 +1,29 @@ +From a7c93dfe91e88a5a561341c523a45c7f8d71a588 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 4 Oct 2021 19:41:34 +0200 +Subject: [PATCH] mount: make mount units start jobs not runnable if + /p/s/mountinfo ratelimit is in effect + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/a7c93dfe91e88a5a561341c523a45c7f8d71a588 +--- + src/core/mount.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 2ebae752b6..88a670dc2a 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -2141,6 +2141,9 @@ static int mount_can_start(Unit *u) { + + assert(m); + ++ if (sd_event_source_is_ratelimited(u->manager->mount_event_source)) ++ return -EAGAIN; ++ + r = unit_test_start_limit(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); +-- +2.33.0 + diff --git a/backport-mount-retrigger-run-queue-after-ratelimit-expired-to.patch b/backport-mount-retrigger-run-queue-after-ratelimit-expired-to.patch new file mode 100644 index 0000000..d66d7f6 --- /dev/null +++ b/backport-mount-retrigger-run-queue-after-ratelimit-expired-to.patch @@ -0,0 +1,56 @@ +From edc027b4f1cfaa49e8ecdde763eb8c623402d464 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 4 Oct 2021 20:31:49 +0200 +Subject: [PATCH] mount: retrigger run queue after ratelimit expired to run + delayed mount start jobs + +Fixes #20329 + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/edc027b4f1cfaa49e8ecdde763eb8c623402d464 +--- + src/core/mount.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 88a670dc2a..3463641c6c 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1838,6 +1838,21 @@ static bool mount_is_mounted(Mount *m) { + return UNIT(m)->perpetual || FLAGS_SET(m->proc_flags, MOUNT_PROC_IS_MOUNTED); + } + ++static int mount_on_ratelimit_expire(sd_event_source *s, void *userdata) { ++ Manager *m = userdata; ++ int r; ++ ++ assert(m); ++ ++ /* By entering ratelimited state we made all mount start jobs not runnable, now rate limit is over so let's ++ * make sure we dispatch them in the next iteration. */ ++ r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_ONESHOT); ++ if (r < 0) ++ log_debug_errno(r, "Failed to enable run queue event source, ignoring: %m"); ++ ++ return 0; ++} ++ + static void mount_enumerate(Manager *m) { + int r; + +@@ -1891,6 +1906,12 @@ static void mount_enumerate(Manager *m) { + goto fail; + } + ++ r = sd_event_source_set_ratelimit_expire_callback(m->mount_event_source, mount_on_ratelimit_expire); ++ if (r < 0) { ++ log_error_errno(r, "Failed to enable rate limit for mount events: %m"); ++ goto fail; ++ } ++ + (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); + } + +-- +2.33.0 + diff --git a/backport-pid1-add-a-manager_trigger_run_queue-helper.patch b/backport-pid1-add-a-manager_trigger_run_queue-helper.patch new file mode 100644 index 0000000..8a3f239 --- /dev/null +++ b/backport-pid1-add-a-manager_trigger_run_queue-helper.patch @@ -0,0 +1,106 @@ +From b0c4b2824693fe6a27ea9439ec7a6328a0e23704 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 12 Nov 2021 09:43:07 +0100 +Subject: [PATCH] pid1: add a manager_trigger_run_queue() helper + +We have two different places where we re-trigger the run queue now. +let's unify it under a common function, that is part of the Manager +code. + +Follow-up for #20953 + +Conflict:code context adaptation +Reference:https://github.com/systemd/systemd-stable/commit/b0c4b2824693fe6a27ea9439ec7a6328a0e23704 +--- + src/core/job.c | 8 ++------ + src/core/manager.c | 12 ++++++++++++ + src/core/manager.h | 2 ++ + src/core/mount.c | 9 +++------ + 4 files changed, 19 insertions(+), 12 deletions(-) + +diff --git a/src/core/job.c b/src/core/job.c +index 6dd01a6f49..b7ac75ac71 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -1107,17 +1107,13 @@ void job_add_to_run_queue(Job *j) { + if (j->in_run_queue) + return; + +- if (prioq_isempty(j->manager->run_queue)) { +- r = sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT); +- if (r < 0) +- log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m"); +- } +- + r = prioq_put(j->manager->run_queue, j, &j->run_queue_idx); + if (r < 0) + log_warning_errno(r, "Failed put job in run queue, ignoring: %m"); + else + j->in_run_queue = true; ++ ++ manager_trigger_run_queue(j->manager); + } + + void job_add_to_dbus_queue(Job *j) { +diff --git a/src/core/manager.c b/src/core/manager.c +index 61895ab092..b21747daea 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -2248,6 +2248,18 @@ static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { + return 1; + } + ++void manager_trigger_run_queue(Manager *m) { ++ int r; ++ ++ assert(m); ++ ++ r = sd_event_source_set_enabled( ++ m->run_queue_event_source, ++ prioq_isempty(m->run_queue) ? SD_EVENT_OFF : SD_EVENT_ONESHOT); ++ if (r < 0) ++ log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m"); ++} ++ + static unsigned manager_dispatch_dbus_queue(Manager *m) { + unsigned n = 0, budget; + Unit *u; +diff --git a/src/core/manager.h b/src/core/manager.h +index 29ce812121..c9230cb5c4 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -507,6 +507,8 @@ int manager_get_effective_environment(Manager *m, char ***ret); + + int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit); + ++void manager_trigger_run_queue(Manager *m); ++ + int manager_loop(Manager *m); + + int manager_open_serialization(Manager *m, FILE **_f); +diff --git a/src/core/mount.c b/src/core/mount.c +index 3463641c6c..4f76b552c2 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1840,15 +1840,12 @@ static bool mount_is_mounted(Mount *m) { + + static int mount_on_ratelimit_expire(sd_event_source *s, void *userdata) { + Manager *m = userdata; +- int r; + + assert(m); + +- /* By entering ratelimited state we made all mount start jobs not runnable, now rate limit is over so let's +- * make sure we dispatch them in the next iteration. */ +- r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_ONESHOT); +- if (r < 0) +- log_debug_errno(r, "Failed to enable run queue event source, ignoring: %m"); ++ /* By entering ratelimited state we made all mount start jobs not runnable, now rate limit is over so ++ * let's make sure we dispatch them in the next iteration. */ ++ manager_trigger_run_queue(m); + + return 0; + } +-- +2.33.0 + diff --git a/backport-sd-event-introduce-callback-invoked-when-event-sourc.patch b/backport-sd-event-introduce-callback-invoked-when-event-sourc.patch new file mode 100644 index 0000000..027eb5b --- /dev/null +++ b/backport-sd-event-introduce-callback-invoked-when-event-sourc.patch @@ -0,0 +1,282 @@ +From fd69f2247520b0be3190ded96d646a415edc97b7 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 4 Oct 2021 19:44:06 +0200 +Subject: [PATCH] sd-event: introduce callback invoked when event source + ratelimit expires + +Conflict:code context adaptation +Reference:https://github.com/systemd/systemd-stable/commit/fd69f2247520b0be3190ded96d646a415edc97b7 +--- + man/sd_event_source_set_ratelimit.xml | 22 +++++++--- + src/libsystemd/libsystemd.sym | 1 + + src/libsystemd/sd-event/event-source.h | 1 + + src/libsystemd/sd-event/sd-event.c | 61 ++++++++++++++++++++++---- + src/libsystemd/sd-event/test-event.c | 12 +++++ + src/systemd/sd-event.h | 1 + + 6 files changed, 85 insertions(+), 13 deletions(-) + +diff --git a/man/sd_event_source_set_ratelimit.xml b/man/sd_event_source_set_ratelimit.xml +index ac8529074a..37354a09f6 100644 +--- a/man/sd_event_source_set_ratelimit.xml ++++ b/man/sd_event_source_set_ratelimit.xml +@@ -19,6 +19,7 @@ + sd_event_source_set_ratelimit + sd_event_source_get_ratelimit + sd_event_source_is_ratelimited ++ sd_event_source_set_ratelimit_expire_callback + + Configure rate limiting on event sources + +@@ -46,6 +47,12 @@ + sd_event_source *source + + ++ ++ int sd_event_source_set_ratelimit_expire_callback ++ sd_event_source *source ++ sd_event_handler_tcallback ++ ++ + + + +@@ -78,6 +85,10 @@ + is currently affected by rate limiting, i.e. it has recently hit the rate limit and is currently + temporarily disabled due to that. + ++ sd_event_source_set_ratelimit_expire_callback may be used to set a callback ++ function that is invoked every time the event source leaves rate limited state. Note that function is ++ called in the same event loop iteration in which state transition occured. ++ + Rate limiting is currently implemented for I/O, timer, signal, defer and inotify event + sources. + +@@ -85,11 +96,12 @@ + + Return Value + +- On success, sd_event_source_set_ratelimit() and +- sd_event_source_get_ratelimit() return a non-negative integer. On failure, they +- return a negative errno-style error code. sd_event_source_is_ratelimited returns +- zero if rate limiting is currently not in effect and greater than zero if it is in effect; it returns a +- negative errno-style error code on failure. ++ On success, sd_event_source_set_ratelimit(), ++ sd_event_source_set_ratelimit_expire_callback and ++ sd_event_source_get_ratelimit() return a non-negative integer. On failure, they return ++ a negative errno-style error code. sd_event_source_is_ratelimited returns zero if rate ++ limiting is currently not in effect and greater than zero if it is in effect; it returns a negative ++ errno-style error code on failure. + + + Errors +diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym +index 5e2fc9e231..2178668d11 100644 +--- a/src/libsystemd/libsystemd.sym ++++ b/src/libsystemd/libsystemd.sym +@@ -767,4 +767,5 @@ LIBSYSTEMD_249 { + sd_device_get_trigger_uuid; + sd_device_new_from_ifname; + sd_device_new_from_ifindex; ++ sd_event_source_set_ratelimit_expire_callback; + } LIBSYSTEMD_248; +diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h +index 41845c0bb5..74cbc26962 100644 +--- a/src/libsystemd/sd-event/event-source.h ++++ b/src/libsystemd/sd-event/event-source.h +@@ -71,6 +71,7 @@ struct sd_event_source { + uint64_t prepare_iteration; + + sd_event_destroy_t destroy_callback; ++ sd_event_handler_t ratelimit_expire_callback; + + LIST_FIELDS(sd_event_source, sources); + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 0ca0248510..d8f84d9ba7 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -2908,7 +2908,7 @@ fail: + return r; + } + +-static int event_source_leave_ratelimit(sd_event_source *s) { ++static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) { + int r; + + assert(s); +@@ -2940,6 +2940,30 @@ static int event_source_leave_ratelimit(sd_event_source *s) { + ratelimit_reset(&s->rate_limit); + + log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description)); ++ ++ if (run_callback && s->ratelimit_expire_callback) { ++ s->dispatching = true; ++ r = s->ratelimit_expire_callback(s, s->userdata); ++ s->dispatching = false; ++ ++ if (r < 0) { ++ log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, %s: %m", ++ strna(s->description), ++ event_source_type_to_string(s->type), ++ s->exit_on_failure ? "exiting" : "disabling"); ++ ++ if (s->exit_on_failure) ++ (void) sd_event_exit(s->event, r); ++ } ++ ++ if (s->n_ref == 0) ++ source_free(s); ++ else if (r < 0) ++ sd_event_source_set_enabled(s, SD_EVENT_OFF); ++ ++ return 1; ++ } ++ + return 0; + + fail: +@@ -3139,6 +3163,7 @@ static int process_timer( + struct clock_data *d) { + + sd_event_source *s; ++ bool callback_invoked = false; + int r; + + assert(e); +@@ -3156,9 +3181,11 @@ static int process_timer( + * again. */ + assert(s->ratelimited); + +- r = event_source_leave_ratelimit(s); ++ r = event_source_leave_ratelimit(s, /* run_callback */ true); + if (r < 0) + return r; ++ else if (r == 1) ++ callback_invoked = true; + + continue; + } +@@ -3173,7 +3200,7 @@ static int process_timer( + event_source_time_prioq_reshuffle(s); + } + +- return 0; ++ return callback_invoked; + } + + static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) { +@@ -4097,15 +4124,15 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + if (r < 0) + goto finish; + +- r = process_timer(e, e->timestamp.realtime, &e->realtime); ++ r = process_inotify(e); + if (r < 0) + goto finish; + +- r = process_timer(e, e->timestamp.boottime, &e->boottime); ++ r = process_timer(e, e->timestamp.realtime, &e->realtime); + if (r < 0) + goto finish; + +- r = process_timer(e, e->timestamp.monotonic, &e->monotonic); ++ r = process_timer(e, e->timestamp.boottime, &e->boottime); + if (r < 0) + goto finish; + +@@ -4117,9 +4144,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + if (r < 0) + goto finish; + +- r = process_inotify(e); ++ r = process_timer(e, e->timestamp.monotonic, &e->monotonic); + if (r < 0) + goto finish; ++ else if (r == 1) { ++ /* Ratelimit expiry callback was called. Let's postpone processing pending sources and ++ * put loop in the initial state in order to evaluate (in the next iteration) also sources ++ * there were potentially re-enabled by the callback. ++ * ++ * Wondering why we treat only this invocation of process_timer() differently? Once event ++ * source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence ++ * ratelimit expiry callback is never called for any other timer type. */ ++ r = 0; ++ goto finish; ++ } + + if (event_next_pending(e)) { + e->state = SD_EVENT_PENDING; +@@ -4488,7 +4526,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval + + /* When ratelimiting is configured we'll always reset the rate limit state first and start fresh, + * non-ratelimited. */ +- r = event_source_leave_ratelimit(s); ++ r = event_source_leave_ratelimit(s, /* run_callback */ false); + if (r < 0) + return r; + +@@ -4496,6 +4534,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval + return 0; + } + ++_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) { ++ assert_return(s, -EINVAL); ++ ++ s->ratelimit_expire_callback = callback; ++ return 0; ++} ++ + _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) { + assert_return(s, -EINVAL); + +diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c +index b637639f26..0ac23c1118 100644 +--- a/src/libsystemd/sd-event/test-event.c ++++ b/src/libsystemd/sd-event/test-event.c +@@ -623,6 +623,11 @@ static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userd + return 0; + } + ++static int expired = -1; ++static int ratelimit_expired(sd_event_source *s, void *userdata) { ++ return ++expired; ++} ++ + static void test_ratelimit(void) { + _cleanup_close_pair_ int p[2] = {-1, -1}; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; +@@ -686,12 +691,19 @@ static void test_ratelimit(void) { + + assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0); + ++ /* Set callback that will be invoked when we leave rate limited state. */ ++ assert_se(sd_event_source_set_ratelimit_expire_callback(s, ratelimit_expired) >= 0); ++ + do { + assert_se(sd_event_run(e, UINT64_MAX) >= 0); + } while (!sd_event_source_is_ratelimited(s)); + + log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited"); + assert_se(count == 20); ++ ++ /* Dispatch the event loop once more and check that ratelimit expiration callback got called */ ++ assert_se(sd_event_run(e, UINT64_MAX) >= 0); ++ assert_se(expired == 0); + } + + static void test_simple_timeout(void) { +diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h +index f4357aa593..63984eef15 100644 +--- a/src/systemd/sd-event.h ++++ b/src/systemd/sd-event.h +@@ -166,6 +166,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b); + int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); + int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); + int sd_event_source_is_ratelimited(sd_event_source *s); ++int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback); + + /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); +-- +2.33.0 + diff --git a/backport-unit-add-jobs-that-were-skipped-because-of-ratelimit.patch b/backport-unit-add-jobs-that-were-skipped-because-of-ratelimit.patch new file mode 100644 index 0000000..b9b3510 --- /dev/null +++ b/backport-unit-add-jobs-that-were-skipped-because-of-ratelimit.patch @@ -0,0 +1,47 @@ +From c29e6a9530316823b0455cd83eb6d0bb8dd664f4 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 25 Nov 2021 18:28:25 +0100 +Subject: [PATCH] unit: add jobs that were skipped because of ratelimit back to + run_queue + +Assumption in edc027b was that job we first skipped because of active +ratelimit is still in run_queue. Hence we trigger the queue and dispatch +it in the next iteration. Actually we remove jobs from run_queue in +job_run_and_invalidate() before we call unit_start(). Hence if we want +to attempt to run the job again in the future we need to add it back +to run_queue. + +Fixes #21458 + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/c29e6a9530316823b0455cd83eb6d0bb8dd664f4 +--- + src/core/mount.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 90b11347f7..35368fe8e6 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1840,9 +1840,18 @@ static bool mount_is_mounted(Mount *m) { + + static int mount_on_ratelimit_expire(sd_event_source *s, void *userdata) { + Manager *m = userdata; ++ Job *j; + + assert(m); + ++ /* Let's enqueue all start jobs that were previously skipped because of active ratelimit. */ ++ HASHMAP_FOREACH(j, m->jobs) { ++ if (j->unit->type != UNIT_MOUNT) ++ continue; ++ ++ job_add_to_run_queue(j); ++ } ++ + /* By entering ratelimited state we made all mount start jobs not runnable, now rate limit is over so + * let's make sure we dispatch them in the next iteration. */ + manager_trigger_run_queue(m); +-- +2.33.0 + diff --git a/backport-unit-check-for-mount-rate-limiting-before-checking-a.patch b/backport-unit-check-for-mount-rate-limiting-before-checking-a.patch new file mode 100644 index 0000000..e2eabd8 --- /dev/null +++ b/backport-unit-check-for-mount-rate-limiting-before-checking-a.patch @@ -0,0 +1,54 @@ +From b161bc394b2cc8b271dda9208e310cc2af0cc29d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 30 May 2022 11:55:41 +0200 +Subject: [PATCH] unit: check for mount rate limiting before checking active + state + +Having this check as part of mount_can_start() is too late because +UNIT(u)->can_start() virtual method is called after checking the active +state of unit in unit_start(). + +We need to hold off running mount start jobs when /p/s/mountinfo monitor +is rate limited even when given mount unit is already active. + +Fixes #20329 + +Conflict:NA +Reference:https://github.com/systemd/systemd-stable/commit/b161bc394b2cc8b271dda9208e310cc2af0cc29d +--- + src/core/mount.c | 3 --- + src/core/unit.c | 4 ++++ + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 20b4bb6d2b..a2fd9c2a99 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -2170,9 +2170,6 @@ static int mount_can_start(Unit *u) { + + assert(m); + +- if (sd_event_source_is_ratelimited(u->manager->mount_event_source)) +- return -EAGAIN; +- + r = unit_test_start_limit(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); +diff --git a/src/core/unit.c b/src/core/unit.c +index b0756bc6f4..d7f5245ca3 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1868,6 +1868,10 @@ int unit_start(Unit *u) { + + assert(u); + ++ /* Let's hold off running start jobs for mount units when /proc/self/mountinfo monitor is rate limited. */ ++ if (u->type == UNIT_MOUNT && sd_event_source_is_ratelimited(u->manager->mount_event_source)) ++ return -EAGAIN; ++ + /* If this is already started, then this will succeed. Note that this will even succeed if this unit + * is not startable by the user. This is relied on to detect when we need to wait for units and when + * waiting is finished. */ +-- +2.33.0 + diff --git a/delete-journal-files-except-system.journal-when-jour.patch b/delete-journal-files-except-system.journal-when-jour.patch index bb57e4e..34d850c 100644 --- a/delete-journal-files-except-system.journal-when-jour.patch +++ b/delete-journal-files-except-system.journal-when-jour.patch @@ -10,12 +10,13 @@ logs). Therefore, when the journal~ file is generated, delete all journal files except system.journal, to ensure that the sd_journal_next function meets user expectations. --- - meson.build | 2 ++ + meson.build | 3 ++- src/basic/dirent-util.c | 24 ++++++++++++++++ src/basic/dirent-util.h | 2 ++ src/libsystemd/sd-journal/journal-file.c | 35 ++++++++++++++++++++++++ src/libsystemd/sd-journal/sd-journal.c | 22 --------------- - 5 files changed, 63 insertions(+), 22 deletions(-) + src/test/meson.build | 2 +- + 6 files changed, 64 insertions(+), 23 deletions(-) diff --git a/meson.build b/meson.build index 278e264..9ab40b6 100644 @@ -30,6 +31,15 @@ index 278e264..9ab40b6 100644 '.') libsystemd_includes = [basic_includes, include_directories( +@@ -1801,7 +1801,7 @@ test_dlopen = executable( + test_dlopen_c, + disable_mempool_c, + include_directories : includes, +- link_with : [libbasic], ++ link_with : [libbasic, libsystemd_static], + dependencies : [libdl], + build_by_default : want_tests != 'false') + diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index f6213a3..b227cae 100644 --- a/src/basic/dirent-util.c @@ -181,6 +191,19 @@ index 1a76bb6..56e1398 100644 static int directory_open(sd_journal *j, const char *path, DIR **ret) { DIR *d; +diff --git a/src/test/meson.build b/src/test/meson.build +index 7f7d8b7..9b1fb87 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -364,7 +364,7 @@ tests += [ + [], [], [], '', 'timeout=90'], + + [['src/test/test-set.c'], +- [libbasic]], ++ [libbasic, libsystemd_static]], + + [['src/test/test-ordered-set.c']], + -- 2.23.0 diff --git a/systemd-core-Add-new-rules-for-lower-priority-events.patch b/systemd-core-Add-new-rules-for-lower-priority-events.patch index 921b362..361dd3d 100644 --- a/systemd-core-Add-new-rules-for-lower-priority-events.patch +++ b/systemd-core-Add-new-rules-for-lower-priority-events.patch @@ -29,7 +29,7 @@ index 053deac..de5b745 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1895,6 +1895,10 @@ static void mount_enumerate(Manager *m) { - goto fail; + goto fail; } + r = sd_event_source_set_preempt_dispatch_count(m->mount_event_source, 5); @@ -53,8 +53,8 @@ index d2dc214..0fa41aa 100644 + unsigned preempt_dispatch_count; /*Will be preempted by lower priority if dispatched count reaches to this*/ + sd_event_destroy_t destroy_callback; + sd_event_handler_t ratelimit_expire_callback; - LIST_FIELDS(sd_event_source, sources); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index e9199de..46f8aff 100644 --- a/src/libsystemd/sd-event/sd-event.c @@ -211,10 +211,10 @@ diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 2ae2a0d..f113aba 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h -@@ -165,6 +165,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b); - int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); +@@ -165,6 +165,7 @@ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); int sd_event_source_is_ratelimited(sd_event_source *s); + int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_set_preempt_dispatch_count(sd_event_source *s, unsigned count); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ diff --git a/systemd.spec b/systemd.spec index d2fbcf6..8c49d38 100644 --- a/systemd.spec +++ b/systemd.spec @@ -21,7 +21,7 @@ Name: systemd Url: https://www.freedesktop.org/wiki/Software/systemd Version: 249 -Release: 52 +Release: 53 License: MIT and LGPLv2+ and GPLv2+ Summary: System and Service Manager @@ -517,6 +517,17 @@ Patch6469: backport-core-device-verify-device-syspath-on-switching-root.pat Patch6470: backport-sd-lldp-use-memcpy_safe-as-the-buffer-size-may-be-ze.patch Patch6471: backport-shared-bootspec-avoid-crashing-on-config-without-a-v.patch Patch6472: backport-sysext-refuse-empty-release-ID-to-avoid-triggering-a.patch +Patch6473: backport-core-Check-unit-start-rate-limiting-earlier.patch +Patch6474: backport-core-Move-r-variable-declaration-to-start-of-unit_st.patch +Patch6475: backport-core-Delay-start-rate-limit-check-when-starting-a-un.patch +Patch6476: backport-core-Add-trigger-limit-for-path-units.patch +Patch6477: backport-sd-event-introduce-callback-invoked-when-event-sourc.patch +Patch6478: backport-core-rename-generalize-UNIT-u-test_start_limit-hook.patch +Patch6479: backport-mount-make-mount-units-start-jobs-not-runnable-if-p-.patch +Patch6480: backport-mount-retrigger-run-queue-after-ratelimit-expired-to.patch +Patch6481: backport-pid1-add-a-manager_trigger_run_queue-helper.patch +Patch6482: backport-unit-add-jobs-that-were-skipped-because-of-ratelimit.patch +Patch6483: backport-unit-check-for-mount-rate-limiting-before-checking-a.patch Patch9001: update-rtc-with-system-clock-when-shutdown.patch Patch9002: udev-add-actions-while-rename-netif-failed.patch @@ -1988,6 +1999,9 @@ fi %{_libdir}/security/pam_systemd.so %changelog +* Tue Aug 15 2023 wangyuhang - 249-53 +- backport: sync patches from systemd community + * Thu Jun 15 2023 hongjinghao - 249-52 - backport: sync patches from systemd community