Commit fde46e96 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Martin K. Petersen
Browse files

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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent d9c30dbc
Loading
Loading
Loading
Loading
+42 −56
Original line number Diff line number Diff line
@@ -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);
	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);
		scsi_dma_unmap(cmd);
	}

	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,
+0 −1
Original line number Diff line number Diff line
@@ -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 */