ioctl(iommufd, IOMMU_HWPT_SET_DIRTY, arg) is the UAPI that enables or disables dirty page tracking. We set it on the whole list of iommu domains we are tracking, and set ::dirty_pages_supported accordingly, used when we attempt at reading out the dirty bits from the hw pagetables. Signed-off-by: Joao Martins <joao.m.martins@xxxxxxxxxx> --- hw/iommufd/iommufd.c | 18 ++++++++++++ hw/iommufd/trace-events | 1 + hw/vfio/iommufd.c | 57 ++++++++++++++++++++++++++++++++++++ include/hw/iommufd/iommufd.h | 1 + 4 files changed, 77 insertions(+) diff --git a/hw/iommufd/iommufd.c b/hw/iommufd/iommufd.c index 4e8179d612a5..e5aff5deaf14 100644 --- a/hw/iommufd/iommufd.c +++ b/hw/iommufd/iommufd.c @@ -201,6 +201,24 @@ int iommufd_copy_dma(int iommufd, uint32_t src_ioas, uint32_t dst_ioas, return !ret ? 0 : -errno; } +int iommufd_set_dirty_tracking(int iommufd, uint32_t hwpt_id, bool start) +{ + int ret; + struct iommu_hwpt_set_dirty set_dirty = { + .size = sizeof(set_dirty), + .hwpt_id = hwpt_id, + .flags = !start ? IOMMU_DIRTY_TRACKING_DISABLED : + IOMMU_DIRTY_TRACKING_ENABLED, + }; + + ret = ioctl(iommufd, IOMMU_HWPT_SET_DIRTY, &set_dirty); + trace_iommufd_set_dirty(iommufd, hwpt_id, start, ret); + if (ret) { + error_report("IOMMU_HWPT_SET_DIRTY failed: %s", strerror(errno)); + } + return !ret ? 0 : -errno; +} + static void iommufd_register_types(void) { qemu_mutex_init(&iommufd_lock); diff --git a/hw/iommufd/trace-events b/hw/iommufd/trace-events index 615d80cdf42c..d3c2b5a0ab95 100644 --- a/hw/iommufd/trace-events +++ b/hw/iommufd/trace-events @@ -9,3 +9,4 @@ iommufd_put_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" iommufd_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)" iommufd_copy_dma(int iommufd, uint32_t src_ioas, uint32_t dst_ioas, uint64_t iova, uint64_t size, bool readonly, int ret) " iommufd=%d src_ioas=%d dst_ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" readonly=%d (%d)" +iommufd_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%d enable=%d (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 8ff5988b0773..8146407feedd 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -33,6 +33,7 @@ #include "hw/qdev-core.h" #include "sysemu/reset.h" #include "qemu/cutils.h" +#include "migration/migration.h" static bool iommufd_check_extension(VFIOContainer *bcontainer, VFIOContainerFeature feat) @@ -82,6 +83,25 @@ static int iommufd_unmap(VFIOContainer *bcontainer, container->ioas_id, iova, size); } +static void iommufd_set_dirty_page_tracking(VFIOContainer *bcontainer, + bool start) +{ + VFIOIOMMUFDContainer *container = container_of(bcontainer, + VFIOIOMMUFDContainer, obj); + int ret; + VFIOIOASHwpt *hwpt; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + ret = iommufd_set_dirty_tracking(container->iommufd, + hwpt->hwpt_id, start); + if (ret) { + return; + } + } + + bcontainer->dirty_pages_supported = start; +} + static int vfio_get_devicefd(const char *sysfs_path, Error **errp) { long int vfio_id = -1, ret = -ENOTTY; @@ -304,6 +324,40 @@ static int vfio_device_reset(VFIODevice *vbasedev) return 0; } +static bool vfio_iommufd_devices_all_dirty_tracking(VFIOContainer *bcontainer) +{ + MigrationState *ms = migrate_get_current(); + VFIOIOMMUFDContainer *container; + VFIODevice *vbasedev; + VFIOIOASHwpt *hwpt; + + if (bcontainer->dirty_pages_supported) { + return true; + } + + if (!migration_is_setup_or_active(ms->state)) { + return false; + } + + container = container_of(bcontainer, VFIOIOMMUFDContainer, obj); + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + QLIST_FOREACH(vbasedev, &hwpt->device_list, hwpt_next) { + VFIOMigration *migration = vbasedev->migration; + + if (!migration) { + return false; + } + + if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF) + && (migration->device_state & VFIO_DEVICE_STATE_RUNNING)) { + return false; + } + } + } + return true; +} + static int vfio_iommufd_container_reset(VFIOContainer *bcontainer) { VFIOIOMMUFDContainer *container; @@ -446,6 +500,7 @@ static int iommufd_attach_device(VFIODevice *vbasedev, AddressSpace *as, */ vfio_as_add_container(space, bcontainer); + bcontainer->dirty_pages_supported = true; bcontainer->initialized = true; out: @@ -554,6 +609,8 @@ static void vfio_iommufd_class_init(ObjectClass *klass, vccs->attach_device = iommufd_attach_device; vccs->detach_device = iommufd_detach_device; vccs->reset = vfio_iommufd_container_reset; + vccs->devices_all_dirty_tracking = vfio_iommufd_devices_all_dirty_tracking; + vccs->set_dirty_page_tracking = iommufd_set_dirty_page_tracking; } static const TypeInfo vfio_iommufd_info = { diff --git a/include/hw/iommufd/iommufd.h b/include/hw/iommufd/iommufd.h index 59835cddcacf..61fd83771099 100644 --- a/include/hw/iommufd/iommufd.h +++ b/include/hw/iommufd/iommufd.h @@ -33,5 +33,6 @@ int iommufd_map_dma(int iommufd, uint32_t ioas, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); int iommufd_copy_dma(int iommufd, uint32_t src_ioas, uint32_t dst_ioas, hwaddr iova, ram_addr_t size, bool readonly); +int iommufd_set_dirty_tracking(int iommufd, uint32_t hwpt_id, bool start); bool iommufd_supported(void); #endif /* HW_IOMMUFD_IOMMUFD_H */ -- 2.17.2