]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: make manager event loop rate limit configurable
authorMichal Sekletar <msekleta@redhat.com>
Wed, 13 May 2026 14:20:55 +0000 (16:20 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 15 May 2026 05:01:57 +0000 (14:01 +0900)
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
man/org.freedesktop.systemd1.xml
man/systemd-system.conf.xml
src/core/dbus-manager.c
src/core/main.c
src/core/manager-serialize.c
src/core/manager.c
src/core/manager.h
src/core/system.conf.in
src/core/user.conf.in

index 5474dd788ed6d954ab2977e5aff4e5f6707821ad..7efc899dba25110bf71d9d45a61162b7733eeb4a 100644 (file)
@@ -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 {
 
     <!--property DefaultStartLimitBurst is not documented!-->
 
+    <!--property EventLoopRateLimitIntervalUSec is not documented!-->
+
+    <!--property EventLoopRateLimitBurst is not documented!-->
+
     <!--property DefaultIOAccounting is not documented!-->
 
     <!--property DefaultIPAccounting is not documented!-->
@@ -1183,6 +1191,10 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultStartLimitBurst"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EventLoopRateLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EventLoopRateLimitBurst"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultIOAccounting"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultIPAccounting"/>
@@ -12691,8 +12703,10 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>DefaultCPUPressureThresholdUSec</varname>,
       <varname>DefaultCPUPressureWatch</varname>,
       <varname>DefaultIOPressureThresholdUSec</varname>,
-      <varname>DefaultIOPressureWatch</varname>, and
-      <varname>ReloadCount</varname> were added in version 261.</para>
+      <varname>DefaultIOPressureWatch</varname>,
+      <varname>ReloadCount</varname>,
+      <varname>EventLoopRateLimitIntervalUSec</varname>, and
+      <varname>EventLoopRateLimitBurst</varname> were added in version 261.</para>
     </refsect2>
     <refsect2>
       <title>Unit Objects</title>
index fb565b03506c7f4b1300127a10a1073467266693..e33267409bf2fd0e903214e5071acfd7dc6d0b36 100644 (file)
         <xi:include href="version-info.xml" xpointer="v253"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>EventLoopRateLimitIntervalSec=</varname></term>
+        <term><varname>EventLoopRateLimitBurst=</varname></term>
+
+        <listitem><para>Configures the rate limiting applied to the manager's main event loop. If the event
+        loop iterates more than <varname>EventLoopRateLimitBurst=</varname> times within
+        <varname>EventLoopRateLimitIntervalSec=</varname>, event processing is briefly paused to prevent
+        excessive CPU usage. <varname>EventLoopRateLimitIntervalSec=</varname> defaults to 1s.
+        <varname>EventLoopRateLimitBurst=</varname> defaults to 50000. These settings can also be set on the
+        kernel command line via
+        <varname>systemd.event_loop_ratelimit_interval_sec=</varname> and
+        <varname>systemd.event_loop_ratelimit_burst=</varname>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>MinimumUptimeSec=</varname></term>
 
index 5b41e1befe7cc646c7411bcd89b7fe9252108aa3..595136f22c9944137441a248b594b2a1bec676a8 100644 (file)
@@ -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),
index c10df7d87a4fca65bc6659daa91ea81e17174e12..2ce73a8624d92953fca1085a0cc43c1fc4eca2ca 100644 (file)
@@ -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;
 }
 
index d3549d81294eab12928e4b4b91f67d2bd374c8ae..6bc41f15f93bfeab295b8a7aba1e671678d00a3f 100644 (file)
@@ -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;
 
index 0e33273c3dc2464f6ccc623c8057c7fb29a96029..038690a808a00f71fa796aba61c2ddc89a5551cd 100644 (file)
@@ -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);
index fb65705d321df08d270ef4f8a2c064d3afabeb93..d17693369ab5a4937dff09b8349f7c2df566a1c3 100644 (file)
@@ -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= */
index 35c7cec6efcbbcbf8aeb23a22947e5658f28b221..aa8208ca1d5043f458173fc8172d52dca359966f 100644 (file)
@@ -88,4 +88,6 @@
 #DefaultRestrictSUIDSGID=
 #ReloadLimitIntervalSec=
 #ReloadLimitBurst=
+#EventLoopRateLimitIntervalSec=1s
+#EventLoopRateLimitBurst=50000
 #DefaultMemoryZSwapWriteback=yes
index 33c6733268c08cd56e6364171704e246d89c838a..f9c40458da4517936b078139b3c3378ca6427567 100644 (file)
@@ -61,4 +61,6 @@
 #DefaultSmackProcessLabel=
 #DefaultRestrictSUIDSGID=
 #ReloadLimitIntervalSec=
-#ReloadLimitBurst
+#ReloadLimitBurst=
+#EventLoopRateLimitIntervalSec=1s
+#EventLoopRateLimitBurst=50000