2019-09-24 15:28:02 -05:00
/*
* Copyright ( C ) 2014 - 2015 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include "lib/misc/lib.h"
# include "lib/metadata/metadata.h"
# include "lib/locking/locking.h"
# include "lib/misc/lvm-string.h"
# include "lib/commands/toolcontext.h"
# include "lib/display/display.h"
# include "lib/metadata/segtype.h"
# include "lib/activate/activate.h"
# include "lib/config/defaults.h"
int lv_is_writecache_origin ( const struct logical_volume * lv )
{
2020-02-19 13:30:02 -06:00
struct lv_segment * seg ;
2019-09-24 15:28:02 -05:00
2020-02-19 13:30:02 -06:00
/* Make sure there's exactly one segment in segs_using_this_lv! */
if ( dm_list_empty ( & lv - > segs_using_this_lv ) | |
( dm_list_size ( & lv - > segs_using_this_lv ) > 1 ) )
return 0 ;
seg = get_only_segment_using_this_lv ( lv ) ;
return seg & & lv_is_writecache ( seg - > lv ) & & ! lv_is_pending_delete ( seg - > lv ) & & ( seg_lv ( seg , 0 ) = = lv ) ;
2019-09-24 15:28:02 -05:00
}
int lv_is_writecache_cachevol ( const struct logical_volume * lv )
{
struct seg_list * sl ;
dm_list_iterate_items ( sl , & lv - > segs_using_this_lv ) {
if ( ! sl - > seg | | ! sl - > seg - > lv | | ! sl - > seg - > writecache )
continue ;
if ( lv_is_writecache ( sl - > seg - > lv ) & & ( sl - > seg - > writecache = = lv ) )
return 1 ;
}
return 0 ;
}
static int _lv_writecache_detach ( struct cmd_context * cmd , struct logical_volume * lv ,
struct logical_volume * lv_fast )
{
struct lv_segment * seg = first_seg ( lv ) ;
struct logical_volume * origin ;
if ( ! seg_is_writecache ( seg ) ) {
log_error ( " LV %s segment is not writecache. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! seg - > writecache ) {
log_error ( " LV %s writecache segment has no writecache. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! ( origin = seg_lv ( seg , 0 ) ) ) {
log_error ( " LV %s writecache segment has no origin " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! remove_seg_from_segs_using_this_lv ( seg - > writecache , seg ) )
return_0 ;
lv_set_visible ( seg - > writecache ) ;
lv - > status & = ~ WRITECACHE ;
seg - > writecache = NULL ;
lv_fast - > status & = ~ LV_CACHE_VOL ;
if ( ! remove_layer_from_lv ( lv , origin ) )
return_0 ;
if ( ! lv_remove ( origin ) )
return_0 ;
return 1 ;
}
static int _get_writecache_kernel_error ( struct cmd_context * cmd ,
struct logical_volume * lv ,
uint32_t * kernel_error )
{
struct lv_with_info_and_seg_status status ;
memset ( & status , 0 , sizeof ( status ) ) ;
status . seg_status . type = SEG_STATUS_NONE ;
status . seg_status . seg = first_seg ( lv ) ;
/* FIXME: why reporter_pool? */
if ( ! ( status . seg_status . mem = dm_pool_create ( " reporter_pool " , 1024 ) ) ) {
log_error ( " Failed to get mem for LV status. " ) ;
return 0 ;
}
if ( ! lv_info_with_seg_status ( cmd , first_seg ( lv ) , & status , 1 , 1 ) ) {
log_error ( " Failed to get device mapper status for %s " , display_lvname ( lv ) ) ;
goto fail ;
}
if ( ! status . info . exists ) {
log_error ( " No device mapper info exists for %s " , display_lvname ( lv ) ) ;
goto fail ;
}
if ( status . seg_status . type ! = SEG_STATUS_WRITECACHE ) {
log_error ( " Invalid device mapper status type (%d) for %s " ,
( uint32_t ) status . seg_status . type , display_lvname ( lv ) ) ;
goto fail ;
}
* kernel_error = status . seg_status . writecache - > error ;
dm_pool_destroy ( status . seg_status . mem ) ;
return 1 ;
fail :
dm_pool_destroy ( status . seg_status . mem ) ;
return 0 ;
}
int lv_detach_writecache_cachevol ( struct logical_volume * lv , int noflush )
{
struct cmd_context * cmd = lv - > vg - > cmd ;
struct logical_volume * lv_fast ;
uint32_t kernel_error = 0 ;
lv_fast = first_seg ( lv ) - > writecache ;
if ( noflush )
goto detach ;
/*
* Activate LV internally since the LV needs to be active to flush .
* LV_TEMPORARY should keep the LV from being exposed to the user
* and being accessed .
*/
lv - > status | = LV_TEMPORARY ;
if ( ! activate_lv ( cmd , lv ) ) {
log_error ( " Failed to activate LV %s for flushing writecache. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! sync_local_dev_names ( cmd ) ) {
log_error ( " Failed to sync local devices before detaching writecache. " ) ;
return 0 ;
}
if ( ! lv_writecache_message ( lv , " flush " ) ) {
log_error ( " Failed to flush writecache for %s. " , display_lvname ( lv ) ) ;
if ( ! deactivate_lv ( cmd , lv ) )
log_error ( " Failed to deactivate %s. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( ! _get_writecache_kernel_error ( cmd , lv , & kernel_error ) ) {
log_error ( " Failed to get writecache error status for %s. " , display_lvname ( lv ) ) ;
if ( ! deactivate_lv ( cmd , lv ) )
log_error ( " Failed to deactivate %s. " , display_lvname ( lv ) ) ;
return 0 ;
}
if ( kernel_error ) {
log_error ( " Failed to flush writecache (error %u) for %s. " , kernel_error , display_lvname ( lv ) ) ;
deactivate_lv ( cmd , lv ) ;
return 0 ;
}
if ( ! deactivate_lv ( cmd , lv ) ) {
log_error ( " Failed to deactivate LV %s for detaching writecache. " , display_lvname ( lv ) ) ;
return 0 ;
}
lv - > status & = ~ LV_TEMPORARY ;
detach :
if ( ! _lv_writecache_detach ( cmd , lv , lv_fast ) ) {
log_error ( " Failed to detach writecache from %s " , display_lvname ( lv ) ) ;
return 0 ;
}
return 1 ;
}