Commit 3f3debdb authored by Robert Hancock's avatar Robert Hancock Committed by Jeff Garzik

sata_nv: don't use legacy DMA in ADMA mode (v3)

We need to run any DMA command with result taskfile requested in ADMA mode
when the port is in ADMA mode, otherwise it may try to use the legacy DMA engine
in ADMA mode which is not allowed. Enforce this with BUG_ON() since data
corruption could potentially result if this happened. Also, fail any attempt to
try and issue NCQ commands with result taskfile requested, since the hardware
doesn't allow this.
Signed-off-by: default avatarRobert Hancock <hancockr@shaw.ca>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 2254c2e0
...@@ -791,11 +791,13 @@ static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc) ...@@ -791,11 +791,13 @@ static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc)
static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf) static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
{ {
/* Since commands where a result TF is requested are not /* Other than when internal or pass-through commands are executed,
executed in ADMA mode, the only time this function will be called the only time this function will be called in ADMA mode will be
in ADMA mode will be if a command fails. In this case we if a command fails. In the failure case we don't care about going
don't care about going into register mode with ADMA commands into register mode with ADMA commands pending, as the commands will
pending, as the commands will all shortly be aborted anyway. */ all shortly be aborted anyway. We assume that NCQ commands are not
issued via passthrough, which is the only way that switching into
ADMA mode could abort outstanding commands. */
nv_adma_register_mode(ap); nv_adma_register_mode(ap);
ata_tf_read(ap, tf); ata_tf_read(ap, tf);
...@@ -1359,11 +1361,9 @@ static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc) ...@@ -1359,11 +1361,9 @@ static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc)
struct nv_adma_port_priv *pp = qc->ap->private_data; struct nv_adma_port_priv *pp = qc->ap->private_data;
/* ADMA engine can only be used for non-ATAPI DMA commands, /* ADMA engine can only be used for non-ATAPI DMA commands,
or interrupt-driven no-data commands, where a result taskfile or interrupt-driven no-data commands. */
is not required. */
if ((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) || if ((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) ||
(qc->tf.flags & ATA_TFLAG_POLLING) || (qc->tf.flags & ATA_TFLAG_POLLING))
(qc->flags & ATA_QCFLAG_RESULT_TF))
return 1; return 1;
if ((qc->flags & ATA_QCFLAG_DMAMAP) || if ((qc->flags & ATA_QCFLAG_DMAMAP) ||
...@@ -1381,6 +1381,8 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc) ...@@ -1381,6 +1381,8 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
NV_CPB_CTL_IEN; NV_CPB_CTL_IEN;
if (nv_adma_use_reg_mode(qc)) { if (nv_adma_use_reg_mode(qc)) {
BUG_ON(!(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) &&
(qc->flags & ATA_QCFLAG_DMAMAP));
nv_adma_register_mode(qc->ap); nv_adma_register_mode(qc->ap);
ata_qc_prep(qc); ata_qc_prep(qc);
return; return;
...@@ -1425,9 +1427,21 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc) ...@@ -1425,9 +1427,21 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc)
VPRINTK("ENTER\n"); VPRINTK("ENTER\n");
/* We can't handle result taskfile with NCQ commands, since
retrieving the taskfile switches us out of ADMA mode and would abort
existing commands. */
if (unlikely(qc->tf.protocol == ATA_PROT_NCQ &&
(qc->flags & ATA_QCFLAG_RESULT_TF))) {
ata_dev_printk(qc->dev, KERN_ERR,
"NCQ w/ RESULT_TF not allowed\n");
return AC_ERR_SYSTEM;
}
if (nv_adma_use_reg_mode(qc)) { if (nv_adma_use_reg_mode(qc)) {
/* use ATA register mode */ /* use ATA register mode */
VPRINTK("using ATA register mode: 0x%lx\n", qc->flags); VPRINTK("using ATA register mode: 0x%lx\n", qc->flags);
BUG_ON(!(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) &&
(qc->flags & ATA_QCFLAG_DMAMAP));
nv_adma_register_mode(qc->ap); nv_adma_register_mode(qc->ap);
return ata_qc_issue_prot(qc); return ata_qc_issue_prot(qc);
} else } else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment