LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC 4/6] bidi support: bidirectional SCSI layer
@ 2007-01-21 23:26 Boaz Harrosh
  0 siblings, 0 replies; only message in thread
From: Boaz Harrosh @ 2007-01-21 23:26 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Mike Christie
  Cc: linux-scsi, linux-kernel, open-iscsi, Daniel.E.Messinger,
	Liran Schour, Benny Halevy

- Add bidi members to struct scsi_cmnd.
- Add API at the SCSI level for bidirectional commands.
- Implement support for BIDI requests in scsi_setup_blk_pc_cmnd() and
  scsi_io_completion().

Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>

---
 drivers/scsi/scsi_lib.c    |  324 +++++++++++++++++++++++++++++++++++---------
 include/scsi/scsi_cmnd.h   |   55 +++++++-
 include/scsi/scsi_device.h |   16 ++
 3 files changed, 330 insertions(+), 65 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 9c21025..92d3d44 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -246,27 +246,28 @@ static void scsi_end_async(struct reques
 {
 	struct scsi_io_context *sioc = req->end_io_data;

-	if (sioc->done)
-		sioc->done(sioc->data, sioc->sense, req->errors, rq_uni(req)->data_len);
+	if (sioc->done) /* FIXME: API of done needs to change for bidi requests*/
+		sioc->done(sioc->data, sioc->sense, req->errors, req->uni.data_len);

 	kmem_cache_free(scsi_io_context_cache, sioc);
 	__blk_put_request(req->q, req);
 }

-static int scsi_merge_bio(struct request *rq, struct bio *bio)
+static int scsi_merge_bio(struct request *rq, struct bio *bio,
+                          enum dma_data_direction dir)
 {
 	struct request_queue *q = rq->q;
-	struct request_io_part* req_io = rq_uni(rq);
+	struct request_io_part* req_io = rq_io(rq, dir);

 	bio->bi_flags &= ~(1 << BIO_SEG_VALID);
-	if (rq_uni_write_dir(rq))
+	if (dir == DMA_TO_DEVICE)
 		bio->bi_rw |= (1 << BIO_RW);
 	else
 		bio->bi_rw &= ~(1 << BIO_RW);
 	blk_queue_bounce(q, &bio);

-	if (!rq_uni(rq)->bio)
-		blk_rq_bio_prep(q, rq, bio);
+	if (!req_io->bio)
+		blk_rq_bio_prep_bidi(q, rq, bio, dir);
 	else if (!ll_back_merge_fn(q, rq, bio))
 		return -EINVAL;
 	else {
@@ -299,7 +300,7 @@ static int scsi_bi_endio(struct bio *bio
  * sent to use, as some ULDs use that struct to only organize the pages.
  */
 static int scsi_req_map_sg(struct request *rq, struct scatterlist *sgl,
-			   int nsegs, unsigned bufflen, gfp_t gfp)
+	int nsegs, unsigned bufflen, gfp_t gfp, enum dma_data_direction dir)
 {
 	struct request_queue *q = rq->q;
 	int nr_pages = (bufflen + sgl[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -337,7 +338,7 @@ static int scsi_req_map_sg(struct reques
 			}

 			if (bio->bi_vcnt >= nr_vecs) {
-				err = scsi_merge_bio(rq, bio);
+				err = scsi_merge_bio(rq, bio ,dir);
 				if (err) {
 					bio_endio(bio, bio->bi_size, 0);
 					goto free_bios;
@@ -352,12 +353,12 @@ static int scsi_req_map_sg(struct reques
 	}

 	rq->buffer = rq->data = NULL;
-	rq_uni(rq)->data_len = data_len;
+	rq_io(rq,dir)->data_len = data_len;
 	return 0;

 free_bios:
-	while ((bio = rq_uni(rq)->bio) != NULL) {
-		rq_uni(rq)->bio = bio->bi_next;
+	while ((bio = rq_io(rq,dir)->bio) != NULL) {
+		rq_io(rq,dir)->bio = bio->bi_next;
 		/*
 		 * call endio instead of bio_put incase it was bounced
 		 */
@@ -385,31 +386,89 @@ int scsi_execute_async(struct scsi_devic
 		       int use_sg, int timeout, int retries, void *privdata,
 		       void (*done)(void *, char *, int, int), gfp_t gfp)
 {
+	struct scsi_cmnd_buff buff;
+
+	buff.use_sg = use_sg;
+	buff.buffer = buffer;
+	buff.bufflen = bufflen;
+
+	return scsi_execute_bidi_async(
+		sdev, (unsigned char *)cmd, cmd_len, data_direction,
+		&buff, NULL,
+		timeout, retries, privdata, done, gfp );
+}
+EXPORT_SYMBOL_GPL(scsi_execute_async);
+
+/**
+ * scsi_execute_bidi_async - insert bidi request, don't wait
+ * @sdev:	scsi device
+ * @cmd:	scsi command
+ * @cmd_len:	length of scsi cdb
+ * @data_direction: data direction
+ * @bidi_write_buff.buffer:	data buffer (this can be a kernel buffer or scatterlist)
+ * @bidi_write_buff.bufflen:	len of buffer
+ * @bidi_write_buff.use_sg:	if buffer is a scatterlist this is the number of elements
+ * @bidi_read_buff: same as above bidi_write_buff but for the bidi read part
+ * @timeout:	request timeout in jiffies
+ * @retries:	number of times to retry request
+ * @privdata:   user data passed to done function
+ * @done:       pointer to done function called at io completion.
+ *              signature: void done(void *user_data, char *sence, int errors, int data_bytes_advanced)
+ * @gfp:	gfp allocation flags
+ **/
+int scsi_execute_bidi_async(struct scsi_device *sdev,
+	unsigned char *cmd, int cmd_len, int data_direction,
+	struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff,
+	int timeout, int retries, void *privdata,
+	void (*done)(void *, char *, int, int), gfp_t gfp)
+{
 	struct request *req;
 	struct scsi_io_context *sioc;
 	int err = 0;
+	int is_bidi;

 	sioc = kmem_cache_alloc(scsi_io_context_cache, gfp);
 	if (!sioc)
 		return DRIVER_ERROR << 24;
 	memset(sioc, 0, sizeof(*sioc));

+	WARN_ON( (data_direction==DMA_NONE) && buff->bufflen);
+	WARN_ON( (data_direction!=DMA_NONE) && !buff->bufflen);
+	WARN_ON( (data_direction==DMA_BIDIRECTIONAL) && !bidi_read_buff);
 	req = blk_get_request(sdev->request_queue, data_direction, gfp);
 	if (!req)
 		goto free_sense;
 	req->cmd_type = REQ_TYPE_BLOCK_PC;
 	req->cmd_flags |= REQ_QUIET;
-
-	if (use_sg)
-		err = scsi_req_map_sg(req, buffer, use_sg, bufflen, gfp);
-	else if (bufflen)
-		err = blk_rq_map_kern(req->q, req, buffer, bufflen, gfp);
-
+	is_bidi = (data_direction == DMA_BIDIRECTIONAL);
+	if (buff->use_sg)
+		err = scsi_req_map_sg(
+			req, buff->buffer, buff->use_sg, buff->bufflen, gfp,
+			is_bidi ? DMA_TO_DEVICE : data_direction);
+	else if (buff->bufflen)
+		err = blk_rq_map_kern_bidi(
+			req->q, req, buff->buffer, buff->bufflen, gfp,
+			is_bidi ? DMA_TO_DEVICE : data_direction);
 	if (err)
 		goto free_req;

 	req->cmd_len = cmd_len;
 	memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */
+	if (is_bidi) {
+		if (bidi_read_buff->use_sg)
+			err = scsi_req_map_sg(
+				req, bidi_read_buff->buffer, bidi_read_buff->use_sg,
+				bidi_read_buff->bufflen, gfp, DMA_FROM_DEVICE);
+		else if (buff->bufflen)
+			err = blk_rq_map_kern_bidi(
+				req->q, req, bidi_read_buff->buffer,
+				bidi_read_buff->bufflen, gfp, DMA_FROM_DEVICE);
+		if (err) {
+			end_that_request_block(req ,0);/* free up the write part */
+			goto free_req;
+		}
+	}
+
 	memcpy(req->cmd, cmd, req->cmd_len);
 	req->sense = sioc->sense;
 	req->sense_len = 0;
@@ -429,7 +488,60 @@ free_sense:
 	kmem_cache_free(scsi_io_context_cache, sioc);
 	return DRIVER_ERROR << 24;
 }
-EXPORT_SYMBOL_GPL(scsi_execute_async);
+EXPORT_SYMBOL_GPL(scsi_execute_bidi_async);
+
+struct scsi_execute_bidi_done_t {
+	struct completion *waiting;
+	char* sense;
+	int errors;
+} ;
+
+static void scsi_execute_bidi_done(void *user_data, char *sense, int errors, int data_len)
+{
+	struct scsi_execute_bidi_done_t *sebd = user_data;
+	sebd->errors = errors;
+	if (sebd->sense) {
+		memcpy(sebd->sense, sense, SCSI_SENSE_BUFFERSIZE);
+	}
+	complete(sebd->waiting);
+}
+
+/**
+ * scsi_execute_bidi - insert a bidi request
+ * @sdev:	scsi device
+ * @cmd:	scsi command
+ * @cmd_len:	length of scsi cdb
+ * @data_direction: data direction
+ * @bidi_write_buff.buffer:	data buffer (this can be a kernel buffer or scatterlist)
+ * @bidi_write_buff.bufflen:	len of buffer
+ * @bidi_write_buff.use_sg:	if buffer is a scatterlist this is the number of elements
+ * @bidi_read_buff: same as above bidi_write_buff but for the bidi read part. can be NULL
+ * @sense:	optional sense buffer
+ * @timeout:	request timeout in jiffies
+ * @retries:	number of times to retry request
+ **/
+int scsi_execute_bidi(struct scsi_device *sdev,
+	unsigned char *cmd, int cmd_len, int data_direction,
+	struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff,
+	char* sense, int timeout, int retries)
+{
+	int ret;
+	struct scsi_execute_bidi_done_t sebd;
+	DECLARE_COMPLETION_ONSTACK(wait);
+	sebd.sense = sense;
+	sebd.waiting = &wait;
+
+	ret = scsi_execute_bidi_async(sdev,cmd, cmd_len, data_direction,
+		buff, bidi_read_buff,timeout, retries,
+		&sebd, scsi_execute_bidi_done, __GFP_WAIT);
+	if (ret)
+		return ret;
+
+	wait_for_completion(&wait);
+
+	return sebd.errors;
+}
+EXPORT_SYMBOL_GPL(scsi_execute_bidi);

 /*
  * Function:    scsi_init_cmd_errh()
@@ -660,6 +772,16 @@ static struct scsi_cmnd *scsi_end_reques
 	struct request *req = cmd->request;
 	unsigned long flags;

+	if (is_bidi_cmnd(cmd)) {
+		if ( end_io_error(uptodate) && requeue ) {
+			WARN_ON(bytes);
+			scsi_requeue_command(q, cmd);
+			return NULL;
+		}
+		end_that_request_block(req, uptodate);
+		goto common_code;
+	}
+
 	/*
 	 * If there are blocks left over at the end, set up the command
 	 * to queue the remainder of them.
@@ -687,6 +809,7 @@ static struct scsi_cmnd *scsi_end_reques
 		}
 	}

+common_code:
 	add_disk_randomness(req->rq_disk);

 	spin_lock_irqsave(q->queue_lock, flags);
@@ -703,34 +826,34 @@ static struct scsi_cmnd *scsi_end_reques
 	return NULL;
 }

-struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+static struct scatterlist *__scsi_alloc_sgtable(struct scsi_data_buffer *sdb, gfp_t gfp_mask)
 {
 	struct scsi_host_sg_pool *sgp;
 	struct scatterlist *sgl;

-	BUG_ON(!cmd->use_sg);
+	BUG_ON(!sdb->use_sg);

-	switch (cmd->use_sg) {
+	switch (sdb->use_sg) {
 	case 1 ... 8:
-		cmd->sglist_len = 0;
+		sdb->sglist_len = 0;
 		break;
 	case 9 ... 16:
-		cmd->sglist_len = 1;
+		sdb->sglist_len = 1;
 		break;
 	case 17 ... 32:
-		cmd->sglist_len = 2;
+		sdb->sglist_len = 2;
 		break;
 #if (SCSI_MAX_PHYS_SEGMENTS > 32)
 	case 33 ... 64:
-		cmd->sglist_len = 3;
+		sdb->sglist_len = 3;
 		break;
 #if (SCSI_MAX_PHYS_SEGMENTS > 64)
 	case 65 ... 128:
-		cmd->sglist_len = 4;
+		sdb->sglist_len = 4;
 		break;
 #if (SCSI_MAX_PHYS_SEGMENTS  > 128)
 	case 129 ... 256:
-		cmd->sglist_len = 5;
+		sdb->sglist_len = 5;
 		break;
 #endif
 #endif
@@ -739,11 +862,25 @@ #endif
 		return NULL;
 	}

-	sgp = scsi_sg_pools + cmd->sglist_len;
+	sgp = scsi_sg_pools + sdb->sglist_len;
 	sgl = mempool_alloc(sgp->pool, gfp_mask);
 	return sgl;
 }

+struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+{
+	struct scsi_data_buffer uni_sdb;
+	struct scatterlist *ret = __scsi_alloc_sgtable(&uni_sdb, gfp_mask);
+
+	if (likely(ret)) {
+		cmd->request_bufflen = uni_sdb.request_bufflen;
+		cmd->request_buffer = uni_sdb.request_buffer;
+		cmd->use_sg = uni_sdb.use_sg;
+		cmd->sglist_len = uni_sdb.sglist_len;
+	}
+	return ret;
+}
+
 EXPORT_SYMBOL(scsi_alloc_sgtable);

 void scsi_free_sgtable(struct scatterlist *sgl, int index)
@@ -777,7 +914,7 @@ EXPORT_SYMBOL(scsi_free_sgtable);
  */
 static void scsi_release_buffers(struct scsi_cmnd *cmd)
 {
-	if (cmd->use_sg)
+	if (cmd->use_sg && cmd->request_buffer)
 		scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);

 	/*
@@ -786,6 +923,15 @@ static void scsi_release_buffers(struct
 	 */
 	cmd->request_buffer = NULL;
 	cmd->request_bufflen = 0;
+
+	if (is_bidi_cmnd(cmd) && cmd->bidi_read_sdb.request_buffer) {
+		if (cmd->bidi_read_sdb.use_sg)
+			scsi_free_sgtable(
+				cmd->bidi_read_sdb.request_buffer,
+				cmd->bidi_read_sdb.sglist_len);
+		cmd->bidi_read_sdb.request_buffer = NULL;
+		cmd->bidi_read_sdb.request_bufflen = 0;
+	}
 }

 /*
@@ -850,17 +996,19 @@ void scsi_io_completion(struct scsi_cmnd
 				memcpy(req->sense, cmd->sense_buffer,  len);
 				req->sense_len = len;
 			}
-		} else
-			rq_uni(req)->data_len = cmd->resid;
+		} else {
+			/* FIXME: implement bidi resid */
+			req->uni.data_len = cmd->resid;
+		}
 	}

 	/*
 	 * Next deal with any sectors which we were able to correctly
 	 * handle.
 	 */
-	SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, "
-				      "%d bytes done.\n",
-				      rq_uni(req)->nr_sectors, good_bytes));
+	SCSI_LOG_HLCOMPLETE(1, printk(
+		"%ld(in) sectors %ld(out) sectors total, %d bytes done.\n",
+		rq_in(req)->nr_sectors, rq_out(req)->nr_sectors, good_bytes));
 	SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg));

 	if (clear_errors)
@@ -873,6 +1021,8 @@ void scsi_io_completion(struct scsi_cmnd
 	if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL)
 		return;

+	BUG_ON(is_bidi_cmnd(cmd));
+
 	/* good_bytes = 0, or (inclusive) there were leftovers and
 	 * result = 0, so scsi_end_request couldn't retry.
 	 */
@@ -982,19 +1132,24 @@ void scsi_io_completion(struct scsi_cmnd
 EXPORT_SYMBOL(scsi_io_completion);

 /*
- * Function:    scsi_init_io()
+ * Function:    scsi_init_data_buf()
  *
- * Purpose:     SCSI I/O initialize function.
+ * Purpose:     SCSI I/O initialize helper.
+ *              maps the request buffers into the given sdb.
  *
- * Arguments:   cmd   - Command descriptor we wish to initialize
+ * Arguments:   sdb     - Command data buff we wish to initialize
+ *              req     - request to map from
+ *              dir     - which part of the request to map
  *
  * Returns:     0 on success
  *		BLKPREP_DEFER if the failure is retryable
  *		BLKPREP_KILL if the failure is fatal
  */
-static int scsi_init_io(struct scsi_cmnd *cmd)
+static int scsi_init_data_buff(
+	struct scsi_data_buffer *sdb, struct request* req,
+	enum dma_data_direction dir)
 {
-	struct request     *req = cmd->request;
+	struct request_io_part *req_io = rq_io(req, dir);
 	struct scatterlist *sgpnt;
 	int		   count;

@@ -1003,45 +1158,87 @@ static int scsi_init_io(struct scsi_cmnd
 	 * but now we do (it makes highmem I/O easier to support without
 	 * kmapping pages)
 	 */
-	cmd->use_sg = rq_uni(req)->nr_phys_segments;
+	sdb->use_sg = req_io->nr_phys_segments;

 	/*
 	 * If sg table allocation fails, requeue request later.
 	 */
-	sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
+	sgpnt = __scsi_alloc_sgtable(sdb, GFP_ATOMIC);
 	if (unlikely(!sgpnt)) {
 		scsi_unprep_request(req);
 		return BLKPREP_DEFER;
 	}

-	req->buffer = NULL;
-	cmd->request_buffer = (char *) sgpnt;
+	sdb->request_buffer = (char *) sgpnt;
 	if (blk_pc_request(req))
-		cmd->request_bufflen = rq_uni(req)->data_len;
+		sdb->request_bufflen = req_io->data_len;
 	else
-		cmd->request_bufflen = rq_uni(req)->nr_sectors << 9;
+		sdb->request_bufflen = req_io->nr_sectors << 9;

 	/*
 	 * Next, walk the list, and fill in the addresses and sizes of
 	 * each segment.
 	 */
-	count = blk_rq_map_sg(req->q, req, cmd->request_buffer);
-	if (likely(count <= cmd->use_sg)) {
-		cmd->use_sg = count;
+	count = blk_rq_map_sg_bidi(req->q, req, sdb->request_buffer, dir);
+	if (likely(count <= sdb->use_sg)) {
+		sdb->use_sg = count;
 		return BLKPREP_OK;
 	}

 	printk(KERN_ERR "Incorrect number of segments after building list\n");
-	printk(KERN_ERR "counted %d, received %d\n", count, cmd->use_sg);
-	printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", rq_uni(req)->nr_sectors,
-			rq_uni(req)->current_nr_sectors);
+	printk(KERN_ERR "counted %d, received %d\n", count, sdb->use_sg);
+	printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req_io->nr_sectors,
+			req_io->current_nr_sectors);

-	/* release the command and kill it */
-	scsi_release_buffers(cmd);
-	scsi_put_command(cmd);
 	return BLKPREP_KILL;
 }

+/*
+ * Function:    scsi_init_io()
+ *
+ * Purpose:     SCSI I/O initialize function.
+ *
+ * Arguments:   cmd   - Command descriptor we wish to initialize
+ *
+ * Returns:     0 on success
+ *		BLKPREP_DEFER if the failure is retryable
+ *		BLKPREP_KILL if the failure is fatal
+ */
+static int scsi_init_io(struct scsi_cmnd *cmd)
+{
+	struct scsi_data_buffer uni_sdb;
+	struct request *req = cmd->request;
+	int error;
+	int is_bidi = is_bidi_cmnd(cmd);
+
+	error = scsi_init_data_buff(&uni_sdb, req,
+		is_bidi ? DMA_TO_DEVICE : rq_dma_dir(req) );
+	if (error)
+		goto err_exit;
+
+	cmd->request_bufflen = uni_sdb.request_bufflen;
+	cmd->request_buffer = uni_sdb.request_buffer;
+	cmd->use_sg = uni_sdb.use_sg;
+	cmd->sglist_len = uni_sdb.sglist_len;
+
+	if (is_bidi) {
+		error = scsi_init_data_buff(&cmd->bidi_read_sdb, req, DMA_FROM_DEVICE);
+		if (error)
+			goto err_exit;
+	}
+
+	req->buffer = NULL;
+	return 0 ;
+
+err_exit:
+	scsi_release_buffers(cmd);
+	if (error == BLKPREP_KILL) {
+		/* release the command and kill it */
+		scsi_put_command(cmd);
+	}
+	return error;
+}
+
 static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk,
 			       sector_t *error_sector)
 {
@@ -1094,6 +1291,7 @@ static void scsi_blk_pc_done(struct scsi
 static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
 {
 	struct scsi_cmnd *cmd;
+	int has_data;

 	cmd = scsi_get_cmd_from_req(sdev, req);
 	if (unlikely(!cmd))
@@ -1105,22 +1303,22 @@ static int scsi_setup_blk_pc_cmnd(struct
 	 * that does not transfer data, in which case they may optionally
 	 * submit a request without an attached bio.
 	 */
-	if (rq_uni(req)->bio) {
-		int ret;
-
-		BUG_ON(!rq_uni(req)->nr_phys_segments);
+	has_data = rq_out(req)->bio || rq_in(req)->bio;

+	if (has_data) {
+		int ret;
+		BUG_ON(rq_bidi_dir(req) && (!rq_out(req)->bio || !rq_in(req)->bio));
 		ret = scsi_init_io(cmd);
 		if (unlikely(ret))
 			return ret;
 	} else {
-		BUG_ON(rq_uni(req)->data_len);
-		BUG_ON(req->data);
+		WARN_ON(rq_uni_dir(req) && rq_uni(req)->data_len);
+		WARN_ON(rq_bidi_dir(req));
+		/* FIXME: WARN_ON(rq_dma_dir!=DMA_NONE) */

 		cmd->request_bufflen = 0;
 		cmd->request_buffer = NULL;
 		cmd->use_sg = 0;
-		req->buffer = NULL;
 		req->data_dir = DMA_NONE;
 	}

@@ -1129,7 +1327,7 @@ static int scsi_setup_blk_pc_cmnd(struct
 	cmd->cmd_len = req->cmd_len;
 	cmd->sc_data_direction = rq_dma_dir(req);
 	
-	cmd->transfersize = rq_uni(req)->data_len;
+	cmd->transfersize = is_bidi_cmnd(cmd) ? 0 : rq_uni(req)->data_len;
 	cmd->allowed = req->retries;
 	cmd->timeout_per_command = req->timeout;
 	cmd->done = scsi_blk_pc_done;
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index d6948d0..78ae417 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -1,16 +1,34 @@
 #ifndef _SCSI_SCSI_CMND_H
 #define _SCSI_SCSI_CMND_H

-#include <linux/dma-mapping.h>
+#include <linux/blkdev.h>
 #include <linux/list.h>
 #include <linux/types.h>
 #include <linux/timer.h>
+#include <scsi/scsi_device.h>

-struct request;
 struct scatterlist;
 struct Scsi_Host;
 struct scsi_device;

+/*
+ * This structure maps data buffers into a scatter-gather list for DMA purposes.
+ * Embedded in struct scsi_cmnd.
+ *
+ * FIXME: We currently embed this structure in scsi_cmnd only for
+ * bidi read buffers.  Buffers for uni-directional commands and write
+ * buffers of bidi commands are mapped in a backward compatible way by an
+ * equivalent set of fields, scattered in the scsi_cmnd.
+ * These should be incorporated into an instance of scsi_cmn_sgl.
+ * This will require a major rework of most scsi LLDDs.
+ */
+
+struct scsi_data_buffer {
+	unsigned short use_sg;       /* Number of pieces of scatter-gather */
+	unsigned short sglist_len;   /* size of malloc'd scatter-gather list */
+	void *request_buffer;        /* Actual requested buffer */
+	unsigned request_bufflen;    /* Actual request size */
+};

 /* embedded in scsi_cmnd */
 struct scsi_pointer {
@@ -120,8 +138,41 @@ #define SCSI_SENSE_BUFFERSIZE 	96

 	unsigned char tag;	/* SCSI-II queued command tag */
 	unsigned long pid;	/* Process ID, starts at 0. Unique per host. */
+
+	/*
+	 * map read buffers of bi-directional commands
+	 *  iff sc_data_direction==DMA_BIDIRECTIONAL
+	 */
+	struct scsi_data_buffer bidi_read_sdb;
 };

+#define is_bidi_cmnd(cmd) rq_bidi_dir((cmd)->request)
+
+/*
+ * these inline helpers take into account the scsi cmnd's data direction
+ * to correctly find sg maps for uni-directional and bi-directional commands
+ */
+static inline void
+scsi_get_out_buff(struct scsi_cmnd *sc, struct scsi_cmnd_buff *scb)
+{
+	scb->use_sg = sc->use_sg;
+	scb->buffer = sc->request_buffer;
+	scb->bufflen = sc->request_bufflen;
+}
+
+static inline void
+scsi_get_in_buff(struct scsi_cmnd *sc, struct scsi_cmnd_buff *scb)
+{
+	if (!is_bidi_cmnd(sc)) {
+		scsi_get_out_buff(sc, scb);
+		return;
+	}
+
+	scb->use_sg = sc->bidi_read_sdb.use_sg;
+	scb->buffer = sc->bidi_read_sdb.request_buffer;
+	scb->bufflen = sc->bidi_read_sdb.request_bufflen;
+}
+
 extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t);
 extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t);
 extern void scsi_put_command(struct scsi_cmnd *);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index ebf31b1..a10989c 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -198,6 +198,13 @@ #define transport_class_to_starget(class
 #define starget_printk(prefix, starget, fmt, a...)	\
 	dev_printk(prefix, &(starget)->dev, fmt, ##a)

+/* used by APIs as a shorthand for <use_sg, sg, bufflen> */
+struct scsi_cmnd_buff {
+	unsigned short use_sg;      /* Number of pieces of scatter-gather */
+	void *buffer;       /* if use_sg==0 requested buffer else an sg list */
+	unsigned bufflen;   /* Actual request size */
+};
+
 extern struct scsi_device *__scsi_add_device(struct Scsi_Host *,
 		uint, uint, uint, void *hostdata);
 extern int scsi_add_device(struct Scsi_Host *host, uint channel,
@@ -297,6 +304,15 @@ extern int scsi_execute_async(struct scs
 			      int timeout, int retries, void *privdata,
 			      void (*done)(void *, char *, int, int),
 			      gfp_t gfp);
+extern int scsi_execute_bidi_async(struct scsi_device *sdev,
+	unsigned char *cmd, int cmd_len, int data_direction,
+	struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff,
+	int timeout, int retries, void *privdata,
+	void (*done)(void *, char *, int, int), gfp_t gfp);
+extern int scsi_execute_bidi(struct scsi_device *sdev,
+	unsigned char *cmd, int cmd_len, int data_direction,
+	struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff,
+	char* sense, int timeout, int retries);

 static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev)
 {
-- 

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-01-21 23:27 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-01-21 23:26 [RFC 4/6] bidi support: bidirectional SCSI layer Boaz Harrosh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).