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

[lvm-devel] [RFC LVM2] (6/6) Allow allocation from LV



This patch allows allocation from LV.

Example 1: striped LV on mirrored LV

# lvcreate -l4 -m1 --corelog testvg
  Logical volume "lvol0" created
# lvcreate -l4 -m1 --corelog testvg
  Logical volume "lvol1" created
# lvchange --addtag m testvg/lvol0 testvg/lvol1
  Logical volume "lvol0" changed
  Logical volume "lvol1" changed
# lvcreate -i2 -l2 testvg @m
  Using default stripesize 64.00 KB
  Logical volume "lvol2" created
# lvextend -l+2 testvg/lvol2 @m
  Using stripesize of last segment 64.00 KB
  Extending logical volume lvol2 to 16.00 MB
  Logical volume lvol2 successfully resized
# lvs -a -o name,attr,devices
  LV               Attr   Devices                            
  [lvol0]          mwi-ao lvol0_mimage_0(0),lvol0_mimage_1(0)
  [lvol0_mimage_0] iwi-ao /dev/mapper/d1(0)                  
  [lvol0_mimage_1] iwi-ao /dev/mapper/d2(0)                  
  [lvol1]          mwi-ao lvol1_mimage_0(0),lvol1_mimage_1(0)
  [lvol1_mimage_0] iwi-ao /dev/mapper/d3(0)                  
  [lvol1_mimage_1] iwi-ao /dev/mapper/d4(0)                  
  lvol2            -wi-a- lvol0(0),lvol1(0)        
# dmsetup ls --tree
testvg-lvol2 (253:15)
 |-testvg-lvol1 (253:14)
 |  |-testvg-lvol1_mimage_1 (253:13)
 |  |  `-d4 (253:4)
 |  |     `- (7:0)
 |  `-testvg-lvol1_mimage_0 (253:12)
 |     `-d3 (253:3)
 |        `- (7:0)
 `-testvg-lvol0 (253:11)
    |-testvg-lvol0_mimage_1 (253:10)
    |  `-d2 (253:2)
    |     `- (7:0)
    `-testvg-lvol0_mimage_0 (253:9)
       `-d1 (253:1)
          `- (7:0)

Example 2: mirrored mirror log

# lvcreate -m1 --corelog -l1 --addtag m testvg
  Logical volume "lvol0" created
# lvcreate -m1 -l1 --corelog testvg
  Logical volume "lvol1" created
# lvconvert --mirrorlog disk testvg/lvol1 @m
  Logical volume lvol1 converted.
# lvs -a -o name,attr,devices
  LV               Attr   Devices                            
  [lvol0]          mwi-ao lvol0_mimage_0(0),lvol0_mimage_1(0)
  [lvol0_mimage_0] iwi-ao /dev/mapper/d1(0)                  
  [lvol0_mimage_1] iwi-ao /dev/mapper/d2(0)                  
  lvol1            mwi-a- lvol1_mimage_0(0),lvol1_mimage_1(0)
  [lvol1_mimage_0] iwi-ao /dev/mapper/d3(0)                  
  [lvol1_mimage_1] iwi-ao /dev/mapper/d4(0)                  
  [lvol1_mlog]     lwi-ao lvol0(0)                      
# dmsetup ls --tree
testvg-lvol1 (253:14)
 |-testvg-lvol1_mimage_1 (253:13)
 |  `-d4 (253:4)
 |     `- (7:0)
 |-testvg-lvol1_mimage_0 (253:12)
 |  `-d3 (253:3)
 |     `- (7:0)
 `-testvg-lvol1_mlog (253:15)
    `-testvg-lvol0 (253:11)
       |-testvg-lvol0_mimage_1 (253:10)
       |  `-d2 (253:2)
       |     `- (7:0)
       `-testvg-lvol0_mimage_0 (253:9)
          `-d1 (253:1)
             `- (7:0)

Thanks,
-- 
Jun'ichi Nomura, NEC Corporation of America
Allow allocation from LV.

Examples:
 First, create mirrored LV vg/lvol0 and vg/lvol1, then tag them as 'raid1'.
     # lvcreate -l10 -m1 -n lvol0 vg
     # lvcreate -l10 -m1 -n lvol1 vg
     # lvchange --addtag raid1 vg/lvol0 vg/lvol1
 1. To create striped-mirror (raid0 over raid1),
     # lvcreate -i2 -l2 vg @raid1
 2. To create a mirrored LV with mirrored mirror log
     # lvcreate -m1 --corelog -n lvol2 -l1 vg
     # lvconvert --log disk vg/lvol2 @raid1

