multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / configure.c
index ce07741..5796683 100644 (file)
 #include <string.h>
 #include <sys/file.h>
 #include <errno.h>
+#include <ctype.h>
 #include <libdevmapper.h>
 #include <libudev.h>
-#include <mpath_cmd.h>
+#include "mpath_cmd.h"
 
 #include "checkers.h"
 #include "vector.h"
@@ -30,6 +31,7 @@
 #include "discovery.h"
 #include "debug.h"
 #include "switchgroup.h"
+#include "dm-generic.h"
 #include "print.h"
 #include "configure.h"
 #include "pgpolicies.h"
@@ -39,6 +41,8 @@
 #include "util.h"
 #include "uxsock.h"
 #include "wwids.h"
+#include "sysfs.h"
+#include "io_err_stat.h"
 
 /* group paths in pg by host adapter
  */
@@ -253,10 +257,11 @@ int rr_optimize_path_order(struct pathgroup *pgp)
        return 0;
 }
 
-extern int
-setup_map (struct multipath * mpp, char * params, int params_size)
+int setup_map(struct multipath *mpp, char *params, int params_size,
+             struct vectors *vecs)
 {
        struct pathgroup * pgp;
+       struct config *conf;
        int i;
 
        /*
@@ -271,30 +276,56 @@ setup_map (struct multipath * mpp, char * params, int params_size)
         * free features, selector, and hwhandler properties if they are being reused
         */
        free_multipath_attributes(mpp);
+       if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0)
+               mpp->disable_queueing = 0;
 
        /*
         * properties selectors
+        *
+        * Ordering matters for some properties:
+        * - features after no_path_retry and retain_hwhandler
+        * - hwhandler after retain_hwhandler
+        * No guarantee that this list is complete, check code in
+        * propsel.c if in doubt.
         */
