[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] VT-d: pci code cleanup



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1245397520 -3600
# Node ID 2f1fa2215e60a325aabc14e22187761c13987f20
# Parent  e2625f2359408ed47bc6e65278c79b3cddafebd3
VT-d: pci code cleanup

This patch moves the pci code from iommu.c to pci.c. Instead of setup
pci hierarchy in array bus2bridge in iommu_context_mapping, use
scan_pci_devices once to add all existed PCI devices in system to
alldevs_list and setup pci hierarchy in array bus2bridge. In addition,
implement find_upstream_bridge to find the upstream PCIe-to-PCI/PCIX
bridge or PCI legacy bridge for a PCI device, therefore it's cleanly
to handle context map/unmap for PCI device, even for source-id
setting.

Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx>
---
 xen/drivers/passthrough/pci.c       |  153 +++++++++++++++++++++++++++
 xen/drivers/passthrough/vtd/iommu.c |  203 +++++++++---------------------------
 xen/include/xen/pci.h               |   11 +
 3 files changed, 215 insertions(+), 152 deletions(-)

diff -r e2625f235940 -r 2f1fa2215e60 xen/drivers/passthrough/pci.c
--- a/xen/drivers/passthrough/pci.c     Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/drivers/passthrough/pci.c     Fri Jun 19 08:45:20 2009 +0100
@@ -30,6 +30,16 @@ LIST_HEAD(alldevs_list);
 LIST_HEAD(alldevs_list);
 spinlock_t pcidevs_lock = SPIN_LOCK_UNLOCKED;
 
+#define MAX_BUSES 256
+static struct {
+    u8 map;
+    u8 bus;
+    u8 devfn;
+} bus2bridge[MAX_BUSES];
+
+/* bus2bridge_lock protects bus2bridge array */
+static DEFINE_SPINLOCK(bus2bridge_lock);
+
 struct pci_dev *alloc_pdev(u8 bus, u8 devfn)
 {
     struct pci_dev *pdev;
@@ -240,6 +250,149 @@ void pci_release_devices(struct domain *
     spin_unlock(&pcidevs_lock);
 }
 
+#define PCI_CLASS_BRIDGE_PCI     0x0604
+
+int pdev_type(u8 bus, u8 devfn)
+{
+    u16 class_device;
+    u16 status, creg;
+    int pos;
+    u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
+
+    class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
+    if ( class_device == PCI_CLASS_BRIDGE_PCI )
+    {
+        pos = pci_find_next_cap(bus, devfn,
+                                PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
+        if ( !pos )
+            return DEV_TYPE_LEGACY_PCI_BRIDGE;
+        creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
+        return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
+            DEV_TYPE_PCIe2PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
+    }
+
+    status = pci_conf_read16(bus, d, f, PCI_STATUS);
+    if ( !(status & PCI_STATUS_CAP_LIST) )
+        return DEV_TYPE_PCI;
+
+    if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
+        return DEV_TYPE_PCIe_ENDPOINT;
+
+    return DEV_TYPE_PCI;
+}
+
+/*
+ * find the upstream PCIe-to-PCI/PCIX bridge or PCI legacy bridge
+ * return 0: the device is integrated PCI device or PCIe
+ * return 1: find PCIe-to-PCI/PCIX bridge or PCI legacy bridge
+ * return -1: fail
+ */
+int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus)
+{
+    int ret = 0;
+    int cnt = 0;
+
+    if ( *bus == 0 )
+        return 0;
+
+    if ( !bus2bridge[*bus].map )
+        return 0;
+
+    ret = 1;
+    spin_lock(&bus2bridge_lock);
+    while ( bus2bridge[*bus].map )
+    {
+        *secbus = *bus;
+        *devfn = bus2bridge[*bus].devfn;
+        *bus = bus2bridge[*bus].bus;
+        if ( cnt++ >= MAX_BUSES )
+        {
+            ret = -1;
+            goto out;
+        }
+    }
+
+out:
+    spin_unlock(&bus2bridge_lock);
+    return ret;
+}
+
+/*
+ * scan pci devices to add all existed PCI devices to alldevs_list,
+ * and setup pci hierarchy in array bus2bridge. This function is only
+ * called in VT-d hardware setup
+ */
+int __init scan_pci_devices(void)
+{
+    struct pci_dev *pdev;
+    int bus, dev, func;
+    u8 sec_bus, sub_bus;
+    int type;
+    u32 l;
+
+    spin_lock(&pcidevs_lock);
+    for ( bus = 0; bus < 256; bus++ )
+    {
+        for ( dev = 0; dev < 32; dev++ )
+        {
+            for ( func = 0; func < 8; func++ )
+            {
+                l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
+                /* some broken boards return 0 or ~0 if a slot is empty: */
+                if ( (l == 0xffffffff) || (l == 0x00000000) ||
+                     (l == 0x0000ffff) || (l == 0xffff0000) )
+                    continue;
+
+                pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
+                if ( !pdev )
+                {
+                    printk("%s: alloc_pdev failed.\n", __func__);
+                    spin_unlock(&pcidevs_lock);
+                    return -ENOMEM;
+                }
+
+                /* build bus2bridge */
+                type = pdev_type(bus, PCI_DEVFN(dev, func));
+                switch ( type )
+                {
+                    case DEV_TYPE_PCIe_BRIDGE:
+                        break;
+
+                    case DEV_TYPE_PCIe2PCI_BRIDGE:
+                    case DEV_TYPE_LEGACY_PCI_BRIDGE:
+                        sec_bus = pci_conf_read8(bus, dev, func,
+                                                 PCI_SECONDARY_BUS);
+                        sub_bus = pci_conf_read8(bus, dev, func,
+                                                 PCI_SUBORDINATE_BUS);
+
+                        spin_lock(&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);
+                        }
+                        spin_unlock(&bus2bridge_lock);
+                        break;
+
+                    case DEV_TYPE_PCIe_ENDPOINT:
+                    case DEV_TYPE_PCI:
+                        break;
+
+                    default:
+                        printk("%s: unknown type: bdf = %x:%x.%x\n",
+                               __func__, bus, dev, func);
+                        spin_unlock(&pcidevs_lock);
+                        return -EINVAL;
+                }
+            }
+        }
+    }
+
+    spin_unlock(&pcidevs_lock);
+    return 0;
+}
+
 #ifdef SUPPORT_MSI_REMAPPING
 static void dump_pci_devices(unsigned char ch)
 {
diff -r e2625f235940 -r 2f1fa2215e60 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c       Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c       Fri Jun 19 08:45:20 2009 +0100
@@ -1066,92 +1066,12 @@ static int domain_context_mapping_one(
     return 0;
 }
 
-#define PCI_BASE_CLASS_BRIDGE    0x06
-#define PCI_CLASS_BRIDGE_PCI     0x0604
-
-enum {
-    DEV_TYPE_PCIe_ENDPOINT,
-    DEV_TYPE_PCIe_BRIDGE,    // PCIe root port, switch
-    DEV_TYPE_PCI_BRIDGE,     // PCIe-to-PCI/PCIx bridge, PCI-to-PCI bridge
-    DEV_TYPE_PCI,
-};
-
-int pdev_type(u8 bus, u8 devfn)
-{
-    u16 class_device;
-    u16 status, creg;
-    int pos;
-    u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
-
-    class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
-    if ( class_device == PCI_CLASS_BRIDGE_PCI )
-    {
-        pos = pci_find_next_cap(bus, devfn,
-                                PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
-        if ( !pos )
-            return DEV_TYPE_PCI_BRIDGE;
-        creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
-        return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
-            DEV_TYPE_PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
-    }
-
-    status = pci_conf_read16(bus, d, f, PCI_STATUS);
-    if ( !(status & PCI_STATUS_CAP_LIST) )
-        return DEV_TYPE_PCI;
-
-    if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
-        return DEV_TYPE_PCIe_ENDPOINT;
-
-    return DEV_TYPE_PCI;
-}
-
-#define MAX_BUSES 256
-static DEFINE_SPINLOCK(bus2bridge_lock);
-static struct { u8 map, bus, devfn; } bus2bridge[MAX_BUSES];
-
-static int _find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
-{
-    int cnt = 0;
-    *secbus = *bus;
-
-    ASSERT(spin_is_locked(&bus2bridge_lock));
-    if ( !bus2bridge[*bus].map )
-        return 0;
-
-    while ( bus2bridge[*bus].map )
-    {
-        *secbus = *bus;
-        *devfn = bus2bridge[*bus].devfn;
-        *bus = bus2bridge[*bus].bus;
-        if ( cnt++ >= MAX_BUSES )
-            return 0;
-    }
-
-    return 1;
-}
-
-static int find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
-{
-    int ret = 0;
-
-    if ( *bus == 0 )
-        /* assume integrated PCI devices in RC have valid requester-id */
-        return 1;
-
-    spin_lock(&bus2bridge_lock);
-    ret = _find_pcie_endpoint(bus, devfn, secbus);
-    spin_unlock(&bus2bridge_lock);
-
-    return ret;
-}
-
 static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
 {
     struct acpi_drhd_unit *drhd;
     int ret = 0;
-    u16 sec_bus, sub_bus;
     u32 type;
-    u8 secbus, secdevfn;
+    u8 secbus;
     struct pci_dev *pdev = pci_get_pdev(bus, devfn);
 
     if ( pdev == NULL )
@@ -1179,22 +1099,8 @@ static int domain_context_mapping(struct
     switch ( type )
     {
     case DEV_TYPE_PCIe_BRIDGE:
-        break;
-
-    case DEV_TYPE_PCI_BRIDGE:
-        sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
-                                 PCI_SECONDARY_BUS);
-        sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
-                                 PCI_SUBORDINATE_BUS);
-
-        spin_lock(&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 =  devfn;
-        }
-        spin_unlock(&bus2bridge_lock);
+    case DEV_TYPE_PCIe2PCI_BRIDGE:
+    case DEV_TYPE_LEGACY_PCI_BRIDGE:
         break;
 
     case DEV_TYPE_PCIe_ENDPOINT:
@@ -1211,31 +1117,29 @@ static int domain_context_mapping(struct
 
         ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
         if ( ret )
-           break;
-
-        secbus = bus;
-        secdevfn = devfn;
-        /* dependent devices mapping */
-        while ( bus2bridge[bus].map )
-        {
-            secbus = bus;
-            secdevfn = devfn;
-            devfn = bus2bridge[bus].devfn;
-            bus = bus2bridge[bus].bus;
+            break;
+
+        if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
+            break;
+
+        /* PCIe to PCI/PCIx bridge */
+        if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
+        {
             ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
             if ( ret )
                 return ret;
-        }
-
-        if ( (secbus != bus) && (secdevfn != 0) )
+
             /*
-             * The source-id for transactions on non-PCIe buses seem
-             * to originate from devfn=0 on the secondary bus behind
-             * the bridge.  Map that id as well.  The id to use in
-             * these scanarios is not particularly well documented
-             * anywhere.
+             * Devices behind PCIe-to-PCI/PCIx bridge may generate
+             * different requester-id. It may originate from devfn=0
+             * on the secondary bus behind the bridge. Map that id
+             * as well.
              */
             ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0);
+        }
+        else /* Legacy PCI bridge */
+            ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
+
         break;
 
     default:
@@ -1296,7 +1200,7 @@ static int domain_context_unmap(struct d
     struct acpi_drhd_unit *drhd;
     int ret = 0;
     u32 type;
-    u8 secbus, secdevfn;
+    u8 secbus;
     struct pci_dev *pdev = pci_get_pdev(bus, devfn);
 
     BUG_ON(!pdev);
@@ -1309,7 +1213,8 @@ static int domain_context_unmap(struct d
     switch ( type )
     {
     case DEV_TYPE_PCIe_BRIDGE:
-    case DEV_TYPE_PCI_BRIDGE:
+    case DEV_TYPE_PCIe2PCI_BRIDGE:
+    case DEV_TYPE_LEGACY_PCI_BRIDGE:
         break;
 
     case DEV_TYPE_PCIe_ENDPOINT:
@@ -1327,22 +1232,21 @@ static int domain_context_unmap(struct d
         if ( ret )
             break;
 
-        secbus = bus;
-        secdevfn = devfn;
-        /* dependent devices unmapping */
-        while ( bus2bridge[bus].map )
-        {
-            secbus = bus;
-            secdevfn = devfn;
-            devfn = bus2bridge[bus].devfn;
-            bus = bus2bridge[bus].bus;
+        if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
+            break;
+
+        /* PCIe to PCI/PCIx bridge */
+        if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
+        {
             ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
             if ( ret )
                 return ret;
-        }
-
-        if ( (secbus != bus) && (secdevfn != 0) )
+
             ret = domain_context_unmap_one(domain, drhd->iommu, secbus, 0);
+        }
+        else /* Legacy PCI bridge */
+            ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
+
         break;
 
     default:
@@ -1584,31 +1488,24 @@ static void setup_dom0_devices(struct do
 {
     struct hvm_iommu *hd;
     struct pci_dev *pdev;
-    int bus, dev, func;
-    u32 l;
+    int bus, devfn;
 
     hd = domain_hvm_iommu(d);
 
     spin_lock(&pcidevs_lock);
     for ( bus = 0; bus < 256; bus++ )
     {
-        for ( dev = 0; dev < 32; dev++ )
-        {
-            for ( func = 0; func < 8; func++ )
-            {
-                l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
-                /* some broken boards return 0 or ~0 if a slot is empty: */
-                if ( (l == 0xffffffff) || (l == 0x00000000) ||
-                     (l == 0x0000ffff) || (l == 0xffff0000) )
-                    continue;
-
-                pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
-                pdev->domain = d;
-                list_add(&pdev->domain_list, &d->arch.pdev_list);
-                domain_context_mapping(d, pdev->bus, pdev->devfn);
-                if ( ats_device(0, pdev->bus, pdev->devfn) )
-                    enable_ats_device(0, pdev->bus, pdev->devfn);
-            }
+        for ( devfn = 0; devfn < 256; devfn++ )
+        {
+            pdev = pci_get_pdev(bus, devfn);
+            if ( !pdev )
+                continue;
+
+            pdev->domain = d;
+            list_add(&pdev->domain_list, &d->arch.pdev_list);
+            domain_context_mapping(d, pdev->bus, pdev->devfn);
+            if ( ats_device(0, pdev->bus, pdev->devfn) )
+                enable_ats_device(0, pdev->bus, pdev->devfn);
         }
     }
     spin_unlock(&pcidevs_lock);
@@ -1809,6 +1706,8 @@ int intel_vtd_setup(void)
     memset(domid_bitmap, 0, domid_bitmap_size / 8);
     set_bit(0, domid_bitmap);
 
+    scan_pci_devices();
+
     if ( init_vtd_hw() )
         goto error;
 
@@ -1902,10 +1801,10 @@ static int intel_iommu_group_id(u8 bus, 
 static int intel_iommu_group_id(u8 bus, u8 devfn)
 {
     u8 secbus;
-    if ( !bus2bridge[bus].map || find_pcie_endpoint(&bus, &devfn, &secbus) )
+    if ( find_upstream_bridge(&bus, &devfn, &secbus) < 0 )
+        return -1;
+    else
         return PCI_BDF2(bus, devfn);
-    else
-        return -1;
 }
 
 static u32 iommu_state[MAX_IOMMUS][MAX_IOMMU_REGS];
diff -r e2625f235940 -r 2f1fa2215e60 xen/include/xen/pci.h
--- a/xen/include/xen/pci.h     Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/include/xen/pci.h     Fri Jun 19 08:45:20 2009 +0100
@@ -66,6 +66,17 @@ struct pci_dev {
 
 extern spinlock_t pcidevs_lock;
 
+enum {
+    DEV_TYPE_PCIe_ENDPOINT,
+    DEV_TYPE_PCIe_BRIDGE,       // PCIe root port, switch
+    DEV_TYPE_PCIe2PCI_BRIDGE,   // PCIe-to-PCI/PCIx bridge
+    DEV_TYPE_LEGACY_PCI_BRIDGE, // Legacy PCI bridge
+    DEV_TYPE_PCI,
+};
+
+int scan_pci_devices(void);
+int pdev_type(u8 bus, u8 devfn);
+int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus);
 struct pci_dev *alloc_pdev(u8 bus, u8 devfn);
 void free_pdev(struct pci_dev *pdev);
 struct pci_dev *pci_lock_pdev(int bus, int devfn);

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.