From nobody Mon Apr 13 00:05:19 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass header.i=thierry.escande@vates.tech; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=vates.tech ARC-Seal: i=1; a=rsa-sha256; t=1775751693; cv=none; d=zohomail.com; s=zohoarc; b=Hlzmu8gkUNDZl01EHtElkfEbJdnuJuQIMW4daka5aB1INKGxrxUWNqU9aA5oF7btAx0YDFWuk/XOBrPV9b7lq4jB7o8FmQS+nU5vbED36eeviAn7ceETdY9F2T8kDPRfvOEiWRxjH5GRgJdpXTC/06gpMMQSrUXqwfYdB3R7Q4M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775751693; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=0UnZsd6VMv3nhahDJTmKjpYZpYAGnJoqucRzautn7QA=; b=JoAH4+kM5Yd36jEKWthQT08Koj2r8qCyuwjyl+QxokGOMMo5Beod1ahze7GPyI/DXdf5efTsbZnvTuN7Vnn26pJY8ks+zitg7DEJDKWZDZ/681EAfnmy6YVXhHh/RsBXkRoSpx01dMcGNDlTjFNaINz9r/X2Wt/gYIP00aiZ7so= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=thierry.escande@vates.tech; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1775751693349265.73837421373776; Thu, 9 Apr 2026 09:21:33 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1277970.1563035 (Exim 4.92) (envelope-from ) id 1wAs7y-0006an-Gj; Thu, 09 Apr 2026 16:21:10 +0000 Received: by outflank-mailman (output) from mailman id 1277970.1563035; Thu, 09 Apr 2026 16:21:10 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wAs7y-0006ag-E3; Thu, 09 Apr 2026 16:21:10 +0000 Received: by outflank-mailman (input) for mailman id 1277970; Thu, 09 Apr 2026 16:21:09 +0000 Received: from mx.expurgate.net ([195.190.135.10]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wAs7x-0006Zr-6L for xen-devel@lists.xenproject.org; Thu, 09 Apr 2026 16:21:09 +0000 Received: from mx.expurgate.net (helo=localhost) by mx.expurgate.net with esmtp id 1wAs7w-002Kv1-IN for xen-devel@lists.xenproject.org; Thu, 09 Apr 2026 18:21:08 +0200 Received: from [10.42.69.2] (helo=localhost) by localhost with ESMTP (eXpurgate MTA 0.9.1) (envelope-from ) id 69d7d1c8-2eae-0a2a0a5409dd-0a2a450295e2-42 for ; Thu, 09 Apr 2026 18:21:08 +0200 Received: from [198.2.187.14] (helo=mail187-14.suw11.mandrillapp.com) by tlsNG-720697.mxtls.expurgate.net with ESMTPS (eXpurgate 4.56.0) (envelope-from ) id 69d7d1f3-42fa-0a2a45020019-c602bb0e046b-3 for ; Thu, 09 Apr 2026 18:21:08 +0200 Received: from pmta09.mandrill.prod.suw01.rsglab.com (localhost [127.0.0.1]) by mail187-14.suw11.mandrillapp.com (Mailchimp) with ESMTP id 4fs4tW0whgz8XWZyF for ; Thu, 9 Apr 2026 16:21:07 +0000 (GMT) Received: from [37.26.189.201] by mandrillapp.com id 2d68c2e86b134086aff945f8d500e260; Thu, 09 Apr 2026 16:21:07 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" Authentication-Results: eu.smtp.expurgate.cloud; dkim=pass header.s=mte1 header.d=mandrillapp.com header.i="@mandrillapp.com" header.h="From:Subject:To:Cc:Message-Id:In-Reply-To:References:Feedback-ID:Date:MIME-Version:Content-Type:Content-Transfer-Encoding"; dkim=pass header.s=mte1 header.d=vates.tech header.i="thierry.escande@vates.tech" header.h="From:Subject:To:Cc:Message-Id:In-Reply-To:References:Feedback-ID:Date:MIME-Version:Content-Type:Content-Transfer-Encoding" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mandrillapp.com; s=mte1; t=1775751667; x=1776021667; bh=0UnZsd6VMv3nhahDJTmKjpYZpYAGnJoqucRzautn7QA=; h=From:Subject:To:Cc:Message-Id:In-Reply-To:References:Feedback-ID: Date:MIME-Version:Content-Type:Content-Transfer-Encoding:CC:Date: Subject:From; b=qbefgWiqvZErQ9/ybAH4YbWRSO4lAPnBrUa9n60XJtZ+FaQUBpuwIZGQtJ7wf6jAd 20TesU2SbQbkXhU62M5LdTgbHooW1tgnnR7gwxu26BfYWdhY6aMCADgQgzedyVCZ87 66eKLmn73R33tJZyoTsN0uZiJdZOjKJl+RUuKfNbWbn0H5io3aBCgN+YDWwWFKxQdU tfuefmUyuxSETWquPkU3CiwC2owPqjVYKu45uqaEYjJydRabwHdLB6ltPf3+tDr3Dj tW6PwzhDXhM+d8lIIDRo4y7Q1DYq+x06dbvwP3paBdQq68ySheMhO+KbuCW4/e0bRX x89LfC7f55tSQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vates.tech; s=mte1; t=1775751667; x=1776012167; i=thierry.escande@vates.tech; bh=0UnZsd6VMv3nhahDJTmKjpYZpYAGnJoqucRzautn7QA=; h=From:Subject:To:Cc:Message-Id:In-Reply-To:References:Feedback-ID: Date:MIME-Version:Content-Type:Content-Transfer-Encoding:CC:Date: Subject:From; b=UReoS+ahOd18SgPZmRFWbbfx0xIgFHLHhSfxKjnlXGUAz8e9dg+UGV6vPiv4EVfPC oucDswE963viLI8gwfSxOx70oy6L+EiNoIOxpwH8pGE+FWaSvLPOEK1bRfDVrfCWFQ cePt3vjSVZW1vUH7M+lW2EVq6+j/HyFFPCWZTnToXJ3+G7x8Bo+HbsnllxIcdhsVnZ JmwZJh5cQdd0yA3ROgHTrEW2fcsaxMtTUNMUPVJx+kroQtbbd83VLrzqGBUZyOIJzp nc4MyShmXadcHSABuafEuOlqgfQVF2Stk6VFKI3BgLM5Fdzo6WVP6gNoYqG6ovFcvG 7JinwSTsMZz8A== From: "Thierry Escande" Subject: =?utf-8?Q?[RFC=20PATCH=20v2=202/3]=20libxl:=20Allow=20PCI=20device=20passthrough=20using=20-device=20Qemu=20command=20line?= X-Mailer: git-send-email 2.53.0 X-Bm-Disclaimer: Yes X-Bm-Milter-Handled: 4ffbd6c1-ee69-4e1b-aabd-f977039bd3e2 X-Bm-Transport-Timestamp: 1775751666400 To: xen-devel@lists.xenproject.org Cc: "Thierry Escande" , "Anthony PERARD" , "Juergen Gross" , "=?utf-8?Q?Daniel=20P=20.=20Berrang=C3=A9?=" , "=?utf-8?Q?Marek=20Marczykowski-G=C3=B3recki?=" Message-Id: <20260409162000.1102680-3-thierry.escande@vates.tech> In-Reply-To: <20260409162000.1102680-1-thierry.escande@vates.tech> References: <20260409162000.1102680-1-thierry.escande@vates.tech> X-Native-Encoded: 1 X-Report-Abuse: =?UTF-8?Q?Please=20forward=20a=20copy=20of=20this=20message,=20including=20all=20headers,=20to=20abuse@mandrill.com.=20You=20can=20also=20report=20abuse=20here:=20https://mandrillapp.com/contact/abuse=3Fid=3D30504962.2d68c2e86b134086aff945f8d500e260?= X-Mandrill-User: md_30504962 Feedback-ID: 30504962:30504962.20260409:md Date: Thu, 09 Apr 2026 16:21:07 +0000 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-purgate-ID: tlsNG-720697/1775751668-AE926CD1-767AF0B8/0/0 X-purgate-type: clean X-purgate-size: 8941 X-ZohoMail-DKIM: pass (identity thierry.escande@vates.tech) (identity @mandrillapp.com) X-ZM-MESSAGEID: 1775751696346154100 Content-Type: text/plain; charset="utf-8" This change makes use of the new option 'hotplug' for host PCI devices passthrough'd to the guest. If hotplug=3D0 is used in the pci device configuration table, the device will be attached to the guest using the Qemu command line as '-device xen-pci-passthrough,hostaddr=3D...' The host device configuration is passed to the -device option as a json array, just like it's done for hotplug using QMP. The json array is created by a new internal function libxl__device_pci_get_qmp_json() that is also used by pci_add_qmp_device_add(). Then, instead of sending the 'device_add' command, the device_add callback is called to perform the 'query-pci' check to make sure the passthrough'd device is present. In the same way at shutdown, the device is not removed using QMP and only the pci_remove_done() function is called. As with QMP, the use of the 'hotplug=3D0' option honors the 'seize' option by adding the PCI device to the assignable list if needed. Example use: pci =3D [ "00:03.0,seize=3D1,hotplug=3D0" ] Signed-off-by: Thierry Escande --- v2: - Add support for YAJL json parser --- tools/include/libxl.h | 1 + tools/libs/light/libxl_dm.c | 80 +++++++++++++++++++++++++++++++ tools/libs/light/libxl_internal.h | 2 + tools/libs/light/libxl_pci.c | 41 ++++++++++++---- 4 files changed, 114 insertions(+), 10 deletions(-) diff --git a/tools/include/libxl.h b/tools/include/libxl.h index 7c098edab6..66fb07ad67 100644 --- a/tools/include/libxl.h +++ b/tools/include/libxl.h @@ -2666,6 +2666,7 @@ int libxl_device_pci_assignable_add(libxl_ctx *ctx, l= ibxl_device_pci *pci, int r int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *p= ci, int rebind); libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *nu= m); void libxl_device_pci_assignable_list_free(libxl_device_pci *list, int num= ); +bool libxl_pci_assignable(libxl_ctx *ctx, libxl_device_pci *pci); =20 /* CPUID handling */ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* s= tr); diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c index 511ec76a65..4f4781b36d 100644 --- a/tools/libs/light/libxl_dm.c +++ b/tools/libs/light/libxl_dm.c @@ -1169,6 +1169,86 @@ static int libxl__build_device_model_args_new(libxl_= _gc *gc, } } } + + if (guest_config->num_pcidevs) { + libxl_ctx *ctx =3D libxl__gc_owner(gc); + libxl_device_pci *pci; + libxl__json_object *qmp_json; + char *json_str; +#ifdef HAVE_LIBJSONC + json_object *jso; + const char *buf; +#elif defined(HAVE_LIBYAJL) + yajl_gen hand; + /* memory for 'buf' is owned by 'hand' */ + const unsigned char *buf; + libxl_yajl_length len; +#else +# error Missing JSON library +#endif + + for (i =3D 0; i < guest_config->num_pcidevs; i++) { + pci =3D &guest_config->pcidevs[i]; + + if (pci->hotplug) + continue; + + if (!libxl_pci_assignable(ctx, pci) && pci->seize) { + rc =3D libxl_device_pci_assignable_add(ctx, pci, 1= ); + if (rc) + return rc; + } + + qmp_json =3D libxl__device_pci_get_qmp_json(gc, pci); + +#ifdef HAVE_LIBJSONC + rc =3D libxl__json_object_to_json_object(gc, &jso, qmp= _json); + if (rc) + return rc; + + buf =3D json_object_to_json_string_ext(jso, + JSON_C_TO_STRING_= PLAIN); + if (!buf) { + json_object_put(jso); + return ERROR_NOMEM; + } +#elif defined(HAVE_LIBYAJL) + hand =3D libxl_yajl_gen_alloc(NULL); + if (!hand) { + return ERROR_NOMEM; + } +#if HAVE_YAJL_V2 + /* Disable beautify for data sent to QEMU */ + yajl_gen_config(hand, yajl_gen_beautify, 0); +#endif + + rc =3D libxl__json_object_to_yajl_gen(gc, hand, qmp_js= on); + if (rc) { + yajl_gen_free(hand); + return rc; + } + + rc =3D yajl_gen_get_buf(hand, &buf, &len); + if (rc !=3D yajl_gen_status_ok) { + yajl_gen_free(hand); + return rc; + } +#endif + + json_str =3D libxl__strdup(gc, (const char *)buf); + if (json_str) + flexarray_vappend(dm_args, "-device", json_str, NU= LL); + +#ifdef HAVE_LIBJSONC + json_object_put(jso); +#elif defined(HAVE_LIBYAJL) + yajl_gen_free(hand); +#endif + + if (!json_str) + return ERROR_NOMEM; + } + } } =20 if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) { diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_int= ernal.h index b65e0064b9..06b5a14409 100644 --- a/tools/libs/light/libxl_internal.h +++ b/tools/libs/light/libxl_internal.h @@ -1729,6 +1729,8 @@ _hidden int libxl__device_pci_setdefault(libxl__gc *g= c, uint32_t domid, libxl_device_pci *pci, bool hotpl= ug); _hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc, const libxl_domain_config *d_confi= g); +_hidden libxl__json_object *libxl__device_pci_get_qmp_json(libxl__gc *gc, + libxl_device_pci = *pci); =20 /* from libxl_dtdev */ =20 diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c index 49d272d0de..3ef2c43412 100644 --- a/tools/libs/light/libxl_pci.c +++ b/tools/libs/light/libxl_pci.c @@ -1098,16 +1098,10 @@ out: pci_add_dm_done(egc, pas, rc); /* must be last */ } =20 -static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas) +libxl__json_object *libxl__device_pci_get_qmp_json(libxl__gc *gc, + libxl_device_pci *pci) { - STATE_AO_GC(pas->aodev->ao); libxl__json_object *args =3D NULL; - int rc; - - /* Convenience aliases */ - libxl_domid domid =3D pas->domid; - libxl_device_pci *pci =3D &pas->pci; - libxl__ev_qmp *const qmp =3D &pas->qmp; =20 libxl__qmp_param_add_string(gc, &args, "driver", "xen-pci-passthrough"); @@ -1134,11 +1128,30 @@ static void pci_add_qmp_device_add(libxl__egc *egc,= pci_add_state *pas) if (pci->permissive) libxl__qmp_param_add_bool(gc, &args, "permissive", true); =20 + return args; +} + +static void pci_add_qmp_device_add(libxl__egc *egc, pci_add_state *pas) +{ + STATE_AO_GC(pas->aodev->ao); + libxl__json_object *args =3D NULL; + int rc =3D 0; + + /* Convenience aliases */ + libxl_domid domid =3D pas->domid; + libxl_device_pci *pci =3D &pas->pci; + libxl__ev_qmp *const qmp =3D &pas->qmp; + + args =3D libxl__device_pci_get_qmp_json(gc, pci); + qmp->ao =3D pas->aodev->ao; qmp->domid =3D domid; qmp->payload_fd =3D -1; qmp->callback =3D pci_add_qmp_device_add_cb; - rc =3D libxl__ev_qmp_send(egc, qmp, "device_add", args); + if (pci->hotplug) + rc =3D libxl__ev_qmp_send(egc, qmp, "device_add", args); + else + pci_add_qmp_device_add_cb(egc, qmp, NULL, 0); if (rc) goto out; return; =20 @@ -1509,7 +1522,7 @@ int libxl_device_pci_add(libxl_ctx *ctx, uint32_t dom= id, return AO_INPROGRESS; } =20 -static bool libxl_pci_assignable(libxl_ctx *ctx, libxl_device_pci *pci) +bool libxl_pci_assignable(libxl_ctx *ctx, libxl_device_pci *pci) { libxl_device_pci *pcis; int num; @@ -1820,6 +1833,14 @@ static void do_pci_remove(libxl__egc *egc, pci_remov= e_state *prs) libxl_domain_type type =3D libxl__domain_type(gc, domid); libxl_device_pci *pci =3D &prs->pci; int rc, num; + + /* Passthrough'd device has been passed to Qemu command line so there = is + * no need to remove it via QMP */ + if (!pci->hotplug) { + pci_remove_done(egc, prs, 0); + return; + } + pcis =3D libxl_device_pci_list(ctx, domid, &num); if (!pcis) { rc =3D ERROR_FAIL; --=20 2.53.0 -- Thierry Escande | Vates XCP-ng Developer XCP-ng & Xen Orchestra - Vates solutions web: https://vates.tech