diff mbox series

vfio: Add an option to get migration data size

Message ID 20221020132109.112708-1-yishaih@nvidia.com (mailing list archive)
State New, archived
Headers show
Series vfio: Add an option to get migration data size | expand

Commit Message

Yishai Hadas Oct. 20, 2022, 1:21 p.m. UTC
Add an option to get migration data size by introducing a new migration
feature named VFIO_DEVICE_FEATURE_MIG_DATA_SIZE.

Upon VFIO_DEVICE_FEATURE_GET the estimated data length that will be
required to complete STOP_COPY is returned.

This option may better enable user space to consider before moving to
STOP_COPY whether it can meet the downtime SLA based on the returned
data.

The patch also includes the implementation for mlx5 and hisi for this
new option to make it feature complete for the existing drivers in this
area.

Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
---
 .../vfio/pci/hisilicon/hisi_acc_vfio_pci.c    |  9 ++++++
 drivers/vfio/pci/mlx5/main.c                  | 18 +++++++++++
 drivers/vfio/vfio_main.c                      | 32 +++++++++++++++++++
 include/linux/vfio.h                          |  5 +++
 include/uapi/linux/vfio.h                     | 13 ++++++++
 5 files changed, 77 insertions(+)

Comments

Alex Williamson Oct. 25, 2022, 1:04 p.m. UTC | #1
On Thu, 20 Oct 2022 16:21:09 +0300
Yishai Hadas <yishaih@nvidia.com> wrote:

