2002-01-07 18:08:28 +03:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2001 - 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-01-07 18:08:28 +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-01-07 18:08:28 +03:00
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2002-01-10 21:12:26 +03:00
# include "metadata.h"
2003-09-15 22:22:50 +04:00
# include "toolcontext.h"
# include "lv_alloc.h"
2005-05-03 21:28:23 +04:00
# include "pv_alloc.h"
2004-03-08 20:19:15 +03:00
# include "str_list.h"
2004-09-16 22:40:56 +04:00
# include "segtype.h"
2003-09-01 23:55:16 +04:00
/*
* Attempt to merge two adjacent segments .
2004-05-05 01:25:57 +04:00
* Currently only supports striped segments on AREA_PV .
2003-09-01 23:55:16 +04:00
* Returns success if successful , in which case ' first '
* gets adjusted to contain both areas .
*/
static int _merge ( struct lv_segment * first , struct lv_segment * second )
{
2004-05-05 01:25:57 +04:00
if ( ! first | | ! second | | first - > segtype ! = second - > segtype | |
! first - > segtype - > ops - > merge_segments ) return 0 ;
2003-09-01 23:55:16 +04:00
2004-05-05 01:25:57 +04:00
return first - > segtype - > ops - > merge_segments ( first , second ) ;
2002-01-07 18:08:28 +03:00
}
2002-01-10 14:18:08 +03:00
int lv_merge_segments ( struct logical_volume * lv )
2002-01-07 18:08:28 +03:00
{
2003-08-27 19:30:39 +04:00
struct list * segh , * t ;
2002-11-18 17:04:08 +03:00
struct lv_segment * current , * prev = NULL ;
2002-01-07 18:08:28 +03:00
2004-08-18 02:09:02 +04:00
if ( lv - > status & LOCKED | | lv - > status & PVMOVE )
return 1 ;
2003-08-27 19:30:39 +04:00
list_iterate_safe ( segh , t , & lv - > segments ) {
2002-11-18 17:04:08 +03:00
current = list_item ( segh , struct lv_segment ) ;
2002-01-07 18:08:28 +03:00
if ( _merge ( prev , current ) )
list_del ( & current - > list ) ;
else
prev = current ;
}
return 1 ;
}
2003-09-01 23:55:16 +04:00
/*
* Verify that an LV ' s segments are consecutive , complete and don ' t overlap .
*/
2005-10-28 01:51:28 +04:00
int check_lv_segments ( struct logical_volume * lv , int complete_vg )
2002-01-11 02:21:07 +03:00
{
2005-10-28 01:51:28 +04:00
struct lv_segment * seg , * seg2 ;
2003-09-01 23:55:16 +04:00
uint32_t le = 0 ;
unsigned seg_count = 0 ;
2005-06-01 20:51:55 +04:00
int r = 1 ;
uint32_t area_multiplier , s ;
2003-09-01 23:55:16 +04:00
list_iterate_items ( seg , & lv - > segments ) {
seg_count + + ;
if ( seg - > le ! = le ) {
log_error ( " LV %s invalid: segment %u should begin at "
" LE % " PRIu32 " (found % " PRIu32 " ). " ,
lv - > name , seg_count , le , seg - > le ) ;
2005-06-01 20:51:55 +04:00
r = 0 ;
}
area_multiplier = segtype_is_striped ( seg - > segtype ) ?
seg - > area_count : 1 ;
if ( seg - > area_len * area_multiplier ! = seg - > len ) {
log_error ( " LV %s: segment %u has inconsistent "
" area_len %u " ,
lv - > name , seg_count , seg - > area_len ) ;
r = 0 ;
}
2005-10-28 01:51:28 +04:00
if ( complete_vg & & seg - > log_lv ) {
2005-10-27 23:58:22 +04:00
if ( ! seg_is_mirrored ( seg ) ) {
log_error ( " LV %s: segment %u has log LV but "
" is not mirrored " ,
lv - > name , seg_count ) ;
r = 0 ;
}
if ( ! ( seg - > log_lv - > status & MIRROR_LOG ) ) {
log_error ( " LV %s: segment %u log LV %s is not "
" a mirror log " ,
lv - > name , seg_count , seg - > log_lv - > name ) ;
r = 0 ;
}
2005-10-28 16:48:50 +04:00
if ( ! ( seg2 = first_seg ( seg - > log_lv ) ) | |
2005-10-28 01:51:28 +04:00
seg2 - > mirror_seg ! = seg ) {
2005-10-27 23:58:22 +04:00
log_error ( " LV %s: segment %u log LV does not "
" point back to mirror segment " ,
lv - > name , seg_count ) ;
r = 0 ;
}
}
2005-10-28 01:51:28 +04:00
if ( complete_vg & & seg - > status & MIRROR_IMAGE ) {
2005-10-27 23:58:22 +04:00
if ( ! seg - > mirror_seg | |
! seg_is_mirrored ( seg - > mirror_seg ) ) {
log_error ( " LV %s: segment %u mirror image "
" is not mirrored " ,
lv - > name , seg_count ) ;
r = 0 ;
}
}
2007-11-07 19:33:12 +03:00
if ( seg_is_snapshot ( seg ) ) {
if ( seg - > cow & & seg - > cow = = seg - > origin ) {
log_error ( " LV %s: segment %u has same LV %s for "
" both origin and snapshot " ,
lv - > name , seg_count , seg - > cow - > name ) ;
r = 0 ;
}
}
2005-06-01 20:51:55 +04:00
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-14 21:54:48 +04:00
if ( seg_type ( seg , s ) = = AREA_UNASSIGNED ) {
log_error ( " LV %s: segment %u has unassigned "
" area %u. " ,
lv - > name , seg_count , s ) ;
r = 0 ;
} else if ( seg_type ( seg , s ) = = AREA_PV ) {
2005-06-01 20:51:55 +04:00
if ( ! seg_pvseg ( seg , s ) | |
seg_pvseg ( seg , s ) - > lvseg ! = seg | |
seg_pvseg ( seg , s ) - > lv_area ! = s ) {
log_error ( " LV %s: segment %u has "
" inconsistent PV area %u " ,
lv - > name , seg_count , s ) ;
r = 0 ;
}
} else {
if ( ! seg_lv ( seg , s ) | |
seg_lv ( seg , s ) - > vg ! = lv - > vg | |
seg_lv ( seg , s ) = = lv ) {
log_error ( " LV %s: segment %u has "
" inconsistent LV area %u " ,
lv - > name , seg_count , s ) ;
r = 0 ;
}
2005-10-27 23:58:22 +04:00
2005-10-28 01:51:28 +04:00
if ( complete_vg & & seg_lv ( seg , s ) & &
2005-10-27 23:58:22 +04:00
( seg_lv ( seg , s ) - > status & MIRROR_IMAGE ) & &
2007-07-10 22:50:03 +04:00
( ! ( seg2 = find_seg_by_le ( seg_lv ( seg , s ) ,
seg_le ( seg , s ) ) ) | |
seg2 - > mirror_seg ! = seg ) ) {
2005-10-27 23:58:22 +04:00
log_error ( " LV %s: segment %u mirror "
" image %u missing mirror ptr " ,
lv - > name , seg_count , s ) ;
r = 0 ;
}
2005-06-14 21:54:48 +04:00
/* FIXME I don't think this ever holds?
2005-06-01 20:51:55 +04:00
if ( seg_le ( seg , s ) ! = le ) {
log_error ( " LV %s: segment %u has "
" inconsistent LV area %u "
" size " ,
lv - > name , seg_count , s ) ;
r = 0 ;
}
2005-06-14 21:54:48 +04:00
*/
2005-06-01 20:51:55 +04:00
}
2003-09-01 23:55:16 +04:00
}
le + = seg - > len ;
}
2005-06-01 20:51:55 +04:00
if ( le ! = lv - > le_count ) {
log_error ( " LV %s: inconsistent LE count %u != %u " ,
lv - > name , le , lv - > le_count ) ;
r = 0 ;
}
return r ;
2002-01-11 02:21:07 +03:00
}
2003-09-01 23:55:16 +04:00
2003-09-15 22:22:50 +04:00
/*
* Split the supplied segment at the supplied logical extent
2004-08-18 02:09:02 +04:00
* NB Use LE numbering that works across stripes PV1 : 0 , 2 , 4 PV2 : 1 , 3 , 5 etc .
2003-09-15 22:22:50 +04:00
*/
static int _lv_split_segment ( struct logical_volume * lv , struct lv_segment * seg ,
uint32_t le )
{
struct lv_segment * split_seg ;
uint32_t s ;
uint32_t offset = le - seg - > le ;
2004-08-18 02:09:02 +04:00
uint32_t area_offset ;
2003-09-15 22:22:50 +04:00
2005-05-09 20:59:01 +04:00
if ( ! seg_can_split ( seg ) ) {
2004-05-05 01:25:57 +04:00
log_error ( " Unable to split the %s segment at LE % " PRIu32
" in LV %s " , seg - > segtype - > name , le , lv - > name ) ;
2003-09-15 22:22:50 +04:00
return 0 ;
}
/* Clone the existing segment */
2005-04-22 19:44:00 +04:00
if ( ! ( split_seg = alloc_lv_segment ( lv - > vg - > cmd - > mem , seg - > segtype ,
seg - > lv , seg - > le , seg - > len ,
seg - > status , seg - > stripe_size ,
2005-06-01 20:51:55 +04:00
seg - > log_lv ,
2005-04-22 19:44:00 +04:00
seg - > area_count , seg - > area_len ,
2005-06-01 20:51:55 +04:00
seg - > chunk_size , seg - > region_size ,
2005-04-22 19:44:00 +04:00
seg - > extents_copied ) ) ) {
log_error ( " Couldn't allocate cloned LV segment. " ) ;
2003-09-15 22:22:50 +04:00
return 0 ;
}
2004-03-08 20:19:15 +03:00
if ( ! str_list_dup ( lv - > vg - > cmd - > mem , & split_seg - > tags , & seg - > tags ) ) {
log_error ( " LV segment tags duplication failed " ) ;
return 0 ;
}
2003-09-15 22:22:50 +04:00
/* In case of a striped segment, the offset has to be / stripes */
2004-08-18 02:09:02 +04:00
area_offset = offset ;
2005-05-09 20:59:01 +04:00
if ( seg_is_striped ( seg ) )
2004-08-18 02:09:02 +04:00
area_offset / = seg - > area_count ;
2003-09-15 22:22:50 +04:00
2005-05-03 21:28:23 +04:00
split_seg - > area_len - = area_offset ;
seg - > area_len = area_offset ;
split_seg - > len - = offset ;
seg - > len = offset ;
split_seg - > le = seg - > le + seg - > len ;
2003-09-15 22:22:50 +04:00
/* Adjust the PV mapping */
for ( s = 0 ; s < seg - > area_count ; s + + ) {
2005-06-01 20:51:55 +04:00
seg_type ( split_seg , s ) = seg_type ( seg , s ) ;
2005-05-03 21:28:23 +04:00
2003-09-15 22:22:50 +04:00
/* Split area at the offset */
2005-06-01 20:51:55 +04:00
switch ( seg_type ( seg , s ) ) {
2003-09-15 22:22:50 +04:00
case AREA_LV :
2005-06-01 20:51:55 +04:00
seg_lv ( split_seg , s ) = seg_lv ( seg , s ) ;
seg_le ( split_seg , s ) =
seg_le ( seg , s ) + seg - > area_len ;
2004-08-18 02:09:02 +04:00
log_debug ( " Split %s:%u[%u] at %u: %s LE %u " , lv - > name ,
2005-06-01 20:51:55 +04:00
seg - > le , s , le , seg_lv ( seg , s ) - > name ,
seg_le ( split_seg , s ) ) ;
2003-09-15 22:22:50 +04:00
break ;
case AREA_PV :
2005-07-12 23:40:59 +04:00
if ( ! ( seg_pvseg ( split_seg , s ) =
assign_peg_to_lvseg ( seg_pv ( seg , s ) ,
2005-06-01 20:51:55 +04:00
seg_pe ( seg , s ) +
2005-05-03 21:28:23 +04:00
seg - > area_len ,
2005-06-01 20:51:55 +04:00
seg_pvseg ( seg , s ) - > len -
2005-05-03 21:28:23 +04:00
seg - > area_len ,
2005-07-12 23:40:59 +04:00
split_seg , s ) ) ) {
2005-05-03 21:28:23 +04:00
stack ;
return 0 ;
}
2004-08-18 02:09:02 +04:00
log_debug ( " Split %s:%u[%u] at %u: %s PE %u " , lv - > name ,
seg - > le , s , le ,
2005-06-01 20:51:55 +04:00
dev_name ( seg_dev ( seg , s ) ) ,
seg_pe ( split_seg , s ) ) ;
2003-09-15 22:22:50 +04:00
break ;
2005-06-14 21:54:48 +04:00
case AREA_UNASSIGNED :
log_error ( " Unassigned area %u found in segment " , s ) ;
2003-09-15 22:22:50 +04:00
return 0 ;
}
}
/* Add split off segment to the list _after_ the original one */
list_add_h ( & seg - > list , & split_seg - > list ) ;
return 1 ;
}
/*
* Ensure there ' s a segment boundary at the given logical extent
*/
int lv_split_segment ( struct logical_volume * lv , uint32_t le )
{
struct lv_segment * seg ;
if ( ! ( seg = find_seg_by_le ( lv , le ) ) ) {
log_error ( " Segment with extent % " PRIu32 " in LV %s not found " ,
le , lv - > name ) ;
return 0 ;
}
/* This is a segment start already */
if ( le = = seg - > le )
return 1 ;
if ( ! _lv_split_segment ( lv , seg , le ) ) {
stack ;
return 0 ;
}
2005-07-12 23:40:59 +04:00
if ( ! vg_validate ( lv - > vg ) ) {
stack ;
return 0 ;
}
2003-09-15 22:22:50 +04:00
return 1 ;
}