Patch from Erik Habbinga:
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 11 Jan 2007 12:39:10 +0000 (12:39 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 11 Jan 2007 12:39:10 +0000 (12:39 +0000)
There are a few minor changes:

- fix some 80 char line length issues
- fix some indenting
- change scst_to_dma_dir to scst_to_tgt_dma_dir

The big issue is fixing the sending of sense data.  The LSI chip, when talking to a non-packetized SCSI initiator, cannot send both
status (i.e. check condition) and sense data (from autosense) in the same transaction.  The result is a 006B
(MPI_IOC_STATUS_TARGET_STS_DATA_NOT_SENT) IOCStatus value when this is attempted.  The sense data doesn't get transmitted over the
SCSI cable in this case.  My fix, which is modeled on LSI's own SCSI target implementation, is to cache sense data before attempting
to send it.  If a REQUEST SENSE command comes in with pending sense, the LSI driver will handle the REQUEST SENSE command on it's
own without involving SCST.  Pending sense is cleared once the REQUEST SENSE command is handled, or if any other command comes in.
Sense data is cached per initiator.  Apparantely this problem is only related to non-packetized SCSI interfaces.  That's all I have
to test with, so I can't vouch for SAS or FC.

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@75 d57e44dd-8a1f-0410-8b47-8ef2f437770f

mpt/mpt_scst.c
mpt/mpt_scst.h

index ba2839a..67f3cac 100644 (file)
@@ -264,6 +264,7 @@ static struct scst_proc_data mpt_target_proc_data = {
 
 static int mpt_target_detect(struct scst_tgt_template *temp1);
 static int mpt_target_release(struct scst_tgt *scst_tgt);
+static int stmapp_pending_sense(struct mpt_cmd *mpt_cmd);
 static int mpt_xmit_response(struct scst_cmd *scst_cmd);
 static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd);
 static void mpt_on_free_cmd(struct scst_cmd *scst_cmd);
@@ -690,11 +691,11 @@ stm_data_done(MPT_ADAPTER *ioc, u32 reply_word,
                        pci_unmap_sg(priv->ioc->pcidev,
                                scst_cmd_get_sg(scst_cmd),
                                scst_cmd_get_sg_cnt(scst_cmd),
-                               scst_to_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
+                               scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
                } else {
                        pci_unmap_single(priv->ioc->pcidev, cmd->dma_handle,
                                scst_get_buf_first(scst_cmd, &buf),
-                               scst_to_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
+                               scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
                }
        }
        TRACE_EXIT();
@@ -705,6 +706,7 @@ stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
 {
        MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
        int index;
+       int init_index;
        struct scst_cmd *scst_cmd;
        struct mpt_cmd *cmd;
        volatile int *io_state;
@@ -712,6 +714,7 @@ stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
        TRACE_ENTRY();
 
        index = GET_IO_INDEX(reply_word);
+       init_index = GET_INITIATOR_INDEX(reply_word);
        scst_cmd = priv->scst_cmd[index];
        io_state = priv->io_state + index;
 
@@ -726,7 +729,7 @@ stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
                *io_state &= ~IO_STATE_POSTED;
        
                mpt_msg_frame_free(priv, index);
-               
+
                stmapp_tgt_command(priv, reply_word);
                goto out;
        }
@@ -786,7 +789,20 @@ stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
                        stm_cmd_buf_post(priv, index);
                }
 
-               scst_tgt_cmd_done(scst_cmd);
+               /*
+                * don't go through SCST if we've sent cached sense.
+                * We're done sending the sense, so clear the pending sense flag
+                */
+               if (IsScsi(priv) && 
+                       (atomic_read(&priv->pending_sense[init_index]) == 1) &&
+                       (scst_cmd->cdb[0] == REQUEST_SENSE)) {
+                       TRACE_DBG("%s: clearing pending sense", ioc->name);
+                       atomic_set(&priv->pending_sense[init_index], 0);
+                       mpt_on_free_cmd(scst_cmd);
+                       kfree(scst_cmd); /* created in stmapp_pending_sense */
+               } else {
+                       scst_tgt_cmd_done(scst_cmd);
+               }
 
                goto out;
        }
@@ -945,10 +961,15 @@ stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word)
        if (test_bit(MPT_SESS_INITING, &sess->sess_flags)) {
                list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds);
        } else {
-               res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_TASKLET);
-               /*res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_DIRECT_ATOMIC);*/
-               if (res != 0)
-                       goto out_free_cmd;
+               /* if there is pending sense left over from the last command,
+                * we need to send that if this is a REQUEST SENSE command.
+                * Otherwise send the command to SCST */
+               if (!stmapp_pending_sense(cmd)) {
+                       res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_TASKLET);
+                       /*res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_DIRECT_ATOMIC);*/
+                       if (res != 0)
+                               goto out_free_cmd;
+               }
        }
        
  out:
@@ -1035,11 +1056,11 @@ mpt_sge_to_sgl(struct mpt_prm *prm, MPT_STM_PRIV *priv, MPT_SGL *sgl)
                prm->sg = (struct scatterlist *)prm->buffer;
                prm->seg_cnt = 
                        pci_map_sg(priv->ioc->pcidev, prm->sg, prm->use_sg,
-                                  scst_to_dma_dir(prm->data_direction));
+                                  scst_to_tgt_dma_dir(prm->data_direction));
                
                pci_dma_sync_sg_for_cpu(priv->ioc->pcidev, prm->sg, 
                                prm->use_sg, 
-                               scst_to_dma_dir(prm->data_direction));
+                               scst_to_tgt_dma_dir(prm->data_direction));
                for (i = 0; i < prm->use_sg; i++) {
                        sgl->sge[i].length = sg_dma_len(&prm->sg[i]);
                        sgl->sge[i].address = sg_dma_address(&prm->sg[i]);
@@ -1053,19 +1074,25 @@ mpt_sge_to_sgl(struct mpt_prm *prm, MPT_STM_PRIV *priv, MPT_SGL *sgl)
                }
                pci_dma_sync_sg_for_device(priv->ioc->pcidev, prm->sg, 
                                prm->use_sg, 
-                               scst_to_dma_dir(prm->data_direction));
+                               scst_to_tgt_dma_dir(prm->data_direction));
        } else {
                prm->cmd->dma_handle = 
                        pci_map_single(priv->ioc->pcidev, prm->buffer, 
                                       prm->bufflen,
-                                      scst_to_dma_dir(prm->data_direction));
+                                      scst_to_tgt_dma_dir(prm->data_direction));
                
-               pci_dma_sync_single_for_cpu(priv->ioc->pcidev, prm->cmd->dma_handle, prm->bufflen, scst_to_dma_dir(prm->data_direction));
+               pci_dma_sync_single_for_cpu(priv->ioc->pcidev, 
+                               prm->cmd->dma_handle, 
+                               prm->bufflen, 
+                               scst_to_tgt_dma_dir(prm->data_direction));
                sgl->sge[0].length = prm->bufflen;
                sgl->sge[0].address = virt_to_phys(prm->buffer);
                
                mpt_dump_sge(&sgl->sge[0], NULL);
-               pci_dma_sync_single_for_device(priv->ioc->pcidev, prm->cmd->dma_handle, prm->bufflen, scst_to_dma_dir(prm->data_direction));
+               pci_dma_sync_single_for_device(priv->ioc->pcidev, 
+                               prm->cmd->dma_handle, 
+                               prm->bufflen, 
+                               scst_to_tgt_dma_dir(prm->data_direction));
 
                prm->seg_cnt = 1;
        }
@@ -1365,6 +1392,101 @@ mpt_send_target_data(struct mpt_prm *prm, int flags)
        return;
 }
 
