mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-04 04:06:26 +00:00
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>
This commit is contained in:
parent
d9c30dbca7
commit
fde46e9682
@ -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)
|
static void wd719x_destroy(struct wd719x *wd)
|
||||||
{
|
{
|
||||||
struct wd719x_scb *scb;
|
|
||||||
|
|
||||||
/* stop the RISC */
|
/* stop the RISC */
|
||||||
if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
|
if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
|
||||||
WD719X_WAIT_FOR_RISC))
|
WD719X_WAIT_FOR_RISC))
|
||||||
@ -164,10 +162,6 @@ static void wd719x_destroy(struct wd719x *wd)
|
|||||||
|
|
||||||
WARN_ON_ONCE(!list_empty(&wd->active_scbs));
|
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 */
|
/* free internal buffers */
|
||||||
pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
|
pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
|
||||||
wd->fw_virt = NULL;
|
wd->fw_virt = NULL;
|
||||||
@ -180,18 +174,20 @@ static void wd719x_destroy(struct wd719x *wd)
|
|||||||
free_irq(wd->pdev->irq, wd);
|
free_irq(wd->pdev->irq, wd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */
|
/* finish a SCSI command, unmap buffers */
|
||||||
static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result)
|
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 *wd = shost_priv(cmd->device->host);
|
||||||
struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
|
|
||||||
|
|
||||||
if (scb) {
|
list_del(&scb->list);
|
||||||
list_move(&scb->list, &wd->free_scbs);
|
|
||||||
dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
|
dma_unmap_single(&wd->pdev->dev, scb->phys,
|
||||||
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
|
sizeof(struct wd719x_scb), DMA_BIDIRECTIONAL);
|
||||||
scsi_dma_unmap(cmd);
|
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->result = result << 16;
|
||||||
cmd->scsi_done(cmd);
|
cmd->scsi_done(cmd);
|
||||||
}
|
}
|
||||||
@ -201,36 +197,10 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
|
|||||||
{
|
{
|
||||||
int i, count_sg;
|
int i, count_sg;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct wd719x_scb *scb;
|
struct wd719x_scb *scb = scsi_cmd_priv(cmd);
|
||||||
struct wd719x *wd = shost_priv(sh);
|
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;
|
scb->cmd = cmd;
|
||||||
cmd->host_scribble = (char *) scb;
|
|
||||||
|
|
||||||
scb->CDB_tag = 0; /* Tagged queueing not supported yet */
|
scb->CDB_tag = 0; /* Tagged queueing not supported yet */
|
||||||
scb->devid = cmd->device->id;
|
scb->devid = cmd->device->id;
|
||||||
@ -239,10 +209,19 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
|
|||||||
/* copy the command */
|
/* copy the command */
|
||||||
memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
|
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 */
|
/* map sense buffer */
|
||||||
scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
|
scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
|
||||||
cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
|
cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
|
||||||
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
|
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);
|
scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
|
||||||
|
|
||||||
/* request autosense */
|
/* request autosense */
|
||||||
@ -257,11 +236,8 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
|
|||||||
|
|
||||||
/* Scather/gather */
|
/* Scather/gather */
|
||||||
count_sg = scsi_dma_map(cmd);
|
count_sg = scsi_dma_map(cmd);
|
||||||
if (count_sg < 0) {
|
if (count_sg < 0)
|
||||||
wd719x_finish_cmd(cmd, DID_ERROR);
|
goto out_unmap_sense;
|
||||||
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BUG_ON(count_sg > WD719X_SG);
|
BUG_ON(count_sg > WD719X_SG);
|
||||||
|
|
||||||
if (count_sg) {
|
if (count_sg) {
|
||||||
@ -282,19 +258,33 @@ static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
|
|||||||
scb->data_p = 0;
|
scb->data_p = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(wd->sh->host_lock, flags);
|
||||||
|
|
||||||
/* check if the Command register is free */
|
/* check if the Command register is free */
|
||||||
if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
|
if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
|
||||||
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
||||||
return SCSI_MLQUEUE_HOST_BUSY;
|
return SCSI_MLQUEUE_HOST_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add(&scb->list, &wd->active_scbs);
|
||||||
|
|
||||||
/* write pointer to the AMR */
|
/* write pointer to the AMR */
|
||||||
wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
|
wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
|
||||||
/* send SCB opcode */
|
/* send SCB opcode */
|
||||||
wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
|
wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
|
||||||
|
|
||||||
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +453,7 @@ static int wd719x_abort(struct scsi_cmnd *cmd)
|
|||||||
{
|
{
|
||||||
int action, result;
|
int action, result;
|
||||||
unsigned long flags;
|
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);
|
struct wd719x *wd = shost_priv(cmd->device->host);
|
||||||
|
|
||||||
dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag);
|
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;
|
result = FAILED;
|
||||||
|
|
||||||
/* flush all SCBs */
|
/* flush all SCBs */
|
||||||
list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) {
|
list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list)
|
||||||
struct scsi_cmnd *tmp_cmd = scb->cmd;
|
wd719x_finish_cmd(scb, result);
|
||||||
wd719x_finish_cmd(tmp_cmd, result);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
spin_unlock_irqrestore(wd->sh->host_lock, flags);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -554,7 +542,6 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
|
|||||||
union wd719x_regs regs,
|
union wd719x_regs regs,
|
||||||
struct wd719x_scb *scb)
|
struct wd719x_scb *scb)
|
||||||
{
|
{
|
||||||
struct scsi_cmnd *cmd;
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
/* now have to find result from card */
|
/* now have to find result from card */
|
||||||
@ -642,9 +629,8 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd,
|
|||||||
result = DID_ERROR;
|
result = DID_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cmd = scb->cmd;
|
|
||||||
|
|
||||||
wd719x_finish_cmd(cmd, result);
|
wd719x_finish_cmd(scb, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t wd719x_interrupt(int irq, void *dev_id)
|
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;
|
int ret;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&wd->active_scbs);
|
INIT_LIST_HEAD(&wd->active_scbs);
|
||||||
INIT_LIST_HEAD(&wd->free_scbs);
|
|
||||||
|
|
||||||
sh->base = pci_resource_start(wd->pdev, 0);
|
sh->base = pci_resource_start(wd->pdev, 0);
|
||||||
|
|
||||||
@ -873,6 +858,7 @@ static int wd719x_board_found(struct Scsi_Host *sh)
|
|||||||
static struct scsi_host_template wd719x_template = {
|
static struct scsi_host_template wd719x_template = {
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.name = "Western Digital 719x",
|
.name = "Western Digital 719x",
|
||||||
|
.cmd_size = sizeof(struct wd719x_scb),
|
||||||
.queuecommand = wd719x_queuecommand,
|
.queuecommand = wd719x_queuecommand,
|
||||||
.eh_abort_handler = wd719x_abort,
|
.eh_abort_handler = wd719x_abort,
|
||||||
.eh_device_reset_handler = wd719x_dev_reset,
|
.eh_device_reset_handler = wd719x_dev_reset,
|
||||||
|
@ -74,7 +74,6 @@ struct wd719x {
|
|||||||
void *hash_virt; /* hash table CPU address */
|
void *hash_virt; /* hash table CPU address */
|
||||||
dma_addr_t hash_phys; /* hash table bus address */
|
dma_addr_t hash_phys; /* hash table bus address */
|
||||||
struct list_head active_scbs;
|
struct list_head active_scbs;
|
||||||
struct list_head free_scbs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* timeout delays in microsecs */
|
/* timeout delays in microsecs */
|
||||||
|
Loading…
Reference in New Issue
Block a user