diff mbox series

[v7,11/13] vfio: Add function to get dirty page list

Message ID 1562665760-26158-12-git-send-email-kwankhede@nvidia.com
State New
Headers show
Series Add migration support for VFIO device | expand

Commit Message

Kirti Wankhede July 9, 2019, 9:49 a.m. UTC
Dirty page tracking (.log_sync) is part of RAM copying state, where
vendor driver provides the bitmap of pages which are dirtied by vendor
driver through migration region and as part of RAM copy, those pages
gets copied to file stream.

To get dirty page bitmap:
- write start address, page_size and pfn count.
- read count of pfns copied.
    - Vendor driver should return 0 if driver doesn't have any page to
      report dirty in given range.
    - Vendor driver should return -1 to mark all pages dirty for given range.
- read data_offset, where vendor driver has written bitmap.
- read bitmap from the region or mmaped part of the region.
- Iterate above steps till page bitmap for all requested pfns are copied.

Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
Reviewed-by: Neo Jia <cjia@nvidia.com>
---
 hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
 hw/vfio/trace-events          |   1 +
 include/hw/vfio/vfio-common.h |   2 +
 3 files changed, 126 insertions(+)

Comments

Yan Zhao July 12, 2019, 12:33 a.m. UTC | #1
On Tue, Jul 09, 2019 at 05:49:18PM +0800, Kirti Wankhede wrote:
> Dirty page tracking (.log_sync) is part of RAM copying state, where
> vendor driver provides the bitmap of pages which are dirtied by vendor
> driver through migration region and as part of RAM copy, those pages
> gets copied to file stream.
> 
> To get dirty page bitmap:
> - write start address, page_size and pfn count.
> - read count of pfns copied.
>     - Vendor driver should return 0 if driver doesn't have any page to
>       report dirty in given range.
>     - Vendor driver should return -1 to mark all pages dirty for given range.
> - read data_offset, where vendor driver has written bitmap.
> - read bitmap from the region or mmaped part of the region.
> - Iterate above steps till page bitmap for all requested pfns are copied.
> 
> Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
> Reviewed-by: Neo Jia <cjia@nvidia.com>
> ---
>  hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
>  hw/vfio/trace-events          |   1 +
>  include/hw/vfio/vfio-common.h |   2 +
>  3 files changed, 126 insertions(+)
> 
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 5fb4c5329ede..ca1a8c0f5f1f 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -269,6 +269,129 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
>      return qemu_file_get_error(f);
>  }
>  
> +void vfio_get_dirty_page_list(VFIODevice *vbasedev,
> +                              uint64_t start_pfn,
> +                              uint64_t pfn_count,
> +                              uint64_t page_size)
> +{
> +    VFIOMigration *migration = vbasedev->migration;
> +    VFIORegion *region = &migration->region.buffer;
> +    uint64_t count = 0;
> +    int64_t copied_pfns = 0;
> +    int64_t total_pfns = pfn_count;
> +    int ret;
> +
> +    qemu_mutex_lock(&migration->lock);
> +
> +    while (total_pfns > 0) {
> +        uint64_t bitmap_size, data_offset = 0;
> +        uint64_t start = start_pfn + count;
> +        void *buf = NULL;
> +        bool buffer_mmaped = false;
> +
> +        ret = pwrite(vbasedev->fd, &start, sizeof(start),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              start_pfn));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty pages start address %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              page_size));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty page size %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              total_pfns));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty page total pfns %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        /* Read copied dirty pfns */
> +        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                             copied_pfns));
> +        if (ret < 0) {
> +            error_report("%s: Failed to get dirty pages bitmap count %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
> +            /*
> +             * copied_pfns could be 0 if driver doesn't have any page to
> +             * report dirty in given range
> +             */
> +            break;
> +        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
> +            /* Mark all pages dirty for this range */
> +            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
> +                                                pfn_count * page_size,
> +                                                DIRTY_MEMORY_MIGRATION);
seesm pfn_count here is not right
> +            break;
> +        }
> +
> +        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
> +
> +        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                             data_offset));
> +        if (ret != sizeof(data_offset)) {
> +            error_report("%s: Failed to get migration buffer data offset %d",
> +                         vbasedev->name, ret);
> +            goto dpl_unlock;
> +        }
> +
> +        if (region->mmaps) {
> +            buf = find_data_region(region, data_offset, bitmap_size);
> +        }
> +
> +        buffer_mmaped = (buf != NULL) ? true : false;
> +
> +        if (!buffer_mmaped) {
> +            buf = g_try_malloc0(bitmap_size);
> +            if (!buf) {
> +                error_report("%s: Error allocating buffer ", __func__);
> +                goto dpl_unlock;
> +            }
> +
> +            ret = pread(vbasedev->fd, buf, bitmap_size,
> +                        region->fd_offset + data_offset);
> +            if (ret != bitmap_size) {
> +                error_report("%s: Failed to get dirty pages bitmap %d",
> +                             vbasedev->name, ret);
> +                g_free(buf);
> +                goto dpl_unlock;
> +            }
> +        }
> +
> +        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
> +                                               (start_pfn + count) * page_size,
> +                                                copied_pfns);
> +        count      += copied_pfns;
> +        total_pfns -= copied_pfns;
> +
> +        if (!buffer_mmaped) {
> +            g_free(buf);
> +        }
> +    }
> +
> +    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
> +                                   page_size);
> +
> +dpl_unlock:
> +    qemu_mutex_unlock(&migration->lock);
> +}
> +
>  /* ---------------------------------------------------------------------- */
>  
>  static int vfio_save_setup(QEMUFile *f, void *opaque)
> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
> index ac065b559f4e..414a5e69ec5e 100644
> --- a/hw/vfio/trace-events
> +++ b/hw/vfio/trace-events
> @@ -160,3 +160,4 @@ vfio_save_complete_precopy(char *name) " (%s)"
>  vfio_load_device_config_state(char *name) " (%s)"
>  vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
>  vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
> +vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
> diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
> index a022484d2636..dc1b83a0b4ef 100644
> --- a/include/hw/vfio/vfio-common.h
> +++ b/include/hw/vfio/vfio-common.h
> @@ -222,5 +222,7 @@ int vfio_spapr_remove_window(VFIOContainer *container,
>  
>  int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
>  void vfio_migration_finalize(VFIODevice *vbasedev);
> +void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
> +                               uint64_t pfn_count, uint64_t page_size);
>  
>  #endif /* HW_VFIO_VFIO_COMMON_H */
> -- 
> 2.7.0
>
Kirti Wankhede July 18, 2019, 6:39 p.m. UTC | #2
On 7/12/2019 6:03 AM, Yan Zhao wrote:
> On Tue, Jul 09, 2019 at 05:49:18PM +0800, Kirti Wankhede wrote:
>> Dirty page tracking (.log_sync) is part of RAM copying state, where
>> vendor driver provides the bitmap of pages which are dirtied by vendor
>> driver through migration region and as part of RAM copy, those pages
>> gets copied to file stream.
>>
>> To get dirty page bitmap:
>> - write start address, page_size and pfn count.
>> - read count of pfns copied.
>>     - Vendor driver should return 0 if driver doesn't have any page to
>>       report dirty in given range.
>>     - Vendor driver should return -1 to mark all pages dirty for given range.
>> - read data_offset, where vendor driver has written bitmap.
>> - read bitmap from the region or mmaped part of the region.
>> - Iterate above steps till page bitmap for all requested pfns are copied.
>>
>> Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
>> Reviewed-by: Neo Jia <cjia@nvidia.com>
>> ---
>>  hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
>>  hw/vfio/trace-events          |   1 +
>>  include/hw/vfio/vfio-common.h |   2 +
>>  3 files changed, 126 insertions(+)
>>
>> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
>> index 5fb4c5329ede..ca1a8c0f5f1f 100644
>> --- a/hw/vfio/migration.c
>> +++ b/hw/vfio/migration.c
>> @@ -269,6 +269,129 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
>>      return qemu_file_get_error(f);
>>  }
>>  
>> +void vfio_get_dirty_page_list(VFIODevice *vbasedev,
>> +                              uint64_t start_pfn,
>> +                              uint64_t pfn_count,
>> +                              uint64_t page_size)
>> +{
>> +    VFIOMigration *migration = vbasedev->migration;
>> +    VFIORegion *region = &migration->region.buffer;
>> +    uint64_t count = 0;
>> +    int64_t copied_pfns = 0;
>> +    int64_t total_pfns = pfn_count;
>> +    int ret;
>> +
>> +    qemu_mutex_lock(&migration->lock);
>> +
>> +    while (total_pfns > 0) {
>> +        uint64_t bitmap_size, data_offset = 0;
>> +        uint64_t start = start_pfn + count;
>> +        void *buf = NULL;
>> +        bool buffer_mmaped = false;
>> +
>> +        ret = pwrite(vbasedev->fd, &start, sizeof(start),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              start_pfn));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty pages start address %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              page_size));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty page size %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              total_pfns));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty page total pfns %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        /* Read copied dirty pfns */
>> +        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
>> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                             copied_pfns));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to get dirty pages bitmap count %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
>> +            /*
>> +             * copied_pfns could be 0 if driver doesn't have any page to
>> +             * report dirty in given range
>> +             */
>> +            break;
>> +        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
>> +            /* Mark all pages dirty for this range */
>> +            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
>> +                                                pfn_count * page_size,
>> +                                                DIRTY_MEMORY_MIGRATION);
> seesm pfn_count here is not right

Changing it to total_pfns in next version

Thanks,
Kirti

>> +            break;
>> +        }
>> +
>> +        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
>> +
>> +        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
>> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                             data_offset));
>> +        if (ret != sizeof(data_offset)) {
>> +            error_report("%s: Failed to get migration buffer data offset %d",
>> +                         vbasedev->name, ret);
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        if (region->mmaps) {
>> +            buf = find_data_region(region, data_offset, bitmap_size);
>> +        }
>> +
>> +        buffer_mmaped = (buf != NULL) ? true : false;
>> +
>> +        if (!buffer_mmaped) {
>> +            buf = g_try_malloc0(bitmap_size);
>> +            if (!buf) {
>> +                error_report("%s: Error allocating buffer ", __func__);
>> +                goto dpl_unlock;
>> +            }
>> +
>> +            ret = pread(vbasedev->fd, buf, bitmap_size,
>> +                        region->fd_offset + data_offset);
>> +            if (ret != bitmap_size) {
>> +                error_report("%s: Failed to get dirty pages bitmap %d",
>> +                             vbasedev->name, ret);
>> +                g_free(buf);
>> +                goto dpl_unlock;
>> +            }
>> +        }
>> +
>> +        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
>> +                                               (start_pfn + count) * page_size,
>> +                                                copied_pfns);
>> +        count      += copied_pfns;
>> +        total_pfns -= copied_pfns;
>> +
>> +        if (!buffer_mmaped) {
>> +            g_free(buf);
>> +        }
>> +    }
>> +
>> +    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
>> +                                   page_size);
>> +
>> +dpl_unlock:
>> +    qemu_mutex_unlock(&migration->lock);
>> +}
>> +
>>  /* ---------------------------------------------------------------------- */
>>  
>>  static int vfio_save_setup(QEMUFile *f, void *opaque)
>> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
>> index ac065b559f4e..414a5e69ec5e 100644
>> --- a/hw/vfio/trace-events
>> +++ b/hw/vfio/trace-events
>> @@ -160,3 +160,4 @@ vfio_save_complete_precopy(char *name) " (%s)"
>>  vfio_load_device_config_state(char *name) " (%s)"
>>  vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
>>  vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
>> +vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
>> diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
>> index a022484d2636..dc1b83a0b4ef 100644
>> --- a/include/hw/vfio/vfio-common.h
>> +++ b/include/hw/vfio/vfio-common.h
>> @@ -222,5 +222,7 @@ int vfio_spapr_remove_window(VFIOContainer *container,
>>  
>>  int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
>>  void vfio_migration_finalize(VFIODevice *vbasedev);
>> +void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
>> +                               uint64_t pfn_count, uint64_t page_size);
>>  
>>  #endif /* HW_VFIO_VFIO_COMMON_H */
>> -- 
>> 2.7.0
>>
Yan Zhao July 19, 2019, 1:24 a.m. UTC | #3
On Fri, Jul 19, 2019 at 02:39:10AM +0800, Kirti Wankhede wrote:
> 
> 
> On 7/12/2019 6:03 AM, Yan Zhao wrote:
> > On Tue, Jul 09, 2019 at 05:49:18PM +0800, Kirti Wankhede wrote:
> >> Dirty page tracking (.log_sync) is part of RAM copying state, where
> >> vendor driver provides the bitmap of pages which are dirtied by vendor
> >> driver through migration region and as part of RAM copy, those pages
> >> gets copied to file stream.
> >>
> >> To get dirty page bitmap:
> >> - write start address, page_size and pfn count.
> >> - read count of pfns copied.
> >>     - Vendor driver should return 0 if driver doesn't have any page to
> >>       report dirty in given range.
> >>     - Vendor driver should return -1 to mark all pages dirty for given range.
> >> - read data_offset, where vendor driver has written bitmap.
> >> - read bitmap from the region or mmaped part of the region.
> >> - Iterate above steps till page bitmap for all requested pfns are copied.
> >>
> >> Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
> >> Reviewed-by: Neo Jia <cjia@nvidia.com>
> >> ---
> >>  hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
> >>  hw/vfio/trace-events          |   1 +
> >>  include/hw/vfio/vfio-common.h |   2 +
> >>  3 files changed, 126 insertions(+)
> >>
> >> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> >> index 5fb4c5329ede..ca1a8c0f5f1f 100644
> >> --- a/hw/vfio/migration.c
> >> +++ b/hw/vfio/migration.c
> >> @@ -269,6 +269,129 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
> >>      return qemu_file_get_error(f);
> >>  }
> >>  
> >> +void vfio_get_dirty_page_list(VFIODevice *vbasedev,
> >> +                              uint64_t start_pfn,
> >> +                              uint64_t pfn_count,
> >> +                              uint64_t page_size)
> >> +{
> >> +    VFIOMigration *migration = vbasedev->migration;
> >> +    VFIORegion *region = &migration->region.buffer;
> >> +    uint64_t count = 0;
> >> +    int64_t copied_pfns = 0;
> >> +    int64_t total_pfns = pfn_count;
> >> +    int ret;
> >> +
> >> +    qemu_mutex_lock(&migration->lock);
> >> +
> >> +    while (total_pfns > 0) {
> >> +        uint64_t bitmap_size, data_offset = 0;
> >> +        uint64_t start = start_pfn + count;
> >> +        void *buf = NULL;
> >> +        bool buffer_mmaped = false;
> >> +
> >> +        ret = pwrite(vbasedev->fd, &start, sizeof(start),
> >> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> >> +                                              start_pfn));
> >> +        if (ret < 0) {
> >> +            error_report("%s: Failed to set dirty pages start address %d %s",
> >> +                         vbasedev->name, ret, strerror(errno));
> >> +            goto dpl_unlock;
> >> +        }
> >> +
> >> +        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
> >> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> >> +                                              page_size));
> >> +        if (ret < 0) {
> >> +            error_report("%s: Failed to set dirty page size %d %s",
> >> +                         vbasedev->name, ret, strerror(errno));
> >> +            goto dpl_unlock;
> >> +        }
> >> +
> >> +        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
> >> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> >> +                                              total_pfns));
> >> +        if (ret < 0) {
> >> +            error_report("%s: Failed to set dirty page total pfns %d %s",
> >> +                         vbasedev->name, ret, strerror(errno));
> >> +            goto dpl_unlock;
> >> +        }
> >> +
> >> +        /* Read copied dirty pfns */
> >> +        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
> >> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> >> +                                             copied_pfns));
> >> +        if (ret < 0) {
> >> +            error_report("%s: Failed to get dirty pages bitmap count %d %s",
> >> +                         vbasedev->name, ret, strerror(errno));
> >> +            goto dpl_unlock;
> >> +        }
> >> +
> >> +        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
> >> +            /*
> >> +             * copied_pfns could be 0 if driver doesn't have any page to
> >> +             * report dirty in given range
> >> +             */
> >> +            break;
> >> +        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
> >> +            /* Mark all pages dirty for this range */
> >> +            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
> >> +                                                pfn_count * page_size,
> >> +                                                DIRTY_MEMORY_MIGRATION);
> > seesm pfn_count here is not right
> 
> Changing it to total_pfns in next version
>
if it's total_pfns, then it cannot be in the loop, right?

Thanks
Yan

> Thanks,
> Kirti
> 
> >> +            break;
> >> +        }
> >> +
> >> +        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
> >> +
> >> +        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
> >> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> >> +                                             data_offset));
> >> +        if (ret != sizeof(data_offset)) {
> >> +            error_report("%s: Failed to get migration buffer data offset %d",
> >> +                         vbasedev->name, ret);
> >> +            goto dpl_unlock;
> >> +        }
> >> +
> >> +        if (region->mmaps) {
> >> +            buf = find_data_region(region, data_offset, bitmap_size);
> >> +        }
> >> +
> >> +        buffer_mmaped = (buf != NULL) ? true : false;
> >> +
> >> +        if (!buffer_mmaped) {
> >> +            buf = g_try_malloc0(bitmap_size);
> >> +            if (!buf) {
> >> +                error_report("%s: Error allocating buffer ", __func__);
> >> +                goto dpl_unlock;
> >> +            }
> >> +
> >> +            ret = pread(vbasedev->fd, buf, bitmap_size,
> >> +                        region->fd_offset + data_offset);
> >> +            if (ret != bitmap_size) {
> >> +                error_report("%s: Failed to get dirty pages bitmap %d",
> >> +                             vbasedev->name, ret);
> >> +                g_free(buf);
> >> +                goto dpl_unlock;
> >> +            }
> >> +        }
> >> +
> >> +        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
> >> +                                               (start_pfn + count) * page_size,
> >> +                                                copied_pfns);
> >> +        count      += copied_pfns;
> >> +        total_pfns -= copied_pfns;
> >> +
> >> +        if (!buffer_mmaped) {
> >> +            g_free(buf);
> >> +        }
> >> +    }
> >> +
> >> +    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
> >> +                                   page_size);
> >> +
> >> +dpl_unlock:
> >> +    qemu_mutex_unlock(&migration->lock);
> >> +}
> >> +
> >>  /* ---------------------------------------------------------------------- */
> >>  
> >>  static int vfio_save_setup(QEMUFile *f, void *opaque)
> >> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
> >> index ac065b559f4e..414a5e69ec5e 100644
> >> --- a/hw/vfio/trace-events
> >> +++ b/hw/vfio/trace-events
> >> @@ -160,3 +160,4 @@ vfio_save_complete_precopy(char *name) " (%s)"
> >>  vfio_load_device_config_state(char *name) " (%s)"
> >>  vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
> >>  vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
> >> +vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
> >> diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
> >> index a022484d2636..dc1b83a0b4ef 100644
> >> --- a/include/hw/vfio/vfio-common.h
> >> +++ b/include/hw/vfio/vfio-common.h
> >> @@ -222,5 +222,7 @@ int vfio_spapr_remove_window(VFIOContainer *container,
> >>  
> >>  int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
> >>  void vfio_migration_finalize(VFIODevice *vbasedev);
> >> +void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
> >> +                               uint64_t pfn_count, uint64_t page_size);
> >>  
> >>  #endif /* HW_VFIO_VFIO_COMMON_H */
> >> -- 
> >> 2.7.0
> >>
Yan Zhao July 22, 2019, 8:39 a.m. UTC | #4
On Tue, Jul 09, 2019 at 05:49:18PM +0800, Kirti Wankhede wrote:
> Dirty page tracking (.log_sync) is part of RAM copying state, where
> vendor driver provides the bitmap of pages which are dirtied by vendor
> driver through migration region and as part of RAM copy, those pages
> gets copied to file stream.
> 
> To get dirty page bitmap:
> - write start address, page_size and pfn count.
> - read count of pfns copied.
>     - Vendor driver should return 0 if driver doesn't have any page to
>       report dirty in given range.
>     - Vendor driver should return -1 to mark all pages dirty for given range.
> - read data_offset, where vendor driver has written bitmap.
> - read bitmap from the region or mmaped part of the region.
> - Iterate above steps till page bitmap for all requested pfns are copied.
> 
> Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
> Reviewed-by: Neo Jia <cjia@nvidia.com>
> ---
>  hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
>  hw/vfio/trace-events          |   1 +
>  include/hw/vfio/vfio-common.h |   2 +
>  3 files changed, 126 insertions(+)
> 
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 5fb4c5329ede..ca1a8c0f5f1f 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -269,6 +269,129 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
>      return qemu_file_get_error(f);
>  }
>  
> +void vfio_get_dirty_page_list(VFIODevice *vbasedev,
> +                              uint64_t start_pfn,
> +                              uint64_t pfn_count,
> +                              uint64_t page_size)
> +{
> +    VFIOMigration *migration = vbasedev->migration;
> +    VFIORegion *region = &migration->region.buffer;
> +    uint64_t count = 0;
> +    int64_t copied_pfns = 0;
> +    int64_t total_pfns = pfn_count;
> +    int ret;
> +
> +    qemu_mutex_lock(&migration->lock);
> +
> +    while (total_pfns > 0) {
> +        uint64_t bitmap_size, data_offset = 0;
> +        uint64_t start = start_pfn + count;
> +        void *buf = NULL;
> +        bool buffer_mmaped = false;
> +
> +        ret = pwrite(vbasedev->fd, &start, sizeof(start),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              start_pfn));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty pages start address %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              page_size));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty page size %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                              total_pfns));
> +        if (ret < 0) {
> +            error_report("%s: Failed to set dirty page total pfns %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        /* Read copied dirty pfns */
> +        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                             copied_pfns));
> +        if (ret < 0) {
> +            error_report("%s: Failed to get dirty pages bitmap count %d %s",
> +                         vbasedev->name, ret, strerror(errno));
> +            goto dpl_unlock;
> +        }
> +
> +        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
> +            /*
> +             * copied_pfns could be 0 if driver doesn't have any page to
> +             * report dirty in given range
> +             */
> +            break;
> +        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
> +            /* Mark all pages dirty for this range */
> +            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
> +                                                pfn_count * page_size,
> +                                                DIRTY_MEMORY_MIGRATION);
> +            break;
> +        }
> +
> +        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
hi Kirti

