multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / propsel.c
index 429e039..627d366 100644 (file)
 #include "discovery.h"
 #include "dict.h"
 #include "util.h"
+#include "sysfs.h"
 #include "prioritizers/alua_rtpg.h"
+#include "prkey.h"
+#include "propsel.h"
 #include <inttypes.h>
+#include <libudev.h>
 
 pgpolicyfn *pgpolicies[] = {
        NULL,
@@ -39,31 +43,46 @@ do {                                                                        \
                goto out;                                               \
        }                                                               \
 } while(0)
+
+static const char default_origin[] = "(setting: multipath internal)";
+static const char hwe_origin[] =
+       "(setting: storage device configuration)";
+static const char multipaths_origin[] =
+       "(setting: multipath.conf multipaths section)";
+static const char conf_origin[] =
+       "(setting: multipath.conf defaults/devices section)";
+static const char overrides_origin[] =
+       "(setting: multipath.conf overrides section)";
+static const char cmdline_origin[] =
+       "(setting: multipath command line [-p] flag)";
+static const char autodetect_origin[] =
+       "(setting: storage device autodetected)";
+
 #define do_default(dest, value)                                                \
 do {                                                                   \
        dest = value;                                                   \
-       origin = "(setting: multipath internal)";                       \
+       origin = default_origin;                                        \
 } while(0)
 
 #define mp_set_mpe(var)                                                        \
-do_set(var, mp->mpe, mp->var, "(setting: multipath.conf multipaths section)")
+do_set(var, mp->mpe, mp->var, multipaths_origin)
 #define mp_set_hwe(var)                                                        \
-do_set(var, mp->hwe, mp->var, "(setting: array configuration)")
+do_set(var, mp->hwe, mp->var, hwe_origin)
 #define mp_set_ovr(var)                                                        \
-do_set(var, conf->overrides, mp->var, "(setting: multipath.conf overrides section)")
+do_set(var, conf->overrides, mp->var, overrides_origin)
 #define mp_set_conf(var)                                               \
-do_set(var, conf, mp->var, "(setting: multipath.conf defaults/devices section)")
+do_set(var, conf, mp->var, conf_origin)
 #define mp_set_default(var, value)                                     \
 do_default(mp->var, value)
 
 #define pp_set_mpe(var)                                                        \
-do_set(var, mpe, pp->var, "(setting: multipath.conf multipaths section)")
+do_set(var, mpe, pp->var, multipaths_origin)
 #define pp_set_hwe(var)                                                        \
-do_set(var, pp->hwe, pp->var, "(setting: array configuration)")
+do_set(var, pp->hwe, pp->var, hwe_origin)
 #define pp_set_conf(var)                                               \
-do_set(var, conf, pp->var, "(setting: multipath.conf defaults/devices section)")
+do_set(var, conf, pp->var, conf_origin)
 #define pp_set_ovr(var)                                                        \
-do_set(var, conf->overrides, pp->var, "(setting: multipath.conf overrides section)")
+do_set(var, conf->overrides, pp->var, overrides_origin)
 #define pp_set_default(var, value)                                     \
 do_default(pp->var, value)
 
@@ -82,9 +101,19 @@ do_attr_set(var, mp->mpe, shift, "(setting: multipath.conf multipaths section)")
 #define set_attr_conf(var, shift)                                      \
 do_attr_set(var, conf, shift, "(setting: multipath.conf defaults/devices section)")
 
+#define do_prkey_set(src, msg)                                         \
+do {                                                                   \
+       if (src && src->prkey_source != PRKEY_SOURCE_NONE) {            \
+               mp->prkey_source = src->prkey_source;                   \
+               mp->reservation_key = src->reservation_key;             \
+               origin = msg;                                           \
+               goto out;                                               \
+       }                                                               \
+} while (0)
+
 int select_mode(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
        set_attr_mpe(mode, ATTR_MODE);
        set_attr_conf(mode, ATTR_MODE);
@@ -97,7 +126,7 @@ out:
 
 int select_uid(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
        set_attr_mpe(uid, ATTR_UID);
        set_attr_conf(uid, ATTR_UID);
@@ -110,7 +139,7 @@ out:
 
 int select_gid(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
        set_attr_mpe(gid, ATTR_GID);
        set_attr_conf(gid, ATTR_GID);
@@ -128,7 +157,8 @@ out:
  */
 int select_rr_weight(struct config *conf, struct multipath * mp)
 {
-       char *origin, buff[13];
+       const char *origin;
+       char buff[13];
 
        mp_set_mpe(rr_weight);
        mp_set_ovr(rr_weight);
@@ -136,14 +166,15 @@ int select_rr_weight(struct config *conf, struct multipath * mp)
        mp_set_conf(rr_weight);
        mp_set_default(rr_weight, DEFAULT_RR_WEIGHT);
 out:
-       print_rr_weight(buff, 13, &mp->rr_weight);
+       print_rr_weight(buff, 13, mp->rr_weight);
        condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin);
        return 0;
 }
 
 int select_pgfailback(struct config *conf, struct multipath * mp)
 {
-       char *origin, buff[13];
+       const char *origin;
+       char buff[13];
 
        mp_set_mpe(pgfailback);
        mp_set_ovr(pgfailback);
@@ -151,18 +182,19 @@ int select_pgfailback(struct config *conf, struct multipath * mp)
        mp_set_conf(pgfailback);
        mp_set_default(pgfailback, DEFAULT_FAILBACK);
 out:
-       print_pgfailback(buff, 13, &mp->pgfailback);
+       print_pgfailback(buff, 13, mp->pgfailback);
        condlog(3, "%s: failback = %s %s", mp->alias, buff, origin);
        return 0;
 }
 
 int select_pgpolicy(struct config *conf, struct multipath * mp)
 {
-       char *origin, buff[POLICY_NAME_SIZE];
+       const char *origin;
+       char buff[POLICY_NAME_SIZE];
 
        if (conf->pgpolicy_flag > 0) {
                mp->pgpolicy = conf->pgpolicy_flag;
-               origin = "(setting: multipath command line [-p] flag)";
+               origin = cmdline_origin;
                goto out;
        }
        mp_set_mpe(pgpolicy);
@@ -179,7 +211,7 @@ out:
 
 int select_selector(struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_mpe(selector);
        mp_set_ovr(selector);
@@ -196,7 +228,7 @@ out:
 static void
 select_alias_prefix (struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_ovr(alias_prefix);
        mp_set_hwe(alias_prefix);
@@ -211,17 +243,17 @@ static int
 want_user_friendly_names(struct config *conf, struct multipath * mp)
 {
 
-       char *origin;
+       const char *origin;
        int user_friendly_names;
 
        do_set(user_friendly_names, mp->mpe, user_friendly_names,
-              "(setting: multipath.conf multipaths section)");
+              multipaths_origin);
        do_set(user_friendly_names, conf->overrides, user_friendly_names,
-              "(setting: multipath.conf overrides section)");
+              overrides_origin);
        do_set(user_friendly_names, mp->hwe, user_friendly_names,
-              "(setting: array configuration)");
+              hwe_origin);
        do_set(user_friendly_names, conf, user_friendly_names,
-              "(setting: multipath.conf defaults/devices section)");
+              conf_origin);
        do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES);
 out:
        condlog(3, "%s: user_friendly_names = %s %s", mp->wwid,
@@ -232,11 +264,11 @@ out:
 
 int select_alias(struct config *conf, struct multipath * mp)
 {
-       char *origin = NULL;
+       const char *origin = NULL;
 
        if (mp->mpe && mp->mpe->alias) {
                mp->alias = STRDUP(mp->mpe->alias);
-               origin = "(setting: multipath.conf multipaths section)";
+               origin = multipaths_origin;
                goto out;
        }
 
@@ -296,14 +328,14 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa
                if (*no_path_retry == NO_PATH_RETRY_UNDEF) {
                        *no_path_retry = NO_PATH_RETRY_QUEUE;
                        print_no_path_retry(buff, sizeof(buff),
-                                           no_path_retry);
+                                           *no_path_retry);
                        condlog(3, "%s: no_path_retry = %s (inherited setting from feature '%s')",
                                id, buff, q_i_n_p);
                };
                /* Warn only if features string is overridden */
                if (*no_path_retry != NO_PATH_RETRY_QUEUE) {
                        print_no_path_retry(buff, sizeof(buff),
-                                           no_path_retry);
+                                           *no_path_retry);
                        condlog(2, "%s: ignoring feature '%s' because no_path_retry is set to '%s'",
                                id, q_i_n_p, buff);
                }
@@ -325,7 +357,7 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa
 
 int select_features(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_mpe(features);
        mp_set_ovr(features);
@@ -342,56 +374,128 @@ out:
        return 0;
 }
 
+static int get_dh_state(struct path *pp, char *value, size_t value_len)
+{
+       struct udev_device *ud;
+
+       if (pp->udev == NULL)
+               return -1;
+
+       ud = udev_device_get_parent_with_subsystem_devtype(
+               pp->udev, "scsi", "scsi_device");
+       if (ud == NULL)
+               return -1;
+
+       return sysfs_attr_get_value(ud, "dh_state", value, value_len);
+}
+
 int select_hwhandler(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
+       struct path *pp;
+       /* dh_state is no longer than "detached" */
+       char handler[12];
+       static char alua_name[] = "1 alua";
+       static const char tpgs_origin[]= "(setting: autodetected from TPGS)";
+       char *dh_state;
+       int i;
+       bool all_tpgs = true;
+
+       dh_state = &handler[2];
+       if (mp->retain_hwhandler != RETAIN_HWHANDLER_OFF) {
+               vector_foreach_slot(mp->paths, pp, i) {
+                       all_tpgs = all_tpgs && (pp->tpgs > 0);
+                       if (get_dh_state(pp, dh_state, sizeof(handler) - 2) > 0
+                           && strcmp(dh_state, "detached")) {
+                               memcpy(handler, "1 ", 2);
+                               mp->hwhandler = handler;
+                               origin = "(setting: retained by kernel driver)";
+                               goto out;
+                       }
+               }
+       }
 
        mp_set_hwe(hwhandler);
        mp_set_conf(hwhandler);
        mp_set_default(hwhandler, DEFAULT_HWHANDLER);
 out:
+       if (all_tpgs && !strcmp(mp->hwhandler, DEFAULT_HWHANDLER) &&
+               origin == default_origin) {
+               mp->hwhandler = alua_name;
+               origin = tpgs_origin;
+       } else if (!all_tpgs && !strcmp(mp->hwhandler, alua_name)) {
+               mp->hwhandler = DEFAULT_HWHANDLER;
+               origin = tpgs_origin;
+       }
        mp->hwhandler = STRDUP(mp->hwhandler);
        condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler,
                origin);
        return 0;
 }
 
