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:
+31
-8
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user