Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christoph Hellwig and committed by
Martin K. Petersen
fde46e96 d9c30dbc

+42 -57
+42 -56
drivers/scsi/wd719x.c
··· 153 153 154 154 static void wd719x_destroy(struct wd719x *wd) 155 155 { 156 - struct wd719x_scb *scb; 157 - 158 156 /* stop the RISC */ 159 157 if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0, 160 158 WD719X_WAIT_FOR_RISC)) ··· 162 164 163 165 WARN_ON_ONCE(!list_empty(&wd->active_scbs)); 164 166 165 - /* free all SCBs */ 166 - list_for_each_entry(scb, &wd->free_scbs, list) 167 - pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb, 168 - scb->phys); 169 167 /* free internal buffers */ 170 168 pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys); 171 169 wd->fw_virt = NULL; ··· 174 180 free_irq(wd->pdev->irq, wd); 175 181 } 176 182 177 - /* finish a SCSI command, mark SCB (if any) as free, unmap buffers */ 178 - static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result) 183 + /* finish a SCSI command, unmap buffers */ 184 + static void wd719x_finish_cmd(struct wd719x_scb *scb, int result) 179 185 { 186 + struct scsi_cmnd *cmd = scb->cmd; 180 187 struct wd719x *wd = shost_priv(cmd->device->host); 181 - struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble; 182 188 183 - if (scb) { 184 - list_move(&scb->list, &wd->free_scbs); 185 - dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, 186 - SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 187 - scsi_dma_unmap(cmd); 188 - } 189 + list_del(&scb->list); 190 + 191 + dma_unmap_single(&wd->pdev->dev, scb->phys, 192 + sizeof(struct wd719x_scb), DMA_BIDIRECTIONAL); 193 + scsi_dma_unmap(cmd); 194 + dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, 195 + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 196 + 189 197 cmd->result = result << 16; 190 198 cmd->scsi_done(cmd); 191 199 } ··· 197 201 { 198 202 int i, count_sg; 199 203 unsigned long flags; 200 - struct wd719x_scb *scb; 204 + struct wd719x_scb *scb = scsi_cmd_priv(cmd); 201 205 struct wd719x *wd = shost_priv(sh); 202 - dma_addr_t phys; 203 206 204 - cmd->host_scribble = NULL; 205 - 206 - /* get a free SCB - either from existing ones or allocate a new one */ 207 - spin_lock_irqsave(wd->sh->host_lock, flags); 208 - scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list); 209 - if (scb) { 210 - list_del(&scb->list); 211 - phys = scb->phys; 212 - } else { 213 - spin_unlock_irqrestore(wd->sh->host_lock, flags); 214 - scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb), 215 - &phys); 216 - spin_lock_irqsave(wd->sh->host_lock, flags); 217 - if (!scb) { 218 - dev_err(&wd->pdev->dev, "unable to allocate SCB\n"); 219 - wd719x_finish_cmd(cmd, DID_ERROR); 220 - spin_unlock_irqrestore(wd->sh->host_lock, flags); 221 - return 0; 222 - } 223 - } 224 - memset(scb, 0, sizeof(struct wd719x_scb)); 225 - list_add(&scb->list, &wd->active_scbs); 226 - 227 - scb->phys = phys; 228 207 scb->cmd = cmd; 229 - cmd->host_scribble = (char *) scb; 230 208 231 209 scb->CDB_tag = 0; /* Tagged queueing not supported yet */ 232 210 scb->devid = cmd->device->id; ··· 209 239 /* copy the command */ 210 240 memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len); 211 241 242 + /* map SCB */ 243 + scb->phys = dma_map_single(&wd->pdev->dev, scb, sizeof(*scb), 244 + DMA_BIDIRECTIONAL); 245 + 246 + if (dma_mapping_error(&wd->pdev->dev, scb->phys)) 247 + goto out_error; 248 + 212 249 /* map sense buffer */ 213 250 scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE; 214 251 cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer, 215 252 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 253 + if (dma_mapping_error(&wd->pdev->dev, cmd->SCp.dma_handle)) 254 + goto out_unmap_scb; 216 255 scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle); 217 256 218 257 /* request autosense */ ··· 236 257 237 258 /* Scather/gather */ 238 259 count_sg = scsi_dma_map(cmd); 239 - if (count_sg < 0) { 240 - wd719x_finish_cmd(cmd, DID_ERROR); 241 - spin_unlock_irqrestore(wd->sh->host_lock, flags); 242 - return 0; 243 - } 260 + if (count_sg < 0) 261 + goto out_unmap_sense; 244 262 BUG_ON(count_sg > WD719X_SG); 245 263 246 264 if (count_sg) { ··· 258 282 scb->data_p = 0; 259 283 } 260 284 285 + spin_lock_irqsave(wd->sh->host_lock, flags); 286 + 261 287 /* check if the Command register is free */ 262 288 if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) { 263 289 spin_unlock_irqrestore(wd->sh->host_lock, flags); 264 290 return SCSI_MLQUEUE_HOST_BUSY; 265 291 } 292 + 293 + list_add(&scb->list, &wd->active_scbs); 266 294 267 295 /* write pointer to the AMR */ 268 296 wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys); ··· 274 294 wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB); 275 295 276 296 spin_unlock_irqrestore(wd->sh->host_lock, flags); 297 + return 0; 277 298 299 + out_unmap_sense: 300 + dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle, 301 + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); 302 + out_unmap_scb: 303 + dma_unmap_single(&wd->pdev->dev, scb->phys, sizeof(*scb), 304 + DMA_BIDIRECTIONAL); 305 + out_error: 306 + cmd->result = DID_ERROR << 16; 307 + cmd->scsi_done(cmd); 278 308 return 0; 279 309 } 280 310 ··· 453 463 { 454 464 int action, result; 455 465 unsigned long flags; 456 - struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble; 466 + struct wd719x_scb *scb = scsi_cmd_priv(cmd); 457 467 struct wd719x *wd = shost_priv(cmd->device->host); 458 468 459 469 dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag); ··· 515 525 result = FAILED; 516 526 517 527 /* flush all SCBs */ 518 - list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) { 519 - struct scsi_cmnd *tmp_cmd = scb->cmd; 520 - wd719x_finish_cmd(tmp_cmd, result); 521 - } 528 + list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) 529 + wd719x_finish_cmd(scb, result); 522 530 spin_unlock_irqrestore(wd->sh->host_lock, flags); 523 531 524 532 return result; ··· 542 554 union wd719x_regs regs, 543 555 struct wd719x_scb *scb) 544 556 { 545 - struct scsi_cmnd *cmd; 546 557 int result; 547 558 548 559 /* now have to find result from card */ ··· 629 642 result = DID_ERROR; 630 643 break; 631 644 } 632 - cmd = scb->cmd; 633 645 634 - wd719x_finish_cmd(cmd, result); 646 + wd719x_finish_cmd(scb, result); 635 647 } 636 648 637 649 static irqreturn_t wd719x_interrupt(int irq, void *dev_id) ··· 794 808 int ret; 795 809 796 810 INIT_LIST_HEAD(&wd->active_scbs); 797 - INIT_LIST_HEAD(&wd->free_scbs); 798 811 799 812 sh->base = pci_resource_start(wd->pdev, 0); 800 813 ··· 858 873 static struct scsi_host_template wd719x_template = { 859 874 .module = THIS_MODULE, 860 875 .name = "Western Digital 719x", 876 + .cmd_size = sizeof(struct wd719x_scb), 861 877 .queuecommand = wd719x_queuecommand, 862 878 .eh_abort_handler = wd719x_abort, 863 879 .eh_device_reset_handler = wd719x_dev_reset,
-1
drivers/scsi/wd719x.h
··· 74 74 void *hash_virt; /* hash table CPU address */ 75 75 dma_addr_t hash_phys; /* hash table bus address */ 76 76 struct list_head active_scbs; 77 - struct list_head free_scbs; 78 77 }; 79 78 80 79 /* timeout delays in microsecs */