You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
6.9 KiB
223 lines
6.9 KiB
From 36226a9afe96bdffce9d0697be020f2ca9d7fe6f Mon Sep 17 00:00:00 2001 |
|
From: Michal Sekletar <msekletar@users.noreply.github.com> |
|
Date: Fri, 23 Mar 2018 15:28:06 +0100 |
|
Subject: [PATCH] core: delay adding target dependencies until all units are |
|
loaded and aliases resolved (#8381) |
|
|
|
Currently we add target dependencies while we are loading units. This |
|
can create ordering loops even if configuration doesn't contain any |
|
loop. Take for example following configuration, |
|
|
|
$ systemctl get-default |
|
multi-user.target |
|
|
|
$ cat /etc/systemd/system/test.service |
|
[Unit] |
|
After=default.target |
|
|
|
[Service] |
|
ExecStart=/bin/true |
|
|
|
[Install] |
|
WantedBy=multi-user.target |
|
|
|
If we encounter such unit file early during manager start-up (e.g. load |
|
queue is dispatched while enumerating devices due to SYSTEMD_WANTS in |
|
udev rules) we would add stub unit default.target and we order it Before |
|
test.service. At the same time we add implicit Before to |
|
multi-user.target. Later we merge two units and we create ordering cycle |
|
in the process. |
|
|
|
To fix the issue we will now never add any target dependencies until we |
|
loaded all the unit files and resolved all the aliases. |
|
|
|
(cherry picked from commit 19496554e23ea4861ce780430052dcf86a2ffcba) |
|
|
|
Resolves: #1368856 |
|
--- |
|
src/core/manager.c | 40 ++++++++++++++++++++++++++++++++++++++++ |
|
src/core/manager.h | 3 +++ |
|
src/core/unit.c | 46 ++++++++++++++++------------------------------ |
|
src/core/unit.h | 5 +++++ |
|
4 files changed, 64 insertions(+), 30 deletions(-) |
|
|
|
diff --git a/src/core/manager.c b/src/core/manager.c |
|
index 9c406bb5bf..0466e4bb8a 100644 |
|
--- a/src/core/manager.c |
|
+++ b/src/core/manager.c |
|
@@ -1373,6 +1373,41 @@ Unit *manager_get_unit(Manager *m, const char *name) { |
|
return hashmap_get(m->units, name); |
|
} |
|
|
|
+static int manager_dispatch_target_deps_queue(Manager *m) { |
|
+ Unit *u; |
|
+ unsigned k; |
|
+ int r = 0; |
|
+ |
|
+ static const UnitDependency deps[] = { |
|
+ UNIT_REQUIRED_BY, |
|
+ UNIT_REQUIRED_BY_OVERRIDABLE, |
|
+ UNIT_WANTED_BY, |
|
+ UNIT_BOUND_BY |
|
+ }; |
|
+ |
|
+ assert(m); |
|
+ |
|
+ while ((u = m->target_deps_queue)) { |
|
+ assert(u->in_target_deps_queue); |
|
+ |
|
+ LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); |
|
+ u->in_target_deps_queue = false; |
|
+ |
|
+ for (k = 0; k < ELEMENTSOF(deps); k++) { |
|
+ Unit *target; |
|
+ Iterator i; |
|
+ |
|
+ SET_FOREACH(target, u->dependencies[deps[k]], i) { |
|
+ r = unit_add_default_target_dependency(u, target); |
|
+ if (r < 0) |
|
+ return r; |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ return r; |
|
+} |
|
+ |
|
unsigned manager_dispatch_load_queue(Manager *m) { |
|
Unit *u; |
|
unsigned n = 0; |
|
@@ -1396,6 +1431,11 @@ unsigned manager_dispatch_load_queue(Manager *m) { |
|
} |
|
|
|
m->dispatching_load_queue = false; |
|
+ |
|
+ /* Dispatch the units waiting for their target dependencies to be added now, as all targets that we know about |
|
+ * should be loaded and have aliases resolved */ |
|
+ (void) manager_dispatch_target_deps_queue(m); |
|
+ |
|
return n; |
|
} |
|
|
|
diff --git a/src/core/manager.h b/src/core/manager.h |
|
index 90d2d982e1..b0e4cad1fc 100644 |
|
--- a/src/core/manager.h |
|
+++ b/src/core/manager.h |
|
@@ -114,6 +114,9 @@ struct Manager { |
|
/* Units that should be realized */ |
|
LIST_HEAD(Unit, cgroup_queue); |
|
|
|
+ /* Target units whose default target dependencies haven't been set yet */ |
|
+ LIST_HEAD(Unit, target_deps_queue); |
|
+ |
|
sd_event *event; |
|
|
|
/* We use two hash tables here, since the same PID might be |
|
diff --git a/src/core/unit.c b/src/core/unit.c |
|
index 22d9beed76..cfddce34d4 100644 |
|
--- a/src/core/unit.c |
|
+++ b/src/core/unit.c |
|
@@ -519,6 +519,9 @@ void unit_free(Unit *u) { |
|
u->manager->n_in_gc_queue--; |
|
} |
|
|
|
+ if (u->in_target_deps_queue) |
|
+ LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); |
|
+ |
|
if (u->in_cgroup_queue) |
|
LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); |
|
|
|
@@ -1065,6 +1068,18 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { |
|
return 0; |
|
} |
|
|
|
+void unit_add_to_target_deps_queue(Unit *u) { |
|
+ Manager *m = u->manager; |
|
+ |
|
+ assert(u); |
|
+ |
|
+ if (u->in_target_deps_queue) |
|
+ return; |
|
+ |
|
+ LIST_PREPEND(target_deps_queue, m->target_deps_queue, u); |
|
+ u->in_target_deps_queue = true; |
|
+} |
|
+ |
|
int unit_add_default_target_dependency(Unit *u, Unit *target) { |
|
assert(u); |
|
assert(target); |
|
@@ -1091,32 +1106,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { |
|
return unit_add_dependency(target, UNIT_AFTER, u, true); |
|
} |
|
|
|
-static int unit_add_target_dependencies(Unit *u) { |
|
- |
|
- static const UnitDependency deps[] = { |
|
- UNIT_REQUIRED_BY, |
|
- UNIT_REQUIRED_BY_OVERRIDABLE, |
|
- UNIT_WANTED_BY, |
|
- UNIT_BOUND_BY |
|
- }; |
|
- |
|
- Unit *target; |
|
- Iterator i; |
|
- unsigned k; |
|
- int r = 0; |
|
- |
|
- assert(u); |
|
- |
|
- for (k = 0; k < ELEMENTSOF(deps); k++) |
|
- SET_FOREACH(target, u->dependencies[deps[k]], i) { |
|
- r = unit_add_default_target_dependency(u, target); |
|
- if (r < 0) |
|
- return r; |
|
- } |
|
- |
|
- return r; |
|
-} |
|
- |
|
static int unit_add_slice_dependencies(Unit *u) { |
|
assert(u); |
|
|
|
@@ -1217,10 +1206,7 @@ int unit_load(Unit *u) { |
|
} |
|
|
|
if (u->load_state == UNIT_LOADED) { |
|
- |
|
- r = unit_add_target_dependencies(u); |
|
- if (r < 0) |
|
- goto fail; |
|
+ unit_add_to_target_deps_queue(u); |
|
|
|
r = unit_add_slice_dependencies(u); |
|
if (r < 0) |
|
diff --git a/src/core/unit.h b/src/core/unit.h |
|
index 480e2e95f1..dfec9cea01 100644 |
|
--- a/src/core/unit.h |
|
+++ b/src/core/unit.h |
|
@@ -162,6 +162,9 @@ struct Unit { |
|
/* CGroup realize members queue */ |
|
LIST_FIELDS(Unit, cgroup_queue); |
|
|
|
+ /* Target dependencies queue */ |
|
+ LIST_FIELDS(Unit, target_deps_queue); |
|
+ |
|
/* PIDs we keep an eye on. Note that a unit might have many |
|
* more, but these are the ones we care enough about to |
|
* process SIGCHLD for */ |
|
@@ -228,6 +231,7 @@ struct Unit { |
|
bool in_cleanup_queue:1; |
|
bool in_gc_queue:1; |
|
bool in_cgroup_queue:1; |
|
+ bool in_target_deps_queue:1; |
|
|
|
bool sent_dbus_new_signal:1; |
|
|
|
@@ -498,6 +502,7 @@ void unit_add_to_load_queue(Unit *u); |
|
void unit_add_to_dbus_queue(Unit *u); |
|
void unit_add_to_cleanup_queue(Unit *u); |
|
void unit_add_to_gc_queue(Unit *u); |
|
+void unit_add_to_target_deps_queue(Unit *u); |
|
|
|
int unit_merge(Unit *u, Unit *other); |
|
int unit_merge_by_name(Unit *u, const char *other);
|
|
|