diff options
author | James Bottomley <James.Bottomley@SteelEye.com> | 2005-06-20 08:06:52 -0400 |
---|---|---|
committer | Jens Axboe <axboe@suse.de> | 2005-06-20 08:06:52 -0400 |
commit | f1970baf6d74e03bd32072ab453f2fc01bc1b8d3 (patch) | |
tree | 559898cdf83bd0f93b8a72248c6423a6548fb604 /drivers | |
parent | dd1cab95f356f1395278633565f198463cf6bd24 (diff) |
[PATCH] Add scatter-gather support for the block layer SG_IO
Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/ll_rw_blk.c | 64 | ||||
-rw-r--r-- | drivers/block/scsi_ioctl.c | 34 |
2 files changed, 81 insertions, 17 deletions
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 42c4f3651cf8..874e46fc3748 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c | |||
@@ -2149,6 +2149,50 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf, | |||
2149 | EXPORT_SYMBOL(blk_rq_map_user); | 2149 | EXPORT_SYMBOL(blk_rq_map_user); |
2150 | 2150 | ||
2151 | /** | 2151 | /** |
2152 | * blk_rq_map_user_iov - map user data to a request, for REQ_BLOCK_PC usage | ||
2153 | * @q: request queue where request should be inserted | ||
2154 | * @rq: request to map data to | ||
2155 | * @iov: pointer to the iovec | ||
2156 | * @iov_count: number of elements in the iovec | ||
2157 | * | ||
2158 | * Description: | ||
2159 | * Data will be mapped directly for zero copy io, if possible. Otherwise | ||
2160 | * a kernel bounce buffer is used. | ||
2161 | * | ||
2162 | * A matching blk_rq_unmap_user() must be issued at the end of io, while | ||
2163 | * still in process context. | ||
2164 | * | ||
2165 | * Note: The mapped bio may need to be bounced through blk_queue_bounce() | ||
2166 | * before being submitted to the device, as pages mapped may be out of | ||
2167 | * reach. It's the callers responsibility to make sure this happens. The | ||
2168 | * original bio must be passed back in to blk_rq_unmap_user() for proper | ||
2169 | * unmapping. | ||
2170 | */ | ||
2171 | int blk_rq_map_user_iov(request_queue_t *q, struct request *rq, | ||
2172 | struct sg_iovec *iov, int iov_count) | ||
2173 | { | ||
2174 | struct bio *bio; | ||
2175 | |||
2176 | if (!iov || iov_count <= 0) | ||
2177 | return -EINVAL; | ||
2178 | |||
2179 | /* we don't allow misaligned data like bio_map_user() does. If the | ||
2180 | * user is using sg, they're expected to know the alignment constraints | ||
2181 | * and respect them accordingly */ | ||
2182 | bio = bio_map_user_iov(q, NULL, iov, iov_count, rq_data_dir(rq)== READ); | ||
2183 | if (IS_ERR(bio)) | ||
2184 | return PTR_ERR(bio); | ||
2185 | |||
2186 | rq->bio = rq->biotail = bio; | ||
2187 | blk_rq_bio_prep(q, rq, bio); | ||
2188 | rq->buffer = rq->data = NULL; | ||
2189 | rq->data_len = bio->bi_size; | ||
2190 | return 0; | ||
2191 | } | ||
2192 | |||
2193 | EXPORT_SYMBOL(blk_rq_map_user_iov); | ||
2194 | |||
2195 | /** | ||
2152 | * blk_rq_unmap_user - unmap a request with user data | 2196 | * blk_rq_unmap_user - unmap a request with user data |
2153 | * @rq: request to be unmapped | 2197 | * @rq: request to be unmapped |
2154 | * @bio: bio for the request | 2198 | * @bio: bio for the request |
@@ -2207,6 +2251,19 @@ int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf, | |||
2207 | 2251 | ||
2208 | EXPORT_SYMBOL(blk_rq_map_kern); | 2252 | EXPORT_SYMBOL(blk_rq_map_kern); |
2209 | 2253 | ||
2254 | void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk, | ||
2255 | struct request *rq, int at_head, | ||
2256 | void (*done)(struct request *)) | ||
2257 | { | ||
2258 | int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; | ||
2259 | |||
2260 | rq->rq_disk = bd_disk; | ||
2261 | rq->flags |= REQ_NOMERGE; | ||
2262 | rq->end_io = done; | ||
2263 | elv_add_request(q, rq, where, 1); | ||
2264 | generic_unplug_device(q); | ||
2265 | } | ||
2266 | |||
2210 | /** | 2267 | /** |
2211 | * blk_execute_rq - insert a request into queue for execution | 2268 | * blk_execute_rq - insert a request into queue for execution |
2212 | * @q: queue to insert the request in | 2269 | * @q: queue to insert the request in |
@@ -2224,8 +2281,6 @@ int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk, | |||
2224 | char sense[SCSI_SENSE_BUFFERSIZE]; | 2281 | char sense[SCSI_SENSE_BUFFERSIZE]; |
2225 | int err = 0; | 2282 | int err = 0; |
2226 | 2283 | ||
2227 | rq->rq_disk = bd_disk; | ||
2228 | |||
2229 | /* | 2284 | /* |
2230 | * we need an extra reference to the request, so we can look at | 2285 | * we need an extra reference to the request, so we can look at |
2231 | * it after io completion | 2286 | * it after io completion |
@@ -2238,11 +2293,8 @@ int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk, | |||
2238 | rq->sense_len = 0; | 2293 | rq->sense_len = 0; |
2239 | } | 2294 | } |
2240 | 2295 | ||
2241 | rq->flags |= REQ_NOMERGE; | ||
2242 | rq->waiting = &wait; | 2296 | rq->waiting = &wait; |
2243 | rq->end_io = blk_end_sync_rq; | 2297 | blk_execute_rq_nowait(q, bd_disk, rq, 0, blk_end_sync_rq); |
2244 | elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); | ||
2245 | generic_unplug_device(q); | ||
2246 | wait_for_completion(&wait); | 2298 | wait_for_completion(&wait); |
2247 | rq->waiting = NULL; | 2299 | rq->waiting = NULL; |
2248 | 2300 | ||
diff --git a/drivers/block/scsi_ioctl.c b/drivers/block/scsi_ioctl.c index 93c4ca874be3..09a7e73a0812 100644 --- a/drivers/block/scsi_ioctl.c +++ b/drivers/block/scsi_ioctl.c | |||
@@ -231,17 +231,11 @@ static int sg_io(struct file *file, request_queue_t *q, | |||
231 | if (verify_command(file, cmd)) | 231 | if (verify_command(file, cmd)) |
232 | return -EPERM; | 232 | return -EPERM; |
233 | 233 | ||
234 | /* | ||
235 | * we'll do that later | ||
236 | */ | ||
237 | if (hdr->iovec_count) | ||
238 | return -EOPNOTSUPP; | ||
239 | |||
240 | if (hdr->dxfer_len > (q->max_sectors << 9)) | 234 | if (hdr->dxfer_len > (q->max_sectors << 9)) |
241 | return -EIO; | 235 | return -EIO; |
242 | 236 | ||
243 | reading = writing = 0; | 237 | reading = writing = 0; |
244 | if (hdr->dxfer_len) { | 238 | if (hdr->dxfer_len) |
245 | switch (hdr->dxfer_direction) { | 239 | switch (hdr->dxfer_direction) { |
246 | default: | 240 | default: |
247 | return -EINVAL; | 241 | return -EINVAL; |
@@ -261,11 +255,29 @@ static int sg_io(struct file *file, request_queue_t *q, | |||
261 | if (!rq) | 255 | if (!rq) |
262 | return -ENOMEM; | 256 | return -ENOMEM; |
263 | 257 | ||
264 | if (reading || writing) { | 258 | if (hdr->iovec_count) { |
265 | ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); | 259 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; |
266 | if (ret) | 260 | struct sg_iovec *iov; |
261 | |||
262 | iov = kmalloc(size, GFP_KERNEL); | ||
263 | if (!iov) { | ||
264 | ret = -ENOMEM; | ||
267 | goto out; | 265 | goto out; |
268 | } | 266 | } |
267 | |||
268 | if (copy_from_user(iov, hdr->dxferp, size)) { | ||
269 | kfree(iov); | ||
270 | ret = -EFAULT; | ||
271 | goto out; | ||
272 | } | ||
273 | |||
274 | ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count); | ||
275 | kfree(iov); | ||
276 | } else if (hdr->dxfer_len) | ||
277 | ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); | ||
278 | |||
279 | if (ret) | ||
280 | goto out; | ||
269 | 281 | ||
270 | /* | 282 | /* |
271 | * fill in request structure | 283 | * fill in request structure |