Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 176571
b: refs/heads/master
c: d698aa4
h: refs/heads/master
i:
  176569: 44d0f13
  176567: 60baedf
v: v3
  • Loading branch information
Mikulas Patocka authored and Alasdair G Kergon committed Dec 10, 2009
1 parent 7d22110 commit 8682f7f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 18 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 4454a6216f75a9ef8c4bd0a65e34b101f725ef1e
refs/heads/master: d698aa4500aa3ca9559142060caf0f79da998744
60 changes: 55 additions & 5 deletions trunk/Documentation/device-mapper/snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ the block device which are also writable without interfering with the
original content;
*) To create device "forks", i.e. multiple different versions of the
same data stream.
*) To merge a snapshot of a block device back into the snapshot's origin
device.

In the first two cases, dm copies only the chunks of data that get
changed and uses a separate copy-on-write (COW) block device for
storage.

In both cases, dm copies only the chunks of data that get changed and
uses a separate copy-on-write (COW) block device for storage.
For snapshot merge the contents of the COW storage are merged back into
the origin device.


There are two dm targets available: snapshot and snapshot-origin.
There are three dm targets available:
snapshot, snapshot-origin, and snapshot-merge.

*) snapshot-origin <origin>

Expand All @@ -40,8 +46,25 @@ The difference is that for transient snapshots less metadata must be
saved on disk - they can be kept in memory by the kernel.


How this is used by LVM2
========================
* snapshot-merge <origin> <COW device> <persistent> <chunksize>

takes the same table arguments as the snapshot target except it only
works with persistent snapshots. This target assumes the role of the
"snapshot-origin" target and must not be loaded if the "snapshot-origin"
is still present for <origin>.

Creates a merging snapshot that takes control of the changed chunks
stored in the <COW device> of an existing snapshot, through a handover
procedure, and merges these chunks back into the <origin>. Once merging
has started (in the background) the <origin> may be opened and the merge
will continue while I/O is flowing to it. Changes to the <origin> are
deferred until the merging snapshot's corresponding chunk(s) have been
merged. Once merging has started the snapshot device, associated with
the "snapshot" target, will return -EIO when accessed.


How snapshot is used by LVM2
============================
When you create the first LVM2 snapshot of a volume, four dm devices are used:

1) a device containing the original mapping table of the source volume;
Expand Down Expand Up @@ -72,3 +95,30 @@ brw------- 1 root root 254, 12 29 ago 18:15 /dev/mapper/volumeGroup-snap-cow
brw------- 1 root root 254, 13 29 ago 18:15 /dev/mapper/volumeGroup-snap
brw------- 1 root root 254, 10 29 ago 18:14 /dev/mapper/volumeGroup-base


How snapshot-merge is used by LVM2
==================================
A merging snapshot assumes the role of the "snapshot-origin" while
merging. As such the "snapshot-origin" is replaced with
"snapshot-merge". The "-real" device is not changed and the "-cow"
device is renamed to <origin name>-cow to aid LVM2's cleanup of the
merging snapshot after it completes. The "snapshot" that hands over its
COW device to the "snapshot-merge" is deactivated (unless using lvchange
--refresh); but if it is left active it will simply return I/O errors.

A snapshot will merge into its origin with the following command:

lvconvert --merge volumeGroup/snap

we'll now have this situation:

# dmsetup table|grep volumeGroup

volumeGroup-base-real: 0 2097152 linear 8:19 384
volumeGroup-base-cow: 0 204800 linear 8:19 2097536
volumeGroup-base: 0 2097152 snapshot-merge 254:11 254:12 P 16

# ls -lL /dev/mapper/volumeGroup-*
brw------- 1 root root 254, 11 29 ago 18:15 /dev/mapper/volumeGroup-base-real
brw------- 1 root root 254, 12 29 ago 18:16 /dev/mapper/volumeGroup-base-cow
brw------- 1 root root 254, 10 29 ago 18:16 /dev/mapper/volumeGroup-base
53 changes: 41 additions & 12 deletions trunk/drivers/md/dm-snap.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@

#define DM_MSG_PREFIX "snapshots"

static const char dm_snapshot_merge_target_name[] = "snapshot-merge";

#define dm_target_is_snapshot_merge(ti) \
((ti)->type->name == dm_snapshot_merge_target_name)

/*
* The percentage increment we will wake up users at
*/
Expand Down Expand Up @@ -1743,6 +1748,21 @@ static struct target_type snapshot_target = {
.iterate_devices = snapshot_iterate_devices,
};

static struct target_type merge_target = {
.name = dm_snapshot_merge_target_name,
.version = {1, 0, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
.map = snapshot_map,
.end_io = snapshot_end_io,
.postsuspend = snapshot_postsuspend,
.preresume = snapshot_preresume,
.resume = snapshot_resume,
.status = snapshot_status,
.iterate_devices = snapshot_iterate_devices,
};

static int __init dm_snapshot_init(void)
{
int r;
Expand All @@ -1754,42 +1774,48 @@ static int __init dm_snapshot_init(void)
}

r = dm_register_target(&snapshot_target);
if (r) {
if (r < 0) {
DMERR("snapshot target register failed %d", r);
goto bad_register_snapshot_target;
}

r = dm_register_target(&origin_target);
if (r < 0) {
DMERR("Origin target register failed %d", r);
goto bad1;
goto bad_register_origin_target;
}

r = dm_register_target(&merge_target);
if (r < 0) {
DMERR("Merge target register failed %d", r);
goto bad_register_merge_target;
}

r = init_origin_hash();
if (r) {
DMERR("init_origin_hash failed.");
goto bad2;
goto bad_origin_hash;
}

exception_cache = KMEM_CACHE(dm_exception, 0);
if (!exception_cache) {
DMERR("Couldn't create exception cache.");
r = -ENOMEM;
goto bad3;
goto bad_exception_cache;
}

pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0);
if (!pending_cache) {
DMERR("Couldn't create pending cache.");
r = -ENOMEM;
goto bad4;
goto bad_pending_cache;
}

tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0);
if (!tracked_chunk_cache) {
DMERR("Couldn't create cache to track chunks in use.");
r = -ENOMEM;
goto bad5;
goto bad_tracked_chunk_cache;
}

ksnapd = create_singlethread_workqueue("ksnapd");
Expand All @@ -1803,19 +1829,21 @@ static int __init dm_snapshot_init(void)

bad_pending_pool:
kmem_cache_destroy(tracked_chunk_cache);
bad5:
bad_tracked_chunk_cache:
kmem_cache_destroy(pending_cache);
bad4:
bad_pending_cache:
kmem_cache_destroy(exception_cache);
bad3:
bad_exception_cache:
exit_origin_hash();
bad2:
bad_origin_hash:
dm_unregister_target(&merge_target);
bad_register_merge_target:
dm_unregister_target(&origin_target);
bad1:
bad_register_origin_target:
dm_unregister_target(&snapshot_target);

bad_register_snapshot_target:
dm_exception_store_exit();

return r;
}

Expand All @@ -1825,6 +1853,7 @@ static void __exit dm_snapshot_exit(void)

dm_unregister_target(&snapshot_target);
dm_unregister_target(&origin_target);
dm_unregister_target(&merge_target);

exit_origin_hash();
kmem_cache_destroy(pending_cache);
Expand Down

0 comments on commit 8682f7f

Please sign in to comment.