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
{
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 if (dev1
->pdev
->bus
->not_hotpluggable
) {
680 g_test_skip("pci bus does not support hotplug");
684 /* plug secondary disk */
685 qtest_qmp_device_add(qts
, "vhost-user-blk-pci", "drv1",
686 "{'addr': %s, 'chardev': 'char2'}",
687 stringify(PCI_SLOT_HP
) ".0");
689 dev
= virtio_pci_new(dev1
->pdev
->bus
,
690 &(QPCIAddress
) { .devfn
= QPCI_DEVFN(PCI_SLOT_HP
, 0)
692 g_assert_nonnull(dev
);
693 g_assert_cmpint(dev
->vdev
.device_type
, ==, VIRTIO_ID_BLOCK
);
694 qvirtio_pci_device_disable(dev
);
695 qos_object_destroy((QOSGraphObject
*)dev
);
697 /* unplug secondary disk */
698 qpci_unplug_acpi_device_test(qts
, "drv1", PCI_SLOT_HP
);
701 static void multiqueue(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
703 QVirtioPCIDevice
*pdev1
= obj
;
704 QVirtioDevice
*dev1
= &pdev1
->vdev
;
705 QVirtioPCIDevice
*pdev8
;
707 QTestState
*qts
= pdev1
->pdev
->bus
->qts
;
711 if (pdev1
->pdev
->bus
->not_hotpluggable
) {
712 g_test_skip("bus pci.0 does not support hotplug");
717 * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The
718 * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is
719 * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which
720 * is also spec-compliant).
722 features
= qvirtio_get_features(dev1
);
723 g_assert_cmpint(features
& (1u << VIRTIO_BLK_F_MQ
), ==, 0);
724 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
725 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
726 (1u << VIRTIO_F_NOTIFY_ON_EMPTY
) |
727 (1u << VIRTIO_BLK_F_SCSI
));
728 qvirtio_set_features(dev1
, features
);
730 /* Hotplug a secondary device with 8 queues */
731 qtest_qmp_device_add(qts
, "vhost-user-blk-pci", "drv1",
732 "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}",
733 stringify(PCI_SLOT_HP
) ".0");
735 pdev8
= virtio_pci_new(pdev1
->pdev
->bus
,
737 .devfn
= QPCI_DEVFN(PCI_SLOT_HP
, 0)
739 g_assert_nonnull(pdev8
);
740 g_assert_cmpint(pdev8
->vdev
.device_type
, ==, VIRTIO_ID_BLOCK
);
742 qos_object_start_hw(&pdev8
->obj
);
745 features
= qvirtio_get_features(dev8
);
746 g_assert_cmpint(features
& (1u << VIRTIO_BLK_F_MQ
),
748 (1u << VIRTIO_BLK_F_MQ
));
749 features
= features
& ~(QVIRTIO_F_BAD_FEATURE
|
750 (1u << VIRTIO_RING_F_INDIRECT_DESC
) |
751 (1u << VIRTIO_F_NOTIFY_ON_EMPTY
) |
752 (1u << VIRTIO_BLK_F_SCSI
) |
753 (1u << VIRTIO_BLK_F_MQ
));
754 qvirtio_set_features(dev8
, features
);
756 num_queues
= qvirtio_config_readw(dev8
,
757 offsetof(struct virtio_blk_config
, num_queues
));
758 g_assert_cmpint(num_queues
, ==, 8);
760 qvirtio_pci_device_disable(pdev8
);
761 qos_object_destroy(&pdev8
->obj
);
763 /* unplug secondary disk */
764 qpci_unplug_acpi_device_test(qts
, "drv1", PCI_SLOT_HP
);
768 * Check that setting the vring addr on a non-existent virtqueue does
771 static void test_nonexistent_virtqueue(void *obj
, void *data
,
772 QGuestAllocator
*t_alloc
)
774 QVhostUserBlkPCI
*blk
= obj
;
775 QVirtioPCIDevice
*pdev
= &blk
->pci_vdev
;
779 dev
= qpci_device_find(pdev
->pdev
->bus
, QPCI_DEVFN(4, 0));
780 g_assert(dev
!= NULL
);
781 qpci_device_enable(dev
);
783 bar0
= qpci_iomap(dev
, 0, NULL
);
785 qpci_io_writeb(dev
, bar0
, VIRTIO_PCI_QUEUE_SEL
, 2);
786 qpci_io_writel(dev
, bar0
, VIRTIO_PCI_QUEUE_PFN
, 1);
791 static const char *qtest_qemu_storage_daemon_binary(void)
793 const char *qemu_storage_daemon_bin
;
795 qemu_storage_daemon_bin
= getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY");
796 if (!qemu_storage_daemon_bin
) {
797 fprintf(stderr
, "Environment variable "
798 "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n");
802 /* If we've got a path to the binary, check whether we can access it */
803 if (strchr(qemu_storage_daemon_bin
, '/') &&
804 access(qemu_storage_daemon_bin
, X_OK
) != 0) {
805 fprintf(stderr
, "ERROR: '%s' is not accessible\n",
806 qemu_storage_daemon_bin
);
810 return qemu_storage_daemon_bin
;
813 /* g_test_queue_destroy() cleanup function for files */
814 static void destroy_file(void *path
)
818 qos_invalidate_command_line();
821 static char *drive_create(void)
824 /** vhost-user-blk won't recognize drive located in /tmp */
825 char *t_path
= g_strdup("qtest.XXXXXX");
827 /** Create a temporary raw image */
828 fd
= mkstemp(t_path
);
829 g_assert_cmpint(fd
, >=, 0);
830 ret
= ftruncate(fd
, TEST_IMAGE_SIZE
);
831 g_assert_cmpint(ret
, ==, 0);
834 g_test_queue_destroy(destroy_file
, t_path
);
838 static char *create_listen_socket(int *fd
)
843 /* No race because our pid makes the path unique */
844 path
= g_strdup_printf("%s/qtest-%d-sock.XXXXXX",
845 g_get_tmp_dir(), getpid());
846 tmp_fd
= mkstemp(path
);
847 g_assert_cmpint(tmp_fd
, >=, 0);
851 *fd
= qtest_socket_server(path
);
852 g_test_queue_destroy(destroy_file
, path
);
857 * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for
858 * qemu-storage-daemon.
860 static void quit_storage_daemon(void *data
)
862 QemuStorageDaemonState
*qsd
= data
;
867 * If we were invoked as a g_test_queue_destroy() cleanup function we need
868 * to remove the abrt handler to avoid being called again if the code below
869 * aborts. Also, we must not leave the abrt handler installed after
872 qtest_remove_abrt_handler(data
);
874 /* Before quitting storage-daemon, quit qemu to avoid dubious messages */
875 qtest_kill_qemu(global_qtest
);
877 kill(qsd
->pid
, SIGTERM
);
878 pid
= waitpid(qsd
->pid
, &wstatus
, 0);
879 g_assert_cmpint(pid
, ==, qsd
->pid
);
880 if (!WIFEXITED(wstatus
)) {
881 fprintf(stderr
, "%s: expected qemu-storage-daemon to exit\n",
885 if (WEXITSTATUS(wstatus
) != 0) {
886 fprintf(stderr
, "%s: expected qemu-storage-daemon to exit "
887 "successfully, got %d\n",
888 __func__
, WEXITSTATUS(wstatus
));
895 static void start_vhost_user_blk(GString
*cmd_line
, int vus_instances
,
898 const char *vhost_user_blk_bin
= qtest_qemu_storage_daemon_binary();
901 GString
*storage_daemon_command
= g_string_new(NULL
);
902 QemuStorageDaemonState
*qsd
;
904 g_string_append_printf(storage_daemon_command
,
908 g_string_append_printf(cmd_line
,
909 " -object memory-backend-shm,id=mem,size=256M "
910 " -M memory-backend=mem -m 256M ");
912 for (i
= 0; i
< vus_instances
; i
++) {
914 char *sock_path
= create_listen_socket(&fd
);
916 /* create image file */
917 img_path
= drive_create();
918 g_string_append_printf(storage_daemon_command
,
919 "--blockdev driver=file,node-name=disk%d,filename=%s "
920 "--export type=vhost-user-blk,id=disk%d,addr.type=fd,addr.str=%d,"
921 "node-name=disk%i,writable=on,num-queues=%d ",
922 i
, img_path
, i
, fd
, i
, num_queues
);
924 g_string_append_printf(cmd_line
, "-chardev socket,id=char%d,path=%s ",
928 g_test_message("starting vhost-user backend: %s",
929 storage_daemon_command
->str
);
933 * Close standard file descriptors so tap-driver.pl pipe detects when
934 * our parent terminates.
938 open("/dev/null", O_RDONLY
);
939 open("/dev/null", O_WRONLY
);
941 execlp("/bin/sh", "sh", "-c", storage_daemon_command
->str
, NULL
);
944 g_string_free(storage_daemon_command
, true);
946 qsd
= g_new(QemuStorageDaemonState
, 1);
949 /* Make sure qemu-storage-daemon is stopped */
950 qtest_add_abrt_handler(quit_storage_daemon
, qsd
);
951 g_test_queue_destroy(quit_storage_daemon
, qsd
);
954 static void *vhost_user_blk_test_setup(GString
*cmd_line
, void *arg
)
956 start_vhost_user_blk(cmd_line
, 1, 1);
963 * Since vhost-user server only serves one vhost-user client one time,
967 static void *vhost_user_blk_hotplug_test_setup(GString
*cmd_line
, void *arg
)
969 /* "-chardev socket,id=char2" is used for pci_hotplug*/
970 start_vhost_user_blk(cmd_line
, 2, 1);
974 static void *vhost_user_blk_multiqueue_test_setup(GString
*cmd_line
, void *arg
)
976 start_vhost_user_blk(cmd_line
, 2, 8);
980 static void register_vhost_user_blk_test(void)
982 QOSGraphTestOptions opts
= {
983 .before
= vhost_user_blk_test_setup
,
986 if (!getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY")) {
987 g_test_message("QTEST_QEMU_STORAGE_DAEMON_BINARY not defined, "
988 "skipping vhost-user-blk-test");
993 * tests for vhost-user-blk and vhost-user-blk-pci
994 * The tests are borrowed from tests/virtio-blk-test.c. But some tests
995 * regarding block_resize don't work for vhost-user-blk.
996 * vhost-user-blk device doesn't have -drive, so tests containing
997 * block_resize are also abandoned,
1001 qos_add_test("basic", "vhost-user-blk", basic
, &opts
);
1002 qos_add_test("indirect", "vhost-user-blk", indirect
, &opts
);
1003 qos_add_test("idx", "vhost-user-blk-pci", idx
, &opts
);
1004 qos_add_test("nxvirtq", "vhost-user-blk-pci",
1005 test_nonexistent_virtqueue
, &opts
);
1007 opts
.before
= vhost_user_blk_hotplug_test_setup
;
1008 qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug
, &opts
);
1010 opts
.before
= vhost_user_blk_multiqueue_test_setup
;
1011 qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue
, &opts
);
1014 libqos_init(register_vhost_user_blk_test
);