--- /dev/null
+From ce57f17eff5e6a01e15341e49aa0b65dd3def486 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Wed, 21 Jan 2026 12:32:50 +0900
+Subject: [PATCH 1/2] udev: move functions
+
+Nothing changed except for the functions are moved.
+Preparation for the next commit, where worker_detach_event() will be
+used in on_worker_exit().
+
+(cherry picked from commit 59bc3e7cd6fc7bcfc1d01245ccefd10ad1d3c304)
+Upstream-Status: Backport
+Signed-off-by: Mikko Rapeli <mikko.rapeli@linaro.org>
+---
+ src/udev/udev-manager.c | 150 ++++++++++++++++++++--------------------
+ 1 file changed, 75 insertions(+), 75 deletions(-)
+
+diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c
+index 9fd7f4a6857b..5c6d354f5ba3 100644
+--- a/src/udev/udev-manager.c
++++ b/src/udev/udev-manager.c
+@@ -392,6 +392,81 @@ void manager_revert(Manager *manager) {
+ manager_kill_workers(manager, SIGTERM);
+ }
+
++static int on_worker_timeout_kill(sd_event_source *s, uint64_t usec, void *userdata) {
++ Worker *worker = ASSERT_PTR(userdata);
++ Manager *manager = ASSERT_PTR(worker->manager);
++ Event *event = ASSERT_PTR(worker->event);
++
++ (void) pidref_kill_and_sigcont(&worker->pidref, manager->config.timeout_signal);
++ worker->state = WORKER_KILLED;
++
++ log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed.", worker->pidref.pid, event->seqnum);
++ return 0;
++}
++
++static int on_worker_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
++ Worker *worker = ASSERT_PTR(userdata);
++ Event *event = ASSERT_PTR(worker->event);
++
++ log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time.", worker->pidref.pid, event->seqnum);
++ return 0;
++}
++
++static void worker_attach_event(Worker *worker, Event *event) {
++ Manager *manager = ASSERT_PTR(ASSERT_PTR(worker)->manager);
++
++ assert(event);
++ assert(event->state == EVENT_QUEUED);
++ assert(!event->worker);
++ assert(IN_SET(worker->state, WORKER_UNDEF, WORKER_IDLE));
++ assert(!worker->event);
++
++ worker->state = WORKER_RUNNING;
++ worker->event = event;
++ event->state = EVENT_RUNNING;
++ event->worker = worker;
++
++ (void) event_reset_time_relative(
++ manager->event,
++ &worker->timeout_warning_event_source,
++ CLOCK_MONOTONIC,
++ udev_warn_timeout(manager->config.timeout_usec),
++ USEC_PER_SEC,
++ on_worker_timeout_warning,
++ worker,
++ EVENT_PRIORITY_WORKER_TIMER,
++ "worker-timeout-warn",
++ /* force_reset= */ true);
++
++ (void) event_reset_time_relative(
++ manager->event,
++ &worker->timeout_kill_event_source,
++ CLOCK_MONOTONIC,
++ manager_kill_worker_timeout(manager),
++ USEC_PER_SEC,
++ on_worker_timeout_kill,
++ worker,
++ EVENT_PRIORITY_WORKER_TIMER,
++ "worker-timeout-kill",
++ /* force_reset= */ true);
++}
++
++static Event* worker_detach_event(Worker *worker) {
++ assert(worker);
++
++ Event *event = TAKE_PTR(worker->event);
++ if (event)
++ assert_se(TAKE_PTR(event->worker) == worker);
++
++ if (worker->state != WORKER_KILLED)
++ worker->state = WORKER_IDLE;
++
++ (void) event_source_disable(worker->timeout_warning_event_source);
++ (void) event_source_disable(worker->timeout_kill_event_source);
++
++ return event;
++}
++
+ static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
+ sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
+@@ -472,81 +547,6 @@ static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_
+ return 0;
+ }
+
+-static int on_worker_timeout_kill(sd_event_source *s, uint64_t usec, void *userdata) {
+- Worker *worker = ASSERT_PTR(userdata);
+- Manager *manager = ASSERT_PTR(worker->manager);
+- Event *event = ASSERT_PTR(worker->event);
+-
+- (void) pidref_kill_and_sigcont(&worker->pidref, manager->config.timeout_signal);
+- worker->state = WORKER_KILLED;
+-
+- log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed.", worker->pidref.pid, event->seqnum);
+- return 0;
+-}
+-
+-static int on_worker_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+- Worker *worker = ASSERT_PTR(userdata);
+- Event *event = ASSERT_PTR(worker->event);
+-
+- log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time.", worker->pidref.pid, event->seqnum);
+- return 0;
+-}
+-
+-static void worker_attach_event(Worker *worker, Event *event) {
+- Manager *manager = ASSERT_PTR(ASSERT_PTR(worker)->manager);
+-
+- assert(event);
+- assert(event->state == EVENT_QUEUED);
+- assert(!event->worker);
+- assert(IN_SET(worker->state, WORKER_UNDEF, WORKER_IDLE));
+- assert(!worker->event);
+-
+- worker->state = WORKER_RUNNING;
+- worker->event = event;
+- event->state = EVENT_RUNNING;
+- event->worker = worker;
+-
+- (void) event_reset_time_relative(
+- manager->event,
+- &worker->timeout_warning_event_source,
+- CLOCK_MONOTONIC,
+- udev_warn_timeout(manager->config.timeout_usec),
+- USEC_PER_SEC,
+- on_worker_timeout_warning,
+- worker,
+- EVENT_PRIORITY_WORKER_TIMER,
+- "worker-timeout-warn",
+- /* force_reset= */ true);
+-
+- (void) event_reset_time_relative(
+- manager->event,
+- &worker->timeout_kill_event_source,
+- CLOCK_MONOTONIC,
+- manager_kill_worker_timeout(manager),
+- USEC_PER_SEC,
+- on_worker_timeout_kill,
+- worker,
+- EVENT_PRIORITY_WORKER_TIMER,
+- "worker-timeout-kill",
+- /* force_reset= */ true);
+-}
+-
+-static Event* worker_detach_event(Worker *worker) {
+- assert(worker);
+-
+- Event *event = TAKE_PTR(worker->event);
+- if (event)
+- assert_se(TAKE_PTR(event->worker) == worker);
+-
+- if (worker->state != WORKER_KILLED)
+- worker->state = WORKER_IDLE;
+-
+- (void) event_source_disable(worker->timeout_warning_event_source);
+- (void) event_source_disable(worker->timeout_kill_event_source);
+-
+- return event;
+-}
+-
+ static int worker_spawn(Manager *manager, Event *event) {
+ int r;
+
+--
+2.34.1
+
--- /dev/null
+From 340d469aec6d3daff5a518149ebcdaa82485124d Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sat, 17 Jan 2026 04:14:05 +0900
+Subject: [PATCH 2/2] udev: do not build full list of dependencies
+
+Suppose the following dependent events are queued:
+
+A(1000) -> C(1002)
+ \
+ --> E(1004)
+ /
+B(1001) -> D(1003)
+
+Here, each number is the seqnum of the event, and A -> B means that
+event B depends on event A, that is, event B cannot be started until
+event A is processed.
+
+In this case, to know if event E can be started, we only need to check
+if event C and D are processed, and not necessary to check the state of
+event A or B.
+
+However, the commit e1ae931064be9483aa98249294f6e195537f43d1 introduced
+full dependency list (in the above example, all A, B, C, D are listed as
+the blocker for E), but it is overkill and most information is not necessary.
+Also, before the commit e1ae931064be9483aa98249294f6e195537f43d1, we
+found blocker from the beginning of the queued events, but that's also
+not necessary, as even A is processed, still C may be queued, and we
+anyway need to check if C is processed or not.
+
+This makes each event only stores the last blocker event for the event,
+and finds the blocker from the end of the queue. With this change, the
+memory cost can be reduced from O(n^2) to O(n). Also, as previously we
+used Set for managing blockers, but now we only increment/decrement
+the reference counter of events, so the speed should be also improved.
+
+Fixes #39583 and #39817.
+
+(cherry picked from commit cb16d47f30cc0d848ca6868c39b844810520d713)
+Upstream-Status: Backport
+Signed-off-by: Mikko Rapeli <mikko.rapeli@linaro.org>
+---
+ src/udev/udev-manager.c | 107 +++++++++++++++++++++-------------------
+ 1 file changed, 55 insertions(+), 52 deletions(-)
+
+diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c
+index 5c6d354f5ba3..6d9da33e4932 100644
+--- a/src/udev/udev-manager.c
++++ b/src/udev/udev-manager.c
+@@ -53,9 +53,14 @@ typedef enum EventState {
+ EVENT_QUEUED,
+ EVENT_RUNNING,
+ EVENT_LOCKED,
++ EVENT_PROCESSED,
+ } EventState;
+
+ typedef struct Event {
++ /* All events that have not been processed (state != EVENT_PROCESSED) are referenced by the Manager.
++ * Additionally, an event may be referenced by events blocked by this event. See event_find_blocker(). */
++ unsigned n_ref;
++
+ Manager *manager;
+ Worker *worker;
+ EventState state;
+@@ -76,9 +81,8 @@ typedef struct Event {
+ char *whole_disk;
+ LIST_FIELDS(Event, same_disk);
+
+- bool dependencies_built;
+- Set *blocker_events;
+- Set *blocking_events;
++ /* The last blocker for this event. This event must not be processed before the blocker is processed. */
++ Event *blocker;
+
+ LIST_FIELDS(Event, event);
+ } Event;
+@@ -101,21 +105,6 @@ typedef struct Worker {
+ Event *event;
+ } Worker;
+
+-static void event_clear_dependencies(Event *event) {
+- assert(event);
+-
+- Event *e;
+- while ((e = set_steal_first(event->blocker_events)))
+- assert_se(set_remove(e->blocking_events, event) == event);
+- event->blocker_events = set_free(event->blocker_events);
+-
+- while ((e = set_steal_first(event->blocking_events)))
+- assert_se(set_remove(e->blocker_events, event) == event);
+- event->blocking_events = set_free(event->blocking_events);
+-
+- event->dependencies_built = false;
+-}
+-
+ static void event_unset_whole_disk(Event *event) {
+ Manager *manager = ASSERT_PTR(ASSERT_PTR(event)->manager);
+
+@@ -140,6 +129,10 @@ static void event_unset_whole_disk(Event *event) {
+ event->whole_disk = mfree(event->whole_disk);
+ }
+
++static Event* event_free(Event *event);
++DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(Event, event, event_free);
++DEFINE_TRIVIAL_CLEANUP_FUNC(Event*, event_unref);
++
+ static Event* event_free(Event *event) {
+ if (!event)
+ return NULL;
+@@ -156,14 +149,25 @@ static Event* event_free(Event *event) {
+ if (event->worker)
+ event->worker->event = NULL;
+
+- event_clear_dependencies(event);
++ event_unref(event->blocker);
+
+ sd_device_unref(event->dev);
+
+ return mfree(event);
+ }
+
+-DEFINE_TRIVIAL_CLEANUP_FUNC(Event*, event_free);
++static Event* event_enter_processed(Event *event) {
++ if (!event)
++ return NULL;
++
++ if (event->state == EVENT_PROCESSED)
++ return NULL;
++
++ event->state = EVENT_PROCESSED;
++ return event_unref(event);
++}
++
++DEFINE_TRIVIAL_CLEANUP_FUNC(Event*, event_enter_processed);
+
+ static Worker* worker_free(Worker *worker) {
+ if (!worker)
+@@ -176,7 +180,6 @@ static Worker* worker_free(Worker *worker) {
+ sd_event_source_unref(worker->timeout_warning_event_source);
+ sd_event_source_unref(worker->timeout_kill_event_source);
+ pidref_done(&worker->pidref);
+- event_free(worker->event);
+
+ return mfree(worker);
+ }
+@@ -200,8 +203,8 @@ Manager* manager_free(Manager *manager) {
+ udev_rules_free(manager->rules);
+
+ hashmap_free(manager->workers);
+- while (manager->events)
+- event_free(manager->events);
++ LIST_FOREACH(event, event, manager->events)
++ event_enter_processed(event);
+
+ prioq_free(manager->locked_events_by_time);
+ hashmap_free(manager->locked_events_by_disk);
+@@ -469,7 +472,8 @@ static Event* worker_detach_event(Worker *worker) {
+
+ static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
+- sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
++ _cleanup_(event_enter_processedp) Event *event = worker_detach_event(worker);
++ sd_device *dev = event ? ASSERT_PTR(event->dev) : NULL;
+
+ assert(si);
+
+@@ -649,17 +653,21 @@ bool devpath_conflict(const char *a, const char *b) {
+ return *a == '/' || *b == '/' || *a == *b;
+ }
+
+-static int event_build_dependencies(Event *event) {
+- int r;
+-
++static void event_find_blocker(Event *event) {
+ assert(event);
+
+ /* lookup event for identical, parent, child device */
+
+- if (event->dependencies_built)
+- return 0;
++ if (event->blocker && event->blocker->state != EVENT_PROCESSED)
++ /* Previously found blocker is not processed yet. */
++ return;
++
++ /* If we have not found blocker yet, or the previously found blocker has been processed, let's find
++ * (another) blocker for this event. */
++ LIST_FOREACH_BACKWARDS(event, e, (event->blocker ?: event)->event_prev) {
++ if (e->state == EVENT_PROCESSED)
++ continue;
+
+- LIST_FOREACH_BACKWARDS(event, e, event->event_prev) {
+ if (!streq_ptr(event->id, e->id) &&
+ !devpath_conflict(event->devpath, e->devpath) &&
+ !devpath_conflict(event->devpath, e->devpath_old) &&
+@@ -667,22 +675,16 @@ static int event_build_dependencies(Event *event) {
+ !(event->devnode && streq_ptr(event->devnode, e->devnode)))
+ continue;
+
+- r = set_ensure_put(&event->blocker_events, NULL, e);
+- if (r < 0)
+- return r;
+-
+- r = set_ensure_put(&e->blocking_events, NULL, event);
+- if (r < 0) {
+- assert_se(set_remove(event->blocker_events, e) == e);
+- return r;
+- }
+-
+ log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
+ event->seqnum, e->seqnum);
++
++ unref_and_replace_full(event->blocker, e, event_ref, event_unref);
++ return;
+ }
+
+- event->dependencies_built = true;
+- return 0;
++ /* No new blocker is found, and if set, previously found blocker has been processed. Clear the
++ * previous blocker if set. */
++ event->blocker = event_unref(event->blocker);
+ }
+
+ static bool manager_can_process_event(Manager *manager) {
+@@ -739,14 +741,10 @@ static int event_queue_start(Manager *manager) {
+ if (event->state != EVENT_QUEUED)
+ continue;
+
+- r = event_build_dependencies(event);
+- if (r < 0)
+- log_device_warning_errno(event->dev, r,
+- "Failed to check dependencies for event (SEQNUM=%"PRIu64", ACTION=%s), ignoring: %m",
+- event->seqnum, strna(device_action_to_string(event->action)));
++ event_find_blocker(event);
+
+ /* do not start event if parent or child event is still running or queued */
+- if (!set_isempty(event->blocker_events))
++ if (event->blocker)
+ continue;
+
+ r = event_run(event);
+@@ -927,11 +925,12 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+- _cleanup_(event_freep) Event *event = new(Event, 1);
++ _cleanup_(event_unrefp) Event *event = new(Event, 1);
+ if (!event)
+ return -ENOMEM;
+
+ *event = (Event) {
++ .n_ref = 1,
+ .dev = sd_device_ref(dev),
+ .seqnum = seqnum,
+ .action = action,
+@@ -943,6 +942,9 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
+ .locked_event_prioq_index = PRIOQ_IDX_NULL,
+ };
+
++ /* The kernel sometimes sends events in a wrong order, and we may receive an event with smaller
++ * SEQNUM after one with larger SEQNUM. To workaround the issue, let's reorder events if necessary. */
++
+ Event *prev = NULL;
+ LIST_FOREACH_BACKWARDS(event, e, manager->last_event) {
+ if (e->seqnum < event->seqnum) {
+@@ -954,8 +956,9 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
+ "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);
++ /* The inserted event may be a blocker of an already queued event, hence the already found
++ * blocker may not be the last one. Let's find the last blocker again later. */
++ e->blocker = event_unref(e->blocker);
+ }
+
+ LIST_INSERT_AFTER(event, manager->events, prev, event);
+@@ -1211,7 +1214,7 @@ static int on_worker_notify(sd_event_source *s, int fd, uint32_t revents, void *
+ return 0;
+ }
+
+- _cleanup_(event_freep) Event *event = worker_detach_event(worker);
++ _cleanup_(event_enter_processedp) Event *event = worker_detach_event(worker);
+
+ if (strv_contains(l, "TRY_AGAIN=1")) {
+ /* Worker cannot lock the device. */
+--
+2.34.1
+