]> sourceware.org Git - lvm2.git/commitdiff
thin: lvconvert support for external origin
authorZdenek Kabelac <zkabelac@redhat.com>
Tue, 5 Feb 2013 10:26:27 +0000 (11:26 +0100)
committerZdenek Kabelac <zkabelac@redhat.com>
Sat, 23 Feb 2013 09:38:20 +0000 (10:38 +0100)
Add basic support for converting LV into an external origin volume.

Syntax:

lvconvert --thinpool vg/pool  --originname renamed_origin -T origin

It will convert volume  'origin' into a thin volume, which will
use 'renamed_origin' as an external read-only origin.
All read/write into origin will go via 'pool'.

renamed_origin volume is read-only volume, that could be activated
only in read-only mode, and cannot be modified.

WHATS_NEW
lib/activate/activate.c
man/lvconvert.8.in
tools/args.h
tools/commands.h
tools/lvconvert.c

index 4e1f758cc21c60772d9759aa31c599ab27ee5055..dc4fc8f0589ca405c565cd14926e563a40bd37cf 100644 (file)
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
 Version 2.02.99 - 
 ===================================
+  Initial support for lvconvert of thin external origin.
   Add _lv_remove_segs_using_this_lv() for removal of dependent lvs.
   Improve activation code for better support of stacked devices.
   Add _add_layer_target_to_dtree() for adding linear layer into dtree.
index 6187828ced8d41a233cd756707e745a637b9c64b..f19dff73e6dcaeeb8d883252d125f4efa02bfa5b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
  *
  * This file is part of LVM2.
  *
@@ -1580,6 +1580,10 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
            (lv_is_origin(lv_pre) || lv_is_cow(lv_pre)))
                lockfs = 1;
 
+       /* Converting non-thin LV to thin external origin ? */
+       if (!lv_is_thin_volume(lv) && lv_is_thin_volume(lv_pre))
+               lockfs = 1; /* Sync before conversion */
+
        if (laopts->origin_only && lv_is_thin_volume(lv) && lv_is_thin_volume(lv_pre))
                lockfs = 1;
 
index 578047b3e70cd7b3bdf0f57e4166f29d49cad767..c5929c2f891f624a01ad203c0840b9b712501ca4 100644 (file)
@@ -99,6 +99,10 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot
 .RB [ \-h | \-? | \-\-help ]
 .RB [ \-v | \-\-verbose ]
 .RB [ \-\-version ]
+.RB [ \-T | \-\-thin
+.IR ExternalOriginLogicalVolume { Name | Path }
+.RB [ \-\-originname
+.IR NewExternalOriginVolumeName ]]
 .RI [ PhysicalVolume [ Path ][ :PE [ -PE ]]...]
 .sp
 
@@ -230,6 +234,13 @@ merge finishes, the merged snapshot is removed.  Multiple snapshots may
 be specified on the commandline or a @tag may be used to specify
 multiple snapshots be merged to their respective origin.
 .TP
+.B \-\-originname \fINewExternalOriginVolumeName\fP
+The name for converted external origin volume.
+.br
+Without this option a default names of "lvol#" will be generated where
+# is the LVM internal number of the logical volume.
+Converted volume will be read-only.
+.TP
 .BR \-\-poolmetadata " " \fIThinPoolMetadataLogicalVolume { \fIName | \fIPath }
 Specifies thin pool metadata logical volume.
 The size should be in between 2MiB and 16GiB.
@@ -288,6 +299,13 @@ StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format.
 For metadata in LVM2 format, the stripe size may be a larger
 power of 2 but must not exceed the physical extent size.
 .TP
