diff options
-rw-r--r-- | Documentation/device-mapper/snapshot.txt | 60 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 53 |
2 files changed, 96 insertions, 17 deletions
diff --git a/Documentation/device-mapper/snapshot.txt b/Documentation/device-mapper/snapshot.txt index a5009c8300f3..e3a77b215135 100644 --- a/Documentation/device-mapper/snapshot.txt +++ b/Documentation/device-mapper/snapshot.txt | |||
@@ -8,13 +8,19 @@ the block device which are also writable without interfering with the | |||
8 | original content; | 8 | original content; |
9 | *) To create device "forks", i.e. multiple different versions of the | 9 | *) To create device "forks", i.e. multiple different versions of the |
10 | same data stream. | 10 | same data stream. |
11 | *) To merge a snapshot of a block device back into the snapshot's origin | ||
12 | device. | ||
11 | 13 | ||
14 | In the first two cases, dm copies only the chunks of data that get | ||
15 | changed and uses a separate copy-on-write (COW) block device for | ||
16 | storage. | ||
12 | 17 | ||
13 | In both cases, dm copies only the chunks of data that get changed and | 18 | For snapshot merge the contents of the COW storage are merged back into |
14 | uses a separate copy-on-write (COW) block device for storage. | 19 | the origin device. |
15 | 20 | ||
16 | 21 | ||
17 | There are two dm targets available: snapshot and snapshot-origin. | 22 | There are three dm targets available: |
23 | snapshot, snapshot-origin, and snapshot-merge. | ||
18 | 24 | ||
19 | *) snapshot-origin <origin> | 25 | *) snapshot-origin <origin> |
20 | 26 | ||
@@ -40,8 +46,25 @@ The difference is that for transient snapshots less metadata must be | |||
40 | saved on disk - they can be kept in memory by the kernel. | 46 | saved on disk - they can be kept in memory by the kernel. |
41 | 47 | ||
42 | 48 | ||
43 | How this is used by LVM2 | 49 | * snapshot-merge <origin> <COW device> <persistent> <chunksize> |
44 | ======================== | 50 | |
51 | takes the same table arguments as the snapshot target except it only | ||
52 | works with persistent snapshots. This target assumes the role of the | ||
53 | "snapshot-origin" target and must not be loaded if the "snapshot-origin" | ||
54 | is still present for <origin>. | ||
55 | |||
56 | Creates a merging snapshot that takes control of the changed chunks | ||
57 | stored in the <COW device> of an existing snapshot, through a handover | ||
58 | procedure, and merges these chunks back into the <origin>. Once merging | ||
59 | has started (in the background) the <origin> may be opened and the merge | ||
60 | will continue while I/O is flowing to it. Changes to the <origin> are | ||
61 | deferred until the merging snapshot's corresponding chunk(s) have been | ||
62 | merged. Once merging has started the snapshot device, associated with | ||
63 | the "snapshot" target, will return -EIO when accessed. | ||
64 | |||
65 | |||
66 | How snapshot is used by LVM2 | ||
67 | ============================ | ||
45 | When you create the first LVM2 snapshot of a volume, four dm devices are used: | 68 | When you create the first LVM2 snapshot of a volume, four dm devices are used: |
46 | 69 | ||
47 | 1) a device containing the original mapping table of the source volume; | 70 | 1) a device containing the original mapping table of the source volume; |
@@ -72,3 +95,30 @@ brw------- 1 root root 254, 12 29 ago 18:15 /dev/mapper/volumeGroup-snap-cow | |||
72 | brw------- 1 root root 254, 13 29 ago 18:15 /dev/mapper/volumeGroup-snap | 95 | brw------- 1 root root 254, 13 29 ago 18:15 /dev/mapper/volumeGroup-snap |
73 | brw------- 1 root root 254, 10 29 ago 18:14 /dev/mapper/volumeGroup-base | 96 | brw------- 1 root root 254, 10 29 ago 18:14 /dev/mapper/volumeGroup-base |
74 | 97 | ||
98 | |||
99 | How snapshot-merge is used by LVM2 | ||
100 | ================================== | ||
101 | A merging snapshot assumes the role of the "snapshot-origin" while | ||
102 | merging. As such the "snapshot-origin" is replaced with | ||
103 | "snapshot-merge". The "-real" device is not changed and the "-cow" | ||
104 | device is renamed to <origin name>-cow to aid LVM2's cleanup of the | ||
105 | merging snapshot after it completes. The "snapshot" that hands over its | ||
106 | COW device to the "snapshot-merge" is deactivated (unless using lvchange | ||
107 | --refresh); but if it is left active it will simply return I/O errors. | ||
108 | |||
109 | A snapshot will merge into its origin with the following command: | ||
110 | |||
111 | lvconvert --merge volumeGroup/snap | ||
112 | |||
113 | we'll now have this situation: | ||
114 | |||
115 | # dmsetup table|grep volumeGroup | ||
116 | |||
117 | volumeGroup-base-real: 0 2097152 linear 8:19 384 | ||
118 | volumeGroup-base-cow: 0 204800 linear 8:19 2097536 | ||
119 | volumeGroup-base: 0 2097152 snapshot-merge 254:11 254:12 P 16 | ||
120 | |||
121 | # ls -lL /dev/mapper/volumeGroup-* | ||
122 | brw------- 1 root root 254, 11 29 ago 18:15 /dev/mapper/volumeGroup-base-real | ||
123 | brw------- 1 root root 254, 12 29 ago 18:16 /dev/mapper/volumeGroup-base-cow | ||
124 | brw------- 1 root root 254, 10 29 ago 18:16 /dev/mapper/volumeGroup-base | ||
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 288994ee7142..446827f98236 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -25,6 +25,11 @@ | |||
25 | 25 | ||
26 | #define DM_MSG_PREFIX "snapshots" | 26 | #define DM_MSG_PREFIX "snapshots" |
27 | 27 | ||
28 | static const char dm_snapshot_merge_target_name[] = "snapshot-merge"; | ||
29 | |||
30 | #define dm_target_is_snapshot_merge(ti) \ | ||
31 | ((ti)->type->name == dm_snapshot_merge_target_name) | ||
32 | |||
28 | /* | 33 | /* |
29 | * The percentage increment we will wake up users at | 34 | * The percentage increment we will wake up users at |
30 | */ | 35 | */ |
@@ -1743,6 +1748,21 @@ static struct target_type snapshot_target = { | |||
1743 | .iterate_devices = snapshot_iterate_devices, | 1748 | .iterate_devices = snapshot_iterate_devices, |
1744 | }; | 1749 | }; |
1745 | 1750 | ||
1751 | static struct target_type merge_target = { | ||
1752 | .name = dm_snapshot_merge_target_name, | ||
1753 | .version = {1, 0, 0}, | ||
1754 | .module = THIS_MODULE, | ||
1755 | .ctr = snapshot_ctr, | ||
1756 | .dtr = snapshot_dtr, | ||
1757 | .map = snapshot_map, | ||
1758 | .end_io = snapshot_end_io, | ||
1759 | .postsuspend = snapshot_postsuspend, | ||
1760 | .preresume = snapshot_preresume, | ||
1761 | .resume = snapshot_resume, | ||
1762 | .status = snapshot_status, | ||
1763 | .iterate_devices = snapshot_iterate_devices, | ||
1764 | }; | ||
1765 | |||
1746 | static int __init dm_snapshot_init(void) | 1766 | static int __init dm_snapshot_init(void) |
1747 | { | 1767 | { |
1748 | int r; | 1768 | int r; |
@@ -1754,7 +1774,7 @@ static int __init dm_snapshot_init(void) | |||
1754 | } | 1774 | } |
1755 | 1775 | ||
1756 | r = dm_register_target(&snapshot_target); | 1776 | r = dm_register_target(&snapshot_target); |
1757 | if (r) { | 1777 | if (r < 0) { |
1758 | DMERR("snapshot target register failed %d", r); | 1778 | DMERR("snapshot target register failed %d", r); |
1759 | goto bad_register_snapshot_target; | 1779 | goto bad_register_snapshot_target; |
1760 | } | 1780 | } |
@@ -1762,34 +1782,40 @@ static int __init dm_snapshot_init(void) | |||
1762 | r = dm_register_target(&origin_target); | 1782 | r = dm_register_target(&origin_target); |
1763 | if (r < 0) { | 1783 | if (r < 0) { |
1764 | DMERR("Origin target register failed %d", r); | 1784 | DMERR("Origin target register failed %d", r); |
1765 | goto bad1; | 1785 | goto bad_register_origin_target; |
1786 | } | ||
1787 | |||
1788 | r = dm_register_target(&merge_target); | ||
1789 | if (r < 0) { | ||
1790 | DMERR("Merge target register failed %d", r); | ||
1791 | goto bad_register_merge_target; | ||
1766 | } | 1792 | } |
1767 | 1793 | ||
1768 | r = init_origin_hash(); | 1794 | r = init_origin_hash(); |
1769 | if (r) { | 1795 | if (r) { |
1770 | DMERR("init_origin_hash failed."); | 1796 | DMERR("init_origin_hash failed."); |
1771 | goto bad2; | 1797 | goto bad_origin_hash; |
1772 | } | 1798 | } |
1773 | 1799 | ||
1774 | exception_cache = KMEM_CACHE(dm_exception, 0); | 1800 | exception_cache = KMEM_CACHE(dm_exception, 0); |
1775 | if (!exception_cache) { | 1801 | if (!exception_cache) { |
1776 | DMERR("Couldn't create exception cache."); | 1802 | DMERR("Couldn't create exception cache."); |
1777 | r = -ENOMEM; | 1803 | r = -ENOMEM; |
1778 | goto bad3; | 1804 | goto bad_exception_cache; |
1779 | } | 1805 | } |
1780 | 1806 | ||
1781 | pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0); | 1807 | pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0); |
1782 | if (!pending_cache) { | 1808 | if (!pending_cache) { |
1783 | DMERR("Couldn't create pending cache."); | 1809 | DMERR("Couldn't create pending cache."); |
1784 | r = -ENOMEM; | 1810 | r = -ENOMEM; |
1785 | goto bad4; | 1811 | goto bad_pending_cache; |
1786 | } | 1812 | } |
1787 | 1813 | ||
1788 | tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0); | 1814 | tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0); |
1789 | if (!tracked_chunk_cache) { | 1815 | if (!tracked_chunk_cache) { |
1790 | DMERR("Couldn't create cache to track chunks in use."); | 1816 | DMERR("Couldn't create cache to track chunks in use."); |
1791 | r = -ENOMEM; | 1817 | r = -ENOMEM; |
1792 | goto bad5; | 1818 | goto bad_tracked_chunk_cache; |
1793 | } | 1819 | } |
1794 | 1820 | ||
1795 | ksnapd = create_singlethread_workqueue("ksnapd"); | 1821 | ksnapd = create_singlethread_workqueue("ksnapd"); |
@@ -1803,19 +1829,21 @@ static int __init dm_snapshot_init(void) | |||
1803 | 1829 | ||
1804 | bad_pending_pool: | 1830 | bad_pending_pool: |
1805 | kmem_cache_destroy(tracked_chunk_cache); | 1831 | kmem_cache_destroy(tracked_chunk_cache); |
1806 | bad5: | 1832 | bad_tracked_chunk_cache: |
1807 | kmem_cache_destroy(pending_cache); | 1833 | kmem_cache_destroy(pending_cache); |
1808 | bad4: | 1834 | bad_pending_cache: |
1809 | kmem_cache_destroy(exception_cache); | 1835 | kmem_cache_destroy(exception_cache); |
1810 | bad3: | 1836 | bad_exception_cache: |
1811 | exit_origin_hash(); | 1837 | exit_origin_hash(); |
1812 | bad2: | 1838 | bad_origin_hash: |
1839 | dm_unregister_target(&merge_target); | ||
1840 | bad_register_merge_target: | ||
1813 | dm_unregister_target(&origin_target); | 1841 | dm_unregister_target(&origin_target); |
1814 | bad1: | 1842 | bad_register_origin_target: |
1815 | dm_unregister_target(&snapshot_target); | 1843 | dm_unregister_target(&snapshot_target); |
1816 | |||
1817 | bad_register_snapshot_target: | 1844 | bad_register_snapshot_target: |
1818 | dm_exception_store_exit(); | 1845 | dm_exception_store_exit(); |
1846 | |||
1819 | return r; | 1847 | return r; |
1820 | } | 1848 | } |
1821 | 1849 | ||
@@ -1825,6 +1853,7 @@ static void __exit dm_snapshot_exit(void) | |||
1825 | 1853 | ||
1826 | dm_unregister_target(&snapshot_target); | 1854 | dm_unregister_target(&snapshot_target); |
1827 | dm_unregister_target(&origin_target); | 1855 | dm_unregister_target(&origin_target); |
1856 | dm_unregister_target(&merge_target); | ||
1828 | 1857 | ||
1829 | exit_origin_hash(); | 1858 | exit_origin_hash(); |
1830 | kmem_cache_destroy(pending_cache); | 1859 | kmem_cache_destroy(pending_cache); |