[PATCH v4 00/11] Implement IOThreads related APIs for test driver

v4: - Rebase to current master branch - Add the forgotten virObjectUnlock(vm) in PATCH 09/11 - Refine tests CI link: https://gitlab.com/lukedyue/libvirt/-/pipelines/371315349 Luke Yue (11): domain_driver.c: Introduce and use virDomainDriverAddIOThreadCheck() test_driver: Introduce testIOThreadInfo and generate IOThread infos test_driver: Implement virDomainAddIOThread test_driver: Implement virDomainDelIOThread domain_driver.c: Introduce and use virDomainDriverGetIOThreadsConfig() test_driver: Implement virDomainGetIOThreadInfo test_driver: Implement virDomainPinIOThread test_driver: Implement testDomainSetIOThreadParams test_driver: Implement virConnectGetAllDomainStats test_driver: Introduce testDomainGetStatsIOThread tests: Test IOThread related functions for test driver examples/xml/test/testdomfc4.xml | 5 + src/hypervisor/domain_driver.c | 132 ++++++++ src/hypervisor/domain_driver.h | 10 + src/libvirt_private.syms | 3 + src/qemu/qemu_driver.c | 113 +------ src/test/test_driver.c | 516 +++++++++++++++++++++++++++++++ tests/virshtest.c | 110 +++++++ 7 files changed, 781 insertions(+), 108 deletions(-) -- 2.33.0

