xen/18934-vtd-PCI-X-dev-assign.patch

213 lines
7.7 KiB
Diff

# HG changeset patch
# User Keir Fraser <keir.fraser@citrix.com>
# Date 1229694124 0
# Node ID d238101c1832ba178bfc00a20b461fcebe21d5df
# Parent 8c35da364ab39605839869d8eb0ac9b831c370f0
VT-d: Fix PCI-X device assignment
When assign PCI device, current code just map its bridge and its
secondary bus number and devfn 0. It doesn't work for PCI-x device
assignment, because the request may be the source-id in the original
PCI-X transaction or the source-id provided by the bridge. It needs to
map the device itself, and its upstream bridges till PCIe-to-PCI/PCI-x
bridge.
In addition, add description for DEV_TYPE_PCIe_BRIDGE and
DEV_TYPE_PCI_BRIDGE for understandability.
Signed-off-by: Weidong Han <weidong.han@intel.com>
# HG changeset patch
# User Keir Fraser <keir.fraser@citrix.com>
# Date 1231154002 0
# Node ID b3a9bc72624166a230da74c498154ae2cb45eacc
# Parent 9cc632cc6d400685679671b6bbc58dfe4c5e287e
vtd: avoid redundant context mapping
After changeset 18934 (VT-d: Fix PCI-X device assignment), my assigned
PCI E1000 NIC doesn't work in guest.
The NIC is 03:00.0. Its parent bridge is: 00:1e.0.
In domain_context_mapping():
case DEV_TYPE_PCI:
After we domain_context_mapping_one() 03:00.0 and 00:1e.0, the
'secbus' is 3 and 'bus' is 0, so we domain_context_mapping_one()
03:00.0 again -- this redundant invocation returns -EINVAL because
we have created the mapping but haven't changed pdev->domain from
Dom0 to a new domain at this time and eventually the
XEN_DOMCTL_assign_device hypercall returns a failure.
The attached patch detects this case and avoids the redundant
invocation.
Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -1155,8 +1155,8 @@ static int domain_context_mapping_one(
enum {
DEV_TYPE_PCIe_ENDPOINT,
- DEV_TYPE_PCIe_BRIDGE,
- DEV_TYPE_PCI_BRIDGE,
+ DEV_TYPE_PCIe_BRIDGE, // PCIe root port, switch
+ DEV_TYPE_PCI_BRIDGE, // PCIe-to-PCI/PCIx bridge, PCI-to-PCI bridge
DEV_TYPE_PCI,
};
@@ -1170,7 +1170,8 @@ int pdev_type(u8 bus, u8 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);
+ 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);
@@ -1219,9 +1220,9 @@ static int domain_context_mapping(struct
{
struct acpi_drhd_unit *drhd;
int ret = 0;
- u16 sec_bus, sub_bus, ob, odf;
+ u16 sec_bus, sub_bus;
u32 type;
- u8 secbus;
+ u8 secbus, secdevfn;
drhd = acpi_find_matched_drhd_unit(bus, devfn);
if ( !drhd )
@@ -1231,15 +1232,13 @@ 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);
- /*dmar_scope_add_buses(&drhd->scope, sec_bus, sub_bus);*/
-
- if ( type == DEV_TYPE_PCIe_BRIDGE )
- break;
for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
{
@@ -1258,26 +1257,28 @@ static int domain_context_mapping(struct
case DEV_TYPE_PCI:
gdprintk(XENLOG_INFO VTDPREFIX,
- "domain_context_mapping:PCI: bdf = %x:%x.%x\n",
+ "domain_context_mapping:PCI: bdf = %x:%x.%x\n",
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
- ob = bus; odf = devfn;
- if ( !find_pcie_endpoint(&bus, &devfn, &secbus) )
+ ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
+ if ( ret )
+ break;
+
+ secbus = bus;
+ secdevfn = devfn;
+ /* dependent devices mapping */
+ while ( bus2bridge[bus].map )
{
- gdprintk(XENLOG_WARNING VTDPREFIX,
- "domain_context_mapping:invalid\n");
- break;
+ secbus = bus;
+ secdevfn = devfn;
+ devfn = bus2bridge[bus].devfn;
+ bus = bus2bridge[bus].bus;
+ ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
+ if ( ret )
+ return ret;
}
- if ( ob != bus || odf != devfn )
- gdprintk(XENLOG_INFO VTDPREFIX,
- "domain_context_mapping:map: "
- "bdf = %x:%x.%x -> %x:%x.%x\n",
- ob, PCI_SLOT(odf), PCI_FUNC(odf),
- bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
-
- ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
- if ( secbus != bus )
+ 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
@@ -1285,7 +1286,7 @@ static int domain_context_mapping(struct
* these scanarios is not particularly well documented
* anywhere.
*/
- domain_context_mapping_one(domain, drhd->iommu, secbus, 0);
+ ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0);
break;
default:
@@ -1339,10 +1340,9 @@ static int domain_context_unmap_one(
static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn)
{
struct acpi_drhd_unit *drhd;
- u16 sec_bus, sub_bus;
int ret = 0;
u32 type;
- u8 secbus;
+ u8 secbus, secdevfn;
drhd = acpi_find_matched_drhd_unit(bus, devfn);
if ( !drhd )
@@ -1353,24 +1353,39 @@ static int domain_context_unmap(struct d
{
case DEV_TYPE_PCIe_BRIDGE:
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);
- /*dmar_scope_remove_buses(&drhd->scope, sec_bus, sub_bus);*/
- if ( DEV_TYPE_PCI_BRIDGE )
- ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
break;
case DEV_TYPE_PCIe_ENDPOINT:
+ gdprintk(XENLOG_INFO VTDPREFIX,
+ "domain_context_unmap:PCIe: bdf = %x:%x.%x\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
break;
case DEV_TYPE_PCI:
- if ( find_pcie_endpoint(&bus, &devfn, &secbus) )
+ gdprintk(XENLOG_INFO VTDPREFIX,
+ "domain_context_unmap:PCI: bdf = %x:%x.%x\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
+ 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;
ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
- if ( bus != secbus )
- domain_context_unmap_one(domain, drhd->iommu, secbus, 0);
+ if ( ret )
+ return ret;
+ }
+
+ if ( (secbus != bus) && (secdevfn != 0) )
+ ret = domain_context_unmap_one(domain, drhd->iommu, secbus, 0);
break;
default: