diff options
Diffstat (limited to 'block/scsi_ioctl.c')
-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, |