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 ) & &
2009-06-30 22:39:31 +04:00
( arg_count ( cmd , mirrorlog_ARG ) | | arg_count ( cmd , mirrors_ARG ) | |
arg_count ( cmd , repair_ARG ) ) ) {
log_error ( " --snapshot argument cannot be mixed "
" 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 ;
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 ) ;
}
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
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 ,
2009-06-01 18:43:27 +04:00
const char * lv_name , const char * uuid )
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
2009-07-01 20:59:37 +04:00
return vg_read_for_update ( cmd , extract_vgname ( cmd , lv_name ) ,
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 _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 ,
2009-09-30 22:15:06 +04:00
. poll_progress = poll_mirror_progress ,
2007-12-22 15:13:29 +03:00
. update_metadata = _update_lvconvert_mirror ,
. finish_copy = _finish_lvconvert_mirror ,
} ;
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
{
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 ) ) ;
return poll_daemon ( cmd , lv_full_name , uuid , background , 0 ,
& _lvconvert_mirror_fns , " Converted " ) ;
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 ) & &
( seg_pv ( lvseg , s ) - > status & MISSING_PV ) )
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 ) {
if ( ! ( pvl - > pv - > status & MISSING_PV ) )
continue ;
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
}
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 ,
" 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 ;
}
}
2009-08-03 01:56:29 +04:00
static int _using_corelog ( struct logical_volume * lv )
{
return ! first_seg ( _original_lv ( lv ) ) - > log_lv ;
}
static int _lv_update_log_type ( struct cmd_context * cmd ,
struct lvconvert_params * lp ,
struct logical_volume * lv ,
int corelog )
{
struct logical_volume * original_lv = _original_lv ( lv ) ;
if ( _using_corelog ( 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 ( ! _using_corelog ( lv ) & & corelog ) {
if ( ! remove_mirror_log ( cmd , original_lv ,
lp - > pv_count ? lp - > pvh : NULL ) )
return_0 ;
}
return 1 ;
}
2009-05-21 07:04:52 +04:00
static int _lvconvert_mirrors ( struct cmd_context * cmd , struct logical_volume * lv ,
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 ;
2009-04-21 18:31:57 +04:00
int r = 0 ;
2009-10-23 05:24:17 +04:00
struct logical_volume * log_lv , * layer_lv ;
2009-04-23 20:56:21 +04:00
int failed_mirrors = 0 , failed_log = 0 ;
2009-05-19 14:12:41 +04:00
struct dm_list * old_pvh = NULL , * remove_pvs = NULL ;
2005-11-29 21:20:23 +03:00
2009-06-04 16:01:15 +04:00
int repair = arg_count ( cmd , repair_ARG ) ;
int replace_log = 1 , replace_mirrors = 1 ;
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 ) & &
2009-06-04 16:01:15 +04:00
! repair ) {
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
2009-06-04 16:01:15 +04:00
if ( arg_count ( cmd , mirrors_ARG ) & & repair ) {
2009-06-30 22:39:31 +04:00
log_error ( " You may only use one of --mirrors and --repair. " ) ;
2009-04-23 20:56:21 +04:00
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-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 ;
}
2009-06-04 16:01:15 +04:00
if ( repair ) {
2009-04-23 20:56:21 +04:00
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 ;
}
2009-05-21 07:04:52 +04:00
if ( ( failed_mirrors = _failed_mirrors_count ( lv ) ) < 0 )
2009-04-23 20:56:21 +04:00
return_0 ;
lp - > mirrors - = failed_mirrors ;
2009-10-22 02:19:39 +04:00
log_error ( " Mirror status: %d of %d images failed. " ,
2009-04-23 20:56:21 +04:00
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 ;
}
2009-04-30 00:11:46 +04:00
2009-04-23 20:56:21 +04:00
/*
* Adjust log type
*/
if ( arg_count ( cmd , corelog_ARG ) )
corelog = 1 ;
2009-04-30 00:11:46 +04:00
2009-04-23 20:56:21 +04:00
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
/*
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
2009-06-04 16:01:15 +04:00
if ( repair )
_lvconvert_mirrors_repair_ask ( cmd , failed_log , failed_mirrors ,
& replace_log , & replace_mirrors ) ;
2009-04-23 20:56:21 +04:00
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 */
2009-06-04 16:01:15 +04:00
if ( repair | | lp - > pv_count )
2009-04-23 20:56:21 +04:00
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 ;
2009-08-03 01:59:21 +04:00
if ( lp - > mirrors > 1 & &
! _lv_update_log_type ( cmd , lp , lv , corelog ) )
return_0 ;
2009-04-23 20:56:21 +04:00
} else if ( ! ( lv - > status & MIRRORED ) ) {
/*
* Converting from linear to mirror
*/
2009-04-30 00:11:46 +04:00
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 ) {
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 " ) ;
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 .
*/
2009-08-03 01:56:29 +04:00
if ( ! _lv_update_log_type ( cmd , lp , lv , corelog ) )
return_0 ;
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 ;
}
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 ,
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 ;
}
2007-12-20 21:55:46 +03:00
return_0 ;
2009-10-23 05:24:17 +04:00
}
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 ) {
2009-08-03 01:56:29 +04:00
if ( _using_corelog ( lv ) ! = corelog ) {
if ( ! _lv_update_log_type ( cmd , lp , lv , corelog ) )
2009-04-23 20:56:21 +04:00
return_0 ;
} else {
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 ;
2009-06-04 16:01:15 +04:00
if ( failed_log & & replace_log )
2009-04-23 20:56:21 +04:00
failed_log = corelog = 0 ;
2009-06-04 16:01:15 +04:00
if ( replace_mirrors )
lp - > mirrors + = failed_mirrors ;
2009-04-23 20:56:21 +04:00
failed_mirrors = 0 ;
existing_mirrors = lv_mirror_count ( lv ) ;
/* Now replace missing devices. */
2009-06-04 16:01:15 +04:00
if ( replace_log | | replace_mirrors )
goto restart ;
2009-04-23 20:56:21 +04:00
}
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 ;
}
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
}
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_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 ;
}
2009-06-10 19:27:57 +04:00
if ( arg_count ( cmd , repair_ARG ) & & ! ( lv - > status & MIRRORED ) ) {
log_error ( " Can't repair non-mirrored 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 ;
}
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
}
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 ;
2009-06-15 18:47:39 +04:00
int saved_ignore_suspended_devices = ignore_suspended_devices ( ) ;
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 ;
}
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
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
2009-07-01 20:59:37 +04:00
vg = vg_read_for_update ( cmd , lp . vg_name , NULL , 0 ) ;
if ( vg_read_error ( vg ) )
2009-06-26 14:55:57 +04:00
goto out ;
2005-06-06 21:12:08 +04:00
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
}
2009-06-01 18:43:27 +04:00
ret = lvconvert_poll ( cmd , lvl - > lv ,
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 :
2009-06-15 18:47:39 +04:00
init_ignore_suspended_devices ( saved_ignore_suspended_devices ) ;
2009-04-10 14:01:38 +04:00
vg_release ( vg ) ;
2005-06-06 21:12:08 +04:00
return ret ;
}