Implementation:
  Thinking 'PV' as a source of LE allocation, attaching 'struct pv' to
  'struct lv' if the LV is used as a base of other LVs.
  Such struct pv is not bound to the VG.

Index: LVM2.work/tools/toollib.c
===================================================================
--- LVM2.work.orig/tools/toollib.c
+++ LVM2.work/tools/toollib.c
@@ -1050,6 +1050,7 @@ struct list *create_pv_list(struct dm_po
 {
 	struct list *r;
 	struct pv_list *pvl;
+	struct lv_list *lvl;
 	struct list tags, arg_pvnames;
 	const char *pvname = NULL;
 	char *colon, *tagname;
@@ -1072,6 +1073,22 @@ struct list *create_pv_list(struct dm_po
 				log_error("Skipping invalid tag %s", tagname);
 				continue;
 			}
+			list_iterate_items(lvl, &vg->lvs) {
+				if (str_list_match_item(&lvl->lv->tags,
+							tagname)) {
+					if (!lv_is_allocatable(lvl->lv))
+						continue;
+					if (!_create_pv_entry(mem,
+						      lvl->lv->free_area,
+						      NULL,
+						      allocatable_only,
+						      r)) {
+						stack;
+						return NULL;
+					}
+				}
+			}
+
 			list_iterate_items(pvl, &vg->pvs) {
 				if (str_list_match_item(&pvl->pv->tags,
 							tagname)) {
Index: LVM2.work/lib/metadata/metadata.c
===================================================================
--- LVM2.work.orig/lib/metadata/metadata.c
+++ LVM2.work/lib/metadata/metadata.c
@@ -801,6 +801,50 @@ static struct physical_volume *_pv_creat
 	return NULL;
 }
 
+int set_lv_as_pv(struct dm_pool *mem, struct logical_volume *lv)
+{
+	struct physical_volume *pv = _pv_alloc(mem);
+	struct pv_list *pvl;
+
+	if (!pv)
+		return_0;
+
+	pv->size = lv->size;
+
+	if (pv->size < PV_MIN_SIZE) {
+		log_error("%s: Size must exceed minimum of %ld sectors.",
+			  lv->name, PV_MIN_SIZE);
+		goto bad;
+	}
+
+	pv->dev = (struct device *) pv; /* FIXME: need something identical */
+	pv->fmt = lv->vg->fid->fmt; /* FIXME: need to get fmt->mem */
+
+	pv->pe_count = lv->le_count;
+	pv->pe_size = pv->size / pv->pe_count;
+	pv->pe_start = 0;
+	pv->pe_alloc_count = 0;
+	pv->stacked_lv = lv;
+	if (!alloc_pv_segment_whole_pv(mem, pv)) {
+		log_error("Failed to allocate pv segments");
+		goto bad;
+	}
+
+	pvl = dm_pool_zalloc(mem, sizeof(*pvl));
+	if (!pvl)
+		goto bad;
+	pvl->pv = pv;
+	list_init(&pvl->list);
+
+	lv->free_area = pvl;
+
+	return 1;
+
+      bad:
+	dm_pool_free(mem, pv);
+	return 0;
+}
+
 /* FIXME: liblvm todo - make into function that returns handle */
 struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
 {
@@ -1970,6 +2014,9 @@ const char *pv_vg_name(const pv_t *pv)
 
 const char *pv_dev_name(const pv_t *pv)
 {
+	if (base_lv(pv))
+		return base_lv(pv)->name;
+
 	return dev_name(pv_dev(pv));
 }
 
Index: LVM2.work/lib/metadata/metadata.h
===================================================================
--- LVM2.work.orig/lib/metadata/metadata.h
+++ LVM2.work/lib/metadata/metadata.h
@@ -263,6 +263,9 @@ struct lv_segment *find_seg_by_le(struct
 /* Find PV segment containing given LE */
 struct pv_segment *find_peg_by_pe(struct physical_volume *pv, uint32_t pe);
 
+/* attach PV structure to the LV */
+int set_lv_as_pv(struct dm_pool *mem, struct logical_volume *lv);
+
 /*
  * Remove a dev_dir if present.
  */
@@ -301,6 +304,9 @@ int add_mirror_layers(struct alloc_handl
 struct lv_segment *find_mirror_seg(struct lv_segment *seg);
 int fixup_imported_mirrors(struct volume_group *vg);
 
+/* For stacked LV */
+int calculate_free_areas_for_stacked_lvs(struct volume_group *vg);
+
 /*
  * Begin skeleton for external LVM library
  */
Index: LVM2.work/lib/metadata/metadata-exported.h
===================================================================
--- LVM2.work.orig/lib/metadata/metadata-exported.h
+++ LVM2.work/lib/metadata/metadata-exported.h
@@ -158,6 +158,8 @@ struct physical_volume {
 
 	struct list segments;	/* Ordered pv_segments covering complete PV */
 	struct list tags;
+
+	struct logical_volume *stacked_lv; /* underlying LV */
 };
 
 struct format_instance {
@@ -261,8 +263,13 @@ struct logical_volume {
 
 	struct list segments;
 	struct list tags;
+
+	struct pv_list *free_area; /* PV structure in case of stacked LV */
 };
 
+#define lv_pv(lv)	((lv)->free_area->pv)
+#define base_lv(pv)	((pv)->stacked_lv)
+
 struct pe_range {
 	struct list list;
 	uint32_t start;		/* PEs */
Index: LVM2.work/lib/metadata/lv_manip.c
===================================================================
--- LVM2.work.orig/lib/metadata/lv_manip.c
+++ LVM2.work/lib/metadata/lv_manip.c
@@ -159,10 +159,11 @@ void release_lv_segment_area(struct lv_s
 	if (seg_type(seg, s) == AREA_UNASSIGNED)
 		return;
 
-	if (seg_type(seg, s) == AREA_PV) {
+	if (seg_pvseg(seg, s))
 		release_pv_segment(seg_pvseg(seg, s), area_reduction);
+
+	if (seg_type(seg, s) == AREA_PV)
 		return;
-	}
 
 	if (seg_lv(seg, s)->status & MIRROR_IMAGE) {
 		lv_reduce(seg_lv(seg, s), area_reduction);
@@ -227,7 +228,11 @@ int move_lv_segment_area(struct lv_segme
 int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
 			   struct physical_volume *pv, uint32_t pe)
 {
-	seg->areas[area_num].type = AREA_PV;
+	if (base_lv(pv)) {
+		set_lv_segment_area_lv(seg, area_num, base_lv(pv), pe, 0);
+		base_lv(pv)->status &= ~VISIBLE_LV;
+	} else
+		seg->areas[area_num].type = AREA_PV;
 
 	if (!(seg_pvseg(seg, area_num) =
 	      assign_peg_to_lvseg(pv, pe, seg->area_len, seg, area_num))) {
@@ -302,6 +307,11 @@ static int _lv_segment_reduce(struct lv_
 	return 1;
 }
 
+static int _lv_used_as_pv(struct logical_volume *lv)
+{
+	return lv->free_area ? 1 : 0;
+}
+
 /*
  * Entry point for all LV reductions in size.
  */
@@ -312,6 +322,14 @@ static int _lv_reduce(struct logical_vol
 	uint32_t count = extents;
 	uint32_t reduction;
 
+	/* FIXME */
+	if (_lv_used_as_pv(lv)) {
+		if (!pv_resize(lv_pv(lv), lv->vg, lv->le_count - extents)) {
+			log_error("%s is used as PV of other LV.", lv->name);
+			return 0;
+		}
+	}
+
 	list_iterate_back_items(seg, &lv->segments) {
 		if (!count)
 			break;
@@ -1560,6 +1578,7 @@ static int _for_each_sub_lv(struct cmd_c
 			return 0;
 		for (s = 0; s < seg->area_count; s++)
 			if (seg_type(seg, s) == AREA_LV &&
+			    !_lv_used_as_pv(seg_lv(seg, s)) &&
 			    !func(cmd, seg_lv(seg, s), data))
 				return 0;
 	}
@@ -1802,6 +1821,65 @@ struct list *build_parallel_areas_from_l
 	return parallel_areas;
 }
 
+/*
+ * FIXME: The function has a side-effect.
+ * struct physical_volume is attached to the lv, if necessary.
+ */
+int lv_is_allocatable(struct logical_volume *lv)
+{
+	/*
+	 * These LVs are internal.
+	 * Not available for allocation over them.
+	 */
+	if (lv->status & MIRROR_IMAGE ||
+	    lv->status & MIRROR_LOG ||
+	    lv->status & PVMOVE)
+		return 0;
+
+	if (!lv->free_area)
+		if (!set_lv_as_pv(lv->vg->cmd->mem, lv))
+			return_0;
+
+	return 1;
+}
+
+static int _consume_lv_area(struct lv_segment *seg, uint32_t area_num)
+{
+	struct logical_volume *lv = seg_lv(seg, area_num);
+	uint32_t len = seg->area_len;
+
+	if (!lv_is_allocatable(lv))
+		return 1;
+
+	if (!(seg_pvseg(seg, area_num) =
+	     assign_peg_to_lvseg(lv_pv(lv), seg_le(seg, area_num),
+				 len, seg, area_num)))
+		return_0;
+
+	return 1;
+}
+
+int calculate_free_areas_for_stacked_lvs(struct volume_group *vg)
+{
+	struct lv_list *lvl;
+	struct logical_volume *lv;
+	struct lv_segment *seg;
+	uint32_t s;
+
+	list_iterate_items(lvl, &vg->lvs) {
+		lv = lvl->lv;
+		list_iterate_items(seg, &lv->segments) {
+			for (s = 0; s < seg->area_count; s++) {
+				if (seg_type(seg, s) == AREA_LV)
+					if (!_consume_lv_area(seg, s))
+						return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
 int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
 		     const force_t force)
 {
Index: LVM2.work/lib/format_text/import_vsn1.c
===================================================================
--- LVM2.work.orig/lib/format_text/import_vsn1.c
+++ LVM2.work/lib/format_text/import_vsn1.c
@@ -789,6 +789,12 @@ static struct volume_group *_read_vg(str
 		goto bad;
 	}
 
+	if (!calculate_free_areas_for_stacked_lvs(vg)) {
+		log_error("Failed to calculate free areas of stacked LVs in"
+			  "volume group %s.", vg->name);
+		goto bad;
+	}
+
 	dm_hash_destroy(pv_hash);
 
 	if (vg->status & PARTIAL_VG) {
Index: LVM2.work/lib/metadata/pv_manip.c
===================================================================
--- LVM2.work.orig/lib/metadata/pv_manip.c
+++ LVM2.work/lib/metadata/pv_manip.c
@@ -82,6 +82,13 @@ int peg_dup(struct dm_pool *mem, struct 
 	return 1;
 }
 
+static void peg_update_vg_free_count(struct pv_segment *peg, int32_t count)
+{
+	/* Reduce/Increment VG free space only if peg is not from LV */
+	if (!base_lv(peg->pv))
+		peg->lvseg->lv->vg->free_count += count;
+}
+
 /*
  * Split peg at given extent.
  * Second part is always deallocated.
@@ -104,7 +111,7 @@ static int _pv_split_segment(struct phys
 
 	if (peg->lvseg) {
 		peg->pv->pe_alloc_count -= peg_new->len;
-		peg->lvseg->lv->vg->free_count += peg_new->len;
+		peg_update_vg_free_count(peg, peg_new->len);
 	}
 
 	return 1;
@@ -170,7 +177,8 @@ struct pv_segment *assign_peg_to_lvseg(s
 	peg->lv_area = area_num;
 
 	peg->pv->pe_alloc_count += area_len;
-	peg->lvseg->lv->vg->free_count -= area_len;
+
+	peg_update_vg_free_count(peg, -area_len);
 
 	return peg;
 }
@@ -185,7 +193,7 @@ int release_pv_segment(struct pv_segment
 
 	if (peg->lvseg->area_len == area_reduction) {
 		peg->pv->pe_alloc_count -= area_reduction;
-		peg->lvseg->lv->vg->free_count += area_reduction;
+		peg_update_vg_free_count(peg, area_reduction);
 
 		peg->lvseg = NULL;
 		peg->lv_area = 0;
@@ -383,8 +391,10 @@ static int _reduce_pv(struct physical_vo
 
 	pv->pe_count = new_pe_count;
 
-	vg->extent_count -= (old_pe_count - new_pe_count);
-	vg->free_count -= (old_pe_count - new_pe_count);
+	if (!base_lv(pv)) {
+		vg->extent_count -= (old_pe_count - new_pe_count);
+		vg->free_count -= (old_pe_count - new_pe_count);
+	}
 
 	return 1;
 }
@@ -410,8 +420,10 @@ static int _extend_pv(struct physical_vo
 
 	pv->pe_count = new_pe_count;
 
-	vg->extent_count += (new_pe_count - old_pe_count);
-	vg->free_count += (new_pe_count - old_pe_count);
+	if (!base_lv(pv)) {
+		vg->extent_count += (new_pe_count - old_pe_count);
+		vg->free_count += (new_pe_count - old_pe_count);
+	}
 
 	return 1;
 }
Index: LVM2.work/lib/metadata/lv_alloc.h
===================================================================
--- LVM2.work.orig/lib/metadata/lv_alloc.h
+++ LVM2.work/lib/metadata/lv_alloc.h
@@ -86,4 +86,5 @@ void alloc_destroy(struct alloc_handle *
 struct list *build_parallel_areas_from_lv(struct cmd_context *cmd,
 					  struct logical_volume *lv);
 
+int lv_is_allocatable(struct logical_volume *lv);
 #endif

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