why bitmap_size is 
(BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long).
why it's not
BITS_TO_LONGS(copied_pfns) * sizeof(unsigned long) ?

Thanks
Yan

> +        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
> +                                             data_offset));
> +        if (ret != sizeof(data_offset)) {
> +            error_report("%s: Failed to get migration buffer data offset %d",
> +                         vbasedev->name, ret);
> +            goto dpl_unlock;
> +        }
> +
> +        if (region->mmaps) {
> +            buf = find_data_region(region, data_offset, bitmap_size);
> +        }
> +
> +        buffer_mmaped = (buf != NULL) ? true : false;
> +
> +        if (!buffer_mmaped) {
> +            buf = g_try_malloc0(bitmap_size);
> +            if (!buf) {
> +                error_report("%s: Error allocating buffer ", __func__);
> +                goto dpl_unlock;
> +            }
> +
> +            ret = pread(vbasedev->fd, buf, bitmap_size,
> +                        region->fd_offset + data_offset);
> +            if (ret != bitmap_size) {
> +                error_report("%s: Failed to get dirty pages bitmap %d",
> +                             vbasedev->name, ret);
> +                g_free(buf);
> +                goto dpl_unlock;
> +            }
> +        }
> +
> +        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
> +                                               (start_pfn + count) * page_size,
> +                                                copied_pfns);
> +        count      += copied_pfns;
> +        total_pfns -= copied_pfns;
> +
> +        if (!buffer_mmaped) {
> +            g_free(buf);
> +        }
> +    }
> +
> +    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
> +                                   page_size);
> +
> +dpl_unlock:
> +    qemu_mutex_unlock(&migration->lock);
> +}
> +
>  /* ---------------------------------------------------------------------- */
>  
>  static int vfio_save_setup(QEMUFile *f, void *opaque)
> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
> index ac065b559f4e..414a5e69ec5e 100644
> --- a/hw/vfio/trace-events
> +++ b/hw/vfio/trace-events
> @@ -160,3 +160,4 @@ vfio_save_complete_precopy(char *name) " (%s)"
>  vfio_load_device_config_state(char *name) " (%s)"
>  vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
>  vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
> +vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
> diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
> index a022484d2636..dc1b83a0b4ef 100644
> --- a/include/hw/vfio/vfio-common.h
> +++ b/include/hw/vfio/vfio-common.h
> @@ -222,5 +222,7 @@ int vfio_spapr_remove_window(VFIOContainer *container,
>  
>  int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
>  void vfio_migration_finalize(VFIODevice *vbasedev);
> +void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
> +                               uint64_t pfn_count, uint64_t page_size);
>  
>  #endif /* HW_VFIO_VFIO_COMMON_H */
> -- 
> 2.7.0
>
Kirti Wankhede Aug. 20, 2019, 8:34 p.m. UTC | #5
On 7/22/2019 2:09 PM, Yan Zhao wrote:
> On Tue, Jul 09, 2019 at 05:49:18PM +0800, Kirti Wankhede wrote:
>> Dirty page tracking (.log_sync) is part of RAM copying state, where
>> vendor driver provides the bitmap of pages which are dirtied by vendor
>> driver through migration region and as part of RAM copy, those pages
>> gets copied to file stream.
>>
>> To get dirty page bitmap:
>> - write start address, page_size and pfn count.
>> - read count of pfns copied.
>>     - Vendor driver should return 0 if driver doesn't have any page to
>>       report dirty in given range.
>>     - Vendor driver should return -1 to mark all pages dirty for given range.
>> - read data_offset, where vendor driver has written bitmap.
>> - read bitmap from the region or mmaped part of the region.
>> - Iterate above steps till page bitmap for all requested pfns are copied.
>>
>> Signed-off-by: Kirti Wankhede <kwankhede@nvidia.com>
>> Reviewed-by: Neo Jia <cjia@nvidia.com>
>> ---
>>  hw/vfio/migration.c           | 123 ++++++++++++++++++++++++++++++++++++++++++
>>  hw/vfio/trace-events          |   1 +
>>  include/hw/vfio/vfio-common.h |   2 +
>>  3 files changed, 126 insertions(+)
>>
>> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
>> index 5fb4c5329ede..ca1a8c0f5f1f 100644
>> --- a/hw/vfio/migration.c
>> +++ b/hw/vfio/migration.c
>> @@ -269,6 +269,129 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
>>      return qemu_file_get_error(f);
>>  }
>>  
>> +void vfio_get_dirty_page_list(VFIODevice *vbasedev,
>> +                              uint64_t start_pfn,
>> +                              uint64_t pfn_count,
>> +                              uint64_t page_size)
>> +{
>> +    VFIOMigration *migration = vbasedev->migration;
>> +    VFIORegion *region = &migration->region.buffer;
>> +    uint64_t count = 0;
>> +    int64_t copied_pfns = 0;
>> +    int64_t total_pfns = pfn_count;
>> +    int ret;
>> +
>> +    qemu_mutex_lock(&migration->lock);
>> +
>> +    while (total_pfns > 0) {
>> +        uint64_t bitmap_size, data_offset = 0;
>> +        uint64_t start = start_pfn + count;
>> +        void *buf = NULL;
>> +        bool buffer_mmaped = false;
>> +
>> +        ret = pwrite(vbasedev->fd, &start, sizeof(start),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              start_pfn));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty pages start address %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              page_size));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty page size %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
>> +                 region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                              total_pfns));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to set dirty page total pfns %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        /* Read copied dirty pfns */
>> +        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
>> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                             copied_pfns));
>> +        if (ret < 0) {
>> +            error_report("%s: Failed to get dirty pages bitmap count %d %s",
>> +                         vbasedev->name, ret, strerror(errno));
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
>> +            /*
>> +             * copied_pfns could be 0 if driver doesn't have any page to
>> +             * report dirty in given range
>> +             */
>> +            break;
>> +        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
>> +            /* Mark all pages dirty for this range */
>> +            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
>> +                                                pfn_count * page_size,
>> +                                                DIRTY_MEMORY_MIGRATION);
>> +            break;
>> +        }
>> +
>> +        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
> hi Kirti
> 
> why bitmap_size is 
> (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long).
> why it's not
> BITS_TO_LONGS(copied_pfns) * sizeof(unsigned long) ?
> 