+/*
+ * Current RDAC (NetApp E-Series) firmware relies
+ * on periodic REPORT TARGET PORT GROUPS for
+ * internal load balancing.
+ * Using the sysfs priority checker defeats this purpose.
+ *
+ * Moreover, NetApp would also prefer the RDAC checker over ALUA.
+ * (https://www.redhat.com/archives/dm-devel/2017-September/msg00326.html)
+ */
+static int
+check_rdac(struct path * pp)
+{
+       int len;
+       char buff[44];
+
+       len = get_vpd_sgio(pp->fd, 0xC9, buff, 44);
+       if (len <= 0)
+               return 0;
+       return !(memcmp(buff + 4, "vac1", 4));
+}
+
 int select_checker(struct config *conf, struct path *pp)
 {
-       char *origin, *checker_name;
+       const char *origin;
+       char *checker_name;
        struct checker * c = &pp->checker;
 
-       if (pp->detect_checker == DETECT_CHECKER_ON && pp->tpgs > 0) {
-               checker_name = TUR;
-               origin = "(setting: array autodetected)";
-               goto out;
-       }
-       do_set(checker_name, conf->overrides, checker_name, "(setting: multipath.conf overrides section)");
-       do_set(checker_name, pp->hwe, checker_name, "(setting: array configuration)");
-       do_set(checker_name, conf, checker_name, "(setting: multipath.conf defaults/devices section)");
+       if (pp->detect_checker == DETECT_CHECKER_ON) {
+               origin = autodetect_origin;
+               if (check_rdac(pp)) {
+                       checker_name = RDAC;
+                       goto out;
+               } else if (pp->tpgs > 0) {
+                       checker_name = TUR;
+                       goto out;
+               }
+       }
+       do_set(checker_name, conf->overrides, checker_name, overrides_origin);
+       do_set(checker_name, pp->hwe, checker_name, hwe_origin);
+       do_set(checker_name, conf, checker_name, conf_origin);
        do_default(checker_name, DEFAULT_CHECKER);
 out:
        checker_get(conf->multipath_dir, c, checker_name);
        condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin);
        if (conf->checker_timeout) {
                c->timeout = conf->checker_timeout;
-               condlog(3, "%s: checker timeout = %u s (setting: multipath.conf defaults/devices section)",
-                               pp->dev, c->timeout);
+               condlog(3, "%s: checker timeout = %u s %s",
+                       pp->dev, c->timeout, conf_origin);
        }
        else if (sysfs_get_timeout(pp, &c->timeout) > 0)
                condlog(3, "%s: checker timeout = %u s (setting: kernel sysfs)",
-                               pp->dev, c->timeout);
+                       pp->dev, c->timeout);
        else {
                c->timeout = DEF_TIMEOUT;
-               condlog(3, "%s: checker timeout = %u s (setting: multipath internal)",
-                               pp->dev, c->timeout);
+               condlog(3, "%s: checker timeout = %u s %s",
+                       pp->dev, c->timeout, default_origin);
        }
        return 0;
 }
 
 int select_getuid(struct config *conf, struct path *pp)
 {
-       char *origin;
+       const char *origin;
 
        pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev);
        if (pp->uid_attribute) {
@@ -416,24 +520,6 @@ out:
        return 0;
 }
 
-/*
- * Current RDAC (NetApp E-Series) firmware relies
- * on periodic REPORT TARGET PORT GROUPS for
- * internal load balancing.
- * Using the sysfs priority checker defeats this purpose.
- */
-static int
-check_rdac(struct path * pp)
-{
-       int len;
-       char buff[44];
-
-       len = get_vpd_sgio(pp->fd, 0xC9, buff, 44);
-       if (len <= 0)
-               return 0;
-       return !(memcmp(buff + 4, "vac1", 4));
-}
-
 void
 detect_prio(struct config *conf, struct path * pp)
 {
@@ -443,7 +529,7 @@ detect_prio(struct config *conf, struct path * pp)
 
        if (pp->tpgs <= 0)
                return;
-       if (pp->tpgs == 2 && !check_rdac(pp)) {
+       if (pp->tpgs == 2 || !check_rdac(pp)) {
                if (sysfs_get_asymmetric_access_state(pp, buff, 512) >= 0)
                        default_prio = PRIO_SYSFS;
        }
@@ -461,24 +547,24 @@ do {                                                                      \
 
 int select_prio(struct config *conf, struct path *pp)
 {
-       char *origin;
+       const char *origin;
        struct mpentry * mpe;
        struct prio * p = &pp->prio;
 
        if (pp->detect_prio == DETECT_PRIO_ON) {
                detect_prio(conf, pp);
                if (prio_selected(p)) {
-                       origin = "(setting: array autodetected)";
+                       origin = autodetect_origin;
                        goto out;
                }
        }
        mpe = find_mpe(conf->mptable, pp->wwid);
-       set_prio(conf->multipath_dir, mpe, "(setting: multipath.conf multipaths section)");
-       set_prio(conf->multipath_dir, conf->overrides, "(setting: multipath.conf overrides section)");
-       set_prio(conf->multipath_dir, pp->hwe, "(setting: array configuration)");
-       set_prio(conf->multipath_dir, conf, "(setting: multipath.conf defaults/devices section)");
+       set_prio(conf->multipath_dir, mpe, multipaths_origin);
+       set_prio(conf->multipath_dir, conf->overrides, overrides_origin);
+       set_prio(conf->multipath_dir, pp->hwe, hwe_origin);
+       set_prio(conf->multipath_dir, conf, conf_origin);
        prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
-       origin = "(setting: multipath internal)";
+       origin = default_origin;
 out:
        /*
         * fetch tpgs mode for alua, if its not already obtained
@@ -498,11 +584,11 @@ out:
 
 int select_no_path_retry(struct config *conf, struct multipath *mp)
 {
-       char *origin = NULL;
+       const char *origin = NULL;
        char buff[12];
 
-       if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) {
-               condlog(0, "%s: flush_on_last_del in progress", mp->alias);
+       if (mp->disable_queueing) {
+               condlog(0, "%s: queueing disabled", mp->alias);
                mp->no_path_retry = NO_PATH_RETRY_FAIL;
                return 0;
        }
@@ -511,25 +597,25 @@ int select_no_path_retry(struct config *conf, struct multipath *mp)
        mp_set_hwe(no_path_retry);
        mp_set_conf(no_path_retry);
 out:
-       print_no_path_retry(buff, 12, &mp->no_path_retry);
+       print_no_path_retry(buff, 12, mp->no_path_retry);
        if (origin)
                condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff,
                        origin);
        else
-               condlog(3, "%s: no_path_retry = undef (setting: multipath internal)",
-                       mp->alias);
+               condlog(3, "%s: no_path_retry = undef %s",
+                       mp->alias, default_origin);
        return 0;
 }
 
 int
 select_minio_rq (struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
-       do_set(minio_rq, mp->mpe, mp->minio, "(setting: multipath.conf multipaths section)");
-       do_set(minio_rq, conf->overrides, mp->minio, "(setting: multipath.conf overrides section)");
-       do_set(minio_rq, mp->hwe, mp->minio, "(setting: array configuration)");
-       do_set(minio_rq, conf, mp->minio, "(setting: multipath.conf defaults/devices section)");
+       do_set(minio_rq, mp->mpe, mp->minio, multipaths_origin);
+       do_set(minio_rq, conf->overrides, mp->minio, overrides_origin);
+       do_set(minio_rq, mp->hwe, mp->minio, hwe_origin);
+       do_set(minio_rq, conf, mp->minio, conf_origin);
        do_default(mp->minio, DEFAULT_MINIO_RQ);
 out:
        condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin);
@@ -539,7 +625,7 @@ out:
 int
 select_minio_bio (struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_mpe(minio);
        mp_set_ovr(minio);
@@ -563,21 +649,23 @@ int select_minio(struct config *conf, struct multipath *mp)
 
 int select_fast_io_fail(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
        mp_set_ovr(fast_io_fail);
        mp_set_hwe(fast_io_fail);
        mp_set_conf(fast_io_fail);
        mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
 out:
-       print_fast_io_fail(buff, 12, &mp->fast_io_fail);
+       print_fast_io_fail(buff, 12, mp->fast_io_fail);
        condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin);
        return 0;
 }
 
 int select_dev_loss(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
        mp_set_ovr(dev_loss);
        mp_set_hwe(dev_loss);
@@ -585,17 +673,15 @@ int select_dev_loss(struct config *conf, struct multipath *mp)
        mp->dev_loss = 0;
        return 0;
 out:
-       print_dev_loss(buff, 12, &mp->dev_loss);
+       print_dev_loss(buff, 12, mp->dev_loss);
        condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin);
        return 0;
 }
 
 int select_flush_on_last_del(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
-       if (mp->flush_on_last_del == FLUSH_IN_PROGRESS)
-               return 0;
        mp_set_mpe(flush_on_last_del);
        mp_set_ovr(flush_on_last_del);
        mp_set_hwe(flush_on_last_del);
@@ -609,21 +695,34 @@ out:
 
 int select_reservation_key(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[PRKEY_SIZE];
+       const char *origin;
+       char buff[PRKEY_SIZE];
+       char *from_file = "";
+       uint64_t prkey = 0;
 
-       mp_set_mpe(reservation_key._v);
-       mp_set_conf(reservation_key._v);
+       do_prkey_set(mp->mpe, multipaths_origin);
+       do_prkey_set(conf, conf_origin);
        put_be64(mp->reservation_key, 0);
+       mp->prkey_source = PRKEY_SOURCE_NONE;
        return 0;
 out:
-       print_reservation_key(buff, PRKEY_SIZE, &mp->reservation_key);
-       condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin);
+       if (mp->prkey_source == PRKEY_SOURCE_FILE) {
+               from_file = " (from prkeys file)";
+               if (get_prkey(conf, mp, &prkey) != 0)
+                       put_be64(mp->reservation_key, 0);
+               else
+                       put_be64(mp->reservation_key, prkey);
+       }
+       print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key,
+                             mp->prkey_source);
+       condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin,
+               from_file);
        return 0;
 }
 
 int select_retain_hwhandler(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
        unsigned int minv_dm_retain[3] = {1, 5, 0};
 
        if (!VERSION_GE(conf->version, minv_dm_retain)) {
@@ -649,7 +748,7 @@ out:
 
 int select_detect_prio(struct config *conf, struct path *pp)
 {
-       char *origin;
+       const char *origin;
 
        pp_set_ovr(detect_prio);
        pp_set_hwe(detect_prio);
@@ -663,7 +762,7 @@ out:
 
 int select_detect_checker(struct config *conf, struct path *pp)
 {
-       char *origin;
+       const char *origin;
 
        pp_set_ovr(detect_checker);
        pp_set_hwe(detect_checker);
@@ -678,7 +777,7 @@ out:
 
 int select_deferred_remove(struct config *conf, struct multipath *mp)
 {
-       char *origin;
+       const char *origin;
 
 #ifndef LIBDM_API_DEFERRED
        mp->deferred_remove = DEFERRED_REMOVE_OFF;
@@ -703,7 +802,8 @@ out:
 
 int select_delay_watch_checks(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
        mp_set_mpe(delay_watch_checks);
        mp_set_ovr(delay_watch_checks);
@@ -711,14 +811,15 @@ int select_delay_watch_checks(struct config *conf, struct multipath *mp)
        mp_set_conf(delay_watch_checks);
        mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS);
 out:
-       print_off_int_undef(buff, 12, &mp->delay_watch_checks);
+       print_off_int_undef(buff, 12, mp->delay_watch_checks);
        condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin);
        return 0;
 }
 
 int select_delay_wait_checks(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
        mp_set_mpe(delay_wait_checks);
        mp_set_ovr(delay_wait_checks);
@@ -726,59 +827,83 @@ int select_delay_wait_checks(struct config *conf, struct multipath *mp)
        mp_set_conf(delay_wait_checks);
        mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS);
 out:
-       print_off_int_undef(buff, 12, &mp->delay_wait_checks);
+       print_off_int_undef(buff, 12, mp->delay_wait_checks);
        condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin);
        return 0;
 
 }
-int select_san_path_err_threshold(struct config *conf, struct multipath *mp)
+
+int select_marginal_path_err_sample_time(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
-       mp_set_mpe(san_path_err_threshold);
-       mp_set_ovr(san_path_err_threshold);
-       mp_set_hwe(san_path_err_threshold);
-       mp_set_conf(san_path_err_threshold);
-       mp_set_default(san_path_err_threshold, DEFAULT_ERR_CHECKS);
+       mp_set_mpe(marginal_path_err_sample_time);
+       mp_set_ovr(marginal_path_err_sample_time);
+       mp_set_hwe(marginal_path_err_sample_time);
+       mp_set_conf(marginal_path_err_sample_time);
+       mp_set_default(marginal_path_err_sample_time, DEFAULT_ERR_CHECKS);
 out:
-       print_off_int_undef(buff, 12, &mp->san_path_err_threshold);
-       condlog(3, "%s: san_path_err_threshold = %s %s", mp->alias, buff, origin);
+       print_off_int_undef(buff, 12, mp->marginal_path_err_sample_time);
+       condlog(3, "%s: marginal_path_err_sample_time = %s %s", mp->alias, buff,
+                       origin);
        return 0;
 }
 
-int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp)
+int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
-       mp_set_mpe(san_path_err_forget_rate);
-       mp_set_ovr(san_path_err_forget_rate);
-       mp_set_hwe(san_path_err_forget_rate);
-       mp_set_conf(san_path_err_forget_rate);
-       mp_set_default(san_path_err_forget_rate, DEFAULT_ERR_CHECKS);
+       mp_set_mpe(marginal_path_err_rate_threshold);
+       mp_set_ovr(marginal_path_err_rate_threshold);
+       mp_set_hwe(marginal_path_err_rate_threshold);
+       mp_set_conf(marginal_path_err_rate_threshold);
+       mp_set_default(marginal_path_err_rate_threshold, DEFAULT_ERR_CHECKS);
 out:
-       print_off_int_undef(buff, 12, &mp->san_path_err_forget_rate);
-       condlog(3, "%s: san_path_err_forget_rate = %s %s", mp->alias, buff, origin);
+       print_off_int_undef(buff, 12, mp->marginal_path_err_rate_threshold);
+       condlog(3, "%s: marginal_path_err_rate_threshold = %s %s", mp->alias, buff,
+                       origin);
        return 0;
-
 }
-int select_san_path_err_recovery_time(struct config *conf, struct multipath *mp)
+
+int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp)
 {
-       char *origin, buff[12];
+       const char *origin;
+       char buff[12];
 
-       mp_set_mpe(san_path_err_recovery_time);
-       mp_set_ovr(san_path_err_recovery_time);
-       mp_set_hwe(san_path_err_recovery_time);
-       mp_set_conf(san_path_err_recovery_time);
-       mp_set_default(san_path_err_recovery_time, DEFAULT_ERR_CHECKS);
+       mp_set_mpe(marginal_path_err_recheck_gap_time);
+       mp_set_ovr(marginal_path_err_recheck_gap_time);
+       mp_set_hwe(marginal_path_err_recheck_gap_time);
+       mp_set_conf(marginal_path_err_recheck_gap_time);
+       mp_set_default(marginal_path_err_recheck_gap_time, DEFAULT_ERR_CHECKS);
 out:
-       print_off_int_undef(buff, 12, &mp->san_path_err_recovery_time);
-       condlog(3, "%s: san_path_err_recovery_time = %s %s", mp->alias, buff, origin);
+       print_off_int_undef(buff, 12, mp->marginal_path_err_recheck_gap_time);
+       condlog(3, "%s: marginal_path_err_recheck_gap_time = %s %s", mp->alias, buff,
+                       origin);
        return 0;
+}
 
+int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp)
+{
+       const char *origin;
+       char buff[12];
+
+       mp_set_mpe(marginal_path_double_failed_time);
+       mp_set_ovr(marginal_path_double_failed_time);
+       mp_set_hwe(marginal_path_double_failed_time);
+       mp_set_conf(marginal_path_double_failed_time);
+       mp_set_default(marginal_path_double_failed_time, DEFAULT_ERR_CHECKS);
+out:
+       print_off_int_undef(buff, 12, mp->marginal_path_double_failed_time);
+       condlog(3, "%s: marginal_path_double_failed_time = %s %s", mp->alias, buff,
+                       origin);
+       return 0;
 }
+
 int select_skip_kpartx (struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_mpe(skip_kpartx);
        mp_set_ovr(skip_kpartx);
@@ -794,7 +919,7 @@ out:
 
 int select_max_sectors_kb(struct config *conf, struct multipath * mp)
 {
-       char *origin;
+       const char *origin;
 
        mp_set_mpe(max_sectors_kb);
        mp_set_ovr(max_sectors_kb);
@@ -812,3 +937,44 @@ out:
                origin);
        return 0;
 }
+
+int select_ghost_delay (struct config *conf, struct multipath * mp)
+{
+       const char *origin;
+       char buff[12];
+
+       mp_set_mpe(ghost_delay);
+       mp_set_ovr(ghost_delay);
+       mp_set_hwe(ghost_delay);
+       mp_set_conf(ghost_delay);
+       mp_set_default(ghost_delay, DEFAULT_GHOST_DELAY);
+out:
+       print_off_int_undef(buff, 12, mp->ghost_delay);
+       condlog(3, "%s: ghost_delay = %s %s", mp->alias, buff, origin);
+       return 0;
+}
+
+int select_find_multipaths_timeout(struct config *conf, struct path *pp)
+{
+       const char *origin;
+
+       pp_set_conf(find_multipaths_timeout);
+       pp_set_default(find_multipaths_timeout,
+                      DEFAULT_FIND_MULTIPATHS_TIMEOUT);
+out:
+       /*
+        * If configured value is negative, and this "unknown" hardware
+        * (no hwentry), use very small timeout to avoid delays.
+        */
+       if (pp->find_multipaths_timeout < 0) {
+               pp->find_multipaths_timeout = -pp->find_multipaths_timeout;
+               if (!pp->hwe) {
+                       pp->find_multipaths_timeout =
+                               DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT;
+                       origin = "(default for unknown hardware)";
+               }
+       }
+       condlog(3, "%s: timeout for find_multipaths \"smart\" = %ds %s",
+               pp->dev, pp->find_multipaths_timeout, origin);
+       return 0;
+}