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