-       select_pgfailback(mpp);
-       select_pgpolicy(mpp);
-       select_selector(mpp);
-       select_features(mpp);
-       select_hwhandler(mpp);
-       select_rr_weight(mpp);
-       select_minio(mpp);
-       select_no_path_retry(mpp);
-       select_mode(mpp);
-       select_uid(mpp);
-       select_gid(mpp);
-       select_fast_io_fail(mpp);
-       select_dev_loss(mpp);
-       select_reservation_key(mpp);
-       select_retain_hwhandler(mpp);
-       select_deferred_remove(mpp);
-       select_delay_watch_checks(mpp);
-       select_delay_wait_checks(mpp);
-
-       sysfs_set_scsi_tmo(mpp);
+       conf = get_multipath_config();
+       pthread_cleanup_push(put_multipath_config, conf);
+
+       select_pgfailback(conf, mpp);
+       select_pgpolicy(conf, mpp);
+       select_selector(conf, mpp);
+       select_no_path_retry(conf, mpp);
+       select_retain_hwhandler(conf, mpp);
+       select_features(conf, mpp);
+       select_hwhandler(conf, mpp);
+       select_rr_weight(conf, mpp);
+       select_minio(conf, mpp);
+       select_mode(conf, mpp);
+       select_uid(conf, mpp);
+       select_gid(conf, mpp);
+       select_fast_io_fail(conf, mpp);
+       select_dev_loss(conf, mpp);
+       select_reservation_key(conf, mpp);
+       select_deferred_remove(conf, mpp);
+       select_delay_watch_checks(conf, mpp);
+       select_delay_wait_checks(conf, mpp);
+       select_marginal_path_err_sample_time(conf, mpp);
+       select_marginal_path_err_rate_threshold(conf, mpp);
+       select_marginal_path_err_recheck_gap_time(conf, mpp);
+       select_marginal_path_double_failed_time(conf, mpp);
+       select_skip_kpartx(conf, mpp);
+       select_max_sectors_kb(conf, mpp);
+       select_ghost_delay(conf, mpp);
+       select_flush_on_last_del(conf, mpp);
+
+       sysfs_set_scsi_tmo(mpp, conf->checkint);
+       pthread_cleanup_pop(1);
+
+       if (mpp->marginal_path_double_failed_time > 0 &&
+           mpp->marginal_path_err_sample_time > 0 &&
+           mpp->marginal_path_err_recheck_gap_time > 0 &&
+           mpp->marginal_path_err_rate_threshold >= 0)
+               start_io_err_stat_thread(vecs);
        /*
         * assign paths to path groups -- start with no groups and all paths
         * in mpp->paths
@@ -382,11 +413,161 @@ pgcmp (struct multipath * mpp, struct multipath * cmpp)
        return r;
 }
 
+static struct udev_device *
+get_udev_for_mpp(const struct multipath *mpp)
+{
+       dev_t devnum;
+       struct udev_device *udd;
+
+       if (!mpp || !mpp->dmi) {
+               condlog(1, "%s called with empty mpp", __func__);
+               return NULL;
+       }
+
+       devnum = makedev(mpp->dmi->major, mpp->dmi->minor);
+       udd = udev_device_new_from_devnum(udev, 'b', devnum);
+       if (!udd) {
+               condlog(1, "failed to get udev device for %s", mpp->alias);
+               return NULL;
+       }
+       return udd;
+}
+
+static void
+trigger_udev_change(const struct multipath *mpp)
+{
+       static const char change[] = "change";
+       struct udev_device *udd = get_udev_for_mpp(mpp);
+       if (!udd)
+               return;
+       condlog(3, "triggering %s uevent for %s", change, mpp->alias);
+       sysfs_attr_set_value(udd, "uevent", change, sizeof(change)-1);
+       udev_device_unref(udd);
+}
+
+void
+trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
+{
+       struct pathgroup *pgp;
+       struct path *pp;
+       int i, j;
+       /*
+        * If a path changes from multipath to non-multipath, we must
+        * synthesize an artificial "add" event, otherwise the LVM2 rules
+        * (69-lvm2-lvmetad.rules) won't pick it up. Otherwise, we'd just
+        * irritate ourselves with an "add", so use "change".
+        */
+       const char *action = is_mpath ? "change" : "add";
+
+       if (!mpp || !mpp->pg)
+               return;
+
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               if (!pgp->paths)
+                       continue;
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       const char *env;
+
+                       if (!pp->udev)
+                               continue;
+                       /*
+                        * Paths that are already classified as multipath
+                        * members don't need another uevent.
+                        */
+                       env = udev_device_get_property_value(
+                               pp->udev, "DM_MULTIPATH_DEVICE_PATH");
+
+                       if (is_mpath && env != NULL && !strcmp(env, "1")) {
+                               /*
+                                * If FIND_MULTIPATHS_WAIT_UNTIL is not "0",
+                                * path is in "maybe" state and timer is running
+                                * Send uevent now (see multipath.rules).
+                                */
+                               env = udev_device_get_property_value(
+                                       pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL");
+                               if (env == NULL || !strcmp(env, "0"))
+                                       continue;
+                       } else if (!is_mpath &&
+                                  (env == NULL || !strcmp(env, "0")))
+                               continue;
+
+                       condlog(3, "triggering %s uevent for %s (is %smultipath member)",
+                               action, pp->dev, is_mpath ? "" : "no ");
+                       sysfs_attr_set_value(pp->udev, "uevent",
+                                            action, strlen(action));
+               }
+       }
+
+       mpp->needs_paths_uevent = 0;
+}
+
+static int
+is_mpp_known_to_udev(const struct multipath *mpp)
+{
+       struct udev_device *udd = get_udev_for_mpp(mpp);
+       int ret = (udd != NULL);
+       udev_device_unref(udd);
+       return ret;
+}
+
+static int
+sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
+{
+       struct pathgroup * pgp;
+       struct path *pp;
+       char buff[11];
+       int i, j, ret, err = 0;
+       struct udev_device *udd;
+       int max_sectors_kb;
+
+       if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
+               return 0;
+       max_sectors_kb = mpp->max_sectors_kb;
+       if (is_reload) {
+               if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) {
+                       condlog(1, "failed to get dm info for %s", mpp->alias);
+                       return 1;
+               }
+               udd = get_udev_for_mpp(mpp);
+               if (!udd) {
+                       condlog(1, "failed to get udev device to set max_sectors_kb for %s", mpp->alias);
+                       return 1;
+               }
+               ret = sysfs_attr_get_value(udd, "queue/max_sectors_kb", buff,
+                                          sizeof(buff));
+               udev_device_unref(udd);
+               if (ret <= 0) {
+                       condlog(1, "failed to get current max_sectors_kb from %s", mpp->alias);
+                       return 1;
+               }
+               if (sscanf(buff, "%u\n", &max_sectors_kb) != 1) {
+                       condlog(1, "can't parse current max_sectors_kb from %s",
+                               mpp->alias);
+                       return 1;
+               }
+       }
+       snprintf(buff, 11, "%d", max_sectors_kb);
+
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       ret = sysfs_attr_set_value(pp->udev,
+                                                  "queue/max_sectors_kb",
+                                                  buff, strlen(buff));
+                       if (ret < 0) {
+                               condlog(1, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret));
+                               err = 1;
+                       }
+               }
+       }
+       return err;
+}
+
 static void
 select_action (struct multipath * mpp, vector curmp, int force_reload)
 {
        struct multipath * cmpp;
        struct multipath * cmpp_by_name;
+       char * mpp_feat, * cmpp_feat;
 
        cmpp = find_mp_by_wwid(curmp, mpp->wwid);
        cmpp_by_name = find_mp_by_alias(curmp, mpp->alias);
@@ -397,8 +578,10 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                                cmpp->alias, mpp->alias);
                        strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE - 1);
                        mpp->action = ACT_RENAME;
-                       if (force_reload)
+                       if (force_reload) {
+                               mpp->force_udev_reload = 1;
                                mpp->action = ACT_FORCERENAME;
+                       }
                        return;
                }
                mpp->action = ACT_CREATE;
@@ -425,38 +608,42 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                /* reset alias to existing alias */
                FREE(mpp->alias);
                mpp->alias = STRDUP(cmpp->alias);
-               mpp->action = ACT_NOTHING;
+               mpp->action = ACT_IMPOSSIBLE;
                return;
        }
 
        if (pathcount(mpp, PATH_UP) == 0) {
-               mpp->action = ACT_NOTHING;
-               condlog(3, "%s: set ACT_NOTHING (no usable path)",
+               mpp->action = ACT_IMPOSSIBLE;
+               condlog(3, "%s: set ACT_IMPOSSIBLE (no usable path)",
                        mpp->alias);
                return;
        }
        if (force_reload) {
+               mpp->force_udev_reload = 1;
                mpp->action = ACT_RELOAD;
                condlog(3, "%s: set ACT_RELOAD (forced by user)",
                        mpp->alias);
                return;
        }
        if (cmpp->size != mpp->size) {
+               mpp->force_udev_reload = 1;
                mpp->action = ACT_RESIZE;
                condlog(3, "%s: set ACT_RESIZE (size change)",
                        mpp->alias);
                return;
        }
-       if (!mpp->no_path_retry &&
-           (strlen(cmpp->features) != strlen(mpp->features) ||
-            strcmp(cmpp->features, mpp->features))) {
+
+       if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
+           !!strstr(mpp->features, "queue_if_no_path") !=
+           !!strstr(cmpp->features, "queue_if_no_path")) {
                mpp->action =  ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (features change)",
+               condlog(3, "%s: set ACT_RELOAD (no_path_retry change)",
                        mpp->alias);
                return;
        }
-       if (mpp->retain_hwhandler != RETAIN_HWHANDLER_ON &&
-            (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
+       if ((mpp->retain_hwhandler != RETAIN_HWHANDLER_ON ||
+            strcmp(cmpp->hwhandler, "0") == 0) &&
+           (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
             strncmp(cmpp->hwhandler, mpp->hwhandler,
                    strlen(mpp->hwhandler)))) {
                mpp->action = ACT_RELOAD;
@@ -464,6 +651,33 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                        mpp->alias);
                return;
        }
+
+       if (mpp->retain_hwhandler != RETAIN_HWHANDLER_UNDEF &&
+           !!strstr(mpp->features, "retain_attached_hw_handler") !=
+           !!strstr(cmpp->features, "retain_attached_hw_handler") &&
+           get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) {
+               mpp->action = ACT_RELOAD;
+               condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)",
+                       mpp->alias);
+               return;
+       }
+
+       cmpp_feat = STRDUP(cmpp->features);
+       mpp_feat = STRDUP(mpp->features);
+       if (cmpp_feat && mpp_feat) {
+               remove_feature(&mpp_feat, "queue_if_no_path");
+               remove_feature(&mpp_feat, "retain_attached_hw_handler");
+               remove_feature(&cmpp_feat, "queue_if_no_path");
+               remove_feature(&cmpp_feat, "retain_attached_hw_handler");
+               if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) {
+                       mpp->action =  ACT_RELOAD;
+                       condlog(3, "%s: set ACT_RELOAD (features change)",
+                               mpp->alias);
+               }
+       }
+       FREE(cmpp_feat);
+       FREE(mpp_feat);
+
        if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector,
                    strlen(mpp->selector))) {
                mpp->action = ACT_RELOAD;
@@ -495,14 +709,19 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                        mpp->alias);
                return;
        }