+/*
+ * this function checks if any pending sense is available for
+ * a SCSI device.  This sense wasn't able to be sent for the
+ * last command with a check condition, so it needs to either
+ * get sent for a REQUEST SENSE command or forgotten.
+ */
+static int 
+stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
+{
+       int res = 0;
+       MPT_STM_PRIV *priv;
+       int index = 0;
+       int init_index = 0;
+       int flags = 0;
+       SCSI_CMD *scsi_cmd = NULL;
+       CMD *cmd;
+       u8 *cdb;
+       struct mpt_prm prm = { 0 };
+       struct scst_cmd *scst_cmd;
+
+       TRACE_ENTRY();
+
+       /* 
+        * check for pending sense if we're a SCSI controller, and
+        * send it back in response to REQUEST SENSE or clear it 
+        */
+       priv = mpt_cmd->priv;
+       if (IsScsi(priv)) {
+               index = GET_IO_INDEX(mpt_cmd->reply_word);
+               init_index = GET_INITIATOR_INDEX(mpt_cmd->reply_word);
+               if (atomic_read(&priv->pending_sense[init_index]) != 0) {
+                       cmd = &priv->hw->cmd_buf[index];
+                       scsi_cmd = (SCSI_CMD *)cmd->cmd;
+                       cdb = scsi_cmd->CDB;
+
+                       if (cdb[0] == REQUEST_SENSE) {
+                               /* scst_cmd used as a container in stm_tgt_reply,
+                                * command doesn't actually go to SCST */
+                               scst_cmd = kmalloc(sizeof(struct scst_cmd), 
+                                               GFP_ATOMIC);
+                               TRACE_DBG("scst_cmd 0x%p", scst_cmd);
+                               if (scst_cmd != NULL) {
+                                       cmd->reply_word = mpt_cmd->reply_word;
+                                       if (cmd->reply_word & 
+                                               TARGET_MODE_REPLY_ALIAS_MASK) {
+                                               cmd->alias = (scsi_cmd->AliasID - 
+                                                               priv->port_id) & 15;
+                                       } else {
+                                               cmd->alias = 0;
+                                       }
+                                       cmd->lun = get2bytes(scsi_cmd->LogicalUnitNumber, 
+                                                       0);
+                                       cmd->tag = scsi_cmd->Tag;
+                                       mpt_cmd->CMD = cmd;
+
+                                       memset(scst_cmd, 0x00, 
+                                               sizeof(struct scst_cmd));
+                                       scst_cmd->resp_data_len = -1;
+                                       memcpy(scst_cmd->cdb, cdb, 
+                                                       MPT_MAX_CDB_LEN);
+                                       priv->scst_cmd[index] = scst_cmd;
+                                       scst_cmd_set_tag(scst_cmd, cmd->tag);
+                                       scst_cmd_set_tgt_priv(scst_cmd, mpt_cmd);
+
+                                       TRACE_BUFFER("CDB", cdb, MPT_MAX_CDB_LEN);
+
+                                       flags = TARGET_ASSIST_FLAGS_AUTO_STATUS;
+                                       prm.cmd = mpt_cmd;
+                                       prm.bufflen = min((size_t)cdb[4], 
+                                                       (size_t)SCSI_SENSE_BUFFERSIZE);
+                                       prm.buffer = 
+                                               priv->pending_sense_buffer[init_index];
+                                       prm.use_sg = 0;
+                                       prm.data_direction = SCST_DATA_READ;
+                                       prm.tgt = priv->tgt->sess[init_index]->tgt;
+                                       prm.cmd->state = MPT_STATE_DATA_OUT;
+
+                                       TRACE_DBG("%s: sending pending sense", 
+                                                       priv->ioc->name);
+                                       mpt_send_target_data(&prm, flags);
+                                       res = 1;
+                               } else {
+                                       atomic_set(&priv->pending_sense[init_index], 
+                                                       0);
+                               }
+                       } else {
+                               atomic_set(&priv->pending_sense[init_index], 0);
+                       }
+               }
+       }
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 /*
  * this function is equivalent to the SCSI queuecommand(). The target should
  * transmit the response data and the status in the struct scst_cmd. See
@@ -1397,9 +1519,6 @@ mpt_xmit_response(struct scst_cmd *scst_cmd)
        prm.seg_cnt = 0;
        resp_flags = scst_cmd_get_tgt_resp_flags(scst_cmd);
 
-       /* FIXME */
-       prm.sense_buffer_len = 14;
-
        TRACE_DBG("rq_result=%x, resp_flags=%x, %x, %d", prm.rq_result, 
                        resp_flags, prm.bufflen, prm.sense_buffer_len);
        if (prm.rq_result != 0)
@@ -2802,10 +2921,10 @@ stm_send_target_status(MPT_STM_PRIV *priv,
     int                                length;
     int                                status;
     int                                init_index;
-       dma_addr_t                      dma_addr;
-       MPT_FRAME_HDR *mf = priv->current_mf[index];
+    dma_addr_t                 dma_addr;
+    MPT_FRAME_HDR *mf = priv->current_mf[index];
 
-       TRACE_ENTRY();
+    TRACE_ENTRY();
     if (priv->io_state[index] & IO_STATE_DATA_SENT) {
                priv->current_mf[index] = NULL;
        }
@@ -2840,6 +2959,7 @@ stm_send_target_status(MPT_STM_PRIV *priv,
        length = 0;
        if (IsScsi(priv)) {
            SCSI_RSP    *rsp = (SCSI_RSP *)cmd->rsp;
+           size_t sense_size;
 
            length += sizeof(*rsp);
            length -= sizeof(rsp->SenseData);
@@ -2855,6 +2975,21 @@ stm_send_target_status(MPT_STM_PRIV *priv,
                    flags &= ~TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
                    priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
                }
+               /*
+                * copy sense buffer so we can send it on the next
+                * REQUEST SENSE command if the IOC can't send the
+                * status and sense simultaneously (generating 
+                * MPT_IOC_STATUS_TARGET_STS_DATA_NOT_SENT IOCStatus)
+                */
+               sense_size = min(sizeof(rsp->SenseData), 
+                                      (size_t)SCSI_SENSE_BUFFERSIZE);
+               TRACE_DBG("caching %d bytes pending sense", sense_size);
+               memcpy(priv->pending_sense_buffer[init_index], 
+                               rsp->SenseData, sense_size);
+               TRACE_BUFFER("priv->pending_sense_buffer", 
+                               priv->pending_sense_buffer[init_index], 
+                               sense_size);
+               atomic_set(&priv->pending_sense[init_index], 1);
            }
            if (rsp->Valid & SCSI_RSP_LEN_VALID) {
                length += be32_to_cpu(rsp->PktFailuresListLength);
@@ -2918,37 +3053,37 @@ stm_send_target_status(MPT_STM_PRIV *priv,
      *  there's a limitation here -- if target data is outstanding, we must
      *  wait for it to finish before we send the target status
      */
-       if (priv->io_state[index] & IO_STATE_DATA_SENT) {
-               priv->current_mf[index] = mf;
-               priv->status_deferred_mf[index] = (MPT_FRAME_HDR *)req;
-               priv->io_state[index] |= IO_STATE_STATUS_DEFERRED;
-               TRACE_EXIT_RES(1);
-               return (1);
-       }
-       priv->io_state[index] |= IO_STATE_STATUS_SENT;
+    if (priv->io_state[index] & IO_STATE_DATA_SENT) {
+           priv->current_mf[index] = mf;
+           priv->status_deferred_mf[index] = (MPT_FRAME_HDR *)req;
+           priv->io_state[index] |= IO_STATE_STATUS_DEFERRED;
+           TRACE_EXIT_RES(1);
+           return (1);
+    }
+    priv->io_state[index] |= IO_STATE_STATUS_SENT;
 
 #ifdef TRACING
-       if(trace_mpi)
-       {
-               u32 *p = (u32 *)req;
-               int i;
+    if(trace_mpi)
+    {
+           u32 *p = (u32 *)req;
+           int i;
 
-               TRACE(TRACE_MPI, "%s: stm_send_target_status %d",
-                               ioc->name, index);
-               for (i = 0; i < sizeof(*req) / 4; i++) {
-                       TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
-                                       ioc->name, i * 4, le32_to_cpu(p[i]));
-               }
-       }
+           TRACE(TRACE_MPI, "%s: stm_send_target_status %d",
+                           ioc->name, index);
+           for (i = 0; i < sizeof(*req) / 4; i++) {
+                   TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
+                                   ioc->name, i * 4, le32_to_cpu(p[i]));
+           }
+    }
 #endif
 
-       if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
-               mpt_send_handshake_request(stm_context, _IOC_ID,
-                               sizeof(*req), (u32 *)req _HS_SLEEP);
-       } else {
-               mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
-       }
-       TRACE_EXIT_RES(1);
+    if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
+           mpt_send_handshake_request(stm_context, _IOC_ID,
+                           sizeof(*req), (u32 *)req _HS_SLEEP);
+    } else {
+           mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
+    }
+    TRACE_EXIT_RES(1);
     return (1);
 }
 
