Blame SOURCES/0033-core-rework-device-state-logic.patch

17b0f1
From 30ced6a8c742e1c798fff439b28a9800ca43f3e7 Mon Sep 17 00:00:00 2001
17b0f1
From: Lennart Poettering <lennart@poettering.net>
17b0f1
Date: Fri, 27 Feb 2015 21:55:08 +0100
17b0f1
Subject: [PATCH] core: rework device state logic
17b0f1
17b0f1
This change introduces a new state "tentative" for device units. Device
17b0f1
units are considered "plugged" when udev announced them, "dead" when
17b0f1
they are not available in the kernel, and "tentative" when they are
17b0f1
referenced in /proc/self/mountinfo or /proc/swaps but not (yet)
17b0f1
announced via udev.
17b0f1
17b0f1
This should fix a race when device nodes (like loop devices) are created
17b0f1
and immediately mounted. Previously, systemd might end up seeing the
17b0f1
mount unit before the device, and would thus pull down the mount because
17b0f1
its BindTo dependency on the device would not be fulfilled.
17b0f1
17b0f1
(cherry picked from commit 628c89cc68ab96fce2de7ebba5933725d147aecc)
17b0f1
---
17b0f1
 src/core/device.c | 368 ++++++++++++++++++++++++++++------------------
17b0f1
 src/core/device.h |  14 +-
17b0f1
 src/core/mount.c  |  46 +++---
17b0f1
 src/core/swap.c   |  32 ++--
17b0f1
 src/core/swap.h   |   4 +-
17b0f1
 src/core/unit.c   |   1 -
17b0f1
 6 files changed, 285 insertions(+), 180 deletions(-)
17b0f1
17b0f1
diff --git a/src/core/device.c b/src/core/device.c
17b0f1
index d3deac3936..75b9a46287 100644
17b0f1
--- a/src/core/device.c
17b0f1
+++ b/src/core/device.c
17b0f1
@@ -36,7 +36,8 @@
17b0f1
 
17b0f1
 static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
17b0f1
         [DEVICE_DEAD] = UNIT_INACTIVE,
17b0f1
-        [DEVICE_PLUGGED] = UNIT_ACTIVE
17b0f1
+        [DEVICE_TENTATIVE] = UNIT_ACTIVATING,
17b0f1
+        [DEVICE_PLUGGED] = UNIT_ACTIVE,
17b0f1
 };
17b0f1
 
17b0f1
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
17b0f1
@@ -65,6 +66,41 @@ static void device_unset_sysfs(Device *d) {
17b0f1
         d->sysfs = NULL;
17b0f1
 }
17b0f1
 
