diff options
Diffstat (limited to 'drivers/md/dm-snap-persistent.c')
-rw-r--r-- | drivers/md/dm-snap-persistent.c | 195 |
1 files changed, 156 insertions, 39 deletions
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 0c746420c008..7d08879689ac 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c | |||
@@ -55,6 +55,8 @@ | |||
55 | */ | 55 | */ |
56 | #define SNAPSHOT_DISK_VERSION 1 | 56 | #define SNAPSHOT_DISK_VERSION 1 |
57 | 57 | ||
58 | #define NUM_SNAPSHOT_HDR_CHUNKS 1 | ||
59 | |||
58 | struct disk_header { | 60 | struct disk_header { |
59 | uint32_t magic; | 61 | uint32_t magic; |
60 | 62 | ||
@@ -120,7 +122,22 @@ struct pstore { | |||
120 | 122 | ||
121 | /* | 123 | /* |
122 | * The next free chunk for an exception. | 124 | * The next free chunk for an exception. |
125 | * | ||
126 | * When creating exceptions, all the chunks here and above are | ||
127 | * free. It holds the next chunk to be allocated. On rare | ||
128 | * occasions (e.g. after a system crash) holes can be left in | ||
129 | * the exception store because chunks can be committed out of | ||
130 | * order. | ||
131 | * | ||
132 | * When merging exceptions, it does not necessarily mean all the | ||
133 | * chunks here and above are free. It holds the value it would | ||
134 | * have held if all chunks had been committed in order of | ||
135 | * allocation. Consequently the value may occasionally be | ||
136 | * slightly too low, but since it's only used for 'status' and | ||
137 | * it can never reach its minimum value too early this doesn't | ||
138 | * matter. | ||
123 | */ | 139 | */ |
140 | |||
124 | chunk_t next_free; | 141 | chunk_t next_free; |
125 | 142 | ||
126 | /* | 143 | /* |
@@ -214,7 +231,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw, | |||
214 | int metadata) | 231 | int metadata) |
215 | { | 232 | { |
216 | struct dm_io_region where = { | 233 | struct dm_io_region where = { |
217 | .bdev = ps->store->cow->bdev, | 234 | .bdev = dm_snap_cow(ps->store->snap)->bdev, |
218 | .sector = ps->store->chunk_size * chunk, | 235 | .sector = ps->store->chunk_size * chunk, |
219 | .count = ps->store->chunk_size, | 236 | .count = ps->store->chunk_size, |
220 | }; | 237 | }; |
@@ -294,7 +311,8 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
294 | */ | 311 | */ |
295 | if (!ps->store->chunk_size) { | 312 | if (!ps->store->chunk_size) { |
296 | ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, | 313 | ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, |
297 | bdev_logical_block_size(ps->store->cow->bdev) >> 9); | 314 | bdev_logical_block_size(dm_snap_cow(ps->store->snap)-> |
315 | bdev) >> 9); | ||
298 | ps->store->chunk_mask = ps->store->chunk_size - 1; | 316 | ps->store->chunk_mask = ps->store->chunk_size - 1; |
299 | ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1; | 317 | ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1; |
300 | chunk_size_supplied = 0; | 318 | chunk_size_supplied = 0; |
@@ -408,6 +426,15 @@ static void write_exception(struct pstore *ps, | |||
408 | e->new_chunk = cpu_to_le64(de->new_chunk); | 426 | e->new_chunk = cpu_to_le64(de->new_chunk); |
409 | } | 427 | } |
410 | 428 | ||
429 | static void clear_exception(struct pstore *ps, uint32_t index) | ||
430 | { | ||
431 | struct disk_exception *e = get_exception(ps, index); | ||
432 | |||
433 | /* clear it */ | ||
434 | e->old_chunk = 0; | ||
435 | e->new_chunk = 0; | ||
436 | } | ||
437 | |||
411 | /* | 438 | /* |
412 | * Registers the exceptions that are present in the current area. | 439 | * Registers the exceptions that are present in the current area. |
413 | * 'full' is filled in to indicate if the area has been | 440 | * 'full' is filled in to indicate if the area has been |
@@ -489,11 +516,23 @@ static struct pstore *get_info(struct dm_exception_store *store) | |||
489 | return (struct pstore *) store->context; | 516 | return (struct pstore *) store->context; |
490 | } | 517 | } |
491 | 518 | ||
492 | static void persistent_fraction_full(struct dm_exception_store *store, | 519 | static void persistent_usage(struct dm_exception_store *store, |
493 | sector_t *numerator, sector_t *denominator) | 520 | sector_t *total_sectors, |
521 | sector_t *sectors_allocated, | ||
522 | sector_t *metadata_sectors) | ||
494 | { | 523 | { |
495 | *numerator = get_info(store)->next_free * store->chunk_size; | 524 | struct pstore *ps = get_info(store); |
496 | *denominator = get_dev_size(store->cow->bdev); | 525 | |
526 | *sectors_allocated = ps->next_free * store->chunk_size; | ||
527 | *total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev); | ||
528 | |||
529 | /* | ||
530 | * First chunk is the fixed header. | ||
531 | * Then there are (ps->current_area + 1) metadata chunks, each one | ||
532 | * separated from the next by ps->exceptions_per_area data chunks. | ||
533 | */ | ||
534 | *metadata_sectors = (ps->current_area + 1 + NUM_SNAPSHOT_HDR_CHUNKS) * | ||
535 | store->chunk_size; | ||
497 | } | 536 | } |
498 | 537 | ||
499 | static void persistent_dtr(struct dm_exception_store *store) | 538 | static void persistent_dtr(struct dm_exception_store *store) |
@@ -552,44 +591,40 @@ static int persistent_read_metadata(struct dm_exception_store *store, | |||
552 | ps->current_area = 0; | 591 | ps->current_area = 0; |
553 | zero_memory_area(ps); | 592 | zero_memory_area(ps); |
554 | r = zero_disk_area(ps, 0); | 593 | r = zero_disk_area(ps, 0); |
555 | if (r) { | 594 | if (r) |
556 | DMWARN("zero_disk_area(0) failed"); | 595 | DMWARN("zero_disk_area(0) failed"); |
557 | return r; | 596 | return r; |
558 | } | 597 | } |
559 | } else { | 598 | /* |
560 | /* | 599 | * Sanity checks. |
561 | * Sanity checks. | 600 | */ |
562 | */ | 601 | if (ps->version != SNAPSHOT_DISK_VERSION) { |
563 | if (ps->version != SNAPSHOT_DISK_VERSION) { | 602 | DMWARN("unable to handle snapshot disk version %d", |
564 | DMWARN("unable to handle snapshot disk version %d", | 603 | ps->version); |
565 | ps->version); | 604 | return -EINVAL; |
566 | return -EINVAL; | 605 | } |
567 | } | ||
568 | 606 | ||
569 | /* | 607 | /* |
570 | * Metadata are valid, but snapshot is invalidated | 608 | * Metadata are valid, but snapshot is invalidated |
571 | */ | 609 | */ |
572 | if (!ps->valid) | 610 | if (!ps->valid) |
573 | return 1; | 611 | return 1; |
574 | 612 | ||
575 | /* | 613 | /* |
576 | * Read the metadata. | 614 | * Read the metadata. |
577 | */ | 615 | */ |
578 | r = read_exceptions(ps, callback, callback_context); | 616 | r = read_exceptions(ps, callback, callback_context); |
579 | if (r) | ||
580 | return r; | ||
581 | } | ||
582 | 617 | ||
583 | return 0; | 618 | return r; |
584 | } | 619 | } |
585 | 620 | ||
586 | static int persistent_prepare_exception(struct dm_exception_store *store, | 621 | static int persistent_prepare_exception(struct dm_exception_store *store, |
587 | struct dm_snap_exception *e) | 622 | struct dm_exception *e) |
588 | { | 623 | { |
589 | struct pstore *ps = get_info(store); | 624 | struct pstore *ps = get_info(store); |
590 | uint32_t stride; | 625 | uint32_t stride; |
591 | chunk_t next_free; | 626 | chunk_t next_free; |
592 | sector_t size = get_dev_size(store->cow->bdev); | 627 | sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev); |
593 | 628 | ||
594 | /* Is there enough room ? */ | 629 | /* Is there enough room ? */ |
595 | if (size < ((ps->next_free + 1) * store->chunk_size)) | 630 | if (size < ((ps->next_free + 1) * store->chunk_size)) |
@@ -611,7 +646,7 @@ static int persistent_prepare_exception(struct dm_exception_store *store, | |||
611 | } | 646 | } |
612 | 647 | ||
613 | static void persistent_commit_exception(struct dm_exception_store *store, | 648 | static void persistent_commit_exception(struct dm_exception_store *store, |
614 | struct dm_snap_exception *e, | 649 | struct dm_exception *e, |
615 | void (*callback) (void *, int success), | 650 | void (*callback) (void *, int success), |
616 | void *callback_context) | 651 | void *callback_context) |
617 | { | 652 | { |
@@ -672,6 +707,85 @@ static void persistent_commit_exception(struct dm_exception_store *store, | |||
672 | ps->callback_count = 0; | 707 | ps->callback_count = 0; |
673 | } | 708 | } |
674 | 709 | ||
710 | static int persistent_prepare_merge(struct dm_exception_store *store, | ||
711 | chunk_t *last_old_chunk, | ||
712 | chunk_t *last_new_chunk) | ||
713 | { | ||
714 | struct pstore *ps = get_info(store); | ||
715 | struct disk_exception de; | ||
716 | int nr_consecutive; | ||
717 | int r; | ||
718 | |||
719 | /* | ||
720 | * When current area is empty, move back to preceding area. | ||
721 | */ | ||
722 | if (!ps->current_committed) { | ||
723 | /* | ||
724 | * Have we finished? | ||
725 | */ | ||
726 | if (!ps->current_area) | ||
727 | return 0; | ||
728 | |||
729 | ps->current_area--; | ||
730 | r = area_io(ps, READ); | ||
731 | if (r < 0) | ||
732 | return r; | ||
733 | ps->current_committed = ps->exceptions_per_area; | ||
734 | } | ||
735 | |||
736 | read_exception(ps, ps->current_committed - 1, &de); | ||
737 | *last_old_chunk = de.old_chunk; | ||
738 | *last_new_chunk = de.new_chunk; | ||
739 | |||
740 | /* | ||
741 | * Find number of consecutive chunks within the current area, | ||
742 | * working backwards. | ||
743 | */ | ||
744 | for (nr_consecutive = 1; nr_consecutive < ps->current_committed; | ||
745 | nr_consecutive++) { | ||
746 | read_exception(ps, ps->current_committed - 1 - nr_consecutive, | ||
747 | &de); | ||
748 | if (de.old_chunk != *last_old_chunk - nr_consecutive || | ||
749 | de.new_chunk != *last_new_chunk - nr_consecutive) | ||
750 | break; | ||
751 | } | ||
752 | |||
753 | return nr_consecutive; | ||
754 | } | ||
755 | |||
756 | static int persistent_commit_merge(struct dm_exception_store *store, | ||
757 | int nr_merged) | ||
758 | { | ||
759 | int r, i; | ||
760 | struct pstore *ps = get_info(store); | ||
761 | |||
762 | BUG_ON(nr_merged > ps->current_committed); | ||
763 | |||
764 | for (i = 0; i < nr_merged; i++) | ||
765 | clear_exception(ps, ps->current_committed - 1 - i); | ||
766 | |||
767 | r = area_io(ps, WRITE); | ||
768 | if (r < 0) | ||
769 | return r; | ||
770 | |||
771 | ps->current_committed -= nr_merged; | ||
772 | |||
773 | /* | ||
774 | * At this stage, only persistent_usage() uses ps->next_free, so | ||
775 | * we make no attempt to keep ps->next_free strictly accurate | ||
776 | * as exceptions may have been committed out-of-order originally. | ||
777 | * Once a snapshot has become merging, we set it to the value it | ||
778 | * would have held had all the exceptions been committed in order. | ||
779 | * | ||
780 | * ps->current_area does not get reduced by prepare_merge() until | ||
781 | * after commit_merge() has removed the nr_merged previous exceptions. | ||
782 | */ | ||
783 | ps->next_free = (area_location(ps, ps->current_area) - 1) + | ||
784 | (ps->current_committed + 1) + NUM_SNAPSHOT_HDR_CHUNKS; | ||
785 | |||
786 | return 0; | ||
787 | } | ||
788 | |||
675 | static void persistent_drop_snapshot(struct dm_exception_store *store) | 789 | static void persistent_drop_snapshot(struct dm_exception_store *store) |
676 | { | 790 | { |
677 | struct pstore *ps = get_info(store); | 791 | struct pstore *ps = get_info(store); |
@@ -697,7 +811,7 @@ static int persistent_ctr(struct dm_exception_store *store, | |||
697 | ps->area = NULL; | 811 | ps->area = NULL; |
698 | ps->zero_area = NULL; | 812 | ps->zero_area = NULL; |
699 | ps->header_area = NULL; | 813 | ps->header_area = NULL; |
700 | ps->next_free = 2; /* skipping the header and first area */ | 814 | ps->next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1; /* header and 1st area */ |
701 | ps->current_committed = 0; | 815 | ps->current_committed = 0; |
702 | 816 | ||
703 | ps->callback_count = 0; | 817 | ps->callback_count = 0; |
@@ -726,8 +840,7 @@ static unsigned persistent_status(struct dm_exception_store *store, | |||
726 | case STATUSTYPE_INFO: | 840 | case STATUSTYPE_INFO: |
727 | break; | 841 | break; |
728 | case STATUSTYPE_TABLE: | 842 | case STATUSTYPE_TABLE: |
729 | DMEMIT(" %s P %llu", store->cow->name, | 843 | DMEMIT(" P %llu", (unsigned long long)store->chunk_size); |
730 | (unsigned long long)store->chunk_size); | ||
731 | } | 844 | } |
732 | 845 | ||
733 | return sz; | 846 | return sz; |
@@ -741,8 +854,10 @@ static struct dm_exception_store_type _persistent_type = { | |||
741 | .read_metadata = persistent_read_metadata, | 854 | .read_metadata = persistent_read_metadata, |
742 | .prepare_exception = persistent_prepare_exception, | 855 | .prepare_exception = persistent_prepare_exception, |
743 | .commit_exception = persistent_commit_exception, | 856 | .commit_exception = persistent_commit_exception, |
857 | .prepare_merge = persistent_prepare_merge, | ||
858 | .commit_merge = persistent_commit_merge, | ||
744 | .drop_snapshot = persistent_drop_snapshot, | 859 | .drop_snapshot = persistent_drop_snapshot, |
745 | .fraction_full = persistent_fraction_full, | 860 | .usage = persistent_usage, |
746 | .status = persistent_status, | 861 | .status = persistent_status, |
747 | }; | 862 | }; |
748 | 863 | ||
@@ -754,8 +869,10 @@ static struct dm_exception_store_type _persistent_compat_type = { | |||
754 | .read_metadata = persistent_read_metadata, | 869 | .read_metadata = persistent_read_metadata, |
755 | .prepare_exception = persistent_prepare_exception, | 870 | .prepare_exception = persistent_prepare_exception, |
756 | .commit_exception = persistent_commit_exception, | 871 | .commit_exception = persistent_commit_exception, |
872 | .prepare_merge = persistent_prepare_merge, | ||
873 | .commit_merge = persistent_commit_merge, | ||
757 | .drop_snapshot = persistent_drop_snapshot, | 874 | .drop_snapshot = persistent_drop_snapshot, |
758 | .fraction_full = persistent_fraction_full, | 875 | .usage = persistent_usage, |
759 | .status = persistent_status, | 876 | .status = persistent_status, |
760 | }; | 877 | }; |
761 | 878 | ||