ioctl(iommufd, IOMMU_HWPT_GET_DIRTY_IOVA, arg) is the UAPI that fetches the bitmap that tells what was dirty in an IOVA range. A single bitmap is allocated and used across all the hw_pagetables sharing an IOAS which is then used in log_sync() to set Qemu global bitmaps. There's no point of even attempting to fetch these bitmaps, should the iommu tracker fail to start in a previous call to HWPT_SET_DIRTY. Signed-off-by: Joao Martins <joao.m.martins@xxxxxxxxxx> --- hw/iommufd/iommufd.c | 24 ++++++++++++++++++++ hw/iommufd/trace-events | 1 + hw/vfio/iommufd.c | 44 ++++++++++++++++++++++++++++++++++++ include/hw/iommufd/iommufd.h | 2 ++ 4 files changed, 71 insertions(+) diff --git a/hw/iommufd/iommufd.c b/hw/iommufd/iommufd.c index e5aff5deaf14..bc870b5e9b2f 100644 --- a/hw/iommufd/iommufd.c +++ b/hw/iommufd/iommufd.c @@ -219,6 +219,30 @@ int iommufd_set_dirty_tracking(int iommufd, uint32_t hwpt_id, bool start) return !ret ? 0 : -errno; } +int iommufd_get_dirty_iova(int iommufd, uint32_t hwpt_id, uint64_t iova, + ram_addr_t size, uint64_t page_size, uint64_t *data) +{ + int ret; + struct iommu_hwpt_get_dirty_iova get_dirty_iova = { + .size = sizeof(get_dirty_iova), + .hwpt_id = hwpt_id, + .bitmap = { + .iova = iova, .length = size, + .page_size = page_size, .data = (__u64 *)data, + }, + }; + + ret = ioctl(iommufd, IOMMU_HWPT_GET_DIRTY_IOVA, &get_dirty_iova); + trace_iommufd_get_dirty_iova(iommufd, hwpt_id, iova, size, page_size, ret); + if (ret) { + error_report("IOMMU_HWPT_GET_DIRTY_IOVA (iova: 0x%"PRIx64 + " size: 0x%"PRIx64") failed: %s", iova, + size, 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 d3c2b5a0ab95..9fe2cc60c6fe 100644 --- a/hw/iommufd/trace-events +++ b/hw/iommufd/trace-events @@ -10,3 +10,4 @@ iommufd_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int 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)" +iommufd_get_dirty_iova(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%d iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 8146407feedd..6c12239a40ab 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 "exec/ram_addr.h" #include "migration/migration.h" static bool iommufd_check_extension(VFIOContainer *bcontainer, @@ -102,6 +103,48 @@ static void iommufd_set_dirty_page_tracking(VFIOContainer *bcontainer, bcontainer->dirty_pages_supported = start; } +static int iommufd_get_dirty_bitmap(VFIOContainer *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr) +{ + VFIOIOMMUFDContainer *container = container_of(bcontainer, + VFIOIOMMUFDContainer, obj); + int ret; + VFIOIOASHwpt *hwpt; + unsigned long *data, page_size, bitmap_size, pages; + + if (!bcontainer->dirty_pages_supported) { + return 0; + } + + page_size = qemu_real_host_page_size; + pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size; + bitmap_size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / + BITS_PER_BYTE; + data = g_try_malloc0(bitmap_size); + if (!data) { + ret = -ENOMEM; + goto err_out; + } + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + ret = iommufd_get_dirty_iova(container->iommufd, hwpt->hwpt_id, + iova, size, page_size, data); + if (ret) { + goto err_out; + } + } + + cpu_physical_memory_set_dirty_lebitmap(data, ram_addr, pages); + + trace_vfio_get_dirty_bitmap(container->iommufd, iova, size, bitmap_size, + ram_addr); + +err_out: + g_free(data); + + return ret; +} + static int vfio_get_devicefd(const char *sysfs_path, Error **errp) { long int vfio_id = -1, ret = -ENOTTY; @@ -611,6 +654,7 @@ static void vfio_iommufd_class_init(ObjectClass *klass, 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; + vccs->get_dirty_bitmap = iommufd_get_dirty_bitmap; } static const TypeInfo vfio_iommufd_info = { diff --git a/include/hw/iommufd/iommufd.h b/include/hw/iommufd/iommufd.h index 61fd83771099..9b467e57723b 100644 --- a/include/hw/iommufd/iommufd.h +++ b/include/hw/iommufd/iommufd.h @@ -34,5 +34,7 @@ int iommufd_map_dma(int iommufd, uint32_t ioas, hwaddr iova, 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); +int iommufd_get_dirty_iova(int iommufd, uint32_t hwpt_id, uint64_t iova, + ram_addr_t size, uint64_t page_size, uint64_t *data); bool iommufd_supported(void); #endif /* HW_IOMMUFD_IOMMUFD_H */ -- 2.17.2