aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--block/ll_rw_blk.c28
-rw-r--r--block/scsi_ioctl.c3
-rw-r--r--drivers/cdrom/cdrom.c3
-rw-r--r--include/linux/blkdev.h2
4 files changed, 19 insertions, 17 deletions
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index a66ec30855d8..e07c079e07e6 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -2405,6 +2405,7 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
2405 unsigned long len) 2405 unsigned long len)
2406{ 2406{
2407 unsigned long bytes_read = 0; 2407 unsigned long bytes_read = 0;
2408 struct bio *bio = NULL;
2408 int ret; 2409 int ret;
2409 2410
2410 if (len > (q->max_hw_sectors << 9)) 2411 if (len > (q->max_hw_sectors << 9))
@@ -2431,6 +2432,8 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
2431 ret = __blk_rq_map_user(q, rq, ubuf, map_len); 2432 ret = __blk_rq_map_user(q, rq, ubuf, map_len);
2432 if (ret < 0) 2433 if (ret < 0)
2433 goto unmap_rq; 2434 goto unmap_rq;
2435 if (!bio)
2436 bio = rq->bio;
2434 bytes_read += ret; 2437 bytes_read += ret;
2435 ubuf += ret; 2438 ubuf += ret;
2436 } 2439 }
@@ -2438,7 +2441,7 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
2438 rq->buffer = rq->data = NULL; 2441 rq->buffer = rq->data = NULL;
2439 return 0; 2442 return 0;
2440unmap_rq: 2443unmap_rq:
2441 blk_rq_unmap_user(rq); 2444 blk_rq_unmap_user(bio);
2442 return ret; 2445 return ret;
2443} 2446}
2444 2447
@@ -2495,29 +2498,30 @@ EXPORT_SYMBOL(blk_rq_map_user_iov);
2495 2498
2496/** 2499/**
2497 * blk_rq_unmap_user - unmap a request with user data 2500 * blk_rq_unmap_user - unmap a request with user data
2498 * @rq: rq to be unmapped 2501 * @bio: start of bio list
2499 * 2502 *
2500 * Description: 2503 * Description:
2501 * Unmap a rq previously mapped by blk_rq_map_user(). 2504 * Unmap a rq previously mapped by blk_rq_map_user(). The caller must
2502 * rq->bio must be set to the original head of the request. 2505 * supply the original rq->bio from the blk_rq_map_user() return, since
2506 * the io completion may have changed rq->bio.
2503 */ 2507 */
2504int blk_rq_unmap_user(struct request *rq) 2508int blk_rq_unmap_user(struct bio *bio)
2505{ 2509{
2506 struct bio *bio, *mapped_bio; 2510 struct bio *mapped_bio;
2507 int ret = 0, ret2; 2511 int ret = 0, ret2;
2508 2512
2509 while ((bio = rq->bio)) { 2513 while (bio) {
2510 if (bio_flagged(bio, BIO_BOUNCED)) 2514 mapped_bio = bio;
2515 if (unlikely(bio_flagged(bio, BIO_BOUNCED)))
2511 mapped_bio = bio->bi_private; 2516 mapped_bio = bio->bi_private;
2512 else
2513 mapped_bio = bio;
2514 2517
2515 ret2 = __blk_rq_unmap_user(mapped_bio); 2518 ret2 = __blk_rq_unmap_user(mapped_bio);
2516 if (ret2 && !ret) 2519 if (ret2 && !ret)
2517 ret = ret2; 2520 ret = ret2;
2518 2521
2519 rq->bio = bio->bi_next; 2522 mapped_bio = bio;
2520 bio_put(bio); 2523 bio = bio->bi_next;
2524 bio_put(mapped_bio);
2521 } 2525 }
2522 2526
2523 return ret; 2527 return ret;
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index f322b6a441d8..2528a0c0dec8 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -333,8 +333,7 @@ static int sg_io(struct file *file, request_queue_t *q,
333 hdr->sb_len_wr = len; 333 hdr->sb_len_wr = len;
334 } 334 }
335 335
336 rq->bio = bio; 336 if (blk_rq_unmap_user(bio))
337 if (blk_rq_unmap_user(rq))
338 ret = -EFAULT; 337 ret = -EFAULT;
339 338
340 /* may not have succeeded, but output values written to control 339 /* may not have succeeded, but output values written to control
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index e4a2f8f3a1d7..66d028d30439 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -2139,8 +2139,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
2139 cdi->last_sense = s->sense_key; 2139 cdi->last_sense = s->sense_key;
2140 } 2140 }
2141 2141
2142 rq->bio = bio; 2142 if (blk_rq_unmap_user(bio))
2143 if (blk_rq_unmap_user(rq))
2144 ret = -EFAULT; 2143 ret = -EFAULT;
2145 2144
2146 if (ret) 2145 if (ret)
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 0fa33017ec02..36a6eacefe20 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -672,7 +672,7 @@ extern void __blk_stop_queue(request_queue_t *q);
672extern void blk_run_queue(request_queue_t *); 672extern void blk_run_queue(request_queue_t *);
673extern void blk_start_queueing(request_queue_t *); 673extern void blk_start_queueing(request_queue_t *);
674extern int blk_rq_map_user(request_queue_t *, struct request *, void __user *, unsigned long); 674extern int blk_rq_map_user(request_queue_t *, struct request *, void __user *, unsigned long);
675extern int blk_rq_unmap_user(struct request *); 675extern int blk_rq_unmap_user(struct bio *);
676extern int blk_rq_map_kern(request_queue_t *, struct request *, void *, unsigned int, gfp_t); 676extern int blk_rq_map_kern(request_queue_t *, struct request *, void *, unsigned int, gfp_t);
677extern int blk_rq_map_user_iov(request_queue_t *, struct request *, 677extern int blk_rq_map_user_iov(request_queue_t *, struct request *,
678 struct sg_iovec *, int, unsigned int); 678 struct sg_iovec *, int, unsigned int);