diff options
author | Christoph Hellwig <hch@lst.de> | 2018-11-09 10:54:05 -0500 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-11-15 14:27:08 -0500 |
commit | fde46e968258500fdf94ab9a450a958e4062174b (patch) | |
tree | 005272b8de47f88daba5d0be10812173c0268a1b /drivers/scsi/wd719x.c | |
parent | d9c30dbca7994859d14eef51813454adaff1a9a0 (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.c | 98 |
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 | ||
154 | static void wd719x_destroy(struct wd719x *wd) | 154 | static 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 */ |
184 | static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result) | 178 | static 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 | ||
279 | out_unmap_sense: | ||
280 | dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, | ||
281 | SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); | ||
282 | out_unmap_scb: | ||
283 | dma_unmap_single(&wd->pdev->dev, scb->phys, sizeof(*scb), | ||
284 | DMA_BIDIRECTIONAL); | ||
285 | out_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 | ||
650 | static irqreturn_t wd719x_interrupt(int irq, void *dev_id) | 636 | static 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: | |||
873 | static struct scsi_host_template wd719x_template = { | 858 | static 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, |