2011-08-24 12:27:49 +04:00
/*
2013-02-21 13:25:44 +04:00
* Copyright ( C ) 2011 - 2013 Red Hat , Inc . All rights reserved .
2011-08-24 12:27:49 +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 ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "lib.h"
# include "toolcontext.h"
# include "metadata.h"
# include "segtype.h"
# include "text_export.h"
# include "config.h"
# include "activate.h"
# include "str_list.h"
2011-12-21 17:08:11 +04:00
# include "defaults.h"
2011-08-24 12:27:49 +04:00
# ifdef DMEVENTD
# include "libdevmapper-event.h"
# endif
/* Dm kernel module name for thin provisiong */
2013-07-18 18:06:26 +04:00
static const char _thin_pool_module [ ] = " thin-pool " ;
static const char _thin_module [ ] = " thin " ;
2011-08-24 12:27:49 +04:00
2011-08-26 17:37:47 +04:00
/*
* Macro used as return argument - returns 0.
* return is left to be written in the function for better readability .
*/
# define SEG_LOG_ERROR(t, p...) \
log_error ( t " segment %s of logical volume %s. " , # # p , \
2011-08-30 18:55:15 +04:00
dm_config_parent_name ( sn ) , seg - > lv - > name ) , 0 ;
2011-08-26 17:37:47 +04:00
2012-12-03 16:03:41 +04:00
/* TODO: using static field here, maybe should be a part of segment_type */
2012-12-03 14:52:04 +04:00
static unsigned _feature_mask ;
2011-08-25 14:00:09 +04:00
static const char * _thin_pool_name ( const struct lv_segment * seg )
{
return seg - > segtype - > name ;
}
2011-10-17 18:17:09 +04:00
static int _thin_pool_add_message ( struct lv_segment * seg ,
const char * key ,
const struct dm_config_node * sn )
{
const char * lv_name = NULL ;
struct logical_volume * lv = NULL ;
2011-11-03 18:37:23 +04:00
uint32_t delete_id = 0 ;
2011-10-17 18:17:09 +04:00
dm_thin_message_t type ;
2012-03-03 01:43:26 +04:00
/* Message must have only one from: create, delete */
2011-10-17 18:17:09 +04:00
if ( dm_config_get_str ( sn , " create " , & lv_name ) ) {
if ( ! ( lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
return SEG_LOG_ERROR ( " Unknown LV %s for create message in " ,
lv_name ) ;
/* FIXME: switch to _SNAP later, if the created LV has an origin */
type = DM_THIN_MESSAGE_CREATE_THIN ;
2014-01-08 13:56:05 +04:00
} else if ( dm_config_get_uint32 ( sn , " delete " , & delete_id ) )
2011-10-17 18:17:09 +04:00
type = DM_THIN_MESSAGE_DELETE ;
2014-01-08 13:56:05 +04:00
else
return SEG_LOG_ERROR ( " Unknown message in " ) ;
2011-10-17 18:17:09 +04:00
2011-11-03 18:37:23 +04:00
if ( ! attach_pool_message ( seg , type , lv , delete_id , 1 ) )
2011-10-17 18:17:09 +04:00
return_0 ;
return 1 ;
}
2011-10-11 12:51:56 +04:00
static int _thin_pool_text_import ( struct lv_segment * seg ,
const struct dm_config_node * sn ,
struct dm_hash_table * pv_hash __attribute__ ( ( unused ) ) )
2011-08-25 14:00:09 +04:00
{
2011-09-01 14:16:32 +04:00
const char * lv_name ;
2011-09-07 02:43:56 +04:00
struct logical_volume * pool_data_lv , * pool_metadata_lv ;
2012-08-08 00:24:41 +04:00
const char * discards_str = NULL ;
2011-08-26 17:37:47 +04:00
2011-09-01 14:16:32 +04:00
if ( ! dm_config_get_str ( sn , " metadata " , & lv_name ) )
2011-09-07 02:35:44 +04:00
return SEG_LOG_ERROR ( " Metadata must be a string in " ) ;
2011-08-26 17:37:47 +04:00
2011-09-07 02:43:56 +04:00
if ( ! ( pool_metadata_lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
2011-09-07 02:35:44 +04:00
return SEG_LOG_ERROR ( " Unknown metadata %s in " , lv_name ) ;
2011-08-26 17:37:47 +04:00
2011-10-22 20:45:25 +04:00
if ( ! dm_config_get_str ( sn , " pool " , & lv_name ) )
return SEG_LOG_ERROR ( " Pool must be a string in " ) ;
2011-09-07 02:43:56 +04:00
2011-10-22 20:45:25 +04:00
if ( ! ( pool_data_lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
return SEG_LOG_ERROR ( " Unknown pool %s in " , lv_name ) ;
2013-07-31 17:13:54 +04:00
if ( ! attach_pool_data_lv ( seg , pool_data_lv ) )
2011-09-07 02:43:56 +04:00
return_0 ;
2013-07-31 17:13:54 +04:00
if ( ! attach_pool_metadata_lv ( seg , pool_metadata_lv ) )
2011-10-22 20:45:25 +04:00
return_0 ;
2011-08-30 18:55:15 +04:00
if ( ! dm_config_get_uint64 ( sn , " transaction_id " , & seg - > transaction_id ) )
2011-08-26 17:37:47 +04:00
return SEG_LOG_ERROR ( " Could not read transaction_id for " ) ;
2012-01-24 04:55:03 +04:00
if ( ! dm_config_get_uint32 ( sn , " chunk_size " , & seg - > chunk_size ) )
return SEG_LOG_ERROR ( " Could not read chunk_size " ) ;
2011-10-22 20:45:25 +04:00
2012-08-08 00:24:41 +04:00
if ( dm_config_has_node ( sn , " discards " ) & &
2012-08-08 00:59:06 +04:00
! dm_config_get_str ( sn , " discards " , & discards_str ) )
2012-08-08 00:24:41 +04:00
return SEG_LOG_ERROR ( " Could not read discards for " ) ;
2012-06-28 16:47:34 +04:00
2012-08-08 00:24:41 +04:00
if ( ! discards_str )
2012-08-09 13:24:37 +04:00
seg - > discards = THIN_DISCARDS_IGNORE ;
2012-08-08 00:24:41 +04:00
else if ( ! get_pool_discards ( discards_str , & seg - > discards ) )
return SEG_LOG_ERROR ( " Discards option unsupported for " ) ;
2012-06-28 16:47:34 +04:00
2011-10-20 14:30:39 +04:00
if ( dm_config_has_node ( sn , " low_water_mark " ) & &
! dm_config_get_uint64 ( sn , " low_water_mark " , & seg - > low_water_mark ) )
2011-09-29 12:56:38 +04:00
return SEG_LOG_ERROR ( " Could not read low_water_mark " ) ;
2012-01-24 04:55:03 +04:00
if ( ( seg - > chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE ) | |
( seg - > chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE ) )
return SEG_LOG_ERROR ( " Unsupported value %u for chunk_size " ,
2011-10-06 15:06:36 +04:00
seg - > device_id ) ;
2011-09-01 14:16:32 +04:00
if ( dm_config_has_node ( sn , " zero_new_blocks " ) & &
2011-08-30 18:55:15 +04:00
! dm_config_get_uint32 ( sn , " zero_new_blocks " , & seg - > zero_new_blocks ) )
2011-08-26 17:37:47 +04:00
return SEG_LOG_ERROR ( " Could not read zero_new_blocks for " ) ;
2011-10-17 18:17:09 +04:00
/* Read messages */
for ( ; sn ; sn = sn - > sib )
if ( ! ( sn - > v ) & & ! _thin_pool_add_message ( seg , sn - > key , sn - > child ) )
return_0 ;
2011-09-08 20:41:18 +04:00
return 1 ;
}
static int _thin_pool_text_import_area_count ( const struct dm_config_node * sn ,
uint32_t * area_count )
{
* area_count = 1 ;
2011-08-25 14:00:09 +04:00
return 1 ;
}
static int _thin_pool_text_export ( const struct lv_segment * seg , struct formatter * f )
{
2011-10-17 18:17:09 +04:00
unsigned cnt = 0 ;
2011-10-20 14:31:27 +04:00
const struct lv_thin_message * tmsg ;
2011-10-17 18:17:09 +04:00
2012-01-19 19:23:50 +04:00
outf ( f , " metadata = \" %s \" " , seg - > metadata_lv - > name ) ;
2011-10-22 20:45:25 +04:00
outf ( f , " pool = \" %s \" " , seg_lv ( seg , 0 ) - > name ) ;
2011-08-26 17:37:47 +04:00
outf ( f , " transaction_id = % " PRIu64 , seg - > transaction_id ) ;
2012-01-24 04:55:03 +04:00
outsize ( f , ( uint64_t ) seg - > chunk_size ,
" chunk_size = %u " , seg - > chunk_size ) ;
2011-10-17 18:17:09 +04:00
2012-08-08 00:24:41 +04:00
switch ( seg - > discards ) {
case THIN_DISCARDS_PASSDOWN :
case THIN_DISCARDS_NO_PASSDOWN :
case THIN_DISCARDS_IGNORE :
outf ( f , " discards = \" %s \" " , get_pool_discards_name ( seg - > discards ) ) ;
2012-06-28 16:47:34 +04:00
break ;
default :
2012-08-08 00:24:41 +04:00
log_error ( INTERNAL_ERROR " Invalid discards value %d. " , seg - > discards ) ;
2012-06-28 16:47:34 +04:00
return 0 ;
}
2011-10-20 14:30:39 +04:00
if ( seg - > low_water_mark )
outf ( f , " low_water_mark = % " PRIu64 , seg - > low_water_mark ) ;
2011-08-26 17:37:47 +04:00
if ( seg - > zero_new_blocks )
outf ( f , " zero_new_blocks = 1 " ) ;
2011-10-17 18:17:09 +04:00
dm_list_iterate_items ( tmsg , & seg - > thin_messages ) {
/* Extra validation */
switch ( tmsg - > type ) {
case DM_THIN_MESSAGE_CREATE_SNAP :
case DM_THIN_MESSAGE_CREATE_THIN :
if ( ! lv_is_thin_volume ( tmsg - > u . lv ) ) {
log_error ( INTERNAL_ERROR
" LV %s is not a thin volume. " ,
tmsg - > u . lv - > name ) ;
return 0 ;
}
break ;
default :
break ;
}
if ( ! cnt )
outnl ( f ) ;
outf ( f , " message%d { " , + + cnt ) ;
out_inc_indent ( f ) ;
switch ( tmsg - > type ) {
case DM_THIN_MESSAGE_CREATE_SNAP :
case DM_THIN_MESSAGE_CREATE_THIN :
outf ( f , " create = \" %s \" " , tmsg - > u . lv - > name ) ;
break ;
case DM_THIN_MESSAGE_DELETE :
outf ( f , " delete = %d " , tmsg - > u . delete_id ) ;
break ;
default :
log_error ( INTERNAL_ERROR " Passed unsupported message. " ) ;
return 0 ;
}
out_dec_indent ( f ) ;
outf ( f , " } " ) ;
}
2011-08-25 14:00:09 +04:00
return 1 ;
}
2011-08-24 12:27:49 +04:00
2011-09-29 12:56:38 +04:00
# ifdef DEVMAPPER_SUPPORT
2013-09-27 15:58:55 +04:00
static int _thin_target_present ( struct cmd_context * cmd ,
2014-02-24 16:13:20 +04:00
const struct lv_segment * seg __attribute__ ( ( unused ) ) ,
2013-09-27 15:58:55 +04:00
unsigned * attributes ) ;
2013-09-30 10:17:56 +04:00
static int _thin_pool_modules_needed ( struct dm_pool * mem ,
const struct lv_segment * seg __attribute__ ( ( unused ) ) ,
struct dm_list * modules )
{
if ( ! str_list_add ( mem , modules , _thin_pool_module ) ) {
log_error ( " String list allocation failed for thin_pool. " ) ;
return 0 ;
}
return 1 ;
}
static int _thin_modules_needed ( struct dm_pool * mem ,
const struct lv_segment * seg ,
struct dm_list * modules )
{
if ( ! _thin_pool_modules_needed ( mem , seg , modules ) )
return_0 ;
if ( ! str_list_add ( mem , modules , _thin_module ) ) {
log_error ( " String list allocation failed for thin. " ) ;
return 0 ;
}
return 1 ;
}
2011-09-29 12:56:38 +04:00
static int _thin_pool_add_target_line ( struct dev_manager * dm ,
2012-01-25 12:51:29 +04:00
struct dm_pool * mem ,
struct cmd_context * cmd ,
2011-10-11 12:51:56 +04:00
void * * target_state __attribute__ ( ( unused ) ) ,
struct lv_segment * seg ,
2012-01-25 12:51:29 +04:00
const struct lv_activate_opts * laopts ,
2011-10-11 12:51:56 +04:00
struct dm_tree_node * node , uint64_t len ,
uint32_t * pvmove_mirror_count __attribute__ ( ( unused ) ) )
2011-09-29 12:56:38 +04:00
{
2012-08-08 00:24:41 +04:00
static int _no_discards = 0 ;
2011-09-29 12:56:38 +04:00
char * metadata_dlid , * pool_dlid ;
2011-10-20 14:31:27 +04:00
const struct lv_thin_message * lmsg ;
2011-11-10 19:30:59 +04:00
const struct logical_volume * origin ;
2012-01-25 13:06:43 +04:00
struct lvinfo info ;
uint64_t transaction_id = 0 ;
2012-06-28 16:47:34 +04:00
unsigned attr ;
2014-02-24 16:13:20 +04:00
if ( ! _thin_target_present ( cmd , NULL , & attr ) )
2012-06-28 16:47:34 +04:00
return_0 ;
2011-09-29 12:56:38 +04:00
2013-07-19 19:28:43 +04:00
if ( ! seg - > metadata_lv ) {
log_error ( INTERNAL_ERROR " Thin pool is missing metadata device. " ) ;
return 0 ;
}
2012-02-02 17:37:51 +04:00
if ( ! ( attr & THIN_FEATURE_BLOCK_SIZE ) & &
( seg - > chunk_size & ( seg - > chunk_size - 1 ) ) ) {
log_error ( " Thin pool target does not support %uKiB chunk size "
2012-10-11 16:07:35 +04:00
" (needs kernel >= 3.6). " , seg - > chunk_size / 2 ) ;
2012-02-02 17:37:51 +04:00
return 0 ;
}
2014-03-11 20:13:47 +04:00
if ( ! ( metadata_dlid = build_dm_uuid ( mem , seg - > metadata_lv , NULL ) ) ) {
2011-10-17 18:17:30 +04:00
log_error ( " Failed to build uuid for metadata LV %s. " ,
2012-01-19 19:23:50 +04:00
seg - > metadata_lv - > name ) ;
2011-09-29 12:56:38 +04:00
return 0 ;
}
2014-03-11 20:13:47 +04:00
if ( ! ( pool_dlid = build_dm_uuid ( mem , seg_lv ( seg , 0 ) , NULL ) ) ) {
2011-10-17 18:17:30 +04:00
log_error ( " Failed to build uuid for pool LV %s. " ,
seg_lv ( seg , 0 ) - > name ) ;
2011-09-29 12:56:38 +04:00
return 0 ;
}
2011-10-17 18:17:09 +04:00
if ( ! dm_tree_node_add_thin_pool_target ( node , len , seg - > transaction_id ,
metadata_dlid , pool_dlid ,
2012-01-24 04:55:03 +04:00
seg - > chunk_size , seg - > low_water_mark ,
2011-09-29 12:56:38 +04:00
seg - > zero_new_blocks ? 0 : 1 ) )
return_0 ;
2012-08-09 13:25:41 +04:00
if ( attr & THIN_FEATURE_DISCARDS ) {
2012-11-26 14:20:13 +04:00
/* Use ignore for discards ignore or non-power-of-2 chunk_size and <1.5 target */
2012-08-08 00:59:06 +04:00
/* FIXME: Check whether underlying dev supports discards */
2012-11-26 14:20:13 +04:00
if ( ( ( ! ( attr & THIN_FEATURE_DISCARDS_NON_POWER_2 ) & &
( seg - > chunk_size & ( seg - > chunk_size - 1 ) ) ) | |
2012-12-10 13:22:48 +04:00
( seg - > discards = = THIN_DISCARDS_IGNORE ) ) ) {
if ( ! dm_tree_node_set_thin_pool_discard ( node , 1 , 0 ) )
return_0 ;
} else if ( ! dm_tree_node_set_thin_pool_discard ( node , 0 ,
( seg - > discards = = THIN_DISCARDS_NO_PASSDOWN ) ) )
2012-08-08 00:59:06 +04:00
return_0 ;
2012-08-09 13:25:41 +04:00
} else if ( seg - > discards ! = THIN_DISCARDS_IGNORE )
2012-08-08 00:59:06 +04:00
log_warn_suppress ( _no_discards + + , " WARNING: Thin pool target does "
" not support discards (needs kernel >= 3.4). " ) ;
2012-06-28 16:47:34 +04:00
2012-01-25 13:06:43 +04:00
/*
* Add messages only for activation tree .
* Otherwise avoid checking for existence of suspended origin .
* Also transation_id is checked only when snapshot origin is active .
* ( This might change later )
*/
2013-01-17 13:35:27 +04:00
if ( ! laopts - > send_messages )
2012-01-25 13:06:43 +04:00
return 1 ;
2011-10-20 14:32:29 +04:00
dm_list_iterate_items ( lmsg , & seg - > thin_messages ) {
switch ( lmsg - > type ) {
case DM_THIN_MESSAGE_CREATE_THIN :
2011-11-10 19:30:59 +04:00
origin = first_seg ( lmsg - > u . lv ) - > origin ;
2012-01-25 13:06:43 +04:00
/* Check if the origin is suspended */
2013-07-15 13:47:10 +04:00
if ( origin & & lv_info ( cmd , origin , 1 , & info , 0 , 0 ) & &
2012-01-25 13:06:43 +04:00
info . exists & & ! info . suspended ) {
/* Origin is not suspended, but the transaction may have been
* already transfered , so test for transaction_id and
* allow to pass in the message for dmtree processing
* so it will skip all messages later .
*/
if ( ! lv_thin_pool_transaction_id ( seg - > lv , & transaction_id ) )
return_0 ; /* Thin pool should exist and work */
if ( transaction_id ! = seg - > transaction_id ) {
log_error ( " Can't create snapshot %s as origin %s is not suspended. " ,
lmsg - > u . lv - > name , origin - > name ) ;
return 0 ;
}
}
2013-01-08 02:30:29 +04:00
log_debug_activation ( " Thin pool create_%s %s. " , ( ! origin ) ? " thin " : " snap " , lmsg - > u . lv - > name ) ;
2011-11-03 18:45:01 +04:00
if ( ! dm_tree_node_add_thin_pool_message ( node ,
2011-11-10 19:30:59 +04:00
( ! origin ) ? lmsg - > type : DM_THIN_MESSAGE_CREATE_SNAP ,
2011-11-03 18:45:01 +04:00
first_seg ( lmsg - > u . lv ) - > device_id ,
2011-11-10 19:30:59 +04:00
( ! origin ) ? 0 : first_seg ( origin ) - > device_id ) )
2011-10-20 14:32:29 +04:00
return_0 ;
break ;
case DM_THIN_MESSAGE_DELETE :
2013-01-08 02:30:29 +04:00
log_debug_activation ( " Thin pool delete %u. " , lmsg - > u . delete_id ) ;
2011-11-03 18:45:01 +04:00
if ( ! dm_tree_node_add_thin_pool_message ( node ,
lmsg - > type ,
lmsg - > u . delete_id , 0 ) )
2011-10-20 14:32:29 +04:00
return_0 ;
break ;
default :
log_error ( INTERNAL_ERROR " Unsupported message. " ) ;
return 0 ;
2011-10-17 18:17:09 +04:00
}
2011-10-20 14:32:29 +04:00
}
2011-10-17 18:17:09 +04:00
2011-10-20 14:32:29 +04:00
if ( ! dm_list_empty ( & seg - > thin_messages ) ) {
/* Messages were passed, modify transaction_id as the last one */
2013-01-08 02:30:29 +04:00
log_debug_activation ( " Thin pool set transaction id % " PRIu64 " . " , seg - > transaction_id ) ;
2011-11-03 18:45:01 +04:00
if ( ! dm_tree_node_add_thin_pool_message ( node ,
DM_THIN_MESSAGE_SET_TRANSACTION_ID ,
seg - > transaction_id - 1 ,
seg - > transaction_id ) )
2011-10-17 18:17:09 +04:00
return_0 ;
}
2011-09-29 12:56:38 +04:00
return 1 ;
}
2011-12-21 17:08:11 +04:00
static int _thin_pool_target_percent ( void * * target_state __attribute__ ( ( unused ) ) ,
percent_t * percent ,
struct dm_pool * mem ,
struct cmd_context * cmd __attribute__ ( ( unused ) ) ,
2012-01-19 19:21:23 +04:00
struct lv_segment * seg ,
2011-12-21 17:08:11 +04:00
char * params ,
uint64_t * total_numerator ,
uint64_t * total_denominator )
{
struct dm_status_thin_pool * s ;
if ( ! dm_get_status_thin_pool ( mem , params , & s ) )
return_0 ;
2012-01-19 19:42:18 +04:00
/* With 'seg' report metadata percent, otherwice data percent */
2012-01-19 19:21:23 +04:00
if ( seg ) {
* percent = make_percent ( s - > used_metadata_blocks ,
s - > total_metadata_blocks ) ;
* total_numerator + = s - > used_metadata_blocks ;
* total_denominator + = s - > total_metadata_blocks ;
} else {
* percent = make_percent ( s - > used_data_blocks ,
s - > total_data_blocks ) ;
* total_numerator + = s - > used_data_blocks ;
* total_denominator + = s - > total_data_blocks ;
}
2011-12-21 17:08:11 +04:00
return 1 ;
}
2012-03-20 21:42:19 +04:00
# ifdef DMEVENTD
static const char * _get_thin_dso_path ( struct cmd_context * cmd )
{
2013-06-25 14:29:54 +04:00
return get_monitor_dso_path ( cmd , find_config_tree_str ( cmd , dmeventd_thin_library_CFG , NULL ) ) ;
2012-03-20 21:42:19 +04:00
}
/* FIXME Cache this */
static int _target_registered ( struct lv_segment * seg , int * pending )
{
return target_registered_with_dmeventd ( seg - > lv - > vg - > cmd ,
_get_thin_dso_path ( seg - > lv - > vg - > cmd ) ,
seg - > lv , pending ) ;
}
/* FIXME This gets run while suspended and performs banned operations. */
static int _target_set_events ( struct lv_segment * seg , int evmask , int set )
{
/* FIXME Make timeout (10) configurable */
return target_register_events ( seg - > lv - > vg - > cmd ,
_get_thin_dso_path ( seg - > lv - > vg - > cmd ) ,
seg - > lv , evmask , set , 10 ) ;
}
static int _target_register_events ( struct lv_segment * seg ,
int events )
{
return _target_set_events ( seg , events , 1 ) ;
}
static int _target_unregister_events ( struct lv_segment * seg ,
int events )
{
return _target_set_events ( seg , events , 0 ) ;
}
2013-09-27 15:58:55 +04:00
2012-03-20 21:42:19 +04:00
# endif /* DMEVENTD */
2011-12-21 17:08:11 +04:00
# endif /* DEVMAPPER_SUPPORT */
2011-09-29 12:56:38 +04:00
2011-08-24 12:27:49 +04:00
static const char * _thin_name ( const struct lv_segment * seg )
{
return seg - > segtype - > name ;
}
2011-10-11 12:51:56 +04:00
static int _thin_text_import ( struct lv_segment * seg ,
const struct dm_config_node * sn ,
struct dm_hash_table * pv_hash __attribute__ ( ( unused ) ) )
2011-08-24 12:27:49 +04:00
{
2011-09-01 14:16:32 +04:00
const char * lv_name ;
2013-11-29 18:51:28 +04:00
struct logical_volume * pool_lv , * origin = NULL , * external_lv = NULL , * merge_lv = NULL ;
2011-08-26 17:37:47 +04:00
2011-09-01 14:16:32 +04:00
if ( ! dm_config_get_str ( sn , " thin_pool " , & lv_name ) )
2011-08-26 17:37:47 +04:00
return SEG_LOG_ERROR ( " Thin pool must be a string in " ) ;
2011-09-07 02:43:56 +04:00
if ( ! ( pool_lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
2011-09-01 14:16:32 +04:00
return SEG_LOG_ERROR ( " Unknown thin pool %s in " , lv_name ) ;
2011-08-26 17:37:47 +04:00
2011-10-21 15:38:35 +04:00
if ( ! dm_config_get_uint64 ( sn , " transaction_id " , & seg - > transaction_id ) )
return SEG_LOG_ERROR ( " Could not read transaction_id for " ) ;
2011-09-01 14:16:32 +04:00
if ( dm_config_has_node ( sn , " origin " ) ) {
if ( ! dm_config_get_str ( sn , " origin " , & lv_name ) )
2011-09-07 02:35:44 +04:00
return SEG_LOG_ERROR ( " Origin must be a string in " ) ;
2011-08-26 17:37:47 +04:00
2011-11-07 15:03:47 +04:00
if ( ! ( origin = find_lv ( seg - > lv - > vg , lv_name ) ) )
2011-09-01 14:16:32 +04:00
return SEG_LOG_ERROR ( " Unknown origin %s in " , lv_name ) ;
2011-08-26 17:37:47 +04:00
}
2013-11-29 18:51:28 +04:00
if ( dm_config_has_node ( sn , " merge " ) ) {
if ( ! dm_config_get_str ( sn , " merge " , & lv_name ) )
return SEG_LOG_ERROR ( " Merge lv must be a string in " ) ;
if ( ! ( merge_lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
return SEG_LOG_ERROR ( " Unknown merge lv %s in " , lv_name ) ;
}
2011-09-29 12:56:38 +04:00
if ( ! dm_config_get_uint32 ( sn , " device_id " , & seg - > device_id ) )
2011-08-26 17:37:47 +04:00
return SEG_LOG_ERROR ( " Could not read device_id for " ) ;
2011-10-06 15:06:36 +04:00
if ( seg - > device_id > DM_THIN_MAX_DEVICE_ID )
return SEG_LOG_ERROR ( " Unsupported value %u for device_id " ,
seg - > device_id ) ;
2013-02-21 13:25:44 +04:00
if ( dm_config_has_node ( sn , " external_origin " ) ) {
if ( ! dm_config_get_str ( sn , " external_origin " , & lv_name ) )
return SEG_LOG_ERROR ( " External origin must be a string in " ) ;
if ( ! ( external_lv = find_lv ( seg - > lv - > vg , lv_name ) ) )
return SEG_LOG_ERROR ( " Unknown external origin %s in " , lv_name ) ;
}
2013-11-29 18:51:28 +04:00
if ( ! attach_pool_lv ( seg , pool_lv , origin , merge_lv ) )
2011-11-07 15:03:47 +04:00
return_0 ;
2013-02-21 13:25:44 +04:00
if ( ! attach_thin_external_origin ( seg , external_lv ) )
return_0 ;
2011-08-24 12:27:49 +04:00
return 1 ;
}
static int _thin_text_export ( const struct lv_segment * seg , struct formatter * f )
{
2011-09-07 02:43:56 +04:00
outf ( f , " thin_pool = \" %s \" " , seg - > pool_lv - > name ) ;
2011-10-21 15:38:35 +04:00
outf ( f , " transaction_id = % " PRIu64 , seg - > transaction_id ) ;
2011-09-29 12:56:38 +04:00
outf ( f , " device_id = %d " , seg - > device_id ) ;
2011-08-26 17:37:47 +04:00
2013-02-21 13:25:44 +04:00
if ( seg - > external_lv )
outf ( f , " external_origin = \" %s \" " , seg - > external_lv - > name ) ;
2011-08-26 21:40:53 +04:00
if ( seg - > origin )
outf ( f , " origin = \" %s \" " , seg - > origin - > name ) ;
2013-11-29 18:51:28 +04:00
if ( seg - > merge_lv )
outf ( f , " merge = \" %s \" " , seg - > merge_lv - > name ) ;
2011-08-26 17:37:47 +04:00
2011-08-24 12:27:49 +04:00
return 1 ;
}
# ifdef DEVMAPPER_SUPPORT
2011-09-29 12:56:38 +04:00
static int _thin_add_target_line ( struct dev_manager * dm ,
2011-10-31 02:00:57 +04:00
struct dm_pool * mem ,
2011-09-29 12:56:38 +04:00
struct cmd_context * cmd __attribute__ ( ( unused ) ) ,
void * * target_state __attribute__ ( ( unused ) ) ,
struct lv_segment * seg ,
2013-11-29 18:51:28 +04:00
const struct lv_activate_opts * laopts ,
2011-09-29 12:56:38 +04:00
struct dm_tree_node * node , uint64_t len ,
uint32_t * pvmove_mirror_count __attribute__ ( ( unused ) ) )
{
2013-02-21 13:25:44 +04:00
char * pool_dlid , * external_dlid ;
2011-11-03 18:52:09 +04:00
uint32_t device_id = seg - > device_id ;
2014-01-23 16:10:29 +04:00
unsigned attr ;
2011-09-29 12:56:38 +04:00
2013-06-14 11:51:09 +04:00
if ( ! seg - > pool_lv ) {
log_error ( INTERNAL_ERROR " Segment %s has no pool. " ,
seg - > lv - > name ) ;
return 0 ;
}
2014-03-11 20:13:47 +04:00
if ( ! ( pool_dlid = build_dm_uuid ( mem , seg - > pool_lv , lv_layer ( seg - > pool_lv ) ) ) ) {
2011-11-03 18:52:09 +04:00
log_error ( " Failed to build uuid for pool LV %s. " ,
seg - > pool_lv - > name ) ;
2011-09-29 12:56:38 +04:00
return 0 ;
}
2013-11-29 18:51:28 +04:00
if ( ! laopts - > no_merging ) {
2014-02-18 00:48:27 +04:00
if ( seg - > merge_lv ) {
log_error ( INTERNAL_ERROR " Failed to add merged segment of %s. " ,
seg - > lv - > name ) ;
return 0 ;
}
2013-11-29 18:51:28 +04:00
/*
* merge support for thinp snapshots is implemented by
* simply swapping the thinp device_id of the snapshot
* and origin .
*/
2014-02-18 00:48:27 +04:00
if ( lv_is_merging_origin ( seg - > lv ) & & seg_is_thin_volume ( find_snapshot ( seg - > lv ) ) )
2013-11-29 18:51:28 +04:00
/* origin, use merging snapshot's device_id */
device_id = find_snapshot ( seg - > lv ) - > device_id ;
}
2011-11-03 18:52:09 +04:00
if ( ! dm_tree_node_add_thin_target ( node , len , pool_dlid , device_id ) )
2011-09-29 12:56:38 +04:00
return_0 ;
2013-02-21 13:25:44 +04:00
/* Add external origin LV */
if ( seg - > external_lv ) {
2014-01-29 17:27:13 +04:00
if ( ! pool_supports_external_origin ( first_seg ( seg - > pool_lv ) , seg - > external_lv ) )
return_0 ;
2014-01-23 16:10:29 +04:00
if ( seg - > external_lv - > size < seg - > lv - > size ) {
/* Validate target supports smaller external origin */
2014-02-24 16:13:20 +04:00
if ( ! _thin_target_present ( cmd , NULL , & attr ) | |
2014-01-23 16:10:29 +04:00
! ( attr & THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND ) ) {
log_error ( " Thin target does not support smaller size of external origin LV %s. " ,
seg - > external_lv - > name ) ;
return 0 ;
}
}
2014-03-11 20:13:47 +04:00
if ( ! ( external_dlid = build_dm_uuid ( mem , seg - > external_lv ,
2013-02-21 13:25:44 +04:00
lv_layer ( seg - > external_lv ) ) ) ) {
log_error ( " Failed to build uuid for external origin LV %s. " ,
seg - > external_lv - > name ) ;
return 0 ;
}
if ( ! dm_tree_node_set_thin_external_origin ( node , external_dlid ) )
return_0 ;
}
2011-09-29 12:56:38 +04:00
return 1 ;
}
2012-01-19 19:27:54 +04:00
static int _thin_target_percent ( void * * target_state __attribute__ ( ( unused ) ) ,
percent_t * percent ,
struct dm_pool * mem ,
struct cmd_context * cmd __attribute__ ( ( unused ) ) ,
struct lv_segment * seg ,
char * params ,
uint64_t * total_numerator ,
uint64_t * total_denominator )
{
struct dm_status_thin * s ;
/* Status for thin device is in sectors */
if ( ! dm_get_status_thin ( mem , params , & s ) )
return_0 ;
if ( seg ) {
* percent = make_percent ( s - > mapped_sectors , seg - > lv - > size ) ;
* total_denominator + = seg - > lv - > size ;
} else {
/* No lv_segment info here */
* percent = PERCENT_INVALID ;
/* FIXME: Using denominator to pass the mapped info upward? */
* total_denominator + = s - > highest_mapped_sector ;
}
* total_numerator + = s - > mapped_sectors ;
return 1 ;
}
2011-08-24 12:27:49 +04:00
static int _thin_target_present ( struct cmd_context * cmd ,
2014-02-24 16:13:20 +04:00
const struct lv_segment * seg __attribute__ ( ( unused ) ) ,
2012-05-25 15:38:03 +04:00
unsigned * attributes )
2011-08-24 12:27:49 +04:00
{
2012-12-03 16:03:41 +04:00
/* List of features with their kernel target version */
static const struct feature {
uint32_t maj ;
uint32_t min ;
unsigned thin_feature ;
const char * feature ;
} const _features [ ] = {
{ 1 , 1 , THIN_FEATURE_DISCARDS , " discards " } ,
{ 1 , 1 , THIN_FEATURE_EXTERNAL_ORIGIN , " external_origin " } ,
{ 1 , 4 , THIN_FEATURE_BLOCK_SIZE , " block_size " } ,
{ 1 , 5 , THIN_FEATURE_DISCARDS_NON_POWER_2 , " discards_non_power_2 " } ,
2014-01-21 16:48:57 +04:00
{ 1 , 10 , THIN_FEATURE_METADATA_RESIZE , " metadata_resize " } ,
2014-01-23 16:10:29 +04:00
{ 9 , 11 , THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND , " external_origin_extend " } ,
2012-12-03 16:03:41 +04:00
} ;
static const char _lvmconf [ ] = " global/thin_disabled_features " ;
2011-08-24 12:27:49 +04:00
static int _checked = 0 ;
static int _present = 0 ;
2012-12-03 14:52:04 +04:00
static unsigned _attrs = 0 ;
2012-05-25 15:38:03 +04:00
uint32_t maj , min , patchlevel ;
2012-12-03 14:52:04 +04:00
unsigned i ;
2012-12-03 16:03:41 +04:00
const struct dm_config_node * cn ;
const struct dm_config_value * cv ;
const char * str ;
2011-08-24 12:27:49 +04:00
if ( ! _checked ) {
2013-07-18 18:06:26 +04:00
_present = target_present ( cmd , _thin_pool_module , 1 ) ;
2012-05-25 15:38:03 +04:00
2013-07-18 18:06:26 +04:00
if ( ! target_version ( _thin_pool_module , & maj , & min , & patchlevel ) ) {
log_error ( " Cannot read %s target version. " , _thin_pool_module ) ;
2012-05-25 15:38:03 +04:00
return 0 ;
}
2012-12-03 14:52:04 +04:00
for ( i = 0 ; i < sizeof ( _features ) / sizeof ( * _features ) ; i + + )
2014-01-23 16:47:23 +04:00
if ( ( maj > _features [ i ] . maj ) | |
( maj = = _features [ i ] . maj & & min > = _features [ i ] . min ) )
2012-12-03 14:52:04 +04:00
_attrs | = _features [ i ] . thin_feature ;
else
2013-07-18 18:06:26 +04:00
log_very_verbose ( " Target %s does not support %s. " ,
_thin_pool_module ,
2012-12-03 14:52:04 +04:00
_features [ i ] . feature ) ;
2012-11-26 14:04:00 +04:00
2011-08-24 12:27:49 +04:00
_checked = 1 ;
}
2012-12-03 14:52:04 +04:00
if ( attributes ) {
2012-12-03 16:03:41 +04:00
if ( ! _feature_mask ) {
/* Support runtime lvm.conf changes, N.B. avoid 32 feature */
2013-06-25 14:29:33 +04:00
if ( ( cn = find_config_tree_node ( cmd , global_thin_disabled_features_CFG , NULL ) ) ) {
2012-12-03 16:03:41 +04:00
for ( cv = cn - > v ; cv ; cv = cv - > next ) {
if ( cv - > type ! = DM_CFG_STRING ) {
log_error ( " Ignoring invalid string in config file %s. " ,
_lvmconf ) ;
continue ;
}
str = cv - > v . str ;
2014-03-12 18:53:20 +04:00
if ( ! * str )
2012-12-03 16:03:41 +04:00
continue ;
for ( i = 0 ; i < sizeof ( _features ) / sizeof ( * _features ) ; i + + )
if ( strcasecmp ( str , _features [ i ] . feature ) = = 0 )
_feature_mask | = _features [ i ] . thin_feature ;
}
}
_feature_mask = ~ _feature_mask ;
2012-12-03 14:52:04 +04:00
for ( i = 0 ; i < sizeof ( _features ) / sizeof ( * _features ) ; i + + )
if ( ( _attrs & _features [ i ] . thin_feature ) & &
! ( _feature_mask & _features [ i ] . thin_feature ) )
2013-07-18 18:06:26 +04:00
log_very_verbose ( " Target %s %s support disabled by %s " ,
_thin_pool_module ,
2012-12-03 14:52:04 +04:00
_features [ i ] . feature , _lvmconf ) ;
}
* attributes = _attrs & _feature_mask ;
}
2012-05-25 15:38:03 +04:00
2011-08-24 12:27:49 +04:00
return _present ;
}
# endif
static void _thin_destroy ( struct segment_type * segtype )
{
dm_free ( segtype ) ;
}
2011-08-25 14:00:09 +04:00
static struct segtype_handler _thin_pool_ops = {
. name = _thin_pool_name ,
. text_import = _thin_pool_text_import ,
2011-09-08 20:41:18 +04:00
. text_import_area_count = _thin_pool_text_import_area_count ,
2011-08-25 14:00:09 +04:00
. text_export = _thin_pool_text_export ,
2011-09-29 12:56:38 +04:00
# ifdef DEVMAPPER_SUPPORT
. add_target_line = _thin_pool_add_target_line ,
2011-12-21 17:08:11 +04:00
. target_percent = _thin_pool_target_percent ,
2011-09-29 12:56:38 +04:00
. target_present = _thin_target_present ,
2012-03-20 21:42:19 +04:00
# ifdef DMEVENTD
. target_monitored = _target_registered ,
. target_monitor_events = _target_register_events ,
. target_unmonitor_events = _target_unregister_events ,
# endif /* DMEVENTD */
2011-09-29 12:56:38 +04:00
# endif
2013-09-27 15:58:55 +04:00
# ifdef DEVMAPPER_SUPPORT
2013-07-18 18:06:26 +04:00
. modules_needed = _thin_pool_modules_needed ,
2013-09-27 15:58:55 +04:00
# endif
2011-08-25 14:00:09 +04:00
. destroy = _thin_destroy ,
} ;
2011-08-24 12:27:49 +04:00
static struct segtype_handler _thin_ops = {
. name = _thin_name ,
. text_import = _thin_text_import ,
. text_export = _thin_text_export ,
# ifdef DEVMAPPER_SUPPORT
2011-09-29 12:56:38 +04:00
. add_target_line = _thin_add_target_line ,
2012-01-19 19:27:54 +04:00
. target_percent = _thin_target_percent ,
2011-08-24 12:27:49 +04:00
. target_present = _thin_target_present ,
. modules_needed = _thin_modules_needed ,
2013-09-27 15:58:55 +04:00
# endif
2011-08-24 12:27:49 +04:00
. destroy = _thin_destroy ,
} ;
# ifdef THIN_INTERNAL
2011-08-25 14:00:09 +04:00
int init_thin_segtypes ( struct cmd_context * cmd , struct segtype_library * seglib )
# else /* Shared */
int init_multiple_segtypes ( struct cmd_context * cmd , struct segtype_library * seglib ) ;
int init_multiple_segtypes ( struct cmd_context * cmd , struct segtype_library * seglib )
2011-08-24 12:27:49 +04:00
# endif
{
2011-08-25 14:00:09 +04:00
static const struct {
struct segtype_handler * ops ;
const char name [ 16 ] ;
uint32_t flags ;
} reg_segtypes [ ] = {
2011-12-21 16:54:19 +04:00
{ & _thin_pool_ops , " thin-pool " , SEG_THIN_POOL } ,
2011-09-06 04:26:42 +04:00
/* FIXME Maybe use SEG_THIN_VOLUME instead of SEG_VIRTUAL */
{ & _thin_ops , " thin " , SEG_THIN_VOLUME | SEG_VIRTUAL }
2011-08-25 14:00:09 +04:00
} ;
struct segment_type * segtype ;
unsigned i ;
for ( i = 0 ; i < sizeof ( reg_segtypes ) / sizeof ( reg_segtypes [ 0 ] ) ; + + i ) {
segtype = dm_zalloc ( sizeof ( * segtype ) ) ;
if ( ! segtype ) {
log_error ( " Failed to allocate memory for %s segtype " ,
reg_segtypes [ i ] . name ) ;
return 0 ;
}
segtype - > ops = reg_segtypes [ i ] . ops ;
segtype - > name = reg_segtypes [ i ] . name ;
segtype - > flags = reg_segtypes [ i ] . flags ;
2011-08-24 12:27:49 +04:00
# ifdef DEVMAPPER_SUPPORT
# ifdef DMEVENTD
2012-02-15 17:49:51 +04:00
if ( ( reg_segtypes [ i ] . flags & SEG_THIN_POOL ) & &
_get_thin_dso_path ( cmd ) )
2011-12-21 17:08:11 +04:00
segtype - > flags | = SEG_MONITORED ;
# endif /* DMEVENTD */
2011-08-24 12:27:49 +04:00
# endif
2012-02-28 18:23:41 +04:00
if ( ! lvm_register_segtype ( seglib , segtype ) )
/* segtype is already destroyed */
2011-08-25 14:00:09 +04:00
return_0 ;
2011-08-24 12:27:49 +04:00
2011-08-25 14:00:09 +04:00
log_very_verbose ( " Initialised segtype: %s " , segtype - > name ) ;
}
2012-12-03 14:52:04 +04:00
2012-12-03 16:03:41 +04:00
/* Reset mask for recalc */
_feature_mask = 0 ;
2012-12-03 14:52:04 +04:00
2011-08-25 14:00:09 +04:00
return 1 ;
2011-08-24 12:27:49 +04:00
}