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