Release memory if snapshot is invalidated. All io operations on the full (invalidated) snapshot will cause -EIO so no memory is needed anymore. [Is it possible to achieve this without the new atomic? - AGK] Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon Index: linux-2.6.19-rc4/drivers/md/dm-snap.c =================================================================== --- linux-2.6.19-rc4.orig/drivers/md/dm-snap.c 2006-11-01 21:53:54.000000000 +0000 +++ linux-2.6.19-rc4/drivers/md/dm-snap.c 2006-11-01 21:53:55.000000000 +0000 @@ -519,6 +519,7 @@ static int snapshot_ctr(struct dm_target ti->error = "Could not create kcopyd client"; goto bad5; } + atomic_set(&s->kcopyd_active_jobs, 0); /* Metadata must only be loaded into one table at once */ r = s->store.read_metadata(&s->store); @@ -564,6 +565,17 @@ static int snapshot_ctr(struct dm_target return r; } +static void __free_exceptions(struct dm_snapshot *s) +{ + kcopyd_client_destroy(s->kcopyd_client); + s->kcopyd_client = NULL; + + exit_exception_table(&s->pending, pending_cache); + exit_exception_table(&s->complete, exception_cache); + + s->store.destroy(&s->store); +} + static void snapshot_dtr(struct dm_target *ti) { struct dm_snapshot *s = (struct dm_snapshot *) ti->private; @@ -574,13 +586,8 @@ static void snapshot_dtr(struct dm_targe /* After this returns there can be no new kcopyd jobs. */ unregister_snapshot(s); - kcopyd_client_destroy(s->kcopyd_client); - - exit_exception_table(&s->pending, pending_cache); - exit_exception_table(&s->complete, exception_cache); - - /* Deallocate memory used */ - s->store.destroy(&s->store); + if (s->valid) + __free_exceptions(s); dm_put_device(ti, s->origin); dm_put_device(ti, s->cow); @@ -606,14 +613,20 @@ static void flush_bios(struct bio *bio) static void flush_queued_bios(void *data) { struct dm_snapshot *s = (struct dm_snapshot *) data; - struct bio *queued_bios; - unsigned long flags; +// struct bio *queued_bios; +// unsigned long flags; + if (!s->valid && s->kcopyd_client) { + __free_exceptions(s); + return; + } +/* spin_lock_irqsave(&s->pe_lock, flags); queued_bios = bio_list_get(&s->queued_bios); spin_unlock_irqrestore(&s->pe_lock, flags); flush_bios(queued_bios); +*/ } /* @@ -646,6 +659,13 @@ static void __invalidate_snapshot(struct s->valid = 0; + /* + * if no kcopy jobs in progress, schedule release memory, + * or memory will be released by last kcopyd complete callback + */ + if (!atomic_read(&s->kcopyd_active_jobs)) + queue_work(ksnapd, &s->queued_bios_work); + dm_table_event(s->table); } @@ -694,10 +714,11 @@ static void pending_complete(struct pend struct bio *snapshot_bios = NULL; int error = 0; - if (!success) { + if (!success || !s->valid) { /* Read/write error - snapshot is unusable */ down_write(&s->lock); - __invalidate_snapshot(s, -EIO); + if (s->valid) /* valid = 0 -> deferred kcopy job */ + __invalidate_snapshot(s, -EIO); error = 1; goto out; } @@ -762,6 +783,13 @@ static void copy_callback(int read_err, /* Update the metadata if we are persistent */ s->store.commit_exception(&s->store, &pe->e, commit_callback, pe); + + /* + * if snapshot is invalid and this is the last job + * schedule release memory + */ + if (atomic_dec_and_test(&s->kcopyd_active_jobs) && !s->valid) + queue_work(ksnapd, &s->queued_bios_work); } /* @@ -785,6 +813,7 @@ static void start_copy(struct pending_ex dest.count = src.count; /* Hand over to kcopyd */ + atomic_inc(&s->kcopyd_active_jobs); kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe); } Index: linux-2.6.19-rc4/drivers/md/dm-snap.h =================================================================== --- linux-2.6.19-rc4.orig/drivers/md/dm-snap.h 2006-11-01 21:55:10.000000000 +0000 +++ linux-2.6.19-rc4/drivers/md/dm-snap.h 2006-11-01 21:55:20.000000000 +0000 @@ -125,6 +125,9 @@ struct dm_snapshot { struct kcopyd_client *kcopyd_client; + /* jobs processing by kcopyd */ + atomic_t kcopyd_active_jobs; + /* Queue of snapshot writes for ksnapd to flush */ struct bio_list queued_bios; struct work_struct queued_bios_work;