Add prepare_merge() and commit_merge() methods to the exception store. The former retrieves the details of the last exception in the store in preparation for the chunk to be merged back into the origin. The latter method is then later used to remove that same exception from the store once the merge has completed. Index: linux-2.6.16.i686/drivers/md/dm-exception-store.c =================================================================== --- linux-2.6.16.i686.orig/drivers/md/dm-exception-store.c 2006-04-28 17:24:07.000000000 +0100 +++ linux-2.6.16.i686/drivers/md/dm-exception-store.c 2006-05-04 09:45:34.000000000 +0100 @@ -589,6 +589,71 @@ static void persistent_commit(struct exc } } +/* Given an exception index within a certain area, return the + * corresponding chunk index. + */ +static inline uint32_t exception_to_chunk(uint32_t area, + uint32_t exceptions_per_area, + uint32_t exception) +{ + /* Header followed by sets of (metadata chunk, exceptions_per_area chunks) + */ + return 1 + (area * (1 + exceptions_per_area)) + 1 + exception; +} + +static int persistent_prepare_merge(struct exception_store *store, + struct exception *e, + int *empty) +{ + struct pstore *ps = get_info(store); + struct disk_exception de; + int r; + + *empty = 0; + + if (ps->current_committed == 0) { + if (ps->current_area == 0) { + *empty = 1; + return 0; + } + + r = area_io(ps, ps->current_area - 1, READ); + if (r) + return r; + + ps->current_committed = ps->exceptions_per_area - 1; + } else { + ps->current_committed--; + } + + read_exception(ps, ps->current_committed, &de); + + e->old_chunk = de.old_chunk; + e->new_chunk = de.new_chunk; + + ps->next_free = exception_to_chunk(ps->current_area, + ps->exceptions_per_area, + ps->current_committed); + + return 0; +} + +static void persistent_commit_merge(struct exception_store *store) +{ + struct pstore *ps = get_info(store); + struct disk_exception de; + int r; + + read_exception(ps, ps->current_committed, &de); + + de.new_chunk = 0; + write_exception(ps, ps->current_committed, &de); + + r = area_io(ps, ps->current_area, WRITE); + if (r) + ps->valid = 0; +} + static void persistent_drop(struct exception_store *store) { struct pstore *ps = get_info(store); @@ -622,6 +687,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->context = ps; @@ -684,6 +751,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; Index: linux-2.6.16.i686/drivers/md/dm-snap.h =================================================================== --- linux-2.6.16.i686.orig/drivers/md/dm-snap.h 2006-04-28 17:24:07.000000000 +0100 +++ linux-2.6.16.i686/drivers/md/dm-snap.h 2006-05-04 09:44:59.000000000 +0100 @@ -69,6 +69,18 @@ struct exception_store { void *callback_context); /* + * Prepare the next exception for merging. + */ + int (*prepare_merge) (struct exception_store *store, + struct exception *e, + int *empty); + + /* + * Remove the currently merging exception. + */ + void (*commit_merge) (struct exception_store *store); + + /* * The snapshot is invalid, note this in the metadata. */ void (*drop_snapshot) (struct exception_store *store);