+       if (!is_mpp_known_to_udev(cmpp)) {
+               mpp->action = ACT_RELOAD;
+               condlog(3, "%s: set ACT_RELOAD (udev device not initialized)",
+                       mpp->alias);
+               return;
+       }
        mpp->action = ACT_NOTHING;
        condlog(3, "%s: set ACT_NOTHING (map unchanged)",
                mpp->alias);
        return;
 }
 
-extern int
-reinstate_paths (struct multipath * mpp)
+int reinstate_paths(struct multipath *mpp)
 {
        int i, j;
        struct pathgroup * pgp;
@@ -578,16 +797,20 @@ fail:
 #define DOMAP_EXIST    2
 #define DOMAP_DRY      3
 
-extern int
-domap (struct multipath * mpp, char * params, int is_daemon)
+int domap(struct multipath *mpp, char *params, int is_daemon)
 {
        int r = DOMAP_FAIL;
+       struct config *conf;
+       int verbosity;
 
        /*
         * last chance to quit before touching the devmaps
         */
        if (mpp->action == ACT_DRY_RUN) {
-               print_multipath_topology(mpp, conf->verbosity);
+               conf = get_multipath_config();
+               verbosity = conf->verbosity;
+               put_multipath_config(conf);
+               print_multipath_topology(mpp, verbosity);
                return DOMAP_DRY;
        }
 
@@ -600,6 +823,7 @@ domap (struct multipath * mpp, char * params, int is_daemon)
        switch (mpp->action) {
        case ACT_REJECT:
        case ACT_NOTHING:
+       case ACT_IMPOSSIBLE:
                return DOMAP_EXIST;
 
        case ACT_SWITCHPG:
@@ -619,29 +843,50 @@ domap (struct multipath * mpp, char * params, int is_daemon)
                        return DOMAP_RETRY;
                }
 
+               sysfs_set_max_sectors_kb(mpp, 0);
+               if (is_daemon && mpp->ghost_delay > 0 && mpp->nr_active &&
+                   pathcount(mpp, PATH_GHOST) == mpp->nr_active)
+                       mpp->ghost_delay_tick = mpp->ghost_delay;
                r = dm_addmap_create(mpp, params);
 
                lock_multipath(mpp, 0);
                break;
 
        case ACT_RELOAD:
+               sysfs_set_max_sectors_kb(mpp, 1);
+               if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))
+                       mpp->ghost_delay_tick = 0;
                r = dm_addmap_reload(mpp, params, 0);
                break;
 
        case ACT_RESIZE:
