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;