[PATCH 07/27] hw/usb/dev-smartcard-reader: block migration with pending queues

Marc-AndrĂ© Lureau <[email protected]> Wed, 05 Aug 2026 15:50:57 +0400
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
Several queue state fields are not included in the vmstate:
bulk_in_pending_num, pending_answers_start, and pending_answers_end.
Also if current_bulk_in != NULL (ongoing transfer). Migrating while any
of these are non-zero produces inconsistent state on the destination.

Rather than extending the vmstate (which would require a version bump
and migration compat machinery), block migration when the queues are
non-empty. In practice this window is very short since the CMD_SLOT_BUSY
enforcement limits pending_answers to at most one entry.

Convert pre_save to the pre_save_errp form so it can report the error
cleanly. Follow up patches will add migration blocker and new migration
version.

Fixes: 367071447ec5 ("usb-ccid: add CCID bus")
Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 hw/usb/dev-smartcard-reader.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index a147cd19f388..1b8f0e10e172 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -1377,13 +1377,20 @@ static int ccid_post_load(void *opaque, int version_id)
     return 0;
 }
 
-static int ccid_pre_save(void *opaque)
+static bool ccid_pre_save(void *opaque, Error **errp)
 {
     USBCCIDState *s = opaque;
 
+    if (s->pending_answers_num || s->bulk_in_pending_num ||
+        s->current_bulk_in) {
+        error_setg(errp, "usb-ccid has pending queue state which cannot be "
+                   "migrated safely");
+        return false;
+    }
+
     s->state_vmstate = s->dev.state;
 
-    return 0;
+    return true;
 }
 
 static const VMStateDescription bulk_in_vmstate = {
@@ -1426,7 +1433,7 @@ static const VMStateDescription ccid_vmstate = {
     .version_id = 1,
     .minimum_version_id = 1,
     .post_load = ccid_post_load,
-    .pre_save = ccid_pre_save,
+    .pre_save_errp = ccid_pre_save,
     .fields = (const VMStateField[]) {
         VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice),
         VMSTATE_UINT8(debug, USBCCIDState),

-- 
2.55.0