From: Yu Watanabe Date: Fri, 30 Jan 2026 15:47:31 +0000 (+0900) Subject: udev: remember UUIDs of triggered events X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b7363d33302fa86fe46e03b500e729fea3277ed7;p=thirdparty%2Fsystemd.git udev: remember UUIDs of triggered events 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. --- diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index adbc4655712..a62af149db8 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -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) diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index e0160e237ca..6c4469ce735 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -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; diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index bebe575dfb6..3f4aa37dec9 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -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(