diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-21 13:30:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-21 13:30:10 -0400 |
commit | b7e6f62fe259187f2578d00960ef1b0e6ff6afd5 (patch) | |
tree | 796fd3c878a2f8d8d55ea1e52ef236742a48c3dd | |
parent | 8a392625b665c676a77c62f8608d10ff430bcb83 (diff) | |
parent | d41e26b901111f4e540aa2c27ec7a1681c782be9 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm
* git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm:
dm crypt: add merge
dm table: remove merge_bvec sector restriction
dm: linear add merge
dm: introduce merge_bvec_fn
dm snapshot: use per device mempools
dm snapshot: fix race during exception creation
dm snapshot: track snapshot reads
dm mpath: fix test for reinstate_path
dm mpath: return parameter error
dm io: remove struct padding
dm log: make dm_dirty_log init and exit static
dm mpath: free path selector on invalid args
-rw-r--r-- | drivers/md/dm-crypt.c | 18 | ||||
-rw-r--r-- | drivers/md/dm-linear.c | 38 | ||||
-rw-r--r-- | drivers/md/dm-log.c | 4 | ||||
-rw-r--r-- | drivers/md/dm-mpath.c | 10 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 163 | ||||
-rw-r--r-- | drivers/md/dm-snap.h | 11 | ||||
-rw-r--r-- | drivers/md/dm-table.c | 13 | ||||
-rw-r--r-- | drivers/md/dm.c | 46 | ||||
-rw-r--r-- | drivers/md/dm.h | 6 | ||||
-rw-r--r-- | include/linux/device-mapper.h | 6 | ||||
-rw-r--r-- | include/linux/dm-ioctl.h | 4 |
11 files changed, 270 insertions, 49 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ab6a61db63ce..13956437bc81 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c | |||
@@ -1216,9 +1216,24 @@ error: | |||
1216 | return -EINVAL; | 1216 | return -EINVAL; |
1217 | } | 1217 | } |
1218 | 1218 | ||
1219 | static int crypt_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | ||
1220 | struct bio_vec *biovec, int max_size) | ||
1221 | { | ||
1222 | struct crypt_config *cc = ti->private; | ||
1223 | struct request_queue *q = bdev_get_queue(cc->dev->bdev); | ||
1224 | |||
1225 | if (!q->merge_bvec_fn) | ||
1226 | return max_size; | ||
1227 | |||
1228 | bvm->bi_bdev = cc->dev->bdev; | ||
1229 | bvm->bi_sector = cc->start + bvm->bi_sector - ti->begin; | ||
1230 | |||
1231 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | ||
1232 | } | ||
1233 | |||
1219 | static struct target_type crypt_target = { | 1234 | static struct target_type crypt_target = { |
1220 | .name = "crypt", | 1235 | .name = "crypt", |
1221 | .version= {1, 5, 0}, | 1236 | .version= {1, 6, 0}, |
1222 | .module = THIS_MODULE, | 1237 | .module = THIS_MODULE, |
1223 | .ctr = crypt_ctr, | 1238 | .ctr = crypt_ctr, |
1224 | .dtr = crypt_dtr, | 1239 | .dtr = crypt_dtr, |
@@ -1228,6 +1243,7 @@ static struct target_type crypt_target = { | |||
1228 | .preresume = crypt_preresume, | 1243 | .preresume = crypt_preresume, |
1229 | .resume = crypt_resume, | 1244 | .resume = crypt_resume, |
1230 | .message = crypt_message, | 1245 | .message = crypt_message, |
1246 | .merge = crypt_merge, | ||
1231 | }; | 1247 | }; |
1232 | 1248 | ||
1233 | static int __init dm_crypt_init(void) | 1249 | static int __init dm_crypt_init(void) |
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 17753d80ad22..6449bcdf84ca 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c | |||
@@ -69,13 +69,25 @@ static void linear_dtr(struct dm_target *ti) | |||
69 | kfree(lc); | 69 | kfree(lc); |
70 | } | 70 | } |
71 | 71 | ||
72 | static int linear_map(struct dm_target *ti, struct bio *bio, | 72 | static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector) |
73 | union map_info *map_context) | ||
74 | { | 73 | { |
75 | struct linear_c *lc = (struct linear_c *) ti->private; | 74 | struct linear_c *lc = ti->private; |
75 | |||
76 | return lc->start + (bi_sector - ti->begin); | ||
77 | } | ||
78 | |||
79 | static void linear_map_bio(struct dm_target *ti, struct bio *bio) | ||
80 | { | ||
81 | struct linear_c *lc = ti->private; | ||
76 | 82 | ||
77 | bio->bi_bdev = lc->dev->bdev; | 83 | bio->bi_bdev = lc->dev->bdev; |
78 | bio->bi_sector = lc->start + (bio->bi_sector - ti->begin); | 84 | bio->bi_sector = linear_map_sector(ti, bio->bi_sector); |
85 | } | ||
86 | |||
87 | static int linear_map(struct dm_target *ti, struct bio *bio, | ||
88 | union map_info *map_context) | ||
89 | { | ||
90 | linear_map_bio(ti, bio); | ||
79 | 91 | ||
80 | return DM_MAPIO_REMAPPED; | 92 | return DM_MAPIO_REMAPPED; |
81 | } | 93 | } |
@@ -114,15 +126,31 @@ static int linear_ioctl(struct dm_target *ti, struct inode *inode, | |||
114 | return blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg); | 126 | return blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg); |
115 | } | 127 | } |
116 | 128 | ||
129 | static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | ||
130 | struct bio_vec *biovec, int max_size) | ||
131 | { | ||
132 | struct linear_c *lc = ti->private; | ||
133 | struct request_queue *q = bdev_get_queue(lc->dev->bdev); | ||
134 | |||
135 | if (!q->merge_bvec_fn) | ||
136 | return max_size; | ||
137 | |||
138 | bvm->bi_bdev = lc->dev->bdev; | ||
139 | bvm->bi_sector = linear_map_sector(ti, bvm->bi_sector); | ||
140 | |||
141 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | ||
142 | } | ||
143 | |||
117 | static struct target_type linear_target = { | 144 | static struct target_type linear_target = { |
118 | .name = "linear", | 145 | .name = "linear", |
119 | .version= {1, 0, 2}, | 146 | .version= {1, 0, 3}, |
120 | .module = THIS_MODULE, | 147 | .module = THIS_MODULE, |
121 | .ctr = linear_ctr, | 148 | .ctr = linear_ctr, |
122 | .dtr = linear_dtr, | 149 | .dtr = linear_dtr, |
123 | .map = linear_map, | 150 | .map = linear_map, |
124 | .status = linear_status, | 151 | .status = linear_status, |
125 | .ioctl = linear_ioctl, | 152 | .ioctl = linear_ioctl, |
153 | .merge = linear_merge, | ||
126 | }; | 154 | }; |
127 | 155 | ||
128 | int __init dm_linear_init(void) | 156 | int __init dm_linear_init(void) |
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 67a6f31b7fc3..5b48478c79f5 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c | |||
@@ -831,7 +831,7 @@ static struct dm_dirty_log_type _disk_type = { | |||
831 | .status = disk_status, | 831 | .status = disk_status, |
832 | }; | 832 | }; |
833 | 833 | ||
834 | int __init dm_dirty_log_init(void) | 834 | static int __init dm_dirty_log_init(void) |
835 | { | 835 | { |
836 | int r; | 836 | int r; |
837 | 837 | ||
@@ -848,7 +848,7 @@ int __init dm_dirty_log_init(void) | |||
848 | return r; | 848 | return r; |
849 | } | 849 | } |
850 | 850 | ||
851 | void __exit dm_dirty_log_exit(void) | 851 | static void __exit dm_dirty_log_exit(void) |
852 | { | 852 | { |
853 | dm_dirty_log_type_unregister(&_disk_type); | 853 | dm_dirty_log_type_unregister(&_disk_type); |
854 | dm_dirty_log_type_unregister(&_core_type); | 854 | dm_dirty_log_type_unregister(&_core_type); |
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 9f7302d4878d..fea966d66f98 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c | |||
@@ -525,8 +525,10 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg, | |||
525 | } | 525 | } |
526 | 526 | ||
527 | r = read_param(_params, shift(as), &ps_argc, &ti->error); | 527 | r = read_param(_params, shift(as), &ps_argc, &ti->error); |
528 | if (r) | 528 | if (r) { |
529 | dm_put_path_selector(pst); | ||
529 | return -EINVAL; | 530 | return -EINVAL; |
531 | } | ||
530 | 532 | ||
531 | r = pst->create(&pg->ps, ps_argc, as->argv); | 533 | r = pst->create(&pg->ps, ps_argc, as->argv); |
532 | if (r) { | 534 | if (r) { |
@@ -623,8 +625,10 @@ static struct priority_group *parse_priority_group(struct arg_set *as, | |||
623 | struct pgpath *pgpath; | 625 | struct pgpath *pgpath; |
624 | struct arg_set path_args; | 626 | struct arg_set path_args; |
625 | 627 | ||
626 | if (as->argc < nr_params) | 628 | if (as->argc < nr_params) { |
629 | ti->error = "not enough path parameters"; | ||
627 | goto bad; | 630 | goto bad; |
631 | } | ||
628 | 632 | ||
629 | path_args.argc = nr_params; | 633 | path_args.argc = nr_params; |
630 | path_args.argv = as->argv; | 634 | path_args.argv = as->argv; |
@@ -867,7 +871,7 @@ static int reinstate_path(struct pgpath *pgpath) | |||
867 | if (pgpath->path.is_active) | 871 | if (pgpath->path.is_active) |
868 | goto out; | 872 | goto out; |
869 | 873 | ||
870 | if (!pgpath->pg->ps.type) { | 874 | if (!pgpath->pg->ps.type->reinstate_path) { |
871 | DMWARN("Reinstate path not supported by path selector %s", | 875 | DMWARN("Reinstate path not supported by path selector %s", |
872 | pgpath->pg->ps.type->name); | 876 | pgpath->pg->ps.type->name); |
873 | r = -EINVAL; | 877 | r = -EINVAL; |
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 */ |
diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h index 24f9fb73b982..292c15609ae3 100644 --- a/drivers/md/dm-snap.h +++ b/drivers/md/dm-snap.h | |||
@@ -130,6 +130,10 @@ struct exception_store { | |||
130 | void *context; | 130 | void *context; |
131 | }; | 131 | }; |
132 | 132 | ||
133 | #define DM_TRACKED_CHUNK_HASH_SIZE 16 | ||
134 | #define DM_TRACKED_CHUNK_HASH(x) ((unsigned long)(x) & \ | ||
135 | (DM_TRACKED_CHUNK_HASH_SIZE - 1)) | ||
136 | |||
133 | struct dm_snapshot { | 137 | struct dm_snapshot { |
134 | struct rw_semaphore lock; | 138 | struct rw_semaphore lock; |
135 | struct dm_target *ti; | 139 | struct dm_target *ti; |
@@ -157,6 +161,8 @@ struct dm_snapshot { | |||
157 | /* The last percentage we notified */ | 161 | /* The last percentage we notified */ |
158 | int last_percent; | 162 | int last_percent; |
159 | 163 | ||
164 | mempool_t *pending_pool; | ||
165 | |||
160 | struct exception_table pending; | 166 | struct exception_table pending; |
161 | struct exception_table complete; | 167 | struct exception_table complete; |
162 | 168 | ||
@@ -174,6 +180,11 @@ struct dm_snapshot { | |||
174 | /* Queue of snapshot writes for ksnapd to flush */ | 180 | /* Queue of snapshot writes for ksnapd to flush */ |
175 | struct bio_list queued_bios; | 181 | struct bio_list queued_bios; |
176 | struct work_struct queued_bios_work; | 182 | struct work_struct queued_bios_work; |
183 | |||
184 | /* Chunks with outstanding reads */ | ||
185 | mempool_t *tracked_chunk_pool; | ||
186 | spinlock_t tracked_chunk_lock; | ||
187 | struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE]; | ||
177 | }; | 188 | }; |
178 | 189 | ||
179 | /* | 190 | /* |
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 94116eaf4709..798e468103b8 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c | |||
@@ -506,14 +506,13 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev) | |||
506 | rs->max_sectors = | 506 | rs->max_sectors = |
507 | min_not_zero(rs->max_sectors, q->max_sectors); | 507 | min_not_zero(rs->max_sectors, q->max_sectors); |
508 | 508 | ||
509 | /* FIXME: Device-Mapper on top of RAID-0 breaks because DM | 509 | /* |
510 | * currently doesn't honor MD's merge_bvec_fn routine. | 510 | * Check if merge fn is supported. |
511 | * In this case, we'll force DM to use PAGE_SIZE or | 511 | * If not we'll force DM to use PAGE_SIZE or |
512 | * smaller I/O, just to be safe. A better fix is in the | 512 | * smaller I/O, just to be safe. |
513 | * works, but add this for the time being so it will at | ||
514 | * least operate correctly. | ||
515 | */ | 513 | */ |
516 | if (q->merge_bvec_fn) | 514 | |
515 | if (q->merge_bvec_fn && !ti->type->merge) | ||
517 | rs->max_sectors = | 516 | rs->max_sectors = |
518 | min_not_zero(rs->max_sectors, | 517 | min_not_zero(rs->max_sectors, |
519 | (unsigned int) (PAGE_SIZE >> 9)); | 518 | (unsigned int) (PAGE_SIZE >> 9)); |
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 372369b1cc20..bca448e11878 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -37,8 +37,8 @@ static DEFINE_SPINLOCK(_minor_lock); | |||
37 | struct dm_io { | 37 | struct dm_io { |
38 | struct mapped_device *md; | 38 | struct mapped_device *md; |
39 | int error; | 39 | int error; |
40 | struct bio *bio; | ||
41 | atomic_t io_count; | 40 | atomic_t io_count; |
41 | struct bio *bio; | ||
42 | unsigned long start_time; | 42 | unsigned long start_time; |
43 | }; | 43 | }; |
44 | 44 | ||
@@ -829,6 +829,49 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) | |||
829 | * CRUD END | 829 | * CRUD END |
830 | *---------------------------------------------------------------*/ | 830 | *---------------------------------------------------------------*/ |
831 | 831 | ||
832 | static int dm_merge_bvec(struct request_queue *q, | ||
833 | struct bvec_merge_data *bvm, | ||
834 | struct bio_vec *biovec) | ||
835 | { | ||
836 | struct mapped_device *md = q->queuedata; | ||
837 | struct dm_table *map = dm_get_table(md); | ||
838 | struct dm_target *ti; | ||
839 | sector_t max_sectors; | ||
840 | int max_size; | ||
841 | |||
842 | if (unlikely(!map)) | ||
843 | return 0; | ||
844 | |||
845 | ti = dm_table_find_target(map, bvm->bi_sector); | ||
846 | |||
847 | /* | ||
848 | * Find maximum amount of I/O that won't need splitting | ||
849 | */ | ||
850 | max_sectors = min(max_io_len(md, bvm->bi_sector, ti), | ||
851 | (sector_t) BIO_MAX_SECTORS); | ||
852 | max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; | ||
853 | if (max_size < 0) | ||
854 | max_size = 0; | ||
855 | |||
856 | /* | ||
857 | * merge_bvec_fn() returns number of bytes | ||
858 | * it can accept at this offset | ||
859 | * max is precomputed maximal io size | ||
860 | */ | ||
861 | if (max_size && ti->type->merge) | ||
862 | max_size = ti->type->merge(ti, bvm, biovec, max_size); | ||
863 | |||
864 | /* | ||
865 | * Always allow an entire first page | ||
866 | */ | ||
867 | if (max_size <= biovec->bv_len && !(bvm->bi_size >> SECTOR_SHIFT)) | ||
868 | max_size = biovec->bv_len; | ||
869 | |||
870 | dm_table_put(map); | ||
871 | |||
872 | return max_size; | ||
873 | } | ||
874 | |||
832 | /* | 875 | /* |
833 | * The request function that just remaps the bio built up by | 876 | * The request function that just remaps the bio built up by |
834 | * dm_merge_bvec. | 877 | * dm_merge_bvec. |
@@ -1032,6 +1075,7 @@ static struct mapped_device *alloc_dev(int minor) | |||
1032 | blk_queue_make_request(md->queue, dm_request); | 1075 | blk_queue_make_request(md->queue, dm_request); |
1033 | blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); | 1076 | blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); |
1034 | md->queue->unplug_fn = dm_unplug_all; | 1077 | md->queue->unplug_fn = dm_unplug_all; |
1078 | blk_queue_merge_bvec(md->queue, dm_merge_bvec); | ||
1035 | 1079 | ||
1036 | md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); | 1080 | md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); |
1037 | if (!md->io_pool) | 1081 | if (!md->io_pool) |
diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 8c03b634e62e..1e59a0b0a78a 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h | |||
@@ -100,12 +100,6 @@ int dm_lock_for_deletion(struct mapped_device *md); | |||
100 | 100 | ||
101 | void dm_kobject_uevent(struct mapped_device *md); | 101 | void dm_kobject_uevent(struct mapped_device *md); |
102 | 102 | ||
103 | /* | ||
104 | * Dirty log | ||
105 | */ | ||
106 | int dm_dirty_log_init(void); | ||
107 | void dm_dirty_log_exit(void); | ||
108 | |||
109 | int dm_kcopyd_init(void); | 103 | int dm_kcopyd_init(void); |
110 | void dm_kcopyd_exit(void); | 104 | void dm_kcopyd_exit(void); |
111 | 105 | ||
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 0d8d419d191a..a90222e3297d 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h | |||
@@ -9,11 +9,13 @@ | |||
9 | #define _LINUX_DEVICE_MAPPER_H | 9 | #define _LINUX_DEVICE_MAPPER_H |
10 | 10 | ||
11 | #include <linux/bio.h> | 11 | #include <linux/bio.h> |
12 | #include <linux/blkdev.h> | ||
12 | 13 | ||
13 | struct dm_target; | 14 | struct dm_target; |
14 | struct dm_table; | 15 | struct dm_table; |
15 | struct dm_dev; | 16 | struct dm_dev; |
16 | struct mapped_device; | 17 | struct mapped_device; |
18 | struct bio_vec; | ||
17 | 19 | ||
18 | typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; | 20 | typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; |
19 | 21 | ||
@@ -72,6 +74,9 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, struct inode *inode, | |||
72 | struct file *filp, unsigned int cmd, | 74 | struct file *filp, unsigned int cmd, |
73 | unsigned long arg); | 75 | unsigned long arg); |
74 | 76 | ||
77 | typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm, | ||
78 | struct bio_vec *biovec, int max_size); | ||
79 | |||
75 | void dm_error(const char *message); | 80 | void dm_error(const char *message); |
76 | 81 | ||
77 | /* | 82 | /* |
@@ -107,6 +112,7 @@ struct target_type { | |||
107 | dm_status_fn status; | 112 | dm_status_fn status; |
108 | dm_message_fn message; | 113 | dm_message_fn message; |
109 | dm_ioctl_fn ioctl; | 114 | dm_ioctl_fn ioctl; |
115 | dm_merge_fn merge; | ||
110 | }; | 116 | }; |
111 | 117 | ||
112 | struct io_restrictions { | 118 | struct io_restrictions { |
diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index b03c41bbfa14..28c2940eb30d 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h | |||
@@ -256,9 +256,9 @@ enum { | |||
256 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) | 256 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) |
257 | 257 | ||
258 | #define DM_VERSION_MAJOR 4 | 258 | #define DM_VERSION_MAJOR 4 |
259 | #define DM_VERSION_MINOR 13 | 259 | #define DM_VERSION_MINOR 14 |
260 | #define DM_VERSION_PATCHLEVEL 0 | 260 | #define DM_VERSION_PATCHLEVEL 0 |
261 | #define DM_VERSION_EXTRA "-ioctl (2007-10-18)" | 261 | #define DM_VERSION_EXTRA "-ioctl (2008-04-23)" |
262 | 262 | ||
263 | /* Status bits */ | 263 | /* Status bits */ |
264 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ | 264 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ |