From d8982300d8f3cb37533ce5d04bf88be476469b95 Mon Sep 17 00:00:00 2001 From: Jan Macku Date: Tue, 12 May 2026 13:35:20 +0200 Subject: [PATCH] systemd-252-70 Resolves: RHEL-127425, RHEL-3631, RHEL-25518, RHEL-166187, RHEL-164539 --- ...t-s-dropin-directories-from-global-c.patch | 42 ++ ...istent-storage-rules-for-rbd-devices.patch | 41 ++ ...introduce-naming-scheme-for-RHEL-9.9.patch | 55 +++ ...support-swap-on-network-block-device.patch | 322 ++++++++++++ ...tart-limit-counter-only-when-we-can-.patch | 460 ++++++++++++++++++ ...t-for-systemd-resolved-being-stopped.patch | 38 ++ 1342-test-extend-start-limit-interval.patch | 40 ++ systemd.spec | 18 +- 8 files changed, 1015 insertions(+), 1 deletion(-) create mode 100644 1336-core-cleanup-unit-s-dropin-directories-from-global-c.patch create mode 100644 1337-udev-allow-persistent-storage-rules-for-rbd-devices.patch create mode 100644 1338-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch create mode 100644 1339-fstab-generator-support-swap-on-network-block-device.patch create mode 100644 1340-core-increment-start-limit-counter-only-when-we-can-.patch create mode 100644 1341-TEST-07-PID1-wait-for-systemd-resolved-being-stopped.patch create mode 100644 1342-test-extend-start-limit-interval.patch diff --git a/1336-core-cleanup-unit-s-dropin-directories-from-global-c.patch b/1336-core-cleanup-unit-s-dropin-directories-from-global-c.patch new file mode 100644 index 0000000..d6ba4a7 --- /dev/null +++ b/1336-core-cleanup-unit-s-dropin-directories-from-global-c.patch @@ -0,0 +1,42 @@ +From 4843d4f679bb4dd70f15ce49f0786e80bb821e28 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 25 Feb 2026 19:45:55 +0100 +Subject: [PATCH] core: cleanup unit's dropin directories from global cache + +When user creates dropin files via API (e.g. systemctl set-property ...) +we put the dropin directory path into unit_path_cache. Drop those +directories from the cache in unit_free() and prevent memory leak. + +Follow-up for fce94c5c563b8f6ede2b8f7f283d2d2faff4e062. + +(cherry picked from commit 0c98e432d1def1e8428dbead50dc629ed0645366) + +Resolves: RHEL-127425 +--- + src/core/unit.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 7f321c911d..790f4023a2 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -603,6 +603,8 @@ static void unit_remove_transient(Unit *u) { + if (!u->transient) + return; + ++ const char *dropin_directory = strjoina(u->id, ".d"); ++ + STRV_FOREACH(i, u->dropin_paths) { + _cleanup_free_ char *p = NULL, *pp = NULL; + +@@ -616,6 +618,10 @@ static void unit_remove_transient(Unit *u) { + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + ++ /* Drop the transient drop-in directory also from unit path cache. */ ++ if (path_equal(last_path_component(p), dropin_directory)) ++ free(set_remove(u->manager->unit_path_cache, p)); ++ + (void) unlink(*i); + (void) rmdir(p); + } diff --git a/1337-udev-allow-persistent-storage-rules-for-rbd-devices.patch b/1337-udev-allow-persistent-storage-rules-for-rbd-devices.patch new file mode 100644 index 0000000..ae0bb5c --- /dev/null +++ b/1337-udev-allow-persistent-storage-rules-for-rbd-devices.patch @@ -0,0 +1,41 @@ +From b7156edf92d815ad3bf84fbcaaa7a4a94a648999 Mon Sep 17 00:00:00 2001 +From: Peter Rajnoha +Date: Thu, 5 Sep 2024 12:31:20 +0200 +Subject: [PATCH] udev: allow persistent storage rules for rbd devices + +The RADOS Block Device (rbd) can be used as any other block device with +further layers on top of it, hence allow the common persistent storage +rules to apply, including watching for changes. + +(cherry picked from commit cbe65d38cf0a2e55cdba75871de108bc505a7095) + +Resolves: RHEL-3631 +--- + rules.d/60-block.rules | 2 +- + rules.d/60-persistent-storage.rules | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/rules.d/60-block.rules b/rules.d/60-block.rules +index 3134ab995e..13f88e92c8 100644 +--- a/rules.d/60-block.rules ++++ b/rules.d/60-block.rules +@@ -9,5 +9,5 @@ ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", + + # watch metadata changes, caused by tools closing the device node which was opened for writing + ACTION!="remove", SUBSYSTEM=="block", \ +- KERNEL=="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|ubi*|scm*|pmem*|nbd*|zd*", \ ++ KERNEL=="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|ubi*|scm*|pmem*|nbd*|zd*|rbd*", \ + OPTIONS+="watch" +diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules +index 10b347e191..3aa365bec4 100644 +--- a/rules.d/60-persistent-storage.rules ++++ b/rules.d/60-persistent-storage.rules +@@ -7,7 +7,7 @@ ACTION=="remove", GOTO="persistent_storage_end" + ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end" + + SUBSYSTEM!="block|ubi", GOTO="persistent_storage_end" +-KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|ubi*|scm*|pmem*|nbd*|zd*", GOTO="persistent_storage_end" ++KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|ubi*|scm*|pmem*|nbd*|zd*|rbd*", GOTO="persistent_storage_end" + + # ignore partitions that span the entire disk + TEST=="whole_disk", GOTO="persistent_storage_end" diff --git a/1338-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch b/1338-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch new file mode 100644 index 0000000..5478e86 --- /dev/null +++ b/1338-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch @@ -0,0 +1,55 @@ +From 1aaebe972b912e1f2ef3b874edb22e47f7def5b9 Mon Sep 17 00:00:00 2001 +From: Jan Macku +Date: Mon, 4 May 2026 09:30:17 +0200 +Subject: [PATCH] udev/net_id: introduce naming scheme for RHEL-9.9 + +rhel-only: policy + +Resolves: RHEL-25518 +--- + man/systemd.net-naming-scheme.xml | 6 ++++++ + src/shared/netif-naming-scheme.c | 1 + + src/shared/netif-naming-scheme.h | 1 + + 3 files changed, 8 insertions(+) + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +index c6ee7b4b6e..be7197d62d 100644 +--- a/man/systemd.net-naming-scheme.xml ++++ b/man/systemd.net-naming-scheme.xml +@@ -533,6 +533,12 @@ + PCI slot number is now read from firmware_node/sun sysfs file. + + ++ ++ rhel-9.9 ++ ++ Same as naming scheme rhel-9.8. ++ ++ + + + By default rhel-9.0 is used. +diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c +index 4ed866491e..5edc50069d 100644 +--- a/src/shared/netif-naming-scheme.c ++++ b/src/shared/netif-naming-scheme.c +@@ -48,6 +48,7 @@ static const NamingScheme naming_schemes[] = { + { "rhel-9.6", NAMING_RHEL_9_6 }, + { "rhel-9.7", NAMING_RHEL_9_7 }, + { "rhel-9.8", NAMING_RHEL_9_8 }, ++ { "rhel-9.9", NAMING_RHEL_9_9 }, + /* … add more schemes here, as the logic to name devices is updated … */ + + EXTRA_NET_NAMING_MAP +diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h +index c16476522a..6bb8db920e 100644 +--- a/src/shared/netif-naming-scheme.h ++++ b/src/shared/netif-naming-scheme.h +@@ -78,6 +78,7 @@ typedef enum NamingSchemeFlags { + NAMING_RHEL_9_6 = NAMING_RHEL_9_5, + NAMING_RHEL_9_7 = NAMING_RHEL_9_5, + NAMING_RHEL_9_8 = NAMING_RHEL_9_5 | NAMING_FIRMWARE_NODE_SUN, ++ NAMING_RHEL_9_9 = NAMING_RHEL_9_8, + + EXTRA_NET_NAMING_SCHEMES + diff --git a/1339-fstab-generator-support-swap-on-network-block-device.patch b/1339-fstab-generator-support-swap-on-network-block-device.patch new file mode 100644 index 0000000..b2e0e9a --- /dev/null +++ b/1339-fstab-generator-support-swap-on-network-block-device.patch @@ -0,0 +1,322 @@ +From e5e4bfa6dcdc2502e57c813ea0d0a72ee37fb337 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 7 Apr 2026 11:16:42 +0200 +Subject: [PATCH] fstab-generator: support swap on network block devices + +Teach swap units to support the _netdev option as well, which should +make swaps on iSCSI possible. This mirrors the logic we already have for +regular mounts in both the fstab-generator and the core +(mount.c/swap.c). + +Co-developed-by: Claude Opus 4.6 +(cherry picked from commit 3d5bd67a2259e7a4edc27476d4cae049653c4414) + +Resolves: RHEL-166187 +--- + man/systemd.swap.xml | 28 +++++++++-- + src/core/swap.c | 46 ++++++++++++++++--- + src/fstab-generator/fstab-generator.c | 16 +++++-- + src/shared/generator.c | 2 +- + .../systemd-remount-fs.service | 0 + .../sysroot.mount | 0 + .../50-netdev-dependencies.conf | 5 ++ + .../dev-sdx1.swap | 10 ++++ + .../systemd-remount-fs.service | 0 + .../remote-fs.target.requires/dev-sdx1.swap | 1 + + .../50-netdev-dependencies.conf | 5 ++ + .../dev-sdx1.swap | 10 ++++ + .../sysroot.mount | 0 + .../remote-fs.target.requires/dev-sdx1.swap | 1 + + .../test-21-swap-netdev.fstab.input | 1 + + 15 files changed, 111 insertions(+), 14 deletions(-) + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service + create mode 120000 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount + create mode 120000 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.input + +diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml +index 8287382eb6..6af8a31021 100644 +--- a/man/systemd.swap.xml ++++ b/man/systemd.swap.xml +@@ -90,9 +90,15 @@ + The following dependencies are added unless DefaultDependencies=no is set: + + +- Swap units automatically acquire a Conflicts= and a ++ Local swap units automatically acquire a Conflicts= and a + Before= dependency on umount.target so that they are deactivated at + shutdown as well as a Before=swap.target dependency. ++ ++ Network swap units (those with in their options) automatically acquire ++ After= dependencies on remote-fs-pre.target and ++ network.target, plus After= and Wants= dependencies ++ on network-online.target, and a Before= dependency on ++ remote-fs.target instead of swap.target. + + + +@@ -124,7 +130,8 @@ + + With , the swap unit + will not be added as a dependency for +- swap.target. This means that it will not ++ swap.target (or remote-fs.target for network swap devices, ++ see below). This means that it will not + be activated automatically during boot, unless it is pulled in + by some other unit. The option has the + opposite meaning and is the default. +@@ -136,8 +143,8 @@ + + With , the swap unit + will be only wanted, not required by +- swap.target. This means that the boot +- will continue even if this swap device is not activated ++ swap.target (or remote-fs.target for network swap ++ devices). This means that the boot will continue even if this swap device is not activated + successfully. + + +@@ -161,6 +168,19 @@ + in systemd.mount5. + + ++ ++ ++ ++ ++ Marks this swap device as requiring network access. This is useful for swap on ++ network block devices (e.g. iSCSI). ++ ++ Network swap units are ordered between remote-fs-pre.target and ++ remote-fs.target, instead of being ordered before ++ swap.target. They also pull in network-online.target and ++ are ordered after it and network.target. ++ ++ + + + +diff --git a/src/core/swap.c b/src/core/swap.c +index 5c83c4780f..10743d4b9d 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -253,6 +253,7 @@ static int swap_add_device_dependencies(Swap *s) { + } + + static int swap_add_default_dependencies(Swap *s) { ++ SwapParameters *p; + int r; + + assert(s); +@@ -266,13 +267,46 @@ static int swap_add_default_dependencies(Swap *s) { + if (detect_container() > 0) + return 0; + +- /* swap units generated for the swap dev links are missing the +- * ordering dep against the swap target. */ +- r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, true, UNIT_DEPENDENCY_DEFAULT); +- if (r < 0) +- return r; ++ p = swap_get_parameters(s); ++ ++ if (p && fstab_test_option(p->options, "_netdev\0")) { ++ /* Network swap devices (those with _netdev in options) are routed through ++ * remote-fs.target instead of swap.target, mirroring how network mounts use ++ * remote-fs.target instead of local-fs.target. This avoids an ordering cycle: ++ * swap.target is pulled in at sysinit.target time, but network-online.target ++ * only comes after basic.target which is after sysinit.target. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOTE_FS_PRE_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ /* Pull in and order after network-online.target, analogous to ++ * mount_add_default_network_dependencies() for network mounts. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_NETWORK_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ } else { ++ /* swap units generated for the swap dev links are missing the ++ * ordering dep against the swap target. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ } + +- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); ++ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); + } + + static int swap_verify(Swap *s) { +diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c +index 28677a2f39..7b417dd2d1 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -208,6 +208,7 @@ static int add_swap( + + _cleanup_free_ char *name = NULL; + _cleanup_fclose_ FILE *f = NULL; ++ bool is_network; + int r; + + assert(what); +@@ -227,10 +228,12 @@ static int add_swap( + return true; + } + +- log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s", ++ is_network = fstab_test_option(options, "_netdev\0"); ++ ++ log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s netdev=%s", + what, + yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS), +- yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL)); ++ yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL), yes_no(is_network)); + + r = unit_name_from_path(what, ".swap", &name); + if (r < 0) +@@ -271,6 +274,12 @@ static int add_swap( + if (r < 0) + return r; + ++ if (is_network) { ++ r = generator_write_device_deps(arg_dest, what, /* where= */ NULL, options); ++ if (r < 0) ++ return r; ++ } ++ + if (flags & MOUNT_MAKEFS) { + r = generator_hook_up_mkswap(arg_dest, what); + if (r < 0) +@@ -284,7 +293,8 @@ static int add_swap( + log_warning("%s: measuring swap devices is currently unsupported.", what); + + if (!(flags & MOUNT_NOAUTO)) { +- r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, ++ const char *target = is_network ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_SWAP_TARGET; ++ r = generator_add_symlink(arg_dest, target, + (flags & MOUNT_NOFAIL) ? "wants" : "requires", name); + if (r < 0) + return r; +diff --git a/src/shared/generator.c b/src/shared/generator.c +index a688ba446c..5dc103400b 100644 +--- a/src/shared/generator.c ++++ b/src/shared/generator.c +@@ -428,7 +428,7 @@ int generator_write_device_deps( + _cleanup_free_ char *node = NULL, *unit = NULL; + int r; + +- if (fstab_is_extrinsic(where, opts)) ++ if (where && fstab_is_extrinsic(where, opts)) + return 0; + + if (!fstab_test_option(opts, "_netdev\0")) +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf +new file mode 100644 +index 0000000000..33d814c275 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf +@@ -0,0 +1,5 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++After=network-online.target network.target ++Wants=network-online.target +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap +new file mode 100644 +index 0000000000..32f276c9e1 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap +@@ -0,0 +1,10 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++Documentation=man:fstab(5) man:systemd-fstab-generator(8) ++SourcePath=/etc/fstab ++After=blockdev@dev-sdx1.target ++ ++[Swap] ++What=/dev/sdx1 ++Options=_netdev +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap +new file mode 120000 +index 0000000000..00f0c5ce66 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap +@@ -0,0 +1 @@ ++../dev-sdx1.swap +\ No newline at end of file +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf +new file mode 100644 +index 0000000000..33d814c275 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf +@@ -0,0 +1,5 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++After=network-online.target network.target ++Wants=network-online.target +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap +new file mode 100644 +index 0000000000..32f276c9e1 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap +@@ -0,0 +1,10 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++Documentation=man:fstab(5) man:systemd-fstab-generator(8) ++SourcePath=/etc/fstab ++After=blockdev@dev-sdx1.target ++ ++[Swap] ++What=/dev/sdx1 ++Options=_netdev +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap +new file mode 120000 +index 0000000000..00f0c5ce66 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap +@@ -0,0 +1 @@ ++../dev-sdx1.swap +\ No newline at end of file +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.input b/test/test-fstab-generator/test-21-swap-netdev.fstab.input +new file mode 100644 +index 0000000000..5f719a4202 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.input +@@ -0,0 +1 @@ ++/dev/sdx1 none swap _netdev 0 0 diff --git a/1340-core-increment-start-limit-counter-only-when-we-can-.patch b/1340-core-increment-start-limit-counter-only-when-we-can-.patch new file mode 100644 index 0000000..a7a8fce --- /dev/null +++ b/1340-core-increment-start-limit-counter-only-when-we-can-.patch @@ -0,0 +1,460 @@ +From 4060cdad388b0ae658f2024633b842a46c37962e Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 20 Oct 2025 19:40:28 +0900 +Subject: [PATCH] core: increment start limit counter only when we can start + the unit + +Otherwise, e.g. requesting to start a unit that is under stopping may +enter the failed state. + +This makes +- rename .can_start() -> .test_startable(), and make it allow to return + boolean and refuse to start units when it returns false, +- refuse earlier to start units that are in the deactivating state, so + several redundant conditions in .start() can be dropped, +- move checks for unit states mapped to UNIT_ACTIVATING from .start() to + .test_startable(). + +Fixes #39247. + +(cherry picked from commit 8eefd0f4debc0bcfeea89dd39c43e3318f3f7ae7) + +Resolves: RHEL-164539 +--- + src/core/automount.c | 6 ++-- + src/core/mount.c | 25 +++++---------- + src/core/path.c | 6 ++-- + src/core/service.c | 24 ++++++--------- + src/core/socket.c | 32 ++++++-------------- + src/core/swap.c | 23 +++++--------- + src/core/timer.c | 6 ++-- + src/core/unit.c | 11 ++++--- + src/core/unit.h | 4 +-- + test/units/TEST-07-PID1.start-limit.sh | 42 ++++++++++++++++++++++++++ + 10 files changed, 93 insertions(+), 86 deletions(-) + create mode 100755 test/units/TEST-07-PID1.start-limit.sh + +diff --git a/src/core/automount.c b/src/core/automount.c +index a44b8e878d..ae8399d1af 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -1084,7 +1084,7 @@ static bool automount_supported(void) { + return supported; + } + +-static int automount_can_start(Unit *u) { ++static int automount_test_startable(Unit *u) { + Automount *a = AUTOMOUNT(u); + int r; + +@@ -1096,7 +1096,7 @@ static int automount_can_start(Unit *u) { + return r; + } + +- return 1; ++ return true; + } + + static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { +@@ -1162,5 +1162,5 @@ const UnitVTable automount_vtable = { + }, + }, + +- .can_start = automount_can_start, ++ .test_startable = automount_test_startable, + }; +diff --git a/src/core/mount.c b/src/core/mount.c +index be6fbf4cc4..5789a253cd 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1212,21 +1212,6 @@ static int mount_start(Unit *u) { + Mount *m = MOUNT(u); + int r; + +- assert(m); +- +- /* We cannot fulfill this request right now, try again later +- * please! */ +- if (IN_SET(m->state, +- MOUNT_UNMOUNTING, +- MOUNT_UNMOUNTING_SIGTERM, +- MOUNT_UNMOUNTING_SIGKILL, +- MOUNT_CLEANING)) +- return -EAGAIN; +- +- /* Already on it! */ +- if (IN_SET(m->state, MOUNT_MOUNTING, MOUNT_MOUNTING_DONE)) +- return 0; +- + assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED)); + + r = unit_acquire_invocation_id(u); +@@ -2214,19 +2199,23 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&m->exec_context, ret); + } + +-static int mount_can_start(Unit *u) { ++static int mount_test_startable(Unit *u) { + Mount *m = MOUNT(u); + int r; + + assert(m); + ++ /* It is already being started. */ ++ if (IN_SET(m->state, MOUNT_MOUNTING, MOUNT_MOUNTING_DONE)) ++ return false; ++ + r = unit_test_start_limit(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + +- return 1; ++ return true; + } + + char* mount_get_where_escaped(const Mount *m) { +@@ -2337,5 +2326,5 @@ const UnitVTable mount_vtable = { + }, + }, + +- .can_start = mount_can_start, ++ .test_startable = mount_test_startable, + }; +diff --git a/src/core/path.c b/src/core/path.c +index 3a46e44928..6f850244f1 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -846,7 +846,7 @@ static void path_reset_failed(Unit *u) { + p->result = PATH_SUCCESS; + } + +-static int path_can_start(Unit *u) { ++static int path_test_startable(Unit *u) { + Path *p = PATH(u); + int r; + +@@ -858,7 +858,7 @@ static int path_can_start(Unit *u) { + return r; + } + +- return 1; ++ return true; + } + + static void activation_details_path_done(ActivationDetails *details) { +@@ -1000,7 +1000,7 @@ const UnitVTable path_vtable = { + + .bus_set_property = bus_path_set_property, + +- .can_start = path_can_start, ++ .test_startable = path_test_startable, + }; + + const ActivationDetailsVTable activation_details_path_vtable = { +diff --git a/src/core/service.c b/src/core/service.c +index 53f40b2d78..e152fb6227 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2557,17 +2557,6 @@ static int service_start(Unit *u) { + + assert(s); + +- /* We cannot fulfill this request right now, try again later +- * please! */ +- if (IN_SET(s->state, +- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, +- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) +- return -EAGAIN; +- +- /* Already on it! */ +- if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) +- return 0; +- + /* A service that will be restarted must be stopped first to + * trigger BindsTo and/or OnFailure dependencies. If a user + * does not want to wait for the holdoff time to elapse, the +@@ -4707,12 +4696,19 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) { + return NULL; + } + +-static int service_can_start(Unit *u) { ++static int service_test_startable(Unit *u) { + Service *s = SERVICE(u); + int r; + + assert(s); + ++ /* First check the state, and do not increment start limit counter if the service cannot start due to ++ * that e.g. it is already being started. Note, the service states mapped to UNIT_ACTIVE, ++ * UNIT_RELOADING, UNIT_DEACTIVATING, UNIT_MAINTENANCE, and UNIT_REFRESHING are already filtered in ++ * unit_start(). Hence, here we only need to check states that mapped to UNIT_ACTIVATING. */ ++ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) ++ return false; ++ + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_test_start_limit(u); + if (r < 0) { +@@ -4720,7 +4716,7 @@ static int service_can_start(Unit *u) { + return r; + } + +- return 1; ++ return true; + } + + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { +@@ -4896,5 +4892,5 @@ const UnitVTable service_vtable = { + .finished_job = service_finished_job, + }, + +- .can_start = service_can_start, ++ .test_startable = service_test_startable, + }; +diff --git a/src/core/socket.c b/src/core/socket.c +index 7abae70255..103b399ab8 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2460,25 +2460,6 @@ static int socket_start(Unit *u) { + + assert(s); + +- /* We cannot fulfill this request right now, try again later +- * please! */ +- if (IN_SET(s->state, +- SOCKET_STOP_PRE, +- SOCKET_STOP_PRE_SIGKILL, +- SOCKET_STOP_PRE_SIGTERM, +- SOCKET_STOP_POST, +- SOCKET_FINAL_SIGTERM, +- SOCKET_FINAL_SIGKILL, +- SOCKET_CLEANING)) +- return -EAGAIN; +- +- /* Already on it! */ +- if (IN_SET(s->state, +- SOCKET_START_PRE, +- SOCKET_START_CHOWN, +- SOCKET_START_POST)) +- return 0; +- + /* Cannot run this without the service being around */ + if (UNIT_ISSET(s->service)) { + Service *service; +@@ -3392,19 +3373,26 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + +-static int socket_can_start(Unit *u) { ++static int socket_test_startable(Unit *u) { + Socket *s = SOCKET(u); + int r; + + assert(s); + ++ /* It is already being started. */ ++ if (IN_SET(s->state, ++ SOCKET_START_PRE, ++ SOCKET_START_CHOWN, ++ SOCKET_START_POST)) ++ return false; ++ + r = unit_test_start_limit(u); + if (r < 0) { + socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); + return r; + } + +- return 1; ++ return true; + } + + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { +@@ -3534,5 +3522,5 @@ const UnitVTable socket_vtable = { + }, + }, + +- .can_start = socket_can_start, ++ .test_startable = socket_test_startable, + }; +diff --git a/src/core/swap.c b/src/core/swap.c +index 10743d4b9d..458c935b0c 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -933,19 +933,6 @@ static int swap_start(Unit *u) { + int r; + + assert(s); +- +- /* We cannot fulfill this request right now, try again later please! */ +- if (IN_SET(s->state, +- SWAP_DEACTIVATING, +- SWAP_DEACTIVATING_SIGTERM, +- SWAP_DEACTIVATING_SIGKILL, +- SWAP_CLEANING)) +- return -EAGAIN; +- +- /* Already on it! */ +- if (s->state == SWAP_ACTIVATING) +- return 0; +- + assert(IN_SET(s->state, SWAP_DEAD, SWAP_FAILED)); + + if (detect_container() > 0) +@@ -1612,19 +1599,23 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) { + return exec_context_get_clean_mask(&s->exec_context, ret); + } + +-static int swap_can_start(Unit *u) { ++static int swap_test_startable(Unit *u) { + Swap *s = SWAP(u); + int r; + + assert(s); + ++ /* It is already being started. */ ++ if (s->state == SWAP_ACTIVATING) ++ return false; ++ + r = unit_test_start_limit(u); + if (r < 0) { + swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); + return r; + } + +- return 1; ++ return true; + } + + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { +@@ -1723,5 +1714,5 @@ const UnitVTable swap_vtable = { + }, + }, + +- .can_start = swap_can_start, ++ .test_startable = swap_test_startable, + }; +diff --git a/src/core/timer.c b/src/core/timer.c +index 8fb79bc0cb..b96e88af90 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -897,7 +897,7 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) { + return 0; + } + +-static int timer_can_start(Unit *u) { ++static int timer_test_startable(Unit *u) { + Timer *t = TIMER(u); + int r; + +@@ -909,7 +909,7 @@ static int timer_can_start(Unit *u) { + return r; + } + +- return 1; ++ return true; + } + + static void activation_details_timer_serialize(ActivationDetails *details, FILE *f) { +@@ -1057,7 +1057,7 @@ const UnitVTable timer_vtable = { + + .bus_set_property = bus_timer_set_property, + +- .can_start = timer_can_start, ++ .test_startable = timer_test_startable, + }; + + const ActivationDetailsVTable activation_details_timer_vtable = { +diff --git a/src/core/unit.c b/src/core/unit.c +index 790f4023a2..0b58d0498b 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1846,7 +1846,7 @@ int unit_start(Unit *u, ActivationDetails *details) { + state = unit_active_state(u); + if (UNIT_IS_ACTIVE_OR_RELOADING(state)) + return -EALREADY; +- if (state == UNIT_MAINTENANCE) ++ if (IN_SET(state, UNIT_DEACTIVATING, UNIT_MAINTENANCE)) + return -EAGAIN; + + /* Units that aren't loaded cannot be started */ +@@ -1889,10 +1889,11 @@ int unit_start(Unit *u, ActivationDetails *details) { + return unit_start(following, details); + } + +- /* Check our ability to start early so that failure conditions don't cause us to enter a busy loop. */ +- if (UNIT_VTABLE(u)->can_start) { +- r = UNIT_VTABLE(u)->can_start(u); +- if (r < 0) ++ /* Check our ability to start early so that ratelimited or already starting/started units don't ++ * cause us to enter a busy loop. */ ++ if (UNIT_VTABLE(u)->test_startable) { ++ r = UNIT_VTABLE(u)->test_startable(u); ++ if (r <= 0) + return r; + } + +diff --git a/src/core/unit.h b/src/core/unit.h +index fdea76458d..acbf74477e 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -751,8 +751,8 @@ typedef struct UnitVTable { + bool (*supported)(void); + + /* If this function is set, it's invoked first as part of starting a unit to allow start rate +- * limiting checks to occur before we do anything else. */ +- int (*can_start)(Unit *u); ++ * limiting checks and unit state checks to occur before we do anything else. */ ++ int (*test_startable)(Unit *u); + + /* The strings to print in status messages */ + UnitStatusMessageFormats status_message_formats; +diff --git a/test/units/TEST-07-PID1.start-limit.sh b/test/units/TEST-07-PID1.start-limit.sh +new file mode 100755 +index 0000000000..f793d32876 +--- /dev/null ++++ b/test/units/TEST-07-PID1.start-limit.sh +@@ -0,0 +1,42 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++set -eux ++set -o pipefail ++ ++# For issue #39247. ++ ++at_exit() { ++ set +e ++ ++ rm -rf /run/systemd/system/systemd-resolved.service.d/ ++ systemctl daemon-reload ++ systemctl restart systemd-resolved.service ++} ++ ++trap at_exit EXIT ++ ++mkdir -p /run/systemd/system/systemd-resolved.service.d/ ++cat >/run/systemd/system/systemd-resolved.service.d/99-start-limit.conf < +Date: Tue, 28 Oct 2025 13:20:58 +0900 +Subject: [PATCH] TEST-07-PID1: wait for systemd-resolved being stopped + +As 'systemctl stop' is called with --no-block, previously systemd-resolved +might not be stopped when 'resolvectl' is called, and the DBus connection +might be closed during the call: +``` +TEST-07-PID1.sh[5643]: + systemctl stop --no-block systemd-resolved.service +TEST-07-PID1.sh[5643]: + resolvectl +TEST-07-PID1.sh[5732]: Failed to get global data: Remote peer disconnected +``` + +Follow-up for 8eefd0f4debc0bcfeea89dd39c43e3318f3f7ae7. +Fixes https://github.com/systemd/systemd/pull/39388#issuecomment-3439277442. + +(cherry picked from commit 6454fde83eef8da7391ad18a1b1a3248402f9214) + +Related: RHEL-164539 +--- + test/units/TEST-07-PID1.start-limit.sh | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/test/units/TEST-07-PID1.start-limit.sh b/test/units/TEST-07-PID1.start-limit.sh +index f793d32876..b512c58ff2 100755 +--- a/test/units/TEST-07-PID1.start-limit.sh ++++ b/test/units/TEST-07-PID1.start-limit.sh +@@ -35,6 +35,9 @@ for i in {1..5}; do + echo "Start #$i" + + systemctl stop --no-block systemd-resolved.service ++ # Wait for systemd-resolved in ExecStart= being stopped. ++ # shellcheck disable=SC2016 ++ timeout 10 bash -c 'until [[ "$(systemctl show --property=MainPID --value systemd-resolved.service)" == 0 ]]; do sleep 0.1; done' + if ! resolvectl; then + journalctl -o short-monotonic --no-hostname --no-pager -u systemd-resolved.service -n 15 + exit 1 diff --git a/1342-test-extend-start-limit-interval.patch b/1342-test-extend-start-limit-interval.patch new file mode 100644 index 0000000..d11fa08 --- /dev/null +++ b/1342-test-extend-start-limit-interval.patch @@ -0,0 +1,40 @@ +From d092520e2c3221e191b46c6c771d54ec51f678dc Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sat, 25 Oct 2025 15:34:44 +0900 +Subject: [PATCH] test: extend start limit interval + +As the modified service requires about ~10 seconds for stopping, the +service never hit the start limit even if we tried to restart the +service more than 5 times. + +This also checks that the service is actually triggered by dbus method +call. + +Follow-up for 8eefd0f4debc0bcfeea89dd39c43e3318f3f7ae7. + +(cherry picked from commit 44b4caad6cc99449bbf705350939fde1ed9b1248) + +Related: RHEL-164539 +--- + test/units/TEST-07-PID1.start-limit.sh | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/test/units/TEST-07-PID1.start-limit.sh b/test/units/TEST-07-PID1.start-limit.sh +index b512c58ff2..93447452da 100755 +--- a/test/units/TEST-07-PID1.start-limit.sh ++++ b/test/units/TEST-07-PID1.start-limit.sh +@@ -19,7 +19,7 @@ mkdir -p /run/systemd/system/systemd-resolved.service.d/ + cat >/run/systemd/system/systemd-resolved.service.d/99-start-limit.conf </dev/null || : %{_prefix}/lib/dracut/modules.d/70rhel-net-naming-sysattrs/* %changelog +* Tue May 12 2026 systemd maintenance team - 252-70 +- core: cleanup unit's dropin directories from global cache (RHEL-127425) +- udev: allow persistent storage rules for rbd devices (RHEL-3631) +- udev/net_id: introduce naming scheme for RHEL-9.9 (RHEL-25518) +- fstab-generator: support swap on network block devices (RHEL-166187) +- core: increment start limit counter only when we can start the unit (RHEL-164539) +- TEST-07-PID1: wait for systemd-resolved being stopped (RHEL-164539) +- test: extend start limit interval (RHEL-164539) + * Thu Apr 16 2026 systemd maintenance team - 252-69 - nspawn: apply BindUser/Ephemeral from settings file only if trusted (RHEL-163871) - nspawn: normalize pivot_root paths (RHEL-163871)