intel-iommu: Handle PCI domains appropriately.

We were comparing {bus,devfn} and assuming that a match meant it was the
same device. It doesn't -- the same {bus,devfn} can exist in
multiple PCI domains. Include domain number in device identification
(and call it 'segment' in most places, because there's already a lot of
references to 'domain' which means something else, and this code is
infected with ACPI thinking already).

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
David Woodhouse 2009-04-04 01:45:37 +01:00
parent 924b6231ed
commit 276dbf9970
3 changed files with 50 additions and 28 deletions

View file

@ -180,6 +180,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
dmaru->hdr = header; dmaru->hdr = header;
drhd = (struct acpi_dmar_hardware_unit *)header; drhd = (struct acpi_dmar_hardware_unit *)header;
dmaru->reg_base_addr = drhd->address; dmaru->reg_base_addr = drhd->address;
dmaru->segment = drhd->segment;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
ret = alloc_iommu(dmaru); ret = alloc_iommu(dmaru);

View file

@ -248,7 +248,8 @@ struct dmar_domain {
struct device_domain_info { struct device_domain_info {
struct list_head link; /* link to domain siblings */ struct list_head link; /* link to domain siblings */
struct list_head global; /* link to global list */ 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 */ u8 devfn; /* PCI devfn number */
struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
struct dmar_domain *domain; /* pointer to domain */ 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); 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; struct dmar_drhd_unit *drhd = NULL;
int i; int i;
@ -476,6 +477,8 @@ static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn)
for_each_drhd_unit(drhd) { for_each_drhd_unit(drhd) {
if (drhd->ignored) if (drhd->ignored)
continue; continue;
if (segment != drhd->segment)
continue;
for (i = 0; i < drhd->devices_cnt; i++) { for (i = 0; i < drhd->devices_cnt; i++) {
if (drhd->devices[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, static int domain_context_mapping_one(struct dmar_domain *domain,
u8 bus, u8 devfn) int segment, u8 bus, u8 devfn)
{ {
struct context_entry *context; struct context_entry *context;
unsigned long flags; unsigned long flags;
@ -1333,7 +1336,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
BUG_ON(!domain->pgd); BUG_ON(!domain->pgd);
iommu = device_to_iommu(bus, devfn); iommu = device_to_iommu(segment, bus, devfn);
if (!iommu) if (!iommu)
return -ENODEV; return -ENODEV;
@ -1423,8 +1426,8 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev)
int ret; int ret;
struct pci_dev *tmp, *parent; struct pci_dev *tmp, *parent;
ret = domain_context_mapping_one(domain, pdev->bus->number, ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus),
pdev->devfn); pdev->bus->number, pdev->devfn);
if (ret) if (ret)
return 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 */ /* Secondary interface's bus number and devfn 0 */
parent = pdev->bus->self; parent = pdev->bus->self;
while (parent != tmp) { while (parent != tmp) {
ret = domain_context_mapping_one(domain, parent->bus->number, ret = domain_context_mapping_one(domain,
parent->devfn); pci_domain_nr(parent->bus),
parent->bus->number,
parent->devfn);
if (ret) if (ret)
return ret; return ret;
parent = parent->bus->self; parent = parent->bus->self;
} }
if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
return domain_context_mapping_one(domain, 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 */ else /* this is a legacy PCI bridge */
return domain_context_mapping_one(domain, 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) 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 pci_dev *tmp, *parent;
struct intel_iommu *iommu; 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) if (!iommu)
return -ENODEV; return -ENODEV;
ret = device_context_mapped(iommu, ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn);
pdev->bus->number, pdev->devfn);
if (!ret) if (!ret)
return ret; return ret;
/* dependent device mapping */ /* dependent device mapping */
@ -1471,17 +1479,17 @@ static int domain_context_mapped(struct pci_dev *pdev)
parent = pdev->bus->self; parent = pdev->bus->self;
while (parent != tmp) { while (parent != tmp) {
ret = device_context_mapped(iommu, parent->bus->number, ret = device_context_mapped(iommu, parent->bus->number,
parent->devfn); parent->devfn);
if (!ret) if (!ret)
return ret; return ret;
parent = parent->bus->self; parent = parent->bus->self;
} }
if (tmp->is_pcie) if (tmp->is_pcie)
return device_context_mapped(iommu, return device_context_mapped(iommu, tmp->subordinate->number,
tmp->subordinate->number, 0); 0);
else else
return device_context_mapped(iommu, return device_context_mapped(iommu, tmp->bus->number,
tmp->bus->number, tmp->devfn); tmp->devfn);
} }
static int static int
@ -1548,7 +1556,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
info->dev->dev.archdata.iommu = NULL; info->dev->dev.archdata.iommu = NULL;
spin_unlock_irqrestore(&device_domain_lock, flags); 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); iommu_detach_dev(iommu, info->bus, info->devfn);
free_devinfo_mem(info); 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; struct pci_dev *dev_tmp;
unsigned long flags; unsigned long flags;
int bus = 0, devfn = 0; int bus = 0, devfn = 0;
int segment;
domain = find_domain(pdev); domain = find_domain(pdev);
if (domain) if (domain)
return domain; return domain;
segment = pci_domain_nr(pdev->bus);
dev_tmp = pci_find_upstream_pcie_bridge(pdev); dev_tmp = pci_find_upstream_pcie_bridge(pdev);
if (dev_tmp) { if (dev_tmp) {
if (dev_tmp->is_pcie) { 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); spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry(info, &device_domain_list, global) { 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; found = info->domain;
break; break;
} }
@ -1637,6 +1649,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
domain_exit(domain); domain_exit(domain);
goto error; goto error;
} }
info->segment = segment;
info->bus = bus; info->bus = bus;
info->devfn = devfn; info->devfn = devfn;
info->dev = NULL; info->dev = NULL;
@ -1648,7 +1661,8 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
found = NULL; found = NULL;
spin_lock_irqsave(&device_domain_lock, flags); spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry(tmp, &device_domain_list, global) { 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; found = tmp->domain;
break; break;
} }
@ -1668,6 +1682,7 @@ found_domain:
info = alloc_devinfo_mem(); info = alloc_devinfo_mem();
if (!info) if (!info)
goto error; goto error;
info->segment = segment;
info->bus = pdev->bus->number; info->bus = pdev->bus->number;
info->devfn = pdev->devfn; info->devfn = pdev->devfn;
info->dev = pdev; info->dev = pdev;
@ -2808,6 +2823,7 @@ static int vm_domain_add_dev_info(struct dmar_domain *domain,
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
info->segment = pci_domain_nr(pdev->bus);
info->bus = pdev->bus->number; info->bus = pdev->bus->number;
info->devfn = pdev->devfn; info->devfn = pdev->devfn;
info->dev = pdev; info->dev = pdev;
@ -2837,15 +2853,15 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
parent = pdev->bus->self; parent = pdev->bus->self;
while (parent != tmp) { while (parent != tmp) {
iommu_detach_dev(iommu, parent->bus->number, iommu_detach_dev(iommu, parent->bus->number,
parent->devfn); parent->devfn);
parent = parent->bus->self; parent = parent->bus->self;
} }
if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
iommu_detach_dev(iommu, iommu_detach_dev(iommu,
tmp->subordinate->number, 0); tmp->subordinate->number, 0);
else /* this is a legacy PCI bridge */ else /* this is a legacy PCI bridge */
iommu_detach_dev(iommu, iommu_detach_dev(iommu, tmp->bus->number,
tmp->bus->number, tmp->devfn); tmp->devfn);
} }
} }
@ -2858,13 +2874,15 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain,
int found = 0; int found = 0;
struct list_head *entry, *tmp; 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) if (!iommu)
return; return;
spin_lock_irqsave(&device_domain_lock, flags); spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_safe(entry, tmp, &domain->devices) { list_for_each_safe(entry, tmp, &domain->devices) {
info = list_entry(entry, struct device_domain_info, link); 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 && if (info->bus == pdev->bus->number &&
info->devfn == pdev->devfn) { info->devfn == pdev->devfn) {
list_del(&info->link); 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 * owned by this domain, clear this iommu in iommu_bmp
* update iommu count and coherency * 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; 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); 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_dev(iommu, info->bus, info->devfn);
iommu_detach_dependent_devices(iommu, info->dev); 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) if (!iommu)
return -ENODEV; return -ENODEV;

View file

@ -34,6 +34,7 @@ struct dmar_drhd_unit {
u64 reg_base_addr; /* register base address*/ u64 reg_base_addr; /* register base address*/
struct pci_dev **devices; /* target device array */ struct pci_dev **devices; /* target device array */
int devices_cnt; /* target device count */ int devices_cnt; /* target device count */
u16 segment; /* PCI domain */
u8 ignored:1; /* ignore drhd */ u8 ignored:1; /* ignore drhd */
u8 include_all:1; u8 include_all:1;
struct intel_iommu *iommu; struct intel_iommu *iommu;