1
0
mirror of https://gitlab.com/qemu-project/qemu synced 2024-07-09 04:27:12 +00:00

9pfs: performance, Windows host prep, tests restructure

* Highlight of this PR is Linus Heckemann's GHashTable patch which
   brings massive general performance improvements of 9p server
   somewhere between factor 6 .. 12.
 
 * Bin Meng's g_mkdir patch is a preparatory patch for upcoming
   Windows host support of 9p server.
 
 * The rest of the patches in this PR are 9p test code restructuring
   and refactoring changes to improve readability and to ease
   maintenance of 9p test code on the long-term.
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEltjREM96+AhPiFkBNMK1h2Wkc5UFAmNWbs8XHHFlbXVfb3Nz
 QGNydWRlYnl0ZS5jb20ACgkQNMK1h2Wkc5V4cw/8CqoSJqoJixlP8kAGDYWq3CgF
 SKd09rIzLSWyyufAoZr1TqLwRrvEQRlZJSpL4fGvRpQLv0IQCu4x59ohHRob25Tm
 Fe7IxYBNuBwLW4yu+Y7FaujeGoYAi9Qw5q4ijq3/aSSiIeuXySKB2JmW71CQ+Tbe
 uwivsnMtWzQ7qsNwrtXYbxDs7UGkdsiW2sEQUS26GMApAXZoB+38hwtTW2Y9MOrC
 58JuZza/fUVPzo0V1D0ggRawb5O2VTF5fz8aGFG4FvoyIW6DDZFSfnyre9QxivOl
 5McWwSQ/D04vdEK9ornGPYr9YRGuP8g07p1EW9OfKeie4I41e9pS3UminK5lVCgo
 SfBHzz96efM5XR+Wnl4yVKowivmTqjwUU8lDqW2eB/7YBRuYUzrpxYe//UPv4q1J
 zaQV3pgwFAVkVJCnkcLCa1JQbH581bXSsuRlDdYqoRYfyzXoxbywNjvn9BXE0PrG
 WRecS//GyN3GVZYxMwb3H052110pYsYIg2YZ2H4QiqCwpEHHvy+L/ZXm19vbDm7B
 GYJQPUK8/y0NGwZsUYcUSx1TWlU9ZPwrbqZfv7e7+B6FL4VNjdaqb8PvS9admWSq
 LOSzrVVIus+nb7tP99d1Fb6oRyCy3x8E48gTr5UtTJHC4SAw/OBJmem6GOc/D490
 H7Dq8Y27qsQ6fT7iPm8=
 =MxSG
 -----END PGP SIGNATURE-----

Merge tag 'pull-9p-20221024' of https://github.com/cschoenebeck/qemu into staging

9pfs: performance, Windows host prep, tests restructure

* Highlight of this PR is Linus Heckemann's GHashTable patch which
  brings massive general performance improvements of 9p server
  somewhere between factor 6 .. 12.

* Bin Meng's g_mkdir patch is a preparatory patch for upcoming
  Windows host support of 9p server.

* The rest of the patches in this PR are 9p test code restructuring
  and refactoring changes to improve readability and to ease
  maintenance of 9p test code on the long-term.

# -----BEGIN PGP SIGNATURE-----
#
# iQJLBAABCgA1FiEEltjREM96+AhPiFkBNMK1h2Wkc5UFAmNWbs8XHHFlbXVfb3Nz
# QGNydWRlYnl0ZS5jb20ACgkQNMK1h2Wkc5V4cw/8CqoSJqoJixlP8kAGDYWq3CgF
# SKd09rIzLSWyyufAoZr1TqLwRrvEQRlZJSpL4fGvRpQLv0IQCu4x59ohHRob25Tm
# Fe7IxYBNuBwLW4yu+Y7FaujeGoYAi9Qw5q4ijq3/aSSiIeuXySKB2JmW71CQ+Tbe
# uwivsnMtWzQ7qsNwrtXYbxDs7UGkdsiW2sEQUS26GMApAXZoB+38hwtTW2Y9MOrC
# 58JuZza/fUVPzo0V1D0ggRawb5O2VTF5fz8aGFG4FvoyIW6DDZFSfnyre9QxivOl
# 5McWwSQ/D04vdEK9ornGPYr9YRGuP8g07p1EW9OfKeie4I41e9pS3UminK5lVCgo
# SfBHzz96efM5XR+Wnl4yVKowivmTqjwUU8lDqW2eB/7YBRuYUzrpxYe//UPv4q1J
# zaQV3pgwFAVkVJCnkcLCa1JQbH581bXSsuRlDdYqoRYfyzXoxbywNjvn9BXE0PrG
# WRecS//GyN3GVZYxMwb3H052110pYsYIg2YZ2H4QiqCwpEHHvy+L/ZXm19vbDm7B
# GYJQPUK8/y0NGwZsUYcUSx1TWlU9ZPwrbqZfv7e7+B6FL4VNjdaqb8PvS9admWSq
# LOSzrVVIus+nb7tP99d1Fb6oRyCy3x8E48gTr5UtTJHC4SAw/OBJmem6GOc/D490
# H7Dq8Y27qsQ6fT7iPm8=
# =MxSG
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 24 Oct 2022 06:54:07 EDT
# gpg:                using RSA key 96D8D110CF7AF8084F88590134C2B58765A47395
# gpg:                issuer "qemu_oss@crudebyte.com"
# gpg: Good signature from "Christian Schoenebeck <qemu_oss@crudebyte.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: ECAB 1A45 4014 1413 BA38  4926 30DB 47C3 A012 D5F4
#      Subkey fingerprint: 96D8 D110 CF7A F808 4F88  5901 34C2 B587 65A4 7395

* tag 'pull-9p-20221024' of https://github.com/cschoenebeck/qemu: (23 commits)
  tests/9p: remove unnecessary g_strdup() calls
  tests/9p: merge v9fs_tunlinkat() and do_unlinkat()
  tests/9p: merge v9fs_tlink() and do_hardlink()
  tests/9p: merge v9fs_tsymlink() and do_symlink()
  tests/9p: merge v9fs_tlcreate() and do_lcreate()
  tests/9p: merge v9fs_tmkdir() and do_mkdir()
  tests/9p: convert v9fs_tflush() to declarative arguments
  tests/9p: simplify callers of twrite()
  tests/9p: convert v9fs_twrite() to declarative arguments
  tests/9p: simplify callers of tlopen()
  tests/9p: convert v9fs_tlopen() to declarative arguments
  tests/9p: simplify callers of treaddir()
  tests/9p: convert v9fs_treaddir() to declarative arguments
  tests/9p: simplify callers of tgetattr()
  tests/9p: convert v9fs_tgetattr() to declarative arguments
  tests/9p: simplify callers of tattach()
  tests/9p: merge v9fs_tattach(), do_attach(), do_attach_rqid()
  tests/9p: merge v9fs_tversion() and do_version()
  tests/9p: simplify callers of twalk()
  tests/9p: merge *walk*() functions
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-10-24 14:27:12 -04:00
commit e750a7ace4
7 changed files with 1867 additions and 1177 deletions

View File

@ -10,6 +10,7 @@
*/
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include <sys/resource.h>
#include <getopt.h>
#include <syslog.h>
@ -639,7 +640,7 @@ static int do_create_others(int type, struct iovec *iovec)
if (retval < 0) {
goto err_out;
}
retval = mkdir(path.data, mode);
retval = g_mkdir(path.data, mode);
break;
case T_SYMLINK:
retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);

View File

