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

[lvm-devel] [PATCH vgsplit 2/4] Update vgsplit to allow a list of LVs or PVs on the commandline.



Add some helper functions to allow vgsplit to continue in more cases.
Introduce "related volumes" concept, as defined in the comments below.


--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -151,6 +151,9 @@ struct pv_segment {
 	uint32_t lv_area;	/* Index to area in LV segment */
 };
 
+#define pvseg_is_allocated(pvseg) (pvseg->lvseg)
+#define pvseg_is_free(pvseg) (!pvseg_is_allocated(pvseg))
+
 struct physical_volume {
 	struct id id;
 	struct device *dev;
@@ -297,6 +300,52 @@ struct lv_list {
 };
 
 /*
+ * A list of related volumes (PVs and LVs) is useful because LVM internals
+ * require:
+ * 1) A logical volume must be within a single volume group
+ * 2) A physical volume must be within a single volume group
+ *
+ * A related volume is one that is in the same volume group and related
+ * to another volume by nature of one of the above two rules.
+ *
+ * As an example, consider the following:
+ *
+ *                     VG0
+ *  +---------------------------+
+ *  |          LV0              |
+ *  |          / \              | 
+ *  |         /   \             |
+ *  |        /     \            +---------------+
+ *  |     LV1       LV2      LV3        LV4     |
+ *  |     / \       / \      /          / \     |
+ *  |    /   \     /   \    /          /   \    |
+ *  |   /     \   /     \  /          /     \   |
+ *  | PV1      PV2       PV3         PV4    PV5 |
+ *  +-------------------------------------------+
+ *
+ * The list of volumes related to LV0 are:
+ * - PV1, PV2, PV3
+ * - LV0, LV1, LV2
+ *
+ * Similarly, the list of volumes related to PV3 is the same list.  This might
+ * not seem as clear but is the result of applying the above two rules
+ * repeatedly.  For instance, if we start with PV3, from rule #1, it is clear
+ * we must put LV3 and LV2 into the list of related volumes since we cannot
+ * split logical volumes across volume groups.  Once LV3 and LV2 are added,
+ * we must then add PV2 to the list of related volumes because of the second
+ * rule.  Continuing to apply this rule will result in the same list of
+ * related volumes as above.
+ *
+ * Note that LV4, PV4, and PV5 are not related to the list mentioned above,
+ * but are related to each other.
+ */
+struct related_volumes {
+	struct volume_group *vg;
+	struct list ll; /* struct lv_list */
+	struct list pl; /* struct pv_list */
+};
+
+/*
 * Utility functions
 */
 int vg_write(struct volume_group *vg);
@@ -443,6 +492,13 @@ struct logical_volume *find_lv(const struct volume_group *vg,
 struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
 					const char *pv_name);
 
+void init_related_volumes(struct related_volumes *rvs, struct volume_group *vg);
+void pv_add_related_volume(struct related_volumes *rvs, struct pv_list *pvl);
+void lv_add_related_volume(struct related_volumes *rvs, struct lv_list *lvl);
+void move_related_volumes(struct related_volumes *rvs,
+			  struct volume_group *vg_from,
+			  struct volume_group *vg_to);
+
 /* Find LV segment containing given LE */
 struct lv_segment *first_seg(const struct logical_volume *lv);
 
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index dc6e624..4174457 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -1754,6 +1754,240 @@ struct logical_volume *lv_from_lvid(struct cmd_context *cmd, const char *lvid_s,
 	return lvl->lv;
 }
 
+static void _move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
+		     struct pv_list *pvl)
+{
+	struct physical_volume *pv;
+
+	list_del(&pvl->list);
+	list_add(&vg_to->pvs, &pvl->list);
+
+	vg_from->pv_count--;
+	vg_to->pv_count++;
+
+	pv = list_item(pvl, struct pv_list)->pv;
+
+	vg_from->extent_count -= pv_pe_count(pv);
+	vg_to->extent_count += pv_pe_count(pv);
+
+	vg_from->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+	vg_to->free_count += pv_pe_count(pv) - pv_pe_alloc_count(pv);
+}
+
+static void _move_lv(struct volume_group *vg_from, struct volume_group *vg_to,
+		    struct lv_list *lvl)
+{
+	struct logical_volume *lv;
+
+	lv = lvl->lv;
+	
+	list_del(&lvl->list);
+	list_add(&vg_to->lvs, &lvl->list);
+	if (lv->status & SNAPSHOT) {
+		vg_from->snapshot_count--;
+		vg_to->snapshot_count++;
+	} else {
+		vg_from->lv_count--;
+		vg_to->lv_count++;
+	}
+}
+
+static int find_in_pv_list(const struct list *pl,
+			   const struct physical_volume *pv)
+{
+	const struct pv_list *pvl;
+
+	list_iterate_items(pvl, pl)
+		if (pvl->pv == pv)
+			return 1;
+	return 0;
+}
+
+static int find_in_lv_list(const struct list *ll,
+			   const struct logical_volume *lv)
+{
+	const struct lv_list *lvl;
+
+	list_iterate_items(lvl, ll)
+		if (lvl->lv == lv)
+			return 1;
+	return 0;
+}
+
+static void add_related_pv(struct related_volumes *rvs,
+			   const struct physical_volume *pv)
+{
+	struct pv_list *assoc_pv;
+
+	assoc_pv = find_pv_in_vg(rvs->vg, pv_dev_name(pv));
+	if (assoc_pv)
+		pv_add_related_volume(rvs, assoc_pv);
+}
+
+static void add_related_lv(struct related_volumes *rvs,
+			   const struct logical_volume *lv)
+{
+	struct lv_list *assoc_lv;
+
+	assoc_lv = find_lv_in_vg(lv->vg, lv->name);
+	if (assoc_lv)
+		lv_add_related_volume(rvs, assoc_lv);
+}
+
+/*
+ * The lv_segment is at the heart of related volumes.
+ * Handle special cases (lv_log), then iterate through segs_using_this_lv
+ * (important for stacked LVs) as well as each area, adding LVs or PVs as
+ * necessary.
+ */
+static void add_related_segment(struct related_volumes *rvs,
+				const struct lv_segment *lvseg)
+{
+	int s;
+	struct lv_segment *lvseg2;
+
+	if (lvseg->lv)
+		add_related_lv(rvs, lvseg->lv);
+	if (lvseg->log_lv)
+		add_related_lv(rvs, lvseg->log_lv);
+	if (!list_empty(&lvseg->lv->segs_using_this_lv)) {
+		lvseg2 = get_only_segment_using_this_lv(lvseg->lv);
+		if (lvseg2)
+			add_related_segment(rvs, lvseg2);
+	}
+
+	for (s = 0; s < lvseg->area_count; s++) {
+		if (seg_type(lvseg, s) == AREA_LV)
+			add_related_lv(rvs, seg_lv(lvseg, s));
+		else if (seg_type(lvseg, s) == AREA_PV)
+			add_related_pv(rvs, seg_pv(lvseg, s));
+	}
+}	
+
+/*
+ * Find all volumes related to logical volume 'lv'.
+ * Go through each lv_segment adding as necessary.
+ * Handle special cases such as snapshots.
+ */
+static void lv_find_related_volumes(struct related_volumes *rvs,
+				    const struct logical_volume *lv)
+{
+	struct lv_segment *lvseg, *snap;
+	struct logical_volume *lv2;
+	struct list *snh, *snht;
+
+	if (lv_is_origin(lv)) {
+		/*
+		 * If this is an origin LV for snapshots, all snapshots are
+		 * related.
+		 */
+		list_iterate_safe(snh, snht, &lv->snapshot_segs) {
+			lv2 = list_struct_base(snh, struct lv_segment,
+					      origin_list)->cow;
+			add_related_lv(rvs, lv2);
+		}
+	} else if (lv_is_cow(lv)) {
+		/*
+		 * If this is a snapshot, the origin is related.
+		 */
+		add_related_lv(rvs, origin_from_cow(lv));
+
+		/*
+		 * The "hidden" struct logical_volume with name
+		 * "snapshotN" is also related.
+		 */
+		snap = find_cow(lv);
+		add_related_lv(rvs, snap->lv);
+	}
+
+	/*
+	 * Go through each LV segment and associate PVs or LVs below.
+	 */
+	list_iterate_items(lvseg, &lv->segments) {
+		add_related_segment(rvs, lvseg);
+	}
+}
+
+/*
+ * Find all volumes related to physical volume 'pv'.
+ * Any LVs that 'pv' backs are related, so we search each segment and add LVs
+ * as necessary.
+ */
+static void pv_find_related_volumes(struct related_volumes *rvs,
+				    const struct physical_volume *pv)
+{
+	struct pv_segment *pvseg;
+
+	/*
+	 * Go through pv segments and see which LVs are tied to them.  If we
+	 * find any LVs, add them to the list of LVs.
+	 */
+	list_iterate_items(pvseg, &pv->segments) {
+		if (pvseg_is_allocated(pvseg) && (pvseg->lvseg->lv))
+			add_related_lv(rvs, pvseg->lvseg->lv);
+	}
+}
+
+/*
+ * Add a PV entry to a list of related volumes.
+ * Avoid duplicate entries by first calling find_in_pv_list().
+ * Once added, we must then look for further related volumes, so we call
+ * pv_find_related_volumes().
+ */
+void pv_add_related_volume(struct related_volumes *rvs, struct pv_list *pvl)
+{
+	if (pvl && !find_in_pv_list(&rvs->pl, pvl->pv)) {
+		list_del(&pvl->list);
+		list_add(&rvs->pl, &pvl->list);
+		pv_find_related_volumes(rvs, pvl->pv);
+	}
+}
+
+/*
+ * Add a LV entry to a list of related volumes.
+ * Avoid duplicate entries by first calling find_in_lv_list().
+ * Once added, we must then look for further related volumes, so we call
+ * lv_find_related_volumes().
+ */
+void lv_add_related_volume(struct related_volumes *rvs, struct lv_list *lvl)
+{
+	if (lvl && !find_in_lv_list(&rvs->ll, lvl->lv)) {
+		list_del(&lvl->list);
+		list_add(&rvs->ll, &lvl->list);
+		lv_find_related_volumes(rvs, lvl->lv);
+	}
+}
+
+/*
+ * Move a list of related volumes from one volume group to another.
+ */
+void move_related_volumes(struct related_volumes *rvs,
+			  struct volume_group *vg_from,
+			  struct volume_group *vg_to)
+{
+	struct lv_list *lvl;
+	struct pv_list *pvl;
+	struct list *pvh, *pvht;
+	struct list *lvh, *lvht;
+
+	list_iterate_safe(pvh, pvht, &rvs->pl) {
+		pvl = list_item(pvh, struct pv_list);
+		_move_pv(vg_from, vg_to, pvl);
+	}
+
+	list_iterate_safe(lvh, lvht, &rvs->ll) {
+		lvl = list_item(lvh, struct lv_list);
+		_move_lv(vg_from, vg_to, lvl);
+	}
+}
+
+void init_related_volumes(struct related_volumes *rvs, struct volume_group *vg)
+{
+	rvs->vg = vg;
+	list_init(&rvs->ll);
+	list_init(&rvs->pl);
+}
+
 /**
  * pv_read - read and return a handle to a physical volume
  * @cmd: LVM command initiating the pv_read
diff --git a/man/vgsplit.8 b/man/vgsplit.8
index 8614c73..e80cbd9 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -877,6 +877,7 @@ xx(vgsplit,
    "\t[--alloc AllocationPolicy] " "\n"
    "\t[-c|--clustered {y|n}] " "\n"
    "\t[-d|--debug] " "\n"
+   "\t[-f|--force]\n"
    "\t[-h|--help] " "\n"
    "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
    "\t[-M|--metadatatype 1|2] " "\n"
@@ -888,7 +889,7 @@ xx(vgsplit,
    "\tPhysicalVolumePath [PhysicalVolumePath...]\n",
 
    alloc_ARG, autobackup_ARG, clustered_ARG,
-   maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
+   force_ARG, maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
    metadatatype_ARG, test_ARG)
 
 xx(version,
diff --git a/tools/toollib.c b/tools/toollib.c
index d9544d4..150db8d 100644
--- a/tools/vgsplit.c
+++ b/tools/vgsplit.c
@@ -15,191 +15,31 @@
 
 #include "tools.h"
 
-static void _move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
-		     struct pv_list *pvl)
-{
-	struct physical_volume *pv;
-
-	list_del(&pvl->list);
-	list_add(&vg_to->pvs, &pvl->list);
-
-	vg_from->pv_count--;
-	vg_to->pv_count++;
-
-	pv = list_item(pvl, struct pv_list)->pv;
-
-	vg_from->extent_count -= pv_pe_count(pv);
-	vg_to->extent_count += pv_pe_count(pv);
-
-	vg_from->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
-	vg_to->free_count += pv_pe_count(pv) - pv_pe_alloc_count(pv);
-}
-
-/* FIXME Why not (lv->vg == vg) ? */
-static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv)
+static int confirm_vgsplit(const struct list *ll, const struct list *pl)
 {
+	const char *prompt = "Really move? [y/n]: ";
+	struct pv_list *pvl;
 	struct lv_list *lvl;
 
-	list_iterate_items(lvl, &vg->lvs)
-		if (lv == lvl->lv)
-			 return 1;
-
-	return 0;
-}
-
-
-static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
-{
-	struct list *lvh, *lvht;
-	struct logical_volume *lv;
-	struct lv_segment *seg;
-	struct physical_volume *pv;
-	struct volume_group *vg_with;
-	unsigned s;
-
-	list_iterate_safe(lvh, lvht, &vg_from->lvs) {
-		lv = list_item(lvh, struct lv_list)->lv;
-
-		if ((lv->status & SNAPSHOT))
-			continue;
-
-		if ((lv->status & MIRRORED))
-			continue;
-
-		/* Ensure all the PVs used by this LV remain in the same */
-		/* VG as each other */
-		vg_with = NULL;
-		list_iterate_items(seg, &lv->segments) {
-			for (s = 0; s < seg->area_count; s++) {
-				/* FIXME Check AREA_LV too */
-				if (seg_type(seg, s) != AREA_PV)
-					continue;
-
-				pv = seg_pv(seg, s);
-				if (vg_with) {
-					if (!pv_is_in_vg(vg_with, pv)) {
-						log_error("Can't split Logical "
-							  "Volume %s between "
-							  "two Volume Groups",
-							  lv->name);
-						return 0;
-					}
-					continue;
-				}
-
-				if (pv_is_in_vg(vg_from, pv)) {
-					vg_with = vg_from;
-					continue;
-				}
-				if (pv_is_in_vg(vg_to, pv)) {
-					vg_with = vg_to;
-					continue;
-				}
-				log_error("Physical Volume %s not found",
-					  pv_dev_name(pv));
-				return 0;
-			}
-
-		}
-			
-		if (vg_with == vg_from)
+	printf("Continuing with vgsplit will cause more volumes to move "
+	       "than originally requested.\n"
+	       "The following logical volumes will move:\n");
+	list_iterate_items(lvl, ll) {
+		if (is_reserved_name(lvl->lv->name))
 			continue;
-
-		/* Move this LV */
-		list_del(lvh);
-		list_add(&vg_to->lvs, lvh);
-
-		vg_from->lv_count--;
-		vg_to->lv_count++;
+		printf("%s ", lvl->lv->name);
 	}
-
-	/* FIXME Ensure no LVs contain segs pointing at LVs in the other VG */
-
-	return 1;
-}
-
-static int _move_snapshots(struct volume_group *vg_from,
-			   struct volume_group *vg_to)
-{
-	struct list *lvh, *lvht;
-	struct logical_volume *lv;
-	struct lv_segment *seg;
-	int cow_from = 0;
-	int origin_from = 0;
-
-	list_iterate_safe(lvh, lvht, &vg_from->lvs) {
-		lv = list_item(lvh, struct lv_list)->lv;
-
-		if (!(lv->status & SNAPSHOT))
-			continue;
-
-		list_iterate_items(seg, &lv->segments) {
-			cow_from = _lv_is_in_vg(vg_from, seg->cow);
-			origin_from = _lv_is_in_vg(vg_from, seg->origin);
-
-			if (cow_from && origin_from)
-				continue;
-			if ((!cow_from && origin_from) ||
-			     (cow_from && !origin_from)) {
-				log_error("Can't split snapshot %s between"
-					  " two Volume Groups", seg->cow->name);
-				return 0;
-			}
-		}
-
-		/* Move this snapshot */
-		list_del(lvh);
-		list_add(&vg_to->lvs, lvh);
-
-		vg_from->snapshot_count--;
-		vg_to->snapshot_count++;
+	printf("\nAnd the following physical volumes will move:\n");
+	list_iterate_items(pvl, pl)
+		printf("%s ", pv_dev_name(pvl->pv));
+	printf("\n");
+	if (yes_no_prompt(prompt) == 'n') {
+		log_print("Aborting vgsplit operation.");
+		return 0;
 	}
-
 	return 1;
 }
 
-static int _move_mirrors(struct volume_group *vg_from,
-			 struct volume_group *vg_to)
-{
-	struct list *lvh, *lvht;
-	struct logical_volume *lv;
-	struct lv_segment *seg;
-	unsigned s, seg_in, log_in;
-
-	list_iterate_safe(lvh, lvht, &vg_from->lvs) {
-		lv = list_item(lvh, struct lv_list)->lv;
-
-		if (!(lv->status & MIRRORED))
-			continue;
-
-		seg = first_seg(lv);
-
-		seg_in = 0;
-		for (s = 0; s < seg->area_count; s++)
-			if (_lv_is_in_vg(vg_to, seg_lv(seg, s)))
-			    seg_in++;
-
-		log_in = (!seg->log_lv || _lv_is_in_vg(vg_to, seg->log_lv));
-		
-		if ((seg_in && seg_in < seg->area_count) ||
-		    (seg_in && seg->log_lv && !log_in) ||
-		    (!seg_in && seg->log_lv && log_in)) {
-			log_error("Can't split mirror %s between "
-				  "two Volume Groups", lv->name);
-			return 0;
-		}
-
-		if (seg_in == seg->area_count && log_in) {
-			list_del(lvh);
-			list_add(&vg_to->lvs, lvh);
-
-			vg_from->lv_count--;
-			vg_to->lv_count++;
-		}
-	}
-
-	return 1;
-}
 
 /*
  * Has the user given an option related to a new vg as the split destination?
@@ -222,6 +62,10 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
 	int active;
 	int existing_vg;
 	struct pv_list *pvl;
+	struct lv_list *lvl;
+	struct related_volumes rvs;
+	unsigned int lv_count_cmdline;
+	unsigned int pv_count_cmdline;
 
 	if (argc < 3) {
 		log_error("Existing VG, new VG and physical volumes required.");
@@ -302,28 +146,64 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
 	if (!archive(vg_from))
 		goto error;
 
-	/* Move PVs across to new structure */
+	/*
+	 * First verify the cmdline arguments - must be either PVs or LVs that
+	 * are in 'vg_from'.
+	 */
 	for (opt = 0; opt < argc; opt++) {
-		if (!(pvl = find_pv_in_vg(vg_from, argv[opt]))) {
-			log_error("Physical volume %s not in volume group %s",
+		/* FIXME: handle tags */
+		if (!find_lv_in_vg(vg_from, argv[opt]) &&
+		    !find_pv_in_vg(vg_from, argv[opt])) {
+			log_error("Argument %s is neither a physical or "
+				  "logical volume related to volume "
+				  "group %s",
 				  argv[opt], vg_from->name);
 			goto error;
 		}
+	}
 
-		_move_pv(vg_from, vg_to, pvl);
+	/*
+	 * Go through the cmdline a second time.  The reason we do this is
+	 * because each call to *_add_related_volume() may add other PVs or
+	 * LVs from the lists on 'vg_from' to the related volumes list.  Thus,
+	 * the find_{lv|pv}_in_vg() may fail.
+	 *
+	 * Build temporary related volumes list by removing the lvs and pvs
+	 * from 'vg_from' and storing them inside 'rvs'.
+	 */
+	init_related_volumes(&rvs, vg_from);
+	lv_count_cmdline = 0;
+	pv_count_cmdline = 0;
+	for (opt = 0; opt < argc; opt++) {
+		/* FIXME: handle tags */
+		if ((lvl = find_lv_in_vg(vg_from, argv[opt]))) {
+			lv_count_cmdline++;
+			lv_add_related_volume(&rvs, lvl);
+		} else if ((pvl = find_pv_in_vg(vg_from, argv[opt]))) {
+			pv_count_cmdline++;
+			pv_add_related_volume(&rvs, pvl);
+		}
 	}
 
-	/* Move required LVs across, checking consistency */
-	if (!(_move_lvs(vg_from, vg_to)))
-		goto error;
+	/*
+	 * Now that we have the lists of all PVs and LVs involved in the
+	 * split, check if this list contains PVs or LVs above what the user
+	 * requested.  If so, ask for confirmation before continuing, as this
+	 * is most likely what the user would expect.
+	 */
+	if ( ((pv_count_cmdline && list_size(&rvs.pl) > pv_count_cmdline) ||
+	      (lv_count_cmdline && list_size(&rvs.ll) > lv_count_cmdline)) &&
+	     !arg_count(cmd, force_ARG) ) {
+		if (!confirm_vgsplit(&rvs.ll, &rvs.pl))
+			goto error;
+	}
 
-	/* Move required snapshots across */
-	if (!(_move_snapshots(vg_from, vg_to)))
-		goto error;
+	/* FIXME: Move LV active check here and restrict it to 'rvs' */
 
-	/* Move required mirrors across */
-	if (!(_move_mirrors(vg_from, vg_to)))
-		goto error;
+	/*
+	 * Move PVs and LVs, updating internal structures and accounting.
+	 */
+	move_related_volumes(&rvs, vg_from, vg_to);
 
 	/* Split metadata areas and check if both vgs have at least one area */
 	if (!(vg_split_mdas(cmd, vg_from, vg_to)) && vg_from->pv_count) {
-- 
1.5.3.4

-- 


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