Make chunk_size an optional argument to the snapshot target, except when creating a new snapshot. 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-14 10:25:45.000000000 -0400 +++ linux-2.6.16.i686/drivers/md/dm-exception-store.c 2006-04-14 11:55:03.000000000 -0400 @@ -148,6 +148,7 @@ static int alloc_area(struct pstore *ps) static void free_area(struct pstore *ps) { vfree(ps->area); + ps->area = NULL; } /* @@ -196,48 +197,81 @@ static int read_header(struct pstore *ps int r; struct disk_header *dh; chunk_t chunk_size; + int need_chunk_size = 0; + + if (!ps->snap->chunk_size) { + ps->snap->chunk_size = 1; + need_chunk_size = 1; + } + + r = dm_io_get(sectors_to_pages(ps->snap->chunk_size)); + if (r) + goto bad1; + + r = alloc_area(ps); + if (r) + goto bad2; r = chunk_io(ps, 0, READ); if (r) - return r; + goto bad3; dh = (struct disk_header *) ps->area; if (le32_to_cpu(dh->magic) == 0) { *new_snapshot = 1; + if (need_chunk_size) { + DMWARN("No existing metadata available and no chunk " + "size supplied."); + r = -EINVAL; + goto bad3; + } + } else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) { *new_snapshot = 0; ps->valid = le32_to_cpu(dh->valid); ps->version = le32_to_cpu(dh->version); chunk_size = le32_to_cpu(dh->chunk_size); - if (ps->snap->chunk_size != chunk_size) { + + if (!need_chunk_size && ps->snap->chunk_size != chunk_size) { DMWARN("chunk size %llu in device metadata overrides " "table chunk size of %llu.", (unsigned long long)chunk_size, (unsigned long long)ps->snap->chunk_size); + need_chunk_size = 1; + } + if (need_chunk_size) { /* We had a bogus chunk_size. Fix stuff up. */ - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); free_area(ps); + dm_io_put(sectors_to_pages(ps->snap->chunk_size)); ps->snap->chunk_size = chunk_size; ps->snap->chunk_mask = chunk_size - 1; ps->snap->chunk_shift = ffs(chunk_size) - 1; - r = alloc_area(ps); + r = dm_io_get(sectors_to_pages(chunk_size)); if (r) - return r; + goto bad1; - r = dm_io_get(sectors_to_pages(chunk_size)); + r = alloc_area(ps); if (r) - return r; + goto bad2; } } else { DMWARN("Invalid/corrupt snapshot"); r = -ENXIO; + goto bad3; } + return 0; + + bad3: + free_area(ps); + bad2: + dm_io_put(sectors_to_pages(ps->snap->chunk_size)); + bad1: return r; } @@ -386,7 +420,8 @@ static void persistent_destroy(struct ex { struct pstore *ps = get_info(store); - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); + if (ps->snap->chunk_size) + dm_io_put(sectors_to_pages(ps->snap->chunk_size)); vfree(ps->callbacks); free_area(ps); kfree(ps); @@ -545,32 +580,22 @@ static void persistent_drop(struct excep DMWARN("write header failed"); } -int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) +int dm_create_persistent(struct exception_store *store) { - int r; struct pstore *ps; - r = dm_io_get(sectors_to_pages(chunk_size)); - if (r) - return r; - /* allocate the pstore */ ps = kmalloc(sizeof(*ps), GFP_KERNEL); - if (!ps) { - r = -ENOMEM; - goto bad; - } + if (!ps) + return -ENOMEM; ps->snap = store->snap; ps->valid = 1; ps->version = SNAPSHOT_DISK_VERSION; + ps->area = NULL; ps->next_free = 2; /* skipping the header and first area */ ps->current_committed = 0; - r = alloc_area(ps); - if (r) - goto bad; - ps->callback_count = 0; atomic_set(&ps->pending_count, 0); ps->callbacks = NULL; @@ -584,13 +609,6 @@ int dm_create_persistent(struct exceptio store->context = ps; return 0; - - bad: - dm_io_put(sectors_to_pages(chunk_size)); - if (ps && ps->area) - free_area(ps); - kfree(ps); - return r; } /*----------------------------------------------------------------- @@ -640,18 +658,16 @@ static void transient_fraction_full(stru *denominator = get_dev_size(store->snap->cow->bdev); } -int dm_create_transient(struct exception_store *store, - struct dm_snapshot *s, int blocksize) +int dm_create_transient(struct exception_store *store) { struct transient_c *tc; - memset(store, 0, sizeof(*store)); store->destroy = transient_destroy; store->read_metadata = transient_read_metadata; store->prepare_exception = transient_prepare; store->commit_exception = transient_commit; + store->drop_snapshot = NULL; store->fraction_full = transient_fraction_full; - store->snap = s; tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); if (!tc) Index: linux-2.6.16.i686/drivers/md/dm-snap.c =================================================================== --- linux-2.6.16.i686.orig/drivers/md/dm-snap.c 2006-04-14 10:25:45.000000000 -0400 +++ linux-2.6.16.i686/drivers/md/dm-snap.c 2006-04-14 11:54:10.000000000 -0400 @@ -397,22 +397,56 @@ static void read_snapshot_metadata(struc } } +static int check_chunk_size(struct dm_snapshot *s, const char *chunk_size_str, + char **error) +{ + unsigned long chunk_size; + char *value; + + chunk_size = simple_strtoul(chunk_size_str, &value, 10); + if (chunk_size == 0 || value == NULL) { + *error = "Invalid chunk size"; + return 0; + } + + /* + * Chunk size must be multiple of page size. Silently + * round up if it's not. + */ + chunk_size = round_up(chunk_size, PAGE_SIZE >> 9); + + /* Check chunk_size is a power of 2 */ + if (chunk_size & (chunk_size - 1)) { + *error = "Chunk size is not a power of 2"; + return 0; + } + + /* Validate the chunk size against the device block size */ + if (chunk_size % (bdev_hardsect_size(s->cow->bdev) >> 9)) { + *error = "Chunk size is not a multiple of device blocksize"; + return 0; + } + + s->chunk_size = chunk_size; + s->chunk_mask = chunk_size - 1; + s->chunk_shift = ffs(chunk_size) - 1; + + return 1; +} + /* * Construct a snapshot mapping:

*/ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct dm_snapshot *s; - unsigned long chunk_size; int r = -EINVAL; char persistent; char *origin_path; char *cow_path; - char *value; - int blocksize; - if (argc < 4) { - ti->error = "dm-snapshot: requires exactly 4 arguments"; + if (argc != 3 && argc != 4) { + ti->error = "dm-snapshot: requires exactly 3 or 4 arguments"; r = -EINVAL; goto bad1; } @@ -427,9 +461,8 @@ static int snapshot_ctr(struct dm_target goto bad1; } - chunk_size = simple_strtoul(argv[3], &value, 10); - if (chunk_size == 0 || value == NULL) { - ti->error = "Invalid chunk size"; + if (persistent == 'N' && argc != 4) { + ti->error = "Chunk size is required for transient snapshots"; r = -EINVAL; goto bad1; } @@ -456,31 +489,13 @@ static int snapshot_ctr(struct dm_target goto bad2; } - /* - * Chunk size must be multiple of page size. Silently - * round up if it's not. - */ - chunk_size = round_up(chunk_size, PAGE_SIZE >> 9); - - /* Validate the chunk size against the device block size */ - blocksize = s->cow->bdev->bd_disk->queue->hardsect_size; - if (chunk_size % (blocksize >> 9)) { - ti->error = "Chunk size is not a multiple of device blocksize"; + s->chunk_size = s->chunk_mask = s->chunk_shift = 0; + if (argc == 4 && !check_chunk_size(s, argv[3], &ti->error)) { r = -EINVAL; goto bad3; } - /* Check chunk_size is a power of 2 */ - if (chunk_size & (chunk_size - 1)) { - ti->error = "Chunk size is not a power of 2"; - r = -EINVAL; - goto bad3; - } - - s->chunk_size = chunk_size; - s->chunk_mask = chunk_size - 1; s->type = persistent; - s->chunk_shift = ffs(chunk_size) - 1; s->valid = 1; s->active = 0; @@ -495,16 +510,12 @@ static int snapshot_ctr(struct dm_target goto bad3; } - /* - * Check the persistent flag - done here because we need the iobuf - * to check the LV header - */ s->store.snap = s; if (persistent == 'P') - r = dm_create_persistent(&s->store, chunk_size); + r = dm_create_persistent(&s->store); else - r = dm_create_transient(&s->store, s, blocksize); + r = dm_create_transient(&s->store); if (r) { ti->error = "Couldn't create exception store"; Index: linux-2.6.16.i686/drivers/md/dm-snap.h =================================================================== --- linux-2.6.16.i686.orig/drivers/md/dm-snap.h 2006-04-14 10:25:45.000000000 -0400 +++ linux-2.6.16.i686/drivers/md/dm-snap.h 2006-04-14 10:31:25.000000000 -0400 @@ -128,10 +128,9 @@ int dm_add_exception(struct dm_snapshot * Constructor and destructor for the default persistent * store. */ -int dm_create_persistent(struct exception_store *store, uint32_t chunk_size); +int dm_create_persistent(struct exception_store *store); +int dm_create_transient(struct exception_store *store); -int dm_create_transient(struct exception_store *store, - struct dm_snapshot *s, int blocksize); /* * Return the number of sectors in the device.