Skip to contentSkip to conversation

Add user scatter gather I/O to SG_IO

RFC In review James Bottomley 3 participants 4 messages Jun 10, 2005 – Jun 13, 2005

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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, requ
 	if (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, requ
 		return -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, requ
 			break;
 		}
 
+	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 *, st
 extern 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_que
 extern 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);
#1 lore raw
Quoted 18 lines · James Bottomley

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

(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
diff -aurp cogito/drivers/block/ll_rw_blk.c linux-block-cogito/drivers/block/ll_rw_blk.c
@@ -2301,6 +2301,8 @@ void blk_execute_rq_nowait(request_queue
 	generic_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
diff -aurp cogito/include/linux/blkdev.h linux-block-cogito/include/linux/blkdev.h
@@ -540,11 +540,15 @@ extern void blk_unregister_queue(struct 
 extern 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 *);
#2 lore raw
In reply to James Bottomley

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, requ
 	if (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
#3 lore raw
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