[PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten

Kohei Tokunaga posted 10 patches 8 months, 1 week ago
[PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Kohei Tokunaga 8 months, 1 week ago
Emscripten's fiber does not support submitting coroutines to other
threads. So this commit modifies hw/9pfs/coth.h to disable this behavior
when compiled with Emscripten.

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
 fsdev/file-op-9p.h     |  3 +++
 fsdev/meson.build      |  2 +-
 hw/9pfs/9p-util-stub.c | 43 ++++++++++++++++++++++++++++++++++++++++++
 hw/9pfs/9p-util.h      | 18 ++++++++++++++++++
 hw/9pfs/9p.c           |  3 +++
 hw/9pfs/coth.h         | 12 ++++++++++++
 hw/9pfs/meson.build    |  2 ++
 meson.build            |  6 +++---
 8 files changed, 85 insertions(+), 4 deletions(-)
 create mode 100644 hw/9pfs/9p-util-stub.c

diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index 4997677460..b7ca2640ce 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -26,6 +26,9 @@
 # include <sys/param.h>
 # include <sys/mount.h>
 #endif
+#ifdef EMSCRIPTEN
+#include <sys/vfs.h>
+#endif
 
 #define SM_LOCAL_MODE_BITS    0600
 #define SM_LOCAL_DIR_MODE_BITS    0700
diff --git a/fsdev/meson.build b/fsdev/meson.build
index c751d8cb62..c3e92a29d7 100644
--- a/fsdev/meson.build
+++ b/fsdev/meson.build
@@ -5,6 +5,6 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files(
   '9p-marshal.c',
   'qemu-fsdev.c',
 ), if_false: files('qemu-fsdev-dummy.c'))
-if host_os in ['linux', 'darwin']
+if host_os in ['linux', 'darwin', 'emscripten']
   system_ss.add_all(fsdev_ss)
 endif
diff --git a/hw/9pfs/9p-util-stub.c b/hw/9pfs/9p-util-stub.c
new file mode 100644
index 0000000000..57c89902ab
--- /dev/null
+++ b/hw/9pfs/9p-util-stub.c
@@ -0,0 +1,43 @@
+/*
+ * 9p utilities stub functions
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "9p-util.h"
+
+ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
+                             void *value, size_t size)
+{
+    return -1;
+}
+
+ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+                              char *list, size_t size)
+{
+    return -1;
+}
+
+ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+                                const char *name)
+{
+    return -1;
+}
+
+int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
+                         void *value, size_t size, int flags)
+{
+    return -1;
+
+}
+
+int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+{
+    return -1;
+}
+
+ssize_t fgetxattr(int fd, const char *name, void *value, size_t size)
+{
+    return -1;
+}
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 7bc4ec8e85..8c5006fcdc 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -84,6 +84,24 @@ static inline int errno_to_dotl(int err) {
     } else if (err == EOPNOTSUPP) {
         err = 95; /* ==EOPNOTSUPP on Linux */
     }
+#elif defined(EMSCRIPTEN)
+    /*
+     * FIXME: Only most important errnos translated here yet, this should be
+     * extended to as many errnos being translated as possible in future.
+     */
+    if (err == ENAMETOOLONG) {
+        err = 36; /* ==ENAMETOOLONG on Linux */
+    } else if (err == ENOTEMPTY) {
+        err = 39; /* ==ENOTEMPTY on Linux */
+    } else if (err == ELOOP) {
+        err = 40; /* ==ELOOP on Linux */
+    } else if (err == ENODATA) {
+        err = 61; /* ==ENODATA on Linux */
+    } else if (err == ENOTSUP) {
+        err = 95; /* ==EOPNOTSUPP on Linux */
+    } else if (err == EOPNOTSUPP) {
+        err = 95; /* ==EOPNOTSUPP on Linux */
+    }
 #else
 #error Missing errno translation to Linux for this host system
 #endif
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 7cad2bce62..4f45f0edd3 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -4013,6 +4013,9 @@ out_nofid:
  * Linux guests.
  */
 #define P9_XATTR_SIZE_MAX 65536
+#elif defined(EMSCRIPTEN)
+/* No support for xattr */
+#define P9_XATTR_SIZE_MAX 0
 #else
 #error Missing definition for P9_XATTR_SIZE_MAX for this host system
 #endif
diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h
index 2c54249b35..7b0d05ba1b 100644
--- a/hw/9pfs/coth.h
+++ b/hw/9pfs/coth.h
@@ -19,6 +19,7 @@
 #include "qemu/coroutine-core.h"
 #include "9p.h"
 
+#ifndef EMSCRIPTEN
 /*
  * we want to use bottom half because we want to make sure the below
  * sequence of events.
@@ -57,6 +58,17 @@
         /* re-enter back to qemu thread */                              \
         qemu_coroutine_yield();                                         \
     } while (0)
+#else
+/*
+ * FIXME: implement this on emscripten but emscripten's coroutine
+ * implementation (fiber) doesn't support submitting a coroutine to other
+ * threads.
+ */
+#define v9fs_co_run_in_worker(code_block)                               \
+    do {                                                                \
+        code_block;                                                     \
+    } while (0)
+#endif
 
 void co_run_in_worker_bh(void *);
 int coroutine_fn v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *);
diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
index d35d4f44ff..04f85fb9e9 100644
--- a/hw/9pfs/meson.build
+++ b/hw/9pfs/meson.build
@@ -17,6 +17,8 @@ if host_os == 'darwin'
   fs_ss.add(files('9p-util-darwin.c'))
 elif host_os == 'linux'
   fs_ss.add(files('9p-util-linux.c'))
+elif host_os == 'emscripten'
+  fs_ss.add(files('9p-util-stub.c'))
 endif
 fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c'))
 system_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss)
diff --git a/meson.build b/meson.build
index ab84820bc5..a3aadf8b59 100644
--- a/meson.build
+++ b/meson.build
@@ -2356,11 +2356,11 @@ dbus_display = get_option('dbus_display') \
   .allowed()
 
 have_virtfs = get_option('virtfs') \
-    .require(host_os == 'linux' or host_os == 'darwin',
+    .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'emscripten',
              error_message: 'virtio-9p (virtfs) requires Linux or macOS') \
-    .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'),
+    .require(host_os == 'linux' or host_os == 'emscripten' or cc.has_function('pthread_fchdir_np'),
              error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \
-    .require(host_os == 'darwin' or libattr.found(),
+    .require(host_os == 'darwin' or host_os == 'emscripten' or libattr.found(),
              error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \
     .disable_auto_if(not have_tools and not have_system) \
     .allowed()
-- 
2.25.1
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Paolo Bonzini 8 months, 1 week ago
On 4/7/25 16:45, Kohei Tokunaga wrote:
> Emscripten's fiber does not support submitting coroutines to other
> threads. 

Does it work as long as the thread does not rewind?

> diff --git a/hw/9pfs/9p-util-stub.c b/hw/9pfs/9p-util-stub.c
> new file mode 100644
> index 0000000000..57c89902ab
> --- /dev/null
> +++ b/hw/9pfs/9p-util-stub.c
> @@ -0,0 +1,43 @@
> +/*
> + * 9p utilities stub functions
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "9p-util.h"
> +
> +ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
> +                             void *value, size_t size)
> +{
> +    return -1;
> +}
> +
> +ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> +                              char *list, size_t size)
> +{
> +    return -1;
> +}
> +
> +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> +                                const char *name)
> +{
> +    return -1;
> +}
> +
> +int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
> +                         void *value, size_t size, int flags)
> +{
> +    return -1;
> +
> +}
> +
> +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
> +{
> +    return -1;
> +}
> +
> +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size)
> +{
> +    return -1;
> +}

You can add all these to the stubs/emscripten.c file that I suggested 
elsewhere.

> diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
> index 7bc4ec8e85..8c5006fcdc 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -84,6 +84,24 @@ static inline int errno_to_dotl(int err) {
>       } else if (err == EOPNOTSUPP) {
>           err = 95; /* ==EOPNOTSUPP on Linux */
>       }
> +#elif defined(EMSCRIPTEN)
> +    /*
> +     * FIXME: Only most important errnos translated here yet, this should be
> +     * extended to as many errnos being translated as possible in future.
> +     */
> +    if (err == ENAMETOOLONG) {
> +        err = 36; /* ==ENAMETOOLONG on Linux */
> +    } else if (err == ENOTEMPTY) {
> +        err = 39; /* ==ENOTEMPTY on Linux */
> +    } else if (err == ELOOP) {
> +        err = 40; /* ==ELOOP on Linux */
> +    } else if (err == ENODATA) {
> +        err = 61; /* ==ENODATA on Linux */
> +    } else if (err == ENOTSUP) {
> +        err = 95; /* ==EOPNOTSUPP on Linux */
> +    } else if (err == EOPNOTSUPP) {
> +        err = 95; /* ==EOPNOTSUPP on Linux */
> +    }
>   #else
>   #error Missing errno translation to Linux for this host system
>   #endif
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index 7cad2bce62..4f45f0edd3 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -4013,6 +4013,9 @@ out_nofid:
>    * Linux guests.
>    */
>   #define P9_XATTR_SIZE_MAX 65536
> +#elif defined(EMSCRIPTEN)
> +/* No support for xattr */
> +#define P9_XATTR_SIZE_MAX 0
>   #else
>   #error Missing definition for P9_XATTR_SIZE_MAX for this host system
>   #endif
> diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h
> index 2c54249b35..7b0d05ba1b 100644
> --- a/hw/9pfs/coth.h
> +++ b/hw/9pfs/coth.h
> @@ -19,6 +19,7 @@
>   #include "qemu/coroutine-core.h"
>   #include "9p.h"
>   
> +#ifndef EMSCRIPTEN
>   /*
>    * we want to use bottom half because we want to make sure the below
>    * sequence of events.
> @@ -57,6 +58,17 @@
>           /* re-enter back to qemu thread */                              \
>           qemu_coroutine_yield();                                         \
>       } while (0)
> +#else
> +/*
> + * FIXME: implement this on emscripten but emscripten's coroutine
> + * implementation (fiber) doesn't support submitting a coroutine to other
> + * threads.
> + */
> +#define v9fs_co_run_in_worker(code_block)                               \
> +    do {                                                                \
> +        code_block;                                                     \
> +    } while (0)
> +#endif

You could extracting v9fs_co_run_in_worker()'s bodies into separate 
functions.  It is tedious but not hard; all you have to do is define 
structs for the to parameters and return values of v9fs_co_*(), unpack 
them in the callback functions, and retrieve the return value in 
v9fs_co_*().  Many functions

The advantage is that, instead of all the bottom half and yielding dance 
that is done by v9fs_co_run_in_worker() and co_run_in_worker_bh(), you 
can just use thread_pool_submit_co().

Paolo

>   void co_run_in_worker_bh(void *);
>   int coroutine_fn v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *);
> diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
> index d35d4f44ff..04f85fb9e9 100644
> --- a/hw/9pfs/meson.build
> +++ b/hw/9pfs/meson.build
> @@ -17,6 +17,8 @@ if host_os == 'darwin'
>     fs_ss.add(files('9p-util-darwin.c'))
>   elif host_os == 'linux'
>     fs_ss.add(files('9p-util-linux.c'))
> +elif host_os == 'emscripten'
> +  fs_ss.add(files('9p-util-stub.c'))
>   endif
>   fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c'))
>   system_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss)
> diff --git a/meson.build b/meson.build
> index ab84820bc5..a3aadf8b59 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2356,11 +2356,11 @@ dbus_display = get_option('dbus_display') \
>     .allowed()
>   
>   have_virtfs = get_option('virtfs') \
> -    .require(host_os == 'linux' or host_os == 'darwin',
> +    .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'emscripten',
>                error_message: 'virtio-9p (virtfs) requires Linux or macOS') \
> -    .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'),
> +    .require(host_os == 'linux' or host_os == 'emscripten' or cc.has_function('pthread_fchdir_np'),
>                error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \
> -    .require(host_os == 'darwin' or libattr.found(),
> +    .require(host_os == 'darwin' or host_os == 'emscripten' or libattr.found(),
>                error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \
>       .disable_auto_if(not have_tools and not have_system) \
>       .allowed()
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Kohei Tokunaga 8 months, 1 week ago
Hi Paolo,

> > Emscripten's fiber does not support submitting coroutines to other
> > threads.
>
> Does it work as long as the thread does not rewind?

The structure used by Fiber includes a thread-specific field related to
rewind [1], which prevents it from being shared across threads. The behavior
of the remaining fields in multi-threaded contexts is not documented, so
further experimentation is needed to determine whether they can be safely
shared.

[1]
https://emscripten.org/docs/api_reference/fiber.h.html#c.asyncify_data_t.rewind_id

> You can add all these to the stubs/emscripten.c file that I suggested
> elsewhere.

Sure, I'll apply this reorganization in the next verison of the series.

> You could extracting v9fs_co_run_in_worker()'s bodies into separate
> functions.  It is tedious but not hard; all you have to do is define
> structs for the to parameters and return values of v9fs_co_*(), unpack
> them in the callback functions, and retrieve the return value in
> v9fs_co_*().  Many functions
>
> The advantage is that, instead of all the bottom half and yielding dance
> that is done by v9fs_co_run_in_worker() and co_run_in_worker_bh(), you
> can just use thread_pool_submit_co().

Thank you for the suggestion. I'll explore this approach, though it's still
unclear whether thread_pool_submit_co() can be used with Emscripten's Fiber
due to the limitations mentioned above.
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Christian Schoenebeck 8 months, 1 week ago
On Monday, April 7, 2025 4:45:59 PM CEST Kohei Tokunaga wrote:
> Emscripten's fiber does not support submitting coroutines to other
> threads. So this commit modifies hw/9pfs/coth.h to disable this behavior
> when compiled with Emscripten.

The lack of being able to dispatch a coroutine to a worker thread is one
thing, however it would probably still make sense to use fibers in 9pfs as
replacement of its coroutines mechanism.

In 9pfs coroutines are used to dispatch blocking fs I/O syscalls from main
thread to worker thread(s):

https://wiki.qemu.org/Documentation/9p#Control_Flow

If you just remove the coroutine code entirely, 9p server might hang for good,
and with it QEMU's main thread.

By using fibers instead, it would not hang, as it seems as if I/O syscalls are
emulated in Emscripten, right?

> Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
> ---
>  fsdev/file-op-9p.h     |  3 +++
>  fsdev/meson.build      |  2 +-
>  hw/9pfs/9p-util-stub.c | 43 ++++++++++++++++++++++++++++++++++++++++++
>  hw/9pfs/9p-util.h      | 18 ++++++++++++++++++
>  hw/9pfs/9p.c           |  3 +++
>  hw/9pfs/coth.h         | 12 ++++++++++++
>  hw/9pfs/meson.build    |  2 ++
>  meson.build            |  6 +++---
>  8 files changed, 85 insertions(+), 4 deletions(-)
>  create mode 100644 hw/9pfs/9p-util-stub.c
> 
> diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
> index 4997677460..b7ca2640ce 100644
> --- a/fsdev/file-op-9p.h
> +++ b/fsdev/file-op-9p.h
> @@ -26,6 +26,9 @@
>  # include <sys/param.h>
>  # include <sys/mount.h>
>  #endif
> +#ifdef EMSCRIPTEN
> +#include <sys/vfs.h>
> +#endif
>  
>  #define SM_LOCAL_MODE_BITS    0600
>  #define SM_LOCAL_DIR_MODE_BITS    0700
> diff --git a/fsdev/meson.build b/fsdev/meson.build
> index c751d8cb62..c3e92a29d7 100644
> --- a/fsdev/meson.build
> +++ b/fsdev/meson.build
> @@ -5,6 +5,6 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files(
>    '9p-marshal.c',
>    'qemu-fsdev.c',
>  ), if_false: files('qemu-fsdev-dummy.c'))
> -if host_os in ['linux', 'darwin']
> +if host_os in ['linux', 'darwin', 'emscripten']
>    system_ss.add_all(fsdev_ss)
>  endif
> diff --git a/hw/9pfs/9p-util-stub.c b/hw/9pfs/9p-util-stub.c
> new file mode 100644
> index 0000000000..57c89902ab
> --- /dev/null
> +++ b/hw/9pfs/9p-util-stub.c
> @@ -0,0 +1,43 @@
> +/*
> + * 9p utilities stub functions
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "9p-util.h"
> +
> +ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
> +                             void *value, size_t size)
> +{
> +    return -1;
> +}
> +
> +ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> +                              char *list, size_t size)
> +{
> +    return -1;
> +}
> +
> +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> +                                const char *name)
> +{
> +    return -1;
> +}
> +
> +int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
> +                         void *value, size_t size, int flags)
> +{
> +    return -1;
> +
> +}
> +
> +int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
> +{
> +    return -1;
> +}
> +
> +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size)
> +{
> +    return -1;
> +}

Missing

    errno = ENOTSUP;

> diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
> index 7bc4ec8e85..8c5006fcdc 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -84,6 +84,24 @@ static inline int errno_to_dotl(int err) {
>      } else if (err == EOPNOTSUPP) {
>          err = 95; /* ==EOPNOTSUPP on Linux */
>      }
> +#elif defined(EMSCRIPTEN)
> +    /*
> +     * FIXME: Only most important errnos translated here yet, this should be
> +     * extended to as many errnos being translated as possible in future.
> +     */
> +    if (err == ENAMETOOLONG) {
> +        err = 36; /* ==ENAMETOOLONG on Linux */
> +    } else if (err == ENOTEMPTY) {
> +        err = 39; /* ==ENOTEMPTY on Linux */
> +    } else if (err == ELOOP) {
> +        err = 40; /* ==ELOOP on Linux */
> +    } else if (err == ENODATA) {
> +        err = 61; /* ==ENODATA on Linux */
> +    } else if (err == ENOTSUP) {
> +        err = 95; /* ==EOPNOTSUPP on Linux */
> +    } else if (err == EOPNOTSUPP) {
> +        err = 95; /* ==EOPNOTSUPP on Linux */
> +    }

Looks like you just copied the macOS errno translation code. That probably
doesn't make sense.

/Christian

>  #else
>  #error Missing errno translation to Linux for this host system
>  #endif
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index 7cad2bce62..4f45f0edd3 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -4013,6 +4013,9 @@ out_nofid:
>   * Linux guests.
>   */
>  #define P9_XATTR_SIZE_MAX 65536
> +#elif defined(EMSCRIPTEN)
> +/* No support for xattr */
> +#define P9_XATTR_SIZE_MAX 0
>  #else
>  #error Missing definition for P9_XATTR_SIZE_MAX for this host system
>  #endif
> diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h
> index 2c54249b35..7b0d05ba1b 100644
> --- a/hw/9pfs/coth.h
> +++ b/hw/9pfs/coth.h
> @@ -19,6 +19,7 @@
>  #include "qemu/coroutine-core.h"
>  #include "9p.h"
>  
> +#ifndef EMSCRIPTEN
>  /*
>   * we want to use bottom half because we want to make sure the below
>   * sequence of events.
> @@ -57,6 +58,17 @@
>          /* re-enter back to qemu thread */                              \
>          qemu_coroutine_yield();                                         \
>      } while (0)
> +#else
> +/*
> + * FIXME: implement this on emscripten but emscripten's coroutine
> + * implementation (fiber) doesn't support submitting a coroutine to other
> + * threads.
> + */
> +#define v9fs_co_run_in_worker(code_block)                               \
> +    do {                                                                \
> +        code_block;                                                     \
> +    } while (0)
> +#endif
>  
>  void co_run_in_worker_bh(void *);
>  int coroutine_fn v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *);
> diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
> index d35d4f44ff..04f85fb9e9 100644
> --- a/hw/9pfs/meson.build
> +++ b/hw/9pfs/meson.build
> @@ -17,6 +17,8 @@ if host_os == 'darwin'
>    fs_ss.add(files('9p-util-darwin.c'))
>  elif host_os == 'linux'
>    fs_ss.add(files('9p-util-linux.c'))
> +elif host_os == 'emscripten'
> +  fs_ss.add(files('9p-util-stub.c'))
>  endif
>  fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c'))
>  system_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss)
> diff --git a/meson.build b/meson.build
> index ab84820bc5..a3aadf8b59 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2356,11 +2356,11 @@ dbus_display = get_option('dbus_display') \
>    .allowed()
>  
>  have_virtfs = get_option('virtfs') \
> -    .require(host_os == 'linux' or host_os == 'darwin',
> +    .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'emscripten',
>               error_message: 'virtio-9p (virtfs) requires Linux or macOS') \
> -    .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'),
> +    .require(host_os == 'linux' or host_os == 'emscripten' or cc.has_function('pthread_fchdir_np'),
>               error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \
> -    .require(host_os == 'darwin' or libattr.found(),
> +    .require(host_os == 'darwin' or host_os == 'emscripten' or libattr.found(),
>               error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \
>      .disable_auto_if(not have_tools and not have_system) \
>      .allowed()
>
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Kohei Tokunaga 8 months, 1 week ago
Hi Christian,

> > Emscripten's fiber does not support submitting coroutines to other
> > threads. So this commit modifies hw/9pfs/coth.h to disable this behavior
> > when compiled with Emscripten.
>
> The lack of being able to dispatch a coroutine to a worker thread is one
> thing, however it would probably still make sense to use fibers in 9pfs as
> replacement of its coroutines mechanism.
>
> In 9pfs coroutines are used to dispatch blocking fs I/O syscalls from main
> thread to worker thread(s):
>
> https://wiki.qemu.org/Documentation/9p#Control_Flow
>
> If you just remove the coroutine code entirely, 9p server might hang for
good,
> and with it QEMU's main thread.
>
> By using fibers instead, it would not hang, as it seems as if I/O
syscalls are
> emulated in Emscripten, right?

Thank you for the feedback. Yes, it would be great if Emscripten's fiber
could be used to address this limitation. Since Emscripten's fiber is
cooperative, I believe a blocking code_block can still block the 9pfs server
unless an explicit yield occurs within it. I'll continue exploring better
solutions for this. Please let me know if I'm missing anything.

> Missing
>
>     errno = ENOTSUP;

Sure, I'll fix this in the next version of the series.

> Looks like you just copied the macOS errno translation code. That probably
> doesn't make sense.

Errno values differ between Emscripten and Linux, so conversion is required
here. I've used the same mappings as macOS for now, but I'm happy to add
more conversions if needed.
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Christian Schoenebeck 8 months, 1 week ago
On Friday, April 11, 2025 12:47:29 PM CEST Kohei Tokunaga wrote:
> Hi Christian,
> 
> > > Emscripten's fiber does not support submitting coroutines to other
> > > threads. So this commit modifies hw/9pfs/coth.h to disable this behavior
> > > when compiled with Emscripten.
> >
> > The lack of being able to dispatch a coroutine to a worker thread is one
> > thing, however it would probably still make sense to use fibers in 9pfs as
> > replacement of its coroutines mechanism.
> >
> > In 9pfs coroutines are used to dispatch blocking fs I/O syscalls from main
> > thread to worker thread(s):
> >
> > https://wiki.qemu.org/Documentation/9p#Control_Flow
> >
> > If you just remove the coroutine code entirely, 9p server might hang for
> good,
> > and with it QEMU's main thread.
> >
> > By using fibers instead, it would not hang, as it seems as if I/O
> syscalls are
> > emulated in Emscripten, right?
> 
> Thank you for the feedback. Yes, it would be great if Emscripten's fiber
> could be used to address this limitation. Since Emscripten's fiber is
> cooperative, I believe a blocking code_block can still block the 9pfs server
> unless an explicit yield occurs within it. I'll continue exploring better
> solutions for this. Please let me know if I'm missing anything.

As far as I understand it, the I/O syscalls are emulated, and when being
called by fibers, blocking syscalls would imply to yield under the hood,
without explicit yield by application that is.

If that's true, it would only require little code changes for this to work.

> > Missing
> >
> >     errno = ENOTSUP;
> 
> Sure, I'll fix this in the next version of the series.
> 
> > Looks like you just copied the macOS errno translation code. That probably
> > doesn't make sense.
> 
> Errno values differ between Emscripten and Linux, so conversion is required
> here. I've used the same mappings as macOS for now, but I'm happy to add
> more conversions if needed.

OK, but why have you chosen macOS errno mapping exactly? Are you testing on a
macOS host machine? Are errno values of emscripten machine dependent?

The errno mapping must be correct, otherwise funny things will happen on guest
side if 9p2000.L client is used, as it blindly expects errno numbers sent by
9p server to be in native Linux errno number presentation.

Alternatively 9p2000.u protocol variant could be used for Emscripten. Not
ideal, as this 9p protocol version is somewhat a legacy protocol from QEMU
perspective, reduced performance, less reliable, but it transmits error
strings to client which it can map to correct errno values by itself. Linux 9p
client uses a hash map for this errno translation of 9p2000.u error strings.

/Christian
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Christian Schoenebeck 8 months, 1 week ago
On Saturday, April 12, 2025 10:21:47 AM CEST Christian Schoenebeck wrote:
> On Friday, April 11, 2025 12:47:29 PM CEST Kohei Tokunaga wrote:
> > Hi Christian,
> > 
> > > > Emscripten's fiber does not support submitting coroutines to other
> > > > threads. So this commit modifies hw/9pfs/coth.h to disable this behavior
> > > > when compiled with Emscripten.
> > >
> > > The lack of being able to dispatch a coroutine to a worker thread is one
> > > thing, however it would probably still make sense to use fibers in 9pfs as
> > > replacement of its coroutines mechanism.
> > >
> > > In 9pfs coroutines are used to dispatch blocking fs I/O syscalls from main
> > > thread to worker thread(s):
> > >
> > > https://wiki.qemu.org/Documentation/9p#Control_Flow
> > >
> > > If you just remove the coroutine code entirely, 9p server might hang for
> > good,
> > > and with it QEMU's main thread.
> > >
> > > By using fibers instead, it would not hang, as it seems as if I/O
> > syscalls are
> > > emulated in Emscripten, right?
> > 
> > Thank you for the feedback. Yes, it would be great if Emscripten's fiber
> > could be used to address this limitation. Since Emscripten's fiber is
> > cooperative, I believe a blocking code_block can still block the 9pfs server
> > unless an explicit yield occurs within it. I'll continue exploring better
> > solutions for this. Please let me know if I'm missing anything.
> 
> As far as I understand it, the I/O syscalls are emulated, and when being
> called by fibers, blocking syscalls would imply to yield under the hood,
> without explicit yield by application that is.
> 
> If that's true, it would only require little code changes for this to work.
> 
> > > Missing
> > >
> > >     errno = ENOTSUP;
> > 
> > Sure, I'll fix this in the next version of the series.
> > 
> > > Looks like you just copied the macOS errno translation code. That probably
> > > doesn't make sense.
> > 
> > Errno values differ between Emscripten and Linux, so conversion is required
> > here. I've used the same mappings as macOS for now, but I'm happy to add
> > more conversions if needed.
> 
> OK, but why have you chosen macOS errno mapping exactly? Are you testing on a
> macOS host machine? Are errno values of emscripten machine dependent?
> 
> The errno mapping must be correct, otherwise funny things will happen on guest
> side if 9p2000.L client is used, as it blindly expects errno numbers sent by
> 9p server to be in native Linux errno number presentation.

Let my answer my own question: I just checked the wasi sources. The errno
values are hard coded by the wasi API, consistent over systems. So the current
mapping of this patch is wrong. macOS uses a different mapping than the wasi
API.

https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno_values.h

https://github.com/emscripten-core/emscripten/blob/4af36cf80647f9a82be617a0ff32f3e56f220e41/system/include/wasi/api.h#L116

So please use a correct mapping as defined in that header file.

/Christian

> Alternatively 9p2000.u protocol variant could be used for Emscripten. Not
> ideal, as this 9p protocol version is somewhat a legacy protocol from QEMU
> perspective, reduced performance, less reliable, but it transmits error
> strings to client which it can map to correct errno values by itself. Linux 9p
> client uses a hash map for this errno translation of 9p2000.u error strings.
> 
> /Christian
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Christian Schoenebeck 8 months, 1 week ago
On Saturday, April 12, 2025 12:21:47 PM CEST Christian Schoenebeck wrote:
> On Saturday, April 12, 2025 10:21:47 AM CEST Christian Schoenebeck wrote:
> > On Friday, April 11, 2025 12:47:29 PM CEST Kohei Tokunaga wrote:
[...]
> Let my answer my own question: I just checked the wasi sources. The errno
> values are hard coded by the wasi API, consistent over systems. So the current
> mapping of this patch is wrong. macOS uses a different mapping than the wasi
> API.
> 
> https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno_values.h
> 
> https://github.com/emscripten-core/emscripten/blob/4af36cf80647f9a82be617a0ff32f3e56f220e41/system/include/wasi/api.h#L116
> 
> So please use a correct mapping as defined in that header file.
> 
> /Christian
> 
> > Alternatively 9p2000.u protocol variant could be used for Emscripten. Not
> > ideal, as this 9p protocol version is somewhat a legacy protocol from QEMU
> > perspective, reduced performance, less reliable, but it transmits error
> > strings to client which it can map to correct errno values by itself. Linux 9p
> > client uses a hash map for this errno translation of 9p2000.u error strings.

Stupid me. That's host errno -> Linux errno translation. So your values are
obviously correct, sorry!

However still worth comparing the Linux vs. wasi header files on this.

And I would avoid duplicating the macOS translation code. Instead I would just
do a one-line change:

#elif defined(CONFIG_DARWIN) || defined(EMSCRIPTEN)
...

And probably leave a comment with a link to the wasi API header file there, so
in case new errno translations are added for macOS, that people also check
whether those macros exist in the wasi header file as well.

/Christian
Re: [PATCH 08/10] hw/9pfs: Allow using hw/9pfs with emscripten
Posted by Kohei Tokunaga 8 months ago
Hi Christian,

> > > > Emscripten's fiber does not support submitting coroutines to other
> > > > threads. So this commit modifies hw/9pfs/coth.h to disable this
behavior
> > > > when compiled with Emscripten.
> > >
> > > The lack of being able to dispatch a coroutine to a worker thread is
one
> > > thing, however it would probably still make sense to use fibers in
9pfs as
> > > replacement of its coroutines mechanism.
> > >
> > > In 9pfs coroutines are used to dispatch blocking fs I/O syscalls from
main
> > > thread to worker thread(s):
> > >
> > > https://wiki.qemu.org/Documentation/9p#Control_Flow
> > >
> > > If you just remove the coroutine code entirely, 9p server might hang
for
> > good,
> > > and with it QEMU's main thread.
> > >
> > > By using fibers instead, it would not hang, as it seems as if I/O
> > syscalls are
> > > emulated in Emscripten, right?
> >
> > Thank you for the feedback. Yes, it would be great if Emscripten's fiber
> > could be used to address this limitation. Since Emscripten's fiber is
> > cooperative, I believe a blocking code_block can still block the 9pfs
server
> > unless an explicit yield occurs within it. I'll continue exploring
better
> > solutions for this. Please let me know if I'm missing anything.
>
> As far as I understand it, the I/O syscalls are emulated, and when being
> called by fibers, blocking syscalls would imply to yield under the hood,
> without explicit yield by application that is.
>
> If that's true, it would only require little code changes for this to
work.

Thank you for the information. Yes, I/O syscalls are emulated by
Emscripten. While I haven't found documentation or implementation details on
whether Fibers implicitly yield on blocking syscalls, I'll continue to
explore this approach.

> > Let my answer my own question: I just checked the wasi sources. The
errno
> > values are hard coded by the wasi API, consistent over systems. So the
current
> > mapping of this patch is wrong. macOS uses a different mapping than the
wasi
> > API.
> >
> >
https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno_values.h
> >
> >
https://github.com/emscripten-core/emscripten/blob/4af36cf80647f9a82be617a0ff32f3e56f220e41/system/include/wasi/api.h#L116
> >
> > So please use a correct mapping as defined in that header file.
> >
> > /Christian
> >
> > > Alternatively 9p2000.u protocol variant could be used for Emscripten.
Not
> > > ideal, as this 9p protocol version is somewhat a legacy protocol from
QEMU
> > > perspective, reduced performance, less reliable, but it transmits
error
> > > strings to client which it can map to correct errno values by itself.
Linux 9p
> > > client uses a hash map for this errno translation of 9p2000.u error
strings.
>
> Stupid me. That's host errno -> Linux errno translation. So your values
are
> obviously correct, sorry!
>
> However still worth comparing the Linux vs. wasi header files on this.
>
> And I would avoid duplicating the macOS translation code. Instead I would
just
> do a one-line change:
>
> #elif defined(CONFIG_DARWIN) || defined(EMSCRIPTEN)
> ...
>
> And probably leave a comment with a link to the wasi API header file
there, so
> in case new errno translations are added for macOS, that people also check
> whether those macros exist in the wasi header file as well.

Thanks again for the suggestion. I'll apply this change in the next version
of the series.