The test driver can share the same code with qemu driver when implement testDomainAddIOThreadCheck and testDomainDelIOThreadCheck, so extract them for test driver to use. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/hypervisor/domain_driver.c | 64 ++++++++++++++++++++++++++++++++++ src/hypervisor/domain_driver.h | 6 ++++ src/libvirt_private.syms | 2 ++ src/qemu/qemu_driver.c | 60 +++---------------------------- 4 files changed, 76 insertions(+), 56 deletions(-) diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c index 2969d55173..3eb2401053 100644 --- a/src/hypervisor/domain_driver.c +++ b/src/hypervisor/domain_driver.c @@ -512,3 +512,67 @@ virDomainDriverNodeDeviceDetachFlags(virNodeDevicePtr dev, return virHostdevPCINodeDeviceDetach(hostdevMgr, pci); } + +/** + * virDomainDriverAddIOThreadCheck: + * @def: domain definition + * @iothread_id: iothread id + * + * Returns -1 if an IOThread is already using the given iothread id + */ +int +virDomainDriverAddIOThreadCheck(virDomainDef *def, + unsigned int iothread_id) +{ + if (virDomainIOThreadIDFind(def, iothread_id)) { + virReportError(VIR_ERR_INVALID_ARG, + _("an IOThread is already using iothread_id '%u'"), + iothread_id); + return -1; + } + + return 0; +} + +/** + * virDomainDriverDelIOThreadCheck: + * @def: domain definition + * @iothread_id: iothread id + * + * Returns -1 if there is no IOThread using the given iothread id + */ +int +virDomainDriverDelIOThreadCheck(virDomainDef *def, + unsigned int iothread_id) +{ + size_t i; + + if (!virDomainIOThreadIDFind(def, iothread_id)) { + virReportError(VIR_ERR_INVALID_ARG, + _("cannot find IOThread '%u' in iothreadids list"), + iothread_id); + return -1; + } + + for (i = 0; i < def->ndisks; i++) { + if (def->disks[i]->iothread == iothread_id) { + virReportError(VIR_ERR_INVALID_ARG, + _("cannot remove IOThread %u since it " + "is being used by disk '%s'"), + iothread_id, def->disks[i]->dst); + return -1; + } + } + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->iothread == iothread_id) { + virReportError(VIR_ERR_INVALID_ARG, + _("cannot remove IOThread '%u' since it " + "is being used by controller"), + iothread_id); + return -1; + } + } + + return 0; +} diff --git a/src/hypervisor/domain_driver.h b/src/hypervisor/domain_driver.h index 5970eef082..d91d21bc91 100644 --- a/src/hypervisor/domain_driver.h +++ b/src/hypervisor/domain_driver.h @@ -60,3 +60,9 @@ int virDomainDriverNodeDeviceReAttach(virNodeDevicePtr dev, int virDomainDriverNodeDeviceDetachFlags(virNodeDevicePtr dev, virHostdevManager *hostdevMgr, const char *driverName); + +int virDomainDriverAddIOThreadCheck(virDomainDef *def, + unsigned int iothread_id); + +int virDomainDriverDelIOThreadCheck(virDomainDef *def, + unsigned int iothread_id); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ace35d709f..d45b644caf 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1537,6 +1537,8 @@ virDomainCgroupSetupMemtune; # hypervisor/domain_driver.h +virDomainDriverAddIOThreadCheck; +virDomainDriverDelIOThreadCheck; virDomainDriverGenerateMachineName; virDomainDriverGenerateRootHash; virDomainDriverMergeBlkioDevice; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6ae678b165..f2acb37c76 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5445,58 +5445,6 @@ qemuDomainHotplugDelIOThread(virQEMUDriver *driver, } -static int -qemuDomainAddIOThreadCheck(virDomainDef *def, - unsigned int iothread_id) -{ - if (virDomainIOThreadIDFind(def, iothread_id)) { - virReportError(VIR_ERR_INVALID_ARG, - _("an IOThread is already using iothread_id '%u'"), - iothread_id); - return -1; - } - - return 0; -} - - -static int -qemuDomainDelIOThreadCheck(virDomainDef *def, - unsigned int iothread_id) -{ - size_t i; - - if (!virDomainIOThreadIDFind(def, iothread_id)) { - virReportError(VIR_ERR_INVALID_ARG, - _("cannot find IOThread '%u' in iothreadids list"), - iothread_id); - return -1; - } - - for (i = 0; i < def->ndisks; i++) { - if (def->disks[i]->iothread == iothread_id) { - virReportError(VIR_ERR_INVALID_ARG, - _("cannot remove IOThread %u since it " - "is being used by disk '%s'"), - iothread_id, def->disks[i]->dst); - return -1; - } - } - - for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->iothread == iothread_id) { - virReportError(VIR_ERR_INVALID_ARG, - _("cannot remove IOThread '%u' since it " - "is being used by controller"), - iothread_id); - return -1; - } - } - - return 0; -} - - /** * @params: Pointer to params list * @nparams: Number of params to be parsed @@ -5630,7 +5578,7 @@ qemuDomainChgIOThread(virQEMUDriver *driver, switch (action) { case VIR_DOMAIN_IOTHREAD_ACTION_ADD: - if (qemuDomainAddIOThreadCheck(def, iothread.iothread_id) < 0) + if (virDomainDriverAddIOThreadCheck(def, iothread.iothread_id) < 0) goto endjob; if (qemuDomainHotplugAddIOThread(driver, vm, iothread.iothread_id) < 0) @@ -5639,7 +5587,7 @@ qemuDomainChgIOThread(virQEMUDriver *driver, break; case VIR_DOMAIN_IOTHREAD_ACTION_DEL: - if (qemuDomainDelIOThreadCheck(def, iothread.iothread_id) < 0) + if (virDomainDriverDelIOThreadCheck(def, iothread.iothread_id) < 0) goto endjob; if (qemuDomainHotplugDelIOThread(driver, vm, iothread.iothread_id) < 0) @@ -5668,7 +5616,7 @@ qemuDomainChgIOThread(virQEMUDriver *driver, if (persistentDef) { switch (action) { case VIR_DOMAIN_IOTHREAD_ACTION_ADD: - if (qemuDomainAddIOThreadCheck(persistentDef, iothread.iothread_id) < 0) + if (virDomainDriverAddIOThreadCheck(persistentDef, iothread.iothread_id) < 0) goto endjob; if (!virDomainIOThreadIDAdd(persistentDef, iothread.iothread_id)) @@ -5677,7 +5625,7 @@ qemuDomainChgIOThread(virQEMUDriver *driver, break; case VIR_DOMAIN_IOTHREAD_ACTION_DEL: - if (qemuDomainDelIOThreadCheck(persistentDef, iothread.iothread_id) < 0) + if (virDomainDriverDelIOThreadCheck(persistentDef, iothread.iothread_id) < 0) goto endjob; virDomainIOThreadIDDel(persistentDef, iothread.iothread_id); -- 2.33.0

Introduce testIOThreadInfo to store IOThread infos: iothread_id, poll_max_ns, poll_grow and poll_shrink for future usage. Add an example of IOThread configuration to testdomfc4.xml, we also want to generate default testIOThreadInfo for the IOThread configured in the xml, so introduce testDomainGenerateIOThreadInfos, the values are taken from QEMU. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- examples/xml/test/testdomfc4.xml | 5 +++++ src/test/test_driver.c | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/examples/xml/test/testdomfc4.xml b/examples/xml/test/testdomfc4.xml index 26b7f25a06..cb4dd0cf70 100644 --- a/examples/xml/test/testdomfc4.xml +++ b/examples/xml/test/testdomfc4.xml @@ -11,6 +11,11 @@ <memory>261072</memory> <currentMemory>131072</currentMemory> <vcpu>1</vcpu> + <iothreads>2</iothreads> + <iothreadids> + <iothread id="2"/> + <iothread id="4"/> + </iothreadids> <devices> <disk type='file'> <source file='/u/fc4.img'/> diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 2c2c6f52d1..8de8779c7f 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -183,6 +183,14 @@ struct _testDomainNamespaceDef { xmlNodePtr *snap_nodes; }; +typedef struct _testIOThreadInfo testIOThreadInfo; +struct _testIOThreadInfo { + unsigned int iothread_id; + unsigned long long poll_max_ns; + unsigned int poll_grow; + unsigned int poll_shrink; +}; + static void testDomainDefNamespaceFree(void *data) { @@ -381,6 +389,9 @@ struct _testDomainObjPrivate { /* used by get/set time APIs */ long long seconds; unsigned int nseconds; + + /* used by IOThread APIs */ + GArray *iothreads; }; @@ -397,6 +408,8 @@ testDomainObjPrivateAlloc(void *opaque) priv->seconds = 627319920; priv->nseconds = 0; + priv->iothreads = g_array_new(FALSE, FALSE, sizeof(testIOThreadInfo)); + return priv; } @@ -427,6 +440,8 @@ static void testDomainObjPrivateFree(void *data) { testDomainObjPrivate *priv = data; + + g_array_free(priv->iothreads, TRUE); g_free(priv); } @@ -696,6 +711,26 @@ testDomainGenerateIfnames(virDomainDef *domdef) return 0; } +static void +testDomainGenerateIOThreadInfos(virDomainObj *obj) +{ + size_t i; + testDomainObjPrivate *priv; + + if (!obj->def->iothreadids || !obj->def->niothreadids) + return; + + priv = obj->privateData; + + for (i = 0; i < obj->def->niothreadids; i++) { + testIOThreadInfo iothread; + iothread.iothread_id = obj->def->iothreadids[i]->iothread_id; + iothread.poll_max_ns = 32768; + iothread.poll_grow = 0; + iothread.poll_shrink = 0; + g_array_append_val(priv->iothreads, iothread); + } +} static void testDomainShutdownState(virDomainPtr domain, @@ -1045,6 +1080,8 @@ testParseDomains(testDriver *privconn, testDomainObjCheckTaint(obj); + testDomainGenerateIOThreadInfos(obj); + virDomainObjEndAPI(&obj); } -- 2.33.0

Introduce testDomainChgIOThread at the same time, could be used for virDomainDelIOThread etc. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 8de8779c7f..e0b7ace4ed 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9586,6 +9586,81 @@ testDomainGetMessages(virDomainPtr dom, return rv; } +typedef enum { + VIR_DOMAIN_IOTHREAD_ACTION_ADD, +} virDomainIOThreadAction; + +static int +testDomainChgIOThread(virDomainObj *vm, + unsigned int iothread_id, + virDomainIOThreadAction action, + unsigned int flags) +{ + virDomainDef *def; + int ret = -1; + + if (!(def = virDomainObjGetOneDef(vm, flags))) + return ret; + + if (def) { + switch (action) { + case VIR_DOMAIN_IOTHREAD_ACTION_ADD: + if (virDomainDriverAddIOThreadCheck(def, iothread_id) < 0) + return ret; + + if (!virDomainIOThreadIDAdd(def, iothread_id)) + return ret; + + break; + } + } + + ret = 0; + + return ret; +} + +static int +testDomainAddIOThread(virDomainPtr dom, + unsigned int iothread_id, + unsigned int flags) +{ + virDomainObj *vm = NULL; + testDomainObjPrivate *priv; + testIOThreadInfo iothread; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (iothread_id == 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid value of 0 for iothread_id")); + return -1; + } + + if (!(vm = testDomObjFromDomain(dom))) + goto cleanup; + + if (testDomainChgIOThread(vm, iothread_id, + VIR_DOMAIN_IOTHREAD_ACTION_ADD, flags) < 0) + goto cleanup; + + priv = vm->privateData; + + iothread.iothread_id = iothread_id; + iothread.poll_max_ns = 32768; + iothread.poll_grow = 0; + iothread.poll_shrink = 0; + + g_array_append_val(priv->iothreads, iothread); + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} /* * Test driver */ @@ -9652,6 +9727,7 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetVcpus = testDomainGetVcpus, /* 0.7.3 */ .domainGetVcpuPinInfo = testDomainGetVcpuPinInfo, /* 1.2.18 */ .domainGetMaxVcpus = testDomainGetMaxVcpus, /* 0.7.3 */ + .domainAddIOThread = testDomainAddIOThread, /* 7.8.0 */ .domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */ .nodeGetSecurityModel = testNodeGetSecurityModel, /* 7.5.0 */ .domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */ -- 2.33.0

Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e0b7ace4ed..b588bbc32e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9588,6 +9588,7 @@ testDomainGetMessages(virDomainPtr dom, typedef enum { VIR_DOMAIN_IOTHREAD_ACTION_ADD, + VIR_DOMAIN_IOTHREAD_ACTION_DEL, } virDomainIOThreadAction; static int @@ -9612,6 +9613,14 @@ testDomainChgIOThread(virDomainObj *vm, return ret; break; + + case VIR_DOMAIN_IOTHREAD_ACTION_DEL: + if (virDomainDriverDelIOThreadCheck(def, iothread_id) < 0) + return ret; + + virDomainIOThreadIDDel(def, iothread_id); + + break; } } @@ -9661,6 +9670,51 @@ testDomainAddIOThread(virDomainPtr dom, virDomainObjEndAPI(&vm); return ret; } + +static int +testDomainDelIOThread(virDomainPtr dom, + unsigned int iothread_id, + unsigned int flags) +{ + virDomainObj *vm = NULL; + testDomainObjPrivate *priv; + size_t i; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (iothread_id == 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid value of 0 for iothread_id")); + return -1; + } + + if (!(vm = testDomObjFromDomain(dom))) + goto cleanup; + + if (testDomainChgIOThread(vm, iothread_id, + VIR_DOMAIN_IOTHREAD_ACTION_DEL, flags) < 0) + goto cleanup; + + priv = vm->privateData; + + for (i = 0; i < priv->iothreads->len; i++) { + testIOThreadInfo iothread = g_array_index(priv->iothreads, + testIOThreadInfo, i); + if (iothread.iothread_id == iothread_id) { + g_array_remove_index(priv->iothreads, i); + break; + } + } + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + /* * Test driver */ @@ -9728,6 +9782,7 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetVcpuPinInfo = testDomainGetVcpuPinInfo, /* 1.2.18 */ .domainGetMaxVcpus = testDomainGetMaxVcpus, /* 0.7.3 */ .domainAddIOThread = testDomainAddIOThread, /* 7.8.0 */ + .domainDelIOThread = testDomainDelIOThread, /* 7.8.0 */ .domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */ .nodeGetSecurityModel = testNodeGetSecurityModel, /* 7.5.0 */ .domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */ -- 2.33.0

The test driver can share the same code with qemu driver when implement testDomainGetIOThreadsConfig, so extract it for test driver to use. Also add a new parameter `bitmap_size` to the function, it's used for specifying the bitmap size of the bitmap to generate, it would be helpful for test driver or some special situation. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/hypervisor/domain_driver.c | 68 ++++++++++++++++++++++++++++++++++ src/hypervisor/domain_driver.h | 4 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_driver.c | 53 +------------------------- 4 files changed, 74 insertions(+), 52 deletions(-) diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c index 3eb2401053..31737b0f4a 100644 --- a/src/hypervisor/domain_driver.c +++ b/src/hypervisor/domain_driver.c @@ -576,3 +576,71 @@ virDomainDriverDelIOThreadCheck(virDomainDef *def, return 0; } + +/** + * virDomainDriverGetIOThreadsConfig: + * @targetDef: domain definition + * @info: information about the IOThread in a domain + * @bitmap_size: generate bitmap with bitmap_size, 0 for getting the size + * from host + * + * Returns the number of IOThreads in the given domain or -1 in case of error + */ +int +virDomainDriverGetIOThreadsConfig(virDomainDef *targetDef, + virDomainIOThreadInfoPtr **info, + unsigned int bitmap_size) +{ + virDomainIOThreadInfoPtr *info_ret = NULL; + virBitmap *bitmap = NULL; + virBitmap *cpumask = NULL; + size_t i; + int ret = -1; + + if (targetDef->niothreadids == 0) + return 0; + + info_ret = g_new0(virDomainIOThreadInfoPtr, targetDef->niothreadids); + + for (i = 0; i < targetDef->niothreadids; i++) { + info_ret[i] = g_new0(virDomainIOThreadInfo, 1); + + /* IOThread ID's are taken from the iothreadids list */ + info_ret[i]->iothread_id = targetDef->iothreadids[i]->iothread_id; + + cpumask = targetDef->iothreadids[i]->cpumask; + if (!cpumask) { + if (targetDef->cpumask) { + cpumask = targetDef->cpumask; + } else { + if (bitmap_size) { + if (!(bitmap = virBitmapNew(bitmap_size))) + goto cleanup; + virBitmapSetAll(bitmap); + } else { + if (!(bitmap = virHostCPUGetAvailableCPUsBitmap())) + goto cleanup; + } + cpumask = bitmap; + } + } + if (virBitmapToData(cpumask, &info_ret[i]->cpumap, + &info_ret[i]->cpumaplen) < 0) + goto cleanup; + virBitmapFree(bitmap); + bitmap = NULL; + } + + *info = g_steal_pointer(&info_ret); + ret = targetDef->niothreadids; + + cleanup: + if (info_ret) { + for (i = 0; i < targetDef->niothreadids; i++) + virDomainIOThreadInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + virBitmapFree(bitmap); + + return ret; +} diff --git a/src/hypervisor/domain_driver.h b/src/hypervisor/domain_driver.h index d91d21bc91..7b0fbae2fd 100644 --- a/src/hypervisor/domain_driver.h +++ b/src/hypervisor/domain_driver.h @@ -66,3 +66,7 @@ int virDomainDriverAddIOThreadCheck(virDomainDef *def, int virDomainDriverDelIOThreadCheck(virDomainDef *def, unsigned int iothread_id); + +int virDomainDriverGetIOThreadsConfig(virDomainDef *targetDef, + virDomainIOThreadInfoPtr **info, + unsigned int bitmap_size); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d45b644caf..bcc413be02 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1541,6 +1541,7 @@ virDomainDriverAddIOThreadCheck; virDomainDriverDelIOThreadCheck; virDomainDriverGenerateMachineName; virDomainDriverGenerateRootHash; +virDomainDriverGetIOThreadsConfig; virDomainDriverMergeBlkioDevice; virDomainDriverNodeDeviceDetachFlags; virDomainDriverNodeDeviceGetPCIInfo; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f2acb37c76..54b8529d88 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5032,57 +5032,6 @@ qemuDomainGetIOThreadsLive(virQEMUDriver *driver, return ret; } -static int -qemuDomainGetIOThreadsConfig(virDomainDef *targetDef, - virDomainIOThreadInfoPtr **info) -{ - virDomainIOThreadInfoPtr *info_ret = NULL; - virBitmap *bitmap = NULL; - virBitmap *cpumask = NULL; - size_t i; - int ret = -1; - - if (targetDef->niothreadids == 0) - return 0; - - info_ret = g_new0(virDomainIOThreadInfoPtr, targetDef->niothreadids); - - for (i = 0; i < targetDef->niothreadids; i++) { - info_ret[i] = g_new0(virDomainIOThreadInfo, 1); - - /* IOThread ID's are taken from the iothreadids list */ - info_ret[i]->iothread_id = targetDef->iothreadids[i]->iothread_id; - - cpumask = targetDef->iothreadids[i]->cpumask; - if (!cpumask) { - if (targetDef->cpumask) { - cpumask = targetDef->cpumask; - } else { - if (!(bitmap = virHostCPUGetAvailableCPUsBitmap())) - goto cleanup; - cpumask = bitmap; - } - } - if (virBitmapToData(cpumask, &info_ret[i]->cpumap, - &info_ret[i]->cpumaplen) < 0) - goto cleanup; - virBitmapFree(bitmap); - bitmap = NULL; - } - - *info = g_steal_pointer(&info_ret); - ret = targetDef->niothreadids; - - cleanup: - if (info_ret) { - for (i = 0; i < targetDef->niothreadids; i++) - virDomainIOThreadInfoFree(info_ret[i]); - VIR_FREE(info_ret); - } - virBitmapFree(bitmap); - - return ret; -} static int qemuDomainGetIOThreadInfo(virDomainPtr dom, @@ -5109,7 +5058,7 @@ qemuDomainGetIOThreadInfo(virDomainPtr dom, if (!targetDef) ret = qemuDomainGetIOThreadsLive(driver, vm, info); else - ret = qemuDomainGetIOThreadsConfig(targetDef, info); + ret = virDomainDriverGetIOThreadsConfig(targetDef, info, 0); cleanup: virDomainObjEndAPI(&vm); -- 2.33.0

If we use test driver on different machines, and use 0 as bitmap_size for virDomainDriverGetIOThreadsConfig(), we would get different results for the `CPU Affinity`, because it's depending on the host CPU's bitmap. In order to get a stable result for testing, use result of virDomainDefGetVcpus() as bitmap_size instead. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index b588bbc32e..0971661c89 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9586,6 +9586,34 @@ testDomainGetMessages(virDomainPtr dom, return rv; } +static int +testDomainGetIOThreadInfo(virDomainPtr dom, + virDomainIOThreadInfoPtr **info, + unsigned int flags) +{ + virDomainObj *vm; + virDomainDef *targetDef = NULL; + unsigned int bitmap_size = 0; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (!(vm = testDomObjFromDomain(dom))) + goto cleanup; + + if (!(targetDef = virDomainObjGetOneDef(vm, flags))) + goto cleanup; + + bitmap_size = virDomainDefGetVcpus(targetDef); + + ret = virDomainDriverGetIOThreadsConfig(targetDef, info, bitmap_size); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + typedef enum { VIR_DOMAIN_IOTHREAD_ACTION_ADD, VIR_DOMAIN_IOTHREAD_ACTION_DEL, @@ -9781,6 +9809,7 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetVcpus = testDomainGetVcpus, /* 0.7.3 */ .domainGetVcpuPinInfo = testDomainGetVcpuPinInfo, /* 1.2.18 */ .domainGetMaxVcpus = testDomainGetMaxVcpus, /* 0.7.3 */ + .domainGetIOThreadInfo = testDomainGetIOThreadInfo, /* 7.8.0 */ .domainAddIOThread = testDomainAddIOThread, /* 7.8.0 */ .domainDelIOThread = testDomainDelIOThread, /* 7.8.0 */ .domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */ -- 2.33.0

Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 0971661c89..43c0822c87 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9743,6 +9743,55 @@ testDomainDelIOThread(virDomainPtr dom, return ret; } +static int +testDomainPinIOThread(virDomainPtr dom, + unsigned int iothread_id, + unsigned char *cpumap, + int maplen, + unsigned int flags) +{ + int ret = -1; + virDomainObj *vm; + virDomainDef *def; + virDomainIOThreadIDDef *iothrid; + virBitmap *cpumask = NULL; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (!(vm = testDomObjFromDomain(dom))) + goto cleanup; + + if (!(def = virDomainObjGetOneDef(vm, flags))) + goto cleanup; + + if (!(cpumask = virBitmapNewData(cpumap, maplen))) + goto cleanup; + + if (virBitmapIsAllClear(cpumask)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Empty iothread cpumap list for pinning")); + goto cleanup; + } + + if (!(iothrid = virDomainIOThreadIDFind(def, iothread_id))) { + virReportError(VIR_ERR_INVALID_ARG, + _("iothreadid %d not found"), iothread_id); + goto cleanup; + } + + virBitmapFree(iothrid->cpumask); + iothrid->cpumask = g_steal_pointer(&cpumask); + iothrid->autofill = false; + + ret = 0; + + cleanup: + virBitmapFree(cpumask); + virDomainObjEndAPI(&vm); + return ret; +} + /* * Test driver */ @@ -9812,6 +9861,7 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetIOThreadInfo = testDomainGetIOThreadInfo, /* 7.8.0 */ .domainAddIOThread = testDomainAddIOThread, /* 7.8.0 */ .domainDelIOThread = testDomainDelIOThread, /* 7.8.0 */ + .domainPinIOThread = testDomainPinIOThread, /* 7.8.0 */ .domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */ .nodeGetSecurityModel = testNodeGetSecurityModel, /* 7.5.0 */ .domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */ -- 2.33.0

Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 43c0822c87..7cff26776a 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9617,6 +9617,7 @@ testDomainGetIOThreadInfo(virDomainPtr dom, typedef enum { VIR_DOMAIN_IOTHREAD_ACTION_ADD, VIR_DOMAIN_IOTHREAD_ACTION_DEL, + VIR_DOMAIN_IOTHREAD_ACTION_MOD, } virDomainIOThreadAction; static int @@ -9648,6 +9649,16 @@ testDomainChgIOThread(virDomainObj *vm, virDomainIOThreadIDDel(def, iothread_id); + break; + + case VIR_DOMAIN_IOTHREAD_ACTION_MOD: + if (!(virDomainIOThreadIDFind(def, iothread_id))) { + virReportError(VIR_ERR_INVALID_ARG, + _("cannot find IOThread '%u' in iothreadids"), + iothread_id); + return ret; + } + break; } } @@ -9792,6 +9803,84 @@ testDomainPinIOThread(virDomainPtr dom, return ret; } +static int +testDomainIOThreadParseParams(virTypedParameterPtr params, + int nparams, + testIOThreadInfo *iothread) +{ + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_MAX_NS, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_IOTHREAD_POLL_GROW, + VIR_TYPED_PARAM_UINT, + VIR_DOMAIN_IOTHREAD_POLL_SHRINK, + VIR_TYPED_PARAM_UINT, + NULL) < 0) + return -1; + + if (virTypedParamsGetULLong(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_MAX_NS, + &iothread->poll_max_ns) < 0) + return -1; + + if (virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_GROW, + &iothread->poll_grow) < 0) + return -1; + + if (virTypedParamsGetUInt(params, nparams, + VIR_DOMAIN_IOTHREAD_POLL_SHRINK, + &iothread->poll_shrink) < 0) + return -1; + + return 0; +} + +static int +testDomainSetIOThreadParams(virDomainPtr dom, + unsigned int iothread_id, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + testDomainObjPrivate *priv; + size_t i; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1); + + if (iothread_id == 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("invalid value of 0 for iothread_id")); + goto cleanup; + } + + if (!(vm = testDomObjFromDomain(dom))) + goto cleanup; + + if (testDomainChgIOThread(vm, iothread_id, + VIR_DOMAIN_IOTHREAD_ACTION_MOD, flags) < 0) + goto cleanup; + + priv = vm->privateData; + + for (i = 0; i < priv->iothreads->len; i++) { + testIOThreadInfo *iothread = &g_array_index(priv->iothreads, + testIOThreadInfo, i); + if (iothread->iothread_id == iothread_id) { + if (testDomainIOThreadParseParams(params, nparams, iothread) < 0) + goto cleanup; + ret = 0; + break; + } + } + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + /* * Test driver */ @@ -9862,6 +9951,7 @@ static virHypervisorDriver testHypervisorDriver = { .domainAddIOThread = testDomainAddIOThread, /* 7.8.0 */ .domainDelIOThread = testDomainDelIOThread, /* 7.8.0 */ .domainPinIOThread = testDomainPinIOThread, /* 7.8.0 */ + .domainSetIOThreadParams = testDomainSetIOThreadParams, /* 7.8.0 */ .domainGetSecurityLabel = testDomainGetSecurityLabel, /* 7.5.0 */ .nodeGetSecurityModel = testNodeGetSecurityModel, /* 7.5.0 */ .domainGetXMLDesc = testDomainGetXMLDesc, /* 0.1.4 */ -- 2.33.0

Implement virConnectGetAllDomainStats in a modular way just like QEMU driver, though remove some params in GetStatsWorker that we don't need in test driver currently. Only add the worker to get state so far, more worker will be added in the future. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 137 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7cff26776a..a6f7de2ccd 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9881,6 +9881,142 @@ testDomainSetIOThreadParams(virDomainPtr dom, return ret; } +static int +testDomainGetStatsState(virDomainObj *dom, + virTypedParamList *params) +{ + if (virTypedParamListAddInt(params, dom->state.state, "state.state") < 0) + return -1; + + if (virTypedParamListAddInt(params, dom->state.reason, "state.reason") < 0) + return -1; + + return 0; +} + +typedef int +(*testDomainGetStatsFunc)(virDomainObj *dom, + virTypedParamList *list); + +struct testDomainGetStatsWorker { + testDomainGetStatsFunc func; + unsigned int stats; +}; + +static struct testDomainGetStatsWorker testDomainGetStatsWorkers[] = { + { testDomainGetStatsState, VIR_DOMAIN_STATS_STATE }, + { NULL, 0 } +}; + +static int +testDomainGetStats(virConnectPtr conn, + virDomainObj *dom, + unsigned int stats, + virDomainStatsRecordPtr *record) +{ + g_autofree virDomainStatsRecordPtr tmp = NULL; + g_autoptr(virTypedParamList) params = NULL; + size_t i; + + params = g_new0(virTypedParamList, 1); + + for (i = 0; testDomainGetStatsWorkers[i].func; i++) { + if (stats & testDomainGetStatsWorkers[i].stats) { + if (testDomainGetStatsWorkers[i].func(dom, params) < 0) + return -1; + } + } + + tmp = g_new0(virDomainStatsRecord, 1); + + if (!(tmp->dom = virGetDomain(conn, dom->def->name, + dom->def->uuid, dom->def->id))) + return -1; + + tmp->nparams = virTypedParamListStealParams(params, &tmp->params); + *record = g_steal_pointer(&tmp); + return 0; +} + +static int +testConnectGetAllDomainStats(virConnectPtr conn, + virDomainPtr *doms, + unsigned int ndoms, + unsigned int stats, + virDomainStatsRecordPtr **retStats, + unsigned int flags) +{ + testDriver *driver = conn->privateData; + unsigned int lflags = flags & (VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT | + VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE); + + unsigned int supported = VIR_DOMAIN_STATS_STATE; + virDomainObj **vms = NULL; + size_t nvms; + virDomainStatsRecordPtr *tmpstats = NULL; + int nstats = 0; + int ret = -1; + size_t i; + + virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT | + VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE | + VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT | + VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING | + VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS, -1); + + if (!stats) { + stats = supported; + } else if ((flags & VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS) && + (stats & ~supported)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("Stats types bits 0x%x are not supported by this daemon"), + stats & ~supported); + return -1; + } + + if (ndoms) { + if (virDomainObjListConvert(driver->domains, conn, doms, ndoms, &vms, + &nvms, NULL, lflags, true) < 0) + return -1; + } else { + if (virDomainObjListCollect(driver->domains, conn, &vms, &nvms, + NULL, lflags) < 0) + return -1; + } + + tmpstats = g_new0(virDomainStatsRecordPtr, nvms + 1); + + for (i = 0; i < nvms; i++) { + virDomainStatsRecordPtr tmp = NULL; + virDomainObj *vm = vms[i]; + + virObjectLock(vm); + + if ((testDomainGetStats(conn, vm, stats, &tmp)) < 0) { + virObjectUnlock(vm); + goto cleanup; + } + + virObjectUnlock(vm); + + if (!tmp) + goto cleanup; + + tmpstats[nstats++] = tmp; + } + + *retStats = g_steal_pointer(&tmpstats); + ret = nstats; + + cleanup: + virDomainStatsRecordListFree(tmpstats); + virObjectListFreeCount(vms, nvms); + + return ret; +} + /* * Test driver */ @@ -9895,6 +10031,7 @@ static virHypervisorDriver testHypervisorDriver = { .nodeGetCPUStats = testNodeGetCPUStats, /* 2.3.0 */ .nodeGetFreeMemory = testNodeGetFreeMemory, /* 2.3.0 */ .nodeGetFreePages = testNodeGetFreePages, /* 2.3.0 */ + .connectGetAllDomainStats = testConnectGetAllDomainStats, /* 7.8.0 */ .connectGetCapabilities = testConnectGetCapabilities, /* 0.2.1 */ .connectGetSysinfo = testConnectGetSysinfo, /* 2.3.0 */ .connectGetType = testConnectGetType, /* 2.3.0 */ -- 2.33.0

Introduce testDomainGetStatsIOThread to add support for testConnectGetAllDomainStats to get IOThread infos. Signed-off-by: Luke Yue <lukedyue@gmail.com> --- src/test/test_driver.c | 44 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index a6f7de2ccd..d9b0578e13 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9894,6 +9894,46 @@ testDomainGetStatsState(virDomainObj *dom, return 0; } +static int +testDomainGetStatsIOThread(virDomainObj *dom, + virTypedParamList *params) +{ + testDomainObjPrivate *priv = dom->privateData; + size_t i; + int niothreads = 0; + + if (!virDomainObjIsActive(dom)) + return 0; + + niothreads = priv->iothreads->len; + + if (niothreads == 0) { + return 0; + } + + if (virTypedParamListAddUInt(params, niothreads, "iothread.count") < 0) + return -1; + + for (i = 0; i < niothreads; i++) { + testIOThreadInfo iothread = g_array_index(priv->iothreads, + testIOThreadInfo, i); + if (virTypedParamListAddULLong(params, iothread.poll_max_ns, + "iothread.%u.poll-max-ns", + iothread.iothread_id) < 0) + return -1; + if (virTypedParamListAddUInt(params, iothread.poll_grow, + "iothread.%u.poll-grow", + iothread.iothread_id) < 0) + return -1; + if (virTypedParamListAddUInt(params, iothread.poll_shrink, + "iothread.%u.poll-shrink", + iothread.iothread_id) < 0) + return -1; + } + + return 0; +} + typedef int (*testDomainGetStatsFunc)(virDomainObj *dom, virTypedParamList *list); @@ -9905,6 +9945,7 @@ struct testDomainGetStatsWorker { static struct testDomainGetStatsWorker testDomainGetStatsWorkers[] = { { testDomainGetStatsState, VIR_DOMAIN_STATS_STATE }, + { testDomainGetStatsIOThread, VIR_DOMAIN_STATS_IOTHREAD }, { NULL, 0 } }; @@ -9951,7 +9992,8 @@ testConnectGetAllDomainStats(virConnectPtr conn, VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT | VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE); - unsigned int supported = VIR_DOMAIN_STATS_STATE; + unsigned int supported = VIR_DOMAIN_STATS_STATE | + VIR_DOMAIN_STATS_IOTHREAD; virDomainObj **vms = NULL; size_t nvms; virDomainStatsRecordPtr *tmpstats = NULL; -- 2.33.0

testIOThreadAdd tests iothreadinfo and iothreadadd testIOThreadDel tests iothreadinfo and iothreaddel testIOThreadSet tests domstats and iothreadset testIOThreadPin tests iothreadadd, iothreadinfo and iothreadpin Above tests should cover the IOThreads related APIs for test driver Signed-off-by: Luke Yue <lukedyue@gmail.com> --- tests/virshtest.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/virshtest.c b/tests/virshtest.c index 87da1f5889..c2c892c60d 100644 --- a/tests/virshtest.c +++ b/tests/virshtest.c @@ -32,6 +32,7 @@ main(void) deprecated configuration: CPU model Deprecated-Test" # define GET_BLKIO_PARAMETER "/dev/hda,700" # define SET_BLKIO_PARAMETER "/dev/hda,1000" +# define EQUAL "=" static const char *dominfo_fc4 = "\ Id: 2\n\ @@ -338,6 +339,99 @@ static int testCompareSetBlkioParameters(const void *data G_GNUC_UNUSED) return testCompareOutputLit(exp, NULL, argv); } +static int testIOThreadAdd(const void *data G_GNUC_UNUSED) +{ + const char *const argv[] = { VIRSH_CUSTOM, "iothreadinfo --domain fc4;\ + iothreadadd --domain fc4 --id 6;\ + iothreadinfo --domain fc4", NULL}; + const char *exp = "\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 2 0\n\ + 4 0\n\ +\n\ +\n\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 2 0\n\ + 4 0\n\ + 6 0\n\ +\n"; + return testCompareOutputLit(exp, NULL, argv); +} + +static int testIOThreadDel(const void *data G_GNUC_UNUSED) +{ + const char *const argv[] = { VIRSH_CUSTOM, "iothreadinfo --domain fc4;\ + iothreaddel --domain fc4 --id 2;\ + iothreadinfo --domain fc4", NULL}; + const char *exp = "\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 2 0\n\ + 4 0\n\ +\n\ +\n\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 4 0\n\ +\n"; + return testCompareOutputLit(exp, NULL, argv); +} + +static int testIOThreadSet(const void *data G_GNUC_UNUSED) +{ + const char *const argv[] = { VIRSH_CUSTOM, "domstats --domain fc4;\ + iothreadset --domain fc4\ + --id 2 --poll-max-ns 100\ + --poll-shrink 10 --poll-grow 10;\ + domstats --domain fc4", NULL}; + const char *exp = "\ +Domain: 'fc4'\n\ + state.state" EQUAL "1\n\ + state.reason" EQUAL "0\n\ + iothread.count" EQUAL "2\n\ + iothread.2.poll-max-ns" EQUAL "32768\n\ + iothread.2.poll-grow" EQUAL "0\n\ + iothread.2.poll-shrink" EQUAL "0\n\ + iothread.4.poll-max-ns" EQUAL "32768\n\ + iothread.4.poll-grow" EQUAL "0\n\ + iothread.4.poll-shrink" EQUAL "0\n\n\ +\n\ +Domain: 'fc4'\n\ + state.state" EQUAL "1\n\ + state.reason" EQUAL "0\n\ + iothread.count" EQUAL "2\n\ + iothread.2.poll-max-ns" EQUAL "100\n\ + iothread.2.poll-grow" EQUAL "10\n\ + iothread.2.poll-shrink" EQUAL "10\n\ + iothread.4.poll-max-ns" EQUAL "32768\n\ + iothread.4.poll-grow" EQUAL "0\n\ + iothread.4.poll-shrink" EQUAL "0\n\n"; + return testCompareOutputLit(exp, NULL, argv); +} + +static int testIOThreadPin(const void *data G_GNUC_UNUSED) +{ + const char *const argv[] = { VIRSH_CUSTOM, + "iothreadadd --domain fc5 --id 2;\ + iothreadinfo --domain fc5;\ + iothreadpin --domain fc5 --iothread 2\ + --cpulist 0;\ + iothreadinfo --domain fc5", NULL}; + const char *exp = "\n\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 2 0-3\n\ +\n\ +\n\ + IOThread ID CPU Affinity\n\ +-----------------------------\n\ + 2 0\n\ +\n"; + return testCompareOutputLit(exp, NULL, argv); +} + struct testInfo { const char *const *argv; const char *result; @@ -438,6 +532,22 @@ mymain(void) testCompareSetBlkioParameters, NULL) != 0) ret = -1; + if (virTestRun("virsh iothreadadd", + testIOThreadAdd, NULL) != 0) + ret = -1; + + if (virTestRun("virsh iothreaddel", + testIOThreadDel, NULL) != 0) + ret = -1; + + if (virTestRun("virsh iothreadset", + testIOThreadSet, NULL) != 0) + ret = -1; + + if (virTestRun("virsh iothreadpin", + testIOThreadPin, NULL) != 0) + ret = -1; + /* It's a bit awkward listing result before argument, but that's a * limitation of C99 vararg macros. */ # define DO_TEST(i, result, ...) \ -- 2.33.0

On 9/15/21 5:30 PM, Luke Yue wrote:
v4: - Rebase to current master branch - Add the forgotten virObjectUnlock(vm) in PATCH 09/11 - Refine tests
CI link: https://gitlab.com/lukedyue/libvirt/-/pipelines/371315349
Luke Yue (11): domain_driver.c: Introduce and use virDomainDriverAddIOThreadCheck() test_driver: Introduce testIOThreadInfo and generate IOThread infos test_driver: Implement virDomainAddIOThread test_driver: Implement virDomainDelIOThread domain_driver.c: Introduce and use virDomainDriverGetIOThreadsConfig() test_driver: Implement virDomainGetIOThreadInfo test_driver: Implement virDomainPinIOThread test_driver: Implement testDomainSetIOThreadParams test_driver: Implement virConnectGetAllDomainStats test_driver: Introduce testDomainGetStatsIOThread tests: Test IOThread related functions for test driver
examples/xml/test/testdomfc4.xml | 5 + src/hypervisor/domain_driver.c | 132 ++++++++ src/hypervisor/domain_driver.h | 10 + src/libvirt_private.syms | 3 + src/qemu/qemu_driver.c | 113 +------ src/test/test_driver.c | 516 +++++++++++++++++++++++++++++++ tests/virshtest.c | 110 +++++++ 7 files changed, 781 insertions(+), 108 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Luke Yue
-
Michal Prívozník