]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: reload rules and builtins only when mtime of a config changed
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 13 Jul 2022 02:52:37 +0000 (11:52 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 23 Jul 2022 06:05:22 +0000 (15:05 +0900)
This makes udevd reload rules only when the timestamp is updated,
even on SIGHUP or `udevadm control --reload`.
So, we can call `udevadm control --reload` without huge performance
penalty when no rules, .link files, and so on are changed.

src/udev/udev-rules.h
src/udev/udevd.c

index 704f690b676ef11119ae269d3df8b22b69705ce1..860fe7c8e4e6cc01029f7de82b063b7e8a38d535 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 #pragma once
 
+#include "alloc-util.h"
 #include "hashmap.h"
 #include "time-util.h"
 #include "udev-util.h"
@@ -21,6 +22,7 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
 int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
 UdevRules *udev_rules_free(UdevRules *rules);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
+#define udev_rules_free_and_replace(a, b) free_and_replace_full(a, b, udev_rules_free)
 
 bool udev_rules_should_reload(UdevRules *rules);
 int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
index a457ebcc9a3b182e2a19a256e5f4599bc36e4c77..0a68b7e6ac18356f7eda1c34eace8177aa449176 100644 (file)
@@ -351,16 +351,40 @@ static void notify_ready(void) {
 }
 
 /* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager) {
+static void manager_reload(Manager *manager, bool force) {
+        _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+        usec_t now_usec;
+        int r;
+
         assert(manager);
 
+        assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &now_usec) >= 0);
+        if (!force && now_usec < usec_add(manager->last_usec, 3 * USEC_PER_SEC))
+                /* check for changed config, every 3 seconds at most */
+                return;
+        manager->last_usec = now_usec;
+
+        /* Reload SELinux label database, to make the child inherit the up-to-date database. */
+        mac_selinux_maybe_reload();
+
+        /* Nothing changed. It is not necessary to reload. */
+        if (!udev_rules_should_reload(manager->rules) && !udev_builtin_validate())
+                return;
+
         sd_notify(false,
                   "RELOADING=1\n"
                   "STATUS=Flushing configuration...");
 
         manager_kill_workers(manager, false);
-        manager->rules = udev_rules_free(manager->rules);
+
         udev_builtin_exit();
+        udev_builtin_init();
+
+        r = udev_rules_load(&rules, arg_resolve_name_timing);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m");
+        else
+                udev_rules_free_and_replace(manager->rules, rules);
 
         notify_ready();
 }
@@ -937,7 +961,6 @@ no_blocker:
 }
 
 static int event_queue_start(Manager *manager) {
-        usec_t usec;
         int r;
 
         assert(manager);
@@ -945,32 +968,11 @@ static int event_queue_start(Manager *manager) {
         if (!manager->events || manager->exit || manager->stop_exec_queue)
                 return 0;
 
-        assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
-        /* check for changed config, every 3 seconds at most */
-        if (manager->last_usec == 0 ||
-            usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
-                if (udev_rules_should_reload(manager->rules) ||
-                    udev_builtin_validate())
-                        manager_reload(manager);
-
-                manager->last_usec = usec;
-        }
-
         r = event_source_disable(manager->kill_workers_event);
         if (r < 0)
                 log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
 
-        udev_builtin_init();
-
-        if (!manager->rules) {
-                r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
-                if (r < 0)
-                        return log_warning_errno(r, "Failed to read udev rules: %m");
-        }
-
-        /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
-         * and, until the next SELinux policy changes, we safe further reloads in future children */
-        mac_selinux_maybe_reload();
+        manager_reload(manager, /* force = */ false);
 
         LIST_FOREACH(event, event, manager->events) {
                 if (event->state != EVENT_QUEUED)
@@ -1252,7 +1254,7 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl
                 break;
         case UDEV_CTRL_RELOAD:
                 log_debug("Received udev control message (RELOAD)");
-                manager_reload(manager);
+                manager_reload(manager, /* force = */ true);
                 break;
         case UDEV_CTRL_SET_ENV: {
                 _unused_ _cleanup_free_ char *old_val = NULL;
@@ -1501,7 +1503,7 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void
 
         assert(manager);
 
-        manager_reload(manager);
+        manager_reload(manager, /* force = */ true);
 
         return 1;
 }
@@ -2011,6 +2013,8 @@ static int main_loop(Manager *manager) {
         if (r < 0)
                 return log_error_errno(r, "Failed to create post event source: %m");
 
+        manager->last_usec = now(CLOCK_MONOTONIC);
+
         udev_builtin_init();
 
         r = udev_rules_load(&manager->rules, arg_resolve_name_timing);