Throughout IOMMU domain lifetime that wants to use dirty tracking, some guarantees are needed such that any device attached to the iommu_domain supports dirty tracking. The idea is to handle a case where IOMMUs are assymetric feature-wise and thus the capability may not be advertised for all devices. This is done by adding a flag into HWPT_ALLOC namely: IOMMUFD_HWPT_ALLOC_ENFORCE_DIRTY .. Passed in HWPT_ALLOC ioctl flags. The enforcement is done by creating a iommu_domain and setting the associated flags (via iommu_domain_set_flags) cross-checking with IOMMU driver advertised flags (and failing if it's not advertised). Advertising the new IOMMU domain feature flag requires that the individual iommu driver capability is supported when we attach a new device to the iommu_domain or otherwise fail the attachment if the capability is not set in the device. Userspace will have also the option of checking which that dirty tracking is supported in the IOMMU behind the device. Link: https://lore.kernel.org/kvm/20220721142421.GB4609@xxxxxxxxxx/ Signed-off-by: Joao Martins <joao.m.martins@xxxxxxxxxx> --- drivers/iommu/iommufd/device.c | 2 +- drivers/iommu/iommufd/hw_pagetable.c | 29 ++++++++++++++++++++++--- drivers/iommu/iommufd/iommufd_private.h | 4 +++- include/uapi/linux/iommufd.h | 9 ++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 989bd485f92f..48d1300f0350 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -511,7 +511,7 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, } hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev, - immediate_attach); + immediate_attach, false); if (IS_ERR(hwpt)) { destroy_hwpt = ERR_CAST(hwpt); goto out_unlock; diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index cf2c1504e20d..4f0b72737ae2 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -55,12 +55,26 @@ int iommufd_hw_pagetable_enforce_cc(struct iommufd_hw_pagetable *hwpt) return 0; } +int iommufd_hw_pagetable_enforce_dirty(struct iommufd_hw_pagetable *hwpt, + struct iommufd_device *idev) +{ + hwpt->enforce_dirty = + !iommu_domain_set_flags(hwpt->domain, idev->dev->bus, + IOMMU_DOMAIN_F_ENFORCE_DIRTY); + if (!hwpt->enforce_dirty) + return -EINVAL; + + return 0; +} + /** * iommufd_hw_pagetable_alloc() - Get an iommu_domain for a device * @ictx: iommufd context * @ioas: IOAS to associate the domain with * @idev: Device to get an iommu_domain for * @immediate_attach: True if idev should be attached to the hwpt + * @enforce_dirty: True if dirty tracking support should be enforce + * on device attach * * Allocate a new iommu_domain and return it as a hw_pagetable. The HWPT * will be linked to the given ioas and upon return the underlying iommu_domain @@ -72,7 +86,8 @@ int iommufd_hw_pagetable_enforce_cc(struct iommufd_hw_pagetable *hwpt) */ struct iommufd_hw_pagetable * iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, - struct iommufd_device *idev, bool immediate_attach) + struct iommufd_device *idev, bool immediate_attach, + bool enforce_dirty) { struct iommufd_hw_pagetable *hwpt; int rc; @@ -107,6 +122,12 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, goto out_abort; } + if (enforce_dirty) { + rc = iommufd_hw_pagetable_enforce_dirty(hwpt, idev); + if (rc) + goto out_abort; + } + /* * immediate_attach exists only to accommodate iommu drivers that cannot * directly allocate a domain. These drivers do not finish creating the @@ -141,7 +162,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd) struct iommufd_ioas *ioas; int rc; - if (cmd->flags || cmd->__reserved) + if ((cmd->flags & ~(IOMMU_HWPT_ALLOC_ENFORCE_DIRTY)) || + cmd->__reserved) return -EOPNOTSUPP; idev = iommufd_get_device(ucmd, cmd->dev_id); @@ -155,7 +177,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd) } mutex_lock(&ioas->mutex); - hwpt = iommufd_hw_pagetable_alloc(ucmd->ictx, ioas, idev, false); + hwpt = iommufd_hw_pagetable_alloc(ucmd->ictx, ioas, idev, false, + cmd->flags & IOMMU_HWPT_ALLOC_ENFORCE_DIRTY); if (IS_ERR(hwpt)) { rc = PTR_ERR(hwpt); goto out_unlock; diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index dba730129b8c..2552eb44d83a 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -247,6 +247,7 @@ struct iommufd_hw_pagetable { struct iommu_domain *domain; bool auto_domain : 1; bool enforce_cache_coherency : 1; + bool enforce_dirty : 1; bool msi_cookie : 1; /* Head at iommufd_ioas::hwpt_list */ struct list_head hwpt_item; @@ -254,7 +255,8 @@ struct iommufd_hw_pagetable { struct iommufd_hw_pagetable * iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, - struct iommufd_device *idev, bool immediate_attach); + struct iommufd_device *idev, bool immediate_attach, + bool enforce_dirty); int iommufd_hw_pagetable_enforce_cc(struct iommufd_hw_pagetable *hwpt); int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, struct iommufd_device *idev); diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 8245c01adca6..1cd9c54d0f64 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -346,6 +346,15 @@ struct iommu_vfio_ioas { }; #define IOMMU_VFIO_IOAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VFIO_IOAS) +/** + * enum iommufd_hwpt_alloc_flags - Flags for alloc hwpt + * @IOMMU_HWPT_ALL_ENFORCE_DIRTY: Dirty tracking support for device IOMMU is + * enforced on device attachment + */ +enum iommufd_hwpt_alloc_flags { + IOMMU_HWPT_ALLOC_ENFORCE_DIRTY = 1 << 0, +}; + /** * struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC) * @size: sizeof(struct iommu_hwpt_alloc) -- 2.17.2