Commit d68017fa authored by Joe Thornber's avatar Joe Thornber Committed by Linus Torvalds

[PATCH] dm: bio split fix

The block layer does not honour bio->bi_size when issuing io, instead
it performs io to the complete bvecs.  This means we have to change
the bio splitting code slightly.

Given a bio we repeatedly apply one of the following three operations
until there is no more io left in the bio:

1) The remaining io does not cross an io/target boundary, so just
   create a clone and issue all of the io.

2) There are some bvecs at the start of the bio that are not split by
   a target boundary.  Create a clone for these bvecs only.

3) The first bvec needs splitting, use bio_alloc() to create *two*
   bios, one for the first half of the bvec, the other for the second
   half.  A bvec can never contain more than one boundary.
parent bef272c4
...@@ -228,6 +228,15 @@ static int queue_io(struct mapped_device *md, struct bio *bio) ...@@ -228,6 +228,15 @@ static int queue_io(struct mapped_device *md, struct bio *bio)
* interests of getting something for people to use I give * interests of getting something for people to use I give
* you this clearly demarcated crap. * you this clearly demarcated crap.
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
static inline sector_t to_sector(unsigned int bytes)
{
return bytes >> SECTOR_SHIFT;
}
static inline unsigned int to_bytes(sector_t sector)
{
return sector << SECTOR_SHIFT;
}
/* /*
* Decrements the number of outstanding ios that a bio has been * Decrements the number of outstanding ios that a bio has been
...@@ -270,16 +279,17 @@ static int clone_endio(struct bio *bio, unsigned int done, int error) ...@@ -270,16 +279,17 @@ static int clone_endio(struct bio *bio, unsigned int done, int error)
static sector_t max_io_len(struct mapped_device *md, static sector_t max_io_len(struct mapped_device *md,
sector_t sector, struct dm_target *ti) sector_t sector, struct dm_target *ti)
{ {
sector_t len = ti->len; sector_t offset = sector - ti->begin;
sector_t len = ti->len - offset;
/* FIXME: obey io_restrictions ! */ /* FIXME: obey io_restrictions ! */
/* /*
* Does the target need to split even further ? * Does the target need to split even further ?
*/ */
if (ti->split_io) { if (ti->split_io) {
sector_t boundary; sector_t boundary;
sector_t offset = sector - ti->begin;
boundary = dm_round_up(offset + 1, ti->split_io) - offset; boundary = dm_round_up(offset + 1, ti->split_io) - offset;
if (len > boundary) if (len > boundary)
...@@ -289,16 +299,17 @@ static sector_t max_io_len(struct mapped_device *md, ...@@ -289,16 +299,17 @@ static sector_t max_io_len(struct mapped_device *md,
return len; return len;
} }
static void __map_bio(struct dm_target *ti, struct bio *clone) static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_io *io)
{ {
struct dm_io *io = clone->bi_private;
int r; int r;
/* /*
* Sanity checks. * Sanity checks.
*/ */
if (!clone->bi_size) BUG_ON(!clone->bi_size);
BUG();
clone->bi_end_io = clone_endio;
clone->bi_private = io;
/* /*
* Map the clone. If r == 0 we don't need to do * Map the clone. If r == 0 we don't need to do
...@@ -326,77 +337,125 @@ struct clone_info { ...@@ -326,77 +337,125 @@ struct clone_info {
}; };
/* /*
* Issues a little bio that just does the back end of a split page. * Creates a little bio that is just does part of a bvec.
*/ */
static void __split_page(struct clone_info *ci, unsigned int len) static struct bio *split_bvec(struct bio *bio, sector_t sector,
unsigned short idx, unsigned int offset,
unsigned int len)
{ {
struct dm_target *ti = dm_table_find_target(ci->md->map, ci->sector); struct bio *clone;
struct bio *clone, *bio = ci->bio; struct bio_vec *bv = bio->bi_io_vec + idx;
struct bio_vec *bv = bio->bi_io_vec + ci->idx;
if (len > ci->sector_count)
len = ci->sector_count;
clone = bio_alloc(GFP_NOIO, 1); clone = bio_alloc(GFP_NOIO, 1);
memcpy(clone->bi_io_vec, bv, sizeof(*bv));
clone->bi_sector = ci->sector; if (clone) {
clone->bi_bdev = bio->bi_bdev; memcpy(clone->bi_io_vec, bv, sizeof(*bv));
clone->bi_rw = bio->bi_rw;
clone->bi_vcnt = 1; clone->bi_sector = sector;
clone->bi_size = len << SECTOR_SHIFT; clone->bi_bdev = bio->bi_bdev;
clone->bi_end_io = clone_endio; clone->bi_rw = bio->bi_rw;
clone->bi_private = ci->io; clone->bi_vcnt = 1;
clone->bi_io_vec->bv_offset = bv->bv_len - clone->bi_size; clone->bi_size = to_bytes(len);
clone->bi_io_vec->bv_len = clone->bi_size; clone->bi_io_vec->bv_offset = offset;
clone->bi_io_vec->bv_len = clone->bi_size;
}
return clone;
}
ci->sector += len; /*
ci->sector_count -= len; * Creates a bio that consists of range of complete bvecs.
*/
static struct bio *clone_bio(struct bio *bio, sector_t sector,
unsigned short idx, unsigned short bv_count,
unsigned int len)
{
struct bio *clone;
__map_bio(ti, clone); clone = bio_clone(bio, GFP_NOIO);
clone->bi_sector = sector;
clone->bi_idx = idx;
clone->bi_vcnt = idx + bv_count;
clone->bi_size = to_bytes(len);
return clone;
} }
static void __clone_and_map(struct clone_info *ci) static void __clone_and_map(struct clone_info *ci)
{ {
struct bio *clone, *bio = ci->bio; struct bio *clone, *bio = ci->bio;
struct dm_target *ti = dm_table_find_target(ci->md->map, ci->sector); struct dm_target *ti = dm_table_find_target(ci->md->map, ci->sector);
sector_t len = max_io_len(ci->md, bio->bi_sector, ti); sector_t len = 0, max = max_io_len(ci->md, ci->sector, ti);
/* shorter than current target ? */ if (ci->sector_count <= max) {
if (ci->sector_count < len) /*
len = ci->sector_count; * Optimise for the simple case where we can do all of
* the remaining io with a single clone.
*/
clone = clone_bio(bio, ci->sector, ci->idx,
bio->bi_vcnt - ci->idx, ci->sector_count);
__map_bio(ti, clone, ci->io);
ci->sector_count = 0;
/* create the clone */ } else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) {
clone = bio_clone(ci->bio, GFP_NOIO); /*
clone->bi_sector = ci->sector; * There are some bvecs that don't span targets.
clone->bi_idx = ci->idx; * Do as many of these as possible.
clone->bi_size = len << SECTOR_SHIFT; */
clone->bi_end_io = clone_endio; int i;
clone->bi_private = ci->io; sector_t remaining = max;
sector_t bv_len;
/* adjust the remaining io */ for (i = ci->idx; remaining && (i < bio->bi_vcnt); i++) {
ci->sector += len; bv_len = to_sector(bio->bi_io_vec[i].bv_len);
ci->sector_count -= len;
__map_bio(ti, clone);
/* if (bv_len > remaining)
* If we are not performing all remaining io in this break;
* clone then we need to calculate ci->idx for the next
* time round. remaining -= bv_len;
*/ len += bv_len;
if (ci->sector_count) { }
while (len) {
struct bio_vec *bv = clone->bi_io_vec + ci->idx; clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len);
sector_t bv_len = bv->bv_len >> SECTOR_SHIFT; __map_bio(ti, clone, ci->io);
if (bv_len <= len)
len -= bv_len; ci->sector += len;
ci->sector_count -= len;
else { ci->idx = i;
__split_page(ci, bv_len - len);
len = 0; } else {
} /*
ci->idx++; * Create two copy bios to deal with io that has
* been split across a target.
*/
struct bio_vec *bv = bio->bi_io_vec + ci->idx;
clone = split_bvec(bio, ci->sector, ci->idx,
bv->bv_offset, max);
if (!clone) {
dec_pending(ci->io, -ENOMEM);
return;
}
__map_bio(ti, clone, ci->io);
ci->sector += max;
ci->sector_count -= max;
ti = dm_table_find_target(ci->md->map, ci->sector);
len = to_sector(bv->bv_len) - max;
clone = split_bvec(bio, ci->sector, ci->idx,
bv->bv_offset + to_bytes(max), len);
if (!clone) {
dec_pending(ci->io, -ENOMEM);
return;
} }
__map_bio(ti, clone, ci->io);
ci->sector += len;
ci->sector_count -= len;
ci->idx++;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment