From: Michal Sekletar Date: Wed, 13 May 2026 14:20:55 +0000 (+0200) Subject: core: make manager event loop rate limit configurable X-Git-Tag: v261-rc1~162 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e1c63fd4c0657a7fc7b749c64283ab4dbc6fb39e;p=thirdparty%2Fsystemd.git core: make manager event loop rate limit configurable Co-developed-by: Claude Opus 4.6 --- diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 5474dd788ed..7efc899dba2 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -475,6 +475,10 @@ node /org/freedesktop/systemd1 { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly u DefaultStartLimitBurst = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t EventLoopRateLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u EventLoopRateLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b DefaultIOAccounting = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b DefaultIPAccounting = ...; @@ -725,6 +729,10 @@ node /org/freedesktop/systemd1 { + + + + @@ -1183,6 +1191,10 @@ node /org/freedesktop/systemd1 { + + + + @@ -12691,8 +12703,10 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ DefaultCPUPressureThresholdUSec, DefaultCPUPressureWatch, DefaultIOPressureThresholdUSec, - DefaultIOPressureWatch, and - ReloadCount were added in version 261. + DefaultIOPressureWatch, + ReloadCount, + EventLoopRateLimitIntervalUSec, and + EventLoopRateLimitBurst were added in version 261. Unit Objects diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index fb565b03506..e33267409bf 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -705,6 +705,22 @@ + + EventLoopRateLimitIntervalSec= + EventLoopRateLimitBurst= + + Configures the rate limiting applied to the manager's main event loop. If the event + loop iterates more than EventLoopRateLimitBurst= times within + EventLoopRateLimitIntervalSec=, event processing is briefly paused to prevent + excessive CPU usage. EventLoopRateLimitIntervalSec= defaults to 1s. + EventLoopRateLimitBurst= defaults to 50000. These settings can also be set on the + kernel command line via + systemd.event_loop_ratelimit_interval_sec= and + systemd.event_loop_ratelimit_burst=. + + + + MinimumUptimeSec= diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 5b41e1befe7..595136f22c9 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2959,6 +2959,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, defaults.start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, defaults.start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, defaults.start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("EventLoopRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, event_loop_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("EventLoopRateLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, event_loop_ratelimit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultIOAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.io_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultIPAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.ip_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/main.c b/src/core/main.c index c10df7d87a4..2ce73a8624d 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -165,6 +165,8 @@ static void *arg_random_seed; static size_t arg_random_seed_size; static usec_t arg_reload_limit_interval_sec; static unsigned arg_reload_limit_burst; +static usec_t arg_event_loop_ratelimit_interval_sec; +static unsigned arg_event_loop_ratelimit_burst; static usec_t arg_minimum_uptime_usec; /* A copy of the original environment block */ @@ -557,6 +559,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } + } else if (proc_cmdline_key_streq(key, "systemd.event_loop_ratelimit_interval_sec")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = parse_sec(value, &arg_event_loop_ratelimit_interval_sec); + if (r < 0) { + log_warning_errno(r, "Failed to parse systemd.event_loop_ratelimit_interval_sec= argument '%s', ignoring: %m", value); + return 0; + } + + } else if (proc_cmdline_key_streq(key, "systemd.event_loop_ratelimit_burst")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = safe_atou(value, &arg_event_loop_ratelimit_burst); + if (r < 0) { + log_warning_errno(r, "Failed to parse systemd.event_loop_ratelimit_burst= argument '%s', ignoring: %m", value); + return 0; + } + } else if (proc_cmdline_key_streq(key, "systemd.minimum_uptime_sec")) { if (proc_cmdline_value_missing(key, value)) @@ -865,6 +889,8 @@ static int parse_config_file(void) { { "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL }, { "Manager", "ReloadLimitIntervalSec", config_parse_sec, 0, &arg_reload_limit_interval_sec }, { "Manager", "ReloadLimitBurst", config_parse_unsigned, 0, &arg_reload_limit_burst }, + { "Manager", "EventLoopRateLimitIntervalSec", config_parse_sec, 0, &arg_event_loop_ratelimit_interval_sec }, + { "Manager", "EventLoopRateLimitBurst", config_parse_unsigned, 0, &arg_event_loop_ratelimit_burst }, { "Manager", "DefaultMemoryZSwapWriteback", config_parse_bool, 0, &arg_defaults.memory_zswap_writeback }, { "Manager", "MinimumUptimeSec", config_parse_sec, 0, &arg_minimum_uptime_usec }, #if ENABLE_SMACK @@ -951,6 +977,8 @@ static void set_manager_settings(Manager *m) { * counter on every daemon-reload. */ m->reload_reexec_ratelimit.interval = arg_reload_limit_interval_sec; m->reload_reexec_ratelimit.burst = arg_reload_limit_burst; + m->event_loop_ratelimit.interval = arg_event_loop_ratelimit_interval_sec; + m->event_loop_ratelimit.burst = arg_event_loop_ratelimit_burst; manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog); manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog); @@ -2902,6 +2930,9 @@ static void reset_arguments(void) { arg_reload_limit_interval_sec = 0; arg_reload_limit_burst = 0; + arg_event_loop_ratelimit_interval_sec = 1 * USEC_PER_SEC; + arg_event_loop_ratelimit_burst = 50000; + arg_minimum_uptime_usec = USEC_INFINITY; } diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c index d3549d81294..6bc41f15f93 100644 --- a/src/core/manager-serialize.c +++ b/src/core/manager-serialize.c @@ -163,6 +163,7 @@ int manager_serialize( (void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit); (void) serialize_ratelimit(f, "reload-reexec-ratelimit", &m->reload_reexec_ratelimit); + (void) serialize_ratelimit(f, "event-loop-ratelimit", &m->event_loop_ratelimit); (void) serialize_id128(f, "bus-id", m->bus_id); bus_track_serialize(m->subscribed, f, "subscribed"); @@ -747,6 +748,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val); else if ((val = startswith(l, "reload-reexec-ratelimit="))) deserialize_ratelimit(&m->reload_reexec_ratelimit, "reload-reexec-ratelimit", val); + else if ((val = startswith(l, "event-loop-ratelimit="))) + deserialize_ratelimit(&m->event_loop_ratelimit, "event-loop-ratelimit", val); else if ((val = startswith(l, "soft-reboots-count="))) { unsigned n; diff --git a/src/core/manager.c b/src/core/manager.c index 0e33273c3dc..038690a808a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -3310,7 +3310,6 @@ static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t use } int manager_loop(Manager *m) { - RateLimit rl = { .interval = 1*USEC_PER_SEC, .burst = 50000 }; int r; assert(m); @@ -3325,7 +3324,7 @@ int manager_loop(Manager *m) { while (m->objective == MANAGER_OK) { - if (!ratelimit_below(&rl)) { + if (!ratelimit_below(&m->event_loop_ratelimit)) { /* Yay, something is going seriously wrong, pause a little */ log_warning("Looping too fast. Throttling execution a little."); sleep(1); diff --git a/src/core/manager.h b/src/core/manager.h index fb65705d321..d17693369ab 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -495,6 +495,9 @@ typedef struct Manager { /* Dump*() are slow, so always rate limit them to 10 per 10 minutes */ RateLimit dump_ratelimit; + /* Rate limit for the manager event loop */ + RateLimit event_loop_ratelimit; + sd_event_source *pressure_event_source[_PRESSURE_RESOURCE_MAX]; /* For NFTSet= */ diff --git a/src/core/system.conf.in b/src/core/system.conf.in index 35c7cec6efc..aa8208ca1d5 100644 --- a/src/core/system.conf.in +++ b/src/core/system.conf.in @@ -88,4 +88,6 @@ #DefaultRestrictSUIDSGID= #ReloadLimitIntervalSec= #ReloadLimitBurst= +#EventLoopRateLimitIntervalSec=1s +#EventLoopRateLimitBurst=50000 #DefaultMemoryZSwapWriteback=yes diff --git a/src/core/user.conf.in b/src/core/user.conf.in index 33c6733268c..f9c40458da4 100644 --- a/src/core/user.conf.in +++ b/src/core/user.conf.in @@ -61,4 +61,6 @@ #DefaultSmackProcessLabel= #DefaultRestrictSUIDSGID= #ReloadLimitIntervalSec= -#ReloadLimitBurst +#ReloadLimitBurst= +#EventLoopRateLimitIntervalSec=1s +#EventLoopRateLimitBurst=50000