@ -256,7 +256,8 @@ static size_t v9fs_string_size(V9fsString *str)
}
/*
* returns 0 if fid got re-opened, 1 if not, < 0 on error */
* returns 0 if fid got re-opened, 1 if not, < 0 on error
*/
static int coroutine_fn v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
{
int err = 1;
@ -282,33 +283,32 @@ static V9fsFidState *coroutine_fn get_fid(V9fsPDU *pdu, int32_t fid)
V9fsFidState *f;
V9fsState *s = pdu->s;
QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
if (f) {
BUG_ON(f->clunked);
if (f->fid == fid) {
/*
* Update the fid ref upfront so that
* we don't get reclaimed when we yield
* in open later.
*/
f->ref++;
/*
* check whether we need to reopen the
* file. We might have closed the fd
* while trying to free up some file
* descriptors.
*/
err = v9fs_reopen_fid(pdu, f);
if (err < 0) {
f->ref--;
return NULL;
}
/*
* Mark the fid as referenced so that the LRU
* reclaim won't close the file descriptor
*/
f->flags |= FID_REFERENCED;
return f;
/*
* Update the fid ref upfront so that
* we don't get reclaimed when we yield
* in open later.
*/
f->ref++;
/*
* check whether we need to reopen the
* file. We might have closed the fd
* while trying to free up some file
* descriptors.
*/
err = v9fs_reopen_fid(pdu, f);
if (err < 0) {
f->ref--;
return NULL;
}
/*
* Mark the fid as referenced so that the LRU
* reclaim won't close the file descriptor
*/
f->flags |= FID_REFERENCED;
return f;
}
return NULL;
}
@ -317,12 +317,11 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
{
V9fsFidState *f;
QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
if (f) {
/* If fid is already there return NULL */
BUG_ON(f->clunked);
if (f->fid == fid) {
return NULL;
}
return NULL;
}
f = g_new0(V9fsFidState, 1);
f->fid = fid;
@ -333,7 +332,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
* reclaim won't close the file descriptor
*/
f->flags |= FID_REFERENCED;
QSIMPLEQ_INSERT_TAIL(&s->fid_list, f, next);
g_hash_table_insert(s->fids, GINT_TO_POINTER(fid), f);
v9fs_readdir_init(s->proto_version, &f->fs.dir);
v9fs_readdir_init(s->proto_version, &f->fs_reclaim.dir);
@ -424,12 +423,12 @@ static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid)
{
V9fsFidState *fidp;
QSIMPLEQ_FOREACH(fidp, &s->fid_list, next) {
if (fidp->fid == fid) {
QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next);
fidp->clunked = true;
return fidp;
}
/* TODO: Use g_hash_table_steal_extended() instead? */
fidp = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
if (fidp) {
g_hash_table_remove(s->fids, GINT_TO_POINTER(fid));
fidp->clunked = true;
return fidp;
}
return NULL;
}
@ -439,10 +438,15 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
int reclaim_count = 0;
V9fsState *s = pdu->s;
V9fsFidState *f;
GHashTableIter iter;
gpointer fid;
g_hash_table_iter_init(&iter, s->fids);
QSLIST_HEAD(, V9fsFidState) reclaim_list =
QSLIST_HEAD_INITIALIZER(reclaim_list);
QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) {
/*
* Unlink fids cannot be reclaimed. Check
* for them and skip them. Also skip fids
@ -514,72 +518,85 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
}
}
/*
* This is used when a path is removed from the directory tree. Any
* fids that still reference it must not be closed from then on, since
* they cannot be reopened.
*/
static int coroutine_fn v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path)
{
int err;
int err = 0;
V9fsState *s = pdu->s;
V9fsFidState *fidp, *fidp_next;
V9fsFidState *fidp;
gpointer fid;
GHashTableIter iter;
/*
* The most common case is probably that we have exactly one
* fid for the given path, so preallocate exactly one.
*/
g_autoptr(GArray) to_reopen = g_array_sized_new(FALSE, FALSE,
sizeof(V9fsFidState *), 1);
gint i;
fidp = QSIMPLEQ_FIRST(&s->fid_list);
if (!fidp) {
return 0;
}
g_hash_table_iter_init(&iter, s->fids);
/*
* v9fs_reopen_fid() can yield : a reference on the fid must be held
* to ensure its pointer remains valid and we can safely pass it to
* QSIMPLEQ_NEXT(). The corresponding put_fid() can also yield so
* we must keep a reference on the next fid as well. So the logic here
* is to get a reference on a fid and only put it back during the next
* iteration after we could get a reference on the next fid. Start with
* the first one.
* We iterate over the fid table looking for the entries we need
* to reopen, and store them in to_reopen. This is because
* v9fs_reopen_fid() and put_fid() yield. This allows the fid table
* to be modified in the meantime, invalidating our iterator.
*/
for (fidp->ref++; fidp; fidp = fidp_next) {
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &fidp)) {
if (fidp->path.size == path->size &&
!memcmp(fidp->path.data, path->data, path->size)) {
/* Mark the fid non reclaimable. */
fidp->flags |= FID_NON_RECLAIMABLE;
/* reopen the file/dir if already closed */
err = v9fs_reopen_fid(pdu, fidp);
if (err < 0) {
put_fid(pdu, fidp);
return err;
}
}
fidp_next = QSIMPLEQ_NEXT(fidp, next);
if (fidp_next) {
/*
* Ensure the next fid survives a potential clunk request during
* put_fid() below and v9fs_reopen_fid() in the next iteration.
* Ensure the fid survives a potential clunk request during
* v9fs_reopen_fid or put_fid.
*/
fidp_next->ref++;
fidp->ref++;
fidp->flags |= FID_NON_RECLAIMABLE;
g_array_append_val(to_reopen, fidp);
}
/* We're done with this fid */
put_fid(pdu, fidp);
}
return 0;
for (i = 0; i < to_reopen->len; i++) {
fidp = g_array_index(to_reopen, V9fsFidState*, i);
/* reopen the file/dir if already closed */
err = v9fs_reopen_fid(pdu, fidp);
if (err < 0) {
break;
}
}
for (i = 0; i < to_reopen->len; i++) {
put_fid(pdu, g_array_index(to_reopen, V9fsFidState*, i));
}
return err;
}
static void coroutine_fn virtfs_reset(V9fsPDU *pdu)
{
V9fsState *s = pdu->s;
V9fsFidState *fidp;
GList *freeing;
/*
* Get a list of all the values (fid states) in the table, which
* we then...
*/
g_autoptr(GList) fids = g_hash_table_get_values(s->fids);
/* Free all fids */
while (!QSIMPLEQ_EMPTY(&s->fid_list)) {
/* Get fid */
fidp = QSIMPLEQ_FIRST(&s->fid_list);
/* ... remove from the table, taking over ownership. */
g_hash_table_steal_all(s->fids);
/*
* This allows us to release our references to them asynchronously without
* iterating over the hash table and risking iterator invalidation
* through concurrent modifications.
*/
for (freeing = fids; freeing; freeing = freeing->next) {
fidp = freeing->data;
fidp->ref++;
/* Clunk fid */
QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next);
fidp->clunked = true;
put_fid(pdu, fidp);
}
}
@ -3205,6 +3222,8 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
V9fsFidState *tfidp;
V9fsState *s = pdu->s;
V9fsFidState *dirfidp = NULL;
GHashTableIter iter;
gpointer fid;
v9fs_path_init(&new_path);
if (newdirfid != -1) {
@ -3238,11 +3257,13 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
if (err < 0) {
goto out;
}
/*
* Fixup fid's pointing to the old name to
* start pointing to the new name
*/
QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) {
g_hash_table_iter_init(&iter, s->fids);
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) {
if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
/* replace the name */
v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data));
@ -3320,6 +3341,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
V9fsPath oldpath, newpath;
V9fsState *s = pdu->s;
int err;
GHashTableIter iter;
gpointer fid;
v9fs_path_init(&oldpath);
v9fs_path_init(&newpath);
@ -3336,7 +3359,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
* Fixup fid's pointing to the old name to
* start pointing to the new name
*/
QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) {
g_hash_table_iter_init(&iter, s->fids);
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) {
if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) {
/* replace the name */
v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
@ -4226,7 +4250,7 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
s->ctx.fmode = fse->fmode;
s->ctx.dmode = fse->dmode;
QSIMPLEQ_INIT(&s->fid_list);
s->fids = g_hash_table_new(NULL, NULL);
qemu_co_rwlock_init(&s->rename_lock);
if (s->ops->init(&s->ctx, errp) < 0) {
@ -4286,6 +4310,10 @@ void v9fs_device_unrealize_common(V9fsState *s)
if (s->ctx.fst) {
fsdev_throttle_cleanup(s->ctx.fst);
}
if (s->fids) {
g_hash_table_destroy(s->fids);
s->fids = NULL;
}
g_free(s->tag);
qp_table_destroy(&s->qpd_table);
qp_table_destroy(&s->qpp_table);

View File

@ -339,7 +339,7 @@ typedef struct {
struct V9fsState {
QLIST_HEAD(, V9fsPDU) free_list;
QLIST_HEAD(, V9fsPDU) active_list;
QSIMPLEQ_HEAD(, V9fsFidState) fid_list;
GHashTable *fids;
FileOperations *ops;
FsContext ctx;
char *tag;

View File

@ -34,6 +34,7 @@ libqos_srcs = files(
'tpci200.c',
'virtio.c',
'virtio-9p.c',
'virtio-9p-client.c',
'virtio-balloon.c',
'virtio-blk.c',
'vhost-user-blk.c',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,494 @@
/*
* 9P network client for VirtIO 9P test cases (based on QTest)
*
* Copyright (c) 2014 SUSE LINUX Products GmbH
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#ifndef TESTS_LIBQOS_VIRTIO_9P_CLIENT_H
#define TESTS_LIBQOS_VIRTIO_9P_CLIENT_H
#include "hw/9pfs/9p.h"
#include "hw/9pfs/9p-synth.h"
#include "virtio-9p.h"
#include "qgraph.h"
#include "tests/qtest/libqtest-single.h"
#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
typedef struct {
QTestState *qts;
QVirtio9P *v9p;
uint16_t tag;
uint64_t t_msg;
uint32_t t_size;
uint64_t r_msg;
/* No r_size, it is hardcoded to P9_MAX_SIZE */
size_t t_off;
size_t r_off;
uint32_t free_head;
} P9Req;
/* type[1] version[4] path[8] */
typedef char v9fs_qid[13];
typedef struct v9fs_attr {
uint64_t valid;
v9fs_qid qid;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint64_t nlink;
uint64_t rdev;
uint64_t size;
uint64_t blksize;
uint64_t blocks;
uint64_t atime_sec;
uint64_t atime_nsec;
uint64_t mtime_sec;
uint64_t mtime_nsec;
uint64_t ctime_sec;
uint64_t ctime_nsec;
uint64_t btime_sec;
uint64_t btime_nsec;
uint64_t gen;
uint64_t data_version;
} v9fs_attr;
#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */
struct V9fsDirent {
v9fs_qid qid;
uint64_t offset;
uint8_t type;
char *name;
struct V9fsDirent *next;
};
/* options for 'Twalk' 9p request */
typedef struct TWalkOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID of directory from where walk should start (optional) */
uint32_t fid;
/* file ID for target directory being walked to (optional) */
uint32_t newfid;
/* low level variant of path to walk to (optional) */
uint16_t nwname;
char **wnames;
/* high level variant of path to walk to (optional) */
const char *path;
/* data being received from 9p server as 'Rwalk' response (optional) */
struct {
uint16_t *nwqid;
v9fs_qid **wqid;
} rwalk;
/* only send Twalk request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TWalkOpt;
/* result of 'Twalk' 9p request */
typedef struct TWalkRes {
/* file ID of target directory been walked to */
uint32_t newfid;
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TWalkRes;
/* options for 'Tversion' 9p request */
typedef struct TVersionOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* maximum message size that can be handled by client (optional) */
uint32_t msize;
/* protocol version (optional) */
const char *version;
/* only send Tversion request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TVersionOpt;
/* result of 'Tversion' 9p request */
typedef struct TVersionRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TVersionRes;
/* options for 'Tattach' 9p request */
typedef struct TAttachOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID to be associated with root of file tree (optional) */
uint32_t fid;
/* numerical uid of user being introduced to server (optional) */
uint32_t n_uname;
/* data being received from 9p server as 'Rattach' response (optional) */
struct {
/* server's idea of the root of the file tree */
v9fs_qid *qid;
} rattach;
/* only send Tattach request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TAttachOpt;
/* result of 'Tattach' 9p request */
typedef struct TAttachRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TAttachRes;
/* options for 'Tgetattr' 9p request */
typedef struct TGetAttrOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID of file/dir whose attributes shall be retrieved (required) */
uint32_t fid;
/* bitmask indicating attribute fields to be retrieved (optional) */
uint64_t request_mask;
/* data being received from 9p server as 'Rgetattr' response (optional) */
struct {
v9fs_attr *attr;
} rgetattr;
/* only send Tgetattr request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TGetAttrOpt;
/* result of 'Tgetattr' 9p request */
typedef struct TGetAttrRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TGetAttrRes;
/* options for 'Treaddir' 9p request */
typedef struct TReadDirOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID of directory whose entries shall be retrieved (required) */
uint32_t fid;
/* offset in entries stream, i.e. for multiple requests (optional) */
uint64_t offset;
/* maximum bytes to be returned by server (required) */
uint32_t count;
/* data being received from 9p server as 'Rreaddir' response (optional) */
struct {
uint32_t *count;
uint32_t *nentries;
struct V9fsDirent **entries;
} rreaddir;
/* only send Treaddir request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TReadDirOpt;
/* result of 'Treaddir' 9p request */
typedef struct TReadDirRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TReadDirRes;
/* options for 'Tlopen' 9p request */
typedef struct TLOpenOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID of file / directory to be opened (required) */
uint32_t fid;
/* Linux open(2) flags such as O_RDONLY, O_RDWR, O_WRONLY (optional) */
uint32_t flags;
/* data being received from 9p server as 'Rlopen' response (optional) */
struct {
v9fs_qid *qid;
uint32_t *iounit;
} rlopen;
/* only send Tlopen request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TLOpenOpt;
/* result of 'Tlopen' 9p request */
typedef struct TLOpenRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TLOpenRes;
/* options for 'Twrite' 9p request */
typedef struct TWriteOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* file ID of file to write to (required) */
uint32_t fid;
/* start position of write from beginning of file (optional) */
uint64_t offset;
/* how many bytes to write */
uint32_t count;
/* data to be written */
const void *data;
/* only send Twrite request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TWriteOpt;
/* result of 'Twrite' 9p request */
typedef struct TWriteRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
/* amount of bytes written */
uint32_t count;
} TWriteRes;
/* options for 'Tflush' 9p request */
typedef struct TFlushOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* message to flush (required) */
uint16_t oldtag;
/* only send Tflush request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TFlushOpt;
/* result of 'Tflush' 9p request */
typedef struct TFlushRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TFlushRes;
/* options for 'Tmkdir' 9p request */
typedef struct TMkdirOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* low level variant of directory where new one shall be created */
uint32_t dfid;
/* high-level variant of directory where new one shall be created */
const char *atPath;
/* New directory's name (required) */
const char *name;
/* Linux mkdir(2) mode bits (optional) */
uint32_t mode;
/* effective group ID of caller */
uint32_t gid;
/* data being received from 9p server as 'Rmkdir' response (optional) */
struct {
/* QID of newly created directory */
v9fs_qid *qid;
} rmkdir;
/* only send Tmkdir request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TMkdirOpt;
/* result of 'TMkdir' 9p request */
typedef struct TMkdirRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TMkdirRes;
/* options for 'Tlcreate' 9p request */
typedef struct TlcreateOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* low-level variant of directory where new file shall be created */
uint32_t fid;
/* high-level variant of directory where new file shall be created */
const char *atPath;
/* name of new file (required) */
const char *name;
/* Linux kernel intent bits */
uint32_t flags;
/* Linux create(2) mode bits */
uint32_t mode;
/* effective group ID of caller */
uint32_t gid;
/* data being received from 9p server as 'Rlcreate' response (optional) */
struct {
v9fs_qid *qid;
uint32_t *iounit;
} rlcreate;
/* only send Tlcreate request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TlcreateOpt;
/* result of 'Tlcreate' 9p request */
typedef struct TlcreateRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TlcreateRes;
/* options for 'Tsymlink' 9p request */
typedef struct TsymlinkOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* low-level variant of directory where symlink shall be created */
uint32_t fid;
/* high-level variant of directory where symlink shall be created */
const char *atPath;
/* name of symlink (required) */
const char *name;
/* where symlink will point to (required) */
const char *symtgt;
/* effective group ID of caller */
uint32_t gid;
/* data being received from 9p server as 'Rsymlink' response (optional) */
struct {
v9fs_qid *qid;
} rsymlink;
/* only send Tsymlink request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TsymlinkOpt;
/* result of 'Tsymlink' 9p request */
typedef struct TsymlinkRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TsymlinkRes;
/* options for 'Tlink' 9p request */
typedef struct TlinkOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* low-level variant of directory where hard link shall be created */
uint32_t dfid;
/* high-level variant of directory where hard link shall be created */
const char *atPath;
/* low-level variant of target referenced by new hard link */
uint32_t fid;
/* high-level variant of target referenced by new hard link */
const char *toPath;
/* name of hard link (required) */
const char *name;
/* only send Tlink request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TlinkOpt;
/* result of 'Tlink' 9p request */
typedef struct TlinkRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TlinkRes;
/* options for 'Tunlinkat' 9p request */
typedef struct TunlinkatOpt {
/* 9P client being used (mandatory) */
QVirtio9P *client;
/* user supplied tag number being returned with response (optional) */
uint16_t tag;
/* low-level variant of directory where name shall be unlinked */
uint32_t dirfd;
/* high-level variant of directory where name shall be unlinked */
const char *atPath;
/* name of directory entry to be unlinked (required) */
const char *name;
/* Linux unlinkat(2) flags */
uint32_t flags;
/* only send Tunlinkat request but not wait for a reply? (optional) */
bool requestOnly;
/* do we expect an Rlerror response, if yes which error code? (optional) */
uint32_t expectErr;
} TunlinkatOpt;
/* result of 'Tunlinkat' 9p request */
typedef struct TunlinkatRes {
/* if requestOnly was set: request object for further processing */
P9Req *req;
} TunlinkatRes;
void v9fs_set_allocator(QGuestAllocator *t_alloc);
void v9fs_memwrite(P9Req *req, const void *addr, size_t len);
void v9fs_memskip(P9Req *req, size_t len);
void v9fs_memread(P9Req *req, void *addr, size_t len);
void v9fs_uint8_read(P9Req *req, uint8_t *val);
void v9fs_uint16_write(P9Req *req, uint16_t val);
void v9fs_uint16_read(P9Req *req, uint16_t *val);
void v9fs_uint32_write(P9Req *req, uint32_t val);
void v9fs_uint64_write(P9Req *req, uint64_t val);
void v9fs_uint32_read(P9Req *req, uint32_t *val);
void v9fs_uint64_read(P9Req *req, uint64_t *val);
uint16_t v9fs_string_size(const char *string);
void v9fs_string_write(P9Req *req, const char *string);
void v9fs_string_read(P9Req *req, uint16_t *len, char **string);
P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
uint16_t tag);
void v9fs_req_send(P9Req *req);
void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len);
void v9fs_req_recv(P9Req *req, uint8_t id);
void v9fs_req_free(P9Req *req);
void v9fs_rlerror(P9Req *req, uint32_t *err);
TVersionRes v9fs_tversion(TVersionOpt);
void v9fs_rversion(P9Req *req, uint16_t *len, char **version);
TAttachRes v9fs_tattach(TAttachOpt);
void v9fs_rattach(P9Req *req, v9fs_qid *qid);
TWalkRes v9fs_twalk(TWalkOpt opt);
void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid);
TGetAttrRes v9fs_tgetattr(TGetAttrOpt);
void v9fs_rgetattr(P9Req *req, v9fs_attr *attr);
TReadDirRes v9fs_treaddir(TReadDirOpt);
void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
struct V9fsDirent **entries);
void v9fs_free_dirents(struct V9fsDirent *e);
TLOpenRes v9fs_tlopen(TLOpenOpt);
void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit);
TWriteRes v9fs_twrite(TWriteOpt);
void v9fs_rwrite(P9Req *req, uint32_t *count);
TFlushRes v9fs_tflush(TFlushOpt);
void v9fs_rflush(P9Req *req);
TMkdirRes v9fs_tmkdir(TMkdirOpt);
void v9fs_rmkdir(P9Req *req, v9fs_qid *qid);
TlcreateRes v9fs_tlcreate(TlcreateOpt);
void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit);
TsymlinkRes v9fs_tsymlink(TsymlinkOpt);
void v9fs_rsymlink(P9Req *req, v9fs_qid *qid);
TlinkRes v9fs_tlink(TlinkOpt);
void v9fs_rlink(P9Req *req);
TunlinkatRes v9fs_tunlinkat(TunlinkatOpt);
void v9fs_runlinkat(P9Req *req);
#endif

File diff suppressed because it is too large Load Diff