scsi: wd719x: use per-command private data
authorChristoph Hellwig <hch@lst.de>
Fri, 9 Nov 2018 15:54:05 +0000 (16:54 +0100)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 15 Nov 2018 19:27:08 +0000 (14:27 -0500)
Add the SCB onto the scsi command allocation and use dma streaming mappings
for it only when in use.  This avoid possibly calling dma_alloc_coherent
under a lock or even in irq context, while also making the code simpler.

Thanks to Ondrej Zary for testing and various bug fixes.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/wd719x.c
drivers/scsi/wd719x.h

index 7b05bbcfb18618543d466c0ae8fc2036826a300c..b73e7f24a1c49b482be0d3a9cf33736c885ef1f6 100644 (file)
@@ -153,8 +153,6 @@ static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun,
 
 static void wd719x_destroy(struct wd719x *wd)
 {
-       struct wd719x_scb *scb;
-
        /* stop the RISC */
        if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
                              WD719X_WAIT_FOR_RISC))
@@ -164,10 +162,6 @@ static void wd719x_destroy(struct wd719x *wd)
 
        WARN_ON_ONCE(!list_empty(&wd->active_scbs));
 
-       /* free all SCBs */
-       list_for_each_entry(scb, &wd->free_scbs, list)
-               pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
-                                   scb->phys);
        /* free internal buffers */
        pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
        wd->fw_virt = NULL;
@@ -180,18 +174,20 @@ static void wd719x_destroy(struct wd719x *wd)
        free_irq(wd->pdev->irq, wd);
 }
 
-/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */
-static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result)
+/* finish a SCSI command, unmap buffers */
+static void wd719x_finish_cmd(struct wd719x_scb *scb, int result)
 {
+       struct scsi_cmnd *cmd = scb->cmd;
        struct wd719x *wd = shost_priv(cmd->device->host);
-       struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
 
-       if (scb) {
-               list_move(&scb->list, &wd->free_scbs);
-               dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
-                                SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
-               scsi_dma_unmap(cmd);
-       }
+       list_del(&scb->list);
+
+       dma_unmap_single(&wd->pdev->dev, scb->phys,
+                       sizeof(struct wd719x_scb), DMA_BIDIRECTIONAL);
+       scsi_dma_unmap(cmd);
+       dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
+                        SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+
        cmd->result = result << 16;
        cmd->scsi_done(cmd);
 }
@@ -201,36 +197,10 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
 {
        int i, count_sg;
        unsigned long flags;
-       struct wd719x_scb *scb;
+       struct wd719x_scb *scb = scsi_cmd_priv(cmd);
        struct wd719x *wd = shost_priv(sh);
-       dma_addr_t phys;
-
-       cmd->host_scribble = NULL;
-
-       /* get a free SCB - either from existing ones or allocate a new one */
-       spin_lock_irqsave(wd->sh->host_lock, flags);
-       scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list);
-       if (scb) {
-               list_del(&scb->list);
-               phys = scb->phys;
-       } else {
-               spin_unlock_irqrestore(wd->sh->host_lock, flags);
-               scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb),
-                                          &phys);
-               spin_lock_irqsave(wd->sh->host_lock, flags);
-               if (!scb) {
-                       dev_err(&wd->pdev->dev, "unable to allocate SCB\n");
-                       wd719x_finish_cmd(cmd, DID_ERROR);
-                       spin_unlock_irqrestore(wd->sh->host_lock, flags);
-                       return 0;
-               }
-       }
-       memset(scb, 0, sizeof(struct wd719x_scb));
-       list_add(&scb->list, &wd->active_scbs);
 
-       scb->phys = phys;
        scb->cmd = cmd;
-       cmd->host_scribble = (char *) scb;
 
        scb->CDB_tag = 0;       /* Tagged queueing not supported yet */
        scb->devid = cmd->device->id;
@@ -239,10 +209,19 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
        /* copy the command */
        memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
 
+       /* map SCB */
+       scb->phys = dma_map_single(&wd->pdev->dev, scb, sizeof(*scb),
+                                  DMA_BIDIRECTIONAL);
+
+       if (dma_mapping_error(&wd->pdev->dev, scb->phys))
+               goto out_error;
+
        /* map sense buffer */
        scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
        cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
                        SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&wd->pdev->dev, cmd->SCp.dma_handle))
