From bfde9421af1458e18999d787b1ab46a6a33e8bb6 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 6 Nov 2019 12:24:41 +0100
Subject: [PATCH 0573/1760] udevd: wait for workers to finish when exiting

On some systems with lots of devices, device probing for certain drivers can
take a very long time. If systemd-udevd detects a timeout and kills the worker
running modprobe using SIGKILL, some devices will not be probed, or end up in
unusable state. The --event-timeout option can be used to modify the maximum
time spent in an uevent handler. But if systemd-udevd exits, it uses a
different timeout, hard-coded to 30s, and exits when this timeout expires,
causing all workers to be KILLed by systemd afterwards. In practice, this may
lead to workers being killed after significantly less time than specified with
the event-timeout. This is particularly significant during initrd processing:
systemd-udevd will be stopped by systemd when initrd-switch-root.target is
about to be isolated, which usually happens quickly after finding and mounting
the root FS.

If systemd-udevd is started by PID 1 (i.e. basically always), systemd will
kill both udevd and the workers after expiry of TimeoutStopSec. This is
actually better than the built-in udevd timeout, because it's more transparent
and configurable for users. This way users can avoid the mentioned boot problem
by simply increasing StopTimeoutSec= in systemd-udevd.service.

If udevd is not started by systemd (standalone), this is still an
improvement. udevd will kill hanging workers when the event timeout is
reached, which is configurable via the udev.event_timeout= kernel
command line parameter. Before this patch, udevd would simply exit with
workers still running, which would then become zombie processes.

With the timeout removed, the sd_event_now() assertion in manager_exit() can be
dropped.

NOTE: This patch is modified to fit current code. The first part (some NEWS) has
been removed, and this modification will not affect functionality.

https://github.com/systemd/systemd/pull/13961/commits/bfde9421af1458e18999d787b1ab46a6a33e8bb6
Reference: https://github.com/systemd/systemd/commit/bfde9421af1458e18999d787b1ab46a6a33e8bb6
Conflict: NA
---
 NEWS             | 13 +++++++++++++
 src/udev/udevd.c | 21 ---------------------
 2 files changed, 13 insertions(+), 21 deletions(-)

diff --git a/NEWS b/NEWS
index de4f93a..aabafb9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,18 @@
 systemd System and Service Manager
 
+CHANGES WITH 244(We backported this to V243):
+
+        * systemd-udevd: removed the 30s timeout for killing stale workers on
+          exit. systemd-udevd now waits for workers to finish. The hard-coded
+          exit timeout of 30s was too short for some large installations, where
+          driver initialization could be prematurely interrupted during initrd
+          processing if the root file system had been mounted and init was
+          preparing to switch root. If udevd is run without systemd and workers
+          are hanging while udevd receives an exit signal, udevd will now exit
+          when udev.event_timeout is reached for the last hanging worker. With
+          systemd, the exit timeout can additionally be configured using
+          TimeoutStopSec= in systemd-udevd.service.
+
 CHANGES WITH 243:
 
         * This release enables unprivileged programs (i.e. requiring neither
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index cb51230..0a2c8a2 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -777,21 +777,7 @@ set_delaying_seqnum:
         return true;
 }
 
-static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        Manager *manager = userdata;
-
-        assert(manager);
-
-        log_error("Giving up waiting for workers to finish.");
-        sd_event_exit(manager->event, -ETIMEDOUT);
-
-        return 1;
-}
-
 static void manager_exit(Manager *manager) {
-        uint64_t usec;
-        int r;
-
         assert(manager);
 
         manager->exit = true;
@@ -811,13 +797,6 @@ static void manager_exit(Manager *manager) {
         /* discard queued events and kill workers */
         event_queue_cleanup(manager, EVENT_QUEUED);
         manager_kill_workers(manager);
-
-        assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
-
-        r = sd_event_add_time(manager->event, NULL, CLOCK_MONOTONIC,
-                              usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
-        if (r < 0)
-                return;
 }
 
 /* reload requested, HUP signal received, rules changed, builtin changed */
-- 
2.19.1

