2010-05-21 16:40:05 +04:00
/*
* Copyright ( C ) 2009 - 2010 Red Hat , Inc . All rights reserved .
*
* 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 Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser 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
*/
# include "lib.h"
2010-05-21 16:52:01 +04:00
# include "locking.h"
2010-05-21 16:40:05 +04:00
# include "metadata.h"
# include "segtype.h"
2010-05-21 17:34:09 +04:00
# include "toolcontext.h"
2010-05-21 16:40:05 +04:00
/* Add lv as replicator_dev device */
int replicator_dev_add_rimage ( struct replicator_device * rdev ,
struct logical_volume * lv )
{
if ( ! lv | | ! rdev )
return_0 ;
if ( lv_is_rimage ( lv ) ) {
log_error ( " Logical volume %s is already part of other "
" replicator. " , lv - > name ) ;
return 0 ;
}
if ( rdev - > lv ) {
log_error ( " Logical volume %s can not be attached to an "
" already defined replicator device " , lv - > name ) ;
return 0 ;
}
lv_set_hidden ( lv ) ;
lv - > rdevice = rdev ;
rdev - > lv = lv ;
return add_seg_to_segs_using_this_lv ( lv , rdev - > replicator_dev ) ;
}
/* Remove lv from replicator_dev device */
struct logical_volume * replicator_dev_remove_rimage ( struct replicator_device * rdev )
{
struct logical_volume * lv ;
if ( ! rdev | | ! rdev - > lv )
return_NULL ;
lv = rdev - > lv ;
if ( ! remove_seg_from_segs_using_this_lv ( lv , rdev - > replicator_dev ) )
return_NULL ;
/* FIXME: - check for site references */
rdev - > lv = NULL ;
lv - > rdevice = NULL ;
lv_set_visible ( lv ) ;
return lv ;
}
int replicator_dev_add_slog ( struct replicator_device * rdev ,
struct logical_volume * slog )
{
if ( ! slog | | ! rdev )
return_0 ;
if ( rdev - > slog ) {
log_error ( " Replicator device in site %s already has sync log. " ,
rdev - > rsite - > name ) ;
return 0 ;
}
if ( slog - > rdevice ) {
log_error ( " Sync log %s is already used by replicator %s. " ,
slog - > name , slog - > rdevice - > rsite - > replicator - > name ) ;
return 0 ;
}
lv_set_hidden ( slog ) ;
slog - > rdevice = rdev ;
rdev - > slog = slog ;
return add_seg_to_segs_using_this_lv ( slog , rdev - > replicator_dev ) ;
}
struct logical_volume * replicator_dev_remove_slog ( struct replicator_device * rdev )
{
struct logical_volume * lv ;
if ( ! rdev )
return_NULL ;
lv = rdev - > slog ;
if ( ! lv ) {
log_error ( " Replicator device in site %s does not have sync log. " ,
rdev - > rsite - > name ) ;
return NULL ;
}
if ( ! remove_seg_from_segs_using_this_lv ( lv , rdev - > replicator_dev ) )
return_NULL ;
rdev - > slog = NULL ;
lv - > rdevice = NULL ;
lv_set_visible ( lv ) ;
return lv ;
}
int replicator_add_replicator_dev ( struct logical_volume * replicator_lv ,
struct lv_segment * replicator_dev_seg )
{
if ( ! replicator_lv )
return_0 ;
if ( ! ( replicator_lv - > status & REPLICATOR ) ) {
dm_list_init ( & replicator_lv - > rsites ) ;
lv_set_hidden ( replicator_lv ) ;
replicator_lv - > status | = REPLICATOR ;
}
if ( ! replicator_dev_seg )
return 1 ;
if ( replicator_dev_seg - > replicator ) {
log_error ( " Replicator device %s is already part of replicator. " ,
replicator_dev_seg - > lv - > name ) ;
return 0 ;
}
replicator_dev_seg - > replicator = replicator_lv ;
return add_seg_to_segs_using_this_lv ( replicator_lv , replicator_dev_seg ) ;
}
/**
* Returns rimage ? ? lv upon succeful detach of device
* entire LV entry should be removed by this crootall ? ?
*/
struct logical_volume * replicator_remove_replicator_dev ( struct lv_segment * replicator_dev_seg )
{
struct logical_volume * lv = NULL ;
log_error ( " FIXME: not implemented. " ) ;
#if 0
/* FIXME: - this is going to be complex.... */
if ( ! replicator_dev_seg )
return_NULL ;
/* if slog or rimage - exit */
if ( ! remove_seg_from_segs_using_this_lv ( lv , replicator_seg ) )
return_NULL ;
replicator_seg - > rlog_lv = NULL ;
lv - > status & = ~ REPLICATOR_LOG ;
lv_set_visible ( lv ) ;
# endif
return lv ;
}
int replicator_add_rlog ( struct lv_segment * replicator_seg ,
struct logical_volume * rlog_lv )
{
if ( ! rlog_lv )
return_0 ;
if ( rlog_lv - > status & REPLICATOR_LOG ) {
log_error ( " Rlog device %s is already used. " , rlog_lv - > name ) ;
return 0 ;
}
lv_set_hidden ( rlog_lv ) ;
rlog_lv - > status | = REPLICATOR_LOG ;
replicator_seg - > rlog_lv = rlog_lv ;
return add_seg_to_segs_using_this_lv ( rlog_lv , replicator_seg ) ;
}
struct logical_volume * replicator_remove_rlog ( struct lv_segment * replicator_seg )
{
struct logical_volume * lv ;
if ( ! replicator_seg )
return_0 ;
if ( ! ( lv = replicator_seg - > rlog_lv ) ) {
log_error ( " Replog segment %s does not have rlog. " ,
replicator_seg - > lv - > name ) ;
return NULL ;
}
if ( ! remove_seg_from_segs_using_this_lv ( lv , replicator_seg ) )
return_NULL ;
replicator_seg - > rlog_lv = NULL ;
lv - > status & = ~ REPLICATOR_LOG ;
lv_set_visible ( lv ) ;
return lv ;
}
#if 0
/*
* Create new LV to pretend the original LV
* this target will have a ' replicator ' segment
*/
int lv_add_replicator ( struct logical_volume * origin , const char * rep_suffix )
{
struct logical_volume * rep_lv ;
char * name ;
size_t slen ;
if ( ! ( name = strstr ( origin - > name , rep_suffix ) ) ) {
log_error ( " Failed to find replicator suffix %s in LV name %s " ,
rep_suffix , origin - > name ) ;
return 0 ;
}
slen = ( size_t ) ( name - origin - > name ) ;
name = alloca ( slen + 1 ) ;
memcpy ( name , origin - > name , slen ) ;
name [ slen ] = 0 ;
if ( ( rep_lv = find_lv ( origin - > vg , name ) ) ) {
rep_lv - > status | = VIRTUAL ;
return 1 ;
}
if ( ! ( rep_lv = lv_create_empty ( name , & origin - > lvid ,
LVM_READ | LVM_WRITE | VISIBLE_LV ,
ALLOC_INHERIT , origin - > vg ) ) )
return_0 ;
if ( ! lv_add_virtual_segment ( rep_lv , 0 , origin - > le_count ,
get_segtype_from_string ( origin - > vg - > cmd ,
" error " ) ) )
return_0 ;
rep_lv - > status | = VIRTUAL ;
return 1 ;
}
int lv_remove_replicator ( struct logical_volume * lv )
{
return 1 ;
}
# endif
2010-05-21 16:43:02 +04:00
/*
* Check all replicator structures :
* only non - clustered VG for Replicator
* only one segment in replicator LV
* site has correct combination of operation_mode parameters
* site and related devices have correct index numbers
* duplicate site names , site indexes , device names , device indexes
*/
int check_replicator_segment ( const struct lv_segment * rseg )
{
struct replicator_site * rsite , * rsiteb ;
struct replicator_device * rdev , * rdevb ;
struct logical_volume * lv = rseg - > lv ;
int r = 1 ;
if ( vg_is_clustered ( lv - > vg ) ) {
log_error ( " Volume Group %s of replicator %s is clustered " ,
lv - > vg - > name , lv - > name ) ;
return 0 ;
}
if ( dm_list_size ( & lv - > segments ) ! = 1 ) {
log_error ( " Replicator %s segment size %d != 1 " ,
lv - > name , dm_list_size ( & lv - > segments ) ) ;
return 0 ;
}
dm_list_iterate_items ( rsite , & lv - > rsites ) {
if ( rsite - > op_mode = = DM_REPLICATOR_SYNC ) {
if ( rsite - > fall_behind_timeout ) {
log_error ( " Defined fall_behind_timeout= "
" %d for sync replicator %s/%s. " ,
rsite - > fall_behind_timeout , lv - > name ,
rsite - > name ) ;
r = 0 ;
}
if ( rsite - > fall_behind_ios ) {
log_error ( " Defined fall_behind_ios= "
" %d for sync replicator %s/%s. " ,
rsite - > fall_behind_ios , lv - > name , rsite - > name ) ;
r = 0 ;
}
if ( rsite - > fall_behind_data ) {
log_error ( " Defined fall_behind_data= "
" % " PRIu64 " for sync replicator %s/%s. " ,
rsite - > fall_behind_data , lv - > name , rsite - > name ) ;
r = 0 ;
}
} else {
if ( rsite - > fall_behind_timeout & & rsite - > fall_behind_ios ) {
log_error ( " Defined fall_behind_timeout and "
" fall_behind_ios for async replicator %s/%s. " ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
if ( rsite - > fall_behind_timeout & & rsite - > fall_behind_data ) {
log_error ( " Defined fall_behind_timeout and "
" fall_behind_data for async replicator %s/%s. " ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
if ( rsite - > fall_behind_ios & & rsite - > fall_behind_data ) {
log_error ( " Defined fall_behind_ios and "
" fall_behind_data for async replicator %s/%s. " ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
if ( ! rsite - > fall_behind_ios & &
! rsite - > fall_behind_data & &
! rsite - > fall_behind_timeout ) {
log_error ( " fall_behind_timeout, "
" fall_behind_ios and fall_behind_data are "
" undefined for async replicator %s/%s. " ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
}
dm_list_iterate_items ( rsiteb , & lv - > rsites ) {
if ( rsite = = rsiteb )
break ;
if ( strcasecmp ( rsite - > name , rsiteb - > name ) = = 0 ) {
log_error ( " Duplicate site name "
" %s detected for replicator %s. " ,
rsite - > name , lv - > name ) ;
r = 0 ;
}
if ( ( rsite - > vg_name & & rsiteb - > vg_name & &
strcasecmp ( rsite - > vg_name , rsiteb - > vg_name ) = = 0 ) | |
( ! rsite - > vg_name & & ! rsiteb - > vg_name ) ) {
log_error ( " Duplicate VG name "
" %s detected for replicator %s. " ,
( rsite - > vg_name ) ? rsite - > vg_name : " <local> " ,
lv - > name ) ;
r = 0 ;
}
if ( rsite - > site_index = = rsiteb - > site_index ) {
log_error ( " Duplicate site index %d detected "
" for replicator site %s/%s. " ,
rsite - > site_index , lv - > name ,
rsite - > name ) ;
r = 0 ;
}
if ( rsite - > site_index > rseg - > rsite_index_highest ) {
log_error ( " Site index %d > %d (too high) "
" for replicator site %s/%s. " ,
rsite - > site_index ,
rseg - > rsite_index_highest ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
}
dm_list_iterate_items ( rdev , & rsite - > rdevices ) {
dm_list_iterate_items ( rdevb , & rsite - > rdevices ) {
if ( rdev = = rdevb )
break ;
if ( rdev - > slog & & ( rdev - > slog = = rdevb - > slog ) ) {
log_error ( " Duplicate sync log %s "
" detected for replicator %s. " ,
rdev - > slog - > name , lv - > name ) ;
r = 0 ;
}
if ( strcasecmp ( rdev - > name , rdevb - > name ) = = 0 ) {
log_error ( " Duplicate device name %s "
" detected for replicator %s. " ,
rdev - > name , lv - > name ) ;
r = 0 ;
}
if ( rdev - > device_index = = rdevb - > device_index ) {
log_error ( " Duplicate device index % "
PRId64 " detected for "
" replicator site %s/%s. " ,
rdev - > device_index ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
if ( rdev - > device_index > rseg - > rdevice_index_highest ) {
log_error ( " Device index % " PRIu64
" > % " PRIu64 " (too high) "
" for replicator site %s/%s. " ,
rdev - > device_index ,
rseg - > rdevice_index_highest ,
lv - > name , rsite - > name ) ;
r = 0 ;
}
}
}
}
return r ;
}
2010-05-21 16:40:05 +04:00
/**
* Is this segment part of active replicator
*/
int lv_is_active_replicator_dev ( const struct logical_volume * lv )
{
return ( ( lv - > status & REPLICATOR ) & &
lv - > rdevice & &
lv - > rdevice - > rsite & &
lv - > rdevice - > rsite - > state = = REPLICATOR_STATE_ACTIVE ) ;
}
/**
* Is this LV replicator control device
*/
int lv_is_replicator ( const struct logical_volume * lv )
{
return ( ( lv - > status & REPLICATOR ) & &
! dm_list_empty ( & lv - > segments ) & &
seg_is_replicator ( first_seg ( lv ) ) ) ;
}
/**
* Is this LV replicator device
*/
int lv_is_replicator_dev ( const struct logical_volume * lv )
{
return ( ( lv - > status & REPLICATOR ) & &
! dm_list_empty ( & lv - > segments ) & &
seg_is_replicator_dev ( first_seg ( lv ) ) ) ;
}
/**
* Is this LV replicated origin lv
*/
int lv_is_rimage ( const struct logical_volume * lv )
{
return ( lv - > rdevice & & lv - > rdevice - > lv = = lv ) ;
}
/**
* Is this LV sync log
*/
int lv_is_slog ( const struct logical_volume * lv )
{
return ( lv - > rdevice & & lv - > rdevice - > slog = = lv ) ;
}
/**
* Returns first replicator - dev in site in case the LV is replicator - dev ,
* NULL otherwise
*/
struct logical_volume * first_replicator_dev ( const struct logical_volume * lv )
{
struct replicator_device * rdev ;
struct replicator_site * rsite ;
if ( lv_is_replicator_dev ( lv ) )
dm_list_iterate_items ( rsite , & first_seg ( lv ) - > replicator - > rsites ) {
dm_list_iterate_items ( rdev , & rsite - > rdevices )
return rdev - > replicator_dev - > lv ;
break ;
}
return NULL ;
}
2010-05-21 16:52:01 +04:00
/**
* Add VG open parameters to sorted cmd_vg list .
*
* Maintain the alphabeticaly ordered list , avoid duplications .
*
* \ return Returns newly created or already present cmd_vg entry ,
* or NULL in error case .
*/
struct cmd_vg * cmd_vg_add ( struct dm_pool * mem , struct dm_list * cmd_vgs ,
const char * vg_name , const char * vgid ,
uint32_t flags )
{
struct cmd_vg * cvl , * ins ;
if ( ! vg_name & & ! vgid ) {
log_error ( " Either vg_name or vgid must be set. " ) ;
return NULL ;
}
/* Is it already in the list ? */
if ( ( cvl = cmd_vg_lookup ( cmd_vgs , vg_name , vgid ) ) )
return cvl ;
if ( ! ( cvl = dm_pool_zalloc ( mem , sizeof ( * cvl ) ) ) ) {
log_error ( " Allocation of cmd_vg failed. " ) ;
return NULL ;
}
if ( vg_name & & ! ( cvl - > vg_name = dm_pool_strdup ( mem , vg_name ) ) ) {
dm_pool_free ( mem , cvl ) ;
log_error ( " Allocation of vg_name failed. " ) ;
return NULL ;
}
if ( vgid & & ! ( cvl - > vgid = dm_pool_strdup ( mem , vgid ) ) ) {
dm_pool_free ( mem , cvl ) ;
log_error ( " Allocation of vgid failed. " ) ;
return NULL ;
}
cvl - > flags = flags ;
if ( vg_name )
dm_list_iterate_items ( ins , cmd_vgs )
if ( strcmp ( vg_name , ins - > vg_name ) < 0 ) {
cmd_vgs = & ins - > list ; /* new position */
break ;
}
dm_list_add ( cmd_vgs , & cvl - > list ) ;
return cvl ;
}
/**
* Find cmd_vg with given vg_name in cmd_vgs list .
*
* \ param cmd_vgs List of cmd_vg entries .
*
* \ param vg_name Name of VG to be found .
* \ param vgid UUID of VG to be found .
*
* \ return Returns cmd_vg entry if vg_name or vgid is found ,
* NULL otherwise .
*/
struct cmd_vg * cmd_vg_lookup ( struct dm_list * cmd_vgs ,
const char * vg_name , const char * vgid )
{
struct cmd_vg * cvl ;
dm_list_iterate_items ( cvl , cmd_vgs )
if ( ( vgid & & cvl - > vgid & & ! strcmp ( vgid , cvl - > vgid ) ) | |
( vg_name & & cvl - > vg_name & & ! strcmp ( vg_name , cvl - > vg_name ) ) )
return cvl ;
return NULL ;
}
/**
* Read and lock multiple VGs stored in cmd_vgs list alphabeticaly .
* On the success list head pointer is set to VGs ' cmd_vgs .
* ( supports FAILED_INCONSISTENT )
*
* \ param cmd_vg Contains list of cmd_vg entries .
*
* \ return Returns 1 if all VG in cmd_vgs list are correctly
* openned and locked , 0 otherwise .
*/
int cmd_vg_read ( struct cmd_context * cmd , struct dm_list * cmd_vgs )
{
struct cmd_vg * cvl ;
/* Iterate through alphabeticaly ordered cmd_vg list */
dm_list_iterate_items ( cvl , cmd_vgs ) {
cvl - > vg = vg_read ( cmd , cvl - > vg_name , cvl - > vgid , cvl - > flags ) ;
if ( vg_read_error ( cvl - > vg ) ) {
2013-01-08 02:30:29 +04:00
log_debug_metadata ( " Failed to vg_read %s " , cvl - > vg_name ) ;
2010-05-21 16:52:01 +04:00
return 0 ;
}
cvl - > vg - > cmd_vgs = cmd_vgs ; /* Make it usable in VG */
}
return 1 ;
}
/**
* Release opened and locked VGs from list .
*
* \ param cmd_vgs Contains list of cmd_vg entries .
*/
2010-12-08 23:50:48 +03:00
void free_cmd_vgs ( struct dm_list * cmd_vgs )
2010-05-21 16:52:01 +04:00
{
struct cmd_vg * cvl ;
/* Backward iterate cmd_vg list */
dm_list_iterate_back_items ( cvl , cmd_vgs ) {
if ( vg_read_error ( cvl - > vg ) )
2011-08-11 00:25:29 +04:00
release_vg ( cvl - > vg ) ;
2010-05-21 16:52:01 +04:00
else
2011-08-11 00:25:29 +04:00
unlock_and_release_vg ( cvl - > vg - > cmd , cvl - > vg , cvl - > vg_name ) ;
2010-05-21 16:52:01 +04:00
cvl - > vg = NULL ;
}
}
2010-05-21 16:55:25 +04:00
/**
* Find all needed remote VGs for processing given LV .
* Missing VGs are added to VG ' s cmd_vg list and flag cmd_missing_vgs is set .
*/
int find_replicator_vgs ( struct logical_volume * lv )
{
struct replicator_site * rsite ;
int ret = 1 ;
if ( ! lv_is_replicator_dev ( lv ) )
return 1 ;
dm_list_iterate_items ( rsite , & first_seg ( lv ) - > replicator - > rsites ) {
if ( ! rsite - > vg_name | | ! lv - > vg - > cmd_vgs | |
cmd_vg_lookup ( lv - > vg - > cmd_vgs , rsite - > vg_name , NULL ) )
continue ;
ret = 0 ;
/* Using cmd memory pool for cmd_vg list allocation */
if ( ! cmd_vg_add ( lv - > vg - > cmd - > mem , lv - > vg - > cmd_vgs ,
rsite - > vg_name , NULL , 0 ) ) {
lv - > vg - > cmd_missing_vgs = 0 ; /* do not retry */
stack ;
break ;
}
2013-01-08 02:30:29 +04:00
log_debug_metadata ( " VG: %s added as missing. " , rsite - > vg_name ) ;
2010-05-21 16:55:25 +04:00
lv - > vg - > cmd_missing_vgs + + ;
}
return ret ;
}
2010-05-21 18:07:16 +04:00
/**
* Read all remote VGs from lv ' s replicator sites .
* Function is used in activation context and needs all VGs already locked .
*/
int lv_read_replicator_vgs ( struct logical_volume * lv )
{
struct replicator_device * rdev ;
struct replicator_site * rsite ;
struct volume_group * vg ;
if ( ! lv_is_replicator_dev ( lv ) )
return 1 ;
dm_list_iterate_items ( rsite , & first_seg ( lv ) - > replicator - > rsites ) {
if ( ! rsite - > vg_name )
continue ;
vg = vg_read ( lv - > vg - > cmd , rsite - > vg_name , 0 , 0 ) ; // READ_WITHOUT_LOCK
if ( vg_read_error ( vg ) ) {
log_error ( " Unable to read volume group %s " ,
rsite - > vg_name ) ;
goto bad ;
}
rsite - > vg = vg ;
/* FIXME: handling missing LVs needs to be better */
dm_list_iterate_items ( rdev , & rsite - > rdevices )
if ( ! ( rdev - > lv = find_lv ( vg , rdev - > name ) ) ) {
log_error ( " Unable to find %s in volume group %s " ,
rdev - > name , rsite - > vg_name ) ;
goto bad ;
}
}
return 1 ;
bad :
lv_release_replicator_vgs ( lv ) ;
return 0 ;
}
/**
* Release all VG resources taken by lv ' s replicator sites .
* Function is used in activation context and needs all VGs already locked .
*/
void lv_release_replicator_vgs ( struct logical_volume * lv )
{
struct replicator_site * rsite ;
if ( ! lv_is_replicator_dev ( lv ) )
return ;
dm_list_iterate_back_items ( rsite , & first_seg ( lv ) - > replicator - > rsites )
if ( rsite - > vg_name & & rsite - > vg ) {
2011-08-11 00:25:29 +04:00
release_vg ( rsite - > vg ) ;
2010-05-21 18:07:16 +04:00
rsite - > vg = NULL ;
}
}