[PATCH v5 05/18] iommu: Pass in reset result to pci_dev_reset_iommu_done()

Baolu Lu baolu.lu at linux.intel.com
Mon Jul 13 04:48:53 PDT 2026


On 7/3/2026 12:06 PM, Nicolin Chen wrote:
> IOMMU drivers handle ATC cache maintenance. They may encounter ATC-related
> errors (e.g., ATC invalidation timeout), indicating that the ATC cache may
> have stale entries that can corrupt the memory. In this case, IOMMU driver
> has no choice but to block the device's ATS function and wait for a device
> recovery.
> 
> The pci_dev_reset_iommu_done() called at the end of a reset function could
> serve as a reliable signal to the IOMMU subsystem that the physical device
> cache is completely clean. However, the function is called unconditionally
> even if the reset operation had actually failed, which would re-attach the
> faulty device back to a normal translation domain. And this will leave the
> system highly exposed, creating vulnerabilities for data corruption:
>      IOMMU blocks RID/ATS
>      pci_reset_function():
>          pci_dev_reset_iommu_prepare(); // Block RID/ATS
>          __reset(); // Failed (ATC is still stale)
>          pci_dev_reset_iommu_done(); // Unblock RID/ATS (ah-ha)
> 
> Instead, pass in @reset_result to pci_dev_reset_iommu_done() from callers:
>      IOMMU blocks RID/ATS
>      pci_reset_function():
>          pci_dev_reset_iommu_prepare(); // Block RID/ATS
>          rc = __reset();
>          pci_dev_reset_iommu_done(rc); // Unblock or quarantine
> 
> On a successful reset, done() restores the device to its RID/PASID domains
> and decrements group->recovery_cnt. On failure, the device remains blocked,
> and concurrent domain attachment will be rejected until a successful reset.
> 
> Note: -ENOTTY is overloaded with different meanings by PCI reset functions.
> Some of them indicate "reset was not attempted", while others indicate "try
> the next reset method and the current method failed". IOMMU that must react
> these two outcomes separately has no choice but to keep the device blocked
> on -ENOTTY as well. Leave an inline FIXME and warning.
> 
> This introduces a new situation where a blocked device is being unplugged.
> Decrement the group->recovery_cnt accordingly.
> 
> Suggested-by: Kevin Tian<kevin.tian at intel.com>
> Signed-off-by: Nicolin Chen<nicolinc at nvidia.com>
> ---
>   include/linux/iommu.h  |  5 ++--
>   drivers/iommu/iommu.c  | 62 ++++++++++++++++++++++++++++++++++++++++--
>   drivers/pci/pci-acpi.c |  2 +-
>   drivers/pci/pci.c      | 10 +++----
>   drivers/pci/quirks.c   |  2 +-
>   5 files changed, 69 insertions(+), 12 deletions(-)
> 
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index d20aa6f6863ab..59ea7e601a2d7 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -1224,7 +1224,7 @@ void iommu_free_global_pasid(ioasid_t pasid);
>   
>   /* PCI device reset functions */
>   int pci_dev_reset_iommu_prepare(struct pci_dev *pdev);
> -void pci_dev_reset_iommu_done(struct pci_dev *pdev);
> +void pci_dev_reset_iommu_done(struct pci_dev *pdev, int reset_result);
>   #else /* CONFIG_IOMMU_API */
>   
>   struct iommu_ops {};
> @@ -1554,7 +1554,8 @@ static inline int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
>   	return 0;
>   }
>   
> -static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
> +static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev,
> +					    int reset_result)
>   {
>   }
>   #endif /* CONFIG_IOMMU_API */
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index 342e8a5ad628c..6e2e607de8d8f 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -76,6 +76,7 @@ struct iommu_group {
>   enum gdev_blocked {
>   	BLOCKED_NO = 0, /* Not blocked */
>   	BLOCKED_RESETTING, /* PCI reset in flight */
> +	BLOCKED_RESET_FAILED, /* PCI reset failed */
>   };
>   
>   struct group_device {
> @@ -762,6 +763,9 @@ static void __iommu_group_remove_device(struct device *dev)
>   		if (device->dev != dev)
>   			continue;
>   
> +		/* Must drop the recovery_cnt when removing a blocked device */
> +		if (device->blocked && !WARN_ON(group->recovery_cnt == 0))
> +			group->recovery_cnt--;

I feel that the change above is unrelated to the purpose of this patch.
It looks like an independent fix that belongs to a separate patch.

Otherwise, this looks good to me.

Reviewed-by: Lu Baolu <baolu.lu at linux.intel.com>



More information about the linux-arm-kernel mailing list