diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index adbc465571..2e52a367a8 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) @@ -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) diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index e0160e237c..2eaaf5b0c3 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; @@ -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); diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 10cbc84846..7cb34edbf6 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,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;