]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
watchdog: add setting to configure pretimeout governor 19970/head
authorLuca Boccassi <bluca@debian.org>
Tue, 8 Feb 2022 12:58:30 +0000 (12:58 +0000)
committerLuca Boccassi <bluca@debian.org>
Tue, 22 Feb 2022 17:19:54 +0000 (17:19 +0000)
12 files changed:
man/kernel-command-line.xml
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/shared/watchdog.c
src/shared/watchdog.h
test/fuzz/fuzz-unit-file/directives-all.service

index 9b04aa17061ee2f435005fc094a801b90075d3a7..79bd90b340f30955b00acde9a0945a1992f29ced 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>systemd.watchdog_pretimeout_governor=</varname></term>
+
+        <listitem>
+          <para>Overrides the watchdog pre-timeout settings otherwise configured with
+          <varname>RuntimeWatchdogPreGovernor=</varname>. Takes a string value. For details, see
+          <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>systemd.cpu_affinity=</varname></term>
 
index 8976521589805805ea0a2c4e402bf9add7d3f4da..fdf8ca3cc82353b1e41a743e301bd7ce86acfa71 100644 (file)
@@ -405,6 +405,9 @@ node /org/freedesktop/systemd1 {
       readwrite t RuntimeWatchdogPreUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       @org.freedesktop.systemd1.Privileged("true")
+      readwrite s RuntimeWatchdogPreGovernor = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
       readwrite t RebootWatchdogUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       @org.freedesktop.systemd1.Privileged("true")
@@ -655,6 +658,8 @@ node /org/freedesktop/systemd1 {
 
     <!--property RuntimeWatchdogPreUSec is not documented!-->
 
+    <!--property RuntimeWatchdogPreGovernor is not documented!-->
+
     <!--property RebootWatchdogUSec is not documented!-->
 
     <!--property KExecWatchdogUSec is not documented!-->
@@ -1059,6 +1064,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RuntimeWatchdogPreUSec"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeWatchdogPreGovernor"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="RebootWatchdogUSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="KExecWatchdogUSec"/>
index 01eff0839e35ff10f407594c85af140b73e961dd..351662b757047e3fcb57d9c12c041655c4bd5e9f 100644 (file)
         notification generated by the watchdog before the watchdog reset might
         occur in the event the watchdog has not been serviced. This notification
         is handled by the kernel and can be configured to take an action (i.e.
-        generate a kernel panic) using the
-        <filename>/sys/class/watchdog/watchdog0/pretimeout_governor</filename>
-        sysfs file for your watchdog device. The available actions (or
-        governors) are listed in the
-        <filename>/sys/class/watchdog/watchdog0/pretimeout_available_governors</filename>
-        sysfs file for your watchdog device. The default action for the
-        pre-timeout event is to log a kernel message but that can be changed in
-        the kernel's configuration. Not all watchdog hardware or drivers support
-        generating a pre-timeout and depending on the state of the system, the
-        kernel may be unable to take the configured action before the watchdog
-        reboot. The watchdog will be configured to generate the pre-timeout event
-        at the amount of time specified by <varname>RuntimeWatchdogPreSec=</varname>
-        before the runtime watchdog timeout (set by
-        <varname>RuntimeWatchdogSec=</varname>). For example, if the we have
+        generate a kernel panic) using <varname>RuntimeWatchdogPreGovernor=</varname>.
+        Not all watchdog hardware or drivers support generating a pre-timeout and
+        depending on the state of the system, the kernel may be unable to take the
+        configured action before the watchdog reboot. The watchdog will be configured
+        to generate the pre-timeout event at the amount of time specified by
+        <varname>RuntimeWatchdogPreSec=</varname> before the runtime watchdog timeout
+        (set by <varname>RuntimeWatchdogSec=</varname>). For example, if the we have
         <varname>RuntimeWatchdogSec=30</varname> and
         <varname>RuntimeWatchdogPreSec=10</varname>, then the pre-timeout event
         will occur if the watchdog has not pinged for 20s (10s before the
         kernel if the setting is greater than the actual watchdog timeout.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RuntimeWatchdogPreGovernor=</varname></term>
+
+        <listitem><para>Configure the action taken by the hardware watchdog device
+        when the pre-timeout expires. The default action for the pre-timeout event
+        depends on the kernel configuration, but it is usually to log a kernel
+        message. For a list of valid actions available for a given watchdog device,
+        check the content of the
+        <filename>/sys/class/watchdog/watchdog<replaceable>X</replaceable>/pretimeout_available_governors</filename>
+        file. Typically, available governor types are <varname>noop</varname> and <varname>panic</varname>.
+        Availability, names and functionality might vary depending on the specific device driver
+        in use. If the <filename>pretimeout_available_governors</filename> sysfs file is empty,
+        the governor might be built as a kernel module and might need to be manually loaded
+        (e.g. <varname>pretimeout_noop.ko</varname>), or the watchdog device might not support
+        pre-timeouts.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>WatchdogDevice=</varname></term>
 
index 9d0ce35bacc70031c24137f769ad5e44333c59b3..91ff68fb5c8485d059d3cbca8b9fce2a778663a0 100644 (file)
@@ -283,6 +283,24 @@ static int property_get_pretimeout_watchdog(
         return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_PRETIMEOUT));
 }
 
