diff options
| author | Jeff Garzik <jgarzik@pobox.com> | 2004-12-07 08:54:34 -0500 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@pobox.com> | 2004-12-07 08:54:34 -0500 |
| commit | 046de9be7179b9b97eb899aa7c5b0c2c83a87b21 (patch) | |
| tree | ffb869b73cb051b6231c8331fb46db273b45eb3c | |
| parent | 6695ad97269159f81361d988b773750ce81fba37 (diff) | |
[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).
| -rw-r--r-- | drivers/scsi/ahci.c | 3 | ||||
| -rw-r--r-- | drivers/scsi/libata-core.c | 42 | ||||
| -rw-r--r-- | include/linux/libata.h | 1 |
3 files changed, 37 insertions, 9 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 8c300e0a52d9..e0cd4ba41678 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -229,7 +229,8 @@ static struct ata_port_info ahci_port_info[] = { { .sht = &ahci_sht, .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 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 1d2b344aab01..93d987433966 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -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->offset = (unsigned long) buf & ~PAGE_MASK; sg_dma_len(sg) = buflen; - - WARN_ON(buflen > PAGE_SIZE); } 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) 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 * @qc: command to issue to device @@ -2713,12 +2735,16 @@ int ata_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_SG) { - if (ata_sg_setup(qc)) - goto err_out; - } else if (qc->flags & ATA_QCFLAG_SINGLE) { - if (ata_sg_setup_one(qc)) - goto err_out; + if (ata_should_dma_map(qc)) { + if (qc->flags & ATA_QCFLAG_SG) { + if (ata_sg_setup(qc)) + goto err_out; + } else if (qc->flags & ATA_QCFLAG_SINGLE) { + if (ata_sg_setup_one(qc)) + goto err_out; + } + } else { + qc->flags &= ~ATA_QCFLAG_DMAMAP; } ap->ops->qc_prep(qc); diff --git a/include/linux/libata.h b/include/linux/libata.h index 605e0a728c0e..95a7b0ddb096 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -112,6 +112,7 @@ enum { ATA_FLAG_SRST = (1 << 5), /* use ATA SRST, not E.D.D. */ ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ 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_SG = (1 << 3), /* have s/g table? */ |
