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

Commit 25636e28 authored by Tejun Heo's avatar Tejun Heo Committed by Jens Axboe
Browse files

block: fix SG_IO vector request data length handling



Impact: fix SG_IO behavior such that it matches the documentation

SG_IO howto says that if ->dxfer_len and sum of iovec disagress, the
shorter one wins.  However, the current implementation returns -EINVAL
for such cases.  Trim iovc if it's longer than ->dxfer_len.

This patch uses iov_*() helpers which take struct iovec * by casting
struct sg_iovec * to it.  sg_iovec is always identical to iovec and
this will be further cleaned up with later patches.

Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent 23c560a9
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -290,6 +290,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,

	if (hdr->iovec_count) {
		const int size = sizeof(struct sg_iovec) * hdr->iovec_count;
		size_t iov_data_len;
		struct sg_iovec *iov;

		iov = kmalloc(size, GFP_KERNEL);
@@ -304,8 +305,18 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
			goto out;
		}

		/* SG_IO howto says that the shorter of the two wins */
		iov_data_len = iov_length((struct iovec *)iov,
					  hdr->iovec_count);
		if (hdr->dxfer_len < iov_data_len) {
			hdr->iovec_count = iov_shorten((struct iovec *)iov,
						       hdr->iovec_count,
						       hdr->dxfer_len);
			iov_data_len = hdr->dxfer_len;
		}

		ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count,
					  hdr->dxfer_len, GFP_KERNEL);
					  iov_data_len, GFP_KERNEL);
		kfree(iov);
	} else if (hdr->dxfer_len)
		ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len,