]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: remember UUIDs of triggered events
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 30 Jan 2026 15:47:31 +0000 (00:47 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 3 Feb 2026 12:25:01 +0000 (21:25 +0900)
Otherwise, /run/udev/queue file may be removed before udevd receives
the triggered uevents. This avoids the following race:

1. uevent A is being processed,
2. received an inotify event and trigger a synthesized uevent,
3. uevent A has been processed,
4. the uevent queue becomes empty, and /run/udev/queue is removed,
5. received and queued the synthesized uevent, and /run/udev/queue is recreated.

With this patch, /run/udev/queue is not removed in step 4. Hence,
'udevadm settle' will wait for the synthesized event being processed.

This is similar to waiting for child processes that reread partitions
(thus trigger synthetic events) being terminated, but for synthetic
events triggered by the manager.

src/udev/udev-manager.c
src/udev/udev-manager.h
src/udev/udev-watch.c

index adbc465571218b31ab2b40ecfa630cdc23582579..a62af149db8f165528025b666ad99f139d00cf83 100644 (file)
@@ -218,6 +218,8 @@ Manager* manager_free(Manager *manager) {
 
         sd_event_source_unref(manager->inotify_event);
         set_free(manager->synthesize_change_child_event_sources);
+        set_free(manager->synthesized_events);
+        sd_event_source_unref(manager->synthesized_events_clear_event_source);
         sd_event_source_unref(manager->kill_workers_event);
         sd_event_unref(manager->event);
 
@@ -895,6 +897,11 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
         assert(manager);
         assert(dev);
 
+        /* If the event is the one we triggered, remove the UUID from the list. */
+        sd_id128_t uuid;
+        if (sd_device_get_trigger_uuid(dev, &uuid) >= 0)
+                free(set_remove(manager->synthesized_events, &uuid));
+
         /* We only accepts devices received by device monitor. */
         r = sd_device_get_seqnum(dev, &seqnum);
         if (r < 0)
@@ -1265,6 +1272,9 @@ static int manager_unlink_queue_file(Manager *manager) {
         if (!set_isempty(manager->synthesize_change_child_event_sources))
                 return 0; /* There are child processes that should trigger synthetic events. */
 
+        if (!set_isempty(manager->synthesized_events))
+                return 0; /* We have triggered synthesized change events. */
+
         /* There are no queued events. Let's remove /run/udev/queue and clean up the idle processes. */
         if (unlink("/run/udev/queue") < 0) {
                 if (errno != ENOENT)
index e0160e237ca673ca6b283279541b70bdc7e1c9c8..6c4469ce735b31469e19a026f0197acfe5bdece2 100644 (file)
@@ -54,6 +54,8 @@ typedef struct Manager {
         int inotify_fd;
         sd_event_source *inotify_event;
         Set *synthesize_change_child_event_sources;
+        Set *synthesized_events;
+        sd_event_source *synthesized_events_clear_event_source;
 
         sd_event_source *kill_workers_event;
 
index bebe575dfb6f3412760396b5c0360a9d6a24e9b5..3f4aa37dec9af996aaf56a1cfc98447051997fef 100644 (file)
@@ -18,6 +18,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "id128-util.h"
 #include "inotify-util.h"
 #include "parse-util.h"
 #include "pidref.h"
@@ -28,6 +29,7 @@
 #include "signal-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
+#include "time-util.h"
 #include "udev-manager.h"
 #include "udev-trace.h"
 #include "udev-util.h"
@@ -141,9 +143,23 @@ void udev_watch_dump(void) {
         }
 }
 
-static int synthesize_change_one(sd_device *dev) {
+static int on_synthesized_events_clear(sd_event_source *s, uint64_t usec, void *userdata) {
+        Manager *manager = ASSERT_PTR(userdata);
+
+        for (;;) {
+                _cleanup_free_ sd_id128_t *uuid = set_steal_first(manager->synthesized_events);
+                if (!uuid)
+                        return 0;
+
+                log_warning("Could not receive synthesized event with UUID %s, ignoring.",
+                            SD_ID128_TO_STRING(*uuid));
+        }
+}
+
+static int synthesize_change_one(Manager *manager, sd_device *dev) {
         int r;
 
+        assert(manager);
         assert(dev);
 
         if (DEBUG_LOGGING) {
@@ -152,12 +168,45 @@ static int synthesize_change_one(sd_device *dev) {
                 log_device_debug(dev, "device is closed, synthesising 'change' on %s", strna(syspath));
         }
 
-        r = sd_device_trigger(dev, SD_DEVICE_CHANGE);
+        sd_id128_t uuid;
+        r = sd_device_trigger_with_uuid(dev, SD_DEVICE_CHANGE, &uuid);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to trigger 'change' uevent: %m");
 
         DEVICE_TRACE_POINT(synthetic_change_event, dev);
 
+        /* Avoid /run/udev/queue file being removed by on_post(). */
+        sd_id128_t *copy = newdup(sd_id128_t, &uuid, 1);
+        if (!copy)
+                return log_oom_debug();
+
+        /* Let's not wait for too many events, to make not udevd consume huge amount of memory.
+         * Typically (but unfortunately, not always), the kernel provides events in the order we triggered.
+         * Hence, remembering the newest UUID should be mostly enough. */
+        while (set_size(manager->synthesized_events) >= 1024) {
+                _cleanup_free_ sd_id128_t *id = ASSERT_PTR(set_steal_first(manager->synthesized_events));
+                log_debug("Too many synthesized events are waiting, forgetting synthesized event with UUID %s.",
+                          SD_ID128_TO_STRING(*id));
+        }
+
+        r = set_ensure_consume(&manager->synthesized_events, &id128_hash_ops_free, copy);
+        if (r < 0)
+                return log_oom_debug();
+
+        r = event_reset_time_relative(
+                        manager->event,
+                        &manager->synthesized_events_clear_event_source,
+                        CLOCK_MONOTONIC,
+                        1 * USEC_PER_MINUTE,
+                        USEC_PER_SEC,
+                        on_synthesized_events_clear,
+                        manager,
+                        SD_EVENT_PRIORITY_NORMAL,
+                        "synthesized-events-clear",
+                        /* force_reset= */ true);
+        if (r < 0)
+                log_debug_errno(r, "Failed to reset timer event source for clearling synthesized event UUIDs: %m");
+
         return 0;
 }
 
@@ -179,13 +228,13 @@ static int synthesize_change(Manager *manager, sd_device *dev) {
         if (r < 0)
                 return r;
         if (r > 0)
-                return synthesize_change_one(dev);
+                return synthesize_change_one(manager, dev);
 
         r = block_device_is_whole_disk(dev);
         if (r < 0)
                 return r;
         if (r == 0)
-                return synthesize_change_one(dev);
+                return synthesize_change_one(manager, dev);
 
         _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
         r = pidref_safe_fork(