|
|
4e2e74 |
From 960ea6ae7a9e2aca3594318ab893f7d5383e46b6 Mon Sep 17 00:00:00 2001
|
|
|
4e2e74 |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
4e2e74 |
Date: Fri, 24 Apr 2015 12:29:05 +0200
|
|
|
4e2e74 |
Subject: [PATCH] device: rework how we enter tentative state
|
|
|
4e2e74 |
|
|
|
4e2e74 |
This reworks how we enter tentative state and does so only when a device
|
|
|
4e2e74 |
was previously not announced via udev. The previous check actually just
|
|
|
4e2e74 |
checked whether a new state bit was set, which is not correct.
|
|
|
4e2e74 |
|
|
|
4e2e74 |
Also, to be able to reliably maintain the tentative state across daemon
|
|
|
4e2e74 |
reloads, we need to serialize and deserialize it.
|
|
|
4e2e74 |
|
|
|
4e2e74 |
Cherry-picked from: f62009410
|
|
|
4e2e74 |
Resolves: #1283579
|
|
|
4e2e74 |
---
|
|
|
4e2e74 |
src/core/device.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++--------
|
|
|
4e2e74 |
src/core/device.h | 2 +-
|
|
|
4e2e74 |
2 files changed, 69 insertions(+), 12 deletions(-)
|
|
|
4e2e74 |
|
|
|
4e2e74 |
diff --git a/src/core/device.c b/src/core/device.c
|
|
|
4e2e74 |
index cc4ebd2..8a6855d 100644
|
|
|
4e2e74 |
--- a/src/core/device.c
|
|
|
4e2e74 |
+++ b/src/core/device.c
|
|
|
4e2e74 |
@@ -151,14 +151,47 @@ static int device_coldplug(Unit *u, Hashmap *deferred_work) {
|
|
|
4e2e74 |
if (d->found & DEVICE_FOUND_UDEV)
|
|
|
4e2e74 |
/* If udev says the device is around, it's around */
|
|
|
4e2e74 |
device_set_state(d, DEVICE_PLUGGED);
|
|
|
4e2e74 |
- else if (d->found != DEVICE_NOT_FOUND)
|
|
|
4e2e74 |
+ else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED)
|
|
|
4e2e74 |
/* If a device is found in /proc/self/mountinfo or
|
|
|
4e2e74 |
- * /proc/swaps, it's "tentatively" around. */
|
|
|
4e2e74 |
+ * /proc/swaps, and was not yet announced via udev,
|
|
|
4e2e74 |
+ * it's "tentatively" around. */
|
|
|
4e2e74 |
device_set_state(d, DEVICE_TENTATIVE);
|
|
|
4e2e74 |
|
|
|
4e2e74 |
return 0;
|
|
|
4e2e74 |
}
|
|
|
4e2e74 |
|
|
|
4e2e74 |
+static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
|
|
|
4e2e74 |
+ Device *d = DEVICE(u);
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ assert(u);
|
|
|
4e2e74 |
+ assert(f);
|
|
|
4e2e74 |
+ assert(fds);
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ unit_serialize_item(u, f, "state", device_state_to_string(d->state));
|
|
|
4e2e74 |
+}
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
|
|
4e2e74 |
+ Device *d = DEVICE(u);
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ assert(u);
|
|
|
4e2e74 |
+ assert(key);
|
|
|
4e2e74 |
+ assert(value);
|
|
|
4e2e74 |
+ assert(fds);
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ if (streq(key, "state")) {
|
|
|
4e2e74 |
+ DeviceState state;
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ state = device_state_from_string(value);
|
|
|
4e2e74 |
+ if (state < 0)
|
|
|
4e2e74 |
+ log_unit_debug(u->id, "Failed to parse state value %s", value);
|
|
|
4e2e74 |
+ else
|
|
|
4e2e74 |
+ d->deserialized_state = state;
|
|
|
4e2e74 |
+ } else
|
|
|
4e2e74 |
+ log_unit_debug(u->id, "Unknown serialization key '%s'", key);
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ return 0;
|
|
|
4e2e74 |
+}
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
static void device_dump(Unit *u, FILE *f, const char *prefix) {
|
|
|
4e2e74 |
Device *d = DEVICE(u);
|
|
|
4e2e74 |
|
|
|
4e2e74 |
@@ -408,7 +441,7 @@ static int device_process_new(Manager *m, struct udev_device *dev) {
|
|
|
4e2e74 |
}
|
|
|
4e2e74 |
|
|
|
4e2e74 |
static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
|
|
|
4e2e74 |
- DeviceFound n;
|
|
|
4e2e74 |
+ DeviceFound n, previous;
|
|
|
4e2e74 |
|
|
|
4e2e74 |
assert(d);
|
|
|
4e2e74 |
|
|
|
4e2e74 |
@@ -416,16 +449,27 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool
|
|
|
4e2e74 |
if (n == d->found)
|
|
|
4e2e74 |
return;
|
|
|
4e2e74 |
|
|
|
4e2e74 |
+ previous = d->found;
|
|
|
4e2e74 |
d->found = n;
|
|
|
4e2e74 |
|
|
|
4e2e74 |
- if (now) {
|
|
|
4e2e74 |
- if (d->found & DEVICE_FOUND_UDEV)
|
|
|
4e2e74 |
- device_set_state(d, DEVICE_PLUGGED);
|
|
|
4e2e74 |
- else if (add && d->found != DEVICE_NOT_FOUND)
|
|
|
4e2e74 |
- device_set_state(d, DEVICE_TENTATIVE);
|
|
|
4e2e74 |
- else
|
|
|
4e2e74 |
- device_set_state(d, DEVICE_DEAD);
|
|
|
4e2e74 |
- }
|
|
|
4e2e74 |
+ if (!now)
|
|
|
4e2e74 |
+ return;
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
+ if (d->found & DEVICE_FOUND_UDEV)
|
|
|
4e2e74 |
+ /* When the device is known to udev we consider it
|
|
|
4e2e74 |
+ * plugged. */
|
|
|
4e2e74 |
+ device_set_state(d, DEVICE_PLUGGED);
|
|
|
4e2e74 |
+ else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0)
|
|
|
4e2e74 |
+ /* If the device has not been seen by udev yet, but is
|
|
|
4e2e74 |
+ * now referenced by the kernel, then we assume the
|
|
|
4e2e74 |
+ * kernel knows it now, and udev might soon too. */
|
|
|
4e2e74 |
+ device_set_state(d, DEVICE_TENTATIVE);
|
|
|
4e2e74 |
+ else
|
|
|
4e2e74 |
+ /* If nobody sees the device, or if the device was
|
|
|
4e2e74 |
+ * previously seen by udev and now is only referenced
|
|
|
4e2e74 |
+ * from the kernel, then we consider the device is
|
|
|
4e2e74 |
+ * gone, the kernel just hasn't noticed it yet. */
|
|
|
4e2e74 |
+ device_set_state(d, DEVICE_DEAD);
|
|
|
4e2e74 |
}
|
|
|
4e2e74 |
|
|
|
4e2e74 |
static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
|
|
|
4e2e74 |
@@ -735,6 +779,16 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
|
|
|
4e2e74 |
if (!path_startswith(node, "/dev"))
|
|
|
4e2e74 |
return 0;
|
|
|
4e2e74 |
|
|
|
4e2e74 |
+ /* We make an extra check here, if the device node
|
|
|
4e2e74 |
+ * actually exists. If it's missing, then this is an
|
|
|
4e2e74 |
+ * indication that device was unplugged but is still
|
|
|
4e2e74 |
+ * referenced in /proc/swaps or
|
|
|
4e2e74 |
+ * /proc/self/mountinfo. Note that this check doesn't
|
|
|
4e2e74 |
+ * really cover all cases where a device might be gone
|
|
|
4e2e74 |
+ * away, since drives that can have a medium inserted
|
|
|
4e2e74 |
+ * will still have a device node even when the medium
|
|
|
4e2e74 |
+ * is not there... */
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
if (stat(node, &st) < 0) {
|
|
|
4e2e74 |
if (errno == ENOENT)
|
|
|
4e2e74 |
return 0;
|
|
|
4e2e74 |
@@ -788,6 +842,9 @@ const UnitVTable device_vtable = {
|
|
|
4e2e74 |
|
|
|
4e2e74 |
.coldplug = device_coldplug,
|
|
|
4e2e74 |
|
|
|
4e2e74 |
+ .serialize = device_serialize,
|
|
|
4e2e74 |
+ .deserialize_item = device_deserialize_item,
|
|
|
4e2e74 |
+
|
|
|
4e2e74 |
.dump = device_dump,
|
|
|
4e2e74 |
|
|
|
4e2e74 |
.active_state = device_active_state,
|
|
|
4e2e74 |
diff --git a/src/core/device.h b/src/core/device.h
|
|
|
4e2e74 |
index 0609b20..6724ab2 100644
|
|
|
4e2e74 |
--- a/src/core/device.h
|
|
|
4e2e74 |
+++ b/src/core/device.h
|
|
|
4e2e74 |
@@ -53,7 +53,7 @@ struct Device {
|
|
|
4e2e74 |
devices for the same sysfs path. We chain them up here. */
|
|
|
4e2e74 |
LIST_FIELDS(struct Device, same_sysfs);
|
|
|
4e2e74 |
|
|
|
4e2e74 |
- DeviceState state;
|
|
|
4e2e74 |
+ DeviceState state, deserialized_state;
|
|
|
4e2e74 |
};
|
|
|
4e2e74 |
|
|
|
4e2e74 |
extern const UnitVTable device_vtable;
|