2011-09-06 23:25:42 +04:00
/*
* Copyright ( C ) 2011 Red Hat , Inc . All rights reserved .
*
* 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"
2011-09-08 20:41:18 +04:00
# include "segtype.h"
# include "lv_alloc.h"
2011-09-06 23:25:42 +04:00
2011-09-07 02:43:56 +04:00
int attach_pool_metadata_lv ( struct lv_segment * seg , struct logical_volume * pool_metadata_lv )
2011-09-06 23:25:42 +04:00
{
2011-09-07 02:43:56 +04:00
seg - > pool_metadata_lv = pool_metadata_lv ;
pool_metadata_lv - > status | = THIN_POOL_METADATA ;
2011-10-19 20:36:39 +04:00
lv_set_hidden ( pool_metadata_lv ) ;
2011-09-06 23:25:42 +04:00
2011-10-19 20:36:39 +04:00
return add_seg_to_segs_using_this_lv ( pool_metadata_lv , seg ) ;
2011-09-06 23:25:42 +04:00
}
2011-09-07 02:43:56 +04:00
int attach_pool_data_lv ( struct lv_segment * seg , struct logical_volume * pool_data_lv )
2011-09-06 23:25:42 +04:00
{
2011-09-08 20:41:18 +04:00
if ( ! set_lv_segment_area_lv ( seg , 0 , pool_data_lv , 0 , THIN_POOL_DATA ) )
return_0 ;
2011-10-19 20:36:39 +04:00
lv_set_hidden ( pool_data_lv ) ;
2011-09-06 23:25:42 +04:00
2011-10-19 20:36:39 +04:00
return 1 ;
2011-09-06 23:25:42 +04:00
}
2011-09-07 02:43:56 +04:00
int attach_pool_lv ( struct lv_segment * seg , struct logical_volume * pool_lv )
{
seg - > pool_lv = pool_lv ;
2011-09-08 20:41:18 +04:00
seg - > lv - > status | = THIN_VOLUME ;
2011-09-07 02:43:56 +04:00
2011-10-19 20:36:39 +04:00
return add_seg_to_segs_using_this_lv ( pool_lv , seg ) ;
2011-09-07 02:43:56 +04:00
}
2011-09-08 20:41:18 +04:00
int detach_pool_lv ( struct lv_segment * seg )
{
2011-10-19 20:37:30 +04:00
struct lv_thin_message * tmsg ;
struct dm_list * l , * lt ;
2011-09-08 20:41:18 +04:00
if ( ! lv_is_thin_pool ( seg - > pool_lv ) ) {
log_error ( INTERNAL_ERROR " LV %s is not a thin pool " ,
seg - > pool_lv - > name ) ;
return 0 ;
}
2011-10-19 20:37:30 +04:00
/* Drop any message referencing removed segment */
dm_list_iterate_safe ( l , lt , & first_seg ( seg - > pool_lv ) - > thin_messages ) {
tmsg = dm_list_item ( l , struct lv_thin_message ) ;
switch ( tmsg - > type ) {
case DM_THIN_MESSAGE_CREATE_SNAP :
case DM_THIN_MESSAGE_CREATE_THIN :
case DM_THIN_MESSAGE_TRIM :
if ( first_seg ( tmsg - > u . lv ) = = seg ) {
log_debug ( " Discarding message for LV %s. " ,
tmsg - > u . lv - > name ) ;
dm_list_del ( & tmsg - > list ) ;
}
default :
break ;
}
}
2011-10-17 18:17:09 +04:00
if ( ! attach_pool_message ( first_seg ( seg - > pool_lv ) ,
DM_THIN_MESSAGE_DELETE ,
NULL , seg - > device_id , 0 ) )
return_0 ;
2011-09-08 20:41:18 +04:00
return remove_seg_from_segs_using_this_lv ( seg - > pool_lv , seg ) ;
}
2011-10-17 18:17:09 +04:00
int attach_pool_message ( struct lv_segment * seg , dm_thin_message_t type ,
2011-10-19 20:39:09 +04:00
struct logical_volume * lv , uint32_t delete_id ,
2011-10-17 18:17:09 +04:00
int read_only )
{
struct lv_thin_message * tmsg ;
2011-10-19 20:39:09 +04:00
dm_list_iterate_items ( tmsg , & seg - > thin_messages ) {
if ( tmsg - > type = = type ) {
switch ( tmsg - > type ) {
case DM_THIN_MESSAGE_CREATE_SNAP :
case DM_THIN_MESSAGE_CREATE_THIN :
case DM_THIN_MESSAGE_TRIM :
if ( tmsg - > u . lv = = lv ) {
log_error ( " Message referring LV %s already queued for %s. " ,
tmsg - > u . lv - > name , seg - > lv - > name ) ;
return 0 ;
}
break ;
case DM_THIN_MESSAGE_DELETE :
if ( tmsg - > u . delete_id = = delete_id ) {
log_error ( " Delete of device %u already queued for %s. " ,
tmsg - > u . delete_id , seg - > lv - > name ) ;
return 0 ;
}
break ;
default :
break ;
}
}
}
2011-10-17 18:17:09 +04:00
if ( ! ( tmsg = dm_pool_alloc ( seg - > lv - > vg - > vgmem , sizeof ( * tmsg ) ) ) ) {
log_error ( " Failed to allocate memory for message. " ) ;
return 0 ;
}
switch ( type ) {
case DM_THIN_MESSAGE_CREATE_SNAP :
case DM_THIN_MESSAGE_CREATE_THIN :
case DM_THIN_MESSAGE_TRIM :
tmsg - > u . lv = lv ;
break ;
case DM_THIN_MESSAGE_DELETE :
2011-10-19 20:39:09 +04:00
tmsg - > u . delete_id = delete_id ;
2011-10-17 18:17:09 +04:00
break ;
default :
log_error ( INTERNAL_ERROR " Unsupported message type %d " , type ) ;
return 0 ;
}
tmsg - > type = type ;
/* If the 1st message is add in non-read-only mode, modify transaction_id */
if ( ! read_only & & dm_list_empty ( & seg - > thin_messages ) )
seg - > transaction_id + + ;
dm_list_add ( & seg - > thin_messages , & tmsg - > list ) ;
log_debug ( " Added %s message " ,
( type = = DM_THIN_MESSAGE_CREATE_SNAP | |
type = = DM_THIN_MESSAGE_CREATE_THIN ) ? " create " :
( type = = DM_THIN_MESSAGE_TRIM ) ? " trim " :
( type = = DM_THIN_MESSAGE_DELETE ) ? " delete " : " unknown " ) ;
return 1 ;
}
int detach_pool_messages ( struct lv_segment * seg )
{
if ( ! lv_is_thin_pool ( seg - > lv ) ) {
log_error ( INTERNAL_ERROR " LV %s is not a thin pool. " ,
seg - > lv - > name ) ;
return 0 ;
}
dm_list_init ( & seg - > thin_messages ) ;
return 1 ;
}
2011-09-09 05:15:18 +04:00
struct lv_segment * find_pool_seg ( const struct lv_segment * seg )
2011-09-08 20:41:18 +04:00
{
2011-10-19 20:36:39 +04:00
struct lv_segment * pool_seg ;
2011-09-08 20:41:18 +04:00
2011-10-19 20:36:39 +04:00
pool_seg = get_only_segment_using_this_lv ( seg - > lv ) ;
2011-09-08 20:41:18 +04:00
2011-10-19 20:36:39 +04:00
if ( ! pool_seg ) {
log_error ( " Failed to find pool_seg for %s " , seg - > lv - > name ) ;
return NULL ;
}
2011-09-08 20:41:18 +04:00
2011-10-19 20:36:39 +04:00
if ( ! seg_is_thin_pool ( pool_seg ) ) {
log_error ( " %s on %s is not a pool segment " ,
pool_seg - > lv - > name , seg - > lv - > name ) ;
return NULL ;
}
2011-09-08 20:41:18 +04:00
2011-10-19 20:36:39 +04:00
return pool_seg ;
2011-09-08 20:41:18 +04:00
}
2011-10-03 22:39:17 +04:00
/*
* Find a free device_id for given thin_pool segment .
*
* \ return
* Free device id , or 0 if free device_id is not found .
*
* FIXME : Improve naive search and keep the value cached
* and updated during VG lifetime ( so no const for lv_segment )
*/
uint32_t get_free_pool_device_id ( struct lv_segment * thin_pool_seg )
{
uint32_t dev_id , max_id = 0 ;
struct dm_list * h ;
if ( ! seg_is_thin_pool ( thin_pool_seg ) ) {
log_error ( " Segment in %s is not a thin pool segment. " ,
2011-10-03 23:10:52 +04:00
thin_pool_seg - > lv - > name ) ;
2011-10-03 22:39:17 +04:00
return 0 ;
}
dm_list_iterate ( h , & thin_pool_seg - > lv - > segs_using_this_lv ) {
dev_id = dm_list_item ( h , struct seg_list ) - > seg - > device_id ;
if ( dev_id > max_id )
max_id = dev_id ;
}
if ( + + max_id > = ( 1 < < 24 ) ) {
// FIXME: try to find empty holes....
log_error ( " Free device_id exhausted... " ) ;
return 0 ;
}
log_debug ( " Found free pool device_id %u. " , max_id ) ;
return max_id ;
}