All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] Mini-OS: add minimal 9pfs support
@ 2023-02-13  8:44 Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 1/7] Mini-OS: xenbus: add support for reading node from directory Juergen Gross
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

This series is adding minimal support to use 9pfs in Mini-OS. It is
adding a PV 9pfs frontend and the ability to open, close, read and
write files.

The series has been tested with qemu as 9pfs backend in a PVH Mini-OS
guest (I've used a slightly modified Xenstore-stubdom for that purpose
in order to reuse the build runes).

This series is meant to setup the stage for adding file based logging
support to Xenstore-stubdom and later to add live update support (being
able to save the LU data stream in a dom0 file makes this a _lot_
easier).

In order to keep Mini-OS's license I have only used the protocol docs
available on the internet [1] and then verified those with the qemu 9pfs
backend implementation (especially for supporting the 9P2000.u variant,
as qemu doesn't support the basic 9P2000 protocol).

The needed fixed values of the protocol have been taken from [2].

[1]: http://ericvh.github.io/9p-rfc/rfc9p2000.html
[2]: https://github.com/0intro/libixp

Changes in V2:
- addressed comments by Samuel Thibault

Changes in V3:
- addressed comments by Samuel Thibault and Andrew Cooper

Juergen Gross (7):
  Mini-OS: xenbus: add support for reading node from directory
  Mini-OS: add concept of mount points
  Mini-OS: add support for runtime mounts
  Mini-OS: add 9pfs frontend
  Mini-OS: add 9pfs transport layer
  Mini-OS: add open and close handling to the 9pfs frontend
  Mini-OS: add read and write support to 9pfsfront

 9pfront.c                     | 1300 +++++++++++++++++++++++++++++++++
 Config.mk                     |    1 +
 Makefile                      |    1 +
 arch/x86/testbuild/all-no     |    1 +
 arch/x86/testbuild/all-yes    |    1 +
 arch/x86/testbuild/newxen-yes |    1 +
 include/9pfront.h             |    7 +
 include/lib.h                 |   14 +
 include/xenbus.h              |    6 +
 lib/sys.c                     |  123 +++-
 xenbus.c                      |   40 +-
 11 files changed, 1473 insertions(+), 22 deletions(-)
 create mode 100644 9pfront.c
 create mode 100644 include/9pfront.h

-- 
2.35.3



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v3 1/7] Mini-OS: xenbus: add support for reading node from directory
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 2/7] Mini-OS: add concept of mount points Juergen Gross
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Especially PV device drivers often need to read multiple Xenstore
nodes from a common directory. Add support for reading a string or an
unsigned value by specifying the directory and the node.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
V2:
- check sscanf() return value (Samuel Thibault)
---
 include/xenbus.h |  6 ++++++
 xenbus.c         | 40 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/include/xenbus.h b/include/xenbus.h
index 3871f358..c0fc0ac5 100644
--- a/include/xenbus.h
+++ b/include/xenbus.h
@@ -108,6 +108,12 @@ int xenbus_read_integer(const char *path);
  * read and parsing were successful, 0 if not */
 int xenbus_read_uuid(const char* path, unsigned char uuid[16]);
 
+/* Support functions for reading values from directory/node tuple. */
+char *xenbus_read_string(xenbus_transaction_t xbt, const char *dir,
+                         const char *node, char **value);
+char *xenbus_read_unsigned(xenbus_transaction_t xbt, const char *dir,
+                           const char *node, unsigned int *value);
+
 /* Contraction of snprintf and xenbus_write(path/node). */
 char* xenbus_printf(xenbus_transaction_t xbt,
                                   const char* node, const char* path,
diff --git a/xenbus.c b/xenbus.c
index 81e9b65d..923e8181 100644
--- a/xenbus.c
+++ b/xenbus.c
@@ -936,16 +936,21 @@ int xenbus_read_uuid(const char *path, unsigned char uuid[16])
     return 1;
 }
 
+#define BUFFER_SIZE 256
+static void xenbus_build_path(const char *dir, const char *node, char *res)
+{
+    BUG_ON(strlen(dir) + strlen(node) + 1 >= BUFFER_SIZE);
+    sprintf(res,"%s/%s", dir, node);
+}
+
 char *xenbus_printf(xenbus_transaction_t xbt, const char* node,
                     const char* path, const char* fmt, ...)
 {
-#define BUFFER_SIZE 256
     char fullpath[BUFFER_SIZE];
     char val[BUFFER_SIZE];
     va_list args;
 
-    BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE);
-    sprintf(fullpath,"%s/%s", node, path);
+    xenbus_build_path(node, path, fullpath);
     va_start(args, fmt);
     vsprintf(val, fmt, args);
     va_end(args);
@@ -964,6 +969,35 @@ domid_t xenbus_get_self_id(void)
     return ret;
 }
 
+char *xenbus_read_string(xenbus_transaction_t xbt, const char *dir,
+                         const char *node, char **value)
+{
+    char path[BUFFER_SIZE];
+
+    xenbus_build_path(dir, node, path);
+
+    return xenbus_read(xbt, path, value);
+}
+
+char *xenbus_read_unsigned(xenbus_transaction_t xbt, const char *dir,
+                           const char *node, unsigned int *value)
+{
+    char path[BUFFER_SIZE];
+    char *msg;
+    char *str;
+
+    xenbus_build_path(dir, node, path);
+    msg = xenbus_read(xbt, path, &str);
+    if ( msg )
+        return msg;
+
+    if ( sscanf(str, "%u", value) != 1 )
+        msg = strdup("EINVAL");
+    free(str);
+
+    return msg;
+}
+
 /*
  * Local variables:
  * mode: C
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 2/7] Mini-OS: add concept of mount points
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 1/7] Mini-OS: xenbus: add support for reading node from directory Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 3/7] Mini-OS: add support for runtime mounts Juergen Gross
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add the concept of mount points to Mini-OS. A mount point is a path
associated with a device pointer and an open() callback. A mount point
can be either a file (e.g. "/dev/mem") or a directory ("/var/log").

This allows to replace the special casing in the generic open()
handling with a generic mount point handling.

Prepare the open() callbacks to support creating new files by adding a
mode parameter.

Additionally add a close() prototype to include/lib.h, as it is missing
today.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
V2:
- pass path below mount point to open callbacks (Samuel Thibault)
---
 include/lib.h |  9 ++++++
 lib/sys.c     | 80 +++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 70 insertions(+), 19 deletions(-)

diff --git a/include/lib.h b/include/lib.h
index bec99646..36d94ec4 100644
--- a/include/lib.h
+++ b/include/lib.h
@@ -187,6 +187,13 @@ struct file_ops {
     bool (*select_wr)(struct file *file);
 };
 
+struct mount_point {
+    const char *path;
+    int (*open)(struct mount_point *mnt, const char *pathname, int flags,
+                mode_t mode);
+    void *dev;
+};
+
 unsigned int alloc_file_type(const struct file_ops *ops);
 
 off_t lseek_default(struct file *file, off_t offset, int whence);
@@ -198,6 +205,8 @@ int alloc_fd(unsigned int type);
 void close_all_files(void);
 extern struct thread *main_thread;
 void sparse(unsigned long data, size_t size);
+
+int close(int fd);
 #endif
 
 #endif /* _LIB_H_ */
diff --git a/lib/sys.c b/lib/sys.c
index 8f8a3de2..2f33c937 100644
--- a/lib/sys.c
+++ b/lib/sys.c
@@ -263,11 +263,6 @@ char *getcwd(char *buf, size_t size)
     return buf;
 }
 
-#define LOG_PATH "/var/log/"
-#define SAVE_PATH "/var/lib/xen"
-#define SAVE_CONSOLE 1
-#define RESTORE_CONSOLE 2
-
 int mkdir(const char *pathname, mode_t mode)
 {
     errno = EIO;
@@ -286,18 +281,30 @@ int posix_openpt(int flags)
     return fd;
 }
 
+static int open_pt(struct mount_point *mnt, const char *pathname, int flags,
+                   mode_t mode)
+{
+    return posix_openpt(flags);
+}
+
 int open_savefile(const char *path, int save)
 {
     int fd;
     char nodename[64];
 
-    snprintf(nodename, sizeof(nodename), "device/console/%d", save ? SAVE_CONSOLE : RESTORE_CONSOLE);
+    snprintf(nodename, sizeof(nodename), "device/console/%d", save ? 1 : 2);
 
     fd = open_consfront(nodename);
     printk("fd(%d) = open_savefile\n", fd);
 
     return fd;
 }
+
+static int open_save(struct mount_point *mnt, const char *pathname, int flags,
+                     mode_t mode)
+{
+    return open_savefile(pathname, flags & O_WRONLY);
+}
 #else
 int posix_openpt(int flags)
 {
@@ -311,24 +318,59 @@ int open_savefile(const char *path, int save)
 }
 #endif
 
-int open(const char *pathname, int flags, ...)
+static int open_log(struct mount_point *mnt, const char *pathname, int flags,
+                    mode_t mode)
 {
     int fd;
+
     /* Ugly, but fine.  */
-    if (!strncmp(pathname,LOG_PATH,strlen(LOG_PATH))) {
-	fd = alloc_fd(FTYPE_CONSOLE);
-        printk("open(%s) -> %d\n", pathname, fd);
-        return fd;
+    fd = alloc_fd(FTYPE_CONSOLE);
+    printk("open(%s%s) -> %d\n", mnt->path, pathname, fd);
+    return fd;
+}
+
+static int open_mem(struct mount_point *mnt, const char *pathname, int flags,
+                    mode_t mode)
+{
+    int fd;
+
+    fd = alloc_fd(FTYPE_MEM);
+    printk("open(%s%s) -> %d\n", mnt->path, pathname, fd);
+    return fd;
+}
+
+static struct mount_point mount_points[] = {
+    { .path = "/var/log",     .open = open_log,  .dev = NULL },
+    { .path = "/dev/mem",     .open = open_mem,  .dev = NULL },
+#ifdef CONFIG_CONSFRONT
+    { .path = "/dev/ptmx",    .open = open_pt,   .dev = NULL },
+    { .path = "/var/lib/xen", .open = open_save, .dev = NULL },
+#endif
+};
+
+int open(const char *pathname, int flags, ...)
+{
+    unsigned int m, mlen;
+    struct mount_point *mnt;
+    mode_t mode = 0;
+    va_list ap;
+
+    if ( flags & O_CREAT )
+    {
+        va_start(ap, flags);
+        mode = va_arg(ap, mode_t);
+        va_end(ap);
     }
-    if (!strncmp(pathname, "/dev/mem", strlen("/dev/mem"))) {
-        fd = alloc_fd(FTYPE_MEM);
-        printk("open(/dev/mem) -> %d\n", fd);
-        return fd;
+
+    for ( m = 0; m < ARRAY_SIZE(mount_points); m++ )
+    {
+        mnt = mount_points + m;
+        mlen = strlen(mnt->path);
+        if ( !strncmp(pathname, mnt->path, mlen) &&
+             (pathname[mlen] == '/' || pathname[mlen] == 0) )
+            return mnt->open(mnt, pathname + mlen, flags, mode);
     }
-    if (!strncmp(pathname, "/dev/ptmx", strlen("/dev/ptmx")))
-        return posix_openpt(flags);
-    if (!strncmp(pathname,SAVE_PATH,strlen(SAVE_PATH)))
-        return open_savefile(pathname, flags & O_WRONLY);
+
     errno = EIO;
     return -1;
 }
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 3/7] Mini-OS: add support for runtime mounts
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 1/7] Mini-OS: xenbus: add support for reading node from directory Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 2/7] Mini-OS: add concept of mount points Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 4/7] Mini-OS: add 9pfs frontend Juergen Gross
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add the support to mount a device at runtime. The number of dynamic
mounts is limited by a #define.

For devices supporting multiple files struct file is modified to hold
a pointer to file specific data in contrast to device specific data.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
V3:
- set number of possible mountpoints to 16 (Andrew Cooper)
---
 include/lib.h |  5 +++++
 lib/sys.c     | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/include/lib.h b/include/lib.h
index 36d94ec4..fd8c36de 100644
--- a/include/lib.h
+++ b/include/lib.h
@@ -172,6 +172,7 @@ struct file {
     union {
         int fd; /* Any fd from an upper layer. */
         void *dev;
+        void *filedata;
     };
 };
 