+               sysfs_set_max_sectors_kb(mpp, 1);
+               if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))
+                       mpp->ghost_delay_tick = 0;
                r = dm_addmap_reload(mpp, params, 1);
                break;
 
        case ACT_RENAME:
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                r = dm_rename(mpp->alias_old, mpp->alias,
-                             conf->partition_delim);
+                             conf->partition_delim, mpp->skip_kpartx);
+               pthread_cleanup_pop(1);
                break;
 
        case ACT_FORCERENAME:
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                r = dm_rename(mpp->alias_old, mpp->alias,
-                             conf->partition_delim);
-               if (r)
+                             conf->partition_delim, mpp->skip_kpartx);
+               pthread_cleanup_pop(1);
+               if (r) {
+                       sysfs_set_max_sectors_kb(mpp, 1);
+                       if (mpp->ghost_delay_tick > 0 &&
+                           pathcount(mpp, PATH_UP))
+                               mpp->ghost_delay_tick = 0;
                        r = dm_addmap_reload(mpp, params, 0);
+               }
                break;
 
        default:
@@ -653,8 +898,11 @@ domap (struct multipath * mpp, char * params, int is_daemon)
                 * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD
                 * succeeded
                 */
-               if (mpp->action == ACT_CREATE)
-                       remember_wwid(mpp->wwid);
+               mpp->force_udev_reload = 0;
+               if (mpp->action == ACT_CREATE &&
+                   (remember_wwid(mpp->wwid) == 1 ||
+                    mpp->needs_paths_uevent))
+                       trigger_paths_udev_change(mpp, true);
                if (!is_daemon) {
                        /* multipath client mode */
                        dm_switchgroup(mpp->alias, mpp->bestpg);
@@ -671,13 +919,18 @@ domap (struct multipath * mpp, char * params, int is_daemon)
                        if (mpp->action != ACT_CREATE)
                                mpp->action = ACT_NOTHING;
                        else {
+                               conf = get_multipath_config();
                                mpp->wait_for_udev = 1;
                                mpp->uev_wait_tick = conf->uev_wait_timeout;
+                               put_multipath_config(conf);
                        }
                }
                dm_setgeometry(mpp);
                return DOMAP_OK;
-       }
+       } else if (r == DOMAP_FAIL && mpp->action == ACT_CREATE &&
+                  mpp->needs_paths_uevent)
+               trigger_paths_udev_change(mpp, false);
+
        return DOMAP_FAIL;
 }
 
@@ -708,6 +961,8 @@ int check_daemon(void)
        int fd;
        char *reply;
        int ret = 0;
+       unsigned int timeout;
+       struct config *conf;
 
        fd = mpath_connect();
        if (fd == -1)
@@ -715,7 +970,10 @@ int check_daemon(void)
 
        if (send_packet(fd, "show daemon") != 0)
                goto out;
-       if (recv_packet(fd, &reply, conf->uxsock_timeout) != 0)
+       conf = get_multipath_config();
+       timeout = conf->uxsock_timeout;
+       put_multipath_config(conf);
+       if (recv_packet(fd, &reply, timeout) != 0)
                goto out;
 
        if (strstr(reply, "shutdown"))
@@ -730,8 +988,15 @@ out:
        return ret;
 }
 
