diff --git a/src/udev/udevadm-wait.c b/src/udev/udevadm-wait.c index d9692ab86e..544c94616d 100644 --- a/src/udev/udevadm-wait.c +++ b/src/udev/udevadm-wait.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "chase-symlinks.h" +#include "device-monitor-private.h" #include "device-util.h" #include "errno-util.h" #include "event-util.h" @@ -165,28 +166,21 @@ static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, if (path_strv_contains(arg_devices, name)) return check_and_exit(sd_device_monitor_get_event(monitor)); - STRV_FOREACH(p, arg_devices) { - const char *link; - - if (!path_startswith(*p, "/dev")) - continue; - - FOREACH_DEVICE_DEVLINK(device, link) - if (path_equal(*p, link)) - return check_and_exit(sd_device_monitor_get_event(monitor)); - } + FOREACH_DEVICE_DEVLINK(device, name) + if (path_strv_contains(arg_devices, name)) + return check_and_exit(sd_device_monitor_get_event(monitor)); return 0; } -static int setup_monitor(sd_event *event, sd_device_monitor **ret) { +static int setup_monitor(sd_event *event, MonitorNetlinkGroup group, const char *description, sd_device_monitor **ret) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; int r; assert(event); assert(ret); - r = sd_device_monitor_new(&monitor); + r = device_monitor_new_full(&monitor, group, /* fd = */ -1); if (r < 0) return r; @@ -196,12 +190,11 @@ static int setup_monitor(sd_event *event, sd_device_monitor **ret) { if (r < 0) return r; - r = sd_device_monitor_start(monitor, device_monitor_handler, NULL); + r = sd_device_monitor_set_description(monitor, description); if (r < 0) return r; - r = sd_event_source_set_description(sd_device_monitor_get_event_source(monitor), - "device-monitor-event-source"); + r = sd_device_monitor_start(monitor, device_monitor_handler, NULL); if (r < 0) return r; @@ -257,6 +250,7 @@ static int setup_timer(sd_event *event) { static int reset_timer(sd_event *e, sd_event_source **s); static int on_periodic_timer(sd_event_source *s, uint64_t usec, void *userdata) { + static unsigned counter = 0; sd_event *e; int r; @@ -264,9 +258,16 @@ static int on_periodic_timer(sd_event_source *s, uint64_t usec, void *userdata) e = sd_event_source_get_event(s); - r = check_and_exit(e); - if (r != 0) - return r; + /* Even if all devices exists, we try to wait for uevents to be emitted from kernel. */ + if (check()) + counter++; + else + counter = 0; + + if (counter >= 2) { + log_debug("All requested devices popped up without receiving kernel uevents."); + return sd_event_exit(e, 0); + } r = reset_timer(e, &s); if (r < 0) @@ -286,24 +287,15 @@ static int setup_periodic_timer(sd_event *event) { assert(event); - /* If --initialized=no is specified, it is not necessary to wait uevents for the specified devices to - * be processed by udevd. Let's periodically check the devices. Then, we may be able to finish this - * program earlier when udevd is very busy. - * - * This is useful for working around issues #24360 and #24450. - * For some reasons, the kernel sometimes does not emit uevents for loop block device on attach. - * Hence, without the periodic timer, no event source for this program will be triggered, and this - * will be timed out. - * Theoretically, inotify watch may be better, but this program typically expected to run in a short - * time. Hence, let's use the simpler periodic timer event source here. */ - - if (arg_wait_until != WAIT_UNTIL_ADDED) - return 0; - r = reset_timer(event, &s); if (r < 0) return r; + /* Set the lower priority than device monitor, to make uevents always dispatched first. */ + r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_NORMAL + 1); + if (r < 0) + return r; + return sd_event_source_set_floating(s, true); } @@ -388,7 +380,7 @@ static int parse_argv(int argc, char *argv[]) { } int wait_main(int argc, char *argv[], void *userdata) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *udev_monitor = NULL, *kernel_monitor = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; int r; @@ -424,13 +416,32 @@ int wait_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to set up inotify: %m"); - r = setup_monitor(event, &monitor); + r = setup_monitor(event, MONITOR_GROUP_UDEV, "udev-uevent-monitor-event-source", &udev_monitor); if (r < 0) - return log_error_errno(r, "Failed to set up device monitor: %m"); + return log_error_errno(r, "Failed to set up udev uevent monitor: %m"); - r = setup_periodic_timer(event); - if (r < 0) - return log_error_errno(r, "Failed to set up periodic timer: %m"); + if (arg_wait_until == WAIT_UNTIL_ADDED) { + /* If --initialized=no is specified, it is not necessary to wait uevents for the specified + * devices to be processed by udevd. Hence, let's listen on the kernel's uevent stream. Then, + * we may be able to finish this program earlier when udevd is very busy. + * Note, we still need to also setup udev monitor, as this may be invoked with a devlink + * (e.g. /dev/disk/by-id/foo). In that case, the devlink may not exist when we received a + * uevent from kernel, as the udevd may not finish to process the uevent yet. Hence, we need + * to wait until the event is processed by udevd. */ + r = setup_monitor(event, MONITOR_GROUP_KERNEL, "kernel-uevent-monitor-event-source", &kernel_monitor); + if (r < 0) + return log_error_errno(r, "Failed to set up kernel uevent monitor: %m"); + + /* This is a workaround for issues #24360 and #24450. + * For some reasons, the kernel sometimes does not emit uevents for loop block device on + * attach. Hence, without the periodic timer, no event source for this program will be + * triggered, and this will be timed out. + * Theoretically, inotify watch may be better, but this program typically expected to run in + * a short time. Hence, let's use the simpler periodic timer event source here. */ + r = setup_periodic_timer(event); + if (r < 0) + return log_error_errno(r, "Failed to set up periodic timer: %m"); + } /* Check before entering the event loop, as devices may be initialized during setting up event sources. */ if (check())