Commit 046de9be authored by Jeff Garzik's avatar Jeff Garzik

[libata] only DMA map data for DMA commands (fix >=4GB bug)

libata made the assumption that (for PIO commands in this case)
it could modify DMA memory at the kernel-virtual address, after
mapping this.  This is incorrect, and fails on e.g. platforms that
copy DMA memory back and forth (swiotlb on Intel EM64T and IA64).

Remove this assumption by ensuring that we only call the DMA mapping
routines if we really are going to use DMA for data xfer.

Also:  remove a bogus WARN_ON() in ata_sg_init_one() which caused
bug reports (but no problems).
parent 6695ad97
...@@ -229,7 +229,8 @@ static struct ata_port_info ahci_port_info[] = { ...@@ -229,7 +229,8 @@ static struct ata_port_info ahci_port_info[] = {
{ {
.sht = &ahci_sht, .sht = &ahci_sht,
.host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO, ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
ATA_FLAG_PIO_DMA,
.pio_mask = 0x03, /* pio3-4 */ .pio_mask = 0x03, /* pio3-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops, .port_ops = &ahci_ops,
......
...@@ -1950,8 +1950,6 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) ...@@ -1950,8 +1950,6 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen)
sg->page = virt_to_page(buf); sg->page = virt_to_page(buf);
sg->offset = (unsigned long) buf & ~PAGE_MASK; sg->offset = (unsigned long) buf & ~PAGE_MASK;
sg_dma_len(sg) = buflen; sg_dma_len(sg) = buflen;
WARN_ON(buflen > PAGE_SIZE);
} }
void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg,
...@@ -2693,6 +2691,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) ...@@ -2693,6 +2691,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
VPRINTK("EXIT\n"); VPRINTK("EXIT\n");
} }
static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
switch (qc->tf.protocol) {
case ATA_PROT_DMA:
case ATA_PROT_ATAPI_DMA:
return 1;
case ATA_PROT_ATAPI:
case ATA_PROT_PIO:
case ATA_PROT_PIO_MULT:
if (ap->flags & ATA_FLAG_PIO_DMA)
return 1;
/* fall through */
default:
return 0;
}
/* never reached */
}
/** /**
* ata_qc_issue - issue taskfile to device * ata_qc_issue - issue taskfile to device
* @qc: command to issue to device * @qc: command to issue to device
...@@ -2713,12 +2735,16 @@ int ata_qc_issue(struct ata_queued_cmd *qc) ...@@ -2713,12 +2735,16 @@ int ata_qc_issue(struct ata_queued_cmd *qc)
{ {
struct ata_port *ap = qc->ap; struct ata_port *ap = qc->ap;
if (qc->flags & ATA_QCFLAG_SG) { if (ata_should_dma_map(qc)) {
if (ata_sg_setup(qc)) if (qc->flags & ATA_QCFLAG_SG) {
goto err_out; if (ata_sg_setup(qc))
} else if (qc->flags & ATA_QCFLAG_SINGLE) { goto err_out;
if (ata_sg_setup_one(qc)) } else if (qc->flags & ATA_QCFLAG_SINGLE) {
goto err_out; if (ata_sg_setup_one(qc))
goto err_out;
}
} else {
qc->flags &= ~ATA_QCFLAG_DMAMAP;
} }
ap->ops->qc_prep(qc); ap->ops->qc_prep(qc);
......
...@@ -112,6 +112,7 @@ enum { ...@@ -112,6 +112,7 @@ enum {
ATA_FLAG_SRST = (1 << 5), /* use ATA SRST, not E.D.D. */ ATA_FLAG_SRST = (1 << 5), /* use ATA SRST, not E.D.D. */
ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */
ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */ ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */
ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */
ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */
ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */
......
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