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
"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"
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");
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");