# HG changeset patch
# User Jan Beulich <jbeulich@xxxxxxxx>
# Date 1316301003 -3600
# Node ID e5263921c85e2ec98946dc4e6aa6bfd94b1920e3
# Parent b78235de5c6407023759f9bbf723dd83887fedf0
PCI multi-seg: introduce notion of PCI segments
Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
diff -r b78235de5c64 -r e5263921c85e xen/arch/x86/setup.c
--- a/xen/arch/x86/setup.c Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/arch/x86/setup.c Sun Sep 18 00:10:03 2011 +0100
@@ -1246,6 +1246,8 @@
local_irq_enable();
+ pt_pci_init();
+
#ifdef CONFIG_X86_64
vesa_mtrr_init();
diff -r b78235de5c64 -r e5263921c85e xen/arch/x86/x86_64/acpi_mmcfg.c
--- a/xen/arch/x86/x86_64/acpi_mmcfg.c Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/arch/x86/x86_64/acpi_mmcfg.c Sun Sep 18 00:10:03 2011 +0100
@@ -111,6 +111,7 @@
pci_mmcfg_config_num = 0;
return -ENODEV;
}
+ pci_add_segment(pci_mmcfg_config[i].pci_segment);
}
return 0;
diff -r b78235de5c64 -r e5263921c85e xen/arch/x86/x86_64/mmconfig-shared.c
--- a/xen/arch/x86/x86_64/mmconfig-shared.c Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/arch/x86/x86_64/mmconfig-shared.c Sun Sep 18 00:10:03 2011 +0100
@@ -171,6 +171,7 @@
pci_mmcfg_config[i].pci_segment = i;
pci_mmcfg_config[i].start_bus_number = 0;
pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1;
+ pci_add_segment(i);
}
return "AMD Family 10h NB";
diff -r b78235de5c64 -r e5263921c85e xen/drivers/passthrough/pci.c
--- a/xen/drivers/passthrough/pci.c Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/drivers/passthrough/pci.c Sun Sep 18 00:10:03 2011 +0100
@@ -26,29 +26,93 @@
#include <asm/hvm/irq.h>
#include <xen/delay.h>
#include <xen/keyhandler.h>
+#include <xen/radix-tree.h>
#include <xen/tasklet.h>
#ifdef CONFIG_X86
#include <asm/msi.h>
#endif
-LIST_HEAD(alldevs_list);
+struct pci_seg {
+ struct list_head alldevs_list;
+ u16 nr;
+ /* bus2bridge_lock protects bus2bridge array */
+ spinlock_t bus2bridge_lock;
+#define MAX_BUSES 256
+ struct {
+ u8 map;
+ u8 bus;
+ u8 devfn;
+ } bus2bridge[MAX_BUSES];
+};
+
spinlock_t pcidevs_lock = SPIN_LOCK_UNLOCKED;
+static struct radix_tree_root pci_segments;
-#define MAX_BUSES 256
-static struct {
- u8 map;
- u8 bus;
- u8 devfn;
-} bus2bridge[MAX_BUSES];
+static inline struct pci_seg *get_pseg(u16 seg)
+{
+ return radix_tree_lookup(&pci_segments, seg);
+}
-/* bus2bridge_lock protects bus2bridge array */
-static DEFINE_SPINLOCK(bus2bridge_lock);
+static struct pci_seg *alloc_pseg(u16 seg)
+{
+ struct pci_seg *pseg = get_pseg(seg);
-static struct pci_dev *alloc_pdev(u8 bus, u8 devfn)
+ if ( pseg )
+ return pseg;
+
+ pseg = xmalloc(struct pci_seg);
+ if ( !pseg )
+ return NULL;
+
+ pseg->nr = seg;
+ INIT_LIST_HEAD(&pseg->alldevs_list);
+ spin_lock_init(&pseg->bus2bridge_lock);
+ memset(pseg->bus2bridge, 0, sizeof(pseg->bus2bridge));
+
+ if ( radix_tree_insert(&pci_segments, seg, pseg) )
+ {
+ xfree(pseg);
+ pseg = NULL;
+ }
+
+ return pseg;
+}
+
+static int pci_segments_iterate(
+ int (*handler)(struct pci_seg *, void *), void *arg)
+{
+ u16 seg = 0;
+ int rc = 0;
+
+ do {
+ struct pci_seg *pseg;
+
+ if ( !radix_tree_gang_lookup(&pci_segments, (void **)&pseg, seg, 1) )
+ break;
+ rc = handler(pseg, arg);
+ seg = pseg->nr + 1;
+ } while (!rc && seg);
+
+ return rc;
+}
+
+void __init pt_pci_init(void)
+{
+ radix_tree_init(&pci_segments);
+ if ( !alloc_pseg(0) )
+ panic("Could not initialize PCI segment 0\n");
+}
+
+int __init pci_add_segment(u16 seg)
+{
+ return alloc_pseg(seg) ? 0 : -ENOMEM;
+}
+
+static struct pci_dev *alloc_pdev(struct pci_seg *pseg, u8 bus, u8 devfn)
{
struct pci_dev *pdev;
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
if ( pdev->bus == bus && pdev->devfn == devfn )
return pdev;
@@ -61,7 +125,7 @@
*((u8*) &pdev->devfn) = devfn;
pdev->domain = NULL;
INIT_LIST_HEAD(&pdev->msi_list);
- list_add(&pdev->alldevs_list, &alldevs_list);
+ list_add(&pdev->alldevs_list, &pseg->alldevs_list);
spin_lock_init(&pdev->msix_table_lock);
return pdev;
@@ -75,11 +139,15 @@
struct pci_dev *pci_get_pdev(int bus, int devfn)
{
+ struct pci_seg *pseg = get_pseg(0);
struct pci_dev *pdev = NULL;
ASSERT(spin_is_locked(&pcidevs_lock));
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ if ( !pseg )
+ return NULL;
+
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
if ( (pdev->bus == bus || bus == -1) &&
(pdev->devfn == devfn || devfn == -1) )
{
@@ -91,9 +159,13 @@
struct pci_dev *pci_get_pdev_by_domain(struct domain *d, int bus, int devfn)
{
+ struct pci_seg *pseg = get_pseg(0);
struct pci_dev *pdev = NULL;
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ if ( !pseg )
+ return NULL;
+
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
if ( (pdev->bus == bus || bus == -1) &&
(pdev->devfn == devfn || devfn == -1) &&
(pdev->domain == d) )
@@ -145,6 +217,7 @@
int pci_add_device(u8 bus, u8 devfn, const struct pci_dev_info *info)
{
+ struct pci_seg *pseg;
struct pci_dev *pdev;
unsigned int slot = PCI_SLOT(devfn), func = PCI_FUNC(devfn);
const char *pdev_type;
@@ -167,7 +240,10 @@
return -EINVAL;
spin_lock(&pcidevs_lock);
- pdev = alloc_pdev(bus, devfn);
+ pseg = alloc_pseg(0);
+ if ( !pseg )
+ goto out;
+ pdev = alloc_pdev(pseg, bus, devfn);
if ( !pdev )
goto out;
@@ -262,11 +338,15 @@
int pci_remove_device(u8 bus, u8 devfn)
{
+ struct pci_seg *pseg = get_pseg(0);
struct pci_dev *pdev;
int ret = -ENODEV;
+ if ( !pseg )
+ return -ENODEV;
+
spin_lock(&pcidevs_lock);
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
if ( pdev->bus == bus && pdev->devfn == devfn )
{
ret = iommu_remove_device(pdev);
@@ -384,22 +464,26 @@
*/
int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus)
{
+ struct pci_seg *pseg = get_pseg(0);
int ret = 0;
int cnt = 0;
if ( *bus == 0 )
return 0;
- if ( !bus2bridge[*bus].map )
+ if ( !pseg )
+ return -1;
+
+ if ( !pseg->bus2bridge[*bus].map )
return 0;
ret = 1;
- spin_lock(&bus2bridge_lock);
- while ( bus2bridge[*bus].map )
+ spin_lock(&pseg->bus2bridge_lock);
+ while ( pseg->bus2bridge[*bus].map )
{
*secbus = *bus;
- *devfn = bus2bridge[*bus].devfn;
- *bus = bus2bridge[*bus].bus;
+ *devfn = pseg->bus2bridge[*bus].devfn;
+ *bus = pseg->bus2bridge[*bus].bus;
if ( cnt++ >= MAX_BUSES )
{
ret = -1;
@@ -408,7 +492,7 @@
}
out:
- spin_unlock(&bus2bridge_lock);
+ spin_unlock(&pseg->bus2bridge_lock);
return ret;
}
@@ -431,14 +515,13 @@
* scan pci devices to add all existed PCI devices to alldevs_list,
* and setup pci hierarchy in array bus2bridge.
*/
-int __init scan_pci_devices(void)
+static int __init _scan_pci_devices(struct pci_seg *pseg, void *arg)
{
struct pci_dev *pdev;
int bus, dev, func;
u8 sec_bus, sub_bus;
int type;
- spin_lock(&pcidevs_lock);
for ( bus = 0; bus < 256; bus++ )
{
for ( dev = 0; dev < 32; dev++ )
@@ -448,11 +531,10 @@
if ( pci_device_detect(bus, dev, func) == 0 )
continue;
- pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
+ pdev = alloc_pdev(pseg, bus, PCI_DEVFN(dev, func));
if ( !pdev )
{
printk("%s: alloc_pdev failed.\n", __func__);
- spin_unlock(&pcidevs_lock);
return -ENOMEM;
}
@@ -470,14 +552,15 @@
sub_bus = pci_conf_read8(bus, dev, func,
PCI_SUBORDINATE_BUS);
- spin_lock(&bus2bridge_lock);
+ spin_lock(&pseg->bus2bridge_lock);
for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
{
- bus2bridge[sec_bus].map = 1;
- bus2bridge[sec_bus].bus = bus;
- bus2bridge[sec_bus].devfn = PCI_DEVFN(dev, func);
+ pseg->bus2bridge[sec_bus].map = 1;
+ pseg->bus2bridge[sec_bus].bus = bus;
+ pseg->bus2bridge[sec_bus].devfn =
+ PCI_DEVFN(dev, func);
}
- spin_unlock(&bus2bridge_lock);
+ spin_unlock(&pseg->bus2bridge_lock);
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -487,7 +570,6 @@
default:
printk("%s: unknown type: bdf = %x:%x.%x\n",
__func__, bus, dev, func);
- spin_unlock(&pcidevs_lock);
return -EINVAL;
}
@@ -498,8 +580,18 @@
}
}
+ return 0;
+}
+
+int __init scan_pci_devices(void)
+{
+ int ret;
+
+ spin_lock(&pcidevs_lock);
+ ret = pci_segments_iterate(_scan_pci_devices, NULL);
spin_unlock(&pcidevs_lock);
- return 0;
+
+ return ret;
}
/* Disconnect all PCI devices from the PCI buses. From the PCI spec:
@@ -508,29 +600,33 @@
* configuration accesses. All devices are required to support
* this base level of functionality."
*/
-void disconnect_pci_devices(void)
+static int _disconnect_pci_devices(struct pci_seg *pseg, void *arg)
{
struct pci_dev *pdev;
- spin_lock(&pcidevs_lock);
-
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
pci_conf_write16(pdev->bus, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn), PCI_COMMAND, 0);
+ return 0;
+}
+
+void disconnect_pci_devices(void)
+{
+ spin_lock(&pcidevs_lock);
+ pci_segments_iterate(_disconnect_pci_devices, NULL);
spin_unlock(&pcidevs_lock);
}
#ifdef SUPPORT_MSI_REMAPPING
-static void dump_pci_devices(unsigned char ch)
+static int _dump_pci_devices(struct pci_seg *pseg, void *arg)
{
struct pci_dev *pdev;
struct msi_desc *msi;
- printk("==== PCI devices ====\n");
- spin_lock(&pcidevs_lock);
+ printk("==== segment %04x ====\n", pseg->nr);
- list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
{
printk("%02x:%02x.%x - dom %-3d - MSIs < ",
pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
@@ -540,6 +636,14 @@
printk(">\n");
}
+ return 0;
+}
+
+static void dump_pci_devices(unsigned char ch)
+{
+ printk("==== PCI devices ====\n");
+ spin_lock(&pcidevs_lock);
+ pci_segments_iterate(_dump_pci_devices, NULL);
spin_unlock(&pcidevs_lock);
}
diff -r b78235de5c64 -r e5263921c85e xen/include/xen/iommu.h
--- a/xen/include/xen/iommu.h Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/include/xen/iommu.h Sun Sep 18 00:10:03 2011 +0100
@@ -92,6 +92,8 @@
void iommu_set_pgd(struct domain *d);
void iommu_domain_teardown(struct domain *d);
+void pt_pci_init(void);
+
struct pirq;
int hvm_do_IRQ_dpci(struct domain *, struct pirq *);
int dpci_ioport_intercept(ioreq_t *p);
diff -r b78235de5c64 -r e5263921c85e xen/include/xen/pci.h
--- a/xen/include/xen/pci.h Sun Sep 18 00:01:58 2011 +0100
+++ b/xen/include/xen/pci.h Sun Sep 18 00:10:03 2011 +0100
@@ -89,6 +89,7 @@
struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn);
void pci_release_devices(struct domain *d);
+int pci_add_segment(u16 seg);
int pci_add_device(u8 bus, u8 devfn, const struct pci_dev_info *);
int pci_remove_device(u8 bus, u8 devfn);
struct pci_dev *pci_get_pdev(int bus, int devfn);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|