111 lines
3.6 KiB
Diff
111 lines
3.6 KiB
Diff
|
# HG changeset patch
|
||
|
# User Jan Beulich <jbeulich@novell.com>
|
||
|
# Date 1295353690 0
|
||
|
# Node ID 1e7594758b28706c2b72358598ecf632ddda247b
|
||
|
# Parent 78e2e5a50daa1702e3fd9dbceada700cdaefd511
|
||
|
VT-d/ATS: misc fixes
|
||
|
|
||
|
First of all there were three places potentially de-referencing NULL
|
||
|
(two after an allocation failure, and one after a failed lookup).
|
||
|
|
||
|
Second, if ATS_ENABLE was already set, the device would not have got
|
||
|
added to the ats_devices list, potentially resulting in
|
||
|
dev_invalidate_iotlb() doing an incomplete job.
|
||
|
|
||
|
Signed-off-by: Jan Beulich <jbeulich@novell.com>
|
||
|
|
||
|
Index: xen-4.0.2-testing/xen/drivers/passthrough/vtd/x86/ats.c
|
||
|
===================================================================
|
||
|
--- xen-4.0.2-testing.orig/xen/drivers/passthrough/vtd/x86/ats.c
|
||
|
+++ xen-4.0.2-testing/xen/drivers/passthrough/vtd/x86/ats.c
|
||
|
@@ -91,6 +91,9 @@ int ats_device(int seg, int bus, int dev
|
||
|
return 0;
|
||
|
|
||
|
pdev = pci_get_pdev(bus, devfn);
|
||
|
+ if ( !pdev )
|
||
|
+ return 0;
|
||
|
+
|
||
|
drhd = acpi_find_matched_drhd_unit(pdev);
|
||
|
if ( !drhd )
|
||
|
return 0;
|
||
|
@@ -108,6 +111,8 @@ int ats_device(int seg, int bus, int dev
|
||
|
if ( pos && (ats_drhd == NULL) )
|
||
|
{
|
||
|
new_drhd = xmalloc(struct acpi_drhd_unit);
|
||
|
+ if ( !new_drhd )
|
||
|
+ return 0;
|
||
|
memcpy(new_drhd, drhd, sizeof(struct acpi_drhd_unit));
|
||
|
list_add_tail(&new_drhd->list, &ats_dev_drhd_units);
|
||
|
}
|
||
|
@@ -116,9 +121,8 @@ int ats_device(int seg, int bus, int dev
|
||
|
|
||
|
int enable_ats_device(int seg, int bus, int devfn)
|
||
|
{
|
||
|
- struct pci_ats_dev *pdev;
|
||
|
+ struct pci_ats_dev *pdev = NULL;
|
||
|
u32 value;
|
||
|
- u16 queue_depth;
|
||
|
int pos;
|
||
|
|
||
|
if ( !acpi_find_matched_atsr_unit(bus, devfn) )
|
||
|
@@ -142,26 +146,43 @@ int enable_ats_device(int seg, int bus,
|
||
|
|
||
|
/* 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_CAP);
|
||
|
- queue_depth = value & ATS_QUEUE_DEPTH_MASK;
|
||
|
-
|
||
|
- value = pci_conf_read16(bus, PCI_SLOT(devfn),
|
||
|
PCI_FUNC(devfn), pos + ATS_REG_CTL);
|
||
|
if ( value & ATS_ENABLE )
|
||
|
- return 0;
|
||
|
+ {
|
||
|
+ list_for_each_entry ( pdev, &ats_devices, list )
|
||
|
+ {
|
||
|
+ if ( pdev->bus == bus && pdev->devfn == devfn )
|
||
|
+ {
|
||
|
+ pos = 0;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if ( pos )
|
||
|
+ pdev = xmalloc(struct pci_ats_dev);
|
||
|
+ if ( !pdev )
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ if ( !(value & ATS_ENABLE) )
|
||
|
+ {
|
||
|
+ value |= ATS_ENABLE;
|
||
|
+ pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
||
|
+ pos + ATS_REG_CTL, value);
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( pos )
|
||
|
+ {
|
||
|
+ pdev->bus = bus;
|
||
|
+ pdev->devfn = devfn;
|
||
|
+ value = pci_conf_read16(bus, PCI_SLOT(devfn),
|
||
|
+ PCI_FUNC(devfn), pos + ATS_REG_CAP);
|
||
|
+ pdev->ats_queue_depth = value & ATS_QUEUE_DEPTH_MASK;
|
||
|
+ list_add(&pdev->list, &ats_devices);
|
||
|
+ }
|
||
|
|
||
|
- value |= ATS_ENABLE;
|
||
|
- pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
||
|
- pos + ATS_REG_CTL, value);
|
||
|
-
|
||
|
- pdev = xmalloc(struct pci_ats_dev);
|
||
|
- pdev->bus = bus;
|
||
|
- 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));
|
||
|
+ dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS %s enabled\n",
|
||
|
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos ? "is" : "was");
|
||
|
|
||
|
return pos;
|
||
|
}
|