aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2009-12-10 18:52:33 -0500
committerAlasdair G Kergon <agk@redhat.com>2009-12-10 18:52:33 -0500
commit9fe862548821b0c206c58e8057b782530a173703 (patch)
tree1a23bec091d5163dfec29750b5daf3e93637c5ab /drivers/md
parent1e03f97e4301f75a2f3b649787f7876516764929 (diff)
dm snapshot: queue writes to chunks being merged
While a set of chunks is being merged, any overlapping writes need to be queued. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-snap.c91
1 files changed, 78 insertions, 13 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index dc2412e6c5cf..91a47c522b09 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -109,6 +109,16 @@ struct dm_snapshot {
109 109
110 /* Wait for events based on state_bits */ 110 /* Wait for events based on state_bits */
111 unsigned long state_bits; 111 unsigned long state_bits;
112
113 /* Range of chunks currently being merged. */
114 chunk_t first_merging_chunk;
115 int num_merging_chunks;
116
117 /*
118 * Incoming bios that overlap with chunks being merged must wait
119 * for them to be committed.
120 */
121 struct bio_list bios_queued_during_merge;
112}; 122};
113 123
114/* 124/*
@@ -747,6 +757,14 @@ static void merge_shutdown(struct dm_snapshot *s)
747 wake_up_bit(&s->state_bits, RUNNING_MERGE); 757 wake_up_bit(&s->state_bits, RUNNING_MERGE);
748} 758}
749 759
760static struct bio *__release_queued_bios_after_merge(struct dm_snapshot *s)
761{
762 s->first_merging_chunk = 0;
763 s->num_merging_chunks = 0;
764
765 return bio_list_get(&s->bios_queued_during_merge);
766}
767
750/* 768/*
751 * Remove one chunk from the index of completed exceptions. 769 * Remove one chunk from the index of completed exceptions.
752 */ 770 */
@@ -755,8 +773,6 @@ static int __remove_single_exception_chunk(struct dm_snapshot *s,
755{ 773{
756 struct dm_exception *e; 774 struct dm_exception *e;
757 775
758 /* FIXME: interlock writes to this chunk */
759
760 e = dm_lookup_exception(&s->complete, old_chunk); 776 e = dm_lookup_exception(&s->complete, old_chunk);
761 if (!e) { 777 if (!e) {
762 DMERR("Corruption detected: exception for block %llu is " 778 DMERR("Corruption detected: exception for block %llu is "
@@ -801,14 +817,32 @@ static int __remove_single_exception_chunk(struct dm_snapshot *s,
801 return 0; 817 return 0;
802} 818}
803 819
804static int remove_single_exception_chunk(struct dm_snapshot *s, 820static void flush_bios(struct bio *bio);
805 chunk_t old_chunk) 821
822static int remove_single_exception_chunk(struct dm_snapshot *s)
806{ 823{
807 int r = 0; 824 struct bio *b = NULL;
825 int r;
826 chunk_t old_chunk = s->first_merging_chunk + s->num_merging_chunks - 1;
808 827
809 down_write(&s->lock); 828 down_write(&s->lock);
810 r = __remove_single_exception_chunk(s, old_chunk); 829
830 /*
831 * Process chunks (and associated exceptions) in reverse order
832 * so that dm_consecutive_chunk_count_dec() accounting works.
833 */
834 do {
835 r = __remove_single_exception_chunk(s, old_chunk);
836 if (r)
837 goto out;
838 } while (old_chunk-- > s->first_merging_chunk);
839
840 b = __release_queued_bios_after_merge(s);
841
842out:
811 up_write(&s->lock); 843 up_write(&s->lock);
844 if (b)
845 flush_bios(b);
812 846
813 return r; 847 return r;
814} 848}
@@ -844,9 +878,6 @@ static void snapshot_merge_next_chunks(struct dm_snapshot *s)
844 878
845 /* TODO: use larger I/O size once we verify that kcopyd handles it */ 879 /* TODO: use larger I/O size once we verify that kcopyd handles it */
846 880
847 if (remove_single_exception_chunk(s, old_chunk) < 0)
848 goto shut;
849
850 dest.bdev = s->origin->bdev; 881 dest.bdev = s->origin->bdev;
851 dest.sector = chunk_to_sector(s->store, old_chunk); 882 dest.sector = chunk_to_sector(s->store, old_chunk);
852 dest.count = min((sector_t)s->store->chunk_size, 883 dest.count = min((sector_t)s->store->chunk_size,
@@ -856,6 +887,13 @@ static void snapshot_merge_next_chunks(struct dm_snapshot *s)
856 src.sector = chunk_to_sector(s->store, new_chunk); 887 src.sector = chunk_to_sector(s->store, new_chunk);
857 src.count = dest.count; 888 src.count = dest.count;
858 889
890 down_write(&s->lock);
891 s->first_merging_chunk = old_chunk;
892 s->num_merging_chunks = 1;
893 up_write(&s->lock);
894
895 /* !!! FIXME: wait until writes to this chunk drain */
896
859 dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s); 897 dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
860 return; 898 return;
861 899
@@ -863,9 +901,12 @@ shut:
863 merge_shutdown(s); 901 merge_shutdown(s);
864} 902}
865 903
904static void error_bios(struct bio *bio);
905
866static void merge_callback(int read_err, unsigned long write_err, void *context) 906static void merge_callback(int read_err, unsigned long write_err, void *context)
867{ 907{
868 struct dm_snapshot *s = context; 908 struct dm_snapshot *s = context;
909 struct bio *b = NULL;
869 910
870 if (read_err || write_err) { 911 if (read_err || write_err) {
871 if (read_err) 912 if (read_err)
@@ -875,16 +916,25 @@ static void merge_callback(int read_err, unsigned long write_err, void *context)
875 goto shut; 916 goto shut;
876 } 917 }
877 918
878 if (s->store->type->commit_merge(s->store, 1) < 0) { 919 if (s->store->type->commit_merge(s->store,
920 s->num_merging_chunks) < 0) {
879 DMERR("Write error in exception store: shutting down merge"); 921 DMERR("Write error in exception store: shutting down merge");
880 goto shut; 922 goto shut;
881 } 923 }
882 924
925 if (remove_single_exception_chunk(s) < 0)
926 goto shut;
927
883 snapshot_merge_next_chunks(s); 928 snapshot_merge_next_chunks(s);
884 929
885 return; 930 return;
886 931
887shut: 932shut:
933 down_write(&s->lock);
934 b = __release_queued_bios_after_merge(s);
935 up_write(&s->lock);
936 error_bios(b);
937
888 merge_shutdown(s); 938 merge_shutdown(s);
889} 939}
890 940
@@ -983,6 +1033,9 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
983 INIT_LIST_HEAD(&s->list); 1033 INIT_LIST_HEAD(&s->list);
984 spin_lock_init(&s->pe_lock); 1034 spin_lock_init(&s->pe_lock);
985 s->state_bits = 0; 1035 s->state_bits = 0;
1036 s->first_merging_chunk = 0;
1037 s->num_merging_chunks = 0;
1038 bio_list_init(&s->bios_queued_during_merge);
986 1039
987 /* Allocate hash table for COW data */ 1040 /* Allocate hash table for COW data */
988 if (init_hash_tables(s)) { 1041 if (init_hash_tables(s)) {
@@ -1539,6 +1592,8 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
1539 * For each chunk, if there is an existing exception, it is used to 1592 * For each chunk, if there is an existing exception, it is used to
1540 * redirect I/O to the cow device. Otherwise I/O is sent to the origin, 1593 * redirect I/O to the cow device. Otherwise I/O is sent to the origin,
1541 * which in turn might generate exceptions in other snapshots. 1594 * which in turn might generate exceptions in other snapshots.
1595 * If merging is currently taking place on the chunk in question, the
1596 * I/O is deferred by adding it to s->bios_queued_during_merge.
1542 */ 1597 */
1543static int snapshot_merge_map(struct dm_target *ti, struct bio *bio, 1598static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
1544 union map_info *map_context) 1599 union map_info *map_context)
@@ -1559,7 +1614,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
1559 1614
1560 chunk = sector_to_chunk(s->store, bio->bi_sector); 1615 chunk = sector_to_chunk(s->store, bio->bi_sector);
1561 1616
1562 down_read(&s->lock); 1617 down_write(&s->lock);
1563 1618
1564 /* Full snapshots are not usable */ 1619 /* Full snapshots are not usable */
1565 if (!s->valid) { 1620 if (!s->valid) {
@@ -1570,6 +1625,16 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
1570 /* If the block is already remapped - use that */ 1625 /* If the block is already remapped - use that */
1571 e = dm_lookup_exception(&s->complete, chunk); 1626 e = dm_lookup_exception(&s->complete, chunk);
1572 if (e) { 1627 if (e) {
1628 /* Queue writes overlapping with chunks being merged */
1629 if (bio_rw(bio) == WRITE &&
1630 chunk >= s->first_merging_chunk &&
1631 chunk < (s->first_merging_chunk +
1632 s->num_merging_chunks)) {
1633 bio->bi_bdev = s->origin->bdev;
1634 bio_list_add(&s->bios_queued_during_merge, bio);
1635 r = DM_MAPIO_SUBMITTED;
1636 goto out_unlock;
1637 }
1573 remap_exception(s, e, bio, chunk); 1638 remap_exception(s, e, bio, chunk);
1574 goto out_unlock; 1639 goto out_unlock;
1575 } 1640 }
@@ -1577,12 +1642,12 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
1577 bio->bi_bdev = s->origin->bdev; 1642 bio->bi_bdev = s->origin->bdev;
1578 1643
1579 if (bio_rw(bio) == WRITE) { 1644 if (bio_rw(bio) == WRITE) {
1580 up_read(&s->lock); 1645 up_write(&s->lock);
1581 return do_origin(s->origin, bio); 1646 return do_origin(s->origin, bio);
1582 } 1647 }
1583 1648
1584out_unlock: 1649out_unlock:
1585 up_read(&s->lock); 1650 up_write(&s->lock);
1586 1651
1587 return r; 1652 return r;
1588} 1653}