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 ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2002-02-12 19:31:31 +03:00
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2002-02-12 19:31:31 +03:00
# include "metadata.h"
2014-02-18 00:48:27 +04:00
# include "segtype.h"
2010-02-18 01:59:46 +03:00
# include "locking.h"
2002-02-12 19:31:31 +03:00
# include "toolcontext.h"
2005-04-07 16:39:44 +04:00
# include "lv_alloc.h"
2010-04-23 06:57:39 +04:00
# include "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 */
return ( ! lv_is_thin_volume ( lv ) & & ! lv_is_origin ( lv ) & & lv - > snapshot ) ? 1 : 0 ;
2002-04-24 22:20:51 +04:00
}
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 )
{
2013-05-29 14:44:54 +04:00
return lv_is_cow ( lv ) & &
2014-02-25 22:43:07 +04:00
( lv - > size > = _cow_max_size ( lv - > vg - > cmd , origin_from_cow ( lv ) - > 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 )
{
2009-05-14 01:25:45 +04:00
if ( lv - > status & SNAPSHOT )
return 0 ;
if ( lv_is_cow ( lv ) ) {
if ( lv_is_virtual_origin ( origin_from_cow ( lv ) ) )
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 ;
2009-05-14 01:25:45 +04:00
return lv_is_visible ( origin_from_cow ( lv ) ) ;
}
2006-04-07 21:41:56 +04:00
return lv - > status & VISIBLE_LV ? 1 : 0 ;
}
2009-04-25 05:17:59 +04:00
int lv_is_virtual_origin ( const struct logical_volume * lv )
{
return ( lv - > status & VIRTUAL_ORIGIN ) ? 1 : 0 ;
}
2010-01-13 04:55:43 +03:00
int lv_is_merging_origin ( const struct logical_volume * origin )
{
2014-09-16 00:33:53 +04:00
return lv_is_merging ( origin ) ;
2010-01-13 04:55:43 +03:00
}
int lv_is_merging_cow ( const struct logical_volume * snapshot )
{
2014-06-16 14:41:30 +04:00
struct lv_segment * snap_seg = find_snapshot ( snapshot ) ;
2014-09-16 00:33:53 +04:00
2010-01-13 04:56:18 +03:00
/* checks lv_segment's status to see if cow 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 ;
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
}
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
2008-01-30 16:19:47 +03:00
if ( ! ( seg = alloc_snapshot_seg ( snap , 0 , 0 ) ) )
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
{
2011-07-08 16:48:41 +04:00
int merging_snapshot = 0 ;
2010-01-13 04:55:43 +03:00
struct logical_volume * origin = origin_from_cow ( cow ) ;
2013-04-21 12:37:52 +04:00
int 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 ) ) {
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
*/
2014-09-21 03:16:06 +04:00
if ( lv_has_target_type ( origin - > vg - > vgmem , origin , NULL ,
2010-04-23 06:57:39 +04:00
" snapshot-merge " ) ) {
/*
* preload origin to :
* - allow proper release of - cow
* - avoid allocations with other devices suspended
* when transitioning from " snapshot-merge " to
* " snapshot-origin after a merge completes.
*/
2011-07-08 16:48:41 +04:00
merging_snapshot = 1 ;
2010-04-23 06:57:39 +04:00
}
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
2011-12-20 04:02:18 +04:00
/* format1 must do the change in one step, with the commit last. */
2013-04-21 12:37:52 +04:00
if ( ! ( origin - > vg - > fid - > fmt - > features & FMT_MDAS ) ) {
/* Get the lock for COW volume */
if ( is_origin_active & & ! activate_lv ( cow - > vg - > cmd , cow ) ) {
log_error ( " Unable to activate logical volume \" %s \" " ,
cow - > name ) ;
return 0 ;
}
2011-12-20 04:02:18 +04:00
return 1 ;
2013-04-21 12:37:52 +04:00
}
2011-12-20 04:02:18 +04:00
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 .
*/
if ( ! merging_snapshot & & ! activate_lv ( cow - > vg - > cmd , cow ) ) {
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 ;
}
2013-07-22 18:20:36 +04:00
/*
2013-07-23 17:15:04 +04:00
* For merged snapshot and clustered VG activate cow LV so
* the following call to deactivate_lv ( ) can clean - up table
* entries . For this clustered lock need to be held .
2013-07-22 18:20:36 +04:00
*/
2013-07-23 17:15:04 +04:00
if ( vg_is_clustered ( cow - > vg ) & &
merging_snapshot & & ! activate_lv ( cow - > vg - > cmd , cow ) ) {
2013-07-22 18:20:36 +04:00
log_error ( " Failed to activate %s. " , cow - > 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
}