-extern int
-coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_reload, enum mpath_cmds cmd)
+/*
+ * The force_reload parameter determines how coalesce_paths treats existing maps.
+ * FORCE_RELOAD_NONE: existing maps aren't touched at all
+ * FORCE_RELOAD_YES: all maps are rebuilt from scratch and (re)loaded in DM
+ * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only
+ * reloaded in DM if there's a difference. This is useful during startup.
+ */
+int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
+                   int force_reload, enum mpath_cmds cmd)
 {
        int r = 1;
        int k, i;
@@ -742,22 +1007,29 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
        struct path * pp2;
        vector curmp = vecs->mpvec;
        vector pathvec = vecs->pathvec;
+       struct config *conf;
+       int allow_queueing;
 
        /* ignore refwwid if it's empty */
        if (refwwid && !strlen(refwwid))
                refwwid = NULL;
 
-       if (force_reload) {
+       if (force_reload != FORCE_RELOAD_NONE) {
                vector_foreach_slot (pathvec, pp1, k) {
                        pp1->mpp = NULL;
                }
        }
        vector_foreach_slot (pathvec, pp1, k) {
+               int invalid = 0;
                /* skip this path for some reason */
 
                /* 1. if path has no unique id or wwid blacklisted */
-               if (strlen(pp1->wwid) == 0 ||
-                   filter_path(conf, pp1) > 0) {
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
+               if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0)
+                       invalid = 1;
+               pthread_cleanup_pop(1);
+               if (invalid) {
                        orphan_path(pp1, "wwid blacklisted");
                        continue;
                }
@@ -777,7 +1049,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                        continue;
 
                /* If find_multipaths was selected check if the path is valid */
-               if (!refwwid && !should_multipath(pp1, pathvec)) {
+               if (!refwwid && !should_multipath(pp1, pathvec, curmp)) {
                        orphan_path(pp1, "only one path");
                        continue;
                }
@@ -786,8 +1058,10 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                 * at this point, we know we really got a new mp
                 */
                mpp = add_map_with_path(vecs, pp1, 0);
-               if (!mpp)
-                       return 1;
+               if (!mpp) {
+                       orphan_path(pp1, "failed to create multipath device");
+                       continue;
+               }
 
                if (pp1->priority == PRIO_UNDEF)
                        mpp->action = ACT_REJECT;
@@ -823,7 +1097,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                verify_paths(mpp, vecs);
 
                params[0] = '\0';
-               if (setup_map(mpp, params, PARAMS_SIZE)) {
+               if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
                        remove_map(mpp, vecs, 0);
                        continue;
                }
@@ -831,7 +1105,8 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                if (cmd == CMD_DRY_RUN)
                        mpp->action = ACT_DRY_RUN;
                if (mpp->action == ACT_UNDEF)
-                       select_action(mpp, curmp, force_reload);
+                       select_action(mpp, curmp,
+                                     force_reload == FORCE_RELOAD_YES ? 1 : 0);
 
                r = domap(mpp, params, is_daemon);
 
@@ -839,7 +1114,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                        condlog(3, "%s: domap (%u) failure "
                                   "for create/reload map",
                                mpp->alias, r);
-                       if (r == DOMAP_FAIL) {
+                       if (r == DOMAP_FAIL || is_daemon) {
                                condlog(2, "%s: %s map",
                                        mpp->alias, (mpp->action == ACT_CREATE)?
                                        "ignoring" : "removing");
@@ -851,7 +1126,22 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                if (r == DOMAP_DRY)
                        continue;
 
-               if (!is_daemon && !conf->allow_queueing && !check_daemon()) {
+               if (r == DOMAP_EXIST && mpp->action == ACT_NOTHING &&
+                   force_reload == FORCE_RELOAD_WEAK)
+                       /*
+                        * First time we're called, and no changes applied.
+                        * domap() was a noop. But we can't be sure that
+                        * udev has already finished setting up this device
+                        * (udev in initrd may have been shut down while
+                        * processing this device or its children).
+                        * Trigger a change event, just in case.
+                        */
+                       trigger_udev_change(find_mp_by_wwid(curmp, mpp->wwid));
+
+               conf = get_multipath_config();
+               allow_queueing = conf->allow_queueing;
+               put_multipath_config(conf);
+               if (!is_daemon && !allow_queueing && !check_daemon()) {
                        if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
                            mpp->no_path_retry != NO_PATH_RETRY_FAIL)
                                condlog(3, "%s: multipathd not running, unset "
@@ -860,24 +1150,15 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                                remove_feature(&mpp->features,
                                               "queue_if_no_path");
                }
-               else if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
-                       if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) {
-                               condlog(3, "%s: unset queue_if_no_path feature",
-                                       mpp->alias);
-                               if (!dm_queue_if_no_path(mpp->alias, 0))
-                                       remove_feature(&mpp->features,
-                                                      "queue_if_no_path");
-                       } else {
-                               condlog(3, "%s: set queue_if_no_path feature",
-                                       mpp->alias);
-                               if (!dm_queue_if_no_path(mpp->alias, 1))
-                                       add_feature(&mpp->features,
-                                                   "queue_if_no_path");
-                       }
-               }
 
-               if (!is_daemon && mpp->action != ACT_NOTHING)
-                       print_multipath_topology(mpp, conf->verbosity);
+               if (!is_daemon && mpp->action != ACT_NOTHING) {
+                       int verbosity;
+
+                       conf = get_multipath_config();
+                       verbosity = conf->verbosity;
+                       put_multipath_config(conf);
+                       print_multipath_topology(mpp, verbosity);
+               }
 
                if (newmp) {
                        if (mpp->action != ACT_REJECT) {
@@ -896,16 +1177,14 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
        if (newmp) {
                vector_foreach_slot (newmp, mpp, i) {
                        char alias[WWID_SIZE];
-                       int j;
 
                        if (!deadmap(mpp))
                                continue;
 
                        strncpy(alias, mpp->alias, WWID_SIZE - 1);
 
-                       if ((j = find_slot(newmp, (void *)mpp)) != -1)
-                               vector_del_slot(newmp, j);
-
+                       vector_del_slot(newmp, i);
+                       i--;
                        remove_map(mpp, vecs, 0);
 
                        if (dm_flush_map(alias))
@@ -918,21 +1197,57 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
        return 0;
 }
 
+struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type)
+{
+       struct udev_device *ud = NULL;
+       const char *base;
+
+       if (dev == NULL || *dev == '\0')
+               return NULL;
+
+       switch (dev_type) {
+       case DEV_DEVNODE:
+       case DEV_DEVMAP:
+               /* This should be GNU basename, compiler will warn if not */
+               base = basename(dev);
+               if (*base == '\0')
+                       break;
+               ud = udev_device_new_from_subsystem_sysname(udev, "block",
+                                                           base);
+               break;
+       case DEV_DEVT:
+               ud = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+               break;
+       case DEV_UEVENT:
+               ud = udev_device_new_from_environment(udev);
+               break;
+       default:
+               condlog(0, "Internal error: get_udev_device called with invalid type %d\n",
+                       dev_type);
+               break;
+       }
+       if (ud == NULL)
+               condlog(2, "get_udev_device: failed to look up %s with type %d",
+                       dev, dev_type);
+       return ud;
+}
+
 /*
  * returns:
  * 0 - success
  * 1 - failure
  * 2 - blacklist
  */
-extern int
-get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
-            vector pathvec, char **wwid)
+int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
+               vector pathvec, char **wwid)
 {
        int ret = 1;
        struct path * pp;
        char buff[FILE_NAME_SIZE];
        char * refwwid = NULL, tmpwwid[WWID_SIZE];
        int flags = DI_SYSFS | DI_WWID;
+       struct config *conf;
+       int invalid = 0;
 
        if (!wwid)
                return 1;
@@ -953,14 +1268,17 @@ get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
 
                pp = find_path_by_dev(pathvec, buff);
                if (!pp) {
-                       struct udev_device *udevice = udev_device_new_from_subsystem_sysname(udev, "block", buff);
+                       struct udev_device *udevice =
+                               get_udev_device(buff, dev_type);
 
-                       if (!udevice) {
-                               condlog(2, "%s: can't get udev device", buff);
+                       if (!udevice)
                                return 1;
-                       }
-                       ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+
+                       conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       ret = store_pathinfo(pathvec, conf, udevice,
                                             flags, &pp);
+                       pthread_cleanup_pop(1);
                        udev_device_unref(udevice);
                        if (!pp) {
                                if (ret == 1)
@@ -969,8 +1287,13 @@ get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
                                return ret;
                        }
                }
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
                    filter_property(conf, pp->udev) > 0)
+                       invalid = 1;
+               pthread_cleanup_pop(1);
+               if (invalid)
                        return 2;
 
                refwwid = pp->wwid;
@@ -985,14 +1308,17 @@ get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
                }
                pp = find_path_by_dev(pathvec, buff);
                if (!pp) {
-                       struct udev_device *udevice = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+                       struct udev_device *udevice =
+                               get_udev_device(dev, dev_type);
 
-                       if (!udevice) {
-                               condlog(2, "%s: can't get udev device", dev);
+                       if (!udevice)
                                return 1;
-                       }
-                       ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+
+                       conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       ret = store_pathinfo(pathvec, conf, udevice,
                                             flags, &pp);
+                       pthread_cleanup_pop(1);
                        udev_device_unref(udevice);
                        if (!pp) {
                                if (ret == 1)
@@ -1001,40 +1327,51 @@ get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
                                return ret;
                        }
                }
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
                    filter_property(conf, pp->udev) > 0)
+                       invalid = 1;
+               pthread_cleanup_pop(1);
+               if (invalid)
                        return 2;
-
                refwwid = pp->wwid;
                goto out;
        }
 
        if (dev_type == DEV_UEVENT) {
-               struct udev_device *udevice = udev_device_new_from_environment(udev);
+               struct udev_device *udevice = get_udev_device(dev, dev_type);
 
-               if (!udevice) {
-                       condlog(2, "%s: can't get udev device", dev);
+               if (!udevice)
                        return 1;
-               }
-               ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
+               ret = store_pathinfo(pathvec, conf, udevice,
                                     flags, &pp);
+               pthread_cleanup_pop(1);
                udev_device_unref(udevice);
                if (!pp) {
                        if (ret == 1)
-                               condlog(0, "%s: can't store path info",
-                                       dev);
+                               condlog(0, "%s: can't store path info", dev);
                        return ret;
                }
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
                    filter_property(conf, pp->udev) > 0)
+                       invalid = 1;
+               pthread_cleanup_pop(1);
+               if (invalid)
                        return 2;
-
                refwwid = pp->wwid;
                goto out;
        }
 
        if (dev_type == DEV_DEVMAP) {
 
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
                        refwwid = tmpwwid;
                        goto check;
@@ -1061,11 +1398,13 @@ get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
                        refwwid = dev;
 
 check:
-               if (refwwid && strlen(refwwid)) {
-                       if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
-                                       refwwid, NULL) > 0)
+               if (refwwid && strlen(refwwid) &&
+                   filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid,
+                               NULL) > 0)
+                       invalid = 1;
+               pthread_cleanup_pop(1);
+               if (invalid)
                        return 2;
-               }
        }
 out:
        if (refwwid && strlen(refwwid)) {
@@ -1076,7 +1415,8 @@ out:
        return 1;
 }
 
-extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon)
+int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
+              int is_daemon)
 {
        char params[PARAMS_SIZE] = {0};
        struct path *pp;
@@ -1085,7 +1425,10 @@ extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
        update_mpp_paths(mpp, vecs->pathvec);
        if (refresh) {
                vector_foreach_slot (mpp->paths, pp, i) {
-                       r = pathinfo(pp, conf->hwtable, DI_PRIO);
+                       struct config *conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       r = pathinfo(pp, conf, DI_PRIO);
+                       pthread_cleanup_pop(1);
                        if (r) {
                                condlog(2, "%s: failed to refresh pathinfo",
                                        mpp->alias);
@@ -1093,7 +1436,7 @@ extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
                        }
                }
        }
-       if (setup_map(mpp, params, PARAMS_SIZE)) {
+       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
                condlog(0, "%s: failed to setup map", mpp->alias);
                return 1;
        }
@@ -1105,12 +1448,6 @@ extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
                        "for reload map", mpp->alias, r);
                return 1;
        }
-       if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
-               if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
-                       dm_queue_if_no_path(mpp->alias, 0);
-               else
-                       dm_queue_if_no_path(mpp->alias, 1);
-       }
 
        return 0;
 }