diff options
| -rw-r--r-- | drivers/md/dm-exception-store.h | 17 | ||||
| -rw-r--r-- | drivers/md/dm-snap-persistent.c | 114 |
2 files changed, 129 insertions, 2 deletions
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h index bb8874653de1..c53e08935b42 100644 --- a/drivers/md/dm-exception-store.h +++ b/drivers/md/dm-exception-store.h | |||
| @@ -75,6 +75,23 @@ struct dm_exception_store_type { | |||
| 75 | void *callback_context); | 75 | void *callback_context); |
| 76 | 76 | ||
| 77 | /* | 77 | /* |
| 78 | * Returns 0 if the exception store is empty. | ||
| 79 | * | ||
| 80 | * If there are exceptions still to be merged, sets | ||
| 81 | * *last_old_chunk and *last_new_chunk to the most recent | ||
| 82 | * still-to-be-merged chunk and returns the number of | ||
| 83 | * consecutive previous ones. | ||
| 84 | */ | ||
| 85 | int (*prepare_merge) (struct dm_exception_store *store, | ||
| 86 | chunk_t *last_old_chunk, chunk_t *last_new_chunk); | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Clear the last n exceptions. | ||
| 90 | * nr_merged must be <= the value returned by prepare_merge. | ||
| 91 | */ | ||
| 92 | int (*commit_merge) (struct dm_exception_store *store, int nr_merged); | ||
| 93 | |||
| 94 | /* | ||
| 78 | * The snapshot is invalid, note this in the metadata. | 95 | * The snapshot is invalid, note this in the metadata. |
| 79 | */ | 96 | */ |
| 80 | void (*drop_snapshot) (struct dm_exception_store *store); | 97 | void (*drop_snapshot) (struct dm_exception_store *store); |
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 157999ebd236..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 | /* |
| @@ -409,6 +426,15 @@ static void write_exception(struct pstore *ps, | |||
| 409 | e->new_chunk = cpu_to_le64(de->new_chunk); | 426 | e->new_chunk = cpu_to_le64(de->new_chunk); |
| 410 | } | 427 | } |
| 411 | 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 | |||
| 412 | /* | 438 | /* |
| 413 | * Registers the exceptions that are present in the current area. | 439 | * Registers the exceptions that are present in the current area. |
| 414 | * 'full' is filled in to indicate if the area has been | 440 | * 'full' is filled in to indicate if the area has been |
| @@ -505,7 +531,8 @@ static void persistent_usage(struct dm_exception_store *store, | |||
| 505 | * Then there are (ps->current_area + 1) metadata chunks, each one | 531 | * Then there are (ps->current_area + 1) metadata chunks, each one |
| 506 | * separated from the next by ps->exceptions_per_area data chunks. | 532 | * separated from the next by ps->exceptions_per_area data chunks. |
| 507 | */ | 533 | */ |
| 508 | *metadata_sectors = (ps->current_area + 2) * store->chunk_size; | 534 | *metadata_sectors = (ps->current_area + 1 + NUM_SNAPSHOT_HDR_CHUNKS) * |
| 535 | store->chunk_size; | ||
| 509 | } | 536 | } |
| 510 | 537 | ||
| 511 | static void persistent_dtr(struct dm_exception_store *store) | 538 | static void persistent_dtr(struct dm_exception_store *store) |
| @@ -680,6 +707,85 @@ static void persistent_commit_exception(struct dm_exception_store *store, | |||
| 680 | ps->callback_count = 0; | 707 | ps->callback_count = 0; |
| 681 | } | 708 | } |
| 682 | 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 | |||
| 683 | static void persistent_drop_snapshot(struct dm_exception_store *store) | 789 | static void persistent_drop_snapshot(struct dm_exception_store *store) |
| 684 | { | 790 | { |
| 685 | struct pstore *ps = get_info(store); | 791 | struct pstore *ps = get_info(store); |
| @@ -705,7 +811,7 @@ static int persistent_ctr(struct dm_exception_store *store, | |||
| 705 | ps->area = NULL; | 811 | ps->area = NULL; |
| 706 | ps->zero_area = NULL; | 812 | ps->zero_area = NULL; |
| 707 | ps->header_area = NULL; | 813 | ps->header_area = NULL; |
| 708 | ps->next_free = 2; /* skipping the header and first area */ | 814 | ps->next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1; /* header and 1st area */ |
| 709 | ps->current_committed = 0; | 815 | ps->current_committed = 0; |
| 710 | 816 | ||
| 711 | ps->callback_count = 0; | 817 | ps->callback_count = 0; |
| @@ -748,6 +854,8 @@ static struct dm_exception_store_type _persistent_type = { | |||
| 748 | .read_metadata = persistent_read_metadata, | 854 | .read_metadata = persistent_read_metadata, |
| 749 | .prepare_exception = persistent_prepare_exception, | 855 | .prepare_exception = persistent_prepare_exception, |
| 750 | .commit_exception = persistent_commit_exception, | 856 | .commit_exception = persistent_commit_exception, |
| 857 | .prepare_merge = persistent_prepare_merge, | ||
| 858 | .commit_merge = persistent_commit_merge, | ||
| 751 | .drop_snapshot = persistent_drop_snapshot, | 859 | .drop_snapshot = persistent_drop_snapshot, |
| 752 | .usage = persistent_usage, | 860 | .usage = persistent_usage, |
| 753 | .status = persistent_status, | 861 | .status = persistent_status, |
| @@ -761,6 +869,8 @@ static struct dm_exception_store_type _persistent_compat_type = { | |||
| 761 | .read_metadata = persistent_read_metadata, | 869 | .read_metadata = persistent_read_metadata, |
| 762 | .prepare_exception = persistent_prepare_exception, | 870 | .prepare_exception = persistent_prepare_exception, |
| 763 | .commit_exception = persistent_commit_exception, | 871 | .commit_exception = persistent_commit_exception, |
| 872 | .prepare_merge = persistent_prepare_merge, | ||
| 873 | .commit_merge = persistent_commit_merge, | ||
| 764 | .drop_snapshot = persistent_drop_snapshot, | 874 | .drop_snapshot = persistent_drop_snapshot, |
| 765 | .usage = persistent_usage, | 875 | .usage = persistent_usage, |
| 766 | .status = persistent_status, | 876 | .status = persistent_status, |
