From: DongHun Kwak Date: Fri, 14 Jan 2022 04:50:19 +0000 (+0900) Subject: Imported Upstream version 0.8.3 X-Git-Tag: upstream/0.8.3^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9f1483fb0bea7defd7bdcfedb8a5329171f6d1d9;p=platform%2Fupstream%2Fmultipath-tools.git Imported Upstream version 0.8.3 --- diff --git a/kpartx/dasd.c b/kpartx/dasd.c index d95d8ca..4e7e474 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -133,9 +133,6 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) /* Couldn't open the device */ return -1; } - } else if ((unsigned int)major(sbuf.st_rdev) != 94) { - /* Not a DASD */ - return -1; } else { fd_dasd = dup(fd); if (fd_dasd < 0) @@ -279,6 +276,10 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) size = disksize; if (fmt_size < size) size = fmt_size; + } else if ((unsigned int)major(sbuf.st_rdev) != 94) { + /* Not a DASD */ + retval = -1; + goto out; } else size = disksize; diff --git a/kpartx/solaris.c b/kpartx/solaris.c index 8c1a971..e7826c6 100644 --- a/kpartx/solaris.c +++ b/kpartx/solaris.c @@ -1,17 +1,15 @@ #include "kpartx.h" #include -#include +#include #include /* time_t */ #define SOLARIS_X86_NUMSLICE 8 #define SOLARIS_X86_VTOC_SANE (0x600DDEEEUL) -//typedef int daddr_t; /* or long - check */ - struct solaris_x86_slice { unsigned short s_tag; /* ID tag of partition */ unsigned short s_flag; /* permission flags */ - long s_start; /* start sector no of partition */ + __kernel_daddr_t s_start; /* start sector no of partition */ long s_size; /* # of blocks in partition */ }; diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 53022f5..603cfc3 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -82,18 +82,10 @@ updatepaths (struct multipath * mpp) vector_foreach_slot (pgp->paths, pp, j){ if (!strlen(pp->dev)){ - if (devt2devname(pp->dev, FILE_NAME_SIZE, - pp->dev_t)){ - /* - * path is not in sysfs anymore - */ - pp->state = PATH_DOWN; - continue; - } - pp->mpp = mpp; - conf = get_multipath_config(); - pathinfo(pp, conf, DI_ALL); - put_multipath_config(conf); + /* + * path is not in sysfs anymore + */ + pp->state = PATH_DOWN; continue; } pp->mpp = mpp; diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index f4fdcae..a08bf41 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -16,6 +16,7 @@ struct checker_class { char name[CHECKER_NAME_LEN]; int (*check)(struct checker *); int (*init)(struct checker *); /* to allocate the context */ + int (*mp_init)(struct checker *); /* to allocate the mpcontext */ void (*free)(struct checker *); /* to free the context */ const char **msgtable; short msgtable_size; @@ -140,6 +141,10 @@ static struct checker_class *add_checker_class(const char *multipath_dir, if (!c->init) goto out; + c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init"); + /* NULL mp_init is o.k. call dlerror() to clear out any error string */ + dlerror(); + c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free"); errstr = dlerror(); if (errstr != NULL) @@ -212,8 +217,26 @@ int checker_init (struct checker * c, void ** mpctxt_addr) if (!c || !c->cls) return 1; c->mpcontext = mpctxt_addr; - if (c->cls->init) - return c->cls->init(c); + if (c->cls->init && c->cls->init(c) != 0) + return 1; + if (mpctxt_addr && *mpctxt_addr == NULL && c->cls->mp_init && + c->cls->mp_init(c) != 0) /* continue even if mp_init fails */ + c->mpcontext = NULL; + return 0; +} + +int checker_mp_init(struct checker * c, void ** mpctxt_addr) +{ + if (!c || !c->cls) + return 1; + if (c->mpcontext || !mpctxt_addr) + return 0; + c->mpcontext = mpctxt_addr; + if (*mpctxt_addr == NULL && c->cls->mp_init && + c->cls->mp_init(c) != 0) { + c->mpcontext = NULL; + return 1; + } return 0; } diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index dab197f..5237e7e 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -138,6 +138,7 @@ const char *checker_state_name(int); int init_checkers(const char *); void cleanup_checkers (void); int checker_init (struct checker *, void **); +int checker_mp_init(struct checker *, void **); void checker_clear (struct checker *); void checker_put (struct checker *); void checker_reset (struct checker *); diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c index 6fc8911..5cd63ac 100644 --- a/libmultipath/checkers/emc_clariion.c +++ b/libmultipath/checkers/emc_clariion.c @@ -107,11 +107,18 @@ int libcheck_init (struct checker * c) return 1; ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; + return 0; +} + +int libcheck_mp_init (struct checker * c) +{ /* * Allocate and initialize the multi-path global context. */ if (c->mpcontext && *c->mpcontext == NULL) { void * mpctxt = malloc(sizeof(int)); + if (!mpctxt) + return 1; *c->mpcontext = mpctxt; CLR_INACTIVE_SNAP(c); } diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index 6b08dbb..e886fcf 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -290,7 +290,7 @@ static void *tur_thread(void *ctx) static void tur_timeout(struct timespec *tsp) { - clock_gettime(CLOCK_MONOTONIC, tsp); + get_monotonic_time(tsp); tsp->tv_nsec += 1000 * 1000; /* 1 millisecond */ normalize_timespec(tsp); } @@ -300,7 +300,7 @@ static void tur_set_async_timeout(struct checker *c) struct tur_checker_context *ct = c->context; struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + get_monotonic_time(&now); ct->time = now.tv_sec + c->timeout; } @@ -309,7 +309,7 @@ static int tur_check_async_timeout(struct checker *c) struct tur_checker_context *ct = c->context; struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + get_monotonic_time(&now); return (now.tv_sec > ct->time); } diff --git a/libmultipath/config.h b/libmultipath/config.h index ff2b4e8..ffec310 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -186,6 +186,7 @@ struct config { int max_sectors_kb; int ghost_delay; int find_multipaths_timeout; + int marginal_pathgroups; unsigned int version[3]; char * multipath_dir; @@ -224,6 +225,7 @@ struct config { vector elist_device; vector elist_property; vector elist_protocol; + char *enable_foreign; }; extern struct udev * udev; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index b09ef52..5ac7d90 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -297,7 +297,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, { struct pathgroup * pgp; struct config *conf; - int i, n_paths; + int i, n_paths, marginal_pathgroups; /* * don't bother if devmap size is unknown @@ -342,8 +342,6 @@ int setup_map(struct multipath *mpp, char *params, int params_size, 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); @@ -351,29 +349,18 @@ int setup_map(struct multipath *mpp, char *params, int params_size, select_san_path_err_threshold(conf, mpp); select_san_path_err_forget_rate(conf, mpp); select_san_path_err_recovery_time(conf, mpp); + select_delay_checks(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); + marginal_pathgroups = conf->marginal_pathgroups; pthread_cleanup_pop(1); - if (marginal_path_check_enabled(mpp)) { - if (delay_check_enabled(mpp)) { - condlog(1, "%s: WARNING: both marginal_path and delay_checks error detection selected", - mpp->alias); - condlog(0, "%s: unexpected behavior may occur!", - mpp->alias); - } + if (marginal_path_check_enabled(mpp)) start_io_err_stat_thread(vecs); - } - if (san_path_check_enabled(mpp) && delay_check_enabled(mpp)) { - condlog(1, "%s: WARNING: both san_path_err and delay_checks error detection selected", - mpp->alias); - condlog(0, "%s: unexpected behavior may occur!", - mpp->alias); - } n_paths = VECTOR_SIZE(mpp->paths); /* @@ -387,7 +374,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, vector_free(mpp->pg); mpp->pg = NULL; } - if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp)) + if (group_paths(mpp, marginal_pathgroups)) return 1; /* @@ -519,6 +506,42 @@ trigger_udev_change(const struct multipath *mpp) udev_device_unref(udd); } +static void trigger_partitions_udev_change(struct udev_device *dev, + const char *action, int len) +{ + struct udev_enumerate *part_enum; + struct udev_list_entry *item; + + part_enum = udev_enumerate_new(udev); + if (!part_enum) + return; + + if (udev_enumerate_add_match_parent(part_enum, dev) < 0 || + udev_enumerate_add_match_subsystem(part_enum, "block") < 0 || + udev_enumerate_scan_devices(part_enum) < 0) + goto unref; + + udev_list_entry_foreach(item, + udev_enumerate_get_list_entry(part_enum)) { + const char *syspath; + struct udev_device *part; + + syspath = udev_list_entry_get_name(item); + part = udev_device_new_from_syspath(udev, syspath); + if (!part) + continue; + + if (!strcmp("partition", udev_device_get_devtype(part))) { + condlog(4, "%s: triggering %s event for %s", __func__, + action, syspath); + sysfs_attr_set_value(part, "uevent", action, len); + } + udev_device_unref(part); + } +unref: + udev_enumerate_unref(part_enum); +} + void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) { @@ -569,6 +592,8 @@ trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) action, pp->dev, is_mpath ? "" : "no "); sysfs_attr_set_value(pp->udev, "uevent", action, strlen(action)); + trigger_partitions_udev_change(pp->udev, action, + strlen(action)); } } diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index decc933..4dfe007 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -48,6 +48,8 @@ #define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10 #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF +/* Enable all foreign libraries by default */ +#define DEFAULT_ENABLE_FOREIGN "" #define CHECKINT_UNDEF (~0U) #define DEFAULT_CHECKINT 5 diff --git a/libmultipath/dict.c b/libmultipath/dict.c index c6eba0f..2b046e1 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -610,6 +610,10 @@ declare_def_handler(find_multipaths_timeout, set_int) declare_def_snprint_defint(find_multipaths_timeout, print_int, DEFAULT_FIND_MULTIPATHS_TIMEOUT) +declare_def_handler(enable_foreign, set_str) +declare_def_snprint_defstr(enable_foreign, print_str, + DEFAULT_ENABLE_FOREIGN) + static int def_config_dir_handler(struct config *conf, vector strvec) { @@ -1339,6 +1343,8 @@ declare_ovr_snprint(all_tg_pt, print_yes_no_undef) declare_hw_handler(all_tg_pt, set_yes_no_undef) declare_hw_snprint(all_tg_pt, print_yes_no_undef) +declare_def_handler(marginal_pathgroups, set_yes_no) +declare_def_snprint(marginal_pathgroups, print_yes_no) static int def_uxsock_timeout_handler(struct config *conf, vector strvec) @@ -1710,6 +1716,9 @@ init_keywords(vector keywords) install_keyword("find_multipaths_timeout", &def_find_multipaths_timeout_handler, &snprint_def_find_multipaths_timeout); + install_keyword("enable_foreign", &def_enable_foreign_handler, + &snprint_def_enable_foreign); + install_keyword("marginal_pathgroups", &def_marginal_pathgroups_handler, &snprint_def_marginal_pathgroups); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index acca466..72f455e 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -1608,6 +1608,8 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate) return PATH_UNCHECKED; } } + if (pp->mpp && !c->mpcontext) + checker_mp_init(c, &pp->mpp->mpcontext); checker_clear_message(c); if (daemon) { if (conf->force_sync == 0) diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index 48e8d24..4b34e14 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include "vector.h" #include "debug.h" #include "util.h" @@ -111,17 +113,45 @@ static int select_foreign_libs(const struct dirent *di) return fnmatch(foreign_pattern, di->d_name, FNM_FILE_NAME) == 0; } -static int _init_foreign(const char *multipath_dir) +static void free_pre(void *arg) +{ + regex_t **pre = arg; + + if (pre != NULL && *pre != NULL) { + regfree(*pre); + free(*pre); + *pre = NULL; + } +} + +static int _init_foreign(const char *multipath_dir, const char *enable) { char pathbuf[PATH_MAX]; struct dirent **di; struct scandir_result sr; int r, i; + regex_t *enable_re = NULL; foreigns = vector_alloc(); if (foreigns == NULL) return -ENOMEM; + pthread_cleanup_push(free_pre, &enable_re); + enable_re = calloc(1, sizeof(*enable_re)); + if (enable_re) { + const char *str = enable ? enable : DEFAULT_ENABLE_FOREIGN; + + r = regcomp(enable_re, str, REG_EXTENDED|REG_NOSUB); + if (r != 0) { + char errbuf[64]; + + (void)regerror(r, enable_re, errbuf, sizeof(errbuf)); + condlog (2, "%s: error compiling enable_foreign = \"%s\": \"%s\"", + __func__, str, errbuf); + free_pre(&enable_re); + } + } + r = scandir(multipath_dir, &di, select_foreign_libs, alphasort); if (r == 0) { @@ -163,6 +193,20 @@ static int _init_foreign(const char *multipath_dir) memset(fgn, 0, sizeof(*fgn)); strlcpy((char*)fgn + offsetof(struct foreign, name), c, namesz); + if (enable_re != NULL) { + int ret = regexec(enable_re, fgn->name, 0, NULL, 0); + + if (ret == REG_NOMATCH) { + condlog(3, "%s: foreign library \"%s\" is not enabled", + __func__, fgn->name); + free(fgn); + continue; + } else if (ret != 0) + /* assume it matches */ + condlog(2, "%s: error %d in regexec() for %s", + __func__, ret, fgn->name); + } + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn); fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL); msg = dlerror(); @@ -205,11 +249,12 @@ static int _init_foreign(const char *multipath_dir) dl_err: free_foreign(fgn); } - pthread_cleanup_pop(1); + pthread_cleanup_pop(1); /* free_scandir_result */ + pthread_cleanup_pop(1); /* free_pre */ return 0; } -int init_foreign(const char *multipath_dir) +int init_foreign(const char *multipath_dir, const char *enable) { int ret; @@ -222,7 +267,7 @@ int init_foreign(const char *multipath_dir) } pthread_cleanup_push(unlock_foreigns, NULL); - ret = _init_foreign(multipath_dir); + ret = _init_foreign(multipath_dir, enable); pthread_cleanup_pop(1); return ret; diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h index 697f12f..acd3360 100644 --- a/libmultipath/foreign.h +++ b/libmultipath/foreign.h @@ -195,9 +195,10 @@ struct foreign { * init_foreign(dir) * load and initialize foreign multipath libraries in dir (libforeign-*.so). * @param dir: directory to search + * @param enable: regex to match foreign library name ("*" above) against * @returns: 0 on success, negative value on failure. */ -int init_foreign(const char *multipath_dir); +int init_foreign(const char *multipath_dir, const char *enable); /** * cleanup_foreign(dir) diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 96e8b25..16627ec 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -107,7 +107,7 @@ static struct hwentry default_hw[] = { * HPE */ { - /* 3PAR */ + /* 3PAR / Primera */ .vendor = "3PARdata", .product = "VV", .pgpolicy = GROUP_BY_PRIO, @@ -300,6 +300,27 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, .no_path_retry = 30, }, + { + /* + * Nexenta COMSTAR + * + * Maintainer: Yacine Kheddache + */ + .vendor = "NEXENTA", + .product = "COMSTAR", + .pgpolicy = GROUP_BY_SERIAL, + .no_path_retry = 30, + }, + { + /* Tegile IntelliFlash */ + .vendor = "TEGILE", + .product = "(ZEBI-(FC|ISCSI)|INTELLIFLASH)", + .hwhandler = "1 alua", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .prio_name = PRIO_ALUA, + .no_path_retry = 10, + }, /* * Dell EMC */ @@ -360,6 +381,12 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, + { + /* EMC PowerMax NVMe */ + .vendor = "NVME", + .product = "^EMC PowerMax_", + .pgpolicy = MULTIBUS, + }, /* * Fujitsu */ @@ -780,18 +807,6 @@ static struct hwentry default_hw[] = { .pgpolicy = MULTIBUS, .no_path_retry = NO_PATH_RETRY_QUEUE, }, - /* - * Nexenta - * - * Maintainer: Yacine Kheddache - */ - { - /* COMSTAR */ - .vendor = "NEXENTA", - .product = "COMSTAR", - .pgpolicy = GROUP_BY_SERIAL, - .no_path_retry = 30, - }, /* * NEC */ @@ -1075,19 +1090,6 @@ static struct hwentry default_hw[] = { .product = "K2", .pgpolicy = MULTIBUS, }, - /* - * Western Digital (Tegile Systems) - */ - { - /* IntelliFlash */ - .vendor = "TEGILE", - .product = "(ZEBI-(FC|ISCSI)|INTELLIFLASH)", - .hwhandler = "1 alua", - .pgpolicy = GROUP_BY_PRIO, - .pgfailback = -FAILBACK_IMMEDIATE, - .prio_name = PRIO_ALUA, - .no_path_retry = 10, - }, /* * Imation/Nexsan */ diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index 660768a..8f7c6b1 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "checkers.h" #include "util.h" @@ -71,9 +72,11 @@ sort_pathgroups (struct multipath *mp) { pgp2 = VECTOR_SLOT(mp->pg, j); if (!pgp2) continue; - if (pgp2->priority > pgp1->priority || - (pgp2->priority == pgp1->priority && - pgp2->enabled_paths >= pgp1->enabled_paths)) { + if (pgp2->marginal < pgp1->marginal || + (pgp2->marginal == pgp1->marginal && + (pgp2->priority > pgp1->priority || + (pgp2->priority == pgp1->priority && + pgp2->enabled_paths >= pgp1->enabled_paths)))) { vector_move_up(mp->pg, i, j + 1); break; } @@ -83,87 +86,114 @@ sort_pathgroups (struct multipath *mp) { } } +static int +split_marginal_paths(vector paths, vector *normal_p, vector *marginal_p) +{ + int i; + int has_marginal = 0; + int has_normal = 0; + struct path *pp; + vector normal = NULL; + vector marginal = NULL; + + *normal_p = *marginal_p = NULL; + vector_foreach_slot(paths, pp, i) { + if (pp->marginal) + has_marginal = 1; + else + has_normal = 1; + } -/* - * One path group per unique tgt_node_name present in the path vector - */ -int group_by_node_name(struct multipath * mp) + if (!has_marginal || !has_normal) + return -1; + + normal = vector_alloc(); + marginal = vector_alloc(); + if (!normal || !marginal) + goto fail; + + vector_foreach_slot(paths, pp, i) { + if (pp->marginal) { + if (store_path(marginal, pp)) + goto fail; + } + else { + if (store_path(normal, pp)) + goto fail; + } + } + *normal_p = normal; + *marginal_p = marginal; + return 0; +fail: + vector_free(normal); + vector_free(marginal); + return -1; +} + +int group_paths(struct multipath *mp, int marginal_pathgroups) { - int i, j; - int * bitmap; - struct path * pp; - struct pathgroup * pgp; - struct path * pp2; + vector normal, marginal; if (!mp->pg) mp->pg = vector_alloc(); - if (!mp->pg) return 1; - /* init the bitmap */ - bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); - - if (!bitmap) + if (VECTOR_SIZE(mp->paths) == 0) goto out; - - for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { - - if (bitmap[i]) - continue; - - pp = VECTOR_SLOT(mp->paths, i); - - /* here, we really got a new pg */ - pgp = alloc_pathgroup(); - - if (!pgp) - goto out1; - - if (add_pathgroup(mp, pgp)) - goto out2; - - /* feed the first path */ - if (store_path(pgp->paths, pp)) - goto out2; - - bitmap[i] = 1; - - for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { - - if (bitmap[j]) - continue; - - pp2 = VECTOR_SLOT(mp->paths, j); - - if (!strncmp(pp->tgt_node_name, pp2->tgt_node_name, - NODE_NAME_SIZE)) { - if (store_path(pgp->paths, pp2)) - goto out2; - - bitmap[j] = 1; - } - } + if (!mp->pgpolicyfn) + goto fail; + + if (!marginal_pathgroups || + split_marginal_paths(mp->paths, &normal, &marginal) != 0) { + if (mp->pgpolicyfn(mp, mp->paths) != 0) + goto fail; + } else { + if (mp->pgpolicyfn(mp, normal) != 0) + goto fail_marginal; + if (mp->pgpolicyfn(mp, marginal) != 0) + goto fail_marginal; + vector_free(normal); + vector_free(marginal); } - FREE(bitmap); sort_pathgroups(mp); - free_pathvec(mp->paths, KEEP_PATHS); +out: + vector_free(mp->paths); mp->paths = NULL; return 0; -out2: - free_pathgroup(pgp, KEEP_PATHS); -out1: - FREE(bitmap); -out: - free_pgvec(mp->pg, KEEP_PATHS); +fail_marginal: + vector_free(normal); + vector_free(marginal); +fail: + vector_free(mp->pg); mp->pg = NULL; return 1; } -/* - * One path group per unique serial number present in the path vector - */ -int group_by_serial(struct multipath * mp) +typedef bool (path_match_fn)(struct path *pp1, struct path *pp2); + +bool +node_names_match(struct path *pp1, struct path *pp2) +{ + return (strncmp(pp1->tgt_node_name, pp2->tgt_node_name, + NODE_NAME_SIZE) == 0); +} + +bool +serials_match(struct path *pp1, struct path *pp2) +{ + return (strncmp(pp1->serial, pp2->serial, SERIAL_SIZE) == 0); +} + +bool +prios_match(struct path *pp1, struct path *pp2) +{ + return (pp1->priority == pp2->priority); +} + +int group_by_match(struct multipath * mp, vector paths, + bool (*path_match_fn)(struct path *, struct path *)) { int i, j; int * bitmap; @@ -171,24 +201,18 @@ int group_by_serial(struct multipath * mp) struct pathgroup * pgp; struct path * pp2; - if (!mp->pg) - mp->pg = vector_alloc(); - - if (!mp->pg) - return 1; - /* init the bitmap */ - bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); + bitmap = (int *)MALLOC(VECTOR_SIZE(paths) * sizeof (int)); if (!bitmap) goto out; - for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { + for (i = 0; i < VECTOR_SIZE(paths); i++) { if (bitmap[i]) continue; - pp = VECTOR_SLOT(mp->paths, i); + pp = VECTOR_SLOT(paths, i); /* here, we really got a new pg */ pgp = alloc_pathgroup(); @@ -201,29 +225,26 @@ int group_by_serial(struct multipath * mp) /* feed the first path */ if (store_path(pgp->paths, pp)) - goto out2; + goto out1; bitmap[i] = 1; - for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { + for (j = i + 1; j < VECTOR_SIZE(paths); j++) { if (bitmap[j]) continue; - pp2 = VECTOR_SLOT(mp->paths, j); + pp2 = VECTOR_SLOT(paths, j); - if (0 == strcmp(pp->serial, pp2->serial)) { + if (path_match_fn(pp, pp2)) { if (store_path(pgp->paths, pp2)) - goto out2; + goto out1; bitmap[j] = 1; } } } FREE(bitmap); - sort_pathgroups(mp); - free_pathvec(mp->paths, KEEP_PATHS); - mp->paths = NULL; return 0; out2: free_pathgroup(pgp, KEEP_PATHS); @@ -235,71 +256,49 @@ out: return 1; } -int one_path_per_group(struct multipath *mp) +/* + * One path group per unique tgt_node_name present in the path vector + */ +int group_by_node_name(struct multipath * mp, vector paths) { - int i; - struct path * pp; - struct pathgroup * pgp; - - if (!mp->pg) - mp->pg = vector_alloc(); - - if (!mp->pg) - return 1; - - for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { - pp = VECTOR_SLOT(mp->paths, i); - pgp = alloc_pathgroup(); - - if (!pgp) - goto out; + return group_by_match(mp, paths, node_names_match); +} - if (add_pathgroup(mp, pgp)) - goto out1; +/* + * One path group per unique serial number present in the path vector + */ +int group_by_serial(struct multipath * mp, vector paths) +{ + return group_by_match(mp, paths, serials_match); +} - if (store_path(pgp->paths, pp)) - goto out1; - } - sort_pathgroups(mp); - free_pathvec(mp->paths, KEEP_PATHS); - mp->paths = NULL; - return 0; -out1: - free_pathgroup(pgp, KEEP_PATHS); -out: - free_pgvec(mp->pg, KEEP_PATHS); - mp->pg = NULL; - return 1; +/* + * One path group per priority present in the path vector + */ +int group_by_prio(struct multipath *mp, vector paths) +{ + return group_by_match(mp, paths, prios_match); } -int one_group(struct multipath *mp) /* aka multibus */ +int one_path_per_group(struct multipath *mp, vector paths) { + int i; + struct path * pp; struct pathgroup * pgp; - if (VECTOR_SIZE(mp->paths) < 0) - return 0; - - if (!mp->pg) - mp->pg = vector_alloc(); - - if (!mp->pg) - return 1; - - if (VECTOR_SIZE(mp->paths) > 0) { + for (i = 0; i < VECTOR_SIZE(paths); i++) { + pp = VECTOR_SLOT(paths, i); pgp = alloc_pathgroup(); if (!pgp) goto out; - vector_free(pgp->paths); - if (add_pathgroup(mp, pgp)) goto out1; - pgp->paths = mp->paths; - mp->paths = NULL; + if (store_path(pgp->paths, pp)) + goto out; } - return 0; out1: free_pathgroup(pgp, KEEP_PATHS); @@ -309,94 +308,31 @@ out: return 1; } -int group_by_prio(struct multipath *mp) +int one_group(struct multipath *mp, vector paths) /* aka multibus */ { int i; - int prio; struct path * pp; struct pathgroup * pgp; - vector pathvec = NULL; - if (!mp->pg) - mp->pg = vector_alloc(); - - if (!mp->pg) - return 1; + pgp = alloc_pathgroup(); - pathvec = vector_alloc(); - if (!pathvec) + if (!pgp) goto out; - vector_foreach_slot(mp->paths, pp, i) { - if (!vector_alloc_slot(pathvec)) - goto out1; - vector_set_slot(pathvec, pp); - } - - while (VECTOR_SIZE(pathvec) > 0) { - pp = VECTOR_SLOT(pathvec, 0); - prio = pp->priority; - - /* - * Find the position to insert the new path group. All groups - * are ordered by the priority value (higher value first). - */ - vector_foreach_slot(mp->pg, pgp, i) { - pp = VECTOR_SLOT(pgp->paths, 0); + if (add_pathgroup(mp, pgp)) + goto out1; - if (prio > pp->priority) - break; - } - - /* - * Initialize the new path group. - */ - pgp = alloc_pathgroup(); - - if (!pgp) - goto out1; - - if (store_path(pgp->paths, VECTOR_SLOT(pathvec, 0))) - goto out2; - - vector_del_slot(pathvec, 0); - - /* - * Store the new path group into the vector. - */ - if (i < VECTOR_SIZE(mp->pg)) { - if (!vector_insert_slot(mp->pg, i, pgp)) - goto out2; - pgp->mpp = mp; - } else { - if (add_pathgroup(mp, pgp)) - goto out2; - } + for (i = 0; i < VECTOR_SIZE(paths); i++) { + pp = VECTOR_SLOT(paths, i); - /* - * add the other paths with the same prio - */ - vector_foreach_slot(pathvec, pp, i) { - if (pp->priority == prio) { - if (store_path(pgp->paths, pp)) - goto out2; - - vector_del_slot(pathvec, i); - i--; - } - } + if (store_path(pgp->paths, pp)) + goto out; } - free_pathvec(pathvec, KEEP_PATHS); - free_pathvec(mp->paths, KEEP_PATHS); - mp->paths = NULL; return 0; -out2: - free_pathgroup(pgp, KEEP_PATHS); out1: - free_pathvec(pathvec, KEEP_PATHS); + free_pathgroup(pgp, KEEP_PATHS); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; - } diff --git a/libmultipath/pgpolicies.h b/libmultipath/pgpolicies.h index c0eaa7f..1592761 100644 --- a/libmultipath/pgpolicies.h +++ b/libmultipath/pgpolicies.h @@ -21,14 +21,14 @@ enum iopolicies { int get_pgpolicy_id(char *); int get_pgpolicy_name (char *, int, int); - +int group_paths(struct multipath *, int); /* * policies */ -int one_path_per_group(struct multipath *); -int one_group(struct multipath *); -int group_by_serial(struct multipath *); -int group_by_prio(struct multipath *); -int group_by_node_name(struct multipath *); +int one_path_per_group(struct multipath *, vector); +int one_group(struct multipath *, vector); +int group_by_serial(struct multipath *, vector); +int group_by_prio(struct multipath *, vector); +int group_by_node_name(struct multipath *, vector); #endif diff --git a/libmultipath/print.c b/libmultipath/print.c index fc40b0f..907469a 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -502,6 +502,14 @@ snprint_pg_state (char * buff, size_t len, const struct pathgroup * pgp) } } +static int +snprint_pg_marginal (char * buff, size_t len, const struct pathgroup * pgp) +{ + if (pgp->marginal) + return snprintf(buff, len, "marginal"); + return snprintf(buff, len, "normal"); +} + static int snprint_path_size (char * buff, size_t len, const struct path * pp) { @@ -672,6 +680,14 @@ snprint_path_protocol(char * buff, size_t len, const struct path * pp) } } +int +snprint_path_marginal(char * buff, size_t len, const struct path * pp) +{ + if (pp->marginal) + return snprintf(buff, len, "marginal"); + return snprintf(buff, len, "normal"); +} + struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, @@ -713,6 +729,7 @@ struct path_data pd[] = { {'p', "pri", 0, snprint_pri}, {'S', "size", 0, snprint_path_size}, {'z', "serial", 0, snprint_path_serial}, + {'M', "marginal_st", 0, snprint_path_marginal}, {'m', "multipath", 0, snprint_path_mpp}, {'N', "host WWNN", 0, snprint_host_wwnn}, {'n', "target WWNN", 0, snprint_tgt_wwnn}, @@ -729,6 +746,7 @@ struct pathgroup_data pgd[] = { {'s', "selector", 0, snprint_pg_selector}, {'p', "pri", 0, snprint_pg_pri}, {'t', "dm_st", 0, snprint_pg_state}, + {'M', "marginal_st", 0, snprint_pg_marginal}, {0, NULL, 0 , NULL} }; diff --git a/libmultipath/print.h b/libmultipath/print.h index e2fb865..7e36ec6 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -50,7 +50,8 @@ #define PRINT_JSON_GROUP "{\n" \ " \"selector\" : \"%s\",\n" \ " \"pri\" : %p,\n" \ - " \"dm_st\" : \"%t\"," + " \"dm_st\" : \"%t\",\n" \ + " \"marginal_st\" : \"%M\"," #define PRINT_JSON_GROUP_NUM " \"group\" : %d,\n" @@ -66,7 +67,8 @@ " \"target_wwnn\" : \"%n\",\n" \ " \"host_wwpn\" : \"%R\",\n" \ " \"target_wwpn\" : \"%r\",\n" \ - " \"host_adapter\" : \"%a\"" + " \"host_adapter\" : \"%a\",\n" \ + " \"marginal_st\" : \"%M\"" #define MAX_LINE_LEN 80 #define MAX_LINES 64 diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 6af2513..27e8d68 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -85,6 +85,10 @@ static const char autodetect_origin[] = "(setting: storage device autodetected)"; static const char marginal_path_origin[] = "(setting: implied by marginal_path check)"; +static const char delay_watch_origin[] = + "(setting: implied by delay_watch_checks)"; +static const char delay_wait_origin[] = + "(setting: implied by delay_wait_checks)"; #define do_default(dest, value) \ do { \ @@ -877,39 +881,80 @@ out: return 0; } -int select_delay_watch_checks(struct config *conf, struct multipath *mp) +static inline int san_path_check_options_set(const struct multipath *mp) { - const char *origin; + return mp->san_path_err_threshold > 0 || + mp->san_path_err_forget_rate > 0 || + mp->san_path_err_recovery_time > 0; +} + +static int +use_delay_watch_checks(struct config *conf, struct multipath *mp) +{ + int value = NU_UNDEF; + const char *origin = default_origin; char buff[12]; - mp_set_mpe(delay_watch_checks); - mp_set_ovr(delay_watch_checks); - mp_set_hwe(delay_watch_checks); - mp_set_conf(delay_watch_checks); - mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS); + do_set(delay_watch_checks, mp->mpe, value, multipaths_origin); + do_set(delay_watch_checks, conf->overrides, value, overrides_origin); + do_set_from_hwe(delay_watch_checks, mp, value, hwe_origin); + do_set(delay_watch_checks, conf, value, conf_origin); out: - if (print_off_int_undef(buff, 12, mp->delay_watch_checks) != 0) - condlog(3, "%s: delay_watch_checks = %s %s", - mp->alias, buff, origin); - return 0; + if (print_off_int_undef(buff, 12, value) != 0) + condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, + origin); + return value; } -int select_delay_wait_checks(struct config *conf, struct multipath *mp) +static int +use_delay_wait_checks(struct config *conf, struct multipath *mp) { - const char *origin; + int value = NU_UNDEF; + const char *origin = default_origin; char buff[12]; - mp_set_mpe(delay_wait_checks); - mp_set_ovr(delay_wait_checks); - mp_set_hwe(delay_wait_checks); - mp_set_conf(delay_wait_checks); - mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS); + do_set(delay_wait_checks, mp->mpe, value, multipaths_origin); + do_set(delay_wait_checks, conf->overrides, value, overrides_origin); + do_set_from_hwe(delay_wait_checks, mp, value, hwe_origin); + do_set(delay_wait_checks, conf, value, conf_origin); out: - if (print_off_int_undef(buff, 12, mp->delay_wait_checks) != 0) - condlog(3, "%s: delay_wait_checks = %s %s", - mp->alias, buff, origin); - return 0; + if (print_off_int_undef(buff, 12, value) != 0) + condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, + origin); + return value; +} + +int select_delay_checks(struct config *conf, struct multipath *mp) +{ + int watch_checks, wait_checks; + char buff[12]; + watch_checks = use_delay_watch_checks(conf, mp); + wait_checks = use_delay_wait_checks(conf, mp); + if (watch_checks <= 0 && wait_checks <= 0) + return 0; + if (san_path_check_options_set(mp)) { + condlog(3, "%s: both marginal_path and delay_checks error detection options selected", mp->alias); + condlog(3, "%s: ignoring delay_checks options", mp->alias); + return 0; + } + mp->san_path_err_threshold = 1; + condlog(3, "%s: san_path_err_threshold = 1 %s", mp->alias, + (watch_checks > 0)? delay_watch_origin : delay_wait_origin); + if (watch_checks > 0) { + mp->san_path_err_forget_rate = watch_checks; + 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, delay_watch_origin); + } + if (wait_checks > 0) { + mp->san_path_err_recovery_time = wait_checks * + conf->max_checkint; + 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, delay_wait_origin); + } + return 0; } static int san_path_deprecated_warned; diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index b352c16..ddfd626 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -22,8 +22,7 @@ int select_retain_hwhandler (struct config *conf, struct multipath * mp); int select_detect_prio(struct config *conf, struct path * pp); int select_detect_checker(struct config *conf, struct path * pp); int select_deferred_remove(struct config *conf, struct multipath *mp); -int select_delay_watch_checks (struct config *conf, struct multipath * mp); -int select_delay_wait_checks (struct config *conf, struct multipath * mp); +int select_delay_checks(struct config *conf, struct multipath * mp); int select_skip_kpartx (struct config *conf, struct multipath * mp); int select_max_sectors_kb (struct config *conf, struct multipath * mp); int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index fee899b..bf7fdd7 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -318,23 +318,13 @@ store_path (vector pathvec, struct path * pp) return 0; } -int -store_pathgroup (vector pgvec, struct pathgroup * pgp) +int add_pathgroup(struct multipath *mpp, struct pathgroup *pgp) { - if (!vector_alloc_slot(pgvec)) + if (!vector_alloc_slot(mpp->pg)) return 1; - vector_set_slot(pgvec, pgp); - - return 0; -} - -int add_pathgroup(struct multipath *mpp, struct pathgroup *pgp) -{ - int ret = store_pathgroup(mpp->pg, pgp); + vector_set_slot(mpp->pg, pgp); - if (ret) - return ret; pgp->mpp = mpp; return 0; } diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 7879d76..a3adf90 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -268,8 +268,6 @@ struct path { int pgindex; int detect_prio; int detect_checker; - int watch_checks; - int wait_checks; int tpgs; char * uid_attribute; char * getuid; @@ -289,12 +287,13 @@ struct path { int io_err_pathfail_cnt; int io_err_pathfail_starttime; int find_multipaths_timeout; + int marginal; /* configlet pointers */ vector hwe; struct gen_path generic_path; }; -typedef int (pgpolicyfn) (struct multipath *); +typedef int (pgpolicyfn) (struct multipath *, vector); struct multipath { char wwid[WWID_SIZE]; @@ -320,8 +319,6 @@ struct multipath { int fast_io_fail; int retain_hwhandler; int deferred_remove; - int delay_watch_checks; - int delay_wait_checks; int san_path_err_threshold; int san_path_err_forget_rate; int san_path_err_recovery_time; @@ -392,17 +389,12 @@ static inline int san_path_check_enabled(const struct multipath *mpp) mpp->san_path_err_recovery_time > 0; } -static inline int delay_check_enabled(const struct multipath *mpp) -{ - return mpp->delay_watch_checks != NU_NO || - mpp->delay_wait_checks != NU_NO; -} - struct pathgroup { long id; int status; int priority; int enabled_paths; + int marginal; vector paths; struct multipath *mpp; struct gen_pathgroup generic_pg; @@ -443,7 +435,6 @@ int store_adaptergroup(vector adapters, struct adapter_group *agp); int store_hostgroup(vector hostgroupvec, struct host_group *hgp); int store_path (vector pathvec, struct path * pp); -int store_pathgroup (vector pgvec, struct pathgroup * pgp); int add_pathgroup(struct multipath*, struct pathgroup *); struct multipath * find_mp_by_alias (const struct _vector *mp, const char *alias); diff --git a/libmultipath/switchgroup.c b/libmultipath/switchgroup.c index 9632ce2..6fdfcfa 100644 --- a/libmultipath/switchgroup.c +++ b/libmultipath/switchgroup.c @@ -11,6 +11,7 @@ void path_group_prio_update(struct pathgroup *pgp) { int i; int priority = 0; + int marginal = 0; struct path * pp; pgp->enabled_paths = 0; @@ -19,6 +20,8 @@ void path_group_prio_update(struct pathgroup *pgp) return; } vector_foreach_slot (pgp->paths, pp, i) { + if (pp->marginal) + marginal++; if (pp->state == PATH_UP || pp->state == PATH_GHOST) { priority += pp->priority; @@ -29,11 +32,14 @@ void path_group_prio_update(struct pathgroup *pgp) pgp->priority = priority / pgp->enabled_paths; else pgp->priority = 0; + if (marginal && marginal == i) + pgp->marginal = 1; } int select_path_group(struct multipath *mpp) { int i; + int normal_pgp = 0; int max_priority = 0; int bestpg = 1; int max_enabled_paths = 1; @@ -47,8 +53,15 @@ int select_path_group(struct multipath *mpp) continue; path_group_prio_update(pgp); + if (pgp->marginal && normal_pgp) + continue; if (pgp->enabled_paths) { - if (pgp->priority > max_priority) { + if (!pgp->marginal && !normal_pgp) { + normal_pgp = 1; + max_priority = pgp->priority; + max_enabled_paths = pgp->enabled_paths; + bestpg = i + 1; + } else if (pgp->priority > max_priority) { max_priority = pgp->priority; max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c index 6d79c0e..a3739a2 100644 --- a/libmultipath/time-util.c +++ b/libmultipath/time-util.c @@ -3,6 +3,15 @@ #include #include "time-util.h" +void get_monotonic_time(struct timespec *res) +{ + struct timespec ts; + int rv = clock_gettime(CLOCK_MONOTONIC, &ts); + + assert(rv == 0); + *res = ts; +} + /* Initialize @cond as a condition variable that uses the monotonic clock */ void pthread_cond_init_mono(pthread_cond_t *cond) { diff --git a/libmultipath/time-util.h b/libmultipath/time-util.h index b76d2aa..b23d328 100644 --- a/libmultipath/time-util.h +++ b/libmultipath/time-util.h @@ -5,6 +5,7 @@ struct timespec; +void get_monotonic_time(struct timespec *res); void pthread_cond_init_mono(pthread_cond_t *cond); void normalize_timespec(struct timespec *ts); void timespecsub(const struct timespec *a, const struct timespec *b, diff --git a/libmultipath/vector.h b/libmultipath/vector.h index 41d2b89..344dffd 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -40,7 +40,7 @@ typedef struct _vector *vector; #define vector_foreach_slot_after(v,p,i) \ for (; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_backwards(v,p,i) \ - for (i = VECTOR_SIZE(v); i > 0 && ((p) = (v)->slot[i-1]); i--) + for (i = VECTOR_SIZE(v) - 1; (int)i >= 0 && ((p) = (v)->slot[i]); i--) #define identity(x) (x) /* diff --git a/libmultipath/version.h b/libmultipath/version.h index f751c30..5e4c328 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000802 -#define DATE_CODE 0x070313 +#define VERSION_CODE 0x000803 +#define DATE_CODE 0x0a0213 #define PROG "multipath-tools" diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 5ad06a9..278b8d5 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -155,7 +155,8 @@ static int do_batch_file(const char *batch_fn) static int handle_args(int argc, char * argv[], int nline) { - int fd, c; + int c; + int fd = -1; const char *device_name = NULL; int num_prin_sa = 0; int num_prout_sa = 0; @@ -213,7 +214,8 @@ static int handle_args(int argc, char * argv[], int nline) if (nline == 0 && 1 != sscanf (optarg, "%d", &loglevel)) { fprintf (stderr, "bad argument to '--verbose'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } break; @@ -228,6 +230,7 @@ static int handle_args(int argc, char * argv[], int nline) case 'h': usage (); + free(batch_fn); return 0; case 'H': @@ -254,7 +257,8 @@ static int handle_args(int argc, char * argv[], int nline) if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_rk)) { fprintf (stderr, "bad argument to '--param-rk'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } ++num_prout_param; break; @@ -263,7 +267,8 @@ static int handle_args(int argc, char * argv[], int nline) if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_sark)) { fprintf (stderr, "bad argument to '--param-sark'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } ++num_prout_param; break; @@ -282,7 +287,8 @@ static int handle_args(int argc, char * argv[], int nline) if (1 != sscanf (optarg, "%x", &prout_type)) { fprintf (stderr, "bad argument to '--prout-type'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } ++num_prout_param; break; @@ -330,7 +336,8 @@ static int handle_args(int argc, char * argv[], int nline) case 'X': if (0 != construct_transportid(optarg, transportids, num_transport)) { fprintf(stderr, "bad argument to '--transport-id'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } ++num_transport; @@ -339,11 +346,13 @@ static int handle_args(int argc, char * argv[], int nline) case 'l': if (1 != sscanf(optarg, "%u", &mpath_mx_alloc_len)) { fprintf(stderr, "bad argument to '--alloc-length'\n"); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } else if (MPATH_MAX_PARAM_LEN < mpath_mx_alloc_len) { fprintf(stderr, "'--alloc-length' argument exceeds maximum" " limit(%d)\n", MPATH_MAX_PARAM_LEN); - return MPATH_PR_SYNTAX_ERROR; + ret = MPATH_PR_SYNTAX_ERROR; + goto out; } break; @@ -420,12 +429,14 @@ static int handle_args(int argc, char * argv[], int nline) fprintf (stderr, " No service action given for Persistent Reserve IN\n"); ret = MPATH_PR_SYNTAX_ERROR; + goto out; } else if (num_prin_sa > 1) { fprintf (stderr, " Too many service actions given; choose " "one only\n"); ret = MPATH_PR_SYNTAX_ERROR; + goto out; } } else @@ -481,14 +492,14 @@ static int handle_args(int argc, char * argv[], int nline) { fprintf (stderr, "failed to allocate PRIN response buffer\n"); ret = MPATH_PR_OTHER; - goto out; + goto out_fd; } ret = __mpath_persistent_reserve_in (fd, prin_sa, resp, noisy); if (ret != MPATH_PR_SUCCESS ) { fprintf (stderr, "Persistent Reserve IN command failed\n"); - goto out; + goto out_fd; } switch(prin_sa) @@ -568,8 +579,8 @@ static int handle_args(int argc, char * argv[], int nline) printf("PR out: command failed\n"); } +out_fd: close (fd); - out : if (ret == MPATH_PR_SYNTAX_ERROR) { free(batch_fn); @@ -844,12 +855,7 @@ static void usage(void) " --reserve|-R PR Out: Reserve\n" " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned\n" " in several forms\n" - " --alloc-length=LEN|-l LEN PR In: maximum allocation length\n" - " Examples:\n" - " mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n" - " mpathpersist -i -k /dev/mapper/mpath9\n" - " mpathpersist --out --reserve --param-sark=123abc --prout-type=8 -d /dev/mapper/mpath9\n" - " mpathpersist -i -s -d /dev/mapper/mpath9\n"); + " --alloc-length=LEN|-l LEN PR In: maximum allocation length\n"); } void diff --git a/multipath/main.c b/multipath/main.c index 96a1146..4f4d8e8 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -1050,7 +1050,7 @@ main (int argc, char *argv[]) goto out; } /* Failing here is non-fatal */ - init_foreign(conf->multipath_dir); + init_foreign(conf->multipath_dir, conf->enable_foreign); if (cmd == CMD_USABLE_PATHS) { r = check_usable_paths(conf, dev, dev_type) ? RTVL_FAIL : RTVL_OK; diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index f7d21b4..e866da2 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -4,8 +4,6 @@ .\" groff -z -wall -b -e -t multipath/multipath.conf.5 .\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null .\" -.\" TODO: Look for XXX and ??? -.\" .\" ---------------------------------------------------------------------------- . .TH MULTIPATH.CONF 5 2018-05-21 Linux @@ -1013,10 +1011,12 @@ The default is: \fBno\fR . .TP .B delay_watch_checks -If set to a value greater than 0, multipathd will watch paths that have -recently become valid for this many checks. If they fail again while they are -being watched, when they next become valid, they will not be used until they -have stayed up for \fIdelay_wait_checks\fR checks. See "Shaky paths detection" below. +This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_forget_rate\fR. +If this is set to a value greater than 0 and no \fIsan_path_err\fR options +are set, \fIsan_path_err_forget_rate\fR will be set to the value of +\fIdelay_watch_checks\fR and \fIsan_path_err_threshold\fR will be set to 1. +See the \fIsan_path_err_forget_rate\fR and \fIsan_path_err_threshold\fR +options, and "Shaky paths detection" below for more information. .RS .TP The default is: \fBno\fR @@ -1025,10 +1025,14 @@ The default is: \fBno\fR . .TP .B delay_wait_checks -If set to a value greater than 0, when a device that has recently come back -online fails again within \fIdelay_watch_checks\fR checks, the next time it -comes back online, it will marked and delayed, and not used until it has passed -\fIdelay_wait_checks\fR checks. See "Shaky paths detection" below. +This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_recovery_time\fR. +If this is set to a value greater than 0 and no \fIsan_path_err\fR options +are set, \fIsan_path_err_recovery_time\fR will be set to the value of +\fIdelay_wait_checks\fR times \fImax_polling_interval\fR. This will give +approximately the same wait time as delay_wait_checks previously did. +Also, \fIsan_path_err_threshold\fR will be set to 1. See the +\fIsan_path_err_recovery_time\fR and \fIsan_path_err_threshold\fR +options, and "Shaky paths detection" below for more information. .RS .TP The default is: \fBno\fR @@ -1036,6 +1040,28 @@ The default is: \fBno\fR . . .TP +.B marginal_pathgroups +If set to \fIno\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and +\fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from +being reinstated until they have been monitored for some time. This can cause +situations where all non-marginal paths are down, and no paths are usable +until multipathd detects this and reinstates a marginal path. If the multipath +device is not configured to queue IO in this case, it can cause IO errors to +occur, even though there are marginal paths available. However, if this +option is set to \fIyes\fR, when one of the marginal path detecting methods +determines that a path is marginal, it will be reinstated and placed in a +seperate pathgroup that will only be used after all the non-marginal pathgroups +have been tried first. This prevents the possibility of IO errors occuring +while marginal paths are still usable. After the path has been monitored +for the configured time, and is declared healthy, it will be returned to its +normal pathgroup. See "Shaky paths detection" below for more information. +.RS +.TP +The default is: \fBno\fR +.RE +. +. +.TP .B find_multipaths This option controls whether multipath and multipathd try to create multipath maps over non-blacklisted devices they encounter. This matters a) when a device is @@ -1194,6 +1220,21 @@ makes multipath immediately mark a device with only ghost paths as ready. The default is: \fBno\fR .RE . +. +.TP +.B enable_foreign +Enables or disables foreign libraries (see section +.I FOREIGN MULTIPATH SUPPORT +below). The value is a regular expression; foreign libraries are loaded +if their name (e.g. \(dqnvme\(dq) matches the expression. By default, +all foreign libraries are enabled. +.RS +.TP +The default is: \fB\(dq\(dq\fR (the empty regular expression) +.RE +. +. + . .\" ---------------------------------------------------------------------------- .SH "blacklist and blacklist_exceptions sections" @@ -1683,19 +1724,20 @@ events. \fImultipathd\fR supports three different methods for detecting this situation and dealing with it. All methods share the same basic mode of operation: If a path is found to be \(dqshaky\(dq or \(dqflipping\(dq, and appears to be in healthy status, it is not reinstated (put back to use) -immediately. Instead, it is watched for some time, and only reinstated -if the healthy state appears to be stable. The logic of determining -\(dqshaky\(dq condition, as well as the logic when to reinstate, -differs between the three methods. +immediately. Instead, it is placed in the \(dqdelayed\(dq state and watched +for some time, and only reinstated if the healthy state appears to be stable. +If the \fImarginal_pathgroups\fR option is set, the path will reinstated +immediately, but placed in a special pathgroup for marginal paths. Marginal +pathgroups will not be used until all other pathgroups have been tried. At the +time when the path would normally be reinstated, it will be returned to its +normal pathgroup. The logic of determining \(dqshaky\(dq condition, as well as +the logic when to reinstate, differs between the three methods. .TP 8 .B \(dqdelay_checks\(dq failure tracking -If a path fails again within a -\fIdelay_watch_checks\fR interval after a failure, don't -reinstate it until it passes a \fIdelay_wait_checks\fR interval -in always good status. -The intervals are measured in \(dqticks\(dq, i.e. the -time between path checks by multipathd, which is variable and controlled by the -\fIpolling_interval\fR and \fImax_polling_interval\fR parameters. +This method is \fBdeprecated\fR and mapped to the \(dqsan_path_err\(dq method. +See the \fIdelay_watch_checks\fR and \fIdelay_wait_checks\fR options above +for more information. + .TP .B \(dqmarginal_path\(dq failure tracking If a second failure event (good->bad transition) occurs within @@ -1712,12 +1754,13 @@ in seconds. .B \(dqsan_path_err\(dq failure tracking multipathd counts path failures for each path. Once the number of failures exceeds the value given by \fIsan_path_err_threshold\fR, the path is not -reinstated for \fIsan_path_err_recovery_time\fR ticks. While counting +reinstated for \fIsan_path_err_recovery_time\fR seconds. While counting failures, multipathd \(dqforgets\(dq one past failure every \(dqsan_path_err_forget_rate\(dq ticks; thus if errors don't occur more often then once in the forget rate interval, the failure count doesn't -increase and the threshold is never reached. As for the \fIdelay_xy\fR method, -intervals are measured in \(dqticks\(dq. +increase and the threshold is never reached. Ticks are the time between +path checks by multipathd, which is variable and controlled by the +\fIpolling_interval\fR and \fImax_polling_interval\fR parameters. . .RS 8 .LP @@ -1735,6 +1778,31 @@ unpredictable ways. If the \(dqmarginal_path\(dq method is active, the . . .\" ---------------------------------------------------------------------------- +.SH "FOREIGN MULTIPATH SUPPORT" +.\" ---------------------------------------------------------------------------- +. +multipath and multipathd can load \(dqforeign\(dq libraries to add +support for other multipathing technologies besides the Linux device mapper. +Currently this support is limited to printing detected information about +multipath setup. In topology output, the names of foreign maps are prefixed by +the foreign library name in square brackets, as in this example: +. +.P +.EX +# multipath -ll +uuid.fedcba98-3579-4567-8765-123456789abc [nvme]:nvme4n9 NVMe,Some NVMe controller,FFFFFFFF +size=167772160 features='n/a' hwhandler='ANA' wp=rw +|-+- policy='n/a' prio=50 status=optimized +| `- 4:38:1 nvme4c38n1 0:0 n/a optimized live +`-+- policy='n/a' prio=50 status=optimized + `- 4:39:1 nvme4c39n1 0:0 n/a optimized live +.EE +. +.P +The \(dqnvme\(dq foreign library provides support for NVMe native multipathing +in the kernel. It is part of the standard multipath package. +. +.\" ---------------------------------------------------------------------------- .SH "KNOWN ISSUES" .\" ---------------------------------------------------------------------------- . diff --git a/multipathd/cli.c b/multipathd/cli.c index 17795b6..800c0fb 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -215,6 +215,8 @@ load_keys (void) r += add_key(keys, "unsetprkey", UNSETPRKEY, 0); r += add_key(keys, "key", KEY, 1); r += add_key(keys, "local", LOCAL, 0); + r += add_key(keys, "setmarginal", SETMARGINAL, 0); + r += add_key(keys, "unsetmarginal", UNSETMARGINAL, 0); if (r) { @@ -589,6 +591,9 @@ cli_init (void) { add_handler(UNSETPRKEY+MAP, NULL); add_handler(FORCEQ+DAEMON, NULL); add_handler(RESTOREQ+DAEMON, NULL); + add_handler(SETMARGINAL+PATH, NULL); + add_handler(UNSETMARGINAL+PATH, NULL); + add_handler(UNSETMARGINAL+MAP, NULL); return 0; } diff --git a/multipathd/cli.h b/multipathd/cli.h index 32dcffa..fdfb9ae 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -45,6 +45,8 @@ enum { __UNSETPRKEY, __KEY, __LOCAL, + __SETMARGINAL, + __UNSETMARGINAL, }; #define LIST (1 << __LIST) @@ -89,6 +91,8 @@ enum { #define UNSETPRKEY (1ULL << __UNSETPRKEY) #define KEY (1ULL << __KEY) #define LOCAL (1ULL << __LOCAL) +#define SETMARGINAL (1ULL << __SETMARGINAL) +#define UNSETMARGINAL (1ULL << __UNSETMARGINAL) #define INITIAL_REPLY_LEN 1200 diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 4c32d95..8a89904 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -1537,3 +1537,94 @@ cli_setprkey(void * v, char ** reply, int * len, void * data) return ret; } + +int cli_set_marginal(void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * param = get_keyparam(v, PATH); + struct path * pp; + + param = convert_dev(param, 1); + pp = find_path_by_dev(vecs->pathvec, param); + + if (!pp) + pp = find_path_by_devt(vecs->pathvec, param); + + if (!pp || !pp->mpp || !pp->mpp->alias) + return 1; + + condlog(2, "%s: set marginal path %s (operator)", + pp->mpp->alias, pp->dev_t); + if (pp->mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing set marginal", + pp->mpp->alias); + return 1; + } + pp->marginal = 1; + + return update_path_groups(pp->mpp, vecs, 0); +} + +int cli_unset_marginal(void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * param = get_keyparam(v, PATH); + struct path * pp; + + param = convert_dev(param, 1); + pp = find_path_by_dev(vecs->pathvec, param); + + if (!pp) + pp = find_path_by_devt(vecs->pathvec, param); + + if (!pp || !pp->mpp || !pp->mpp->alias) + return 1; + + condlog(2, "%s: unset marginal path %s (operator)", + pp->mpp->alias, pp->dev_t); + if (pp->mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, " + "failing unset marginal", pp->mpp->alias); + return 1; + } + pp->marginal = 0; + + return update_path_groups(pp->mpp, vecs, 0); +} + +int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * mapname = get_keyparam(v, MAP); + struct multipath *mpp; + struct pathgroup * pgp; + struct path * pp; + unsigned int i, j; + int minor; + + mapname = convert_dev(mapname, 0); + condlog(2, "%s: unset all marginal paths (operator)", + mapname); + + if (sscanf(mapname, "dm-%d", &minor) == 1) + mpp = find_mp_by_minor(vecs->mpvec, minor); + else + mpp = find_mp_by_alias(vecs->mpvec, mapname); + + if (!mpp) { + condlog(0, "%s: invalid map name. " + "cannot unset marginal paths", mapname); + return 1; + } + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, " + "failing unset all marginal", mpp->alias); + return 1; + } + + vector_foreach_slot (mpp->pg, pgp, i) + vector_foreach_slot (pgp->paths, pp, j) + pp->marginal = 0; + + return update_path_groups(mpp, vecs, 0); +} diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index edbdf06..0f45106 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -49,3 +49,6 @@ int cli_unsetprstatus(void * v, char ** reply, int * len, void * data); int cli_getprkey(void * v, char ** reply, int * len, void * data); int cli_setprkey(void * v, char ** reply, int * len, void * data); int cli_unsetprkey(void * v, char ** reply, int * len, void * data); +int cli_set_marginal(void * v, char ** reply, int * len, void * data); +int cli_unset_marginal(void * v, char ** reply, int * len, void * data); +int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data); diff --git a/multipathd/main.c b/multipathd/main.c index 7a5cd11..34a5768 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -283,11 +283,10 @@ int set_config_state(enum daemon_status state) else if (running_state != DAEMON_IDLE) { struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - ts.tv_sec += 1; - rc = pthread_cond_timedwait(&config_cond, - &config_lock, &ts); - } + get_monotonic_time(&ts); + ts.tv_sec += 1; + rc = pthread_cond_timedwait(&config_cond, + &config_lock, &ts); } if (!rc && (running_state != DAEMON_SHUTDOWN)) { running_state = state; @@ -1609,6 +1608,9 @@ uxlsnrloop (void * ap) set_handler_callback(GETPRKEY+MAP, cli_getprkey); set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey); set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); + set_handler_callback(SETMARGINAL+PATH, cli_set_marginal); + set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal); + set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal); umask(077); uxsock_listen(&uxsock_trigger, ux_sock, ap); @@ -1888,15 +1890,24 @@ static int check_path_reinstate_state(struct path * pp) { } if (pp->disable_reinstate) { - /* If we don't know how much time has passed, automatically - * reinstate the path, just to be safe. Also, if there are - * no other usable paths, reinstate the path - */ - if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0 || - pp->mpp->nr_active == 0) { + /* If there are no other usable paths, reinstate the path */ + if (pp->mpp->nr_active == 0) { condlog(2, "%s : reinstating path early", pp->dev); goto reinstate_path; } + get_monotonic_time(&curr_time); + + /* If path became failed again or continue failed, should reset + * path san_path_err_forget_rate and path dis_reinstate_time to + * start a new stable check. + */ + if ((pp->state != PATH_UP) && (pp->state != PATH_GHOST) && + (pp->state != PATH_DELAYED)) { + pp->san_path_err_forget_rate = + pp->mpp->san_path_err_forget_rate; + pp->dis_reinstate_time = curr_time.tv_sec; + } + if ((curr_time.tv_sec - pp->dis_reinstate_time ) > pp->mpp->san_path_err_recovery_time) { condlog(2,"%s : reinstate the path after err recovery time", pp->dev); goto reinstate_path; @@ -1932,8 +1943,7 @@ static int check_path_reinstate_state(struct path * pp) { * delay the path, so there's no point in checking if we should */ - if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) - return 0; + get_monotonic_time(&curr_time); /* when path failures has exceeded the san_path_err_threshold * place the path in delayed state till san_path_err_recovery_time * so that the cutomer can rectify the issue within this time. After @@ -1957,6 +1967,18 @@ reinstate_path: return 0; } +static int +should_skip_path(struct path *pp){ + if (marginal_path_check_enabled(pp->mpp)) { + if (pp->io_err_disable_reinstate && need_io_err_check(pp)) + return 1; + } else if (san_path_check_enabled(pp->mpp)) { + if (check_path_reinstate_state(pp)) + return 1; + } + return 0; +} + /* * Returns '1' if the path has been checked, '-1' if it was blacklisted * and '0' otherwise @@ -1972,6 +1994,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) int oldchkrstate = pp->chkrstate; int retrigger_tries, checkint, max_checkint, verbosity; struct config *conf; + int marginal_pathgroups, marginal_changed = 0; int ret; if ((pp->initialized == INIT_OK || @@ -1988,6 +2011,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) checkint = conf->checkint; max_checkint = conf->max_checkint; verbosity = conf->verbosity; + marginal_pathgroups = conf->marginal_pathgroups; put_multipath_config(conf); if (pp->checkint == CHECKINT_UNDEF) { @@ -2054,6 +2078,11 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) pathinfo(pp, conf, 0); pthread_cleanup_pop(1); return 1; + } else if ((newstate != PATH_UP && newstate != PATH_GHOST) && + (pp->state == PATH_DELAYED)) { + /* If path state become failed again cancel path delay state */ + pp->state = newstate; + return 1; } if (!pp->mpp) { if (!strlen(pp->wwid) && @@ -2103,30 +2132,27 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) set_no_path_retry(pp->mpp); if ((newstate == PATH_UP || newstate == PATH_GHOST) && - check_path_reinstate_state(pp)) { - pp->state = PATH_DELAYED; - return 1; - } - - if ((newstate == PATH_UP || newstate == PATH_GHOST) && - pp->io_err_disable_reinstate && need_io_err_check(pp)) { - pp->state = PATH_SHAKY; - /* - * to reschedule as soon as possible,so that this path can - * be recoverd in time - */ - pp->tick = 1; - return 1; - } - - if ((newstate == PATH_UP || newstate == PATH_GHOST) && - pp->wait_checks > 0) { - if (pp->mpp->nr_active > 0) { - pp->state = PATH_DELAYED; - pp->wait_checks--; - return 1; - } else - pp->wait_checks = 0; + (san_path_check_enabled(pp->mpp) || + marginal_path_check_enabled(pp->mpp))) { + int was_marginal = pp->marginal; + if (should_skip_path(pp)) { + if (!marginal_pathgroups) { + if (marginal_path_check_enabled(pp->mpp)) + /* to reschedule as soon as possible, + * so that this path can be recovered + * in time */ + pp->tick = 1; + pp->state = PATH_DELAYED; + return 1; + } + if (!was_marginal) { + pp->marginal = 1; + marginal_changed = 1; + } + } else if (marginal_pathgroups && was_marginal) { + pp->marginal = 0; + marginal_changed = 1; + } } /* @@ -2159,19 +2185,10 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) * proactively fail path in the DM */ if (oldstate == PATH_UP || - oldstate == PATH_GHOST) { + oldstate == PATH_GHOST) fail_path(pp, 1); - if (pp->mpp->delay_wait_checks > 0 && - pp->watch_checks > 0) { - pp->wait_checks = pp->mpp->delay_wait_checks; - pp->watch_checks = 0; - } - } else { + else fail_path(pp, 0); - if (pp->wait_checks > 0) - pp->wait_checks = - pp->mpp->delay_wait_checks; - } /* * cancel scheduled failback @@ -2197,15 +2214,10 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) * reinstate this path */ if (oldstate != PATH_UP && - oldstate != PATH_GHOST) { - if (pp->mpp->delay_watch_checks > 0) - pp->watch_checks = pp->mpp->delay_watch_checks; + oldstate != PATH_GHOST) add_active = 1; - } else { - if (pp->watch_checks > 0) - pp->watch_checks--; + else add_active = 0; - } if (!disable_reinstate && reinstate_path(pp, add_active)) { condlog(3, "%s: reload map", pp->dev); ev_add_path(pp, vecs, 1); @@ -2250,8 +2262,6 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) condlog(4, "%s: delay next check %is", pp->dev_t, pp->checkint); } - if (pp->watch_checks > 0) - pp->watch_checks--; pp->tick = pp->checkint; } } @@ -2281,7 +2291,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) */ condlog(4, "path prio refresh"); - if (update_prio(pp, new_path_up) && + if (marginal_changed) + update_path_groups(pp->mpp, vecs, 1); + else if (update_prio(pp, new_path_up) && (pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) && pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) update_path_groups(pp->mpp, vecs, !new_path_up); @@ -2315,17 +2327,14 @@ checkerloop (void *ap) condlog(2, "path checkers start up"); /* Tweak start time for initial path check */ - if (clock_gettime(CLOCK_MONOTONIC, &last_time) != 0) - last_time.tv_sec = 0; - else - last_time.tv_sec -= 1; + get_monotonic_time(&last_time); + last_time.tv_sec -= 1; while (1) { struct timespec diff_time, start_time, end_time; int num_paths = 0, ticks = 0, strict_timing, rc = 0; - if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) - start_time.tv_sec = 0; + get_monotonic_time(&start_time); if (start_time.tv_sec && last_time.tv_sec) { timespecsub(&start_time, &last_time, &diff_time); condlog(4, "tick (%lu.%06lu secs)", @@ -2384,8 +2393,8 @@ checkerloop (void *ap) } diff_time.tv_nsec = 0; - if (start_time.tv_sec && - clock_gettime(CLOCK_MONOTONIC, &end_time) == 0) { + if (start_time.tv_sec) { + get_monotonic_time(&end_time); timespecsub(&end_time, &start_time, &diff_time); if (num_paths) { unsigned int max_checkint; @@ -2848,7 +2857,7 @@ child (void * param) } /* Failing this is non-fatal */ - init_foreign(conf->multipath_dir); + init_foreign(conf->multipath_dir, conf->enable_foreign); if (poll_dmevents) poll_dmevents = dmevent_poll_supported(); diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index edac7a9..048a838 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -277,6 +277,25 @@ Remove the persistent reservation key associated with $map from the \fIreservation_key\fR is set to \fBfile\fR in \fI/etc/multipath.conf\fR. . .TP +.B path $path setmarginal +move $path to a marginal pathgroup. The path will remain in the marginal +path group until \fIunsetmarginal\fR is called. This command will only +work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths +detection method configured (see the multipath.conf man page for details). +. +.TP +.B path $path unsetmarginal +return marginal path $path to its normal pathgroup. This command will only +work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths +detection method configured (see the multipath.conf man page for details). +. +.TP +.B map $map unsetmarginal +return all marginal paths in $map to their normal pathgroups. This command +will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky +paths detection method configured (see the multipath.conf man page for details). +. +.TP .B quit|exit End interactive session. . diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index 04cbb7c..bc71679 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -130,10 +130,10 @@ void check_timeout(struct timespec start_time, char *inbuf, { struct timespec diff_time, end_time; - if (start_time.tv_sec && - clock_gettime(CLOCK_MONOTONIC, &end_time) == 0) { + if (start_time.tv_sec) { unsigned long msecs; + get_monotonic_time(&end_time); timespecsub(&end_time, &start_time, &diff_time); msecs = diff_time.tv_sec * 1000 + diff_time.tv_nsec / (1000 * 1000); @@ -280,9 +280,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, i, polls[i].fd); continue; } - if (clock_gettime(CLOCK_MONOTONIC, &start_time) - != 0) - start_time.tv_sec = 0; + get_monotonic_time(&start_time); if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) { diff --git a/tests/Makefile b/tests/Makefile index bf159b2..a5cdf39 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,7 +3,7 @@ include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka -TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd +TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy .SILENT: $(TESTS:%=%.o) .PRECIOUS: $(TESTS:%=%-test) diff --git a/tests/pgpolicy.c b/tests/pgpolicy.c new file mode 100644 index 0000000..3f61b12 --- /dev/null +++ b/tests/pgpolicy.c @@ -0,0 +1,1036 @@ +/* + * Copyright (c) 2018 Benjamin Marzinski, Redhat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "globals.c" +#include "pgpolicies.h" + +struct multipath mp8, mp4, mp1, mp0, mp_null; +struct path p8[8], p4[4], p1[1]; + + +static void set_priority(struct path *pp, int *prio, int size) +{ + int i; + + for (i = 0; i < size; i++) { + pp[i].priority = prio[i]; + } +} + +static void set_marginal(struct path *pp, int *marginal, int size) +{ + int i; + + for (i = 0; i < size; i++) { + pp[i].marginal = marginal[i]; + } +} + +static void set_tgt_node_name(struct path *pp, char **tgt_node_name, int size) +{ + int i; + + for (i = 0; i < size; i++) { + strcpy(pp[i].tgt_node_name, tgt_node_name[i]); + } +} + +static void set_serial(struct path *pp, char **serial, int size) +{ + int i; + + for (i = 0; i < size; i++) { + strcpy(pp[i].serial, serial[i]); + } +} + +static int setup(void **state) +{ + int i; + + for (i = 0; i < 8; i++) { + sprintf(p8[i].dev, "p8_%d", i); + sprintf(p8[i].dev_t, "8:%d", i); + p8[i].state = PATH_UP; + } + for (i = 0; i < 4; i++) { + sprintf(p4[i].dev, "p4_%d", i); + sprintf(p4[i].dev_t, "4:%d", i); + p4[i].state = PATH_UP; + } + sprintf(p1[0].dev, "p1_0"); + sprintf(p1[0].dev_t, "4:0"); + p1[0].state = PATH_UP; + return 0; +} + +static int setupX(struct multipath *mp, struct path *pp, int size) +{ + int i; + int prio[8] = {10, 10, 10, 10, 10, 10, 10, 10}; + int marginal[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + mp->paths = vector_alloc(); + if (!mp->paths) + return -1; + for (i = 0; i < size; i++) { + if (!vector_alloc_slot(mp->paths)) + return -1; + vector_set_slot(mp->paths, &pp[i]); + } + set_priority(pp, prio, size); + set_marginal(pp, marginal, size); + mp->pgpolicyfn = NULL; + return 0; +} + +static int setup8(void **state) +{ + return setupX(&mp8, p8, 8); +} + +static int setup4(void **state) +{ + return setupX(&mp4, p4, 4); +} + +static int setup1(void **state) +{ + return setupX(&mp1, p1, 1); +} + +static int setup0(void **state) +{ + return setupX(&mp0, NULL, 0); +} + +static int setup_null(void **state) +{ + return 0; +} + +static int teardownX(struct multipath *mp) +{ + free_pgvec(mp->pg, KEEP_PATHS); + mp->pg = NULL; + return 0; +} + +static int teardown8(void **state) +{ + return teardownX(&mp8); +} + +static int teardown4(void **state) +{ + return teardownX(&mp4); +} + +static int teardown1(void **state) +{ + return teardownX(&mp1); +} + +static int teardown0(void **state) +{ + return teardownX(&mp0); +} + +static int teardown_null(void **state) +{ + return teardownX(&mp_null); +} + +static void +verify_pathgroups(struct multipath *mp, struct path *pp, int **groups, + int *group_size, int *marginal, int size) +{ + int i, j; + struct pathgroup *pgp; + + assert_null(mp->paths); + assert_non_null(mp->pg); + assert_int_equal(VECTOR_SIZE(mp->pg), size); + for (i = 0; i < size; i++) { + pgp = VECTOR_SLOT(mp->pg, i); + assert_non_null(pgp); + assert_non_null(pgp->paths); + assert_int_equal(VECTOR_SIZE(pgp->paths), group_size[i]); + if (marginal) + assert_int_equal(pgp->marginal, marginal[i]); + else + assert_int_equal(pgp->marginal, 0); + for (j = 0; j < group_size[i]; j++) { + int path_nr = groups[i][j]; + struct path *pgp_path = VECTOR_SLOT(pgp->paths, j); + struct path *pp_path = &pp[path_nr]; + /* Test names instead of pointers to get a more + * useful error message */ + assert_string_equal(pgp_path->dev, pp_path->dev); + /* This test is just a backkup in case the + * something wenth wrong naming the paths */ + assert_ptr_equal(pgp_path, pp_path); + } + } +} + +static void test_one_group8(void **state) +{ + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {paths}; + int group_size[] = {8}; + + mp8.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1); +} + +static void test_one_group4(void **state) +{ + int paths[] = {0,1,2,3}; + int *groups[] = {paths}; + int group_size[] = {4}; + + mp4.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 1); +} + +static void test_one_group1(void **state) +{ + int paths[] = {0}; + int *groups[] = {paths}; + int group_size[] = {1}; + + mp1.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp1, 0), 0); + verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1); +} + +static void test_one_group0(void **state) +{ + mp0.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp0, 0), 0); + verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0); +} + +static void test_one_group_null(void **state) +{ + mp_null.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp_null, 0), 0); + verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0); +} + +static void test_one_group_all_marginal8(void **state) +{ + int paths[] = {0,1,2,3,4,5,6,7}; + int marginal[] = {1,1,1,1,1,1,1,1}; + int *groups[] = {paths}; + int group_size[] = {8}; + int group_marginal[] = {1}; + + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 1); +} + +static void test_one_group_half_marginal8(void **state) +{ + int marginal[] = {1,0,1,0,1,1,0,0}; + int group0[] = {1,3,6,7}; + int group1[] = {0,2,4,5}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + int group_marginal[] = {0,1}; + + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 2); +} + +static void test_one_group_ignore_marginal8(void **state) +{ + int marginal[] = {1,0,1,0,1,1,0,0}; + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {paths}; + int group_size[] = {8}; + + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1); +} + +static void test_one_group_one_marginal8(void **state) +{ + int marginal[] = {0,0,0,0,0,1,0,0}; + int group0[] = {0,1,2,3,4,6,7}; + int group1[] = {5}; + int *groups[] = {group0, group1}; + int group_size[] = {7,1}; + int group_marginal[] = {0,1}; + + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_group; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 2); +} + +static void test_one_path_per_group_same8(void **state) +{ + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_one_path_per_group_increasing8(void **state) +{ + int prio[] = {1,2,3,4,5,6,7,8}; + int paths[] = {7,6,5,4,3,2,1,0}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_one_path_per_group_decreasing8(void **state) +{ + int prio[] = {8,7,6,5,4,3,2,1}; + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_one_path_per_group_mixed8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int paths[] = {6,0,4,2,3,5,7,1}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_one_path_per_group4(void **state) +{ + int paths[] = {0,1,2,3}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3]}; + int group_size[] = {1,1,1,1}; + + mp4.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 4); +} + +static void test_one_path_per_group1(void **state) +{ + int paths[] = {0}; + int *groups[] = {paths}; + int group_size[] = {1}; + + mp1.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp1, 0), 0); + verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1); +} + +static void test_one_path_per_group0(void **state) +{ + mp0.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp0, 0), 0); + verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0); +} + +static void test_one_path_per_group_null(void **state) +{ + mp_null.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp_null, 0), 0); + verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0); +} + +static void test_one_path_per_group_mixed_all_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int marginal[] = {1,1,1,1,1,1,1,1}; + int paths[] = {6,0,4,2,3,5,7,1}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + int group_marginal[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 8); +} + +static void test_one_path_per_group_mixed_half_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int marginal[] = {0,1,1,0,0,0,1,1}; + int paths[] = {0,4,3,5,6,2,7,1}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + int group_marginal[] = {0,0,0,0,1,1,1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = one_path_per_group; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 8); +} + +static void test_group_by_prio_same8(void **state) +{ + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {paths}; + int group_size[] = {8}; + + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1); +} + +static void test_group_by_prio_increasing8(void **state) +{ + int prio[] = {1,2,3,4,5,6,7,8}; + int paths[] = {7,6,5,4,3,2,1,0}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_group_by_prio_decreasing8(void **state) +{ + int prio[] = {8,7,6,5,4,3,2,1}; + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_group_by_prio_mixed8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int group0[] = {6}; + int group1[] = {0}; + int group2[] = {4}; + int group3[] = {2,3}; + int group4[] = {5,7}; + int group5[] = {1}; + int *groups[] = {group0, group1, group2, group3, + group4, group5}; + int group_size[] = {1,1,1,2,2,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 6); +} + +static void test_group_by_prio_mixed_no_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int group0[] = {6}; + int group1[] = {0}; + int group2[] = {4}; + int group3[] = {2,3}; + int group4[] = {5,7}; + int group5[] = {1}; + int *groups[] = {group0, group1, group2, group3, + group4, group5}; + int group_size[] = {1,1,1,2,2,1}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 6); +} + +static void test_group_by_prio_2_groups8(void **state) +{ + int prio[] = {1,2,2,1,2,1,1,2}; + int group0[] = {1,2,4,7}; + int group1[] = {0,3,5,6}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + + set_priority(p8, prio, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 2); +} + +static void test_group_by_prio_mixed4(void **state) +{ + int prio[] = {2,3,1,3}; + int group0[] = {1,3}; + int group1[] = {0}; + int group2[] = {2}; + int *groups[] = {group0, group1, group2}; + int group_size[] = {2,1,1}; + + set_priority(p4, prio, 4); + mp4.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 3); +} + +static void test_group_by_prio_2_groups4(void **state) +{ + int prio[] = {2,1,1,2}; + int group0[] = {0,3}; + int group1[] = {1,2}; + int *groups[] = {group0, group1}; + int group_size[] = {2,2}; + + set_priority(p4, prio, 4); + mp4.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 2); +} + +static void test_group_by_prio1(void **state) +{ + int paths[] = {0}; + int *groups[] = {paths}; + int group_size[] = {1}; + + mp1.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp1, 0), 0); + verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1); +} + +static void test_group_by_prio0(void **state) +{ + mp0.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp0, 0), 0); + verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_prio_null(void **state) +{ + mp_null.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp_null, 0), 0); + verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_prio_mixed_all_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int marginal[] = {1,1,1,1,1,1,1,1}; + int group0[] = {6}; + int group1[] = {0}; + int group2[] = {4}; + int group3[] = {2,3}; + int group4[] = {5,7}; + int group5[] = {1}; + int *groups[] = {group0, group1, group2, group3, + group4, group5}; + int group_size[] = {1,1,1,2,2,1}; + int group_marginal[] = {1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 6); +} + +static void test_group_by_prio_mixed_half_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int marginal[] = {0,0,0,1,0,1,1,1}; + int group0[] = {0}; + int group1[] = {4}; + int group2[] = {2}; + int group3[] = {1}; + int group4[] = {6}; + int group5[] = {3}; + int group6[] = {5,7}; + int *groups[] = {group0, group1, group2, group3, + group4, group5, group6}; + int group_size[] = {1,1,1,1,1,1,2}; + int group_marginal[] = {0,0,0,0,1,1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 7); +} + +static void test_group_by_prio_mixed_one_marginal8(void **state) +{ + int prio[] = {7,1,3,3,5,2,8,2}; + int marginal[] = {0,0,0,0,0,1,0,0}; + int group0[] = {6}; + int group1[] = {0}; + int group2[] = {4}; + int group3[] = {2,3}; + int group4[] = {7}; + int group5[] = {1}; + int group6[] = {5}; + int *groups[] = {group0, group1, group2, group3, + group4, group5, group6}; + int group_size[] = {1,1,1,2,1,1,1}; + int group_marginal[] = {0,0,0,0,0,0,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = group_by_prio; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 7); +} + +static void test_group_by_node_name_same8(void **state) +{ + char *node_name[] = {"a","a","a","a","a","a","a","a"}; + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {paths}; + int group_size[] = {8}; + + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1); +} + +static void test_group_by_node_name_increasing8(void **state) +{ + char *node_name[] = {"a","b","c","d","e","f","g","h"}; + int prio[] = {1,2,3,4,5,6,7,8}; + int paths[] = {7,6,5,4,3,2,1,0}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_group_by_node_name_3_groups8(void **state) +{ + char *node_name[] = {"a","b","a","c","b","c","c","a"}; + int prio[] = {4,1,4,1,1,1,1,4}; + int group0[] = {0,2,7}; + int group1[] = {3,5,6}; + int group2[] = {1,4}; + int *groups[] = {group0, group1, group2}; + int group_size[] = {3,3,2}; + + set_priority(p8, prio, 8); + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 3); +} + +static void test_group_by_node_name_2_groups8(void **state) +{ + char *node_name[] = {"a", "a", "b", "a", "b", "b", "b", "a"}; + int prio[] = {4,1,2,1,2,2,2,1}; + int group0[] = {2,4,5,6}; + int group1[] = {0,1,3,7}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + + set_priority(p8, prio, 8); + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 2); +} + +static void test_group_by_node_name_3_groups4(void **state) +{ + char *node_name[] = {"a","b","c","a"}; + int prio[] = {3,1,3,1}; + int group0[] = {2}; + int group1[] = {0,3}; + int group2[] = {1}; + int *groups[] = {group0, group1, group2}; + int group_size[] = {1,2,1}; + + set_priority(p4, prio, 4); + set_tgt_node_name(p4, node_name, 4); + mp4.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 3); +} + +static void test_group_by_node_name_2_groups4(void **state) +{ + char *node_name[] = {"a","b","b","a"}; + int prio[] = {2,1,1,2}; + int group0[] = {0,3}; + int group1[] = {1,2}; + int *groups[] = {group0, group1}; + int group_size[] = {2,2}; + + set_priority(p4, prio, 4); + set_tgt_node_name(p4, node_name, 4); + mp4.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 2); +} + +static void test_group_by_node_name1(void **state) +{ + char *node_name[] = {"a"}; + int paths[] = {0}; + int *groups[] = {paths}; + int group_size[] = {1}; + + set_tgt_node_name(p1, node_name, 1); + mp1.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp1,0), 0); + verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1); +} + +static void test_group_by_node_name0(void **state) +{ + mp0.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp0, 0), 0); + verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_node_name_null(void **state) +{ + mp_null.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp_null, 0), 0); + verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_node_name_2_groups_all_marginal8(void **state) +{ + char *node_name[] = {"a", "a", "b", "a", "b", "b", "b", "a"}; + int prio[] = {4,1,2,1,2,2,2,1}; + int marginal[] = {1,1,1,1,1,1,1,1}; + int group0[] = {2,4,5,6}; + int group1[] = {0,1,3,7}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + int group_marginal[] = {1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 2); +} + +static void test_group_by_node_name_2_groups_half_marginal8(void **state) +{ + char *node_name[] = {"a", "a", "b", "a", "b", "b", "b", "a"}; + int prio[] = {4,1,2,1,2,2,2,1}; + int marginal[] = {1,0,1,1,0,1,0,0}; + int group0[] = {4,6}; + int group1[] = {1,7}; + int group2[] = {0,3}; + int group3[] = {2,5}; + int *groups[] = {group0, group1, group2, group3}; + int group_size[] = {2,2,2,2}; + int group_marginal[] = {0,0,1,1}; + + set_priority(p8, prio, 8); + set_marginal(p8, marginal, 8); + set_tgt_node_name(p8, node_name, 8); + mp8.pgpolicyfn = group_by_node_name; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 4); +} + +static void test_group_by_serial_same8(void **state) +{ + char *serial[] = {"1","1","1","1","1","1","1","1"}; + int paths[] = {0,1,2,3,4,5,6,7}; + int *groups[] = {paths}; + int group_size[] = {8}; + + set_serial(p8, serial, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1); +} + +static void test_group_by_serial_increasing8(void **state) +{ + char *serial[] = {"1","2","3","4","5","6","7","8"}; + int prio[] = {1,2,3,4,5,6,7,8}; + int paths[] = {7,6,5,4,3,2,1,0}; + int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3], + &paths[4], &paths[5], &paths[6], &paths[7]}; + int group_size[] = {1,1,1,1,1,1,1,1}; + + set_priority(p8, prio, 8); + set_serial(p8, serial, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8); +} + +static void test_group_by_serial_3_groups8(void **state) +{ + char *serial[] = {"1","2","1","3","2","3","2","1"}; + int prio[] = {4,1,4,3,1,3,1,4}; + int group0[] = {0,2,7}; + int group1[] = {3,5}; + int group2[] = {1,4,6}; + int *groups[] = {group0, group1, group2}; + int group_size[] = {3,2,3}; + + set_priority(p8, prio, 8); + set_serial(p8, serial, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 3); +} + +static void test_group_by_serial_2_groups8(void **state) +{ + char *serial[] = {"1", "2", "1", "1", "2", "2", "1", "2"}; + int prio[] = {3,2,2,1,2,2,1,2}; + int group0[] = {1,4,5,7}; + int group1[] = {0,2,3,6}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + + set_priority(p8, prio, 8); + set_serial(p8, serial, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 0), 0); + verify_pathgroups(&mp8, p8, groups, group_size, NULL, 2); +} + +static void test_group_by_serial_3_groups4(void **state) +{ + char *serial[] = {"1","2","3","2"}; + int prio[] = {3,1,3,1}; + int group0[] = {0}; + int group1[] = {2}; + int group2[] = {1,3}; + int *groups[] = {group0, group1, group2}; + int group_size[] = {1,1,2}; + + set_priority(p4, prio, 4); + set_serial(p4, serial, 4); + mp4.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 3); +} + +static void test_group_by_serial_2_groups4(void **state) +{ + char *serial[] = {"1","2","1","2"}; + int prio[] = {3,1,3,1}; + int group0[] = {0,2}; + int group1[] = {1,3}; + int *groups[] = {group0, group1}; + int group_size[] = {2,2}; + + set_priority(p4, prio, 4); + set_serial(p4, serial, 4); + mp4.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp4, 0), 0); + verify_pathgroups(&mp4, p4, groups, group_size, NULL, 2); +} + +static void test_group_by_serial1(void **state) +{ + char *serial[1] = {"1"}; + int paths[1] = {0}; + int *groups[1] = {paths}; + int group_size[1] = {1}; + + set_serial(p1, serial, 1); + mp1.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp1, 0), 0); + verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1); +} + +static void test_group_by_serial0(void **state) +{ + mp0.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp0, 0), 0); + verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_serial_null(void **state) +{ + mp_null.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp_null, 0), 0); + verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0); +} + +static void test_group_by_serial_2_groups8_all_marginal8(void **state) +{ + char *serial[] = {"1", "2", "1", "1", "2", "2", "1", "2"}; + int marginal[] = {1,1,1,1,1,1,1,1}; + int prio[] = {3,2,2,1,2,2,1,2}; + int group0[] = {1,4,5,7}; + int group1[] = {0,2,3,6}; + int *groups[] = {group0, group1}; + int group_size[] = {4,4}; + int group_marginal[] = {1,1}; + + set_priority(p8, prio, 8); + set_serial(p8, serial, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 2); +} + +static void test_group_by_serial_2_groups8_half_marginal8(void **state) +{ + char *serial[] = {"1", "2", "1", "1", "2", "2", "1", "2"}; + int marginal[] = {0,0,1,1,1,1,0,0}; + int prio[] = {3,2,2,1,2,2,1,2}; + int group0[] = {0,6}; + int group1[] = {1,7}; + int group2[] = {4,5}; + int group3[] = {2,3}; + int *groups[] = {group0, group1, group2, group3}; + int group_size[] = {2,2,2,2}; + int group_marginal[] = {0,0,1,1}; + + set_priority(p8, prio, 8); + set_serial(p8, serial, 8); + set_marginal(p8, marginal, 8); + mp8.pgpolicyfn = group_by_serial; + assert_int_equal(group_paths(&mp8, 1), 0); + verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 4); +} + +#define setup_test(name, nr) \ +cmocka_unit_test_setup_teardown(name ## nr, setup ## nr, teardown ## nr) + +int test_pgpolicies(void) +{ + const struct CMUnitTest tests[] = { + setup_test(test_one_group, 8), + setup_test(test_one_group, 4), + setup_test(test_one_group, 1), + setup_test(test_one_group, 0), + setup_test(test_one_group, _null), + setup_test(test_one_group_all_marginal, 8), + setup_test(test_one_group_half_marginal, 8), + setup_test(test_one_group_ignore_marginal, 8), + setup_test(test_one_group_one_marginal, 8), + setup_test(test_one_path_per_group_same, 8), + setup_test(test_one_path_per_group_increasing, 8), + setup_test(test_one_path_per_group_decreasing, 8), + setup_test(test_one_path_per_group_mixed, 8), + setup_test(test_one_path_per_group, 4), + setup_test(test_one_path_per_group, 1), + setup_test(test_one_path_per_group, 0), + setup_test(test_one_path_per_group, _null), + setup_test(test_one_path_per_group_mixed_all_marginal, 8), + setup_test(test_one_path_per_group_mixed_half_marginal, 8), + setup_test(test_group_by_prio_same, 8), + setup_test(test_group_by_prio_increasing, 8), + setup_test(test_group_by_prio_decreasing, 8), + setup_test(test_group_by_prio_mixed, 8), + setup_test(test_group_by_prio_mixed_no_marginal, 8), + setup_test(test_group_by_prio_2_groups, 8), + setup_test(test_group_by_prio_mixed, 4), + setup_test(test_group_by_prio_2_groups, 4), + setup_test(test_group_by_prio, 1), + setup_test(test_group_by_prio, 0), + setup_test(test_group_by_prio, _null), + setup_test(test_group_by_prio_mixed_all_marginal, 8), + setup_test(test_group_by_prio_mixed_half_marginal, 8), + setup_test(test_group_by_prio_mixed_one_marginal, 8), + setup_test(test_group_by_node_name_same, 8), + setup_test(test_group_by_node_name_increasing, 8), + setup_test(test_group_by_node_name_3_groups, 8), + setup_test(test_group_by_node_name_2_groups, 8), + setup_test(test_group_by_node_name_3_groups, 4), + setup_test(test_group_by_node_name_2_groups, 4), + setup_test(test_group_by_node_name, 1), + setup_test(test_group_by_node_name, 0), + setup_test(test_group_by_node_name, _null), + setup_test(test_group_by_node_name_2_groups_all_marginal, 8), + setup_test(test_group_by_node_name_2_groups_half_marginal, 8), + setup_test(test_group_by_serial_same, 8), + setup_test(test_group_by_serial_increasing, 8), + setup_test(test_group_by_serial_3_groups, 8), + setup_test(test_group_by_serial_2_groups, 8), + setup_test(test_group_by_serial_3_groups, 4), + setup_test(test_group_by_serial_2_groups, 4), + setup_test(test_group_by_serial, 1), + setup_test(test_group_by_serial, 0), + setup_test(test_group_by_serial, _null), + setup_test(test_group_by_serial_2_groups8_all_marginal, 8), + setup_test(test_group_by_serial_2_groups8_half_marginal, 8), + }; + return cmocka_run_group_tests(tests, setup, NULL); +} + +int main(void) +{ + int ret = 0; + + ret += test_pgpolicies(); + return ret; +}