mirror of
https://github.com/xemu-project/xemu.git
synced 2025-02-21 12:53:01 +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:
commit
e750a7ace4
@ -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);
|
||||
|
196
hw/9pfs/9p.c
196
hw/9pfs/9p.c
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
1049
tests/qtest/libqos/virtio-9p-client.c
Normal file
1049
tests/qtest/libqos/virtio-9p-client.c
Normal file
File diff suppressed because it is too large
Load Diff
494
tests/qtest/libqos/virtio-9p-client.h
Normal file
494
tests/qtest/libqos/virtio-9p-client.h
Normal 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
Loading…
x
Reference in New Issue
Block a user