qemu/tests/qtest/drive_del-test.c
<<
>>
Prefs
   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
  19static const char *qvirtio_get_dev_type(void);
  20
  21static bool look_for_drive0(QTestState *qts, const char *command, const char *key)
  22{
  23    QDict *response;
  24    QList *ret;
  25    QListEntry *entry;
  26    bool found;
  27
  28    response = qtest_qmp(qts, "{'execute': %s}", command);
  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);
  35        if (!strcmp(qdict_get_str(entry_dict, key), "drive0")) {
  36            found = true;
  37            break;
  38        }
  39    }
  40
  41    qobject_unref(response);
  42    return found;
  43}
  44
  45/*
  46 * This covers the possible absence of a device due to QEMU build
  47 * options.
  48 */
  49static 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
  58static bool has_drive(QTestState *qts)
  59{
  60    return look_for_drive0(qts, "query-block", "device");
  61}
  62
  63static bool has_blockdev(QTestState *qts)
  64{
  65    return look_for_drive0(qts, "query-named-block-nodes", "node-name");
  66}
  67
  68static 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
  90static void drive_add(QTestState *qts)
  91{
  92    char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0");
  93
  94    g_assert_cmpstr(resp, ==, "OK\r\n");
  95    g_assert(has_drive(qts));
  96    g_free(resp);
  97}
  98
  99static 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
 110static void drive_del(QTestState *qts)
 111{
 112    char *resp;
 113
 114    g_assert(has_drive(qts));
 115    resp = qtest_hmp(qts, "drive_del drive0");
 116    g_assert_cmpstr(resp, ==, "");
 117    g_assert(!has_drive(qts));
 118    g_free(resp);
 119}
 120
 121/*
 122 * qvirtio_get_dev_type:
 123 * Returns: the preferred virtio bus/device type for the current architecture.
 124 * TODO: delete this
 125 */
 126static 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
 139static void device_add(QTestState *qts)
 140{
 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',"
 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
 155static void device_del(QTestState *qts, bool and_reset)
 156{
 157    QDict *response;
 158
 159    qtest_qmp_device_del_send(qts, "dev0");
 160
 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
 168    qtest_qmp_eventwait(qts, "DEVICE_DELETED");
 169}
 170
 171static void test_drive_without_dev(void)
 172{
 173    QTestState *qts;
 174
 175    /* Start with an empty drive */
 176    qts = qtest_init("-drive if=none,id=drive0");
 177
 178    /* Delete the drive */
 179    drive_del(qts);
 180
 181    /* Ensure re-adding the drive works - there should be no duplicate ID error
 182     * because the old drive must be gone.
 183     */
 184    drive_add(qts);
 185
 186    qtest_quit(qts);
 187}
 188
 189static void test_after_failed_device_add(void)
 190{
 191    char driver[32];
 192    QDict *response;
 193    QTestState *qts;
 194
 195    snprintf(driver, sizeof(driver), "virtio-blk-%s",
 196             qvirtio_get_dev_type());
 197
 198    qts = qtest_init("-drive if=none,id=drive0");
 199
 200    /* Make device_add fail. If this leaks the virtio-blk device then a
 201     * reference to drive0 will also be held (via qdev properties).
 202     */
 203    response = qtest_qmp(qts, "{'execute': 'device_add',"
 204                              " 'arguments': {"
 205                              "   'driver': %s,"
 206                              "   'drive': 'drive0'"
 207                              "}}", driver);
 208    g_assert(response);
 209    qmp_expect_error_and_unref(response, "GenericError");
 210
 211    /* Delete the drive */
 212    drive_del(qts);
 213
 214    /* Try to re-add the drive.  This fails with duplicate IDs if a leaked
 215     * virtio-blk device exists that holds a reference to the old drive0.
 216     */
 217    drive_add(qts);
 218
 219    qtest_quit(qts);
 220}
 221
 222static void test_drive_del_device_del(void)
 223{
 224    QTestState *qts;
 225
 226    if (!has_device_builtin("virtio-scsi")) {
 227        g_test_skip("Device virtio-scsi is not available");
 228        return;
 229    }
 230
 231    /* Start with a drive used by a device that unplugs instantaneously */
 232    qts = qtest_initf("-drive if=none,id=drive0,file=null-co://,"
 233                      "file.read-zeroes=on,format=raw"
 234                      " -device virtio-scsi-%s"
 235                      " -device scsi-hd,drive=drive0,id=dev0",
 236                      qvirtio_get_dev_type());
 237
 238    /*
 239     * Delete the drive, and then the device
 240     * Doing it in this order takes notoriously tricky special paths
 241     */
 242    drive_del(qts);
 243    device_del(qts, false);
 244    g_assert(!has_drive(qts));
 245
 246    qtest_quit(qts);
 247}
 248
 249static void test_cli_device_del(void)
 250{
 251    QTestState *qts;
 252    const char *arch = qtest_get_arch();
 253    const char *machine_addition = "";
 254
 255    if (!has_device_builtin("virtio-blk")) {
 256        g_test_skip("Device virtio-blk is not available");
 257        return;
 258    }
 259
 260    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 261        machine_addition = "-machine pc";
 262    }
 263
 264    /*
 265     * -drive/-device and device_del.  Start with a drive used by a
 266     * device that unplugs after reset.
 267     */
 268    qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://,"
 269                      "file.read-zeroes=on,format=raw"
 270                      " -device virtio-blk-%s,drive=drive0,id=dev0",
 271                      machine_addition,
 272                      qvirtio_get_dev_type());
 273
 274    device_del(qts, true);
 275    g_assert(!has_drive(qts));
 276
 277    qtest_quit(qts);
 278}
 279
 280static void test_cli_device_del_q35(void)
 281{
 282    QTestState *qts;
 283
 284    if (!has_device_builtin("virtio-blk")) {
 285        g_test_skip("Device virtio-blk is not available");
 286        return;
 287    }
 288
 289    /*
 290     * -drive/-device and device_del.  Start with a drive used by a
 291     * device that unplugs after reset.
 292     */
 293    qts = qtest_initf("-drive if=none,id=drive0,file=null-co://,"
 294                      "file.read-zeroes=on,format=raw "
 295                      "-machine q35 -device pcie-root-port,id=p1 "
 296                      "-device pcie-pci-bridge,bus=p1,id=b1 "
 297                      "-device virtio-blk-%s,drive=drive0,bus=b1,id=dev0",
 298                      qvirtio_get_dev_type());
 299
 300    device_del(qts, true);
 301    g_assert(!has_drive(qts));
 302
 303    qtest_quit(qts);
 304}
 305
 306static void test_empty_device_del(void)
 307{
 308    QTestState *qts;
 309
 310    if (!has_device_builtin("virtio-scsi")) {
 311        g_test_skip("Device virtio-scsi is not available");
 312        return;
 313    }
 314
 315    /* device_del with no drive plugged.  */
 316    qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0",
 317                      qvirtio_get_dev_type());
 318
 319    device_del(qts, false);
 320    qtest_quit(qts);
 321}
 322
 323static void test_device_add_and_del(void)
 324{
 325    QTestState *qts;
 326    const char *arch = qtest_get_arch();
 327    const char *machine_addition = "";
 328
 329    if (!has_device_builtin("virtio-blk")) {
 330        g_test_skip("Device virtio-blk is not available");
 331        return;
 332    }
 333
 334    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 335        machine_addition = "-machine pc";
 336    }
 337
 338    /*
 339     * -drive/device_add and device_del.  Start with a drive used by a
 340     * device that unplugs after reset.
 341     */
 342    qts = qtest_initf("%s -drive if=none,id=drive0,file=null-co://,"
 343                     "file.read-zeroes=on,format=raw", machine_addition);
 344
 345    device_add(qts);
 346    device_del(qts, true);
 347    g_assert(!has_drive(qts));
 348
 349    qtest_quit(qts);
 350}
 351
 352static void device_add_q35(QTestState *qts)
 353{
 354    g_autofree char *driver = g_strdup_printf("virtio-blk-%s",
 355                                              qvirtio_get_dev_type());
 356    QDict *response =
 357               qtest_qmp(qts, "{'execute': 'device_add',"
 358                              " 'arguments': {"
 359                              "   'driver': %s,"
 360                              "   'drive': 'drive0',"
 361                              "   'id': 'dev0',"
 362                              "   'bus': 'b1'"
 363                              "}}", driver);
 364    g_assert(response);
 365    g_assert(qdict_haskey(response, "return"));
 366    qobject_unref(response);
 367}
 368
 369static void test_device_add_and_del_q35(void)
 370{
 371    QTestState *qts;
 372
 373    if (!has_device_builtin("virtio-blk")) {
 374        g_test_skip("Device virtio-blk is not available");
 375        return;
 376    }
 377
 378    /*
 379     * -drive/device_add and device_del.  Start with a drive used by a
 380     * device that unplugs after reset.
 381     */
 382    qts = qtest_initf("-machine q35 -device pcie-root-port,id=p1 "
 383                     "-device pcie-pci-bridge,bus=p1,id=b1 "
 384                     "-drive if=none,id=drive0,file=null-co://,"
 385                     "file.read-zeroes=on,format=raw");
 386
 387    device_add_q35(qts);
 388    device_del(qts, true);
 389    g_assert(!has_drive(qts));
 390
 391    qtest_quit(qts);
 392}
 393
 394static void test_drive_add_device_add_and_del(void)
 395{
 396    QTestState *qts;
 397    const char *arch = qtest_get_arch();
 398    const char *machine_addition = "";
 399
 400    if (!has_device_builtin("virtio-blk")) {
 401        g_test_skip("Device virtio-blk is not available");
 402        return;
 403    }
 404
 405    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 406        machine_addition = "-machine pc";
 407    }
 408
 409    qts = qtest_init(machine_addition);
 410
 411    /*
 412     * drive_add/device_add and device_del.  The drive is used by a
 413     * device that unplugs after reset.
 414     */
 415    drive_add_with_media(qts);
 416    device_add(qts);
 417    device_del(qts, true);
 418    g_assert(!has_drive(qts));
 419
 420    qtest_quit(qts);
 421}
 422
 423static void test_drive_add_device_add_and_del_q35(void)
 424{
 425    QTestState *qts;
 426
 427    if (!has_device_builtin("virtio-blk")) {
 428        g_test_skip("Device virtio-blk is not available");
 429        return;
 430    }
 431
 432    qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 "
 433                     "-device pcie-pci-bridge,bus=p1,id=b1");
 434
 435    /*
 436     * drive_add/device_add and device_del.  The drive is used by a
 437     * device that unplugs after reset.
 438     */
 439    drive_add_with_media(qts);
 440    device_add_q35(qts);
 441    device_del(qts, true);
 442    g_assert(!has_drive(qts));
 443
 444    qtest_quit(qts);
 445}
 446
 447static void test_blockdev_add_device_add_and_del(void)
 448{
 449    QTestState *qts;
 450    const char *arch = qtest_get_arch();
 451    const char *machine_addition = "";
 452
 453    if (!has_device_builtin("virtio-blk")) {
 454        g_test_skip("Device virtio-blk is not available");
 455        return;
 456    }
 457
 458    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 459        machine_addition = "-machine pc";
 460    }
 461
 462    qts = qtest_init(machine_addition);
 463
 464    /*
 465     * blockdev_add/device_add and device_del. The drive is used by a
 466     * device that unplugs after reset, but it doesn't go away.
 467     */
 468    blockdev_add_with_media(qts);
 469    device_add(qts);
 470    device_del(qts, true);
 471    g_assert(has_blockdev(qts));
 472
 473    qtest_quit(qts);
 474}
 475
 476static void test_blockdev_add_device_add_and_del_q35(void)
 477{
 478    QTestState *qts;
 479
 480    if (!has_device_builtin("virtio-blk")) {
 481        g_test_skip("Device virtio-blk is not available");
 482        return;
 483    }
 484
 485    qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 "
 486                     "-device pcie-pci-bridge,bus=p1,id=b1");
 487
 488    /*
 489     * blockdev_add/device_add and device_del. The drive is used by a
 490     * device that unplugs after reset, but it doesn't go away.
 491     */
 492    blockdev_add_with_media(qts);
 493    device_add_q35(qts);
 494    device_del(qts, true);
 495    g_assert(has_blockdev(qts));
 496
 497    qtest_quit(qts);
 498}
 499
 500int main(int argc, char **argv)
 501{
 502    g_test_init(&argc, &argv, NULL);
 503
 504    qtest_add_func("/drive_del/without-dev", test_drive_without_dev);
 505
 506    if (qvirtio_get_dev_type() != NULL) {
 507        qtest_add_func("/drive_del/after_failed_device_add",
 508                       test_after_failed_device_add);
 509        qtest_add_func("/drive_del/drive_del_device_del",
 510                       test_drive_del_device_del);
 511        qtest_add_func("/device_del/drive/cli_device",
 512                       test_cli_device_del);
 513        qtest_add_func("/device_del/drive/device_add",
 514                       test_device_add_and_del);
 515        qtest_add_func("/device_del/drive/drive_add_device_add",
 516                       test_drive_add_device_add_and_del);
 517        qtest_add_func("/device_del/empty",
 518                       test_empty_device_del);
 519        qtest_add_func("/device_del/blockdev",
 520                       test_blockdev_add_device_add_and_del);
 521
 522        if (qtest_has_machine("q35")) {
 523            qtest_add_func("/device_del/drive/cli_device_q35",
 524                           test_cli_device_del_q35);
 525            qtest_add_func("/device_del/drive/device_add_q35",
 526                           test_device_add_and_del_q35);
 527            qtest_add_func("/device_del/drive/drive_add_device_add_q35",
 528                           test_drive_add_device_add_and_del_q35);
 529            qtest_add_func("/device_del/blockdev_q35",
 530                           test_blockdev_add_device_add_and_del_q35);
 531        }
 532    }
 533
 534    return g_test_run();
 535}
 536