Version 2.00.21 -
=============================
+ Fix extents_moved metadata size comment.
+ Remove duplicate line in pvremove help text.
+ Support variable mirror region size.
+ Support PE ranges in pvmove source PV.
+ Fixes to as-yet-unused LV segment splitting code.
Change alloc_areas to pe_ranges and allow suppression of availability checks.
Add dev_size column to pvs.
Add report columns for in-kernel device number.
struct list *segh, *t;
struct lv_segment *current, *prev = NULL;
+ if (lv->status & LOCKED || lv->status & PVMOVE)
+ return 1;
+
list_iterate_safe(segh, t, &lv->segments) {
current = list_item(segh, struct lv_segment);
/*
* Split the supplied segment at the supplied logical extent
+ * NB Use LE numbering that works across stripes PV1: 0,2,4 PV2: 1,3,5 etc.
*/
static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg,
uint32_t le)
struct lv_segment *split_seg;
uint32_t s;
uint32_t offset = le - seg->le;
+ uint32_t area_offset;
if (!(seg->segtype->flags & SEG_CAN_SPLIT)) {
log_error("Unable to split the %s segment at LE %" PRIu32
}
/* In case of a striped segment, the offset has to be / stripes */
+ area_offset = offset;
if (seg->segtype->flags & SEG_AREAS_STRIPED)
- offset /= seg->area_count;
+ area_offset /= seg->area_count;
/* Adjust the PV mapping */
for (s = 0; s < seg->area_count; s++) {
switch (seg->area[s].type) {
case AREA_LV:
split_seg->area[s].u.lv.le =
- seg->area[s].u.lv.le + offset;
+ seg->area[s].u.lv.le + area_offset;
+ log_debug("Split %s:%u[%u] at %u: %s LE %u", lv->name,
+ seg->le, s, le, seg->area[s].u.lv.lv->name,
+ split_seg->area[s].u.lv.le);
break;
case AREA_PV:
split_seg->area[s].u.pv.pe =
- seg->area[s].u.pv.pe + offset;
+ seg->area[s].u.pv.pe + area_offset;
+ log_debug("Split %s:%u[%u] at %u: %s PE %u", lv->name,
+ seg->le, s, le,
+ dev_name(seg->area[s].u.pv.pv->dev),
+ split_seg->area[s].u.pv.pe);
break;
default:
}
}
- split_seg->area_len = seg->area_len - offset;
- seg->area_len = offset;
+ split_seg->area_len -= area_offset;
+ seg->area_len = area_offset;
+
+ split_seg->len -= offset;
+ seg->len = offset;
+
+ split_seg->le = seg->le + seg->len;
/* Add split off segment to the list _after_ the original one */
list_add_h(&seg->list, &split_seg->list);
*/
int insert_pvmove_mirrors(struct cmd_context *cmd,
struct logical_volume *lv_mirr,
- struct physical_volume *pv,
+ struct list *source_pvl,
struct logical_volume *lv,
struct list *allocatable_pvs,
struct list *lvs_changed);
*/
int insert_pvmove_mirrors(struct cmd_context *cmd,
struct logical_volume *lv_mirr,
- struct physical_volume *pv,
+ struct list *source_pvl,
struct logical_volume *lv,
struct list *allocatable_pvs,
struct list *lvs_changed)
struct list *segh;
struct lv_segment *seg;
struct lv_list *lvl;
+ struct pv_list *pvl;
int lv_used = 0;
uint32_t s, start_le, extent_count = 0u;
struct segment_type *segtype;
+ struct pe_range *per;
+ uint32_t pe_start, pe_end, per_end, stripe_multiplier;
+
+ /* Only 1 PV may feature in source_pvl */
+ pvl = list_item(source_pvl->n, struct pv_list);
if (!(segtype = get_segtype_from_string(lv->vg->cmd, "mirror"))) {
stack;
return 0;
}
- /* Work through all segments on the supplied PV */
+ /* Split LV segments to match PE ranges */
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_PV ||
- seg->area[s].u.pv.pv->dev != pv->dev)
+ seg->area[s].u.pv.pv->dev != pvl->pv->dev)
continue;
- /* First time, add LV to list of LVs affected */
- if (!lv_used) {
- if (!(lvl = pool_alloc(cmd->mem, sizeof(*lvl)))) {
- log_error("lv_list alloc failed");
+ /* Do these PEs need moving? */
+ list_iterate_items(per, pvl->pe_ranges) {
+ pe_start = seg->area[s].u.pv.pe;
+ pe_end = pe_start + seg->area_len - 1;
+ per_end = per->start + per->count - 1;
+
+ /* No overlap? */
+ if ((pe_end < per->start) ||
+ (pe_start > per_end))
+ continue;
+
+ if (seg->segtype->flags & SEG_AREAS_STRIPED)
+ stripe_multiplier = seg->area_count;
+ else
+ stripe_multiplier = 1;
+
+ if ((per->start != pe_start &&
+ per->start > pe_start) &&
+ !lv_split_segment(lv, seg->le +
+ (per->start - pe_start) *
+ stripe_multiplier)) {
+ stack;
return 0;
}
- lvl->lv = lv;
- list_add(lvs_changed, &lvl->list);
- lv_used = 1;
- }
- start_le = lv_mirr->le_count;
- if (!lv_extend(lv->vg->fid, lv_mirr, segtype, 1,
- seg->area_len, 0u, seg->area_len,
- seg->area[s].u.pv.pv,
- seg->area[s].u.pv.pe,
- PVMOVE, allocatable_pvs,
- lv->alloc)) {
- log_error("Allocation for temporary "
- "pvmove LV failed");
- return 0;
+ if ((per_end != pe_end &&
+ per_end < pe_end) &&
+ !lv_split_segment(lv, seg->le +
+ (per_end - pe_start + 1) *
+ stripe_multiplier)) {
+ stack;
+ return 0;
+ }
}
- seg->area[s].type = AREA_LV;
- seg->area[s].u.lv.lv = lv_mirr;
- seg->area[s].u.lv.le = start_le;
+ }
+ }
- extent_count += seg->area_len;
+ /* Work through all segments on the supplied PV */
+ list_iterate(segh, &lv->segments) {
+ seg = list_item(segh, struct lv_segment);
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg->area[s].type != AREA_PV ||
+ seg->area[s].u.pv.pv->dev != pvl->pv->dev)
+ continue;
+
+ pe_start = seg->area[s].u.pv.pe;
+
+ /* Do these PEs need moving? */
+ list_iterate_items(per, pvl->pe_ranges) {
+ per_end = per->start + per->count - 1;
+
+ if ((pe_start < per->start) ||
+ (pe_start > per_end))
+ continue;
- lv->status |= LOCKED;
+ log_debug("Matched PE range %u-%u against "
+ "%s %u len %u", per->start, per_end,
+ dev_name(seg->area[s].u.pv.pv->dev),
+ seg->area[s].u.pv.pe, seg->area_len);
+
+ /* First time, add LV to list of LVs affected */
+ if (!lv_used) {
+ if (!(lvl = pool_alloc(cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = lv;
+ list_add(lvs_changed, &lvl->list);
+ lv_used = 1;
+ }
+
+ log_very_verbose("Moving %s:%u-%u of %s/%s",
+ dev_name(pvl->pv->dev),
+ seg->area[s].u.pv.pe,
+ seg->area[s].u.pv.pe +
+ seg->area_len - 1,
+ lv->vg->name, lv->name);
+
+ start_le = lv_mirr->le_count;
+ if (!lv_extend(lv->vg->fid, lv_mirr, segtype, 1,
+ seg->area_len, 0u, seg->area_len,
+ seg->area[s].u.pv.pv,
+ seg->area[s].u.pv.pe,
+ PVMOVE, allocatable_pvs,
+ lv->alloc)) {
+ log_error("Allocation for temporary "
+ "pvmove LV failed");
+ return 0;
+ }
+ seg->area[s].type = AREA_LV;
+ seg->area[s].u.lv.lv = lv_mirr;
+ seg->area[s].u.lv.le = start_le;
+
+ extent_count += seg->area_len;
+
+ lv->status |= LOCKED;
+
+ break;
+ }
}
}
lv1->status &= ~LOCKED;
}
}
+ if (!lv_merge_segments(lv1))
+ stack;
+
}
+
return 1;
}
{
outf(f, "mirror_count = %u", seg->area_count);
if (seg->status & PVMOVE)
- out_size(f, (uint64_t) seg->extents_copied,
+ out_size(f, (uint64_t) seg->extents_copied * seg->lv->vg->extent_size,
"extents_moved = %u", seg->extents_copied);
return out_areas(f, seg, "mirror");
int mirror_status = MIRR_RUNNING;
int areas = seg->area_count;
int start_area = 0u;
+ uint32_t region_size, region_max;
if (!*target_state)
*target_state = _init_target(mem, cft);
if (seg->extents_copied == seg->area_len) {
mirror_status = MIRR_COMPLETED;
start_area = 1;
- } else if (*pvmove_mirror_count++) {
+ } else if ((*pvmove_mirror_count)++) {
mirror_status = MIRR_DISABLED;
areas = 1;
}
*target = "linear";
} else {
*target = "mirror";
+
+ /* Find largest power of 2 region size unit we can use */
+ region_max = (1 << (ffs(seg->area_len) - 1)) *
+ seg->lv->vg->extent_size;
+
+ region_size = mirr_state->region_size;
+ if (region_max < region_size) {
+ region_size = region_max;
+ log_verbose("Using reduced mirror region size of %u sectors",
+ region_size);
+ }
+
if ((*pos = lvm_snprintf(params, paramsize, "core 1 %u %u ",
- mirr_state->region_size, areas)) < 0) {
+ region_size, areas)) < 0) {
stack;
return -1;
}
return compose_areas_line(dm, seg, params, paramsize, pos, start_area,
areas);
-
}
static int _target_percent(void **target_state, struct pool *mem,
*total_denominator += denominator;
if (seg)
- seg->extents_copied = mirr_state->region_size *
- numerator / seg->lv->vg->extent_size;
+ seg->extents_copied = seg->area_len * numerator / denominator;
return 1;
}
/* Create new LV with mirror segments for the required copies */
static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
struct volume_group *vg,
- struct physical_volume *pv,
+ struct list *source_pvl,
const char *lv_name,
struct list *allocatable_pvs,
struct list **lvs_changed)
log_print("Skipping locked LV %s", lv->name);
continue;
}
- if (!insert_pvmove_mirrors(cmd, lv_mirr, pv, lv,
+ if (!insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
allocatable_pvs, *lvs_changed)) {
stack;
return NULL;
int argc, char **argv)
{
const char *lv_name = NULL;
+ char *pv_name_arg;
struct volume_group *vg;
+ struct list *source_pvl;
struct list *allocatable_pvs;
struct list *lvs_changed;
struct physical_volume *pv;
struct logical_volume *lv_mirr;
int first_time = 1;
+ pv_name_arg = argv[0];
+ argc--;
+ argv++;
+
/* Find PV (in VG) */
if (!(pv = find_pv_by_name(cmd, pv_name))) {
stack;
first_time = 0;
} else {
+ /* Determine PE ranges to be moved */
+ if (!(source_pvl = create_pv_list(cmd->mem, vg, 1,
+ &pv_name_arg, 0))) {
+ stack;
+ unlock_vg(cmd, pv->vg_name);
+ return ECMD_FAILED;
+ }
/* Get PVs we can use for allocation */
if (!(allocatable_pvs = _get_allocatable_pvs(cmd, argc, argv,
return ECMD_FAILED;
}
- if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, pv, lv_name,
+ if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, source_pvl, lv_name,
allocatable_pvs,
&lvs_changed))) {
stack;
int pvmove(struct cmd_context *cmd, int argc, char **argv)
{
char *pv_name = NULL;
+ char *colon;
int ret;
if (argc) {
pv_name = argv[0];
- argc--;
- argv++;
+
+ /* Drop any PE lists from PV name */
+ if ((colon = strchr(pv_name, ':'))) {
+ if (!(pv_name = pool_strndup(cmd->mem, pv_name,
+ (unsigned) (colon -
+ pv_name)))) {
+ log_error("Failed to clone PV name");
+ return 0;
+ }
+ }
if (!arg_count(cmd, abort_ARG) &&
(ret = _set_up_pvmove(cmd, pv_name, argc, argv)) !=
stack;
return ret;
}
+
}
return pvmove_poll(cmd, pv_name,