2002-02-12 19:31:31 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2002-02-12 19:31:31 +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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2002-02-12 19:31:31 +03:00
*/
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
# include "lib/metadata/metadata.h"
# include "lib/metadata/segtype.h"
# include "lib/locking/locking.h"
# include "lib/commands/toolcontext.h"
# include "lib/metadata/lv_alloc.h"
# include "lib/activate/activate.h"
2002-02-12 19:31:31 +03:00
2014-03-17 16:04:14 +04:00
# define SNAPSHOT_MIN_CHUNKS 3 /* Minimum number of chunks in snapshot */
2002-12-20 02:25:55 +03:00
int lv_is_origin ( const struct logical_volume * lv )
2002-02-12 19:31:31 +03:00
{
2005-04-07 16:39:44 +04:00
return lv - > origin_count ? 1 : 0 ;
2002-02-12 19:31:31 +03:00
}
2002-12-20 02:25:55 +03:00
int lv_is_cow ( const struct logical_volume * lv )
2002-02-12 19:31:31 +03:00
{
2013-11-29 18:51:28 +04:00
/* Make sure a merging thin origin isn't confused as a cow LV */
2021-03-04 19:05:10 +03:00
return ( lv - > snapshot & & ! lv_is_thin_volume ( lv ) & & ! lv_is_origin ( lv ) ) ? 1 : 0 ;
2002-04-24 22:20:51 +04:00
}
2016-04-21 02:30:17 +03:00
struct logical_volume * find_cow ( const struct logical_volume * snap )
{
return first_seg ( snap ) - > cow ;
}
2014-02-25 22:43:07 +04:00
/*
* Some kernels have a bug that they may leak space in the snapshot on crash .
* If the kernel is buggy , we add some extra space .
*/
static uint64_t _cow_extra_chunks ( struct cmd_context * cmd , uint64_t n_chunks )
{
const struct segment_type * segtype ;
2014-05-07 13:44:33 +04:00
unsigned attrs = 0 ;
2014-02-25 22:43:07 +04:00
if ( activation ( ) & &
2015-09-22 21:04:12 +03:00
( segtype = get_segtype_from_string ( cmd , SEG_TYPE_NAME_SNAPSHOT ) ) & &
2014-02-25 22:43:07 +04:00
segtype - > ops - > target_present & &
segtype - > ops - > target_present ( cmd , NULL , & attrs ) & &
( attrs & SNAPSHOT_FEATURE_FIXED_LEAK ) )
2014-02-27 15:55:50 +04:00
return 0 ;
2014-02-25 22:43:07 +04:00
2014-02-27 15:55:50 +04:00
return ( n_chunks + 63 ) / 64 ;
2014-02-25 22:43:07 +04:00
}
static uint64_t _cow_max_size ( struct cmd_context * cmd , uint64_t origin_size , uint32_t chunk_size )
2013-05-29 14:44:54 +04:00
{
/* Snapshot disk layout:
* COW is divided into chunks
* 1 st . chunk is reserved for header
* 2 nd . chunk is the 1 st . metadata chunk
* 3 rd . chunk is the 1 st . data chunk
*/
2014-02-25 22:43:07 +04:00
uint64_t origin_chunks = ( origin_size + chunk_size - 1 ) / chunk_size ;
uint64_t chunks_per_metadata_area = ( uint64_t ) chunk_size < < ( SECTOR_SHIFT - 4 ) ;
2013-05-29 14:44:54 +04:00
2014-02-25 22:43:07 +04:00
/*
* Note : if origin_chunks is divisible by chunks_per_metadata_area , we
* need one extra metadata chunk as a terminator .
*/
uint64_t metadata_chunks = ( origin_chunks + chunks_per_metadata_area ) / chunks_per_metadata_area ;
uint64_t n_chunks = 1 + origin_chunks + metadata_chunks ;
2013-05-29 14:44:54 +04:00
2014-02-25 22:43:07 +04:00
return ( n_chunks + _cow_extra_chunks ( cmd , n_chunks ) ) * chunk_size ;
2013-05-29 14:44:54 +04:00
}
uint32_t cow_max_extents ( const struct logical_volume * origin , uint32_t chunk_size )
{
2014-02-25 22:43:07 +04:00
uint64_t size = _cow_max_size ( origin - > vg - > cmd , origin - > size , chunk_size ) ;
2013-05-29 14:44:54 +04:00
uint32_t extent_size = origin - > vg - > extent_size ;
2013-06-16 23:32:11 +04:00
uint64_t max_size = ( uint64_t ) MAX_EXTENT_COUNT * extent_size ;
2013-05-29 14:44:54 +04:00
if ( size % extent_size )
size + = extent_size - size % extent_size ;
2013-06-16 23:32:11 +04:00
if ( size > max_size )
size = max_size ; /* Origin is too big for 100% snapshot anyway */
2013-05-29 14:44:54 +04:00
return ( uint32_t ) ( size / extent_size ) ;
}
2014-03-17 16:04:14 +04:00
int cow_has_min_chunks ( const struct volume_group * vg , uint32_t cow_extents , uint32_t chunk_size )
{
if ( ( ( uint64_t ) vg - > extent_size * cow_extents ) > = ( SNAPSHOT_MIN_CHUNKS * chunk_size ) )
return 1 ;
log_error ( " Snapshot volume cannot be smaller than " DM_TO_STRING ( SNAPSHOT_MIN_CHUNKS )
" chunks (%u extents, %s). " , ( unsigned )
( ( ( uint64_t ) SNAPSHOT_MIN_CHUNKS * chunk_size +
vg - > extent_size - 1 ) / vg - > extent_size ) ,
display_size ( vg - > cmd , ( uint64_t ) SNAPSHOT_MIN_CHUNKS * chunk_size ) ) ;
return 0 ;
}
2013-05-27 12:18:20 +04:00
int lv_is_cow_covering_origin ( const struct logical_volume * lv )
{
2021-09-18 23:04:54 +03:00
const struct logical_volume * origin ;
return ( lv_is_cow ( lv ) & &
( origin = origin_from_cow ( lv ) ) & &
( lv - > size > = _cow_max_size ( lv - > vg - > cmd , origin - > size ,
find_snapshot ( lv ) - > chunk_size ) ) ) ;
2013-05-27 12:18:20 +04:00
}
2006-04-07 21:41:56 +04:00
int lv_is_visible ( const struct logical_volume * lv )
{
2021-09-18 23:04:54 +03:00
const struct logical_volume * origin ;
2016-03-01 17:22:36 +03:00
if ( lv_is_historical ( lv ) )
return 1 ;
2016-12-13 02:09:15 +03:00
if ( lv_is_snapshot ( lv ) )
2009-05-14 01:25:45 +04:00
return 0 ;
if ( lv_is_cow ( lv ) ) {
2021-09-18 23:04:54 +03:00
if ( ! ( origin = origin_from_cow ( lv ) ) )
return_0 ;
if ( lv_is_virtual_origin ( origin ) )
2009-05-14 01:25:45 +04:00
return 1 ;
2010-01-13 04:55:43 +03:00
if ( lv_is_merging_cow ( lv ) )
2010-01-13 04:35:49 +03:00
return 0 ;
2021-09-18 23:04:54 +03:00
return lv_is_visible ( origin ) ;
2009-05-14 01:25:45 +04:00
}
2006-04-07 21:41:56 +04:00
return lv - > status & VISIBLE_LV ? 1 : 0 ;
}
2016-01-07 16:30:21 +03:00
int lv_is_merging_cow ( const struct logical_volume * cow )
2009-04-25 05:17:59 +04:00
{
2016-05-27 14:49:30 +03:00
struct lv_segment * snap_seg ;
if ( ! lv_is_cow ( cow ) )
return 0 ;
snap_seg = find_snapshot ( cow ) ;
2014-09-16 00:33:53 +04:00
2016-01-07 16:30:21 +03:00
/* checks lv_segment's status to see if snapshot is merging */
2014-06-16 14:41:30 +04:00
return ( snap_seg & & ( snap_seg - > status & MERGING ) ) ? 1 : 0 ;
2010-01-13 04:55:43 +03:00
}
2009-04-25 05:17:59 +04:00
2013-07-03 00:26:03 +04:00
struct lv_segment * find_snapshot ( const struct logical_volume * lv )
2002-02-21 13:16:33 +03:00
{
2005-04-07 16:39:44 +04:00
return lv - > snapshot ;
2002-02-21 13:16:33 +03:00
}
2006-04-06 17:39:16 +04:00
/* Given a cow LV, return its origin */
struct logical_volume * origin_from_cow ( const struct logical_volume * lv )
{
2011-07-19 20:23:52 +04:00
if ( lv - > snapshot )
return lv - > snapshot - > origin ;
2021-09-18 23:04:54 +03:00
log_debug ( INTERNAL_ERROR " Cannot get origin from snapshot %s. " ,
display_lvname ( lv ) ) ;
2011-07-19 20:23:52 +04:00
return NULL ;
2006-04-06 17:39:16 +04:00
}
2009-05-14 01:21:58 +04:00
void init_snapshot_seg ( struct lv_segment * seg , struct logical_volume * origin ,
2010-01-13 04:35:49 +03:00
struct logical_volume * cow , uint32_t chunk_size , int merge )
2009-05-14 01:21:58 +04:00
{
seg - > chunk_size = chunk_size ;
seg - > origin = origin ;
seg - > cow = cow ;
2009-05-21 07:04:52 +04:00
lv_set_hidden ( cow ) ;
2009-05-14 01:26:45 +04:00
2009-05-14 01:21:58 +04:00
cow - > snapshot = seg ;
origin - > origin_count + + ;
/* FIXME Assumes an invisible origin belongs to a sparse device */
if ( ! lv_is_visible ( origin ) )
origin - > status | = VIRTUAL_ORIGIN ;
seg - > lv - > status | = ( SNAPSHOT | VIRTUAL ) ;
2010-01-13 04:35:49 +03:00
if ( merge )
init_snapshot_merge ( seg , origin ) ;
2009-05-14 01:21:58 +04:00
dm_list_add ( & origin - > snapshot_segs , & seg - > origin_list ) ;
}
2013-11-22 17:52:35 +04:00
void init_snapshot_merge ( struct lv_segment * snap_seg ,
struct logical_volume * origin )
2010-01-13 04:35:49 +03:00
{
2013-11-22 21:19:13 +04:00
snap_seg - > status | = MERGING ;
origin - > snapshot = snap_seg ;
origin - > status | = MERGING ;
2014-02-18 00:48:27 +04:00
if ( seg_is_thin_volume ( snap_seg ) ) {
2013-11-29 18:51:28 +04:00
snap_seg - > merge_lv = origin ;
2014-09-18 02:00:41 +04:00
/* Making thin LV invisible with regular log */
2013-11-29 18:51:28 +04:00
lv_set_hidden ( snap_seg - > lv ) ;
return ;
}
2010-01-13 04:35:49 +03:00
/*
2013-07-03 00:26:03 +04:00
* Even though lv_is_visible ( snap_seg - > lv ) returns 0 ,
* the snap_seg - > lv ( name : snapshotX ) is _not_ hidden ;
2010-01-13 04:35:49 +03:00
* this is part of the lvm2 snapshot fiction . Must
* clear VISIBLE_LV directly ( lv_set_visible can ' t )
2013-07-03 00:26:03 +04:00
* - snap_seg - > lv - > status is used to control whether ' lv '
2010-01-13 04:35:49 +03:00
* ( with user provided snapshot LV name ) is visible
* - this also enables vg_validate ( ) to succeed with
2013-07-03 00:26:03 +04:00
* merge metadata ( snap_seg - > lv is now " internal " )
2010-01-13 04:35:49 +03:00
*/
2013-07-03 00:26:03 +04:00
snap_seg - > lv - > status & = ~ VISIBLE_LV ;
2010-01-13 04:35:49 +03:00
}
2010-01-13 04:55:43 +03:00
void clear_snapshot_merge ( struct logical_volume * origin )
{
/* clear merge attributes */
2013-11-29 18:51:28 +04:00
if ( origin - > snapshot - > merge_lv )
/* Removed thin volume has to be visible */
lv_set_visible ( origin - > snapshot - > lv ) ;
origin - > snapshot - > merge_lv = NULL ;
2010-01-13 04:56:18 +03:00
origin - > snapshot - > status & = ~ MERGING ;
origin - > snapshot = NULL ;
origin - > status & = ~ MERGING ;
2010-01-13 04:55:43 +03:00
}
2016-01-07 14:16:18 +03:00
static struct lv_segment * _alloc_snapshot_seg ( struct logical_volume * lv )
{
struct lv_segment * seg ;
const struct segment_type * segtype ;
segtype = get_segtype_from_string ( lv - > vg - > cmd , SEG_TYPE_NAME_SNAPSHOT ) ;
if ( ! segtype ) {
log_error ( " Failed to find snapshot segtype " ) ;
return NULL ;
}
2017-02-24 02:50:00 +03:00
if ( ! ( seg = alloc_lv_segment ( segtype , lv , 0 , lv - > le_count , 0 , 0 , 0 ,
NULL , 0 , lv - > le_count , 0 , 0 , 0 , 0 , NULL ) ) ) {
2016-01-07 14:16:18 +03:00
log_error ( " Couldn't allocate new snapshot segment. " ) ;
return NULL ;
}
dm_list_add ( & lv - > segments , & seg - > list ) ;
return seg ;
}
2009-05-14 01:21:58 +04:00
int vg_add_snapshot ( struct logical_volume * origin ,
2005-04-07 16:39:44 +04:00
struct logical_volume * cow , union lvid * lvid ,
uint32_t extent_count , uint32_t chunk_size )
2002-05-08 20:57:46 +04:00
{
2005-04-07 16:39:44 +04:00
struct logical_volume * snap ;
struct lv_segment * seg ;
2002-02-12 19:31:31 +03:00
/*
* Is the cow device already being used ?
*/
2002-02-20 22:04:55 +03:00
if ( lv_is_cow ( cow ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " '%s' is already in use as a snapshot. " , cow - > name ) ;
2002-02-12 19:31:31 +03:00
return 0 ;
}
2007-11-07 19:33:12 +03:00
if ( cow = = origin ) {
log_error ( " Snapshot and origin LVs must differ. " ) ;
return 0 ;
}
2009-05-14 01:21:58 +04:00
if ( ! ( snap = lv_create_empty ( " snapshot%d " ,
2005-04-07 16:39:44 +04:00
lvid , LVM_READ | LVM_WRITE | VISIBLE_LV ,
2009-05-14 01:28:31 +04:00
ALLOC_INHERIT , origin - > vg ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2002-02-12 19:31:31 +03:00
2005-04-07 16:39:44 +04:00
snap - > le_count = extent_count ;
2002-02-12 19:31:31 +03:00
2016-01-07 14:16:18 +03:00
if ( ! ( seg = _alloc_snapshot_seg ( snap ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2002-02-12 19:31:31 +03:00
2010-01-13 04:35:49 +03:00
init_snapshot_seg ( seg , origin , cow , chunk_size , 0 ) ;
2002-02-12 19:31:31 +03:00
return 1 ;
}
2005-05-09 20:59:01 +04:00
int vg_remove_snapshot ( struct logical_volume * cow )
2002-02-12 19:31:31 +03:00
{
2021-09-18 23:04:54 +03:00
struct logical_volume * origin ;
int is_origin_active ;
if ( ! lv_is_cow ( cow ) )
return_0 ;
origin = origin_from_cow ( cow ) ;
is_origin_active = lv_is_active ( origin ) ;
2010-01-13 04:55:43 +03:00
2013-10-12 01:53:28 +04:00
if ( is_origin_active & &
lv_is_virtual_origin ( origin ) ) {
2019-08-26 14:28:17 +03:00
if ( ! sync_local_dev_names ( origin - > vg - > cmd ) ) {
log_error ( " Failed to sync local devices before deactivating origin LV %s. " ,
display_lvname ( origin ) ) ;
return 0 ;
}
2013-10-12 01:53:28 +04:00
if ( ! deactivate_lv ( origin - > vg - > cmd , origin ) ) {
log_error ( " Failed to deactivate logical volume \" %s \" " ,
origin - > name ) ;
return 0 ;
}
is_origin_active = 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_del ( & cow - > snapshot - > origin_list ) ;
2010-01-13 04:55:43 +03:00
origin - > origin_count - - ;
2010-04-23 06:57:39 +04:00
2013-11-28 14:39:38 +04:00
if ( lv_is_merging_origin ( origin ) & &
( find_snapshot ( origin ) = = find_snapshot ( cow ) ) ) {
2010-02-18 01:59:46 +03:00
clear_snapshot_merge ( origin ) ;
/*
2010-04-23 06:57:39 +04:00
* preload origin IFF " snapshot-merge " target is active
2014-09-21 03:16:06 +04:00
* - IMPORTANT : avoids preload if inactivate merge is pending
2010-02-18 01:59:46 +03:00
*/
}
2002-02-12 19:31:31 +03:00
2005-05-09 20:59:01 +04:00
if ( ! lv_remove ( cow - > snapshot - > lv ) ) {
2005-04-07 16:39:44 +04:00
log_error ( " Failed to remove internal snapshot LV %s " ,
cow - > snapshot - > lv - > name ) ;
return 0 ;
2002-02-12 19:31:31 +03:00
}
2005-04-07 16:39:44 +04:00
cow - > snapshot = NULL ;
2009-05-14 01:26:45 +04:00
lv_set_visible ( cow ) ;
2005-04-07 16:39:44 +04:00
2021-03-14 15:18:45 +03:00
/* When origin with all its snapshots is going to be remove
* don ' t bother with individual manipulation with COWs
* Note : removal proceeds only when origin is inactive */
if ( is_origin_active & & origin - > to_remove ) {
origin - > vg - > needs_write_and_commit = 1 ;
log_debug_metadata ( " Postponing write and commit for remove of snapshot %s. " ,
display_lvname ( cow ) ) ;
return 1 ;
}
2011-07-08 16:48:41 +04:00
if ( ! vg_write ( origin - > vg ) )
return_0 ;
2013-04-21 12:37:52 +04:00
/* Skip call suspend, if device is not active */
if ( is_origin_active & & ! suspend_lv ( origin - > vg - > cmd , origin ) ) {
2011-07-08 16:48:41 +04:00
log_error ( " Failed to refresh %s without snapshot. " ,
origin - > name ) ;
2014-09-21 03:10:06 +04:00
vg_revert ( origin - > vg ) ;
2011-07-08 16:48:41 +04:00
return 0 ;
}
if ( ! vg_commit ( origin - > vg ) )
return_0 ;
2013-04-21 12:37:52 +04:00
if ( is_origin_active ) {
/*
* If the snapshot was active and the COW LV is taken away
* the LV lock on cluster has to be grabbed , so use
* activate_lv ( ) which resumes suspend cow device .
*/
2019-10-26 00:29:16 +03:00
if ( ! activate_lv ( cow - > vg - > cmd , cow ) ) {
2013-04-21 12:37:52 +04:00
log_error ( " Failed to activate %s. " , cow - > name ) ;
return 0 ;
}
if ( ! resume_lv ( origin - > vg - > cmd , origin ) ) {
log_error ( " Failed to resume %s. " , origin - > name ) ;
return 0 ;
}
2010-02-18 01:59:46 +03:00
}
2005-04-07 16:39:44 +04:00
return 1 ;
2002-02-12 19:31:31 +03:00
}
2017-10-27 17:48:57 +03:00
/* Check if given LV is usable as snapshot origin LV */
int validate_snapshot_origin ( const struct logical_volume * origin_lv )
{
const char * err = NULL ; /* For error string */
2023-03-18 00:39:48 +03:00
if ( lv_is_cache ( origin_lv ) | | lv_is_writecache ( origin_lv ) ) {
struct logical_volume * lv = seg_lv ( first_seg ( origin_lv ) , 0 ) ;
if ( lv_is_raid ( lv ) & & lv_raid_has_integrity ( lv ) ) {
err = " raid with integrity " ;
goto out ;
}
}
2017-10-27 17:48:57 +03:00
if ( lv_is_cow ( origin_lv ) )
err = " snapshots " ;
else if ( lv_is_locked ( origin_lv ) )
err = " locked volumes " ;
else if ( lv_is_pvmove ( origin_lv ) )
err = " pvmoved volumes " ;
else if ( ! lv_is_visible ( origin_lv ) )
err = " hidden volumes " ;
else if ( lv_is_merging_origin ( origin_lv ) )
err = " an origin that has a merging snapshot " ;
else if ( lv_is_cache_type ( origin_lv ) & & ! lv_is_cache ( origin_lv ) )
err = " cache type volumes " ;
else if ( lv_is_thin_type ( origin_lv ) & & ! lv_is_thin_volume ( origin_lv ) )
err = " thin pool type volumes " ;
else if ( lv_is_mirror_type ( origin_lv ) ) {
if ( ! lv_is_mirror ( origin_lv ) )
err = " mirror subvolumes " ;
else {
log_warn ( " WARNING: Snapshots of mirrors can deadlock under rare device failures. " ) ;
log_warn ( " WARNING: Consider using the raid1 mirror type to avoid this. " ) ;
log_warn ( " WARNING: See global/mirror_segtype_default in lvm.conf. " ) ;
}
2020-02-06 20:27:33 +03:00
} else if ( lv_is_raid_type ( origin_lv ) & & ! lv_is_raid ( origin_lv ) ) {
2017-10-27 17:48:57 +03:00
err = " raid subvolumes " ;
2019-11-21 01:07:27 +03:00
} else if ( lv_is_raid ( origin_lv ) & & lv_raid_has_integrity ( ( struct logical_volume * ) origin_lv ) ) {
err = " raid with integrity " ;
2020-02-06 20:27:33 +03:00
}
2017-10-27 17:48:57 +03:00
2023-03-18 00:39:48 +03:00
out :
2017-10-27 17:48:57 +03:00
if ( err ) {
log_error ( " Snapshots of %s are not supported. " , err ) ;
return 0 ;
}
return 1 ;
}