diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c new file mode 100644 index 000000000..ee07a0609 --- /dev/null +++ b/lib/activate/dev_manager.c @@ -0,0 +1,492 @@ +/* + * 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" + + +/* layer types */ +enum { + VANILLA, + ORIGIN, + SNAPSHOT +}; + +struct dev_layer { + char *name; + int mark; + + /* + * Setup the dm_task. + */ + int type; + int exists; + int suspended; + 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 *layer; +}; + + +struct dev_manager { + struct pool *mem; + + char *vg_name; + struct hash_table *layers; +}; + +static void _count_colons(const char *str, size_t *len, int *colons) +{ + const char *ptr; + + for (ptr = str; *ptr; ptr++, (*len)++) + if (*ptr == ':') + (*colons)++; +} + +/* + * Copies a string, quoting colons with colons. + */ +static void _quote_colons(char **out, const char *src) +{ + while (*src) { + if (*src == ':') + *(*out)++ = ':'; + + *(*out)++ = *src++; + } +} + +static char *_build_name(struct pool *mem, const char *vg, + const char *lv, const char *layer) +{ + size_t len = 0; + int colons = 0; + char *r, *out; + + _count_colons(vg, &len, &colons); + _count_colons(lv, &len, &colons); + _count_colons(layer, &len, &colons); + + len += colons + 1; + + if (!(r = pool_alloc(mem, len))) { + stack; + return NULL; + } + + out = r; + _quote_colons(&out, vg); + _quote_colons(&out, lv); + _quote_colons(&out, layer); + *out = '\0'; + + return r; +} + +/* + * 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; + } + + return dm; + + bad: + pool_destroy(mem); + return NULL; +} + +void dev_manager_destroy(struct dev_manager *dm) +{ + hash_destroy(dm->layers); + pool_destroy(dm->mem); +} + +static struct dev_layer * +_create_layer(struct pool *mem, const char *layer, + int type, struct logical_volume *lv) +{ + struct dev_layer *dl; + + if (!(dl = pool_zalloc(mem, sizeof(*dl)))) { + stack; + return NULL; + } + + if (!(dl->name = _build_name(mem, lv->vg->name, lv->name, layer))) { + stack; + return NULL; + } + + dl->type = type; + dl->suspended = 0; + dl->lv = lv; + + return dl; +} + +/* + * 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; +} + + +/* + * Inserts the 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))) { + /* + * snapshot(org, cow) + * cow + */ + log_err("Snapshot devices not supported yet."); + return 0; + + + } else if (lv_is_origin(lv)) { + /* + * origin(org) + * org + */ + log_err("Origin devices not supported yet."); + return 0; + + } else { + /* + * only one layer. + */ + struct dev_layer *dl; + if (!(dl = _create_layer(dm->mem, "top", VANILLA, lv))) { + stack; + return 0; + } + + if (!hash_insert(dm->layers, dl->name, dl)) { + stack; + return 0; + } + } + + return 1; +} + +#if 0 +static int _get_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_dm_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; +} +#endif + +/* + * Clears the mark bit on all layers. + */ +static void _clear_marks(struct dev_manager *dm) +{ + struct hash_node *hn; + struct dev_layer *dl; + + hash_iterate (hn, dm->layers) { + dl = hash_get_data(dm->layers, hn); + dl->mark = 0; + } +} + + +/* + * Starting with a given layer this function recurses through all + * dependent layers setting the mark bit. + */ +static int _mark_pre_create(struct dev_manager *dm, struct dev_layer *dl) +{ + 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 (dep->mark) + continue; + + dep->mark = 1; + + if (!_mark_pre_create(dm, dep)) { + stack; + return 0; + } + } + + return 1; +} + +void _emit(struct dev_layer *dl) +{ + log_print("emitting layer '%s'", dl->name); +} + +/* + * Recurses through the tree, ensuring that devices are created + * in correct order. + */ +int _create(struct dev_layer *dl) +{ + struct list *sh; + struct dev_layer *dep; + char *name; + + if (dl->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(dep)) { + stack; + return 0; + } + } + + if (dl->exists) { + /* reload */ + if (!_reload(dl)) { + stack; + return 0; + } + + if (!_resume(dl)) { + stack; + return 0; + } + } else { + /* create */ + if (!_create(dl)) { + stack; + return 0; + } + } + + return 1; +} + +/* + * The guts of the activation unit, this examines the device + * layers in the manager, and tries to issue the correct activate + * them in the correct order. + */ +static int _execute(struct dev_manager *dm) +{ + struct hash_node *hn; + struct dev_layer *dl; + + /* + * We need to make a list of top level devices, ie. those + * that have no entries in 'pre_create'. + */ + _clear_marks(dm); + + /* + * Mark any dependents. + */ + hash_iterate (hn, dm->layers) { + dl = hash_get_data(dm->layers, hn); + + if (!dl->mark) { + if (!_mark_pre_create(dm, dl)) { + stack; + return 0; + } + + if (dl->mark) { + log_err("Circular device dependency found for '%s'.", + dl->name); + return 0; + } + } + } + + /* + * Now only top level devices will be unmarked. + */ + hash_iterate (hn, dm->layers) { + dl = hash_get_data(dm->layers, hn); + + if (!dl->mark) + _emit(dl); + } + + return 1; +} + + +/* + * Remove all layers from the hash table that do not have their + * mark flag set. + */ +static int _prune_unmarked(struct dev_manager *dm) +{ + struct hash_node *hn; + struct dev_layer *dl; + + for (hn = hash_get_first(dm->layers); hn; + hn = hash_get_next(dm->layers, hn)) { + dl = hash_get_data(dm->layers, hn); + + if (!dl->mark) + hash_remove(dm->layers, dl->name); + } + + return 1; +} + +int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv) +{ + struct dev_layer *dl; + struct list *lvh; + + /* + * Build layers for complete vg. + */ + list_iterate (lvh, &lv->vg->lvs) { + lv = list_item(lvh, struct lv_list)->lv; + if (!_expand_lv(dm, lv)) { + stack; + return 0; + } + } + + /* + * Mark the desired logical volume. + */ + dl = _lookup(dm, lv->name, "top"); + dl->mark = 1; + if (!_mark_pre_create(dm, dl)) { + stack; + return 0; + } + + _prune_unmarked(dm); + + /* + * Now we are just left with the layers required to + * implement the lv. + */ + + if (!_execute(dm)) { + stack; + return 0; + } + + return 1; +} + +int dev_manager_reactivate(struct dev_manager *dm, struct logical_volume *lv) +{ + log_err("dev_manager_reactivate not implemented."); + return 0; +} + +int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv) +{ + log_err("dev_manager_reactivate not implemented."); + return 0; +} + diff --git a/lib/activate/dev_manager.h b/lib/activate/dev_manager.h new file mode 100644 index 000000000..ee0149afc --- /dev/null +++ b/lib/activate/dev_manager.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2002 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef _LVM_DEV_MANAGER_H +#define _LVM_DEV_MANAGER_H + +#include "metadata.h" + +struct dev_manager; + +/* + * Constructor and destructor. + */ +struct dev_manager *dev_manager_create(const char *vg_name); +void dev_manager_destroy(struct dev_manager *dm); + +/* + * The device handler is responsible for creating all the layered + * dm devices, and ensuring that all constraints are maintained + * (eg, an origin is created before its snapshot, but is not + * unsuspended until the snapshot is also created.) + */ +int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv); +int dev_manager_reactivate(struct dev_manager *dm, struct logical_volume *lv); +int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv); + + +/* + * Put the desired changes into effect. + */ +int dev_manager_execute(struct dev_manager *dm); + +#endif