2011-12-21 17:08:11 +04:00
/*
2016-06-29 16:56:29 +03:00
* Copyright ( C ) 2011 - 2016 Red Hat , Inc . All rights reserved .
2011-12-21 17:08:11 +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 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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2011-12-21 17:08:11 +04:00
*/
2015-10-10 17:58:31 +03:00
# include "lib.h" /* using here lvm log */
2011-12-21 17:08:11 +04:00
# include "dmeventd_lvm.h"
2015-10-10 17:58:31 +03:00
# include "libdevmapper-event.h"
2011-12-21 17:08:11 +04:00
# include <sys/wait.h>
2014-04-15 15:27:47 +04:00
# include <stdarg.h>
2015-10-29 13:52:11 +03:00
# include <pthread.h>
2011-12-21 17:08:11 +04:00
2015-10-12 12:35:33 +03:00
/* TODO - move this mountinfo code into library to be reusable */
# ifdef __linux__
# include "kdev_t.h"
# else
# define MAJOR(x) major((x))
# define MINOR(x) minor((x))
# endif
/* First warning when thin data or metadata is 80% full. */
2015-10-20 15:19:35 +03:00
# define WARNING_THRESH (DM_PERCENT_1 * 80)
2016-06-29 16:56:29 +03:00
/* Umount thin LVs when thin data or metadata LV is >=
* and lvextend - - use - policies has failed . */
# define UMOUNT_THRESH (DM_PERCENT_1 * 95)
2011-12-21 17:08:11 +04:00
/* Run a check every 5%. */
2015-10-20 15:19:35 +03:00
# define CHECK_STEP (DM_PERCENT_1 * 5)
2015-10-12 12:35:33 +03:00
/* Do not bother checking thin data or metadata is less than 50% full. */
2015-10-20 15:19:35 +03:00
# define CHECK_MINIMUM (DM_PERCENT_1 * 50)
2011-12-21 17:08:11 +04:00
# define UMOUNT_COMMAND " / bin / umount"
2015-10-29 13:52:11 +03:00
# define MAX_FAILS (10)
2011-12-21 17:08:11 +04:00
# define THIN_DEBUG 0
struct dso_state {
struct dm_pool * mem ;
2012-01-19 19:21:23 +04:00
int metadata_percent_check ;
2011-12-21 17:08:11 +04:00
int data_percent_check ;
2012-01-19 19:21:23 +04:00
uint64_t known_metadata_size ;
2011-12-21 17:08:11 +04:00
uint64_t known_data_size ;
2015-10-29 13:52:11 +03:00
unsigned fails ;
2011-12-21 17:08:11 +04:00
char cmd_str [ 1024 ] ;
} ;
2015-10-10 17:58:31 +03:00
DM_EVENT_LOG_FN ( " thin " )
2016-06-29 16:56:29 +03:00
# define UUID_PREFIX "LVM-"
/* Figure out device UUID has LVM- prefix and is OPEN */
static int _has_unmountable_prefix ( int major , int minor )
{
struct dm_task * dmt ;
struct dm_info info ;
const char * uuid ;
int r = 0 ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_INFO ) ) )
return_0 ;
if ( ! dm_task_set_major_minor ( dmt , major , minor , 1 ) )
goto_out ;
if ( ! dm_task_no_flush ( dmt ) )
stack ;
if ( ! dm_task_run ( dmt ) )
goto out ;
if ( ! dm_task_get_info ( dmt , & info ) )
goto out ;
if ( ! info . exists | | ! info . open_count )
goto out ; /* Not open -> not mounted */
if ( ! ( uuid = dm_task_get_uuid ( dmt ) ) )
goto out ;
/* Check it's public mountable LV
* has prefix LVM - and UUID size is 68 chars */
if ( memcmp ( uuid , UUID_PREFIX , sizeof ( UUID_PREFIX ) - 1 ) | |
strlen ( uuid ) ! = 68 )
goto out ;
# if THIN_DEBUG
2016-07-04 18:30:27 +03:00
log_debug ( " Found logical volume %s (%u:%u). " , uuid , major , minor ) ;
2016-06-29 16:56:29 +03:00
# endif
r = 1 ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2012-03-22 21:29:38 +04:00
/* Get dependencies for device, and try to find matching device */
static int _has_deps ( const char * name , int tp_major , int tp_minor , int * dev_minor )
{
struct dm_task * dmt ;
const struct dm_deps * deps ;
struct dm_info info ;
int major , minor ;
int r = 0 ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_DEPS ) ) )
return 0 ;
if ( ! dm_task_set_name ( dmt , name ) )
goto out ;
if ( ! dm_task_no_open_count ( dmt ) )
goto out ;
if ( ! dm_task_run ( dmt ) )
goto out ;
if ( ! dm_task_get_info ( dmt , & info ) )
goto out ;
if ( ! ( deps = dm_task_get_deps ( dmt ) ) )
goto out ;
if ( ! info . exists | | deps - > count ! = 1 )
goto out ;
major = ( int ) MAJOR ( deps - > device [ 0 ] ) ;
minor = ( int ) MINOR ( deps - > device [ 0 ] ) ;
if ( ( major ! = tp_major ) | | ( minor ! = tp_minor ) )
goto out ;
* dev_minor = info . minor ;
2016-06-29 16:56:29 +03:00
if ( ! _has_unmountable_prefix ( major , info . minor ) )
goto out ;
2012-03-22 21:29:38 +04:00
# if THIN_DEBUG
{
char dev_name [ PATH_MAX ] ;
if ( dm_device_get_name ( major , minor , 0 , dev_name , sizeof ( dev_name ) ) )
2015-10-09 22:57:48 +03:00
log_debug ( " Found %s (%u:%u) depends on %s. " ,
name , major , * dev_minor , dev_name ) ;
2012-03-22 21:29:38 +04:00
}
# endif
r = 1 ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
/* Get all active devices */
static int _find_all_devs ( dm_bitset_t bs , int tp_major , int tp_minor )
{
struct dm_task * dmt ;
struct dm_names * names ;
unsigned next = 0 ;
int minor , r = 1 ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_LIST ) ) )
return 0 ;
if ( ! dm_task_run ( dmt ) ) {
r = 0 ;
goto out ;
}
if ( ! ( names = dm_task_get_names ( dmt ) ) ) {
r = 0 ;
goto out ;
}
if ( ! names - > dev )
goto out ;
do {
names = ( struct dm_names * ) ( ( char * ) names + next ) ;
if ( _has_deps ( names - > name , tp_major , tp_minor , & minor ) )
dm_bit_set ( bs , minor ) ;
next = names - > next ;
} while ( next ) ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2011-12-21 17:08:11 +04:00
static int _run ( const char * cmd , . . . )
{
2012-03-22 21:29:38 +04:00
va_list ap ;
int argc = 1 ; /* for argv[0], i.e. cmd */
int i = 0 ;
const char * * argv ;
pid_t pid = fork ( ) ;
int status ;
if ( pid = = 0 ) { /* child */
va_start ( ap , cmd ) ;
while ( va_arg ( ap , const char * ) )
+ + argc ;
va_end ( ap ) ;
/* + 1 for the terminating NULL */
argv = alloca ( sizeof ( const char * ) * ( argc + 1 ) ) ;
argv [ 0 ] = cmd ;
2015-10-29 13:52:11 +03:00
va_start ( ap , cmd ) ;
2012-03-22 21:29:38 +04:00
while ( ( argv [ + + i ] = va_arg ( ap , const char * ) ) ) ;
va_end ( ap ) ;
2011-12-21 17:08:11 +04:00
2012-03-22 21:29:38 +04:00
execvp ( cmd , ( char * * ) argv ) ;
2015-10-09 22:57:48 +03:00
log_sys_error ( " exec " , cmd ) ;
2012-03-22 21:29:38 +04:00
exit ( 127 ) ;
}
2011-12-21 17:08:11 +04:00
2012-03-22 21:29:38 +04:00
if ( pid > 0 ) { /* parent */
if ( waitpid ( pid , & status , 0 ) ! = pid )
return 0 ; /* waitpid failed */
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) )
return 0 ; /* the child failed */
}
2011-12-21 17:08:11 +04:00
2012-03-22 21:29:38 +04:00
if ( pid < 0 )
return 0 ; /* fork failed */
2011-12-21 17:08:11 +04:00
2012-03-22 21:29:38 +04:00
return 1 ; /* all good */
2011-12-21 17:08:11 +04:00
}
2013-05-20 15:10:07 +04:00
struct mountinfo_s {
2015-10-20 15:19:35 +03:00
const char * device ;
2013-05-20 15:10:07 +04:00
struct dm_info info ;
dm_bitset_t minors ; /* Bitset for active thin pool minors */
} ;
static int _umount_device ( char * buffer , unsigned major , unsigned minor ,
char * target , void * cb_data )
{
struct mountinfo_s * data = cb_data ;
2016-07-04 18:30:27 +03:00
char * words [ 10 ] ;
2013-05-20 15:10:07 +04:00
if ( ( major = = data - > info . major ) & & dm_bit ( data - > minors , minor ) ) {
2016-07-04 18:30:27 +03:00
if ( dm_split_words ( buffer , DM_ARRAY_SIZE ( words ) , 0 , words ) < DM_ARRAY_SIZE ( words ) )
words [ 9 ] = NULL ; /* just don't show device name */
2016-08-31 12:05:39 +03:00
log_info ( " Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \" %s \" . " ,
2016-07-04 18:30:27 +03:00
words [ 9 ] ? : " " , major , minor , data - > device ,
data - > info . major , data - > info . minor ,
target ) ;
2013-05-20 15:10:07 +04:00
if ( ! _run ( UMOUNT_COMMAND , " -fl " , target , NULL ) )
2016-07-04 18:30:27 +03:00
log_error ( " Failed to lazy umount thin %s (%d:%d) from %s: %s. " ,
words [ 9 ] , major , minor , target , strerror ( errno ) ) ;
2013-05-20 15:10:07 +04:00
}
return 1 ;
}
2012-03-22 21:29:38 +04:00
/*
2016-06-29 16:56:29 +03:00
* Find all thin pool LV users and try to umount them .
2012-03-22 21:29:38 +04:00
* TODO : work with read - only thin pool support
*/
2015-10-20 15:19:35 +03:00
static void _umount ( struct dm_task * dmt )
2011-12-21 17:08:11 +04:00
{
2014-04-17 15:26:45 +04:00
/* TODO: Convert to use hash to reduce memory usage */
static const size_t MINORS = ( 1U < < 20 ) ; /* 20 bit */
2015-10-20 15:19:35 +03:00
struct mountinfo_s data = { NULL } ;
2011-12-21 17:08:11 +04:00
2013-05-20 15:10:07 +04:00
if ( ! dm_task_get_info ( dmt , & data . info ) )
2011-12-21 17:08:11 +04:00
return ;
2012-03-22 21:29:38 +04:00
2015-10-20 15:19:35 +03:00
data . device = dm_task_get_name ( dmt ) ;
2013-05-20 15:10:07 +04:00
if ( ! ( data . minors = dm_bitset_create ( NULL , MINORS ) ) ) {
2015-10-20 15:19:35 +03:00
log_error ( " Failed to allocate bitset. Not unmounting %s. " , data . device ) ;
2012-03-22 21:29:38 +04:00
goto out ;
2011-12-21 17:08:11 +04:00
}
2013-05-20 15:10:07 +04:00
if ( ! _find_all_devs ( data . minors , data . info . major , data . info . minor ) ) {
2015-10-20 15:19:35 +03:00
log_error ( " Failed to detect mounted volumes for %s. " , data . device ) ;
2012-03-22 21:29:38 +04:00
goto out ;
}
2011-12-21 17:08:11 +04:00
2013-05-20 15:10:07 +04:00
if ( ! dm_mountinfo_read ( _umount_device , & data ) ) {
2015-10-09 22:57:48 +03:00
log_error ( " Could not parse mountinfo file. " ) ;
2013-05-20 15:10:07 +04:00
goto out ;
2011-12-21 17:08:11 +04:00
}
2013-04-21 14:19:25 +04:00
out :
2013-05-20 15:10:07 +04:00
if ( data . minors )
dm_bitset_destroy ( data . minors ) ;
2011-12-21 17:08:11 +04:00
}
2016-06-29 16:56:29 +03:00
static int _use_policy ( struct dm_task * dmt , struct dso_state * state )
2015-10-20 15:19:35 +03:00
{
# if THIN_DEBUG
log_info ( " dmeventd executes: %s. " , state - > cmd_str ) ;
# endif
if ( ! dmeventd_lvm2_run_with_lock ( state - > cmd_str ) ) {
log_error ( " Failed to extend thin pool %s. " ,
dm_task_get_name ( dmt ) ) ;
2015-10-29 13:52:11 +03:00
state - > fails + + ;
2016-06-29 16:56:29 +03:00
return 0 ;
}
state - > fails = 0 ;
return 1 ;
2015-10-20 15:19:35 +03:00
}
2011-12-21 17:08:11 +04:00
void process_event ( struct dm_task * dmt ,
enum dm_event_mask event __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2011-12-21 17:08:11 +04:00
{
const char * device = dm_task_get_name ( dmt ) ;
int percent ;
2015-10-12 12:40:51 +03:00
struct dso_state * state = * user ;
2012-01-20 14:59:26 +04:00
struct dm_status_thin_pool * tps = NULL ;
2011-12-21 17:08:11 +04:00
void * next = NULL ;
uint64_t start , length ;
char * target_type = NULL ;
char * params ;
2015-10-20 15:19:35 +03:00
int needs_policy = 0 ;
2016-06-29 16:56:29 +03:00
int needs_umount = 0 ;
# if THIN_DEBUG
log_debug ( " Watch for tp-data:%.2f%% tp-metadata:%.2f%%. " ,
dm_percent_to_float ( state - > data_percent_check ) ,
dm_percent_to_float ( state - > metadata_percent_check ) ) ;
# endif
2011-12-21 17:08:11 +04:00
#if 0
/* No longer monitoring, waiting for remove */
if ( ! state - > meta_percent_check & & ! state - > data_percent_check )
return ;
# endif
2015-10-20 15:19:35 +03:00
if ( event & DM_EVENT_DEVICE_ERROR ) {
/* Error -> no need to check and do instant resize */
2016-06-29 16:56:29 +03:00
if ( _use_policy ( dmt , state ) )
goto out ;
stack ;
2015-10-20 15:19:35 +03:00
}
2011-12-21 17:08:11 +04:00
dm_get_next_target ( dmt , next , & start , & length , & target_type , & params ) ;
if ( ! target_type | | ( strcmp ( target_type , " thin-pool " ) ! = 0 ) ) {
2015-10-09 22:57:48 +03:00
log_error ( " Invalid target type. " ) ;
2011-12-21 17:08:11 +04:00
goto out ;
}
if ( ! dm_get_status_thin_pool ( state - > mem , params , & tps ) ) {
2015-10-09 22:57:48 +03:00
log_error ( " Failed to parse status. " ) ;
2016-06-29 16:56:29 +03:00
needs_umount = 1 ;
2011-12-21 17:08:11 +04:00
goto out ;
}
# if THIN_DEBUG
2015-10-20 15:19:35 +03:00
log_debug ( " Thin pool status " FMTu64 " / " FMTu64 " "
FMTu64 " / " FMTu64 " . " ,
2015-10-09 22:57:48 +03:00
tps - > used_metadata_blocks , tps - > total_metadata_blocks ,
tps - > used_data_blocks , tps - > total_data_blocks ) ;
2011-12-21 17:08:11 +04:00
# endif
/* Thin pool size had changed. Clear the threshold. */
2012-01-19 19:21:23 +04:00
if ( state - > known_metadata_size ! = tps - > total_metadata_blocks ) {
state - > metadata_percent_check = CHECK_MINIMUM ;
state - > known_metadata_size = tps - > total_metadata_blocks ;
2011-12-21 17:08:11 +04:00
}
if ( state - > known_data_size ! = tps - > total_data_blocks ) {
state - > data_percent_check = CHECK_MINIMUM ;
state - > known_data_size = tps - > total_data_blocks ;
}
2015-10-20 15:19:35 +03:00
percent = dm_make_percent ( tps - > used_metadata_blocks , tps - > total_metadata_blocks ) ;
2012-01-19 19:21:23 +04:00
if ( percent > = state - > metadata_percent_check ) {
2011-12-21 17:08:11 +04:00
/*
* Usage has raised more than CHECK_STEP since the last
* time . Run actions .
*/
2012-01-19 19:21:23 +04:00
state - > metadata_percent_check = ( percent / CHECK_STEP ) * CHECK_STEP + CHECK_STEP ;
2011-12-21 17:08:11 +04:00
/* FIXME: extension of metadata needs to be written! */
if ( percent > = WARNING_THRESH ) /* Print a warning to syslog. */
2015-10-20 15:19:35 +03:00
log_warn ( " WARNING: Thin pool %s metadata is now %.2f%% full. " ,
device , dm_percent_to_float ( percent ) ) ;
needs_policy = 1 ;
2016-06-29 16:56:29 +03:00
if ( percent > = UMOUNT_THRESH )
needs_umount = 1 ;
2011-12-21 17:08:11 +04:00
}
2015-10-20 15:19:35 +03:00
percent = dm_make_percent ( tps - > used_data_blocks , tps - > total_data_blocks ) ;
2011-12-21 17:08:11 +04:00
if ( percent > = state - > data_percent_check ) {
/*
* Usage has raised more than CHECK_STEP since
* the last time . Run actions .
*/
state - > data_percent_check = ( percent / CHECK_STEP ) * CHECK_STEP + CHECK_STEP ;
if ( percent > = WARNING_THRESH ) /* Print a warning to syslog. */
2015-10-20 15:19:35 +03:00
log_warn ( " WARNING: Thin pool %s data is now %.2f%% full. " ,
device , dm_percent_to_float ( percent ) ) ;
needs_policy = 1 ;
2016-06-29 16:56:29 +03:00
if ( percent > = UMOUNT_THRESH )
needs_umount = 1 ;
2011-12-21 17:08:11 +04:00
}
2015-10-20 15:19:35 +03:00
2016-06-29 16:56:29 +03:00
if ( needs_policy & &
_use_policy ( dmt , state ) )
needs_umount = 0 ; /* No umount when command was successful */
2011-12-21 17:08:11 +04:00
out :
2016-06-29 16:56:29 +03:00
if ( needs_umount ) {
_umount ( dmt ) ;
/* Until something changes, do not retry any more actions */
state - > data_percent_check = state - > metadata_percent_check = ( DM_PERCENT_1 * 101 ) ;
}
2011-12-21 17:08:11 +04:00
if ( tps )
dm_pool_free ( state - > mem , tps ) ;
2015-10-29 13:52:11 +03:00
if ( state - > fails > = MAX_FAILS ) {
log_warn ( " WARNING: Dropping monitoring of %s. "
2016-08-31 12:05:39 +03:00
" lvm2 command fails too often (%u times in row). " ,
2015-10-29 13:52:11 +03:00
device , state - > fails ) ;
pthread_kill ( pthread_self ( ) , SIGALRM ) ;
}
2011-12-21 17:08:11 +04:00
}
int register_device ( const char * device ,
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2011-12-21 17:08:11 +04:00
{
struct dso_state * state ;
2015-10-13 12:30:37 +03:00
if ( ! dmeventd_lvm2_init_with_pool ( " thin_pool_state " , state ) )
goto_bad ;
2011-12-22 19:57:29 +04:00
2015-10-13 12:30:37 +03:00
if ( ! dmeventd_lvm2_command ( state - > mem , state - > cmd_str ,
2011-12-22 19:57:29 +04:00
sizeof ( state - > cmd_str ) ,
" lvextend --use-policies " ,
device ) ) {
2015-10-13 12:30:37 +03:00
dmeventd_lvm2_exit_with_pool ( state ) ;
goto_bad ;
2011-12-21 17:08:11 +04:00
}
2012-01-19 19:21:23 +04:00
state - > metadata_percent_check = CHECK_MINIMUM ;
2011-12-21 17:08:11 +04:00
state - > data_percent_check = CHECK_MINIMUM ;
2015-10-12 12:40:51 +03:00
* user = state ;
2011-12-21 17:08:11 +04:00
2016-08-31 12:05:39 +03:00
log_info ( " Monitoring thin pool %s. " , device ) ;
2011-12-21 17:08:11 +04:00
return 1 ;
2011-12-22 19:57:29 +04:00
bad :
2016-08-31 12:05:39 +03:00
log_error ( " Failed to monitor thin pool %s. " , device ) ;
2011-12-22 19:57:29 +04:00
return 0 ;
2011-12-21 17:08:11 +04:00
}
int unregister_device ( const char * device ,
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2011-12-21 17:08:11 +04:00
{
2015-10-12 12:40:51 +03:00
struct dso_state * state = * user ;
2011-12-21 17:08:11 +04:00
2015-10-13 12:30:37 +03:00
dmeventd_lvm2_exit_with_pool ( state ) ;
2016-08-31 12:05:39 +03:00
log_info ( " No longer monitoring thin pool %s. " , device ) ;
2011-12-21 17:08:11 +04:00
return 1 ;
}