Add a very basic version of the snapshot-merged target. The only arguments required for the target are the origin device path and the COW device path. The origin device may not be used with another snapshot-origin target at the same time and the COW device may not be used with another snapshot target. The target acts much like a snapshot target exception no new exceptions are triggered by writes. If an I/O request corresponds to a chunk which has not already been mapped to the COW device, then the I/O will always be dispatched to the chunk on the origin device. However, if it is a write, before dispatching it we will trigger exceptions in any other associated snapshots much like the snapshot-origin target does. Another slight twist is the status() function reports the merging progress of the target - i.e. if the COW device is full, we report 0% and if it's empty we report 100%. Signed-off-by: Mark McLoughlin <markmc@xxxxxxxxxx> --- drivers/md/dm-snap.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/md/dm-snap.h | 4 + 2 files changed, 159 insertions(+), 11 deletions(-) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index e4ba02c..5236763 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1326,6 +1326,127 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result, return 0; } +/* + * Construct a merged snapshot: <origin_dev> <COW-dev> + */ +static int merged_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct dm_merged *merged; + char *origin_path; + char *cow_path; + int r; + + if (argc != 2) { + ti->error = "dm-snapshot: requires exactly 2 arguments"; + return -EINVAL; + } + + origin_path = argv[0]; + cow_path = argv[1]; + + merged = kmalloc(sizeof(struct dm_merged), GFP_KERNEL); + if (!merged) { + ti->error = "Cannot allocate merged snapshot context private " + "structure"; + return -ENOMEM; + } + + r = snapshot_init(ti, &merged->snap, + origin_path, dm_table_get_mode(ti->table), + cow_path, FMODE_READ | FMODE_WRITE, + 'P', 0); + if (r) { + kfree(merged); + return r; + } + + ti->private = merged; + + return 0; +} + +static void merged_dtr(struct dm_target *ti) +{ + struct dm_merged *merged = ti->private; + + snapshot_destroy(ti, &merged->snap); + + kfree(merged); +} + +static int merged_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) +{ + struct dm_merged *merged = ti->private; + struct dm_snapshot *s = &merged->snap; + struct dm_snap_exception *e; + chunk_t chunk; + int r = DM_MAPIO_REMAPPED;; + + if (unlikely(bio_barrier(bio))) + return -EOPNOTSUPP; + + chunk = sector_to_chunk(s, bio->bi_sector); + + down_read(&s->lock); + + if (!s->valid) { + up_read(&s->lock); + return -EIO; + } + + e = lookup_exception(&s->complete, chunk); + if (e) + remap_exception(s, e, bio, chunk); + else { + bio->bi_bdev = s->origin->bdev; + + if (bio_rw(bio) == WRITE) + r = do_origin(s->origin, bio->bi_sector, bio); + } + + up_read(&s->lock); + + return r; +} + +static void merged_resume(struct dm_target *ti) +{ + struct dm_merged *merged = ti->private; + + ti->split_io = min_chunk_size(merged->snap.origin); +} + +static int merged_status(struct dm_target *ti, status_type_t type, char *result, + unsigned int maxlen) +{ + struct dm_merged *merged = ti->private; + struct dm_snapshot *snap = &merged->snap; + + switch (type) { + case STATUSTYPE_INFO: + if (!snap->valid) + snprintf(result, maxlen, "Invalid"); + else { + sector_t numerator, denominator; + snap->store.fraction_full(&snap->store, + &numerator, + &denominator); + snprintf(result, maxlen, "%llu/%llu", + (unsigned long long)denominator - numerator, + (unsigned long long)denominator); + } + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s %s", + snap->origin->name, snap->cow->name); + break; + } + + return 0; +} + static struct target_type origin_target = { .name = "snapshot-origin", .version = {1, 6, 0}, @@ -1349,6 +1470,17 @@ static struct target_type snapshot_target = { .status = snapshot_status, }; +static struct target_type merged_target = { + .name = "snapshot-merged", + .version = {1, 1, 1}, + .module = THIS_MODULE, + .ctr = merged_ctr, + .dtr = merged_dtr, + .map = merged_map, + .resume = merged_resume, + .status = merged_status, +}; + static int __init dm_snapshot_init(void) { int r; @@ -1365,53 +1497,61 @@ static int __init dm_snapshot_init(void) goto bad1; } + r = dm_register_target(&merged_target); + if (r < 0) { + DMERR("Device mapper: Merged: register failed %d\n", r); + goto bad2; + } + r = init_origin_hash(); if (r) { DMERR("init_origin_hash failed."); - goto bad2; + goto bad3; } exception_cache = KMEM_CACHE(dm_snap_exception, 0); if (!exception_cache) { DMERR("Couldn't create exception cache."); r = -ENOMEM; - goto bad3; + goto bad4; } pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0); if (!pending_cache) { DMERR("Couldn't create pending cache."); r = -ENOMEM; - goto bad4; + goto bad5; } pending_pool = mempool_create_slab_pool(128, pending_cache); if (!pending_pool) { DMERR("Couldn't create pending pool."); r = -ENOMEM; - goto bad5; + goto bad6; } ksnapd = create_singlethread_workqueue("ksnapd"); if (!ksnapd) { DMERR("Failed to create ksnapd workqueue."); r = -ENOMEM; - goto bad6; + goto bad7; } return 0; - bad6: +bad7: mempool_destroy(pending_pool); - bad5: +bad6: kmem_cache_destroy(pending_cache); - bad4: +bad5: kmem_cache_destroy(exception_cache); - bad3: +bad4: exit_origin_hash(); - bad2: +bad3: + dm_unregister_target(&merged_target); +bad2: dm_unregister_target(&origin_target); - bad1: +bad1: dm_unregister_target(&snapshot_target); return r; } @@ -1430,6 +1570,10 @@ static void __exit dm_snapshot_exit(void) if (r) DMERR("origin unregister failed %d", r); + r = dm_unregister_target(&merged_target); + if (r) + DMERR("merged unregister failed %d", r); + exit_origin_hash(); mempool_destroy(pending_pool); kmem_cache_destroy(pending_cache); diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h index 8b02a42..8600477 100644 --- a/drivers/md/dm-snap.h +++ b/drivers/md/dm-snap.h @@ -188,6 +188,10 @@ struct dm_snapshot { struct work_struct queued_bios_work; }; +struct dm_merged { + struct dm_snapshot snap; +}; + /* * Used by the exception stores to load exceptions hen * initialising. -- 1.5.4.1 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel