On Wed, Feb 25, 2015 at 09:40:23PM +0300, Ivan Shapovalov wrote: > Because the order of coldplugging is not defined, we can reference a > not-yet-coldplugged unit and read its state while it has not yet been > set to a meaningful value. > > This way, already active units may get started again. > > We fix this by deferring such actions until all units have been at least > somehow coldplugged. > > Fixes https://bugs.freedesktop.org/show_bug.cgi?id=88401 > --- > > v2: set waiting state on path/timer units after deferring the actual coldplug, > so that we won't run into the exactly same problem during processing the > deferred entries. This looks good. I seems to be the correct thing to do independently of the idea to split device states into three with the new pending state. Let's see what Lennart thinks though.
Zbyszek > > src/core/automount.c | 2 +- > src/core/busname.c | 2 +- > src/core/device.c | 2 +- > src/core/manager.c | 33 ++++++++++++++++++++++++++++++++- > src/core/mount.c | 2 +- > src/core/path.c | 14 ++++++++++---- > src/core/scope.c | 2 +- > src/core/service.c | 2 +- > src/core/slice.c | 2 +- > src/core/snapshot.c | 2 +- > src/core/socket.c | 2 +- > src/core/swap.c | 2 +- > src/core/target.c | 2 +- > src/core/timer.c | 14 ++++++++++---- > src/core/unit.c | 25 ++++++++++++++++--------- > src/core/unit.h | 12 +++++++++--- > 16 files changed, 88 insertions(+), 32 deletions(-) > > diff --git a/src/core/automount.c b/src/core/automount.c > index 4a509ef..0539fbb 100644 > --- a/src/core/automount.c > +++ b/src/core/automount.c > @@ -233,7 +233,7 @@ static void automount_set_state(Automount *a, > AutomountState state) { > unit_notify(UNIT(a), state_translation_table[old_state], > state_translation_table[state], true); > } > > -static int automount_coldplug(Unit *u) { > +static int automount_coldplug(Unit *u, Hashmap *deferred_work) { > Automount *a = AUTOMOUNT(u); > int r; > > diff --git a/src/core/busname.c b/src/core/busname.c > index 1d77292..43d7607 100644 > --- a/src/core/busname.c > +++ b/src/core/busname.c > @@ -335,7 +335,7 @@ static void busname_set_state(BusName *n, BusNameState > state) { > unit_notify(UNIT(n), state_translation_table[old_state], > state_translation_table[state], true); > } > > -static int busname_coldplug(Unit *u) { > +static int busname_coldplug(Unit *u, Hashmap *deferred_work) { > BusName *n = BUSNAME(u); > int r; > > diff --git a/src/core/device.c b/src/core/device.c > index 2d983cc..70c2233 100644 > --- a/src/core/device.c > +++ b/src/core/device.c > @@ -104,7 +104,7 @@ static void device_set_state(Device *d, DeviceState > state) { > unit_notify(UNIT(d), state_translation_table[old_state], > state_translation_table[state], true); > } > > -static int device_coldplug(Unit *u) { > +static int device_coldplug(Unit *u, Hashmap *deferred_work) { > Device *d = DEVICE(u); > > assert(d); > diff --git a/src/core/manager.c b/src/core/manager.c > index 79a9d54..239c8bb 100644 > --- a/src/core/manager.c > +++ b/src/core/manager.c > @@ -975,8 +975,29 @@ static int manager_coldplug(Manager *m) { > Unit *u; > char *k; > > + /* > + * Some unit types tend to spawn jobs or check other units' state > + * during coldplug. This is wrong because it is undefined whether the > + * units in question have been already coldplugged (i. e. their state > + * restored). This way, we can easily re-start an already started > unit > + * or otherwise make a wrong decision based on the unit's state. > + * > + * Solve this by providing a way for coldplug functions to defer > + * such actions until after all units have been coldplugged. > + * > + * We store Unit* -> int(*)(Unit*). > + * > + * https://bugs.freedesktop.org/show_bug.cgi?id=88401 > + */ > + _cleanup_hashmap_free_ Hashmap *deferred_work = NULL; > + int(*proc)(Unit*); > + > assert(m); > > + deferred_work = hashmap_new(&trivial_hash_ops); > + if (!deferred_work) > + return -ENOMEM; > + > /* Then, let's set up their initial state. */ > HASHMAP_FOREACH_KEY(u, k, m->units, i) { > int q; > @@ -985,7 +1006,17 @@ static int manager_coldplug(Manager *m) { > if (u->id != k) > continue; > > - q = unit_coldplug(u); > + q = unit_coldplug(u, deferred_work); > + if (q < 0) > + r = q; > + } > + > + /* After coldplugging and setting up initial state of the units, > + * let's perform operations which spawn jobs or query units' state. > */ > + HASHMAP_FOREACH_KEY(proc, u, deferred_work, i) { > + int q; > + > + q = proc(u); > if (q < 0) > r = q; > } > diff --git a/src/core/mount.c b/src/core/mount.c > index 40037e7..ac91134 100644 > --- a/src/core/mount.c > +++ b/src/core/mount.c > @@ -612,7 +612,7 @@ static void mount_set_state(Mount *m, MountState state) { > m->reload_result = MOUNT_SUCCESS; > } > > -static int mount_coldplug(Unit *u) { > +static int mount_coldplug(Unit *u, Hashmap *deferred_work) { > Mount *m = MOUNT(u); > MountState new_state = MOUNT_DEAD; > int r; > diff --git a/src/core/path.c b/src/core/path.c > index fbb695d..6be9ac8 100644 > --- a/src/core/path.c > +++ b/src/core/path.c > @@ -438,7 +438,12 @@ static void path_set_state(Path *p, PathState state) { > > static void path_enter_waiting(Path *p, bool initial, bool recheck); > > -static int path_coldplug(Unit *u) { > +static int path_enter_waiting_coldplug(Unit *u) { > + path_enter_waiting(PATH(u), true, true); > + return 0; > +} > + > +static int path_coldplug(Unit *u, Hashmap *deferred_work) { > Path *p = PATH(u); > > assert(p); > @@ -447,9 +452,10 @@ static int path_coldplug(Unit *u) { > if (p->deserialized_state != p->state) { > > if (p->deserialized_state == PATH_WAITING || > - p->deserialized_state == PATH_RUNNING) > - path_enter_waiting(p, true, true); > - else > + p->deserialized_state == PATH_RUNNING) { > + hashmap_put(deferred_work, u, > &path_enter_waiting_coldplug); > + path_set_state(p, PATH_WAITING); > + } else > path_set_state(p, p->deserialized_state); > } > > diff --git a/src/core/scope.c b/src/core/scope.c > index a0e4732..d94ef1f 100644 > --- a/src/core/scope.c > +++ b/src/core/scope.c > @@ -171,7 +171,7 @@ static int scope_load(Unit *u) { > return scope_verify(s); > } > > -static int scope_coldplug(Unit *u) { > +static int scope_coldplug(Unit *u, Hashmap *deferred_work) { > Scope *s = SCOPE(u); > int r; > > diff --git a/src/core/service.c b/src/core/service.c > index c7b3505..a1cc4b0 100644 > --- a/src/core/service.c > +++ b/src/core/service.c > @@ -878,7 +878,7 @@ static void service_set_state(Service *s, ServiceState > state) { > s->reload_result = SERVICE_SUCCESS; > } > > -static int service_coldplug(Unit *u) { > +static int service_coldplug(Unit *u, Hashmap *deferred_work) { > Service *s = SERVICE(u); > int r; > > diff --git a/src/core/slice.c b/src/core/slice.c > index 4d2eaf7..22d54dc 100644 > --- a/src/core/slice.c > +++ b/src/core/slice.c > @@ -150,7 +150,7 @@ static int slice_load(Unit *u) { > return slice_verify(s); > } > > -static int slice_coldplug(Unit *u) { > +static int slice_coldplug(Unit *u, Hashmap *deferred_work) { > Slice *t = SLICE(u); > > assert(t); > diff --git a/src/core/snapshot.c b/src/core/snapshot.c > index b70c3be..b1d8448 100644 > --- a/src/core/snapshot.c > +++ b/src/core/snapshot.c > @@ -75,7 +75,7 @@ static int snapshot_load(Unit *u) { > return 0; > } > > -static int snapshot_coldplug(Unit *u) { > +static int snapshot_coldplug(Unit *u, Hashmap *deferred_work) { > Snapshot *s = SNAPSHOT(u); > > assert(s); > diff --git a/src/core/socket.c b/src/core/socket.c > index 7d052f2..576b90d 100644 > --- a/src/core/socket.c > +++ b/src/core/socket.c > @@ -1322,7 +1322,7 @@ static void socket_set_state(Socket *s, SocketState > state) { > unit_notify(UNIT(s), state_translation_table[old_state], > state_translation_table[state], true); > } > > -static int socket_coldplug(Unit *u) { > +static int socket_coldplug(Unit *u, Hashmap *deferred_work) { > Socket *s = SOCKET(u); > int r; > > diff --git a/src/core/swap.c b/src/core/swap.c > index f73a8e6..b192094 100644 > --- a/src/core/swap.c > +++ b/src/core/swap.c > @@ -505,7 +505,7 @@ static void swap_set_state(Swap *s, SwapState state) { > job_add_to_run_queue(UNIT(other)->job); > } > > -static int swap_coldplug(Unit *u) { > +static int swap_coldplug(Unit *u, Hashmap *deferred_work) { > Swap *s = SWAP(u); > SwapState new_state = SWAP_DEAD; > int r; > diff --git a/src/core/target.c b/src/core/target.c > index 8817ef2..5f64402 100644 > --- a/src/core/target.c > +++ b/src/core/target.c > @@ -103,7 +103,7 @@ static int target_load(Unit *u) { > return 0; > } > > -static int target_coldplug(Unit *u) { > +static int target_coldplug(Unit *u, Hashmap *deferred_work) { > Target *t = TARGET(u); > > assert(t); > diff --git a/src/core/timer.c b/src/core/timer.c > index 9405501..79a7540 100644 > --- a/src/core/timer.c > +++ b/src/core/timer.c > @@ -267,7 +267,12 @@ static void timer_set_state(Timer *t, TimerState state) { > > static void timer_enter_waiting(Timer *t, bool initial); > > -static int timer_coldplug(Unit *u) { > +static int timer_enter_waiting_coldplug(Unit *u) { > + timer_enter_waiting(TIMER(u), false); > + return 0; > +} > + > +static int timer_coldplug(Unit *u, Hashmap *deferred_work) { > Timer *t = TIMER(u); > > assert(t); > @@ -275,9 +280,10 @@ static int timer_coldplug(Unit *u) { > > if (t->deserialized_state != t->state) { > > - if (t->deserialized_state == TIMER_WAITING) > - timer_enter_waiting(t, false); > - else > + if (t->deserialized_state == TIMER_WAITING) { > + hashmap_put(deferred_work, u, > &timer_enter_waiting_coldplug); > + timer_set_state(t, TIMER_WAITING); > + } else > timer_set_state(t, t->deserialized_state); > } > > diff --git a/src/core/unit.c b/src/core/unit.c > index ad5348b..df75370 100644 > --- a/src/core/unit.c > +++ b/src/core/unit.c > @@ -2852,27 +2852,34 @@ int unit_add_node_link(Unit *u, const char *what, > bool wants) { > return 0; > } > > -int unit_coldplug(Unit *u) { > +static int unit_add_deserialized_job_coldplug(Unit *u) { > + int r; > + > + r = manager_add_job(u->manager, u->deserialized_job, u, > JOB_IGNORE_REQUIREMENTS, false, NULL, NULL); > + if (r < 0) > + return r; > + > + u->deserialized_job = _JOB_TYPE_INVALID; > + > + return 0; > +} > + > +int unit_coldplug(Unit *u, Hashmap *deferred_work) { > int r; > > assert(u); > > if (UNIT_VTABLE(u)->coldplug) > - if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0) > + if ((r = UNIT_VTABLE(u)->coldplug(u, deferred_work)) < 0) > return r; > > if (u->job) { > r = job_coldplug(u->job); > if (r < 0) > return r; > - } else if (u->deserialized_job >= 0) { > + } else if (u->deserialized_job >= 0) > /* legacy */ > - r = manager_add_job(u->manager, u->deserialized_job, u, > JOB_IGNORE_REQUIREMENTS, false, NULL, NULL); > - if (r < 0) > - return r; > - > - u->deserialized_job = _JOB_TYPE_INVALID; > - } > + hashmap_put(deferred_work, u, > &unit_add_deserialized_job_coldplug); > > return 0; > } > diff --git a/src/core/unit.h b/src/core/unit.h > index b3775d4..44682db 100644 > --- a/src/core/unit.h > +++ b/src/core/unit.h > @@ -298,8 +298,14 @@ struct UnitVTable { > int (*load)(Unit *u); > > /* If a lot of units got created via enumerate(), this is > - * where to actually set the state and call unit_notify(). */ > - int (*coldplug)(Unit *u); > + * where to actually set the state and call unit_notify(). > + * > + * This must not reference other units (maybe implicitly through > spawning > + * jobs), because it is possible that they are not yet coldplugged. > + * Such actions must be deferred until the end of coldplug bу adding > + * a "Unit* -> int(*)(Unit*)" entry into the hashmap. > + */ > + int (*coldplug)(Unit *u, Hashmap *deferred_work); > > void (*dump)(Unit *u, FILE *f, const char *prefix); > > @@ -535,7 +541,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds); > > int unit_add_node_link(Unit *u, const char *what, bool wants); > > -int unit_coldplug(Unit *u); > +int unit_coldplug(Unit *u, Hashmap *deferred_work); > > void unit_status_printf(Unit *u, const char *status, const char > *unit_status_msg_format) _printf_(3, 0); > > -- > 2.3.0 > > _______________________________________________ > systemd-devel mailing list > systemd-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/systemd-devel _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel