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