udev: create /run/udev/queue file earlier (#40518)

When udevd received a uevent or inotify event, then we will queue
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.
This commit is contained in:
Yu Watanabe
2026-02-03 23:36:28 +09:00
committed by GitHub
3 changed files with 91 additions and 15 deletions
+31 -8
View 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)
@@ -962,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;
}
@@ -1068,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;
}
@@ -1078,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);
@@ -1256,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);
@@ -1265,6 +1285,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)
+4
View 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;
@@ -91,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);
+56 -7
View 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,24 +143,70 @@ void udev_watch_dump(void) {
}
}
static int synthesize_change_one(sd_device *dev, sd_device *target) {
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);
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);
sd_id128_t uuid;
r = sd_device_trigger_with_uuid(dev, SD_DEVICE_CHANGE, &uuid);
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);
/* 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;
}
@@ -180,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, 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, dev);
return synthesize_change_one(manager, dev);
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = pidref_safe_fork(
@@ -242,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;