]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommu: Warn on premature unblock during DMA aliased sibling reset
authorNicolin Chen <nicolinc@nvidia.com>
Sat, 25 Apr 2026 01:15:27 +0000 (18:15 -0700)
committerJoerg Roedel <joerg.roedel@amd.com>
Mon, 11 May 2026 08:12:45 +0000 (10:12 +0200)
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: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
drivers/iommu/iommu.c

index 845284a9eeaf4e52894032ba426e779e3820031b..e7bd28cc77eeb45d316448b1e12b5e352869b4f1 100644 (file)
@@ -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
         *