2 * QTest testcase for Vhost-user Block Device
4 * Based on tests/qtest//virtio-blk-test.c
6 * Copyright (c) 2014 SUSE LINUX Products GmbH
7 * Copyright (c) 2014 Marc MarĂ
8 * Copyright (c) 2020 Coiby Xu
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "libqtest-single.h"
16 #include "qemu/bswap.h"
17 #include "qemu/module.h"
18 #include "standard-headers/linux/virtio_blk.h"
19 #include "standard-headers/linux/virtio_pci.h"
20 #include "libqos/qgraph.h"
21 #include "libqos/vhost-user-blk.h"
22 #include "libqos/libqos-pc.h"
24 #define TEST_IMAGE_SIZE (64 * 1024 * 1024)
25 #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
26 #define PCI_SLOT_HP 0x06
30 } QemuStorageDaemonState
;
32 typedef struct QVirtioBlkReq
{
40 #ifdef HOST_WORDS_BIGENDIAN
41 static const bool host_is_big_endian
= true;
43 static const bool host_is_big_endian
; /* false */
46 static inline void virtio_blk_fix_request(QVirtioDevice
*d
, QVirtioBlkReq
*req
)
48 if (qvirtio_is_big_endian(d
) != host_is_big_endian
) {
49 req
->type
= bswap32(req
->type
);
50 req
->ioprio
= bswap32(req
->ioprio
);
51 req
->sector
= bswap64(req
->sector
);
55 static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice
*d
,
56 struct virtio_blk_discard_write_zeroes
*dwz_hdr
)
58 if (qvirtio_is_big_endian(d
) != host_is_big_endian
) {
59 dwz_hdr
->sector
= bswap64(dwz_hdr
->sector
);
60 dwz_hdr
->num_sectors
= bswap32(dwz_hdr
->num_sectors
);
61 dwz_hdr
->flags
= bswap32(dwz_hdr
->flags
);
65 static uint64_t virtio_blk_request(QGuestAllocator
*alloc
, QVirtioDevice
*d
,
66 QVirtioBlkReq
*req
, uint64_t data_size
)
69 uint8_t status
= 0xFF;
70 QTestState
*qts
= global_qtest
;
74 case VIRTIO_BLK_T_OUT
:
75 g_assert_cmpuint(data_size
% 512, ==, 0);
77 case VIRTIO_BLK_T_DISCARD
:
78 case VIRTIO_BLK_T_WRITE_ZEROES
:
79 g_assert_cmpuint(data_size
%
80 sizeof(struct virtio_blk_discard_write_zeroes
), ==, 0);
83 g_assert_cmpuint(data_size
, ==, 0);
86 addr
= guest_alloc(alloc
, sizeof(*req
) + data_size
);
88 virtio_blk_fix_request(d
, req
);
90 qtest_memwrite(qts
, addr
, req
, 16);
91 qtest_memwrite(qts
, addr
+ 16, req
->data
, data_size
);
92 qtest_memwrite(qts
, addr
+ 16 + data_size
, &status
, sizeof(status
));
97 static void test_invalid_discard_write_zeroes(QVirtioDevice
*dev
,
98 QGuestAllocator
*alloc
,
104 struct virtio_blk_discard_write_zeroes dwz_hdr
;
105 struct virtio_blk_discard_write_zeroes dwz_hdr2
[2];
110 /* More than one dwz is not supported */
112 req
.data
= (char *) dwz_hdr2
;
113 dwz_hdr2
[0].sector
= 0;
114 dwz_hdr2
[0].num_sectors
= 1;
115 dwz_hdr2
[0].flags
= 0;
116 dwz_hdr2
[1].sector
= 1;
117 dwz_hdr2
[1].num_sectors
= 1;
118 dwz_hdr2
[1].flags
= 0;
120 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr2
[0]);
121 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr2
[1]);
123 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr2
));
125 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
126 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr2
), false, true);
127 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr2
), 1, true,
130 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
132 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
133 QVIRTIO_BLK_TIMEOUT_US
);
134 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr2
));
135 g_assert_cmpint(status
, ==, VIRTIO_BLK_S_UNSUPP
);
137 guest_free(alloc
, req_addr
);
139 /* num_sectors must be less than config->max_write_zeroes_sectors */
141 req
.data
= (char *) &dwz_hdr
;
143 dwz_hdr
.num_sectors
= 0xffffffff;
146 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr
);
148 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr
));
150 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
151 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr
), false, true);
152 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr
), 1, true,
155 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
157 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
158 QVIRTIO_BLK_TIMEOUT_US
);
159 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr
));
160 g_assert_cmpint(status
, ==, VIRTIO_BLK_S_IOERR
);
162 guest_free(alloc
, req_addr
);
164 /* sector must be less than the device capacity */
166 req
.data
= (char *) &dwz_hdr
;
167 dwz_hdr
.sector
= TEST_IMAGE_SIZE
/ 512 + 1;
168 dwz_hdr
.num_sectors
= 1;
171 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr
);
173 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr
));
175 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
176 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr
), false, true);
177 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr
), 1, true,
180 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
182 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
183 QVIRTIO_BLK_TIMEOUT_US
);
184 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr
));
185 g_assert_cmpint(status
, ==, VIRTIO_BLK_S_IOERR
);
187 guest_free(alloc
, req_addr
);
189 /* reserved flag bits must be zero */
191 req
.data
= (char *) &dwz_hdr
;
193 dwz_hdr
.num_sectors
= 1;
194 dwz_hdr
.flags
= ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP
;
196 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr
);
198 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr
));
200 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
201 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr
), false, true);
202 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr
), 1, true,
205 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
207 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
208 QVIRTIO_BLK_TIMEOUT_US
);
209 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr
));
210 g_assert_cmpint(status
, ==, VIRTIO_BLK_S_UNSUPP
);
212 guest_free(alloc
, req_addr
);
215 /* Returns the request virtqueue so the caller can perform further tests */
216 static QVirtQueue
*test_basic(QVirtioDevice
*dev
, QGuestAllocator
*alloc
)
225 QTestState
*qts
= global_qtest
;
228 features
= qvirtio_get_features(dev
);
229 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
230 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
231 (1u << VIRTIO_RING_F_EVENT_IDX
) |
232 (1u << VIRTIO_BLK_F_SCSI
));
233 qvirtio_set_features(dev
, features
);
235 capacity
= qvirtio_config_readq(dev
, 0);
236 g_assert_cmpint(capacity
, ==, TEST_IMAGE_SIZE
/ 512);
238 vq
= qvirtqueue_setup(dev
, alloc
, 0);
240 qvirtio_set_driver_ok(dev
);
242 /* Write and read with 3 descriptor layout */
244 req
.type
= VIRTIO_BLK_T_OUT
;
247 req
.data
= g_malloc0(512);
248 strcpy(req
.data
, "TEST");
250 req_addr
= virtio_blk_request(alloc
, dev
, &req
, 512);
254 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
255 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, false, true);
256 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
258 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
260 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
261 QVIRTIO_BLK_TIMEOUT_US
);
262 status
= readb(req_addr
+ 528);
263 g_assert_cmpint(status
, ==, 0);
265 guest_free(alloc
, req_addr
);
268 req
.type
= VIRTIO_BLK_T_IN
;
271 req
.data
= g_malloc0(512);
273 req_addr
= virtio_blk_request(alloc
, dev
, &req
, 512);
277 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
278 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, true, true);
279 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
281 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
283 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
284 QVIRTIO_BLK_TIMEOUT_US
);
285 status
= readb(req_addr
+ 528);
286 g_assert_cmpint(status
, ==, 0);
288 data
= g_malloc0(512);
289 qtest_memread(qts
, req_addr
+ 16, data
, 512);
290 g_assert_cmpstr(data
, ==, "TEST");
293 guest_free(alloc
, req_addr
);
295 if (features
& (1u << VIRTIO_BLK_F_WRITE_ZEROES
)) {
296 struct virtio_blk_discard_write_zeroes dwz_hdr
;
300 * WRITE_ZEROES request on the same sector of previous test where
303 req
.type
= VIRTIO_BLK_T_WRITE_ZEROES
;
304 req
.data
= (char *) &dwz_hdr
;
306 dwz_hdr
.num_sectors
= 1;
309 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr
);
311 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr
));
313 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
314 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr
), false, true);
315 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr
), 1, true,
318 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
320 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
321 QVIRTIO_BLK_TIMEOUT_US
);
322 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr
));
323 g_assert_cmpint(status
, ==, 0);
325 guest_free(alloc
, req_addr
);
327 /* Read request to check if the sector contains all zeroes */
328 req
.type
= VIRTIO_BLK_T_IN
;
331 req
.data
= g_malloc0(512);
333 req_addr
= virtio_blk_request(alloc
, dev
, &req
, 512);
337 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
338 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, true, true);
339 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
341 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
343 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
344 QVIRTIO_BLK_TIMEOUT_US
);
345 status
= readb(req_addr
+ 528);
346 g_assert_cmpint(status
, ==, 0);
348 data
= g_malloc(512);
349 expected
= g_malloc0(512);
350 qtest_memread(qts
, req_addr
+ 16, data
, 512);
351 g_assert_cmpmem(data
, 512, expected
, 512);
355 guest_free(alloc
, req_addr
);
357 test_invalid_discard_write_zeroes(dev
, alloc
, qts
, vq
,
358 VIRTIO_BLK_T_WRITE_ZEROES
);
361 if (features
& (1u << VIRTIO_BLK_F_DISCARD
)) {
362 struct virtio_blk_discard_write_zeroes dwz_hdr
;
364 req
.type
= VIRTIO_BLK_T_DISCARD
;
365 req
.data
= (char *) &dwz_hdr
;
367 dwz_hdr
.num_sectors
= 1;
370 virtio_blk_fix_dwz_hdr(dev
, &dwz_hdr
);
372 req_addr
= virtio_blk_request(alloc
, dev
, &req
, sizeof(dwz_hdr
));
374 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
375 qvirtqueue_add(qts
, vq
, req_addr
+ 16, sizeof(dwz_hdr
), false, true);
376 qvirtqueue_add(qts
, vq
, req_addr
+ 16 + sizeof(dwz_hdr
),
379 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
381 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
382 QVIRTIO_BLK_TIMEOUT_US
);
383 status
= readb(req_addr
+ 16 + sizeof(dwz_hdr
));
384 g_assert_cmpint(status
, ==, 0);
386 guest_free(alloc
, req_addr
);
388 test_invalid_discard_write_zeroes(dev
, alloc
, qts
, vq
,
389 VIRTIO_BLK_T_DISCARD
);
392 if (features
& (1u << VIRTIO_F_ANY_LAYOUT
)) {
393 /* Write and read with 2 descriptor layout */
395 req
.type
= VIRTIO_BLK_T_OUT
;
398 req
.data
= g_malloc0(512);
399 strcpy(req
.data
, "TEST");
401 req_addr
= virtio_blk_request(alloc
, dev
, &req
, 512);
405 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 528, false, true);
406 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
407 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
409 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
410 QVIRTIO_BLK_TIMEOUT_US
);
411 status
= readb(req_addr
+ 528);
412 g_assert_cmpint(status
, ==, 0);
414 guest_free(alloc
, req_addr
);
417 req
.type
= VIRTIO_BLK_T_IN
;
420 req
.data
= g_malloc0(512);
422 req_addr
= virtio_blk_request(alloc
, dev
, &req
, 512);
426 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
427 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 513, true, false);
429 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
431 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
432 QVIRTIO_BLK_TIMEOUT_US
);
433 status
= readb(req_addr
+ 528);
434 g_assert_cmpint(status
, ==, 0);
436 data
= g_malloc0(512);
437 qtest_memread(qts
, req_addr
+ 16, data
, 512);
438 g_assert_cmpstr(data
, ==, "TEST");
441 guest_free(alloc
, req_addr
);
447 static void basic(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
449 QVhostUserBlk
*blk_if
= obj
;
452 vq
= test_basic(blk_if
->vdev
, t_alloc
);
453 qvirtqueue_cleanup(blk_if
->vdev
->bus
, vq
, t_alloc
);
457 static void indirect(void *obj
, void *u_data
, QGuestAllocator
*t_alloc
)
460 QVhostUserBlk
*blk_if
= obj
;
461 QVirtioDevice
*dev
= blk_if
->vdev
;
463 QVRingIndirectDesc
*indirect
;
470 QTestState
*qts
= global_qtest
;
472 features
= qvirtio_get_features(dev
);
473 g_assert_cmphex(features
& (1u << VIRTIO_RING_F_INDIRECT_DESC
), !=, 0);
474 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
475 (1u << VIRTIO_RING_F_EVENT_IDX
) |
476 (1u << VIRTIO_BLK_F_SCSI
));
477 qvirtio_set_features(dev
, features
);
479 capacity
= qvirtio_config_readq(dev
, 0);
480 g_assert_cmpint(capacity
, ==, TEST_IMAGE_SIZE
/ 512);
482 vq
= qvirtqueue_setup(dev
, t_alloc
, 0);
483 qvirtio_set_driver_ok(dev
);
486 req
.type
= VIRTIO_BLK_T_OUT
;
489 req
.data
= g_malloc0(512);
490 strcpy(req
.data
, "TEST");
492 req_addr
= virtio_blk_request(t_alloc
, dev
, &req
, 512);
496 indirect
= qvring_indirect_desc_setup(qts
, dev
, t_alloc
, 2);
497 qvring_indirect_desc_add(dev
, qts
, indirect
, req_addr
, 528, false);
498 qvring_indirect_desc_add(dev
, qts
, indirect
, req_addr
+ 528, 1, true);
499 free_head
= qvirtqueue_add_indirect(qts
, vq
, indirect
);
500 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
502 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
503 QVIRTIO_BLK_TIMEOUT_US
);
504 status
= readb(req_addr
+ 528);
505 g_assert_cmpint(status
, ==, 0);
508 guest_free(t_alloc
, req_addr
);
511 req
.type
= VIRTIO_BLK_T_IN
;
514 req
.data
= g_malloc0(512);
515 strcpy(req
.data
, "TEST");
517 req_addr
= virtio_blk_request(t_alloc
, dev
, &req
, 512);
521 indirect
= qvring_indirect_desc_setup(qts
, dev
, t_alloc
, 2);
522 qvring_indirect_desc_add(dev
, qts
, indirect
, req_addr
, 16, false);
523 qvring_indirect_desc_add(dev
, qts
, indirect
, req_addr
+ 16, 513, true);
524 free_head
= qvirtqueue_add_indirect(qts
, vq
, indirect
);
525 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
527 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
528 QVIRTIO_BLK_TIMEOUT_US
);
529 status
= readb(req_addr
+ 528);
530 g_assert_cmpint(status
, ==, 0);
532 data
= g_malloc0(512);
533 qtest_memread(qts
, req_addr
+ 16, data
, 512);
534 g_assert_cmpstr(data
, ==, "TEST");
538 guest_free(t_alloc
, req_addr
);
539 qvirtqueue_cleanup(dev
->bus
, vq
, t_alloc
);
542 static void idx(void *obj
, void *u_data
, QGuestAllocator
*t_alloc
)
545 QVhostUserBlkPCI
*blk
= obj
;
546 QVirtioPCIDevice
*pdev
= &blk
->pci_vdev
;
547 QVirtioDevice
*dev
= &pdev
->vdev
;
557 QOSGraphObject
*blk_object
= obj
;
558 QPCIDevice
*pci_dev
= blk_object
->get_driver(blk_object
, "pci-device");
559 QTestState
*qts
= global_qtest
;
561 if (qpci_check_buggy_msi(pci_dev
)) {
565 qpci_msix_enable(pdev
->pdev
);
566 qvirtio_pci_set_msix_configuration_vector(pdev
, t_alloc
, 0);
568 features
= qvirtio_get_features(dev
);
569 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
570 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
571 (1u << VIRTIO_F_NOTIFY_ON_EMPTY
) |
572 (1u << VIRTIO_BLK_F_SCSI
));
573 qvirtio_set_features(dev
, features
);
575 capacity
= qvirtio_config_readq(dev
, 0);
576 g_assert_cmpint(capacity
, ==, TEST_IMAGE_SIZE
/ 512);
578 vq
= qvirtqueue_setup(dev
, t_alloc
, 0);
579 qvirtqueue_pci_msix_setup(pdev
, (QVirtQueuePCI
*)vq
, t_alloc
, 1);
581 qvirtio_set_driver_ok(dev
);
584 * libvhost-user signals the call fd in VHOST_USER_SET_VRING_CALL, make
585 * sure to wait for the isr here so we don't race and confuse it later on.
587 qvirtio_wait_queue_isr(qts
, dev
, vq
, QVIRTIO_BLK_TIMEOUT_US
);
590 req
.type
= VIRTIO_BLK_T_OUT
;
593 req
.data
= g_malloc0(512);
594 strcpy(req
.data
, "TEST");
596 req_addr
= virtio_blk_request(t_alloc
, dev
, &req
, 512);
600 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
601 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, false, true);
602 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
603 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
605 qvirtio_wait_used_elem(qts
, dev
, vq
, free_head
, NULL
,
606 QVIRTIO_BLK_TIMEOUT_US
);
609 req
.type
= VIRTIO_BLK_T_OUT
;
612 req
.data
= g_malloc0(512);
613 strcpy(req
.data
, "TEST");
615 req_addr
= virtio_blk_request(t_alloc
, dev
, &req
, 512);
619 /* Notify after processing the third request */
620 qvirtqueue_set_used_event(qts
, vq
, 2);
621 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
622 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, false, true);
623 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
624 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
625 write_head
= free_head
;
627 /* No notification expected */
628 status
= qvirtio_wait_status_byte_no_isr(qts
, dev
,
630 QVIRTIO_BLK_TIMEOUT_US
);
631 g_assert_cmpint(status
, ==, 0);
633 guest_free(t_alloc
, req_addr
);
636 req
.type
= VIRTIO_BLK_T_IN
;
639 req
.data
= g_malloc0(512);
641 req_addr
= virtio_blk_request(t_alloc
, dev
, &req
, 512);
645 free_head
= qvirtqueue_add(qts
, vq
, req_addr
, 16, false, true);
646 qvirtqueue_add(qts
, vq
, req_addr
+ 16, 512, true, true);
647 qvirtqueue_add(qts
, vq
, req_addr
+ 528, 1, true, false);
649 qvirtqueue_kick(qts
, dev
, vq
, free_head
);
651 /* We get just one notification for both requests */
652 qvirtio_wait_used_elem(qts
, dev
, vq
, write_head
, NULL
,
653 QVIRTIO_BLK_TIMEOUT_US
);
654 g_assert(qvirtqueue_get_buf(qts
, vq
, &desc_idx
, NULL
));
655 g_assert_cmpint(desc_idx
, ==, free_head
);
657 status
= readb(req_addr
+ 528);
658 g_assert_cmpint(status
, ==, 0);
660 data
= g_malloc0(512);
661 qtest_memread(qts
, req_addr
+ 16, data
, 512);
662 g_assert_cmpstr(data
, ==, "TEST");
665 guest_free(t_alloc
, req_addr
);
668 qpci_msix_disable(pdev
->pdev
);
670 qvirtqueue_cleanup(dev
->bus
, vq
, t_alloc
);
673 static void pci_hotplug(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
675 QVirtioPCIDevice
*dev1
= obj
;
676 QVirtioPCIDevice
*dev
;
677 QTestState
*qts
= dev1
->pdev
->bus
->qts
;
679 /* plug secondary disk */
680 qtest_qmp_device_add(qts
, "vhost-user-blk-pci", "drv1",
681 "{'addr': %s, 'chardev': 'char2'}",
682 stringify(PCI_SLOT_HP
) ".0");
684 dev
= virtio_pci_new(dev1
->pdev
->bus
,
685 &(QPCIAddress
) { .devfn
= QPCI_DEVFN(PCI_SLOT_HP
, 0)
687 g_assert_nonnull(dev
);
688 g_assert_cmpint(dev
->vdev
.device_type
, ==, VIRTIO_ID_BLOCK
);
689 qvirtio_pci_device_disable(dev
);
690 qos_object_destroy((QOSGraphObject
*)dev
);
692 /* unplug secondary disk */
693 qpci_unplug_acpi_device_test(qts
, "drv1", PCI_SLOT_HP
);
696 static void multiqueue(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
698 QVirtioPCIDevice
*pdev1
= obj
;
699 QVirtioDevice
*dev1
= &pdev1
->vdev
;
700 QVirtioPCIDevice
*pdev8
;
702 QTestState
*qts
= pdev1
->pdev
->bus
->qts
;
707 * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The
708 * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is
709 * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which
710 * is also spec-compliant).
712 features
= qvirtio_get_features(dev1
);
713 g_assert_cmpint(features
& (1u << VIRTIO_BLK_F_MQ
), ==, 0);
714 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
715 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
716 (1u << VIRTIO_F_NOTIFY_ON_EMPTY
) |
717 (1u << VIRTIO_BLK_F_SCSI
));
718 qvirtio_set_features(dev1
, features
);
720 /* Hotplug a secondary device with 8 queues */
721 qtest_qmp_device_add(qts
, "vhost-user-blk-pci", "drv1",
722 "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}",
723 stringify(PCI_SLOT_HP
) ".0");
725 pdev8
= virtio_pci_new(pdev1
->pdev
->bus
,
727 .devfn
= QPCI_DEVFN(PCI_SLOT_HP
, 0)
729 g_assert_nonnull(pdev8
);
730 g_assert_cmpint(pdev8
->vdev
.device_type
, ==, VIRTIO_ID_BLOCK
);
732 qos_object_start_hw(&pdev8
->obj
);
735 features
= qvirtio_get_features(dev8
);
736 g_assert_cmpint(features
& (1u << VIRTIO_BLK_F_MQ
),
738 (1u << VIRTIO_BLK_F_MQ
));
739 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
740 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
741 (1u << VIRTIO_F_NOTIFY_ON_EMPTY
) |
742 (1u << VIRTIO_BLK_F_SCSI
) |
743 (1u << VIRTIO_BLK_F_MQ
));
744 qvirtio_set_features(dev8
, features
);
746 num_queues
= qvirtio_config_readw(dev8
,
747 offsetof(struct virtio_blk_config
, num_queues
));
748 g_assert_cmpint(num_queues
, ==, 8);
750 qvirtio_pci_device_disable(pdev8
);
751 qos_object_destroy(&pdev8
->obj
);
753 /* unplug secondary disk */
754 qpci_unplug_acpi_device_test(qts
, "drv1", PCI_SLOT_HP
);
758 * Check that setting the vring addr on a non-existent virtqueue does
761 static void test_nonexistent_virtqueue(void *obj
, void *data
,
762 QGuestAllocator
*t_alloc
)
764 QVhostUserBlkPCI
*blk
= obj
;
765 QVirtioPCIDevice
*pdev
= &blk
->pci_vdev
;
769 dev
= qpci_device_find(pdev
->pdev
->bus
, QPCI_DEVFN(4, 0));
770 g_assert(dev
!= NULL
);
771 qpci_device_enable(dev
);
773 bar0
= qpci_iomap(dev
, 0, NULL
);
775 qpci_io_writeb(dev
, bar0
, VIRTIO_PCI_QUEUE_SEL
, 2);
776 qpci_io_writel(dev
, bar0
, VIRTIO_PCI_QUEUE_PFN
, 1);
781 static const char *qtest_qemu_storage_daemon_binary(void)
783 const char *qemu_storage_daemon_bin
;
785 qemu_storage_daemon_bin
= getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY");
786 if (!qemu_storage_daemon_bin
) {
787 fprintf(stderr
, "Environment variable "
788 "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n");
792 return qemu_storage_daemon_bin
;
795 /* g_test_queue_destroy() cleanup function for files */
796 static void destroy_file(void *path
)
800 qos_invalidate_command_line();
803 static char *drive_create(void)
806 /** vhost-user-blk won't recognize drive located in /tmp */
807 char *t_path
= g_strdup("qtest.XXXXXX");
809 /** Create a temporary raw image */
810 fd
= mkstemp(t_path
);
811 g_assert_cmpint(fd
, >=, 0);
812 ret
= ftruncate(fd
, TEST_IMAGE_SIZE
);
813 g_assert_cmpint(ret
, ==, 0);
816 g_test_queue_destroy(destroy_file
, t_path
);
820 static char *create_listen_socket(int *fd
)
825 /* No race because our pid makes the path unique */
826 path
= g_strdup_printf("/tmp/qtest-%d-sock.XXXXXX", getpid());
827 tmp_fd
= mkstemp(path
);
828 g_assert_cmpint(tmp_fd
, >=, 0);
832 *fd
= qtest_socket_server(path
);
833 g_test_queue_destroy(destroy_file
, path
);
838 * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for
839 * qemu-storage-daemon.
841 static void quit_storage_daemon(void *data
)
843 QemuStorageDaemonState
*qsd
= data
;
848 * If we were invoked as a g_test_queue_destroy() cleanup function we need
849 * to remove the abrt handler to avoid being called again if the code below
850 * aborts. Also, we must not leave the abrt handler installed after
853 qtest_remove_abrt_handler(data
);
855 /* Before quitting storage-daemon, quit qemu to avoid dubious messages */
856 qtest_kill_qemu(global_qtest
);
858 kill(qsd
->pid
, SIGTERM
);
859 pid
= waitpid(qsd
->pid
, &wstatus
, 0);
860 g_assert_cmpint(pid
, ==, qsd
->pid
);
861 if (!WIFEXITED(wstatus
)) {
862 fprintf(stderr
, "%s: expected qemu-storage-daemon to exit\n",
866 if (WEXITSTATUS(wstatus
) != 0) {
867 fprintf(stderr
, "%s: expected qemu-storage-daemon to exit "
868 "successfully, got %d\n",
869 __func__
, WEXITSTATUS(wstatus
));
876 static void start_vhost_user_blk(GString
*cmd_line
, int vus_instances
,
879 const char *vhost_user_blk_bin
= qtest_qemu_storage_daemon_binary();
882 GString
*storage_daemon_command
= g_string_new(NULL
);
883 QemuStorageDaemonState
*qsd
;
885 g_string_append_printf(storage_daemon_command
,
889 g_string_append_printf(cmd_line
,
890 " -object memory-backend-memfd,id=mem,size=256M,share=on "
891 " -M memory-backend=mem -m 256M ");
893 for (i
= 0; i
< vus_instances
; i
++) {
895 char *sock_path
= create_listen_socket(&fd
);
897 /* create image file */
898 img_path
= drive_create();
899 g_string_append_printf(storage_daemon_command
,
900 "--blockdev driver=file,node-name=disk%d,filename=%s "
901 "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
902 "node-name=disk%i,writable=on,num-queues=%d ",
903 i
, img_path
, i
, sock_path
, i
, num_queues
);
905 g_string_append_printf(cmd_line
, "-chardev socket,id=char%d,path=%s ",
909 g_test_message("starting vhost-user backend: %s",
910 storage_daemon_command
->str
);
914 * Close standard file descriptors so tap-driver.pl pipe detects when
915 * our parent terminates.
919 open("/dev/null", O_RDONLY
);
920 open("/dev/null", O_WRONLY
);
922 execlp("/bin/sh", "sh", "-c", storage_daemon_command
->str
, NULL
);
925 g_string_free(storage_daemon_command
, true);
927 qsd
= g_new(QemuStorageDaemonState
, 1);
930 /* Make sure qemu-storage-daemon is stopped */
931 qtest_add_abrt_handler(quit_storage_daemon
, qsd
);
932 g_test_queue_destroy(quit_storage_daemon
, qsd
);
935 static void *vhost_user_blk_test_setup(GString
*cmd_line
, void *arg
)
937 start_vhost_user_blk(cmd_line
, 1, 1);
944 * Since vhost-user server only serves one vhost-user client one time,
948 static void *vhost_user_blk_hotplug_test_setup(GString
*cmd_line
, void *arg
)
950 /* "-chardev socket,id=char2" is used for pci_hotplug*/
951 start_vhost_user_blk(cmd_line
, 2, 1);
955 static void *vhost_user_blk_multiqueue_test_setup(GString
*cmd_line
, void *arg
)
957 start_vhost_user_blk(cmd_line
, 2, 8);
961 static void register_vhost_user_blk_test(void)
963 QOSGraphTestOptions opts
= {
964 .before
= vhost_user_blk_test_setup
,
968 * tests for vhost-user-blk and vhost-user-blk-pci
969 * The tests are borrowed from tests/virtio-blk-test.c. But some tests
970 * regarding block_resize don't work for vhost-user-blk.
971 * vhost-user-blk device doesn't have -drive, so tests containing
972 * block_resize are also abandoned,
976 qos_add_test("basic", "vhost-user-blk", basic
, &opts
);
977 qos_add_test("indirect", "vhost-user-blk", indirect
, &opts
);
978 qos_add_test("idx", "vhost-user-blk-pci", idx
, &opts
);
979 qos_add_test("nxvirtq", "vhost-user-blk-pci",
980 test_nonexistent_virtqueue
, &opts
);
982 opts
.before
= vhost_user_blk_hotplug_test_setup
;
983 qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug
, &opts
);
985 opts
.before
= vhost_user_blk_multiqueue_test_setup
;
986 qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue
, &opts
);
989 libqos_init(register_vhost_user_blk_test
);