1 /* 2 * QTest testcase for CPU plugging 3 * 4 * Copyright (c) 2015 SUSE Linux GmbH 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 12 #include "qemu-common.h" 13 #include "libqtest-single.h" 14 #include "qapi/qmp/qdict.h" 15 #include "qapi/qmp/qlist.h" 16 17 struct PlugTestData { 18 char *machine; 19 const char *cpu_model; 20 char *device_model; 21 unsigned sockets; 22 unsigned cores; 23 unsigned threads; 24 unsigned maxcpus; 25 }; 26 typedef struct PlugTestData PlugTestData; 27 28 static void test_plug_with_cpu_add(gconstpointer data) 29 { 30 const PlugTestData *s = data; 31 char *args; 32 QDict *response; 33 unsigned int i; 34 35 args = g_strdup_printf("-machine %s -cpu %s " 36 "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", 37 s->machine, s->cpu_model, 38 s->sockets, s->cores, s->threads, s->maxcpus); 39 qtest_start(args); 40 41 for (i = 1; i < s->maxcpus; i++) { 42 response = qmp("{ 'execute': 'cpu-add'," 43 " 'arguments': { 'id': %d } }", i); 44 g_assert(response); 45 g_assert(!qdict_haskey(response, "error")); 46 qobject_unref(response); 47 } 48 49 qtest_end(); 50 g_free(args); 51 } 52 53 static void test_plug_without_cpu_add(gconstpointer data) 54 { 55 const PlugTestData *s = data; 56 char *args; 57 QDict *response; 58 59 args = g_strdup_printf("-machine %s -cpu %s " 60 "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", 61 s->machine, s->cpu_model, 62 s->sockets, s->cores, s->threads, s->maxcpus); 63 qtest_start(args); 64 65 response = qmp("{ 'execute': 'cpu-add'," 66 " 'arguments': { 'id': %d } }", 67 s->sockets * s->cores * s->threads); 68 g_assert(response); 69 g_assert(qdict_haskey(response, "error")); 70 qobject_unref(response); 71 72 qtest_end(); 73 g_free(args); 74 } 75 76 static void test_plug_with_device_add(gconstpointer data) 77 { 78 const PlugTestData *td = data; 79 char *args; 80 QTestState *qts; 81 QDict *resp; 82 QList *cpus; 83 QObject *e; 84 int hotplugged = 0; 85 86 args = g_strdup_printf("-machine %s -cpu %s " 87 "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", 88 td->machine, td->cpu_model, 89 td->sockets, td->cores, td->threads, td->maxcpus); 90 qts = qtest_init(args); 91 92 resp = qtest_qmp(qts, "{ 'execute': 'query-hotpluggable-cpus'}"); 93 g_assert(qdict_haskey(resp, "return")); 94 cpus = qdict_get_qlist(resp, "return"); 95 g_assert(cpus); 96 97 while ((e = qlist_pop(cpus))) { 98 const QDict *cpu, *props; 99 100 cpu = qobject_to(QDict, e); 101 if (qdict_haskey(cpu, "qom-path")) { 102 qobject_unref(e); 103 continue; 104 } 105 106 g_assert(qdict_haskey(cpu, "props")); 107 props = qdict_get_qdict(cpu, "props"); 108 109 qtest_qmp_device_add_qdict(qts, td->device_model, props); 110 hotplugged++; 111 qobject_unref(e); 112 } 113 114 /* make sure that there were hotplugged CPUs */ 115 g_assert(hotplugged); 116 qobject_unref(resp); 117 qtest_quit(qts); 118 g_free(args); 119 } 120 121 static void test_data_free(gpointer data) 122 { 123 PlugTestData *pc = data; 124 125 g_free(pc->machine); 126 g_free(pc->device_model); 127 g_free(pc); 128 } 129 130 static void add_pc_test_case(const char *mname) 131 { 132 char *path; 133 PlugTestData *data; 134 135 if (!g_str_has_prefix(mname, "pc-")) { 136 return; 137 } 138 data = g_new(PlugTestData, 1); 139 data->machine = g_strdup(mname); 140 data->cpu_model = "Haswell"; /* 1.3+ theoretically */ 141 data->device_model = g_strdup_printf("%s-%s-cpu", data->cpu_model, 142 qtest_get_arch()); 143 data->sockets = 1; 144 data->cores = 3; 145 data->threads = 2; 146 data->maxcpus = data->sockets * data->cores * data->threads; 147 if (g_str_has_suffix(mname, "-1.4") || 148 (strcmp(mname, "pc-1.3") == 0) || 149 (strcmp(mname, "pc-1.2") == 0) || 150 (strcmp(mname, "pc-1.1") == 0) || 151 (strcmp(mname, "pc-1.0") == 0)) { 152 path = g_strdup_printf("cpu-plug/%s/init/%ux%ux%u&maxcpus=%u", 153 mname, data->sockets, data->cores, 154 data->threads, data->maxcpus); 155 qtest_add_data_func_full(path, data, test_plug_without_cpu_add, 156 test_data_free); 157 g_free(path); 158 } else { 159 PlugTestData *data2 = g_memdup(data, sizeof(PlugTestData)); 160 161 data2->machine = g_strdup(data->machine); 162 data2->device_model = g_strdup(data->device_model); 163 164 path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", 165 mname, data->sockets, data->cores, 166 data->threads, data->maxcpus); 167 qtest_add_data_func_full(path, data, test_plug_with_cpu_add, 168 test_data_free); 169 g_free(path); 170 path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", 171 mname, data2->sockets, data2->cores, 172 data2->threads, data2->maxcpus); 173 qtest_add_data_func_full(path, data2, test_plug_with_device_add, 174 test_data_free); 175 g_free(path); 176 } 177 } 178 179 static void add_pseries_test_case(const char *mname) 180 { 181 char *path; 182 PlugTestData *data; 183 184 if (!g_str_has_prefix(mname, "pseries-") || 185 (g_str_has_prefix(mname, "pseries-2.") && atoi(&mname[10]) < 7)) { 186 return; 187 } 188 data = g_new(PlugTestData, 1); 189 data->machine = g_strdup(mname); 190 data->cpu_model = "power8_v2.0"; 191 data->device_model = g_strdup("power8_v2.0-spapr-cpu-core"); 192 data->sockets = 2; 193 data->cores = 3; 194 data->threads = 1; 195 data->maxcpus = data->sockets * data->cores * data->threads; 196 197 path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", 198 mname, data->sockets, data->cores, 199 data->threads, data->maxcpus); 200 qtest_add_data_func_full(path, data, test_plug_with_device_add, 201 test_data_free); 202 g_free(path); 203 } 204 205 static void add_s390x_test_case(const char *mname) 206 { 207 char *path; 208 PlugTestData *data, *data2; 209 210 if (!g_str_has_prefix(mname, "s390-ccw-virtio-")) { 211 return; 212 } 213 214 data = g_new(PlugTestData, 1); 215 data->machine = g_strdup(mname); 216 data->cpu_model = "qemu"; 217 data->device_model = g_strdup("qemu-s390x-cpu"); 218 data->sockets = 1; 219 data->cores = 3; 220 data->threads = 1; 221 data->maxcpus = data->sockets * data->cores * data->threads; 222 223 data2 = g_memdup(data, sizeof(PlugTestData)); 224 data2->machine = g_strdup(data->machine); 225 data2->device_model = g_strdup(data->device_model); 226 227 path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", 228 mname, data->sockets, data->cores, 229 data->threads, data->maxcpus); 230 qtest_add_data_func_full(path, data, test_plug_with_cpu_add, 231 test_data_free); 232 g_free(path); 233 234 path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", 235 mname, data2->sockets, data2->cores, 236 data2->threads, data2->maxcpus); 237 qtest_add_data_func_full(path, data2, test_plug_with_device_add, 238 test_data_free); 239 g_free(path); 240 } 241 242 int main(int argc, char **argv) 243 { 244 const char *arch = qtest_get_arch(); 245 246 g_test_init(&argc, &argv, NULL); 247 248 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 249 qtest_cb_for_every_machine(add_pc_test_case, g_test_quick()); 250 } else if (g_str_equal(arch, "ppc64")) { 251 qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); 252 } else if (g_str_equal(arch, "s390x")) { 253 qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); 254 } 255 256 return g_test_run(); 257 } 258