From nobody Tue Feb 10 12:39:46 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1632771079363334.50575576227686; Mon, 27 Sep 2021 12:31:19 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-541-bBRSLpFGMamPoUjpYQYHfA-1; Mon, 27 Sep 2021 15:31:16 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A3783192203E; Mon, 27 Sep 2021 19:31:10 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4DB9E19C79; Mon, 27 Sep 2021 19:31:10 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id EDF524EA29; Mon, 27 Sep 2021 19:31:08 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJV7tJ014430 for ; Mon, 27 Sep 2021 15:31:07 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1DDE02144B33; Mon, 27 Sep 2021 19:31:07 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1586A2144B31 for ; Mon, 27 Sep 2021 19:31:00 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E7C2218A01A9 for ; Mon, 27 Sep 2021 19:30:59 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-431-zeukNQN7OXKB8gmOwvyHGw-1; Mon, 27 Sep 2021 15:30:57 -0400 Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id D26894079D for ; Mon, 27 Sep 2021 19:30:56 +0000 (UTC) Received: by mail-lf1-f70.google.com with SMTP id a28-20020a056512021c00b003f5883dcd4bso16755472lfo.1 for ; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:55 -0700 (PDT) X-MC-Unique: bBRSLpFGMamPoUjpYQYHfA-1 X-MC-Unique: zeukNQN7OXKB8gmOwvyHGw-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/LjAbWlB7il8GtfXDCbpvMPXWOYLelbzYpB6WmDq+mw=; b=Z7YQTVhlggSgrnF8MDGv1IH43Ob4OQTJMuIuT6IBlx661J2bChc9OsV9jy6TQa2eh8 GQRRvlBNyHVmyLVDflfh2ydyVmdvLW12aR0JDoaGNSHLJi1jnZNJlI3PLoB3MDNNkjnR dlsGijNqLQiqpzRox/48sAwv5MKYXS12tGPmzXx4PmNOJmbfVoUQKuEP34FuKWKJF6uA u6YZeAla6GsnVtEVO9wiMisviYUfbat+FAgvrlYuL93PHC3HAqEcWV65FMvhCfconzNb KwSVBF9ZKD00vJhwk8/al/J2gK2VxvOwqeT9VkSwN6VqC1HGOc6BGXS7aRAPJ3vBWmNv ekiQ== X-Gm-Message-State: AOAM531419pzGJu8oqyA3Cw1g4RiBc7eY3RGB+d0FfDoW6nvtXdQm1MJ z011MfQaEcUMjBqQK1yfKwi/QT0qxKPp7zNzzRjObaDehYb+fSwvuJt0tCZZGoqy0kZYTO59gpl 1ZYNXWTe1RhfkzYsVux441tqD0pORCLVL3A== X-Received: by 2002:a05:6512:4008:: with SMTP id br8mr1434952lfb.23.1632771056256; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzqwAx6D2idJAPAX2iSRHbEyR/z+geuP+wgowc5U8ytl113X/yu2ookCmpdCOv9omN60pZemA== X-Received: by 2002:a05:6512:4008:: with SMTP id br8mr1434934lfb.23.1632771056014; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 3/7] Add PCI VPD-related helper functions to virpci Date: Mon, 27 Sep 2021 22:30:49 +0300 Message-Id: <20210927193053.109029-4-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJV7tJ014430 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771081701100003 Content-Type: text/plain; charset="utf-8" Add helper functions to virpci to provide means of checking for a VPD file presence and for VPD resource retrieval using the PCI VPD parser. The added test assesses the basic functionality of VPD retrieval while the full parser is tested by virpcivpdtest. Signed-off-by: Dmitrii Shcherbakov --- src/libvirt_private.syms | 2 ++ src/util/virpci.c | 62 ++++++++++++++++++++++++++++++++++++++ src/util/virpci.h | 3 ++ tests/virpcimock.c | 30 +++++++++++++++++++ tests/virpcitest.c | 64 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb9b380599..ebee46ce9b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2988,7 +2988,9 @@ virPCIDeviceGetReprobe; virPCIDeviceGetStubDriver; virPCIDeviceGetUnbindFromStub; virPCIDeviceGetUsedBy; +virPCIDeviceGetVPDResources; virPCIDeviceHasPCIExpressLink; +virPCIDeviceHasVPD; virPCIDeviceIsAssignable; virPCIDeviceIsPCIExpress; virPCIDeviceListAdd; diff --git a/src/util/virpci.c b/src/util/virpci.c index f307580a53..480d91c17f 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -37,6 +37,7 @@ #include "virkmod.h" #include "virstring.h" #include "viralloc.h" +#include "virpcivpd.h" =20 VIR_LOG_INIT("util.pci"); =20 @@ -2640,6 +2641,53 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_de= vice_path, return 0; } =20 + +gboolean +virPCIDeviceHasVPD(virPCIDevice *dev) +{ + g_autofree char *vpdPath =3D NULL; + vpdPath =3D virPCIFile(dev->name, "vpd"); + if (!virFileExists(vpdPath)) { + VIR_INFO("Device VPD file does not exist %s", vpdPath); + return false; + } else if (!virFileIsRegular(vpdPath)) { + VIR_WARN("VPD path does not point to a regular file %s", vpdPath); + return false; + } + return true; +} + +/** + * virPCIDeviceGetVPDResources: + * @dev: a PCI device to get a list of VPD resources for. + * + * Obtain resources stored in the PCI device's Vital Product Data (VPD). + * VPD is optional in both PCI Local Bus and PCIe specifications so there = is + * no guarantee it will be there for a particular device. + * + * Returns: a pointer to a list of VPDResource types which needs to be fre= ed by the caller or + * NULL if getting it failed for some reason. + */ +GList * +virPCIDeviceGetVPDResources(virPCIDevice *dev) +{ + g_autofree char *vpdPath =3D NULL; + int fd; + + vpdPath =3D virPCIFile(dev->name, "vpd"); + if (!virPCIDeviceHasVPD(dev)) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %s does not have = a VPD"), + virPCIDeviceGetName(dev)); + return NULL; + } + if ((fd =3D open(vpdPath, O_RDONLY)) < 0) { + virReportSystemError(-fd, + _("Failed to open a VPD file '%s'"), vpdPath); + return NULL; + } + return virPCIVPDParse(fd); +} + #else static const char *unsupported =3D N_("not supported on non-linux platform= s"); =20 @@ -2713,6 +2761,20 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_de= vice_path G_GNUC_UNUSED, virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); return -1; } + +gboolean +virPCIDeviceHasVPD(virPCIDevice *dev) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return NULL; +} + +GList * +virPCIDeviceGetVPDResources(virPCIDevice *dev) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return NULL; +} #endif /* __linux__ */ =20 int diff --git a/src/util/virpci.h b/src/util/virpci.h index 9a3db6c6d8..a89561496b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -269,6 +269,9 @@ int virPCIGetVirtualFunctionInfo(const char *vf_sysfs_d= evice_path, char **pfname, int *vf_index); =20 +gboolean virPCIDeviceHasVPD(virPCIDevice *dev); +GList * virPCIDeviceGetVPDResources(virPCIDevice *dev); + int virPCIDeviceUnbind(virPCIDevice *dev); int virPCIDeviceRebind(virPCIDevice *dev); int virPCIDeviceGetDriverPathAndName(virPCIDevice *dev, diff --git a/tests/virpcimock.c b/tests/virpcimock.c index d4d43aac51..e10ebce76f 100644 --- a/tests/virpcimock.c +++ b/tests/virpcimock.c @@ -18,6 +18,8 @@ =20 #include =20 +#include "virpcivpd.h" + #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) # define VIR_MOCK_LOOKUP_MAIN # include "virmock.h" @@ -123,6 +125,13 @@ struct pciDeviceAddress { }; # define ADDR_STR_FMT "%04x:%02x:%02x.%u" =20 +struct pciVPD { + /* PCI VPD contents (binary, may contain NULLs), NULL if not present. = */ + const char *data; + /* VPD length in bytes. */ + size_t vpd_len; +}; + struct pciDevice { struct pciDeviceAddress addr; int vendor; @@ -131,6 +140,7 @@ struct pciDevice { int iommuGroup; const char *physfn; struct pciDriver *driver; /* Driver attached. NULL if attached to no= driver */ + struct pciVPD vpd; }; =20 struct fdCallback { @@ -537,6 +547,10 @@ pci_device_new_from_stub(const struct pciDevice *data) make_symlink(devpath, "physfn", tmp); } =20 + if (dev->vpd.data && dev->vpd.vpd_len) { + make_file(devpath, "vpd", dev->vpd.data, dev->vpd.vpd_len); + } + if (pci_device_autobind(dev) < 0) ABORT("Unable to bind: %s", devid); =20 @@ -942,6 +956,20 @@ static void init_env(void) { g_autofree char *tmp =3D NULL; + const char fullVPDExampleData[] =3D { + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x08, = 0x00, + 't', 'e', 's', 't', 'n', 'a', 'm', 'e', + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLA= G, 0x16, 0x00, + 'P', 'N', 0x02, '4', '2', + 'E', 'C', 0x04, '4', '2', '4', '2', + 'V', 'A', 0x02, 'E', 'X', + 'R', 'V', 0x02, 0x31, 0x00, + PCI_VPD_RESOURCE_END_VAL + }; + struct pciVPD exampleVPD =3D { + .data =3D fullVPDExampleData, + .vpd_len =3D sizeof(fullVPDExampleData) / sizeof(fullVPDExampleDat= a[0]), + }; =20 if (!(fakerootdir =3D getenv("LIBVIRT_FAKE_ROOT_DIR"))) ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n"); @@ -1008,6 +1036,8 @@ init_env(void) =20 MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass =3D 0x01080= 2); MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass =3D 0x01080= 2); + + MAKE_PCI_DEVICE("0000:03:00.0", 0x15b3, 0xa2d6, 16, .vpd =3D exampleVP= D); } =20 =20 diff --git a/tests/virpcitest.c b/tests/virpcitest.c index 6fe9b7d13a..c6bf12ba0b 100644 --- a/tests/virpcitest.c +++ b/tests/virpcitest.c @@ -17,6 +17,7 @@ */ =20 #include +#include "internal.h" =20 #include "testutils.h" =20 @@ -26,6 +27,7 @@ # include # include # include +# include =20 # define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -328,6 +330,66 @@ testVirPCIDeviceUnbind(const void *opaque) return ret; } =20 + +static gboolean +testVirPCIGetOneResourceField(gpointer *key, gpointer *val, gpointer userD= ata) +{ + const char **fieldValue =3D (const char **)userData; + if (STREQ((const char*)key, "PN")) { + *fieldValue =3D (const char*)val; + return true; + } + return false; +} + +static int +testVirPCIDeviceGetVPDResources(const void *opaque) +{ + const struct testPCIDevData *data =3D opaque; + int ret =3D -1; + g_autofree virPCIDevice *dev =3D NULL; + virPCIDeviceAddress devAddr =3D {.domain =3D data->domain, .bus =3D da= ta->bus, + .slot =3D data->slot, .function =3D dat= a->function}; + g_autofree GList *resources =3D NULL; + const char* resValue =3D NULL; + const char* fieldValue =3D NULL; + virPCIVPDStringResource *strRes; + virPCIVPDKeywordResource *kwRes; + + dev =3D virPCIDeviceNew(&devAddr); + if (!dev) { + return ret; + } + + resources =3D virPCIDeviceGetVPDResources(dev); + + /* Here we simply check that 2 resources are present and their basic u= se. + * Full parser validation is done elsewhere. + */ + if (g_list_length(resources) !=3D 2) { + return ret; + } + + strRes =3D g_list_nth_data(resources, 0); + resValue =3D virPCIVPDStringResourceGetValue(strRes); + if (STRNEQ(resValue, "testname")) { + VIR_TEST_DEBUG("Unexpected string resource value: %s", resValue); + return ret; + } + kwRes =3D g_list_nth_data(resources, 1); + virPCIVPDKeywordResourceForEach(kwRes, + (GTraverseFunc)testVirPCIGetOneResourceField, &fieldValue); + + if (!fieldValue || STRNEQ(fieldValue, "42")) { + VIR_TEST_DEBUG("Could not retrieve an expected value from the Keyw= ord resource: %s", + resValue); + return ret; + } + + ret =3D 0; + return ret; +} + # define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX" =20 static int @@ -409,6 +471,8 @@ mymain(void) DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0); DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL); =20 + DO_TEST_PCI(testVirPCIDeviceGetVPDResources, 0, 0x03, 0, 0); + if (getenv("LIBVIRT_SKIP_CLEANUP") =3D=3D NULL) virFileDeleteTree(fakerootdir); =20 --=20 2.30.2