diff options
author | Jonathan Brassow <jbrassow@redhat.com> | 2009-04-02 14:55:34 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2009-04-02 14:55:34 -0400 |
commit | fee1998e9c690f9920671e1e0ef187a48cfbbde4 (patch) | |
tree | a217e9bc2142350d9e8065c6e581be0524636498 /drivers/md | |
parent | 2e4a31df2b10cbcaf43c333112f6f7440a035c69 (diff) |
dm snapshot: move ctr parsing to exception store
First step of having the exception stores parse their own arguments -
generalizing the interface.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-exception-store.c | 109 | ||||
-rw-r--r-- | drivers/md/dm-exception-store.h | 5 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 147 |
3 files changed, 132 insertions, 129 deletions
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 2078b92470a1..a2e26c242141 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #include "dm-exception-store.h" | 8 | #include "dm-exception-store.h" |
9 | 9 | ||
10 | #include <linux/ctype.h> | ||
10 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
11 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
12 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
@@ -137,49 +138,129 @@ int dm_exception_store_type_unregister(struct dm_exception_store_type *type) | |||
137 | } | 138 | } |
138 | EXPORT_SYMBOL(dm_exception_store_type_unregister); | 139 | EXPORT_SYMBOL(dm_exception_store_type_unregister); |
139 | 140 | ||
140 | int dm_exception_store_create(const char *type_name, struct dm_target *ti, | 141 | /* |
141 | chunk_t chunk_size, chunk_t chunk_mask, | 142 | * Round a number up to the nearest 'size' boundary. size must |
142 | chunk_t chunk_shift, struct dm_dev *cow, | 143 | * be a power of 2. |
144 | */ | ||
145 | static ulong round_up(ulong n, ulong size) | ||
146 | { | ||
147 | size--; | ||
148 | return (n + size) & ~size; | ||
149 | } | ||
150 | |||
151 | static int set_chunk_size(struct dm_exception_store *store, | ||
152 | const char *chunk_size_arg, char **error) | ||
153 | { | ||
154 | unsigned long chunk_size_ulong; | ||
155 | char *value; | ||
156 | |||
157 | chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10); | ||
158 | if (*chunk_size_arg == '\0' || *value != '\0') { | ||
159 | *error = "Invalid chunk size"; | ||
160 | return -EINVAL; | ||
161 | } | ||
162 | |||
163 | if (!chunk_size_ulong) { | ||
164 | store->chunk_size = store->chunk_mask = store->chunk_shift = 0; | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Chunk size must be multiple of page size. Silently | ||
170 | * round up if it's not. | ||
171 | */ | ||
172 | chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9); | ||
173 | |||
174 | /* Check chunk_size is a power of 2 */ | ||
175 | if (!is_power_of_2(chunk_size_ulong)) { | ||
176 | *error = "Chunk size is not a power of 2"; | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | /* Validate the chunk size against the device block size */ | ||
181 | if (chunk_size_ulong % (bdev_hardsect_size(store->cow->bdev) >> 9)) { | ||
182 | *error = "Chunk size is not a multiple of device blocksize"; | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | store->chunk_size = chunk_size_ulong; | ||
187 | store->chunk_mask = chunk_size_ulong - 1; | ||
188 | store->chunk_shift = ffs(chunk_size_ulong) - 1; | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, | ||
194 | unsigned *args_used, | ||
143 | struct dm_exception_store **store) | 195 | struct dm_exception_store **store) |
144 | { | 196 | { |
145 | int r = 0; | 197 | int r = 0; |
146 | struct dm_exception_store_type *type; | 198 | struct dm_exception_store_type *type; |
147 | struct dm_exception_store *tmp_store; | 199 | struct dm_exception_store *tmp_store; |
200 | char persistent; | ||
201 | |||
202 | if (argc < 3) { | ||
203 | ti->error = "Insufficient exception store arguments"; | ||
204 | return -EINVAL; | ||
205 | } | ||
148 | 206 | ||
149 | tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL); | 207 | tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL); |
150 | if (!tmp_store) | 208 | if (!tmp_store) { |
209 | ti->error = "Exception store allocation failed"; | ||
151 | return -ENOMEM; | 210 | return -ENOMEM; |
211 | } | ||
152 | 212 | ||
153 | type = get_type(type_name); | 213 | persistent = toupper(*argv[1]); |
154 | if (!type) { | 214 | if (persistent != 'P' && persistent != 'N') { |
155 | kfree(tmp_store); | 215 | ti->error = "Persistent flag is not P or N"; |
156 | return -EINVAL; | 216 | return -EINVAL; |
157 | } | 217 | } |
158 | 218 | ||
219 | type = get_type(argv[1]); | ||
220 | if (!type) { | ||
221 | ti->error = "Exception store type not recognised"; | ||
222 | r = -EINVAL; | ||
223 | goto bad_type; | ||
224 | } | ||
225 | |||
159 | tmp_store->type = type; | 226 | tmp_store->type = type; |
160 | tmp_store->ti = ti; | 227 | tmp_store->ti = ti; |
161 | 228 | ||
162 | tmp_store->chunk_size = chunk_size; | 229 | r = dm_get_device(ti, argv[0], 0, 0, |
163 | tmp_store->chunk_mask = chunk_mask; | 230 | FMODE_READ | FMODE_WRITE, &tmp_store->cow); |
164 | tmp_store->chunk_shift = chunk_shift; | 231 | if (r) { |
232 | ti->error = "Cannot get COW device"; | ||
233 | goto bad_cow; | ||
234 | } | ||
165 | 235 | ||
166 | tmp_store->cow = cow; | 236 | r = set_chunk_size(tmp_store, argv[2], &ti->error); |
237 | if (r) | ||
238 | goto bad_cow; | ||
167 | 239 | ||
168 | r = type->ctr(tmp_store, 0, NULL); | 240 | r = type->ctr(tmp_store, 0, NULL); |
169 | if (r) { | 241 | if (r) { |
170 | put_type(type); | 242 | ti->error = "Exception store type constructor failed"; |
171 | kfree(tmp_store); | 243 | goto bad_ctr; |
172 | return r; | ||
173 | } | 244 | } |
174 | 245 | ||
246 | *args_used = 3; | ||
175 | *store = tmp_store; | 247 | *store = tmp_store; |
176 | return 0; | 248 | return 0; |
249 | |||
250 | bad_ctr: | ||
251 | dm_put_device(ti, tmp_store->cow); | ||
252 | bad_cow: | ||
253 | put_type(type); | ||
254 | bad_type: | ||
255 | kfree(tmp_store); | ||
256 | return r; | ||
177 | } | 257 | } |
178 | EXPORT_SYMBOL(dm_exception_store_create); | 258 | EXPORT_SYMBOL(dm_exception_store_create); |
179 | 259 | ||
180 | void dm_exception_store_destroy(struct dm_exception_store *store) | 260 | void dm_exception_store_destroy(struct dm_exception_store *store) |
181 | { | 261 | { |
182 | store->type->dtr(store); | 262 | store->type->dtr(store); |
263 | dm_put_device(store->ti, store->cow); | ||
183 | put_type(store->type); | 264 | put_type(store->type); |
184 | kfree(store); | 265 | kfree(store); |
185 | } | 266 | } |
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h index 835f402d9470..023a707503c4 100644 --- a/drivers/md/dm-exception-store.h +++ b/drivers/md/dm-exception-store.h | |||
@@ -167,9 +167,8 @@ static inline chunk_t sector_to_chunk(struct dm_exception_store *store, | |||
167 | int dm_exception_store_type_register(struct dm_exception_store_type *type); | 167 | int dm_exception_store_type_register(struct dm_exception_store_type *type); |
168 | int dm_exception_store_type_unregister(struct dm_exception_store_type *type); | 168 | int dm_exception_store_type_unregister(struct dm_exception_store_type *type); |
169 | 169 | ||
170 | int dm_exception_store_create(const char *type_name, struct dm_target *ti, | 170 | int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, |
171 | chunk_t chunk_size, chunk_t chunk_mask, | 171 | unsigned *args_used, |
172 | chunk_t chunk_shift, struct dm_dev *cow, | ||
173 | struct dm_exception_store **store); | 172 | struct dm_exception_store **store); |
174 | void dm_exception_store_destroy(struct dm_exception_store *store); | 173 | void dm_exception_store_destroy(struct dm_exception_store *store); |
175 | 174 | ||
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index fcb1ac12119f..974916b9ea21 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -7,7 +7,6 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/blkdev.h> | 9 | #include <linux/blkdev.h> |
10 | #include <linux/ctype.h> | ||
11 | #include <linux/device-mapper.h> | 10 | #include <linux/device-mapper.h> |
12 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
13 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
@@ -538,8 +537,7 @@ static int calc_max_buckets(void) | |||
538 | /* | 537 | /* |
539 | * Allocate room for a suitable hash table. | 538 | * Allocate room for a suitable hash table. |
540 | */ | 539 | */ |
541 | static int init_hash_tables(struct dm_snapshot *s, chunk_t chunk_shift, | 540 | static int init_hash_tables(struct dm_snapshot *s) |
542 | struct dm_dev *cow) | ||
543 | { | 541 | { |
544 | sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets; | 542 | sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets; |
545 | 543 | ||
@@ -547,11 +545,11 @@ static int init_hash_tables(struct dm_snapshot *s, chunk_t chunk_shift, | |||
547 | * Calculate based on the size of the original volume or | 545 | * Calculate based on the size of the original volume or |
548 | * the COW volume... | 546 | * the COW volume... |
549 | */ | 547 | */ |
550 | cow_dev_size = get_dev_size(cow->bdev); | 548 | cow_dev_size = get_dev_size(s->store->cow->bdev); |
551 | origin_dev_size = get_dev_size(s->origin->bdev); | 549 | origin_dev_size = get_dev_size(s->origin->bdev); |
552 | max_buckets = calc_max_buckets(); | 550 | max_buckets = calc_max_buckets(); |
553 | 551 | ||
554 | hash_size = min(origin_dev_size, cow_dev_size) >> chunk_shift; | 552 | hash_size = min(origin_dev_size, cow_dev_size) >> s->store->chunk_shift; |
555 | hash_size = min(hash_size, max_buckets); | 553 | hash_size = min(hash_size, max_buckets); |
556 | 554 | ||
557 | hash_size = rounddown_pow_of_two(hash_size); | 555 | hash_size = rounddown_pow_of_two(hash_size); |
@@ -576,60 +574,6 @@ static int init_hash_tables(struct dm_snapshot *s, chunk_t chunk_shift, | |||
576 | } | 574 | } |
577 | 575 | ||
578 | /* | 576 | /* |
579 | * Round a number up to the nearest 'size' boundary. size must | ||
580 | * be a power of 2. | ||
581 | */ | ||
582 | static ulong round_up(ulong n, ulong size) | ||
583 | { | ||
584 | size--; | ||
585 | return (n + size) & ~size; | ||
586 | } | ||
587 | |||
588 | static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg, | ||
589 | chunk_t *chunk_size, chunk_t *chunk_mask, | ||
590 | chunk_t *chunk_shift, struct dm_dev *cow, | ||
591 | char **error) | ||
592 | { | ||
593 | unsigned long chunk_size_ulong; | ||
594 | char *value; | ||
595 | |||
596 | chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10); | ||
597 | if (*chunk_size_arg == '\0' || *value != '\0') { | ||
598 | *error = "Invalid chunk size"; | ||
599 | return -EINVAL; | ||
600 | } | ||
601 | |||
602 | if (!chunk_size_ulong) { | ||
603 | *chunk_size = *chunk_mask = *chunk_shift = 0; | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * Chunk size must be multiple of page size. Silently | ||
609 | * round up if it's not. | ||
610 | */ | ||
611 | chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9); | ||
612 | |||
613 | /* Check chunk_size is a power of 2 */ | ||
614 | if (!is_power_of_2(chunk_size_ulong)) { | ||
615 | *error = "Chunk size is not a power of 2"; | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | |||
619 | /* Validate the chunk size against the device block size */ | ||
620 | if (chunk_size_ulong % (bdev_hardsect_size(cow->bdev) >> 9)) { | ||
621 | *error = "Chunk size is not a multiple of device blocksize"; | ||
622 | return -EINVAL; | ||
623 | } | ||
624 | |||
625 | *chunk_size = chunk_size_ulong; | ||
626 | *chunk_mask = chunk_size_ulong - 1; | ||
627 | *chunk_shift = ffs(chunk_size_ulong) - 1; | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size> | 577 | * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size> |
634 | */ | 578 | */ |
635 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 579 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
@@ -637,55 +581,45 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
637 | struct dm_snapshot *s; | 581 | struct dm_snapshot *s; |
638 | int i; | 582 | int i; |
639 | int r = -EINVAL; | 583 | int r = -EINVAL; |
640 | char persistent; | ||
641 | char *origin_path; | 584 | char *origin_path; |
642 | char *cow_path; | 585 | struct dm_exception_store *store; |
643 | chunk_t chunk_size, chunk_mask, chunk_shift; | 586 | unsigned args_used; |
644 | struct dm_dev *cow; | ||
645 | 587 | ||
646 | if (argc != 4) { | 588 | if (argc != 4) { |
647 | ti->error = "requires exactly 4 arguments"; | 589 | ti->error = "requires exactly 4 arguments"; |
648 | r = -EINVAL; | 590 | r = -EINVAL; |
649 | goto bad1; | 591 | goto bad_args; |
650 | } | 592 | } |
651 | 593 | ||
652 | origin_path = argv[0]; | 594 | origin_path = argv[0]; |
653 | cow_path = argv[1]; | 595 | argv++; |
654 | persistent = toupper(*argv[2]); | 596 | argc--; |
655 | 597 | ||
656 | if (persistent != 'P' && persistent != 'N') { | 598 | r = dm_exception_store_create(ti, argc, argv, &args_used, &store); |
657 | ti->error = "Persistent flag is not P or N"; | 599 | if (r) { |
600 | ti->error = "Couldn't create exception store"; | ||
658 | r = -EINVAL; | 601 | r = -EINVAL; |
659 | goto bad1; | 602 | goto bad_args; |
660 | } | 603 | } |
661 | 604 | ||
605 | argv += args_used; | ||
606 | argc -= args_used; | ||
607 | |||
662 | s = kmalloc(sizeof(*s), GFP_KERNEL); | 608 | s = kmalloc(sizeof(*s), GFP_KERNEL); |
663 | if (s == NULL) { | 609 | if (!s) { |
664 | ti->error = "Cannot allocate snapshot context private " | 610 | ti->error = "Cannot allocate snapshot context private " |
665 | "structure"; | 611 | "structure"; |
666 | r = -ENOMEM; | 612 | r = -ENOMEM; |
667 | goto bad1; | 613 | goto bad_snap; |
668 | } | 614 | } |
669 | 615 | ||
670 | r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin); | 616 | r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin); |
671 | if (r) { | 617 | if (r) { |
672 | ti->error = "Cannot get origin device"; | 618 | ti->error = "Cannot get origin device"; |
673 | goto bad2; | 619 | goto bad_origin; |
674 | } | 620 | } |
675 | 621 | ||
676 | r = dm_get_device(ti, cow_path, 0, 0, | 622 | s->store = store; |
677 | FMODE_READ | FMODE_WRITE, &cow); | ||
678 | if (r) { | ||
679 | dm_put_device(ti, s->origin); | ||
680 | ti->error = "Cannot get COW device"; | ||
681 | goto bad2; | ||
682 | } | ||
683 | |||
684 | r = set_chunk_size(s, argv[3], &chunk_size, &chunk_mask, &chunk_shift, | ||
685 | cow, &ti->error); | ||
686 | if (r) | ||
687 | goto bad3; | ||
688 | |||
689 | s->valid = 1; | 623 | s->valid = 1; |
690 | s->active = 0; | 624 | s->active = 0; |
691 | atomic_set(&s->pending_exceptions_count, 0); | 625 | atomic_set(&s->pending_exceptions_count, 0); |
@@ -693,30 +627,22 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
693 | spin_lock_init(&s->pe_lock); | 627 | spin_lock_init(&s->pe_lock); |
694 | 628 | ||
695 | /* Allocate hash table for COW data */ | 629 | /* Allocate hash table for COW data */ |
696 | if (init_hash_tables(s, chunk_shift, cow)) { | 630 | if (init_hash_tables(s)) { |
697 | ti->error = "Unable to allocate hash table space"; | 631 | ti->error = "Unable to allocate hash table space"; |
698 | r = -ENOMEM; | 632 | r = -ENOMEM; |
699 | goto bad3; | 633 | goto bad_hash_tables; |
700 | } | ||
701 | |||
702 | r = dm_exception_store_create(argv[2], ti, chunk_size, chunk_mask, | ||
703 | chunk_shift, cow, &s->store); | ||
704 | if (r) { | ||
705 | ti->error = "Couldn't create exception store"; | ||
706 | r = -EINVAL; | ||
707 | goto bad4; | ||
708 | } | 634 | } |
709 | 635 | ||
710 | r = dm_kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client); | 636 | r = dm_kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client); |
711 | if (r) { | 637 | if (r) { |
712 | ti->error = "Could not create kcopyd client"; | 638 | ti->error = "Could not create kcopyd client"; |
713 | goto bad5; | 639 | goto bad_kcopyd; |
714 | } | 640 | } |
715 | 641 | ||
716 | s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache); | 642 | s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache); |
717 | if (!s->pending_pool) { | 643 | if (!s->pending_pool) { |
718 | ti->error = "Could not allocate mempool for pending exceptions"; | 644 | ti->error = "Could not allocate mempool for pending exceptions"; |
719 | goto bad6; | 645 | goto bad_pending_pool; |
720 | } | 646 | } |
721 | 647 | ||
722 | s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS, | 648 | s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS, |
@@ -759,30 +685,29 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
759 | 685 | ||
760 | return 0; | 686 | return 0; |
761 | 687 | ||
762 | bad_load_and_register: | 688 | bad_load_and_register: |
763 | mempool_destroy(s->tracked_chunk_pool); | 689 | mempool_destroy(s->tracked_chunk_pool); |
764 | 690 | ||
765 | bad_tracked_chunk_pool: | 691 | bad_tracked_chunk_pool: |
766 | mempool_destroy(s->pending_pool); | 692 | mempool_destroy(s->pending_pool); |
767 | 693 | ||
768 | bad6: | 694 | bad_pending_pool: |
769 | dm_kcopyd_client_destroy(s->kcopyd_client); | 695 | dm_kcopyd_client_destroy(s->kcopyd_client); |
770 | 696 | ||
771 | bad5: | 697 | bad_kcopyd: |
772 | s->store->type->dtr(s->store); | ||
773 | |||
774 | bad4: | ||
775 | exit_exception_table(&s->pending, pending_cache); | 698 | exit_exception_table(&s->pending, pending_cache); |
776 | exit_exception_table(&s->complete, exception_cache); | 699 | exit_exception_table(&s->complete, exception_cache); |
777 | 700 | ||
778 | bad3: | 701 | bad_hash_tables: |
779 | dm_put_device(ti, cow); | ||
780 | dm_put_device(ti, s->origin); | 702 | dm_put_device(ti, s->origin); |
781 | 703 | ||
782 | bad2: | 704 | bad_origin: |
783 | kfree(s); | 705 | kfree(s); |
784 | 706 | ||
785 | bad1: | 707 | bad_snap: |
708 | dm_exception_store_destroy(store); | ||
709 | |||
710 | bad_args: | ||
786 | return r; | 711 | return r; |
787 | } | 712 | } |
788 | 713 | ||
@@ -793,8 +718,6 @@ static void __free_exceptions(struct dm_snapshot *s) | |||
793 | 718 | ||
794 | exit_exception_table(&s->pending, pending_cache); | 719 | exit_exception_table(&s->pending, pending_cache); |
795 | exit_exception_table(&s->complete, exception_cache); | 720 | exit_exception_table(&s->complete, exception_cache); |
796 | |||
797 | s->store->type->dtr(s->store); | ||
798 | } | 721 | } |
799 | 722 | ||
800 | static void snapshot_dtr(struct dm_target *ti) | 723 | static void snapshot_dtr(struct dm_target *ti) |
@@ -803,7 +726,6 @@ static void snapshot_dtr(struct dm_target *ti) | |||
803 | int i; | 726 | int i; |
804 | #endif | 727 | #endif |
805 | struct dm_snapshot *s = ti->private; | 728 | struct dm_snapshot *s = ti->private; |
806 | struct dm_dev *cow = s->store->cow; | ||
807 | 729 | ||
808 | flush_workqueue(ksnapd); | 730 | flush_workqueue(ksnapd); |
809 | 731 | ||
@@ -831,7 +753,8 @@ static void snapshot_dtr(struct dm_target *ti) | |||
831 | mempool_destroy(s->pending_pool); | 753 | mempool_destroy(s->pending_pool); |
832 | 754 | ||
833 | dm_put_device(ti, s->origin); | 755 | dm_put_device(ti, s->origin); |
834 | dm_put_device(ti, cow); | 756 | |
757 | dm_exception_store_destroy(s->store); | ||
835 | 758 | ||
836 | kfree(s); | 759 | kfree(s); |
837 | } | 760 | } |