|
@@ -28,7 +28,6 @@
|
|
|
#include <linux/mempool.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
#include <linux/cgroup.h>
|
|
|
-#include <scsi/sg.h> /* for struct sg_iovec */
|
|
|
|
|
|
#include <trace/events/block.h>
|
|
|
|
|
@@ -1022,21 +1021,11 @@ void bio_copy_data(struct bio *dst, struct bio *src)
|
|
|
EXPORT_SYMBOL(bio_copy_data);
|
|
|
|
|
|
struct bio_map_data {
|
|
|
- int nr_sgvecs;
|
|
|
int is_our_pages;
|
|
|
- struct sg_iovec sgvecs[];
|
|
|
+ struct iov_iter iter;
|
|
|
+ struct iovec iov[];
|
|
|
};
|
|
|
|
|
|
-static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio,
|
|
|
- const struct sg_iovec *iov, int iov_count,
|
|
|
- int is_our_pages)
|
|
|
-{
|
|
|
- memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count);
|
|
|
- bmd->nr_sgvecs = iov_count;
|
|
|
- bmd->is_our_pages = is_our_pages;
|
|
|
- bio->bi_private = bmd;
|
|
|
-}
|
|
|
-
|
|
|
static struct bio_map_data *bio_alloc_map_data(unsigned int iov_count,
|
|
|
gfp_t gfp_mask)
|
|
|
{
|
|
@@ -1044,85 +1033,101 @@ static struct bio_map_data *bio_alloc_map_data(unsigned int iov_count,
|
|
|
return NULL;
|
|
|
|
|
|
return kmalloc(sizeof(struct bio_map_data) +
|
|
|
- sizeof(struct sg_iovec) * iov_count, gfp_mask);
|
|
|
+ sizeof(struct iovec) * iov_count, gfp_mask);
|
|
|
}
|
|
|
|
|
|
-static int __bio_copy_iov(struct bio *bio, const struct sg_iovec *iov, int iov_count,
|
|
|
- int to_user, int from_user, int do_free_page)
|
|
|
+/**
|
|
|
+ * bio_copy_from_iter - copy all pages from iov_iter to bio
|
|
|
+ * @bio: The &struct bio which describes the I/O as destination
|
|
|
+ * @iter: iov_iter as source
|
|
|
+ *
|
|
|
+ * Copy all pages from iov_iter to bio.
|
|
|
+ * Returns 0 on success, or error on failure.
|
|
|
+ */
|
|
|
+static int bio_copy_from_iter(struct bio *bio, struct iov_iter iter)
|
|
|
{
|
|
|
- int ret = 0, i;
|
|
|
+ int i;
|
|
|
struct bio_vec *bvec;
|
|
|
- int iov_idx = 0;
|
|
|
- unsigned int iov_off = 0;
|
|
|
|
|
|
bio_for_each_segment_all(bvec, bio, i) {
|
|
|
- char *bv_addr = page_address(bvec->bv_page);
|
|
|
- unsigned int bv_len = bvec->bv_len;
|
|
|
+ ssize_t ret;
|
|
|
|
|
|
- while (bv_len && iov_idx < iov_count) {
|
|
|
- unsigned int bytes;
|
|
|
- char __user *iov_addr;
|
|
|
+ ret = copy_page_from_iter(bvec->bv_page,
|
|
|
+ bvec->bv_offset,
|
|
|
+ bvec->bv_len,
|
|
|
+ &iter);
|
|
|
|
|
|
- bytes = min_t(unsigned int,
|
|
|
- iov[iov_idx].iov_len - iov_off, bv_len);
|
|
|
- iov_addr = iov[iov_idx].iov_base + iov_off;
|
|
|
+ if (!iov_iter_count(&iter))
|
|
|
+ break;
|
|
|
|
|
|
- if (!ret) {
|
|
|
- if (to_user)
|
|
|
- ret = copy_to_user(iov_addr, bv_addr,
|
|
|
- bytes);
|
|
|
+ if (ret < bvec->bv_len)
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
|
|
|
- if (from_user)
|
|
|
- ret = copy_from_user(bv_addr, iov_addr,
|
|
|
- bytes);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- if (ret)
|
|
|
- ret = -EFAULT;
|
|
|
- }
|
|
|
+/**
|
|
|
+ * bio_copy_to_iter - copy all pages from bio to iov_iter
|
|
|
+ * @bio: The &struct bio which describes the I/O as source
|
|
|
+ * @iter: iov_iter as destination
|
|
|
+ *
|
|
|
+ * Copy all pages from bio to iov_iter.
|
|
|
+ * Returns 0 on success, or error on failure.
|
|
|
+ */
|
|
|
+static int bio_copy_to_iter(struct bio *bio, struct iov_iter iter)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct bio_vec *bvec;
|
|
|
|
|
|
- bv_len -= bytes;
|
|
|
- bv_addr += bytes;
|
|
|
- iov_addr += bytes;
|
|
|
- iov_off += bytes;
|
|
|
+ bio_for_each_segment_all(bvec, bio, i) {
|
|
|
+ ssize_t ret;
|
|
|
|
|
|
- if (iov[iov_idx].iov_len == iov_off) {
|
|
|
- iov_idx++;
|
|
|
- iov_off = 0;
|
|
|
- }
|
|
|
- }
|
|
|
+ ret = copy_page_to_iter(bvec->bv_page,
|
|
|
+ bvec->bv_offset,
|
|
|
+ bvec->bv_len,
|
|
|
+ &iter);
|
|
|
+
|
|
|
+ if (!iov_iter_count(&iter))
|
|
|
+ break;
|
|
|
|
|
|
- if (do_free_page)
|
|
|
- __free_page(bvec->bv_page);
|
|
|
+ if (ret < bvec->bv_len)
|
|
|
+ return -EFAULT;
|
|
|
}
|
|
|
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void bio_free_pages(struct bio *bio)
|
|
|
+{
|
|
|
+ struct bio_vec *bvec;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ bio_for_each_segment_all(bvec, bio, i)
|
|
|
+ __free_page(bvec->bv_page);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* bio_uncopy_user - finish previously mapped bio
|
|
|
* @bio: bio being terminated
|
|
|
*
|
|
|
- * Free pages allocated from bio_copy_user() and write back data
|
|
|
+ * Free pages allocated from bio_copy_user_iov() and write back data
|
|
|
* to user space in case of a read.
|
|
|
*/
|
|
|
int bio_uncopy_user(struct bio *bio)
|
|
|
{
|
|
|
struct bio_map_data *bmd = bio->bi_private;
|
|
|
- struct bio_vec *bvec;
|
|
|
- int ret = 0, i;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
if (!bio_flagged(bio, BIO_NULL_MAPPED)) {
|
|
|
/*
|
|
|
* if we're in a workqueue, the request is orphaned, so
|
|
|
* don't copy into a random user address space, just free.
|
|
|
*/
|
|
|
- if (current->mm)
|
|
|
- ret = __bio_copy_iov(bio, bmd->sgvecs, bmd->nr_sgvecs,
|
|
|
- bio_data_dir(bio) == READ,
|
|
|
- 0, bmd->is_our_pages);
|
|
|
- else if (bmd->is_our_pages)
|
|
|
- bio_for_each_segment_all(bvec, bio, i)
|
|
|
- __free_page(bvec->bv_page);
|
|
|
+ if (current->mm && bio_data_dir(bio) == READ)
|
|
|
+ ret = bio_copy_to_iter(bio, bmd->iter);
|
|
|
+ if (bmd->is_our_pages)
|
|
|
+ bio_free_pages(bio);
|
|
|
}
|
|
|
kfree(bmd);
|
|
|
bio_put(bio);
|
|
@@ -1132,12 +1137,10 @@ EXPORT_SYMBOL(bio_uncopy_user);
|
|
|
|
|
|
/**
|
|
|
* bio_copy_user_iov - copy user data to bio
|
|
|
- * @q: destination block queue
|
|
|
- * @map_data: pointer to the rq_map_data holding pages (if necessary)
|
|
|
- * @iov: the iovec.
|
|
|
- * @iov_count: number of elements in the iovec
|
|
|
- * @write_to_vm: bool indicating writing to pages or not
|
|
|
- * @gfp_mask: memory allocation flags
|
|
|
+ * @q: destination block queue
|
|
|
+ * @map_data: pointer to the rq_map_data holding pages (if necessary)
|
|
|
+ * @iter: iovec iterator
|
|
|
+ * @gfp_mask: memory allocation flags
|
|
|
*
|
|
|
* Prepares and returns a bio for indirect user io, bouncing data
|
|
|
* to/from kernel pages as necessary. Must be paired with
|
|
@@ -1145,25 +1148,25 @@ EXPORT_SYMBOL(bio_uncopy_user);
|
|
|
*/
|
|
|
struct bio *bio_copy_user_iov(struct request_queue *q,
|
|
|
struct rq_map_data *map_data,
|
|
|
- const struct sg_iovec *iov, int iov_count,
|
|
|
- int write_to_vm, gfp_t gfp_mask)
|
|
|
+ const struct iov_iter *iter,
|
|
|
+ gfp_t gfp_mask)
|
|
|
{
|
|
|
struct bio_map_data *bmd;
|
|
|
- struct bio_vec *bvec;
|
|
|
struct page *page;
|
|
|
struct bio *bio;
|
|
|
int i, ret;
|
|
|
int nr_pages = 0;
|
|
|
- unsigned int len = 0;
|
|
|
+ unsigned int len = iter->count;
|
|
|
unsigned int offset = map_data ? map_data->offset & ~PAGE_MASK : 0;
|
|
|
|
|
|
- for (i = 0; i < iov_count; i++) {
|
|
|
+ for (i = 0; i < iter->nr_segs; i++) {
|
|
|
unsigned long uaddr;
|
|
|
unsigned long end;
|
|
|
unsigned long start;
|
|
|
|
|
|
- uaddr = (unsigned long)iov[i].iov_base;
|
|
|
- end = (uaddr + iov[i].iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
+ uaddr = (unsigned long) iter->iov[i].iov_base;
|
|
|
+ end = (uaddr + iter->iov[i].iov_len + PAGE_SIZE - 1)
|
|
|
+ >> PAGE_SHIFT;
|
|
|
start = uaddr >> PAGE_SHIFT;
|
|
|
|
|
|
/*
|
|
@@ -1173,22 +1176,31 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
nr_pages += end - start;
|
|
|
- len += iov[i].iov_len;
|
|
|
}
|
|
|
|
|
|
if (offset)
|
|
|
nr_pages++;
|
|
|
|
|
|
- bmd = bio_alloc_map_data(iov_count, gfp_mask);
|
|
|
+ bmd = bio_alloc_map_data(iter->nr_segs, gfp_mask);
|
|
|
if (!bmd)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
+ /*
|
|
|
+ * We need to do a deep copy of the iov_iter including the iovecs.
|
|
|
+ * The caller provided iov might point to an on-stack or otherwise
|
|
|
+ * shortlived one.
|
|
|
+ */
|
|
|
+ bmd->is_our_pages = map_data ? 0 : 1;
|
|
|
+ memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs);
|
|
|
+ iov_iter_init(&bmd->iter, iter->type, bmd->iov,
|
|
|
+ iter->nr_segs, iter->count);
|
|
|
+
|
|
|
ret = -ENOMEM;
|
|
|
bio = bio_kmalloc(gfp_mask, nr_pages);
|
|
|
if (!bio)
|
|
|
goto out_bmd;
|
|
|
|
|
|
- if (!write_to_vm)
|
|
|
+ if (iter->type & WRITE)
|
|
|
bio->bi_rw |= REQ_WRITE;
|
|
|
|
|
|
ret = 0;
|
|
@@ -1236,20 +1248,18 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
|
|
|
/*
|
|
|
* success
|
|
|
*/
|
|
|
- if ((!write_to_vm && (!map_data || !map_data->null_mapped)) ||
|
|
|
+ if (((iter->type & WRITE) && (!map_data || !map_data->null_mapped)) ||
|
|
|
(map_data && map_data->from_user)) {
|
|
|
- ret = __bio_copy_iov(bio, iov, iov_count, 0, 1, 0);
|
|
|
+ ret = bio_copy_from_iter(bio, *iter);
|
|
|
if (ret)
|
|
|
goto cleanup;
|
|
|
}
|
|
|
|
|
|
- bio_set_map_data(bmd, bio, iov, iov_count, map_data ? 0 : 1);
|
|
|
+ bio->bi_private = bmd;
|
|
|
return bio;
|
|
|
cleanup:
|
|
|
if (!map_data)
|
|
|
- bio_for_each_segment_all(bvec, bio, i)
|
|
|
- __free_page(bvec->bv_page);
|
|
|
-
|
|
|
+ bio_free_pages(bio);
|
|
|
bio_put(bio);
|
|
|
out_bmd:
|
|
|
kfree(bmd);
|
|
@@ -1257,46 +1267,30 @@ out_bmd:
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * bio_copy_user - copy user data to bio
|
|
|
- * @q: destination block queue
|
|
|
- * @map_data: pointer to the rq_map_data holding pages (if necessary)
|
|
|
- * @uaddr: start of user address
|
|
|
- * @len: length in bytes
|
|
|
- * @write_to_vm: bool indicating writing to pages or not
|
|
|
- * @gfp_mask: memory allocation flags
|
|
|
+ * bio_map_user_iov - map user iovec into bio
|
|
|
+ * @q: the struct request_queue for the bio
|
|
|
+ * @iter: iovec iterator
|
|
|
+ * @gfp_mask: memory allocation flags
|
|
|
*
|
|
|
- * Prepares and returns a bio for indirect user io, bouncing data
|
|
|
- * to/from kernel pages as necessary. Must be paired with
|
|
|
- * call bio_uncopy_user() on io completion.
|
|
|
+ * Map the user space address into a bio suitable for io to a block
|
|
|
+ * device. Returns an error pointer in case of error.
|
|
|
*/
|
|
|
-struct bio *bio_copy_user(struct request_queue *q, struct rq_map_data *map_data,
|
|
|
- unsigned long uaddr, unsigned int len,
|
|
|
- int write_to_vm, gfp_t gfp_mask)
|
|
|
+struct bio *bio_map_user_iov(struct request_queue *q,
|
|
|
+ const struct iov_iter *iter,
|
|
|
+ gfp_t gfp_mask)
|
|
|
{
|
|
|
- struct sg_iovec iov;
|
|
|
-
|
|
|
- iov.iov_base = (void __user *)uaddr;
|
|
|
- iov.iov_len = len;
|
|
|
-
|
|
|
- return bio_copy_user_iov(q, map_data, &iov, 1, write_to_vm, gfp_mask);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(bio_copy_user);
|
|
|
-
|
|
|
-static struct bio *__bio_map_user_iov(struct request_queue *q,
|
|
|
- struct block_device *bdev,
|
|
|
- const struct sg_iovec *iov, int iov_count,
|
|
|
- int write_to_vm, gfp_t gfp_mask)
|
|
|
-{
|
|
|
- int i, j;
|
|
|
+ int j;
|
|
|
int nr_pages = 0;
|
|
|
struct page **pages;
|
|
|
struct bio *bio;
|
|
|
int cur_page = 0;
|
|
|
int ret, offset;
|
|
|
+ struct iov_iter i;
|
|
|
+ struct iovec iov;
|
|
|
|
|
|
- for (i = 0; i < iov_count; i++) {
|
|
|
- unsigned long uaddr = (unsigned long)iov[i].iov_base;
|
|
|
- unsigned long len = iov[i].iov_len;
|
|
|
+ iov_for_each(iov, i, *iter) {
|
|
|
+ unsigned long uaddr = (unsigned long) iov.iov_base;
|
|
|
+ unsigned long len = iov.iov_len;
|
|
|
unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
unsigned long start = uaddr >> PAGE_SHIFT;
|
|
|
|
|
@@ -1326,16 +1320,17 @@ static struct bio *__bio_map_user_iov(struct request_queue *q,
|
|
|
if (!pages)
|
|
|
goto out;
|
|
|
|
|
|
- for (i = 0; i < iov_count; i++) {
|
|
|
- unsigned long uaddr = (unsigned long)iov[i].iov_base;
|
|
|
- unsigned long len = iov[i].iov_len;
|
|
|
+ iov_for_each(iov, i, *iter) {
|
|
|
+ unsigned long uaddr = (unsigned long) iov.iov_base;
|
|
|
+ unsigned long len = iov.iov_len;
|
|
|
unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
unsigned long start = uaddr >> PAGE_SHIFT;
|
|
|
const int local_nr_pages = end - start;
|
|
|
const int page_limit = cur_page + local_nr_pages;
|
|
|
|
|
|
ret = get_user_pages_fast(uaddr, local_nr_pages,
|
|
|
- write_to_vm, &pages[cur_page]);
|
|
|
+ (iter->type & WRITE) != WRITE,
|
|
|
+ &pages[cur_page]);
|
|
|
if (ret < local_nr_pages) {
|
|
|
ret = -EFAULT;
|
|
|
goto out_unmap;
|
|
@@ -1375,72 +1370,10 @@ static struct bio *__bio_map_user_iov(struct request_queue *q,
|
|
|
/*
|
|
|
* set data direction, and check if mapped pages need bouncing
|
|
|
*/
|
|
|
- if (!write_to_vm)
|
|
|
+ if (iter->type & WRITE)
|
|
|
bio->bi_rw |= REQ_WRITE;
|
|
|
|
|
|
- bio->bi_bdev = bdev;
|
|
|
bio->bi_flags |= (1 << BIO_USER_MAPPED);
|
|
|
- return bio;
|
|
|
-
|
|
|
- out_unmap:
|
|
|
- for (i = 0; i < nr_pages; i++) {
|
|
|
- if(!pages[i])
|
|
|
- break;
|
|
|
- page_cache_release(pages[i]);
|
|
|
- }
|
|
|
- out:
|
|
|
- kfree(pages);
|
|
|
- bio_put(bio);
|
|
|
- return ERR_PTR(ret);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * bio_map_user - map user address into bio
|
|
|
- * @q: the struct request_queue for the bio
|
|
|
- * @bdev: destination block device
|
|
|
- * @uaddr: start of user address
|
|
|
- * @len: length in bytes
|
|
|
- * @write_to_vm: bool indicating writing to pages or not
|
|
|
- * @gfp_mask: memory allocation flags
|
|
|
- *
|
|
|
- * Map the user space address into a bio suitable for io to a block
|
|
|
- * device. Returns an error pointer in case of error.
|
|
|
- */
|
|
|
-struct bio *bio_map_user(struct request_queue *q, struct block_device *bdev,
|
|
|
- unsigned long uaddr, unsigned int len, int write_to_vm,
|
|
|
- gfp_t gfp_mask)
|
|
|
-{
|
|
|
- struct sg_iovec iov;
|
|
|
-
|
|
|
- iov.iov_base = (void __user *)uaddr;
|
|
|
- iov.iov_len = len;
|
|
|
-
|
|
|
- return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm, gfp_mask);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(bio_map_user);
|
|
|
-
|
|
|
-/**
|
|
|
- * bio_map_user_iov - map user sg_iovec table into bio
|
|
|
- * @q: the struct request_queue for the bio
|
|
|
- * @bdev: destination block device
|
|
|
- * @iov: the iovec.
|
|
|
- * @iov_count: number of elements in the iovec
|
|
|
- * @write_to_vm: bool indicating writing to pages or not
|
|
|
- * @gfp_mask: memory allocation flags
|
|
|
- *
|
|
|
- * Map the user space address into a bio suitable for io to a block
|
|
|
- * device. Returns an error pointer in case of error.
|
|
|
- */
|
|
|
-struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev,
|
|
|
- const struct sg_iovec *iov, int iov_count,
|
|
|
- int write_to_vm, gfp_t gfp_mask)
|
|
|
-{
|
|
|
- struct bio *bio;
|
|
|
-
|
|
|
- bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm,
|
|
|
- gfp_mask);
|
|
|
- if (IS_ERR(bio))
|
|
|
- return bio;
|
|
|
|
|
|
/*
|
|
|
* subtle -- if __bio_map_user() ended up bouncing a bio,
|
|
@@ -1449,8 +1382,18 @@ struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev,
|
|
|
* reference to it
|
|
|
*/
|
|
|
bio_get(bio);
|
|
|
-
|
|
|
return bio;
|
|
|
+
|
|
|
+ out_unmap:
|
|
|
+ for (j = 0; j < nr_pages; j++) {
|
|
|
+ if (!pages[j])
|
|
|
+ break;
|
|
|
+ page_cache_release(pages[j]);
|
|
|
+ }
|
|
|
+ out:
|
|
|
+ kfree(pages);
|
|
|
+ bio_put(bio);
|
|
|
+ return ERR_PTR(ret);
|
|
|
}
|
|
|
|
|
|
static void __bio_unmap_user(struct bio *bio)
|
|
@@ -1492,8 +1435,18 @@ static void bio_map_kern_endio(struct bio *bio, int err)
|
|
|
bio_put(bio);
|
|
|
}
|
|
|
|
|
|
-static struct bio *__bio_map_kern(struct request_queue *q, void *data,
|
|
|
- unsigned int len, gfp_t gfp_mask)
|
|
|
+/**
|
|
|
+ * bio_map_kern - map kernel address into bio
|
|
|
+ * @q: the struct request_queue for the bio
|
|
|
+ * @data: pointer to buffer to map
|
|
|
+ * @len: length in bytes
|
|
|
+ * @gfp_mask: allocation flags for bio allocation
|
|
|
+ *
|
|
|
+ * Map the kernel address into a bio suitable for io to a block
|
|
|
+ * device. Returns an error pointer in case of error.
|
|
|
+ */
|
|
|
+struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
|
|
|
+ gfp_t gfp_mask)
|
|
|
{
|
|
|
unsigned long kaddr = (unsigned long)data;
|
|
|
unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
@@ -1517,8 +1470,11 @@ static struct bio *__bio_map_kern(struct request_queue *q, void *data,
|
|
|
bytes = len;
|
|
|
|
|
|
if (bio_add_pc_page(q, bio, virt_to_page(data), bytes,
|
|
|
- offset) < bytes)
|
|
|
- break;
|
|
|
+ offset) < bytes) {
|
|
|
+ /* we don't support partial mappings */
|
|
|
+ bio_put(bio);
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
|
|
|
data += bytes;
|
|
|
len -= bytes;
|
|
@@ -1528,57 +1484,26 @@ static struct bio *__bio_map_kern(struct request_queue *q, void *data,
|
|
|
bio->bi_end_io = bio_map_kern_endio;
|
|
|
return bio;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(bio_map_kern);
|
|
|
|
|
|
-/**
|
|
|
- * bio_map_kern - map kernel address into bio
|
|
|
- * @q: the struct request_queue for the bio
|
|
|
- * @data: pointer to buffer to map
|
|
|
- * @len: length in bytes
|
|
|
- * @gfp_mask: allocation flags for bio allocation
|
|
|
- *
|
|
|
- * Map the kernel address into a bio suitable for io to a block
|
|
|
- * device. Returns an error pointer in case of error.
|
|
|
- */
|
|
|
-struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
|
|
|
- gfp_t gfp_mask)
|
|
|
+static void bio_copy_kern_endio(struct bio *bio, int err)
|
|
|
{
|
|
|
- struct bio *bio;
|
|
|
-
|
|
|
- bio = __bio_map_kern(q, data, len, gfp_mask);
|
|
|
- if (IS_ERR(bio))
|
|
|
- return bio;
|
|
|
-
|
|
|
- if (bio->bi_iter.bi_size == len)
|
|
|
- return bio;
|
|
|
-
|
|
|
- /*
|
|
|
- * Don't support partial mappings.
|
|
|
- */
|
|
|
+ bio_free_pages(bio);
|
|
|
bio_put(bio);
|
|
|
- return ERR_PTR(-EINVAL);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(bio_map_kern);
|
|
|
|
|
|
-static void bio_copy_kern_endio(struct bio *bio, int err)
|
|
|
+static void bio_copy_kern_endio_read(struct bio *bio, int err)
|
|
|
{
|
|
|
+ char *p = bio->bi_private;
|
|
|
struct bio_vec *bvec;
|
|
|
- const int read = bio_data_dir(bio) == READ;
|
|
|
- struct bio_map_data *bmd = bio->bi_private;
|
|
|
int i;
|
|
|
- char *p = bmd->sgvecs[0].iov_base;
|
|
|
|
|
|
bio_for_each_segment_all(bvec, bio, i) {
|
|
|
- char *addr = page_address(bvec->bv_page);
|
|
|
-
|
|
|
- if (read)
|
|
|
- memcpy(p, addr, bvec->bv_len);
|
|
|
-
|
|
|
- __free_page(bvec->bv_page);
|
|
|
+ memcpy(p, page_address(bvec->bv_page), bvec->bv_len);
|
|
|
p += bvec->bv_len;
|
|
|
}
|
|
|
|
|
|
- kfree(bmd);
|
|
|
- bio_put(bio);
|
|
|
+ bio_copy_kern_endio(bio, err);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1595,28 +1520,59 @@ 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,
|
|
|
gfp_t gfp_mask, int reading)
|
|
|
{
|
|
|
+ unsigned long kaddr = (unsigned long)data;
|
|
|
+ unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
+ unsigned long start = kaddr >> PAGE_SHIFT;
|
|
|
struct bio *bio;
|
|
|
- struct bio_vec *bvec;
|
|
|
- int i;
|
|
|
+ void *p = data;
|
|
|
+ int nr_pages = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Overflow, abort
|
|
|
+ */
|
|
|
+ if (end < start)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- bio = bio_copy_user(q, NULL, (unsigned long)data, len, 1, gfp_mask);
|
|
|
- if (IS_ERR(bio))
|
|
|
- return bio;
|
|
|
+ nr_pages = end - start;
|
|
|
+ bio = bio_kmalloc(gfp_mask, nr_pages);
|
|
|
+ if (!bio)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- if (!reading) {
|
|
|
- void *p = data;
|
|
|
+ while (len) {
|
|
|
+ struct page *page;
|
|
|
+ unsigned int bytes = PAGE_SIZE;
|
|
|
|
|
|
- bio_for_each_segment_all(bvec, bio, i) {
|
|
|
- char *addr = page_address(bvec->bv_page);
|
|
|
+ if (bytes > len)
|
|
|
+ bytes = len;
|
|
|
|
|
|
- memcpy(addr, p, bvec->bv_len);
|
|
|
- p += bvec->bv_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_end_io = bio_copy_kern_endio_read;
|
|
|
+ bio->bi_private = data;
|
|
|
+ } else {
|
|
|
+ bio->bi_end_io = bio_copy_kern_endio;
|
|
|
+ bio->bi_rw |= REQ_WRITE;
|
|
|
+ }
|
|
|
|
|
|
return bio;
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ bio_free_pages(bio);
|
|
|
+ bio_put(bio);
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
EXPORT_SYMBOL(bio_copy_kern);
|
|
|
|