diff options
-rw-r--r-- | drivers/md/dm-kcopyd.c | 31 | ||||
-rw-r--r-- | drivers/md/dm-snap.c | 60 | ||||
-rw-r--r-- | include/linux/dm-kcopyd.h | 15 |
3 files changed, 103 insertions, 3 deletions
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 98725e119324..f82147029636 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c | |||
@@ -617,6 +617,37 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, | |||
617 | } | 617 | } |
618 | EXPORT_SYMBOL(dm_kcopyd_copy); | 618 | EXPORT_SYMBOL(dm_kcopyd_copy); |
619 | 619 | ||
620 | void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, | ||
621 | dm_kcopyd_notify_fn fn, void *context) | ||
622 | { | ||
623 | struct kcopyd_job *job; | ||
624 | |||
625 | job = mempool_alloc(kc->job_pool, GFP_NOIO); | ||
626 | |||
627 | memset(job, 0, sizeof(struct kcopyd_job)); | ||
628 | job->kc = kc; | ||
629 | job->fn = fn; | ||
630 | job->context = context; | ||
631 | |||
632 | atomic_inc(&kc->nr_jobs); | ||
633 | |||
634 | return job; | ||
635 | } | ||
636 | EXPORT_SYMBOL(dm_kcopyd_prepare_callback); | ||
637 | |||
638 | void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err) | ||
639 | { | ||
640 | struct kcopyd_job *job = j; | ||
641 | struct dm_kcopyd_client *kc = job->kc; | ||
642 | |||
643 | job->read_err = read_err; | ||
644 | job->write_err = write_err; | ||
645 | |||
646 | push(&kc->complete_jobs, job); | ||
647 | wake(kc); | ||
648 | } | ||
649 | EXPORT_SYMBOL(dm_kcopyd_do_callback); | ||
650 | |||
620 | /* | 651 | /* |
621 | * Cancels a kcopyd job, eg. someone might be deactivating a | 652 | * Cancels a kcopyd job, eg. someone might be deactivating a |
622 | * mirror. | 653 | * mirror. |
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 94dee05dd28e..6f758870fc19 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -170,6 +170,13 @@ struct dm_snap_pending_exception { | |||
170 | * kcopyd. | 170 | * kcopyd. |
171 | */ | 171 | */ |
172 | int started; | 172 | int started; |
173 | |||
174 | /* | ||
175 | * For writing a complete chunk, bypassing the copy. | ||
176 | */ | ||
177 | struct bio *full_bio; | ||
178 | bio_end_io_t *full_bio_end_io; | ||
179 | void *full_bio_private; | ||
173 | }; | 180 | }; |
174 | 181 | ||
175 | /* | 182 | /* |
@@ -1369,6 +1376,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) | |||
1369 | struct dm_snapshot *s = pe->snap; | 1376 | struct dm_snapshot *s = pe->snap; |
1370 | struct bio *origin_bios = NULL; | 1377 | struct bio *origin_bios = NULL; |
1371 | struct bio *snapshot_bios = NULL; | 1378 | struct bio *snapshot_bios = NULL; |
1379 | struct bio *full_bio = NULL; | ||
1372 | int error = 0; | 1380 | int error = 0; |
1373 | 1381 | ||
1374 | if (!success) { | 1382 | if (!success) { |
@@ -1408,6 +1416,11 @@ out: | |||
1408 | dm_remove_exception(&pe->e); | 1416 | dm_remove_exception(&pe->e); |
1409 | snapshot_bios = bio_list_get(&pe->snapshot_bios); | 1417 | snapshot_bios = bio_list_get(&pe->snapshot_bios); |
1410 | origin_bios = bio_list_get(&pe->origin_bios); | 1418 | origin_bios = bio_list_get(&pe->origin_bios); |
1419 | full_bio = pe->full_bio; | ||
1420 | if (full_bio) { | ||
1421 | full_bio->bi_end_io = pe->full_bio_end_io; | ||
1422 | full_bio->bi_private = pe->full_bio_private; | ||
1423 | } | ||
1411 | free_pending_exception(pe); | 1424 | free_pending_exception(pe); |
1412 | 1425 | ||
1413 | increment_pending_exceptions_done_count(); | 1426 | increment_pending_exceptions_done_count(); |
@@ -1415,10 +1428,15 @@ out: | |||
1415 | up_write(&s->lock); | 1428 | up_write(&s->lock); |
1416 | 1429 | ||
1417 | /* Submit any pending write bios */ | 1430 | /* Submit any pending write bios */ |
1418 | if (error) | 1431 | if (error) { |
1432 | if (full_bio) | ||
1433 | bio_io_error(full_bio); | ||
1419 | error_bios(snapshot_bios); | 1434 | error_bios(snapshot_bios); |
1420 | else | 1435 | } else { |
1436 | if (full_bio) | ||
1437 | bio_endio(full_bio, 0); | ||
1421 | flush_bios(snapshot_bios); | 1438 | flush_bios(snapshot_bios); |
1439 | } | ||
1422 | 1440 | ||
1423 | retry_origin_bios(s, origin_bios); | 1441 | retry_origin_bios(s, origin_bios); |
1424 | } | 1442 | } |
@@ -1472,6 +1490,32 @@ static void start_copy(struct dm_snap_pending_exception *pe) | |||
1472 | dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe); | 1490 | dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe); |
1473 | } | 1491 | } |
1474 | 1492 | ||
1493 | static void full_bio_end_io(struct bio *bio, int error) | ||
1494 | { | ||
1495 | void *callback_data = bio->bi_private; | ||
1496 | |||
1497 | dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0); | ||
1498 | } | ||
1499 | |||
1500 | static void start_full_bio(struct dm_snap_pending_exception *pe, | ||
1501 | struct bio *bio) | ||
1502 | { | ||
1503 | struct dm_snapshot *s = pe->snap; | ||
1504 | void *callback_data; | ||
1505 | |||
1506 | pe->full_bio = bio; | ||
1507 | pe->full_bio_end_io = bio->bi_end_io; | ||
1508 | pe->full_bio_private = bio->bi_private; | ||
1509 | |||
1510 | callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client, | ||
1511 | copy_callback, pe); | ||
1512 | |||
1513 | bio->bi_end_io = full_bio_end_io; | ||
1514 | bio->bi_private = callback_data; | ||
1515 | |||
1516 | generic_make_request(bio); | ||
1517 | } | ||
1518 | |||
1475 | static struct dm_snap_pending_exception * | 1519 | static struct dm_snap_pending_exception * |
1476 | __lookup_pending_exception(struct dm_snapshot *s, chunk_t chunk) | 1520 | __lookup_pending_exception(struct dm_snapshot *s, chunk_t chunk) |
1477 | { | 1521 | { |
@@ -1507,6 +1551,7 @@ __find_pending_exception(struct dm_snapshot *s, | |||
1507 | bio_list_init(&pe->origin_bios); | 1551 | bio_list_init(&pe->origin_bios); |
1508 | bio_list_init(&pe->snapshot_bios); | 1552 | bio_list_init(&pe->snapshot_bios); |
1509 | pe->started = 0; | 1553 | pe->started = 0; |
1554 | pe->full_bio = NULL; | ||
1510 | 1555 | ||
1511 | if (s->store->type->prepare_exception(s->store, &pe->e)) { | 1556 | if (s->store->type->prepare_exception(s->store, &pe->e)) { |
1512 | free_pending_exception(pe); | 1557 | free_pending_exception(pe); |
@@ -1600,10 +1645,19 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
1600 | } | 1645 | } |
1601 | 1646 | ||
1602 | remap_exception(s, &pe->e, bio, chunk); | 1647 | remap_exception(s, &pe->e, bio, chunk); |
1603 | bio_list_add(&pe->snapshot_bios, bio); | ||
1604 | 1648 | ||
1605 | r = DM_MAPIO_SUBMITTED; | 1649 | r = DM_MAPIO_SUBMITTED; |
1606 | 1650 | ||
1651 | if (!pe->started && | ||
1652 | bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) { | ||
1653 | pe->started = 1; | ||
1654 | up_write(&s->lock); | ||
1655 | start_full_bio(pe, bio); | ||
1656 | goto out; | ||
1657 | } | ||
1658 | |||
1659 | bio_list_add(&pe->snapshot_bios, bio); | ||
1660 | |||
1607 | if (!pe->started) { | 1661 | if (!pe->started) { |
1608 | /* this is protected by snap->lock */ | 1662 | /* this is protected by snap->lock */ |
1609 | pe->started = 1; | 1663 | pe->started = 1; |
diff --git a/include/linux/dm-kcopyd.h b/include/linux/dm-kcopyd.h index 298d587e349b..5e54458e920f 100644 --- a/include/linux/dm-kcopyd.h +++ b/include/linux/dm-kcopyd.h | |||
@@ -42,5 +42,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from, | |||
42 | unsigned num_dests, struct dm_io_region *dests, | 42 | unsigned num_dests, struct dm_io_region *dests, |
43 | unsigned flags, dm_kcopyd_notify_fn fn, void *context); | 43 | unsigned flags, dm_kcopyd_notify_fn fn, void *context); |
44 | 44 | ||
45 | /* | ||
46 | * Prepare a callback and submit it via the kcopyd thread. | ||
47 | * | ||
48 | * dm_kcopyd_prepare_callback allocates a callback structure and returns it. | ||
49 | * It must not be called from interrupt context. | ||
50 | * The returned value should be passed into dm_kcopyd_do_callback. | ||
51 | * | ||
52 | * dm_kcopyd_do_callback submits the callback. | ||
53 | * It may be called from interrupt context. | ||
54 | * The callback is issued from the kcopyd thread. | ||
55 | */ | ||
56 | void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, | ||
57 | dm_kcopyd_notify_fn fn, void *context); | ||
58 | void dm_kcopyd_do_callback(void *job, int read_err, unsigned long write_err); | ||
59 | |||
45 | #endif /* __KERNEL__ */ | 60 | #endif /* __KERNEL__ */ |
46 | #endif /* _LINUX_DM_KCOPYD_H */ | 61 | #endif /* _LINUX_DM_KCOPYD_H */ |