From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from GVXPR05CU001.outbound.protection.outlook.com (mail-swedencentralazon11023102.outbound.protection.outlook.com [52.101.83.102]) by lore.virtuozzo.com (Postfix) with ESMTPS id 1C968807CE for ; Tue, 6 Oct 2026 16:28:02 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=OP4wx1KfosuFRGPRyrAQrzVz0sIc3RJdPwlUVFCgcm0lt3FkoohDmMe5l0kux9RYmi3fHP77cLaWV+ivyzTP8HUSAcAPVcT1kng3I2dXIdLPkTLvA+zVDz0rTQusueMe5mukJzChfsUuhj26Je5/Sk8d9x6d8tGVSh9fjwl3Lyxa883/U/YDYKE1cYupfohL8NKfImjn6lNn1Gcy+05DplG7CARWXs2UYnxAwBX07llWnrMgfL047dfN1CeMJS3Lys4AgQk4U0FtvW8CztqNtNKSnmH1FbaRND9Zjb6nmXejea7dE/EU1mIMjcXzM58pd7yefrflEBaVk0wx8NQwCQ== 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=KTSwwXNni9uP77+YghMiBXyB0Q/qH8e1ET4jbqA4oLo=; b=Ckm4LaHDzmzZ4OruJW2dz5Qq9pbcLvK6JKA8EvMEfQhOdXYhk0af9zkxhM/qDgfOFOV2seXnzUFox14V2KU86e+xHpq5494GZsyG5VfXHBulzxtijwWQYEyoZ3iB5CZlaaZzeLrXxZsGh4p++6n87NpPql3CC7jHnvT8b+vQFHl9C4fa9+vI3Hve8zbVZdzNr4NXoH0DsnU88ucUzpckJON8Z/K2mkhuVl31EcpGQIX3ryS+Zlf14kGxrUap6LRG6e1LGmZtqHgwK5SAZKZ2YxnScPzkgKpVMACQjcWu/ofsPCu8BT6s30EKB1jTaCLP7EytbGX3rEiGxNc71etZeA== 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=KTSwwXNni9uP77+YghMiBXyB0Q/qH8e1ET4jbqA4oLo=; b=q5FWEFLC8zE9TxAOMAVnvtRe5g+DGZA62ni3mLQ4vQXhqjzKfaNpUCptmnsMwyaIBKny9drd1gL87/vSiKxp8U619ygrW++UF3nJVtSnpm6+crSmPdVq/D45nxJB/w5SghqttgqUDMbbjoyZgWqWmvkqcRWnU0sR5nTpvryQgDmOKEZVZkRNklZEZwAQxXWtSxlJYvOJc7FWS1omr7m8/feXUBk29IjxlTulkhhQPS5yiWd6M+9sVv16FIxjyEgkzLTtMiVNYBshwYjmRjgXC03YZtys1ArbIAF3zxyPmT5XC9Bv96abF4CnMf5uGk12d4H5RAZO6stOMzuwlOMi+g== 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 AS8PR08MB8899.eurprd08.prod.outlook.com (2603:10a6:20b:5b6::19) 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 16:27: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 16:27:59 +0000 From: Andrey Drobyshev Date: Tue, 06 Oct 2026 19:27:58 +0300 Subject: [PATCH QEMU HCI-8.0 v8 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-v8-7-72ae4fb8977b@virtuozzo.com> References: <20261006-usb-host_v3-v8-0-72ae4fb8977b@virtuozzo.com> In-Reply-To: <20261006-usb-host_v3-v8-0-72ae4fb8977b@virtuozzo.com> To: svt-core@virtuozzo.com Cc: andrey.drobyshev@virtuozzo.com, den@openvz.org X-Mailer: b4 0.16.0 X-ClientProxiedBy: VI1PR08CA0230.eurprd08.prod.outlook.com (2603:10a6:802:15::39) To VI0PR08MB10656.eurprd08.prod.outlook.com (2603:10a6:800:20a::12) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI0PR08MB10656:EE_|AS8PR08MB8899:EE_ X-MS-Office365-Filtering-Correlation-Id: 1d7b99d7-8322-4fd2-d825-08df23c6c7c5 X-LD-Processed: 0bc7f26d-0264-416e-a6fc-8352af79c58f,ExtAddr List-Id: svt-core@virtuozzo.com X-MS-Exchange-Transport-Rules-Loop: 1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|366016|52116014|376014|23010399003|5113699003|22082099003|18002099003|38350700014|56012099006|10067099003|5023799004; X-Microsoft-Antispam-Message-Info: 7mS8nJ0/uJm/PON+I20ZM5vRIxhJm8TVYNdPCnjnp2B2y8y/YCDiAfsi0Nl/TzJEyMMmxcQgY8QasfT+UbvuFh5pavorz3VgjuPrmCBFP6CNezieXF/O53wniBvJxLUQb6v1VM0pRCuukADMecmeNxvu77p81Qk2ioBW3cTMc5iIplBRGv4M+aRCU1cyibyxIrH2JyJnOGdU6ltkYVSxe2nAyyjXkts947DuuXiIzhp+ZEj4BA+NrGmqV+j+EHTHyiifolb8turiqpgLtaYIDKFI1OliSJFcuLffs/YAbvQP+rSXuRSGqq2BQ9H59rI87PRRWnosArMIPrUTgJJd5vGoFSXtnIdFkcBSMoIOP6+Q8P9DCTA5R4aoDt9/F2PU7wbuo7rAvFJnjO1119KuKhGyTx6Lq7YesUmaNCqtbmdSMxX98Uhajcfv4znNsX0ZyEvCJevaL3rnysLqFNuwkv8sqNOeyrPGwqw9qHimxHNKQbhA1h4zg7N4WQkZbcwOPTtqww5gNhYmytmRZdinbiTVxFqc9fdpSBTvzG1ClGduW5XFy0uZrSOOIn7yl+5gIY5bsta0dccMEHo9f8+LciQCr5mlMnjmKKNZY59WNFaX+gZUWZqHj9U+J2DmZjw4txL+rXLMYd42w5/m5uxU83LB/imUAOOfD8DFg8SLw0uVZKinpSeJ2Ejb57uwDaqk8ZG9syqwpNH9UekKVc54SNcTwbTSzipJOGxRHwNF/G8= 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)(1800799024)(366016)(52116014)(376014)(23010399003)(5113699003)(22082099003)(18002099003)(38350700014)(56012099006)(10067099003)(5023799004);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?cVlMS2RzaFp0aWxvNXJwc2VxRk14WXdkcUhDTEYvK05ONXUvdFkzV0tIMUp0?= =?utf-8?B?ZWFqTXIxSnNQYnNxNnMvRUtLc0lDWFJrb2RQaVlWeDBCUDhJeE5UV2dReTVs?= =?utf-8?B?YjgxTmVMRWt2dHlKOXN6TmhLNTYzWFl3ZWI1WjIrUlMrNEVjaE9JWlRNOW5Q?= =?utf-8?B?U1YySFR2YmN5c2VuNzA3eXNZT1VGa3Y5VG9PbS9ibXlydVpqREdBcDdnZ2Jw?= =?utf-8?B?Z21McjhXNU4rejFNVHhqeDE3TUt0MGkwRWxiczRDcXhVZFRBTXprMEtvTzlV?= =?utf-8?B?VW9jZGY4WCtmNWtoRWRuSGp2SVp2Z0xNVjlKNW8xTWJJM0pGdFlIRmxxT2Y3?= =?utf-8?B?MjI3TmZJTVZBSlU4d29TbG1PQTA0ZWlUeG9LL3VCVVRxY05ubCtGNktVMkFC?= =?utf-8?B?ZmZIenVNTjJFMENqNEo1Y1gyQ3RtT2FUVmFQL0xaRkxRemdqRnpsSno3Z0s3?= =?utf-8?B?eVZWSUNrUnU5d3VKeGlOVzViVmU0em1DcXBiZVpCaXJKaXMyYmNqM0hHUUt4?= =?utf-8?B?WThQQ1BFSmMwWXdPWHRjUmIvYVYzcGUvaTJFeVNvTFd1UGVZelhPbmluQkFW?= =?utf-8?B?N2x5dkk5MytNN0dkb3VLTDR0UGtPdCtQZGFRWUhpNzBQK20yc2x4VlZ0blRu?= =?utf-8?B?L2RxV081RmRvYWJnQ3RBaHMxeVRBcGJoSDQxQ3BzSU85SzVIeFpxczB3cnU1?= =?utf-8?B?eVUrMWpUWEphLzNocEdNa3dvakdqVWM1dzBjZFN3TEVOMUE1QlNGQWN0ZW4r?= =?utf-8?B?ck9XZi9aNzBVNkNPL3Uyb0RaTnlWbU1XQkRGVW0yOWo2cGt2eDE4ZFV0NGt6?= =?utf-8?B?d2R1UnRoQ2F5MDd1U25yK0JjdXRSaCthekxZSXU3eDYxMlJzNHRkMmVZRnY3?= =?utf-8?B?SjY1MGg0cFZ5RlZVUDYyb0tFMk51cVY0cHkwK2xVb3d1Z2tXVkxWWU4rYlM4?= =?utf-8?B?cWxqa1YwWENUSzMzaEJxZUdjbWwrekRuK0wyN2VXamtMQzhBcEsrUEllZTls?= =?utf-8?B?NHFuMkUvaFpPOWh3U0dpZlZtRG5lRHd0c3JjUlE5cEZtc0NwbU5XZ1lCSzY1?= =?utf-8?B?MnlBR0duTFUxQk5CN2xSZWVJZjcrWUNYTUMxb2hDY09QeFkxOEV5Qkl6OTNG?= =?utf-8?B?K09jdHBocmFsRUZXK2xEQklnOU50UFJhU0RQK0RWOUpudUdXaGFFOTVHMThz?= =?utf-8?B?T2ZidnVnbXBYR24zbmRJYzR1NFhVR01RZHQ5ZzFGZFlLcS9YdWJNSTFHTU90?= =?utf-8?B?R2dTR0d2Y2c2bEN0R0JxWWg5cWNXNFA0TzN4Z3JWaEdWejMzV05CNEtVNVM3?= =?utf-8?B?Z1IxM0xoTEFyQ1pUaGxOOWRncVhyV2NQTkNuOGJ0SU9KR0x6TXpBbXlCZGFw?= =?utf-8?B?R3ZQK3U2UWpFL3NKM1U0SmVTL0Y1RGhFcDlULzN1TEZPelhQRlVWdjREZlBj?= =?utf-8?B?UEdXUmpOcngxRmNITHJLczdhYTZuV3lPa0E0QlczYmF2K240VHBTQXlpbUEw?= =?utf-8?B?amlXTHV0T2Mzb3FMWlRjNVpmNmlraEtjNjd4UVpsR2h2dlNKWFVDeTlYZ1R2?= =?utf-8?B?NktCSndUWmZaZ3NhWWpKNFR6Y0dNMVBVc3lZYXhic1VTbHhJYjRJOWYvbDFx?= =?utf-8?B?UEhpa1grc3AwRjh1QnRNM29ndDJITkZ6cXZDbkZGTmV3ZjgvN1EyUFBleEdN?= =?utf-8?B?T3N3K2tmM0g5ZHFPOFpPNVNYZ1JEbmU4S3pvR1BxT2RNQTR1cVNjWjJtWnJ1?= =?utf-8?B?UXpVaTFIUXBQM05HOEs0Rjl4NkZ2bk13OWVSS3lPT3N3N0JYM0xGOTlnVXM0?= =?utf-8?B?SlhNSEJiS3ovSHZpWkhMZDVRV1laM0ZnVXVURnBYU2ZKLzE3aU4zVDh1cXZX?= =?utf-8?B?TWJXR1MrS2xHamFDQXdJN2NXR09tQkN5ZDhzSDQwOWdDV21IcGhqWEdlU1VO?= =?utf-8?B?Z0dWRXU3Z0dJVHdEb2pwUnNIV1JJL0ZjT0h6dTFKT0psbmRYaXVGSzZTbjRN?= =?utf-8?B?c0FKeitTRmtwVTE2dDZJeU1RYzQ0LzRkUUplYVNwTzVsVlpocXNFeGVrd01T?= =?utf-8?B?VEl1TEZzUnRsa2M3TGQwdG45NUl3RUcwT1NEKzJpZGZTMFNqRUdjamQycVBv?= =?utf-8?B?Ymp6dEJVZGtDYWxLOUowM3hjK1hkNE1CcEFoWlpKZ1hIdklIcmE2ckJYL1Vh?= =?utf-8?B?MStINWp3cXR4Um43Z1B3OUtWdGRpY3dDR0R2OEtnMFllTDdVQXhackJJdzYr?= =?utf-8?B?Y0FhcVFyUXRrY3FDSjRPNFdmZGp4T0Jac09qazdyNW5peWtjcXVrd2F2TVgw?= =?utf-8?B?REZWZTFhYlFYQXZnRmIwSEdWc0FiQTdZK3VxSHYvZ2duMjNWN00yMFJoZmxo?= =?utf-8?Q?RY4ZKTWwrQCTfQVM=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1d7b99d7-8322-4fd2-d825-08df23c6c7c5 X-MS-Exchange-CrossTenant-AuthSource: VI0PR08MB10656.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 Oct 2026 16:27:57.2941 (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: DzfGMLj4EN7cZEBsR8ZR5N7G7TtEh9hYtClur8rd/C3lfE9qu8qldw/JodxVtFktEz9k9aszZy7/6vlr9cAZ7HNE9md1+M1nxNNulf+sPWY= X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS8PR08MB8899 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 413ddc12ee4..9cc7d72aa63 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) @@ -462,6 +465,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; } @@ -1284,16 +1290,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(); @@ -1499,6 +1563,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) @@ -1506,6 +1572,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