and consequences ...
- coalesce_paths() and all functions used only in this code path are
folded into configure.c
- move print_*() to libmultipath/print.c, although they are only needed
for multipath(8) (in their current form)
- declare {map,path}_layout as globals in print.c so we can now remove
them from the parameter list in all print.c-exported functions
Now we can use coalesce_path() from multipathd.
structs.o cache.o discovery.o propsel.o dict.o \
pgpolicies.o debug.o regex.o defaults.o uevent.o \
switchgroup.o uxsock.o print.o alias.o log_pthread.o \
- log.o
+ log.o configure.o
CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes
--- /dev/null
+/*
+ * Copyright (c) 2003, 2004, 2005 Christophe Varoqui
+ * Copyright (c) 2005 Benjamin Marzinski, Redhat
+ * Copyright (c) 2005 Kiyoshi Ueda, NEC
+ * Copyright (c) 2005 Patrick Caulfield, Redhat
+ * Copyright (c) 2005 Edward Goggin, EMC
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <libdevmapper.h>
+
+#include "../libcheckers/path_state.h"
+#include "vector.h"
+#include "memory.h"
+#include "devmapper.h"
+#include "blacklist.h"
+#include "defaults.h"
+#include "structs.h"
+#include "dmparser.h"
+#include "config.h"
+#include "propsel.h"
+#include "discovery.h"
+#include "debug.h"
+#include "switchgroup.h"
+#include "print.h"
+#include "configure.h"
+#include "pgpolicies.h"
+#include "dict.h"
+
+static int
+setup_map (struct multipath * mpp)
+{
+ struct pathgroup * pgp;
+ int i;
+
+ /*
+ * don't bother if devmap size is unknown
+ */
+ if (mpp->size <= 0) {
+ condlog(3, "%s: devmap size is unknown", mpp->alias);
+ return 1;
+ }
+
+ /*
+ * properties selectors
+ */
+ select_pgfailback(mpp);
+ select_pgpolicy(mpp);
+ select_selector(mpp);
+ select_features(mpp);
+ select_hwhandler(mpp);
+ select_rr_weight(mpp);
+ select_minio(mpp);
+ select_no_path_retry(mpp);
+
+ /*
+ * assign paths to path groups -- start with no groups and all paths
+ * in mpp->paths
+ */
+ if (mpp->pg) {
+ vector_foreach_slot (mpp->pg, pgp, i)
+ free_pathgroup(pgp, KEEP_PATHS);
+
+ vector_free(mpp->pg);
+ mpp->pg = NULL;
+ }
+ if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp))
+ return 1;
+
+ mpp->nr_active = pathcount(mpp, PATH_UP);
+
+ /*
+ * ponders each path group and determine highest prio pg
+ * to switch over (default to first)
+ */
+ mpp->bestpg = select_path_group(mpp);
+
+ /*
+ * transform the mp->pg vector of vectors of paths
+ * into a mp->params strings to feed the device-mapper
+ */
+ if (assemble_map(mpp)) {
+ condlog(0, "%s: problem assembing map", mpp->alias);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+compute_pgid(struct pathgroup * pgp)
+{
+ struct path * pp;
+ int i;
+
+ vector_foreach_slot (pgp->paths, pp, i)
+ pgp->id ^= (long)pp;
+}
+
+static int
+pgcmp (struct multipath * mpp, struct multipath * cmpp)
+{
+ int i, j;
+ struct pathgroup * pgp;
+ struct pathgroup * cpgp;
+ int r = 0;
+
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ compute_pgid(pgp);
+
+ vector_foreach_slot (cmpp->pg, cpgp, j) {
+ if (pgp->id == cpgp->id) {
+ r = 0;
+ break;
+ }
+ r++;
+ }
+ if (r)
+ return r;
+ }
+ return r;
+}
+
+static void
+select_action (struct multipath * mpp, vector curmp)
+{
+ struct multipath * cmpp;
+
+ cmpp = find_mp_by_alias(curmp, mpp->alias);
+
+ if (!cmpp) {
+ cmpp = find_mp_by_wwid(curmp, mpp->wwid);
+
+ if (cmpp && !conf->dry_run) {
+ condlog(2, "remove: %s (dup of %s)",
+ cmpp->alias, mpp->alias);
+ dm_flush_map(cmpp->alias, DEFAULT_TARGET);
+ }
+ mpp->action = ACT_CREATE;
+ condlog(3, "set ACT_CREATE: map does not exists");
+ return;
+ }
+
+ if (!find_mp_by_wwid(curmp, mpp->wwid)) {
+ condlog(2, "remove: %s (wwid changed)", cmpp->alias);
+ dm_flush_map(mpp->alias, NULL);
+ strncat(cmpp->wwid, mpp->wwid, WWID_SIZE);
+ drop_multipath(curmp, cmpp->wwid, KEEP_PATHS);
+ mpp->action = ACT_CREATE;
+ condlog(3, "set ACT_CREATE: map wwid change");
+ return;
+ }
+
+ if (pathcount(mpp, PATH_UP) == 0) {
+ mpp->action = ACT_NOTHING;
+ condlog(3, "set ACT_NOTHING: no usable path");
+ return;
+ }
+ if (cmpp->size != mpp->size) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: size change");
+ return;
+ }
+ if (!mpp->no_path_retry && /* let features be handled by the daemon */
+ strncmp(cmpp->features, mpp->features, strlen(mpp->features))) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: features change");
+ return;
+ }
+ if (strncmp(cmpp->hwhandler, mpp->hwhandler,
+ strlen(mpp->hwhandler))) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: hwhandler change");
+ return;
+ }
+ if (strncmp(cmpp->selector, mpp->selector,
+ strlen(mpp->selector))) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: selector change");
+ return;
+ }
+ if (cmpp->minio != mpp->minio) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: minio change (%u->%u)",
+ cmpp->minio, mpp->minio);
+ return;
+ }
+ if (VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: number of path group change");
+ return;
+ }
+ if (pgcmp(mpp, cmpp)) {
+ mpp->action = ACT_RELOAD;
+ condlog(3, "set ACT_RELOAD: path group topology change");
+ return;
+ }
+ if (cmpp->nextpg != mpp->bestpg) {
+ mpp->action = ACT_SWITCHPG;
+ condlog(3, "set ACT_SWITCHPG: next path group change");
+ return;
+ }
+ mpp->action = ACT_NOTHING;
+ condlog(3, "set ACT_NOTHING: map unchanged");
+ return;
+}
+
+extern int
+reinstate_paths (struct multipath * mpp)
+{
+ int i, j;
+ struct pathgroup * pgp;
+ struct path * pp;
+
+ if (!mpp->pg)
+ return 0;
+
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ if (!pgp->paths)
+ continue;
+
+ vector_foreach_slot (pgp->paths, pp, j) {
+ if (pp->state != PATH_UP &&
+ (pgp->status == PGSTATE_DISABLED ||
+ pgp->status == PGSTATE_ACTIVE))
+ continue;
+
+ if (pp->dmstate == PSTATE_FAILED) {
+ if (dm_reinstate_path(mpp->alias, pp->dev_t))
+ condlog(0, "error reinstating %s",
+ pp->dev);
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+lock_multipath (struct multipath * mpp, int lock)
+{
+ struct pathgroup * pgp;
+ struct path * pp;
+ int i, j;
+
+ if (!mpp || !mpp->pg)
+ return 0;
+
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ if (!pgp->paths)
+ continue;
+ vector_foreach_slot(pgp->paths, pp, j) {
+ if (lock && flock(pp->fd, LOCK_EX | LOCK_NB) &&
+ errno == EWOULDBLOCK)
+ return 1;
+ else if (!lock)
+ flock(pp->fd, LOCK_UN);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return value:
+ * -1: Retry
+ * 0: DM_DEVICE_CREATE or DM_DEVICE_RELOAD failed, or dry_run mode.
+ * 1: DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded.
+ * 2: Map is already existing.
+ */
+static int
+domap (struct multipath * mpp)
+{
+ int r = 0;
+
+ /*
+ * last chance to quit before touching the devmaps
+ */
+ if (conf->dry_run) {
+ print_mp(mpp, conf->verbosity);
+ return 0;
+ }
+
+ switch (mpp->action) {
+ case ACT_NOTHING:
+ return 2;
+
+ case ACT_SWITCHPG:
+ dm_switchgroup(mpp->alias, mpp->bestpg);
+ /*
+ * we may have avoided reinstating paths because there where in
+ * active or disabled PG. Now that the topology has changed,
+ * retry.
+ */
+ reinstate_paths(mpp);
+ return 2;
+
+ case ACT_CREATE:
+ if (lock_multipath(mpp, 1)) {
+ condlog(3, "%s: in use", mpp->alias);
+ return -1;
+ }
+ dm_shut_log();
+
+ if (dm_map_present(mpp->alias))
+ break;
+
+ r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET,
+ mpp->params, mpp->size, mpp->wwid);
+
+ /*
+ * DM_DEVICE_CREATE is actually DM_DEV_CREATE plus
+ * DM_TABLE_LOAD. Failing the second part leaves an
+ * empty map. Clean it up.
+ */
+ if (!r && dm_map_present(mpp->alias)) {
+ condlog(3, "%s: failed to load map "
+ "(a path might be in use)",
+ mpp->alias);
+ dm_flush_map(mpp->alias, NULL);
+ }
+
+ lock_multipath(mpp, 0);
+ dm_restore_log();
+ break;
+
+ case ACT_RELOAD:
+ r = (dm_addmap(DM_DEVICE_RELOAD, mpp->alias, DEFAULT_TARGET,
+ mpp->params, mpp->size, NULL) &&
+ dm_simplecmd(DM_DEVICE_RESUME, mpp->alias));
+ break;
+
+ default:
+ break;
+ }
+
+ if (r) {
+ /*
+ * DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded
+ */
+ dm_switchgroup(mpp->alias, mpp->bestpg);
+ print_mp(mpp, conf->verbosity);
+ }
+
+ return r;
+}
+
+static int
+deadmap (struct multipath * mpp)
+{
+ int i, j;
+ struct pathgroup * pgp;
+ struct path * pp;
+
+ if (!mpp->pg)
+ return 1;
+
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ if (!pgp->paths)
+ continue;
+
+ vector_foreach_slot (pgp->paths, pp, j)
+ if (strlen(pp->dev))
+ return 0; /* alive */
+ }
+
+ return 1; /* dead */
+}
+
+extern int
+coalesce_paths (vector curmp, vector pathvec)
+{
+ int r = 1;
+ int k, i;
+ char empty_buff[WWID_SIZE];
+ struct multipath * mpp;
+ struct path * pp1;
+ struct path * pp2;
+
+ memset(empty_buff, 0, WWID_SIZE);
+
+ vector_foreach_slot (pathvec, pp1, k) {
+ /* skip this path for some reason */
+
+ /* 1. if path has no unique id or wwid blacklisted */
+ if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
+ blacklist(conf->blist, pp1->wwid))
+ continue;
+
+ /* 2. if path already coalesced */
+ if (pp1->mpp)
+ continue;
+
+ /*
+ * at this point, we know we really got a new mp
+ */
+ mpp = alloc_multipath();
+
+ if (!mpp)
+ return 1;
+
+ mpp->mpe = find_mpe(pp1->wwid);
+ mpp->hwe = pp1->hwe;
+ strcpy(mpp->wwid, pp1->wwid);
+ select_alias(mpp);
+
+ pp1->mpp = mpp;
+ mpp->size = pp1->size;
+ mpp->paths = vector_alloc();
+
+ if (pp1->priority < 0)
+ mpp->action = ACT_NOTHING;
+
+ if (!mpp->paths)
+ return 1;
+
+ if (store_path(mpp->paths, pp1))
+ return 1;
+
+ for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) {
+ pp2 = VECTOR_SLOT(pathvec, i);
+
+ if (strcmp(pp1->wwid, pp2->wwid))
+ continue;
+
+ pp2->mpp = mpp;
+
+ if (pp2->size != mpp->size) {
+ /*
+ * ouch, avoid feeding that to the DM
+ */
+ condlog(3, "path size mismatch : discard %s",
+ mpp->wwid);
+ mpp->action = ACT_NOTHING;
+ }
+ if (pp2->priority < 0)
+ mpp->action = ACT_NOTHING;
+
+ if (store_path(mpp->paths, pp2))
+ return 1;
+ }
+ if (setup_map(mpp))
+ goto next;
+
+ if (mpp->action == ACT_UNDEF)
+ select_action(mpp, curmp);
+
+ r = domap(mpp);
+
+ if (r < 0)
+ return r;
+
+ if (r && mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
+ if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
+ dm_queue_if_no_path(mpp->alias, 0);
+ else
+ dm_queue_if_no_path(mpp->alias, 1);
+ }
+
+next:
+ drop_multipath(curmp, mpp->wwid, KEEP_PATHS);
+ free_multipath(mpp, KEEP_PATHS);
+ }
+ /*
+ * Flush maps with only dead paths (ie not in sysfs)
+ * Keep maps with only failed paths
+ */
+ vector_foreach_slot (curmp, mpp, i) {
+ if (!deadmap(mpp))
+ continue;
+
+ if (dm_flush_map(mpp->alias, DEFAULT_TARGET))
+ condlog(2, "remove: %s (dead) failed!",
+ mpp->alias);
+ else
+ condlog(2, "remove: %s (dead)", mpp->alias);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * configurator actions
+ */
+#define ACT_NOTHING_STR "unchanged"
+#define ACT_RELOAD_STR "reload"
+#define ACT_SWITCHPG_STR "switchpg"
+#define ACT_CREATE_STR "create"
+
+enum actions {
+ ACT_UNDEF,
+ ACT_NOTHING,
+ ACT_RELOAD,
+ ACT_SWITCHPG,
+ ACT_CREATE
+};
+
+#define FLUSH_ONE 1
+#define FLUSH_ALL 2
+
+int reinstate_paths (struct multipath * mpp);
+int coalesce_paths (vector curmp, vector pathvec);
#include "structs.h"
#include "print.h"
#include "dmparser.h"
+#include "configure.h"
+#include "defaults.h"
#include "../libcheckers/path_state.h"
#define MAX(x,y) (x > y) ? x : y
+/* for column aligned output */
+struct path_layout pl;
+struct map_layout ml;
+
void
-get_path_layout (struct path_layout * pl, vector pathvec)
+get_path_layout (vector pathvec)
{
int i;
char buff[MAX_FIELD_LEN];
int prio_len;
/* reset max col lengths */
- pl->uuid_len = 0;
- pl->hbtl_len = 0;
- pl->dev_len = 0;
- pl->dev_t_len = 0;
- pl->prio_len = 0;
+ pl.uuid_len = 0;
+ pl.hbtl_len = 0;
+ pl.dev_len = 0;
+ pl.dev_t_len = 0;
+ pl.prio_len = 0;
vector_foreach_slot (pathvec, pp, i) {
uuid_len = strlen(pp->wwid);
dev_t_len = strlen(pp->dev_t);
prio_len = 1 + (int)log10(pp->priority);
- pl->uuid_len = MAX(uuid_len, pl->uuid_len);
- pl->hbtl_len = MAX(hbtl_len, pl->hbtl_len);
- pl->dev_len = MAX(dev_len, pl->dev_len);
- pl->dev_t_len = MAX(dev_t_len, pl->dev_t_len);
- pl->prio_len = MAX(prio_len, pl->prio_len);
+ pl.uuid_len = MAX(uuid_len, pl.uuid_len);
+ pl.hbtl_len = MAX(hbtl_len, pl.hbtl_len);
+ pl.dev_len = MAX(dev_len, pl.dev_len);
+ pl.dev_t_len = MAX(dev_t_len, pl.dev_t_len);
+ pl.prio_len = MAX(prio_len, pl.prio_len);
}
return;
}
void
-get_map_layout (struct map_layout * ml, vector mpvec)
+get_map_layout (vector mpvec)
{
int i;
char buff[MAX_FIELD_LEN];
int nr_active_len;
/* reset max col lengths */
- ml->mapname_len = 0;
- ml->mapdev_len = 0;
- ml->failback_progress_len = 0;
- ml->queueing_progress_len = 0;
- ml->nr_active_len = 0;
+ ml.mapname_len = 0;
+ ml.mapdev_len = 0;
+ ml.failback_progress_len = 0;
+ ml.queueing_progress_len = 0;
+ ml.nr_active_len = 0;
vector_foreach_slot (mpvec, mpp, i) {
mapname_len = (mpp->alias) ?
queueing_progress_len = 5 + (int)log10(mpp->retry_tick);
nr_active_len = (int)log10(mpp->nr_active);
- ml->mapname_len = MAX(mapname_len, ml->mapname_len);
- ml->mapdev_len = MAX(mapdev_len, ml->mapdev_len);
- ml->failback_progress_len = MAX(failback_progress_len,
- ml->failback_progress_len);
- ml->queueing_progress_len = MAX(queueing_progress_len,
- ml->queueing_progress_len);
- ml->nr_active_len = MAX(nr_active_len, ml->nr_active_len);
+ ml.mapname_len = MAX(mapname_len, ml.mapname_len);
+ ml.mapdev_len = MAX(mapdev_len, ml.mapdev_len);
+ ml.failback_progress_len = MAX(failback_progress_len,
+ ml.failback_progress_len);
+ ml.queueing_progress_len = MAX(queueing_progress_len,
+ ml.queueing_progress_len);
+ ml.nr_active_len = MAX(nr_active_len, ml.nr_active_len);
}
return;
}
PRINT(c, TAIL, " %i/%i", cur, total)
int
-snprint_map_header (char * line, int len, char * format,
- struct map_layout * ml)
+snprint_map_header (char * line, int len, char * format)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
switch (*f) {
case 'w':
PRINT(c, TAIL, "name");
- ml->mapname_len = MAX(ml->mapname_len, 4);
- PAD(ml->mapname_len);
+ ml.mapname_len = MAX(ml.mapname_len, 4);
+ PAD(ml.mapname_len);
break;
case 'd':
PRINT(c, TAIL, "sysfs");
- ml->mapdev_len = MAX(ml->mapdev_len, 5);
- PAD(ml->mapdev_len);
+ ml.mapdev_len = MAX(ml.mapdev_len, 5);
+ PAD(ml.mapdev_len);
break;
case 'F':
PRINT(c, TAIL, "failback");
- ml->failback_progress_len =
- MAX(ml->failback_progress_len, 8);
- PAD(ml->failback_progress_len);
+ ml.failback_progress_len =
+ MAX(ml.failback_progress_len, 8);
+ PAD(ml.failback_progress_len);
break;
case 'Q':
PRINT(c, TAIL, "queueing");
- ml->queueing_progress_len =
- MAX(ml->queueing_progress_len, 8);
- PAD(ml->queueing_progress_len);
+ ml.queueing_progress_len =
+ MAX(ml.queueing_progress_len, 8);
+ PAD(ml.queueing_progress_len);
break;
case 'n':
PRINT(c, TAIL, "paths");
- ml->nr_active_len = MAX(ml->nr_active_len, 5);
- PAD(ml->nr_active_len);
+ ml.nr_active_len = MAX(ml.nr_active_len, 5);
+ PAD(ml.nr_active_len);
break;
case 't':
PRINT(c, TAIL, "dm-st");
int
snprint_map (char * line, int len, char * format,
- struct multipath * mpp, struct map_layout * ml)
+ struct multipath * mpp)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
} else {
PRINT(c, TAIL, "%s", mpp->wwid);
}
- PAD(ml->mapname_len);
+ PAD(ml.mapname_len);
break;
case 'd':
if (mpp->dmi) {
PRINT(c, TAIL, "dm-%i", mpp->dmi->minor);
}
- PAD(ml->mapdev_len);
+ PAD(ml.mapdev_len);
break;
case 'F':
if (mpp->pgfailback == -FAILBACK_IMMEDIATE) {
PRINT(c, TAIL, "immediate");
- PAD(ml->failback_progress_len);
+ PAD(ml.failback_progress_len);
break;
}
if (!mpp->failback_tick) {
PRINT_PROGRESS(mpp->failback_tick,
mpp->pgfailback);
}
- PAD(ml->failback_progress_len);
+ PAD(ml.failback_progress_len);
break;
case 'Q':
if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) {
mpp->no_path_retry);
}
}
- PAD(ml->queueing_progress_len);
+ PAD(ml.queueing_progress_len);
break;
case 'n':
PRINT(c, TAIL, "%i", mpp->nr_active);
- PAD(ml->nr_active_len);
+ PAD(ml.nr_active_len);
break;
case 't':
if (mpp->dmi && mpp->dmi->suspended) {
}
int
-snprint_path_header (char * line, int len, char * format,
- struct path_layout * pl)
+snprint_path_header (char * line, int len, char * format)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
switch (*f) {
case 'w':
PRINT(c, TAIL, "uuid");
- PAD(pl->uuid_len);
+ PAD(pl.uuid_len);
break;
case 'i':
PRINT(c, TAIL, "hcil");
- PAD(pl->hbtl_len);
+ PAD(pl.hbtl_len);
break;
case 'd':
PRINT(c, TAIL, "dev");
- pl->dev_len = MAX(pl->dev_len, 3);
- PAD(pl->dev_len);
+ pl.dev_len = MAX(pl.dev_len, 3);
+ PAD(pl.dev_len);
break;
case 'D':
PRINT(c, TAIL, "dev_t");
- pl->dev_t_len = MAX(pl->dev_t_len, 5);
- PAD(pl->dev_t_len);
+ pl.dev_t_len = MAX(pl.dev_t_len, 5);
+ PAD(pl.dev_t_len);
break;
case 'T':
PRINT(c, TAIL, "chk-st");
break;
case 'p':
PRINT(c, TAIL, "pri");
- pl->prio_len = MAX(pl->prio_len, 3);
- PAD(pl->prio_len);
+ pl.prio_len = MAX(pl.prio_len, 3);
+ PAD(pl.prio_len);
break;
default:
break;
}
int
-snprint_path (char * line, int len, char * format, struct path * pp,
- struct path_layout * pl)
+snprint_path (char * line, int len, char * format, struct path * pp)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
switch (*f) {
case 'w':
PRINT(c, TAIL, "%s", pp->wwid);
- PAD(pl->uuid_len);
+ PAD(pl.uuid_len);
break;
case 'i':
if (pp->sg_id.host_no < 0) {
pp->sg_id.scsi_id,
pp->sg_id.lun);
}
- PAD(pl->hbtl_len);
+ PAD(pl.hbtl_len);
break;
case 'd':
if (!strlen(pp->dev)) {
} else {
PRINT(c, TAIL, "%s", pp->dev);
}
- PAD(pl->dev_len);
+ PAD(pl.dev_len);
break;
case 'D':
PRINT(c, TAIL, "%s", pp->dev_t);
- PAD(pl->dev_t_len);
+ PAD(pl.dev_t_len);
break;
case 'T':
switch (pp->state) {
} else {
PRINT(c, TAIL, "#");
}
- PAD(pl->prio_len);
+ PAD(pl.prio_len);
break;
default:
break;
return (c - line);
}
+extern void
+print_mp (struct multipath * mpp, int verbosity)
+{
+ int j, i;
+ struct path * pp = NULL;
+ struct pathgroup * pgp = NULL;
+
+ if (mpp->action == ACT_NOTHING || !verbosity || !mpp->size)
+ return;
+
+ if (verbosity > 1) {
+ switch (mpp->action) {
+ case ACT_RELOAD:
+ printf("%s: ", ACT_RELOAD_STR);
+ break;
+
+ case ACT_CREATE:
+ printf("%s: ", ACT_CREATE_STR);
+ break;
+
+ case ACT_SWITCHPG:
+ printf("%s: ", ACT_SWITCHPG_STR);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (mpp->alias)
+ printf("%s", mpp->alias);
+
+ if (verbosity == 1) {
+ printf("\n");
+ return;
+ }
+ if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
+ printf(" (%s)", mpp->wwid);
+
+ printf("\n");
+
+ if (mpp->size < (1 << 11))
+ printf("[size=%llu kB]", mpp->size >> 1);
+ else if (mpp->size < (1 << 21))
+ printf("[size=%llu MB]", mpp->size >> 11);
+ else if (mpp->size < (1 << 31))
+ printf("[size=%llu GB]", mpp->size >> 21);
+ else
+ printf("[size=%llu TB]", mpp->size >> 31);
+
+ if (mpp->features)
+ printf("[features=\"%s\"]", mpp->features);
+
+ if (mpp->hwhandler)
+ printf("[hwhandler=\"%s\"]", mpp->hwhandler);
+
+ fprintf(stdout, "\n");
+
+ if (!mpp->pg)
+ return;
+
+ vector_foreach_slot (mpp->pg, pgp, j) {
+ printf("\\_ ");
+
+ if (mpp->selector) {
+ printf("%s ", mpp->selector);
+#if 0
+ /* align to path status info */
+ for (i = pl.hbtl_len + pl.dev_len + pl.dev_t_len + 4;
+ i > strlen(mpp->selector); i--)
+ printf(" ");
+#endif
+ }
+ if (pgp->priority)
+ printf("[prio=%i]", pgp->priority);
+
+ switch (pgp->status) {
+ case PGSTATE_ENABLED:
+ printf("[enabled]");
+ break;
+ case PGSTATE_DISABLED:
+ printf("[disabled]");
+ break;
+ case PGSTATE_ACTIVE:
+ printf("[active]");
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+
+ vector_foreach_slot (pgp->paths, pp, i)
+ print_path(pp, PRINT_PATH_INDENT);
+ }
+ printf("\n");
+}
+
+extern void
+print_path (struct path * pp, char * style)
+{
+ char line[MAX_LINE_LEN];
+
+ snprint_path(&line[0], MAX_LINE_LEN, style, pp);
+ printf("%s", line);
+}
+
+extern void
+print_map (struct multipath * mpp)
+{
+ if (mpp->size && mpp->params)
+ printf("0 %llu %s %s\n",
+ mpp->size, DEFAULT_TARGET, mpp->params);
+ return;
+}
+
+extern void
+print_all_paths (vector pathvec, int banner)
+{
+ int i;
+ struct path * pp;
+ char line[MAX_LINE_LEN];
+
+ if (!VECTOR_SIZE(pathvec)) {
+ if (banner)
+ fprintf(stdout, "===== no paths =====\n");
+ return;
+ }
+
+ if (banner)
+ fprintf(stdout, "===== paths list =====\n");
+
+ get_path_layout(pathvec);
+ snprint_path_header(line, MAX_LINE_LEN, PRINT_PATH_LONG);
+ fprintf(stdout, "%s", line);
+
+ vector_foreach_slot (pathvec, pp, i)
+ print_path(pp, PRINT_PATH_LONG);
+}
+
};
-void get_path_layout (struct path_layout * pl, vector pathvec);
-void get_map_layout (struct map_layout * pl, vector mpvec);
-int snprint_path_header (char *, int, char *, struct path_layout *);
-int snprint_map_header (char *, int, char *, struct map_layout *);
-int snprint_path (char *, int, char *, struct path *, struct path_layout *);
-int snprint_map (char *, int, char *,struct multipath *, struct map_layout *);
+void get_path_layout (vector pathvec);
+void get_map_layout (vector mpvec);
+int snprint_path_header (char *, int, char *);
+int snprint_map_header (char *, int, char *);
+int snprint_path (char *, int, char *, struct path *);
+int snprint_map (char *, int, char *,struct multipath *);
+
+void print_mp (struct multipath * mpp, int verbosity);
+void print_path (struct path * pp, char * style);
+void print_map (struct multipath * mpp);
+void print_all_paths (vector pathvec, int banner);
*/
#include <stdio.h>
-#include <stdlib.h>
#include <unistd.h>
-#include <string.h>
-#include <sys/file.h>
-#include <errno.h>
+#include <ctype.h>
+#include <sysfs/libsysfs.h>
-#include <parser.h>
#include <vector.h>
#include <memory.h>
#include <libdevmapper.h>
#include <devmapper.h>
-#include <checkers.h>
#include <path_state.h>
#include <blacklist.h>
-#include <hwtable.h>
#include <util.h>
#include <defaults.h>
#include <structs.h>
#include <dmparser.h>
#include <cache.h>
#include <config.h>
-#include <propsel.h>
#include <discovery.h>
#include <debug.h>
#include <switchgroup.h>
-#include <sysfs/libsysfs.h>
#include <print.h>
#include <alias.h>
+#include <configure.h>
+#include <pgpolicies.h>
#include "main.h"
-#include "pgpolicies.h"
-#include "dict.h"
-
-/* for column aligned output */
-struct path_layout pl;
static char *
get_refwwid (vector pathvec)
return NULL;
}
-static void
-print_path (struct path * pp, char * style)
-{
- char line[MAX_LINE_LEN];
-
- snprint_path(&line[0], MAX_LINE_LEN, style, pp, &pl);
- printf("%s", line);
-}
-
-static void
-print_map (struct multipath * mpp)
-{
- if (mpp->size && mpp->params)
- printf("0 %llu %s %s\n",
- mpp->size, DEFAULT_TARGET, mpp->params);
- return;
-}
-
-static void
-print_all_paths (vector pathvec, int banner)
-{
- int i;
- struct path * pp;
- char line[MAX_LINE_LEN];
-
- if (!VECTOR_SIZE(pathvec)) {
- if (banner)
- fprintf(stdout, "===== no paths =====\n");
- return;
- }
-
- if (banner)
- fprintf(stdout, "===== paths list =====\n");
-
- get_path_layout(&pl, pathvec);
- snprint_path_header(line, MAX_LINE_LEN, PRINT_PATH_LONG, &pl);
- fprintf(stdout, "%s", line);
-
- vector_foreach_slot (pathvec, pp, i)
- print_path(pp, PRINT_PATH_LONG);
-}
-
-static void
-print_mp (struct multipath * mpp)
-{
- int j, i;
- struct path * pp = NULL;
- struct pathgroup * pgp = NULL;
-
- if (mpp->action == ACT_NOTHING || !conf->verbosity || !mpp->size)
- return;
-
- if (conf->verbosity > 1) {
- switch (mpp->action) {
- case ACT_RELOAD:
- printf("%s: ", ACT_RELOAD_STR);
- break;
-
- case ACT_CREATE:
- printf("%s: ", ACT_CREATE_STR);
- break;
-
- case ACT_SWITCHPG:
- printf("%s: ", ACT_SWITCHPG_STR);
- break;
-
- default:
- break;
- }
- }
-
- if (mpp->alias)
- printf("%s", mpp->alias);
-
- if (conf->verbosity == 1) {
- printf("\n");
- return;
- }
- if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
- printf(" (%s)", mpp->wwid);
-
- printf("\n");
-
- if (mpp->size < (1 << 11))
- printf("[size=%llu kB]", mpp->size >> 1);
- else if (mpp->size < (1 << 21))
- printf("[size=%llu MB]", mpp->size >> 11);
- else if (mpp->size < (1 << 31))
- printf("[size=%llu GB]", mpp->size >> 21);
- else
- printf("[size=%llu TB]", mpp->size >> 31);
-
- if (mpp->features)
- printf("[features=\"%s\"]", mpp->features);
-
- if (mpp->hwhandler)
- printf("[hwhandler=\"%s\"]", mpp->hwhandler);
-
- fprintf(stdout, "\n");
-
- if (!mpp->pg)
- return;
-
- vector_foreach_slot (mpp->pg, pgp, j) {
- printf("\\_ ");
-
- if (mpp->selector) {
- printf("%s ", mpp->selector);
-#if 0
- /* align to path status info */
- for (i = pl.hbtl_len + pl.dev_len + pl.dev_t_len + 4;
- i > strlen(mpp->selector); i--)
- printf(" ");
-#endif
- }
- if (pgp->priority)
- printf("[prio=%i]", pgp->priority);
-
- switch (pgp->status) {
- case PGSTATE_ENABLED:
- printf("[enabled]");
- break;
- case PGSTATE_DISABLED:
- printf("[disabled]");
- break;
- case PGSTATE_ACTIVE:
- printf("[active]");
- break;
- default:
- break;
- }
- printf("\n");
-
- vector_foreach_slot (pgp->paths, pp, i)
- print_path(pp, PRINT_PATH_INDENT);
- }
- printf("\n");
-}
-
static int
filter_pathvec (vector pathvec, char * refwwid)
{
return 0;
}
-static int
-setup_map (struct multipath * mpp)
-{
- struct pathgroup * pgp;
- int i;
-
- /*
- * don't bother if devmap size is unknown
- */
- if (mpp->size <= 0) {
- condlog(3, "%s: devmap size is unknown", mpp->alias);
- return 1;
- }
-
- /*
- * properties selectors
- */
- select_pgfailback(mpp);
- select_pgpolicy(mpp);
- select_selector(mpp);
- select_features(mpp);
- select_hwhandler(mpp);
- select_rr_weight(mpp);
- select_minio(mpp);
- select_no_path_retry(mpp);
-
- /*
- * assign paths to path groups -- start with no groups and all paths
- * in mpp->paths
- */
- if (mpp->pg) {
- vector_foreach_slot (mpp->pg, pgp, i)
- free_pathgroup(pgp, KEEP_PATHS);
-
- vector_free(mpp->pg);
- mpp->pg = NULL;
- }
- if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp))
- return 1;
-
- mpp->nr_active = pathcount(mpp, PATH_UP);
-
- /*
- * ponders each path group and determine highest prio pg
- * to switch over (default to first)
- */
- mpp->bestpg = select_path_group(mpp);
-
- /*
- * transform the mp->pg vector of vectors of paths
- * into a mp->params strings to feed the device-mapper
- */
- if (assemble_map(mpp)) {
- condlog(0, "%s: problem assembing map", mpp->alias);
- return 1;
- }
- return 0;
-}
-
-static void
-compute_pgid(struct pathgroup * pgp)
-{
- struct path * pp;
- int i;
-
- vector_foreach_slot (pgp->paths, pp, i)
- pgp->id ^= (long)pp;
-}
-
-static int
-pgcmp (struct multipath * mpp, struct multipath * cmpp)
-{
- int i, j;
- struct pathgroup * pgp;
- struct pathgroup * cpgp;
- int r = 0;
-
- vector_foreach_slot (mpp->pg, pgp, i) {
- compute_pgid(pgp);
-
- vector_foreach_slot (cmpp->pg, cpgp, j) {
- if (pgp->id == cpgp->id) {
- r = 0;
- break;
- }
- r++;
- }
- if (r)
- return r;
- }
- return r;
-}
-
-static void
-select_action (struct multipath * mpp, vector curmp)
-{
- struct multipath * cmpp;
-
- cmpp = find_mp_by_alias(curmp, mpp->alias);
-
- if (!cmpp) {
- cmpp = find_mp_by_wwid(curmp, mpp->wwid);
-
- if (cmpp && !conf->dry_run) {
- condlog(2, "remove: %s (dup of %s)",
- cmpp->alias, mpp->alias);
- dm_flush_map(cmpp->alias, DEFAULT_TARGET);
- }
- mpp->action = ACT_CREATE;
- condlog(3, "set ACT_CREATE: map does not exists");
- return;
- }
-
- if (!find_mp_by_wwid(curmp, mpp->wwid)) {
- condlog(2, "remove: %s (wwid changed)", cmpp->alias);
- dm_flush_map(mpp->alias, NULL);
- strncat(cmpp->wwid, mpp->wwid, WWID_SIZE);
- drop_multipath(curmp, cmpp->wwid, KEEP_PATHS);
- mpp->action = ACT_CREATE;
- condlog(3, "set ACT_CREATE: map wwid change");
- return;
- }
-
- if (pathcount(mpp, PATH_UP) == 0) {
- mpp->action = ACT_NOTHING;
- condlog(3, "set ACT_NOTHING: no usable path");
- return;
- }
- if (cmpp->size != mpp->size) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: size change");
- return;
- }
- if (!mpp->no_path_retry && /* let features be handled by the daemon */
- strncmp(cmpp->features, mpp->features, strlen(mpp->features))) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: features change");
- return;
- }
- if (strncmp(cmpp->hwhandler, mpp->hwhandler,
- strlen(mpp->hwhandler))) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: hwhandler change");
- return;
- }
- if (strncmp(cmpp->selector, mpp->selector,
- strlen(mpp->selector))) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: selector change");
- return;
- }
- if (cmpp->minio != mpp->minio) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: minio change (%u->%u)",
- cmpp->minio, mpp->minio);
- return;
- }
- if (VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: number of path group change");
- return;
- }
- if (pgcmp(mpp, cmpp)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "set ACT_RELOAD: path group topology change");
- return;
- }
- if (cmpp->nextpg != mpp->bestpg) {
- mpp->action = ACT_SWITCHPG;
- condlog(3, "set ACT_SWITCHPG: next path group change");
- return;
- }
- mpp->action = ACT_NOTHING;
- condlog(3, "set ACT_NOTHING: map unchanged");
- return;
-}
-
-static int
-reinstate_paths (struct multipath * mpp)
-{
- int i, j;
- struct pathgroup * pgp;
- struct path * pp;
-
- if (!mpp->pg)
- return 0;
-
- vector_foreach_slot (mpp->pg, pgp, i) {
- if (!pgp->paths)
- continue;
-
- vector_foreach_slot (pgp->paths, pp, j) {
- if (pp->state != PATH_UP &&
- (pgp->status == PGSTATE_DISABLED ||
- pgp->status == PGSTATE_ACTIVE))
- continue;
-
- if (pp->dmstate == PSTATE_FAILED) {
- if (dm_reinstate_path(mpp->alias, pp->dev_t))
- condlog(0, "error reinstating %s",
- pp->dev);
- }
- }
- }
- return 0;
-}
-
-int lock_multipath (struct multipath * mpp, int lock)
-{
- struct pathgroup * pgp;
- struct path * pp;
- int i, j;
-
- if (!mpp || !mpp->pg)
- return 0;
-
- vector_foreach_slot (mpp->pg, pgp, i) {
- if (!pgp->paths)
- continue;
- vector_foreach_slot(pgp->paths, pp, j) {
- if (lock && flock(pp->fd, LOCK_EX | LOCK_NB) &&
- errno == EWOULDBLOCK)
- return 1;
- else if (!lock)
- flock(pp->fd, LOCK_UN);
- }
- }
- return 0;
-}
-
-/*
- * Return value:
- * -1: Retry
- * 0: DM_DEVICE_CREATE or DM_DEVICE_RELOAD failed, or dry_run mode.
- * 1: DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded.
- * 2: Map is already existing.
- */
-static int
-domap (struct multipath * mpp)
-{
- int r = 0;
-
- /*
- * last chance to quit before touching the devmaps
- */
- if (conf->dry_run) {
- print_mp(mpp);
- return 0;
- }
-
- switch (mpp->action) {
- case ACT_NOTHING:
- return 2;
-
- case ACT_SWITCHPG:
- dm_switchgroup(mpp->alias, mpp->bestpg);
- /*
- * we may have avoided reinstating paths because there where in
- * active or disabled PG. Now that the topology has changed,
- * retry.
- */
- reinstate_paths(mpp);
- return 2;
-
- case ACT_CREATE:
- if (lock_multipath(mpp, 1)) {
- condlog(3, "%s: in use", mpp->alias);
- return -1;
- }
- dm_shut_log();
-
- if (dm_map_present(mpp->alias))
- break;
-
- r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET,
- mpp->params, mpp->size, mpp->wwid);
-
- /*
- * DM_DEVICE_CREATE is actually DM_DEV_CREATE plus
- * DM_TABLE_LOAD. Failing the second part leaves an
- * empty map. Clean it up.
- */
- if (!r && dm_map_present(mpp->alias)) {
- condlog(3, "%s: failed to load map "
- "(a path might be in use)",
- mpp->alias);
- dm_flush_map(mpp->alias, NULL);
- }
-
- lock_multipath(mpp, 0);
- dm_restore_log();
- break;
-
- case ACT_RELOAD:
- r = (dm_addmap(DM_DEVICE_RELOAD, mpp->alias, DEFAULT_TARGET,
- mpp->params, mpp->size, NULL) &&
- dm_simplecmd(DM_DEVICE_RESUME, mpp->alias));
- break;
-
- default:
- break;
- }
-
- if (r) {
- /*
- * DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded
- */
- dm_switchgroup(mpp->alias, mpp->bestpg);
- print_mp(mpp);
- }
-
- return r;
-}
-
-static int
-deadmap (struct multipath * mpp)
-{
- int i, j;
- struct pathgroup * pgp;
- struct path * pp;
-
- if (!mpp->pg)
- return 1;
-
- vector_foreach_slot (mpp->pg, pgp, i) {
- if (!pgp->paths)
- continue;
-
- vector_foreach_slot (pgp->paths, pp, j)
- if (strlen(pp->dev))
- return 0; /* alive */
- }
-
- return 1; /* dead */
-}
-
-static int
-coalesce_paths (vector curmp, vector pathvec)
-{
- int r = 1;
- int k, i;
- char empty_buff[WWID_SIZE];
- struct multipath * mpp;
- struct path * pp1;
- struct path * pp2;
-
- memset(empty_buff, 0, WWID_SIZE);
-
- vector_foreach_slot (pathvec, pp1, k) {
- /* skip this path for some reason */
-
- /* 1. if path has no unique id or wwid blacklisted */
- if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
- blacklist(conf->blist, pp1->wwid))
- continue;
-
- /* 2. if path already coalesced */
- if (pp1->mpp)
- continue;
-
- /*
- * at this point, we know we really got a new mp
- */
- mpp = alloc_multipath();
-
- if (!mpp)
- return 1;
-
- mpp->mpe = find_mpe(pp1->wwid);
- mpp->hwe = pp1->hwe;
- strcpy(mpp->wwid, pp1->wwid);
- select_alias(mpp);
-
- pp1->mpp = mpp;
- mpp->size = pp1->size;
- mpp->paths = vector_alloc();
-
- if (pp1->priority < 0)
- mpp->action = ACT_NOTHING;
-
- if (!mpp->paths)
- return 1;
-
- if (store_path(mpp->paths, pp1))
- return 1;
-
- for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) {
- pp2 = VECTOR_SLOT(pathvec, i);
-
- if (strcmp(pp1->wwid, pp2->wwid))
- continue;
-
- pp2->mpp = mpp;
-
- if (pp2->size != mpp->size) {
- /*
- * ouch, avoid feeding that to the DM
- */
- condlog(3, "path size mismatch : discard %s",
- mpp->wwid);
- mpp->action = ACT_NOTHING;
- }
- if (pp2->priority < 0)
- mpp->action = ACT_NOTHING;
-
- if (store_path(mpp->paths, pp2))
- return 1;
- }
- if (setup_map(mpp))
- goto next;
-
- if (mpp->action == ACT_UNDEF)
- select_action(mpp, curmp);
-
- r = domap(mpp);
-
- if (r < 0)
- return r;
-
- if (r && mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
- if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
- dm_queue_if_no_path(mpp->alias, 0);
- else
- dm_queue_if_no_path(mpp->alias, 1);
- }
-
-next:
- drop_multipath(curmp, mpp->wwid, KEEP_PATHS);
- free_multipath(mpp, KEEP_PATHS);
- }
- /*
- * Flush maps with only dead paths (ie not in sysfs)
- * Keep maps with only failed paths
- */
- vector_foreach_slot (curmp, mpp, i) {
- if (!deadmap(mpp))
- continue;
-
- if (dm_flush_map(mpp->alias, DEFAULT_TARGET))
- condlog(2, "remove: %s (dead) failed!",
- mpp->alias);
- else
- condlog(2, "remove: %s (dead)", mpp->alias);
- }
- return 0;
-}
-
static void
usage (char * progname)
{
disassemble_status(mpp->status, mpp);
if (conf->list)
- print_mp(mpp);
+ print_mp(mpp, conf->verbosity);
if (!conf->dry_run)
reinstate_paths(mpp);
if (conf->verbosity > 2)
print_all_paths(pathvec, 1);
- get_path_layout(&pl, pathvec);
+ get_path_layout(pathvec);
if (get_dm_mpvec(curmp, pathvec, refwwid))
goto out;
#define _MAIN_H
/*
- * configurator actions
- */
-#define ACT_NOTHING_STR "unchanged"
-#define ACT_RELOAD_STR "reload"
-#define ACT_SWITCHPG_STR "switchpg"
-#define ACT_CREATE_STR "create"
-
-enum actions {
- ACT_UNDEF,
- ACT_NOTHING,
- ACT_RELOAD,
- ACT_SWITCHPG,
- ACT_CREATE
-};
-
-#define FLUSH_ONE 1
-#define FLUSH_ALL 2
-
-/*
* Build version
*/
#define PROG "multipath"
struct path * pp;
char * c;
char * reply;
- struct path_layout pl;
int maxlen = INITIAL_REPLY_LEN;
int again = 1;
- get_path_layout(&pl, vecs->pathvec);
+ get_path_layout(vecs->pathvec);
reply = MALLOC(maxlen);
while (again) {
if (VECTOR_SIZE(vecs->pathvec) > 0)
c += snprint_path_header(c, reply + maxlen - c,
- PRINT_PATH_CHECKER, &pl);
+ PRINT_PATH_CHECKER);
vector_foreach_slot(vecs->pathvec, pp, i)
c += snprint_path(c, reply + maxlen - c,
- PRINT_PATH_CHECKER, pp, &pl);
+ PRINT_PATH_CHECKER, pp);
again = ((c - reply) == (maxlen - 1));
struct multipath * mpp;
char * c;
char * reply;
- struct map_layout ml;
int maxlen = INITIAL_REPLY_LEN;
int again = 1;
- get_map_layout(&ml, vecs->mpvec);
+ get_map_layout(vecs->mpvec);
reply = MALLOC(maxlen);
while (again) {
c = reply;
if (VECTOR_SIZE(vecs->mpvec) > 0)
c += snprint_map_header(c, reply + maxlen - c,
- PRINT_MAP_FAILBACK, &ml);
+ PRINT_MAP_FAILBACK);
vector_foreach_slot(vecs->mpvec, mpp, i)
c += snprint_map(c, reply + maxlen - c,
- PRINT_MAP_FAILBACK, mpp, &ml);
+ PRINT_MAP_FAILBACK, mpp);
again = ((c - reply) == (maxlen - 1));