2002-01-07 18:08:28 +03:00
/*
* Copyright ( C ) 2001 Sistina Software
*
* This file is released under the LGPL .
*/
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"
2002-01-07 18:08:28 +03:00
/*
2003-09-01 23:55:16 +04:00
* Test whether two segments could be merged by the current merging code
2002-01-07 18:08:28 +03:00
*/
2003-09-01 23:55:16 +04:00
static int _segments_compatible ( struct lv_segment * first ,
struct lv_segment * second )
2002-01-07 18:08:28 +03:00
{
uint32_t width ;
2003-09-01 23:55:16 +04:00
unsigned s ;
2002-01-07 18:08:28 +03:00
2003-09-01 23:55:16 +04:00
/* FIXME Relax the seg type restriction */
if ( ! first | | ! second | |
( first - > type ! = SEG_STRIPED ) | | ( second - > type ! = first - > type ) | |
2003-04-25 02:23:24 +04:00
( first - > area_count ! = second - > area_count ) | |
2002-01-07 18:08:28 +03:00
( first - > stripe_size ! = second - > stripe_size ) )
return 0 ;
2003-04-25 02:23:24 +04:00
for ( s = 0 ; s < first - > area_count ; s + + ) {
2002-01-07 18:08:28 +03:00
2003-09-01 23:55:16 +04:00
/* FIXME Relax this to first area type != second area type */
/* plus the additional AREA_LV checks needed */
if ( ( first - > area [ s ] . type ! = AREA_PV ) | |
( second - > area [ s ] . type ! = AREA_PV ) )
2003-04-25 02:23:24 +04:00
return 0 ;
2003-09-01 23:55:16 +04:00
width = first - > area_len ;
2003-04-25 02:23:24 +04:00
if ( ( first - > area [ s ] . u . pv . pv ! = second - > area [ s ] . u . pv . pv ) | |
( first - > area [ s ] . u . pv . pe + width ! = second - > area [ s ] . u . pv . pe ) )
2002-01-07 18:08:28 +03:00
return 0 ;
}
2003-09-01 23:55:16 +04:00
return 1 ;
}
/*
* Attempt to merge two adjacent segments .
* Currently only supports SEG_STRIPED on AREA_PV .
* 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 )
{
if ( ! _segments_compatible ( first , second ) )
return 0 ;
2002-01-07 18:08:28 +03:00
first - > len + = second - > len ;
2003-04-25 02:46:47 +04:00
first - > area_len + = second - > area_len ;
2002-01-07 18:08:28 +03:00
return 1 ;
}
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
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 .
*/
2002-01-11 02:21:07 +03:00
int lv_check_segments ( struct logical_volume * lv )
{
2003-09-01 23:55:16 +04:00
struct lv_segment * seg ;
uint32_t le = 0 ;
unsigned seg_count = 0 ;
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 ) ;
return 0 ;
}
le + = seg - > len ;
}
2002-01-11 02:21:07 +03:00
return 1 ;
}
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
*/
static int _lv_split_segment ( struct logical_volume * lv , struct lv_segment * seg ,
uint32_t le )
{
size_t len ;
struct lv_segment * split_seg ;
uint32_t s ;
uint32_t offset = le - seg - > le ;
if ( seg - > type = = SEG_SNAPSHOT ) {
log_error ( " Unable to split the snapshot segment at LE % " PRIu32
" in LV %s " , le , lv - > name ) ;
return 0 ;
}
/* Clone the existing segment */
if ( ! ( split_seg = alloc_lv_segment ( lv - > vg - > cmd - > mem ,
seg - > area_count ) ) ) {
log_error ( " Couldn't allocate new LV segment. " ) ;
return 0 ;
}
len = sizeof ( * seg ) + ( seg - > area_count * sizeof ( seg - > area [ 0 ] ) ) ;
memcpy ( split_seg , seg , len ) ;
/* In case of a striped segment, the offset has to be / stripes */
if ( seg - > type = = SEG_STRIPED )
offset / = seg - > area_count ;
/* Adjust the PV mapping */
for ( s = 0 ; s < seg - > area_count ; s + + ) {
/* Split area at the offset */
switch ( seg - > area [ s ] . type ) {
case AREA_LV :
split_seg - > area [ s ] . u . lv . le =
seg - > area [ s ] . u . lv . le + offset ;
break ;
case AREA_PV :
split_seg - > area [ s ] . u . pv . pe =
seg - > area [ s ] . u . pv . pe + offset ;
break ;
default :
log_error ( " Unrecognised segment type %u " ,
seg - > area [ s ] . type ) ;
return 0 ;
}
}
split_seg - > area_len = seg - > area_len - offset ;
seg - > area_len = offset ;
/* 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 ;
}
return 1 ;
}