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(