systemd-252-70

Resolves: RHEL-127425, RHEL-3631, RHEL-25518, RHEL-166187, RHEL-164539
This commit is contained in:
Jan Macku 2026-05-12 13:35:20 +02:00
parent d5a4a5f515
commit d8982300d8
8 changed files with 1015 additions and 1 deletions

View File

@ -0,0 +1,42 @@
From 4843d4f679bb4dd70f15ce49f0786e80bb821e28 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
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);
}

View File

@ -0,0 +1,41 @@
From b7156edf92d815ad3bf84fbcaaa7a4a94a648999 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
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"

View File

@ -0,0 +1,55 @@
From 1aaebe972b912e1f2ef3b874edb22e47f7def5b9 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
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 @@
<para>PCI slot number is now read from <constant>firmware_node/sun</constant> sysfs file.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>rhel-9.9</constant></term>
+
+ <listitem><para>Same as naming scheme <constant>rhel-9.8</constant>.</para></listitem>
+ </varlistentry>
+
</variablelist>
<para>By default <constant>rhel-9.0</constant> is used.</para>
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

View File

@ -0,0 +1,322 @@
From e5e4bfa6dcdc2502e57c813ea0d0a72ee37fb337 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
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 <noreply@anthropic.com>
(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 @@
<para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para>
<itemizedlist>
- <listitem><para>Swap units automatically acquire a <varname>Conflicts=</varname> and a
+ <listitem><para>Local swap units automatically acquire a <varname>Conflicts=</varname> and a
<varname>Before=</varname> dependency on <filename>umount.target</filename> so that they are deactivated at
shutdown as well as a <varname>Before=swap.target</varname> dependency.</para></listitem>
+
+ <listitem><para>Network swap units (those with <option>_netdev</option> in their options) automatically acquire
+ <varname>After=</varname> dependencies on <filename>remote-fs-pre.target</filename> and
+ <filename>network.target</filename>, plus <varname>After=</varname> and <varname>Wants=</varname> dependencies
+ on <filename>network-online.target</filename>, and a <varname>Before=</varname> dependency on
+ <filename>remote-fs.target</filename> instead of <filename>swap.target</filename>.</para></listitem>
</itemizedlist>
</refsect2>
</refsect1>
@@ -124,7 +130,8 @@
<listitem><para>With <option>noauto</option>, the swap unit
will not be added as a dependency for
- <filename>swap.target</filename>. This means that it will not
+ <filename>swap.target</filename> (or <filename>remote-fs.target</filename> for network swap devices,
+ see <option>_netdev</option> below). This means that it will not
be activated automatically during boot, unless it is pulled in
by some other unit. The <option>auto</option> option has the
opposite meaning and is the default.</para>
@@ -136,8 +143,8 @@
<listitem><para>With <option>nofail</option>, the swap unit
will be only wanted, not required by
- <filename>swap.target</filename>. This means that the boot
- will continue even if this swap device is not activated
+ <filename>swap.target</filename> (or <filename>remote-fs.target</filename> for network swap
+ devices). This means that the boot will continue even if this swap device is not activated
successfully.</para>
</listitem>
</varlistentry>
@@ -161,6 +168,19 @@
in <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>_netdev</option></term>
+
+ <listitem><para>Marks this swap device as requiring network access. This is useful for swap on
+ network block devices (e.g. iSCSI).</para>
+
+ <para>Network swap units are ordered between <filename>remote-fs-pre.target</filename> and
+ <filename>remote-fs.target</filename>, instead of being ordered before
+ <filename>swap.target</filename>. They also pull in <filename>network-online.target</filename> and
+ are ordered after it and <filename>network.target</filename>.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
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

View File

@ -0,0 +1,460 @@
From 4060cdad388b0ae658f2024633b842a46c37962e Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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 <<EOF
+[Unit]
+StartLimitBurst=5
+StartLimitInterval=30
+
+[Service]
+ExecStopPost=sleep 10
+EOF
+
+systemctl daemon-reload
+systemctl restart systemd-resolved.service
+systemctl reset-failed systemd-resolved.service
+systemctl status --no-pager systemd-resolved.service
+systemctl show systemd-resolved.service | grep StartLimit
+
+for i in {1..5}; do
+ echo "Start #$i"
+
+ systemctl stop --no-block systemd-resolved.service
+ if ! resolvectl; then
+ journalctl -o short-monotonic --no-hostname --no-pager -u systemd-resolved.service -n 15
+ exit 1
+ fi
+done

View File

@ -0,0 +1,38 @@
From 769a4fd1ad342ff84e985f0b73f23e03bbb677db Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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

View File

@ -0,0 +1,40 @@
From d092520e2c3221e191b46c6c771d54ec51f678dc Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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 <<EOF
[Unit]
StartLimitBurst=5
-StartLimitInterval=30
+StartLimitInterval=100
[Service]
ExecStopPost=sleep 10
@@ -42,4 +42,5 @@ for i in {1..5}; do
journalctl -o short-monotonic --no-hostname --no-pager -u systemd-resolved.service -n 15
exit 1
fi
+ systemctl is-active systemd-resolved.service
done

View File

@ -21,7 +21,7 @@
Name: systemd
Url: https://systemd.io
Version: 252
Release: 69%{?dist}
Release: 70%{?dist}
# For a breakdown of the licensing, see README
License: LGPLv2+ and MIT and GPLv2+
Summary: System and Service Manager
@ -1418,6 +1418,13 @@ Patch1332: 1332-core-service-do-not-propagate-reload-for-combined-RE.patch
Patch1333: 1333-udev-check-for-invalid-chars-in-various-fields-recei.patch
Patch1334: 1334-udev-fix-review-mixup.patch
Patch1335: 1335-udev-scsi-id-check-for-invalid-chars-in-various-fiel.patch
Patch1336: 1336-core-cleanup-unit-s-dropin-directories-from-global-c.patch
Patch1337: 1337-udev-allow-persistent-storage-rules-for-rbd-devices.patch
Patch1338: 1338-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch
Patch1339: 1339-fstab-generator-support-swap-on-network-block-device.patch
Patch1340: 1340-core-increment-start-limit-counter-only-when-we-can-.patch
Patch1341: 1341-TEST-07-PID1-wait-for-systemd-resolved-being-stopped.patch
Patch1342: 1342-test-extend-start-limit-interval.patch
# Downstream-only patches (90009999)
@ -2295,6 +2302,15 @@ systemd-hwdb update &>/dev/null || :
%{_prefix}/lib/dracut/modules.d/70rhel-net-naming-sysattrs/*
%changelog
* Tue May 12 2026 systemd maintenance team <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 252-69
- nspawn: apply BindUser/Ephemeral from settings file only if trusted (RHEL-163871)
- nspawn: normalize pivot_root paths (RHEL-163871)