2 * QTest testcase for VirtIO 9P
4 * Copyright (c) 2014 SUSE LINUX Products GmbH
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
12 #include "hw/9pfs/9p.h"
13 #include "hw/9pfs/9p-synth.h"
14 #include "libqos/virtio-9p.h"
15 #include "libqos/qgraph.h"
17 #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
18 static QGuestAllocator
*alloc
;
20 static void pci_config(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
24 size_t tag_len
= qvirtio_config_readw(v9p
->vdev
, 0);
28 g_assert_cmpint(tag_len
, ==, strlen(MOUNT_TAG
));
30 tag
= g_malloc(tag_len
);
31 for (i
= 0; i
< tag_len
; i
++) {
32 tag
[i
] = qvirtio_config_readb(v9p
->vdev
, i
+ 2);
34 g_assert_cmpmem(tag
, tag_len
, MOUNT_TAG
, tag_len
);
38 #define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
46 /* No r_size, it is hardcoded to P9_MAX_SIZE */
52 static void v9fs_memwrite(P9Req
*req
, const void *addr
, size_t len
)
54 memwrite(req
->t_msg
+ req
->t_off
, addr
, len
);
58 static void v9fs_memskip(P9Req
*req
, size_t len
)
63 static void v9fs_memread(P9Req
*req
, void *addr
, size_t len
)
65 memread(req
->r_msg
+ req
->r_off
, addr
, len
);
69 static void v9fs_uint16_write(P9Req
*req
, uint16_t val
)
71 uint16_t le_val
= cpu_to_le16(val
);
73 v9fs_memwrite(req
, &le_val
, 2);
76 static void v9fs_uint16_read(P9Req
*req
, uint16_t *val
)
78 v9fs_memread(req
, val
, 2);
82 static void v9fs_uint32_write(P9Req
*req
, uint32_t val
)
84 uint32_t le_val
= cpu_to_le32(val
);
86 v9fs_memwrite(req
, &le_val
, 4);
89 static void v9fs_uint64_write(P9Req
*req
, uint64_t val
)
91 uint64_t le_val
= cpu_to_le64(val
);
93 v9fs_memwrite(req
, &le_val
, 8);
96 static void v9fs_uint32_read(P9Req
*req
, uint32_t *val
)
98 v9fs_memread(req
, val
, 4);
102 /* len[2] string[len] */
103 static uint16_t v9fs_string_size(const char *string
)
105 size_t len
= strlen(string
);
107 g_assert_cmpint(len
, <=, UINT16_MAX
- 2);
112 static void v9fs_string_write(P9Req
*req
, const char *string
)
114 int len
= strlen(string
);
116 g_assert_cmpint(len
, <=, UINT16_MAX
);
118 v9fs_uint16_write(req
, (uint16_t) len
);
119 v9fs_memwrite(req
, string
, len
);
122 static void v9fs_string_read(P9Req
*req
, uint16_t *len
, char **string
)
126 v9fs_uint16_read(req
, &local_len
);
131 *string
= g_malloc(local_len
);
132 v9fs_memread(req
, *string
, local_len
);
134 v9fs_memskip(req
, local_len
);
144 static P9Req
*v9fs_req_init(QVirtio9P
*v9p
, uint32_t size
, uint8_t id
,
147 P9Req
*req
= g_new0(P9Req
, 1);
148 uint32_t total_size
= 7; /* 9P header has well-known size of 7 bytes */
151 .tag
= cpu_to_le16(tag
)
154 g_assert_cmpint(total_size
, <=, UINT32_MAX
- size
);
156 hdr
.size
= cpu_to_le32(total_size
);
158 g_assert_cmpint(total_size
, <=, P9_MAX_SIZE
);
161 req
->t_size
= total_size
;
162 req
->t_msg
= guest_alloc(alloc
, req
->t_size
);
163 v9fs_memwrite(req
, &hdr
, 7);
168 static void v9fs_req_send(P9Req
*req
)
170 QVirtio9P
*v9p
= req
->v9p
;
172 req
->r_msg
= guest_alloc(alloc
, P9_MAX_SIZE
);
173 req
->free_head
= qvirtqueue_add(v9p
->vq
, req
->t_msg
, req
->t_size
, false,
175 qvirtqueue_add(v9p
->vq
, req
->r_msg
, P9_MAX_SIZE
, true, false);
176 qvirtqueue_kick(v9p
->vdev
, v9p
->vq
, req
->free_head
);
180 static const char *rmessage_name(uint8_t id
)
183 id
== P9_RLERROR
? "RLERROR" :
184 id
== P9_RVERSION
? "RVERSION" :
185 id
== P9_RATTACH
? "RATTACH" :
186 id
== P9_RWALK
? "RWALK" :
187 id
== P9_RLOPEN
? "RLOPEN" :
188 id
== P9_RWRITE
? "RWRITE" :
189 id
== P9_RFLUSH
? "RFLUSH" :
193 static void v9fs_req_wait_for_reply(P9Req
*req
, uint32_t *len
)
195 QVirtio9P
*v9p
= req
->v9p
;
197 qvirtio_wait_used_elem(v9p
->vdev
, v9p
->vq
, req
->free_head
, len
,
198 QVIRTIO_9P_TIMEOUT_US
);
201 static void v9fs_req_recv(P9Req
*req
, uint8_t id
)
205 v9fs_memread(req
, &hdr
, 7);
206 hdr
.size
= ldl_le_p(&hdr
.size
);
207 hdr
.tag
= lduw_le_p(&hdr
.tag
);
209 g_assert_cmpint(hdr
.size
, >=, 7);
210 g_assert_cmpint(hdr
.size
, <=, P9_MAX_SIZE
);
211 g_assert_cmpint(hdr
.tag
, ==, req
->tag
);
214 g_printerr("Received response %d (%s) instead of %d (%s)\n",
215 hdr
.id
, rmessage_name(hdr
.id
), id
, rmessage_name(id
));
217 if (hdr
.id
== P9_RLERROR
) {
219 v9fs_uint32_read(req
, &err
);
220 g_printerr("Rlerror has errno %d (%s)\n", err
, strerror(err
));
223 g_assert_cmpint(hdr
.id
, ==, id
);
226 static void v9fs_req_free(P9Req
*req
)
228 guest_free(alloc
, req
->t_msg
);
229 guest_free(alloc
, req
->r_msg
);
233 /* size[4] Rlerror tag[2] ecode[4] */
234 static void v9fs_rlerror(P9Req
*req
, uint32_t *err
)
236 v9fs_req_recv(req
, P9_RLERROR
);
237 v9fs_uint32_read(req
, err
);
241 /* size[4] Tversion tag[2] msize[4] version[s] */
242 static P9Req
*v9fs_tversion(QVirtio9P
*v9p
, uint32_t msize
, const char *version
,
246 uint32_t body_size
= 4;
247 uint16_t string_size
= v9fs_string_size(version
);
249 g_assert_cmpint(body_size
, <=, UINT32_MAX
- string_size
);
250 body_size
+= string_size
;
251 req
= v9fs_req_init(v9p
, body_size
, P9_TVERSION
, tag
);
253 v9fs_uint32_write(req
, msize
);
254 v9fs_string_write(req
, version
);
259 /* size[4] Rversion tag[2] msize[4] version[s] */
260 static void v9fs_rversion(P9Req
*req
, uint16_t *len
, char **version
)
264 v9fs_req_recv(req
, P9_RVERSION
);
265 v9fs_uint32_read(req
, &msize
);
267 g_assert_cmpint(msize
, ==, P9_MAX_SIZE
);
269 if (len
|| version
) {
270 v9fs_string_read(req
, len
, version
);
276 /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
277 static P9Req
*v9fs_tattach(QVirtio9P
*v9p
, uint32_t fid
, uint32_t n_uname
,
280 const char *uname
= ""; /* ignored by QEMU */
281 const char *aname
= ""; /* ignored by QEMU */
282 P9Req
*req
= v9fs_req_init(v9p
, 4 + 4 + 2 + 2 + 4, P9_TATTACH
, tag
);
284 v9fs_uint32_write(req
, fid
);
285 v9fs_uint32_write(req
, P9_NOFID
);
286 v9fs_string_write(req
, uname
);
287 v9fs_string_write(req
, aname
);
288 v9fs_uint32_write(req
, n_uname
);
293 typedef char v9fs_qid
[13];
295 /* size[4] Rattach tag[2] qid[13] */
296 static void v9fs_rattach(P9Req
*req
, v9fs_qid
*qid
)
298 v9fs_req_recv(req
, P9_RATTACH
);
300 v9fs_memread(req
, qid
, 13);
305 /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
306 static P9Req
*v9fs_twalk(QVirtio9P
*v9p
, uint32_t fid
, uint32_t newfid
,
307 uint16_t nwname
, char *const wnames
[], uint16_t tag
)
311 uint32_t body_size
= 4 + 4 + 2;
313 for (i
= 0; i
< nwname
; i
++) {
314 uint16_t wname_size
= v9fs_string_size(wnames
[i
]);
316 g_assert_cmpint(body_size
, <=, UINT32_MAX
- wname_size
);
317 body_size
+= wname_size
;
319 req
= v9fs_req_init(v9p
, body_size
, P9_TWALK
, tag
);
320 v9fs_uint32_write(req
, fid
);
321 v9fs_uint32_write(req
, newfid
);
322 v9fs_uint16_write(req
, nwname
);
323 for (i
= 0; i
< nwname
; i
++) {
324 v9fs_string_write(req
, wnames
[i
]);
330 /* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */
331 static void v9fs_rwalk(P9Req
*req
, uint16_t *nwqid
, v9fs_qid
**wqid
)
333 uint16_t local_nwqid
;
335 v9fs_req_recv(req
, P9_RWALK
);
336 v9fs_uint16_read(req
, &local_nwqid
);
338 *nwqid
= local_nwqid
;
341 *wqid
= g_malloc(local_nwqid
* 13);
342 v9fs_memread(req
, *wqid
, local_nwqid
* 13);
347 /* size[4] Tlopen tag[2] fid[4] flags[4] */
348 static P9Req
*v9fs_tlopen(QVirtio9P
*v9p
, uint32_t fid
, uint32_t flags
,
353 req
= v9fs_req_init(v9p
, 4 + 4, P9_TLOPEN
, tag
);
354 v9fs_uint32_write(req
, fid
);
355 v9fs_uint32_write(req
, flags
);
360 /* size[4] Rlopen tag[2] qid[13] iounit[4] */
361 static void v9fs_rlopen(P9Req
*req
, v9fs_qid
*qid
, uint32_t *iounit
)
363 v9fs_req_recv(req
, P9_RLOPEN
);
365 v9fs_memread(req
, qid
, 13);
367 v9fs_memskip(req
, 13);
370 v9fs_uint32_read(req
, iounit
);
375 /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
376 static P9Req
*v9fs_twrite(QVirtio9P
*v9p
, uint32_t fid
, uint64_t offset
,
377 uint32_t count
, const void *data
, uint16_t tag
)
380 uint32_t body_size
= 4 + 8 + 4;
382 g_assert_cmpint(body_size
, <=, UINT32_MAX
- count
);
384 req
= v9fs_req_init(v9p
, body_size
, P9_TWRITE
, tag
);
385 v9fs_uint32_write(req
, fid
);
386 v9fs_uint64_write(req
, offset
);
387 v9fs_uint32_write(req
, count
);
388 v9fs_memwrite(req
, data
, count
);
393 /* size[4] Rwrite tag[2] count[4] */
394 static void v9fs_rwrite(P9Req
*req
, uint32_t *count
)
396 v9fs_req_recv(req
, P9_RWRITE
);
398 v9fs_uint32_read(req
, count
);
403 /* size[4] Tflush tag[2] oldtag[2] */
404 static P9Req
*v9fs_tflush(QVirtio9P
*v9p
, uint16_t oldtag
, uint16_t tag
)
408 req
= v9fs_req_init(v9p
, 2, P9_TFLUSH
, tag
);
409 v9fs_uint32_write(req
, oldtag
);
414 /* size[4] Rflush tag[2] */
415 static void v9fs_rflush(P9Req
*req
)
417 v9fs_req_recv(req
, P9_RFLUSH
);
421 static void fs_version(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
423 QVirtio9P
*v9p
= obj
;
425 const char *version
= "9P2000.L";
427 char *server_version
;
430 req
= v9fs_tversion(v9p
, P9_MAX_SIZE
, version
, P9_NOTAG
);
431 v9fs_req_wait_for_reply(req
, NULL
);
432 v9fs_rversion(req
, &server_len
, &server_version
);
434 g_assert_cmpmem(server_version
, server_len
, version
, strlen(version
));
436 g_free(server_version
);
439 static void fs_attach(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
441 QVirtio9P
*v9p
= obj
;
445 fs_version(v9p
, NULL
, t_alloc
);
446 req
= v9fs_tattach(v9p
, 0, getuid(), 0);
447 v9fs_req_wait_for_reply(req
, NULL
);
448 v9fs_rattach(req
, NULL
);
451 static void fs_walk(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
453 QVirtio9P
*v9p
= obj
;
455 char *wnames
[P9_MAXWELEM
];
461 for (i
= 0; i
< P9_MAXWELEM
; i
++) {
462 wnames
[i
] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE
, i
);
465 fs_attach(v9p
, NULL
, t_alloc
);
466 req
= v9fs_twalk(v9p
, 0, 1, P9_MAXWELEM
, wnames
, 0);
467 v9fs_req_wait_for_reply(req
, NULL
);
468 v9fs_rwalk(req
, &nwqid
, &wqid
);
470 g_assert_cmpint(nwqid
, ==, P9_MAXWELEM
);
472 for (i
= 0; i
< P9_MAXWELEM
; i
++) {
479 static void fs_walk_no_slash(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
481 QVirtio9P
*v9p
= obj
;
483 char *const wnames
[] = { g_strdup(" /") };
487 fs_attach(v9p
, NULL
, t_alloc
);
488 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
489 v9fs_req_wait_for_reply(req
, NULL
);
490 v9fs_rlerror(req
, &err
);
492 g_assert_cmpint(err
, ==, ENOENT
);
497 static void fs_walk_dotdot(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
499 QVirtio9P
*v9p
= obj
;
501 char *const wnames
[] = { g_strdup("..") };
502 v9fs_qid root_qid
, *wqid
;
505 fs_version(v9p
, NULL
, t_alloc
);
506 req
= v9fs_tattach(v9p
, 0, getuid(), 0);
507 v9fs_req_wait_for_reply(req
, NULL
);
508 v9fs_rattach(req
, &root_qid
);
510 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
511 v9fs_req_wait_for_reply(req
, NULL
);
512 v9fs_rwalk(req
, NULL
, &wqid
); /* We now we'll get one qid */
514 g_assert_cmpmem(&root_qid
, 13, wqid
[0], 13);
520 static void fs_lopen(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
522 QVirtio9P
*v9p
= obj
;
524 char *const wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE
) };
527 fs_attach(v9p
, NULL
, t_alloc
);
528 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
529 v9fs_req_wait_for_reply(req
, NULL
);
530 v9fs_rwalk(req
, NULL
, NULL
);
532 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
533 v9fs_req_wait_for_reply(req
, NULL
);
534 v9fs_rlopen(req
, NULL
, NULL
);
539 static void fs_write(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
541 QVirtio9P
*v9p
= obj
;
543 static const uint32_t write_count
= P9_MAX_SIZE
/ 2;
544 char *const wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE
) };
545 char *buf
= g_malloc0(write_count
);
549 fs_attach(v9p
, NULL
, t_alloc
);
550 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
551 v9fs_req_wait_for_reply(req
, NULL
);
552 v9fs_rwalk(req
, NULL
, NULL
);
554 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
555 v9fs_req_wait_for_reply(req
, NULL
);
556 v9fs_rlopen(req
, NULL
, NULL
);
558 req
= v9fs_twrite(v9p
, 1, 0, write_count
, buf
, 0);
559 v9fs_req_wait_for_reply(req
, NULL
);
560 v9fs_rwrite(req
, &count
);
561 g_assert_cmpint(count
, ==, write_count
);
567 static void fs_flush_success(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
569 QVirtio9P
*v9p
= obj
;
571 char *const wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE
) };
572 P9Req
*req
, *flush_req
;
574 uint8_t should_block
;
576 fs_attach(v9p
, NULL
, t_alloc
);
577 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
578 v9fs_req_wait_for_reply(req
, NULL
);
579 v9fs_rwalk(req
, NULL
, NULL
);
581 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
582 v9fs_req_wait_for_reply(req
, NULL
);
583 v9fs_rlopen(req
, NULL
, NULL
);
585 /* This will cause the 9p server to try to write data to the backend,
586 * until the write request gets cancelled.
589 req
= v9fs_twrite(v9p
, 1, 0, sizeof(should_block
), &should_block
, 0);
591 flush_req
= v9fs_tflush(v9p
, req
->tag
, 1);
593 /* The write request is supposed to be flushed: the server should just
594 * mark the write request as used and reply to the flush request.
596 v9fs_req_wait_for_reply(req
, &reply_len
);
597 g_assert_cmpint(reply_len
, ==, 0);
599 v9fs_rflush(flush_req
);
604 static void fs_flush_ignored(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
606 QVirtio9P
*v9p
= obj
;
608 char *const wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE
) };
609 P9Req
*req
, *flush_req
;
611 uint8_t should_block
;
613 fs_attach(v9p
, NULL
, t_alloc
);
614 req
= v9fs_twalk(v9p
, 0, 1, 1, wnames
, 0);
615 v9fs_req_wait_for_reply(req
, NULL
);
616 v9fs_rwalk(req
, NULL
, NULL
);
618 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
619 v9fs_req_wait_for_reply(req
, NULL
);
620 v9fs_rlopen(req
, NULL
, NULL
);
622 /* This will cause the write request to complete right away, before it
623 * could be actually cancelled.
626 req
= v9fs_twrite(v9p
, 1, 0, sizeof(should_block
), &should_block
, 0);
628 flush_req
= v9fs_tflush(v9p
, req
->tag
, 1);
630 /* The write request is supposed to complete. The server should
631 * reply to the write request and the flush request.
633 v9fs_req_wait_for_reply(req
, NULL
);
634 v9fs_rwrite(req
, &count
);
635 g_assert_cmpint(count
, ==, sizeof(should_block
));
636 v9fs_rflush(flush_req
);
641 static void register_virtio_9p_test(void)
643 qos_add_test("config", "virtio-9p", pci_config
, NULL
);
644 qos_add_test("fs/version/basic", "virtio-9p", fs_version
, NULL
);
645 qos_add_test("fs/attach/basic", "virtio-9p", fs_attach
, NULL
);
646 qos_add_test("fs/walk/basic", "virtio-9p", fs_walk
, NULL
);
647 qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash
,
649 qos_add_test("fs/walk/dotdot_from_root", "virtio-9p",
650 fs_walk_dotdot
, NULL
);
651 qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen
, NULL
);
652 qos_add_test("fs/write/basic", "virtio-9p", fs_write
, NULL
);
653 qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success
,
655 qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored
,
659 libqos_init(register_virtio_9p_test
);