]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: certainly restart event for previously locked device
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 25 Aug 2022 15:16:17 +0000 (00:16 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 25 Aug 2022 15:40:47 +0000 (00:40 +0900)
If udevd receives a uevent for a locked block device, then the event
is requeued. However, the queued event will be processed only when at
least one sd_event_source is processed. Hence, if udevd has no event
under processing, or receives no new uevent, etc., then the requeued
event will be never processed.

Follow-up for 400e3d21f8cae53a8ba9f9567f244fbf6f3e076c.

Fixes #24439.

src/udev/udevd.c

index 01162bc7b601ce41c2a20faaef52a2023d1502d8..bbda015250330ab8889726841a4689871f43479f 100644 (file)
@@ -135,8 +135,11 @@ typedef struct Event {
         const char *devpath;
         const char *devpath_old;
         const char *devnode;
+
+        /* Used when the device is locked by another program. */
         usec_t retry_again_next_usec;
         usec_t retry_again_timeout_usec;
+        sd_event_source *retry_event_source;
 
         sd_event_source *timeout_warning_event;
         sd_event_source *timeout_event;
@@ -186,6 +189,7 @@ static Event *event_free(Event *event) {
 
         /* Do not use sd_event_source_disable_unref() here, as this is called by both workers and the
          * main process. */
+        sd_event_source_unref(event->retry_event_source);
         sd_event_source_unref(event->timeout_warning_event);
         sd_event_source_unref(event->timeout_event);
 
@@ -843,6 +847,8 @@ static int event_run(Event *event) {
 
         log_device_uevent(event->dev, "Device ready for processing");
 
+        (void) event_source_disable(event->retry_event_source);
+
         manager = event->manager;
         HASHMAP_FOREACH(worker, manager->workers) {
                 if (worker->state != WORKER_IDLE)
@@ -999,6 +1005,11 @@ static int event_queue_start(Manager *manager) {
         return 0;
 }
 
+static int on_event_retry(sd_event_source *s, uint64_t usec, void *userdata) {
+        /* This does nothing. The on_post() callback will start the event if there exists an idle worker. */
+        return 1;
+}
+
 static int event_requeue(Event *event) {
         usec_t now_usec;
         int r;
@@ -1029,6 +1040,15 @@ static int event_requeue(Event *event) {
         if (event->retry_again_timeout_usec == 0)
                 event->retry_again_timeout_usec = usec_add(now_usec, EVENT_RETRY_TIMEOUT_USEC);
 
+        r = event_reset_time_relative(event->manager->event, &event->retry_event_source,
+                                      CLOCK_MONOTONIC, EVENT_RETRY_INTERVAL_USEC, 0,
+                                      on_event_retry, NULL,
+                                      0, "retry-event", true);
+        if (r < 0)
+                return log_device_warning_errno(event->dev, r, "Failed to reset timer event source for retrying event, "
+                                                "skipping event (SEQNUM=%"PRIu64", ACTION=%s): %m",
+                                                event->seqnum, strna(device_action_to_string(event->action)));
+
         if (event->worker && event->worker->event == event)
                 event->worker->event = NULL;
         event->worker = NULL;