From: Boris Fiuczynski <[email protected]>

From: Boris Fiuczynski <[email protected]>

Add tests covering the three cases of qemuProcessGetAllCpuAffinity():
no isolated CPUs, overlapping isolated CPUs, and isolated CPUs that do
not overlap with online CPUs (the hot-plug regression scenario).

Promote the function from static to allow direct testing, and provide
qemuprocessmock.c to replace the three virHostCPU* calls it depends on.

Signed-off-by: Boris Fiuczynski <[email protected]>
---
 src/qemu/qemu_process.c |   2 +-
 src/qemu/qemu_process.h |   2 +
 src/util/virhostcpu.h   |   6 +-
 tests/meson.build       |   1 +
 tests/qemuprocessmock.c |  65 ++++++++++++++++++
 tests/qemuprocesstest.c | 144 ++++++++++++++++++++++++++++++++++++++++
 tests/qemuprocesstest.h |  25 +++++++
 7 files changed, 241 insertions(+), 4 deletions(-)
 create mode 100644 tests/qemuprocessmock.c
 create mode 100644 tests/qemuprocesstest.c
 create mode 100644 tests/qemuprocesstest.h

diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 413ec0622d..aaa9046146 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -2580,7 +2580,7 @@ qemuProcessDetectIOThreadPIDs(virDomainObj *vm,
  *
  * Returns: 0 on success, -1 on error.
  */
-static int
+int
 qemuProcessGetAllCpuAffinity(virBitmap **cpumapRet)
 {
     g_autoptr(virBitmap) isolCpus = NULL;
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
index 2db5186e08..5d8f1f89d5 100644
--- a/src/qemu/qemu_process.h
+++ b/src/qemu/qemu_process.h
@@ -269,3 +269,5 @@ void qemuProcessHandleNbdkitExit(qemuNbdkitProcess *nbdkit,
 int qemuPrepareNVRAM(virQEMUDriver *driver,
                      virDomainDef *def,
                      bool reset_nvram);
+
+int qemuProcessGetAllCpuAffinity(virBitmap **cpumapRet);
diff --git a/src/util/virhostcpu.h b/src/util/virhostcpu.h
index 4df126dcc4..7aa02b35aa 100644
--- a/src/util/virhostcpu.h
+++ b/src/util/virhostcpu.h
@@ -38,11 +38,11 @@ int virHostCPUGetStats(int cpuNum,
                        virNodeCPUStatsPtr params,
                        int *nparams);
 
-bool virHostCPUHasBitmap(void);
+bool virHostCPUHasBitmap(void) ATTRIBUTE_MOCKABLE;
 virBitmap *virHostCPUGetPresentBitmap(void);
-virBitmap *virHostCPUGetOnlineBitmap(void);
+virBitmap *virHostCPUGetOnlineBitmap(void) ATTRIBUTE_MOCKABLE;
 virBitmap *virHostCPUGetAvailableCPUsBitmap(void);
-int virHostCPUGetIsolated(virBitmap **isolated);
+int virHostCPUGetIsolated(virBitmap **isolated) ATTRIBUTE_MOCKABLE;
 
 int virHostCPUGetCount(void);
 int virHostCPUGetThreadsPerSubcore(virArch arch) ATTRIBUTE_MOCKABLE;
diff --git a/tests/meson.build b/tests/meson.build
index ea50f89fb5..ddaee05517 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -475,6 +475,7 @@ if conf.has('WITH_QEMU')
     { 'name': 'qemumigparamstest', 'link_with': [ test_qemu_driver_lib, 
test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
     { 'name': 'qemumigrationcookiexmltest', 'link_with': [ 
test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole': [ 
test_utils_qemu_lib, test_file_wrapper_lib ] },
     { 'name': 'qemumonitorjsontest', 'link_with': [ test_qemu_driver_lib, 
test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] },
+    { 'name': 'qemuprocesstest', 'sources': [ 'qemuprocesstest.c', 
'qemuprocessmock.c' ], 'link_with': [ test_qemu_driver_lib ] },
     { 'name': 'qemusecuritytest', 'sources': [ 'qemusecuritytest.c', 
'qemusecuritymock.c' ], 'link_with': [ test_qemu_driver_lib ], 'link_whole': [ 
test_utils_qemu_lib ] },
     { 'name': 'qemuxmlactivetest', 'link_with': [ test_qemu_driver_lib ], 
'link_whole': [ test_utils_qemu_lib, test_file_wrapper_lib ] },
     { 'name': 'qemuvhostusertest', 'link_with': [ test_qemu_driver_lib ], 
'link_whole': [ test_file_wrapper_lib ] },
diff --git a/tests/qemuprocessmock.c b/tests/qemuprocessmock.c
new file mode 100644
index 0000000000..79c66bab4b
--- /dev/null
+++ b/tests/qemuprocessmock.c
@@ -0,0 +1,65 @@
+/*
+ * qemuprocessmock.c: mocks for qemuprocesstest
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "internal.h"
+#include "virbitmap.h"
+#include "virhostcpu.h"
+#include "qemuprocesstest.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+static const char *mock_online_cpus;
+static const char *mock_isolated_cpus;
+static bool mock_has_bitmap = true;
+
+void
+qemuProcessMockSetCpus(const char *online,
+                        const char *isolated,
+                        bool hasBitmap)
+{
+    mock_online_cpus = online;
+    mock_isolated_cpus = isolated;
+    mock_has_bitmap = hasBitmap;
+}
+
+bool
+virHostCPUHasBitmap(void)
+{
+    return mock_has_bitmap;
+}
+
+virBitmap *
+virHostCPUGetOnlineBitmap(void)
+{
+    if (!mock_online_cpus)
+        return NULL;
+    return virBitmapParseUnlimited(mock_online_cpus);
+}
+
+int
+virHostCPUGetIsolated(virBitmap **isolated)
+{
+    *isolated = NULL;
+    if (!mock_isolated_cpus)
+        return 0;
+    if (!(*isolated = virBitmapParseUnlimitedAllowEmpty(mock_isolated_cpus)))
+        return -1;
+    return 0;
+}
diff --git a/tests/qemuprocesstest.c b/tests/qemuprocesstest.c
new file mode 100644
index 0000000000..37ed5756a1
--- /dev/null
+++ b/tests/qemuprocesstest.c
@@ -0,0 +1,144 @@
+/*
+ * qemuprocesstest.c: tests for qemu_process.c internals
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "testutils.h"
+#include "virbitmap.h"
+#include "qemu/qemu_process.h"
+#include "qemuprocesstest.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+/*
+ * Case 1: no isolated CPUs (sysfs file absent).
+ *
+ * qemuProcessGetAllCpuAffinity() must return NULL, indicating that
+ * sched_setaffinity() should NOT be called so that CPUs hot-plugged
+ * after startup remain usable.
+ */
+static int
+testGetAllCpuAffinityNoIsolated(const void *opaque G_GNUC_UNUSED)
+{
+    g_autoptr(virBitmap) result = NULL;
+
+    qemuProcessMockSetCpus("0-7", NULL, true);
+
+    if (qemuProcessGetAllCpuAffinity(&result) < 0)
+        return -1;
+
+    if (result) {
+        g_autofree char *str = virBitmapFormat(result);
+        fprintf(stderr,
+                "expected NULL (no sched_setaffinity) when no isolated CPUs, "
+                "got '%s'\n", str);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Case 2: isolated CPUs that DO overlap with online CPUs.
+ *
+ * qemuProcessGetAllCpuAffinity() must return the online set minus the
+ * isolated set so that QEMU is pinned away from the isolated cores.
+ */
+static int
+testGetAllCpuAffinityOverlapping(const void *opaque G_GNUC_UNUSED)
+{
+    g_autoptr(virBitmap) result = NULL;
+    g_autofree char *str = NULL;
+
+    qemuProcessMockSetCpus("0-7", "6-7", true);
+
+    if (qemuProcessGetAllCpuAffinity(&result) < 0)
+        return -1;
+
+    if (!result) {
+        fprintf(stderr,
+                "expected non-NULL affinity map when isolated CPUs overlap "
+                "online CPUs\n");
+        return -1;
+    }
+
+    str = virBitmapFormat(result);
+    if (STRNEQ(str, "0-5")) {
+        fprintf(stderr, "expected '0-5', got '%s'\n", str);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Case 3: isolated CPUs that do NOT overlap with online CPUs.
+ *
+ * This is the hot-plug regression scenario: CPUs 8-9 are listed as
+ * isolated but are not currently online (online = 0-7).  Before the
+ * fix, the function returned the full online bitmap, causing
+ * sched_setaffinity() to pin QEMU to CPUs 0-7 at startup and thereby
+ * preventing any subsequently hot-plugged CPU from being used.
+ * After the fix it must return NULL.
+ */
+static int
+testGetAllCpuAffinityNonOverlapping(const void *opaque G_GNUC_UNUSED)
+{
+    g_autoptr(virBitmap) result = NULL;
+
+    qemuProcessMockSetCpus("0-7", "8-9", true);
+
+    if (qemuProcessGetAllCpuAffinity(&result) < 0)
+        return -1;
+
+    if (result) {
+        g_autofree char *str = virBitmapFormat(result);
+        fprintf(stderr,
+                "expected NULL when isolated CPUs do not overlap online CPUs "
+                "(hot-plug regression), got '%s'\n", str);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+#define DO_TEST(desc, func) \
+    do { \
+        if (virTestRun(desc, func, NULL) < 0) \
+            ret = -1; \
+    } while (0)
+
+    DO_TEST("GetAllCpuAffinity: no isolated CPUs -> NULL",
+            testGetAllCpuAffinityNoIsolated);
+    DO_TEST("GetAllCpuAffinity: overlapping isolated CPUs -> online minus 
isolated",
+            testGetAllCpuAffinityOverlapping);
+    DO_TEST("GetAllCpuAffinity: non-overlapping isolated CPUs -> NULL 
(hot-plug fix)",
+            testGetAllCpuAffinityNonOverlapping);
+
+    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN(mymain)
diff --git a/tests/qemuprocesstest.h b/tests/qemuprocesstest.h
new file mode 100644
index 0000000000..02af5c0dc5
--- /dev/null
+++ b/tests/qemuprocesstest.h
@@ -0,0 +1,25 @@
+/*
+ * qemuprocesstest.h: shared declarations for qemuprocesstest
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "internal.h"
+
+void qemuProcessMockSetCpus(const char *online,
+                             const char *isolated,
+                             bool hasBitmap) ATTRIBUTE_MOCKABLE;
-- 
2.55.0

Reply via email to