diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 22bb1f305..55eb71cbd 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -1402,6 +1402,8 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv, uint32_t *virtual_extents, int format, uint64_t vdo_pool_header_size); +struct logical_volume *convert_vdo_lv(struct logical_volume *lv, + const struct vdo_convert_params *vcp); int set_vdo_write_policy(enum dm_vdo_write_policy *vwp, const char *policy); int fill_vdo_target_params(struct cmd_context *cmd, struct dm_vdo_target_params *vtp, diff --git a/lib/metadata/vdo_manip.c b/lib/metadata/vdo_manip.c index ff364e376..32568fae7 100644 --- a/lib/metadata/vdo_manip.c +++ b/lib/metadata/vdo_manip.c @@ -22,6 +22,7 @@ #include "lib/activate/activate.h" #include "lib/config/defaults.h" #include "lib/misc/lvm-exec.h" +#include "lib/metadata/lv_alloc.h" #include // sysinfo #include @@ -441,6 +442,115 @@ int convert_vdo_pool_lv(struct logical_volume *data_lv, return 1; } +/* + * Convert LV into vdopool data LV and build virtual VDO LV on top of it. + * After this it swaps these two LVs so the returned LV is VDO LV! + */ +struct logical_volume *convert_vdo_lv(struct logical_volume *lv, + const struct vdo_convert_params *vcp) +{ + struct cmd_context *cmd = lv->vg->cmd; + char vdopool_name[NAME_LEN], vdopool_tmpl[NAME_LEN]; + struct lvcreate_params lvc = { + .activate = vcp->activate, + .alloc = ALLOC_INHERIT, + .lv_name = vcp->lv_name ? : lv->name, /* preserve the name */ + .major = -1, + .minor = -1, + .permission = LVM_READ | LVM_WRITE, + .pool_name = vdopool_name, + .pvh = &lv->vg->pvs, + .read_ahead = DM_READ_AHEAD_AUTO, + .stripes = 1, + .suppress_zero_warn = 1, /* suppress warning for this VDO */ + .tags = DM_LIST_HEAD_INIT(lvc.tags), + .virtual_extents = vcp->virtual_extents ? : lv->le_count, /* same size for Pool and Virtual LV */ + }; + struct logical_volume *vdo_lv, tmp_lv = { + .segments = DM_LIST_HEAD_INIT(tmp_lv.segments) + }; + + if (!(lvc.segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_VDO))) + return_NULL; + + if (activation() && + lvc.segtype->ops->target_present && + !lvc.segtype->ops->target_present(cmd, NULL, &lvc.target_attr)) { + log_error("%s: Required device-mapper target(s) not detected in your kernel.", + lvc.segtype->name); + return NULL; + } + + if (!vcp->lv_name) { + /* TODO: maybe _vpool would be sufficient */ + if (dm_snprintf(vdopool_tmpl, sizeof(vdopool_tmpl), "%s_vpool%%d", lv->name) < 0) { + log_error("Can't prepare vdo pool name for %s.", display_lvname(lv)); + return NULL; + } + + if (!generate_lv_name(lv->vg, vdopool_tmpl, vdopool_name, sizeof(vdopool_name))) { + log_error("Can't generate new name for %s.", vdopool_tmpl); + return NULL; + } + + /* Rename to use _vpool name and release the passed-in name here */ + if (!lv_rename_update(cmd, lv, vdopool_name, 1)) + return_NULL; + } else + lvc.pool_name = lv->name; + + if (!activate_lv(cmd, lv)) { + log_error("Aborting. Failed to activate pool metadata %s.", + display_lvname(lv)); + return NULL; + } + + if (vcp->do_zero) { + if (test_mode()) { + log_verbose("Test mode: Skipping activation, zeroing and signature wiping."); + } else if (!(wipe_lv(lv, (struct wipe_params) + { + .do_zero = 1, + .do_wipe_signatures = vcp->do_wipe_signatures, + .yes = vcp->yes, + .force = vcp->force + }))) { + log_error("Aborting. Failed to wipe VDO data store %s.", + display_lvname(lv)); + return NULL; + } + } + + if (!convert_vdo_pool_lv(lv, &vcp->vdo_params, &lv->le_count, 1, 0)) + return_NULL; + + /* Create VDO LV with the name, we just release above */ + if (!(vdo_lv = lv_create_single(lv->vg, &lvc))) + return_NULL; + + if (vcp->lv_name) + return vdo_lv; + + /* Swap vdo_lv and lv segment, so passed-in LV appears as virtual VDO_LV */ + if (!move_lv_segments(&tmp_lv, lv, 0, 0) || + !move_lv_segments(lv, vdo_lv, 0, 0) || + !move_lv_segments(vdo_lv, &tmp_lv, 0, 0)) + return_NULL; + + /* Also swap naming, so the passed in LV keeps the passed-in name */ + vdo_lv->name = lv->name; + lv->name = lvc.lv_name; + + /* Swap segment referencing */ + if (!remove_seg_from_segs_using_this_lv(lv, first_seg(lv))) + return_NULL; + + if (!set_lv_segment_area_lv(first_seg(lv), 0, vdo_lv, 0, 0)) + return_NULL; + + return lv; +} + int set_vdo_write_policy(enum dm_vdo_write_policy *vwp, const char *policy) { if (strcasecmp(policy, "sync") == 0) diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 9a7b80ae9..72e58d466 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -5453,97 +5453,56 @@ static int _lvconvert_to_vdopool_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { - const char *vg_name = NULL; - unsigned int vdo_pool_zero; - uint64_t vdo_pool_header_size; struct volume_group *vg = lv->vg; struct logical_volume *vdo_lv; - struct dm_vdo_target_params vdo_params; /* vdo */ - struct lvcreate_params lvc = { + const char *vg_name = NULL; + uint64_t vdo_pool_header_size; + struct vdo_convert_params vcp = { .activate = CHANGE_AEY, - .alloc = ALLOC_INHERIT, - .major = -1, - .minor = -1, - .suppress_zero_warn = 1, /* Suppress warning for this VDO */ - .permission = LVM_READ | LVM_WRITE, - .pool_name = lv->name, - .pvh = &vg->pvs, - .read_ahead = arg_uint_value(cmd, readahead_ARG, DM_READ_AHEAD_AUTO), - .stripes = 1, .lv_name = arg_str_value(cmd, name_ARG, NULL), + .virtual_extents = extents_from_size(cmd, + arg_uint64_value(cmd, virtualsize_ARG, UINT64_C(0)), + vg->extent_size), + .do_zero = arg_int_value(cmd, zero_ARG, 1), + .do_wipe_signatures = 1, + .yes = arg_count(cmd, yes_ARG), + .force = arg_count(cmd, force_ARG) }; - if (lvc.lv_name && - !validate_restricted_lvname_param(cmd, &vg_name, &lvc.lv_name)) + if (vcp.lv_name) { + if (!validate_restricted_lvname_param(cmd, &vg_name, &vcp.lv_name)) + goto_out; + } else + vcp.lv_name = "lvol%d"; + + if (!fill_vdo_target_params(cmd, &vcp.vdo_params, &vdo_pool_header_size, vg->profile)) goto_out; - lvc.virtual_extents = extents_from_size(cmd, - arg_uint64_value(cmd, virtualsize_ARG, UINT64_C(0)), - vg->extent_size); - - if (!(lvc.segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_VDO))) - goto_out; - - if (activation() && lvc.segtype->ops->target_present) { - if (!lvc.segtype->ops->target_present(cmd, NULL, &lvc.target_attr)) { - log_error("%s: Required device-mapper target(s) not detected in your kernel.", - lvc.segtype->name); - goto out; - } - } - - if (!fill_vdo_target_params(cmd, &vdo_params, &vdo_pool_header_size, vg->profile)) - goto_out; - - if (!get_vdo_settings(cmd, &vdo_params, NULL)) + if (!get_vdo_settings(cmd, &vcp.vdo_params, NULL)) goto_out; /* If LV is inactive here, ensure it's not active elsewhere. */ if (!lockd_lv(cmd, lv, "ex", 0)) goto_out; - if (!activate_lv(cmd, lv)) { - log_error("Cannot activate %s.", display_lvname(lv)); - goto out; - } - - vdo_pool_zero = arg_int_value(cmd, zero_ARG, 1); - log_warn("WARNING: Converting logical volume %s to VDO pool volume %s formatting.", - display_lvname(lv), vdo_pool_zero ? "with" : "WITHOUT"); + display_lvname(lv), vcp.do_zero ? "with" : "WITHOUT"); - if (vdo_pool_zero) + if (vcp.do_zero) log_warn("THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)"); else log_warn("WARNING: Using invalid VDO pool data MAY DESTROY YOUR DATA!"); - if (!arg_count(cmd, yes_ARG) && + if (!vcp.yes && yes_no_prompt("Do you really want to convert %s? [y/n]: ", display_lvname(lv)) == 'n') { log_error("Conversion aborted."); goto out; } - if (vdo_pool_zero) { - if (test_mode()) { - log_verbose("Test mode: Skipping activation, zeroing and signature wiping."); - } else if (!wipe_lv(lv, (struct wipe_params) { .do_zero = 1, .do_wipe_signatures = 1, - .yes = arg_count(cmd, yes_ARG), - .force = arg_count(cmd, force_ARG)})) { - log_error("Aborting. Failed to wipe VDO data store."); - goto out; - } - } - - if (!convert_vdo_pool_lv(lv, &vdo_params, &lvc.virtual_extents, - vdo_pool_zero, vdo_pool_header_size)) + if (!(vdo_lv = convert_vdo_lv(lv, &vcp))) goto_out; - dm_list_init(&lvc.tags); - - if (!(vdo_lv = lv_create_single(vg, &lvc))) - goto_out; /* FIXME: hmmm what to do now */ - log_print_unless_silent("Converted %s to VDO pool volume and created virtual %s VDO volume.", display_lvname(lv), display_lvname(vdo_lv));