2011-12-21 13:08:11 +00:00
/*
2015-10-10 16:58:31 +02:00
* Copyright ( C ) 2011 - 2015 Red Hat , Inc . All rights reserved .
2011-12-21 13:08:11 +00: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 11:49:46 +01:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2011-12-21 13:08:11 +00:00
*/
2015-10-10 16:58:31 +02:00
# include "lib.h" /* using here lvm log */
2011-12-21 13:08:11 +00:00
# include "dmeventd_lvm.h"
2015-10-10 16:58:31 +02:00
# include "libdevmapper-event.h"
2011-12-21 13:08:11 +00:00
# include <sys/wait.h>
2014-04-15 13:27:47 +02:00
# include <stdarg.h>
2015-10-29 11:52:11 +01:00
# include <pthread.h>
2011-12-21 13:08:11 +00:00
2015-10-12 11:35:33 +02: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 14:19:35 +02:00
# define WARNING_THRESH (DM_PERCENT_1 * 80)
2011-12-21 13:08:11 +00:00
/* Run a check every 5%. */
2015-10-20 14:19:35 +02:00
# define CHECK_STEP (DM_PERCENT_1 * 5)
2015-10-12 11:35:33 +02:00
/* Do not bother checking thin data or metadata is less than 50% full. */
2015-10-20 14:19:35 +02:00
# define CHECK_MINIMUM (DM_PERCENT_1 * 50)
2011-12-21 13:08:11 +00:00
# define UMOUNT_COMMAND " / bin / umount"
2015-10-29 11:52:11 +01:00
# define MAX_FAILS (10)
2011-12-21 13:08:11 +00:00
# define THIN_DEBUG 0
struct dso_state {
struct dm_pool * mem ;
2012-01-19 15:21:23 +00:00
int metadata_percent_check ;
2011-12-21 13:08:11 +00:00
int data_percent_check ;
2012-01-19 15:21:23 +00:00
uint64_t known_metadata_size ;
2011-12-21 13:08:11 +00:00
uint64_t known_data_size ;
2015-10-29 11:52:11 +01:00
unsigned fails ;
2011-12-21 13:08:11 +00:00
char cmd_str [ 1024 ] ;
} ;
2015-10-10 16:58:31 +02:00
DM_EVENT_LOG_FN ( " thin " )
2012-03-22 18:29:38 +01: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 ;
# if THIN_DEBUG
{
char dev_name [ PATH_MAX ] ;
if ( dm_device_get_name ( major , minor , 0 , dev_name , sizeof ( dev_name ) ) )
2015-10-09 21:57:48 +02:00
log_debug ( " Found %s (%u:%u) depends on %s. " ,
name , major , * dev_minor , dev_name ) ;
2012-03-22 18:29:38 +01: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 13:08:11 +00:00
static int _run ( const char * cmd , . . . )
{
2012-03-22 18:29:38 +01: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 11:52:11 +01:00
va_start ( ap , cmd ) ;
2012-03-22 18:29:38 +01:00
while ( ( argv [ + + i ] = va_arg ( ap , const char * ) ) ) ;
va_end ( ap ) ;
2011-12-21 13:08:11 +00:00
2012-03-22 18:29:38 +01:00
execvp ( cmd , ( char * * ) argv ) ;
2015-10-09 21:57:48 +02:00
log_sys_error ( " exec " , cmd ) ;
2012-03-22 18:29:38 +01:00
exit ( 127 ) ;
}
2011-12-21 13:08:11 +00:00
2012-03-22 18:29:38 +01: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 13:08:11 +00:00
2012-03-22 18:29:38 +01:00
if ( pid < 0 )
return 0 ; /* fork failed */
2011-12-21 13:08:11 +00:00
2012-03-22 18:29:38 +01:00
return 1 ; /* all good */
2011-12-21 13:08:11 +00:00
}
2013-05-20 13:10:07 +02:00
struct mountinfo_s {
2015-10-20 14:19:35 +02:00
const char * device ;
2013-05-20 13:10:07 +02: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 ;
if ( ( major = = data - > info . major ) & & dm_bit ( data - > minors , minor ) ) {
2015-10-09 21:57:48 +02:00
log_info ( " Unmounting thin volume %s from %s. " ,
data - > device , target ) ;
2013-05-20 13:10:07 +02:00
if ( ! _run ( UMOUNT_COMMAND , " -fl " , target , NULL ) )
2015-10-09 21:57:48 +02:00
log_error ( " Failed to umount thin %s from %s: %s. " ,
data - > device , target , strerror ( errno ) ) ;
2013-05-20 13:10:07 +02:00
}
return 1 ;
}
2012-03-22 18:29:38 +01:00
/*
* Find all thin pool users and try to umount them .
* TODO : work with read - only thin pool support
*/
2015-10-20 14:19:35 +02:00
static void _umount ( struct dm_task * dmt )
2011-12-21 13:08:11 +00:00
{
2014-04-17 13:26:45 +02:00
/* TODO: Convert to use hash to reduce memory usage */
static const size_t MINORS = ( 1U < < 20 ) ; /* 20 bit */
2015-10-20 14:19:35 +02:00
struct mountinfo_s data = { NULL } ;
2011-12-21 13:08:11 +00:00
2013-05-20 13:10:07 +02:00
if ( ! dm_task_get_info ( dmt , & data . info ) )
2011-12-21 13:08:11 +00:00
return ;
2012-03-22 18:29:38 +01:00
2015-10-20 14:19:35 +02:00
data . device = dm_task_get_name ( dmt ) ;
2013-05-20 13:10:07 +02:00
if ( ! ( data . minors = dm_bitset_create ( NULL , MINORS ) ) ) {
2015-10-20 14:19:35 +02:00
log_error ( " Failed to allocate bitset. Not unmounting %s. " , data . device ) ;
2012-03-22 18:29:38 +01:00
goto out ;
2011-12-21 13:08:11 +00:00
}
2013-05-20 13:10:07 +02:00
if ( ! _find_all_devs ( data . minors , data . info . major , data . info . minor ) ) {
2015-10-20 14:19:35 +02:00
log_error ( " Failed to detect mounted volumes for %s. " , data . device ) ;
2012-03-22 18:29:38 +01:00
goto out ;
}
2011-12-21 13:08:11 +00:00
2013-05-20 13:10:07 +02:00
if ( ! dm_mountinfo_read ( _umount_device , & data ) ) {
2015-10-09 21:57:48 +02:00
log_error ( " Could not parse mountinfo file. " ) ;
2013-05-20 13:10:07 +02:00
goto out ;
2011-12-21 13:08:11 +00:00
}
2013-04-21 12:19:25 +02:00
out :
2013-05-20 13:10:07 +02:00
if ( data . minors )
dm_bitset_destroy ( data . minors ) ;
2011-12-21 13:08:11 +00:00
}
2015-10-20 14:19:35 +02:00
static void _use_policy ( struct dm_task * dmt , struct dso_state * state )
{
# 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 ) ) ;
_umount ( dmt ) ;
2015-10-29 11:52:11 +01:00
state - > fails + + ;
} else
state - > fails = 0 ;
2015-10-20 14:19:35 +02:00
}
2011-12-21 13:08:11 +00:00
void process_event ( struct dm_task * dmt ,
enum dm_event_mask event __attribute__ ( ( unused ) ) ,
2015-10-12 11:40:51 +02:00
void * * user )
2011-12-21 13:08:11 +00:00
{
const char * device = dm_task_get_name ( dmt ) ;
int percent ;
2015-10-12 11:40:51 +02:00
struct dso_state * state = * user ;
2012-01-20 10:59:26 +00:00
struct dm_status_thin_pool * tps = NULL ;
2011-12-21 13:08:11 +00:00
void * next = NULL ;
uint64_t start , length ;
char * target_type = NULL ;
char * params ;
2015-10-20 14:19:35 +02:00
int needs_policy = 0 ;
2011-12-21 13:08:11 +00:00
#if 0
/* No longer monitoring, waiting for remove */
if ( ! state - > meta_percent_check & & ! state - > data_percent_check )
return ;
# endif
2015-10-20 14:19:35 +02:00
if ( event & DM_EVENT_DEVICE_ERROR ) {
/* Error -> no need to check and do instant resize */
_use_policy ( dmt , state ) ;
2015-10-29 11:52:11 +01:00
goto out ;
2015-10-20 14:19:35 +02:00
}
2011-12-21 13:08:11 +00:00
dm_get_next_target ( dmt , next , & start , & length , & target_type , & params ) ;
if ( ! target_type | | ( strcmp ( target_type , " thin-pool " ) ! = 0 ) ) {
2015-10-09 21:57:48 +02:00
log_error ( " Invalid target type. " ) ;
2011-12-21 13:08:11 +00:00
goto out ;
}
if ( ! dm_get_status_thin_pool ( state - > mem , params , & tps ) ) {
2015-10-09 21:57:48 +02:00
log_error ( " Failed to parse status. " ) ;
2015-10-20 14:19:35 +02:00
_umount ( dmt ) ;
2011-12-21 13:08:11 +00:00
goto out ;
}
# if THIN_DEBUG
2015-10-20 14:19:35 +02:00
log_debug ( " Thin pool status " FMTu64 " / " FMTu64 " "
FMTu64 " / " FMTu64 " . " ,
2015-10-09 21:57:48 +02:00
tps - > used_metadata_blocks , tps - > total_metadata_blocks ,
tps - > used_data_blocks , tps - > total_data_blocks ) ;
2011-12-21 13:08:11 +00:00
# endif
/* Thin pool size had changed. Clear the threshold. */
2012-01-19 15:21:23 +00: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 13:08:11 +00: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 14:19:35 +02:00
percent = dm_make_percent ( tps - > used_metadata_blocks , tps - > total_metadata_blocks ) ;
2012-01-19 15:21:23 +00:00
if ( percent > = state - > metadata_percent_check ) {
2011-12-21 13:08:11 +00:00
/*
* Usage has raised more than CHECK_STEP since the last
* time . Run actions .
*/
2012-01-19 15:21:23 +00:00
state - > metadata_percent_check = ( percent / CHECK_STEP ) * CHECK_STEP + CHECK_STEP ;
2011-12-21 13:08:11 +00:00
/* FIXME: extension of metadata needs to be written! */
if ( percent > = WARNING_THRESH ) /* Print a warning to syslog. */
2015-10-20 14:19:35 +02:00
log_warn ( " WARNING: Thin pool %s metadata is now %.2f%% full. " ,
device , dm_percent_to_float ( percent ) ) ;
needs_policy = 1 ;
2011-12-21 13:08:11 +00:00
}
2015-10-20 14:19:35 +02:00
percent = dm_make_percent ( tps - > used_data_blocks , tps - > total_data_blocks ) ;
2011-12-21 13:08:11 +00: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 14:19:35 +02:00
log_warn ( " WARNING: Thin pool %s data is now %.2f%% full. " ,
device , dm_percent_to_float ( percent ) ) ;
needs_policy = 1 ;
2011-12-21 13:08:11 +00:00
}
2015-10-20 14:19:35 +02:00
if ( needs_policy )
_use_policy ( dmt , state ) ;
2011-12-21 13:08:11 +00:00
out :
if ( tps )
dm_pool_free ( state - > mem , tps ) ;
2015-10-29 11:52:11 +01:00
if ( state - > fails > = MAX_FAILS ) {
log_warn ( " WARNING: Dropping monitoring of %s. "
" lvm2 command fails too often (%u times in raw). " ,
device , state - > fails ) ;
pthread_kill ( pthread_self ( ) , SIGALRM ) ;
}
2011-12-21 13:08:11 +00:00
}
int register_device ( const char * device ,
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 11:40:51 +02:00
void * * user )
2011-12-21 13:08:11 +00:00
{
struct dso_state * state ;
2015-10-13 11:30:37 +02:00
if ( ! dmeventd_lvm2_init_with_pool ( " thin_pool_state " , state ) )
goto_bad ;
2011-12-22 15:57:29 +00:00
2015-10-13 11:30:37 +02:00
if ( ! dmeventd_lvm2_command ( state - > mem , state - > cmd_str ,
2011-12-22 15:57:29 +00:00
sizeof ( state - > cmd_str ) ,
" lvextend --use-policies " ,
device ) ) {
2015-10-13 11:30:37 +02:00
dmeventd_lvm2_exit_with_pool ( state ) ;
goto_bad ;
2011-12-21 13:08:11 +00:00
}
2012-01-19 15:21:23 +00:00
state - > metadata_percent_check = CHECK_MINIMUM ;
2011-12-21 13:08:11 +00:00
state - > data_percent_check = CHECK_MINIMUM ;
2015-10-12 11:40:51 +02:00
* user = state ;
2011-12-21 13:08:11 +00:00
2015-10-09 21:57:48 +02:00
log_info ( " Monitoring thin %s. " , device ) ;
2011-12-21 13:08:11 +00:00
return 1 ;
2011-12-22 15:57:29 +00:00
bad :
2015-10-09 21:57:48 +02:00
log_error ( " Failed to monitor thin %s. " , device ) ;
2011-12-22 15:57:29 +00:00
return 0 ;
2011-12-21 13:08:11 +00:00
}
int unregister_device ( const char * device ,
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 11:40:51 +02:00
void * * user )
2011-12-21 13:08:11 +00:00
{
2015-10-12 11:40:51 +02:00
struct dso_state * state = * user ;
2011-12-21 13:08:11 +00:00
2015-10-13 11:30:37 +02:00
dmeventd_lvm2_exit_with_pool ( state ) ;
2015-10-09 21:57:48 +02:00
log_info ( " No longer monitoring thin %s. " , device ) ;
2011-12-21 13:08:11 +00:00
return 1 ;
}