2003-04-30 19:28:17 +04:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2003 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
2003-04-30 19:28:17 +04:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
2003-04-30 19:28:17 +04:00
*
2004-03-30 23:35:44 +04:00
* 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 .
2003-04-30 19:28:17 +04:00
*
* You should have received a copy of the GNU General Public License
2004-03-30 23:35:44 +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
2003-04-30 19:28:17 +04:00
*/
# include "tools.h"
2004-05-05 21:56:20 +04:00
# include "polldaemon.h"
2003-04-30 19:28:17 +04:00
/* Allow /dev/vgname/lvname, vgname/lvname or lvname */
static const char * _extract_lvname ( struct cmd_context * cmd , const char * vgname ,
const char * arg )
{
const char * lvname ;
/* Is an lvname supplied directly? */
if ( ! strchr ( arg , ' / ' ) )
return arg ;
lvname = arg ;
if ( ! strncmp ( lvname , cmd - > dev_dir , strlen ( cmd - > dev_dir ) ) )
lvname + = strlen ( cmd - > dev_dir ) ;
while ( * lvname = = ' / ' )
lvname + + ;
if ( ! strchr ( lvname , ' / ' ) ) {
log_error ( " --name takes a logical volume name " ) ;
return NULL ;
}
if ( strncmp ( vgname , lvname , strlen ( vgname ) ) | |
( lvname + = strlen ( vgname ) , * lvname ! = ' / ' ) ) {
log_error ( " Named LV and old PV must be in the same VG " ) ;
return NULL ;
}
while ( * lvname = = ' / ' )
lvname + + ;
if ( ! * lvname ) {
log_error ( " Incomplete LV name supplied with --name " ) ;
return NULL ;
}
return lvname ;
}
2003-05-06 16:20:11 +04:00
static struct volume_group * _get_vg ( struct cmd_context * cmd , const char * vgname )
{
int consistent = 1 ;
struct volume_group * vg ;
2003-04-30 19:28:17 +04:00
2004-03-31 22:41:39 +04:00
dev_close_all ( ) ;
2003-05-06 16:20:11 +04:00
if ( ! lock_vol ( cmd , vgname , LCK_VG_WRITE ) ) {
log_error ( " Can't get lock for %s " , vgname ) ;
return NULL ;
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
if ( ! ( vg = vg_read ( cmd , vgname , & consistent ) ) | | ! consistent ) {
log_error ( " Volume group \" %s \" doesn't exist " , vgname ) ;
unlock_vg ( cmd , vgname ) ;
return NULL ;
2003-04-30 19:28:17 +04:00
}
if ( vg - > status & EXPORTED_VG ) {
2003-05-06 16:20:11 +04:00
log_error ( " Volume group \" %s \" is exported " , vgname ) ;
unlock_vg ( cmd , vgname ) ;
return NULL ;
2003-04-30 19:28:17 +04:00
}
if ( ! ( vg - > status & LVM_WRITE ) ) {
2003-05-06 16:20:11 +04:00
log_error ( " Volume group \" %s \" is read-only " , vgname ) ;
unlock_vg ( cmd , vgname ) ;
return NULL ;
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
return vg ;
}
/* Create list of PVs for allocation of replacement extents */
static struct list * _get_allocatable_pvs ( struct cmd_context * cmd , int argc ,
char * * argv , struct volume_group * vg ,
struct physical_volume * pv )
{
struct list * allocatable_pvs , * pvht , * pvh ;
struct pv_list * pvl ;
2003-04-30 19:28:17 +04:00
if ( argc ) {
if ( ! ( allocatable_pvs = create_pv_list ( cmd - > mem , vg , argc ,
argv ) ) ) {
stack ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
} else {
if ( ! ( allocatable_pvs = clone_pv_list ( cmd - > mem , & vg - > pvs ) ) ) {
stack ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
}
/* Don't allocate onto the PV we're clearing! */
/* Remove PV if full */
list_iterate_safe ( pvh , pvht , allocatable_pvs ) {
pvl = list_item ( pvh , struct pv_list ) ;
if ( ( pvl - > pv - > dev = = pv - > dev ) | |
( pvl - > pv - > pe_count = = pvl - > pv - > pe_alloc_count ) )
2003-08-18 17:52:43 +04:00
list_del ( & pvl - > list ) ;
2003-04-30 19:28:17 +04:00
}
if ( list_empty ( allocatable_pvs ) ) {
log_error ( " No extents available for allocation " ) ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
return allocatable_pvs ;
}
2004-05-05 21:56:20 +04:00
/* Create new LV with mirror segments for the required copies */
2003-05-06 16:20:11 +04:00
static struct logical_volume * _set_up_pvmove_lv ( struct cmd_context * cmd ,
struct volume_group * vg ,
struct physical_volume * pv ,
const char * lv_name ,
struct list * allocatable_pvs ,
struct list * * lvs_changed )
{
struct logical_volume * lv_mirr , * lv ;
2003-10-16 00:02:46 +04:00
struct lv_list * lvl ;
2003-04-30 19:28:17 +04:00
2003-07-05 02:34:56 +04:00
/* FIXME Cope with non-contiguous => splitting existing segments */
2003-05-06 16:20:11 +04:00
if ( ! ( lv_mirr = lv_create_empty ( vg - > fid , NULL , " pvmove%d " ,
LVM_READ | LVM_WRITE ,
2003-04-30 19:28:17 +04:00
ALLOC_CONTIGUOUS , vg ) ) ) {
log_error ( " Creation of temporary pvmove LV failed " ) ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
lv_mirr - > status | = ( PVMOVE | LOCKED ) ;
if ( ! ( * lvs_changed = pool_alloc ( cmd - > mem , sizeof ( * * lvs_changed ) ) ) ) {
log_error ( " lvs_changed list struct allocation failed " ) ;
return NULL ;
}
list_init ( * lvs_changed ) ;
2003-04-30 19:28:17 +04:00
2003-07-05 02:34:56 +04:00
/* Find segments to be moved and set up mirrors */
2003-10-16 00:02:46 +04:00
list_iterate_items ( lvl , & vg - > lvs ) {
lv = lvl - > lv ;
2003-04-30 19:28:17 +04:00
if ( ( lv = = lv_mirr ) | | ( lv_name & & strcmp ( lv - > name , lv_name ) ) )
continue ;
2003-04-30 19:58:09 +04:00
if ( lv_is_origin ( lv ) | | lv_is_cow ( lv ) ) {
log_print ( " Skipping snapshot-related LV %s " , lv - > name ) ;
continue ;
}
2003-05-06 16:20:11 +04:00
if ( lv - > status & LOCKED ) {
log_print ( " Skipping locked LV %s " , lv - > name ) ;
continue ;
}
2003-04-30 19:28:17 +04:00
if ( ! insert_pvmove_mirrors ( cmd , lv_mirr , pv , lv ,
2003-05-06 16:20:11 +04:00
allocatable_pvs , * lvs_changed ) ) {
2003-04-30 19:28:17 +04:00
stack ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
}
if ( ! lv_mirr - > le_count ) {
log_error ( " No data to move for %s " , vg - > name ) ;
2003-05-06 16:20:11 +04:00
return NULL ;
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
return lv_mirr ;
}
static int _update_metadata ( struct cmd_context * cmd , struct volume_group * vg ,
struct logical_volume * lv_mirr ,
struct list * lvs_changed , int first_time )
{
2003-07-05 02:34:56 +04:00
log_verbose ( " Updating volume group metadata " ) ;
if ( ! vg_write ( vg ) ) {
log_error ( " ABORTING: Volume group metadata update failed. " ) ;
return 0 ;
}
backup ( vg ) ;
2004-05-05 16:03:07 +04:00
if ( ! suspend_lvs ( cmd , lvs_changed ) ) {
2003-04-30 19:28:17 +04:00
stack ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
if ( ! first_time ) {
2004-05-05 16:03:07 +04:00
if ( ! suspend_lv ( cmd , lv_mirr - > lvid . s ) ) {
2003-05-06 16:20:11 +04:00
stack ;
2004-05-05 16:03:07 +04:00
resume_lvs ( cmd , lvs_changed ) ;
2003-07-05 02:34:56 +04:00
vg_revert ( vg ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
2003-04-30 19:28:17 +04:00
}
2003-07-05 02:34:56 +04:00
if ( ! vg_commit ( vg ) ) {
2003-05-06 16:20:11 +04:00
log_error ( " ABORTING: Volume group metadata update failed. " ) ;
if ( ! first_time )
2004-05-05 16:03:07 +04:00
resume_lv ( cmd , lv_mirr - > lvid . s ) ;
resume_lvs ( cmd , lvs_changed ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
if ( first_time ) {
2004-05-05 16:03:07 +04:00
if ( ! activate_lv ( cmd , lv_mirr - > lvid . s ) ) {
2004-05-05 21:56:20 +04:00
log_error ( " ABORTING: Temporary mirror activation "
" failed. Run pvmove --abort. " ) ;
/* FIXME Resume using *original* metadata here! */
2004-05-05 16:03:07 +04:00
resume_lvs ( cmd , lvs_changed ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
2004-05-05 16:03:07 +04:00
} else if ( ! resume_lv ( cmd , lv_mirr - > lvid . s ) ) {
log_error ( " Unable to reactivate logical volume \" %s \" " ,
2003-05-06 16:20:11 +04:00
lv_mirr - > name ) ;
2004-05-05 16:03:07 +04:00
resume_lvs ( cmd , lvs_changed ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
2004-05-05 16:03:07 +04:00
if ( ! resume_lvs ( cmd , lvs_changed ) ) {
log_error ( " Unable to resume logical volumes " ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
return 1 ;
}
static int _set_up_pvmove ( struct cmd_context * cmd , const char * pv_name ,
int argc , char * * argv )
{
const char * lv_name = NULL ;
struct volume_group * vg ;
struct list * allocatable_pvs ;
struct list * lvs_changed ;
struct physical_volume * pv ;
struct logical_volume * lv_mirr ;
int first_time = 1 ;
2003-07-05 02:34:56 +04:00
/* Find PV (in VG) */
2004-05-05 15:04:28 +04:00
if ( ! ( pv = find_pv_by_name ( cmd , pv_name ) ) ) {
2003-05-06 16:20:11 +04:00
stack ;
return EINVALID_CMD_LINE ;
}
if ( arg_count ( cmd , name_ARG ) ) {
if ( ! ( lv_name = _extract_lvname ( cmd , pv - > vg_name ,
arg_value ( cmd , name_ARG ) ) ) ) {
stack ;
return EINVALID_CMD_LINE ;
}
}
2003-07-05 02:34:56 +04:00
/* Read VG */
2003-05-06 16:20:11 +04:00
log_verbose ( " Finding volume group \" %s \" " , pv - > vg_name ) ;
if ( ! ( vg = _get_vg ( cmd , pv - > vg_name ) ) ) {
2003-04-30 19:28:17 +04:00
stack ;
return ECMD_FAILED ;
}
2004-05-05 21:56:20 +04:00
if ( ( lv_mirr = find_pvmove_lv ( vg , pv - > dev , PVMOVE ) ) ) {
2003-05-06 16:20:11 +04:00
log_print ( " Detected pvmove in progress for %s " , pv_name ) ;
if ( argc | | lv_name )
log_error ( " Ignoring remaining command line arguments " ) ;
2003-04-30 19:28:17 +04:00
2003-05-06 16:20:11 +04:00
if ( ! ( lvs_changed = lvs_using_lv ( cmd , vg , lv_mirr ) ) ) {
log_error
( " ABORTING: Failed to generate list of moving LVs " ) ;
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
2003-04-30 19:28:17 +04:00
2003-07-05 02:34:56 +04:00
/* Ensure mirror LV is active */
2004-05-05 16:03:07 +04:00
if ( ! activate_lv ( cmd , lv_mirr - > lvid . s ) ) {
2003-05-06 16:20:11 +04:00
log_error
( " ABORTING: Temporary mirror activation failed. " ) ;
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
first_time = 0 ;
} else {
if ( ! ( allocatable_pvs = _get_allocatable_pvs ( cmd , argc , argv ,
vg , pv ) ) ) {
stack ;
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
if ( ! archive ( vg ) ) {
unlock_vg ( cmd , pv - > vg_name ) ;
stack ;
return ECMD_FAILED ;
}
if ( ! ( lv_mirr = _set_up_pvmove_lv ( cmd , vg , pv , lv_name ,
allocatable_pvs ,
& lvs_changed ) ) ) {
stack ;
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
}
2004-05-05 16:03:07 +04:00
if ( ! activate_lvs_excl ( cmd , lvs_changed ) ) {
2003-05-06 16:20:11 +04:00
stack ;
2003-04-30 19:28:17 +04:00
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
2003-05-06 16:20:11 +04:00
if ( first_time ) {
if ( ! _update_metadata
( cmd , vg , lv_mirr , lvs_changed , first_time ) ) {
stack ;
unlock_vg ( cmd , pv - > vg_name ) ;
return ECMD_FAILED ;
}
2003-04-30 19:28:17 +04:00
}
2003-05-06 16:20:11 +04:00
unlock_vg ( cmd , pv - > vg_name ) ;
2003-04-30 19:28:17 +04:00
2003-05-06 16:20:11 +04:00
return ECMD_PROCESSED ;
}
2003-04-30 19:28:17 +04:00
2003-05-06 16:20:11 +04:00
static int _finish_pvmove ( struct cmd_context * cmd , struct volume_group * vg ,
struct logical_volume * lv_mirr ,
struct list * lvs_changed )
{
int r = 1 ;
2003-07-05 02:34:56 +04:00
if ( ! remove_pvmove_mirrors ( vg , lv_mirr ) ) {
log_error ( " ABORTING: Removal of temporary mirror failed " ) ;
return 0 ;
}
if ( ! vg_write ( vg ) ) {
log_error ( " ABORTING: Failed to write new data locations "
" to disk. " ) ;
return 0 ;
}
2004-05-05 16:03:07 +04:00
if ( ! suspend_lvs ( cmd , lvs_changed ) ) {
2003-05-06 16:20:11 +04:00
log_error ( " Locking LVs to remove temporary mirror failed " ) ;
r = 0 ;
2003-04-30 19:28:17 +04:00
}
2004-05-05 16:03:07 +04:00
if ( ! suspend_lv ( cmd , lv_mirr - > lvid . s ) ) {
2003-05-06 16:20:11 +04:00
log_error ( " Suspension of temporary mirror LV failed " ) ;
r = 0 ;
}
2003-04-30 19:28:17 +04:00
2003-07-05 02:34:56 +04:00
if ( ! vg_commit ( vg ) ) {
2003-05-06 16:20:11 +04:00
log_error ( " ABORTING: Failed to write new data locations "
" to disk. " ) ;
2003-07-05 02:34:56 +04:00
vg_revert ( vg ) ;
2004-05-05 16:03:07 +04:00
resume_lv ( cmd , lv_mirr - > lvid . s ) ;
resume_lvs ( cmd , lvs_changed ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
2003-04-30 19:28:17 +04:00
}
2004-05-05 16:03:07 +04:00
if ( ! resume_lv ( cmd , lv_mirr - > lvid . s ) ) {
log_error ( " Unable to reactivate logical volume \" %s \" " ,
2003-04-30 19:28:17 +04:00
lv_mirr - > name ) ;
2003-05-06 16:20:11 +04:00
r = 0 ;
2003-04-30 19:28:17 +04:00
}
2004-05-05 16:03:07 +04:00
resume_lvs ( cmd , lvs_changed ) ;
2003-11-20 19:22:04 +03:00
2004-05-05 16:03:07 +04:00
if ( ! deactivate_lv ( cmd , lv_mirr - > lvid . s ) ) {
2003-04-30 19:28:17 +04:00
log_error ( " ABORTING: Unable to deactivate temporary logical "
" volume \" %s \" " , lv_mirr - > name ) ;
2003-05-06 16:20:11 +04:00
r = 0 ;
2003-04-30 19:28:17 +04:00
}
2003-11-20 19:22:04 +03:00
log_verbose ( " Removing temporary pvmove LV " ) ;
2003-04-30 19:28:17 +04:00
if ( ! lv_remove ( vg , lv_mirr ) ) {
log_error ( " ABORTING: Removal of temporary pvmove LV failed " ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
2003-04-30 19:28:17 +04:00
}
log_verbose ( " Writing out final volume group after pvmove " ) ;
2003-07-05 02:34:56 +04:00
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) ) {
2003-04-30 19:28:17 +04:00
log_error ( " ABORTING: Failed to write new data locations "
" to disk. " ) ;
2003-05-06 16:20:11 +04:00
return 0 ;
}
2003-07-05 02:34:56 +04:00
/* FIXME backup positioning */
2003-05-06 16:20:11 +04:00
backup ( vg ) ;
return r ;
}
2004-05-05 21:56:20 +04:00
static struct volume_group * _get_move_vg ( struct cmd_context * cmd ,
const char * name )
2003-05-06 16:20:11 +04:00
{
struct physical_volume * pv ;
2004-05-05 21:56:20 +04:00
/* Reread all metadata in case it got changed */
if ( ! ( pv = find_pv_by_name ( cmd , name ) ) ) {
log_error ( " ABORTING: Can't reread PV %s " , name ) ;
/* What more could we do here? */
return NULL ;
2003-05-06 16:20:11 +04:00
}
2003-04-30 19:28:17 +04:00
2004-05-05 21:56:20 +04:00
return _get_vg ( cmd , pv - > vg_name ) ;
2003-05-06 16:20:11 +04:00
}
2004-05-05 21:56:20 +04:00
static struct poll_functions _pvmove_fns = {
get_copy_name_from_lv : get_pvmove_pvname_from_lv_mirr ,
get_copy_vg : _get_move_vg ,
get_copy_lv : find_pvmove_lv_from_pvname ,
update_metadata : _update_metadata ,
finish_copy : _finish_pvmove ,
} ;
2003-05-06 16:20:11 +04:00
int pvmove_poll ( struct cmd_context * cmd , const char * pv_name ,
unsigned background )
{
2004-05-05 21:56:20 +04:00
return poll_daemon ( cmd , pv_name , background , PVMOVE , & _pvmove_fns ) ;
2003-05-06 16:20:11 +04:00
}
int pvmove ( struct cmd_context * cmd , int argc , char * * argv )
{
char * pv_name = NULL ;
int ret ;
if ( argc ) {
pv_name = argv [ 0 ] ;
argc - - ;
argv + + ;
2003-08-20 19:48:27 +04:00
if ( ! arg_count ( cmd , abort_ARG ) & &
( ret = _set_up_pvmove ( cmd , pv_name , argc , argv ) ) ! =
2004-05-05 21:56:20 +04:00
ECMD_PROCESSED ) {
2003-05-06 16:20:11 +04:00
stack ;
return ret ;
}
}
2003-04-30 19:28:17 +04:00
2003-05-06 16:20:11 +04:00
return pvmove_poll ( cmd , pv_name ,
arg_count ( cmd , background_ARG ) ? 1 : 0 ) ;
2003-04-30 19:28:17 +04:00
}