* [RFC 0/3] tests/vhost-user-fs-test: add vhost-user-fs test case
@ 2019-10-25 10:01 Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file Stefan Hajnoczi
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-10-25 10:01 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, virtio-fs, Stefan Hajnoczi,
Paolo Bonzini, vgoyal
This patch series adds an automated test for the vhost-user-fs device using
virtiofsd. This makes low-level testing of FUSE messages and even VIRTIO
possible. These things can be hard to do inside a normal guest environment
where such tests require building kernel modules and can result in kernel
panics.
To get a feel for how vhost-user-fs-test.c test cases work, here is an example:
/* Create file on host and check its contents and metadata in guest */
static void test_file_from_host(void *parent, void *arg, QGuestAllocator *alloc)
{
g_autofree gchar *filename = g_strdup_printf("%s/%s", shared_dir, "foo");
const char *str = "This is a test\n";
char buf[strlen(str)];
QVirtioFS *vfs = parent;
struct fuse_entry_out entry;
int32_t error;
uint64_t nodeid;
uint64_t fh;
ssize_t nread;
gboolean ok;
SKIP_TEST_IF_CROSS_ENDIAN();
/* Create the test file in the shared directory */
ok = g_file_set_contents(filename, str, strlen(str), NULL);
g_assert(ok);
fuse_init(vfs);
error = fuse_lookup(vfs, FUSE_ROOT_ID, "foo", &entry);
g_assert_cmpint(error, ==, 0);
g_assert_cmpint(guest64(entry.attr.size), ==, strlen(str));
nodeid = guest64(entry.nodeid);
error = fuse_open(vfs, nodeid, O_RDONLY, &fh);
g_assert_cmpint(error, ==, 0);
nread = fuse_read(vfs, fh, 0, buf, sizeof(buf));
g_assert_cmpint(nread, ==, sizeof(buf));
g_assert_cmpint(memcmp(buf, str, sizeof(buf)), ==, 0);
fuse_release(vfs, fh);
fuse_forget(vfs, nodeid);
}
This patch series is based on "[PATCH v4 00/16] libqos: add VIRTIO PCI 1.0
support" and the https://gitlab.com/virtio-fs/qemu virtio-fs-dev branch. I
expect conflicts and will resend again once these dependencies have landed in
qemu.git/master.
Stefan Hajnoczi (3):
WIP virtiofsd: import Linux <fuse.h> header file
qgraph: add an "after" test callback function
tests/vhost-user-fs-test: add vhost-user-fs test case
tests/Makefile.include | 8 +-
contrib/virtiofsd/fuse_lowlevel.h | 2 +-
.../standard-headers/linux/fuse.h | 0
tests/libqos/qgraph.h | 2 +
tests/libqos/qgraph_internal.h | 1 +
tests/libqos/virtio-fs.h | 46 ++
contrib/virtiofsd/fuse_loop_mt.c | 2 +-
contrib/virtiofsd/fuse_lowlevel.c | 2 +-
contrib/virtiofsd/fuse_virtio.c | 2 +-
tests/libqos/qgraph.c | 1 +
tests/libqos/virtio-fs.c | 104 +++
tests/qos-test.c | 6 +
tests/vhost-user-fs-test.c | 660 ++++++++++++++++++
scripts/update-linux-headers.sh | 3 +-
14 files changed, 832 insertions(+), 7 deletions(-)
rename contrib/virtiofsd/fuse_kernel.h => include/standard-headers/linux/fuse.h (100%)
create mode 100644 tests/libqos/virtio-fs.h
create mode 100644 tests/libqos/virtio-fs.c
create mode 100644 tests/vhost-user-fs-test.c
--
2.21.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file
2019-10-25 10:01 [RFC 0/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
@ 2019-10-25 10:01 ` Stefan Hajnoczi
2019-10-26 21:49 ` Michael S. Tsirkin
2019-10-25 10:01 ` [RFC 2/3] qgraph: add an "after" test callback function Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2 siblings, 1 reply; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-10-25 10:01 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, virtio-fs, Stefan Hajnoczi,
Paolo Bonzini, vgoyal
tests/vhost-user-fs-test.c needs fuse.h. The private copy that
virtiofsd has can be replaced with a properly imported file using
update-linux-headers.sh.
TODO rerun update-linux-headers.sh with upstream kernel tree!
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
contrib/virtiofsd/fuse_lowlevel.h | 2 +-
.../fuse_kernel.h => include/standard-headers/linux/fuse.h | 0
contrib/virtiofsd/fuse_loop_mt.c | 2 +-
contrib/virtiofsd/fuse_lowlevel.c | 2 +-
contrib/virtiofsd/fuse_virtio.c | 2 +-
| 3 ++-
6 files changed, 6 insertions(+), 5 deletions(-)
rename contrib/virtiofsd/fuse_kernel.h => include/standard-headers/linux/fuse.h (100%)
diff --git a/contrib/virtiofsd/fuse_lowlevel.h b/contrib/virtiofsd/fuse_lowlevel.h
index 79fb30a1c2..a8c92ff7e0 100644
--- a/contrib/virtiofsd/fuse_lowlevel.h
+++ b/contrib/virtiofsd/fuse_lowlevel.h
@@ -23,7 +23,7 @@
#endif
#include "fuse_common.h"
-#include "fuse_kernel.h"
+#include "standard-headers/linux/fuse.h"
#include <utime.h>
#include <fcntl.h>
diff --git a/contrib/virtiofsd/fuse_kernel.h b/include/standard-headers/linux/fuse.h
similarity index 100%
rename from contrib/virtiofsd/fuse_kernel.h
rename to include/standard-headers/linux/fuse.h
diff --git a/contrib/virtiofsd/fuse_loop_mt.c b/contrib/virtiofsd/fuse_loop_mt.c
index 2000a8902a..af7b501fac 100644
--- a/contrib/virtiofsd/fuse_loop_mt.c
+++ b/contrib/virtiofsd/fuse_loop_mt.c
@@ -11,7 +11,7 @@
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include "fuse_misc.h"
-#include "fuse_kernel.h"
+#include "standard-headers/linux/fuse.h"
#include "fuse_virtio.h"
#include <stdio.h>
diff --git a/contrib/virtiofsd/fuse_lowlevel.c b/contrib/virtiofsd/fuse_lowlevel.c
index 78ccfe3a27..c1a901cb4d 100644
--- a/contrib/virtiofsd/fuse_lowlevel.c
+++ b/contrib/virtiofsd/fuse_lowlevel.c
@@ -10,7 +10,7 @@
*/
#include "fuse_i.h"
-#include "fuse_kernel.h"
+#include "standard-headers/linux/fuse.h"
#include "fuse_opt.h"
#include "fuse_misc.h"
#include "fuse_virtio.h"
diff --git a/contrib/virtiofsd/fuse_virtio.c b/contrib/virtiofsd/fuse_virtio.c
index 533ef24bb7..7a0d0b2603 100644
--- a/contrib/virtiofsd/fuse_virtio.c
+++ b/contrib/virtiofsd/fuse_virtio.c
@@ -15,7 +15,7 @@
#include "qapi/error.h"
#include "fuse_i.h"
-#include "fuse_kernel.h"
+#include "standard-headers/linux/fuse.h"
#include "fuse_misc.h"
#include "fuse_opt.h"
#include "fuse_virtio.h"
--git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index f76d77363b..1a627ccd73 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -184,7 +184,8 @@ EOF
rm -rf "$output/include/standard-headers/linux"
mkdir -p "$output/include/standard-headers/linux"
-for i in "$tmpdir"/include/linux/*virtio*.h \
+for i in "$tmpdir/include/linux/fuse.h" \
+ "$tmpdir"/include/linux/*virtio*.h \
"$tmpdir/include/linux/qemu_fw_cfg.h" \
"$tmpdir/include/linux/input.h" \
"$tmpdir/include/linux/input-event-codes.h" \
--
2.21.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC 2/3] qgraph: add an "after" test callback function
2019-10-25 10:01 [RFC 0/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file Stefan Hajnoczi
@ 2019-10-25 10:01 ` Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2 siblings, 0 replies; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-10-25 10:01 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, virtio-fs, Stefan Hajnoczi,
Paolo Bonzini, vgoyal
Add a callback that runs after a test completes. This will be used for
cleanup.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/libqos/qgraph.h | 2 ++
tests/libqos/qgraph_internal.h | 1 +
tests/libqos/qgraph.c | 1 +
tests/qos-test.c | 6 ++++++
4 files changed, 10 insertions(+)
diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h
index 3a25dda4b2..5e73a00e4a 100644
--- a/tests/libqos/qgraph.h
+++ b/tests/libqos/qgraph.h
@@ -47,6 +47,7 @@ typedef void (*QOSStartFunct) (QOSGraphObject *object);
/* Test options functions */
typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
+typedef void (*QOSAfterTest) (void *arg);
/**
* SECTION: qgraph.h
@@ -341,6 +342,7 @@ struct QOSGraphTestOptions {
* additional parameters to the command line
* and modify the argument to the test function.
*/
+ QOSAfterTest after; /* executed after the test */
bool subprocess; /* run the test in a subprocess */
};
diff --git a/tests/libqos/qgraph_internal.h b/tests/libqos/qgraph_internal.h
index f4734c8681..5e1131f06c 100644
--- a/tests/libqos/qgraph_internal.h
+++ b/tests/libqos/qgraph_internal.h
@@ -68,6 +68,7 @@ struct QOSGraphNode {
QOSTestFunc function;
void *arg;
QOSBeforeTest before;
+ QOSAfterTest after;
bool subprocess;
} test;
} u;
diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c
index 7a7ae2a19e..f3e792a509 100644
--- a/tests/libqos/qgraph.c
+++ b/tests/libqos/qgraph.c
@@ -603,6 +603,7 @@ void qos_add_test(const char *name, const char *interface,
assert(!opts->edge.size_arg);
node->u.test.before = opts->before;
+ node->u.test.after = opts->after;
node->u.test.subprocess = opts->subprocess;
node->available = true;
add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
diff --git a/tests/qos-test.c b/tests/qos-test.c
index fd70d73ea5..fa77f661c6 100644
--- a/tests/qos-test.c
+++ b/tests/qos-test.c
@@ -273,6 +273,7 @@ void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
* 3) call all nodes constructor and get_driver/get_device depending on edge,
* start the hardware (*_device_enable functions)
* 4) start test
+ * 5) @after test function as defined in the given QOSGraphTestOptions
*/
static void run_one_test(const void *arg)
{
@@ -296,6 +297,11 @@ static void run_one_test(const void *arg)
obj = qos_allocate_objects(global_qtest, &alloc);
test_node->u.test.function(obj, test_arg, alloc);
+
+ /* After test */
+ if (test_node->u.test.after) {
+ test_node->u.test.after(test_arg);
+ }
}
static void subprocess_run_one_test(const void *arg)
--
2.21.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case
2019-10-25 10:01 [RFC 0/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 2/3] qgraph: add an "after" test callback function Stefan Hajnoczi
@ 2019-10-25 10:01 ` Stefan Hajnoczi
2019-10-29 0:36 ` Dr. David Alan Gilbert
2 siblings, 1 reply; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-10-25 10:01 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, virtio-fs, Stefan Hajnoczi,
Paolo Bonzini, vgoyal
Add a test case for the vhost-user-fs device. There are two
limitations:
1. This test only runs when invoked as root. The virtiofsd vhost-user
device backend currently requires root in order to maintain accurate
file system ownership information (uid/gid).
2. Cross-endian is not supported because virtiofsd currently only
supports same-endian configurations.
This test uses FUSE_INIT, FUSE_LOOKUP, FUSE_OPEN, FUSE_CREATE,
FUSE_READ, FUSE_WRITE, FUSE_RELEASE, and FUSE_FORGET messages to perform
basic sanity testing.
This test can be expanded on in the future to perform low-level
virtio-fs testing, including invalid FUSE messages that are hard to
generate from a real guest.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
tests/Makefile.include | 8 +-
tests/libqos/virtio-fs.h | 46 +++
tests/libqos/virtio-fs.c | 104 ++++++
tests/vhost-user-fs-test.c | 660 +++++++++++++++++++++++++++++++++++++
4 files changed, 816 insertions(+), 2 deletions(-)
create mode 100644 tests/libqos/virtio-fs.h
create mode 100644 tests/libqos/virtio-fs.c
create mode 100644 tests/vhost-user-fs-test.c
diff --git a/tests/Makefile.include b/tests/Makefile.include
index fde8a0c5ef..0472565d96 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -718,6 +718,7 @@ qos-test-obj-y += tests/libqos/sdhci.o
qos-test-obj-y += tests/libqos/tpci200.o
qos-test-obj-y += tests/libqos/virtio.o
qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o
+qos-test-obj-$(CONFIG_VHOST_USER_FS) += tests/libqos/virtio-fs.o
qos-test-obj-y += tests/libqos/virtio-balloon.o
qos-test-obj-y += tests/libqos/virtio-blk.o
qos-test-obj-y += tests/libqos/virtio-mmio.o
@@ -759,6 +760,7 @@ qos-test-obj-y += tests/spapr-phb-test.o
qos-test-obj-y += tests/tmp105-test.o
qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y)
+qos-test-obj-$(CONFIG_VHOST_USER_FS) += tests/vhost-user-fs-test.o
qos-test-obj-y += tests/virtio-test.o
qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o
qos-test-obj-y += tests/virtio-blk-test.o
@@ -907,7 +909,8 @@ endef
$(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: %-softmmu/all $(check-qtest-y)
$(call do_test_human,$(check-qtest-$*-y) $(check-qtest-generic-y), \
QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
- QTEST_QEMU_IMG=qemu-img$(EXESUF))
+ QTEST_QEMU_IMG=qemu-img$(EXESUF) \
+ QTEST_VIRTIOFSD=virtiofsd$(EXESUF))
check-unit: $(check-unit-y)
$(call do_test_human, $^)
@@ -920,7 +923,8 @@ check-speed: $(check-speed-y)
$(patsubst %, check-report-qtest-%.tap, $(QTEST_TARGETS)): check-report-qtest-%.tap: %-softmmu/all $(check-qtest-y)
$(call do_test_tap, $(check-qtest-$*-y) $(check-qtest-generic-y), \
QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
- QTEST_QEMU_IMG=qemu-img$(EXESUF))
+ QTEST_QEMU_IMG=qemu-img$(EXESUF) \
+ QTEST_VIRTIOFSD=virtiofsd$(EXESUF))
check-report-unit.tap: $(check-unit-y)
$(call do_test_tap,$^)
diff --git a/tests/libqos/virtio-fs.h b/tests/libqos/virtio-fs.h
new file mode 100644
index 0000000000..40289ba283
--- /dev/null
+++ b/tests/libqos/virtio-fs.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifer: GPL-2.0-or-later */
+/*
+ * libqos virtio-fs device driver
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_FS_H
+#define TESTS_LIBQOS_VIRTIO_FS_H
+
+#include "libqos/virtio-pci.h"
+
+#define VIRTIO_FS_TAG "myfs"
+
+typedef struct {
+ QVirtioDevice *vdev;
+ QGuestAllocator *alloc;
+ QVirtQueue *hiprio_vq;
+ QVirtQueue *request_vq;
+ uint64_t unique_counter;
+} QVirtioFS;
+
+typedef struct {
+ QVirtioPCIDevice pci_vdev;
+ QVirtioFS vfs;
+} QVirtioFSPCI;
+
+typedef struct {
+ QOSGraphObject obj;
+ QVirtioFS vfs;
+} QVirtioFSDevice;
+
+static inline uint64_t virtio_fs_get_unique(QVirtioFS *vfs)
+{
+ /*
+ * Interrupt requests share the unique ID of the request, except the
+ * least-significant bit.
+ *
+ * Note that unique ID 0 is invalid so we increment right away.
+ */
+ vfs->unique_counter += 2;
+
+ return vfs->unique_counter;
+}
+
+#endif /* TESTS_LIBQOS_VIRTIO_FS_H */
diff --git a/tests/libqos/virtio-fs.c b/tests/libqos/virtio-fs.c
new file mode 100644
index 0000000000..47f22d50b9
--- /dev/null
+++ b/tests/libqos/virtio-fs.c
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifer: GPL-2.0-or-later */
+/*
+ * libqos virtio-fs device driver
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/virtio_fs.h"
+#include "libqos/virtio-fs.h"
+
+static void virtio_fs_cleanup(QVirtioFS *vfs)
+{
+ QVirtioDevice *vdev = vfs->vdev;
+
+ qvirtqueue_cleanup(vdev->bus, vfs->hiprio_vq, vfs->alloc);
+ qvirtqueue_cleanup(vdev->bus, vfs->request_vq, vfs->alloc);
+ vfs->hiprio_vq = NULL;
+ vfs->request_vq = NULL;
+}
+
+static void virtio_fs_setup(QVirtioFS *vfs)
+{
+ QVirtioDevice *vdev = vfs->vdev;
+ uint64_t features;
+
+ features = qvirtio_get_features(vdev);
+ features &= ~(QVIRTIO_F_BAD_FEATURE |
+ (1ull << VIRTIO_RING_F_EVENT_IDX));
+ qvirtio_set_features(vdev, features);
+
+ vfs->hiprio_vq = qvirtqueue_setup(vdev, vfs->alloc, 0);
+ vfs->request_vq = qvirtqueue_setup(vdev, vfs->alloc, 1);
+
+ qvirtio_set_driver_ok(vdev);
+}
+
+static void vhost_user_fs_pci_destructor(QOSGraphObject *obj)
+{
+ QVirtioFSPCI *vfs_pci = (QVirtioFSPCI *)obj;
+ QVirtioFS *vfs = &vfs_pci->vfs;
+
+ virtio_fs_cleanup(vfs);
+ qvirtio_pci_destructor(&vfs_pci->pci_vdev.obj);
+}
+
+static void vhost_user_fs_pci_start_hw(QOSGraphObject *obj)
+{
+ QVirtioFSPCI *vfs_pci = (QVirtioFSPCI *)obj;
+ QVirtioFS *vfs = &vfs_pci->vfs;
+
+ qvirtio_pci_start_hw(&vfs_pci->pci_vdev.obj);
+ virtio_fs_setup(vfs);
+}
+
+static void *vhost_user_fs_pci_get_driver(void *object, const char *interface)
+{
+ QVirtioFSPCI *vfs_pci = object;
+
+ if (g_strcmp0(interface, "virtio-fs") == 0) {
+ return &vfs_pci->vfs;
+ }
+
+ fprintf(stderr, "%s not present in virtio-fs\n", interface);
+ g_assert_not_reached();
+}
+
+static void *vhost_user_fs_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
+{
+ QVirtioFSPCI *vfs_pci = g_new0(QVirtioFSPCI, 1);
+ QVirtioFS *vfs = &vfs_pci->vfs;
+ QOSGraphObject *obj = &vfs_pci->pci_vdev.obj;
+
+ virtio_pci_init(&vfs_pci->pci_vdev, pci_bus, addr);
+ vfs->vdev = &vfs_pci->pci_vdev.vdev;
+ vfs->alloc = alloc;
+
+ g_assert_cmphex(vfs->vdev->device_type, ==, VIRTIO_ID_FS);
+
+ obj->destructor = vhost_user_fs_pci_destructor;
+ obj->start_hw = vhost_user_fs_pci_start_hw;
+ obj->get_driver = vhost_user_fs_pci_get_driver;
+
+ return obj;
+}
+
+static void virtio_fs_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "chardev=char-virtio-fs,addr=04.0,tag=" VIRTIO_FS_TAG,
+ .before_cmd_line = "-m 512M -object memory-backend-file,id=mem,"
+ "size=512M,mem-path=/dev/shm,share=on -numa node,memdev=mem",
+ };
+ QPCIAddress addr = {
+ .devfn = QPCI_DEVFN(4, 0),
+ };
+
+ add_qpci_address(&opts, &addr);
+ qos_node_create_driver("vhost-user-fs-pci", vhost_user_fs_pci_create);
+ qos_node_consumes("vhost-user-fs-pci", "pci-bus", &opts);
+ qos_node_produces("vhost-user-fs-pci", "virtio-fs");
+}
+
+libqos_init(virtio_fs_register_nodes);
diff --git a/tests/vhost-user-fs-test.c b/tests/vhost-user-fs-test.c
new file mode 100644
index 0000000000..76394adee6
--- /dev/null
+++ b/tests/vhost-user-fs-test.c
@@ -0,0 +1,660 @@
+/* SPDX-License-Identifer: GPL-2.0-or-later */
+/*
+ * vhost-user-fs device test
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "qemu/iov.h"
+#include "standard-headers/linux/virtio_fs.h"
+#include "standard-headers/linux/fuse.h"
+#include "libqos/virtio-fs.h"
+#include "libqtest-single.h"
+
+#define TIMEOUT_US (30 * 1000 * 1000)
+
+#ifdef HOST_WORDS_BIGENDIAN
+static const bool host_is_big_endian = true;
+#else
+static const bool host_is_big_endian; /* false */
+#endif
+
+/*
+ * This macro skips tests when run in a cross-endian configuration.
+ * virtiofsd does not byte-swap FUSE messages and therefore does not support
+ * cross-endian.
+ */
+#define SKIP_TEST_IF_CROSS_ENDIAN() { \
+ if (host_is_big_endian != qtest_big_endian(global_qtest)) { \
+ g_test_skip("cross-endian is not supported by virtiofsd yet"); \
+ return; \
+ } \
+}
+
+static char *socket_path;
+static char *shared_dir;
+
+static bool remove_dir_and_children(const char *path)
+{
+ GDir *dir;
+ const gchar *name;
+
+ dir = g_dir_open(path, 0, NULL);
+ if (!dir) {
+ return false;
+ }
+
+ while ((name = g_dir_read_name(dir)) != NULL) {
+ g_autofree gchar *child = g_strdup_printf("%s/%s", path, name);
+
+ g_test_message("unlinking %s", child);
+
+ if (unlink(child) == -1 && errno == EISDIR) {
+ remove_dir_and_children(child);
+ }
+ }
+
+ g_dir_close(dir);
+
+ g_test_message("rmdir %s", path);
+ return rmdir(path) == 0;
+}
+
+static void after_test(void *arg G_GNUC_UNUSED)
+{
+ unlink(socket_path);
+
+ remove_dir_and_children(shared_dir);
+
+ /*
+ * Both QEMU and virtiofsd need to be restarted after each test and the
+ * shared directory will be recreated. This ensures isolation between test
+ * runs.
+ */
+ qos_invalidate_command_line();
+}
+
+/* Called on SIGABRT */
+static void abrt_handler(void *arg G_GNUC_UNUSED)
+{
+ after_test(NULL);
+}
+
+static int create_socket(const char *path)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ g_test_message("socket failed (errno=%d)", errno);
+ abort();
+ }
+
+ unlink(path); /* in case it already exists */
+
+ sa.un.sun_family = AF_UNIX;
+ snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), "%s", path);
+
+ if (bind(fd, &sa.sa, sizeof(sa.un)) < 0) {
+ g_test_message("bind failed (errno=%d)", errno);
+ abort();
+ }
+
+ if (listen(fd, 1) < 0) {
+ g_test_message("listen failed (errno=%d)", errno);
+ abort();
+ }
+
+ return fd;
+}
+
+static const char *qtest_virtiofsd(void)
+{
+ const char *virtiofsd_binary;
+
+ virtiofsd_binary = getenv("QTEST_VIRTIOFSD");
+ if (!virtiofsd_binary) {
+ fprintf(stderr, "Environment variable QTEST_VIRTIOFSD required\n");
+ exit(1);
+ }
+
+ return virtiofsd_binary;
+}
+
+/* Launch virtiofsd before each test with an empty shared directory */
+static void *before_test(GString *cmd_line G_GNUC_UNUSED, void *arg)
+{
+ g_autofree char *command = NULL;
+ char *virtiofsd_path;
+ int fd;
+ pid_t pid;
+
+ fd = create_socket(socket_path);
+
+ if (mkdir(shared_dir, 0777) < 0) {
+ g_message("mkdir failed (errno=%d)", errno);
+ abort();
+ }
+
+ virtiofsd_path = realpath(qtest_virtiofsd(), NULL);
+ g_assert_nonnull(virtiofsd_path);
+
+ command = g_strdup_printf("exec %s --fd=%d -o source=%s",
+ virtiofsd_path,
+ fd,
+ shared_dir);
+ free(virtiofsd_path);
+ g_test_message("starting virtiofsd: %s", command);
+
+ /* virtiofsd terminates when QEMU closes the vhost-user socket connection,
+ * so there is no need to kill it explicitly later on.
+ */
+ pid = fork();
+ g_assert_cmpint(pid, >=, 0);
+ if (pid == 0) {
+ execlp("/bin/sh", "sh", "-c", command, NULL);
+ exit(1);
+ }
+
+ close(fd);
+
+ return arg;
+}
+
+/*
+ * Send scatter-gather lists on the request virtqueue and return the number of
+ * bytes filled by the device.
+ *
+ * Note that in/out have opposite meanings in FUSE and VIRTIO. This function
+ * uses VIRTIO terminology (out - to device, in - from device).
+ */
+static uint32_t do_request(QVirtioFS *vfs, QTestState *qts,
+ struct iovec *sg_out, unsigned out_num,
+ struct iovec *sg_in, unsigned in_num)
+{
+ QVirtioDevice *dev = vfs->vdev;
+ QVirtQueue *vq = vfs->request_vq;
+ size_t out_bytes = iov_size(sg_out, out_num);
+ size_t in_bytes = iov_size(sg_in, in_num);
+ uint64_t out_addr;
+ uint64_t in_addr;
+ uint64_t addr;
+ uint32_t head = 0;
+ uint32_t nfilled;
+ unsigned i;
+
+ g_assert_cmpint(out_num, >, 0);
+ g_assert_cmpint(in_num, >, 0);
+
+ /* Add out buffers */
+ addr = out_addr = guest_alloc(vfs->alloc, out_bytes);
+ for (i = 0; i < out_num; i++) {
+ size_t len = sg_out[i].iov_len;
+ uint32_t desc_idx;
+ bool first = i == 0;
+
+ qtest_memwrite(qts, addr, sg_out[i].iov_base, len);
+ desc_idx = qvirtqueue_add(qts, vq, addr, len, false, true);
+
+ if (first) {
+ head = desc_idx;
+ }
+
+ addr += len;
+ }
+
+ /* Add in buffers */
+ addr = in_addr = guest_alloc(vfs->alloc, in_bytes);
+ for (i = 0; i < in_num; i++) {
+ size_t len = sg_in[i].iov_len;
+ bool next = i != in_num - 1;
+
+ qvirtqueue_add(qts, vq, addr, len, true, next);
+
+ addr += len;
+ }
+
+ /* Process the request */
+ qvirtqueue_kick(qts, dev, vq, head);
+ qvirtio_wait_used_elem(qts, dev, vq, head, &nfilled, TIMEOUT_US);
+
+ /* Copy in buffers back */
+ addr = in_addr;
+ for (i = 0; i < in_num; i++) {
+ size_t len = sg_in[i].iov_len;
+
+ qtest_memread(qts, addr, sg_in[i].iov_base, len);
+ addr += len;
+ }
+
+ guest_free(vfs->alloc, in_addr);
+ guest_free(vfs->alloc, out_addr);
+
+ return nfilled;
+}
+
+/* Byte-swap values if host endianness differs from guest */
+static uint32_t guest32(uint32_t val)
+{
+ if (qtest_big_endian(global_qtest) != host_is_big_endian) {
+ return bswap32(val);
+ }
+ return val;
+}
+
+static uint64_t guest64(uint64_t val)
+{
+ if (qtest_big_endian(global_qtest) != host_is_big_endian) {
+ return bswap64(val);
+ }
+ return val;
+}
+
+/* Make a FUSE_INIT request */
+static void fuse_init(QVirtioFS *vfs)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_INIT),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ };
+ struct fuse_init_in in = {
+ .major = guest32(FUSE_KERNEL_VERSION),
+ .minor = guest32(FUSE_KERNEL_MINOR_VERSION),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ };
+ struct fuse_out_header out_hdr;
+ struct fuse_init_out out;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = &out, .iov_len = sizeof(out) },
+ };
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ g_assert_cmpint(guest32(out_hdr.error), ==, 0);
+ g_assert_cmpint(guest32(out.major), ==, FUSE_KERNEL_VERSION);
+}
+
+/* Look up a directory entry by name using FUSE_LOOKUP */
+static int32_t fuse_lookup(QVirtioFS *vfs, uint64_t parent, const char *name,
+ struct fuse_entry_out *entry)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_LOOKUP),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ .nodeid = guest64(parent),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = (void *)name, .iov_len = strlen(name) + 1 },
+ };
+ struct fuse_out_header out_hdr;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = entry, .iov_len = sizeof(*entry) },
+ };
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ return guest32(out_hdr.error);
+}
+
+/* Open a file by nodeid using FUSE_OPEN */
+static int32_t fuse_open(QVirtioFS *vfs, uint64_t nodeid, uint32_t flags,
+ uint64_t *fh)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_OPEN),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ .nodeid = guest64(nodeid),
+ };
+ struct fuse_open_in in = {
+ .flags = guest32(flags),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ };
+ struct fuse_out_header out_hdr;
+ struct fuse_open_out out;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = &out, .iov_len = sizeof(out) },
+ };
+ int32_t error;
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ error = guest32(out_hdr.error);
+ if (!error) {
+ *fh = guest64(out.fh);
+ } else {
+ *fh = 0;
+ }
+ return error;
+}
+
+/* Create a file using FUSE_CREATE */
+static int32_t fuse_create(QVirtioFS *vfs, uint64_t parent, const char *name,
+ uint32_t mode, uint32_t flags,
+ uint64_t *nodeid, uint64_t *fh)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_CREATE),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ .nodeid = guest64(parent),
+ };
+ struct fuse_create_in in = {
+ .flags = guest32(flags),
+ .mode = guest32(mode),
+ .umask = guest32(0002),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ { .iov_base = (void *)name, .iov_len = strlen(name) + 1 },
+ };
+ struct fuse_out_header out_hdr;
+ struct fuse_entry_out entry;
+ struct fuse_open_out out;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = &entry, .iov_len = sizeof(entry) },
+ { .iov_base = &out, .iov_len = sizeof(out) },
+ };
+ int32_t error;
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ error = guest32(out_hdr.error);
+ if (!error) {
+ *nodeid = guest64(entry.nodeid);
+ *fh = guest64(out.fh);
+ } else {
+ *nodeid = 0;
+ *fh = 0;
+ }
+ return error;
+}
+
+/* Read bytes from a file using FILE_READ */
+static ssize_t fuse_read(QVirtioFS *vfs, uint64_t fh, uint64_t offset,
+ void *buf, size_t len)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_READ),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ };
+ struct fuse_read_in in = {
+ .fh = guest64(fh),
+ .offset = guest64(offset),
+ .size = guest32(len),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ };
+ struct fuse_out_header out_hdr;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = buf, .iov_len = len },
+ };
+ uint32_t nread;
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ nread = do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+ g_assert_cmpint(guest32(out_hdr.error), ==, 0);
+
+ return nread - sizeof(out_hdr);
+}
+
+/* Write bytes to a file using FILE_WRITE */
+static ssize_t fuse_write(QVirtioFS *vfs, uint64_t fh, uint64_t offset,
+ const void *buf, size_t len)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_WRITE),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ };
+ struct fuse_write_in in = {
+ .fh = guest64(fh),
+ .offset = guest64(offset),
+ .size = guest32(len),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ { .iov_base = (void *)buf, .iov_len = len },
+ };
+ struct fuse_out_header out_hdr;
+ struct fuse_write_out out;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ { .iov_base = &out, .iov_len = sizeof(out) },
+ };
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+ g_assert_cmpint(guest32(out_hdr.error), ==, 0);
+
+ return guest32(out.size);
+}
+
+/* Close a file handle using FUSE_RELEASE */
+static void fuse_release(QVirtioFS *vfs, uint64_t fh)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_RELEASE),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ };
+ struct fuse_release_in in = {
+ .fh = guest64(fh),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ };
+ struct fuse_out_header out_hdr;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ };
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ g_assert_cmpint(guest32(out_hdr.error), ==, 0);
+}
+
+/* Drop an inode reference using FUSE_FORGET */
+static void fuse_forget(QVirtioFS *vfs, uint64_t nodeid)
+{
+ struct fuse_in_header in_hdr = {
+ .opcode = guest32(FUSE_FORGET),
+ .unique = guest64(virtio_fs_get_unique(vfs)),
+ .nodeid = guest64(nodeid),
+ };
+ struct fuse_forget_in in = {
+ .nlookup = guest64(1),
+ };
+ struct iovec sg_in[] = {
+ { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
+ { .iov_base = &in, .iov_len = sizeof(in) },
+ };
+ struct fuse_out_header out_hdr;
+ struct iovec sg_out[] = {
+ { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
+ };
+
+ in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
+
+ do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
+ sg_out, G_N_ELEMENTS(sg_out));
+
+ g_assert_cmpint(guest32(out_hdr.error), ==, 0);
+}
+
+/* Check contents of VIRTIO Configuration Space */
+static void test_config(void *parent, void *arg, QGuestAllocator *alloc)
+{
+ QVirtioFS *vfs = parent;
+ size_t i;
+ uint32_t num_request_queues;
+ char tag[37];
+
+ SKIP_TEST_IF_CROSS_ENDIAN();
+
+ for (i = 0; i < sizeof(tag) - 1; i++) {
+ tag[i] = qvirtio_config_readw(vfs->vdev, i);
+ }
+ tag[36] = '\0';
+
+ g_assert_cmpstr(tag, ==, VIRTIO_FS_TAG);
+
+ num_request_queues = qvirtio_config_readl(vfs->vdev,
+ offsetof(struct virtio_fs_config, num_request_queues));
+
+ g_assert_cmpint(num_request_queues, ==, 1);
+}
+
+/* Create file on host and check its contents and metadata in guest */
+static void test_file_from_host(void *parent, void *arg, QGuestAllocator *alloc)
+{
+ g_autofree gchar *filename = g_strdup_printf("%s/%s", shared_dir, "foo");
+ const char *str = "This is a test\n";
+ char buf[strlen(str)];
+ QVirtioFS *vfs = parent;
+ struct fuse_entry_out entry;
+ int32_t error;
+ uint64_t nodeid;
+ uint64_t fh;
+ ssize_t nread;
+ gboolean ok;
+
+ SKIP_TEST_IF_CROSS_ENDIAN();
+
+ /* Create the test file in the shared directory */
+ ok = g_file_set_contents(filename, str, strlen(str), NULL);
+ g_assert(ok);
+
+ fuse_init(vfs);
+
+ error = fuse_lookup(vfs, FUSE_ROOT_ID, "foo", &entry);
+ g_assert_cmpint(error, ==, 0);
+ g_assert_cmpint(guest64(entry.attr.size), ==, strlen(str));
+ nodeid = guest64(entry.nodeid);
+
+ error = fuse_open(vfs, nodeid, O_RDONLY, &fh);
+ g_assert_cmpint(error, ==, 0);
+
+ nread = fuse_read(vfs, fh, 0, buf, sizeof(buf));
+ g_assert_cmpint(nread, ==, sizeof(buf));
+ g_assert_cmpint(memcmp(buf, str, sizeof(buf)), ==, 0);
+
+ fuse_release(vfs, fh);
+ fuse_forget(vfs, nodeid);
+}
+
+/* Create file from host and check its contents and metadata on host */
+static void test_file_from_guest(void *parent, void *arg,
+ QGuestAllocator *alloc)
+{
+ g_autofree gchar *filename = g_strdup_printf("%s/%s", shared_dir, "foo");
+ const char *str = "This is a test\n";
+ gchar *contents = NULL;
+ gsize length = 0;
+ QVirtioFS *vfs = parent;
+ int32_t error;
+ uint64_t nodeid;
+ uint64_t fh;
+ ssize_t nwritten;
+ gboolean ok;
+
+ SKIP_TEST_IF_CROSS_ENDIAN();
+
+ fuse_init(vfs);
+
+ error = fuse_create(vfs, FUSE_ROOT_ID, "foo", 0644, O_CREAT | O_WRONLY,
+ &nodeid, &fh);
+ g_assert_cmpint(error, ==, 0);
+
+ nwritten = fuse_write(vfs, fh, 0, str, strlen(str));
+ g_assert_cmpint(nwritten, ==, strlen(str));
+
+ fuse_release(vfs, fh);
+ fuse_forget(vfs, nodeid);
+
+ /* Check the file on the host */
+ ok = g_file_get_contents(filename, &contents, &length, NULL);
+ g_assert(ok);
+ g_assert_cmpint(length, ==, strlen(str));
+ g_assert_cmpint(memcmp(contents, str, strlen(str)), ==, 0);
+ g_free(contents);
+}
+
+static void register_vhost_user_fs_test(void)
+{
+ g_autofree gchar *cmd_line =
+ g_strdup_printf("-chardev socket,id=char-virtio-fs,path=%s",
+ socket_path);
+ QOSGraphTestOptions opts = {
+ .edge.before_cmd_line = cmd_line,
+ .before = before_test,
+ .after = after_test,
+ };
+
+ if (geteuid() != 0) {
+ g_test_message("Skipping vhost-user-fs tests because root is "
+ "required for virtiofsd");
+ return;
+ }
+
+ qtest_add_abrt_handler(abrt_handler, NULL);
+
+ qos_add_test("config", "virtio-fs", test_config, &opts);
+ qos_add_test("file-from-host", "virtio-fs", test_file_from_host, &opts);
+ qos_add_test("file-from-guest", "virtio-fs", test_file_from_guest, &opts);
+}
+
+libqos_init(register_vhost_user_fs_test);
+
+static void __attribute__((constructor)) init_paths(void)
+{
+ socket_path = g_strdup_printf("/tmp/qtest-%d-vhost-fs.sock", getpid());
+ shared_dir = g_strdup_printf("/tmp/qtest-%d-virtio-fs-dir", getpid());
+}
+
+static void __attribute__((destructor)) destroy_paths(void)
+{
+ g_free(shared_dir);
+ shared_dir = NULL;
+
+ g_free(socket_path);
+ socket_path = NULL;
+}
--
2.21.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file
2019-10-25 10:01 ` [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file Stefan Hajnoczi
@ 2019-10-26 21:49 ` Michael S. Tsirkin
2019-10-27 12:36 ` Stefan Hajnoczi
0 siblings, 1 reply; 11+ messages in thread
From: Michael S. Tsirkin @ 2019-10-26 21:49 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Laurent Vivier, Thomas Huth, Cornelia Huck, qemu-devel,
Dr. David Alan Gilbert, virtio-fs, Paolo Bonzini, vgoyal
On Fri, Oct 25, 2019 at 12:01:50PM +0200, Stefan Hajnoczi wrote:
> tests/vhost-user-fs-test.c needs fuse.h. The private copy that
> virtiofsd has can be replaced with a properly imported file using
> update-linux-headers.sh.
>
> TODO rerun update-linux-headers.sh with upstream kernel tree!
>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
OK I would just add this with the virtiofsd patchset.
> ---
> contrib/virtiofsd/fuse_lowlevel.h | 2 +-
> .../fuse_kernel.h => include/standard-headers/linux/fuse.h | 0
> contrib/virtiofsd/fuse_loop_mt.c | 2 +-
> contrib/virtiofsd/fuse_lowlevel.c | 2 +-
> contrib/virtiofsd/fuse_virtio.c | 2 +-
> scripts/update-linux-headers.sh | 3 ++-
> 6 files changed, 6 insertions(+), 5 deletions(-)
> rename contrib/virtiofsd/fuse_kernel.h => include/standard-headers/linux/fuse.h (100%)
>
> diff --git a/contrib/virtiofsd/fuse_lowlevel.h b/contrib/virtiofsd/fuse_lowlevel.h
> index 79fb30a1c2..a8c92ff7e0 100644
> --- a/contrib/virtiofsd/fuse_lowlevel.h
> +++ b/contrib/virtiofsd/fuse_lowlevel.h
> @@ -23,7 +23,7 @@
> #endif
>
> #include "fuse_common.h"
> -#include "fuse_kernel.h"
> +#include "standard-headers/linux/fuse.h"
>
> #include <utime.h>
> #include <fcntl.h>
> diff --git a/contrib/virtiofsd/fuse_kernel.h b/include/standard-headers/linux/fuse.h
> similarity index 100%
> rename from contrib/virtiofsd/fuse_kernel.h
> rename to include/standard-headers/linux/fuse.h
> diff --git a/contrib/virtiofsd/fuse_loop_mt.c b/contrib/virtiofsd/fuse_loop_mt.c
> index 2000a8902a..af7b501fac 100644
> --- a/contrib/virtiofsd/fuse_loop_mt.c
> +++ b/contrib/virtiofsd/fuse_loop_mt.c
> @@ -11,7 +11,7 @@
> #include "fuse_i.h"
> #include "fuse_lowlevel.h"
> #include "fuse_misc.h"
> -#include "fuse_kernel.h"
> +#include "standard-headers/linux/fuse.h"
> #include "fuse_virtio.h"
>
> #include <stdio.h>
> diff --git a/contrib/virtiofsd/fuse_lowlevel.c b/contrib/virtiofsd/fuse_lowlevel.c
> index 78ccfe3a27..c1a901cb4d 100644
> --- a/contrib/virtiofsd/fuse_lowlevel.c
> +++ b/contrib/virtiofsd/fuse_lowlevel.c
> @@ -10,7 +10,7 @@
> */
>
> #include "fuse_i.h"
> -#include "fuse_kernel.h"
> +#include "standard-headers/linux/fuse.h"
> #include "fuse_opt.h"
> #include "fuse_misc.h"
> #include "fuse_virtio.h"
> diff --git a/contrib/virtiofsd/fuse_virtio.c b/contrib/virtiofsd/fuse_virtio.c
> index 533ef24bb7..7a0d0b2603 100644
> --- a/contrib/virtiofsd/fuse_virtio.c
> +++ b/contrib/virtiofsd/fuse_virtio.c
> @@ -15,7 +15,7 @@
> #include "qapi/error.h"
>
> #include "fuse_i.h"
> -#include "fuse_kernel.h"
> +#include "standard-headers/linux/fuse.h"
> #include "fuse_misc.h"
> #include "fuse_opt.h"
> #include "fuse_virtio.h"
> diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
> index f76d77363b..1a627ccd73 100755
> --- a/scripts/update-linux-headers.sh
> +++ b/scripts/update-linux-headers.sh
> @@ -184,7 +184,8 @@ EOF
>
> rm -rf "$output/include/standard-headers/linux"
> mkdir -p "$output/include/standard-headers/linux"
> -for i in "$tmpdir"/include/linux/*virtio*.h \
> +for i in "$tmpdir/include/linux/fuse.h" \
> + "$tmpdir"/include/linux/*virtio*.h \
> "$tmpdir/include/linux/qemu_fw_cfg.h" \
> "$tmpdir/include/linux/input.h" \
> "$tmpdir/include/linux/input-event-codes.h" \
> --
> 2.21.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file
2019-10-26 21:49 ` Michael S. Tsirkin
@ 2019-10-27 12:36 ` Stefan Hajnoczi
2020-06-01 10:28 ` Alex Bennée
0 siblings, 1 reply; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-10-27 12:36 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Laurent Vivier, Thomas Huth, Cornelia Huck, qemu-devel,
Dr. David Alan Gilbert, virtio-fs, Paolo Bonzini, vgoyal
[-- Attachment #1: Type: text/plain, Size: 543 bytes --]
On Sat, Oct 26, 2019 at 05:49:11PM -0400, Michael S. Tsirkin wrote:
> On Fri, Oct 25, 2019 at 12:01:50PM +0200, Stefan Hajnoczi wrote:
> > tests/vhost-user-fs-test.c needs fuse.h. The private copy that
> > virtiofsd has can be replaced with a properly imported file using
> > update-linux-headers.sh.
> >
> > TODO rerun update-linux-headers.sh with upstream kernel tree!
> >
> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>
> OK I would just add this with the virtiofsd patchset.
Yes, I'll talk to David.
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case
2019-10-25 10:01 ` [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
@ 2019-10-29 0:36 ` Dr. David Alan Gilbert
2019-11-05 16:02 ` Stefan Hajnoczi
0 siblings, 1 reply; 11+ messages in thread
From: Dr. David Alan Gilbert @ 2019-10-29 0:36 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
qemu-devel, virtio-fs, Paolo Bonzini, vgoyal
* Stefan Hajnoczi (stefanha@redhat.com) wrote:
> Add a test case for the vhost-user-fs device. There are two
> limitations:
>
> 1. This test only runs when invoked as root. The virtiofsd vhost-user
> device backend currently requires root in order to maintain accurate
> file system ownership information (uid/gid).
>
> 2. Cross-endian is not supported because virtiofsd currently only
> supports same-endian configurations.
>
> This test uses FUSE_INIT, FUSE_LOOKUP, FUSE_OPEN, FUSE_CREATE,
> FUSE_READ, FUSE_WRITE, FUSE_RELEASE, and FUSE_FORGET messages to perform
> basic sanity testing.
>
> This test can be expanded on in the future to perform low-level
> virtio-fs testing, including invalid FUSE messages that are hard to
> generate from a real guest.
>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
> tests/Makefile.include | 8 +-
> tests/libqos/virtio-fs.h | 46 +++
> tests/libqos/virtio-fs.c | 104 ++++++
> tests/vhost-user-fs-test.c | 660 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 816 insertions(+), 2 deletions(-)
> create mode 100644 tests/libqos/virtio-fs.h
> create mode 100644 tests/libqos/virtio-fs.c
> create mode 100644 tests/vhost-user-fs-test.c
>
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index fde8a0c5ef..0472565d96 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -718,6 +718,7 @@ qos-test-obj-y += tests/libqos/sdhci.o
> qos-test-obj-y += tests/libqos/tpci200.o
> qos-test-obj-y += tests/libqos/virtio.o
> qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o
> +qos-test-obj-$(CONFIG_VHOST_USER_FS) += tests/libqos/virtio-fs.o
> qos-test-obj-y += tests/libqos/virtio-balloon.o
> qos-test-obj-y += tests/libqos/virtio-blk.o
> qos-test-obj-y += tests/libqos/virtio-mmio.o
> @@ -759,6 +760,7 @@ qos-test-obj-y += tests/spapr-phb-test.o
> qos-test-obj-y += tests/tmp105-test.o
> qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
> qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y)
> +qos-test-obj-$(CONFIG_VHOST_USER_FS) += tests/vhost-user-fs-test.o
> qos-test-obj-y += tests/virtio-test.o
> qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o
> qos-test-obj-y += tests/virtio-blk-test.o
> @@ -907,7 +909,8 @@ endef
> $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: %-softmmu/all $(check-qtest-y)
> $(call do_test_human,$(check-qtest-$*-y) $(check-qtest-generic-y), \
> QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
> - QTEST_QEMU_IMG=qemu-img$(EXESUF))
> + QTEST_QEMU_IMG=qemu-img$(EXESUF) \
> + QTEST_VIRTIOFSD=virtiofsd$(EXESUF))
>
> check-unit: $(check-unit-y)
> $(call do_test_human, $^)
> @@ -920,7 +923,8 @@ check-speed: $(check-speed-y)
> $(patsubst %, check-report-qtest-%.tap, $(QTEST_TARGETS)): check-report-qtest-%.tap: %-softmmu/all $(check-qtest-y)
> $(call do_test_tap, $(check-qtest-$*-y) $(check-qtest-generic-y), \
> QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
> - QTEST_QEMU_IMG=qemu-img$(EXESUF))
> + QTEST_QEMU_IMG=qemu-img$(EXESUF) \
> + QTEST_VIRTIOFSD=virtiofsd$(EXESUF))
>
> check-report-unit.tap: $(check-unit-y)
> $(call do_test_tap,$^)
> diff --git a/tests/libqos/virtio-fs.h b/tests/libqos/virtio-fs.h
> new file mode 100644
> index 0000000000..40289ba283
> --- /dev/null
> +++ b/tests/libqos/virtio-fs.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifer: GPL-2.0-or-later */
> +/*
> + * libqos virtio-fs device driver
> + *
> + * Copyright (C) 2019 Red Hat, Inc.
> + */
> +
> +#ifndef TESTS_LIBQOS_VIRTIO_FS_H
> +#define TESTS_LIBQOS_VIRTIO_FS_H
> +
> +#include "libqos/virtio-pci.h"
> +
> +#define VIRTIO_FS_TAG "myfs"
> +
> +typedef struct {
> + QVirtioDevice *vdev;
> + QGuestAllocator *alloc;
> + QVirtQueue *hiprio_vq;
> + QVirtQueue *request_vq;
> + uint64_t unique_counter;
> +} QVirtioFS;
> +
> +typedef struct {
> + QVirtioPCIDevice pci_vdev;
> + QVirtioFS vfs;
> +} QVirtioFSPCI;
> +
> +typedef struct {
> + QOSGraphObject obj;
> + QVirtioFS vfs;
> +} QVirtioFSDevice;
> +
> +static inline uint64_t virtio_fs_get_unique(QVirtioFS *vfs)
> +{
> + /*
> + * Interrupt requests share the unique ID of the request, except the
> + * least-significant bit.
> + *
> + * Note that unique ID 0 is invalid so we increment right away.
> + */
> + vfs->unique_counter += 2;
> +
> + return vfs->unique_counter;
> +}
> +
> +#endif /* TESTS_LIBQOS_VIRTIO_FS_H */
> diff --git a/tests/libqos/virtio-fs.c b/tests/libqos/virtio-fs.c
> new file mode 100644
> index 0000000000..47f22d50b9
> --- /dev/null
> +++ b/tests/libqos/virtio-fs.c
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifer: GPL-2.0-or-later */
> +/*
> + * libqos virtio-fs device driver
> + *
> + * Copyright (C) 2019 Red Hat, Inc.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "standard-headers/linux/virtio_fs.h"
> +#include "libqos/virtio-fs.h"
> +
> +static void virtio_fs_cleanup(QVirtioFS *vfs)
> +{
> + QVirtioDevice *vdev = vfs->vdev;
> +
> + qvirtqueue_cleanup(vdev->bus, vfs->hiprio_vq, vfs->alloc);
> + qvirtqueue_cleanup(vdev->bus, vfs->request_vq, vfs->alloc);
> + vfs->hiprio_vq = NULL;
> + vfs->request_vq = NULL;
> +}
> +
> +static void virtio_fs_setup(QVirtioFS *vfs)
> +{
> + QVirtioDevice *vdev = vfs->vdev;
> + uint64_t features;
> +
> + features = qvirtio_get_features(vdev);
> + features &= ~(QVIRTIO_F_BAD_FEATURE |
> + (1ull << VIRTIO_RING_F_EVENT_IDX));
> + qvirtio_set_features(vdev, features);
> +
> + vfs->hiprio_vq = qvirtqueue_setup(vdev, vfs->alloc, 0);
> + vfs->request_vq = qvirtqueue_setup(vdev, vfs->alloc, 1);
> +
> + qvirtio_set_driver_ok(vdev);
> +}
> +
> +static void vhost_user_fs_pci_destructor(QOSGraphObject *obj)
> +{
> + QVirtioFSPCI *vfs_pci = (QVirtioFSPCI *)obj;
> + QVirtioFS *vfs = &vfs_pci->vfs;
> +
> + virtio_fs_cleanup(vfs);
> + qvirtio_pci_destructor(&vfs_pci->pci_vdev.obj);
> +}
> +
> +static void vhost_user_fs_pci_start_hw(QOSGraphObject *obj)
> +{
> + QVirtioFSPCI *vfs_pci = (QVirtioFSPCI *)obj;
> + QVirtioFS *vfs = &vfs_pci->vfs;
> +
> + qvirtio_pci_start_hw(&vfs_pci->pci_vdev.obj);
> + virtio_fs_setup(vfs);
> +}
> +
> +static void *vhost_user_fs_pci_get_driver(void *object, const char *interface)
> +{
> + QVirtioFSPCI *vfs_pci = object;
> +
> + if (g_strcmp0(interface, "virtio-fs") == 0) {
> + return &vfs_pci->vfs;
> + }
> +
> + fprintf(stderr, "%s not present in virtio-fs\n", interface);
> + g_assert_not_reached();
> +}
> +
> +static void *vhost_user_fs_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
> +{
> + QVirtioFSPCI *vfs_pci = g_new0(QVirtioFSPCI, 1);
> + QVirtioFS *vfs = &vfs_pci->vfs;
> + QOSGraphObject *obj = &vfs_pci->pci_vdev.obj;
> +
> + virtio_pci_init(&vfs_pci->pci_vdev, pci_bus, addr);
> + vfs->vdev = &vfs_pci->pci_vdev.vdev;
> + vfs->alloc = alloc;
> +
> + g_assert_cmphex(vfs->vdev->device_type, ==, VIRTIO_ID_FS);
> +
> + obj->destructor = vhost_user_fs_pci_destructor;
> + obj->start_hw = vhost_user_fs_pci_start_hw;
> + obj->get_driver = vhost_user_fs_pci_get_driver;
> +
> + return obj;
> +}
> +
> +static void virtio_fs_register_nodes(void)
> +{
> + QOSGraphEdgeOptions opts = {
> + .extra_device_opts = "chardev=char-virtio-fs,addr=04.0,tag=" VIRTIO_FS_TAG,
> + .before_cmd_line = "-m 512M -object memory-backend-file,id=mem,"
> + "size=512M,mem-path=/dev/shm,share=on -numa node,memdev=mem",
> + };
> + QPCIAddress addr = {
> + .devfn = QPCI_DEVFN(4, 0),
> + };
> +
> + add_qpci_address(&opts, &addr);
> + qos_node_create_driver("vhost-user-fs-pci", vhost_user_fs_pci_create);
> + qos_node_consumes("vhost-user-fs-pci", "pci-bus", &opts);
> + qos_node_produces("vhost-user-fs-pci", "virtio-fs");
> +}
> +
> +libqos_init(virtio_fs_register_nodes);
> diff --git a/tests/vhost-user-fs-test.c b/tests/vhost-user-fs-test.c
> new file mode 100644
> index 0000000000..76394adee6
> --- /dev/null
> +++ b/tests/vhost-user-fs-test.c
> @@ -0,0 +1,660 @@
> +/* SPDX-License-Identifer: GPL-2.0-or-later */
> +/*
> + * vhost-user-fs device test
> + *
> + * Copyright (C) 2019 Red Hat, Inc.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/bswap.h"
> +#include "qemu/iov.h"
> +#include "standard-headers/linux/virtio_fs.h"
> +#include "standard-headers/linux/fuse.h"
> +#include "libqos/virtio-fs.h"
> +#include "libqtest-single.h"
> +
> +#define TIMEOUT_US (30 * 1000 * 1000)
> +
> +#ifdef HOST_WORDS_BIGENDIAN
> +static const bool host_is_big_endian = true;
> +#else
> +static const bool host_is_big_endian; /* false */
> +#endif
> +
> +/*
> + * This macro skips tests when run in a cross-endian configuration.
> + * virtiofsd does not byte-swap FUSE messages and therefore does not support
> + * cross-endian.
> + */
> +#define SKIP_TEST_IF_CROSS_ENDIAN() { \
> + if (host_is_big_endian != qtest_big_endian(global_qtest)) { \
> + g_test_skip("cross-endian is not supported by virtiofsd yet"); \
> + return; \
> + } \
> +}
> +
> +static char *socket_path;
> +static char *shared_dir;
> +
> +static bool remove_dir_and_children(const char *path)
> +{
> + GDir *dir;
> + const gchar *name;
> +
> + dir = g_dir_open(path, 0, NULL);
> + if (!dir) {
> + return false;
> + }
> +
> + while ((name = g_dir_read_name(dir)) != NULL) {
> + g_autofree gchar *child = g_strdup_printf("%s/%s", path, name);
> +
> + g_test_message("unlinking %s", child);
> +
> + if (unlink(child) == -1 && errno == EISDIR) {
> + remove_dir_and_children(child);
> + }
> + }
> +
> + g_dir_close(dir);
> +
> + g_test_message("rmdir %s", path);
> + return rmdir(path) == 0;
> +}
> +
> +static void after_test(void *arg G_GNUC_UNUSED)
> +{
> + unlink(socket_path);
> +
> + remove_dir_and_children(shared_dir);
This scares me. Especially since it's running as root.
Can we add a bunch of paranoid checks to make sure it doesn't
end up rm -rf / ?
> + /*
> + * Both QEMU and virtiofsd need to be restarted after each test and the
> + * shared directory will be recreated. This ensures isolation between test
> + * runs.
> + */
> + qos_invalidate_command_line();
> +}
> +
> +/* Called on SIGABRT */
> +static void abrt_handler(void *arg G_GNUC_UNUSED)
> +{
> + after_test(NULL);
> +}
> +
> +static int create_socket(const char *path)
> +{
> + union {
> + struct sockaddr sa;
> + struct sockaddr_un un;
> + } sa;
> + int fd;
> +
> + fd = socket(AF_UNIX, SOCK_STREAM, 0);
> + if (fd < 0) {
> + g_test_message("socket failed (errno=%d)", errno);
> + abort();
> + }
> +
> + unlink(path); /* in case it already exists */
> +
> + sa.un.sun_family = AF_UNIX;
> + snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), "%s", path);
> +
> + if (bind(fd, &sa.sa, sizeof(sa.un)) < 0) {
> + g_test_message("bind failed (errno=%d)", errno);
> + abort();
> + }
> +
> + if (listen(fd, 1) < 0) {
> + g_test_message("listen failed (errno=%d)", errno);
> + abort();
> + }
> +
> + return fd;
> +}
> +
> +static const char *qtest_virtiofsd(void)
> +{
> + const char *virtiofsd_binary;
> +
> + virtiofsd_binary = getenv("QTEST_VIRTIOFSD");
> + if (!virtiofsd_binary) {
> + fprintf(stderr, "Environment variable QTEST_VIRTIOFSD required\n");
> + exit(1);
> + }
> +
> + return virtiofsd_binary;
> +}
> +
> +/* Launch virtiofsd before each test with an empty shared directory */
> +static void *before_test(GString *cmd_line G_GNUC_UNUSED, void *arg)
> +{
> + g_autofree char *command = NULL;
> + char *virtiofsd_path;
> + int fd;
> + pid_t pid;
> +
> + fd = create_socket(socket_path);
> +
> + if (mkdir(shared_dir, 0777) < 0) {
> + g_message("mkdir failed (errno=%d)", errno);
> + abort();
> + }
> +
> + virtiofsd_path = realpath(qtest_virtiofsd(), NULL);
> + g_assert_nonnull(virtiofsd_path);
> +
> + command = g_strdup_printf("exec %s --fd=%d -o source=%s",
> + virtiofsd_path,
> + fd,
> + shared_dir);
> + free(virtiofsd_path);
> + g_test_message("starting virtiofsd: %s", command);
> +
> + /* virtiofsd terminates when QEMU closes the vhost-user socket connection,
> + * so there is no need to kill it explicitly later on.
> + */
> + pid = fork();
> + g_assert_cmpint(pid, >=, 0);
> + if (pid == 0) {
> + execlp("/bin/sh", "sh", "-c", command, NULL);
> + exit(1);
> + }
> +
> + close(fd);
> +
> + return arg;
> +}
> +
> +/*
> + * Send scatter-gather lists on the request virtqueue and return the number of
> + * bytes filled by the device.
> + *
> + * Note that in/out have opposite meanings in FUSE and VIRTIO. This function
> + * uses VIRTIO terminology (out - to device, in - from device).
> + */
> +static uint32_t do_request(QVirtioFS *vfs, QTestState *qts,
> + struct iovec *sg_out, unsigned out_num,
> + struct iovec *sg_in, unsigned in_num)
> +{
> + QVirtioDevice *dev = vfs->vdev;
> + QVirtQueue *vq = vfs->request_vq;
> + size_t out_bytes = iov_size(sg_out, out_num);
> + size_t in_bytes = iov_size(sg_in, in_num);
> + uint64_t out_addr;
> + uint64_t in_addr;
> + uint64_t addr;
> + uint32_t head = 0;
> + uint32_t nfilled;
> + unsigned i;
> +
> + g_assert_cmpint(out_num, >, 0);
> + g_assert_cmpint(in_num, >, 0);
> +
> + /* Add out buffers */
> + addr = out_addr = guest_alloc(vfs->alloc, out_bytes);
> + for (i = 0; i < out_num; i++) {
> + size_t len = sg_out[i].iov_len;
> + uint32_t desc_idx;
> + bool first = i == 0;
> +
> + qtest_memwrite(qts, addr, sg_out[i].iov_base, len);
> + desc_idx = qvirtqueue_add(qts, vq, addr, len, false, true);
> +
> + if (first) {
> + head = desc_idx;
> + }
> +
> + addr += len;
> + }
> +
> + /* Add in buffers */
> + addr = in_addr = guest_alloc(vfs->alloc, in_bytes);
> + for (i = 0; i < in_num; i++) {
> + size_t len = sg_in[i].iov_len;
> + bool next = i != in_num - 1;
> +
> + qvirtqueue_add(qts, vq, addr, len, true, next);
> +
> + addr += len;
> + }
> +
> + /* Process the request */
> + qvirtqueue_kick(qts, dev, vq, head);
> + qvirtio_wait_used_elem(qts, dev, vq, head, &nfilled, TIMEOUT_US);
> +
> + /* Copy in buffers back */
> + addr = in_addr;
> + for (i = 0; i < in_num; i++) {
> + size_t len = sg_in[i].iov_len;
> +
> + qtest_memread(qts, addr, sg_in[i].iov_base, len);
> + addr += len;
> + }
> +
> + guest_free(vfs->alloc, in_addr);
> + guest_free(vfs->alloc, out_addr);
> +
> + return nfilled;
> +}
> +
> +/* Byte-swap values if host endianness differs from guest */
> +static uint32_t guest32(uint32_t val)
> +{
> + if (qtest_big_endian(global_qtest) != host_is_big_endian) {
> + return bswap32(val);
> + }
> + return val;
> +}
> +
> +static uint64_t guest64(uint64_t val)
> +{
> + if (qtest_big_endian(global_qtest) != host_is_big_endian) {
> + return bswap64(val);
> + }
> + return val;
> +}
> +
> +/* Make a FUSE_INIT request */
> +static void fuse_init(QVirtioFS *vfs)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_INIT),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + };
> + struct fuse_init_in in = {
> + .major = guest32(FUSE_KERNEL_VERSION),
> + .minor = guest32(FUSE_KERNEL_MINOR_VERSION),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + };
> + struct fuse_out_header out_hdr;
> + struct fuse_init_out out;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = &out, .iov_len = sizeof(out) },
> + };
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + g_assert_cmpint(guest32(out_hdr.error), ==, 0);
> + g_assert_cmpint(guest32(out.major), ==, FUSE_KERNEL_VERSION);
> +}
> +
> +/* Look up a directory entry by name using FUSE_LOOKUP */
> +static int32_t fuse_lookup(QVirtioFS *vfs, uint64_t parent, const char *name,
> + struct fuse_entry_out *entry)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_LOOKUP),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + .nodeid = guest64(parent),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = (void *)name, .iov_len = strlen(name) + 1 },
> + };
> + struct fuse_out_header out_hdr;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = entry, .iov_len = sizeof(*entry) },
> + };
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + return guest32(out_hdr.error);
> +}
> +
> +/* Open a file by nodeid using FUSE_OPEN */
> +static int32_t fuse_open(QVirtioFS *vfs, uint64_t nodeid, uint32_t flags,
> + uint64_t *fh)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_OPEN),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + .nodeid = guest64(nodeid),
> + };
> + struct fuse_open_in in = {
> + .flags = guest32(flags),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + };
> + struct fuse_out_header out_hdr;
> + struct fuse_open_out out;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = &out, .iov_len = sizeof(out) },
> + };
I wonder if anything can be done to reduce the size of the iovec boiler
plate?
> + int32_t error;
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + error = guest32(out_hdr.error);
> + if (!error) {
> + *fh = guest64(out.fh);
> + } else {
> + *fh = 0;
> + }
> + return error;
> +}
> +
> +/* Create a file using FUSE_CREATE */
> +static int32_t fuse_create(QVirtioFS *vfs, uint64_t parent, const char *name,
> + uint32_t mode, uint32_t flags,
> + uint64_t *nodeid, uint64_t *fh)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_CREATE),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + .nodeid = guest64(parent),
> + };
> + struct fuse_create_in in = {
> + .flags = guest32(flags),
> + .mode = guest32(mode),
> + .umask = guest32(0002),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + { .iov_base = (void *)name, .iov_len = strlen(name) + 1 },
> + };
> + struct fuse_out_header out_hdr;
> + struct fuse_entry_out entry;
> + struct fuse_open_out out;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = &entry, .iov_len = sizeof(entry) },
> + { .iov_base = &out, .iov_len = sizeof(out) },
> + };
> + int32_t error;
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + error = guest32(out_hdr.error);
> + if (!error) {
> + *nodeid = guest64(entry.nodeid);
> + *fh = guest64(out.fh);
> + } else {
> + *nodeid = 0;
> + *fh = 0;
> + }
> + return error;
> +}
> +
> +/* Read bytes from a file using FILE_READ */
> +static ssize_t fuse_read(QVirtioFS *vfs, uint64_t fh, uint64_t offset,
> + void *buf, size_t len)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_READ),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + };
> + struct fuse_read_in in = {
> + .fh = guest64(fh),
> + .offset = guest64(offset),
> + .size = guest32(len),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + };
> + struct fuse_out_header out_hdr;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = buf, .iov_len = len },
> + };
> + uint32_t nread;
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + nread = do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> + g_assert_cmpint(guest32(out_hdr.error), ==, 0);
> +
> + return nread - sizeof(out_hdr);
> +}
> +
> +/* Write bytes to a file using FILE_WRITE */
> +static ssize_t fuse_write(QVirtioFS *vfs, uint64_t fh, uint64_t offset,
> + const void *buf, size_t len)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_WRITE),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + };
> + struct fuse_write_in in = {
> + .fh = guest64(fh),
> + .offset = guest64(offset),
> + .size = guest32(len),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + { .iov_base = (void *)buf, .iov_len = len },
> + };
> + struct fuse_out_header out_hdr;
> + struct fuse_write_out out;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + { .iov_base = &out, .iov_len = sizeof(out) },
> + };
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> + g_assert_cmpint(guest32(out_hdr.error), ==, 0);
> +
> + return guest32(out.size);
> +}
> +
> +/* Close a file handle using FUSE_RELEASE */
> +static void fuse_release(QVirtioFS *vfs, uint64_t fh)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_RELEASE),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + };
> + struct fuse_release_in in = {
> + .fh = guest64(fh),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + };
> + struct fuse_out_header out_hdr;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + };
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + g_assert_cmpint(guest32(out_hdr.error), ==, 0);
> +}
> +
> +/* Drop an inode reference using FUSE_FORGET */
> +static void fuse_forget(QVirtioFS *vfs, uint64_t nodeid)
> +{
> + struct fuse_in_header in_hdr = {
> + .opcode = guest32(FUSE_FORGET),
> + .unique = guest64(virtio_fs_get_unique(vfs)),
> + .nodeid = guest64(nodeid),
> + };
> + struct fuse_forget_in in = {
> + .nlookup = guest64(1),
> + };
> + struct iovec sg_in[] = {
> + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> + { .iov_base = &in, .iov_len = sizeof(in) },
> + };
> + struct fuse_out_header out_hdr;
> + struct iovec sg_out[] = {
> + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> + };
> +
> + in_hdr.len = guest32(iov_size(sg_in, G_N_ELEMENTS(sg_in)));
> +
> + do_request(vfs, global_qtest, sg_in, G_N_ELEMENTS(sg_in),
> + sg_out, G_N_ELEMENTS(sg_out));
> +
> + g_assert_cmpint(guest32(out_hdr.error), ==, 0);
> +}
> +
> +/* Check contents of VIRTIO Configuration Space */
> +static void test_config(void *parent, void *arg, QGuestAllocator *alloc)
> +{
> + QVirtioFS *vfs = parent;
> + size_t i;
> + uint32_t num_request_queues;
> + char tag[37];
> +
> + SKIP_TEST_IF_CROSS_ENDIAN();
> +
> + for (i = 0; i < sizeof(tag) - 1; i++) {
> + tag[i] = qvirtio_config_readw(vfs->vdev, i);
> + }
> + tag[36] = '\0';
> +
> + g_assert_cmpstr(tag, ==, VIRTIO_FS_TAG);
> +
> + num_request_queues = qvirtio_config_readl(vfs->vdev,
> + offsetof(struct virtio_fs_config, num_request_queues));
> +
> + g_assert_cmpint(num_request_queues, ==, 1);
> +}
> +
> +/* Create file on host and check its contents and metadata in guest */
> +static void test_file_from_host(void *parent, void *arg, QGuestAllocator *alloc)
> +{
> + g_autofree gchar *filename = g_strdup_printf("%s/%s", shared_dir, "foo");
> + const char *str = "This is a test\n";
> + char buf[strlen(str)];
> + QVirtioFS *vfs = parent;
> + struct fuse_entry_out entry;
> + int32_t error;
> + uint64_t nodeid;
> + uint64_t fh;
> + ssize_t nread;
> + gboolean ok;
> +
> + SKIP_TEST_IF_CROSS_ENDIAN();
> +
> + /* Create the test file in the shared directory */
> + ok = g_file_set_contents(filename, str, strlen(str), NULL);
> + g_assert(ok);
> +
> + fuse_init(vfs);
> +
> + error = fuse_lookup(vfs, FUSE_ROOT_ID, "foo", &entry);
> + g_assert_cmpint(error, ==, 0);
> + g_assert_cmpint(guest64(entry.attr.size), ==, strlen(str));
> + nodeid = guest64(entry.nodeid);
> +
> + error = fuse_open(vfs, nodeid, O_RDONLY, &fh);
> + g_assert_cmpint(error, ==, 0);
> +
> + nread = fuse_read(vfs, fh, 0, buf, sizeof(buf));
> + g_assert_cmpint(nread, ==, sizeof(buf));
> + g_assert_cmpint(memcmp(buf, str, sizeof(buf)), ==, 0);
> +
> + fuse_release(vfs, fh);
> + fuse_forget(vfs, nodeid);
> +}
> +
> +/* Create file from host and check its contents and metadata on host */
> +static void test_file_from_guest(void *parent, void *arg,
> + QGuestAllocator *alloc)
> +{
> + g_autofree gchar *filename = g_strdup_printf("%s/%s", shared_dir, "foo");
> + const char *str = "This is a test\n";
> + gchar *contents = NULL;
> + gsize length = 0;
> + QVirtioFS *vfs = parent;
> + int32_t error;
> + uint64_t nodeid;
> + uint64_t fh;
> + ssize_t nwritten;
> + gboolean ok;
> +
> + SKIP_TEST_IF_CROSS_ENDIAN();
> +
> + fuse_init(vfs);
> +
> + error = fuse_create(vfs, FUSE_ROOT_ID, "foo", 0644, O_CREAT | O_WRONLY,
> + &nodeid, &fh);
> + g_assert_cmpint(error, ==, 0);
> +
> + nwritten = fuse_write(vfs, fh, 0, str, strlen(str));
> + g_assert_cmpint(nwritten, ==, strlen(str));
> +
> + fuse_release(vfs, fh);
> + fuse_forget(vfs, nodeid);
> +
> + /* Check the file on the host */
> + ok = g_file_get_contents(filename, &contents, &length, NULL);
> + g_assert(ok);
> + g_assert_cmpint(length, ==, strlen(str));
> + g_assert_cmpint(memcmp(contents, str, strlen(str)), ==, 0);
> + g_free(contents);
> +}
> +
> +static void register_vhost_user_fs_test(void)
> +{
> + g_autofree gchar *cmd_line =
> + g_strdup_printf("-chardev socket,id=char-virtio-fs,path=%s",
> + socket_path);
> + QOSGraphTestOptions opts = {
> + .edge.before_cmd_line = cmd_line,
> + .before = before_test,
> + .after = after_test,
> + };
> +
> + if (geteuid() != 0) {
> + g_test_message("Skipping vhost-user-fs tests because root is "
> + "required for virtiofsd");
> + return;
> + }
> +
> + qtest_add_abrt_handler(abrt_handler, NULL);
> +
> + qos_add_test("config", "virtio-fs", test_config, &opts);
> + qos_add_test("file-from-host", "virtio-fs", test_file_from_host, &opts);
> + qos_add_test("file-from-guest", "virtio-fs", test_file_from_guest, &opts);
> +}
> +
> +libqos_init(register_vhost_user_fs_test);
> +
> +static void __attribute__((constructor)) init_paths(void)
> +{
> + socket_path = g_strdup_printf("/tmp/qtest-%d-vhost-fs.sock", getpid());
> + shared_dir = g_strdup_printf("/tmp/qtest-%d-virtio-fs-dir", getpid());
> +}
> +
> +static void __attribute__((destructor)) destroy_paths(void)
> +{
> + g_free(shared_dir);
> + shared_dir = NULL;
> +
> + g_free(socket_path);
> + socket_path = NULL;
> +}
> --
> 2.21.0
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case
2019-10-29 0:36 ` Dr. David Alan Gilbert
@ 2019-11-05 16:02 ` Stefan Hajnoczi
2019-11-07 12:26 ` Dr. David Alan Gilbert
0 siblings, 1 reply; 11+ messages in thread
From: Stefan Hajnoczi @ 2019-11-05 16:02 UTC (permalink / raw)
To: Dr. David Alan Gilbert
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
qemu-devel, virtio-fs, Stefan Hajnoczi, Paolo Bonzini, vgoyal
[-- Attachment #1: Type: text/plain, Size: 1758 bytes --]
On Tue, Oct 29, 2019 at 12:36:05AM +0000, Dr. David Alan Gilbert wrote:
> * Stefan Hajnoczi (stefanha@redhat.com) wrote:
> > +static void after_test(void *arg G_GNUC_UNUSED)
> > +{
> > + unlink(socket_path);
> > +
> > + remove_dir_and_children(shared_dir);
>
> This scares me. Especially since it's running as root.
> Can we add a bunch of paranoid checks to make sure it doesn't
> end up rm -rf / ?
Yes, we can resolve the path and check it is not "/".
> > +/* Open a file by nodeid using FUSE_OPEN */
> > +static int32_t fuse_open(QVirtioFS *vfs, uint64_t nodeid, uint32_t flags,
> > + uint64_t *fh)
> > +{
> > + struct fuse_in_header in_hdr = {
> > + .opcode = guest32(FUSE_OPEN),
> > + .unique = guest64(virtio_fs_get_unique(vfs)),
> > + .nodeid = guest64(nodeid),
> > + };
> > + struct fuse_open_in in = {
> > + .flags = guest32(flags),
> > + };
> > + struct iovec sg_in[] = {
> > + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> > + { .iov_base = &in, .iov_len = sizeof(in) },
> > + };
> > + struct fuse_out_header out_hdr;
> > + struct fuse_open_out out;
> > + struct iovec sg_out[] = {
> > + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> > + { .iov_base = &out, .iov_len = sizeof(out) },
> > + };
>
> I wonder if anything can be done to reduce the size of the iovec boiler
> plate?
I'm not aware of a clean way to build the iovec array automatically but
we could do this if you prefer it:
#define IOVEC(elem) { .iov_base = &elem, .iov_len = sizeof(elem) }
struct iovec sg_in[] = {
IOVEC(in_hdr),
IOVEC(in),
};
Do you find this nicer?
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case
2019-11-05 16:02 ` Stefan Hajnoczi
@ 2019-11-07 12:26 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 11+ messages in thread
From: Dr. David Alan Gilbert @ 2019-11-07 12:26 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
qemu-devel, virtio-fs, Stefan Hajnoczi, Paolo Bonzini, vgoyal
* Stefan Hajnoczi (stefanha@gmail.com) wrote:
> On Tue, Oct 29, 2019 at 12:36:05AM +0000, Dr. David Alan Gilbert wrote:
> > * Stefan Hajnoczi (stefanha@redhat.com) wrote:
> > > +static void after_test(void *arg G_GNUC_UNUSED)
> > > +{
> > > + unlink(socket_path);
> > > +
> > > + remove_dir_and_children(shared_dir);
> >
> > This scares me. Especially since it's running as root.
> > Can we add a bunch of paranoid checks to make sure it doesn't
> > end up rm -rf / ?
>
> Yes, we can resolve the path and check it is not "/".
I suggest checking for "/", ".", ".." and ""
if any of those get in it's probably bad.
> > > +/* Open a file by nodeid using FUSE_OPEN */
> > > +static int32_t fuse_open(QVirtioFS *vfs, uint64_t nodeid, uint32_t flags,
> > > + uint64_t *fh)
> > > +{
> > > + struct fuse_in_header in_hdr = {
> > > + .opcode = guest32(FUSE_OPEN),
> > > + .unique = guest64(virtio_fs_get_unique(vfs)),
> > > + .nodeid = guest64(nodeid),
> > > + };
> > > + struct fuse_open_in in = {
> > > + .flags = guest32(flags),
> > > + };
> > > + struct iovec sg_in[] = {
> > > + { .iov_base = &in_hdr, .iov_len = sizeof(in_hdr) },
> > > + { .iov_base = &in, .iov_len = sizeof(in) },
> > > + };
> > > + struct fuse_out_header out_hdr;
> > > + struct fuse_open_out out;
> > > + struct iovec sg_out[] = {
> > > + { .iov_base = &out_hdr, .iov_len = sizeof(out_hdr) },
> > > + { .iov_base = &out, .iov_len = sizeof(out) },
> > > + };
> >
> > I wonder if anything can be done to reduce the size of the iovec boiler
> > plate?
>
> I'm not aware of a clean way to build the iovec array automatically but
> we could do this if you prefer it:
>
> #define IOVEC(elem) { .iov_base = &elem, .iov_len = sizeof(elem) }
>
> struct iovec sg_in[] = {
> IOVEC(in_hdr),
> IOVEC(in),
> };
>
> Do you find this nicer?
Only a little; probably not worth it.
Dave
> Stefan
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file
2019-10-27 12:36 ` Stefan Hajnoczi
@ 2020-06-01 10:28 ` Alex Bennée
2020-06-01 15:55 ` Stefan Hajnoczi
0 siblings, 1 reply; 11+ messages in thread
From: Alex Bennée @ 2020-06-01 10:28 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, qemu-devel, virtio-fs, Paolo Bonzini,
vgoyal
Stefan Hajnoczi <stefanha@redhat.com> writes:
> On Sat, Oct 26, 2019 at 05:49:11PM -0400, Michael S. Tsirkin wrote:
>> On Fri, Oct 25, 2019 at 12:01:50PM +0200, Stefan Hajnoczi wrote:
>> > tests/vhost-user-fs-test.c needs fuse.h. The private copy that
>> > virtiofsd has can be replaced with a properly imported file using
>> > update-linux-headers.sh.
>> >
>> > TODO rerun update-linux-headers.sh with upstream kernel tree!
>> >
>> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>>
>> OK I would just add this with the virtiofsd patchset.
>
> Yes, I'll talk to David.
Catching up after the fact I see this didn't get merged. Was there a
reason?
--
Alex Bennée
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file
2020-06-01 10:28 ` Alex Bennée
@ 2020-06-01 15:55 ` Stefan Hajnoczi
0 siblings, 0 replies; 11+ messages in thread
From: Stefan Hajnoczi @ 2020-06-01 15:55 UTC (permalink / raw)
To: Alex Bennée
Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Cornelia Huck,
Dr. David Alan Gilbert, qemu-devel, virtio-fs, Paolo Bonzini,
vgoyal
[-- Attachment #1: Type: text/plain, Size: 1062 bytes --]
On Mon, Jun 01, 2020 at 11:28:44AM +0100, Alex Bennée wrote:
>
> Stefan Hajnoczi <stefanha@redhat.com> writes:
>
> > On Sat, Oct 26, 2019 at 05:49:11PM -0400, Michael S. Tsirkin wrote:
> >> On Fri, Oct 25, 2019 at 12:01:50PM +0200, Stefan Hajnoczi wrote:
> >> > tests/vhost-user-fs-test.c needs fuse.h. The private copy that
> >> > virtiofsd has can be replaced with a properly imported file using
> >> > update-linux-headers.sh.
> >> >
> >> > TODO rerun update-linux-headers.sh with upstream kernel tree!
> >> >
> >> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> >>
> >> OK I would just add this with the virtiofsd patchset.
> >
> > Yes, I'll talk to David.
>
> Catching up after the fact I see this didn't get merged. Was there a
> reason?
This patch series is stalled because I have been busy with other tasks.
The standard-headers fuse.h header will be needed when something outside
virtiofsd uses FUSE definitions for virtio-fs. At the moment nothing
does so there is no need to merge this patch yet.
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2020-06-01 15:56 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-25 10:01 [RFC 0/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 1/3] WIP virtiofsd: import Linux <fuse.h> header file Stefan Hajnoczi
2019-10-26 21:49 ` Michael S. Tsirkin
2019-10-27 12:36 ` Stefan Hajnoczi
2020-06-01 10:28 ` Alex Bennée
2020-06-01 15:55 ` Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 2/3] qgraph: add an "after" test callback function Stefan Hajnoczi
2019-10-25 10:01 ` [RFC 3/3] tests/vhost-user-fs-test: add vhost-user-fs test case Stefan Hajnoczi
2019-10-29 0:36 ` Dr. David Alan Gilbert
2019-11-05 16:02 ` Stefan Hajnoczi
2019-11-07 12:26 ` Dr. David Alan Gilbert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).