From: Don Dutile <ddutile@redhat.com> Date: Fri, 24 Apr 2009 14:37:51 -0400 Subject: [misc] VT-d: add pci_find_upstream_pcie_bridge Message-id: 49F206FF.3010607@redhat.com O-Subject: [RHEL5.4 PATCH 2/6] VT-d for KVM: Add upstream's pci_find_upstream_pcie_bridge() Bugzilla: 480411 RH-Acked-by: Rik van Riel <riel@redhat.com> BZ 480411 Add upstream's pci_find_upstream_pcie_bridge() to common PCI code for use by Intel Vt-d. Please review & ACK. - Don >From 252ba504f5aba7697756d4966ebdf7169860e0d9 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin <markmc@redhat.com> Date: Tue, 18 Nov 2008 10:30:56 +0000 Subject: [PATCH 2/6] vt-d: add pci_find_upstream_pcie_bridge() Backport this cset: commit 994a65e25df85abc465cfee495557200e8205f9e Author: Keshavamurthy, Anil S <anil.s.keshavamurthy@intel.com> Date: Sun Oct 21 16:41:46 2007 -0700 Intel IOMMU: PCI generic helper function When devices are under a p2p bridge, upstream transactions get replaced by the device id of the bridge as it owns the PCIE transaction. Hence its necessary to setup translations on behalf of the bridge as well. Due to this limitation all devices under a p2p share the same domain in a DMAR. We just cache the type of device, if its a native PCIe device or not for later use. The addition of pci_dev::pcie_type and pci_dev::is_pci needs careful validation wrt. kABI implications. It looks like there's room for both, though. Signed-off-by: Mark McLoughlin <markmc@redhat.com> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0e97265..8c8e51c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -163,3 +163,4 @@ static inline int pci_iov_bus_range(struct pci_bus *bus) return 0; } #endif /* CONFIG_PCI_IOV */ +struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 255b1bc..e378ffe 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -688,6 +688,19 @@ static void pci_read_irq(struct pci_dev *dev) dev->irq = irq; } +static void set_pcie_port_type(struct pci_dev *pdev) +{ + int pos; + u16 reg16; + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!pos) + return; + pdev->is_pcie = 1; + pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); + pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; +} + /** * pci_setup_device - fill in class and map information of a device * @dev: the device structure to fill @@ -713,6 +726,7 @@ int pci_setup_device(struct pci_dev * dev) dev->multifunction = !!(hdr_type & 0x80); dev->cfg_size = pci_cfg_space_size(dev); dev->error_state = pci_channel_io_normal; + set_pcie_port_type(dev); /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 6473e96..55962e7 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -14,6 +14,40 @@ #include "pci.h" DECLARE_RWSEM(pci_bus_sem); +/* + * find the upstream PCIE-to-PCI bridge of a PCI device + * if the device is PCIE, return NULL + * if the device isn't connected to a PCIE bridge (that is its parent is a + * legacy PCI bridge and the bridge is directly connected to bus 0), return its + * parent + */ +struct pci_dev * +pci_find_upstream_pcie_bridge(struct pci_dev *pdev) +{ + struct pci_dev *tmp = NULL; + + if (pdev->is_pcie) + return NULL; + while (1) { + if (!pdev->bus->self) + break; + pdev = pdev->bus->self; + /* a p2p bridge */ + if (!pdev->is_pcie) { + tmp = pdev; + continue; + } + /* PCI device should connect to a PCIE bridge */ + if (pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE) { + /* Busted hardware? */ + WARN_ON_ONCE(1); + return NULL; + } + return pdev; + } + + return tmp; +} static struct pci_bus * __devinit pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) diff --git a/include/linux/pci.h b/include/linux/pci.h index 0e2359e..0db1dff 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -140,6 +140,9 @@ struct pci_dev { u8 hdr_type; /* PCI header type (`multi' flag masked out) */ u8 rom_base_reg; /* which config register controls the ROM */ u8 pin; /* which interrupt pin this device uses */ +#ifndef __GENKSYMS__ + u8 pcie_type; /* PCI-E device/port type */ +#endif struct pci_driver *driver; /* which driver has allocated this device */ u64 dma_mask; /* Mask of the bits of bus address this @@ -185,6 +188,7 @@ struct pci_dev { unsigned int is_managed:1; unsigned int is_physfn:1; unsigned int is_virtfn:1; + unsigned int is_pcie:1; #endif u32 saved_config_space[16]; /* config space saved at suspend time */