169 lines
5.8 KiB
Diff
169 lines
5.8 KiB
Diff
References: bnc#787169
|
|
|
|
# HG changeset patch
|
|
# User Jan Beulich <jbeulich@suse.com>
|
|
# Date 1352709367 -3600
|
|
# Node ID fdb69dd527cd01a46f87efb380050559dcf12d37
|
|
# Parent 286ef4ced2164f4e9bf52fd0c52248182e69a6e6
|
|
IOMMU: don't immediately disable bus mastering on faults
|
|
|
|
Instead, give the owning domain at least a small opportunity of fixing
|
|
things up, and allow for rare faults to not bring down the device at
|
|
all.
|
|
|
|
Signed-off-by: Jan Beulich <jbeulich@suse.com>
|
|
Acked-by: Tim Deegan <tim@xen.org>
|
|
Acked-by: Dario Faggioli <dario.faggioli@citrix.com>
|
|
|
|
--- a/xen/drivers/passthrough/amd/iommu_init.c
|
|
+++ b/xen/drivers/passthrough/amd/iommu_init.c
|
|
@@ -564,7 +564,7 @@ static hw_irq_controller iommu_msi_type
|
|
|
|
static void parse_event_log_entry(struct amd_iommu *iommu, u32 entry[])
|
|
{
|
|
- u16 domain_id, device_id, bdf, cword;
|
|
+ u16 domain_id, device_id, bdf;
|
|
u32 code;
|
|
u64 *addr;
|
|
int count = 0;
|
|
@@ -615,18 +615,10 @@ static void parse_event_log_entry(struct
|
|
"fault address = 0x%"PRIx64"\n",
|
|
event_str[code-1], domain_id, device_id, *addr);
|
|
|
|
- /* Tell the device to stop DMAing; we can't rely on the guest to
|
|
- * control it for us. */
|
|
for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ )
|
|
if ( get_dma_requestor_id(iommu->seg, bdf) == device_id )
|
|
- {
|
|
- cword = pci_conf_read16(iommu->seg, PCI_BUS(bdf),
|
|
- PCI_SLOT(bdf), PCI_FUNC(bdf),
|
|
- PCI_COMMAND);
|
|
- pci_conf_write16(iommu->seg, PCI_BUS(bdf), PCI_SLOT(bdf),
|
|
- PCI_FUNC(bdf), PCI_COMMAND,
|
|
- cword & ~PCI_COMMAND_MASTER);
|
|
- }
|
|
+ pci_check_disable_device(iommu->seg, PCI_BUS(bdf),
|
|
+ PCI_DEVFN2(bdf));
|
|
}
|
|
else
|
|
{
|
|
--- a/xen/drivers/passthrough/iommu.c
|
|
+++ b/xen/drivers/passthrough/iommu.c
|
|
@@ -214,6 +214,7 @@ static int device_assigned(u16 seg, u8 b
|
|
static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn)
|
|
{
|
|
struct hvm_iommu *hd = domain_hvm_iommu(d);
|
|
+ struct pci_dev *pdev;
|
|
int rc = 0;
|
|
|
|
if ( !iommu_enabled || !hd->platform_ops )
|
|
@@ -227,6 +228,10 @@ static int assign_device(struct domain *
|
|
return -EXDEV;
|
|
|
|
spin_lock(&pcidevs_lock);
|
|
+ pdev = pci_get_pdev(seg, bus, devfn);
|
|
+ if ( pdev )
|
|
+ pdev->fault.count = 0;
|
|
+
|
|
if ( (rc = hd->platform_ops->assign_device(d, seg, bus, devfn)) )
|
|
goto done;
|
|
|
|
@@ -378,6 +383,8 @@ int deassign_device(struct domain *d, u1
|
|
return ret;
|
|
}
|
|
|
|
+ pdev->fault.count = 0;
|
|
+
|
|
if ( !has_arch_pdevs(d) && need_iommu(d) )
|
|
{
|
|
d->need_iommu = 0;
|
|
--- a/xen/drivers/passthrough/pci.c
|
|
+++ b/xen/drivers/passthrough/pci.c
|
|
@@ -637,6 +637,36 @@ int __init pci_device_detect(u16 seg, u8
|
|
return 1;
|
|
}
|
|
|
|
+void pci_check_disable_device(u16 seg, u8 bus, u8 devfn)
|
|
+{
|
|
+ struct pci_dev *pdev;
|
|
+ s_time_t now = NOW();
|
|
+ u16 cword;
|
|
+
|
|
+ spin_lock(&pcidevs_lock);
|
|
+ pdev = pci_get_pdev(seg, bus, devfn);
|
|
+ if ( pdev )
|
|
+ {
|
|
+ if ( now < pdev->fault.time ||
|
|
+ now - pdev->fault.time > MILLISECS(10) )
|
|
+ pdev->fault.count >>= 1;
|
|
+ pdev->fault.time = now;
|
|
+ if ( ++pdev->fault.count < PT_FAULT_THRESHOLD )
|
|
+ pdev = NULL;
|
|
+ }
|
|
+ spin_unlock(&pcidevs_lock);
|
|
+
|
|
+ if ( !pdev )
|
|
+ return;
|
|
+
|
|
+ /* Tell the device to stop DMAing; we can't rely on the guest to
|
|
+ * control it for us. */
|
|
+ cword = pci_conf_read16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
|
+ PCI_COMMAND);
|
|
+ pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
|
+ PCI_COMMAND, cword & ~PCI_COMMAND_MASTER);
|
|
+}
|
|
+
|
|
/*
|
|
* scan pci devices to add all existed PCI devices to alldevs_list,
|
|
* and setup pci hierarchy in array bus2bridge.
|
|
--- a/xen/drivers/passthrough/vtd/iommu.c
|
|
+++ b/xen/drivers/passthrough/vtd/iommu.c
|
|
@@ -936,7 +936,7 @@ static void __do_iommu_page_fault(struct
|
|
while (1)
|
|
{
|
|
u8 fault_reason;
|
|
- u16 source_id, cword;
|
|
+ u16 source_id;
|
|
u32 data;
|
|
u64 guest_addr;
|
|
int type;
|
|
@@ -969,14 +969,8 @@ static void __do_iommu_page_fault(struct
|
|
iommu_page_fault_do_one(iommu, type, fault_reason,
|
|
source_id, guest_addr);
|
|
|
|
- /* Tell the device to stop DMAing; we can't rely on the guest to
|
|
- * control it for us. */
|
|
- cword = pci_conf_read16(iommu->intel->drhd->segment,
|
|
- PCI_BUS(source_id), PCI_SLOT(source_id),
|
|
- PCI_FUNC(source_id), PCI_COMMAND);
|
|
- pci_conf_write16(iommu->intel->drhd->segment, PCI_BUS(source_id),
|
|
- PCI_SLOT(source_id), PCI_FUNC(source_id),
|
|
- PCI_COMMAND, cword & ~PCI_COMMAND_MASTER);
|
|
+ pci_check_disable_device(iommu->intel->drhd->segment,
|
|
+ PCI_BUS(source_id), PCI_DEVFN2(source_id));
|
|
|
|
fault_index++;
|
|
if ( fault_index > cap_num_fault_regs(iommu->cap) )
|
|
--- a/xen/include/xen/pci.h
|
|
+++ b/xen/include/xen/pci.h
|
|
@@ -64,6 +64,11 @@ struct pci_dev {
|
|
const u8 devfn;
|
|
struct pci_dev_info info;
|
|
struct arch_pci_dev arch;
|
|
+ struct {
|
|
+ s_time_t time;
|
|
+ unsigned int count;
|
|
+#define PT_FAULT_THRESHOLD 10
|
|
+ } fault;
|
|
u64 vf_rlen[6];
|
|
};
|
|
|
|
@@ -106,6 +111,7 @@ void arch_pci_ro_device(int seg, int bdf
|
|
struct pci_dev *pci_get_pdev(int seg, int bus, int devfn);
|
|
struct pci_dev *pci_get_pdev_by_domain(
|
|
struct domain *, int seg, int bus, int devfn);
|
|
+void pci_check_disable_device(u16 seg, u8 bus, u8 devfn);
|
|
|
|
uint8_t pci_conf_read8(
|
|
unsigned int seg, unsigned int bus, unsigned int dev, unsigned int func,
|