It should be later. I'll update in next version.

Thanks,
Kirti


> Thanks
> Yan
> 
>> +        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
>> +                region->fd_offset + offsetof(struct vfio_device_migration_info,
>> +                                             data_offset));
>> +        if (ret != sizeof(data_offset)) {
>> +            error_report("%s: Failed to get migration buffer data offset %d",
>> +                         vbasedev->name, ret);
>> +            goto dpl_unlock;
>> +        }
>> +
>> +        if (region->mmaps) {
>> +            buf = find_data_region(region, data_offset, bitmap_size);
>> +        }
>> +
>> +        buffer_mmaped = (buf != NULL) ? true : false;
>> +
>> +        if (!buffer_mmaped) {
>> +            buf = g_try_malloc0(bitmap_size);
>> +            if (!buf) {
>> +                error_report("%s: Error allocating buffer ", __func__);
>> +                goto dpl_unlock;
>> +            }
>> +
>> +            ret = pread(vbasedev->fd, buf, bitmap_size,
>> +                        region->fd_offset + data_offset);
>> +            if (ret != bitmap_size) {
>> +                error_report("%s: Failed to get dirty pages bitmap %d",
>> +                             vbasedev->name, ret);
>> +                g_free(buf);
>> +                goto dpl_unlock;
>> +            }
>> +        }
>> +
>> +        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
>> +                                               (start_pfn + count) * page_size,
>> +                                                copied_pfns);
>> +        count      += copied_pfns;
>> +        total_pfns -= copied_pfns;
>> +
>> +        if (!buffer_mmaped) {
>> +            g_free(buf);
>> +        }
>> +    }
>> +
>> +    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
>> +                                   page_size);
>> +
>> +dpl_unlock:
>> +    qemu_mutex_unlock(&migration->lock);
>> +}
>> +
>>  /* ---------------------------------------------------------------------- */
>>  
>>  static int vfio_save_setup(QEMUFile *f, void *opaque)
>> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
>> index ac065b559f4e..414a5e69ec5e 100644
>> --- a/hw/vfio/trace-events
>> +++ b/hw/vfio/trace-events
>> @@ -160,3 +160,4 @@ vfio_save_complete_precopy(char *name) " (%s)"
>>  vfio_load_device_config_state(char *name) " (%s)"
>>  vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
>>  vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
>> +vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
>> diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
>> index a022484d2636..dc1b83a0b4ef 100644
>> --- a/include/hw/vfio/vfio-common.h
>> +++ b/include/hw/vfio/vfio-common.h
>> @@ -222,5 +222,7 @@ int vfio_spapr_remove_window(VFIOContainer *container,
>>  
>>  int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
>>  void vfio_migration_finalize(VFIODevice *vbasedev);
>> +void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
>> +                               uint64_t pfn_count, uint64_t page_size);
>>  
>>  #endif /* HW_VFIO_VFIO_COMMON_H */
>> -- 
>> 2.7.0
>>
>
diff mbox series

Patch

diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 5fb4c5329ede..ca1a8c0f5f1f 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -269,6 +269,129 @@  static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
     return qemu_file_get_error(f);
 }
 
+void vfio_get_dirty_page_list(VFIODevice *vbasedev,
+                              uint64_t start_pfn,
+                              uint64_t pfn_count,
+                              uint64_t page_size)
+{
+    VFIOMigration *migration = vbasedev->migration;
+    VFIORegion *region = &migration->region.buffer;
+    uint64_t count = 0;
+    int64_t copied_pfns = 0;
+    int64_t total_pfns = pfn_count;
+    int ret;
+
+    qemu_mutex_lock(&migration->lock);
+
+    while (total_pfns > 0) {
+        uint64_t bitmap_size, data_offset = 0;
+        uint64_t start = start_pfn + count;
+        void *buf = NULL;
+        bool buffer_mmaped = false;
+
+        ret = pwrite(vbasedev->fd, &start, sizeof(start),
+                 region->fd_offset + offsetof(struct vfio_device_migration_info,
+                                              start_pfn));
+        if (ret < 0) {
+            error_report("%s: Failed to set dirty pages start address %d %s",
+                         vbasedev->name, ret, strerror(errno));
+            goto dpl_unlock;
+        }
+
+        ret = pwrite(vbasedev->fd, &page_size, sizeof(page_size),
+                 region->fd_offset + offsetof(struct vfio_device_migration_info,
+                                              page_size));
+        if (ret < 0) {
+            error_report("%s: Failed to set dirty page size %d %s",
+                         vbasedev->name, ret, strerror(errno));
+            goto dpl_unlock;
+        }
+
+        ret = pwrite(vbasedev->fd, &total_pfns, sizeof(total_pfns),
+                 region->fd_offset + offsetof(struct vfio_device_migration_info,
+                                              total_pfns));
+        if (ret < 0) {
+            error_report("%s: Failed to set dirty page total pfns %d %s",
+                         vbasedev->name, ret, strerror(errno));
+            goto dpl_unlock;
+        }
+
+        /* Read copied dirty pfns */
+        ret = pread(vbasedev->fd, &copied_pfns, sizeof(copied_pfns),
+                region->fd_offset + offsetof(struct vfio_device_migration_info,
+                                             copied_pfns));
+        if (ret < 0) {
+            error_report("%s: Failed to get dirty pages bitmap count %d %s",
+                         vbasedev->name, ret, strerror(errno));
+            goto dpl_unlock;
+        }
+
+        if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_NONE) {
+            /*
+             * copied_pfns could be 0 if driver doesn't have any page to
+             * report dirty in given range
+             */
+            break;
+        } else if (copied_pfns == VFIO_DEVICE_DIRTY_PFNS_ALL) {
+            /* Mark all pages dirty for this range */
+            cpu_physical_memory_set_dirty_range(start_pfn * page_size,
+                                                pfn_count * page_size,
+                                                DIRTY_MEMORY_MIGRATION);
+            break;
+        }
+
+        bitmap_size = (BITS_TO_LONGS(copied_pfns) + 1) * sizeof(unsigned long);
+
+        ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
+                region->fd_offset + offsetof(struct vfio_device_migration_info,
+                                             data_offset));
+        if (ret != sizeof(data_offset)) {
+            error_report("%s: Failed to get migration buffer data offset %d",
+                         vbasedev->name, ret);
+            goto dpl_unlock;
+        }
+
+        if (region->mmaps) {
+            buf = find_data_region(region, data_offset, bitmap_size);
+        }
+
+        buffer_mmaped = (buf != NULL) ? true : false;
+
+        if (!buffer_mmaped) {
+            buf = g_try_malloc0(bitmap_size);
+            if (!buf) {
+                error_report("%s: Error allocating buffer ", __func__);
+                goto dpl_unlock;
+            }
+
+            ret = pread(vbasedev->fd, buf, bitmap_size,
+                        region->fd_offset + data_offset);
+            if (ret != bitmap_size) {
+                error_report("%s: Failed to get dirty pages bitmap %d",
+                             vbasedev->name, ret);
+                g_free(buf);
+                goto dpl_unlock;
+            }
+        }
+
+        cpu_physical_memory_set_dirty_lebitmap((unsigned long *)buf,
+                                               (start_pfn + count) * page_size,
+                                                copied_pfns);
+        count      += copied_pfns;
+        total_pfns -= copied_pfns;
+
+        if (!buffer_mmaped) {
+            g_free(buf);
+        }
+    }
+
+    trace_vfio_get_dirty_page_list(vbasedev->name, start_pfn, pfn_count,
+                                   page_size);
+
+dpl_unlock:
+    qemu_mutex_unlock(&migration->lock);
+}
+
 /* ---------------------------------------------------------------------- */
 
 static int vfio_save_setup(QEMUFile *f, void *opaque)
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index ac065b559f4e..414a5e69ec5e 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -160,3 +160,4 @@  vfio_save_complete_precopy(char *name) " (%s)"
 vfio_load_device_config_state(char *name) " (%s)"
 vfio_load_state(char *name, uint64_t data) " (%s) data 0x%"PRIx64
 vfio_load_state_device_data(char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64
+vfio_get_dirty_page_list(char *name, uint64_t start, uint64_t pfn_count, uint64_t page_size) " (%s) start 0x%"PRIx64" pfn_count 0x%"PRIx64 " page size 0x%"PRIx64
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index a022484d2636..dc1b83a0b4ef 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -222,5 +222,7 @@  int vfio_spapr_remove_window(VFIOContainer *container,
 
 int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
 void vfio_migration_finalize(VFIODevice *vbasedev);
+void vfio_get_dirty_page_list(VFIODevice *vbasedev, uint64_t start_pfn,
+                               uint64_t pfn_count, uint64_t page_size);
 
 #endif /* HW_VFIO_VFIO_COMMON_H */