Replace problematic bi_idx usage in crypt_free_buffer_pages with a bi_vcnt
and bi_size based solution until issues in the kernel code are sorted out.

[Christophe Saout]
--- diff/drivers/md/dm-crypt.c	2004-01-03 12:14:42.000000000 +0000
+++ source/drivers/md/dm-crypt.c	2004-01-03 12:14:48.000000000 +0000
@@ -272,17 +272,35 @@
 static void crypt_free_buffer_pages(struct crypt_config *cc,
                                     struct bio *bio, unsigned int bytes)
 {
-	int i = bio->bi_idx;
+	unsigned int start, end;
+	struct bio_vec *bv;
+	int i;
 
-	while(bytes) {
-		struct bio_vec *bv = bio_iovec_idx(bio, i++);
-		if (bytes < bv->bv_len)
-			break;
+	/*
+	 * This is ugly, but Jens Axboe thinks that using bi_idx in the
+	 * endio function is too dangerous at the moment, so I calculate the
+	 * correct position using bi_vcnt and bi_size.
+	 * The bv_offset and bv_len fields might already be modified but we
+	 * know that we always allocated whole pages.
+	 * A fix to the bi_idx issue in the kernel is in the works, so
+	 * we will hopefully be able to revert to the cleaner solution soon.
+	 */
+	i = bio->bi_vcnt - 1;
+	bv = bio_iovec_idx(bio, i);
+	end = (i << PAGE_SHIFT) + (bv->bv_offset + bv->bv_len) - bio->bi_size;
+	start = end - bytes;
+
+	start >>= PAGE_SHIFT;
+	if (!bio->bi_size)
+		end = bio->bi_vcnt;
+	else
+		end >>= PAGE_SHIFT;
 
+	for(i = start; i < end; i++) {
+		bv = bio_iovec_idx(bio, i);
 		BUG_ON(!bv->bv_page);
 		mempool_free(bv->bv_page, cc->page_pool);
 		bv->bv_page = NULL;
-		bytes -= bv->bv_len;
 	}
 }
 
