From: Rocker Zhang Date: Sat, 6 Jun 2026 07:29:43 +0000 (+0000) Subject: generator: order cryptsetup/verity/integrity after systemd-udevd X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=66a0bdad4f03f0c260ea2ba409f7b7c3884309fe;p=thirdparty%2Fsystemd.git generator: order cryptsetup/verity/integrity after systemd-udevd During soft-reboot teardown, systemd-cryptsetup@.service's ExecStop runs libcryptsetup's crypt_deactivate(), which issues DM_REMOVE with a udev cookie and blocks in dm_udev_wait() until 95-dm-notify.rules decrements the cookie semaphore via "dmsetup udevcomplete $env{DM_COOKIE}". The generated cryptsetup unit had only After=systemd-udevd-kernel.socket, not After=systemd-udevd.service. The .socket has IgnoreOnIsolate=yes and is not stopped during soft-reboot, so it provides no ordering at all for the service teardown. Meanwhile systemd-udevd.service has Conflicts=soft-reboot.target (added in 0d1819e791) and stops as soon as soft-reboot.target starts, with no ordering relative to cryptsetup's ExecStop. Once udevd's device monitor event source is disabled in manager_exit(), pending DM_REMOVE uevents are no longer processed and the cookie semaphore stays at 1 forever, blocking soft-reboot at "Stopping Cryptography Setup..." until JobTimeoutSec=30min on soft-reboot.target fires. Add systemd-udevd.service to the After= ordering of the generated cryptsetup, veritysetup and integritysetup units. By systemd's job ordering rules (job_compare() in src/core/job.c), when two units are both being stopped and one has After= the other, the After= unit is stopped first. So with cryptsetup@*.service After=systemd-udevd.service, cryptsetup stops first (cookie acknowledged by the still-live udevd), then udevd stops. Putting After=umount.target on systemd-udevd.service does not work: at soft-reboot, udevd's stop job runs concurrently with umount.target's start job, and JOB_STOP unconditionally precedes JOB_START in the transaction (see src/core/job.c:1742). The ordering has to be expressed between two stop jobs, which is what putting After=systemd-udevd.service on the dm consumers achieves. Fixes: #40298 --- diff --git a/src/integritysetup/integritysetup-generator.c b/src/integritysetup/integritysetup-generator.c index ab01873cdd1..ce7a8ce1789 100644 --- a/src/integritysetup/integritysetup-generator.c +++ b/src/integritysetup/integritysetup-generator.c @@ -80,7 +80,11 @@ static int create_disk( "SourcePath=%s\n" "DefaultDependencies=no\n" "IgnoreOnIsolate=true\n" - "After=integritysetup-pre.target systemd-udevd-kernel.socket\n" + /* The systemd-udevd.service ordering is mostly about shutdown, not startup: on stop the dm + * device is detached via an ioctl carrying a udev cookie that blocks in dm_udev_wait() until + * udev releases it (95-dm-notify.rules). Stopping before udevd keeps it around to service the + * cookie; otherwise during soft-reboot/shutdown udevd may exit first and the detach hangs. */ + "After=integritysetup-pre.target systemd-udevd-kernel.socket systemd-udevd.service\n" "Before=blockdev@dev-mapper-%%i.target\n" "Wants=blockdev@dev-mapper-%%i.target\n" "Conflicts=umount.target\n" diff --git a/src/shared/generator.c b/src/shared/generator.c index 36fdff5263b..fa5bd25f1f8 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -921,7 +921,12 @@ int generator_write_cryptsetup_unit_section(FILE *f, const char *source) { fprintf(f, "\n" "DefaultDependencies=no\n" - "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n" + /* The ordering against systemd-udevd.service is mostly about shutdown, not startup: on stop + * the device is detached via a device-mapper ioctl that carries a udev cookie, and we block in + * dm_udev_wait() until udev releases it (see 95-dm-notify.rules). Ordering After= it means we + * are stopped first, while udevd is still around to service that cookie; otherwise during + * soft-reboot/shutdown udevd may exit first and the detach hangs until the job timeout. */ + "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-udevd.service systemd-tpm2-setup-early.service\n" "Before=blockdev@dev-mapper-%%i.target\n" "Wants=blockdev@dev-mapper-%%i.target\n" "IgnoreOnIsolate=true\n"); @@ -993,7 +998,11 @@ int generator_write_veritysetup_unit_section(FILE *f, const char *source) { fprintf(f, "DefaultDependencies=no\n" "IgnoreOnIsolate=true\n" - "After=veritysetup-pre.target systemd-udevd-kernel.socket\n" + /* The systemd-udevd.service ordering is mostly about shutdown, not startup: on stop the dm + * device is detached via an ioctl carrying a udev cookie that blocks in dm_udev_wait() until + * udev releases it (95-dm-notify.rules). Stopping before udevd keeps it around to service the + * cookie; otherwise during soft-reboot/shutdown udevd may exit first and the detach hangs. */ + "After=veritysetup-pre.target systemd-udevd-kernel.socket systemd-udevd.service\n" "Before=blockdev@dev-mapper-%%i.target\n" "Wants=blockdev@dev-mapper-%%i.target\n");