diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2008-09-02 03:20:19 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2008-10-09 02:56:11 -0400 |
commit | 818827669d85b84241696ffef2de485db46b0b5e (patch) | |
tree | 694d09728733e65d604bf3e1f13679db73fc1d9a | |
parent | 839e96afba87117befd39cf4e43f156edc8047a7 (diff) |
block: make blk_rq_map_user take a NULL user-space buffer
This patch changes blk_rq_map_user to accept a NULL user-space buffer
with a READ command if rq_map_data is not NULL. Thus a caller can pass
page frames to lk_rq_map_user to just set up a request and bios with
page frames propely. bio_uncopy_user (called via blk_rq_unmap_user)
doesn't copy data to user space with such request.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | block/blk-map.c | 16 | ||||
-rw-r--r-- | fs/bio.c | 8 | ||||
-rw-r--r-- | include/linux/bio.h | 1 |
3 files changed, 17 insertions, 8 deletions
diff --git a/block/blk-map.c b/block/blk-map.c index 572140cda5ff..4849fa36161e 100644 --- a/block/blk-map.c +++ b/block/blk-map.c | |||
@@ -42,7 +42,7 @@ static int __blk_rq_unmap_user(struct bio *bio) | |||
42 | 42 | ||
43 | static int __blk_rq_map_user(struct request_queue *q, struct request *rq, | 43 | static int __blk_rq_map_user(struct request_queue *q, struct request *rq, |
44 | struct rq_map_data *map_data, void __user *ubuf, | 44 | struct rq_map_data *map_data, void __user *ubuf, |
45 | unsigned int len, gfp_t gfp_mask) | 45 | unsigned int len, int null_mapped, gfp_t gfp_mask) |
46 | { | 46 | { |
47 | unsigned long uaddr; | 47 | unsigned long uaddr; |
48 | struct bio *bio, *orig_bio; | 48 | struct bio *bio, *orig_bio; |
@@ -63,6 +63,9 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, | |||
63 | if (IS_ERR(bio)) | 63 | if (IS_ERR(bio)) |
64 | return PTR_ERR(bio); | 64 | return PTR_ERR(bio); |
65 | 65 | ||
66 | if (null_mapped) | ||
67 | bio->bi_flags |= (1 << BIO_NULL_MAPPED); | ||
68 | |||
66 | orig_bio = bio; | 69 | orig_bio = bio; |
67 | blk_queue_bounce(q, &bio); | 70 | blk_queue_bounce(q, &bio); |
68 | 71 | ||
@@ -111,12 +114,17 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, | |||
111 | { | 114 | { |
112 | unsigned long bytes_read = 0; | 115 | unsigned long bytes_read = 0; |
113 | struct bio *bio = NULL; | 116 | struct bio *bio = NULL; |
114 | int ret; | 117 | int ret, null_mapped = 0; |
115 | 118 | ||
116 | if (len > (q->max_hw_sectors << 9)) | 119 | if (len > (q->max_hw_sectors << 9)) |
117 | return -EINVAL; | 120 | return -EINVAL; |
118 | if (!len || !ubuf) | 121 | if (!len) |
119 | return -EINVAL; | 122 | return -EINVAL; |
123 | if (!ubuf) { | ||
124 | if (!map_data || rq_data_dir(rq) != READ) | ||
125 | return -EINVAL; | ||
126 | null_mapped = 1; | ||
127 | } | ||
120 | 128 | ||
121 | while (bytes_read != len) { | 129 | while (bytes_read != len) { |
122 | unsigned long map_len, end, start; | 130 | unsigned long map_len, end, start; |
@@ -135,7 +143,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, | |||
135 | map_len -= PAGE_SIZE; | 143 | map_len -= PAGE_SIZE; |
136 | 144 | ||
137 | ret = __blk_rq_map_user(q, rq, map_data, ubuf, map_len, | 145 | ret = __blk_rq_map_user(q, rq, map_data, ubuf, map_len, |
138 | gfp_mask); | 146 | null_mapped, gfp_mask); |
139 | if (ret < 0) | 147 | if (ret < 0) |
140 | goto unmap_rq; | 148 | goto unmap_rq; |
141 | if (!bio) | 149 | if (!bio) |
@@ -547,11 +547,11 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, | |||
547 | int bio_uncopy_user(struct bio *bio) | 547 | int bio_uncopy_user(struct bio *bio) |
548 | { | 548 | { |
549 | struct bio_map_data *bmd = bio->bi_private; | 549 | struct bio_map_data *bmd = bio->bi_private; |
550 | int ret; | 550 | int ret = 0; |
551 | |||
552 | ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1, | ||
553 | bmd->is_our_pages); | ||
554 | 551 | ||
552 | if (!bio_flagged(bio, BIO_NULL_MAPPED)) | ||
553 | ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, | ||
554 | bmd->nr_sgvecs, 1, bmd->is_our_pages); | ||
555 | bio_free_map_data(bmd); | 555 | bio_free_map_data(bmd); |
556 | bio_put(bio); | 556 | bio_put(bio); |
557 | return ret; | 557 | return ret; |
diff --git a/include/linux/bio.h b/include/linux/bio.h index bc386cd5e996..7af373f253dc 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h | |||
@@ -108,6 +108,7 @@ struct bio { | |||
108 | #define BIO_USER_MAPPED 6 /* contains user pages */ | 108 | #define BIO_USER_MAPPED 6 /* contains user pages */ |
109 | #define BIO_EOPNOTSUPP 7 /* not supported */ | 109 | #define BIO_EOPNOTSUPP 7 /* not supported */ |
110 | #define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */ | 110 | #define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */ |
111 | #define BIO_NULL_MAPPED 9 /* contains invalid user pages */ | ||
111 | #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) | 112 | #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) |
112 | 113 | ||
113 | /* | 114 | /* |