diff options
Diffstat (limited to 'block/scsi_ioctl.c')
-rw-r--r-- | block/scsi_ioctl.c | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 626ee274c5c4..82a0ca2f6729 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c | |||
@@ -217,7 +217,7 @@ static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, | |||
217 | static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, | 217 | static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, |
218 | struct bio *bio) | 218 | struct bio *bio) |
219 | { | 219 | { |
220 | int ret = 0; | 220 | int r, ret = 0; |
221 | 221 | ||
222 | /* | 222 | /* |
223 | * fill in all the output members | 223 | * fill in all the output members |
@@ -242,7 +242,9 @@ static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, | |||
242 | ret = -EFAULT; | 242 | ret = -EFAULT; |
243 | } | 243 | } |
244 | 244 | ||
245 | blk_rq_unmap_user(bio); | 245 | r = blk_rq_unmap_user(bio); |
246 | if (!ret) | ||
247 | ret = r; | ||
246 | blk_put_request(rq); | 248 | blk_put_request(rq); |
247 | 249 | ||
248 | return ret; | 250 | return ret; |
@@ -288,6 +290,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, | |||
288 | 290 | ||
289 | if (hdr->iovec_count) { | 291 | if (hdr->iovec_count) { |
290 | 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; | ||
291 | struct sg_iovec *iov; | 294 | struct sg_iovec *iov; |
292 | 295 | ||
293 | iov = kmalloc(size, GFP_KERNEL); | 296 | iov = kmalloc(size, GFP_KERNEL); |
@@ -302,8 +305,18 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, | |||
302 | goto out; | 305 | goto out; |
303 | } | 306 | } |
304 | 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 | |||
305 | 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, |
306 | hdr->dxfer_len, GFP_KERNEL); | 319 | iov_data_len, GFP_KERNEL); |
307 | kfree(iov); | 320 | kfree(iov); |
308 | } else if (hdr->dxfer_len) | 321 | } else if (hdr->dxfer_len) |
309 | 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, |