summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikos Tsironis <ntsironis@arrikto.com>2019-03-17 08:22:57 -0400
committerMike Snitzer <snitzer@redhat.com>2019-04-18 16:18:29 -0400
commitf79ae415b64c35d9ecca159fe796cf98d2ff9e9c (patch)
tree83b3ccd0c5cb27d1b696602c57834d003b8bcc65
parent4ad8d880b6c4497e365fb6fd16bab52e9974a3f6 (diff)
dm snapshot: Make exception tables scalable
Use list_bl to implement the exception hash tables' buckets. This change permits concurrent access, to distinct buckets, by multiple threads. Also, implement helper functions to lock and unlock the exception tables based on the chunk number of the exception at hand. We retain the global locking, by means of down_write(), which is replaced by the next commit. Still, we must acquire the per-bucket spinlocks when accessing the hash tables, since list_bl does not allow modification on unlocked lists. Co-developed-by: Ilias Tsitsimpis <iliastsi@arrikto.com> Signed-off-by: Nikos Tsironis <ntsironis@arrikto.com> Acked-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/dm-exception-store.h3
-rw-r--r--drivers/md/dm-snap.c137
2 files changed, 116 insertions, 24 deletions
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index 12b5216c2cfe..5a3c696c057f 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -11,6 +11,7 @@
11#define _LINUX_DM_EXCEPTION_STORE 11#define _LINUX_DM_EXCEPTION_STORE
12 12
13#include <linux/blkdev.h> 13#include <linux/blkdev.h>
14#include <linux/list_bl.h>
14#include <linux/device-mapper.h> 15#include <linux/device-mapper.h>
15 16
16/* 17/*
@@ -27,7 +28,7 @@ typedef sector_t chunk_t;
27 * chunk within the device. 28 * chunk within the device.
28 */ 29 */
29struct dm_exception { 30struct dm_exception {
30 struct list_head hash_list; 31 struct hlist_bl_node hash_list;
31 32
32 chunk_t old_chunk; 33 chunk_t old_chunk;
33 chunk_t new_chunk; 34 chunk_t new_chunk;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 5a67f408876e..10bb37e27ecf 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -13,6 +13,7 @@
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/kdev_t.h> 14#include <linux/kdev_t.h>
15#include <linux/list.h> 15#include <linux/list.h>
16#include <linux/list_bl.h>
16#include <linux/mempool.h> 17#include <linux/mempool.h>
17#include <linux/module.h> 18#include <linux/module.h>
18#include <linux/slab.h> 19#include <linux/slab.h>
@@ -44,7 +45,7 @@ static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
44struct dm_exception_table { 45struct dm_exception_table {
45 uint32_t hash_mask; 46 uint32_t hash_mask;
46 unsigned hash_shift; 47 unsigned hash_shift;
47 struct list_head *table; 48 struct hlist_bl_head *table;
48}; 49};
49 50
50struct dm_snapshot { 51struct dm_snapshot {
@@ -618,6 +619,36 @@ static void unregister_snapshot(struct dm_snapshot *s)
618 * The lowest hash_shift bits of the chunk number are ignored, allowing 619 * The lowest hash_shift bits of the chunk number are ignored, allowing
619 * some consecutive chunks to be grouped together. 620 * some consecutive chunks to be grouped together.
620 */ 621 */
622static uint32_t exception_hash(struct dm_exception_table *et, chunk_t chunk);
623
624/* Lock to protect access to the completed and pending exception hash tables. */
625struct dm_exception_table_lock {
626 struct hlist_bl_head *complete_slot;
627 struct hlist_bl_head *pending_slot;
628};
629
630static void dm_exception_table_lock_init(struct dm_snapshot *s, chunk_t chunk,
631 struct dm_exception_table_lock *lock)
632{
633 struct dm_exception_table *complete = &s->complete;
634 struct dm_exception_table *pending = &s->pending;
635
636 lock->complete_slot = &complete->table[exception_hash(complete, chunk)];
637 lock->pending_slot = &pending->table[exception_hash(pending, chunk)];
638}
639
640static void dm_exception_table_lock(struct dm_exception_table_lock *lock)
641{
642 hlist_bl_lock(lock->complete_slot);
643 hlist_bl_lock(lock->pending_slot);
644}
645
646static void dm_exception_table_unlock(struct dm_exception_table_lock *lock)
647{
648 hlist_bl_unlock(lock->pending_slot);
649 hlist_bl_unlock(lock->complete_slot);
650}
651
621static int dm_exception_table_init(struct dm_exception_table *et, 652static int dm_exception_table_init(struct dm_exception_table *et,
622 uint32_t size, unsigned hash_shift) 653 uint32_t size, unsigned hash_shift)
623{ 654{
@@ -625,12 +656,12 @@ static int dm_exception_table_init(struct dm_exception_table *et,
625 656
626 et->hash_shift = hash_shift; 657 et->hash_shift = hash_shift;
627 et->hash_mask = size - 1; 658 et->hash_mask = size - 1;
628 et->table = dm_vcalloc(size, sizeof(struct list_head)); 659 et->table = dm_vcalloc(size, sizeof(struct hlist_bl_head));
629 if (!et->table) 660 if (!et->table)
630 return -ENOMEM; 661 return -ENOMEM;
631 662
632 for (i = 0; i < size; i++) 663 for (i = 0; i < size; i++)
633 INIT_LIST_HEAD(et->table + i); 664 INIT_HLIST_BL_HEAD(et->table + i);
634 665
635 return 0; 666 return 0;
636} 667}
@@ -638,15 +669,16 @@ static int dm_exception_table_init(struct dm_exception_table *et,
638static void dm_exception_table_exit(struct dm_exception_table *et, 669static void dm_exception_table_exit(struct dm_exception_table *et,
639 struct kmem_cache *mem) 670 struct kmem_cache *mem)
640{ 671{
641 struct list_head *slot; 672 struct hlist_bl_head *slot;
642 struct dm_exception *ex, *next; 673 struct dm_exception *ex;
674 struct hlist_bl_node *pos, *n;
643 int i, size; 675 int i, size;
644 676
645 size = et->hash_mask + 1; 677 size = et->hash_mask + 1;
646 for (i = 0; i < size; i++) { 678 for (i = 0; i < size; i++) {
647 slot = et->table + i; 679 slot = et->table + i;
648 680
649 list_for_each_entry_safe (ex, next, slot, hash_list) 681 hlist_bl_for_each_entry_safe(ex, pos, n, slot, hash_list)
650 kmem_cache_free(mem, ex); 682 kmem_cache_free(mem, ex);
651 } 683 }
652 684
@@ -660,7 +692,7 @@ static uint32_t exception_hash(struct dm_exception_table *et, chunk_t chunk)
660 692
661static void dm_remove_exception(struct dm_exception *e) 693static void dm_remove_exception(struct dm_exception *e)
662{ 694{
663 list_del(&e->hash_list); 695 hlist_bl_del(&e->hash_list);
664} 696}
665 697
666/* 698/*
@@ -670,11 +702,12 @@ static void dm_remove_exception(struct dm_exception *e)
670static struct dm_exception *dm_lookup_exception(struct dm_exception_table *et, 702static struct dm_exception *dm_lookup_exception(struct dm_exception_table *et,
671 chunk_t chunk) 703 chunk_t chunk)
672{ 704{
673 struct list_head *slot; 705 struct hlist_bl_head *slot;
706 struct hlist_bl_node *pos;
674 struct dm_exception *e; 707 struct dm_exception *e;
675 708
676 slot = &et->table[exception_hash(et, chunk)]; 709 slot = &et->table[exception_hash(et, chunk)];
677 list_for_each_entry (e, slot, hash_list) 710 hlist_bl_for_each_entry(e, pos, slot, hash_list)
678 if (chunk >= e->old_chunk && 711 if (chunk >= e->old_chunk &&
679 chunk <= e->old_chunk + dm_consecutive_chunk_count(e)) 712 chunk <= e->old_chunk + dm_consecutive_chunk_count(e))
680 return e; 713 return e;
@@ -721,7 +754,8 @@ static void free_pending_exception(struct dm_snap_pending_exception *pe)
721static void dm_insert_exception(struct dm_exception_table *eh, 754static void dm_insert_exception(struct dm_exception_table *eh,
722 struct dm_exception *new_e) 755 struct dm_exception *new_e)
723{ 756{
724 struct list_head *l; 757 struct hlist_bl_head *l;
758 struct hlist_bl_node *pos;
725 struct dm_exception *e = NULL; 759 struct dm_exception *e = NULL;
726 760
727 l = &eh->table[exception_hash(eh, new_e->old_chunk)]; 761 l = &eh->table[exception_hash(eh, new_e->old_chunk)];
@@ -731,7 +765,7 @@ static void dm_insert_exception(struct dm_exception_table *eh,
731 goto out; 765 goto out;
732 766
733 /* List is ordered by old_chunk */ 767 /* List is ordered by old_chunk */
734 list_for_each_entry_reverse(e, l, hash_list) { 768 hlist_bl_for_each_entry(e, pos, l, hash_list) {
735 /* Insert after an existing chunk? */ 769 /* Insert after an existing chunk? */
736 if (new_e->old_chunk == (e->old_chunk + 770 if (new_e->old_chunk == (e->old_chunk +
737 dm_consecutive_chunk_count(e) + 1) && 771 dm_consecutive_chunk_count(e) + 1) &&
@@ -752,12 +786,24 @@ static void dm_insert_exception(struct dm_exception_table *eh,
752 return; 786 return;
753 } 787 }
754 788
755 if (new_e->old_chunk > e->old_chunk) 789 if (new_e->old_chunk < e->old_chunk)
756 break; 790 break;
757 } 791 }
758 792
759out: 793out:
760 list_add(&new_e->hash_list, e ? &e->hash_list : l); 794 if (!e) {
795 /*
796 * Either the table doesn't support consecutive chunks or slot
797 * l is empty.
798 */
799 hlist_bl_add_head(&new_e->hash_list, l);
800 } else if (new_e->old_chunk < e->old_chunk) {
801 /* Add before an existing exception */
802 hlist_bl_add_before(&new_e->hash_list, &e->hash_list);
803 } else {
804 /* Add to l's tail: e is the last exception in this slot */
805 hlist_bl_add_behind(&new_e->hash_list, &e->hash_list);
806 }
761} 807}
762 808
763/* 809/*
@@ -766,6 +812,7 @@ out:
766 */ 812 */
767static int dm_add_exception(void *context, chunk_t old, chunk_t new) 813static int dm_add_exception(void *context, chunk_t old, chunk_t new)
768{ 814{
815 struct dm_exception_table_lock lock;
769 struct dm_snapshot *s = context; 816 struct dm_snapshot *s = context;
770 struct dm_exception *e; 817 struct dm_exception *e;
771 818
@@ -778,7 +825,17 @@ static int dm_add_exception(void *context, chunk_t old, chunk_t new)
778 /* Consecutive_count is implicitly initialised to zero */ 825 /* Consecutive_count is implicitly initialised to zero */
779 e->new_chunk = new; 826 e->new_chunk = new;
780 827
828 /*
829 * Although there is no need to lock access to the exception tables
830 * here, if we don't then hlist_bl_add_head(), called by
831 * dm_insert_exception(), will complain about accessing the
832 * corresponding list without locking it first.
833 */
834 dm_exception_table_lock_init(s, old, &lock);
835
836 dm_exception_table_lock(&lock);
781 dm_insert_exception(&s->complete, e); 837 dm_insert_exception(&s->complete, e);
838 dm_exception_table_unlock(&lock);
782 839
783 return 0; 840 return 0;
784} 841}
@@ -807,7 +864,7 @@ static int calc_max_buckets(void)
807{ 864{
808 /* use a fixed size of 2MB */ 865 /* use a fixed size of 2MB */
809 unsigned long mem = 2 * 1024 * 1024; 866 unsigned long mem = 2 * 1024 * 1024;
810 mem /= sizeof(struct list_head); 867 mem /= sizeof(struct hlist_bl_head);
811 868
812 return mem; 869 return mem;
813} 870}
@@ -1473,13 +1530,18 @@ static void pending_complete(void *context, int success)
1473 struct bio *origin_bios = NULL; 1530 struct bio *origin_bios = NULL;
1474 struct bio *snapshot_bios = NULL; 1531 struct bio *snapshot_bios = NULL;
1475 struct bio *full_bio = NULL; 1532 struct bio *full_bio = NULL;
1533 struct dm_exception_table_lock lock;
1476 int error = 0; 1534 int error = 0;
1477 1535
1536 dm_exception_table_lock_init(s, pe->e.old_chunk, &lock);
1537
1478 if (!success) { 1538 if (!success) {
1479 /* Read/write error - snapshot is unusable */ 1539 /* Read/write error - snapshot is unusable */
1480 down_write(&s->lock); 1540 down_write(&s->lock);
1481 __invalidate_snapshot(s, -EIO); 1541 __invalidate_snapshot(s, -EIO);
1482 error = 1; 1542 error = 1;
1543
1544 dm_exception_table_lock(&lock);
1483 goto out; 1545 goto out;
1484 } 1546 }
1485 1547
@@ -1488,11 +1550,14 @@ static void pending_complete(void *context, int success)
1488 down_write(&s->lock); 1550 down_write(&s->lock);
1489 __invalidate_snapshot(s, -ENOMEM); 1551 __invalidate_snapshot(s, -ENOMEM);
1490 error = 1; 1552 error = 1;
1553
1554 dm_exception_table_lock(&lock);
1491 goto out; 1555 goto out;
1492 } 1556 }
1493 *e = pe->e; 1557 *e = pe->e;
1494 1558
1495 down_write(&s->lock); 1559 down_write(&s->lock);
1560 dm_exception_table_lock(&lock);
1496 if (!s->valid) { 1561 if (!s->valid) {
1497 free_completed_exception(e); 1562 free_completed_exception(e);
1498 error = 1; 1563 error = 1;
@@ -1510,14 +1575,19 @@ static void pending_complete(void *context, int success)
1510 1575
1511 /* Wait for conflicting reads to drain */ 1576 /* Wait for conflicting reads to drain */
1512 if (__chunk_is_tracked(s, pe->e.old_chunk)) { 1577 if (__chunk_is_tracked(s, pe->e.old_chunk)) {
1578 dm_exception_table_unlock(&lock);
1513 up_write(&s->lock); 1579 up_write(&s->lock);
1514 __check_for_conflicting_io(s, pe->e.old_chunk); 1580 __check_for_conflicting_io(s, pe->e.old_chunk);
1515 down_write(&s->lock); 1581 down_write(&s->lock);
1582 dm_exception_table_lock(&lock);
1516 } 1583 }
1517 1584
1518out: 1585out:
1519 /* Remove the in-flight exception from the list */ 1586 /* Remove the in-flight exception from the list */
1520 dm_remove_exception(&pe->e); 1587 dm_remove_exception(&pe->e);
1588
1589 dm_exception_table_unlock(&lock);
1590
1521 snapshot_bios = bio_list_get(&pe->snapshot_bios); 1591 snapshot_bios = bio_list_get(&pe->snapshot_bios);
1522 origin_bios = bio_list_get(&pe->origin_bios); 1592 origin_bios = bio_list_get(&pe->origin_bios);
1523 full_bio = pe->full_bio; 1593 full_bio = pe->full_bio;
@@ -1733,6 +1803,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1733 int r = DM_MAPIO_REMAPPED; 1803 int r = DM_MAPIO_REMAPPED;
1734 chunk_t chunk; 1804 chunk_t chunk;
1735 struct dm_snap_pending_exception *pe = NULL; 1805 struct dm_snap_pending_exception *pe = NULL;
1806 struct dm_exception_table_lock lock;
1736 1807
1737 init_tracked_chunk(bio); 1808 init_tracked_chunk(bio);
1738 1809
@@ -1742,6 +1813,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1742 } 1813 }
1743 1814
1744 chunk = sector_to_chunk(s->store, bio->bi_iter.bi_sector); 1815 chunk = sector_to_chunk(s->store, bio->bi_iter.bi_sector);
1816 dm_exception_table_lock_init(s, chunk, &lock);
1745 1817
1746 /* Full snapshots are not usable */ 1818 /* Full snapshots are not usable */
1747 /* To get here the table must be live so s->active is always set. */ 1819 /* To get here the table must be live so s->active is always set. */
@@ -1749,6 +1821,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1749 return DM_MAPIO_KILL; 1821 return DM_MAPIO_KILL;
1750 1822
1751 down_write(&s->lock); 1823 down_write(&s->lock);
1824 dm_exception_table_lock(&lock);
1752 1825
1753 if (!s->valid || (unlikely(s->snapshot_overflowed) && 1826 if (!s->valid || (unlikely(s->snapshot_overflowed) &&
1754 bio_data_dir(bio) == WRITE)) { 1827 bio_data_dir(bio) == WRITE)) {
@@ -1771,9 +1844,11 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1771 if (bio_data_dir(bio) == WRITE) { 1844 if (bio_data_dir(bio) == WRITE) {
1772 pe = __lookup_pending_exception(s, chunk); 1845 pe = __lookup_pending_exception(s, chunk);
1773 if (!pe) { 1846 if (!pe) {
1847 dm_exception_table_unlock(&lock);
1774 up_write(&s->lock); 1848 up_write(&s->lock);
1775 pe = alloc_pending_exception(s); 1849 pe = alloc_pending_exception(s);
1776 down_write(&s->lock); 1850 down_write(&s->lock);
1851 dm_exception_table_lock(&lock);
1777 1852
1778 if (!s->valid || s->snapshot_overflowed) { 1853 if (!s->valid || s->snapshot_overflowed) {
1779 free_pending_exception(pe); 1854 free_pending_exception(pe);
@@ -1790,13 +1865,17 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1790 1865
1791 pe = __find_pending_exception(s, pe, chunk); 1866 pe = __find_pending_exception(s, pe, chunk);
1792 if (!pe) { 1867 if (!pe) {
1868 dm_exception_table_unlock(&lock);
1869
1793 if (s->store->userspace_supports_overflow) { 1870 if (s->store->userspace_supports_overflow) {
1794 s->snapshot_overflowed = 1; 1871 s->snapshot_overflowed = 1;
1795 DMERR("Snapshot overflowed: Unable to allocate exception."); 1872 DMERR("Snapshot overflowed: Unable to allocate exception.");
1796 } else 1873 } else
1797 __invalidate_snapshot(s, -ENOMEM); 1874 __invalidate_snapshot(s, -ENOMEM);
1875 up_write(&s->lock);
1876
1798 r = DM_MAPIO_KILL; 1877 r = DM_MAPIO_KILL;
1799 goto out_unlock; 1878 goto out;
1800 } 1879 }
1801 } 1880 }
1802 1881
@@ -1808,6 +1887,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1808 bio->bi_iter.bi_size == 1887 bio->bi_iter.bi_size ==
1809 (s->store->chunk_size << SECTOR_SHIFT)) { 1888 (s->store->chunk_size << SECTOR_SHIFT)) {
1810 pe->started = 1; 1889 pe->started = 1;
1890 dm_exception_table_unlock(&lock);
1811 up_write(&s->lock); 1891 up_write(&s->lock);
1812 start_full_bio(pe, bio); 1892 start_full_bio(pe, bio);
1813 goto out; 1893 goto out;
@@ -1818,6 +1898,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1818 if (!pe->started) { 1898 if (!pe->started) {
1819 /* this is protected by snap->lock */ 1899 /* this is protected by snap->lock */
1820 pe->started = 1; 1900 pe->started = 1;
1901 dm_exception_table_unlock(&lock);
1821 up_write(&s->lock); 1902 up_write(&s->lock);
1822 start_copy(pe); 1903 start_copy(pe);
1823 goto out; 1904 goto out;
@@ -1828,6 +1909,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
1828 } 1909 }
1829 1910
1830out_unlock: 1911out_unlock:
1912 dm_exception_table_unlock(&lock);
1831 up_write(&s->lock); 1913 up_write(&s->lock);
1832out: 1914out:
1833 return r; 1915 return r;
@@ -2129,6 +2211,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
2129 struct dm_snap_pending_exception *pe, *pe2; 2211 struct dm_snap_pending_exception *pe, *pe2;
2130 struct dm_snap_pending_exception *pe_to_start_now = NULL; 2212 struct dm_snap_pending_exception *pe_to_start_now = NULL;
2131 struct dm_snap_pending_exception *pe_to_start_last = NULL; 2213 struct dm_snap_pending_exception *pe_to_start_last = NULL;
2214 struct dm_exception_table_lock lock;
2132 chunk_t chunk; 2215 chunk_t chunk;
2133 2216
2134 /* Do all the snapshots on this origin */ 2217 /* Do all the snapshots on this origin */
@@ -2140,21 +2223,23 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
2140 if (dm_target_is_snapshot_merge(snap->ti)) 2223 if (dm_target_is_snapshot_merge(snap->ti))
2141 continue; 2224 continue;
2142 2225
2143 down_write(&snap->lock);
2144
2145 /* Only deal with valid and active snapshots */
2146 if (!snap->valid || !snap->active)
2147 goto next_snapshot;
2148
2149 /* Nothing to do if writing beyond end of snapshot */ 2226 /* Nothing to do if writing beyond end of snapshot */
2150 if (sector >= dm_table_get_size(snap->ti->table)) 2227 if (sector >= dm_table_get_size(snap->ti->table))
2151 goto next_snapshot; 2228 continue;
2152 2229
2153 /* 2230 /*
2154 * Remember, different snapshots can have 2231 * Remember, different snapshots can have
2155 * different chunk sizes. 2232 * different chunk sizes.
2156 */ 2233 */
2157 chunk = sector_to_chunk(snap->store, sector); 2234 chunk = sector_to_chunk(snap->store, sector);
2235 dm_exception_table_lock_init(snap, chunk, &lock);
2236
2237 down_write(&snap->lock);
2238 dm_exception_table_lock(&lock);
2239
2240 /* Only deal with valid and active snapshots */
2241 if (!snap->valid || !snap->active)
2242 goto next_snapshot;
2158 2243
2159 pe = __lookup_pending_exception(snap, chunk); 2244 pe = __lookup_pending_exception(snap, chunk);
2160 if (!pe) { 2245 if (!pe) {
@@ -2167,9 +2252,11 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
2167 if (e) 2252 if (e)
2168 goto next_snapshot; 2253 goto next_snapshot;
2169 2254
2255 dm_exception_table_unlock(&lock);
2170 up_write(&snap->lock); 2256 up_write(&snap->lock);
2171 pe = alloc_pending_exception(snap); 2257 pe = alloc_pending_exception(snap);
2172 down_write(&snap->lock); 2258 down_write(&snap->lock);
2259 dm_exception_table_lock(&lock);
2173 2260
2174 if (!snap->valid) { 2261 if (!snap->valid) {
2175 free_pending_exception(pe); 2262 free_pending_exception(pe);
@@ -2187,8 +2274,11 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
2187 2274
2188 pe = __insert_pending_exception(snap, pe, chunk); 2275 pe = __insert_pending_exception(snap, pe, chunk);
2189 if (!pe) { 2276 if (!pe) {
2277 dm_exception_table_unlock(&lock);
2190 __invalidate_snapshot(snap, -ENOMEM); 2278 __invalidate_snapshot(snap, -ENOMEM);
2191 goto next_snapshot; 2279 up_write(&snap->lock);
2280
2281 continue;
2192 } 2282 }
2193 } else { 2283 } else {
2194 free_pending_exception(pe); 2284 free_pending_exception(pe);
@@ -2219,6 +2309,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
2219 } 2309 }
2220 2310
2221next_snapshot: 2311next_snapshot:
2312 dm_exception_table_unlock(&lock);
2222 up_write(&snap->lock); 2313 up_write(&snap->lock);
2223 2314
2224 if (pe_to_start_now) { 2315 if (pe_to_start_now) {