[PATCH for review] [69/145] x86_64: Disable DAC on VIA PCI bridges

Andi Kleen
2006-08-10T19:36:29Z

r

Because of several reports that it doesn't work

TBD needs a real confirmation this fixes the problem
TBD needs more testing

Signed-off-by: Andi Kleen <ak@suse.de>

---
Documentation/x86_64/boot-options.txt | 4 +++
arch/x86_64/kernel/pci-dma.c | 42 ++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+)

Index: linux/Documentation/x86_64/boot-options.txt
===================================================================
--- linux.orig/Documentation/x86_64/boot-options.txt
+++ linux/Documentation/x86_64/boot-options.txt
@@ -199,6 +199,10 @@ IOMMU
allowed overwrite iommu off workarounds for specific chipsets.
soft Use software bounce buffering (default for Intel machines)
noaperture Don't touch the aperture for AGP.
+ allowdac Allow DMA >4GB - default selected based on chipset bugs
+ When off all DMA over >4GB is forced through an IOMMU or bounce
+ buffering.
+ nodac Forbid DMA >4GB

swiotlb=pages[,force]

Index: linux/arch/x86_64/kernel/pci-dma.c
===================================================================
--- linux.orig/arch/x86_64/kernel/pci-dma.c
+++ linux/arch/x86_64/kernel/pci-dma.c
@@ -170,11 +170,47 @@ void dma_free_coherent(struct device *de
}
EXPORT_SYMBOL(dma_free_coherent);

+static int allow_dac;
+
+static int bridge_from_vendor(struct device *dev, u16 vendor)
+{
+#ifdef CONFIG_PCI
+ struct pci_bus *bus;
+ if (dev->bus != &pci_bus_type)
+ return 0;
+ bus = to_pci_dev(dev)->bus;
+ /* RED-PEN
+ Assumes no locking is needed on these lists because someone
+ should hold a reference count on the target device.
+ Correct assumption? */
+ while (bus != NULL) {
+ if (bus->self && bus->self->vendor == vendor)
+ return 1;
+ bus = bus->parent;
+ }
+#endif
+ return 0;
+}
+
int dma_supported(struct device *dev, u64 mask)
{
if (dma_ops->dma_supported)
return dma_ops->dma_supported(dev, mask);

+ if (mask > DMA_32BIT_MASK) {
+ /* Some VIA bridges seem to have trouble with Double Address
+ Cycle. Disable it behind them all for now. The driver
+ should fall back to non DAC. */
+ if (bridge_from_vendor(dev, PCI_VENDOR_ID_VIA) && !allow_dac) {
+ printk(KERN_INFO
+ "PCI: %s disallowing DAC because of VIA bridge.\n",
+ dev->bus_id);
+ return 0;
+ }
+ if (allow_dac < 0)
+ return 0;
+ }
+
/* Copied from i386. Doesn't make much sense, because it will
only work for pci_alloc_coherent.
The caller just has to use GFP_DMA in this case. */
@@ -231,6 +267,8 @@ EXPORT_SYMBOL(dma_set_mask);
allowed overwrite iommu off workarounds for specific chipsets.
soft Use software bounce buffering (default for Intel machines)
noaperture Don't touch the aperture for AGP.
+ allowdac Allow DMA >4GB - default selected based on chipset bugs
+ nodac Forbid DMA >4GB
*/
__init int iommu_setup(char *p)
{
@@ -264,6 +302,10 @@ __init int iommu_setup(char *p)
iommu_merge = 0;
if (!strncmp(p, "forcesac",8))
iommu_sac_force = 1;
+ if (!strncmp(p, "allowdac", 8))
+ allow_dac = 1;
+ if (!strncmp(p, "nodac", 5))
+ allow_dac = -1;

#ifdef CONFIG_SWIOTLB
if (!strncmp(p, "soft",4))