17b0f1
+static int device_set_sysfs(Device *d, const char *sysfs) {
17b0f1
+        Device *first;
17b0f1
+        char *copy;
17b0f1
+        int r;
17b0f1
+
17b0f1
+        assert(d);
17b0f1
+
17b0f1
+        if (streq_ptr(d->sysfs, sysfs))
17b0f1
+                return 0;
17b0f1
+
17b0f1
+        r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops);
17b0f1
+        if (r < 0)
17b0f1
+                return r;
17b0f1
+
17b0f1
+        copy = strdup(sysfs);
17b0f1
+        if (!copy)
17b0f1
+                return -ENOMEM;
17b0f1
+
17b0f1
+        device_unset_sysfs(d);
17b0f1
+
17b0f1
+        first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs);
17b0f1
+        LIST_PREPEND(same_sysfs, first, d);
17b0f1
+
17b0f1
+        r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first);
17b0f1
+        if (r < 0) {
17b0f1
+                LIST_REMOVE(same_sysfs, first, d);
17b0f1
+                free(copy);
17b0f1
+                return r;
17b0f1
+        }
17b0f1
+
17b0f1
+        d->sysfs = copy;
17b0f1
+
17b0f1
+        return 0;
17b0f1
+}
17b0f1
+
17b0f1
 static void device_init(Unit *u) {
17b0f1
         Device *d = DEVICE(u);
17b0f1
 
17b0f1
@@ -112,8 +148,13 @@ static int device_coldplug(Unit *u) {
17b0f1
         assert(d);
17b0f1
         assert(d->state == DEVICE_DEAD);
17b0f1
 
17b0f1
-        if (d->sysfs)
17b0f1
+        if (d->found & DEVICE_FOUND_UDEV)
17b0f1
+                /* If udev says the device is around, it's around */
17b0f1
                 device_set_state(d, DEVICE_PLUGGED);
17b0f1
+        else if (d->found != DEVICE_NOT_FOUND)
17b0f1
+                /* If a device is found in /proc/self/mountinfo or
17b0f1
+                 * /proc/swaps, it's "tentatively" around. */
17b0f1
+                device_set_state(d, DEVICE_TENTATIVE);
17b0f1
 
17b0f1
         return 0;
17b0f1
 }
17b0f1
@@ -142,49 +183,9 @@ _pure_ static const char *device_sub_state_to_string(Unit *u) {
17b0f1
         return device_state_to_string(DEVICE(u)->state);
17b0f1
 }
17b0f1
 
17b0f1
-static int device_add_escaped_name(Unit *u, const char *dn) {
17b0f1
-        _cleanup_free_ char *e = NULL;
17b0f1
-        int r;
17b0f1
-
17b0f1
-        assert(u);
17b0f1
-        assert(dn);
17b0f1
-        assert(dn[0] == '/');
17b0f1
-
17b0f1
-        e = unit_name_from_path(dn, ".device");
17b0f1
-        if (!e)
17b0f1
-                return -ENOMEM;
17b0f1
-
17b0f1
-        r = unit_add_name(u, e);
17b0f1
-        if (r < 0 && r != -EEXIST)
17b0f1
-                return r;
17b0f1
-
17b0f1
-        return 0;
17b0f1
-}
17b0f1
-
17b0f1
-static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
17b0f1
-        _cleanup_free_ char *e = NULL;
17b0f1
-        Unit *u;
17b0f1
-
17b0f1
-        assert(m);
17b0f1
-        assert(dn);
17b0f1
-        assert(dn[0] == '/');
17b0f1
-        assert(_u);
17b0f1
-
17b0f1
-        e = unit_name_from_path(dn, ".device");
17b0f1
-        if (!e)
17b0f1
-                return -ENOMEM;
17b0f1
-
17b0f1
-        u = manager_get_unit(m, e);
17b0f1
-        if (u) {
17b0f1
-                *_u = u;
17b0f1
-                return 1;
17b0f1
-        }
17b0f1
-
17b0f1
-        return 0;
17b0f1
-}
17b0f1
-
17b0f1
-static int device_make_description(Unit *u, struct udev_device *dev, const char *path) {
17b0f1
+static int device_update_description(Unit *u, struct udev_device *dev, const char *path) {
17b0f1
         const char *model;
17b0f1
+        int r;
17b0f1
 
17b0f1
         assert(u);
17b0f1
         assert(dev);
17b0f1
@@ -209,13 +210,16 @@ static int device_make_description(Unit *u, struct udev_device *dev, const char
17b0f1
 
17b0f1
                         j = strjoin(model, " ", label, NULL);
17b0f1
                         if (j)
17b0f1
-                                return unit_set_description(u, j);
17b0f1
-                }
17b0f1
+                                r = unit_set_description(u, j);
17b0f1
+                } else
17b0f1
+                        r = unit_set_description(u, model);
17b0f1
+        } else
17b0f1
+                r = unit_set_description(u, path);
17b0f1
 
17b0f1
-                return unit_set_description(u, model);
17b0f1
-        }
17b0f1
+        if (r < 0)
17b0f1
+                log_unit_error_errno(u->id, r, "Failed to set device description: %m");
17b0f1
 
17b0f1
-        return unit_set_description(u, path);
17b0f1
+        return r;
17b0f1
 }
17b0f1
 
17b0f1
 static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
17b0f1
@@ -242,20 +246,20 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
17b0f1
 
17b0f1
                 n = unit_name_mangle(e, MANGLE_NOGLOB);
17b0f1
                 if (!n)
17b0f1
-                        return -ENOMEM;
17b0f1
+                        return log_oom();
17b0f1
 
17b0f1
                 r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
17b0f1
                 if (r < 0)
17b0f1
-                        return r;
17b0f1
+                        return log_unit_error_errno(u->id, r, "Failed to add wants dependency: %m");
17b0f1
         }
17b0f1
         if (!isempty(state))
17b0f1
-                log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.",
17b0f1
-                                 property, strna(udev_device_get_syspath(dev)));
17b0f1
+                log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev)));
17b0f1
 
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
-static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
17b0f1
+static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
17b0f1
+        _cleanup_free_ char *e = NULL;
17b0f1
         const char *sysfs;
17b0f1
         Unit *u = NULL;
17b0f1
         bool delete;
17b0f1
@@ -269,12 +273,18 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
17b0f1
         if (!sysfs)
17b0f1
                 return 0;
17b0f1
 
17b0f1
-        r = device_find_escape_name(m, path, &u);
17b0f1
-        if (r < 0)
17b0f1
-                return r;
17b0f1
+        e = unit_name_from_path(path, ".device");
17b0f1
+        if (!e)
17b0f1
+                return log_oom();
17b0f1
+
17b0f1
+        u = manager_get_unit(m, e);
17b0f1
 
