block: simplify bio_map_kern

Just open code the trivial mapping from a kernel virtual address to
a bio instead of going through the complex user address mapping
machinery.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <tom.leiming@gmail.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
Christoph Hellwig 2015-01-18 16:16:28 +01:00 committed by Jens Axboe
parent ad9cf3bbd1
commit 42d2683a27
1 changed files with 44 additions and 16 deletions

View File

@ -1563,9 +1563,8 @@ static void bio_copy_kern_endio(struct bio *bio, int err)
{ {
struct bio_vec *bvec; struct bio_vec *bvec;
const int read = bio_data_dir(bio) == READ; const int read = bio_data_dir(bio) == READ;
struct bio_map_data *bmd = bio->bi_private; char *p = bio->bi_private;
int i; int i;
char *p = bmd->sgvecs[0].iov_base;
bio_for_each_segment_all(bvec, bio, i) { bio_for_each_segment_all(bvec, bio, i) {
char *addr = page_address(bvec->bv_page); char *addr = page_address(bvec->bv_page);
@ -1577,7 +1576,6 @@ static void bio_copy_kern_endio(struct bio *bio, int err)
p += bvec->bv_len; p += bvec->bv_len;
} }
kfree(bmd);
bio_put(bio); bio_put(bio);
} }
@ -1595,28 +1593,58 @@ static void bio_copy_kern_endio(struct bio *bio, int err)
struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len,
gfp_t gfp_mask, int reading) gfp_t gfp_mask, int reading)
{ {
struct bio *bio; unsigned long kaddr = (unsigned long)data;
unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
unsigned long start = kaddr >> PAGE_SHIFT;
struct bio_vec *bvec; struct bio_vec *bvec;
int i; struct bio *bio;
void *p = data;
int nr_pages = 0, i;
bio = bio_copy_user(q, NULL, (unsigned long)data, len, 1, gfp_mask); /*
if (IS_ERR(bio)) * Overflow, abort
return bio; */
if (end < start)
return ERR_PTR(-EINVAL);
if (!reading) { nr_pages = end - start;
void *p = data; bio = bio_kmalloc(gfp_mask, nr_pages);
if (!bio)
return ERR_PTR(-ENOMEM);
bio_for_each_segment_all(bvec, bio, i) { while (len) {
char *addr = page_address(bvec->bv_page); struct page *page;
unsigned int bytes = PAGE_SIZE;
memcpy(addr, p, bvec->bv_len); if (bytes > len)
p += bvec->bv_len; bytes = len;
}
page = alloc_page(q->bounce_gfp | gfp_mask);
if (!page)
goto cleanup;
if (!reading)
memcpy(page_address(page), p, bytes);
if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes)
break;
len -= bytes;
p += bytes;
} }
bio->bi_end_io = bio_copy_kern_endio; if (!reading)
bio->bi_rw |= REQ_WRITE;
bio->bi_private = data;
bio->bi_end_io = bio_copy_kern_endio;
return bio; return bio;
cleanup:
bio_for_each_segment_all(bvec, bio, i)
__free_page(bvec->bv_page);
bio_put(bio);
return ERR_PTR(-ENOMEM);
} }
EXPORT_SYMBOL(bio_copy_kern); EXPORT_SYMBOL(bio_copy_kern);