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: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
Tejun Heo 2009-04-15 22:10:24 +09:00 committed by Jens Axboe
parent 23c560a99d
commit 25636e282f

View file

@ -290,6 +290,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
if (hdr->iovec_count) { if (hdr->iovec_count) {
const int size = sizeof(struct sg_iovec) * hdr->iovec_count; const int size = sizeof(struct sg_iovec) * hdr->iovec_count;
size_t iov_data_len;
struct sg_iovec *iov; struct sg_iovec *iov;
iov = kmalloc(size, GFP_KERNEL); iov = kmalloc(size, GFP_KERNEL);
@ -304,8 +305,18 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
goto out; 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, 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); kfree(iov);
} else if (hdr->dxfer_len) } else if (hdr->dxfer_len)
ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len,