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"
2005-06-06 21:12:08 +04:00
struct lvconvert_params {
2006-04-06 00:43:23 +04:00
int snapshot ;
int zero ;
const char * origin ;
2005-08-15 18:10:28 +04:00
const char * lv_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 ;
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 ;
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
if ( lp - > snapshot ) {
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 ;
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 ) ) ;
2007-08-21 23:46:36 +04:00
if ( arg_count ( cmd , snapshot_ARG ) & &
( arg_count ( cmd , mirrorlog_ARG ) | | arg_count ( cmd , mirrors_ARG ) ) ) {
2007-08-02 01:01:06 +04:00
log_error ( " --snapshots argument cannot be mixed "
" with --mirrors 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 ;
if ( arg_count ( cmd , mirrors_ARG ) ) {
lp - > mirrors = arg_uint_value ( cmd , mirrors_ARG , 0 ) ;
lp - > mirrors_sign = arg_sign_value ( cmd , mirrors_ARG , 0 ) ;
}
2005-08-15 18:10:28 +04:00
lp - > alloc = ALLOC_INHERIT ;
if ( arg_count ( cmd , alloc_ARG ) )
2006-05-10 01:23:51 +04:00
lp - > alloc = arg_uint_value ( cmd , alloc_ARG , lp - > alloc ) ;
2005-08-15 18:10:28 +04:00
2006-04-06 00:43:23 +04:00
if ( lp - > snapshot ) {
if ( arg_count ( cmd , regionsize_ARG ) ) {
log_error ( " --regionsize is only available with mirrors " ) ;
return 0 ;
}
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 ;
}
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 ;
return 1 ;
}
2007-12-22 15:13:29 +03:00
static struct volume_group * _get_lvconvert_vg ( struct cmd_context * cmd ,
const char * lv_name )
{
2008-01-30 17:00:02 +03:00
dev_close_all ( ) ;
2007-12-22 15:13:29 +03:00
return vg_lock_and_read ( cmd , extract_vgname ( cmd , lv_name ) ,
NULL , LCK_VG_WRITE ,
CLUSTERED | EXPORTED_VG | LVM_WRITE ,
CORRECT_INCONSISTENT | FAIL_INCONSISTENT ) ;
}
static struct logical_volume * _get_lvconvert_lv ( struct cmd_context * cmd __attribute ( ( unused ) ) ,
struct volume_group * vg ,
const char * name ,
uint32_t lv_type __attribute ( ( unused ) ) )
{
return find_lv ( vg , name ) ;
}
static int _update_lvconvert_mirror ( struct cmd_context * cmd __attribute ( ( unused ) ) ,
struct volume_group * vg __attribute ( ( unused ) ) ,
struct logical_volume * lv __attribute ( ( unused ) ) ,
2008-11-04 01:14:30 +03:00
struct dm_list * lvs_changed __attribute ( ( unused ) ) ,
2008-04-09 16:56:34 +04:00
unsigned flags __attribute ( ( unused ) ) )
2007-12-22 15:13:29 +03:00
{
/* lvconvert mirror doesn't require periodical metadata update */
return 1 ;
}
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 ;
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
}
static struct poll_functions _lvconvert_mirror_fns = {
. get_copy_vg = _get_lvconvert_vg ,
. get_copy_lv = _get_lvconvert_lv ,
. update_metadata = _update_lvconvert_mirror ,
. finish_copy = _finish_lvconvert_mirror ,
} ;
2008-01-10 21:35:51 +03:00
int lvconvert_poll ( struct cmd_context * cmd , const char * lv_name ,
unsigned background )
2007-12-22 15:13:29 +03:00
{
return poll_daemon ( cmd , lv_name , background , 0 , & _lvconvert_mirror_fns ,
" Converted " ) ;
}
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 ;
} else if ( seg_type ( lvseg , s ) = = AREA_PV ) {
if ( seg_pv ( lvseg , s ) - > status & MISSING_PV )
return 1 ;
}
return 0 ;
}
/* FIXME we want to handle mirror stacks here... */
static int _count_failed_mirrors ( struct logical_volume * lv )
{
struct lv_segment * lvseg ;
int ret = 0 ;
int s ;
dm_list_iterate_items ( lvseg , & lv - > segments ) {
if ( ! seg_is_mirrored ( lvseg ) )
return - 1 ;
for ( s = 0 ; s < lvseg - > area_count ; + + s ) {
if ( _area_missing ( lvseg , s ) )
+ + ret ;
}
}
return ret ;
}
static struct dm_list * _failed_pv_list ( struct volume_group * vg )
{
struct dm_list * r ;
struct pv_list * pvl , * new_pvl ;
if ( ! ( r = dm_pool_alloc ( vg - > vgmem , sizeof ( * r ) ) ) ) {
log_error ( " Allocation of list failed " ) ;
return_0 ;
}
dm_list_init ( r ) ;
dm_list_iterate_items ( pvl , & vg - > pvs ) {
if ( ! ( pvl - > pv - > status & MISSING_PV ) )
continue ;
if ( ! ( new_pvl = dm_pool_alloc ( vg - > vgmem , sizeof ( * new_pvl ) ) ) ) {
log_error ( " Unable to allocate physical volume list. " ) ;
return_0 ;
}
new_pvl - > pv = pvl - > pv ;
dm_list_add ( r , & new_pvl - > list ) ;
}
return r ;
}
2008-01-16 22:16:48 +03:00
/* walk down the stacked mirror LV to the original mirror LV */
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 ;
}
2005-06-06 21:12:08 +04:00
static int lvconvert_mirrors ( struct cmd_context * cmd , struct logical_volume * lv ,
2005-08-15 18:10:28 +04:00
struct lvconvert_params * lp )
2005-06-06 21:12:08 +04:00
{
2005-10-28 16:48:50 +04:00
struct lv_segment * seg ;
2005-08-15 18:10:28 +04:00
uint32_t existing_mirrors ;
2007-08-21 23:46:36 +04:00
const char * mirrorlog ;
unsigned corelog = 0 ;
2008-01-16 22:16:48 +03:00
struct logical_volume * original_lv ;
2009-04-21 18:31:57 +04:00
int r = 0 ;
2009-04-23 20:56:21 +04:00
struct logical_volume * log_lv ;
int failed_mirrors = 0 , failed_log = 0 ;
struct dm_list * old_pvh , * remove_pvs = NULL ;
2005-11-29 21:20:23 +03:00
seg = first_seg ( lv ) ;
2007-12-21 04:08:18 +03:00
existing_mirrors = lv_mirror_count ( lv ) ;
/* 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 ) & &
! arg_count ( cmd , repair_ARG ) ) {
2008-01-15 00:11:47 +03:00
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
2009-04-23 20:56:21 +04:00
if ( arg_count ( cmd , mirrors_ARG ) & & arg_count ( cmd , repair_ARG ) ) {
log_error ( " You can only use one of -m, --repair. " ) ;
return 0 ;
}
2007-08-02 00:54:28 +04:00
/*
* Adjust required number of mirrors
*
* We check mirrors_ARG again to see if it
* was supplied . If not , they want the mirror
* count to remain the same . They may be changing
* the logging type .
*/
if ( ! arg_count ( cmd , mirrors_ARG ) )
lp - > mirrors = existing_mirrors ;
else if ( lp - > mirrors_sign = = SIGN_PLUS )
2005-11-29 21:20:23 +03:00
lp - > mirrors = existing_mirrors + lp - > mirrors ;
2007-08-02 00:54:28 +04:00
else if ( lp - > mirrors_sign = = SIGN_MINUS )
2005-11-29 21:20:23 +03:00
lp - > mirrors = existing_mirrors - lp - > mirrors ;
2007-08-02 00:54:28 +04:00
else
2005-11-29 21:20:23 +03:00
lp - > mirrors + = 1 ;
2009-04-23 20:56:21 +04:00
if ( arg_count ( cmd , repair_ARG ) ) {
cmd - > handles_missing_pvs = 1 ;
cmd - > partial_activation = 1 ;
lp - > need_polling = 0 ;
if ( ! ( lv - > status & PARTIAL_LV ) ) {
log_error ( " The mirror is consistent, nothing to repair. " ) ;
return 0 ;
}
if ( ( failed_mirrors = _count_failed_mirrors ( lv ) ) < 0 )
return_0 ;
lp - > mirrors - = failed_mirrors ;
log_error ( " Mirror status: %d/%d legs failed. " ,
failed_mirrors , existing_mirrors ) ;
old_pvh = lp - > pvh ;
if ( ! ( lp - > pvh = _failed_pv_list ( lv - > vg ) ) )
return_0 ;
log_lv = first_seg ( lv ) - > log_lv ;
if ( ! log_lv | | log_lv - > status & PARTIAL_LV )
failed_log = corelog = 1 ;
} else {
/*
* 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 , existing_mirrors ) ;
return 0 ;
}
/*
* Adjust log type
*/
if ( arg_count ( cmd , corelog_ARG ) )
corelog = 1 ;
mirrorlog = arg_str_value ( cmd , mirrorlog_ARG ,
corelog ? " core " : DEFAULT_MIRRORLOG ) ;
if ( ! strcmp ( " disk " , mirrorlog ) ) {
if ( corelog ) {
log_error ( " --mirrorlog disk and --corelog "
" are incompatible " ) ;
return 0 ;
}
corelog = 0 ;
} else if ( ! strcmp ( " core " , mirrorlog ) )
corelog = 1 ;
else {
log_error ( " Unknown mirrorlog type: %s " , mirrorlog ) ;
2007-08-02 01:01:06 +04:00
return 0 ;
}
2009-04-23 20:56:21 +04:00
log_verbose ( " Setting logging type to %s " , mirrorlog ) ;
}
2007-08-21 23:46:36 +04:00
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 ) & &
( lp - > region_size ! = seg - > region_size ) ) {
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
/*
* FIXME This check used to precede mirror - > mirror conversion
* but didn ' t affect mirror - > linear or linear - > mirror . I do
* not understand what is its intention , in fact .
*/
if ( dm_list_size ( & lv - > segments ) ! = 1 ) {
log_error ( " Logical volume %s has multiple "
" mirror segments. " , lv - > name ) ;
return 0 ;
}
restart :
2007-08-02 00:54:28 +04:00
/*
* Converting from mirror to linear
*/
2005-08-15 18:10:28 +04:00
if ( ( lp - > mirrors = = 1 ) ) {
2005-06-06 21:12:08 +04:00
if ( ! ( lv - > status & MIRRORED ) ) {
log_error ( " Logical volume %s is already not mirrored. " ,
lv - > name ) ;
return 1 ;
}
2007-08-02 00:54:28 +04:00
}
/*
2009-04-23 20:56:21 +04:00
* Downconversion .
2007-08-02 00:54:28 +04:00
*/
2009-04-23 20:56:21 +04:00
if ( lp - > mirrors < existing_mirrors ) {
/* Reduce number of mirrors */
if ( arg_count ( cmd , repair_ARG ) | | lp - > pv_count )
remove_pvs = lp - > pvh ;
if ( ! lv_remove_mirrors ( cmd , lv , existing_mirrors - lp - > mirrors ,
( corelog | | lp - > mirrors = = 1 ) ? 1U : 0U ,
remove_pvs , 0 ) )
return_0 ;
} else if ( ! ( lv - > status & MIRRORED ) ) {
/*
* Converting from linear to mirror
*/
2007-08-02 00:54:28 +04:00
/* FIXME Share code with lvcreate */
/* FIXME Why is this restriction here? Fix it! */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( seg , & lv - > segments ) {
2007-08-02 00:54:28 +04:00
if ( seg_is_striped ( seg ) & & seg - > area_count > 1 ) {
log_error ( " Mirrors of striped volumes are not yet supported. " ) ;
2005-06-06 21:12:08 +04:00
return 0 ;
2005-11-29 21:20:23 +03:00
}
2007-08-02 00:54:28 +04:00
}
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 .
*/
2007-12-20 18:42:55 +03:00
if ( ! lv_add_mirrors ( cmd , lv , lp - > mirrors - 1 , 1 ,
adjusted_mirror_region_size (
lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ,
corelog ? 0U : 1U , lp - > pvh , lp - > alloc ,
MIRROR_BY_LV ) )
2007-08-02 00:54:28 +04:00
return_0 ;
2008-01-15 00:11:47 +03:00
if ( lp - > wait_completion )
lp - > need_polling = 1 ;
2009-04-23 20:56:21 +04:00
} else if ( lp - > mirrors > existing_mirrors | | failed_mirrors ) {
2007-12-22 05:13:00 +03:00
if ( lv - > status & MIRROR_NOTSYNCED ) {
log_error ( " Not adding mirror to mirrored LV "
" without initial resync " ) ;
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 .
*/
2008-01-16 22:16:48 +03:00
original_lv = _original_lv ( lv ) ;
2008-01-16 22:40:42 +03:00
if ( ! first_seg ( original_lv ) - > log_lv & & ! corelog ) {
2008-01-16 22:16:48 +03:00
if ( ! add_mirror_log ( cmd , original_lv , 1 ,
2008-01-10 21:35:51 +03:00
adjusted_mirror_region_size (
lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ,
lp - > pvh , lp - > alloc ) )
return_0 ;
2008-01-16 22:40:42 +03:00
} else if ( first_seg ( original_lv ) - > log_lv & & corelog ) {
2008-01-16 22:16:48 +03:00
if ( ! remove_mirror_log ( cmd , original_lv ,
2008-01-10 21:35:51 +03:00
lp - > pv_count ? lp - > pvh : NULL ) )
return_0 ;
}
/* 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 ;
}
2008-01-10 21:35:51 +03:00
/* FIXME: can't have multiple mlogs. force corelog. */
2007-12-20 21:55:46 +03:00
if ( ! lv_add_mirrors ( cmd , lv , lp - > mirrors - existing_mirrors , 1 ,
adjusted_mirror_region_size (
lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ,
2008-01-10 21:35:51 +03:00
0U , lp - > pvh , lp - > alloc ,
2007-12-20 21:55:46 +03:00
MIRROR_BY_LV ) )
return_0 ;
2008-01-10 21:35:51 +03:00
lv - > status | = CONVERTING ;
2008-01-15 00:11:47 +03:00
lp - > need_polling = 1 ;
2005-06-06 21:12:08 +04:00
}
2009-04-23 20:56:21 +04:00
if ( lp - > mirrors = = existing_mirrors ) {
/*
* Convert Mirror log type
*/
original_lv = _original_lv ( lv ) ;
if ( ! first_seg ( original_lv ) - > log_lv & & ! corelog ) {
if ( ! add_mirror_log ( cmd , original_lv , 1 ,
adjusted_mirror_region_size (
lv - > vg - > extent_size ,
lv - > le_count ,
lp - > region_size ) ,
lp - > pvh , lp - > alloc ) )
return_0 ;
} else if ( first_seg ( original_lv ) - > log_lv & & corelog ) {
if ( ! remove_mirror_log ( cmd , original_lv ,
lp - > pv_count ? lp - > pvh : NULL ) )
return_0 ;
} else {
/* No change */
log_error ( " Logical volume %s already has % "
PRIu32 " mirror(s). " , lv - > name ,
lp - > mirrors - 1 ) ;
if ( lv - > status & CONVERTING )
lp - > need_polling = 1 ;
return 1 ;
}
}
2005-06-06 21:12:08 +04:00
log_very_verbose ( " Updating logical volume \" %s \" on disk(s) " , lv - > name ) ;
2006-04-06 00:43:23 +04:00
if ( ! vg_write ( lv - > vg ) )
return_0 ;
2005-06-06 21:12:08 +04:00
2005-08-15 16:00:04 +04:00
if ( ! suspend_lv ( cmd , lv ) ) {
2005-06-06 21:12:08 +04:00
log_error ( " Failed to lock %s " , lv - > name ) ;
vg_revert ( lv - > vg ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2005-06-06 21:12:08 +04:00
}
if ( ! vg_commit ( lv - > vg ) ) {
2005-08-15 16:00:04 +04:00
resume_lv ( cmd , lv ) ;
2009-04-21 18:31:57 +04:00
goto_out ;
2005-06-06 21:12:08 +04:00
}
log_very_verbose ( " Updating \" %s \" in kernel " , lv - > name ) ;
2005-08-15 16:00:04 +04:00
if ( ! resume_lv ( cmd , lv ) ) {
2005-06-06 21:12:08 +04:00
log_error ( " Problem reactivating %s " , lv - > name ) ;
2009-04-21 18:31:57 +04:00
goto out ;
2005-06-06 21:12:08 +04:00
}
2009-04-23 20:56:21 +04:00
if ( failed_log | | failed_mirrors ) {
lp - > pvh = old_pvh ;
if ( failed_log )
failed_log = corelog = 0 ;
lp - > mirrors + = failed_mirrors ;
failed_mirrors = 0 ;
existing_mirrors = lv_mirror_count ( lv ) ;
/* Now replace missing devices. */
goto restart ;
}
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
r = 1 ;
out :
backup ( lv - > vg ) ;
return r ;
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 ;
}
2007-11-02 23:40:05 +03:00
if ( ! vg_add_snapshot ( NULL , org , lv , NULL , org - > le_count ,
2006-04-06 00:43:23 +04:00
lp - > chunk_size ) ) {
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
}
2005-08-15 18:10:28 +04: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 ;
}
if ( lv_is_origin ( lv ) ) {
log_error ( " Can't convert logical volume \" %s \" under snapshot " ,
lv - > name ) ;
return ECMD_FAILED ;
}
if ( lv_is_cow ( lv ) ) {
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 ;
}
2008-06-27 01:38:58 +04:00
if ( lp - > snapshot ) {
if ( lv - > status & MIRRORED ) {
log_error ( " Unable to convert mirrored LV \" %s \" into a snapshot. " , lv - > name ) ;
return ECMD_FAILED ;
}
2005-06-06 21:12:08 +04:00
if ( ! archive ( lv - > vg ) )
return ECMD_FAILED ;
2008-06-27 01:38:58 +04:00
if ( ! lvconvert_snapshot ( cmd , lv , lp ) )
2005-06-06 21:12:08 +04:00
return ECMD_FAILED ;
2008-06-27 01:38:58 +04:00
} else if ( arg_count ( cmd , mirrors_ARG ) | | ( lv - > status & MIRRORED ) ) {
2006-04-06 00:43:23 +04:00
if ( ! archive ( lv - > vg ) )
return ECMD_FAILED ;
2008-06-27 01:38:58 +04:00
if ( ! lvconvert_mirrors ( cmd , lv , lp ) )
2006-04-06 00:43:23 +04:00
return ECMD_FAILED ;
2005-06-06 21:12:08 +04:00
}
return ECMD_PROCESSED ;
}
int lvconvert ( struct cmd_context * cmd , int argc , char * * argv )
{
struct volume_group * vg ;
struct lv_list * lvl ;
struct lvconvert_params lp ;
int ret = ECMD_FAILED ;
2008-01-10 21:35:51 +03:00
struct lvinfo info ;
2005-06-06 21:12:08 +04:00
2005-08-15 18:10:28 +04:00
if ( ! _read_params ( & lp , cmd , argc , argv ) ) {
stack ;
2005-06-06 21:12:08 +04:00
return EINVALID_CMD_LINE ;
}
2006-04-06 00:43:23 +04:00
log_verbose ( " Checking for existing volume group \" %s \" " , lp . vg_name ) ;
2005-06-06 21:12:08 +04:00
2007-11-15 05:20:03 +03:00
if ( ! ( vg = vg_lock_and_read ( cmd , lp . vg_name , NULL , LCK_VG_WRITE ,
2007-07-24 02:20:42 +04:00
CLUSTERED | EXPORTED_VG | LVM_WRITE ,
CORRECT_INCONSISTENT ) ) )
2005-06-06 21:12:08 +04:00
return ECMD_FAILED ;
2005-08-15 18:10:28 +04:00
if ( ! ( lvl = find_lv_in_vg ( vg , lp . lv_name ) ) ) {
2005-06-06 21:12:08 +04:00
log_error ( " Logical volume \" %s \" not found in "
2006-04-06 00:43:23 +04:00
" volume group \" %s \" " , lp . lv_name , lp . vg_name ) ;
2008-01-30 16:19:47 +03:00
goto bad ;
2005-06-06 21:12:08 +04:00
}
2005-08-15 18:10:28 +04:00
if ( lp . pv_count ) {
if ( ! ( lp . pvh = create_pv_list ( cmd - > mem , vg , lp . pv_count ,
2008-04-18 16:50:24 +04:00
lp . pvs , 0 ) ) )
2008-01-30 16:19:47 +03:00
goto_bad ;
2005-06-06 21:12:08 +04:00
} else
lp . pvh = & vg - > pvs ;
2005-08-15 18:10:28 +04:00
ret = lvconvert_single ( cmd , lvl - > lv , & lp ) ;
2005-06-06 21:12:08 +04:00
2008-01-30 16:19:47 +03:00
bad :
2006-04-06 00:43:23 +04:00
unlock_vg ( cmd , lp . vg_name ) ;
2007-12-22 15:13:29 +03:00
2008-01-15 00:11:47 +03:00
if ( ret = = ECMD_PROCESSED & & lp . need_polling ) {
2008-01-10 21:35:51 +03:00
if ( ! lv_info ( cmd , lvl - > lv , & info , 1 , 0 ) | | ! info . exists ) {
log_print ( " Conversion starts after activation " ) ;
2009-04-10 14:01:38 +04:00
goto out ;
2008-01-10 21:35:51 +03:00
}
ret = lvconvert_poll ( cmd , lp . lv_name_full ,
2008-01-15 00:11:47 +03:00
lp . wait_completion ? 0 : 1U ) ;
2008-01-10 21:35:51 +03:00
}
2009-04-10 14:01:38 +04:00
out :
vg_release ( vg ) ;
2005-06-06 21:12:08 +04:00
return ret ;
}