[PATCH for 10.2.0 0/3] Fix spurious error from PCI VPD parsing

Multiple people complained, let's fix it for the next release. Peter Krempa (3): virpcivpd: Revert error reporting from PCI VPD parser pci: Remove error reporting from PCI VPD parsing NEWS: Mention fix for PCI VPD error reporting NEWS.rst | 11 ++++++++ po/POTFILES | 1 - src/conf/node_device_conf.c | 13 ++++------ src/libvirt_private.syms | 1 - src/util/virpci.c | 35 ++++++-------------------- src/util/virpci.h | 1 - src/util/virpcivpd.c | 50 +++++++++++++++---------------------- 7 files changed, 43 insertions(+), 69 deletions(-) -- 2.44.0

The VPD parsing is fragile and depends on hardware vendor's adherance to standards. Since libvirt only ever uses this data to report it in the nodedev XML which ignores any errors there's no much point in having error reporting which I've added recently. Turn the errors into VIR_DEBUG statements in preparation for upcoming patch which completely removes the expectation to report errors. This effectively reverts commits dfc85658bd0 and f85a382a0e7. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- po/POTFILES | 1 - src/util/virpci.c | 8 ++++++- src/util/virpcivpd.c | 50 ++++++++++++++++++-------------------------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/po/POTFILES b/po/POTFILES index 428919815d..6fbff4bef2 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -313,7 +313,6 @@ src/util/virnuma.c src/util/virnvme.c src/util/virobject.c src/util/virpci.c -src/util/virpcivpd.c src/util/virperf.c src/util/virpidfile.c src/util/virpolkit.c diff --git a/src/util/virpci.c b/src/util/virpci.c index 780b4f9eec..8becec4aa5 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -3104,6 +3104,7 @@ virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev) { g_autofree char *vpdPath = virPCIFile(dev->name, "vpd"); + virPCIVPDResource *ret = NULL; VIR_AUTOCLOSE fd = -1; if (!virPCIDeviceHasVPD(dev)) { @@ -3117,7 +3118,12 @@ virPCIDeviceGetVPD(virPCIDevice *dev) return NULL; } - return virPCIVPDParse(fd); + if (!(ret = virPCIVPDParse(fd))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to parse device VPD data")); + return NULL; + } + + return ret; } #else diff --git a/src/util/virpcivpd.c b/src/util/virpcivpd.c index 16df468875..02d405f038 100644 --- a/src/util/virpcivpd.c +++ b/src/util/virpcivpd.c @@ -361,9 +361,9 @@ virPCIVPDResourceUpdateKeyword(virPCIVPDResource *res, * @offset: The offset at which bytes need to be read. * @csum: A pointer to a byte containing the current checksum value. Mutated by this function. * - * Returns 0 if exactly @count bytes were read from @vpdFileFd. The csum value is - * also modified as bytes are read. If an error occurs while reading data from the VPD file - * descriptor, it is reported and -1 is returned to the caller. + * Returns 0 if exactly @count bytes were read from @vpdFileFd. The csum value + * is also modified as bytes are read. If an error occurs while reading data + * from the VPD file descriptor -1 is returned to the caller. */ static int virPCIVPDReadVPDBytes(int vpdFileFd, @@ -375,8 +375,7 @@ virPCIVPDReadVPDBytes(int vpdFileFd, ssize_t numRead = pread(vpdFileFd, buf, count, offset); if (numRead != count) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data")); + VIR_DEBUG("read='%zd' expected='%zu'", numRead, count); return -1; } @@ -447,8 +446,7 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, case VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RDWR: if (readOnly) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: unexpected RW keyword in read-only section")); + VIR_DEBUG("unexpected RW keyword in read-only section"); return -1; } @@ -457,8 +455,7 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, case VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RESVD: if (!readOnly) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: unexpected RV keyword in read-write section")); + VIR_DEBUG("unexpected RV keyword in read-write section"); return -1; } /* Only need one byte to be read and accounted towards @@ -477,8 +474,7 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, if (resPos + resDataLen < fieldPos + fieldDataLen) { /* In this case the field cannot simply be skipped since the position of the * next field is determined based on the length of a previous field. */ - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: data field length invalid")); + VIR_DEBUG("data field length invalid"); return -1; } @@ -517,8 +513,7 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, case VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RESVD: if (*csum) { /* All bytes up to and including the checksum byte should add up to 0. */ - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: invalid checksum")); + VIR_DEBUG("invalid checksum"); return -1; } hasChecksum = true; @@ -546,14 +541,12 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, * they were not the last fields in the section. */ if ((fieldPos < resPos + resDataLen)) { /* unparsed data still present */ - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: parsing ended prematurely")); + VIR_DEBUG("parsing ended prematurely"); return -1; } if (readOnly && !hasChecksum) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: missing mandatory checksum")); + VIR_DEBUG("missing mandatory checksum"); return -1; } @@ -568,7 +561,7 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, * @resDataLen: A length of the data portion of a resource. * @csum: A pointer to a 1-byte checksum. * - * Returns: 0 on success -1 and an error on failure + * Returns: 0 on success -1 on failure. No libvirt errors are reported. */ static int virPCIVPDParseVPDLargeResourceString(int vpdFileFd, uint16_t resPos, @@ -584,8 +577,7 @@ virPCIVPDParseVPDLargeResourceString(int vpdFileFd, uint16_t resPos, resValue = g_strdup(g_strstrip(buf)); if (!virPCIVPDResourceIsValidTextValue(resValue)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to parse PCI VPD string value with invalid characters")); + VIR_DEBUG("PCI VPD string contains invalid characters"); return -1; } res->name = g_steal_pointer(&resValue); @@ -599,7 +591,7 @@ virPCIVPDParseVPDLargeResourceString(int vpdFileFd, uint16_t resPos, * Parse a PCI device's Vital Product Data (VPD) contained in a file descriptor. * * Returns: a pointer to a GList of VPDResource types which needs to be freed by the caller or - * NULL if getting it failed for some reason. + * NULL if getting it failed for some reason. No libvirt errors are reported. */ virPCIVPDResource * virPCIVPDParse(int vpdFileFd) @@ -629,7 +621,8 @@ virPCIVPDParse(int vpdFileFd) if (tag & PCI_VPD_LARGE_RESOURCE_FLAG) { if (resPos > PCI_VPD_ADDR_MASK + 1 - 3) { /* Bail if the large resource starts at the position where the end tag should be. */ - goto malformed; + VIR_DEBUG("expected end tag, got more data"); + return NULL; } /* Read the two length bytes of the large resource record. */ @@ -652,8 +645,7 @@ virPCIVPDParse(int vpdFileFd) if (tag == PCI_VPD_RESOURCE_END_TAG) { /* Stop VPD traversal since the end tag was encountered. */ if (!hasReadOnly) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: missing read-only section")); + VIR_DEBUG("missing read-only section"); return NULL; } @@ -662,7 +654,8 @@ virPCIVPDParse(int vpdFileFd) if (resDataLen > PCI_VPD_ADDR_MASK + 1 - resPos) { /* Bail if the resource is too long to fit into the VPD address space. */ - goto malformed; + VIR_DEBUG("VPD resource too long"); + return NULL; } switch (tag) { @@ -698,9 +691,7 @@ virPCIVPDParse(int vpdFileFd) resPos += resDataLen; } - malformed: - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("failed to read the PCI VPD data: malformed data")); + VIR_DEBUG("unexpected end of VPD data, expected end tag"); return NULL; } @@ -709,8 +700,7 @@ virPCIVPDParse(int vpdFileFd) virPCIVPDResource * virPCIVPDParse(int vpdFileFd G_GNUC_UNUSED) { - virReportError(VIR_ERR_NO_SUPPORT, "%s", - _("PCI VPD reporting not available on this platform")); + VIR_DEBUG("not implemented"); return NULL; } -- 2.44.0

The PCI VPD (Vital Product Data) may be missing or the kernel can report presence but not actually have the data. Also the data is specified by the device vendor and thus may be invalid in some cases. To avoid log spamming, since the only usage in the node device driver is ignoring errors, remove all error reporting. Closes: https://gitlab.com/libvirt/libvirt/-/issues/607 Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/node_device_conf.c | 13 +++++------- src/libvirt_private.syms | 1 - src/util/virpci.c | 41 +++++++------------------------------ src/util/virpci.h | 1 - 4 files changed, 12 insertions(+), 44 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 48140b17ba..32b69aae84 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -3066,15 +3066,12 @@ virNodeDeviceGetPCIVPDDynamicCap(virNodeDevCapPCIDev *devCapPCIDev) if (!(pciDev = virPCIDeviceNew(&devAddr))) return -1; - if (virPCIDeviceHasVPD(pciDev)) { - /* VPD is optional in PCI(e) specs. If it is there, attempt to add it. */ - if ((res = virPCIDeviceGetVPD(pciDev))) { - devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; - devCapPCIDev->vpd = g_steal_pointer(&res); - } else { - virResetLastError(); - } + /* VPD is optional in PCI(e) specs. If it is there, attempt to add it. */ + if ((res = virPCIDeviceGetVPD(pciDev))) { + devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; + devCapPCIDev->vpd = g_steal_pointer(&res); } + return 0; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6b6bcc368a..84e30b711c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3107,7 +3107,6 @@ virPCIDeviceGetUnbindFromStub; virPCIDeviceGetUsedBy; virPCIDeviceGetVPD; virPCIDeviceHasPCIExpressLink; -virPCIDeviceHasVPD; virPCIDeviceIsAssignable; virPCIDeviceIsPCIExpress; virPCIDeviceListAdd; diff --git a/src/util/virpci.c b/src/util/virpci.c index 8becec4aa5..1582c8b9ab 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -3078,24 +3078,14 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path, } -bool -virPCIDeviceHasVPD(virPCIDevice *dev) -{ - g_autofree char *vpdPath = virPCIFile(dev->name, "vpd"); - bool ret = virFileIsRegular(vpdPath); - - VIR_DEBUG("path='%s', exists='%d'", vpdPath, ret); - - return ret; -} - /** * virPCIDeviceGetVPD: * @dev: a PCI device to get a PCI VPD for. * * Obtain a 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. + * will be there for a particular device. The VPD data is returned in @vpd if + * it's available or otherwise NULL is set. * * Returns: a pointer to virPCIVPDResource which needs to be freed by the caller * or NULL if getting it failed for some reason (e.g. invalid format, I/O error). @@ -3104,26 +3094,16 @@ virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev) { g_autofree char *vpdPath = virPCIFile(dev->name, "vpd"); - virPCIVPDResource *ret = NULL; VIR_AUTOCLOSE fd = -1; - if (!virPCIDeviceHasVPD(dev)) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %1$s does not have a VPD"), - virPCIDeviceGetName(dev)); - return NULL; - } + fd = open(vpdPath, O_RDONLY); - if ((fd = open(vpdPath, O_RDONLY)) < 0) { - virReportSystemError(errno, _("Failed to open a VPD file '%1$s'"), vpdPath); - return NULL; - } + VIR_DEBUG("dev='%s' path='%s' fd='%d'", virPCIDeviceGetName(dev), vpdPath, fd); - if (!(ret = virPCIVPDParse(fd))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to parse device VPD data")); - return NULL; - } + if (fd < 0) + return 0; - return ret; + return virPCIVPDParse(fd); } #else @@ -3200,17 +3180,10 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path G_GNUC_UNUSED, return -1; } -bool -virPCIDeviceHasVPD(virPCIDevice *dev G_GNUC_UNUSED) -{ - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); - return NULL; -} virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev G_GNUC_UNUSED) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); return NULL; } #endif /* __linux__ */ diff --git a/src/util/virpci.h b/src/util/virpci.h index a5bfe9c35d..ba5e0ae6f1 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -270,7 +270,6 @@ int virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path, char **pfname, int *vf_index); -bool virPCIDeviceHasVPD(virPCIDevice *dev); virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev); int virPCIDeviceUnbind(virPCIDevice *dev); -- 2.44.0

On a Wednesday in 2024, Peter Krempa wrote:
The PCI VPD (Vital Product Data) may be missing or the kernel can report presence but not actually have the data. Also the data is specified by the device vendor and thus may be invalid in some cases.
To avoid log spamming, since the only usage in the node device driver is ignoring errors, remove all error reporting.
Closes: https://gitlab.com/libvirt/libvirt/-/issues/607 Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/node_device_conf.c | 13 +++++------- src/libvirt_private.syms | 1 - src/util/virpci.c | 41 +++++++------------------------------ src/util/virpci.h | 1 - 4 files changed, 12 insertions(+), 44 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 48140b17ba..32b69aae84 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -3104,26 +3094,16 @@ virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev) { g_autofree char *vpdPath = virPCIFile(dev->name, "vpd"); - virPCIVPDResource *ret = NULL; VIR_AUTOCLOSE fd = -1;
- if (!virPCIDeviceHasVPD(dev)) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %1$s does not have a VPD"), - virPCIDeviceGetName(dev)); - return NULL; - } + fd = open(vpdPath, O_RDONLY);
- if ((fd = open(vpdPath, O_RDONLY)) < 0) { - virReportSystemError(errno, _("Failed to open a VPD file '%1$s'"), vpdPath); - return NULL; - } + VIR_DEBUG("dev='%s' path='%s' fd='%d'", virPCIDeviceGetName(dev), vpdPath, fd);
- if (!(ret = virPCIVPDParse(fd))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to parse device VPD data")); - return NULL; - } + if (fd < 0) + return 0;
This function returns a pointer. s/0/NULL/
- return ret; + return virPCIVPDParse(fd); }
#else

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- NEWS.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index ef9e312698..05fd9e3194 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -52,6 +52,17 @@ v10.2.0 (unreleased) releases to 10.2.0 works as well, but the other direction remains broken unless the fix is backported. + * node_device: Don't report spurious errors from PCI VPD parsing + + In last release the PCI Vital Product Data parser was enhanced to report + errors but that effort failed as some kernels have the file but don't allow + reading it causing logs to be spammed with:: + + libvirtd[21055]: operation failed: failed to read the PCI VPD data + + Since the data is used only in the node device XML and errors are ignored if + the parsing failed, this release removes all the error reporting. + v10.1.0 (2024-03-01) ==================== -- 2.44.0

On a Wednesday in 2024, Peter Krempa wrote:
Multiple people complained, let's fix it for the next release.
Peter Krempa (3): virpcivpd: Revert error reporting from PCI VPD parser pci: Remove error reporting from PCI VPD parsing NEWS: Mention fix for PCI VPD error reporting
NEWS.rst | 11 ++++++++ po/POTFILES | 1 - src/conf/node_device_conf.c | 13 ++++------ src/libvirt_private.syms | 1 - src/util/virpci.c | 35 ++++++-------------------- src/util/virpci.h | 1 - src/util/virpcivpd.c | 50 +++++++++++++++---------------------- 7 files changed, 43 insertions(+), 69 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
participants (2)
-
Ján Tomko
-
Peter Krempa