Add user scatter gather I/O to SG_IO
Subsystems (3)
RFC 1/1 Add user scatter gather I/O to SG_IO
This should be the last in the series of patches implementing scsi_wait_req and scsi_do_req in the block layer. It adds scatter/gather I/O from the user as a new feature of SG_IO. It also exports the API for this which can now be used by the sg driver.
The patch is lightly tested, but appears to work fine.
James
drivers/block/ll_rw_blk.c +71 -7
@@ -2152,6 +2152,61 @@ struct request *blk_rq_map_user(request_EXPORT_SYMBOL (blk_rq_map_user);/** +* blk_rq_map_user - map user data to a request, for REQ_BLOCK_PC usage +* @q: request queue where request should be inserted +* @rw: READ or WRITE data +* @iov: pointer to the iovec +* @iov_count: number of elements in the iovec +* +* Description: +* Data will be mapped directly for zero copy io, if possible. Otherwise +* a kernel bounce buffer is used. +* +* A matching blk_rq_unmap_user() must be issued at the end of io, while +* still in process context. +* +* Note: The mapped bio may need to be bounced through blk_queue_bounce() +* before being submitted to the device, as pages mapped may be out of +* reach. It's the callers responsibility to make sure this happens. The +* original bio must be passed back in to blk_rq_unmap_user() for proper +* unmapping. +*/ +struct request *blk_rq_map_user_iov (request_queue_t *q,int rw, +struct sg_iovec *iov,int iov_count) +{ +struct request *rq; +struct bio *bio; + +if (!iov || iov_count <=0 ) +return ERR_PTR (-EINVAL ); + + rq =blk_get_request (q, rw, __GFP_WAIT); +if (!rq) +return ERR_PTR (-ENOMEM ); + +/* we don't allow misaligned data like bio_map_user() does. If the +* user is using sg, they're expected to know the alignment constraints +* and respect them accordingly */ + bio =bio_map_user_iov (q,NULL , iov, iov_count, rw ==READ ); + +if (!IS_ERR (bio)) { + rq->bio = rq->biotail = bio; +blk_rq_bio_prep (q, rq, bio); + + rq->buffer = rq->data =NULL ; + rq->data_len = bio->bi_size; +return rq; + } + +/* +* bio is the err-ptr +*/ +blk_put_request (rq); +return (struct request *) bio; +} +EXPORT_SYMBOL (blk_rq_map_user_iov); + +/** * blk_rq_unmap_user - unmap a request with user data * @rq: request to be unmapped * @bio: bio for the request
@@ -2231,6 +2286,21 @@ struct request *blk_rq_map_kern(request_EXPORT_SYMBOL (blk_rq_map_kern); +void blk_execute_rq_nowait (request_queue_t *q,struct gendisk *bd_disk, +struct request *rq,int at_head, +void (*done)(struct request *)) +{ +int where = at_head ?ELEVATOR_INSERT_FRONT :ELEVATOR_INSERT_BACK ; + + rq->rq_disk = bd_disk; + + + rq->flags |=REQ_NOMERGE ; + rq->end_io = done; +elv_add_request (q, rq, where,1 ); +generic_unplug_device (q); +} +/** * blk_execute_rq - insert a request into queue for execution * @q: queue to insert the request in
@@ -2245,13 +2315,10 @@ EXPORT_SYMBOL(blk_rq_map_kern);int blk_execute_rq (request_queue_t *q,struct gendisk *bd_disk,struct request *rq,int at_head) { -int where = at_head ?ELEVATOR_INSERT_FRONT :ELEVATOR_INSERT_BACK ;DECLARE_COMPLETION (wait);char sense[SCSI_SENSE_BUFFERSIZE ];int err =0 ; - rq->rq_disk = bd_disk; -/* * we need an extra reference to the request, so we can look at * it after io completion
@@ -2264,11 +2331,8 @@ int blk_execute_rq(request_queue_t *q, s rq->sense_len =0 ; } - rq->flags |=REQ_NOMERGE ; rq->waiting = &wait; - rq->end_io = blk_end_sync_rq; -elv_add_request (q, rq, where,1 ); -generic_unplug_device (q); +blk_execute_rq_nowait (q, bd_disk, rq, at_head, blk_end_sync_rq);wait_for_completion (&wait); rq->waiting =NULL ;
drivers/block/scsi_ioctl.c +18 -6
@@ -231,9 +231,6 @@ static int sg_io(struct file *file, requif (verify_command (file, cmd))return -EPERM ; -/* -* we'll do that later -*/ if (hdr->iovec_count)return -EOPNOTSUPP ;
@@ -241,7 +238,7 @@ static int sg_io(struct file *file, requreturn -EIO ; reading = writing =0 ; -if (hdr->dxfer_len) { +if (hdr->dxfer_len)switch (hdr->dxfer_direction) {default :return -EINVAL ;
@@ -256,14 +253,29 @@ static int sg_io(struct file *file, requbreak ; } +if (hdr->iovec_count) { +const int size =sizeof (struct sg_iovec ) * hdr->iovec_count; +struct sg_iovec *iov =kmalloc (size,GFP_KERNEL ); +if (!iov) +return -ENOMEM ; + +if (copy_from_user (iov, hdr->dxferp, size)) { +kfree (iov); +return -EFAULT ; + } + rq =blk_rq_map_user_iov (q, writing ?WRITE :READ , iov, + hdr->iovec_count); +kfree (iov); + }else if (hdr->dxfer_len) { rq =blk_rq_map_user (q, writing ?WRITE :READ , hdr->dxferp, hdr->dxfer_len); -if (IS_ERR (rq)) -return PTR_ERR (rq); }else rq =blk_get_request (q,READ , __GFP_WAIT); +if (IS_ERR (rq)) +return PTR_ERR (rq); +/* * fill in request structure */
drivers/scsi/scsi_error.c +3 -0
@@ -1230,6 +1230,9 @@ static void scsi_eh_offline_sdevs(struct scmd->device->channel, scmd->device->id, scmd->device->lun); +/* try to reset the bus and the card to a sane state */ +scsi_try_bus_reset (scmd); +scsi_try_host_reset (scmd);scsi_device_set_state (scmd->device,SDEV_OFFLINE );if (scsi_eh_eflags_chk (scmd,SCSI_EH_CANCEL_CMD )) {/*
fs/bio.c +103 -43
197 lines not on this page · raw message
include/linux/bio.h +4 -0
@@ -281,6 +281,10 @@ extern int bio_add_page(struct bio *, stextern int bio_get_nr_vecs (struct block_device *);extern struct bio *bio_map_user (struct request_queue *,struct block_device *,unsigned long ,unsigned int ,int ); +struct sg_iovec ; +extern struct bio *bio_map_user_iov (struct request_queue *, +struct block_device *, +struct sg_iovec *,int ,int );extern void bio_unmap_user (struct bio *);extern struct bio *bio_map_kern (struct request_queue *,void *,unsigned int ,unsigned int );
include/linux/blkdev.h +2 -0
@@ -559,6 +559,8 @@ extern void __blk_stop_queue(request_queextern void blk_run_queue (request_queue_t *);extern void blk_queue_activity_fn (request_queue_t *,activity_fn *,void *);extern struct request *blk_rq_map_user (request_queue_t *,int ,void __user *,unsigned int ); +struct sg_iovec ; +extern struct request *blk_rq_map_user_iov (request_queue_t *,int ,struct sg_iovec *,int );extern int blk_rq_unmap_user (struct request *,struct bio *,unsigned int );extern struct request *blk_rq_map_kern (request_queue_t *,int ,void *,unsigned int ,unsigned int );
Quoted 18 lines · James Bottomley
This should be the last in the series of patches implementing
scsi_wait_reqandscsi_do_reqin the block layer. It adds scatter/gather I/O from the user as a new feature ofSG_IO. It also exports the API for this which can now be used by the sg driver.The patch is lightly tested, but appears to work fine.
James
(quoted diff) +5
-0+void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk, + struct request *rq, int at_head, + void (*done)(struct request *)) +{ + int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
Review
Quoted 10 lines
(quoted diff) +10
-0+ + rq->rq_disk = bd_disk; + + + rq->flags |= REQ_NOMERGE; + rq->end_io = done; + elv_add_request(q, rq, where, 1); + generic_unplug_device(q); +} +
This is useful for device mapper hw_hanlders. Can we get it and __blk_put_request exported? See below.
export blk_execute_rq_nowait and __blk_put_request so device mapper hw_handlers can use them.
drivers/block/ll_rw_blk.c +5 -1
@@ -2301,6 +2301,8 @@ void blk_execute_rq_nowait(request_queuegeneric_unplug_device (q); } +EXPORT_SYMBOL (blk_execute_rq_nowait); +/** * blk_execute_rq - insert a request into queue for execution * @q: queue to insert the request in
@@ -2482,7 +2484,7 @@ void disk_round_stats(struct gendisk *di/* * queue lock must be held */ -static void __blk_put_request (request_queue_t *q,struct request *req) +void __blk_put_request (request_queue_t *q,struct request *req) {struct request_list *rl = req->rl;
@@ -2511,6 +2513,8 @@ static void __blk_put_request(request_qu } } +EXPORT_SYMBOL (__blk_put_request); +void blk_put_request (struct request *req) {/*
include/linux/blkdev.h +4 -0
@@ -540,11 +540,15 @@ extern void blk_unregister_queue(structextern void register_disk (struct gendisk *dev);extern void generic_make_request (struct bio *bio);extern void blk_put_request (struct request *); +extern void __blk_put_request (request_queue_t *,struct request *);extern void blk_end_sync_rq (struct request *rq);extern void blk_attempt_remerge (request_queue_t *,struct request *);extern void __blk_attempt_remerge (request_queue_t *,struct request *);extern struct request *blk_get_request (request_queue_t *,int ,int );extern void blk_insert_request (request_queue_t *,struct request *,int ,void *); +extern void blk_execute_rq_nowait (request_queue_t *q,struct gendisk *bd_disk, +struct request *rq,int at_head, +void (*done)(struct request *));extern void blk_requeue_request (request_queue_t *,struct request *);extern void blk_plug_device (request_queue_t *);extern int blk_remove_plug (request_queue_t *);
drivers/block/scsi_ioctl.c static int sg_io(struct file *file, requ · line 235
Quoted 12 lines · James Bottomley
drivers/block/scsi_ioctl.c +0
-3@@ -231,9 +231,6 @@ static int sg_io(struct file *file, requif (verify_command (file, cmd))return -EPERM ; -/* -* we'll do that later -*/ if (hdr->iovec_count)return -EOPNOTSUPP ;
Didn't you want to remove more than the comment here?
drivers/scsi/scsi_error.c static void scsi_eh_offline_sdevs(struct · line 1237
Quoted 12 lines
drivers/scsi/scsi_error.c +3
-0@@ -1230,6 +1230,9 @@ static void scsi_eh_offline_sdevs(struct scmd->device->channel, scmd->device->id, scmd->device->lun); +/* try to reset the bus and the card to a sane state */ +scsi_try_bus_reset (scmd); +scsi_try_host_reset (scmd);scsi_device_set_state (scmd->device,SDEV_OFFLINE );if (scsi_eh_eflags_chk (scmd,SCSI_EH_CANCEL_CMD )) {
Different patch?
Other than that I think it looks good, thanks. I'll get this added asap.
Signature · 1 line
Jens Axboe
Quoted 1 line · Jens Axboe
Didn't you want to remove more than the comment here?
Well ... like I said, it was lightly tested ...
I figured if I could still get SG_IO to work at all, then I'd proved that single element SG lists worked ...
Quoted 7 lines
drivers/scsi/scsi_error.c static void scsi_eh_offline_sdevs(struct · line 1237
Quoted 5 lines
drivers/scsi/scsi_error.c +3
-0+/* try to reset the bus and the card to a sane state */ +scsi_try_bus_reset (scmd); +scsi_try_host_reset (scmd);scsi_device_set_state (scmd->device,SDEV_OFFLINE );if (scsi_eh_eflags_chk (scmd,SCSI_EH_CANCEL_CMD )) {Different patch?
Yes, contamination, sorry.
Quoted 1 line
Other than that I think it looks good, thanks. I'll get this added asap.
Great ... let me know when you have a tree ready and I'll move to base the SCSI changes off it.
James