]>
Commit | Line | Data |
---|---|---|
43cd2098 | 1 | /* |
e2f3f221 | 2 | * blockdev.c test cases |
43cd2098 | 3 | * |
e2f3f221 | 4 | * Copyright (C) 2013-2014 Red Hat Inc. |
43cd2098 SH |
5 | * |
6 | * Authors: | |
7 | * Stefan Hajnoczi <stefanha@redhat.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | */ | |
12 | ||
681c28a3 | 13 | #include "qemu/osdep.h" |
907b5105 | 14 | #include "libqtest.h" |
2f84a92e | 15 | #include "libqos/virtio.h" |
452fcdbc | 16 | #include "qapi/qmp/qdict.h" |
9a613ddc PB |
17 | #include "qapi/qmp/qlist.h" |
18 | ||
184c16d1 FR |
19 | static const char *qvirtio_get_dev_type(void); |
20 | ||
d8a18da5 | 21 | static bool look_for_drive0(QTestState *qts, const char *command, const char *key) |
9a613ddc PB |
22 | { |
23 | QDict *response; | |
24 | QList *ret; | |
25 | QListEntry *entry; | |
26 | bool found; | |
27 | ||
d8a18da5 | 28 | response = qtest_qmp(qts, "{'execute': %s}", command); |
9a613ddc PB |
29 | g_assert(response && qdict_haskey(response, "return")); |
30 | ret = qdict_get_qlist(response, "return"); | |
31 | ||
32 | found = false; | |
33 | QLIST_FOREACH_ENTRY(ret, entry) { | |
34 | QDict *entry_dict = qobject_to(QDict, entry->value); | |
d8a18da5 | 35 | if (!strcmp(qdict_get_str(entry_dict, key), "drive0")) { |
9a613ddc PB |
36 | found = true; |
37 | break; | |
38 | } | |
39 | } | |
40 | ||
41 | qobject_unref(response); | |
42 | return found; | |
43 | } | |
43cd2098 | 44 | |
184c16d1 FR |
45 | /* |
46 | * This covers the possible absence of a device due to QEMU build | |
47 | * options. | |
48 | */ | |
49 | static bool has_device_builtin(const char *dev) | |
50 | { | |
51 | gchar *device = g_strdup_printf("%s-%s", dev, qvirtio_get_dev_type()); | |
52 | bool rc = qtest_has_device(device); | |
53 | ||
54 | g_free(device); | |
55 | return rc; | |
56 | } | |
57 | ||
d8a18da5 PB |
58 | static bool has_drive(QTestState *qts) |
59 | { | |
60 | return look_for_drive0(qts, "query-block", "device"); | |
61 | } | |
62 | ||
63 | static bool has_blockdev(QTestState *qts) | |
64 | { | |
65 | return look_for_drive0(qts, "query-named-block-nodes", "node-name"); | |
66 | } | |
67 | ||
68 | static void blockdev_add_with_media(QTestState *qts) | |
69 | { | |
70 | QDict *response; | |
71 | ||
72 | response = qtest_qmp(qts, | |
73 | "{ 'execute': 'blockdev-add'," | |
74 | " 'arguments': {" | |
75 | " 'driver': 'raw'," | |
76 | " 'node-name': 'drive0'," | |
77 | " 'file': {" | |
78 | " 'driver': 'null-co'," | |
79 | " 'read-zeroes': true" | |
80 | " }" | |
81 | " }" | |
82 | "}"); | |
83 | ||
84 | g_assert(response); | |
85 | g_assert(qdict_haskey(response, "return")); | |
86 | qobject_unref(response); | |
87 | g_assert(has_blockdev(qts)); | |
88 | } | |
89 | ||
a771729c | 90 | static void drive_add(QTestState *qts) |
e2f3f221 | 91 | { |
a771729c | 92 | char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0"); |
e2f3f221 | 93 | |
5fb48d96 | 94 | g_assert_cmpstr(resp, ==, "OK\r\n"); |
9a613ddc | 95 | g_assert(has_drive(qts)); |
5fb48d96 | 96 | g_free(resp); |
2eea5cd4 MA |
97 | } |
98 | ||
d8a18da5 PB |
99 | static void drive_add_with_media(QTestState *qts) |
100 | { | |
101 | char *resp = qtest_hmp(qts, | |
102 | "drive_add 0 if=none,id=drive0,file=null-co://," | |
103 | "file.read-zeroes=on,format=raw"); | |
104 | ||
105 | g_assert_cmpstr(resp, ==, "OK\r\n"); | |
106 | g_assert(has_drive(qts)); | |
107 | g_free(resp); | |
108 | } | |
109 | ||
a771729c | 110 | static void drive_del(QTestState *qts) |
2eea5cd4 | 111 | { |
9a613ddc | 112 | char *resp; |
e2f3f221 | 113 | |
9a613ddc PB |
114 | g_assert(has_drive(qts)); |
115 | resp = qtest_hmp(qts, "drive_del drive0"); | |
5fb48d96 | 116 | g_assert_cmpstr(resp, ==, ""); |
9a613ddc | 117 | g_assert(!has_drive(qts)); |
5fb48d96 | 118 | g_free(resp); |
2eea5cd4 MA |
119 | } |
120 | ||
d8a18da5 PB |
121 | /* |
122 | * qvirtio_get_dev_type: | |
123 | * Returns: the preferred virtio bus/device type for the current architecture. | |
124 | * TODO: delete this | |
125 | */ | |
126 | static const char *qvirtio_get_dev_type(void) | |
127 | { | |
128 | const char *arch = qtest_get_arch(); | |
129 | ||
130 | if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { | |
131 | return "device"; /* for virtio-mmio */ | |
132 | } else if (g_str_equal(arch, "s390x")) { | |
133 | return "ccw"; | |
134 | } else { | |
135 | return "pci"; | |
136 | } | |
137 | } | |
138 | ||
139 | static void device_add(QTestState *qts) | |
140 | { | |
5356d752 ML |
141 | g_autofree char *driver = g_strdup_printf("virtio-blk-%s", |
142 | qvirtio_get_dev_type()); | |
143 | QDict *response = | |
144 | qtest_qmp(qts, "{'execute': 'device_add'," | |
d8a18da5 PB |
145 | " 'arguments': {" |
146 | " 'driver': %s," | |
147 | " 'drive': 'drive0'," | |
148 | " 'id': 'dev0'" | |
149 | "}}", driver); | |
150 | g_assert(response); | |
151 | g_assert(qdict_haskey(response, "return")); | |
152 | qobject_unref(response); | |
153 | } | |
154 | ||
155 | static void device_del(QTestState *qts, bool and_reset) | |
767c86d3 MA |
156 | { |
157 | QDict *response; | |
158 | ||
ea42a6c4 | 159 | qtest_qmp_device_del_send(qts, "dev0"); |
bb1a5b97 | 160 | |
d8a18da5 PB |
161 | if (and_reset) { |
162 | response = qtest_qmp(qts, "{'execute': 'system_reset' }"); | |
163 | g_assert(response); | |
164 | g_assert(qdict_haskey(response, "return")); | |
165 | qobject_unref(response); | |
166 | } | |
167 | ||
bb1a5b97 | 168 | qtest_qmp_eventwait(qts, "DEVICE_DELETED"); |
767c86d3 MA |
169 | } |
170 | ||
2eea5cd4 MA |
171 | static void test_drive_without_dev(void) |
172 | { | |
a771729c TH |
173 | QTestState *qts; |
174 | ||
2eea5cd4 | 175 | /* Start with an empty drive */ |
fa5365e8 | 176 | qts = qtest_init("-drive if=none,id=drive0 -M none"); |
2eea5cd4 MA |
177 | |
178 | /* Delete the drive */ | |
a771729c | 179 | drive_del(qts); |
e2f3f221 MA |
180 | |
181 | /* Ensure re-adding the drive works - there should be no duplicate ID error | |
182 | * because the old drive must be gone. | |
183 | */ | |
a771729c | 184 | drive_add(qts); |
e2f3f221 | 185 | |
a771729c | 186 | qtest_quit(qts); |
e2f3f221 MA |
187 | } |
188 | ||
189 | static void test_after_failed_device_add(void) | |
43cd2098 | 190 | { |
83273e84 | 191 | char driver[32]; |
43cd2098 | 192 | QDict *response; |
a771729c | 193 | QTestState *qts; |
43cd2098 | 194 | |
fa5365e8 PB |
195 | if (!has_device_builtin("virtio-blk")) { |
196 | g_test_skip("Device virtio-blk is not available"); | |
197 | return; | |
198 | } | |
199 | ||
83273e84 MA |
200 | snprintf(driver, sizeof(driver), "virtio-blk-%s", |
201 | qvirtio_get_dev_type()); | |
202 | ||
a771729c | 203 | qts = qtest_init("-drive if=none,id=drive0"); |
43cd2098 | 204 | |
2f84a92e | 205 | /* Make device_add fail. If this leaks the virtio-blk device then a |
43cd2098 SH |
206 | * reference to drive0 will also be held (via qdev properties). |
207 | */ | |
a771729c TH |
208 | response = qtest_qmp(qts, "{'execute': 'device_add'," |
209 | " 'arguments': {" | |
210 | " 'driver': %s," | |
211 | " 'drive': 'drive0'" | |
212 | "}}", driver); | |
43cd2098 | 213 | g_assert(response); |
3bc1b8ee | 214 | qmp_expect_error_and_unref(response, "GenericError"); |
43cd2098 SH |
215 | |
216 | /* Delete the drive */ | |
a771729c | 217 | drive_del(qts); |
43cd2098 SH |
218 | |
219 | /* Try to re-add the drive. This fails with duplicate IDs if a leaked | |
2f84a92e | 220 | * virtio-blk device exists that holds a reference to the old drive0. |
43cd2098 | 221 | */ |
a771729c | 222 | drive_add(qts); |
43cd2098 | 223 | |
a771729c | 224 | qtest_quit(qts); |
43cd2098 SH |
225 | } |
226 | ||
767c86d3 MA |
227 | static void test_drive_del_device_del(void) |
228 | { | |
a771729c | 229 | QTestState *qts; |
2f84a92e | 230 | |
184c16d1 FR |
231 | if (!has_device_builtin("virtio-scsi")) { |
232 | g_test_skip("Device virtio-scsi is not available"); | |
233 | return; | |
234 | } | |
235 | ||
767c86d3 | 236 | /* Start with a drive used by a device that unplugs instantaneously */ |
ca1ef1e6 AS |
237 | qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," |
238 | "file.read-zeroes=on,format=raw" | |
a771729c TH |
239 | " -device virtio-scsi-%s" |
240 | " -device scsi-hd,drive=drive0,id=dev0", | |
241 | qvirtio_get_dev_type()); | |
767c86d3 MA |
242 | |
243 | /* | |
244 | * Delete the drive, and then the device | |
245 | * Doing it in this order takes notoriously tricky special paths | |
246 | */ | |
a771729c | 247 | drive_del(qts); |
d8a18da5 | 248 | device_del(qts, false); |
9a613ddc | 249 | g_assert(!has_drive(qts)); |
767c86d3 | 250 | |
a771729c | 251 | qtest_quit(qts); |
767c86d3 MA |
252 | } |
253 | ||
d8a18da5 PB |
254 | static void test_cli_device_del(void) |
255 | { | |
256 | QTestState *qts; | |
7b172333 DDAG |
257 | const char *arch = qtest_get_arch(); |
258 | const char *machine_addition = ""; | |
259 | ||
184c16d1 FR |
260 | if (!has_device_builtin("virtio-blk")) { |
261 | g_test_skip("Device virtio-blk is not available"); | |
262 | return; | |
263 | } | |
264 | ||
7b172333 DDAG |
265 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
266 | machine_addition = "-machine pc"; | |
267 | } | |
d8a18da5 PB |
268 | |
269 | /* | |
270 | * -drive/-device and device_del. Start with a drive used by a | |
271 | * device that unplugs after reset. | |
272 | */ | |
7b172333 | 273 | qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://," |
d8a18da5 PB |
274 | "file.read-zeroes=on,format=raw" |
275 | " -device virtio-blk-%s,drive=drive0,id=dev0", | |
7b172333 | 276 | machine_addition, |
d8a18da5 PB |
277 | qvirtio_get_dev_type()); |
278 | ||
279 | device_del(qts, true); | |
280 | g_assert(!has_drive(qts)); | |
281 | ||
282 | qtest_quit(qts); | |
283 | } | |
284 | ||
eb4440ef ML |
285 | static void test_cli_device_del_q35(void) |
286 | { | |
287 | QTestState *qts; | |
288 | ||
184c16d1 FR |
289 | if (!has_device_builtin("virtio-blk")) { |
290 | g_test_skip("Device virtio-blk is not available"); | |
291 | return; | |
292 | } | |
293 | ||
eb4440ef ML |
294 | /* |
295 | * -drive/-device and device_del. Start with a drive used by a | |
296 | * device that unplugs after reset. | |
297 | */ | |
298 | qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," | |
299 | "file.read-zeroes=on,format=raw " | |
300 | "-machine q35 -device pcie-root-port,id=p1 " | |
301 | "-device pcie-pci-bridge,bus=p1,id=b1 " | |
302 | "-device virtio-blk-%s,drive=drive0,bus=b1,id=dev0", | |
303 | qvirtio_get_dev_type()); | |
304 | ||
305 | device_del(qts, true); | |
306 | g_assert(!has_drive(qts)); | |
307 | ||
308 | qtest_quit(qts); | |
309 | } | |
310 | ||
d8a18da5 PB |
311 | static void test_empty_device_del(void) |
312 | { | |
313 | QTestState *qts; | |
314 | ||
184c16d1 FR |
315 | if (!has_device_builtin("virtio-scsi")) { |
316 | g_test_skip("Device virtio-scsi is not available"); | |
317 | return; | |
318 | } | |
319 | ||
d8a18da5 PB |
320 | /* device_del with no drive plugged. */ |
321 | qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0", | |
322 | qvirtio_get_dev_type()); | |
323 | ||
324 | device_del(qts, false); | |
325 | qtest_quit(qts); | |
326 | } | |
327 | ||
328 | static void test_device_add_and_del(void) | |
329 | { | |
330 | QTestState *qts; | |
7b172333 DDAG |
331 | const char *arch = qtest_get_arch(); |
332 | const char *machine_addition = ""; | |
333 | ||
184c16d1 FR |
334 | if (!has_device_builtin("virtio-blk")) { |
335 | g_test_skip("Device virtio-blk is not available"); | |
336 | return; | |
337 | } | |
338 | ||
7b172333 DDAG |
339 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
340 | machine_addition = "-machine pc"; | |
341 | } | |
d8a18da5 PB |
342 | |
343 | /* | |
344 | * -drive/device_add and device_del. Start with a drive used by a | |
345 | * device that unplugs after reset. | |
346 | */ | |
7b172333 DDAG |
347 | qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://," |
348 | "file.read-zeroes=on,format=raw", machine_addition); | |
d8a18da5 PB |
349 | |
350 | device_add(qts); | |
351 | device_del(qts, true); | |
352 | g_assert(!has_drive(qts)); | |
353 | ||
354 | qtest_quit(qts); | |
355 | } | |
356 | ||
eb4440ef ML |
357 | static void device_add_q35(QTestState *qts) |
358 | { | |
359 | g_autofree char *driver = g_strdup_printf("virtio-blk-%s", | |
360 | qvirtio_get_dev_type()); | |
361 | QDict *response = | |
362 | qtest_qmp(qts, "{'execute': 'device_add'," | |
363 | " 'arguments': {" | |
364 | " 'driver': %s," | |
365 | " 'drive': 'drive0'," | |
366 | " 'id': 'dev0'," | |
367 | " 'bus': 'b1'" | |
368 | "}}", driver); | |
369 | g_assert(response); | |
370 | g_assert(qdict_haskey(response, "return")); | |
371 | qobject_unref(response); | |
372 | } | |
373 | ||
374 | static void test_device_add_and_del_q35(void) | |
375 | { | |
376 | QTestState *qts; | |
377 | ||
184c16d1 FR |
378 | if (!has_device_builtin("virtio-blk")) { |
379 | g_test_skip("Device virtio-blk is not available"); | |
380 | return; | |
381 | } | |
382 | ||
eb4440ef ML |
383 | /* |
384 | * -drive/device_add and device_del. Start with a drive used by a | |
385 | * device that unplugs after reset. | |
386 | */ | |
387 | qts = qtest_initf("-machine q35 -device pcie-root-port,id=p1 " | |
388 | "-device pcie-pci-bridge,bus=p1,id=b1 " | |
389 | "-drive if=none,id=drive0,file=null-co://," | |
390 | "file.read-zeroes=on,format=raw"); | |
391 | ||
392 | device_add_q35(qts); | |
393 | device_del(qts, true); | |
394 | g_assert(!has_drive(qts)); | |
395 | ||
396 | qtest_quit(qts); | |
397 | } | |
398 | ||
d8a18da5 PB |
399 | static void test_drive_add_device_add_and_del(void) |
400 | { | |
401 | QTestState *qts; | |
7b172333 DDAG |
402 | const char *arch = qtest_get_arch(); |
403 | const char *machine_addition = ""; | |
404 | ||
184c16d1 FR |
405 | if (!has_device_builtin("virtio-blk")) { |
406 | g_test_skip("Device virtio-blk is not available"); | |
407 | return; | |
408 | } | |
409 | ||
7b172333 DDAG |
410 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
411 | machine_addition = "-machine pc"; | |
412 | } | |
d8a18da5 | 413 | |
7b172333 | 414 | qts = qtest_init(machine_addition); |
d8a18da5 PB |
415 | |
416 | /* | |
417 | * drive_add/device_add and device_del. The drive is used by a | |
418 | * device that unplugs after reset. | |
419 | */ | |
420 | drive_add_with_media(qts); | |
421 | device_add(qts); | |
422 | device_del(qts, true); | |
423 | g_assert(!has_drive(qts)); | |
424 | ||
425 | qtest_quit(qts); | |
426 | } | |
427 | ||
eb4440ef ML |
428 | static void test_drive_add_device_add_and_del_q35(void) |
429 | { | |
430 | QTestState *qts; | |
431 | ||
184c16d1 FR |
432 | if (!has_device_builtin("virtio-blk")) { |
433 | g_test_skip("Device virtio-blk is not available"); | |
434 | return; | |
435 | } | |
436 | ||
eb4440ef ML |
437 | qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 " |
438 | "-device pcie-pci-bridge,bus=p1,id=b1"); | |
439 | ||
440 | /* | |
441 | * drive_add/device_add and device_del. The drive is used by a | |
442 | * device that unplugs after reset. | |
443 | */ | |
444 | drive_add_with_media(qts); | |
445 | device_add_q35(qts); | |
446 | device_del(qts, true); | |
447 | g_assert(!has_drive(qts)); | |
448 | ||
449 | qtest_quit(qts); | |
450 | } | |
451 | ||
d8a18da5 PB |
452 | static void test_blockdev_add_device_add_and_del(void) |
453 | { | |
454 | QTestState *qts; | |
7b172333 DDAG |
455 | const char *arch = qtest_get_arch(); |
456 | const char *machine_addition = ""; | |
457 | ||
184c16d1 FR |
458 | if (!has_device_builtin("virtio-blk")) { |
459 | g_test_skip("Device virtio-blk is not available"); | |
460 | return; | |
461 | } | |
462 | ||
7b172333 DDAG |
463 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
464 | machine_addition = "-machine pc"; | |
465 | } | |
d8a18da5 | 466 | |
7b172333 | 467 | qts = qtest_init(machine_addition); |
d8a18da5 PB |
468 | |
469 | /* | |
cb06b3d9 | 470 | * blockdev_add/device_add and device_del. The drive is used by a |
d8a18da5 PB |
471 | * device that unplugs after reset, but it doesn't go away. |
472 | */ | |
473 | blockdev_add_with_media(qts); | |
474 | device_add(qts); | |
475 | device_del(qts, true); | |
476 | g_assert(has_blockdev(qts)); | |
477 | ||
478 | qtest_quit(qts); | |
479 | } | |
480 | ||
eb4440ef ML |
481 | static void test_blockdev_add_device_add_and_del_q35(void) |
482 | { | |
483 | QTestState *qts; | |
484 | ||
184c16d1 FR |
485 | if (!has_device_builtin("virtio-blk")) { |
486 | g_test_skip("Device virtio-blk is not available"); | |
487 | return; | |
488 | } | |
489 | ||
eb4440ef ML |
490 | qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 " |
491 | "-device pcie-pci-bridge,bus=p1,id=b1"); | |
492 | ||
493 | /* | |
494 | * blockdev_add/device_add and device_del. The drive is used by a | |
495 | * device that unplugs after reset, but it doesn't go away. | |
496 | */ | |
497 | blockdev_add_with_media(qts); | |
498 | device_add_q35(qts); | |
499 | device_del(qts, true); | |
500 | g_assert(has_blockdev(qts)); | |
501 | ||
502 | qtest_quit(qts); | |
503 | } | |
504 | ||
43cd2098 SH |
505 | int main(int argc, char **argv) |
506 | { | |
43cd2098 SH |
507 | g_test_init(&argc, &argv, NULL); |
508 | ||
e2f3f221 MA |
509 | qtest_add_func("/drive_del/without-dev", test_drive_without_dev); |
510 | ||
19e3d979 | 511 | if (qvirtio_get_dev_type() != NULL) { |
e2f3f221 MA |
512 | qtest_add_func("/drive_del/after_failed_device_add", |
513 | test_after_failed_device_add); | |
d8a18da5 | 514 | qtest_add_func("/drive_del/drive_del_device_del", |
767c86d3 | 515 | test_drive_del_device_del); |
d8a18da5 PB |
516 | qtest_add_func("/device_del/drive/cli_device", |
517 | test_cli_device_del); | |
518 | qtest_add_func("/device_del/drive/device_add", | |
519 | test_device_add_and_del); | |
520 | qtest_add_func("/device_del/drive/drive_add_device_add", | |
521 | test_drive_add_device_add_and_del); | |
522 | qtest_add_func("/device_del/empty", | |
523 | test_empty_device_del); | |
524 | qtest_add_func("/device_del/blockdev", | |
525 | test_blockdev_add_device_add_and_del); | |
eb4440ef ML |
526 | |
527 | if (qtest_has_machine("q35")) { | |
528 | qtest_add_func("/device_del/drive/cli_device_q35", | |
529 | test_cli_device_del_q35); | |
530 | qtest_add_func("/device_del/drive/device_add_q35", | |
531 | test_device_add_and_del_q35); | |
532 | qtest_add_func("/device_del/drive/drive_add_device_add_q35", | |
533 | test_drive_add_device_add_and_del_q35); | |
534 | qtest_add_func("/device_del/blockdev_q35", | |
535 | test_blockdev_add_device_add_and_del_q35); | |
536 | } | |
e2f3f221 | 537 | } |
43cd2098 SH |
538 | |
539 | return g_test_run(); | |
540 | } |