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