2005-04-20 00:52:35 +04:00
/*
* Copyright ( C ) 2003 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2005-04-20 00:52:35 +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 .
2005-04-20 00:52:35 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2005-04-20 00:52:35 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-20 00:52:35 +04:00
*/
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
# include "lib/metadata/metadata.h"
# include "lib/metadata/pv_alloc.h"
# include "lib/commands/toolcontext.h"
# include "lib/locking/locking.h"
# include "lib/config/defaults.h"
# include "lib/display/display.h"
2005-04-20 00:52:35 +04:00
2005-10-17 03:03:59 +04:00
static struct pv_segment * _alloc_pv_segment ( struct dm_pool * mem ,
2005-04-20 00:52:35 +04:00
struct physical_volume * pv ,
2005-05-03 21:28:23 +04:00
uint32_t pe , uint32_t len ,
struct lv_segment * lvseg ,
uint32_t lv_area )
2005-04-20 00:52:35 +04:00
{
struct pv_segment * peg ;
2005-10-17 03:03:59 +04:00
if ( ! ( peg = dm_pool_zalloc ( mem , sizeof ( * peg ) ) ) ) {
2005-04-20 00:52:35 +04:00
log_error ( " pv_segment allocation failed " ) ;
return NULL ;
}
peg - > pv = pv ;
peg - > pe = pe ;
peg - > len = len ;
2005-05-03 21:28:23 +04:00
peg - > lvseg = lvseg ;
peg - > lv_area = lv_area ;
2005-04-20 00:52:35 +04:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & peg - > list ) ;
2005-04-20 00:52:35 +04:00
return peg ;
}
2005-10-17 03:03:59 +04:00
int alloc_pv_segment_whole_pv ( struct dm_pool * mem , struct physical_volume * pv )
2005-04-20 00:52:35 +04:00
{
2005-05-03 21:28:23 +04:00
struct pv_segment * peg ;
if ( ! pv - > pe_count )
return 1 ;
2005-04-20 00:52:35 +04:00
/* FIXME Cope with holes in PVs */
2008-01-30 16:19:47 +03:00
if ( ! ( peg = _alloc_pv_segment ( mem , pv , 0 , pv - > pe_count , NULL , 0 ) ) )
return_0 ;
2005-04-20 00:52:35 +04:00
2008-11-04 01:14:30 +03:00
dm_list_add ( & pv - > segments , & peg - > list ) ;
2005-04-20 00:52:35 +04:00
2005-05-03 21:28:23 +04:00
return 1 ;
2005-04-20 00:52:35 +04:00
}
2008-11-04 01:14:30 +03:00
int peg_dup ( struct dm_pool * mem , struct dm_list * peg_new , struct dm_list * peg_old )
2005-04-20 00:52:35 +04:00
{
struct pv_segment * peg , * pego ;
2008-11-04 01:14:30 +03:00
dm_list_init ( peg_new ) ;
2005-04-20 00:52:35 +04:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pego , peg_old ) {
2005-04-20 00:52:35 +04:00
if ( ! ( peg = _alloc_pv_segment ( mem , pego - > pv , pego - > pe ,
2005-05-03 21:28:23 +04:00
pego - > len , pego - > lvseg ,
2008-01-30 16:19:47 +03:00
pego - > lv_area ) ) )
return_0 ;
2008-11-04 01:14:30 +03:00
dm_list_add ( peg_new , & peg - > list ) ;
2005-04-20 00:52:35 +04:00
}
return 1 ;
}
2010-03-31 21:21:40 +04:00
/* Find segment at a given physical extent in a PV */
2017-10-18 17:57:46 +03:00
static struct pv_segment * _find_peg_by_pe ( const struct physical_volume * pv ,
uint32_t pe )
2010-03-31 21:21:40 +04:00
{
struct pv_segment * pvseg ;
/* search backwards to optimise mostly used last segment split */
dm_list_iterate_back_items ( pvseg , & pv - > segments )
if ( pe > = pvseg - > pe & & pe < pvseg - > pe + pvseg - > len )
return pvseg ;
return NULL ;
}
2005-05-03 21:28:23 +04:00
/*
* Split peg at given extent .
2010-03-31 21:22:26 +04:00
* Second part is always not allocated to a LV and returned .
2005-05-03 21:28:23 +04:00
*/
2010-03-31 21:23:18 +04:00
static struct pv_segment * _pv_split_segment ( struct dm_pool * mem ,
struct physical_volume * pv ,
2010-03-31 21:22:26 +04:00
struct pv_segment * peg ,
uint32_t pe )
2005-05-03 21:28:23 +04:00
{
struct pv_segment * peg_new ;
2010-03-31 21:23:18 +04:00
if ( ! ( peg_new = _alloc_pv_segment ( mem , peg - > pv , pe ,
2005-05-03 21:28:23 +04:00
peg - > len + peg - > pe - pe ,
2008-01-30 16:19:47 +03:00
NULL , 0 ) ) )
2010-03-31 21:22:26 +04:00
return_NULL ;
2005-05-03 21:28:23 +04:00
peg - > len = peg - > len - peg_new - > len ;
2008-11-04 01:14:30 +03:00
dm_list_add_h ( & peg - > list , & peg_new - > list ) ;
2005-05-03 21:28:23 +04:00
2005-07-12 23:40:59 +04:00
if ( peg - > lvseg ) {
peg - > pv - > pe_alloc_count - = peg_new - > len ;
peg - > lvseg - > lv - > vg - > free_count + = peg_new - > len ;
}
2010-03-31 21:22:26 +04:00
return peg_new ;
2005-05-03 21:28:23 +04:00
}
/*
* Ensure there is a PV segment boundary at the given extent .
*/
2010-03-31 21:23:18 +04:00
int pv_split_segment ( struct dm_pool * mem ,
struct physical_volume * pv , uint32_t pe ,
2010-03-31 21:22:26 +04:00
struct pv_segment * * pvseg_allocated )
2005-05-03 21:28:23 +04:00
{
2010-03-31 21:22:26 +04:00
struct pv_segment * pvseg , * pvseg_new = NULL ;
2005-05-03 21:28:23 +04:00
if ( pe = = pv - > pe_count )
2010-03-31 21:22:26 +04:00
goto out ;
2005-05-03 21:28:23 +04:00
2017-10-18 17:57:46 +03:00
if ( ! ( pvseg = _find_peg_by_pe ( pv , pe ) ) ) {
2005-05-03 21:28:23 +04:00
log_error ( " Segment with extent % " PRIu32 " in PV %s not found " ,
2007-10-12 18:29:32 +04:00
pe , pv_dev_name ( pv ) ) ;
2005-05-03 21:28:23 +04:00
return 0 ;
}
/* This is a peg start already */
2010-03-31 21:22:26 +04:00
if ( pe = = pvseg - > pe ) {
pvseg_new = pvseg ;
goto out ;
}
2005-05-03 21:28:23 +04:00
2010-03-31 21:23:18 +04:00
if ( ! ( pvseg_new = _pv_split_segment ( mem , pv , pvseg , pe ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2010-03-31 21:22:26 +04:00
out :
if ( pvseg_allocated )
* pvseg_allocated = pvseg_new ;
2005-05-03 21:28:23 +04:00
return 1 ;
}
2017-10-18 17:57:46 +03:00
static struct pv_segment _null_pv_segment = {
2006-05-10 01:23:51 +04:00
. pv = NULL ,
. pe = 0 ,
2005-05-11 19:04:06 +04:00
} ;
2005-05-03 21:28:23 +04:00
struct pv_segment * assign_peg_to_lvseg ( struct physical_volume * pv ,
uint32_t pe , uint32_t area_len ,
struct lv_segment * seg ,
uint32_t area_num )
{
2010-03-31 21:22:26 +04:00
struct pv_segment * peg = NULL ;
2005-05-03 21:28:23 +04:00
2005-05-11 19:04:06 +04:00
/* Missing format1 PV */
if ( ! pv )
2017-10-18 17:57:46 +03:00
return & _null_pv_segment ;
2005-05-11 19:04:06 +04:00
2010-03-31 21:23:18 +04:00
if ( ! pv_split_segment ( seg - > lv - > vg - > vgmem , pv , pe , & peg ) | |
! pv_split_segment ( seg - > lv - > vg - > vgmem , pv , pe + area_len , NULL ) )
2008-01-30 16:19:47 +03:00
return_NULL ;
2005-05-03 21:28:23 +04:00
2010-03-31 21:22:26 +04:00
if ( ! peg ) {
2005-05-03 21:28:23 +04:00
log_error ( " Missing PV segment on %s at %u. " ,
2007-10-12 18:29:32 +04:00
pv_dev_name ( pv ) , pe ) ;
2005-05-03 21:28:23 +04:00
return NULL ;
}
peg - > lvseg = seg ;
peg - > lv_area = area_num ;
2005-06-14 21:54:48 +04:00
peg - > pv - > pe_alloc_count + = area_len ;
peg - > lvseg - > lv - > vg - > free_count - = area_len ;
2005-05-03 21:28:23 +04:00
return peg ;
}
2012-06-27 23:07:04 +04:00
int discard_pv_segment ( struct pv_segment * peg , uint32_t discard_area_reduction )
2005-05-03 21:28:23 +04:00
{
2011-04-13 22:26:39 +04:00
uint64_t discard_offset_sectors ;
2011-04-13 01:59:01 +04:00
uint64_t pe_start = peg - > pv - > pe_start ;
2012-09-19 15:48:56 +04:00
char uuid [ 64 ] __attribute__ ( ( aligned ( 8 ) ) ) ;
2011-04-13 01:59:01 +04:00
2005-07-12 23:40:59 +04:00
if ( ! peg - > lvseg ) {
2012-06-27 23:07:04 +04:00
log_error ( " discard_pv_segment with unallocated segment: "
2007-10-12 18:29:32 +04:00
" %s PE % " PRIu32 , pv_dev_name ( peg - > pv ) , peg - > pe ) ;
2005-07-12 23:40:59 +04:00
return 0 ;
}
2005-06-01 20:51:55 +04:00
2011-04-13 01:59:01 +04:00
/*
* Only issue discards if enabled in lvm . conf and both
* the device and kernel ( > = 2.6 .35 ) supports discards .
*/
2013-06-25 14:31:53 +04:00
if ( ! find_config_tree_bool ( peg - > pv - > fmt - > cmd , devices_issue_discards_CFG , NULL ) )
2012-09-19 15:48:56 +04:00
return 1 ;
/* Missing PV? */
if ( is_missing_pv ( peg - > pv ) | | ! peg - > pv - > dev ) {
if ( ! id_write_format ( & peg - > pv - > id , uuid , sizeof ( uuid ) ) )
return_0 ;
log_verbose ( " Skipping discard on missing device with uuid %s. " , uuid ) ;
return 1 ;
}
2013-06-12 14:08:56 +04:00
if ( ! dev_discard_max_bytes ( peg - > pv - > fmt - > cmd - > dev_types , peg - > pv - > dev ) | |
! dev_discard_granularity ( peg - > pv - > fmt - > cmd - > dev_types , peg - > pv - > dev ) )
2012-06-27 23:07:04 +04:00
return 1 ;
discard_offset_sectors = ( peg - > pe + peg - > lvseg - > area_len - discard_area_reduction ) *
( uint64_t ) peg - > pv - > vg - > extent_size + pe_start ;
if ( ! discard_offset_sectors ) {
/*
* pe_start = 0 and the PV ' s first extent contains the label .
* Must skip past the first extent .
*/
discard_offset_sectors = peg - > pv - > vg - > extent_size ;
discard_area_reduction - - ;
}
2013-01-08 02:30:29 +04:00
log_debug_alloc ( " Discarding % " PRIu32 " extents offset % " PRIu64 " sectors on %s. " ,
discard_area_reduction , discard_offset_sectors , dev_name ( peg - > pv - > dev ) ) ;
2012-06-27 23:07:04 +04:00
if ( discard_area_reduction & &
! dev_discard_blocks ( peg - > pv - > dev , discard_offset_sectors < < SECTOR_SHIFT ,
discard_area_reduction * ( uint64_t ) peg - > pv - > vg - > extent_size * SECTOR_SIZE ) )
return_0 ;
return 1 ;
}
2014-06-26 07:04:58 +04:00
static int _merge_free_pv_segment ( struct pv_segment * peg )
{
struct dm_list * l ;
struct pv_segment * merge_peg ;
if ( peg - > lvseg ) {
log_error ( INTERNAL_ERROR
" _merge_free_pv_seg called on a "
" segment that is not free. " ) ;
return 0 ;
}
/*
* FIXME :
* Should we free the list element once it is deleted
* from the list ? I think not . It is likely part of
* a mempool .
*/
/* Attempt to merge with Free space before */
if ( ( l = dm_list_prev ( & peg - > pv - > segments , & peg - > list ) ) ) {
merge_peg = dm_list_item ( l , struct pv_segment ) ;
if ( ! merge_peg - > lvseg ) {
merge_peg - > len + = peg - > len ;
dm_list_del ( & peg - > list ) ;
peg = merge_peg ;
}
}
/* Attempt to merge with Free space after */
if ( ( l = dm_list_next ( & peg - > pv - > segments , & peg - > list ) ) ) {
merge_peg = dm_list_item ( l , struct pv_segment ) ;
if ( ! merge_peg - > lvseg ) {
peg - > len + = merge_peg - > len ;
dm_list_del ( & merge_peg - > list ) ;
}
}
return 1 ;
}
/*
* release_pv_segment
* @ peg
* @ area_reduction
*
* WARNING : When release_pv_segment is called , the freed space may be
* merged into the ' pv_segment ' s before and after it in the
* list if they are also free . Thus , any iterators of the
* ' pv - > segments ' list that call this function must be aware
* that the list can change in a way that is unsafe even for
* * _safe iterators . Restart the iterator in these cases .
*
* Returns : 1 on success , 0 on failure
*/
2012-06-27 23:07:04 +04:00
int release_pv_segment ( struct pv_segment * peg , uint32_t area_reduction )
{
2014-06-26 07:04:58 +04:00
struct dm_list * l ;
struct pv_segment * merge_peg ;
2012-06-27 23:07:04 +04:00
if ( ! peg - > lvseg ) {
log_error ( " release_pv_segment with unallocated segment: "
" %s PE % " PRIu32 , pv_dev_name ( peg - > pv ) , peg - > pe ) ;
return 0 ;
2011-04-13 01:59:01 +04:00
}
2005-06-14 21:54:48 +04:00
if ( peg - > lvseg - > area_len = = area_reduction ) {
2005-07-12 23:40:59 +04:00
peg - > pv - > pe_alloc_count - = area_reduction ;
peg - > lvseg - > lv - > vg - > free_count + = area_reduction ;
2005-05-03 21:28:23 +04:00
peg - > lvseg = NULL ;
peg - > lv_area = 0 ;
2014-06-26 07:04:58 +04:00
return _merge_free_pv_segment ( peg ) ;
2005-05-03 21:28:23 +04:00
}
2010-03-31 21:23:18 +04:00
if ( ! pv_split_segment ( peg - > lvseg - > lv - > vg - > vgmem ,
peg - > pv , peg - > pe + peg - > lvseg - > area_len -
area_reduction , NULL ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2005-05-03 21:28:23 +04:00
2014-06-26 07:04:58 +04:00
/* The segment after 'peg' now holds free space, try to merge it */
if ( ( l = dm_list_next ( & peg - > pv - > segments , & peg - > list ) ) ) {
merge_peg = dm_list_item ( l , struct pv_segment ) ;
return _merge_free_pv_segment ( merge_peg ) ;
}
2005-05-03 21:28:23 +04:00
return 1 ;
}
/*
* Only for use by lv_segment merging routines .
*/
void merge_pv_segments ( struct pv_segment * peg1 , struct pv_segment * peg2 )
{
peg1 - > len + = peg2 - > len ;
2008-11-04 01:14:30 +03:00
dm_list_del ( & peg2 - > list ) ;
2005-05-03 21:28:23 +04:00
}
2007-09-21 01:39:08 +04:00
/*
* Calculate the overlap , in extents , between a struct pv_segment and
* a struct pe_range .
*/
2007-09-25 01:30:00 +04:00
static uint32_t _overlap_pe ( const struct pv_segment * pvseg ,
const struct pe_range * per )
2007-09-21 01:39:08 +04:00
{
uint32_t start ;
uint32_t end ;
start = max ( pvseg - > pe , per - > start ) ;
end = min ( pvseg - > pe + pvseg - > len , per - > start + per - > count ) ;
2017-07-19 17:16:12 +03:00
2007-09-21 01:39:08 +04:00
if ( end < start )
return 0 ;
2017-07-19 17:16:12 +03:00
return end - start ;
2007-09-21 01:39:08 +04:00
}
/*
* Returns : number of free PEs in a struct pv_list
*/
2008-11-04 01:14:30 +03:00
uint32_t pv_list_extents_free ( const struct dm_list * pvh )
2007-09-21 01:39:08 +04:00
{
struct pv_list * pvl ;
struct pe_range * per ;
uint32_t extents = 0 ;
struct pv_segment * pvseg ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , pvh ) {
2014-10-28 13:02:58 +03:00
if ( ! pvl - > pe_ranges ) {
log_warn ( INTERNAL_ERROR " WARNING: PV %s is without initialized PE ranges. " , dev_name ( pvl - > pv - > dev ) ) ;
continue ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( per , pvl - > pe_ranges ) {
dm_list_iterate_items ( pvseg , & pvl - > pv - > segments ) {
2008-03-26 19:48:10 +03:00
if ( ! pvseg_is_allocated ( pvseg ) )
2007-09-21 01:39:08 +04:00
extents + = _overlap_pe ( pvseg , per ) ;
}
}
}
return extents ;
}
2005-05-03 21:28:23 +04:00
/*
* Check all pv_segments in VG for consistency
*/
int check_pv_segments ( struct volume_group * vg )
{
struct physical_volume * pv ;
struct pv_list * pvl ;
struct pv_segment * peg ;
unsigned s , segno ;
2005-06-01 20:51:55 +04:00
uint32_t start_pe , alloced ;
uint32_t pv_count = 0 , free_count = 0 , extent_count = 0 ;
2005-05-03 21:28:23 +04:00
int ret = 1 ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( pvl , & vg - > pvs ) {
2005-05-03 21:28:23 +04:00
pv = pvl - > pv ;
segno = 0 ;
start_pe = 0 ;
2005-06-01 20:51:55 +04:00
alloced = 0 ;
pv_count + + ;
2005-05-03 21:28:23 +04:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( peg , & pv - > segments ) {
2005-05-03 21:28:23 +04:00
s = peg - > lv_area ;
/* FIXME Remove this next line eventually */
2013-01-08 02:30:29 +04:00
log_debug_alloc ( " %s %u: %6u %6u: %s(%u:%u) " ,
pv_dev_name ( pv ) , segno + + , peg - > pe , peg - > len ,
peg - > lvseg ? peg - > lvseg - > lv - > name : " NULL " ,
peg - > lvseg ? peg - > lvseg - > le : 0 , s ) ;
2005-05-03 21:28:23 +04:00
/* FIXME Add details here on failure instead */
if ( start_pe ! = peg - > pe ) {
2005-06-01 20:51:55 +04:00
log_error ( " Gap in pvsegs: %u, %u " ,
2005-05-03 21:28:23 +04:00
start_pe , peg - > pe ) ;
ret = 0 ;
}
if ( peg - > lvseg ) {
2005-06-01 20:51:55 +04:00
if ( seg_type ( peg - > lvseg , s ) ! = AREA_PV ) {
log_error ( " Wrong lvseg area type " ) ;
2005-05-03 21:28:23 +04:00
ret = 0 ;
}
2005-06-01 20:51:55 +04:00
if ( seg_pvseg ( peg - > lvseg , s ) ! = peg ) {
log_error ( " Inconsistent pvseg pointers " ) ;
2005-05-03 21:28:23 +04:00
ret = 0 ;
}
if ( peg - > lvseg - > area_len ! = peg - > len ) {
2005-06-01 20:51:55 +04:00
log_error ( " Inconsistent length: %u %u " ,
2005-05-03 21:28:23 +04:00
peg - > len ,
peg - > lvseg - > area_len ) ;
ret = 0 ;
}
2005-06-01 20:51:55 +04:00
alloced + = peg - > len ;
2005-05-03 21:28:23 +04:00
}
start_pe + = peg - > len ;
}
2005-06-01 20:51:55 +04:00
if ( start_pe ! = pv - > pe_count ) {
log_error ( " PV segment pe_count mismatch: %u != %u " ,
start_pe , pv - > pe_count ) ;
ret = 0 ;
}
if ( alloced ! = pv - > pe_alloc_count ) {
log_error ( " PV segment pe_alloc_count mismatch: "
" %u != %u " , alloced , pv - > pe_alloc_count ) ;
ret = 0 ;
}
extent_count + = start_pe ;
free_count + = ( start_pe - alloced ) ;
}
if ( pv_count ! = vg - > pv_count ) {
log_error ( " PV segment VG pv_count mismatch: %u != %u " ,
pv_count , vg - > pv_count ) ;
ret = 0 ;
}
if ( free_count ! = vg - > free_count ) {
log_error ( " PV segment VG free_count mismatch: %u != %u " ,
free_count , vg - > free_count ) ;
ret = 0 ;
}
if ( extent_count ! = vg - > extent_count ) {
log_error ( " PV segment VG extent_count mismatch: %u != %u " ,
extent_count , vg - > extent_count ) ;
ret = 0 ;
2005-05-03 21:28:23 +04:00
}
return ret ;
}
2005-10-31 05:37:29 +03:00
2011-02-21 15:27:26 +03:00
static int _reduce_pv ( struct physical_volume * pv , struct volume_group * vg ,
uint32_t old_pe_count , uint32_t new_pe_count )
2005-10-31 05:37:29 +03:00
{
struct pv_segment * peg , * pegt ;
if ( new_pe_count < pv - > pe_alloc_count ) {
log_error ( " %s: cannot resize to % " PRIu32 " extents "
" as % " PRIu32 " are allocated. " ,
2007-10-12 18:29:32 +04:00
pv_dev_name ( pv ) , new_pe_count ,
2005-10-31 05:37:29 +03:00
pv - > pe_alloc_count ) ;
return 0 ;
}
/* Check PEs to be removed are not already allocated */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( peg , & pv - > segments ) {
2005-10-31 05:37:29 +03:00
if ( peg - > pe + peg - > len < = new_pe_count )
continue ;
if ( peg - > lvseg ) {
log_error ( " %s: cannot resize to % " PRIu32 " extents as "
" later ones are allocated. " ,
2007-10-12 18:29:32 +04:00
pv_dev_name ( pv ) , new_pe_count ) ;
2005-10-31 05:37:29 +03:00
return 0 ;
}
}
2010-03-31 21:23:18 +04:00
if ( ! pv_split_segment ( vg - > vgmem , pv , new_pe_count , NULL ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2005-10-31 05:37:29 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items_safe ( peg , pegt , & pv - > segments ) {
2005-10-31 05:37:29 +03:00
if ( peg - > pe + peg - > len > new_pe_count )
2008-11-04 01:14:30 +03:00
dm_list_del ( & peg - > list ) ;
2005-10-31 05:37:29 +03:00
}
pv - > pe_count = new_pe_count ;
vg - > extent_count - = ( old_pe_count - new_pe_count ) ;
vg - > free_count - = ( old_pe_count - new_pe_count ) ;
return 1 ;
}
static int _extend_pv ( struct physical_volume * pv , struct volume_group * vg ,
2011-02-21 15:27:26 +03:00
uint32_t old_pe_count , uint32_t new_pe_count )
2005-10-31 05:37:29 +03:00
{
struct pv_segment * peg ;
if ( ( uint64_t ) new_pe_count * pv - > pe_size > pv - > size ) {
log_error ( " %s: cannot resize to % " PRIu32 " extents as there "
2007-10-12 18:29:32 +04:00
" is only room for % " PRIu64 " . " , pv_dev_name ( pv ) ,
2005-10-31 05:37:29 +03:00
new_pe_count , pv - > size / pv - > pe_size ) ;
return 0 ;
}
2012-03-02 01:49:32 +04:00
if ( ! ( peg = _alloc_pv_segment ( pv - > fmt - > cmd - > mem , pv ,
old_pe_count ,
new_pe_count - old_pe_count ,
NULL , 0 ) ) )
return_0 ;
2008-11-04 01:14:30 +03:00
dm_list_add ( & pv - > segments , & peg - > list ) ;
2005-10-31 05:37:29 +03:00
pv - > pe_count = new_pe_count ;
vg - > extent_count + = ( new_pe_count - old_pe_count ) ;
vg - > free_count + = ( new_pe_count - old_pe_count ) ;
return 1 ;
}
/*
* Resize a PV in a VG , adding or removing segments as needed .
* New size must fit within pv - > size .
*/
2017-10-18 17:57:46 +03:00
static int _pv_resize ( struct physical_volume * pv , struct volume_group * vg , uint64_t size )
2005-10-31 05:37:29 +03:00
{
2011-02-21 15:27:26 +03:00
uint32_t old_pe_count , new_pe_count = 0 ;
if ( size < pv_min_size ( ) ) {
2011-02-21 16:09:27 +03:00
log_error ( " Size must exceed minimum of % " PRIu64 " sectors on PV %s. " ,
2011-02-21 15:27:26 +03:00
pv_min_size ( ) , pv_dev_name ( pv ) ) ;
return 0 ;
2005-10-31 05:37:29 +03:00
}
2011-02-21 15:27:26 +03:00
if ( size < pv_pe_start ( pv ) ) {
log_error ( " Size must exceed physical extent start "
" of % " PRIu64 " sectors on PV %s. " ,
pv_pe_start ( pv ) , pv_dev_name ( pv ) ) ;
2018-06-05 22:21:26 +03:00
return 0 ;
2011-02-21 15:27:26 +03:00
}
2005-10-31 05:37:29 +03:00
2011-02-21 15:27:26 +03:00
old_pe_count = pv - > pe_count ;
if ( ! pv - > fmt - > ops - > pv_resize ( pv - > fmt , pv , vg , size ) ) {
log_error ( " Format specific resize of PV %s failed. " ,
pv_dev_name ( pv ) ) ;
return 0 ;
}
/* pv->pe_count is 0 now! We need to recalculate! */
/* If there's a VG, calculate new PE count value. */
2013-06-29 01:25:28 +04:00
/* Don't do for orphan VG */
if ( vg & & ! is_orphan_vg ( vg - > name ) ) {
2011-02-21 15:27:26 +03:00
/* FIXME: Maybe PE calculation should go into pv->fmt->resize?
( like it is for pv - > fmt - > setup ) */
if ( ! ( new_pe_count = pv_size ( pv ) / vg - > extent_size ) ) {
log_error ( " Size must leave space for at least one physical "
" extent of % " PRIu32 " sectors on PV %s. " ,
pv_pe_size ( pv ) , pv_dev_name ( pv ) ) ;
return 0 ;
}
2011-03-30 00:19:03 +04:00
if ( new_pe_count = = old_pe_count ) {
2011-02-21 15:27:26 +03:00
pv - > pe_count = old_pe_count ;
log_verbose ( " No change to size of physical volume %s. " ,
pv_dev_name ( pv ) ) ;
return 1 ;
}
log_verbose ( " Resizing physical volume %s from % " PRIu32
" to % " PRIu32 " extents. " ,
2017-04-27 04:36:34 +03:00
pv_dev_name ( pv ) , old_pe_count , new_pe_count ) ;
2011-02-21 15:27:26 +03:00
if ( new_pe_count > old_pe_count )
return _extend_pv ( pv , vg , old_pe_count , new_pe_count ) ;
2017-07-19 17:16:12 +03:00
return _reduce_pv ( pv , vg , old_pe_count , new_pe_count ) ;
2011-02-21 15:27:26 +03:00
}
return 1 ;
2005-10-31 05:37:29 +03:00
}
2013-04-03 00:10:18 +04:00
2013-06-29 01:25:28 +04:00
int pv_resize_single ( struct cmd_context * cmd ,
2013-02-19 04:54:34 +04:00
struct volume_group * vg ,
struct physical_volume * pv ,
2017-04-27 04:36:34 +03:00
const uint64_t new_size ,
int yes )
2013-06-29 01:25:28 +04:00
{
uint64_t size = 0 ;
int r = 0 ;
const char * pv_name = pv_dev_name ( pv ) ;
2014-03-19 02:54:46 +04:00
const char * vg_name = pv - > vg_name ;
2013-06-29 01:25:28 +04:00
int vg_needs_pv_write = 0 ;
if ( ! ( pv - > fmt - > features & FMT_RESIZE_PV ) ) {
log_error ( " Physical volume %s format does not support resizing. " ,
pv_name ) ;
goto out ;
}
/* Get new size */
if ( ! dev_get_size ( pv_dev ( pv ) , & size ) ) {
log_error ( " %s: Couldn't get size. " , pv_name ) ;
goto out ;
}
if ( new_size ) {
2017-04-27 04:36:34 +03:00
if ( new_size > size ) {
log_warn ( " WARNING: %s: Overriding real size %s. You could lose data. " ,
pv_name , display_size ( cmd , ( uint64_t ) size ) ) ;
if ( ! yes & & yes_no_prompt ( " %s: Requested size %s exceeds real size %s. Proceed? [y/n]: " ,
2017-07-20 10:57:09 +03:00
pv_name , display_size ( cmd , new_size ) ,
display_size ( cmd , size ) ) = = ' n ' ) {
2017-04-27 16:59:40 +03:00
log_error ( " Physical Volume %s not resized. " , pv_name ) ;
2018-06-05 22:21:26 +03:00
goto out ;
2017-04-27 16:59:40 +03:00
}
2017-04-27 04:36:34 +03:00
} else if ( new_size < size )
if ( ! yes & & yes_no_prompt ( " %s: Requested size %s is less than real size %s. Proceed? [y/n]: " ,
2017-07-20 10:57:09 +03:00
pv_name , display_size ( cmd , new_size ) ,
display_size ( cmd , size ) ) = = ' n ' ) {
2017-04-27 16:59:40 +03:00
log_error ( " Physical Volume %s not resized. " , pv_name ) ;
2018-06-05 22:21:26 +03:00
goto out ;
2017-04-27 16:59:40 +03:00
}
2017-04-27 04:36:34 +03:00
if ( new_size = = size )
log_verbose ( " %s: Size is already %s (% " PRIu64 " sectors). " ,
2017-07-20 10:57:09 +03:00
pv_name , display_size ( cmd , new_size ) , new_size ) ;
2017-04-27 04:36:34 +03:00
else
log_warn ( " WARNING: %s: Pretending size is % " PRIu64 " not % " PRIu64 " sectors. " ,
pv_name , new_size , size ) ;
2013-06-29 01:25:28 +04:00
size = new_size ;
}
log_verbose ( " Resizing volume \" %s \" to % " PRIu64 " sectors. " ,
2014-11-25 17:16:09 +03:00
pv_name , size ) ;
2013-06-29 01:25:28 +04:00
2017-10-18 17:57:46 +03:00
if ( ! _pv_resize ( pv , vg , size ) )
2013-06-29 01:25:28 +04:00
goto_out ;
log_verbose ( " Updating physical volume \" %s \" " , pv_name ) ;
/* Write PV label only if this an orphan PV or it has 2nd mda. */
if ( ( is_orphan_vg ( vg_name ) | |
( vg_needs_pv_write = ( fid_get_mda_indexed ( vg - > fid ,
( const char * ) & pv - > id , ID_LEN , 1 ) ! = NULL ) ) ) & &
! pv_write ( cmd , pv , 1 ) ) {
log_error ( " Failed to store physical volume \" %s \" " ,
pv_name ) ;
goto out ;
}
if ( ! is_orphan_vg ( vg_name ) ) {
if ( ! vg_write ( vg ) | | ! vg_commit ( vg ) ) {
log_error ( " Failed to store physical volume \" %s \" in "
" volume group \" %s \" " , pv_name , vg_name ) ;
goto out ;
}
}
log_print_unless_silent ( " Physical volume \" %s \" changed " , pv_name ) ;
r = 1 ;
out :
if ( ! r & & vg_needs_pv_write )
log_error ( " Use pvcreate and vgcfgrestore "
" to repair from archived metadata. " ) ;
return r ;
}