scsi: aacraid: Add task management functionality
authorRaghava Aditya Renukunta <RaghavaAditya.Renukunta@microsemi.com>
Thu, 2 Feb 2017 23:53:30 +0000 (15:53 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 3 Feb 2017 15:35:03 +0000 (10:35 -0500)
Added support to send out task management commands.

[mkp: removed // fibsize... ]

Signed-off-by: Raghava Aditya Renukunta <RaghavaAditya.Renukunta@microsemi.com>
Signed-off-by: Dave Carroll <David.Carroll@microsemi.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/aacraid/aachba.c

index 5719ac39c45af941b6fcbc06bcc93762375ac60e..9e9144860fa50416bfdb9690408926888b7835fd 100644 (file)
@@ -217,9 +217,13 @@ static long aac_build_sg64(struct scsi_cmnd *scsicmd, struct sgmap64 *psg);
 static long aac_build_sgraw(struct scsi_cmnd *scsicmd, struct sgmapraw *psg);
 static long aac_build_sgraw2(struct scsi_cmnd *scsicmd,
                                struct aac_raw_io2 *rio2, int sg_max);
+static long aac_build_sghba(struct scsi_cmnd *scsicmd,
+                               struct aac_hba_cmd_req *hbacmd,
+                               int sg_max, u64 sg_address);
 static int aac_convert_sgraw2(struct aac_raw_io2 *rio2,
                                int pages, int nseg, int nseg_new);
 static int aac_send_srb_fib(struct scsi_cmnd* scsicmd);
+static int aac_send_hba_fib(struct scsi_cmnd *scsicmd);
 #ifdef AAC_DETAILED_STATUS_INFO
 static char *aac_get_status_string(u32 status);
 #endif
@@ -1446,6 +1450,52 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd
        return srbcmd;
 }
 
+static struct aac_hba_cmd_req *aac_construct_hbacmd(struct fib *fib,
+                                                       struct scsi_cmnd *cmd)
+{
+       struct aac_hba_cmd_req *hbacmd;
+       struct aac_dev *dev;
+       int bus, target;
+       u64 address;
+
+       dev = (struct aac_dev *)cmd->device->host->hostdata;
+
+       hbacmd = (struct aac_hba_cmd_req *)fib->hw_fib_va;
+       memset(hbacmd, 0, 96);  /* sizeof(*hbacmd) is not necessary */
+       /* iu_type is a parameter of aac_hba_send */
+       switch (cmd->sc_data_direction) {
+       case DMA_TO_DEVICE:
+               hbacmd->byte1 = 2;
+               break;
+       case DMA_FROM_DEVICE:
+       case DMA_BIDIRECTIONAL:
+               hbacmd->byte1 = 1;
+               break;
+       case DMA_NONE:
+       default:
+               break;
+       }
+       hbacmd->lun[1] = cpu_to_le32(cmd->device->lun);
+
+       bus = aac_logical_to_phys(scmd_channel(cmd));
+       target = scmd_id(cmd);
+       hbacmd->it_nexus = dev->hba_map[bus][target].rmw_nexus;
+
+       /* we fill in reply_qid later in aac_src_deliver_message */
+       /* we fill in iu_type, request_id later in aac_hba_send */
+       /* we fill in emb_data_desc_count later in aac_build_sghba */
+
+       memcpy(hbacmd->cdb, cmd->cmnd, cmd->cmd_len);
+       hbacmd->data_length = cpu_to_le32(scsi_bufflen(cmd));
+
+       address = (u64)fib->hw_error_pa;
+       hbacmd->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
+       hbacmd->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
+       hbacmd->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+
+       return hbacmd;
+}
+
 static void aac_srb_callback(void *context, struct fib * fibptr);
 
 static int aac_scsi_64(struct fib * fib, struct scsi_cmnd * cmd)
@@ -1516,6 +1566,30 @@ static int aac_scsi_32_64(struct fib * fib, struct scsi_cmnd * cmd)
        return aac_scsi_32(fib, cmd);
 }
 
