This patch adds two new methods to exception store: prepare_merge: returns the last chunk in the variables passed by reference. The return value is the number of consecutive chunks. commit_merge: permanently removes 'n' chunks from the exception store. 'n' is less or equal that the number returned by prepare_merge. If the caller wishes, it can do the optimization of merging several consecutive chunks at once. If it doesn't want to do this optimization, it just calls commit_merge with n == 1. Signed-off-by: Mikulas Patocka --- drivers/md/dm-exception-store.c | 70 ++++++++++++++++++++++++++++++++++++++++ drivers/md/dm-snap.h | 15 ++++++++ 2 files changed, 85 insertions(+) Index: linux-2.6.26-fast/drivers/md/dm-snap.h =================================================================== --- linux-2.6.26-fast.orig/drivers/md/dm-snap.h 2008-07-23 23:01:06.000000000 +0200 +++ linux-2.6.26-fast/drivers/md/dm-snap.h 2008-07-23 23:01:10.000000000 +0200 @@ -130,6 +130,21 @@ struct exception_store { void *callback_context); /* + * Returns the last chunk in the pointers. + * Returns > 0: the number of consecutive chunk that can be copied in + * one shot. + * Returns == 0: the exception store is empty. + * Returns < 0: error. + */ + int (*prepare_merge) (struct exception_store *store, chunk_t *old_chunk, chunk_t *new_chunk); + + /* + * Clear n last exceptions. + * n must be <= than the value returned by prepare_merge. + */ + int (*commit_merge) (struct exception_store *store, int n); + + /* * The snapshot is invalid, note this in the metadata. */ void (*drop_snapshot) (struct exception_store *store); Index: linux-2.6.26-fast/drivers/md/dm-exception-store.c =================================================================== --- linux-2.6.26-fast.orig/drivers/md/dm-exception-store.c 2008-07-23 23:01:06.000000000 +0200 +++ linux-2.6.26-fast/drivers/md/dm-exception-store.c 2008-07-23 23:01:10.000000000 +0200 @@ -398,6 +398,16 @@ static void write_exception(struct pstor e->new_chunk = cpu_to_le64(de->new_chunk); } +static void clear_exception(struct pstore *ps, + uint32_t index) +{ + struct disk_exception *e = get_exception(ps, index); + + /* clear it */ + e->old_chunk = 0; + e->new_chunk = 0; +} + /* * Registers the exceptions that are present in the current area. * 'full' is filled in to indicate if the area has been @@ -650,6 +660,62 @@ skip_area_io: } } +static int persistent_prepare_merge(struct exception_store *store, chunk_t *old_chunk, chunk_t *new_chunk) +{ + int r, i; + struct pstore *ps = get_info(store); + struct disk_exception de; + + if (!ps->current_committed) { + if (!ps->current_area) + return 0; + ps->current_area--; + r = area_io(ps, ps->current_area, READ); + if (r < 0) + return r; + ps->current_committed = ps->exceptions_per_area; + } + + read_exception(ps, ps->current_committed - 1, &de); + *old_chunk = de.old_chunk; + *new_chunk = de.new_chunk; + + for (i = 1; i < ps->current_committed; i++) { + read_exception(ps, ps->current_committed - 1 - i, &de); + if (de.old_chunk == *old_chunk - i && + de.new_chunk == *new_chunk - i) + continue; + } + + return i; +} + +static int persistent_commit_merge(struct exception_store *store, int n) +{ + int r, i; + struct pstore *ps = get_info(store); + + BUG_ON(n > ps->current_committed); + + for (i = 0; i < n; i++) + clear_exception(ps, ps->current_committed - 1 - i); + + r = area_io(ps, ps->current_area, WRITE); + if (r < 0) + return r; + + ps->current_committed -= i; + + /* + * ps->next_free cannot really be reliably decreased here (because of + * misordered chunks), so don't do it. We don't even need it, because + * there is no situation where merging snapshot would become + * non-merging. + */ + + return 0; +} + static void persistent_drop(struct exception_store *store) { struct pstore *ps = get_info(store); @@ -697,6 +763,8 @@ int dm_create_persistent(struct exceptio store->read_metadata = persistent_read_metadata; store->prepare_exception = persistent_prepare; store->commit_exception = persistent_commit; + store->prepare_merge = persistent_prepare_merge; + store->commit_merge = persistent_commit_merge; store->drop_snapshot = persistent_drop; store->fraction_full = persistent_fraction_full; store->handover = persistent_handover; @@ -765,6 +833,8 @@ int dm_create_transient(struct exception store->read_metadata = transient_read_metadata; store->prepare_exception = transient_prepare; store->commit_exception = transient_commit; + store->prepare_merge = NULL; + store->commit_merge = NULL; store->drop_snapshot = NULL; store->fraction_full = transient_fraction_full; store->handover = transient_handover;