From nobody Sun Jul 26 12:28:58 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=bytedance.com ARC-Seal: i=1; a=rsa-sha256; t=1781786916; cv=none; d=zohomail.com; s=zohoarc; b=V8S1SvTydz7zP5lO2HqvobeKAG7b5r8nw4lgOYaj79sAUgnZ+4YFwZtKn55xr5OdyGwJfHFP05a7Eg0inCGwnJwrkIdC978f24+ByAOhF2KvD1PDbDBMJsnuTwoW8jggabQj9g9SbsA1GXKGJEXpikhofL3hOxkcnVSWMwBVrL0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781786916; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=3npWah6PK9X89Q87/+arVCTPuG7yXO4k/Ki/KNmHuao=; b=Fh1hHk+NnZEkBN7A8PISenjWmoDcYVvmGBjvvfhPABVg51i3wPk4lGvdTu3KiskXpykpjD+aYliq5az1Mm4e4X0ptuDw9Du5MEXVa0kyZAFGErOIQj2EmpGUKWw0Iq8PXRukDdcfuO1sQZTmc1qGC6l2EOHPJAVjk9KP8Vl/uOI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781786916754591.75278359469; Thu, 18 Jun 2026 05:48:36 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waCAQ-0001px-I5; Thu, 18 Jun 2026 08:48:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waAWM-0000zp-OF for qemu-devel@nongnu.org; Thu, 18 Jun 2026 07:02:58 -0400 Received: from va-1-114.ptr.blmpb.com ([209.127.230.114]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waAWL-00048F-2C for qemu-devel@nongnu.org; Thu, 18 Jun 2026 07:02:54 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=2212171451; d=bytedance.com; t=1781780565; h=from:subject: mime-version:from:date:message-id:subject:to:cc:reply-to:content-type: mime-version:in-reply-to:message-id; bh=3npWah6PK9X89Q87/+arVCTPuG7yXO4k/Ki/KNmHuao=; b=BqBABcjU/DvpD1SntNsPyj2EPnwxtk/DG8zaQsd6qydfkVNEfDILDTtDlajRaFHeYtkrhh JHmwwo70iYoxFxP5xLdqAh92BBbGiATSVdjU7ifHyfpjG/DImwoXBY8p1QBgWn13jv/EBI VKyblCzdvQhiQdziQuMOu4mf2k6KMbY3gBPFQVdrL16pSyP0WpMkDiJNd1sQeq5nYbQoNW YGMljWDaW0I48bISLUSfAhJP/BxthhlL/QacENCnjlel5PX0ISiC7qNzyoinTV/GuSe/0l HXCVIiPrOyY0e+q5BQwg131YXuHv3hE5hbmBBOIcnMlHkxSHCmzfIs29UMlZig== X-Lms-Return-Path: From: "Xiangfeng Cai" Date: Thu, 18 Jun 2026 19:01:18 +0800 In-Reply-To: <20260618110119.3084296-1-caixiangfeng@bytedance.com> X-Original-From: Xiangfeng Cai Subject: [PATCH 1/2] hw/usb/hcd-xhci-pci: break host link cycle so device_finalize() runs on unplug X-Mailer: git-send-email 2.55.0.rc1 Cc: , , "Fabiano Rosas" , "Laurent Vivier" , "Paolo Bonzini" Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 References: <20260618110119.3084296-1-caixiangfeng@bytedance.com> To: Message-Id: <20260618110119.3084296-2-caixiangfeng@bytedance.com> Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=209.127.230.114; envelope-from=caixiangfeng@bytedance.com; helo=va-1-114.ptr.blmpb.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 18 Jun 2026 08:48:01 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @bytedance.com) X-ZM-MESSAGEID: 1781786918836158500 Content-Type: text/plain; charset="utf-8" The xHCI PCI wrapper embeds an xhci-core child via object_initialize_child() and, in usb_xhci_pci_realize(), points the child's "host" link back at the = PCI device: object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); "host" is a DEFINE_PROP_LINK property, which qdev registers as an OBJ_PROP_LINK_STRONG link. A strong link takes a reference on its target, so this creates a refcount cycle: the PCI device owns the child, and the child= 's strong link pins the PCI device. On unplug (guest ACPI eject or QMP device_del), pci_qdev_unrealize() calls pc->exit() but never unrealizes the no-bus child. object_unparent() then dr= ops only the parent/bus references, leaving the link reference in place. The PCI device stays at refcount 1 forever, so object_finalize()/device_finalize() = is never reached. Symptom observed under gdb after eject: p *((Object *)dev) =3D> ref =3D 1, parent =3D 0x0, realized =3D false p ((XHCIPciState *)dev)->xhci.hostOpaque =3D> points back at dev Fix usb_xhci_pci_exit() to tear down the embedded child explicitly: unreali= ze it first (so the set-link-before-realize check passes), then clear the "hos= t" link. This releases the strong reference, lets the PCI device refcount reac= h 0, and allows device_finalize() to run. Fixes: 8ddab8dd3d81 ("usb/hcd-xhci: Split pci wrapper for xhci base model") Signed-off-by: Xiangfeng Cai Acked-by: Marc-Andr=C3=A9 Lureau --- hw/usb/hcd-xhci-pci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index c5446a4a5e..b124251ae3 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -196,6 +196,18 @@ static void usb_xhci_pci_exit(PCIDevice *dev) && dev->msix_entry_used) { msix_uninit(dev, &s->xhci.mem, &s->xhci.mem); } + /* + * The embedded xhci-core child holds a strong "host" link back to this + * PCI device (set in usb_xhci_pci_realize()), forming a refcount cycl= e: + * the PCI device owns the child, and the child's strong link pins the= PCI + * device. On unplug, object_unparent() only drops the parent/bus refs= , so + * the link ref keeps this device at refcount 1 forever and + * device_finalize() never runs. Unrealize the child first (so the + * realized-check in set_link passes), then clear the link to break the + * cycle. + */ + qdev_unrealize(DEVICE(&s->xhci)); + object_property_set_link(OBJECT(&s->xhci), "host", NULL, &error_abort); } =20 static const VMStateDescription vmstate_xhci_pci =3D { --=20 2.55.0.rc1 From nobody Sun Jul 26 12:28:58 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=bytedance.com ARC-Seal: i=1; a=rsa-sha256; t=1781787004; cv=none; d=zohomail.com; s=zohoarc; b=RB6TbbsdrHwOAyORac7u14cZ9cBgaLgez7U0cZdOrvvnrjiRAnCbgCsp2sqJSug7g8M8/Iki6Lq21VFxtUbtH+mixf29DiRfP8Fw8g0kKkj5vYcUpYaiBkEhhJOC0xqultwO36tHm5ac1Z6RjlTM+c6FC794ABhob+IyaoXUjnU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781787004; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=aNMwjAAnCe0QKH0dKPiG5znG6N5MLtjse8A7jPAR4Xo=; b=EhRv/afLWAnGAS4iZHkAZFco9Hbr9kxcGVXplEFT6ZxQW0D5TueVAjkv2Z2e2rQSFS+yUbVBzyaGbLOgYic9DX9bszdiQP/spXf1JAdoIguUdtZsKoGVvcna9rqvQ8K9vH6HiTRwykqkvOnM4vu0sWFUcOIuGhrxyk5zGYJAmOA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781787004108429.87702294019107; Thu, 18 Jun 2026 05:50:04 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1waCAv-0002Ca-7f; Thu, 18 Jun 2026 08:48:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1waAWi-00014M-Bw for qemu-devel@nongnu.org; Thu, 18 Jun 2026 07:03:27 -0400 Received: from va-1-112.ptr.blmpb.com ([209.127.230.112]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1waAWb-0004V4-FY for qemu-devel@nongnu.org; Thu, 18 Jun 2026 07:03:10 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=2212171451; d=bytedance.com; t=1781780583; h=from:subject: mime-version:from:date:message-id:subject:to:cc:reply-to:content-type: mime-version:in-reply-to:message-id; bh=aNMwjAAnCe0QKH0dKPiG5znG6N5MLtjse8A7jPAR4Xo=; b=YeKI3xGvOAuh0SCtK8/sUBYm+8Yfnmn7iOtWjJI1lYeDAh1RhVgjySg4dFAGvivdyfjXSj gu/1HwBkqZX+esX/kfI1cwQDIh4/czN4jCSMNKDLQ3vvayuaX5x4j8g4emhyJ8TcSrvPCY KQbwQ6R1xjtE/woQu6BWlnLjHNtCT6l3Scz8metyeTD7Ftp1NtyiaKGN4TSMPsw78tkAFZ m67LRvQGgOMWMR+ZZuQk4fWQZ9sGABGcLdcN4LSlR4lvEFf2i5ozlKLoXoe5n8nLPDNMZs 34hFMqu5lhCaGHCGJw0czy0tcC2OcrlssawPYoTcc1kYF4uewOli2yxqvDUcGQ== Cc: , , "Fabiano Rosas" , "Laurent Vivier" , "Paolo Bonzini" Content-Transfer-Encoding: quoted-printable X-Lms-Return-Path: Subject: [PATCH 2/2] tests/qtest: add xhci-pci unplug finalize regression test In-Reply-To: <20260618110119.3084296-1-caixiangfeng@bytedance.com> Message-Id: <20260618110119.3084296-3-caixiangfeng@bytedance.com> Mime-Version: 1.0 X-Mailer: git-send-email 2.55.0.rc1 References: <20260618110119.3084296-1-caixiangfeng@bytedance.com> To: From: "Xiangfeng Cai" Date: Thu, 18 Jun 2026 19:01:19 +0800 X-Original-From: Xiangfeng Cai Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=209.127.230.112; envelope-from=caixiangfeng@bytedance.com; helo=va-1-112.ptr.blmpb.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 18 Jun 2026 08:48:01 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @bytedance.com) X-ZM-MESSAGEID: 1781787007181158500 Content-Type: text/plain; charset="utf-8" Add a qtest that hot-adds an nec-usb-xhci controller, requests unplug, resets the system to process the request, and waits for DEVICE_DELETED. This covers the xHCI PCI host-link refcount cycle by verifying that device_finalize() runs after unplug. Signed-off-by: Xiangfeng Cai --- tests/qtest/usb-hcd-xhci-test.c | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-tes= t.c index 0cccfd85a6..b58fa1e2da 100644 --- a/tests/qtest/usb-hcd-xhci-test.c +++ b/tests/qtest/usb-hcd-xhci-test.c @@ -10,6 +10,72 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/usb.h" +#include "qobject/qdict.h" + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + const char *device; + + /* + * Other devices might get removed along with the removed device. Skip + * these. The device of interest will be the last one. + */ + for (;;) { + resp =3D qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); + data =3D qdict_get_qdict(resp, "data"); + device =3D data ? qdict_get_try_str(data, "device") : NULL; + if (device && !strcmp(device, id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +/* + * Regression test for the xHCI-PCI "host" strong-link reference cycle. + * + * The xHCI PCI wrapper embeds an xhci-core child whose strong "host" link + * points back at the PCI device, forming a refcount cycle. If + * usb_xhci_pci_exit() does not break that cycle, the device's refcount ne= ver + * reaches 0 on unplug, device_finalize() never runs, and therefore the + * DEVICE_DELETED event (emitted from device_finalize()) is never sent. + * + * This test hot-plugs an xHCI controller into an ACPI-hotpluggable bus, + * requests its removal and waits for DEVICE_DELETED. Without the fix the = event + * is never delivered (device_finalize() is blocked), so the test would + * hang/time out. + */ +static void test_xhci_unplug_finalize(void) +{ + QTestState *qtest; + const char *arch =3D qtest_get_arch(); + + if (strcmp(arch, "i386") !=3D 0 && strcmp(arch, "x86_64") !=3D 0) { + g_test_skip("Test only runs on x86 (ACPI PCI hotplug)"); + return; + } + if (!qtest_has_device("nec-usb-xhci")) { + g_test_skip("Device nec-usb-xhci not available"); + return; + } + + qtest =3D qtest_initf("-machine pc"); + + qtest_qmp_device_add(qtest, "nec-usb-xhci", "xhci-finalize", "{}"); + + /* + * Request device removal. As the guest is not running, the unplug req= uest + * won't be processed until the next system reset, which performs the + * removal and triggers device_finalize() (and thus DEVICE_DELETED). + */ + qtest_qmp_device_del_send(qtest, "xhci-finalize"); + qtest_system_reset_nowait(qtest); + wait_device_deleted_event(qtest, "xhci-finalize"); + + qtest_quit(qtest); +} =20 static void test_xhci_hotplug(void) { @@ -50,6 +116,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); =20 qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/unplug/finalize", test_xhci_unplug_finalize); if (qtest_has_device("usb-uas")) { qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); } --=20 2.55.0.rc1