+               goto out_unmap_scb;
        scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
 
        /* request autosense */
@@ -257,11 +236,8 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
 
        /* Scather/gather */
        count_sg = scsi_dma_map(cmd);
-       if (count_sg < 0) {
-               wd719x_finish_cmd(cmd, DID_ERROR);
-               spin_unlock_irqrestore(wd->sh->host_lock, flags);
-               return 0;
-       }
+       if (count_sg < 0)
+               goto out_unmap_sense;
        BUG_ON(count_sg > WD719X_SG);
 
        if (count_sg) {
@@ -282,19 +258,33 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
                scb->data_p = 0;
        }
 
+       spin_lock_irqsave(wd->sh->host_lock, flags);
+
        /* check if the Command register is free */
        if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
                spin_unlock_irqrestore(wd->sh->host_lock, flags);
                return SCSI_MLQUEUE_HOST_BUSY;
        }
 
+       list_add(&scb->list, &wd->active_scbs);
+
        /* write pointer to the AMR */
        wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
        /* send SCB opcode */
        wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
 
        spin_unlock_irqrestore(wd->sh->host_lock, flags);
+       return 0;
 
+out_unmap_sense:
+       dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
+                        SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+out_unmap_scb:
+       dma_unmap_single(&wd->pdev->dev, scb->phys, sizeof(*scb),
+                        DMA_BIDIRECTIONAL);
+out_error:
+       cmd->result = DID_ERROR << 16;
+       cmd->scsi_done(cmd);
        return 0;
 }
 
@@ -463,7 +453,7 @@ static int wd719x_abort(struct scsi_cmnd *cmd)
 {
        int action, result;
        unsigned long flags;
-       struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble;
+       struct wd719x_scb *scb = scsi_cmd_priv(cmd);
        struct wd719x *wd = shost_priv(cmd->device->host);
 
        dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag);
@@ -525,10 +515,8 @@ static int wd719x_host_reset(struct scsi_cmnd *cmd)
                result = FAILED;
 
        /* flush all SCBs */
-       list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) {
-               struct scsi_cmnd *tmp_cmd = scb->cmd;
-               wd719x_finish_cmd(tmp_cmd, result);
-       }
+       list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list)
+               wd719x_finish_cmd(scb, result);
        spin_unlock_irqrestore(wd->sh->host_lock, flags);
 
        return result;
@@ -554,7 +542,6 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
                                        union wd719x_regs regs,
                                        struct wd719x_scb *scb)
 {
-       struct scsi_cmnd *cmd;
        int result;
 
        /* now have to find result from card */
@@ -642,9 +629,8 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
                result = DID_ERROR;
                break;
        }
-       cmd = scb->cmd;
 
-       wd719x_finish_cmd(cmd, result);
+       wd719x_finish_cmd(scb, result);
 }
 
 static irqreturn_t wd719x_interrupt(int irq, void *dev_id)
@@ -808,7 +794,6 @@ static int wd719x_board_found(struct Scsi_Host *sh)
        int ret;
 
        INIT_LIST_HEAD(&wd->active_scbs);
-       INIT_LIST_HEAD(&wd->free_scbs);
 
        sh->base = pci_resource_start(wd->pdev, 0);
 
@@ -873,6 +858,7 @@ fail_free_params:
 static struct scsi_host_template wd719x_template = {
        .module                         = THIS_MODULE,
        .name                           = "Western Digital 719x",
+       .cmd_size                       = sizeof(struct wd719x_scb),
        .queuecommand                   = wd719x_queuecommand,
        .eh_abort_handler               = wd719x_abort,
        .eh_device_reset_handler        = wd719x_dev_reset,
index 0455b1633ca72b5d05d260c97a209b23266f87f0..abaabd419a548643dc87baf4d54bf0156a8bae48 100644 (file)
@@ -74,7 +74,6 @@ struct wd719x {
        void *hash_virt;        /* hash table CPU address */
        dma_addr_t hash_phys;   /* hash table bus address */
        struct list_head active_scbs;
-       struct list_head free_scbs;
 };
 
 /* timeout delays in microsecs */