Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/PCI: Allow DMA ops specific to a PCI domain

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>

authored by

Keith Busch and committed by
Bjorn Helgaas
d9c3d6ff 64bce3e8

+48
+10
arch/x86/include/asm/device.h
··· 10 10 #endif 11 11 }; 12 12 13 + #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS) 14 + struct dma_domain { 15 + struct list_head node; 16 + struct dma_map_ops *dma_ops; 17 + int domain_nr; 18 + }; 19 + void add_dma_domain(struct dma_domain *domain); 20 + void del_dma_domain(struct dma_domain *domain); 21 + #endif 22 + 13 23 struct pdev_archdata { 14 24 }; 15 25
+38
arch/x86/pci/common.c
··· 641 641 return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; 642 642 } 643 643 644 + #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS) 645 + static LIST_HEAD(dma_domain_list); 646 + static DEFINE_SPINLOCK(dma_domain_list_lock); 647 + 648 + void add_dma_domain(struct dma_domain *domain) 649 + { 650 + spin_lock(&dma_domain_list_lock); 651 + list_add(&domain->node, &dma_domain_list); 652 + spin_unlock(&dma_domain_list_lock); 653 + } 654 + EXPORT_SYMBOL_GPL(add_dma_domain); 655 + 656 + void del_dma_domain(struct dma_domain *domain) 657 + { 658 + spin_lock(&dma_domain_list_lock); 659 + list_del(&domain->node); 660 + spin_unlock(&dma_domain_list_lock); 661 + } 662 + EXPORT_SYMBOL_GPL(del_dma_domain); 663 + 664 + static void set_dma_domain_ops(struct pci_dev *pdev) 665 + { 666 + struct dma_domain *domain; 667 + 668 + spin_lock(&dma_domain_list_lock); 669 + list_for_each_entry(domain, &dma_domain_list, node) { 670 + if (pci_domain_nr(pdev->bus) == domain->domain_nr) { 671 + pdev->dev.archdata.dma_ops = domain->dma_ops; 672 + break; 673 + } 674 + } 675 + spin_unlock(&dma_domain_list_lock); 676 + } 677 + #else 678 + static void set_dma_domain_ops(struct pci_dev *pdev) {} 679 + #endif 680 + 644 681 int pcibios_add_device(struct pci_dev *dev) 645 682 { 646 683 struct setup_data *data; ··· 707 670 pa_data = data->next; 708 671 iounmap(data); 709 672 } 673 + set_dma_domain_ops(dev); 710 674 return 0; 711 675 } 712 676