import UBI systemd-239-82.el8_10.8

This commit is contained in:
eabdullin 2025-11-04 08:05:26 +00:00
parent 69b3d9e0f7
commit a4c73665dd
23 changed files with 1806 additions and 1 deletions

View File

@ -0,0 +1,171 @@
From dd01fc11f599dbfe0a1d5a42e60f9364ae991d31 Mon Sep 17 00:00:00 2001
From: Ronan Pigott <ronan@rjp.ie>
Date: Fri, 18 Jul 2025 15:10:03 +0200
Subject: [PATCH] dbus: stash the subscriber list when we disconenct from the
bus
If we unexpectly disconnect from the bus, systemd would end up dropping
the list of subscribers, which breaks the ability of clients like logind
to monitor the state of units.
Stash the list of subscribers into the deserialized state in the event
of a disconnect so that when we recover we can renew the broken
subscriptions.
(cherry picked from commit 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98)
Related: RHEL-75081
---
src/core/dbus.c | 23 ++++++++++++++++-------
src/core/dbus.h | 2 +-
src/core/manager.c | 5 ++++-
src/shared/bus-util.c | 22 ++++++++++++++++++++++
src/shared/bus-util.h | 1 +
5 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/src/core/dbus.c b/src/core/dbus.c
index ec6c52cb85..1b8bb44eda 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -908,6 +908,8 @@ int bus_init_api(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to set up API bus: %m");
+ (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->deserialized_subscribed);
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
m->api_bus = TAKE_PTR(bus);
r = manager_enqueue_sync_bus_names(m);
@@ -1070,8 +1072,17 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
}
/* Get rid of tracked clients on this bus */
- if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus)
+ if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus) {
+ _cleanup_strv_free_ char **subscribed = NULL;
+ int r;
+
+ r = bus_track_to_strv(m->subscribed, &subscribed);
+ if (r < 0)
+ log_warning_errno(r, "Failed to serialize api subscribers, ignoring: %m");
+ strv_free_and_replace(m->deserialized_subscribed, subscribed);
+
m->subscribed = sd_bus_track_unref(m->subscribed);
+ }
HASHMAP_FOREACH(j, m->jobs, i)
if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
@@ -1131,7 +1142,6 @@ void bus_done(Manager *m) {
assert(!m->subscribed);
- m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
bus_verify_polkit_async_registry_free(m->polkit_registry);
}
@@ -1229,20 +1239,19 @@ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
}
}
-int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
- int r = 0;
+int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l) {
+ int r;
- assert(m);
assert(t);
if (strv_isempty(l))
return 0;
- if (!m->api_bus)
+ if (!bus)
return 0;
if (!*t) {
- r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+ r = sd_bus_track_new(bus, t, NULL, NULL);
if (r < 0)
return r;
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index f1c0fa86c0..402c970d74 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -19,7 +19,7 @@ void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
+int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l);
int manager_enqueue_sync_bus_names(Manager *m);
diff --git a/src/core/manager.c b/src/core/manager.c
index e09227d5ac..c5d906e42e 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1348,6 +1348,9 @@ Manager* manager_free(Manager *m) {
free(m->switch_root);
free(m->switch_root_init);
+ sd_bus_track_unref(m->subscribed);
+ strv_free(m->deserialized_subscribed);
+
rlimit_free_all(m->rlimit);
assert(hashmap_isempty(m->units_requiring_mounts_for));
@@ -1656,7 +1659,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
manager_setup_bus(m);
/* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
- (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ (void) bus_track_coldplug(m->api_bus, &m->subscribed, false, m->deserialized_subscribed);
m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
/* Third, fire things up! */
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index ff0e800347..5ce4613262 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1694,6 +1694,28 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) {
return r;
}
+int bus_track_to_strv(sd_bus_track *t, char ***ret) {
+ _cleanup_strv_free_ char **subscribed = NULL;
+ int r = 0;
+
+ assert(ret);
+
+ for (const char *n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+ r = sd_bus_track_count_name(t, n);
+ if (r < 0)
+ return r;
+
+ for (int j = 0; j < r; j++) {
+ r = strv_extend(&subscribed, n);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = TAKE_PTR(subscribed);
+ return r;
+}
+
int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
const char *e;
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index b400eb81e2..b3aa62e6e5 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -171,6 +171,7 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
int bus_track_add_name_many(sd_bus_track *t, char **l);
+int bus_track_to_strv(sd_bus_track *t, char ***ret);
int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description);
static inline int bus_open_system_watch_bind(sd_bus **ret) {

View File

@ -0,0 +1,87 @@
From 1cf170260b78e550ed0a0cd3b729527852de0991 Mon Sep 17 00:00:00 2001
From: Ronan Pigott <ronan@rjp.ie>
Date: Fri, 18 Jul 2025 15:12:31 +0200
Subject: [PATCH] manager: s/deserialized_subscribed/subscribed_as_strv
Now that this field may get populated at runtime, the deserialized name
is misleading. Change the name to reflect its updated purpose.
(cherry picked from commit e1315a621ae26473fcc9cd0d6013836f5f498d40)
Related: RHEL-75081
---
src/core/dbus.c | 6 +++---
src/core/manager.c | 8 ++++----
src/core/manager.h | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 1b8bb44eda..9f439dcb23 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -908,8 +908,8 @@ int bus_init_api(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to set up API bus: %m");
- (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->deserialized_subscribed);
- m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+ (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->subscribed_as_strv);
+ m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
m->api_bus = TAKE_PTR(bus);
r = manager_enqueue_sync_bus_names(m);
@@ -1079,7 +1079,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
r = bus_track_to_strv(m->subscribed, &subscribed);
if (r < 0)
log_warning_errno(r, "Failed to serialize api subscribers, ignoring: %m");
- strv_free_and_replace(m->deserialized_subscribed, subscribed);
+ strv_free_and_replace(m->subscribed_as_strv, subscribed);
m->subscribed = sd_bus_track_unref(m->subscribed);
}
diff --git a/src/core/manager.c b/src/core/manager.c
index c5d906e42e..37bdac8b1d 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1349,7 +1349,7 @@ Manager* manager_free(Manager *m) {
free(m->switch_root_init);
sd_bus_track_unref(m->subscribed);
- strv_free(m->deserialized_subscribed);
+ strv_free(m->subscribed_as_strv);
rlimit_free_all(m->rlimit);
@@ -1659,8 +1659,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
manager_setup_bus(m);
/* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
- (void) bus_track_coldplug(m->api_bus, &m->subscribed, false, m->deserialized_subscribed);
- m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+ (void) bus_track_coldplug(m->api_bus, &m->subscribed, false, m->subscribed_as_strv);
+ m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
/* Third, fire things up! */
manager_coldplug(m);
@@ -3382,7 +3382,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
exec_runtime_deserialize_one(m, val, fds);
else if ((val = startswith(l, "subscribed="))) {
- if (strv_extend(&m->deserialized_subscribed, val) < 0)
+ if (strv_extend(&m->subscribed_as_strv, val) < 0)
log_oom();
} else {
ManagerTimestamp q;
diff --git a/src/core/manager.h b/src/core/manager.h
index 98d381bc5b..e713250238 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -215,7 +215,7 @@ struct Manager {
considered subscribes, since they last for very short only,
and it is much simpler that way. */
sd_bus_track *subscribed;
- char **deserialized_subscribed;
+ char **subscribed_as_strv;
/* This is used during reloading: before the reload we queue
* the reply message here, and afterwards we send it */

View File

@ -0,0 +1,55 @@
From 42126f36367c4bcb39e37ba251ffbc1f04c9092b Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Fri, 18 Jul 2025 15:13:03 +0200
Subject: [PATCH] bus-util: do not reset the count returned by
sd_bus_track_count_name()
Follow-up for 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98
While at it, turn the retval check for sd_bus_track_count_name()
into assertion, given we're working with already established tracks
(service_name_is_valid() should never yield false in this case).
Addresses https://github.com/systemd/systemd/pull/35406#discussion_r1912066774
(cherry picked from commit 33eeea4128f31df7ab4bd8866b582062d70114ae)
Related: RHEL-75081
---
src/shared/bus-util.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 5ce4613262..ccce078902 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1696,16 +1696,15 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) {
int bus_track_to_strv(sd_bus_track *t, char ***ret) {
_cleanup_strv_free_ char **subscribed = NULL;
- int r = 0;
+ int r;
assert(ret);
for (const char *n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
- r = sd_bus_track_count_name(t, n);
- if (r < 0)
- return r;
+ int c = sd_bus_track_count_name(t, n);
+ assert(c >= 0);
- for (int j = 0; j < r; j++) {
+ for (int j = 0; j < c; j++) {
r = strv_extend(&subscribed, n);
if (r < 0)
return r;
@@ -1713,7 +1712,7 @@ int bus_track_to_strv(sd_bus_track *t, char ***ret) {
}
*ret = TAKE_PTR(subscribed);
- return r;
+ return 0;
}
int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) {

View File

@ -0,0 +1,78 @@
From c322f6916c2a69d31d124b59a295297cd3ca492d Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 29 Dec 2024 15:50:43 +0900
Subject: [PATCH] core: do not disconnect from bus when failed to install
signal match
If bus_add_match_full() is called without install callback and we failed
to install the signal match e.g. by timeout, then add_match_callback()
will disconnect from the bus.
Let's use a custom install handler and handle failures gracefully.
This does not *solve* the root cause of issue #30573, but should improve
the situation when the issue is triggered.
(cherry picked from commit db6b214f95aa42f9a9fa3d94a3c6492cc57b58fb)
Related: RHEL-75081
---
src/core/unit.c | 39 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/src/core/unit.c b/src/core/unit.c
index ac960ef0c8..aedc1d806f 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3115,6 +3115,43 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
return r;
}
+static int signal_name_owner_changed_install_handler(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ const sd_bus_error *e;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ e = sd_bus_message_get_error(message);
+ if (!e) {
+ log_unit_debug(u, "Successfully installed NameOwnerChanged signal match.");
+ return 0;
+ }
+
+ r = sd_bus_error_get_errno(e);
+ log_unit_error_errno(u, r,
+ "Unexpected error response on installing NameOwnerChanged signal match: %s",
+ bus_error_message(e, r));
+
+ /* If we failed to install NameOwnerChanged signal, also unref the bus slot of GetNameOwner(). */
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+
+ if (UNIT_VTABLE(u)->bus_name_owner_change) {
+ /* HACK: I'd like to avoid backporting fc67a943d9 so I have to deal with former
+ vtable->bus_name_owner_change() signature, i.e. provide either old_owner or new_owner and bus_name.
+ Also, vtable->bus_name_owner_change() is implemented only for services. */
+ Service *s = SERVICE(u);
+
+ assert(u->type == UNIT_SERVICE);
+
+ if (s->bus_name_good)
+ UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name, s->bus_name_owner, NULL);
+ }
+
+ return 0;
+}
+
static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name, *old_owner, *new_owner;
Unit *u = userdata;
@@ -3155,7 +3192,7 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
"member='NameOwnerChanged',"
"arg0='", name, "'");
- return sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
+ return sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, signal_name_owner_changed_install_handler, u);
}
int unit_watch_bus_name(Unit *u, const char *name) {

View File

@ -0,0 +1,99 @@
From fd3ab2173da5a35660565c289675c9961be28c16 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 31 Jul 2025 18:26:09 +0200
Subject: [PATCH] sd-bus/bus-track: use install_callback in
sd_bus_track_add_name()
Previously we didn't provide any install_callback to
sd_bus_add_match_async() so in case AddMatch() method call timed out we
destroyed the bus connection. This seems overly aggressive and simply
updating the sd_bus_track object accordingly should be enough.
Follow-up for 37ce3fd2b7dd8f81f6f4bca2003961a92b2963dc.
Fixes #32381
(cherry picked from commit dcf42d1ee21222ee698e5e0ab3ecf3411b63da40)
Related: RHEL-75081
---
src/libsystemd/sd-bus/bus-track.c | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index b818e93bec..7bb92a507b 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -5,6 +5,7 @@
#include "sd-bus.h"
#include "alloc-util.h"
+#include "bus-error.h"
#include "bus-internal.h"
#include "bus-track.h"
#include "bus-util.h"
@@ -13,6 +14,7 @@ struct track_item {
unsigned n_ref;
char *name;
sd_bus_slot *slot;
+ sd_bus_track *track;
};
struct sd_bus_track {
@@ -181,18 +183,37 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
}
static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- sd_bus_track *track = userdata;
+ struct track_item *item = userdata;
const char *name;
int r;
assert(message);
- assert(track);
+ assert(item->track);
r = sd_bus_message_read(message, "sss", &name, NULL, NULL);
if (r < 0)
return 0;
- bus_track_remove_name_fully(track, name);
+ bus_track_remove_name_fully(item->track, name);
+ return 0;
+}
+
+static int name_owner_changed_install_callback(sd_bus_message *message, void *userdata, sd_bus_error *reterr_error) {
+ struct track_item *item = userdata;
+ const sd_bus_error *e;
+
+ assert(userdata);
+ assert(message);
+ assert(item->track);
+ assert(item->name);
+
+ e = sd_bus_message_get_error(message);
+ if (!e)
+ return 0;
+
+ log_debug_errno(sd_bus_error_get_errno(e), "Failed to install match for tracking name '%s': %s", item->name, e->message);
+
+ bus_track_remove_name_fully(item->track, item->name);
return 0;
}
@@ -234,13 +255,14 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
n->name = strdup(name);
if (!n->name)
return -ENOMEM;
+ n->track = track;
/* First, subscribe to this name */
match = MATCH_FOR_NAME(name);
bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
- r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
+ r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, name_owner_changed_install_callback, n);
if (r < 0) {
bus_track_add_to_queue(track);
return r;

View File

@ -0,0 +1,31 @@
From f51c9ff5bd879c5cd1a7872fbd97cc2c447c19a0 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Mon, 13 Jan 2025 17:30:51 +0100
Subject: [PATCH] core/manager: restore bus track deserialization cleanup in
manager_reload()
There's zero explanation why it got (spuriously) removed in
8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98...
(cherry picked from commit 34f4b817f67b002eae7e2c09b19bf4b66c4791b6)
Related: RHEL-75081
---
src/core/manager.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/core/manager.c b/src/core/manager.c
index 37bdac8b1d..bee94fb80d 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -3542,6 +3542,10 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0)
r = q;
+ /* Clean up deserialized bus track information. They're never consumed during reload (as opposed to
+ * reexec) since we do not disconnect from the bus. */
+ m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
+
/* Third, fire things up! */
manager_coldplug(m);

View File

@ -0,0 +1,30 @@
From ba370cdb895ee952a795e4934ef0b51fbbffe720 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Sat, 11 Jan 2025 18:38:49 +0100
Subject: [PATCH] core/manager: drop duplicate bus track deserialization
bus_init_api() now does this internally
(after 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98).
(cherry picked from commit af0e10354e567bfd0b9521376b2aad55f12a4e3d)
Related: RHEL-75081
---
src/core/manager.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/core/manager.c b/src/core/manager.c
index bee94fb80d..5212650fec 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1658,10 +1658,6 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* Connect to the bus if we are good for it */
manager_setup_bus(m);
- /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
- (void) bus_track_coldplug(m->api_bus, &m->subscribed, false, m->subscribed_as_strv);
- m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
-
/* Third, fire things up! */
manager_coldplug(m);

View File

@ -0,0 +1,31 @@
From 0b05aee0750c68bfd18bda777a3860c26cf154fc Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 24 Oct 2018 17:30:46 +0200
Subject: [PATCH] cgroup-util: add mask definitions for sets of controllers
supported by cgroupsv1 vs. cgroupsv2
(cherry picked from commit 4edd65e4cff40158dded65c010b8c3e0b5ff5519)
Related: RHEL-9322
---
src/basic/cgroup-util.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 65d2dbd4b6..1b0f53e8b8 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -43,6 +43,13 @@ typedef enum CGroupMask {
CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES),
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
+
+ /* All real cgroupv1 controllers */
+ CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
+
+ /* All real cgroupv2 controllers */
+ CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
+
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;

View File

@ -0,0 +1,51 @@
From fb30cb6cb667dc97afeadb4e0ac6ad9134f2d30a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 21 Nov 2018 17:48:41 +0100
Subject: [PATCH] cgroup: dump delegation mask too
(cherry picked from commit 0adf88b68ce71b49009d731ac6d96d9d59c4f2a9)
Related: RHEL-9322
---
src/core/unit.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/core/unit.c b/src/core/unit.c
index aedc1d806f..15c5bdf2a2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1194,17 +1194,20 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
}
+
if (u->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
}
+
m = unit_get_own_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
}
+
m = unit_get_members_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
@@ -1212,6 +1215,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
}
+ m = unit_get_delegate_mask(u);
+ if (m != 0) {
+ _cleanup_free_ char *s = NULL;
+ (void) cg_mask_to_string(m, &s);
+ fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
+ }
+
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);