+static int property_get_pretimeout_watchdog_governor(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "s", m->watchdog_pretimeout_governor);
+}
+
 static int property_get_reboot_watchdog(
                 sd_bus *bus,
                 const char *path,
@@ -360,6 +378,30 @@ static int property_set_pretimeout_watchdog(
         return property_set_watchdog(userdata, WATCHDOG_PRETIMEOUT, value);
 }
 
+static int property_set_pretimeout_watchdog_governor(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        char *governor;
+        int r;
+
+        assert(m);
+
+        r = sd_bus_message_read(value, "s", &governor);
+        if (r < 0)
+                return r;
+        if (!string_is_safe(governor))
+                return -EINVAL;
+
+        return manager_override_watchdog_pretimeout_governor(m, governor);
+}
+
 static int property_set_reboot_watchdog(
                 sd_bus *bus,
                 const char *path,
@@ -2727,6 +2769,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_error), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 0, 0),
         SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogPreUSec", "t", property_get_pretimeout_watchdog, property_set_pretimeout_watchdog, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogPreGovernor", "s", property_get_pretimeout_watchdog_governor, property_set_pretimeout_watchdog_governor, 0, 0),
         SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, 0),
         /* The following item is an obsolete alias */
         SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN),
index dd6a51dfe9135c0fba47a48f43f102c3fbb6dc3f..5009b8d85fd6d4785ece110d9fef8e817ca63188 100644 (file)
@@ -140,6 +140,7 @@ static usec_t arg_reboot_watchdog;
 static usec_t arg_kexec_watchdog;
 static usec_t arg_pretimeout_watchdog;
 static char *arg_early_core_pattern;
+static char *arg_watchdog_pretimeout_governor;
 static char *arg_watchdog_device;
 static char **arg_default_environment;
 static char **arg_manager_environment;
@@ -575,6 +576,20 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         }
                 }
 
