diff options
author | Tejun Heo <tj@kernel.org> | 2009-04-15 09:10:24 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-04-22 02:35:09 -0400 |
commit | 25636e282fe95508cae96bb27f86407aef935817 (patch) | |
tree | 3ab5b1ab589c02756211c8faa098f756488ff0de | |
parent | 23c560a99d78bddf5c251bfa97bce19e4da4b3f3 (diff) |
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>
-rw-r--r-- | block/scsi_ioctl.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 84b7f8709f41..82a0ca2f6729 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c | |||
@@ -290,6 +290,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, | |||
290 | 290 | ||
291 | if (hdr->iovec_count) { | 291 | if (hdr->iovec_count) { |
292 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; | 292 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; |
293 | size_t iov_data_len; | ||
293 | struct sg_iovec *iov; | 294 | struct sg_iovec *iov; |
294 | 295 | ||
295 | iov = kmalloc(size, GFP_KERNEL); | 296 | iov = kmalloc(size, GFP_KERNEL); |
@@ -304,8 +305,18 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, | |||
304 | goto out; | 305 | goto out; |
305 | } | 306 | } |
306 | 307 | ||
308 | /* SG_IO howto says that the shorter of the two wins */ | ||
309 | iov_data_len = iov_length((struct iovec *)iov, | ||
310 | hdr->iovec_count); | ||
311 | if (hdr->dxfer_len < iov_data_len) { | ||
312 | hdr->iovec_count = iov_shorten((struct iovec *)iov, | ||
313 | hdr->iovec_count, | ||
314 | hdr->dxfer_len); | ||
315 | iov_data_len = hdr->dxfer_len; | ||
316 | } | ||
317 | |||
307 | ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, | 318 | ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, |
308 | hdr->dxfer_len, GFP_KERNEL); | 319 | iov_data_len, GFP_KERNEL); |
309 | kfree(iov); | 320 | kfree(iov); |
310 | } else if (hdr->dxfer_len) | 321 | } else if (hdr->dxfer_len) |
311 | ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, | 322 | ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, |