From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11020085.outbound.protection.outlook.com [52.101.69.85]) by lore.virtuozzo.com (Postfix) with ESMTPS id 88335807C6 for ; Tue, 6 Oct 2026 15:36:01 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=N9yoO/N5wabvy5/9acUo4VcN+vBjOyldvlQ90656DjVwi+xiNSO572MdaaDrFnvsUm3XTOgpnNdK2IO8a2X+p5tp5oJa62CykzzQ6S6nuzKhAJ9CQLmZ7Uou/hR7YkgJEvS+qfcHBgXKjbM8fA+XqldKOIe16Rt/uG6vnu5hB+HPLEHrosK4c73TazGkAQ03EbCnMAPPUwATYbmNq2icoNn9TYpJpJx8KU+CUuOfLC8MlaRVqavDoYJa+6gvUbXzpsVwfsLn6oCa7A9WcreD4OCUmSv1N4jnUBmLEy7Bz09gGtQThzAlwbq0WZN9/E2kv4SCijBoD9pFU43RkY3LOw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=F5e1Ee95dzDyHfU89kZrL6+5rdqIjn95Lugg1hDdOKg=; b=hl7G3UN19v/sHEYpzI6fB4kt4GoAgy/8AtAeEkxmdDrw9O76BKX/ajcJRU/H39apP0MRw786bjOiNI5yqKqlkkW4d6wET+4ysbWdcnaL0xhXlcyJsINvk5AaAI0irMhdFL0Uke0lTi9dHhItR+LkPObTuQmXYTw9FMpeh8WmV3x0BPNBdik6NMC0/Xo5N6flzmvShO7kAIayPyisDPR8Iw8nfZatX+esn6XFIam8JNwWThphqyFoef/r1Nai6qguyPP4Ke7riI3aKhC/qtTq4dQqRFove1X4fdgPG95POn2lvErnwfKjdZ+A51uve7BKyirweXaLAalY8B9VVh2LPQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=F5e1Ee95dzDyHfU89kZrL6+5rdqIjn95Lugg1hDdOKg=; b=p4RuvwjGVSdc/oiOmE1iCh1u41epZdVRtSwkTlRGGnVO7oZjv1HxElsntjeiyJI2r6dVjJpsCfNiYw5bqy50FmafzmGZRFvxUO89e86VpbSj29R0axNKru8ufBi8XIJvtuUuSW7oeAaTYkbxp2aSkFg+zzKL4hrj50JDFq7EWraTKGqaCyIYgSWBAiOYyCH5sL7eoKFkLP19SXNJOHFTev9yc3MRdAgmM2WRmk2VYI9TAeKVWpysI4horOKRKt01XhNSM9oJjx3zzaQ9x6mUfhz/GCMgj7qFoNdkZh15KcNKKHSTi4iyC/kuGdDkpdJ6KLNqCqfK3dLfWFZVLBJj6w== Authentication-Results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; Received: from VI0PR08MB10656.eurprd08.prod.outlook.com (2603:10a6:800:20a::12) by DU0PR08MB9774.eurprd08.prod.outlook.com (2603:10a6:10:445::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.496.14; Tue, 6 Oct 2026 15:35:59 +0000 Received: from VI0PR08MB10656.eurprd08.prod.outlook.com ([fe80::4e37:b189:ddcd:3dd8]) by VI0PR08MB10656.eurprd08.prod.outlook.com ([fe80::4e37:b189:ddcd:3dd8%7]) with mapi id 15.21.0496.010; Tue, 6 Oct 2026 15:35:59 +0000 From: Andrey Drobyshev Date: Tue, 06 Oct 2026 18:35:58 +0300 Subject: [PATCH QEMU HCI-8.0 v7 07/14] usb-host: re-issue drained URBs if CPR is aborted #VSTOR-137800 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261006-usb-host_v3-v7-7-044f668789a1@virtuozzo.com> References: <20261006-usb-host_v3-v7-0-044f668789a1@virtuozzo.com> In-Reply-To: <20261006-usb-host_v3-v7-0-044f668789a1@virtuozzo.com> To: svt-core@virtuozzo.com Cc: den@openvz.org, andrey.drobyshev@virtuozzo.com X-Mailer: b4 0.16.0 X-ClientProxiedBy: VI5PEPF00000985.AUTP296.PROD.OUTLOOK.COM (2603:10a6:808:1::81a) To VI0PR08MB10656.eurprd08.prod.outlook.com (2603:10a6:800:20a::12) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI0PR08MB10656:EE_|DU0PR08MB9774:EE_ X-MS-Office365-Filtering-Correlation-Id: 7210ea16-4d3e-4b49-a532-08df23bf84d5 X-LD-Processed: 0bc7f26d-0264-416e-a6fc-8352af79c58f,ExtAddr List-Id: svt-core@virtuozzo.com X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|23010399003|1800799024|366016|376014|52116014|5023799004|5113699003|56012099006|10067099003|22082099003|18002099003|38350700014; X-Microsoft-Antispam-Message-Info: 1zNEjwvnl1r1bmdYassJRjX8oC4XYolrwHg+cboKc8L/n2j9zPGBbrJ3I4BrdB17zFbtMQoUw3H0ubguqKRQy5IL8Y190KY8rheFPjDJhBKXIgYOMNkNCrjPV2CVqlnb/YMVyTaYf6Z6gaEeLmCeXr6VxQ5twmtp3dkSTxcx037Td4beQvebgM9jqTagU+pxEtGEqvj3KqtvHJvF/+duvP5s+/El4cgXC9mbhV5XQT/3fBD1At6RFRz+2+m77ZiceoKffNMBkGf2MKJ2FIkAsZiOMUzJyPHilxfaYYhOlC6AMIzOkqeCdRnQnVWglh13HbSO/TR5/dXpJ5jdMAz7OQps1JUozZT9mqT8z22X+a80GuBESOLUNRW5EwaXVu6XNBtGkhtjTgB/7IwqDgWS2WSYwjvQ+UZkINwGjhKDH41PFaE66ave1p8FPee7ndXmq3Hc76kZvgw27obEYPJ0d4slP/Ab/0RpLS5+QD38+PxaepTMc1IbN0bjBXKEY3ZhxOceIzqlJMkiLtkkHfC+GUWGOaYyjaavrphSxg6ubEbImTkMbbKaVdYWub/ffhW/p4qEEk3PNRidwVIIIpH9gt47T26lMR2TWA8VyHGIsgbLR/GXRbj7e1U3Vt7V+SEVzYlXKgPPn0mbwwxVXb9oSUjjQ6W7FTJenGlhshb1p0hKxQHwUwGZQMWOyn7np3ljUSdMz4sAcdVXReDTv19liGKFgxYxviCNG3yoYRN2gb4= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:VI0PR08MB10656.eurprd08.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(23010399003)(1800799024)(366016)(376014)(52116014)(5023799004)(5113699003)(56012099006)(10067099003)(22082099003)(18002099003)(38350700014);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NGNWMVhFTXZBWU5qa0V5ckRCcnp1S292ZThYSS9aYnh2TEwyYjVzejZ5K0I1?= =?utf-8?B?STNveDQ4WEtGdSt3emFiU0YwVnFmTGJ6SGVZT0NmWnBEa1JyMnh0eTRiZ2Ri?= =?utf-8?B?TE4vQzNEV1ZYd0Z5T2ZDNjJrNkw1UVdmRmlVUVhyUG1EV1lSbnBHeXYwcDF2?= =?utf-8?B?cFpic2NnSlJJdC91dUdXMlFFN1FCbW9rQWgvNyt0dkhlSytZYlA2WlJnSTQ3?= =?utf-8?B?VFBEdjkzaFZxWVF0M0dhS2htUmdPR3J3SE9pWnl0ZTFhR0JKbHRXT1Y3aXFu?= =?utf-8?B?REwybEszZ0RCTVZjejgrcEdKTkNxS01NU0dmNEhIN3FhSENNTloxVGJLNVQr?= =?utf-8?B?Ti9FcEp4T2xFeU1lejlsL3UxUkM5WWxtM2k0WkU1d1ZtSVhidnNvZm5hbFBF?= =?utf-8?B?ampqN0pERWlyUS9YU3hSZU51WCtuVy8zb2VDc2ZTR0RCb1BjUGtnczQwdGd1?= =?utf-8?B?N2ZFZmdyck5uTCtUQ0NBLzFJYzdLcnAvL3NKdTA3dGk3ZTRFWWlkSWdKT01X?= =?utf-8?B?SjZ4TGFkM2VOKzVHSHI3d1ozbkdaYWt2M3RKemVuSG9icmJaODFDdXB0M2hS?= =?utf-8?B?T05RTHFaajJjeS92N3FkY0ZKSE9PUldKMEt4ZDltVmlSMnhFTlM0cW05T0Ri?= =?utf-8?B?c3BFTmRlV3ErSlBFamVDc2FqcjNHN1VvMWxxeW92U2tlMW1pTE9HOXlWVDJB?= =?utf-8?B?T2RCbFZtWlVRRDRtTXI4cDc4cGFkVFcySmVjVTlGZVdzR1JoZTFLekYrZHBo?= =?utf-8?B?Q0Z6Yks2OFBJbkJ6R2diSE90SjhBRGZSa3lDNnhBZG9Oek1WZzBZMGhUbVJo?= =?utf-8?B?TUIzRVZndVdyVzRBM2NGSlUyeTdhOHRFLys4WWhNQ2RpME9GWlVjZFhJbzdF?= =?utf-8?B?b1FDL1RlTHJLKytZaC9TYWVuVXUvMDdxbUJrQmlwL2hZNTQ1bjVUWmJXVkRu?= =?utf-8?B?dlVKNzFjdWlodjFSMFBTNURrVmpFQWp4RXU0dmFpMXMrL003bXNSeHp2cXpr?= =?utf-8?B?cURpTmZOc3o5WjdYVDcwMGs4WkdHOWx2UXMwSlBGS20xUzJhTkZSK1hXWjJW?= =?utf-8?B?REpoMHFmVXZlMjVVcGxVS1YrNTVrOUFLY3VheFhzTk5jcWtBMUZsWTlieG5H?= =?utf-8?B?cU12VWdONGdrV1FkazBkMzZkRGI0SlRtVGRZWWhmcVY3cS9jdVUrcC9TYzVa?= =?utf-8?B?a0haRFNPRnpqK242dlRDWkFUT1dpb3FqMll3ekkrYUt5Q1VVK0NpYndoU0M3?= =?utf-8?B?OU5mcHVLOUloUVRaNkJJRDUxM3M5cDBFQm00NzYyalh5Z1BoRUQxWFNRaXNp?= =?utf-8?B?OEw3SUhMSXFZeHJKVkNSVUwxYUhPRzIrSU5rQk1qUkExWWd0NEtoMVk0WHBT?= =?utf-8?B?eHV4MHlaa0EvNkRtTHR4bENMaFJ0OHZoZnZ1aDBFSU9XSlNYUWt2Y0FVS3FM?= =?utf-8?B?c1pZNnkrQXNLUmdpcmt6c2JINDhpQy85OURWMGJJZENBVzR5eDhiMlF3Ulho?= =?utf-8?B?YzBYVTBwVmx0RGs0WmdjTWE3dk1JV0JxV1liakZrOXZYUll5emliRUM0cUxY?= =?utf-8?B?YzE2bmZtTHBGeFNuY3FuY0VIRTE0a0RlR1I4TmZkeE45aGN2dnlPSWZYaC8r?= =?utf-8?B?M2theHNuUVhNUk0zNWJjZXJ5OUZtTDVFbklTYjFXSmdwZENtM1ZVVzRabjZp?= =?utf-8?B?c2lkLzdmTzRmZW9NR1EzWVl5WlE0WW9nd1Z2NG9GbGlEdmRJMjBLV3plZHIy?= =?utf-8?B?bHcyM0wxYUxUTG9tbFNBVlBKSlNaMm1IZUFIK2ZBTlkvL3k1VlpPLzAwQVpQ?= =?utf-8?B?V1Jhb1U1QlZaRDFCbWljS3R3S21qZDVwQWoxb2pJVEtFam9pSjR1MXkvNVV5?= =?utf-8?B?aHNCVU5yTHFRTGZaamZlZEhHdXRMbG5wditpNkdiVDcrRHQrbmNseXd1SUZW?= =?utf-8?B?ekEwSEIrTmhwN1o4NkxXQmNCWThCZzRSdExvVkpLOFpxKy8vOUMrREZiTTNX?= =?utf-8?B?cWgyYk9VUzBSSm4vckVxNTB4UHBvK051MGxTUWFDT3Nhb3lLcW1DSTdrNW1o?= =?utf-8?B?V0hWS21zRjh2OFNOcXZYL2VWYWtVR3l1Q3lqY0F5YzJUbTM0UjZURW1tUmgx?= =?utf-8?B?UVJCeEhCdFdnT2ZxOVNLYU92OHplS29YSS83MU9NdHBmZHV1SVRHNWxCZ3N3?= =?utf-8?B?eThjYVpHYzNraDA4U0h1MDYzTXBVOHJMSmQ5OFpNSlJ2V1N4SC95SnJFVnJJ?= =?utf-8?B?OG5FNzA3T0pqSyszZGRwdzJ0M3hhWlltYVYrWUkyZWNJU21tRVNCaTNpU2Fn?= =?utf-8?B?UlczYW5ET3BIT0VsV2FzVlhOK2Z6WTZTangwVUF3WE9sbk1PajRqZFpkanhH?= =?utf-8?Q?zYNW/aB/Gtrn/Kb8=3D?= X-Auto-Response-Suppress: DR, OOF, AutoReply X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7210ea16-4d3e-4b49-a532-08df23bf84d5 X-MS-Exchange-CrossTenant-AuthSource: VI0PR08MB10656.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 Oct 2026 15:35:58.5288 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 7BlIte3Jkou57D18tDRe8Bs0CMR05ptydzQTDf69ZuOM8cFQIeedYMHaFut+Z2SsvbbDw6s1uewbw9U9ysU43fIYXaI9aXAnD5PPcENCtlM= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR08MB9774 If CPR fails, the source VM resumes with the drained packets still owned by the host controller as in-flight, and nothing left to complete them. Remember the packets suppressed during the drain on a list, and re-issue them on the preserved fd from the vm-state handler's resume branch -- it runs on any resume but only acts once we have actually drained (cpr_drained). Control transfers cannot be reconstructed from the packet alone, so complete those as errors and let the guest driver retry. A re-issued transfer is replayed from the start of the TD, so a partial transfer loses its progress -- an OUT fragment already consumed by the device may be sent twice. Only the paused-VM .pre_save() fallback queues partial transfers for re-issue; the normal running-VM drain delivers them as short transfers and is unaffected. Signed-off-by: Andrey Drobyshev --- hw/usb/host-libusb.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index e631b683ed1..d90117e4292 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -124,6 +124,7 @@ struct USBHostDevice { /* CPR migration */ Error *cpr_blocker; VMChangeStateEntry *cpr_vmse; + GSList *cpr_inflight; bool cpr_drained; bool cpr_replay_all; @@ -169,6 +170,7 @@ static void usb_host_release_interfaces(USBHostDevice *s); static void usb_host_nodev(USBHostDevice *s); static void usb_host_detach_kernel(USBHostDevice *s); static void usb_host_attach_kernel(USBHostDevice *s); +static void usb_host_handle_data(USBDevice *udev, USBPacket *p); /* ------------------------------------------------------------------------ */ @@ -433,9 +435,10 @@ static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p) /* * Classify a reap during the CPR drain (r->p is still set). The paused-VM * fallback (cpr_replay_all) can't deliver completions, so it suppresses every - * reap for the target to replay; otherwise one that moved no data is suppressed - * and one that moved some is kept as a short transfer. Returns true if - * suppressed, so the caller frees the request. + * reap; otherwise one that moved no data is suppressed and one that moved some + * is kept as a short transfer. Suppressed packets are queued on cpr_inflight + * for the target to replay. Returns true if suppressed, so the caller frees + * the request. */ static bool usb_host_req_cpr_replay(USBHostRequest *r, struct libusb_transfer *xfer) @@ -450,6 +453,9 @@ static bool usb_host_req_cpr_replay(USBHostRequest *r, return false; } } + if (r->p->state == USB_PACKET_ASYNC) { + r->host->cpr_inflight = g_slist_append(r->host->cpr_inflight, r->p); + } return true; } @@ -1271,16 +1277,74 @@ static int usb_host_cpr_quiesce(USBHostDevice *s, MigMode mode) return 0; } +/* + * If CPR is aborted the source resumes with the drained packets still owned + * by the controller as in-flight; re-issue them on the preserved fd. Control + * transfers can't be reconstructed from the packet, so error those out. + */ +static void usb_host_cpr_reissue(USBHostDevice *s) +{ + USBDevice *udev = USB_DEVICE(s); + GSList *it; + USBPacket *p; + + if (!s->cpr_drained) { + return; + } + + for (it = s->cpr_inflight; it; it = it->next) { + p = it->data; + /* + * Replay from the start of the TD. A transfer that already moved some + * data is re-run in full, so an OUT fragment the device consumed may be + * sent twice. Only the paused-VM pre_save fallback queues such partial + * transfers here; the normal drain delivers them as short transfers. + */ + p->actual_length = 0; + if (p->ep->nr == 0) { + /* control endpoint: can't be reconstructed, so error it out */ + p->status = USB_RET_IOERROR; + usb_generic_async_ctrl_complete(udev, p); + } else { + usb_host_handle_data(udev, p); + if (p->status != USB_RET_ASYNC) { + /* completed synchronously with an error */ + if (usb_host_use_combining(p->ep)) { + usb_combined_input_packet_complete(udev, p); + } else { + usb_packet_complete(udev, p); + } + } + } + } + g_slist_free(s->cpr_inflight); + s->cpr_inflight = NULL; + s->cpr_replay_all = false; + s->cpr_drained = false; +} + /* * Run the CPR quiesce from the vm-state handler, on the stop-for-CPR - * transition before the device/irqchip snapshot. + * transition before the device/irqchip snapshot, and re-issue the drained + * packets on resume if CPR was aborted. */ static void usb_host_cpr_vm_state(void *opaque, bool running, RunState state) { USBHostDevice *s = opaque; MigMode mode; - if (running || !s->dh || state != RUN_STATE_FINISH_MIGRATE) { + if (running) { + /* + * Resume: re-issue drained packets and reset state, even if the + * device was lost between the drain and now (dh == NULL). + */ + usb_host_cpr_reissue(s); + return; + } + if (!s->dh) { + return; + } + if (state != RUN_STATE_FINISH_MIGRATE) { return; } mode = migrate_mode(); @@ -1486,6 +1550,8 @@ static void usb_host_unrealize(USBDevice *udev) if (s->cpr_vmse) { qemu_del_vm_change_state_handler(s->cpr_vmse); } + g_slist_free(s->cpr_inflight); + s->cpr_inflight = NULL; } static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) @@ -1493,6 +1559,9 @@ static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) USBHostDevice *s = USB_HOST_DEVICE(udev); USBHostRequest *r; + /* Packet is being cancelled -> it mustn't be re-issued on failed CPR */ + s->cpr_inflight = g_slist_remove(s->cpr_inflight, p); + if (p->combined) { usb_combined_packet_cancel(udev, p); return; -- 2.47.1