@@ -3522,7 +3657,7 @@ stm_scsi_configuration(MPT_STM_PRIV *priv,
     int                        flags;
     int                        i;
 
-       TRACE_ENTRY();
+    TRACE_ENTRY();
     memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
     if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 2, 0, sleep)) {
        return (-1);
@@ -3553,7 +3688,7 @@ stm_scsi_configuration(MPT_STM_PRIV *priv,
 
     for (i = 0; i < NUM_SCSI_DEVICES; i++) {
        int wide = 0;
-    SCSIDevicePage1_t  *ScsiDevice1 = &priv->SCSIDevicePage1[i];
+       SCSIDevicePage1_t *ScsiDevice1 = &priv->SCSIDevicePage1[i];
        sync = ScsiPort2->DeviceSettings[i].SyncFactor;
        flags = le16_to_cpu(ScsiPort2->DeviceSettings[i].DeviceFlags);
        if (flags & MPI_SCSIPORTPAGE2_DEVICE_WIDE_DISABLE)
@@ -3572,8 +3707,9 @@ stm_scsi_configuration(MPT_STM_PRIV *priv,
                   wide ? "WIDE" : " ");
        memcpy(priv->hw->config_buf, ScsiDevice1, sizeof(*ScsiDevice1));
        stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, 1, i, sleep);
+       atomic_set(&priv->pending_sense[i], 0);
     }
-       TRACE_EXIT();
+    TRACE_EXIT();
 
     return (0);
 }
