diff --git a/WHATS_NEW b/WHATS_NEW index e1f1d5d40..7122e3c72 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.01.10 - ================================ + vgchange --physicalextentsize Internal snapshot restructuring. Remove unused internal non-persistent snapshot option. Allow offline extension of snapshot volumes. diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 31890fa80..29912141d 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -281,6 +281,153 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name, return NULL; } +static int _recalc_extents(uint32_t *extents, const char *desc1, + const char *desc2, uint32_t old_size, + uint32_t new_size) +{ + uint64_t size = (uint64_t) old_size * (*extents); + + if (size % new_size) { + log_error("New size %" PRIu64 " for %s%s not an exact number " + "of new extents.", size, desc1, desc2); + return 0; + } + + size /= new_size; + + if (size > UINT32_MAX) { + log_error("New extent count %" PRIu64 " for %s%s exceeds " + "32 bits.", size, desc1, desc2); + return 0; + } + + *extents = (uint32_t) size; + + return 1; +} + +int vg_change_pesize(struct cmd_context *cmd, struct volume_group *vg, + uint32_t new_size) +{ + uint32_t old_size = vg->extent_size; + struct pv_list *pvl; + struct lv_list *lvl; + struct physical_volume *pv; + struct logical_volume *lv; + struct lv_segment *seg; + uint32_t s; + + vg->extent_size = new_size; + + if (vg->fid->fmt->ops->vg_setup && + !vg->fid->fmt->ops->vg_setup(vg->fid, vg)) { + stack; + return 0; + } + + if (!_recalc_extents(&vg->extent_count, vg->name, "", old_size, + new_size)) { + stack; + return 0; + } + + if (!_recalc_extents(&vg->free_count, vg->name, " free space", + old_size, new_size)) { + stack; + return 0; + } + + /* foreach PV */ + list_iterate_items(pvl, &vg->pvs) { + pv = pvl->pv; + + pv->pe_size = new_size; + if (!_recalc_extents(&pv->pe_count, dev_name(pv->dev), "", + old_size, new_size)) { + stack; + return 0; + } + + if (!_recalc_extents(&pv->pe_alloc_count, dev_name(pv->dev), + " allocated space", old_size, new_size)) { + stack; + return 0; + } + } + + /* foreach LV */ + list_iterate_items(lvl, &vg->lvs) { + lv = lvl->lv; + + if (!_recalc_extents(&lv->le_count, lv->name, "", old_size, + new_size)) { + stack; + return 0; + } + + list_iterate_items(seg, &lv->segments) { + if (!_recalc_extents(&seg->le, lv->name, + " segment start", old_size, + new_size)) { + stack; + return 0; + } + + if (!_recalc_extents(&seg->len, lv->name, + " segment length", old_size, + new_size)) { + stack; + return 0; + } + + if (!_recalc_extents(&seg->area_len, lv->name, + " area length", old_size, + new_size)) { + stack; + return 0; + } + + if (!_recalc_extents(&seg->extents_copied, lv->name, + " extents moved", old_size, + new_size)) { + stack; + return 0; + } + + /* foreach area */ + for (s = 0; s < seg->area_count; s++) { + switch (seg->area[s].type) { + case AREA_PV: + if (!_recalc_extents + (&seg->area[s].u.pv.pe, lv->name, + " area start", old_size, + new_size)) { + stack; + return 0; + } + break; + case AREA_LV: + if (!_recalc_extents + (&seg->area[s].u.lv.le, lv->name, + " area start", old_size, + new_size)) { + stack; + return 0; + } + break; + default: + log_error("Unrecognised segment type " + "%u", seg->area[s].type); + return 0; + } + } + } + + } + + return 1; +} + /* Sizes in sectors */ struct physical_volume *pv_create(const struct format_type *fmt, struct device *dev, diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 6bf86f234..35a1a5368 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -402,6 +402,8 @@ int vg_rename(struct cmd_context *cmd, struct volume_group *vg, const char *new_name); int vg_extend(struct format_instance *fi, struct volume_group *vg, int pv_count, char **pv_names); +int vg_change_pesize(struct cmd_context *cmd, struct volume_group *vg, + uint32_t new_extent_size); /* Manipulate LVs */ struct logical_volume *lv_create_empty(struct format_instance *fi, diff --git a/tools/commands.h b/tools/commands.h index 172b60220..9d8ef7256 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -569,13 +569,15 @@ xx(vgchange, "\t -c|--clustered {y|n} |" "\n" "\t -x|--resizeable {y|n} |" "\n" "\t -l|--logicalvolume MaxLogicalVolumes |" "\n" + "\t -s|--physicalextentsize PhysicalExtentSize[kKmMgGtT] |" "\n" "\t --addtag Tag |\n" "\t --deltag Tag}\n" "\t[VolumeGroupName...]\n", addtag_ARG, alloc_ARG, allocation_ARG, autobackup_ARG, available_ARG, clustered_ARG, deltag_ARG, ignorelockingfailure_ARG, logicalvolume_ARG, - partial_ARG, resizeable_ARG, resizable_ARG, test_ARG, uuid_ARG) + partial_ARG, physicalextentsize_ARG, resizeable_ARG, resizable_ARG, + test_ARG, uuid_ARG) xx(vgck, "Check the consistency of volume group(s)", diff --git a/tools/vgchange.c b/tools/vgchange.c index 2b7c36e3d..c87d259a4 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -251,6 +251,64 @@ static int _vgchange_logicalvolume(struct cmd_context *cmd, return ECMD_PROCESSED; } +static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg) +{ + uint32_t extent_size; + + if (!(vg->status & RESIZEABLE_VG)) { + log_error("Volume group \"%s\" must be resizeable " + "to change PE size", vg->name); + return ECMD_FAILED; + } + + if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) { + log_error("Physical extent size may not be negative"); + return EINVALID_CMD_LINE; + } + + extent_size = arg_uint_value(cmd, physicalextentsize_ARG, 0) * 2; + if (!extent_size) { + log_error("Physical extent size may not be zero"); + return EINVALID_CMD_LINE; + } + + if (extent_size == vg->extent_size) { + log_error("Physical extent size of VG %s is already %s", + vg->name, display_size(cmd, extent_size, SIZE_SHORT)); + return ECMD_PROCESSED; + } + + if (extent_size & (extent_size - 1)) { + log_error("Physical extent size must be a power of 2."); + return EINVALID_CMD_LINE; + } + + if (extent_size > vg->extent_size) { + if ((uint64_t) vg->extent_size * vg->extent_count % extent_size) { + /* FIXME Adjust used PV sizes instead */ + log_error("New extent size is not a perfect fit"); + return EINVALID_CMD_LINE; + } + } + + if (!archive(vg)) + return ECMD_FAILED; + + if (!vg_change_pesize(cmd, vg, extent_size)) { + stack; + return ECMD_FAILED; + } + + if (!vg_write(vg) || !vg_commit(vg)) + return ECMD_FAILED; + + backup(vg); + + log_print("Volume group \"%s\" successfully changed", vg->name); + + return ECMD_PROCESSED; +} + static int _vgchange_tag(struct cmd_context *cmd, struct volume_group *vg, int arg) { @@ -369,6 +427,9 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, else if (arg_count(cmd, deltag_ARG)) r = _vgchange_tag(cmd, vg, deltag_ARG); + else if (arg_count(cmd, physicalextentsize_ARG)) + r = _vgchange_pesize(cmd, vg); + else if (arg_count(cmd, uuid_ARG)) r = _vgchange_uuid(cmd, vg); @@ -387,9 +448,10 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + arg_count(cmd, resizeable_ARG) + arg_count(cmd, deltag_ARG) + arg_count(cmd, addtag_ARG) + arg_count(cmd, uuid_ARG) + + arg_count(cmd, physicalextentsize_ARG) + arg_count(cmd, clustered_ARG) + arg_count(cmd, alloc_ARG))) { - log_error("One of -a, -c, -l, -x, --alloc, --addtag, --deltag " - "or --uuid required"); + log_error("One of -a, -c, -l, -s, -x, --uuid, --alloc, --addtag or " + "--deltag required"); return EINVALID_CMD_LINE; } @@ -397,8 +459,9 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) if (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + arg_count(cmd, resizeable_ARG) + arg_count(cmd, deltag_ARG) + arg_count(cmd, addtag_ARG) + arg_count(cmd, alloc_ARG) + - arg_count(cmd, uuid_ARG) + arg_count(cmd, clustered_ARG) > 1) { - log_error("Only one of -a, -c, -l, -x, --uuid, --alloc, " + arg_count(cmd, uuid_ARG) + arg_count(cmd, clustered_ARG) + + arg_count(cmd, physicalextentsize_ARG) > 1) { + log_error("Only one of -a, -c, -l, -s, -x, --uuid, --alloc, " "--addtag or --deltag allowed"); return EINVALID_CMD_LINE; }