17b0f1
-        if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
17b0f1
+        if (u &&
17b0f1
+            DEVICE(u)->sysfs &&
17b0f1
+            !path_equal(DEVICE(u)->sysfs, sysfs)) {
17b0f1
+                log_unit_error(u->id, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
17b0f1
                 return -EEXIST;
17b0f1
+        }
17b0f1
 
17b0f1
         if (!u) {
17b0f1
                 delete = true;
17b0f1
@@ -283,7 +293,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
17b0f1
                 if (!u)
17b0f1
                         return log_oom();
17b0f1
 
17b0f1
-                r = device_add_escaped_name(u, path);
17b0f1
+                r = unit_add_name(u, e);
17b0f1
                 if (r < 0)
17b0f1
                         goto fail;
17b0f1
 
17b0f1
@@ -295,37 +305,16 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
17b0f1
          * actually been seen yet ->sysfs will not be
17b0f1
          * initialized. Hence initialize it if necessary. */
17b0f1
 
17b0f1
-        if (!DEVICE(u)->sysfs) {
17b0f1
-                Device *first;
17b0f1
-
17b0f1
-                DEVICE(u)->sysfs = strdup(sysfs);
17b0f1
-                if (!DEVICE(u)->sysfs) {
17b0f1
-                        r = -ENOMEM;
17b0f1
-                        goto fail;
17b0f1
-                }
17b0f1
-
17b0f1
-                r = hashmap_ensure_allocated(&m->devices_by_sysfs, &string_hash_ops);
17b0f1
-                if (r < 0)
17b0f1
-                        goto fail;
17b0f1
-
17b0f1
-                first = hashmap_get(m->devices_by_sysfs, sysfs);
17b0f1
-                LIST_PREPEND(same_sysfs, first, DEVICE(u));
17b0f1
-
17b0f1
-                r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first);
17b0f1
-                if (r < 0)
17b0f1
-                        goto fail;
17b0f1
-        }
17b0f1
-
17b0f1
-        device_make_description(u, dev, path);
17b0f1
+        r = device_set_sysfs(DEVICE(u), sysfs);
17b0f1
+        if (r < 0)
17b0f1
+                goto fail;
17b0f1
 
17b0f1
-        if (main) {
17b0f1
-                /* The additional systemd udev properties we only
17b0f1
-                 * interpret for the main object */
17b0f1
+        (void) device_update_description(u, dev, path);
17b0f1
 
17b0f1
-                r = device_add_udev_wants(u, dev);
17b0f1
-                if (r < 0)
17b0f1
-                        goto fail;
17b0f1
-        }
17b0f1
+        /* The additional systemd udev properties we only interpret
17b0f1
+         * for the main object */
17b0f1
+        if (main)
17b0f1
+                (void) device_add_udev_wants(u, dev);
17b0f1
 
17b0f1
         /* Note that this won't dispatch the load queue, the caller
17b0f1
          * has to do that if needed and appropriate */
17b0f1
@@ -334,7 +323,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
17b0f1
         return 0;
17b0f1
 
17b0f1
 fail:
17b0f1
-        log_warning_errno(r, "Failed to load device unit: %m");
17b0f1
+        log_unit_warning_errno(u->id, r, "Failed to set up device unit: %m");
17b0f1
 
17b0f1
         if (delete && u)
17b0f1
                 unit_free(u);
17b0f1
@@ -342,7 +331,7 @@ fail:
17b0f1
         return r;
17b0f1
 }
17b0f1
 
17b0f1
-static int device_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
+static int device_process_new(Manager *m, struct udev_device *dev) {
17b0f1
         const char *sysfs, *dn, *alias;
17b0f1
         struct udev_list_entry *item = NULL, *first = NULL;
17b0f1
         int r;
17b0f1
@@ -354,14 +343,14 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
                 return 0;
17b0f1
 
17b0f1
         /* Add the main unit named after the sysfs path */
17b0f1
-        r = device_update_unit(m, dev, sysfs, true);
17b0f1
+        r = device_setup_unit(m, dev, sysfs, true);
17b0f1
         if (r < 0)
17b0f1
                 return r;
17b0f1
 
17b0f1
         /* Add an additional unit for the device node */
17b0f1
         dn = udev_device_get_devnode(dev);
17b0f1
         if (dn)
17b0f1
-                device_update_unit(m, dev, dn, false);
17b0f1
+                (void) device_setup_unit(m, dev, dn, false);
17b0f1
 
17b0f1
         /* Add additional units for all symlinks */
17b0f1
         first = udev_device_get_devlinks_list_entry(dev);
17b0f1
@@ -388,7 +377,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
                             st.st_rdev != udev_device_get_devnum(dev))
17b0f1
                                 continue;
17b0f1
 
17b0f1
-                device_update_unit(m, dev, p, false);
17b0f1
+                (void) device_setup_unit(m, dev, p, false);
17b0f1
         }
17b0f1
 
