Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cefe4db authored by Kent Overstreet's avatar Kent Overstreet Committed by Jens Axboe
Browse files

block: add bio_iov_iter_get_pages()



This is a helper that pins down a range from an iov_iter and adds it to
a bio without requiring a separate memory allocation for the page array.
It will be used for upcoming direct I/O implementations for block devices
and iomap based file systems.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
[hch: ported to the iov_iter interface, renamed and added comments.
      All blame should be directed to me and all fame should go to Kent
      after this!]
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 13edd5e7
Loading
Loading
Loading
Loading
+49 −0
Original line number Original line Diff line number Diff line
@@ -847,6 +847,55 @@ int bio_add_page(struct bio *bio, struct page *page,
}
}
EXPORT_SYMBOL(bio_add_page);
EXPORT_SYMBOL(bio_add_page);


/**
 * bio_iov_iter_get_pages - pin user or kernel pages and add them to a bio
 * @bio: bio to add pages to
 * @iter: iov iterator describing the region to be mapped
 *
 * Pins as many pages from *iter and appends them to @bio's bvec array. The
 * pages will have to be released using put_page() when done.
 */
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
{
	unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt;
	struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt;
	struct page **pages = (struct page **)bv;
	size_t offset, diff;
	ssize_t size;

	size = iov_iter_get_pages(iter, pages, LONG_MAX, nr_pages, &offset);
	if (unlikely(size <= 0))
		return size ? size : -EFAULT;
	nr_pages = (size + offset + PAGE_SIZE - 1) / PAGE_SIZE;

	/*
	 * Deep magic below:  We need to walk the pinned pages backwards
	 * because we are abusing the space allocated for the bio_vecs
	 * for the page array.  Because the bio_vecs are larger than the
	 * page pointers by definition this will always work.  But it also
	 * means we can't use bio_add_page, so any changes to it's semantics
	 * need to be reflected here as well.
	 */
	bio->bi_iter.bi_size += size;
	bio->bi_vcnt += nr_pages;

	diff = (nr_pages * PAGE_SIZE - offset) - size;
	while (nr_pages--) {
		bv[nr_pages].bv_page = pages[nr_pages];
		bv[nr_pages].bv_len = PAGE_SIZE;
		bv[nr_pages].bv_offset = 0;
	}

	bv[0].bv_offset += offset;
	bv[0].bv_len -= offset;
	if (diff)
		bv[bio->bi_vcnt - 1].bv_len -= diff;

	iov_iter_advance(iter, size);
	return 0;
}
EXPORT_SYMBOL_GPL(bio_iov_iter_get_pages);

struct submit_bio_ret {
struct submit_bio_ret {
	struct completion event;
	struct completion event;
	int error;
	int error;
+1 −0
Original line number Original line Diff line number Diff line
@@ -427,6 +427,7 @@ void bio_chain(struct bio *, struct bio *);
extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int);
extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int);
extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *,
extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *,
			   unsigned int, unsigned int);
			   unsigned int, unsigned int);
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter);
struct rq_map_data;
struct rq_map_data;
extern struct bio *bio_map_user_iov(struct request_queue *,
extern struct bio *bio_map_user_iov(struct request_queue *,
				    const struct iov_iter *, gfp_t);
				    const struct iov_iter *, gfp_t);