2005-06-06 21:12:08 +04:00
/*
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2005 - 2007 Red Hat , Inc . All rights reserved .
2005-06-06 21:12:08 +04:00
*
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2005-06-06 21:12:08 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2005-06-06 21:12:08 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "tools.h"
2007-12-22 15:13:29 +03:00
# include "polldaemon.h"
2005-11-29 21:20:23 +03:00
# include "lv_alloc.h"
2010-05-24 19:32:20 +04:00
# include "metadata.h"
2005-06-06 21:12:08 +04:00
struct lvconvert_params {
2006-04-06 00:43:23 +04:00
int snapshot ;
2010-01-13 04:45:15 +03:00
int merge ;
2006-04-06 00:43:23 +04:00
int zero ;
const char * origin ;
2005-08-15 18:10:28 +04:00
const char * lv_name ;
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
const char * lv_split_name ;
2007-12-22 15:13:29 +03:00
const char * lv_name_full ;
2006-04-06 00:43:23 +04:00
const char * vg_name ;
2008-01-15 00:11:47 +03:00
int wait_completion ;
int need_polling ;
2006-04-06 00:43:23 +04:00
uint32_t chunk_size ;
uint32_t region_size ;
2005-08-15 18:10:28 +04:00
uint32_t mirrors ;
2005-11-29 21:20:23 +03:00
sign_t mirrors_sign ;
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
uint32_t keep_mimages ;
2010-04-13 05:54:32 +04:00
uint32_t stripes ;
uint32_t stripe_size ;
2006-04-06 00:43:23 +04:00
struct segment_type * segtype ;
2005-08-15 18:10:28 +04:00
alloc_policy_t alloc ;
int pv_count ;
char * * pvs ;
2008-11-04 01:14:30 +03:00
struct dm_list * pvh ;
2010-01-08 16:04:10 +03:00
struct dm_list * failed_pvs ;
2010-01-13 04:49:52 +03:00
struct logical_volume * lv_to_poll ;
2005-06-06 21:12:08 +04:00
} ;
2006-04-19 19:33:07 +04:00
static int _lvconvert_name_params ( struct lvconvert_params * lp ,
struct cmd_context * cmd ,
int * pargc , char * * * pargv )
2006-04-06 00:43:23 +04:00
{
char * ptr ;
2006-04-07 18:14:31 +04:00
const char * vg_name = NULL ;
2006-04-06 00:43:23 +04:00
2010-02-06 01:44:37 +03:00
if ( lp - > merge )
return 1 ;
if ( lp - > snapshot ) {
2006-04-06 00:43:23 +04:00
if ( ! * pargc ) {
log_error ( " Please specify a logical volume to act as "
" the snapshot origin. " ) ;
return 0 ;
}
lp - > origin = * pargv [ 0 ] ;
( * pargv ) + + , ( * pargc ) - - ;
if ( ! ( lp - > vg_name = extract_vgname ( cmd , lp - > origin ) ) ) {
log_error ( " The origin name should include the "
" volume group. " ) ;
return 0 ;
}
/* Strip the volume group from the origin */
if ( ( ptr = strrchr ( lp - > origin , ( int ) ' / ' ) ) )
lp - > origin = ptr + 1 ;
}
if ( ! * pargc ) {
log_error ( " Please provide logical volume path " ) ;
return 0 ;
}
2007-12-22 15:13:29 +03:00
lp - > lv_name = lp - > lv_name_full = ( * pargv ) [ 0 ] ;
2006-04-06 00:43:23 +04:00
( * pargv ) + + , ( * pargc ) - - ;
2007-12-22 15:13:29 +03:00
if ( strchr ( lp - > lv_name_full , ' / ' ) & &
( vg_name = extract_vgname ( cmd , lp - > lv_name_full ) ) & &
2006-04-06 00:43:23 +04:00
lp - > vg_name & & strcmp ( vg_name , lp - > vg_name ) ) {
log_error ( " Please use a single volume group name "
" ( \" %s \" or \" %s \" ) " , vg_name , lp - > vg_name ) ;
return 0 ;
}
if ( ! lp - > vg_name )
lp - > vg_name = vg_name ;
if ( ! validate_name ( lp - > vg_name ) ) {
log_error ( " Please provide a valid volume group name " ) ;
return 0 ;
}
2007-12-22 15:13:29 +03:00
if ( ( ptr = strrchr ( lp - > lv_name_full , ' / ' ) ) )
2006-04-06 00:43:23 +04:00
lp - > lv_name = ptr + 1 ;
if ( ! apply_lvname_restrictions ( lp - > lv_name ) )
return_0 ;
2010-02-06 01:44:37 +03:00
if ( * pargc & & lp - > snapshot ) {
2010-01-13 04:45:15 +03:00
log_error ( " Too many arguments provided for snapshots " ) ;
return 0 ;
}
2006-04-06 00:43:23 +04:00
return 1 ;
}
2005-08-15 18:10:28 +04:00
static int _read_params ( struct lvconvert_params * lp , struct cmd_context * cmd ,
int argc , char * * argv )
{
2006-04-28 19:01:39 +04:00
int region_size ;
2006-08-17 22:23:44 +04:00
int pagesize = lvm_getpagesize ( ) ;
2006-04-28 19:01:39 +04:00
2005-08-15 18:10:28 +04:00
memset ( lp , 0 , sizeof ( * lp ) ) ;
2010-01-13 04:45:15 +03:00
if ( ( arg_count ( cmd , snapshot_ARG ) | | arg_count ( cmd , merge_ARG ) ) & &
2009-06-30 22:39:31 +04:00
( arg_count ( cmd , mirrorlog_ARG ) | | arg_count ( cmd , mirrors_ARG ) | |
arg_count ( cmd , repair_ARG ) ) ) {
2010-01-13 04:45:15 +03:00
log_error ( " --snapshot or --merge argument cannot be mixed "
2009-06-30 22:39:31 +04:00
" with --mirrors, --repair or --log " ) ;
2006-04-06 00:43:23 +04:00
return 0 ;
}
2008-01-15 00:11:47 +03:00
if ( ! arg_count ( cmd , background_ARG ) )
lp - > wait_completion = 1 ;
2006-04-06 00:43:23 +04:00
if ( arg_count ( cmd , snapshot_ARG ) )
lp - > snapshot = 1 ;
2010-01-13 04:45:15 +03:00
if ( arg_count ( cmd , snapshot_ARG ) & & arg_count ( cmd , merge_ARG ) ) {
log_error ( " --snapshot and --merge are mutually exclusive " ) ;
return 0 ;
}
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
if ( arg_count ( cmd , splitmirrors_ARG ) & & arg_count ( cmd , mirrors_ARG ) ) {
log_error ( " --mirrors and --splitmirrors are "
" mutually exclusive " ) ;
return 0 ;
}
/*
* The ' - - splitmirrors n ' argument is equivalent to ' - - mirrors - n '
* ( note the minus sign ) , except that it signifies the additional
* intent to keep the mimage that is detached , rather than
* discarding it .
*/
if ( arg_count ( cmd , splitmirrors_ARG ) ) {
if ( ! arg_count ( cmd , name_ARG ) ) {
log_error ( " Please name the new logical volume using '--name' " ) ;
return 0 ;
}
lp - > lv_split_name = arg_value ( cmd , name_ARG ) ;
if ( ! apply_lvname_restrictions ( lp - > lv_split_name ) )
return_0 ;
lp - > keep_mimages = 1 ;
if ( arg_sign_value ( cmd , mirrors_ARG , 0 ) = = SIGN_MINUS ) {
log_error ( " Argument to --splitmirrors "
" cannot be negative " ) ;
return 0 ;
}
lp - > mirrors = arg_uint_value ( cmd , splitmirrors_ARG , 0 ) ;
lp - > mirrors_sign = SIGN_MINUS ;
} else if ( arg_count ( cmd , name_ARG ) ) {
log_error ( " The 'name' argument is only valid "
" with --splitmirrors " ) ;
return 0 ;
}
2010-01-13 04:45:15 +03:00
if ( arg_count ( cmd , merge_ARG ) )
lp - > merge = 1 ;
2006-04-06 00:43:23 +04:00
if ( arg_count ( cmd , mirrors_ARG ) ) {
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
/*
* - - splitmirrors has been chosen as the mechanism for
* specifying the intent of detaching and keeping a mimage
* versus an additional qualifying argument being added here .
*/
2006-04-06 00:43:23 +04:00
lp - > mirrors = arg_uint_value ( cmd , mirrors_ARG , 0 ) ;
lp - > mirrors_sign = arg_sign_value ( cmd , mirrors_ARG , 0 ) ;
}
2009-11-03 18:50:42 +03:00
lp - > alloc = arg_uint_value ( cmd , alloc_ARG , ALLOC_INHERIT ) ;
2005-08-15 18:10:28 +04:00
2010-04-13 05:54:32 +04:00
/* There are three types of lvconvert. */
if ( lp - > merge ) { /* Snapshot merge */
2010-01-13 04:45:15 +03:00
if ( arg_count ( cmd , regionsize_ARG ) | | arg_count ( cmd , chunksize_ARG ) | |
2010-04-13 05:54:32 +04:00
arg_count ( cmd , zero_ARG ) | | arg_count ( cmd , regionsize_ARG ) | |
arg_count ( cmd , stripes_long_ARG ) | | arg_count ( cmd , stripesize_ARG ) ) {
2010-01-13 04:45:15 +03:00
log_error ( " Only --background and --interval are valid "
" arguments for snapshot merge " ) ;
return 0 ;
}
if ( ! ( lp - > segtype = get_segtype_from_string ( cmd , " snapshot " ) ) )
return_0 ;
2010-04-13 05:54:32 +04:00
} else if ( lp - > snapshot ) { /* Snapshot creation from pre-existing cow */
2006-04-06 00:43:23 +04:00
if ( arg_count ( cmd , regionsize_ARG ) ) {
log_error ( " --regionsize is only available with mirrors " ) ;
return 0 ;
}
2010-04-13 05:54:32 +04:00
if ( arg_count ( cmd , stripesize_ARG ) | | arg_count ( cmd , stripes_long_ARG ) ) {
log_error ( " --stripes and --stripesize are only available with striped mirrors " ) ;
return 0 ;
}
2006-04-06 00:43:23 +04:00
if ( arg_sign_value ( cmd , chunksize_ARG , 0 ) = = SIGN_MINUS ) {
log_error ( " Negative chunk size is invalid " ) ;
return 0 ;
}
2007-11-14 03:08:25 +03:00
lp - > chunk_size = arg_uint_value ( cmd , chunksize_ARG , 8 ) ;
2006-04-06 00:43:23 +04:00
if ( lp - > chunk_size < 8 | | lp - > chunk_size > 1024 | |
( lp - > chunk_size & ( lp - > chunk_size - 1 ) ) ) {
log_error ( " Chunk size must be a power of 2 in the "
" range 4K to 512K " ) ;
return 0 ;
}
log_verbose ( " Setting chunksize to %d sectors. " , lp - > chunk_size ) ;
2005-08-15 18:10:28 +04:00
2006-04-06 00:43:23 +04:00
if ( ! ( lp - > segtype = get_segtype_from_string ( cmd , " snapshot " ) ) )
return_0 ;
2005-11-29 21:20:23 +03:00
2006-04-06 00:43:23 +04:00
lp - > zero = strcmp ( arg_str_value ( cmd , zero_ARG ,
( lp - > segtype - > flags &
SEG_CANNOT_BE_ZEROED ) ?
" n " : " y " ) , " n " ) ;
} else { /* Mirrors */
if ( arg_count ( cmd , chunksize_ARG ) ) {
log_error ( " --chunksize is only available with "
" snapshots " ) ;
2005-11-29 21:20:23 +03:00
return 0 ;
}
2006-04-06 00:43:23 +04:00
if ( arg_count ( cmd , zero_ARG ) ) {
log_error ( " --zero is only available with snapshots " ) ;
return 0 ;
}
/*
* - - regionsize is only valid if converting an LV into a mirror .
* Checked when we know the state of the LV being converted .
*/
2006-04-28 19:01:39 +04:00
2006-04-06 00:43:23 +04:00
if ( arg_count ( cmd , regionsize_ARG ) ) {
if ( arg_sign_value ( cmd , regionsize_ARG , 0 ) = =
SIGN_MINUS ) {
log_error ( " Negative regionsize is invalid " ) ;
return 0 ;
}
2007-11-14 03:08:25 +03:00
lp - > region_size = arg_uint_value ( cmd , regionsize_ARG , 0 ) ;
2006-04-28 19:01:39 +04:00
} else {
2006-05-16 20:48:31 +04:00
region_size = 2 * find_config_tree_int ( cmd ,
2006-04-06 00:43:23 +04:00
" activation/mirror_region_size " ,
DEFAULT_MIRROR_REGION_SIZE ) ;
2006-04-28 19:01:39 +04:00
if ( region_size < 0 ) {
log_error ( " Negative regionsize in "
" configuration file is invalid " ) ;
return 0 ;
}
lp - > region_size = region_size ;
}
2006-04-28 21:25:54 +04:00
if ( lp - > region_size % ( pagesize > > SECTOR_SHIFT ) ) {
log_error ( " Region size (% " PRIu32 " ) must be "
" a multiple of machine memory "
" page size (%d) " ,
lp - > region_size , pagesize > > SECTOR_SHIFT ) ;
return 0 ;
}
2006-04-06 00:43:23 +04:00
if ( lp - > region_size & ( lp - > region_size - 1 ) ) {
log_error ( " Region size (% " PRIu32
" ) must be a power of 2 " , lp - > region_size ) ;
return 0 ;
}
2006-04-28 17:11:05 +04:00
if ( ! lp - > region_size ) {
log_error ( " Non-zero region size must be supplied. " ) ;
return 0 ;
}
2010-04-13 05:54:32 +04:00
/* Default is never striped, regardless of existing LV configuration. */
if ( ! get_stripe_params ( cmd , & lp - > stripes , & lp - > stripe_size ) ) {
stack ;
return 0 ;
}
2007-08-23 00:03:46 +04:00
if ( ! ( lp - > segtype = get_segtype_from_string ( cmd , " mirror " ) ) )
2006-04-06 00:43:23 +04:00
return_0 ;
2005-11-29 21:20:23 +03:00
}
2005-08-15 18:10:28 +04:00
2006-04-06 00:43:23 +04:00
if ( activation ( ) & & lp - > segtype - > ops - > target_present & &
2009-02-28 23:04:24 +03:00
! lp - > segtype - > ops - > target_present ( cmd , NULL , NULL ) ) {
2006-04-06 00:43:23 +04:00
log_error ( " %s: Required device-mapper target(s) not "
" detected in your kernel " , lp - > segtype - > name ) ;
2005-08-15 18:10:28 +04:00
return 0 ;
}
2006-04-19 19:33:07 +04:00
if ( ! _lvconvert_name_params ( lp , cmd , & argc , & argv ) )
2006-04-06 00:43:23 +04:00
return_0 ;
2005-08-15 18:10:28 +04:00
lp - > pv_count = argc ;
lp - > pvs = argv ;
2010-01-08 16:04:10 +03:00
lp - > failed_pvs = NULL ;
2005-08-15 18:10:28 +04:00
return 1 ;
}
2007-12-22 15:13:29 +03:00
static struct volume_group * _get_lvconvert_vg ( struct cmd_context * cmd ,
2010-02-06 01:40:49 +03:00
const char * name ,
const char * uuid __attribute ( ( unused ) ) )
2007-12-22 15:13:29 +03:00
{
2008-01-30 17:00:02 +03:00
dev_close_all ( ) ;
2007-12-22 15:13:29 +03:00
2010-02-06 01:40:49 +03:00
if ( name & & ! strchr ( name , ' / ' ) )
return vg_read_for_update ( cmd , name , NULL , 0 ) ;
/* 'name' is the full LV name; must extract_vgname() */
return vg_read_for_update ( cmd , extract_vgname ( cmd , name ) ,
2009-07-01 20:59:37 +04:00
NULL , 0 ) ;
2007-12-22 15:13:29 +03:00
}
static struct logical_volume * _get_lvconvert_lv ( struct cmd_context * cmd __attribute ( ( unused ) ) ,
struct volume_group * vg ,
const char * name ,
2009-06-01 18:43:27 +04:00
const char * uuid ,
2007-12-22 15:13:29 +03:00
uint32_t lv_type __attribute ( ( unused ) ) )
{
2009-06-01 18:43:27 +04:00
struct logical_volume * lv = find_lv ( vg , name ) ;
if ( ! lv | | ( uuid & & strcmp ( uuid , ( char * ) & lv - > lvid ) ) )
return NULL ;
return lv ;
2007-12-22 15:13:29 +03:00
}
static int _finish_lvconvert_mirror ( struct cmd_context * cmd ,
struct volume_group * vg ,
struct logical_volume * lv ,
2008-11-04 01:14:30 +03:00
struct dm_list * lvs_changed __attribute ( ( unused ) ) )
2007-12-22 15:13:29 +03:00
{
2009-04-21 18:31:57 +04:00
int r = 0 ;
2010-02-06 00:49:16 +03:00
if ( ! ( lv - > status & CONVERTING ) )
return 1 ;
2007-12-22 15:13:29 +03:00
if ( ! collapse_mirrored_lv ( lv ) ) {
log_error ( " Failed to remove temporary sync layer. " ) ;
return 0 ;
}
2008-01-10 21:35:51 +03:00
lv - > status & = ~ CONVERTING ;
2007-12-22 15:13:29 +03:00
log_very_verbose ( " Updating logical volume \" %s \" on disk(s) " , lv - > name ) ;
if ( ! vg_write ( vg ) )
return_0 ;
if ( ! suspend_lv ( cmd , lv ) ) {
log_error ( " Failed to lock %s " , lv - > name ) ;
vg_revert ( vg ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2007-12-22 15:13:29 +03:00
}
if ( ! vg_commit ( vg ) ) {
resume_lv ( cmd , lv ) ;
2009-04-21 18:31:57 +04:00
goto_out ;
2007-12-22 15:13:29 +03:00
}
log_very_verbose ( " Updating \" %s \" in kernel " , lv - > name ) ;
if ( ! resume_lv ( cmd , lv ) ) {
log_error ( " Problem reactivating %s " , lv - > name ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2007-12-22 15:13:29 +03:00
}
2009-04-21 18:31:57 +04:00
r = 1 ;
2007-12-22 15:13:29 +03:00
log_print ( " Logical volume %s converted. " , lv - > name ) ;
2009-04-21 18:31:57 +04:00
out :
backup ( vg ) ;
return r ;
2007-12-22 15:13:29 +03:00
}
2010-01-13 04:49:52 +03:00
static int _finish_lvconvert_merge ( struct cmd_context * cmd ,
struct volume_group * vg ,
struct logical_volume * lv ,
struct dm_list * lvs_changed __attribute ( ( unused ) ) )
{
2010-01-13 04:55:43 +03:00
struct lv_segment * snap_seg = find_merging_cow ( lv ) ;
2010-01-13 04:49:52 +03:00
if ( ! snap_seg ) {
log_error ( " Logical volume %s has no merging snapshot. " , lv - > name ) ;
return 0 ;
}
log_print ( " Merge of snapshot into logical volume %s has finished. " , lv - > name ) ;
if ( ! lv_remove_single ( cmd , snap_seg - > cow , DONT_PROMPT ) ) {
log_error ( " Could not remove snapshot %s merged into %s. " ,
snap_seg - > cow - > name , lv - > name ) ;
return 0 ;
}
return 1 ;
}
static progress_t _poll_merge_progress ( struct cmd_context * cmd ,
struct logical_volume * lv ,
const char * name __attribute ( ( unused ) ) ,
struct daemon_parms * parms )
{
float percent = 0.0 ;
percent_range_t percent_range ;
if ( ! lv_snapshot_percent ( lv , & percent , & percent_range ) ) {
2010-01-16 01:58:25 +03:00
log_error ( " %s: Failed query for merging percentage. Aborting merge. " , lv - > name ) ;
2010-01-13 04:49:52 +03:00
return PROGRESS_CHECK_FAILED ;
} else if ( percent_range = = PERCENT_INVALID ) {
log_error ( " %s: Merging snapshot invalidated. Aborting merge. " , lv - > name ) ;
return PROGRESS_CHECK_FAILED ;
}
if ( parms - > progress_display )
log_print ( " %s: %s: %.1f%% " , lv - > name , parms - > progress_title , percent ) ;
else
log_verbose ( " %s: %s: %.1f%% " , lv - > name , parms - > progress_title , percent ) ;
if ( percent_range = = PERCENT_0 )
return PROGRESS_FINISHED_ALL ;
return PROGRESS_UNFINISHED ;
}
2007-12-22 15:13:29 +03:00
static struct poll_functions _lvconvert_mirror_fns = {
. get_copy_vg = _get_lvconvert_vg ,
. get_copy_lv = _get_lvconvert_lv ,
2009-09-30 22:15:06 +04:00
. poll_progress = poll_mirror_progress ,
2007-12-22 15:13:29 +03:00
. finish_copy = _finish_lvconvert_mirror ,
} ;
2010-01-13 04:49:52 +03:00
static struct poll_functions _lvconvert_merge_fns = {
. get_copy_vg = _get_lvconvert_vg ,
. get_copy_lv = _get_lvconvert_lv ,
. poll_progress = _poll_merge_progress ,
. finish_copy = _finish_lvconvert_merge ,
} ;
2009-06-01 18:43:27 +04:00
int lvconvert_poll ( struct cmd_context * cmd , struct logical_volume * lv ,
2008-01-10 21:35:51 +03:00
unsigned background )
2007-12-22 15:13:29 +03:00
{
2010-02-06 01:40:49 +03:00
/*
* FIXME allocate an " object key " structure with split
* out members ( vg_name , lv_name , uuid , etc ) and pass that
* around the lvconvert and polldaemon code
* - will avoid needless work , e . g . extract_vgname ( )
* - unfortunately there are enough overloaded " name " dragons in
* the polldaemon , lvconvert , pvmove code that a comprehensive
* audit / rework is needed
*/
2009-06-01 18:43:27 +04:00
int len = strlen ( lv - > vg - > name ) + strlen ( lv - > name ) + 2 ;
char * uuid = alloca ( sizeof ( lv - > lvid ) ) ;
char * lv_full_name = alloca ( len ) ;
if ( ! uuid | | ! lv_full_name )
return_0 ;
if ( ! dm_snprintf ( lv_full_name , len , " %s/%s " , lv - > vg - > name , lv - > name ) )
return_0 ;
memcpy ( uuid , & lv - > lvid , sizeof ( lv - > lvid ) ) ;
2010-01-13 04:55:43 +03:00
if ( ! lv_is_merging_origin ( lv ) )
2010-01-13 04:49:52 +03:00
return poll_daemon ( cmd , lv_full_name , uuid , background , 0 ,
& _lvconvert_mirror_fns , " Converted " ) ;
else
return poll_daemon ( cmd , lv_full_name , uuid , background , 0 ,
& _lvconvert_merge_fns , " Merged " ) ;
2007-12-22 15:13:29 +03:00
}
2007-12-22 05:13:00 +03:00
static int _insert_lvconvert_layer ( struct cmd_context * cmd ,
struct logical_volume * lv )
{
char * format , * layer_name ;
size_t len ;
int i ;
/*
* We would like to give the same number for this layer
* and the newly added mimage .
* However , LV name of newly added mimage is determined * after *
* the LV name of this layer is determined .
*
* So , use generate_lv_name ( ) to generate mimage name first
* and take the number from it .
*/
len = strlen ( lv - > name ) + 32 ;
if ( ! ( format = alloca ( len ) ) | |
! ( layer_name = alloca ( len ) ) | |
dm_snprintf ( format , len , " %s_mimage_%%d " , lv - > name ) < 0 ) {
log_error ( " lvconvert: layer name allocation failed. " ) ;
return 0 ;
}
if ( ! generate_lv_name ( lv - > vg , format , layer_name , len ) | |
sscanf ( layer_name , format , & i ) ! = 1 ) {
log_error ( " lvconvert: layer name generation failed. " ) ;
return 0 ;
}
if ( dm_snprintf ( layer_name , len , MIRROR_SYNC_LAYER " _%d " , i ) < 0 ) {
log_error ( " layer name allocation failed. " ) ;
return 0 ;
}
if ( ! insert_layer_for_lv ( cmd , lv , 0 , layer_name ) ) {
log_error ( " Failed to insert resync layer " ) ;
return 0 ;
}
return 1 ;
}
2009-04-23 20:56:21 +04:00
static int _area_missing ( struct lv_segment * lvseg , int s )
{
if ( seg_type ( lvseg , s ) = = AREA_LV ) {
if ( seg_lv ( lvseg , s ) - > status & PARTIAL_LV )
return 1 ;
2009-05-21 07:04:52 +04:00
} else if ( ( seg_type ( lvseg , s ) = = AREA_PV ) & &
2010-03-16 17:37:38 +03:00
( is_missing_pv ( seg_pv ( lvseg , s ) ) ) )
2009-05-21 07:04:52 +04:00
return 1 ;
2009-04-23 20:56:21 +04:00
return 0 ;
}
/* FIXME we want to handle mirror stacks here... */
2009-05-21 07:04:52 +04:00
static int _failed_mirrors_count ( struct logical_volume * lv )
2009-04-23 20:56:21 +04:00
{
struct lv_segment * lvseg ;
int ret = 0 ;
int s ;
2009-05-21 07:04:52 +04:00
2009-04-23 20:56:21 +04:00
dm_list_iterate_items ( lvseg , & lv - > segments ) {
if ( ! seg_is_mirrored ( lvseg ) )
return - 1 ;
2009-05-21 07:04:52 +04:00
for ( s = 0 ; s < lvseg - > area_count ; s + + )
2009-04-23 20:56:21 +04:00
if ( _area_missing ( lvseg , s ) )
2009-05-21 07:04:52 +04:00
ret + + ;
2009-04-23 20:56:21 +04:00
}
2009-05-21 07:04:52 +04:00
2009-04-23 20:56:21 +04:00
return ret ;
}
static struct dm_list * _failed_pv_list ( struct volume_group * vg )
{
2009-05-21 07:04:52 +04:00
struct dm_list * failed_pvs ;
2009-04-23 20:56:21 +04:00
struct pv_list * pvl , * new_pvl ;
2009-05-21 07:04:52 +04:00
if ( ! ( failed_pvs = dm_pool_alloc ( vg - > vgmem , sizeof ( * failed_pvs ) ) ) ) {
log_error ( " Allocation of list of failed_pvs failed. " ) ;
return_NULL ;
2009-04-23 20:56:21 +04:00
}
2009-05-21 07:04:52 +04:00
dm_list_init ( failed_pvs ) ;
2009-04-23 20:56:21 +04:00
dm_list_iterate_items ( pvl , & vg - > pvs ) {
2010-03-16 17:37:38 +03:00
if ( ! is_missing_pv ( pvl - > pv ) )
2009-04-23 20:56:21 +04:00
continue ;
2010-01-06 16:26:21 +03:00
/*
* Finally , - - repair will remove empty PVs .
* But we only want remove these which are output of repair ,
* Do not count these which are already empty here .
* FIXME : code should traverse PV in LV not in whole VG .
* FIXME : layer violation ? should it depend on vgreduce - - removemising ?
*/
if ( pvl - > pv - > pe_alloc_count = = 0 )
continue ;
2009-04-23 20:56:21 +04:00
if ( ! ( new_pvl = dm_pool_alloc ( vg - > vgmem , sizeof ( * new_pvl ) ) ) ) {
2009-05-21 07:04:52 +04:00
log_error ( " Allocation of failed_pvs list entry failed. " ) ;
return_NULL ;
2009-04-23 20:56:21 +04:00
}
new_pvl - > pv = pvl - > pv ;
2009-05-21 07:04:52 +04:00
dm_list_add ( failed_pvs , & new_pvl - > list ) ;
2009-04-23 20:56:21 +04:00
}
2009-05-21 07:04:52 +04:00
return failed_pvs ;
2009-04-23 20:56:21 +04:00
}
2010-05-24 19:32:20 +04:00
static int _is_partial_lv ( struct logical_volume * lv ,
void * baton __attribute ( ( unused ) ) )
{
return lv - > status & PARTIAL_LV ;
}
2009-05-21 07:04:52 +04:00
/*
* Walk down the stacked mirror LV to the original mirror LV .
*/
2008-01-16 22:16:48 +03:00
static struct logical_volume * _original_lv ( struct logical_volume * lv )
{
struct logical_volume * next_lv = lv , * tmp_lv ;
while ( ( tmp_lv = find_temporary_mirror ( next_lv ) ) )
next_lv = tmp_lv ;
return next_lv ;
}
2009-06-04 16:01:15 +04:00
static void _lvconvert_mirrors_repair_ask ( struct cmd_context * cmd ,
int failed_log , int failed_mirrors ,
int * replace_log , int * replace_mirrors )
{
const char * leg_policy = NULL , * log_policy = NULL ;
int force = arg_count ( cmd , force_ARG ) ;
int yes = arg_count ( cmd , yes_ARG ) ;
* replace_log = * replace_mirrors = 1 ;
if ( arg_count ( cmd , use_policies_ARG ) ) {
leg_policy = find_config_tree_str ( cmd ,
2010-01-06 16:27:06 +03:00
" activation/mirror_image_fault_policy " , NULL ) ;
if ( ! leg_policy )
leg_policy = find_config_tree_str ( cmd ,
2009-06-04 16:01:15 +04:00
" activation/mirror_device_fault_policy " ,
DEFAULT_MIRROR_DEVICE_FAULT_POLICY ) ;
log_policy = find_config_tree_str ( cmd ,
" activation/mirror_log_fault_policy " ,
DEFAULT_MIRROR_LOG_FAULT_POLICY ) ;
* replace_mirrors = strcmp ( leg_policy , " remove " ) ;
* replace_log = strcmp ( log_policy , " remove " ) ;
return ;
}
if ( yes )
return ;
if ( force ! = PROMPT ) {
* replace_log = * replace_mirrors = 0 ;
return ;
}
if ( failed_log & &
yes_no_prompt ( " Attempt to replace failed mirror log? [y/n]: " ) = = ' n ' ) {
* replace_log = 0 ;
}
if ( failed_mirrors & &
yes_no_prompt ( " Attempt to replace failed mirror images "
" (requires full device resync)? [y/n]: " ) = = ' n ' ) {
* replace_mirrors = 0 ;
}
}
2010-03-27 01:15:43 +03:00
/*
* _get_log_count
* @ lv : the mirror LV
*
* Get the number of on - disk copies of the log .
* 0 = ' core '
* 1 = ' disk '
* 2 + = ' mirrored '
*/
static int _get_log_count ( struct logical_volume * lv )
2009-08-03 01:56:29 +04:00
{
2010-03-27 01:15:43 +03:00
struct logical_volume * log_lv ;
log_lv = first_seg ( _original_lv ( lv ) ) - > log_lv ;
if ( ! log_lv )
return 0 ;
return lv_mirror_count ( log_lv ) ;
2009-08-03 01:56:29 +04:00
}
2010-06-24 00:32:29 +04:00
static int _lv_update_mirrored_log ( struct logical_volume * lv ,
struct dm_list * operable_pvs ,
int log_count )
{
int old_log_count ;
struct logical_volume * log_lv ;
log_lv = first_seg ( _original_lv ( lv ) ) - > log_lv ;
if ( ! log_lv | | ! ( log_lv - > status & MIRRORED ) )
return 1 ;
old_log_count = _get_log_count ( lv ) ;
if ( old_log_count = = log_count )
return 1 ;
/* Reducing redundancy of the log */
return remove_mirror_images ( log_lv , log_count ,
is_mirror_image_removable ,
operable_pvs , 0U ) ;
}
2009-08-03 01:56:29 +04:00
static int _lv_update_log_type ( struct cmd_context * cmd ,
struct lvconvert_params * lp ,
struct logical_volume * lv ,
2010-03-27 01:15:43 +03:00
struct dm_list * operable_pvs ,
2010-01-09 01:32:35 +03:00
int log_count )
2009-08-03 01:56:29 +04:00
{
2010-03-27 01:15:43 +03:00
uint32_t region_size ;
int old_log_count ;
struct logical_volume * original_lv ;
struct logical_volume * log_lv ;
old_log_count = _get_log_count ( lv ) ;
if ( old_log_count = = log_count )
return 1 ;
original_lv = _original_lv ( lv ) ;
region_size = adjusted_mirror_region_size ( lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ;
/* Add a log where there is none */
if ( ! old_log_count ) {
2010-01-09 01:32:35 +03:00
if ( ! add_mirror_log ( cmd , original_lv , log_count ,
2010-03-27 01:15:43 +03:00
region_size , operable_pvs , lp - > alloc ) )
2009-08-03 01:56:29 +04:00
return_0 ;
2010-03-27 01:15:43 +03:00
return 1 ;
}
/* Remove an existing log completely */
if ( ! log_count ) {
if ( ! remove_mirror_log ( cmd , original_lv , operable_pvs ) )
2009-08-03 01:56:29 +04:00
return_0 ;
2010-03-27 01:15:43 +03:00
return 1 ;
2009-08-03 01:56:29 +04:00
}
2010-03-27 01:15:43 +03:00
log_lv = first_seg ( original_lv ) - > log_lv ;
/* Adding redundancy to the log */
if ( old_log_count < log_count ) {
log_error ( " Adding log redundancy not supported yet. " ) ;
log_error ( " Try converting the log to 'core' first. " ) ;
return_0 ;
}
/* Reducing redundancy of the log */
2010-05-24 19:32:20 +04:00
return remove_mirror_images ( log_lv , log_count , is_mirror_image_removable , operable_pvs , 1U ) ;
2009-08-03 01:56:29 +04:00
}
2010-01-06 16:26:21 +03:00
/*
* Reomove missing and empty PVs from VG , if are also in provided list
*/
static void _remove_missing_empty_pv ( struct volume_group * vg , struct dm_list * remove_pvs )
{
struct pv_list * pvl , * pvl_vg , * pvlt ;
int removed = 0 ;
if ( ! remove_pvs )
return ;
dm_list_iterate_items ( pvl , remove_pvs ) {
dm_list_iterate_items_safe ( pvl_vg , pvlt , & vg - > pvs ) {
if ( ! id_equal ( & pvl - > pv - > id , & pvl_vg - > pv - > id ) | |
2010-03-16 17:37:38 +03:00
! is_missing_pv ( pvl_vg - > pv ) | |
2010-01-06 16:26:21 +03:00
pvl_vg - > pv - > pe_alloc_count ! = 0 )
continue ;
/* FIXME: duplication of vgreduce code, move this to library */
vg - > free_count - = pvl_vg - > pv - > pe_count ;
vg - > extent_count - = pvl_vg - > pv - > pe_count ;
2010-04-13 21:26:03 +04:00
del_pvl_from_vgs ( vg , pvl_vg ) ;
2010-01-06 16:26:21 +03:00
removed + + ;
}
}
if ( removed ) {
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) ) {
stack ;
return ;
}
log_warn ( " %d missing and now unallocated Physical Volumes removed from VG. " , removed ) ;
}
}
2010-03-27 01:15:43 +03:00
/*
* _lvconvert_mirrors_parse_params
*
* This function performs the following :
* 1 ) Gets the old values of mimage and log counts
* 2 ) Parses the CLI args to find the new desired values
* 3 ) Adjusts ' lp - > mirrors ' to the appropriate absolute value .
* ( Remember , ' lp - > mirrors ' is specified in terms of the number of " copies "
* vs . the number of mimages . It can also be a relative value . )
* 4 ) Sets ' lp - > need_polling ' if collapsing
* 5 ) Validates other mirror params
*
* Returns : 1 on success , 0 on error
*/
static int _lvconvert_mirrors_parse_params ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp ,
uint32_t * old_mimage_count ,
uint32_t * old_log_count ,
uint32_t * new_mimage_count ,
uint32_t * new_log_count )
2005-06-06 21:12:08 +04:00
{
2009-06-04 16:01:15 +04:00
int repair = arg_count ( cmd , repair_ARG ) ;
2010-03-27 01:15:43 +03:00
const char * mirrorlog ;
* old_mimage_count = lv_mirror_count ( lv ) ;
* old_log_count = _get_log_count ( lv ) ;
2007-12-21 04:08:18 +03:00
2010-03-27 01:15:43 +03:00
/*
* Collapsing a stack of mirrors :
*
* If called with no argument , try collapsing the resync layers
*/
2008-01-10 21:35:51 +03:00
if ( ! arg_count ( cmd , mirrors_ARG ) & & ! arg_count ( cmd , mirrorlog_ARG ) & &
2009-04-23 20:56:21 +04:00
! arg_count ( cmd , corelog_ARG ) & & ! arg_count ( cmd , regionsize_ARG ) & &
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
! arg_count ( cmd , splitmirrors_ARG ) & & ! repair ) {
2010-03-27 01:15:43 +03:00
* new_mimage_count = * old_mimage_count ;
* new_log_count = * old_log_count ;
2009-06-15 16:08:59 +04:00
if ( find_temporary_mirror ( lv ) | | ( lv - > status & CONVERTING ) )
lp - > need_polling = 1 ;
2007-12-22 15:13:29 +03:00
return 1 ;
2007-12-21 04:08:18 +03:00
}
2005-11-29 21:20:23 +03:00
2010-03-27 01:15:43 +03:00
if ( ( arg_count ( cmd , mirrors_ARG ) & & repair ) | |
( arg_count ( cmd , mirrorlog_ARG ) & & repair ) | |
( arg_count ( cmd , corelog_ARG ) & & repair ) ) {
log_error ( " --repair cannot be used with --mirrors, --mirrorlog, "
" or --corelog " ) ;
return 0 ;
}
if ( arg_count ( cmd , mirrorlog_ARG ) & & arg_count ( cmd , corelog_ARG ) ) {
log_error ( " --mirrorlog and --corelog are incompatible " ) ;
2009-04-23 20:56:21 +04:00
return 0 ;
}
2007-08-02 00:54:28 +04:00
/*
2010-03-27 01:15:43 +03:00
* Adjusting mimage count ?
2007-08-02 00:54:28 +04:00
*/
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
if ( ! arg_count ( cmd , mirrors_ARG ) & & ! arg_count ( cmd , splitmirrors_ARG ) )
2010-03-27 01:15:43 +03:00
lp - > mirrors = * old_mimage_count ;
2007-08-02 00:54:28 +04:00
else if ( lp - > mirrors_sign = = SIGN_PLUS )
2010-03-27 01:15:43 +03:00
lp - > mirrors = * old_mimage_count + lp - > mirrors ;
2007-08-02 00:54:28 +04:00
else if ( lp - > mirrors_sign = = SIGN_MINUS )
2010-03-27 01:15:43 +03:00
lp - > mirrors = * old_mimage_count - lp - > mirrors ;
2007-08-02 00:54:28 +04:00
else
2005-11-29 21:20:23 +03:00
lp - > mirrors + = 1 ;
2010-03-27 01:15:43 +03:00
* new_mimage_count = lp - > mirrors ;
/* Too many mimages? */
2009-11-27 17:35:38 +03:00
if ( lp - > mirrors > DEFAULT_MIRROR_MAX_IMAGES ) {
log_error ( " Only up to %d images in mirror supported currently. " ,
DEFAULT_MIRROR_MAX_IMAGES ) ;
return 0 ;
}
2010-03-27 01:15:43 +03:00
/* Did the user try to subtract more legs than available? */
if ( lp - > mirrors < 1 ) {
log_error ( " Logical volume %s only has % " PRIu32 " mirrors. " ,
lv - > name , * old_mimage_count ) ;
return 0 ;
2010-01-07 23:42:55 +03:00
}
2010-03-27 01:15:43 +03:00
/*
* FIXME : It would be nice to say what we are adjusting to , but
* I really don ' t know whether to specify the # of copies or mimages .
*/
if ( * old_mimage_count ! = * new_mimage_count )
log_verbose ( " Adjusting mirror image count of %s " , lv - > name ) ;
2009-04-30 00:11:46 +04:00
2010-03-27 01:15:43 +03:00
/*
* Adjust log type
*
* If we are converting from a mirror to another mirror or simply
* changing the log type , we start by assuming they want the log
* type the same and then parse the given args . OTOH , If we are
* converting from linear to mirror , then we start from the default
* position that the user would like a ' disk ' log .
*/
* new_log_count = ( * old_mimage_count > 1 ) ? * old_log_count : 1 ;
if ( ! arg_count ( cmd , corelog_ARG ) & & ! arg_count ( cmd , mirrorlog_ARG ) )
return 1 ;
2010-01-09 01:32:35 +03:00
2010-03-27 01:15:43 +03:00
if ( arg_count ( cmd , corelog_ARG ) )
* new_log_count = 0 ;
2010-01-09 01:32:35 +03:00
2010-03-27 01:15:43 +03:00
mirrorlog = arg_str_value ( cmd , mirrorlog_ARG ,
! * new_log_count ? " core " : DEFAULT_MIRRORLOG ) ;
2007-08-02 01:01:06 +04:00
2010-03-27 01:15:43 +03:00
if ( ! strcmp ( " mirrored " , mirrorlog ) )
* new_log_count = 2 ;
else if ( ! strcmp ( " disk " , mirrorlog ) )
* new_log_count = 1 ;
else if ( ! strcmp ( " core " , mirrorlog ) )
* new_log_count = 0 ;
else {
log_error ( " Unknown mirrorlog type: %s " , mirrorlog ) ;
return 0 ;
2009-04-23 20:56:21 +04:00
}
2007-08-21 23:46:36 +04:00
2010-03-27 01:15:43 +03:00
log_verbose ( " Setting logging type to %s " , mirrorlog ) ;
2007-08-02 01:01:06 +04:00
/*
* Region size must not change on existing mirrors
*/
2005-11-29 21:20:23 +03:00
if ( arg_count ( cmd , regionsize_ARG ) & & ( lv - > status & MIRRORED ) & &
2010-03-27 01:15:43 +03:00
( lp - > region_size ! = first_seg ( lv ) - > region_size ) ) {
2005-11-29 21:20:23 +03:00
log_error ( " Mirror log region size cannot be changed on "
" an existing mirror. " ) ;
return 0 ;
}
2005-06-06 21:12:08 +04:00
2009-04-23 20:56:21 +04:00
/*
2009-05-19 14:27:47 +04:00
* For the most part , we cannot handle multi - segment mirrors . Bail out
* early if we have encountered one .
2009-04-23 20:56:21 +04:00
*/
2009-05-19 14:27:47 +04:00
if ( ( lv - > status & MIRRORED ) & & dm_list_size ( & lv - > segments ) ! = 1 ) {
2009-04-23 20:56:21 +04:00
log_error ( " Logical volume %s has multiple "
" mirror segments. " , lv - > name ) ;
return 0 ;
}
2009-04-30 00:11:46 +04:00
2010-03-27 01:15:43 +03:00
return 1 ;
}
2009-06-04 16:01:15 +04:00
2010-05-24 19:32:20 +04:00
static int _reload_lv ( struct cmd_context * cmd , struct logical_volume * lv )
{
log_very_verbose ( " Updating logical volume \" %s \" on disk(s) " , lv - > name ) ;
if ( ! vg_write ( lv - > vg ) )
return_0 ;
if ( ! suspend_lv ( cmd , lv ) ) {
log_error ( " Failed to lock %s " , lv - > name ) ;
vg_revert ( lv - > vg ) ;
return 0 ;
}
if ( ! vg_commit ( lv - > vg ) ) {
if ( ! resume_lv ( cmd , lv ) )
stack ;
return_0 ;
}
log_very_verbose ( " Updating \" %s \" in kernel " , lv - > name ) ;
if ( ! resume_lv ( cmd , lv ) ) {
log_error ( " Problem reactivating %s " , lv - > name ) ;
return 0 ;
}
return 1 ;
}
2010-03-27 01:15:43 +03:00
/*
* _lvconvert_mirrors_aux
*
* Add / remove mirror images and adjust log type . ' operable_pvs '
* are the set of PVs open to removal or allocation - depending
* on the operation being performed .
*/
static int _lvconvert_mirrors_aux ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp ,
struct dm_list * operable_pvs ,
uint32_t new_mimage_count ,
2010-04-14 17:51:58 +04:00
uint32_t new_log_count )
2010-03-27 01:15:43 +03:00
{
uint32_t region_size ;
struct lv_segment * seg ;
struct logical_volume * layer_lv ;
uint32_t old_mimage_count = lv_mirror_count ( lv ) ;
uint32_t old_log_count = _get_log_count ( lv ) ;
if ( ( lp - > mirrors = = 1 ) & & ! ( lv - > status & MIRRORED ) ) {
log_error ( " Logical volume %s is already not mirrored. " ,
lv - > name ) ;
return 1 ;
2007-08-02 00:54:28 +04:00
}
2010-03-27 01:15:43 +03:00
region_size = adjusted_mirror_region_size ( lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ;
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
2010-03-27 01:15:43 +03:00
if ( ! operable_pvs )
operable_pvs = lp - > pvh ;
This patch adds the capability to split off a mirror legs.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.
The following command line interface is enforced:
prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of images to split off, and
where 'name' is the name of the newly split off logical volume.
If more than one leg is split off, a new mirror will be the
result. The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_1] /dev/sdc1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mimage_3] /dev/sde1(0)
[lv_mlog] /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
LV Copy% Devices
lv 100.00 lv_mimage_0(0),lv_mimage_2(0)
[lv_mimage_0] /dev/sdb1(0)
[lv_mimage_2] /dev/sdd1(0)
[lv_mlog] /dev/sdi1(0)
split 100.00 split_mimage_0(0),split_mimage_1(0)
[split_mimage_0] /dev/sde1(0)
[split_mimage_1] /dev/sdc1(0)
It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
2010-01-09 01:00:31 +03:00
2010-03-27 01:15:43 +03:00
seg = first_seg ( lv ) ;
2009-04-30 00:11:46 +04:00
2010-03-27 01:15:43 +03:00
/*
* Up - convert from linear to mirror
*/
if ( ! ( lv - > status & MIRRORED ) ) {
2007-08-02 00:54:28 +04:00
/* FIXME Share code with lvcreate */
2009-04-23 20:56:21 +04:00
/*
* FIXME should we give not only lp - > pvh , but also all PVs
* currently taken by the mirror ? Would make more sense from
* user perspective .
*/
2010-04-13 05:54:32 +04:00
if ( ! lv_add_mirrors ( cmd , lv , new_mimage_count - 1 , lp - > stripes ,
lp - > stripe_size , region_size , new_log_count , operable_pvs ,
2010-03-27 01:15:43 +03:00
lp - > alloc , MIRROR_BY_LV ) ) {
2010-01-08 16:04:10 +03:00
stack ;
2010-04-14 17:51:58 +04:00
return 0 ;
2010-01-08 16:04:10 +03:00
}
2008-01-15 00:11:47 +03:00
if ( lp - > wait_completion )
lp - > need_polling = 1 ;
2010-03-27 01:15:43 +03:00
goto out ;
}
/*
* Up - convert m - way mirror to n - way mirror
*/
if ( new_mimage_count > old_mimage_count ) {
2007-12-22 05:13:00 +03:00
if ( lv - > status & MIRROR_NOTSYNCED ) {
2009-06-15 17:43:15 +04:00
log_error ( " Can't add mirror to out-of-sync mirrored "
" LV: use lvchange --resync first. " ) ;
2007-12-22 05:13:00 +03:00
return 0 ;
}
2009-10-26 13:01:56 +03:00
/*
* We allow snapshots of mirrors , but for now , we
* do not allow up converting mirrors that are under
* snapshots . The layering logic is somewhat complex ,
* and preliminary test show that the conversion can ' t
* seem to get the correct % ' age of completion .
*/
if ( lv_is_origin ( lv ) ) {
log_error ( " Can't add additional mirror images to "
" mirrors that are under snapshots " ) ;
2010-04-14 17:51:58 +04:00
return 0 ;
2009-10-26 13:01:56 +03:00
}
2010-04-21 18:04:24 +04:00
/*
* Is there already a convert in progress ? We do not
* currently allow more than one .
*/
if ( find_temporary_mirror ( lv ) | | ( lv - > status & CONVERTING ) ) {
log_error ( " %s is already being converted. Unable to start another conversion. " ,
lv - > name ) ;
return 0 ;
}
2010-05-24 19:32:20 +04:00
/*
* Is there already a convert in progress ? We do not
* currently allow more than one .
*/
if ( find_temporary_mirror ( lv ) | | ( lv - > status & CONVERTING ) ) {
log_error ( " %s is already being converted. Unable to start another conversion. " ,
lv - > name ) ;
return 0 ;
}
2008-01-10 21:35:51 +03:00
/*
* Log addition / removal should be done before the layer
* insertion to make the end result consistent with
* linear - to - mirror conversion .
*/
2010-03-27 01:15:43 +03:00
if ( ! _lv_update_log_type ( cmd , lp , lv ,
operable_pvs , new_log_count ) ) {
2010-01-08 16:04:10 +03:00
stack ;
2010-04-14 17:51:58 +04:00
return 0 ;
2010-01-08 16:04:10 +03:00
}
2010-03-27 01:15:43 +03:00
2008-01-10 21:35:51 +03:00
/* Insert a temporary layer for syncing,
* only if the original lv is using disk log . */
if ( seg - > log_lv & & ! _insert_lvconvert_layer ( cmd , lv ) ) {
2007-12-20 21:55:46 +03:00
log_error ( " Failed to insert resync layer " ) ;
return 0 ;
}
2010-03-27 01:15:43 +03:00
2008-01-10 21:35:51 +03:00
/* FIXME: can't have multiple mlogs. force corelog. */
2010-03-27 01:15:43 +03:00
if ( ! lv_add_mirrors ( cmd , lv ,
2010-04-13 05:54:32 +04:00
new_mimage_count - old_mimage_count , lp - > stripes , lp - > stripe_size ,
2010-03-27 01:15:43 +03:00
region_size , 0U , operable_pvs , lp - > alloc ,
2009-10-23 05:24:17 +04:00
MIRROR_BY_LV ) ) {
layer_lv = seg_lv ( first_seg ( lv ) , 0 ) ;
if ( ! remove_layer_from_lv ( lv , layer_lv ) | |
! deactivate_lv ( cmd , layer_lv ) | |
! lv_remove ( layer_lv ) | | ! vg_write ( lv - > vg ) | |
! vg_commit ( lv - > vg ) ) {
log_error ( " ABORTING: Failed to remove "
" temporary mirror layer %s. " ,
layer_lv - > name ) ;
log_error ( " Manual cleanup with vgcfgrestore "
" and dmsetup may be required. " ) ;
return 0 ;
}
2010-01-08 16:04:10 +03:00
stack ;
2010-04-14 17:51:58 +04:00
return 0 ;
2009-10-23 05:24:17 +04:00
}
2010-02-06 00:49:16 +03:00
if ( seg - > log_lv )
lv - > status | = CONVERTING ;
2008-01-15 00:11:47 +03:00
lp - > need_polling = 1 ;
2010-03-27 01:15:43 +03:00
goto out_skip_log_convert ;
2005-06-06 21:12:08 +04:00
}
2010-03-27 01:15:43 +03:00
/*
* Down - convert ( reduce # of mimages ) .
*/
if ( new_mimage_count < old_mimage_count ) {
uint32_t nmc = old_mimage_count - new_mimage_count ;
uint32_t nlc = ( ! new_log_count | | lp - > mirrors = = 1 ) ? 1U : 0U ;
2010-04-20 16:18:31 +04:00
/* FIXME: Why did nlc used to be calculated that way? */
2010-03-27 01:15:43 +03:00
/* Reduce number of mirrors */
if ( lp - > keep_mimages ) {
if ( ! lv_split_mirror_images ( lv , lp - > lv_split_name ,
nmc , operable_pvs ) )
return 0 ;
} else if ( ! lv_remove_mirrors ( cmd , lv , nmc , nlc ,
2010-05-24 19:32:20 +04:00
is_mirror_image_removable , operable_pvs , 0 ) )
2010-03-27 01:15:43 +03:00
return_0 ;
goto out ; /* Just in case someone puts code between */
}
out :
/*
* Converting the log type
*/
2010-04-28 21:41:30 +04:00
if ( ( lv - > status & MIRRORED ) & & ( old_log_count ! = new_log_count ) ) {
2010-03-27 01:15:43 +03:00
if ( ! _lv_update_log_type ( cmd , lp , lv ,
operable_pvs , new_log_count ) ) {
stack ;
2010-04-14 17:51:58 +04:00
return 0 ;
2009-04-23 20:56:21 +04:00
}
}
2010-03-27 01:15:43 +03:00
out_skip_log_convert :
2010-05-24 19:32:20 +04:00
if ( ! _reload_lv ( cmd , lv ) )
return 0 ;
2005-06-06 21:12:08 +04:00
2010-03-27 01:15:43 +03:00
return 1 ;
}
/*
* _lvconvert_mirrors_repair
*
* This function operates in two phases . First , all of the bad
* devices are removed from the mirror . Then , if desired by the
* user , the devices are replaced .
*
* ' old_mimage_count ' and ' old_log_count ' are there so we know
* what to convert to after the removal of devices .
*/
static int _lvconvert_mirrors_repair ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp ,
uint32_t old_mimage_count ,
uint32_t old_log_count )
{
int failed_log = 0 ;
int failed_mirrors = 0 ;
int replace_log = 0 ;
int replace_mirrors = 0 ;
2010-04-14 17:51:58 +04:00
uint32_t new_log_count , log_count ;
2010-03-27 01:15:43 +03:00
struct logical_volume * log_lv ;
cmd - > handles_missing_pvs = 1 ;
cmd - > partial_activation = 1 ;
lp - > need_polling = 0 ;
2010-05-24 19:32:20 +04:00
lv_check_transient ( lv ) ; /* TODO check this in lib for all commands? */
2010-03-27 01:15:43 +03:00
if ( ! ( lv - > status & PARTIAL_LV ) ) {
log_error ( " %s is consistent. Nothing to repair. " , lv - > name ) ;
return 1 ;
}
/*
* Count the failed mimages - negative if ' lv ' is not a mirror
*/
if ( ( failed_mirrors = _failed_mirrors_count ( lv ) ) < 0 )
return_0 ;
lp - > mirrors = old_mimage_count - failed_mirrors ;
if ( lp - > mirrors ! = old_mimage_count )
log_error ( " Mirror status: %d of %d images failed. " ,
failed_mirrors , old_mimage_count ) ;
/*
* Count the failed log devices
*/
new_log_count = old_log_count ;
log_lv = first_seg ( lv ) - > log_lv ;
if ( log_lv ) {
new_log_count = lv_mirror_count ( log_lv ) ;
if ( log_lv - > status & PARTIAL_LV ) {
failed_log = 1 ;
if ( log_lv - > status & MIRRORED )
new_log_count - = _failed_mirrors_count ( log_lv ) ;
else
new_log_count = 0 ;
2010-01-09 01:32:35 +03:00
}
2010-03-27 01:15:43 +03:00
}
if ( old_log_count ! = new_log_count )
log_error ( " Mirror log status: %d of %d images failed%s " ,
old_log_count - new_log_count , old_log_count ,
( ! new_log_count ) ? " - switching to core " : " " ) ;
/*
* Find out our policies
*/
_lvconvert_mirrors_repair_ask ( cmd , failed_log , failed_mirrors ,
& replace_log , & replace_mirrors ) ;
/*
* First phase - remove faulty devices
*/
2010-07-01 14:10:52 +04:00
if ( ! ( lp - > failed_pvs = _failed_pv_list ( lv - > vg ) ) )
2010-03-27 01:15:43 +03:00
return_0 ;
/*
* We must adjust the log first , or the entire mirror
* will get stuck during a suspend .
*/
2010-07-01 14:10:52 +04:00
if ( ! _lv_update_mirrored_log ( lv , lp - > failed_pvs , new_log_count ) )
2010-03-27 01:15:43 +03:00
return 0 ;
2010-06-24 00:32:29 +04:00
if ( lp - > mirrors = = 1 )
new_log_count = 0 ;
2010-05-24 19:32:20 +04:00
if ( failed_mirrors ) {
2010-06-24 00:32:29 +04:00
if ( ! lv_remove_mirrors ( cmd , lv , failed_mirrors ,
new_log_count ? 0U : 1U ,
2010-05-24 19:32:20 +04:00
_is_partial_lv , NULL , 0 ) )
return 0 ;
}
2010-07-01 14:10:52 +04:00
if ( ! _lv_update_log_type ( cmd , lp , lv , lp - > failed_pvs ,
2010-06-24 00:32:29 +04:00
new_log_count ) )
return 0 ;
2010-05-24 19:32:20 +04:00
if ( ! _reload_lv ( cmd , lv ) )
2010-03-27 01:15:43 +03:00
return 0 ;
/*
* Second phase - replace faulty devices
*/
2010-04-14 17:51:58 +04:00
if ( replace_mirrors )
2010-03-27 01:15:43 +03:00
lp - > mirrors = old_mimage_count ;
2010-05-24 19:32:20 +04:00
/*
* It does not make sense to replace the log if the volume is no longer
* a mirror .
*/
if ( ! replace_mirrors & & lp - > mirrors = = 1 )
replace_log = 0 ;
2010-04-14 17:51:58 +04:00
log_count = replace_log ? old_log_count : new_log_count ;
while ( replace_mirrors | | replace_log ) {
log_warn ( " Trying to up-convert to %d images, %d logs. " , lp - > mirrors , log_count ) ;
if ( _lvconvert_mirrors_aux ( cmd , lv , lp , NULL ,
lp - > mirrors , log_count ) )
break ;
else {
if ( lp - > mirrors > 2 )
- - lp - > mirrors ;
else if ( log_count > 0 )
- - log_count ;
else
break ; /* nowhere to go, anymore... */
}
2009-04-23 20:56:21 +04:00
}
2010-05-24 19:32:20 +04:00
if ( replace_mirrors & & lp - > mirrors ! = old_mimage_count )
2010-04-14 17:51:58 +04:00
log_warn ( " WARNING: Failed to replace %d of %d images in volume %s " ,
old_mimage_count - lp - > mirrors , old_mimage_count , lv - > name ) ;
2010-05-24 19:32:20 +04:00
if ( replace_log & & log_count ! = old_log_count )
2010-04-14 17:51:58 +04:00
log_warn ( " WARNING: Failed to replace %d of %d logs in volume %s " ,
old_log_count - log_count , old_log_count , lv - > name ) ;
/* if (!arg_count(cmd, use_policies_ARG) && (lp->mirrors != old_mimage_count
| | log_count ! = old_log_count ) )
return 0 ; */
2010-03-27 01:15:43 +03:00
return 1 ;
}
/*
* _lvconvert_mirrors
*
* Determine what is being done . Are we doing a conversion , repair , or
* collapsing a stack ? Once determined , call helper functions .
*/
static int _lvconvert_mirrors ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp )
{
int repair = arg_count ( cmd , repair_ARG ) ;
uint32_t old_mimage_count ;
uint32_t old_log_count ;
uint32_t new_mimage_count ;
uint32_t new_log_count ;
/* Adjust mimage and/or log count */
if ( ! _lvconvert_mirrors_parse_params ( cmd , lv , lp ,
& old_mimage_count , & old_log_count ,
& new_mimage_count , & new_log_count ) )
return 0 ;
/* Nothing to do? (Probably finishing collapse.) */
if ( ( old_mimage_count = = new_mimage_count ) & &
( old_log_count = = new_log_count ) & & ! repair )
return 1 ;
if ( repair )
return _lvconvert_mirrors_repair ( cmd , lv , lp ,
old_mimage_count ,
old_log_count ) ;
if ( ! _lvconvert_mirrors_aux ( cmd , lv , lp , NULL ,
2010-04-14 17:51:58 +04:00
new_mimage_count , new_log_count ) )
2010-03-27 01:15:43 +03:00
return 0 ;
2008-01-15 00:11:47 +03:00
if ( ! lp - > need_polling )
2007-12-22 15:13:29 +03:00
log_print ( " Logical volume %s converted. " , lv - > name ) ;
2005-06-06 21:12:08 +04:00
2009-04-21 18:31:57 +04:00
backup ( lv - > vg ) ;
2010-03-27 01:15:43 +03:00
return 1 ;
2005-06-06 21:12:08 +04:00
}
2006-04-06 00:43:23 +04:00
static int lvconvert_snapshot ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp )
{
struct logical_volume * org ;
2009-04-21 18:31:57 +04:00
int r = 0 ;
2006-04-06 00:43:23 +04:00
if ( ! ( org = find_lv ( lv - > vg , lp - > origin ) ) ) {
log_error ( " Couldn't find origin volume '%s'. " , lp - > origin ) ;
return 0 ;
}
2007-11-07 19:33:12 +03:00
if ( org = = lv ) {
log_error ( " Unable to use \" %s \" as both snapshot and origin. " ,
lv - > name ) ;
return 0 ;
}
2008-06-27 01:38:58 +04:00
if ( org - > status & ( LOCKED | PVMOVE | MIRRORED ) | | lv_is_cow ( org ) ) {
2006-04-06 00:43:23 +04:00
log_error ( " Unable to create a snapshot of a %s LV. " ,
org - > status & LOCKED ? " locked " :
2008-06-27 01:38:58 +04:00
org - > status & PVMOVE ? " pvmove " :
org - > status & MIRRORED ? " mirrored " :
" snapshot " ) ;
2006-04-06 00:43:23 +04:00
return 0 ;
}
2007-01-10 17:13:46 +03:00
if ( ! lp - > zero | | ! ( lv - > status & LVM_WRITE ) )
2007-06-28 21:33:44 +04:00
log_warn ( " WARNING: \" %s \" not zeroed " , lv - > name ) ;
2007-08-22 18:38:18 +04:00
else if ( ! set_lv ( cmd , lv , UINT64_C ( 0 ) , 0 ) ) {
2007-08-02 00:54:28 +04:00
log_error ( " Aborting. Failed to wipe snapshot "
" exception store. " ) ;
return 0 ;
2006-04-06 00:43:23 +04:00
}
if ( ! deactivate_lv ( cmd , lv ) ) {
log_error ( " Couldn't deactivate LV %s. " , lv - > name ) ;
return 0 ;
}
2009-05-14 01:21:58 +04:00
if ( ! vg_add_snapshot ( org , lv , NULL , org - > le_count , lp - > chunk_size ) ) {
2006-04-06 00:43:23 +04:00
log_error ( " Couldn't create snapshot. " ) ;
return 0 ;
}
/* store vg on disk(s) */
if ( ! vg_write ( lv - > vg ) )
return_0 ;
if ( ! suspend_lv ( cmd , org ) ) {
log_error ( " Failed to suspend origin %s " , org - > name ) ;
vg_revert ( lv - > vg ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2006-04-06 00:43:23 +04:00
}
if ( ! vg_commit ( lv - > vg ) )
2009-04-21 18:31:57 +04:00
goto_out ;
2006-04-06 00:43:23 +04:00
if ( ! resume_lv ( cmd , org ) ) {
log_error ( " Problem reactivating origin %s " , org - > name ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2006-04-06 00:43:23 +04:00
}
log_print ( " Logical volume %s converted to snapshot. " , lv - > name ) ;
2009-04-21 18:31:57 +04:00
r = 1 ;
out :
backup ( lv - > vg ) ;
return r ;
2006-04-06 00:43:23 +04:00
}
2010-01-13 04:45:15 +03:00
static int lvconvert_merge ( struct cmd_context * cmd ,
struct logical_volume * lv ,
struct lvconvert_params * lp )
{
int r = 0 ;
2010-01-13 04:54:34 +03:00
int merge_on_activate = 0 ;
2010-01-13 04:45:15 +03:00
struct logical_volume * origin = origin_from_cow ( lv ) ;
struct lv_segment * cow_seg = find_cow ( lv ) ;
2010-01-13 04:47:18 +03:00
struct lvinfo info ;
2010-01-13 04:45:15 +03:00
/* Check if merge is possible */
2010-01-13 04:55:43 +03:00
if ( lv_is_merging_cow ( lv ) ) {
2010-01-13 04:45:15 +03:00
log_error ( " Snapshot %s is already merging " , lv - > name ) ;
return 0 ;
}
2010-01-13 04:55:43 +03:00
if ( lv_is_merging_origin ( origin ) ) {
2010-01-13 04:45:15 +03:00
log_error ( " Snapshot %s is already merging into the origin " ,
2010-01-13 04:55:43 +03:00
find_merging_cow ( origin ) - > cow - > name ) ;
2010-01-13 04:45:15 +03:00
return 0 ;
}
2010-01-13 04:47:18 +03:00
/*
* Prevent merge with open device ( s ) as it would likely lead
2010-01-13 04:54:34 +03:00
* to application / filesystem failure . Merge on origin ' s next
* activation if either the origin or snapshot LV are currently
* open .
2010-01-13 04:47:18 +03:00
*
* FIXME testing open_count is racey ; snapshot - merge target ' s
* constructor and DM should prevent appropriate devices from
* being open .
*/
if ( lv_info ( cmd , origin , & info , 1 , 0 ) ) {
if ( info . open_count ) {
log_error ( " Can't merge over open origin volume " ) ;
2010-01-13 04:54:34 +03:00
merge_on_activate = 1 ;
2010-01-13 04:47:18 +03:00
}
}
if ( lv_info ( cmd , lv , & info , 1 , 0 ) ) {
if ( info . open_count ) {
2010-01-13 04:54:34 +03:00
log_print ( " Can't merge when snapshot is open " ) ;
merge_on_activate = 1 ;
2010-01-13 04:47:18 +03:00
}
}
2010-01-13 04:45:15 +03:00
init_snapshot_merge ( cow_seg , origin ) ;
/* store vg on disk(s) */
if ( ! vg_write ( lv - > vg ) )
return_0 ;
2010-01-13 04:54:34 +03:00
if ( merge_on_activate ) {
/* commit vg but skip starting the merge */
if ( ! vg_commit ( lv - > vg ) )
return_0 ;
r = 1 ;
log_print ( " Merging of snapshot %s will start "
" next activation. " , lv - > name ) ;
goto out ;
}
2010-01-13 04:45:15 +03:00
/* Perform merge */
if ( ! suspend_lv ( cmd , origin ) ) {
log_error ( " Failed to suspend origin %s " , origin - > name ) ;
vg_revert ( lv - > vg ) ;
goto out ;
}
if ( ! vg_commit ( lv - > vg ) ) {
if ( ! resume_lv ( cmd , origin ) )
stack ;
goto_out ;
}
if ( ! resume_lv ( cmd , origin ) ) {
log_error ( " Failed to reactivate origin %s " , origin - > name ) ;
goto out ;
}
2010-01-13 04:49:52 +03:00
lp - > need_polling = 1 ;
lp - > lv_to_poll = origin ;
2010-01-13 04:45:15 +03:00
r = 1 ;
log_print ( " Merging of volume %s started. " , lv - > name ) ;
out :
backup ( lv - > vg ) ;
return r ;
}
2010-02-06 01:44:37 +03:00
static int _lvconvert_single ( struct cmd_context * cmd , struct logical_volume * lv ,
void * handle )
2005-06-06 21:12:08 +04:00
{
struct lvconvert_params * lp = handle ;
if ( lv - > status & LOCKED ) {
log_error ( " Cannot convert locked LV %s " , lv - > name ) ;
return ECMD_FAILED ;
}
2010-01-13 04:45:15 +03:00
if ( lv_is_cow ( lv ) & & ! lp - > merge ) {
2005-06-06 21:12:08 +04:00
log_error ( " Can't convert snapshot logical volume \" %s \" " ,
lv - > name ) ;
return ECMD_FAILED ;
}
if ( lv - > status & PVMOVE ) {
log_error ( " Unable to convert pvmove LV %s " , lv - > name ) ;
return ECMD_FAILED ;
}
2009-06-10 19:27:57 +04:00
if ( arg_count ( cmd , repair_ARG ) & & ! ( lv - > status & MIRRORED ) ) {
2010-02-06 10:44:16 +03:00
if ( arg_count ( cmd , use_policies_ARG ) )
return ECMD_PROCESSED ; /* nothing to be done here */
2009-06-10 19:27:57 +04:00
log_error ( " Can't repair non-mirrored LV \" %s \" . " , lv - > name ) ;
return ECMD_FAILED ;
}
2010-01-13 04:45:15 +03:00
if ( lp - > merge ) {
if ( ! lv_is_cow ( lv ) ) {
log_error ( " Logical volume \" %s \" is not a snapshot " ,
lv - > name ) ;
return ECMD_FAILED ;
}
if ( ! archive ( lv - > vg ) ) {
stack ;
return ECMD_FAILED ;
}
if ( ! lvconvert_merge ( cmd , lv , lp ) ) {
stack ;
return ECMD_FAILED ;
}
} else if ( lp - > snapshot ) {
2008-06-27 01:38:58 +04:00
if ( lv - > status & MIRRORED ) {
log_error ( " Unable to convert mirrored LV \" %s \" into a snapshot. " , lv - > name ) ;
return ECMD_FAILED ;
}
2009-09-15 02:47:49 +04:00
if ( ! archive ( lv - > vg ) ) {
stack ;
2005-06-06 21:12:08 +04:00
return ECMD_FAILED ;
2009-09-15 02:47:49 +04:00
}
if ( ! lvconvert_snapshot ( cmd , lv , lp ) ) {
stack ;
2005-06-06 21:12:08 +04:00
return ECMD_FAILED ;
2009-09-15 02:47:49 +04:00
}
2008-06-27 01:38:58 +04:00
} else if ( arg_count ( cmd , mirrors_ARG ) | | ( lv - > status & MIRRORED ) ) {
2009-09-15 02:47:49 +04:00
if ( ! archive ( lv - > vg ) ) {
stack ;
2006-04-06 00:43:23 +04:00
return ECMD_FAILED ;
2009-09-15 02:47:49 +04:00
}
if ( ! _lvconvert_mirrors ( cmd , lv , lp ) ) {
stack ;
2006-04-06 00:43:23 +04:00
return ECMD_FAILED ;
2009-09-15 02:47:49 +04:00
}
2010-01-08 16:04:10 +03:00
/* If repairing and using policies, remove missing PVs from VG */
if ( arg_count ( cmd , repair_ARG ) & & arg_count ( cmd , use_policies_ARG ) )
_remove_missing_empty_pv ( lv - > vg , lp - > failed_pvs ) ;
2005-06-06 21:12:08 +04:00
}
return ECMD_PROCESSED ;
}
2010-02-06 01:44:37 +03:00
/*
* FIXME move to toollib along with the rest of the drop / reacquire
* VG locking that is used by lvconvert_merge_single ( )
*/
static struct logical_volume * get_vg_lock_and_logical_volume ( struct cmd_context * cmd ,
const char * vg_name ,
const char * lv_name )
2005-06-06 21:12:08 +04:00
{
2010-02-06 01:44:37 +03:00
/*
* Returns NULL if the requested LV doesn ' t exist ;
* otherwise the caller must vg_release ( lv - > vg )
* - it is also up to the caller to unlock_vg ( ) as needed
*/
2005-06-06 21:12:08 +04:00
struct volume_group * vg ;
2010-02-06 01:44:37 +03:00
struct logical_volume * lv = NULL ;
vg = _get_lvconvert_vg ( cmd , vg_name , NULL ) ;
if ( vg_read_error ( vg ) ) {
vg_release ( vg ) ;
2010-04-26 22:31:58 +04:00
return_NULL ;
2010-02-06 01:44:37 +03:00
}
if ( ! ( lv = _get_lvconvert_lv ( cmd , vg , lv_name , NULL , 0 ) ) ) {
2010-04-26 22:31:58 +04:00
log_error ( " Can't find LV %s in VG %s " , lv_name , vg_name ) ;
2010-02-06 01:44:37 +03:00
unlock_and_release_vg ( cmd , vg , vg_name ) ;
return NULL ;
}
return lv ;
}
static int poll_logical_volume ( struct cmd_context * cmd , struct logical_volume * lv ,
int wait_completion )
{
2008-01-10 21:35:51 +03:00
struct lvinfo info ;
2005-06-06 21:12:08 +04:00
2010-02-06 01:44:37 +03:00
if ( ! lv_info ( cmd , lv , & info , 1 , 0 ) | | ! info . exists ) {
log_print ( " Conversion starts after activation. " ) ;
return ECMD_PROCESSED ;
2005-06-06 21:12:08 +04:00
}
2010-02-06 01:44:37 +03:00
return lvconvert_poll ( cmd , lv , wait_completion ? 0 : 1U ) ;
}
static int lvconvert_single ( struct cmd_context * cmd , struct lvconvert_params * lp )
{
2010-02-06 01:47:22 +03:00
struct logical_volume * lv = NULL ;
2010-02-06 01:44:37 +03:00
int ret = ECMD_FAILED ;
int saved_ignore_suspended_devices = ignore_suspended_devices ( ) ;
2005-06-06 21:12:08 +04:00
2009-07-15 09:47:55 +04:00
if ( arg_count ( cmd , repair_ARG ) ) {
2009-06-15 18:47:39 +04:00
init_ignore_suspended_devices ( 1 ) ;
2009-07-15 09:47:55 +04:00
cmd - > handles_missing_pvs = 1 ;
}
2009-06-15 18:47:39 +04:00
2010-02-06 01:47:22 +03:00
lv = get_vg_lock_and_logical_volume ( cmd , lp - > vg_name , lp - > lv_name ) ;
if ( ! lv )
goto_out ;
2005-06-06 21:12:08 +04:00
2010-04-26 22:12:40 +04:00
/*
* lp - > pvh holds the list of PVs available for allocation or removal
*/
2010-02-06 01:44:37 +03:00
if ( lp - > pv_count ) {
2010-02-06 01:47:22 +03:00
if ( ! ( lp - > pvh = create_pv_list ( cmd - > mem , lv - > vg , lp - > pv_count ,
2010-02-06 01:44:37 +03:00
lp - > pvs , 0 ) ) )
2008-01-30 16:19:47 +03:00
goto_bad ;
2005-06-06 21:12:08 +04:00
} else
2010-02-06 01:47:22 +03:00
lp - > pvh = & lv - > vg - > pvs ;
2005-06-06 21:12:08 +04:00
2010-02-06 01:47:22 +03:00
lp - > lv_to_poll = lv ;
ret = _lvconvert_single ( cmd , lv , lp ) ;
2008-01-30 16:19:47 +03:00
bad :
2010-02-06 01:44:37 +03:00
unlock_vg ( cmd , lp - > vg_name ) ;
2007-12-22 15:13:29 +03:00
2010-02-06 01:44:37 +03:00
if ( ret = = ECMD_PROCESSED & & lp - > need_polling )
ret = poll_logical_volume ( cmd , lp - > lv_to_poll ,
lp - > wait_completion ) ;
2010-02-06 01:47:22 +03:00
vg_release ( lv - > vg ) ;
2009-04-10 14:01:38 +04:00
out :
2009-06-15 18:47:39 +04:00
init_ignore_suspended_devices ( saved_ignore_suspended_devices ) ;
2005-06-06 21:12:08 +04:00
return ret ;
}
2010-02-06 01:44:37 +03:00
static int lvconvert_merge_single ( struct cmd_context * cmd , struct logical_volume * lv ,
void * handle )
{
struct lvconvert_params * lp = handle ;
const char * vg_name = NULL ;
struct logical_volume * refreshed_lv = NULL ;
int ret ;
/*
* FIXME can ' t trust lv ' s VG to be current given that caller
* is process_each_lv ( ) - - poll_logical_volume ( ) may have
* already updated the VG ' s metadata in an earlier iteration .
* - preemptively drop the VG lock , as is needed for
* poll_logical_volume ( ) , refresh LV ( and VG in the process ) .
*/
vg_name = lv - > vg - > name ;
unlock_vg ( cmd , vg_name ) ;
refreshed_lv = get_vg_lock_and_logical_volume ( cmd , vg_name , lv - > name ) ;
2010-04-26 22:31:58 +04:00
if ( ! refreshed_lv ) {
log_error ( " ABORTING: Can't reread LV %s/%s " , vg_name , lv - > name ) ;
2010-02-06 01:44:37 +03:00
return ECMD_FAILED ;
2010-04-26 22:31:58 +04:00
}
2010-02-06 01:44:37 +03:00
lp - > lv_to_poll = refreshed_lv ;
ret = _lvconvert_single ( cmd , refreshed_lv , lp ) ;
if ( ret = = ECMD_PROCESSED & & lp - > need_polling ) {
/*
* Must drop VG lock , because lvconvert_poll ( ) needs it ,
* then reacquire it after polling completes
*/
unlock_vg ( cmd , vg_name ) ;
ret = poll_logical_volume ( cmd , lp - > lv_to_poll ,
lp - > wait_completion ) ;
/* use LCK_VG_WRITE to match lvconvert()'s READ_FOR_UPDATE */
if ( ! lock_vol ( cmd , vg_name , LCK_VG_WRITE ) ) {
log_error ( " ABORTING: Can't relock VG for %s "
" after polling finished " , vg_name ) ;
ret = ECMD_FAILED ;
}
}
vg_release ( refreshed_lv - > vg ) ;
return ret ;
}
int lvconvert ( struct cmd_context * cmd , int argc , char * * argv )
{
struct lvconvert_params lp ;
if ( ! _read_params ( & lp , cmd , argc , argv ) ) {
stack ;
return EINVALID_CMD_LINE ;
}
if ( lp . merge )
return process_each_lv ( cmd , argc , argv , READ_FOR_UPDATE , & lp ,
& lvconvert_merge_single ) ;
return lvconvert_single ( cmd , & lp ) ;
}