xref: /openbmc/qemu/tests/qtest/virtio-9p-test.c (revision 4e6b1384)
1 /*
2  * QTest testcase for VirtIO 9P
3  *
4  * Copyright (c) 2014 SUSE LINUX Products GmbH
5  *
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.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "libqtest-single.h"
12 #include "qemu/module.h"
13 #include "hw/9pfs/9p.h"
14 #include "hw/9pfs/9p-synth.h"
15 #include "libqos/virtio-9p.h"
16 #include "libqos/qgraph.h"
17 
18 #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
19 static QGuestAllocator *alloc;
20 
21 static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
22 {
23     QVirtio9P *v9p = obj;
24     alloc = t_alloc;
25     size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
26     char *tag;
27     int i;
28 
29     g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG));
30 
31     tag = g_malloc(tag_len);
32     for (i = 0; i < tag_len; i++) {
33         tag[i] = qvirtio_config_readb(v9p->vdev, i + 2);
34     }
35     g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
36     g_free(tag);
37 }
38 
39 #define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
40 
41 typedef struct {
42     QTestState *qts;
43     QVirtio9P *v9p;
44     uint16_t tag;
45     uint64_t t_msg;
46     uint32_t t_size;
47     uint64_t r_msg;
48     /* No r_size, it is hardcoded to P9_MAX_SIZE */
49     size_t t_off;
50     size_t r_off;
51     uint32_t free_head;
52 } P9Req;
53 
54 static void v9fs_memwrite(P9Req *req, const void *addr, size_t len)
55 {
56     qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len);
57     req->t_off += len;
58 }
59 
60 static void v9fs_memskip(P9Req *req, size_t len)
61 {
62     req->r_off += len;
63 }
64 
65 static void v9fs_memread(P9Req *req, void *addr, size_t len)
66 {
67     qtest_memread(req->qts, req->r_msg + req->r_off, addr, len);
68     req->r_off += len;
69 }
70 
71 static void v9fs_uint16_write(P9Req *req, uint16_t val)
72 {
73     uint16_t le_val = cpu_to_le16(val);
74 
75     v9fs_memwrite(req, &le_val, 2);
76 }
77 
78 static void v9fs_uint16_read(P9Req *req, uint16_t *val)
79 {
80     v9fs_memread(req, val, 2);
81     le16_to_cpus(val);
82 }
83 
84 static void v9fs_uint32_write(P9Req *req, uint32_t val)
85 {
86     uint32_t le_val = cpu_to_le32(val);
87 
88     v9fs_memwrite(req, &le_val, 4);
89 }
90 
91 static void v9fs_uint64_write(P9Req *req, uint64_t val)
92 {
93     uint64_t le_val = cpu_to_le64(val);
94 
95     v9fs_memwrite(req, &le_val, 8);
96 }
97 
98 static void v9fs_uint32_read(P9Req *req, uint32_t *val)
99 {
100     v9fs_memread(req, val, 4);
101     le32_to_cpus(val);
102 }
103 
104 /* len[2] string[len] */
105 static uint16_t v9fs_string_size(const char *string)
106 {
107     size_t len = strlen(string);
108 
109     g_assert_cmpint(len, <=, UINT16_MAX - 2);
110 
111     return 2 + len;
112 }
113 
114 static void v9fs_string_write(P9Req *req, const char *string)
115 {
116     int len = strlen(string);
117 
118     g_assert_cmpint(len, <=, UINT16_MAX);
119 
120     v9fs_uint16_write(req, (uint16_t) len);
121     v9fs_memwrite(req, string, len);
122 }
123 
124 static void v9fs_string_read(P9Req *req, uint16_t *len, char **string)
125 {
126     uint16_t local_len;
127 
128     v9fs_uint16_read(req, &local_len);
129     if (len) {
130         *len = local_len;
131     }
132     if (string) {
133         *string = g_malloc(local_len);
134         v9fs_memread(req, *string, local_len);
135     } else {
136         v9fs_memskip(req, local_len);
137     }
138 }
139 
140  typedef struct {
141     uint32_t size;
142     uint8_t id;
143     uint16_t tag;
144 } QEMU_PACKED P9Hdr;
145 
146 static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
147                             uint16_t tag)
148 {
149     P9Req *req = g_new0(P9Req, 1);
150     uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */
151     P9Hdr hdr = {
152         .id = id,
153         .tag = cpu_to_le16(tag)
154     };
155 
156     g_assert_cmpint(total_size, <=, UINT32_MAX - size);
157     total_size += size;
158     hdr.size = cpu_to_le32(total_size);
159 
160     g_assert_cmpint(total_size, <=, P9_MAX_SIZE);
161 
162     req->qts = global_qtest;
163     req->v9p = v9p;
164     req->t_size = total_size;
165     req->t_msg = guest_alloc(alloc, req->t_size);
166     v9fs_memwrite(req, &hdr, 7);
167     req->tag = tag;
168     return req;
169 }
170 
171 static void v9fs_req_send(P9Req *req)
172 {
173     QVirtio9P *v9p = req->v9p;
174 
175     req->r_msg = guest_alloc(alloc, P9_MAX_SIZE);
176     req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size,
177                                     false, true);
178     qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
179     qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head);
180     req->t_off = 0;
181 }
182 
183 static const char *rmessage_name(uint8_t id)
184 {
185     return
186         id == P9_RLERROR ? "RLERROR" :
187         id == P9_RVERSION ? "RVERSION" :
188         id == P9_RATTACH ? "RATTACH" :
189         id == P9_RWALK ? "RWALK" :
190         id == P9_RLOPEN ? "RLOPEN" :
191         id == P9_RWRITE ? "RWRITE" :
192         id == P9_RFLUSH ? "RFLUSH" :
193         "<unknown>";
194 }
195 
196 static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len)
197 {
198     QVirtio9P *v9p = req->v9p;
199 
200     qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len,
201                            QVIRTIO_9P_TIMEOUT_US);
202 }
203 
204 static void v9fs_req_recv(P9Req *req, uint8_t id)
205 {
206     P9Hdr hdr;
207 
208     v9fs_memread(req, &hdr, 7);
209     hdr.size = ldl_le_p(&hdr.size);
210     hdr.tag = lduw_le_p(&hdr.tag);
211 
212     g_assert_cmpint(hdr.size, >=, 7);
213     g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE);
214     g_assert_cmpint(hdr.tag, ==, req->tag);
215 
216     if (hdr.id != id) {
217         g_printerr("Received response %d (%s) instead of %d (%s)\n",
218                    hdr.id, rmessage_name(hdr.id), id, rmessage_name(id));
219 
220         if (hdr.id == P9_RLERROR) {
221             uint32_t err;
222             v9fs_uint32_read(req, &err);
223             g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err));
224         }
225     }
226     g_assert_cmpint(hdr.id, ==, id);
227 }
228 
229 static void v9fs_req_free(P9Req *req)
230 {
231     guest_free(alloc, req->t_msg);
232     guest_free(alloc, req->r_msg);
233     g_free(req);
234 }
235 
236 /* size[4] Rlerror tag[2] ecode[4] */
237 static void v9fs_rlerror(P9Req *req, uint32_t *err)
238 {
239     v9fs_req_recv(req, P9_RLERROR);
240     v9fs_uint32_read(req, err);
241     v9fs_req_free(req);
242 }
243 
244 /* size[4] Tversion tag[2] msize[4] version[s] */
245 static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version,
246                             uint16_t tag)
247 {
248     P9Req *req;
249     uint32_t body_size = 4;
250     uint16_t string_size = v9fs_string_size(version);
251 
252     g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
253     body_size += string_size;
254     req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag);
255 
256     v9fs_uint32_write(req, msize);
257     v9fs_string_write(req, version);
258     v9fs_req_send(req);
259     return req;
260 }
261 
262 /* size[4] Rversion tag[2] msize[4] version[s] */
263 static void v9fs_rversion(P9Req *req, uint16_t *len, char **version)
264 {
265     uint32_t msize;
266 
267     v9fs_req_recv(req, P9_RVERSION);
268     v9fs_uint32_read(req, &msize);
269 
270     g_assert_cmpint(msize, ==, P9_MAX_SIZE);
271 
272     if (len || version) {
273         v9fs_string_read(req, len, version);
274     }
275 
276     v9fs_req_free(req);
277 }
278 
279 /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
280 static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname,
281                            uint16_t tag)
282 {
283     const char *uname = ""; /* ignored by QEMU */
284     const char *aname = ""; /* ignored by QEMU */
285     P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag);
286 
287     v9fs_uint32_write(req, fid);
288     v9fs_uint32_write(req, P9_NOFID);
289     v9fs_string_write(req, uname);
290     v9fs_string_write(req, aname);
291     v9fs_uint32_write(req, n_uname);
292     v9fs_req_send(req);
293     return req;
294 }
295 
296 typedef char v9fs_qid[13];
297 
298 /* size[4] Rattach tag[2] qid[13] */
299 static void v9fs_rattach(P9Req *req, v9fs_qid *qid)
300 {
301     v9fs_req_recv(req, P9_RATTACH);
302     if (qid) {
303         v9fs_memread(req, qid, 13);
304     }
305     v9fs_req_free(req);
306 }
307 
308 /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
309 static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid,
310                          uint16_t nwname, char *const wnames[], uint16_t tag)
311 {
312     P9Req *req;
313     int i;
314     uint32_t body_size = 4 + 4 + 2;
315 
316     for (i = 0; i < nwname; i++) {
317         uint16_t wname_size = v9fs_string_size(wnames[i]);
318 
319         g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size);
320         body_size += wname_size;
321     }
322     req = v9fs_req_init(v9p,  body_size, P9_TWALK, tag);
323     v9fs_uint32_write(req, fid);
324     v9fs_uint32_write(req, newfid);
325     v9fs_uint16_write(req, nwname);
326     for (i = 0; i < nwname; i++) {
327         v9fs_string_write(req, wnames[i]);
328     }
329     v9fs_req_send(req);
330     return req;
331 }
332 
333 /* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */
334 static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid)
335 {
336     uint16_t local_nwqid;
337 
338     v9fs_req_recv(req, P9_RWALK);
339     v9fs_uint16_read(req, &local_nwqid);
340     if (nwqid) {
341         *nwqid = local_nwqid;
342     }
343     if (wqid) {
344         *wqid = g_malloc(local_nwqid * 13);
345         v9fs_memread(req, *wqid, local_nwqid * 13);
346     }
347     v9fs_req_free(req);
348 }
349 
350 /* size[4] Tlopen tag[2] fid[4] flags[4] */
351 static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags,
352                           uint16_t tag)
353 {
354     P9Req *req;
355 
356     req = v9fs_req_init(v9p,  4 + 4, P9_TLOPEN, tag);
357     v9fs_uint32_write(req, fid);
358     v9fs_uint32_write(req, flags);
359     v9fs_req_send(req);
360     return req;
361 }
362 
363 /* size[4] Rlopen tag[2] qid[13] iounit[4] */
364 static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
365 {
366     v9fs_req_recv(req, P9_RLOPEN);
367     if (qid) {
368         v9fs_memread(req, qid, 13);
369     } else {
370         v9fs_memskip(req, 13);
371     }
372     if (iounit) {
373         v9fs_uint32_read(req, iounit);
374     }
375     v9fs_req_free(req);
376 }
377 
378 /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
379 static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
380                           uint32_t count, const void *data, uint16_t tag)
381 {
382     P9Req *req;
383     uint32_t body_size = 4 + 8 + 4;
384 
385     g_assert_cmpint(body_size, <=, UINT32_MAX - count);
386     body_size += count;
387     req = v9fs_req_init(v9p,  body_size, P9_TWRITE, tag);
388     v9fs_uint32_write(req, fid);
389     v9fs_uint64_write(req, offset);
390     v9fs_uint32_write(req, count);
391     v9fs_memwrite(req, data, count);
392     v9fs_req_send(req);
393     return req;
394 }
395 
396 /* size[4] Rwrite tag[2] count[4] */
397 static void v9fs_rwrite(P9Req *req, uint32_t *count)
398 {
399     v9fs_req_recv(req, P9_RWRITE);
400     if (count) {
401         v9fs_uint32_read(req, count);
402     }
403     v9fs_req_free(req);
404 }
405 
406 /* size[4] Tflush tag[2] oldtag[2] */
407 static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag)
408 {
409     P9Req *req;
410 
411     req = v9fs_req_init(v9p,  2, P9_TFLUSH, tag);
412     v9fs_uint32_write(req, oldtag);
413     v9fs_req_send(req);
414     return req;
415 }
416 
417 /* size[4] Rflush tag[2] */
418 static void v9fs_rflush(P9Req *req)
419 {
420     v9fs_req_recv(req, P9_RFLUSH);
421     v9fs_req_free(req);
422 }
423 
424 static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
425 {
426     QVirtio9P *v9p = obj;
427     alloc = t_alloc;
428     const char *version = "9P2000.L";
429     uint16_t server_len;
430     char *server_version;
431     P9Req *req;
432 
433     req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG);
434     v9fs_req_wait_for_reply(req, NULL);
435     v9fs_rversion(req, &server_len, &server_version);
436 
437     g_assert_cmpmem(server_version, server_len, version, strlen(version));
438 
439     g_free(server_version);
440 }
441 
442 static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
443 {
444     QVirtio9P *v9p = obj;
445     alloc = t_alloc;
446     P9Req *req;
447 
448     fs_version(v9p, NULL, t_alloc);
449     req = v9fs_tattach(v9p, 0, getuid(), 0);
450     v9fs_req_wait_for_reply(req, NULL);
451     v9fs_rattach(req, NULL);
452 }
453 
454 static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
455 {
456     QVirtio9P *v9p = obj;
457     alloc = t_alloc;
458     char *wnames[P9_MAXWELEM];
459     uint16_t nwqid;
460     v9fs_qid *wqid;
461     int i;
462     P9Req *req;
463 
464     for (i = 0; i < P9_MAXWELEM; i++) {
465         wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
466     }
467 
468     fs_attach(v9p, NULL, t_alloc);
469     req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0);
470     v9fs_req_wait_for_reply(req, NULL);
471     v9fs_rwalk(req, &nwqid, &wqid);
472 
473     g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
474 
475     for (i = 0; i < P9_MAXWELEM; i++) {
476         g_free(wnames[i]);
477     }
478 
479     g_free(wqid);
480 }
481 
482 static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
483 {
484     QVirtio9P *v9p = obj;
485     alloc = t_alloc;
486     char *const wnames[] = { g_strdup(" /") };
487     P9Req *req;
488     uint32_t err;
489 
490     fs_attach(v9p, NULL, t_alloc);
491     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
492     v9fs_req_wait_for_reply(req, NULL);
493     v9fs_rlerror(req, &err);
494 
495     g_assert_cmpint(err, ==, ENOENT);
496 
497     g_free(wnames[0]);
498 }
499 
500 static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
501 {
502     QVirtio9P *v9p = obj;
503     alloc = t_alloc;
504     char *const wnames[] = { g_strdup("..") };
505     v9fs_qid root_qid, *wqid;
506     P9Req *req;
507 
508     fs_version(v9p, NULL, t_alloc);
509     req = v9fs_tattach(v9p, 0, getuid(), 0);
510     v9fs_req_wait_for_reply(req, NULL);
511     v9fs_rattach(req, &root_qid);
512 
513     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
514     v9fs_req_wait_for_reply(req, NULL);
515     v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */
516 
517     g_assert_cmpmem(&root_qid, 13, wqid[0], 13);
518 
519     g_free(wqid);
520     g_free(wnames[0]);
521 }
522 
523 static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
524 {
525     QVirtio9P *v9p = obj;
526     alloc = t_alloc;
527     char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
528     P9Req *req;
529 
530     fs_attach(v9p, NULL, t_alloc);
531     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
532     v9fs_req_wait_for_reply(req, NULL);
533     v9fs_rwalk(req, NULL, NULL);
534 
535     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
536     v9fs_req_wait_for_reply(req, NULL);
537     v9fs_rlopen(req, NULL, NULL);
538 
539     g_free(wnames[0]);
540 }
541 
542 static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
543 {
544     QVirtio9P *v9p = obj;
545     alloc = t_alloc;
546     static const uint32_t write_count = P9_MAX_SIZE / 2;
547     char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
548     char *buf = g_malloc0(write_count);
549     uint32_t count;
550     P9Req *req;
551 
552     fs_attach(v9p, NULL, t_alloc);
553     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
554     v9fs_req_wait_for_reply(req, NULL);
555     v9fs_rwalk(req, NULL, NULL);
556 
557     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
558     v9fs_req_wait_for_reply(req, NULL);
559     v9fs_rlopen(req, NULL, NULL);
560 
561     req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0);
562     v9fs_req_wait_for_reply(req, NULL);
563     v9fs_rwrite(req, &count);
564     g_assert_cmpint(count, ==, write_count);
565 
566     g_free(buf);
567     g_free(wnames[0]);
568 }
569 
570 static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
571 {
572     QVirtio9P *v9p = obj;
573     alloc = t_alloc;
574     char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
575     P9Req *req, *flush_req;
576     uint32_t reply_len;
577     uint8_t should_block;
578 
579     fs_attach(v9p, NULL, t_alloc);
580     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
581     v9fs_req_wait_for_reply(req, NULL);
582     v9fs_rwalk(req, NULL, NULL);
583 
584     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
585     v9fs_req_wait_for_reply(req, NULL);
586     v9fs_rlopen(req, NULL, NULL);
587 
588     /* This will cause the 9p server to try to write data to the backend,
589      * until the write request gets cancelled.
590      */
591     should_block = 1;
592     req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
593 
594     flush_req = v9fs_tflush(v9p, req->tag, 1);
595 
596     /* The write request is supposed to be flushed: the server should just
597      * mark the write request as used and reply to the flush request.
598      */
599     v9fs_req_wait_for_reply(req, &reply_len);
600     g_assert_cmpint(reply_len, ==, 0);
601     v9fs_req_free(req);
602     v9fs_rflush(flush_req);
603 
604     g_free(wnames[0]);
605 }
606 
607 static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
608 {
609     QVirtio9P *v9p = obj;
610     alloc = t_alloc;
611     char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
612     P9Req *req, *flush_req;
613     uint32_t count;
614     uint8_t should_block;
615 
616     fs_attach(v9p, NULL, t_alloc);
617     req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
618     v9fs_req_wait_for_reply(req, NULL);
619     v9fs_rwalk(req, NULL, NULL);
620 
621     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
622     v9fs_req_wait_for_reply(req, NULL);
623     v9fs_rlopen(req, NULL, NULL);
624 
625     /* This will cause the write request to complete right away, before it
626      * could be actually cancelled.
627      */
628     should_block = 0;
629     req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
630 
631     flush_req = v9fs_tflush(v9p, req->tag, 1);
632 
633     /* The write request is supposed to complete. The server should
634      * reply to the write request and the flush request.
635      */
636     v9fs_req_wait_for_reply(req, NULL);
637     v9fs_rwrite(req, &count);
638     g_assert_cmpint(count, ==, sizeof(should_block));
639     v9fs_rflush(flush_req);
640 
641     g_free(wnames[0]);
642 }
643 
644 static void register_virtio_9p_test(void)
645 {
646     qos_add_test("config", "virtio-9p", pci_config, NULL);
647     qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL);
648     qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL);
649     qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL);
650     qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash,
651                  NULL);
652     qos_add_test("fs/walk/dotdot_from_root", "virtio-9p",
653                  fs_walk_dotdot, NULL);
654     qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL);
655     qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL);
656     qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success,
657                  NULL);
658     qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored,
659                  NULL);
660 }
661 
662 libqos_init(register_virtio_9p_test);
663