+        } else if (proc_cmdline_key_streq(key, "systemd.watchdog_pretimeout_governor")) {
+
+                if (proc_cmdline_value_missing(key, value) || isempty(value)) {
+                        arg_watchdog_pretimeout_governor = mfree(arg_watchdog_pretimeout_governor);
+                        return 0;
+                }
+
+                if (!string_is_safe(value)) {
+                        log_warning("Watchdog pretimeout governor '%s' is not valid, ignoring.", value);
+                        return 0;
+                }
+
+                return free_and_strdup_warn(&arg_watchdog_pretimeout_governor, value);
+
         } else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) {
 
                 if (proc_cmdline_value_missing(key, value))
@@ -732,6 +747,7 @@ static int parse_config_file(void) {
                 { "Manager", "ShutdownWatchdogSec",          config_parse_watchdog_sec,          0, &arg_reboot_watchdog                   }, /* obsolete alias */
                 { "Manager", "KExecWatchdogSec",             config_parse_watchdog_sec,          0, &arg_kexec_watchdog                    },
                 { "Manager", "WatchdogDevice",               config_parse_path,                  0, &arg_watchdog_device                   },
+                { "Manager", "RuntimeWatchdogPreGovernor",   config_parse_safe_string,           0, &arg_watchdog_pretimeout_governor      },
                 { "Manager", "CapabilityBoundingSet",        config_parse_capability_set,        0, &arg_capability_bounding_set           },
                 { "Manager", "NoNewPrivileges",              config_parse_bool,                  0, &arg_no_new_privs                      },
 #if HAVE_SECCOMP
@@ -856,6 +872,7 @@ static void set_manager_defaults(Manager *m) {
 }
 
 static void set_manager_settings(Manager *m) {
+        int r;
 
         assert(m);
 
@@ -871,6 +888,9 @@ static void set_manager_settings(Manager *m) {
         manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
         manager_set_watchdog(m, WATCHDOG_KEXEC, arg_kexec_watchdog);
         manager_set_watchdog(m, WATCHDOG_PRETIMEOUT, arg_pretimeout_watchdog);
+        r = manager_set_watchdog_pretimeout_governor(m, arg_watchdog_pretimeout_governor);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set watchdog pretimeout governor to '%s', ignoring: %m", arg_watchdog_pretimeout_governor);
 
         manager_set_show_status(m, arg_show_status, "commandline");
         m->status_unit_format = arg_status_unit_format;
@@ -1618,6 +1638,7 @@ static int become_shutdown(
          * shutdown binary to repeatedly ping it.
          * Disable the pretimeout watchdog, as we do not support it from the shutdown binary. */
         (void) watchdog_setup_pretimeout(0);
+        (void) watchdog_setup_pretimeout_governor(NULL);
         r = watchdog_setup(watchdog_timer);
         watchdog_close(r < 0);
 
@@ -2473,6 +2494,7 @@ static void reset_arguments(void) {
         arg_pretimeout_watchdog = 0;
         arg_early_core_pattern = NULL;
         arg_watchdog_device = NULL;
+        arg_watchdog_pretimeout_governor = mfree(arg_watchdog_pretimeout_governor);
 
         arg_default_environment = strv_free(arg_default_environment);
         arg_manager_environment = strv_free(arg_manager_environment);
index 58063f0193c5704c1ee065484c211b45408e110e..6a5dec436f44907dc224ed35867379ad7ffbf923 100644 (file)
@@ -119,6 +119,7 @@ int manager_serialize(
         (void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]);
         (void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]);
         (void) serialize_usec(f, "pretimeout-watchdog-overridden", m->watchdog_overridden[WATCHDOG_PRETIMEOUT]);
+        (void) serialize_item(f, "pretimeout-watchdog-governor-overridden", m->watchdog_pretimeout_governor_overridden);
 
         for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
                 _cleanup_free_ char *joined = NULL;
@@ -464,6 +465,11 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 manager_override_watchdog(m, WATCHDOG_PRETIMEOUT, t);
 
+                } else if ((val = startswith(l, "pretimeout-watchdog-governor-overridden="))) {
+                        r = free_and_strdup(&m->watchdog_pretimeout_governor_overridden, val);
+                        if (r < 0)
+                                return r;
+
                 } else if (startswith(l, "env=")) {
                         r = deserialize_environment(l + 4, &m->client_environment);
                         if (r < 0)
index 117df23e3d4ccd466fba9f66efdafaaa73cad8d7..4c59506ccb511170c4db66370f1afd0a5a4d5b4c 100644 (file)
@@ -1542,6 +1542,9 @@ Manager* manager_free(Manager *m) {
                 m->prefix[dt] = mfree(m->prefix[dt]);
         free(m->received_credentials);
 
+        free(m->watchdog_pretimeout_governor);
+        free(m->watchdog_pretimeout_governor_overridden);
+
 #if BPF_FRAMEWORK
         lsm_bpf_destroy(m->restrict_fs);
 #endif
@@ -3263,6 +3266,52 @@ void manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
         m->watchdog_overridden[t] = timeout;
 }
 
+int manager_set_watchdog_pretimeout_governor(Manager *m, const char *governor) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return 0;
+
+        if (streq_ptr(m->watchdog_pretimeout_governor, governor))
+                return 0;
+
+        p = strdup(governor);
+        if (!p)
+                return -ENOMEM;
+
+        r = watchdog_setup_pretimeout_governor(governor);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(m->watchdog_pretimeout_governor, p);
+}
+
+int manager_override_watchdog_pretimeout_governor(Manager *m, const char *governor) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return 0;
+
+        if (streq_ptr(m->watchdog_pretimeout_governor_overridden, governor))
+                return 0;
+
+        p = strdup(governor);
+        if (!p)
+                return -ENOMEM;
+
+        r = watchdog_setup_pretimeout_governor(governor);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(m->watchdog_pretimeout_governor_overridden, p);
+}
+
 int manager_reload(Manager *m) {
         _unused_ _cleanup_(manager_reloading_stopp) Manager *reloading = NULL;
         _cleanup_fdset_free_ FDSet *fds = NULL;
index 285da9451ee09ebc2cd9e435c365914f84837ab0..e5d988b745652258db0daa82ed25c97d1816c335 100644 (file)
@@ -248,6 +248,8 @@ struct Manager {
 
         usec_t watchdog[_WATCHDOG_TYPE_MAX];
         usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX];
+        char *watchdog_pretimeout_governor;
+        char *watchdog_pretimeout_governor_overridden;
 
         dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX];
 
@@ -575,6 +577,8 @@ ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
 usec_t manager_get_watchdog(Manager *m, WatchdogType t);
 void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout);
 void manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout);
+int manager_set_watchdog_pretimeout_governor(Manager *m, const char *governor);
+int manager_override_watchdog_pretimeout_governor(Manager *m, const char *governor);
 
 const char* oom_policy_to_string(OOMPolicy i) _const_;
 OOMPolicy oom_policy_from_string(const char *s) _pure_;
index 7fd42452ebd232a10c3a643ee296cd1600003666..67e55f10a29c4e07f219caad02443b9a226c69b8 100644 (file)
@@ -31,6 +31,7 @@
 #NUMAMask=
 #RuntimeWatchdogSec=off
 #RuntimeWatchdogPreSec=off
+#RuntimeWatchdogPreGovernor=
 #RebootWatchdogSec=10min
 #KExecWatchdogSec=off
 #WatchdogDevice=
index aea6d68253a7cbd2ca9597a43188ab7276a25f18..1c0dfc991507779bab3530d25fb657ebd465682c 100644 (file)
@@ -22,6 +22,7 @@ static usec_t watchdog_timeout; /* 0 → close device and USEC_INFINITY → don'
 static usec_t watchdog_pretimeout; /* 0 → disable pretimeout and USEC_INFINITY → don't change pretimeout */
 static usec_t watchdog_last_ping = USEC_INFINITY;
 static bool watchdog_supports_pretimeout = false; /* Depends on kernel state that might change at runtime */
+static char *watchdog_pretimeout_governor = NULL;
 
 /* Starting from kernel version 4.5, the maximum allowable watchdog timeout is
  * UINT_MAX/1000U seconds (since internal calculations are done in milliseconds
@@ -76,6 +77,28 @@ static int get_pretimeout_governor(char **ret_gov) {
         return 0;
 }
 
+static int set_pretimeout_governor(const char *governor) {
+        _cleanup_free_ char *sys_fn = NULL;
+        int r;
+
+        if (isempty(governor))
+                return 0; /* Nothing to do */
+
+        r = get_watchdog_sysfs_path("pretimeout_governor", &sys_fn);
+        if (r < 0)
+                return r;
+
+        log_info("Watchdog: setting pretimeout_governor to '%s' via '%s'", governor, sys_fn);
+
+        r = write_string_file(sys_fn,
+                              governor,
+                              WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+
+        return r;
+}
+
 static int watchdog_set_enable(bool enable) {
         int flags = enable ? WDIOS_ENABLECARD : WDIOS_DISABLECARD;
 
@@ -194,6 +217,9 @@ static int update_pretimeout(void) {
          * might have been unloaded. */
         watchdog_supports_pretimeout = false;
 
+        /* Update the pretimeout governor as well */
+        (void) set_pretimeout_governor(watchdog_pretimeout_governor);
+
         r = get_pretimeout_governor(&governor);
         if (r < 0)
                 return log_warning_errno(r, "Watchdog: failed to read pretimeout governor: %m");
@@ -362,6 +388,13 @@ int watchdog_setup_pretimeout(usec_t timeout) {
         return update_pretimeout();
 }
 
+int watchdog_setup_pretimeout_governor(const char *governor) {
+        if (free_and_strdup(&watchdog_pretimeout_governor, governor) < 0)
+                return -ENOMEM;
+
+        return set_pretimeout_governor(watchdog_pretimeout_governor);
+}
+
 static usec_t calc_timeout(void) {
         /* Calculate the effective timeout which accounts for the watchdog
          * pretimeout if configured and supported. */
index dc259f0a32ff504d0779ce61300d27e00fe0c655..a537f8ae70dd3b12e553343eeda8d1260d5bbc9a 100644 (file)
@@ -9,6 +9,7 @@
 int watchdog_set_device(const char *path);
 int watchdog_setup(usec_t timeout);
 int watchdog_setup_pretimeout(usec_t usec);
+int watchdog_setup_pretimeout_governor(const char *governor);
 int watchdog_ping(void);
 void watchdog_close(bool disarm);
 usec_t watchdog_runtime_wait(void);
index b1890b91faed384678efcdc6fd1598294273b03c..487001b6b78ea5c74329f98564f8444c93196af2 100644 (file)
@@ -738,6 +738,7 @@ LogLocation=
 LogTarget=
 RuntimeWatchdogSec=
 RuntimeWatchdogPreSec=
+RuntimeWatchdogPreGovernor=
 ShowStatus=
 RebootWatchdogSec=
 ShutdownWatchdogSec=