#include #include #include #include "util.h" #include "checkers.h" #include "vector.h" #include "defaults.h" #include "debug.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "sysfs.h" #include "devmapper.h" #include "dmparser.h" #include "propsel.h" #include "discovery.h" #include "prio.h" #include "configure.h" #include "libdevmapper.h" #include "io_err_stat.h" #include "switchgroup.h" /* * creates or updates mpp->paths reading mpp->pg */ int update_mpp_paths(struct multipath *mpp, vector pathvec) { struct pathgroup * pgp; struct path * pp; int i,j; if (!mpp || !mpp->pg) return 0; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (!find_path_by_devt(mpp->paths, pp->dev_t) && (find_path_by_devt(pathvec, pp->dev_t)) && store_path(mpp->paths, pp)) return 1; } } return 0; } int adopt_paths(vector pathvec, struct multipath *mpp) { int i, ret; struct path * pp; struct config *conf; if (!mpp) return 0; if (update_mpp_paths(mpp, pathvec)) return 1; vector_foreach_slot (pathvec, pp, i) { if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) { if (pp->size != 0 && mpp->size != 0 && pp->size != mpp->size) { condlog(3, "%s: size mismatch for %s, not adding path", pp->dev, mpp->alias); continue; } condlog(3, "%s: ownership set to %s", pp->dev, mpp->alias); pp->mpp = mpp; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; if (!find_path_by_dev(mpp->paths, pp->dev) && store_path(mpp->paths, pp)) return 1; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); ret = pathinfo(pp, conf, DI_PRIO | DI_CHECKER); pthread_cleanup_pop(1); if (ret) return 1; } } return 0; } void orphan_path(struct path *pp, const char *reason) { condlog(3, "%s: orphan path, %s", pp->dev, reason); pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->uid_attribute = NULL; pp->getuid = NULL; prio_put(&pp->prio); checker_put(&pp->checker); if (pp->fd >= 0) close(pp->fd); pp->fd = -1; } void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason) { int i; struct path * pp; vector_foreach_slot (pathvec, pp, i) { if (pp->mpp == mpp) { orphan_path(pp, reason); } } } void remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec) { int i; /* * clear references to this map */ orphan_paths(vecs->pathvec, mpp, "map removed internally"); if (purge_vec && (i = find_slot(vecs->mpvec, (void *)mpp)) != -1) vector_del_slot(vecs->mpvec, i); /* * final free */ free_multipath(mpp, KEEP_PATHS); } void remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec) { struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { condlog(2, "%s: removing map by alias", alias); remove_map(mpp, vecs, purge_vec); } } void remove_maps(struct vectors * vecs) { int i; struct multipath * mpp; if (!vecs) return; vector_foreach_slot (vecs->mpvec, mpp, i) { remove_map(mpp, vecs, 1); i--; } vector_free(vecs->mpvec); vecs->mpvec = NULL; } void extract_hwe_from_path(struct multipath * mpp) { struct path * pp = NULL; int i; if (mpp->hwe || !mpp->paths) return; condlog(3, "%s: searching paths for valid hwe", mpp->alias); /* doing this in two passes seems like paranoia to me */ vector_foreach_slot(mpp->paths, pp, i) { if (pp->state != PATH_UP) continue; if (pp->hwe) { mpp->hwe = pp->hwe; return; } } vector_foreach_slot(mpp->paths, pp, i) { if (pp->state == PATH_UP) continue; if (pp->hwe) { mpp->hwe = pp->hwe; return; } } } int update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon) { int r = DMP_ERR; char params[PARAMS_SIZE] = {0}; if (!mpp) return r; r = dm_get_map(mpp->alias, &mpp->size, params); if (r != DMP_OK) { condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present"); return r; } if (disassemble_map(pathvec, params, mpp, is_daemon)) { condlog(3, "%s: cannot disassemble map", mpp->alias); return DMP_ERR; } return DMP_OK; } int update_multipath_status (struct multipath *mpp) { int r = DMP_ERR; char status[PARAMS_SIZE] = {0}; if (!mpp) return r; r = dm_get_status(mpp->alias, status); if (r != DMP_OK) { condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present"); return r; } if (disassemble_status(status, mpp)) { condlog(3, "%s: cannot disassemble status", mpp->alias); return DMP_ERR; } return DMP_OK; } void sync_paths(struct multipath *mpp, vector pathvec) { struct path *pp; struct pathgroup *pgp; int found, i, j; vector_foreach_slot (mpp->paths, pp, i) { found = 0; vector_foreach_slot(mpp->pg, pgp, j) { if (find_slot(pgp->paths, (void *)pp) != -1) { found = 1; break; } } if (!found) { condlog(3, "%s dropped path %s", mpp->alias, pp->dev); vector_del_slot(mpp->paths, i--); orphan_path(pp, "path removed externally"); } } update_mpp_paths(mpp, pathvec); vector_foreach_slot (mpp->paths, pp, i) pp->mpp = mpp; } int update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) { struct pathgroup *pgp; int i, r = DMP_ERR; if (!mpp) return r; update_mpp_paths(mpp, pathvec); condlog(4, "%s: %s", mpp->alias, __FUNCTION__); free_multipath_attributes(mpp); free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; r = update_multipath_table(mpp, pathvec, is_daemon); if (r != DMP_OK) return r; sync_paths(mpp, pathvec); r = update_multipath_status(mpp); if (r != DMP_OK) return r; vector_foreach_slot(mpp->pg, pgp, i) if (pgp->paths) path_group_prio_update(pgp); return DMP_OK; } static void enter_recovery_mode(struct multipath *mpp) { unsigned int checkint; struct config *conf; if (mpp->in_recovery || mpp->no_path_retry <= 0) return; conf = get_multipath_config(); checkint = conf->checkint; put_multipath_config(conf); /* * Enter retry mode. * meaning of +1: retry_tick may be decremented in checkerloop before * starting retry. */ mpp->in_recovery = true; mpp->stat_queueing_timeouts++; mpp->retry_tick = mpp->no_path_retry * checkint + 1; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); } static void leave_recovery_mode(struct multipath *mpp) { bool recovery = mpp->in_recovery; mpp->in_recovery = false; mpp->retry_tick = 0; /* * in_recovery is only ever set if mpp->no_path_retry > 0 * (see enter_recovery_mode()). But no_path_retry may have been * changed while the map was recovering, so test it here again. */ if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE || mpp->no_path_retry > 0)) { dm_queue_if_no_path(mpp->alias, 1); condlog(2, "%s: queue_if_no_path enabled", mpp->alias); condlog(1, "%s: Recovered to normal mode", mpp->alias); } } void __set_no_path_retry(struct multipath *mpp, bool check_features) { bool is_queueing = false; /* assign a value to make gcc happy */ check_features = check_features && mpp->features != NULL; if (check_features) is_queueing = strstr(mpp->features, "queue_if_no_path"); switch (mpp->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; case NO_PATH_RETRY_FAIL: if (!check_features || is_queueing) dm_queue_if_no_path(mpp->alias, 0); break; case NO_PATH_RETRY_QUEUE: if (!check_features || !is_queueing) dm_queue_if_no_path(mpp->alias, 1); break; default: if (count_active_paths(mpp) > 0) { /* * If in_recovery is set, leave_recovery_mode() takes * care of dm_queue_if_no_path. Otherwise, do it here. */ if ((!check_features || !is_queueing) && !mpp->in_recovery) dm_queue_if_no_path(mpp->alias, 1); leave_recovery_mode(mpp); } else enter_recovery_mode(mpp); break; } } void sync_map_state(struct multipath *mpp) { struct pathgroup *pgp; struct path *pp; unsigned int i, j; if (!mpp->pg) return; vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD || pp->state == PATH_DELAYED) continue; if (mpp->ghost_delay_tick > 0) continue; if ((pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_UP || pp->state == PATH_GHOST)) dm_reinstate_path(mpp->alias, pp->dev_t); else if ((pp->dmstate == PSTATE_ACTIVE || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_DOWN || pp->state == PATH_SHAKY)) { condlog(2, "sync_map_state: failing %s state %d dmstate %d", pp->dev, pp->state, pp->dmstate); dm_fail_path(mpp->alias, pp->dev_t); } } } } static void find_existing_alias (struct multipath * mpp, struct vectors *vecs) { struct multipath * mp; int i; vector_foreach_slot (vecs->mpvec, mp, i) if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) { strlcpy(mpp->alias_old, mp->alias, WWID_SIZE); return; } } struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp, int add_vec) { struct multipath * mpp; struct config *conf = NULL; if (!strlen(pp->wwid)) return NULL; if (!(mpp = alloc_multipath())) return NULL; conf = get_multipath_config(); mpp->mpe = find_mpe(conf->mptable, pp->wwid); mpp->hwe = pp->hwe; put_multipath_config(conf); strcpy(mpp->wwid, pp->wwid); find_existing_alias(mpp, vecs); if (select_alias(conf, mpp)) goto out; mpp->size = pp->size; if (adopt_paths(vecs->pathvec, mpp)) goto out; if (add_vec) { if (!vector_alloc_slot(vecs->mpvec)) goto out; vector_set_slot(vecs->mpvec, mpp); } return mpp; out: remove_map(mpp, vecs, PURGE_VEC); return NULL; } int verify_paths(struct multipath *mpp, struct vectors *vecs) { struct path * pp; int count = 0; int i, j; if (!mpp) return 0; vector_foreach_slot (mpp->paths, pp, i) { /* * see if path is in sysfs */ if (sysfs_attr_get_value(pp->udev, "dev", pp->dev_t, BLK_DEV_SIZE) < 0) { if (pp->state != PATH_DOWN) { condlog(1, "%s: removing valid path %s in state %d", mpp->alias, pp->dev, pp->state); } else { condlog(3, "%s: failed to access path %s", mpp->alias, pp->dev); } count++; vector_del_slot(mpp->paths, i); i--; /* Make sure mpp->hwe doesn't point to freed memory. * We call extract_hwe_from_path() below to restore * mpp->hwe */ if (mpp->hwe == pp->hwe) mpp->hwe = NULL; if ((j = find_slot(vecs->pathvec, (void *)pp)) != -1) vector_del_slot(vecs->pathvec, j); free_path(pp); } else { condlog(4, "%s: verified path %s dev_t %s", mpp->alias, pp->dev, pp->dev_t); } } extract_hwe_from_path(mpp); return count; } /* * mpp->no_path_retry: * -2 (QUEUE) : queue_if_no_path enabled, never turned off * -1 (FAIL) : fail_if_no_path * 0 (UNDEF) : nothing * >0 : queue_if_no_path enabled, turned off after polling n times */ void update_queue_mode_del_path(struct multipath *mpp) { int active = count_active_paths(mpp); if (active == 0) { enter_recovery_mode(mpp); if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE) mpp->stat_map_failures++; } condlog(2, "%s: remaining active paths: %d", mpp->alias, active); } void update_queue_mode_add_path(struct multipath *mpp) { int active = count_active_paths(mpp); if (active > 0) leave_recovery_mode(mpp); condlog(2, "%s: remaining active paths: %d", mpp->alias, active); } vector get_used_hwes(const struct _vector *pathvec) { int i, j; struct path *pp; struct hwentry *hwe; vector v = vector_alloc(); if (v == NULL) return NULL; vector_foreach_slot(pathvec, pp, i) { vector_foreach_slot_backwards(pp->hwe, hwe, j) { vector_find_or_add_slot(v, hwe); } } return v; }