diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-04-24 12:29:05 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-04-24 12:29:05 +0200 |
commit | f62009410a72f5a89bfb8fdd7e48d9d472a6887b (patch) | |
tree | 6de683ec5cf4f54460c8a282f652667280af800c /src/core | |
parent | f06db33455f5134c5c94b71a5b1d94cc9d0d630c (diff) |
device: rework how we enter tentative state
This reworks how we enter tentative state and does so only when a device
was previously not announced via udev. The previous check actually just
checked whether a new state bit was set, which is not correct.
Also, to be able to reliably maintain the tentative state across daemon
reloads, we need to serialize and deserialize it.
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/device.c | 79 | ||||
-rw-r--r-- | src/core/device.h | 3 |
2 files changed, 69 insertions, 13 deletions
diff --git a/src/core/device.c b/src/core/device.c index 43c4c671dd..a7572306d7 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -149,14 +149,47 @@ static int device_coldplug(Unit *u, Hashmap *deferred_work) { if (d->found & DEVICE_FOUND_UDEV) /* If udev says the device is around, it's around */ device_set_state(d, DEVICE_PLUGGED); - else if (d->found != DEVICE_NOT_FOUND) + else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED) /* If a device is found in /proc/self/mountinfo or - * /proc/swaps, it's "tentatively" around. */ + * /proc/swaps, and was not yet announced via udev, + * it's "tentatively" around. */ device_set_state(d, DEVICE_TENTATIVE); return 0; } +static int device_serialize(Unit *u, FILE *f, FDSet *fds) { + Device *d = DEVICE(u); + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", device_state_to_string(d->state)); +} + +static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Device *d = DEVICE(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + DeviceState state; + + state = device_state_from_string(value); + if (state < 0) + log_unit_debug(u->id, "Failed to parse state value %s", value); + else + d->deserialized_state = state; + } else + log_unit_debug(u->id, "Unknown serialization key '%s'", key); + + return 0; +} + static void device_dump(Unit *u, FILE *f, const char *prefix) { Device *d = DEVICE(u); @@ -406,7 +439,7 @@ static int device_process_new(Manager *m, struct udev_device *dev) { } static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) { - DeviceFound n; + DeviceFound n, previous; assert(d); @@ -414,16 +447,27 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool if (n == d->found) return; + previous = d->found; d->found = n; - if (now) { - if (d->found & DEVICE_FOUND_UDEV) - device_set_state(d, DEVICE_PLUGGED); - else if (add && d->found != DEVICE_NOT_FOUND) - device_set_state(d, DEVICE_TENTATIVE); - else - device_set_state(d, DEVICE_DEAD); - } + if (!now) + return; + + if (d->found & DEVICE_FOUND_UDEV) + /* When the device is known to udev we consider it + * plugged. */ + device_set_state(d, DEVICE_PLUGGED); + else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0) + /* If the device has not been seen by udev yet, but is + * now referenced by the kernel, then we assume the + * kernel knows it now, and udev might soon too. */ + device_set_state(d, DEVICE_TENTATIVE); + else + /* If nobody sees the device, or if the device was + * previously seen by udev and now is only referenced + * from the kernel, then we consider the device is + * gone, the kernel just hasn't noticed it yet. */ + device_set_state(d, DEVICE_DEAD); } static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) { @@ -733,6 +777,16 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, if (!path_startswith(node, "/dev")) return 0; + /* We make an extra check here, if the device node + * actually exists. If it's missing, then this is an + * indication that device was unplugged but is still + * referenced in /proc/swaps or + * /proc/self/mountinfo. Note that this check doesn't + * really cover all cases where a device might be gone + * away, since drives that can have a medium inserted + * will still have a device node even when the medium + * is not there... */ + if (stat(node, &st) < 0) { if (errno == ENOENT) return 0; @@ -786,6 +840,9 @@ const UnitVTable device_vtable = { .coldplug = device_coldplug, + .serialize = device_serialize, + .deserialize_item = device_deserialize_item, + .dump = device_dump, .active_state = device_active_state, diff --git a/src/core/device.h b/src/core/device.h index 9f46e08953..10ab113176 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -23,7 +23,6 @@ typedef struct Device Device; - /* We simply watch devices, we cannot plug/unplug them. That * simplifies the state engine greatly */ typedef enum DeviceState { @@ -52,7 +51,7 @@ struct Device { devices for the same sysfs path. We chain them up here. */ LIST_FIELDS(struct Device, same_sysfs); - DeviceState state; + DeviceState state, deserialized_state; }; extern const UnitVTable device_vtable; |