From 18babdc3ac61af614b2c5e4751ae429f9aaaa5a7 Mon Sep 17 00:00:00 2001 From: David Teigland Date: Wed, 18 Sep 2024 14:41:52 -0500 Subject: [PATCH] vg: copy alternative to reimporting One vg struct is copied to another vg struct, rather than importing vg metadata text to create the new vg struct. Enable with global/vg_copy_internal="binary" --- lib/commands/toolcontext.c | 7 + lib/commands/toolcontext.h | 1 + lib/config/config_settings.h | 7 + lib/config/defaults.h | 2 + lib/format_text/format-text.c | 35 +- lib/metadata/lv_manip.c | 14 +- lib/metadata/metadata-exported.h | 19 + lib/metadata/metadata.c | 2 + lib/metadata/vg.c | 859 +++++++++++++++++++++++++++++++ 9 files changed, 937 insertions(+), 9 deletions(-) diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c index 60739af8f..44e4dc998 100644 --- a/lib/commands/toolcontext.c +++ b/lib/commands/toolcontext.c @@ -659,6 +659,7 @@ static int _process_config(struct cmd_context *cmd) mode_t old_umask; const char *dev_ext_info_src = NULL; const char *read_ahead; + const char *str; struct stat st; const struct dm_config_node *cn; const struct dm_config_value *cv; @@ -816,6 +817,12 @@ static int _process_config(struct cmd_context *cmd) cmd->check_pv_dev_sizes = find_config_tree_bool(cmd, metadata_check_pv_device_sizes_CFG, NULL); cmd->event_activation = find_config_tree_bool(cmd, global_event_activation_CFG, NULL); + + if ((str = find_config_tree_str(cmd, global_vg_copy_internal_CFG, NULL))) { + if (!strcmp(str, "binary")) + cmd->vg_copy_binary = 1; + } + if (!process_profilable_config(cmd)) return_0; diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index b12f5c37b..66c0dcf75 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -218,6 +218,7 @@ struct cmd_context { unsigned device_ids_invalid:1; unsigned device_ids_auto_import:1; unsigned get_vgname_from_options:1; /* used by lvconvert */ + unsigned vg_copy_binary:1; /* * Devices and filtering. diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h index c99f9143e..68b934c39 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h @@ -1387,6 +1387,13 @@ cfg(global_io_memory_size_CFG, "io_memory_size", global_CFG_SECTION, CFG_DEFAULT "This value should usually not be decreased from the default; setting\n" "it too low can result in lvm failing to read VGs.\n") +cfg(global_vg_copy_internal_CFG, "vg_copy_internal", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_VG_COPY_INTERNAL, vsn(2, 3, 27), NULL, 0, NULL, + "The method that lvm uses for internal VG structure copying.\n" + "\"binary\" copies between binary structures to improve performance\n" + "with large metadata (experimental.) \"text\" exports a binary\n" + "struct to text format, and reimports text to a new binary\n" + "structure (traditional.)\n") + cfg(activation_udev_sync_CFG, "udev_sync", activation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_UDEV_SYNC, vsn(2, 2, 51), NULL, 0, NULL, "Use udev notifications to synchronize udev and LVM.\n" "The --noudevsync option overrides this setting.\n" diff --git a/lib/config/defaults.h b/lib/config/defaults.h index 54dba8191..6ececa3a5 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -344,4 +344,6 @@ #define DEFAULT_DEVICESFILE_BACKUP_LIMIT 50 +#define DEFAULT_VG_COPY_INTERNAL "text" + #endif /* _LVM_DEFAULTS_H */ diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c index d2c99f6bb..3228df929 100644 --- a/lib/format_text/format-text.c +++ b/lib/format_text/format-text.c @@ -647,15 +647,36 @@ static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg, * * 'Lazy' creation of such VG might improve performance, but we * lose important validation that written metadata can be parsed. */ - if (!(cft = config_tree_from_string_without_dup_node_check(write_buf))) { - log_error("Error parsing metadata for VG %s.", vg->name); - goto out; - } + release_vg(vg->vg_precommitted); - vg->vg_precommitted = import_vg_from_config_tree(vg->cmd, vg->fid, cft); - dm_config_destroy(cft); - if (!vg->vg_precommitted) + vg->vg_precommitted = NULL; + + if (!vg->cmd->vg_copy_binary) { + if (!(cft = config_tree_from_string_without_dup_node_check(write_buf))) { + log_error("Error parsing metadata for VG %s.", vg->name); + goto out; + } + vg->vg_precommitted = import_vg_from_config_tree(vg->cmd, vg->fid, cft); + dm_config_destroy(cft); + } else { + vg->vg_precommitted = vg_copy_struct(vg); + + if (!vg->vg_precommitted) { + log_debug("vg_copy_struct failed, trying text import."); + if (!(cft = config_tree_from_string_without_dup_node_check(write_buf))) { + log_error("Error parsing metadata for VG %s.", vg->name); + goto out; + } + vg->vg_precommitted = import_vg_from_config_tree(vg->cmd, vg->fid, cft); + dm_config_destroy(cft); + } + } + if (!vg->vg_precommitted) { + log_error("Failed to copy vg struct."); goto_out; + } + + log_debug("Saved vg struct %p as precommitted", vg->vg_precommitted); fidtc->checksum = checksum = calc_crc(INITIAL_CRC, (uint8_t *)write_buf, new_size); } diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index a0cdab127..bee112593 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -9569,8 +9569,10 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, if (seg_is_raid(lp) && lp->raidintegrity) { log_debug("Adding integrity to new LV"); - if (!lv_add_integrity_to_raid(lv, &lp->integrity_settings, lp->pvh, NULL)) + if (!lv_add_integrity_to_raid(lv, &lp->integrity_settings, lp->pvh, NULL)) { + stack; goto revert_new_lv; + } } /* Do not scan this LV until properly zeroed/wiped. */ @@ -9643,6 +9645,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, first_seg(pool_lv)->transaction_id = seg->transaction_id; first_seg(lv)->device_id = 0; /* no delete of never existing thin device */ } + stack; goto revert_new_lv; } /* At this point remove pool messages, snapshot is active */ @@ -9798,6 +9801,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, return_NULL; } + stack; goto deactivate_and_revert_new_lv; } } else if (lp->snapshot) { @@ -9819,8 +9823,10 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, if (lp->virtual_extents && !(origin_lv = _create_virtual_origin(cmd, vg, lv->name, (lp->permission & ~LVM_WRITE), - lp->virtual_extents))) + lp->virtual_extents))) { + stack; goto revert_new_lv; + } /* Reset permission after zeroing */ if (!(lp->permission & LVM_WRITE)) @@ -9862,9 +9868,12 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, } } out: + if (!lv) + log_debug("No LV created."); return lv; deactivate_and_revert_new_lv: + log_debug("Deactivate and revert new lv"); if (!sync_local_dev_names(lv->vg->cmd)) log_error("Failed to sync local devices before reverting %s.", display_lvname(lv)); @@ -9875,6 +9884,7 @@ deactivate_and_revert_new_lv: } revert_new_lv: + log_debug("Revert new lv"); if (!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT)) log_warn("WARNING: Failed to unlock %s.", display_lvname(lv)); lockd_free_lv(vg->cmd, vg, lv->name, &lv->lvid.id[1], lv->lock_args); diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 0d42b4464..6f61b2875 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -1439,6 +1439,21 @@ char *top_level_lv_name(struct volume_group *vg, const char *lv_name); struct generic_logical_volume *get_or_create_glv(struct dm_pool *mem, struct logical_volume *lv, int *glv_created); struct glv_list *get_or_create_glvl(struct dm_pool *mem, struct logical_volume *lv, int *glv_created); +struct logical_volume *get_data_from_pool(struct logical_volume *pool_lv); +struct logical_volume *get_meta_from_pool(struct logical_volume *pool_lv); +struct logical_volume *get_pool_from_thin(struct logical_volume *thin_lv); +struct logical_volume *get_pool_from_cache(struct logical_volume *cache_lv); +struct logical_volume *get_pool_from_vdo(struct logical_volume *vdo_lv); +struct logical_volume *get_origin_from_cache(struct logical_volume *cache_lv); +struct logical_volume *get_origin_from_writecache(struct logical_volume *writecache_lv); +struct logical_volume *get_origin_from_integrity(struct logical_volume *integrity_lv); +struct logical_volume *get_origin_from_thin(struct logical_volume *thin_lv); +struct logical_volume *get_merge_lv_from_thin(struct logical_volume *thin_lv); +struct logical_volume *get_external_lv_from_thin(struct logical_volume *thin_lv); +struct logical_volume *get_origin_from_snap(struct logical_volume *snap_lv); +struct logical_volume *get_cow_from_snap(struct logical_volume *snap_lv); +struct logical_volume *get_fast_from_writecache(struct logical_volume *writecache_lv); + /* * Begin skeleton for external LVM library */ @@ -1514,4 +1529,8 @@ int lv_raid_integrity_total_mismatches(struct cmd_context *cmd, const struct log int setting_str_list_add(const char *field, uint64_t val, char *val_str, struct dm_list *result, struct dm_pool *mem); +struct volume_group *vg_copy_struct(struct volume_group *vgo); + +void insert_segment(struct logical_volume *lv, struct lv_segment *seg); + #endif diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index afe224bf2..2d22100ae 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -4346,6 +4346,8 @@ const struct logical_volume *lv_committed(const struct logical_volume *lv) found_lv = lv; /* Use uncommitted LV as best effort */ } + log_debug("lv_committed %s from vg_committed %p", display_lvname(found_lv), vg); + return found_lv; } diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c index 8e7fb41a6..c8e3a6e26 100644 --- a/lib/metadata/vg.c +++ b/lib/metadata/vg.c @@ -15,10 +15,14 @@ #include "lib/misc/lib.h" #include "lib/metadata/metadata.h" +#include "lib/metadata/lv_alloc.h" +#include "lib/metadata/segtype.h" +#include "lib/metadata/pv_alloc.h" #include "lib/display/display.h" #include "lib/activate/activate.h" #include "lib/commands/toolcontext.h" #include "lib/format_text/archiver.h" +#include "lib/datastruct/str_list.h" struct volume_group *alloc_vg(const char *pool_name, struct cmd_context *cmd, const char *vg_name) @@ -757,3 +761,858 @@ void vg_backup_if_needed(struct volume_group *vg) vg->needs_backup = 0; backup(vg->vg_committed); } + +void insert_segment(struct logical_volume *lv, struct lv_segment *seg) +{ + struct lv_segment *comp; + + dm_list_iterate_items(comp, &lv->segments) { + if (comp->le > seg->le) { + dm_list_add(&comp->list, &seg->list); + return; + } + } + + lv->le_count += seg->len; + dm_list_add(&lv->segments, &seg->list); +} + +struct logical_volume *get_data_from_pool(struct logical_volume *pool_lv) +{ + /* works for cache pool, thin pool, vdo pool */ + /* first_seg() = dm_list_first_entry(&lv->segments) */ + /* seg_lv(seg, n) = seg->areas[n].u.lv.lv */ + return seg_lv(first_seg(pool_lv), 0); +} + +struct logical_volume *get_meta_from_pool(struct logical_volume *pool_lv) +{ + /* works for cache pool, thin pool, vdo pool */ + /* first_seg() = dm_list_first_entry(&lv->segments) */ + /* seg_lv(seg, n) = seg->areas[n].u.lv.lv */ + return first_seg(pool_lv)->metadata_lv; +} + +struct logical_volume *get_pool_from_thin(struct logical_volume *thin_lv) +{ + return first_seg(thin_lv)->pool_lv; +} + +struct logical_volume *get_pool_from_cache(struct logical_volume *cache_lv) +{ + return first_seg(cache_lv)->pool_lv; +} + +struct logical_volume *get_pool_from_vdo(struct logical_volume *vdo_lv) +{ + return seg_lv(first_seg(vdo_lv), 0); +} + +struct logical_volume *get_origin_from_cache(struct logical_volume *cache_lv) +{ + return seg_lv(first_seg(cache_lv), 0); +} + +struct logical_volume *get_origin_from_writecache(struct logical_volume *writecache_lv) +{ + return seg_lv(first_seg(writecache_lv), 0); +} + +struct logical_volume *get_origin_from_integrity(struct logical_volume *integrity_lv) +{ + return seg_lv(first_seg(integrity_lv), 0); +} + +struct logical_volume *get_origin_from_thin(struct logical_volume *thin_lv) +{ + return first_seg(thin_lv)->origin; +} + +struct logical_volume *get_merge_lv_from_thin(struct logical_volume *thin_lv) +{ + return first_seg(thin_lv)->merge_lv; +} + +struct logical_volume *get_external_lv_from_thin(struct logical_volume *thin_lv) +{ + return first_seg(thin_lv)->external_lv; +} + +struct logical_volume *get_origin_from_snap(struct logical_volume *snap_lv) +{ + return first_seg(snap_lv)->origin; +} + +struct logical_volume *get_cow_from_snap(struct logical_volume *snap_lv) +{ + return first_seg(snap_lv)->cow; +} + +struct logical_volume *get_fast_from_writecache(struct logical_volume *writecache_lv) +{ + return first_seg(writecache_lv)->writecache; +} + +/* + * When reading from text: + * - pv comes from looking up the "pv0" key in pv_hash + * - pe comes from text field + * - pv and pe are passed to set_lv_segment_area_pv() to + * create the pv_segment structs, and connect them to + * the lv_segment. + * + * When copying the struct: + * - pv comes from looking up the pv id in vg->pvs + * - pe comes from the original pvseg struct + * - pv and pe are passed to set_lv_segment_area_pv() to + * create the pv_segment structs, and connect them to + * the lv_segment (same as when reading from text.) + * + * set_lv_segment_area_pv(struct lv_segment *seg, uint32_t s, + * struct physical_volume *pv, uint32_t pe); + * does: + * + * seg_pvseg(seg, s) = + * assign_peg_to_lvseg(pv, pe, seg->area_len, seg, s); + * + * does: + * + * seg->areas[s].u.pv.pvseg = + * assign_peg_to_lvseg(pv, pe, area_len, seg, s); + * + * struct pv_segment *assign_peg_to_lvseg(struct physical_volume *pv, + * uint32_t pe, uint32_t area_len, + * struct lv_segment *seg, uint32_t s); + * + * This does multiple things: + * 1. creates pv_segment and connects it to lv_segment + * 2. creates pv->segments list of all pv_segments on the pv + * 3. updates pv->pe_alloc_count, vg->free_count + */ + +static int _areas_copy_struct(struct volume_group *vg, + struct logical_volume *lv, + struct lv_segment *seg, + struct volume_group *vgo, + struct logical_volume *lvo, + struct lv_segment *sego, + struct dm_hash_table *pv_hash, + struct dm_hash_table *lv_hash) +{ + uint32_t s; + + /* text_import_areas */ + + for (s = 0; s < sego->area_count; s++) { + seg->areas[s].type = sego->areas[s].type; + + if (sego->areas[s].type == AREA_PV) { + struct physical_volume *area_pvo; + struct physical_volume *area_pv; + + if (!(area_pvo = sego->areas[s].u.pv.pvseg->pv)) + goto_bad; + if (!(area_pv = dm_hash_lookup_binary(pv_hash, &area_pvo->id, ID_LEN))) + goto_bad; + if (!set_lv_segment_area_pv(seg, s, area_pv, sego->areas[s].u.pv.pvseg->pe)) + goto_bad; + + } else if (sego->areas[s].type == AREA_LV) { + struct logical_volume *area_lvo; + struct logical_volume *area_lv; + + if (!(area_lvo = sego->areas[s].u.lv.lv)) + goto_bad; + if (!(area_lv = dm_hash_lookup(lv_hash, area_lvo->name))) + goto_bad; + if (!set_lv_segment_area_lv(seg, s, area_lv, sego->areas[s].u.lv.le, 0)) + goto_bad; + } + } + + return 1; +bad: + return 0; +} + +static int _thin_messages_copy_struct(struct volume_group *vgo, struct volume_group *vg, + struct logical_volume *lvo, struct logical_volume *lv, + struct lv_segment *sego, struct lv_segment *seg, + struct dm_hash_table *lv_hash) +{ + struct lv_thin_message *mso; + struct lv_thin_message *ms; + struct logical_volume *ms_lvo; + struct logical_volume *ms_lv; + + if (dm_list_empty(&sego->thin_messages)) + return 1; + + dm_list_iterate_items(mso, &sego->thin_messages) { + if (!(ms = dm_pool_alloc(vg->vgmem, sizeof(*ms)))) + goto_bad; + + ms->type = mso->type; + + switch (ms->type) { + case DM_THIN_MESSAGE_CREATE_SNAP: + case DM_THIN_MESSAGE_CREATE_THIN: + if (!(ms_lvo = mso->u.lv)) + goto_bad; + if (!(ms_lv = dm_hash_lookup(lv_hash, ms_lvo->name))) + goto_bad; + ms->u.lv = ms_lv; + break; + case DM_THIN_MESSAGE_DELETE: + ms->u.delete_id = mso->u.delete_id; + break; + default: + break; + } + + dm_list_add(&seg->thin_messages, &ms->list); + } + return 1; +bad: + return 0; +} + +static struct lv_segment *_seg_copy_struct(struct volume_group *vg, + struct logical_volume *lv, + struct volume_group *vgo, + struct logical_volume *lvo, + struct lv_segment *sego, + struct dm_hash_table *pv_hash, + struct dm_hash_table *lv_hash) +{ + struct dm_pool *mem = vg->vgmem; + struct lv_segment *seg; + uint32_t s; + + if (!(seg = dm_pool_zalloc(mem, sizeof(*seg)))) + return_NULL; + + if (sego->area_count && sego->areas && + !(seg->areas = dm_pool_zalloc(mem, sego->area_count * sizeof(*seg->areas)))) + return_NULL; + + /* + * This is a more accurate copy of the original segment: + * if (sego->area_count && sego->meta_areas && + * !(seg->meta_areas = dm_pool_zalloc(mem, sego->area_count * sizeof(*seg->meta_areas)))) + * return_NULL; + * + * But it causes a segfault in for_each_sub_lv, which seems to want meta_areas allocated + * in the copy even when it's null in the original. So, this copies alloc_lv_segment + * which always allocates meta_areas. + */ + if (segtype_is_raid_with_meta(sego->segtype)) { + if (!(seg->meta_areas = dm_pool_zalloc(mem, sego->area_count * sizeof(*seg->meta_areas)))) + return_NULL; + } + + /* see _read_segment, alloc_lv_segment */ + + dm_list_init(&seg->tags); + dm_list_init(&seg->origin_list); + dm_list_init(&seg->thin_messages); + + seg->lv = lv; + + seg->segtype = sego->segtype; + seg->le = sego->le; + seg->len = sego->len; + seg->status = sego->status; + seg->area_count = sego->area_count; + seg->area_len = sego->area_len; + + if (!dm_list_empty(&sego->tags) && !str_list_dup(mem, &seg->tags, &sego->tags)) + goto_bad; + + /* + * _read_segment, ->text_import(), i.e. _foo_text_import() + */ + + if (seg_is_striped_target(sego)) { + + /* see _striped_text_import, N.B. not "seg_is_striped" */ + + seg->stripe_size = sego->stripe_size; + + if (!_areas_copy_struct(vg, lv, seg, vgo, lvo, sego, pv_hash, lv_hash)) + goto_bad; + + } else if (seg_is_cache_pool(sego)) { + struct logical_volume *data_lvo; + struct logical_volume *meta_lvo; + struct logical_volume *data_lv; + struct logical_volume *meta_lv; + + /* see _cache_pool_text_import */ + + seg->cache_metadata_format = sego->cache_metadata_format; + seg->chunk_size = sego->chunk_size; + seg->cache_mode = sego->cache_mode; + + if (sego->policy_name) + seg->policy_name = dm_pool_strdup(mem, sego->policy_name); + if (sego->policy_settings) + seg->policy_settings = dm_config_clone_node_with_mem(mem, sego->policy_settings, 0); + + if (!(data_lvo = get_data_from_pool(lvo))) + goto_bad; + if (!(meta_lvo = get_meta_from_pool(lvo))) + goto_bad; + if (!(data_lv = dm_hash_lookup(lv_hash, data_lvo->name))) + goto_bad; + if (!(meta_lv = dm_hash_lookup(lv_hash, meta_lvo->name))) + goto_bad; + if (!attach_pool_data_lv(seg, data_lv)) + goto_bad; + if (!attach_pool_metadata_lv(seg, meta_lv)) + goto_bad; + + } else if (seg_is_cache(sego)) { + struct logical_volume *pool_lvo; + struct logical_volume *origin_lvo; + struct logical_volume *pool_lv; + struct logical_volume *origin_lv; + + /* see _cache_text_import */ + + seg->cache_metadata_format = sego->cache_metadata_format; + seg->chunk_size = sego->chunk_size; + seg->cache_mode = sego->cache_mode; + + if (sego->policy_name) + seg->policy_name = dm_pool_strdup(mem, sego->policy_name); + if (sego->policy_settings) + seg->policy_settings = dm_config_clone_node_with_mem(mem, sego->policy_settings, 0); + + seg->cleaner_policy = sego->cleaner_policy; + seg->metadata_start = sego->metadata_start; + seg->metadata_len = sego->metadata_len; + seg->data_start = sego->data_start; + seg->data_len = sego->data_len; + + if (sego->metadata_id) { + if (!(seg->metadata_id = dm_pool_zalloc(mem, sizeof(struct id)))) + goto_bad; + memcpy(seg->metadata_id, sego->metadata_id, sizeof(struct id)); + } + if (sego->data_id) { + if (!(seg->data_id = dm_pool_zalloc(mem, sizeof(struct id)))) + goto_bad; + memcpy(seg->data_id, sego->data_id, sizeof(struct id)); + } + + if (!(pool_lvo = get_pool_from_cache(lvo))) + goto_bad; + if (!(origin_lvo = get_origin_from_cache(lvo))) + goto_bad; + if (!(pool_lv = dm_hash_lookup(lv_hash, pool_lvo->name))) + goto_bad; + if (!(origin_lv = dm_hash_lookup(lv_hash, origin_lvo->name))) + goto_bad; + if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0)) + goto_bad; + if (!attach_pool_lv(seg, pool_lv, NULL, NULL, NULL)) + goto_bad; + + } else if (seg_is_integrity(sego)) { + struct logical_volume *origin_lvo; + struct logical_volume *origin_lv; + struct logical_volume *meta_lvo; + struct logical_volume *meta_lv; + const char *hash; + + /* see _integrity_text_import */ + + if (!(origin_lvo = get_origin_from_integrity(lvo))) + goto_bad; + if (!(origin_lv = dm_hash_lookup(lv_hash, origin_lvo->name))) + goto_bad; + if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0)) + goto_bad; + seg->origin = origin_lv; + + if ((meta_lvo = sego->integrity_meta_dev)) { + if (!(meta_lv = dm_hash_lookup(lv_hash, meta_lvo->name))) + goto_bad; + seg->integrity_meta_dev = meta_lv; + if (!add_seg_to_segs_using_this_lv(meta_lv, seg)) + goto_bad; + } + + seg->integrity_data_sectors = sego->integrity_data_sectors; + seg->integrity_recalculate = sego->integrity_recalculate; + + memcpy(&seg->integrity_settings, &sego->integrity_settings, sizeof(seg->integrity_settings)); + + if ((hash = sego->integrity_settings.internal_hash)) { + if (!(seg->integrity_settings.internal_hash = dm_pool_strdup(mem, hash))) + goto_bad; + } + + } else if (seg_is_mirror(sego)) { + struct logical_volume *log_lv; + + /* see _mirrored_text_import */ + + seg->extents_copied = sego->extents_copied; + seg->region_size = sego->region_size; + + if (sego->log_lv) { + if (!(log_lv = dm_hash_lookup(lv_hash, sego->log_lv->name))) + goto_bad; + seg->log_lv = log_lv; + } + + if (!_areas_copy_struct(vg, lv, seg, vgo, lvo, sego, pv_hash, lv_hash)) + goto_bad; + + } else if (seg_is_thin_pool(sego)) { + struct logical_volume *data_lvo; + struct logical_volume *meta_lvo; + struct logical_volume *data_lv; + struct logical_volume *meta_lv; + + /* see _thin_pool_text_import */ + + if (!(data_lvo = get_data_from_pool(lvo))) + goto_bad; + if (!(meta_lvo = get_meta_from_pool(lvo))) + goto_bad; + if (!(data_lv = dm_hash_lookup(lv_hash, data_lvo->name))) + goto_bad; + if (!(meta_lv = dm_hash_lookup(lv_hash, meta_lvo->name))) + goto_bad; + if (!attach_pool_data_lv(seg, data_lv)) + goto_bad; + if (!attach_pool_metadata_lv(seg, meta_lv)) + goto_bad; + seg->transaction_id = sego->transaction_id; + seg->chunk_size = sego->chunk_size; + seg->discards = sego->discards; + seg->zero_new_blocks = sego->zero_new_blocks; + seg->crop_metadata = sego->crop_metadata; + + if (!_thin_messages_copy_struct(vgo, vg, lvo, lv, sego, seg, lv_hash)) + goto_bad; + + } else if (seg_is_thin_volume(sego)) { + struct logical_volume *pool_lvo; + struct logical_volume *origin_lvo; + struct logical_volume *merge_lvo; + struct logical_volume *external_lvo; + struct logical_volume *pool_lv = NULL; + struct logical_volume *origin_lv = NULL; + struct logical_volume *merge_lv = NULL; + struct logical_volume *external_lv = NULL; + + /* see _thin_text_import */ + + if (!(pool_lvo = get_pool_from_thin(lvo))) + goto_bad; + if (!(pool_lv = dm_hash_lookup(lv_hash, pool_lvo->name))) + goto_bad; + + if ((origin_lvo = get_origin_from_thin(lvo))) { + if (!(origin_lv = dm_hash_lookup(lv_hash, origin_lvo->name))) + goto_bad; + } + if ((merge_lvo = get_merge_lv_from_thin(lvo))) { + if (!(merge_lv = dm_hash_lookup(lv_hash, merge_lvo->name))) + goto_bad; + } + if ((external_lvo = get_external_lv_from_thin(lvo))) { + if (!(external_lv = dm_hash_lookup(lv_hash, external_lvo->name))) + goto_bad; + } + if (!attach_pool_lv(seg, pool_lv, origin_lv, NULL, merge_lv)) + goto_bad; + if (!attach_thin_external_origin(seg, external_lv)) + goto_bad; + + seg->transaction_id = sego->transaction_id; + seg->device_id = sego->device_id; + + } else if (seg_is_snapshot(sego)) { + struct logical_volume *origin_lvo; + struct logical_volume *cow_lvo; + struct logical_volume *origin_lv; + struct logical_volume *cow_lv; + + /* see _snap_text_import */ + + if (!(origin_lvo = get_origin_from_snap(lvo))) + goto_bad; + if (!(cow_lvo = get_cow_from_snap(lvo))) + goto_bad; + if (!(origin_lv = dm_hash_lookup(lv_hash, origin_lvo->name))) + goto_bad; + if (!(cow_lv = dm_hash_lookup(lv_hash, cow_lvo->name))) + goto_bad; + + init_snapshot_seg(seg, origin_lv, cow_lv, sego->chunk_size, + (sego->status & MERGING) ? 1 : 0); + + } else if (seg_is_writecache(sego)) { + struct logical_volume *origin_lvo; + struct logical_volume *fast_lvo; + struct logical_volume *origin_lv; + struct logical_volume *fast_lv; + + /* see _writecache_text_import */ + + if (!(origin_lvo = get_origin_from_writecache(lvo))) + goto_bad; + if (!(fast_lvo = get_fast_from_writecache(lvo))) + goto_bad; + if (!(origin_lv = dm_hash_lookup(lv_hash, origin_lvo->name))) + goto_bad; + if (!(fast_lv = dm_hash_lookup(lv_hash, fast_lvo->name))) + goto_bad; + + if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0)) + return_0; + + seg->writecache_block_size = sego->writecache_block_size; + + seg->origin = origin_lv; + seg->writecache = fast_lv; + + if (!add_seg_to_segs_using_this_lv(fast_lv, seg)) + return_0; + + memcpy(&seg->writecache_settings, &sego->writecache_settings, sizeof(seg->writecache_settings)); + + if (sego->writecache_settings.new_key && + !(seg->writecache_settings.new_key = dm_pool_strdup(vg->vgmem, sego->writecache_settings.new_key))) + goto_bad; + if (sego->writecache_settings.new_val && + !(seg->writecache_settings.new_val = dm_pool_strdup(vg->vgmem, sego->writecache_settings.new_val))) + goto_bad; + + } else if (seg_is_raid(sego)) { + struct logical_volume *area_lvo; + struct logical_volume *area_lv; + + /* see _raid_text_import_areas */ + + seg->region_size = sego->region_size; + seg->stripe_size = sego->stripe_size; + seg->data_copies = sego->data_copies; + seg->writebehind = sego->writebehind; + seg->min_recovery_rate = sego->min_recovery_rate; + seg->max_recovery_rate = sego->max_recovery_rate; + seg->data_offset = sego->data_offset; + seg->reshape_len = sego->reshape_len; + + for (s = 0; s < sego->area_count; s++) { + if (!(area_lvo = sego->areas[s].u.lv.lv)) + goto_bad; + if (!(area_lv = dm_hash_lookup(lv_hash, area_lvo->name))) + goto_bad; + if (!set_lv_segment_area_lv(seg, s, area_lv, 0, RAID_IMAGE)) + goto_bad; + if (!sego->meta_areas) + continue; + if (!(area_lvo = sego->meta_areas[s].u.lv.lv)) + continue; + if (!(area_lv = dm_hash_lookup(lv_hash, area_lvo->name))) + goto_bad; + if (!set_lv_segment_area_lv(seg, s, area_lv, 0, RAID_META)) + goto_bad; + } + + } else if (seg_is_vdo_pool(sego)) { + struct logical_volume *data_lvo; + struct logical_volume *data_lv; + + if (!(data_lvo = get_data_from_pool(lvo))) + goto_bad; + if (!(data_lv = dm_hash_lookup(lv_hash, data_lvo->name))) + goto_bad; + + seg->vdo_pool_header_size = sego->vdo_pool_header_size; + seg->vdo_pool_virtual_extents = sego->vdo_pool_virtual_extents; + memcpy(&seg->vdo_params, &sego->vdo_params, sizeof(seg->vdo_params)); + + if (!set_lv_segment_area_lv(seg, 0, data_lv, 0, LV_VDO_POOL_DATA)) + goto_bad; + + } else if (seg_is_vdo(sego)) { + struct logical_volume *pool_lvo; + struct logical_volume *pool_lv; + uint32_t vdo_offset; + + if (!(pool_lvo = get_pool_from_vdo(lvo))) + goto_bad; + if (!(pool_lv = dm_hash_lookup(lv_hash, pool_lvo->name))) + goto_bad; + vdo_offset = sego->areas[0].u.lv.le; /* or seg_le(seg, 0)) */ + + if (!set_lv_segment_area_lv(seg, 0, pool_lv, vdo_offset, LV_VDO_POOL)) + goto_bad; + + } else if (seg_is_zero(sego) || seg_is_error(sego)) { + /* nothing to copy */ + + } else { + log_error("Missing copy for lv %s segtype %s.", + display_lvname(lvo), sego->segtype->name); + goto bad; + } + + return seg; + +bad: + return NULL; +} + +/* _read_lvsegs, _read_segments, _read_segment, alloc_lv_segment, ->text_import */ + +static int _lvsegs_copy_struct(struct volume_group *vg, + struct logical_volume *lv, + struct volume_group *vgo, + struct logical_volume *lvo, + struct dm_hash_table *pv_hash, + struct dm_hash_table *lv_hash) +{ + struct lv_segment *sego; + struct lv_segment *seg; + + /* see _read_segment */ + + dm_list_iterate_items(sego, &lvo->segments) { + /* see _read_segment */ + if (!(seg = _seg_copy_struct(vg, lv, vgo, lvo, sego, pv_hash, lv_hash))) + goto_bad; + + /* last step in _read_segment */ + /* adds seg to lv->segments and sets lv->le_count */ + insert_segment(lv, seg); + } + + return 1; +bad: + return 0; +} + +static struct logical_volume *_lv_copy_struct(struct volume_group *vg, + struct volume_group *vgo, + struct logical_volume *lvo, + struct dm_hash_table *pv_hash, + struct dm_hash_table *lv_hash) +{ + struct dm_pool *mem = vg->vgmem; + struct logical_volume *lv; + + if (!(lv = alloc_lv(mem))) + return NULL; + if (!(lv->name = dm_pool_strdup(mem, lvo->name))) + goto_bad; + if (lvo->profile && !(lv->profile = add_profile(lvo->vg->cmd, lvo->profile->name, CONFIG_PROFILE_METADATA))) + goto_bad; + if (lvo->hostname && !(lv->hostname = dm_pool_strdup(mem, lvo->hostname))) + goto_bad; + if (lvo->lock_args && !(lv->lock_args = dm_pool_strdup(mem, lvo->lock_args))) + goto_bad; + if (!dm_list_empty(&lvo->tags) && !str_list_dup(mem, &lv->tags, &lvo->tags)) + goto_bad; + + memcpy(&lv->lvid, &lvo->lvid, sizeof(lvo->lvid)); + lv->vg = vg; + lv->status = lvo->status; + lv->alloc = lvo->alloc; + lv->read_ahead = lvo->read_ahead; + lv->major = lvo->major; + lv->minor = lvo->minor; + lv->size = lvo->size; + /* lv->le_count = lvo->le_count; */ /* set by calls to insert_segment() */ + lv->origin_count = lvo->origin_count; + lv->external_count = lvo->external_count; + lv->timestamp = lvo->timestamp; + + if (!dm_hash_insert(lv_hash, lv->name, lv)) + goto_bad; + return lv; +bad: + return NULL; +} + +/* _read_pv */ + +static struct physical_volume *_pv_copy_struct(struct volume_group *vg, struct volume_group *vgo, + struct physical_volume *pvo, struct dm_hash_table *pv_hash) +{ + struct dm_pool *mem = vg->vgmem; + struct physical_volume *pv; + + if (!(pv = dm_pool_zalloc(mem, sizeof(*pv)))) + return_NULL; + + if (!(pv->vg_name = dm_pool_strdup(mem, vg->name))) + goto_bad; + pv->is_labelled = pvo->is_labelled; + memcpy(&pv->id, &pvo->id, sizeof(struct id)); + memcpy(&pv->vg_id, &vgo->id, sizeof(struct id)); + pv->status = pvo->status; + pv->size = pvo->size; + + if (pvo->device_hint && !(pv->device_hint = dm_pool_strdup(mem, pvo->device_hint))) + goto_bad; + if (pvo->device_id && !(pv->device_id = dm_pool_strdup(mem, pvo->device_id))) + goto_bad; + if (pvo->device_id_type && !(pv->device_id_type = dm_pool_strdup(mem, pvo->device_id_type))) + goto_bad; + + pv->pe_start = pvo->pe_start; + pv->pe_count = pvo->pe_count; + pv->ba_start = pvo->ba_start; + pv->ba_size = pvo->ba_size; + + dm_list_init(&pv->tags); + dm_list_init(&pv->segments); + + if (!dm_list_empty(&pvo->tags) && !str_list_dup(mem, &pv->tags, &pvo->tags)) + goto_bad; + + pv->pe_size = vg->extent_size; + pv->pe_alloc_count = 0; + pv->pe_align = 0; + + /* Note: text import uses "pv0" style keys rather than pv id. */ + if (!dm_hash_insert_binary(pv_hash, &pv->id, ID_LEN, pv)) + goto_bad; + + return pv; +bad: + return NULL; +} + +/* + * We only need to copy things that are exported to metadata text. + * This struct copy is an alternative to text export+import, so the + * the reference for what to copy are the text export and import + * functions. + * + * There are two parts to copying the struct: + * 1. Setting the values, e.g. new->field = old->field. + * 2. Creating the linkages (pointers/lists) among all of + * the new structs. + * + * Creating the linkages is the complex part, and for that we use + * most of the same functions that text import uses. + * + * In some cases, the functions creating linkage also set values. + * This is not common, but in those cases we need to be careful. + * + * Many parts of the vg struct are not used by the activation code, + * but it's difficult to know exactly what is or isn't used, so we + * try to copy everything, except in cases where we know it's not + * used and implementing it would be complicated. + */ + +struct volume_group *vg_copy_struct(struct volume_group *vgo) +{ + struct volume_group *vg; + struct logical_volume *lv; + struct pv_list *pvlo; + struct pv_list *pvl; + struct lv_list *lvlo; + struct lv_list *lvl; + struct dm_hash_table *pv_hash = NULL; + struct dm_hash_table *lv_hash = NULL; + + if (!(vg = alloc_vg("read_vg", vgo->cmd, vgo->name))) + return NULL; + + log_debug("Copying vg struct %p to %p", vgo, vg); + + /* + * TODO: put hash tables in vg struct, and also use for text import. + */ + if (!(pv_hash = dm_hash_create(58))) + goto_bad; + if (!(lv_hash = dm_hash_create(8180))) + goto_bad; + + vg->seqno = vgo->seqno; + vg->alloc = vgo->alloc; + vg->status = vgo->status; + vg->id = vgo->id; + vg->extent_size = vgo->extent_size; + vg->max_lv = vgo->max_lv; + vg->max_pv = vgo->max_pv; + vg->pv_count = vgo->pv_count; + vg->open_mode = vgo->open_mode; + vg->mda_copies = vgo->mda_copies; + + if (vgo->profile && !(vg->profile = add_profile(vgo->cmd, vgo->profile->name, CONFIG_PROFILE_METADATA))) + goto_bad; + if (vgo->system_id && !(vg->system_id = dm_pool_strdup(vg->vgmem, vgo->system_id))) + goto_bad; + if (vgo->lock_type && !(vg->lock_type = dm_pool_strdup(vg->vgmem, vgo->lock_type))) + goto_bad; + if (vgo->lock_args && !(vg->lock_args = dm_pool_strdup(vg->vgmem, vgo->lock_args))) + goto_bad; + if (!dm_list_empty(&vgo->tags) && !str_list_dup(vg->vgmem, &vg->tags, &vgo->tags)) + goto_bad; + + dm_list_iterate_items(pvlo, &vgo->pvs) { + if (!(pvl = dm_pool_zalloc(vg->vgmem, sizeof(struct pv_list)))) + goto_bad; + if (!(pvl->pv = _pv_copy_struct(vg, vgo, pvlo->pv, pv_hash))) + goto_bad; + if (!alloc_pv_segment_whole_pv(vg->vgmem, pvl->pv)) + goto_bad; + vg->extent_count += pvl->pv->pe_count; + vg->free_count += pvl->pv->pe_count; + add_pvl_to_vgs(vg, pvl); + } + + dm_list_iterate_items(lvlo, &vgo->lvs) { + if (!(lvl = dm_pool_zalloc(vg->vgmem, sizeof(struct lv_list)))) + goto_bad; + if (!(lvl->lv = _lv_copy_struct(vg, vgo, lvlo->lv, pv_hash, lv_hash))) + goto_bad; + dm_list_add(&vg->lvs, &lvl->list); + } + + if (vgo->pool_metadata_spare_lv && + !(vg->pool_metadata_spare_lv = dm_hash_lookup(lv_hash, vgo->pool_metadata_spare_lv->name))) + goto_bad; + + if (vgo->sanlock_lv && + !(vg->sanlock_lv = dm_hash_lookup(lv_hash, vgo->sanlock_lv->name))) + goto_bad; + + dm_list_iterate_items(lvlo, &vgo->lvs) { + if (!(lv = dm_hash_lookup(lv_hash, lvlo->lv->name))) + goto_bad; + if (!_lvsegs_copy_struct(vg, lv, vgo, lvlo->lv, pv_hash, lv_hash)) + goto_bad; + } + + /* sanity check */ + if ((vg->free_count != vgo->free_count) || (vg->extent_count != vgo->extent_count)) { + log_error("vg copy wrong free_count %u %u extent_count %u %u", + vgo->free_count, vg->free_count, vgo->extent_count, vg->extent_count); + goto_bad; + } + + set_pv_devices(vgo->fid, vg); + + dm_hash_destroy(pv_hash); + dm_hash_destroy(lv_hash); + return vg; + +bad: + dm_hash_destroy(pv_hash); + dm_hash_destroy(lv_hash); + release_vg(vg); + return NULL; +} +