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