Commit 37987547 authored by Kanchan Joshi's avatar Kanchan Joshi Committed by Jens Axboe
Browse files

block: extend functionality to map bvec iterator



Extend blk_rq_map_user_iov so that it can handle bvec iterator, using
the new blk_rq_map_user_bvec function. It maps the pages from bvec
iterator into a bio and place the bio into request.

This helper will be used by nvme for uring-passthrough path when IO is
done using pre-mapped buffers.

Signed-off-by: default avatarKanchan Joshi <joshi.k@samsung.com>
Signed-off-by: default avatarAnuj Gupta <anuj20.g@samsung.com>
Suggested-by: default avatarChristoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20220930062749.152261-11-anuj20.g@samsung.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent ab89e8e7
Loading
Loading
Loading
Loading
+71 −4
Original line number Diff line number Diff line
@@ -548,6 +548,62 @@ int blk_rq_append_bio(struct request *rq, struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_append_bio);

/* Prepare bio for passthrough IO given ITER_BVEC iter */
static int blk_rq_map_user_bvec(struct request *rq, const struct iov_iter *iter)
{
	struct request_queue *q = rq->q;
	size_t nr_iter = iov_iter_count(iter);
	size_t nr_segs = iter->nr_segs;
	struct bio_vec *bvecs, *bvprvp = NULL;
	struct queue_limits *lim = &q->limits;
	unsigned int nsegs = 0, bytes = 0;
	struct bio *bio;
	size_t i;

	if (!nr_iter || (nr_iter >> SECTOR_SHIFT) > queue_max_hw_sectors(q))
		return -EINVAL;
	if (nr_segs > queue_max_segments(q))
		return -EINVAL;

	/* no iovecs to alloc, as we already have a BVEC iterator */
	bio = blk_rq_map_bio_alloc(rq, 0, GFP_KERNEL);
	if (bio == NULL)
		return -ENOMEM;

	bio_iov_bvec_set(bio, (struct iov_iter *)iter);
	blk_rq_bio_prep(rq, bio, nr_segs);

	/* loop to perform a bunch of sanity checks */
	bvecs = (struct bio_vec *)iter->bvec;
	for (i = 0; i < nr_segs; i++) {
		struct bio_vec *bv = &bvecs[i];

		/*
		 * If the queue doesn't support SG gaps and adding this
		 * offset would create a gap, fallback to copy.
		 */
		if (bvprvp && bvec_gap_to_prev(lim, bvprvp, bv->bv_offset)) {
			blk_mq_map_bio_put(bio);
			return -EREMOTEIO;
		}
		/* check full condition */
		if (nsegs >= nr_segs || bytes > UINT_MAX - bv->bv_len)
			goto put_bio;
		if (bytes + bv->bv_len > nr_iter)
			goto put_bio;
		if (bv->bv_offset + bv->bv_len > PAGE_SIZE)
			goto put_bio;

		nsegs++;
		bytes += bv->bv_len;
		bvprvp = bv;
	}
	return 0;
put_bio:
	blk_mq_map_bio_put(bio);
	return -EINVAL;
}

/**
 * blk_rq_map_user_iov - map user data to a request, for passthrough requests
 * @q:		request queue where request should be inserted
@@ -567,24 +623,35 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
			struct rq_map_data *map_data,
			const struct iov_iter *iter, gfp_t gfp_mask)
{
	bool copy = false;
	bool copy = false, map_bvec = false;
	unsigned long align = q->dma_pad_mask | queue_dma_alignment(q);
	struct bio *bio = NULL;
	struct iov_iter i;
	int ret = -EINVAL;

	if (!iter_is_iovec(iter))
		goto fail;

	if (map_data)
		copy = true;
	else if (blk_queue_may_bounce(q))
		copy = true;
	else if (iov_iter_alignment(iter) & align)
		copy = true;
	else if (iov_iter_is_bvec(iter))
		map_bvec = true;
	else if (!iter_is_iovec(iter))
		copy = true;
	else if (queue_virt_boundary(q))
		copy = queue_virt_boundary(q) & iov_iter_gap_alignment(iter);

	if (map_bvec) {
		ret = blk_rq_map_user_bvec(rq, iter);
		if (!ret)
			return 0;
		if (ret != -EREMOTEIO)
			goto fail;
		/* fall back to copying the data on limits mismatches */
		copy = true;
	}

	i = *iter;
	do {
		if (copy)