180695202SCoiby Xu /*
280695202SCoiby Xu * QTest testcase for Vhost-user Block Device
380695202SCoiby Xu *
480695202SCoiby Xu * Based on tests/qtest//virtio-blk-test.c
580695202SCoiby Xu
680695202SCoiby Xu * Copyright (c) 2014 SUSE LINUX Products GmbH
780695202SCoiby Xu * Copyright (c) 2014 Marc Marí
880695202SCoiby Xu * Copyright (c) 2020 Coiby Xu
980695202SCoiby Xu *
1080695202SCoiby Xu * This work is licensed under the terms of the GNU GPL, version 2 or later.
1180695202SCoiby Xu * See the COPYING file in the top-level directory.
1280695202SCoiby Xu */
1380695202SCoiby Xu
1480695202SCoiby Xu #include "qemu/osdep.h"
1580695202SCoiby Xu #include "libqtest-single.h"
1680695202SCoiby Xu #include "qemu/bswap.h"
1780695202SCoiby Xu #include "qemu/module.h"
1880695202SCoiby Xu #include "standard-headers/linux/virtio_blk.h"
1980695202SCoiby Xu #include "standard-headers/linux/virtio_pci.h"
2080695202SCoiby Xu #include "libqos/qgraph.h"
2180695202SCoiby Xu #include "libqos/vhost-user-blk.h"
2280695202SCoiby Xu #include "libqos/libqos-pc.h"
2380695202SCoiby Xu
2480695202SCoiby Xu #define TEST_IMAGE_SIZE (64 * 1024 * 1024)
2580695202SCoiby Xu #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
2680695202SCoiby Xu #define PCI_SLOT_HP 0x06
2780695202SCoiby Xu
2880695202SCoiby Xu typedef struct {
2980695202SCoiby Xu pid_t pid;
3080695202SCoiby Xu } QemuStorageDaemonState;
3180695202SCoiby Xu
3280695202SCoiby Xu typedef struct QVirtioBlkReq {
3380695202SCoiby Xu uint32_t type;
3480695202SCoiby Xu uint32_t ioprio;
3580695202SCoiby Xu uint64_t sector;
3680695202SCoiby Xu char *data;
3780695202SCoiby Xu uint8_t status;
3880695202SCoiby Xu } QVirtioBlkReq;
3980695202SCoiby Xu
40e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
4180695202SCoiby Xu static const bool host_is_big_endian = true;
4280695202SCoiby Xu #else
4380695202SCoiby Xu static const bool host_is_big_endian; /* false */
4480695202SCoiby Xu #endif
4580695202SCoiby Xu
virtio_blk_fix_request(QVirtioDevice * d,QVirtioBlkReq * req)4680695202SCoiby Xu static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
4780695202SCoiby Xu {
4880695202SCoiby Xu if (qvirtio_is_big_endian(d) != host_is_big_endian) {
4980695202SCoiby Xu req->type = bswap32(req->type);
5080695202SCoiby Xu req->ioprio = bswap32(req->ioprio);
5180695202SCoiby Xu req->sector = bswap64(req->sector);
5280695202SCoiby Xu }
5380695202SCoiby Xu }
5480695202SCoiby Xu
virtio_blk_fix_dwz_hdr(QVirtioDevice * d,struct virtio_blk_discard_write_zeroes * dwz_hdr)5580695202SCoiby Xu static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
5680695202SCoiby Xu struct virtio_blk_discard_write_zeroes *dwz_hdr)
5780695202SCoiby Xu {
5880695202SCoiby Xu if (qvirtio_is_big_endian(d) != host_is_big_endian) {
5980695202SCoiby Xu dwz_hdr->sector = bswap64(dwz_hdr->sector);
6080695202SCoiby Xu dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
6180695202SCoiby Xu dwz_hdr->flags = bswap32(dwz_hdr->flags);
6280695202SCoiby Xu }
6380695202SCoiby Xu }
6480695202SCoiby Xu
virtio_blk_request(QGuestAllocator * alloc,QVirtioDevice * d,QVirtioBlkReq * req,uint64_t data_size)6580695202SCoiby Xu static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
6680695202SCoiby Xu QVirtioBlkReq *req, uint64_t data_size)
6780695202SCoiby Xu {
6880695202SCoiby Xu uint64_t addr;
6980695202SCoiby Xu uint8_t status = 0xFF;
7080695202SCoiby Xu QTestState *qts = global_qtest;
7180695202SCoiby Xu
7280695202SCoiby Xu switch (req->type) {
7380695202SCoiby Xu case VIRTIO_BLK_T_IN:
7480695202SCoiby Xu case VIRTIO_BLK_T_OUT:
7580695202SCoiby Xu g_assert_cmpuint(data_size % 512, ==, 0);
7680695202SCoiby Xu break;
7780695202SCoiby Xu case VIRTIO_BLK_T_DISCARD:
7880695202SCoiby Xu case VIRTIO_BLK_T_WRITE_ZEROES:
7980695202SCoiby Xu g_assert_cmpuint(data_size %
8080695202SCoiby Xu sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
8180695202SCoiby Xu break;
8280695202SCoiby Xu default:
8380695202SCoiby Xu g_assert_cmpuint(data_size, ==, 0);
8480695202SCoiby Xu }
8580695202SCoiby Xu
8680695202SCoiby Xu addr = guest_alloc(alloc, sizeof(*req) + data_size);
8780695202SCoiby Xu
8880695202SCoiby Xu virtio_blk_fix_request(d, req);
8980695202SCoiby Xu
9080695202SCoiby Xu qtest_memwrite(qts, addr, req, 16);
9180695202SCoiby Xu qtest_memwrite(qts, addr + 16, req->data, data_size);
9280695202SCoiby Xu qtest_memwrite(qts, addr + 16 + data_size, &status, sizeof(status));
9380695202SCoiby Xu
9480695202SCoiby Xu return addr;
9580695202SCoiby Xu }
9680695202SCoiby Xu
test_invalid_discard_write_zeroes(QVirtioDevice * dev,QGuestAllocator * alloc,QTestState * qts,QVirtQueue * vq,uint32_t type)977999e313SStefan Hajnoczi static void test_invalid_discard_write_zeroes(QVirtioDevice *dev,
987999e313SStefan Hajnoczi QGuestAllocator *alloc,
997999e313SStefan Hajnoczi QTestState *qts,
1007999e313SStefan Hajnoczi QVirtQueue *vq,
1017999e313SStefan Hajnoczi uint32_t type)
1027999e313SStefan Hajnoczi {
1037999e313SStefan Hajnoczi QVirtioBlkReq req;
1047999e313SStefan Hajnoczi struct virtio_blk_discard_write_zeroes dwz_hdr;
1057999e313SStefan Hajnoczi struct virtio_blk_discard_write_zeroes dwz_hdr2[2];
1067999e313SStefan Hajnoczi uint64_t req_addr;
1077999e313SStefan Hajnoczi uint32_t free_head;
1087999e313SStefan Hajnoczi uint8_t status;
1097999e313SStefan Hajnoczi
1107999e313SStefan Hajnoczi /* More than one dwz is not supported */
1117999e313SStefan Hajnoczi req.type = type;
1127999e313SStefan Hajnoczi req.data = (char *) dwz_hdr2;
1137999e313SStefan Hajnoczi dwz_hdr2[0].sector = 0;
1147999e313SStefan Hajnoczi dwz_hdr2[0].num_sectors = 1;
1157999e313SStefan Hajnoczi dwz_hdr2[0].flags = 0;
1167999e313SStefan Hajnoczi dwz_hdr2[1].sector = 1;
1177999e313SStefan Hajnoczi dwz_hdr2[1].num_sectors = 1;
1187999e313SStefan Hajnoczi dwz_hdr2[1].flags = 0;
1197999e313SStefan Hajnoczi
1207999e313SStefan Hajnoczi virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[0]);
1217999e313SStefan Hajnoczi virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[1]);
1227999e313SStefan Hajnoczi
1237999e313SStefan Hajnoczi req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr2));
1247999e313SStefan Hajnoczi
1257999e313SStefan Hajnoczi free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
1267999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr2), false, true);
1277999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr2), 1, true,
1287999e313SStefan Hajnoczi false);
1297999e313SStefan Hajnoczi
1307999e313SStefan Hajnoczi qvirtqueue_kick(qts, dev, vq, free_head);
1317999e313SStefan Hajnoczi
1327999e313SStefan Hajnoczi qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
1337999e313SStefan Hajnoczi QVIRTIO_BLK_TIMEOUT_US);
1347999e313SStefan Hajnoczi status = readb(req_addr + 16 + sizeof(dwz_hdr2));
1357999e313SStefan Hajnoczi g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
1367999e313SStefan Hajnoczi
1377999e313SStefan Hajnoczi guest_free(alloc, req_addr);
1387999e313SStefan Hajnoczi
1397999e313SStefan Hajnoczi /* num_sectors must be less than config->max_write_zeroes_sectors */
1407999e313SStefan Hajnoczi req.type = type;
1417999e313SStefan Hajnoczi req.data = (char *) &dwz_hdr;
1427999e313SStefan Hajnoczi dwz_hdr.sector = 0;
1437999e313SStefan Hajnoczi dwz_hdr.num_sectors = 0xffffffff;
1447999e313SStefan Hajnoczi dwz_hdr.flags = 0;
1457999e313SStefan Hajnoczi
1467999e313SStefan Hajnoczi virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
1477999e313SStefan Hajnoczi
1487999e313SStefan Hajnoczi req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
1497999e313SStefan Hajnoczi
1507999e313SStefan Hajnoczi free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
1517999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
1527999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
1537999e313SStefan Hajnoczi false);
1547999e313SStefan Hajnoczi
1557999e313SStefan Hajnoczi qvirtqueue_kick(qts, dev, vq, free_head);
1567999e313SStefan Hajnoczi
1577999e313SStefan Hajnoczi qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
1587999e313SStefan Hajnoczi QVIRTIO_BLK_TIMEOUT_US);
1597999e313SStefan Hajnoczi status = readb(req_addr + 16 + sizeof(dwz_hdr));
1607999e313SStefan Hajnoczi g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
1617999e313SStefan Hajnoczi
1627999e313SStefan Hajnoczi guest_free(alloc, req_addr);
1637999e313SStefan Hajnoczi
1647999e313SStefan Hajnoczi /* sector must be less than the device capacity */
1657999e313SStefan Hajnoczi req.type = type;
1667999e313SStefan Hajnoczi req.data = (char *) &dwz_hdr;
1677999e313SStefan Hajnoczi dwz_hdr.sector = TEST_IMAGE_SIZE / 512 + 1;
1687999e313SStefan Hajnoczi dwz_hdr.num_sectors = 1;
1697999e313SStefan Hajnoczi dwz_hdr.flags = 0;
1707999e313SStefan Hajnoczi
1717999e313SStefan Hajnoczi virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
1727999e313SStefan Hajnoczi
1737999e313SStefan Hajnoczi req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
1747999e313SStefan Hajnoczi
1757999e313SStefan Hajnoczi free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
1767999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
1777999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
1787999e313SStefan Hajnoczi false);
1797999e313SStefan Hajnoczi
1807999e313SStefan Hajnoczi qvirtqueue_kick(qts, dev, vq, free_head);
1817999e313SStefan Hajnoczi
1827999e313SStefan Hajnoczi qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
1837999e313SStefan Hajnoczi QVIRTIO_BLK_TIMEOUT_US);
1847999e313SStefan Hajnoczi status = readb(req_addr + 16 + sizeof(dwz_hdr));
1857999e313SStefan Hajnoczi g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
1867999e313SStefan Hajnoczi
1877999e313SStefan Hajnoczi guest_free(alloc, req_addr);
1887999e313SStefan Hajnoczi
1897999e313SStefan Hajnoczi /* reserved flag bits must be zero */
1907999e313SStefan Hajnoczi req.type = type;
1917999e313SStefan Hajnoczi req.data = (char *) &dwz_hdr;
1927999e313SStefan Hajnoczi dwz_hdr.sector = 0;
1937999e313SStefan Hajnoczi dwz_hdr.num_sectors = 1;
1947999e313SStefan Hajnoczi dwz_hdr.flags = ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP;
1957999e313SStefan Hajnoczi
1967999e313SStefan Hajnoczi virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
1977999e313SStefan Hajnoczi
1987999e313SStefan Hajnoczi req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
1997999e313SStefan Hajnoczi
2007999e313SStefan Hajnoczi free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
2017999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
2027999e313SStefan Hajnoczi qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
2037999e313SStefan Hajnoczi false);
2047999e313SStefan Hajnoczi
2057999e313SStefan Hajnoczi qvirtqueue_kick(qts, dev, vq, free_head);
2067999e313SStefan Hajnoczi
2077999e313SStefan Hajnoczi qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
2087999e313SStefan Hajnoczi QVIRTIO_BLK_TIMEOUT_US);
2097999e313SStefan Hajnoczi status = readb(req_addr + 16 + sizeof(dwz_hdr));
2107999e313SStefan Hajnoczi g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
2117999e313SStefan Hajnoczi
2127999e313SStefan Hajnoczi guest_free(alloc, req_addr);
2137999e313SStefan Hajnoczi }
2147999e313SStefan Hajnoczi
21580695202SCoiby Xu /* Returns the request virtqueue so the caller can perform further tests */
test_basic(QVirtioDevice * dev,QGuestAllocator * alloc)21680695202SCoiby Xu static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
21780695202SCoiby Xu {
21880695202SCoiby Xu QVirtioBlkReq req;
21980695202SCoiby Xu uint64_t req_addr;
22080695202SCoiby Xu uint64_t capacity;
22180695202SCoiby Xu uint64_t features;
22280695202SCoiby Xu uint32_t free_head;
22380695202SCoiby Xu uint8_t status;
22480695202SCoiby Xu char *data;
22580695202SCoiby Xu QTestState *qts = global_qtest;
22680695202SCoiby Xu QVirtQueue *vq;
22780695202SCoiby Xu
22880695202SCoiby Xu features = qvirtio_get_features(dev);
22980695202SCoiby Xu features = features & ~(QVIRTIO_F_BAD_FEATURE |
23080695202SCoiby Xu (1u << VIRTIO_RING_F_INDIRECT_DESC) |
23180695202SCoiby Xu (1u << VIRTIO_RING_F_EVENT_IDX) |
23280695202SCoiby Xu (1u << VIRTIO_BLK_F_SCSI));
23380695202SCoiby Xu qvirtio_set_features(dev, features);
23480695202SCoiby Xu
23580695202SCoiby Xu capacity = qvirtio_config_readq(dev, 0);
23680695202SCoiby Xu g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
23780695202SCoiby Xu
23880695202SCoiby Xu vq = qvirtqueue_setup(dev, alloc, 0);
23980695202SCoiby Xu
24080695202SCoiby Xu qvirtio_set_driver_ok(dev);
24180695202SCoiby Xu
24280695202SCoiby Xu /* Write and read with 3 descriptor layout */
24380695202SCoiby Xu /* Write request */
24480695202SCoiby Xu req.type = VIRTIO_BLK_T_OUT;
24580695202SCoiby Xu req.ioprio = 1;
24680695202SCoiby Xu req.sector = 0;
24780695202SCoiby Xu req.data = g_malloc0(512);
24880695202SCoiby Xu strcpy(req.data, "TEST");
24980695202SCoiby Xu
25080695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, 512);
25180695202SCoiby Xu
25280695202SCoiby Xu g_free(req.data);
25380695202SCoiby Xu
25480695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
25580695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
25680695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
25780695202SCoiby Xu
25880695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
25980695202SCoiby Xu
26080695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
26180695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
26280695202SCoiby Xu status = readb(req_addr + 528);
26380695202SCoiby Xu g_assert_cmpint(status, ==, 0);
26480695202SCoiby Xu
26580695202SCoiby Xu guest_free(alloc, req_addr);
26680695202SCoiby Xu
26780695202SCoiby Xu /* Read request */
26880695202SCoiby Xu req.type = VIRTIO_BLK_T_IN;
26980695202SCoiby Xu req.ioprio = 1;
27080695202SCoiby Xu req.sector = 0;
27180695202SCoiby Xu req.data = g_malloc0(512);
27280695202SCoiby Xu
27380695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, 512);
27480695202SCoiby Xu
27580695202SCoiby Xu g_free(req.data);
27680695202SCoiby Xu
27780695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
27880695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
27980695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
28080695202SCoiby Xu
28180695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
28280695202SCoiby Xu
28380695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
28480695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
28580695202SCoiby Xu status = readb(req_addr + 528);
28680695202SCoiby Xu g_assert_cmpint(status, ==, 0);
28780695202SCoiby Xu
28880695202SCoiby Xu data = g_malloc0(512);
28980695202SCoiby Xu qtest_memread(qts, req_addr + 16, data, 512);
29080695202SCoiby Xu g_assert_cmpstr(data, ==, "TEST");
29180695202SCoiby Xu g_free(data);
29280695202SCoiby Xu
29380695202SCoiby Xu guest_free(alloc, req_addr);
29480695202SCoiby Xu
29580695202SCoiby Xu if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
29680695202SCoiby Xu struct virtio_blk_discard_write_zeroes dwz_hdr;
29780695202SCoiby Xu void *expected;
29880695202SCoiby Xu
29980695202SCoiby Xu /*
30080695202SCoiby Xu * WRITE_ZEROES request on the same sector of previous test where
30180695202SCoiby Xu * we wrote "TEST".
30280695202SCoiby Xu */
30380695202SCoiby Xu req.type = VIRTIO_BLK_T_WRITE_ZEROES;
30480695202SCoiby Xu req.data = (char *) &dwz_hdr;
30580695202SCoiby Xu dwz_hdr.sector = 0;
30680695202SCoiby Xu dwz_hdr.num_sectors = 1;
30780695202SCoiby Xu dwz_hdr.flags = 0;
30880695202SCoiby Xu
30980695202SCoiby Xu virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
31080695202SCoiby Xu
31180695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
31280695202SCoiby Xu
31380695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
31480695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
31580695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
31680695202SCoiby Xu false);
31780695202SCoiby Xu
31880695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
31980695202SCoiby Xu
32080695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
32180695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
32280695202SCoiby Xu status = readb(req_addr + 16 + sizeof(dwz_hdr));
32380695202SCoiby Xu g_assert_cmpint(status, ==, 0);
32480695202SCoiby Xu
32580695202SCoiby Xu guest_free(alloc, req_addr);
32680695202SCoiby Xu
32780695202SCoiby Xu /* Read request to check if the sector contains all zeroes */
32880695202SCoiby Xu req.type = VIRTIO_BLK_T_IN;
32980695202SCoiby Xu req.ioprio = 1;
33080695202SCoiby Xu req.sector = 0;
33180695202SCoiby Xu req.data = g_malloc0(512);
33280695202SCoiby Xu
33380695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, 512);
33480695202SCoiby Xu
33580695202SCoiby Xu g_free(req.data);
33680695202SCoiby Xu
33780695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
33880695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
33980695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
34080695202SCoiby Xu
34180695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
34280695202SCoiby Xu
34380695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
34480695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
34580695202SCoiby Xu status = readb(req_addr + 528);
34680695202SCoiby Xu g_assert_cmpint(status, ==, 0);
34780695202SCoiby Xu
34880695202SCoiby Xu data = g_malloc(512);
34980695202SCoiby Xu expected = g_malloc0(512);
35080695202SCoiby Xu qtest_memread(qts, req_addr + 16, data, 512);
35180695202SCoiby Xu g_assert_cmpmem(data, 512, expected, 512);
35280695202SCoiby Xu g_free(expected);
35380695202SCoiby Xu g_free(data);
35480695202SCoiby Xu
35580695202SCoiby Xu guest_free(alloc, req_addr);
3567999e313SStefan Hajnoczi
3577999e313SStefan Hajnoczi test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
3587999e313SStefan Hajnoczi VIRTIO_BLK_T_WRITE_ZEROES);
35980695202SCoiby Xu }
36080695202SCoiby Xu
36180695202SCoiby Xu if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
36280695202SCoiby Xu struct virtio_blk_discard_write_zeroes dwz_hdr;
36380695202SCoiby Xu
36480695202SCoiby Xu req.type = VIRTIO_BLK_T_DISCARD;
36580695202SCoiby Xu req.data = (char *) &dwz_hdr;
36680695202SCoiby Xu dwz_hdr.sector = 0;
36780695202SCoiby Xu dwz_hdr.num_sectors = 1;
36880695202SCoiby Xu dwz_hdr.flags = 0;
36980695202SCoiby Xu
37080695202SCoiby Xu virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
37180695202SCoiby Xu
37280695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
37380695202SCoiby Xu
37480695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
37580695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
37680695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr),
37780695202SCoiby Xu 1, true, false);
37880695202SCoiby Xu
37980695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
38080695202SCoiby Xu
38180695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
38280695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
38380695202SCoiby Xu status = readb(req_addr + 16 + sizeof(dwz_hdr));
38480695202SCoiby Xu g_assert_cmpint(status, ==, 0);
38580695202SCoiby Xu
38680695202SCoiby Xu guest_free(alloc, req_addr);
3877999e313SStefan Hajnoczi
3887999e313SStefan Hajnoczi test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
3897999e313SStefan Hajnoczi VIRTIO_BLK_T_DISCARD);
39080695202SCoiby Xu }
39180695202SCoiby Xu
39280695202SCoiby Xu if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
39380695202SCoiby Xu /* Write and read with 2 descriptor layout */
39480695202SCoiby Xu /* Write request */
39580695202SCoiby Xu req.type = VIRTIO_BLK_T_OUT;
39680695202SCoiby Xu req.ioprio = 1;
39780695202SCoiby Xu req.sector = 1;
39880695202SCoiby Xu req.data = g_malloc0(512);
39980695202SCoiby Xu strcpy(req.data, "TEST");
40080695202SCoiby Xu
40180695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, 512);
40280695202SCoiby Xu
40380695202SCoiby Xu g_free(req.data);
40480695202SCoiby Xu
40580695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
40680695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
40780695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
40880695202SCoiby Xu
40980695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
41080695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
41180695202SCoiby Xu status = readb(req_addr + 528);
41280695202SCoiby Xu g_assert_cmpint(status, ==, 0);
41380695202SCoiby Xu
41480695202SCoiby Xu guest_free(alloc, req_addr);
41580695202SCoiby Xu
41680695202SCoiby Xu /* Read request */
41780695202SCoiby Xu req.type = VIRTIO_BLK_T_IN;
41880695202SCoiby Xu req.ioprio = 1;
41980695202SCoiby Xu req.sector = 1;
42080695202SCoiby Xu req.data = g_malloc0(512);
42180695202SCoiby Xu
42280695202SCoiby Xu req_addr = virtio_blk_request(alloc, dev, &req, 512);
42380695202SCoiby Xu
42480695202SCoiby Xu g_free(req.data);
42580695202SCoiby Xu
42680695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
42780695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
42880695202SCoiby Xu
42980695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
43080695202SCoiby Xu
43180695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
43280695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
43380695202SCoiby Xu status = readb(req_addr + 528);
43480695202SCoiby Xu g_assert_cmpint(status, ==, 0);
43580695202SCoiby Xu
43680695202SCoiby Xu data = g_malloc0(512);
43780695202SCoiby Xu qtest_memread(qts, req_addr + 16, data, 512);
43880695202SCoiby Xu g_assert_cmpstr(data, ==, "TEST");
43980695202SCoiby Xu g_free(data);
44080695202SCoiby Xu
44180695202SCoiby Xu guest_free(alloc, req_addr);
44280695202SCoiby Xu }
44380695202SCoiby Xu
44480695202SCoiby Xu return vq;
44580695202SCoiby Xu }
44680695202SCoiby Xu
basic(void * obj,void * data,QGuestAllocator * t_alloc)44780695202SCoiby Xu static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
44880695202SCoiby Xu {
44980695202SCoiby Xu QVhostUserBlk *blk_if = obj;
45080695202SCoiby Xu QVirtQueue *vq;
45180695202SCoiby Xu
45280695202SCoiby Xu vq = test_basic(blk_if->vdev, t_alloc);
45380695202SCoiby Xu qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
45480695202SCoiby Xu
45580695202SCoiby Xu }
45680695202SCoiby Xu
indirect(void * obj,void * u_data,QGuestAllocator * t_alloc)45780695202SCoiby Xu static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
45880695202SCoiby Xu {
45980695202SCoiby Xu QVirtQueue *vq;
46080695202SCoiby Xu QVhostUserBlk *blk_if = obj;
46180695202SCoiby Xu QVirtioDevice *dev = blk_if->vdev;
46280695202SCoiby Xu QVirtioBlkReq req;
46380695202SCoiby Xu QVRingIndirectDesc *indirect;
46480695202SCoiby Xu uint64_t req_addr;
46580695202SCoiby Xu uint64_t capacity;
46680695202SCoiby Xu uint64_t features;
46780695202SCoiby Xu uint32_t free_head;
46880695202SCoiby Xu uint8_t status;
46980695202SCoiby Xu char *data;
47080695202SCoiby Xu QTestState *qts = global_qtest;
47180695202SCoiby Xu
47280695202SCoiby Xu features = qvirtio_get_features(dev);
47380695202SCoiby Xu g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
47480695202SCoiby Xu features = features & ~(QVIRTIO_F_BAD_FEATURE |
47580695202SCoiby Xu (1u << VIRTIO_RING_F_EVENT_IDX) |
47680695202SCoiby Xu (1u << VIRTIO_BLK_F_SCSI));
47780695202SCoiby Xu qvirtio_set_features(dev, features);
47880695202SCoiby Xu
47980695202SCoiby Xu capacity = qvirtio_config_readq(dev, 0);
48080695202SCoiby Xu g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
48180695202SCoiby Xu
48280695202SCoiby Xu vq = qvirtqueue_setup(dev, t_alloc, 0);
48380695202SCoiby Xu qvirtio_set_driver_ok(dev);
48480695202SCoiby Xu
48580695202SCoiby Xu /* Write request */
48680695202SCoiby Xu req.type = VIRTIO_BLK_T_OUT;
48780695202SCoiby Xu req.ioprio = 1;
48880695202SCoiby Xu req.sector = 0;
48980695202SCoiby Xu req.data = g_malloc0(512);
49080695202SCoiby Xu strcpy(req.data, "TEST");
49180695202SCoiby Xu
49280695202SCoiby Xu req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
49380695202SCoiby Xu
49480695202SCoiby Xu g_free(req.data);
49580695202SCoiby Xu
49680695202SCoiby Xu indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
49780695202SCoiby Xu qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
49880695202SCoiby Xu qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
49980695202SCoiby Xu free_head = qvirtqueue_add_indirect(qts, vq, indirect);
50080695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
50180695202SCoiby Xu
50280695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
50380695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
50480695202SCoiby Xu status = readb(req_addr + 528);
50580695202SCoiby Xu g_assert_cmpint(status, ==, 0);
50680695202SCoiby Xu
50780695202SCoiby Xu g_free(indirect);
50880695202SCoiby Xu guest_free(t_alloc, req_addr);
50980695202SCoiby Xu
51080695202SCoiby Xu /* Read request */
51180695202SCoiby Xu req.type = VIRTIO_BLK_T_IN;
51280695202SCoiby Xu req.ioprio = 1;
51380695202SCoiby Xu req.sector = 0;
51480695202SCoiby Xu req.data = g_malloc0(512);
51580695202SCoiby Xu strcpy(req.data, "TEST");
51680695202SCoiby Xu
51780695202SCoiby Xu req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
51880695202SCoiby Xu
51980695202SCoiby Xu g_free(req.data);
52080695202SCoiby Xu
52180695202SCoiby Xu indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
52280695202SCoiby Xu qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
52380695202SCoiby Xu qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
52480695202SCoiby Xu free_head = qvirtqueue_add_indirect(qts, vq, indirect);
52580695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
52680695202SCoiby Xu
52780695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
52880695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
52980695202SCoiby Xu status = readb(req_addr + 528);
53080695202SCoiby Xu g_assert_cmpint(status, ==, 0);
53180695202SCoiby Xu
53280695202SCoiby Xu data = g_malloc0(512);
53380695202SCoiby Xu qtest_memread(qts, req_addr + 16, data, 512);
53480695202SCoiby Xu g_assert_cmpstr(data, ==, "TEST");
53580695202SCoiby Xu g_free(data);
53680695202SCoiby Xu
53780695202SCoiby Xu g_free(indirect);
53880695202SCoiby Xu guest_free(t_alloc, req_addr);
53980695202SCoiby Xu qvirtqueue_cleanup(dev->bus, vq, t_alloc);
54080695202SCoiby Xu }
54180695202SCoiby Xu
idx(void * obj,void * u_data,QGuestAllocator * t_alloc)54280695202SCoiby Xu static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
54380695202SCoiby Xu {
54480695202SCoiby Xu QVirtQueue *vq;
54580695202SCoiby Xu QVhostUserBlkPCI *blk = obj;
54680695202SCoiby Xu QVirtioPCIDevice *pdev = &blk->pci_vdev;
54780695202SCoiby Xu QVirtioDevice *dev = &pdev->vdev;
54880695202SCoiby Xu QVirtioBlkReq req;
54980695202SCoiby Xu uint64_t req_addr;
55080695202SCoiby Xu uint64_t capacity;
55180695202SCoiby Xu uint64_t features;
55280695202SCoiby Xu uint32_t free_head;
55380695202SCoiby Xu uint32_t write_head;
55480695202SCoiby Xu uint32_t desc_idx;
55580695202SCoiby Xu uint8_t status;
55680695202SCoiby Xu char *data;
55780695202SCoiby Xu QOSGraphObject *blk_object = obj;
55880695202SCoiby Xu QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
55980695202SCoiby Xu QTestState *qts = global_qtest;
56080695202SCoiby Xu
56180695202SCoiby Xu if (qpci_check_buggy_msi(pci_dev)) {
56280695202SCoiby Xu return;
56380695202SCoiby Xu }
56480695202SCoiby Xu
56580695202SCoiby Xu qpci_msix_enable(pdev->pdev);
56680695202SCoiby Xu qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
56780695202SCoiby Xu
56880695202SCoiby Xu features = qvirtio_get_features(dev);
56980695202SCoiby Xu features = features & ~(QVIRTIO_F_BAD_FEATURE |
57080695202SCoiby Xu (1u << VIRTIO_RING_F_INDIRECT_DESC) |
57180695202SCoiby Xu (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
57280695202SCoiby Xu (1u << VIRTIO_BLK_F_SCSI));
57380695202SCoiby Xu qvirtio_set_features(dev, features);
57480695202SCoiby Xu
57580695202SCoiby Xu capacity = qvirtio_config_readq(dev, 0);
57680695202SCoiby Xu g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
57780695202SCoiby Xu
57880695202SCoiby Xu vq = qvirtqueue_setup(dev, t_alloc, 0);
57980695202SCoiby Xu qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
58080695202SCoiby Xu
58180695202SCoiby Xu qvirtio_set_driver_ok(dev);
58280695202SCoiby Xu
58380695202SCoiby Xu /*
58480695202SCoiby Xu * libvhost-user signals the call fd in VHOST_USER_SET_VRING_CALL, make
58580695202SCoiby Xu * sure to wait for the isr here so we don't race and confuse it later on.
58680695202SCoiby Xu */
58780695202SCoiby Xu qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
58880695202SCoiby Xu
58980695202SCoiby Xu /* Write request */
59080695202SCoiby Xu req.type = VIRTIO_BLK_T_OUT;
59180695202SCoiby Xu req.ioprio = 1;
59280695202SCoiby Xu req.sector = 0;
59380695202SCoiby Xu req.data = g_malloc0(512);
59480695202SCoiby Xu strcpy(req.data, "TEST");
59580695202SCoiby Xu
59680695202SCoiby Xu req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
59780695202SCoiby Xu
59880695202SCoiby Xu g_free(req.data);
59980695202SCoiby Xu
60080695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
60180695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
60280695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
60380695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
60480695202SCoiby Xu
60580695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
60680695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
60780695202SCoiby Xu
60880695202SCoiby Xu /* Write request */
60980695202SCoiby Xu req.type = VIRTIO_BLK_T_OUT;
61080695202SCoiby Xu req.ioprio = 1;
61180695202SCoiby Xu req.sector = 1;
61280695202SCoiby Xu req.data = g_malloc0(512);
61380695202SCoiby Xu strcpy(req.data, "TEST");
61480695202SCoiby Xu
61580695202SCoiby Xu req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
61680695202SCoiby Xu
61780695202SCoiby Xu g_free(req.data);
61880695202SCoiby Xu
61980695202SCoiby Xu /* Notify after processing the third request */
62080695202SCoiby Xu qvirtqueue_set_used_event(qts, vq, 2);
62180695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
62280695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
62380695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
62480695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
62580695202SCoiby Xu write_head = free_head;
62680695202SCoiby Xu
62780695202SCoiby Xu /* No notification expected */
62880695202SCoiby Xu status = qvirtio_wait_status_byte_no_isr(qts, dev,
62980695202SCoiby Xu vq, req_addr + 528,
63080695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
63180695202SCoiby Xu g_assert_cmpint(status, ==, 0);
63280695202SCoiby Xu
63380695202SCoiby Xu guest_free(t_alloc, req_addr);
63480695202SCoiby Xu
63580695202SCoiby Xu /* Read request */
63680695202SCoiby Xu req.type = VIRTIO_BLK_T_IN;
63780695202SCoiby Xu req.ioprio = 1;
63880695202SCoiby Xu req.sector = 1;
63980695202SCoiby Xu req.data = g_malloc0(512);
64080695202SCoiby Xu
64180695202SCoiby Xu req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
64280695202SCoiby Xu
64380695202SCoiby Xu g_free(req.data);
64480695202SCoiby Xu
64580695202SCoiby Xu free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
64680695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
64780695202SCoiby Xu qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
64880695202SCoiby Xu
64980695202SCoiby Xu qvirtqueue_kick(qts, dev, vq, free_head);
65080695202SCoiby Xu
65180695202SCoiby Xu /* We get just one notification for both requests */
65280695202SCoiby Xu qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
65380695202SCoiby Xu QVIRTIO_BLK_TIMEOUT_US);
65480695202SCoiby Xu g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
65580695202SCoiby Xu g_assert_cmpint(desc_idx, ==, free_head);
65680695202SCoiby Xu
65780695202SCoiby Xu status = readb(req_addr + 528);
65880695202SCoiby Xu g_assert_cmpint(status, ==, 0);
65980695202SCoiby Xu
66080695202SCoiby Xu data = g_malloc0(512);
66180695202SCoiby Xu qtest_memread(qts, req_addr + 16, data, 512);
66280695202SCoiby Xu g_assert_cmpstr(data, ==, "TEST");
66380695202SCoiby Xu g_free(data);
66480695202SCoiby Xu
66580695202SCoiby Xu guest_free(t_alloc, req_addr);
66680695202SCoiby Xu
66780695202SCoiby Xu /* End test */
66880695202SCoiby Xu qpci_msix_disable(pdev->pdev);
66980695202SCoiby Xu
67080695202SCoiby Xu qvirtqueue_cleanup(dev->bus, vq, t_alloc);
67180695202SCoiby Xu }
67280695202SCoiby Xu
pci_hotplug(void * obj,void * data,QGuestAllocator * t_alloc)67380695202SCoiby Xu static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
67480695202SCoiby Xu {
67580695202SCoiby Xu QVirtioPCIDevice *dev1 = obj;
67680695202SCoiby Xu QVirtioPCIDevice *dev;
67780695202SCoiby Xu QTestState *qts = dev1->pdev->bus->qts;
67880695202SCoiby Xu
67902ee7a8aSEric Auger if (dev1->pdev->bus->not_hotpluggable) {
68002ee7a8aSEric Auger g_test_skip("pci bus does not support hotplug");
68102ee7a8aSEric Auger return;
68202ee7a8aSEric Auger }
68302ee7a8aSEric Auger
68480695202SCoiby Xu /* plug secondary disk */
68580695202SCoiby Xu qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
68680695202SCoiby Xu "{'addr': %s, 'chardev': 'char2'}",
68780695202SCoiby Xu stringify(PCI_SLOT_HP) ".0");
68880695202SCoiby Xu
68980695202SCoiby Xu dev = virtio_pci_new(dev1->pdev->bus,
69080695202SCoiby Xu &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
69180695202SCoiby Xu });
69280695202SCoiby Xu g_assert_nonnull(dev);
69380695202SCoiby Xu g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
69480695202SCoiby Xu qvirtio_pci_device_disable(dev);
69580695202SCoiby Xu qos_object_destroy((QOSGraphObject *)dev);
69680695202SCoiby Xu
69780695202SCoiby Xu /* unplug secondary disk */
69880695202SCoiby Xu qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
69980695202SCoiby Xu }
70080695202SCoiby Xu
multiqueue(void * obj,void * data,QGuestAllocator * t_alloc)7019c4e99e8SStefan Hajnoczi static void multiqueue(void *obj, void *data, QGuestAllocator *t_alloc)
7029c4e99e8SStefan Hajnoczi {
7039c4e99e8SStefan Hajnoczi QVirtioPCIDevice *pdev1 = obj;
7049c4e99e8SStefan Hajnoczi QVirtioDevice *dev1 = &pdev1->vdev;
7059c4e99e8SStefan Hajnoczi QVirtioPCIDevice *pdev8;
7069c4e99e8SStefan Hajnoczi QVirtioDevice *dev8;
7079c4e99e8SStefan Hajnoczi QTestState *qts = pdev1->pdev->bus->qts;
7089c4e99e8SStefan Hajnoczi uint64_t features;
7099c4e99e8SStefan Hajnoczi uint16_t num_queues;
7109c4e99e8SStefan Hajnoczi
71102ee7a8aSEric Auger if (pdev1->pdev->bus->not_hotpluggable) {
71202ee7a8aSEric Auger g_test_skip("bus pci.0 does not support hotplug");
71302ee7a8aSEric Auger return;
71402ee7a8aSEric Auger }
71502ee7a8aSEric Auger
7169c4e99e8SStefan Hajnoczi /*
7179c4e99e8SStefan Hajnoczi * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The
7189c4e99e8SStefan Hajnoczi * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is
7199c4e99e8SStefan Hajnoczi * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which
7209c4e99e8SStefan Hajnoczi * is also spec-compliant).
7219c4e99e8SStefan Hajnoczi */
7229c4e99e8SStefan Hajnoczi features = qvirtio_get_features(dev1);
7239c4e99e8SStefan Hajnoczi g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ), ==, 0);
7249c4e99e8SStefan Hajnoczi features = features & ~(QVIRTIO_F_BAD_FEATURE |
7259c4e99e8SStefan Hajnoczi (1u << VIRTIO_RING_F_INDIRECT_DESC) |
7269c4e99e8SStefan Hajnoczi (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
7279c4e99e8SStefan Hajnoczi (1u << VIRTIO_BLK_F_SCSI));
7289c4e99e8SStefan Hajnoczi qvirtio_set_features(dev1, features);
7299c4e99e8SStefan Hajnoczi
7309c4e99e8SStefan Hajnoczi /* Hotplug a secondary device with 8 queues */
7319c4e99e8SStefan Hajnoczi qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
7329c4e99e8SStefan Hajnoczi "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}",
7339c4e99e8SStefan Hajnoczi stringify(PCI_SLOT_HP) ".0");
7349c4e99e8SStefan Hajnoczi
7359c4e99e8SStefan Hajnoczi pdev8 = virtio_pci_new(pdev1->pdev->bus,
7369c4e99e8SStefan Hajnoczi &(QPCIAddress) {
7379c4e99e8SStefan Hajnoczi .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
7389c4e99e8SStefan Hajnoczi });
7399c4e99e8SStefan Hajnoczi g_assert_nonnull(pdev8);
7409c4e99e8SStefan Hajnoczi g_assert_cmpint(pdev8->vdev.device_type, ==, VIRTIO_ID_BLOCK);
7419c4e99e8SStefan Hajnoczi
7429c4e99e8SStefan Hajnoczi qos_object_start_hw(&pdev8->obj);
7439c4e99e8SStefan Hajnoczi
7449c4e99e8SStefan Hajnoczi dev8 = &pdev8->vdev;
7459c4e99e8SStefan Hajnoczi features = qvirtio_get_features(dev8);
7469c4e99e8SStefan Hajnoczi g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ),
7479c4e99e8SStefan Hajnoczi ==,
7489c4e99e8SStefan Hajnoczi (1u << VIRTIO_BLK_F_MQ));
7499c4e99e8SStefan Hajnoczi features = features & ~(QVIRTIO_F_BAD_FEATURE |
7509c4e99e8SStefan Hajnoczi (1u << VIRTIO_RING_F_INDIRECT_DESC) |
7519c4e99e8SStefan Hajnoczi (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
7529c4e99e8SStefan Hajnoczi (1u << VIRTIO_BLK_F_SCSI) |
7539c4e99e8SStefan Hajnoczi (1u << VIRTIO_BLK_F_MQ));
7549c4e99e8SStefan Hajnoczi qvirtio_set_features(dev8, features);
7559c4e99e8SStefan Hajnoczi
7569c4e99e8SStefan Hajnoczi num_queues = qvirtio_config_readw(dev8,
7579c4e99e8SStefan Hajnoczi offsetof(struct virtio_blk_config, num_queues));
7589c4e99e8SStefan Hajnoczi g_assert_cmpint(num_queues, ==, 8);
7599c4e99e8SStefan Hajnoczi
7609c4e99e8SStefan Hajnoczi qvirtio_pci_device_disable(pdev8);
7619c4e99e8SStefan Hajnoczi qos_object_destroy(&pdev8->obj);
7629c4e99e8SStefan Hajnoczi
7639c4e99e8SStefan Hajnoczi /* unplug secondary disk */
7649c4e99e8SStefan Hajnoczi qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
7659c4e99e8SStefan Hajnoczi }
7669c4e99e8SStefan Hajnoczi
76780695202SCoiby Xu /*
76880695202SCoiby Xu * Check that setting the vring addr on a non-existent virtqueue does
76980695202SCoiby Xu * not crash.
77080695202SCoiby Xu */
test_nonexistent_virtqueue(void * obj,void * data,QGuestAllocator * t_alloc)77180695202SCoiby Xu static void test_nonexistent_virtqueue(void *obj, void *data,
77280695202SCoiby Xu QGuestAllocator *t_alloc)
77380695202SCoiby Xu {
77480695202SCoiby Xu QVhostUserBlkPCI *blk = obj;
77580695202SCoiby Xu QVirtioPCIDevice *pdev = &blk->pci_vdev;
77680695202SCoiby Xu QPCIBar bar0;
77780695202SCoiby Xu QPCIDevice *dev;
77880695202SCoiby Xu
77980695202SCoiby Xu dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
78080695202SCoiby Xu g_assert(dev != NULL);
78180695202SCoiby Xu qpci_device_enable(dev);
78280695202SCoiby Xu
78380695202SCoiby Xu bar0 = qpci_iomap(dev, 0, NULL);
78480695202SCoiby Xu
78580695202SCoiby Xu qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
78680695202SCoiby Xu qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
78780695202SCoiby Xu
78880695202SCoiby Xu g_free(dev);
78980695202SCoiby Xu }
79080695202SCoiby Xu
qtest_qemu_storage_daemon_binary(void)79180695202SCoiby Xu static const char *qtest_qemu_storage_daemon_binary(void)
79280695202SCoiby Xu {
79380695202SCoiby Xu const char *qemu_storage_daemon_bin;
79480695202SCoiby Xu
79580695202SCoiby Xu qemu_storage_daemon_bin = getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY");
79680695202SCoiby Xu if (!qemu_storage_daemon_bin) {
79780695202SCoiby Xu fprintf(stderr, "Environment variable "
79880695202SCoiby Xu "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n");
79980695202SCoiby Xu exit(0);
80080695202SCoiby Xu }
80180695202SCoiby Xu
802b063c290SThomas Huth /* If we've got a path to the binary, check whether we can access it */
803b063c290SThomas Huth if (strchr(qemu_storage_daemon_bin, '/') &&
804b063c290SThomas Huth access(qemu_storage_daemon_bin, X_OK) != 0) {
805b063c290SThomas Huth fprintf(stderr, "ERROR: '%s' is not accessible\n",
806b063c290SThomas Huth qemu_storage_daemon_bin);
807b063c290SThomas Huth exit(1);
808b063c290SThomas Huth }
809b063c290SThomas Huth
81080695202SCoiby Xu return qemu_storage_daemon_bin;
81180695202SCoiby Xu }
81280695202SCoiby Xu
81380695202SCoiby Xu /* g_test_queue_destroy() cleanup function for files */
destroy_file(void * path)81480695202SCoiby Xu static void destroy_file(void *path)
81580695202SCoiby Xu {
81680695202SCoiby Xu unlink(path);
81780695202SCoiby Xu g_free(path);
81880695202SCoiby Xu qos_invalidate_command_line();
81980695202SCoiby Xu }
82080695202SCoiby Xu
drive_create(void)82180695202SCoiby Xu static char *drive_create(void)
82280695202SCoiby Xu {
82380695202SCoiby Xu int fd, ret;
82480695202SCoiby Xu /** vhost-user-blk won't recognize drive located in /tmp */
82580695202SCoiby Xu char *t_path = g_strdup("qtest.XXXXXX");
82680695202SCoiby Xu
82780695202SCoiby Xu /** Create a temporary raw image */
82880695202SCoiby Xu fd = mkstemp(t_path);
82980695202SCoiby Xu g_assert_cmpint(fd, >=, 0);
83080695202SCoiby Xu ret = ftruncate(fd, TEST_IMAGE_SIZE);
83180695202SCoiby Xu g_assert_cmpint(ret, ==, 0);
83280695202SCoiby Xu close(fd);
83380695202SCoiby Xu
83480695202SCoiby Xu g_test_queue_destroy(destroy_file, t_path);
83580695202SCoiby Xu return t_path;
83680695202SCoiby Xu }
83780695202SCoiby Xu
create_listen_socket(int * fd)83880695202SCoiby Xu static char *create_listen_socket(int *fd)
83980695202SCoiby Xu {
84080695202SCoiby Xu int tmp_fd;
84180695202SCoiby Xu char *path;
84280695202SCoiby Xu
84380695202SCoiby Xu /* No race because our pid makes the path unique */
844c2041355SBin Meng path = g_strdup_printf("%s/qtest-%d-sock.XXXXXX",
845c2041355SBin Meng g_get_tmp_dir(), getpid());
84680695202SCoiby Xu tmp_fd = mkstemp(path);
84780695202SCoiby Xu g_assert_cmpint(tmp_fd, >=, 0);
84880695202SCoiby Xu close(tmp_fd);
84980695202SCoiby Xu unlink(path);
85080695202SCoiby Xu
85180695202SCoiby Xu *fd = qtest_socket_server(path);
85280695202SCoiby Xu g_test_queue_destroy(destroy_file, path);
85380695202SCoiby Xu return path;
85480695202SCoiby Xu }
85580695202SCoiby Xu
85680695202SCoiby Xu /*
85780695202SCoiby Xu * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for
85880695202SCoiby Xu * qemu-storage-daemon.
85980695202SCoiby Xu */
quit_storage_daemon(void * data)86080695202SCoiby Xu static void quit_storage_daemon(void *data)
86180695202SCoiby Xu {
86280695202SCoiby Xu QemuStorageDaemonState *qsd = data;
86380695202SCoiby Xu int wstatus;
86480695202SCoiby Xu pid_t pid;
86580695202SCoiby Xu
86680695202SCoiby Xu /*
86780695202SCoiby Xu * If we were invoked as a g_test_queue_destroy() cleanup function we need
86880695202SCoiby Xu * to remove the abrt handler to avoid being called again if the code below
86980695202SCoiby Xu * aborts. Also, we must not leave the abrt handler installed after
87080695202SCoiby Xu * cleanup.
87180695202SCoiby Xu */
87280695202SCoiby Xu qtest_remove_abrt_handler(data);
87380695202SCoiby Xu
87480695202SCoiby Xu /* Before quitting storage-daemon, quit qemu to avoid dubious messages */
87580695202SCoiby Xu qtest_kill_qemu(global_qtest);
87680695202SCoiby Xu
87780695202SCoiby Xu kill(qsd->pid, SIGTERM);
87880695202SCoiby Xu pid = waitpid(qsd->pid, &wstatus, 0);
87980695202SCoiby Xu g_assert_cmpint(pid, ==, qsd->pid);
88080695202SCoiby Xu if (!WIFEXITED(wstatus)) {
88180695202SCoiby Xu fprintf(stderr, "%s: expected qemu-storage-daemon to exit\n",
88280695202SCoiby Xu __func__);
88380695202SCoiby Xu abort();
88480695202SCoiby Xu }
88580695202SCoiby Xu if (WEXITSTATUS(wstatus) != 0) {
88680695202SCoiby Xu fprintf(stderr, "%s: expected qemu-storage-daemon to exit "
88780695202SCoiby Xu "successfully, got %d\n",
88880695202SCoiby Xu __func__, WEXITSTATUS(wstatus));
88980695202SCoiby Xu abort();
89080695202SCoiby Xu }
89180695202SCoiby Xu
89280695202SCoiby Xu g_free(data);
89380695202SCoiby Xu }
89480695202SCoiby Xu
start_vhost_user_blk(GString * cmd_line,int vus_instances,int num_queues)8959c4e99e8SStefan Hajnoczi static void start_vhost_user_blk(GString *cmd_line, int vus_instances,
8969c4e99e8SStefan Hajnoczi int num_queues)
89780695202SCoiby Xu {
89880695202SCoiby Xu const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary();
89980695202SCoiby Xu int i;
90080695202SCoiby Xu gchar *img_path;
90180695202SCoiby Xu GString *storage_daemon_command = g_string_new(NULL);
90280695202SCoiby Xu QemuStorageDaemonState *qsd;
90380695202SCoiby Xu
90480695202SCoiby Xu g_string_append_printf(storage_daemon_command,
90580695202SCoiby Xu "exec %s ",
90680695202SCoiby Xu vhost_user_blk_bin);
90780695202SCoiby Xu
90880695202SCoiby Xu g_string_append_printf(cmd_line,
909*e3490627SStefano Garzarella " -object memory-backend-shm,id=mem,size=256M "
91080695202SCoiby Xu " -M memory-backend=mem -m 256M ");
91180695202SCoiby Xu
91280695202SCoiby Xu for (i = 0; i < vus_instances; i++) {
91380695202SCoiby Xu int fd;
91480695202SCoiby Xu char *sock_path = create_listen_socket(&fd);
91580695202SCoiby Xu
91680695202SCoiby Xu /* create image file */
91780695202SCoiby Xu img_path = drive_create();
91880695202SCoiby Xu g_string_append_printf(storage_daemon_command,
91980695202SCoiby Xu "--blockdev driver=file,node-name=disk%d,filename=%s "
920515efffcSStefan Hajnoczi "--export type=vhost-user-blk,id=disk%d,addr.type=fd,addr.str=%d,"
9219c4e99e8SStefan Hajnoczi "node-name=disk%i,writable=on,num-queues=%d ",
922515efffcSStefan Hajnoczi i, img_path, i, fd, i, num_queues);
92380695202SCoiby Xu
92480695202SCoiby Xu g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ",
92580695202SCoiby Xu i + 1, sock_path);
92680695202SCoiby Xu }
92780695202SCoiby Xu
92880695202SCoiby Xu g_test_message("starting vhost-user backend: %s",
92980695202SCoiby Xu storage_daemon_command->str);
93080695202SCoiby Xu pid_t pid = fork();
93180695202SCoiby Xu if (pid == 0) {
93280695202SCoiby Xu /*
93380695202SCoiby Xu * Close standard file descriptors so tap-driver.pl pipe detects when
93480695202SCoiby Xu * our parent terminates.
93580695202SCoiby Xu */
93680695202SCoiby Xu close(0);
93780695202SCoiby Xu close(1);
93880695202SCoiby Xu open("/dev/null", O_RDONLY);
93980695202SCoiby Xu open("/dev/null", O_WRONLY);
94080695202SCoiby Xu
94180695202SCoiby Xu execlp("/bin/sh", "sh", "-c", storage_daemon_command->str, NULL);
94280695202SCoiby Xu exit(1);
94380695202SCoiby Xu }
94480695202SCoiby Xu g_string_free(storage_daemon_command, true);
94580695202SCoiby Xu
94680695202SCoiby Xu qsd = g_new(QemuStorageDaemonState, 1);
94780695202SCoiby Xu qsd->pid = pid;
94880695202SCoiby Xu
94980695202SCoiby Xu /* Make sure qemu-storage-daemon is stopped */
95080695202SCoiby Xu qtest_add_abrt_handler(quit_storage_daemon, qsd);
95180695202SCoiby Xu g_test_queue_destroy(quit_storage_daemon, qsd);
95280695202SCoiby Xu }
95380695202SCoiby Xu
vhost_user_blk_test_setup(GString * cmd_line,void * arg)95480695202SCoiby Xu static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
95580695202SCoiby Xu {
9569c4e99e8SStefan Hajnoczi start_vhost_user_blk(cmd_line, 1, 1);
95780695202SCoiby Xu return arg;
95880695202SCoiby Xu }
95980695202SCoiby Xu
96080695202SCoiby Xu /*
96180695202SCoiby Xu * Setup for hotplug.
96280695202SCoiby Xu *
96380695202SCoiby Xu * Since vhost-user server only serves one vhost-user client one time,
96496420a30SMichael Tokarev * another export
96580695202SCoiby Xu *
96680695202SCoiby Xu */
vhost_user_blk_hotplug_test_setup(GString * cmd_line,void * arg)96780695202SCoiby Xu static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg)
96880695202SCoiby Xu {
96980695202SCoiby Xu /* "-chardev socket,id=char2" is used for pci_hotplug*/
9709c4e99e8SStefan Hajnoczi start_vhost_user_blk(cmd_line, 2, 1);
9719c4e99e8SStefan Hajnoczi return arg;
9729c4e99e8SStefan Hajnoczi }
9739c4e99e8SStefan Hajnoczi
vhost_user_blk_multiqueue_test_setup(GString * cmd_line,void * arg)9749c4e99e8SStefan Hajnoczi static void *vhost_user_blk_multiqueue_test_setup(GString *cmd_line, void *arg)
9759c4e99e8SStefan Hajnoczi {
9769c4e99e8SStefan Hajnoczi start_vhost_user_blk(cmd_line, 2, 8);
97780695202SCoiby Xu return arg;
97880695202SCoiby Xu }
97980695202SCoiby Xu
register_vhost_user_blk_test(void)98080695202SCoiby Xu static void register_vhost_user_blk_test(void)
98180695202SCoiby Xu {
98280695202SCoiby Xu QOSGraphTestOptions opts = {
98380695202SCoiby Xu .before = vhost_user_blk_test_setup,
98480695202SCoiby Xu };
98580695202SCoiby Xu
9864bf1b669SChristian Schoenebeck if (!getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY")) {
9874bf1b669SChristian Schoenebeck g_test_message("QTEST_QEMU_STORAGE_DAEMON_BINARY not defined, "
9884bf1b669SChristian Schoenebeck "skipping vhost-user-blk-test");
9894bf1b669SChristian Schoenebeck return;
9904bf1b669SChristian Schoenebeck }
9914bf1b669SChristian Schoenebeck
99280695202SCoiby Xu /*
99380695202SCoiby Xu * tests for vhost-user-blk and vhost-user-blk-pci
99480695202SCoiby Xu * The tests are borrowed from tests/virtio-blk-test.c. But some tests
99580695202SCoiby Xu * regarding block_resize don't work for vhost-user-blk.
99680695202SCoiby Xu * vhost-user-blk device doesn't have -drive, so tests containing
99780695202SCoiby Xu * block_resize are also abandoned,
99880695202SCoiby Xu * - config
99980695202SCoiby Xu * - resize
100080695202SCoiby Xu */
100180695202SCoiby Xu qos_add_test("basic", "vhost-user-blk", basic, &opts);
100280695202SCoiby Xu qos_add_test("indirect", "vhost-user-blk", indirect, &opts);
100380695202SCoiby Xu qos_add_test("idx", "vhost-user-blk-pci", idx, &opts);
100480695202SCoiby Xu qos_add_test("nxvirtq", "vhost-user-blk-pci",
100580695202SCoiby Xu test_nonexistent_virtqueue, &opts);
100680695202SCoiby Xu
100780695202SCoiby Xu opts.before = vhost_user_blk_hotplug_test_setup;
100880695202SCoiby Xu qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts);
10099c4e99e8SStefan Hajnoczi
10109c4e99e8SStefan Hajnoczi opts.before = vhost_user_blk_multiqueue_test_setup;
10119c4e99e8SStefan Hajnoczi qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue, &opts);
101280695202SCoiby Xu }
101380695202SCoiby Xu
101480695202SCoiby Xu libqos_init(register_vhost_user_blk_test);
1015