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 QDict *response; 127 char driver[32]; 128 snprintf(driver, sizeof(driver), "virtio-blk-%s", 129 qvirtio_get_dev_type()); 130 131 response = qtest_qmp(qts, "{'execute': 'device_add'," 132 " 'arguments': {" 133 " 'driver': %s," 134 " 'drive': 'drive0'," 135 " 'id': 'dev0'" 136 "}}", driver); 137 g_assert(response); 138 g_assert(qdict_haskey(response, "return")); 139 qobject_unref(response); 140 } 141 142 static void device_del(QTestState *qts, bool and_reset) 143 { 144 QDict *response; 145 146 response = qtest_qmp(qts, "{'execute': 'device_del'," 147 " 'arguments': { 'id': 'dev0' } }"); 148 g_assert(response); 149 g_assert(qdict_haskey(response, "return")); 150 qobject_unref(response); 151 152 if (and_reset) { 153 response = qtest_qmp(qts, "{'execute': 'system_reset' }"); 154 g_assert(response); 155 g_assert(qdict_haskey(response, "return")); 156 qobject_unref(response); 157 } 158 159 qtest_qmp_eventwait(qts, "DEVICE_DELETED"); 160 } 161 162 static void test_drive_without_dev(void) 163 { 164 QTestState *qts; 165 166 /* Start with an empty drive */ 167 qts = qtest_init("-drive if=none,id=drive0"); 168 169 /* Delete the drive */ 170 drive_del(qts); 171 172 /* Ensure re-adding the drive works - there should be no duplicate ID error 173 * because the old drive must be gone. 174 */ 175 drive_add(qts); 176 177 qtest_quit(qts); 178 } 179 180 static void test_after_failed_device_add(void) 181 { 182 char driver[32]; 183 QDict *response; 184 QTestState *qts; 185 186 snprintf(driver, sizeof(driver), "virtio-blk-%s", 187 qvirtio_get_dev_type()); 188 189 qts = qtest_init("-drive if=none,id=drive0"); 190 191 /* Make device_add fail. If this leaks the virtio-blk device then a 192 * reference to drive0 will also be held (via qdev properties). 193 */ 194 response = qtest_qmp(qts, "{'execute': 'device_add'," 195 " 'arguments': {" 196 " 'driver': %s," 197 " 'drive': 'drive0'" 198 "}}", driver); 199 g_assert(response); 200 qmp_expect_error_and_unref(response, "GenericError"); 201 202 /* Delete the drive */ 203 drive_del(qts); 204 205 /* Try to re-add the drive. This fails with duplicate IDs if a leaked 206 * virtio-blk device exists that holds a reference to the old drive0. 207 */ 208 drive_add(qts); 209 210 qtest_quit(qts); 211 } 212 213 static void test_drive_del_device_del(void) 214 { 215 QTestState *qts; 216 217 /* Start with a drive used by a device that unplugs instantaneously */ 218 qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," 219 "file.read-zeroes=on,format=raw" 220 " -device virtio-scsi-%s" 221 " -device scsi-hd,drive=drive0,id=dev0", 222 qvirtio_get_dev_type()); 223 224 /* 225 * Delete the drive, and then the device 226 * Doing it in this order takes notoriously tricky special paths 227 */ 228 drive_del(qts); 229 device_del(qts, false); 230 g_assert(!has_drive(qts)); 231 232 qtest_quit(qts); 233 } 234 235 static void test_cli_device_del(void) 236 { 237 QTestState *qts; 238 const char *arch = qtest_get_arch(); 239 const char *machine_addition = ""; 240 241 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 242 machine_addition = "-machine pc"; 243 } 244 245 /* 246 * -drive/-device and device_del. Start with a drive used by a 247 * device that unplugs after reset. 248 */ 249 qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://," 250 "file.read-zeroes=on,format=raw" 251 " -device virtio-blk-%s,drive=drive0,id=dev0", 252 machine_addition, 253 qvirtio_get_dev_type()); 254 255 device_del(qts, true); 256 g_assert(!has_drive(qts)); 257 258 qtest_quit(qts); 259 } 260 261 static void test_empty_device_del(void) 262 { 263 QTestState *qts; 264 265 /* device_del with no drive plugged. */ 266 qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0", 267 qvirtio_get_dev_type()); 268 269 device_del(qts, false); 270 qtest_quit(qts); 271 } 272 273 static void test_device_add_and_del(void) 274 { 275 QTestState *qts; 276 const char *arch = qtest_get_arch(); 277 const char *machine_addition = ""; 278 279 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 280 machine_addition = "-machine pc"; 281 } 282 283 /* 284 * -drive/device_add and device_del. Start with a drive used by a 285 * device that unplugs after reset. 286 */ 287 qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://," 288 "file.read-zeroes=on,format=raw", machine_addition); 289 290 device_add(qts); 291 device_del(qts, true); 292 g_assert(!has_drive(qts)); 293 294 qtest_quit(qts); 295 } 296 297 static void test_drive_add_device_add_and_del(void) 298 { 299 QTestState *qts; 300 const char *arch = qtest_get_arch(); 301 const char *machine_addition = ""; 302 303 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 304 machine_addition = "-machine pc"; 305 } 306 307 qts = qtest_init(machine_addition); 308 309 /* 310 * drive_add/device_add and device_del. The drive is used by a 311 * device that unplugs after reset. 312 */ 313 drive_add_with_media(qts); 314 device_add(qts); 315 device_del(qts, true); 316 g_assert(!has_drive(qts)); 317 318 qtest_quit(qts); 319 } 320 321 static void test_blockdev_add_device_add_and_del(void) 322 { 323 QTestState *qts; 324 const char *arch = qtest_get_arch(); 325 const char *machine_addition = ""; 326 327 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 328 machine_addition = "-machine pc"; 329 } 330 331 qts = qtest_init(machine_addition); 332 333 /* 334 * blockdev_add/device_add and device_del. The it drive is used by a 335 * device that unplugs after reset, but it doesn't go away. 336 */ 337 blockdev_add_with_media(qts); 338 device_add(qts); 339 device_del(qts, true); 340 g_assert(has_blockdev(qts)); 341 342 qtest_quit(qts); 343 } 344 345 int main(int argc, char **argv) 346 { 347 g_test_init(&argc, &argv, NULL); 348 349 qtest_add_func("/drive_del/without-dev", test_drive_without_dev); 350 351 if (qvirtio_get_dev_type() != NULL) { 352 qtest_add_func("/drive_del/after_failed_device_add", 353 test_after_failed_device_add); 354 qtest_add_func("/drive_del/drive_del_device_del", 355 test_drive_del_device_del); 356 qtest_add_func("/device_del/drive/cli_device", 357 test_cli_device_del); 358 qtest_add_func("/device_del/drive/device_add", 359 test_device_add_and_del); 360 qtest_add_func("/device_del/drive/drive_add_device_add", 361 test_drive_add_device_add_and_del); 362 qtest_add_func("/device_del/empty", 363 test_empty_device_del); 364 qtest_add_func("/device_del/blockdev", 365 test_blockdev_add_device_add_and_del); 366 } 367 368 return g_test_run(); 369 } 370