@@ -4884,7 +5020,7 @@ stmapp_target_error_prioprity_io(MPT_STM_PRIV *priv,
         *  command buffer, so fake it out a bit
         */
        TRACE_ENTRY();
-    io_state = priv->io_state + index;
+       io_state = priv->io_state + index;
        *io_state |= IO_STATE_AUTO_REPOST;
        scst_cmd = priv->scst_cmd[index];
        mpt_cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
@@ -5075,13 +5211,23 @@ stmapp_target_error(MPT_STM_PRIV *priv,
      *  ignore the error here, so that we don't post this command buffer again
      *  (that is, treat this as a successful completion, which is what it is)
      */
-       if (IsScsi(priv) && status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT) {
-               if (*io_state & IO_STATE_AUTO_REPOST) {
-                       stm_tgt_reply(ioc, reply_word);
-                       TRACE_EXIT();
-                       return;
-               }
-       }
+    /*
+     *  Allow STATUS_SENT status to go through also, this is the
+     *  result of an attempt to send a check condition with 
+     *  attached sense bytes.  
+     *  The IOC knows it can't send status and sense over a 
+     *  traditional SCSI cable (non-packetized), so we should 
+     *  treat this as a successful completion, manually repost the
+     *  command to the IOC, and free the SCST command.
+     */
+    if (IsScsi(priv) && status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT) {
+           if ((*io_state & IO_STATE_AUTO_REPOST) ||
+               (*io_state & IO_STATE_STATUS_SENT)) {
+                   stm_tgt_reply(ioc, reply_word);
+                   TRACE_EXIT();
+                   return;
+           }
+    }
 
     *io_state &= ~IO_STATE_AUTO_REPOST;
     stm_target_cleanup(priv, index);
index b0dd43f..05d8cb5 100644 (file)
@@ -203,13 +203,15 @@ typedef struct _MPT_STM_PRIV
     MPT_FRAME_HDR      *current_mf[NUM_CMD_BUFFERS];
     MPT_FRAME_HDR      *status_deferred_mf[NUM_CMD_BUFFERS];
     MPT_SGL            sgl;
-       SCSIPortPage0_t SCSIPortPage0;
-       SCSIPortPage1_t SCSIPortPage1;
-       SCSIPortPage2_t SCSIPortPage2;
+    SCSIPortPage0_t SCSIPortPage0;
+    SCSIPortPage1_t SCSIPortPage1;
+    SCSIPortPage2_t SCSIPortPage2;
 #define NUM_SCSI_DEVICES       16
-       SCSIDevicePage1_t SCSIDevicePage1[NUM_SCSI_DEVICES];
-       struct mpt_tgt *tgt;
-       struct scst_cmd *scst_cmd[NUM_CMD_BUFFERS];
+    SCSIDevicePage1_t SCSIDevicePage1[NUM_SCSI_DEVICES];
+    struct mpt_tgt *tgt;
+    struct scst_cmd *scst_cmd[NUM_CMD_BUFFERS];
+    atomic_t pending_sense[NUM_SCSI_DEVICES];
+    u8 pending_sense_buffer[NUM_SCSI_DEVICES][SCSI_SENSE_BUFFERSIZE];
 } MPT_STM_PRIV;
 
 #define IO_STATE_POSTED                        0x1
@@ -314,7 +316,7 @@ typedef struct _MPT_STM_PRIV
 #define MPT_STATE_NEW              1    /* New command and SCST processes it */
 #define MPT_STATE_NEED_DATA        2    /* SCST needs data to process */
 #define MPT_STATE_DATA_IN          3    /* Data arrived and SCST processes it */
-#define MPT_STATE_DATA_OUT                4
+#define MPT_STATE_DATA_OUT         4
 #define MPT_STATE_PROCESSED        5    /* SCST done processing */
 
 /* Target's flags */