aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/md/dm-exception-store.c124
-rw-r--r--drivers/md/dm-snap.c90
-rw-r--r--drivers/md/dm-snap.h5
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)
150static void free_area(struct pstore *ps) 151static 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
272bad2:
273 free_area(ps);
274bad1:
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
550int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) 583int 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
645int dm_create_transient(struct exception_store *store, 661int 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
401static 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 */
404static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) 446static 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
1206static struct target_type origin_target = { 1214static 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
1217static struct target_type snapshot_target = { 1225static 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 */
131int dm_create_persistent(struct exception_store *store, uint32_t chunk_size); 131int dm_create_persistent(struct exception_store *store);
132 132
133int dm_create_transient(struct exception_store *store, 133int 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.