<div dir="ltr">Hi Ben,<div><br></div><div>the patchset is merged until this patch.</div><div>Can you consider rebasing it ?</div><div><br></div><div>Thanks,</div><div>Christophe.</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Nov 8, 2017 at 1:15 AM, Benjamin Marzinski <span dir="ltr"><<a href="mailto:bmarzins@redhat.com" target="_blank">bmarzins@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">If the lower-priority passive paths for a multipath device appear first,<br>
IO can go to them and cause the hardware handler to activate them,<br>
before the higher priority paths appear, causing the devices to<br>
failback. Setting the "ghost_delay" parameter to a value greater than<br>
0 can avoid this ping-ponging by causing udev to not mark the device as<br>
Ready after its initial creation until either an active path appears,<br>
or ghost_delay seconds have passed. Multipathd does this by setting<br>
the MPATH_UDEV_NO_PATHS_FLAG.<br>
<br>
Signed-off-by: Benjamin Marzinski <<a href="mailto:bmarzins@redhat.com">bmarzins@redhat.com</a>><br>
---<br>
 libmultipath/config.c      |  3 +++<br>
 libmultipath/config.h      |  3 +++<br>
 libmultipath/configure.c   | 11 +++++++++++<br>
 libmultipath/defaults.h    |  1 +<br>
 libmultipath/devmapper.c   |  2 +-<br>
 libmultipath/dict.c        | 14 ++++++++++++++<br>
 libmultipath/hwtable.c     |  1 +<br>
 libmultipath/propsel.c     | 15 +++++++++++++++<br>
 libmultipath/propsel.h     |  1 +<br>
 libmultipath/structs.h     |  7 +++++++<br>
 multipath/multipath.conf.5 | 19 +++++++++++++++++++<br>
 multipathd/main.c          | 28 +++++++++++++++++++++++++++-<br>
 12 files changed, 103 insertions(+), 2 deletions(-)<br>
<br>
diff --git a/libmultipath/config.c b/libmultipath/config.c<br>
index ea2359a..9486116 100644<br>
--- a/libmultipath/config.c<br>
+++ b/libmultipath/config.c<br>
@@ -351,6 +351,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)<br>
        merge_num(delay_wait_checks);<br>
        merge_num(skip_kpartx);<br>
        merge_num(max_sectors_kb);<br>
+       merge_num(ghost_delay);<br>
        merge_num(san_path_err_<wbr>threshold);<br>
        merge_num(san_path_err_forget_<wbr>rate);<br>
        merge_num(san_path_err_<wbr>recovery_time);<br>
@@ -422,6 +423,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe)<br>
        hwe->retain_hwhandler = dhwe->retain_hwhandler;<br>
        hwe->detect_prio = dhwe->detect_prio;<br>
        hwe->detect_checker = dhwe->detect_checker;<br>
+       hwe->ghost_delay = dhwe->ghost_delay;<br>
<br>
        if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_<wbr>product)))<br>
                goto out;<br>
@@ -622,6 +624,7 @@ load_config (char * file)<br>
        conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;<br>
        conf->disable_changed_wwids = DEFAULT_DISABLE_CHANGED_WWIDS;<br>
        conf->remove_retries = 0;<br>
