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); |