> Add an option to get migration data size by introducing a new migration
> feature named VFIO_DEVICE_FEATURE_MIG_DATA_SIZE.
> 
> Upon VFIO_DEVICE_FEATURE_GET the estimated data length that will be
> required to complete STOP_COPY is returned.
> 
> This option may better enable user space to consider before moving to
> STOP_COPY whether it can meet the downtime SLA based on the returned
> data.
> 
> The patch also includes the implementation for mlx5 and hisi for this
> new option to make it feature complete for the existing drivers in this
> area.
> 
> Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
> ---
>  .../vfio/pci/hisilicon/hisi_acc_vfio_pci.c    |  9 ++++++
>  drivers/vfio/pci/mlx5/main.c                  | 18 +++++++++++
>  drivers/vfio/vfio_main.c                      | 32 +++++++++++++++++++
>  include/linux/vfio.h                          |  5 +++
>  include/uapi/linux/vfio.h                     | 13 ++++++++
>  5 files changed, 77 insertions(+)
> 
> diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
> index 39eeca18a0f7..0c0c0c7f0521 100644
> --- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
> +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
> @@ -957,6 +957,14 @@ hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev,
>  	return res;
>  }
>  
> +static int
> +hisi_acc_vfio_pci_get_data_size(struct vfio_device *vdev,
> +				unsigned long *stop_copy_length)
> +{
> +	*stop_copy_length = sizeof(struct acc_vf_data);
> +	return 0;
> +}
> +
>  static int
>  hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev,
>  				   enum vfio_device_mig_state *curr_state)
> @@ -1213,6 +1221,7 @@ static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev)
>  static const struct vfio_migration_ops hisi_acc_vfio_pci_migrn_state_ops = {
>  	.migration_set_state = hisi_acc_vfio_pci_set_device_state,
>  	.migration_get_state = hisi_acc_vfio_pci_get_device_state,
> +	.migration_get_data_size = hisi_acc_vfio_pci_get_data_size,
>  };
>  
>  static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev)
> diff --git a/drivers/vfio/pci/mlx5/main.c b/drivers/vfio/pci/mlx5/main.c
> index fd6ccb8454a2..4c7a39ffd247 100644
> --- a/drivers/vfio/pci/mlx5/main.c
> +++ b/drivers/vfio/pci/mlx5/main.c
> @@ -512,6 +512,23 @@ mlx5vf_pci_set_device_state(struct vfio_device *vdev,
>  	return res;
>  }
>  
> +static int mlx5vf_pci_get_data_size(struct vfio_device *vdev,
> +				    unsigned long *stop_copy_length)
> +{
> +	struct mlx5vf_pci_core_device *mvdev = container_of(
> +		vdev, struct mlx5vf_pci_core_device, core_device.vdev);
> +	size_t state_size;
> +	int ret;
> +
> +	mutex_lock(&mvdev->state_mutex);
> +	ret = mlx5vf_cmd_query_vhca_migration_state(mvdev,
> +						    &state_size);
> +	if (!ret)
> +		*stop_copy_length = state_size;
> +	mlx5vf_state_mutex_unlock(mvdev);
> +	return ret;
> +}
> +
>  static int mlx5vf_pci_get_device_state(struct vfio_device *vdev,
>  				       enum vfio_device_mig_state *curr_state)
>  {
> @@ -577,6 +594,7 @@ static void mlx5vf_pci_close_device(struct vfio_device *core_vdev)
>  static const struct vfio_migration_ops mlx5vf_pci_mig_ops = {
>  	.migration_set_state = mlx5vf_pci_set_device_state,
>  	.migration_get_state = mlx5vf_pci_get_device_state,
> +	.migration_get_data_size = mlx5vf_pci_get_data_size,
>  };
>  
>  static const struct vfio_log_ops mlx5vf_pci_log_ops = {
> diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
> index 2d168793d4e1..b118e7b1bc59 100644
> --- a/drivers/vfio/vfio_main.c
> +++ b/drivers/vfio/vfio_main.c
> @@ -1256,6 +1256,34 @@ vfio_ioctl_device_feature_mig_device_state(struct vfio_device *device,
>  	return 0;
>  }
>  
> +static int
> +vfio_ioctl_device_feature_migration_data_size(struct vfio_device *device,
> +					      u32 flags, void __user *arg,
> +					      size_t argsz)
> +{
> +	struct vfio_device_feature_mig_data_size data_size = {};
> +	unsigned long stop_copy_length;
> +	int ret;
> +
> +	if (!device->mig_ops)
> +		return -ENOTTY;
> +
> +	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
> +				 sizeof(data_size));
> +	if (ret != 1)
> +		return ret;
> +
> +	ret = device->mig_ops->migration_get_data_size(device, &stop_copy_length);
> +	if (ret)
> +		return ret;
> +
> +	data_size.stop_copy_length = stop_copy_length;
> +	if (copy_to_user(arg, &data_size, sizeof(data_size)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
>  static int vfio_ioctl_device_feature_migration(struct vfio_device *device,
>  					       u32 flags, void __user *arg,
>  					       size_t argsz)
> @@ -1483,6 +1511,10 @@ static int vfio_ioctl_device_feature(struct vfio_device *device,
>  		return vfio_ioctl_device_feature_logging_report(
>  			device, feature.flags, arg->data,
>  			feature.argsz - minsz);
> +	case VFIO_DEVICE_FEATURE_MIG_DATA_SIZE:
> +		return vfio_ioctl_device_feature_migration_data_size(
> +			device, feature.flags, arg->data,
> +			feature.argsz - minsz);
>  	default:
>  		if (unlikely(!device->ops->device_feature))
>  			return -EINVAL;
> diff --git a/include/linux/vfio.h b/include/linux/vfio.h
> index e7cebeb875dd..5509451ae709 100644
> --- a/include/linux/vfio.h
> +++ b/include/linux/vfio.h
> @@ -107,6 +107,9 @@ struct vfio_device_ops {
>   * @migration_get_state: Optional callback to get the migration state for
>   *         devices that support migration. It's mandatory for
>   *         VFIO_DEVICE_FEATURE_MIGRATION migration support.
> + * @migration_get_data_size: Optional callback to get the estimated data
> + *          length that will be required to complete stop copy. It's mandatory for
> + *          VFIO_DEVICE_FEATURE_MIGRATION migration support.
>   */

This is listed as an optional callback, but we call it
deterministically and there's no added check like there is for
set/get_state in vfio_pci_core_register_device().  Thanks,

Alex


>  struct vfio_migration_ops {
>  	struct file *(*migration_set_state)(
> @@ -114,6 +117,8 @@ struct vfio_migration_ops {
>  		enum vfio_device_mig_state new_state);
>  	int (*migration_get_state)(struct vfio_device *device,
>  				   enum vfio_device_mig_state *curr_state);
> +	int (*migration_get_data_size)(struct vfio_device *device,
> +				       unsigned long *stop_copy_length);
>  };
>  
>  /**
> diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> index d7d8e0922376..3e45dbaf190e 100644
> --- a/include/uapi/linux/vfio.h
> +++ b/include/uapi/linux/vfio.h
> @@ -1128,6 +1128,19 @@ struct vfio_device_feature_dma_logging_report {
>  
>  #define VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT 8
>  
> +/*
> + * Upon VFIO_DEVICE_FEATURE_GET read back the estimated data length that will
> + * be required to complete stop copy.
> + *
> + * Note: Can be called on each device state.
> + */
> +
> +struct vfio_device_feature_mig_data_size {
> +	__aligned_u64 stop_copy_length;
> +};
> +
> +#define VFIO_DEVICE_FEATURE_MIG_DATA_SIZE 9
> +
>  /* -------- API for Type1 VFIO IOMMU -------- */
>  
>  /**
Yishai Hadas Oct. 25, 2022, 2:43 p.m. UTC | #2
On 25/10/2022 16:04, Alex Williamson wrote:
> On Thu, 20 Oct 2022 16:21:09 +0300
> Yishai Hadas <yishaih@nvidia.com> wrote:
>
>> Add an option to get migration data size by introducing a new migration
>> feature named VFIO_DEVICE_FEATURE_MIG_DATA_SIZE.
>>
>> Upon VFIO_DEVICE_FEATURE_GET the estimated data length that will be
>> required to complete STOP_COPY is returned.
>>
>> This option may better enable user space to consider before moving to
>> STOP_COPY whether it can meet the downtime SLA based on the returned
>> data.
>>
>> The patch also includes the implementation for mlx5 and hisi for this
>> new option to make it feature complete for the existing drivers in this
>> area.
>>
>> Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
>> ---
>>   .../vfio/pci/hisilicon/hisi_acc_vfio_pci.c    |  9 ++++++
>>   drivers/vfio/pci/mlx5/main.c                  | 18 +++++++++++
>>   drivers/vfio/vfio_main.c                      | 32 +++++++++++++++++++
>>   include/linux/vfio.h                          |  5 +++
>>   include/uapi/linux/vfio.h                     | 13 ++++++++
>>   5 files changed, 77 insertions(+)
>>
>> diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
>> index 39eeca18a0f7..0c0c0c7f0521 100644
>> --- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
>> +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
>> @@ -957,6 +957,14 @@ hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev,
>>   	return res;
>>   }
>>   
>> +static int
>> +hisi_acc_vfio_pci_get_data_size(struct vfio_device *vdev,
>> +				unsigned long *stop_copy_length)
>> +{
>> +	*stop_copy_length = sizeof(struct acc_vf_data);
>> +	return 0;
>> +}
>> +
>>   static int
>>   hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev,
>>   				   enum vfio_device_mig_state *curr_state)
>> @@ -1213,6 +1221,7 @@ static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev)
>>   static const struct vfio_migration_ops hisi_acc_vfio_pci_migrn_state_ops = {
>>   	.migration_set_state = hisi_acc_vfio_pci_set_device_state,
>>   	.migration_get_state = hisi_acc_vfio_pci_get_device_state,
>> +	.migration_get_data_size = hisi_acc_vfio_pci_get_data_size,
>>   };
>>   
>>   static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev)
>> diff --git a/drivers/vfio/pci/mlx5/main.c b/drivers/vfio/pci/mlx5/main.c
>> index fd6ccb8454a2..4c7a39ffd247 100644
>> --- a/drivers/vfio/pci/mlx5/main.c
>> +++ b/drivers/vfio/pci/mlx5/main.c
>> @@ -512,6 +512,23 @@ mlx5vf_pci_set_device_state(struct vfio_device *vdev,
>>   	return res;
>>   }
>>   
>> +static int mlx5vf_pci_get_data_size(struct vfio_device *vdev,
>> +				    unsigned long *stop_copy_length)
>> +{
>> +	struct mlx5vf_pci_core_device *mvdev = container_of(
>> +		vdev, struct mlx5vf_pci_core_device, core_device.vdev);
>> +	size_t state_size;
>> +	int ret;
>> +
>> +	mutex_lock(&mvdev->state_mutex);
>> +	ret = mlx5vf_cmd_query_vhca_migration_state(mvdev,
>> +						    &state_size);
>> +	if (!ret)
>> +		*stop_copy_length = state_size;
>> +	mlx5vf_state_mutex_unlock(mvdev);
>> +	return ret;
>> +}
>> +
>>   static int mlx5vf_pci_get_device_state(struct vfio_device *vdev,
>>   				       enum vfio_device_mig_state *curr_state)
>>   {
>> @@ -577,6 +594,7 @@ static void mlx5vf_pci_close_device(struct vfio_device *core_vdev)
>>   static const struct vfio_migration_ops mlx5vf_pci_mig_ops = {
>>   	.migration_set_state = mlx5vf_pci_set_device_state,
>>   	.migration_get_state = mlx5vf_pci_get_device_state,
>> +	.migration_get_data_size = mlx5vf_pci_get_data_size,
>>   };
>>   
>>   static const struct vfio_log_ops mlx5vf_pci_log_ops = {
>> diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
>> index 2d168793d4e1..b118e7b1bc59 100644
>> --- a/drivers/vfio/vfio_main.c
>> +++ b/drivers/vfio/vfio_main.c
>> @@ -1256,6 +1256,34 @@ vfio_ioctl_device_feature_mig_device_state(struct vfio_device *device,
>>   	return 0;
>>   }
>>   
>> +static int
>> +vfio_ioctl_device_feature_migration_data_size(struct vfio_device *device,
>> +					      u32 flags, void __user *arg,
>> +					      size_t argsz)
>> +{
>> +	struct vfio_device_feature_mig_data_size data_size = {};
>> +	unsigned long stop_copy_length;
>> +	int ret;
>> +
>> +	if (!device->mig_ops)
>> +		return -ENOTTY;
>> +
>> +	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
>> +				 sizeof(data_size));
>> +	if (ret != 1)
>> +		return ret;
>> +
>> +	ret = device->mig_ops->migration_get_data_size(device, &stop_copy_length);
>> +	if (ret)
>> +		return ret;
>> +
>> +	data_size.stop_copy_length = stop_copy_length;
>> +	if (copy_to_user(arg, &data_size, sizeof(data_size)))
>> +		return -EFAULT;
>> +
>> +	return 0;
>> +}
>> +
>>   static int vfio_ioctl_device_feature_migration(struct vfio_device *device,
>>   					       u32 flags, void __user *arg,
>>   					       size_t argsz)
>> @@ -1483,6 +1511,10 @@ static int vfio_ioctl_device_feature(struct vfio_device *device,
>>   		return vfio_ioctl_device_feature_logging_report(
>>   			device, feature.flags, arg->data,
>>   			feature.argsz - minsz);
>> +	case VFIO_DEVICE_FEATURE_MIG_DATA_SIZE:
>> +		return vfio_ioctl_device_feature_migration_data_size(
>> +			device, feature.flags, arg->data,
>> +			feature.argsz - minsz);
>>   	default:
>>   		if (unlikely(!device->ops->device_feature))
>>   			return -EINVAL;
>> diff --git a/include/linux/vfio.h b/include/linux/vfio.h
>> index e7cebeb875dd..5509451ae709 100644
>> --- a/include/linux/vfio.h
>> +++ b/include/linux/vfio.h
>> @@ -107,6 +107,9 @@ struct vfio_device_ops {
>>    * @migration_get_state: Optional callback to get the migration state for
>>    *         devices that support migration. It's mandatory for
>>    *         VFIO_DEVICE_FEATURE_MIGRATION migration support.
>> + * @migration_get_data_size: Optional callback to get the estimated data
>> + *          length that will be required to complete stop copy. It's mandatory for
>> + *          VFIO_DEVICE_FEATURE_MIGRATION migration support.
>>    */
> This is listed as an optional callback, but we call it
> deterministically and there's no added check like there is for
> set/get_state in vfio_pci_core_register_device().  Thanks,

Right,

We may add as part of V1 the below chunk.

diff --git a/drivers/vfio/pci/vfio_pci_core.c 
b/drivers/vfio/pci/vfio_pci_core.c
index badc9d828cac..4d97ca66ba6c 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -2128,7 +2128,8 @@ int vfio_pci_core_register_device(struct 
vfio_pci_core_device *vdev)

         if (vdev->vdev.mig_ops) {
                 if (!(vdev->vdev.mig_ops->migration_get_state &&
- vdev->vdev.mig_ops->migration_set_state) ||
+ vdev->vdev.mig_ops->migration_set_state &&
+ vdev->vdev.mig_ops->migration_get_data_size) ||
                     !(vdev->vdev.migration_flags & 
VFIO_MIGRATION_STOP_COPY))
                         return -EINVAL;
         }

Yishai

> Alex
>
>
>>   struct vfio_migration_ops {
>>   	struct file *(*migration_set_state)(
>> @@ -114,6 +117,8 @@ struct vfio_migration_ops {
>>   		enum vfio_device_mig_state new_state);
>>   	int (*migration_get_state)(struct vfio_device *device,
>>   				   enum vfio_device_mig_state *curr_state);
>> +	int (*migration_get_data_size)(struct vfio_device *device,
>> +				       unsigned long *stop_copy_length);
>>   };
>>   
>>   /**
>> diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
>> index d7d8e0922376..3e45dbaf190e 100644
>> --- a/include/uapi/linux/vfio.h
>> +++ b/include/uapi/linux/vfio.h
>> @@ -1128,6 +1128,19 @@ struct vfio_device_feature_dma_logging_report {
>>   
>>   #define VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT 8
>>   
>> +/*
>> + * Upon VFIO_DEVICE_FEATURE_GET read back the estimated data length that will
>> + * be required to complete stop copy.
>> + *
>> + * Note: Can be called on each device state.
>> + */
>> +
>> +struct vfio_device_feature_mig_data_size {
>> +	__aligned_u64 stop_copy_length;
>> +};
>> +
>> +#define VFIO_DEVICE_FEATURE_MIG_DATA_SIZE 9
>> +
>>   /* -------- API for Type1 VFIO IOMMU -------- */
>>   
>>   /**
diff mbox series

Patch

diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
index 39eeca18a0f7..0c0c0c7f0521 100644
--- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
+++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
@@ -957,6 +957,14 @@  hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev,
 	return res;
 }
 
+static int
+hisi_acc_vfio_pci_get_data_size(struct vfio_device *vdev,
+				unsigned long *stop_copy_length)
+{
+	*stop_copy_length = sizeof(struct acc_vf_data);
+	return 0;
+}
+
 static int
 hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev,
 				   enum vfio_device_mig_state *curr_state)
@@ -1213,6 +1221,7 @@  static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev)
 static const struct vfio_migration_ops hisi_acc_vfio_pci_migrn_state_ops = {
 	.migration_set_state = hisi_acc_vfio_pci_set_device_state,
 	.migration_get_state = hisi_acc_vfio_pci_get_device_state,
+	.migration_get_data_size = hisi_acc_vfio_pci_get_data_size,
 };
 
 static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev)
diff --git a/drivers/vfio/pci/mlx5/main.c b/drivers/vfio/pci/mlx5/main.c
index fd6ccb8454a2..4c7a39ffd247 100644
--- a/drivers/vfio/pci/mlx5/main.c
+++ b/drivers/vfio/pci/mlx5/main.c
@@ -512,6 +512,23 @@  mlx5vf_pci_set_device_state(struct vfio_device *vdev,
 	return res;
 }
 
+static int mlx5vf_pci_get_data_size(struct vfio_device *vdev,
+				    unsigned long *stop_copy_length)
+{
+	struct mlx5vf_pci_core_device *mvdev = container_of(
+		vdev, struct mlx5vf_pci_core_device, core_device.vdev);
+	size_t state_size;
+	int ret;
+
+	mutex_lock(&mvdev->state_mutex);
+	ret = mlx5vf_cmd_query_vhca_migration_state(mvdev,
+						    &state_size);
+	if (!ret)
+		*stop_copy_length = state_size;
+	mlx5vf_state_mutex_unlock(mvdev);
+	return ret;
+}
+
 static int mlx5vf_pci_get_device_state(struct vfio_device *vdev,
 				       enum vfio_device_mig_state *curr_state)
 {
@@ -577,6 +594,7 @@  static void mlx5vf_pci_close_device(struct vfio_device *core_vdev)
 static const struct vfio_migration_ops mlx5vf_pci_mig_ops = {
 	.migration_set_state = mlx5vf_pci_set_device_state,
 	.migration_get_state = mlx5vf_pci_get_device_state,
+	.migration_get_data_size = mlx5vf_pci_get_data_size,
 };
 
 static const struct vfio_log_ops mlx5vf_pci_log_ops = {
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 2d168793d4e1..b118e7b1bc59 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -1256,6 +1256,34 @@  vfio_ioctl_device_feature_mig_device_state(struct vfio_device *device,
 	return 0;
 }
 
+static int
+vfio_ioctl_device_feature_migration_data_size(struct vfio_device *device,
+					      u32 flags, void __user *arg,
+					      size_t argsz)
+{
+	struct vfio_device_feature_mig_data_size data_size = {};
+	unsigned long stop_copy_length;
+	int ret;
+
+	if (!device->mig_ops)
+		return -ENOTTY;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
+				 sizeof(data_size));
+	if (ret != 1)
+		return ret;
+
+	ret = device->mig_ops->migration_get_data_size(device, &stop_copy_length);
+	if (ret)
+		return ret;
+
+	data_size.stop_copy_length = stop_copy_length;
+	if (copy_to_user(arg, &data_size, sizeof(data_size)))
+		return -EFAULT;
+
+	return 0;
+}
+
 static int vfio_ioctl_device_feature_migration(struct vfio_device *device,
 					       u32 flags, void __user *arg,
 					       size_t argsz)
@@ -1483,6 +1511,10 @@  static int vfio_ioctl_device_feature(struct vfio_device *device,
 		return vfio_ioctl_device_feature_logging_report(
 			device, feature.flags, arg->data,
 			feature.argsz - minsz);
+	case VFIO_DEVICE_FEATURE_MIG_DATA_SIZE:
+		return vfio_ioctl_device_feature_migration_data_size(
+			device, feature.flags, arg->data,
+			feature.argsz - minsz);
 	default:
 		if (unlikely(!device->ops->device_feature))
 			return -EINVAL;
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index e7cebeb875dd..5509451ae709 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -107,6 +107,9 @@  struct vfio_device_ops {
  * @migration_get_state: Optional callback to get the migration state for
  *         devices that support migration. It's mandatory for
  *         VFIO_DEVICE_FEATURE_MIGRATION migration support.
+ * @migration_get_data_size: Optional callback to get the estimated data
+ *          length that will be required to complete stop copy. It's mandatory for
+ *          VFIO_DEVICE_FEATURE_MIGRATION migration support.
  */
 struct vfio_migration_ops {
 	struct file *(*migration_set_state)(
@@ -114,6 +117,8 @@  struct vfio_migration_ops {
 		enum vfio_device_mig_state new_state);
 	int (*migration_get_state)(struct vfio_device *device,
 				   enum vfio_device_mig_state *curr_state);
+	int (*migration_get_data_size)(struct vfio_device *device,
+				       unsigned long *stop_copy_length);
 };
 
 /**
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index d7d8e0922376..3e45dbaf190e 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1128,6 +1128,19 @@  struct vfio_device_feature_dma_logging_report {
 
 #define VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT 8
 
+/*
+ * Upon VFIO_DEVICE_FEATURE_GET read back the estimated data length that will
+ * be required to complete stop copy.
+ *
+ * Note: Can be called on each device state.
+ */
+
+struct vfio_device_feature_mig_data_size {
+	__aligned_u64 stop_copy_length;
+};
+
+#define VFIO_DEVICE_FEATURE_MIG_DATA_SIZE 9
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**