+       conf->ghost_delay = DEFAULT_GHOST_DELAY;<br>
<br>
        /*<br>
         * preload default hwtable<br>
diff --git a/libmultipath/config.h b/libmultipath/config.h<br>
index 240730b..67ff983 100644<br>
--- a/libmultipath/config.h<br>
+++ b/libmultipath/config.h<br>
@@ -80,6 +80,7 @@ struct hwentry {<br>
        int san_path_err_recovery_time;<br>
        int skip_kpartx;<br>
        int max_sectors_kb;<br>
+       int ghost_delay;<br>
        char * bl_product;<br>
 };<br>
<br>
@@ -112,6 +113,7 @@ struct mpentry {<br>
        int san_path_err_recovery_time;<br>
        int skip_kpartx;<br>
        int max_sectors_kb;<br>
+       int ghost_delay;<br>
        uid_t uid;<br>
        gid_t gid;<br>
        mode_t mode;<br>
@@ -170,6 +172,7 @@ struct config {<br>
        int disable_changed_wwids;<br>
        int remove_retries;<br>
        int max_sectors_kb;<br>
+       int ghost_delay;<br>
        unsigned int version[3];<br>
<br>
        char * multipath_dir;<br>
diff --git a/libmultipath/configure.c b/libmultipath/configure.c<br>
index 7a3db31..e2f393f 100644<br>
--- a/libmultipath/configure.c<br>
+++ b/libmultipath/configure.c<br>
@@ -300,6 +300,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size)<br>
        select_san_path_err_recovery_<wbr>time(conf, mpp);<br>
        select_skip_kpartx(conf, mpp);<br>
        select_max_sectors_kb(conf, mpp);<br>
+       select_ghost_delay(conf, mpp);<br>
<br>
        sysfs_set_scsi_tmo(mpp, conf->checkint);<br>
        put_multipath_config(conf);<br>
@@ -760,6 +761,9 @@ int domap(struct multipath *mpp, char *params, int is_daemon)<br>
                }<br>
<br>
                sysfs_set_max_sectors_kb(mpp, 0);<br>
+               if (is_daemon && mpp->ghost_delay > 0 && mpp->nr_active &&<br>
+                   pathcount(mpp, PATH_GHOST) == mpp->nr_active)<br>
+                       mpp->ghost_delay_tick = mpp->ghost_delay;<br>
                r = dm_addmap_create(mpp, params);<br>
<br>
                lock_multipath(mpp, 0);<br>
@@ -767,11 +771,15 @@ int domap(struct multipath *mpp, char *params, int is_daemon)<br>
<br>
        case ACT_RELOAD:<br>
                sysfs_set_max_sectors_kb(mpp, 1);<br>
+               if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))<br>
+                       mpp->ghost_delay_tick = 0;<br>
                r = dm_addmap_reload(mpp, params, 0);<br>
                break;<br>
<br>
        case ACT_RESIZE:<br>
                sysfs_set_max_sectors_kb(mpp, 1);<br>
+               if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))<br>
+                       mpp->ghost_delay_tick = 0;<br>
                r = dm_addmap_reload(mpp, params, 1);<br>
                break;<br>
<br>
@@ -789,6 +797,9 @@ int domap(struct multipath *mpp, char *params, int is_daemon)<br>
                put_multipath_config(conf);<br>
                if (r) {<br>
                        sysfs_set_max_sectors_kb(mpp, 1);<br>
+                       if (mpp->ghost_delay_tick > 0 &&<br>
+                           pathcount(mpp, PATH_UP))<br>
+                               mpp->ghost_delay_tick = 0;<br>
                        r = dm_addmap_reload(mpp, params, 0);<br>
                }<br>
                break;<br>
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h<br>
index 740ccf4..c9e3411 100644<br>
--- a/libmultipath/defaults.h<br>
+++ b/libmultipath/defaults.h<br>
@@ -40,6 +40,7 @@<br>
 #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF<br>
 #define DEFAULT_DISABLE_CHANGED_WWIDS 0<br>
 #define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF<br>
+#define DEFAULT_GHOST_DELAY GHOST_DELAY_OFF<br>
<br>
 #define DEFAULT_CHECKINT       5<br>
 #define MAX_CHECKINT(a)                (a << 2)<br>
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c<br>
index fcac6bc..573fc75 100644<br>
--- a/libmultipath/devmapper.c<br>
+++ b/libmultipath/devmapper.c<br>
@@ -378,7 +378,7 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload)<br>
        /* DM_UDEV_DISABLE_LIBRARY_<wbr>FALLBACK is added in dm_addmap */<br>
        return  (mpp->skip_kpartx == SKIP_KPARTX_ON ?<br>
                 MPATH_UDEV_NO_KPARTX_FLAG : 0) |<br>
