intel_iommu_accel: Handle PASID entry removal for pc_inv_dsc request
When guest deletes PASID entries, QEMU will capture the pasid cache invalidation request, walk through pasid_cache_list in each passthrough device to find stale VTDAccelPASIDCacheEntry and delete them. Co-developed-by: Yi Liu <yi.l.liu@intel.com> Signed-off-by: Yi Liu <yi.l.liu@intel.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Tested-by: Xudong Hao <xudong.hao@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20260527054658.1021096-13-zhenzhong.duan@intel.com>
Zhenzhong Duan committed
May 27, 2026 at 01:46 UTC
8f3301bde02620fcbd4ad337bb2cb9505fa2060f
1 file changed
+61
hw/i386/intel_iommu_accel.c
+61
@@ -280,6 +280,59 @@ static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid,
280
QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next);
281
}
282
283
+static void vtd_accel_delete_pc(VTDAccelPASIDCacheEntry *vtd_pce)
284
+{
285
+ QLIST_REMOVE(vtd_pce, next);
286
+ g_free(vtd_pce);
287
+}
288
+
289
+static void
290
+vtd_accel_pasid_cache_invalidate_one(VTDAccelPASIDCacheEntry *vtd_pce,
291
+ VTDPASIDCacheInfo *pc_info)
292
+{
293
+ VTDHostIOMMUDevice *vtd_hiod = vtd_pce->vtd_hiod;
294
+ VTDPASIDEntry pe;
295
+ uint16_t did;
296
+
297
+ /*
298
+ * VTD_INV_DESC_PASIDC_G_DSI and VTD_INV_DESC_PASIDC_G_PASID_SI require
299
+ * DID check. If DID doesn't match the value in cache or memory, then
300
+ * it's not a pasid entry we want to invalidate.
301
+ */
302
+ switch (pc_info->type) {
303
+ case VTD_INV_DESC_PASIDC_G_PASID_SI:
304
+ if (pc_info->pasid != vtd_pce->pasid) {
305
+ return;
306
+ }
307
+ /* Fall through */
308
+ case VTD_INV_DESC_PASIDC_G_DSI:
309
+ did = VTD_SM_PASID_ENTRY_DID(&vtd_pce->pasid_entry);
310
+ if (pc_info->did != did) {
311
+ return;
312
+ }
313
+ }
314
+
315
+ if (vtd_dev_get_pe_from_pasid(vtd_hiod->iommu_state, vtd_hiod->bus,
316
+ vtd_hiod->devfn, vtd_pce->pasid, &pe)) {
317
+ /*
318
+ * No valid pasid entry in guest memory. e.g. pasid entry was modified
319
+ * to be either all-zero or non-present. Either case means existing
320
+ * pasid cache should be invalidated.
321
+ */
322
+ vtd_accel_delete_pc(vtd_pce);
323
+ }
324
+}
325
+
326
+static void vtd_accel_pasid_cache_invalidate(VTDHostIOMMUDevice *vtd_hiod,
327
+ VTDPASIDCacheInfo *pc_info)
328
+{
329
+ VTDAccelPASIDCacheEntry *vtd_pce, *next;
330
+
331
+ QLIST_FOREACH_SAFE(vtd_pce, &vtd_hiod->pasid_cache_list, next, next) {
332
+ vtd_accel_pasid_cache_invalidate_one(vtd_pce, pc_info);
333
+ }
334
+}
335
+
336
/*
337
* This function walks over PASID range within [start, end) in a single
338
* PASID table for entries matching @info type/did, then create
@@ -411,6 +464,14 @@ void vtd_accel_pasid_cache_sync(IntelIOMMUState *s, VTDPASIDCacheInfo *pc_info)
464
TYPE_HOST_IOMMU_DEVICE_IOMMUFD)) {
465
continue;
466
}
467
+
468
+ /*
469
+ * The replay path inevitably needs to iterate through existing
470
+ * PASID cache entries. Since cached PASID entries that are marked
471
+ * for removal don't need to be iterated, we intentionally handle
472
+ * removals before additions to optimize the replay process.
473
+ */
474
+ vtd_accel_pasid_cache_invalidate(vtd_hiod, pc_info);
475
vtd_accel_replay_pasid_bind_for_dev(vtd_hiod, start, end, pc_info);
476
}
477
}