17b0f1
         /* Add additional units for all explicitly configured
17b0f1
@@ -405,7 +394,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
                         e[l] = 0;
17b0f1
 
17b0f1
                         if (path_is_absolute(e))
17b0f1
-                                device_update_unit(m, dev, e, false);
17b0f1
+                                (void) device_setup_unit(m, dev, e, false);
17b0f1
                         else
17b0f1
                                 log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e);
17b0f1
                 }
17b0f1
@@ -416,39 +405,62 @@ static int device_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
-static void device_set_path_plugged(Manager *m, struct udev_device *dev) {
17b0f1
-        const char *sysfs;
17b0f1
+static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) {
17b0f1
+        DeviceFound n;
17b0f1
+
17b0f1
+        assert(d);
17b0f1
+
17b0f1
+        n = add ? (d->found | found) : (d->found & ~found);
17b0f1
+        if (n == d->found)
17b0f1
+                return;
17b0f1
+
17b0f1
+        d->found = n;
17b0f1
+
17b0f1
+        if (now) {
17b0f1
+                if (d->found & DEVICE_FOUND_UDEV)
17b0f1
+                        device_set_state(d, DEVICE_PLUGGED);
17b0f1
+                else if (d->found != DEVICE_NOT_FOUND)
17b0f1
+                        device_set_state(d, DEVICE_TENTATIVE);
17b0f1
+                else
17b0f1
+                        device_set_state(d, DEVICE_DEAD);
17b0f1
+        }
17b0f1
+}
17b0f1
+
17b0f1
+static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
17b0f1
         Device *d, *l;
17b0f1
 
17b0f1
         assert(m);
17b0f1
-        assert(dev);
17b0f1
+        assert(sysfs);
17b0f1
 
17b0f1
-        sysfs = udev_device_get_syspath(dev);
17b0f1
-        if (!sysfs)
17b0f1
-                return;
17b0f1
+        if (found == DEVICE_NOT_FOUND)
17b0f1
+                return 0;
17b0f1
 
17b0f1
         l = hashmap_get(m->devices_by_sysfs, sysfs);
17b0f1
         LIST_FOREACH(same_sysfs, d, l)
17b0f1
-                device_set_state(d, DEVICE_PLUGGED);
17b0f1
+                device_update_found_one(d, add, found, now);
17b0f1
+
17b0f1
+        return 0;
17b0f1
 }
17b0f1
 
17b0f1
-static int device_process_removed_device(Manager *m, struct udev_device *dev) {
17b0f1
-        const char *sysfs;
17b0f1
-        Device *d;
17b0f1
+static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) {
17b0f1
+        _cleanup_free_ char *e = NULL;
17b0f1
+        Unit *u;
17b0f1
 
17b0f1
         assert(m);
17b0f1
-        assert(dev);
17b0f1
+        assert(path);
17b0f1
 
17b0f1
-        sysfs = udev_device_get_syspath(dev);
17b0f1
-        if (!sysfs)
17b0f1
-                return -ENOMEM;
17b0f1
+        if (found == DEVICE_NOT_FOUND)
17b0f1
+                return 0;
17b0f1
 
17b0f1
-        /* Remove all units of this sysfs path */
17b0f1
-        while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
17b0f1
-                device_unset_sysfs(d);
17b0f1
-                device_set_state(d, DEVICE_DEAD);
17b0f1
-        }
17b0f1
+        e = unit_name_from_path(path, ".device");
17b0f1
+        if (!e)
17b0f1
+                return log_oom();
17b0f1
 
17b0f1
+        u = manager_get_unit(m, e);
17b0f1
+        if (!u)
17b0f1
+                return 0;
17b0f1
+
17b0f1
+        device_update_found_one(DEVICE(u), add, found, now);
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
@@ -464,22 +476,6 @@ static bool device_is_ready(struct udev_device *dev) {
17b0f1
         return parse_boolean(ready) != 0;
17b0f1
 }
17b0f1
 