-               (mpp->nr_active == 0 ?<br>
+               ((mpp->nr_active == 0 || mpp->ghost_delay_tick > 0)?<br>
                 MPATH_UDEV_NO_PATHS_FLAG : 0) |<br>
                (reload && !mpp->force_udev_reload ?<br>
                 MPATH_UDEV_RELOAD_FLAG : 0);<br>
diff --git a/libmultipath/dict.c b/libmultipath/dict.c<br>
index 36cccc9..54652d4 100644<br>
--- a/libmultipath/dict.c<br>
+++ b/libmultipath/dict.c<br>
@@ -1110,6 +1110,16 @@ declare_hw_handler(san_path_<wbr>err_recovery_time, set_off_int_undef)<br>
 declare_hw_snprint(san_path_<wbr>err_recovery_time, print_off_int_undef)<br>
 declare_mp_handler(san_path_<wbr>err_recovery_time, set_off_int_undef)<br>
 declare_mp_snprint(san_path_<wbr>err_recovery_time, print_off_int_undef)<br>
+<br>
+declare_def_handler(ghost_<wbr>delay, set_off_int_undef)<br>
+declare_def_snprint(ghost_<wbr>delay, print_off_int_undef)<br>
+declare_ovr_handler(ghost_<wbr>delay, set_off_int_undef)<br>
+declare_ovr_snprint(ghost_<wbr>delay, print_off_int_undef)<br>
+declare_hw_handler(ghost_<wbr>delay, set_off_int_undef)<br>
+declare_hw_snprint(ghost_<wbr>delay, print_off_int_undef)<br>
+declare_mp_handler(ghost_<wbr>delay, set_off_int_undef)<br>
+declare_mp_snprint(ghost_<wbr>delay, print_off_int_undef)<br>
+<br>
 static int<br>
 def_uxsock_timeout_handler(<wbr>struct config *conf, vector strvec)<br>
 {<br>
@@ -1456,6 +1466,7 @@ init_keywords(vector keywords)<br>
        install_keyword("disable_<wbr>changed_wwids", &def_disable_changed_wwids_<wbr>handler, &snprint_def_disable_changed_<wbr>wwids);<br>
        install_keyword("remove_<wbr>retries", &def_remove_retries_handler, &snprint_def_remove_retries);<br>
        install_keyword("max_sectors_<wbr>kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb);<br>
+       install_keyword("ghost_delay", &def_ghost_delay_handler, &snprint_def_ghost_delay);<br>
        __deprecated install_keyword("default_<wbr>selector", &def_selector_handler, NULL);<br>
        __deprecated install_keyword("default_path_<wbr>grouping_policy", &def_pgpolicy_handler, NULL);<br>
        __deprecated install_keyword("default_uid_<wbr>attribute", &def_uid_attribute_handler, NULL);<br>
@@ -1535,6 +1546,7 @@ init_keywords(vector keywords)<br>
        install_keyword("san_path_err_<wbr>recovery_time", &hw_san_path_err_recovery_<wbr>time_handler, &snprint_hw_san_path_err_<wbr>recovery_time);<br>
        install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx);<br>
        install_keyword("max_sectors_<wbr>kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb);<br>
+       install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);<br>
        install_sublevel_end();<br>
<br>
        install_keyword_root("<wbr>overrides", &overrides_handler);<br>
@@ -1569,6 +1581,7 @@ init_keywords(vector keywords)<br>
<br>
        install_keyword("skip_kpartx", &ovr_skip_kpartx_handler, &snprint_ovr_skip_kpartx);<br>
        install_keyword("max_sectors_<wbr>kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb);<br>
+       install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay);<br>
<br>
        install_keyword_root("<wbr>multipaths", &multipaths_handler);<br>
        install_keyword_multi("<wbr>multipath", &multipath_handler, NULL);<br>
@@ -1600,5 +1613,6 @@ init_keywords(vector keywords)<br>
        install_keyword("san_path_err_<wbr>recovery_time", &mp_san_path_err_recovery_<wbr>time_handler, &snprint_mp_san_path_err_<wbr>recovery_time);<br>
        install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx);<br>
        install_keyword("max_sectors_<wbr>kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb);<br>
+       install_keyword("ghost_delay", &mp_ghost_delay_handler, &snprint_mp_ghost_delay);<br>
        install_sublevel_end();<br>
 }<br>
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c<br>
index 78de1fa..7226fb1 100644<br>
--- a/libmultipath/hwtable.c<br>
+++ b/libmultipath/hwtable.c<br>
@@ -72,6 +72,7 @@<br>
                .delay_wait_checks = DELAY_CHECKS_OFF,<br>
                .skip_kpartx   = SKIP_KPARTX_OFF,<br>
                .max_sectors_kb = MAX_SECTORS_KB_UNDEF,<br>
+               .ghost_delay = GHOST_DELAY_OFF<br>
        },<br>
 #endif<br>
