@samitouri / QOSamiQemu / commits / 8f3301bde0

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 }