+static int aac_adapter_hba(struct fib *fib, struct scsi_cmnd *cmd)
+{
+       struct aac_hba_cmd_req *hbacmd = aac_construct_hbacmd(fib, cmd);
+       struct aac_dev *dev;
+       long ret;
+
+       dev = (struct aac_dev *)cmd->device->host->hostdata;
+
+       ret = aac_build_sghba(cmd, hbacmd,
+               dev->scsi_host_ptr->sg_tablesize, (u64)fib->hw_sgl_pa);
+       if (ret < 0)
+               return ret;
+
+       /*
+        *      Now send the HBA command to the adapter
+        */
+       fib->hbacmd_size = 64 + le32_to_cpu(hbacmd->emb_data_desc_count) *
+               sizeof(struct aac_hba_sgl);
+
+       return aac_hba_send(HBA_IU_TYPE_SCSI_CMD_REQ, fib,
+                                 (fib_callback) aac_hba_callback,
+                                 (void *) cmd);
+}
+
 int aac_issue_bmic_identify(struct aac_dev *dev, u32 bus, u32 target)
 {
        struct fib *fibptr;
@@ -1527,6 +1601,7 @@ int aac_issue_bmic_identify(struct aac_dev *dev, u32 bus, u32 target)
        u16 fibsize, datasize;
        int rcode = -ENOMEM;
 
+
        fibptr = aac_fib_alloc(dev);
        if (!fibptr)
                goto out;
@@ -1997,6 +2072,11 @@ int aac_get_adapter_info(struct aac_dev* dev)
                          (dev->scsi_host_ptr->sg_tablesize * 8) + 112;
                }
        }
