[PATCH v1 5/5] iommu/vt-d: Convert nested cache invalidation to the core array loop

From: Nicolin Chen

Date: Mon Jun 29 2026 - 17:18:55 EST


intel_nested_cache_invalidate_user() used to walk the whole request array
on its own, copying and then flushing one single entry at a time.

The iommufd core now iterates the request array itself and re-invokes the
op with the not-yet-handled sub-array, so handle just a single request per
call out of the front of that sub-array and report one handled entry via
the array->entry_num. An empty array keeps returning a success, used as a
probe of IOMMU_HWPT_INVALIDATE_DATA_VTD_S1 support.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx>
---
drivers/iommu/intel/nested.c | 54 ++++++++++++++++++++----------------
1 file changed, 30 insertions(+), 24 deletions(-)

diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index 2b979bec56cef..cad5c1573196e 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -93,7 +93,7 @@ static int intel_nested_cache_invalidate_user(struct iommu_domain *domain,
{
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
struct iommu_hwpt_vtd_s1_invalidate inv_entry;
- u32 index, processed = 0;
+ u32 processed = 0;
int ret = 0;

if (array->type != IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) {
@@ -101,31 +101,37 @@ static int intel_nested_cache_invalidate_user(struct iommu_domain *domain,
goto out;
}

- for (index = 0; index < array->entry_num; index++) {
- ret = iommu_copy_struct_from_user_array(&inv_entry, array,
- IOMMU_HWPT_INVALIDATE_DATA_VTD_S1,
- index, __reserved);
- if (ret)
- break;
-
- if ((inv_entry.flags & ~IOMMU_VTD_INV_FLAGS_LEAF) ||
- inv_entry.__reserved) {
- ret = -EOPNOTSUPP;
- break;
- }
-
- if (!IS_ALIGNED(inv_entry.addr, VTD_PAGE_SIZE) ||
- ((inv_entry.npages == U64_MAX) && inv_entry.addr)) {
- ret = -EINVAL;
- break;
- }
-
- cache_tag_flush_range(dmar_domain, inv_entry.addr,
- inv_entry.addr + nrpages_to_size(inv_entry.npages) - 1,
- inv_entry.flags & IOMMU_VTD_INV_FLAGS_LEAF);
- processed++;
+ /*
+ * The core re-invokes this op for the remaining requests, so handle one
+ * request per call. A zero-length array only probes the type, validated
+ * above.
+ */
+ if (!array->entry_num)
+ goto out;
+
+ ret = iommu_copy_struct_from_user_array(
+ &inv_entry, array, IOMMU_HWPT_INVALIDATE_DATA_VTD_S1, 0,
+ __reserved);
+ if (ret)
+ goto out;
+
+ if ((inv_entry.flags & ~IOMMU_VTD_INV_FLAGS_LEAF) ||
+ inv_entry.__reserved) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!IS_ALIGNED(inv_entry.addr, VTD_PAGE_SIZE) ||
+ (inv_entry.npages == U64_MAX && inv_entry.addr)) {
+ ret = -EINVAL;
+ goto out;
}

+ cache_tag_flush_range(dmar_domain, inv_entry.addr,
+ inv_entry.addr +
+ nrpages_to_size(inv_entry.npages) - 1,
+ inv_entry.flags & IOMMU_VTD_INV_FLAGS_LEAF);
+ processed = 1;
out:
array->entry_num = processed;
return ret;
--
2.43.0