17b0f1
-static int device_process_new_path(Manager *m, const char *path) {
17b0f1
-        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
17b0f1
-
17b0f1
-        assert(m);
17b0f1
-        assert(path);
17b0f1
-
17b0f1
-        dev = udev_device_new_from_syspath(m->udev, path);
17b0f1
-        if (!dev)
17b0f1
-                return log_oom();
17b0f1
-
17b0f1
-        if (!device_is_ready(dev))
17b0f1
-                return 0;
17b0f1
-
17b0f1
-        return device_process_new_device(m, dev);
17b0f1
-}
17b0f1
-
17b0f1
 static Unit *device_following(Unit *u) {
17b0f1
         Device *d = DEVICE(u);
17b0f1
         Device *other, *first = NULL;
17b0f1
@@ -606,12 +602,31 @@ static int device_enumerate(Manager *m) {
17b0f1
                 goto fail;
17b0f1
 
17b0f1
         first = udev_enumerate_get_list_entry(e);
17b0f1
-        udev_list_entry_foreach(item, first)
17b0f1
-                device_process_new_path(m, udev_list_entry_get_name(item));
17b0f1
+        udev_list_entry_foreach(item, first) {
17b0f1
+                _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
17b0f1
+                const char *sysfs;
17b0f1
+
17b0f1
+                sysfs = udev_list_entry_get_name(item);
17b0f1
+
17b0f1
+                dev = udev_device_new_from_syspath(m->udev, sysfs);
17b0f1
+                if (!dev) {
17b0f1
+                        log_oom();
17b0f1
+                        continue;
17b0f1
+                }
17b0f1
+
17b0f1
+                if (!device_is_ready(dev))
17b0f1
+                        continue;
17b0f1
+
17b0f1
+                (void) device_process_new(m, dev);
17b0f1
+
17b0f1
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
17b0f1
+        }
17b0f1
 
17b0f1
         return 0;
17b0f1
 
17b0f1
 fail:
17b0f1
+        log_error_errno(r, "Failed to enumerate devices: %m");
17b0f1
+
17b0f1
         device_shutdown(m);
17b0f1
         return r;
17b0f1
 }
17b0f1
@@ -619,7 +634,7 @@ fail:
17b0f1
 static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
17b0f1
         _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
17b0f1
         Manager *m = userdata;
17b0f1
-        const char *action;
17b0f1
+        const char *action, *sysfs;
17b0f1
         int r;
17b0f1
 
17b0f1
         assert(m);
17b0f1
@@ -641,33 +656,47 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
17b0f1
         if (!dev)
17b0f1
                 return 0;
17b0f1
 
17b0f1
+        sysfs = udev_device_get_syspath(dev);
17b0f1
+        if (!sysfs) {
17b0f1
+                log_error("Failed to get udev sys path.");
17b0f1
+                return 0;
17b0f1
+        }
17b0f1
+
17b0f1
         action = udev_device_get_action(dev);
17b0f1
         if (!action) {
17b0f1
                 log_error("Failed to get udev action string.");
17b0f1
                 return 0;
17b0f1
         }
17b0f1
 
17b0f1
-        if (streq(action, "remove") || !device_is_ready(dev))  {
17b0f1
-                r = device_process_removed_device(m, dev);
17b0f1
-                if (r < 0)
17b0f1
-                        log_error_errno(r, "Failed to process device remove event: %m");
17b0f1
-
17b0f1
-                r = swap_process_removed_device(m, dev);
17b0f1
+        if (streq(action, "remove"))  {
17b0f1
+                r = swap_process_device_remove(m, dev);
17b0f1
                 if (r < 0)
17b0f1
                         log_error_errno(r, "Failed to process swap device remove event: %m");
17b0f1
 
17b0f1
-        } else {
17b0f1
-                r = device_process_new_device(m, dev);
17b0f1
-                if (r < 0)
17b0f1
-                        log_error_errno(r, "Failed to process device new event: %m");
17b0f1
+                /* If we get notified that a device was removed by
17b0f1
+                 * udev, then it's completely gone, hence unset all
17b0f1
+                 * found bits */
17b0f1
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true);
17b0f1
 
17b0f1
-                r = swap_process_new_device(m, dev);
17b0f1
+        } else if (device_is_ready(dev)) {
17b0f1
+
17b0f1
+                (void) device_process_new(m, dev);
17b0f1
+
17b0f1
+                r = swap_process_device_new(m, dev);
17b0f1
                 if (r < 0)
17b0f1
                         log_error_errno(r, "Failed to process swap device new event: %m");
17b0f1
 
17b0f1
                 manager_dispatch_load_queue(m);
17b0f1
 
17b0f1
-                device_set_path_plugged(m, dev);
17b0f1
+                /* The device is found now, set the udev found bit */
17b0f1
+                device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true);
17b0f1
+
17b0f1
+        } else {
17b0f1
+                /* The device is nominally around, but not ready for
17b0f1
+                 * us. Hence unset the udev bit, but leave the rest
17b0f1
+                 * around. */
17b0f1
+
17b0f1
+                device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true);
17b0f1
         }
17b0f1
 
17b0f1
         return 0;
17b0f1
@@ -686,9 +715,58 @@ static bool device_supported(Manager *m) {
17b0f1
         return read_only <= 0;
17b0f1
 }
17b0f1
 
17b0f1
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) {
17b0f1
+        _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
17b0f1
+        struct stat st;
17b0f1
+
17b0f1
+        assert(m);
17b0f1
+        assert(node);
17b0f1
+
17b0f1
+        /* This is called whenever we find a device referenced in
17b0f1
+         * /proc/swaps or /proc/self/mounts. Such a device might be
17b0f1
+         * mounted/enabled at a time where udev has not finished
17b0f1
+         * probing it yet, and we thus haven't learned about it
17b0f1
+         * yet. In this case we will set the device unit to
17b0f1
+         * "tentative" state. */
17b0f1
+
17b0f1
+        if (add) {
17b0f1
+                if (!path_startswith(node, "/dev"))
17b0f1
+                        return 0;
17b0f1
+
17b0f1
+                if (stat(node, &st) < 0) {
17b0f1
+                        if (errno == ENOENT)
17b0f1
+                                return 0;
17b0f1
+
17b0f1
+                        return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
17b0f1
+                }
17b0f1
+
17b0f1
+                if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
17b0f1
+                        return 0;
17b0f1
+
17b0f1
+                dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
17b0f1
+                if (!dev) {
17b0f1
+                        if (errno == ENOENT)
17b0f1
+                                return 0;
17b0f1
+
17b0f1
+                        return log_oom();
17b0f1
+                }
17b0f1
+
17b0f1
+                /* If the device is known in the kernel and newly
17b0f1
+                 * appeared, then we'll create a device unit for it,
17b0f1
+                 * under the name referenced in /proc/swaps or
17b0f1
+                 * /proc/self/mountinfo. */
17b0f1
+
17b0f1
+                (void) device_setup_unit(m, dev, node, false);
17b0f1
+        }
17b0f1
+
17b0f1
+        /* Update the device unit's state, should it exist */
17b0f1
+        return device_update_found_by_name(m, node, add, found, now);
17b0f1
+}
17b0f1
+
17b0f1
 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