+       if (!dev->sync_mode && dev->sa_firmware &&
+               dev->scsi_host_ptr->sg_tablesize > HBA_MAX_SG_SEPARATE)
+               dev->scsi_host_ptr->sg_tablesize = dev->sg_tablesize =
+                       HBA_MAX_SG_SEPARATE;
+
        /* FIB should be freed only after getting the response from the F/W */
        if (rcode != -ERESTARTSYS) {
                aac_fib_complete(fibptr);
@@ -2553,7 +2633,7 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
 
 int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
 {
-       u32 cid;
+       u32 cid, bus;
        struct Scsi_Host *host = scsicmd->device->host;
        struct aac_dev *dev = (struct aac_dev *)host->hostdata;
        struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev;
@@ -2599,8 +2679,24 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                                }
                        }
                } else {  /* check for physical non-dasd devices */
-                       if (dev->nondasd_support || expose_physicals ||
-                                       dev->jbod) {
+                       bus = aac_logical_to_phys(scmd_channel(scsicmd));
+                       if (bus < AAC_MAX_BUSES && cid < AAC_MAX_TARGETS &&
+                               (dev->hba_map[bus][cid].expose
+                                               == AAC_HIDE_DISK)){
+                               if (scsicmd->cmnd[0] == INQUIRY) {
+                                       scsicmd->result = DID_NO_CONNECT << 16;
+                                       goto scsi_done_ret;
+                               }
+                       }
+
+                       if (bus < AAC_MAX_BUSES && cid < AAC_MAX_TARGETS &&
+                               dev->hba_map[bus][cid].devtype
+                                       == AAC_DEVTYPE_NATIVE_RAW) {
+                               if (dev->in_reset)
+                                       return -1;
+                               return aac_send_hba_fib(scsicmd);
+                       } else if (dev->nondasd_support || expose_physicals ||
+                               dev->jbod) {
                                if (dev->in_reset)
                                        return -1;
                                return aac_send_srb_fib(scsicmd);
@@ -3357,9 +3453,152 @@ static void aac_srb_callback(void *context, struct fib * fibptr)
        scsicmd->scsi_done(scsicmd);
 }
 
+static void hba_resp_task_complete(struct aac_dev *dev,
+                                       struct scsi_cmnd *scsicmd,
+                                       struct aac_hba_resp *err) {
+
+       scsicmd->result = err->status;
+       /* set residual count */
+       scsi_set_resid(scsicmd, le32_to_cpu(err->residual_count));
+
+       switch (err->status) {
+       case SAM_STAT_GOOD:
+               scsicmd->result |= DID_OK << 16 | COMMAND_COMPLETE << 8;
+               break;
+       case SAM_STAT_CHECK_CONDITION:
+       {
+               int len;
+
+               len = min_t(u8, err->sense_response_data_len,
+                       SCSI_SENSE_BUFFERSIZE);
+               if (len)
+                       memcpy(scsicmd->sense_buffer,
+                               err->sense_response_buf, len);
+               scsicmd->result |= DID_OK << 16 | COMMAND_COMPLETE << 8;
+               break;
+       }
+       case SAM_STAT_BUSY:
+               scsicmd->result |= DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8;
+               break;
+       case SAM_STAT_TASK_ABORTED:
+               scsicmd->result |= DID_ABORT << 16 | ABORT << 8;
+               break;
+       case SAM_STAT_RESERVATION_CONFLICT:
+       case SAM_STAT_TASK_SET_FULL:
+       default:
+               scsicmd->result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+               break;
+       }
+}
+
+static void hba_resp_task_failure(struct aac_dev *dev,
+                                       struct scsi_cmnd *scsicmd,
+                                       struct aac_hba_resp *err)
+{
+       switch (err->status) {
+       case HBA_RESP_STAT_HBAMODE_DISABLED:
+       {
+               u32 bus, cid;
+
+               bus = aac_logical_to_phys(scmd_channel(scsicmd));
+               cid = scmd_id(scsicmd);
+               if (dev->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
+                       dev->hba_map[bus][cid].devtype = AAC_DEVTYPE_ARC_RAW;
+                       dev->hba_map[bus][cid].rmw_nexus = 0xffffffff;
+               }
+               scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+               break;
+       }
+       case HBA_RESP_STAT_IO_ERROR:
+       case HBA_RESP_STAT_NO_PATH_TO_DEVICE:
+               scsicmd->result = DID_OK << 16 |
+                       COMMAND_COMPLETE << 8 | SAM_STAT_BUSY;
+               break;
+       case HBA_RESP_STAT_IO_ABORTED:
+               scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+               break;
+       case HBA_RESP_STAT_INVALID_DEVICE:
+               scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+               break;
+       case HBA_RESP_STAT_UNDERRUN:
+               /* UNDERRUN is OK */
+               scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+               break;
+       case HBA_RESP_STAT_OVERRUN:
+       default:
+               scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+               break;
+       }
+}
+
+/**
+ *
+ * aac_hba_callback
+ * @context: the context set in the fib - here it is scsi cmd
+ * @fibptr: pointer to the fib
+ *
+ * Handles the completion of a native HBA scsi command
+ *
+ */
+void aac_hba_callback(void *context, struct fib *fibptr)
+{
+       struct aac_dev *dev;
+       struct scsi_cmnd *scsicmd;
+
+       struct aac_hba_resp *err =
+                       &((struct aac_native_hba *)fibptr->hw_fib_va)->resp.err;
+
+       scsicmd = (struct scsi_cmnd *) context;
+
+       if (!aac_valid_context(scsicmd, fibptr))
+               return;
+
+       WARN_ON(fibptr == NULL);
+       dev = fibptr->dev;
+
+       if (!(fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF))
+               scsi_dma_unmap(scsicmd);
+
+       if (fibptr->flags & FIB_CONTEXT_FLAG_FASTRESP) {
+               /* fast response */
+               scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+               goto out;
+       }
+
+       switch (err->service_response) {
+       case HBA_RESP_SVCRES_TASK_COMPLETE:
+               hba_resp_task_complete(dev, scsicmd, err);
+               break;
+       case HBA_RESP_SVCRES_FAILURE:
+               hba_resp_task_failure(dev, scsicmd, err);
+               break;
+       case HBA_RESP_SVCRES_TMF_REJECTED:
+               scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
+               break;
+       case HBA_RESP_SVCRES_TMF_LUN_INVALID:
+               scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+               break;
+       case HBA_RESP_SVCRES_TMF_COMPLETE:
+       case HBA_RESP_SVCRES_TMF_SUCCEEDED:
+               scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+               break;
+       default:
+               scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+               break;
+       }
+
+out:
+       aac_fib_complete(fibptr);
+
+       if (fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF)
+               scsicmd->SCp.sent_command = 1;
+       else
+               scsicmd->scsi_done(scsicmd);
+}
+
 /**
  *
- * aac_send_scb_fib
+ * aac_send_srb_fib
  * @scsicmd: the scsi command block
  *
  * This routine will form a FIB and fill in the aac_srb from the
@@ -3402,6 +3641,54 @@ static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
        return -1;
 }
 
+/**
+ *
+ * aac_send_hba_fib
+ * @scsicmd: the scsi command block
+ *
+ * This routine will form a FIB and fill in the aac_hba_cmd_req from the
+ * scsicmd passed in.
+ */
+static int aac_send_hba_fib(struct scsi_cmnd *scsicmd)
+{
+       struct fib *cmd_fibcontext;
+       struct aac_dev *dev;
+       int status;
+
+       dev = shost_priv(scsicmd->device->host);
+       if (scmd_id(scsicmd) >= dev->maximum_num_physicals ||
+                       scsicmd->device->lun > AAC_MAX_LUN - 1) {
+               scsicmd->result = DID_NO_CONNECT << 16;
+               scsicmd->scsi_done(scsicmd);
+               return 0;
+       }
+
+       /*
+        *      Allocate and initialize a Fib then setup a BlockWrite command
+        */
+       cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
+       if (!cmd_fibcontext)
+               return -1;
+
+       status = aac_adapter_hba(cmd_fibcontext, scsicmd);
+
+       /*
+        *      Check that the command queued to the controller
+        */
+       if (status == -EINPROGRESS) {
+               scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+               return 0;
+       }
+
+       pr_warn("aac_hba_cmd_req: aac_fib_send failed with status: %d\n",
+               status);
+       aac_fib_complete(cmd_fibcontext);
+       aac_fib_free(cmd_fibcontext);
+
+       return -1;
+}
+
+
 static long aac_build_sg(struct scsi_cmnd *scsicmd, struct sgmap *psg)
 {
        struct aac_dev *dev;
@@ -3654,6 +3941,77 @@ static int aac_convert_sgraw2(struct aac_raw_io2 *rio2, int pages, int nseg, int
        return 0;
 }
 
+static long aac_build_sghba(struct scsi_cmnd *scsicmd,
+                       struct aac_hba_cmd_req *hbacmd,
+                       int sg_max,
+                       u64 sg_address)
+{
+       unsigned long byte_count = 0;
+       int nseg;
+       struct scatterlist *sg;
+       int i;
+       u32 cur_size;
+       struct aac_hba_sgl *sge;
+
+
+
+       nseg = scsi_dma_map(scsicmd);
+       if (nseg <= 0) {
+               byte_count = nseg;
+               goto out;
+       }
+
+       if (nseg > HBA_MAX_SG_EMBEDDED)
+               sge = &hbacmd->sge[2];
+       else
+               sge = &hbacmd->sge[0];
+
+       scsi_for_each_sg(scsicmd, sg, nseg, i) {
+               int count = sg_dma_len(sg);
+               u64 addr = sg_dma_address(sg);
+
+               WARN_ON(i >= sg_max);
+               sge->addr_hi = cpu_to_le32((u32)(addr>>32));
+               sge->addr_lo = cpu_to_le32((u32)(addr & 0xffffffff));
+               cur_size = cpu_to_le32(count);
+               sge->len = cur_size;
+               sge->flags = 0;
+               byte_count += count;
+               sge++;
+       }
+
+       sge--;
+       /* hba wants the size to be exact */
+       if (byte_count > scsi_bufflen(scsicmd)) {
+               u32 temp;
+
+               temp = le32_to_cpu(sge->len) - byte_count
+                                               - scsi_bufflen(scsicmd);
+               sge->len = cpu_to_le32(temp);
+               byte_count = scsi_bufflen(scsicmd);
+       }
+
+       if (nseg <= HBA_MAX_SG_EMBEDDED) {
+               hbacmd->emb_data_desc_count = cpu_to_le32(nseg);
+               sge->flags = cpu_to_le32(0x40000000);
+       } else {
+               /* not embedded */
+               hbacmd->sge[0].flags = cpu_to_le32(0x80000000);
+               hbacmd->emb_data_desc_count = (u8)cpu_to_le32(1);
+               hbacmd->sge[0].addr_hi = (u32)cpu_to_le32(sg_address >> 32);
+               hbacmd->sge[0].addr_lo =
+                       cpu_to_le32((u32)(sg_address & 0xffffffff));
+       }
+
+       /* Check for command underflow */
+       if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+               pr_warn("aacraid: cmd len %08lX cmd underflow %08X\n",
+                               byte_count, scsicmd->underflow);
+       }
+out:
+       return byte_count;
+}
+
 #ifdef AAC_DETAILED_STATUS_INFO
 
 struct aac_srb_status_info {