/* * Copyright (C) 2002 Sistina Software (UK) Limited. * * This file is released under the LGPL. */ #include "dev_manager.h" #include "pool.h" #include "hash.h" #include "log.h" #include "lvm-string.h" #include "fs.h" #include #include #include /* * activate(dirty lvs) * ------------------- * * 1) Examine dm directory, and build up a list of active lv's, *include* * dirty lvs. All vg layers go into tree. * * 2) Build complete tree for vg, marking lv's stack as dirty. Note this * tree is a function of the active_list (eg, no origin layer needed * if snapshot not active). * * 3) Query layers to see which exist. * * 4) Mark active_list. * * 5) Propagate marks. * * 6) Any unmarked, but existing layers get added to the remove_list. * * 7) Remove unmarked layers from core. * * 8) Activate remaining layers (in order), skipping any that already * exist, unless they are marked dirty. * * 9) remove layers in the remove_list (Requires examination of deps). * * * deactivate(dirty lvs) * --------------------- * * 1) Examine dm directory, create active_list *excluding* * dirty_list. All vg layers go into tree. * * 2) Build vg tree given active_list, no dirty layers. * * ... same as activate. */ enum { ACTIVE = 0, DIRTY = 1, VISIBLE = 2 }; struct dev_layer { char *name; int flags; /* * Setup the dm_task. */ int (*populate)(struct dev_manager *dm, struct dm_task *dmt, struct dev_layer *dl); struct dm_info info; struct logical_volume *lv; /* * Devices that must be created before this one can be * created. Holds str_lists. */ struct list pre_create; /* * Devices that must be created before this one can be * unsuspended. Holds str_lists. */ struct list pre_active; }; struct dl_list { struct list list; struct dev_layer *dl; }; struct dev_manager { struct pool *mem; char *vg_name; /* * list of struct lv_list, contains lvs that we wish to * be active after execution. */ struct list active_list; /* * Layers that need reloading. */ struct list dirty_list; /* * Layers that will need removing after activation. */ struct list remove_list; struct hash_table *layers; }; /* * Functions to manage the flags. */ static inline int _get_flag(struct dev_layer *dl, int bit) { return (dl->flags & (1 << bit)) ? 1 : 0; } static inline void _set_flag(struct dev_layer *dl, int bit) { dl->flags |= (1 << bit); } static inline void _clear_flag(struct dev_layer *dl, int bit) { dl->flags &= ~(1 << bit); } /* * Device layer names are all of the form --, any * other hyphens that appear in these names are quoted with yet * another hyphen. The top layer of any device has no layer * name. eg, vg0-lvol0. */ static void _count_hyphens(const char *str, size_t *len, int *hyphens) { const char *ptr; for (ptr = str; *ptr; ptr++, (*len)++) if (*ptr == '-') (*hyphens)++; } /* * Copies a string, quoting hyphens with hyphens. */ static void _quote_hyphens(char **out, const char *src) { while (*src) { if (*src == '-') *(*out)++ = '-'; *(*out)++ = *src++; } } /* * -- or if !layer just -. */ static char *_build_name(struct pool *mem, const char *vg, const char *lv, const char *layer) { size_t len = 0; int hyphens = 0; char *r, *out; _count_hyphens(vg, &len, &hyphens); _count_hyphens(lv, &len, &hyphens); if (layer) _count_hyphens(layer, &len, &hyphens); len += hyphens + 2; if (!(r = pool_alloc(mem, len))) { stack; return NULL; } out = r; _quote_hyphens(&out, vg); *out++ = '-'; _quote_hyphens(&out, lv); if (layer) { *out++ = '-'; _quote_hyphens(&out, layer); } *out = '\0'; return r; } /* * Low level device-layer operations. */ static struct dm_task *_setup_task(const char *name, int task) { struct dm_task *dmt; if (!(dmt = dm_task_create(task))) { stack; return NULL; } dm_task_set_name(dmt, name); return dmt; } static int _load(struct dev_manager *dm, struct dev_layer *dl, int task) { int r; struct dm_task *dmt; log_verbose("Loading %s", dl->name); if (!(dmt = _setup_task(dl->name, task))) { stack; return 0; } /* * Populate the table. */ if (!dl->populate(dm, dmt, dl)) { log_err("Couldn't populate device '%s'.", dl->name); return 0; } if (_get_flag(dl, VISIBLE)) { dm_task_set_uuid(dmt, dl->lv->lvid.s); } if (!(r = dm_task_run(dmt))) log_err("Couldn't load device '%s'.", dl->name); dm_task_destroy(dmt); if (_get_flag(dl, VISIBLE)) fs_add_lv(dl->lv, dl->name); dl->info.exists = 1; return r; } static int _remove(struct dev_layer *dl) { int r; struct dm_task *dmt; log_verbose("Removing %s", dl->name); if (!(dmt = _setup_task(dl->name, DM_DEVICE_REMOVE))) { stack; return 0; } if (!(r = dm_task_run(dmt))) log_err("Couldn't remove device '%s'", dl->name); dm_task_destroy(dmt); if (_get_flag(dl, VISIBLE)) fs_del_lv(dl->lv); return r; } static int _suspend_or_resume(const char *name, int sus) { int r; struct dm_task *dmt; int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME; log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name); if (!(dmt = _setup_task(name, task))) { stack; return 0; } if (!(r = dm_task_run(dmt))) log_err("Couldn't %s device '%s'", sus ? "suspend" : "resume", name); dm_task_destroy(dmt); return r; } static int _suspend(struct dev_layer *dl) { if (dl->info.suspended) return 1; if (!_suspend_or_resume(dl->name, 1)) { stack; return 0; } dl->info.suspended = 1; return 1; } static int _resume(struct dev_layer *dl) { if (!dl->info.suspended) return 1; if (!_suspend_or_resume(dl->name, 0)) { stack; return 0; } dl->info.suspended = 0; return 1; } static int _info(const char *name, struct dm_info *info) { int r = 0; struct dm_task *dmt; log_very_verbose("Getting device info for %s", name); if (!(dmt = _setup_task(name, DM_DEVICE_INFO))) { stack; return 0; } if (!dm_task_run(dmt)) { stack; goto out; } if (!dm_task_get_info(dmt, info)) { stack; goto out; } r = 1; out: dm_task_destroy(dmt); return r; } /* * The functions that populate the table in a dm_task as part of * a create/reload. */ /* * Emit a target for a given segment. * FIXME: tidy this function. */ static int _emit_target(struct dm_task *dmt, struct stripe_segment *seg) { char params[1024]; uint64_t esize = seg->lv->vg->extent_size; uint32_t s, stripes = seg->stripes; int w = 0, tw = 0, error = 0; const char *no_space = "Insufficient space to write target parameters."; char *filler = "/dev/ioerror"; char *target; if (stripes == 1) { if (!seg->area[0].pv) { target = "error"; error = 1; } else target = "linear"; } if (stripes > 1) { target = "striped"; tw = lvm_snprintf(params, sizeof(params), "%u %u ", stripes, seg->stripe_size); if (tw < 0) { log_err(no_space); return 0; } w = tw; } if (!error) { for (s = 0; s < stripes; s++, w += tw) { if (!seg->area[s].pv) tw = lvm_snprintf( params + w, sizeof(params) - w, "%s 0%s", filler, s == (stripes - 1) ? "" : " "); else tw = lvm_snprintf( params + w, sizeof(params) - w, "%s %" PRIu64 "%s", dev_name(seg->area[s].pv->dev), (seg->area[s].pv->pe_start + (esize * seg->area[s].pe)), s == (stripes - 1) ? "" : " "); if (tw < 0) { log_err(no_space); return 0; } } } log_very_verbose("Adding target: %" PRIu64 " %" PRIu64 " %s %s", esize * seg->le, esize * seg->len, target, params); if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len, target, params)) { stack; return 0; } return 1; } static int _populate_vanilla(struct dev_manager *dm, struct dm_task *dmt, struct dev_layer *dl) { struct list *segh; struct stripe_segment *seg; struct logical_volume *lv = dl->lv; list_iterate(segh, &lv->segments) { seg = list_item(segh, struct stripe_segment); if (!_emit_target(dmt, seg)) { log_error("Unable to build table for '%s'", lv->name); return 0; } } return 1; } static int _populate_origin(struct dev_manager *dm, struct dm_task *dmt, struct dev_layer *dl) { char *real; char params[PATH_MAX + 32]; if (!(real = _build_name(dm->mem, dm->vg_name, dl->lv->name, "real"))) { stack; return 0; } if (lvm_snprintf(params, sizeof(params), "%s/%s", dm_dir(), real) == -1) { log_err("Couldn't create origin device parameters for '%s'.", dl->name); return 0; } log_very_verbose("Adding target: 0 %" PRIu64 " snapshot-origin %s", dl->lv->size, params); if (!dm_task_add_target(dmt, 0, dl->lv->size, "snapshot-origin", params)) { stack; return 0; } return 1; } static int _populate_snapshot(struct dev_manager *dm, struct dm_task *dmt, struct dev_layer *dl) { char *origin, *cow; char params[PATH_MAX * 2 + 32]; struct snapshot *s; if (!(s = find_cow(dl->lv))) { log_err("Couldn't find snapshot for '%s'.", dl->name); return 0; } if (!(origin = _build_name(dm->mem, dm->vg_name, s->origin->name, "real"))) { stack; return 0; } if (!(cow = _build_name(dm->mem, dm->vg_name, s->cow->name, "cow"))) { stack; return 0; } if (snprintf(params, sizeof(params), "%s/%s %s/%s P %d 128", dm_dir(), origin, dm_dir(), cow, s->chunk_size) == -1) { stack; return 0; } log_very_verbose("Adding target: 0 %" PRIu64 " snapshot %s", s->origin->size, params); if (!dm_task_add_target(dmt, 0, s->origin->size, "snapshot", params)) { stack; return 0; } return 1; } /* * dev_manager implementation. */ struct dev_manager *dev_manager_create(const char *vg_name) { struct pool *mem; struct dev_manager *dm; if (!(mem = pool_create(16 * 1024))) { stack; return NULL; } if (!(dm = pool_alloc(mem, sizeof(*dm)))) { stack; goto bad; } dm->mem = mem; if (!(dm->vg_name = pool_strdup(dm->mem, vg_name))) { stack; goto bad; } if (!(dm->layers = hash_create(32))) { stack; goto bad; } list_init(&dm->active_list); list_init(&dm->dirty_list); list_init(&dm->remove_list); return dm; bad: pool_destroy(mem); return NULL; } void dev_manager_destroy(struct dev_manager *dm) { hash_destroy(dm->layers); pool_destroy(dm->mem); } int dev_manager_info(struct dev_manager *dm, struct logical_volume *lv, struct dm_info *info) { char *name; /* * Build a name for the top layer. */ if (!(name = _build_name(dm->mem, lv->vg->name, lv->name, NULL))) { stack; return 0; } /* * Try and get some info on this device. */ if (!_info(name, info)) { stack; return 0; } return 1; } int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv) { char *name; /* * Build a name for the top layer. */ if (!(name = _build_name(dm->mem, lv->vg->name, lv->name, NULL))) { stack; return 0; } if (!_suspend_or_resume(name, 1)) { stack; return 0; } return 1; } static struct dev_layer *_create_dev(struct pool *mem, char *name, struct logical_volume *lv) { struct dev_layer *dl; if (!(dl = pool_zalloc(mem, sizeof(*dl)))) { stack; return NULL; } dl->name = name; if (!_info(dl->name, &dl->info)) { stack; return NULL; } dl->lv = lv; list_init(&dl->pre_create); list_init(&dl->pre_active); return dl; } static struct dev_layer *_create_layer(struct pool *mem, const char *layer, struct logical_volume *lv) { char *name; if (!(name = _build_name(mem, lv->vg->name, lv->name, layer))) { stack; return NULL; } return _create_dev(mem, name, lv); } /* * Finds the specified layer. */ static struct dev_layer *_lookup(struct dev_manager *dm, const char *lv, const char *layer) { char *name; struct dev_layer *dl; if (!(name = _build_name(dm->mem, dm->vg_name, lv, layer))) { stack; return NULL; } dl = hash_lookup(dm->layers, name); pool_free(dm->mem, name); return dl; } static int _expand_vanilla(struct dev_manager *dm, struct logical_volume *lv) { /* * only one layer. */ struct dev_layer *dl; if (!(dl = _create_layer(dm->mem, NULL, lv))) { stack; return 0; } dl->populate = _populate_vanilla; _set_flag(dl, VISIBLE); if (!hash_insert(dm->layers, dl->name, dl)) { stack; return 0; } return 1; } static int _expand_origin_real(struct dev_manager *dm, struct logical_volume *lv) { struct dev_layer *dl; char *real_name; struct str_list *sl; if (!(dl = _create_layer(dm->mem, "real", lv))) { stack; return 0; } dl->populate = _populate_vanilla; _clear_flag(dl, VISIBLE); if (!hash_insert(dm->layers, dl->name, dl)) { stack; return 0; } real_name = dl->name; if (!(dl = _create_layer(dm->mem, NULL, lv))) { stack; return 0; } dl->populate = _populate_origin; _set_flag(dl, VISIBLE); /* add the dependency on the real device */ if (!(sl = pool_alloc(dm->mem, sizeof(*sl)))) { stack; return 0; } if (!(sl->str = pool_strdup(dm->mem, real_name))) { stack; return 0; } list_add(&dl->pre_create, &sl->list); if (!hash_insert(dm->layers,dl->name, dl)) { stack; return 0; } return 1; } static int _expand_origin(struct dev_manager *dm, struct logical_volume *lv) { struct logical_volume *active; struct snapshot *s; struct list *sh; /* * We only need to create an origin layer if one of our * snapshots is in the active list. */ list_iterate (sh, &dm->active_list) { active = list_item(sh, struct lv_list)->lv; if ((s = find_cow(active)) && (s->origin == lv)) return _expand_origin_real(dm, lv); } return _expand_vanilla(dm, lv); } static int _expand_snapshot(struct dev_manager *dm, struct logical_volume *lv, struct snapshot *s) { /* * snapshot(org, cow) * cow */ struct dev_layer *dl; char *cow_name; struct str_list *sl; if (!(dl = _create_layer(dm->mem, "cow", lv))) { stack; return 0; } dl->populate = _populate_vanilla; _clear_flag(dl, VISIBLE); /* insert the cow layer */ if (!hash_insert(dm->layers, dl->name, dl)) { stack; return 0; } cow_name = dl->name; if (!(dl = _create_layer(dm->mem, NULL, lv))) { stack; return 0; } dl->populate = _populate_snapshot; _set_flag(dl, VISIBLE); /* add the dependency on the real device */ if (!(sl = pool_alloc(dm->mem, sizeof(*sl)))) { stack; return 0; } if (!(sl->str = pool_strdup(dm->mem, cow_name))) { stack; return 0; } list_add(&dl->pre_create, &sl->list); /* add the dependency on the org device */ if (!(sl = pool_alloc(dm->mem, sizeof(*sl)))) { stack; return 0; } if (!(sl->str = _build_name(dm->mem, dm->vg_name, s->origin->name, "real"))) { stack; return 0; } list_add(&dl->pre_create, &sl->list); /* insert the snapshot layer */ if (!hash_insert(dm->layers,dl->name, dl)) { stack; return 0; } return 1; } /* * Inserts the appropriate dev_layers for a logical volume. */ static int _expand_lv(struct dev_manager *dm, struct logical_volume *lv) { struct snapshot *s; /* * FIXME: this doesn't cope with recursive snapshots yet. */ if ((s = find_cow(lv))) return _expand_snapshot(dm, lv, s); else if (lv_is_origin(lv)) return _expand_origin(dm, lv); return _expand_vanilla(dm, lv); } /* * Clears the mark bit on all layers. */ static void _clear_marks(struct dev_manager *dm, int flag) { struct hash_node *hn; struct dev_layer *dl; hash_iterate (hn, dm->layers) { dl = hash_get_data(dm->layers, hn); _clear_flag(dl, flag); } } /* * Propogates marks via the pre_create dependency list. */ static int _trace_layer_marks(struct dev_manager *dm, struct dev_layer *dl, int flag) { struct list *sh; char *name; struct dev_layer *dep; list_iterate (sh, &dl->pre_create) { name = list_item(sh, struct str_list)->str; if (!(dep = hash_lookup(dm->layers, name))) { log_err("Couldn't find device layer '%s'.", name); return 0; } if (_get_flag(dep, flag)) continue; _set_flag(dep, flag); if (!_trace_layer_marks(dm, dep, flag)) { stack; return 0; } } return 1; } /* * Calls _trace_single for every marked layer. */ static int _trace_all_marks(struct dev_manager *dm, int flag) { struct hash_node *hn; struct dev_layer *dl; hash_iterate (hn, dm->layers) { dl = hash_get_data(dm->layers, hn); if (_get_flag(dl, flag) && !_trace_layer_marks(dm, dl, flag)) { stack; return 0; } } return 1; } /* * Marks the top layers, then traces these through the * dependencies. */ static int _mark_lvs(struct dev_manager *dm, struct list *lvs, int flag) { struct list *lvh; struct logical_volume *lv; struct dev_layer *dl; list_iterate (lvh, lvs) { lv = list_item(lvh, struct lv_list)->lv; if (!(dl = _lookup(dm, lv->name, NULL))) { stack; return 0; } _set_flag(dl, flag); } if (!_trace_all_marks(dm, flag)) { stack; return 0; } return 1; } /* * Recurses through the tree, ensuring that devices are created * in correct order. */ int _create_rec(struct dev_manager *dm, struct dev_layer *dl) { struct list *sh; struct dev_layer *dep; char *name; if (dl->info.exists && !_suspend(dl)) { stack; return 0; } list_iterate (sh, &dl->pre_create) { name = list_item(sh, struct str_list)->str; if (!(dep = hash_lookup(dm->layers, name))) { log_err("Couldn't find device layer '%s'.", name); return 0; } if (!_create_rec(dm, dep)) { stack; return 0; } } if (dl->info.exists) { /* reload */ if (!_load(dm, dl, DM_DEVICE_RELOAD)) { stack; return 0; } if (!_resume(dl)) { stack; return 0; } } else { /* create */ if (!_load(dm, dl, DM_DEVICE_CREATE)) { stack; return 0; } } return 1; } static int _build_all_layers(struct dev_manager *dm, struct volume_group *vg) { struct list *lvh; struct logical_volume *lvt; /* * Build layers for complete vg. */ list_iterate (lvh, &vg->lvs) { lvt = list_item(lvh, struct lv_list)->lv; if (!_expand_lv(dm, lvt)) { stack; return 0; } } return 1; } static int _fill_in_remove_list(struct dev_manager *dm) { struct hash_node *hn; struct dev_layer *dl; struct dl_list *dll; hash_iterate (hn, dm->layers) { dl = hash_get_data(dm->layers, hn); if (!_get_flag(dl, ACTIVE)) { dll = pool_alloc(dm->mem, sizeof(*dll)); if (!dll) { stack; return 0; } dll->dl = dl; list_add(&dm->remove_list, &dll->list); } } return 1; } /* * Layers are removed in a top-down manner. */ int _remove_old_layers(struct dev_manager *dm) { int change; /* * FIXME: quick hack. We just loop round removing * unopened devices, until we run out of things to close. */ do { struct list *rh, *n; struct dev_layer *dl; change = 0; for (rh = dm->remove_list.n; rh != &dm->remove_list; rh = n) { n = rh->n; dl = list_item(rh, struct dl_list)->dl; if (!_info(dl->name, &dl->info)) { stack; return 0; } if (dl->info.exists && !dl->info.open_count) { change = 1; if (!_remove(dl)) { stack; return 0; } list_del(rh); } } } while(change); if (!list_empty(&dm->remove_list)) { log_err("Couldn't remove all redundant layers."); return 0; } return 1; } /* * The guts of the activation unit, this examines the device * layers in the manager, and tries to issue the correct * instructions to activate them in order. */ static int _execute(struct dev_manager *dm, struct volume_group *vg) { struct hash_node *hn; struct dev_layer *dl; if (!_build_all_layers(dm, vg)) { stack; return 0; } /* * Mark all dirty layers. */ _clear_marks(dm, DIRTY); if (!_mark_lvs(dm, &dm->dirty_list, DIRTY)) { stack; return 0; } /* * Mark all active layers. */ _clear_marks(dm, ACTIVE); if (!_mark_lvs(dm, &dm->active_list, ACTIVE)) { stack; return 0; } if (!_fill_in_remove_list(dm)) { stack; return 0; } /* * Now only top level devices will be unmarked. */ hash_iterate (hn, dm->layers) { dl = hash_get_data(dm->layers, hn); if (_get_flag(dl, ACTIVE) && _get_flag(dl, VISIBLE)) _create_rec(dm, dl); } if (!_remove_old_layers(dm)) { stack; return 0; } return 1; } /* * ATM we decide which vg a layer belongs to by * looking at the beginning of the device * name. */ static int _belong_to_vg(const char *vg, const char *name) { /* * FIXME: broken for vg's with '-'s in. */ return !strncmp(vg, name, strlen(vg)); } static int _add_existing_layer(struct dev_manager *dm, const char *name) { struct dev_layer *new; char *copy; log_verbose("Found layer '%s'", name); if (!(copy = pool_strdup(dm->mem, name))) { stack; return 0; } if (!(new = _create_dev(dm->mem, copy, NULL))) { stack; return 0; } if (!_info(new->name, &new->info)) { stack; return 0; } if (!hash_insert(dm->layers, new->name, new)) { stack; return 0; } return 1; } static int _scan_existing_devices(struct dev_manager *dm) { const char *dev_dir = dm_dir(); int i, count, r = 1; struct dirent **dirent; const char *name; count = scandir(dev_dir, &dirent, NULL, alphasort); if (!count) return 1; if (count < 0) { log_err("Couldn't scan device-mapper directory '%s'.", dev_dir); return 0; } /* * Scan the devices. */ for (i = 0; i < count; i++) { name = dirent[i]->d_name; /* * Ignore dot files. */ if (name[0] == '.') continue; /* * Does this layer belong to us ? */ if (_belong_to_vg(dm->vg_name, name) && !_add_existing_layer(dm, name)) { stack; r = 0; break; } } /* * Free the directory entries. */ for (i = 0; i < count; i++) free(dirent[i]); free(dirent); return r; } static int _add_lv(struct pool *mem, struct list *head, struct logical_volume *lv) { struct lv_list *lvl; if (!(lvl = pool_alloc(mem, sizeof(*lvl)))) { stack; return 0; } lvl->lv = lv; list_add(head, &lvl->list); return 1; } static void _remove_lv(struct list *head, struct logical_volume *lv) { struct list *lvh; struct lv_list *lvl; list_iterate (lvh, head) { lvl = list_item(lvh, struct lv_list); if (lvl->lv == lv) { list_del(lvh); break; } } } static int _fill_in_active_list(struct dev_manager *dm, struct volume_group *vg) { int found; char *name; struct list *lvh; struct logical_volume *lv; list_iterate (lvh, &vg->lvs) { lv = list_item(lvh, struct lv_list)->lv; name = _build_name(dm->mem, dm->vg_name, lv->name, NULL); if (!name) { stack; return 0; } found = hash_lookup(dm->layers, name) ? 1 : 0; pool_free(dm->mem, name); if (found) { log_verbose("Active lv '%s' found.", lv->name); if (!_add_lv(dm->mem, &dm->active_list, lv)) { stack; return 0; } } } return 1; } /* * FIXME: There's a lot of common code between activate and * deactivate. */ int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv) { if (!_scan_existing_devices(dm)) { stack; return 0; } if (!_fill_in_active_list(dm, lv->vg)) { stack; return 0; } /* * Remove it so we don't add it twice. */ _remove_lv(&dm->active_list, lv); if (!_add_lv(dm->mem, &dm->dirty_list, lv) || !_add_lv(dm->mem, &dm->active_list, lv)) { stack; return 0; } if (!_execute(dm, lv->vg)) { stack; return 0; } return 1; } int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv) { if (!_scan_existing_devices(dm)) { stack; return 0; } if (!_fill_in_active_list(dm, lv->vg)) { stack; return 0; } _remove_lv(&dm->active_list, lv); if (!_execute(dm, lv->vg)) { stack; return 0; } return 0; }