2010-09-30 17:16:55 +04:00
/*
2010-10-01 00:47:18 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 - 2010 Red Hat , Inc . All rights reserved .
2010-09-30 17:16:55 +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 "metadata.h"
2010-10-25 17:54:29 +04:00
# include "display.h"
2010-09-30 17:16:55 +04:00
# include "activate.h"
2011-08-11 00:25:29 +04:00
# include "toolcontext.h"
2011-08-11 21:24:23 +04:00
# include "lvmcache.h"
2011-03-10 15:43:29 +03:00
struct volume_group * alloc_vg ( const char * pool_name , struct cmd_context * cmd ,
const char * vg_name )
{
struct dm_pool * vgmem ;
struct volume_group * vg ;
if ( ! ( vgmem = dm_pool_create ( pool_name , VG_MEMPOOL_CHUNK ) ) | |
! ( vg = dm_pool_zalloc ( vgmem , sizeof ( * vg ) ) ) ) {
log_error ( " Failed to allocate volume group structure " ) ;
if ( vgmem )
dm_pool_destroy ( vgmem ) ;
return NULL ;
}
if ( vg_name & & ! ( vg - > name = dm_pool_strdup ( vgmem , vg_name ) ) ) {
log_error ( " Failed to allocate VG name. " ) ;
dm_pool_destroy ( vgmem ) ;
return NULL ;
}
vg - > cmd = cmd ;
vg - > vgmem = vgmem ;
vg - > alloc = ALLOC_NORMAL ;
2012-01-19 19:31:45 +04:00
if ( ! ( vg - > hostnames = dm_hash_create ( 16 ) ) ) {
log_error ( " Failed to allocate VG hostname hashtable. " ) ;
dm_pool_destroy ( vgmem ) ;
return NULL ;
}
2011-03-10 15:43:29 +03:00
dm_list_init ( & vg - > pvs ) ;
2011-06-01 23:29:31 +04:00
dm_list_init ( & vg - > pvs_to_create ) ;
2011-03-10 15:43:29 +03:00
dm_list_init ( & vg - > lvs ) ;
dm_list_init ( & vg - > tags ) ;
dm_list_init ( & vg - > removed_pvs ) ;
2011-08-11 21:24:23 +04:00
log_debug ( " Allocated VG %s at %p. " , vg - > name , vg ) ;
2011-03-10 15:43:29 +03:00
return vg ;
}
2011-08-11 00:25:29 +04:00
static void _free_vg ( struct volume_group * vg )
{
vg_set_fid ( vg , NULL ) ;
if ( vg - > cmd & & vg - > vgmem = = vg - > cmd - > mem ) {
log_error ( INTERNAL_ERROR " global memory pool used for VG %s " ,
vg - > name ) ;
return ;
}
2011-08-11 21:24:23 +04:00
log_debug ( " Freeing VG %s at %p. " , vg - > name , vg ) ;
2012-01-19 19:31:45 +04:00
dm_hash_destroy ( vg - > hostnames ) ;
2011-08-11 00:25:29 +04:00
dm_pool_destroy ( vg - > vgmem ) ;
}
void release_vg ( struct volume_group * vg )
{
2012-02-10 06:53:03 +04:00
if ( ! vg | | ( vg - > fid & & vg = = vg - > fid - > fmt - > orphan_vg ) )
2011-08-11 00:25:29 +04:00
return ;
2011-08-11 21:24:23 +04:00
/* Check if there are any vginfo holders */
if ( vg - > vginfo & &
2012-02-10 05:28:27 +04:00
! lvmcache_vginfo_holders_dec_and_test_for_zero ( vg - > vginfo ) )
2011-08-11 21:24:23 +04:00
return ;
2011-08-11 00:25:29 +04:00
_free_vg ( vg ) ;
}
2010-09-30 17:16:55 +04:00
2012-02-13 15:03:59 +04:00
/*
* FIXME out of place , but the main ( cmd ) pool has been already
* destroyed and touching the fid ( also via release_vg ) will crash the
* program
*
* For now quick wrapper to allow destroy of orphan vg
*/
void free_orphan_vg ( struct volume_group * vg )
{
_free_vg ( vg ) ;
}
2010-09-30 18:08:33 +04:00
char * vg_fmt_dup ( const struct volume_group * vg )
{
if ( ! vg - > fid | | ! vg - > fid - > fmt )
return NULL ;
return dm_pool_strdup ( vg - > vgmem , vg - > fid - > fmt - > name ) ;
}
char * vg_name_dup ( const struct volume_group * vg )
{
return dm_pool_strdup ( vg - > vgmem , vg - > name ) ;
}
char * vg_system_id_dup ( const struct volume_group * vg )
{
return dm_pool_strdup ( vg - > vgmem , vg - > system_id ) ;
}
2010-09-30 18:07:47 +04:00
char * vg_uuid_dup ( const struct volume_group * vg )
{
return id_format_and_copy ( vg - > vgmem , & vg - > id ) ;
}
2010-09-30 18:08:19 +04:00
char * vg_tags_dup ( const struct volume_group * vg )
{
return tags_format_and_copy ( vg - > vgmem , & vg - > tags ) ;
}
2010-09-30 17:16:55 +04:00
uint32_t vg_seqno ( const struct volume_group * vg )
{
return vg - > seqno ;
}
uint64_t vg_status ( const struct volume_group * vg )
{
return vg - > status ;
}
uint64_t vg_size ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > extent_count * vg - > extent_size ;
}
uint64_t vg_free ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > free_count * vg - > extent_size ;
}
uint64_t vg_extent_size ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > extent_size ;
}
uint64_t vg_extent_count ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > extent_count ;
}
uint64_t vg_free_count ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > free_count ;
}
uint64_t vg_pv_count ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > pv_count ;
}
uint64_t vg_max_pv ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > max_pv ;
}
uint64_t vg_max_lv ( const struct volume_group * vg )
{
return ( uint64_t ) vg - > max_lv ;
}
unsigned snapshot_count ( const struct volume_group * vg )
{
struct lv_list * lvl ;
unsigned num_snapshots = 0 ;
dm_list_iterate_items ( lvl , & vg - > lvs )
if ( lv_is_cow ( lvl - > lv ) )
num_snapshots + + ;
return num_snapshots ;
}
unsigned vg_visible_lvs ( const struct volume_group * vg )
{
struct lv_list * lvl ;
unsigned lv_count = 0 ;
dm_list_iterate_items ( lvl , & vg - > lvs ) {
if ( lv_is_visible ( lvl - > lv ) )
lv_count + + ;
}
return lv_count ;
}
uint32_t vg_mda_count ( const struct volume_group * vg )
{
return dm_list_size ( & vg - > fid - > metadata_areas_in_use ) +
dm_list_size ( & vg - > fid - > metadata_areas_ignored ) ;
}
uint32_t vg_mda_used_count ( const struct volume_group * vg )
{
uint32_t used_count = 0 ;
struct metadata_area * mda ;
/*
* Ignored mdas could be on either list - the reason being the state
* may have changed from ignored to un - ignored and we need to write
* the state to disk .
*/
dm_list_iterate_items ( mda , & vg - > fid - > metadata_areas_in_use )
if ( ! mda_is_ignored ( mda ) )
used_count + + ;
return used_count ;
}
uint32_t vg_mda_copies ( const struct volume_group * vg )
{
return vg - > mda_copies ;
}
uint64_t vg_mda_size ( const struct volume_group * vg )
{
return find_min_mda_size ( & vg - > fid - > metadata_areas_in_use ) ;
}
uint64_t vg_mda_free ( const struct volume_group * vg )
{
uint64_t freespace = UINT64_MAX , mda_free ;
struct metadata_area * mda ;
dm_list_iterate_items ( mda , & vg - > fid - > metadata_areas_in_use ) {
if ( ! mda - > ops - > mda_free_sectors )
continue ;
mda_free = mda - > ops - > mda_free_sectors ( mda ) ;
if ( mda_free < freespace )
freespace = mda_free ;
}
if ( freespace = = UINT64_MAX )
freespace = UINT64_C ( 0 ) ;
return freespace ;
}
int vg_set_mda_copies ( struct volume_group * vg , uint32_t mda_copies )
{
vg - > mda_copies = mda_copies ;
/* FIXME Use log_verbose when this is due to specific cmdline request. */
log_debug ( " Setting mda_copies to % " PRIu32 " for VG %s " ,
mda_copies , vg - > name ) ;
return 1 ;
}
static int _recalc_extents ( uint32_t * extents , const char * desc1 ,
const char * desc2 , uint32_t old_size ,
uint32_t new_size )
{
uint64_t size = ( uint64_t ) old_size * ( * extents ) ;
if ( size % new_size ) {
log_error ( " New size % " PRIu64 " for %s%s not an exact number "
" of new extents. " , size , desc1 , desc2 ) ;
return 0 ;
}
size / = new_size ;
2011-11-05 02:49:53 +04:00
if ( size > MAX_EXTENT_COUNT ) {
2010-09-30 17:16:55 +04:00
log_error ( " New extent count % " PRIu64 " for %s%s exceeds "
" 32 bits. " , size , desc1 , desc2 ) ;
return 0 ;
}
* extents = ( uint32_t ) size ;
return 1 ;
}
int vg_set_extent_size ( struct volume_group * vg , uint32_t new_size )
{
uint32_t old_size = vg - > extent_size ;
struct pv_list * pvl ;
struct lv_list * lvl ;
struct physical_volume * pv ;
struct logical_volume * lv ;
struct lv_segment * seg ;
struct pv_segment * pvseg ;
uint32_t s ;
if ( ! vg_is_resizeable ( vg ) ) {
log_error ( " Volume group \" %s \" must be resizeable "
" to change PE size " , vg - > name ) ;
return 0 ;
}
if ( ! new_size ) {
log_error ( " Physical extent size may not be zero " ) ;
return 0 ;
}
if ( new_size = = vg - > extent_size )
return 1 ;
if ( new_size & ( new_size - 1 ) ) {
log_error ( " Physical extent size must be a power of 2. " ) ;
return 0 ;
}
if ( new_size > vg - > extent_size ) {
if ( ( uint64_t ) vg_size ( vg ) % new_size ) {
/* FIXME Adjust used PV sizes instead */
log_error ( " New extent size is not a perfect fit " ) ;
return 0 ;
}
}
vg - > extent_size = new_size ;
if ( vg - > fid - > fmt - > ops - > vg_setup & &
! vg - > fid - > fmt - > ops - > vg_setup ( vg - > fid , vg ) )
return_0 ;
if ( ! _recalc_extents ( & vg - > extent_count , vg - > name , " " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents ( & vg - > free_count , vg - > name , " free space " ,
old_size , new_size ) )
return_0 ;
/* foreach PV */
dm_list_iterate_items ( pvl , & vg - > pvs ) {
pv = pvl - > pv ;
pv - > pe_size = new_size ;
if ( ! _recalc_extents ( & pv - > pe_count , pv_dev_name ( pv ) , " " ,
old_size , new_size ) )
return_0 ;
if ( ! _recalc_extents ( & pv - > pe_alloc_count , pv_dev_name ( pv ) ,
" allocated space " , old_size , new_size ) )
return_0 ;
/* foreach free PV Segment */
dm_list_iterate_items ( pvseg , & pv - > segments ) {
if ( pvseg_is_allocated ( pvseg ) )
continue ;
if ( ! _recalc_extents ( & pvseg - > pe , pv_dev_name ( pv ) ,
" PV segment start " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents ( & pvseg - > len , pv_dev_name ( pv ) ,
" PV segment length " , old_size ,
new_size ) )
return_0 ;
}
}
/* foreach LV */
dm_list_iterate_items ( lvl , & vg - > lvs ) {
lv = lvl - > lv ;
if ( ! _recalc_extents ( & lv - > le_count , lv - > name , " " , old_size ,
new_size ) )
return_0 ;
dm_list_iterate_items ( seg , & lv - > segments ) {
if ( ! _recalc_extents ( & seg - > le , lv - > name ,
" segment start " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents ( & seg - > len , lv - > name ,
" segment length " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents ( & seg - > area_len , lv - > name ,
" area length " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents ( & seg - > extents_copied , lv - > name ,
" extents moved " , old_size ,
new_size ) )
return_0 ;
/* foreach area */
for ( s = 0 ; s < seg - > area_count ; s + + ) {
switch ( seg_type ( seg , s ) ) {
case AREA_PV :
if ( ! _recalc_extents
( & seg_pe ( seg , s ) ,
lv - > name ,
" pvseg start " , old_size ,
new_size ) )
return_0 ;
if ( ! _recalc_extents
( & seg_pvseg ( seg , s ) - > len ,
lv - > name ,
" pvseg length " , old_size ,
new_size ) )
return_0 ;
break ;
case AREA_LV :
if ( ! _recalc_extents
( & seg_le ( seg , s ) , lv - > name ,
" area start " , old_size ,
new_size ) )
return_0 ;
break ;
case AREA_UNASSIGNED :
log_error ( " Unassigned area %u found in "
" segment " , s ) ;
return 0 ;
}
}
}
}
return 1 ;
}
int vg_set_max_lv ( struct volume_group * vg , uint32_t max_lv )
{
if ( ! vg_is_resizeable ( vg ) ) {
log_error ( " Volume group \" %s \" must be resizeable "
" to change MaxLogicalVolume " , vg - > name ) ;
return 0 ;
}
if ( ! ( vg - > fid - > fmt - > features & FMT_UNLIMITED_VOLS ) ) {
if ( ! max_lv )
max_lv = 255 ;
else if ( max_lv > 255 ) {
log_error ( " MaxLogicalVolume limit is 255 " ) ;
return 0 ;
}
}
if ( max_lv & & max_lv < vg_visible_lvs ( vg ) ) {
log_error ( " MaxLogicalVolume is less than the current number "
" %d of LVs for %s " , vg_visible_lvs ( vg ) ,
vg - > name ) ;
return 0 ;
}
vg - > max_lv = max_lv ;
return 1 ;
}
int vg_set_max_pv ( struct volume_group * vg , uint32_t max_pv )
{
if ( ! vg_is_resizeable ( vg ) ) {
log_error ( " Volume group \" %s \" must be resizeable "
" to change MaxPhysicalVolumes " , vg - > name ) ;
return 0 ;
}
if ( ! ( vg - > fid - > fmt - > features & FMT_UNLIMITED_VOLS ) ) {
if ( ! max_pv )
max_pv = 255 ;
else if ( max_pv > 255 ) {
log_error ( " MaxPhysicalVolume limit is 255 " ) ;
return 0 ;
}
}
if ( max_pv & & max_pv < vg - > pv_count ) {
log_error ( " MaxPhysicalVolumes is less than the current number "
" %d of PVs for \" %s \" " , vg - > pv_count ,
vg - > name ) ;
return 0 ;
}
vg - > max_pv = max_pv ;
return 1 ;
}
int vg_set_alloc_policy ( struct volume_group * vg , alloc_policy_t alloc )
{
if ( alloc = = ALLOC_INHERIT ) {
log_error ( " Volume Group allocation policy cannot inherit "
" from anything " ) ;
return 0 ;
}
if ( alloc = = vg - > alloc )
return 1 ;
vg - > alloc = alloc ;
return 1 ;
}
int vg_set_clustered ( struct volume_group * vg , int clustered )
{
struct lv_list * lvl ;
/*
* We do not currently support switching the cluster attribute
* on active mirrors or snapshots .
*/
dm_list_iterate_items ( lvl , & vg - > lvs ) {
2012-04-25 17:38:41 +04:00
if ( lv_is_active ( lvl - > lv ) & &
( lv_is_mirrored ( lvl - > lv ) | | lv_is_raid_type ( lvl - > lv ) ) ) {
log_error ( " %s logical volumes must be inactive "
" when changing the cluster attribute. " ,
lv_is_raid_type ( lvl - > lv ) ? " RAID " : " Mirror " ) ;
2010-09-30 17:16:55 +04:00
return 0 ;
}
if ( clustered ) {
if ( lv_is_origin ( lvl - > lv ) | | lv_is_cow ( lvl - > lv ) ) {
log_error ( " Volume group %s contains snapshots "
" that are not yet supported. " ,
vg - > name ) ;
return 0 ;
}
}
if ( ( lv_is_origin ( lvl - > lv ) | | lv_is_cow ( lvl - > lv ) ) & &
lv_is_active ( lvl - > lv ) ) {
log_error ( " Snapshot logical volumes must be inactive "
" when changing the cluster attribute. " ) ;
return 0 ;
}
}
if ( clustered )
vg - > status | = CLUSTERED ;
else
vg - > status & = ~ CLUSTERED ;
return 1 ;
}
2010-09-30 17:52:55 +04:00
char * vg_attr_dup ( struct dm_pool * mem , const struct volume_group * vg )
{
char * repstr ;
if ( ! ( repstr = dm_pool_zalloc ( mem , 7 ) ) ) {
log_error ( " dm_pool_alloc failed " ) ;
return NULL ;
}
2010-09-30 18:07:19 +04:00
repstr [ 0 ] = ( vg - > status & LVM_WRITE ) ? ' w ' : ' r ' ;
repstr [ 1 ] = ( vg_is_resizeable ( vg ) ) ? ' z ' : ' - ' ;
repstr [ 2 ] = ( vg_is_exported ( vg ) ) ? ' x ' : ' - ' ;
repstr [ 3 ] = ( vg_missing_pv_count ( vg ) ) ? ' p ' : ' - ' ;
2010-09-30 17:52:55 +04:00
repstr [ 4 ] = alloc_policy_char ( vg - > alloc ) ;
2010-09-30 18:07:19 +04:00
repstr [ 5 ] = ( vg_is_clustered ( vg ) ) ? ' c ' : ' - ' ;
2010-09-30 17:52:55 +04:00
return repstr ;
}