2003-05-06 16:22:24 +04:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2003 - 2004 Sistina Software , Inc . All rights reserved .
2005-11-29 21:20:23 +03:00
* Copyright ( C ) 2004 - 2005 Red Hat , Inc . All rights reserved .
2003-05-06 16:22:24 +04:00
*
2004-03-30 23:35:44 +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
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2003-05-06 16:22:24 +04:00
*/
# include "lib.h"
# include "metadata.h"
# include "toolcontext.h"
2004-09-16 22:40:56 +04:00
# include "segtype.h"
2004-05-05 01:25:57 +04:00
# include "display.h"
2004-05-25 00:51:56 +04:00
# include "activate.h"
2005-04-22 19:43:02 +04:00
# include "lv_alloc.h"
2005-06-03 18:49:51 +04:00
# include "lvm-string.h"
2007-03-26 20:10:10 +04:00
# include "str_list.h"
2005-11-29 21:20:23 +03:00
# include "locking.h" /* FIXME Should not be used in this file */
2005-06-03 18:49:51 +04:00
2006-05-11 23:45:53 +04:00
# include "defaults.h" /* FIXME: should this be defaults.h? */
/* These are the flags that represent the mirror failure restoration policies */
# define MIRROR_REMOVE 0
# define MIRROR_ALLOCATE 1
# define MIRROR_ALLOCATE_ANYWHERE 2
2005-10-27 23:58:22 +04:00
struct lv_segment * find_mirror_seg ( struct lv_segment * seg )
{
return seg - > mirror_seg ;
}
2005-08-12 23:23:08 +04:00
/*
2006-11-10 22:35:03 +03:00
* Reduce the region size if necessary to ensure
* the volume size is a multiple of the region size .
2005-08-12 23:23:08 +04:00
*/
uint32_t adjusted_mirror_region_size ( uint32_t extent_size , uint32_t extents ,
uint32_t region_size )
{
2006-11-10 22:35:03 +03:00
uint64_t region_max ;
2005-08-12 23:23:08 +04:00
2006-11-10 23:15:10 +03:00
region_max = ( 1 < < ( ffs ( ( int ) extents ) - 1 ) ) * ( uint64_t ) extent_size ;
2005-08-12 23:23:08 +04:00
2006-11-10 22:35:03 +03:00
if ( region_max < UINT32_MAX & & region_size > region_max ) {
region_size = ( uint32_t ) region_max ;
2005-08-12 23:23:08 +04:00
log_print ( " Using reduced mirror region size of % " PRIu32
2006-11-10 22:35:03 +03:00
" sectors " , region_size ) ;
2005-08-12 23:23:08 +04:00
}
return region_size ;
}
2005-10-28 16:48:50 +04:00
static void _move_lv_segments ( struct logical_volume * lv_to , struct logical_volume * lv_from )
{
struct lv_segment * seg ;
lv_to - > segments = lv_from - > segments ;
lv_to - > segments . n - > p = & lv_to - > segments ;
lv_to - > segments . p - > n = & lv_to - > segments ;
list_iterate_items ( seg , & lv_to - > segments )
seg - > lv = lv_to ;
/* FIXME set or reset seg->mirror_seg (according to status)? */
list_init ( & lv_from - > segments ) ;
2005-11-29 21:20:23 +03:00
lv_to - > le_count = lv_from - > le_count ;
lv_to - > size = lv_from - > size ;
2005-10-28 16:48:50 +04:00
lv_from - > le_count = 0 ;
lv_from - > size = 0 ;
}
2007-03-26 20:10:10 +04:00
/*
* Delete independent / orphan LV , it must acquire lock .
*/
static int _delete_lv ( struct lv_segment * mirrored_seg , struct logical_volume * lv )
{
struct cmd_context * cmd = mirrored_seg - > lv - > vg - > cmd ;
struct str_list * sl ;
/* Inherit tags - maybe needed for activation */
if ( ! str_list_match_list ( & mirrored_seg - > lv - > tags , & lv - > tags ) ) {
list_iterate_items ( sl , & mirrored_seg - > lv - > tags )
if ( ! str_list_add ( cmd - > mem , & lv - > tags , sl - > str ) ) {
log_error ( " Aborting. Unable to tag. " ) ;
return 0 ;
}
if ( ! vg_write ( mirrored_seg - > lv - > vg ) | |
! vg_commit ( mirrored_seg - > lv - > vg ) ) {
log_error ( " Intermediate VG commit for orphan volume failed. " ) ;
return 0 ;
}
}
if ( ! activate_lv ( cmd , lv ) )
return_0 ;
if ( ! deactivate_lv ( cmd , lv ) )
return_0 ;
if ( ! lv_remove ( lv ) )
return_0 ;
return 1 ;
}
2005-06-06 21:12:08 +04:00
/*
* Reduce mirrored_seg to num_mirrors images .
*/
2005-11-29 21:20:23 +03:00
int remove_mirror_images ( struct lv_segment * mirrored_seg , uint32_t num_mirrors ,
struct list * removable_pvs , int remove_log )
2005-06-06 21:12:08 +04:00
{
uint32_t m ;
2006-11-30 20:52:47 +03:00
uint32_t extents ;
2005-11-29 21:20:23 +03:00
uint32_t s , s1 ;
struct logical_volume * sub_lv ;
struct logical_volume * log_lv = NULL ;
struct logical_volume * lv1 = NULL ;
struct physical_volume * pv ;
struct lv_segment * seg ;
struct lv_segment_area area ;
int all_pvs_removable , pv_found ;
struct pv_list * pvl ;
uint32_t old_area_count = mirrored_seg - > area_count ;
uint32_t new_area_count = mirrored_seg - > area_count ;
2006-11-30 20:52:47 +03:00
struct segment_type * segtype ;
2005-11-29 21:20:23 +03:00
log_very_verbose ( " Reducing mirror set from % " PRIu32 " to % "
PRIu32 " image(s)%s. " ,
old_area_count , num_mirrors ,
remove_log ? " and no log volume " : " " ) ;
/* Move removable_pvs to end of array */
if ( removable_pvs ) {
for ( s = 0 ; s < mirrored_seg - > area_count ; s + + ) {
all_pvs_removable = 1 ;
sub_lv = seg_lv ( mirrored_seg , s ) ;
list_iterate_items ( seg , & sub_lv - > segments ) {
for ( s1 = 0 ; s1 < seg - > area_count ; s1 + + ) {
if ( seg_type ( seg , s1 ) ! = AREA_PV )
/* FIXME Recurse for AREA_LV */
continue ;
pv = seg_pv ( seg , s1 ) ;
pv_found = 0 ;
list_iterate_items ( pvl , removable_pvs ) {
if ( pv - > dev - > dev = = pvl - > pv - > dev - > dev ) {
pv_found = 1 ;
break ;
}
}
if ( ! pv_found ) {
all_pvs_removable = 0 ;
break ;
}
}
if ( ! all_pvs_removable )
break ;
}
if ( all_pvs_removable ) {
/* Swap segment to end */
new_area_count - - ;
area = mirrored_seg - > areas [ new_area_count ] ;
mirrored_seg - > areas [ new_area_count ] = mirrored_seg - > areas [ s ] ;
mirrored_seg - > areas [ s ] = area ;
}
/* Found enough matches? */
if ( new_area_count = = num_mirrors )
break ;
}
if ( new_area_count = = mirrored_seg - > area_count ) {
log_error ( " No mirror images found using specified PVs. " ) ;
2005-06-06 21:12:08 +04:00
return 0 ;
}
}
2005-11-29 21:20:23 +03:00
for ( m = num_mirrors ; m < mirrored_seg - > area_count ; m + + ) {
seg_lv ( mirrored_seg , m ) - > status & = ~ MIRROR_IMAGE ;
seg_lv ( mirrored_seg , m ) - > status | = VISIBLE_LV ;
}
2005-06-06 21:12:08 +04:00
mirrored_seg - > area_count = num_mirrors ;
2005-11-29 21:20:23 +03:00
/* If no more mirrors, remove mirror layer */
if ( num_mirrors = = 1 ) {
lv1 = seg_lv ( mirrored_seg , 0 ) ;
2006-11-30 20:52:47 +03:00
extents = lv1 - > le_count ;
2005-11-29 21:20:23 +03:00
_move_lv_segments ( mirrored_seg - > lv , lv1 ) ;
mirrored_seg - > lv - > status & = ~ MIRRORED ;
remove_log = 1 ;
2006-11-30 20:52:47 +03:00
/* Replace mirror with error segment */
segtype = get_segtype_from_string ( mirrored_seg - > lv - > vg - > cmd , " error " ) ;
if ( ! lv_add_virtual_segment ( lv1 , 0 , extents , segtype ) )
return_0 ;
2005-11-29 21:20:23 +03:00
}
2005-06-06 21:12:08 +04:00
2006-09-19 23:13:41 +04:00
if ( remove_log & & mirrored_seg - > log_lv ) {
2005-11-29 21:20:23 +03:00
log_lv = mirrored_seg - > log_lv ;
mirrored_seg - > log_lv = NULL ;
2006-09-19 23:13:41 +04:00
log_lv - > status & = ~ MIRROR_LOG ;
log_lv - > status | = VISIBLE_LV ;
2005-11-29 21:20:23 +03:00
}
2005-06-06 21:12:08 +04:00
2005-11-29 21:20:23 +03:00
/*
* To successfully remove these unwanted LVs we need to
* remove the LVs from the mirror set , commit that metadata
* then deactivate and remove them fully .
*/
2005-06-06 21:12:08 +04:00
2005-11-29 21:20:23 +03:00
if ( ! vg_write ( mirrored_seg - > lv - > vg ) ) {
log_error ( " intermediate VG write failed. " ) ;
2005-06-06 21:12:08 +04:00
return 0 ;
}
2005-11-29 21:20:23 +03:00
if ( ! suspend_lv ( mirrored_seg - > lv - > vg - > cmd , mirrored_seg - > lv ) ) {
log_error ( " Failed to lock %s " , mirrored_seg - > lv - > name ) ;
vg_revert ( mirrored_seg - > lv - > vg ) ;
2005-06-06 21:12:08 +04:00
return 0 ;
}
2005-11-29 21:20:23 +03:00
if ( ! vg_commit ( mirrored_seg - > lv - > vg ) ) {
resume_lv ( mirrored_seg - > lv - > vg - > cmd , mirrored_seg - > lv ) ;
return 0 ;
}
2005-06-06 21:12:08 +04:00
2005-11-29 21:20:23 +03:00
log_very_verbose ( " Updating \" %s \" in kernel " , mirrored_seg - > lv - > name ) ;
2005-06-06 21:12:08 +04:00
2005-11-29 21:20:23 +03:00
if ( ! resume_lv ( mirrored_seg - > lv - > vg - > cmd , mirrored_seg - > lv ) ) {
log_error ( " Problem reactivating %s " , mirrored_seg - > lv - > name ) ;
2005-06-06 21:12:08 +04:00
return 0 ;
}
2005-11-29 21:20:23 +03:00
/* Delete the 'orphan' LVs */
2007-03-26 20:10:10 +04:00
for ( m = num_mirrors ; m < old_area_count ; m + + )
if ( ! _delete_lv ( mirrored_seg , seg_lv ( mirrored_seg , m ) ) )
2005-11-29 21:20:23 +03:00
return 0 ;
2006-07-21 00:37:10 +04:00
2007-03-26 20:10:10 +04:00
if ( lv1 & & ! _delete_lv ( mirrored_seg , lv1 ) )
return 0 ;
2005-11-29 21:20:23 +03:00
2007-03-26 20:10:10 +04:00
if ( log_lv & & ! _delete_lv ( mirrored_seg , log_lv ) )
return 0 ;
2005-11-29 21:20:23 +03:00
return 1 ;
2005-06-06 21:12:08 +04:00
}
2006-05-11 23:45:53 +04:00
static int get_mirror_fault_policy ( struct cmd_context * cmd , int log_policy )
{
const char * policy ;
if ( log_policy )
policy = find_config_str ( NULL , " activation/mirror_log_fault_policy " ,
DEFAULT_MIRROR_LOG_FAULT_POLICY ) ;
else
2006-05-12 23:47:40 +04:00
policy = find_config_str ( NULL , " activation/mirror_device_fault_policy " ,
2006-05-11 23:45:53 +04:00
DEFAULT_MIRROR_DEV_FAULT_POLICY ) ;
if ( ! strcmp ( policy , " remove " ) )
return MIRROR_REMOVE ;
else if ( ! strcmp ( policy , " allocate " ) )
return MIRROR_ALLOCATE ;
else if ( ! strcmp ( policy , " allocate_anywhere " ) )
return MIRROR_ALLOCATE_ANYWHERE ;
if ( log_policy )
log_error ( " Bad activation/mirror_log_fault_policy " ) ;
else
2006-05-12 23:47:40 +04:00
log_error ( " Bad activation/mirror_device_fault_policy " ) ;
2006-05-11 23:45:53 +04:00
return MIRROR_REMOVE ;
}
static int get_mirror_log_fault_policy ( struct cmd_context * cmd )
{
return get_mirror_fault_policy ( cmd , 1 ) ;
}
2006-05-12 23:47:40 +04:00
static int get_mirror_device_fault_policy ( struct cmd_context * cmd )
2006-05-11 23:45:53 +04:00
{
return get_mirror_fault_policy ( cmd , 0 ) ;
}
/*
* replace_mirror_images
* @ mirrored_seg : segment ( which may be linear now ) to restore
* @ num_mirrors : number of copies we should end up with
* @ replace_log : replace log if not present
* @ in_sync : was the original mirror in - sync ?
*
* in_sync will be set to 0 if new mirror devices are being added
* In other words , it is only useful if the log ( and only the log )
* is being restored .
*
* Returns : 0 on failure , 1 on reconfig , - 1 if no reconfig done
*/
static int replace_mirror_images ( struct lv_segment * mirrored_seg ,
uint32_t num_mirrors ,
int log_policy , int in_sync )
{
int r = - 1 ;
struct logical_volume * lv = mirrored_seg - > lv ;
/* FIXME: Use lvconvert rather than duplicating its code */
if ( mirrored_seg - > area_count < num_mirrors ) {
log_error ( " WARNING: Failed to replace mirror device in %s/%s " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
if ( ( mirrored_seg - > area_count > 1 ) & & ! mirrored_seg - > log_lv )
log_error ( " WARNING: Use 'lvconvert -m %d %s/%s --corelog' to replace failed devices " ,
num_mirrors - 1 , lv - > vg - > name , lv - > name ) ;
else
log_error ( " WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices " ,
num_mirrors - 1 , lv - > vg - > name , lv - > name ) ;
r = 0 ;
/* REMEMBER/FIXME: set in_sync to 0 if a new mirror device was added */
in_sync = 0 ;
}
/*
* FIXME : right now , we ignore the allocation policy specified to
* allocate the new log .
*/
if ( ( mirrored_seg - > area_count > 1 ) & & ! mirrored_seg - > log_lv & &
( log_policy ! = MIRROR_REMOVE ) ) {
log_error ( " WARNING: Failed to replace mirror log device in %s/%s " ,
lv - > vg - > name , lv - > name ) ;
log_error ( " WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices " ,
mirrored_seg - > area_count - 1 , lv - > vg - > name , lv - > name ) ;
r = 0 ;
}
return r ;
}
2006-05-11 23:01:11 +04:00
int reconfigure_mirror_images ( struct lv_segment * mirrored_seg , uint32_t num_mirrors ,
struct list * removable_pvs , int remove_log )
{
int r ;
int insync = 0 ;
2006-05-11 23:45:53 +04:00
int log_policy , dev_policy ;
uint32_t old_num_mirrors = mirrored_seg - > area_count ;
int had_log = ( mirrored_seg - > log_lv ) ? 1 : 0 ;
2006-05-11 23:01:11 +04:00
float sync_percent = 0 ;
/* was the mirror in-sync before problems? */
if ( ! lv_mirror_percent ( mirrored_seg - > lv - > vg - > cmd ,
mirrored_seg - > lv , 0 , & sync_percent , NULL ) )
2006-05-11 23:45:53 +04:00
log_error ( " WARNING: Unable to determine mirror sync status of %s/%s. " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
2006-05-11 23:01:11 +04:00
else if ( sync_percent > = 100.0 )
insync = 1 ;
/*
* While we are only removing devices , we can have sync set .
* Setting this is only useful if we are moving to core log
* otherwise the disk log will contain the sync information
*/
init_mirror_in_sync ( insync ) ;
r = remove_mirror_images ( mirrored_seg , num_mirrors ,
removable_pvs , remove_log ) ;
if ( ! r )
/* Unable to remove bad devices */
return 0 ;
2006-05-11 23:45:53 +04:00
log_print ( " WARNING: Bad device removed from mirror volume, %s/%s " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
log_policy = get_mirror_log_fault_policy ( mirrored_seg - > lv - > vg - > cmd ) ;
2006-05-12 23:47:40 +04:00
dev_policy = get_mirror_device_fault_policy ( mirrored_seg - > lv - > vg - > cmd ) ;
2006-05-11 23:45:53 +04:00
r = replace_mirror_images ( mirrored_seg ,
( dev_policy ! = MIRROR_REMOVE ) ?
old_num_mirrors : num_mirrors ,
log_policy , insync ) ;
if ( ! r )
/* Failed to replace device(s) */
log_error ( " WARNING: Unable to find substitute device for mirror volume, %s/%s " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
else if ( r > 0 )
/* Success in replacing device(s) */
log_print ( " WARNING: Mirror volume, %s/%s restored - substitute for failed device found. " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
else
/* Bad device removed, but not replaced because of policy */
if ( mirrored_seg - > area_count = = 1 ) {
log_print ( " WARNING: Mirror volume, %s/%s converted to linear due to device failure. " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
} else if ( had_log & & ! mirrored_seg - > log_lv ) {
log_print ( " WARNING: Mirror volume, %s/%s disk log removed due to device failure. " ,
mirrored_seg - > lv - > vg - > name , mirrored_seg - > lv - > name ) ;
}
/*
* If we made it here , we at least removed the bad device .
* Consider this success .
*/
2006-05-11 23:01:11 +04:00
return 1 ;
}
2005-10-28 16:48:50 +04:00
static int _create_layers_for_mirror ( struct alloc_handle * ah ,
uint32_t first_area ,
uint32_t num_mirrors ,
struct logical_volume * lv ,
2006-05-10 01:23:51 +04:00
const struct segment_type * segtype ,
2005-10-28 16:48:50 +04:00
struct logical_volume * * img_lvs )
2005-06-03 18:49:51 +04:00
{
uint32_t m ;
char * img_name ;
size_t len ;
len = strlen ( lv - > name ) + 32 ;
if ( ! ( img_name = alloca ( len ) ) ) {
log_error ( " img_name allocation failed. "
" Remove new LV and retry. " ) ;
return 0 ;
}
2006-08-21 16:54:53 +04:00
if ( dm_snprintf ( img_name , len , " %s_mimage_%%d " , lv - > name ) < 0 ) {
2005-06-03 18:49:51 +04:00
log_error ( " img_name allocation failed. "
" Remove new LV and retry. " ) ;
return 0 ;
}
for ( m = 0 ; m < num_mirrors ; m + + ) {
if ( ! ( img_lvs [ m ] = lv_create_empty ( lv - > vg - > fid , img_name ,
NULL , LVM_READ | LVM_WRITE ,
2005-10-27 23:58:22 +04:00
ALLOC_INHERIT , 0 , lv - > vg ) ) ) {
log_error ( " Aborting. Failed to create mirror image LV. "
2005-06-03 18:49:51 +04:00
" Remove new LV and retry. " ) ;
return 0 ;
}
2005-11-29 21:20:23 +03:00
if ( m < first_area )
continue ;
if ( ! lv_add_segment ( ah , m - first_area , 1 , img_lvs [ m ] ,
2005-06-03 18:49:51 +04:00
get_segtype_from_string ( lv - > vg - > cmd ,
" striped " ) ,
0 , NULL , 0 , 0 , 0 , NULL ) ) {
2005-10-27 23:58:22 +04:00
log_error ( " Aborting. Failed to add mirror image segment "
2005-06-03 18:49:51 +04:00
" to %s. Remove new LV and retry. " ,
img_lvs [ m ] - > name ) ;
return 0 ;
}
}
2005-10-28 16:48:50 +04:00
return 1 ;
}
int create_mirror_layers ( struct alloc_handle * ah ,
uint32_t first_area ,
uint32_t num_mirrors ,
struct logical_volume * lv ,
2006-05-10 01:23:51 +04:00
const struct segment_type * segtype ,
2005-10-28 16:48:50 +04:00
uint32_t status ,
uint32_t region_size ,
struct logical_volume * log_lv )
{
struct logical_volume * * img_lvs ;
if ( ! ( img_lvs = alloca ( sizeof ( * img_lvs ) * num_mirrors ) ) ) {
log_error ( " img_lvs allocation failed. "
" Remove new LV and retry. " ) ;
return 0 ;
}
if ( ! _create_layers_for_mirror ( ah , first_area , num_mirrors , lv ,
segtype , img_lvs ) ) {
stack ;
return 0 ;
}
/* Already got the parent mirror segment? */
if ( lv - > status & MIRRORED )
return lv_add_more_mirrored_areas ( lv , img_lvs , num_mirrors ,
MIRROR_IMAGE ) ;
/* Already got a non-mirrored area to be converted? */
2005-11-09 01:52:26 +03:00
if ( first_area )
2005-10-28 16:48:50 +04:00
_move_lv_segments ( img_lvs [ 0 ] , lv ) ;
2005-06-03 18:49:51 +04:00
if ( ! lv_add_mirror_segment ( ah , lv , img_lvs , num_mirrors , segtype ,
0 , region_size , log_lv ) ) {
log_error ( " Aborting. Failed to add mirror segment. "
" Remove new LV and retry. " ) ;
return 0 ;
}
2005-11-09 01:52:26 +03:00
lv - > status | = MIRRORED ;
2005-06-03 18:49:51 +04:00
return 1 ;
}
2003-05-06 16:22:24 +04:00
2005-11-29 21:20:23 +03:00
int add_mirror_layers ( struct alloc_handle * ah ,
uint32_t num_mirrors ,
uint32_t existing_mirrors ,
struct logical_volume * lv ,
2006-05-10 01:23:51 +04:00
const struct segment_type * segtype )
2005-11-29 21:20:23 +03:00
{
struct logical_volume * * img_lvs ;
if ( ! ( img_lvs = alloca ( sizeof ( * img_lvs ) * num_mirrors ) ) ) {
log_error ( " img_lvs allocation failed. "
" Remove new LV and retry. " ) ;
return 0 ;
}
if ( ! _create_layers_for_mirror ( ah , 0 , num_mirrors ,
lv , segtype ,
img_lvs ) ) {
stack ;
return 0 ;
}
return lv_add_more_mirrored_areas ( lv , img_lvs , num_mirrors , 0 ) ;
}
2003-05-06 16:22:24 +04:00
/*
* Replace any LV segments on given PV with temporary mirror .
* Returns list of LVs changed .
*/
int insert_pvmove_mirrors ( struct cmd_context * cmd ,
struct logical_volume * lv_mirr ,
2004-08-18 02:09:02 +04:00
struct list * source_pvl ,
2003-05-06 16:22:24 +04:00
struct logical_volume * lv ,
struct list * allocatable_pvs ,
2005-05-09 20:59:01 +04:00
alloc_policy_t alloc ,
2003-05-06 16:22:24 +04:00
struct list * lvs_changed )
{
struct lv_segment * seg ;
struct lv_list * lvl ;
2004-08-18 02:09:02 +04:00
struct pv_list * pvl ;
2005-06-14 21:54:48 +04:00
struct physical_volume * pv ;
uint32_t pe ;
2003-05-06 16:22:24 +04:00
int lv_used = 0 ;
uint32_t s , start_le , extent_count = 0u ;
2006-05-10 01:23:51 +04:00
const struct segment_type * segtype ;
2004-08-18 02:09:02 +04:00
struct pe_range * per ;
uint32_t pe_start , pe_end , per_end , stripe_multiplier ;
/* Only 1 PV may feature in source_pvl */
pvl = list_item ( source_pvl - > n , struct pv_list ) ;
2004-05-05 01:25:57 +04:00
if ( ! ( segtype = get_segtype_from_string ( lv - > vg - > cmd , " mirror " ) ) ) {
stack ;
return 0 ;
}
2003-05-06 16:22:24 +04:00
2004-05-25 00:51:56 +04:00
if ( activation ( ) & & segtype - > ops - > target_present & &
2006-10-18 22:01:53 +04:00
! segtype - > ops - > target_present ( NULL ) ) {
2004-05-25 00:51:56 +04:00
log_error ( " %s: Required device-mapper target(s) not "
" detected in your kernel " , segtype - > name ) ;
return 0 ;
}
2004-08-18 02:09:02 +04:00
/* Split LV segments to match PE ranges */
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2003-05-06 16:22:24 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( seg , s ) ! = AREA_PV | |
seg_dev ( seg , s ) ! = pvl - > pv - > dev )
2003-05-06 16:22:24 +04:00
continue ;
2004-08-18 02:09:02 +04:00
/* Do these PEs need moving? */
list_iterate_items ( per , pvl - > pe_ranges ) {
2005-06-01 20:51:55 +04:00
pe_start = seg_pe ( seg , s ) ;
2004-08-18 02:09:02 +04:00
pe_end = pe_start + seg - > area_len - 1 ;
per_end = per - > start + per - > count - 1 ;
/* No overlap? */
if ( ( pe_end < per - > start ) | |
( pe_start > per_end ) )
continue ;
2005-05-09 20:59:01 +04:00
if ( seg_is_striped ( seg ) )
2004-08-18 02:09:02 +04:00
stripe_multiplier = seg - > area_count ;
else
stripe_multiplier = 1 ;
if ( ( per - > start ! = pe_start & &
per - > start > pe_start ) & &
! lv_split_segment ( lv , seg - > le +
( per - > start - pe_start ) *
stripe_multiplier ) ) {
stack ;
2003-05-06 16:22:24 +04:00
return 0 ;
}
2004-08-18 02:09:02 +04:00
if ( ( per_end ! = pe_end & &
per_end < pe_end ) & &
! lv_split_segment ( lv , seg - > le +
( per_end - pe_start + 1 ) *
stripe_multiplier ) ) {
stack ;
return 0 ;
}
2003-05-06 16:22:24 +04:00
}
2004-08-18 02:09:02 +04:00
}
}
2003-05-06 16:22:24 +04:00
2004-08-18 02:09:02 +04:00
/* Work through all segments on the supplied PV */
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2004-08-18 02:09:02 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( seg , s ) ! = AREA_PV | |
seg_dev ( seg , s ) ! = pvl - > pv - > dev )
2004-08-18 02:09:02 +04:00
continue ;
2005-06-01 20:51:55 +04:00
pe_start = seg_pe ( seg , s ) ;
2004-08-18 02:09:02 +04:00
/* Do these PEs need moving? */
list_iterate_items ( per , pvl - > pe_ranges ) {
per_end = per - > start + per - > count - 1 ;
2003-05-06 16:22:24 +04:00
2004-08-18 02:09:02 +04:00
if ( ( pe_start < per - > start ) | |
( pe_start > per_end ) )
continue ;
log_debug ( " Matched PE range %u-%u against "
" %s %u len %u " , per - > start , per_end ,
2005-06-01 20:51:55 +04:00
dev_name ( seg_dev ( seg , s ) ) ,
seg_pe ( seg , s ) ,
2005-05-03 21:28:23 +04:00
seg - > area_len ) ;
2004-08-18 02:09:02 +04:00
/* First time, add LV to list of LVs affected */
if ( ! lv_used ) {
2005-10-17 03:03:59 +04:00
if ( ! ( lvl = dm_pool_alloc ( cmd - > mem , sizeof ( * lvl ) ) ) ) {
2004-08-18 02:09:02 +04:00
log_error ( " lv_list alloc failed " ) ;
return 0 ;
}
lvl - > lv = lv ;
list_add ( lvs_changed , & lvl - > list ) ;
lv_used = 1 ;
}
2005-06-14 21:54:48 +04:00
pv = seg_pv ( seg , s ) ;
pe = seg_pe ( seg , s ) ;
2004-08-18 02:09:02 +04:00
log_very_verbose ( " Moving %s:%u-%u of %s/%s " ,
dev_name ( pvl - > pv - > dev ) ,
2005-06-14 21:54:48 +04:00
pe , pe + seg - > area_len - 1 ,
2004-08-18 02:09:02 +04:00
lv - > vg - > name , lv - > name ) ;
start_le = lv_mirr - > le_count ;
2005-06-14 21:54:48 +04:00
/* FIXME Clean this up */
release_lv_segment_area ( seg , s , seg - > area_len ) ;
2005-05-09 20:59:01 +04:00
if ( ! lv_extend ( lv_mirr , segtype , 1 ,
2004-08-18 02:09:02 +04:00
seg - > area_len , 0u , seg - > area_len ,
2005-06-14 21:54:48 +04:00
pv , pe ,
2004-08-18 02:09:02 +04:00
PVMOVE , allocatable_pvs ,
2005-05-09 20:59:01 +04:00
alloc ) ) {
2004-09-14 17:56:18 +04:00
log_error ( " Unable to allocate "
" temporary LV for pvmove. " ) ;
2004-08-18 02:09:02 +04:00
return 0 ;
}
2005-06-03 18:49:51 +04:00
set_lv_segment_area_lv ( seg , s , lv_mirr , start_le , 0 ) ;
2004-08-18 02:09:02 +04:00
extent_count + = seg - > area_len ;
lv - > status | = LOCKED ;
break ;
}
2003-05-06 16:22:24 +04:00
}
}
log_verbose ( " Moving %u extents of logical volume %s/%s " , extent_count ,
lv - > vg - > name , lv - > name ) ;
return 1 ;
}
2004-05-05 22:35:04 +04:00
/* Remove a temporary mirror */
2003-05-06 16:22:24 +04:00
int remove_pvmove_mirrors ( struct volume_group * vg ,
struct logical_volume * lv_mirr )
{
2005-06-01 20:51:55 +04:00
struct lv_list * lvl ;
2003-05-06 16:22:24 +04:00
struct logical_volume * lv1 ;
struct lv_segment * seg , * mir_seg ;
uint32_t s , c ;
2004-05-05 22:35:04 +04:00
/* Loop through all LVs except the temporary mirror */
2005-06-01 20:51:55 +04:00
list_iterate_items ( lvl , & vg - > lvs ) {
lv1 = lvl - > lv ;
2003-05-06 16:22:24 +04:00
if ( lv1 = = lv_mirr )
continue ;
2004-05-05 22:35:04 +04:00
/* Find all segments that point at the temporary mirror */
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv1 - > segments ) {
2003-05-06 16:22:24 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( seg , s ) ! = AREA_LV | |
seg_lv ( seg , s ) ! = lv_mirr )
2003-05-06 16:22:24 +04:00
continue ;
2004-05-05 22:35:04 +04:00
/* Find the mirror segment pointed at */
2003-05-06 16:22:24 +04:00
if ( ! ( mir_seg = find_seg_by_le ( lv_mirr ,
2005-06-01 20:51:55 +04:00
seg_le ( seg , s ) ) ) ) {
2004-05-05 22:35:04 +04:00
/* FIXME Error message */
2003-05-06 16:22:24 +04:00
log_error ( " No segment found with LE " ) ;
return 0 ;
}
2004-05-05 22:35:04 +04:00
/* Check the segment params are compatible */
/* FIXME Improve error mesg & remove restrcn */
2005-05-09 20:59:01 +04:00
if ( ! seg_is_mirrored ( mir_seg ) | |
2003-05-06 16:22:24 +04:00
! ( mir_seg - > status & PVMOVE ) | |
2005-06-01 20:51:55 +04:00
mir_seg - > le ! = seg_le ( seg , s ) | |
2003-05-06 16:22:24 +04:00
mir_seg - > area_count ! = 2 | |
mir_seg - > area_len ! = seg - > area_len ) {
log_error ( " Incompatible segments " ) ;
return 0 ;
}
2004-05-05 22:35:04 +04:00
/* Replace original segment with newly-mirrored
* area ( or original if reverting )
*/
if ( mir_seg - > extents_copied = =
mir_seg - > area_len )
2003-05-06 16:22:24 +04:00
c = 1 ;
else
c = 0 ;
2005-06-14 21:54:48 +04:00
if ( ! move_lv_segment_area ( seg , s , mir_seg , c ) ) {
2005-05-03 21:28:23 +04:00
stack ;
return 0 ;
}
2003-05-06 16:22:24 +04:00
2006-05-10 01:23:51 +04:00
release_lv_segment_area ( mir_seg , c ? 0 : 1U , mir_seg - > area_len ) ;
2005-06-14 21:54:48 +04:00
/* Replace mirror with error segment */
2004-05-05 01:25:57 +04:00
if ( !
( mir_seg - > segtype =
get_segtype_from_string ( vg - > cmd ,
2005-06-14 21:54:48 +04:00
" error " ) ) ) {
log_error ( " Missing error segtype " ) ;
2004-05-05 01:25:57 +04:00
return 0 ;
}
2005-06-14 21:54:48 +04:00
mir_seg - > area_count = 0 ;
2003-05-06 16:22:24 +04:00
2004-05-05 22:35:04 +04:00
/* FIXME Assumes only one pvmove at a time! */
2003-05-06 16:22:24 +04:00
lv1 - > status & = ~ LOCKED ;
}
}
2004-08-18 02:09:02 +04:00
if ( ! lv_merge_segments ( lv1 ) )
stack ;
2003-05-06 16:22:24 +04:00
}
2005-06-14 21:54:48 +04:00
if ( ! lv_empty ( lv_mirr ) ) {
stack ;
return 0 ;
}
2004-08-18 02:09:02 +04:00
2003-05-06 16:22:24 +04:00
return 1 ;
}
2004-05-05 21:56:20 +04:00
const char * get_pvmove_pvname_from_lv_mirr ( struct logical_volume * lv_mirr )
2003-05-06 16:22:24 +04:00
{
struct lv_segment * seg ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv_mirr - > segments ) {
2005-05-09 20:59:01 +04:00
if ( ! seg_is_mirrored ( seg ) )
2003-05-06 16:22:24 +04:00
continue ;
2005-06-14 21:54:48 +04:00
if ( seg_type ( seg , 0 ) ! = AREA_PV )
2003-05-06 16:22:24 +04:00
continue ;
2005-06-01 20:51:55 +04:00
return dev_name ( seg_dev ( seg , 0 ) ) ;
2003-05-06 16:22:24 +04:00
}
return NULL ;
}
2004-05-05 21:56:20 +04:00
const char * get_pvmove_pvname_from_lv ( struct logical_volume * lv )
2003-05-06 16:22:24 +04:00
{
struct lv_segment * seg ;
uint32_t s ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2003-05-06 16:22:24 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( seg , s ) ! = AREA_LV )
2003-05-06 16:22:24 +04:00
continue ;
2005-06-01 20:51:55 +04:00
return get_pvmove_pvname_from_lv_mirr ( seg_lv ( seg , s ) ) ;
2003-05-06 16:22:24 +04:00
}
}
return NULL ;
}
struct logical_volume * find_pvmove_lv ( struct volume_group * vg ,
2004-05-05 21:56:20 +04:00
struct device * dev ,
uint32_t lv_type )
2003-05-06 16:22:24 +04:00
{
2005-06-01 20:51:55 +04:00
struct lv_list * lvl ;
2003-05-06 16:22:24 +04:00
struct logical_volume * lv ;
struct lv_segment * seg ;
/* Loop through all LVs */
2005-06-01 20:51:55 +04:00
list_iterate_items ( lvl , & vg - > lvs ) {
lv = lvl - > lv ;
2003-05-06 16:22:24 +04:00
2004-05-05 21:56:20 +04:00
if ( ! ( lv - > status & lv_type ) )
2003-05-06 16:22:24 +04:00
continue ;
2004-05-05 22:35:04 +04:00
/* Check segment origins point to pvname */
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv - > segments ) {
2005-06-14 21:54:48 +04:00
if ( seg_type ( seg , 0 ) ! = AREA_PV )
2003-05-06 16:22:24 +04:00
continue ;
2005-06-01 20:51:55 +04:00
if ( seg_dev ( seg , 0 ) ! = dev )
2003-05-06 16:22:24 +04:00
continue ;
return lv ;
}
}
return NULL ;
}
2004-05-05 21:56:20 +04:00
struct logical_volume * find_pvmove_lv_from_pvname ( struct cmd_context * cmd ,
struct volume_group * vg ,
const char * name ,
uint32_t lv_type )
{
struct physical_volume * pv ;
if ( ! ( pv = find_pv_by_name ( cmd , name ) ) ) {
stack ;
return NULL ;
}
return find_pvmove_lv ( vg , pv - > dev , lv_type ) ;
}
2003-05-06 16:22:24 +04:00
struct list * lvs_using_lv ( struct cmd_context * cmd , struct volume_group * vg ,
struct logical_volume * lv )
{
2005-06-01 20:51:55 +04:00
struct list * lvs ;
2003-05-06 16:22:24 +04:00
struct logical_volume * lv1 ;
2005-06-01 20:51:55 +04:00
struct lv_list * lvl , * lvl1 ;
2003-05-06 16:22:24 +04:00
struct lv_segment * seg ;
uint32_t s ;
2005-10-17 03:03:59 +04:00
if ( ! ( lvs = dm_pool_alloc ( cmd - > mem , sizeof ( * lvs ) ) ) ) {
2003-05-06 16:22:24 +04:00
log_error ( " lvs list alloc failed " ) ;
return NULL ;
}
list_init ( lvs ) ;
/* Loop through all LVs except the one supplied */
2005-06-01 20:51:55 +04:00
list_iterate_items ( lvl1 , & vg - > lvs ) {
lv1 = lvl1 - > lv ;
2003-05-06 16:22:24 +04:00
if ( lv1 = = lv )
continue ;
2004-05-05 22:35:04 +04:00
/* Find whether any segment points at the supplied LV */
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv1 - > segments ) {
2003-05-06 16:22:24 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( seg , s ) ! = AREA_LV | |
seg_lv ( seg , s ) ! = lv )
2003-05-06 16:22:24 +04:00
continue ;
2005-10-17 03:03:59 +04:00
if ( ! ( lvl = dm_pool_alloc ( cmd - > mem , sizeof ( * lvl ) ) ) ) {
2003-05-06 16:22:24 +04:00
log_error ( " lv_list alloc failed " ) ;
return NULL ;
}
lvl - > lv = lv1 ;
list_add ( lvs , & lvl - > list ) ;
goto next_lv ;
}
}
next_lv :
2003-07-05 02:34:56 +04:00
;
2003-05-06 16:22:24 +04:00
}
return lvs ;
}
2004-05-05 21:56:20 +04:00
float copy_percent ( struct logical_volume * lv_mirr )
2003-05-06 16:22:24 +04:00
{
uint32_t numerator = 0u , denominator = 0u ;
struct lv_segment * seg ;
2005-06-01 20:51:55 +04:00
list_iterate_items ( seg , & lv_mirr - > segments ) {
2003-05-06 16:22:24 +04:00
denominator + = seg - > area_len ;
2004-05-05 21:56:20 +04:00
2005-05-09 20:59:01 +04:00
if ( seg_is_mirrored ( seg ) )
2004-05-05 21:56:20 +04:00
numerator + = seg - > extents_copied ;
else
numerator + = seg - > area_len ;
2003-05-06 16:22:24 +04:00
}
return denominator ? ( float ) numerator * 100 / denominator : 100.0 ;
}
2005-10-28 01:51:28 +04:00
/*
* Fixup mirror pointers after single - pass segment import
*/
int fixup_imported_mirrors ( struct volume_group * vg )
{
struct lv_list * lvl ;
struct lv_segment * seg ;
uint32_t s ;
list_iterate_items ( lvl , & vg - > lvs ) {
list_iterate_items ( seg , & lvl - > lv - > segments ) {
if ( seg - > segtype ! =
get_segtype_from_string ( vg - > cmd , " mirror " ) )
continue ;
if ( seg - > log_lv )
2005-10-28 16:48:50 +04:00
first_seg ( seg - > log_lv ) - > mirror_seg = seg ;
2005-10-28 01:51:28 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + )
if ( seg_type ( seg , s ) = = AREA_LV )
2005-10-28 16:48:50 +04:00
first_seg ( seg_lv ( seg , s ) ) - > mirror_seg
= seg ;
2005-10-28 01:51:28 +04:00
}
}
2005-10-28 16:48:50 +04:00
return 1 ;
2005-10-28 01:51:28 +04:00
}