From cd618e02120936e0e72624878081bd9c54437a30 Mon Sep 17 00:00:00 2001 From: Chunyan Liu Date: Thu, 6 Mar 2014 16:08:36 +0800 Subject: [PATCH] add unit test for new virhostdev common library Add unit test for hostdev common library. Current tests are based on virpcimock. --- tests/Makefile.am | 5 + tests/virhostdevtest.c | 470 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 475 insertions(+) create mode 100644 tests/virhostdevtest.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 3267ad31a7..1745469402 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -152,6 +152,7 @@ test_programs = virshtest sockettest \ virkmodtest \ vircapstest \ domainconftest \ + virhostdevtest \ $(NULL) if WITH_REMOTE @@ -822,6 +823,10 @@ vircryptotest_SOURCES = \ vircryptotest.c testutils.h testutils.c vircryptotest_LDADD = $(LDADDS) +virhostdevtest_SOURCES = \ + virhostdevtest.c testutils.h testutils.c +virhostdevtest_LDADD = $(LDADDS) + virpcitest_SOURCES = \ virpcitest.c testutils.h testutils.c virpcitest_LDADD = $(LDADDS) diff --git a/tests/virhostdevtest.c b/tests/virhostdevtest.c new file mode 100644 index 0000000000..04bd2eaf41 --- /dev/null +++ b/tests/virhostdevtest.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Chunyan Liu + */ + +#include + +#include "testutils.h" + +#ifdef __linux__ + +# include +# include +# include +# include +# include +# include +# include "virlog.h" +# include "virhostdev.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +# define CHECK_LIST_COUNT(list, cnt) \ + if ((count = virPCIDeviceListCount(list)) != cnt) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + "Unexpected count of items in " #list ": %d, " \ + "expecting %zu", count, (size_t) cnt); \ + goto cleanup; \ + } + +# define TEST_STATE_DIR abs_builddir "/hostdevmgr" +static const char *drv_name = "test_driver"; +static const char *dom_name = "test_domain"; +static const unsigned char *uuid = + (unsigned char *)("f92360b0-2541-8791-fb32-d1f838811541"); +static int nhostdevs = 3; +static virDomainHostdevDefPtr hostdevs[] = {NULL, NULL, NULL}; +static virPCIDevicePtr dev[] = {NULL, NULL, NULL}; +static virHostdevManagerPtr mgr = NULL; + +static void +myCleanup(void) +{ + size_t i; + for (i = 0; i < nhostdevs; i++) { + virPCIDeviceFree(dev[i]); + virDomainHostdevDefFree(hostdevs[i]); + } + + if (mgr) { + virObjectUnref(mgr->activePCIHostdevs); + virObjectUnref(mgr->inactivePCIHostdevs); + virObjectUnref(mgr->activeUSBHostdevs); + VIR_FREE(mgr->stateDir); + VIR_FREE(mgr); + } +} + +static int +myInit(void) +{ + size_t i; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevSubsys subsys; + hostdevs[i] = virDomainHostdevDefAlloc(); + if (!hostdevs[i]) + goto cleanup; + hostdevs[i]->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + subsys.u.pci.addr.domain = 0; + subsys.u.pci.addr.bus = 0; + subsys.u.pci.addr.slot = i + 1; + subsys.u.pci.addr.function = 0; + subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + hostdevs[i]->source.subsys = subsys; + } + + for (i = 0; i < nhostdevs; i++) { + if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)) || + virPCIDeviceSetStubDriver(dev[i], "pci-stub") < 0) + goto cleanup; + } + + if (VIR_ALLOC(mgr) < 0) + goto cleanup; + if ((mgr->activePCIHostdevs = virPCIDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->activeUSBHostdevs = virUSBDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->inactivePCIHostdevs = virPCIDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->activeSCSIHostdevs = virSCSIDeviceListNew()) == NULL) + goto cleanup; + if (VIR_STRDUP(mgr->stateDir, TEST_STATE_DIR) < 0) + goto cleanup; + if (virFileMakePath(mgr->stateDir) < 0) + goto cleanup; + + return 0; + +cleanup: + myCleanup(); + return -1; +} + +# if HAVE_LINUX_KVM_H +# include +static bool +virHostdevHostSupportsPassthroughKVM(void) +{ + int kvmfd = -1; + bool ret = false; + + if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0) + goto cleanup; + +# ifdef KVM_CAP_IOMMU + if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) <= 0) + goto cleanup; + + ret = true; +# endif + +cleanup: + VIR_FORCE_CLOSE(kvmfd); + + return ret; +} +# else +static bool +virHostdevHostSupportsPassthroughKVM(void) +{ + return false; +} +# endif + +static int +testVirHostdevPreparePCIHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1, count2; + + for (i = 0; i < nhostdevs; i++) + hostdevs[i]->managed = false; + + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePCIHostdevs); + + /* Test normal functionality */ + VIR_DEBUG("Test 0 hostdevs\n"); + if (virHostdevPreparePCIDevices(mgr, drv_name, dom_name, uuid, + NULL, 0, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + /* Test unmanaged hostdevs */ + VIR_DEBUG("Test >=1 unmanaged hostdevs\n"); + if (virHostdevPreparePCIDevices(mgr, drv_name, dom_name, uuid, + hostdevs, nhostdevs, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1 + 3); + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count2 - 3); + + /* Test conflict */ + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePCIHostdevs); + VIR_DEBUG("Test: prepare same hostdevs for same driver/domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, drv_name, dom_name, uuid, + &hostdevs[0], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count2); + + VIR_DEBUG("Test: prepare same hostdevs for same driver, diff domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, drv_name, "test_domain1", uuid, + &hostdevs[1], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count2); + + VIR_DEBUG("Test: prepare same hostdevs for diff driver/domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, "test_driver1", dom_name, uuid, + &hostdevs[2], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count2); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPCIHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1, count2; + + for (i = 0; i < nhostdevs; i++) { + if (hostdevs[i]->managed != false) { + VIR_DEBUG("invalid test\n"); + return -1; + } + } + + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePCIHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + virHostdevReAttachPCIDevices(mgr, drv_name, dom_name, NULL, 0, NULL); + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + VIR_DEBUG("Test >=1 unmanaged hostdevs\n"); + virHostdevReAttachPCIDevices(mgr, drv_name, dom_name, + hostdevs, nhostdevs, NULL); + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1 - 3); + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count2 + 3); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevPreparePCIHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) + hostdevs[i]->managed = true; + + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + + /* Test normal functionality */ + VIR_DEBUG("Test >=1 hostdevs\n"); + if (virHostdevPreparePCIDevices(mgr, drv_name, dom_name, uuid, + hostdevs, nhostdevs, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1 + 3); + + /* Test conflict */ + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + VIR_DEBUG("Test: prepare same hostdevs for same driver/domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, drv_name, dom_name, uuid, + &hostdevs[0], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + VIR_DEBUG("Test: prepare same hostdevs for same driver, diff domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, drv_name, "test_domain1", uuid, + &hostdevs[1], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + VIR_DEBUG("Test: prepare same hostdevs for diff driver/domain again\n"); + if (!virHostdevPreparePCIDevices(mgr, "test_driver1", dom_name, uuid, + &hostdevs[2], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPCIHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) { + if (hostdevs[i]->managed != true) { + VIR_DEBUG("invalid test\n"); + return -1; + } + } + + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + virHostdevReAttachPCIDevices(mgr, drv_name, dom_name, NULL, 0, NULL); + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + VIR_DEBUG("Test >=1 hostdevs\n"); + virHostdevReAttachPCIDevices(mgr, drv_name, dom_name, + hostdevs, nhostdevs, NULL); + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1 - 3); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevDetachPCINodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) { + count1 = virPCIDeviceListCount(mgr->inactivePCIHostdevs); + if (virHostdevPCINodeDeviceDetach(mgr, dev[i]) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count1 + 1); + } + + ret = 0; + +cleanup: + return ret; +} +static int +testVirHostdevResetPCINodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + + for (i = 0; i < nhostdevs; i++) { + if (virHostdevPCINodeDeviceReset(mgr, dev[i]) < 0) + goto cleanup; + } + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPCINodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) { + count1 = virPCIDeviceListCount(mgr->inactivePCIHostdevs); + if (virHostdevPCINodeDeviceReAttach(mgr, dev[i]) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->inactivePCIHostdevs, count1 - 1); + } + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevUpdateActivePCIHostdevs(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + int count, count1; + + count1 = virPCIDeviceListCount(mgr->activePCIHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + if (virHostdevUpdateActivePCIDevices(mgr, NULL, 0, + drv_name, dom_name) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1); + + VIR_DEBUG("Test >=1 hostdevs\n"); + if (virHostdevUpdateActivePCIDevices(mgr, hostdevs, nhostdevs, + drv_name, dom_name) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePCIHostdevs, count1 + 3); + + ret = 0; + +cleanup: + return ret; +} + +# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX" + +static int +mymain(void) +{ + int ret = 0; + char *fakesysfsdir; + + if (VIR_STRDUP_QUIET(fakesysfsdir, FAKESYSFSDIRTEMPLATE) < 0) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + if (!mkdtemp(fakesysfsdir)) { + fprintf(stderr, "Cannot create fakesysfsdir"); + abort(); + } + + setenv("LIBVIRT_FAKE_SYSFS_DIR", fakesysfsdir, 1); + +# define DO_TEST(fnc) \ + do { \ + VIR_DEBUG("\nTesting: %s", #fnc); \ + if (virtTestRun(#fnc, fnc, NULL) < 0) \ + ret = -1; \ + } while (0) + + if (myInit() < 0) + fprintf(stderr, "Init data structures failed."); + + DO_TEST(testVirHostdevDetachPCINodeDevice); + if (virHostdevHostSupportsPassthroughKVM()) { + /* following tests would check KVM support */ + DO_TEST(testVirHostdevPreparePCIHostdevs_unmanaged); + DO_TEST(testVirHostdevReAttachPCIHostdevs_unmanaged); + } + DO_TEST(testVirHostdevResetPCINodeDevice); + DO_TEST(testVirHostdevReAttachPCINodeDevice); + if (virHostdevHostSupportsPassthroughKVM()) { + /* following tests would check KVM support */ + DO_TEST(testVirHostdevPreparePCIHostdevs_managed); + DO_TEST(testVirHostdevReAttachPCIHostdevs_managed); + } + DO_TEST(testVirHostdevUpdateActivePCIHostdevs); + + myCleanup(); + + if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL) + virFileDeleteTree(fakesysfsdir); + + VIR_FREE(fakesysfsdir); + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virpcimock.so") +#else +int +main(void) +{ + return EXIT_AM_SKIP; +} +#endif