diff options
Diffstat (limited to 'drivers/md/dm-snap.c')
-rw-r--r-- | drivers/md/dm-snap.c | 60 |
1 files changed, 57 insertions, 3 deletions
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; |