x86/PCI: Allow DMA ops specific to a PCI domain
authorKeith Busch <keith.busch@intel.com>
Tue, 12 Jan 2016 20:18:08 +0000 (13:18 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 15 Jan 2016 19:54:55 +0000 (13:54 -0600)
The Intel Volume Management Device (VMD) is a PCIe endpoint that acts as a
host bridge to another PCI domain.  When devices below the VMD perform DMA,
the VMD replaces their DMA source IDs with its own source ID.  Therefore,
those devices require special DMA ops.

Add interfaces to allow the VMD driver to set up dma_ops for the devices
below it.

[bhelgaas: remove "extern", add "static", changelog]
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
arch/x86/include/asm/device.h
arch/x86/pci/common.c

index 03dd72957d2f635a570fa26a978ddb0e26027873..684ed6c3aa679d15dac80a48cd832ecf721b52dd 100644 (file)
@@ -10,6 +10,16 @@ struct dev_archdata {
 #endif
 };
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+struct dma_domain {
+       struct list_head node;
+       struct dma_map_ops *dma_ops;
+       int domain_nr;
+};
+void add_dma_domain(struct dma_domain *domain);
+void del_dma_domain(struct dma_domain *domain);
+#endif
+
 struct pdev_archdata {
 };
 
index eccd4d99e6a4a7182bf74469f5ecd580e8f22417..2879efc73a967bca70606547014decd3703c84bf 100644 (file)
@@ -641,6 +641,43 @@ unsigned int pcibios_assign_all_busses(void)
        return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
 }
 
+#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+static LIST_HEAD(dma_domain_list);
+static DEFINE_SPINLOCK(dma_domain_list_lock);
+
+void add_dma_domain(struct dma_domain *domain)
+{
+       spin_lock(&dma_domain_list_lock);
+       list_add(&domain->node, &dma_domain_list);
+       spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(add_dma_domain);
+
+void del_dma_domain(struct dma_domain *domain)
+{
+       spin_lock(&dma_domain_list_lock);
+       list_del(&domain->node);
+       spin_unlock(&dma_domain_list_lock);
+}
+EXPORT_SYMBOL_GPL(del_dma_domain);
+
+static void set_dma_domain_ops(struct pci_dev *pdev)
+{
+       struct dma_domain *domain;
+
+       spin_lock(&dma_domain_list_lock);
+       list_for_each_entry(domain, &dma_domain_list, node) {
+               if (pci_domain_nr(pdev->bus) == domain->domain_nr) {
+                       pdev->dev.archdata.dma_ops = domain->dma_ops;
+                       break;
+               }
+       }
+       spin_unlock(&dma_domain_list_lock);
+}
+#else
+static void set_dma_domain_ops(struct pci_dev *pdev) {}
+#endif
+
 int pcibios_add_device(struct pci_dev *dev)
 {
        struct setup_data *data;
@@ -670,6 +707,7 @@ int pcibios_add_device(struct pci_dev *dev)
                pa_data = data->next;
                iounmap(data);
        }
+       set_dma_domain_ops(dev);
        return 0;
 }