<br>
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c<br>
index 00adc0d..6721cc6 100644<br>
--- a/libmultipath/propsel.c<br>
+++ b/libmultipath/propsel.c<br>
@@ -835,3 +835,18 @@ out:<br>
                origin);<br>
        return 0;<br>
 }<br>
+<br>
+int select_ghost_delay (struct config *conf, struct multipath * mp)<br>
+{<br>
+       char *origin, buff[12];<br>
+<br>
+       mp_set_mpe(ghost_delay);<br>
+       mp_set_ovr(ghost_delay);<br>
+       mp_set_hwe(ghost_delay);<br>
+       mp_set_conf(ghost_delay);<br>
+       mp_set_default(ghost_delay, DEFAULT_GHOST_DELAY);<br>
+out:<br>
+       print_off_int_undef(buff, 12, &mp->ghost_delay);<br>
+       condlog(3, "%s: ghost_delay = %s %s", mp->alias, buff, origin);<br>
+       return 0;<br>
+}<br>
diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h<br>
index f8e96d8..494fb10 100644<br>
--- a/libmultipath/propsel.h<br>
+++ b/libmultipath/propsel.h<br>
@@ -25,6 +25,7 @@ int select_delay_watch_checks (struct config *conf, struct multipath * mp);<br>
 int select_delay_wait_checks (struct config *conf, struct multipath * mp);<br>
 int select_skip_kpartx (struct config *conf, struct multipath * mp);<br>
 int select_max_sectors_kb (struct config *conf, struct multipath * mp);<br>
+int select_ghost_delay(struct config *conf, struct multipath * mp);<br>
 int select_san_path_err_forget_<wbr>rate(struct config *conf, struct multipath *mp);<br>
 int select_san_path_err_threshold(<wbr>struct config *conf, struct multipath *mp);<br>
 int select_san_path_err_recovery_<wbr>time(struct config *conf, struct multipath *mp);<br>
diff --git a/libmultipath/structs.h b/libmultipath/structs.h<br>
index f06824a..d2d7701 100644<br>
--- a/libmultipath/structs.h<br>
+++ b/libmultipath/structs.h<br>
@@ -167,6 +167,11 @@ enum no_undef_states {<br>
        NU_UNDEF = 0,<br>
 };<br>
<br>
+enum ghost_delay_states {<br>
+       GHOST_DELAY_OFF = NU_NO,<br>
+       GHOST_DELAY_UNDEF = NU_UNDEF,<br>
+};<br>
+<br>
 enum initialized_states {<br>
        INIT_FAILED,<br>
        INIT_MISSING_UDEV,<br>
@@ -282,6 +287,8 @@ struct multipath {<br>
        int max_sectors_kb;<br>
        int force_readonly;<br>
        int force_udev_reload;<br>
+       int ghost_delay;<br>
+       int ghost_delay_tick;<br>
        unsigned int dev_loss;<br>
        uid_t uid;<br>
        gid_t gid;<br>
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5<br>
index 4bd1a8d..8783124 100644<br>
--- a/multipath/multipath.conf.5<br>
+++ b/multipath/multipath.conf.5<br>
@@ -1017,6 +1017,19 @@ The default is: \fB<device dependent>\fR<br>
 .RE<br>
 .<br>
 .<br>
+.TP<br>
+.B ghost_delay<br>
+Sets the number of seconds that multipath will wait after creating a device<br>
+with only ghost paths before marking it ready for use in systemd. This gives<br>
+the active paths time to appear before the multipath runs the hardware handler<br>
+to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIon\fR<br>
+makes multipath immediately mark a device with only ghost paths as ready.<br>
+.RS<br>
+.TP<br>
+The default is \fBno\fR<br>
+.RE<br>
+.<br>
+.<br>
 .\" ------------------------------<wbr>------------------------------<wbr>----------------<br>
 .SH "blacklist section"<br>
 .\" ------------------------------<wbr>------------------------------<wbr>----------------<br>
@@ -1157,6 +1170,8 @@ are taken from the \fIdefaults\fR or \fIdevices\fR section:<br>
 .B skip_kpartx<br>
 .TP<br>
 .B max_sectors_kb<br>
+.TP<br>
+.B ghost_delay<br>
 .RE<br>
 .PD<br>
 .LP<br>
@@ -1284,6 +1299,8 @@ section:<br>
 .B skip_kpartx<br>
 .TP<br>
 .B max_sectors_kb<br>
+.TP<br>
+.B ghost_delay<br>
 .RE<br>
 .PD<br>
 .LP<br>
@@ -1354,6 +1371,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:<br>
 .B delay_wait_checks<br>
 .TP<br>
 .B skip_kpartx<br>
+.TP<br>
+.B ghost_delay<br>
 .RE<br>
 .PD<br>
 .LP<br>
diff --git a/multipathd/main.c b/multipathd/main.c<br>
index 8049da2..c475fcd 100644<br>
--- a/multipathd/main.c<br>
+++ b/multipathd/main.c<br>
@@ -351,6 +351,8 @@ sync_map_state(struct multipath *mpp)<br>
                            pp->state == PATH_WILD ||<br>
                            pp->state == PATH_DELAYED)<br>
                                continue;<br>
+                       if (mpp->ghost_delay_tick > 0)<br>
+                               continue;<br>
                        if ((pp->dmstate == PSTATE_FAILED ||<br>
                             pp->dmstate == PSTATE_UNDEF) &&<br>
                            (pp->state == PATH_UP || pp->state == PATH_GHOST))<br>
@@ -735,7 +737,8 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)<br>
        mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);<br>
        if (mpp && mpp->wait_for_udev &&<br>
            (pathcount(mpp, PATH_UP) > 0 ||<br>
-            (pathcount(mpp, PATH_GHOST) > 0 && pp->tpgs != TPGS_IMPLICIT))) {<br>
+            (pathcount(mpp, PATH_GHOST) > 0 && pp->tpgs != TPGS_IMPLICIT &&<br>
+             mpp->ghost_delay_tick <= 0))) {<br>
                /* if wait_for_udev is set and valid paths exist */<br>
                condlog(2, "%s: delaying path addition until %s is fully initialized", pp->dev, mpp->alias);<br>
                mpp->wait_for_udev = 2;<br>
@@ -1416,6 +1419,28 @@ missing_uev_wait_tick(struct vectors *vecs)<br>
 }<br>
<br>
 static void<br>
+ghost_delay_tick(struct vectors *vecs)<br>
+{<br>
+       struct multipath * mpp;<br>
+       unsigned int i;<br>
+<br>
+       vector_foreach_slot (vecs->mpvec, mpp, i) {<br>
+               if (mpp->ghost_delay_tick <= 0)<br>
+                       continue;<br>
+               if (--mpp->ghost_delay_tick <= 0) {<br>
+                       condlog(0, "%s: timed out waiting for active path",<br>
+                               mpp->alias);<br>
+                       mpp->force_udev_reload = 1;<br>
+                       if (update_map(mpp, vecs) != 0) {<br>
+                               /* update_map removed map */<br>
+                               i--;<br>
+                               continue;<br>
+                       }<br>
+               }<br>
+       }<br>
+}<br>
+<br>
+static void<br>
 defered_failback_tick (vector mpvec)<br>
 {<br>
        struct multipath * mpp;<br>
@@ -1961,6 +1986,7 @@ checkerloop (void *ap)<br>
                defered_failback_tick(vecs-><wbr>mpvec);<br>
                retry_count_tick(vecs->mpvec);<br>
                missing_uev_wait_tick(vecs);<br>
+               ghost_delay_tick(vecs);<br>
                lock_cleanup_pop(vecs->lock);<br>
<br>
                if (count)<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.7.4<br>
<br>
</font></span></blockquote></div><br></div>