@@ -194,6 +195,10 @@ struct mount_point {
     void *dev;
 };
 
+int mount(const char *path, void *dev,
+          int (*open)(struct mount_point *, const char *, int, mode_t));
+void umount(const char *path);
+
 unsigned int alloc_file_type(const struct file_ops *ops);
 
 off_t lseek_default(struct file *file, off_t offset, int whence);
diff --git a/lib/sys.c b/lib/sys.c
index 2f33c937..118fc441 100644
--- a/lib/sys.c
+++ b/lib/sys.c
@@ -85,6 +85,8 @@
     }
 
 #define NOFILE 32
+#define N_MOUNTS  16
+
 extern void minios_evtchn_close_fd(int fd);
 extern void minios_gnttab_close_fd(int fd);
 
@@ -339,7 +341,7 @@ static int open_mem(struct mount_point *mnt, const char *pathname, int flags,
     return fd;
 }
 
-static struct mount_point mount_points[] = {
+static struct mount_point mount_points[N_MOUNTS] = {
     { .path = "/var/log",     .open = open_log,  .dev = NULL },
     { .path = "/dev/mem",     .open = open_mem,  .dev = NULL },
 #ifdef CONFIG_CONSFRONT
@@ -365,6 +367,8 @@ int open(const char *pathname, int flags, ...)
     for ( m = 0; m < ARRAY_SIZE(mount_points); m++ )
     {
         mnt = mount_points + m;
+        if ( !mnt->path )
+            continue;
         mlen = strlen(mnt->path);
         if ( !strncmp(pathname, mnt->path, mlen) &&
              (pathname[mlen] == '/' || pathname[mlen] == 0) )
@@ -375,6 +379,45 @@ int open(const char *pathname, int flags, ...)
     return -1;
 }
 
+int mount(const char *path, void *dev,
+          int (*open)(struct mount_point *, const char *, int, mode_t))
+{
+    unsigned int m;
+    struct mount_point *mnt;
+
+    for ( m = 0; m < ARRAY_SIZE(mount_points); m++ )
+    {
+        mnt = mount_points + m;
+        if ( !mnt->path )
+        {
+            mnt->path = strdup(path);
+            mnt->open = open;
+            mnt->dev = dev;
+            return 0;
+        }
+    }
+
+    errno = ENOSPC;
+    return -1;
+}
+
+void umount(const char *path)
+{
+    unsigned int m;
+    struct mount_point *mnt;
+
+    for ( m = 0; m < ARRAY_SIZE(mount_points); m++ )
+    {
+        mnt = mount_points + m;
+        if ( mnt->path && !strcmp(mnt->path, path) )
+        {
+            free((char *)mnt->path);
+            mnt->path = NULL;
+            return;
+        }
+    }
+}
+
 int isatty(int fd)
 {
     return files[fd].type == FTYPE_CONSOLE;
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 4/7] Mini-OS: add 9pfs frontend
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
                   ` (2 preceding siblings ...)
  2023-02-13  8:44 ` [PATCH v3 3/7] Mini-OS: add support for runtime mounts Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 5/7] Mini-OS: add 9pfs transport layer Juergen Gross
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add a frontend for the 9pfs PV device. For now add only the code needed
to connect to the backend and the related disconnect functionality. The
9pfs protocol support will be added later.

Due to its nature (ability to access files) the whole code is guarded
by "#ifdef HAVE_LIBC".

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
V2:
- add better error handling to version parsing (Samuel Thibault)
---
 9pfront.c                     | 286 ++++++++++++++++++++++++++++++++++
 Config.mk                     |   1 +
 Makefile                      |   1 +
 arch/x86/testbuild/all-no     |   1 +
 arch/x86/testbuild/all-yes    |   1 +
 arch/x86/testbuild/newxen-yes |   1 +
 include/9pfront.h             |   7 +
 7 files changed, 298 insertions(+)
 create mode 100644 9pfront.c
 create mode 100644 include/9pfront.h

diff --git a/9pfront.c b/9pfront.c
new file mode 100644
index 00000000..89ecb3a1
--- /dev/null
+++ b/9pfront.c
@@ -0,0 +1,286 @@
+/*
+ * Minimal 9pfs PV frontend for Mini-OS.
+ * Copyright (c) 2023 Juergen Gross, SUSE Software Solution GmbH
+ */
+
+#include <mini-os/os.h>
+#include <mini-os/lib.h>
+#include <mini-os/events.h>
+#include <mini-os/gnttab.h>
+#include <mini-os/xenbus.h>
+#include <mini-os/xmalloc.h>
+#include <errno.h>
+#include <xen/io/9pfs.h>
+#include <mini-os/9pfront.h>
+
+#ifdef HAVE_LIBC
+struct dev_9pfs {
+    int id;
+    char nodename[20];
+    unsigned int dom;
+    char *backend;
+
+    char *tag;
+    const char *mnt;
+
+    struct xen_9pfs_data_intf *intf;
+    struct xen_9pfs_data data;
+    RING_IDX prod_pvt_out;
+    RING_IDX cons_pvt_in;
+
+    grant_ref_t ring_ref;
+    evtchn_port_t evtchn;
+    unsigned int ring_order;
+    xenbus_event_queue events;
+};
+
+#define DEFAULT_9PFS_RING_ORDER  4
+
+static unsigned int ftype_9pfs;
+
+static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
+{
+}
+
+static int open_9pfs(struct mount_point *mnt, const char *pathname, int flags,
+                     mode_t mode)
+{
+    errno = ENOSYS;
+
+    return -1;
+}
+
+static void free_9pfront(struct dev_9pfs *dev)
+{
+    unsigned int i;
+
+    if ( dev->data.in && dev->intf )
+    {
+        for ( i = 0; i < (1 << dev->ring_order); i++ )
+            gnttab_end_access(dev->intf->ref[i]);
+        free_pages(dev->data.in, dev->ring_order);
+    }
+    unbind_evtchn(dev->evtchn);
+    gnttab_end_access(dev->ring_ref);
+    free_page(dev->intf);
+    free(dev->backend);
+    free(dev->tag);
+    free(dev);
+}
+
+void *init_9pfront(unsigned int id, const char *mnt)
+{
+    struct dev_9pfs *dev;
+    char *msg;
+    char *reason = "";
+    xenbus_transaction_t xbt;
+    int retry = 1;
+    char bepath[64] = { 0 };
+    XenbusState state;
+    unsigned int i;
+    void *addr;
+    char *version;
+    char *v;
+
+    printk("9pfsfront add %u, for mount at %s\n", id, mnt);
+    dev = malloc(sizeof(*dev));
+    memset(dev, 0, sizeof(*dev));
+    snprintf(dev->nodename, sizeof(dev->nodename), "device/9pfs/%u", id);
+    dev->id = id;
+
+    msg = xenbus_read_unsigned(XBT_NIL, dev->nodename, "backend-id", &dev->dom);
+    if ( msg )
+        goto err;
+    msg = xenbus_read_string(XBT_NIL, dev->nodename, "backend", &dev->backend);
+    if ( msg )
+        goto err;
+    msg = xenbus_read_string(XBT_NIL, dev->nodename, "tag", &dev->tag);
+    if ( msg )
+        goto err;
+
+    snprintf(bepath, sizeof(bepath), "%s/state", dev->backend);
+    free(xenbus_watch_path_token(XBT_NIL, bepath, bepath, &dev->events));
+    state = xenbus_read_integer(bepath);
+    while ( msg == NULL && state < XenbusStateInitWait )
+        msg = xenbus_wait_for_state_change(bepath, &state, &dev->events);
+    if ( msg || state != XenbusStateInitWait )
+    {
+        reason = "illegal backend state";
+        goto err;
+    }
+
+    msg = xenbus_read_unsigned(XBT_NIL, dev->backend, "max-ring-page-order",
+                               &dev->ring_order);
+    if ( msg )
+        goto err;
+    if ( dev->ring_order > DEFAULT_9PFS_RING_ORDER )
+        dev->ring_order = DEFAULT_9PFS_RING_ORDER;
+
+    msg = xenbus_read_string(XBT_NIL, dev->backend, "versions", &version);
+    if ( msg )
+        goto err;
+    for ( v = version; *v; v++ )
+    {
+        if ( strtoul(v, &v, 10) == 1 && (*v == ',' || *v == 0) )
+        {
+            v = NULL;
+            break;
+        }
+        if ( *v != ',' && *v != 0 )
+        {
+            reason = "backend published illegal version string";
+            free(version);
+            goto err;
+        }
+    }
+    free(version);
+    if ( v )
+    {
+        reason = "backend doesn't support version 1";
+        goto err;
+    }
+
+    dev->ring_ref = gnttab_alloc_and_grant((void **)&dev->intf);
+    memset(dev->intf, 0, PAGE_SIZE);
+    if ( evtchn_alloc_unbound(dev->dom, intr_9pfs, dev, &dev->evtchn) )
+    {
+        reason = "no event channel";
+        goto err;
+    }
+    dev->intf->ring_order = dev->ring_order;
+    dev->data.in = (void *)alloc_pages(dev->ring_order);
+    dev->data.out = dev->data.in + XEN_FLEX_RING_SIZE(dev->ring_order);
+    for ( i = 0; i < (1 << dev->ring_order); i++ )
+    {
+        addr = dev->data.in + i * PAGE_SIZE;
+        dev->intf->ref[i] = gnttab_grant_access(dev->dom, virt_to_mfn(addr), 0);
+    }
+
+    while ( retry )
+    {
+        msg = xenbus_transaction_start(&xbt);
+        if ( msg )
+        {
+            free(msg);
+            msg = NULL;
+            reason = "starting transaction";
+            goto err;
+        }
+
+        msg = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
+        if ( msg )
+            goto err_tr;
+        msg = xenbus_printf(xbt, dev->nodename, "num-rings", "%u", 1);
+        if ( msg )
+            goto err_tr;
+        msg = xenbus_printf(xbt, dev->nodename, "ring-ref0", "%u",
+                            dev->ring_ref);
+        if ( msg )
+            goto err_tr;
+        msg = xenbus_printf(xbt, dev->nodename, "event-channel-0", "%u",
+                            dev->evtchn);
+        if ( msg )
+            goto err_tr;
+        msg = xenbus_printf(xbt, dev->nodename, "state", "%u",
+                            XenbusStateInitialised);
+        if ( msg )
+            goto err_tr;
+
+        free(xenbus_transaction_end(xbt, 0, &retry));
+    }
+
+    state = xenbus_read_integer(bepath);
+    while ( msg == NULL && state < XenbusStateConnected )
+        msg = xenbus_wait_for_state_change(bepath, &state, &dev->events);
+    if ( msg || state != XenbusStateConnected )
+    {
+        reason = "illegal backend state";
+        goto err;
+    }
+
+    msg = xenbus_printf(XBT_NIL, dev->nodename, "state", "%u",
+                        XenbusStateConnected);
+    if ( msg )
+        goto err;
+
+    unmask_evtchn(dev->evtchn);
+
+    dev->mnt = mnt;
+    if ( mount(dev->mnt, dev, open_9pfs) )
+    {
+        reason = "mount failed";
+        goto err;
+    }
+
+    return dev;
+
+ err_tr:
+    free(xenbus_transaction_end(xbt, 1, &retry));
+
+ err:
+    if ( bepath[0] )
+        free(xenbus_unwatch_path_token(XBT_NIL, bepath, bepath));
+    if ( msg )
+        printk("9pfsfront add %u failed, error %s accessing Xenstore\n",
+               id, msg);
+    else
+        printk("9pfsfront add %u failed, %s\n", id, reason);
+    free_9pfront(dev);
+    free(msg);
+    return NULL;
+}
+
+void shutdown_9pfront(void *dev)
+{
+    struct dev_9pfs *dev9p = dev;
+    char bepath[64];
+    XenbusState state;
+    char *msg;
+    char *reason = "";
+
+    umount(dev9p->mnt);
+    snprintf(bepath, sizeof(bepath), "%s/state", dev9p->backend);
+
+    msg = xenbus_printf(XBT_NIL, dev9p->nodename, "state", "%u",
+                        XenbusStateClosing);
+    if ( msg )
+        goto err;
+
+    state = xenbus_read_integer(bepath);
+    while ( msg == NULL && state < XenbusStateClosing)
+        msg = xenbus_wait_for_state_change(bepath, &state, &dev9p->events);
+    if ( msg || state != XenbusStateClosing )
+    {
+        reason = "illegal backend state";
+        goto err;
+    }
+
+    msg = xenbus_printf(XBT_NIL, dev9p->nodename, "state", "%u",
+                        XenbusStateClosed);
+    if ( msg )
+        goto err;
+
+    free_9pfront(dev9p);
+
+    return;
+
+ err:
+    if ( msg )
+        printk("9pfsfront shutdown %u failed, error %s accessing Xenstore\n",
+               dev9p->id, msg);
+    else
+        printk("9pfsfront shutdown %u failed, %s\n", dev9p->id, reason);
+    free(msg);
+}
+
+static const struct file_ops ops_9pfs = {
+    .name = "9pfs",
+};
+
+__attribute__((constructor))
+static void initialize_9pfs(void)
+{
+    ftype_9pfs = alloc_file_type(&ops_9pfs);
+}
+
+#endif /* HAVE_LIBC */
diff --git a/Config.mk b/Config.mk
index 1a24b30e..677e93df 100644
--- a/Config.mk
+++ b/Config.mk
@@ -187,6 +187,7 @@ CONFIG-n += CONFIG_QEMU_XS_ARGS
 CONFIG-n += CONFIG_TEST
 CONFIG-n += CONFIG_PCIFRONT
 CONFIG-n += CONFIG_TPMFRONT
+CONFIG-n += CONFIG_9PFRONT
 CONFIG-n += CONFIG_TPM_TIS
 CONFIG-n += CONFIG_TPMBACK
 CONFIG-n += CONFIG_BALLOON
diff --git a/Makefile b/Makefile
index 747c7c01..7ee181a2 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,7 @@ SUBDIRS := lib
 src-$(CONFIG_BLKFRONT) += blkfront.c
 src-$(CONFIG_CONSFRONT) += consfront.c
 src-$(CONFIG_TPMFRONT) += tpmfront.c
+src-$(CONFIG_9PFRONT) += 9pfront.c
 src-$(CONFIG_TPM_TIS) += tpm_tis.c
 src-$(CONFIG_TPMBACK) += tpmback.c
 src-y += console.c
diff --git a/arch/x86/testbuild/all-no b/arch/x86/testbuild/all-no
index f79a1012..5b3e99ed 100644
--- a/arch/x86/testbuild/all-no
+++ b/arch/x86/testbuild/all-no
@@ -12,6 +12,7 @@ CONFIG_NETFRONT = n
 CONFIG_FBFRONT = n
 CONFIG_KBDFRONT = n
 CONFIG_CONSFRONT = n
+CONFIG_9PFRONT = n
 CONFIG_XENBUS = n
 CONFIG_LIBXS = n
 CONFIG_LWIP = n
diff --git a/arch/x86/testbuild/all-yes b/arch/x86/testbuild/all-yes
index faa3af32..8ae489a4 100644
--- a/arch/x86/testbuild/all-yes
+++ b/arch/x86/testbuild/all-yes
@@ -12,6 +12,7 @@ CONFIG_NETFRONT = y
 CONFIG_FBFRONT = y
 CONFIG_KBDFRONT = y
 CONFIG_CONSFRONT = y
+CONFIG_9PFRONT = y
 CONFIG_XENBUS = y
 CONFIG_LIBXS = y
 CONFIG_BALLOON = y
diff --git a/arch/x86/testbuild/newxen-yes b/arch/x86/testbuild/newxen-yes
index dc83e670..ee27a328 100644
--- a/arch/x86/testbuild/newxen-yes
+++ b/arch/x86/testbuild/newxen-yes
@@ -12,6 +12,7 @@ CONFIG_NETFRONT = y
 CONFIG_FBFRONT = y
 CONFIG_KBDFRONT = y
 CONFIG_CONSFRONT = y
+CONFIG_9PFRONT = y
 CONFIG_XENBUS = y
 CONFIG_LIBXS = y
 CONFIG_BALLOON = y
diff --git a/include/9pfront.h b/include/9pfront.h
new file mode 100644
index 00000000..722ec564
--- /dev/null
+++ b/include/9pfront.h
@@ -0,0 +1,7 @@
+#ifndef __9PFRONT_H__
+#define __9PFRONT_H__
+
+void *init_9pfront(unsigned int id, const char *mnt);
+void shutdown_9pfront(void *dev);
+
+#endif /* __9PFRONT_H__ */
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 5/7] Mini-OS: add 9pfs transport layer
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
                   ` (3 preceding siblings ...)
  2023-02-13  8:44 ` [PATCH v3 4/7] Mini-OS: add 9pfs frontend Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13  8:44 ` [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend Juergen Gross
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add the transport layer of 9pfs. This is basically the infrastructure
to send requests to the backend and to receive the related answers via
the rings.

As a first example add the version and attach requests of the 9pfs
protocol when mounting a new 9pfs device. For the version use the
"9P2000.u" variant, as it is the smallest subset supported by the qemu
based backend.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
V2:
- add more comments (Samuel Thibault)
- log short copy (Samuel Thibault)
- send event after consuming response (Samuel Thibault)
- reaturn EAGAIN in case no free request could be got (Samuel Thibault)
---
 9pfront.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 478 insertions(+)

diff --git a/9pfront.c b/9pfront.c
index 89ecb3a1..0b8d5461 100644
--- a/9pfront.c
+++ b/9pfront.c
@@ -7,6 +7,8 @@
 #include <mini-os/lib.h>
 #include <mini-os/events.h>
 #include <mini-os/gnttab.h>
+#include <mini-os/semaphore.h>
+#include <mini-os/wait.h>
 #include <mini-os/xenbus.h>
 #include <mini-os/xmalloc.h>
 #include <errno.h>
@@ -14,6 +16,9 @@
 #include <mini-os/9pfront.h>
 
 #ifdef HAVE_LIBC
+
+#define N_REQS   64
+
 struct dev_9pfs {
     int id;
     char nodename[20];
@@ -22,6 +27,7 @@ struct dev_9pfs {
 
     char *tag;
     const char *mnt;
+    unsigned int msize_max;
 
     struct xen_9pfs_data_intf *intf;
     struct xen_9pfs_data data;
@@ -32,14 +38,470 @@ struct dev_9pfs {
     evtchn_port_t evtchn;
     unsigned int ring_order;
     xenbus_event_queue events;
+
+    unsigned int free_reqs;
+    struct req {
+        unsigned int id;
+        unsigned int next_free;     /* N_REQS == end of list. */
+        unsigned int cmd;
+        int result;
+        bool inflight;
+        unsigned char *data;        /* Returned data. */
+    } req[N_REQS];
+
+    struct wait_queue_head waitq;
+    struct semaphore ring_out_sem;
+    struct semaphore ring_in_sem;
 };
 
 #define DEFAULT_9PFS_RING_ORDER  4
 
+#define P9_CMD_VERSION    100
+#define P9_CMD_ATTACH     104
+#define P9_CMD_ERROR      107
+
+#define P9_QID_SIZE    13
+
+struct p9_header {
+    uint32_t size;
+    uint8_t cmd;
+    uint16_t tag;
+} __attribute__((packed));
+
+#define P9_VERSION        "9P2000.u"
+#define P9_ROOT_FID       ~0
+
 static unsigned int ftype_9pfs;
 
+static struct req *get_free_req(struct dev_9pfs *dev)
+{
+    struct req *req;
+
+    if ( dev->free_reqs == N_REQS )
+        return NULL;
+
+    req = dev->req + dev->free_reqs;
+    dev->free_reqs = req->next_free;
+
+    return req;
+}
+
+static void put_free_req(struct dev_9pfs *dev, struct req *req)
+{
+    req->next_free = dev->free_reqs;
+    req->inflight = false;
+    req->data = NULL;
+    dev->free_reqs = req->id;
+}
+
+static unsigned int ring_out_free(struct dev_9pfs *dev)
+{
+    RING_IDX ring_size = XEN_FLEX_RING_SIZE(dev->ring_order);
+    unsigned int queued;
+
+    queued = xen_9pfs_queued(dev->prod_pvt_out, dev->intf->out_cons, ring_size);
+    rmb();
+
+    return ring_size - queued;
+}
+
+static unsigned int ring_in_data(struct dev_9pfs *dev)
+{
+    RING_IDX ring_size = XEN_FLEX_RING_SIZE(dev->ring_order);
+    unsigned int queued;
+
+    queued = xen_9pfs_queued(dev->intf->in_prod, dev->cons_pvt_in, ring_size);
+    rmb();
+
+    return queued;
+}
+
+static void copy_to_ring(struct dev_9pfs *dev, void *data, unsigned int len)
+{
+    RING_IDX ring_size = XEN_FLEX_RING_SIZE(dev->ring_order);
+    RING_IDX prod = xen_9pfs_mask(dev->prod_pvt_out, ring_size);
+    RING_IDX cons = xen_9pfs_mask(dev->intf->out_cons, ring_size);
+
+    xen_9pfs_write_packet(dev->data.out, data, len, &prod, cons, ring_size);
+    dev->prod_pvt_out += len;
+}
+
+static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int len)
+{
+    RING_IDX ring_size = XEN_FLEX_RING_SIZE(dev->ring_order);
+    RING_IDX prod = xen_9pfs_mask(dev->intf->in_prod, ring_size);
+    RING_IDX cons = xen_9pfs_mask(dev->cons_pvt_in, ring_size);
+
+    xen_9pfs_read_packet(data, dev->data.in, len, prod, &cons, ring_size);
+    dev->cons_pvt_in += len;
+}
+
+/*
+ * send_9p() and rcv_9p() are using a special format string for specifying
+ * the kind of data sent/expected. Each data item is represented by a single
+ * character:
+ * U: 4 byte unsigned integer (uint32_t)
+ * S: String (2 byte length + <length> characters)
+ *    in the rcv_9p() case the data for string is allocated (length omitted,
+ *    string terminated by a NUL character)
+ * Q: A 13 byte "qid", consisting of 1 byte file type, 4 byte file version
+ *    and 8 bytes unique file id. Only valid for receiving.
+ */
+static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
+{
+    struct p9_header hdr;
+    va_list ap, aq;
+    const char *f;
+    uint32_t intval;
+    uint16_t len;
+    char *strval;
+
+    hdr.size = sizeof(hdr);
+    hdr.cmd = req->cmd;
+    hdr.tag = req->id;
+
+    va_start(ap, fmt);
+
+    va_copy(aq, ap);
+    for ( f = fmt; *f; f++ )
+    {
+        switch ( *f )
+        {
+        case 'U':
+            hdr.size += 4;
+            intval = va_arg(aq, unsigned int);
+            break;
+        case 'S':
+            hdr.size += 2;
+            strval = va_arg(aq, char *);
+            hdr.size += strlen(strval);
+            break;
+        default:
+            printk("send_9p: unknown format character %c\n", *f);
+            break;
+        }
+    }
+    va_end(aq);
+
+    /*
+     * Waiting for free space must be done in the critical section!
+     * Otherwise we might get overtaken by other short requests.
+     */
+    down(&dev->ring_out_sem);
+
+    wait_event(dev->waitq, ring_out_free(dev) >= hdr.size);
+
+    copy_to_ring(dev, &hdr, sizeof(hdr));
+    for ( f = fmt; *f; f++ )
+    {
+        switch ( *f )
+        {
+        case 'U':
+            intval = va_arg(ap, unsigned int);
+            copy_to_ring(dev, &intval, sizeof(intval));
+            break;
+        case 'S':
+            strval = va_arg(ap, char *);
+            len = strlen(strval);
+            copy_to_ring(dev, &len, sizeof(len));
+            copy_to_ring(dev, strval, len);
+            break;
+        }
+    }
+
+    wmb();   /* Data on ring must be seen before updating index. */
+    dev->intf->out_prod = dev->prod_pvt_out;
+    req->inflight = true;
+
+    up(&dev->ring_out_sem);
+
+    va_end(ap);
+
+    notify_remote_via_evtchn(dev->evtchn);
+}
+
+/*
+ * Using an opportunistic approach for receiving data: in case multiple
+ * requests are outstanding (which is very unlikely), we nevertheless need
+ * to consume all data available until we reach the desired request.
+ * For requests other than the one we are waiting for, we link the complete
+ * data to the request via an intermediate buffer. For our own request we can
+ * omit that buffer and directly fill the caller provided variables.
+ *
+ * Helper functions:
+ *
+ * copy_bufs(): copy raw data into a target buffer. There can be 2 source
+ *   buffers involved (in case the copy is done from the ring and it is across
+ *   the ring end). The buffer pointers and lengths are updated according to
+ *   the number of bytes copied.
+ *
+ * rcv_9p_copy(): copy the data (without the generic header) of a 9p response
+ *   to the specified variables using the specified format string for
+ *   deciphering the single item types. The decision whether to copy from the
+ *   ring or an allocated buffer is done via the "hdr" parameter, which is
+ *   NULL in the buffer case (in that case the header is located at the start
+ *   of the buffer).
+ *
+ * rcv_9p_one(): Checks for an already filled buffer with the correct tag in
+ *   it. If none is found, consumes one response. It checks the tag of the
+ *   response in order to decide whether to allocate a buffer for putting the
+ *   data into, or to fill the user supplied variables. Return true, if the
+ *   tag did match. Waits if no data is ready to be consumed.
+ */
+static void copy_bufs(unsigned char **buf1, unsigned char **buf2,
+                      unsigned int *len1, unsigned int *len2,
+                      void *target, unsigned int len)
+{
+    if ( len <= *len1 )
+    {
+        memcpy(target, *buf1, len);
+        *buf1 += len;
+        *len1 -= len;
+    }
+    else
+    {
+        memcpy(target, *buf1, *len1);
+        target = (char *)target + *len1;
+        len -= *len1;
+        *buf1 = *buf2;
+        *len1 = *len2;
+        *buf2 = NULL;
+        *len2 = 0;
+        if ( len > *len1 )
+        {
+            printk("9pfs: short copy (dropping %u bytes)\n", len - *len1);
+            len = *len1;
+        }
+        memcpy(target, *buf1, *len1);
+    }
+}
+
+static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
+                        struct p9_header *hdr, const char *fmt, va_list ap)
+{
+    struct p9_header *h = hdr ? hdr : (void *)req->data;
+    RING_IDX cons = dev->cons_pvt_in + h->size - sizeof(*h);
+    RING_IDX ring_size = XEN_FLEX_RING_SIZE(dev->ring_order);
+    unsigned char *buf1, *buf2;
+    unsigned int len1, len2;
+    const char *f;
+    char *str;
+    uint16_t len;
+    uint32_t err;
+    uint32_t *val;
+    char **strval;
+    uint8_t *qval;
+
+    if ( hdr )
+    {
+        buf1 = xen_9pfs_get_ring_ptr(dev->data.in, dev->cons_pvt_in, ring_size);
+        buf2 = xen_9pfs_get_ring_ptr(dev->data.in, 0,  ring_size);
+        len1 = ring_size - xen_9pfs_mask(dev->cons_pvt_in, ring_size);
+        if ( len1 > h->size - sizeof(*h) )
+            len1 = h->size - sizeof(*h);
+        len2 = h->size - sizeof(*h) - len1;
+    }
+    else
+    {
+        buf1 = req->data + sizeof(*h);
+        buf2 = NULL;
+        len1 = h->size - sizeof(*h);
+        len2 = 0;
+    }
+
+    if ( h->cmd == P9_CMD_ERROR )
+    {
+        copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len));
+        str = malloc(len + 1);
+        copy_bufs(&buf1, &buf2, &len1, &len2, str, len);
+        str[len] = 0;
+        printk("9pfs: request %u resulted in \"%s\"\n", req->cmd, str);
+        free(str);
+        err = EIO;
+        copy_bufs(&buf1, &buf2, &len1, &len2, &err, sizeof(err));
+        req->result = err;
+
+        if ( hdr )
+            dev->cons_pvt_in = cons;
+
+        return;
+    }
+
+    if ( h->cmd != req->cmd + 1 )
+    {
+        req->result = EDOM;
+        printk("9pfs: illegal response: wrong return type (%u instead of %u)\n",
+               h->cmd, req->cmd + 1);
+
+        if ( hdr )
+            dev->cons_pvt_in = cons;
+
+        return;
+    }
+
+    req->result = 0;
+
+    for ( f = fmt; *f; f++ )
+    {
+        switch ( *f )
+        {
+        case 'U':
+            val = va_arg(ap, uint32_t *);
+            copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
+            break;
+        case 'S':
+            strval = va_arg(ap, char **);
+            copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len));
+            *strval = malloc(len + 1);
+            copy_bufs(&buf1, &buf2, &len1, &len2, *strval, len);
+            (*strval)[len] = 0;
+            break;
+        case 'Q':
+            qval = va_arg(ap, uint8_t *);
+            copy_bufs(&buf1, &buf2, &len1, &len2, qval, P9_QID_SIZE);
+            break;
+        default:
+            printk("rcv_9p: unknown format character %c\n", *f);
+            break;
+        }
+    }
+
+    if ( hdr )
+        dev->cons_pvt_in = cons;
+}
+
+static bool rcv_9p_one(struct dev_9pfs *dev, struct req *req, const char *fmt,
+                       va_list ap)
+{
+    struct p9_header hdr;
+    struct req *tmp;
+
+    if ( req->data )
+    {
+        rcv_9p_copy(dev, req, NULL, fmt, ap);
+        free(req->data);
+        req->data = NULL;
+
+        return true;
+    }
+
+    wait_event(dev->waitq, ring_in_data(dev) >= sizeof(hdr));
+
+    copy_from_ring(dev, &hdr, sizeof(hdr));
+
+    wait_event(dev->waitq, ring_in_data(dev) >= hdr.size - sizeof(hdr));
+
+    tmp = dev->req + hdr.tag;
+    if ( hdr.tag >= N_REQS || !tmp->inflight )
+    {
+        printk("9pfs: illegal response: %s\n",
+               hdr.tag >= N_REQS ? "tag out of bounds" : "request not pending");
+        dev->cons_pvt_in += hdr.size - sizeof(hdr);
+
+        return false;
+    }
+
+    tmp->inflight = false;
+
+    if ( tmp != req )
+    {
+        tmp->data = malloc(hdr.size);
+        memcpy(tmp->data, &hdr, sizeof(hdr));
+        copy_from_ring(dev, tmp->data + sizeof(hdr), hdr.size - sizeof(hdr));
+
+        return false;
+    }
+
+    rcv_9p_copy(dev, req, &hdr, fmt, ap);
+
+    return true;
+}
+
+static void rcv_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+
+    down(&dev->ring_in_sem);
+
+    while ( !rcv_9p_one(dev, req, fmt, ap) );
+
+    rmb(); /* Read all data before updating ring index. */
+    dev->intf->in_cons = dev->cons_pvt_in;
+
+    notify_remote_via_evtchn(dev->evtchn);
+
+    up(&dev->ring_in_sem);
+
+    va_end(ap);
+}
+
+static int p9_version(struct dev_9pfs *dev)
+{
+    unsigned int msize = XEN_FLEX_RING_SIZE(dev->ring_order) / 2;
+    struct req *req = get_free_req(dev);
+    char *verret;
+    int ret;
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_VERSION;
+    send_9p(dev, req, "US", msize, P9_VERSION);
+    rcv_9p(dev, req, "US", &dev->msize_max, &verret);
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    if ( ret )
+        return ret;
+
+    if ( strcmp(verret, P9_VERSION) )
+        ret = ENOMSG;
+    free(verret);
+
+    return ret;
+}
+
+static int p9_attach(struct dev_9pfs *dev)
+{
+    uint32_t fid = P9_ROOT_FID;
+    uint32_t afid = 0;
+    uint32_t uid = 0;
+    uint8_t qid[P9_QID_SIZE];
+    struct req *req = get_free_req(dev);
+    int ret;
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_ATTACH;
+    send_9p(dev, req, "UUSSU", fid, afid, "root", "root", uid);
+    rcv_9p(dev, req, "Q", qid);
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int connect_9pfs(struct dev_9pfs *dev)
+{
+    int ret;
+
+    ret = p9_version(dev);
+    if ( ret )
+        return ret;
+
+    return p9_attach(dev);
+}
+
 static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
 {
+    struct dev_9pfs *dev = data;
+
+    wake_up(&dev->waitq);
 }
 
 static int open_9pfs(struct mount_point *mnt, const char *pathname, int flags,
@@ -87,6 +549,16 @@ void *init_9pfront(unsigned int id, const char *mnt)
     memset(dev, 0, sizeof(*dev));
     snprintf(dev->nodename, sizeof(dev->nodename), "device/9pfs/%u", id);
     dev->id = id;
+    init_waitqueue_head(&dev->waitq);
+    init_SEMAPHORE(&dev->ring_out_sem, 1);
+    init_SEMAPHORE(&dev->ring_in_sem, 1);
+
+    for ( i = 0; i < N_REQS; i++ )
+    {
+        dev->req[i].id = i;
+        dev->req[i].next_free = i + 1;
+    }
+    dev->free_reqs = 0;
 
     msg = xenbus_read_unsigned(XBT_NIL, dev->nodename, "backend-id", &dev->dom);
     if ( msg )
@@ -205,6 +677,12 @@ void *init_9pfront(unsigned int id, const char *mnt)
 
     unmask_evtchn(dev->evtchn);
 
+    if ( connect_9pfs(dev) )
+    {
+        reason = "9pfs connect failed";
+        goto err;
+    }
+
     dev->mnt = mnt;
     if ( mount(dev->mnt, dev, open_9pfs) )
     {
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
                   ` (4 preceding siblings ...)
  2023-02-13  8:44 ` [PATCH v3 5/7] Mini-OS: add 9pfs transport layer Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13 23:11   ` Samuel Thibault
  2023-02-13  8:44 ` [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront Juergen Gross
  2023-02-13 23:13 ` [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Samuel Thibault
  7 siblings, 1 reply; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add the open() and close() support to the 9pfs frontend. This requires
to split the path name and to walk to the desired directory level.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- check path to be canonical
- avoid short read when walking the path
- fix get_fid() (Samuel Thibault)
- return EAGAIN if no free request got (Samuel Thibault)
V3:
- add check for "//" in path_canonical() (Samuel Thibault)
---
 9pfront.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 322 insertions(+), 2 deletions(-)

diff --git a/9pfront.c b/9pfront.c
index 0b8d5461..fb2e5669 100644
--- a/9pfront.c
+++ b/9pfront.c
@@ -6,6 +6,7 @@
 #include <mini-os/os.h>
 #include <mini-os/lib.h>
 #include <mini-os/events.h>
+#include <mini-os/fcntl.h>
 #include <mini-os/gnttab.h>
 #include <mini-os/semaphore.h>
 #include <mini-os/wait.h>
@@ -52,13 +53,32 @@ struct dev_9pfs {
     struct wait_queue_head waitq;
     struct semaphore ring_out_sem;
     struct semaphore ring_in_sem;
+
+    unsigned long long fid_mask;              /* Bit mask for free fids. */
+};
+
+struct file_9pfs {
+    uint32_t fid;
+    struct dev_9pfs *dev;
+    bool append;
 };
 
 #define DEFAULT_9PFS_RING_ORDER  4
 
+/* P9 protocol commands (response is either cmd+1 or P9_CMD_ERROR). */
 #define P9_CMD_VERSION    100
 #define P9_CMD_ATTACH     104
 #define P9_CMD_ERROR      107
+#define P9_CMD_WALK       110
+#define P9_CMD_OPEN       112
+#define P9_CMD_CREATE     114
+#define P9_CMD_CLUNK      120
+
+/* P9 protocol open flags. */
+#define P9_OREAD            0   /* read */
+#define P9_OWRITE           1   /* write */
+#define P9_ORDWR            2   /* read and write */
+#define P9_OTRUNC          16   /* or'ed in, truncate file first */
 
 #define P9_QID_SIZE    13
 
@@ -69,10 +89,27 @@ struct p9_header {
 } __attribute__((packed));
 
 #define P9_VERSION        "9P2000.u"
-#define P9_ROOT_FID       ~0
+#define P9_ROOT_FID       0
 
 static unsigned int ftype_9pfs;
 
+static unsigned int get_fid(struct dev_9pfs *dev)
+{
+    unsigned int fid;
+
+    fid = ffs(dev->fid_mask);
+    if ( fid )
+        dev->fid_mask &= ~(1ULL << (fid - 1));
+
+     return fid;
+}
+
+static void put_fid(struct dev_9pfs *dev, unsigned int fid)
+{
+    if ( fid )
+        dev->fid_mask |= 1ULL << (fid - 1);
+}
+
 static struct req *get_free_req(struct dev_9pfs *dev)
 {
     struct req *req;
@@ -140,6 +177,9 @@ static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int len)
  * send_9p() and rcv_9p() are using a special format string for specifying
  * the kind of data sent/expected. Each data item is represented by a single
  * character:
+ * b: 1 byte unsigned integer (uint8_t)
+ *    Only valid for sending.
+ * u: 2 byte unsigned integer (uint16_t)
  * U: 4 byte unsigned integer (uint32_t)
  * S: String (2 byte length + <length> characters)
  *    in the rcv_9p() case the data for string is allocated (length omitted,
@@ -153,7 +193,9 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
     va_list ap, aq;
     const char *f;
     uint32_t intval;
+    uint16_t shortval;
     uint16_t len;
+    uint8_t byte;
     char *strval;
 
     hdr.size = sizeof(hdr);
@@ -167,6 +209,14 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
     {
         switch ( *f )
         {
+        case 'b':
+            hdr.size += 1;
+            byte = va_arg(aq, unsigned int);
+            break;
+        case 'u':
+            hdr.size += 2;
+            shortval = va_arg(aq, unsigned int);
+            break;
         case 'U':
             hdr.size += 4;
             intval = va_arg(aq, unsigned int);
@@ -196,6 +246,14 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
     {
         switch ( *f )
         {
+        case 'b':
+            byte = va_arg(ap, unsigned int);
+            copy_to_ring(dev, &byte, sizeof(byte));
+            break;
+        case 'u':
+            shortval = va_arg(ap, unsigned int);
+            copy_to_ring(dev, &shortval, sizeof(shortval));
+            break;
         case 'U':
             intval = va_arg(ap, unsigned int);
             copy_to_ring(dev, &intval, sizeof(intval));
@@ -288,6 +346,7 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
     char *str;
     uint16_t len;
     uint32_t err;
+    uint16_t *shortval;
     uint32_t *val;
     char **strval;
     uint8_t *qval;
@@ -345,6 +404,10 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
     {
         switch ( *f )
         {
+        case 'u':
+            shortval = va_arg(ap, uint16_t *);
+            copy_bufs(&buf1, &buf2, &len1, &len2, shortval, sizeof(*shortval));
+            break;
         case 'U':
             val = va_arg(ap, uint32_t *);
             copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
@@ -486,6 +549,170 @@ static int p9_attach(struct dev_9pfs *dev)
     return ret;
 }
 
+static int p9_clunk(struct dev_9pfs *dev, uint32_t fid)
+{
+    struct req *req = get_free_req(dev);
+    int ret;
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_CLUNK;
+    send_9p(dev, req, "U", fid);
+    rcv_9p(dev, req, "");
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int p9_walk(struct dev_9pfs *dev, uint32_t fid, uint32_t newfid,
+                   char *name)
+{
+    struct req *req = get_free_req(dev);
+    int ret;
+    uint16_t nqid;
+    uint8_t qid[P9_QID_SIZE];
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_WALK;
+    if ( name[0] )
+    {
+        send_9p(dev, req, "UUuS", fid, newfid, 1, name);
+        rcv_9p(dev, req, "uQ", &nqid, qid);
+    }
+    else
+    {
+        send_9p(dev, req, "UUu", fid, newfid, 0);
+        rcv_9p(dev, req, "u", &nqid);
+    }
+
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int p9_open(struct dev_9pfs *dev, uint32_t fid, uint8_t omode)
+{
+    struct req *req = get_free_req(dev);
+    int ret;
+    uint8_t qid[P9_QID_SIZE];
+    uint32_t iounit;
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_OPEN;
+    send_9p(dev, req, "Ub", fid, omode);
+    rcv_9p(dev, req, "QU", qid, &iounit);
+
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int p9_create(struct dev_9pfs *dev, uint32_t fid, char *path,
+                     uint32_t mode, uint8_t omode)
+{
+    struct req *req = get_free_req(dev);
+    int ret;
+    uint8_t qid[P9_QID_SIZE];
+    uint32_t iounit;
+
+    if ( !req )
+        return EAGAIN;
+
+    req->cmd = P9_CMD_CREATE;
+    send_9p(dev, req, "USUbS", fid, path, mode, omode, "");
+    rcv_9p(dev, req, "QU", qid, &iounit);
+
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+/*
+ * Walk from root <steps> levels with the levels listed in <*paths> as a
+ * sequence of names. Returns the number of steps not having been able to
+ * walk, with <*paths> pointing at the name of the failing walk step.
+ * <fid> will be associated with the last successful walk step. Note that
+ * the first step should always succeed, as it is an empty walk in order
+ * to start at the root (needed for creating new files in root).
+ */
+static unsigned int walk_9pfs(struct dev_9pfs *dev, uint32_t fid,
+                              unsigned int steps, char **paths)
+{
+    uint32_t curr_fid = P9_ROOT_FID;
+    int ret;
+
+    while ( steps-- )
+    {
+        ret = p9_walk(dev, curr_fid, fid, *paths);
+        if ( ret )
+            return steps + 1;
+        curr_fid = fid;
+        *paths += strlen(*paths) + 1;
+    }
+
+    return 0;
+}
+
+static unsigned int split_path(const char *pathname, char **split_ptr)
+{
+    unsigned int parts = 1;
+    char *p;
+
+    *split_ptr = strdup(pathname);
+
+    for ( p = strchr(*split_ptr, '/'); p; p = strchr(p + 1, '/') )
+    {
+        *p = 0;
+        parts++;
+    }
+
+    return parts;
+}
+
+static bool path_canonical(const char *pathname)
+{
+    unsigned int len = strlen(pathname);
+    const char *c;
+
+    /* Empty path is allowed. */
+    if ( !len )
+        return true;
+
+    /* No trailing '/'. */
+    if ( pathname[len - 1] == '/' )
+        return false;
+
+    /* No self or parent references. */
+    c = pathname;
+    while ( (c = strstr(c, "/.")) != NULL )
+    {
+        if ( c[2] == '.' )
+            c++;
+        if ( c[2] == 0 || c[2] == '/' )
+            return false;
+        c += 2;
+    }
+
+    /* No "//". */
+    if ( strstr(pathname, "//") )
+        return false;
+
+    return true;
+}
+
 static int connect_9pfs(struct dev_9pfs *dev)
 {
     int ret;
@@ -504,10 +731,101 @@ static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
     wake_up(&dev->waitq);
 }
 
+static int close_9pfs(struct file *file)
+{
+    struct file_9pfs *f9pfs = file->filedata;
+
+    if ( f9pfs->fid != P9_ROOT_FID )
+    {
+        p9_clunk(f9pfs->dev, f9pfs->fid);
+        put_fid(f9pfs->dev, f9pfs->fid);
+    }
+
+    free(f9pfs);
+
+    return 0;
+}
+
 static int open_9pfs(struct mount_point *mnt, const char *pathname, int flags,
                      mode_t mode)
 {
-    errno = ENOSYS;
+    int fd;
+    char *path = NULL;
+    char *p;
+    struct file *file;
+    struct file_9pfs *f9pfs;
+    uint16_t nwalk;
+    uint8_t omode;
+    int ret;
+
+    if ( !path_canonical(pathname) )
+        return EINVAL;
+
+    f9pfs = calloc(1, sizeof(*f9pfs));
+    f9pfs->dev = mnt->dev;
+    f9pfs->fid = P9_ROOT_FID;
+
+    fd = alloc_fd(ftype_9pfs);
+    file = get_file_from_fd(fd);
+    file->filedata = f9pfs;
+
+    switch ( flags & O_ACCMODE )
+    {
+    case O_RDONLY:
+        omode = P9_OREAD;
+        break;
+    case O_WRONLY:
+        omode = P9_OWRITE;
+        break;
+    case O_RDWR:
+        omode = P9_ORDWR;
+        break;
+    default:
+        ret = EINVAL;
+        goto err;
+    }
+
+    if ( flags & O_TRUNC )
+        omode |= P9_OTRUNC;
+    f9pfs->append = flags & O_APPEND;
+
+    nwalk = split_path(pathname, &path);
+
+    f9pfs->fid = get_fid(mnt->dev);
+    if ( !f9pfs->fid )
+    {
+        ret = ENFILE;
+        goto err;
+    }
+    p = path;
+    nwalk = walk_9pfs(mnt->dev, f9pfs->fid, nwalk, &p);
+    if ( nwalk )
+    {
+        if ( nwalk > 1 || !(flags & O_CREAT) )
+        {
+            ret = ENOENT;
+            goto err;
+        }
+
+        ret = p9_create(mnt->dev, f9pfs->fid, p, mode, omode);
+        if ( ret )
+            goto err;
+        goto out;
+    }
+
+    ret = p9_open(mnt->dev, f9pfs->fid, omode);
+    if ( ret )
+        goto err;
+
+ out:
+    free(path);
+
+    return fd;
+
+ err:
+    free(path);
+    close(fd);
+    errno = ret;
 
     return -1;
 }
@@ -552,6 +870,7 @@ void *init_9pfront(unsigned int id, const char *mnt)
     init_waitqueue_head(&dev->waitq);
     init_SEMAPHORE(&dev->ring_out_sem, 1);
     init_SEMAPHORE(&dev->ring_in_sem, 1);
+    dev->fid_mask = ~0ULL;
 
     for ( i = 0; i < N_REQS; i++ )
     {
@@ -753,6 +1072,7 @@ void shutdown_9pfront(void *dev)
 
 static const struct file_ops ops_9pfs = {
     .name = "9pfs",
+    .close = close_9pfs,
 };
 
 __attribute__((constructor))
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
                   ` (5 preceding siblings ...)
  2023-02-13  8:44 ` [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend Juergen Gross
@ 2023-02-13  8:44 ` Juergen Gross
  2023-02-13 23:13   ` Samuel Thibault
  2023-02-13 23:13 ` [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Samuel Thibault
  7 siblings, 1 reply; 11+ messages in thread
From: Juergen Gross @ 2023-02-13  8:44 UTC (permalink / raw)
  To: minios-devel, xen-devel; +Cc: samuel.thibault, wl, Juergen Gross

Add support to read from and write to a file handled by 9pfsfront.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- add check for max message size
- return EAGAIN in case no free request got (Samuel Thibault)
- loop until all data read/written (Samuel Thibault)
V3:
- use an exact limit for read/write (Samuel Thibault)
- log read(write errors (Samuel Thibault)
---
 9pfront.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 216 insertions(+)

diff --git a/9pfront.c b/9pfront.c
index fb2e5669..5da8a365 100644
--- a/9pfront.c
+++ b/9pfront.c
@@ -72,7 +72,10 @@ struct file_9pfs {
 #define P9_CMD_WALK       110
 #define P9_CMD_OPEN       112
 #define P9_CMD_CREATE     114
+#define P9_CMD_READ       116
+#define P9_CMD_WRITE      118
 #define P9_CMD_CLUNK      120
+#define P9_CMD_STAT       124
 
 /* P9 protocol open flags. */
 #define P9_OREAD            0   /* read */
@@ -88,11 +91,39 @@ struct p9_header {
     uint16_t tag;
 } __attribute__((packed));
 
+struct p9_stat {
+    uint16_t size;
+    uint16_t type;
+    uint32_t dev;
+    uint8_t qid[P9_QID_SIZE];
+    uint32_t mode;
+    uint32_t atime;
+    uint32_t mtime;
+    uint64_t length;
+    char *name;
+    char *uid;
+    char *gid;
+    char *muid;
+    char *extension;
+    uint32_t n_uid;
+    uint32_t n_gid;
+    uint32_t n_muid;
+};
+
 #define P9_VERSION        "9P2000.u"
 #define P9_ROOT_FID       0
 
 static unsigned int ftype_9pfs;
 
+static void free_stat(struct p9_stat *stat)
+{
+    free(stat->name);
+    free(stat->uid);
+    free(stat->gid);
+    free(stat->muid);
+    free(stat->extension);
+}
+
 static unsigned int get_fid(struct dev_9pfs *dev)
 {
     unsigned int fid;
@@ -181,9 +212,12 @@ static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int len)
  *    Only valid for sending.
  * u: 2 byte unsigned integer (uint16_t)
  * U: 4 byte unsigned integer (uint32_t)
+ * L: 8 byte unsigned integer (uint64_t)
  * S: String (2 byte length + <length> characters)
  *    in the rcv_9p() case the data for string is allocated (length omitted,
  *    string terminated by a NUL character)
+ * D: Binary data (4 byte length + <length> bytes of data), requires a length
+ *    and a buffer pointer parameter.
  * Q: A 13 byte "qid", consisting of 1 byte file type, 4 byte file version
  *    and 8 bytes unique file id. Only valid for receiving.
  */
@@ -192,10 +226,12 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
     struct p9_header hdr;
     va_list ap, aq;
     const char *f;
+    uint64_t longval;
     uint32_t intval;
     uint16_t shortval;
     uint16_t len;
     uint8_t byte;
+    uint8_t *data;
     char *strval;
 
     hdr.size = sizeof(hdr);
@@ -221,11 +257,21 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
             hdr.size += 4;
             intval = va_arg(aq, unsigned int);
             break;
+        case 'L':
+            hdr.size += 8;
+            longval = va_arg(aq, uint64_t);
+            break;
         case 'S':
             hdr.size += 2;
             strval = va_arg(aq, char *);
             hdr.size += strlen(strval);
             break;
+        case 'D':
+            hdr.size += 4;
+            intval = va_arg(aq, unsigned int);
+            hdr.size += intval;
+            data = va_arg(aq, uint8_t *);
+            break;
         default:
             printk("send_9p: unknown format character %c\n", *f);
             break;
@@ -258,12 +304,22 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
             intval = va_arg(ap, unsigned int);
             copy_to_ring(dev, &intval, sizeof(intval));
             break;
+        case 'L':
+            longval = va_arg(ap, uint64_t);
+            copy_to_ring(dev, &longval, sizeof(longval));
+            break;
         case 'S':
             strval = va_arg(ap, char *);
             len = strlen(strval);
             copy_to_ring(dev, &len, sizeof(len));
             copy_to_ring(dev, strval, len);
             break;
+        case 'D':
+            intval = va_arg(ap, unsigned int);
+            copy_to_ring(dev, &intval, sizeof(intval));
+            data = va_arg(ap, uint8_t *);
+            copy_to_ring(dev, data, intval);
+            break;
         }
     }
 
@@ -348,6 +404,8 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
     uint32_t err;
     uint16_t *shortval;
     uint32_t *val;
+    uint64_t *longval;
+    uint8_t *data;
     char **strval;
     uint8_t *qval;
 
@@ -412,6 +470,10 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
             val = va_arg(ap, uint32_t *);
             copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
             break;
+        case 'L':
+            longval = va_arg(ap, uint64_t *);
+            copy_bufs(&buf1, &buf2, &len1, &len2, longval, sizeof(*longval));
+            break;
         case 'S':
             strval = va_arg(ap, char **);
             copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len));
@@ -419,6 +481,12 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
             copy_bufs(&buf1, &buf2, &len1, &len2, *strval, len);
             (*strval)[len] = 0;
             break;
+        case 'D':
+            val = va_arg(ap, uint32_t *);
+            data = va_arg(ap, uint8_t *);
+            copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
+            copy_bufs(&buf1, &buf2, &len1, &len2, data, *val);
+            break;
         case 'Q':
             qval = va_arg(ap, uint8_t *);
             copy_bufs(&buf1, &buf2, &len1, &len2, qval, P9_QID_SIZE);
@@ -640,6 +708,115 @@ static int p9_create(struct dev_9pfs *dev, uint32_t fid, char *path,
     return ret;
 }
 
+static int p9_stat(struct dev_9pfs *dev, uint32_t fid, struct p9_stat *stat)
+{
+    struct req *req = get_free_req(dev);
+    int ret;
+
+    if ( !req )
+        return EAGAIN;
+
+    memset(stat, 0, sizeof(*stat));
+    req->cmd = P9_CMD_STAT;
+    send_9p(dev, req, "U", fid);
+    rcv_9p(dev, req, "uuUQUUULSSSSSUUU", &stat->size, &stat->type, &stat->dev,
+           stat->qid, &stat->mode, &stat->atime, &stat->mtime, &stat->length,
+           &stat->name, &stat->uid, &stat->gid, &stat->muid, &stat->extension,
+           &stat->n_uid, &stat->n_gid, &stat->n_muid);
+
+    ret = req->result;
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int p9_read(struct dev_9pfs *dev, uint32_t fid, uint64_t offset,
+                   uint8_t *data, uint32_t len)
+{
+    struct req *req = get_free_req(dev);
+    int ret = 0;
+    uint32_t count, count_max;
+
+    if ( !req )
+    {
+        errno = EAGAIN;
+        return -1;
+    }
+    req->cmd = P9_CMD_READ;
+    count_max = dev->msize_max - (sizeof(struct p9_header) + sizeof(uint32_t));
+
+    while ( len )
+    {
+        count = len;
+        if ( count > count_max )
+            count = count_max;
+
+        send_9p(dev, req, "ULU", fid, offset, count);
+        rcv_9p(dev, req, "D", &count, data);
+
+        if ( !count )
+            break;
+        if ( req->result )
+        {
+            ret = -1;
+            errno = EIO;
+            printk("9pfs: read got error %d\n", req->result);
+            break;
+        }
+        ret += count;
+        offset += count;
+        data += count;
+        len -= count;
+    }
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
+static int p9_write(struct dev_9pfs *dev, uint32_t fid, uint64_t offset,
+                    const uint8_t *data, uint32_t len)
+{
+    struct req *req = get_free_req(dev);
+    int ret = 0;
+    uint32_t count, count_max;
+
+    if ( !req )
+    {
+        errno = EAGAIN;
+        return -1;
+    }
+    req->cmd = P9_CMD_WRITE;
+    count_max = dev->msize_max - (sizeof(struct p9_header) + sizeof(uint32_t) +
+                                  sizeof(uint64_t) + sizeof(uint32_t));
+
+    while ( len )
+    {
+        count = len;
+        if ( count > count_max )
+            count = count_max;
+
+        send_9p(dev, req, "ULD", fid, offset, count, data);
+        rcv_9p(dev, req, "U", &count);
+        if ( req->result )
+        {
+            ret = -1;
+            errno = EIO;
+            printk("9pfs: write got error %d\n", req->result);
+            break;
+        }
+        ret += count;
+        offset += count;
+        data += count;
+        len -= count;
+    }
+
+    put_free_req(dev, req);
+
+    return ret;
+}
+
 /*
  * Walk from root <steps> levels with the levels listed in <*paths> as a
  * sequence of names. Returns the number of steps not having been able to
@@ -731,6 +908,43 @@ static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
     wake_up(&dev->waitq);
 }
 
+static int read_9pfs(struct file *file, void *buf, size_t nbytes)
+{
+    struct file_9pfs *f9pfs = file->filedata;
+    int ret;
+
+    ret = p9_read(f9pfs->dev, f9pfs->fid, file->offset, buf, nbytes);
+    if ( ret >= 0 )
+        file->offset += ret;
+
+    return ret;
+}
+
+static int write_9pfs(struct file *file, const void *buf, size_t nbytes)
+{
+    struct file_9pfs *f9pfs = file->filedata;
+    struct p9_stat stat;
+    int ret;
+
+    if ( f9pfs->append )
+    {
+        ret = p9_stat(f9pfs->dev, f9pfs->fid, &stat);
+        free_stat(&stat);
+        if ( ret )
+        {
+            errno = EIO;
+            return -1;
+        }
+        file->offset = stat.length;
+    }
+
+    ret = p9_write(f9pfs->dev, f9pfs->fid, file->offset, buf, nbytes);
+    if ( ret >= 0 )
+        file->offset += ret;
+
+    return ret;
+}
+
 static int close_9pfs(struct file *file)
 {
     struct file_9pfs *f9pfs = file->filedata;
@@ -1072,6 +1286,8 @@ void shutdown_9pfront(void *dev)
 
 static const struct file_ops ops_9pfs = {
     .name = "9pfs",
+    .read = read_9pfs,
+    .write = write_9pfs,
     .close = close_9pfs,
 };
 
-- 
2.35.3



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend
  2023-02-13  8:44 ` [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend Juergen Gross
@ 2023-02-13 23:11   ` Samuel Thibault
  0 siblings, 0 replies; 11+ messages in thread
From: Samuel Thibault @ 2023-02-13 23:11 UTC (permalink / raw)
  To: Juergen Gross; +Cc: minios-devel, xen-devel, wl

Juergen Gross, le lun. 13 févr. 2023 09:44:11 +0100, a ecrit:
> Add the open() and close() support to the 9pfs frontend. This requires
> to split the path name and to walk to the desired directory level.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>

Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>

> ---
> V2:
> - check path to be canonical
> - avoid short read when walking the path
> - fix get_fid() (Samuel Thibault)
> - return EAGAIN if no free request got (Samuel Thibault)
> V3:
> - add check for "//" in path_canonical() (Samuel Thibault)
> ---
>  9pfront.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 322 insertions(+), 2 deletions(-)
> 
> diff --git a/9pfront.c b/9pfront.c
> index 0b8d5461..fb2e5669 100644
> --- a/9pfront.c
> +++ b/9pfront.c
> @@ -6,6 +6,7 @@
>  #include <mini-os/os.h>
>  #include <mini-os/lib.h>
>  #include <mini-os/events.h>
> +#include <mini-os/fcntl.h>
>  #include <mini-os/gnttab.h>
>  #include <mini-os/semaphore.h>
>  #include <mini-os/wait.h>
> @@ -52,13 +53,32 @@ struct dev_9pfs {
>      struct wait_queue_head waitq;
>      struct semaphore ring_out_sem;
>      struct semaphore ring_in_sem;
> +
> +    unsigned long long fid_mask;              /* Bit mask for free fids. */
> +};
> +
> +struct file_9pfs {
> +    uint32_t fid;
> +    struct dev_9pfs *dev;
> +    bool append;
>  };
>  
>  #define DEFAULT_9PFS_RING_ORDER  4
>  
> +/* P9 protocol commands (response is either cmd+1 or P9_CMD_ERROR). */
>  #define P9_CMD_VERSION    100
>  #define P9_CMD_ATTACH     104
>  #define P9_CMD_ERROR      107
> +#define P9_CMD_WALK       110
> +#define P9_CMD_OPEN       112
> +#define P9_CMD_CREATE     114
> +#define P9_CMD_CLUNK      120
> +
> +/* P9 protocol open flags. */
> +#define P9_OREAD            0   /* read */
> +#define P9_OWRITE           1   /* write */
> +#define P9_ORDWR            2   /* read and write */
> +#define P9_OTRUNC          16   /* or'ed in, truncate file first */
>  
>  #define P9_QID_SIZE    13
>  
> @@ -69,10 +89,27 @@ struct p9_header {
>  } __attribute__((packed));
>  
>  #define P9_VERSION        "9P2000.u"
> -#define P9_ROOT_FID       ~0
> +#define P9_ROOT_FID       0
>  
>  static unsigned int ftype_9pfs;
>  
> +static unsigned int get_fid(struct dev_9pfs *dev)
> +{
> +    unsigned int fid;
> +
> +    fid = ffs(dev->fid_mask);
> +    if ( fid )
> +        dev->fid_mask &= ~(1ULL << (fid - 1));
> +
> +     return fid;
> +}
> +
> +static void put_fid(struct dev_9pfs *dev, unsigned int fid)
> +{
> +    if ( fid )
> +        dev->fid_mask |= 1ULL << (fid - 1);
> +}
> +
>  static struct req *get_free_req(struct dev_9pfs *dev)
>  {
>      struct req *req;
> @@ -140,6 +177,9 @@ static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int len)
>   * send_9p() and rcv_9p() are using a special format string for specifying
>   * the kind of data sent/expected. Each data item is represented by a single
>   * character:
> + * b: 1 byte unsigned integer (uint8_t)
> + *    Only valid for sending.
> + * u: 2 byte unsigned integer (uint16_t)
>   * U: 4 byte unsigned integer (uint32_t)
>   * S: String (2 byte length + <length> characters)
>   *    in the rcv_9p() case the data for string is allocated (length omitted,
> @@ -153,7 +193,9 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>      va_list ap, aq;
>      const char *f;
>      uint32_t intval;
> +    uint16_t shortval;
>      uint16_t len;
> +    uint8_t byte;
>      char *strval;
>  
>      hdr.size = sizeof(hdr);
> @@ -167,6 +209,14 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>      {
>          switch ( *f )
>          {
> +        case 'b':
> +            hdr.size += 1;
> +            byte = va_arg(aq, unsigned int);
> +            break;
> +        case 'u':
> +            hdr.size += 2;
> +            shortval = va_arg(aq, unsigned int);
> +            break;
>          case 'U':
>              hdr.size += 4;
>              intval = va_arg(aq, unsigned int);
> @@ -196,6 +246,14 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>      {
>          switch ( *f )
>          {
> +        case 'b':
> +            byte = va_arg(ap, unsigned int);
> +            copy_to_ring(dev, &byte, sizeof(byte));
> +            break;
> +        case 'u':
> +            shortval = va_arg(ap, unsigned int);
> +            copy_to_ring(dev, &shortval, sizeof(shortval));
> +            break;
>          case 'U':
>              intval = va_arg(ap, unsigned int);
>              copy_to_ring(dev, &intval, sizeof(intval));
> @@ -288,6 +346,7 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
>      char *str;
>      uint16_t len;
>      uint32_t err;
> +    uint16_t *shortval;
>      uint32_t *val;
>      char **strval;
>      uint8_t *qval;
> @@ -345,6 +404,10 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
>      {
>          switch ( *f )
>          {
> +        case 'u':
> +            shortval = va_arg(ap, uint16_t *);
> +            copy_bufs(&buf1, &buf2, &len1, &len2, shortval, sizeof(*shortval));
> +            break;
>          case 'U':
>              val = va_arg(ap, uint32_t *);
>              copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
> @@ -486,6 +549,170 @@ static int p9_attach(struct dev_9pfs *dev)
>      return ret;
>  }
>  
> +static int p9_clunk(struct dev_9pfs *dev, uint32_t fid)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_CLUNK;
> +    send_9p(dev, req, "U", fid);
> +    rcv_9p(dev, req, "");
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_walk(struct dev_9pfs *dev, uint32_t fid, uint32_t newfid,
> +                   char *name)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint16_t nqid;
> +    uint8_t qid[P9_QID_SIZE];
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_WALK;
> +    if ( name[0] )
> +    {
> +        send_9p(dev, req, "UUuS", fid, newfid, 1, name);
> +        rcv_9p(dev, req, "uQ", &nqid, qid);
> +    }
> +    else
> +    {
> +        send_9p(dev, req, "UUu", fid, newfid, 0);
> +        rcv_9p(dev, req, "u", &nqid);
> +    }
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_open(struct dev_9pfs *dev, uint32_t fid, uint8_t omode)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint8_t qid[P9_QID_SIZE];
> +    uint32_t iounit;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_OPEN;
> +    send_9p(dev, req, "Ub", fid, omode);
> +    rcv_9p(dev, req, "QU", qid, &iounit);
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_create(struct dev_9pfs *dev, uint32_t fid, char *path,
> +                     uint32_t mode, uint8_t omode)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint8_t qid[P9_QID_SIZE];
> +    uint32_t iounit;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_CREATE;
> +    send_9p(dev, req, "USUbS", fid, path, mode, omode, "");
> +    rcv_9p(dev, req, "QU", qid, &iounit);
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +/*
> + * Walk from root <steps> levels with the levels listed in <*paths> as a
> + * sequence of names. Returns the number of steps not having been able to
> + * walk, with <*paths> pointing at the name of the failing walk step.
> + * <fid> will be associated with the last successful walk step. Note that
> + * the first step should always succeed, as it is an empty walk in order
> + * to start at the root (needed for creating new files in root).
> + */
> +static unsigned int walk_9pfs(struct dev_9pfs *dev, uint32_t fid,
> +                              unsigned int steps, char **paths)
> +{
> +    uint32_t curr_fid = P9_ROOT_FID;
> +    int ret;
> +
> +    while ( steps-- )
> +    {
> +        ret = p9_walk(dev, curr_fid, fid, *paths);
> +        if ( ret )
> +            return steps + 1;
> +        curr_fid = fid;
> +        *paths += strlen(*paths) + 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static unsigned int split_path(const char *pathname, char **split_ptr)
> +{
> +    unsigned int parts = 1;
> +    char *p;
> +
> +    *split_ptr = strdup(pathname);
> +
> +    for ( p = strchr(*split_ptr, '/'); p; p = strchr(p + 1, '/') )
> +    {
> +        *p = 0;
> +        parts++;
> +    }
> +
> +    return parts;
> +}
> +
> +static bool path_canonical(const char *pathname)
> +{
> +    unsigned int len = strlen(pathname);
> +    const char *c;
> +
> +    /* Empty path is allowed. */
> +    if ( !len )
> +        return true;
> +
> +    /* No trailing '/'. */
> +    if ( pathname[len - 1] == '/' )
> +        return false;
> +
> +    /* No self or parent references. */
> +    c = pathname;
> +    while ( (c = strstr(c, "/.")) != NULL )
> +    {
> +        if ( c[2] == '.' )
> +            c++;
> +        if ( c[2] == 0 || c[2] == '/' )
> +            return false;
> +        c += 2;
> +    }
> +
> +    /* No "//". */
> +    if ( strstr(pathname, "//") )
> +        return false;
> +
> +    return true;
> +}
> +
>  static int connect_9pfs(struct dev_9pfs *dev)
>  {
>      int ret;
> @@ -504,10 +731,101 @@ static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
>      wake_up(&dev->waitq);
>  }
>  
> +static int close_9pfs(struct file *file)
> +{
> +    struct file_9pfs *f9pfs = file->filedata;
> +
> +    if ( f9pfs->fid != P9_ROOT_FID )
> +    {
> +        p9_clunk(f9pfs->dev, f9pfs->fid);
> +        put_fid(f9pfs->dev, f9pfs->fid);
> +    }
> +
> +    free(f9pfs);
> +
> +    return 0;
> +}
> +
>  static int open_9pfs(struct mount_point *mnt, const char *pathname, int flags,
>                       mode_t mode)
>  {
> -    errno = ENOSYS;
> +    int fd;
> +    char *path = NULL;
> +    char *p;
> +    struct file *file;
> +    struct file_9pfs *f9pfs;
> +    uint16_t nwalk;
> +    uint8_t omode;
> +    int ret;
> +
> +    if ( !path_canonical(pathname) )
> +        return EINVAL;
> +
> +    f9pfs = calloc(1, sizeof(*f9pfs));
> +    f9pfs->dev = mnt->dev;
> +    f9pfs->fid = P9_ROOT_FID;
> +
> +    fd = alloc_fd(ftype_9pfs);
> +    file = get_file_from_fd(fd);
> +    file->filedata = f9pfs;
> +
> +    switch ( flags & O_ACCMODE )
> +    {
> +    case O_RDONLY:
> +        omode = P9_OREAD;
> +        break;
> +    case O_WRONLY:
> +        omode = P9_OWRITE;
> +        break;
> +    case O_RDWR:
> +        omode = P9_ORDWR;
> +        break;
> +    default:
> +        ret = EINVAL;
> +        goto err;
> +    }
> +
> +    if ( flags & O_TRUNC )
> +        omode |= P9_OTRUNC;
> +    f9pfs->append = flags & O_APPEND;
> +
> +    nwalk = split_path(pathname, &path);
> +
> +    f9pfs->fid = get_fid(mnt->dev);
> +    if ( !f9pfs->fid )
> +    {
> +        ret = ENFILE;
> +        goto err;
> +    }
> +    p = path;
> +    nwalk = walk_9pfs(mnt->dev, f9pfs->fid, nwalk, &p);
> +    if ( nwalk )
> +    {
> +        if ( nwalk > 1 || !(flags & O_CREAT) )
> +        {
> +            ret = ENOENT;
> +            goto err;
> +        }
> +
> +        ret = p9_create(mnt->dev, f9pfs->fid, p, mode, omode);
> +        if ( ret )
> +            goto err;
> +        goto out;
> +    }
> +
> +    ret = p9_open(mnt->dev, f9pfs->fid, omode);
> +    if ( ret )
> +        goto err;
> +
> + out:
> +    free(path);
> +
> +    return fd;
> +
> + err:
> +    free(path);
> +    close(fd);
> +    errno = ret;
>  
>      return -1;
>  }
> @@ -552,6 +870,7 @@ void *init_9pfront(unsigned int id, const char *mnt)
>      init_waitqueue_head(&dev->waitq);
>      init_SEMAPHORE(&dev->ring_out_sem, 1);
>      init_SEMAPHORE(&dev->ring_in_sem, 1);
> +    dev->fid_mask = ~0ULL;
>  
>      for ( i = 0; i < N_REQS; i++ )
>      {
> @@ -753,6 +1072,7 @@ void shutdown_9pfront(void *dev)
>  
>  static const struct file_ops ops_9pfs = {
>      .name = "9pfs",
> +    .close = close_9pfs,
>  };
>  
>  __attribute__((constructor))
> -- 
> 2.35.3
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront
  2023-02-13  8:44 ` [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront Juergen Gross
@ 2023-02-13 23:13   ` Samuel Thibault
  0 siblings, 0 replies; 11+ messages in thread
From: Samuel Thibault @ 2023-02-13 23:13 UTC (permalink / raw)
  To: Juergen Gross; +Cc: minios-devel, xen-devel, wl

Juergen Gross, le lun. 13 févr. 2023 09:44:12 +0100, a ecrit:
> Add support to read from and write to a file handled by 9pfsfront.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>

Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>

> ---
> V2:
> - add check for max message size
> - return EAGAIN in case no free request got (Samuel Thibault)
> - loop until all data read/written (Samuel Thibault)
> V3:
> - use an exact limit for read/write (Samuel Thibault)
> - log read(write errors (Samuel Thibault)
> ---
>  9pfront.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 216 insertions(+)
> 
> diff --git a/9pfront.c b/9pfront.c
> index fb2e5669..5da8a365 100644
> --- a/9pfront.c
> +++ b/9pfront.c
> @@ -72,7 +72,10 @@ struct file_9pfs {
>  #define P9_CMD_WALK       110
>  #define P9_CMD_OPEN       112
>  #define P9_CMD_CREATE     114
> +#define P9_CMD_READ       116
> +#define P9_CMD_WRITE      118
>  #define P9_CMD_CLUNK      120
> +#define P9_CMD_STAT       124
>  
>  /* P9 protocol open flags. */
>  #define P9_OREAD            0   /* read */
> @@ -88,11 +91,39 @@ struct p9_header {
>      uint16_t tag;
>  } __attribute__((packed));
>  
> +struct p9_stat {
> +    uint16_t size;
> +    uint16_t type;
> +    uint32_t dev;
> +    uint8_t qid[P9_QID_SIZE];
> +    uint32_t mode;
> +    uint32_t atime;
> +    uint32_t mtime;
> +    uint64_t length;
> +    char *name;
> +    char *uid;
> +    char *gid;
> +    char *muid;
> +    char *extension;
> +    uint32_t n_uid;
> +    uint32_t n_gid;
> +    uint32_t n_muid;
> +};
> +
>  #define P9_VERSION        "9P2000.u"
>  #define P9_ROOT_FID       0
>  
>  static unsigned int ftype_9pfs;
>  
> +static void free_stat(struct p9_stat *stat)
> +{
> +    free(stat->name);
> +    free(stat->uid);
> +    free(stat->gid);
> +    free(stat->muid);
> +    free(stat->extension);
> +}
> +
>  static unsigned int get_fid(struct dev_9pfs *dev)
>  {
>      unsigned int fid;
> @@ -181,9 +212,12 @@ static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int len)
>   *    Only valid for sending.
>   * u: 2 byte unsigned integer (uint16_t)
>   * U: 4 byte unsigned integer (uint32_t)
> + * L: 8 byte unsigned integer (uint64_t)
>   * S: String (2 byte length + <length> characters)
>   *    in the rcv_9p() case the data for string is allocated (length omitted,
>   *    string terminated by a NUL character)
> + * D: Binary data (4 byte length + <length> bytes of data), requires a length
> + *    and a buffer pointer parameter.
>   * Q: A 13 byte "qid", consisting of 1 byte file type, 4 byte file version
>   *    and 8 bytes unique file id. Only valid for receiving.
>   */
> @@ -192,10 +226,12 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>      struct p9_header hdr;
>      va_list ap, aq;
>      const char *f;
> +    uint64_t longval;
>      uint32_t intval;
>      uint16_t shortval;
>      uint16_t len;
>      uint8_t byte;
> +    uint8_t *data;
>      char *strval;
>  
>      hdr.size = sizeof(hdr);
> @@ -221,11 +257,21 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>              hdr.size += 4;
>              intval = va_arg(aq, unsigned int);
>              break;
> +        case 'L':
> +            hdr.size += 8;
> +            longval = va_arg(aq, uint64_t);
> +            break;
>          case 'S':
>              hdr.size += 2;
>              strval = va_arg(aq, char *);
>              hdr.size += strlen(strval);
>              break;
> +        case 'D':
> +            hdr.size += 4;
> +            intval = va_arg(aq, unsigned int);
> +            hdr.size += intval;
> +            data = va_arg(aq, uint8_t *);
> +            break;
>          default:
>              printk("send_9p: unknown format character %c\n", *f);
>              break;
> @@ -258,12 +304,22 @@ static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt, ...)
>              intval = va_arg(ap, unsigned int);
>              copy_to_ring(dev, &intval, sizeof(intval));
>              break;
> +        case 'L':
> +            longval = va_arg(ap, uint64_t);
> +            copy_to_ring(dev, &longval, sizeof(longval));
> +            break;
>          case 'S':
>              strval = va_arg(ap, char *);
>              len = strlen(strval);
>              copy_to_ring(dev, &len, sizeof(len));
>              copy_to_ring(dev, strval, len);
>              break;
> +        case 'D':
> +            intval = va_arg(ap, unsigned int);
> +            copy_to_ring(dev, &intval, sizeof(intval));
> +            data = va_arg(ap, uint8_t *);
> +            copy_to_ring(dev, data, intval);
> +            break;
>          }
>      }
>  
> @@ -348,6 +404,8 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
>      uint32_t err;
>      uint16_t *shortval;
>      uint32_t *val;
> +    uint64_t *longval;
> +    uint8_t *data;
>      char **strval;
>      uint8_t *qval;
>  
> @@ -412,6 +470,10 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
>              val = va_arg(ap, uint32_t *);
>              copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
>              break;
> +        case 'L':
> +            longval = va_arg(ap, uint64_t *);
> +            copy_bufs(&buf1, &buf2, &len1, &len2, longval, sizeof(*longval));
> +            break;
>          case 'S':
>              strval = va_arg(ap, char **);
>              copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len));
> @@ -419,6 +481,12 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req,
>              copy_bufs(&buf1, &buf2, &len1, &len2, *strval, len);
>              (*strval)[len] = 0;
>              break;
> +        case 'D':
> +            val = va_arg(ap, uint32_t *);
> +            data = va_arg(ap, uint8_t *);
> +            copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
> +            copy_bufs(&buf1, &buf2, &len1, &len2, data, *val);
> +            break;
>          case 'Q':
>              qval = va_arg(ap, uint8_t *);
>              copy_bufs(&buf1, &buf2, &len1, &len2, qval, P9_QID_SIZE);
> @@ -640,6 +708,115 @@ static int p9_create(struct dev_9pfs *dev, uint32_t fid, char *path,
>      return ret;
>  }
>  
> +static int p9_stat(struct dev_9pfs *dev, uint32_t fid, struct p9_stat *stat)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    memset(stat, 0, sizeof(*stat));
> +    req->cmd = P9_CMD_STAT;
> +    send_9p(dev, req, "U", fid);
> +    rcv_9p(dev, req, "uuUQUUULSSSSSUUU", &stat->size, &stat->type, &stat->dev,
> +           stat->qid, &stat->mode, &stat->atime, &stat->mtime, &stat->length,
> +           &stat->name, &stat->uid, &stat->gid, &stat->muid, &stat->extension,
> +           &stat->n_uid, &stat->n_gid, &stat->n_muid);
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_read(struct dev_9pfs *dev, uint32_t fid, uint64_t offset,
> +                   uint8_t *data, uint32_t len)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret = 0;
> +    uint32_t count, count_max;
> +
> +    if ( !req )
> +    {
> +        errno = EAGAIN;
> +        return -1;
> +    }
> +    req->cmd = P9_CMD_READ;
> +    count_max = dev->msize_max - (sizeof(struct p9_header) + sizeof(uint32_t));
> +
> +    while ( len )
> +    {
> +        count = len;
> +        if ( count > count_max )
> +            count = count_max;
> +
> +        send_9p(dev, req, "ULU", fid, offset, count);
> +        rcv_9p(dev, req, "D", &count, data);
> +
> +        if ( !count )
> +            break;
> +        if ( req->result )
> +        {
> +            ret = -1;
> +            errno = EIO;
> +            printk("9pfs: read got error %d\n", req->result);
> +            break;
> +        }
> +        ret += count;
> +        offset += count;
> +        data += count;
> +        len -= count;
> +    }
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_write(struct dev_9pfs *dev, uint32_t fid, uint64_t offset,
> +                    const uint8_t *data, uint32_t len)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret = 0;
> +    uint32_t count, count_max;
> +
> +    if ( !req )
> +    {
> +        errno = EAGAIN;
> +        return -1;
> +    }
> +    req->cmd = P9_CMD_WRITE;
> +    count_max = dev->msize_max - (sizeof(struct p9_header) + sizeof(uint32_t) +
> +                                  sizeof(uint64_t) + sizeof(uint32_t));
> +
> +    while ( len )
> +    {
> +        count = len;
> +        if ( count > count_max )
> +            count = count_max;
> +
> +        send_9p(dev, req, "ULD", fid, offset, count, data);
> +        rcv_9p(dev, req, "U", &count);
> +        if ( req->result )
> +        {
> +            ret = -1;
> +            errno = EIO;
> +            printk("9pfs: write got error %d\n", req->result);
> +            break;
> +        }
> +        ret += count;
> +        offset += count;
> +        data += count;
> +        len -= count;
> +    }
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
>  /*
>   * Walk from root <steps> levels with the levels listed in <*paths> as a
>   * sequence of names. Returns the number of steps not having been able to
> @@ -731,6 +908,43 @@ static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data)
>      wake_up(&dev->waitq);
>  }
>  
> +static int read_9pfs(struct file *file, void *buf, size_t nbytes)
> +{
> +    struct file_9pfs *f9pfs = file->filedata;
> +    int ret;
> +
> +    ret = p9_read(f9pfs->dev, f9pfs->fid, file->offset, buf, nbytes);
> +    if ( ret >= 0 )
> +        file->offset += ret;
> +
> +    return ret;
> +}
> +
> +static int write_9pfs(struct file *file, const void *buf, size_t nbytes)
> +{
> +    struct file_9pfs *f9pfs = file->filedata;
> +    struct p9_stat stat;
> +    int ret;
> +
> +    if ( f9pfs->append )
> +    {
> +        ret = p9_stat(f9pfs->dev, f9pfs->fid, &stat);
> +        free_stat(&stat);
> +        if ( ret )
> +        {
> +            errno = EIO;
> +            return -1;
> +        }
> +        file->offset = stat.length;
> +    }
> +
> +    ret = p9_write(f9pfs->dev, f9pfs->fid, file->offset, buf, nbytes);
> +    if ( ret >= 0 )
> +        file->offset += ret;
> +
> +    return ret;
> +}
> +
>  static int close_9pfs(struct file *file)
>  {
>      struct file_9pfs *f9pfs = file->filedata;
> @@ -1072,6 +1286,8 @@ void shutdown_9pfront(void *dev)
>  
>  static const struct file_ops ops_9pfs = {
>      .name = "9pfs",
> +    .read = read_9pfs,
> +    .write = write_9pfs,
>      .close = close_9pfs,
>  };
>  
> -- 
> 2.35.3
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v3 0/7] Mini-OS: add minimal 9pfs support
  2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
                   ` (6 preceding siblings ...)
  2023-02-13  8:44 ` [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront Juergen Gross
@ 2023-02-13 23:13 ` Samuel Thibault
  7 siblings, 0 replies; 11+ messages in thread
From: Samuel Thibault @ 2023-02-13 23:13 UTC (permalink / raw)
  To: Juergen Gross; +Cc: minios-devel, xen-devel, wl

Thanks!!

Juergen Gross, le lun. 13 févr. 2023 09:44:05 +0100, a ecrit:
> This series is adding minimal support to use 9pfs in Mini-OS. It is
> adding a PV 9pfs frontend and the ability to open, close, read and
> write files.
> 
> The series has been tested with qemu as 9pfs backend in a PVH Mini-OS
> guest (I've used a slightly modified Xenstore-stubdom for that purpose
> in order to reuse the build runes).
> 
> This series is meant to setup the stage for adding file based logging
> support to Xenstore-stubdom and later to add live update support (being
> able to save the LU data stream in a dom0 file makes this a _lot_
> easier).
> 
> In order to keep Mini-OS's license I have only used the protocol docs
> available on the internet [1] and then verified those with the qemu 9pfs
> backend implementation (especially for supporting the 9P2000.u variant,
> as qemu doesn't support the basic 9P2000 protocol).
> 
> The needed fixed values of the protocol have been taken from [2].
> 
> [1]: http://ericvh.github.io/9p-rfc/rfc9p2000.html
> [2]: https://github.com/0intro/libixp
> 
> Changes in V2:
> - addressed comments by Samuel Thibault
> 
> Changes in V3:
> - addressed comments by Samuel Thibault and Andrew Cooper
> 
> Juergen Gross (7):
>   Mini-OS: xenbus: add support for reading node from directory
>   Mini-OS: add concept of mount points
>   Mini-OS: add support for runtime mounts
>   Mini-OS: add 9pfs frontend
>   Mini-OS: add 9pfs transport layer
>   Mini-OS: add open and close handling to the 9pfs frontend
>   Mini-OS: add read and write support to 9pfsfront
> 
>  9pfront.c                     | 1300 +++++++++++++++++++++++++++++++++
>  Config.mk                     |    1 +
>  Makefile                      |    1 +
>  arch/x86/testbuild/all-no     |    1 +
>  arch/x86/testbuild/all-yes    |    1 +
>  arch/x86/testbuild/newxen-yes |    1 +
>  include/9pfront.h             |    7 +
>  include/lib.h                 |   14 +
>  include/xenbus.h              |    6 +
>  lib/sys.c                     |  123 +++-
>  xenbus.c                      |   40 +-
>  11 files changed, 1473 insertions(+), 22 deletions(-)
>  create mode 100644 9pfront.c
>  create mode 100644 include/9pfront.h
> 
> -- 
> 2.35.3
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2023-02-13 23:13 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-13  8:44 [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Juergen Gross
2023-02-13  8:44 ` [PATCH v3 1/7] Mini-OS: xenbus: add support for reading node from directory Juergen Gross
2023-02-13  8:44 ` [PATCH v3 2/7] Mini-OS: add concept of mount points Juergen Gross
2023-02-13  8:44 ` [PATCH v3 3/7] Mini-OS: add support for runtime mounts Juergen Gross
2023-02-13  8:44 ` [PATCH v3 4/7] Mini-OS: add 9pfs frontend Juergen Gross
2023-02-13  8:44 ` [PATCH v3 5/7] Mini-OS: add 9pfs transport layer Juergen Gross
2023-02-13  8:44 ` [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend Juergen Gross
2023-02-13 23:11   ` Samuel Thibault
2023-02-13  8:44 ` [PATCH v3 7/7] Mini-OS: add read and write support to 9pfsfront Juergen Gross
2023-02-13 23:13   ` Samuel Thibault
2023-02-13 23:13 ` [PATCH v3 0/7] Mini-OS: add minimal 9pfs support Samuel Thibault

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.