View File

@ -0,0 +1,32 @@
From 3bd48135a82129199e99d212304cee4e0045302c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 21 Nov 2018 18:25:37 +0100
Subject: [PATCH] cgroup: units that aren't loaded properly should not result
in cgroup controllers being pulled in
This shouldn't make much difference in real life, but is a bit cleaner.
(cherry picked from commit 442ce7759c668bf9857eff13a90b0cfa6be8d426)
Related: RHEL-9322
---
src/core/cgroup.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 0c8a66edd1..331c97d288 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1308,7 +1308,11 @@ static CGroupMask unit_get_cgroup_mask(Unit *u) {
CGroupMask unit_get_own_mask(Unit *u) {
CGroupContext *c;
- /* Returns the mask of controllers the unit needs for itself */
+ /* Returns the mask of controllers the unit needs for itself. If a unit is not properly loaded, return an empty
+ * mask, as we shouldn't reflect it in the cgroup hierarchy then. */
+
+ if (u->load_state != UNIT_LOADED)
+ return 0;
c = unit_get_cgroup_context(u);
if (!c)

View File

@ -0,0 +1,249 @@
From 38e4b2bb1bea9b727424ee9ba30b8e1e2988a0b8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 22 Nov 2018 21:45:33 +0100
Subject: [PATCH] cgroup: be more careful with which controllers we can
enable/disable on a cgroup
This changes cg_enable_everywhere() to return which controllers are
enabled for the specified cgroup. This information is then used to
correctly track the enablement mask currently in effect for a unit.
Moreover, when we try to turn off a controller, and this works, then
this is indicates that the parent unit might succesfully turn it off
now, too as our unit might have kept it busy.
So far, when realizing cgroups, i.e. when syncing up the kernel
representation of relevant cgroups with our own idea we would strictly
work from the root to the leaves. This is generally a good approach, as
when controllers are enabled this has to happen in root-to-leaves order.
However, when controllers are disabled this has to happen in the
opposite order: in leaves-to-root order (this is because controllers can
only be enabled in a child if it is already enabled in the parent, and
if it shall be disabled in the parent then it has to be disabled in the
child first, otherwise it is considered busy when it is attempted to
remove it in the parent).
To make things complicated when invalidating a unit's cgroup membershup
systemd can actually turn off some controllers previously turned on at
the very same time as it turns on other controllers previously turned
off. In such a case we have to work up leaves-to-root *and*
root-to-leaves right after each other. With this patch this is
implemented: we still generally operate root-to-leaves, but as soon as
we noticed we successfully turned off a controller previously turned on
for a cgroup we'll re-enqueue the cgroup realization for all parents of
a unit, thus implementing leaves-to-root where necessary.
(cherry picked from commit 27adcc973771a998433635672e2eee0a4489b8a4)
Related: RHEL-9322
---
src/basic/cgroup-util.c | 58 ++++++++++++++++++++++++++++++++++++--
src/basic/cgroup-util.h | 2 +-
src/core/cgroup.c | 31 ++++++++++++++++----
src/core/cgroup.h | 3 +-
src/nspawn/nspawn-cgroup.c | 2 +-
5 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 14abe6e014..f0aca25a00 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -2573,22 +2573,45 @@ int cg_unified_flush(void) {
return cg_unified_update();
}
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
+int cg_enable_everywhere(
+ CGroupMask supported,
+ CGroupMask mask,
+ const char *p,
+ CGroupMask *ret_result_mask) {
+
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *fs = NULL;
CGroupController c;
+ CGroupMask ret = 0;
int r;
assert(p);
- if (supported == 0)
+ if (supported == 0) {
+ if (ret_result_mask)
+ *ret_result_mask = 0;
return 0;
+ }
r = cg_all_unified();
if (r < 0)
return r;
- if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
+ if (r == 0) {
+ /* On the legacy hiearchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
+ * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
+ * caller tends to use the returned mask later on to compare if all controllers where properly joined,
+ * and if not requeues realization. This use is the primary purpose of the return value, hence let's
+ * minimize surprises here and reduce triggers for re-realization by always saying we fully
+ * succeeded.) */
+ if (ret_result_mask)
+ *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
+ * CGROUP_MASK_V2: The 'supported' mask
+ * might contain pure-V1 or BPF
+ * controllers, and we never want to
+ * claim that we could enable those with
+ * cgroup.subtree_control */
return 0;
+ }
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
if (r < 0)
@@ -2620,10 +2643,39 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
if (r < 0) {
log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
clearerr(f);
+
+ /* If we can't turn off a controller, leave it on in the reported resulting mask. This
+ * happens for example when we attempt to turn off a controller up in the tree that is
+ * used down in the tree. */
+ if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
+ * only here, and not follow the same logic
+ * for other errors such as EINVAL or
+ * EOPNOTSUPP or anything else. That's
+ * because EBUSY indicates that the
+ * controllers is currently enabled and
+ * cannot be disabled because something down
+ * the hierarchy is still using it. Any other
+ * error most likely means something like "I
+ * never heard of this controller" or
+ * similar. In the former case it's hence
+ * safe to assume the controller is still on
+ * after the failed operation, while in the
+ * latter case it's safer to assume the
+ * controller is unknown and hence certainly
+ * not enabled. */
+ ret |= bit;
+ } else {
+ /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
+ if (FLAGS_SET(mask, bit))
+ ret |= bit;
}
}
}
+ /* Let's return the precise set of controllers now enabled for the cgroup. */
+ if (ret_result_mask)
+ *ret_result_mask = ret;
+
return 0;
}
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 1b0f53e8b8..0ce63f98e8 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -248,7 +248,7 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
int cg_mask_supported(CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 331c97d288..93c0920c54 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1660,8 +1660,8 @@ static int unit_create_cgroup(
bool needs_bpf) {
CGroupContext *c;
- int r;
bool created;
+ int r;
assert(u);
@@ -1685,18 +1685,37 @@ static int unit_create_cgroup(
/* Preserve enabled controllers in delegated units, adjust others. */
if (created || !unit_cgroup_delegate(u)) {
+ CGroupMask result_mask = 0;
/* Enable all controllers we need */
- r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path);
+ r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path, &result_mask);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m",
- u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
+
+ /* If we just turned off a controller, this might release the controller for our parent too, let's
+ * enqueue the parent for re-realization in that case again. */
+ if (UNIT_ISSET(u->slice)) {
+ CGroupMask turned_off;
+
+ turned_off = (u->cgroup_realized ? u->cgroup_enabled_mask & ~result_mask : 0);
+ if (turned_off != 0) {
+ Unit *parent;
+
+ /* Force the parent to propagate the enable mask to the kernel again, by invalidating
+ * the controller we just turned off. */
+
+ for (parent = UNIT_DEREF(u->slice); parent; parent = UNIT_DEREF(parent->slice))
+ unit_invalidate_cgroup(parent, turned_off);
+ }
+ }
+
+ /* Remember what's actually enabled now */
+ u->cgroup_enabled_mask = result_mask;
}
/* Keep track that this is now realized */
u->cgroup_realized = true;
u->cgroup_realized_mask = target_mask;
- u->cgroup_enabled_mask = enable_mask;
u->cgroup_bpf_state = needs_bpf ? UNIT_CGROUP_BPF_ON : UNIT_CGROUP_BPF_OFF;
if (u->type != UNIT_SLICE && !unit_cgroup_delegate(u)) {
@@ -1885,7 +1904,7 @@ static bool unit_has_mask_realized(
(!needs_bpf && u->cgroup_bpf_state == UNIT_CGROUP_BPF_OFF));
}
-static void unit_add_to_cgroup_realize_queue(Unit *u) {
+void unit_add_to_cgroup_realize_queue(Unit *u) {
assert(u);
if (u->in_cgroup_realize_queue)
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 36ea77fdc5..535e328ab6 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -175,7 +175,6 @@ CGroupMask unit_get_delegate_mask(Unit *u);
CGroupMask unit_get_members_mask(Unit *u);
CGroupMask unit_get_siblings_mask(Unit *u);
CGroupMask unit_get_subtree_mask(Unit *u);
-
CGroupMask unit_get_target_mask(Unit *u);
CGroupMask unit_get_enable_mask(Unit *u);
@@ -183,6 +182,8 @@ bool unit_get_needs_bpf(Unit *u);
void unit_update_cgroup_members_masks(Unit *u);
+void unit_add_to_cgroup_realize_queue(Unit *u);
+
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask);
char *unit_default_cgroup_path(Unit *u);
int unit_set_cgroup_path(Unit *u, const char *path);
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index a231622e29..975427aa31 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -185,6 +185,6 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
}
/* Try to enable as many controllers as possible for the new payload. */
- (void) cg_enable_everywhere(supported, supported, cgroup);
+ (void) cg_enable_everywhere(supported, supported, cgroup, NULL);
return 0;
}

