@@ -723,9 +723,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
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;
+ if (de.old_chunk != *old_chunk - i ||
+ de.new_chunk != *new_chunk - i)
+ break;
}
return i;
@@ -653,12 +653,13 @@ static void merge_callback(int read_err, unsigned long write_err,
static void snapshot_merge_process(struct dm_snapshot *s)
{
- int r;
+ int r, linear_chunks;
chunk_t old_chunk, new_chunk;
struct origin *o;
chunk_t min_chunksize;
int must_wait;
struct dm_io_region src, dest;
+ sector_t io_size;
BUG_ON(!s->merge_running);
if (s->merge_shutdown)
@@ -674,34 +675,37 @@ static void snapshot_merge_process(struct dm_snapshot *s)
DMERR("target store does not support merging");
goto shut;
}
- r = s->store->type->prepare_merge(s->store, &old_chunk, &new_chunk);
- if (r <= 0) {
- if (r < 0)
+ linear_chunks = s->store->type->prepare_merge(s->store,
+ &old_chunk, &new_chunk);
+ if (linear_chunks <= 0) {
+ if (linear_chunks < 0)
DMERR("Read error in exception store, "
"shutting down merge");
goto shut;
}
- /* TODO: use larger I/O size once we verify that kcopyd handles it */
-
+ /*
+ * Use one (potentially large) I/O to copy all 'linear_chunks'
+ * from the exception store to the origin
+ */
+ io_size = linear_chunks * s->store->chunk_size;
dest.bdev = s->origin->bdev;
- dest.sector = chunk_to_sector(s->store, old_chunk);
- dest.count = min((sector_t)s->store->chunk_size,
- get_dev_size(dest.bdev) - dest.sector);
+ dest.sector = chunk_to_sector(s->store, old_chunk + 1 - linear_chunks);
+ dest.count = min(io_size, get_dev_size(dest.bdev) - dest.sector);
src.bdev = s->cow->bdev;
- src.sector = chunk_to_sector(s->store, new_chunk);
+ src.sector = chunk_to_sector(s->store, new_chunk + 1 - linear_chunks);
src.count = dest.count;
test_again:
- /* Reallocate other snapshots */
+ /* Reallocate other snapshots; must account for all 'linear_chunks' */
down_read(&_origins_lock);
o = __lookup_origin(s->origin->bdev);
must_wait = 0;
min_chunksize = minimum_chunk_size(o);
if (min_chunksize) {
chunk_t n;
- for (n = 0; n < s->store->chunk_size; n += min_chunksize) {
+ for (n = 0; n < io_size; n += min_chunksize) {
r = __origin_write(&o->snapshots, dest.sector + n,
NULL);
if (r == DM_MAPIO_SUBMITTED)
@@ -715,10 +719,12 @@ test_again:
}
down_write(&s->lock);
- s->merge_write_interlock = old_chunk;
- s->merge_write_interlock_n = 1;
+ s->merge_write_interlock = old_chunk + 1 - linear_chunks;
+ s->merge_write_interlock_n = linear_chunks;
up_write(&s->lock);
+ /* TODO: rather than only checking if the first chunk has a conflicting
+ read; do all 'linear_chunks' need to be checked? */
while (__chunk_is_tracked(s, old_chunk))
msleep(1);
@@ -745,7 +751,7 @@ static inline void release_write_interlock(struct dm_snapshot *s, int err)
static void merge_callback(int read_err, unsigned long write_err, void *context)
{
- int r;
+ int r, i;
struct dm_snapshot *s = context;
struct dm_snap_exception *e;
@@ -757,25 +763,34 @@ static void merge_callback(int read_err, unsigned long write_err, void *context)
goto shut;
}
- r = s->store->type->commit_merge(s->store, 1);
+ r = s->store->type->commit_merge(s->store, s->merge_write_interlock_n);
if (r < 0) {
DMERR("Write error in exception store, shutting down merge");
goto shut;
}
down_write(&s->lock);
- e = lookup_exception(&s->complete, s->merge_write_interlock);
- if (!e) {
- DMERR("exception for block %llu is on disk but not in memory",
- (unsigned long long)s->merge_write_interlock);
- up_write(&s->lock);
- goto shut;
- }
- if (dm_consecutive_chunk_count(e)) {
- dm_consecutive_chunk_count_dec(e);
- } else {
- remove_exception(e);
- free_exception(e);
+ /*
+ * Must process chunks (and associated exceptions) in reverse
+ * so that dm_consecutive_chunk_count_dec() accounting works
+ */
+ for (i = s->merge_write_interlock_n - 1; i >= 0; i--) {
+ e = lookup_exception(&s->complete,
+ s->merge_write_interlock + i);
+ if (!e) {
+ DMERR("exception for block %llu is on "
+ "disk but not in memory",
+ (unsigned long long)
+ s->merge_write_interlock + i);
+ up_write(&s->lock);
+ goto shut;
+ }
+ if (dm_consecutive_chunk_count(e)) {
+ dm_consecutive_chunk_count_dec(e);
+ } else {
+ remove_exception(e);
+ free_exception(e);
+ }
}
release_write_interlock(s, 0);