aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/wd719x.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2018-11-09 10:54:05 -0500
committerMartin K. Petersen <martin.petersen@oracle.com>2018-11-15 14:27:08 -0500
commitfde46e968258500fdf94ab9a450a958e4062174b (patch)
tree005272b8de47f88daba5d0be10812173c0268a1b /drivers/scsi/wd719x.c
parentd9c30dbca7994859d14eef51813454adaff1a9a0 (diff)
scsi: wd719x: use per-command private data
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>
Diffstat (limited to 'drivers/scsi/wd719x.c')
-rw-r--r--drivers/scsi/wd719x.c98
1 files changed, 42 insertions, 56 deletions
diff --git a/drivers/scsi/wd719x.c b/drivers/scsi/wd719x.c
index 7b05bbcfb186..b73e7f24a1c4 100644
--- a/drivers/scsi/wd719x.c
+++ b/drivers/scsi/wd719x.c
@@ -153,8 +153,6 @@ static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun,
153 153
154static void wd719x_destroy(struct wd719x *wd) 154static void wd719x_destroy(struct wd719x *wd)
155{ 155{
156 struct wd719x_scb *scb;
157
158 /* stop the RISC */ 156 /* stop the RISC */
159 if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0, 157 if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
160 WD719X_WAIT_FOR_RISC)) 158 WD719X_WAIT_FOR_RISC))
@@ -164,10 +162,6 @@ static void wd719x_destroy(struct wd719x *wd)
164 162
165 WARN_ON_ONCE(!list_empty(&wd->active_scbs)); 163 WARN_ON_ONCE(!list_empty(&wd->active_scbs));
166 164
167 /* free all SCBs */
168 list_for_each_entry(scb, &wd->free_scbs, list)
169 pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
170 scb->phys);
171 /* free internal buffers */ 165 /* free internal buffers */
172 pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys); 166 pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
173 wd->fw_virt = NULL; 167 wd->fw_virt = NULL;
@@ -180,18 +174,20 @@ static void wd719x_destroy(struct wd719x *wd)
180 free_irq(wd->pdev->irq, wd); 174 free_irq(wd->pdev->irq, wd);
181} 175}
182 176
183/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */ 177/* finish a SCSI command, unmap buffers */
184static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result) 178static void wd719x_finish_cmd(struct wd719x_scb *scb, int result)
185{ 179{
180 struct scsi_cmnd *cmd = scb->cmd;
186 struct wd719x *wd = shost_priv(cmd->device->host); 181 struct wd719x *wd = shost_priv(cmd->device->host);
187 struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
188 182
189 if (scb) { 183 list_del(&scb->list);
190 list_move(&scb->list, &wd->free_scbs); 184
191 dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, 185 dma_unmap_single(&wd->pdev->dev, scb->phys,
192 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 186 sizeof(struct wd719x_scb), DMA_BIDIRECTIONAL);
193 scsi_dma_unmap(cmd); 187 scsi_dma_unmap(cmd);
194 } 188 dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
189 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
190
195 cmd->result = result << 16; 191 cmd->result = result << 16;
196 cmd->scsi_done(cmd); 192 cmd->scsi_done(cmd);
197} 193}
@@ -201,36 +197,10 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
201{ 197{
202 int i, count_sg; 198 int i, count_sg;
203 unsigned long flags; 199 unsigned long flags;
204 struct wd719x_scb *scb; 200 struct wd719x_scb *scb = scsi_cmd_priv(cmd);
205 struct wd719x *wd = shost_priv(sh); 201 struct wd719x *wd = shost_priv(sh);
206 dma_addr_t phys;
207
208 cmd->host_scribble = NULL;
209
210 /* get a free SCB - either from existing ones or allocate a new one */
211 spin_lock_irqsave(wd->sh->host_lock, flags);
212 scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list);
213 if (scb) {
214 list_del(&scb->list);
215 phys = scb->phys;
216 } else {
217 spin_unlock_irqrestore(wd->sh->host_lock, flags);
218 scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb),
219 &phys);
220 spin_lock_irqsave(wd->sh->host_lock, flags);
221 if (!scb) {
222 dev_err(&wd->pdev->dev, "unable to allocate SCB\n");
223 wd719x_finish_cmd(cmd, DID_ERROR);
224 spin_unlock_irqrestore(wd->sh->host_lock, flags);
225 return 0;
226 }
227 }
228 memset(scb, 0, sizeof(struct wd719x_scb));
229 list_add(&scb->list, &wd->active_scbs);
230 202
231 scb->phys = phys;
232 scb->cmd = cmd; 203 scb->cmd = cmd;
233 cmd->host_scribble = (char *) scb;
234 204
235 scb->CDB_tag = 0; /* Tagged queueing not supported yet */ 205 scb->CDB_tag = 0; /* Tagged queueing not supported yet */
236 scb->devid = cmd->device->id; 206 scb->devid = cmd->device->id;
@@ -239,10 +209,19 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
239 /* copy the command */ 209 /* copy the command */
240 memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len); 210 memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
241 211
212 /* map SCB */
213 scb->phys = dma_map_single(&wd->pdev->dev, scb, sizeof(*scb),
214 DMA_BIDIRECTIONAL);
215
216 if (dma_mapping_error(&wd->pdev->dev, scb->phys))
217 goto out_error;
218
242 /* map sense buffer */ 219 /* map sense buffer */
243 scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE; 220 scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
244 cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer, 221 cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
245 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 222 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
223 if (dma_mapping_error(&wd->pdev->dev, cmd->SCp.dma_handle))
224 goto out_unmap_scb;
246 scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle); 225 scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
247 226
248 /* request autosense */ 227 /* request autosense */
@@ -257,11 +236,8 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
257 236
258 /* Scather/gather */ 237 /* Scather/gather */
259 count_sg = scsi_dma_map(cmd); 238 count_sg = scsi_dma_map(cmd);
260 if (count_sg < 0) { 239 if (count_sg < 0)
261 wd719x_finish_cmd(cmd, DID_ERROR); 240 goto out_unmap_sense;
262 spin_unlock_irqrestore(wd->sh->host_lock, flags);
263 return 0;
264 }
265 BUG_ON(count_sg > WD719X_SG); 241 BUG_ON(count_sg > WD719X_SG);
266 242
267 if (count_sg) { 243 if (count_sg) {
@@ -282,19 +258,33 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
282 scb->data_p = 0; 258 scb->data_p = 0;
283 } 259 }
284 260
261 spin_lock_irqsave(wd->sh->host_lock, flags);
262
285 /* check if the Command register is free */ 263 /* check if the Command register is free */
286 if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) { 264 if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
287 spin_unlock_irqrestore(wd->sh->host_lock, flags); 265 spin_unlock_irqrestore(wd->sh->host_lock, flags);
288 return SCSI_MLQUEUE_HOST_BUSY; 266 return SCSI_MLQUEUE_HOST_BUSY;
289 } 267 }
290 268
269 list_add(&scb->list, &wd->active_scbs);
270
291 /* write pointer to the AMR */ 271 /* write pointer to the AMR */
292 wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys); 272 wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
293 /* send SCB opcode */ 273 /* send SCB opcode */
294 wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB); 274 wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
295 275
296 spin_unlock_irqrestore(wd->sh->host_lock, flags); 276 spin_unlock_irqrestore(wd->sh->host_lock, flags);
277 return 0;
297 278
279out_unmap_sense:
280 dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
281 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
282out_unmap_scb:
283 dma_unmap_single(&wd->pdev->dev, scb->phys, sizeof(*scb),
284 DMA_BIDIRECTIONAL);
285out_error:
286 cmd->result = DID_ERROR << 16;
287 cmd->scsi_done(cmd);
298 return 0; 288 return 0;
299} 289}
300 290
@@ -463,7 +453,7 @@ static int wd719x_abort(struct scsi_cmnd *cmd)
463{ 453{
464 int action, result; 454 int action, result;
465 unsigned long flags; 455 unsigned long flags;
466 struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble; 456 struct wd719x_scb *scb = scsi_cmd_priv(cmd);
467 struct wd719x *wd = shost_priv(cmd->device->host); 457 struct wd719x *wd = shost_priv(cmd->device->host);
468 458
469 dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag); 459 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)
525 result = FAILED; 515 result = FAILED;
526 516
527 /* flush all SCBs */ 517 /* flush all SCBs */
528 list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) { 518 list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list)
529 struct scsi_cmnd *tmp_cmd = scb->cmd; 519 wd719x_finish_cmd(scb, result);
530 wd719x_finish_cmd(tmp_cmd, result);
531 }
532 spin_unlock_irqrestore(wd->sh->host_lock, flags); 520 spin_unlock_irqrestore(wd->sh->host_lock, flags);
533 521
534 return result; 522 return result;
@@ -554,7 +542,6 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
554 union wd719x_regs regs, 542 union wd719x_regs regs,
555 struct wd719x_scb *scb) 543 struct wd719x_scb *scb)
556{ 544{
557 struct scsi_cmnd *cmd;
558 int result; 545 int result;
559 546
560 /* now have to find result from card */ 547 /* now have to find result from card */
@@ -642,9 +629,8 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
642 result = DID_ERROR; 629 result = DID_ERROR;
643 break; 630 break;
644 } 631 }
645 cmd = scb->cmd;
646 632
647 wd719x_finish_cmd(cmd, result); 633 wd719x_finish_cmd(scb, result);
648} 634}
649 635
650static irqreturn_t wd719x_interrupt(int irq, void *dev_id) 636static irqreturn_t wd719x_interrupt(int irq, void *dev_id)
@@ -808,7 +794,6 @@ static int wd719x_board_found(struct Scsi_Host *sh)
808 int ret; 794 int ret;
809 795
810 INIT_LIST_HEAD(&wd->active_scbs); 796 INIT_LIST_HEAD(&wd->active_scbs);
811 INIT_LIST_HEAD(&wd->free_scbs);
812 797
813 sh->base = pci_resource_start(wd->pdev, 0); 798 sh->base = pci_resource_start(wd->pdev, 0);
814 799
@@ -873,6 +858,7 @@ fail_free_params:
873static struct scsi_host_template wd719x_template = { 858static struct scsi_host_template wd719x_template = {
874 .module = THIS_MODULE, 859 .module = THIS_MODULE,
875 .name = "Western Digital 719x", 860 .name = "Western Digital 719x",
861 .cmd_size = sizeof(struct wd719x_scb),
876 .queuecommand = wd719x_queuecommand, 862 .queuecommand = wd719x_queuecommand,
877 .eh_abort_handler = wd719x_abort, 863 .eh_abort_handler = wd719x_abort,
878 .eh_device_reset_handler = wd719x_dev_reset, 864 .eh_device_reset_handler = wd719x_dev_reset,