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