-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
device: make sure to always retroactively start device dependencies #8675
device: make sure to always retroactively start device dependencies #8675
Conversation
Please double check as I'm not really sure about this one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes a lot of sense, but I'd prefer if we'd not turn the raw bit values into API, see comments...
src/core/device.c
Outdated
@@ -165,6 +165,7 @@ static int device_serialize(Unit *u, FILE *f, FDSet *fds) { | |||
assert(fds); | |||
|
|||
unit_serialize_item(u, f, "state", device_state_to_string(d->state)); | |||
unit_serialize_item_format(u, f, "found", "%i", d->found); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i feel a bit nervous about serializing the binary value of this flag field, as it turns something into an API that is not necessarily obvious as one... Hence, I'd rather see three individual bool fields serialized for the three relevant bits in the flags field...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, I'll update the PR accordingly, thanks for the review.
@poettering I just realize that it will probably wont work when we'll upgrade systemd from a version that doesn't serialize "d->found" to a new one that does... Indeed when reexec systemd, the old version won't serialize "d->found" and the new one expects to find the info to correctly initialize this field. But that's actually a more general issue which should happen every times a new field is serialized, or am I missing something ? |
d56f183
to
df96af5
Compare
New version forced pushed:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this patch, after daemon-reexec from previous version, all devices will be in not-found state. Not good.
I think it's necessary to provide an upgrade mechanism:
- add a tri-state field in the device structure, with values "not seen during deserialization", "seen during deserialization", "found state set during serialization"
- during deserialization set state to "seen" when the device is encountered, and to "found state set" if "found=" is encountered
- after deserialization is complete, flip all "seen" to DEVICE_FOUND_UDEV.
That way this upgrade from previous versions will behave like upgrades did previously, and upgrade from this version to another will make use of the serialized state.
src/core/device.c
Outdated
@@ -179,6 +179,16 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value, | |||
assert(value); | |||
assert(fds); | |||
|
|||
/* The device was known at the time units were serialized but it's not | |||
* anymore at the time units are deserialized. This happens when PID1 is | |||
* reexec after having switched to the new rootfs. When devices are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reexec → reexecuted
src/core/device.c
Outdated
* reexec after having switched to the new rootfs. When devices are | ||
* enumerated, udevd isn't running yet and therefore the list of devices | ||
* (handled by systemd) to init was empty. We ignore completely such | ||
* case and wait for the device events to be re-triggered by udev so |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... "we completely ignore such case" — I guess you mean the opposite, we handle this case (by doing nothing now and waiting for the device to be re-triggered by udev and initialized properly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that's what I tried to mean, will reword this part. Thanks.
@keszybz you're right it's currently broken when PID is reexecuted. That was actually the point I raised in my previous comment #8675 (comment). And I wondered how it was supposed to be handled since all new introductions of field serialization have the same issue... |
I was responding to that ;)
Right. We always need to do something backwards compatible, as far as reasonable. Sometimes it's not possible, vide e8a565c. |
PID1 updates the state of device units upon 2 different events: - when it processes an event sent by udev and in this case the device deps are started if the device enters in the "plugged" state. - when it enumerates all devices during its startup or when it is asked to reload its configuration data but in this case the device deps (if any) are not retroactively started. When udev processes a new "add" kernel event, it first registers the new device in its databases then sends an event to systemd. If for any reason, systemd is asked to reload its configuration between the previous 2 steps, it might see for the first time the new device while scanning /sys for all devices. Only during a second step, udev will send the event for the new device. In this peculiar case the device deps wont be started (even though the device is first seen by PID1). Indeed when reloading its configurations, PID1 will put the device unit in the "plugged" state but without starting the device deps. Thereafter PID1 will get the event from udev for the new device but the device unit will be in "plugged" state already therefore it won't see any need to start the device dependencies. Rather than assuming that during the reloading of systemd manager configuration all devices listed in udev DBs have been already processed and should be put in the "plugged" state (done by device_coldplug()), this patch does that only for devices which have been processed via an udev event (device_dispatch_io()) previously. In this case we set "d->found" to "DEVICE_FOUND_UDEV" and we make also sure to no more initialize "d->found" while enumerating devices. Instead this field is now saved/restored while devices are serialized.
Do not try to party initialize a device during deserialization if it's not known by udev (anymore) and therefore hasn't been seen during device enumeration. The device unit in this case has not been initialized properly and setting it in the "plugged" state can be confusing. Actually this happens during every boots when PID switches to the new rootfs: PID is reexecuted and enumerates devices but since udev is not running, the list of enumerated devices is empty.
df96af5
to
036d2ee
Compare
@keszybz how about something like this ? |
|
Looks good. |
This reworks how device units are "powered on". This makes sure that any device changes that might have happened while we were restarting/reloading will be noticed properly. For that we'll now properly serialize/deserialize both the device unit state and the device "found" flags, and restore these initially in the "coldplug" phase of the manager deserialization. While enumerating the udev devices during startup we'll put together a new "found" flags mask, which we'll the switch to in the "catchup" phase of the manager deserialization, which follows the "coldplug" phase. Note that during the "coldplug" phase no unit state change events are generated, which is different for the "catchall" phase which will do that. Thus we correctly make sure that the deserialized state won't pull in new deps, but any device's change while we were reloading would. Fixes: systemd#8832 Replaces: systemd#8675
This reworks how device units are "powered on". This makes sure that any device changes that might have happened while we were restarting/reloading will be noticed properly. For that we'll now properly serialize/deserialize both the device unit state and the device "found" flags, and restore these initially in the "coldplug" phase of the manager deserialization. While enumerating the udev devices during startup we'll put together a new "found" flags mask, which we'll the switch to in the "catchup" phase of the manager deserialization, which follows the "coldplug" phase. Note that during the "coldplug" phase no unit state change events are generated, which is different for the "catchall" phase which will do that. Thus we correctly make sure that the deserialized state won't pull in new deps, but any device's change while we were reloading would. Fixes: systemd#8832 Replaces: systemd#8675
This reworks how device units are "powered on". This makes sure that any device changes that might have happened while we were restarting/reloading will be noticed properly. For that we'll now properly serialize/deserialize both the device unit state and the device "found" flags, and restore these initially in the "coldplug" phase of the manager deserialization. While enumerating the udev devices during startup we'll put together a new "found" flags mask, which we'll the switch to in the "catchup" phase of the manager deserialization, which follows the "coldplug" phase. Note that during the "coldplug" phase no unit state change events are generated, which is different for the "catchall" phase which will do that. Thus we correctly make sure that the deserialized state won't pull in new deps, but any device's change while we were reloading would. Fixes: systemd#8832 Replaces: systemd#8675
This reworks how device units are "powered on". This makes sure that any device changes that might have happened while we were restarting/reloading will be noticed properly. For that we'll now properly serialize/deserialize both the device unit state and the device "found" flags, and restore these initially in the "coldplug" phase of the manager deserialization. While enumerating the udev devices during startup we'll put together a new "found" flags mask, which we'll the switch to in the "catchup" phase of the manager deserialization, which follows the "coldplug" phase. Note that during the "coldplug" phase no unit state change events are generated, which is different for the "catchall" phase which will do that. Thus we correctly make sure that the deserialized state won't pull in new deps, but any device's change while we were reloading would. Fixes: systemd#8832 Replaces: systemd#8675
PID1 updates the state of device units upon 2 different events:
when it processes an event sent by udev and in this case the device deps are
started if the device enters in the "plugged" state.
when it enumerates all devices during its startup or when it is asked to
reload its configuration data but in this case the device deps (if any) are
not retroactively started.
Whe udev processes a new "add" kernel event, it first registers the new device
in its database then sends a new event to systemd.
If for any reason, systemd is asked to reload its configuration (via
"daemon-reload" command for example) between the previous 2 steps, it might see
for the first time the new device when enumerating all devices via udev DB
first and process the event sent by udev in a second step.
In this peculiar case the device deps wont be started (even though the device
is first seen by PID1).
Indeed while "daemon-reload", PID1 will put the device unit in the "plugged"
state but without starting the device deps. Thereafter PID1 will get the event
from udev for the new device but the device unit will be in "plugged" state
already therefore it won't see any need to start the device dependencies.
Rather than assuming that during the reloading of systemd manager configuration
all devices listed in udev DB have been already processed and should be put in
the "plugged" state (done by device_coldplug()), this patch does that only for
devices which have been processed via an udev event (device_dispatch_io())
previously. In this case we set "d->found" to "DEVICE_FOUND_UDEV" and we make
also sure to no more initialize "d->found" while enumerating devices. Instead
this field is now saved/restored while devices are serialized.