]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
oomd: support reloading configuration at runtime
authorSea-Eun Lee <seaeunlee@microsoft.com>
Mon, 18 Nov 2024 19:13:40 +0000 (19:13 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 14 Jan 2025 13:42:23 +0000 (14:42 +0100)
man/systemd-oomd.service.xml
src/oom/meson.build
src/oom/oomd-conf.c [new file with mode: 0644]
src/oom/oomd-conf.h [new file with mode: 0644]
src/oom/oomd-manager.c
src/oom/oomd-manager.h
src/oom/oomd.c
src/shared/conf-parser.c
src/shared/conf-parser.h
test/units/TEST-55-OOMD.sh
units/systemd-oomd.service.in

index 53a92509830a30aa41fe26f19a071cb0b3bd9355..9d04c9da98df58c005480e58e8f5c2a344764dc2 100644 (file)
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Signals</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>SIGHUP</constant></term>
+
+        <listitem><para>Upon reception of the <constant>SIGHUP</constant> process signal
+        <command>systemd-oomd</command> will reload its configuration file.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para><simplelist type="inline">
index 690ed7ac6bc028b235fa301bb8ed8af6c8de95d8..bd4b734e0f88617ce76ccdce6a80b7d4610db410 100644 (file)
@@ -1,10 +1,11 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 systemd_oomd_sources = files(
-        'oomd-manager-bus.c',
+        'oomd.c',
+        'oomd-conf.c',
         'oomd-manager.c',
+        'oomd-manager-bus.c',
         'oomd-util.c',
-        'oomd.c',
 )
 
 executables += [
diff --git a/src/oom/oomd-conf.c b/src/oom/oomd-conf.c
new file mode 100644 (file)
index 0000000..1fcac36
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "conf-parser.h"
+#include "log.h"
+#include "oomd-conf.h"
+#include "oomd-manager.h"
+
+static int config_parse_duration(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        usec_t usec, *duration = ASSERT_PTR(data);
+        int r;
+
+        if (isempty(rvalue)) {
+                *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &usec);
+        if (r < 0)
+                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+        if (usec == 0) {
+                /* Map zero -> default for backwards compatibility. */
+                *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+                return 0;
+        }
+
+        if (usec < 1 * USEC_PER_SEC || usec == USEC_INFINITY)
+                return log_syntax(
+                                unit,
+                                LOG_WARNING,
+                                filename,
+                                line,
+                                0,
+                                "%s= must be at least 1s and less than infinity, ignoring: %s",
+                                lvalue,
+                                rvalue);
+
+        *duration = usec;
+        return 0;
+}
+
+void manager_set_defaults(Manager *m) {
+        int r;
+
+        assert(m);
+
+        m->default_mem_pressure_duration_usec = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+
+        m->swap_used_limit_permyriad = DEFAULT_SWAP_USED_LIMIT_PERCENT * 100;
+        r = store_loadavg_fixed_point(DEFAULT_MEM_PRESSURE_LIMIT_PERCENT, 0, &m->default_mem_pressure_limit);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set default for default_mem_pressure_limit, ignoring: %m");
+}
+
+void manager_parse_config_file(Manager *m) {
+        int r;
+
+        assert(m);
+
+        const ConfigTableItem items[] = {
+                { "OOM", "SwapUsedLimit",                    config_parse_permyriad, 0, &m->swap_used_limit_permyriad          },
+                { "OOM", "DefaultMemoryPressureLimit",       config_parse_loadavg,   0, &m->default_mem_pressure_limit         },
+                { "OOM", "DefaultMemoryPressureDurationSec", config_parse_duration,  0, &m->default_mem_pressure_duration_usec },
+                {}
+        };
+
+        r = config_parse_standard_file_with_dropins(
+                        "systemd/oomd.conf",
+                        "OOM\0",
+                        config_item_table_lookup,
+                        items,
+                        CONFIG_PARSE_WARN,
+                        /* userdata= */ m);
+        if (r >= 0)
+                log_debug("Config file successfully parsed.");
+}
diff --git a/src/oom/oomd-conf.h b/src/oom/oomd-conf.h
new file mode 100644 (file)
index 0000000..0283b9e
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "conf-parser.h"
+#include "oomd-manager.h"
+
+void manager_set_defaults(Manager *m);
+
+void manager_parse_config_file(Manager *m);
index baa88a2f2a0efe2d729dddee7787aca13010e5bf..ce3674365a34d94a78f34d74bc2202082145d491 100644 (file)
@@ -4,15 +4,17 @@
 #include "sd-json.h"
 
 #include "bus-log-control-api.h"
-#include "bus-util.h"
 #include "bus-polkit.h"
+#include "bus-util.h"
 #include "cgroup-util.h"
+#include "daemon-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
 #include "json-util.h"
 #include "memory-util.h"
 #include "memstream-util.h"
+#include "oomd-conf.h"
 #include "oomd-manager-bus.h"
 #include "oomd-manager.h"
 #include "path-util.h"
@@ -649,6 +651,18 @@ Manager* manager_free(Manager *m) {
         return mfree(m);
 }
 
+static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+
+        (void) notify_reloading();
+
+        manager_set_defaults(m);
+        manager_parse_config_file(m);
+
+        (void) sd_notify(/* unset= */ false, NOTIFY_READY);
+        return 0;
+}
+
 int manager_new(Manager **ret) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -659,12 +673,19 @@ int manager_new(Manager **ret) {
         if (!m)
                 return -ENOMEM;
 
+        manager_set_defaults(m);
+        manager_parse_config_file(m);
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
 
         (void) sd_event_set_watchdog(m->event, true);
 
+        r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m);
+        if (r < 0)
+                return r;
+
         r = sd_event_set_signal_exit(m->event, true);
         if (r < 0)
                 return r;
@@ -753,36 +774,14 @@ static int manager_varlink_init(Manager *m, int fd) {
 int manager_start(
                 Manager *m,
                 bool dry_run,
-                int swap_used_limit_permyriad,
-                int mem_pressure_limit_permyriad,
-                usec_t mem_pressure_usec,
                 int fd) {
 
-        unsigned long l, f;
         int r;
 
         assert(m);
 
         m->dry_run = dry_run;
 
-        m->swap_used_limit_permyriad = swap_used_limit_permyriad >= 0 ? swap_used_limit_permyriad : DEFAULT_SWAP_USED_LIMIT_PERCENT * 100;
-        assert(m->swap_used_limit_permyriad <= 10000);
-
-        if (mem_pressure_limit_permyriad >= 0) {
-                assert(mem_pressure_limit_permyriad <= 10000);
-
-                l = mem_pressure_limit_permyriad / 100;
-                f = mem_pressure_limit_permyriad % 100;
-        } else {
-                l = DEFAULT_MEM_PRESSURE_LIMIT_PERCENT;
-                f = 0;
-        }
-        r = store_loadavg_fixed_point(l, f, &m->default_mem_pressure_limit);
-        if (r < 0)
-                return r;
-
-        m->default_mem_pressure_duration_usec = mem_pressure_usec;
-
         r = manager_connect_bus(m);
         if (r < 0)
                 return r;
index 635e16ddfc1d23301b4351cdd68b6605a6bd6ead..a40946ce4e1426285f1fc892331282735a566da5 100644 (file)
@@ -66,7 +66,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
 int manager_new(Manager **ret);
 
-int manager_start(Manager *m, bool dry_run, int swap_used_limit_permyriad, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec, int fd);
+int manager_start(Manager *m, bool dry_run, int fd);
 
 int manager_get_dump_string(Manager *m, char **ret);
 
index dd34251cd24ad23b125350c95b870a723dbb97f9..518ddc47c10bbc5f9c195b90e33c4146562a8c71 100644 (file)
@@ -11,6 +11,7 @@
 #include "fileio.h"
 #include "log.h"
 #include "main-func.h"
+#include "oomd-conf.h"
 #include "oomd-manager-bus.h"
 #include "oomd-manager.h"
 #include "parse-util.h"
 #include "signal-util.h"
 
 static bool arg_dry_run = false;
-static int arg_swap_used_limit_permyriad = -1;
-static int arg_mem_pressure_limit_permyriad = -1;
-static usec_t arg_mem_pressure_usec = DEFAULT_MEM_PRESSURE_DURATION_USEC;
-
-static int config_parse_duration(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        usec_t usec, *duration = ASSERT_PTR(data);
-        int r;
-
-        if (isempty(rvalue)) {
-                *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
-                return 0;
-        }
-
-        r = parse_sec(rvalue, &usec);
-        if (r < 0)
-                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
-
-        if (usec == 0) {
-                /* Map zero -> default for backwards compatibility. */
-                *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
-                return 0;
-        }
-
-        if (usec < 1 * USEC_PER_SEC || usec == USEC_INFINITY)
-                return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= must be at least 1s and less than infinity, ignoring: %s", lvalue, rvalue);
-
-        *duration = usec;
-        return 0;
-}
-
-static int parse_config(void) {
-        static const ConfigTableItem items[] = {
-                { "OOM", "SwapUsedLimit",                    config_parse_permyriad, 0, &arg_swap_used_limit_permyriad    },
-                { "OOM", "DefaultMemoryPressureLimit",       config_parse_permyriad, 0, &arg_mem_pressure_limit_permyriad },
-                { "OOM", "DefaultMemoryPressureDurationSec", config_parse_duration,  0, &arg_mem_pressure_usec            },
-                {}
-        };
-
-        return config_parse_standard_file_with_dropins(
-                        "systemd/oomd.conf",
-                        "OOM\0",
-                        config_item_table_lookup, items,
-                        CONFIG_PARSE_WARN,
-                        /* userdata= */ NULL);
-}
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -166,10 +111,6 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
-        r = parse_config();
-        if (r < 0)
-                return r;
-
         /* Do some basic requirement checks for running systemd-oomd. It's not exhaustive as some of the other
          * requirements do not have a reliable means to check for in code. */
 
@@ -211,9 +152,6 @@ static int run(int argc, char *argv[]) {
         r = manager_start(
                         m,
                         arg_dry_run,
-                        arg_swap_used_limit_permyriad,
-                        arg_mem_pressure_limit_permyriad,
-                        arg_mem_pressure_usec,
                         fd);
         if (r < 0)
                 return log_error_errno(r, "Failed to start up daemon: %m");
index eaa8a5f11c6b48fbbcec622e6e0cfb2db1b57f53..6ba8adf5c212631d86618e0dd29552d2dfe93019 100644 (file)
@@ -2180,3 +2180,31 @@ int config_parse_ip_protocol(
         *proto = r;
         return 1; /* done. */
 }
+
+int config_parse_loadavg(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        loadavg_t *i = ASSERT_PTR(data);
+        int r;
+
+        assert(rvalue);
+
+        r = parse_permyriad(rvalue);
+        if (r < 0)
+                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+        r = store_loadavg_fixed_point(r / 100, r % 100, i);
+        if (r < 0)
+                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+        return 1; /* done. */
+}
index 1599738f8492385dde4fe35f4da8b84f2d91b384..d882e2d32022ab566a93b60aa124dd6718ca49ed 100644 (file)
@@ -313,6 +313,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
 CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
 CONFIG_PARSER_PROTOTYPE(config_parse_calendar);
 CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol);
+CONFIG_PARSER_PROTOTYPE(config_parse_loadavg);
 
 typedef enum Disabled {
         DISABLED_CONFIGURATION,
index 3c9b4391a963e7d58872b1608c91ebc44098ed77..66793378584c6ff4ab8f10e9094dacb19347ed74 100755 (executable)
@@ -264,6 +264,40 @@ EOF
     systemctl daemon-reload
 }
 
+testcase_reload() {
+    # Check if the oomd.conf drop-in config is loaded.
+    assert_in 'Swap Used Limit: 90.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Limit: 60.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Duration: 2s' "$(oomctl)"
+
+    # Test oomd reload
+    mkdir -p /run/systemd/oomd.conf.d/
+    {
+        echo "[OOM]"
+        echo "SwapUsedLimit=80%"
+        echo "DefaultMemoryPressureLimit=55%"
+        echo "DefaultMemoryPressureDurationSec=5s"
+    } >/run/systemd/oomd.conf.d/99-oomd-test.conf
+
+    systemctl reload systemd-oomd.service
+    assert_in 'Swap Used Limit: 80.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Limit: 55.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Duration: 5s' "$(oomctl)"
+
+    # Set back to default via reload
+    mkdir -p /run/systemd/oomd.conf.d/
+    {
+        echo "[OOM]"
+        echo "DefaultMemoryPressureDurationSec=2s"
+    } >/run/systemd/oomd.conf.d/99-oomd-test.conf
+
+    systemctl reload systemd-oomd.service
+
+    assert_in 'Swap Used Limit: 90.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Limit: 60.00%' "$(oomctl)"
+    assert_in 'Default Memory Pressure Duration: 2s' "$(oomctl)"
+}
+
 run_testcases
 
 systemd-analyze log-level info
index 6838ddccef46a0f3941440b807bfc93052792542..3e9533dac4e7925ada6b3b63a7258a1f5b58d0a7 100644 (file)
@@ -53,7 +53,7 @@ RestrictSUIDSGID=yes
 SystemCallArchitectures=native
 SystemCallErrorNumber=EPERM
 SystemCallFilter=@system-service
-Type=notify
+Type=notify-reload
 User=systemd-oom
 {{SERVICE_WATCHDOG}}