[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[lvm-devel] [PATCH mirror 5/13] Add collapsing feature of stacked mirror



This patch adds collapse_mirrored_lv() function.
When the function finds a temporary mirror layer in which the resync
completed, it removes the layer and moves in-sync mirror images to
the original mirror.

Thanks,
-- 
Jun'ichi Nomura, NEC Corporation of America

collapse_mirrored_lv() is added to collapse the mirror layers by
removing intermediate LVs when the resync completes.


Index: LVM2.work/lib/metadata/metadata-exported.h
===================================================================
--- LVM2.work.orig/lib/metadata/metadata-exported.h
+++ LVM2.work/lib/metadata/metadata-exported.h
@@ -437,6 +437,8 @@ int remove_mirror_images(struct lv_segme
 			 struct list *removable_pvs, unsigned remove_log);
 int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
 			      struct list *removable_pvs, unsigned remove_log);
+int collapse_mirrored_lv(struct logical_volume *lv);
+int is_temporary_mirror_layer(const struct logical_volume *lv);
 
 int insert_pvmove_mirrors(struct cmd_context *cmd,
 			  struct logical_volume *lv_mirr,
Index: LVM2.work/lib/metadata/mirror.c
===================================================================
--- LVM2.work.orig/lib/metadata/mirror.c
+++ LVM2.work/lib/metadata/mirror.c
@@ -31,6 +31,16 @@
 #define MIRROR_ALLOCATE          1
 #define MIRROR_ALLOCATE_ANYWHERE 2
 
+int is_temporary_mirror_layer(const struct logical_volume *lv)
+{
+	if (lv->status & MIRROR_IMAGE
+	    && lv->status & MIRRORED
+	    && !(lv->status & LOCKED))
+		return 1;
+
+	return 0;
+}
+
 struct lv_segment *find_mirror_seg(struct lv_segment *seg)
 {
 	return seg->mirror_seg;
@@ -169,6 +179,155 @@ static int _delete_lv(struct lv_segment 
 	return 1;
 }
 
+static int _mirrored_lv_in_sync(struct logical_volume *lv)
+{
+	float sync_percent;
+
+	if (!lv_mirror_percent(lv->vg->cmd, lv, 0, &sync_percent, NULL)) {
+		log_error("Unable to determine mirror sync status of %s/%s.",
+			  lv->vg->name, lv->name);
+		return 0;
+	}
+
+	if (sync_percent >= 100.0)
+		return 1;
+
+	return 0;
+}
+
+static int _merge_mirror_images(struct logical_volume *lv,
+				const struct list *mimages)
+{
+	int addition = list_size(mimages);
+	struct logical_volume **img_lvs;
+	struct lv_list *lvl;
+	int i = 0;
+
+	if (!addition)
+		return 1;
+
+	if (!(img_lvs = alloca(sizeof(*img_lvs) * addition)))
+		return_0;
+
+	list_iterate_items(lvl, mimages)
+		img_lvs[i++] = lvl->lv;
+
+	return lv_add_more_mirrored_areas(lv, img_lvs, addition, MIRROR_IMAGE);
+}
+
+/*
+ * Return a temporary LV for resyncing added mirror image.
+ * Add other mirror legs to lvs list.
+ */
+static struct logical_volume *_find_tmp_mirror(struct logical_volume *lv,
+					       struct list *lvs)
+{
+	struct lv_segment *mirrored_seg;
+	uint32_t s;
+	struct logical_volume *tmp_lv = NULL;
+
+	if (!(lv->status & MIRRORED))
+		return NULL;
+
+	mirrored_seg = first_seg(lv);
+	for (s = 0; s < mirrored_seg->area_count; s++) {
+		if (seg_type(mirrored_seg, s) != AREA_LV)
+			return NULL;
+
+		if (is_temporary_mirror_layer(seg_lv(mirrored_seg, s))) {
+			/* There should not be multiple temporary LVs */
+			if (tmp_lv) {
+				log_error("LV %s has multiple temporary LVs",
+					  lv->name);
+				return NULL;
+			}
+			tmp_lv = seg_lv(mirrored_seg, s);
+		} else {
+			if (!_lv_add_list(lv->vg->cmd->mem,
+					  seg_lv(mirrored_seg, s), lvs))
+				return_NULL;
+		}
+	}
+
+	return tmp_lv;
+}
+
+/* Empty the mirror segment */
+static void _release_mirror_images_and_logs(struct lv_segment *seg)
+{
+	uint32_t s;
+
+	for (s = 0; s < seg->area_count; s++) {
+		/* release_lv_segment_area() blindly reduces MIRROR_IMAGE lv */
+		seg_lv(seg, s)->status &= ~MIRROR_IMAGE;
+		release_lv_segment_area(seg, s, seg->area_len);
+	}
+
+	if (seg->log_lv) {
+		seg->log_lv->status &= ~MIRROR_LOG;
+		seg->log_lv = NULL;
+	}
+}
+
+/*
+ * Collapsing temporary mirror layers.
+ *
+ * When mirrors are added to already-mirrored LV, a temporary mirror layer
+ * is inserted at the top of the stack to reduce resync work.
+ * The function will remove the intermediate layer and collapse the stack
+ * as far as mirrors are in-sync.
+ *
+ * The function is destructive: to remove intermediate mirror layers,
+ * VG metadata commits and suspend/resume are necessary.
+ */
+int collapse_mirrored_lv(struct logical_volume *lv)
+{
+	struct logical_volume *tmp_lv, *log_lv;
+	struct list lvlist;
+
+	list_init(&lvlist);
+	while ((tmp_lv = _find_tmp_mirror(lv, &lvlist))) {
+		if (!_mirrored_lv_in_sync(lv)) {
+			log_verbose("Not collapsing LV %s: not in-sync",
+				    lv->name);
+			return 1;
+		}
+
+		log_lv = first_seg(lv)->log_lv;
+		_release_mirror_images_and_logs(first_seg(lv));
+
+		if (!lv_empty(lv)) {
+			log_error("Emptying temporary lv failed");
+			return 0;
+		}
+
+		_move_lv_segments(lv, tmp_lv);
+
+		if (!_merge_mirror_images(lv, &lvlist)) {
+			log_error("Failed to add mirror images");
+			return 0;
+		}
+		list_init(&lvlist);
+
+		if (!_apply_lv_change(lv)) {
+			log_error("Failed to update LV %s", lv->name);
+			return 0;
+		}
+
+		if (!_delete_lv(first_seg(lv), tmp_lv)) {
+			log_error("Failed to remove intermediate LV");
+			return 0;
+		}
+
+		if (log_lv && !_delete_lv(first_seg(lv), log_lv)) {
+			log_error("Failed to remove intermediate log LV");
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
 /*
  * Reduce mirrored_seg to num_mirrors images.
  */
@@ -386,19 +545,13 @@ int reconfigure_mirror_images(struct lv_
 			      struct list *removable_pvs, unsigned remove_log)
 {
 	int r;
-	int insync = 0;
+	int insync;
 	int log_policy, dev_policy;
 	uint32_t old_num_mirrors = mirrored_seg->area_count;
 	int had_log = (mirrored_seg->log_lv) ? 1 : 0;
-	float sync_percent = 0;
 
 	/* was the mirror in-sync before problems? */
-	if (!lv_mirror_percent(mirrored_seg->lv->vg->cmd,
-			       mirrored_seg->lv, 0, &sync_percent, NULL))
-		log_error("WARNING: Unable to determine mirror sync status of %s/%s.",
-			  mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
-	else if (sync_percent >= 100.0)
-		insync = 1;
+	insync = _mirrored_lv_in_sync(mirrored_seg->lv);
 
 	/*
 	 * While we are only removing devices, we can have sync set.

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]