diff options
Diffstat (limited to 'drivers/md/dm-snap.c')
-rw-r--r-- | drivers/md/dm-snap.c | 147 |
1 files changed, 35 insertions, 112 deletions
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 | } |