17b0f1
         [DEVICE_DEAD] = "dead",
17b0f1
-        [DEVICE_PLUGGED] = "plugged"
17b0f1
+        [DEVICE_TENTATIVE] = "tentative",
17b0f1
+        [DEVICE_PLUGGED] = "plugged",
17b0f1
 };
17b0f1
 
17b0f1
 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
17b0f1
diff --git a/src/core/device.h b/src/core/device.h
17b0f1
index bb7ae07834..0609b20fdb 100644
17b0f1
--- a/src/core/device.h
17b0f1
+++ b/src/core/device.h
17b0f1
@@ -29,20 +29,28 @@ typedef struct Device Device;
17b0f1
  * simplifies the state engine greatly */
17b0f1
 typedef enum DeviceState {
17b0f1
         DEVICE_DEAD,
17b0f1
-        DEVICE_PLUGGED,
17b0f1
+        DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
17b0f1
+        DEVICE_PLUGGED,   /* announced by udev */
17b0f1
         _DEVICE_STATE_MAX,
17b0f1
         _DEVICE_STATE_INVALID = -1
17b0f1
 } DeviceState;
17b0f1
 
17b0f1
+typedef enum DeviceFound {
17b0f1
+        DEVICE_NOT_FOUND = 0,
17b0f1
+        DEVICE_FOUND_UDEV = 1,
17b0f1
+        DEVICE_FOUND_MOUNT = 2,
17b0f1
+        DEVICE_FOUND_SWAP = 4,
17b0f1
+} DeviceFound;
17b0f1
+
17b0f1
 struct Device {
17b0f1
         Unit meta;
17b0f1
 
17b0f1
         char *sysfs;
17b0f1
+        DeviceFound found;
17b0f1
 
17b0f1
         /* In order to be able to distinguish dependencies on
17b0f1
         different device nodes we might end up creating multiple
17b0f1
         devices for the same sysfs path. We chain them up here. */
17b0f1
-
17b0f1
         LIST_FIELDS(struct Device, same_sysfs);
17b0f1
 
17b0f1
         DeviceState state;
17b0f1
@@ -52,3 +60,5 @@ extern const UnitVTable device_vtable;
17b0f1
 
17b0f1
 const char* device_state_to_string(DeviceState i) _const_;
17b0f1
 DeviceState device_state_from_string(const char *s) _pure_;
17b0f1
+
17b0f1
+int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
17b0f1
diff --git a/src/core/mount.c b/src/core/mount.c
17b0f1
index f3977e62de..c971330af2 100644
17b0f1
--- a/src/core/mount.c
17b0f1
+++ b/src/core/mount.c
17b0f1
@@ -1391,7 +1391,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
-static int mount_add_one(
17b0f1
+static int mount_setup_unit(
17b0f1
                 Manager *m,
17b0f1
                 const char *what,
17b0f1
                 const char *where,
17b0f1
@@ -1434,7 +1434,7 @@ static int mount_add_one(
17b0f1
 
17b0f1
                 u = unit_new(m, sizeof(Mount));
17b0f1
                 if (!u)
17b0f1
-                        return -ENOMEM;
17b0f1
+                        return log_oom();
17b0f1
 
17b0f1
                 r = unit_add_name(u, e);
17b0f1
                 if (r < 0)
17b0f1
@@ -1547,6 +1547,8 @@ static int mount_add_one(
17b0f1
         return 0;
17b0f1
 
17b0f1
 fail:
17b0f1
+        log_warning_errno(r, "Failed to set up mount unit: %m");
17b0f1
+
17b0f1
         if (delete && u)
17b0f1
                 unit_free(u);
17b0f1
 
17b0f1
@@ -1554,33 +1556,36 @@ fail:
17b0f1
 }
17b0f1
 
17b0f1
 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
17b0f1
-        _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL;
17b0f1
-        _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL;
17b0f1
-        struct libmnt_fs *fs;
17b0f1
+        _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
17b0f1
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
17b0f1
         int r = 0;
17b0f1
 
17b0f1
         assert(m);
17b0f1
 
17b0f1
-        tb = mnt_new_table();
17b0f1
-        itr = mnt_new_iter(MNT_ITER_FORWARD);
17b0f1
-        if (!tb || !itr)
17b0f1
+        t = mnt_new_table();
17b0f1
+        if (!t)
17b0f1
                 return log_oom();
17b0f1
 
17b0f1
-        r = mnt_table_parse_mtab(tb, NULL);
17b0f1
+        i = mnt_new_iter(MNT_ITER_FORWARD);
17b0f1
+        if (!i)
17b0f1
+                return log_oom();
17b0f1
+
17b0f1
+        r = mnt_table_parse_mtab(t, NULL);
17b0f1
         if (r < 0)
17b0f1
-                return r;
17b0f1
+                return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
17b0f1
 
17b0f1
         r = 0;
17b0f1
         for (;;) {
17b0f1
                 const char *device, *path, *options, *fstype;
17b0f1
                 _cleanup_free_ const char *d = NULL, *p = NULL;
17b0f1
+                struct libmnt_fs *fs;
17b0f1
                 int k;
17b0f1
 
17b0f1
-                k = mnt_table_next_fs(tb, itr, &fs);
17b0f1
+                k = mnt_table_next_fs(t, i, &fs);
17b0f1
                 if (k == 1)
17b0f1
                         break;
17b0f1
-                else if (k < 0)
17b0f1
-                        return log_error_errno(k, "Failed to get next entry from /etc/fstab: %m");
17b0f1
+                if (k < 0)
17b0f1
+                        return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m");
17b0f1
 
17b0f1
                 device = mnt_fs_get_source(fs);
17b0f1
                 path = mnt_fs_get_target(fs);
17b0f1
@@ -1588,11 +1593,16 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
17b0f1
                 fstype = mnt_fs_get_fstype(fs);
17b0f1
 
17b0f1
                 d = cunescape(device);
17b0f1
+                if (!d)
17b0f1
+                        return log_oom();
17b0f1
+
17b0f1
                 p = cunescape(path);
17b0f1
-                if (!d || !p)
17b0f1
+                if (!p)
17b0f1
                         return log_oom();
17b0f1
 
17b0f1
-                k = mount_add_one(m, d, p, options, fstype, set_flags);
17b0f1
+                (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
17b0f1
+
17b0f1
+                k = mount_setup_unit(m, d, p, options, fstype, set_flags);
17b0f1
                 if (r == 0 && k < 0)
17b0f1
                         r = k;
17b0f1
         }
17b0f1
@@ -1736,8 +1746,6 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
17b0f1
 
17b0f1
         r = mount_load_proc_self_mountinfo(m, true);
17b0f1
         if (r < 0) {
17b0f1
-                log_error_errno(r, "Failed to reread /proc/self/mountinfo: %m");
17b0f1
-
17b0f1
                 /* Reset flags, just in case, for later calls */
17b0f1
                 LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
17b0f1
                         Mount *mount = MOUNT(u);
17b0f1
@@ -1770,6 +1778,10 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
17b0f1
                                 break;
17b0f1
                         }
17b0f1
 
17b0f1
+                        if (mount->parameters_proc_self_mountinfo.what)
17b0f1
+                                (void) device_found_node(m, mount->parameters_proc_self_mountinfo.what, false, DEVICE_FOUND_MOUNT, true);
17b0f1
+
17b0f1
+
17b0f1
                 } else if (mount->just_mounted || mount->just_changed) {
17b0f1
 
17b0f1
                         /* New or changed mount entry */
17b0f1
diff --git a/src/core/swap.c b/src/core/swap.c
17b0f1
index 6997921fde..5c19af5d91 100644
17b0f1
--- a/src/core/swap.c
17b0f1
+++ b/src/core/swap.c
17b0f1
@@ -338,7 +338,7 @@ static int swap_load(Unit *u) {
17b0f1
         return swap_verify(s);
17b0f1
 }
17b0f1
 
17b0f1
-static int swap_add_one(
17b0f1
+static int swap_setup_unit(
17b0f1
                 Manager *m,
17b0f1
                 const char *what,
17b0f1
                 const char *what_proc_swaps,
17b0f1
@@ -363,8 +363,10 @@ static int swap_add_one(
17b0f1
 
17b0f1
         if (u &&
17b0f1
             SWAP(u)->from_proc_swaps &&
17b0f1
-            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
17b0f1
+            !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) {
17b0f1
+                log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
17b0f1
                 return -EEXIST;
17b0f1
+        }
17b0f1
 
17b0f1
         if (!u) {
17b0f1
                 delete = true;
17b0f1
@@ -379,7 +381,7 @@ static int swap_add_one(
17b0f1
 
17b0f1
                 SWAP(u)->what = strdup(what);
17b0f1
                 if (!SWAP(u)->what) {
17b0f1
-                        r = log_oom();
17b0f1
+                        r = -ENOMEM;
17b0f1
                         goto fail;
17b0f1
                 }
17b0f1
 
17b0f1
@@ -407,7 +409,6 @@ static int swap_add_one(
17b0f1
         p->priority = priority;
17b0f1
 
17b0f1
         unit_add_to_dbus_queue(u);
17b0f1
-
17b0f1
         return 0;
17b0f1
 
17b0f1
 fail:
17b0f1
@@ -419,7 +420,7 @@ fail:
17b0f1
         return r;
17b0f1
 }
17b0f1
 
17b0f1
-static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
17b0f1
+static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
17b0f1
         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
17b0f1
         struct udev_list_entry *item = NULL, *first = NULL;
17b0f1
         const char *dn;
17b0f1
@@ -428,7 +429,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
17b0f1
 
17b0f1
         assert(m);
17b0f1
 
17b0f1
-        r = swap_add_one(m, device, device, prio, set_flags);
17b0f1
+        r = swap_setup_unit(m, device, device, prio, set_flags);
17b0f1
         if (r < 0)
17b0f1
                 return r;
17b0f1
 
17b0f1
@@ -444,7 +445,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
17b0f1
         /* Add the main device node */
17b0f1
         dn = udev_device_get_devnode(d);
17b0f1
         if (dn && !streq(dn, device))
17b0f1
-                swap_add_one(m, dn, device, prio, set_flags);
17b0f1
+                swap_setup_unit(m, dn, device, prio, set_flags);
17b0f1
 
17b0f1
         /* Add additional units for all symlinks */
17b0f1
         first = udev_device_get_devlinks_list_entry(d);
17b0f1
@@ -465,7 +466,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
17b0f1
                             st.st_rdev != udev_device_get_devnum(d))
17b0f1
                                 continue;
17b0f1
 
17b0f1
-                swap_add_one(m, p, device, prio, set_flags);
17b0f1
+                swap_setup_unit(m, p, device, prio, set_flags);
17b0f1
         }
17b0f1
 
17b0f1
         return r;
17b0f1
@@ -1091,15 +1092,17 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
17b0f1
                         if (k == EOF)
17b0f1
                                 break;
17b0f1
 
17b0f1
-                        log_warning("Failed to parse /proc/swaps:%u", i);
17b0f1
+                        log_warning("Failed to parse /proc/swaps:%u.", i);
17b0f1
                         continue;
17b0f1
                 }
17b0f1
 
17b0f1
                 d = cunescape(dev);
17b0f1
                 if (!d)
17b0f1
-                        return -ENOMEM;
17b0f1
+                        return log_oom();
17b0f1
+
17b0f1
+                device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);
17b0f1
 
17b0f1
-                k = swap_process_new_swap(m, d, prio, set_flags);
17b0f1
+                k = swap_process_new(m, d, prio, set_flags);
17b0f1
                 if (k < 0)
17b0f1
                         r = k;
17b0f1
         }
17b0f1
@@ -1151,6 +1154,9 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
17b0f1
                                 break;
17b0f1
                         }
17b0f1
 
17b0f1
+                        if (swap->what)
17b0f1
+                                device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true);
17b0f1
+
17b0f1
                 } else if (swap->just_activated) {
17b0f1
 
17b0f1
                         /* New swap entry */
17b0f1
@@ -1298,7 +1304,7 @@ fail:
17b0f1
         return r;
17b0f1
 }
17b0f1
 
17b0f1
-int swap_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
+int swap_process_device_new(Manager *m, struct udev_device *dev) {
17b0f1
         struct udev_list_entry *item = NULL, *first = NULL;
17b0f1
         _cleanup_free_ char *e = NULL;
17b0f1
         const char *dn;
17b0f1
@@ -1341,7 +1347,7 @@ int swap_process_new_device(Manager *m, struct udev_device *dev) {
17b0f1
         return r;
17b0f1
 }
17b0f1
 
17b0f1
-int swap_process_removed_device(Manager *m, struct udev_device *dev) {
17b0f1
+int swap_process_device_remove(Manager *m, struct udev_device *dev) {
17b0f1
         const char *dn;
17b0f1
         int r = 0;
17b0f1
         Swap *s;
17b0f1
diff --git a/src/core/swap.h b/src/core/swap.h
17b0f1
index 73e64d87a4..914a2dbccd 100644
17b0f1
--- a/src/core/swap.h
17b0f1
+++ b/src/core/swap.h
17b0f1
@@ -116,8 +116,8 @@ struct Swap {
17b0f1
 
17b0f1
 extern const UnitVTable swap_vtable;
17b0f1
 
17b0f1
-int swap_process_new_device(Manager *m, struct udev_device *dev);
17b0f1
-int swap_process_removed_device(Manager *m, struct udev_device *dev);
17b0f1
+int swap_process_device_new(Manager *m, struct udev_device *dev);
17b0f1
+int swap_process_device_remove(Manager *m, struct udev_device *dev);
17b0f1
 
17b0f1
 const char* swap_state_to_string(SwapState i) _const_;
17b0f1
 SwapState swap_state_from_string(const char *s) _pure_;
17b0f1
diff --git a/src/core/unit.c b/src/core/unit.c
17b0f1
index 563f6fe850..a6558ee23b 100644
17b0f1
--- a/src/core/unit.c
17b0f1
+++ b/src/core/unit.c
17b0f1
@@ -2843,7 +2843,6 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
17b0f1
                 return -ENOMEM;
17b0f1
 
17b0f1
         r = manager_load_unit(u->manager, e, NULL, NULL, &device);
17b0f1
-
17b0f1
         if (r < 0)
17b0f1
                 return r;
17b0f1