Introduce a new field to mark devices as broken: having it set prevents
the device from being assigned to guests. Use the field in order to mark
ATS devices that have failed a flush when using VT-d as broken, thus
preventing them to be assigned to any guest.
This allows the device IOMMU context entry to be cleaned up properly, as
calling _pci_hide_device will just change the ownership of the device,
but the IOMMU context entry of the device would be left as-is. It would
also leak a VT-d Domain ID if using one, as removing the device from
its previous owner will allow releasing the IOMMU DID used by the device
without having cleaned up the context entry.
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
xfree(pdev);
}
-static void _pci_hide_device(struct pci_dev *pdev)
+static void __init _pci_hide_device(struct pci_dev *pdev)
{
if ( pdev->domain )
return;
ASSERT(pdev && (pdev->domain == hardware_domain ||
pdev->domain == dom_io));
+ /* Do not allow broken devices to be assigned to guests. */
+ rc = -EBADF;
+ if ( pdev->broken && d != hardware_domain && d != dom_io )
+ goto done;
+
rc = pdev_msix_assign(d, pdev);
if ( rc )
goto done;
return;
}
- list_del(&pdev->domain_list);
- pdev->domain = NULL;
- _pci_hide_device(pdev);
+ pdev->broken = true;
if ( !d->is_shutting_down && printk_ratelimit() )
printk(XENLOG_ERR "dom%d: ATS device %pp flush failed\n",
ASSERT(iommu->qinval_maddr);
rc = queue_invalidate_wait(iommu, 0, 1, 1, 1);
- if ( rc == -ETIMEDOUT )
+ if ( rc == -ETIMEDOUT && !pdev->broken )
{
struct domain *d = rcu_lock_domain_by_id(did_to_domain_id(iommu, did));
iommu_dev_iotlb_flush_timeout(d, pdev);
rcu_unlock_domain(d);
}
+ else if ( rc == -ETIMEDOUT )
+ /*
+ * The device is already marked as broken, ignore the error in order to
+ * allow {de,}assign to succeed.
+ */
+ rc = 0;
return rc;
}
/* Device with errata, ignore the BARs. */
bool ignore_bars;
+ /* Device misbehaving, prevent assigning it to guests. */
+ bool broken;
+
enum pdev_type {
DEV_TYPE_PCI_UNKNOWN,
DEV_TYPE_PCIe_ENDPOINT,