View File

@ -0,0 +1,35 @@
From 70d03cffc01a77df5441e8c62e07c010e16882b2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 23 Nov 2018 01:03:18 +0100
Subject: [PATCH] cgroup: extend reasons when we realize the enable mask
After creating a cgroup we need to initialize its
"cgroup.subtree_control" file with the controllers its children want to
use. Currently we do so whenever the mkdir() on the cgroup succeeded,
i.e. when we know the cgroup is "fresh". Let's update the condition
slightly that we also do so when internally we assume a cgroup doesn't
exist yet, even if it already does (maybe left-over from a previous
run).
This shouldn't change anything IRL but make things a bit more robust.
(cherry picked from commit 1fd3a10c38a0be4fc42ae94cf9f8401003187b80)
Related: RHEL-9322
---
src/core/cgroup.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 93c0920c54..bf829407b8 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1684,7 +1684,7 @@ static int unit_create_cgroup(
(void) unit_watch_cgroup(u);
/* Preserve enabled controllers in delegated units, adjust others. */
- if (created || !unit_cgroup_delegate(u)) {
+ if (created || !u->cgroup_realized || !unit_cgroup_delegate(u)) {
CGroupMask result_mask = 0;
/* Enable all controllers we need */

View File

@ -0,0 +1,228 @@
From 18d667eb7895b47dad3b4144b69e6e9e3afe4994 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 23 Nov 2018 01:07:34 +0100
Subject: [PATCH] cgroup: drastically simplify caching of cgroups members mask
Previously we tried to be smart: when a new unit appeared and it only
added controllers to the cgroup mask we'd update the cached members mask
in all parents by ORing in the controller flags in their cached values.
Unfortunately this was quite broken, as we missed some conditions when
this cache had to be reset (for example, when a unit got unloaded),
moreover the optimization doesn't work when a controller is removed
anyway (as in that case there's no other way for the parent to iterate
though all children if any other, remaining child unit still needs it).
Hence, let's simplify the logic substantially: instead of updating the
cache on the right events (which we didn't get right), let's simply
invalidate the cache, and generate it lazily when we encounter it later.
This should actually result in better behaviour as we don't have to
calculate the new members mask for a whole subtree whever we have the
suspicion something changed, but can delay it to the point where we
actually need the members mask.
This allows us to simplify things quite a bit, which is good, since
validating this cache for correctness is hard enough.
Fixes: #9512
(cherry picked from commit 5af8805872809e6de4cc4d9495cb1a904772ab4e)
Resolves: RHEL-9322
---
src/core/cgroup.c | 49 +++++------------------------------------
src/core/cgroup.h | 1 +
src/core/dbus-mount.c | 2 +-
src/core/dbus-scope.c | 2 +-
src/core/dbus-service.c | 2 +-
src/core/dbus-slice.c | 2 +-
src/core/dbus-socket.c | 2 +-
src/core/dbus-swap.c | 2 +-
src/core/unit.c | 3 ++-
src/core/unit.h | 2 --
10 files changed, 14 insertions(+), 53 deletions(-)
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index bf829407b8..3276c876ec 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1454,53 +1454,14 @@ bool unit_get_needs_bpf(Unit *u) {
return false;
}
-/* Recurse from a unit up through its containing slices, propagating
- * mask bits upward. A unit is also member of itself. */
-void unit_update_cgroup_members_masks(Unit *u) {
- CGroupMask m;
- bool more;
-
+void unit_invalidate_cgroup_members_masks(Unit *u) {
assert(u);
- /* Calculate subtree mask */
- m = unit_get_subtree_mask(u);
-
- /* See if anything changed from the previous invocation. If
- * not, we're done. */
- if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask)
- return;
-
- more =
- u->cgroup_subtree_mask_valid &&
- ((m & ~u->cgroup_subtree_mask) != 0) &&
- ((~m & u->cgroup_subtree_mask) == 0);
-
- u->cgroup_subtree_mask = m;
- u->cgroup_subtree_mask_valid = true;
-
- if (UNIT_ISSET(u->slice)) {
- Unit *s = UNIT_DEREF(u->slice);
-
- if (more)
- /* There's more set now than before. We
- * propagate the new mask to the parent's mask
- * (not caring if it actually was valid or
- * not). */
-
- s->cgroup_members_mask |= m;
-
- else
- /* There's less set now than before (or we
- * don't know), we need to recalculate
- * everything, so let's invalidate the
- * parent's members mask */
+ /* Recurse invalidate the member masks cache all the way up the tree */
+ u->cgroup_members_mask_valid = false;
- s->cgroup_members_mask_valid = false;
-
- /* And now make sure that this change also hits our
- * grandparents */
- unit_update_cgroup_members_masks(s);
- }
+ if (UNIT_ISSET(u->slice))
+ unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
}
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 535e328ab6..2ec8dd8fba 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -180,6 +180,7 @@ CGroupMask unit_get_enable_mask(Unit *u);
bool unit_get_needs_bpf(Unit *u);
+void unit_invalidate_cgroup_members_masks(Unit *u);
void unit_update_cgroup_members_masks(Unit *u);
void unit_add_to_cgroup_realize_queue(Unit *u);
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index a089b37e04..345494fd64 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -190,7 +190,7 @@ int bus_mount_set_property(
int bus_mount_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index 534302d188..8504352cbf 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -192,7 +192,7 @@ int bus_scope_set_property(
int bus_scope_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 5f768a77c8..6db583492b 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -393,7 +393,7 @@ int bus_service_set_property(
int bus_service_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
index 722a5688a5..effd5fa5d7 100644
--- a/src/core/dbus-slice.c
+++ b/src/core/dbus-slice.c
@@ -28,7 +28,7 @@ int bus_slice_set_property(
int bus_slice_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 17494b80c8..dc74c82714 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -469,7 +469,7 @@ int bus_socket_set_property(
int bus_socket_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index b272d10113..353fa20132 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -63,7 +63,7 @@ int bus_swap_set_property(
int bus_swap_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/unit.c b/src/core/unit.c
index 15c5bdf2a2..a7f610eca8 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1583,7 +1583,8 @@ int unit_load(Unit *u) {
if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout)
log_unit_warning(u, "JobRunningTimeoutSec= is greater than JobTimeoutSec=, it has no effect.");
- unit_update_cgroup_members_masks(u);
+ /* We finished loading, let's ensure our parents recalculate the members mask */
+ unit_invalidate_cgroup_members_masks(u);
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
diff --git a/src/core/unit.h b/src/core/unit.h
index b8b914711f..e2dd7949e5 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -265,7 +265,6 @@ typedef struct Unit {
char *cgroup_path;
CGroupMask cgroup_realized_mask;
CGroupMask cgroup_enabled_mask;
- CGroupMask cgroup_subtree_mask;
CGroupMask cgroup_members_mask;
int cgroup_inotify_wd;
@@ -341,7 +340,6 @@ typedef struct Unit {
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
- bool cgroup_subtree_mask_valid:1;
UnitCGroupBPFState cgroup_bpf_state:2;

View File

@ -0,0 +1,36 @@
From 8b64f8944a9e48ca07c2fb086457675031814b46 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 23 Nov 2018 01:13:47 +0100
Subject: [PATCH] cgroup: when we unload a unit, also update all its parent's
members mask
This way we can corectly ensure that when a unit that requires some
controller goes away, we propagate the removal of it all the way up, so
that the controller is turned off in all the parents too.
(cherry picked from commit b8b6f321044ab085358de91a1f72a7d86593dfda)
Related: RHEL-9322
---
src/core/unit.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/core/unit.c b/src/core/unit.c
index a7f610eca8..70e1d68ea4 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -578,6 +578,14 @@ void unit_free(Unit *u) {
if (!u)
return;
+ if (UNIT_ISSET(u->slice)) {
+ /* A unit is being dropped from the tree, make sure our parent slice recalculates the member mask */
+ unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
+
+ /* And make sure the parent is realized again, updating cgroup memberships */
+ unit_add_to_cgroup_realize_queue(UNIT_DEREF(u->slice));
+ }
+
u->transient_file = safe_fclose(u->transient_file);
if (!MANAGER_IS_RELOADING(u->manager))

View File

@ -0,0 +1,47 @@
From a3ce5bf1ff531f29c329b3298f3ef69d99e3cbed Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 23 Nov 2018 01:15:19 +0100
Subject: [PATCH] test: extend testcase to ensure controller membership doesn't
regress
(cherry picked from commit 43738e001e64ba0ec6522e0c35b67a006abf10e9)
Related: RHEL-9322
---
test/TEST-19-DELEGATE/testsuite.sh | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/test/TEST-19-DELEGATE/testsuite.sh b/test/TEST-19-DELEGATE/testsuite.sh
index c4c948cc11..8a3e9eb5fb 100755
--- a/test/TEST-19-DELEGATE/testsuite.sh
+++ b/test/TEST-19-DELEGATE/testsuite.sh
@@ -21,10 +21,27 @@ if grep -q cgroup2 /proc/filesystems ; then
-w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
+ grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+ grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+
+ # "io" is not among the controllers enabled by default for all units, verify that
+ grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+ # Run a service with "io" enabled, and verify it works
+ systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice" \
+ grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
+
+ # We want to check if "io" is removed again from the controllers
+ # list. However, PID 1 (rightfully) does this asynchronously. In order
+ # to force synchronization on this, let's start a short-lived service
+ # which requires PID 1 to refresh the cgroup tree, so that we can
+ # verify that this all works.
+ systemd-run --wait --unit=test4.service true
+
+ # And now check again, "io" should have vanished
+ grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
# Check that unprivileged delegation works for scopes
test_scope_unpriv_delegation

View File

@ -0,0 +1,41 @@
From edc3ca8a481a918f61404a7d41aa328c247721cd Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Tue, 2 Sep 2025 13:41:07 +0200
Subject: [PATCH] test-execute: let's ignore the difference between CLD_KILLED
and CLD_DUMPED
Depending on system configuration and whether SCMP_ACT_KILL_PROCESS or SCMP_ACT_KILL_THREAD is available/used processes might coredump on specific coredumps or are just plain killed. For our test case the difference doesn't really matter, hence let's hide it away.
(cherry picked from commit c3ab2c389ee60d92fb8d7fe779ae9c4e3c092e4c)
Related: RHEL-108744
---
src/test/test-execute.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 7581d5ed68..97649ee5ec 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -30,6 +30,12 @@
typedef void (*test_function_t)(Manager *m);
+static int cld_dumped_to_killed(int code) {
+ /* Depending on the system, seccomp version, … some signals might result in dumping, others in plain
+ * killing. Let's ignore the difference here, and map both cases to CLD_KILLED */
+ return code == CLD_DUMPED ? CLD_KILLED : code;
+}
+
static void wait_for_service_finish(Manager *m, Unit *unit) {
Service *service = NULL;
usec_t ts;
@@ -73,7 +79,7 @@ static void check_main_result(const char *func, Manager *m, Unit *unit, int stat
service->main_exec_status.status, status_expected);
abort();
}
- if (service->main_exec_status.code != code_expected) {
+ if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
log_error("%s: %s: exit code %d, expected %d",
func, unit->id,
service->main_exec_status.code, code_expected);

View File

@ -0,0 +1,37 @@
From bbec8198d56f141ab3e97dca3607744e2497bf9a Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Tue, 2 Sep 2025 13:41:22 +0200
Subject: [PATCH] test-execute: turn off coredump generation in test services
These services are likely to coredump, and we expect that but aren't interested in the coredump. Hence let's turn off processing by setting RLIMIT_CORE to 0/0.
(cherry picked from commit a429223d1767a9d6cb0a95fd2fa3b0d64ce3d4e7)
Related: RHEL-108744
---
test/test-execute/exec-systemcallfilter-failing.service | 1 +
test/test-execute/exec-systemcallfilter-failing2.service | 1 +
2 files changed, 2 insertions(+)
diff --git a/test/test-execute/exec-systemcallfilter-failing.service b/test/test-execute/exec-systemcallfilter-failing.service
index bcebc99507..996f859217 100644
--- a/test/test-execute/exec-systemcallfilter-failing.service
+++ b/test/test-execute/exec-systemcallfilter-failing.service
@@ -4,6 +4,7 @@ Description=Test for SystemCallFilter
[Service]
ExecStart=/bin/sh -c 'echo "This should not be seen"'
Type=oneshot
+LimitCORE=0
SystemCallFilter=ioperm
SystemCallFilter=~ioperm
SystemCallFilter=ioperm
diff --git a/test/test-execute/exec-systemcallfilter-failing2.service b/test/test-execute/exec-systemcallfilter-failing2.service
index 2fdc0ed772..c74f42248b 100644
--- a/test/test-execute/exec-systemcallfilter-failing2.service
+++ b/test/test-execute/exec-systemcallfilter-failing2.service
@@ -4,4 +4,5 @@ Description=Test for SystemCallFilter
[Service]
ExecStart=/bin/sh -c 'echo "This should not be seen"'
Type=oneshot
+LimitCORE=0
SystemCallFilter=~write open execve exit_group close mmap munmap fstat DONOTEXIST

View File

@ -0,0 +1,106 @@
From 7097abc3e9a0b76942b6844d43152e37f22158cf Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Mon, 29 Sep 2025 13:37:02 +0200
Subject: [PATCH] test: introduce TEST-53-TIMER
This tries to mirror upstream's
953c347fb6f293acbd6da009646bfc071b68ddd7, but we don't have
TEST-53-ISSUE-16347 in RHEL 8 at all, so just introduce a new empty test
instead.
rhel-only
Related: RHEL-108744
---
test/TEST-53-TIMER/Makefile | 1 +
test/TEST-53-TIMER/test.sh | 51 +++++++++++++++++++++++++++++++++
test/TEST-53-TIMER/testsuite.sh | 14 +++++++++
3 files changed, 66 insertions(+)
create mode 120000 test/TEST-53-TIMER/Makefile
create mode 100755 test/TEST-53-TIMER/test.sh
create mode 100755 test/TEST-53-TIMER/testsuite.sh
diff --git a/test/TEST-53-TIMER/Makefile b/test/TEST-53-TIMER/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-53-TIMER/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-53-TIMER/test.sh b/test/TEST-53-TIMER/test.sh
new file mode 100755
index 0000000000..3619ff92b4
--- /dev/null
+++ b/test/TEST-53-TIMER/test.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="Tests for systemd timers"
+TEST_NO_NSPAWN=1
+
+# shellcheck source=test/test-functions
+. "$TEST_BASE_DIR/test-functions"
+
+test_setup() {
+ create_empty_image
+ mkdir -p "${TESTDIR:?}/root"
+ mount "${LOOPDEV:?}p1" "$TESTDIR/root"
+
+ (
+ LOG_LEVEL=5
+ # shellcheck disable=SC2046
+ eval $(udevadm info --export --query=env --name="${LOOPDEV}p2")
+
+ setup_basic_environment
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-hwdb-update.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-journal-catalog-update.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-networkd.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-networkd.socket"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-resolved.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-machined.service"
+
+ # setup the testsuite service
+ cat >"$initdir/etc/systemd/system/testsuite.service" <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+NotifyAccess=all
+EOF
+ cp testsuite*.sh "$initdir/"
+
+ setup_testsuite
+ ) || return 1
+ setup_nspawn_root
+
+ ddebug "umount $TESTDIR/root"
+ umount "$TESTDIR/root"
+}
+
+do_test "$@"
diff --git a/test/TEST-53-TIMER/testsuite.sh b/test/TEST-53-TIMER/testsuite.sh
new file mode 100755
index 0000000000..13c767e490
--- /dev/null
+++ b/test/TEST-53-TIMER/testsuite.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+: >/failed
+
+for script in "${0%.sh}".*.sh; do
+ echo "Running $script"
+ "./$script"
+done
+
+touch /testok
+rm /failed

View File

@ -0,0 +1,98 @@
From 9cb832f3c26c292d31109cb54d142277f8751e52 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 23 Sep 2025 17:42:01 +0200
Subject: [PATCH] test: restarting elapsed timer shouldn't trigger the
corresponding service
Provides coverage for:
- https://github.com/systemd/systemd/issues/31231
- https://github.com/systemd/systemd/issues/35805
(cherry picked from commit 5730a400fd5ee82566fe03eb832121a0d4bc26b6)
Related: RHEL-108744
---
.../testsuite.restart-trigger.sh | 74 +++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100755 test/TEST-53-TIMER/testsuite.restart-trigger.sh
diff --git a/test/TEST-53-TIMER/testsuite.restart-trigger.sh b/test/TEST-53-TIMER/testsuite.restart-trigger.sh
new file mode 100755
index 0000000000..313b5a7fd8
--- /dev/null
+++ b/test/TEST-53-TIMER/testsuite.restart-trigger.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Restarting an already elapsed timer shouldn't immediately trigger the corresponding service unit.
+#
+# Provides coverage for:
+# - https://github.com/systemd/systemd/issues/31231
+# - https://github.com/systemd/systemd/issues/35805
+set -eux
+set -o pipefail
+
+UNIT_NAME="timer-restart-$RANDOM"
+TEST_MESSAGE="Hello from timer $RANDOM"
+
+# Setup
+cat >"/run/systemd/system/$UNIT_NAME.timer" <<EOF
+[Timer]
+OnCalendar=$(date --date="+1 hour" "+%Y-%m-%d %H:%M:%S")
+AccuracySec=1s
+EOF
+
+cat >"/run/systemd/system/$UNIT_NAME.service" <<EOF
+[Service]
+ExecStart=echo "$TEST_MESSAGE"
+EOF
+
+systemctl daemon-reload
+
+JOURNAL_TS="$(date "+%s")"
+# Paranoia check that the test message is not already in the logs
+[[ "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" -eq 0 ]]
+
+# Restart time timer and move time forward by 2 hours to trigger the timer
+systemctl restart "$UNIT_NAME.timer"
+systemctl status --no-pager "$UNIT_NAME.timer"
+
+date -s '+2 hours'
+trap 'date -s "-2 hours"' EXIT
+sleep 1
+systemctl status --no-pager "$UNIT_NAME.timer"
+[[ "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" -eq 1 ]]
+
+# Restarting the timer unit shouldn't trigger neither the timer nor the service, so these
+# fields should remain constant through the following tests
+SERVICE_INV_ID="$(systemctl show --property=InvocationID "$UNIT_NAME.service")"
+TIMER_LAST_TRIGGER="$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")"
+
+# Now restart the timer and check if the timer and the service weren't triggered again
+systemctl restart "$UNIT_NAME.timer"
+sleep 5
+[[ "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" -eq 1 ]]
+[[ "$SERVICE_INV_ID" == "$(systemctl show --property=InvocationID "$UNIT_NAME.service")" ]]
+[[ "$TIMER_LAST_TRIGGER" == "$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")" ]]
+
+# Set the timer into the past, restart it, and again check if it wasn't triggered
+TIMER_TS="$(date --date="-1 day" "+%Y-%m-%d %H:%M:%S")"
+mkdir "/run/systemd/system/$UNIT_NAME.timer.d/"
+cat >"/run/systemd/system/$UNIT_NAME.timer.d/99-override.conf" <<EOF
+[Timer]
+OnCalendar=$TIMER_TS
+EOF
+systemctl daemon-reload
+systemctl status --no-pager "$UNIT_NAME.timer"
+[[ "$(systemctl show -p TimersCalendar "$UNIT_NAME".timer)" == *"OnCalendar=$TIMER_TS"* ]]
+systemctl restart "$UNIT_NAME.timer"
+sleep 5
+[[ "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" -eq 1 ]]
+[[ "$SERVICE_INV_ID" == "$(systemctl show --property=InvocationID "$UNIT_NAME.service")" ]]
+[[ "$TIMER_LAST_TRIGGER" == "$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")" ]]
+
+# Cleanup
+systemctl stop "$UNIT_NAME".{timer,service}
+rm -f "/run/systemd/system/$UNIT_NAME".{timer,service}
+systemctl daemon-reload

View File

@ -0,0 +1,137 @@
From fdd763b42d864b4f7777fe9394a29c520c613d78 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 23 Sep 2025 21:04:12 +0200
Subject: [PATCH] test: check the next elapse timer timestamp after
deserialization
When deserializing a serialized timer unit with RandomizedDelaySec= set,
systemd should use the last inactive exit timestamp instead of current
realtime to calculate the new next elapse, so the timer unit actually
runs in the given calendar window.
Provides coverage for:
- https://github.com/systemd/systemd/issues/18678
- https://github.com/systemd/systemd/pull/27752
(cherry picked from commit f4c3c107d9be4e922a080fc292ed3889c4e0f4a5)
Related: RHEL-108744
---
.../testsuite.RandomizedDelaySec-reload.sh | 94 +++++++++++++++++++
test/test-functions | 2 +-
2 files changed, 95 insertions(+), 1 deletion(-)
create mode 100755 test/TEST-53-TIMER/testsuite.RandomizedDelaySec-reload.sh
diff --git a/test/TEST-53-TIMER/testsuite.RandomizedDelaySec-reload.sh b/test/TEST-53-TIMER/testsuite.RandomizedDelaySec-reload.sh
new file mode 100755
index 0000000000..54b90d3dd1
--- /dev/null
+++ b/test/TEST-53-TIMER/testsuite.RandomizedDelaySec-reload.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# When deserializing a serialized timer unit with RandomizedDelaySec= set, systemd should use the last
+# inactive exit timestamp instead of current realtime to calculate the new next elapse, so the timer unit
+# actually runs in the given calendar window.
+#
+# Provides coverage for:
+# - https://github.com/systemd/systemd/issues/18678
+# - https://github.com/systemd/systemd/pull/27752
+set -eux
+set -o pipefail
+
+UNIT_NAME="timer-RandomizedDelaySec-$RANDOM"
+TARGET_TS="$(date --date="tomorrow 00:10")"
+TARGET_TS_S="$(date --date="$TARGET_TS" "+%s")"
+# Maximum possible next elapse timestamp: $TARGET_TS (OnCalendar=) + 22 hours (RandomizedDelaySec=)
+MAX_NEXT_ELAPSE_REALTIME_S="$((TARGET_TS_S + 22 * 60 * 60))"
+MAX_NEXT_ELAPSE_REALTIME="$(date --date="@$MAX_NEXT_ELAPSE_REALTIME_S")"
+
+# Let's make sure to return the date & time back to the original state once we're done with our time
+# shenigans. One way to do this would be to use hwclock, but the RTC in VMs can be unreliable or slow to
+# respond, causing unexpected test fails/timeouts.
+#
+# Instead, let's save the realtime timestamp before we start with the test together with a current monotonic
+# timestamp, after the test ends take the difference between the current monotonic timestamp and the "start"
+# one, add it to the originally saved realtime timestamp, and finally use that timestamp to set the system
+# time. This should advance the system time by the amount of time the test actually ran, and hence restore it
+# to some sane state after the time jumps performed by the test. It won't be perfect, but it should be close
+# enough for our needs.
+START_REALTIME="$(date "+%s")"
+START_MONOTONIC="$(cut -d . -f 1 /proc/uptime)"
+at_exit() {
+ : "Restore the system date to a sane state"
+ END_MONOTONIC="$(cut -d . -f 1 /proc/uptime)"
+ date --set="@$((START_REALTIME + END_MONOTONIC - START_MONOTONIC))"
+}
+trap at_exit EXIT
+
+# Set some predictable time so we can schedule the first timer elapse in a deterministic-ish way
+date --set="23:00"
+
+# Setup
+cat >"/run/systemd/system/$UNIT_NAME.timer" <<EOF
+[Timer]
+# Run this timer daily, ten minutes after midnight
+OnCalendar=*-*-* 00:10:00
+RandomizedDelaySec=22h
+AccuracySec=1ms
+EOF
+
+cat >"/run/systemd/system/$UNIT_NAME.service" <<EOF
+[Service]
+ExecStart=echo "Hello world"
+EOF
+
+systemctl daemon-reload
+
+check_elapse_timestamp() {
+ systemctl status --no-pager "$UNIT_NAME.timer"
+ systemctl show -p InactiveExitTimestamp --no-pager "$UNIT_NAME.timer"
+
+ NEXT_ELAPSE_REALTIME="$(systemctl show -p NextElapseUSecRealtime "$UNIT_NAME.timer" | cut -d = -f 2)"
+ NEXT_ELAPSE_REALTIME_S="$(date --date="$NEXT_ELAPSE_REALTIME" "+%s")"
+ : "Next elapse timestamp should be $TARGET_TS <= $NEXT_ELAPSE_REALTIME <= $MAX_NEXT_ELAPSE_REALTIME"
+ [[ "$NEXT_ELAPSE_REALTIME_S" -ge "$TARGET_TS_S" ]]
+ [[ "$NEXT_ELAPSE_REALTIME_S" -le "$MAX_NEXT_ELAPSE_REALTIME_S" ]]
+}
+
+# Restart the timer unit and check the initial next elapse timestamp
+: "Initial next elapse timestamp"
+systemctl restart "$UNIT_NAME.timer"
+check_elapse_timestamp
+
+# Bump the system date to 1 minute after the original calendar timer would've expired (without any random
+# delay!) - systemd should recalculate the next elapse timestamp with a new randomized delay, but it should
+# use the original inactive exit timestamp as a "base", so the final timestamp should not end up beyond the
+# original calendar timestamp + randomized delay range.
+#
+# Similarly, do the same check after doing daemon-reload, as that also forces systemd to recalculate the next
+# elapse timestamp (this goes through a slightly different codepath that actually contained the original
+# issue).
+: "Next elapse timestamp after time jump"
+date -s "tomorrow 00:11"
+check_elapse_timestamp
+
+: "Next elapse timestamp after daemon-reload"
+systemctl daemon-reload
+check_elapse_timestamp
+
+# Cleanup
+systemctl stop "$UNIT_NAME".{timer,service}
+rm -f "/run/systemd/system/$UNIT_NAME".{timer,service}
+systemctl daemon-reload
diff --git a/test/test-functions b/test/test-functions
index 2345ab6e8a..f367994fe5 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -23,7 +23,7 @@ fi
PATH_TO_INIT=$ROOTLIBDIR/systemd
-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs env mktemp mountpoint useradd userdel timeout jq wc awk diff dirname readlink"
+BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs env mktemp mountpoint useradd userdel timeout jq wc awk diff dirname readlink cut"
DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"
STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"

View File

@ -0,0 +1,29 @@
From 0c0bcf0d2d812fe2fe755a30c32421e8d8c6993a Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnykryn@redhat.com>
Date: Tue, 9 Sep 2025 15:24:22 +0200
Subject: [PATCH] timer: don't run service immediately after restart of a timer
When a timer is restarted, don't reset the last_trigger field.
This prevents the timer from triggering immediately.
Fixes: #31231
(cherry picked from commit 3fc44a0f68412b649e16f12ff2f97a36c615457d)
Resolves: RHEL-108744
---
src/core/timer.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/core/timer.c b/src/core/timer.c
index 81468d4ca6..ef48a62781 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -601,8 +601,6 @@ static int timer_start(Unit *u) {
if (r < 0)
return r;
- t->last_trigger = DUAL_TIMESTAMP_NULL;
-
/* Reenable all timers that depend on unit activation time */
LIST_FOREACH(value, v, t->values)
if (v->base == TIMER_ACTIVE)

View File

@ -0,0 +1,47 @@
From 3aa7f6e335b388a38ef756ecd2acdf545894d3fb Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Thu, 2 Oct 2025 10:06:01 +0200
Subject: [PATCH] Revert "test: extend testcase to ensure controller membership
doesn't regress"
This reverts commit a3ce5bf1ff531f29c329b3298f3ef69d99e3cbed.
Related: RHEL-9322
---
test/TEST-19-DELEGATE/testsuite.sh | 21 ++-------------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/test/TEST-19-DELEGATE/testsuite.sh b/test/TEST-19-DELEGATE/testsuite.sh
index 8a3e9eb5fb..c4c948cc11 100755
--- a/test/TEST-19-DELEGATE/testsuite.sh
+++ b/test/TEST-19-DELEGATE/testsuite.sh
@@ -21,27 +21,10 @@ if grep -q cgroup2 /proc/filesystems ; then
-w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
+ grep memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
-
- # "io" is not among the controllers enabled by default for all units, verify that
- grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
- # Run a service with "io" enabled, and verify it works
- systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice" \
- grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
-
- # We want to check if "io" is removed again from the controllers
- # list. However, PID 1 (rightfully) does this asynchronously. In order
- # to force synchronization on this, let's start a short-lived service
- # which requires PID 1 to refresh the cgroup tree, so that we can
- # verify that this all works.
- systemd-run --wait --unit=test4.service true
-
- # And now check again, "io" should have vanished
- grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+ grep pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
# Check that unprivileged delegation works for scopes
test_scope_unpriv_delegation

View File

@ -13,7 +13,7 @@
Name: systemd
Url: http://www.freedesktop.org/wiki/Software/systemd
Version: 239
Release: 82%{?dist}.5
Release: 82%{?dist}.8
# For a breakdown of the licensing, see README
License: LGPLv2+ and MIT and GPLv2+
Summary: System and Service Manager
@ -1075,6 +1075,28 @@ Patch1022: 1022-core-fix-member-access-within-null-pointer.patch
Patch1023: 1023-man-be-even-clearer-that-tmpfiles-user-group-mode-ar.patch
Patch1024: 1024-Revert-man-fix-description-of-force-in-halt-8-7392.patch
Patch1025: 1025-man-explicitly-document-that-reboot-f-is-different-f.patch
Patch1026: 1026-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch
Patch1027: 1027-manager-s-deserialized_subscribed-subscribed_as_strv.patch
Patch1028: 1028-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch
Patch1029: 1029-core-do-not-disconnect-from-bus-when-failed-to-insta.patch
Patch1030: 1030-sd-bus-bus-track-use-install_callback-in-sd_bus_trac.patch
Patch1031: 1031-core-manager-restore-bus-track-deserialization-clean.patch
Patch1032: 1032-core-manager-drop-duplicate-bus-track-deserializatio.patch
Patch1033: 1033-cgroup-util-add-mask-definitions-for-sets-of-control.patch
Patch1034: 1034-cgroup-dump-delegation-mask-too.patch
Patch1035: 1035-cgroup-units-that-aren-t-loaded-properly-should-not-.patch
Patch1036: 1036-cgroup-be-more-careful-with-which-controllers-we-can.patch
Patch1037: 1037-cgroup-extend-reasons-when-we-realize-the-enable-mas.patch
Patch1038: 1038-cgroup-drastically-simplify-caching-of-cgroups-membe.patch
Patch1039: 1039-cgroup-when-we-unload-a-unit-also-update-all-its-par.patch
Patch1040: 1040-test-extend-testcase-to-ensure-controller-membership.patch
Patch1041: 1041-test-execute-let-s-ignore-the-difference-between-CLD.patch
Patch1042: 1042-test-execute-turn-off-coredump-generation-in-test-se.patch
Patch1043: 1043-test-introduce-TEST-53-TIMER.patch
Patch1044: 1044-test-restarting-elapsed-timer-shouldn-t-trigger-the-.patch
Patch1045: 1045-test-check-the-next-elapse-timer-timestamp-after-des.patch
Patch1046: 1046-timer-don-t-run-service-immediately-after-restart-of.patch
Patch1047: 1047-Revert-test-extend-testcase-to-ensure-controller-mem.patch
%ifarch %{ix86} x86_64 aarch64
%global have_gnu_efi 1
@ -1701,6 +1723,34 @@ fi
%files tests -f .file-list-tests
%changelog
* Thu Oct 02 2025 systemd maintenance team <systemd-maint@redhat.com> - 239-82.8
- test-execute: let's ignore the difference between CLD_KILLED and CLD_DUMPED (RHEL-108744)
- test-execute: turn off coredump generation in test services (RHEL-108744)
- test: introduce TEST-53-TIMER (RHEL-108744)
- test: restarting elapsed timer shouldn't trigger the corresponding service (RHEL-108744)
- test: check the next elapse timer timestamp after deserialization (RHEL-108744)
- timer: don't run service immediately after restart of a timer (RHEL-108744)
- Revert "test: extend testcase to ensure controller membership doesn't regress" (RHEL-9322)
* Fri Aug 29 2025 systemd maintenance team <systemd-maint@redhat.com> - 239-82.7
- cgroup-util: add mask definitions for sets of controllers supported by cgroupsv1 vs. cgroupsv2 (RHEL-9322)
- cgroup: dump delegation mask too (RHEL-9322)
- cgroup: units that aren't loaded properly should not result in cgroup controllers being pulled in (RHEL-9322)
- cgroup: be more careful with which controllers we can enable/disable on a cgroup (RHEL-9322)
- cgroup: extend reasons when we realize the enable mask (RHEL-9322)
- cgroup: drastically simplify caching of cgroups members mask (RHEL-9322)
- cgroup: when we unload a unit, also update all its parent's members mask (RHEL-9322)
- test: extend testcase to ensure controller membership doesn't regress (RHEL-9322)
* Mon Aug 25 2025 systemd maintenance team <systemd-maint@redhat.com> - 239-82.6
- dbus: stash the subscriber list when we disconenct from the bus (RHEL-75081)
- manager: s/deserialized_subscribed/subscribed_as_strv (RHEL-75081)
- bus-util: do not reset the count returned by sd_bus_track_count_name() (RHEL-75081)
- core: do not disconnect from bus when failed to install signal match (RHEL-75081)
- sd-bus/bus-track: use install_callback in sd_bus_track_add_name() (RHEL-75081)
- core/manager: restore bus track deserialization cleanup in manager_reload() (RHEL-75081)
- core/manager: drop duplicate bus track deserialization (RHEL-75081)
* Wed Mar 05 2025 systemd maintenance team <systemd-maint@redhat.com> - 239-82.5
- man: be even clearer that tmpfiles user/group/mode are applied on existing inodes (RHEL-77145)
- Revert "man: fix description of --force in halt(8) (#7392)" (RHEL-81056)