[PATCH 0/4] qemuhotplugtest: Introduce interface-hostdev test case
Inspired by the following patch: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/YQHQG... Michal Prívozník (4): virpcimock: Create sriov_totalvfs file and virtfnN symlinks virpcimock: Create net/ subdir for devices virnetdevmock: Mock virNetDevSaveNetConfig() and virNetDevSetNetConfig() qemuhotplugtest: Introduce interface-hostdev test case src/util/virnetdev.h | 5 +- tests/qemuhotplugtest.c | 7 +- .../qemuhotplug-interface-hostdev.xml | 6 ++ ...emuhotplug-base-live+interface-hostdev.xml | 63 +++++++++++++++ tests/virnetdevmock.c | 22 ++++++ tests/virpcimock.c | 78 +++++++++++++++++-- 6 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-interface-hostdev.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-hostdev.xml -- 2.52.0
From: Michal Privoznik <mprivozn@redhat.com> In case of an SRIOV device the sysfs struct looks like this: -r--r--r--. 1 root root 4096 Feb 26 14:40 /sys/bus/pci/devices/0000:82:00.0/sriov_totalvfs lrwxrwxrwx. 1 root root 0 Feb 25 22:51 /sys/bus/pci/devices/0000:82:00.0/virtfn0 -> ../0000:82:10.0 lrwxrwxrwx. 1 root root 0 Feb 25 22:51 /sys/bus/pci/devices/0000:82:00.0/virtfn1 -> ../0000:82:10.4 lrwxrwxrwx. 1 root root 0 Feb 25 22:51 /sys/bus/pci/devices/0000:82:00.0/virtfn2 -> ../0000:82:11.0 lrwxrwxrwx. 1 root root 0 Feb 25 22:51 /sys/bus/pci/devices/0000:82:00.0/virtfn3 -> ../0000:82:11.4 Of course, there is much more, I've just picked up files that our code touches during hotplug of an <interface type='hostdev'/>. The first file 'sriov_totalvfs' contains the maximum number of VFs supported. Then, for each VF created there's 'virtfnN' symlink to individual VFs. Teach our virpcimock to create the file and symlinks. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/virpcimock.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/tests/virpcimock.c b/tests/virpcimock.c index ca345f37a3..c59359b889 100644 --- a/tests/virpcimock.c +++ b/tests/virpcimock.c @@ -141,6 +141,8 @@ struct pciDevice { int device; int klass; int iommuGroup; + int sriovTotalvfs; + unsigned int virtfnCount; const char *physfn; struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */ struct pciVPD vpd; @@ -439,6 +441,38 @@ pci_device_create_iommu(const struct pciDevice *dev, } +static void +register_vf(struct pciDevice *vf) +{ + struct pciDeviceAddress pfAddr; + struct pciDevice *pf; + g_autofree char *relPath = NULL; + g_autofree char *pfPath = NULL; + g_autofree char *virtfnName = NULL; + + if (pci_address_parse(&pfAddr, vf->physfn) < 0) + ABORT("Unable to parse PCI address %s", vf->physfn); + + if (!(pf = pci_device_find_by_id(&pfAddr))) { + ABORT("Unable to find PF for VF %s", vf->physfn); + } + + relPath = g_strdup_printf("../" ADDR_STR_FMT, + vf->addr.domain, + vf->addr.bus, + vf->addr.device, + vf->addr.function); + + virtfnName = g_strdup_printf("virtfn%u", pf->virtfnCount); + + pfPath = pci_device_get_path(pf, NULL, true); + + make_symlink(pfPath, virtfnName, relPath); + + pf->virtfnCount++; +} + + static void pci_device_new_from_stub(const struct pciDevice *data) { @@ -537,6 +571,14 @@ pci_device_new_from_stub(const struct pciDevice *data) make_symlink(devsympath, devid, tmp); + if (dev->sriovTotalvfs) { + if (g_snprintf(tmp, sizeof(tmp), "%d\n", dev->sriovTotalvfs) < 0) { + ABORT("@tmp overflow"); + } + + make_file(devpath, "sriov_totalvfs", tmp, -1); + } + if (dev->physfn) { if (g_snprintf(tmp, sizeof(tmp), "%s%s/devices/%s", fakerootdir, @@ -544,6 +586,8 @@ pci_device_new_from_stub(const struct pciDevice *data) ABORT("@tmp overflow"); } make_symlink(devpath, "physfn", tmp); + + register_vf(dev); } if (dev->vpd.data && dev->vpd.vpd_len) @@ -1043,12 +1087,14 @@ init_env(void) MAKE_PCI_DEVICE("0000:0a:01.0", 0x8086, 0x0047, 8); MAKE_PCI_DEVICE("0000:0a:02.0", 0x8286, 0x0048, 8); MAKE_PCI_DEVICE("0000:0a:03.0", 0x8386, 0x0048, 8); - MAKE_PCI_DEVICE("0000:06:12.0", 0x8086, 0x0047, 9); + MAKE_PCI_DEVICE("0000:06:12.0", 0x8086, 0x0047, 9, + .sriovTotalvfs = 7); MAKE_PCI_DEVICE("0000:06:12.1", 0x8086, 0x0047, 10, .physfn = "0000:06:12.0"); /* Virtual Function */ MAKE_PCI_DEVICE("0000:06:12.2", 0x8086, 0x0047, 11, .physfn = "0000:06:12.0"); /* Virtual Function */ - MAKE_PCI_DEVICE("0021:de:1f.0", 0x8086, 0x0047, 12); + MAKE_PCI_DEVICE("0021:de:1f.0", 0x8086, 0x0047, 12, + .sriovTotalvfs = 7); MAKE_PCI_DEVICE("0021:de:1f.1", 0x8086, 0x0047, 13, .physfn = "0021:de:1f.0"); /* Virtual Function */ -- 2.52.0
From: Michal Privoznik <mprivozn@redhat.com> A PCI device that is a network interface card also has 'net/' subdir with interface name it corresponds to. For instance: # ls -l /sys/bus/pci/devices/0000\:00\:1f.6/net/ total 0 drwxr-xr-x 5 root root 0 Feb 26 16:51 eth0 Allow setting interface name for PCI devices. Now, in real life the net/$IFNAME/ is a directory, but since our code opens net/ dir and then just reads dentries creating file instead of full blown dir is okay. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/virpcimock.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/virpcimock.c b/tests/virpcimock.c index c59359b889..22d2354ebe 100644 --- a/tests/virpcimock.c +++ b/tests/virpcimock.c @@ -144,6 +144,7 @@ struct pciDevice { int sriovTotalvfs; unsigned int virtfnCount; const char *physfn; + const char *netName; struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */ struct pciVPD vpd; }; @@ -590,6 +591,18 @@ pci_device_new_from_stub(const struct pciDevice *data) register_vf(dev); } + if (dev->netName) { + if (g_snprintf(tmp, sizeof(tmp), "%s/net", devpath) < 0) { + ABORT("@tmp overflow"); + } + + if (g_mkdir_with_parents(tmp, 0777) < 0) + ABORT("Unable to create: %s", tmp); + + /* In real sysfs this is a dir, but for test purposes a file is okay. */ + make_file(tmp, dev->netName, dev->netName, -1); + } + if (dev->vpd.data && dev->vpd.vpd_len) make_file(devpath, "vpd", dev->vpd.data, dev->vpd.vpd_len); @@ -1072,8 +1085,10 @@ init_env(void) pci_device_new_from_stub(&dev); \ } while (0) - MAKE_PCI_DEVICE("0000:00:00.0", 0x8086, 0x0044, 0); - MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044, 1); + MAKE_PCI_DEVICE("0000:00:00.0", 0x8086, 0x0044, 0, + .netName = "wlan0"); + MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044, 1, + .netName = "wlan1"); MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046, 2); MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048, 3); MAKE_PCI_DEVICE("0001:00:00.0", 0x1014, 0x03b9, 4, .klass = 0x060400); @@ -1088,15 +1103,18 @@ init_env(void) MAKE_PCI_DEVICE("0000:0a:02.0", 0x8286, 0x0048, 8); MAKE_PCI_DEVICE("0000:0a:03.0", 0x8386, 0x0048, 8); MAKE_PCI_DEVICE("0000:06:12.0", 0x8086, 0x0047, 9, - .sriovTotalvfs = 7); + .sriovTotalvfs = 7, .netName = "enp1s0f0"); MAKE_PCI_DEVICE("0000:06:12.1", 0x8086, 0x0047, 10, - .physfn = "0000:06:12.0"); /* Virtual Function */ + .physfn = "0000:06:12.0", /* Virtual Function */ + .netName = "enp1s0f1"); MAKE_PCI_DEVICE("0000:06:12.2", 0x8086, 0x0047, 11, - .physfn = "0000:06:12.0"); /* Virtual Function */ + .physfn = "0000:06:12.0", /* Virtual Function */ + .netName = "enp1s0f2"); MAKE_PCI_DEVICE("0021:de:1f.0", 0x8086, 0x0047, 12, - .sriovTotalvfs = 7); + .sriovTotalvfs = 7, .netName = "enp2s0f0"); MAKE_PCI_DEVICE("0021:de:1f.1", 0x8086, 0x0047, 13, - .physfn = "0021:de:1f.0"); /* Virtual Function */ + .physfn = "0021:de:1f.0", /* Virtual Function */ + .netName = "enp2s0f1"); MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass = 0x010802); MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass = 0x010802); -- 2.52.0
From: Michal Privoznik <mprivozn@redhat.com> We really don't want our test suite talking to kernel and setting various attributes on NICs. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/util/virnetdev.h | 5 +++-- tests/virnetdevmock.c | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 1da1795487..424d4c0e52 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -248,7 +248,8 @@ int virNetDevGetVirtualFunctions(const char *pfname, int virNetDevSaveNetConfig(const char *linkdev, int vf, const char *stateDir, bool saveVlan) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) G_GNUC_WARN_UNUSED_RESULT; + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) G_GNUC_WARN_UNUSED_RESULT + ATTRIBUTE_MOCKABLE; int virNetDevReadNetConfig(const char *linkdev, int vf, @@ -265,7 +266,7 @@ virNetDevSetNetConfig(const char *linkdev, int vf, const virNetDevVlan *vlan, const virMacAddr *MAC, bool setVLan) - ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; + ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT ATTRIBUTE_MOCKABLE; int virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname, int *vf) diff --git a/tests/virnetdevmock.c b/tests/virnetdevmock.c index 910009d80c..c45b89288b 100644 --- a/tests/virnetdevmock.c +++ b/tests/virnetdevmock.c @@ -33,6 +33,28 @@ virNetDevSysfsFile(char **pf_sysfs_device_link, NET_DEV_TEST_DATA_PREFIX, ifname, file); return 0; } + + +int +virNetDevSaveNetConfig(const char *linkdev G_GNUC_UNUSED, + int vf G_GNUC_UNUSED, + const char *stateDir G_GNUC_UNUSED, + bool saveVlan G_GNUC_UNUSED) +{ + return 0; +} + + +int +virNetDevSetNetConfig(const char *linkdev G_GNUC_UNUSED, + int vf G_GNUC_UNUSED, + const virMacAddr *adminMAC G_GNUC_UNUSED, + const virNetDevVlan *vlan G_GNUC_UNUSED, + const virMacAddr *MAC G_GNUC_UNUSED, + bool setVLan G_GNUC_UNUSED) +{ + return 0; +} #else /* Nothing to override on non-__linux__ platforms */ #endif -- 2.52.0
From: Michal Privoznik <mprivozn@redhat.com> While our qemuhotplugtest already does a PCI hotplug and unlpug ("hostdev-pci") there is another way to hotplug a PCI device, esp. if it's a NIC: <interface type='hostdev'/>. This has been missing and as shown in v12.1.0-rc1-4-gfe782ed334 can be potentially dangerous as some different paths are taken. Introduce a test case for interface-hostdev. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/qemuhotplugtest.c | 7 ++- .../qemuhotplug-interface-hostdev.xml | 6 ++ ...emuhotplug-base-live+interface-hostdev.xml | 63 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-interface-hostdev.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-hostdev.xml diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 58413b6e0d..3367804ed0 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -697,6 +697,10 @@ mymain(void) DO_TEST_DETACH("ppc64", "pseries-base-live", "hostdev-pci", false, false, "device_del", QMP_DEVICE_DELETED("hostdev0") QMP_OK); + DO_TEST_ATTACH("x86_64", "base-live", "interface-hostdev", false, true, + "device_add", QMP_OK); + DO_TEST_DETACH("x86_64", "base-live", "interface-hostdev", false, false, + "device_del", QMP_DEVICE_DELETED("hostdev0") QMP_OK); DO_TEST_ATTACH("x86_64", "base-live", "interface-vdpa", false, true, "query-fdsets", "{\"return\":[{\"fdset-id\":99999}]}", "add-fd", "{ \"return\": { \"fdset-id\": 1, \"fd\": 95 }}", @@ -803,4 +807,5 @@ VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virpci"), VIR_TEST_MOCK("domaincaps"), VIR_TEST_MOCK("virprocess"), - VIR_TEST_MOCK("qemuhotplug")); + VIR_TEST_MOCK("qemuhotplug"), + VIR_TEST_MOCK("virnetdev")); diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-interface-hostdev.xml b/tests/qemuhotplugtestdevices/qemuhotplug-interface-hostdev.xml new file mode 100644 index 0000000000..0eb7a1df1a --- /dev/null +++ b/tests/qemuhotplugtestdevices/qemuhotplug-interface-hostdev.xml @@ -0,0 +1,6 @@ +<interface type='hostdev' managed='yes'> + <source> + <address type='pci' domain='0x0000' bus='0x06' slot='0x12' function='0x2'/> + </source> + <mac address='52:54:00:6d:90:02'/> +</interface> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-hostdev.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-hostdev.xml new file mode 100644 index 0000000000..13288f7711 --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-hostdev.xml @@ -0,0 +1,63 @@ +<domain type='kvm' id='7'> + <name>hotplug</name> + <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0' model='piix3-uhci'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <alias name='ide'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='scsi' index='0' model='virtio-scsi'> + <alias name='scsi0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci'/> + </controller> + <controller type='virtio-serial' index='0'> + <alias name='virtio-serial0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </controller> + <interface type='hostdev' managed='yes'> + <mac address='52:54:00:6d:90:02'/> + <driver name='vfio'/> + <source> + <address type='pci' domain='0x0000' bus='0x06' slot='0x12' function='0x2'/> + </source> + <alias name='hostdev0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <audio id='1' type='none'/> + <memballoon model='none'/> + </devices> + <seclabel type='none' model='none'/> +</domain> -- 2.52.0
participants (1)
-
Michal Privoznik