|
@@ -248,7 +248,8 @@ struct dmar_domain {
|
|
|
struct device_domain_info {
|
|
|
struct list_head link; /* link to domain siblings */
|
|
|
struct list_head global; /* link to global list */
|
|
|
- u8 bus; /* PCI bus numer */
|
|
|
+ int segment; /* PCI domain */
|
|
|
+ u8 bus; /* PCI bus number */
|
|
|
u8 devfn; /* PCI devfn number */
|
|
|
struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
|
|
|
struct dmar_domain *domain; /* pointer to domain */
|
|
@@ -468,7 +469,7 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
|
|
|
domain_update_iommu_snooping(domain);
|
|
|
}
|
|
|
|
|
|
-static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn)
|
|
|
+static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
|
|
|
{
|
|
|
struct dmar_drhd_unit *drhd = NULL;
|
|
|
int i;
|
|
@@ -476,6 +477,8 @@ static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn)
|
|
|
for_each_drhd_unit(drhd) {
|
|
|
if (drhd->ignored)
|
|
|
continue;
|
|
|
+ if (segment != drhd->segment)
|
|
|
+ continue;
|
|
|
|
|
|
for (i = 0; i < drhd->devices_cnt; i++) {
|
|
|
if (drhd->devices[i] &&
|
|
@@ -1318,7 +1321,7 @@ static void domain_exit(struct dmar_domain *domain)
|
|
|
}
|
|
|
|
|
|
static int domain_context_mapping_one(struct dmar_domain *domain,
|
|
|
- u8 bus, u8 devfn)
|
|
|
+ int segment, u8 bus, u8 devfn)
|
|
|
{
|
|
|
struct context_entry *context;
|
|
|
unsigned long flags;
|
|
@@ -1333,7 +1336,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
|
|
|
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
|
|
BUG_ON(!domain->pgd);
|
|
|
|
|
|
- iommu = device_to_iommu(bus, devfn);
|
|
|
+ iommu = device_to_iommu(segment, bus, devfn);
|
|
|
if (!iommu)
|
|
|
return -ENODEV;
|
|
|
|
|
@@ -1423,8 +1426,8 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev)
|
|
|
int ret;
|
|
|
struct pci_dev *tmp, *parent;
|
|
|
|
|
|
- ret = domain_context_mapping_one(domain, pdev->bus->number,
|
|
|
- pdev->devfn);
|
|
|
+ ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus),
|
|
|
+ pdev->bus->number, pdev->devfn);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
@@ -1435,18 +1438,23 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev)
|
|
|
/* Secondary interface's bus number and devfn 0 */
|
|
|
parent = pdev->bus->self;
|
|
|
while (parent != tmp) {
|
|
|
- ret = domain_context_mapping_one(domain, parent->bus->number,
|
|
|
- parent->devfn);
|
|
|
+ ret = domain_context_mapping_one(domain,
|
|
|
+ pci_domain_nr(parent->bus),
|
|
|
+ parent->bus->number,
|
|
|
+ parent->devfn);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
parent = parent->bus->self;
|
|
|
}
|
|
|
if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
|
|
|
return domain_context_mapping_one(domain,
|
|
|
- tmp->subordinate->number, 0);
|
|
|
+ pci_domain_nr(tmp->subordinate),
|
|
|
+ tmp->subordinate->number, 0);
|
|
|
else /* this is a legacy PCI bridge */
|
|
|
return domain_context_mapping_one(domain,
|
|
|
- tmp->bus->number, tmp->devfn);
|
|
|
+ pci_domain_nr(tmp->bus),
|
|
|
+ tmp->bus->number,
|
|
|
+ tmp->devfn);
|
|
|
}
|
|
|
|
|
|
static int domain_context_mapped(struct pci_dev *pdev)
|
|
@@ -1455,12 +1463,12 @@ static int domain_context_mapped(struct pci_dev *pdev)
|
|
|
struct pci_dev *tmp, *parent;
|
|
|
struct intel_iommu *iommu;
|
|
|
|
|
|
- iommu = device_to_iommu(pdev->bus->number, pdev->devfn);
|
|
|
+ iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
|
|
|
+ pdev->devfn);
|
|
|
if (!iommu)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- ret = device_context_mapped(iommu,
|
|
|
- pdev->bus->number, pdev->devfn);
|
|
|
+ ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn);
|
|
|
if (!ret)
|
|
|
return ret;
|
|
|
/* dependent device mapping */
|
|
@@ -1471,17 +1479,17 @@ static int domain_context_mapped(struct pci_dev *pdev)
|
|
|
parent = pdev->bus->self;
|
|
|
while (parent != tmp) {
|
|
|
ret = device_context_mapped(iommu, parent->bus->number,
|
|
|
- parent->devfn);
|
|
|
+ parent->devfn);
|
|
|
if (!ret)
|
|
|
return ret;
|
|
|
parent = parent->bus->self;
|
|
|
}
|
|
|
if (tmp->is_pcie)
|
|
|
- return device_context_mapped(iommu,
|
|
|
- tmp->subordinate->number, 0);
|
|
|
+ return device_context_mapped(iommu, tmp->subordinate->number,
|
|
|
+ 0);
|
|
|
else
|
|
|
- return device_context_mapped(iommu,
|
|
|
- tmp->bus->number, tmp->devfn);
|
|
|
+ return device_context_mapped(iommu, tmp->bus->number,
|
|
|
+ tmp->devfn);
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -1548,7 +1556,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
|
|
|
info->dev->dev.archdata.iommu = NULL;
|
|
|
spin_unlock_irqrestore(&device_domain_lock, flags);
|
|
|
|
|
|
- iommu = device_to_iommu(info->bus, info->devfn);
|
|
|
+ iommu = device_to_iommu(info->segment, info->bus, info->devfn);
|
|
|
iommu_detach_dev(iommu, info->bus, info->devfn);
|
|
|
free_devinfo_mem(info);
|
|
|
|
|
@@ -1583,11 +1591,14 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
|
|
|
struct pci_dev *dev_tmp;
|
|
|
unsigned long flags;
|
|
|
int bus = 0, devfn = 0;
|
|
|
+ int segment;
|
|
|
|
|
|
domain = find_domain(pdev);
|
|
|
if (domain)
|
|
|
return domain;
|
|
|
|
|
|
+ segment = pci_domain_nr(pdev->bus);
|
|
|
+
|
|
|
dev_tmp = pci_find_upstream_pcie_bridge(pdev);
|
|
|
if (dev_tmp) {
|
|
|
if (dev_tmp->is_pcie) {
|
|
@@ -1599,7 +1610,8 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
|
|
|
}
|
|
|
spin_lock_irqsave(&device_domain_lock, flags);
|
|
|
list_for_each_entry(info, &device_domain_list, global) {
|
|
|
- if (info->bus == bus && info->devfn == devfn) {
|
|
|
+ if (info->segment == segment &&
|
|
|
+ info->bus == bus && info->devfn == devfn) {
|
|
|
found = info->domain;
|
|
|
break;
|
|
|
}
|
|
@@ -1637,6 +1649,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
|
|
|
domain_exit(domain);
|
|
|
goto error;
|
|
|
}
|
|
|
+ info->segment = segment;
|
|
|
info->bus = bus;
|
|
|
info->devfn = devfn;
|
|
|
info->dev = NULL;
|
|
@@ -1648,7 +1661,8 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
|
|
|
found = NULL;
|
|
|
spin_lock_irqsave(&device_domain_lock, flags);
|
|
|
list_for_each_entry(tmp, &device_domain_list, global) {
|
|
|
- if (tmp->bus == bus && tmp->devfn == devfn) {
|
|
|
+ if (tmp->segment == segment &&
|
|
|
+ tmp->bus == bus && tmp->devfn == devfn) {
|
|
|
found = tmp->domain;
|
|
|
break;
|
|
|
}
|
|
@@ -1668,6 +1682,7 @@ found_domain:
|
|
|
info = alloc_devinfo_mem();
|
|
|
if (!info)
|
|
|
goto error;
|
|
|
+ info->segment = segment;
|
|
|
info->bus = pdev->bus->number;
|
|
|
info->devfn = pdev->devfn;
|
|
|
info->dev = pdev;
|
|
@@ -2808,6 +2823,7 @@ static int vm_domain_add_dev_info(struct dmar_domain *domain,
|
|
|
if (!info)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ info->segment = pci_domain_nr(pdev->bus);
|
|
|
info->bus = pdev->bus->number;
|
|
|
info->devfn = pdev->devfn;
|
|
|
info->dev = pdev;
|
|
@@ -2837,15 +2853,15 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
|
|
|
parent = pdev->bus->self;
|
|
|
while (parent != tmp) {
|
|
|
iommu_detach_dev(iommu, parent->bus->number,
|
|
|
- parent->devfn);
|
|
|
+ parent->devfn);
|
|
|
parent = parent->bus->self;
|
|
|
}
|
|
|
if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
|
|
|
iommu_detach_dev(iommu,
|
|
|
tmp->subordinate->number, 0);
|
|
|
else /* this is a legacy PCI bridge */
|
|
|
- iommu_detach_dev(iommu,
|
|
|
- tmp->bus->number, tmp->devfn);
|
|
|
+ iommu_detach_dev(iommu, tmp->bus->number,
|
|
|
+ tmp->devfn);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2858,13 +2874,15 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain,
|
|
|
int found = 0;
|
|
|
struct list_head *entry, *tmp;
|
|
|
|
|
|
- iommu = device_to_iommu(pdev->bus->number, pdev->devfn);
|
|
|
+ iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
|
|
|
+ pdev->devfn);
|
|
|
if (!iommu)
|
|
|
return;
|
|
|
|
|
|
spin_lock_irqsave(&device_domain_lock, flags);
|
|
|
list_for_each_safe(entry, tmp, &domain->devices) {
|
|
|
info = list_entry(entry, struct device_domain_info, link);
|
|
|
+ /* No need to compare PCI domain; it has to be the same */
|
|
|
if (info->bus == pdev->bus->number &&
|
|
|
info->devfn == pdev->devfn) {
|
|
|
list_del(&info->link);
|
|
@@ -2889,7 +2907,8 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain,
|
|
|
* owned by this domain, clear this iommu in iommu_bmp
|
|
|
* update iommu count and coherency
|
|
|
*/
|
|
|
- if (device_to_iommu(info->bus, info->devfn) == iommu)
|
|
|
+ if (iommu == device_to_iommu(info->segment, info->bus,
|
|
|
+ info->devfn))
|
|
|
found = 1;
|
|
|
}
|
|
|
|
|
@@ -2922,7 +2941,7 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain)
|
|
|
|
|
|
spin_unlock_irqrestore(&device_domain_lock, flags1);
|
|
|
|
|
|
- iommu = device_to_iommu(info->bus, info->devfn);
|
|
|
+ iommu = device_to_iommu(info->segment, info->bus, info->devfn);
|
|
|
iommu_detach_dev(iommu, info->bus, info->devfn);
|
|
|
iommu_detach_dependent_devices(iommu, info->dev);
|
|
|
|
|
@@ -3110,7 +3129,8 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- iommu = device_to_iommu(pdev->bus->number, pdev->devfn);
|
|
|
+ iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
|
|
|
+ pdev->devfn);
|
|
|
if (!iommu)
|
|
|
return -ENODEV;
|
|
|
|