diff --git a/hw/virtio/vdpa-dev-iommufd.c b/hw/virtio/vdpa-dev-iommufd.c index 668c6a1cb1ba24b0f0ac46ebdcc23eac809daeeb..2b0498f9dce81b174ee197cb6f0bb364b64535a8 100644 --- a/hw/virtio/vdpa-dev-iommufd.c +++ b/hw/virtio/vdpa-dev-iommufd.c @@ -12,6 +12,9 @@ #include "exec/target_page.h" #include "exec/address-spaces.h" #include "hw/virtio/vdpa-dev-iommufd.h" +#include "migration/migration.h" +#include "qapi/qapi-commands-migration.h" +#include "hw/virtio/vhost.h" static QLIST_HEAD(, VDPAIOMMUFDContainer) vdpa_container_list = QLIST_HEAD_INITIALIZER(vdpa_container_list); @@ -118,6 +121,51 @@ static void vhost_vdpa_iommufd_container_region_del(MemoryListener *listener, memory_region_unref(section->mr); } +static void vhost_vdpa_iommufd_container_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + VDPAIOMMUFDContainer *container = container_of(listener, VDPAIOMMUFDContainer, listener); + IOMMUFDHWPT *hwpt; + VhostVdpaDevice *vdev; + MigrationState *ms = migrate_get_current(); + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + QLIST_FOREACH(vdev, &hwpt->device_list, next) { + if (!vdev->dev.log_enabled || !vdev->dev.log) { + continue; + } + + /** + * For the vhost-vdpa device, log_sync is performed on the entire VM, + * that is, this sync is for the entire flatview. + * Therefore, the first MemoryRegionSection of flatview needs to be + * synchronized. The rest of the mrs do not need to be synchronized. + */ + if (is_first_section(section)) { + int r = vdev->dev.vhost_ops->vhost_log_sync(&vdev->dev); + if (r < 0) { + qemu_log("Failed to sync dirty log: %d\n", r); + if (migration_is_running(ms->state)) { + qmp_migrate_cancel(NULL); + } + return; + } + } + + /** + * Dirty maps are merged separately by MRS, so each MRS needs to be iterated. + */ + if (vhost_bytemap_log_support(&vdev->dev)) { + vhost_sync_dirty_bytemap(&vdev->dev, section); + } else { + vhost_sync_dirty_bitmap(&vdev->dev, section, 0x0, ~0x0ULL); + } + return; + } + } +} + + /* * IOTLB API used by vhost vdpa iommufd container */ @@ -125,6 +173,7 @@ const MemoryListener vhost_vdpa_iommufd_container_listener = { .name = "vhost-vdpa-iommufd-container", .region_add = vhost_vdpa_iommufd_container_region_add, .region_del = vhost_vdpa_iommufd_container_region_del, + .log_sync = vhost_vdpa_iommufd_container_log_sync, }; static int vhost_vdpa_container_connect_iommufd(VDPAIOMMUFDContainer *container) @@ -268,6 +317,7 @@ static int vhost_vdpa_container_attach_device(VDPAIOMMUFDContainer *container, V ret = ioctl(vdev->vhostfd, VHOST_VDPA_ATTACH_IOMMUFD_PT, &pt_id); if (ret == 0) { QLIST_INSERT_HEAD(&hwpt->device_list, vdev, next); + vdev->dev.has_container = true; return 0; } } @@ -293,6 +343,7 @@ static int vhost_vdpa_container_attach_device(VDPAIOMMUFDContainer *container, V } QLIST_INSERT_HEAD(&hwpt->device_list, vdev, next); + vdev->dev.has_container = true; QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); return 0; @@ -318,6 +369,7 @@ static void vhost_vdpa_container_detach_device(VDPAIOMMUFDContainer *container, ioctl(vdev->vhostfd, VHOST_VDPA_DETACH_IOMMUFD_PT, &hwpt->hwpt_id); QLIST_SAFE_REMOVE(vdev, next); + vdev->dev.has_container = false; /* No device using this hwpt, free it */ if (QLIST_EMPTY(&hwpt->device_list)) { diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index b256ad540c92c963afa84fa3f00c07bb7895894f..7ce8547419a4c807df89b4a58dd10c5217754ddc 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -111,6 +111,7 @@ static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) v->dev.vq_index = 0; v->dev.vq_index_end = v->dev.nvqs; v->dev.backend_features = 0; + v->dev.has_container = false; v->started = false; ret = vhost_vdpa_get_iova_range(v->vhostfd, &iova_range); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index ed2f41e47a628686dd1e7640a0a963e7aaaaf31b..58207e472b082c3a2c10a139de86a6445239abba 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -46,7 +46,7 @@ do { } while (0) #endif -static inline bool vhost_bytemap_log_support(struct vhost_dev *dev) +bool vhost_bytemap_log_support(struct vhost_dev *dev) { return (dev->backend_cap & BIT_ULL(VHOST_BACKEND_F_BYTEMAPLOG)); } @@ -159,10 +159,10 @@ bool vhost_dev_has_iommu(struct vhost_dev *dev) } } -static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, - MemoryRegionSection *section, - hwaddr first, - hwaddr last) +int vhost_sync_dirty_bitmap(struct vhost_dev *dev, + MemoryRegionSection *section, + hwaddr first, + hwaddr last) { int i; hwaddr start_addr; @@ -239,8 +239,8 @@ static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, return 0; } -static int vhost_sync_dirty_bytemap(struct vhost_dev *dev, - MemoryRegionSection *section) +int vhost_sync_dirty_bytemap(struct vhost_dev *dev, + MemoryRegionSection *section) { unsigned long *bytemap = dev->log->log; return memory_section_set_dirty_bytemap(section, bytemap); @@ -253,7 +253,7 @@ static void vhost_log_sync(MemoryListener *listener, memory_listener); MigrationState *ms = migrate_get_current(); - if (!dev->log_enabled || !dev->log) { + if (!dev->log_enabled || !dev->log || dev->has_container) { return; } diff --git a/include/exec/memory.h b/include/exec/memory.h index c14dc69d277bde140bd13d310ac1be5e2672d668..e58ca3d3688973881aa7e13b61130efc2c67b08e 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2085,6 +2085,16 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client); void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size); +/** + * is_first_section: Determine whether a MemoryRegionSection is the first section + * + * Determine whether a MemoryRegionSection is the first section + * of its corresponding parent MemoryRegion. + * + * @section: MemoryRegionSection + */ +bool is_first_section(MemoryRegionSection *section); + /** * memory_region_clear_dirty_bitmap - clear dirty bitmap for memory range * diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 9ca5819deb08c0a63c19cb60da9180c2bc471489..598ae137575304dac90c6bb5889901656d015b70 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -133,6 +133,7 @@ struct vhost_dev { QLIST_HEAD(, vhost_iommu) iommu_list; IOMMUNotifier n; const VhostDevConfigOps *config_ops; + bool has_container; }; extern const VhostOps kernel_ops; @@ -206,6 +207,14 @@ static inline bool vhost_dev_is_started(struct vhost_dev *hdev) return hdev->started; } +/** + * vhost_bytemap_log_support() - check if the vhost device supports dirty bytemap + * @dev: common vhost_dev structure + * + * Return: true if the vhost device supports dirty bytemap, false otherwise. + */ +bool vhost_bytemap_log_support(struct vhost_dev *dev); + /** * vhost_dev_start() - start the vhost device * @hdev: common vhost_dev structure @@ -343,6 +352,12 @@ int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, struct vhost_inflight *inflight); bool used_memslots_is_exceeded(void); bool vhost_dev_has_iommu(struct vhost_dev *dev); +int vhost_sync_dirty_bitmap(struct vhost_dev *dev, + MemoryRegionSection *section, + hwaddr first, + hwaddr last); +int vhost_sync_dirty_bytemap(struct vhost_dev *dev, + MemoryRegionSection *section); #ifdef CONFIG_VHOST int vhost_reset_device(struct vhost_dev *hdev); diff --git a/system/memory.c b/system/memory.c index 9db07fd83234d792a030b8647f59c9b5b4740e25..dff55f738829b403a58acd74872f6f14848500e3 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2271,6 +2271,12 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, memory_region_get_dirty_log_mask(mr)); } +bool is_first_section(MemoryRegionSection *section) +{ + return section->fv->ranges->addr.start == section->offset_within_address_space && + section->fv->ranges->addr.size == section->size; +} + /* * If memory region `mr' is NULL, do global sync. Otherwise, sync * dirty bitmap for the specified memory region.