diff options
-rw-r--r-- | drivers/md/dm-exception-store.c | 124 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 90 | ||||
-rw-r--r-- | drivers/md/dm-snap.h | 5 |
3 files changed, 120 insertions, 99 deletions
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index d12379b5cdb5..8b4cd02f75a8 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | 18 | ||
19 | #define DM_MSG_PREFIX "snapshots" | 19 | #define DM_MSG_PREFIX "snapshots" |
20 | #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ | ||
20 | 21 | ||
21 | /*----------------------------------------------------------------- | 22 | /*----------------------------------------------------------------- |
22 | * Persistent snapshots, by persistent we mean that the snapshot | 23 | * Persistent snapshots, by persistent we mean that the snapshot |
@@ -150,6 +151,7 @@ static int alloc_area(struct pstore *ps) | |||
150 | static void free_area(struct pstore *ps) | 151 | static void free_area(struct pstore *ps) |
151 | { | 152 | { |
152 | vfree(ps->area); | 153 | vfree(ps->area); |
154 | ps->area = NULL; | ||
153 | } | 155 | } |
154 | 156 | ||
155 | /* | 157 | /* |
@@ -198,48 +200,79 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
198 | int r; | 200 | int r; |
199 | struct disk_header *dh; | 201 | struct disk_header *dh; |
200 | chunk_t chunk_size; | 202 | chunk_t chunk_size; |
203 | int chunk_size_supplied = 1; | ||
201 | 204 | ||
202 | r = chunk_io(ps, 0, READ); | 205 | /* |
206 | * Use default chunk size (or hardsect_size, if larger) if none supplied | ||
207 | */ | ||
208 | if (!ps->snap->chunk_size) { | ||
209 | ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, | ||
210 | bdev_hardsect_size(ps->snap->cow->bdev) >> 9); | ||
211 | ps->snap->chunk_mask = ps->snap->chunk_size - 1; | ||
212 | ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1; | ||
213 | chunk_size_supplied = 0; | ||
214 | } | ||
215 | |||
216 | r = dm_io_get(sectors_to_pages(ps->snap->chunk_size)); | ||
203 | if (r) | 217 | if (r) |
204 | return r; | 218 | return r; |
205 | 219 | ||
220 | r = alloc_area(ps); | ||
221 | if (r) | ||
222 | goto bad1; | ||
223 | |||
224 | r = chunk_io(ps, 0, READ); | ||
225 | if (r) | ||
226 | goto bad2; | ||
227 | |||
206 | dh = (struct disk_header *) ps->area; | 228 | dh = (struct disk_header *) ps->area; |
207 | 229 | ||
208 | if (le32_to_cpu(dh->magic) == 0) { | 230 | if (le32_to_cpu(dh->magic) == 0) { |
209 | *new_snapshot = 1; | 231 | *new_snapshot = 1; |
232 | return 0; | ||
233 | } | ||
210 | 234 | ||
211 | } else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) { | 235 | if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { |
212 | *new_snapshot = 0; | 236 | DMWARN("Invalid or corrupt snapshot"); |
213 | ps->valid = le32_to_cpu(dh->valid); | ||
214 | ps->version = le32_to_cpu(dh->version); | ||
215 | chunk_size = le32_to_cpu(dh->chunk_size); | ||
216 | if (ps->snap->chunk_size != chunk_size) { | ||
217 | DMWARN("chunk size %llu in device metadata overrides " | ||
218 | "table chunk size of %llu.", | ||
219 | (unsigned long long)chunk_size, | ||
220 | (unsigned long long)ps->snap->chunk_size); | ||
221 | |||
222 | /* We had a bogus chunk_size. Fix stuff up. */ | ||
223 | dm_io_put(sectors_to_pages(ps->snap->chunk_size)); | ||
224 | free_area(ps); | ||
225 | |||
226 | ps->snap->chunk_size = chunk_size; | ||
227 | ps->snap->chunk_mask = chunk_size - 1; | ||
228 | ps->snap->chunk_shift = ffs(chunk_size) - 1; | ||
229 | |||
230 | r = alloc_area(ps); | ||
231 | if (r) | ||
232 | return r; | ||
233 | |||
234 | r = dm_io_get(sectors_to_pages(chunk_size)); | ||
235 | if (r) | ||
236 | return r; | ||
237 | } | ||
238 | } else { | ||
239 | DMWARN("Invalid/corrupt snapshot"); | ||
240 | r = -ENXIO; | 237 | r = -ENXIO; |
238 | goto bad2; | ||
241 | } | 239 | } |
242 | 240 | ||
241 | *new_snapshot = 0; | ||
242 | ps->valid = le32_to_cpu(dh->valid); | ||
243 | ps->version = le32_to_cpu(dh->version); | ||
244 | chunk_size = le32_to_cpu(dh->chunk_size); | ||
245 | |||
246 | if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size) | ||
247 | return 0; | ||
248 | |||
249 | DMWARN("chunk size %llu in device metadata overrides " | ||
250 | "table chunk size of %llu.", | ||
251 | (unsigned long long)chunk_size, | ||
252 | (unsigned long long)ps->snap->chunk_size); | ||
253 | |||
254 | /* We had a bogus chunk_size. Fix stuff up. */ | ||
255 | dm_io_put(sectors_to_pages(ps->snap->chunk_size)); | ||
256 | free_area(ps); | ||
257 | |||
258 | ps->snap->chunk_size = chunk_size; | ||
259 | ps->snap->chunk_mask = chunk_size - 1; | ||
260 | ps->snap->chunk_shift = ffs(chunk_size) - 1; | ||
261 | |||
262 | r = dm_io_get(sectors_to_pages(chunk_size)); | ||
263 | if (r) | ||
264 | return r; | ||
265 | |||
266 | r = alloc_area(ps); | ||
267 | if (r) | ||
268 | goto bad1; | ||
269 | |||
270 | return 0; | ||
271 | |||
272 | bad2: | ||
273 | free_area(ps); | ||
274 | bad1: | ||
275 | dm_io_put(sectors_to_pages(ps->snap->chunk_size)); | ||
243 | return r; | 276 | return r; |
244 | } | 277 | } |
245 | 278 | ||
@@ -547,32 +580,22 @@ static void persistent_drop(struct exception_store *store) | |||
547 | DMWARN("write header failed"); | 580 | DMWARN("write header failed"); |
548 | } | 581 | } |
549 | 582 | ||
550 | int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) | 583 | int dm_create_persistent(struct exception_store *store) |
551 | { | 584 | { |
552 | int r; | ||
553 | struct pstore *ps; | 585 | struct pstore *ps; |
554 | 586 | ||
555 | r = dm_io_get(sectors_to_pages(chunk_size)); | ||
556 | if (r) | ||
557 | return r; | ||
558 | |||
559 | /* allocate the pstore */ | 587 | /* allocate the pstore */ |
560 | ps = kmalloc(sizeof(*ps), GFP_KERNEL); | 588 | ps = kmalloc(sizeof(*ps), GFP_KERNEL); |
561 | if (!ps) { | 589 | if (!ps) |
562 | r = -ENOMEM; | 590 | return -ENOMEM; |
563 | goto bad; | ||
564 | } | ||
565 | 591 | ||
566 | ps->snap = store->snap; | 592 | ps->snap = store->snap; |
567 | ps->valid = 1; | 593 | ps->valid = 1; |
568 | ps->version = SNAPSHOT_DISK_VERSION; | 594 | ps->version = SNAPSHOT_DISK_VERSION; |
595 | ps->area = NULL; | ||
569 | ps->next_free = 2; /* skipping the header and first area */ | 596 | ps->next_free = 2; /* skipping the header and first area */ |
570 | ps->current_committed = 0; | 597 | ps->current_committed = 0; |
571 | 598 | ||
572 | r = alloc_area(ps); | ||
573 | if (r) | ||
574 | goto bad; | ||
575 | |||
576 | ps->callback_count = 0; | 599 | ps->callback_count = 0; |
577 | atomic_set(&ps->pending_count, 0); | 600 | atomic_set(&ps->pending_count, 0); |
578 | ps->callbacks = NULL; | 601 | ps->callbacks = NULL; |
@@ -586,13 +609,6 @@ int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) | |||
586 | store->context = ps; | 609 | store->context = ps; |
587 | 610 | ||
588 | return 0; | 611 | return 0; |
589 | |||
590 | bad: | ||
591 | dm_io_put(sectors_to_pages(chunk_size)); | ||
592 | if (ps && ps->area) | ||
593 | free_area(ps); | ||
594 | kfree(ps); | ||
595 | return r; | ||
596 | } | 612 | } |
597 | 613 | ||
598 | /*----------------------------------------------------------------- | 614 | /*----------------------------------------------------------------- |
@@ -642,18 +658,16 @@ static void transient_fraction_full(struct exception_store *store, | |||
642 | *denominator = get_dev_size(store->snap->cow->bdev); | 658 | *denominator = get_dev_size(store->snap->cow->bdev); |
643 | } | 659 | } |
644 | 660 | ||
645 | int dm_create_transient(struct exception_store *store, | 661 | int dm_create_transient(struct exception_store *store) |
646 | struct dm_snapshot *s, int blocksize) | ||
647 | { | 662 | { |
648 | struct transient_c *tc; | 663 | struct transient_c *tc; |
649 | 664 | ||
650 | memset(store, 0, sizeof(*store)); | ||
651 | store->destroy = transient_destroy; | 665 | store->destroy = transient_destroy; |
652 | store->read_metadata = transient_read_metadata; | 666 | store->read_metadata = transient_read_metadata; |
653 | store->prepare_exception = transient_prepare; | 667 | store->prepare_exception = transient_prepare; |
654 | store->commit_exception = transient_commit; | 668 | store->commit_exception = transient_commit; |
669 | store->drop_snapshot = NULL; | ||
655 | store->fraction_full = transient_fraction_full; | 670 | store->fraction_full = transient_fraction_full; |
656 | store->snap = s; | ||
657 | 671 | ||
658 | tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); | 672 | tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); |
659 | if (!tc) | 673 | if (!tc) |
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 7c14867b5903..89f8dd1bfaa0 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -398,21 +398,60 @@ static void read_snapshot_metadata(struct dm_snapshot *s) | |||
398 | } | 398 | } |
399 | } | 399 | } |
400 | 400 | ||
401 | static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg, | ||
402 | char **error) | ||
403 | { | ||
404 | unsigned long chunk_size; | ||
405 | char *value; | ||
406 | |||
407 | chunk_size = simple_strtoul(chunk_size_arg, &value, 10); | ||
408 | if (*chunk_size_arg == '\0' || *value != '\0') { | ||
409 | *error = "Invalid chunk size"; | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | if (!chunk_size) { | ||
414 | s->chunk_size = s->chunk_mask = s->chunk_shift = 0; | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * Chunk size must be multiple of page size. Silently | ||
420 | * round up if it's not. | ||
421 | */ | ||
422 | chunk_size = round_up(chunk_size, PAGE_SIZE >> 9); | ||
423 | |||
424 | /* Check chunk_size is a power of 2 */ | ||
425 | if (chunk_size & (chunk_size - 1)) { | ||
426 | *error = "Chunk size is not a power of 2"; | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | |||
430 | /* Validate the chunk size against the device block size */ | ||
431 | if (chunk_size % (bdev_hardsect_size(s->cow->bdev) >> 9)) { | ||
432 | *error = "Chunk size is not a multiple of device blocksize"; | ||
433 | return -EINVAL; | ||
434 | } | ||
435 | |||
436 | s->chunk_size = chunk_size; | ||
437 | s->chunk_mask = chunk_size - 1; | ||
438 | s->chunk_shift = ffs(chunk_size) - 1; | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
401 | /* | 443 | /* |
402 | * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size> | 444 | * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size> |
403 | */ | 445 | */ |
404 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 446 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
405 | { | 447 | { |
406 | struct dm_snapshot *s; | 448 | struct dm_snapshot *s; |
407 | unsigned long chunk_size; | ||
408 | int r = -EINVAL; | 449 | int r = -EINVAL; |
409 | char persistent; | 450 | char persistent; |
410 | char *origin_path; | 451 | char *origin_path; |
411 | char *cow_path; | 452 | char *cow_path; |
412 | char *value; | ||
413 | int blocksize; | ||
414 | 453 | ||
415 | if (argc < 4) { | 454 | if (argc != 4) { |
416 | ti->error = "requires exactly 4 arguments"; | 455 | ti->error = "requires exactly 4 arguments"; |
417 | r = -EINVAL; | 456 | r = -EINVAL; |
418 | goto bad1; | 457 | goto bad1; |
@@ -428,13 +467,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
428 | goto bad1; | 467 | goto bad1; |
429 | } | 468 | } |
430 | 469 | ||
431 | chunk_size = simple_strtoul(argv[3], &value, 10); | ||
432 | if (chunk_size == 0 || value == NULL) { | ||
433 | ti->error = "Invalid chunk size"; | ||
434 | r = -EINVAL; | ||
435 | goto bad1; | ||
436 | } | ||
437 | |||
438 | s = kmalloc(sizeof(*s), GFP_KERNEL); | 470 | s = kmalloc(sizeof(*s), GFP_KERNEL); |
439 | if (s == NULL) { | 471 | if (s == NULL) { |
440 | ti->error = "Cannot allocate snapshot context private " | 472 | ti->error = "Cannot allocate snapshot context private " |
@@ -457,31 +489,11 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
457 | goto bad2; | 489 | goto bad2; |
458 | } | 490 | } |
459 | 491 | ||
460 | /* | 492 | r = set_chunk_size(s, argv[3], &ti->error); |
461 | * Chunk size must be multiple of page size. Silently | 493 | if (r) |
462 | * round up if it's not. | ||
463 | */ | ||
464 | chunk_size = round_up(chunk_size, PAGE_SIZE >> 9); | ||
465 | |||
466 | /* Validate the chunk size against the device block size */ | ||
467 | blocksize = s->cow->bdev->bd_disk->queue->hardsect_size; | ||
468 | if (chunk_size % (blocksize >> 9)) { | ||
469 | ti->error = "Chunk size is not a multiple of device blocksize"; | ||
470 | r = -EINVAL; | ||
471 | goto bad3; | ||
472 | } | ||
473 | |||
474 | /* Check chunk_size is a power of 2 */ | ||
475 | if (chunk_size & (chunk_size - 1)) { | ||
476 | ti->error = "Chunk size is not a power of 2"; | ||
477 | r = -EINVAL; | ||
478 | goto bad3; | 494 | goto bad3; |
479 | } | ||
480 | 495 | ||
481 | s->chunk_size = chunk_size; | ||
482 | s->chunk_mask = chunk_size - 1; | ||
483 | s->type = persistent; | 496 | s->type = persistent; |
484 | s->chunk_shift = ffs(chunk_size) - 1; | ||
485 | 497 | ||
486 | s->valid = 1; | 498 | s->valid = 1; |
487 | s->active = 0; | 499 | s->active = 0; |
@@ -496,16 +508,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
496 | goto bad3; | 508 | goto bad3; |
497 | } | 509 | } |
498 | 510 | ||
499 | /* | ||
500 | * Check the persistent flag - done here because we need the iobuf | ||
501 | * to check the LV header | ||
502 | */ | ||
503 | s->store.snap = s; | 511 | s->store.snap = s; |
504 | 512 | ||
505 | if (persistent == 'P') | 513 | if (persistent == 'P') |
506 | r = dm_create_persistent(&s->store, chunk_size); | 514 | r = dm_create_persistent(&s->store); |
507 | else | 515 | else |
508 | r = dm_create_transient(&s->store, s, blocksize); | 516 | r = dm_create_transient(&s->store); |
509 | 517 | ||
510 | if (r) { | 518 | if (r) { |
511 | ti->error = "Couldn't create exception store"; | 519 | ti->error = "Couldn't create exception store"; |
@@ -1205,7 +1213,7 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result, | |||
1205 | 1213 | ||
1206 | static struct target_type origin_target = { | 1214 | static struct target_type origin_target = { |
1207 | .name = "snapshot-origin", | 1215 | .name = "snapshot-origin", |
1208 | .version = {1, 4, 0}, | 1216 | .version = {1, 5, 0}, |
1209 | .module = THIS_MODULE, | 1217 | .module = THIS_MODULE, |
1210 | .ctr = origin_ctr, | 1218 | .ctr = origin_ctr, |
1211 | .dtr = origin_dtr, | 1219 | .dtr = origin_dtr, |
@@ -1216,7 +1224,7 @@ static struct target_type origin_target = { | |||
1216 | 1224 | ||
1217 | static struct target_type snapshot_target = { | 1225 | static struct target_type snapshot_target = { |
1218 | .name = "snapshot", | 1226 | .name = "snapshot", |
1219 | .version = {1, 4, 0}, | 1227 | .version = {1, 5, 0}, |
1220 | .module = THIS_MODULE, | 1228 | .module = THIS_MODULE, |
1221 | .ctr = snapshot_ctr, | 1229 | .ctr = snapshot_ctr, |
1222 | .dtr = snapshot_dtr, | 1230 | .dtr = snapshot_dtr, |
diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h index fdec1e2dc871..221eb8880c80 100644 --- a/drivers/md/dm-snap.h +++ b/drivers/md/dm-snap.h | |||
@@ -128,10 +128,9 @@ int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new); | |||
128 | * Constructor and destructor for the default persistent | 128 | * Constructor and destructor for the default persistent |
129 | * store. | 129 | * store. |
130 | */ | 130 | */ |
131 | int dm_create_persistent(struct exception_store *store, uint32_t chunk_size); | 131 | int dm_create_persistent(struct exception_store *store); |
132 | 132 | ||
133 | int dm_create_transient(struct exception_store *store, | 133 | int dm_create_transient(struct exception_store *store); |
134 | struct dm_snapshot *s, int blocksize); | ||
135 | 134 | ||
136 | /* | 135 | /* |
137 | * Return the number of sectors in the device. | 136 | * Return the number of sectors in the device. |