>-----Original Message-----
>From: Eric Auger <[email protected]>
>Subject: Re: [PATCH v7 16/23] intel_iommu: Replay pasid bindings after
>context cache invalidation
>
>
>
>On 10/24/25 10:43 AM, Zhenzhong Duan wrote:
>> From: Yi Liu <[email protected]>
>>
>> This replays guest pasid bindings after context cache invalidation.
>> Actually, programmer should issue pasid cache invalidation with proper
>> granularity after issuing context cache invalidation.
>>
>> We see old linux such as 6.7.0-rc2 not following the spec, it sends
>> pasid cache invalidation before context cache invalidation, then QEMU
>> depends on context cache invalidation to get pasid entry and setup
>> binding.
>>
>> Signed-off-by: Yi Liu <[email protected]>
>> Signed-off-by: Zhenzhong Duan <[email protected]>
>> ---
>>  hw/i386/intel_iommu.c | 47
>+++++++++++++++++++++++++++++++++++++++++++
>>  hw/i386/trace-events  |  1 +
>>  2 files changed, 48 insertions(+)
>>
>> diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
>> index 1f78274204..edd1416382 100644
>> --- a/hw/i386/intel_iommu.c
>> +++ b/hw/i386/intel_iommu.c
>> @@ -93,6 +93,8 @@ static void
>vtd_address_space_refresh_all(IntelIOMMUState *s);
>>  static void vtd_address_space_unmap(VTDAddressSpace *as,
>IOMMUNotifier *n);
>>  static int vtd_bind_guest_pasid(VTDAddressSpace *vtd_as, Error **errp);
>>  static void vtd_replay_pasid_bindings_all(IntelIOMMUState *s);
>> +static void vtd_pasid_cache_sync_locked(gpointer key, gpointer value,
>> +                                        gpointer user_data);
>>
>>  static void vtd_pasid_cache_reset_locked(IntelIOMMUState *s)
>>  {
>> @@ -2388,6 +2390,13 @@ static void
>vtd_context_global_invalidate(IntelIOMMUState *s)
>>       * VT-d emulation codes.
>>       */
>>      vtd_iommu_replay_all(s);
>> +    /*
>> +     * Same for pasid cache invalidation, per VT-d spec 6.5.2.1, a global
>> +     * context cache invalidation should be followed by global PASID
>cache
>> +     * invalidation. In order to work with guest not following spec,
>> +     * handle global PASID cache invalidation here.
>> +     */
>> +    vtd_replay_pasid_bindings_all(s);
>>  }
>>
>>  #ifdef CONFIG_IOMMUFD
>> @@ -2589,6 +2598,35 @@
>vtd_flush_host_piotlb_all_locked(IntelIOMMUState *s,
>>  }
>>  #endif
>>
>> +static void vtd_pasid_cache_devsi(VTDAddressSpace *vtd_as)
>> +{
>> +    IntelIOMMUState *s = vtd_as->iommu_state;
>> +    PCIBus *bus = vtd_as->bus;
>> +    uint8_t devfn = vtd_as->devfn;
>> +    struct vtd_as_key key = {
>> +        .bus = bus,
>> +        .devfn = devfn,
>> +        .pasid = vtd_as->pasid,
>> +    };
>> +    VTDPASIDCacheInfo pc_info;
>> +
>> +    if (!s->fsts || !s->root_scalable || !s->dmar_enabled) {
>> +        return;
>> +    }
>> +
>> +    trace_vtd_pasid_cache_devsi(pci_bus_num(bus),
>> +                                VTD_PCI_SLOT(devfn),
>VTD_PCI_FUNC(devfn));
>> +
>> +    /* We fake to be global invalidation just to bypass all checks */
>can you clarify which checks and why you want to bypass them?

All below checks in vtd_pasid_cache_sync_locked(). Because after context
cache invalidation, all cached pasid entry for this device become stale.
We need to invalidate them all.

    switch (pc_info->type) {
    case VTD_INV_DESC_PASIDC_G_PASID_SI:
        if (pc_info->pasid != vtd_as->pasid) {
            return;
        }
        /* Fall through */
    case VTD_INV_DESC_PASIDC_G_DSI:
        if (pc_entry->valid) {
            did = VTD_SM_PASID_ENTRY_DID(&pc_entry->pasid_entry);
        } else {
            did = VTD_SM_PASID_ENTRY_DID(&pe);
        }
        if (pc_info->did != did) {
            return;
        }
    }


>> +    pc_info.type = VTD_INV_DESC_PASIDC_G_GLOBAL;
>> +
>> +    /*
>> +     * We already get vtd_as of the device whose PASID cache is
>invalidated,
>s/get/got. Not sure the comment is worth.

OK, I can remove it.

>> +     * so just call vtd_pasid_cache_sync_locked() once.
>> +     */
>> +    vtd_pasid_cache_sync_locked(&key, vtd_as, &pc_info);
>> +}
>> +
>>  /* Do a context-cache device-selective invalidation.
>>   * @func_mask: FM field after shifting
>>   */
>> @@ -2647,6 +2685,15 @@ static void
>vtd_context_device_invalidate(IntelIOMMUState *s,
>>               * happened.
>>               */
>>              vtd_address_space_sync(vtd_as);
>> +            /*
>> +             * Per spec 6.5.2.1, context flush should be followed by
>PASID
>> +             * cache and iotlb flush. In order to work with a guest
>which does
>> +             * not follow spec and missed PASID cache flush, e.g., linux
>> +             * 6.7.0-rc2, we have vtd_pasid_cache_devsi() to invalidate
>PASID
>> +             * cache of passthrough device. Host iommu driver would
>flush
>> +             * piotlb when a pasid unbind is pass down to it.
>passed

Will do

Thanks
Zhenzhong

>> +             */
>> +            vtd_pasid_cache_devsi(vtd_as);
>>          }
>>      }
>>  }
>> diff --git a/hw/i386/trace-events b/hw/i386/trace-events
>> index 5a3ee1cf64..5fa5e93b68 100644
>> --- a/hw/i386/trace-events
>> +++ b/hw/i386/trace-events
>> @@ -28,6 +28,7 @@ vtd_pasid_cache_reset(void) ""
>>  vtd_inv_desc_pasid_cache_gsi(void) ""
>>  vtd_inv_desc_pasid_cache_dsi(uint16_t domain) "Domain selective PC
>invalidation domain 0x%"PRIx16
>>  vtd_inv_desc_pasid_cache_psi(uint16_t domain, uint32_t pasid) "PASID
>selective PC invalidation domain 0x%"PRIx16" pasid 0x%"PRIx32
>> +vtd_pasid_cache_devsi(uint8_t bus, uint8_t dev, uint8_t fn) "Dev selective
>PC invalidation dev: %02"PRIx8":%02"PRIx8".%02"PRIx8
>>  vtd_re_not_present(uint8_t bus) "Root entry bus %"PRIu8" not present"
>>  vtd_ce_not_present(uint8_t bus, uint8_t devfn) "Context entry
>bus %"PRIu8" devfn %"PRIu8" not present"
>>  vtd_iotlb_page_hit(uint16_t sid, uint64_t addr, uint64_t slpte, uint16_t
>domain) "IOTLB page hit sid 0x%"PRIx16" iova 0x%"PRIx64" slpte
>0x%"PRIx64" domain 0x%"PRIx16
>Besides
>
>Reviewed-by: Eric Auger <[email protected]>
>
>Eric

Reply via email to