From: Zhenzhong Duan Date: Wed, 27 May 2026 05:46:50 +0000 (-0400) Subject: intel_iommu_accel: Handle PASID entry removal for pc_inv_dsc request X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f3301bde02620fcbd4ad337bb2cb9505fa2060f;p=thirdparty%2Fqemu.git 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 Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Tested-by: Xudong Hao Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-Id: <20260527054658.1021096-13-zhenzhong.duan@intel.com> --- diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c index d2653f22ee..ed0d6bda7e 100644 --- a/hw/i386/intel_iommu_accel.c +++ b/hw/i386/intel_iommu_accel.c @@ -280,6 +280,59 @@ static void vtd_accel_fill_pc(VTDHostIOMMUDevice *vtd_hiod, uint32_t pasid, QLIST_INSERT_HEAD(&vtd_hiod->pasid_cache_list, vtd_pce, next); } +static void vtd_accel_delete_pc(VTDAccelPASIDCacheEntry *vtd_pce) +{ + QLIST_REMOVE(vtd_pce, next); + g_free(vtd_pce); +} + +static void +vtd_accel_pasid_cache_invalidate_one(VTDAccelPASIDCacheEntry *vtd_pce, + VTDPASIDCacheInfo *pc_info) +{ + VTDHostIOMMUDevice *vtd_hiod = vtd_pce->vtd_hiod; + VTDPASIDEntry pe; + uint16_t did; + + /* + * VTD_INV_DESC_PASIDC_G_DSI and VTD_INV_DESC_PASIDC_G_PASID_SI require + * DID check. If DID doesn't match the value in cache or memory, then + * it's not a pasid entry we want to invalidate. + */ + switch (pc_info->type) { + case VTD_INV_DESC_PASIDC_G_PASID_SI: + if (pc_info->pasid != vtd_pce->pasid) { + return; + } + /* Fall through */ + case VTD_INV_DESC_PASIDC_G_DSI: + did = VTD_SM_PASID_ENTRY_DID(&vtd_pce->pasid_entry); + if (pc_info->did != did) { + return; + } + } + + if (vtd_dev_get_pe_from_pasid(vtd_hiod->iommu_state, vtd_hiod->bus, + vtd_hiod->devfn, vtd_pce->pasid, &pe)) { + /* + * No valid pasid entry in guest memory. e.g. pasid entry was modified + * to be either all-zero or non-present. Either case means existing + * pasid cache should be invalidated. + */ + vtd_accel_delete_pc(vtd_pce); + } +} + +static void vtd_accel_pasid_cache_invalidate(VTDHostIOMMUDevice *vtd_hiod, + VTDPASIDCacheInfo *pc_info) +{ + VTDAccelPASIDCacheEntry *vtd_pce, *next; + + QLIST_FOREACH_SAFE(vtd_pce, &vtd_hiod->pasid_cache_list, next, next) { + vtd_accel_pasid_cache_invalidate_one(vtd_pce, pc_info); + } +} + /* * This function walks over PASID range within [start, end) in a single * 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) TYPE_HOST_IOMMU_DEVICE_IOMMUFD)) { continue; } + + /* + * The replay path inevitably needs to iterate through existing + * PASID cache entries. Since cached PASID entries that are marked + * for removal don't need to be iterated, we intentionally handle + * removals before additions to optimize the replay process. + */ + vtd_accel_pasid_cache_invalidate(vtd_hiod, pc_info); vtd_accel_replay_pasid_bind_for_dev(vtd_hiod, start, end, pc_info); } }