/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Stefan Bader, IBM * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "structs.h" #include "util.h" #include "debug.h" #include "dmparser.h" #define WORD_SIZE 64 static int merge_words(char **dst, char *word) { char * p = *dst; int len, dstlen; dstlen = strlen(*dst); len = dstlen + strlen(word) + 2; *dst = REALLOC(*dst, len); if (!*dst) { free(p); return 1; } p = *dst + dstlen; *p = ' '; ++p; strncpy(p, word, len - dstlen - 1); return 0; } #define APPEND(p, end, args...) \ ({ \ int ret; \ \ ret = snprintf(p, end - p, ##args); \ if (ret < 0) { \ condlog(0, "%s: conversion error", mp->alias); \ goto err; \ } \ p += ret; \ if (p >= end) { \ condlog(0, "%s: params too small", mp->alias); \ goto err; \ } \ }) /* * Transforms the path group vector into a proper device map string */ int assemble_map (struct multipath * mp, char * params, int len) { int i, j; int minio; int nr_priority_groups, initial_pg_nr; char * p, * f; const char *const end = params + len; char no_path_retry[] = "queue_if_no_path"; char retain_hwhandler[] = "retain_attached_hw_handler"; struct pathgroup * pgp; struct path * pp; minio = mp->minio; p = params; nr_priority_groups = VECTOR_SIZE(mp->pg); initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0); if (mp->no_path_retry != NO_PATH_RETRY_UNDEF && mp->no_path_retry != NO_PATH_RETRY_FAIL) { add_feature(&mp->features, no_path_retry); } if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON && get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) add_feature(&mp->features, retain_hwhandler); f = STRDUP(mp->features); APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups, initial_pg_nr); vector_foreach_slot (mp->pg, pgp, i) { pgp = VECTOR_SLOT(mp->pg, i); APPEND(p, end, " %s %i 1", mp->selector, VECTOR_SIZE(pgp->paths)); vector_foreach_slot (pgp->paths, pp, j) { int tmp_minio = minio; if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) tmp_minio = minio * pp->priority; if (!strlen(pp->dev_t) ) { condlog(0, "dev_t not set for '%s'", pp->dev); goto err; } APPEND(p, end, " %s %d", pp->dev_t, tmp_minio); } } FREE(f); condlog(4, "%s: assembled map [%s]", mp->alias, params); return 0; err: FREE(f); return 1; } #undef APPEND int disassemble_map(vector pathvec, char *params, struct multipath *mpp, int is_daemon) { char * word; char * p; int i, j, k; int num_features = 0; int num_hwhandler = 0; int num_pg = 0; int num_pg_args = 0; int num_paths = 0; int num_paths_args = 0; int def_minio = 0; struct path * pp; struct pathgroup * pgp; p = params; condlog(4, "%s: disassemble map [%s]", mpp->alias, params); /* * features */ p += get_word(p, &mpp->features); if (!mpp->features) return 1; num_features = atoi(mpp->features); for (i = 0; i < num_features; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->features, word)) { FREE(word); return 1; } FREE(word); } /* * hwhandler */ p += get_word(p, &mpp->hwhandler); if (!mpp->hwhandler) return 1; num_hwhandler = atoi(mpp->hwhandler); for (i = 0; i < num_hwhandler; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->hwhandler, word)) { FREE(word); return 1; } FREE(word); } /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg > 0) { if (!mpp->pg) { mpp->pg = vector_alloc(); if (!mpp->pg) return 1; } } else { free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; } /* * first pg to try */ p += get_word(p, &word); if (!word) goto out; mpp->nextpg = atoi(word); FREE(word); for (i = 0; i < num_pg; i++) { /* * selector */ if (!mpp->selector) { p += get_word(p, &mpp->selector); if (!mpp->selector) goto out; /* * selector args */ p += get_word(p, &word); if (!word) goto out; num_pg_args = atoi(word); if (merge_words(&mpp->selector, word)) goto out1; FREE(word); } else { p += get_word(p, NULL); p += get_word(p, NULL); } for (j = 0; j < num_pg_args; j++) p += get_word(p, NULL); /* * paths */ pgp = alloc_pathgroup(); if (!pgp) goto out; if (add_pathgroup(mpp, pgp)) goto out; p += get_word(p, &word); if (!word) goto out; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) goto out; num_paths_args = atoi(word); FREE(word); for (j = 0; j < num_paths; j++) { char devname[FILE_NAME_SIZE]; pp = NULL; p += get_word(p, &word); if (!word) goto out; if (devt2devname(devname, FILE_NAME_SIZE, word)) { condlog(2, "%s: cannot find block device", word); devname[0] = '\0'; } if (pathvec) { if (strlen(devname)) pp = find_path_by_dev(pathvec, devname); else pp = find_path_by_devt(pathvec, word); } if (!pp) { pp = alloc_path(); if (!pp) goto out1; strlcpy(pp->dev_t, word, BLK_DEV_SIZE); strlcpy(pp->dev, devname, FILE_NAME_SIZE); if (strlen(mpp->wwid)) { strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); } /* Only call this in multipath client mode */ if (!is_daemon && store_path(pathvec, pp)) goto out1; } else { if (!strlen(pp->wwid) && strlen(mpp->wwid)) strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); } FREE(word); if (store_path(pgp->paths, pp)) goto out; /* * Update wwid for multipaths which are not setup * in the get_dm_mpvec() code path */ if (!strlen(mpp->wwid)) strlcpy(mpp->wwid, pp->wwid, WWID_SIZE); /* * Update wwid for paths which may not have been * active at the time the getuid callout was run */ else if (!strlen(pp->wwid)) strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); /* * Do not allow in-use patch to change wwid */ else if (strcmp(pp->wwid, mpp->wwid) != 0) { condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t); strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); } pgp->id ^= (long)pp; pp->pgindex = i + 1; for (k = 0; k < num_paths_args; k++) if (k == 0) { p += get_word(p, &word); def_minio = atoi(word); FREE(word); if (!strncmp(mpp->selector, "round-robin", 11)) { if (mpp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) def_minio /= pp->priority; } if (def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } return 0; out1: FREE(word); out: free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; return 1; } int disassemble_status(char *params, struct multipath *mpp) { char * word; char * p; int i, j, k; int num_feature_args; int num_hwhandler_args; int num_pg; int num_pg_args; int num_paths; int def_minio = 0; struct path * pp; struct pathgroup * pgp; p = params; condlog(4, "%s: disassemble status [%s]", mpp->alias, params); /* * features */ p += get_word(p, &word); if (!word) return 1; num_feature_args = atoi(word); FREE(word); for (i = 0; i < num_feature_args; i++) { if (i == 1) { p += get_word(p, &word); if (!word) return 1; mpp->queuedio = atoi(word); FREE(word); continue; } /* unknown */ p += get_word(p, NULL); } /* * hwhandler */ p += get_word(p, &word); if (!word) return 1; num_hwhandler_args = atoi(word); FREE(word); for (i = 0; i < num_hwhandler_args; i++) p += get_word(p, NULL); /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg == 0) return 0; /* * next pg to try */ p += get_word(p, NULL); if (VECTOR_SIZE(mpp->pg) < num_pg) return 1; for (i = 0; i < num_pg; i++) { pgp = VECTOR_SLOT(mpp->pg, i); /* * PG status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'D': pgp->status = PGSTATE_DISABLED; break; case 'A': pgp->status = PGSTATE_ACTIVE; break; case 'E': pgp->status = PGSTATE_ENABLED; break; default: pgp->status = PGSTATE_UNDEF; break; } FREE(word); /* * PG Status (discarded, would be '0' anyway) */ p += get_word(p, NULL); p += get_word(p, &word); if (!word) return 1; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) return 1; num_pg_args = atoi(word); FREE(word); if (VECTOR_SIZE(pgp->paths) < num_paths) return 1; for (j = 0; j < num_paths; j++) { pp = VECTOR_SLOT(pgp->paths, j); /* * path */ p += get_word(p, NULL); /* * path status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'F': pp->dmstate = PSTATE_FAILED; break; case 'A': pp->dmstate = PSTATE_ACTIVE; break; default: break; } FREE(word); /* * fail count */ p += get_word(p, &word); if (!word) return 1; pp->failcount = atoi(word); FREE(word); /* * selector args */ for (k = 0; k < num_pg_args; k++) { if (!strncmp(mpp->selector, "least-pending", 13)) { p += get_word(p, &word); if (sscanf(word,"%d:*d", &def_minio) == 1 && def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } } return 0; }