+.IR \fB\-T ", " \fB\-\-thin " " ExternalOriginLogicalVolume { Name | Path }
+Changes the logical volume into a thin volume for the thin pool
+specified with the option \fB\-\-thinpool\fP. \fIExternalOriginLogicalVolume\fP
+is converted into a new read-only logical volume which will be used as an
+external origin volume for unprovisioned areas.
+The non-default name for this new volume can be specified with \fB\-\-originname\fP.
+.TP
 .IR \fB\-\-thinpool " " ThinPoolLogicalVolume { Name | Path }
 Changes logical volume into a thin pool volume. The volume
 will store the pool's data.
@@ -379,6 +397,20 @@ available in the volume group.
 .sp
 .B lvconvert \-\-replace /dev/sdb1 vg00/my_raid1 /dev/sdf1
 
+Convert the logical volume "vg00/lvpool" into a thin pool with chunk size 128KiB
+and convert "vg00/lv1" into a thin volume using this pool. Original "vg00/lv1"
+is used as an external read-only origin, where all writes to such volume
+are stored in the "vg00/lvpool".
+.sp
+.B lvconvert \-\-thinpool vg00/lvpool -c 128 -T lv1
+
+Convert the logical volume "vg00/origin" into a thin volume from the thin pool
+"vg00/lvpool". This thin volume will use "vg00/origin" as an external origin
+volume for unprovisioned areas in this volume.
+For the read-only external origin use the new name "vg00/external".
+.sp
+.B lvconvert \-\-thinpool vg00/lvpool \-\-originname external -T vg00/origin
+
 .SH SEE ALSO
 .BR lvm (8),
 .BR vgcreate (8),
index d4d6c4012757c0f1b47c8fb4f53f895b0ddea9f2..b140cdbbb77a90c9bf48d9c08fb87655460778c9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
- * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
  *
  * This file is part of LVM2.
  *
@@ -69,6 +69,7 @@ arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
 arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", size_kb_arg, 0)
 arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
 arg(noudevsync_ARG, '\0', "noudevsync", NULL, 0)
+arg(originname_ARG, '\0', "originname", string_arg, 0)
 arg(poll_ARG, '\0', "poll", yes_no_arg, 0)
 arg(poolmetadata_ARG, '\0', "poolmetadata", string_arg, 0)
 arg(poolmetadatasize_ARG, '\0', "poolmetadatasize", size_mb_arg, 0)
index 986539e130eb31389752fd9bf1955bfb2a9783e9..32d3c96911bb6c7441c3fb40b976a5c1f8e6868b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
  *
  * This file is part of LVM2.
  *
@@ -151,6 +151,8 @@ xx(lvconvert,
    "\t [--poolmetadatasize size]\n"
    "\t [-r|--readahead ReadAheadSectors|auto|none]\n"
    "\t [--stripes Stripes [-I|--stripesize StripeSize]]]\n"
+   "\t[-T|--thin ExternalLogicalVolume[Path]\n"
+   "\t [--originname NewExternalOriginVolumeName]]\n"
    "\t[-Z|--zero {y|n}]\n"
    "\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n",
 
@@ -158,7 +160,8 @@ xx(lvconvert,
    merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG,
    readahead_ARG, regionsize_ARG, repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG,
    trackchanges_ARG, type_ARG, stripes_long_ARG, stripesize_ARG, test_ARG,
-   chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG, thinpool_ARG,
+   chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG,
+   originname_ARG, thin_ARG, thinpool_ARG,
    use_policies_ARG, yes_ARG, force_ARG, zero_ARG)
 
 xx(lvcreate,
index 962a35ace49162b6fe80eb8394044d602182aac8..234eb41f46b96ca6d7a601f0fd76f4d06fd9f51d 100644 (file)
@@ -57,6 +57,7 @@ struct lvconvert_params {
        struct logical_volume *lv_to_poll;
 
        uint64_t poolmetadata_size;
+       const char *origin_lv_name;
        const char *pool_data_lv_name;
        const char *pool_metadata_lv_name;
        thin_discards_t discards;
@@ -68,6 +69,7 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
 {
        char *ptr;
        const char *vg_name = NULL;
+       const char *tmp_str;
 
        if (lp->merge)
                return 1;
@@ -94,16 +96,35 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
 
        if (lp->pool_data_lv_name) {
                if (*pargc) {
-                       log_error("More then one logical volume name name specified.");
-                       return 0;
+                       if (!arg_count(cmd, thin_ARG)) {
+                               log_error("More then one logical volume name specified.");
+                               return 0;
+                       }
+               } else {
+                       if (arg_count(cmd, thin_ARG)) {
+                               log_error("External thin volume name is missing.");
+                               return 0;
+                       }
+
+                       if (!lp->vg_name || !validate_name(lp->vg_name)) {
+                               log_error("Please provide a valid volume group name.");
+                               return 0;
+                       }
+
+                       lp->lv_name = lp->pool_data_lv_name;
+                       return 1;
                }
+       }
 
-               if (!lp->vg_name || !validate_name(lp->vg_name)) {
-                       log_error("Please provide a valid volume group name.");
-                       return 0;
+       if (lp->origin_lv_name) {
+               /* FIXME: Using generic routine */
+               if (strchr(lp->origin_lv_name, '/')) {
+                       if (!(lp->vg_name = extract_vgname(cmd, lp->origin_lv_name)))
+                               return_0;
+                       /* Strip VG from origin_lv_name */
+                       if ((tmp_str = strrchr(lp->origin_lv_name, '/')))
+                               lp->origin_lv_name = tmp_str + 1;
                }
-               lp->lv_name = lp->pool_data_lv_name;
-               return 1; /* Create metadata LV on it's own */
        }
 
        if (!*pargc) {
@@ -219,6 +240,9 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
                        return 0;
                }
                lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN);
+       } else if (arg_count(cmd, thin_ARG)) {
+               log_error("--thin is only valid with --thinpool.");
+               return 0;
        } else if (arg_count(cmd, discards_ARG)) {
                log_error("--discards is only valid with --thinpool.");
                return 0;
@@ -376,6 +400,13 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
                        lp->pool_data_lv_name = tmp_str + 1;
                }
 
+               if (arg_count(cmd, originname_ARG)) {
+                       if (!(lp->origin_lv_name = arg_str_value(cmd, originname_ARG, NULL))) {
+                               log_error("--originname is invalid.");
+                               return 0;
+                       }
+               }
+
                lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "thin-pool"));
                if (!lp->segtype)
                        return_0;
@@ -1825,6 +1856,116 @@ out:
        return r;
 }
 
