VT-d: Fix ATS enabling for device assignment
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 19 May 2010 07:22:06 +0000 (08:22 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 19 May 2010 07:22:06 +0000 (08:22 +0100)
Currently, Xen only enables ATS in Xen booting. When an ATS capable
device is assigned to guest, ATS is actually not enabled because FLR
before assignment causes it to be disabled. Thus ATS cannot be used in
guest. This patch enables ATS in domain_context_mapping. This ensures
ATS is enabled in assignment because FLR is earlier than
domain_context_mapping call. Therefore ATS can be used in guest. This
patch also implements disable_ats_device to disable ATS when the
device is deassigned from a domain.

Signed-off-by: Weidong Han <weidong.han@intel.com>
xen/drivers/passthrough/vtd/ia64/ats.c
xen/drivers/passthrough/vtd/iommu.c
xen/drivers/passthrough/vtd/x86/ats.c

index ba8ecb20268a6d5e9dceefd175ed7dbb4e85ff47..cfa97acc4e1ac04d4b47718b0f805cb8bb6b12b9 100644 (file)
@@ -47,6 +47,11 @@ int enable_ats_device(int seg, int bus, int devfn)
     return 0;
 }
 
+int disable_ats_device(int seg, int bus, int devfn)
+{
+    return 0;
+}
+
 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
     u64 addr, unsigned int size_order, u64 type)
 {
index 71143cf4f7eb8d9a276b9aa78f30cb0b7e79e7c9..e41c854798fed8eff772b2d159b369394ddcd50a 100644 (file)
@@ -1325,6 +1325,9 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
             dprintk(VTDPREFIX, "d%d:PCIe: map bdf = %x:%x.%x\n",
                     domain->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
         ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
+        if ( !ret && ats_device(0, bus, devfn) )
+            enable_ats_device(0, bus, devfn);
+
         break;
 
     case DEV_TYPE_PCI:
@@ -1454,6 +1457,9 @@ static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn)
             dprintk(VTDPREFIX, "d%d:PCIe: unmap bdf = %x:%x.%x\n",
                     domain->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
         ret = domain_context_unmap_one(domain, iommu, bus, devfn);
+        if ( !ret && ats_device(0, bus, devfn) )
+            disable_ats_device(0, bus, devfn);
+
         break;
 
     case DEV_TYPE_PCI:
@@ -1772,8 +1778,6 @@ static void setup_dom0_devices(struct domain *d)
             list_add(&pdev->domain_list, &d->arch.pdev_list);
             domain_context_mapping(d, pdev->bus, pdev->devfn);
             pci_enable_acs(pdev);
-            if ( ats_device(0, pdev->bus, pdev->devfn) )
-                enable_ats_device(0, pdev->bus, pdev->devfn);
         }
     }
     spin_unlock(&pcidevs_lock);
index 3c325385165058aeb45aff8c7a7d0857e7f13f9d..e56c9d5ad450a95681d2bab2b719d1fd4ee9ee79 100644 (file)
@@ -92,6 +92,9 @@ int ats_device(int seg, int bus, int devfn)
 
     pdev = pci_get_pdev(bus, devfn);
     drhd = acpi_find_matched_drhd_unit(pdev);
+    if ( !drhd )
+        return 0;
+
     if ( !ecap_queued_inval(drhd->iommu->ecap) ||
          !ecap_dev_iotlb(drhd->iommu->ecap) )
         return 0;
@@ -144,6 +147,9 @@ int enable_ats_device(int seg, int bus, int devfn)
 
     value = pci_conf_read16(bus, PCI_SLOT(devfn),
                             PCI_FUNC(devfn), pos + ATS_REG_CTL);
+    if ( value & ATS_ENABLE )
+        return 0;
+
     value |= ATS_ENABLE;
     pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
                      pos + ATS_REG_CTL, value);
@@ -153,10 +159,50 @@ int enable_ats_device(int seg, int bus, int devfn)
     pdev->devfn = devfn;
     pdev->ats_queue_depth = queue_depth;
     list_add(&(pdev->list), &ats_devices);
+    if ( iommu_verbose )
+        dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS is enabled\n",
+                bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
 
     return pos;
 }
 
+int disable_ats_device(int seg, int bus, int devfn)
+{
+    struct list_head *pdev_list, *tmp;
+    struct pci_ats_dev *pdev;
+    u32 value;
+    int pos;
+
+    pos = pci_find_ext_capability(seg, bus, devfn, PCI_EXT_CAP_ID_ATS);
+    if ( !pos )
+        return 0;
+
+    /* BUGBUG: add back seg when multi-seg platform support is enabled */
+    value = pci_conf_read16(bus, PCI_SLOT(devfn),
+                            PCI_FUNC(devfn), pos + ATS_REG_CTL);
+    value &= ~ATS_ENABLE;
+    pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+                     pos + ATS_REG_CTL, value);
+
+    list_for_each_safe( pdev_list, tmp, &ats_devices )
+    {
+        pdev = list_entry(pdev_list, struct pci_ats_dev, list);
+        if ( pdev->bus == bus && pdev->devfn == devfn )
+        {
+            list_del(&pdev->list);
+            xfree(pdev);
+            break;
+        }
+    }
+
+    if ( iommu_verbose )
+        dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS is disabled\n",
+                bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+    return 0;
+}
+
+
 static int device_in_domain(struct iommu *iommu, struct pci_ats_dev *pdev, u16 did)
 {
     struct root_entry *root_entry = NULL;