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>
46 #include "pgpolicies.h"
50 get_refwwid (vector pathvec)
53 char buff[FILE_NAME_SIZE];
56 if (conf->dev_type == DEV_NONE)
59 if (conf->dev_type == DEV_DEVNODE) {
60 condlog(3, "limited scope = %s", conf->dev);
61 basename(conf->dev, buff);
62 pp = find_path_by_dev(pathvec, buff);
70 strncpy(pp->dev, buff, FILE_NAME_SIZE);
72 if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
75 if (store_path(pathvec, pp)) {
81 refwwid = MALLOC(WWID_SIZE);
86 memcpy(refwwid, pp->wwid, WWID_SIZE);
90 if (conf->dev_type == DEV_DEVT) {
91 condlog(3, "limited scope = %s", conf->dev);
92 pp = find_path_by_devt(pathvec, conf->dev);
100 devt2devname(conf->dev, buff);
102 if(safe_sprintf(pp->dev, "%s", buff)) {
103 fprintf(stderr, "pp->dev too small\n");
106 if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
109 if (store_path(pathvec, pp)) {
115 refwwid = MALLOC(WWID_SIZE);
120 memcpy(refwwid, pp->wwid, WWID_SIZE);
123 if (conf->dev_type == DEV_DEVMAP) {
124 condlog(3, "limited scope = %s", conf->dev);
128 refwwid = get_mpe_wwid(conf->dev);
136 refwwid = MALLOC(WWID_SIZE);
141 strncpy(refwwid, conf->dev, WWID_SIZE);
150 #define PRINT_PATH_ALL 0
151 #define PRINT_PATH_SHORT 1
154 print_path (struct path * pp, int style)
156 if (style != PRINT_PATH_SHORT && pp->wwid)
157 printf ("%s ", pp->wwid);
161 if (pp->sg_id.host_no < 0)
164 printf("%i:%i:%i:%i ",
171 printf("%-4s ", pp->dev);
174 printf("%-7s ", pp->dev_t);
192 switch (pp->dmstate) {
205 if (style != PRINT_PATH_SHORT && pp->product_id)
206 printf("[%.16s]", pp->product_id);
208 fprintf(stdout, "\n");
212 print_map (struct multipath * mpp)
214 if (mpp->size && mpp->params)
215 printf("0 %lu %s %s\n",
216 mpp->size, DEFAULT_TARGET, mpp->params);
221 print_all_paths (vector pathvec)
226 vector_foreach_slot (pathvec, pp, i)
227 print_path(pp, PRINT_PATH_ALL);
231 print_mp (struct multipath * mpp)
234 struct path * pp = NULL;
235 struct pathgroup * pgp = NULL;
237 if (mpp->action == ACT_NOTHING || conf->verbosity == 0)
240 if (conf->verbosity > 1) {
241 switch (mpp->action) {
243 printf("%s: ", ACT_RELOAD_STR);
247 printf("%s: ", ACT_CREATE_STR);
251 printf("%s: ", ACT_SWITCHPG_STR);
260 printf("%s", mpp->alias);
262 if (conf->verbosity == 1) {
266 if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
267 printf(" (%s)", mpp->wwid);
271 if (mpp->size < 2000)
272 printf("[size=%lu kB]", mpp->size / 2);
273 else if (mpp->size < (2000 * 1024))
274 printf("[size=%lu MB]", mpp->size / 2 / 1024);
275 else if (mpp->size < (2000 * 1024 * 1024))
276 printf("[size=%lu GB]", mpp->size / 2 / 1024 / 1024);
278 printf("[size=%lu TB]", mpp->size / 2 / 1024 / 1024 / 1024);
281 printf("[features=\"%s\"]", mpp->features);
284 printf("[hwhandler=\"%s\"]", mpp->hwhandler);
286 fprintf(stdout, "\n");
291 vector_foreach_slot (mpp->pg, pgp, j) {
295 printf("%s ", mpp->selector);
297 switch (pgp->status) {
298 case PGSTATE_ENABLED:
301 case PGSTATE_DISABLED:
302 printf("[disabled]");
312 vector_foreach_slot (pgp->paths, pp, i)
313 print_path(pp, PRINT_PATH_SHORT);
319 filter_pathvec (vector pathvec, char * refwwid)
324 if (!refwwid || !strlen(refwwid))
327 vector_foreach_slot (pathvec, pp, i) {
328 if (memcmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
329 condlog(3, "skip path %s : out of scope", pp->dev);
331 vector_del_slot(pathvec, i);
339 * Transforms the path group vector into a proper device map string
342 assemble_map (struct multipath * mp)
347 struct pathgroup * pgp;
351 freechar = sizeof(mp->params);
353 shift = snprintf(p, freechar, "%s %s %i %i",
354 mp->features, mp->hwhandler,
355 VECTOR_SIZE(mp->pg), mp->nextpg);
357 if (shift >= freechar) {
358 fprintf(stderr, "mp->params too small\n");
364 vector_foreach_slot (mp->pg, pgp, i) {
365 pgp = VECTOR_SLOT(mp->pg, i);
366 shift = snprintf(p, freechar, " %s %i 1", mp->selector,
367 VECTOR_SIZE(pgp->paths));
368 if (shift >= freechar) {
369 fprintf(stderr, "mp->params too small\n");
375 vector_foreach_slot (pgp->paths, pp, j) {
376 shift = snprintf(p, freechar, " %s %d",
377 pp->dev_t, conf->minio);
378 if (shift >= freechar) {
379 fprintf(stderr, "mp->params too small\n");
387 fprintf(stderr, "mp->params too small\n");
390 snprintf(p, 1, "\n");
392 if (conf->verbosity > 2)
399 setup_map (struct multipath * mpp)
405 * don't bother if devmap size is unknown
407 if (mpp->size <= 0) {
408 condlog(3, "%s devmap size is unknown", mpp->alias);
413 * don't bother if a constituant path is claimed
414 * FIXME : claimed detection broken, always unclaimed for now
416 vector_foreach_slot (mpp->paths, pp, i) {
418 condlog(3, "%s claimed", pp->dev);
424 * properties selectors
426 select_pgpolicy(mpp);
427 select_selector(mpp);
428 select_features(mpp);
429 select_hwhandler(mpp);
432 * apply selected grouping policy to valid paths
434 switch (mpp->pgpolicy) {
439 one_path_per_group(mpp);
441 case GROUP_BY_SERIAL:
442 group_by_serial(mpp);
447 case GROUP_BY_NODE_NAME:
448 group_by_node_name(mpp);
454 if (mpp->pg == NULL) {
455 condlog(3, "pgpolicy failed to produce a pg vector");
460 * ponders each path group and determine highest prio pg
461 * to switch over (default to first)
463 select_path_group(mpp);
466 * transform the mp->pg vector of vectors of paths
467 * into a mp->params strings to feed the device-mapper
469 if (assemble_map(mpp)) {
470 condlog(3, "problem assembing map");
477 pathcount (struct multipath * mpp, int state)
479 struct pathgroup *pgp;
484 vector_foreach_slot (mpp->pg, pgp, i)
485 vector_foreach_slot (pgp->paths, pp, j)
486 if (pp->state == state)
492 compute_pgid(struct pathgroup * pgp)
497 vector_foreach_slot (pgp->paths, pp, i)
502 pgcmp (struct multipath * mpp, struct multipath * cmpp)
505 struct pathgroup * pgp;
506 struct pathgroup * cpgp;
509 vector_foreach_slot (mpp->pg, pgp, i) {
512 vector_foreach_slot (cmpp->pg, cpgp, j) {
513 if (pgp->id == cpgp->id) {
526 select_action (struct multipath * mpp, vector curmp)
528 struct multipath * cmpp;
530 cmpp = find_mp(curmp, mpp->alias);
533 mpp->action = ACT_CREATE;
536 if (pathcount(mpp, PATH_UP) == 0) {
537 condlog(3, "no good path");
538 mpp->action = ACT_NOTHING;
541 if (cmpp->size != mpp->size) {
542 condlog(3, "size different than current");
543 mpp->action = ACT_RELOAD;
546 if (strncmp(cmpp->features, mpp->features,
547 strlen(mpp->features))) {
548 condlog(3, "features different than current");
549 mpp->action = ACT_RELOAD;
552 if (strncmp(cmpp->hwhandler, mpp->hwhandler,
553 strlen(mpp->hwhandler))) {
554 condlog(3, "hwhandler different than current");
555 mpp->action = ACT_RELOAD;
558 if (strncmp(cmpp->selector, mpp->selector,
559 strlen(mpp->selector))) {
560 condlog(3, "selector different than current");
561 mpp->action = ACT_RELOAD;
564 if (VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
565 condlog(3, "different number of PG");
566 mpp->action = ACT_RELOAD;
569 if (pgcmp(mpp, cmpp)) {
570 condlog(3, "different path group topology");
571 mpp->action = ACT_RELOAD;
574 if (cmpp->nextpg != mpp->nextpg) {
575 condlog(3, "nextpg different than current");
576 mpp->action = ACT_SWITCHPG;
579 mpp->action = ACT_NOTHING;
584 reinstate_paths (struct multipath * mpp)
587 struct pathgroup * pgp;
590 vector_foreach_slot (mpp->pg, pgp, i) {
591 vector_foreach_slot (pgp->paths, pp, j) {
592 if (pp->state != PATH_UP &&
593 (pgp->status == PGSTATE_DISABLED ||
594 pgp->status == PGSTATE_ACTIVE))
597 if (pp->dmstate == PSTATE_FAILED) {
598 if (dm_reinstate(mpp->alias, pp->dev_t))
599 condlog(0, "error reinstating %s",
608 domap (struct multipath * mpp)
610 int op = ACT_NOTHING;
616 * last chance to quit before touching the devmaps
618 if (conf->dry_run || mpp->action == ACT_NOTHING)
621 if (mpp->action == ACT_SWITCHPG) {
622 dm_switchgroup(mpp->alias, mpp->nextpg);
624 * we may have avoided reinstating paths because there where in
625 * active or disabled PG. Now that the topology has changed,
628 reinstate_paths(mpp);
631 if (mpp->action == ACT_CREATE)
632 op = DM_DEVICE_CREATE;
634 if (mpp->action == ACT_RELOAD)
635 op = DM_DEVICE_RELOAD;
639 * device mapper creation or updating
640 * here we know we'll have garbage on stderr from
641 * libdevmapper. so shut it down temporarily.
643 dm_log_init_verbose(0);
645 r = dm_addmap(op, mpp->alias, DEFAULT_TARGET, mpp->params, mpp->size);
648 dm_simplecmd(DM_DEVICE_REMOVE, mpp->alias);
649 else if (op == DM_DEVICE_RELOAD)
650 dm_simplecmd(DM_DEVICE_RESUME, mpp->alias);
653 * PG order is random, so we need to set the primary one
654 * upon create or reload
656 dm_switchgroup(mpp->alias, mpp->nextpg);
658 dm_log_init_verbose(1);
664 coalesce_paths (vector curmp, vector pathvec)
667 char empty_buff[WWID_SIZE];
668 struct multipath * mpp;
672 memset(empty_buff, 0, WWID_SIZE);
674 vector_foreach_slot (pathvec, pp1, k) {
675 /* skip this path for some reason */
677 /* 1. if path has no unique id or wwid blacklisted */
678 if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
679 blacklist(conf->blist, pp1->wwid))
682 /* 2. if path already coalesced */
687 * at this point, we know we really got a new mp
689 mpp = alloc_multipath();
694 mpp->mpe = find_mpe(pp1->wwid);
699 strcpy(mpp->wwid, pp1->wwid);
700 mpp->size = pp1->size;
701 mpp->paths = vector_alloc();
703 if (pp1->priority < 0)
704 mpp->action = ACT_NOTHING;
709 if (store_path(mpp->paths, pp1))
712 for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) {
713 pp2 = VECTOR_SLOT(pathvec, i);
715 if (strcmp(pp1->wwid, pp2->wwid))
720 if (pp2->size != mpp->size) {
722 * ouch, avoid feeding that to the DM
724 condlog(3, "path size mismatch : discard %s",
726 mpp->action = ACT_NOTHING;
728 if (pp2->priority < 0)
729 mpp->action = ACT_NOTHING;
731 if (store_path(mpp->paths, pp2))
734 if (setup_map(mpp)) {
735 free_multipath(mpp, KEEP_PATHS);
738 condlog(3, "action preset to %i", mpp->action);
740 if (mpp->action == ACT_UNDEF)
741 select_action(mpp, curmp);
743 condlog(3, "action set to %i", mpp->action);
746 free_multipath(mpp, KEEP_PATHS);
752 usage (char * progname)
754 fprintf (stderr, VERSION_STRING);
755 fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n",
758 "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \
761 "\t-v level\tverbosty level\n" \
762 "\t 0\t\t\tno output\n" \
763 "\t 1\t\t\tprint created devmap names only\n" \
764 "\t 2\t\t\tdefault verbosity\n" \
765 "\t 3\t\t\tprint debug information\n" \
766 "\t-d\t\tdry run, do not create or update devmaps\n" \
767 "\t-l\t\tshow multipath topology (sysfs and DM info)\n" \
768 "\t-ll\t\tshow multipath topology (maximum info)\n" \
769 "\t-F\t\tflush a multipath device map\n" \
770 "\t-F\t\tflush all multipath device maps\n" \
771 "\t-p policy\tforce all maps to specified policy :\n" \
772 "\t failover\t\t1 path per priority group\n" \
773 "\t multibus\t\tall paths in 1 priority group\n" \
774 "\t group_by_serial\t1 priority group per serial\n" \
775 "\t group_by_prio\t1 priority group per priority lvl\n" \
776 "\t group_by_node_name\t1 priority group per target node\n" \
778 "\tdevice\t\tlimit scope to the device's multipath\n" \
779 "\t\t\t(udev-style $DEVNAME reference, eg /dev/sdb\n" \
780 "\t\t\tor major:minor or a device map name)\n" \
787 update_pathvec (vector pathvec)
792 vector_foreach_slot (pathvec, pp, i) {
793 if (pp->dev && pp->dev_t && strlen(pp->dev) == 0) {
794 devt2devname(pp->dev, pp->dev_t);
795 pathinfo(pp, conf->hwtable,
796 DI_SYSFS | DI_CHECKER | DI_SERIAL | DI_PRIO);
798 if (pp->checkfn && pp->state == PATH_UNCHECKED)
799 pp->state = pp->checkfn(pp->fd, NULL, NULL);
805 get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
808 struct multipath * mpp;
811 if (dm_get_maps(curmp, DEFAULT_TARGET))
814 vector_foreach_slot (curmp, mpp, i) {
815 wwid = get_mpe_wwid(mpp->alias);
818 strncpy(mpp->wwid, wwid, WWID_SIZE);
821 strncpy(mpp->wwid, mpp->alias, WWID_SIZE);
823 if (refwwid && strncmp(mpp->wwid, refwwid, WWID_SIZE))
826 condlog(3, "params = %s", mpp->params);
827 condlog(3, "status = %s", mpp->status);
828 disassemble_map(pathvec, mpp->params, mpp);
831 * disassemble_map may have added new paths to pathvec.
832 * If not in "fast list mode", we need to fetch information
836 update_pathvec(pathvec);
838 disassemble_status(mpp->status, mpp);
844 reinstate_paths(mpp);
850 main (int argc, char *argv[])
853 vector pathvec = NULL;
859 char * refwwid = NULL;
862 fprintf(stderr, "need to be root\n");
866 if (dm_prereq(DEFAULT_TARGET, 1, 0, 3)) {
867 condlog(0, "device mapper prerequisites not met");
870 if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
871 condlog(0, "multipath tools need sysfs mounted");
874 if (load_config(DEFAULT_CONFIGFILE))
877 while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:")) != EOF ) {
879 case 1: printf("optarg : %s\n",optarg);
882 if (sizeof(optarg) > sizeof(char *) ||
886 conf->verbosity = atoi(optarg);
895 dm_flush_maps(DEFAULT_TARGET);
902 if (optarg && !strncmp(optarg, "l", 1))
908 debug = atoi(optarg);
912 conf->pgpolicy_flag = get_pgpolicy_id(optarg);
913 if (conf->pgpolicy_flag == -1) {
914 printf("'%s' is not a valid policy\n", optarg);
919 fprintf(stderr, "Missing option arguement\n");
922 fprintf(stderr, "Unknown switch: %s\n", optarg);
929 conf->dev = MALLOC(FILE_NAME_SIZE);
934 strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
936 if (filepresent(conf->dev))
937 conf->dev_type = DEV_DEVNODE;
938 else if (sscanf(conf->dev, "%d:%d", &i, &i) == 2)
939 conf->dev_type = DEV_DEVT;
941 conf->dev_type = DEV_DEVMAP;
946 if (conf->dev_type == DEV_DEVMAP) {
947 condlog(4, "remove %s map", conf->dev);
948 dm_flush_map(conf->dev, DEFAULT_TARGET);
951 condlog(0, "must provide a map name to remove");
957 * allocate core vectors to store paths and multipaths
959 curmp = vector_alloc();
960 pathvec = vector_alloc();
962 if (!curmp || !pathvec) {
963 condlog(0, "can not allocate memory");
968 * if we have a blacklisted device parameter, exit early
970 if (conf->dev && blacklist(conf->blist, conf->dev))
973 if (!cache_cold(CACHE_EXPIRE)) {
974 condlog(3, "load path identifiers cache");
985 /* extended path info '-ll' */
986 di_flag |= DI_SYSFS | DI_CHECKER;
988 /* minimum path info '-l' */
994 if (path_discovery(pathvec, conf, di_flag) || VECTOR_SIZE(pathvec) == 0)
997 if (conf->verbosity > 2) {
998 fprintf(stdout, "#\n# all paths :\n#\n");
999 print_all_paths(pathvec);
1002 refwwid = get_refwwid(pathvec);
1004 if (get_dm_mpvec(curmp, pathvec, refwwid))
1007 cache_dump(pathvec);
1008 filter_pathvec(pathvec, refwwid);
1014 * core logic entry point
1016 coalesce_paths(curmp, pathvec);
1022 free_multipathvec(curmp, KEEP_PATHS);
1023 free_pathvec(pathvec, FREE_PATHS);
1028 dbg_free_final(NULL);