123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- /*
- * QTest testcase for VirtIO Block Device
- *
- * Copyright (c) 2014 SUSE LINUX Products GmbH
- * Copyright (c) 2014 Marc Marí
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
- #include "qemu/osdep.h"
- #include "libqtest-single.h"
- #include "qemu/bswap.h"
- #include "qemu/module.h"
- #include "standard-headers/linux/virtio_blk.h"
- #include "standard-headers/linux/virtio_pci.h"
- #include "libqos/qgraph.h"
- #include "libqos/virtio-blk.h"
- /* TODO actually test the results and get rid of this */
- #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
- #define TEST_IMAGE_SIZE (64 * 1024 * 1024)
- #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
- #define PCI_SLOT_HP 0x06
- typedef struct QVirtioBlkReq {
- uint32_t type;
- uint32_t ioprio;
- uint64_t sector;
- char *data;
- uint8_t status;
- } QVirtioBlkReq;
- #ifdef HOST_WORDS_BIGENDIAN
- const bool host_is_big_endian = true;
- #else
- const bool host_is_big_endian; /* false */
- #endif
- static void drive_destroy(void *path)
- {
- unlink(path);
- g_free(path);
- qos_invalidate_command_line();
- }
- static char *drive_create(void)
- {
- int fd, ret;
- char *t_path = g_strdup("/tmp/qtest.XXXXXX");
- /* Create a temporary raw image */
- fd = mkstemp(t_path);
- g_assert_cmpint(fd, >=, 0);
- ret = ftruncate(fd, TEST_IMAGE_SIZE);
- g_assert_cmpint(ret, ==, 0);
- close(fd);
- g_test_queue_destroy(drive_destroy, t_path);
- return t_path;
- }
- static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
- {
- if (qvirtio_is_big_endian(d) != host_is_big_endian) {
- req->type = bswap32(req->type);
- req->ioprio = bswap32(req->ioprio);
- req->sector = bswap64(req->sector);
- }
- }
- static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
- struct virtio_blk_discard_write_zeroes *dwz_hdr)
- {
- if (qvirtio_is_big_endian(d) != host_is_big_endian) {
- dwz_hdr->sector = bswap64(dwz_hdr->sector);
- dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
- dwz_hdr->flags = bswap32(dwz_hdr->flags);
- }
- }
- static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
- QVirtioBlkReq *req, uint64_t data_size)
- {
- uint64_t addr;
- uint8_t status = 0xFF;
- switch (req->type) {
- case VIRTIO_BLK_T_IN:
- case VIRTIO_BLK_T_OUT:
- g_assert_cmpuint(data_size % 512, ==, 0);
- break;
- case VIRTIO_BLK_T_DISCARD:
- case VIRTIO_BLK_T_WRITE_ZEROES:
- g_assert_cmpuint(data_size %
- sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
- break;
- default:
- g_assert_cmpuint(data_size, ==, 0);
- }
- addr = guest_alloc(alloc, sizeof(*req) + data_size);
- virtio_blk_fix_request(d, req);
- memwrite(addr, req, 16);
- memwrite(addr + 16, req->data, data_size);
- memwrite(addr + 16 + data_size, &status, sizeof(status));
- return addr;
- }
- /* Returns the request virtqueue so the caller can perform further tests */
- static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
- {
- QVirtioBlkReq req;
- uint64_t req_addr;
- uint64_t capacity;
- uint64_t features;
- uint32_t free_head;
- uint8_t status;
- char *data;
- QTestState *qts = global_qtest;
- QVirtQueue *vq;
- features = qvirtio_get_features(dev);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_INDIRECT_DESC) |
- (1u << VIRTIO_RING_F_EVENT_IDX) |
- (1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(dev, features);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- vq = qvirtqueue_setup(dev, alloc, 0);
- qvirtio_set_driver_ok(dev);
- /* Write and read with 3 descriptor layout */
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
- /* Read request */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpstr(data, ==, "TEST");
- g_free(data);
- guest_free(alloc, req_addr);
- if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
- struct virtio_blk_discard_write_zeroes dwz_hdr;
- void *expected;
- /*
- * WRITE_ZEROES request on the same sector of previous test where
- * we wrote "TEST".
- */
- req.type = VIRTIO_BLK_T_WRITE_ZEROES;
- req.data = (char *) &dwz_hdr;
- dwz_hdr.sector = 0;
- dwz_hdr.num_sectors = 1;
- dwz_hdr.flags = 0;
- virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
- req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
- qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
- false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 16 + sizeof(dwz_hdr));
- g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
- /* Read request to check if the sector contains all zeroes */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc(512);
- expected = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpmem(data, 512, expected, 512);
- g_free(expected);
- g_free(data);
- guest_free(alloc, req_addr);
- }
- if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
- struct virtio_blk_discard_write_zeroes dwz_hdr;
- req.type = VIRTIO_BLK_T_DISCARD;
- req.data = (char *) &dwz_hdr;
- dwz_hdr.sector = 0;
- dwz_hdr.num_sectors = 1;
- dwz_hdr.flags = 0;
- virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
- req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
- qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 16 + sizeof(dwz_hdr));
- g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
- }
- if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
- /* Write and read with 2 descriptor layout */
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 1;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
- /* Read request */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 1;
- req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpstr(data, ==, "TEST");
- g_free(data);
- guest_free(alloc, req_addr);
- }
- return vq;
- }
- static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
- {
- QVirtioBlk *blk_if = obj;
- QVirtQueue *vq;
- vq = test_basic(blk_if->vdev, t_alloc);
- qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
- }
- static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
- {
- QVirtQueue *vq;
- QVirtioBlk *blk_if = obj;
- QVirtioDevice *dev = blk_if->vdev;
- QVirtioBlkReq req;
- QVRingIndirectDesc *indirect;
- uint64_t req_addr;
- uint64_t capacity;
- uint64_t features;
- uint32_t free_head;
- uint8_t status;
- char *data;
- QTestState *qts = global_qtest;
- features = qvirtio_get_features(dev);
- g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_EVENT_IDX) |
- (1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(dev, features);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- vq = qvirtqueue_setup(dev, t_alloc, 0);
- qvirtio_set_driver_ok(dev);
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
- qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
- qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
- free_head = qvirtqueue_add_indirect(qts, vq, indirect);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- g_free(indirect);
- guest_free(t_alloc, req_addr);
- /* Read request */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
- qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
- qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
- free_head = qvirtqueue_add_indirect(qts, vq, indirect);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpstr(data, ==, "TEST");
- g_free(data);
- g_free(indirect);
- guest_free(t_alloc, req_addr);
- qvirtqueue_cleanup(dev->bus, vq, t_alloc);
- }
- static void config(void *obj, void *data, QGuestAllocator *t_alloc)
- {
- QVirtioBlk *blk_if = obj;
- QVirtioDevice *dev = blk_if->vdev;
- int n_size = TEST_IMAGE_SIZE / 2;
- uint64_t features;
- uint64_t capacity;
- features = qvirtio_get_features(dev);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_INDIRECT_DESC) |
- (1u << VIRTIO_RING_F_EVENT_IDX) |
- (1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(dev, features);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- qvirtio_set_driver_ok(dev);
- qmp_discard_response("{ 'execute': 'block_resize', "
- " 'arguments': { 'device': 'drive0', "
- " 'size': %d } }", n_size);
- qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, n_size / 512);
- }
- static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc)
- {
- QVirtQueue *vq;
- QVirtioBlkPCI *blk = obj;
- QVirtioPCIDevice *pdev = &blk->pci_vdev;
- QVirtioDevice *dev = &pdev->vdev;
- QVirtioBlkReq req;
- int n_size = TEST_IMAGE_SIZE / 2;
- uint64_t req_addr;
- uint64_t capacity;
- uint64_t features;
- uint32_t free_head;
- uint8_t status;
- char *data;
- QOSGraphObject *blk_object = obj;
- QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
- QTestState *qts = global_qtest;
- if (qpci_check_buggy_msi(pci_dev)) {
- return;
- }
- qpci_msix_enable(pdev->pdev);
- qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
- features = qvirtio_get_features(dev);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_INDIRECT_DESC) |
- (1u << VIRTIO_RING_F_EVENT_IDX) |
- (1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(dev, features);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- vq = qvirtqueue_setup(dev, t_alloc, 0);
- qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
- qvirtio_set_driver_ok(dev);
- qmp_discard_response("{ 'execute': 'block_resize', "
- " 'arguments': { 'device': 'drive0', "
- " 'size': %d } }", n_size);
- qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, n_size / 512);
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- guest_free(t_alloc, req_addr);
- /* Read request */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpstr(data, ==, "TEST");
- g_free(data);
- guest_free(t_alloc, req_addr);
- /* End test */
- qpci_msix_disable(pdev->pdev);
- qvirtqueue_cleanup(dev->bus, vq, t_alloc);
- }
- static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
- {
- QVirtQueue *vq;
- QVirtioBlkPCI *blk = obj;
- QVirtioPCIDevice *pdev = &blk->pci_vdev;
- QVirtioDevice *dev = &pdev->vdev;
- QVirtioBlkReq req;
- uint64_t req_addr;
- uint64_t capacity;
- uint64_t features;
- uint32_t free_head;
- uint32_t write_head;
- uint32_t desc_idx;
- uint8_t status;
- char *data;
- QOSGraphObject *blk_object = obj;
- QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
- QTestState *qts = global_qtest;
- if (qpci_check_buggy_msi(pci_dev)) {
- return;
- }
- qpci_msix_enable(pdev->pdev);
- qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
- features = qvirtio_get_features(dev);
- features = features & ~(QVIRTIO_F_BAD_FEATURE |
- (1u << VIRTIO_RING_F_INDIRECT_DESC) |
- (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
- (1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(dev, features);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- vq = qvirtqueue_setup(dev, t_alloc, 0);
- qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
- qvirtio_set_driver_ok(dev);
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 0;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- /* Write request */
- req.type = VIRTIO_BLK_T_OUT;
- req.ioprio = 1;
- req.sector = 1;
- req.data = g_malloc0(512);
- strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- /* Notify after processing the third request */
- qvirtqueue_set_used_event(qts, vq, 2);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- write_head = free_head;
- /* No notification expected */
- status = qvirtio_wait_status_byte_no_isr(qts, dev,
- vq, req_addr + 528,
- QVIRTIO_BLK_TIMEOUT_US);
- g_assert_cmpint(status, ==, 0);
- guest_free(t_alloc, req_addr);
- /* Read request */
- req.type = VIRTIO_BLK_T_IN;
- req.ioprio = 1;
- req.sector = 1;
- req.data = g_malloc0(512);
- req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
- g_free(req.data);
- free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
- qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
- qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(qts, dev, vq, free_head);
- /* We get just one notification for both requests */
- qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
- QVIRTIO_BLK_TIMEOUT_US);
- g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
- g_assert_cmpint(desc_idx, ==, free_head);
- status = readb(req_addr + 528);
- g_assert_cmpint(status, ==, 0);
- data = g_malloc0(512);
- memread(req_addr + 16, data, 512);
- g_assert_cmpstr(data, ==, "TEST");
- g_free(data);
- guest_free(t_alloc, req_addr);
- /* End test */
- qpci_msix_disable(pdev->pdev);
- qvirtqueue_cleanup(dev->bus, vq, t_alloc);
- }
- static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
- {
- QVirtioPCIDevice *dev1 = obj;
- QVirtioPCIDevice *dev;
- QTestState *qts = dev1->pdev->bus->qts;
- /* plug secondary disk */
- qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1",
- "{'addr': %s, 'drive': 'drive1'}",
- stringify(PCI_SLOT_HP) ".0");
- dev = virtio_pci_new(dev1->pdev->bus,
- &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) });
- g_assert_nonnull(dev);
- g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
- qvirtio_pci_device_disable(dev);
- qos_object_destroy((QOSGraphObject *)dev);
- /* unplug secondary disk */
- qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
- }
- /*
- * Check that setting the vring addr on a non-existent virtqueue does
- * not crash.
- */
- static void test_nonexistent_virtqueue(void *obj, void *data,
- QGuestAllocator *t_alloc)
- {
- QVirtioBlkPCI *blk = obj;
- QVirtioPCIDevice *pdev = &blk->pci_vdev;
- QPCIBar bar0;
- QPCIDevice *dev;
- dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
- g_assert(dev != NULL);
- qpci_device_enable(dev);
- bar0 = qpci_iomap(dev, 0, NULL);
- qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
- qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
- g_free(dev);
- }
- static void resize(void *obj, void *data, QGuestAllocator *t_alloc)
- {
- QVirtioBlk *blk_if = obj;
- QVirtioDevice *dev = blk_if->vdev;
- int n_size = TEST_IMAGE_SIZE / 2;
- uint64_t capacity;
- QVirtQueue *vq;
- QTestState *qts = global_qtest;
- vq = test_basic(dev, t_alloc);
- qmp_discard_response("{ 'execute': 'block_resize', "
- " 'arguments': { 'device': 'drive0', "
- " 'size': %d } }", n_size);
- qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(dev, 0);
- g_assert_cmpint(capacity, ==, n_size / 512);
- qvirtqueue_cleanup(dev->bus, vq, t_alloc);
- }
- static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
- {
- char *tmp_path = drive_create();
- g_string_append_printf(cmd_line,
- " -drive if=none,id=drive0,file=%s,"
- "format=raw,auto-read-only=off "
- "-drive if=none,id=drive1,file=null-co://,"
- "file.read-zeroes=on,format=raw ",
- tmp_path);
- return arg;
- }
- static void register_virtio_blk_test(void)
- {
- QOSGraphTestOptions opts = {
- .before = virtio_blk_test_setup,
- };
- qos_add_test("indirect", "virtio-blk", indirect, &opts);
- qos_add_test("config", "virtio-blk", config, &opts);
- qos_add_test("basic", "virtio-blk", basic, &opts);
- qos_add_test("resize", "virtio-blk", resize, &opts);
- /* tests just for virtio-blk-pci */
- qos_add_test("msix", "virtio-blk-pci", msix, &opts);
- qos_add_test("idx", "virtio-blk-pci", idx, &opts);
- qos_add_test("nxvirtq", "virtio-blk-pci",
- test_nonexistent_virtqueue, &opts);
- qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts);
- }
- libqos_init(register_virtio_blk_test);
|