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.
158 lines
6.0 KiB
158 lines
6.0 KiB
From ff6c50a1451e0e8fdca72039a0a00ebb0a20ba6c Mon Sep 17 00:00:00 2001 |
|
From: Lennart Poettering <lennart@poettering.net> |
|
Date: Fri, 24 Apr 2015 12:29:05 +0200 |
|
Subject: [PATCH] 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. |
|
|
|
Cherry-picked from: f62009410 |
|
Resolves: #1283579 |
|
--- |
|
src/core/device.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++-------- |
|
src/core/device.h | 2 +- |
|
2 files changed, 69 insertions(+), 12 deletions(-) |
|
|
|
diff --git a/src/core/device.c b/src/core/device.c |
|
index cc4ebd2c8..8a6855dfc 100644 |
|
--- a/src/core/device.c |
|
+++ b/src/core/device.c |
|
@@ -151,14 +151,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); |
|
|
|
@@ -408,7 +441,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); |
|
|
|
@@ -416,16 +449,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) { |
|
@@ -735,6 +779,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; |
|
@@ -788,6 +842,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 0609b20fd..6724ab21e 100644 |
|
--- a/src/core/device.h |
|
+++ b/src/core/device.h |
|
@@ -53,7 +53,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;
|
|
|