diff options
Diffstat (limited to 'drivers/md/dm-snap.c')
-rw-r--r-- | drivers/md/dm-snap.c | 163 |
1 files changed, 141 insertions, 22 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 1ba8a47d61b1..6e5528aecc98 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -40,6 +40,11 @@ | |||
40 | */ | 40 | */ |
41 | #define SNAPSHOT_PAGES (((1UL << 20) >> PAGE_SHIFT) ? : 1) | 41 | #define SNAPSHOT_PAGES (((1UL << 20) >> PAGE_SHIFT) ? : 1) |
42 | 42 | ||
43 | /* | ||
44 | * The size of the mempool used to track chunks in use. | ||
45 | */ | ||
46 | #define MIN_IOS 256 | ||
47 | |||
43 | static struct workqueue_struct *ksnapd; | 48 | static struct workqueue_struct *ksnapd; |
44 | static void flush_queued_bios(struct work_struct *work); | 49 | static void flush_queued_bios(struct work_struct *work); |
45 | 50 | ||
@@ -91,7 +96,63 @@ struct dm_snap_pending_exception { | |||
91 | */ | 96 | */ |
92 | static struct kmem_cache *exception_cache; | 97 | static struct kmem_cache *exception_cache; |
93 | static struct kmem_cache *pending_cache; | 98 | static struct kmem_cache *pending_cache; |
94 | static mempool_t *pending_pool; | 99 | |
100 | struct dm_snap_tracked_chunk { | ||
101 | struct hlist_node node; | ||
102 | chunk_t chunk; | ||
103 | }; | ||
104 | |||
105 | static struct kmem_cache *tracked_chunk_cache; | ||
106 | |||
107 | static struct dm_snap_tracked_chunk *track_chunk(struct dm_snapshot *s, | ||
108 | chunk_t chunk) | ||
109 | { | ||
110 | struct dm_snap_tracked_chunk *c = mempool_alloc(s->tracked_chunk_pool, | ||
111 | GFP_NOIO); | ||
112 | unsigned long flags; | ||
113 | |||
114 | c->chunk = chunk; | ||
115 | |||
116 | spin_lock_irqsave(&s->tracked_chunk_lock, flags); | ||
117 | hlist_add_head(&c->node, | ||
118 | &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)]); | ||
119 | spin_unlock_irqrestore(&s->tracked_chunk_lock, flags); | ||
120 | |||
121 | return c; | ||
122 | } | ||
123 | |||
124 | static void stop_tracking_chunk(struct dm_snapshot *s, | ||
125 | struct dm_snap_tracked_chunk *c) | ||
126 | { | ||
127 | unsigned long flags; | ||
128 | |||
129 | spin_lock_irqsave(&s->tracked_chunk_lock, flags); | ||
130 | hlist_del(&c->node); | ||
131 | spin_unlock_irqrestore(&s->tracked_chunk_lock, flags); | ||
132 | |||
133 | mempool_free(c, s->tracked_chunk_pool); | ||
134 | } | ||
135 | |||
136 | static int __chunk_is_tracked(struct dm_snapshot *s, chunk_t chunk) | ||
137 | { | ||
138 | struct dm_snap_tracked_chunk *c; | ||
139 | struct hlist_node *hn; | ||
140 | int found = 0; | ||
141 | |||
142 | spin_lock_irq(&s->tracked_chunk_lock); | ||
143 | |||
144 | hlist_for_each_entry(c, hn, | ||
145 | &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)], node) { | ||
146 | if (c->chunk == chunk) { | ||
147 | found = 1; | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | spin_unlock_irq(&s->tracked_chunk_lock); | ||
153 | |||
154 | return found; | ||
155 | } | ||
95 | 156 | ||
96 | /* | 157 | /* |
97 | * One of these per registered origin, held in the snapshot_origins hash | 158 | * One of these per registered origin, held in the snapshot_origins hash |
@@ -302,14 +363,19 @@ static void free_exception(struct dm_snap_exception *e) | |||
302 | kmem_cache_free(exception_cache, e); | 363 | kmem_cache_free(exception_cache, e); |
303 | } | 364 | } |
304 | 365 | ||
305 | static struct dm_snap_pending_exception *alloc_pending_exception(void) | 366 | static struct dm_snap_pending_exception *alloc_pending_exception(struct dm_snapshot *s) |
306 | { | 367 | { |
307 | return mempool_alloc(pending_pool, GFP_NOIO); | 368 | struct dm_snap_pending_exception *pe = mempool_alloc(s->pending_pool, |
369 | GFP_NOIO); | ||
370 | |||
371 | pe->snap = s; | ||
372 | |||
373 | return pe; | ||
308 | } | 374 | } |
309 | 375 | ||
310 | static void free_pending_exception(struct dm_snap_pending_exception *pe) | 376 | static void free_pending_exception(struct dm_snap_pending_exception *pe) |
311 | { | 377 | { |
312 | mempool_free(pe, pending_pool); | 378 | mempool_free(pe, pe->snap->pending_pool); |
313 | } | 379 | } |
314 | 380 | ||
315 | static void insert_completed_exception(struct dm_snapshot *s, | 381 | static void insert_completed_exception(struct dm_snapshot *s, |
@@ -482,6 +548,7 @@ static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg, | |||
482 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 548 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
483 | { | 549 | { |
484 | struct dm_snapshot *s; | 550 | struct dm_snapshot *s; |
551 | int i; | ||
485 | int r = -EINVAL; | 552 | int r = -EINVAL; |
486 | char persistent; | 553 | char persistent; |
487 | char *origin_path; | 554 | char *origin_path; |
@@ -564,11 +631,30 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
564 | goto bad5; | 631 | goto bad5; |
565 | } | 632 | } |
566 | 633 | ||
634 | s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache); | ||
635 | if (!s->pending_pool) { | ||
636 | ti->error = "Could not allocate mempool for pending exceptions"; | ||
637 | goto bad6; | ||
638 | } | ||
639 | |||
640 | s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS, | ||
641 | tracked_chunk_cache); | ||
642 | if (!s->tracked_chunk_pool) { | ||
643 | ti->error = "Could not allocate tracked_chunk mempool for " | ||
644 | "tracking reads"; | ||
645 | goto bad_tracked_chunk_pool; | ||
646 | } | ||
647 | |||
648 | for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++) | ||
649 | INIT_HLIST_HEAD(&s->tracked_chunk_hash[i]); | ||
650 | |||
651 | spin_lock_init(&s->tracked_chunk_lock); | ||
652 | |||
567 | /* Metadata must only be loaded into one table at once */ | 653 | /* Metadata must only be loaded into one table at once */ |
568 | r = s->store.read_metadata(&s->store); | 654 | r = s->store.read_metadata(&s->store); |
569 | if (r < 0) { | 655 | if (r < 0) { |
570 | ti->error = "Failed to read snapshot metadata"; | 656 | ti->error = "Failed to read snapshot metadata"; |
571 | goto bad6; | 657 | goto bad_load_and_register; |
572 | } else if (r > 0) { | 658 | } else if (r > 0) { |
573 | s->valid = 0; | 659 | s->valid = 0; |
574 | DMWARN("Snapshot is marked invalid."); | 660 | DMWARN("Snapshot is marked invalid."); |
@@ -582,7 +668,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
582 | if (register_snapshot(s)) { | 668 | if (register_snapshot(s)) { |
583 | r = -EINVAL; | 669 | r = -EINVAL; |
584 | ti->error = "Cannot register snapshot origin"; | 670 | ti->error = "Cannot register snapshot origin"; |
585 | goto bad6; | 671 | goto bad_load_and_register; |
586 | } | 672 | } |
587 | 673 | ||
588 | ti->private = s; | 674 | ti->private = s; |
@@ -590,6 +676,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
590 | 676 | ||
591 | return 0; | 677 | return 0; |
592 | 678 | ||
679 | bad_load_and_register: | ||
680 | mempool_destroy(s->tracked_chunk_pool); | ||
681 | |||
682 | bad_tracked_chunk_pool: | ||
683 | mempool_destroy(s->pending_pool); | ||
684 | |||
593 | bad6: | 685 | bad6: |
594 | dm_kcopyd_client_destroy(s->kcopyd_client); | 686 | dm_kcopyd_client_destroy(s->kcopyd_client); |
595 | 687 | ||
@@ -624,6 +716,9 @@ static void __free_exceptions(struct dm_snapshot *s) | |||
624 | 716 | ||
625 | static void snapshot_dtr(struct dm_target *ti) | 717 | static void snapshot_dtr(struct dm_target *ti) |
626 | { | 718 | { |
719 | #ifdef CONFIG_DM_DEBUG | ||
720 | int i; | ||
721 | #endif | ||
627 | struct dm_snapshot *s = ti->private; | 722 | struct dm_snapshot *s = ti->private; |
628 | 723 | ||
629 | flush_workqueue(ksnapd); | 724 | flush_workqueue(ksnapd); |
@@ -632,8 +727,17 @@ static void snapshot_dtr(struct dm_target *ti) | |||
632 | /* After this returns there can be no new kcopyd jobs. */ | 727 | /* After this returns there can be no new kcopyd jobs. */ |
633 | unregister_snapshot(s); | 728 | unregister_snapshot(s); |
634 | 729 | ||
730 | #ifdef CONFIG_DM_DEBUG | ||
731 | for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++) | ||
732 | BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i])); | ||
733 | #endif | ||
734 | |||
735 | mempool_destroy(s->tracked_chunk_pool); | ||
736 | |||
635 | __free_exceptions(s); | 737 | __free_exceptions(s); |
636 | 738 | ||
739 | mempool_destroy(s->pending_pool); | ||
740 | |||
637 | dm_put_device(ti, s->origin); | 741 | dm_put_device(ti, s->origin); |
638 | dm_put_device(ti, s->cow); | 742 | dm_put_device(ti, s->cow); |
639 | 743 | ||
@@ -772,6 +876,13 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) | |||
772 | } | 876 | } |
773 | 877 | ||
774 | /* | 878 | /* |
879 | * Check for conflicting reads. This is extremely improbable, | ||
880 | * so yield() is sufficient and there is no need for a wait queue. | ||
881 | */ | ||
882 | while (__chunk_is_tracked(s, pe->e.old_chunk)) | ||
883 | yield(); | ||
884 | |||
885 | /* | ||
775 | * Add a proper exception, and remove the | 886 | * Add a proper exception, and remove the |
776 | * in-flight exception from the list. | 887 | * in-flight exception from the list. |
777 | */ | 888 | */ |
@@ -873,7 +984,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio) | |||
873 | * to hold the lock while we do this. | 984 | * to hold the lock while we do this. |
874 | */ | 985 | */ |
875 | up_write(&s->lock); | 986 | up_write(&s->lock); |
876 | pe = alloc_pending_exception(); | 987 | pe = alloc_pending_exception(s); |
877 | down_write(&s->lock); | 988 | down_write(&s->lock); |
878 | 989 | ||
879 | if (!s->valid) { | 990 | if (!s->valid) { |
@@ -893,7 +1004,6 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio) | |||
893 | bio_list_init(&pe->snapshot_bios); | 1004 | bio_list_init(&pe->snapshot_bios); |
894 | pe->primary_pe = NULL; | 1005 | pe->primary_pe = NULL; |
895 | atomic_set(&pe->ref_count, 0); | 1006 | atomic_set(&pe->ref_count, 0); |
896 | pe->snap = s; | ||
897 | pe->started = 0; | 1007 | pe->started = 0; |
898 | 1008 | ||
899 | if (s->store.prepare_exception(&s->store, &pe->e)) { | 1009 | if (s->store.prepare_exception(&s->store, &pe->e)) { |
@@ -974,14 +1084,10 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
974 | start_copy(pe); | 1084 | start_copy(pe); |
975 | goto out; | 1085 | goto out; |
976 | } | 1086 | } |
977 | } else | 1087 | } else { |
978 | /* | ||
979 | * FIXME: this read path scares me because we | ||
980 | * always use the origin when we have a pending | ||
981 | * exception. However I can't think of a | ||
982 | * situation where this is wrong - ejt. | ||
983 | */ | ||
984 | bio->bi_bdev = s->origin->bdev; | 1088 | bio->bi_bdev = s->origin->bdev; |
1089 | map_context->ptr = track_chunk(s, chunk); | ||
1090 | } | ||
985 | 1091 | ||
986 | out_unlock: | 1092 | out_unlock: |
987 | up_write(&s->lock); | 1093 | up_write(&s->lock); |
@@ -989,6 +1095,18 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
989 | return r; | 1095 | return r; |
990 | } | 1096 | } |
991 | 1097 | ||
1098 | static int snapshot_end_io(struct dm_target *ti, struct bio *bio, | ||
1099 | int error, union map_info *map_context) | ||
1100 | { | ||
1101 | struct dm_snapshot *s = ti->private; | ||
1102 | struct dm_snap_tracked_chunk *c = map_context->ptr; | ||
1103 | |||
1104 | if (c) | ||
1105 | stop_tracking_chunk(s, c); | ||
1106 | |||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
992 | static void snapshot_resume(struct dm_target *ti) | 1110 | static void snapshot_resume(struct dm_target *ti) |
993 | { | 1111 | { |
994 | struct dm_snapshot *s = ti->private; | 1112 | struct dm_snapshot *s = ti->private; |
@@ -1266,6 +1384,7 @@ static struct target_type snapshot_target = { | |||
1266 | .ctr = snapshot_ctr, | 1384 | .ctr = snapshot_ctr, |
1267 | .dtr = snapshot_dtr, | 1385 | .dtr = snapshot_dtr, |
1268 | .map = snapshot_map, | 1386 | .map = snapshot_map, |
1387 | .end_io = snapshot_end_io, | ||
1269 | .resume = snapshot_resume, | 1388 | .resume = snapshot_resume, |
1270 | .status = snapshot_status, | 1389 | .status = snapshot_status, |
1271 | }; | 1390 | }; |
@@ -1306,9 +1425,9 @@ static int __init dm_snapshot_init(void) | |||
1306 | goto bad4; | 1425 | goto bad4; |
1307 | } | 1426 | } |
1308 | 1427 | ||
1309 | pending_pool = mempool_create_slab_pool(128, pending_cache); | 1428 | tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0); |
1310 | if (!pending_pool) { | 1429 | if (!tracked_chunk_cache) { |
1311 | DMERR("Couldn't create pending pool."); | 1430 | DMERR("Couldn't create cache to track chunks in use."); |
1312 | r = -ENOMEM; | 1431 | r = -ENOMEM; |
1313 | goto bad5; | 1432 | goto bad5; |
1314 | } | 1433 | } |
@@ -1317,13 +1436,13 @@ static int __init dm_snapshot_init(void) | |||
1317 | if (!ksnapd) { | 1436 | if (!ksnapd) { |
1318 | DMERR("Failed to create ksnapd workqueue."); | 1437 | DMERR("Failed to create ksnapd workqueue."); |
1319 | r = -ENOMEM; | 1438 | r = -ENOMEM; |
1320 | goto bad6; | 1439 | goto bad_pending_pool; |
1321 | } | 1440 | } |
1322 | 1441 | ||
1323 | return 0; | 1442 | return 0; |
1324 | 1443 | ||
1325 | bad6: | 1444 | bad_pending_pool: |
1326 | mempool_destroy(pending_pool); | 1445 | kmem_cache_destroy(tracked_chunk_cache); |
1327 | bad5: | 1446 | bad5: |
1328 | kmem_cache_destroy(pending_cache); | 1447 | kmem_cache_destroy(pending_cache); |
1329 | bad4: | 1448 | bad4: |
@@ -1352,9 +1471,9 @@ static void __exit dm_snapshot_exit(void) | |||
1352 | DMERR("origin unregister failed %d", r); | 1471 | DMERR("origin unregister failed %d", r); |
1353 | 1472 | ||
1354 | exit_origin_hash(); | 1473 | exit_origin_hash(); |
1355 | mempool_destroy(pending_pool); | ||
1356 | kmem_cache_destroy(pending_cache); | 1474 | kmem_cache_destroy(pending_cache); |
1357 | kmem_cache_destroy(exception_cache); | 1475 | kmem_cache_destroy(exception_cache); |
1476 | kmem_cache_destroy(tracked_chunk_cache); | ||
1358 | } | 1477 | } |
1359 | 1478 | ||
1360 | /* Module hooks */ | 1479 | /* Module hooks */ |