aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2014-02-12 16:37:30 -0500
committerMike Snitzer <snitzer@redhat.com>2014-02-17 11:00:05 -0500
commitd73f9907294c670da14baea3fb31be27879e818e (patch)
treeaebcc204b06a8c68b003adb66623281a7fc00840
parent4d1662a30dde6e545086fe0e8fd7e474c4e0b639 (diff)
dm io: fix I/O to multiple destinations
Commit 003b5c5719f159f4f4bf97511c4702a0638313dd ("block: Convert drivers to immutable biovecs") broke dm-mirror due to dm-io breakage. dm-io had three possible iterators (DM_IO_PAGE_LIST, DM_IO_BVEC, DM_IO_VMA) that iterate over pages where the I/O should be performed. The switch to immutable biovecs changed the DM_IO_BVEC iterator to DM_IO_BIO. Before this change the iterator stored the pointer to a bio vector in the dpages structure. The iterator incremented the pointer in the dpages structure as it advanced over the pages. After the immutable biovecs change, the DM_IO_BIO iterator stores a pointer to the bio in the dpages structure and uses bio_advance to change the bio as it advances. The problem is that the function dispatch_io stores the content of the dpages structure into the variable old_pages and restores it before issuing I/O to each of the devices. Before the change, the statement "*dp = old_pages;" restored the iterator to its starting position. After the change, struct dpages holds a pointer to the bio, thus the statement "*dp = old_pages;" doesn't restore the iterator. Consequently, in the context of dm-mirror: only the first mirror leg is written correctly, the kernel locks up when trying to write the other mirror legs because the number of sectors to write in the where->count variable doesn't match the number of sectors returned by the iterator. This patch fixes the bug by partially reverting the original patch - it changes the code so that struct dpages holds a pointer to the bio vector, so that the statement "*dp = old_pages;" restores the iterator correctly. The field "context_u" holds the offset from the beginning of the current bio vector entry, just like the "bio->bi_iter.bi_bvec_done" field. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/dm-io.c23
1 files changed, 11 insertions, 12 deletions
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index b2b8a10e8427..3842ac738f98 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -201,29 +201,28 @@ static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned offse
201/* 201/*
202 * Functions for getting the pages from a bvec. 202 * Functions for getting the pages from a bvec.
203 */ 203 */
204static void bio_get_page(struct dpages *dp, 204static void bio_get_page(struct dpages *dp, struct page **p,
205 struct page **p, unsigned long *len, unsigned *offset) 205 unsigned long *len, unsigned *offset)
206{ 206{
207 struct bio *bio = dp->context_ptr; 207 struct bio_vec *bvec = dp->context_ptr;
208 struct bio_vec bvec = bio_iovec(bio); 208 *p = bvec->bv_page;
209 *p = bvec.bv_page; 209 *len = bvec->bv_len - dp->context_u;
210 *len = bvec.bv_len; 210 *offset = bvec->bv_offset + dp->context_u;
211 *offset = bvec.bv_offset;
212} 211}
213 212
214static void bio_next_page(struct dpages *dp) 213static void bio_next_page(struct dpages *dp)
215{ 214{
216 struct bio *bio = dp->context_ptr; 215 struct bio_vec *bvec = dp->context_ptr;
217 struct bio_vec bvec = bio_iovec(bio); 216 dp->context_ptr = bvec + 1;
218 217 dp->context_u = 0;
219 bio_advance(bio, bvec.bv_len);
220} 218}
221 219
222static void bio_dp_init(struct dpages *dp, struct bio *bio) 220static void bio_dp_init(struct dpages *dp, struct bio *bio)
223{ 221{
224 dp->get_page = bio_get_page; 222 dp->get_page = bio_get_page;
225 dp->next_page = bio_next_page; 223 dp->next_page = bio_next_page;
226 dp->context_ptr = bio; 224 dp->context_ptr = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
225 dp->context_u = bio->bi_iter.bi_bvec_done;
227} 226}
228 227
229/* 228/*