Commit 15dd29ca authored by Nicolin Chen's avatar Nicolin Chen Committed by Joerg Roedel
Browse files

iommu: Warn on premature unblock during DMA aliased sibling reset



When two aliased siblings are in the same iommu_group, they might share the
same RID. The reset functions don't support this case, though it is unclear
whether there is a real case of having an ATS capable device on a PCI/PCI-X
bus.

Theoretically, however, if two aliased devices are resetting concurrently,
one might be unblocked prematurely in the middle of the reset by the other
sibling who completes the reset first.

This isn't a regression from this series but it's better to spit a warning,
so we can know if such use case is common enough for us to make subsequent
patches for its coverage.

Signed-off-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Signed-off-by: default avatarJoerg Roedel <joerg.roedel@amd.com>
parent 5474e6e1
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -4105,6 +4105,41 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
}
EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_prepare);

static int __group_device_cmp_dma_alias(struct pci_dev *dev, u16 alias,
					void *data)
{
	return alias == *(u16 *)data;
}

static int group_device_cmp_dma_alias(struct pci_dev *dev, u16 alias,
				      void *data)
{
	return pci_for_each_dma_alias(data, __group_device_cmp_dma_alias,
				      &alias);
}

static bool group_device_dma_alias_is_blocked(struct iommu_group *group,
					      struct group_device *gdev)
{
	struct group_device *sibling;

	lockdep_assert_held(&group->mutex);

	if (!dev_is_pci(gdev->dev))
		return false;

	for_each_group_device(group, sibling) {
		if (sibling == gdev || !sibling->blocked ||
		    !dev_is_pci(sibling->dev))
			continue;
		if (pci_for_each_dma_alias(to_pci_dev(gdev->dev),
					   group_device_cmp_dma_alias,
					   to_pci_dev(sibling->dev)))
			return true;
	}
	return false;
}

/**
 * pci_dev_reset_iommu_done() - Restore IOMMU after a PCI device reset is done
 * @pdev: PCI device that has finished a reset routine
@@ -4144,6 +4179,20 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
	if (WARN_ON(!group->blocking_domain))
		return;

	if (group_device_dma_alias_is_blocked(group, gdev)) {
		/*
		 * FIXME: DMA aliased devices share the same RID, which would be
		 * convoluted to handle, as "gdev->blocked" is not sufficient:
		 *  - "blocked" state is effectively shared across these devices
		 *  - if the core skipped the blocking on the second device, the
		 *    IOMMU driver's attachment state would diverge from the HW
		 *    state
		 * For now, just warn and see whether real ATS use cases hit it.
		 */
		pci_warn(pdev,
			 "DMA-aliased sibling may be prematurely unblocked\n");
	}

	/*
	 * Re-attach RID domain back to group->domain
	 *