From 95b40e4cf31ce38d36010526403c1bdb27788d7e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 31 Jan 2026 00:15:29 +0900 Subject: [PATCH 1/3] udev/watch: drop redundant argument The arguments dev and target are always same. --- src/udev/udev-watch.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 10cbc84846..bebe575dfb 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -141,21 +141,20 @@ void udev_watch_dump(void) { } } -static int synthesize_change_one(sd_device *dev, sd_device *target) { +static int synthesize_change_one(sd_device *dev) { int r; assert(dev); - assert(target); if (DEBUG_LOGGING) { const char *syspath = NULL; - (void) sd_device_get_syspath(target, &syspath); + (void) sd_device_get_syspath(dev, &syspath); log_device_debug(dev, "device is closed, synthesising 'change' on %s", strna(syspath)); } - r = sd_device_trigger(target, SD_DEVICE_CHANGE); + r = sd_device_trigger(dev, SD_DEVICE_CHANGE); if (r < 0) - return log_device_debug_errno(target, r, "Failed to trigger 'change' uevent: %m"); + return log_device_debug_errno(dev, r, "Failed to trigger 'change' uevent: %m"); DEVICE_TRACE_POINT(synthetic_change_event, dev); @@ -180,13 +179,13 @@ static int synthesize_change(Manager *manager, sd_device *dev) { if (r < 0) return r; if (r > 0) - return synthesize_change_one(dev, dev); + return synthesize_change_one(dev); r = block_device_is_whole_disk(dev); if (r < 0) return r; if (r == 0) - return synthesize_change_one(dev, dev); + return synthesize_change_one(dev); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; r = pidref_safe_fork( From b7363d33302fa86fe46e03b500e729fea3277ed7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 31 Jan 2026 00:47:31 +0900 Subject: [PATCH 2/3] 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. --- src/udev/udev-manager.c | 10 ++++++++ src/udev/udev-manager.h | 2 ++ src/udev/udev-watch.c | 57 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index adbc465571..a62af149db 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 e0160e237c..6c4469ce73 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 bebe575dfb..3f4aa37dec 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( From dc709eff0bc956df7f1c311006b319eae8bc8f1f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 30 Jan 2026 20:32:15 +0900 Subject: [PATCH 3/3] udev: create /run/udev/queue file earlier When udevd received a uevent or inotify event, we will queue or trigger synthesized events. To minimize the race between processing these events by udevd and user invocation of 'udevadm settle', let's create /run/udev/queue file earlier. On some errors, no event may be queued, but in that case, the file will be removed by the post event source if nothing queued. See on_post(). Hopefully mitigate the issue #40499. --- src/udev/udev-manager.c | 29 +++++++++++++++++++++-------- src/udev/udev-manager.h | 2 ++ src/udev/udev-watch.c | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index a62af149db..2e52a367a8 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -969,14 +969,6 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { TAKE_PTR(event); log_device_uevent(dev, "Device is queued"); - if (!manager->queue_file_created) { - r = touch("/run/udev/queue"); - if (r < 0) - log_warning_errno(r, "Failed to touch /run/udev/queue, ignoring: %m"); - else - manager->queue_file_created = true; - } - return 0; } @@ -1075,6 +1067,9 @@ static int manager_deserialize_events(Manager *manager, int *fd) { n++; } + if (n > 0) + (void) manager_create_queue_file(manager); + log_debug("Deserialized %"PRIu64" events.", n); return 0; } @@ -1085,6 +1080,8 @@ static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) DEVICE_TRACE_POINT(kernel_uevent_received, dev); + (void) manager_create_queue_file(manager); + device_ensure_usec_initialized(dev, NULL); r = event_queue_insert(manager, dev); @@ -1263,6 +1260,22 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void return 1; } +int manager_create_queue_file(Manager *manager) { + int r; + + assert(manager); + + if (manager->queue_file_created) + return 0; + + r = touch("/run/udev/queue"); + if (r < 0) + return log_warning_errno(r, "Failed to touch /run/udev/queue: %m"); + + manager->queue_file_created = true; + return 0; +} + static int manager_unlink_queue_file(Manager *manager) { assert(manager); diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 6c4469ce73..2eaaf5b0c3 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -93,3 +93,5 @@ int manager_reset_kill_workers_timer(Manager *manager); bool devpath_conflict(const char *a, const char *b); int manager_requeue_locked_events_by_device(Manager *manager, sd_device *dev); + +int manager_create_queue_file(Manager *manager); diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 3f4aa37dec..7cb34edbf6 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -290,6 +290,7 @@ static int manager_process_inotify(Manager *manager, const struct inotify_event log_device_debug(dev, "Received inotify event of watch handle %i.", e->wd); + (void) manager_create_queue_file(manager); (void) manager_requeue_locked_events_by_device(manager, dev); (void) synthesize_change(manager, dev); return 0;