@@ -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);
@@ -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)"
@@ -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 = {
@@ -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 */
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@oracle.com> --- 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(+)