2 * Soft: multipath device mapper target autoconfig
4 * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
6 * Author: Copyright (C) 2003 Christophe Varoqui
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 * See the GNU General Public License for more details.
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version
16 * 2 of the License, or (at your option) any later version.
27 #include <libdevmapper.h>
28 #include <devmapper.h>
30 #include <path_state.h>
31 #include <blacklist.h>
40 #include <discovery.h>
42 #include <switchgroup.h>
43 #include <sysfs/libsysfs.h>
47 #include "pgpolicies.h"
50 /* for column aligned output */
51 struct path_layout pl;
54 get_refwwid (vector pathvec)
57 char buff[FILE_NAME_SIZE];
60 if (conf->dev_type == DEV_NONE)
63 if (conf->dev_type == DEV_DEVNODE) {
64 condlog(3, "limited scope = %s", conf->dev);
65 basename(conf->dev, buff);
66 pp = find_path_by_dev(pathvec, buff);
74 strncpy(pp->dev, buff, FILE_NAME_SIZE);
76 if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
79 if (store_path(pathvec, pp)) {
85 refwwid = MALLOC(WWID_SIZE);
90 memcpy(refwwid, pp->wwid, WWID_SIZE);
94 if (conf->dev_type == DEV_DEVT) {
95 condlog(3, "limited scope = %s", conf->dev);
96 pp = find_path_by_devt(pathvec, conf->dev);
99 if (devt2devname(conf->dev, buff))
107 if(safe_sprintf(pp->dev, "%s", buff)) {
108 fprintf(stderr, "pp->dev too small\n");
111 if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
114 if (store_path(pathvec, pp)) {
120 refwwid = MALLOC(WWID_SIZE);
125 memcpy(refwwid, pp->wwid, WWID_SIZE);
128 if (conf->dev_type == DEV_DEVMAP) {
129 condlog(3, "limited scope = %s", conf->dev);
133 refwwid = get_mpe_wwid(conf->dev);
141 refwwid = MALLOC(WWID_SIZE);
146 strncpy(refwwid, conf->dev, WWID_SIZE);
153 print_path (struct path * pp, char * style)
155 char buff[MAX_LINE_LEN];
157 snprint_path(&buff[0], MAX_LINE_LEN, style, pp, &pl);
162 print_map (struct multipath * mpp)
164 if (mpp->size && mpp->params)
165 printf("0 %llu %s %s\n",
166 mpp->size, DEFAULT_TARGET, mpp->params);
171 print_all_paths (vector pathvec)
176 vector_foreach_slot (pathvec, pp, i)
177 print_path(pp, PRINT_PATH_LONG);
181 print_mp (struct multipath * mpp)
184 struct path * pp = NULL;
185 struct pathgroup * pgp = NULL;
187 if (mpp->action == ACT_NOTHING || conf->verbosity == 0)
190 if (conf->verbosity > 1) {
191 switch (mpp->action) {
193 printf("%s: ", ACT_RELOAD_STR);
197 printf("%s: ", ACT_CREATE_STR);
201 printf("%s: ", ACT_SWITCHPG_STR);
210 printf("%s", mpp->alias);
212 if (conf->verbosity == 1) {
216 if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
217 printf(" (%s)", mpp->wwid);
221 if (mpp->size < (1 << 11))
222 printf("[size=%llu kB]", mpp->size >> 1);
223 else if (mpp->size < (1 << 21))
224 printf("[size=%llu MB]", mpp->size >> 11);
225 else if (mpp->size < (1 << 31))
226 printf("[size=%llu GB]", mpp->size >> 21);
228 printf("[size=%llu TB]", mpp->size >> 31);
231 printf("[features=\"%s\"]", mpp->features);
234 printf("[hwhandler=\"%s\"]", mpp->hwhandler);
236 fprintf(stdout, "\n");
241 vector_foreach_slot (mpp->pg, pgp, j) {
245 printf("%s ", mpp->selector);
247 /* align to path status info */
248 for (i = pl.hbtl_len + pl.dev_len + pl.dev_t_len + 4;
249 i > strlen(mpp->selector); i--)
254 printf("[prio=%i]", pgp->priority);
256 switch (pgp->status) {
257 case PGSTATE_ENABLED:
260 case PGSTATE_DISABLED:
261 printf("[disabled]");
271 vector_foreach_slot (pgp->paths, pp, i)
272 print_path(pp, PRINT_PATH_INDENT);
278 filter_pathvec (vector pathvec, char * refwwid)
283 if (!refwwid || !strlen(refwwid))
286 vector_foreach_slot (pathvec, pp, i) {
287 if (memcmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
288 condlog(3, "skip path %s : out of scope", pp->dev);
290 vector_del_slot(pathvec, i);
298 * Transforms the path group vector into a proper device map string
301 assemble_map (struct multipath * mp)
307 struct pathgroup * pgp;
311 freechar = sizeof(mp->params);
313 shift = snprintf(p, freechar, "%s %s %i %i",
314 mp->features, mp->hwhandler,
315 VECTOR_SIZE(mp->pg), mp->nextpg);
317 if (shift >= freechar) {
318 fprintf(stderr, "mp->params too small\n");
324 vector_foreach_slot (mp->pg, pgp, i) {
325 pgp = VECTOR_SLOT(mp->pg, i);
326 shift = snprintf(p, freechar, " %s %i 1", mp->selector,
327 VECTOR_SIZE(pgp->paths));
328 if (shift >= freechar) {
329 fprintf(stderr, "mp->params too small\n");
335 vector_foreach_slot (pgp->paths, pp, j) {
338 if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority)
339 minio *= pp->priority;
341 shift = snprintf(p, freechar, " %s %d",
343 if (shift >= freechar) {
344 fprintf(stderr, "mp->params too small\n");
352 fprintf(stderr, "mp->params too small\n");
355 snprintf(p, 1, "\n");
357 if (conf->verbosity > 2)
364 setup_map (struct multipath * mpp)
370 * don't bother if devmap size is unknown
372 if (mpp->size <= 0) {
373 condlog(3, "%s devmap size is unknown", mpp->alias);
378 * don't bother if a constituant path is claimed
379 * (not by the device mapper driver)
381 vector_foreach_slot (mpp->paths, pp, i) {
382 if (pp->claimed && pp->dmstate == PSTATE_UNDEF) {
383 condlog(3, "%s claimed", pp->dev);
389 * properties selectors
391 select_pgpolicy(mpp);
392 select_selector(mpp);
393 select_features(mpp);
394 select_hwhandler(mpp);
395 select_rr_weight(mpp);
396 select_no_path_retry(mpp);
399 * apply selected grouping policy to valid paths
401 switch (mpp->pgpolicy) {
406 one_path_per_group(mpp);
408 case GROUP_BY_SERIAL:
409 group_by_serial(mpp);
414 case GROUP_BY_NODE_NAME:
415 group_by_node_name(mpp);
421 if (mpp->pg == NULL) {
422 condlog(3, "pgpolicy failed to produce a pg vector");
427 * ponders each path group and determine highest prio pg
428 * to switch over (default to first)
430 select_path_group(mpp);
433 * transform the mp->pg vector of vectors of paths
434 * into a mp->params strings to feed the device-mapper
436 if (assemble_map(mpp)) {
437 condlog(3, "problem assembing map");
444 pathcount (struct multipath * mpp, int state)
446 struct pathgroup *pgp;
451 vector_foreach_slot (mpp->pg, pgp, i)
452 vector_foreach_slot (pgp->paths, pp, j)
453 if (pp->state == state)
459 compute_pgid(struct pathgroup * pgp)
464 vector_foreach_slot (pgp->paths, pp, i)
469 pgcmp (struct multipath * mpp, struct multipath * cmpp)
472 struct pathgroup * pgp;
473 struct pathgroup * cpgp;
476 vector_foreach_slot (mpp->pg, pgp, i) {
479 vector_foreach_slot (cmpp->pg, cpgp, j) {
480 if (pgp->id == cpgp->id) {
493 select_action (struct multipath * mpp, vector curmp)
495 struct multipath * cmpp;
497 cmpp = find_mp(curmp, mpp->alias);
500 cmpp = find_mp_by_wwid(curmp, mpp->wwid);
502 if (cmpp && !conf->dry_run) {
503 condlog(2, "remove: %s (dup of %s)",
504 cmpp->alias, mpp->alias);
505 dm_flush_map(cmpp->alias, DEFAULT_TARGET);
507 mpp->action = ACT_CREATE;
511 if (!find_mp_by_wwid(curmp, mpp->wwid)) {
512 dm_flush_map(mpp->alias, NULL);
513 strncat(cmpp->wwid, mpp->wwid, WWID_SIZE);
514 drop_multipath(curmp, cmpp->wwid, KEEP_PATHS);
515 mpp->action = ACT_CREATE;
519 if (pathcount(mpp, PATH_UP) == 0) {
520 condlog(3, "no good path");
521 mpp->action = ACT_NOTHING;
524 if (cmpp->size != mpp->size) {
525 condlog(3, "size different than current");
526 mpp->action = ACT_RELOAD;
529 if (strncmp(cmpp->features, mpp->features,
530 strlen(mpp->features))) {
531 condlog(3, "features different than current");
532 mpp->action = ACT_RELOAD;
535 if (strncmp(cmpp->hwhandler, mpp->hwhandler,
536 strlen(mpp->hwhandler))) {
537 condlog(3, "hwhandler different than current");
538 mpp->action = ACT_RELOAD;
541 if (strncmp(cmpp->selector, mpp->selector,
542 strlen(mpp->selector))) {
543 condlog(3, "selector different than current");
544 mpp->action = ACT_RELOAD;
547 if (VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
548 condlog(3, "different number of PG");
549 mpp->action = ACT_RELOAD;
552 if (pgcmp(mpp, cmpp)) {
553 condlog(3, "different path group topology");
554 mpp->action = ACT_RELOAD;
557 if (cmpp->nextpg != mpp->nextpg) {
558 condlog(3, "nextpg different than current");
559 mpp->action = ACT_SWITCHPG;
562 mpp->action = ACT_NOTHING;
567 reinstate_paths (struct multipath * mpp)
570 struct pathgroup * pgp;
576 vector_foreach_slot (mpp->pg, pgp, i) {
580 vector_foreach_slot (pgp->paths, pp, j) {
581 if (pp->state != PATH_UP &&
582 (pgp->status == PGSTATE_DISABLED ||
583 pgp->status == PGSTATE_ACTIVE))
586 if (pp->dmstate == PSTATE_FAILED) {
587 if (dm_reinstate(mpp->alias, pp->dev_t))
588 condlog(0, "error reinstating %s",
598 * 0: DM_DEVICE_CREATE or DM_DEVICE_RELOAD failed, or dry_run mode.
599 * 1: DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded.
600 * 2: Map is already existing.
603 domap (struct multipath * mpp)
610 * last chance to quit before touching the devmaps
615 switch (mpp->action) {
620 dm_switchgroup(mpp->alias, mpp->nextpg);
622 * we may have avoided reinstating paths because there where in
623 * active or disabled PG. Now that the topology has changed,
626 reinstate_paths(mpp);
630 r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET,
631 mpp->params, mpp->size, mpp->wwid);
635 r = (dm_addmap(DM_DEVICE_RELOAD, mpp->alias, DEFAULT_TARGET,
636 mpp->params, mpp->size, NULL) &&
637 dm_simplecmd(DM_DEVICE_RESUME, mpp->alias));
646 * DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded
648 dm_switchgroup(mpp->alias, mpp->nextpg);
654 deadmap (struct multipath * mpp)
657 struct pathgroup * pgp;
663 vector_foreach_slot (mpp->pg, pgp, i) {
667 vector_foreach_slot (pgp->paths, pp, j)
669 return 0; /* alive */
676 coalesce_paths (vector curmp, vector pathvec)
679 char empty_buff[WWID_SIZE];
680 struct multipath * mpp;
684 memset(empty_buff, 0, WWID_SIZE);
686 vector_foreach_slot (pathvec, pp1, k) {
687 /* skip this path for some reason */
689 /* 1. if path has no unique id or wwid blacklisted */
690 if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
691 blacklist(conf->blist, pp1->wwid))
694 /* 2. if path already coalesced */
699 * at this point, we know we really got a new mp
701 mpp = alloc_multipath();
706 mpp->mpe = find_mpe(pp1->wwid);
711 strcpy(mpp->wwid, pp1->wwid);
712 mpp->size = pp1->size;
713 mpp->paths = vector_alloc();
715 if (pp1->priority < 0)
716 mpp->action = ACT_NOTHING;
721 if (store_path(mpp->paths, pp1))
724 for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) {
725 pp2 = VECTOR_SLOT(pathvec, i);
727 if (strcmp(pp1->wwid, pp2->wwid))
732 if (pp2->size != mpp->size) {
734 * ouch, avoid feeding that to the DM
736 condlog(3, "path size mismatch : discard %s",
738 mpp->action = ACT_NOTHING;
740 if (pp2->priority < 0)
741 mpp->action = ACT_NOTHING;
743 if (store_path(mpp->paths, pp2))
749 condlog(3, "action preset to %i", mpp->action);
751 if (mpp->action == ACT_UNDEF)
752 select_action(mpp, curmp);
754 condlog(3, "action set to %i", mpp->action);
756 if (domap(mpp) && mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
757 if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
758 dm_queue_if_no_path(mpp->alias, 0);
760 dm_queue_if_no_path(mpp->alias, 1);
764 drop_multipath(curmp, mpp->wwid, KEEP_PATHS);
765 free_multipath(mpp, KEEP_PATHS);
768 * Flush maps with only dead paths (ie not in sysfs)
769 * Keep maps with only failed paths
771 vector_foreach_slot (curmp, mpp, i) {
775 if (dm_flush_map(mpp->alias, DEFAULT_TARGET))
776 condlog(2, "remove: %s (dead) failed!",
779 condlog(2, "remove: %s (dead)", mpp->alias);
785 usage (char * progname)
787 fprintf (stderr, VERSION_STRING);
788 fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n",
791 "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \
794 "\t-v level\tverbosty level\n" \
795 "\t 0\t\t\tno output\n" \
796 "\t 1\t\t\tprint created devmap names only\n" \
797 "\t 2\t\t\tdefault verbosity\n" \
798 "\t 3\t\t\tprint debug information\n" \
799 "\t-d\t\tdry run, do not create or update devmaps\n" \
800 "\t-l\t\tshow multipath topology (sysfs and DM info)\n" \
801 "\t-ll\t\tshow multipath topology (maximum info)\n" \
802 "\t-F\t\tflush a multipath device map\n" \
803 "\t-F\t\tflush all multipath device maps\n" \
804 "\t-p policy\tforce all maps to specified policy :\n" \
805 "\t failover\t\t1 path per priority group\n" \
806 "\t multibus\t\tall paths in 1 priority group\n" \
807 "\t group_by_serial\t1 priority group per serial\n" \
808 "\t group_by_prio\t1 priority group per priority lvl\n" \
809 "\t group_by_node_name\t1 priority group per target node\n" \
811 "\tdevice\t\tlimit scope to the device's multipath\n" \
812 "\t\t\t(udev-style $DEVNAME reference, eg /dev/sdb\n" \
813 "\t\t\tor major:minor or a device map name)\n" \
820 update_paths (struct multipath * mpp)
823 struct pathgroup * pgp;
829 vector_foreach_slot (mpp->pg, pgp, i) {
833 vector_foreach_slot (pgp->paths, pp, j) {
834 if (!strlen(pp->dev)) {
835 if (devt2devname(pp->dev_t, pp->dev)) {
837 * path is not in sysfs anymore
839 pp->state = PATH_DOWN;
842 pathinfo(pp, conf->hwtable,
843 DI_SYSFS | DI_CHECKER | \
844 DI_SERIAL | DI_PRIO);
847 if (pp->state == PATH_UNCHECKED)
848 pathinfo(pp, conf->hwtable, DI_CHECKER);
851 pathinfo(pp, conf->hwtable, DI_PRIO);
858 get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
861 struct multipath * mpp;
863 if (dm_get_maps(curmp, DEFAULT_TARGET))
866 vector_foreach_slot (curmp, mpp, i) {
868 * discard out of scope maps
870 if (mpp->wwid && refwwid &&
871 strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
872 condlog(3, "skip map %s: out of scope", mpp->alias);
873 free_multipath(mpp, KEEP_PATHS);
874 vector_del_slot(curmp, i);
879 condlog(3, "params = %s", mpp->params);
880 condlog(3, "status = %s", mpp->status);
882 disassemble_map(pathvec, mpp->params, mpp);
885 * disassemble_map() can add new paths to pathvec.
886 * If not in "fast list mode", we need to fetch information
893 select_path_group(mpp);
895 disassemble_status(mpp->status, mpp);
901 reinstate_paths(mpp);
907 main (int argc, char *argv[])
910 vector pathvec = NULL;
916 char * refwwid = NULL;
919 fprintf(stderr, "need to be root\n");
923 if (dm_prereq(DEFAULT_TARGET, 1, 0, 3))
926 if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
927 condlog(0, "multipath tools need sysfs mounted");
930 if (load_config(DEFAULT_CONFIGFILE))
933 while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:")) != EOF ) {
935 case 1: printf("optarg : %s\n",optarg);
938 if (sizeof(optarg) > sizeof(char *) ||
942 conf->verbosity = atoi(optarg);
957 if (optarg && !strncmp(optarg, "l", 1))
963 debug = atoi(optarg);
967 conf->pgpolicy_flag = get_pgpolicy_id(optarg);
968 if (conf->pgpolicy_flag == -1) {
969 printf("'%s' is not a valid policy\n", optarg);
974 fprintf(stderr, "Missing option arguement\n");
977 fprintf(stderr, "Unknown switch: %s\n", optarg);
984 conf->dev = MALLOC(FILE_NAME_SIZE);
989 strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
991 if (filepresent(conf->dev))
992 conf->dev_type = DEV_DEVNODE;
993 else if (sscanf(conf->dev, "%d:%d", &i, &i) == 2)
994 conf->dev_type = DEV_DEVT;
996 conf->dev_type = DEV_DEVMAP;
1000 if (conf->remove == FLUSH_ONE) {
1001 if (conf->dev_type == DEV_DEVMAP)
1002 dm_flush_map(conf->dev, DEFAULT_TARGET);
1004 condlog(0, "must provide a map name to remove");
1008 else if (conf->remove == FLUSH_ALL) {
1009 dm_flush_maps(DEFAULT_TARGET);
1014 * allocate core vectors to store paths and multipaths
1016 curmp = vector_alloc();
1017 pathvec = vector_alloc();
1019 if (!curmp || !pathvec) {
1020 condlog(0, "can not allocate memory");
1025 * if we have a blacklisted device parameter, exit early
1027 if (conf->dev && blacklist(conf->blist, conf->dev))
1030 condlog(3, "load path identifiers cache");
1031 cache_load(pathvec);
1033 if (conf->verbosity > 2) {
1034 fprintf(stdout, "#\n# all paths in cache :\n#\n");
1035 print_all_paths(pathvec);
1045 /* extended path info '-ll' */
1046 di_flag |= DI_SYSFS | DI_CHECKER;
1047 else if (conf->list)
1048 /* minimum path info '-l' */
1049 di_flag |= DI_SYSFS;
1054 if (path_discovery(pathvec, conf, di_flag))
1057 if (conf->verbosity > 2) {
1058 fprintf(stdout, "#\n# all paths :\n#\n");
1059 print_all_paths(pathvec);
1063 * scope limiting must be translated into a wwid
1064 * failing the translation is fatal (by policy)
1067 refwwid = get_refwwid(pathvec);
1070 condlog(3, "scope is nul");
1075 get_path_layout(&pl, pathvec);
1077 if (get_dm_mpvec(curmp, pathvec, refwwid))
1080 filter_pathvec(pathvec, refwwid);
1086 * core logic entry point
1088 coalesce_paths(curmp, pathvec);
1094 free_multipathvec(curmp, KEEP_PATHS);
1095 free_pathvec(pathvec, FREE_PATHS);
1100 dbg_free_final(NULL);