+/* Swap lvid and LV names */
+static int _swap_lv(struct cmd_context *cmd,
+                   struct logical_volume *a, struct logical_volume *b)
+{
+       union lvid lvid;
+       const char *name;
+
+       lvid = a->lvid;
+       a->lvid = b->lvid;
+       b->lvid = lvid;
+
+       name = a->name;
+       a->name = b->name;
+       if (!lv_rename_update(cmd, b, name, 0))
+               return_0;
+
+       return 1;
+}
+
+static int _lvconvert_thinpool_external(struct cmd_context *cmd,
+                                       struct logical_volume *pool_lv,
+                                       struct logical_volume *external_lv,
+                                       struct lvconvert_params *lp)
+{
+       struct logical_volume *torigin_lv;
+       struct volume_group *vg = pool_lv->vg;
+       struct lvcreate_params lvc = { 0 };
+
+       dm_list_init(&lvc.tags);
+
+       if (!(lvc.segtype = get_segtype_from_string(cmd, "thin")))
+               return_0;
+
+       lvc.activate = CHANGE_AE;
+       lvc.alloc = ALLOC_INHERIT;
+       lvc.lv_name = lp->origin_lv_name;
+       lvc.major = -1;
+       lvc.minor = -1;
+       lvc.permission = LVM_READ;
+       lvc.pool = pool_lv->name;
+       lvc.pvh = &vg->pvs;
+       lvc.read_ahead = DM_READ_AHEAD_AUTO;
+       lvc.stripes = 1;
+       lvc.vg_name = vg->name;
+       lvc.voriginextents = external_lv->le_count;
+       lvc.voriginsize = external_lv->size;
+
+       /* New thin LV needs to be created (all messages sent to pool) */
+       if (!(torigin_lv = lv_create_single(vg, &lvc)))
+               return_0;
+
+       /* Activate again via -torigin, so this active LV is not needed */
+       if (!deactivate_lv(cmd, torigin_lv)) {
+               log_error("Aborting. Unable to deactivate new LV. "
+                         "Manual intervention required.");
+               return 0;
+       }
+
+       /*
+        * Crashing till this point will leave plain thin volume
+        * which could be easily removed by the user after i.e. power-off
+        */
+
+       if (!_swap_lv(cmd, torigin_lv, external_lv)) {
+               stack;
+               goto revert_new_lv;
+       }
+
+       /* Preserve read-write status of original LV here */
+       torigin_lv->status |= (external_lv->status & LVM_WRITE);
+
+       if (!attach_thin_external_origin(first_seg(torigin_lv), external_lv)) {
+               stack;
+               goto revert_new_lv;
+       }
+
+       if (!_reload_lv(cmd, vg, torigin_lv)) {
+               stack;
+               goto deactivate_and_revert_new_lv;
+       }
+
+       log_print_unless_silent("Converted %s/%s to thin external origin.",
+                               vg->name, external_lv->name);
+
+       return 1;
+
+deactivate_and_revert_new_lv:
+       if (!_swap_lv(cmd, torigin_lv, external_lv))
+               stack;
+
+       if (!deactivate_lv(cmd, torigin_lv)) {
+               log_error("Unable to deactivate failed new LV. "
+                         "Manual intervention required.");
+               return 0;
+       }
+
+       if (!detach_thin_external_origin(first_seg(torigin_lv)))
+               return_0;
+
+revert_new_lv:
+       /* FIXME Better to revert to backup of metadata? */
+       if (!lv_remove(torigin_lv) || !vg_write(vg) || !vg_commit(vg))
+               log_error("Manual intervention may be required to remove "
+                         "abandoned LV(s) before retrying.");
+       else
+               backup(vg);
+
+       return 0;
+}
+
 /*
  * Thin lvconvert version which
  *  rename metadata
@@ -1843,6 +1984,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
        struct logical_volume *data_lv;
        struct logical_volume *metadata_lv;
        struct logical_volume *pool_metadata_lv;
+       struct logical_volume *external_lv = NULL;
 
        if (!lv_is_visible(pool_lv)) {
                log_error("Can't convert internal LV %s/%s.",
@@ -1850,6 +1992,19 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
                return 0;
        }
 
+       if (arg_count(cmd, thin_ARG)) {
+               external_lv = pool_lv;
+               if (!(pool_lv = find_lv(external_lv->vg, lp->pool_data_lv_name))) {
+                       log_error("Can't find pool LV %s/%s.",
+                                 external_lv->vg->name, lp->pool_data_lv_name);
+                       return 0;
+               }
+               if (lv_is_thin_pool(pool_lv)) {
+                       r = 1; /* Already existing thin pool */
+                       goto out;
+               }
+       }
+
        if (lv_is_thin_type(pool_lv) && !lp->pool_metadata_lv_name) {
                log_error("Can't use thin logical volume %s/%s for thin pool data.",
                          pool_lv->vg->name, pool_lv->name);
@@ -2064,7 +2219,12 @@ mda_write:
 
        r = 1;
 out:
+       if (r && external_lv &&
+           !(r = _lvconvert_thinpool_external(cmd, pool_lv, external_lv, lp)))
+               stack;
+
        backup(pool_lv->vg);
+
        return r;
 }
 
This page took 0.055839 seconds and 5 git commands to generate.