Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 276dbf99 authored by David Woodhouse's avatar David Woodhouse
Browse files

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: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 924b6231
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -180,6 +180,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
	dmaru->hdr = header;
	drhd = (struct acpi_dmar_hardware_unit *)header;
	dmaru->reg_base_addr = drhd->address;
	dmaru->segment = drhd->segment;
	dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */

	ret = alloc_iommu(dmaru);
+48 −28
Original line number Diff line number Diff line
@@ -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,7 +1438,9 @@ 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,
		ret = domain_context_mapping_one(domain,
						 pci_domain_nr(parent->bus),
						 parent->bus->number,
						 parent->devfn);
		if (ret)
			return ret;
@@ -1443,10 +1448,13 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev)
	}
	if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
		return domain_context_mapping_one(domain,
					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 */
@@ -1477,11 +1485,11 @@ static int domain_context_mapped(struct pci_dev *pdev)
		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 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
	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;
@@ -2844,8 +2860,8 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
			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;

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