setup_map: wait for pending path checkers to finish
authorMartin Wilck <mwilck@suse.com>
Sun, 23 Dec 2018 22:21:13 +0000 (23:21 +0100)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Mon, 7 Jan 2019 10:46:23 +0000 (11:46 +0100)
The timeout for synchronous checks is only 1ms when async path
checking is used, which may easily be missed on busy systems.
That's not a problem for the path checker, because it simply
repeats the check one tick later. However, when maps are
set up (e.g. when new paths are detected), it's desirable
to get the number of active paths right. Therefore, wait a bit
longer if path checkers are found pending in setup_map().

Signed-off-by: Martin Wilck <mwilck@suse.com>
libmultipath/configure.c

index 5af4a18..39d2a95 100644 (file)
 #include "sysfs.h"
 #include "io_err_stat.h"
 
+/* Time in ms to wait for pending checkers in setup_map() */
+#define WAIT_CHECKERS_PENDING_MS 10
+#define WAIT_ALL_CHECKERS_PENDING_MS 90
+
 /* group paths in pg by host adapter
  */
 int group_by_host_adapter(struct pathgroup *pgp, vector adapters)
@@ -257,12 +261,43 @@ int rr_optimize_path_order(struct pathgroup *pgp)
        return 0;
 }
 
+static int wait_for_pending_paths(struct multipath *mpp,
+                                 struct config *conf,
+                                 int n_pending, int goal, int wait_ms)
+{
+       static const struct timespec millisec =
+               { .tv_sec = 0, .tv_nsec = 1000*1000 };
+       int i, j;
+       struct path *pp;
+       struct pathgroup *pgp;
+       struct timespec ts;
+
+       do {
+               vector_foreach_slot(mpp->pg, pgp, i) {
+                       vector_foreach_slot(pgp->paths, pp, j) {
+                               if (pp->state != PATH_PENDING)
+                                       continue;
+                               pp->state = get_state(pp, conf,
+                                                     0, PATH_PENDING);
+                               if (pp->state != PATH_PENDING &&
+                                   --n_pending <= goal)
+                                       return 0;
+                       }
+               }
+               ts = millisec;
+               while (nanosleep(&ts, &ts) != 0 && errno == EINTR)
+                       /* nothing */;
+       } while (--wait_ms > 0);
+
+       return n_pending;
+}
+
 int setup_map(struct multipath *mpp, char *params, int params_size,
              struct vectors *vecs)
 {
        struct pathgroup * pgp;
        struct config *conf;
-       int i;
+       int i, n_paths;
 
        /*
         * don't bother if devmap size is unknown
@@ -339,7 +374,9 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
                condlog(0, "%s: unexpected behavior may occur!",
                        mpp->alias);
        }
-       /*
+
+       n_paths = VECTOR_SIZE(mpp->paths);
+        /*
         * assign paths to path groups -- start with no groups and all paths
         * in mpp->paths
         */
@@ -353,6 +390,30 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
        if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp))
                return 1;
 
+       /*
+        * If async state detection is used, see if pending state checks
+        * have finished, to get nr_active right. We can't wait until the
+        * checkers time out, as that may take 30s or more, and we are
+        * holding the vecs lock.
+        */
+       if (conf->force_sync == 0 && n_paths > 0) {
+               int n_pending = pathcount(mpp, PATH_PENDING);
+
+               if (n_pending > 0)
+                       n_pending = wait_for_pending_paths(
+                               mpp, conf, n_pending, 0,
+                               WAIT_CHECKERS_PENDING_MS);
+               /* ALL paths pending - wait some more, but be satisfied
+                  with only some paths finished */
+               if (n_pending == n_paths)
+                       n_pending = wait_for_pending_paths(
+                               mpp, conf, n_pending,
+                               n_paths >= 4 ? 2 : 1,
+                               WAIT_ALL_CHECKERS_PENDING_MS);
+               if (n_pending > 0)
+                       condlog(2, "%s: setting up map with %d/%d path checkers pending",
+                               mpp->alias, n_pending, n_paths);
+       }
        mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
 
        /*