From: Boaz Harrosh <[EMAIL PROTECTED]>
Date: Thu, 13 Dec 2007 13:50:53 +0200
Subject: [SCSI] bidirectional command support

At the block level bidi request uses req->next_rq pointer for a second
bidi_read request.
At Scsi-midlayer a second scsi_data_buffer structure is used for the
bidi_read part. This bidi scsi_data_buffer is put on
request->next_rq->special. Struct scsi_cmnd is not changed.

- Define scsi_bidi_cmnd() to return true if it is a bidi request and a
  second sgtable was allocated.

- Define scsi_in()/scsi_out() to return the in or out scsi_data_buffer
  from this command This API is to isolate users from the mechanics of
  bidi.

- Define scsi_end_bidi_request() to do what scsi_end_request() does but
  for a bidi request. This is necessary because bidi commands are a bit
  tricky here. (See comments in body)

- scsi_release_buffers() will also release the bidi_read scsi_data_buffer

- scsi_io_completion() on bidi commands will now call
  scsi_end_bidi_request() and return.

- The previous work done in scsi_init_io() is now done in a new
  scsi_init_sgtable() (which is 99% identical to old scsi_init_io())
  The new scsi_init_io() will call the above twice if needed also for
  the bidi_read command. Only at this point is a command bidi.

- In scsi_error.c at scsi_eh_prep/restore_cmnd() make sure bidi-lld is not
  confused by a get-sense command that looks like bidi. This is done
  by puting NULL at request->next_rq, and restoring.

[jejb: update to sg_table and resolve conflicts]

Signed-off-by: Boaz Harrosh <[EMAIL PROTECTED]>
Signed-off-by: James Bottomley <[EMAIL PROTECTED]>
---
 drivers/scsi/scsi_error.c |    3 +
 drivers/scsi/scsi_lib.c   |  144 ++++++++++++++++++++++++++++++++++++---------
 include/scsi/scsi_cmnd.h  |   23 +++++++-
 include/scsi/scsi_eh.h    |    1 +
 4 files changed, 141 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 8752a9f..65d02e8 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -618,9 +618,11 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct 
scsi_eh_save *ses,
        memcpy(ses->cmnd, scmd->cmnd, sizeof(scmd->cmnd));
        ses->data_direction = scmd->sc_data_direction;
        ses->sdb = scmd->sdb;
+       ses->next_rq = scmd->request->next_rq;
        ses->result = scmd->result;
 
        memset(&scmd->sdb, 0, sizeof(scmd->sdb));
+       scmd->request->next_rq = NULL;
 
        if (sense_bytes) {
                scmd->sdb.length = min_t(unsigned,
@@ -673,6 +675,7 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct 
scsi_eh_save *ses)
        memcpy(scmd->cmnd, ses->cmnd, sizeof(scmd->cmnd));
        scmd->sc_data_direction = ses->data_direction;
        scmd->sdb = ses->sdb;
+       scmd->request->next_rq = ses->next_rq;
        scmd->result = ses->result;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 90d5ee0..3c681de 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -64,6 +64,8 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = {
 };
 #undef SP
 
+static struct kmem_cache *scsi_bidi_sdb_cache;
+
 static void scsi_run_queue(struct request_queue *q);
 
 /*
@@ -627,6 +629,28 @@ void scsi_run_host_queues(struct Scsi_Host *shost)
                scsi_run_queue(sdev->request_queue);
 }
 
+static void scsi_finalize_request(struct scsi_cmnd *cmd, int uptodate)
+{
+       struct request_queue *q = cmd->device->request_queue;
+       struct request *req = cmd->request;
+       unsigned long flags;
+
+       add_disk_randomness(req->rq_disk);
+
+       spin_lock_irqsave(q->queue_lock, flags);
+       if (blk_rq_tagged(req))
+               blk_queue_end_tag(q, req);
+
+       end_that_request_last(req, uptodate);
+       spin_unlock_irqrestore(q->queue_lock, flags);
+
+       /*
+        * This will goose the queue request function at the end, so we don't
+        * need to worry about launching another command.
+        */
+       scsi_next_command(cmd);
+}
+
 /*
  * Function:    scsi_end_request()
  *
@@ -654,7 +678,6 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd 
*cmd, int uptodate,
 {
        struct request_queue *q = cmd->device->request_queue;
        struct request *req = cmd->request;
-       unsigned long flags;
 
        /*
         * If there are blocks left over at the end, set up the command
@@ -683,19 +706,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd 
*cmd, int uptodate,
                }
        }
 
-       add_disk_randomness(req->rq_disk);
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       if (blk_rq_tagged(req))
-               blk_queue_end_tag(q, req);
-       end_that_request_last(req, uptodate);
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       /*
-        * This will goose the queue request function at the end, so we don't
-        * need to worry about launching another command.
-        */
-       scsi_next_command(cmd);
+       scsi_finalize_request(cmd, uptodate);
        return NULL;
 }
 
@@ -797,10 +808,39 @@ void scsi_release_buffers(struct scsi_cmnd *cmd)
                scsi_free_sgtable(&cmd->sdb);
 
        memset(&cmd->sdb, 0, sizeof(cmd->sdb));
