diff --git a/block/blk-map.c b/block/blk-map.c index 2990447f45e9..c7e55b23a2bc 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -150,6 +150,9 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, bio = rq->bio; bytes_read += ret; ubuf += ret; + + if (map_data) + map_data->offset += ret; } if (!bio_flagged(bio, BIO_USER_MAPPED)) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 5103855242ae..7d0b3d9ee43b 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1669,6 +1669,7 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) md->pages = req_schp->pages; md->page_order = req_schp->page_order; md->nr_entries = req_schp->k_use_sg; + md->offset = 0; } if (iov_count) diff --git a/fs/bio.c b/fs/bio.c index 356e7423b923..13be075806b6 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -788,6 +788,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, int i, ret; int nr_pages = 0; unsigned int len = 0; + unsigned int offset = map_data ? map_data->offset & ~PAGE_MASK : 0; for (i = 0; i < iov_count; i++) { unsigned long uaddr; @@ -814,12 +815,16 @@ struct bio *bio_copy_user_iov(struct request_queue *q, bio->bi_rw |= (!write_to_vm << BIO_RW); ret = 0; - i = 0; - if (map_data) + + if (map_data) { nr_pages = 1 << map_data->page_order; + i = map_data->offset / PAGE_SIZE; + } while (len) { unsigned int bytes = PAGE_SIZE; + bytes -= offset; + if (bytes > len) bytes = len; @@ -841,10 +846,11 @@ struct bio *bio_copy_user_iov(struct request_queue *q, } } - if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) + if (bio_add_pc_page(q, bio, page, bytes, offset) < bytes) break; len -= bytes; + offset = 0; } if (ret) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 7035cec583b6..811e5342c452 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -690,6 +690,7 @@ struct rq_map_data { struct page **pages; int page_order; int nr_entries; + unsigned long offset; }; struct req_iterator {