]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: sort queued events by their seqnum 37314/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 26 Apr 2025 06:37:25 +0000 (15:37 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 9 May 2025 17:44:54 +0000 (02:44 +0900)
Unfortunately, the kernel may send events in a random order:
```
[   25.769624] systemd-udevd[194]: sdi7: Device is queued (SEQNUM=2843, ACTION=add)
[   25.769893] systemd-udevd[194]: sda5: Device is queued (SEQNUM=2842, ACTION=add)
[   25.770517] systemd-udevd[194]: sdi8: Device is queued (SEQNUM=2844, ACTION=add)
```
As you can see, udevd receives the event with SEQNUM=2843 earlier than
one with SEQNUM=2842.

Let's make queued events sorted, as our logic of determining which event
is ready for being processed assumes that queued events are sorted.
See event_build_dependencies().

Also, refuse to queue an event if another event with the same seqnum is
already queued.

src/udev/udev-manager.c

index 38ab906af200b21f0850ca6bad2592d4b99907bc..fe26496618d4ec90086bfd09cb9f0efd0a3eb243 100644 (file)
@@ -930,8 +930,28 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
                 .locked_event_prioq_index = PRIOQ_IDX_NULL,
         };
 
-        LIST_INSERT_AFTER(event, manager->events, manager->last_event, event);
-        manager->last_event = event;
+        Event *prev = NULL;
+        LIST_FOREACH_BACKWARDS(event, e, manager->last_event) {
+                if (e->seqnum < event->seqnum) {
+                        prev = e;
+                        break;
+                }
+                if (e->seqnum == event->seqnum)
+                        return log_device_warning_errno(dev, SYNTHETIC_ERRNO(EALREADY),
+                                                        "The event (SEQNUM=%"PRIu64") has been already queued.",
+                                                        event->seqnum);
+
+                /* Inserting an event in an earlier place may change dependency tree. Let's rebuild it later. */
+                event_clear_dependencies(e);
+        }
+
+        LIST_INSERT_AFTER(event, manager->events, prev, event);
+        if (prev == manager->last_event)
+                manager->last_event = event;
+        else
+                log_device_debug(dev, "Unordered event is received (last queued event seqnum=%"PRIu64", newly received event seqnum=%"PRIu64"), reordering.",
+                                 manager->last_event->seqnum, event->seqnum);
+
         event->manager = manager;
         TAKE_PTR(event);
         log_device_uevent(dev, "Device is queued");