* [PATCH] vfio/migration: Don't reset a device on a declined stop transition
@ 2026-10-08 2:43 Pankaj Kumar
2026-10-09 12:46 ` Cédric Le Goater
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
0 siblings, 2 replies; 6+ messages in thread
From: Pankaj Kumar @ 2026-10-08 2:43 UTC (permalink / raw)
To: alex, clg; +Cc: qemu-devel, pshit, pmohite, dnigam, avihaih, alwilliamson
QEMU resets a VFIO device whenever a migration state transition it requests
fails. This is done by vfio_migration_set_state_or_reset(), which passes
VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
VFIO_DEVICE_RESET.
However, a device may legitimately decline a state transition, e.g. a vGPU
that refuses to stop while a profiling/debug session is active in the guest.
Such a device is still healthy and running the guest, so resetting it destroys
a perfectly good VM.
Instead of resetting on a declined stop, recover the device to its current
state. The current device state is always a valid, already-reached state, so
recovering to it is effectively a no-op transition that cannot itself fail and
trigger a reset:
- vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
current device state for the stop direction. The resume direction keeps
ERROR as the recover state, since a device that cannot resume is genuinely
broken.
- vfio_save_complete_precopy() recovers to the current device state rather
than STOP. If it runs after an earlier stop was declined (the device still
in a running state), the stop-copy attempt is then rolled back instead of
resetting the device.
This lets vendor drivers decline a stop as a matter of policy without losing
the guest, and lets such drivers drop their own workarounds that swallowed the
spurious reset.
Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
Reviewed-by: Avihai Horon <avihaih@nvidia.com>
---
hw/vfio/migration.c | 37 +++++++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 73c49d8c24..4a98afa7c2 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -771,9 +771,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
trace_vfio_save_complete_precopy_start(vbasedev->name);
- /* We reach here with device state STOP or STOP_COPY only */
+ /*
+ * We normally reach here with the device in STOP or STOP_COPY. However, if
+ * an earlier stop transition was declined by the device, it may still be in
+ * a running state. Recover to the current device state rather than STOP, so
+ * a device that declines the stop-copy transition is rolled back to its
+ * current state instead of being reset out from under the guest.
+ */
ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
- VFIO_DEVICE_STATE_STOP, &local_err);
+ vbasedev->migration->device_state,
+ &local_err);
if (ret) {
error_report_err(local_err);
return ret;
@@ -993,6 +1000,7 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
VFIODevice *vbasedev = opaque;
VFIOMigration *migration = vbasedev->migration;
enum vfio_device_mig_state new_state;
+ enum vfio_device_mig_state recover_state;
Error *local_err = NULL;
int ret;
@@ -1009,7 +1017,15 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
vfio_final_precopy_reinit_check(vbasedev);
}
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
+ /*
+ * If the device declines a stop-direction transition, roll it back to its
+ * current state instead of resetting it out from under the guest. On resume
+ * (running), a device that cannot transition is genuinely broken, so reset
+ * it by using ERROR as the recover state.
+ */
+ recover_state = running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
+ ret = vfio_migration_set_state(vbasedev, new_state, recover_state,
+ &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
@@ -1026,21 +1042,34 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
static void vfio_vmstate_change(void *opaque, bool running, RunState state)
{
VFIODevice *vbasedev = opaque;
+ VFIOMigration *migration = vbasedev->migration;
enum vfio_device_mig_state new_state;
+ enum vfio_device_mig_state recover_state;
Error *local_err = NULL;
int ret;
if (running) {
new_state = VFIO_DEVICE_STATE_RUNNING;
+ /*
+ * If the device fails to resume, it is genuinely broken, so reset it
+ * by using ERROR as the recover state.
+ */
+ recover_state = VFIO_DEVICE_STATE_ERROR;
} else {
new_state =
(vfio_device_state_is_precopy(vbasedev) &&
(state == RUN_STATE_FINISH_MIGRATE || state == RUN_STATE_PAUSED)) ?
VFIO_DEVICE_STATE_STOP_COPY :
VFIO_DEVICE_STATE_STOP;
+ /*
+ * A device may decline to stop (e.g. it is busy). Roll it back to its
+ * current state rather than resetting it out from under the guest.
+ */
+ recover_state = migration->device_state;
}
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
+ ret = vfio_migration_set_state(vbasedev, new_state, recover_state,
+ &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] vfio/migration: Don't reset a device on a declined stop transition
2026-10-08 2:43 [PATCH] vfio/migration: Don't reset a device on a declined stop transition Pankaj Kumar
@ 2026-10-09 12:46 ` Cédric Le Goater
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
1 sibling, 0 replies; 6+ messages in thread
From: Cédric Le Goater @ 2026-10-09 12:46 UTC (permalink / raw)
To: Pankaj Kumar, alex; +Cc: qemu-devel, pmohite, dnigam, avihaih, alwilliamson
Hello Pankaj
On 10/8/26 04:43, Pankaj Kumar wrote:
> QEMU resets a VFIO device whenever a migration state transition it requests
> fails. This is done by vfio_migration_set_state_or_reset(), which passes
> VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
> VFIO_DEVICE_RESET.
>
> However, a device may legitimately decline a state transition, e.g. a vGPU
> that refuses to stop while a profiling/debug session is active in the guest.
> Such a device is still healthy and running the guest, so resetting it destroys
> a perfectly good VM.
>
> Instead of resetting on a declined stop, recover the device to its current
> state. The current device state is always a valid, already-reached state, so
> recovering to it is effectively a no-op transition that cannot itself fail and
> trigger a reset:
>
> - vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
> current device state for the stop direction. The resume direction keeps
> ERROR as the recover state, since a device that cannot resume is genuinely
> broken.
>
> - vfio_save_complete_precopy() recovers to the current device state rather
> than STOP. If it runs after an earlier stop was declined (the device still
> in a running state), the stop-copy attempt is then rolled back instead of
> resetting the device.
>
> This lets vendor drivers decline a stop as a matter of policy without losing
> the guest, and lets such drivers drop their own workarounds that swallowed the
> spurious reset.
>
> Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
> Reviewed-by: Avihai Horon <avihaih@nvidia.com>
I don't remember seeing this change before. If so please remove any
trailers before sending. Reviews should be done on the list, even
more so when the reviewer is a well known and trusted contributor !
> ---
> hw/vfio/migration.c | 37 +++++++++++++++++++++++++++++++++----
> 1 file changed, 33 insertions(+), 4 deletions(-)
>
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 73c49d8c24..4a98afa7c2 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -771,9 +771,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
>
> trace_vfio_save_complete_precopy_start(vbasedev->name);
>
> - /* We reach here with device state STOP or STOP_COPY only */
> + /*
> + * We normally reach here with the device in STOP or STOP_COPY. However, if
> + * an earlier stop transition was declined by the device, it may still be in
> + * a running state. Recover to the current device state rather than STOP, so
> + * a device that declines the stop-copy transition is rolled back to its
> + * current state instead of being reset out from under the guest.
> + */
> ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
> - VFIO_DEVICE_STATE_STOP, &local_err);
> + vbasedev->migration->device_state,
> + &local_err);
> if (ret) {
> error_report_err(local_err);
> return ret;
> @@ -993,6 +1000,7 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> VFIODevice *vbasedev = opaque;
> VFIOMigration *migration = vbasedev->migration;
> enum vfio_device_mig_state new_state;
> + enum vfio_device_mig_state recover_state;
> Error *local_err = NULL;
> int ret;
>
> @@ -1009,7 +1017,15 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> vfio_final_precopy_reinit_check(vbasedev);
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + /*
> + * If the device declines a stop-direction transition, roll it back to its
> + * current state instead of resetting it out from under the guest. On resume
> + * (running), a device that cannot transition is genuinely broken, so reset
> + * it by using ERROR as the recover state.
> + */
> + recover_state = running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
> + ret = vfio_migration_set_state(vbasedev, new_state, recover_state,
> + &local_err);
How about introducing a common helper :
static int vfio_migration_set_state_or_recover(VFIODevice *vbasedev,
enum vfio_device_mig_state new_state,
bool running, Error **errp)
{
VFIOMigration *migration = vbasedev->migration;
enum vfio_device_mig_state recover_state =
running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
return vfio_migration_set_state(vbasedev, new_state, recover_state, errp);
}
C.
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
> @@ -1026,21 +1042,34 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> static void vfio_vmstate_change(void *opaque, bool running, RunState state)
> {
> VFIODevice *vbasedev = opaque;
> + VFIOMigration *migration = vbasedev->migration;
> enum vfio_device_mig_state new_state;
> + enum vfio_device_mig_state recover_state;
> Error *local_err = NULL;
> int ret;
>
> if (running) {
> new_state = VFIO_DEVICE_STATE_RUNNING;
> + /*
> + * If the device fails to resume, it is genuinely broken, so reset it
> + * by using ERROR as the recover state.
> + */
> + recover_state = VFIO_DEVICE_STATE_ERROR;
> } else {
> new_state =
> (vfio_device_state_is_precopy(vbasedev) &&
> (state == RUN_STATE_FINISH_MIGRATE || state == RUN_STATE_PAUSED)) ?
> VFIO_DEVICE_STATE_STOP_COPY :
> VFIO_DEVICE_STATE_STOP;
> + /*
> + * A device may decline to stop (e.g. it is busy). Roll it back to its
> + * current state rather than resetting it out from under the guest.
> + */
> + recover_state = migration->device_state;
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state(vbasedev, new_state, recover_state,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] vfio/migration: Don't reset a device on a declined stop transition
2026-10-08 2:43 [PATCH] vfio/migration: Don't reset a device on a declined stop transition Pankaj Kumar
2026-10-09 12:46 ` Cédric Le Goater
@ 2026-10-13 4:45 ` Pankaj Kumar
2026-10-13 6:33 ` Cédric Le Goater
` (2 more replies)
1 sibling, 3 replies; 6+ messages in thread
From: Pankaj Kumar @ 2026-10-13 4:45 UTC (permalink / raw)
To: alex, clg
Cc: qemu-devel, pshit, pmohite, dnigam, avihaih, alwilliamson, smitra,
kwankhede, vkale, targupta
QEMU resets a VFIO device whenever a migration state transition it requests
fails. This is done by vfio_migration_set_state_or_reset(), which passes
VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
VFIO_DEVICE_RESET.
However, a device may legitimately decline a state transition, e.g. a vGPU
that refuses to stop while a profiling/debug session is active in the guest.
Such a device is still healthy and running the guest, so resetting it destroys
a perfectly good VM.
Instead of resetting on a declined stop, recover the device to its current
state. The current device state is always a valid, already-reached state, so
recovering to it is effectively a no-op transition that cannot itself fail and
trigger a reset.
Introduce a helper, vfio_migration_set_state_or_recover(), that selects the
recover state based on the transition direction, and route the vmstate change
and save-completion paths through it:
- vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
current device state for the stop direction. The resume direction keeps
ERROR as the recover state, since a device that cannot resume is genuinely
broken.
- vfio_save_complete_precopy() recovers to the current device state rather
than STOP. If it runs after an earlier stop was declined (the device still
in a running state), the stop-copy attempt is then rolled back instead of
resetting the device.
The existing vfio_migration_set_state_or_reset() is retained for the paths
that must still reset on failure (save cleanup and the migration-failed
notifier).
This lets vendor drivers decline a stop as a matter of policy without losing
the guest, and lets such drivers drop their own workarounds that swallowed the
spurious reset.
Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
---
v2:
- Introduce a vfio_migration_set_state_or_recover() helper that selects the
recover state by transition direction, and route all three call sites
through it instead of open-coding the recover-state selection (Cédric).
- Keep vfio_migration_set_state_or_reset() for the paths that must still
reset on failure (save cleanup and the migration-failed notifier).
v1: https://lore.kernel.org/qemu-devel/20261008024329.1996536-1-pshit@nvidia.com/
hw/vfio/migration.c | 38 +++++++++++++++++++++++++++++++++-----
1 file changed, 33 insertions(+), 5 deletions(-)
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 73c49d8c24..69d50cebdd 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -246,6 +246,25 @@ vfio_migration_set_state_or_reset(VFIODevice *vbasedev,
VFIO_DEVICE_STATE_ERROR, errp);
}
+/*
+ * On a stop-direction transition failure, recover the device to its current
+ * state instead of resetting it: the current state is valid and already
+ * reached, so recovering to it is effectively a no-op that cannot itself fail.
+ * On a resume-direction (running) failure, the device is genuinely broken, so
+ * reset it by using ERROR as the recover state.
+ */
+static int
+vfio_migration_set_state_or_recover(VFIODevice *vbasedev,
+ enum vfio_device_mig_state new_state,
+ bool running, Error **errp)
+{
+ VFIOMigration *migration = vbasedev->migration;
+ enum vfio_device_mig_state recover_state =
+ running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
+
+ return vfio_migration_set_state(vbasedev, new_state, recover_state, errp);
+}
+
static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
uint64_t data_size)
{
@@ -771,9 +790,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
trace_vfio_save_complete_precopy_start(vbasedev->name);
- /* We reach here with device state STOP or STOP_COPY only */
- ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
- VFIO_DEVICE_STATE_STOP, &local_err);
+ /*
+ * We normally reach here with the device in STOP or STOP_COPY. However, if
+ * an earlier stop transition was declined by the device, it may still be in
+ * a running state. Recover to the current device state rather than STOP, so
+ * a device that declines the stop-copy transition is rolled back to its
+ * current state instead of being reset out from under the guest.
+ */
+ ret = vfio_migration_set_state_or_recover(vbasedev,
+ VFIO_DEVICE_STATE_STOP_COPY,
+ false, &local_err);
if (ret) {
error_report_err(local_err);
return ret;
@@ -1009,7 +1035,8 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
vfio_final_precopy_reinit_check(vbasedev);
}
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
+ ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
+ &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
@@ -1040,7 +1067,8 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
VFIO_DEVICE_STATE_STOP;
}
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
+ ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
+ &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2] vfio/migration: Don't reset a device on a declined stop transition
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
@ 2026-10-13 6:33 ` Cédric Le Goater
2026-10-13 8:36 ` Avihai Horon
2026-10-13 12:13 ` Cédric Le Goater
2 siblings, 0 replies; 6+ messages in thread
From: Cédric Le Goater @ 2026-10-13 6:33 UTC (permalink / raw)
To: Pankaj Kumar, alex
Cc: qemu-devel, pmohite, dnigam, avihaih, alwilliamson, smitra,
kwankhede, vkale, targupta
On 10/13/26 06:45, Pankaj Kumar wrote:
> QEMU resets a VFIO device whenever a migration state transition it requests
> fails. This is done by vfio_migration_set_state_or_reset(), which passes
> VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
> VFIO_DEVICE_RESET.
>
> However, a device may legitimately decline a state transition, e.g. a vGPU
> that refuses to stop while a profiling/debug session is active in the guest.
> Such a device is still healthy and running the guest, so resetting it destroys
> a perfectly good VM.
>
> Instead of resetting on a declined stop, recover the device to its current
> state. The current device state is always a valid, already-reached state, so
> recovering to it is effectively a no-op transition that cannot itself fail and
> trigger a reset.
>
> Introduce a helper, vfio_migration_set_state_or_recover(), that selects the
> recover state based on the transition direction, and route the vmstate change
> and save-completion paths through it:
>
> - vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
> current device state for the stop direction. The resume direction keeps
> ERROR as the recover state, since a device that cannot resume is genuinely
> broken.
>
> - vfio_save_complete_precopy() recovers to the current device state rather
> than STOP. If it runs after an earlier stop was declined (the device still
> in a running state), the stop-copy attempt is then rolled back instead of
> resetting the device.
>
> The existing vfio_migration_set_state_or_reset() is retained for the paths
> that must still reset on failure (save cleanup and the migration-failed
> notifier).
>
> This lets vendor drivers decline a stop as a matter of policy without losing
> the guest, and lets such drivers drop their own workarounds that swallowed the
> spurious reset.
>
> Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
> ---
> v2:
> - Introduce a vfio_migration_set_state_or_recover() helper that selects the
> recover state by transition direction, and route all three call sites
> through it instead of open-coding the recover-state selection (Cédric).
> - Keep vfio_migration_set_state_or_reset() for the paths that must still
> reset on failure (save cleanup and the migration-failed notifier).
>
> v1: https://lore.kernel.org/qemu-devel/20261008024329.1996536-1-pshit@nvidia.com/
>
> hw/vfio/migration.c | 38 +++++++++++++++++++++++++++++++++-----
> 1 file changed, 33 insertions(+), 5 deletions(-)
>
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 73c49d8c24..69d50cebdd 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -246,6 +246,25 @@ vfio_migration_set_state_or_reset(VFIODevice *vbasedev,
> VFIO_DEVICE_STATE_ERROR, errp);
> }
>
> +/*
> + * On a stop-direction transition failure, recover the device to its current
> + * state instead of resetting it: the current state is valid and already
> + * reached, so recovering to it is effectively a no-op that cannot itself fail.
> + * On a resume-direction (running) failure, the device is genuinely broken, so
> + * reset it by using ERROR as the recover state.
> + */
> +static int
> +vfio_migration_set_state_or_recover(VFIODevice *vbasedev,
> + enum vfio_device_mig_state new_state,
> + bool running, Error **errp)
> +{
> + VFIOMigration *migration = vbasedev->migration;
> + enum vfio_device_mig_state recover_state =
> + running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
> +
> + return vfio_migration_set_state(vbasedev, new_state, recover_state, errp);
> +}
> +
> static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
> uint64_t data_size)
> {
> @@ -771,9 +790,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
>
> trace_vfio_save_complete_precopy_start(vbasedev->name);
>
> - /* We reach here with device state STOP or STOP_COPY only */
> - ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
> - VFIO_DEVICE_STATE_STOP, &local_err);
> + /*
> + * We normally reach here with the device in STOP or STOP_COPY. However, if
> + * an earlier stop transition was declined by the device, it may still be in
> + * a running state. Recover to the current device state rather than STOP, so
> + * a device that declines the stop-copy transition is rolled back to its
> + * current state instead of being reset out from under the guest.
> + */
> + ret = vfio_migration_set_state_or_recover(vbasedev,
> + VFIO_DEVICE_STATE_STOP_COPY,
> + false, &local_err);
> if (ret) {
> error_report_err(local_err);
> return ret;
> @@ -1009,7 +1035,8 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> vfio_final_precopy_reinit_check(vbasedev);
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
> @@ -1040,7 +1067,8 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
> VFIO_DEVICE_STATE_STOP;
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] vfio/migration: Don't reset a device on a declined stop transition
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
2026-10-13 6:33 ` Cédric Le Goater
@ 2026-10-13 8:36 ` Avihai Horon
2026-10-13 12:13 ` Cédric Le Goater
2 siblings, 0 replies; 6+ messages in thread
From: Avihai Horon @ 2026-10-13 8:36 UTC (permalink / raw)
To: Pankaj Kumar, alex, clg
Cc: qemu-devel, pmohite, dnigam, alwilliamson, smitra, kwankhede,
vkale, targupta
On 10/13/2026 7:45 AM, Pankaj Kumar wrote:
> QEMU resets a VFIO device whenever a migration state transition it requests
> fails. This is done by vfio_migration_set_state_or_reset(), which passes
> VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
> VFIO_DEVICE_RESET.
>
> However, a device may legitimately decline a state transition, e.g. a vGPU
> that refuses to stop while a profiling/debug session is active in the guest.
> Such a device is still healthy and running the guest, so resetting it destroys
> a perfectly good VM.
>
> Instead of resetting on a declined stop, recover the device to its current
> state. The current device state is always a valid, already-reached state, so
> recovering to it is effectively a no-op transition that cannot itself fail and
> trigger a reset.
>
> Introduce a helper, vfio_migration_set_state_or_recover(), that selects the
> recover state based on the transition direction, and route the vmstate change
> and save-completion paths through it:
>
> - vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
> current device state for the stop direction. The resume direction keeps
> ERROR as the recover state, since a device that cannot resume is genuinely
> broken.
>
> - vfio_save_complete_precopy() recovers to the current device state rather
> than STOP. If it runs after an earlier stop was declined (the device still
> in a running state), the stop-copy attempt is then rolled back instead of
> resetting the device.
>
> The existing vfio_migration_set_state_or_reset() is retained for the paths
> that must still reset on failure (save cleanup and the migration-failed
> notifier).
>
> This lets vendor drivers decline a stop as a matter of policy without losing
> the guest, and lets such drivers drop their own workarounds that swallowed the
> spurious reset.
>
> Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
> ---
> v2:
> - Introduce a vfio_migration_set_state_or_recover() helper that selects the
> recover state by transition direction, and route all three call sites
> through it instead of open-coding the recover-state selection (Cédric).
> - Keep vfio_migration_set_state_or_reset() for the paths that must still
> reset on failure (save cleanup and the migration-failed notifier).
>
> v1: https://lore.kernel.org/qemu-devel/20261008024329.1996536-1-pshit@nvidia.com/
>
> hw/vfio/migration.c | 38 +++++++++++++++++++++++++++++++++-----
> 1 file changed, 33 insertions(+), 5 deletions(-)
Reviewed-by: Avihai Horon <avihaih@nvidia.com>
Thanks.
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 73c49d8c24..69d50cebdd 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -246,6 +246,25 @@ vfio_migration_set_state_or_reset(VFIODevice *vbasedev,
> VFIO_DEVICE_STATE_ERROR, errp);
> }
>
> +/*
> + * On a stop-direction transition failure, recover the device to its current
> + * state instead of resetting it: the current state is valid and already
> + * reached, so recovering to it is effectively a no-op that cannot itself fail.
> + * On a resume-direction (running) failure, the device is genuinely broken, so
> + * reset it by using ERROR as the recover state.
> + */
> +static int
> +vfio_migration_set_state_or_recover(VFIODevice *vbasedev,
> + enum vfio_device_mig_state new_state,
> + bool running, Error **errp)
> +{
> + VFIOMigration *migration = vbasedev->migration;
> + enum vfio_device_mig_state recover_state =
> + running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
> +
> + return vfio_migration_set_state(vbasedev, new_state, recover_state, errp);
> +}
> +
> static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
> uint64_t data_size)
> {
> @@ -771,9 +790,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
>
> trace_vfio_save_complete_precopy_start(vbasedev->name);
>
> - /* We reach here with device state STOP or STOP_COPY only */
> - ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
> - VFIO_DEVICE_STATE_STOP, &local_err);
> + /*
> + * We normally reach here with the device in STOP or STOP_COPY. However, if
> + * an earlier stop transition was declined by the device, it may still be in
> + * a running state. Recover to the current device state rather than STOP, so
> + * a device that declines the stop-copy transition is rolled back to its
> + * current state instead of being reset out from under the guest.
> + */
> + ret = vfio_migration_set_state_or_recover(vbasedev,
> + VFIO_DEVICE_STATE_STOP_COPY,
> + false, &local_err);
> if (ret) {
> error_report_err(local_err);
> return ret;
> @@ -1009,7 +1035,8 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> vfio_final_precopy_reinit_check(vbasedev);
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
> @@ -1040,7 +1067,8 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
> VFIO_DEVICE_STATE_STOP;
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] vfio/migration: Don't reset a device on a declined stop transition
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
2026-10-13 6:33 ` Cédric Le Goater
2026-10-13 8:36 ` Avihai Horon
@ 2026-10-13 12:13 ` Cédric Le Goater
2 siblings, 0 replies; 6+ messages in thread
From: Cédric Le Goater @ 2026-10-13 12:13 UTC (permalink / raw)
To: Pankaj Kumar, alex
Cc: qemu-devel, pmohite, dnigam, avihaih, alwilliamson, smitra,
kwankhede, vkale, targupta
On 10/13/26 06:45, Pankaj Kumar wrote:
> QEMU resets a VFIO device whenever a migration state transition it requests
> fails. This is done by vfio_migration_set_state_or_reset(), which passes
> VFIO_DEVICE_STATE_ERROR as the recover state, forcing an immediate
> VFIO_DEVICE_RESET.
>
> However, a device may legitimately decline a state transition, e.g. a vGPU
> that refuses to stop while a profiling/debug session is active in the guest.
> Such a device is still healthy and running the guest, so resetting it destroys
> a perfectly good VM.
>
> Instead of resetting on a declined stop, recover the device to its current
> state. The current device state is always a valid, already-reached state, so
> recovering to it is effectively a no-op transition that cannot itself fail and
> trigger a reset.
>
> Introduce a helper, vfio_migration_set_state_or_recover(), that selects the
> recover state based on the transition direction, and route the vmstate change
> and save-completion paths through it:
>
> - vfio_vmstate_change() and vfio_vmstate_change_prepare() recover to the
> current device state for the stop direction. The resume direction keeps
> ERROR as the recover state, since a device that cannot resume is genuinely
> broken.
>
> - vfio_save_complete_precopy() recovers to the current device state rather
> than STOP. If it runs after an earlier stop was declined (the device still
> in a running state), the stop-copy attempt is then rolled back instead of
> resetting the device.
>
> The existing vfio_migration_set_state_or_reset() is retained for the paths
> that must still reset on failure (save cleanup and the migration-failed
> notifier).
>
> This lets vendor drivers decline a stop as a matter of policy without losing
> the guest, and lets such drivers drop their own workarounds that swallowed the
> spurious reset.
>
> Signed-off-by: Pankaj Kumar <pshit@nvidia.com>
> ---
> v2:
> - Introduce a vfio_migration_set_state_or_recover() helper that selects the
> recover state by transition direction, and route all three call sites
> through it instead of open-coding the recover-state selection (Cédric).
> - Keep vfio_migration_set_state_or_reset() for the paths that must still
> reset on failure (save cleanup and the migration-failed notifier).
>
> v1: https://lore.kernel.org/qemu-devel/20261008024329.1996536-1-pshit@nvidia.com/
>
> hw/vfio/migration.c | 38 +++++++++++++++++++++++++++++++++-----
> 1 file changed, 33 insertions(+), 5 deletions(-)
>
> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index 73c49d8c24..69d50cebdd 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c
> @@ -246,6 +246,25 @@ vfio_migration_set_state_or_reset(VFIODevice *vbasedev,
> VFIO_DEVICE_STATE_ERROR, errp);
> }
>
> +/*
> + * On a stop-direction transition failure, recover the device to its current
> + * state instead of resetting it: the current state is valid and already
> + * reached, so recovering to it is effectively a no-op that cannot itself fail.
> + * On a resume-direction (running) failure, the device is genuinely broken, so
> + * reset it by using ERROR as the recover state.
> + */
> +static int
> +vfio_migration_set_state_or_recover(VFIODevice *vbasedev,
> + enum vfio_device_mig_state new_state,
> + bool running, Error **errp)
> +{
> + VFIOMigration *migration = vbasedev->migration;
> + enum vfio_device_mig_state recover_state =
> + running ? VFIO_DEVICE_STATE_ERROR : migration->device_state;
> +
> + return vfio_migration_set_state(vbasedev, new_state, recover_state, errp);
> +}
> +
> static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
> uint64_t data_size)
> {
> @@ -771,9 +790,16 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
>
> trace_vfio_save_complete_precopy_start(vbasedev->name);
>
> - /* We reach here with device state STOP or STOP_COPY only */
> - ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
> - VFIO_DEVICE_STATE_STOP, &local_err);
> + /*
> + * We normally reach here with the device in STOP or STOP_COPY. However, if
> + * an earlier stop transition was declined by the device, it may still be in
> + * a running state. Recover to the current device state rather than STOP, so
> + * a device that declines the stop-copy transition is rolled back to its
> + * current state instead of being reset out from under the guest.
> + */
> + ret = vfio_migration_set_state_or_recover(vbasedev,
> + VFIO_DEVICE_STATE_STOP_COPY,
> + false, &local_err);
> if (ret) {
> error_report_err(local_err);
> return ret;
> @@ -1009,7 +1035,8 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
> vfio_final_precopy_reinit_check(vbasedev);
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
> @@ -1040,7 +1067,8 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
> VFIO_DEVICE_STATE_STOP;
> }
>
> - ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
> + ret = vfio_migration_set_state_or_recover(vbasedev, new_state, running,
> + &local_err);
> if (ret) {
> /*
> * Migration should be aborted in this case, but vm_state_notify()
Applied to
https://github.com/legoater/qemu vfio-next
Thanks,
C.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-13 12:14 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08 2:43 [PATCH] vfio/migration: Don't reset a device on a declined stop transition Pankaj Kumar
2026-10-09 12:46 ` Cédric Le Goater
2026-10-13 4:45 ` [PATCH v2] " Pankaj Kumar
2026-10-13 6:33 ` Cédric Le Goater
2026-10-13 8:36 ` Avihai Horon
2026-10-13 12:13 ` Cédric Le Goater
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.