mirror of https://gitee.com/openkylin/libvirt.git
Add PCI VPD-related helper functions to virpci
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. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherbakov@canonical.com>
This commit is contained in:
parent
59c1bc3a0e
commit
38003e7551
|
@ -2994,7 +2994,9 @@ virPCIDeviceGetReprobe;
|
||||||
virPCIDeviceGetStubDriver;
|
virPCIDeviceGetStubDriver;
|
||||||
virPCIDeviceGetUnbindFromStub;
|
virPCIDeviceGetUnbindFromStub;
|
||||||
virPCIDeviceGetUsedBy;
|
virPCIDeviceGetUsedBy;
|
||||||
|
virPCIDeviceGetVPD;
|
||||||
virPCIDeviceHasPCIExpressLink;
|
virPCIDeviceHasPCIExpressLink;
|
||||||
|
virPCIDeviceHasVPD;
|
||||||
virPCIDeviceIsAssignable;
|
virPCIDeviceIsAssignable;
|
||||||
virPCIDeviceIsPCIExpress;
|
virPCIDeviceIsPCIExpress;
|
||||||
virPCIDeviceListAdd;
|
virPCIDeviceListAdd;
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "virkmod.h"
|
#include "virkmod.h"
|
||||||
#include "virstring.h"
|
#include "virstring.h"
|
||||||
#include "viralloc.h"
|
#include "viralloc.h"
|
||||||
|
#include "virpcivpd.h"
|
||||||
|
|
||||||
VIR_LOG_INIT("util.pci");
|
VIR_LOG_INIT("util.pci");
|
||||||
|
|
||||||
|
@ -2640,6 +2641,61 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
virPCIDeviceHasVPD(virPCIDevice *dev)
|
||||||
|
{
|
||||||
|
g_autofree char *vpdPath = NULL;
|
||||||
|
|
||||||
|
vpdPath = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
virPCIVPDResource *
|
||||||
|
virPCIDeviceGetVPD(virPCIDevice *dev)
|
||||||
|
{
|
||||||
|
g_autofree char *vpdPath = NULL;
|
||||||
|
int fd;
|
||||||
|
g_autoptr(virPCIVPDResource) res = NULL;
|
||||||
|
|
||||||
|
vpdPath = 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 = open(vpdPath, O_RDONLY)) < 0) {
|
||||||
|
virReportSystemError(-fd, _("Failed to open a VPD file '%s'"), vpdPath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
res = virPCIVPDParse(fd);
|
||||||
|
|
||||||
|
if (VIR_CLOSE(fd) < 0) {
|
||||||
|
virReportSystemError(errno, _("Unable to close the VPD file, fd: %d"), fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer(&res);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static const char *unsupported = N_("not supported on non-linux platforms");
|
static const char *unsupported = N_("not supported on non-linux platforms");
|
||||||
|
|
||||||
|
@ -2713,6 +2769,20 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path G_GNUC_UNUSED,
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
|
||||||
return -1;
|
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__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "virmdev.h"
|
#include "virmdev.h"
|
||||||
#include "virobject.h"
|
#include "virobject.h"
|
||||||
#include "virenum.h"
|
#include "virenum.h"
|
||||||
|
#include "virpcivpd.h"
|
||||||
|
|
||||||
typedef struct _virPCIDevice virPCIDevice;
|
typedef struct _virPCIDevice virPCIDevice;
|
||||||
typedef struct _virPCIDeviceAddress virPCIDeviceAddress;
|
typedef struct _virPCIDeviceAddress virPCIDeviceAddress;
|
||||||
|
@ -269,6 +270,9 @@ int virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path,
|
||||||
char **pfname,
|
char **pfname,
|
||||||
int *vf_index);
|
int *vf_index);
|
||||||
|
|
||||||
|
bool virPCIDeviceHasVPD(virPCIDevice *dev);
|
||||||
|
virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev);
|
||||||
|
|
||||||
int virPCIDeviceUnbind(virPCIDevice *dev);
|
int virPCIDeviceUnbind(virPCIDevice *dev);
|
||||||
int virPCIDeviceRebind(virPCIDevice *dev);
|
int virPCIDeviceRebind(virPCIDevice *dev);
|
||||||
int virPCIDeviceGetDriverPathAndName(virPCIDevice *dev,
|
int virPCIDeviceGetDriverPathAndName(virPCIDevice *dev,
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#define LIBVIRT_VIRPCIVPDPRIV_H_ALLOW
|
||||||
|
|
||||||
|
#include "virpcivpdpriv.h"
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||||
# define VIR_MOCK_LOOKUP_MAIN
|
# define VIR_MOCK_LOOKUP_MAIN
|
||||||
# include "virmock.h"
|
# include "virmock.h"
|
||||||
|
@ -123,6 +127,13 @@ struct pciDeviceAddress {
|
||||||
};
|
};
|
||||||
# define ADDR_STR_FMT "%04x:%02x:%02x.%u"
|
# define ADDR_STR_FMT "%04x:%02x:%02x.%u"
|
||||||
|
|
||||||
|
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 pciDevice {
|
||||||
struct pciDeviceAddress addr;
|
struct pciDeviceAddress addr;
|
||||||
int vendor;
|
int vendor;
|
||||||
|
@ -131,6 +142,7 @@ struct pciDevice {
|
||||||
int iommuGroup;
|
int iommuGroup;
|
||||||
const char *physfn;
|
const char *physfn;
|
||||||
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
|
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
|
||||||
|
struct pciVPD vpd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fdCallback {
|
struct fdCallback {
|
||||||
|
@ -537,6 +549,9 @@ pci_device_new_from_stub(const struct pciDevice *data)
|
||||||
make_symlink(devpath, "physfn", tmp);
|
make_symlink(devpath, "physfn", tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
if (pci_device_autobind(dev) < 0)
|
||||||
ABORT("Unable to bind: %s", devid);
|
ABORT("Unable to bind: %s", devid);
|
||||||
|
|
||||||
|
@ -942,6 +957,20 @@ static void
|
||||||
init_env(void)
|
init_env(void)
|
||||||
{
|
{
|
||||||
g_autofree char *tmp = NULL;
|
g_autofree char *tmp = NULL;
|
||||||
|
const char fullVPDExampleData[] = {
|
||||||
|
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_FLAG, 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 = {
|
||||||
|
.data = fullVPDExampleData,
|
||||||
|
.vpd_len = sizeof(fullVPDExampleData) / sizeof(fullVPDExampleData[0]),
|
||||||
|
};
|
||||||
|
|
||||||
if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR")))
|
if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR")))
|
||||||
ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
|
ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
|
||||||
|
@ -1008,6 +1037,8 @@ init_env(void)
|
||||||
|
|
||||||
MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass = 0x010802);
|
MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass = 0x010802);
|
||||||
MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass = 0x010802);
|
MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass = 0x010802);
|
||||||
|
|
||||||
|
MAKE_PCI_DEVICE("0000:03:00.0", 0x15b3, 0xa2d6, 16, .vpd = exampleVPD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <virpci.h>
|
# include <virpci.h>
|
||||||
|
# include <virpcivpd.h>
|
||||||
|
|
||||||
# define VIR_FROM_THIS VIR_FROM_NONE
|
# define VIR_FROM_THIS VIR_FROM_NONE
|
||||||
|
|
||||||
|
@ -328,6 +330,39 @@ testVirPCIDeviceUnbind(const void *opaque)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
testVirPCIDeviceGetVPD(const void *opaque)
|
||||||
|
{
|
||||||
|
const struct testPCIDevData *data = opaque;
|
||||||
|
g_autoptr(virPCIDevice) dev = NULL;
|
||||||
|
virPCIDeviceAddress devAddr = {.domain = data->domain, .bus = data->bus,
|
||||||
|
.slot = data->slot, .function = data->function};
|
||||||
|
g_autoptr(virPCIVPDResource) res = NULL;
|
||||||
|
|
||||||
|
dev = virPCIDeviceNew(&devAddr);
|
||||||
|
if (!dev)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
res = virPCIDeviceGetVPD(dev);
|
||||||
|
|
||||||
|
/* Only basic checks - full parser validation is done elsewhere. */
|
||||||
|
if (res->ro == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (STRNEQ(res->name, "testname")) {
|
||||||
|
VIR_TEST_DEBUG("Unexpected name present in VPD: %s", res->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRNEQ(res->ro->part_number, "42")) {
|
||||||
|
VIR_TEST_DEBUG("Unexpected part number value present in VPD: %s", res->ro->part_number);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
# define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
|
# define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -409,6 +444,8 @@ mymain(void)
|
||||||
DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0);
|
DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0);
|
||||||
DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL);
|
DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL);
|
||||||
|
|
||||||
|
DO_TEST_PCI(testVirPCIDeviceGetVPD, 0, 0x03, 0, 0);
|
||||||
|
|
||||||
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
|
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
|
||||||
virFileDeleteTree(fakerootdir);
|
virFileDeleteTree(fakerootdir);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue