From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from MRWPR03CU001.outbound.protection.outlook.com (mail-francesouthazon11021082.outbound.protection.outlook.com [40.107.130.82]) by lore.virtuozzo.com (Postfix) with ESMTPS id AFC96807A9 for ; Tue, 6 Oct 2026 10:34:42 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=R0nqTQmyxaHK27XmJ9Y7fCTynDqy1MRuRRf7yXJ4Afkmgl36OMAygsPCpmjchTX2K3N4EcNgCmYhfJvImusGzNiPqmrD8SgqZiauctC2wN1leLRvXC4lzpsYAAJe3s6ZD8D894P+GVuio9WvCCDwg9z8WQtsatis/0Rm3JQxMDxTEfeciTR3YvoSlLUaO6VQcrH5BJ1j+OSO/tVSinBtVmr3B4HT2QzaQw8l2vFkXHg/2W6MVlswEQi4S3MeITHqh4dz72Ygkpcy0AoybD43UP+vAHDDz+JeQzm+o/SdTcMUJJV7tbOp4OAs1wtuRkYqbUHhAWL5qLFbOxo1N8ujag== 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=OMr/LinQtPPezEARPUqfPwVpBUYMaCk437XeqV/TT5U=; b=CxUo5uPgHMSYCplxwJbHuQluL9tO08dPMJ+4V+4cB1IiZdEXGNciE+UGI6vp3857vqCdL4tmqIHA5a+5Awq6JclAINpjmVVgcdx1CiqMNp2SZ2DybpcxO972hyzI45NZLJfwtLJ+fyR44zg4e37Y+RcHxtCi1GjB3H/vCxvMa7U4tkrUPjfRac8QnfkAJJLAffawNjaqFCNTRdqs8CejYKgl6E+OvKcxU6dc6HbsI06Bb+p/wOio4m0dGyyfW9vlQTxBRZKCE+0nen3eB5BGha1uS3PoGa6r62n4IM1Q7te7wmiqZSjlNZU3S9t6YuZzfqqkO4d+tt320S00/jLfqw== 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=OMr/LinQtPPezEARPUqfPwVpBUYMaCk437XeqV/TT5U=; b=KloLLSL6Lqt08DqwvKunykrETbaCr3SGARTracHyowuCrMqwydZQTm5U1kPfI3pD8/h3kx4M8/FjnLoaPqq+vQI0LPpvz+B8uMe1sQLY7JSvnQ6H02/fdDUl77vTnh2TCB2Xbja5ErRFnz7TTB+E6fxKCRR789tPrYlUAEgp2h8OkJoaKtoWiOinCLaBNDLHAhywLYPDC57SV09QE2RxlG8RmrHz03Oo+GdZ8JS3dB4L/61u4f4DswTCNwpAld6Q8HfjeIPkUMqlW0O3bYAdg9e0W8NY/LkP69QPr+QmH264l4ewAb59mTJe/w78q2fWN/xdOcYsRsa5miofS0syIg== 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 GV1PR08MB8692.eurprd08.prod.outlook.com (2603:10a6:150:86::16) 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 10:34:37 +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 10:34:37 +0000 From: Andrey Drobyshev Date: Tue, 06 Oct 2026 13:34:30 +0300 Subject: [PATCH QEMU HCI-8.0 v6 06/12] 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-v6-6-8688ec9576d1@virtuozzo.com> References: <20261006-usb-host_v3-v6-0-8688ec9576d1@virtuozzo.com> In-Reply-To: <20261006-usb-host_v3-v6-0-8688ec9576d1@virtuozzo.com> To: svt-core@virtuozzo.com Cc: andrey.drobyshev@virtuozzo.com, den@openvz.org X-Mailer: b4 0.16.0 X-ClientProxiedBy: VI1PR09CA0115.eurprd09.prod.outlook.com (2603:10a6:803:78::38) To VI0PR08MB10656.eurprd08.prod.outlook.com (2603:10a6:800:20a::12) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI0PR08MB10656:EE_|GV1PR08MB8692:EE_ X-MS-Office365-Filtering-Correlation-Id: 085b3a66-b2b8-43ec-9ceb-08df2395696b 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|52116014|376014|23010399003|1800799024|366016|10067099003|56012099006|5113699003|5023799004|22082099003|18002099003|38350700014; X-Microsoft-Antispam-Message-Info: JESokZRbgSeV1WGjeZDmfHNibUe5kjrmdiXdJokrOgpYHwU5XWokVXCTabaVI7rrTYMW+4wu6lLOWyr8+R4tNN7Y1kpg3YEDNebz1yOGUFKL5JsxzhPKSSJ7IFCJRO6lTAmUYFwnwxzP6szUYDfnTgPKcK3+MEcY2+F8G/spjdXYO+H6IWmcAgkgYCIeFyKW8TM0Uw55059HY5sQEIHiCqMjD2iKfarAxm0Z0WIjJ4eVd8zHYSo7IFUOl7BkOSXaw51y26ROT+ywRCf0WyaaCn3D5z9r17EWKUEVoy03tAbfGxu2fkwsx/F4w5XvemhaJQJJLOooa5+hZ0wL+DNVyGIOiWx9rGqd1EZWO+gewVKrtaKHv7Q39lPiLZEdZJx9h6pcJktwZMX/V3GPnw9anHlaVV8bXCEw+bipi1nl9XjDgHzQ0h9oovWTatBVRPDNXC5R/vwBoZCRhL/PKY8h6HwQu0g3xzgdlewRMnuue3qr43gNK8h0cKXf+KOgOO4+/bGJYKVpXBEeRJm3/fKzyF2hSCKs4AiJ04En0S0bu1oIwMDd0ljZyM7d4C8SHSmmIuh1zHzMT4cUjb5AmqqmBhuP96xkBajKbTicUXHL2EeH/vF5uj6yAK5D2LfyIPT9iAGYArjAYZiG+ZlFAm2p+bNsffNq/rhzXHTcHtMIMzC4/giokLlxp0vLYfLEGo7f8l9ERVYFiFis3GgKjitDWUIn2BgZTrJnpcxZmpCr+4Y= 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)(52116014)(376014)(23010399003)(1800799024)(366016)(10067099003)(56012099006)(5113699003)(5023799004)(22082099003)(18002099003)(38350700014);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ZGExcHFyelhYVkhKVWdncnFRb1Y4MFh0d21hdVJDMWZIM29LNE1VOGNIL3hT?= =?utf-8?B?WUtRUEZMTk52TmU4WWVvUjV2MGZ0dXEvaHJWWWNoRHRuTGt4Y2RYb0lWY1pC?= =?utf-8?B?QjNDbDdJWW8xMWE3RUEyaTVPZVlvcVJsemdyUGNuNVFYYmozaVJ3NURJcHYx?= =?utf-8?B?cThNcHM1RzNKdjhIbFN1MnBhaThkZElaeGRHc1VxbXY0ME85SzNuYU9YUFpl?= =?utf-8?B?dVplREplaXUvMHVRRjk1bHEwOHFJeUhKRjRaOTEvTEtWbnF2ait5akRLSStM?= =?utf-8?B?c0lQSFgxOXJsVHN1eDN3L2pqbFY5bWx6L21CZkd6ZERMaXZKMlRYZXk5cDVN?= =?utf-8?B?Mmgway9RLzZUekRKQ3h0c1djMVRLU29ML05DdmpUdDFETlVFS1lYQk1YdlFJ?= =?utf-8?B?YkNORERyT0hucFhxQ2lwUUdORnZJcHd2Ui9QSjNJRjcwak5PNGdWbEY0dTdn?= =?utf-8?B?WFF4UHRYa3kwajhHZnBwOXltUjBhVUtTdEt3aGd0V3ZPMG1URUJHaEs5cDFN?= =?utf-8?B?UWFYTFFISFZNaDlUVzNJLzFVM3R1L0pOaTFTTmcybnh4bDBxQkdNTE9QVEtm?= =?utf-8?B?MFhBZlpMeUhBc1pGK1hwKzErL3VZZjRxeWtOZVBqWjI5UTZFd3QrZFovcCtv?= =?utf-8?B?VzZ5YnhCNjJnTVVBVFZ0VEEvQ2ZvSU4weU8rK2JJR0tWTllDL2V1aU55VWd0?= =?utf-8?B?dG9CeENERjR6WXJBeWJmb2pCUCtEZ1o1bUU4SkhPazdWdDM0amxrYUNhSTln?= =?utf-8?B?WFhVZW9xaEREK2RITElYZkFSaEozamkzSmFmeitlRnpyYUFyQ2h5OUpDRmZy?= =?utf-8?B?eXIzSHR3cHlFWTBtTzZ0bnZWNlNBcGZqc1pjZEpvYStTb1dRamd0ZkorTG9q?= =?utf-8?B?UFV3ZXRrVkx5STZnSklUOEJsR0c1YStZMDZxMWphNnE4cjRuRncybzhubDFJ?= =?utf-8?B?KytZMEdzeU4ySFFpSUdRT3dnUGdFd1R1S0p3dm8wbUZOREtIRkUxQm5UTnlX?= =?utf-8?B?UnZsdEw2Y05GcXRiQlF3cEFTU201S3hvcFYrUWpQZ2JFMCtXN1RXYjhPNHpw?= =?utf-8?B?Z0dwOHNpR1JmMXpaUVg0RzBQZ1hZbWFPZ2lqWFhPcXlFYkJjWVJIdlYrZ096?= =?utf-8?B?R1BVS0FmMHR6UVdRTVF3WjVHZERRSE9WcG1XNVFtVDJpZU90UXhDOWFXSUlU?= =?utf-8?B?TE5NdVh3SzY0Q3JrTGRnYlRxMlk5b2YvRFJqU3RveVZBWVFhcEVmeGMzSUNQ?= =?utf-8?B?SmZnK0RsTkRxWUtEVVdPY1Q3a1BmaWJ2cE9FYXgvbnhFOEdBTXF6a0lJd01J?= =?utf-8?B?YnV4L3R2Z29wdlBrbTNRRlVRUEdwWjcyc0FZTlVDZWxRWk9qcVU2WCs5UWhw?= =?utf-8?B?VWd5dDFwWEdFRkd6cDF3Ti9BdFAwSjFWbk9Ddk5wNVVPRGpNNmVXN3NUQ1NS?= =?utf-8?B?UUhaVURNWHkxRUlvcXZodXZvVmFUNlRHM3V3alY1L2VvT3RVSEVjMkNqVTUx?= =?utf-8?B?TFZJMGZ3Mk9jNk9qeitxaUVEdGg3bjJIU1R0YW1sSkdDTWNpMVlKbFVYdXBx?= =?utf-8?B?U041T2p6c2pQdVA4dWNUSFhJMjJZZ3UrbGxxaU5UVmR5ZnF3WEdYZjVERzZL?= =?utf-8?B?Tmx1SFFEU0t4REFkbFQwNUpJUXpyYjA5ZXh1WC9WdG5rSlQrdENBSkVjb005?= =?utf-8?B?ckJkUDFPMnh6TWd2OU1JMUNnaStCR2wzcnRYVWlSR0RudlN4VHQ2NW9hcUV2?= =?utf-8?B?OU0zUGVNaVBBbXdEZS9OWWRTRmlWa1A2VmtacG5Sc1FYa3Mwa3VSclpNcENX?= =?utf-8?B?QkU0ek5GSWVZcHNWRlplY2o0R1JlbFpiZkxDV1BBRkN2N1BHTkpES0IxZTdT?= =?utf-8?B?VHROOEVwUFJhVmlING1acUZDbE44cjF0YmZNWHNKT2dLcEM3OHhqR1VVM2lE?= =?utf-8?B?eDdBSU5kTFVWVXRWbTlDMGd6VGFRVXhMa0tOdjZweC91UFE5TURUcFlqT0lh?= =?utf-8?B?blRaSmhlY1hZaU9EVTNTNnpVc3g5YlpqNnh1WDVNelhqMnpPdjZxZUhsUUJu?= =?utf-8?B?eWZkYnNRRGtiK0hXNDdOM3lsN1JhYUFhQjlWR1FhbE9Sa1d6RXFoWFpUMmJE?= =?utf-8?B?VnQ3RTJCTHdxSnJCYkRZTUZ2VStDUitxbFFHOWlRNkdyL3pEWEplaXFyR2N6?= =?utf-8?B?NUxKd05kYmN0dGZTaHk2ZnFDTTM1cGhERnBkU0tNMkQxS1JQRWxYRStOaXJm?= =?utf-8?B?MjdsNi9kQUhHUENxWEJOU2xKTTc5a2F4MFZ4MU9ZMXRLckhzTkNwY0pMS2lm?= =?utf-8?B?TkdiTkdPclhaZEFVOVE0SlJOMEE5cW9CR2hXOU9hK1oxSmxUWnU5enlzSTZW?= =?utf-8?Q?d9JzMwk7NTxyMcCQ=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 085b3a66-b2b8-43ec-9ceb-08df2395696b X-MS-Exchange-CrossTenant-AuthSource: VI0PR08MB10656.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 Oct 2026 10:34:33.7660 (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: 6mJiux2Ecma/umfiAW9TojUf6ZSBFMf/MEc0tr/n6BbI60/C4httfZXrPUdNKdTrmZI3ElkNAld723FQDcveBjw6UEBQI7UJWo5QcS8v8jg= X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV1PR08MB8692 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. Signed-off-by: Andrey Drobyshev --- hw/usb/host-libusb.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index d68fe0df03e..85509dc2d83 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; @@ -432,8 +433,9 @@ static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p) /* * Classify a reap during the CPR drain (r->p is still set). One that moved no - * data is suppressed for the target to replay; one that moved some is kept as a - * short transfer. Returns true if suppressed, so the caller frees the request. + * data is suppressed and queued on cpr_inflight for the target to replay; one + * that moved some is kept as a short transfer. Returns true if suppressed, so + * the caller frees the request. */ static bool usb_host_req_cpr_replay(USBHostRequest *r, struct libusb_transfer *xfer) @@ -446,6 +448,9 @@ static bool usb_host_req_cpr_replay(USBHostRequest *r, xfer->status = LIBUSB_TRANSFER_COMPLETED; return false; } + if (r->p->state == USB_PACKET_ASYNC) { + r->host->cpr_inflight = g_slist_append(r->host->cpr_inflight, r->p); + } return true; } @@ -1254,17 +1259,26 @@ static int usb_host_cpr_drain_xfers(USBHostDevice *s) return 0; } +static void usb_host_cpr_reissue(USBHostDevice *s); + /* * Quiesce the URB data plane around a CPR switchover: drain when the VM stops * for CPR -- before the device/irqchip snapshot, so completions raised during - * the drain are migrated too. + * the drain are migrated too -- and re-issue 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 (!s->dh) { + return; + } + if (running) { + usb_host_cpr_reissue(s); + return; + } + if (state != RUN_STATE_FINISH_MIGRATE) { return; } mode = migrate_mode(); @@ -1356,6 +1370,45 @@ static libusb_device *usb_host_find_ref(int bus, int addr) return ret; } +static void usb_host_handle_data(USBDevice *udev, USBPacket *p); + +/* + * 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 */ + 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 error */ + usb_packet_complete(udev, p); + } + } + } + g_slist_free(s->cpr_inflight); + s->cpr_inflight = NULL; + s->cpr_replay_all = false; + s->cpr_drained = false; +} + static void usb_host_realize(USBDevice *udev, Error **errp) { USBHostDevice *s = USB_HOST_DEVICE(udev); @@ -1470,6 +1523,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) @@ -1477,6 +1532,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