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 "libqos/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 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("-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 qvirtio_get_dev_type()); 247 248 device_del(qts, true); 249 g_assert(!has_drive(qts)); 250 251 qtest_quit(qts); 252 } 253 254 static void test_empty_device_del(void) 255 { 256 QTestState *qts; 257 258 /* device_del with no drive plugged. */ 259 qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0", 260 qvirtio_get_dev_type()); 261 262 device_del(qts, false); 263 qtest_quit(qts); 264 } 265 266 static void test_device_add_and_del(void) 267 { 268 QTestState *qts; 269 270 /* 271 * -drive/device_add and device_del. Start with a drive used by a 272 * device that unplugs after reset. 273 */ 274 qts = qtest_init("-drive if=none,id=drive0,file=null-co://," 275 "file.read-zeroes=on,format=raw"); 276 277 device_add(qts); 278 device_del(qts, true); 279 g_assert(!has_drive(qts)); 280 281 qtest_quit(qts); 282 } 283 284 static void test_drive_add_device_add_and_del(void) 285 { 286 QTestState *qts; 287 288 qts = qtest_init(""); 289 290 /* 291 * drive_add/device_add and device_del. The drive is used by a 292 * device that unplugs after reset. 293 */ 294 drive_add_with_media(qts); 295 device_add(qts); 296 device_del(qts, true); 297 g_assert(!has_drive(qts)); 298 299 qtest_quit(qts); 300 } 301 302 static void test_blockdev_add_device_add_and_del(void) 303 { 304 QTestState *qts; 305 306 qts = qtest_init(""); 307 308 /* 309 * blockdev_add/device_add and device_del. The it drive is used by a 310 * device that unplugs after reset, but it doesn't go away. 311 */ 312 blockdev_add_with_media(qts); 313 device_add(qts); 314 device_del(qts, true); 315 g_assert(has_blockdev(qts)); 316 317 qtest_quit(qts); 318 } 319 320 int main(int argc, char **argv) 321 { 322 g_test_init(&argc, &argv, NULL); 323 324 qtest_add_func("/drive_del/without-dev", test_drive_without_dev); 325 326 if (qvirtio_get_dev_type() != NULL) { 327 qtest_add_func("/drive_del/after_failed_device_add", 328 test_after_failed_device_add); 329 qtest_add_func("/drive_del/drive_del_device_del", 330 test_drive_del_device_del); 331 qtest_add_func("/device_del/drive/cli_device", 332 test_cli_device_del); 333 qtest_add_func("/device_del/drive/device_add", 334 test_device_add_and_del); 335 qtest_add_func("/device_del/drive/drive_add_device_add", 336 test_drive_add_device_add_and_del); 337 qtest_add_func("/device_del/empty", 338 test_empty_device_del); 339 qtest_add_func("/device_del/blockdev", 340 test_blockdev_add_device_add_and_del); 341 } 342 343 return g_test_run(); 344 } 345