On 2/26/26 11:50 AM, Shameer Kolothum wrote: > Add support for selecting and initializing a CMDQV backend based on > the cmdqv OnOffAuto property. > > If set to OFF, CMDQV is not used and the default IOMMUFD-backed > allocation path is taken. > > If set to AUTO, QEMU attempts to probe a CMDQV backend during > device setup. If probing succeeds, the selected ops are stored > in the accelerated SMMUv3 state and used. If probing fails, > QEMU silently falls back to the default path. > > If set to ON, QEMU requires CMDQV support. Probing is performed > during setup and failure results in an error. > > When a CMDQV backend is active, its callbacks are used for vIOMMU > allocation, free, and reset handling. Otherwise, the base > implementation is used. > > The current implementation wires up the Tegra241 CMDQV backend > through the generic ops interface. Functional CMDQV behaviour is > added in subsequent patches. > > No functional change. > > Signed-off-by: Shameer Kolothum <[email protected]> > --- > include/hw/arm/smmuv3.h | 2 + > hw/arm/smmuv3-accel.c | 93 +++++++++++++++++++++++++++++++++++++---- > 2 files changed, 88 insertions(+), 7 deletions(-) > > diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h > index 26b2fc42fd..648412cafc 100644 > --- a/include/hw/arm/smmuv3.h > +++ b/include/hw/arm/smmuv3.h > @@ -73,6 +73,8 @@ struct SMMUv3State { > bool ats; > uint8_t oas; > uint8_t ssidsize; > + /* SMMU CMDQV extension */ > + OnOffAuto cmdqv; > }; > > typedef enum { > diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c > index ab1b0a3669..4373bbd97b 100644 > --- a/hw/arm/smmuv3-accel.c > +++ b/hw/arm/smmuv3-accel.c > @@ -18,6 +18,7 @@ > > #include "smmuv3-internal.h" > #include "smmuv3-accel.h" > +#include "tegra241-cmdqv.h" > > /* > * The root region aliases the global system memory, and shared_as_sysmem > @@ -522,6 +523,7 @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, > HostIOMMUDeviceIOMMUFD *idev, > Error **errp) > { > SMMUv3AccelState *accel = s->s_accel; > + const SMMUv3AccelCmdqvOps *cmdqv_ops = accel->cmdqv_ops; > struct iommu_hwpt_arm_smmuv3 bypass_data = { > .ste = { SMMU_STE_CFG_BYPASS | SMMU_STE_VALID, 0x0ULL }, > }; > @@ -532,10 +534,17 @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, > HostIOMMUDeviceIOMMUFD *idev, > uint32_t viommu_id, hwpt_id; > IOMMUFDViommu *viommu; > > - if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, > - IOMMU_VIOMMU_TYPE_ARM_SMMUV3, > s2_hwpt_id, > - NULL, 0, &viommu_id, errp)) { > - return false; > + if (cmdqv_ops) { > + if (!cmdqv_ops->alloc_viommu(s, idev, &viommu_id, errp)) { > + return false; > + } > + } else { > + if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid, > + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, > + s2_hwpt_id, NULL, 0, &viommu_id, > + errp)) { > + return false; > + } > } > > viommu = g_new0(IOMMUFDViommu, 1); > @@ -581,12 +590,69 @@ free_bypass_hwpt: > free_abort_hwpt: > iommufd_backend_free_id(idev->iommufd, accel->abort_hwpt_id); > free_viommu: > - iommufd_backend_free_id(idev->iommufd, viommu->viommu_id); > + if (cmdqv_ops && cmdqv_ops->free_viommu) { > + cmdqv_ops->free_viommu(s); > + } else { > + iommufd_backend_free_id(idev->iommufd, viommu->viommu_id); > + } reading [PATCH v3 13/32] hw/arm/tegra241-cmdqv: Implement CMDQV vIOMMU alloc/free tegra241_cmdqv_free_viommu() looks identifical in both implementations So maybe this is not needed Eric > g_free(viommu); > accel->viommu = NULL; > return false; > } > > +static const SMMUv3AccelCmdqvOps * > +smmuv3_accel_probe_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, > + Error **errp) > +{ > + const SMMUv3AccelCmdqvOps *ops = tegra241_cmdqv_get_ops(); > + > + if (!ops || !ops->probe) { > + error_setg(errp, "No CMDQV ops found"); > + return NULL; > + } > + > + if (!ops->probe(s, idev, errp)) { > + return NULL; > + } > + return ops; > +} > + > +static bool > +smmuv3_accel_select_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, > + Error **errp) > +{ > + const SMMUv3AccelCmdqvOps *ops = NULL; > + > + if (s->s_accel->cmdqv_ops) { > + return true; > + } > + > + switch (s->cmdqv) { > + case ON_OFF_AUTO_OFF: > + s->s_accel->cmdqv_ops = NULL; > + return true; > + case ON_OFF_AUTO_AUTO: > + ops = smmuv3_accel_probe_cmdqv(s, idev, NULL); > + break; > + case ON_OFF_AUTO_ON: > + ops = smmuv3_accel_probe_cmdqv(s, idev, errp); > + if (!ops) { > + error_append_hint(errp, "CMDQV requested but not supported"); > + return false; > + } > + s->s_accel->cmdqv_ops = ops; > + break; > + default: > + g_assert_not_reached(); > + } > + > + if (ops && ops->init && !ops->init(s, errp)) { > + return false; > + } > + s->s_accel->cmdqv_ops = ops; > + return true; > +} > + > static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int > devfn, > HostIOMMUDevice *hiod, Error > **errp) > { > @@ -621,6 +687,10 @@ static bool smmuv3_accel_set_iommu_device(PCIBus *bus, > void *opaque, int devfn, > goto done; > } > > + if (!smmuv3_accel_select_cmdqv(s, idev, errp)) { > + return false; > + } > + > if (!smmuv3_accel_alloc_viommu(s, idev, errp)) { > error_append_hint(errp, "Unable to alloc vIOMMU: idev devid 0x%x: ", > idev->devid); > @@ -867,8 +937,17 @@ bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error > **errp) > > void smmuv3_accel_reset(SMMUv3State *s) > { > - /* Attach a HWPT based on GBPA reset value */ > - smmuv3_accel_attach_gbpa_hwpt(s, NULL); > + SMMUv3AccelState *accel = s->s_accel; > + > + if (!accel) { > + return; > + } > + /* Attach a HWPT based on GBPA reset value */ > + smmuv3_accel_attach_gbpa_hwpt(s, NULL); > + > + if (accel->cmdqv_ops && accel->cmdqv_ops->reset) { > + accel->cmdqv_ops->reset(s); > + } > } > > static void smmuv3_accel_as_init(SMMUv3State *s)
