2002-02-25 15:02:33 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2002-02-25 15:02:33 +03:00
*
2004-03-30 23:35:44 +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
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser 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
2002-02-25 15:02:33 +03:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2003-09-18 00:35:57 +04:00
# include "str_list.h"
2002-02-25 15:02:33 +03:00
# include "dev_manager.h"
2002-02-25 19:53:12 +03:00
# include "lvm-string.h"
2002-02-26 14:49:17 +03:00
# include "fs.h"
2003-04-25 02:09:13 +04:00
# include "defaults.h"
2004-09-16 22:40:56 +04:00
# include "segtype.h"
2004-05-05 01:25:57 +04:00
# include "display.h"
2003-11-13 21:47:22 +03:00
# include "toolcontext.h"
2004-05-05 01:25:57 +04:00
# include "targets.h"
# include "config.h"
2005-10-25 23:08:21 +04:00
# include "filter.h"
2007-01-26 00:22:30 +03:00
# include "activate.h"
2002-02-25 15:02:33 +03:00
2002-02-26 17:44:13 +03:00
# include <limits.h>
2002-03-07 20:37:38 +03:00
# include <dirent.h>
2002-02-25 15:02:33 +03:00
2003-08-20 16:53:57 +04:00
# define MAX_TARGET_PARAMSIZE 50000
2005-10-25 23:08:21 +04:00
# define UUID_PREFIX "LVM-"
2003-08-20 16:53:57 +04:00
2002-03-16 01:59:12 +03:00
typedef enum {
2005-11-09 01:52:26 +03:00
PRELOAD ,
2002-03-16 01:59:12 +03:00
ACTIVATE ,
2002-03-27 21:17:43 +03:00
DEACTIVATE ,
2002-03-16 01:59:12 +03:00
SUSPEND ,
2006-08-09 01:20:00 +04:00
SUSPEND_WITH_LOCKFS ,
2005-11-09 01:52:26 +03:00
CLEAN
2002-03-27 21:17:43 +03:00
} action_t ;
2002-03-16 01:59:12 +03:00
2002-02-25 15:02:33 +03:00
struct dev_manager {
2005-10-17 03:03:59 +04:00
struct dm_pool * mem ;
2002-02-25 15:02:33 +03:00
2004-05-05 01:25:57 +04:00
struct cmd_context * cmd ;
void * target_state ;
2003-05-06 16:00:29 +04:00
uint32_t pvmove_mirror_count ;
2009-05-20 13:52:37 +04:00
int flush_required ;
2003-04-25 02:09:13 +04:00
2002-02-25 15:02:33 +03:00
char * vg_name ;
} ;
2005-11-09 01:52:26 +03:00
struct lv_layer {
struct logical_volume * lv ;
const char * old_name ;
} ;
2002-03-07 19:48:46 +03:00
2005-10-17 03:03:59 +04:00
static char * _build_dlid ( struct dm_pool * mem , const char * lvid , const char * layer )
2002-03-19 02:25:50 +03:00
{
char * dlid ;
2002-12-20 02:25:55 +03:00
size_t len ;
2002-03-19 02:25:50 +03:00
if ( ! layer )
layer = " " ;
2005-10-25 23:08:21 +04:00
len = sizeof ( UUID_PREFIX ) + sizeof ( union lvid ) + strlen ( layer ) ;
2002-03-19 02:25:50 +03:00
2005-10-17 03:03:59 +04:00
if ( ! ( dlid = dm_pool_alloc ( mem , len ) ) ) {
2005-11-09 01:52:26 +03:00
log_error ( " _build_dlid: pool allocation failed for % " PRIsize_t
" %s %s. " , len , lvid , layer ) ;
2002-03-19 02:25:50 +03:00
return NULL ;
}
2005-10-25 23:08:21 +04:00
sprintf ( dlid , UUID_PREFIX " %s%s%s " , lvid , ( * layer ) ? " - " : " " , layer ) ;
2002-03-19 02:25:50 +03:00
return dlid ;
}
2005-10-21 01:07:57 +04:00
char * build_dlid ( struct dev_manager * dm , const char * lvid , const char * layer )
{
return _build_dlid ( dm - > mem , lvid , layer ) ;
}
2006-04-19 19:33:07 +04:00
static int _read_only_lv ( struct logical_volume * lv )
2005-11-09 01:52:26 +03:00
{
return ( ! ( lv - > vg - > status & LVM_WRITE ) | | ! ( lv - > status & LVM_WRITE ) ) ;
}
2002-02-25 17:46:57 +03:00
/*
* Low level device - layer operations .
*/
2003-05-06 16:00:29 +04:00
static struct dm_task * _setup_task ( const char * name , const char * uuid ,
2008-12-19 18:23:03 +03:00
uint32_t * event_nr , int task ,
uint32_t major , uint32_t minor )
2002-02-25 17:46:57 +03:00
{
struct dm_task * dmt ;
2008-01-30 16:19:47 +03:00
if ( ! ( dmt = dm_task_create ( task ) ) )
return_NULL ;
2002-02-25 17:46:57 +03:00
2002-03-25 21:54:59 +03:00
if ( name )
dm_task_set_name ( dmt , name ) ;
if ( uuid & & * uuid )
dm_task_set_uuid ( dmt , uuid ) ;
2003-04-30 19:26:25 +04:00
if ( event_nr )
dm_task_set_event_nr ( dmt , * event_nr ) ;
2009-06-18 00:55:24 +04:00
if ( major )
dm_task_set_major_minor ( dmt , major , minor , 1 ) ;
2008-12-19 18:23:03 +03:00
2002-02-25 17:46:57 +03:00
return dmt ;
}
2005-10-19 17:59:18 +04:00
static int _info_run ( const char * name , const char * dlid , struct dm_info * info ,
2007-11-12 23:51:54 +03:00
uint32_t * read_ahead , int mknodes , int with_open_count ,
2008-12-19 18:23:03 +03:00
int with_read_ahead , uint32_t major , uint32_t minor )
2002-03-15 00:17:30 +03:00
{
int r = 0 ;
struct dm_task * dmt ;
2003-11-13 17:11:41 +03:00
int dmtask ;
2002-03-15 00:17:30 +03:00
2003-11-13 17:11:41 +03:00
dmtask = mknodes ? DM_DEVICE_MKNODES : DM_DEVICE_INFO ;
2008-12-19 18:23:03 +03:00
if ( ! ( dmt = _setup_task ( name , dlid , 0 , dmtask , major , minor ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2002-03-15 00:17:30 +03:00
2005-01-13 01:58:21 +03:00
if ( ! with_open_count )
if ( ! dm_task_no_open_count ( dmt ) )
log_error ( " Failed to disable open_count " ) ;
2005-11-09 01:52:26 +03:00
if ( ! dm_task_run ( dmt ) )
goto_out ;
2002-03-15 00:17:30 +03:00
2005-11-09 01:52:26 +03:00
if ( ! dm_task_get_info ( dmt , info ) )
goto_out ;
2002-03-19 02:25:50 +03:00
2009-05-13 18:13:54 +04:00
if ( with_read_ahead & & info - > exists ) {
2007-12-03 21:00:38 +03:00
if ( ! dm_task_get_read_ahead ( dmt , read_ahead ) )
goto_out ;
} else if ( read_ahead )
2007-11-29 18:04:12 +03:00
* read_ahead = DM_READ_AHEAD_NONE ;
2007-11-12 23:51:54 +03:00
2002-03-15 00:17:30 +03:00
r = 1 ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2002-02-25 17:46:57 +03:00
2007-01-26 00:22:30 +03:00
int device_is_usable ( dev_t dev )
{
struct dm_task * dmt ;
struct dm_info info ;
2007-01-26 02:03:48 +03:00
const char * name ;
2008-01-30 17:00:02 +03:00
uint64_t start , length ;
char * target_type = NULL ;
char * params ;
2007-01-26 02:03:48 +03:00
void * next = NULL ;
2007-01-26 00:22:30 +03:00
int r = 0 ;
2007-01-26 02:03:48 +03:00
if ( ! ( dmt = dm_task_create ( DM_DEVICE_STATUS ) ) ) {
2007-01-26 00:22:30 +03:00
log_error ( " Failed to allocate dm_task struct to check dev status " ) ;
return 0 ;
}
2009-06-18 00:55:24 +04:00
if ( ! dm_task_set_major_minor ( dmt , MAJOR ( dev ) , MINOR ( dev ) , 1 ) )
2007-01-26 00:22:30 +03:00
goto_out ;
if ( ! dm_task_run ( dmt ) ) {
log_error ( " Failed to get state of mapped device " ) ;
goto out ;
}
if ( ! dm_task_get_info ( dmt , & info ) )
goto_out ;
if ( ! info . exists | | info . suspended )
goto out ;
2007-01-26 02:03:48 +03:00
name = dm_task_get_name ( dmt ) ;
2007-01-26 00:22:30 +03:00
/* FIXME Also check for mirror block_on_error and mpath no paths */
2007-01-26 02:03:48 +03:00
/* For now, we exclude all mirrors */
2008-01-30 17:00:02 +03:00
do {
next = dm_get_next_target ( dmt , next , & start , & length ,
& target_type , & params ) ;
/* Skip if target type doesn't match */
if ( target_type & & ! strcmp ( target_type , " mirror " ) )
2007-01-26 02:03:48 +03:00
goto out ;
2008-01-30 17:00:02 +03:00
} while ( next ) ;
2007-01-26 00:22:30 +03:00
/* FIXME Also check dependencies? */
r = 1 ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2005-10-19 17:59:18 +04:00
static int _info ( const char * name , const char * dlid , int mknodes ,
2007-11-12 23:51:54 +03:00
int with_open_count , int with_read_ahead ,
struct dm_info * info , uint32_t * read_ahead )
2002-03-25 21:54:59 +03:00
{
2005-10-26 19:00:51 +04:00
if ( ! mknodes & & dlid & & * dlid ) {
2007-11-12 23:51:54 +03:00
if ( _info_run ( NULL , dlid , info , read_ahead , 0 , with_open_count ,
2008-12-19 18:23:03 +03:00
with_read_ahead , 0 , 0 ) & &
2005-10-26 19:00:51 +04:00
info - > exists )
return 1 ;
2005-10-26 21:56:31 +04:00
else if ( _info_run ( NULL , dlid + sizeof ( UUID_PREFIX ) - 1 , info ,
2007-11-12 23:51:54 +03:00
read_ahead , 0 , with_open_count ,
2008-12-19 18:23:03 +03:00
with_read_ahead , 0 , 0 ) & &
2005-10-26 19:00:51 +04:00
info - > exists )
return 1 ;
}
2002-03-25 21:54:59 +03:00
if ( name )
2007-11-12 23:51:54 +03:00
return _info_run ( name , NULL , info , read_ahead , mknodes ,
2008-12-19 18:23:03 +03:00
with_open_count , with_read_ahead , 0 , 0 ) ;
2002-03-25 21:54:59 +03:00
return 0 ;
}
2008-12-19 18:23:03 +03:00
static int _info_by_dev ( uint32_t major , uint32_t minor , struct dm_info * info )
{
return _info_run ( NULL , NULL , info , NULL , 0 , 0 , 0 , major , minor ) ;
}
2005-10-19 17:59:18 +04:00
int dev_manager_info ( struct dm_pool * mem , const char * name ,
const struct logical_volume * lv , int with_mknodes ,
2007-11-12 23:51:54 +03:00
int with_open_count , int with_read_ahead ,
struct dm_info * info , uint32_t * read_ahead )
2005-10-17 22:00:02 +04:00
{
2005-10-19 17:59:18 +04:00
const char * dlid ;
if ( ! ( dlid = _build_dlid ( mem , lv - > lvid . s , NULL ) ) ) {
log_error ( " dlid build failed for %s " , lv - > name ) ;
return 0 ;
}
2007-11-12 23:51:54 +03:00
return _info ( name , dlid , with_mknodes , with_open_count , with_read_ahead ,
info , read_ahead ) ;
2005-10-17 22:00:02 +04:00
}
2002-05-22 18:03:45 +04:00
/* FIXME Interface must cope with multiple targets */
2002-05-10 20:06:06 +04:00
static int _status_run ( const char * name , const char * uuid ,
unsigned long long * s , unsigned long long * l ,
char * * t , uint32_t t_size , char * * p , uint32_t p_size )
2002-05-10 01:17:57 +04:00
{
int r = 0 ;
struct dm_task * dmt ;
2005-10-26 21:56:31 +04:00
struct dm_info info ;
2002-05-10 01:17:57 +04:00
void * next = NULL ;
2002-06-07 12:37:07 +04:00
uint64_t start , length ;
2002-05-10 01:17:57 +04:00
char * type = NULL ;
2002-05-10 20:06:06 +04:00
char * params = NULL ;
2002-05-10 01:17:57 +04:00
2008-12-19 18:23:03 +03:00
if ( ! ( dmt = _setup_task ( name , uuid , 0 , DM_DEVICE_STATUS , 0 , 0 ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2002-05-10 01:17:57 +04:00
2005-01-13 01:58:21 +03:00
if ( ! dm_task_no_open_count ( dmt ) )
log_error ( " Failed to disable open_count " ) ;
2005-11-09 01:52:26 +03:00
if ( ! dm_task_run ( dmt ) )
goto_out ;
2002-05-10 01:17:57 +04:00
2005-11-09 01:52:26 +03:00
if ( ! dm_task_get_info ( dmt , & info ) | | ! info . exists )
goto_out ;
2005-10-26 21:56:31 +04:00
2002-05-10 01:17:57 +04:00
do {
next = dm_get_next_target ( dmt , next , & start , & length ,
& type , & params ) ;
2002-05-22 18:03:45 +04:00
if ( type ) {
2002-05-10 20:06:06 +04:00
* s = start ;
* l = length ;
/* Make sure things are null terminated */
strncpy ( * t , type , t_size ) ;
2002-05-22 18:03:45 +04:00
( * t ) [ t_size - 1 ] = ' \0 ' ;
2002-05-10 20:06:06 +04:00
strncpy ( * p , params , p_size ) ;
2002-05-22 18:03:45 +04:00
( * p ) [ p_size - 1 ] = ' \0 ' ;
2002-05-10 20:06:06 +04:00
r = 1 ;
2002-05-22 18:03:45 +04:00
/* FIXME Cope with multiple targets! */
2002-05-10 20:06:06 +04:00
break ;
2002-05-10 01:17:57 +04:00
}
2002-05-22 18:03:45 +04:00
} while ( next ) ;
2002-05-10 01:17:57 +04:00
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2003-04-25 02:09:13 +04:00
static int _status ( const char * name , const char * uuid ,
unsigned long long * start , unsigned long long * length ,
char * * type , uint32_t type_size , char * * params ,
uint32_t param_size ) __attribute__ ( ( unused ) ) ;
2002-05-10 20:06:06 +04:00
static int _status ( const char * name , const char * uuid ,
unsigned long long * start , unsigned long long * length ,
char * * type , uint32_t type_size , char * * params ,
uint32_t param_size )
2002-05-10 01:17:57 +04:00
{
2005-10-26 19:00:51 +04:00
if ( uuid & & * uuid ) {
if ( _status_run ( NULL , uuid , start , length , type ,
type_size , params , param_size ) & &
* params )
return 1 ;
2005-10-26 21:56:31 +04:00
else if ( _status_run ( NULL , uuid + sizeof ( UUID_PREFIX ) - 1 , start ,
2005-10-26 19:00:51 +04:00
length , type , type_size , params ,
param_size ) & &
* params )
return 1 ;
}
2002-05-10 01:17:57 +04:00
2002-05-10 20:06:06 +04:00
if ( name & & _status_run ( name , NULL , start , length , type , type_size ,
2002-05-22 18:03:45 +04:00
params , param_size ) )
2002-05-10 01:17:57 +04:00
return 1 ;
2002-05-22 18:03:45 +04:00
2002-05-10 01:17:57 +04:00
return 0 ;
}
2009-10-01 04:35:29 +04:00
static percent_range_t _combine_percent_ranges ( percent_range_t a ,
percent_range_t b )
{
if ( a = = PERCENT_INVALID | | b = = PERCENT_INVALID )
return PERCENT_INVALID ;
if ( a = = PERCENT_100 & & b = = PERCENT_100 )
return PERCENT_100 ;
if ( a = = PERCENT_0 & & b = = PERCENT_0 )
return PERCENT_0 ;
return PERCENT_0_TO_100 ;
}
2003-05-06 16:00:29 +04:00
static int _percent_run ( struct dev_manager * dm , const char * name ,
2005-10-19 17:59:18 +04:00
const char * dlid ,
2003-05-06 16:00:29 +04:00
const char * target_type , int wait ,
struct logical_volume * lv , float * percent ,
2009-10-01 04:35:29 +04:00
percent_range_t * overall_percent_range ,
2003-04-30 19:26:25 +04:00
uint32_t * event_nr )
2003-04-25 02:09:13 +04:00
{
int r = 0 ;
struct dm_task * dmt ;
2003-04-30 19:26:25 +04:00
struct dm_info info ;
2003-04-25 02:09:13 +04:00
void * next = NULL ;
uint64_t start , length ;
char * type = NULL ;
char * params = NULL ;
2008-11-04 01:14:30 +03:00
struct dm_list * segh = & lv - > segments ;
2003-05-06 16:00:29 +04:00
struct lv_segment * seg = NULL ;
2004-05-05 01:25:57 +04:00
struct segment_type * segtype ;
2009-10-01 04:35:29 +04:00
percent_range_t percent_range , combined_percent_range ;
int first_time = 1 ;
2003-04-25 02:09:13 +04:00
uint64_t total_numerator = 0 , total_denominator = 0 ;
* percent = - 1 ;
2009-10-01 04:35:29 +04:00
* overall_percent_range = PERCENT_INVALID ;
2003-04-25 02:09:13 +04:00
2005-10-19 17:59:18 +04:00
if ( ! ( dmt = _setup_task ( name , dlid , event_nr ,
2008-12-19 18:23:03 +03:00
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS , 0 , 0 ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2003-04-25 02:09:13 +04:00
2005-01-13 01:58:21 +03:00
if ( ! dm_task_no_open_count ( dmt ) )
log_error ( " Failed to disable open_count " ) ;
2005-11-09 01:52:26 +03:00
if ( ! dm_task_run ( dmt ) )
goto_out ;
2003-04-25 02:09:13 +04:00
2005-11-09 01:52:26 +03:00
if ( ! dm_task_get_info ( dmt , & info ) | | ! info . exists )
goto_out ;
2003-04-30 19:26:25 +04:00
if ( event_nr )
* event_nr = info . event_nr ;
2003-04-25 02:09:13 +04:00
do {
next = dm_get_next_target ( dmt , next , & start , & length , & type ,
& params ) ;
2003-05-06 16:00:29 +04:00
if ( lv ) {
2008-11-04 01:14:30 +03:00
if ( ! ( segh = dm_list_next ( & lv - > segments , segh ) ) ) {
2003-05-06 16:00:29 +04:00
log_error ( " Number of segments in active LV %s "
" does not match metadata " , lv - > name ) ;
goto out ;
}
2008-11-04 01:14:30 +03:00
seg = dm_list_item ( segh , struct lv_segment ) ;
2003-05-06 16:00:29 +04:00
}
2003-04-25 02:09:13 +04:00
if ( ! type | | ! params | | strcmp ( type , target_type ) )
continue ;
2004-05-05 01:25:57 +04:00
if ( ! ( segtype = get_segtype_from_string ( dm - > cmd , type ) ) )
2003-04-30 19:26:25 +04:00
continue ;
2004-05-05 01:25:57 +04:00
if ( segtype - > ops - > target_percent & &
2009-10-01 04:35:29 +04:00
! segtype - > ops - > target_percent ( & dm - > target_state ,
& percent_range , dm - > mem ,
2006-05-17 00:53:13 +04:00
dm - > cmd , seg , params ,
2004-05-05 01:25:57 +04:00
& total_numerator ,
2008-07-15 04:25:52 +04:00
& total_denominator ) )
2005-11-09 01:52:26 +03:00
goto_out ;
2004-05-05 01:25:57 +04:00
2009-10-01 04:35:29 +04:00
if ( first_time ) {
combined_percent_range = percent_range ;
first_time = 0 ;
} else
combined_percent_range =
_combine_percent_ranges ( combined_percent_range ,
percent_range ) ;
2003-04-25 02:09:13 +04:00
} while ( next ) ;
2008-11-04 01:14:30 +03:00
if ( lv & & ( segh = dm_list_next ( & lv - > segments , segh ) ) ) {
2003-05-06 16:00:29 +04:00
log_error ( " Number of segments in active LV %s does not "
" match metadata " , lv - > name ) ;
goto out ;
}
2009-10-01 04:35:29 +04:00
if ( total_denominator ) {
2003-05-06 16:00:29 +04:00
* percent = ( float ) total_numerator * 100 / total_denominator ;
2009-10-01 04:35:29 +04:00
* overall_percent_range = combined_percent_range ;
} else {
2003-04-30 19:26:25 +04:00
* percent = 100 ;
2009-10-01 04:35:29 +04:00
if ( first_time )
* overall_percent_range = PERCENT_100 ;
else
* overall_percent_range = combined_percent_range ;
}
2003-04-25 02:09:13 +04:00
2003-08-20 19:48:27 +04:00
log_debug ( " LV percent: %f " , * percent ) ;
2003-04-25 02:09:13 +04:00
r = 1 ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2005-10-19 17:59:18 +04:00
static int _percent ( struct dev_manager * dm , const char * name , const char * dlid ,
2003-05-06 16:00:29 +04:00
const char * target_type , int wait ,
struct logical_volume * lv , float * percent ,
2009-10-01 04:35:29 +04:00
percent_range_t * overall_percent_range , uint32_t * event_nr )
2003-04-25 02:09:13 +04:00
{
2005-10-26 19:00:51 +04:00
if ( dlid & & * dlid ) {
if ( _percent_run ( dm , NULL , dlid , target_type , wait , lv , percent ,
2009-10-01 04:35:29 +04:00
overall_percent_range , event_nr ) )
2005-10-26 19:00:51 +04:00
return 1 ;
2005-10-26 21:56:31 +04:00
else if ( _percent_run ( dm , NULL , dlid + sizeof ( UUID_PREFIX ) - 1 ,
2005-10-26 19:00:51 +04:00
target_type , wait , lv , percent ,
2009-10-01 04:35:29 +04:00
overall_percent_range , event_nr ) )
2005-10-26 19:00:51 +04:00
return 1 ;
}
2003-04-25 02:09:13 +04:00
2003-05-06 16:00:29 +04:00
if ( name & & _percent_run ( dm , name , NULL , target_type , wait , lv , percent ,
2009-10-01 04:35:29 +04:00
overall_percent_range , event_nr ) )
2003-04-25 02:09:13 +04:00
return 1 ;
return 0 ;
}
2005-11-09 01:52:26 +03:00
/*
* dev_manager implementation .
*/
struct dev_manager * dev_manager_create ( struct cmd_context * cmd ,
const char * vg_name )
2002-03-19 02:25:50 +03:00
{
2005-11-09 01:52:26 +03:00
struct dm_pool * mem ;
struct dev_manager * dm ;
2002-03-19 02:25:50 +03:00
2008-01-30 16:19:47 +03:00
if ( ! ( mem = dm_pool_create ( " dev_manager " , 16 * 1024 ) ) )
return_NULL ;
2002-03-19 02:25:50 +03:00
2009-05-07 16:01:21 +04:00
if ( ! ( dm = dm_pool_zalloc ( mem , sizeof ( * dm ) ) ) )
2007-04-26 20:44:59 +04:00
goto_bad ;
2002-03-19 02:25:50 +03:00
2005-11-09 01:52:26 +03:00
dm - > cmd = cmd ;
dm - > mem = mem ;
2005-01-13 01:58:21 +03:00
2007-04-26 20:44:59 +04:00
if ( ! ( dm - > vg_name = dm_pool_strdup ( dm - > mem , vg_name ) ) )
goto_bad ;
2004-05-13 00:40:34 +04:00
2005-11-09 01:52:26 +03:00
dm - > target_state = NULL ;
2005-01-13 01:58:21 +03:00
2009-08-04 19:36:13 +04:00
dm_udev_set_sync_support ( cmd - > current_settings . udev_sync ) ;
2005-11-09 01:52:26 +03:00
return dm ;
2004-05-13 00:40:34 +04:00
2005-11-09 01:52:26 +03:00
bad :
dm_pool_destroy ( mem ) ;
return NULL ;
2004-05-13 00:40:34 +04:00
}
2005-11-09 01:52:26 +03:00
void dev_manager_destroy ( struct dev_manager * dm )
2004-05-13 00:40:34 +04:00
{
2005-11-09 01:52:26 +03:00
dm_pool_destroy ( dm - > mem ) ;
}
2004-05-13 00:40:34 +04:00
2006-05-16 20:48:31 +04:00
void dev_manager_release ( void )
{
dm_lib_release ( ) ;
}
2005-11-09 01:52:26 +03:00
void dev_manager_exit ( void )
{
dm_lib_exit ( ) ;
2004-05-13 00:40:34 +04:00
}
2005-11-09 01:52:26 +03:00
int dev_manager_snapshot_percent ( struct dev_manager * dm ,
2006-04-06 18:06:27 +04:00
const struct logical_volume * lv ,
2009-10-01 04:35:29 +04:00
float * percent , percent_range_t * percent_range )
2004-05-13 00:40:34 +04:00
{
2005-11-09 01:52:26 +03:00
char * name ;
const char * dlid ;
/*
* Build a name for the top layer .
*/
if ( ! ( name = build_dm_name ( dm - > mem , lv - > vg - > name , lv - > name , NULL ) ) )
return_0 ;
if ( ! ( dlid = build_dlid ( dm , lv - > lvid . s , NULL ) ) )
return_0 ;
2004-05-13 00:40:34 +04:00
2005-11-09 01:52:26 +03:00
/*
* Try and get some info on this device .
*/
log_debug ( " Getting device status percentage for %s " , name ) ;
if ( ! ( _percent ( dm , name , dlid , " snapshot " , 0 , NULL , percent ,
2009-10-01 04:35:29 +04:00
percent_range , NULL ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2004-05-13 00:40:34 +04:00
2005-11-09 01:52:26 +03:00
/* FIXME dm_pool_free ? */
/* If the snapshot isn't available, percent will be -1 */
2004-05-13 00:40:34 +04:00
return 1 ;
}
2005-11-09 01:52:26 +03:00
/* FIXME Merge with snapshot_percent, auto-detecting target type */
/* FIXME Cope with more than one target */
int dev_manager_mirror_percent ( struct dev_manager * dm ,
struct logical_volume * lv , int wait ,
2009-10-01 04:35:29 +04:00
float * percent , percent_range_t * percent_range ,
uint32_t * event_nr )
2002-02-25 17:46:57 +03:00
{
2005-11-09 01:52:26 +03:00
char * name ;
const char * dlid ;
2002-02-25 17:46:57 +03:00
2002-03-26 16:41:37 +03:00
/*
2005-11-09 01:52:26 +03:00
* Build a name for the top layer .
2002-03-26 16:41:37 +03:00
*/
2005-11-09 01:52:26 +03:00
if ( ! ( name = build_dm_name ( dm - > mem , lv - > vg - > name , lv - > name , NULL ) ) )
return_0 ;
2002-03-08 13:41:48 +03:00
2005-11-09 01:52:26 +03:00
/* FIXME dm_pool_free ? */
2005-01-13 01:58:21 +03:00
2005-11-09 01:52:26 +03:00
if ( ! ( dlid = build_dlid ( dm , lv - > lvid . s , NULL ) ) ) {
log_error ( " dlid build failed for %s " , lv - > name ) ;
return 0 ;
2003-07-05 02:34:56 +04:00
}
2002-03-15 00:17:30 +03:00
2005-11-09 01:52:26 +03:00
log_debug ( " Getting device mirror status percentage for %s " , name ) ;
if ( ! ( _percent ( dm , name , dlid , " mirror " , wait , lv , percent ,
2009-10-01 04:35:29 +04:00
percent_range , event_nr ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2002-03-15 00:17:30 +03:00
2005-11-09 01:52:26 +03:00
return 1 ;
}
2003-07-05 02:34:56 +04:00
2005-11-09 01:52:26 +03:00
#if 0
log_very_verbose ( " %s %s " , sus ? " Suspending " : " Resuming " , name ) ;
2004-05-13 00:40:34 +04:00
2005-11-09 01:52:26 +03:00
log_verbose ( " Loading %s " , dl - > name ) ;
log_very_verbose ( " Activating %s read-only " , dl - > name ) ;
2003-07-05 02:34:56 +04:00
log_very_verbose ( " Activated %s %s %03u:%03u " , dl - > name ,
dl - > dlid , dl - > info . major , dl - > info . minor ) ;
2002-03-16 01:59:12 +03:00
if ( _get_flag ( dl , VISIBLE ) )
2002-03-18 16:09:27 +03:00
log_verbose ( " Removing %s " , dl - > name ) ;
2002-03-16 01:59:12 +03:00
else
2002-03-18 16:09:27 +03:00
log_very_verbose ( " Removing %s " , dl - > name ) ;
2002-03-16 01:59:12 +03:00
2005-11-09 01:52:26 +03:00
log_debug ( " Adding target: % " PRIu64 " % " PRIu64 " %s %s " ,
extent_size * seg - > le , extent_size * seg - > len , target , params ) ;
2002-02-25 17:46:57 +03:00
2005-11-09 01:52:26 +03:00
log_debug ( " Adding target: 0 % " PRIu64 " snapshot-origin %s " ,
dl - > lv - > size , params ) ;
log_debug ( " Adding target: 0 % " PRIu64 " snapshot %s " , size , params ) ;
log_debug ( " Getting device info for %s " , dl - > name ) ;
2005-01-13 01:58:21 +03:00
2005-11-09 01:52:26 +03:00
/* Rename? */
2007-07-02 15:17:21 +04:00
if ( ( suffix = strrchr ( dl - > dlid + sizeof ( UUID_PREFIX ) - 1 , ' - ' ) ) )
2005-11-09 01:52:26 +03:00
suffix + + ;
2007-08-06 18:57:48 +04:00
new_name = build_dm_name ( dm - > mem , dm - > vg_name , dl - > lv - > name ,
2005-11-09 01:52:26 +03:00
suffix ) ;
2002-03-16 01:59:12 +03:00
2005-11-09 01:52:26 +03:00
static int _belong_to_vg ( const char * vgname , const char * name )
{
const char * v = vgname , * n = name ;
2002-02-25 17:46:57 +03:00
2005-11-09 01:52:26 +03:00
while ( * v ) {
if ( ( * v ! = * n ) | | ( * v = = ' - ' & & * ( + + n ) ! = ' - ' ) )
return 0 ;
v + + , n + + ;
2002-04-16 18:42:20 +04:00
}
2002-03-18 16:09:27 +03:00
2005-11-09 01:52:26 +03:00
if ( * n = = ' - ' & & * ( n + 1 ) ! = ' - ' )
return 1 ;
else
return 0 ;
}
2002-04-16 18:42:20 +04:00
2005-04-07 16:39:44 +04:00
if ( ! ( snap_seg = find_cow ( lv ) ) )
2002-04-16 18:42:20 +04:00
return 1 ;
2005-04-07 16:39:44 +04:00
old_origin = snap_seg - > origin ;
2002-04-16 18:42:20 +04:00
/* Was this the last active snapshot with this origin? */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( lvl , active_head ) {
2005-06-01 20:51:55 +04:00
active = lvl - > lv ;
2005-04-07 16:39:44 +04:00
if ( ( snap_seg = find_cow ( active ) ) & &
snap_seg - > origin = = old_origin ) {
2002-04-16 18:42:20 +04:00
return 1 ;
2002-04-24 22:20:51 +04:00
}
2002-03-18 16:09:27 +03:00
}
2005-11-09 01:52:26 +03:00
# endif
/*************************/
/* NEW CODE STARTS HERE */
/*************************/
int dev_manager_lv_mknodes ( const struct logical_volume * lv )
{
char * name ;
if ( ! ( name = build_dm_name ( lv - > vg - > cmd - > mem , lv - > vg - > name ,
lv - > name , NULL ) ) )
return_0 ;
return fs_add_lv ( lv , name ) ;
2002-03-18 16:09:27 +03:00
}
2005-11-09 01:52:26 +03:00
int dev_manager_lv_rmnodes ( const struct logical_volume * lv )
2003-04-25 02:09:13 +04:00
{
2005-11-09 01:52:26 +03:00
return fs_del_lv ( lv ) ;
}
2005-11-09 16:08:41 +03:00
static int _add_dev_to_dtree ( struct dev_manager * dm , struct dm_tree * dtree ,
2005-11-09 01:52:26 +03:00
struct logical_volume * lv , const char * layer )
{
char * dlid , * name ;
2008-12-19 18:23:03 +03:00
struct dm_info info , info2 ;
2003-04-25 02:09:13 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( name = build_dm_name ( dm - > mem , lv - > vg - > name , lv - > name , layer ) ) )
return_0 ;
2003-04-25 02:09:13 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( dlid = build_dlid ( dm , lv - > lvid . s , layer ) ) )
return_0 ;
2008-01-30 17:00:02 +03:00
log_debug ( " Getting device info for %s [%s] " , name , dlid ) ;
if ( ! _info ( name , dlid , 0 , 1 , 0 , & info , NULL ) ) {
log_error ( " Failed to get info for %s [%s]. " , name , dlid ) ;
return 0 ;
}
2005-11-09 01:52:26 +03:00
2008-12-19 18:23:03 +03:00
/*
* For top level volumes verify that existing device match
* requested major / minor and that major / minor pair is available for use
*/
if ( ! layer & & lv - > major ! = - 1 & & lv - > minor ! = - 1 ) {
2009-02-12 23:42:07 +03:00
/*
* FIXME compare info . major with lv - > major if multiple major support
*/
if ( info . exists & & ( info . minor ! = lv - > minor ) ) {
2008-12-19 18:23:03 +03:00
log_error ( " Volume %s (% " PRIu32 " :% " PRIu32 " ) "
" differs from already active device "
" (% " PRIu32 " :% " PRIu32 " ) " ,
lv - > name , lv - > major , lv - > minor , info . major , info . minor ) ;
return 0 ;
}
if ( ! info . exists & & _info_by_dev ( lv - > major , lv - > minor , & info2 ) & &
info2 . exists ) {
log_error ( " The requested major:minor pair "
" (% " PRIu32 " :% " PRIu32 " ) is already used " ,
lv - > major , lv - > minor ) ;
return 0 ;
}
}
2005-11-09 16:05:17 +03:00
if ( info . exists & & ! dm_tree_add_dev ( dtree , info . major , info . minor ) ) {
2005-11-09 16:08:41 +03:00
log_error ( " Failed to add device (% " PRIu32 " :% " PRIu32 " ) to dtree " ,
2005-11-09 01:52:26 +03:00
info . major , info . minor ) ;
return 0 ;
2003-04-25 02:09:13 +04:00
}
2005-11-09 01:52:26 +03:00
return 1 ;
}
/*
* Add LV and any known dependencies
*/
2005-11-09 16:08:41 +03:00
static int _add_lv_to_dtree ( struct dev_manager * dm , struct dm_tree * dtree , struct logical_volume * lv )
2005-11-09 01:52:26 +03:00
{
2005-11-09 16:08:41 +03:00
if ( ! _add_dev_to_dtree ( dm , dtree , lv , NULL ) )
2005-11-09 01:52:26 +03:00
return_0 ;
/* FIXME Can we avoid doing this every time? */
2005-11-09 16:08:41 +03:00
if ( ! _add_dev_to_dtree ( dm , dtree , lv , " real " ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2005-11-09 16:08:41 +03:00
if ( ! _add_dev_to_dtree ( dm , dtree , lv , " cow " ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2003-04-25 02:09:13 +04:00
2006-04-28 21:01:07 +04:00
if ( ! _add_dev_to_dtree ( dm , dtree , lv , " _mlog " ) )
return_0 ;
2003-04-25 02:09:13 +04:00
return 1 ;
}
2005-11-09 16:08:41 +03:00
static struct dm_tree * _create_partial_dtree ( struct dev_manager * dm , struct logical_volume * lv )
2004-05-05 22:11:43 +04:00
{
2005-11-09 16:05:17 +03:00
struct dm_tree * dtree ;
2008-11-04 01:14:30 +03:00
struct dm_list * snh , * snht ;
2005-11-10 02:56:36 +03:00
struct lv_segment * seg ;
uint32_t s ;
2004-05-05 22:11:43 +04:00
2005-11-09 16:05:17 +03:00
if ( ! ( dtree = dm_tree_create ( ) ) ) {
2005-11-09 16:08:41 +03:00
log_error ( " Partial dtree creation failed for %s. " , lv - > name ) ;
2005-11-09 01:52:26 +03:00
return NULL ;
}
2004-05-05 22:11:43 +04:00
2008-01-30 16:19:47 +03:00
if ( ! _add_lv_to_dtree ( dm , dtree , lv ) )
goto_bad ;
2004-05-05 22:11:43 +04:00
2005-11-09 01:52:26 +03:00
/* Add any snapshots of this LV */
2008-11-04 01:14:30 +03:00
dm_list_iterate_safe ( snh , snht , & lv - > snapshot_segs )
if ( ! _add_lv_to_dtree ( dm , dtree , dm_list_struct_base ( snh , struct lv_segment , origin_list ) - > cow ) )
2008-01-30 16:19:47 +03:00
goto_bad ;
2004-05-05 22:11:43 +04:00
2005-11-10 02:56:36 +03:00
/* Add any LVs used by segments in this LV */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( seg , & lv - > segments )
2005-11-10 02:56:36 +03:00
for ( s = 0 ; s < seg - > area_count ; s + + )
if ( seg_type ( seg , s ) = = AREA_LV & & seg_lv ( seg , s ) ) {
2008-01-30 16:19:47 +03:00
if ( ! _add_lv_to_dtree ( dm , dtree , seg_lv ( seg , s ) ) )
goto_bad ;
2005-11-10 02:56:36 +03:00
}
2005-11-09 01:52:26 +03:00
return dtree ;
2004-05-05 22:11:43 +04:00
2008-01-30 16:19:47 +03:00
bad :
2005-11-09 16:05:17 +03:00
dm_tree_free ( dtree ) ;
2005-11-09 01:52:26 +03:00
return NULL ;
2004-05-05 22:11:43 +04:00
}
2008-09-19 10:42:00 +04:00
static char * _add_error_device ( struct dev_manager * dm , struct dm_tree * dtree ,
struct lv_segment * seg , int s )
{
char * id , * name ;
char errid [ 32 ] ;
struct dm_tree_node * node ;
struct lv_segment * seg_i ;
int segno = - 1 , i = 0 ; ;
uint64_t size = seg - > len * seg - > lv - > vg - > extent_size ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( seg_i , & seg - > lv - > segments ) {
2008-09-19 10:42:00 +04:00
if ( seg = = seg_i )
segno = i ;
+ + i ;
}
if ( segno < 0 ) {
log_error ( " _add_error_device called with bad segment " ) ;
return_NULL ;
}
sprintf ( errid , " missing_%d_%d " , segno , s ) ;
if ( ! ( id = build_dlid ( dm , seg - > lv - > lvid . s , errid ) ) )
return_NULL ;
if ( ! ( name = build_dm_name ( dm - > mem , seg - > lv - > vg - > name ,
seg - > lv - > name , errid ) ) )
return_NULL ;
if ( ! ( node = dm_tree_add_new_dev ( dtree , name , id , 0 , 0 , 0 , 0 , 0 ) ) )
return_NULL ;
if ( ! dm_tree_node_add_error_target ( node , size ) )
return_NULL ;
return id ;
}
static int _add_error_area ( struct dev_manager * dm , struct dm_tree_node * node ,
struct lv_segment * seg , int s )
{
char * dlid ;
uint64_t extent_size = seg - > lv - > vg - > extent_size ;
if ( ! strcmp ( dm - > cmd - > stripe_filler , " error " ) ) {
/*
* FIXME , the tree pointer is first field of dm_tree_node , but
* we don ' t have the struct definition available .
*/
struct dm_tree * * tree = ( struct dm_tree * * ) node ;
dlid = _add_error_device ( dm , * tree , seg , s ) ;
if ( ! dlid )
return_0 ;
dm_tree_node_add_target_area ( node , NULL , dlid ,
extent_size * seg_le ( seg , s ) ) ;
2008-09-19 11:18:03 +04:00
} else
2008-09-19 10:42:00 +04:00
dm_tree_node_add_target_area ( node ,
dm - > cmd - > stripe_filler ,
NULL , UINT64_C ( 0 ) ) ;
2008-09-19 11:18:03 +04:00
2008-09-19 10:42:00 +04:00
return 1 ;
}
2005-11-09 01:52:26 +03:00
int add_areas_line ( struct dev_manager * dm , struct lv_segment * seg ,
2006-05-10 01:23:51 +04:00
struct dm_tree_node * node , uint32_t start_area ,
uint32_t areas )
2002-03-11 14:27:48 +03:00
{
2005-11-09 01:52:26 +03:00
uint64_t extent_size = seg - > lv - > vg - > extent_size ;
uint32_t s ;
2002-03-19 02:25:50 +03:00
char * dlid ;
2002-03-11 14:27:48 +03:00
2005-11-09 01:52:26 +03:00
for ( s = start_area ; s < areas ; s + + ) {
if ( ( seg_type ( seg , s ) = = AREA_PV & &
( ! seg_pvseg ( seg , s ) | |
! seg_pv ( seg , s ) | |
! seg_dev ( seg , s ) ) ) | |
2008-09-19 10:42:00 +04:00
( seg_type ( seg , s ) = = AREA_LV & & ! seg_lv ( seg , s ) ) ) {
if ( ! _add_error_area ( dm , node , seg , s ) )
return_0 ;
} else if ( seg_type ( seg , s ) = = AREA_PV )
2005-11-09 16:05:17 +03:00
dm_tree_node_add_target_area ( node ,
2005-11-09 01:52:26 +03:00
dev_name ( seg_dev ( seg , s ) ) ,
NULL ,
( seg_pv ( seg , s ) - > pe_start +
( extent_size * seg_pe ( seg , s ) ) ) ) ;
else if ( seg_type ( seg , s ) = = AREA_LV ) {
if ( ! ( dlid = build_dlid ( dm ,
seg_lv ( seg , s ) - > lvid . s ,
NULL ) ) )
return_0 ;
2005-11-09 16:05:17 +03:00
dm_tree_node_add_target_area ( node , NULL , dlid ,
2005-11-09 01:52:26 +03:00
extent_size * seg_le ( seg , s ) ) ;
} else {
log_error ( " Internal error: Unassigned area found in LV %s. " ,
seg - > lv - > name ) ;
2002-03-11 14:27:48 +03:00
return 0 ;
}
2005-11-09 01:52:26 +03:00
}
2002-03-11 14:27:48 +03:00
2005-11-09 01:52:26 +03:00
return 1 ;
}
2002-03-11 14:27:48 +03:00
2005-11-09 16:08:41 +03:00
static int _add_origin_target_to_dtree ( struct dev_manager * dm ,
2005-11-09 16:05:17 +03:00
struct dm_tree_node * dnode ,
2005-11-09 01:52:26 +03:00
struct logical_volume * lv )
{
const char * real_dlid ;
2002-03-11 23:36:04 +03:00
2005-11-09 01:52:26 +03:00
if ( ! ( real_dlid = build_dlid ( dm , lv - > lvid . s , " real " ) ) )
return_0 ;
2003-04-25 02:09:13 +04:00
2005-11-09 16:05:17 +03:00
if ( ! dm_tree_node_add_snapshot_origin_target ( dnode , lv - > size , real_dlid ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2002-03-11 14:27:48 +03:00
return 1 ;
}
2005-11-09 16:08:41 +03:00
static int _add_snapshot_target_to_dtree ( struct dev_manager * dm ,
2005-11-09 16:05:17 +03:00
struct dm_tree_node * dnode ,
2005-11-09 01:52:26 +03:00
struct logical_volume * lv )
2002-03-11 14:27:48 +03:00
{
2005-11-09 01:52:26 +03:00
const char * origin_dlid ;
const char * cow_dlid ;
struct lv_segment * snap_seg ;
uint64_t size ;
2002-03-11 14:27:48 +03:00
2005-11-09 01:52:26 +03:00
if ( ! ( snap_seg = find_cow ( lv ) ) ) {
log_error ( " Couldn't find snapshot for '%s'. " , lv - > name ) ;
2005-10-26 23:50:00 +04:00
return 0 ;
2002-03-11 14:27:48 +03:00
}
2005-11-09 01:52:26 +03:00
if ( ! ( origin_dlid = build_dlid ( dm , snap_seg - > origin - > lvid . s , " real " ) ) )
return_0 ;
2003-04-25 02:09:13 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( cow_dlid = build_dlid ( dm , snap_seg - > cow - > lvid . s , " cow " ) ) )
return_0 ;
2002-03-27 21:17:43 +03:00
2005-11-09 01:52:26 +03:00
size = ( uint64_t ) snap_seg - > len * snap_seg - > origin - > vg - > extent_size ;
2004-05-05 22:11:43 +04:00
2005-11-09 16:05:17 +03:00
if ( ! dm_tree_node_add_snapshot_target ( dnode , size , origin_dlid , cow_dlid , 1 , snap_seg - > chunk_size ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2002-03-11 14:27:48 +03:00
return 1 ;
}
2005-11-09 16:08:41 +03:00
static int _add_target_to_dtree ( struct dev_manager * dm ,
2005-11-09 16:05:17 +03:00
struct dm_tree_node * dnode ,
2005-11-09 01:52:26 +03:00
struct lv_segment * seg )
2002-03-11 14:27:48 +03:00
{
2005-11-09 01:52:26 +03:00
uint64_t extent_size = seg - > lv - > vg - > extent_size ;
if ( ! seg - > segtype - > ops - > add_target_line ) {
log_error ( " _emit_target: Internal error: Can't handle "
" segment type %s " , seg - > segtype - > name ) ;
return 0 ;
}
2006-05-16 20:48:31 +04:00
return seg - > segtype - > ops - > add_target_line ( dm , dm - > mem , dm - > cmd ,
2005-11-09 01:52:26 +03:00
& dm - > target_state , seg ,
dnode ,
extent_size * seg - > len ,
& dm - > pvmove_mirror_count ) ;
2002-03-16 01:59:12 +03:00
}
2002-03-11 14:27:48 +03:00
2005-11-09 16:08:41 +03:00
static int _add_new_lv_to_dtree ( struct dev_manager * dm , struct dm_tree * dtree ,
2005-11-09 01:52:26 +03:00
struct logical_volume * lv , const char * layer ) ;
2005-11-09 16:08:41 +03:00
static int _add_segment_to_dtree ( struct dev_manager * dm ,
2005-11-09 16:05:17 +03:00
struct dm_tree * dtree ,
struct dm_tree_node * dnode ,
2005-11-09 01:52:26 +03:00
struct lv_segment * seg ,
const char * layer )
2003-11-12 22:16:48 +03:00
{
2005-11-09 01:52:26 +03:00
uint32_t s ;
2008-11-04 01:14:30 +03:00
struct dm_list * snh ;
2006-11-20 19:45:45 +03:00
struct lv_segment * seg_present ;
2003-11-12 22:16:48 +03:00
2005-11-09 01:52:26 +03:00
/* Ensure required device-mapper targets are loaded */
2006-11-20 19:45:45 +03:00
seg_present = find_cow ( seg - > lv ) ? : seg ;
log_debug ( " Checking kernel supports %s segment type for %s%s%s " ,
seg_present - > segtype - > name , seg - > lv - > name ,
layer ? " - " : " " , layer ? : " " ) ;
if ( seg_present - > segtype - > ops - > target_present & &
2009-02-28 23:04:24 +03:00
! seg_present - > segtype - > ops - > target_present ( seg_present - > lv - > vg - > cmd ,
seg_present , NULL ) ) {
2005-11-09 01:52:26 +03:00
log_error ( " Can't expand LV %s: %s target support missing "
2006-11-20 19:45:45 +03:00
" from kernel? " , seg - > lv - > name , seg_present - > segtype - > name ) ;
2003-11-12 22:16:48 +03:00
return 0 ;
}
2005-11-09 01:52:26 +03:00
/* Add mirror log */
if ( seg - > log_lv & &
2005-11-09 16:08:41 +03:00
! _add_new_lv_to_dtree ( dm , dtree , seg - > log_lv , NULL ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2003-11-12 22:16:48 +03:00
2005-11-09 01:52:26 +03:00
/* If this is a snapshot origin, add real LV */
if ( lv_is_origin ( seg - > lv ) & & ! layer ) {
2008-04-10 21:09:32 +04:00
if ( vg_is_clustered ( seg - > lv - > vg ) ) {
2005-11-09 01:52:26 +03:00
log_error ( " Clustered snapshots are not yet supported " ) ;
return 0 ;
}
2005-11-09 16:08:41 +03:00
if ( ! _add_new_lv_to_dtree ( dm , dtree , seg - > lv , " real " ) )
2005-11-09 01:52:26 +03:00
return_0 ;
} else if ( lv_is_cow ( seg - > lv ) & & ! layer ) {
2005-11-09 16:08:41 +03:00
if ( ! _add_new_lv_to_dtree ( dm , dtree , seg - > lv , " cow " ) )
2005-11-09 01:52:26 +03:00
return_0 ;
} else {
/* Add any LVs used by this segment */
for ( s = 0 ; s < seg - > area_count ; s + + )
if ( ( seg_type ( seg , s ) = = AREA_LV ) & &
2005-11-09 16:08:41 +03:00
( ! _add_new_lv_to_dtree ( dm , dtree , seg_lv ( seg , s ) , NULL ) ) )
2005-11-09 01:52:26 +03:00
return_0 ;
}
/* Now we've added its dependencies, we can add the target itself */
if ( lv_is_origin ( seg - > lv ) & & ! layer ) {
2006-05-11 21:58:58 +04:00
if ( ! _add_origin_target_to_dtree ( dm , dnode , seg - > lv ) )
2005-11-09 01:52:26 +03:00
return_0 ;
} else if ( lv_is_cow ( seg - > lv ) & & ! layer ) {
2006-05-11 21:58:58 +04:00
if ( ! _add_snapshot_target_to_dtree ( dm , dnode , seg - > lv ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2006-05-11 21:58:58 +04:00
} else if ( ! _add_target_to_dtree ( dm , dnode , seg ) )
2005-11-09 01:52:26 +03:00
return_0 ;
if ( lv_is_origin ( seg - > lv ) & & ! layer )
/* Add any snapshots of this LV */
2008-11-04 01:14:30 +03:00
dm_list_iterate ( snh , & seg - > lv - > snapshot_segs )
if ( ! _add_new_lv_to_dtree ( dm , dtree , dm_list_struct_base ( snh , struct lv_segment , origin_list ) - > cow , NULL ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2003-11-12 22:16:48 +03:00
2005-11-09 01:52:26 +03:00
return 1 ;
2003-07-05 02:34:56 +04:00
}
2005-10-17 22:00:02 +04:00
2005-11-09 16:08:41 +03:00
static int _add_new_lv_to_dtree ( struct dev_manager * dm , struct dm_tree * dtree ,
2005-11-09 01:52:26 +03:00
struct logical_volume * lv , const char * layer )
2005-10-17 22:21:05 +04:00
{
2005-11-09 01:52:26 +03:00
struct lv_segment * seg ;
struct lv_layer * lvlayer ;
2005-11-09 16:05:17 +03:00
struct dm_tree_node * dnode ;
2005-11-09 01:52:26 +03:00
char * name , * dlid ;
2007-11-12 23:51:54 +03:00
uint32_t max_stripe_size = UINT32_C ( 0 ) ;
uint32_t read_ahead = lv - > read_ahead ;
2007-11-29 18:04:12 +03:00
uint32_t read_ahead_flags = UINT32_C ( 0 ) ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( name = build_dm_name ( dm - > mem , lv - > vg - > name , lv - > name , layer ) ) )
return_0 ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( dlid = build_dlid ( dm , lv - > lvid . s , layer ) ) )
return_0 ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
/* We've already processed this node if it already has a context ptr */
2005-11-09 16:05:17 +03:00
if ( ( dnode = dm_tree_find_node_by_uuid ( dtree , dlid ) ) & &
dm_tree_node_get_context ( dnode ) )
2005-11-09 01:52:26 +03:00
return 1 ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
if ( ! ( lvlayer = dm_pool_alloc ( dm - > mem , sizeof ( * lvlayer ) ) ) ) {
2005-11-09 16:08:41 +03:00
log_error ( " _add_new_lv_to_dtree: pool alloc failed for %s %s. " , lv - > name , layer ) ;
2005-10-17 22:21:05 +04:00
return 0 ;
}
2005-11-09 01:52:26 +03:00
lvlayer - > lv = lv ;
/*
2005-11-09 16:08:41 +03:00
* Add LV to dtree .
2005-11-09 01:52:26 +03:00
* If we ' re working with precommitted metadata , clear any
* existing inactive table left behind .
* Major / minor settings only apply to the visible layer .
*/
2005-11-09 16:05:17 +03:00
if ( ! ( dnode = dm_tree_add_new_dev ( dtree , name , dlid ,
2006-07-10 23:17:40 +04:00
layer ? UINT32_C ( 0 ) : ( uint32_t ) lv - > major ,
layer ? UINT32_C ( 0 ) : ( uint32_t ) lv - > minor ,
2005-11-09 01:52:26 +03:00
_read_only_lv ( lv ) ,
2006-05-11 21:58:58 +04:00
( lv - > vg - > status & PRECOMMITTED ) ? 1 : 0 ,
2005-11-09 01:52:26 +03:00
lvlayer ) ) )
return_0 ;
/* Store existing name so we can do rename later */
2005-11-09 16:05:17 +03:00
lvlayer - > old_name = dm_tree_node_get_name ( dnode ) ;
2005-11-09 01:52:26 +03:00
/* Create table */
dm - > pvmove_mirror_count = 0u ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( seg , & lv - > segments ) {
2005-11-09 16:08:41 +03:00
if ( ! _add_segment_to_dtree ( dm , dtree , dnode , seg , layer ) )
2005-11-09 01:52:26 +03:00
return_0 ;
/* These aren't real segments in the LVM2 metadata */
if ( lv_is_origin ( lv ) & & ! layer )
break ;
if ( lv_is_cow ( lv ) & & ! layer )
break ;
2008-06-18 15:32:14 +04:00
if ( max_stripe_size < seg - > stripe_size * seg - > area_count )
max_stripe_size = seg - > stripe_size * seg - > area_count ;
2005-11-09 01:52:26 +03:00
}
2005-10-17 22:21:05 +04:00
2007-12-05 22:24:32 +03:00
if ( read_ahead = = DM_READ_AHEAD_AUTO ) {
2008-01-08 19:47:10 +03:00
/* we need RA at least twice a whole stripe - see the comment in md/raid0.c */
read_ahead = max_stripe_size * 2 ;
2009-05-20 15:09:49 +04:00
if ( ! read_ahead )
2009-06-01 16:43:31 +04:00
lv_calculate_readahead ( lv , & read_ahead ) ;
2007-11-29 18:04:12 +03:00
read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG ;
2007-12-05 22:24:32 +03:00
}
2007-11-12 23:51:54 +03:00
2007-11-29 18:04:12 +03:00
dm_tree_node_set_read_ahead ( dnode , read_ahead , read_ahead_flags ) ;
2007-11-12 23:51:54 +03:00
2005-10-18 16:39:20 +04:00
return 1 ;
2005-10-17 22:21:05 +04:00
}
2007-05-15 18:42:01 +04:00
/* FIXME: symlinks should be created/destroyed at the same time
* as the kernel devices but we can ' t do that from within libdevmapper
* at present so we must walk the tree twice instead . */
2005-11-09 01:52:26 +03:00
/*
* Create LV symlinks for children of supplied root node .
*/
2005-11-09 16:05:17 +03:00
static int _create_lv_symlinks ( struct dev_manager * dm , struct dm_tree_node * root )
2005-10-17 22:21:05 +04:00
{
2005-11-09 01:52:26 +03:00
void * handle = NULL ;
2005-11-09 16:05:17 +03:00
struct dm_tree_node * child ;
2005-11-09 01:52:26 +03:00
struct lv_layer * lvlayer ;
2008-12-19 17:22:48 +03:00
char * old_vgname , * old_lvname , * old_layer ;
char * new_vgname , * new_lvname , * new_layer ;
2005-11-09 01:52:26 +03:00
const char * name ;
int r = 1 ;
2005-10-17 22:21:05 +04:00
2005-11-09 16:05:17 +03:00
while ( ( child = dm_tree_next_child ( & handle , root , 0 ) ) ) {
if ( ! ( lvlayer = ( struct lv_layer * ) dm_tree_node_get_context ( child ) ) )
2005-11-09 01:52:26 +03:00
continue ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
/* Detect rename */
2005-11-09 16:05:17 +03:00
name = dm_tree_node_get_name ( child ) ;
2005-10-17 22:21:05 +04:00
2005-11-09 01:52:26 +03:00
if ( name & & lvlayer - > old_name & & * lvlayer - > old_name & & strcmp ( name , lvlayer - > old_name ) ) {
2008-12-19 17:22:48 +03:00
if ( ! dm_split_lvm_name ( dm - > mem , lvlayer - > old_name , & old_vgname , & old_lvname , & old_layer ) ) {
2008-01-30 17:00:02 +03:00
log_error ( " _create_lv_symlinks: Couldn't split up old device name %s " , lvlayer - > old_name ) ;
return 0 ;
}
2008-12-19 17:22:48 +03:00
if ( ! dm_split_lvm_name ( dm - > mem , name , & new_vgname , & new_lvname , & new_layer ) ) {
log_error ( " _create_lv_symlinks: Couldn't split up new device name %s " , name ) ;
return 0 ;
}
if ( ! fs_rename_lv ( lvlayer - > lv , name , old_vgname , old_lvname ) )
r = 0 ;
2009-05-28 05:11:29 +04:00
continue ;
}
if ( lv_is_visible ( lvlayer - > lv ) ) {
if ( ! dev_manager_lv_mknodes ( lvlayer - > lv ) )
r = 0 ;
continue ;
}
if ( ! dev_manager_lv_rmnodes ( lvlayer - > lv ) )
2005-11-09 01:52:26 +03:00
r = 0 ;
2005-10-17 22:21:05 +04:00
}
2005-11-09 01:52:26 +03:00
return r ;
}
2005-10-18 16:39:20 +04:00
2007-05-15 18:42:01 +04:00
/*
* Remove LV symlinks for children of supplied root node .
*/
static int _remove_lv_symlinks ( struct dev_manager * dm , struct dm_tree_node * root )
{
void * handle = NULL ;
struct dm_tree_node * child ;
char * vgname , * lvname , * layer ;
int r = 1 ;
while ( ( child = dm_tree_next_child ( & handle , root , 0 ) ) ) {
2008-01-30 17:00:02 +03:00
if ( ! dm_split_lvm_name ( dm - > mem , dm_tree_node_get_name ( child ) , & vgname , & lvname , & layer ) ) {
2007-05-15 18:42:01 +04:00
r = 0 ;
continue ;
}
2008-06-05 16:45:55 +04:00
if ( ! * vgname )
continue ;
2007-05-15 18:42:01 +04:00
/* only top level layer has symlinks */
if ( * layer )
continue ;
fs_del_lv_byname ( dm - > cmd - > dev_dir , vgname , lvname ) ;
}
return r ;
}
2006-05-11 21:58:58 +04:00
static int _clean_tree ( struct dev_manager * dm , struct dm_tree_node * root )
2005-11-09 01:52:26 +03:00
{
void * handle = NULL ;
2005-11-09 16:05:17 +03:00
struct dm_tree_node * child ;
2005-11-09 01:52:26 +03:00
char * vgname , * lvname , * layer ;
const char * name , * uuid ;
2009-08-03 22:01:45 +04:00
int r ;
2005-11-09 01:52:26 +03:00
2005-11-09 16:05:17 +03:00
while ( ( child = dm_tree_next_child ( & handle , root , 0 ) ) ) {
if ( ! ( name = dm_tree_node_get_name ( child ) ) )
2005-11-09 01:52:26 +03:00
continue ;
2005-11-09 16:05:17 +03:00
if ( ! ( uuid = dm_tree_node_get_uuid ( child ) ) )
2005-11-09 01:52:26 +03:00
continue ;
2008-01-30 17:00:02 +03:00
if ( ! dm_split_lvm_name ( dm - > mem , name , & vgname , & lvname , & layer ) ) {
log_error ( " _clean_tree: Couldn't split up device name %s. " , name ) ;
return 0 ;
}
2005-11-09 01:52:26 +03:00
/* Not meant to be top level? */
if ( ! * layer )
continue ;
2009-07-31 22:30:31 +04:00
dm_tree_set_cookie ( root , 0 ) ;
2009-08-03 22:01:45 +04:00
r = dm_tree_deactivate_children ( root , uuid , strlen ( uuid ) ) ;
2009-08-03 15:20:15 +04:00
if ( ! dm_udev_wait ( dm_tree_get_cookie ( root ) ) )
stack ;
2009-08-03 22:01:45 +04:00
if ( ! r )
return_0 ;
2005-11-09 01:52:26 +03:00
}
return 1 ;
2005-10-17 22:21:05 +04:00
}
2005-10-25 23:08:21 +04:00
static int _tree_action ( struct dev_manager * dm , struct logical_volume * lv , action_t action )
2005-10-17 22:21:05 +04:00
{
2005-11-09 16:05:17 +03:00
struct dm_tree * dtree ;
struct dm_tree_node * root ;
2005-10-18 16:39:20 +04:00
char * dlid ;
2005-10-17 22:21:05 +04:00
int r = 0 ;
2005-11-09 16:08:41 +03:00
if ( ! ( dtree = _create_partial_dtree ( dm , lv ) ) )
2005-11-09 01:52:26 +03:00
return_0 ;
2005-10-17 22:21:05 +04:00
2005-11-09 16:05:17 +03:00
if ( ! ( root = dm_tree_find_node ( dtree , 0 , 0 ) ) ) {
2005-10-18 16:39:20 +04:00
log_error ( " Lost dependency tree root node " ) ;
2005-10-17 22:21:05 +04:00
goto out ;
}
2005-11-09 01:52:26 +03:00
if ( ! ( dlid = build_dlid ( dm , lv - > lvid . s , NULL ) ) )
goto_out ;
2005-10-17 22:21:05 +04:00
2005-10-19 17:59:18 +04:00
/* Only process nodes with uuid of "LVM-" plus VG id. */
2005-10-25 23:08:21 +04:00
switch ( action ) {
2005-11-09 01:52:26 +03:00
case CLEAN :
/* Deactivate any unused non-toplevel nodes */
2006-05-11 21:58:58 +04:00
if ( ! _clean_tree ( dm , root ) )
2005-11-09 01:52:26 +03:00
goto_out ;
break ;
2005-10-25 23:08:21 +04:00
case DEACTIVATE :
2005-11-09 01:52:26 +03:00
/* Deactivate LV and all devices it references that nothing else has open. */
2009-07-31 22:30:31 +04:00
dm_tree_set_cookie ( root , 0 ) ;
2009-08-03 22:01:45 +04:00
r = dm_tree_deactivate_children ( root , dlid , ID_LEN + sizeof ( UUID_PREFIX ) - 1 ) ;
2009-08-03 15:20:15 +04:00
if ( ! dm_udev_wait ( dm_tree_get_cookie ( root ) ) )
stack ;
2009-08-03 22:01:45 +04:00
if ( ! r )
goto_out ;
2007-05-15 18:42:01 +04:00
if ( ! _remove_lv_symlinks ( dm , root ) )
log_error ( " Failed to remove all device symlinks associated with %s. " , lv - > name ) ;
2005-10-25 23:08:21 +04:00
break ;
case SUSPEND :
2006-08-09 01:20:00 +04:00
dm_tree_skip_lockfs ( root ) ;
2009-05-20 13:52:37 +04:00
if ( ! dm - > flush_required & & ( lv - > status & MIRRORED ) & & ! ( lv - > status & PVMOVE ) )
2007-01-09 23:31:08 +03:00
dm_tree_use_no_flush_suspend ( root ) ;
2006-08-09 01:20:00 +04:00
case SUSPEND_WITH_LOCKFS :
2005-11-09 16:05:17 +03:00
if ( ! dm_tree_suspend_children ( root , dlid , ID_LEN + sizeof ( UUID_PREFIX ) - 1 ) )
2005-11-09 01:52:26 +03:00
goto_out ;
break ;
case PRELOAD :
case ACTIVATE :
/* Add all required new devices to tree */
2005-11-09 16:08:41 +03:00
if ( ! _add_new_lv_to_dtree ( dm , dtree , lv , NULL ) )
2005-11-09 01:52:26 +03:00
goto_out ;
/* Preload any devices required before any suspensions */
2009-07-31 22:30:31 +04:00
dm_tree_set_cookie ( root , 0 ) ;
2009-08-03 22:01:45 +04:00
r = dm_tree_preload_children ( root , dlid , ID_LEN + sizeof ( UUID_PREFIX ) - 1 ) ;
2009-08-03 15:20:15 +04:00
if ( ! dm_udev_wait ( dm_tree_get_cookie ( root ) ) )
stack ;
2009-08-03 22:01:45 +04:00
if ( ! r )
goto_out ;
2005-11-09 01:52:26 +03:00
2009-05-20 13:52:37 +04:00
if ( dm_tree_node_size_changed ( root ) )
dm - > flush_required = 1 ;
2009-07-31 22:30:31 +04:00
if ( action = = ACTIVATE ) {
dm_tree_set_cookie ( root , 0 ) ;
2009-08-03 22:01:45 +04:00
r = dm_tree_activate_children ( root , dlid , ID_LEN + sizeof ( UUID_PREFIX ) - 1 ) ;
2009-08-03 15:20:15 +04:00
if ( ! dm_udev_wait ( dm_tree_get_cookie ( root ) ) )
stack ;
2009-08-03 22:01:45 +04:00
if ( ! r )
goto_out ;
2009-08-03 22:09:25 +04:00
if ( ! _create_lv_symlinks ( dm , root ) ) {
log_error ( " Failed to create symlinks for %s. " , lv - > name ) ;
goto out ;
}
2009-07-31 22:30:31 +04:00
}
2005-11-09 01:52:26 +03:00
2005-10-25 23:08:21 +04:00
break ;
default :
log_error ( " _tree_action: Action %u not supported. " , action ) ;
2005-10-17 22:21:05 +04:00
goto out ;
2005-10-25 23:08:21 +04:00
}
2005-10-17 22:21:05 +04:00
r = 1 ;
out :
2005-11-09 16:05:17 +03:00
dm_tree_free ( dtree ) ;
2005-10-17 22:21:05 +04:00
return r ;
}
2005-11-09 01:52:26 +03:00
int dev_manager_activate ( struct dev_manager * dm , struct logical_volume * lv )
{
if ( ! _tree_action ( dm , lv , ACTIVATE ) )
return_0 ;
return _tree_action ( dm , lv , CLEAN ) ;
}
2009-05-20 13:52:37 +04:00
int dev_manager_preload ( struct dev_manager * dm , struct logical_volume * lv ,
int * flush_required )
2005-11-09 01:52:26 +03:00
{
2005-11-10 02:56:36 +03:00
/* FIXME Update the pvmove implementation! */
if ( ( lv - > status & PVMOVE ) | | ( lv - > status & LOCKED ) )
return 1 ;
2009-05-20 13:52:37 +04:00
if ( ! _tree_action ( dm , lv , PRELOAD ) )
return 0 ;
* flush_required = dm - > flush_required ;
return 1 ;
2005-11-09 01:52:26 +03:00
}
2005-10-26 18:13:52 +04:00
2005-10-25 23:08:21 +04:00
int dev_manager_deactivate ( struct dev_manager * dm , struct logical_volume * lv )
{
int r ;
r = _tree_action ( dm , lv , DEACTIVATE ) ;
fs_del_lv ( lv ) ;
return r ;
}
2006-08-09 01:20:00 +04:00
int dev_manager_suspend ( struct dev_manager * dm , struct logical_volume * lv ,
2009-05-20 13:52:37 +04:00
int lockfs , int flush_required )
2005-10-26 18:13:52 +04:00
{
2009-05-20 13:52:37 +04:00
dm - > flush_required = flush_required ;
2006-08-09 01:20:00 +04:00
return _tree_action ( dm , lv , lockfs ? SUSPEND_WITH_LOCKFS : SUSPEND ) ;
2005-10-26 18:13:52 +04:00
}
2005-10-25 23:08:21 +04:00
/*
* Does device use VG somewhere in its construction ?
* Returns 1 if uncertain .
*/
2006-05-11 21:58:58 +04:00
int dev_manager_device_uses_vg ( struct device * dev ,
2005-10-25 23:08:21 +04:00
struct volume_group * vg )
{
2005-11-09 16:05:17 +03:00
struct dm_tree * dtree ;
struct dm_tree_node * root ;
2006-12-01 02:11:42 +03:00
char dlid [ sizeof ( UUID_PREFIX ) + sizeof ( struct id ) - 1 ] __attribute ( ( aligned ( 8 ) ) ) ;
2005-10-25 23:08:21 +04:00
int r = 1 ;
2005-11-09 16:05:17 +03:00
if ( ! ( dtree = dm_tree_create ( ) ) ) {
2005-11-09 16:08:41 +03:00
log_error ( " partial dtree creation failed " ) ;
2005-10-25 23:08:21 +04:00
return r ;
}
2006-05-11 21:58:58 +04:00
if ( ! dm_tree_add_dev ( dtree , ( uint32_t ) MAJOR ( dev - > dev ) , ( uint32_t ) MINOR ( dev - > dev ) ) ) {
2005-11-09 16:08:41 +03:00
log_error ( " Failed to add device %s (% " PRIu32 " :% " PRIu32 " ) to dtree " ,
2005-10-25 23:08:21 +04:00
dev_name ( dev ) , ( uint32_t ) MAJOR ( dev - > dev ) , ( uint32_t ) MINOR ( dev - > dev ) ) ;
goto out ;
}
memcpy ( dlid , UUID_PREFIX , sizeof ( UUID_PREFIX ) - 1 ) ;
memcpy ( dlid + sizeof ( UUID_PREFIX ) - 1 , & vg - > id . uuid [ 0 ] , sizeof ( vg - > id ) ) ;
2005-11-09 16:05:17 +03:00
if ( ! ( root = dm_tree_find_node ( dtree , 0 , 0 ) ) ) {
2005-10-25 23:08:21 +04:00
log_error ( " Lost dependency tree root node " ) ;
goto out ;
}
2005-11-09 16:05:17 +03:00
if ( dm_tree_children_use_uuid ( root , dlid , sizeof ( UUID_PREFIX ) + sizeof ( vg - > id ) - 1 ) )
2005-11-09 01:52:26 +03:00
goto_out ;
2005-10-25 23:08:21 +04:00
r = 0 ;
out :
2005-11-09 16:05:17 +03:00
dm_tree_free ( dtree ) ;
2005-10-25 23:08:21 +04:00
return r ;
}