+
+       if (scsi_bidi_cmnd(cmd)) {
+               struct scsi_data_buffer *bidi_sdb =
+                       cmd->request->next_rq->special;
+               scsi_free_sgtable(bidi_sdb);
+               kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb);
+               cmd->request->next_rq->special = NULL;
+       }
 }
 EXPORT_SYMBOL(scsi_release_buffers);
 
 /*
+ * Bidi commands Must be complete as a whole, both sides at once.
+ * If part of the bytes were written and lld returned
+ * scsi_in()->resid and/or scsi_out()->resid this information will be left
+ * in req->data_len and req->next_rq->data_len. The upper-layer driver can
+ * decide what to do with this information.
+ */
+void scsi_end_bidi_request(struct scsi_cmnd *cmd)
+{
+       struct request *req = cmd->request;
+
+       end_that_request_chunk(req, 1, req->data_len);
+       req->data_len = scsi_out(cmd)->resid;
+
+       end_that_request_chunk(req->next_rq, 1, req->next_rq->data_len);
+       req->next_rq->data_len = scsi_in(cmd)->resid;
+
+       scsi_release_buffers(cmd);
+       scsi_finalize_request(cmd, 1);
+}
+
+/*
  * Function:    scsi_io_completion()
  *
  * Purpose:     Completion processing for block device I/O requests.
@@ -861,9 +901,15 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned 
int good_bytes)
                                req->sense_len = len;
                        }
                }
+               if (scsi_bidi_cmnd(cmd)) {
+                       /* will also release_buffers */
+                       scsi_end_bidi_request(cmd);
+                       return;
+               }
                req->data_len = scsi_get_resid(cmd);
        }
 
+       BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet */
        scsi_release_buffers(cmd);
 
        /*
@@ -989,28 +1035,16 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned 
int good_bytes)
        scsi_end_request(cmd, 0, this_count, !result);
 }
 
-/*
- * 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
- */
-int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
+                            gfp_t gfp_mask)
 {
-       struct request     *req = cmd->request;
-       int                count;
-       struct scsi_data_buffer *sdb = &cmd->sdb;
+       int count;
 
        /*
         * If sg table allocation fails, requeue request later.
         */
        if (unlikely(scsi_alloc_sgtable(sdb, req->nr_phys_segments,
                                        gfp_mask))) {
-               scsi_unprep_request(req);
                return BLKPREP_DEFER;
        }
 
@@ -1029,6 +1063,50 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
        sdb->table.nents = count;
        return BLKPREP_OK;
 }
+
+/*
+ * 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
+ */
+int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+{
+       int error = scsi_init_sgtable(cmd->request, &cmd->sdb, gfp_mask);
+       if (error)
+               goto err_exit;
+
+       if (blk_bidi_rq(cmd->request)) {
+               struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
+                       scsi_bidi_sdb_cache, GFP_ATOMIC);
+               if (!bidi_sdb) {
+                       error = BLKPREP_DEFER;
+                       goto err_exit;
+               }
+
+               cmd->request->next_rq->special = bidi_sdb;
+               error = scsi_init_sgtable(cmd->request->next_rq, bidi_sdb,
+                                                                   GFP_ATOMIC);
+               if (error)
+                       goto err_exit;
+       }
+
+       return BLKPREP_OK ;
+
+err_exit:
+       scsi_release_buffers(cmd);
+       if (error == BLKPREP_KILL)
+               scsi_put_command(cmd);
+       else /* BLKPREP_DEFER */
+               scsi_unprep_request(cmd->request);
+
+       return error;
+}
 EXPORT_SYMBOL(scsi_init_io);
 
 static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev,
@@ -1646,6 +1724,14 @@ int __init scsi_init_queue(void)
                return -ENOMEM;
        }
 
+       scsi_bidi_sdb_cache = kmem_cache_create("scsi_bidi_sdb",
+                                       sizeof(struct scsi_data_buffer),
+                                       0, 0, NULL);
+       if (!scsi_bidi_sdb_cache) {
+               printk(KERN_ERR "SCSI: can't init scsi bidi sdb cache\n");
+               return -ENOMEM;
+       }
+
        for (i = 0; i < SG_MEMPOOL_NR; i++) {
                struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
                int size = sgp->size * sizeof(struct scatterlist);
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index d95f0bf..90d8c5a 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -2,12 +2,12 @@
 #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 <linux/scatterlist.h>
 
-struct request;
 struct Scsi_Host;
 struct scsi_device;
 
@@ -158,4 +158,25 @@ static inline int scsi_get_resid(struct scsi_cmnd *cmd)
 #define scsi_for_each_sg(cmd, sg, nseg, __i)                   \
        for_each_sg(scsi_sglist(cmd), sg, nseg, __i)
 
+static inline int scsi_bidi_cmnd(struct scsi_cmnd *cmd)
+{
+       return blk_bidi_rq(cmd->request) &&
+               (cmd->request->next_rq->special != NULL);
+}
+
+static inline struct scsi_data_buffer *scsi_in(struct scsi_cmnd *cmd)
+{
+       WARN_ON((cmd->sc_data_direction != DMA_FROM_DEVICE) &&
+               !scsi_bidi_cmnd(cmd));
+       return scsi_bidi_cmnd(cmd) ?
+               cmd->request->next_rq->special : &cmd->sdb;
+}
+
+static inline struct scsi_data_buffer *scsi_out(struct scsi_cmnd *cmd)
+{
+       WARN_ON((cmd->sc_data_direction != DMA_TO_DEVICE) &&
+               !scsi_bidi_cmnd(cmd));
+       return &cmd->sdb;
+}
+
 #endif /* _SCSI_SCSI_CMND_H */
diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h
index 1e08be1..25071d5 100644
--- a/include/scsi/scsi_eh.h
+++ b/include/scsi/scsi_eh.h
@@ -74,6 +74,7 @@ struct scsi_eh_save {
        unsigned char cmd_len;
        unsigned char cmnd[MAX_COMMAND_SIZE];
        struct scsi_data_buffer sdb;
+       struct request *next_rq;
 
        /* new command support */
        struct scatterlist sense_sgl;
-- 
1.5.3.7



-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to