Re: [PATCH] sata_nv: fix ADMA ATAPI issues with memory over 4GB (v3)

From: Tejun Heo
Date: Mon Dec 17 2007 - 22:52:44 EST


Please apply the attached patch and try to use cdrom w/o specifying any
parameter and report kernel log.

Thanks.

--
tejun
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 4753a18..acaa8b8 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4537,6 +4537,10 @@ static void ata_fill_sg(struct ata_queued_cmd *qc)
* Note h/w doesn't support 64-bit, so we unconditionally
* truncate dma_addr_t to u32.
*/
+ if (sg_dma_address(sg) & ~DMA_BIT_MASK(32))
+ ata_dev_printk(qc->dev, KERN_WARNING,
+ "XXX DMA address %llx is above 32bit\n",
+ sg_dma_address(sg));
addr = (u32) sg_dma_address(sg);
sg_len = sg_dma_len(sg);

@@ -6628,6 +6632,10 @@ int ata_port_start(struct ata_port *ap)
if (rc)
return rc;

+ ata_port_printk(ap, KERN_INFO, "XXX: prd %p/%llx pad %p/%llx\n",
+ ap->prd, (unsigned long long)ap->prd_dma,
+ ap->pad, (unsigned long long)ap->pad_dma);
+
DPRINTK("prd alloc, virt %p, dma %llx\n", ap->prd,
(unsigned long long)ap->prd_dma);
return 0;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index ed5dc7c..c55d2ae 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -247,6 +247,7 @@ struct nv_adma_port_priv {
void __iomem *ctl_block;
void __iomem *gen_block;
void __iomem *notifier_clear_block;
+ u64 adma_dma_mask;
u8 flags;
int last_issue_ncq;
};
@@ -748,7 +749,7 @@ static int nv_adma_slave_config(struct scsi_device *sdev)
adma_enable = 0;
nv_adma_register_mode(ap);
} else {
- bounce_limit = *ap->dev->dma_mask;
+ bounce_limit = pp->adma_dma_mask;
segment_boundary = NV_ADMA_DMA_BOUNDARY;
sg_tablesize = NV_ADMA_SGTBL_TOTAL_LEN;
adma_enable = 1;
@@ -1134,10 +1135,20 @@ static int nv_adma_port_start(struct ata_port *ap)
void *mem;
dma_addr_t mem_dma;
void __iomem *mmio;
+ struct pci_dev *pdev = to_pci_dev(dev);
u16 tmp;

VPRINTK("ENTER\n");

+ /* Ensure DMA mask is set to 32-bit before allocating legacy PRD and
+ pad buffers */
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc)
+ return rc;
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc)
+ return rc;
+
rc = ata_port_start(ap);
if (rc)
return rc;
@@ -1153,6 +1164,15 @@ static int nv_adma_port_start(struct ata_port *ap)
pp->notifier_clear_block = pp->gen_block +
NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no);

+ /* Now that the legacy PRD and padding buffer are allocated we can
+ safely raise the DMA mask to allocate the CPB/APRD table.
+ These are allowed to fail since we store the value that ends up
+ being used to set as the bounce limit in slave_config later if
+ needed. */
+ pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ pp->adma_dma_mask = *dev->dma_mask;
+
mem = dmam_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ,
&mem_dma, GFP_KERNEL);
if (!mem)
@@ -2418,12 +2438,6 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
hpriv->type = type;
host->private_data = hpriv;

- /* set 64bit dma masks, may fail */
- if (type == ADMA) {
- if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0)
- pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
- }
-
/* request and iomap NV_MMIO_BAR */
rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME);
if (rc)
diff --git a/include/linux/libata.h b/include/linux/libata.h