From a4c73665dde5b2a3b6675dca77d32ac43f125da2 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 4 Nov 2025 08:05:26 +0000 Subject: [PATCH] import UBI systemd-239-82.el8_10.8 --- ...ubscriber-list-when-we-disconenct-fr.patch | 171 ++++++++++++ ...alized_subscribed-subscribed_as_strv.patch | 87 ++++++ ...reset-the-count-returned-by-sd_bus_t.patch | 55 ++++ ...onnect-from-bus-when-failed-to-insta.patch | 78 ++++++ ...-use-install_callback-in-sd_bus_trac.patch | 99 +++++++ ...tore-bus-track-deserialization-clean.patch | 31 +++ ...p-duplicate-bus-track-deserializatio.patch | 30 +++ ...mask-definitions-for-sets-of-control.patch | 31 +++ ...1034-cgroup-dump-delegation-mask-too.patch | 51 ++++ ...t-aren-t-loaded-properly-should-not-.patch | 32 +++ ...areful-with-which-controllers-we-can.patch | 249 ++++++++++++++++++ ...asons-when-we-realize-the-enable-mas.patch | 35 +++ ...ly-simplify-caching-of-cgroups-membe.patch | 228 ++++++++++++++++ ...nload-a-unit-also-update-all-its-par.patch | 36 +++ ...case-to-ensure-controller-membership.patch | 47 ++++ ...-s-ignore-the-difference-between-CLD.patch | 41 +++ ...n-off-coredump-generation-in-test-se.patch | 37 +++ .../1043-test-introduce-TEST-53-TIMER.patch | 106 ++++++++ ...elapsed-timer-shouldn-t-trigger-the-.patch | 98 +++++++ ...ext-elapse-timer-timestamp-after-des.patch | 137 ++++++++++ ...service-immediately-after-restart-of.patch | 29 ++ ...nd-testcase-to-ensure-controller-mem.patch | 47 ++++ SPECS/systemd.spec | 52 +++- 23 files changed, 1806 insertions(+), 1 deletion(-) create mode 100644 SOURCES/1026-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch create mode 100644 SOURCES/1027-manager-s-deserialized_subscribed-subscribed_as_strv.patch create mode 100644 SOURCES/1028-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch create mode 100644 SOURCES/1029-core-do-not-disconnect-from-bus-when-failed-to-insta.patch create mode 100644 SOURCES/1030-sd-bus-bus-track-use-install_callback-in-sd_bus_trac.patch create mode 100644 SOURCES/1031-core-manager-restore-bus-track-deserialization-clean.patch create mode 100644 SOURCES/1032-core-manager-drop-duplicate-bus-track-deserializatio.patch create mode 100644 SOURCES/1033-cgroup-util-add-mask-definitions-for-sets-of-control.patch create mode 100644 SOURCES/1034-cgroup-dump-delegation-mask-too.patch create mode 100644 SOURCES/1035-cgroup-units-that-aren-t-loaded-properly-should-not-.patch create mode 100644 SOURCES/1036-cgroup-be-more-careful-with-which-controllers-we-can.patch create mode 100644 SOURCES/1037-cgroup-extend-reasons-when-we-realize-the-enable-mas.patch create mode 100644 SOURCES/1038-cgroup-drastically-simplify-caching-of-cgroups-membe.patch create mode 100644 SOURCES/1039-cgroup-when-we-unload-a-unit-also-update-all-its-par.patch create mode 100644 SOURCES/1040-test-extend-testcase-to-ensure-controller-membership.patch create mode 100644 SOURCES/1041-test-execute-let-s-ignore-the-difference-between-CLD.patch create mode 100644 SOURCES/1042-test-execute-turn-off-coredump-generation-in-test-se.patch create mode 100644 SOURCES/1043-test-introduce-TEST-53-TIMER.patch create mode 100644 SOURCES/1044-test-restarting-elapsed-timer-shouldn-t-trigger-the-.patch create mode 100644 SOURCES/1045-test-check-the-next-elapse-timer-timestamp-after-des.patch create mode 100644 SOURCES/1046-timer-don-t-run-service-immediately-after-restart-of.patch create mode 100644 SOURCES/1047-Revert-test-extend-testcase-to-ensure-controller-mem.patch diff --git a/SOURCES/1026-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch b/SOURCES/1026-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch new file mode 100644 index 0000000..476bd85 --- /dev/null +++ b/SOURCES/1026-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch @@ -0,0 +1,171 @@ +From dd01fc11f599dbfe0a1d5a42e60f9364ae991d31 Mon Sep 17 00:00:00 2001 +From: Ronan Pigott +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) { diff --git a/SOURCES/1027-manager-s-deserialized_subscribed-subscribed_as_strv.patch b/SOURCES/1027-manager-s-deserialized_subscribed-subscribed_as_strv.patch new file mode 100644 index 0000000..ea2704c --- /dev/null +++ b/SOURCES/1027-manager-s-deserialized_subscribed-subscribed_as_strv.patch @@ -0,0 +1,87 @@ +From 1cf170260b78e550ed0a0cd3b729527852de0991 Mon Sep 17 00:00:00 2001 +From: Ronan Pigott +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 */ diff --git a/SOURCES/1028-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch b/SOURCES/1028-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch new file mode 100644 index 0000000..3753554 --- /dev/null +++ b/SOURCES/1028-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch @@ -0,0 +1,55 @@ +From 42126f36367c4bcb39e37ba251ffbc1f04c9092b Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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) { diff --git a/SOURCES/1029-core-do-not-disconnect-from-bus-when-failed-to-insta.patch b/SOURCES/1029-core-do-not-disconnect-from-bus-when-failed-to-insta.patch new file mode 100644 index 0000000..7e3c47f --- /dev/null +++ b/SOURCES/1029-core-do-not-disconnect-from-bus-when-failed-to-insta.patch @@ -0,0 +1,78 @@ +From c322f6916c2a69d31d124b59a295297cd3ca492d Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +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) { diff --git a/SOURCES/1030-sd-bus-bus-track-use-install_callback-in-sd_bus_trac.patch b/SOURCES/1030-sd-bus-bus-track-use-install_callback-in-sd_bus_trac.patch new file mode 100644 index 0000000..59f6b73 --- /dev/null +++ b/SOURCES/1030-sd-bus-bus-track-use-install_callback-in-sd_bus_trac.patch @@ -0,0 +1,99 @@ +From fd3ab2173da5a35660565c289675c9961be28c16 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +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; diff --git a/SOURCES/1031-core-manager-restore-bus-track-deserialization-clean.patch b/SOURCES/1031-core-manager-restore-bus-track-deserialization-clean.patch new file mode 100644 index 0000000..1ffee8c --- /dev/null +++ b/SOURCES/1031-core-manager-restore-bus-track-deserialization-clean.patch @@ -0,0 +1,31 @@ +From f51c9ff5bd879c5cd1a7872fbd97cc2c447c19a0 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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); + diff --git a/SOURCES/1032-core-manager-drop-duplicate-bus-track-deserializatio.patch b/SOURCES/1032-core-manager-drop-duplicate-bus-track-deserializatio.patch new file mode 100644 index 0000000..461b2ea --- /dev/null +++ b/SOURCES/1032-core-manager-drop-duplicate-bus-track-deserializatio.patch @@ -0,0 +1,30 @@ +From ba370cdb895ee952a795e4934ef0b51fbbffe720 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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); + diff --git a/SOURCES/1033-cgroup-util-add-mask-definitions-for-sets-of-control.patch b/SOURCES/1033-cgroup-util-add-mask-definitions-for-sets-of-control.patch new file mode 100644 index 0000000..519665b --- /dev/null +++ b/SOURCES/1033-cgroup-util-add-mask-definitions-for-sets-of-control.patch @@ -0,0 +1,31 @@ +From 0b05aee0750c68bfd18bda777a3860c26cf154fc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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; + diff --git a/SOURCES/1034-cgroup-dump-delegation-mask-too.patch b/SOURCES/1034-cgroup-dump-delegation-mask-too.patch new file mode 100644 index 0000000..eeef3ed --- /dev/null +++ b/SOURCES/1034-cgroup-dump-delegation-mask-too.patch @@ -0,0 +1,51 @@ +From fb30cb6cb667dc97afeadb4e0ac6ad9134f2d30a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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); + diff --git a/SOURCES/1035-cgroup-units-that-aren-t-loaded-properly-should-not-.patch b/SOURCES/1035-cgroup-units-that-aren-t-loaded-properly-should-not-.patch new file mode 100644 index 0000000..ee66f15 --- /dev/null +++ b/SOURCES/1035-cgroup-units-that-aren-t-loaded-properly-should-not-.patch @@ -0,0 +1,32 @@ +From 3bd48135a82129199e99d212304cee4e0045302c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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) diff --git a/SOURCES/1036-cgroup-be-more-careful-with-which-controllers-we-can.patch b/SOURCES/1036-cgroup-be-more-careful-with-which-controllers-we-can.patch new file mode 100644 index 0000000..71f4e24 --- /dev/null +++ b/SOURCES/1036-cgroup-be-more-careful-with-which-controllers-we-can.patch @@ -0,0 +1,249 @@ +From 38e4b2bb1bea9b727424ee9ba30b8e1e2988a0b8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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; + } diff --git a/SOURCES/1037-cgroup-extend-reasons-when-we-realize-the-enable-mas.patch b/SOURCES/1037-cgroup-extend-reasons-when-we-realize-the-enable-mas.patch new file mode 100644 index 0000000..808adab --- /dev/null +++ b/SOURCES/1037-cgroup-extend-reasons-when-we-realize-the-enable-mas.patch @@ -0,0 +1,35 @@ +From 70d03cffc01a77df5441e8c62e07c010e16882b2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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 */ diff --git a/SOURCES/1038-cgroup-drastically-simplify-caching-of-cgroups-membe.patch b/SOURCES/1038-cgroup-drastically-simplify-caching-of-cgroups-membe.patch new file mode 100644 index 0000000..1eb9c60 --- /dev/null +++ b/SOURCES/1038-cgroup-drastically-simplify-caching-of-cgroups-membe.patch @@ -0,0 +1,228 @@ +From 18d667eb7895b47dad3b4144b69e6e9e3afe4994 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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; + diff --git a/SOURCES/1039-cgroup-when-we-unload-a-unit-also-update-all-its-par.patch b/SOURCES/1039-cgroup-when-we-unload-a-unit-also-update-all-its-par.patch new file mode 100644 index 0000000..c1e9a20 --- /dev/null +++ b/SOURCES/1039-cgroup-when-we-unload-a-unit-also-update-all-its-par.patch @@ -0,0 +1,36 @@ +From 8b64f8944a9e48ca07c2fb086457675031814b46 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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)) diff --git a/SOURCES/1040-test-extend-testcase-to-ensure-controller-membership.patch b/SOURCES/1040-test-extend-testcase-to-ensure-controller-membership.patch new file mode 100644 index 0000000..eff18ef --- /dev/null +++ b/SOURCES/1040-test-extend-testcase-to-ensure-controller-membership.patch @@ -0,0 +1,47 @@ +From a3ce5bf1ff531f29c329b3298f3ef69d99e3cbed Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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 diff --git a/SOURCES/1041-test-execute-let-s-ignore-the-difference-between-CLD.patch b/SOURCES/1041-test-execute-let-s-ignore-the-difference-between-CLD.patch new file mode 100644 index 0000000..69bdb17 --- /dev/null +++ b/SOURCES/1041-test-execute-let-s-ignore-the-difference-between-CLD.patch @@ -0,0 +1,41 @@ +From edc3ca8a481a918f61404a7d41aa328c247721cd Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +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); diff --git a/SOURCES/1042-test-execute-turn-off-coredump-generation-in-test-se.patch b/SOURCES/1042-test-execute-turn-off-coredump-generation-in-test-se.patch new file mode 100644 index 0000000..609c17d --- /dev/null +++ b/SOURCES/1042-test-execute-turn-off-coredump-generation-in-test-se.patch @@ -0,0 +1,37 @@ +From bbec8198d56f141ab3e97dca3607744e2497bf9a Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +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 diff --git a/SOURCES/1043-test-introduce-TEST-53-TIMER.patch b/SOURCES/1043-test-introduce-TEST-53-TIMER.patch new file mode 100644 index 0000000..5b7ed46 --- /dev/null +++ b/SOURCES/1043-test-introduce-TEST-53-TIMER.patch @@ -0,0 +1,106 @@ +From 7097abc3e9a0b76942b6844d43152e37f22158cf Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +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" </failed ++ ++for script in "${0%.sh}".*.sh; do ++ echo "Running $script" ++ "./$script" ++done ++ ++touch /testok ++rm /failed diff --git a/SOURCES/1044-test-restarting-elapsed-timer-shouldn-t-trigger-the-.patch b/SOURCES/1044-test-restarting-elapsed-timer-shouldn-t-trigger-the-.patch new file mode 100644 index 0000000..0c886f2 --- /dev/null +++ b/SOURCES/1044-test-restarting-elapsed-timer-shouldn-t-trigger-the-.patch @@ -0,0 +1,98 @@ +From 9cb832f3c26c292d31109cb54d142277f8751e52 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +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" <"/run/systemd/system/$UNIT_NAME.service" <"/run/systemd/system/$UNIT_NAME.timer.d/99-override.conf" < +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" <"/run/systemd/system/$UNIT_NAME.service" < +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) diff --git a/SOURCES/1047-Revert-test-extend-testcase-to-ensure-controller-mem.patch b/SOURCES/1047-Revert-test-extend-testcase-to-ensure-controller-mem.patch new file mode 100644 index 0000000..5efa3ce --- /dev/null +++ b/SOURCES/1047-Revert-test-extend-testcase-to-ensure-controller-mem.patch @@ -0,0 +1,47 @@ +From 3aa7f6e335b388a38ef756ecd2acdf545894d3fb Mon Sep 17 00:00:00 2001 +From: David Tardon +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 diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index af4b242..ea8ce28 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -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 - 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 - 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 - 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 - 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)