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